carthagent 1.0.7 → 1.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/README.md +140 -83
  2. package/bin/ctg.mjs +14 -732
  3. package/docs/beta-rewards-e2e-plan.md +48 -0
  4. package/docs/beta-rewards-e2e-results.md +84 -0
  5. package/docs/cloud-onboarding-acceptance.md +65 -0
  6. package/docs/glyph-review/host-chrome-glyph-compatibility-v4.md +83 -0
  7. package/docs/glyph-review/test-isolation-v1.md +26 -0
  8. package/docs/glyph-review/typecheck-base.log +1 -0
  9. package/docs/glyph-review/typecheck-v4.log +1 -0
  10. package/docs/glyph-review/unicode-inventory-v4.csv +402 -0
  11. package/docs/help-dispatch-review.md +69 -0
  12. package/docs/launch-readiness-live-results.md +65 -0
  13. package/docs/legacy-cli.md +150 -0
  14. package/docs/live-theme-plugin-api.md +18 -0
  15. package/docs/native-cli.md +102 -0
  16. package/docs/native-compatibility.md +16 -0
  17. package/docs/native-daily-quests.md +132 -0
  18. package/docs/native-glyph-pty.md +59 -0
  19. package/docs/native-manual-testing.md +336 -0
  20. package/docs/native-slash-acceptance-handoff.md +49 -0
  21. package/docs/native-terminal-paired-failure-2026-10-17.md +40 -0
  22. package/docs/native-terminal-performance.md +310 -0
  23. package/docs/native-terminal-semantics-evidence/README.md +25 -0
  24. package/docs/native-terminal-semantics-evidence/controlled-host-model-selftest.json +16 -0
  25. package/docs/native-terminal-semantics-evidence/current-acceptance-status.json +59 -0
  26. package/docs/native-terminal-semantics-evidence/final-binary-focused-85bb44a.json +154 -0
  27. package/docs/native-terminal-semantics-evidence/final-binary-full-incomplete-85bb44a.json +160 -0
  28. package/docs/native-terminal-semantics-evidence/final-binary-layout-85bb44a.json +149 -0
  29. package/docs/native-terminal-semantics-evidence/final-binary-layout-interactive-85bb44a.json +159 -0
  30. package/docs/native-terminal-semantics-evidence/final-binary-model-picker-escape-unchanged-85bb44a.json +44 -0
  31. package/docs/native-terminal-semantics-evidence/final-binary-model-picker-exact-identity-85bb44a.json +47 -0
  32. package/docs/native-terminal-semantics-evidence/final-binary-model-picker-unchanged-model-incomplete-85bb44a.json +58 -0
  33. package/docs/native-terminal-semantics-evidence/final-binary-model-selftest.json +16 -0
  34. package/docs/native-terminal-semantics-evidence/final-binary-picker-cancel-incomplete-85bb44a.json +62 -0
  35. package/docs/native-terminal-semantics-evidence/focused-clipboard-title-stale-binary.json +161 -0
  36. package/docs/native-terminal-semantics-evidence/layout-stale-binary-observation-not-accepted.json +153 -0
  37. package/docs/native-terminal-semantics-evidence/picker-latest-incomplete-attempt.json +62 -0
  38. package/docs/native-terminal-semantics-evidence/picker-previous-probe-pass.json +61 -0
  39. package/docs/native-terminal-semantics.md +89 -0
  40. package/docs/native-upgrade.md +175 -0
  41. package/docs/opencode-composed-source-review.md +89 -0
  42. package/docs/opencode-migration.md +225 -9
  43. package/docs/opencode-owned-runtime.md +241 -0
  44. package/docs/opencode-runtime.md +12 -0
  45. package/docs/opencode-theme-provider-proof/README.md +55 -0
  46. package/docs/opencode-theme-provider-proof/live-theme-api-base.patch +122 -0
  47. package/docs/opencode-theme-provider-proof/plugin-theme-provider.patch +107 -0
  48. package/docs/opencode-theme-provider-proof/session-ui-base.patch +1217 -0
  49. package/docs/owned-host-controls-proof.md +39 -0
  50. package/docs/owned-runtime-release-manifest.md +42 -0
  51. package/docs/owned-visual-clear-proof.md +40 -0
  52. package/docs/patches/opencode-v2.0.32-glyph-spinner-test-isolation-v1.patch +50 -0
  53. package/docs/patches/opencode-v2.0.32-glyphs.patch +3670 -0
  54. package/docs/patches/opencode-v2.0.32-help-dispatch.patch +156 -0
  55. package/docs/patches/opencode-v2.0.32-live-theme-api.patch +122 -0
  56. package/docs/patches/opencode-v2.0.32-session-ui-combined-v1.patch +5272 -0
  57. package/docs/patches/opencode-v2.0.32-session-ui-combined-v2.patch +6052 -0
  58. package/docs/patches/opencode-v2.0.32-session-ui-combined-v3.patch +8657 -0
  59. package/docs/patches/opencode-v2.0.32-session-ui-combined-v4.patch +9928 -0
  60. package/docs/patches/opencode-v2.0.32-session-ui-combined-v5.patch +13965 -0
  61. package/docs/patches/opencode-v2.0.32-session-ui.patch +5137 -0
  62. package/docs/release-notes-beta.1.md +60 -0
  63. package/docs/release-notes-beta.2.md +60 -0
  64. package/docs/release-notes-native-draft.md +73 -0
  65. package/docs/release-notes-preview.10.md +121 -0
  66. package/docs/release-notes-preview.11.md +76 -0
  67. package/docs/release-notes-preview.12.md +55 -0
  68. package/docs/release-notes-preview.13.md +68 -0
  69. package/docs/release-notes-preview.9.md +130 -0
  70. package/docs/source-preview.md +120 -0
  71. package/evaluate.mjs +28 -1
  72. package/lib/budget.mjs +1 -1
  73. package/lib/cloud/client.mjs +13 -7
  74. package/lib/codegraph.mjs +1 -1
  75. package/lib/delivery-review.mjs +19 -6
  76. package/lib/delivery.mjs +3 -2
  77. package/lib/environment.mjs +8 -0
  78. package/lib/guidance.mjs +1 -1
  79. package/lib/opencode/cli-runtime.mjs +1188 -0
  80. package/lib/opencode/cli.mjs +253 -0
  81. package/lib/opencode/credentials.mjs +63 -8
  82. package/lib/opencode/demo.mjs +53 -0
  83. package/lib/opencode/evaluation-budget-plugin/index.ts +3 -0
  84. package/lib/opencode/evaluation.mjs +32 -12
  85. package/lib/opencode/headless.mjs +22 -5
  86. package/lib/opencode/provider-policy.mjs +23 -0
  87. package/lib/opencode/review.mjs +37 -16
  88. package/lib/opencode/runtime-artifact.mjs +335 -0
  89. package/lib/opencode/runtime-github-transport.mjs +121 -0
  90. package/lib/opencode/runtime-release.json +36 -0
  91. package/lib/opencode/runtime.mjs +299 -20
  92. package/lib/opencode/service.mjs +77 -35
  93. package/lib/opencode/session-options.mjs +6 -2
  94. package/lib/opencode/update.mjs +222 -0
  95. package/lib/opencode/workspace.mjs +1 -1
  96. package/lib/planning-advice.mjs +2 -2
  97. package/lib/providers/experiential.mjs +2 -2
  98. package/lib/providers/names.mjs +1 -1
  99. package/lib/tui/framing.mjs +1 -80
  100. package/lib/tui/models.mjs +1 -1
  101. package/lib/update.mjs +10 -1
  102. package/package.json +33 -7
  103. package/plugins/carthagent/README.md +23 -0
  104. package/plugins/carthagent/src/bootstrap.ts +1 -1
  105. package/plugins/carthagent/src/daily-evidence.mjs +57 -0
  106. package/plugins/carthagent/src/daily-quests-rpc.ts +50 -0
  107. package/plugins/carthagent/src/daily-source-rpc.ts +52 -0
  108. package/plugins/carthagent/src/index.ts +71 -24
  109. package/plugins/carthagent/src/rpc.ts +51 -6
  110. package/plugins/carthagent/src/server/codegraph.ts +12 -5
  111. package/plugins/carthagent/src/server/daily-quests-rpc.ts +277 -0
  112. package/plugins/carthagent/src/server/daily-source-rpc.ts +243 -0
  113. package/plugins/carthagent/src/server/delivery.ts +227 -112
  114. package/plugins/carthagent/src/server/framing.mjs +80 -0
  115. package/plugins/carthagent/src/server/policy.ts +1 -1
  116. package/plugins/carthagent/src/server/providers.ts +65 -36
  117. package/plugins/carthagent/src/server/session-controls.ts +3 -1
  118. package/plugins/carthagent/src/server/ship-agents.ts +77 -0
  119. package/plugins/carthagent/src/server/ship-context.mjs +60 -0
  120. package/plugins/carthagent/src/server/ship-guidance.ts +41 -0
  121. package/plugins/carthagent/src/server/ship-rpc.ts +71 -0
  122. package/plugins/carthagent/src/server/voyages-rpc.ts +280 -0
  123. package/plugins/carthagent/src/testing.ts +5 -0
  124. package/plugins/carthagent/src/tui.tsx +506 -0
  125. package/plugins/carthagent/src/ui/client.ts +127 -0
  126. package/plugins/carthagent/src/ui/commands.ts +70 -0
  127. package/plugins/carthagent/src/ui/daily-editor-focus.mjs +52 -0
  128. package/plugins/carthagent/src/ui/daily-editor.mjs +4 -0
  129. package/plugins/carthagent/src/ui/daily-quests-events.mjs +18 -0
  130. package/plugins/carthagent/src/ui/daily-quests-state.mjs +218 -0
  131. package/plugins/carthagent/src/ui/daily-quests.tsx +235 -0
  132. package/plugins/carthagent/src/ui/host-state.ts +42 -0
  133. package/plugins/carthagent/src/ui/model-command.ts +32 -0
  134. package/plugins/carthagent/src/ui/native-navigation.ts +23 -0
  135. package/plugins/carthagent/src/ui/native-review.d.mts +33 -0
  136. package/plugins/carthagent/src/ui/native-review.mjs +159 -0
  137. package/plugins/carthagent/src/ui/resume-session.mjs +34 -0
  138. package/plugins/carthagent/src/ui/select-mouse.ts +17 -0
  139. package/plugins/carthagent/src/ui/session-preferences.d.mts +7 -0
  140. package/plugins/carthagent/src/ui/session-preferences.mjs +14 -0
  141. package/plugins/carthagent/src/ui/theme-provision.d.mts +3 -0
  142. package/plugins/carthagent/src/ui/theme-provision.mjs +134 -0
  143. package/plugins/carthagent/src/ui/voyages-navigation-state.mjs +56 -0
  144. package/plugins/carthagent/src/ui/voyages-navigation.tsx +131 -0
  145. package/plugins/carthagent/src/ui/voyages-state.mjs +165 -0
  146. package/plugins/carthagent/src/ui/voyages.tsx +131 -0
  147. package/plugins/carthagent/src/ui/workspace-live.ts +61 -0
  148. package/plugins/carthagent/src/ui/workspace.tsx +379 -0
  149. package/plugins/carthagent/src/voyages-rpc.ts +64 -0
  150. package/plugins/carthagent/src/workspace-rpc.ts +36 -0
  151. package/plugins/carthagent/tests/commands.test.ts +107 -0
  152. package/plugins/carthagent/tests/session-preferences.test.mjs +41 -0
  153. package/plugins/carthagent/tests/voyages-navigation.test.tsx +269 -0
  154. package/plugins/carthagent/themes/carthage.json +67 -0
  155. package/plugins/carthagent/tui.ts +26 -1
  156. package/script/ctg/build-owned-runtime.sh +166 -0
  157. package/tests/budget-engine.test.mjs +4 -3
  158. package/tests/cli.test.mjs +52 -96
  159. package/tests/cloud-client.test.mjs +2 -2
  160. package/tests/daily-evidence.test.mjs +62 -0
  161. package/tests/delivery-review.test.mjs +57 -2
  162. package/tests/delivery.test.mjs +22 -1
  163. package/tests/experiential-extension.test.mjs +2 -2
  164. package/tests/experiential.test.mjs +1 -1
  165. package/tests/fixtures/native-discovery-plugin/server.ts +15 -0
  166. package/tests/generate-owned-runtime-release.test.mjs +354 -0
  167. package/tests/installed-runtime-contract.test.mjs +43 -0
  168. package/tests/native-cli-host-smoke.mjs +457 -0
  169. package/tests/native-daily-editor-focus.test.mjs +213 -0
  170. package/tests/native-daily-editor.test.mjs +43 -0
  171. package/tests/native-daily-quests-layout.test.mjs +55 -0
  172. package/tests/native-daily-quests-rpc.test.mjs +456 -0
  173. package/tests/native-daily-quests-ui.test.mjs +778 -0
  174. package/tests/native-daily-source.test.mjs +525 -0
  175. package/tests/native-demo-ui.test.mjs +48 -0
  176. package/tests/native-evaluation-admission.test.mjs +24 -0
  177. package/tests/native-fixture-browser-cleanup.test.mjs +65 -0
  178. package/tests/native-fixture-runtime.test.mjs +111 -0
  179. package/tests/native-fixture-stop-diagnostics.test.mjs +67 -0
  180. package/tests/native-glyph-pty.test.mjs +107 -0
  181. package/tests/native-manual-projects.test.mjs +170 -0
  182. package/tests/native-model-command.test.mjs +67 -0
  183. package/tests/native-owned-runtime-env.test.mjs +140 -0
  184. package/tests/native-paired-guards.test.mjs +464 -0
  185. package/tests/native-provider-policy.test.mjs +33 -0
  186. package/tests/native-pty-invocation.test.mjs +114 -0
  187. package/tests/native-pty-runtime.test.mjs +45 -0
  188. package/tests/native-resume-session.test.mjs +46 -0
  189. package/tests/native-review-consumer.test.mjs +286 -0
  190. package/tests/native-runtime-identity.test.mjs +214 -0
  191. package/tests/native-select-mouse.test.mjs +23 -0
  192. package/tests/native-ship-guidance.test.mjs +16 -0
  193. package/tests/native-ship-parity.test.mjs +526 -0
  194. package/tests/native-ship-plan.test.mjs +85 -0
  195. package/tests/native-ship-rpc.test.mjs +83 -0
  196. package/tests/native-ui-commands.test.mjs +6 -0
  197. package/tests/native-voyages-layout.test.mjs +28 -0
  198. package/tests/native-voyages-navigation.test.mjs +169 -0
  199. package/tests/native-voyages-rpc.test.mjs +397 -0
  200. package/tests/native-voyages-ui.test.mjs +307 -0
  201. package/tests/opencode-atomic-write.test.mjs +55 -0
  202. package/tests/opencode-cli.test.mjs +1312 -0
  203. package/tests/opencode-codegraph-plugin.test.mjs +47 -0
  204. package/tests/opencode-delivery.test.mjs +16 -0
  205. package/tests/opencode-providers.test.mjs +248 -20
  206. package/tests/opencode-runtime.test.mjs +597 -8
  207. package/tests/opencode-session-controls.test.mjs +15 -0
  208. package/tests/opencode-ship-rpc.test.mjs +71 -0
  209. package/tests/opencode-update.test.mjs +475 -0
  210. package/tests/opencode-workspace.test.mjs +51 -2
  211. package/tests/owned-runtime-publish-workflow.test.mjs +477 -0
  212. package/tests/release-workflow.test.mjs +46 -0
  213. package/tests/runtime-artifact.test.mjs +269 -0
  214. package/tests/runtime-development-trust.test.mjs +231 -0
  215. package/tests/runtime-github-transport.test.mjs +132 -0
  216. package/tests/runtime-token-env.test.mjs +23 -0
  217. package/tests/server.test.mjs +3 -1
  218. package/tests/ship-telemetry.test.mjs +1 -1
  219. package/tests/source-preview.test.mjs +253 -0
  220. package/tests/test_native_terminal_probe.py +76 -0
  221. package/tests/test_native_terminal_semantics.py +232 -0
  222. package/tests/tui.test.mjs +2 -2
  223. package/tests/update.test.mjs +42 -5
  224. package/tests/verify-draft-runtime-gate.test.mjs +280 -0
  225. package/tests/verify-owned-runtime-release.test.mjs +371 -0
  226. package/tests/workspace-live.test.mjs +127 -0
  227. package/tools/benchmark/diagnose-request.mjs +59 -36
  228. package/tools/benchmark/eval-common.mjs +3 -3
  229. package/tools/benchmark/evaluators.test.mjs +4 -4
  230. package/tools/benchmark/guard.test.mjs +35 -2
  231. package/tools/benchmark/guard.ts +20 -7
  232. package/tools/benchmark/planning-startup.test.mjs +30 -34
  233. package/tools/benchmark/runner.mjs +12 -7
  234. package/tools/benchmark/runner.test.mjs +23 -7
  235. package/tools/benchmark/upstream-error.test.mjs +16 -2
  236. package/tools/generate-owned-runtime-release.mjs +217 -0
  237. package/tools/installed-runtime-contract.mjs +16 -0
  238. package/tools/manual-runtime-manifest.mjs +39 -0
  239. package/tools/native-fixture-browser-cleanup.mjs +59 -0
  240. package/tools/native-fixture-stop-diagnostics.mjs +45 -0
  241. package/tools/native-fixture.mjs +199 -40
  242. package/tools/native-glyph-helpers.py +127 -0
  243. package/tools/native-glyph-pty.py +144 -0
  244. package/tools/native-manual-projects.mjs +259 -0
  245. package/tools/native-manual.mjs +338 -0
  246. package/tools/native-model-picker-acceptance.py +306 -0
  247. package/tools/native-owned-runtime-env.sh +47 -0
  248. package/tools/native-paired-guards.mjs +282 -0
  249. package/tools/native-paired-manifest.mjs +72 -0
  250. package/tools/native-paired-orchestration.sh +51 -0
  251. package/tools/native-plugin-readiness.mjs +33 -0
  252. package/tools/native-plugin-readiness.test.mjs +53 -0
  253. package/tools/native-pty-invocation.mjs +26 -0
  254. package/tools/native-pty-runtime.mjs +83 -0
  255. package/tools/native-pty-smoke.py +147 -24
  256. package/tools/native-public-repo-shell-result.mjs +13 -0
  257. package/tools/native-public-repo-shell-result.test.mjs +28 -0
  258. package/tools/native-runtime-identity.mjs +201 -0
  259. package/tools/native-slash-acceptance-env.py +38 -0
  260. package/tools/native-terminal-semantics.py +882 -0
  261. package/tools/owned-shell-observer/index.ts +16 -0
  262. package/tools/owned-shell-observer/package.json +1 -0
  263. package/tools/owned-visual-clear-env.py +48 -0
  264. package/tools/owned-visual-clear-model.py +21 -0
  265. package/tools/pinned-owned-runtime-inputs.mjs +46 -0
  266. package/tools/prepare-native-manual.mjs +122 -0
  267. package/tools/run-native-terminal-paired.sh +403 -0
  268. package/tools/runtime-token-env.mjs +8 -0
  269. package/tools/seed-native-switch-fixture.mjs +79 -0
  270. package/tools/test-native-controls.mjs +3 -1
  271. package/tools/test-native-daily-quests.mjs +65 -0
  272. package/tools/test-native-default-cli.mjs +106 -0
  273. package/tools/test-native-delivery-host.mjs +102 -20
  274. package/tools/test-native-evaluation.mjs +7 -3
  275. package/tools/test-native-glyph-pty.mjs +94 -0
  276. package/tools/test-native-host.mjs +91 -65
  277. package/tools/test-native-outcomes.mjs +3 -1
  278. package/tools/test-native-package.mjs +206 -38
  279. package/tools/test-native-performance.mjs +26 -0
  280. package/tools/test-native-product.mjs +90 -0
  281. package/tools/test-native-pty.mjs +34 -9
  282. package/tools/test-native-public-repo.mjs +224 -0
  283. package/tools/test-native-refresh-reentry.mjs +106 -0
  284. package/tools/test-native-report.mjs +27 -20
  285. package/tools/test-native-review.mjs +75 -0
  286. package/tools/test-native-runtime.mjs +120 -0
  287. package/tools/test-native-ship-models.mjs +176 -0
  288. package/tools/test-native-ship-walkthrough.mjs +96 -0
  289. package/tools/test-native-slash-commands.mjs +125 -0
  290. package/tools/test-native-slash-commands.py +242 -0
  291. package/tools/test-native-swe-host.mjs +224 -55
  292. package/tools/test-native-tasks.mjs +77 -0
  293. package/tools/test-native-terminal-performance.py +887 -0
  294. package/tools/test-native-terminal-readiness.sh +85 -0
  295. package/tools/test-native-terminal-semantics-installed.mjs +38 -0
  296. package/tools/test-native-visual-clear-installed.mjs +48 -0
  297. package/tools/test-owned-host-controls.mjs +238 -0
  298. package/tools/test-owned-opencode-runtime.mjs +35 -0
  299. package/tools/test-owned-visual-clear-env.py +120 -0
  300. package/tools/test-owned-visual-clear.py +373 -0
  301. package/tools/verify-draft-runtime-gate.mjs +138 -0
  302. package/tools/verify-owned-runtime-release.mjs +114 -0
  303. package/tools/verify-release-version.mjs +21 -1
@@ -0,0 +1,120 @@
1
+ # Projects sidebar source preview
2
+
3
+ This preview runs the redesigned Projects sidebar from OpenCode source plus the
4
+ Carthagent plugin in this checkout. It is a development preview. Nothing here
5
+ builds, publishes, installs, or changes the published `ctg` runtime.
6
+
7
+ ## What is pinned where
8
+
9
+ | Purpose | Patch | Recipe | Hash reference |
10
+ | --- | --- | --- | --- |
11
+ | Published runtime (preview.13) | `docs/patches/opencode-v2.0.32-session-ui-combined-v5.patch` | `script/ctg/build-owned-runtime.sh`, publish workflow | `CTG_RUNTIME_PATCH_SHA256`, `lib/opencode/runtime-release.json` |
12
+ | Source preview (development) | `docs/patches/opencode-v2.0.32-session-ui-combined-v5.patch` | `script/ctg/prepare-development-runtime.sh` | `CTG_DEVELOPMENT_RUNTIME_PATCH_SHA256` |
13
+
14
+ Both patches apply to clean upstream `https://github.com/anomalyco/opencode` at
15
+ `843a6c514e606b436a7fe713422ace0a93ad3d61`. v5 is a replacement patch: it
16
+ contains everything in v4 plus the sidebar, folder browser and new-session
17
+ setup sources and their tests under `packages/tui/test/sidebar`. Do not apply
18
+ v4 first.
19
+
20
+ ## Setup
21
+
22
+ 1. `npm ci` in this checkout (the plugin runs from `plugins/carthagent`).
23
+ 2. Clone upstream into a new directory, check out the pinned commit, and run
24
+ `script/ctg/prepare-development-runtime.sh <checkout>`. The script refuses a
25
+ different commit, a dirty checkout or a patch with the wrong SHA-256, then
26
+ runs `git apply --check` and `git apply`. It does not install or compile.
27
+ 3. Run `bun install` with Bun 1.4.2 in the prepared checkout.
28
+
29
+ ## Launch
30
+
31
+ ```sh
32
+ script/ctg/source-preview.sh --source DIR --profile DIR --bun PATH [--workspace DIR] [-- ARGS...]
33
+ ```
34
+
35
+ - All three of `--source`, `--profile` and `--bun` are required. Relative
36
+ paths resolve against your current directory, so you can launch from
37
+ anywhere. Paths with spaces work when quoted.
38
+ - The launcher checks that `packages/cli/src/index.ts` and `node_modules`
39
+ exist and that `bun --version` matches the checkout's `packageManager` pin.
40
+ It fails before touching the profile when a prerequisite is missing.
41
+ - It runs Bun with `--no-env-file` from `<source>/packages/cli` (required for
42
+ JSX resolution) under `env -i`. Only these variables reach OpenCode: `HOME`,
43
+ `PATH` (Bun's directory, Node's directory if found, `/usr/bin`, `/bin`),
44
+ `TERM`, `COLORTERM`, `TMPDIR`, the four `XDG_*` directories,
45
+ `OPENCODE_CONFIG_DIR`, `OPENCODE_TEST_HOME`,
46
+ `OPENCODE_DISABLE_PROJECT_CONFIG=1`, `OPENCODE_DISABLE_AUTOUPDATE=1` and
47
+ `BUN_RUNTIME_TRANSPILER_CACHE_PATH=0`. Provider API-key variables are never
48
+ passed through.
49
+ - First launch creates the profile (0700) with `config/opencode/opencode.json`
50
+ (the plugin path), `cli.json` (Carthage theme) and `themes/carthage.json`
51
+ (all 0600). Each file is created only if absent; existing configuration,
52
+ sessions and authentication are left untouched. If an existing
53
+ `opencode.json` does not list the plugin, the launcher prints a warning and
54
+ still leaves the file alone.
55
+ - `--standalone` is always used, so the CLI starts its own private server on
56
+ a loopback ephemeral port (`--port 0`) that exits with the TUI. `--server`
57
+ is rejected. Without `--`, the workspace (default: current directory) is
58
+ opened. With `--`, the remaining arguments run another CLI command in the
59
+ same profile, for example `-- auth login`.
60
+
61
+ ## Use
62
+
63
+ - **Folder card** (top of Projects): current folder, abbreviated path (click
64
+ for the full path), Browse, New session and the model action. Narrow layouts
65
+ show compact `⌕` search, `▤` Browse and `◇` model controls.
66
+ - **Browse** (`/browse`, palette “Browse folders”): Filter and Path inputs
67
+ (Tab switches), Up/Down select, Enter opens, Alt+Left/Right/Up for
68
+ Back/Forward/Up, Alt+H for Home, Ctrl+S confirms, Escape cancels.
69
+ - **New session** (`Ctrl+X N`, `/new`, palette “New session” or “New session
70
+ in folder…”, folder card, compact `+`, folder right-click menu): choose the
71
+ folder, then the model. Global entries start at the folder a plain new
72
+ session would use; project entries start at that project's folder. Nothing
73
+ is created until the first message is sent; cancelling leaves the current
74
+ session, draft and model unchanged. The new session keeps the agent you were
75
+ using and, when you keep the same model, its variant (including the default).
76
+ If the chosen agent or model is not available at the folder when you send,
77
+ the send is blocked with your text kept and no session created; pick another
78
+ agent (`Shift+Tab`) or model (`/model`, `Ctrl+X M`) and send again. There is
79
+ no automatic fallback to another model. `/model` also works before a session
80
+ exists. The model step inside setup has no integration shortcut; connect
81
+ integrations from the standalone model picker (`Ctrl+X M`).
82
+ - **Exit**: `/exit` or palette “Exit the app”. The private server exits with
83
+ the TUI.
84
+ - **Resume**: relaunch with the same `--profile` and select the session in
85
+ Projects. Automatic resume is not part of this preview.
86
+
87
+ ## Tests
88
+
89
+ Run serially. Close the preview first.
90
+
91
+ ```sh
92
+ # Product (from this checkout)
93
+ node --test --test-concurrency=1 tests/source-preview.test.mjs tests/native-ship-plan.test.mjs tests/native-model-command.test.mjs
94
+ npx eslint . && npm run typecheck
95
+
96
+ # Native (from <source>/packages/tui, using the same isolated variables as the launcher)
97
+ bun --no-env-file test --timeout 30000 test/navigation-rail.test.tsx \
98
+ test/component/session-tabs-rail.test.tsx test/context/session-tabs-model.test.ts \
99
+ test/ui/layout.test.ts test/sidebar
100
+ ```
101
+
102
+ `tests/source-preview.test.mjs` uses a Bun test double and synthetic profiles,
103
+ so it proves argument handling, isolation and config preservation, not a real
104
+ launch. It runs the launcher with `/bin/sh` on Linux and macOS and is skipped
105
+ on native Windows, where the POSIX launcher is not supported.
106
+
107
+ ## Limits
108
+
109
+ - Source preview only. No native binary, package or release includes these
110
+ changes, and the published runtime remains v4.
111
+ - The launcher is a POSIX `sh` script for Linux and macOS (GNU and BSD
112
+ utilities). Native Windows is not supported.
113
+ - Real launches were verified on Linux with Bun 1.4.2. No cross-platform
114
+ launch matrix was run.
115
+ - The native type checker (`tsgo`) and native build are not part of this
116
+ workflow; Bun tests compile the changed modules but are not a full static
117
+ type check.
118
+ - Some native tests outside the focused list fail in the same way on the
119
+ pre-change sources (for example missing `ToastProvider` in
120
+ `session-tabs-status` harnesses).
package/evaluate.mjs CHANGED
@@ -23,7 +23,33 @@ const { values: args } = parseArgs({ options: {
23
23
  timeout: { type: 'string', default: '600' }, 'max-turns': { type: 'string', default: '100' }, 'max-cost': { type: 'string', default: '3' },
24
24
  python: { type: 'string' }, 'seed-from': { type: 'string' }, 'allow-live': { type: 'boolean', default: false },
25
25
  'dry-run': { type: 'boolean', default: false },
26
+ help: { type: 'boolean', short: 'h', default: false },
26
27
  } });
28
+ if (args.help) {
29
+ console.log(`Usage: ctg eval [options]
30
+
31
+ Opt-in live A/B evaluation of baseline vs custom configurations against a
32
+ scenario manifest under scenarios/<name>/. Live calls spend API credit and
33
+ generated code runs with your permissions, so a run must be explicit.
34
+
35
+ Options:
36
+ --mode baseline|custom|both What to evaluate (default both)
37
+ --task <name> Scenario short name (default cli)
38
+ --scenario <name> Scenario directory (overrides --task)
39
+ --model <id> Model id
40
+ --provider <name> Provider name; needs <PROVIDER>_API_KEY
41
+ --thinking <level> Thinking level (default medium)
42
+ --timeout <seconds> Per-run timeout, max 3600 (default 600)
43
+ --max-turns <n> Turn budget, max 1000 (default 100)
44
+ --max-cost <usd> Cost budget (default 3)
45
+ --python <path> Python executable for scenarios that need it
46
+ --seed-from <dir> Continue from a previous candidate directory
47
+ --allow-live Confirm live provider calls
48
+ --dry-run Assemble and preview without calling a provider
49
+ --help, -h Show this help
50
+ `);
51
+ process.exit(0);
52
+ }
27
53
  if (!args['allow-live'] && !args['dry-run']) throw Error('Live calls spend API credit and generated code runs with your permissions. Use --allow-live explicitly (or --dry-run to preview the assembled run without calling a provider).');
28
54
  if (!['baseline', 'custom', 'both'].includes(args.mode)) throw Error('Use --mode baseline|custom|both');
29
55
  for (const key of ['timeout', 'max-turns', 'max-cost']) if (!(Number(args[key]) > 0 && Number.isFinite(Number(args[key])))) throw Error(`Invalid ${key}`);
@@ -197,7 +223,8 @@ for (const mode of args.mode === 'both' ? ['baseline', 'custom'] : [args.mode])
197
223
  cwd,
198
224
  evidenceSchema: 2,
199
225
  runtime: NATIVE_EVALUATION_RUNTIME,
200
- runtimeVersion: result.opencodeVersion,
226
+ compatibilityVersion: result.compatibilityVersion,
227
+ runtimeVersion: result.runtimeVersion,
201
228
  exitCode: result.exitCode,
202
229
  timedOut: result.timedOut,
203
230
  budgetReason: result.budgetReason,
package/lib/budget.mjs CHANGED
@@ -22,7 +22,7 @@ export function budgetReason(budget, action, now = Date.now()) {
22
22
 
23
23
  export function validateBudgetOptions(opts, { allowWithoutDelivery = false } = {}) {
24
24
  const keys = ['maxTools', 'maxSeconds', 'maxRepairs'];
25
- if (!allowWithoutDelivery && opts.delivery === false && keys.some(key => opts[key] !== undefined)) {
25
+ if (!allowWithoutDelivery && opts.delivery === false && keys.some(key => opts[key] !== undefined)) {
26
26
  throw Error('Budget options require the delivery extension');
27
27
  }
28
28
  budgetLimits(flag => opts[{ 'delivery-max-tools': 'maxTools', 'delivery-max-seconds': 'maxSeconds', 'delivery-max-repairs': 'maxRepairs' }[flag]]);
@@ -73,7 +73,7 @@ export class CloudClient {
73
73
  }
74
74
 
75
75
  async request(path, { method = 'GET', accessToken, body, signal } = {}) {
76
- if (typeof this.fetch !== 'function') throw new Error('Carthagent Ship requires fetch support.');
76
+ if (typeof this.fetch !== 'function') throw new Error('Carthagent Cloud requires fetch support.');
77
77
  const headers = { accept: 'application/json' };
78
78
  if (body !== undefined) headers['content-type'] = 'application/json';
79
79
  if (accessToken) headers.authorization = `Bearer ${accessToken}`;
@@ -128,6 +128,11 @@ export class CloudClient {
128
128
  return this.request('/v1/billing/checkout', { method: 'POST', accessToken, body: { successUrl, cancelUrl, priceId }, signal });
129
129
  }
130
130
 
131
+ /** @param {{ accessToken?: string, plan?: string, signal?: AbortSignal }} [input] */
132
+ quote({ accessToken, plan = 'builder', signal } = {}) {
133
+ return this.request(`/v1/billing/quote?plan=${encodeURIComponent(plan)}`, { accessToken, signal });
134
+ }
135
+
131
136
  portal({ accessToken, returnUrl, signal } = {}) {
132
137
  return this.request('/v1/billing/portal', { method: 'POST', accessToken, body: { returnUrl }, signal });
133
138
  }
@@ -184,10 +189,10 @@ function formatEpochSeconds(value) {
184
189
 
185
190
  export function cloudManagedUsageRecovery(value) {
186
191
  const text = String(value?.code || value?.message || value || '');
187
- if (/daily_limit_reached|managed daily usage limit/i.test(text)) return 'Managed daily usage is exhausted. It resets at 00:00 UTC. Use /account for headroom or /login for BYOK.';
188
- if (/monthly_allowance_exhausted|managed monthly allowance/i.test(text)) return 'Managed monthly usage is exhausted. Use /billing portal to manage the plan or /login for BYOK.';
189
- if (/subscription_inactive|managed subscription is not active/i.test(text)) return 'The managed subscription is inactive. Use /billing portal or /login for BYOK.';
190
- if (/insufficient_quota|insufficient.credit|credit (?:is )?exhausted/i.test(text)) return 'Carthagent Ship credit is exhausted. Use /billing checkout where available or /login for BYOK.';
192
+ if (/daily_limit_reached|managed daily usage limit/i.test(text)) return 'Cloud daily usage is exhausted. It resets at 00:00 UTC. Use /account for your plan and headroom, /billing portal to manage billing, or /login for your own provider (BYOK). Your task stays in this session.';
193
+ if (/monthly_allowance_exhausted|managed monthly allowance/i.test(text)) return 'Cloud monthly usage is exhausted. Use /account for your renewal date, /billing portal to manage billing, or /login for your own provider (BYOK). To review a different plan use /billing checkout pro or /billing checkout max. After payment use /billing refresh, then explicitly continue this task.';
194
+ if (/subscription_inactive|managed subscription is not active/i.test(text)) return 'Cloud access is awaiting payment or activation. Use /billing refresh to check, /billing portal to manage payment, or /login for your own provider.';
195
+ if (/insufficient_quota|insufficient.credit|credit (?:is )?exhausted/i.test(text)) return 'Carthagent Cloud usage credit is exhausted. Use /billing checkout builder to review your eligible price (new Builder customers: $1 first month, then $10/month), or /login for your own provider. After payment use /billing refresh, then explicitly continue this task.';
191
196
  return null;
192
197
  }
193
198
 
@@ -256,9 +261,10 @@ export function formatCloudAccount(account, { usage } = {}) {
256
261
  const managed = account?.managedUsage;
257
262
  const planName = managed?.planName || 'plan';
258
263
  const lines = [
259
- `Carthagent Ship account ${account?.accountId || 'unknown'}`,
264
+ `Carthagent Cloud account ${account?.accountId || 'unknown'}`,
260
265
  `Credit: ${formatNanoUsd(account?.balance?.availableNanoUsd)} available`,
261
- `Plan: ${subscription.status && subscription.status !== 'none' ? `${planName} (${subscription.status})` : 'Free'}`,
266
+ `Plan: ${subscription.status && subscription.status !== 'none' ? `${planName} (${subscription.status})` : 'No paid plan'}`,
267
+ ...(account?.email ? [`Signed in as: ${account.email}`] : []),
262
268
  ];
263
269
  const subscriptionEnd = formatEpochSeconds(subscription.current_period_end);
264
270
  if (subscriptionEnd) lines.push(`Renews/ends: ${subscriptionEnd}`);
package/lib/codegraph.mjs CHANGED
@@ -153,7 +153,7 @@ const parsers = new Map(); // lang -> dedicated parser (setLanguage once; concur
153
153
  async function parserFor(lang) {
154
154
  if (!runtimePromise) {
155
155
  runtimePromise = (async () => {
156
- const Parser = require_('web-tree-sitter');
156
+ const Parser = require_('web-tree-sitter-legacy');
157
157
  await Parser.init();
158
158
  const wasmDir = require_.resolve('tree-sitter-wasms/package.json').replace(/package\.json$/, 'out');
159
159
  const languages = new Map();
@@ -1,6 +1,6 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
2
  import { readFileSync, writeFileSync, mkdirSync, realpathSync, statSync } from 'node:fs';
3
- import { dirname, join, resolve } from 'node:path';
3
+ import { dirname, join, resolve, relative, isAbsolute, sep } from 'node:path';
4
4
  import { fingerprint, runCommand, localPath, pendingRequirements, evidenceExcludedDirectories, evidenceFresh } from './delivery.mjs';
5
5
 
6
6
  // Captures real Git output and new source contents. The receipt binds a
@@ -13,15 +13,18 @@ export async function captureDeliveryReview(state, cwd, directory, signal) {
13
13
  text = 'No Git repository available. Manually inspect source files and describe the review scope honestly; no diff comparison was possible.\n';
14
14
  } else {
15
15
  if (top.timedOut || top.cancelled || top.outputLimit) throw Error('Git repository discovery failed.');
16
- if (realpathSync.native(resolve(top.output.trim())) !== realpathSync.native(resolve(cwd))) throw Error('Review requires the workspace Git root, not an unrelated ancestor repository.');
16
+ const root = realpathSync.native(resolve(top.output.trim()));
17
+ const workspace = realpathSync.native(resolve(cwd));
18
+ const scope = relative(root, workspace);
19
+ if (isAbsolute(scope) || scope === '..' || scope.startsWith(`..${sep}`)) throw Error('Review requires a workspace inside the discovered Git repository.');
17
20
  const log = join(directory, `diff-${randomUUID()}.log`);
18
21
  const exclusions = evidenceExcludedDirectories.map(name => `:(exclude,glob)**/${name}/**`);
19
- const diff = await runCommand(['git', 'diff', '--no-ext-diff', '--no-textconv', 'HEAD', '--', '.', ...exclusions], { cwd, signal, timeoutSeconds: 30, logPath: log });
22
+ const diff = await runCommand(['git', 'diff', '--relative', '--no-renames', '--no-ext-diff', '--no-textconv', 'HEAD', '--', '.', ...exclusions], { cwd, signal, timeoutSeconds: 30, logPath: log });
20
23
  if (diff.code !== 0 || diff.timedOut || diff.cancelled || diff.outputLimit) throw Error('Cannot capture complete Git diff (requires a committed baseline and bounded output).');
21
24
  const untrackedLog = join(directory, `untracked-${randomUUID()}.log`);
22
- const status = await runCommand(['git', 'ls-files', '-z', '--others', '--exclude-standard', ...evidenceExcludedDirectories.map(name => `--exclude=${name}/`)], { cwd, signal, timeoutSeconds: 10, logPath: untrackedLog });
25
+ const status = await runCommand(['git', 'ls-files', '-z', '--others', '--exclude-standard', ...evidenceExcludedDirectories.map(name => `--exclude=${name}/`), '--', '.'], { cwd, signal, timeoutSeconds: 10, logPath: untrackedLog });
23
26
  if (status.code !== 0 || status.cancelled || status.timedOut || status.outputLimit) throw Error('Cannot enumerate untracked files for review.');
24
- text = `Review scope excludes dependency/cache/output directories: ${evidenceExcludedDirectories.join(', ')}.\n${readFileSync(log, 'utf8')}\nNew source files:\n`;
27
+ text = `Review scope: workspace ${JSON.stringify(scope || '.')} relative to Git root; paths below are workspace-relative.\nReview scope excludes dependency/cache/output directories: ${evidenceExcludedDirectories.join(', ')}.\n${readFileSync(log, 'utf8')}\nNew source files:\n`;
25
28
  for (const name of readFileSync(untrackedLog, 'utf8').split('\0').filter(Boolean).sort()) {
26
29
  const path = localPath(cwd, name);
27
30
  if (statSync(path).size > 1024 * 1024) throw Error(`New file too large for review capture: ${JSON.stringify(name)}. Inspect it separately or narrow the workspace.`);
@@ -41,13 +44,23 @@ export async function captureDeliveryReview(state, cwd, directory, signal) {
41
44
  return { ...capture, diff: text.slice(0, 12000), truncated: text.length > 12000, unresolvedFindings: (state.reviewFindings || []).filter(f => !f.resolution), next: 'Read the full capture path if truncated; new text source is included. Inspect binary assets separately. Probe critical component connections and user outcomes, not only individual functions or artifact existence. Record coverage, findings and limitations; earlier findings require explicit resolutions with evidence, never silently move them to limitations.' };
42
45
  }
43
46
 
47
+ /** Models commonly send `assertion` or a string array; both carry the same claim. */
48
+ function coverageAssertions(item) {
49
+ const value = item.assertions ?? item.assertion;
50
+ if (Array.isArray(value)) return value.filter(v => typeof v === 'string' && v.trim()).join('; ');
51
+ return typeof value === 'string' ? value : '';
52
+ }
53
+
44
54
  export function recordDeliveryReview(state, cwd, params) {
45
55
  const capture = state.reviewCapture;
46
56
  const hash = fingerprint(cwd, ['.']);
47
57
  if (!capture || capture.id !== params.captureId || capture.runId !== state.runId || capture.revision !== state.revision || capture.fingerprint !== hash) throw Error('Review capture missing or stale. Use delivery_review action=inspect after final source edits.');
48
58
  if (pendingRequirements(state, hash).length) throw Error('Run fresh checks for every requirement before recording review.');
49
59
  const nonempty = value => typeof value === 'string' && value.trim().length > 0;
50
- if (!Array.isArray(params.coverage) || (state.plan.acceptance || []).some(a => !params.coverage.some(c => c.requirement === a.requirement && nonempty(c.assertions)))) throw Error('Review coverage must explain concrete assertions for every exact acceptance requirement, including required validators.');
60
+ if (!Array.isArray(params.coverage) || params.coverage.some(c => !c || typeof c !== 'object')) throw Error('coverage must be an array of {requirement, assertions, status?} objects.');
61
+ params = { ...params, coverage: params.coverage.map(c => ({ ...c, assertions: coverageAssertions(c) })) };
62
+ const uncovered = (state.plan.acceptance || []).filter(a => !params.coverage.some(c => c.requirement === a.requirement && nonempty(c.assertions))).map(a => a.requirement);
63
+ if (uncovered.length) throw Error(`Review coverage must explain concrete assertions for every exact acceptance requirement, including required validators. Missing: ${JSON.stringify(uncovered.slice(0, 10))}${uncovered.length > 10 ? ` (+${uncovered.length - 10} more)` : ''}. Each item: {requirement: <exact acceptance text>, assertions: "<what the checks assert>", status?: "demonstrated"|"unverified"}.`);
51
64
  if (params.coverage.some(c => c.status !== undefined && !['demonstrated', 'unverified'].includes(c.status))) throw Error('Coverage status must be demonstrated or unverified.');
52
65
  if (!Array.isArray(params.probes) || !params.probes.length || params.probes.some(p => !nonempty(p))) throw Error('Record boundary/metamorphic probes and observed results, not just a generic review claim.');
53
66
  if (!Array.isArray(params.findings) || params.findings.some(p => !nonempty(p))) throw Error('findings must be an array; unresolved findings require repairs or a blocked handoff.');
package/lib/delivery.mjs CHANGED
@@ -4,6 +4,7 @@ import { createHash, randomUUID } from 'node:crypto';
4
4
  import { existsSync, lstatSync, readdirSync, readFileSync, mkdirSync, writeFileSync, renameSync, openSync, writeSync, closeSync } from 'node:fs';
5
5
  import { resolve, relative, isAbsolute, join, dirname, basename } from 'node:path';
6
6
  import { spawn } from 'node:child_process';
7
+ import { withoutRuntimeGithubToken } from './environment.mjs';
7
8
 
8
9
  // Generic dependency/cache/output directories excluded from the source fingerprint.
9
10
  // Scenarios or projects can add more via the manifest; these are the common defaults.
@@ -425,7 +426,7 @@ export function atomicJson(path, data) {
425
426
  export function killTree(child) {
426
427
  if (!child.pid) return;
427
428
  if (process.platform === 'win32') {
428
- const killer = spawn('taskkill.exe', ['/pid', String(child.pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
429
+ const killer = spawn('taskkill.exe', ['/pid', String(child.pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore', env: withoutRuntimeGithubToken() });
429
430
  killer.on('error', () => child.kill());
430
431
  } else {
431
432
  try { process.kill(-child.pid, 'SIGKILL'); } catch { child.kill('SIGKILL'); }
@@ -443,7 +444,7 @@ export async function runCommand(argv, { cwd, timeoutSeconds = 60, signal, logPa
443
444
  const started = Date.now();
444
445
  return new Promise(resolveResult => {
445
446
  let tail = '', written = 0, timedOut = false, cancelled = false, outputLimit = false;
446
- const child = spawn(argv[0], argv.slice(1), { cwd, shell: false, windowsHide: true, detached: process.platform !== 'win32', stdio: ['ignore', 'pipe', 'pipe'] });
447
+ const child = spawn(argv[0], argv.slice(1), { cwd, env: withoutRuntimeGithubToken(), shell: false, windowsHide: true, detached: process.platform !== 'win32', stdio: ['ignore', 'pipe', 'pipe'] });
447
448
  const stop = () => killTree(child);
448
449
  const abort = () => { cancelled = true; stop(); };
449
450
  const timer = setTimeout(() => { timedOut = true; stop(); }, timeoutSeconds * 1000);
@@ -0,0 +1,8 @@
1
+ /** Return a copy of an environment without the optional runtime transport token. */
2
+ export function withoutRuntimeGithubToken(env = process.env) {
3
+ const clean = { ...env };
4
+ for (const key of Object.keys(clean)) {
5
+ if (key.toUpperCase() === 'CTG_RUNTIME_GITHUB_TOKEN') delete clean[key];
6
+ }
7
+ return clean;
8
+ }
package/lib/guidance.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
2
  import { dirname, join, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { unframe } from './tui/framing.mjs';
4
+ import { unframe } from '../plugins/carthagent/src/server/framing.mjs';
5
5
 
6
6
  const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
7
7
  export const DEFAULT_PROFILE_DIR = join(root, 'guidance', 'profiles');