Ver Fonte

Merge remote-tracking branch 'origin/v2' into promise-chaining

# Conflicts:
#	packages/codemode/src/interpreter/runtime.ts
#	packages/codemode/src/tool-runtime.ts
Aiden Cline há 1 mês atrás
pai
commit
6273304f2a
100 ficheiros alterados com 5809 adições e 1285 exclusões
  1. 2 0
      AGENTS.md
  2. 122 7
      bun.lock
  3. 1 1
      packages/app/e2e/performance/timeline-stability/file-matrix.spec.ts
  4. 3 3
      packages/app/e2e/performance/timeline-stability/file-mutation.spec.ts
  5. 1 1
      packages/app/e2e/performance/timeline/session-timeline-benchmark.fixture.ts
  6. 2 2
      packages/app/e2e/performance/timeline/session-timeline-stress.fixture.ts
  7. 1 1
      packages/app/e2e/regression/session-timeline-file-projection.spec.ts
  8. 1 1
      packages/app/e2e/regression/session-timeline-file-state.spec.ts
  9. 1 1
      packages/app/e2e/regression/session-timeline-projection.spec.ts
  10. 2 2
      packages/app/e2e/regression/session-timeline-tool-projection.spec.ts
  11. 2 2
      packages/app/e2e/smoke/session-timeline.fixture.ts
  12. 1 1
      packages/app/e2e/utils/mock-server.ts
  13. 2 2
      packages/app/src/context/global-sync/event-reducer.ts
  14. 3 3
      packages/app/src/context/global-sync/session-cache.test.ts
  15. 2 2
      packages/app/src/context/global-sync/session-cache.ts
  16. 2 2
      packages/app/src/context/global-sync/types.ts
  17. 3 3
      packages/app/src/context/server-session.ts
  18. 2 2
      packages/app/src/pages/session/review-tab.tsx
  19. 3 11
      packages/app/src/pages/session/session-side-panel.tsx
  20. 1 1
      packages/app/src/pages/session/timeline/message-timeline.tsx
  21. 2 6
      packages/app/src/pages/session/v2/review-diff-kinds.ts
  22. 4 9
      packages/app/src/pages/session/v2/review-panel-v2.tsx
  23. 2 2
      packages/app/src/utils/diffs.test.ts
  24. 2 2
      packages/app/src/utils/diffs.ts
  25. 1 0
      packages/cli/bin/opencode2.cjs
  26. 11 0
      packages/cli/src/commands/commands.ts
  27. 116 0
      packages/cli/src/commands/handlers/console/login.ts
  28. 3 0
      packages/cli/src/index.ts
  29. 3 2
      packages/cli/src/mini/catalog.shared.ts
  30. 2 2
      packages/cli/src/mini/demo.ts
  31. 2 2
      packages/cli/src/mini/footer.prompt.tsx
  32. 0 2
      packages/cli/src/mini/footer.ts
  33. 1 4
      packages/cli/src/mini/footer.view.tsx
  34. 0 4
      packages/cli/src/mini/mini.ts
  35. 0 1
      packages/cli/src/mini/noninteractive.ts
  36. 5 8
      packages/cli/src/mini/run.ts
  37. 0 2
      packages/cli/src/mini/runtime.lifecycle.ts
  38. 0 5
      packages/cli/src/mini/runtime.ts
  39. 6 8
      packages/cli/src/mini/stream-v2.subagent.ts
  40. 15 16
      packages/cli/src/mini/stream-v2.transport.ts
  41. 2 2
      packages/cli/src/mini/tool.ts
  42. 1 1
      packages/cli/src/mini/types.ts
  43. 26 11
      packages/cli/src/server-process.ts
  44. 12 0
      packages/cli/src/tui.ts
  45. 242 0
      packages/cli/src/ui/timeline.tsx
  46. 46 1
      packages/cli/test/service.test.ts
  47. 2 3
      packages/client/script/build.ts
  48. 0 1
      packages/client/src/contract.ts
  49. 142 113
      packages/client/src/effect/api/api.ts
  50. 148 120
      packages/client/src/effect/generated/client.ts
  51. 244 179
      packages/client/src/promise/generated/client.ts
  52. 252 195
      packages/client/src/promise/generated/types.ts
  53. 4 4
      packages/client/test/contract-identity.test.ts
  54. 27 9
      packages/client/test/promise.test.ts
  55. 2 2
      packages/codemode/README.md
  56. 1 1
      packages/codemode/codemode.md
  57. 0 1
      packages/codemode/src/interpreter/model.ts
  58. 24 35
      packages/codemode/src/interpreter/runtime.ts
  59. 2 0
      packages/codemode/src/stdlib/math.ts
  60. 14 2
      packages/codemode/src/stdlib/number.ts
  61. 18 15
      packages/codemode/src/stdlib/object.ts
  62. 1 0
      packages/codemode/src/tool-runtime.ts
  63. 28 0
      packages/codemode/test/LICENSE.test262
  64. 325 0
      packages/codemode/test/array-callbacks-test262.test.ts
  65. 323 0
      packages/codemode/test/array-core-test262.test.ts
  66. 21 0
      packages/codemode/test/codemode.test.ts
  67. 524 88
      packages/codemode/test/fixtures/opencode-v2-openapi.json
  68. 4 4
      packages/codemode/test/openapi.test.ts
  69. 9 87
      packages/codemode/test/parity.test.ts
  70. 6 0
      packages/codemode/test/promise.test.ts
  71. 108 19
      packages/codemode/test/stdlib.test.ts
  72. 580 0
      packages/codemode/test/string-core-test262.test.ts
  73. 625 0
      packages/codemode/test/string-regexp-test262.test.ts
  74. 794 0
      packages/codemode/test/string-search-test262.test.ts
  75. 77 0
      packages/codemode/test/test262-array.md
  76. 69 0
      packages/codemode/test/test262-string.md
  77. 2 0
      packages/core/src/agent.ts
  78. 7 3
      packages/core/src/aisdk.ts
  79. 40 6
      packages/core/src/config.ts
  80. 1 0
      packages/core/src/config/plugin/agent.ts
  81. 1 0
      packages/core/src/config/plugin/command.ts
  82. 3 2
      packages/core/src/config/plugin/provider.ts
  83. 10 0
      packages/core/src/config/plugin/skill.ts
  84. 5 4
      packages/core/src/config/provider.ts
  85. 1 0
      packages/core/src/database/migration.gen.ts
  86. 227 0
      packages/core/src/database/migration/20260707120000_migrate_prelaunch_v2_state.ts
  87. 1 1
      packages/core/src/file-mutation.ts
  88. 2 2
      packages/core/src/file.ts
  89. 1 1
      packages/core/src/filesystem/location-watcher.ts
  90. 1 1
      packages/core/src/git.ts
  91. 59 23
      packages/core/src/integration.ts
  92. 38 71
      packages/core/src/location-services.ts
  93. 118 16
      packages/core/src/mcp/client.ts
  94. 80 50
      packages/core/src/mcp/index.ts
  95. 13 12
      packages/core/src/models-dev.ts
  96. 13 6
      packages/core/src/plugin.ts
  97. 7 0
      packages/core/src/plugin/agent.ts
  98. 67 0
      packages/core/src/plugin/hooks.ts
  99. 74 68
      packages/core/src/plugin/host.ts
  100. 3 3
      packages/core/src/plugin/internal.ts

+ 2 - 0
AGENTS.md

@@ -19,6 +19,8 @@ Valid types are `feat`, `fix`, `docs`, `chore`, `refactor`, and `test`. Scopes a
 
 Examples: `fix(tui): simplify thinking toggle styling`, `docs: update contributing guide`, `chore(sdk): regenerate types`.
 
+Never bypass Git hooks. Do not use `--no-verify` or otherwise disable, skip, or circumvent commit or push hooks. If a hook fails, fix the failure or stop and report it to the user.
+
 ## Style Guide
 
 ### General Principles

+ 122 - 7
bun.lock

@@ -114,6 +114,7 @@
         "effect": "catalog:",
         "fuzzysort": "catalog:",
         "jsonc-parser": "3.3.1",
+        "open": "10.1.2",
         "opentui-spinner": "catalog:",
         "semver": "catalog:",
         "solid-js": "catalog:",
@@ -526,10 +527,10 @@
       "name": "@opencode-ai/http-recorder",
       "version": "1.17.14",
       "dependencies": {
-        "@effect/platform-node": "4.0.0-beta.83",
         "@effect/platform-node-shared": "4.0.0-beta.83",
       },
       "devDependencies": {
+        "@effect/platform-node": "catalog:",
         "@tsconfig/node22": "catalog:",
         "@types/bun": "catalog:",
         "@types/node": "catalog:",
@@ -538,7 +539,7 @@
         "typescript": "catalog:",
       },
       "peerDependencies": {
-        "effect": "4.0.0-beta.83",
+        "effect": "catalog:",
       },
     },
     "packages/httpapi-codegen": {
@@ -873,6 +874,8 @@
       "name": "@opencode-ai/simulation",
       "version": "1.17.13",
       "dependencies": {
+        "@fontsource/adwaita-mono": "5.2.1",
+        "@napi-rs/canvas": "1.0.2",
         "@opencode-ai/core": "workspace:*",
         "@opencode-ai/llm": "workspace:*",
         "@opentui/core": "catalog:",
@@ -1611,11 +1614,11 @@
 
     "@emmetio/stream-reader-utils": ["@emmetio/stream-reader-utils@0.1.0", "", {}, "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A=="],
 
-    "@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
+    "@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
 
-    "@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
+    "@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
 
-    "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
+    "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
 
     "@emotion/is-prop-valid": ["@emotion/is-prop-valid@0.8.8", "", { "dependencies": { "@emotion/memoize": "0.7.4" } }, "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA=="],
 
@@ -1723,6 +1726,8 @@
 
     "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="],
 
+    "@fontsource/adwaita-mono": ["@fontsource/adwaita-mono@5.2.1", "", {}, "sha512-6+Q1UIvklJ9REijs6kv7YlRNt6yktRj0iW8H69YIugdD9P2h3eIX1AB8/9ICMfpVyVeywlsrCXg82y/LfRrjyg=="],
+
     "@fontsource/ibm-plex-mono": ["@fontsource/ibm-plex-mono@5.2.5", "", {}, "sha512-G09N3GfuT9qj3Ax2FDZvKqZttzM3v+cco2l8uXamhKyXLdmlaUDH5o88/C3vtTHj2oT7yRKsvxz9F+BXbWKMYA=="],
 
     "@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="],
@@ -1983,6 +1988,30 @@
 
     "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4", "", { "os": "win32", "cpu": "x64" }, "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ=="],
 
+    "@napi-rs/canvas": ["@napi-rs/canvas@1.0.2", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "1.0.2", "@napi-rs/canvas-darwin-arm64": "1.0.2", "@napi-rs/canvas-darwin-x64": "1.0.2", "@napi-rs/canvas-linux-arm-gnueabihf": "1.0.2", "@napi-rs/canvas-linux-arm64-gnu": "1.0.2", "@napi-rs/canvas-linux-arm64-musl": "1.0.2", "@napi-rs/canvas-linux-riscv64-gnu": "1.0.2", "@napi-rs/canvas-linux-x64-gnu": "1.0.2", "@napi-rs/canvas-linux-x64-musl": "1.0.2", "@napi-rs/canvas-win32-arm64-msvc": "1.0.2", "@napi-rs/canvas-win32-x64-msvc": "1.0.2" } }, "sha512-EYEqlMYaCbpZDz+IgDH5xp9MTd3ui4dmGqbQYryhMLnSRxrhHKq5KQWHHKxFUcEP4Hp8/BWgvqXocX4j7iSbOQ=="],
+
+    "@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@1.0.2", "", { "os": "android", "cpu": "arm64" }, "sha512-IMXKVQod0ol4vt3gmClUfXz4JAgHYESGPCUqmH3lQxBoL0K/2greJaQE1HVBVxWWFKfLc4OLZVdxg7kXVyXv+g=="],
+
+    "@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@1.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sc8tPi6cF+5lqOzCCKFALJHhDiRwyMzTPYm3bbhdXsOunU0lQO5f05ucyOzN2r55I23Hg5bsjH63uSCvWp3EgQ=="],
+
+    "@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@1.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-niDXZ9LhKB1zLrUdYB64RHQFDGz9rr0eGx061qtJJU3U20EMMIx28ADF5fVYbhtOgkWQrBjFicfaye1yM0U62A=="],
+
+    "@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@1.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-sgatQL9JxGRH/Amzcvu0P3t8Am3duou74CisfuJ41Dwt8cWy723z/9KZ8LlgmxfypEwEZxSTNFJtU8d281lmhQ=="],
+
+    "@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dgKuX0peF3xwY6ZF5QxGS4wbfDqpoFAJYXiLSp+guZKARQUKMkRqZSDrXKj7nfrec3UCMzC0PFCPte0ES98AiA=="],
+
+    "@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-qwROoDIC9upfvDoRLuPn2aNg9CGW1x0Ygr4k2Or+8paA9d0qBLwk87U+g8KQpoOviKoPoiwl97kvBYuYD7qZoA=="],
+
+    "@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@1.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-fXRjnPihdnbO6qy1QQOgxAonb68A0TCEG7rj1x7v7rxNElsE8EVIKIEUTvyDtU+sthYSbX+8e7g3oZiLGnOmxw=="],
+
+    "@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-nPR97DXhbWIAy7yazF3jc06kEPMqYMLmPzFOVNlwKPfIoSChnI+x7dc0hTLaihz3jxrjL6j4BbA7earxfx4X3g=="],
+
+    "@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-l7zZY5+jL5qnBZtDz7CoBtY6p7EkHu422g/0zWwrOrzIwWyWxZFRfZZORY1UG7YApymPLx+UbOkN206xXn/c1Q=="],
+
+    "@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@1.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-yE0koHCFF4PIbMc2o2SEALhnipz7WBISh5glLvQiomtIoCcW0np3H4Lw93ceJAfJttTTeIIWFbwH84F7EVzjMQ=="],
+
+    "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@1.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-okU8/t2foV6C31n0GtvEMbfD5rOFc70+/6xUNME9Guld29sgSOIGUEDScAWFlcP3k5TYQRl9TNkwJEEjh15w8A=="],
+
     "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
 
     "@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="],
@@ -2277,7 +2306,7 @@
 
     "@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.127.0", "", { "os": "win32", "cpu": "x64" }, "sha512-rOrnSQSCbhI2kowr9XxE7m9a8oQXnBHjnS6j95LxxAnEZ0+Fz20WlRXG4ondQb+ejjt2KOsa65sE6++L6kUd+w=="],
 
-    "@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="],
+    "@oxc-project/types": ["@oxc-project/types@0.138.0", "", {}, "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA=="],
 
     "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.20.0", "", { "os": "android", "cpu": "arm" }, "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg=="],
 
@@ -2569,7 +2598,37 @@
 
     "@remix-run/router": ["@remix-run/router@1.9.0", "", {}, "sha512-bV63itrKBC0zdT27qYm6SDZHlkXwFL1xMBuhkn+X7l0+IIhNaH5wuuvZKp6eKhCD4KFhujhfhCT1YxXW6esUIA=="],
 
-    "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
+    "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.4", "", { "os": "android", "cpu": "arm64" }, "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw=="],
+
+    "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ=="],
+
+    "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg=="],
+
+    "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ=="],
+
+    "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.4", "", { "os": "linux", "cpu": "arm" }, "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA=="],
+
+    "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w=="],
+
+    "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng=="],
+
+    "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg=="],
+
+    "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ=="],
+
+    "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw=="],
+
+    "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ=="],
+
+    "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.4", "", { "os": "none", "cpu": "arm64" }, "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA=="],
+
+    "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.1.4", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg=="],
+
+    "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA=="],
+
+    "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ=="],
+
+    "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
 
     "@rollup/pluginutils": ["@rollup/pluginutils@5.4.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg=="],
 
@@ -3111,6 +3170,8 @@
 
     "@types/react": ["@types/react@18.0.25", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g=="],
 
+    "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
+
     "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="],
 
     "@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
@@ -3183,6 +3244,12 @@
 
     "@valibot/to-json-schema": ["@valibot/to-json-schema@1.6.0", "", { "peerDependencies": { "valibot": "^1.3.0" } }, "sha512-d6rYyK5KVa2XdqamWgZ4/Nr+cXhxjy7lmpe6Iajw15J/jmU+gyxl2IEd1Otg1d7Rl3gOQL5reulnSypzBtYy1A=="],
 
+    "@vercel/cli-config": ["@vercel/cli-config@0.2.0", "", { "dependencies": { "xdg-app-paths": "5", "zod": "4.1.11" } }, "sha512-fJRRRB7734BDuXZ89yBEaA2ncYhH7bWX30mk04W80J6VAfQc+4iB8lyzAdaGpFV3/vNlkt9VZt+/uoQoWX6UsQ=="],
+
+    "@vercel/cli-exec": ["@vercel/cli-exec@1.0.0", "", { "dependencies": { "execa": "5.1.1" } }, "sha512-kQF8LGie/Hbdq9/psJxLE7owRTcqMQMhgybU04gCeR7cbQAr5t8OrjefDNColJv1QSSucFt4pLwRiARVmlOnug=="],
+
+    "@vercel/functions": ["@vercel/functions@3.7.5", "", { "dependencies": { "@vercel/oidc": "3.8.0" }, "peerDependencies": { "@aws-sdk/credential-provider-web-identity": "*", "ws": ">=8" }, "optionalPeers": ["@aws-sdk/credential-provider-web-identity", "ws"] }, "sha512-ESf8BbeDebqRUyMi09JwRbQqpLn4g6fjcVVHPsHB56j2dSqRrSHO4h3X4aaxJf6iQQjzhAtDGI2xCWQ27JE8PA=="],
+
     "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="],
 
     "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
@@ -4987,6 +5054,8 @@
 
     "opentui-spinner": ["opentui-spinner@0.0.7", "", { "dependencies": { "cli-spinners": "^3.3.0" }, "peerDependencies": { "@opentui/core": "^0.3.4", "@opentui/react": "^0.3.4", "@opentui/solid": "^0.3.4", "typescript": "^5" }, "optionalPeers": ["@opentui/react", "@opentui/solid"] }, "sha512-nPzwAvJG+y9rVEwwHLHqbsMzLnIk2zw+F9LqwA7aYJvpM5gsrKC2rrGi36A+tZpA+1RnWxXeWEgVZMchnaH18Q=="],
 
+    "os-paths": ["os-paths@4.4.0", "", {}, "sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg=="],
+
     "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
 
     "oxc-minify": ["oxc-minify@0.96.0", "", { "optionalDependencies": { "@oxc-minify/binding-android-arm64": "0.96.0", "@oxc-minify/binding-darwin-arm64": "0.96.0", "@oxc-minify/binding-darwin-x64": "0.96.0", "@oxc-minify/binding-freebsd-x64": "0.96.0", "@oxc-minify/binding-linux-arm-gnueabihf": "0.96.0", "@oxc-minify/binding-linux-arm-musleabihf": "0.96.0", "@oxc-minify/binding-linux-arm64-gnu": "0.96.0", "@oxc-minify/binding-linux-arm64-musl": "0.96.0", "@oxc-minify/binding-linux-riscv64-gnu": "0.96.0", "@oxc-minify/binding-linux-s390x-gnu": "0.96.0", "@oxc-minify/binding-linux-x64-gnu": "0.96.0", "@oxc-minify/binding-linux-x64-musl": "0.96.0", "@oxc-minify/binding-wasm32-wasi": "0.96.0", "@oxc-minify/binding-win32-arm64-msvc": "0.96.0", "@oxc-minify/binding-win32-x64-msvc": "0.96.0" } }, "sha512-dXeeGrfPJJ4rMdw+NrqiCRtbzVX2ogq//R0Xns08zql2HjV3Zi2SBJ65saqfDaJzd2bcHqvGWH+M44EQCHPAcA=="],
@@ -5401,6 +5470,8 @@
 
     "roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="],
 
+    "rolldown": ["rolldown@1.1.4", "", { "dependencies": { "@oxc-project/types": "=0.138.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.4", "@rolldown/binding-darwin-arm64": "1.1.4", "@rolldown/binding-darwin-x64": "1.1.4", "@rolldown/binding-freebsd-x64": "1.1.4", "@rolldown/binding-linux-arm-gnueabihf": "1.1.4", "@rolldown/binding-linux-arm64-gnu": "1.1.4", "@rolldown/binding-linux-arm64-musl": "1.1.4", "@rolldown/binding-linux-ppc64-gnu": "1.1.4", "@rolldown/binding-linux-s390x-gnu": "1.1.4", "@rolldown/binding-linux-x64-gnu": "1.1.4", "@rolldown/binding-linux-x64-musl": "1.1.4", "@rolldown/binding-openharmony-arm64": "1.1.4", "@rolldown/binding-wasm32-wasi": "1.1.4", "@rolldown/binding-win32-arm64-msvc": "1.1.4", "@rolldown/binding-win32-x64-msvc": "1.1.4" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA=="],
+
     "rollup": ["rollup@4.60.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.4", "@rollup/rollup-android-arm64": "4.60.4", "@rollup/rollup-darwin-arm64": "4.60.4", "@rollup/rollup-darwin-x64": "4.60.4", "@rollup/rollup-freebsd-arm64": "4.60.4", "@rollup/rollup-freebsd-x64": "4.60.4", "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", "@rollup/rollup-linux-arm-musleabihf": "4.60.4", "@rollup/rollup-linux-arm64-gnu": "4.60.4", "@rollup/rollup-linux-arm64-musl": "4.60.4", "@rollup/rollup-linux-loong64-gnu": "4.60.4", "@rollup/rollup-linux-loong64-musl": "4.60.4", "@rollup/rollup-linux-ppc64-gnu": "4.60.4", "@rollup/rollup-linux-ppc64-musl": "4.60.4", "@rollup/rollup-linux-riscv64-gnu": "4.60.4", "@rollup/rollup-linux-riscv64-musl": "4.60.4", "@rollup/rollup-linux-s390x-gnu": "4.60.4", "@rollup/rollup-linux-x64-gnu": "4.60.4", "@rollup/rollup-linux-x64-musl": "4.60.4", "@rollup/rollup-openbsd-x64": "4.60.4", "@rollup/rollup-openharmony-arm64": "4.60.4", "@rollup/rollup-win32-arm64-msvc": "4.60.4", "@rollup/rollup-win32-ia32-msvc": "4.60.4", "@rollup/rollup-win32-x64-gnu": "4.60.4", "@rollup/rollup-win32-x64-msvc": "4.60.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g=="],
 
     "rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="],
@@ -6021,8 +6092,12 @@
 
     "wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="],
 
+    "xdg-app-paths": ["xdg-app-paths@5.5.1", "", { "dependencies": { "os-paths": "^4.0.1", "xdg-portable": "^7.2.0" } }, "sha512-hI3flOB4PLZIy5prbtTpirobtPE2ZtZ52szO+2mM9Efp6ErM398La+C1lIpNWDfNoQk+6Lsi6nMcCwVB7pxeMQ=="],
+
     "xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="],
 
+    "xdg-portable": ["xdg-portable@7.3.0", "", { "dependencies": { "os-paths": "^4.0.1" } }, "sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw=="],
+
     "xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
 
     "xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
@@ -6619,6 +6694,10 @@
 
     "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],
 
+    "@oxc-parser/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
+
+    "@oxc-parser/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
+
     "@oxc-resolver/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
 
     "@oxc-resolver/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
@@ -6639,6 +6718,8 @@
 
     "@puppeteer/browsers/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
 
+    "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="],
+
     "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
 
     "@sentry/bundler-plugin-core/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
@@ -6747,6 +6828,14 @@
 
     "@types/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
 
+    "@vercel/cli-config/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
+
+    "@vercel/cli-exec/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
+
+    "@vercel/functions/@vercel/oidc": ["@vercel/oidc@3.8.0", "", { "dependencies": { "@vercel/cli-config": "0.2.0", "@vercel/cli-exec": "1.0.0", "jose": "^5.9.6" } }, "sha512-r00laGW6Pv778RoR6M2NxX91ycSj+PBwVo+fOb9Bif+F0IyUKt25zrvBzfEzQpeAzbqOgPZyQibEWDdDFApd+A=="],
+
+    "@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
+
     "@vitest/coverage-v8/@vitest/utils": ["@vitest/utils@4.1.8", "", { "dependencies": { "@vitest/pretty-format": "4.1.8", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg=="],
 
     "@vitest/coverage-v8/magicast": ["magicast@0.5.3", "", { "dependencies": { "@babel/parser": "^7.29.3", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw=="],
@@ -7043,6 +7132,8 @@
 
     "openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
 
+    "oxc-parser/@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="],
+
     "p-any/p-cancelable": ["p-cancelable@3.0.0", "", {}, "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="],
 
     "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
@@ -7707,6 +7798,10 @@
 
     "@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
 
+    "@oxc-parser/binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
+
+    "@oxc-resolver/binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
+
     "@pierre/diffs/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="],
 
     "@pierre/diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
@@ -7715,6 +7810,8 @@
 
     "@puppeteer/browsers/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
 
+    "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="],
+
     "@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@8.0.7", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg=="],
 
     "@sentry/bundler-plugin-core/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
@@ -7785,6 +7882,22 @@
 
     "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
 
+    "@vercel/cli-exec/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
+
+    "@vercel/cli-exec/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
+
+    "@vercel/cli-exec/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
+
+    "@vercel/cli-exec/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
+
+    "@vercel/cli-exec/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
+
+    "@vercel/cli-exec/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
+
+    "@vercel/cli-exec/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
+
+    "@vercel/functions/@vercel/oidc/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
+
     "@vitest/coverage-v8/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@4.1.8", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA=="],
 
     "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
@@ -8351,6 +8464,8 @@
 
     "@stoplight/spectral-core/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
 
+    "@vercel/cli-exec/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
+
     "ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
 
     "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],

+ 1 - 1
packages/app/e2e/performance/timeline-stability/file-matrix.spec.ts

@@ -20,7 +20,7 @@ const profiles = [
   { name: "edit", tool: "edit", input: { filePath: "src/edit.ts" } },
   {
     name: "multi patch",
-    tool: "apply_patch",
+    tool: "patch",
     input: { files: ["src/a.ts", "src/b.ts", "src/old.ts", "src/moved.ts"] },
   },
 ] as const

+ 3 - 3
packages/app/e2e/performance/timeline-stability/file-mutation.spec.ts

@@ -25,7 +25,7 @@ test("adds patch files incrementally without resetting outer expansion", async (
       userMessage(),
       assistantMessage(
         [
-          toolPart(patchID, "apply_patch", "running", { files: [first.filePath] }, { metadata: { files: [first] } }),
+          toolPart(patchID, "patch", "running", { files: [first.filePath] }, { metadata: { files: [first] } }),
           textPart(followingID, "Following incremental patch"),
         ],
         { completed: false },
@@ -49,7 +49,7 @@ test("adds patch files incrementally without resetting outer expansion", async (
     partUpdated(
       toolPart(
         patchID,
-        "apply_patch",
+        "patch",
         "running",
         { files: [first.filePath, second.filePath] },
         { metadata: { files: [first, second] } },
@@ -61,7 +61,7 @@ test("adds patch files incrementally without resetting outer expansion", async (
     partUpdated(
       toolPart(
         patchID,
-        "apply_patch",
+        "patch",
         "completed",
         { files: [first.filePath, second.filePath, third.filePath] },
         { metadata: { files: [first, second, third] } },

+ 1 - 1
packages/app/e2e/performance/timeline/session-timeline-benchmark.fixture.ts

@@ -295,7 +295,7 @@ function performanceTurn(index: number) {
             messageID: assistantID,
             type: "tool",
             callID: `call_0000_${suffix}_patch`,
-            tool: "apply_patch",
+            tool: "patch",
             state: {
               status: "completed",
               input: { patchText: realisticPatch(index) },

+ 2 - 2
packages/app/e2e/performance/timeline/session-timeline-stress.fixture.ts

@@ -131,7 +131,7 @@ function toolPart(
 ): MessagePart {
   const metadata =
     metadataOverride ??
-    (tool === "apply_patch"
+    (tool === "patch"
       ? { files: [patchFile(index, "update"), patchFile(index + 1, index % 2 === 0 ? "add" : "delete")] }
       : tool === "edit" || tool === "write"
         ? {
@@ -219,7 +219,7 @@ function turn(index: number): Message[] {
       ? [toolPart(index, 7, "write", { filePath: `src/generated/write-${index}.ts`, content: code(index, 28) }, 560)]
       : []),
     ...(index % 8 === 0
-      ? [toolPart(index, 8, "apply_patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
+      ? [toolPart(index, 8, "patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
       : []),
     ...(index % 7 === 0
       ? [toolPart(index, 4, "bash", { command: "bun typecheck", description: "Verify generated output" }, 620)]

+ 1 - 1
packages/app/e2e/regression/session-timeline-file-projection.spec.ts

@@ -24,7 +24,7 @@ test("renders a completed single-file patch", async ({ page }) => {
       assistantMessage([
         toolPart(
           id,
-          "apply_patch",
+          "patch",
           "completed",
           { files: ["src/a.ts"] },
           {

+ 1 - 1
packages/app/e2e/regression/session-timeline-file-state.spec.ts

@@ -35,7 +35,7 @@ test("preserves nested patch file state through outer collapse and reopen", asyn
       assistantMessage([
         toolPart(
           patchID,
-          "apply_patch",
+          "patch",
           "completed",
           { files: files.map((file) => file.filePath) },
           { metadata: { files } },

+ 1 - 1
packages/app/e2e/regression/session-timeline-projection.spec.ts

@@ -246,7 +246,7 @@ function editPart(id: string) {
 function patchPart(id: string) {
   return toolPart(
     id,
-    "apply_patch",
+    "patch",
     "completed",
     { files: ["src/a.ts", "src/b.ts"] },
     {

+ 2 - 2
packages/app/e2e/regression/session-timeline-tool-projection.spec.ts

@@ -8,7 +8,7 @@ import {
 } from "../performance/timeline-stability/fixture"
 
 test("renders every tool error outcome without leaking hidden tools", async ({ page }) => {
-  const ordinary = ["bash", "edit", "write", "apply_patch", "webfetch", "websearch", "task", "skill", "mcp_probe"]
+  const ordinary = ["bash", "edit", "write", "patch", "webfetch", "websearch", "task", "skill", "mcp_probe"]
   const parts = ordinary.map((tool, index) =>
     toolPart(`prt_error_${index}`, tool, "error", errorInput(tool), { error: `${tool} failed visibly` }),
   )
@@ -90,7 +90,7 @@ function questionInput() {
 function errorInput(tool: string) {
   if (tool === "bash") return { command: "exit 1" }
   if (["edit", "write"].includes(tool)) return { filePath: "src/error.ts", content: "" }
-  if (tool === "apply_patch") return { files: ["src/error.ts"] }
+  if (tool === "patch") return { files: ["src/error.ts"] }
   if (tool === "webfetch") return { url: "https://example.com" }
   if (tool === "websearch") return { query: "failure" }
   if (tool === "task") return { description: "Fail task", subagent_type: "explore" }

+ 2 - 2
packages/app/e2e/smoke/session-timeline.fixture.ts

@@ -120,7 +120,7 @@ function toolPart(
   outputLength = 160,
 ): MessagePart {
   const metadata =
-    tool === "apply_patch"
+    tool === "patch"
       ? { files: [patchFile(index, "update"), patchFile(index + 1, index % 2 === 0 ? "add" : "delete")] }
       : tool === "edit" || tool === "write"
         ? {
@@ -199,7 +199,7 @@ function turn(index: number): Message[] {
       ? [toolPart(index, 7, "write", { filePath: `src/generated/write-${index}.ts`, content: code(index, 28) }, 560)]
       : []),
     ...(index % 8 === 0
-      ? [toolPart(index, 8, "apply_patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
+      ? [toolPart(index, 8, "patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
       : []),
     ...(index % 7 === 0 ? [toolPart(index, 4, "bash", { command: "bun typecheck" }, 620)] : []),
     ...(index % 10 === 0 ? [toolPart(index, 9, "webfetch", { url: "https://example.com/docs/sample" }, 120)] : []),

+ 1 - 1
packages/app/e2e/utils/mock-server.ts

@@ -55,7 +55,7 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
     const path = url.pathname
     if (path === "/global/event" || path === "/event") return sse(route, config.events?.(), config.eventRetry)
     if (path === "/global/health") return json(route, { healthy: true })
-    if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: false })
+    if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: true })
     if (path === "/permission")
       return json(route, typeof config.permissions === "function" ? config.permissions() : (config.permissions ?? []))
     if (path === "/question")

+ 2 - 2
packages/app/src/context/global-sync/event-reducer.ts

@@ -8,7 +8,7 @@ import type {
   QuestionRequest,
   Session,
   SessionStatus,
-  SnapshotFileDiff,
+  FileDiffInfo,
   Todo,
 } from "@opencode-ai/sdk/v2/client"
 import type { State, VcsCache } from "./types"
@@ -188,7 +188,7 @@ export function applyDirectoryEvent(input: {
       break
     }
     case "session.diff": {
-      const props = event.properties as { sessionID: string; diff: SnapshotFileDiff[] }
+      const props = event.properties as { sessionID: string; diff: FileDiffInfo[] }
       input.setStore("session_diff", props.sessionID, reconcile(list(props.diff), { key: "file" }))
       break
     }

+ 3 - 3
packages/app/src/context/global-sync/session-cache.test.ts

@@ -5,7 +5,7 @@ import type {
   PermissionRequest,
   QuestionRequest,
   SessionStatus,
-  SnapshotFileDiff,
+  FileDiffInfo,
   Todo,
 } from "@opencode-ai/sdk/v2/client"
 import { dropSessionCaches, pickSessionCacheEvictions } from "./session-cache"
@@ -33,7 +33,7 @@ describe("app session cache", () => {
   test("dropSessionCaches clears orphaned parts without message rows", () => {
     const store: {
       session_status: Record<string, SessionStatus | undefined>
-      session_diff: Record<string, SnapshotFileDiff[] | undefined>
+      session_diff: Record<string, FileDiffInfo[] | undefined>
       todo: Record<string, Todo[] | undefined>
       message: Record<string, Message[] | undefined>
       part: Record<string, Part[] | undefined>
@@ -67,7 +67,7 @@ describe("app session cache", () => {
     const m = msg("msg_1", "ses_1")
     const store: {
       session_status: Record<string, SessionStatus | undefined>
-      session_diff: Record<string, SnapshotFileDiff[] | undefined>
+      session_diff: Record<string, FileDiffInfo[] | undefined>
       todo: Record<string, Todo[] | undefined>
       message: Record<string, Message[] | undefined>
       part: Record<string, Part[] | undefined>

+ 2 - 2
packages/app/src/context/global-sync/session-cache.ts

@@ -4,7 +4,7 @@ import type {
   PermissionRequest,
   QuestionRequest,
   SessionStatus,
-  SnapshotFileDiff,
+  FileDiffInfo,
   Todo,
 } from "@opencode-ai/sdk/v2/client"
 
@@ -12,7 +12,7 @@ export const SESSION_CACHE_LIMIT = 40
 
 type SessionCache = {
   session_status: Record<string, SessionStatus | undefined>
-  session_diff: Record<string, SnapshotFileDiff[] | undefined>
+  session_diff: Record<string, FileDiffInfo[] | undefined>
   todo: Record<string, Todo[] | undefined>
   message: Record<string, Message[] | undefined>
   part: Record<string, Part[] | undefined>

+ 2 - 2
packages/app/src/context/global-sync/types.ts

@@ -13,7 +13,7 @@ import type {
   ReferenceInfo,
   Session,
   SessionStatus,
-  SnapshotFileDiff,
+  FileDiffInfo,
   Todo,
   VcsInfo,
 } from "@opencode-ai/sdk/v2/client"
@@ -51,7 +51,7 @@ export type State = {
   }
   session_working(id: string): boolean
   session_diff: {
-    [sessionID: string]: SnapshotFileDiff[]
+    [sessionID: string]: FileDiffInfo[]
   }
   todo: {
     [sessionID: string]: Todo[]

+ 3 - 3
packages/app/src/context/server-session.ts

@@ -8,7 +8,7 @@ import type {
   QuestionRequest,
   Session,
   SessionStatus,
-  SnapshotFileDiff,
+  FileDiffInfo,
   Todo,
 } from "@opencode-ai/sdk/v2/client"
 import { batch } from "solid-js"
@@ -139,7 +139,7 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
   const [data, setData] = createStore({
     info: {} as Record<string, Session | undefined>,
     session_status: {} as Record<string, SessionStatus>,
-    session_diff: {} as Record<string, SnapshotFileDiff[]>,
+    session_diff: {} as Record<string, FileDiffInfo[]>,
     todo: {} as Record<string, Todo[]>,
     permission: {} as Record<string, PermissionRequest[]>,
     question: {} as Record<string, QuestionRequest[]>,
@@ -769,7 +769,7 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
         return
       }
       case "session.diff": {
-        const props = event.properties as { sessionID: string; diff: SnapshotFileDiff[] }
+        const props = event.properties as { sessionID: string; diff: FileDiffInfo[] }
         setData("session_diff", props.sessionID, reconcile(cleanDiffs(props.diff), { key: "file" }))
         return
       }

+ 2 - 2
packages/app/src/pages/session/review-tab.tsx

@@ -1,6 +1,6 @@
 import { createEffect, onCleanup, type JSX } from "solid-js"
 import { makeEventListener } from "@solid-primitives/event-listener"
-import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
+import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2"
 import { SessionReview } from "@opencode-ai/session-ui/session-review"
 import type {
   SessionReviewCommentActions,
@@ -14,7 +14,7 @@ import type { LineComment } from "@/context/comments"
 
 export type DiffStyle = "unified" | "split"
 
-type ReviewDiff = SnapshotFileDiff | VcsFileDiff
+type ReviewDiff = FileDiffInfo | VcsFileDiff
 
 export interface SessionReviewTabProps {
   title?: JSX.Element

+ 3 - 11
packages/app/src/pages/session/session-side-panel.tsx

@@ -8,7 +8,7 @@ import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
 import { Mark } from "@opencode-ai/ui/logo"
 import { DragDropProvider, DragDropSensors, DragOverlay, SortableProvider, closestCenter } from "@thisbeyond/solid-dnd"
 import type { DragEvent } from "@thisbeyond/solid-dnd"
-import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
+import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2"
 import { ConstrainDragYAxis, getDraggableId } from "@/utils/solid-dnd"
 import { useDialog } from "@opencode-ai/ui/context/dialog"
 
@@ -23,7 +23,6 @@ import { useFile, type SelectedLineRange } from "@/context/file"
 import { useLanguage } from "@/context/language"
 import { useLayout } from "@/context/layout"
 import { useSettings } from "@/context/settings"
-import { useSync } from "@/context/sync"
 import { createFileTabListSync } from "@/pages/session/file-tab-scroll"
 import { FileTabContent } from "@/pages/session/file-tabs"
 import {
@@ -36,15 +35,9 @@ import {
 import { setSessionHandoff } from "@/pages/session/handoff"
 import { useSessionLayout } from "@/pages/session/session-layout"
 
-type RenderDiff = (SnapshotFileDiff & { file: string }) | VcsFileDiff
-
-function renderDiff(value: SnapshotFileDiff | VcsFileDiff): value is RenderDiff {
-  return typeof value.file === "string"
-}
-
 export function SessionSidePanel(props: {
   canReview: () => boolean
-  diffs: () => (SnapshotFileDiff | VcsFileDiff)[]
+  diffs: () => (FileDiffInfo | VcsFileDiff)[]
   diffsReady: () => boolean
   empty: () => string
   hasReview: () => boolean
@@ -59,7 +52,6 @@ export function SessionSidePanel(props: {
 }) {
   const layout = useLayout()
   const settings = useSettings()
-  const sync = useSync()
   const file = useFile()
   const language = useLanguage()
   const command = useCommand()
@@ -88,7 +80,7 @@ export function SessionSidePanel(props: {
   })
   const treeWidth = createMemo(() => (fileOpen() ? `${layout.fileTree.width()}px` : "0px"))
 
-  const diffs = createMemo(() => props.diffs().filter(renderDiff))
+  const diffs = createMemo(() => props.diffs())
   const diffFiles = createMemo(() => diffs().map((d) => d.file))
   const kinds = createMemo(() => {
     const merge = (a: "add" | "del" | "mix" | undefined, b: "add" | "del" | "mix") => {

+ 1 - 1
packages/app/src/pages/session/timeline/message-timeline.tsx

@@ -1245,7 +1245,7 @@ export function MessageTimeline(props: {
       const value = row()
       if (value._tag !== "AssistantPart" || value.group.type !== "part") return false
       const part = getMsgPart(value.group.ref.messageID, value.group.ref.partID)
-      return part?.type === "tool" && ["edit", "write", "apply_patch"].includes(part.tool)
+      return part?.type === "tool" && ["edit", "write", "patch", "apply_patch"].includes(part.tool)
     }
     const [ready, setReady] = createSignal(initialItem.size <= timelineFallbackItemSize || !asyncFile())
     let contentMeasureFrame: number | undefined

+ 2 - 6
packages/app/src/pages/session/v2/review-diff-kinds.ts

@@ -1,17 +1,13 @@
-import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
+import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2"
 import type { Kind } from "@/components/file-tree-v2"
 import { normalizeFileTreeV2Path } from "@/components/file-tree-v2-model"
 
-export type RenderDiff = (SnapshotFileDiff & { file: string }) | VcsFileDiff
+export type RenderDiff = FileDiffInfo | VcsFileDiff
 
 export function normalizePath(p: string) {
   return normalizeFileTreeV2Path(p)
 }
 
-export function filterRenderableDiff(value: SnapshotFileDiff | VcsFileDiff): value is RenderDiff {
-  return typeof value.file === "string"
-}
-
 export function reviewDiffKinds(diffs: RenderDiff[]) {
   const merge = (a: Kind | undefined, b: Kind) => {
     if (!a) return b

+ 4 - 9
packages/app/src/pages/session/v2/review-panel-v2.tsx

@@ -1,5 +1,5 @@
 import { createMemo, createSignal, Show, type JSX } from "solid-js"
-import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
+import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2"
 import {
   SESSION_REVIEW_V2_SIDEBAR_WIDTH_MAX,
   SESSION_REVIEW_V2_SIDEBAR_WIDTH_MIN,
@@ -21,16 +21,11 @@ import type {
 import FileTreeV2 from "@/components/file-tree-v2"
 import { useLanguage } from "@/context/language"
 import { useSDK } from "@/context/sdk"
-import {
-  filterRenderableDiff,
-  filterReviewFiles,
-  reviewDiffKinds,
-  type RenderDiff,
-} from "@/pages/session/v2/review-diff-kinds"
+import { filterReviewFiles, reviewDiffKinds, type RenderDiff } from "@/pages/session/v2/review-diff-kinds"
 import type { ReviewPanelV2State } from "@/pages/session/v2/review-panel-v2-state"
 import { applyFileListKeyDown, SessionFileListV2 } from "@/pages/session/v2/session-file-list-v2"
 
-type ReviewDiff = SnapshotFileDiff | VcsFileDiff
+type ReviewDiff = FileDiffInfo | VcsFileDiff
 
 export type ReviewPanelV2Props = {
   title?: JSX.Element
@@ -54,7 +49,7 @@ export type ReviewPanelV2Props = {
 export function ReviewPanelV2(props: ReviewPanelV2Props) {
   const sdk = useSDK()
 
-  const diffs = createMemo(() => props.diffs().filter(filterRenderableDiff))
+  const diffs = createMemo(() => props.diffs())
   const filteredFiles = createMemo(() =>
     filterReviewFiles(
       diffs().map((diff) => diff.file),

+ 2 - 2
packages/app/src/utils/diffs.test.ts

@@ -1,5 +1,5 @@
 import { describe, expect, test } from "bun:test"
-import type { SnapshotFileDiff } from "@opencode-ai/sdk/v2"
+import type { FileDiffInfo } from "@opencode-ai/sdk/v2"
 import type { Message } from "@opencode-ai/sdk/v2/client"
 import { diffs, message } from "./diffs"
 
@@ -9,7 +9,7 @@ const item = {
   additions: 1,
   deletions: 1,
   status: "modified",
-} satisfies SnapshotFileDiff
+} satisfies FileDiffInfo
 
 describe("diffs", () => {
   test("keeps valid arrays", () => {

+ 2 - 2
packages/app/src/utils/diffs.ts

@@ -1,7 +1,7 @@
-import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
+import type { FileDiffInfo } from "@opencode-ai/sdk/v2"
 import type { Message } from "@opencode-ai/sdk/v2/client"
 
-type Diff = SnapshotFileDiff | VcsFileDiff
+type Diff = FileDiffInfo
 
 function diff(value: unknown): value is Diff {
   if (!value || typeof value !== "object" || Array.isArray(value)) return false

+ 1 - 0
packages/cli/bin/opencode2.cjs

@@ -46,6 +46,7 @@
     "effect": "catalog:",
     "fuzzysort": "catalog:",
     "jsonc-parser": "3.3.1",
+    "open": "10.1.2",
     "opentui-spinner": "catalog:",
     "semver": "catalog:",
     "solid-js": "catalog:",

+ 11 - 0
packages/cli/src/commands/commands.ts

@@ -75,6 +75,17 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
       description: "Debugging and troubleshooting tools",
       commands: [Spec.make("agents", { description: "List all agents" })],
     }),
+    Spec.make("console", {
+      description: "Manage OpenCode Console access",
+      commands: [
+        Spec.make("login", {
+          description: "Log in to OpenCode Console",
+          params: {
+            url: Argument.string("url").pipe(Argument.withDescription("Console server URL"), Argument.optional),
+          },
+        }),
+      ],
+    }),
     Spec.make("mcp", {
       description: "Manage MCP (Model Context Protocol) servers",
       commands: [

+ 116 - 0
packages/cli/src/commands/handlers/console/login.ts

@@ -0,0 +1,116 @@
+import { Cause, Effect, Exit, Option } from "effect"
+import { OpenCode, type OpenCodeClient } from "@opencode-ai/client/promise"
+import { AppProcess } from "@opencode-ai/core/process"
+import { Commands } from "../../commands"
+import { Runtime } from "../../../framework/runtime"
+import { Daemon } from "../../../daemon"
+import { createTimelineHost, type TimelineHost } from "../../../ui/timeline"
+
+const integrationID = "opencode"
+const location = { directory: process.cwd() }
+
+export default Runtime.handler(
+  Commands.commands.console.commands.login,
+  Effect.fn("cli.console.login")(function* (input) {
+    const timeline = yield* Effect.acquireRelease(
+      Effect.promise(() => createTimelineHost()),
+      (value) => request(() => value.close()).pipe(Effect.ignore),
+    )
+    const exit = yield* login(timeline, Option.getOrUndefined(input.url)).pipe(
+      Effect.raceFirst(AppProcess.waitForAbort(timeline.signal)),
+      Effect.exit,
+    )
+    if (Exit.isSuccess(exit)) return
+
+    const cancelled = timeline.signal.aborted
+    yield* request(() => timeline.failure(cancelled ? "Authorization cancelled" : errorMessage(exit.cause))).pipe(
+      Effect.ignore,
+    )
+    process.exitCode = cancelled ? 130 : 1
+  }),
+)
+
+const login = Effect.fn("cli.console.login.run")(function* (timeline: TimelineHost, server?: string) {
+  yield* request(() => timeline.intro("Log in"))
+  yield* request(() => timeline.pending("Connecting to OpenCode..."))
+
+  const transport = yield* Daemon.transport({ mode: "shared" })
+  const client = OpenCode.make({ baseUrl: transport.url, headers: transport.headers })
+  const found = yield* request((signal) => client.integration.get({ integrationID, location }, { signal }))
+  const integration = yield* required(found.data, "OpenCode Console integration is unavailable")
+  const method = yield* required(
+    integration.methods.find((candidate) => candidate.type === "oauth"),
+    "OpenCode Console login is unavailable",
+  )
+
+  yield* request(() => timeline.pending("Starting authorization..."))
+  const started = yield* request((signal) =>
+    client.integration.connect.oauth(
+      {
+        integrationID,
+        methodID: method.id,
+        inputs: server ? { server } : {},
+        location,
+      },
+      { signal },
+    ),
+  )
+  const attempt = started.data
+  yield* Effect.addFinalizer(() =>
+    request(() =>
+      client.integration.attempt.cancel(
+        { attemptID: attempt.attemptID, location },
+        { signal: AbortSignal.timeout(5_000) },
+      ),
+    ).pipe(Effect.ignore),
+  )
+  if (attempt.mode !== "auto") yield* Effect.fail(new Error("OpenCode Console requires a device login"))
+
+  yield* request(() => timeline.item(`Go to: ${attempt.url}`))
+  yield* request(() => timeline.item(attempt.instructions))
+  yield* request(async () => {
+    const { default: open } = await import("open")
+    await open(attempt.url)
+  }).pipe(Effect.ignore)
+  yield* request(() => timeline.pending("Waiting for authorization..."))
+
+  const status = yield* waitForConsoleLogin(client, attempt.attemptID)
+  if (status.status === "failed") yield* Effect.fail(new Error(status.message))
+  if (status.status === "expired") yield* Effect.fail(new Error("Device code expired"))
+
+  yield* request(() => timeline.success("Connected to OpenCode Console"))
+  yield* request(() => timeline.outro("Done"))
+})
+
+const waitForConsoleLogin = Effect.fn("cli.console.login.wait")(function* (
+  client: OpenCodeClient,
+  attemptID: string,
+) {
+  while (true) {
+    const response = yield* request((signal) =>
+      client.integration.attempt.status({ attemptID, location }, { signal }),
+    )
+    if (response.data.status !== "pending") return response.data
+    yield* Effect.sleep(500)
+  }
+})
+
+function request<A>(task: (signal: AbortSignal) => Promise<A>) {
+  return Effect.tryPromise({
+    try: task,
+    catch: (cause) => cause,
+  })
+}
+
+function required<A>(value: A | null | undefined, message: string) {
+  return value === null || value === undefined ? Effect.fail(new Error(message)) : Effect.succeed(value)
+}
+
+function errorMessage(cause: Cause.Cause<unknown>) {
+  const error = Cause.squash(cause)
+  if (error instanceof Error) return error.message
+  if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
+    return error.message
+  }
+  return String(error)
+}

+ 3 - 0
packages/cli/src/index.ts

@@ -24,6 +24,9 @@ const Handlers = Runtime.handlers(Commands, {
   debug: {
     agents: () => import("./commands/handlers/debug/agents"),
   },
+  console: {
+    login: () => import("./commands/handlers/console/login"),
+  },
   mcp: {
     list: () => import("./commands/handlers/mcp/list"),
     add: () => import("./commands/handlers/mcp/add"),

+ 3 - 2
packages/cli/src/mini/catalog.shared.ts

@@ -37,7 +37,8 @@ function defaultCost(model: CurrentModel) {
 
 export function runAgent(input: CurrentAgent): RunAgent {
   return {
-    name: input.id,
+    id: input.id,
+    name: input.name,
     description: input.description,
     mode: input.mode,
     hidden: input.hidden,
@@ -53,7 +54,7 @@ export function runCommand(input: CurrentCommand): RunCommand {
 
 export function runSkill(input: CurrentSkill): RunCommand {
   return {
-    name: input.name,
+    name: input.id,
     description: input.description,
     source: "skill",
   }

+ 2 - 2
packages/cli/src/mini/demo.ts

@@ -628,11 +628,11 @@ function emitEdit(state: State): void {
 
 function emitPatch(state: State): void {
   const file = path.join(process.cwd(), "src", "demo-format.ts")
-  const ref = make(state, "apply_patch", {
+  const ref = make(state, "patch", {
     patchText: "*** Begin Patch\n*** End Patch",
   })
   doneTool(state, ref, {
-    title: "apply_patch",
+    title: "patch",
     output: "",
     metadata: {
       files: [

+ 2 - 2
packages/cli/src/mini/footer.prompt.tsx

@@ -333,10 +333,10 @@ export function createPromptState(input: PromptInput): PromptState {
       .map((item) => ({
         kind: "mention",
         display: "@" + item.name,
-        value: item.name,
+        value: item.id,
         part: {
           type: "agent",
-          name: item.name,
+          name: item.id,
           source: {
             start: 0,
             end: 0,

+ 0 - 2
packages/cli/src/mini/footer.ts

@@ -84,7 +84,6 @@ type RunFooterOptions = {
   theme: RunTheme
   keymap: Keymap<Renderable, KeyEvent>
   tuiConfig: RunTuiConfig
-  backgroundSubagents: boolean
   diffStyle: RunDiffStyle
   onPermissionReply: (input: PermissionReply) => void | Promise<void>
   onQuestionReply: (input: QuestionReply) => void | Promise<void>
@@ -326,7 +325,6 @@ export class RunFooter implements FooterApi {
               theme: footer.theme,
               diffStyle: options.diffStyle,
               tuiConfig: options.tuiConfig,
-              backgroundSubagents: options.backgroundSubagents,
               history: footer.history,
               agent: options.agentLabel,
               onSubmit: footer.handlePrompt,

+ 1 - 4
packages/cli/src/mini/footer.view.tsx

@@ -89,7 +89,6 @@ type RunFooterViewProps = {
   theme: () => RunTheme
   diffStyle?: RunDiffStyle
   tuiConfig: RunTuiConfig
-  backgroundSubagents: boolean
   history?: () => RunPrompt[]
   agent: string
   onSubmit: (input: RunPrompt) => boolean
@@ -169,9 +168,7 @@ export function RunFooterView(props: RunFooterViewProps) {
 
     return tabs().findIndex((item) => item.sessionID === sessionID) + 1
   })
-  const foregroundSubagents = createMemo(
-    () => props.backgroundSubagents && activeTabs().some((item) => !item.background),
-  )
+  const foregroundSubagents = createMemo(() => activeTabs().some((item) => !item.background))
   const model = createMemo(() => {
     const current = props.currentModel()
     return current ? modelInfo(props.providers(), current) : { model: props.state().model, provider: undefined }

+ 0 - 4
packages/cli/src/mini/mini.ts

@@ -1,6 +1,5 @@
 import { NodeFileSystem } from "@effect/platform-node"
 import { OpenCode, type OpenCodeClient } from "@opencode-ai/client/promise"
-import { truthy } from "@opencode-ai/core/flag/flag"
 import { Global } from "@opencode-ai/core/global"
 import { Effect } from "effect"
 import path from "node:path"
@@ -84,9 +83,6 @@ export async function runMini(input: MiniCommandInput) {
       files: [],
       initialInput,
       thinking: true,
-      backgroundSubagents:
-        truthy("OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS") ||
-        (process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS === undefined && truthy("OPENCODE_EXPERIMENTAL")),
       replay: input.replay ?? true,
       replayLimit: input.replayLimit,
       demo: input.demo,

+ 0 - 1
packages/cli/src/mini/noninteractive.ts

@@ -281,7 +281,6 @@ export async function runNonInteractivePrompt(input: Input) {
             metadata: {
               structured: event.data.structured,
               content: event.data.content,
-              outputPaths: event.data.outputPaths,
               result: event.data.result,
               providerCall: current.provider,
               providerResult: { executed: event.data.executed, state: event.data.resultState },

+ 5 - 8
packages/cli/src/mini/run.ts

@@ -89,12 +89,11 @@ async function execute(input: RunCommandInput, prepared: Prepared, transport: Tr
     !explicitModel && !sessionModel
       ? await client.model
           .default({ location: { directory: cwd, workspace } })
-          .then((result) =>
-            result.data ? { providerID: result.data.providerID, modelID: result.data.id } : undefined,
-          )
+          .then((result) => (result.data ? { providerID: result.data.providerID, modelID: result.data.id } : undefined))
       : undefined
   const model = pickRunModel(explicitModel, input.variant, sessionModel, defaultModel)
-  if (input.variant && !model) return reportError(input, "Cannot select a variant before selecting a model", session?.id)
+  if (input.variant && !model)
+    return reportError(input, "Cannot select a variant before selecting a model", session?.id)
   if (model) {
     await waitForCatalogReady({ sdk: client, directory: cwd, workspace, model })
     const available = await client.model.list({ location: { directory: cwd, workspace } })
@@ -112,9 +111,7 @@ async function execute(input: RunCommandInput, prepared: Prepared, transport: Tr
   if (!session && input.title !== undefined) {
     await client.session.rename({
       sessionID: selected.id,
-      title:
-        input.title ||
-        prepared.message.slice(0, 50) + (prepared.message.length > 50 ? "..." : ""),
+      title: input.title || prepared.message.slice(0, 50) + (prepared.message.length > 50 ? "..." : ""),
     })
   }
 
@@ -200,7 +197,7 @@ async function validateAgent(client: OpenCodeClient, directory: string, name?: s
     warning(`failed to list agents${server ? ` from ${server}` : ""}. Falling back to default agent`)
     return
   }
-  const agent = agents.find((item) => item.name === name)
+  const agent = agents.find((item) => item.id === name)
   if (!agent) {
     warning(`agent "${name}" not found. Falling back to default agent`)
     return

+ 0 - 2
packages/cli/src/mini/runtime.lifecycle.ts

@@ -64,7 +64,6 @@ export type LifecycleInput = {
   model: RunInput["model"]
   variant: string | undefined
   tuiConfig: RunTuiConfig | Promise<RunTuiConfig>
-  backgroundSubagents: boolean
   onPermissionReply: (input: PermissionReply) => void | Promise<void>
   onQuestionReply: (input: QuestionReply) => void | Promise<void>
   onQuestionReject: (input: QuestionReject) => void | Promise<void>
@@ -236,7 +235,6 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
       wrote,
       keymap,
       tuiConfig,
-      backgroundSubagents: input.backgroundSubagents,
       diffStyle: tuiConfig.diff_style ?? "auto",
       onPermissionReply: input.onPermissionReply,
       onQuestionReply: input.onQuestionReply,

+ 0 - 5
packages/cli/src/mini/runtime.ts

@@ -56,7 +56,6 @@ type RunRuntimeInput = {
   files: RunInput["files"]
   initialInput?: string
   thinking: boolean
-  backgroundSubagents: boolean
   replay?: boolean
   replayLimit?: number
   demo?: RunInput["demo"]
@@ -75,7 +74,6 @@ type RunDeferredInput = {
   files: RunInput["files"]
   initialInput?: string
   thinking: boolean
-  backgroundSubagents: boolean
   replay?: boolean
   replayLimit?: number
   demo?: RunInput["demo"]
@@ -270,7 +268,6 @@ async function runInteractiveRuntime(input: RunRuntimeInput, deps: RunRuntimeDep
     model: state.model,
     variant: state.activeVariant,
     tuiConfig: tuiConfigTask,
-    backgroundSubagents: input.backgroundSubagents,
     onPermissionReply: async (next) => {
       if (state.demo?.permission(next)) {
         return
@@ -876,7 +873,6 @@ export async function runInteractiveDeferredMode(input: RunDeferredInput, deps?:
       files: input.files,
       initialInput: input.initialInput,
       thinking: input.thinking,
-      backgroundSubagents: input.backgroundSubagents,
       replay: input.replay,
       replayLimit: input.replayLimit,
       demo: input.demo,
@@ -928,7 +924,6 @@ export async function runInteractiveMode(
       files: input.files,
       initialInput: input.initialInput,
       thinking: input.thinking,
-      backgroundSubagents: input.backgroundSubagents,
       replay: input.replay,
       replayLimit: input.replayLimit,
       demo: input.demo,

+ 6 - 8
packages/cli/src/mini/stream-v2.subagent.ts

@@ -16,7 +16,7 @@
 // backgrounding is intentionally absent: subagent jobs block the parent
 // session, so only whole-session `v2.session.background(parentID)` exists.
 import type { EventSubscribeOutput, OpenCodeClient } from "@opencode-ai/client/promise"
-import type { SessionMessage, SessionMessageAssistantTool, ToolPart } from "@opencode-ai/sdk/v2"
+import type { SessionMessageAssistantTool, SessionMessageInfo, ToolPart } from "@opencode-ai/sdk/v2"
 import { Locale } from "@opencode-ai/tui/util/locale"
 import type { FooterSubagentDetail, FooterSubagentState, FooterSubagentTab, StreamCommit } from "./types"
 
@@ -54,7 +54,7 @@ export function legacyTool(input: {
     callID: tool.id,
     tool: tool.name,
   }
-  if (tool.state.status === "pending") {
+  if (tool.state.status === "streaming") {
     return {
       ...base,
       state: { status: "pending", input: {}, raw: tool.state.input },
@@ -83,7 +83,6 @@ export function legacyTool(input: {
         metadata: {
           structured: tool.state.structured,
           content: tool.state.content,
-          outputPaths: tool.state.outputPaths,
           result: tool.state.result,
           providerCall,
           providerResult,
@@ -179,7 +178,7 @@ export type SubagentTrackerInput = {
 export type SubagentTracker = {
   main(event: V2Event): void
   foreign(sessionID: string, event: V2Event): void
-  hydrate(next: { messages: SessionMessage[]; active: Record<string, unknown> }): Promise<void>
+  hydrate(next: { messages: SessionMessageInfo[]; active: Record<string, unknown> }): Promise<void>
   select(sessionID: string | undefined): void
   snapshot(): FooterSubagentState
 }
@@ -316,7 +315,7 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
       messageID,
       tool: item,
     })
-    if (item.state.status === "pending") return
+    if (item.state.status === "streaming") return
     child.callIDs.add(item.id)
     if (item.state.status === "running") {
       setFrame(child, `tool:${item.id}`, toolCommit(part, "start"))
@@ -327,7 +326,7 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
     setFrame(child, `tool:${item.id}`, toolCommit(part, "final"))
   }
 
-  const rebuild = (child: ChildState, messages: SessionMessage[]) => {
+  const rebuild = (child: ChildState, messages: SessionMessageInfo[]) => {
     child.frames = []
     child.text.clear()
     child.projectedText.clear()
@@ -412,7 +411,7 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
         for (const [id, prompt] of pendingPrompts) {
           if (!child.prompts.has(id)) child.prompts.set(id, prompt)
         }
-        rebuild(child, structuredClone(response.data).toReversed() as SessionMessage[])
+        rebuild(child, structuredClone(response.data).toReversed() as SessionMessageInfo[])
         for (const [id, tool] of pendingTools) {
           if (!child.finishedTools.has(id) && !child.tools.has(id)) child.tools.set(id, tool)
         }
@@ -622,7 +621,6 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
                 input: current?.input ?? {},
                 structured: event.data.structured,
                 content: event.data.content,
-                outputPaths: event.data.outputPaths,
                 result: event.data.result,
               },
           time: {

+ 15 - 16
packages/cli/src/mini/stream-v2.transport.ts

@@ -2,7 +2,7 @@ import type { EventSubscribeOutput, OpenCodeClient } from "@opencode-ai/client/p
 import type {
   PermissionRequest,
   QuestionRequest,
-  SessionMessage,
+  SessionMessageInfo,
   SessionMessageAssistantTool,
 } from "@opencode-ai/sdk/v2"
 import { Event } from "@opencode-ai/schema/event"
@@ -389,7 +389,7 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
       messageID,
       tool: item,
     })
-    if (item.state.status === "pending") return
+    if (item.state.status === "streaming") return
     if (item.state.status === "running") {
       if (state.tools.get(item.id)?.running) return
       state.tools.set(item.id, {
@@ -417,7 +417,7 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
     ])
   }
 
-  const renderMessage = (message: SessionMessage, render: boolean, reuseVisibleWait: boolean) => {
+  const renderMessage = (message: SessionMessageInfo, render: boolean, reuseVisibleWait: boolean) => {
     if (message.type === "user") {
       const waiting = state.wait?.messageID === message.id
       if (waiting && state.wait) state.wait.promoted = true
@@ -438,34 +438,34 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
       return
     }
     if (message.type === "shell") {
-      state.shellCommands.set(message.shell.id, message.shell.command)
-      if (state.shellWait?.messageID === message.id) state.shellWait.callID = message.shell.id
+      state.shellCommands.set(message.shellID, message.command)
+      if (state.shellWait?.messageID === message.id) state.shellWait.callID = message.shellID
       const completed = message.time.completed !== undefined
       if (!render) {
         // Suppressed history: mark settled shells rendered so live redelivery
         // stays silent. A still-running shell stays unmarked and renders in
         // full when its live shell.ended event arrives.
         if (completed) {
-          state.shellStarted.add(message.shell.id)
-          state.shellEnded.add(message.shell.id)
+          state.shellStarted.add(message.shellID)
+          state.shellEnded.add(message.shellID)
         }
         return
       }
-      if (!state.shellStarted.has(message.shell.id)) {
-        state.shellStarted.add(message.shell.id)
+      if (!state.shellStarted.has(message.shellID)) {
+        state.shellStarted.add(message.shellID)
         write([
-          shellCommit(message.shell.id, message.shell.command, {
+          shellCommit(message.shellID, message.command, {
             text: "running shell",
             phase: "start",
             toolState: "running",
           }),
         ])
       }
-      if (completed && message.output && !state.shellEnded.has(message.shell.id)) {
-        state.shellEnded.add(message.shell.id)
-        write(shellTerminal(message.shell.id, message.shell.command, message.shell, message.output))
+      if (completed && message.output && !state.shellEnded.has(message.shellID)) {
+        state.shellEnded.add(message.shellID)
+        write(shellTerminal(message.shellID, message.command, message, message.output))
       }
-      if (completed && state.shellWait?.callID === message.shell.id) state.shellWait.resolve()
+      if (completed && state.shellWait?.callID === message.shellID) state.shellWait.resolve()
       return
     }
     if (message.type !== "assistant") return
@@ -534,7 +534,7 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
       input.sdk.question.list({ sessionID: input.sessionID }),
       input.sdk.session.active(),
     ])
-    const projected = structuredClone(messages.data).toReversed() as SessionMessage[]
+    const projected = structuredClone(messages.data).toReversed() as SessionMessageInfo[]
     for (const message of projected) renderMessage(message, next.render, next.reuseVisibleWait)
     state.permissions = permissions.map(permission)
     state.questions = questions.map(question)
@@ -762,7 +762,6 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
               input: current?.input ?? {},
               structured: event.data.structured,
               content: event.data.content,
-              outputPaths: event.data.outputPaths,
               result: event.data.result,
             },
         time: { created: current?.started ?? event.created, ran: current?.started, completed: event.created },

+ 2 - 2
packages/cli/src/mini/tool.ts

@@ -119,7 +119,7 @@ type ToolName =
   | "bash"
   | "write"
   | "edit"
-  | "apply_patch"
+  | "patch"
   | "batch"
   | "task"
   | "todowrite"
@@ -1094,7 +1094,7 @@ const TOOL_RULES = {
     },
     permission: permEdit,
   },
-  apply_patch: {
+  patch: {
     view: {
       output: false,
       final: true,

+ 1 - 1
packages/cli/src/mini/types.ts

@@ -113,6 +113,7 @@ export type FooterQueuedPrompt = {
 }
 
 export type RunAgent = {
+  id: string
   name: string
   description?: string
   mode: "subagent" | "primary" | "all"
@@ -135,7 +136,6 @@ export type RunInput = {
   files: RunFilePart[]
   initialInput?: string
   thinking: boolean
-  backgroundSubagents: boolean
   demo?: boolean
 }
 

+ 26 - 11
packages/cli/src/server-process.ts

@@ -7,11 +7,11 @@ import { LayerNode } from "@opencode-ai/core/effect/layer-node"
 import { Global } from "@opencode-ai/core/global"
 import { InstallationVersion } from "@opencode-ai/core/installation/version"
 import { AppProcess } from "@opencode-ai/core/process"
-import { Flock } from "@opencode-ai/core/util/flock"
+import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
 import { start } from "@opencode-ai/server/process"
 import { randomBytes, randomUUID } from "node:crypto"
 import path from "node:path"
-import { Effect, FileSystem, Logger, Option, Redacted, Schedule, Schema } from "effect"
+import { Effect, Exit, FileSystem, Logger, Option, Redacted, Schedule, Schema, Scope } from "effect"
 import { HttpServer } from "effect/unstable/http"
 import { Env } from "./env"
 import { ServiceConfig } from "./services/service-config"
@@ -28,7 +28,7 @@ export type Options = {
 export const run = Effect.fn("cli.server-process.run")((options: Options) =>
   processEffect(options).pipe(
     Effect.provide(Updater.layer),
-    Effect.provide(AppNodeBuilder.build(LayerNode.group([Global.node, AppProcess.node]))),
+    Effect.provide(AppNodeBuilder.build(LayerNode.group([Global.node, AppProcess.node, EffectFlock.node]))),
     Effect.provide(NodeServices.layer),
   ),
 )
@@ -37,13 +37,15 @@ const processEffect = Effect.fnUntraced(function* (options: Options) {
   if (options.mode === "service") yield* Effect.sync(() => process.chdir(Global.Path.home))
   return yield* Effect.scoped(
     Effect.gen(function* () {
-      if (options.mode === "service") {
-        const service = yield* ServiceConfig.options()
-        yield* Flock.effect(path.basename(service.file, ".json") + "-process", {
-          dir: path.dirname(service.file),
-          staleMs: 3_000,
-          timeoutMs: 15_000,
-        })
+      const serviceOptions = options.mode === "service" ? yield* ServiceConfig.options() : undefined
+      const lockScope = serviceOptions === undefined ? undefined : yield* acquireServiceLock(serviceOptions.file)
+      if (
+        serviceOptions !== undefined &&
+        lockScope !== undefined &&
+        (yield* Service.discover(serviceOptions)) !== undefined
+      ) {
+        yield* Scope.close(lockScope, Exit.void)
+        return
       }
       const environmentPassword = yield* Env.password
       // Keep the lease credential out of the environment inherited by tools.
@@ -64,7 +66,10 @@ const processEffect = Effect.fnUntraced(function* (options: Options) {
         port: Option.fromNullishOr(options.port ?? config.port),
         password,
       }).pipe(Effect.provide(Logger.layer([], { mergeWithExisting: false })))
-      if (options.mode === "service") yield* register(address, password)
+      if (lockScope !== undefined) {
+        yield* register(address, password)
+        yield* Scope.close(lockScope, Exit.void)
+      }
       const url = HttpServer.formatAddress(address)
       console.log(options.mode === "stdio" ? JSON.stringify({ url }) : `server listening on ${url}`)
       if (options.mode === "default" && !environmentPassword) console.log(`server password ${password}`)
@@ -75,6 +80,16 @@ const processEffect = Effect.fnUntraced(function* (options: Options) {
   )
 })
 
+const acquireServiceLock = Effect.fnUntraced(function* (file: string) {
+  const flock = yield* EffectFlock.Service
+  const scope = yield* Scope.make()
+  yield* Effect.addFinalizer((exit) => Scope.close(scope, exit))
+  yield* flock
+    .acquire(`service:${file}`, undefined, { staleMs: 3_000, timeoutMs: 3_000 })
+    .pipe(Effect.provideService(Scope.Scope, scope))
+  return scope
+})
+
 // The latest atomic registration wins. A displaced process notices the new id,
 // exits, and cannot remove its successor's registration from its finalizer.
 const infoJson = Schema.fromJsonString(Service.Info)

+ 12 - 0
packages/cli/src/tui.ts

@@ -18,6 +18,7 @@ export function runTui(
   const config = TuiConfig.resolve({}, { terminalSuspend: false })
   let disposeSlots: (() => void) | undefined
   return Effect.gen(function* () {
+    const runFork = Effect.runForkWith(yield* Effect.context())
     const options = { baseUrl: transport.url, headers: transport.headers }
     const api = OpenCode.make(options)
     const directory = yield* Effect.tryPromise(() => api.file.list({ location: { directory: process.cwd() } })).pipe(
@@ -41,6 +42,17 @@ export function runTui(
       reload,
       args,
       config,
+      log: (level, message, tags) => {
+        const effect =
+          level === "debug"
+            ? Effect.logDebug(message, tags)
+            : level === "warn"
+              ? Effect.logWarning(message, tags)
+              : level === "error"
+                ? Effect.logError(message, tags)
+                : Effect.logInfo(message, tags)
+        runFork(effect)
+      },
       pluginHost: {
         async start(input) {
           disposeSlots = await loadBuiltinPlugins(input.api, input.runtime)

+ 242 - 0
packages/cli/src/ui/timeline.tsx

@@ -0,0 +1,242 @@
+/** @jsxImportSource @opentui/solid */
+import { createCliRenderer, RGBA, type CliRenderer, type ColorInput, type ScrollbackWriter } from "@opentui/core"
+import { createScrollbackWriter, render, useKeyboard } from "@opentui/solid"
+import { registerOpencodeSpinner } from "@opencode-ai/tui/component/register-spinner"
+import { Show, createSignal } from "solid-js"
+
+registerOpencodeSpinner()
+
+export type TimelineHost = {
+  readonly signal: AbortSignal
+  intro(text: string): Promise<void>
+  item(text: string): Promise<void>
+  pending(text: string): Promise<void>
+  success(text: string): Promise<void>
+  failure(text: string): Promise<void>
+  outro(text: string): Promise<void>
+  close(): Promise<void>
+}
+
+type RowKind = "intro" | "item" | "success" | "failure" | "outro"
+
+const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
+const IDLE_TIMEOUT = 1_000
+const COLORS = {
+  accent: RGBA.fromIndex(6),
+  error: RGBA.fromIndex(1),
+  foreground: RGBA.defaultForeground(),
+  muted: RGBA.fromIndex(8),
+  success: RGBA.fromIndex(2),
+}
+const ROWS: Record<RowKind, { marker: string; color: ColorInput; connector: boolean }> = {
+  intro: { marker: "┌", color: COLORS.muted, connector: true },
+  item: { marker: "●", color: COLORS.accent, connector: true },
+  success: { marker: "◇", color: COLORS.success, connector: true },
+  failure: { marker: "■", color: COLORS.error, connector: false },
+  outro: { marker: "└", color: COLORS.muted, connector: false },
+}
+
+function row(kind: RowKind, value: string): ScrollbackWriter {
+  const style = ROWS[kind]
+  return createScrollbackWriter(
+    () => (
+      <box width="100%" minHeight={1} flexDirection="column">
+        <box width="100%" minHeight={1} flexDirection="row" gap={1}>
+          <text fg={style.color} flexShrink={0}>
+            {style.marker}
+          </text>
+          <text fg={COLORS.foreground} wrapMode="word">
+            {value}
+          </text>
+        </box>
+        <Show when={style.connector}>
+          <text fg={COLORS.muted}>│</text>
+        </Show>
+      </box>
+    ),
+    { startOnNewLine: true, trailingNewline: !style.connector },
+  )
+}
+
+function TimelineFooter(props: { pending: () => string | undefined; cancel: () => void }) {
+  useKeyboard((event) => {
+    if (event.name !== "escape" && !(event.ctrl && event.name === "c")) return
+    event.preventDefault()
+    props.cancel()
+  })
+
+  return (
+    <box width="100%" height={1} flexDirection="row" gap={1}>
+      <Show when={props.pending()}>
+        {(text) => (
+          <>
+            <spinner frames={SPINNER_FRAMES} interval={80} color={COLORS.accent} />
+            <text fg={COLORS.foreground} wrapMode="none" truncate>
+              {text()}
+            </text>
+          </>
+        )}
+      </Show>
+    </box>
+  )
+}
+
+function bounded(task: Promise<unknown>) {
+  return new Promise<void>((resolve) => {
+    const timer = setTimeout(resolve, IDLE_TIMEOUT)
+    timer.unref()
+    const finish = () => {
+      clearTimeout(timer)
+      resolve()
+    }
+    void task.then(finish, finish)
+  })
+}
+
+async function shutdown(renderer: CliRenderer): Promise<void> {
+  await bounded(renderer.idle())
+  try {
+    renderer.externalOutputMode = "passthrough"
+  } finally {
+    try {
+      renderer.screenMode = "main-screen"
+    } finally {
+      if (!renderer.isDestroyed) renderer.destroy()
+    }
+  }
+}
+
+export async function createTimelineHost(): Promise<TimelineHost> {
+  const stdout = process.stdout
+  const controller = new AbortController()
+  const signals: NodeJS.Signals[] = ["SIGINT", "SIGHUP", "SIGQUIT"]
+  const cancel = () => {
+    if (!controller.signal.aborted) controller.abort()
+  }
+  signals.forEach((signal) => process.on(signal, cancel))
+
+  if (!stdout.isTTY || !process.stdin.isTTY) {
+    let closed = false
+    let writing = false
+    let active: Promise<void> | undefined
+    let closeTask: Promise<void> | undefined
+    const write = async (kind: RowKind | "pending", text: string) => {
+      if (closed) throw new Error("timeline closed")
+      if (writing) throw new Error("timeline write already in progress")
+      writing = true
+      try {
+        const style = kind === "pending" ? undefined : ROWS[kind]
+        const marker = kind === "pending" ? "." : ROWS[kind].marker
+        const connector = style?.connector ? "│\n" : ""
+        active = new Promise<void>((resolve, reject) => {
+          stdout.write(`${marker} ${text}\n${connector}`, (error) => (error ? reject(error) : resolve()))
+        })
+        await active
+      } finally {
+        writing = false
+        active = undefined
+      }
+    }
+    const close = () => {
+      if (closeTask) return closeTask
+      closed = true
+      closeTask = (async () => {
+        await active?.catch(() => { })
+        signals.forEach((signal) => process.off(signal, cancel))
+      })()
+      return closeTask
+    }
+    return {
+      signal: controller.signal,
+      intro: (text) => write("intro", text),
+      item: (text) => write("item", text),
+      pending: (text) => write("pending", text),
+      success: (text) => write("success", text),
+      failure: (text) => write("failure", text),
+      outro: (text) => write("outro", text),
+      close,
+    }
+  }
+
+  let renderer: CliRenderer | undefined
+
+  try {
+    // Start on a fresh row so delayed SSH cursor reports cannot make
+    // split-footer overwrite the shell command.
+    process.stdout.write("\n")
+    renderer = await createCliRenderer({
+      stdin: process.stdin,
+      useMouse: false,
+      autoFocus: false,
+      openConsoleOnError: false,
+      exitOnCtrlC: false,
+      exitSignals: [],
+      screenMode: "split-footer",
+      footerHeight: 1,
+      externalOutputMode: "capture-stdout",
+      consoleMode: "disabled",
+      clearOnShutdown: false,
+    })
+    const activeRenderer = renderer
+    const [pending, setPending] = createSignal<string>()
+    const renderTask = render(() => <TimelineFooter pending={pending} cancel={cancel} />, activeRenderer)
+    void renderTask.catch(cancel)
+    await bounded(activeRenderer.idle())
+
+    let closed = false
+    let writing = false
+    let active: Promise<void> | undefined
+    let closeTask: Promise<void> | undefined
+    const write = (kind: RowKind | "pending", text: string) => {
+      if (closed) return Promise.reject(new Error("timeline closed"))
+      if (writing) return Promise.reject(new Error("timeline write already in progress"))
+      writing = true
+      active = (async () => {
+        if (kind === "pending") {
+          setPending(text)
+          activeRenderer.requestRender()
+        } else {
+          if (kind === "success" || kind === "failure" || kind === "outro") setPending(undefined)
+          activeRenderer.writeToScrollback(row(kind, text))
+          activeRenderer.requestRender()
+        }
+        await bounded(activeRenderer.idle())
+      })().finally(() => {
+        writing = false
+        active = undefined
+      })
+      return active
+    }
+    const close = () => {
+      if (closeTask) return closeTask
+      closed = true
+      closeTask = (async () => {
+        await active?.catch(() => { })
+        try {
+          await shutdown(activeRenderer)
+          await bounded(renderTask)
+        } finally {
+          signals.forEach((signal) => process.off(signal, cancel))
+        }
+      })()
+      return closeTask
+    }
+    return {
+      signal: controller.signal,
+      intro: (text) => write("intro", text),
+      item: (text) => write("item", text),
+      pending: (text) => write("pending", text),
+      success: (text) => write("success", text),
+      failure: (text) => write("failure", text),
+      outro: (text) => write("outro", text),
+      close,
+    }
+  } catch (error) {
+    try {
+      if (renderer) await shutdown(renderer)
+    } finally {
+      signals.forEach((signal) => process.off(signal, cancel))
+    }
+    throw error
+  }
+}

+ 46 - 1
packages/cli/test/service.test.ts

@@ -1,7 +1,8 @@
 import { NodeFileSystem } from "@effect/platform-node"
+import { Service } from "@opencode-ai/client/effect"
 import { Global } from "@opencode-ai/core/global"
 import { expect, test } from "bun:test"
-import { Effect } from "effect"
+import { Effect, Schema } from "effect"
 import fs from "node:fs/promises"
 import os from "node:os"
 import path from "node:path"
@@ -24,3 +25,47 @@ test("local channel stores service config with the local service filename", asyn
     await fs.rm(root, { recursive: true, force: true })
   }
 })
+
+test("concurrent service processes elect one server", async () => {
+  const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-election-"))
+  const env = {
+    ...process.env,
+    HOME: root,
+    OPENCODE_DB: path.join(root, "opencode.db"),
+    OPENCODE_TEST_HOME: root,
+    XDG_CACHE_HOME: path.join(root, "cache"),
+    XDG_CONFIG_HOME: path.join(root, "config"),
+    XDG_DATA_HOME: path.join(root, "data"),
+    XDG_STATE_HOME: path.join(root, "state"),
+  }
+  const command = [process.execPath, path.join(import.meta.dir, "../src/index.ts"), "serve", "--service"]
+  const first = Bun.spawn(command, { env, stderr: "pipe", stdout: "ignore" })
+  const second = Bun.spawn(command, { env, stderr: "pipe", stdout: "ignore" })
+
+  try {
+    const registration = path.join(root, "state", "opencode", "service-local.json")
+    const info = await waitForInfo(registration)
+    const winner = info.pid === first.pid ? first : second
+    const loser = info.pid === first.pid ? second : first
+    const exited = await Promise.race([loser.exited.then(() => true), Bun.sleep(10_000).then(() => false)])
+
+    expect(exited).toBe(true)
+    expect(winner.exitCode).toBe(null)
+  } finally {
+    first.kill("SIGTERM")
+    second.kill("SIGTERM")
+    await Promise.all([first.exited, second.exited])
+    await fs.rm(root, { recursive: true, force: true })
+  }
+})
+
+async function waitForInfo(file: string) {
+  for (let attempt = 0; attempt < 200; attempt++) {
+    const value = await Bun.file(file)
+      .json()
+      .catch(() => undefined)
+    if (value !== undefined) return Schema.decodeUnknownPromise(Service.Info)(value)
+    await Bun.sleep(50)
+  }
+  throw new Error("Timed out waiting for service registration")
+}

+ 2 - 3
packages/client/script/build.ts

@@ -3,15 +3,14 @@ import { compile, emitEffectImported, emitEffectShape, emitPromise, write } from
 import {
   ClientApi,
   effectOmitEndpoints,
-  endpointNames,
   groupNames,
   promiseOmitEndpoints,
 } from "@opencode-ai/protocol/client"
 import { Effect } from "effect"
 import { fileURLToPath } from "url"
 
-const promiseContract = compile(ClientApi, { groupNames, endpointNames, omitEndpoints: promiseOmitEndpoints })
-const effectContract = compile(ClientApi, { groupNames, endpointNames, omitEndpoints: effectOmitEndpoints })
+const promiseContract = compile(ClientApi, { groupNames, omitEndpoints: promiseOmitEndpoints })
+const effectContract = compile(ClientApi, { groupNames, omitEndpoints: effectOmitEndpoints })
 
 await Effect.runPromise(
   Effect.all(

+ 0 - 1
packages/client/src/contract.ts

@@ -1,7 +1,6 @@
 export {
   ClientApi,
   effectOmitEndpoints,
-  endpointNames,
   groupNames,
   promiseOmitEndpoints,
 } from "@opencode-ai/protocol/client"

+ 142 - 113
packages/client/src/effect/api/api.ts

@@ -111,157 +111,167 @@ export type Endpoint4_8Input = {
 export type Endpoint4_8Output = EffectValue<ReturnType<RawClient["server.session"]["session.rename"]>>
 export type SessionRenameOperation<E = never> = (input: Endpoint4_8Input) => Effect.Effect<Endpoint4_8Output, E>
 
-type Endpoint4_9Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
+type Endpoint4_9Request = Parameters<RawClient["server.session"]["session.move"]>[0]
 export type Endpoint4_9Input = {
   readonly sessionID: Endpoint4_9Request["params"]["sessionID"]
-  readonly id?: Endpoint4_9Request["payload"]["id"]
-  readonly prompt: Endpoint4_9Request["payload"]["prompt"]
-  readonly delivery?: Endpoint4_9Request["payload"]["delivery"]
-  readonly resume?: Endpoint4_9Request["payload"]["resume"]
+  readonly destination: Endpoint4_9Request["payload"]["destination"]
+  readonly moveChanges?: Endpoint4_9Request["payload"]["moveChanges"]
 }
-export type Endpoint4_9Output = EffectValue<ReturnType<RawClient["server.session"]["session.prompt"]>>["data"]
-export type SessionPromptOperation<E = never> = (input: Endpoint4_9Input) => Effect.Effect<Endpoint4_9Output, E>
+export type Endpoint4_9Output = EffectValue<ReturnType<RawClient["server.session"]["session.move"]>>
+export type SessionMoveOperation<E = never> = (input: Endpoint4_9Input) => Effect.Effect<Endpoint4_9Output, E>
 
-type Endpoint4_10Request = Parameters<RawClient["server.session"]["session.command"]>[0]
+type Endpoint4_10Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
 export type Endpoint4_10Input = {
   readonly sessionID: Endpoint4_10Request["params"]["sessionID"]
   readonly id?: Endpoint4_10Request["payload"]["id"]
-  readonly command: Endpoint4_10Request["payload"]["command"]
-  readonly arguments?: Endpoint4_10Request["payload"]["arguments"]
-  readonly agent?: Endpoint4_10Request["payload"]["agent"]
-  readonly model?: Endpoint4_10Request["payload"]["model"]
-  readonly files?: Endpoint4_10Request["payload"]["files"]
-  readonly agents?: Endpoint4_10Request["payload"]["agents"]
+  readonly prompt: Endpoint4_10Request["payload"]["prompt"]
   readonly delivery?: Endpoint4_10Request["payload"]["delivery"]
   readonly resume?: Endpoint4_10Request["payload"]["resume"]
 }
-export type Endpoint4_10Output = EffectValue<ReturnType<RawClient["server.session"]["session.command"]>>["data"]
-export type SessionCommandOperation<E = never> = (input: Endpoint4_10Input) => Effect.Effect<Endpoint4_10Output, E>
+export type Endpoint4_10Output = EffectValue<ReturnType<RawClient["server.session"]["session.prompt"]>>["data"]
+export type SessionPromptOperation<E = never> = (input: Endpoint4_10Input) => Effect.Effect<Endpoint4_10Output, E>
 
-type Endpoint4_11Request = Parameters<RawClient["server.session"]["session.skill"]>[0]
+type Endpoint4_11Request = Parameters<RawClient["server.session"]["session.command"]>[0]
 export type Endpoint4_11Input = {
   readonly sessionID: Endpoint4_11Request["params"]["sessionID"]
   readonly id?: Endpoint4_11Request["payload"]["id"]
-  readonly skill: Endpoint4_11Request["payload"]["skill"]
+  readonly command: Endpoint4_11Request["payload"]["command"]
+  readonly arguments?: Endpoint4_11Request["payload"]["arguments"]
+  readonly agent?: Endpoint4_11Request["payload"]["agent"]
+  readonly model?: Endpoint4_11Request["payload"]["model"]
+  readonly files?: Endpoint4_11Request["payload"]["files"]
+  readonly agents?: Endpoint4_11Request["payload"]["agents"]
+  readonly delivery?: Endpoint4_11Request["payload"]["delivery"]
   readonly resume?: Endpoint4_11Request["payload"]["resume"]
 }
-export type Endpoint4_11Output = EffectValue<ReturnType<RawClient["server.session"]["session.skill"]>>
-export type SessionSkillOperation<E = never> = (input: Endpoint4_11Input) => Effect.Effect<Endpoint4_11Output, E>
+export type Endpoint4_11Output = EffectValue<ReturnType<RawClient["server.session"]["session.command"]>>["data"]
+export type SessionCommandOperation<E = never> = (input: Endpoint4_11Input) => Effect.Effect<Endpoint4_11Output, E>
 
-type Endpoint4_12Request = Parameters<RawClient["server.session"]["session.synthetic"]>[0]
+type Endpoint4_12Request = Parameters<RawClient["server.session"]["session.skill"]>[0]
 export type Endpoint4_12Input = {
   readonly sessionID: Endpoint4_12Request["params"]["sessionID"]
-  readonly text: Endpoint4_12Request["payload"]["text"]
-  readonly description?: Endpoint4_12Request["payload"]["description"]
-  readonly metadata?: Endpoint4_12Request["payload"]["metadata"]
+  readonly id?: Endpoint4_12Request["payload"]["id"]
+  readonly skill: Endpoint4_12Request["payload"]["skill"]
+  readonly resume?: Endpoint4_12Request["payload"]["resume"]
 }
-export type Endpoint4_12Output = EffectValue<ReturnType<RawClient["server.session"]["session.synthetic"]>>
-export type SessionSyntheticOperation<E = never> = (input: Endpoint4_12Input) => Effect.Effect<Endpoint4_12Output, E>
+export type Endpoint4_12Output = EffectValue<ReturnType<RawClient["server.session"]["session.skill"]>>
+export type SessionSkillOperation<E = never> = (input: Endpoint4_12Input) => Effect.Effect<Endpoint4_12Output, E>
 
-type Endpoint4_13Request = Parameters<RawClient["server.session"]["session.shell"]>[0]
+type Endpoint4_13Request = Parameters<RawClient["server.session"]["session.synthetic"]>[0]
 export type Endpoint4_13Input = {
   readonly sessionID: Endpoint4_13Request["params"]["sessionID"]
-  readonly id?: Endpoint4_13Request["payload"]["id"]
-  readonly command: Endpoint4_13Request["payload"]["command"]
+  readonly text: Endpoint4_13Request["payload"]["text"]
+  readonly description?: Endpoint4_13Request["payload"]["description"]
+  readonly metadata?: Endpoint4_13Request["payload"]["metadata"]
+  readonly resume?: Endpoint4_13Request["payload"]["resume"]
 }
-export type Endpoint4_13Output = EffectValue<ReturnType<RawClient["server.session"]["session.shell"]>>
-export type SessionShellOperation<E = never> = (input: Endpoint4_13Input) => Effect.Effect<Endpoint4_13Output, E>
+export type Endpoint4_13Output = EffectValue<ReturnType<RawClient["server.session"]["session.synthetic"]>>
+export type SessionSyntheticOperation<E = never> = (input: Endpoint4_13Input) => Effect.Effect<Endpoint4_13Output, E>
 
-type Endpoint4_14Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
+type Endpoint4_14Request = Parameters<RawClient["server.session"]["session.shell"]>[0]
 export type Endpoint4_14Input = {
   readonly sessionID: Endpoint4_14Request["params"]["sessionID"]
   readonly id?: Endpoint4_14Request["payload"]["id"]
+  readonly command: Endpoint4_14Request["payload"]["command"]
 }
-export type Endpoint4_14Output = EffectValue<ReturnType<RawClient["server.session"]["session.compact"]>>["data"]
-export type SessionCompactOperation<E = never> = (input: Endpoint4_14Input) => Effect.Effect<Endpoint4_14Output, E>
+export type Endpoint4_14Output = EffectValue<ReturnType<RawClient["server.session"]["session.shell"]>>
+export type SessionShellOperation<E = never> = (input: Endpoint4_14Input) => Effect.Effect<Endpoint4_14Output, E>
 
-type Endpoint4_15Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
-export type Endpoint4_15Input = { readonly sessionID: Endpoint4_15Request["params"]["sessionID"] }
-export type Endpoint4_15Output = EffectValue<ReturnType<RawClient["server.session"]["session.wait"]>>
-export type SessionWaitOperation<E = never> = (input: Endpoint4_15Input) => Effect.Effect<Endpoint4_15Output, E>
-
-type Endpoint4_16Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
-export type Endpoint4_16Input = {
-  readonly sessionID: Endpoint4_16Request["params"]["sessionID"]
-  readonly messageID: Endpoint4_16Request["payload"]["messageID"]
-  readonly files?: Endpoint4_16Request["payload"]["files"]
+type Endpoint4_15Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
+export type Endpoint4_15Input = {
+  readonly sessionID: Endpoint4_15Request["params"]["sessionID"]
+  readonly id?: Endpoint4_15Request["payload"]["id"]
 }
-export type Endpoint4_16Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.stage"]>>["data"]
-export type SessionRevertStageOperation<E = never> = (input: Endpoint4_16Input) => Effect.Effect<Endpoint4_16Output, E>
+export type Endpoint4_15Output = EffectValue<ReturnType<RawClient["server.session"]["session.compact"]>>["data"]
+export type SessionCompactOperation<E = never> = (input: Endpoint4_15Input) => Effect.Effect<Endpoint4_15Output, E>
+
+type Endpoint4_16Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
+export type Endpoint4_16Input = { readonly sessionID: Endpoint4_16Request["params"]["sessionID"] }
+export type Endpoint4_16Output = EffectValue<ReturnType<RawClient["server.session"]["session.wait"]>>
+export type SessionWaitOperation<E = never> = (input: Endpoint4_16Input) => Effect.Effect<Endpoint4_16Output, E>
 
-type Endpoint4_17Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
-export type Endpoint4_17Input = { readonly sessionID: Endpoint4_17Request["params"]["sessionID"] }
-export type Endpoint4_17Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.clear"]>>
-export type SessionRevertClearOperation<E = never> = (input: Endpoint4_17Input) => Effect.Effect<Endpoint4_17Output, E>
+type Endpoint4_17Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
+export type Endpoint4_17Input = {
+  readonly sessionID: Endpoint4_17Request["params"]["sessionID"]
+  readonly messageID: Endpoint4_17Request["payload"]["messageID"]
+  readonly files?: Endpoint4_17Request["payload"]["files"]
+}
+export type Endpoint4_17Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.stage"]>>["data"]
+export type SessionRevertStageOperation<E = never> = (input: Endpoint4_17Input) => Effect.Effect<Endpoint4_17Output, E>
 
-type Endpoint4_18Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
+type Endpoint4_18Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
 export type Endpoint4_18Input = { readonly sessionID: Endpoint4_18Request["params"]["sessionID"] }
-export type Endpoint4_18Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.commit"]>>
-export type SessionRevertCommitOperation<E = never> = (input: Endpoint4_18Input) => Effect.Effect<Endpoint4_18Output, E>
+export type Endpoint4_18Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.clear"]>>
+export type SessionRevertClearOperation<E = never> = (input: Endpoint4_18Input) => Effect.Effect<Endpoint4_18Output, E>
 
-type Endpoint4_19Request = Parameters<RawClient["server.session"]["session.context"]>[0]
+type Endpoint4_19Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
 export type Endpoint4_19Input = { readonly sessionID: Endpoint4_19Request["params"]["sessionID"] }
-export type Endpoint4_19Output = EffectValue<ReturnType<RawClient["server.session"]["session.context"]>>["data"]
-export type SessionContextOperation<E = never> = (input: Endpoint4_19Input) => Effect.Effect<Endpoint4_19Output, E>
+export type Endpoint4_19Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.commit"]>>
+export type SessionRevertCommitOperation<E = never> = (input: Endpoint4_19Input) => Effect.Effect<Endpoint4_19Output, E>
 
-type Endpoint4_20Request = Parameters<RawClient["server.session"]["session.instructions.entry.list"]>[0]
+type Endpoint4_20Request = Parameters<RawClient["server.session"]["session.context"]>[0]
 export type Endpoint4_20Input = { readonly sessionID: Endpoint4_20Request["params"]["sessionID"] }
-export type Endpoint4_20Output = EffectValue<
+export type Endpoint4_20Output = EffectValue<ReturnType<RawClient["server.session"]["session.context"]>>["data"]
+export type SessionContextOperation<E = never> = (input: Endpoint4_20Input) => Effect.Effect<Endpoint4_20Output, E>
+
+type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.instructions.entry.list"]>[0]
+export type Endpoint4_21Input = { readonly sessionID: Endpoint4_21Request["params"]["sessionID"] }
+export type Endpoint4_21Output = EffectValue<
   ReturnType<RawClient["server.session"]["session.instructions.entry.list"]>
 >["data"]
 export type SessionInstructionsEntryListOperation<E = never> = (
-  input: Endpoint4_20Input,
-) => Effect.Effect<Endpoint4_20Output, E>
-
-type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.instructions.entry.put"]>[0]
-export type Endpoint4_21Input = {
-  readonly sessionID: Endpoint4_21Request["params"]["sessionID"]
-  readonly key: Endpoint4_21Request["params"]["key"]
-  readonly value: Endpoint4_21Request["payload"]["value"]
-}
-export type Endpoint4_21Output = EffectValue<ReturnType<RawClient["server.session"]["session.instructions.entry.put"]>>
-export type SessionInstructionsEntryPutOperation<E = never> = (
   input: Endpoint4_21Input,
 ) => Effect.Effect<Endpoint4_21Output, E>
 
-type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.instructions.entry.remove"]>[0]
+type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.instructions.entry.put"]>[0]
 export type Endpoint4_22Input = {
   readonly sessionID: Endpoint4_22Request["params"]["sessionID"]
   readonly key: Endpoint4_22Request["params"]["key"]
+  readonly value: Endpoint4_22Request["payload"]["value"]
 }
-export type Endpoint4_22Output = EffectValue<
-  ReturnType<RawClient["server.session"]["session.instructions.entry.remove"]>
->
-export type SessionInstructionsEntryRemoveOperation<E = never> = (
+export type Endpoint4_22Output = EffectValue<ReturnType<RawClient["server.session"]["session.instructions.entry.put"]>>
+export type SessionInstructionsEntryPutOperation<E = never> = (
   input: Endpoint4_22Input,
 ) => Effect.Effect<Endpoint4_22Output, E>
 
-type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.log"]>[0]
+type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.instructions.entry.remove"]>[0]
 export type Endpoint4_23Input = {
   readonly sessionID: Endpoint4_23Request["params"]["sessionID"]
-  readonly after?: Endpoint4_23Request["query"]["after"]
-  readonly follow?: Endpoint4_23Request["query"]["follow"]
+  readonly key: Endpoint4_23Request["params"]["key"]
 }
-export type Endpoint4_23Output = StreamValue<EffectValue<ReturnType<RawClient["server.session"]["session.log"]>>>
-export type SessionLogOperation<E = never> = (input: Endpoint4_23Input) => Stream.Stream<Endpoint4_23Output, E>
+export type Endpoint4_23Output = EffectValue<
+  ReturnType<RawClient["server.session"]["session.instructions.entry.remove"]>
+>
+export type SessionInstructionsEntryRemoveOperation<E = never> = (
+  input: Endpoint4_23Input,
+) => Effect.Effect<Endpoint4_23Output, E>
 
-type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
-export type Endpoint4_24Input = { readonly sessionID: Endpoint4_24Request["params"]["sessionID"] }
-export type Endpoint4_24Output = EffectValue<ReturnType<RawClient["server.session"]["session.interrupt"]>>
-export type SessionInterruptOperation<E = never> = (input: Endpoint4_24Input) => Effect.Effect<Endpoint4_24Output, E>
+type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.log"]>[0]
+export type Endpoint4_24Input = {
+  readonly sessionID: Endpoint4_24Request["params"]["sessionID"]
+  readonly after?: Endpoint4_24Request["query"]["after"]
+  readonly follow?: Endpoint4_24Request["query"]["follow"]
+}
+export type Endpoint4_24Output = StreamValue<EffectValue<ReturnType<RawClient["server.session"]["session.log"]>>>
+export type SessionLogOperation<E = never> = (input: Endpoint4_24Input) => Stream.Stream<Endpoint4_24Output, E>
 
-type Endpoint4_25Request = Parameters<RawClient["server.session"]["session.background"]>[0]
+type Endpoint4_25Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
 export type Endpoint4_25Input = { readonly sessionID: Endpoint4_25Request["params"]["sessionID"] }
-export type Endpoint4_25Output = EffectValue<ReturnType<RawClient["server.session"]["session.background"]>>
-export type SessionBackgroundOperation<E = never> = (input: Endpoint4_25Input) => Effect.Effect<Endpoint4_25Output, E>
+export type Endpoint4_25Output = EffectValue<ReturnType<RawClient["server.session"]["session.interrupt"]>>
+export type SessionInterruptOperation<E = never> = (input: Endpoint4_25Input) => Effect.Effect<Endpoint4_25Output, E>
+
+type Endpoint4_26Request = Parameters<RawClient["server.session"]["session.background"]>[0]
+export type Endpoint4_26Input = { readonly sessionID: Endpoint4_26Request["params"]["sessionID"] }
+export type Endpoint4_26Output = EffectValue<ReturnType<RawClient["server.session"]["session.background"]>>
+export type SessionBackgroundOperation<E = never> = (input: Endpoint4_26Input) => Effect.Effect<Endpoint4_26Output, E>
 
-type Endpoint4_26Request = Parameters<RawClient["server.session"]["session.message"]>[0]
-export type Endpoint4_26Input = {
-  readonly sessionID: Endpoint4_26Request["params"]["sessionID"]
-  readonly messageID: Endpoint4_26Request["params"]["messageID"]
+type Endpoint4_27Request = Parameters<RawClient["server.session"]["session.message"]>[0]
+export type Endpoint4_27Input = {
+  readonly sessionID: Endpoint4_27Request["params"]["sessionID"]
+  readonly messageID: Endpoint4_27Request["params"]["messageID"]
 }
-export type Endpoint4_26Output = EffectValue<ReturnType<RawClient["server.session"]["session.message"]>>["data"]
-export type SessionMessageOperation<E = never> = (input: Endpoint4_26Input) => Effect.Effect<Endpoint4_26Output, E>
+export type Endpoint4_27Output = EffectValue<ReturnType<RawClient["server.session"]["session.message"]>>["data"]
+export type SessionMessageOperation<E = never> = (input: Endpoint4_27Input) => Effect.Effect<Endpoint4_27Output, E>
 
 export interface SessionApi<E = never> {
   readonly list: SessionListOperation<E>
@@ -273,6 +283,7 @@ export interface SessionApi<E = never> {
   readonly switchAgent: SessionSwitchAgentOperation<E>
   readonly switchModel: SessionSwitchModelOperation<E>
   readonly rename: SessionRenameOperation<E>
+  readonly move: SessionMoveOperation<E>
   readonly prompt: SessionPromptOperation<E>
   readonly command: SessionCommandOperation<E>
   readonly skill: SessionSkillOperation<E>
@@ -280,9 +291,11 @@ export interface SessionApi<E = never> {
   readonly shell: SessionShellOperation<E>
   readonly compact: SessionCompactOperation<E>
   readonly wait: SessionWaitOperation<E>
-  readonly revertStage: SessionRevertStageOperation<E>
-  readonly revertClear: SessionRevertClearOperation<E>
-  readonly revertCommit: SessionRevertCommitOperation<E>
+  readonly revert: {
+    readonly stage: SessionRevertStageOperation<E>
+    readonly clear: SessionRevertClearOperation<E>
+    readonly commit: SessionRevertCommitOperation<E>
+  }
   readonly context: SessionContextOperation<E>
   readonly instructions: {
     readonly entry: {
@@ -427,11 +440,15 @@ export type IntegrationAttemptCancelOperation<E = never> = (
 export interface IntegrationApi<E = never> {
   readonly list: IntegrationListOperation<E>
   readonly get: IntegrationGetOperation<E>
-  readonly connectKey: IntegrationConnectKeyOperation<E>
-  readonly connectOauth: IntegrationConnectOauthOperation<E>
-  readonly attemptStatus: IntegrationAttemptStatusOperation<E>
-  readonly attemptComplete: IntegrationAttemptCompleteOperation<E>
-  readonly attemptCancel: IntegrationAttemptCancelOperation<E>
+  readonly connect: {
+    readonly key: IntegrationConnectKeyOperation<E>
+    readonly oauth: IntegrationConnectOauthOperation<E>
+  }
+  readonly attempt: {
+    readonly status: IntegrationAttemptStatusOperation<E>
+    readonly complete: IntegrationAttemptCompleteOperation<E>
+    readonly cancel: IntegrationAttemptCancelOperation<E>
+  }
 }
 
 type Endpoint10_0Request = Parameters<RawClient["server.mcp"]["mcp.list"]>[0]
@@ -439,8 +456,16 @@ export type Endpoint10_0Input = { readonly location?: Endpoint10_0Request["query
 export type Endpoint10_0Output = EffectValue<ReturnType<RawClient["server.mcp"]["mcp.list"]>>
 export type ServerMcpListOperation<E = never> = (input?: Endpoint10_0Input) => Effect.Effect<Endpoint10_0Output, E>
 
+type Endpoint10_1Request = Parameters<RawClient["server.mcp"]["mcp.resource.catalog"]>[0]
+export type Endpoint10_1Input = { readonly location?: Endpoint10_1Request["query"]["location"] }
+export type Endpoint10_1Output = EffectValue<ReturnType<RawClient["server.mcp"]["mcp.resource.catalog"]>>
+export type ServerMcpResourceCatalogOperation<E = never> = (
+  input?: Endpoint10_1Input,
+) => Effect.Effect<Endpoint10_1Output, E>
+
 export interface ServerMcpApi<E = never> {
   readonly list: ServerMcpListOperation<E>
+  readonly resource: { readonly catalog: ServerMcpResourceCatalogOperation<E> }
 }
 
 type Endpoint11_0Request = Parameters<RawClient["server.credential"]["credential.update"]>[0]
@@ -490,7 +515,7 @@ export interface ProjectApi<E = never> {
 type Endpoint13_0Request = Parameters<RawClient["server.form"]["form.request.list"]>[0]
 export type Endpoint13_0Input = { readonly location?: Endpoint13_0Request["query"]["location"] }
 export type Endpoint13_0Output = EffectValue<ReturnType<RawClient["server.form"]["form.request.list"]>>
-export type FormListRequestsOperation<E = never> = (input?: Endpoint13_0Input) => Effect.Effect<Endpoint13_0Output, E>
+export type FormRequestListOperation<E = never> = (input?: Endpoint13_0Input) => Effect.Effect<Endpoint13_0Output, E>
 
 type Endpoint13_1Request = Parameters<RawClient["server.form"]["session.form.list"]>[0]
 export type Endpoint13_1Input = { readonly sessionID: Endpoint13_1Request["params"]["sessionID"] }
@@ -501,7 +526,7 @@ type Endpoint13_2Request = Parameters<RawClient["server.form"]["session.form.cre
 export type Endpoint13_2Input = {
   readonly sessionID: Endpoint13_2Request["params"]["sessionID"]
   readonly id?: Endpoint13_2Request["payload"]["id"]
-  readonly title?: Endpoint13_2Request["payload"]["title"]
+  readonly title: Endpoint13_2Request["payload"]["title"]
   readonly metadata?: Endpoint13_2Request["payload"]["metadata"]
   readonly mode: Endpoint13_2Request["payload"]["mode"]
   readonly fields?: Endpoint13_2Request["payload"]["fields"]
@@ -544,7 +569,7 @@ export type Endpoint13_6Output = EffectValue<ReturnType<RawClient["server.form"]
 export type FormCancelOperation<E = never> = (input: Endpoint13_6Input) => Effect.Effect<Endpoint13_6Output, E>
 
 export interface FormApi<E = never> {
-  readonly listRequests: FormListRequestsOperation<E>
+  readonly request: { readonly list: FormRequestListOperation<E> }
   readonly list: FormListOperation<E>
   readonly create: FormCreateOperation<E>
   readonly get: FormGetOperation<E>
@@ -556,7 +581,7 @@ export interface FormApi<E = never> {
 type Endpoint14_0Request = Parameters<RawClient["server.permission"]["permission.request.list"]>[0]
 export type Endpoint14_0Input = { readonly location?: Endpoint14_0Request["query"]["location"] }
 export type Endpoint14_0Output = EffectValue<ReturnType<RawClient["server.permission"]["permission.request.list"]>>
-export type PermissionListRequestsOperation<E = never> = (
+export type PermissionRequestListOperation<E = never> = (
   input?: Endpoint14_0Input,
 ) => Effect.Effect<Endpoint14_0Output, E>
 
@@ -565,14 +590,14 @@ export type Endpoint14_1Input = { readonly projectID?: Endpoint14_1Request["quer
 export type Endpoint14_1Output = EffectValue<
   ReturnType<RawClient["server.permission"]["permission.saved.list"]>
 >["data"]
-export type PermissionListSavedOperation<E = never> = (
+export type PermissionSavedListOperation<E = never> = (
   input?: Endpoint14_1Input,
 ) => Effect.Effect<Endpoint14_1Output, E>
 
 type Endpoint14_2Request = Parameters<RawClient["server.permission"]["permission.saved.remove"]>[0]
 export type Endpoint14_2Input = { readonly id: Endpoint14_2Request["params"]["id"] }
 export type Endpoint14_2Output = EffectValue<ReturnType<RawClient["server.permission"]["permission.saved.remove"]>>
-export type PermissionRemoveSavedOperation<E = never> = (
+export type PermissionSavedRemoveOperation<E = never> = (
   input: Endpoint14_2Input,
 ) => Effect.Effect<Endpoint14_2Output, E>
 
@@ -620,9 +645,8 @@ export type Endpoint14_6Output = EffectValue<ReturnType<RawClient["server.permis
 export type PermissionReplyOperation<E = never> = (input: Endpoint14_6Input) => Effect.Effect<Endpoint14_6Output, E>
 
 export interface PermissionApi<E = never> {
-  readonly listRequests: PermissionListRequestsOperation<E>
-  readonly listSaved: PermissionListSavedOperation<E>
-  readonly removeSaved: PermissionRemoveSavedOperation<E>
+  readonly request: { readonly list: PermissionRequestListOperation<E> }
+  readonly saved: { readonly list: PermissionSavedListOperation<E>; readonly remove: PermissionSavedRemoveOperation<E> }
   readonly create: PermissionCreateOperation<E>
   readonly list: PermissionListOperation<E>
   readonly get: PermissionGetOperation<E>
@@ -791,7 +815,7 @@ export interface ShellApi<E = never> {
 type Endpoint21_0Request = Parameters<RawClient["server.question"]["question.request.list"]>[0]
 export type Endpoint21_0Input = { readonly location?: Endpoint21_0Request["query"]["location"] }
 export type Endpoint21_0Output = EffectValue<ReturnType<RawClient["server.question"]["question.request.list"]>>
-export type QuestionListRequestsOperation<E = never> = (
+export type QuestionRequestListOperation<E = never> = (
   input?: Endpoint21_0Input,
 ) => Effect.Effect<Endpoint21_0Output, E>
 
@@ -818,7 +842,7 @@ export type Endpoint21_3Output = EffectValue<ReturnType<RawClient["server.questi
 export type QuestionRejectOperation<E = never> = (input: Endpoint21_3Input) => Effect.Effect<Endpoint21_3Output, E>
 
 export interface QuestionApi<E = never> {
-  readonly listRequests: QuestionListRequestsOperation<E>
+  readonly request: { readonly list: QuestionRequestListOperation<E> }
   readonly list: QuestionListOperation<E>
   readonly reply: QuestionReplyOperation<E>
   readonly reject: QuestionRejectOperation<E>
@@ -888,10 +912,15 @@ export interface VcsApi<E = never> {
 }
 
 export type Endpoint25_0Output = EffectValue<ReturnType<RawClient["server.debug"]["debug.location"]>>
-export type DebugLocationOperation<E = never> = () => Effect.Effect<Endpoint25_0Output, E>
+export type DebugLocationListOperation<E = never> = () => Effect.Effect<Endpoint25_0Output, E>
+
+type Endpoint25_1Request = Parameters<RawClient["server.debug"]["debug.location.evict"]>[0]
+export type Endpoint25_1Input = { readonly location?: Endpoint25_1Request["query"]["location"] }
+export type Endpoint25_1Output = EffectValue<ReturnType<RawClient["server.debug"]["debug.location.evict"]>>
+export type DebugLocationEvictOperation<E = never> = (input?: Endpoint25_1Input) => Effect.Effect<Endpoint25_1Output, E>
 
 export interface DebugApi<E = never> {
-  readonly location: DebugLocationOperation<E>
+  readonly location: { readonly list: DebugLocationListOperation<E>; readonly evict: DebugLocationEvictOperation<E> }
 }
 
 export interface AppApi<E = never> {

+ 148 - 120
packages/client/src/effect/generated/client.ts

@@ -141,37 +141,49 @@ const Endpoint4_8 = (raw: RawClient["server.session"]) => (input: Endpoint4_8Inp
     Effect.mapError(mapClientError),
   )
 
-type Endpoint4_9Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
+type Endpoint4_9Request = Parameters<RawClient["server.session"]["session.move"]>[0]
 type Endpoint4_9Input = {
   readonly sessionID: Endpoint4_9Request["params"]["sessionID"]
-  readonly id?: Endpoint4_9Request["payload"]["id"]
-  readonly prompt: Endpoint4_9Request["payload"]["prompt"]
-  readonly delivery?: Endpoint4_9Request["payload"]["delivery"]
-  readonly resume?: Endpoint4_9Request["payload"]["resume"]
+  readonly destination: Endpoint4_9Request["payload"]["destination"]
+  readonly moveChanges?: Endpoint4_9Request["payload"]["moveChanges"]
 }
 const Endpoint4_9 = (raw: RawClient["server.session"]) => (input: Endpoint4_9Input) =>
-  raw["session.prompt"]({
+  raw["session.move"]({
     params: { sessionID: input["sessionID"] },
-    payload: { id: input["id"], prompt: input["prompt"], delivery: input["delivery"], resume: input["resume"] },
-  }).pipe(
-    Effect.mapError(mapClientError),
-    Effect.map((value) => value.data),
-  )
+    payload: { destination: input["destination"], moveChanges: input["moveChanges"] },
+  }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint4_10Request = Parameters<RawClient["server.session"]["session.command"]>[0]
+type Endpoint4_10Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
 type Endpoint4_10Input = {
   readonly sessionID: Endpoint4_10Request["params"]["sessionID"]
   readonly id?: Endpoint4_10Request["payload"]["id"]
-  readonly command: Endpoint4_10Request["payload"]["command"]
-  readonly arguments?: Endpoint4_10Request["payload"]["arguments"]
-  readonly agent?: Endpoint4_10Request["payload"]["agent"]
-  readonly model?: Endpoint4_10Request["payload"]["model"]
-  readonly files?: Endpoint4_10Request["payload"]["files"]
-  readonly agents?: Endpoint4_10Request["payload"]["agents"]
+  readonly prompt: Endpoint4_10Request["payload"]["prompt"]
   readonly delivery?: Endpoint4_10Request["payload"]["delivery"]
   readonly resume?: Endpoint4_10Request["payload"]["resume"]
 }
 const Endpoint4_10 = (raw: RawClient["server.session"]) => (input: Endpoint4_10Input) =>
+  raw["session.prompt"]({
+    params: { sessionID: input["sessionID"] },
+    payload: { id: input["id"], prompt: input["prompt"], delivery: input["delivery"], resume: input["resume"] },
+  }).pipe(
+    Effect.mapError(mapClientError),
+    Effect.map((value) => value.data),
+  )
+
+type Endpoint4_11Request = Parameters<RawClient["server.session"]["session.command"]>[0]
+type Endpoint4_11Input = {
+  readonly sessionID: Endpoint4_11Request["params"]["sessionID"]
+  readonly id?: Endpoint4_11Request["payload"]["id"]
+  readonly command: Endpoint4_11Request["payload"]["command"]
+  readonly arguments?: Endpoint4_11Request["payload"]["arguments"]
+  readonly agent?: Endpoint4_11Request["payload"]["agent"]
+  readonly model?: Endpoint4_11Request["payload"]["model"]
+  readonly files?: Endpoint4_11Request["payload"]["files"]
+  readonly agents?: Endpoint4_11Request["payload"]["agents"]
+  readonly delivery?: Endpoint4_11Request["payload"]["delivery"]
+  readonly resume?: Endpoint4_11Request["payload"]["resume"]
+}
+const Endpoint4_11 = (raw: RawClient["server.session"]) => (input: Endpoint4_11Input) =>
   raw["session.command"]({
     params: { sessionID: input["sessionID"] },
     payload: {
@@ -190,67 +202,73 @@ const Endpoint4_10 = (raw: RawClient["server.session"]) => (input: Endpoint4_10I
     Effect.map((value) => value.data),
   )
 
-type Endpoint4_11Request = Parameters<RawClient["server.session"]["session.skill"]>[0]
-type Endpoint4_11Input = {
-  readonly sessionID: Endpoint4_11Request["params"]["sessionID"]
-  readonly id?: Endpoint4_11Request["payload"]["id"]
-  readonly skill: Endpoint4_11Request["payload"]["skill"]
-  readonly resume?: Endpoint4_11Request["payload"]["resume"]
-}
-const Endpoint4_11 = (raw: RawClient["server.session"]) => (input: Endpoint4_11Input) =>
-  raw["session.skill"]({
-    params: { sessionID: input["sessionID"] },
-    payload: { id: input["id"], skill: input["skill"], resume: input["resume"] },
-  }).pipe(Effect.mapError(mapClientError))
-
-type Endpoint4_12Request = Parameters<RawClient["server.session"]["session.synthetic"]>[0]
+type Endpoint4_12Request = Parameters<RawClient["server.session"]["session.skill"]>[0]
 type Endpoint4_12Input = {
   readonly sessionID: Endpoint4_12Request["params"]["sessionID"]
-  readonly text: Endpoint4_12Request["payload"]["text"]
-  readonly description?: Endpoint4_12Request["payload"]["description"]
-  readonly metadata?: Endpoint4_12Request["payload"]["metadata"]
+  readonly id?: Endpoint4_12Request["payload"]["id"]
+  readonly skill: Endpoint4_12Request["payload"]["skill"]
+  readonly resume?: Endpoint4_12Request["payload"]["resume"]
 }
 const Endpoint4_12 = (raw: RawClient["server.session"]) => (input: Endpoint4_12Input) =>
-  raw["session.synthetic"]({
+  raw["session.skill"]({
     params: { sessionID: input["sessionID"] },
-    payload: { text: input["text"], description: input["description"], metadata: input["metadata"] },
+    payload: { id: input["id"], skill: input["skill"], resume: input["resume"] },
   }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint4_13Request = Parameters<RawClient["server.session"]["session.shell"]>[0]
+type Endpoint4_13Request = Parameters<RawClient["server.session"]["session.synthetic"]>[0]
 type Endpoint4_13Input = {
   readonly sessionID: Endpoint4_13Request["params"]["sessionID"]
-  readonly id?: Endpoint4_13Request["payload"]["id"]
-  readonly command: Endpoint4_13Request["payload"]["command"]
+  readonly text: Endpoint4_13Request["payload"]["text"]
+  readonly description?: Endpoint4_13Request["payload"]["description"]
+  readonly metadata?: Endpoint4_13Request["payload"]["metadata"]
+  readonly resume?: Endpoint4_13Request["payload"]["resume"]
 }
 const Endpoint4_13 = (raw: RawClient["server.session"]) => (input: Endpoint4_13Input) =>
-  raw["session.shell"]({
+  raw["session.synthetic"]({
     params: { sessionID: input["sessionID"] },
-    payload: { id: input["id"], command: input["command"] },
+    payload: {
+      text: input["text"],
+      description: input["description"],
+      metadata: input["metadata"],
+      resume: input["resume"],
+    },
   }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint4_14Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
+type Endpoint4_14Request = Parameters<RawClient["server.session"]["session.shell"]>[0]
 type Endpoint4_14Input = {
   readonly sessionID: Endpoint4_14Request["params"]["sessionID"]
   readonly id?: Endpoint4_14Request["payload"]["id"]
+  readonly command: Endpoint4_14Request["payload"]["command"]
 }
 const Endpoint4_14 = (raw: RawClient["server.session"]) => (input: Endpoint4_14Input) =>
+  raw["session.shell"]({
+    params: { sessionID: input["sessionID"] },
+    payload: { id: input["id"], command: input["command"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint4_15Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
+type Endpoint4_15Input = {
+  readonly sessionID: Endpoint4_15Request["params"]["sessionID"]
+  readonly id?: Endpoint4_15Request["payload"]["id"]
+}
+const Endpoint4_15 = (raw: RawClient["server.session"]) => (input: Endpoint4_15Input) =>
   raw["session.compact"]({ params: { sessionID: input["sessionID"] }, payload: { id: input["id"] } }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-type Endpoint4_15Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
-type Endpoint4_15Input = { readonly sessionID: Endpoint4_15Request["params"]["sessionID"] }
-const Endpoint4_15 = (raw: RawClient["server.session"]) => (input: Endpoint4_15Input) =>
+type Endpoint4_16Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
+type Endpoint4_16Input = { readonly sessionID: Endpoint4_16Request["params"]["sessionID"] }
+const Endpoint4_16 = (raw: RawClient["server.session"]) => (input: Endpoint4_16Input) =>
   raw["session.wait"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint4_16Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
-type Endpoint4_16Input = {
-  readonly sessionID: Endpoint4_16Request["params"]["sessionID"]
-  readonly messageID: Endpoint4_16Request["payload"]["messageID"]
-  readonly files?: Endpoint4_16Request["payload"]["files"]
+type Endpoint4_17Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
+type Endpoint4_17Input = {
+  readonly sessionID: Endpoint4_17Request["params"]["sessionID"]
+  readonly messageID: Endpoint4_17Request["payload"]["messageID"]
+  readonly files?: Endpoint4_17Request["payload"]["files"]
 }
-const Endpoint4_16 = (raw: RawClient["server.session"]) => (input: Endpoint4_16Input) =>
+const Endpoint4_17 = (raw: RawClient["server.session"]) => (input: Endpoint4_17Input) =>
   raw["session.revert.stage"]({
     params: { sessionID: input["sessionID"] },
     payload: { messageID: input["messageID"], files: input["files"] },
@@ -259,61 +277,61 @@ const Endpoint4_16 = (raw: RawClient["server.session"]) => (input: Endpoint4_16I
     Effect.map((value) => value.data),
   )
 
-type Endpoint4_17Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
-type Endpoint4_17Input = { readonly sessionID: Endpoint4_17Request["params"]["sessionID"] }
-const Endpoint4_17 = (raw: RawClient["server.session"]) => (input: Endpoint4_17Input) =>
-  raw["session.revert.clear"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
-
-type Endpoint4_18Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
+type Endpoint4_18Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
 type Endpoint4_18Input = { readonly sessionID: Endpoint4_18Request["params"]["sessionID"] }
 const Endpoint4_18 = (raw: RawClient["server.session"]) => (input: Endpoint4_18Input) =>
-  raw["session.revert.commit"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
+  raw["session.revert.clear"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint4_19Request = Parameters<RawClient["server.session"]["session.context"]>[0]
+type Endpoint4_19Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
 type Endpoint4_19Input = { readonly sessionID: Endpoint4_19Request["params"]["sessionID"] }
 const Endpoint4_19 = (raw: RawClient["server.session"]) => (input: Endpoint4_19Input) =>
+  raw["session.revert.commit"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint4_20Request = Parameters<RawClient["server.session"]["session.context"]>[0]
+type Endpoint4_20Input = { readonly sessionID: Endpoint4_20Request["params"]["sessionID"] }
+const Endpoint4_20 = (raw: RawClient["server.session"]) => (input: Endpoint4_20Input) =>
   raw["session.context"]({ params: { sessionID: input["sessionID"] } }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-type Endpoint4_20Request = Parameters<RawClient["server.session"]["session.instructions.entry.list"]>[0]
-type Endpoint4_20Input = { readonly sessionID: Endpoint4_20Request["params"]["sessionID"] }
-const Endpoint4_20 = (raw: RawClient["server.session"]) => (input: Endpoint4_20Input) =>
+type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.instructions.entry.list"]>[0]
+type Endpoint4_21Input = { readonly sessionID: Endpoint4_21Request["params"]["sessionID"] }
+const Endpoint4_21 = (raw: RawClient["server.session"]) => (input: Endpoint4_21Input) =>
   raw["session.instructions.entry.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.instructions.entry.put"]>[0]
-type Endpoint4_21Input = {
-  readonly sessionID: Endpoint4_21Request["params"]["sessionID"]
-  readonly key: Endpoint4_21Request["params"]["key"]
-  readonly value: Endpoint4_21Request["payload"]["value"]
+type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.instructions.entry.put"]>[0]
+type Endpoint4_22Input = {
+  readonly sessionID: Endpoint4_22Request["params"]["sessionID"]
+  readonly key: Endpoint4_22Request["params"]["key"]
+  readonly value: Endpoint4_22Request["payload"]["value"]
 }
-const Endpoint4_21 = (raw: RawClient["server.session"]) => (input: Endpoint4_21Input) =>
+const Endpoint4_22 = (raw: RawClient["server.session"]) => (input: Endpoint4_22Input) =>
   raw["session.instructions.entry.put"]({
     params: { sessionID: input["sessionID"], key: input["key"] },
     payload: { value: input["value"] },
   }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.instructions.entry.remove"]>[0]
-type Endpoint4_22Input = {
-  readonly sessionID: Endpoint4_22Request["params"]["sessionID"]
-  readonly key: Endpoint4_22Request["params"]["key"]
+type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.instructions.entry.remove"]>[0]
+type Endpoint4_23Input = {
+  readonly sessionID: Endpoint4_23Request["params"]["sessionID"]
+  readonly key: Endpoint4_23Request["params"]["key"]
 }
-const Endpoint4_22 = (raw: RawClient["server.session"]) => (input: Endpoint4_22Input) =>
+const Endpoint4_23 = (raw: RawClient["server.session"]) => (input: Endpoint4_23Input) =>
   raw["session.instructions.entry.remove"]({ params: { sessionID: input["sessionID"], key: input["key"] } }).pipe(
     Effect.mapError(mapClientError),
   )
 
-type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.log"]>[0]
-type Endpoint4_23Input = {
-  readonly sessionID: Endpoint4_23Request["params"]["sessionID"]
-  readonly after?: Endpoint4_23Request["query"]["after"]
-  readonly follow?: Endpoint4_23Request["query"]["follow"]
+type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.log"]>[0]
+type Endpoint4_24Input = {
+  readonly sessionID: Endpoint4_24Request["params"]["sessionID"]
+  readonly after?: Endpoint4_24Request["query"]["after"]
+  readonly follow?: Endpoint4_24Request["query"]["follow"]
 }
-const Endpoint4_23 = (raw: RawClient["server.session"]) => (input: Endpoint4_23Input) =>
+const Endpoint4_24 = (raw: RawClient["server.session"]) => (input: Endpoint4_24Input) =>
   Stream.unwrap(
     raw["session.log"]({
       params: { sessionID: input["sessionID"] },
@@ -324,22 +342,22 @@ const Endpoint4_23 = (raw: RawClient["server.session"]) => (input: Endpoint4_23I
     ),
   )
 
-type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
-type Endpoint4_24Input = { readonly sessionID: Endpoint4_24Request["params"]["sessionID"] }
-const Endpoint4_24 = (raw: RawClient["server.session"]) => (input: Endpoint4_24Input) =>
-  raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
-
-type Endpoint4_25Request = Parameters<RawClient["server.session"]["session.background"]>[0]
+type Endpoint4_25Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
 type Endpoint4_25Input = { readonly sessionID: Endpoint4_25Request["params"]["sessionID"] }
 const Endpoint4_25 = (raw: RawClient["server.session"]) => (input: Endpoint4_25Input) =>
+  raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint4_26Request = Parameters<RawClient["server.session"]["session.background"]>[0]
+type Endpoint4_26Input = { readonly sessionID: Endpoint4_26Request["params"]["sessionID"] }
+const Endpoint4_26 = (raw: RawClient["server.session"]) => (input: Endpoint4_26Input) =>
   raw["session.background"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint4_26Request = Parameters<RawClient["server.session"]["session.message"]>[0]
-type Endpoint4_26Input = {
-  readonly sessionID: Endpoint4_26Request["params"]["sessionID"]
-  readonly messageID: Endpoint4_26Request["params"]["messageID"]
+type Endpoint4_27Request = Parameters<RawClient["server.session"]["session.message"]>[0]
+type Endpoint4_27Input = {
+  readonly sessionID: Endpoint4_27Request["params"]["sessionID"]
+  readonly messageID: Endpoint4_27Request["params"]["messageID"]
 }
-const Endpoint4_26 = (raw: RawClient["server.session"]) => (input: Endpoint4_26Input) =>
+const Endpoint4_27 = (raw: RawClient["server.session"]) => (input: Endpoint4_27Input) =>
   raw["session.message"]({ params: { sessionID: input["sessionID"], messageID: input["messageID"] } }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
@@ -355,22 +373,21 @@ const adaptGroup4 = (raw: RawClient["server.session"]) => ({
   switchAgent: Endpoint4_6(raw),
   switchModel: Endpoint4_7(raw),
   rename: Endpoint4_8(raw),
-  prompt: Endpoint4_9(raw),
-  command: Endpoint4_10(raw),
-  skill: Endpoint4_11(raw),
-  synthetic: Endpoint4_12(raw),
-  shell: Endpoint4_13(raw),
-  compact: Endpoint4_14(raw),
-  wait: Endpoint4_15(raw),
-  revertStage: Endpoint4_16(raw),
-  revertClear: Endpoint4_17(raw),
-  revertCommit: Endpoint4_18(raw),
-  context: Endpoint4_19(raw),
-  instructions: { entry: { list: Endpoint4_20(raw), put: Endpoint4_21(raw), remove: Endpoint4_22(raw) } },
-  log: Endpoint4_23(raw),
-  interrupt: Endpoint4_24(raw),
-  background: Endpoint4_25(raw),
-  message: Endpoint4_26(raw),
+  move: Endpoint4_9(raw),
+  prompt: Endpoint4_10(raw),
+  command: Endpoint4_11(raw),
+  skill: Endpoint4_12(raw),
+  synthetic: Endpoint4_13(raw),
+  shell: Endpoint4_14(raw),
+  compact: Endpoint4_15(raw),
+  wait: Endpoint4_16(raw),
+  revert: { stage: Endpoint4_17(raw), clear: Endpoint4_18(raw), commit: Endpoint4_19(raw) },
+  context: Endpoint4_20(raw),
+  instructions: { entry: { list: Endpoint4_21(raw), put: Endpoint4_22(raw), remove: Endpoint4_23(raw) } },
+  log: Endpoint4_24(raw),
+  interrupt: Endpoint4_25(raw),
+  background: Endpoint4_26(raw),
+  message: Endpoint4_27(raw),
 })
 
 type Endpoint5_0Request = Parameters<RawClient["server.message"]["session.messages"]>[0]
@@ -517,11 +534,8 @@ const Endpoint9_6 = (raw: RawClient["server.integration"]) => (input: Endpoint9_
 const adaptGroup9 = (raw: RawClient["server.integration"]) => ({
   list: Endpoint9_0(raw),
   get: Endpoint9_1(raw),
-  connectKey: Endpoint9_2(raw),
-  connectOauth: Endpoint9_3(raw),
-  attemptStatus: Endpoint9_4(raw),
-  attemptComplete: Endpoint9_5(raw),
-  attemptCancel: Endpoint9_6(raw),
+  connect: { key: Endpoint9_2(raw), oauth: Endpoint9_3(raw) },
+  attempt: { status: Endpoint9_4(raw), complete: Endpoint9_5(raw), cancel: Endpoint9_6(raw) },
 })
 
 type Endpoint10_0Request = Parameters<RawClient["server.mcp"]["mcp.list"]>[0]
@@ -529,7 +543,15 @@ type Endpoint10_0Input = { readonly location?: Endpoint10_0Request["query"]["loc
 const Endpoint10_0 = (raw: RawClient["server.mcp"]) => (input?: Endpoint10_0Input) =>
   raw["mcp.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
 
-const adaptGroup10 = (raw: RawClient["server.mcp"]) => ({ list: Endpoint10_0(raw) })
+type Endpoint10_1Request = Parameters<RawClient["server.mcp"]["mcp.resource.catalog"]>[0]
+type Endpoint10_1Input = { readonly location?: Endpoint10_1Request["query"]["location"] }
+const Endpoint10_1 = (raw: RawClient["server.mcp"]) => (input?: Endpoint10_1Input) =>
+  raw["mcp.resource.catalog"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup10 = (raw: RawClient["server.mcp"]) => ({
+  list: Endpoint10_0(raw),
+  resource: { catalog: Endpoint10_1(raw) },
+})
 
 type Endpoint11_0Request = Parameters<RawClient["server.credential"]["credential.update"]>[0]
 type Endpoint11_0Input = {
@@ -599,7 +621,7 @@ type Endpoint13_2Request = Parameters<RawClient["server.form"]["session.form.cre
 type Endpoint13_2Input = {
   readonly sessionID: Endpoint13_2Request["params"]["sessionID"]
   readonly id?: Endpoint13_2Request["payload"]["id"]
-  readonly title?: Endpoint13_2Request["payload"]["title"]
+  readonly title: Endpoint13_2Request["payload"]["title"]
   readonly metadata?: Endpoint13_2Request["payload"]["metadata"]
   readonly mode: Endpoint13_2Request["payload"]["mode"]
   readonly fields?: Endpoint13_2Request["payload"]["fields"]
@@ -666,7 +688,7 @@ const Endpoint13_6 = (raw: RawClient["server.form"]) => (input: Endpoint13_6Inpu
   )
 
 const adaptGroup13 = (raw: RawClient["server.form"]) => ({
-  listRequests: Endpoint13_0(raw),
+  request: { list: Endpoint13_0(raw) },
   list: Endpoint13_1(raw),
   create: Endpoint13_2(raw),
   get: Endpoint13_3(raw),
@@ -754,9 +776,8 @@ const Endpoint14_6 = (raw: RawClient["server.permission"]) => (input: Endpoint14
   }).pipe(Effect.mapError(mapClientError))
 
 const adaptGroup14 = (raw: RawClient["server.permission"]) => ({
-  listRequests: Endpoint14_0(raw),
-  listSaved: Endpoint14_1(raw),
-  removeSaved: Endpoint14_2(raw),
+  request: { list: Endpoint14_0(raw) },
+  saved: { list: Endpoint14_1(raw), remove: Endpoint14_2(raw) },
   create: Endpoint14_3(raw),
   list: Endpoint14_4(raw),
   get: Endpoint14_5(raw),
@@ -989,7 +1010,7 @@ const Endpoint21_3 = (raw: RawClient["server.question"]) => (input: Endpoint21_3
   )
 
 const adaptGroup21 = (raw: RawClient["server.question"]) => ({
-  listRequests: Endpoint21_0(raw),
+  request: { list: Endpoint21_0(raw) },
   list: Endpoint21_1(raw),
   reply: Endpoint21_2(raw),
   reject: Endpoint21_3(raw),
@@ -1069,7 +1090,14 @@ const adaptGroup24 = (raw: RawClient["server.vcs"]) => ({ status: Endpoint24_0(r
 const Endpoint25_0 = (raw: RawClient["server.debug"]) => () =>
   raw["debug.location"]({}).pipe(Effect.mapError(mapClientError))
 
-const adaptGroup25 = (raw: RawClient["server.debug"]) => ({ location: Endpoint25_0(raw) })
+type Endpoint25_1Request = Parameters<RawClient["server.debug"]["debug.location.evict"]>[0]
+type Endpoint25_1Input = { readonly location?: Endpoint25_1Request["query"]["location"] }
+const Endpoint25_1 = (raw: RawClient["server.debug"]) => (input?: Endpoint25_1Input) =>
+  raw["debug.location.evict"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup25 = (raw: RawClient["server.debug"]) => ({
+  location: { list: Endpoint25_0(raw), evict: Endpoint25_1(raw) },
+})
 
 const adaptClient = (raw: RawClient) => ({
   health: adaptGroup0(raw["server.health"]),

+ 244 - 179
packages/client/src/promise/generated/client.ts

@@ -23,6 +23,8 @@ import type {
   SessionSwitchModelOutput,
   SessionRenameInput,
   SessionRenameOutput,
+  SessionMoveInput,
+  SessionMoveOutput,
   SessionPromptInput,
   SessionPromptOutput,
   SessionCommandInput,
@@ -87,6 +89,8 @@ import type {
   IntegrationAttemptCancelOutput,
   ServerMcpListInput,
   ServerMcpListOutput,
+  ServerMcpResourceCatalogInput,
+  ServerMcpResourceCatalogOutput,
   CredentialUpdateInput,
   CredentialUpdateOutput,
   CredentialRemoveInput,
@@ -96,8 +100,8 @@ import type {
   ProjectCurrentOutput,
   ProjectDirectoriesInput,
   ProjectDirectoriesOutput,
-  FormListRequestsInput,
-  FormListRequestsOutput,
+  FormRequestListInput,
+  FormRequestListOutput,
   FormListInput,
   FormListOutput,
   FormCreateInput,
@@ -110,12 +114,12 @@ import type {
   FormReplyOutput,
   FormCancelInput,
   FormCancelOutput,
-  PermissionListRequestsInput,
-  PermissionListRequestsOutput,
-  PermissionListSavedInput,
-  PermissionListSavedOutput,
-  PermissionRemoveSavedInput,
-  PermissionRemoveSavedOutput,
+  PermissionRequestListInput,
+  PermissionRequestListOutput,
+  PermissionSavedListInput,
+  PermissionSavedListOutput,
+  PermissionSavedRemoveInput,
+  PermissionSavedRemoveOutput,
   PermissionCreateInput,
   PermissionCreateOutput,
   PermissionListInput,
@@ -157,8 +161,8 @@ import type {
   ShellOutputOutput,
   ShellRemoveInput,
   ShellRemoveOutput,
-  QuestionListRequestsInput,
-  QuestionListRequestsOutput,
+  QuestionRequestListInput,
+  QuestionRequestListOutput,
   QuestionListInput,
   QuestionListOutput,
   QuestionReplyInput,
@@ -177,7 +181,9 @@ import type {
   VcsStatusOutput,
   VcsDiffInput,
   VcsDiffOutput,
-  DebugLocationOutput,
+  DebugLocationListOutput,
+  DebugLocationEvictInput,
+  DebugLocationEvictOutput,
 } from "./types"
 import { ClientError } from "./client-error"
 
@@ -485,6 +491,18 @@ export function make(options: ClientOptions) {
           },
           requestOptions,
         ),
+      move: (input: SessionMoveInput, requestOptions?: RequestOptions) =>
+        request<SessionMoveOutput>(
+          {
+            method: "POST",
+            path: `/api/session/${encodeURIComponent(input.sessionID)}/move`,
+            body: { destination: input["destination"], moveChanges: input["moveChanges"] },
+            successStatus: 204,
+            declaredStatuses: [404, 400, 401],
+            empty: true,
+          },
+          requestOptions,
+        ),
       prompt: (input: SessionPromptInput, requestOptions?: RequestOptions) =>
         request<{ readonly data: SessionPromptOutput }>(
           {
@@ -536,7 +554,12 @@ export function make(options: ClientOptions) {
           {
             method: "POST",
             path: `/api/session/${encodeURIComponent(input.sessionID)}/synthetic`,
-            body: { text: input["text"], description: input["description"], metadata: input["metadata"] },
+            body: {
+              text: input["text"],
+              description: input["description"],
+              metadata: input["metadata"],
+              resume: input["resume"],
+            },
             successStatus: 204,
             declaredStatuses: [404, 400, 401],
             empty: true,
@@ -578,40 +601,42 @@ export function make(options: ClientOptions) {
           },
           requestOptions,
         ),
-      revertStage: (input: SessionRevertStageInput, requestOptions?: RequestOptions) =>
-        request<{ readonly data: SessionRevertStageOutput }>(
-          {
-            method: "POST",
-            path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/stage`,
-            body: { messageID: input["messageID"], files: input["files"] },
-            successStatus: 200,
-            declaredStatuses: [404, 409, 500, 400, 401],
-            empty: false,
-          },
-          requestOptions,
-        ).then((value) => value.data),
-      revertClear: (input: SessionRevertClearInput, requestOptions?: RequestOptions) =>
-        request<SessionRevertClearOutput>(
-          {
-            method: "POST",
-            path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/clear`,
-            successStatus: 204,
-            declaredStatuses: [404, 409, 500, 400, 401],
-            empty: true,
-          },
-          requestOptions,
-        ),
-      revertCommit: (input: SessionRevertCommitInput, requestOptions?: RequestOptions) =>
-        request<SessionRevertCommitOutput>(
-          {
-            method: "POST",
-            path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/commit`,
-            successStatus: 204,
-            declaredStatuses: [404, 409, 400, 401],
-            empty: true,
-          },
-          requestOptions,
-        ),
+      revert: {
+        stage: (input: SessionRevertStageInput, requestOptions?: RequestOptions) =>
+          request<{ readonly data: SessionRevertStageOutput }>(
+            {
+              method: "POST",
+              path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/stage`,
+              body: { messageID: input["messageID"], files: input["files"] },
+              successStatus: 200,
+              declaredStatuses: [404, 409, 500, 400, 401],
+              empty: false,
+            },
+            requestOptions,
+          ).then((value) => value.data),
+        clear: (input: SessionRevertClearInput, requestOptions?: RequestOptions) =>
+          request<SessionRevertClearOutput>(
+            {
+              method: "POST",
+              path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/clear`,
+              successStatus: 204,
+              declaredStatuses: [404, 409, 500, 400, 401],
+              empty: true,
+            },
+            requestOptions,
+          ),
+        commit: (input: SessionRevertCommitInput, requestOptions?: RequestOptions) =>
+          request<SessionRevertCommitOutput>(
+            {
+              method: "POST",
+              path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/commit`,
+              successStatus: 204,
+              declaredStatuses: [404, 409, 400, 401],
+              empty: true,
+            },
+            requestOptions,
+          ),
+      },
       context: (input: SessionContextInput, requestOptions?: RequestOptions) =>
         request<{ readonly data: SessionContextOutput }>(
           {
@@ -813,69 +838,73 @@ export function make(options: ClientOptions) {
           },
           requestOptions,
         ),
-      connectKey: (input: IntegrationConnectKeyInput, requestOptions?: RequestOptions) =>
-        request<IntegrationConnectKeyOutput>(
-          {
-            method: "POST",
-            path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/key`,
-            query: { location: input["location"] },
-            body: { key: input["key"], label: input["label"] },
-            successStatus: 204,
-            declaredStatuses: [400, 401],
-            empty: true,
-          },
-          requestOptions,
-        ),
-      connectOauth: (input: IntegrationConnectOauthInput, requestOptions?: RequestOptions) =>
-        request<IntegrationConnectOauthOutput>(
-          {
-            method: "POST",
-            path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth`,
-            query: { location: input["location"] },
-            body: { methodID: input["methodID"], inputs: input["inputs"], label: input["label"] },
-            successStatus: 200,
-            declaredStatuses: [400, 401],
-            empty: false,
-          },
-          requestOptions,
-        ),
-      attemptStatus: (input: IntegrationAttemptStatusInput, requestOptions?: RequestOptions) =>
-        request<IntegrationAttemptStatusOutput>(
-          {
-            method: "GET",
-            path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
-            query: { location: input["location"] },
-            successStatus: 200,
-            declaredStatuses: [401, 400],
-            empty: false,
-          },
-          requestOptions,
-        ),
-      attemptComplete: (input: IntegrationAttemptCompleteInput, requestOptions?: RequestOptions) =>
-        request<IntegrationAttemptCompleteOutput>(
-          {
-            method: "POST",
-            path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}/complete`,
-            query: { location: input["location"] },
-            body: { code: input["code"] },
-            successStatus: 204,
-            declaredStatuses: [400, 401],
-            empty: true,
-          },
-          requestOptions,
-        ),
-      attemptCancel: (input: IntegrationAttemptCancelInput, requestOptions?: RequestOptions) =>
-        request<IntegrationAttemptCancelOutput>(
-          {
-            method: "DELETE",
-            path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
-            query: { location: input["location"] },
-            successStatus: 204,
-            declaredStatuses: [401, 400],
-            empty: true,
-          },
-          requestOptions,
-        ),
+      connect: {
+        key: (input: IntegrationConnectKeyInput, requestOptions?: RequestOptions) =>
+          request<IntegrationConnectKeyOutput>(
+            {
+              method: "POST",
+              path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/key`,
+              query: { location: input["location"] },
+              body: { key: input["key"], label: input["label"] },
+              successStatus: 204,
+              declaredStatuses: [400, 401],
+              empty: true,
+            },
+            requestOptions,
+          ),
+        oauth: (input: IntegrationConnectOauthInput, requestOptions?: RequestOptions) =>
+          request<IntegrationConnectOauthOutput>(
+            {
+              method: "POST",
+              path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth`,
+              query: { location: input["location"] },
+              body: { methodID: input["methodID"], inputs: input["inputs"], label: input["label"] },
+              successStatus: 200,
+              declaredStatuses: [400, 401],
+              empty: false,
+            },
+            requestOptions,
+          ),
+      },
+      attempt: {
+        status: (input: IntegrationAttemptStatusInput, requestOptions?: RequestOptions) =>
+          request<IntegrationAttemptStatusOutput>(
+            {
+              method: "GET",
+              path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
+              query: { location: input["location"] },
+              successStatus: 200,
+              declaredStatuses: [401, 400],
+              empty: false,
+            },
+            requestOptions,
+          ),
+        complete: (input: IntegrationAttemptCompleteInput, requestOptions?: RequestOptions) =>
+          request<IntegrationAttemptCompleteOutput>(
+            {
+              method: "POST",
+              path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}/complete`,
+              query: { location: input["location"] },
+              body: { code: input["code"] },
+              successStatus: 204,
+              declaredStatuses: [400, 401],
+              empty: true,
+            },
+            requestOptions,
+          ),
+        cancel: (input: IntegrationAttemptCancelInput, requestOptions?: RequestOptions) =>
+          request<IntegrationAttemptCancelOutput>(
+            {
+              method: "DELETE",
+              path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
+              query: { location: input["location"] },
+              successStatus: 204,
+              declaredStatuses: [401, 400],
+              empty: true,
+            },
+            requestOptions,
+          ),
+      },
     },
     "server.mcp": {
       list: (input?: ServerMcpListInput, requestOptions?: RequestOptions) =>
@@ -890,6 +919,20 @@ export function make(options: ClientOptions) {
           },
           requestOptions,
         ),
+      resource: {
+        catalog: (input?: ServerMcpResourceCatalogInput, requestOptions?: RequestOptions) =>
+          request<ServerMcpResourceCatalogOutput>(
+            {
+              method: "GET",
+              path: `/api/mcp/resource`,
+              query: { location: input?.["location"] },
+              successStatus: 200,
+              declaredStatuses: [401, 400],
+              empty: false,
+            },
+            requestOptions,
+          ),
+      },
     },
     credential: {
       update: (input: CredentialUpdateInput, requestOptions?: RequestOptions) =>
@@ -950,18 +993,20 @@ export function make(options: ClientOptions) {
         ),
     },
     form: {
-      listRequests: (input?: FormListRequestsInput, requestOptions?: RequestOptions) =>
-        request<FormListRequestsOutput>(
-          {
-            method: "GET",
-            path: `/api/form/request`,
-            query: { location: input?.["location"] },
-            successStatus: 200,
-            declaredStatuses: [401, 400],
-            empty: false,
-          },
-          requestOptions,
-        ),
+      request: {
+        list: (input?: FormRequestListInput, requestOptions?: RequestOptions) =>
+          request<FormRequestListOutput>(
+            {
+              method: "GET",
+              path: `/api/form/request`,
+              query: { location: input?.["location"] },
+              successStatus: 200,
+              declaredStatuses: [401, 400],
+              empty: false,
+            },
+            requestOptions,
+          ),
+      },
       list: (input: FormListInput, requestOptions?: RequestOptions) =>
         request<{ readonly data: FormListOutput }>(
           {
@@ -1039,41 +1084,45 @@ export function make(options: ClientOptions) {
         ),
     },
     permission: {
-      listRequests: (input?: PermissionListRequestsInput, requestOptions?: RequestOptions) =>
-        request<PermissionListRequestsOutput>(
-          {
-            method: "GET",
-            path: `/api/permission/request`,
-            query: { location: input?.["location"] },
-            successStatus: 200,
-            declaredStatuses: [401, 400],
-            empty: false,
-          },
-          requestOptions,
-        ),
-      listSaved: (input?: PermissionListSavedInput, requestOptions?: RequestOptions) =>
-        request<{ readonly data: PermissionListSavedOutput }>(
-          {
-            method: "GET",
-            path: `/api/permission/saved`,
-            query: { projectID: input?.["projectID"] },
-            successStatus: 200,
-            declaredStatuses: [401, 400],
-            empty: false,
-          },
-          requestOptions,
-        ).then((value) => value.data),
-      removeSaved: (input: PermissionRemoveSavedInput, requestOptions?: RequestOptions) =>
-        request<PermissionRemoveSavedOutput>(
-          {
-            method: "DELETE",
-            path: `/api/permission/saved/${encodeURIComponent(input.id)}`,
-            successStatus: 204,
-            declaredStatuses: [401, 400],
-            empty: true,
-          },
-          requestOptions,
-        ),
+      request: {
+        list: (input?: PermissionRequestListInput, requestOptions?: RequestOptions) =>
+          request<PermissionRequestListOutput>(
+            {
+              method: "GET",
+              path: `/api/permission/request`,
+              query: { location: input?.["location"] },
+              successStatus: 200,
+              declaredStatuses: [401, 400],
+              empty: false,
+            },
+            requestOptions,
+          ),
+      },
+      saved: {
+        list: (input?: PermissionSavedListInput, requestOptions?: RequestOptions) =>
+          request<{ readonly data: PermissionSavedListOutput }>(
+            {
+              method: "GET",
+              path: `/api/permission/saved`,
+              query: { projectID: input?.["projectID"] },
+              successStatus: 200,
+              declaredStatuses: [401, 400],
+              empty: false,
+            },
+            requestOptions,
+          ).then((value) => value.data),
+        remove: (input: PermissionSavedRemoveInput, requestOptions?: RequestOptions) =>
+          request<PermissionSavedRemoveOutput>(
+            {
+              method: "DELETE",
+              path: `/api/permission/saved/${encodeURIComponent(input.id)}`,
+              successStatus: 204,
+              declaredStatuses: [401, 400],
+              empty: true,
+            },
+            requestOptions,
+          ),
+      },
       create: (input: PermissionCreateInput, requestOptions?: RequestOptions) =>
         request<{ readonly data: PermissionCreateOutput }>(
           {
@@ -1355,18 +1404,20 @@ export function make(options: ClientOptions) {
         ),
     },
     question: {
-      listRequests: (input?: QuestionListRequestsInput, requestOptions?: RequestOptions) =>
-        request<QuestionListRequestsOutput>(
-          {
-            method: "GET",
-            path: `/api/question/request`,
-            query: { location: input?.["location"] },
-            successStatus: 200,
-            declaredStatuses: [401, 400],
-            empty: false,
-          },
-          requestOptions,
-        ),
+      request: {
+        list: (input?: QuestionRequestListInput, requestOptions?: RequestOptions) =>
+          request<QuestionRequestListOutput>(
+            {
+              method: "GET",
+              path: `/api/question/request`,
+              query: { location: input?.["location"] },
+              successStatus: 200,
+              declaredStatuses: [401, 400],
+              empty: false,
+            },
+            requestOptions,
+          ),
+      },
       list: (input: QuestionListInput, requestOptions?: RequestOptions) =>
         request<{ readonly data: QuestionListOutput }>(
           {
@@ -1483,17 +1534,31 @@ export function make(options: ClientOptions) {
         ),
     },
     debug: {
-      location: (requestOptions?: RequestOptions) =>
-        request<DebugLocationOutput>(
-          {
-            method: "GET",
-            path: `/api/debug/location`,
-            successStatus: 200,
-            declaredStatuses: [401, 400],
-            empty: false,
-          },
-          requestOptions,
-        ),
+      location: {
+        list: (requestOptions?: RequestOptions) =>
+          request<DebugLocationListOutput>(
+            {
+              method: "GET",
+              path: `/api/debug/location`,
+              successStatus: 200,
+              declaredStatuses: [401, 400],
+              empty: false,
+            },
+            requestOptions,
+          ),
+        evict: (input?: DebugLocationEvictInput, requestOptions?: RequestOptions) =>
+          request<DebugLocationEvictOutput>(
+            {
+              method: "DELETE",
+              path: `/api/debug/location`,
+              query: { location: input?.["location"] },
+              successStatus: 204,
+              declaredStatuses: [401, 400],
+              empty: true,
+            },
+            requestOptions,
+          ),
+      },
     },
   }
 }

Diff do ficheiro suprimidas por serem muito extensas
+ 252 - 195
packages/client/src/promise/generated/types.ts


+ 4 - 4
packages/client/test/contract-identity.test.ts

@@ -19,7 +19,7 @@ import { SessionMessage } from "@opencode-ai/schema/session-message"
 import { Workspace } from "@opencode-ai/schema/workspace"
 import { Api } from "@opencode-ai/server/api"
 import { compile, emitPromise } from "@opencode-ai/httpapi-codegen"
-import { ClientApi, endpointNames, groupNames, promiseOmitEndpoints } from "../src/contract"
+import { ClientApi, groupNames, promiseOmitEndpoints } from "../src/contract"
 
 const Client = await import("../src/effect")
 
@@ -38,7 +38,7 @@ test("Core and Server reuse the authoritative Schema and Protocol values", () =>
   expect(ProjectV2.Directory).toBe(Project.Directory)
   expect(ProjectV2.Directories).toBe(Project.Directories)
   expect(CoreSessionInput.Admitted).toBe(SessionInput.Admitted)
-  expect(CoreSessionMessage.Message).toBe(SessionMessage.Message)
+  expect(CoreSessionMessage.Info).toBe(SessionMessage.Info)
   expect(Api.groups["server.session"].identifier).toBe("server.session")
   expect(Api.groups["server.project"].identifier).toBe("server.project")
   expect(Object.keys(ClientApi.groups)).toEqual(Object.keys(Api.groups))
@@ -49,8 +49,8 @@ test("Core and Server reuse the authoritative Schema and Protocol values", () =>
 })
 
 test("client and Server contracts generate identically", () => {
-  const server = compile(Api, { groupNames, endpointNames, omitEndpoints: promiseOmitEndpoints })
-  const client = compile(ClientApi, { groupNames, endpointNames, omitEndpoints: promiseOmitEndpoints })
+  const server = compile(Api, { groupNames, omitEndpoints: promiseOmitEndpoints })
+  const client = compile(ClientApi, { groupNames, omitEndpoints: promiseOmitEndpoints })
 
   expect(emitPromise(client)).toEqual(emitPromise(server))
 })

+ 27 - 9
packages/client/test/promise.test.ts

@@ -33,16 +33,11 @@ test("exposes every standard HTTP API group", () => {
     "debug",
   ])
   expect(Object.keys(client.debug)).toEqual(["location"])
+  expect(Object.keys(client.debug.location)).toEqual(["list", "evict"])
   expect(Object.keys(client.message)).toEqual(["list"])
-  expect(Object.keys(client.integration)).toEqual([
-    "list",
-    "get",
-    "connectKey",
-    "connectOauth",
-    "attemptStatus",
-    "attemptComplete",
-    "attemptCancel",
-  ])
+  expect(Object.keys(client.integration)).toEqual(["list", "get", "connect", "attempt"])
+  expect(Object.keys(client.integration.connect)).toEqual(["key", "oauth"])
+  expect(Object.keys(client.integration.attempt)).toEqual(["status", "complete", "cancel"])
   expect(Object.keys(client.file)).toEqual(["read", "list", "find"])
   expect(Object.keys(client.vcs)).toEqual(["status", "diff"])
   expect(Object.keys(client.pty)).toEqual(["list", "create", "get", "update", "remove"])
@@ -50,6 +45,29 @@ test("exposes every standard HTTP API group", () => {
   expect(Object.keys(client.project)).toEqual(["list", "current", "directories"])
 })
 
+test("MCP resource catalog uses the public HTTP contract", async () => {
+  let request: Request | undefined
+  const client = OpenCode.make({
+    baseUrl: "http://localhost:3000",
+    fetch: async (input) => {
+      request = input instanceof Request ? input : new Request(input)
+      return Response.json({
+        location: { directory: "/tmp/project", project: { id: "proj_test", directory: "/tmp/project" } },
+        data: {
+          resources: [{ server: "docs", name: "Readme", uri: "docs://readme" }],
+          templates: [{ server: "docs", name: "File", uriTemplate: "docs://{path}" }],
+        },
+      })
+    },
+  })
+
+  const result = await client["server.mcp"].resource.catalog({ location: { directory: "/tmp/project" } })
+
+  expect(result.data.resources[0]?.uri).toBe("docs://readme")
+  expect(request?.method).toBe("GET")
+  expect(request?.url).toBe("http://localhost:3000/api/mcp/resource?location%5Bdirectory%5D=%2Ftmp%2Fproject")
+})
+
 test("file.read returns binary content from the public HTTP contract", async () => {
   let request: Request | undefined
   const client = OpenCode.make({

+ 2 - 2
packages/codemode/README.md

@@ -62,7 +62,7 @@ const result =
 
 `result` is always a `CodeMode.Result`. Program, validation, limit, and tool failures are returned as diagnostics rather than failing the Effect. Host interruption remains interruption.
 
-Successful result values are JSON-safe data. A program that returns `undefined`, including by reaching the end without `return`, produces `null`; nested `undefined` values are normalized to `null` as well.
+Successful result values are JSON-safe data. An explicit `return` produces the program result; when it is omitted, the final executable top-level expression is returned as a model-friendly REPL convenience. Otherwise reaching the end produces `null`. Returned `undefined` and nested `undefined` values are normalized to `null` as well.
 
 ## API
 
@@ -241,7 +241,7 @@ CodeMode executes a deliberately bounded JavaScript subset. It supports:
 - `if`, conditional expressions, `switch`, `for`, `for...of` (arrays, strings, Maps, Sets, including assignment-form destructuring such as `for ([key, value] of entries)`), `for...in` (own keys of plain objects, index strings of arrays, and namespace/tool names of `tools` references - anything else is an error suggesting `for...of` or `Object.keys`, rather than real JS's surprising behavior of indices for strings and zero iterations for Maps/Sets), `while`, and `do...while`.
 - Arrow functions and function declarations with closures, defaults, rest parameters, and destructuring.
 - Optional chaining, nullish coalescing, templates, spread (arrays, strings, Maps, Sets), and `try`/`catch`.
-- Common array, string, number, `Object`, `Math`, and `JSON` operations. Mutating array methods include `push`/`pop`/`shift`/`unshift`/`splice` (removes in place and returns the removed elements)/`fill`/`copyWithin`; array `keys`/`values`/`entries` return **arrays** (matching the Map/Set convention) and work with `for...of` and spread. String methods include `localeCompare` (locale/options arguments ignored), `normalize`, and the `trimLeft`/`trimRight` aliases. `Object.keys` also accepts arrays (index strings, as in JS) and tool references: `Object.keys(tools)` lists the top-level namespaces, including `$codemode`, and `Object.keys(tools.ns)` lists the names at that node (a callable tool enumerates as `[]`; an unknown path is an `UnknownTool` diagnostic). `Object.values`/`Object.entries` on a tool reference fail with a pointer at `Object.keys(tools)` and `tools.$codemode.search`.
+- Common array, string, number, `Object`, `Math`, and `JSON` operations, including primitive-number `valueOf`, the standard non-finite `Number` constants, and host-backed `Math.random`. Mutating array methods include `push`/`pop`/`shift`/`unshift`/`splice` (removes in place and returns the removed elements)/`fill`/`copyWithin`; array `keys`/`values`/`entries` return **arrays** (matching the Map/Set convention) and work with `for...of` and spread. String methods include `localeCompare` (locale/options arguments ignored), `normalize`, and the `trimLeft`/`trimRight` aliases. `Object.keys` also accepts arrays (index strings, as in JS) and tool references: `Object.keys(tools)` lists the top-level namespaces, including `$codemode`, and `Object.keys(tools.ns)` lists the names at that node (a callable tool enumerates as `[]`; an unknown path is an `UnknownTool` diagnostic). `Object.values`/`Object.entries` on a tool reference fail with a pointer at `Object.keys(tools)` and `tools.$codemode.search`.
 - `Date` - `Date.now()`/`Date.parse()`/`Date.UTC()`, `new Date(...)`, the getter methods, and date arithmetic/comparison via the time value. Dates stringify as ISO (`toString` included, for determinism across host timezones).
 - Regular expressions - `/literals/` and `new RegExp(...)` with `test`/`exec` (stateful `lastIndex` for `g`), plus string `match`/`matchAll`/`replace`/`replaceAll`/`split`/`search` with patterns. Match results are arrays carrying `index` and named `groups` as own properties (`input` is omitted). `replace` and `replaceAll` accept function replacers with captures, offset, input, and named groups; callbacks run sequentially, may await tool calls, and have their results coerced to strings. Invalid patterns, invalid flags, and missing-`g` calls fail with catchable errors that say what was wrong and how to fix it (escaping hints, the exact `/pattern/g` to write). Patterns run on the host engine, so pathological backtracking is bounded only by the execution timeout.
 - `Map` and `Set` - construction from entries/arrays/strings, `get`/`set`/`add`/`has`/`delete`/`clear`/`size`/`forEach`, and `keys`/`values`/`entries` returning **arrays** (not iterators).

+ 1 - 1
packages/codemode/codemode.md

@@ -207,7 +207,7 @@ current omissions to implement, not intentional product boundaries.
 - [ ] Add `Object.is` after runtime method and tool references have stable identity semantics.
 - [ ] Add deterministic modern collection conveniences where they improve orchestration: `Object.groupBy`, Set
       composition methods, and `Array.prototype.toSpliced`.
-- [ ] Decide whether nondeterministic `Math.random` and iterable `Math.sumPrecise` belong in the runtime.
+- [ ] Decide whether iterable `Math.sumPrecise` belongs in the runtime.
 - [ ] Refine diagnostics so user throws, expected tool failures, unexpected host/tool defects, and genuine interpreter
       defects are distinguishable without leaking private causes.
 

+ 0 - 1
packages/codemode/src/interpreter/model.ts

@@ -30,7 +30,6 @@ export type Binding = {
 
 export type StatementResult =
   | { kind: "none" }
-  | { kind: "value"; value: unknown }
   | { kind: "return"; value: unknown }
   | { kind: "break" }
   | { kind: "continue" }

+ 24 - 35
packages/codemode/src/interpreter/runtime.ts

@@ -535,17 +535,29 @@ const invokeArrayStatic = (name: string, args: Array<unknown>, node: AstNode): u
       if (args[0] instanceof SandboxURLSearchParams) {
         return Array.from(args[0].params.entries(), ([key, value]) => [key, value])
       }
-      const source = boundedData(args[0], "Array.from input")
+      const source = args[0]
+      if (source instanceof SandboxPromise) {
+        throw new InterpreterRuntimeError(
+          "Array.from received an un-awaited Promise; await it before creating the array.",
+          node,
+          "InvalidDataValue",
+        )
+      }
       if (typeof source === "string") return Array.from(source)
       if (Array.isArray(source)) return [...source]
       if (
         source !== null &&
         typeof source === "object" &&
+        (Object.getPrototypeOf(source) === Object.prototype || Object.getPrototypeOf(source) === null) &&
         typeof (source as { length?: unknown }).length === "number"
       ) {
         return Array.from(source as ArrayLike<unknown>)
       }
-      throw new InterpreterRuntimeError("Array.from expects an array, string, Map, Set, or array-like value.", node)
+      throw new InterpreterRuntimeError(
+        "Array.from expects an array, string, Map, Set, or array-like value.",
+        node,
+        "InvalidDataValue",
+      )
     }
     default:
       throw new InterpreterRuntimeError(`Array.${name} is not available in CodeMode.`, node)
@@ -684,13 +696,15 @@ class Interpreter<R> {
     const evaluate = Effect.gen(function* () {
       self.hoistFunctions(program.body)
       let value: unknown = undefined
-      let returned = false
-      for (const statement of program.body) {
+      for (const [index, statement] of program.body.entries()) {
+        if (index === program.body.length - 1 && statement.type === "ExpressionStatement") {
+          value = yield* self.evaluateExpression(getNode(statement, "expression"))
+          break
+        }
         const result = yield* self.evaluateStatement(statement)
 
         if (result.kind === "return") {
           value = result.value
-          returned = true
           break
         }
 
@@ -698,11 +712,7 @@ class Interpreter<R> {
           throw new InterpreterRuntimeError(`Unexpected '${result.kind}' outside of a loop.`, statement)
         }
 
-        if (result.kind === "value") {
-          self.lastValue = result.value
-        }
       }
-      if (!returned) value = self.lastValue
 
       // The program body runs inside an implicit async function, so a returned promise
       // resolves before crossing the data boundary - `return tools.ns.tool(...)` works
@@ -827,7 +837,7 @@ class Interpreter<R> {
   private evaluateStatement(node: AstNode): Effect.Effect<StatementResult, unknown, R> {
     switch (node.type) {
       case "ExpressionStatement":
-        return Effect.map(this.evaluateExpression(getNode(node, "expression")), (value) => ({ kind: "value", value }))
+        return Effect.as(this.evaluateExpression(getNode(node, "expression")), { kind: "none" })
       case "VariableDeclaration":
         return Effect.map(this.evaluateVariableDeclaration(node), () => ({ kind: "none" }))
       case "ReturnStatement": {
@@ -880,11 +890,6 @@ class Interpreter<R> {
         const statement = asNode(statementValue, "body")
         const result = yield* self.evaluateStatement(statement)
 
-        if (result.kind === "value") {
-          self.lastValue = result.value
-          continue
-        }
-
         if (result.kind !== "none") {
           return result
         }
@@ -976,7 +981,6 @@ class Interpreter<R> {
           const result = yield* self.evaluateStatement(asNode(statementValue, "consequent"))
           if (result.kind === "break") return { kind: "none" } satisfies StatementResult
           if (result.kind === "return" || result.kind === "continue") return result
-          if (result.kind === "value") self.lastValue = result.value
         }
       }
       return { kind: "none" } satisfies StatementResult
@@ -1004,9 +1008,6 @@ class Interpreter<R> {
           return result
         }
 
-        if (result.kind === "value") {
-          self.lastValue = result.value
-        }
       }
 
       return { kind: "none" } satisfies StatementResult
@@ -1034,9 +1035,6 @@ class Interpreter<R> {
           return result
         }
 
-        if (result.kind === "value") {
-          self.lastValue = result.value
-        }
       } while (yield* self.evaluateExpression(testNode))
 
       return { kind: "none" } satisfies StatementResult
@@ -1092,10 +1090,6 @@ class Interpreter<R> {
           return { kind: "none" } satisfies StatementResult
         }
 
-        if (result.kind === "value") {
-          self.lastValue = result.value
-        }
-
         if (iterationScope) {
           const loopScope = self.currentScope()
           for (const name of perIterationBindings) {
@@ -1180,10 +1174,6 @@ class Interpreter<R> {
           return { kind: "none" }
         }
 
-        if (result.kind === "value") {
-          self.lastValue = result.value
-        }
-
         if (result.kind === "continue") {
           continue
         }
@@ -1273,10 +1263,6 @@ class Interpreter<R> {
           return { kind: "none" }
         }
 
-        if (result.kind === "value") {
-          self.lastValue = result.value
-        }
-
         if (result.kind === "continue") {
           continue
         }
@@ -2077,6 +2063,9 @@ class Interpreter<R> {
         if (callable.namespace === "Object" && objectMethodsPreservingIdentity.has(callable.name)) {
           return invokeGlobalMethod(callable, args, node)
         }
+        if (callable.namespace === "Array" && (callable.name === "from" || callable.name === "of")) {
+          return invokeGlobalMethod(callable, args, node)
+        }
         return boundedData(invokeGlobalMethod(callable, args, node), `${callable.namespace}.${callable.name} result`)
       }
       if (callable instanceof CoercionFunction) {
@@ -2409,7 +2398,7 @@ class Interpreter<R> {
 
       if (fn.body.type === "BlockStatement") {
         const result = yield* invocation.evaluateStatement(fn.body)
-        return result.kind === "return" || result.kind === "value" ? result.value : undefined
+        return result.kind === "return" ? result.value : undefined
       }
 
       return yield* invocation.evaluateExpression(fn.body)

+ 2 - 0
packages/codemode/src/stdlib/math.ts

@@ -1,6 +1,7 @@
 export const mathConstants = new Set(["PI", "E", "LN2", "LN10", "LOG2E", "LOG10E", "SQRT2", "SQRT1_2"])
 
 export const mathMethods = new Set([
+  "random",
   "max",
   "min",
   "abs",
@@ -40,6 +41,7 @@ export const mathMethods = new Set([
 
 export const invokeMathMethod = (name: string, args: Array<unknown>, node: AstNode): number => {
   if (!mathMethods.has(name)) throw new InterpreterRuntimeError(`Math.${name} is not available in CodeMode.`, node)
+  if (name === "random") return Math.random()
   const nums = args.map((arg) => {
     if (typeof arg !== "number") throw new InterpreterRuntimeError(`Math.${name} expects number arguments.`, node)
     return arg

+ 14 - 2
packages/codemode/src/stdlib/number.ts

@@ -1,6 +1,15 @@
-export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString"])
+export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString", "valueOf"])
 
-export const numberConstants = new Set(["MAX_SAFE_INTEGER", "MIN_SAFE_INTEGER", "MAX_VALUE", "MIN_VALUE", "EPSILON"])
+export const numberConstants = new Set([
+  "MAX_SAFE_INTEGER",
+  "MIN_SAFE_INTEGER",
+  "MAX_VALUE",
+  "MIN_VALUE",
+  "EPSILON",
+  "NaN",
+  "POSITIVE_INFINITY",
+  "NEGATIVE_INFINITY",
+])
 
 export const numberStatics = new Set(["isInteger", "isFinite", "isNaN", "isSafeInteger", "parseInt", "parseFloat"])
 
@@ -32,6 +41,9 @@ export const invokeNumberMethod = (value: number, name: string, args: Array<unkn
       result = value.toString(radix)
       break
     }
+    case "valueOf":
+      result = value
+      break
     default:
       throw new InterpreterRuntimeError(`Number method '${name}' is not available in CodeMode.`, node)
   }

+ 18 - 15
packages/codemode/src/stdlib/object.ts

@@ -1,6 +1,6 @@
 import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
 import { isBlockedMember } from "../tool-runtime.js"
-import { isSandboxValue, SandboxMap, SandboxSet, SandboxURLSearchParams } from "../values.js"
+import { isSandboxValue, SandboxMap, SandboxPromise, SandboxSet, SandboxURLSearchParams } from "../values.js"
 import { boundedData, coerceToString } from "./value.js"
 
 export const objectStatics = new Set(["keys", "values", "entries", "hasOwn", "assign", "fromEntries"])
@@ -10,13 +10,23 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
   if (!objectStatics.has(name)) throw new InterpreterRuntimeError(`Object.${name} is not available in CodeMode.`, node)
   const requireObject = (): Record<string, unknown> => {
     const input = args[0]
-    const value = boundedData(args[0], `Object.${name} input`)
     if (Array.isArray(input)) return input as unknown as Record<string, unknown>
-    if (isSandboxValue(value)) return {}
-    if (value === null || typeof value !== "object") {
-      throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node)
+    if (isSandboxValue(input)) return {}
+    if (input instanceof SandboxPromise) {
+      throw new InterpreterRuntimeError(
+        `Object.${name} received an un-awaited Promise; await it before inspecting the result.`,
+        node,
+        "InvalidDataValue",
+      )
     }
-    return value as Record<string, unknown>
+    if (input === null || typeof input !== "object") {
+      throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue")
+    }
+    const prototype = Object.getPrototypeOf(input)
+    if (prototype !== null && prototype !== Object.prototype) {
+      throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue")
+    }
+    return input as Record<string, unknown>
   }
   const guardedSet = (out: Record<string, unknown>, key: string, item: unknown): void => {
     if (isBlockedMember(key)) throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, node)
@@ -28,15 +38,8 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
     guardedSet(out, coerceToString(key), item)
   }
   switch (name) {
-    case "keys": {
-      const value = boundedData(args[0], "Object.keys input")
-      if (isSandboxValue(value)) return []
-      if (Array.isArray(value)) return Object.keys(value)
-      if (value === null || typeof value !== "object") {
-        throw new InterpreterRuntimeError("Object.keys expects a data object or array.", node)
-      }
-      return Object.keys(value)
-    }
+    case "keys":
+      return Object.keys(requireObject())
     case "values":
       return Object.values(requireObject())
     case "entries":

+ 1 - 0
packages/codemode/src/tool-runtime.ts

@@ -617,6 +617,7 @@ export const prepare = <R>(tools: HostTools<R>, catalogBudget = defaultCatalogBu
     "",
     "Use common JavaScript data operations, functions, control flow, selected standard-library methods, and awaited tool calls. Built-ins include Date, RegExp, Map, Set, URL, URLSearchParams, and URI encoding helpers.",
     "Modules/imports, classes, generators, timers, fetch, eval, prototype access, and unlisted methods are unavailable. Use Code Mode tools for external operations. Use await with try/catch.",
+    "Prefer explicit `return`; otherwise only the final top-level expression becomes the result.",
     "Dates and URLs serialize to strings at data boundaries; Map/Set/RegExp/URLSearchParams serialize to `{}`.",
   ]
 

+ 28 - 0
packages/codemode/test/LICENSE.test262

@@ -0,0 +1,28 @@
+Test262: ECMAScript Test Suite ("Software") is protected by copyright and is being
+made available under the  "BSD License", included below. This Software may be subject to third party rights (rights
+from parties other than Ecma International), including patent rights, and no licenses under such third party rights
+are granted under this license even if the third party concerned is a member of Ecma International.  SEE THE ECMA
+CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://www.ecma-international.org/ipr FOR
+INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS*.
+
+Copyright (C) 2012 Ecma International
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+following conditions are met:
+1.   Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+     disclaimer.
+2.   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+     following disclaimer in the documentation and/or other materials provided with the distribution.
+3.   Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from
+     this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+* Ecma International Standards hereafter means Ecma International Standards as well as Ecma Technical Reports

+ 325 - 0
packages/codemode/test/array-callbacks-test262.test.ts

@@ -0,0 +1,325 @@
+/*
+ * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
+ * - test/built-ins/Array/prototype/map/15.4.4.19-8-1.js
+ * - test/built-ins/Array/prototype/map/15.4.4.19-8-2.js
+ * - test/built-ins/Array/prototype/map/15.4.4.19-8-b-1.js
+ * - test/built-ins/Array/prototype/filter/15.4.4.20-9-1.js
+ * - test/built-ins/Array/prototype/filter/15.4.4.20-9-2.js
+ * - test/built-ins/Array/prototype/filter/15.4.4.20-9-b-1.js
+ * - test/built-ins/Array/prototype/find/predicate-call-parameters.js
+ * - test/built-ins/Array/prototype/find/predicate-not-called-on-empty-array.js
+ * - test/built-ins/Array/prototype/find/return-found-value-predicate-result-is-true.js
+ * - test/built-ins/Array/prototype/find/return-undefined-if-predicate-returns-false-value.js
+ * - test/built-ins/Array/prototype/findIndex/predicate-call-parameters.js
+ * - test/built-ins/Array/prototype/findIndex/return-index-predicate-result-is-true.js
+ * - test/built-ins/Array/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js
+ * - test/built-ins/Array/prototype/findLast/predicate-call-parameters.js
+ * - test/built-ins/Array/prototype/findLast/return-found-value-predicate-result-is-true.js
+ * - test/built-ins/Array/prototype/findLast/return-undefined-if-predicate-returns-false-value.js
+ * - test/built-ins/Array/prototype/findLastIndex/predicate-call-parameters.js
+ * - test/built-ins/Array/prototype/findLastIndex/return-index-predicate-result-is-true.js
+ * - test/built-ins/Array/prototype/findLastIndex/return-negative-one-if-predicate-returns-false-value.js
+ * - test/built-ins/Array/prototype/some/15.4.4.17-7-1.js
+ * - test/built-ins/Array/prototype/some/15.4.4.17-8-1.js
+ * - test/built-ins/Array/prototype/every/15.4.4.16-7-1.js
+ * - test/built-ins/Array/prototype/every/15.4.4.16-8-1.js
+ * - test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js
+ * - test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js
+ * - test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js
+ * - test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js
+ * - test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js
+ * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js
+ * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js
+ * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js
+ * - test/built-ins/Array/prototype/flatMap/depth-always-one.js
+ * - test/built-ins/Array/prototype/flatMap/mapperfunction-throws.js
+ * - test/built-ins/Array/prototype/sort/S15.4.4.11_A1.1_T1.js
+ * - test/built-ins/Array/prototype/sort/S15.4.4.11_A2.1_T1.js
+ * - test/built-ins/Array/prototype/sort/stability-5-elements.js
+ * - test/built-ins/Array/prototype/toSorted/comparefn-controls-sort.js
+ * - test/built-ins/Array/prototype/toSorted/comparefn-default.js
+ * - test/built-ins/Array/prototype/toSorted/immutable.js
+ * - test/built-ins/Array/prototype/toSorted/zero-or-one-element.js
+ *
+ * Copyright (C) 2015 the V8 project authors. All rights reserved.
+ * Copyright (C) 2018 Mathias Bynens. All rights reserved.
+ * Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
+ * Copyright (C) 2021 Igalia, S.L. All rights reserved.
+ * Copyright (C) 2021 Microsoft. All rights reserved.
+ * Copyright (C) 2025 Google. All rights reserved.
+ * Copyright (C) 2026 Garham Lee. All rights reserved.
+ * Copyright (c) 2012 Ecma International.  All rights reserved.
+ * Copyright 2009 the Sputnik authors.  All rights reserved.
+ * Test262 portions are governed by the BSD license in LICENSE.test262.
+ */
+import { describe, expect, test } from "bun:test"
+import { Effect } from "effect"
+import { CodeMode } from "../src/index.js"
+
+const value = async (code: string) => {
+  const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
+  if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
+  return result.value
+}
+
+const cases = [
+  {
+    path: "test/built-ins/Array/prototype/map/15.4.4.19-8-1.js",
+    code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const result = input.map((value) => { input[2] = 3; input[5] = 6; return 1 }); return [result.length, result[5] === undefined]`,
+    expected: [5, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/map/15.4.4.19-8-2.js",
+    code: `const input = [1, 2, 3, 4, 5]; const result = input.map((value) => { input[4] = -1; return value > 0 ? 1 : 0 }); return [result.length, result[4]]`,
+    expected: [5, 0],
+  },
+  {
+    path: "test/built-ins/Array/prototype/map/15.4.4.19-8-b-1.js",
+    code: `const input = []; input[10] = 0; input.pop(); input[1] = undefined; let calls = 0; const result = input.map(() => { calls += 1; return 1 }); return [result.length, calls, 0 in result, 1 in result]`,
+    expected: [10, 1, false, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-1.js",
+    code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const result = input.filter(() => { input[2] = 3; input[5] = 6; return true }); return result`,
+    expected: [1, 2, 3, 4, 5],
+  },
+  {
+    path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-2.js",
+    code: `const input = [1, 2, 3, 4, 5]; return input.filter((value) => { input[2] = -1; input[4] = -1; return value > 0 })`,
+    expected: [1, 2, 4],
+  },
+  {
+    path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-b-1.js",
+    code: `const input = []; input[9] = 0; input.pop(); input[1] = undefined; let calls = 0; const result = input.filter(() => { calls += 1; return false }); return [result, calls]`,
+    expected: [[], 1],
+  },
+  {
+    path: "test/built-ins/Array/prototype/find/predicate-call-parameters.js",
+    code: `const input = [10, 20]; const seen = []; input.find((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
+    expected: [
+      [10, 0, true],
+      [20, 1, true],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/find/return-found-value-predicate-result-is-true.js",
+    code: `return [1, 2, 3].find((value) => value > 1)`,
+    expected: 2,
+  },
+  {
+    path: "test/built-ins/Array/prototype/find/return-undefined-if-predicate-returns-false-value.js",
+    code: `return [1, 2, 3].find((value) => value > 4) === undefined`,
+    expected: true,
+  },
+  {
+    path: "test/built-ins/Array/prototype/find/predicate-not-called-on-empty-array.js",
+    code: `let calls = 0; const result = [].find(() => { calls += 1; return true }); return [result === undefined, calls]`,
+    expected: [true, 0],
+  },
+  {
+    path: "test/built-ins/Array/prototype/findIndex/predicate-call-parameters.js",
+    code: `const input = [10, 20]; const seen = []; input.findIndex((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
+    expected: [
+      [10, 0, true],
+      [20, 1, true],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/findIndex/return-index-predicate-result-is-true.js",
+    code: `return [1, 2, 3].findIndex((value) => value > 1)`,
+    expected: 1,
+  },
+  {
+    path: "test/built-ins/Array/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js",
+    code: `return [1, 2, 3].findIndex((value) => value > 4)`,
+    expected: -1,
+  },
+  {
+    path: "test/built-ins/Array/prototype/findLast/predicate-call-parameters.js",
+    code: `const input = [10, 20]; const seen = []; input.findLast((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
+    expected: [
+      [20, 1, true],
+      [10, 0, true],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/findLast/return-found-value-predicate-result-is-true.js",
+    code: `return [1, 2, 3].findLast((value) => value < 3)`,
+    expected: 2,
+  },
+  {
+    path: "test/built-ins/Array/prototype/findLast/return-undefined-if-predicate-returns-false-value.js",
+    code: `return [1, 2, 3].findLast((value) => value > 4) === undefined`,
+    expected: true,
+  },
+  {
+    path: "test/built-ins/Array/prototype/findLastIndex/predicate-call-parameters.js",
+    code: `const input = [10, 20]; const seen = []; input.findLastIndex((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
+    expected: [
+      [20, 1, true],
+      [10, 0, true],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/findLastIndex/return-index-predicate-result-is-true.js",
+    code: `return [1, 2, 3].findLastIndex((value) => value < 3)`,
+    expected: 1,
+  },
+  {
+    path: "test/built-ins/Array/prototype/findLastIndex/return-negative-one-if-predicate-returns-false-value.js",
+    code: `return [1, 2, 3].findLastIndex((value) => value > 4)`,
+    expected: -1,
+  },
+  {
+    path: "test/built-ins/Array/prototype/some/15.4.4.17-7-1.js",
+    code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const seen = []; const result = input.some((value) => { input[2] = 3; seen.push(value); return false }); return [result, seen.includes(3)]`,
+    expected: [false, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/some/15.4.4.17-8-1.js",
+    code: `return [].some(() => true)`,
+    expected: false,
+  },
+  {
+    path: "test/built-ins/Array/prototype/every/15.4.4.16-7-1.js",
+    code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const seen = []; const result = input.every((value) => { input[2] = 3; seen.push(value); return true }); return [result, seen.includes(3)]`,
+    expected: [true, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/every/15.4.4.16-8-1.js",
+    code: `return [].every(() => false)`,
+    expected: true,
+  },
+  {
+    path: "test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js",
+    code: `const input = [1, 2]; input[3] = 4; input[4] = 5; let calls = 0; input.forEach(() => { calls += 1; input[2] = 3; input[5] = 6 }); return calls`,
+    expected: 5,
+  },
+  {
+    path: "test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js",
+    code: `const input = [1, 2, 3]; const seen = []; input.forEach((value, index) => { seen.push(value); if (index === 0) input.pop() }); return seen`,
+    expected: [1, 2],
+  },
+  {
+    path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js",
+    code: `const input = [1, 2]; input[3] = 4; input[4] = "5"; return input.reduce((accumulator, value) => { input[2] = 3; input[5] = 6; return accumulator + value })`,
+    expected: "105",
+  },
+  {
+    path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js",
+    code: `let calls = 0; const result = [1].reduce(() => { calls += 1; return 2 }); return [result, calls]`,
+    expected: [1, 0],
+  },
+  {
+    path: "test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js",
+    code: `const input = [1, 2, 3, 4, 5]; input.reduce(() => 1); return input`,
+    expected: [1, 2, 3, 4, 5],
+  },
+  {
+    path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js",
+    code: `const input = ["1", 2]; input[3] = 4; input[4] = "5"; return input.reduceRight((accumulator, value) => { input[2] = 3; input[5] = 6; return accumulator + value })`,
+    expected: "54321",
+  },
+  {
+    path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js",
+    code: `let calls = 0; const result = [1].reduceRight(() => { calls += 1; return 2 }); return [result, calls]`,
+    expected: [1, 0],
+  },
+  {
+    path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js",
+    code: `const input = [1, 2, 3, 4, 5]; input.reduceRight(() => 1); return input`,
+    expected: [1, 2, 3, 4, 5],
+  },
+  {
+    path: "test/built-ins/Array/prototype/flatMap/depth-always-one.js",
+    code: `return [1, 2, 3].flatMap((value) => [[value * 2]])`,
+    expected: [[2], [4], [6]],
+  },
+  {
+    path: "test/built-ins/Array/prototype/flatMap/mapperfunction-throws.js",
+    code: `try { [1, 2].flatMap(() => { throw "stop" }) } catch (error) { return error === "stop" } return false`,
+    expected: true,
+  },
+  {
+    path: "test/built-ins/Array/prototype/sort/S15.4.4.11_A1.1_T1.js",
+    code: `const input = []; input[2] = 0; input.pop(); input.sort(); return [input.length, input[0] === undefined, input[1] === undefined]`,
+    expected: [2, true, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/sort/S15.4.4.11_A2.1_T1.js",
+    code: `return ["z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n", "M", "L", "K", "J", "I", "H", "G", "F", "E", "D", "C", "B", "A"].sort()`,
+    expected: [
+      "A",
+      "B",
+      "C",
+      "D",
+      "E",
+      "F",
+      "G",
+      "H",
+      "I",
+      "J",
+      "K",
+      "L",
+      "M",
+      "n",
+      "o",
+      "p",
+      "q",
+      "r",
+      "s",
+      "t",
+      "u",
+      "v",
+      "w",
+      "x",
+      "y",
+      "z",
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/sort/stability-5-elements.js",
+    code: `const input = [{ n: "A", r: 2 }, { n: "B", r: 3 }, { n: "C", r: 2 }, { n: "D", r: 3 }, { n: "E", r: 3 }]; return input.sort((left, right) => right.r - left.r).map((item) => item.n).join("")`,
+    expected: "BDEAC",
+  },
+  {
+    path: "test/built-ins/Array/prototype/toSorted/comparefn-controls-sort.js",
+    code: `const mixed = [333, 33, 3, 222, 22, 2, 111, 11, 1]; return [[1, 2, 3, 4].toSorted((a, b) => a - b), [4, 3, 2, 1].toSorted((a, b) => a - b), mixed.toSorted((a, b) => a - b), [1, 2, 3, 4].toSorted((a, b) => b - a), [4, 3, 2, 1].toSorted((a, b) => b - a), mixed.toSorted((a, b) => b - a)]`,
+    expected: [
+      [1, 2, 3, 4],
+      [1, 2, 3, 4],
+      [1, 2, 3, 11, 22, 33, 111, 222, 333],
+      [4, 3, 2, 1],
+      [4, 3, 2, 1],
+      [333, 222, 111, 33, 22, 11, 3, 2, 1],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/toSorted/comparefn-default.js",
+    code: `return [[1, 2, 3, 4].toSorted(), [4, 3, 2, 1].toSorted(), ["a", 2, 1, "z"].toSorted(), [333, 33, 3, 222, 22, 2, 111, 11, 1].toSorted()]`,
+    expected: [
+      [1, 2, 3, 4],
+      [1, 2, 3, 4],
+      [1, 2, "a", "z"],
+      [1, 11, 111, 2, 22, 222, 3, 33, 333],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/toSorted/immutable.js",
+    code: `const input = [2, 0, 1]; const result = input.toSorted(); return [input, result !== input]`,
+    expected: [[2, 0, 1], true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/toSorted/zero-or-one-element.js",
+    code: `const zero = []; const one = [1]; const zeroResult = zero.toSorted(); const oneResult = one.toSorted(); return [zeroResult, oneResult, zeroResult !== zero, oneResult !== one]`,
+    expected: [[], [1], true, true],
+  },
+] as const
+
+describe("Test262 Array callback adaptations", () => {
+  for (const item of cases) {
+    test(item.path, async () => {
+      expect(await value(item.code)).toEqual(item.expected)
+    })
+  }
+})

+ 323 - 0
packages/codemode/test/array-core-test262.test.ts

@@ -0,0 +1,323 @@
+/*
+ * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
+ * - test/built-ins/Array/prototype/includes/samevaluezero.js
+ * - test/built-ins/Array/prototype/includes/using-fromindex.js
+ * - test/built-ins/Array/prototype/join/S15.4.4.5_A3.1_T1.js
+ * - test/built-ins/Array/prototype/join/S15.4.4.5_A3.2_T1.js
+ * - test/built-ins/Array/prototype/slice/S15.4.4.10_A1.2_T2.js
+ * - test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T1.js
+ * - test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T2.js
+ * - test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T3.js
+ * - test/built-ins/Array/prototype/indexOf/fromindex-zero-conversion.js
+ * - test/built-ins/Array/prototype/indexOf/length-zero-returns-minus-one.js
+ * - test/built-ins/Array/prototype/lastIndexOf/fromindex-zero-conversion.js
+ * - test/built-ins/Array/prototype/lastIndexOf/length-zero-returns-minus-one.js
+ * - test/built-ins/Array/prototype/at/returns-item.js
+ * - test/built-ins/Array/prototype/at/returns-item-relative-index.js
+ * - test/built-ins/Array/prototype/at/returns-undefined-for-out-of-range-index.js
+ * - test/built-ins/Array/prototype/flat/null-undefined-elements.js
+ * - test/built-ins/Array/prototype/flat/positive-infinity.js
+ * - test/built-ins/Array/prototype/reverse/S15.4.4.8_A1_T1.js
+ * - test/built-ins/Array/prototype/toReversed/immutable.js
+ * - test/built-ins/Array/prototype/toReversed/zero-or-one-element.js
+ * - test/built-ins/Array/prototype/with/immutable.js
+ * - test/built-ins/Array/prototype/with/index-negative.js
+ * - test/built-ins/Array/prototype/push/S15.4.4.7_A1_T1.js
+ * - test/built-ins/Array/prototype/pop/S15.4.4.6_A1.1_T1.js
+ * - test/built-ins/Array/prototype/shift/S15.4.4.9_A1.1_T1.js
+ * - test/built-ins/Array/prototype/unshift/S15.4.4.13_A1_T1.js
+ * - test/built-ins/Array/prototype/splice/S15.4.4.12_A1.1_T1.js
+ * - test/built-ins/Array/prototype/splice/S15.4.4.12_A1.2_T1.js
+ * - test/built-ins/Array/prototype/splice/called_with_one_argument.js
+ * - test/built-ins/Array/prototype/fill/fill-values.js
+ * - test/built-ins/Array/prototype/fill/fill-values-custom-start-and-end.js
+ * - test/built-ins/Array/prototype/fill/return-this.js
+ * - test/built-ins/Array/prototype/copyWithin/non-negative-target-start-and-end.js
+ * - test/built-ins/Array/prototype/copyWithin/return-this.js
+ * - test/built-ins/Array/prototype/keys/iteration.js
+ * - test/built-ins/Array/prototype/values/iteration.js
+ * - test/built-ins/Array/prototype/entries/iteration.js
+ * - test/built-ins/Array/isArray/15.4.3.2-0-3.js
+ * - test/built-ins/Array/isArray/15.4.3.2-0-4.js
+ * - test/built-ins/Array/from/from-array.js
+ * - test/built-ins/Array/from/from-string.js
+ * - test/built-ins/Array/from/array-like-has-length-but-no-indexes-with-values.js
+ * - test/built-ins/Array/of/creates-a-new-array-from-arguments.js
+ *
+ * Copyright (C) 2015 André Bargull. All rights reserved.
+ * Copyright (C) 2015 the V8 project authors. All rights reserved.
+ * Copyright (C) 2016 the V8 project authors. All rights reserved.
+ * Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
+ * Copyright (C) 2020 Alexey Shvayka. All rights reserved.
+ * Copyright (C) 2020 Rick Waldron. All rights reserved.
+ * Copyright (C) 2021 Igalia, S.L. All rights reserved.
+ * Copyright (c) 2012 Ecma International.  All rights reserved.
+ * Copyright (c) 2014 Hank Yates. All rights reserved.
+ * Copyright (c) 2015 the V8 project authors. All rights reserved.
+ * Copyright (c) 2021 Rick Waldron.  All rights reserved.
+ * Copyright 2009 the Sputnik authors.  All rights reserved.
+ * Copyright 2015 Microsoft Corporation. All rights reserved.
+ * Copyright 2016 The V8 project authors. All rights reserved.
+ * Test262 portions are governed by the BSD license in LICENSE.test262.
+ */
+import { describe, expect, test } from "bun:test"
+import { Effect } from "effect"
+import { CodeMode } from "../src/index.js"
+
+const value = async (code: string) => {
+  const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
+  if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
+  return result.value
+}
+
+const cases = [
+  {
+    path: "test/built-ins/Array/prototype/includes/samevaluezero.js",
+    code: `const input = [42, 0, 1, NaN]; return [input.includes(42), input.includes("42"), input.includes([42]), input.includes(true), input.includes(NaN), input.includes(0), input.includes(-0), input.includes(null), input.includes("")]`,
+    expected: [true, false, false, false, true, true, true, false, false],
+  },
+  {
+    path: "test/built-ins/Array/prototype/includes/using-fromindex.js",
+    code: `const input = ["a", "b", "c"]; return [input.includes("a", 0), input.includes("a", 1), input.includes("a", -4), input.includes("a", -3), input.includes("a", -2), input.includes("b", 0), input.includes("b", 1), input.includes("b", 2), input.includes("b", -3), input.includes("b", -2), input.includes("b", -1), input.includes("c", 0), input.includes("c", 2), input.includes("c", 3), input.includes("c", -3), input.includes("c", -1)]`,
+    expected: [true, false, true, true, false, true, true, false, true, true, false, true, true, false, true, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/join/S15.4.4.5_A3.1_T1.js",
+    code: `return [[0, 1, 2, 3].join("&"), [0, 1, 2, 3].join("")]`,
+    expected: ["0&1&2&3", "0123"],
+  },
+  {
+    path: "test/built-ins/Array/prototype/join/S15.4.4.5_A3.2_T1.js",
+    code: `return [
+      ["", "", ""].join(""),
+      ["&", "&", "&"].join("&"),
+      [true, true, true].join(),
+      [null, null, null].join(),
+      [undefined, undefined, undefined].join(),
+      [Infinity, Infinity, Infinity].join(),
+      [NaN, NaN, NaN].join(),
+    ]`,
+    expected: ["", "&&&&&", "true,true,true", ",,", ",,", "Infinity,Infinity,Infinity", "NaN,NaN,NaN"],
+  },
+  {
+    path: "test/built-ins/Array/prototype/slice/S15.4.4.10_A1.2_T2.js",
+    code: `return [0, 1, 2, 3, 4].slice(-1, 5)`,
+    expected: [4],
+  },
+  {
+    path: "test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T1.js",
+    code: `return [].concat([0, 1], [2, 3, 4])`,
+    expected: [0, 1, 2, 3, 4],
+  },
+  {
+    path: "test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T2.js",
+    code: `const object = { value: 1 }; const result = [0].concat(object, [1, 2], -1, true, "NaN"); return [result, result[1] === object]`,
+    expected: [[0, { value: 1 }, 1, 2, -1, true, "NaN"], true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T3.js",
+    code: `const input = [0, 1]; const result = input.concat(); return [result, result !== input]`,
+    expected: [[0, 1], true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/indexOf/fromindex-zero-conversion.js",
+    code: `const result = [true].indexOf(true, -0); return [result, 1 / result === Infinity]`,
+    expected: [0, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/indexOf/length-zero-returns-minus-one.js",
+    code: `return [].indexOf(1)`,
+    expected: -1,
+  },
+  {
+    path: "test/built-ins/Array/prototype/lastIndexOf/fromindex-zero-conversion.js",
+    code: `const result = [true].lastIndexOf(true, -0); return [result, 1 / result === Infinity]`,
+    expected: [0, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/lastIndexOf/length-zero-returns-minus-one.js",
+    code: `return [].lastIndexOf(1)`,
+    expected: -1,
+  },
+  {
+    path: "test/built-ins/Array/prototype/at/returns-item.js",
+    code: `const input = [1, 2, 3, 4, undefined, 5]; return [input.at(0), input.at(1), input.at(2), input.at(3), input.at(4) === undefined, input.at(5)]`,
+    expected: [1, 2, 3, 4, true, 5],
+  },
+  {
+    path: "test/built-ins/Array/prototype/at/returns-item-relative-index.js",
+    code: `const input = [1, 2, 3, 4, undefined, 5]; return [input.at(0), input.at(-1), input.at(-2) === undefined, input.at(-3), input.at(-4)]`,
+    expected: [1, 5, true, 4, 3],
+  },
+  {
+    path: "test/built-ins/Array/prototype/at/returns-undefined-for-out-of-range-index.js",
+    code: `const input = []; return [input.at(-2) === undefined, input.at(0) === undefined, input.at(1) === undefined]`,
+    expected: [true, true, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/flat/null-undefined-elements.js",
+    code: `const result = [1, [null, [undefined]]].flat(2); return [result.length, result[0], result[1] === null, result[2] === undefined]`,
+    expected: [3, 1, true, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/flat/positive-infinity.js",
+    code: `return [1, [2, [3, [4]]]].flat(Infinity)`,
+    expected: [1, 2, 3, 4],
+  },
+  {
+    path: "test/built-ins/Array/prototype/reverse/S15.4.4.8_A1_T1.js",
+    code: `const empty = []; const one = [1]; const input = [1, 2]; const emptyResult = empty.reverse(); const oneResult = one.reverse(); const result = input.reverse(); return [emptyResult === empty, oneResult === one, result === input, input]`,
+    expected: [true, true, true, [2, 1]],
+  },
+  {
+    path: "test/built-ins/Array/prototype/toReversed/immutable.js",
+    code: `const input = [0, 1, 2]; const result = input.toReversed(); return [input, result !== input]`,
+    expected: [[0, 1, 2], true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/toReversed/zero-or-one-element.js",
+    code: `const zero = []; const one = [1]; const zeroResult = zero.toReversed(); const oneResult = one.toReversed(); return [zeroResult, oneResult, zeroResult !== zero, oneResult !== one]`,
+    expected: [[], [1], true, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/with/immutable.js",
+    code: `const input = [0, 1, 2]; const result = input.with(1, 3); return [input, result !== input, input.with(1, 1) !== input]`,
+    expected: [[0, 1, 2], true, true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/with/index-negative.js",
+    code: `const input = [0, 1, 2]; return [input.with(-1, 4), input.with(-3, 4)]`,
+    expected: [
+      [0, 1, 4],
+      [4, 1, 2],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/push/S15.4.4.7_A1_T1.js",
+    code: `const input = []; return [input.push(1), input.push(), input.push(-1), input]`,
+    expected: [1, 1, 2, [1, -1]],
+  },
+  {
+    path: "test/built-ins/Array/prototype/pop/S15.4.4.6_A1.1_T1.js",
+    code: `const input = []; return [input.pop() === undefined, input.length]`,
+    expected: [true, 0],
+  },
+  {
+    path: "test/built-ins/Array/prototype/shift/S15.4.4.9_A1.1_T1.js",
+    code: `const input = []; return [input.shift() === undefined, input.length]`,
+    expected: [true, 0],
+  },
+  {
+    path: "test/built-ins/Array/prototype/unshift/S15.4.4.13_A1_T1.js",
+    code: `const input = []; return [input.unshift(1), input[0], input.unshift(), input.unshift(-1), input]`,
+    expected: [1, 1, 1, 2, [-1, 1]],
+  },
+  {
+    path: "test/built-ins/Array/prototype/splice/S15.4.4.12_A1.1_T1.js",
+    code: `const input = [0, 1, 2, 3]; const removed = input.splice(0, 3); return [input, removed]`,
+    expected: [[3], [0, 1, 2]],
+  },
+  {
+    path: "test/built-ins/Array/prototype/splice/S15.4.4.12_A1.2_T1.js",
+    code: `const input = [0, 1]; const removed = input.splice(-2, -1); return [input, removed]`,
+    expected: [[0, 1], []],
+  },
+  {
+    path: "test/built-ins/Array/prototype/splice/called_with_one_argument.js",
+    code: `const input = ["first", "second", "third"]; const removed = input.splice(1); return [input, removed]`,
+    expected: [["first"], ["second", "third"]],
+  },
+  {
+    path: "test/built-ins/Array/prototype/fill/fill-values-custom-start-and-end.js",
+    code: `const input = [0, 0, 0, 0, 0]; input.fill(8, -3, 4); const sparse = []; sparse[4] = 0; sparse.fill(8, 1, 3); return [[0, 0, 0].fill(8, 1, 2), input, [0, 0, 0, 0, 0].fill(8, -2, -1), [0, 0, 0, 0, 0].fill(8, -1, -3), [0 in sparse, sparse[1], sparse[2], 3 in sparse, sparse[4]]]`,
+    expected: [
+      [0, 8, 0],
+      [0, 0, 8, 8, 0],
+      [0, 0, 0, 8, 0],
+      [0, 0, 0, 0, 0],
+      [false, 8, 8, false, 0],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/fill/return-this.js",
+    code: `const input = []; return input.fill(1) === input`,
+    expected: true,
+  },
+  {
+    path: "test/built-ins/Array/prototype/fill/fill-values.js",
+    code: `const omitted = [0, 0].fill(); return [[].fill(8), omitted.map((value) => value === undefined), [0, 0, 0].fill(8)]`,
+    expected: [[], [true, true], [8, 8, 8]],
+  },
+  {
+    path: "test/built-ins/Array/prototype/copyWithin/non-negative-target-start-and-end.js",
+    code: `return [[0, 1, 2, 3].copyWithin(0, 0, 0), [0, 1, 2, 3].copyWithin(0, 0, 2), [0, 1, 2, 3].copyWithin(0, 1, 2), [0, 1, 2, 3].copyWithin(1, 0, 2), [0, 1, 2, 3, 4, 5].copyWithin(1, 3, 5)]`,
+    expected: [
+      [0, 1, 2, 3],
+      [0, 1, 2, 3],
+      [1, 1, 2, 3],
+      [0, 0, 1, 3],
+      [0, 3, 4, 3, 4, 5],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/prototype/copyWithin/return-this.js",
+    code: `const input = [0, 1, 2, 3]; const result = input.copyWithin(1, 0, 2); return [input, result === input]`,
+    expected: [[0, 0, 1, 3], true],
+  },
+  {
+    path: "test/built-ins/Array/prototype/keys/iteration.js",
+    code: `return ["a", "b", "c"].keys()`,
+    expected: [0, 1, 2],
+  },
+  {
+    path: "test/built-ins/Array/prototype/values/iteration.js",
+    code: `return ["a", "b", "c"].values()`,
+    expected: ["a", "b", "c"],
+  },
+  {
+    path: "test/built-ins/Array/prototype/entries/iteration.js",
+    code: `return ["a", "b"].entries()`,
+    expected: [
+      [0, "a"],
+      [1, "b"],
+    ],
+  },
+  {
+    path: "test/built-ins/Array/isArray/15.4.3.2-0-3.js",
+    code: `return [Array.isArray([]), Array.isArray([1]), Array.isArray(Array.of(1))]`,
+    expected: [true, true, true],
+  },
+  {
+    path: "test/built-ins/Array/isArray/15.4.3.2-0-4.js",
+    code: `return [Array.isArray(42), Array.isArray({}), Array.isArray(null), Array.isArray("array")]`,
+    expected: [false, false, false, false],
+  },
+  {
+    path: "test/built-ins/Array/from/from-array.js",
+    code: `const input = [0, "foo", undefined, Infinity]; const result = Array.from(input); return [result.length, result[0], result[1], result[2] === undefined, result[3] === Infinity, result !== input, result instanceof Array]`,
+    expected: [4, 0, "foo", true, true, true, true],
+  },
+  {
+    path: "test/built-ins/Array/from/from-string.js",
+    code: `return Array.from("Test")`,
+    expected: ["T", "e", "s", "t"],
+  },
+  {
+    path: "test/built-ins/Array/from/array-like-has-length-but-no-indexes-with-values.js",
+    code: `const result = Array.from({ length: 5 }); const mapped = result.map(() => 1); return [result.length, result.map((value) => value === undefined), mapped.length, mapped]`,
+    expected: [5, [true, true, true, true, true], 5, [1, 1, 1, 1, 1]],
+  },
+  {
+    path: "test/built-ins/Array/of/creates-a-new-array-from-arguments.js",
+    code: `const mixed = Array.of(undefined, false, null, undefined); return [Array.of("Mike", "Rick", "Leo"), mixed.length, mixed[0] === undefined, mixed[1], mixed[2], mixed[3] === undefined, Array.of()]`,
+    expected: [["Mike", "Rick", "Leo"], 4, true, false, null, true, []],
+  },
+] as const
+
+describe("Test262 Array core adaptations", () => {
+  for (const item of cases) {
+    test(item.path, async () => {
+      expect(await value(item.code)).toEqual(item.expected)
+    })
+  }
+})

+ 21 - 0
packages/codemode/test/codemode.test.ts

@@ -661,6 +661,9 @@ describe("CodeMode public contract", () => {
     expect(instructions).toContain("URL, URLSearchParams, and URI encoding helpers")
     expect(instructions).not.toContain("host globals")
     expect(instructions).toContain("Use Code Mode tools for external operations")
+    expect(instructions).toContain(
+      "Prefer explicit `return`; otherwise only the final top-level expression becomes the result.",
+    )
     expect(instructions).toContain(
       "Dates and URLs serialize to strings at data boundaries; Map/Set/RegExp/URLSearchParams serialize to `{}`.",
     )
@@ -1084,6 +1087,24 @@ describe("CodeMode public contract", () => {
     expect(Schema.decodeUnknownSync(CodeMode.Result)(JSON.parse(JSON.stringify(result)))).toStrictEqual(result)
   })
 
+  test("returns the final top-level expression when return is omitted", async () => {
+    const result = await Effect.runPromise(CodeMode.execute({ code: `1; 2` }))
+
+    expect(result).toStrictEqual({ ok: true, value: 2, toolCalls: [] })
+  })
+
+  test("does not implicitly return expressions nested in control flow", async () => {
+    const result = await Effect.runPromise(CodeMode.execute({ code: `if (true) { 2 }` }))
+
+    expect(result).toStrictEqual({ ok: true, value: null, toolCalls: [] })
+  })
+
+  test("returns null when the final top-level statement is not an expression", async () => {
+    const result = await Effect.runPromise(CodeMode.execute({ code: `1; const value = 2` }))
+
+    expect(result).toStrictEqual({ ok: true, value: null, toolCalls: [] })
+  })
+
   test("rejects invalid configuration and discovery limits", async () => {
     expect(() => CodeMode.execute({ code: "return 1", limits: { timeoutMs: 0 } })).toThrow(RangeError)
     expect(() => CodeMode.execute({ code: "return 1", limits: { timeoutMs: Number.POSITIVE_INFINITY } })).toThrow(

Diff do ficheiro suprimidas por serem muito extensas
+ 524 - 88
packages/codemode/test/fixtures/opencode-v2-openapi.json


+ 4 - 4
packages/codemode/test/openapi.test.ts

@@ -177,13 +177,13 @@ describe("OpenAPI.fromSpec", () => {
     const spec = await opencodeSpec()
     const result = OpenAPI.fromSpec({ spec, baseUrl })
 
-    expect(result.skipped).toHaveLength(5)
+    expect(result.skipped).toHaveLength(4)
     expect(result.skipped).toContainEqual({
       method: "GET",
       path: "/api/pty/{ptyID}/connect",
       reason: "WebSocket operations are not supported",
     })
-    expect(result.skipped.filter((item) => item.reason === "SSE operations are not supported")).toHaveLength(3)
+    expect(result.skipped.filter((item) => item.reason === "SSE operations are not supported")).toHaveLength(2)
     expect(result.skipped).toContainEqual({
       method: "GET",
       path: "/api/fs/read/*",
@@ -210,11 +210,11 @@ describe("OpenAPI.fromSpec", () => {
     if (!Tool.isDefinition(instructionPut)) throw new Error("v2.session.instructions.entry.put was not generated")
     expect(inputTypeScript(instructionPut)).toBe("{ sessionID: string; key: string; value: unknown }")
     expect(toolAt(result.tools, "v2_session_instructions_entry_put_2")).toBeUndefined()
-    expect(toolAt(result.tools, "v2.pty.connect")).toBeUndefined()
+    expect(Tool.isDefinition(toolAt(result.tools, "v2.pty.connect"))).toBe(false)
     expect(toolAt(result.tools, "v2.session.log")).toBeUndefined()
     expect(toolAt(result.tools, "v2.event.subscribe")).toBeUndefined()
     expect(toolAt(result.tools, "v2.fs.read")).toBeUndefined()
-    expect(toolAt(result.tools, "v2.pty.connectToken")).not.toBeUndefined()
+    expect(toolAt(result.tools, "v2.pty.connect.token")).not.toBeUndefined()
   })
 
   test("preserves operation path sanitization and collision handling", () => {

+ 9 - 87
packages/codemode/test/parity.test.ts

@@ -42,11 +42,6 @@ describe("H2: string property access reads as undefined (not a throw)", () => {
   test("unknown property on a number is undefined", async () => {
     expect(await value(`return (5).foo ?? "n"`)).toBe("n")
   })
-
-  test("supported string methods still work", async () => {
-    expect(await value(`return "AB".toLowerCase()`)).toBe("ab")
-    expect(await value(`return "hello".length`)).toBe(5)
-  })
 })
 
 describe("H3: array property access reads as undefined (not a throw)", () => {
@@ -63,8 +58,7 @@ describe("H3: array property access reads as undefined (not a throw)", () => {
     expect(await value(`return [1,2,3].toSpliced === undefined`)).toBe(true)
   })
 
-  test("supported array methods and indexing still work", async () => {
-    expect(await value(`return [1,2,3].map(x => x + 1)`)).toEqual([2, 3, 4])
+  test("array indexing still works", async () => {
     expect(await value(`return [1,2,3][9] === undefined`)).toBe(true)
     expect(await value(`return [1,2,3][9]`)).toBeNull()
   })
@@ -202,9 +196,6 @@ describe("Error values and instanceof", () => {
       "TypeError",
       true,
     ])
-    expect(await value(`try { "a".normalize("NOPE") } catch (e) { return [e.name, e instanceof RangeError] }`)).toEqual(
-      ["RangeError", true],
-    )
     expect(await value(`try { "a".match("(") } catch (e) { return [e.name, e instanceof SyntaxError] }`)).toEqual([
       "SyntaxError",
       true,
@@ -263,55 +254,18 @@ describe("Error values and instanceof", () => {
   })
 })
 
-describe("array methods: splice, fill, copyWithin, keys/values/entries", () => {
-  test("sort and reverse mutate and return the receiver", async () => {
-    expect(
-      await value(`
-        const sorted = [3, 1, 2]
-        const sortResult = sorted.sort((a, b) => a - b)
-        const reversed = [1, 2, 3]
-        const reverseResult = reversed.reverse()
-        return { sorted, sameSort: sorted === sortResult, reversed, sameReverse: reversed === reverseResult }
-      `),
-    ).toEqual({ sorted: [1, 2, 3], sameSort: true, reversed: [3, 2, 1], sameReverse: true })
-  })
-
-  test("array callbacks receive the receiver and observe later mutations", async () => {
+describe("CodeMode-specific array behavior", () => {
+  test("sort with a comparator mutates and returns the receiver", async () => {
     expect(
       await value(`
-        const values = [1, 2, 3]
-        const seen = values.map((value, index, receiver) => {
-          if (index === 0) values[1] = 9
-          return [value, receiver === values]
-        })
-        return seen
-      `),
-    ).toEqual([
-      [1, true],
-      [9, true],
-      [3, true],
-    ])
-    expect(
-      await value(`
-        const values = [1, 2, 3]
-        const seen = []
-        values.forEach((value, index) => {
-          seen.push(value)
-          if (index === 0) values.pop()
-        })
-        return seen
+        const input = [3, 1, 2]
+        const result = input.sort((a, b) => a - b)
+        return { input, same: input === result }
       `),
-    ).toEqual([1, 2])
+    ).toEqual({ input: [1, 2, 3], same: true })
   })
 
-  test("splice removes in place and returns the removed elements", async () => {
-    expect(await value(`const a = [1,2,3,4]; const removed = a.splice(1, 2); return { removed, a }`)).toEqual({
-      removed: [2, 3],
-      a: [1, 4],
-    })
-  })
-
-  test("splice inserts new elements at the cut", async () => {
+  test("splice can replace and insert elements", async () => {
     expect(await value(`const a = ["a","d"]; a.splice(1, 0, "b", "c"); return a`)).toEqual(["a", "b", "c", "d"])
     expect(await value(`const a = [1,2,3]; const removed = a.splice(1, 1, "x"); return { removed, a }`)).toEqual({
       removed: [2],
@@ -319,32 +273,12 @@ describe("array methods: splice, fill, copyWithin, keys/values/entries", () => {
     })
   })
 
-  test("splice with one argument removes to the end; negative start counts back", async () => {
-    expect(await value(`const a = [1,2,3]; const removed = a.splice(1); return { removed, a }`)).toEqual({
-      removed: [2, 3],
-      a: [1],
-    })
-    expect(await value(`const a = [1,2,3]; const removed = a.splice(-1); return { removed, a }`)).toEqual({
-      removed: [3],
-      a: [1, 2],
-    })
-  })
-
   test("splice rejects inserting a container into itself", async () => {
     const err = await error(`const a = [1]; a.splice(0, 0, [a]); return a`)
     expect(err.kind).toBe("InvalidDataValue")
     expect(err.message).toContain("circular")
   })
 
-  test("fill overwrites a range and returns the mutated array", async () => {
-    expect(await value(`const a = [1,2,3,4]; return a.fill(0, 1, 3)`)).toEqual([1, 0, 0, 4])
-    expect(await value(`return [1,2,3].fill("z")`)).toEqual(["z", "z", "z"])
-  })
-
-  test("copyWithin copies a range in place", async () => {
-    expect(await value(`return [1,2,3,4,5].copyWithin(0, 3)`)).toEqual([4, 5, 3, 4, 5])
-  })
-
   test("keys/values/entries return arrays usable with for...of and spread", async () => {
     expect(await value(`return [...["x","y","z"].keys()]`)).toEqual([0, 1, 2])
     expect(await value(`return ["x","y"].values()`)).toEqual(["x", "y"])
@@ -359,16 +293,9 @@ describe("array methods: splice, fill, copyWithin, keys/values/entries", () => {
   })
 })
 
-describe("string methods: localeCompare, normalize, trim aliases", () => {
+describe("CodeMode-specific string behavior", () => {
   test("localeCompare orders strings for sorting", async () => {
     expect(await value(`return ["b","a","c"].sort((x, y) => x.localeCompare(y))`)).toEqual(["a", "b", "c"])
-    expect(await value(`return "a".localeCompare("a")`)).toBe(0)
-  })
-
-  test("normalize applies unicode normalization forms", async () => {
-    expect(await value(`return "\\u0065\\u0301".normalize("NFC").length`)).toBe(1)
-    expect(await value(`return "\\u00e9".normalize("NFD").length`)).toBe(2)
-    expect(await value(`return "x".normalize() === "x"`)).toBe(true)
   })
 
   test("an invalid normalize form is a clear catchable error", async () => {
@@ -453,11 +380,6 @@ describe("H5: builtin coercion functions work as array callbacks", () => {
     expect(await value(`return [1, 2, 3].map(String)`)).toEqual(["1", "2", "3"])
   })
 
-  test("arrow callbacks still work (no regression)", async () => {
-    expect(await value(`return [1, 2, 3, 4].filter(x => x % 2 === 0)`)).toEqual([2, 4])
-    expect(await value(`return [1, 2, 3].reduce((a, b) => a + b, 0)`)).toBe(6)
-  })
-
   test("a non-callable callback is still rejected", async () => {
     const err = await error(`return [1,2,3].map(42)`)
     expect(err.message).toContain("callback")

+ 6 - 0
packages/codemode/test/promise.test.ts

@@ -313,6 +313,12 @@ describe("promises at data boundaries", () => {
     expect(diagnostic.message).toContain("await tools.ns.tool(...)")
   })
 
+  test("collection helpers do not let un-awaited promises cross the result boundary", async () => {
+    const diagnostic = await error(`return Array.from([Promise.resolve(1)])`)
+    expect(diagnostic.kind).toBe("InvalidDataValue")
+    expect(diagnostic.message).toContain("un-awaited Promise")
+  })
+
   test("passing an un-awaited promise as a tool argument is a clear diagnostic", async () => {
     const diagnostic = await error(`return await tools.host.sleepy({ id: tools.host.sleepy({ id: 1 }) })`)
     expect(diagnostic.kind).toBe("InvalidDataValue")

+ 108 - 19
packages/codemode/test/stdlib.test.ts

@@ -19,6 +19,28 @@ const error = async (code: string) => {
   return result.error
 }
 
+describe("Number and Math", () => {
+  test("Math.random returns a number in [0, 1)", async () => {
+    expect(await value(`const n = Math.random(); return typeof n === "number" && n >= 0 && n < 1`)).toBe(true)
+  })
+
+  test("Number exposes native non-finite constants", async () => {
+    expect(
+      await value(
+        `return [Number.isNaN(Number.NaN), Number.POSITIVE_INFINITY === Infinity, Number.NEGATIVE_INFINITY === -Infinity]`,
+      ),
+    ).toEqual([true, true, true])
+  })
+
+  test("Number valueOf returns its primitive receiver", async () => {
+    expect(await value(`return (42).valueOf()`)).toBe(42)
+  })
+
+  test("Number valueOf does not enable boxed numbers", async () => {
+    expect((await error(`return new Number(42)`)).kind).toBe("UnsupportedSyntax")
+  })
+})
+
 describe("Date", () => {
   test("Date.now() returns a number", async () => {
     expect(await value(`return typeof Date.now()`)).toBe("number")
@@ -132,9 +154,7 @@ describe("RegExp", () => {
     ).toEqual(["1", "22"])
   })
 
-  test("string match: non-global carries index, global lists all matches", async () => {
-    expect(await value(`const m = "a1b22".match(/\\d+/); return [m[0], m.index]`)).toEqual(["1", 1])
-    expect(await value(`return "a1b22".match(/\\d+/g)`)).toEqual(["1", "22"])
+  test("an unmatched string pattern returns null", async () => {
     expect(await value(`return "abc".match(/\\d/)`)).toBeNull()
   })
 
@@ -142,13 +162,6 @@ describe("RegExp", () => {
     expect(await value(`return "a1b22".matchAll(/(\\d+)/g).map((m) => m[1])`)).toEqual(["1", "22"])
   })
 
-  test("replace and replaceAll with patterns and $1 substitution", async () => {
-    expect(await value(`return "a1b2".replace(/\\d/, "#")`)).toBe("a#b2")
-    expect(await value(`return "a1b2".replace(/\\d/g, "#")`)).toBe("a#b#")
-    expect(await value(`return "a1b2".replaceAll(/\\d/g, "#")`)).toBe("a#b#")
-    expect(await value(`return "hi bob".replace(/b(o)b/, "[$1]")`)).toBe("hi [o]")
-  })
-
   test("function replacers receive captures, offsets, input, and named groups", async () => {
     expect(
       await value(`
@@ -214,12 +227,6 @@ describe("RegExp", () => {
     expect(await value(`try { "a".replaceAll(/a/, "b"); return "no" } catch { return "caught" }`)).toBe("caught")
   })
 
-  test("split and search accept patterns", async () => {
-    expect(await value(`return "a1b22c".split(/\\d+/)`)).toEqual(["a", "b", "c"])
-    expect(await value(`return "ab42".search(/\\d/)`)).toBe(2)
-    expect(await value(`return "ab".search(/\\d/)`)).toBe(-1)
-  })
-
   test("new RegExp constructs from strings; invalid patterns are catchable", async () => {
     expect(await value(`return new RegExp("a+", "i").test("AAA")`)).toBe(true)
     expect(await value(`try { new RegExp("("); return "no" } catch { return "caught" }`)).toBe("caught")
@@ -628,9 +635,7 @@ describe("stdlib integration", () => {
       true,
     )
     expect(
-      await value(
-        `try { Object.fromEntries(new Map([["fn", Math.max]])); return false } catch { return true }`,
-      ),
+      await value(`try { Object.fromEntries(new Map([["fn", Math.max]])); return false } catch { return true }`),
     ).toBe(true)
   })
 
@@ -751,6 +756,43 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
     )
   })
 
+  test("Object.values/entries preserve nested object identity", async () => {
+    expect(
+      await value(`
+      const child = { selected: false }
+      const rows = { a: child }
+      Object.values(rows)[0].selected = true
+      return child.selected
+    `),
+    ).toBe(true)
+    expect(
+      await value(`
+      const child = { selected: false }
+      const rows = { a: child }
+      Object.entries(rows)[0][1].selected = true
+      return child.selected
+    `),
+    ).toBe(true)
+  })
+
+  test("Object enumeration preserves promises and callable references", async () => {
+    expect(
+      await value(`
+      const pending = Promise.resolve(1)
+      const source = { pending }
+      return [Object.keys(source), Object.hasOwn(source, "pending"), await Object.values(source)[0], await Object.entries(source)[0][1]]
+    `),
+    ).toEqual([["pending"], true, 1, 1])
+    expect(await value(`return Object.values({ max: Math.max })[0](1, 2)`)).toBe(2)
+  })
+
+  test("Object enumeration rejects invalid receivers and gives promises an await hint", async () => {
+    const diagnostic = await error(`return Object.keys(Promise.resolve({ a: 1 }))`)
+    expect(diagnostic.kind).toBe("InvalidDataValue")
+    expect(diagnostic.message).toContain("await")
+    expect((await error(`return Object.keys(Math)`)).kind).toBe("InvalidDataValue")
+  })
+
   test("Object.assign keeps Maps usable", async () => {
     expect(await value(`const merged = Object.assign({}, { m: new Map([["a", 1]]) }); return merged.m.get("a")`)).toBe(
       1,
@@ -773,6 +815,53 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
     expect(await value(`return Array.from([new Date(5)])[0].getTime()`)).toBe(5)
   })
 
+  test("Array.from and Array.of preserve nested object identity", async () => {
+    expect(
+      await value(`
+      const child = { selected: false }
+      Array.from([child])[0].selected = true
+      return child.selected
+    `),
+    ).toBe(true)
+    expect(
+      await value(`
+      const child = { selected: false }
+      Array.of(child)[0].selected = true
+      return child.selected
+    `),
+    ).toBe(true)
+  })
+
+  test("Array.from and Array.of preserve promises and callable references", async () => {
+    expect(
+      await value(`
+      const pending = Promise.resolve(1)
+      return [await Array.from([pending])[0], await Array.of(pending)[0]]
+    `),
+    ).toEqual([1, 1])
+    expect(await value(`return [Array.from([Math.max])[0](1, 2), Array.of(Math.max)[0](3, 4)]`)).toEqual([2, 4])
+  })
+
+  test("Array.from preserves identity across supported collection shapes", async () => {
+    expect(
+      await value(`
+      const child = { selected: false }
+      const fromArrayLike = Array.from({ 0: child, length: 1 })
+      const fromMap = Array.from(new Map([["child", child]]))
+      const fromSet = Array.from(new Set([child]))
+      fromArrayLike[0].selected = true
+      return [fromMap[0][1] === child, fromSet[0] === child, child.selected]
+    `),
+    ).toEqual([true, true, true])
+  })
+
+  test("Array.from rejects invalid receivers and gives promises an await hint", async () => {
+    const diagnostic = await error(`return Array.from(Promise.resolve([1]))`)
+    expect(diagnostic.kind).toBe("InvalidDataValue")
+    expect(diagnostic.message).toContain("await")
+    expect((await error(`return Array.from(() => 1)`)).kind).toBe("InvalidDataValue")
+  })
+
   test("regexes stay callable through Object.values", async () => {
     expect(await value(`return Object.values({ r: /ab+/ })[0].test("abb")`)).toBe(true)
   })

+ 580 - 0
packages/codemode/test/string-core-test262.test.ts

@@ -0,0 +1,580 @@
+/*
+ * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
+ * - test/built-ins/String/prototype/toLowerCase/S15.5.4.16_A2_T1.js
+ * - test/built-ins/String/prototype/toLowerCase/special_casing.js
+ * - test/built-ins/String/prototype/toLowerCase/special_casing_conditional.js
+ * - test/built-ins/String/prototype/toLowerCase/Final_Sigma_U180E.js
+ * - test/built-ins/String/prototype/toLowerCase/supplementary_plane.js
+ * - test/built-ins/String/prototype/toUpperCase/S15.5.4.18_A2_T1.js
+ * - test/built-ins/String/prototype/toUpperCase/special_casing.js
+ * - test/built-ins/String/prototype/toUpperCase/supplementary_plane.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-1.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-2.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-3.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-4.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-5.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-6.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-7.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-8.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-9.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-10.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-11.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-12.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-13.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-3-14.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-1.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-2.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-3.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-4.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-5.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-6.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-8.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-10.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-11.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-12.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-13.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-14.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-16.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-18.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-19.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-20.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-21.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-22.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-24.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-27.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-28.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-29.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-30.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-32.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-34.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-35.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-36.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-37.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-38.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-39.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-40.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-41.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-42.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-43.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-44.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-45.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-46.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-47.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-48.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-49.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-50.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-51.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-52.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-53.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-54.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-55.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-56.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-57.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-58.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-59.js
+ * - test/built-ins/String/prototype/trim/15.5.4.20-4-60.js
+ * - test/built-ins/String/prototype/trim/u180e.js
+ * - test/built-ins/String/prototype/trimStart/this-value-whitespace.js
+ * - test/built-ins/String/prototype/trimStart/this-value-line-terminator.js
+ * - test/built-ins/String/prototype/trimEnd/this-value-whitespace.js
+ * - test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js
+ * - test/built-ins/String/prototype/repeat/repeat-string-n-times.js
+ * - test/built-ins/String/prototype/repeat/empty-string-returns-empty.js
+ * - test/built-ins/String/prototype/repeat/count-is-zero-returns-empty-string.js
+ * - test/built-ins/String/prototype/repeat/count-coerced-to-zero-returns-empty-string.js
+ * - test/built-ins/String/prototype/padStart/fill-string-empty.js
+ * - test/built-ins/String/prototype/padStart/normal-operation.js
+ * - test/built-ins/String/prototype/padStart/fill-string-omitted.js
+ * - test/built-ins/String/prototype/padStart/max-length-not-greater-than-string.js
+ * - test/built-ins/String/prototype/padEnd/fill-string-empty.js
+ * - test/built-ins/String/prototype/padEnd/normal-operation.js
+ * - test/built-ins/String/prototype/padEnd/fill-string-omitted.js
+ * - test/built-ins/String/prototype/padEnd/max-length-not-greater-than-string.js
+ * - test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T4.js
+ * - test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T7.js
+ * - test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T8.js
+ * - test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T1.js
+ * - test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T2.js
+ * - test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T3.js
+ * - test/built-ins/String/prototype/charAt/S9.4_A1.js
+ * - test/built-ins/String/prototype/charAt/S9.4_A2.js
+ * - test/built-ins/String/prototype/charAt/pos-rounding.js
+ * - test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T4.js
+ * - test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T7.js
+ * - test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T8.js
+ * - test/built-ins/String/prototype/charCodeAt/pos-rounding.js
+ * - test/built-ins/String/prototype/codePointAt/return-single-code-unit.js
+ * - test/built-ins/String/prototype/codePointAt/return-first-code-unit.js
+ * - test/built-ins/String/prototype/codePointAt/return-utf16-decode.js
+ * - test/built-ins/String/prototype/codePointAt/return-code-unit-coerced-position.js
+ * - test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-less-than-zero.js
+ * - test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-equal-or-more-than-size.js
+ * - test/built-ins/String/prototype/at/returns-code-unit.js
+ * - test/built-ins/String/prototype/at/returns-item.js
+ * - test/built-ins/String/prototype/at/returns-item-relative-index.js
+ * - test/built-ins/String/prototype/at/returns-undefined-for-out-of-range-index.js
+ * - test/built-ins/String/prototype/at/index-non-numeric-argument-tointeger.js
+ * - test/built-ins/String/prototype/concat/S15.5.4.6_A1_T4.js
+ * - test/built-ins/String/prototype/toString/string-primitive.js
+ * - test/built-ins/String/prototype/normalize/return-normalized-string.js
+ * - test/built-ins/String/prototype/normalize/return-normalized-string-using-default-parameter.js
+ * - test/built-ins/String/prototype/normalize/form-is-not-valid-throws.js
+ * - test/built-ins/String/prototype/localeCompare/15.5.4.9_CE.js
+ * - test/built-ins/String/fromCharCode/S15.5.3.2_A2.js
+ * - test/built-ins/String/fromCharCode/S15.5.3.2_A3_T1.js
+ * - test/built-ins/String/fromCharCode/S9.7_A1.js
+ * - test/built-ins/String/fromCharCode/S9.7_A2.1.js
+ * - test/built-ins/String/fromCharCode/S9.7_A2.2.js
+ * - test/built-ins/String/fromCharCode/S9.7_A3.2_T1.js
+ * - test/built-ins/String/fromCodePoint/arguments-is-empty.js
+ * - test/built-ins/String/fromCodePoint/return-string-value.js
+ * - test/built-ins/String/fromCodePoint/argument-is-not-integer.js
+ * - test/built-ins/String/fromCodePoint/number-is-out-of-range.js
+ *
+ * Copyright 2009 the Sputnik authors.  All rights reserved.
+ * Copyright (C) 2009 the Sputnik authors. All rights reserved.
+ * Copyright (c) 2012 Ecma International.  All rights reserved.
+ * Copyright 2012 Norbert Lindenberg. All rights reserved.
+ * Copyright 2012 Mozilla Corporation. All rights reserved.
+ * Copyright 2013 Microsoft Corporation. All rights reserved.
+ * Copyright (C) 2015 the V8 project authors. All rights reserved.
+ * Copyright (C) 2015 André Bargull. All rights reserved.
+ * Copyright (C) 2016 the V8 project authors. All rights reserved.
+ * Copyright (C) 2016 André Bargull. All rights reserved.
+ * Copyright (C) 2016 Jordan Harband. All rights reserved.
+ * Copyright (C) 2016 Mathias Bynens. All rights reserved.
+ * Copyright (c) 2017 Valerie Young.  All rights reserved.
+ * Copyright (C) 2017 Valerie Young. All rights reserved.
+ * Copyright (C) 2020 Rick Waldron. All rights reserved.
+ * Copyright (C) 2022 Richard Gibson. All rights reserved.
+ * Test262 portions are governed by the BSD license in LICENSE.test262.
+ */
+import { describe, expect, test } from "bun:test"
+import { Effect } from "effect"
+import { CodeMode } from "../src/index.js"
+
+type Argument = string | number | undefined
+type Outcome = "undefined" | "length" | "RangeError"
+type Assertion = {
+  label: string
+  input?: string
+  args?: ReadonlyArray<Argument>
+  expected?: string | number
+  outcome?: Outcome
+}
+type Vector = {
+  path: string
+  method: string
+  static?: boolean
+  assertions: ReadonlyArray<Assertion>
+}
+
+const value = async (code: string) => {
+  const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
+  if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
+  return result.value
+}
+
+const literal = (input: Argument) => {
+  if (input === undefined) return "undefined"
+  if (typeof input === "string") return JSON.stringify(input)
+  if (Number.isNaN(input)) return "NaN"
+  if (input === Infinity) return "Infinity"
+  if (input === -Infinity) return "-Infinity"
+  if (Object.is(input, -0)) return "-0"
+  return JSON.stringify(input)
+}
+
+const vectors: Array<Vector> = []
+const add = (path: string, method: string, assertions: ReadonlyArray<Assertion>, staticMethod = false) => {
+  vectors.push({ path, method, assertions, static: staticMethod })
+}
+const assertion = (label: string, input: string, expected: string | number, args: ReadonlyArray<Argument> = []) => ({
+  label,
+  input,
+  args,
+  expected,
+})
+
+add("test/built-ins/String/prototype/toLowerCase/S15.5.4.16_A2_T1.js", "toLowerCase", [
+  assertion("#1 direct value", "Hello, WoRlD!", "hello, world!"),
+  assertion("#2 String value", "Hello, WoRlD!", "hello, world!"),
+])
+add("test/built-ins/String/prototype/toLowerCase/special_casing.js", "toLowerCase", [
+  assertion(
+    "103 SpecialCasing mappings",
+    "\u00DF\u0130\uFB00\uFB01\uFB02\uFB03\uFB04\uFB05\uFB06\u0587\uFB13\uFB14\uFB15\uFB16\uFB17\u0149\u0390\u03B0\u01F0\u1E96\u1E97\u1E98\u1E99\u1E9A\u1F50\u1F52\u1F54\u1F56\u1FB6\u1FC6\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2\u1FE3\u1FE4\u1FE6\u1FE7\u1FF6\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F88\u1F89\u1F8A\u1F8B\u1F8C\u1F8D\u1F8E\u1F8F\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1F98\u1F99\u1F9A\u1F9B\u1F9C\u1F9D\u1F9E\u1F9F\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FA8\u1FA9\u1FAA\u1FAB\u1FAC\u1FAD\u1FAE\u1FAF\u1FB3\u1FBC\u1FC3\u1FCC\u1FF3\u1FFC\u1FB2\u1FB4\u1FC2\u1FC4\u1FF2\u1FF4\u1FB7\u1FC7\u1FF7",
+    "\u00DF\u0069\u0307\uFB00\uFB01\uFB02\uFB03\uFB04\uFB05\uFB06\u0587\uFB13\uFB14\uFB15\uFB16\uFB17\u0149\u0390\u03B0\u01F0\u1E96\u1E97\u1E98\u1E99\u1E9A\u1F50\u1F52\u1F54\u1F56\u1FB6\u1FC6\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2\u1FE3\u1FE4\u1FE6\u1FE7\u1FF6\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FB3\u1FB3\u1FC3\u1FC3\u1FF3\u1FF3\u1FB2\u1FB4\u1FC2\u1FC4\u1FF2\u1FF4\u1FB7\u1FC7\u1FF7",
+  ),
+])
+add("test/built-ins/String/prototype/toLowerCase/special_casing_conditional.js", "toLowerCase", [
+  assertion("single sigma", "\u03A3", "\u03C3"),
+  assertion("preceded by cased", "A\u03A3", "a\u03C2"),
+  assertion("preceded by supplementary cased", "\uD835\uDCA2\u03A3", "\uD835\uDCA2\u03C2"),
+  assertion("preceded by full stop", "A.\u03A3", "a.\u03C2"),
+  assertion("preceded by soft hyphen", "A\u00AD\u03A3", "a\u00AD\u03C2"),
+  assertion("preceded by combining mark", "A\uD834\uDE42\u03A3", "a\uD834\uDE42\u03C2"),
+  assertion("preceded by uncased combining mark", "\u0345\u03A3", "\u0345\u03C3"),
+  assertion("preceded by cased and combining mark", "\u0391\u0345\u03A3", "\u03B1\u0345\u03C2"),
+  assertion("followed by cased", "A\u03A3B", "a\u03C3b"),
+  assertion("followed by supplementary cased", "A\u03A3\uD835\uDCA2", "a\u03C3\uD835\uDCA2"),
+  assertion("followed by full stop and cased", "A\u03A3.b", "a\u03C3.b"),
+  assertion("followed by soft hyphen and cased", "A\u03A3\u00ADB", "a\u03C3\u00ADb"),
+  assertion("followed by combining mark and cased", "A\u03A3\uD834\uDE42B", "a\u03C3\uD834\uDE42b"),
+  assertion("followed by uncased combining mark", "A\u03A3\u0345", "a\u03C2\u0345"),
+  assertion("followed by combining mark and cased Greek", "A\u03A3\u0345\u0391", "a\u03C3\u0345\u03B1"),
+])
+add("test/built-ins/String/prototype/toLowerCase/Final_Sigma_U180E.js", "toLowerCase", [
+  assertion("preceded by U+180E", "A\u180E\u03A3", "a\u180E\u03C2"),
+  assertion("preceded by U+180E and followed by cased", "A\u180E\u03A3B", "a\u180E\u03C3b"),
+  assertion("followed by U+180E", "A\u03A3\u180E", "a\u03C2\u180E"),
+  assertion("followed by U+180E and cased", "A\u03A3\u180EB", "a\u03C3\u180Eb"),
+  assertion("surrounded by U+180E", "A\u180E\u03A3\u180E", "a\u180E\u03C2\u180E"),
+  assertion("surrounded by U+180E and followed by cased", "A\u180E\u03A3\u180EB", "a\u180E\u03C3\u180Eb"),
+])
+add("test/built-ins/String/prototype/toLowerCase/supplementary_plane.js", "toLowerCase", [
+  assertion(
+    "40 Deseret mappings",
+    "\uD801\uDC00\uD801\uDC01\uD801\uDC02\uD801\uDC03\uD801\uDC04\uD801\uDC05\uD801\uDC06\uD801\uDC07\uD801\uDC08\uD801\uDC09\uD801\uDC0A\uD801\uDC0B\uD801\uDC0C\uD801\uDC0D\uD801\uDC0E\uD801\uDC0F\uD801\uDC10\uD801\uDC11\uD801\uDC12\uD801\uDC13\uD801\uDC14\uD801\uDC15\uD801\uDC16\uD801\uDC17\uD801\uDC18\uD801\uDC19\uD801\uDC1A\uD801\uDC1B\uD801\uDC1C\uD801\uDC1D\uD801\uDC1E\uD801\uDC1F\uD801\uDC20\uD801\uDC21\uD801\uDC22\uD801\uDC23\uD801\uDC24\uD801\uDC25\uD801\uDC26\uD801\uDC27",
+    "\uD801\uDC28\uD801\uDC29\uD801\uDC2A\uD801\uDC2B\uD801\uDC2C\uD801\uDC2D\uD801\uDC2E\uD801\uDC2F\uD801\uDC30\uD801\uDC31\uD801\uDC32\uD801\uDC33\uD801\uDC34\uD801\uDC35\uD801\uDC36\uD801\uDC37\uD801\uDC38\uD801\uDC39\uD801\uDC3A\uD801\uDC3B\uD801\uDC3C\uD801\uDC3D\uD801\uDC3E\uD801\uDC3F\uD801\uDC40\uD801\uDC41\uD801\uDC42\uD801\uDC43\uD801\uDC44\uD801\uDC45\uD801\uDC46\uD801\uDC47\uD801\uDC48\uD801\uDC49\uD801\uDC4A\uD801\uDC4B\uD801\uDC4C\uD801\uDC4D\uD801\uDC4E\uD801\uDC4F",
+  ),
+])
+add("test/built-ins/String/prototype/toUpperCase/S15.5.4.18_A2_T1.js", "toUpperCase", [
+  assertion("#1 direct value", "Hello, WoRlD!", "HELLO, WORLD!"),
+  assertion("#2 String value", "Hello, WoRlD!", "HELLO, WORLD!"),
+])
+add("test/built-ins/String/prototype/toUpperCase/special_casing.js", "toUpperCase", [
+  assertion(
+    "103 SpecialCasing mappings",
+    "\u00DF\u0130\uFB00\uFB01\uFB02\uFB03\uFB04\uFB05\uFB06\u0587\uFB13\uFB14\uFB15\uFB16\uFB17\u0149\u0390\u03B0\u01F0\u1E96\u1E97\u1E98\u1E99\u1E9A\u1F50\u1F52\u1F54\u1F56\u1FB6\u1FC6\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2\u1FE3\u1FE4\u1FE6\u1FE7\u1FF6\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F88\u1F89\u1F8A\u1F8B\u1F8C\u1F8D\u1F8E\u1F8F\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1F98\u1F99\u1F9A\u1F9B\u1F9C\u1F9D\u1F9E\u1F9F\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FA8\u1FA9\u1FAA\u1FAB\u1FAC\u1FAD\u1FAE\u1FAF\u1FB3\u1FBC\u1FC3\u1FCC\u1FF3\u1FFC\u1FB2\u1FB4\u1FC2\u1FC4\u1FF2\u1FF4\u1FB7\u1FC7\u1FF7",
+    "\u0053\u0053\u0130\u0046\u0046\u0046\u0049\u0046\u004C\u0046\u0046\u0049\u0046\u0046\u004C\u0053\u0054\u0053\u0054\u0535\u0552\u0544\u0546\u0544\u0535\u0544\u053B\u054E\u0546\u0544\u053D\u02BC\u004E\u0399\u0308\u0301\u03A5\u0308\u0301\u004A\u030C\u0048\u0331\u0054\u0308\u0057\u030A\u0059\u030A\u0041\u02BE\u03A5\u0313\u03A5\u0313\u0300\u03A5\u0313\u0301\u03A5\u0313\u0342\u0391\u0342\u0397\u0342\u0399\u0308\u0300\u0399\u0308\u0301\u0399\u0342\u0399\u0308\u0342\u03A5\u0308\u0300\u03A5\u0308\u0301\u03A1\u0313\u03A5\u0342\u03A5\u0308\u0342\u03A9\u0342\u1F08\u0399\u1F09\u0399\u1F0A\u0399\u1F0B\u0399\u1F0C\u0399\u1F0D\u0399\u1F0E\u0399\u1F0F\u0399\u1F08\u0399\u1F09\u0399\u1F0A\u0399\u1F0B\u0399\u1F0C\u0399\u1F0D\u0399\u1F0E\u0399\u1F0F\u0399\u1F28\u0399\u1F29\u0399\u1F2A\u0399\u1F2B\u0399\u1F2C\u0399\u1F2D\u0399\u1F2E\u0399\u1F2F\u0399\u1F28\u0399\u1F29\u0399\u1F2A\u0399\u1F2B\u0399\u1F2C\u0399\u1F2D\u0399\u1F2E\u0399\u1F2F\u0399\u1F68\u0399\u1F69\u0399\u1F6A\u0399\u1F6B\u0399\u1F6C\u0399\u1F6D\u0399\u1F6E\u0399\u1F6F\u0399\u1F68\u0399\u1F69\u0399\u1F6A\u0399\u1F6B\u0399\u1F6C\u0399\u1F6D\u0399\u1F6E\u0399\u1F6F\u0399\u0391\u0399\u0391\u0399\u0397\u0399\u0397\u0399\u03A9\u0399\u03A9\u0399\u1FBA\u0399\u0386\u0399\u1FCA\u0399\u0389\u0399\u1FFA\u0399\u038F\u0399\u0391\u0342\u0399\u0397\u0342\u0399\u03A9\u0342\u0399",
+  ),
+])
+add("test/built-ins/String/prototype/toUpperCase/supplementary_plane.js", "toUpperCase", [
+  assertion(
+    "40 Deseret mappings",
+    "\uD801\uDC28\uD801\uDC29\uD801\uDC2A\uD801\uDC2B\uD801\uDC2C\uD801\uDC2D\uD801\uDC2E\uD801\uDC2F\uD801\uDC30\uD801\uDC31\uD801\uDC32\uD801\uDC33\uD801\uDC34\uD801\uDC35\uD801\uDC36\uD801\uDC37\uD801\uDC38\uD801\uDC39\uD801\uDC3A\uD801\uDC3B\uD801\uDC3C\uD801\uDC3D\uD801\uDC3E\uD801\uDC3F\uD801\uDC40\uD801\uDC41\uD801\uDC42\uD801\uDC43\uD801\uDC44\uD801\uDC45\uD801\uDC46\uD801\uDC47\uD801\uDC48\uD801\uDC49\uD801\uDC4A\uD801\uDC4B\uD801\uDC4C\uD801\uDC4D\uD801\uDC4E\uD801\uDC4F",
+    "\uD801\uDC00\uD801\uDC01\uD801\uDC02\uD801\uDC03\uD801\uDC04\uD801\uDC05\uD801\uDC06\uD801\uDC07\uD801\uDC08\uD801\uDC09\uD801\uDC0A\uD801\uDC0B\uD801\uDC0C\uD801\uDC0D\uD801\uDC0E\uD801\uDC0F\uD801\uDC10\uD801\uDC11\uD801\uDC12\uD801\uDC13\uD801\uDC14\uD801\uDC15\uD801\uDC16\uD801\uDC17\uD801\uDC18\uD801\uDC19\uD801\uDC1A\uD801\uDC1B\uD801\uDC1C\uD801\uDC1D\uD801\uDC1E\uD801\uDC1F\uD801\uDC20\uD801\uDC21\uD801\uDC22\uD801\uDC23\uD801\uDC24\uD801\uDC25\uD801\uDC26\uD801\uDC27",
+  ),
+])
+
+const whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
+const lineTerminators = "\u000A\u000D\u2028\u2029"
+const trim = (file: string, input: string, expected: string) =>
+  add(`test/built-ins/String/prototype/trim/${file}`, "trim", [assertion("upstream assertion", input, expected)])
+
+trim("15.5.4.20-3-1.js", lineTerminators, "")
+trim("15.5.4.20-3-2.js", whitespace, "")
+trim("15.5.4.20-3-3.js", whitespace + lineTerminators, "")
+trim("15.5.4.20-3-4.js", whitespace + lineTerminators + "abc", "abc")
+trim("15.5.4.20-3-5.js", "abc" + whitespace + lineTerminators, "abc")
+trim("15.5.4.20-3-6.js", whitespace + lineTerminators + "abc" + whitespace + lineTerminators, "abc")
+trim("15.5.4.20-3-7.js", "ab" + whitespace + lineTerminators + "cd", "ab" + whitespace + lineTerminators + "cd")
+trim("15.5.4.20-3-8.js", "\0\u0000", "\0\u0000")
+trim("15.5.4.20-3-9.js", "\0", "\0")
+trim("15.5.4.20-3-10.js", "\u0000", "\u0000")
+trim("15.5.4.20-3-11.js", "\0\u0000abc", "\0\u0000abc")
+trim("15.5.4.20-3-12.js", "abc\0\u0000", "abc\0\u0000")
+trim("15.5.4.20-3-13.js", "\0\u0000abc\0\u0000", "\0\u0000abc\0\u0000")
+trim("15.5.4.20-3-14.js", "a\0\u0000bc", "a\0\u0000bc")
+trim("15.5.4.20-4-1.js", "\u0009a bc \u0009", "a bc")
+trim("15.5.4.20-4-2.js", " \u0009abc \u0009", "abc")
+trim("15.5.4.20-4-3.js", "\u0009abc", "abc")
+trim("15.5.4.20-4-4.js", "\u000Babc", "abc")
+trim("15.5.4.20-4-5.js", "\u000Cabc", "abc")
+trim("15.5.4.20-4-6.js", "\u0020abc", "abc")
+trim("15.5.4.20-4-8.js", "\u00A0abc", "abc")
+trim("15.5.4.20-4-10.js", "\uFEFFabc", "abc")
+trim("15.5.4.20-4-11.js", "abc\u0009", "abc")
+trim("15.5.4.20-4-12.js", "abc\u000B", "abc")
+trim("15.5.4.20-4-13.js", "abc\u000C", "abc")
+trim("15.5.4.20-4-14.js", "abc\u0020", "abc")
+trim("15.5.4.20-4-16.js", "abc\u00A0", "abc")
+trim("15.5.4.20-4-18.js", "abc\uFEFF", "abc")
+trim("15.5.4.20-4-19.js", "\u0009abc\u0009", "abc")
+trim("15.5.4.20-4-20.js", "\u000Babc\u000B", "abc")
+trim("15.5.4.20-4-21.js", "\u000Cabc\u000C", "abc")
+trim("15.5.4.20-4-22.js", "\u0020abc\u0020", "abc")
+trim("15.5.4.20-4-24.js", "\u00A0abc\u00A0", "abc")
+trim("15.5.4.20-4-27.js", "\u0009\u0009", "")
+trim("15.5.4.20-4-28.js", "\u000B\u000B", "")
+trim("15.5.4.20-4-29.js", "\u000C\u000C", "")
+trim("15.5.4.20-4-30.js", "\u0020\u0020", "")
+trim("15.5.4.20-4-32.js", "\u00A0\u00A0", "")
+trim("15.5.4.20-4-34.js", "\uFEFF\uFEFF", "")
+trim("15.5.4.20-4-35.js", "ab\u0009c", "ab\u0009c")
+trim("15.5.4.20-4-36.js", "ab\u000Bc", "ab\u000Bc")
+trim("15.5.4.20-4-37.js", "ab\u000Cc", "ab\u000Cc")
+trim("15.5.4.20-4-38.js", "ab\u0020c", "ab\u0020c")
+trim("15.5.4.20-4-39.js", "ab\u0085c", "ab\u0085c")
+trim("15.5.4.20-4-40.js", "ab\u00A0c", "ab\u00A0c")
+trim("15.5.4.20-4-41.js", "ab\u200Bc", "ab\u200Bc")
+trim("15.5.4.20-4-42.js", "ab\uFEFFc", "ab\uFEFFc")
+trim("15.5.4.20-4-43.js", "\u000Aabc", "abc")
+trim("15.5.4.20-4-44.js", "\u000Dabc", "abc")
+trim("15.5.4.20-4-45.js", "\u2028abc", "abc")
+trim("15.5.4.20-4-46.js", "\u2029abc", "abc")
+trim("15.5.4.20-4-47.js", "abc\u000A", "abc")
+trim("15.5.4.20-4-48.js", "abc\u000D", "abc")
+trim("15.5.4.20-4-49.js", "abc\u2028", "abc")
+trim("15.5.4.20-4-50.js", "abc\u2029", "abc")
+trim("15.5.4.20-4-51.js", "\u000Aabc\u000A", "abc")
+trim("15.5.4.20-4-52.js", "\u000Dabc\u000D", "abc")
+trim("15.5.4.20-4-53.js", "\u2028abc\u2028", "abc")
+trim("15.5.4.20-4-54.js", "\u2029abc\u2029", "abc")
+trim("15.5.4.20-4-55.js", "\u000A\u000A", "")
+trim("15.5.4.20-4-56.js", "\u000D\u000D", "")
+trim("15.5.4.20-4-57.js", "\u2028\u2028", "")
+trim("15.5.4.20-4-58.js", "\u2029\u2029", "")
+trim("15.5.4.20-4-59.js", "\u2029           abc", "abc")
+trim("15.5.4.20-4-60.js", "    ", "")
+add("test/built-ins/String/prototype/trim/u180e.js", "trim", [
+  assertion("trailing U+180E", "_\u180E", "_\u180E"),
+  assertion("only U+180E", "\u180E", "\u180E"),
+  assertion("leading U+180E", "\u180E_", "\u180E_"),
+])
+
+const directionalWhitespace = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF"
+add("test/built-ins/String/prototype/trimStart/this-value-whitespace.js", "trimStart", [
+  assertion("all whitespace", directionalWhitespace + "a" + directionalWhitespace + "b" + directionalWhitespace, "a" + directionalWhitespace + "b" + directionalWhitespace),
+])
+add("test/built-ins/String/prototype/trimStart/this-value-line-terminator.js", "trimStart", [
+  assertion("all line terminators", lineTerminators + "a" + lineTerminators + "b" + lineTerminators, "a" + lineTerminators + "b" + lineTerminators),
+])
+add("test/built-ins/String/prototype/trimEnd/this-value-whitespace.js", "trimEnd", [
+  assertion("all whitespace", directionalWhitespace + "a" + directionalWhitespace + "b" + directionalWhitespace, directionalWhitespace + "a" + directionalWhitespace + "b"),
+])
+add("test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js", "trimEnd", [
+  assertion("all line terminators", lineTerminators + "a" + lineTerminators + "b" + lineTerminators, lineTerminators + "a" + lineTerminators + "b"),
+])
+add("test/built-ins/String/prototype/repeat/repeat-string-n-times.js", "repeat", [
+  assertion("repeat once", "abc", "abc", [1]),
+  assertion("repeat three times", "abc", "abcabcabc", [3]),
+  { label: "repeat 10000 times length", input: ".", args: [10000], expected: 10000, outcome: "length" },
+])
+add("test/built-ins/String/prototype/repeat/empty-string-returns-empty.js", "repeat", [
+  assertion("count 1", "", "", [1]),
+  assertion("count 3", "", "", [3]),
+  assertion("maximum 32-bit count", "", "", [0xffffffff]),
+])
+add("test/built-ins/String/prototype/repeat/count-is-zero-returns-empty-string.js", "repeat", [
+  assertion("zero", "foo", "", [0]),
+])
+add("test/built-ins/String/prototype/repeat/count-coerced-to-zero-returns-empty-string.js", "repeat", [
+  assertion("fraction truncates to zero", "abc", "", [0.9]),
+])
+add("test/built-ins/String/prototype/padStart/fill-string-empty.js", "padStart", [assertion("empty fill", "abc", "abc", [5, ""])])
+add("test/built-ins/String/prototype/padStart/normal-operation.js", "padStart", [
+  assertion("truncated multi-character fill", "abc", "defdabc", [7, "def"]),
+  assertion("single-character fill", "abc", "**abc", [5, "*"]),
+  assertion("truncated surrogate pair", "abc", "\uD83D\uDCA9\uD83Dabc", [6, "\uD83D\uDCA9"]),
+])
+add("test/built-ins/String/prototype/padStart/fill-string-omitted.js", "padStart", [
+  assertion("omitted fill", "abc", "  abc", [5]),
+  assertion("undefined fill", "abc", "  abc", [5, undefined]),
+])
+add("test/built-ins/String/prototype/padStart/max-length-not-greater-than-string.js", "padStart", [
+  assertion("NaN", "abc", "abc", [NaN, "def"]),
+  assertion("negative infinity", "abc", "abc", [-Infinity, "def"]),
+  assertion("zero", "abc", "abc", [0, "def"]),
+  assertion("negative one", "abc", "abc", [-1, "def"]),
+  assertion("equal length", "abc", "abc", [3, "def"]),
+  assertion("fraction truncates", "abc", "abc", [3.9999, "def"]),
+])
+add("test/built-ins/String/prototype/padEnd/fill-string-empty.js", "padEnd", [assertion("empty fill", "abc", "abc", [5, ""])])
+add("test/built-ins/String/prototype/padEnd/normal-operation.js", "padEnd", [
+  assertion("truncated multi-character fill", "abc", "abcdefd", [7, "def"]),
+  assertion("single-character fill", "abc", "abc**", [5, "*"]),
+  assertion("truncated surrogate pair", "abc", "abc\uD83D\uDCA9\uD83D", [6, "\uD83D\uDCA9"]),
+])
+add("test/built-ins/String/prototype/padEnd/fill-string-omitted.js", "padEnd", [
+  assertion("omitted fill", "abc", "abc  ", [5]),
+  assertion("undefined fill", "abc", "abc  ", [5, undefined]),
+])
+add("test/built-ins/String/prototype/padEnd/max-length-not-greater-than-string.js", "padEnd", [
+  assertion("NaN", "abc", "abc", [NaN, "def"]),
+  assertion("negative infinity", "abc", "abc", [-Infinity, "def"]),
+  assertion("zero", "abc", "abc", [0, "def"]),
+  assertion("negative one", "abc", "abc", [-1, "def"]),
+  assertion("equal length", "abc", "abc", [3, "def"]),
+  assertion("fraction truncates", "abc", "abc", [3.9999, "def"]),
+])
+
+add("test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T4.js", "charAt", [assertion("omitted position", "lego", "l")])
+add("test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T7.js", "charAt", [assertion("undefined position", "lego", "l", [undefined])])
+add("test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T8.js", "charAt", [assertion("undefined position", "42", "4", [undefined])])
+add("test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T1.js", "charAt", ["A", "B", "C", "A", "B", "C"].map((expected, position) => assertion(`position ${position}`, "ABCABC", expected, [position])))
+add("test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T2.js", "charAt", [-2, -1].map((position) => assertion(`position ${position}`, "ABCABC", "", [position])))
+add("test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T3.js", "charAt", [6, 7].map((position) => assertion(`position ${position}`, "ABCABC", "", [position])))
+add("test/built-ins/String/prototype/charAt/S9.4_A1.js", "charAt", [assertion("NaN position", "abc", "a", [NaN])])
+add("test/built-ins/String/prototype/charAt/S9.4_A2.js", "charAt", [
+  assertion("positive zero", "abc", "a", [0]),
+  assertion("negative zero", "abc", "a", [-0]),
+])
+add("test/built-ins/String/prototype/charAt/pos-rounding.js", "charAt", [
+  assertion("-0.99999", "abc", "a", [-0.99999]),
+  assertion("-0.00001", "abc", "a", [-0.00001]),
+  assertion("0.00001", "abc", "a", [0.00001]),
+  assertion("0.99999", "abc", "a", [0.99999]),
+  assertion("1.00001", "abc", "b", [1.00001]),
+  assertion("1.99999", "abc", "b", [1.99999]),
+])
+
+add("test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T4.js", "charCodeAt", [assertion("omitted position", "smart", 0x73)])
+add("test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T7.js", "charCodeAt", [assertion("undefined position", "lego", 0x6c, [undefined])])
+add("test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T8.js", "charCodeAt", [assertion("undefined position", "42", 0x34, [undefined])])
+add("test/built-ins/String/prototype/charCodeAt/pos-rounding.js", "charCodeAt", [
+  assertion("-0.99999", "abc", 0x61, [-0.99999]),
+  assertion("-0.00001", "abc", 0x61, [-0.00001]),
+  assertion("0.00001", "abc", 0x61, [0.00001]),
+  assertion("0.99999", "abc", 0x61, [0.99999]),
+  assertion("1.00001", "abc", 0x62, [1.00001]),
+  assertion("1.99999", "abc", 0x62, [1.99999]),
+])
+
+add("test/built-ins/String/prototype/codePointAt/return-single-code-unit.js", "codePointAt", [
+  assertion("a", "abc", 97, [0]), assertion("b", "abc", 98, [1]), assertion("c", "abc", 99, [2]),
+  assertion("ordinary BMP", "\uAAAA\uBBBB", 0xaaaa, [0]), assertion("before high-surrogate range", "\uD7FF\uAAAA", 0xd7ff, [0]),
+  assertion("low surrogate", "\uDC00\uAAAA", 0xdc00, [0]), assertion("trailing D800", "123\uD800", 0xd800, [3]),
+  assertion("trailing DAAA", "123\uDAAA", 0xdaaa, [3]), assertion("trailing DBFF", "123\uDBFF", 0xdbff, [3]),
+])
+add("test/built-ins/String/prototype/codePointAt/return-first-code-unit.js", "codePointAt", [
+  assertion("D800 before DBFF", "\uD800\uDBFF", 0xd800, [0]), assertion("D800 before E000", "\uD800\uE000", 0xd800, [0]),
+  assertion("DAAA before DBFF", "\uDAAA\uDBFF", 0xdaaa, [0]), assertion("DAAA before E000", "\uDAAA\uE000", 0xdaaa, [0]),
+  assertion("DBFF before DBFF", "\uDBFF\uDBFF", 0xdbff, [0]), assertion("DBFF before E000", "\uDBFF\uE000", 0xdbff, [0]),
+  assertion("D800 before NUL", "\uD800\u0000", 0xd800, [0]), assertion("D800 before FFFF", "\uD800\uFFFF", 0xd800, [0]),
+  assertion("DAAA before NUL", "\uDAAA\u0000", 0xdaaa, [0]), assertion("DAAA before FFFF", "\uDAAA\uFFFF", 0xdaaa, [0]),
+  assertion("DBFF before FFFF", "\uDBFF\uFFFF", 0xdbff, [0]),
+])
+add("test/built-ins/String/prototype/codePointAt/return-utf16-decode.js", "codePointAt", [
+  assertion("U+10000", "\uD800\uDC00", 65536, [0]), assertion("U+101D0", "\uD800\uDDD0", 66000, [0]),
+  assertion("U+103FF", "\uD800\uDFFF", 66559, [0]), assertion("U+BA800", "\uDAAA\uDC00", 763904, [0]),
+  assertion("U+BA9D0", "\uDAAA\uDDD0", 764368, [0]), assertion("U+BABFF", "\uDAAA\uDFFF", 764927, [0]),
+  assertion("U+10FC00", "\uDBFF\uDC00", 1113088, [0]), assertion("U+10FDD0", "\uDBFF\uDDD0", 1113552, [0]),
+  assertion("U+10FFFF", "\uDBFF\uDFFF", 1114111, [0]),
+])
+add("test/built-ins/String/prototype/codePointAt/return-code-unit-coerced-position.js", "codePointAt", [
+  assertion("NaN", "\uD800\uDC00", 65536, [NaN]), assertion("undefined", "\uD800\uDC00", 65536, [undefined]),
+])
+add("test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-less-than-zero.js", "codePointAt", [
+  { label: "negative one", input: "abc", args: [-1], outcome: "undefined" },
+  { label: "negative infinity", input: "abc", args: [-Infinity], outcome: "undefined" },
+])
+add("test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-equal-or-more-than-size.js", "codePointAt", [
+  { label: "equal to size", input: "abc", args: [3], outcome: "undefined" },
+  { label: "greater than size", input: "abc", args: [4], outcome: "undefined" },
+  { label: "positive infinity", input: "abc", args: [Infinity], outcome: "undefined" },
+])
+
+add("test/built-ins/String/prototype/at/returns-code-unit.js", "at", [
+  assertion("position 0", "12\uD80034", "1", [0]), assertion("position 1", "12\uD80034", "2", [1]),
+  assertion("unpaired surrogate", "12\uD80034", "\uD800", [2]), assertion("position 3", "12\uD80034", "3", [3]),
+  assertion("position 4", "12\uD80034", "4", [4]),
+])
+add("test/built-ins/String/prototype/at/returns-item.js", "at", ["1", "2", "3", "4", "5"].map((expected, position) => assertion(`position ${position}`, "12345", expected, [position])))
+add("test/built-ins/String/prototype/at/returns-item-relative-index.js", "at", [
+  assertion("zero", "12345", "1", [0]), assertion("negative one", "12345", "5", [-1]),
+  assertion("negative three", "12345", "3", [-3]), assertion("negative four", "12345", "2", [-4]),
+])
+add("test/built-ins/String/prototype/at/returns-undefined-for-out-of-range-index.js", "at", [-2, 0, 1].map((position) => ({ label: `position ${position}`, input: "", args: [position], outcome: "undefined" })))
+add("test/built-ins/String/prototype/at/index-non-numeric-argument-tointeger.js", "at", [assertion("undefined", "01", "0", [undefined])])
+
+add("test/built-ins/String/prototype/concat/S15.5.4.6_A1_T4.js", "concat", [assertion("no arguments", "lego", "lego")])
+add("test/built-ins/String/prototype/toString/string-primitive.js", "toString", [
+  assertion("empty string", "", ""), assertion("non-empty string", "str", "str"),
+])
+
+add("test/built-ins/String/prototype/normalize/return-normalized-string.js", "normalize", [
+  assertion("NFC short", "\u1E9B\u0323", "\u1E9B\u0323", ["NFC"]),
+  assertion("NFD short", "\u1E9B\u0323", "\u017F\u0323\u0307", ["NFD"]),
+  assertion("NFKC short", "\u1E9B\u0323", "\u1E69", ["NFKC"]),
+  assertion("NFKD short", "\u1E9B\u0323", "\u0073\u0323\u0307", ["NFKD"]),
+  assertion("NFC long", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFC"]),
+  assertion("NFD long", "\u00C5\u2ADC\u0958\u2126\u0344", "A\u030A\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFD"]),
+  assertion("NFKC long", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFKC"]),
+  assertion("NFKD long", "\u00C5\u2ADC\u0958\u2126\u0344", "A\u030A\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFKD"]),
+])
+add("test/built-ins/String/prototype/normalize/return-normalized-string-using-default-parameter.js", "normalize", [
+  assertion("omitted", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301"),
+  assertion("undefined", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", [undefined]),
+])
+add("test/built-ins/String/prototype/normalize/form-is-not-valid-throws.js", "normalize", [
+  { label: "bar", input: "foo", args: ["bar"], outcome: "RangeError" },
+  { label: "NFC1", input: "foo", args: ["NFC1"], outcome: "RangeError" },
+])
+
+add("test/built-ins/String/prototype/localeCompare/15.5.4.9_CE.js", "localeCompare", [
+  assertion("D70", "o\u0308", 0, ["ö"]), assertion("reordered diaeresis", "ä\u0323", 0, ["a\u0323\u0308"]),
+  assertion("reordered marks", "a\u0308\u0323", 0, ["a\u0323\u0308"]), assertion("precomposed dot below", "ạ\u0308", 0, ["a\u0323\u0308"]),
+  assertion("breve after diaeresis", "ä\u0306", 0, ["a\u0308\u0306"]), assertion("diaeresis after breve", "ă\u0308", 0, ["a\u0306\u0308"]),
+  assertion("Hangul", "\u1111\u1171\u11B6", 0, ["퓛"]), assertion("angstrom compatibility", "Å", 0, ["Å"]),
+  assertion("angstrom decomposed", "Å", 0, ["A\u030A"]), assertion("reordered horn and dot", "x\u031B\u0323", 0, ["x\u0323\u031B"]),
+  assertion("Vietnamese precomposed 1", "ự", 0, ["ụ\u031B"]), assertion("Vietnamese decomposed", "ự", 0, ["u\u031B\u0323"]),
+  assertion("Vietnamese precomposed 2", "ự", 0, ["ư\u0323"]), assertion("Vietnamese reordered", "ự", 0, ["u\u0323\u031B"]),
+  assertion("cedilla", "Ç", 0, ["C\u0327"]), assertion("q reordered", "q\u0307\u0323", 0, ["q\u0323\u0307"]),
+  assertion("Hangul syllable", "가", 0, ["\u1100\u1161"]), assertion("ohm", "Ω", 0, ["Ω"]),
+  assertion("angstrom", "Å", 0, ["A\u030A"]), assertion("circumflex", "ô", 0, ["o\u0302"]),
+  assertion("s with marks", "ṩ", 0, ["s\u0323\u0307"]), assertion("d composed plus dot", "ḋ\u0323", 0, ["d\u0323\u0307"]),
+  assertion("d two precompositions", "ḋ\u0323", 0, ["ḍ\u0307"]),
+])
+
+add("test/built-ins/String/fromCharCode/S15.5.3.2_A2.js", "fromCharCode", [{ label: "no arguments", expected: "" }], true)
+add("test/built-ins/String/fromCharCode/S15.5.3.2_A3_T1.js", "fromCharCode", [{ label: "ABBA", args: [65, 66, 66, 65], expected: "ABBA" }], true)
+add("test/built-ins/String/fromCharCode/S9.7_A1.js", "fromCharCode", [
+  { label: "NaN", args: [NaN], expected: 0 }, { label: "zero", args: [0], expected: 0 }, { label: "negative zero", args: [-0], expected: 0 },
+  { label: "positive infinity", args: [Infinity], expected: 0 }, { label: "negative infinity", args: [-Infinity], expected: 0 },
+], true)
+add("test/built-ins/String/fromCharCode/S9.7_A2.1.js", "fromCharCode", [
+  [0, 0], [1, 1], [-1, 65535], [65535, 65535], [65534, 65534], [65536, 0], [4294967295, 65535], [4294967294, 65534], [4294967296, 0],
+].map(([input, expected]) => ({ label: String(input), args: [input!], expected })), true)
+add("test/built-ins/String/fromCharCode/S9.7_A2.2.js", "fromCharCode", [
+  [-32767, 32769], [-32768, 32768], [-32769, 32767], [-65535, 1], [-65536, 0], [-65537, 65535], [65535, 65535], [65536, 0], [65537, 1], [131071, 65535], [131072, 0], [131073, 1],
+].map(([input, expected]) => ({ label: String(input), args: [input!], expected })), true)
+add("test/built-ins/String/fromCharCode/S9.7_A3.2_T1.js", "fromCharCode", [
+  { label: "positive fraction", args: [1.2345], expected: 1 }, { label: "negative fraction", args: [-5.4321], expected: 65531 },
+], true)
+
+add("test/built-ins/String/fromCodePoint/arguments-is-empty.js", "fromCodePoint", [{ label: "no arguments", expected: "" }], true)
+add("test/built-ins/String/fromCodePoint/return-string-value.js", "fromCodePoint", [
+  { label: "NUL", args: [0], expected: "\x00" }, { label: "asterisk", args: [42], expected: "*" },
+  { label: "AZ", args: [65, 90], expected: "AZ" }, { label: "Cyrillic", args: [0x404], expected: "\u0404" },
+  { label: "hex supplementary", args: [0x2f804], expected: "\uD87E\uDC04" }, { label: "decimal supplementary", args: [194564], expected: "\uD87E\uDC04" },
+  { label: "mixed supplementary", args: [0x1d306, 0x61, 0x1d307], expected: "\uD834\uDF06a\uD834\uDF07" },
+  { label: "maximum code point", args: [1114111], expected: "\uDBFF\uDFFF" },
+], true)
+add("test/built-ins/String/fromCodePoint/argument-is-not-integer.js", "fromCodePoint", [
+  { label: "fraction", args: [3.14], outcome: "RangeError" }, { label: "fraction after valid", args: [42, 3.14], outcome: "RangeError" },
+], true)
+add("test/built-ins/String/fromCodePoint/number-is-out-of-range.js", "fromCodePoint", [
+  { label: "negative one", args: [-1], outcome: "RangeError" }, { label: "negative after valid", args: [1, -1], outcome: "RangeError" },
+  { label: "above maximum", args: [1114112], outcome: "RangeError" }, { label: "infinity", args: [Infinity], outcome: "RangeError" },
+], true)
+
+describe("Test262-adapted core String behavior", () => {
+  for (const vector of vectors) {
+    test(vector.path, async () => {
+      const results = vector.assertions.map((item) => {
+        const args = (item.args ?? []).map(literal).join(", ")
+        const expression = vector.static
+          ? `String.${vector.method}(${args})`
+          : `${JSON.stringify(item.input)}.${vector.method}(${args})`
+        const observed = vector.static && vector.method === "fromCharCode" && typeof item.expected === "number"
+          ? `${expression}.charCodeAt(0)`
+          : expression
+        const checked = item.outcome === "undefined"
+          ? `${observed} === undefined`
+          : item.outcome === "length"
+            ? `${observed}.length`
+            : item.outcome === "RangeError"
+              ? `(() => { try { ${observed}; return false } catch (error) { return error instanceof RangeError } })()`
+              : observed
+        return `{ label: ${JSON.stringify(item.label)}, value: ${checked} }`
+      })
+      const expected = vector.assertions.map((item) => ({
+        label: item.label,
+        value: item.outcome === undefined || item.outcome === "length" ? item.expected! : true,
+      }))
+      expect(await value(`return [${results.join(",")}]`)).toEqual(expected)
+    })
+  }
+})

+ 625 - 0
packages/codemode/test/string-regexp-test262.test.ts

@@ -0,0 +1,625 @@
+/*
+ * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
+ * - test/built-ins/String/prototype/split/separator-regexp.js
+ * - test/built-ins/String/prototype/split/arguments-are-regexp-s-and-3-and-instance-is-string-a-b-c-de-f.js
+ * - test/built-ins/String/prototype/split/argument-is-regexp-s-and-instance-is-string-a-b-c-de-f.js
+ * - test/built-ins/String/prototype/split/argument-is-regexp-d-and-instance-is-string-dfe23iu-34-65.js
+ * - test/built-ins/String/prototype/split/argument-is-regexp-reg-exp-d-and-instance-is-string-dfe23iu-34-65.js
+ * - test/built-ins/String/prototype/split/argument-is-regexp-a-z-and-instance-is-string-abc.js
+ * - test/built-ins/String/prototype/split/argument-is-reg-exp-a-z-and-instance-is-string-abc.js
+ * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-undefined-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-0-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-1-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-2-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-3-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-4-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/argument-is-regexp-l-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/argument-is-new-reg-exp-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-0-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-1-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-2-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-3-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-4-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-undefined-and-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-2-instance-is-string-one-two-three-four-five.js
+ * - test/built-ins/String/prototype/split/separator-regexp-comma-instance-is-string-one-1-two-2-four-4.js
+ * - test/built-ins/String/prototype/split/argument-is-regexp-x-and-instance-is-string-a-b-c-de-f.js
+ * - test/built-ins/String/prototype/replace/regexp-capture-by-index.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A1_T17.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T1.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T2.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T3.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T4.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T5.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T6.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T7.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T8.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T9.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T10.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A3_T1.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A3_T2.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A3_T3.js
+ * - test/built-ins/String/prototype/replace/S15.5.4.11_A5_T1.js
+ * - test/built-ins/String/prototype/replaceAll/searchValue-replacer-RegExp-call.js
+ * - test/built-ins/String/prototype/replaceAll/searchValue-empty-string.js
+ * - test/built-ins/String/prototype/replaceAll/searchValue-empty-string-this-empty-string.js
+ * - test/built-ins/String/prototype/replaceAll/replaceValue-value-replaces-string.js
+ * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024.js
+ * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0024.js
+ * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0026.js
+ * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0060.js
+ * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0027.js
+ * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024N.js
+ * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024NN.js
+ * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x003C.js
+ * - test/built-ins/String/prototype/match/S15.5.4.10_A1_T14.js
+ * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T2.js
+ * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T3.js
+ * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T4.js
+ * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T5.js
+ * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T6.js
+ * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T7.js
+ * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T8.js
+ * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T12.js
+ * - test/built-ins/String/prototype/matchAll/regexp-prototype-matchAll-v-u-flag.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A1_T14.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T1.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T2.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T3.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T4.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T5.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T6.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T7.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A3_T1.js
+ * - test/built-ins/String/prototype/search/S15.5.4.12_A3_T2.js
+ *
+ * Copyright 2009 the Sputnik authors.  All rights reserved.
+ * Copyright (C) 2019 Leo Balter. All rights reserved.
+ * Copyright (C) 2020 Rick Waldron. All rights reserved.
+ * Copyright (C) 2023 Richard Gibson. All rights reserved.
+ * Copyright (C) 2024 Tan Meng. All rights reserved.
+ * Test262 portions are governed by the BSD license in LICENSE.test262.
+ */
+import { describe, expect, test } from "bun:test"
+import { Effect } from "effect"
+import { CodeMode } from "../src/index.js"
+
+type Vector = {
+  readonly path: string
+  readonly code: string
+  readonly expected: CodeMode.DataValue
+}
+
+const value = async (code: string) => {
+  const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
+  if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
+  return result.value
+}
+
+const run = (name: string, vectors: ReadonlyArray<Vector>) => {
+  describe(name, () => {
+    for (const vector of vectors) {
+      test(vector.path, async () => {
+        expect(await value(vector.code)).toEqual(vector.expected)
+      })
+    }
+  })
+}
+
+run("Test262-adapted regexp split behavior", [
+  {
+    path: "test/built-ins/String/prototype/split/separator-regexp.js",
+    code: `
+      return [
+        "x".split(/^/), "x".split(/$/), "x".split(/.?/), "x".split(/.*/), "x".split(/.+/),
+        "x".split(/.*?/), "x".split(/.{1}/), "x".split(/.{1,}/), "x".split(/.{1,2}/),
+        "x".split(/()/), "x".split(/./), "x".split(/(?:)/), "x".split(/(...)/),
+        "x".split(/(|)/), "x".split(/[]/), "x".split(/[^]/), "x".split(/[.-.]/),
+        "x".split(/\\0/), "x".split(/\\b/), "x".split(/\\B/), "x".split(/\\d/),
+        "x".split(/\\D/), "x".split(/\\n/), "x".split(/\\r/), "x".split(/\\s/),
+        "x".split(/\\S/), "x".split(/\\v/), "x".split(/\\w/), "x".split(/\\W/),
+      ]
+    `,
+    expected: [
+      ["x"], ["x"], ["", ""], ["", ""], ["", ""], ["x"], ["", ""], ["", ""], ["", ""],
+      ["x"], ["", ""], ["x"], ["x"], ["x"], ["x"], ["", ""], ["x"], ["x"], ["x"],
+      ["x"], ["x"], ["", ""], ["x"], ["x"], ["x"], ["", ""], ["x"], ["", ""], ["x"],
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-regexp-s-and-3-and-instance-is-string-a-b-c-de-f.js",
+    code: `return "a b c de f".split(/\\s/, 3)`,
+    expected: ["a", "b", "c"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/argument-is-regexp-s-and-instance-is-string-a-b-c-de-f.js",
+    code: `return "a b c de f".split(/\\s/)`,
+    expected: ["a", "b", "c", "de", "f"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/argument-is-regexp-d-and-instance-is-string-dfe23iu-34-65.js",
+    code: `return "dfe23iu 34 =+65--".split(/\\d+/)`,
+    expected: ["dfe", "iu ", " =+", "--"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/argument-is-regexp-reg-exp-d-and-instance-is-string-dfe23iu-34-65.js",
+    code: `return "dfe23iu 34 =+65--".split(new RegExp("\\\\d+"))`,
+    expected: ["dfe", "iu ", " =+", "--"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/argument-is-regexp-a-z-and-instance-is-string-abc.js",
+    code: `return "abc".split(/[a-z]/)`,
+    expected: ["", "", "", ""],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/argument-is-reg-exp-a-z-and-instance-is-string-abc.js",
+    code: `return "abc".split(new RegExp("[a-z]"))`,
+    expected: ["", "", "", ""],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-undefined-and-instance-is-string-hello.js",
+    code: `return "hello".split(/l/, undefined)`,
+    expected: ["he", "", "o"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-0-and-instance-is-string-hello.js",
+    code: `return "hello".split(/l/, 0)`,
+    expected: [],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-1-and-instance-is-string-hello.js",
+    code: `return "hello".split(/l/, 1)`,
+    expected: ["he"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-2-and-instance-is-string-hello.js",
+    code: `return "hello".split(/l/, 2)`,
+    expected: ["he", ""],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-3-and-instance-is-string-hello.js",
+    code: `return "hello".split(/l/, 3)`,
+    expected: ["he", "", "o"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-4-and-instance-is-string-hello.js",
+    code: `return "hello".split(/l/, 4)`,
+    expected: ["he", "", "o"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/argument-is-regexp-l-and-instance-is-string-hello.js",
+    code: `return "hello".split(/l/)`,
+    expected: ["he", "", "o"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/argument-is-new-reg-exp-and-instance-is-string-hello.js",
+    code: `return "hello".split(new RegExp())`,
+    expected: ["h", "e", "l", "l", "o"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-0-and-instance-is-string-hello.js",
+    code: `return "hello".split(new RegExp(), 0)`,
+    expected: [],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-1-and-instance-is-string-hello.js",
+    code: `return "hello".split(new RegExp(), 1)`,
+    expected: ["h"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-2-and-instance-is-string-hello.js",
+    code: `return "hello".split(new RegExp(), 2)`,
+    expected: ["h", "e"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-3-and-instance-is-string-hello.js",
+    code: `return "hello".split(new RegExp(), 3)`,
+    expected: ["h", "e", "l"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-4-and-instance-is-string-hello.js",
+    code: `return "hello".split(new RegExp(), 4)`,
+    expected: ["h", "e", "l", "l"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-undefined-and-instance-is-string-hello.js",
+    code: `return "hello".split(new RegExp(), undefined)`,
+    expected: ["h", "e", "l", "l", "o"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-2-instance-is-string-one-two-three-four-five.js",
+    code: `return "one two three four five".split(/ /, 2)`,
+    expected: ["one", "two"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/separator-regexp-comma-instance-is-string-one-1-two-2-four-4.js",
+    code: `return "one-1,two-2,four-4".split(/,/)`,
+    expected: ["one-1", "two-2", "four-4"],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/argument-is-regexp-x-and-instance-is-string-a-b-c-de-f.js",
+    code: `return "a b c de f".split(/X/)`,
+    expected: ["a b c de f"],
+  },
+])
+
+run("Test262-adapted replace behavior", [
+  {
+    path: "test/built-ins/String/prototype/replace/regexp-capture-by-index.js",
+    code: `
+      const str = "foo-x-bar"
+      const patterns = ["x", /x/, /(x)/, /(x)($^)?/, /((((((((((x))))))))))/]
+      const replacements = ["|$0|", "|$00|", "|$000|", "|$1|", "|$01|", "|$010|", "|$2|", "|$02|", "|$020|", "|$10|", "|$100|", "|$20|", "|$200|"]
+      return replacements.flatMap((replacement) => patterns.map((pattern) => str.replace(pattern, replacement)))
+    `,
+    expected: [
+      "foo-|$0|-bar", "foo-|$0|-bar", "foo-|$0|-bar", "foo-|$0|-bar", "foo-|$0|-bar",
+      "foo-|$00|-bar", "foo-|$00|-bar", "foo-|$00|-bar", "foo-|$00|-bar", "foo-|$00|-bar",
+      "foo-|$000|-bar", "foo-|$000|-bar", "foo-|$000|-bar", "foo-|$000|-bar", "foo-|$000|-bar",
+      "foo-|$1|-bar", "foo-|$1|-bar", "foo-|x|-bar", "foo-|x|-bar", "foo-|x|-bar",
+      "foo-|$01|-bar", "foo-|$01|-bar", "foo-|x|-bar", "foo-|x|-bar", "foo-|x|-bar",
+      "foo-|$010|-bar", "foo-|$010|-bar", "foo-|x0|-bar", "foo-|x0|-bar", "foo-|x0|-bar",
+      "foo-|$2|-bar", "foo-|$2|-bar", "foo-|$2|-bar", "foo-||-bar", "foo-|x|-bar",
+      "foo-|$02|-bar", "foo-|$02|-bar", "foo-|$02|-bar", "foo-||-bar", "foo-|x|-bar",
+      "foo-|$020|-bar", "foo-|$020|-bar", "foo-|$020|-bar", "foo-|0|-bar", "foo-|x0|-bar",
+      "foo-|$10|-bar", "foo-|$10|-bar", "foo-|x0|-bar", "foo-|x0|-bar", "foo-|x|-bar",
+      "foo-|$100|-bar", "foo-|$100|-bar", "foo-|x00|-bar", "foo-|x00|-bar", "foo-|x0|-bar",
+      "foo-|$20|-bar", "foo-|$20|-bar", "foo-|$20|-bar", "foo-|0|-bar", "foo-|x0|-bar",
+      "foo-|$200|-bar", "foo-|$200|-bar", "foo-|$200|-bar", "foo-|00|-bar", "foo-|x00|-bar",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A1_T17.js",
+    code: `return "asdf".replace(new RegExp(undefined, "g"), "1")`,
+    expected: "1a1s1d1f1",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T1.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/g, "sch")`,
+    expected: "She sells seaschells by the seaschore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T2.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/g, "$$sch")`,
+    expected: "She sells sea$schells by the sea$schore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T3.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/g, "$&sch")`,
+    expected: "She sells seashschells by the seashschore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T4.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/g, "$\`sch")`,
+    expected: "She sells seaShe sells seaschells by the seaShe sells seashells by the seaschore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T5.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/g, "$'sch")`,
+    expected: "She sells seaells by the seashore.schells by the seaore.schore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T6.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/, "sch")`,
+    expected: "She sells seaschells by the seashore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T7.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/, "$$sch")`,
+    expected: "She sells sea$schells by the seashore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T8.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/, "$&sch")`,
+    expected: "She sells seashschells by the seashore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T9.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/, "$\`sch")`,
+    expected: "She sells seaShe sells seaschells by the seashore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T10.js",
+    code: `return "She sells seashells by the seashore.".replace(/sh/, "$'sch")`,
+    expected: "She sells seaells by the seashore.schells by the seashore.",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A3_T1.js",
+    code: `return "uid=31".replace(/(uid=)(\\d+)/, "$1115")`,
+    expected: "uid=115",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A3_T2.js",
+    code: `return "uid=31".replace(/(uid=)(\\d+)/, "$1115")`,
+    expected: "uid=115",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A3_T3.js",
+    code: `return "uid=31".replace(/(uid=)(\\d+)/, "$11A15")`,
+    expected: "uid=1A15",
+  },
+  {
+    path: "test/built-ins/String/prototype/replace/S15.5.4.11_A5_T1.js",
+    code: `return "aaaaaaaaaa,aaaaaaaaaaaaaaa".replace(/^(a+)\\1*,\\1+$/, "$1")`,
+    expected: "aaaaa",
+  },
+])
+
+run("Test262-adapted replaceAll behavior", [
+  {
+    path: "test/built-ins/String/prototype/replaceAll/searchValue-replacer-RegExp-call.js",
+    code: `
+      return [
+        "abc abc abc".replaceAll(new RegExp("b", "g"), "z"),
+        "abc abc abc".replaceAll(new RegExp("b", "gy"), "z"),
+        "abc abc abc".replaceAll(new RegExp("b", "giy"), "z"),
+        "No Uppercase!".replaceAll(new RegExp("[A-Z]", "g"), ""),
+        "No Uppercase?".replaceAll(new RegExp("[A-Z]", "gy"), ""),
+        "NO UPPERCASE!".replaceAll(new RegExp("[A-Z]", "gy"), ""),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "$2-$1"),
+        "abcabcabcabc".replaceAll(new RegExp("(a(.))", "g"), "$1$2$3"),
+        "aabacadaeafagahaiajakalamano a azaya".replaceAll(new RegExp("(((((((((((((a(.).).).).).).).).))))))", "g"), "($10)-($12)-($1)"),
+        "abcba".replaceAll(new RegExp("b", "g"), "$'"),
+        "abcba".replaceAll(new RegExp("b", "g"), "$\`"),
+        "abcba".replaceAll(new RegExp("(?<named>b)", "g"), "($<named>)"),
+        "abcba".replaceAll(new RegExp("(?<named>b)", "g"), "($<named)"),
+        "abcba".replaceAll(new RegExp("(?<named>b)", "g"), "($<unnamed>)"),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$)"),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($)"),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$$$)"),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$$)"),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$&)"),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$1)"),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$\`)"),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$')"),
+        "abcabcabcabc".replaceAll(new RegExp("a(?<z>b)(ca)", "g"), "($$<z>)"),
+        "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($&)"),
+      ]
+    `,
+    expected: [
+      "azc azc azc", "abc abc abc", "abc abc abc", "o ppercase!", "o Uppercase?", " UPPERCASE!",
+      "ca-bbcca-bbc", "abb$3cabb$3cabb$3cabb$3c",
+      "(aabaca)-(aaba)-(aabacadaea)f(agahai)-(agah)-(agahaiajak)(alaman)-(alam)-(alamano a )azaya",
+      "acbacaa", "aacabca", "a(b)c(b)a", "a($<named)c($<named)a", "a()c()a", "($)bc($)bc",
+      "($)bc($)bc", "($$)bc($$)bc", "($$)bc($$)bc", "($&)bc($&)bc", "($1)bc($1)bc",
+      "($`)bc($`)bc", "($')bc($')bc", "($<z>)bc($<z>)bc", "(abca)bc(abca)bc",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/searchValue-empty-string.js",
+    code: `return ["aab c  \\nx".replaceAll("", "_"), "a".replaceAll("", "_")]`,
+    expected: ["_a_a_b_ _c_ _ _\n_x_", "_a_"],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/searchValue-empty-string-this-empty-string.js",
+    code: `return "".replaceAll("", "abc")`,
+    expected: "abc",
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/replaceValue-value-replaces-string.js",
+    code: `return ["aaab a a aac".replaceAll("aa", "z"), "aaab a a aac".replaceAll("aa", "a"), "aaab a a aac".replaceAll("a", "a"), "aaab a a aac".replaceAll("a", "z")]`,
+    expected: ["zab a a zc", "aab a a ac", "aaab a a aac", "zzzb z z zzc"],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024.js",
+    code: `
+      const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
+      return [str.replaceAll("ninguém", "$"), str.replaceAll("é", "$"), str.replaceAll("é", "$ -"), str.replaceAll("é", "$$$")]
+    `,
+    expected: [
+      "Ninguém é igual a $. Todo o ser humano é um estranho ímpar.",
+      "Ningu$m $ igual a ningu$m. Todo o ser humano $ um estranho ímpar.",
+      "Ningu$ -m $ - igual a ningu$ -m. Todo o ser humano $ - um estranho ímpar.",
+      "Ningu$$m $$ igual a ningu$$m. Todo o ser humano $$ um estranho ímpar.",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0024.js",
+    code: `
+      const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
+      return [str.replaceAll("ninguém", "$$"), str.replaceAll("é", "$$"), str.replaceAll("é", "$$ -"), str.replaceAll("é", "$$&"), str.replaceAll("é", "$$$"), str.replaceAll("é", "$$$$")]
+    `,
+    expected: [
+      "Ninguém é igual a $. Todo o ser humano é um estranho ímpar.",
+      "Ningu$m $ igual a ningu$m. Todo o ser humano $ um estranho ímpar.",
+      "Ningu$ -m $ - igual a ningu$ -m. Todo o ser humano $ - um estranho ímpar.",
+      "Ningu$&m $& igual a ningu$&m. Todo o ser humano $& um estranho ímpar.",
+      "Ningu$$m $$ igual a ningu$$m. Todo o ser humano $$ um estranho ímpar.",
+      "Ningu$$m $$ igual a ningu$$m. Todo o ser humano $$ um estranho ímpar.",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0026.js",
+    code: `
+      const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
+      return [str.replaceAll("ninguém", "$&"), str.replaceAll("ninguém", "($&)"), str.replaceAll("é", "($&)"), str.replaceAll("é", "($&) $&")]
+    `,
+    expected: [
+      "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar.",
+      "Ninguém é igual a (ninguém). Todo o ser humano é um estranho ímpar.",
+      "Ningu(é)m (é) igual a ningu(é)m. Todo o ser humano (é) um estranho ímpar.",
+      "Ningu(é) ém (é) é igual a ningu(é) ém. Todo o ser humano (é) é um estranho ímpar.",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0060.js",
+    code: `
+      const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
+      return [str.replaceAll("ninguém", "$\`"), str.replaceAll("Ninguém", "$\`"), str.replaceAll("ninguém", "($\`)"), str.replaceAll("é", "($\`)")]
+    `,
+    expected: [
+      "Ninguém é igual a Ninguém é igual a . Todo o ser humano é um estranho ímpar.",
+      " é igual a ninguém. Todo o ser humano é um estranho ímpar.",
+      "Ninguém é igual a (Ninguém é igual a ). Todo o ser humano é um estranho ímpar.",
+      "Ningu(Ningu)m (Ninguém ) igual a ningu(Ninguém é igual a ningu)m. Todo o ser humano (Ninguém é igual a ninguém. Todo o ser humano ) um estranho ímpar.",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0027.js",
+    code: `
+      const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
+      return [str.replaceAll("ninguém", "$'"), str.replaceAll(".", "--- $'"), str.replaceAll("é", "($')")]
+    `,
+    expected: [
+      "Ninguém é igual a . Todo o ser humano é um estranho ímpar.. Todo o ser humano é um estranho ímpar.",
+      "Ninguém é igual a ninguém---  Todo o ser humano é um estranho ímpar. Todo o ser humano é um estranho ímpar--- ",
+      "Ningu(m é igual a ninguém. Todo o ser humano é um estranho ímpar.)m ( igual a ninguém. Todo o ser humano é um estranho ímpar.) igual a ningu(m. Todo o ser humano é um estranho ímpar.)m. Todo o ser humano ( um estranho ímpar.) um estranho ímpar.",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024N.js",
+    code: `
+      const str = "ABC AAA ABC AAA"
+      return ["$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"].map((replacement) => str.replaceAll("ABC", replacement))
+    `,
+    expected: ["$1 AAA $1 AAA", "$2 AAA $2 AAA", "$3 AAA $3 AAA", "$4 AAA $4 AAA", "$5 AAA $5 AAA", "$6 AAA $6 AAA", "$7 AAA $7 AAA", "$8 AAA $8 AAA", "$9 AAA $9 AAA"],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024NN.js",
+    code: `
+      const str = "aaaaaaaaaaaaaaaa aaaaaaaa aaaaaaaaaaaaaaaa"
+      return [str.replaceAll("a", "$11"), str.replaceAll("a", "$29")]
+    `,
+    expected: [
+      "$11$11$11$11$11$11$11$11$11$11$11$11$11$11$11$11 $11$11$11$11$11$11$11$11 $11$11$11$11$11$11$11$11$11$11$11$11$11$11$11$11",
+      "$29$29$29$29$29$29$29$29$29$29$29$29$29$29$29$29 $29$29$29$29$29$29$29$29 $29$29$29$29$29$29$29$29$29$29$29$29$29$29$29$29",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x003C.js",
+    code: `return "aaaaaaaaaaaaaaaa aaaaaaaa aaaaaaaaaaaaaaaa".replaceAll("a", "$<")`,
+    expected: "$<$<$<$<$<$<$<$<$<$<$<$<$<$<$<$< $<$<$<$<$<$<$<$< $<$<$<$<$<$<$<$<$<$<$<$<$<$<$<$<",
+  },
+])
+
+run("Test262-adapted match behavior", [
+  {
+    path: "test/built-ins/String/prototype/match/S15.5.4.10_A1_T14.js",
+    code: `const match = "ABBABABAB77BBAA".match(new RegExp("77")); return [match[0], match.index]`,
+    expected: ["77", 9],
+  },
+  {
+    path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T2.js",
+    code: `return "343443444".match(/34/g)`,
+    expected: ["34", "34", "34"],
+  },
+  {
+    path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T3.js",
+    code: `return "123456abcde7890".match(/\\d{1}/g)`,
+    expected: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
+  },
+  {
+    path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T4.js",
+    code: `return "123456abcde7890".match(/\\d{2}/g)`,
+    expected: ["12", "34", "56", "78", "90"],
+  },
+  {
+    path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T5.js",
+    code: `return "123456abcde7890".match(/\\D{2}/g)`,
+    expected: ["ab", "cd"],
+  },
+  {
+    path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T6.js",
+    code: `const match = "Boston, Mass. 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/); return [match[0], match[1], match[2] === undefined, match.length, match.index]`,
+    expected: ["02134", "02134", true, 3, 14],
+  },
+  {
+    path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T7.js",
+    code: `return "Boston, Mass. 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/g)`,
+    expected: ["02134"],
+  },
+  {
+    path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T8.js",
+    code: `const match = "Boston, MA 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/); return [match[0], match[1], match[2] === undefined, match.length, match.index]`,
+    expected: ["02134", "02134", true, 3, 11],
+  },
+  {
+    path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T12.js",
+    code: `return "Boston, MA 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/g)`,
+    expected: ["02134"],
+  },
+])
+
+run("Test262-adapted matchAll behavior", [
+  {
+    path: "test/built-ins/String/prototype/matchAll/regexp-prototype-matchAll-v-u-flag.js",
+    code: `
+      const text = "𠮷a𠮷b𠮷"
+      const collect = (regex) => {
+        const matches = text.matchAll(regex)
+        return matches.map((match) => match[0]).concat(matches.map((match) => match.index))
+      }
+      const empty = text.matchAll(/(?:)/gu)
+      const complex = "a𠮷b􏿿c".matchAll(/\\P{ASCII}/gu)
+      return [
+        collect(/𠮷/g),
+        collect(/𠮷/gu),
+        collect(/\\p{Script=Han}/gu),
+        collect(/./gu),
+        empty.map((match) => match[0]).concat(empty.map((match) => match.index)).length,
+        complex.map((match) => match[0]),
+      ]
+    `,
+    expected: [
+      ["𠮷", "𠮷", "𠮷", 0, 3, 6],
+      ["𠮷", "𠮷", "𠮷", 0, 3, 6],
+      ["𠮷", "𠮷", "𠮷", 0, 3, 6],
+      ["𠮷", "a", "𠮷", "b", "𠮷", 0, 2, 3, 5, 6],
+      12,
+      ["𠮷", "􏿿"],
+    ],
+  },
+])
+
+run("Test262-adapted search behavior", [
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A1_T14.js",
+    code: `return "ABBABABAB77BBAA".search(new RegExp("77"))`,
+    expected: 9,
+  },
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T1.js",
+    code: `return "test string".search("string")`,
+    expected: 5,
+  },
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T2.js",
+    code: `return "test string".search("String")`,
+    expected: -1,
+  },
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T3.js",
+    code: `return "test string".search(/String/i)`,
+    expected: 5,
+  },
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T4.js",
+    code: `return "one two three four five".search(/Four/)`,
+    expected: -1,
+  },
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T5.js",
+    code: `return "one two three four five".search(/four/)`,
+    expected: 14,
+  },
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T6.js",
+    code: `return "test string".search("notexist")`,
+    expected: -1,
+  },
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T7.js",
+    code: `return "test string probe".search("string pro")`,
+    expected: 5,
+  },
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A3_T1.js",
+    code: `const text = "power of the power of the power of the great sword"; return [text.search(/the/), text.search(/the/g)]`,
+    expected: [9, 9],
+  },
+  {
+    path: "test/built-ins/String/prototype/search/S15.5.4.12_A3_T2.js",
+    code: `const text = "power of the power of the power of the great sword"; return [text.search(/of/), text.search(/of/g)]`,
+    expected: [6, 6],
+  },
+])

+ 794 - 0
packages/codemode/test/string-search-test262.test.ts

@@ -0,0 +1,794 @@
+/*
+ * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
+ * - test/built-ins/String/prototype/split/call-split-l-0-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-l-1-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-l-2-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-l-3-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-l-4-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-l-na-n-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-l-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-ll-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-h-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-hello-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-hellothere-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-o-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-x-instance-is-string-hello.js
+ * - test/built-ins/String/prototype/split/call-split-x-instance-is-empty-string.js
+ * - test/built-ins/String/prototype/split/call-split-4-instance-is-string-one-1-two-2-four-4.js
+ * - test/built-ins/String/prototype/split/call-split-on-instance-is-string-one-1-two-2-four-4.js
+ * - test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three-four-five.js
+ * - test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three.js
+ * - test/built-ins/String/prototype/split/call-split-instance-is-string.js
+ * - test/built-ins/String/prototype/split/instance-is-string-one-two-three-four-five.js
+ * - test/built-ins/String/prototype/split/instance-is-string.js
+ * - test/built-ins/String/prototype/split/separator-colon-instance-is-string-one-1-two-2-four-4.js
+ * - test/built-ins/String/prototype/split/separator-comma-instance-is-string-one-two-three-four-five.js
+ * - test/built-ins/String/prototype/split/separator-empty-string-instance-is-string.js
+ * - test/built-ins/String/prototype/split/call-split-without-arguments-and-instance-is-empty-string.js
+ * - test/built-ins/String/prototype/split/separator-undef.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A1_T6.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A1_T14.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T1.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T2.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T3.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T4.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T5.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T6.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T7.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T8.js
+ * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T9.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A1_T6.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A1_T14.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T1.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T2.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T3.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T4.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T5.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T6.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T7.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T8.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T9.js
+ * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T10.js
+ * - test/annexB/built-ins/String/prototype/substr/start-negative.js
+ * - test/annexB/built-ins/String/prototype/substr/length-negative.js
+ * - test/annexB/built-ins/String/prototype/substr/length-positive.js
+ * - test/annexB/built-ins/String/prototype/substr/length-falsey.js
+ * - test/annexB/built-ins/String/prototype/substr/length-undef.js
+ * - test/annexB/built-ins/String/prototype/substr/surrogate-pairs.js
+ * - test/built-ins/String/prototype/includes/String.prototype.includes_FailMissingLetter.js
+ * - test/built-ins/String/prototype/includes/String.prototype.includes_SuccessNoLocation.js
+ * - test/built-ins/String/prototype/includes/String.prototype.includes_FailBadLocation.js
+ * - test/built-ins/String/prototype/includes/String.prototype.includes_FailLocation.js
+ * - test/built-ins/String/prototype/includes/String.prototype.includes_Success.js
+ * - test/built-ins/String/prototype/includes/searchstring-found-with-position.js
+ * - test/built-ins/String/prototype/includes/searchstring-found-without-position.js
+ * - test/built-ins/String/prototype/includes/searchstring-not-found-with-position.js
+ * - test/built-ins/String/prototype/includes/searchstring-not-found-without-position.js
+ * - test/built-ins/String/prototype/includes/return-false-with-out-of-bounds-position.js
+ * - test/built-ins/String/prototype/includes/return-true-if-searchstring-is-empty.js
+ * - test/built-ins/String/prototype/includes/coerced-values-of-position.js
+ * - test/built-ins/String/prototype/startsWith/searchstring-found-with-position.js
+ * - test/built-ins/String/prototype/startsWith/searchstring-found-without-position.js
+ * - test/built-ins/String/prototype/startsWith/searchstring-not-found-with-position.js
+ * - test/built-ins/String/prototype/startsWith/searchstring-not-found-without-position.js
+ * - test/built-ins/String/prototype/startsWith/out-of-bounds-position.js
+ * - test/built-ins/String/prototype/startsWith/return-true-if-searchstring-is-empty.js
+ * - test/built-ins/String/prototype/startsWith/coerced-values-of-position.js
+ * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success.js
+ * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_2.js
+ * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_3.js
+ * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_4.js
+ * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail.js
+ * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail_2.js
+ * - test/built-ins/String/prototype/endsWith/searchstring-found-with-position.js
+ * - test/built-ins/String/prototype/endsWith/searchstring-found-without-position.js
+ * - test/built-ins/String/prototype/endsWith/searchstring-not-found-with-position.js
+ * - test/built-ins/String/prototype/endsWith/searchstring-not-found-without-position.js
+ * - test/built-ins/String/prototype/endsWith/return-false-if-search-start-is-less-than-zero.js
+ * - test/built-ins/String/prototype/endsWith/return-true-if-searchstring-is-empty.js
+ * - test/built-ins/String/prototype/endsWith/coerced-values-of-position.js
+ * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T1.js
+ * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T2.js
+ * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T3.js
+ * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T4.js
+ * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T1.js
+ * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T3.js
+ * - test/built-ins/String/prototype/indexOf/position-tointeger.js
+ * - test/built-ins/String/prototype/indexOf/searchstring-tostring.js
+ * - test/built-ins/String/prototype/lastIndexOf/not-a-substring.js
+ *
+ * Copyright 2009 the Sputnik authors.  All rights reserved.
+ * Copyright (c) 2014 Ryan Lewis. All rights reserved.
+ * Copyright (C) 2015 the V8 project authors. All rights reserved.
+ * Copyright (C) 2016 the V8 project authors. All rights reserved.
+ * Copyright (C) 2017 Josh Wolfe. All rights reserved.
+ * Copyright (C) 2020 Leo Balter. All rights reserved.
+ * Copyright (C) 2026 Garham Lee. All rights reserved.
+ * Test262 portions are governed by the BSD license in LICENSE.test262.
+ */
+import { describe, expect, test } from "bun:test"
+import { Effect } from "effect"
+import { CodeMode } from "../src/index.js"
+
+const value = async (code: string) => {
+  const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
+  if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
+  return result.value
+}
+
+const cases = [
+  {
+    path: "test/built-ins/String/prototype/split/call-split-l-0-instance-is-string-hello.js",
+    code: `const result = "hello".split("l", 0); return [result.length, result[0] === undefined]`,
+    expected: [0, true],
+    labels: [
+      "The value of __split.length is expected to equal the value of __expected.length",
+      "The value of __split[0] is expected to equal the value of __expected[0]",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-l-1-instance-is-string-hello.js",
+    code: `const result = "hello".split("l", 1); return [result.length, result[0]]`,
+    expected: [1, "he"],
+    labels: [
+      "The value of __split.length is expected to equal the value of __expected.length",
+      "The value of __split[0] is expected to equal the value of __expected[0]",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-l-2-instance-is-string-hello.js",
+    code: `const result = "hello".split("l", 2); return [result.length, result[0], result[1]]`,
+    expected: [2, "he", ""],
+    labels: [
+      "The value of __split.length is expected to equal the value of __expected.length",
+      "The value of __split[index] is expected to equal the value of __expected[index]",
+      "The value of __split[index] is expected to equal the value of __expected[index]",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-l-3-instance-is-string-hello.js",
+    code: `const result = "hello".split("l", 3); return [result.length, result[0], result[1], result[2]]`,
+    expected: [3, "he", "", "o"],
+    labels: [
+      "The value of __split.length is expected to equal the value of __expected.length",
+      "The value of __split[index] is expected to equal the value of __expected[index]",
+      "The value of __split[index] is expected to equal the value of __expected[index]",
+      "The value of __split[index] is expected to equal the value of __expected[index]",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-l-4-instance-is-string-hello.js",
+    code: `const result = "hello".split("l", 4); return [result.length, result[0], result[1], result[2]]`,
+    expected: [3, "he", "", "o"],
+    labels: [
+      "The value of __split.length is expected to equal the value of __expected.length",
+      "The value of __split[index] is expected to equal the value of __expected[index]",
+      "The value of __split[index] is expected to equal the value of __expected[index]",
+      "The value of __split[index] is expected to equal the value of __expected[index]",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-l-na-n-instance-is-string-hello.js",
+    code: `const result = "hello".split("l", NaN); return [result.length, result[0] === undefined]`,
+    expected: [0, true],
+    labels: [
+      "The value of __split.length is expected to equal the value of __expected.length",
+      "The value of __split[0] is expected to equal the value of __expected[0]",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-l-instance-is-string-hello.js",
+    code: `const result = "hello".split("l"); return [result.length, result[0], result[1], result[2]]`,
+    expected: [3, "he", "", "o"],
+    labels: [
+      "The value of __split.length is 3",
+      'The value of __split[0] is "he"',
+      'The value of __split[1] is ""',
+      'The value of __split[2] is "o"',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-ll-instance-is-string-hello.js",
+    code: `const result = "hello".split("ll"); return [result.length, result[0], result[1]]`,
+    expected: [2, "he", "o"],
+    labels: ["The value of __split.length is 2", 'The value of __split[0] is "he"', 'The value of __split[1] is "o"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-h-instance-is-string-hello.js",
+    code: `const result = "hello".split("h"); return [result.length, result[0], result[1]]`,
+    expected: [2, "", "ello"],
+    labels: ["The value of __split.length is 2", 'The value of __split[0] is ""', 'The value of __split[1] is "ello"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-hello-instance-is-string-hello.js",
+    code: `const result = "hello".split("hello"); return [result.length, result[0], result[1]]`,
+    expected: [2, "", ""],
+    labels: ["The value of __split.length is 2", 'The value of __split[0] is ""', 'The value of __split[1] is ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-hellothere-instance-is-string-hello.js",
+    code: `const result = "hello".split("hellothere"); return [result.length, result[0]]`,
+    expected: [1, "hello"],
+    labels: ["The value of __split.length is 1", 'The value of __split[0] is "hello"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-o-instance-is-string-hello.js",
+    code: `const result = "hello".split("o"); return [result.length, result[0], result[1]]`,
+    expected: [2, "hell", ""],
+    labels: ["The value of __split.length is 2", 'The value of __split[0] is "hell"', 'The value of __split[1] is ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-x-instance-is-string-hello.js",
+    code: `const result = "hello".split("x"); return [result.length, result[0]]`,
+    expected: [1, "hello"],
+    labels: ["The value of __split.length is 1", 'The value of __split[0] is "hello"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-x-instance-is-empty-string.js",
+    code: `const result = "".split("x"); return [result.length, result[0]]`,
+    expected: [1, ""],
+    labels: ["The value of __split.length is 1", 'The value of __split[0] is ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-4-instance-is-string-one-1-two-2-four-4.js",
+    code: `const result = "one-1 two-2 four-4".split("-4"); return [result.length, result[0], result[1]]`,
+    expected: [2, "one-1 two-2 four", ""],
+    labels: [
+      "The value of __split.length is 2",
+      'The value of __split[0] is "one-1 two-2 four"',
+      'The value of __split[1] is ""',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-on-instance-is-string-one-1-two-2-four-4.js",
+    code: `const result = "one-1 two-2 four-4".split("on"); return [result.length, result[0], result[1]]`,
+    expected: [2, "", "e-1 two-2 four-4"],
+    labels: [
+      "The value of __split.length is 2",
+      'The value of __split[0] is ""',
+      'The value of __split[1] is "e-1 two-2 four-4"',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three-four-five.js",
+    code: `const result = "one two three four five".split(" "); return [result.length, ...result]`,
+    expected: [5, "one", "two", "three", "four", "five"],
+    labels: [
+      "The value of __split.length is 5", 'The value of __split[0] is "one"', 'The value of __split[1] is "two"',
+      'The value of __split[2] is "three"', 'The value of __split[3] is "four"', 'The value of __split[4] is "five"',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three.js",
+    code: `const result = "one two three".split(""); return [result[0], result[1], result[11], result[12]]`,
+    expected: ["o", "n", "e", "e"],
+    labels: [
+      'The value of __split[0] is "o"', 'The value of __split[1] is "n"',
+      'The value of __split[11] is "e"', 'The value of __split[12] is "e"',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-instance-is-string.js",
+    code: `const result = " ".split(" "); return [result.length, result[0], result[1]]`,
+    expected: [2, "", ""],
+    labels: ["The value of __split.length is 2", 'The value of __split[0] is ""', 'The value of __split[1] is ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/instance-is-string-one-two-three-four-five.js",
+    code: `const result = "one,two,three,four,five".split(); return [result.length, result[0]]`,
+    expected: [1, "one,two,three,four,five"],
+    labels: ["The value of __split.length is 1", 'The value of __split[0] is "one,two,three,four,five"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/instance-is-string.js",
+    code: `const result = " ".split(); return [result.length, result[0]]`,
+    expected: [1, " "],
+    labels: ["The value of __split.length is 1", 'The value of __split[0] is " "'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/separator-colon-instance-is-string-one-1-two-2-four-4.js",
+    code: `const result = "one-1,two-2,four-4".split(":"); return [result.length, result[0]]`,
+    expected: [1, "one-1,two-2,four-4"],
+    labels: ["The value of __split.length is 1", 'The value of __split[0] is "one-1,two-2,four-4"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/separator-comma-instance-is-string-one-two-three-four-five.js",
+    code: `const result = "one,two,three,four,five".split(","); return [result.length, ...result]`,
+    expected: [5, "one", "two", "three", "four", "five"],
+    labels: [
+      "The value of __split.length is 5",
+      'The value of __split[0] is "one"',
+      'The value of __split[1] is "two"',
+      'The value of __split[2] is "three"',
+      'The value of __split[3] is "four"',
+      'The value of __split[4] is "five"',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/separator-empty-string-instance-is-string.js",
+    code: `const result = " ".split(""); return [result.length, result[0]]`,
+    expected: [1, " "],
+    labels: ["The value of __split.length is 1", 'The value of __split[0] is " "'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/call-split-without-arguments-and-instance-is-empty-string.js",
+    code: `const result = "".split(); return [result.length, result[0]]`,
+    expected: [1, ""],
+    labels: ["The value of __split.length is 1", 'The value of __split[0] is ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/split/separator-undef.js",
+    code: `const result = "undefined is not a function".split(); return [Array.isArray(result), result.length, result[0]]`,
+    expected: [true, 1, "undefined is not a function"],
+    labels: ["implicit separator, result is array", "implicit separator, result.length", "implicit separator, [0] is the same string"],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A1_T6.js",
+    code: `return ["undefined".slice(undefined, 3)]`,
+    expected: ["und"],
+    labels: ['#1: new String("undefined").slice(x,3) === "und"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A1_T14.js",
+    code: `return ["report".slice(undefined)]`,
+    expected: ["report"],
+    labels: ['#1: "report".slice(function(){}()) === "report"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T1.js",
+    code: `return [typeof "this is a string object".slice()]`,
+    expected: ["string"],
+    labels: ['#1: typeof __string.slice() === "string"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T2.js",
+    code: `return ["this is a string object".slice(NaN, Infinity)]`,
+    expected: ["this is a string object"],
+    labels: ['#1: __string.slice(NaN, Infinity) === "this is a string object"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T3.js",
+    code: `return ["".slice(1, 0)]`,
+    expected: [""],
+    labels: ['#1: __string.slice(1,0) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T4.js",
+    code: `return ["this is a string object".slice(Infinity, NaN)]`,
+    expected: [""],
+    labels: ['#1: __string.slice(Infinity, NaN) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T5.js",
+    code: `return ["this is a string object".slice(Infinity, Infinity)]`,
+    expected: [""],
+    labels: ['#1: __string.slice(Infinity, Infinity) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T6.js",
+    code: `return ["this is a string object".slice(-0.01, 0)]`,
+    expected: [""],
+    labels: ['#1: __string.slice(-0.01,0) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T7.js",
+    code: `const text = "this is a string object"; return [text.slice(text.length, text.length)]`,
+    expected: [""],
+    labels: ['#1: __string.slice(__string.length, __string.length) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T8.js",
+    code: `const text = "this is a string object"; return [text.slice(text.length + 1, 0)]`,
+    expected: [""],
+    labels: ['#1: __string.slice(__string.length+1, 0) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T9.js",
+    code: `return ["this is a string object".slice(-Infinity, -Infinity)]`,
+    expected: [""],
+    labels: ['#1: __string.slice(-Infinity, -Infinity) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A1_T6.js",
+    code: `return ["undefined".substring(undefined, 3)]`,
+    expected: ["und"],
+    labels: ['#1: new String("undefined").substring(x,3) === "und"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A1_T14.js",
+    code: `return ["report".substring(undefined)]`,
+    expected: ["report"],
+    labels: ['#1: "report".substring(function(){}()) === "report"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T1.js",
+    code: `return [typeof "this is a string object".substring()]`,
+    expected: ["string"],
+    labels: ['#1: typeof __string.substring() === "string"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T2.js",
+    code: `return ["this is a string object".substring(NaN, Infinity)]`,
+    expected: ["this is a string object"],
+    labels: ['#1: __string.substring(NaN, Infinity) === "this is a string object"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T3.js",
+    code: `return ["".substring(1, 0)]`,
+    expected: [""],
+    labels: ['#1: __string.substring(1,0) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T4.js",
+    code: `return ["this is a string object".substring(Infinity, NaN)]`,
+    expected: ["this is a string object"],
+    labels: ['#1: __string.substring(Infinity, NaN) === "this is a string object"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T5.js",
+    code: `return ["this is a string object".substring(Infinity, Infinity)]`,
+    expected: [""],
+    labels: ['#1: __string.substring(Infinity, Infinity) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T6.js",
+    code: `return ["this is a string object".substring(-0.01, 0)]`,
+    expected: [""],
+    labels: ['#1: __string.substring(-0.01,0) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T7.js",
+    code: `const text = "this is a string object"; return [text.substring(text.length, text.length)]`,
+    expected: [""],
+    labels: ['#1: __string.substring(__string.length, __string.length) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T8.js",
+    code: `const text = "this is a string object"; return [text.substring(text.length + 1, 0)]`,
+    expected: ["this is a string object"],
+    labels: ['#1: __string.substring(__string.length+1, 0) === "this is a string object"'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T9.js",
+    code: `return ["this is a string object".substring(-Infinity, -Infinity)]`,
+    expected: [""],
+    labels: ['#1: __string.substring(-Infinity, -Infinity) === ""'],
+  },
+  {
+    path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T10.js",
+    code: `return ["this_is_a_string object".substring(0, 8)]`,
+    expected: ["this_is_"],
+    labels: ['#1: __string.substring(0,8) === "this_is_"'],
+  },
+  {
+    path: "test/annexB/built-ins/String/prototype/substr/start-negative.js",
+    code: `return ["abc".substr(-1), "abc".substr(-2), "abc".substr(-3), "abc".substr(-4), "abc".substr(-1.1)]`,
+    expected: ["c", "bc", "abc", "abc", "c"],
+    labels: ["-1", "-2", "-3", "size + intStart < 0", "floating point rounding semantics"],
+  },
+  {
+    path: "test/annexB/built-ins/String/prototype/substr/length-negative.js",
+    code: `return [
+      "abc".substr(0, -1), "abc".substr(0, -2), "abc".substr(0, -3), "abc".substr(0, -4),
+      "abc".substr(1, -1), "abc".substr(1, -2), "abc".substr(1, -3), "abc".substr(1, -4),
+      "abc".substr(2, -1), "abc".substr(2, -2), "abc".substr(2, -3), "abc".substr(2, -4),
+      "abc".substr(3, -1), "abc".substr(3, -2), "abc".substr(3, -3), "abc".substr(3, -4),
+    ]`,
+    expected: ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
+    labels: [
+      "0, -1", "0, -2", "0, -3", "0, -4", "1, -1", "1, -2", "1, -3", "1, -4",
+      "2, -1", "2, -2", "2, -3", "2, -4", "3, -1", "3, -2", "3, -3", "3, -4",
+    ],
+  },
+  {
+    path: "test/annexB/built-ins/String/prototype/substr/length-positive.js",
+    code: `return [
+      "abc".substr(0, 1), "abc".substr(0, 2), "abc".substr(0, 3), "abc".substr(0, 4),
+      "abc".substr(1, 1), "abc".substr(1, 2), "abc".substr(1, 3), "abc".substr(1, 4),
+      "abc".substr(2, 1), "abc".substr(2, 2), "abc".substr(2, 3), "abc".substr(2, 4),
+      "abc".substr(3, 1), "abc".substr(3, 2), "abc".substr(3, 3), "abc".substr(3, 4),
+    ]`,
+    expected: ["a", "ab", "abc", "abc", "b", "bc", "bc", "bc", "c", "c", "c", "c", "", "", "", ""],
+    labels: [
+      "0, 1", "0, 1", "0, 1", "0, 1", "1, 1", "1, 1", "1, 1", "1, 1",
+      "2, 1", "2, 1", "2, 1", "2, 1", "3, 1", "3, 1", "3, 1", "3, 1",
+    ],
+  },
+  {
+    path: "test/annexB/built-ins/String/prototype/substr/length-falsey.js",
+    code: `return ["abc".substr(0, NaN), "abc".substr(1, NaN), "abc".substr(2, NaN), "abc".substr(3, NaN)]`,
+    expected: ["", "", "", ""],
+    labels: ["start: 0, length: NaN", "start: 1, length: NaN", "start: 2, length: NaN", "start: 3, length: NaN"],
+  },
+  {
+    path: "test/annexB/built-ins/String/prototype/substr/length-undef.js",
+    code: `return [
+      "abc".substr(0), "abc".substr(1), "abc".substr(2), "abc".substr(3),
+      "abc".substr(0, undefined), "abc".substr(1, undefined), "abc".substr(2, undefined), "abc".substr(3, undefined),
+    ]`,
+    expected: ["abc", "bc", "c", "", "abc", "bc", "c", ""],
+    labels: [
+      "start: 0, length: unspecified", "start: 1, length: unspecified", "start: 2, length: unspecified", "start: 3, length: unspecified",
+      "start: 0, length: undefined", "start: 1, length: undefined", "start: 2, length: undefined", "start: 3, length: undefined",
+    ],
+  },
+  {
+    path: "test/annexB/built-ins/String/prototype/substr/surrogate-pairs.js",
+    code: `return [
+      "\uD834\uDF06".substr(0), "\uD834\uDF06".substr(1), "\uD834\uDF06".substr(2),
+      "\uD834\uDF06".substr(0, 0), "\uD834\uDF06".substr(0, 1), "\uD834\uDF06".substr(0, 2),
+    ]`,
+    expected: ["\uD834\uDF06", "\uDF06", "", "", "\uD834", "\uD834\uDF06"],
+    labels: ["start: 0", "start: 1", "start: 2", "end: 0", "end: 1", "end: 2"],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/String.prototype.includes_FailMissingLetter.js",
+    code: `return ["word".includes("a", 0)]`, expected: [false], labels: ['"word".includes("a", 0)'],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/String.prototype.includes_SuccessNoLocation.js",
+    code: `return ["word".includes("w")]`, expected: [true], labels: ['"word".includes("w")'],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/String.prototype.includes_FailBadLocation.js",
+    code: `return ["word".includes("w", 5)]`, expected: [false], labels: ['"word".includes("w", 5)'],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/String.prototype.includes_FailLocation.js",
+    code: `return ["word".includes("o", 3)]`, expected: [false], labels: ['"word".includes("o", 3)'],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/String.prototype.includes_Success.js",
+    code: `return ["word".includes("w", 0)]`, expected: [true], labels: ['"word".includes("w", 0)'],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/searchstring-found-with-position.js",
+    code: `const text = "The future is cool!"; return [text.includes("The future", 0), text.includes(" is ", 1), text.includes("cool!", 10)]`,
+    expected: [true, true, true],
+    labels: [
+      'Returns true for str.includes("The future", 0)',
+      'Returns true for str.includes(" is ", 1)',
+      'Returns true for str.includes("cool!", 10)',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/searchstring-found-without-position.js",
+    code: `const text = "The future is cool!"; return [text.includes("The future"), text.includes("is cool!"), text.includes(text)]`,
+    expected: [true, true, true],
+    labels: [
+      'Returns true for str.includes("The future")',
+      'Returns true for str.includes("is cool!")',
+      "Returns true for str.includes(str)",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/searchstring-not-found-with-position.js",
+    code: `const text = "The future is cool!"; return [text.includes("The future", 1), text.includes(text, 1)]`,
+    expected: [false, false],
+    labels: ['Returns false on str.includes("The future", 1)', "Returns false on str.includes(str, 1)"],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/searchstring-not-found-without-position.js",
+    code: `const text = "The future is cool!"; return [text.includes("Flash"), text.includes("FUTURE")]`,
+    expected: [false, false], labels: ["Flash if not included", "includes is case sensitive"],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/return-false-with-out-of-bounds-position.js",
+    code: `const text = "The future is cool!"; return [
+      text.includes("!", text.length + 1), text.includes("!", 100), text.includes("!", Infinity), text.includes("!", text.length),
+    ]`,
+    expected: [false, false, false, false],
+    labels: [
+      'str.includes("!", str.length + 1) returns false', 'str.includes("!", 100) returns false',
+      'str.includes("!", Infinity) returns false', 'str.includes("!", str.length) returns false',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/return-true-if-searchstring-is-empty.js",
+    code: `const text = "The future is cool!"; return [text.includes("", text.length), text.includes(""), text.includes("", Infinity)]`,
+    expected: [true, true, true],
+    labels: ['str.includes("", str.length) returns true', 'str.includes("") returns true', 'str.includes("", Infinity) returns true'],
+  },
+  {
+    path: "test/built-ins/String/prototype/includes/coerced-values-of-position.js",
+    code: `const text = "The future is cool!"; return [
+      text.includes("The future", NaN), text.includes("The future", undefined), text.includes("The future", 0.4),
+      text.includes("The future", -1), text.includes("The future", 1.4),
+    ]`,
+    expected: [true, true, true, true, false],
+    labels: ["NaN coerced to 0", "undefined coerced to 0", "0.4 coerced to 0", "negative position", "1.4 coerced to 1"],
+  },
+  {
+    path: "test/built-ins/String/prototype/startsWith/searchstring-found-with-position.js",
+    code: `const text = "The future is cool!"; return [text.startsWith("The future", 0), text.startsWith("future", 4), text.startsWith(" is cool!", 10)]`,
+    expected: [true, true, true],
+    labels: [
+      'str.startsWith("The future", 0) === true', 'str.startsWith("future", 4) === true',
+      'str.startsWith(" is cool!", 10) === true',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/startsWith/searchstring-found-without-position.js",
+    code: `const text = "The future is cool!"; return [text.startsWith("The "), text.startsWith("The future"), text.startsWith(text)]`,
+    expected: [true, true, true],
+    labels: ['str.startsWith("The ") === true', 'str.startsWith("The future") === true', "str.startsWith(str) === true"],
+  },
+  {
+    path: "test/built-ins/String/prototype/startsWith/searchstring-not-found-with-position.js",
+    code: `const text = "The future is cool!"; return [text.startsWith("The future", 1), text.startsWith(text, 1)]`,
+    expected: [false, false],
+    labels: ['str.startsWith("The future", 1) === false', "str.startsWith(str, 1) === false"],
+  },
+  {
+    path: "test/built-ins/String/prototype/startsWith/searchstring-not-found-without-position.js",
+    code: `const text = "The future is cool!"; return [text.startsWith("Flash"), text.startsWith("THE FUTURE"), text.startsWith("future is cool!")]`,
+    expected: [false, false, false],
+    labels: ['str.startsWith("Flash") === false', "startsWith is case sensitive", 'str.startsWith("future is cool!") === false'],
+  },
+  {
+    path: "test/built-ins/String/prototype/startsWith/out-of-bounds-position.js",
+    code: `const text = "The future is cool!"; return [
+      text.startsWith("!", text.length), text.startsWith("!", 100), text.startsWith("!", Infinity),
+      text.startsWith("The future", -1), text.startsWith("The future", -Infinity),
+    ]`,
+    expected: [false, false, false, true, true],
+    labels: [
+      'str.startsWith("!", str.length) returns false', 'str.startsWith("!", 100) returns false',
+      'str.startsWith("!", Infinity) returns false', "position argument < 0 will search from the start of the string (-1)",
+      "position argument < 0 will search from the start of the string (-Infinity)",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/startsWith/return-true-if-searchstring-is-empty.js",
+    code: `const text = "The future is cool!"; return [text.startsWith(""), text.startsWith("", text.length), text.startsWith("", Infinity)]`,
+    expected: [true, true, true],
+    labels: ['str.startsWith("") returns true', 'str.startsWith("", str.length) returns true', 'str.startsWith("", Infinity) returns true'],
+  },
+  {
+    path: "test/built-ins/String/prototype/startsWith/coerced-values-of-position.js",
+    code: `const text = "The future is cool!"; return [
+      text.startsWith("The future", NaN), text.startsWith("The future", undefined),
+      text.startsWith("The future", 0.4), text.startsWith("The future", 1.4),
+    ]`,
+    expected: [true, true, true, false],
+    labels: ["NaN coerced to 0", "undefined coerced to 0", "0.4 coerced to 0", "1.4 coerced to 1"],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success.js",
+    code: `return ["word".endsWith("d")]`, expected: [true], labels: ['"word".endsWith("d")'],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_2.js",
+    code: `return ["word".endsWith("d", 4)]`, expected: [true], labels: ['"word".endsWith("d", 4)'],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_3.js",
+    code: `return ["word".endsWith("d", 25)]`, expected: [true], labels: ['"word".endsWith("d", 25)'],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_4.js",
+    code: `return ["word".endsWith("r", 3)]`, expected: [true], labels: ['"word".endsWith("r", 3)'],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail.js",
+    code: `return ["word".endsWith("r")]`, expected: [false], labels: ['"word".endsWith("r")'],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail_2.js",
+    code: `return ["word".endsWith("d", 3)]`, expected: [false], labels: ['"word".endsWith("d", 3)'],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/searchstring-found-with-position.js",
+    code: `const text = "The future is cool!"; return [text.endsWith("The future", 10), text.endsWith("future", 10), text.endsWith(" is cool!", text.length)]`,
+    expected: [true, true, true],
+    labels: [
+      'str.endsWith("The future", 10) === true', 'str.endsWith("future", 10) === true',
+      'str.endsWith(" is cool!", str.length) === true',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/searchstring-found-without-position.js",
+    code: `const text = "The future is cool!"; return [text.endsWith("cool!"), text.endsWith("!"), text.endsWith(text)]`,
+    expected: [true, true, true],
+    labels: ['str.endsWith("cool!") === true', 'str.endsWith("!") === true', "str.endsWith(str) === true"],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/searchstring-not-found-with-position.js",
+    code: `const text = "The future is cool!"; return [text.endsWith("is cool!", text.length - 1), text.endsWith("!", 1)]`,
+    expected: [false, false],
+    labels: ['str.endsWith("is cool!", str.length - 1) === false', 'str.endsWith("!", 1) === false'],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/searchstring-not-found-without-position.js",
+    code: `const text = "The future is cool!"; return [text.endsWith("is Flash!"), text.endsWith("IS COOL!"), text.endsWith("The future")]`,
+    expected: [false, false, false],
+    labels: ['str.endsWith("is Flash!") === false', "endsWith is case sensitive", 'str.endsWith("The future") === false'],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/return-false-if-search-start-is-less-than-zero.js",
+    code: `return ["web".endsWith("w", 0), "Bob".endsWith("  Bob")]`,
+    expected: [false, false],
+    labels: ['"web".endsWith("w", 0) returns false', '"Bob".endsWith("  Bob") returns false'],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/return-true-if-searchstring-is-empty.js",
+    code: `const text = "The future is cool!"; return [
+      text.endsWith(""), text.endsWith("", text.length), text.endsWith("", Infinity),
+      text.endsWith("", -1), text.endsWith("", -Infinity),
+    ]`,
+    expected: [true, true, true, true, true],
+    labels: [
+      'str.endsWith("") returns true', 'str.endsWith("", str.length) returns true', 'str.endsWith("", Infinity) returns true',
+      'str.endsWith("", -1) returns true', 'str.endsWith("", -Infinity) returns true',
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/endsWith/coerced-values-of-position.js",
+    code: `const text = "The future is cool!"; return [
+      text.endsWith("", NaN), text.endsWith("", undefined), text.endsWith("The future", 10.4),
+    ]`,
+    expected: [true, true, true],
+    labels: ["NaN coerced to 0", "undefined coerced to 0", "10.4 coerced to 10"],
+  },
+  {
+    path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T1.js",
+    code: `return ["abcd".indexOf("abcdab")]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab")===-1'],
+  },
+  {
+    path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T2.js",
+    code: `return ["abcd".indexOf("abcdab", 0)]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab",0)===-1'],
+  },
+  {
+    path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T3.js",
+    code: `return ["abcd".indexOf("abcdab", 99)]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab",99)===-1'],
+  },
+  {
+    path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T4.js",
+    code: `return ["abcd".indexOf("abcdab", NaN)]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab",NaN)===-1'],
+  },
+  {
+    path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T1.js",
+    code: `return ["$$abcdabcd".indexOf("ab", NaN)]`, expected: [2], labels: ['#1: "$$abcdabcd".indexOf("ab",NaN)===2'],
+  },
+  {
+    path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T3.js",
+    code: `return ["$$abcdabcd".indexOf("ab", -Infinity)]`, expected: [2], labels: ['#1: "$$abcdabcd".indexOf("ab", function(){return -Infinity;}())===2'],
+  },
+  {
+    path: "test/built-ins/String/prototype/indexOf/position-tointeger.js",
+    code: `return [
+      "aaaa".indexOf("aa", 0), "aaaa".indexOf("aa", 1), "aaaa".indexOf("aa", -0.9),
+      "aaaa".indexOf("aa", 0.9), "aaaa".indexOf("aa", 1.9), "aaaa".indexOf("aa", NaN),
+      "aaaa".indexOf("aa", Infinity), "aaaa".indexOf("aa", undefined),
+      "aaaa".indexOf("aa", 2), "aaaa".indexOf("aa", 2.9),
+    ]`,
+    expected: [0, 1, 0, 0, 1, 0, -1, 0, 2, 2],
+    labels: [
+      "position 0", "position 1", "ToInteger: truncate towards 0 (-0.9)", "ToInteger: truncate towards 0 (0.9)",
+      "ToInteger: truncate towards 0 (1.9)", "ToInteger: NaN => 0", "position Infinity",
+      "ToInteger: undefined => NaN => 0", "position 2", "ToInteger: truncate towards 0 (2.9)",
+    ],
+  },
+  {
+    path: "test/built-ins/String/prototype/indexOf/searchstring-tostring.js",
+    code: `return ["foo".indexOf(""), "__foo__".indexOf("foo")]`,
+    expected: [0, 2], labels: ['"foo".indexOf("")', '"__foo__".indexOf("foo")'],
+  },
+  {
+    path: "test/built-ins/String/prototype/lastIndexOf/not-a-substring.js",
+    code: `return ["abc".lastIndexOf("d")]`,
+    expected: [-1],
+    labels: ["String.prototype.lastIndexOf returns -1 when searchString is shorter than this and searchString is not a substring of this."],
+  },
+] as const
+
+describe("Test262-adapted String search and extraction behavior", () => {
+  for (const item of cases) {
+    test(item.path, async () => {
+      const actual = await value(item.code)
+      if (!Array.isArray(actual)) throw new Error(`expected assertion values for ${item.path}`)
+      expect(actual.length, "adapted assertion count").toBe(item.expected.length)
+      item.expected.forEach((expected, index) => expect(actual[index], item.labels[index]!).toEqual(expected))
+    })
+  }
+})

+ 77 - 0
packages/codemode/test/test262-array.md

@@ -0,0 +1,77 @@
+# Test262 Array Coverage
+
+The Array tests adapt Test262 at revision `250f204f23a9249ff204be2baec29600faae7b75`. They cover CodeMode's 35
+exposed instance methods and three static methods using actual arrays, accepted argument types, deterministic behavior,
+and CodeMode's materialized collection conventions. Each executable case names its exact upstream source path.
+`LICENSE.test262` contains the upstream BSD terms.
+
+This is coverage of CodeMode's bounded Array surface, not a claim of ECMAScript or Test262 conformance. One upstream
+file may contain both adapted and inapplicable assertions, so a cited source means only that the represented assertions
+were adapted.
+
+## Inventory
+
+The 38 relevant upstream API directories contain 2,837 files. The executable suite adapts assertions from 83 distinct
+sources.
+
+| API                             | Upstream files | Adapted sources |
+| ------------------------------- | -------------: | --------------: |
+| `Array.prototype.map`           |            216 |               3 |
+| `Array.prototype.filter`        |            242 |               3 |
+| `Array.prototype.find`          |             23 |               4 |
+| `Array.prototype.findIndex`     |             23 |               3 |
+| `Array.prototype.findLast`      |             24 |               3 |
+| `Array.prototype.findLastIndex` |             24 |               3 |
+| `Array.prototype.some`          |            219 |               2 |
+| `Array.prototype.every`         |            218 |               2 |
+| `Array.prototype.includes`      |             30 |               2 |
+| `Array.prototype.join`          |             23 |               2 |
+| `Array.prototype.reduce`        |            260 |               3 |
+| `Array.prototype.reduceRight`   |            260 |               3 |
+| `Array.prototype.flatMap`       |             24 |               2 |
+| `Array.prototype.forEach`       |            190 |               2 |
+| `Array.prototype.sort`          |             54 |               3 |
+| `Array.prototype.toSorted`      |             21 |               4 |
+| `Array.prototype.slice`         |             71 |               1 |
+| `Array.prototype.concat`        |             69 |               3 |
+| `Array.prototype.indexOf`       |            201 |               2 |
+| `Array.prototype.lastIndexOf`   |            198 |               2 |
+| `Array.prototype.at`            |             13 |               3 |
+| `Array.prototype.flat`          |             19 |               2 |
+| `Array.prototype.reverse`       |             18 |               1 |
+| `Array.prototype.toReversed`    |             17 |               2 |
+| `Array.prototype.with`          |             21 |               2 |
+| `Array.prototype.push`          |             24 |               1 |
+| `Array.prototype.pop`           |             23 |               1 |
+| `Array.prototype.shift`         |             20 |               1 |
+| `Array.prototype.unshift`       |             22 |               1 |
+| `Array.prototype.splice`        |             81 |               3 |
+| `Array.prototype.fill`          |             22 |               3 |
+| `Array.prototype.copyWithin`    |             39 |               2 |
+| `Array.prototype.keys`          |             12 |               1 |
+| `Array.prototype.values`        |             12 |               1 |
+| `Array.prototype.entries`       |             12 |               1 |
+| `Array.from`                    |             47 |               3 |
+| `Array.isArray`                 |             29 |               2 |
+| `Array.of`                      |             16 |               1 |
+
+## Exclusions
+
+Assertions are not adapted when they test behavior outside CodeMode's documented Array surface:
+
+- Function metadata, property descriptors, constructibility, prototype mutation, species constructors, or cross-realm
+  identity.
+- Generic receivers, detached methods, `.call`, `.apply`, boxed values, custom coercion objects, Symbols, BigInts,
+  proxies, accessors, frozen arrays, typed arrays, or ArrayBuffers.
+- `Array.from` mappers, custom iterables, constructor substitution, and iterator-closing behavior.
+- Native iterator identity, `.next()`, completion records, or live iterator mutation. CodeMode deliberately materializes
+  `keys`, `values`, and `entries` as arrays.
+- Sparse-array assertions that depend on literal elisions or inherited indexed properties. CodeMode's confined data
+  model does not preserve those prototype and hole semantics at every boundary.
+- Argument coercions outside the accepted schema-like surface. Numeric positions must be numbers and `join` separators
+  must be strings.
+- Exact native error brands where CodeMode exposes a safe runtime error instead.
+- Async/effectful callbacks, circular-data rejection, sandbox-value identity, diagnostics, and host-boundary behavior.
+  Those remain covered by CodeMode-specific tests.
+
+Handwritten tests remain where they specify CodeMode behavior rather than ordinary ECMAScript Array semantics.

+ 69 - 0
packages/codemode/test/test262-string.md

@@ -0,0 +1,69 @@
+# Test262 String Coverage
+
+The String tests adapt Test262 at revision `250f204f23a9249ff204be2baec29600faae7b75`. They cover CodeMode's 32
+exposed instance methods and two static methods using primitive receivers, accepted argument types, and deterministic
+behavior. Each executable case names its exact upstream source path. `LICENSE.test262` contains the upstream BSD terms.
+
+This is coverage of CodeMode's bounded String surface, not a claim of ECMAScript or Test262 conformance. One upstream
+file may contain both adapted and inapplicable assertions, so a cited source means only that the represented assertions
+were adapted.
+
+## Inventory
+
+The relevant upstream directories contain 1,048 files: 1,009 core built-in files, 29 Annex B files for exposed methods,
+and 10 Intl `localeCompare` files. The executable suite adapts assertions from 298 distinct sources.
+
+| API | Upstream files | Adapted sources |
+| --- | ---: | ---: |
+| `String.fromCharCode` | 17 | 6 |
+| `String.fromCodePoint` | 11 | 4 |
+| `String.prototype.at` | 11 | 5 |
+| `String.prototype.charAt` | 30 | 9 |
+| `String.prototype.charCodeAt` | 25 | 4 |
+| `String.prototype.codePointAt` | 16 | 6 |
+| `String.prototype.concat` | 22 | 1 |
+| `String.prototype.endsWith` | 27 | 13 |
+| `String.prototype.includes` | 27 | 12 |
+| `String.prototype.indexOf` | 47 | 8 |
+| `String.prototype.lastIndexOf` | 25 | 1 |
+| `String.prototype.localeCompare` | 23 | 1 |
+| `String.prototype.match` | 52 | 9 |
+| `String.prototype.matchAll` | 26 | 1 |
+| `String.prototype.normalize` | 14 | 3 |
+| `String.prototype.padEnd` | 13 | 4 |
+| `String.prototype.padStart` | 13 | 4 |
+| `String.prototype.repeat` | 16 | 4 |
+| `String.prototype.replace` | 56 | 16 |
+| `String.prototype.replaceAll` | 46 | 12 |
+| `String.prototype.search` | 44 | 10 |
+| `String.prototype.slice` | 38 | 11 |
+| `String.prototype.split` | 121 | 50 |
+| `String.prototype.startsWith` | 21 | 7 |
+| `String.prototype.substr` | 15 | 6 |
+| `String.prototype.substring` | 46 | 12 |
+| `String.prototype.toLowerCase` | 30 | 5 |
+| `String.prototype.toString` | 7 | 1 |
+| `String.prototype.toUpperCase` | 26 | 3 |
+| `String.prototype.trim` | 129 | 66 |
+| `String.prototype.trimEnd` | 23 | 2 |
+| `String.prototype.trimLeft` | 4 | 0 |
+| `String.prototype.trimRight` | 4 | 0 |
+| `String.prototype.trimStart` | 23 | 2 |
+
+## Exclusions
+
+Assertions are not adapted when they test behavior outside CodeMode's documented String surface:
+
+- Function metadata, property descriptors, constructibility, prototype mutation, or cross-realm identity.
+- The `trimLeft`/`trimRight` Test262 files assert prototype function identity, which CodeMode does not expose. Their
+  supported call behavior remains covered by CodeMode-specific tests.
+- Boxed strings, generic receivers, custom coercion objects, Symbols, BigInts, or argument types CodeMode rejects.
+- Symbol-based RegExp dispatch, custom matchers, species constructors, or iterator protocol details. CodeMode materializes
+  `matchAll` results instead of exposing iterators.
+- Locale selection and options. CodeMode deliberately uses the host default locale and ignores those arguments.
+- Test262 harness behavior or setup syntax unavailable in the confined interpreter.
+- Function-replacer behavior that is covered by CodeMode-specific tests for sequential callbacks, async tool calls,
+  result coercion, diagnostics, and sandbox boundaries.
+- Assertions requiring an exact native error type when CodeMode deliberately exposes only its safe runtime error.
+
+Handwritten tests remain where they specify CodeMode behavior rather than ordinary ECMAScript String semantics.

+ 2 - 0
packages/core/src/agent.ts

@@ -8,6 +8,8 @@ import { State } from "./state"
 
 export const ID = Agent.ID
 export type ID = typeof ID.Type
+export const Name = Agent.Name
+export type Name = Agent.Name
 export const defaultID = ID.make("build")
 
 export const Color = Agent.Color

+ 7 - 3
packages/core/src/aisdk.ts

@@ -6,6 +6,7 @@ import type {
   JSONValue,
   LanguageModelV3,
   LanguageModelV3CallOptions,
+  LanguageModelV3FinishReason,
   LanguageModelV3FunctionTool,
   LanguageModelV3Message,
   LanguageModelV3Prompt,
@@ -304,6 +305,7 @@ function modelFromLanguage(info: ModelV2.Info, language: LanguageModelV3) {
   const route: AnyRoute = {
     id: `ai-sdk:${ProviderV2.packageName(info.package) ?? "unknown"}`,
     provider: ProviderID.make(info.providerID),
+    providerMetadataKey: optionKey,
     protocol: "ai-sdk",
     endpoint: Endpoint.path("/", { baseURL: "https://ai-sdk.local" }),
     auth: Auth.none,
@@ -416,7 +418,7 @@ function assistantPart(part: ContentPart): AssistantContent {
     case "media":
       return [{ type: "file", mediaType: part.mediaType, data: part.data, filename: part.filename }]
     case "reasoning":
-      return [{ type: "reasoning", text: part.text }]
+      return [{ type: "reasoning", text: part.text, providerOptions: providerOptions(part.providerMetadata) }]
     case "tool-call":
       return [
         {
@@ -425,6 +427,7 @@ function assistantPart(part: ContentPart): AssistantContent {
           toolName: part.name,
           input: part.input,
           providerExecuted: part.providerExecuted,
+          providerOptions: providerOptions(part.providerMetadata),
         },
       ]
     case "tool-result":
@@ -440,6 +443,7 @@ function toolResultPart(part: ContentPart): ToolResultContent[] {
       toolCallId: part.id,
       toolName: part.name,
       output: toolOutput(part.result),
+      providerOptions: providerOptions(part.providerMetadata),
     },
   ]
 }
@@ -624,8 +628,8 @@ function usage(input: Extract<LanguageModelV3StreamPart, { type: "finish" }>["us
   return Object.values(output).some((value) => value !== undefined) ? output : undefined
 }
 
-function finishReason(value: unknown): FinishReason {
-  return Schema.is(FinishReason)(value) ? value : "unknown"
+function finishReason(value: LanguageModelV3FinishReason): FinishReason {
+  return value.unified === "other" ? "unknown" : value.unified
 }
 
 function providerMetadata(value: unknown) {

+ 40 - 6
packages/core/src/config.ts

@@ -119,7 +119,17 @@ export class Directory extends Schema.Class<Directory>("Config.Directory")({
   path: AbsolutePath,
 }) {}
 
-export type Entry = Document | Directory
+export class AgentsDirectory extends Schema.Class<AgentsDirectory>("Config.AgentsDirectory")({
+  type: Schema.Literal("agents"),
+  path: AbsolutePath,
+}) {}
+
+export class ClaudeDirectory extends Schema.Class<ClaudeDirectory>("Config.ClaudeDirectory")({
+  type: Schema.Literal("claude"),
+  path: AbsolutePath,
+}) {}
+
+export type Entry = Document | Directory | AgentsDirectory | ClaudeDirectory
 
 export function latest<K extends keyof Info>(entries: readonly Entry[], key: K): Info[K] | undefined {
   return entries
@@ -176,16 +186,37 @@ const layer = Layer.effect(
 
     const discover = Effect.fn("Config.discover")(function* () {
       const globalDirectory = AbsolutePath.make(global.config)
+      const globalAgentsDirectory = AbsolutePath.make(path.join(global.home, ".agents"))
+      const globalClaudeDirectory = AbsolutePath.make(path.join(global.home, ".claude"))
       const locationIsGlobal = path.resolve(location.directory) === path.resolve(global.config)
       const discovered = locationIsGlobal
         ? []
         : yield* fs
             .up({
-              targets: [".opencode", ...names.toReversed()],
+              targets: [".opencode", ".claude", ".agents", ...names.toReversed()],
               start: location.directory,
               stop: location.project.directory,
             })
-            .pipe(Effect.orDie)
+          .pipe(Effect.orDie)
+
+      // We load certain files from a few other folders in the ecosystem
+      const claude = [
+        ...((yield* fs.isDir(globalClaudeDirectory))
+          ? [new ClaudeDirectory({ type: "claude", path: globalClaudeDirectory })]
+          : []),
+        ...discovered
+          .filter((item) => path.basename(item) === ".claude")
+          .map((directory) => new ClaudeDirectory({ type: "claude", path: AbsolutePath.make(directory) })),
+      ]
+      const agents = [
+        ...((yield* fs.isDir(globalAgentsDirectory))
+          ? [new AgentsDirectory({ type: "agents", path: globalAgentsDirectory })]
+          : []),
+        ...discovered
+          .filter((item) => path.basename(item) === ".agents")
+          .map((directory) => new AgentsDirectory({ type: "agents", path: AbsolutePath.make(directory) })),
+      ]
+
       const directories = [
         globalDirectory,
         ...discovered
@@ -193,15 +224,18 @@ const layer = Layer.effect(
           .toReversed()
           .map((directory) => AbsolutePath.make(directory)),
       ]
-      const directPaths = discovered.filter((item) => path.basename(item) !== ".opencode").toReversed()
+      const directPaths = discovered
+        .filter((item) => ![".agents", ".claude", ".opencode"].includes(path.basename(item)))
+        .toReversed()
       const direct = yield* Effect.forEach(directPaths, loadFile).pipe(
         Effect.orDie,
         Effect.map((configs) => configs.filter((config): config is Document => config !== undefined)),
       )
+
       const supplementary = yield* Effect.forEach(directories, loadDirectory).pipe(Effect.orDie)
       return {
-        entries: [...(supplementary[0] ?? []), ...direct, ...supplementary.slice(1).flat()],
-        directories,
+        entries: [...claude, ...agents, ...(supplementary[0] ?? []), ...direct, ...supplementary.slice(1).flat()],
+        directories: [...directories, ...claude.map((entry) => entry.path), ...agents.map((entry) => entry.path)],
         files: directPaths,
       }
     })

+ 1 - 0
packages/core/src/config/plugin/agent.ts

@@ -40,6 +40,7 @@ export const Plugin = define({
     const load = Effect.fn("ConfigAgentPlugin.load")(function* () {
       return yield* Effect.forEach(yield* config.entries(), (entry) => {
         if (entry.type === "document") return Effect.succeed([entry])
+        if (entry.type !== "directory") return Effect.succeed([])
         return Effect.gen(function* () {
           const files = yield* discover(fs, entry.path)
           return yield* Effect.forEach(files, (file) =>

+ 1 - 0
packages/core/src/config/plugin/command.ts

@@ -19,6 +19,7 @@ export const Plugin = define({
     const load = Effect.fn("ConfigCommandPlugin.load")(function* () {
       return yield* Effect.forEach(yield* config.entries(), (entry) => {
         if (entry.type === "document") return Effect.succeed([{ commands: entry.info.commands }])
+        if (entry.type !== "directory") return Effect.succeed([])
         return loadDirectory(fs, entry.path).pipe(
           Effect.map((commands) => [
             { commands: Object.fromEntries(commands.map((command) => [command.name, command.info])) },

+ 3 - 2
packages/core/src/config/plugin/provider.ts

@@ -1,6 +1,7 @@
 export * as ConfigProviderPlugin from "./provider"
 
 import { define } from "@opencode-ai/plugin/v2/effect/plugin"
+import { Money } from "@opencode-ai/schema/money"
 import { Effect, Stream } from "effect"
 import { Config } from "../../config"
 import { ModelV2 } from "../../model"
@@ -91,8 +92,8 @@ export const Plugin = define({
                   input: cost.input,
                   output: cost.output,
                   cache: {
-                    read: cost.cache?.read ?? 0,
-                    write: cost.cache?.write ?? 0,
+                    read: cost.cache?.read ?? Money.USDPerMillionTokens.zero,
+                    write: cost.cache?.write ?? Money.USDPerMillionTokens.zero,
                   },
                 }))
               }

+ 10 - 0
packages/core/src/config/plugin/skill.ts

@@ -17,8 +17,18 @@ export const Plugin = define({
     const location = yield* Location.Service
     const loaded = { entries: yield* config.entries() }
     yield* ctx.skill.transform((draft) => {
+      const claude = loaded.entries.flatMap((entry) => (entry.type === "claude" ? [entry.path] : []))
+      const agents = loaded.entries.flatMap((entry) => (entry.type === "agents" ? [entry.path] : []))
       const directories = loaded.entries.flatMap((entry) => (entry.type === "directory" ? [entry.path] : []))
       const items = loaded.entries.flatMap((entry) => (entry.type === "document" ? (entry.info.skills ?? []) : []))
+      for (const directory of [...claude, ...agents]) {
+        draft.source(
+          SkillV2.DirectorySource.make({
+            type: "directory",
+            path: AbsolutePath.make(path.join(directory, "skills")),
+          }),
+        )
+      }
       for (const directory of directories) {
         draft.source(
           SkillV2.DirectorySource.make({ type: "directory", path: AbsolutePath.make(path.join(directory, "skill")) }),

+ 5 - 4
packages/core/src/config/provider.ts

@@ -1,6 +1,7 @@
 export * as ConfigProvider from "./provider"
 
 import { Schema } from "effect"
+import { Money } from "@opencode-ai/schema/money"
 import { ModelV2 } from "../model"
 
 const JsonRecord = Schema.Record(Schema.String, Schema.Json)
@@ -17,8 +18,8 @@ export class Request extends Schema.Class<Request>("ConfigV2.Provider.Request")(
 }) {}
 
 class Cache extends Schema.Class<Cache>("ConfigV2.Model.Cost.Cache")({
-  read: Schema.Finite.pipe(Schema.optional),
-  write: Schema.Finite.pipe(Schema.optional),
+  read: Money.USDPerMillionTokens.pipe(Schema.optional),
+  write: Money.USDPerMillionTokens.pipe(Schema.optional),
 }) {}
 
 class Cost extends Schema.Class<Cost>("ConfigV2.Model.Cost")({
@@ -26,8 +27,8 @@ class Cost extends Schema.Class<Cost>("ConfigV2.Model.Cost")({
     type: Schema.Literal("context"),
     size: Schema.Int,
   }).pipe(Schema.optional),
-  input: Schema.Finite,
-  output: Schema.Finite,
+  input: Money.USDPerMillionTokens,
+  output: Money.USDPerMillionTokens,
   cache: Cache.pipe(Schema.optional),
 }) {}
 

+ 1 - 0
packages/core/src/database/migration.gen.ts

@@ -48,5 +48,6 @@ export const migrations = (
     import("./migration/20260705180000_rename_instructions"),
     import("./migration/20260706223930_add-session-fork"),
     import("./migration/20260707010146_durable_session_inbox"),
+    import("./migration/20260707120000_migrate_prelaunch_v2_state"),
   ])
 ).map((module) => module.default) satisfies DatabaseMigration.Migration[]

+ 227 - 0
packages/core/src/database/migration/20260707120000_migrate_prelaunch_v2_state.ts

@@ -0,0 +1,227 @@
+import { sql } from "drizzle-orm"
+import { Effect, Schema } from "effect"
+import type { DatabaseMigration } from "../migration"
+
+const decodeJson = Schema.decodeUnknownSync(Schema.UnknownFromJsonString)
+const isObject = Schema.is(Schema.Record(Schema.String, Schema.Unknown))
+
+export default {
+  id: "20260707120000_migrate_prelaunch_v2_state",
+  up(tx) {
+    return Effect.gen(function* () {
+      yield* tx.run(
+        sql`DELETE FROM session_message WHERE type = 'compaction' AND json_extract(data, '$.status') = 'queued'`,
+      )
+      const messages = yield* tx.all<{ id: string; type: string; data: string }>(
+        sql`SELECT id, type, data FROM session_message WHERE type IN ('skill', 'shell', 'assistant', 'compaction', 'synthetic')`,
+      )
+      for (const row of messages) {
+        const data = object(decodeJson(row.data))
+        yield* tx.run(
+          sql`UPDATE session_message SET data = ${JSON.stringify(messageData(row.type, data))} WHERE id = ${row.id}`,
+        )
+      }
+
+      yield* tx.run(sql`DELETE FROM event WHERE type = 'session.compaction.delta.1'`)
+      const events = yield* tx.all<{ id: string; aggregateID: string; seq: number; type: string; data: string }>(sql`
+        SELECT id, aggregate_id as aggregateID, seq, type, data
+        FROM event
+        WHERE type IN (
+          'session.skill.activated.1',
+          'session.skill.activated.2',
+          'session.compaction.started.1',
+          'session.compaction.started.2',
+          'session.compaction.ended.1',
+          'session.compaction.failed.1',
+          'session.compaction.failed.2',
+          'session.revert.staged.1',
+          'session.revert.staged.2'
+        )
+        ORDER BY aggregate_id, seq
+      `)
+      const compactionReasons = new Map<string, "auto" | "manual">()
+      for (const row of events) {
+        const data = object(decodeJson(row.data))
+        if (row.type.startsWith("session.compaction.ended.")) {
+          compactionReasons.delete(row.aggregateID)
+          continue
+        }
+        const event = eventData(row.type, data, compactionReasons.get(row.aggregateID))
+        if (row.type.startsWith("session.compaction.started."))
+          compactionReasons.set(row.aggregateID, event.data.reason === "auto" ? "auto" : "manual")
+        if (row.type.startsWith("session.compaction.failed.")) compactionReasons.delete(row.aggregateID)
+        yield* tx.run(
+          sql`UPDATE event SET type = ${event.type}, data = ${JSON.stringify(event.data)} WHERE id = ${row.id}`,
+        )
+      }
+    })
+  },
+} satisfies DatabaseMigration.Migration
+
+function messageData(type: string, data: Record<string, unknown>) {
+  if (type === "skill")
+    return defined({
+      metadata: data.metadata,
+      time: data.time,
+      skill: data.skill ?? data.id ?? data.name,
+      name: data.name,
+      text: data.text,
+    })
+  if (type === "shell") {
+    const shell = object(data.shell)
+    return defined({
+      metadata: data.metadata,
+      time: data.time,
+      shellID: data.shellID ?? shell.id,
+      command: data.command ?? shell.command,
+      status: data.status ?? shell.status,
+      exit: data.exit ?? shell.exit,
+      output: data.output,
+    })
+  }
+  if (type === "assistant")
+    return defined({
+      metadata: data.metadata,
+      time: data.time,
+      agent: data.agent,
+      model: data.model,
+      content: Array.isArray(data.content) ? data.content.map(assistantContent) : data.content,
+      snapshot: data.snapshot,
+      finish: data.finish,
+      cost: data.cost,
+      tokens: data.tokens,
+      error: data.error,
+      retry: data.retry,
+    })
+  if (type === "compaction") {
+    if (data.status === "failed")
+      return defined({
+        metadata: data.metadata,
+        time: data.time,
+        status: data.status,
+        reason: data.reason,
+        error: data.error ?? genericCompactionError,
+      })
+    return defined({
+      metadata: data.metadata,
+      time: data.time,
+      status: data.status,
+      reason: data.reason,
+      summary: data.summary,
+      recent: data.recent,
+    })
+  }
+  if (type === "synthetic")
+    return defined({ metadata: data.metadata, time: data.time, text: data.text, description: data.description })
+  const { sessionID: _, ...current } = data
+  return current
+}
+
+function assistantContent(value: unknown) {
+  const content = object(value)
+  if (content.type === "text") return defined({ type: content.type, text: content.text })
+  if (content.type === "reasoning")
+    return defined({ type: content.type, text: content.text, state: content.state, time: content.time })
+  if (content.type !== "tool") return content
+  return defined({
+    type: content.type,
+    id: content.id,
+    name: content.name,
+    executed: content.executed,
+    providerState: content.providerState,
+    providerResultState: content.providerResultState,
+    state: toolState(content.state),
+    time: content.time,
+  })
+}
+
+function toolState(value: unknown) {
+  const state = object(value)
+  if (state.status === "pending" || state.status === "streaming")
+    return defined({ status: "streaming", input: state.input })
+  if (state.status === "running")
+    return defined({ status: state.status, input: state.input, structured: state.structured, content: state.content })
+  if (state.status === "completed")
+    return defined({
+      status: state.status,
+      input: state.input,
+      structured: state.structured,
+      content: state.content,
+      result: state.result,
+    })
+  if (state.status === "error")
+    return defined({
+      status: state.status,
+      input: state.input,
+      structured: state.structured,
+      content: state.content,
+      error: state.error,
+      result: state.result,
+    })
+  return state
+}
+
+function eventData(type: string, data: Record<string, unknown>, compactionReason?: "auto" | "manual") {
+  if (type.startsWith("session.skill.activated."))
+    return {
+      type: "session.skill.activated.1",
+      data: defined({ sessionID: data.sessionID, id: data.id ?? data.name, name: data.name, text: data.text }),
+    }
+  if (type.startsWith("session.compaction.started."))
+    return {
+      type: "session.compaction.started.1",
+      data: defined({
+        sessionID: data.sessionID,
+        reason: data.reason,
+        recent: data.recent ?? "",
+        inputID: data.inputID,
+      }),
+    }
+  if (type.startsWith("session.compaction.failed."))
+    return {
+      type: "session.compaction.failed.1",
+      data: defined({
+        sessionID: data.sessionID,
+        reason: data.reason ?? compactionReason ?? "manual",
+        error: data.error ?? genericCompactionError,
+        inputID: data.inputID,
+      }),
+    }
+  const revert = object(data.revert)
+  return {
+    type: "session.revert.staged.1",
+    data: defined({
+      sessionID: data.sessionID,
+      revert: defined({
+        messageID: revert.messageID,
+        partID: revert.partID,
+        snapshot: revert.snapshot,
+        files: Array.isArray(revert.files)
+          ? revert.files.map((value) => {
+              const file = object(value)
+              return defined({
+                file: file.file ?? file.path,
+                patch: file.patch,
+                additions: file.additions,
+                deletions: file.deletions,
+                status: file.status,
+              })
+            })
+          : undefined,
+      }),
+    }),
+  }
+}
+
+const genericCompactionError = {
+  type: "compaction.failed",
+  message: "Compaction failed before recording an error",
+}
+
+function object(value: unknown): Record<string, unknown> {
+  return isObject(value) ? value : {}
+}
+
+function defined(value: Record<string, unknown>) {
+  return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== undefined))
+}

+ 1 - 1
packages/core/src/file-mutation.ts

@@ -200,6 +200,6 @@ export const node = makeLocationNode({ service: Service, layer, deps: [FSUtil.no
 // TODO: Publish watcher/file-edit events after V2 watcher integration exists.
 // TODO: Add snapshots / undo after V2 snapshot design exists.
 // TODO: Notify LSP and collect diagnostics after V2 LSP runtime exists.
-// TODO: Design multi-file transactions / rollback if apply_patch needs atomic edits.
+// TODO: Design multi-file transactions / rollback if patch needs atomic edits.
 // Until then, edits are sequential and report partial application.
 // TODO: Define crash recovery and idempotency for side effects between Tool.Called and durable settlement.

+ 2 - 2
packages/core/src/file.ts

@@ -1,6 +1,6 @@
 export * as File from "./file"
 
-import { Revert } from "@opencode-ai/schema/revert"
+import { FileDiff } from "@opencode-ai/schema/file-diff"
 
-export const Diff = Revert.FileDiff
+export const Diff = FileDiff.Info
 export type Diff = typeof Diff.Type

+ 1 - 1
packages/core/src/filesystem/location-watcher.ts

@@ -46,7 +46,7 @@ const layer = Layer.effect(
         .flatMap((item) => item.info.watcher?.ignore ?? [])
       const home = path.resolve(location.directory) === path.resolve(os.homedir())
 
-      if (!home) {
+      if (!home && location.vcs) {
         yield* watcher
           .subscribe({
             path: location.directory,

+ 1 - 1
packages/core/src/git.ts

@@ -606,7 +606,7 @@ const layer = Layer.effect(
                 file,
               ])).text
           return {
-            path: file,
+            file,
             status,
             additions: binary ? 0 : Number(stats[0] ?? 0),
             deletions: binary ? 0 : Number(stats[1] ?? 0),

+ 59 - 23
packages/core/src/integration.ts

@@ -203,6 +203,7 @@ type AttemptTime = { created: number; expires: number }
 type PendingAttempt = {
   status: "pending"
   completing: boolean
+  persisting: boolean
   authorization: OAuthAuthorization
   integrationID: ID
   methodID: MethodID
@@ -320,27 +321,61 @@ const layer = Layer.effect(
     }
 
     const settle = Effect.fnUntraced(function* (attemptID: AttemptID, exit: Exit.Exit<Credential.OAuth, unknown>) {
-      const now = yield* Clock.currentTimeMillis
-      const result = yield* SynchronizedRef.modify(attempts, (current) => {
-        const attempt = current.get(attemptID)
-        if (!attempt || attempt.status !== "pending") return [undefined, current]
-        const terminal: TerminalAttempt = Exit.isSuccess(exit)
-          ? { status: "complete", time: attempt.time, removeAt: now + terminalRetention }
-          : { status: "failed", message: message(exit.cause), time: attempt.time, removeAt: now + terminalRetention }
-        return [attempt, new Map(current).set(attemptID, terminal)]
-      })
-      if (!result) return
-      if (Exit.isSuccess(exit)) {
-        const implementation = state.get().integrations.get(result.integrationID)?.implementations.get(result.methodID)
-        yield* credentials.create({
-          integrationID: result.integrationID,
-          label: result.label ?? implementation?.label?.(exit.value),
-          value: exit.value,
-        })
-        yield* events.publish(Event.ConnectionUpdated, { integrationID: result.integrationID })
-        yield* events.publish(Event.Updated, {})
-      }
-      yield* close(result.scope)
+      return yield* Effect.uninterruptible(
+        Effect.gen(function* () {
+          const now = yield* Clock.currentTimeMillis
+          const attempt = yield* SynchronizedRef.modify(attempts, (current) => {
+            const match = current.get(attemptID)
+            if (!match || match.status !== "pending" || match.persisting) return [undefined, current]
+            const next = Exit.isSuccess(exit)
+              ? { ...match, persisting: true }
+              : {
+                  status: "failed" as const,
+                  message: message(exit.cause),
+                  time: match.time,
+                  removeAt: now + terminalRetention,
+                }
+            return [match, new Map(current).set(attemptID, next)]
+          })
+          if (!attempt) return
+          if (Exit.isFailure(exit)) {
+            yield* close(attempt.scope)
+            return
+          }
+
+          yield* Effect.gen(function* () {
+            const implementation = state
+              .get()
+              .integrations.get(attempt.integrationID)
+              ?.implementations.get(attempt.methodID)
+            const persistence = yield* Effect.sync(() => attempt.label ?? implementation?.label?.(exit.value)).pipe(
+              Effect.flatMap((label) =>
+                credentials.create({
+                  integrationID: attempt.integrationID,
+                  label,
+                  value: exit.value,
+                }),
+              ),
+              Effect.asVoid,
+              Effect.exit,
+            )
+            const settledAt = yield* Clock.currentTimeMillis
+            const terminal: TerminalAttempt = Exit.isSuccess(persistence)
+              ? { status: "complete", time: attempt.time, removeAt: settledAt + terminalRetention }
+              : {
+                  status: "failed",
+                message: message(persistence.cause),
+                time: attempt.time,
+                removeAt: settledAt + terminalRetention,
+              }
+            // Persisting attempts cannot be cancelled, expired, or claimed again.
+            yield* SynchronizedRef.update(attempts, (current) => new Map(current).set(attemptID, terminal))
+            if (Exit.isFailure(persistence)) yield* Effect.failCause(persistence.cause)
+            yield* events.publish(Event.ConnectionUpdated, { integrationID: attempt.integrationID })
+            yield* events.publish(Event.Updated, {})
+          }).pipe(Effect.ensuring(close(attempt.scope)))
+        }),
+      )
     })
 
     const scrub = Effect.fnUntraced(function* () {
@@ -349,7 +384,7 @@ const layer = Layer.effect(
         const next = new Map(current)
         const scopes: Scope.Closeable[] = []
         for (const [id, attempt] of current) {
-          if (attempt.status === "pending" && attempt.time.expires <= now) {
+          if (attempt.status === "pending" && !attempt.persisting && attempt.time.expires <= now) {
             scopes.push(attempt.scope)
             next.set(id, { status: "expired", time: attempt.time, removeAt: now + terminalRetention })
             continue
@@ -432,6 +467,7 @@ const layer = Layer.effect(
             new Map(current).set(id, {
               status: "pending",
               completing: authorization.mode === "auto",
+              persisting: false,
               authorization,
               integrationID: input.integrationID,
               methodID: input.methodID,
@@ -506,7 +542,7 @@ const layer = Layer.effect(
         cancel: Effect.fn("Integration.attempt.cancel")(function* (attemptID) {
           const attempt = yield* SynchronizedRef.modify(attempts, (current) => {
             const match = current.get(attemptID)
-            if (!match || match.status !== "pending") return [undefined, current]
+            if (!match || match.status !== "pending" || match.persisting) return [undefined, current]
             const next = new Map(current)
             next.delete(attemptID)
             return [match, next]

+ 38 - 71
packages/core/src/location-services.ts

@@ -5,29 +5,22 @@ import { Catalog } from "./catalog"
 import { CommandV2 } from "./command"
 import { Config } from "./config"
 import { LayerNode } from "./effect/layer-node"
-import { makeLocationNode, Node } from "./effect/app-node"
-import { httpClient } from "./effect/app-node-platform"
+import { Node } from "./effect/app-node"
 import { EventV2 } from "./event"
 import { FileMutation } from "./file-mutation"
 import { FileSystem } from "./filesystem"
 import { FileSystemSearch } from "./filesystem/search"
-import { FSUtil } from "./fs-util"
 import { Generate } from "./generate"
 import { Form } from "./form"
-import { Global } from "./global"
-import { LocationWatcher } from "./filesystem/location-watcher"
 import { Image } from "./image"
+import { LocationWatcher } from "./filesystem/location-watcher"
 import { Integration } from "./integration"
 import { Location } from "./location"
 import { LocationMutation } from "./location-mutation"
 import { LocationServiceMap } from "./location-service-map"
 import { MCP } from "./mcp/index"
-import { ModelsDev } from "./models-dev"
-import { Npm } from "./npm"
 import { PermissionV2 } from "./permission"
 import { PluginV2 } from "./plugin"
-import { PluginRuntime } from "./plugin/runtime"
-import { SdkPlugins } from "./plugin/sdk"
 import { PluginSupervisor } from "./plugin/supervisor"
 import { ProjectCopy } from "./project/copy"
 import { Pty } from "./pty"
@@ -35,7 +28,6 @@ import { QuestionV2 } from "./question"
 import { Shell } from "./shell"
 import { Reference } from "./reference"
 import { ReferenceGuidance } from "./reference/guidance"
-import { Ripgrep } from "./ripgrep"
 import { SessionRunnerLLM } from "./session/runner/llm"
 import { SessionRunnerModel } from "./session/runner/model"
 import { SessionCompaction } from "./session/compaction"
@@ -51,49 +43,11 @@ import { SessionInstructions } from "./session/instructions"
 import { McpTool } from "./tool/mcp"
 import { ReadToolFileSystem } from "./tool/read-filesystem"
 import { ToolRegistry } from "./tool/registry"
-import { WebSearchTool } from "./tool/websearch"
 import { ToolOutputStore } from "./tool-output-store"
 import { Vcs } from "./vcs"
 
 export { LocationServiceMap } from "./location-service-map"
 
-const pluginSupervisorNode = makeLocationNode({
-  service: PluginSupervisor.Service,
-  layer: PluginSupervisor.layer,
-  deps: [
-    PluginV2.node,
-    SdkPlugins.node,
-    AgentV2.node,
-    Catalog.node,
-    CommandV2.node,
-    Config.node,
-    EventV2.node,
-    FileMutation.node,
-    FileSystem.node,
-    FSUtil.node,
-    Global.node,
-    httpClient,
-    Image.node,
-    Integration.node,
-    Location.node,
-    LocationMutation.node,
-    ModelsDev.node,
-    Npm.node,
-    PermissionV2.node,
-    PluginRuntime.node,
-    Form.node,
-    ReadToolFileSystem.node,
-    Reference.node,
-    Ripgrep.node,
-    SessionInstructions.node,
-    SessionTodo.node,
-    Shell.node,
-    SkillV2.node,
-    ToolRegistry.toolsNode,
-    WebSearchTool.configNode,
-  ],
-})
-
 const locationServiceNodes = [
   Location.node,
   Config.node,
@@ -104,7 +58,7 @@ const locationServiceNodes = [
   Catalog.node,
   AISDK.node,
   PluginV2.node,
-  pluginSupervisorNode,
+  PluginSupervisor.node,
   ProjectCopy.node,
   ProjectCopy.refreshNode,
   FileSystemSearch.node,
@@ -150,31 +104,44 @@ export type LocationError = LayerNode.Error<typeof locationServices>
 export function buildLocationServiceMap(
   replacements: LayerNode.Replacements = [],
 ): Layer.Layer<LocationServiceMap.Service> {
+  // Structural Equal is own-key-set sensitive, so `{ directory }` (schema-decoded
+  // payloads omit optional keys) and `{ directory, workspaceID: undefined }` are
+  // different RcMap keys. The RcMap caches by the raw key before the build
+  // callback runs, so canonicalize at the map boundary to the key-present shape.
+  const canonical = (ref: Location.Ref) => Location.Ref.make({ directory: ref.directory, workspaceID: ref.workspaceID })
   return Layer.effect(
     LocationServiceMap.Service,
-    LayerMap.make(
-      (ref: Location.Ref) => {
-        const startedAt = performance.now()
-        const allReplacements = replacements.concat([[Location.node, Location.boundNode(ref)]])
-        // Apply replacements during hoist, not afterward: replacements can
-        // introduce new tagged dependencies (Location.boundNode depends on
-        // Project), and the hoist walk is the only pass that can still slice
-        // those back out.
-        const location = LayerNode.hoist(locationServices, Node.tags.values.global, allReplacements)
+    Effect.map(
+      LayerMap.make(
+        (ref: Location.Ref) => {
+          const startedAt = performance.now()
+          const allReplacements = replacements.concat([[Location.node, Location.boundNode(ref)]])
+          // Apply replacements during hoist, not afterward: replacements can
+          // introduce new tagged dependencies (Location.boundNode depends on
+          // Project), and the hoist walk is the only pass that can still slice
+          // those back out.
+          const location = LayerNode.hoist(locationServices, Node.tags.values.global, allReplacements)
 
-        return LayerNode.compile(location.node).pipe(
-          Layer.fresh,
-          Layer.tap(() =>
-            Effect.logInfo("location services booted", {
-              directory: ref.directory,
-              workspaceID: ref.workspaceID,
-              durationMs: Math.round(performance.now() - startedAt),
-            }),
-          ),
-          Layer.provide(LayerNode.compile(location.hoisted)),
-        )
-      },
-      { idleTimeToLive: "60 minutes" },
+          return LayerNode.compile(location.node).pipe(
+            Layer.fresh,
+            Layer.tap(() =>
+              Effect.logInfo("location services booted", {
+                directory: ref.directory,
+                workspaceID: ref.workspaceID,
+                durationMs: Math.round(performance.now() - startedAt),
+              }),
+            ),
+            Layer.provide(LayerNode.compile(location.hoisted)),
+          )
+        },
+        { idleTimeToLive: "60 minutes" },
+      ),
+      (inner) => ({
+        ...inner,
+        get: (ref: Location.Ref) => inner.get(canonical(ref)),
+        contextEffect: (ref: Location.Ref) => inner.contextEffect(canonical(ref)),
+        invalidate: (ref: Location.Ref) => inner.invalidate(canonical(ref)),
+      }),
     ),
   )
 }

+ 118 - 16
packages/core/src/mcp/client.ts

@@ -3,7 +3,7 @@ export * as MCPClient from "./client"
 import path from "node:path"
 import { execFile } from "node:child_process"
 import { pathToFileURL } from "node:url"
-import { Client, type ClientOptions } from "@modelcontextprotocol/sdk/client/index.js"
+import { Client } from "@modelcontextprotocol/sdk/client/index.js"
 import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
 import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
 import { UnauthorizedError, type OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"
@@ -21,6 +21,7 @@ import {
   ListToolsResultSchema,
   PromptListChangedNotificationSchema,
   PromptSchema,
+  ResourceListChangedNotificationSchema,
   type LoggingMessageNotification,
   LoggingMessageNotificationSchema,
   ToolListChangedNotificationSchema,
@@ -68,11 +69,13 @@ export interface ToolDefinition {
 export interface PromptDefinition {
   readonly name: string
   readonly description: string | undefined
-  readonly arguments: ReadonlyArray<{
-    readonly name: string
-    readonly description: string | undefined
-    readonly required: boolean | undefined
-  }> | undefined
+  readonly arguments:
+    | ReadonlyArray<{
+        readonly name: string
+        readonly description: string | undefined
+        readonly required: boolean | undefined
+      }>
+    | undefined
 }
 
 export interface PromptMessage {
@@ -84,6 +87,28 @@ export interface PromptResult {
   readonly messages: ReadonlyArray<PromptMessage>
 }
 
+export interface ResourceDefinition {
+  readonly name: string
+  readonly uri: string
+  readonly description: string | undefined
+  readonly mimeType: string | undefined
+}
+
+export interface ResourceTemplateDefinition {
+  readonly name: string
+  readonly uriTemplate: string
+  readonly description: string | undefined
+  readonly mimeType: string | undefined
+}
+
+export type ResourceContentPart =
+  | { readonly type: "text"; readonly uri: string; readonly text: string; readonly mimeType: string | undefined }
+  | { readonly type: "blob"; readonly uri: string; readonly blob: string; readonly mimeType: string | undefined }
+
+export interface ReadResourceResult {
+  readonly contents: ReadonlyArray<ResourceContentPart>
+}
+
 export type CallToolContent =
   | { readonly type: "text"; readonly text: string }
   | { readonly type: "media"; readonly data: string; readonly mimeType: string }
@@ -124,6 +149,12 @@ export interface Connection {
   readonly tools: () => Effect.Effect<ToolDefinition[], Error>
   /** Lists the server's prompts; returns [] when the server doesn't advertise prompt support, fails on a transport error. */
   readonly prompts: () => Effect.Effect<PromptDefinition[], Error>
+  /** Lists the server's resources; returns [] when the server doesn't advertise resource support. */
+  readonly resources: () => Effect.Effect<ResourceDefinition[], Error>
+  /** Lists the server's resource templates; returns [] when the server doesn't advertise resource support. */
+  readonly resourceTemplates: () => Effect.Effect<ResourceTemplateDefinition[], Error>
+  /** Reads one resource; returns undefined when the server doesn't advertise resource support. */
+  readonly readResource: (input: { readonly uri: string }) => Effect.Effect<ReadResourceResult | undefined, Error>
   /** Invokes a prompt on the server. Interruption aborts the in-flight request. */
   readonly prompt: (input: {
     readonly name: string
@@ -141,6 +172,8 @@ export interface Connection {
   readonly onToolsChanged: (callback: () => void) => void
   /** Registers a callback fired when the server announces its prompt list changed; no-op if unsupported. */
   readonly onPromptsChanged: (callback: () => void) => void
+  /** Registers a callback fired when the server announces its resource catalog changed. */
+  readonly onResourcesChanged: (callback: () => void) => void
 }
 
 /** Connects an MCP server; closing the calling scope tears down the transport and any spawned process. */
@@ -168,7 +201,8 @@ export const connect = Effect.fnUntraced(function* (
         },
       })
     }
-    if (!URL.canParse(config.url)) return yield* new ConnectError({ server, message: `Invalid MCP URL for "${server}"` })
+    if (!URL.canParse(config.url))
+      return yield* new ConnectError({ server, message: `Invalid MCP URL for "${server}"` })
     return new StreamableHTTPClientTransport(new URL(config.url), {
       requestInit: config.headers ? { headers: config.headers } : undefined,
       authProvider,
@@ -202,10 +236,7 @@ export const connect = Effect.fnUntraced(function* (
   }).pipe(Effect.exit)
   if (Exit.isSuccess(exit)) {
     yield* Effect.addFinalizer(() =>
-      cleanupStdioDescendants(transport).pipe(
-        Effect.andThen(Effect.promise(() => client.close())),
-        Effect.ignore,
-      ),
+      cleanupStdioDescendants(transport).pipe(Effect.andThen(Effect.promise(() => client.close())), Effect.ignore),
     )
     const catalogTimeout = config.timeout?.catalog ?? DEFAULT_CATALOG_TIMEOUT
     const executionTimeout = config.timeout?.execution ?? DEFAULT_EXECUTION_TIMEOUT
@@ -257,7 +288,9 @@ export const connect = Effect.fnUntraced(function* (
               ),
             catch: (error) => (error instanceof Error ? error : new Error(String(error))),
           }).pipe(
-            Effect.tapError((error) => Effect.logWarning("failed to list MCP prompts", { server, error: error.message })),
+            Effect.tapError((error) =>
+              Effect.logWarning("failed to list MCP prompts", { server, error: error.message }),
+            ),
           )
           return prompts.map((prompt) => ({
             name: prompt.name,
@@ -269,6 +302,74 @@ export const connect = Effect.fnUntraced(function* (
             })),
           }))
         }),
+      resources: () =>
+        Effect.gen(function* () {
+          if (!client.getServerCapabilities()?.resources) return []
+          const resources = yield* Effect.tryPromise({
+            try: () =>
+              paginate(
+                (cursor) =>
+                  client.listResources(cursor === undefined ? undefined : { cursor }, { timeout: catalogTimeout }),
+                (result) => result.resources,
+              ),
+            catch: (error) => (error instanceof Error ? error : new Error(String(error))),
+          }).pipe(
+            Effect.tapError((error) =>
+              Effect.logWarning("failed to list MCP resources", { server, error: error.message }),
+            ),
+          )
+          return resources.map((resource) => ({
+            name: resource.name,
+            uri: resource.uri,
+            description: resource.description,
+            mimeType: resource.mimeType,
+          }))
+        }),
+      resourceTemplates: () =>
+        Effect.gen(function* () {
+          if (!client.getServerCapabilities()?.resources) return []
+          const templates = yield* Effect.tryPromise({
+            try: () =>
+              paginate(
+                (cursor) =>
+                  client.listResourceTemplates(cursor === undefined ? undefined : { cursor }, {
+                    timeout: catalogTimeout,
+                  }),
+                (result) => result.resourceTemplates,
+              ),
+            catch: (error) => (error instanceof Error ? error : new Error(String(error))),
+          }).pipe(
+            Effect.tapError((error) =>
+              Effect.logWarning("failed to list MCP resource templates", { server, error: error.message }),
+            ),
+          )
+          return templates.map((template) => ({
+            name: template.name,
+            uriTemplate: template.uriTemplate,
+            description: template.description,
+            mimeType: template.mimeType,
+          }))
+        }),
+      readResource: (input) =>
+        Effect.gen(function* () {
+          if (!client.getServerCapabilities()?.resources) return undefined
+          const result = yield* Effect.tryPromise({
+            try: (signal) => client.readResource({ uri: input.uri }, { signal, timeout: executionTimeout }),
+            catch: (error) => (error instanceof Error ? error : new Error(String(error))),
+          }).pipe(
+            Effect.tapError((error) =>
+              Effect.logWarning("failed to read MCP resource", { server, uri: input.uri, error: error.message }),
+            ),
+          )
+          return {
+            contents: result.contents.map(
+              (part): ResourceContentPart =>
+                "text" in part
+                  ? { type: "text", uri: part.uri, text: part.text, mimeType: part.mimeType }
+                  : { type: "blob", uri: part.uri, blob: part.blob, mimeType: part.mimeType },
+            ),
+          }
+        }),
       prompt: (input) =>
         Effect.tryPromise({
           try: (signal) =>
@@ -328,13 +429,14 @@ export const connect = Effect.fnUntraced(function* (
         if (!client.getServerCapabilities()?.prompts?.listChanged) return
         client.setNotificationHandler(PromptListChangedNotificationSchema, async () => callback())
       },
+      onResourcesChanged: (callback) => {
+        if (!client.getServerCapabilities()?.resources?.listChanged) return
+        client.setNotificationHandler(ResourceListChangedNotificationSchema, async () => callback())
+      },
     } satisfies Connection
   }
 
-  yield* cleanupStdioDescendants(transport).pipe(
-    Effect.andThen(Effect.promise(() => transport.close())),
-    Effect.ignore,
-  )
+  yield* cleanupStdioDescendants(transport).pipe(Effect.andThen(Effect.promise(() => transport.close())), Effect.ignore)
   const error = Cause.squash(exit.cause)
   if (error instanceof UnauthorizedError) return yield* new NeedsAuthError({ server })
   return yield* new ConnectError({ server, message: error instanceof Error ? error.message : String(error) })

+ 80 - 50
packages/core/src/mcp/index.ts

@@ -83,48 +83,16 @@ export class PromptResult extends Schema.Class<PromptResult>("MCP.PromptResult")
   messages: Schema.Array(PromptMessage),
 }) {}
 
-export class Resource extends Schema.Class<Resource>("MCP.Resource")({
-  server: ServerName,
-  name: Schema.String,
-  uri: Schema.String,
-  description: Schema.String.pipe(Schema.optional),
-  mimeType: Schema.String.pipe(Schema.optional),
-}) {}
-
-export class ResourceTemplate extends Schema.Class<ResourceTemplate>("MCP.ResourceTemplate")({
-  server: ServerName,
-  name: Schema.String,
-  uriTemplate: Schema.String,
-  description: Schema.String.pipe(Schema.optional),
-  mimeType: Schema.String.pipe(Schema.optional),
-}) {}
-
-export class ResourceCatalog extends Schema.Class<ResourceCatalog>("MCP.ResourceCatalog")({
-  resources: Schema.Array(Resource),
-  templates: Schema.Array(ResourceTemplate),
-}) {}
-
-export const ResourceContentPart = Schema.Union([
-  Schema.Struct({
-    type: Schema.Literal("text"),
-    uri: Schema.String,
-    text: Schema.String,
-    mimeType: Schema.String.pipe(Schema.optional),
-  }),
-  Schema.Struct({
-    type: Schema.Literal("blob"),
-    uri: Schema.String,
-    blob: Schema.String,
-    mimeType: Schema.String.pipe(Schema.optional),
-  }),
-]).pipe(Schema.toTaggedUnion("type"))
-export type ResourceContentPart = typeof ResourceContentPart.Type
-
-export class ResourceContent extends Schema.Class<ResourceContent>("MCP.ResourceContent")({
-  server: ServerName,
-  uri: Schema.String,
-  contents: Schema.Array(ResourceContentPart),
-}) {}
+export const Resource = Mcp.Resource
+export type Resource = Mcp.Resource
+export const ResourceTemplate = Mcp.ResourceTemplate
+export type ResourceTemplate = Mcp.ResourceTemplate
+export const ResourceCatalog = Mcp.ResourceCatalog
+export type ResourceCatalog = Mcp.ResourceCatalog
+export const ResourceContentPart = Mcp.ResourceContentPart
+export type ResourceContentPart = Mcp.ResourceContentPart
+export const ResourceContent = Mcp.ResourceContent
+export type ResourceContent = Mcp.ResourceContent
 
 export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("MCP.NotFoundError", {
   server: ServerName,
@@ -415,6 +383,24 @@ export const layer = Layer.effect(
         ),
       })
 
+    const toResource = (server: ServerName, def: MCPClient.ResourceDefinition) =>
+      Resource.make({
+        server,
+        name: def.name,
+        uri: def.uri,
+        description: def.description,
+        mimeType: def.mimeType,
+      })
+
+    const toResourceTemplate = (server: ServerName, def: MCPClient.ResourceTemplateDefinition) =>
+      ResourceTemplate.make({
+        server,
+        name: def.name,
+        uriTemplate: def.uriTemplate,
+        description: def.description,
+        mimeType: def.mimeType,
+      })
+
     const refreshTools = (name: ServerName, entry: ServerEntry, connection: MCPClient.Connection) =>
       connection.tools().pipe(
         Effect.map((defs) => {
@@ -443,6 +429,7 @@ export const layer = Layer.effect(
         entry.prompts = undefined
         entry.status = { status: "failed", error: "Connection closed" }
         fork(events.publish(McpEvent.ToolsChanged, { server: name }).pipe(Effect.ignore))
+        fork(events.publish(McpEvent.ResourcesChanged, { server: name }).pipe(Effect.ignore))
         fork(events.publish(Command.Event.Updated, {}).pipe(Effect.ignore))
         fork(events.publish(McpEvent.StatusChanged, { server: name }).pipe(Effect.ignore))
       })
@@ -458,6 +445,10 @@ export const layer = Layer.effect(
       connection.onPromptsChanged(() => {
         fork(refreshPrompts(name, entry, connection).pipe(Effect.ignore))
       })
+      connection.onResourcesChanged(() => {
+        if (entry.client !== connection) return
+        fork(events.publish(McpEvent.ResourcesChanged, { server: name }).pipe(Effect.ignore))
+      })
     }
 
     const serverLog = (server: ServerName, message: MCPClient.LogMessage) => {
@@ -501,6 +492,7 @@ export const layer = Layer.effect(
           // after the initial registration sweep and emits no list-changed notification would otherwise
           // stay invisible to the model.
           yield* events.publish(McpEvent.ToolsChanged, { server: name }).pipe(Effect.ignore)
+          yield* events.publish(McpEvent.ResourcesChanged, { server: name }).pipe(Effect.ignore)
           yield* events.publish(McpEvent.StatusChanged, { server: name }).pipe(Effect.ignore)
           fork(refreshPrompts(name, entry, result.value.connection).pipe(Effect.ignore))
           return
@@ -557,11 +549,6 @@ export const layer = Layer.effect(
       concurrency: "unbounded",
       discard: true,
     })
-    const gate = Effect.fnUntraced(function* (server: ServerName | string) {
-      const target = yield* requireServer(server)
-      yield* Deferred.await(target.entry.startup)
-    })
-
     return Service.of({
       servers: Effect.fn("MCP.servers")(function* () {
         const entries = Array.from(runtime).toSorted(([a], [b]) => a.localeCompare(b))
@@ -637,11 +624,54 @@ export const layer = Layer.effect(
       }),
       resourceCatalog: Effect.fn("MCP.resourceCatalog")(function* () {
         yield* whenAllReady
-        return new ResourceCatalog({ resources: [], templates: [] })
+        const catalogs = yield* Effect.forEach(
+          Array.from(runtime),
+          ([name, entry]) => {
+            if (!entry.client) return Effect.succeed({ resources: [], templates: [] })
+            return Effect.all(
+              {
+                resources: entry.client.resources().pipe(Effect.catch(() => Effect.succeed([]))),
+                templates: entry.client.resourceTemplates().pipe(Effect.catch(() => Effect.succeed([]))),
+              },
+              { concurrency: "unbounded" },
+            ).pipe(
+              Effect.map((catalog) => ({
+                resources: catalog.resources.map((def) => toResource(name, def)),
+                templates: catalog.templates.map((def) => toResourceTemplate(name, def)),
+              })),
+            )
+          },
+          { concurrency: "unbounded" },
+        )
+        return ResourceCatalog.make({
+          resources: catalogs
+            .flatMap((catalog) => catalog.resources)
+            .toSorted(
+              (a, b) => a.server.localeCompare(b.server) || a.name.localeCompare(b.name) || a.uri.localeCompare(b.uri),
+            ),
+          templates: catalogs
+            .flatMap((catalog) => catalog.templates)
+            .toSorted(
+              (a, b) =>
+                a.server.localeCompare(b.server) ||
+                a.name.localeCompare(b.name) ||
+                a.uriTemplate.localeCompare(b.uriTemplate),
+            ),
+        })
       }),
       readResource: Effect.fn("MCP.readResource")(function* (input) {
-        yield* gate(input.server)
-        return undefined
+        const target = yield* requireServer(input.server)
+        yield* Deferred.await(target.entry.startup)
+        if (!target.entry.client) return undefined
+        const result = yield* target.entry.client
+          .readResource({ uri: input.uri })
+          .pipe(Effect.catch(() => Effect.succeed(undefined)))
+        if (!result) return undefined
+        return ResourceContent.make({
+          server: target.name,
+          uri: input.uri,
+          contents: result.contents,
+        })
       }),
     })
   }),

+ 13 - 12
packages/core/src/models-dev.ts

@@ -2,6 +2,7 @@ import path from "path"
 import { Context, Duration, Effect, Layer, Option, Schedule, Schema } from "effect"
 import { HttpClient, HttpClientRequest } from "effect/unstable/http"
 import { ModelsDev } from "@opencode-ai/schema/models-dev"
+import { Money } from "@opencode-ai/schema/money"
 import { Global } from "./global"
 import { Flag } from "./flag/flag"
 import { Flock } from "./util/flock"
@@ -18,10 +19,10 @@ export type CatalogModelStatus = typeof CatalogModelStatus.Type
 const USER_AGENT = `opencode/${InstallationChannel}/${InstallationVersion}/${Flag.OPENCODE_CLIENT}`
 
 const CostTier = Schema.Struct({
-  input: Schema.Finite,
-  output: Schema.Finite,
-  cache_read: Schema.optional(Schema.Finite),
-  cache_write: Schema.optional(Schema.Finite),
+  input: Money.USDPerMillionTokens,
+  output: Money.USDPerMillionTokens,
+  cache_read: Schema.optional(Money.USDPerMillionTokens),
+  cache_write: Schema.optional(Money.USDPerMillionTokens),
   tier: Schema.Struct({
     type: Schema.Literal("context"),
     size: Schema.Finite,
@@ -29,17 +30,17 @@ const CostTier = Schema.Struct({
 })
 
 const Cost = Schema.Struct({
-  input: Schema.Finite,
-  output: Schema.Finite,
-  cache_read: Schema.optional(Schema.Finite),
-  cache_write: Schema.optional(Schema.Finite),
+  input: Money.USDPerMillionTokens,
+  output: Money.USDPerMillionTokens,
+  cache_read: Schema.optional(Money.USDPerMillionTokens),
+  cache_write: Schema.optional(Money.USDPerMillionTokens),
   tiers: Schema.optional(Schema.Array(CostTier)),
   context_over_200k: Schema.optional(
     Schema.Struct({
-      input: Schema.Finite,
-      output: Schema.Finite,
-      cache_read: Schema.optional(Schema.Finite),
-      cache_write: Schema.optional(Schema.Finite),
+      input: Money.USDPerMillionTokens,
+      output: Money.USDPerMillionTokens,
+      cache_read: Schema.optional(Money.USDPerMillionTokens),
+      cache_write: Schema.optional(Money.USDPerMillionTokens),
     }),
   ),
 })

+ 13 - 6
packages/core/src/plugin.ts

@@ -1,6 +1,6 @@
 export * as PluginV2 from "./plugin"
 
-import type { Plugin } from "@opencode-ai/plugin/v2/effect"
+import type { Plugin } from "@opencode-ai/plugin/v2/effect/plugin"
 import { Event, ID, type Info } from "@opencode-ai/schema/plugin"
 import { makeLocationNode } from "./effect/app-node"
 import { Context, Effect, Exit, Layer, Scope, Semaphore } from "effect"
@@ -18,6 +18,7 @@ import { SkillV2 } from "./skill"
 import { State } from "./state"
 import { ToolRegistry } from "./tool/registry"
 import { ToolHooks } from "./tool/hooks"
+import { PluginHooks } from "./plugin/hooks"
 
 export interface Interface {
   readonly activate: (plugins: readonly { readonly plugin: Plugin; readonly version?: string }[]) => Effect.Effect<void>
@@ -57,12 +58,13 @@ const layer = Layer.effect(
             generation.length === definitions.length &&
             generation.every(
               (plugin, index) => plugin.id === definitions[index]?.id && plugin.version === definitions[index]?.version,
-            )
+            ) &&
+            definitions.every((definition) => active.has(definition.id))
           ) {
             return
           }
           generation = undefined
-          const exit = yield* State.batch(
+          yield* State.batch(
             Effect.gen(function* () {
               const scopes = Array.from(active.values()).toReversed()
               active.clear()
@@ -81,13 +83,17 @@ const layer = Layer.effect(
                   Effect.onExit((exit) => (Exit.isFailure(exit) ? Scope.close(child, exit) : Effect.void)),
                   Effect.exit,
                 )
-                if (Exit.isFailure(loaded)) return loaded
+                if (Exit.isFailure(loaded)) {
+                  yield* Effect.logWarning("failed to load plugin", {
+                    "plugin.id": definition.id,
+                    cause: loaded.cause,
+                  })
+                  continue
+                }
                 active.set(definition.id, child)
               }
-              return Exit.void
             }),
           )
-          if (Exit.isFailure(exit)) return yield* exit
           generation = definitions.map((definition) => ({
             id: definition.id,
             ...(definition.version === undefined ? {} : { version: definition.version }),
@@ -131,6 +137,7 @@ export const node = makeLocationNode({
     SkillV2.node,
     ToolRegistry.toolsNode,
     ToolHooks.node,
+    PluginHooks.node,
     PluginRuntime.node,
   ],
 })

+ 7 - 0
packages/core/src/plugin/agent.ts

@@ -125,6 +125,7 @@ export const Plugin = define({
 
     yield* ctx.agent.transform((draft) => {
       draft.update(AgentV2.defaultID, (item) => {
+        item.name = AgentV2.Name.make("Build")
         item.description = "The default agent. Executes tools based on configured permissions."
         item.mode = "primary"
         item.permissions.push(
@@ -136,6 +137,7 @@ export const Plugin = define({
       })
 
       draft.update(AgentV2.ID.make("plan"), (item) => {
+        item.name = AgentV2.Name.make("Plan")
         item.description = "Plan mode. Disallows all edit tools."
         item.mode = "primary"
         item.permissions.push(
@@ -155,6 +157,7 @@ export const Plugin = define({
       })
 
       draft.update(AgentV2.ID.make("general"), (item) => {
+        item.name = AgentV2.Name.make("General")
         item.description =
           "General-purpose agent for researching complex questions and executing multi-step tasks. Use this agent to execute multiple units of work in parallel."
         item.mode = "subagent"
@@ -167,6 +170,7 @@ export const Plugin = define({
       })
 
       draft.update(AgentV2.ID.make("explore"), (item) => {
+        item.name = AgentV2.Name.make("Explore")
         item.description =
           'Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions.'
         item.system = PROMPT_EXPLORE
@@ -189,6 +193,7 @@ export const Plugin = define({
       })
 
       draft.update(AgentV2.ID.make("compaction"), (item) => {
+        item.name = AgentV2.Name.make("Compaction")
         item.mode = "primary"
         item.hidden = true
         item.system = PROMPT_COMPACTION
@@ -196,6 +201,7 @@ export const Plugin = define({
       })
 
       draft.update(AgentV2.ID.make("title"), (item) => {
+        item.name = AgentV2.Name.make("Title")
         item.mode = "primary"
         item.hidden = true
         item.system = PROMPT_TITLE
@@ -203,6 +209,7 @@ export const Plugin = define({
       })
 
       draft.update(AgentV2.ID.make("summary"), (item) => {
+        item.name = AgentV2.Name.make("Summary")
         item.mode = "primary"
         item.hidden = true
         item.system = PROMPT_SUMMARY

+ 67 - 0
packages/core/src/plugin/hooks.ts

@@ -0,0 +1,67 @@
+export * as PluginHooks from "./hooks"
+
+import type { AISDKHooks } from "@opencode-ai/plugin/v2/effect/aisdk"
+import type { SessionHooks } from "@opencode-ai/plugin/v2/effect/session"
+import type { ToolHooks } from "@opencode-ai/plugin/v2/effect/tool"
+import { Context, Effect, Layer, Scope } from "effect"
+import { makeLocationNode } from "../effect/app-node"
+import { State } from "../state"
+
+export interface Domains {
+  readonly aisdk: AISDKHooks
+  readonly session: SessionHooks
+  readonly tool: ToolHooks
+}
+
+type Callback<Event> = (event: Event) => Effect.Effect<void>
+
+export interface Interface {
+  readonly register: <Domain extends keyof Domains, Name extends keyof Domains[Domain]>(
+    domain: Domain,
+    name: Name,
+    callback: Callback<Domains[Domain][Name]>,
+  ) => Effect.Effect<State.Registration, never, Scope.Scope>
+  readonly trigger: <Domain extends keyof Domains, Name extends keyof Domains[Domain]>(
+    domain: Domain,
+    name: Name,
+    event: Domains[Domain][Name],
+  ) => Effect.Effect<Domains[Domain][Name]>
+}
+
+export class Service extends Context.Service<Service, Interface>()("@opencode/v2/PluginHooks") {}
+
+const layer = Layer.effect(
+  Service,
+  Effect.gen(function* () {
+    const callbacks = new Map<string, Function[]>()
+    const key = (domain: keyof Domains, name: PropertyKey) => `${domain}.${String(name)}`
+
+    const register: Interface["register"] = Effect.fn("PluginHooks.register")(function* (domain, name, callback) {
+      const scope = yield* Scope.Scope
+      const id = key(domain, name)
+      let active = true
+      callbacks.set(id, [...(callbacks.get(id) ?? []), callback])
+      const dispose = Effect.sync(() => {
+        if (!active) return
+        active = false
+        const next = (callbacks.get(id) ?? []).filter((item) => item !== callback)
+        if (next.length === 0) callbacks.delete(id)
+        else callbacks.set(id, next)
+      })
+      yield* Scope.addFinalizer(scope, dispose)
+      return { dispose }
+    })
+
+    const trigger: Interface["trigger"] = Effect.fn("PluginHooks.trigger")(function* (domain, name, event) {
+      for (const callback of callbacks.get(key(domain, name)) ?? []) {
+        const result: Effect.Effect<void> = Reflect.apply(callback, undefined, [event])
+        yield* result
+      }
+      return event
+    })
+
+    return Service.of({ register, trigger })
+  }),
+)
+
+export const node = makeLocationNode({ service: Service, layer, deps: [] })

+ 74 - 68
packages/core/src/plugin/host.ts

@@ -1,6 +1,6 @@
 export * as PluginHost from "./host"
 
-import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
+import type { Plugin } from "@opencode-ai/plugin/v2/effect"
 import { EventManifest } from "@opencode-ai/schema/event-manifest"
 import { Effect, Schema, Stream } from "effect"
 import { AgentV2 } from "../agent"
@@ -22,6 +22,7 @@ import { Tool } from "../tool/tool"
 import { Tools } from "../tool/tools"
 import { ToolHooks } from "../tool/hooks"
 import { WorkspaceV2 } from "../workspace"
+import { PluginHooks } from "./hooks"
 
 const mutable = <T>(value: T) => value as DeepMutable<T>
 export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Interface) {
@@ -36,6 +37,7 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
   const skill = yield* SkillV2.Service
   const tools = yield* Tools.Service
   const toolHooks = yield* ToolHooks.Service
+  const hooks = yield* PluginHooks.Service
   const runtime = yield* PluginRuntime.Service
   const locationInfo = () =>
     new Location.Info({
@@ -43,7 +45,7 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
       workspaceID: location.workspaceID,
       project: location.project,
     })
-  const locationRef = (input?: Parameters<PluginContext["agent"]["list"]>[0]) =>
+  const locationRef = (input?: Parameters<Plugin.Context["agent"]["list"]>[0]) =>
     input?.location === undefined
       ? undefined
       : Location.Ref.make({
@@ -79,32 +81,32 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
         }),
     },
     aisdk: {
-      sdk: (callback) =>
-        aisdk.hook.sdk((event) => {
+      hook: (name, callback) => {
+        if (name === "sdk") {
+          return aisdk.hook.sdk((event) => {
+            const output = {
+              model: mutable(event.model),
+              package: event.package,
+              options: event.options,
+              sdk: event.sdk,
+            }
+            return Reflect.apply(callback, undefined, [output]).pipe(
+              Effect.tap(() => Effect.sync(() => (event.sdk = output.sdk))),
+            )
+          })
+        }
+        return aisdk.hook.language((event) => {
           const output = {
             model: mutable(event.model),
-            package: event.package,
             options: event.options,
             sdk: event.sdk,
-          }
-          const result = callback(output)
-          return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
-            Effect.tap(() => Effect.sync(() => (event.sdk = output.sdk))),
-          )
-        }),
-      language: (callback) =>
-        aisdk.hook.language((event) => {
-          const output = {
-            model: mutable(event.model),
-            sdk: event.sdk,
-            options: event.options,
             language: event.language,
           }
-          const result = callback(output)
-          return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
+          return Reflect.apply(callback, undefined, [output]).pipe(
             Effect.tap(() => Effect.sync(() => (event.language = output.language))),
           )
-        }),
+        })
+      },
     },
     catalog: {
       provider: {
@@ -164,25 +166,29 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
     integration: {
       list: () => response(integration.list()),
       get: (input) => response(integration.get(Integration.ID.make(input.integrationID))),
-      connectKey: (input) =>
-        integration.connection.key({
-          integrationID: Integration.ID.make(input.integrationID),
-          key: input.key,
-          label: input.label,
-        }),
-      connectOauth: (input) =>
-        response(
-          integration.connection.oauth({
+      connect: {
+        key: (input) =>
+          integration.connection.key({
             integrationID: Integration.ID.make(input.integrationID),
-            methodID: Integration.MethodID.make(input.methodID),
-            inputs: input.inputs,
+            key: input.key,
             label: input.label,
           }),
-        ),
-      attemptStatus: (input) => response(integration.attempt.status(Integration.AttemptID.make(input.attemptID))),
-      attemptComplete: (input) =>
-        integration.attempt.complete({ attemptID: Integration.AttemptID.make(input.attemptID), code: input.code }),
-      attemptCancel: (input) => integration.attempt.cancel(Integration.AttemptID.make(input.attemptID)),
+        oauth: (input) =>
+          response(
+            integration.connection.oauth({
+              integrationID: Integration.ID.make(input.integrationID),
+              methodID: Integration.MethodID.make(input.methodID),
+              inputs: input.inputs,
+              label: input.label,
+            }),
+          ),
+      },
+      attempt: {
+        status: (input) => response(integration.attempt.status(Integration.AttemptID.make(input.attemptID))),
+        complete: (input) =>
+          integration.attempt.complete({ attemptID: Integration.AttemptID.make(input.attemptID), code: input.code }),
+        cancel: (input) => integration.attempt.cancel(Integration.AttemptID.make(input.attemptID)),
+      },
       reload: integration.reload,
       connection: {
         active: (id) => integration.connection.active(Integration.ID.make(id)),
@@ -317,11 +323,12 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
             registrations,
             (registration) => tools.register({ [registration.name]: registration.tool }, registration.options),
             { discard: true },
-          )
+          ).pipe(Effect.orDie)
+          return { dispose: Effect.void }
         }),
-      execute: {
-        before: (callback) =>
-          toolHooks.hook.before((event) => {
+      hook: (name, callback) => {
+        if (name === "execute.before") {
+          return toolHooks.hook.before((event) => {
             const output = {
               tool: event.tool,
               sessionID: event.sessionID,
@@ -330,38 +337,37 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
               toolCallID: event.toolCallID,
               input: event.input,
             }
-            const result = callback(output)
-            return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
+            return Reflect.apply(callback, undefined, [output]).pipe(
               Effect.tap(() => Effect.sync(() => (event.input = output.input))),
             )
-          }),
-        after: (callback) =>
-          toolHooks.hook.after((event) => {
-            const output = {
-              tool: event.tool,
-              sessionID: event.sessionID,
-              agent: event.agent,
-              assistantMessageID: event.assistantMessageID,
-              toolCallID: event.toolCallID,
-              input: event.input,
-              result: event.result,
-              output: event.output,
-              outputPaths: event.outputPaths,
-            }
-            const result = callback(output)
-            return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
-              Effect.tap(() =>
-                Effect.sync(() => {
-                  event.result = output.result
-                  event.output = output.output
-                  event.outputPaths = output.outputPaths
-                }),
-              ),
-            )
-          }),
+          })
+        }
+        return toolHooks.hook.after((event) => {
+          const output = {
+            tool: event.tool,
+            sessionID: event.sessionID,
+            agent: event.agent,
+            assistantMessageID: event.assistantMessageID,
+            toolCallID: event.toolCallID,
+            input: event.input,
+            result: event.result,
+            output: event.output,
+            outputPaths: event.outputPaths,
+          }
+          return Reflect.apply(callback, undefined, [output]).pipe(
+            Effect.tap(() =>
+              Effect.sync(() => {
+                event.result = output.result
+                event.output = output.output
+                event.outputPaths = output.outputPaths
+              }),
+            ),
+          )
+        })
       },
     },
     session: {
+      hook: (name, callback) => hooks.register("session", name, callback),
       create: (input) =>
         runtime.session.create({
           id: input?.id,
@@ -375,5 +381,5 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
       command: runtime.session.command,
       interrupt: (input) => runtime.session.interrupt(input.sessionID),
     },
-  } satisfies PluginContext
+  } satisfies Plugin.Context
 })

+ 3 - 3
packages/core/src/plugin/internal.ts

@@ -1,6 +1,6 @@
 export * as PluginInternal from "./internal"
 
-import type { Plugin } from "@opencode-ai/plugin/v2/effect"
+import type { Plugin } from "@opencode-ai/plugin/v2/effect/plugin"
 import { Context, Effect, Scope } from "effect"
 import { HttpClient } from "effect/unstable/http"
 import { AgentV2 } from "../agent"
@@ -31,7 +31,7 @@ import { SessionInstructions } from "../session/instructions"
 import { SessionTodo } from "../session/todo"
 import { Shell } from "../shell"
 import { SkillV2 } from "../skill"
-import { ApplyPatchTool } from "../tool/apply-patch"
+import { PatchTool } from "../tool/patch"
 import { EditTool } from "../tool/edit"
 import { GlobTool } from "../tool/glob"
 import { GrepTool } from "../tool/grep"
@@ -127,7 +127,7 @@ const pre = [
   SkillPlugin.Plugin,
   ModelsDevPlugin,
   ...ProviderPlugins,
-  ApplyPatchTool.Plugin,
+  PatchTool.Plugin,
   EditTool.Plugin,
   GlobTool.Plugin,
   GrepTool.Plugin,

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff