@visual-z/xeno 0.2.1 → 0.4.0

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 (282) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +126 -34
  3. package/docs/app-server.md +84 -0
  4. package/docs/install.md +51 -13
  5. package/docs/providers.md +24 -3
  6. package/package.json +7 -5
  7. package/src/apps/app-server.ts +28 -0
  8. package/src/apps/approve.ts +3 -3
  9. package/src/apps/cli.ts +25 -10
  10. package/src/apps/commands.ts +45 -8
  11. package/src/apps/completions.ts +78 -22
  12. package/src/apps/control-transport.ts +89 -0
  13. package/src/apps/image-draft.ts +46 -0
  14. package/src/apps/model-catalog.ts +2 -1
  15. package/src/apps/project-trust.ts +2 -42
  16. package/src/apps/provider-connect.ts +346 -0
  17. package/src/apps/render.ts +22 -47
  18. package/src/apps/resources.ts +5 -2
  19. package/src/apps/shell.ts +333 -1542
  20. package/src/apps/startup-input.ts +45 -9
  21. package/src/apps/startup.ts +149 -94
  22. package/src/apps/ui/app.ts +1553 -0
  23. package/src/apps/ui/control-model.ts +26 -0
  24. package/src/apps/ui/extension-host.ts +194 -0
  25. package/src/apps/ui/image-cache.ts +78 -0
  26. package/src/apps/ui/palette.ts +88 -0
  27. package/src/apps/ui/project.ts +127 -0
  28. package/src/apps/ui/standalone.ts +146 -0
  29. package/src/apps/ui/status-model.ts +68 -0
  30. package/src/apps/ui/surface.ts +86 -0
  31. package/src/apps/ui/theme.ts +86 -0
  32. package/src/core/acceptance.ts +107 -0
  33. package/src/core/action-defaults.ts +47 -295
  34. package/src/core/benchmark.ts +2 -2
  35. package/src/core/branch-receipt.ts +24 -0
  36. package/src/core/checkpoints.ts +64 -0
  37. package/src/core/churn.ts +2 -0
  38. package/src/core/command-artifacts.ts +94 -0
  39. package/src/core/command-contract.ts +35 -0
  40. package/src/core/completion-action.ts +20 -0
  41. package/src/core/completion-review.ts +37 -21
  42. package/src/core/context-detail.ts +37 -0
  43. package/src/core/context-policy.ts +74 -1
  44. package/src/core/continuation.ts +17 -2
  45. package/src/core/control-protocol.ts +62 -0
  46. package/src/core/control-view.ts +42 -0
  47. package/src/core/derive.ts +29 -3
  48. package/src/core/draft-images.ts +56 -0
  49. package/src/core/environment.ts +145 -0
  50. package/src/core/evaluation-coverage.ts +74 -0
  51. package/src/core/event-validation.ts +81 -6
  52. package/src/core/events.ts +99 -3
  53. package/src/core/evidence-links.ts +7 -0
  54. package/src/core/evidence.ts +13 -3
  55. package/src/core/execution.ts +8 -2
  56. package/src/core/experiment.ts +133 -0
  57. package/src/core/export.ts +9 -0
  58. package/src/core/failure-recovery.ts +173 -0
  59. package/src/core/file-precondition.ts +72 -0
  60. package/src/core/handoff.ts +65 -4
  61. package/src/core/harness-diagnostics.ts +206 -0
  62. package/src/core/history-query.ts +38 -2
  63. package/src/core/history.ts +38 -1
  64. package/src/core/integration-health.ts +34 -0
  65. package/src/core/keymap.ts +8 -20
  66. package/src/core/model-picker.ts +64 -0
  67. package/src/core/native-eval.ts +25 -0
  68. package/src/core/note-guidance.ts +34 -0
  69. package/src/core/oauth-fields.ts +44 -0
  70. package/src/core/observation-view.ts +49 -8
  71. package/src/core/permissions.ts +24 -2
  72. package/src/core/process-receipt.ts +105 -0
  73. package/src/core/process-state.ts +37 -0
  74. package/src/core/program-status.ts +31 -0
  75. package/src/core/project.ts +70 -19
  76. package/src/core/prompt-policy.ts +37 -0
  77. package/src/core/query-eval-contract.ts +14 -0
  78. package/src/core/read-observation.ts +82 -0
  79. package/src/core/recovery-corpus.ts +17 -0
  80. package/src/core/recovery-evaluation.ts +60 -0
  81. package/src/core/recovery-sources.ts +74 -0
  82. package/src/core/request-progress.ts +87 -0
  83. package/src/core/request-state.ts +114 -0
  84. package/src/core/runtime-target.ts +94 -0
  85. package/src/core/search-observation.ts +97 -0
  86. package/src/core/session-tree.ts +147 -0
  87. package/src/core/settings.ts +33 -10
  88. package/src/core/skills.ts +44 -0
  89. package/src/core/sql-query.ts +211 -0
  90. package/src/core/theme.ts +36 -58
  91. package/src/core/tool-contract.ts +51 -2
  92. package/src/core/tool-discovery.ts +87 -0
  93. package/src/core/tool-failure.ts +60 -0
  94. package/src/core/tool-input.ts +6 -0
  95. package/src/core/tool-mode-protocol.ts +25 -0
  96. package/src/core/transcript.ts +19 -4
  97. package/src/core/trial-lifecycle.ts +14 -0
  98. package/src/core/tui-mode.ts +29 -0
  99. package/src/core/update-check.ts +97 -0
  100. package/src/core/version.ts +55 -0
  101. package/src/core/work-state.ts +49 -3
  102. package/src/core/working-set.ts +91 -0
  103. package/src/runtime/auth.ts +11 -4
  104. package/src/runtime/branch-jobs.ts +36 -0
  105. package/src/runtime/catalog.ts +13 -3
  106. package/src/runtime/clipboard.ts +15 -4
  107. package/src/runtime/command-artifacts.ts +64 -0
  108. package/src/runtime/control-connection.ts +104 -0
  109. package/src/runtime/environment-tools.ts +60 -0
  110. package/src/runtime/evidence-notes.ts +4 -2
  111. package/src/runtime/exec-sessions.ts +206 -61
  112. package/src/runtime/execution-control.ts +1 -0
  113. package/src/runtime/extension-ui.ts +22 -30
  114. package/src/runtime/extensions.ts +95 -16
  115. package/src/runtime/harness-comparison.ts +27 -7
  116. package/src/runtime/harness-eval.ts +462 -0
  117. package/src/runtime/history-cache.ts +57 -0
  118. package/src/runtime/history-tools.ts +62 -12
  119. package/src/runtime/integrations.ts +210 -0
  120. package/src/runtime/loop.ts +1088 -282
  121. package/src/runtime/model-discovery.ts +208 -0
  122. package/src/runtime/model.ts +9 -0
  123. package/src/runtime/native-eval-fixtures.ts +33 -0
  124. package/src/runtime/native-eval.ts +146 -0
  125. package/src/runtime/oauth.ts +80 -3
  126. package/src/runtime/pi-provider.ts +169 -12
  127. package/src/runtime/program-status.ts +38 -0
  128. package/src/runtime/provider-connect.ts +109 -0
  129. package/src/runtime/provider-presets.ts +87 -0
  130. package/src/runtime/provider-request.ts +13 -2
  131. package/src/runtime/provider.ts +23 -9
  132. package/src/runtime/query-eval-verifier.ts +28 -0
  133. package/src/runtime/recovery-coverage.ts +71 -0
  134. package/src/runtime/recovery-evidence.ts +76 -0
  135. package/src/runtime/recovery-fixtures.ts +116 -0
  136. package/src/runtime/runtime-target.ts +149 -0
  137. package/src/runtime/session-control.ts +198 -0
  138. package/src/runtime/tool-broker.ts +137 -0
  139. package/src/runtime/tool-discovery.ts +57 -0
  140. package/src/runtime/tool-mode-eval.ts +146 -0
  141. package/src/runtime/tool-queue.ts +77 -0
  142. package/src/runtime/tools.ts +628 -183
  143. package/src/runtime/trial-control.ts +45 -0
  144. package/src/runtime/update-check.ts +47 -0
  145. package/src/runtime/window-trial.ts +2 -1
  146. package/src/runtime/working-notes.ts +33 -3
  147. package/src/runtime/workspace-lifecycle.ts +97 -0
  148. package/src/store/acceptance.ts +15 -0
  149. package/src/store/checkpoint-history.ts +43 -0
  150. package/src/store/command-artifacts.ts +96 -0
  151. package/src/store/command.ts +65 -37
  152. package/src/store/file-checkpoints.ts +104 -0
  153. package/src/store/file-index.ts +13 -1
  154. package/src/store/git-checkpoints.ts +67 -0
  155. package/src/store/observations.ts +61 -20
  156. package/src/store/process-receipts.ts +34 -0
  157. package/src/store/query-source.ts +13 -8
  158. package/src/store/recovery.ts +2 -3
  159. package/src/store/replay.ts +16 -4
  160. package/src/store/runtime-target.ts +64 -0
  161. package/src/store/search.ts +171 -132
  162. package/src/store/session-archive.ts +4 -0
  163. package/src/store/source-cancellation.ts +6 -0
  164. package/src/store/update-cache.ts +36 -0
  165. package/src/store/workspace-observation.ts +113 -0
  166. package/src/tui/ansi-spans.ts +126 -0
  167. package/src/tui/style.ts +19 -16
  168. package/src/tui/ui/atomic-draft.ts +121 -0
  169. package/src/tui/ui/canvas.ts +96 -0
  170. package/src/tui/ui/colors.ts +70 -0
  171. package/src/tui/ui/composer.ts +200 -0
  172. package/src/tui/ui/control-panel.ts +23 -0
  173. package/src/tui/ui/dialog.ts +379 -0
  174. package/src/tui/ui/draft.ts +23 -0
  175. package/src/tui/ui/edge.ts +186 -0
  176. package/src/tui/ui/image-size.ts +40 -0
  177. package/src/tui/ui/ink.ts +210 -0
  178. package/src/tui/ui/items.ts +40 -0
  179. package/src/tui/ui/keys.ts +40 -0
  180. package/src/tui/ui/layout.ts +74 -0
  181. package/src/tui/ui/list-model.ts +86 -0
  182. package/src/tui/ui/list-view.ts +172 -0
  183. package/src/tui/ui/mascot.ts +67 -0
  184. package/src/tui/ui/messages.ts +518 -0
  185. package/src/tui/ui/motion.ts +28 -0
  186. package/src/tui/ui/paths.ts +10 -0
  187. package/src/tui/ui/pictures.ts +78 -0
  188. package/src/tui/ui/screen.ts +421 -0
  189. package/src/tui/ui/status.ts +279 -0
  190. package/src/tui/ui/styled.ts +85 -0
  191. package/src/tui/ui/testing.ts +5 -0
  192. package/src/tui/ui/tool-card.ts +150 -0
  193. package/src/apps/extension-ui.ts +0 -330
  194. package/src/apps/resource-browser.ts +0 -75
  195. package/src/apps/session-browser.ts +0 -113
  196. package/src/apps/session-picker.ts +0 -55
  197. package/src/tui/ansi.ts +0 -67
  198. package/src/tui/autocomplete.ts +0 -29
  199. package/src/tui/chat-viewport.ts +0 -47
  200. package/src/tui/complete.ts +0 -87
  201. package/src/tui/editor.ts +0 -940
  202. package/src/tui/host.ts +0 -264
  203. package/src/tui/input.ts +0 -75
  204. package/src/tui/keys.ts +0 -378
  205. package/src/tui/markdown.ts +0 -17
  206. package/src/tui/render.ts +0 -153
  207. package/src/tui/select.ts +0 -258
  208. package/src/tui/settings.ts +0 -44
  209. package/src/tui/statusline.ts +0 -162
  210. package/src/tui/system-theme.ts +0 -700
  211. package/src/tui/terminal.ts +0 -30
  212. package/src/tui/text.ts +0 -52
  213. package/src/tui/toolview.ts +0 -386
  214. package/src/tui/transcript.ts +0 -123
  215. package/src/tui/tree.ts +0 -471
  216. package/src/vendor/pi-tui/LICENSE.upstream +0 -21
  217. package/src/vendor/pi-tui/UPSTREAM.json +0 -50
  218. package/src/vendor/pi-tui/VENDOR.md +0 -25
  219. package/src/vendor/pi-tui/alt-screen-search.ts +0 -327
  220. package/src/vendor/pi-tui/autocomplete.ts +0 -860
  221. package/src/vendor/pi-tui/colors.ts +0 -367
  222. package/src/vendor/pi-tui/components/alt-screen-flash.ts +0 -51
  223. package/src/vendor/pi-tui/components/box.ts +0 -166
  224. package/src/vendor/pi-tui/components/cancellable-loader.ts +0 -40
  225. package/src/vendor/pi-tui/components/editor.ts +0 -2472
  226. package/src/vendor/pi-tui/components/h-stack.ts +0 -44
  227. package/src/vendor/pi-tui/components/image.ts +0 -127
  228. package/src/vendor/pi-tui/components/input.ts +0 -494
  229. package/src/vendor/pi-tui/components/loader.ts +0 -101
  230. package/src/vendor/pi-tui/components/markdown.ts +0 -1021
  231. package/src/vendor/pi-tui/components/mouse-region.ts +0 -33
  232. package/src/vendor/pi-tui/components/scroll-view.ts +0 -224
  233. package/src/vendor/pi-tui/components/select-list.ts +0 -273
  234. package/src/vendor/pi-tui/components/settings-list.ts +0 -328
  235. package/src/vendor/pi-tui/components/spacer.ts +0 -28
  236. package/src/vendor/pi-tui/components/stack.ts +0 -154
  237. package/src/vendor/pi-tui/components/text.ts +0 -107
  238. package/src/vendor/pi-tui/components/truncated-text.ts +0 -65
  239. package/src/vendor/pi-tui/components/v-stack.ts +0 -33
  240. package/src/vendor/pi-tui/editor-component.ts +0 -74
  241. package/src/vendor/pi-tui/fuzzy.ts +0 -138
  242. package/src/vendor/pi-tui/index.ts +0 -186
  243. package/src/vendor/pi-tui/keybindings.ts +0 -320
  244. package/src/vendor/pi-tui/keys.ts +0 -1401
  245. package/src/vendor/pi-tui/kill-ring.ts +0 -46
  246. package/src/vendor/pi-tui/latex.ts +0 -1506
  247. package/src/vendor/pi-tui/layout-node.ts +0 -51
  248. package/src/vendor/pi-tui/layout.ts +0 -449
  249. package/src/vendor/pi-tui/native/UPSTREAM.json +0 -23
  250. package/src/vendor/pi-tui/native/clipboard.h +0 -208
  251. package/src/vendor/pi-tui/native/darwin/README.md +0 -22
  252. package/src/vendor/pi-tui/native/darwin/build.sh +0 -69
  253. package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  254. package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  255. package/src/vendor/pi-tui/native/darwin/src/darwin-platform.m +0 -132
  256. package/src/vendor/pi-tui/native/linux/README.md +0 -23
  257. package/src/vendor/pi-tui/native/linux/build.sh +0 -65
  258. package/src/vendor/pi-tui/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
  259. package/src/vendor/pi-tui/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
  260. package/src/vendor/pi-tui/native/linux/src/clipboard-worker.h +0 -70
  261. package/src/vendor/pi-tui/native/linux/src/linux-platform-x11.c +0 -274
  262. package/src/vendor/pi-tui/native/napi.h +0 -91
  263. package/src/vendor/pi-tui/native/win32/README.md +0 -31
  264. package/src/vendor/pi-tui/native/win32/build.mjs +0 -232
  265. package/src/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
  266. package/src/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
  267. package/src/vendor/pi-tui/native/win32/src/win32-platform.c +0 -241
  268. package/src/vendor/pi-tui/native-modifiers.ts +0 -13
  269. package/src/vendor/pi-tui/native-module-path.ts +0 -31
  270. package/src/vendor/pi-tui/native-platform.ts +0 -65
  271. package/src/vendor/pi-tui/oklab.ts +0 -233
  272. package/src/vendor/pi-tui/stdin-buffer.ts +0 -444
  273. package/src/vendor/pi-tui/terminal-colors.ts +0 -91
  274. package/src/vendor/pi-tui/terminal-image.ts +0 -731
  275. package/src/vendor/pi-tui/terminal.ts +0 -554
  276. package/src/vendor/pi-tui/tui-alt-screen.ts +0 -1750
  277. package/src/vendor/pi-tui/tui-main-screen.ts +0 -655
  278. package/src/vendor/pi-tui/tui.ts +0 -1493
  279. package/src/vendor/pi-tui/undo-stack.ts +0 -28
  280. package/src/vendor/pi-tui/utils.ts +0 -1385
  281. package/src/vendor/pi-tui/wheel-scroll.ts +0 -82
  282. package/src/vendor/pi-tui/word-navigation.ts +0 -117
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # xeno changelog
2
2
 
3
+ ## 0.4.0 — 2026-10-09
4
+
5
+ - Add the goal, evidence progress and Context Lens workbench through `/control`. Keep execution outcomes separate from verified acceptance and explain the sources retained in the actual model context.
6
+ - Share one live Session between the TUI and the experimental `xeno-control/0.1` JSONL app server or private Unix socket. Expose initialization, thread/turn/item identity, snapshots, steering, cancellation and approval; include the interface guide in the package.
7
+ - Report terminal state through OSC 7501 with capability detection, explicit opt-outs, private program identity and generic messages that exclude prompts and model output.
8
+ - Preserve cancellation when a provider returns late. Deliver TUI steering and follow-ups into remotely started turns at their original boundaries, and return undelivered drafts after interruption.
9
+ - Carry original request identity, command and artifact evidence, acceptance gaps and diagnostic progress across context cuts without requiring extra model bookkeeping.
10
+ - Honor selected provider arguments, add official Grok OAuth, and preserve raw tool-call identity, response validation and measured usage/cache accounting across native provider workflows.
11
+
12
+ ## 0.3.0 — 2026-10-07
13
+
14
+ - Replace the vendored Pi terminal UI with OpenTUI: a growing composer, command palette, model and login pickers, responsive status surfaces, terminal colours and bounded message rendering.
15
+ - Keep inline image tags atomic through cursor movement, deletion, undo/redo, history and queued drafts; send only retained attachments and preview the image beside the cursor. Share bounded image decoding across drafts, messages and tool results.
16
+ - Add plain-text mouse selection copying and optional daily version notices, with offline and non-interactive opt-outs.
17
+ - Preserve tool admission and nested-call receipts, cancellable process ownership, deferred discovery, recoverable output and integration health handling. Code Mode is removed.
18
+ - Strengthen file edit preconditions, workspace lifecycle and completion evidence; opening the editor no longer requires an eager workspace snapshot.
19
+ - Require Bun 1.3.0 or newer for the native renderer. Existing session logs remain append-only.
20
+
3
21
  ## 0.2.1 — 2026-09-30
4
22
 
5
23
  - Handle Ctrl+C and termination signals while startup awaits resources or workspace snapshots, restoring the terminal before exit.
package/README.md CHANGED
@@ -17,7 +17,7 @@ is ever summarized by a model, and nothing is ever rewritten in place.
17
17
  messages[] frozen system prefix + handoff + this node's events
18
18
  ```
19
19
 
20
- Version **0.2.1**. The on-disk session format will still change; `0.x` is that
20
+ Version **0.4.0**. The on-disk session format will still change; `0.x` is that
21
21
  statement. Published as `@visual-z/xeno`; the license is `UNLICENSED` (readable and installable, no reuse granted).
22
22
 
23
23
  ## Install
@@ -47,11 +47,13 @@ Validation commands and current evidence are recorded in
47
47
  ## Harness capabilities
48
48
 
49
49
  - One live execution per session, with cancellation, draining, and explicit completion/stop/failure outcomes.
50
+ - Evidence-backed goals, progress and Context Lens in `/control`, shared with the experimental JSONL app server and optional local control socket. See [`docs/app-server.md`](docs/app-server.md) for identity, events, approval, recovery and OSC 7501 status.
50
51
  - Stable work IDs with explicit unblock, reopen, and cancel operations. Verified completion still requires fresh executed evidence.
51
52
  - Shared schema validation and structured tool feedback. Reads return file versions; edits support an expected version and return a diff.
52
53
  - Writing branches start from the parent's working copy, including uncommitted files, in an isolated Git worktree. Adopt continues that checkout.
53
54
  - Independent readers and read-only branches run in a bounded window; writes and state changes are barriers. Results remain in request order.
54
55
  - `note(kind="pin", work_id, event, what)` links original observations; `supersedes` replaces an earlier reference. `history(work_id=...)`, `history(resource=...)`, and `history(events=[...])` locate or batch-read the originals.
56
+ - Verified history projections use a bounded per-session cache. Original ancestry and sealed digests are checked before every hit; a lost cache is rebuilt from the log.
55
57
 
56
58
  Use `/new` or `--fresh` to start with the current tool protocol. Existing sessions
57
59
  keep their saved frozen schemas and concurrency policy. New sessions default to
@@ -78,6 +80,17 @@ authorized `--budget unlimited` on both `plan` and `run` records costs without a
78
80
  dollar ceiling; upstream errors stop the trial, with HTTP 402 marked as awaiting top-up.
79
81
  This checks a controlled configuration, not stock Pi or an official leaderboard.
80
82
 
83
+ The new single-factor protocol separately compares tool profiles, the carried
84
+ working set, and strict verification. It runs four isolated scenarios through
85
+ real sessions and grades files, fresh command evidence, and pending human review.
86
+ Use `bun scripts/eval-harness.ts smoke --output /absolute/new/eval-directory`,
87
+ then `bun scripts/eval-harness.ts audit --output /absolute/new/eval-directory`.
88
+ Smoke has no paid requests and reports synthetic usage. `plan` and `run` require
89
+ the same explicit numeric `--budget`; inputs, source, schemas, model settings,
90
+ and paired order are frozen before spending. Started directories cannot be
91
+ replayed automatically. Failures and missing trials remain in the denominator,
92
+ and unknown charges retain their reservation. Details are in `docs/benchmarks.md`.
93
+
81
94
  ## Usage
82
95
 
83
96
  ```
@@ -92,6 +105,8 @@ xeno [-m <model>] [--provider <name>] [--api-key <key>]
92
105
  [--system-prompt <text-or-file>] [--append-system-prompt <text>]
93
106
  [--no-context-files|-nc]
94
107
  [--tools names] [--exclude-tools names] [--no-tools]
108
+ [--tool-profile coding|readonly|research|data|full]
109
+ [--acceptance-file <contract.json>]
95
110
  [--allow-tools names] [--approve|--no-approve]
96
111
  [--theme <name|path>] [--use-theme <name>] [--no-themes]
97
112
  [--tui-mode regular|fullscreen]
@@ -102,6 +117,33 @@ xeno [-m <model>] [--provider <name>] [--api-key <key>]
102
117
  ```
103
118
 
104
119
  - No prompt, a tty: interactive session.
120
+ - `/state` shows the current request's execution outcome, command receipts,
121
+ verification state and diagnostic progress across segment cuts and restarts.
122
+ Short tasks need no `note` calls to associate evidence. Without a declared
123
+ acceptance contract, successful commands remain evidence rather than verified
124
+ task completion. Diagnostic novelty does not change the stall policy.
125
+ - `/acceptance` shows declared command checks and human criteria. `/accept ID`
126
+ records your acceptance of a human criterion; subsequent edits invalidate it.
127
+ Use `--acceptance-file` or a trusted project's `.xeno/acceptance.json` with
128
+ `{ "version": 1, "id": "task", "criteria": [{ "id": "tests", "kind": "command", "description": "tests pass", "command": "bun test" }] }`.
129
+ The declaration is frozen in the session. A normal execution exit does not
130
+ imply that human acceptance has been granted.
131
+ A contract may additionally declare `targets`, each with `id`, `build`
132
+ (a command criterion ID), `artifacts: { paths, output_roots }`, and `entry`
133
+ (one of those artifact paths). Optional `interpreter` is an absolute executable
134
+ path; `args` are literal strings, `workdir` stays inside the workspace, and
135
+ `port` declares an IPv4 loopback listener. A human criterion binds to it with
136
+ `target: "ID"`. Enable the `target` tool explicitly with `--tools` or the
137
+ `full` profile; the default coding profile stays compact. Run the declared
138
+ build with its exact artifact plan, then `target({action:"start",target:"ID"})`.
139
+ `inspect` rechecks the owned instance, build inputs, artifact/executable identity
140
+ and port ownership. `/acceptance` shows the entry, build, PID and session;
141
+ `/accept ID` binds your decision to that observed instance. Rebuilds, changed
142
+ files, process exits or lost ownership require fresh acceptance. The first
143
+ implementation observes macOS executable identity with the system `lsof` and
144
+ Linux identity with `/proc`; port checks require system `lsof`. Missing OS
145
+ evidence remains unknown. It never adopts an existing server or proves visual
146
+ correctness automatically, and a resumed session cannot reattach an old PID.
105
147
  - A prompt, or `-p` / `--print`: one shot, then exit. `-p` puts only the
106
148
  assistant's final text on stdout; the banner, usage, thinking, and tool
107
149
  lines go to stderr. Piped stdin is appended after the command-line prompt.
@@ -131,8 +173,8 @@ Slash commands that exist in the session (from `/help`):
131
173
  ```
132
174
  /model [id] search models, or switch (cuts a node)
133
175
  /models refresh refresh ~/.xeno/models-cache.json; preserve models.json
134
- /login [provider] choose a login method, or store an API key
135
- /login claude-pro|codex|github-copilot
176
+ /provider [provider] connect or manage an account, API key, gateway or local server
177
+ /provider claude-pro|codex|github-copilot|grok
136
178
  subscription login (first-party client credentials)
137
179
  /logout [provider] remove a stored credential
138
180
  /scoped-models enable, disable and order models for cycling
@@ -203,13 +245,19 @@ credentials, native cloud protocols, and explicit coverage limits.
203
245
  ## Credentials
204
246
 
205
247
  Order: `--api-key`, `~/.xeno/auth.json`, `models.json` provider/model credentials,
206
- then the provider's environment variables. A failed explicit source does not
207
- fall through to another account. `/login codex` uses the `openai-codex` protocol;
248
+ then the provider's environment variables. `/login` and `/connect` remain hidden
249
+ aliases of `/provider`. Enter confirms the selected command argument; Tab inserts it.
250
+ A failed explicit source does not
251
+ fall through to another account. `/provider codex` uses the `openai-codex` protocol;
208
252
  ordinary OpenAI API models use `openai`.
209
253
 
210
- Interactive sessions can start with no key and wait for `/login`. `-p` and
254
+ `/provider grok` uses official device authorization and refreshes the account token.
255
+ Its model is `xai-grok/grok-build` on the official CLI inference proxy. xAI API keys
256
+ remain on `xai`; credentials and rates are not shared between these accounts.
257
+
258
+ Interactive sessions can start with no key and wait for `/provider`. `-p` and
211
259
  non-tty runs exit 1 and name `--api-key` or the variable; they do not mention
212
- `/login`.
260
+ `/provider`.
213
261
 
214
262
  Keys in `auth.json` may be a literal, `!cmd`, or `$VAR`. The file is written
215
263
  mode `0600`.
@@ -231,19 +279,43 @@ Project trust (`/trust`, `--approve`) only decides whether to load
231
279
  It does not change what tools can do. `AGENTS.md` is loaded whether the
232
280
  project is trusted or not.
233
281
 
282
+ New sessions default to the nine-tool `coding` profile: read, grep, find,
283
+ write, edit, exec_command, write_stdin, history and note. `readonly`,
284
+ `research` (with branch/resolve), `data` (with query) and `full` choose other
285
+ builtin defaults. Extensions remain available; explicit `--tools` overrides
286
+ the profile and exclusions apply last. Resumed sessions keep their frozen tools.
287
+
234
288
  `--tools` / `--exclude-tools` / `--no-tools` remove tools from the schema
235
- the model sees. `--allow-tools bash` lets bash run without asking, which
289
+ the model sees. `--allow-tools exec_command` lets commands run without asking, which
236
290
  is what a CI job that has no tty has to pass explicitly. Without a tty,
237
291
  an `ask` is a deny that names that flag.
238
292
 
239
293
  ## Workspace data
240
294
 
295
+ Opening a session does not snapshot the working directory. File tools save the
296
+ original bytes before editing and record the observed result. `/back` restores
297
+ only recorded paths, preserves unrelated files, and refuses conflicting external
298
+ edits. Conversation-only navigation leaves files unchanged.
299
+
300
+ Shell effects are not automatically reversible. Opt in for a new session with
301
+ `--checkpoint-shell` to capture eligible Git files around commands. This uses a
302
+ private index and object store without changing your staging area or HEAD. Git
303
+ ignored paths, non-regular files, untracked files over 2 MiB, and effects outside
304
+ the workspace are excluded. Capture is best effort; unavailable recovery does
305
+ not prevent a conversation or change the command's exit result. A running process
306
+ has a pending checkpoint until its exit is observed; overlapping writes make that
307
+ checkpoint unavailable. Initial capture can take time.
308
+
309
+ Verification observes a bounded source scope separately from file recovery.
310
+ Unknown observations cannot certify a completion claim. Old snapshot logs remain
311
+ readable; new file records and scope markers are appended without rewriting them.
312
+
241
313
  A session writes into the workspace:
242
314
 
243
315
  | Path | |
244
316
  |---|---|
245
317
  | `.xeno/sessions/<id>/` | node logs, handoffs, metadata journal, derived manifest, and observation artifacts |
246
- | `.xeno/blobs/` | content-addressed file snapshots used by `/back` |
318
+ | `.xeno/blobs/` | content-addressed before/after file versions used by `/back` |
247
319
 
248
320
  That tree grows with every turn and every snapshot. There is no garbage
249
321
  collection yet. Dead branches from `/back` stay on disk.
@@ -297,16 +369,29 @@ Shipped, not planned:
297
369
  Repeated content, renamed completion claims and gate regressions do not reset
298
370
  the three-segment stop rule. This is an anti-stall heuristic, not proof of
299
371
  relevance or completion; [`research M.4`](docs/agent-harness-research.md) states its limits.
300
- - A terminal UI using the pinned Pi renderer, raw input, editor, autocomplete,
301
- Markdown, searchable pickers and settings. Durable history is a pure log
302
- projection across handoff boundaries. Includes tool diffs, historical folds,
303
- Pi dark/light palettes, `keybindings.json`, `/settings` and `/hotkeys`.
372
+ - A terminal UI using pinned OpenTUI, a native text editor, autocomplete,
373
+ Markdown, a command palette and searchable pickers. Durable history is a pure
374
+ log projection across handoff boundaries. Includes tool diffs, historical
375
+ folds, terminal colours, `keybindings.json`, `/settings` and `/hotkeys`.
304
376
 
305
377
  - Context files: `AGENTS.md` / `CLAUDE.md` and the override chain, plus
306
378
  `--system-prompt` / `--append-system-prompt` / `-nc`.
307
379
  - Skills (`~/.xeno/skills/`, `/skill:name`) and prompt templates
308
- (`~/.xeno/prompts/`, `/name`). Untrusted projects do not load the
380
+ support progressive loading, user visibility, explicit tool intersections
381
+ and isolated `context: fork`. Unsupported execution fields are diagnosed.
382
+ Loaded instructions keep frozen source hashes and recoverable artifacts.
383
+ Templates use `~/.xeno/prompts/` and `/name`. Untrusted projects do not load
309
384
  project-level copies.
385
+ - `/tasks` shows dependency readiness and claims. Long tasks can use
386
+ `note(kind="next", work_id="b", depends_on=["a"], ...)`, then
387
+ `claim_work` / `release_work`; these are scheduling intent, separate from
388
+ the `done` command proof. Ordinary short tasks do not need a graph.
389
+ - `branch(background=true)` returns an owned task reference. `/branches`
390
+ lists its current capability; `/branches wait|cancel|status <node>` joins or
391
+ controls it without a model request. `resolve` exposes the same actions.
392
+ Restarted unfinished tasks remain unknown; sealed outcomes can be joined.
393
+ - `/processes` separates the last logged observation from a live handle.
394
+ Session exit drains its own processes and archives unread output.
310
395
  - `/reload` refreshes local models, settings, keybindings, context files, skills,
311
396
  templates and themes without losing the draft or active model. A changed system
312
397
  prefix checkpoints first; unchanged resources do not create a new segment.
@@ -326,11 +411,15 @@ Shipped, not planned:
326
411
  `-e` tries a package for one invocation. Manifests, per-kind filters and trust
327
412
  feed the same resource inventory. Normal startup stays offline.
328
413
  See [package management](docs/packages.md).
329
- - `/login` for API keys and for Claude Pro/Max, Codex, and GitHub
330
- Copilot subscription flows. See Known gaps.
414
+ - `/provider` for API keys and for Claude Pro/Max, Codex, GitHub Copilot,
415
+ and official Grok account flows. See Known gaps.
331
416
 
332
417
  Images can be attached with `@path`, a dropped image path, or Ctrl+V (files,
333
- image, then text). `/attachments` removes draft images; queue recall and history
418
+ image, then text). Pasted and dropped images become `[Image #N]` tags at the
419
+ caret. Left/Right cross a whole tag; Backspace/Delete remove it in one step,
420
+ and Undo restores it. Moving the caret next to a tag shows its preview above
421
+ the input (filename and dimensions when image display is unavailable).
422
+ `/attachments` remains available for removal; queue recall and history
334
423
  forks retain them. The log stores immutable blob references. Provider adapters
335
424
  load verified bytes for Anthropic, Chat Completions and Responses; text-only
336
425
  models receive a visible placeholder. Custom models declare `input: ["text", "image"]`
@@ -360,27 +449,30 @@ unblocking affects new submissions, preserving historical request prefixes.
360
449
  - **`/back` does not restore symlinks.**
361
450
  - **`.xeno/` is not collected.** Snapshots and abandoned session branches
362
451
  stay until you delete them.
363
- - **No telemetry, `/share`, auto-update, or `/compact`.** Those are
364
- refusals (`docs/PI_PARITY.md` D15, D16), not backlog.
452
+ - **No telemetry, `/share`, self-installing updates, or `/compact`.** Those
453
+ are refusals (`docs/PI_PARITY.md` D15, D16), not backlog. xeno does say
454
+ when a newer version is published, at most once a day and never installing
455
+ it; `docs/install.md` (Updates) lists what that request reveals and every
456
+ way to turn it off.
365
457
 
366
458
  `docs/TUI_ARCHITECTURE.md` describes the active interaction path and verification.
367
459
  `docs/PI_PARITY.md` records deliberate differences and remaining Pi features.
368
- Pi's editor supports undo, not redo; an old `editor.redo` binding is diagnosed
369
- as unsupported rather than silently accepted. Ctrl+- invokes Pi undo.
460
+ The native editor supports undo (Ctrl+_ or Ctrl+/) and redo (Ctrl+Shift+Z).
461
+ Registered image tags move and delete as whole units and share text undo history.
370
462
  Ctrl+Z suspends the CLI on POSIX; `fg` restores the terminal and unsent draft.
371
463
 
372
- `/settings tuiMode fullscreen` uses Pi's alternate-screen viewport, transcript
373
- search (Ctrl+Shift+F), prompt navigation (Ctrl+Up/Down), mouse scrolling and text
374
- selection. `/settings fullscreenExitOutput resume-hint` leaves a resume command
375
- when exiting; `transcript` prints the conversation. Switching modes preserves
376
- the editor, history and undo. Scrollbar, wheel speed, copy-on-select, editor
377
- padding, autocomplete rows, cursor, output padding and double-Escape behavior
378
- are configurable in `/settings`.
464
+ Fullscreen is the default. Ctrl+P opens commands, Ctrl+L selects a model,
465
+ PgUp/PgDn scroll, and Ctrl+Home/End jump to the start or latest messages.
466
+ Ctrl+O expands tools, Ctrl+T expands thinking, and Ctrl+B toggles the sidebar.
467
+ Mouse selection copies plain text when `fullscreenCopyOnSelect` is enabled;
468
+ Ctrl+X copies a selection or the last reply. Exiting restores the terminal and
469
+ prints the conversation and a resume command. The composer stays at the bottom
470
+ and grows with the draft; pickers and forms use the same input area.
379
471
 
380
472
  `/settings theme` previews built-in and custom themes from the config directory's
381
473
  `themes/`. Escape restores the prior theme. Custom files reload after valid edits;
382
- `auto` follows terminal light/dark changes and `system` derives Pi's palette from
383
- terminal color replies. Pi's namespaced action IDs work in `keybindings.json`
474
+ `auto` follows terminal light/dark changes. Built-in themes keep the terminal's
475
+ background and derive readable accents from its colour replies. Namespaced action IDs work in `keybindings.json`
384
476
  alongside existing xeno names; an empty array disables an action in its own scope.
385
477
 
386
478
  ## Why this shape
@@ -575,10 +667,10 @@ Every path below was checked with `test -f` on this checkout.
575
667
  | `runtime/benchmark.ts` | actual Session trials, bounded spending and cache diagnostics |
576
668
  | `runtime/loop.ts` | orchestration; every judgement delegates to a pure function in core |
577
669
  | `runtime/auth.ts` / `oauth.ts` | credential order and subscription flows |
578
- | **`src/vendor/pi-tui/`** | unmodified Pi terminal and component library, pinned in `VENDOR.md` |
579
- | **`src/tui/`** | Pi input, component and theme adapters; no harness imports |
580
- | `tui/markdown.ts` / `toolview.ts` / `statusline.ts` / `tree.ts` | display |
581
- | **`src/apps/`** | `cli.ts` — Pi main-screen UI for TTY, clean print/JSON output otherwise |
670
+ | **`src/tui/`** | OpenTUI view components and pure display helpers; no harness imports |
671
+ | `tui/ui/composer.ts` / `atomic-draft.ts` / `messages.ts` / `screen.ts` | native editing, atomic image tags, messages and layout |
672
+ | **`src/apps/`** | `cli.ts` — OpenTUI for TTY, clean print/JSON output otherwise |
673
+ | `apps/ui/app.ts` / `project.ts` / `status-model.ts` | controller, transcript projection and status facts |
582
674
  | `apps/startup.ts` / `shell.ts` / `commands.ts` | flags, session, slash commands |
583
675
 
584
676
  ## Properties the tests pin
@@ -0,0 +1,84 @@
1
+ # 会话控制接口与 TUI 工作台
2
+
3
+ 状态:实验性 `xeno-control/0.1`。TUI 和适配器共用一个 `SessionControl`、一个实际 Session 和同一份 append-only 日志。协议借鉴 Codex App Server 的生命周期,**不声明与 Codex v2 wire format 兼容**。没有新增模型调用、第二个执行进程或独立目标数据库。
4
+
5
+ ## 从源码使用
6
+
7
+ ```bash
8
+ bun run agent app-server schema
9
+ bun run agent app-server --model openai-codex/gpt-6-luna
10
+ bun run agent --control-socket /tmp/xeno-control-private/control.sock
11
+ ```
12
+
13
+ 第一条只打印版本、请求参数 schema、通知清单及限制,不加载模型凭证。第二条用 stdin/stdout 双向 JSONL;诊断在 stderr。启动会话仍采用原有的凭证、工具权限、`--session` / `--continue` 和 `--acceptance-file` 参数。提示从 `turn/start` 提交,不能作为位置参数。结构化模式不构造 TUI,也不发送 OSC。
14
+
15
+ 第三条正常启动 TUI,Unix socket 连接到它当前的同一个会话。父目录必须由当前用户拥有且权限为 0700;不存在时自动创建,socket 为 0600。已有路径拒绝覆盖。这里没有 TCP、Web 面板或远程认证服务。每个适配器都是具有当前用户本地控制能力的客户端,可启动工具和答复审批。
16
+
17
+ ## 初始化与恢复
18
+
19
+ 每行一个 JSON 对象;省略 `jsonrpc`。请求必须有字符串或安全整数 `id`,响应返回相同 `id` 及 `result` 或 `error`。只有 `initialized` 是客户端无 id 通知。所有其他方法必须等初始化完成。
20
+
21
+ ```json
22
+ {"id":1,"method":"initialize","params":{"clientInfo":{"name":"my-adapter","version":"0.1"}}}
23
+ {"method":"initialized"}
24
+ ```
25
+
26
+ 读取 initialize 的 `result.threadId` 和 `result.streamId` 后,提交:
27
+
28
+ ```json
29
+ {"id":2,"method":"thread/subscribe","params":{"threadId":"<returned-threadId>"}}
30
+ {"id":3,"method":"turn/start","params":{"threadId":"<returned-threadId>","input":"Inspect the failing test and report the evidence."}}
31
+ ```
32
+
33
+ 通知可以早于对应请求响应,客户端应按 request id 关联响应。`thread/subscribe` 先订阅,再返回快照;应先缓存这期间的通知,拿到快照后只应用同一 streamId 且 sequence 大于快照 sequence 的通知。sequence 是控制器生命周期内递增的通知序号,包含瞬时输出,不能作为持久日志地址。
34
+
35
+ - `threadId` 是规范化绝对 session 目录路径的 SHA-256。同路径冷恢复保持身份;移动或复制目录会改变身份,不是可携带 UUID。
36
+ - `streamId` 每个活控制器生成一次。重启会变化;`turnId` 等于这一轮原始 user event ID;steering 引用这个请求,不另开目标。
37
+ - `item/completed` 在持久 append 成功后发送 `{node,id,type}`。完整事件通过 `item/read` 读取,唯一地址是 node + event id。
38
+ - 断线、序号跳跃或 streamId 改变:重新初始化、订阅,消费返回快照,再分页读 `item/list` / `item/read`。可给订阅传 `after:{streamId,sequence}`;游标与当前快照不同就返回 `resyncRequired:true`。
39
+ - 不保存、重放瞬时文本 delta,不重放命令。控制请求不具备幂等重试保证;丢失 `turn/start` 响应时先查看当前 turn 和日志,不能盲目重发。
40
+ - TUI 切换 Session 后,旧 threadId 拒绝继续控制,适配器应建立新连接重新初始化。当前协议没有多会话列表、创建或切换方法。
41
+
42
+ ## 方法与事件
43
+
44
+ `app-server schema` 输出的 `methods` 是实际入参验证器共用的 schema;目前不是所有响应类型的完整 JSON Schema/生成式 SDK。
45
+
46
+ | 方法 | 除 threadId 外的参数 | 结果 / 作用 |
47
+ |---|---|---|
48
+ | `thread/read` | 无 | 当前目标、执行、进展、上下文和待审批快照 |
49
+ | `thread/subscribe` | 可选 after | 订阅通知并返回快照及 resyncRequired |
50
+ | `turn/start` | input | 开始唯一执行,返回 turnId;忙时拒绝 |
51
+ | `turn/steer` | turnId, input | 排队,在原有模型/工具边界消费;最多 32 项 |
52
+ | `turn/interrupt` | turnId | 取消并清理当前执行;完成由 turn/completed 确认 |
53
+ | `approval/respond` | turnId, approvalId, choice | once / command / tool / deny;过期身份拒绝 |
54
+ | `acceptance/accept` | requestId, criterionId | 只在 idle 显式批准本请求已声明的人验条件 |
55
+ | `context/read` | focus,可选 offset / limit | messages / omitted / history 来源分页,不改模型输入 |
56
+ | `item/list` | 可选 after:{node,event} / limit | 当前活动路径的日志身份分页 |
57
+ | `item/read` | node, event | 当前活动路径上的完整日志事件 |
58
+
59
+ 分页 limit 为 1–100,默认 50。切换路径后旧游标可能失效,应重新获取快照。完整单条事件超过帧上限会报错;此版本不提供大事件分块下载。
60
+
61
+ 通知包括 `turn/started`、`turn/event`(原有 LoopEvent 的瞬时进展)、`item/completed`、`turn/completed`、`turn/error`、`turn/steeringQueued`、`approval/request`、`approval/resolved`、`thread/statusChanged`。每条带 threadId / streamId / sequence,活动 turn 存在时带 turnId。`turn/completed.steeringUndelivered` 明确没有消费的排队输入;客户端不能将“已入队”显示为“已执行”。provider 抛错时仍从持久日志读取 outcome,错误通知不透出原始 provider 报文。
62
+
63
+ 日志是事实来源。观察者得到独立副本,修改或抛错不能改变执行。刷新 idle 快照沿原有验收观察边界重新检查工作区,可能追加失效证据;这不提交提示或调用模型。活动目标和快照游标采用同一个已发布事件边界;切段期间无法一致读取时返回刷新错误。
64
+
65
+ ## 审批、断连与限制
66
+
67
+ 审批是同一个权限请求;TUI 或适配器答复后另一端的待审批状态同时结束。取消清理审批承诺,不允许晚到 provider 响应覆盖取消状态或继续执行工具。stdio 是独立会话的主控制连接:EOF/关闭会拒绝审批并取消执行。Unix socket 客户端断开只撤掉该观察者,TUI 继续拥有当前审批与执行。
68
+
69
+ 输入单帧上限 1 MiB,每连接最多排队 32 请求,输出缓冲上限 2 MiB,socket 最多 32 客户端;这些是传输容量假设,不是模型策略阈值。超限断开或返回有界错误。错误码:`-32600` envelope、`-32601` 未知方法、`-32602` 参数、`-32700` JSON、`-32000` 生命周期/执行冲突、`-32001` 传输容量、`-32002` 响应过大。快照本身过大时使用分页 inspector;超大目标详情需要后续协议扩展。
70
+
71
+ ## TUI 与终端状态
72
+
73
+ 常驻 Goal 区展示原始目标、验收状态、执行状态和观察/重试数。`/control` 打开同一会话的面板:Tab 切 Goal / Progress / Context Lens,左右键切上下文来源/省略事件/handoff 恢复引用,上下键翻阅,R 刷新,I 取消,A 选择当前声明的人验条件。远程开始的普通 turn 会更新当前 TUI,并可在面板取消。TUI 输入的 steering 在同一 turn 的原有边界消费,followup 等原 turn 释放后执行;远程取消也把未发送内容归还编辑器。既有隔离 skill / shell 保留各自执行入口,当前协议不新增远程 skill/shell 方法。
74
+
75
+ 无验收合同保持 `unverified`;模型结束 `model_end` 不等于目标 `verified`。Progress 的观察、产物、验收、新诊断和原样重试均带日志来源;数量不是完成百分比。Context Lens 复用实际 dispatch 的投影,只过滤检查视图,不修改 frozen prefix、handoff 或消息。
76
+
77
+ OSC 7501 遵循 [Program Status Protocol](https://www.superlogical.com/rex/docs/build/program-status) 的状态、身份及 UTF-8 base64 消息语法:
78
+
79
+ - `XENO_PROGRAM_STATUS=auto`(默认):TTY 探测成功后才发送状态;未回复时只有一次探测,不发送报告。
80
+ - `XENO_PROGRAM_STATUS=1`:显式开启;`0`:关闭。管道始终关闭;auto 在 tmux/screen 内关闭,可用 1 显式覆盖。
81
+ - running → working,审批/认证/提问 → blocked + kind,取消 → idle,失败 → error。结束使用 done,同时固定消息明确是否通过目标合同;待人验保持 blocked。
82
+ - 不发送用户提示、模型输出、凭证或假百分比。每进程只操作自己的 `xeno-<pid>` 记录;退出清理瞬时记录,保留规范要求可留存的 done/error。
83
+
84
+ 自动验证包含真实 TUI、真实 Unix socket、CLI JSONL 与 PTY 探测;不等同于作者在所有终端上的视觉验收。配对实验及真实历史语料统计见[研究报告](research/session-control-evaluation-2026-10-08.md)。
package/docs/install.md CHANGED
@@ -16,7 +16,7 @@ The entry point is `src/apps/cli.ts` with `#!/usr/bin/env bun`. The
16
16
  process uses Bun APIs (`Bun.file`, `Bun.spawn`, `Bun.spawnSync`,
17
17
  `for await (const line of console)`). Those are why the runtime is Bun.
18
18
 
19
- `engines.bun` is `>=1.1.0`. This checkout was run on **Bun 1.3.10**.
19
+ `engines.bun` is `>=1.3.0` (required by the OpenTUI renderer, D20). This checkout was run on **Bun 1.3.10**.
20
20
  Node is untested. That is a fact about this tree, not a promise that
21
21
  Node support is coming.
22
22
 
@@ -30,16 +30,14 @@ cd xeno
30
30
  bun install
31
31
  ```
32
32
 
33
- `bun install` pulls the provider SDK, Pi text layout/Markdown dependencies,
34
- syntax highlighting, Mermaid, Photon image processing, and TypeScript tooling.
33
+ `bun install` pulls the provider SDKs, OpenTUI and its platform renderer,
34
+ Markdown, syntax highlighting, Mermaid, Photon image processing, and TypeScript tooling.
35
35
  The exact versions are in `package.json` and `bun.lock`.
36
36
 
37
- Pi native helpers for macOS, Linux X11 and Windows (arm64/x64) ship with the
38
- package. They are pinned upstream builds with matching source and hashes;
39
- startup does not download or compile them. Missing platform support uses the
40
- clipboard command fallbacks. The packaged macOS helper was load-tested on
41
- this machine; Linux/Windows payloads and routing are checked without claiming
42
- native device acceptance.
37
+ OpenTUI supplies the native renderer through its platform dependencies.
38
+ Clipboard integration uses terminal OSC 52 and platform commands; the old
39
+ vendored Pi native helpers are no longer packaged. Release package checks run
40
+ on macOS arm64; they do not establish Linux or Windows device acceptance.
43
41
 
44
42
  Run the CLI without linking:
45
43
 
@@ -68,10 +66,49 @@ Each invocation starts a new session. `xeno -c` continues the newest one;
68
66
  and nothing is deleted. Resume restores the saved model. An explicit
69
67
  `--model` switches at a checkpoint while keeping the original log intact.
70
68
 
71
- Startup loads resources and establishes a workspace snapshot before the editor
72
- opens. Slow initialization shows its current stage; Ctrl+C cancels startup and
73
- restores the terminal. Snapshots cover the selected workspace, so start inside
74
- the intended project or pass `--workspace <path>`.
69
+ Startup loads resources and opens or restores the session. Workspace snapshots
70
+ are deferred until file tools need them, so opening an editor does not scan the
71
+ whole project. Slow initialization shows its current stage; Ctrl+C cancels it
72
+ and restores the terminal. Start inside the intended project or pass
73
+ `--workspace <path>`.
74
+
75
+ ## Updates
76
+
77
+ xeno says when a newer version is published. It never installs one: the
78
+ notice names the command, and you run it.
79
+
80
+ ```
81
+ xeno 0.4.0 is available (you have 0.3.0)
82
+ bun add -g @visual-z/xeno@latest
83
+ ```
84
+
85
+ The command follows where this copy is installed: `bun add -g` under
86
+ `~/.bun/install/global/`, `npm i -g` under an npm global prefix
87
+ (`…/lib/node_modules/`, or `…\npm\node_modules\` on Windows). Anywhere else
88
+ the notice points to this file. A checkout updates with `git pull`.
89
+
90
+ - **Request.** `GET https://registry.npmjs.org/-/package/@visual-z%2fxeno/dist-tags`
91
+ (an 18-byte answer). If that fails, `GET …/@visual-z%2fxeno/latest`.
92
+ `npm_config_registry` replaces the host. The timeout is 3 seconds.
93
+ - **What the registry learns.** Your IP address, the time, the package name
94
+ and Bun's default `User-Agent` (`Bun/<version>`). No identifier, no query
95
+ string, nothing from your session.
96
+ - **How often.** At most once every 24 hours. The time and the version seen
97
+ are kept in `~/.xeno/update-check.json`. A failed attempt is recorded too,
98
+ so a closed network is not retried on every start.
99
+ - **When.** In the background after the editor opens. Startup never waits for
100
+ it, and quitting cancels it. A version found during a session is shown only
101
+ while that session is still empty. Otherwise it waits for the next start,
102
+ so it never interrupts a turn.
103
+
104
+ Nothing is sent when any of these holds:
105
+
106
+ - `--offline` or `XENO_OFFLINE=1`
107
+ - `XENO_NO_UPDATE_CHECK` or `NO_UPDATE_NOTIFIER` is set (to anything but empty, `0` or `false`)
108
+ - `"updateCheck": false` in `settings.json` (*Check for updates* in `/settings`)
109
+ - `CI` or `CONTINUOUS_INTEGRATION` is set, by the same rule
110
+ - the run is not the interactive app: `-p`, `--mode json`, `--export`, `--list-models`, `models --refresh`, package and `config` commands, `--version`
111
+ - xeno runs from a checkout: its package root is not under `node_modules`, or has a `.git` (`bun src/apps/cli.ts`, `bun run agent`, `bun link`)
75
112
 
76
113
  ## Credentials
77
114
 
@@ -129,6 +166,7 @@ settings.json theme, thinking, defaults
129
166
  trust.json which directories may load project settings
130
167
  models.json optional user provider/model configuration
131
168
  models-cache.json generated catalog cache (written by `xeno models --refresh`)
169
+ update-check.json when the last update check ran, and the newest version it saw
132
170
  keybindings.json optional
133
171
  prompts/ skills/ optional
134
172
  ```
package/docs/providers.md CHANGED
@@ -1,9 +1,30 @@
1
1
  # 供应商、模型目录与自定义配置
2
2
 
3
- xeno 内置 Pi 0.99.1 和 OpenCode 所用 Models.dev 的固定源并集:226 个供应商、7,501 个模型条目。同一个模型在不同供应商下分别计数。目录保留能力与已知价格;未提供的价格显示未知,不用零补全。
3
+ xeno 内置 Pi 0.99.1 和 OpenCode 所用 Models.dev 的固定源并集,另加 Grok 官方账户代理入口:227 个供应商、7,502 个模型条目。同一个模型在不同供应商下分别计数。目录保留能力与已知价格;未提供的价格显示未知,不用零补全。
4
4
 
5
5
  来源固定为 Pi `1b347794e2a630e4359f2584f4eea388145d0ddf`、OpenCode `2fa3363c924c5c3e367b84a87ae478296a0ed59b`,Models.dev JSON SHA256 `7345de7358c471e78ac756aed556cd804677cd0d8748b71edc3931225440158f`。SDK 固定为 `@earendil-works/pi-ai@0.99.1`;引入它是为了复用维护中的原生协议和签名处理,避免在 xeno 重写十套 SDK。SDK 仅在 runtime;原有 TUI vendor 的版本与升级方式独立。
6
6
 
7
+ ## 最快的接入:`/provider`
8
+
9
+ 在交互界面输入 `/provider`(`/provider <provider>` 直达;`/login`、`/connect` 是兼容别名)。一个列表里包含全部接入方式。Enter 执行选中的命令或参数,Tab 只插入补全;文件引用只插入,不提交提示:
10
+
11
+ - **账户**:Claude Pro/Max、ChatGPT Plus/Pro(Codex)、GitHub Copilot、Grok 官方账户,走浏览器或设备码登录。
12
+ - **API key**:选择目录中的供应商(Anthropic、OpenAI、Gemini、DeepSeek、OpenRouter、Kimi、智谱、硅基流动、通义、豆包等,可搜索),粘贴 key(掩码显示;`$MY_KEY` 读取环境变量)。
13
+ - **+ Custom endpoint…**:OpenAI Chat Completions / OpenAI Responses / Anthropic Messages / Gemini 兼容地址,或 Ollama、LM Studio。填 base URL(粘贴完整的 `…/chat/completions`、`…/v1/messages` 会自动截断)、ID 和 key 后,xeno 用这个 key 向端点列出模型(OpenAI `GET /models`;Anthropic `GET /v1/models`;Gemini `GET /v1beta/models`),勾选要启用的模型后保存。
14
+ - **⚙ Manage connected…**:重新列出并选择模型、更换 key 或断开。
15
+
16
+ `/model` 只显示已接入且凭据可用的模型,最近使用的排在最前。保存位置与手写配置相同:key 在 `~/.xeno/auth.json`(0600),端点和启用的模型在 `~/.xeno/models.json` 的 `providers.<id>`。
17
+
18
+ ## Grok 官方账户
19
+
20
+ 输入 `/provider grok`,xeno 打开官方确认网页并显示 user code;在浏览器批准后,当前会话直接取得凭据。Esc 取消会中止轮询,取消后返回的 token 不会写入。过期时自动刷新,刷新响应未轮换 refresh token 时保留原值;失败会明确要求重新登录。
21
+
22
+ 账户存为 `auth.json` 的 `xai-grok`(0600),模型身份为 `xai-grok/grok-build`,请求走 `https://cli-chat-proxy.grok.com/v1`。`xai` 仍表示 `XAI_API_KEY` 与 `https://api.x.ai/v1`;两个身份不共享凭据或计费账户。也可显式提供 `GROK_ACCESS_TOKEN`,但环境 token 没有 refresh token,不能自动刷新。账户代理地址不能覆盖到其它服务。
23
+
24
+ 采用官方 RFC 8628 设备授权(`https://auth.x.ai/oauth2/device/code` 与 `/oauth2/token`);只申请推理及刷新所需 scopes,`referrer` 与 User-Agent 标识 xeno。协议所需 `X-XAI-Token-Auth` 与模型路由头遵循官方代理合同。依据:[Grok Build 企业认证](https://docs.x.ai/build/enterprise)、[官方授权源码](https://github.com/xai-org/grok-build/tree/main/crates/codegen/xai-grok-login/src)、[官方代理调用示例](https://github.com/xai-org/grok-build/blob/main/crates/codegen/xai-grok-shell/README.md)。
25
+
26
+ `grok-build` 是官方代理别名,不能把 API 模型的价格、上下文长度或可选 effort 套给它。当前保守暴露文本/工具调用,价格未知、effort 选择关闭;实际账户权限由官方服务决定。设备授权启动已实测返回 200,授权后的推理/刷新有受控 HTTP 验证;没有替用户完成真实账户批准或调用付费模型。
27
+
7
28
  ## 目录和选择
8
29
 
9
30
  ```sh
@@ -114,7 +135,7 @@ xeno models --refresh
114
135
  | `anthropic-messages` | Anthropic、支持 Messages 的网关;API key 或已有 Claude OAuth |
115
136
  | `openai-completions` | Chat Completions 兼容端点 |
116
137
  | `openai-responses` | OpenAI Responses;每次发完整投影,`store: false` |
117
- | `openai-codex-responses` | ChatGPT 订阅端点;`/login codex` 或 `OPENAI_CODEX_ACCESS_TOKEN` |
138
+ | `openai-codex-responses` | ChatGPT 订阅端点;`/provider codex` 或 `OPENAI_CODEX_ACCESS_TOKEN` |
118
139
  | `azure-openai-responses` | Azure key + resource/base URL;支持 `AZURE_RESOURCE_NAME` / `AZURE_API_KEY` 别名 |
119
140
  | `google-generative-ai` | Gemini 原生 API,Google/Gemini API key |
120
141
  | `google-vertex` | Vertex API key,或 `auth: "ambient"` + ADC;项目和位置必需 |
@@ -124,7 +145,7 @@ xeno models --refresh
124
145
 
125
146
  Vertex 可配置 scoped `env`:`GOOGLE_APPLICATION_CREDENTIALS`、`GOOGLE_CLOUD_PROJECT`、`GOOGLE_CLOUD_LOCATION`;也接受 OpenCode 的 `GOOGLE_VERTEX_PROJECT` / `GOOGLE_VERTEX_LOCATION`。Bedrock 使用 `AWS_PROFILE`、`AWS_REGION` 等 SDK 环境设置;resource/project/location 不会被当成 API key。需要端点模板的服务必须提供对应变量,例如 Cloudflare 的 account/gateway。
126
147
 
127
- 旧 `auth.json` 中存于 `openai` 的 Codex OAuth 可由 `openai-codex` 兼容读取;显式原生条目优先,普通 OpenAI API key 不共享到订阅协议。新的 `/login codex` 写入 `openai-codex`。其他 Pi 专属订阅登录流程不冒充已接入;可按协议配置 key/token。
148
+ 旧 `auth.json` 中存于 `openai` 的 Codex OAuth 可由 `openai-codex` 兼容读取;显式原生条目优先,普通 OpenAI API key 不共享到订阅协议。新的 `/provider codex` 写入 `openai-codex`。其他 Pi 专属订阅登录流程不冒充已接入;可按协议配置 key/token。
128
149
 
129
150
  ## 可配置参数与边界
130
151
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visual-z/xeno",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "description": "Segmented append-only agent log with pure context and handoff projections",
@@ -12,13 +12,15 @@
12
12
  "README.md",
13
13
  "CHANGELOG.md",
14
14
  "docs/install.md",
15
- "docs/providers.md"
15
+ "docs/providers.md",
16
+ "docs/app-server.md"
16
17
  ],
17
18
  "engines": {
18
- "bun": ">=1.1.0"
19
+ "bun": ">=1.3.0"
19
20
  },
20
21
  "repository": {
21
- "type": "git"
22
+ "type": "git",
23
+ "url": "git+https://github.com/visual-z/xeno.git"
22
24
  },
23
25
  "scripts": {
24
26
  "test": "bun test",
@@ -37,8 +39,8 @@
37
39
  "dependencies": {
38
40
  "@anthropic-ai/sdk": "^0.127.0",
39
41
  "@earendil-works/pi-ai": "0.99.1",
42
+ "@opentui/core": "0.5.14",
40
43
  "@silvia-odwyer/photon-node": "0.3.4",
41
- "get-east-asian-width": "1.6.0",
42
44
  "grok-mermaid": "0.2.3",
43
45
  "highlight.js": "10.7.3",
44
46
  "marked": "18.0.11"
@@ -0,0 +1,28 @@
1
+ import { CONTROL_SCHEMA } from "../core/control-protocol.ts";
2
+ import { sessionControl } from "../runtime/session-control.ts";
3
+ import { ControlJsonl } from "./control-transport.ts";
4
+ import { interactiveHooks } from "./shell.ts";
5
+ import type { App } from "./startup.ts";
6
+
7
+ /** Owns stdin/stdout only. Startup diagnostics use stderr; terminal UI is never constructed. */
8
+ export async function runAppServer(app: App): Promise<void> {
9
+ const control = sessionControl(app.session);
10
+ await interactiveHooks.bindExtensions(app);
11
+ app.askSlot.current = request => control.ask(request);
12
+ const stop = () => { process.stdin.destroy(); if (control.activeTurnId) control.interrupt(control.activeTurnId); };
13
+ const peer = new ControlJsonl(() => control, text => {
14
+ if (process.stdout.writableLength + Buffer.byteLength(text) > CONTROL_SCHEMA.limits.outputBytes) stop();
15
+ else process.stdout.write(text);
16
+ }, stop, true);
17
+ const signal = () => { process.exitCode = 130; stop(); };
18
+ process.once("SIGINT", signal); process.once("SIGTERM", signal); process.stdout.on("error", stop);
19
+ try {
20
+ for await (const chunk of process.stdin) peer.feed(typeof chunk === "string" ? Buffer.from(chunk) : chunk as Uint8Array);
21
+ await peer.drained();
22
+ } finally {
23
+ peer.close(); stop();
24
+ await control.whenIdle().catch(() => {});
25
+ await app.session.dispose(); await app.extensions?.dispose(); delete app.askSlot.current;
26
+ process.off("SIGINT", signal); process.off("SIGTERM", signal); process.stdout.off("error", stop);
27
+ }
28
+ }