@visual-z/xeno 0.8.4 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +31 -746
- package/bin/xeno.exe +26 -0
- package/install.cjs +88 -0
- package/package.json +18 -48
- package/docs/app-server.md +0 -115
- package/docs/foundations.md +0 -88
- package/docs/install.md +0 -244
- package/docs/providers.md +0 -172
- package/docs/research/foundation-clients-2026-10-09.md +0 -27
- package/docs/research/foundation-editing-2026-10-09.md +0 -56
- package/docs/research/foundation-network-2026-10-09.md +0 -37
- package/docs/research/foundation-workflow-2026-10-09.md +0 -37
- package/src/apps/acp-server.ts +0 -41
- package/src/apps/agent-profiles.ts +0 -19
- package/src/apps/app-server.ts +0 -30
- package/src/apps/approve.ts +0 -49
- package/src/apps/cli.ts +0 -99
- package/src/apps/commands.ts +0 -319
- package/src/apps/completions.ts +0 -87
- package/src/apps/control-transport.ts +0 -90
- package/src/apps/export-cli.ts +0 -24
- package/src/apps/goal-flow.ts +0 -70
- package/src/apps/image-draft.ts +0 -46
- package/src/apps/input.ts +0 -312
- package/src/apps/model-catalog.ts +0 -69
- package/src/apps/preferences.ts +0 -20
- package/src/apps/project-trust.ts +0 -9
- package/src/apps/provider-connect.ts +0 -346
- package/src/apps/question-ui.ts +0 -27
- package/src/apps/render.ts +0 -181
- package/src/apps/resources.ts +0 -66
- package/src/apps/review.ts +0 -109
- package/src/apps/session-hub.ts +0 -96
- package/src/apps/session-lifecycle.ts +0 -64
- package/src/apps/shell.ts +0 -2071
- package/src/apps/startup-input.ts +0 -135
- package/src/apps/startup.ts +0 -1677
- package/src/apps/ui/app.ts +0 -1818
- package/src/apps/ui/control-model.ts +0 -43
- package/src/apps/ui/extension-host.ts +0 -194
- package/src/apps/ui/image-cache.ts +0 -121
- package/src/apps/ui/palette.ts +0 -88
- package/src/apps/ui/project.ts +0 -127
- package/src/apps/ui/standalone.ts +0 -148
- package/src/apps/ui/status-model.ts +0 -68
- package/src/apps/ui/surface.ts +0 -91
- package/src/apps/ui/theme.ts +0 -86
- package/src/apps/update-check.ts +0 -112
- package/src/apps/workflow.ts +0 -58
- package/src/core/acceptance.ts +0 -139
- package/src/core/action-defaults.ts +0 -58
- package/src/core/activity-status.ts +0 -52
- package/src/core/agent-profile.ts +0 -130
- package/src/core/attachment-input.ts +0 -17
- package/src/core/blind-checks.ts +0 -72
- package/src/core/boundary.ts +0 -174
- package/src/core/branch-receipt.ts +0 -26
- package/src/core/catalog-merge.ts +0 -19
- package/src/core/checkpoints.ts +0 -85
- package/src/core/churn.ts +0 -61
- package/src/core/command-artifacts.ts +0 -94
- package/src/core/command-contract.ts +0 -151
- package/src/core/completion-action.ts +0 -20
- package/src/core/completion-review.ts +0 -98
- package/src/core/context-detail.ts +0 -37
- package/src/core/context-policy.ts +0 -133
- package/src/core/context-window.ts +0 -54
- package/src/core/continuation.ts +0 -130
- package/src/core/control-protocol.ts +0 -69
- package/src/core/control-view.ts +0 -39
- package/src/core/cycle.ts +0 -17
- package/src/core/data-query.ts +0 -201
- package/src/core/deadline.ts +0 -40
- package/src/core/derive.ts +0 -516
- package/src/core/draft-images.ts +0 -56
- package/src/core/edit-plan.ts +0 -200
- package/src/core/environment.ts +0 -181
- package/src/core/event-validation.ts +0 -220
- package/src/core/events.ts +0 -670
- package/src/core/evidence-ladder.ts +0 -62
- package/src/core/evidence-links.ts +0 -68
- package/src/core/evidence.ts +0 -240
- package/src/core/execution.ts +0 -18
- package/src/core/exhaustive.ts +0 -18
- package/src/core/export.ts +0 -179
- package/src/core/extension-flags.ts +0 -14
- package/src/core/external-effects.ts +0 -488
- package/src/core/failure-recovery.ts +0 -173
- package/src/core/file-precondition.ts +0 -78
- package/src/core/file-references.ts +0 -35
- package/src/core/frontmatter.ts +0 -213
- package/src/core/git-status.ts +0 -29
- package/src/core/goal-view.ts +0 -64
- package/src/core/goal.ts +0 -200
- package/src/core/handoff.ts +0 -479
- package/src/core/history-query.ts +0 -139
- package/src/core/history.ts +0 -237
- package/src/core/image-limits.ts +0 -72
- package/src/core/images.ts +0 -46
- package/src/core/integration-health.ts +0 -34
- package/src/core/json-event.ts +0 -131
- package/src/core/json-pointer.ts +0 -46
- package/src/core/keymap.ts +0 -153
- package/src/core/lsp-framing.ts +0 -34
- package/src/core/mcp-config.ts +0 -68
- package/src/core/model-picker.ts +0 -64
- package/src/core/model-preferences.ts +0 -28
- package/src/core/model-replay.ts +0 -22
- package/src/core/model-scope.ts +0 -53
- package/src/core/note-guidance.ts +0 -34
- package/src/core/oauth-fields.ts +0 -44
- package/src/core/observation-view.ts +0 -152
- package/src/core/outcome.ts +0 -44
- package/src/core/overflow.ts +0 -59
- package/src/core/packages.ts +0 -47
- package/src/core/patch.ts +0 -130
- package/src/core/path-suggestions.ts +0 -59
- package/src/core/permissions.ts +0 -410
- package/src/core/process-receipt.ts +0 -105
- package/src/core/process-state.ts +0 -37
- package/src/core/program-status.ts +0 -40
- package/src/core/progress.ts +0 -150
- package/src/core/project.ts +0 -431
- package/src/core/prompt-policy.ts +0 -46
- package/src/core/prompts.ts +0 -111
- package/src/core/question.ts +0 -94
- package/src/core/queue.ts +0 -78
- package/src/core/read-bounds.ts +0 -24
- package/src/core/read-observation.ts +0 -106
- package/src/core/recovery-sources.ts +0 -74
- package/src/core/regex-dialect.ts +0 -25
- package/src/core/request-budget.ts +0 -23
- package/src/core/request-progress.ts +0 -87
- package/src/core/request-state.ts +0 -273
- package/src/core/resource-selection.ts +0 -36
- package/src/core/response-recovery.ts +0 -59
- package/src/core/resume.ts +0 -59
- package/src/core/review.ts +0 -68
- package/src/core/ripgrep-release.ts +0 -64
- package/src/core/runtime-target.ts +0 -94
- package/src/core/scheduling.ts +0 -8
- package/src/core/search-observation.ts +0 -97
- package/src/core/secrets.ts +0 -188
- package/src/core/session-archive.ts +0 -4
- package/src/core/session-browser.ts +0 -82
- package/src/core/session-entry.ts +0 -61
- package/src/core/session-exit.ts +0 -42
- package/src/core/session-format.ts +0 -169
- package/src/core/session-position.ts +0 -26
- package/src/core/session-tree.ts +0 -147
- package/src/core/settings.ts +0 -192
- package/src/core/skills.ts +0 -165
- package/src/core/sql-query.ts +0 -211
- package/src/core/stall.ts +0 -128
- package/src/core/stop-report.ts +0 -68
- package/src/core/text-normalize.ts +0 -81
- package/src/core/theme.ts +0 -166
- package/src/core/thinking.ts +0 -26
- package/src/core/tokens.ts +0 -151
- package/src/core/tool-contract.ts +0 -153
- package/src/core/tool-discovery.ts +0 -87
- package/src/core/tool-failure.ts +0 -81
- package/src/core/tool-input.ts +0 -44
- package/src/core/transcript.ts +0 -108
- package/src/core/tui-mode.ts +0 -29
- package/src/core/update-check.ts +0 -157
- package/src/core/version.ts +0 -55
- package/src/core/web-content.ts +0 -80
- package/src/core/work-state.ts +0 -154
- package/src/core/workflow-mode.ts +0 -34
- package/src/core/working-set.ts +0 -119
- package/src/core/yaml.ts +0 -282
- package/src/runtime/acp-agent.ts +0 -289
- package/src/runtime/agent-profiles.ts +0 -21
- package/src/runtime/auth.ts +0 -367
- package/src/runtime/blind-check.ts +0 -36
- package/src/runtime/branch-jobs.ts +0 -36
- package/src/runtime/catalog.ts +0 -285
- package/src/runtime/clipboard.ts +0 -125
- package/src/runtime/command-artifacts.ts +0 -64
- package/src/runtime/connection-reuse.ts +0 -40
- package/src/runtime/control-client.ts +0 -149
- package/src/runtime/control-connection.ts +0 -139
- package/src/runtime/counter-check.ts +0 -74
- package/src/runtime/environment-tools.ts +0 -60
- package/src/runtime/evidence-notes.ts +0 -35
- package/src/runtime/exec-sessions.ts +0 -456
- package/src/runtime/execution-control.ts +0 -40
- package/src/runtime/extension-loader.ts +0 -37
- package/src/runtime/extension-ui.ts +0 -47
- package/src/runtime/extensions.ts +0 -370
- package/src/runtime/formatter-tool.ts +0 -38
- package/src/runtime/formatter.ts +0 -50
- package/src/runtime/foundation.ts +0 -116
- package/src/runtime/history-cache.ts +0 -57
- package/src/runtime/history-tools.ts +0 -168
- package/src/runtime/images.ts +0 -80
- package/src/runtime/integrations.ts +0 -216
- package/src/runtime/loop.ts +0 -3108
- package/src/runtime/lsp-client.ts +0 -201
- package/src/runtime/mcp-client.ts +0 -264
- package/src/runtime/mcp-oauth.ts +0 -168
- package/src/runtime/mcp-schema.ts +0 -51
- package/src/runtime/model-config.ts +0 -232
- package/src/runtime/model-data.LICENSE +0 -27
- package/src/runtime/model-data.ts +0 -21
- package/src/runtime/model-discovery.ts +0 -208
- package/src/runtime/model-replay.ts +0 -10
- package/src/runtime/model-scope.ts +0 -20
- package/src/runtime/model-sources.ts +0 -151
- package/src/runtime/model.ts +0 -686
- package/src/runtime/models-fetch.ts +0 -57
- package/src/runtime/oauth-device.ts +0 -86
- package/src/runtime/oauth.ts +0 -853
- package/src/runtime/open-image.ts +0 -42
- package/src/runtime/openai-responses.ts +0 -304
- package/src/runtime/openai.ts +0 -697
- package/src/runtime/output-schema.ts +0 -26
- package/src/runtime/package-manager.ts +0 -125
- package/src/runtime/patch-tool.ts +0 -38
- package/src/runtime/permission-resources.ts +0 -18
- package/src/runtime/pi-provider.ts +0 -423
- package/src/runtime/program-status.ts +0 -38
- package/src/runtime/progress.ts +0 -26
- package/src/runtime/provider-access.ts +0 -21
- package/src/runtime/provider-connect.ts +0 -109
- package/src/runtime/provider-presets.ts +0 -98
- package/src/runtime/provider-protocols.ts +0 -29
- package/src/runtime/provider-request.ts +0 -87
- package/src/runtime/provider-tool-policy.ts +0 -33
- package/src/runtime/provider.ts +0 -580
- package/src/runtime/question-tool.ts +0 -62
- package/src/runtime/retry.ts +0 -447
- package/src/runtime/review-tool.ts +0 -48
- package/src/runtime/rich-text.ts +0 -36
- package/src/runtime/runtime-target.ts +0 -149
- package/src/runtime/secrets.ts +0 -79
- package/src/runtime/self-update.ts +0 -74
- package/src/runtime/session-control.ts +0 -265
- package/src/runtime/session-hub.ts +0 -85
- package/src/runtime/stream-idle.ts +0 -80
- package/src/runtime/tool-broker.ts +0 -137
- package/src/runtime/tool-discovery.ts +0 -58
- package/src/runtime/tool-queue.ts +0 -77
- package/src/runtime/tools.ts +0 -1820
- package/src/runtime/turn-effects.ts +0 -31
- package/src/runtime/update-check.ts +0 -48
- package/src/runtime/usage.ts +0 -253
- package/src/runtime/version.ts +0 -12
- package/src/runtime/web-http.ts +0 -56
- package/src/runtime/web-tools.ts +0 -88
- package/src/runtime/working-notes.ts +0 -60
- package/src/runtime/workspace-lifecycle.ts +0 -141
- package/src/store/acceptance.ts +0 -15
- package/src/store/agent-profile-files.ts +0 -13
- package/src/store/atomic.ts +0 -31
- package/src/store/auth-store.ts +0 -50
- package/src/store/blind-checks.ts +0 -32
- package/src/store/blobs.ts +0 -143
- package/src/store/checkpoint-history.ts +0 -32
- package/src/store/command-artifacts.ts +0 -96
- package/src/store/command.ts +0 -111
- package/src/store/config.ts +0 -69
- package/src/store/context-files.ts +0 -436
- package/src/store/file-checkpoints.ts +0 -108
- package/src/store/file-index.ts +0 -174
- package/src/store/foundation-config.ts +0 -24
- package/src/store/git-checkpoints.ts +0 -67
- package/src/store/git-status.ts +0 -15
- package/src/store/history-search.ts +0 -19
- package/src/store/log.ts +0 -725
- package/src/store/lsp-document.ts +0 -30
- package/src/store/mcp-credentials.ts +0 -85
- package/src/store/models.ts +0 -60
- package/src/store/observations.ts +0 -224
- package/src/store/packages.ts +0 -98
- package/src/store/patch.ts +0 -92
- package/src/store/permission-paths.ts +0 -34
- package/src/store/process-receipts.ts +0 -34
- package/src/store/query-source.ts +0 -49
- package/src/store/recovery.ts +0 -44
- package/src/store/replay.ts +0 -76
- package/src/store/resource-index.ts +0 -170
- package/src/store/resources.ts +0 -285
- package/src/store/restore.ts +0 -110
- package/src/store/review.ts +0 -109
- package/src/store/runtime-target.ts +0 -64
- package/src/store/search.ts +0 -728
- package/src/store/secrets.ts +0 -44
- package/src/store/serial.ts +0 -13
- package/src/store/session-archive.ts +0 -165
- package/src/store/session-browser.ts +0 -68
- package/src/store/session-position.ts +0 -27
- package/src/store/sessions.ts +0 -525
- package/src/store/source-cancellation.ts +0 -6
- package/src/store/tool-binaries.ts +0 -90
- package/src/store/trust.ts +0 -82
- package/src/store/update-cache.ts +0 -36
- package/src/store/user-settings.ts +0 -30
- package/src/store/workspace-clone.ts +0 -42
- package/src/store/workspace-observation.ts +0 -113
- package/src/store/workspace.ts +0 -115
- package/src/store/worktree.ts +0 -278
- package/src/tui/ansi-spans.ts +0 -126
- package/src/tui/diff.ts +0 -160
- package/src/tui/exhaustive.ts +0 -14
- package/src/tui/fuzzy.ts +0 -75
- package/src/tui/style.ts +0 -208
- package/src/tui/ui/atomic-draft.ts +0 -121
- package/src/tui/ui/branch-panel.ts +0 -47
- package/src/tui/ui/canvas.ts +0 -96
- package/src/tui/ui/colors.ts +0 -70
- package/src/tui/ui/composer.ts +0 -199
- package/src/tui/ui/control-panel.ts +0 -25
- package/src/tui/ui/control-sidebar.ts +0 -49
- package/src/tui/ui/dialog.ts +0 -398
- package/src/tui/ui/draft.ts +0 -23
- package/src/tui/ui/duration.ts +0 -16
- package/src/tui/ui/edge.ts +0 -186
- package/src/tui/ui/image-size.ts +0 -44
- package/src/tui/ui/ink.ts +0 -210
- package/src/tui/ui/items.ts +0 -42
- package/src/tui/ui/keys.ts +0 -40
- package/src/tui/ui/layout.ts +0 -74
- package/src/tui/ui/list-model.ts +0 -86
- package/src/tui/ui/list-view.ts +0 -172
- package/src/tui/ui/mascot.ts +0 -67
- package/src/tui/ui/messages.ts +0 -518
- package/src/tui/ui/motion.ts +0 -28
- package/src/tui/ui/paths.ts +0 -10
- package/src/tui/ui/pictures.ts +0 -108
- package/src/tui/ui/prompt-history.ts +0 -60
- package/src/tui/ui/screen.ts +0 -579
- package/src/tui/ui/status.ts +0 -403
- package/src/tui/ui/styled.ts +0 -85
- package/src/tui/ui/tool-card.ts +0 -150
- package/src/tui/width.ts +0 -567
- package/src/vendor/pi-rich/LICENSE.upstream +0 -21
- package/src/vendor/pi-rich/UPSTREAM.json +0 -17
- package/src/vendor/pi-rich/clipboard-command.ts +0 -44
- package/src/vendor/pi-rich/exif-orientation.ts +0 -182
- package/src/vendor/pi-rich/html.ts +0 -51
- package/src/vendor/pi-rich/image-convert.ts +0 -49
- package/src/vendor/pi-rich/image-resize-core.ts +0 -164
- package/src/vendor/pi-rich/image-resize-worker.ts +0 -42
- package/src/vendor/pi-rich/image-resize.ts +0 -123
- package/src/vendor/pi-rich/mime.ts +0 -116
- package/src/vendor/pi-rich/photon.ts +0 -139
- package/src/vendor/pi-rich/syntax-highlight.ts +0 -214
package/docs/providers.md
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
# 供应商、模型目录与自定义配置
|
|
2
|
-
|
|
3
|
-
xeno 内置 Pi 0.99.1 和 OpenCode 所用 Models.dev 的固定源并集,另加 Grok 官方账户代理入口:227 个供应商、7,502 个模型条目。同一个模型在不同供应商下分别计数。目录保留能力与已知价格;未提供的价格显示未知,不用零补全。
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
确认模型或 thinking 后会自动保存到 `~/.xeno/settings.json`,下次新会话沿用,不需要额外按 Ctrl+S。模型菜单、`/model <provider/model>`、命令面板与模型循环共用这一行为;thinking 菜单、`/thinking <level>` 与快捷键也会保存。保存失败会提示本次已生效但下次启动不能保证沿用,不会切到另一个供应商。
|
|
19
|
-
|
|
20
|
-
显式启动参数只覆盖本次运行;`--continue` / `--resume` 沿用会话日志里的模型与 thinking,恢复和扩展程序化切换不会改写个人默认。未保存默认模型的旧配置先尝试可认证的最近选择,再按目录兜底;明确的配置层级顺序保持不变。
|
|
21
|
-
|
|
22
|
-
## Grok 官方账户
|
|
23
|
-
|
|
24
|
-
输入 `/provider grok`,xeno 打开官方确认网页并显示 user code;在浏览器批准后,当前会话直接取得凭据。Esc 取消会中止轮询,取消后返回的 token 不会写入。过期时自动刷新,刷新响应未轮换 refresh token 时保留原值;失败会明确要求重新登录。
|
|
25
|
-
|
|
26
|
-
账户存为 `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,不能自动刷新。账户代理地址不能覆盖到其它服务。
|
|
27
|
-
|
|
28
|
-
采用官方 RFC 8628 设备授权(`https://auth.x.ai/oauth2/device/code` 与 `/oauth2/token`);只申请推理及刷新所需 scopes,`referrer` 与 User-Agent 标识 xeno。协议所需 `X-XAI-Token-Auth`、模型路由头与版本闸门头 `x-grok-client-version`(固定为 1.0.46,`x-grok-client-identifier: xeno`)遵循官方代理合同;代理最低版本越过它时返回 426,xeno 会提示更新 xeno 或暂用 `xai` + `XAI_API_KEY`。依据:[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)。
|
|
29
|
-
|
|
30
|
-
`grok-build` 是官方代理别名,不能把 API 模型的价格、上下文长度或可选 effort 套给它。当前保守暴露文本/工具调用,价格未知、effort 选择关闭;实际账户权限由官方服务决定。设备授权启动已实测返回 200,授权后的推理/刷新有受控 HTTP 验证;没有替用户完成真实账户批准或调用付费模型。
|
|
31
|
-
|
|
32
|
-
## 目录和选择
|
|
33
|
-
|
|
34
|
-
```sh
|
|
35
|
-
xeno --list-models siliconflow
|
|
36
|
-
xeno --provider deepseek --model deepseek-flash
|
|
37
|
-
xeno --model google/gemini-2.5-flash
|
|
38
|
-
xeno models --refresh
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
按 `provider/model` 识别模型。完整标识优先于其他供应商包含斜杠的原始 ID;裸 ID 多义时给出候选,不按目录顺序猜。旧会话保存了供应商时精确恢复;只存 ID 的旧会话需要目录中的唯一匹配。
|
|
42
|
-
|
|
43
|
-
目录优先级:内置 → `~/.xeno/models-cache.json` → `~/.xeno/models.json`。启动、列举、选择、恢复和 `/reload` 共用同一解析入口。缓存更新模型元数据时保留原有已验证模型的 wire dialect;用户配置仍可明确覆盖它。刷新成功原子替换缓存,失败保留原缓存,始终不重写用户配置。`XENO_CONFIG_DIR` 可以替换这个目录。启动不联网;`--offline` 禁止显式刷新。
|
|
44
|
-
|
|
45
|
-
编辑配置后执行 `/reload`。当前模型的端点、API、wire ID 或回放格式变化会记录 checkpoint;纯凭据轮换不切段。禁用当前模型后,下一条输入保留为草稿,需要重新选择模型。供应商错误或配置错误不会使请求转到另一个账户。
|
|
46
|
-
|
|
47
|
-
新的 assistant 事件同时记录回放配置的摘要,恢复时从日志比较,配置别名不变也能识别上游协议变化。摘要不包含凭据或端点原文。没有摘要的旧原生配置在首次接入时先建立 checkpoint;旧的默认传输保持原恢复行为。
|
|
48
|
-
|
|
49
|
-
## 本地无认证端点(Pi 分组)
|
|
50
|
-
|
|
51
|
-
以下内容保存为 `~/.xeno/models.json`;端口、模型 ID 与本地服务保持一致。
|
|
52
|
-
|
|
53
|
-
```json
|
|
54
|
-
{
|
|
55
|
-
"providers": {
|
|
56
|
-
"local": {
|
|
57
|
-
"api": "openai-completions",
|
|
58
|
-
"baseUrl": "http://127.0.0.1:1234/v1",
|
|
59
|
-
"auth": false,
|
|
60
|
-
"models": [{
|
|
61
|
-
"id": "my-local-model",
|
|
62
|
-
"name": "Local model",
|
|
63
|
-
"contextWindow": 32768,
|
|
64
|
-
"maxTokens": 4096,
|
|
65
|
-
"compat": {
|
|
66
|
-
"supportsStore": false,
|
|
67
|
-
"supportsDeveloperRole": false,
|
|
68
|
-
"maxTokensField": "max_tokens"
|
|
69
|
-
},
|
|
70
|
-
"samplingParams": { "top_k": 40, "min_p": 0.05 }
|
|
71
|
-
}]
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
`xeno --model local/my-local-model` 使用该条目。`auth: false` 或 `authHeader: false` 关闭自动认证;SDK 构造所需的内部占位 key 不会发到服务器。`authHeader: "x-custom-key"` 可把最终解析的 key 放进指定头。Google 原生 SDK 拥有认证流程,不支持这两种头替换方式;本地兼容服务使用 OpenAI 接口。
|
|
78
|
-
|
|
79
|
-
## OpenCode 分组、别名与私有配置
|
|
80
|
-
|
|
81
|
-
```json
|
|
82
|
-
{
|
|
83
|
-
"provider": {
|
|
84
|
-
"company": {
|
|
85
|
-
"npm": "@ai-sdk/openai-compatible",
|
|
86
|
-
"options": {
|
|
87
|
-
"baseURL": "https://gateway.example.com/v1",
|
|
88
|
-
"apiKey": "{env:COMPANY_API_KEY}",
|
|
89
|
-
"temperature": 0.2
|
|
90
|
-
},
|
|
91
|
-
"headers": { "x-tenant": "${COMPANY_TENANT}" },
|
|
92
|
-
"models": {
|
|
93
|
-
"coding": {
|
|
94
|
-
"id": "upstream-model-id",
|
|
95
|
-
"limit": { "context": 128000, "output": 8192 },
|
|
96
|
-
"reasoning": true,
|
|
97
|
-
"thinkingLevelMap": { "off": null, "minimal": "low", "low": "low", "medium": "medium", "high": "high", "xhigh": null, "max": null },
|
|
98
|
-
"compat": { "supportsStore": false, "supportsDeveloperRole": false, "maxTokensField": "max_tokens" },
|
|
99
|
-
"body": { "top_p": 0.9 }
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
选择 `company/coding`,线上请求使用 `upstream-model-id`。`npm` 仅用于识别已接通的协议,不会安装或执行任意供应商包;也可以直接指定 `api`。OpenCode 的完整应用配置、插件、任意 SDK 选项不在这个解析器的兼容范围。
|
|
108
|
-
|
|
109
|
-
凭据优先级为 CLI `--api-key` → `auth.json` → 模型/供应商 `apiKey` → 供应商环境变量。供应商 `apiKey` 可被模型覆盖;头和 scoped env 按字段合并。指定来源失败就报错,不向下兜底。
|
|
110
|
-
|
|
111
|
-
`apiKey`、`headers`、`env` 和 `baseUrl` 支持字面字符串、`$VAR` / `${VAR}` / `{env:VAR}`,以及以 `!` 开头的本地命令。命令只有在凭据解析或实际请求需要时执行,不在目录解析时执行。`$$` 和 `$!` 表示字面开头字符。保留 xeno 原有规则:裸 `MY_KEY` 是字面 key,引用变量要写 `$MY_KEY`。只把自己信任的命令写入用户配置;目录缓存不含用户秘密。
|
|
112
|
-
|
|
113
|
-
`env` 只作用于该供应商,不更改全局进程环境。变量顺序不影响结果,循环引用拒绝。密钥、私有请求头、env 值与请求 options 保存在私有配置中,不随模型对象进入模型列表或日志。
|
|
114
|
-
|
|
115
|
-
## 覆盖与启停
|
|
116
|
-
|
|
117
|
-
```json
|
|
118
|
-
{
|
|
119
|
-
"providers": {
|
|
120
|
-
"deepseek": {
|
|
121
|
-
"apiKey": "$DEEPSEEK_API_KEY",
|
|
122
|
-
"modelOverrides": {
|
|
123
|
-
"deepseek-flash": { "maxTokens": 8192, "headers": { "x-route": "coding" } }
|
|
124
|
-
},
|
|
125
|
-
"allowlist": ["deepseek-*"],
|
|
126
|
-
"denylist": ["*-deprecated"]
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
"disabled_providers": ["unused-provider"]
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
`modelOverrides` 只修改已有条目;`models` 可以新增。保留旧的 `[{provider,id,api,...}]` 或 `{ "models": [...] }` 扁平格式。支持 provider/model 的 `disabled: true` / `enabled: false`、全局 `enabled_providers` / `disabled_providers`,以及 `allowlist` / `denylist`(同义 `whitelist` / `blacklist`),模式支持 `*` 和 `?`,deny 优先。无效同级模型给出字段路径,有效同级项继续可用;损坏的显式覆盖不会继续使用原来的凭据。
|
|
134
|
-
|
|
135
|
-
## 原生协议和云认证
|
|
136
|
-
|
|
137
|
-
| api | 接口与认证 |
|
|
138
|
-
|---|---|
|
|
139
|
-
| `anthropic-messages` | Anthropic、支持 Messages 的网关;API key 或已有 Claude OAuth |
|
|
140
|
-
| `openai-completions` | Chat Completions 兼容端点 |
|
|
141
|
-
| `openai-responses` | OpenAI Responses;每次发完整投影,`store: false` |
|
|
142
|
-
| `openai-codex-responses` | ChatGPT 订阅端点;`/provider codex` 或 `OPENAI_CODEX_ACCESS_TOKEN` |
|
|
143
|
-
| `azure-openai-responses` | Azure key + resource/base URL;支持 `AZURE_RESOURCE_NAME` / `AZURE_API_KEY` 别名 |
|
|
144
|
-
| `google-generative-ai` | Gemini 原生 API,Google/Gemini API key |
|
|
145
|
-
| `google-vertex` | Vertex API key,或 `auth: "ambient"` + ADC;项目和位置必需 |
|
|
146
|
-
| `bedrock-converse-stream` | AWS bearer token、profile、IAM/容器/身份链;`auth: "ambient"` 显式使用 SDK 凭据链 |
|
|
147
|
-
| `mistral-conversations` | Pi 的 Mistral 原生适配 |
|
|
148
|
-
| `pi-messages` | Pi Messages 兼容服务器 |
|
|
149
|
-
|
|
150
|
-
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。
|
|
151
|
-
|
|
152
|
-
旧 `auth.json` 中存于 `openai` 的 Codex OAuth 可由 `openai-codex` 兼容读取;显式原生条目优先,普通 OpenAI API key 不共享到订阅协议。新的 `/provider codex` 写入 `openai-codex`。其他 Pi 专属订阅登录流程不冒充已接入;可按协议配置 key/token。
|
|
153
|
-
|
|
154
|
-
## 可配置参数与边界
|
|
155
|
-
|
|
156
|
-
- `compat` / `nativeCompat` 传递 Pi 协议兼容能力;`thinkingLevelMap` 明确本地思考档到上游值的映射。
|
|
157
|
-
- `options` 支持 `temperature`、`samplingParams`、`cacheRetention`、`timeoutMs`、`maxRetryDelayMs`、`metadata`;具体协议只消费自己支持的参数。云 region/profile 通过 `env`。不认识的 option 明确报错。
|
|
158
|
-
- `samplingParams` 用于 OpenAI 兼容协议的采样扩展;`body` / `extraBody` 是实际原生请求体的额外字段。模型、system、消息、工具、stream 与服务端会话字段由投影/适配器负责,不能被这些扩展覆盖。
|
|
159
|
-
- D18 保留 xeno 客户端身份;不会自动带 Pi/Claude Code/Copilot 的产品身份或 attribution 头,也不会插入 Claude Code 的身份提示。订阅服务可能因此拒绝第三方客户端;此处不承诺所有订阅账户可用。
|
|
160
|
-
- Google/Vertex SDK 自行管理 HTTP,不使用自定义 fetch;Bedrock 自行管理 AWS HTTP 与重试,外层不再叠加重试。取消与终止状态仍统一交回 xeno。
|
|
161
|
-
|
|
162
|
-
## 覆盖、排除与验证依据
|
|
163
|
-
|
|
164
|
-
`src/runtime/model-data.ts` 同时保存完整来源元数据和 1,373 条排除记录。明确无工具能力或非文本输出的模型不进入 agent 可选目录;还需要专用 SDK 的条目保留排除原因,例如 GitLab、WatsonX、Vertex Anthropic、SAP、Merge Gateway、v0、Qvac。可用兼容服务仍可由用户指定 API/端点接入,不能仅新增一个名字就算支持了专用协议。
|
|
165
|
-
|
|
166
|
-
除了两份上游源码,Cohere、Venice、AIHubMix 的接入分别核对了 [Cohere compatibility API](https://docs.cohere.com/docs/compatibility-api)、[Venice API 官方资料](https://github.com/veniceai/api-docs/blob/main/agents.md)、[AIHubMix 快速开始](https://docs.aihubmix.com/en/quick-start)。Cohere 的 reasoning effort 按官方能力映射为 none/high;不发送其未支持的 store 字段。
|
|
167
|
-
|
|
168
|
-
上游配置依据:[Pi models](https://github.com/earendil-works/pi/blob/1b347794e2a630e4359f2584f4eea388145d0ddf/packages/coding-agent/docs/models.md)、[OpenCode provider](https://github.com/anomalyco/opencode/blob/2fa3363c924c5c3e367b84a87ae478296a0ed59b/packages/opencode/src/provider/provider.ts)、[Models.dev](https://models.dev/)。
|
|
169
|
-
|
|
170
|
-
本地验证覆盖十种协议的真实受控 HTTP(包含 AWS 二进制 EventStream)、签名/工具结果往返、错误重试/取消、实际 CLI JSON 输出、实际 PTY 重载/切换。没有逐一调用付费远程模型,目录条目数量不等于账户可用性或模型质量证明。
|
|
171
|
-
|
|
172
|
-
重生成时先下载并保存 Models.dev JSON,再运行 `bun scripts/update-model-catalog.ts <snapshot.json>`,审查来源摘要、条目/排除变化及测试固定计数。应用启动不执行生成器。
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# 客户端与最终输出的实现依据
|
|
2
|
-
|
|
3
|
-
本轮用户已批准并固定 MCP client 2.3.1、ACP SDK 1.7.0、Ajv 8.20.0。实现核对已发布包的 API、官方协议及竞品源码,未调用付费模型做质量结论。
|
|
4
|
-
|
|
5
|
-
## 共同执行拥有者
|
|
6
|
-
|
|
7
|
-
[Codex App Server 官方文档](https://learn.chatgpt.com/docs/app-server)将 thread 的创建/恢复与 turn 的开始/steer/interrupt 分开,并要求一次 initialize/initialized 握手;取消请求之后仍由 completed 事件确认结果。xeno 复用这些生命周期原则,继续用自身 `xeno-control/0.1` envelope 和字段,不声称 wire 兼容。SessionHub 只管理 SessionControl,模型、工具、取消和原始日志仍由原 Session 拥有。
|
|
8
|
-
|
|
9
|
-
高 star 项目 [OpenCode v2 RunCoordinator](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/core/src/session/run-coordinator.ts)按执行身份协调调用,而非视图持有第二个执行循环。xeno 对同一 durable log 的 path/id/hash 别名先归一化再创建;所有 new/fork/resume 都在关闭时等待清理,容量同步预留,不能等异步构造完才计数。control stream 断线后需 inspector/resync,不自动重发未知效果的业务命令。
|
|
10
|
-
|
|
11
|
-
客户端的 typed methods 共用服务端入参 schema;响应必须恰有 result/error 之一,通知必须带合法 thread/stream/sequence。一个连接可订阅多个会话,按身份路由结构化问题与审批;原客户端失去拥有权时取消自己的 pending 请求,观察者重连从快照恢复。32 活会话/待请求、1 MiB 帧、2 MiB 输出缓冲是防失控的运行容量假设,未改变段长阈值。
|
|
12
|
-
|
|
13
|
-
## 资源事务与恢复
|
|
14
|
-
|
|
15
|
-
角色选择、模式、配置重载共享 idle change 排斥边界。先解析当前信任资源和 catalog、验证凭据并构造 provider,再关闭旧 integration 并写一次 frozen checkpoint。仅成功之后发布 app 的 args/catalog/tools/UI。失败退回原配置;已确认关闭的 integration 只重新允许未来显式调用,不重放过去的操作。尚未确认清理的拥有者保持不可替代。
|
|
16
|
-
|
|
17
|
-
私有 context blob 保存角色 selector、模式及显式模式的工具默认配置,按 current trust 重新解析角色。它不改变 handoff schema,不把恢复文件里的角色说明或权限当成现行代码授权。MCP/LSP 代码来自当前注册配置,恢复声明不会安装外部代码。
|
|
18
|
-
|
|
19
|
-
既有 coding/readonly/research/data 合同与默认十工具配置保留;新增 foundation profile 和内置 build/plan 角色启用基础工具。这避免在旧会话或已有 explicit profile 上默默扩大能力,后续默认 surface 的改变应继续使用实际任务数据评估。
|
|
20
|
-
|
|
21
|
-
## 标准 ACP 与 JSON Schema
|
|
22
|
-
|
|
23
|
-
[ACP session modes](https://agentclientprotocol.com/protocol/v1/session-modes)提供可选择的模式,并由 agent 决定是否支持。使用官方 SDK stable v1,initialize/new/load/prompt/cancel/set_mode,以及文本/tool-call 更新、权限请求和协商后的 elicitation,都桥接原 Session。stdio EOF 清理整个 hub;cwd 明确限定到启动 workspace。规范与真实 SDK/stdio 子进程测试共同验证,没有手写协议替代官方 SDK。
|
|
24
|
-
|
|
25
|
-
[Codex CLI 参考](https://learn.chatgpt.com/docs/cli/reference)提供 output schema / last-message 类脚本入口。xeno 的最终响应经 Ajv 完整校验;[Ajv 数据修改说明](https://ajv.js.org/guide/modifying-data.html)列出了 coercion/default/removeAdditional 等会改变输入的选项,本轮明确关闭。完整 schema 支持标准 formats 与三个显式 dialect,未知语义在启动/发现时报错,不静默忽略。无效 JSON 仍作为原始 assistant 事实保留,owner outcome 为 execution_error;print stdout 与 last-message 文件只发布接受后的最终答案,失败退出 1。
|
|
26
|
-
|
|
27
|
-
本地协议、fake 模型 HTTP、实际子进程及 PTY 自动验证证明可调用链路和故障契约;生产 MCP/OAuth/LSP 环境和作者终端体验需各自实际验收,不能由模型一句“完成”替代。
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# 文件修改、审查与目录规范实现依据
|
|
2
|
-
|
|
3
|
-
2026-10-09 查询 GitHub 主仓库元数据与固定提交源码。星数是查询时快照,不作为质量证明:OpenCode 212238、Codex 128265、aider 49428。实现判断依据是实际数据流和失败合同;v1 与 v2 分开记录。
|
|
4
|
-
|
|
5
|
-
## 文件修改
|
|
6
|
-
|
|
7
|
-
- [OpenCode v2 Patch](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/core/src/tool/apply-patch.ts):先解析并解析所有资源、授权,再准备内容;`writeIfUnchanged` 校验源字节。明确采用顺序应用,后段失败报告此前已应用路径,当前拒绝 move。它证明统一资源解析和写入版本比较的重要性,不证明原子批处理已实现。
|
|
8
|
-
- [Codex Patch parser](https://github.com/openai/codex/blob/2351d9e1b608e6f9d9a3699b71d7eb39ee41cfa4/codex-rs/apply-patch/src/parser.rs):独立解析 Add/Delete/Update、Move to、上下文锚点和 End of File,路径解析与文件副作用不属于 parser。
|
|
9
|
-
|
|
10
|
-
选择:保留 Codex 的文件级 Patch 语法,用纯 core 函数解析和匹配;不猜测歧义上下文。store 一次验证全部源版本、目标缺失状态和目录边界,保存原始 before/after blob;真正修改复用 xeno 现有 `applyFileChanges` 的 CAS 和补偿链。所有源必须来自原日志观察或显式 SHA-256;原文件 BOM、换行及权限保留。禁止别名重复目标、重叠目标、软/硬链接和内部状态目录。
|
|
11
|
-
|
|
12
|
-
取消通过同一批处理函数传到每次 replacement;已应用部分的补偿刻意忽略取消,否则取消会中止恢复。恢复仍校验当前版本,外部新版本不得被补偿覆盖。此实现依赖现有文件 checkpoint 的检查与替换边界,不宣称多文件操作具有文件系统原子提交能力。
|
|
13
|
-
|
|
14
|
-
## 格式化
|
|
15
|
-
|
|
16
|
-
- [OpenCode v2 formatter config](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/core/src/config/formatter.ts) 定义明确的 argv、扩展名和 disabled;不要把 legacy `packages/opencode/src/format` 的完整自动执行能力计入 v2。
|
|
17
|
-
- [aider linter](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/linter.py) 使用自定义 lint 命令的真实退出码并把反馈交回模型。
|
|
18
|
-
- [OpenCode 格式化后上下文过期报告](https://github.com/anomalyco/opencode/issues/31422) 提供自动格式化使下一次 edit 基于旧缩进失败的用户报告;这是风险线索,不是对所有 formatter 的定量结论。
|
|
19
|
-
|
|
20
|
-
选择:显式 `format` 工具和可信用户配置,不自动发现或安装 executable,不在 edit/write/Patch 后隐藏执行。扩展名选择、`$FILE` 替换和命令排序共用一个计划函数;替换为 absolute argv 参数,文件名不进入 shell 解析。每个命令经已有 `invokeHost` admission、取消、日志和退出 receipt。结果返回实际退出和当前文件 digest;失败停止后续 formatter。
|
|
21
|
-
|
|
22
|
-
## diff 与独立 review
|
|
23
|
-
|
|
24
|
-
- [Codex review task](https://github.com/openai/codex/blob/2351d9e1b608e6f9d9a3699b71d7eb39ee41cfa4/codex-rs/core/src/tasks/review.rs) 生成单次独立 review 上下文,使用专用 rubric,关闭不适合 review 的工具,不继承主会话 initial history。
|
|
25
|
-
- [Codex review request](https://github.com/openai/codex/blob/2351d9e1b608e6f9d9a3699b71d7eb39ee41cfa4/codex-rs/prompts/src/review_request.rs) 区分 staged/unstaged/untracked、merge base 和指定 commit。
|
|
26
|
-
|
|
27
|
-
选择:diff 是只读 store 观察,review 模型执行由独立只读会话拥有者负责。Git 统一使用 argv、不可变 revision SHA、merge-base、literal pathspec 和 `--relative`;先枚举候选路径并授权全部,再以获准路径的显式 pathspec 收集 diff,防止权限拒绝内容被提前读取或归档、收集期间新增文件扩大范围。禁用 ext-diff 和 textconv,不改用户 index。commit scope 与当前工作区内容分开,merge commit 明示 first-parent。最近一轮从 original log 的 applied checkpoint 折叠,所有 blob 路径授权后读取校验后的 blob;没有 checkpoint 的 shell/外部修改为未知,不能从模型声称或现在的文件倒推。
|
|
28
|
-
|
|
29
|
-
文本 review 输出没有静默截断:超过假设的 16 MiB 预算报不可用。大 untracked 文件、二进制和不支持的链接明确列入 limitations;不会用空结果冒充没有修改。纯统一 diff 用线性连续 hunk,保留其间所有内容,避免全文件二次方 LCS 内存。
|
|
30
|
-
|
|
31
|
-
应用 review 创建独立的新 Session,只有父目录当前可见的内置 read/grep/find 与专门构造的只读 diff;不继承父 extension overrides/hooks、审批槽、命令回调、deferred tool 或更宽 pathRoot。父上下文与 transcript 不输入 review,父 mode/frozen/node 不改;使用父 control 的 idle-change 锁防止审查期间另一个父回合进入。授权后才收集并保存完整 snapshot 为 review 自己日志目录的不可变 premise blob。父 non-context receipt 仅存 review log、premise ref 与真实 ExecutionOutcome;取消和 provider 失败同样留 receipt。没有子进程就没有 command 退出码,模型声称测试通过也不能成为 command evidence。
|
|
32
|
-
|
|
33
|
-
## 目录规范
|
|
34
|
-
|
|
35
|
-
- [Codex AGENTS scope instructions](https://github.com/openai/codex/blob/2351d9e1b608e6f9d9a3699b71d7eb39ee41cfa4/codex-rs/protocol/src/prompts/base_instructions/default.md):目录规则作用于子树,较深目录优先,修改目标文件时必须遵守其所有祖先范围。
|
|
36
|
-
- [OpenCode v2 instruction context](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/core/src/instruction-context.ts) 从当前 location 走到项目目录,观察配置的 AGENTS 并通过 system context 更新。
|
|
37
|
-
|
|
38
|
-
选择:启动解析 Git root→cwd,沿现有 override/AGENTS/CLAUDE 优先级加载;保持 `loadContext` 原兼容语义。目标发现只看显式文件路径的祖先,不扫描兄弟目录,不跟随指令软链接。每个 exact 投影拥有 path、scope 和 content digest key;新增版本通过 durable in-context receipt 给模型,不能在原调用已修改文件之后通知,也不重写冻结 system 或 handoff。首次操作(包括 read)先返回需要接纳指令的回执,模型读到后下一次调用才执行原操作。cold resume 从原 receipt 重新投影已接纳 key。
|
|
39
|
-
|
|
40
|
-
macOS Git 把 `/var` 转成 `/private/var`;用 Git 的 show-prefix 沿原 cwd 拼写上溯,确保 workspace 与 cwd 的目录包含关系没有因路径别名丢失。
|
|
41
|
-
|
|
42
|
-
## 标准 ACP v1
|
|
43
|
-
|
|
44
|
-
- [ACP 会话生命周期](https://agentclientprotocol.com/protocol/v1/session-setup)、[prompt 与取消](https://agentclientprotocol.com/protocol/v1/prompt-turn)、[工具与权限](https://agentclientprotocol.com/protocol/v1/tool-calls) 规定初始化、绝对 cwd、基线 stdio MCP 配置、加载时消息重放、tool call 身份以及取消状态。
|
|
45
|
-
- [ACP elicitation](https://agentclientprotocol.com/protocol/v1/elicitation) 要求 form 能力明确存在且非 null;问题用结构化表单,不能替代执行授权。空 elicitation 对象不代表支持 form。
|
|
46
|
-
- [OpenCode ACP service](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/opencode/src/acp/service.ts) 将 ACP cwd/MCP 配置交给真实 session,把 load 的消息重放和事件订阅分开;不从客户端通知重建执行 loop。
|
|
47
|
-
|
|
48
|
-
选择:使用用户批准的官方 `@agentclientprotocol/sdk@1.7.0` stable `agent().onRequest/onNotification/connect` 和 `ndJsonStream`,不使用 experimental/v2。协议 schema 由 SDK 验证;ACP 与既有控制协议共享 `SessionHub` 和 `SessionControl`,不新建 loop、审批队列或持久协议状态。每个 AgentApp 只服务一个 client connection;进程目前拥有单一 workspace,cwd 不符和 additionalDirectories 明确拒绝,每个 workspace 单独启动进程。标准客户端 stdio/HTTP MCP 配置转换成已有配置结构,经 host 的可信 session 资源加载;重复名称、未知 transport、重复环境变量/headers 拒绝。
|
|
49
|
-
|
|
50
|
-
文本通知只投影已提交的 assistant text,provider 重试中丢弃的 draft 不成为对话。工具身份来自原 invocation 的 node/event,原 input 从原 assistant request 恢复;权限显示具体命令、资源及原参数,仅返回本次批准或拒绝,未知 optionId 和 RPC 失败均拒绝,不能自动重放。客户端 form elicitation 的结果再次经过既有 invocation/turn/question validator;缺能力时 question 产生 environment_missing,不发送权限伪问题。load 只按原日志重放 user/assistant/tool 结果,不触发模型或工具执行,不倒读当前文件伪造历史。
|
|
51
|
-
|
|
52
|
-
取消经同一 control 终止原回合,返回 cancelled;SDK 的 request cancellation 是协作语义,因此本地 await 同时监听 signal,客户端不回应审批/表单也不能阻止取消。输入帧和输出通知限制为假设的 1 MiB、聚合输出队列 2 MiB、异步 handler 并发 32;stdout 写回调施加 backpressure。EOF、stdout 错误和系统信号终止连接,hub 等待并释放原会话。测试使用真实 SDK 双向 client 以及隔离 Bun 子进程 stdin/stdout,不依赖付费模型,也不代表 Zed 等作者 GUI 已验收。当前接受 text/resource_link/embedded text;image/audio/embedded binary 明确拒绝。
|
|
53
|
-
|
|
54
|
-
## 验证边界
|
|
55
|
-
|
|
56
|
-
新契约覆盖固定 seed 的 Patch 可逆性、未触及内容保留、checkpoint 版本投影、最近一轮折叠和目录发现顺序;真实临时 Git 仓库覆盖 staged/unstaged/untracked、分叉 merge-base、指定 commit、unborn branch、项目 diff driver 不执行和用户 index 不变。原有 read-version、checkpoint 和 context-files 回归同时保留。自动测试不代表付费 provider 质量或作者原生终端验收。
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# 联网与 MCP 基础工作流决策
|
|
2
|
-
|
|
3
|
-
日期:2026-10-09。范围:官方协议、已发布客户端 API、OpenCode/Codex/Goose 源码;这些是实现依据,不是付费线上验收。用户授权补齐基础功能并允许为共同架构重构;不改变 D1–D13。
|
|
4
|
-
|
|
5
|
-
## 调研与选择
|
|
6
|
-
|
|
7
|
-
- [OpenCode webfetch](https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/tool/webfetch.ts) 使用 5 MiB 响应上限、默认 30 秒/最多 120 秒以及 HTML 正文提取。xeno 采用相同量级的假设边界,但在流式读取中执行大小限制,并对重定向逐跳验证;保留完整原始响应,避免提取器成为唯一证据。解析 HTML 使用已安装 Bun 的 [HTMLRewriter](https://bun.sh/docs/runtime/html-rewriter),避免自己实现不完整的 HTML tokenizer。静态内容提取不会执行页面 JavaScript;markdown 是保留标题、链接、段落、代码的投影,原始 HTML 始终可恢复。
|
|
8
|
-
- [OpenCode websearch](https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/tool/websearch.ts) 与 [v2 可配置提供商讨论](https://github.com/anomalyco/opencode/issues/36513) 说明工具和供应商选择应分离。xeno 明确配置 Brave 或 Exa,不做未告知的默认供应商、收费切换或搜索页面抓取 fallback。两者都使用官方 REST 合同:[Brave](https://api-dashboard.search.brave.com/api-reference/web/search/post)、[Exa](https://exa.ai/docs/reference/search)。密钥只来自环境变量;搜索结果保留标题、绝对 URL、摘录、发布日期(提供商确实返回时)及完整响应引用。
|
|
9
|
-
- [MCP 标准传输](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports) 定义 stdio 和 Streamable HTTP。必须实际完成握手、发现、调用、取消和关闭,不能只增加配置类型。与 [OpenCode MCP](https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/mcp/index.ts)、[Goose MCP 客户端](https://github.com/aaif-goose/goose/blob/main/crates/goose/src/agents/mcp_client.rs) 一样,协议交给成熟官方客户端,xeno 负责会话/工具/权限/观察拥有权。
|
|
10
|
-
- 核对 npm 发布包后,采用精确版本 `@modelcontextprotocol/client@2.3.1`,替代旧的整包 `@modelcontextprotocol/sdk@1.32.1`。前者公开根入口 `Client`、`StreamableHTTPClientTransport`、`OAuthClientProvider` 与 `auth`,`/stdio` 入口公开 stdio 传输;不需要额外服务器或 Web 框架。官方 [OAuth 指南](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/clients/oauth.md) 已使用拆分客户端。该依赖及 `ajv@8.20.0` 已经本会话用户讨论批准,再由统一入口负责人安装。
|
|
11
|
-
- 新客户端支持 issuer 绑定、完整 OAuth callback 参数、PKCE、protected-resource metadata 和 token refresh。xeno 不自行拼接 OAuth 协议。[授权规范](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization) 和 [Codex OAuth 实现](https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/oauth.rs) 支持将身份/资源/issuer 作为凭据边界,不能仅按一个显示名称复用 token。
|
|
12
|
-
|
|
13
|
-
## 与 xeno 既有拥有权衔接
|
|
14
|
-
|
|
15
|
-
1. Web 工具通过现有 `ToolDefinition` 执行;URL/参数/提供商结果校验进入纯 core;网络与 HTML 解析属于 runtime;原始响应经现有 observation store 存档。默认 5 跳 redirect、5 MiB 完整响应、30 秒 deadline 是资源边界假设,不声称是性能最优值。搜索鉴权请求不允许 redirect,避免密钥被转发到其它端点。
|
|
16
|
-
2. MCP 管理器拥有配置、发现、OAuth 和全部 transport;发现结果转换为不可变的 deferred `ExtensionIntegration` 工具声明。已有 `LazyIntegration` 拥有连接退役、超时、取消、冷却和用户修复。工具调用仍通过原有嵌套调用 broker,模型不会绕过权限链路。
|
|
17
|
-
3. server annotations 不能证明工具只读;保持扩展 write/exclusive/branch:none 策略。Plan 不因 MCP 声称 `readOnlyHint` 就获得额外权限。对资源和 prompt 的操作只在 server capability 存在时开放。
|
|
18
|
-
4. 重连只重建协议连接并验证工具目录;发现 schema 变化要求用户显式 reload,经现有资源检查点后才进入模型上下文。远端 `list_changed` 不能原地改冻结前缀。
|
|
19
|
-
5. 官方传输默认可能在 401/403 后鉴权并重发;新客户端的 `inputRequired.autoFulfill` 也可能重新提交工具请求。xeno 关闭这两条自动 business replay 路径及 SSE 自动 reconnect。OAuth 登录是用户操作,refresh 发生在新调用发送前;失效后的调用报告 `auth_required` 和可能已应用的副作用,等待检查,不自行重试。
|
|
20
|
-
6. OAuth callback 只绑定 `127.0.0.1`,验证 state,再把完整查询参数交给 SDK 验证 issuer。错误消息只报告归类事实,不能回显攻击者提供的 `error_description`、token、请求头或原始 SDK 异常。
|
|
21
|
-
7. 凭据与 append-only 日志完全分开。提供可替换的凭据存储接口;限制文件后端使用 0700 目录、0600 原子文件,按精确资源 URL/issuer 隔离,拒绝 symlink、越权权限和超限 JSON。官方优先推荐 OS vault;限制文件后端是 Codex 同类 fallback,不能宣称为 keychain。登录/退出/刷新都不得将密钥写入日志或模型投影。
|
|
22
|
-
8. [MCP JSON Schema 合同](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/basic/index.mdx) 默认 2020-12。发现时编译并缓存完整 schema,保存 `$defs` / `$ref` / `anyOf` / `additionalProperties` 到冻结声明;显式支持 draft-07、2019-09 和 2020-12。遵循 [Ajv 数据不变选项](https://ajv.js.org/guide/modifying-data.html),禁止类型 coercion、填默认值、删除额外字段。使用官方客户端公开 validator 入口已打包的标准格式实现;未知引用、关键字、格式或 dialect 明确拒绝注册并报告安全诊断,不忽略未知语义。无效参数在 broker 的 `prepareArguments` 阶段成为 `invalid_input/not_started/effects:none`,不能已进入远端调用后才校验。
|
|
23
|
-
9. [HTTP session 终止合同](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports) 要求不再需要会话时发送带 session-id 的 DELETE。即使 deferred 工具从未调用,发现连接仍由 manager 拥有。dispose/logout/退役关闭这个连接;DELETE 使用独立最多 5 秒的假设清理期限,业务取消不会把清理信号一同终止;不会为清理启动 OAuth 登录或 token refresh。重复 dispose 共用同一 promise,清理失败不报告已成功关闭远端状态。
|
|
24
|
-
|
|
25
|
-
## 配置合同
|
|
26
|
-
|
|
27
|
-
`webSearch`:`provider` 为 `brave` 或 `exa`;`apiKeyEnv` 默认分别为 `BRAVE_SEARCH_API_KEY` / `EXA_API_KEY`;可选 `endpoint` 用于明确配置的代理。未配置时工具返回可操作的配置错误,不伪造结果。
|
|
28
|
-
|
|
29
|
-
`mcp`:可信 `foundation.json` 中按 server name 索引。stdio 配置 `transport:"stdio"`、`command`、可选 `args` / `env`。HTTP 配置 `transport:"http"`、`url`、可选 `bearerTokenEnv`、`headers`、`headersEnv` 或 `oauth`。共同字段 `enabled`、`timeoutMs`;OAuth 对象支持预注册 `clientId` / `clientSecretEnv`、`scope`、明确的 loopback `redirectUrl`。Bearer 与 OAuth 不能同时配置。`env` 的 `${NAME}` 插值只由 runtime 解析,不将解析后的秘密放入配置投影。
|
|
30
|
-
|
|
31
|
-
## 验证边界
|
|
32
|
-
|
|
33
|
-
本地真实 HTTP/subprocess 合同覆盖原始存档、截断恢复、大小限制、取消、鉴权错误不重放、发现与 schema 漂移、stdout 协议、资源/prompt capability。固定种子 property test 覆盖 URL 幂等规范化、非 HTTP/credentials 拒绝、搜索来源保留及 config 不共享可变数组。RED 必须破坏真实源码并保留 Git 快照;测试数、退出码、断言记在总卡,不提交原始日志。
|
|
34
|
-
|
|
35
|
-
真实 `FoundationRuntime → ExtensionRegistry → SessionControl/Session` 测试覆盖发现后的完整 schema 冻结检查点、调用前无效参数拦截及实际 webfetch 原始存档;不只直接测试 manager。HTTP fixture 还确认未被 deferred 工具领取的连接发出一次终止 DELETE;凭据目录权限变化或被 symlink 替换后读/退出都会拒绝继续操作。
|
|
36
|
-
|
|
37
|
-
本地 fixture 证明客户端协议和拥有权;它不等价于具体供应商的付费搜索质量、所有第三方 OAuth 配置或用户终端体验验收。
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# 基础工作流实现依据(2026-10-09)
|
|
2
|
-
|
|
3
|
-
本记录讨论 Plan / Build、Agent 角色、结构化提问、资源权限与语言服务器生命周期。源码事实与 xeno 的取舍分开;GitHub issue 是排查线索,不作为功能已实现的证明。没有引入运行时依赖。
|
|
4
|
-
|
|
5
|
-
## 核对的竞品源码
|
|
6
|
-
|
|
7
|
-
- OpenCode 当前 `dev` 核对快照:`388406238bd5ca15564a762840a2362c3a45bd9c`。v2 的 [Agent 服务](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/core/src/agent.ts)负责角色选择;[角色 schema](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/schema/src/agent.ts)把 model、system、steps 和 permissions 放在同一角色中。不能把 `packages/opencode/src/` 的兼容 v1 工具误称为 v2 工具。
|
|
8
|
-
- OpenCode v2 的 [权限服务](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/core/src/permission.ts)按 action / resource 匹配,多个资源取最严格结果,并先判配置的 deny 再合并已保存批准。它采用后匹配规则优先;xeno 既有契约是首匹配优先,不因移植而反转。
|
|
9
|
-
- OpenCode v2 的 [Question 服务](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/core/src/question.ts)由 location 持有 pending request,并在完成、拒绝、取消与 teardown 时清理。[Question 工具](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/core/src/tool/question.ts)传入 session 与原 tool call 身份,支持多选和自定义答案。配置审批与产品提问是独立交互。
|
|
10
|
-
- Codex 当前 `main` 核对快照:`2351d9e1b608e6f9d9a3699b71d7eb39ee41cfa4`。[Plan 模板](https://github.com/openai/codex/blob/2351d9e1b608e6f9d9a3699b71d7eb39ee41cfa4/codex-rs/collaboration-mode-templates/templates/plan.md)把计划定义为显式 collaboration mode;允许用于探索的命令,但禁止实施计划的变更。它不是简单的 TODO 列表。
|
|
11
|
-
- Codex 的 [用户输入工具](https://github.com/openai/codex/blob/2351d9e1b608e6f9d9a3699b71d7eb39ee41cfa4/codex-rs/core/src/tools/handlers/request_user_input_spec.rs)要求稳定 question id、少量问题和非空选项;运行时控制在哪些模式可用。[角色配置](https://github.com/openai/codex/blob/2351d9e1b608e6f9d9a3699b71d7eb39ee41cfa4/codex-rs/agent-roles/src/agent_role_config.rs)拒绝未知字段与空白说明,避免无意的隐式配置。[execpolicy](https://github.com/openai/codex/blob/2351d9e1b608e6f9d9a3699b71d7eb39ee41cfa4/codex-rs/execpolicy/README.md)使用有序 argv 前缀,提供 match / not_match 示例。
|
|
12
|
-
- Codex 产品入口用 [官方 Plan 说明](https://developers.openai.com/blog/run-long-horizon-tasks-with-codex)、[CLI execpolicy 说明](https://learn.chatgpt.com/docs/cli/reference)和[配置参考](https://learn.chatgpt.com/docs/config-file/config-reference)核对。源码用于解释实现细节,产品文档用于确认公开入口。
|
|
13
|
-
|
|
14
|
-
## xeno 的决策
|
|
15
|
-
|
|
16
|
-
1. **模式与能力分开。** Plan 改变模型说明和真实工具 admission。它允许只读工具与本地 note / question,移除 write / shell 及未知扩展;任意扩展代码不能凭模型提示获得只读保证。切换仅在完整工具交换结束后,经现有资源检查点生效,旧 frozen prefix 与事件不改写(D5 / D6)。xeno 无 OS 沙箱,不能可靠证明一个 shell 命令只写构建产物,因此不照搬 Codex 允许探索 shell 的范围。
|
|
17
|
-
2. **一个角色配置拥有协同字段。** 使用惰性的 JSON 文件和现有 project trust:用户 `agents.json`、可信项目 `.xeno/agents.json`、显式 CLI 覆盖。字段包括 mode / instructions / model / tools / permissions / budget,按低到高整字段替换;返回每个字段的来源,不新增自动继承或隐藏模型默认。`maxTurns` 是既有硬上限,`timeBudgetMs` 保留通知语义,不声称能硬停执行。
|
|
18
|
-
3. **问题的身份由运行时拥有。** 选项使用稳定 id,不用可重复或可翻译 label 当身份。回答必须对应原 request / turn / call,精确覆盖 question id;支持单选、多选、自由输入。取消和无客户端明确失败,不能推导用户批准。UI 与协议共用适配层;不持有第二个会话、不把回答塞成脱离原调用的新 prompt。
|
|
19
|
-
4. **资源是实际文件,不是搜索目录。** 字面路径和 glob 先做纯词法匹配,store 再提供符号链接的真实目标,多个路径分别判定后取 deny > ask > allow。搜索和结构化查询在读取每个真实候选前应用 read 策略,不能通过 `.env` 的别名或 grep 绕过。默认实际 `.env` / `.env.*` 请求最后一眼,`.env.example` 保持普通源码读取;这些是审批规则,不是容器隔离。
|
|
20
|
-
5. **批准不突破配置 deny。** 会话记忆仍不落盘,只能消除 ask,不能跨过当前配置的拒绝。命令前缀只匹配无组合、无展开的完整 literal argv;管道、重定向、命令替换等不匹配并保留原审批。这是用户明确配置的匹配机制,不做命令危险分类(既有 D13 决策)。
|
|
21
|
-
6. **角色、模式与资源只有一个提交边界。** 当前配置的解析、资源发现、凭据验证与 provider 构造先在未发布对象上完成;成功后,模式与选择器进入同一个 frozen / model 检查点,再发布进程内规则和运行时所有者。失败保留旧配置;已正常退出的旧可选服务只在用户下一次明确操作时重连,不重放业务调用(PLAYBOOK Q.13)。SDK 调用者原有审批策略单独保存为 host base,不因 `/reload` 自动套用 CLI 默认 shell 审批。
|
|
22
|
-
7. **冷恢复保存意图,重新取得当前权限。** 私有 context 元数据只保存角色名字、是否显式选择模式与 `foundation` 工具范围;不保存批准或执行代码。恢复按当前 project trust 重读角色文件,角色删除或信任撤销时按名字拒绝启动。当前 CLI 工具范围优先,并写入新检查点,避免旧范围在下一次恢复时复活。既有实测 `coding` 十工具与 `readonly` 五工具合同不扩大;新的具名 `foundation` profile、`build` / `plan` 角色和无显式工具限制的 `/plan` 提供新增基础工具。
|
|
23
|
-
|
|
24
|
-
## 验证合同
|
|
25
|
-
|
|
26
|
-
纯规则以固定种子检查确定性、非变异、能力单调收窄和逐资源拒绝;文件测试检查真实 trust 层、文件损坏、符号链接与尚未存在的新文件;问题经 `runTool` 验证正常 admission、旧回合拒绝和忽略 AbortSignal 的客户端仍能取消。
|
|
27
|
-
|
|
28
|
-
源码破坏快照 `eeeb5e29800fafdae54dcaa1384ec91c3004af25` 同时破坏 Plan 收窄、角色层序、提问身份、配置 deny 与真实路径,5 个对应测试真实失败;正常实现恢复后通过。机器可读 RED 与完整门禁计数在总任务卡中维护,不在此存原始日志。
|
|
29
|
-
|
|
30
|
-
## LSP 的协议依据与边界
|
|
31
|
-
|
|
32
|
-
- [LSP 3.17 基础协议](https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/lsp/3.17/specification.md)规定 ASCII header、按字节计量的 Content-Length、UTF-8 JSON 内容与 `$/cancelRequest`。取消消息不能保证服务器合作,因此 xeno 初始化被取消时终止自己启动的服务器;已经初始化的普通请求只取消原 request id,随后仍可查询。
|
|
33
|
-
- [初始化合同](https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/lsp/3.17/general/initialize.md)通过 capabilities 协商位置编码;xeno 明示 UTF-16 并拒绝不兼容的服务器返回值。[诊断合同](https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/lsp/3.17/language/publishDiagnostics.md)的 version 可选,不能从缺失版本推断诊断对应当前文件。xeno 报告 current / stale / unversioned / unavailable,仅将精确当前版本的推送诊断作为 available;每次响应同时检查当前文件 SHA。
|
|
34
|
-
- [Microsoft 官方 JSON-RPC reader](https://github.com/microsoft/vscode-languageserver-node/blob/main/jsonrpc/src/common/messageReader.ts)逐帧消费且维持消息顺序,不把 OS chunk 当成消息。xeno 沿用此生命周期,不引入完整语言客户端依赖;每个 partial buffer 有 8 MiB body / 8 KiB header 上限,单次请求期间的未配对 stream 限额 32 MiB,每份缓存诊断 512 KiB、最多 32 个打开文档。均是防止可选服务器无界占用的保守运行上限,尚未作为性能实测最佳值;不修改日志段阈值。
|
|
35
|
-
- [workspace/applyEdit](https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/lsp/3.17/workspace/applyEdit.md)要求客户返回 applied。xeno 明示不支持服务器写入并返回 applied=false;需要修改代码时使用拥有文件检查点与版本检查的 Patch 工具。服务器使用可信配置的明确 argv,按文件后缀唯一选择,不推测安装位置、不自动下载或运行语言服务器。
|
|
36
|
-
|
|
37
|
-
真实本地 stdin/stdout 子进程覆盖初始化取消与 PID 回收、请求取消和后续复用、服务器修改拒绝、旧诊断、新文件指纹、处理期间的外部改动、流量上限及正常 observation archive。固定种子覆盖 Unicode 字节长度、随机截断、连续消息与诊断版本判定;这些证据只验证协议与生命周期,不代表已对所有语言服务器做兼容验收。
|
package/src/apps/acp-server.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/** A stdio ACP endpoint owns transport lifetime; SessionHub retains execution and disposal ownership. */
|
|
2
|
-
import { ndJsonStream } from "@agentclientprotocol/sdk";
|
|
3
|
-
import { CONTROL_SCHEMA } from "../core/control-protocol.ts";
|
|
4
|
-
import { createAcpAgent } from "../runtime/acp-agent.ts";
|
|
5
|
-
import type { SessionHub } from "../runtime/session-hub.ts";
|
|
6
|
-
import { createAppSessionHub } from "./session-hub.ts";
|
|
7
|
-
import { interactiveHooks } from "./shell.ts";
|
|
8
|
-
import type { App } from "./startup.ts";
|
|
9
|
-
|
|
10
|
-
export async function runAcpTransport(hub: SessionHub, input: ReadableStream<Uint8Array>, output: WritableStream<Uint8Array>, signal?: AbortSignal): Promise<void> {
|
|
11
|
-
const connection = createAcpAgent(hub).connect(ndJsonStream(output, input, { maxMessageBytes: CONTROL_SCHEMA.limits.frameBytes }));
|
|
12
|
-
const stop = () => connection.close();
|
|
13
|
-
signal?.addEventListener("abort", stop, { once: true });
|
|
14
|
-
if (signal?.aborted) stop();
|
|
15
|
-
try { await connection.closed; }
|
|
16
|
-
finally { signal?.removeEventListener("abort", stop); connection.close(); await hub.dispose(); }
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Startup diagnostics use stderr. stdout contains ACP JSON-RPC envelopes exclusively. */
|
|
20
|
-
export async function runAcpServer(app: App): Promise<void> {
|
|
21
|
-
const hub = createAppSessionHub(app), abort = new AbortController();
|
|
22
|
-
await interactiveHooks.bindExtensions(app);
|
|
23
|
-
const stop = () => { abort.abort(); process.stdin.destroy(); };
|
|
24
|
-
const signal = () => { process.exitCode = 130; stop(); };
|
|
25
|
-
process.once("SIGINT", signal); process.once("SIGTERM", signal); process.stdout.on("error", stop);
|
|
26
|
-
const iterator = process.stdin[Symbol.asyncIterator]();
|
|
27
|
-
const input = new ReadableStream<Uint8Array>({ async pull(controller) {
|
|
28
|
-
const next = await iterator.next();
|
|
29
|
-
if (next.done) controller.close();
|
|
30
|
-
else controller.enqueue(typeof next.value === "string" ? Buffer.from(next.value) : next.value);
|
|
31
|
-
}, cancel: stop });
|
|
32
|
-
const output = new WritableStream<Uint8Array>({ write(chunk) {
|
|
33
|
-
if (process.stdout.writableLength + chunk.byteLength > CONTROL_SCHEMA.limits.outputBytes) {
|
|
34
|
-
stop(); throw Error("ACP stdout queue exceeded its limit");
|
|
35
|
-
}
|
|
36
|
-
// Waiting for the write callback propagates stdout backpressure to the SDK.
|
|
37
|
-
return new Promise<void>((done, fail) => process.stdout.write(chunk, error => error ? fail(error) : done()));
|
|
38
|
-
} });
|
|
39
|
-
try { await runAcpTransport(hub, input, output, abort.signal); }
|
|
40
|
-
finally { stop(); process.off("SIGINT", signal); process.off("SIGTERM", signal); process.stdout.off("error", stop); }
|
|
41
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/** Role changes share the same app/controller checkpoint owner as mode and model changes. */
|
|
2
|
-
import { agentProfileNames, builtinAgentProfileLayer, isAgentProfileName } from "../core/agent-profile.ts";
|
|
3
|
-
import { loadAgentProfileLayers } from "../runtime/agent-profiles.ts";
|
|
4
|
-
import { sessionControl } from "../runtime/session-control.ts";
|
|
5
|
-
import { reloadAppResources, type ResourceReloadOptions } from "./resources.ts";
|
|
6
|
-
import type { App } from "./startup.ts";
|
|
7
|
-
export async function listAppAgents(app: App): Promise<string> {
|
|
8
|
-
const loaded = await loadAgentProfileLayers({ workspace: app.workspace, trustProject: app.projectTrusted === true });
|
|
9
|
-
return `Agent: ${app.args.agent ?? "build"}\n` + agentProfileNames([builtinAgentProfileLayer(), ...loaded.layers]).join("\n") + (loaded.diagnostics.length ? "\n" + loaded.diagnostics.join("\n") : "");
|
|
10
|
-
}
|
|
11
|
-
export async function selectAppAgent(app: App, name: string, options: Pick<ResourceReloadOptions, "providerFactory"> = {}): Promise<void> {
|
|
12
|
-
if (!isAgentProfileName(name)) throw Error("Invalid agent profile name");
|
|
13
|
-
await sessionControl(app.session).withIdleChange(async () => {
|
|
14
|
-
const args = structuredClone(app.args); args.agent = name;
|
|
15
|
-
await reloadAppResources(app, { ...options, ownerLocked: true, args });
|
|
16
|
-
await app.session.appendExtension({ customType: "agent_profile", content: `Selected agent ${name}`, display: true, inContext: false,
|
|
17
|
-
data: { name, sources: app.args.role?.sources.map(source => ({ kind: source.kind, ...(source.path ? { path: source.path } : {}) })) ?? [] } });
|
|
18
|
-
});
|
|
19
|
-
}
|
package/src/apps/app-server.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
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
|
-
import { createAppSessionHub } from "./session-hub.ts";
|
|
7
|
-
|
|
8
|
-
/** Owns stdin/stdout only. Startup diagnostics use stderr; terminal UI is never constructed. */
|
|
9
|
-
export async function runAppServer(app: App): Promise<void> {
|
|
10
|
-
const control = sessionControl(app.session);
|
|
11
|
-
const hub = createAppSessionHub(app);
|
|
12
|
-
await interactiveHooks.bindExtensions(app);
|
|
13
|
-
app.askSlot.current = request => control.ask(request);
|
|
14
|
-
const stop = () => { process.stdin.destroy(); if (control.activeTurnId) control.interrupt(control.activeTurnId); };
|
|
15
|
-
const peer = new ControlJsonl(() => control, text => {
|
|
16
|
-
if (process.stdout.writableLength + Buffer.byteLength(text) > CONTROL_SCHEMA.limits.outputBytes) stop();
|
|
17
|
-
else process.stdout.write(text);
|
|
18
|
-
}, stop, true, hub);
|
|
19
|
-
const signal = () => { process.exitCode = 130; stop(); };
|
|
20
|
-
process.once("SIGINT", signal); process.once("SIGTERM", signal); process.stdout.on("error", stop);
|
|
21
|
-
try {
|
|
22
|
-
for await (const chunk of process.stdin) peer.feed(typeof chunk === "string" ? Buffer.from(chunk) : chunk as Uint8Array);
|
|
23
|
-
await peer.drained();
|
|
24
|
-
} finally {
|
|
25
|
-
peer.close(); stop();
|
|
26
|
-
await control.whenIdle().catch(() => {});
|
|
27
|
-
await hub.dispose(); delete app.askSlot.current;
|
|
28
|
-
process.off("SIGINT", signal); process.off("SIGTERM", signal); process.stdout.off("error", stop);
|
|
29
|
-
}
|
|
30
|
-
}
|
package/src/apps/approve.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copy for the bash approval prompt.
|
|
3
|
-
*
|
|
4
|
-
* The full command is the point: approval is the last look at what will
|
|
5
|
-
* run. Truncating it would hide the thing the person is being asked to
|
|
6
|
-
* see. The word this must not use is any that would make the prompt
|
|
7
|
-
* sound like a boundary.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { PermissionRequest } from "../core/permissions.ts";
|
|
11
|
-
import type { ApprovalChoice } from "../runtime/tools.ts";
|
|
12
|
-
import type { ListItem } from "../tui/ui/list-model.ts";
|
|
13
|
-
|
|
14
|
-
export function approvalHeading(request: PermissionRequest): string {
|
|
15
|
-
if (request.command !== undefined) {
|
|
16
|
-
return [
|
|
17
|
-
`${request.tool} wants to run:`,
|
|
18
|
-
"",
|
|
19
|
-
request.command,
|
|
20
|
-
"",
|
|
21
|
-
"Approval is the last look at this command, not a boundary.",
|
|
22
|
-
].join("\n");
|
|
23
|
-
}
|
|
24
|
-
return `${request.tool} wants to run. Approval is the last look, not a boundary.`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function approvalItems(request: PermissionRequest): ListItem[] {
|
|
28
|
-
const items: ListItem[] = [{ value: "once", label: "this once" }];
|
|
29
|
-
if (request.command !== undefined) {
|
|
30
|
-
items.push({ value: "command", label: "this command this session" });
|
|
31
|
-
}
|
|
32
|
-
items.push(
|
|
33
|
-
{ value: "tool", label: `${request.tool} this session` },
|
|
34
|
-
{ value: "deny", label: "deny" },
|
|
35
|
-
);
|
|
36
|
-
return items;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function choiceFromValue(value: string): ApprovalChoice {
|
|
40
|
-
switch (value) {
|
|
41
|
-
case "once":
|
|
42
|
-
case "command":
|
|
43
|
-
case "tool":
|
|
44
|
-
case "deny":
|
|
45
|
-
return value;
|
|
46
|
-
default:
|
|
47
|
-
return "deny";
|
|
48
|
-
}
|
|
49
|
-
}
|
package/src/apps/cli.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* A terminal session.
|
|
4
|
-
*
|
|
5
|
-
* Interactive output uses Pi's main-screen renderer and terminal lifecycle
|
|
6
|
-
* (D19). Durable messages are a pure log projection; deltas remain transient.
|
|
7
|
-
* Print and piped modes do not construct a terminal UI.
|
|
8
|
-
*
|
|
9
|
-
* Usage accounting is on by default rather than behind a flag, because the
|
|
10
|
-
* point of running this at all is to find out whether the prefix stays stable
|
|
11
|
-
* in practice. A cache that quietly stopped working raises no error -- it just
|
|
12
|
-
* costs more -- so the numbers have to be in front of you.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
parseArgs, startup, readPipedStdin, mergePrintPrompt, isInteractiveSession, printCliHelp, provisionToolsInBackground,
|
|
17
|
-
} from "./startup.ts";
|
|
18
|
-
import { createStartupInput } from "./startup-input.ts";
|
|
19
|
-
import { runPrompt, runRepl } from "./shell.ts";
|
|
20
|
-
import { runPackageCommand } from "../runtime/package-manager.ts";
|
|
21
|
-
import { exportHtmlFile } from "./export-cli.ts";
|
|
22
|
-
import { CONTROL_SCHEMA } from "../core/control-protocol.ts";
|
|
23
|
-
import { sessionControl } from "../runtime/session-control.ts";
|
|
24
|
-
import { listenControl } from "./control-transport.ts";
|
|
25
|
-
import { runAppServer } from "./app-server.ts";
|
|
26
|
-
import { runManualUpdate } from "./update-check.ts";
|
|
27
|
-
import { XENO_VERSION } from "../runtime/version.ts";
|
|
28
|
-
import { closeAppSession } from "./session-lifecycle.ts";
|
|
29
|
-
import { runAcpServer } from "./acp-server.ts";
|
|
30
|
-
import { runReview, reviewScopeFromText } from "./review.ts";
|
|
31
|
-
|
|
32
|
-
const serverMode = process.argv[2] === "app-server";
|
|
33
|
-
const acpMode = process.argv[2] === "acp";
|
|
34
|
-
const reviewMode = process.argv[2] === "review";
|
|
35
|
-
if (serverMode && process.argv[3] === "schema") { console.log(JSON.stringify(CONTROL_SCHEMA, null, 2)); process.exit(0); }
|
|
36
|
-
const args = parseArgs(process.argv.slice(serverMode || acpMode || reviewMode ? 3 : 2), { deferExtensions: true });
|
|
37
|
-
if (serverMode || acpMode) { args.print = true; args.mode = "json"; if (args.prompt) throw Error("protocol servers receive prompts through their client"); }
|
|
38
|
-
if (reviewMode) args.print = true;
|
|
39
|
-
let closeControl: (() => Promise<void>) | undefined;
|
|
40
|
-
let stopToolInstall = (): void => {};
|
|
41
|
-
const input = createStartupInput({ interactive: isInteractiveSession(args) && !args.packageCommand && !args.version && !args.exportFile && !args.listModels && !args.modelsCommand });
|
|
42
|
-
process.once("exit", input.restore);
|
|
43
|
-
try {
|
|
44
|
-
if (args.help && (args.packageCommand || args.configCommand || args.exportFile || args.version || args.listModels || args.modelsCommand)) {
|
|
45
|
-
printCliHelp();
|
|
46
|
-
} else if (args.version) {
|
|
47
|
-
if (args.extensionArgv) throw new Error("Unknown flags for --version");
|
|
48
|
-
console.log(XENO_VERSION);
|
|
49
|
-
} else if (args.exportFile) {
|
|
50
|
-
if (args.extensionArgv) throw new Error("Unknown flags for offline export");
|
|
51
|
-
console.log(`Exported ${await exportHtmlFile(args.exportFile, args.prompt || undefined, args.workspace)}`);
|
|
52
|
-
} else if (args.manualUpdateCheck) {
|
|
53
|
-
if (args.extensionArgv) throw new Error("Unknown update check flag");
|
|
54
|
-
process.exitCode = await runManualUpdate({ offline: args.offline === true }, text => console.log(text));
|
|
55
|
-
} else if (args.packageCommand) {
|
|
56
|
-
if (args.extensionArgv) throw new Error("Unknown package command flag");
|
|
57
|
-
if ((args.offline || process.env["XENO_OFFLINE"] === "1") && (args.packageCommand.operation === "update" || args.packageCommand.operation === "install" && args.packageCommand.sources.some(source => /^(npm:|git:|git\+|https?:\/\/|ssh:\/\/|git@)/.test(source)))) throw new Error("--offline prevents remote package installation or updates");
|
|
58
|
-
const controller = new AbortController(), cancel = () => controller.abort();
|
|
59
|
-
process.once("SIGINT", cancel); process.once("SIGTERM", cancel);
|
|
60
|
-
try { await runPackageCommand(args.packageCommand, { workspace: args.workspace, signal: controller.signal, ...(args.approve === undefined ? {} : { trusted: args.approve }) }); }
|
|
61
|
-
catch (error) { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = controller.signal.aborted ? 130 : 1; }
|
|
62
|
-
finally { process.off("SIGINT", cancel); process.off("SIGTERM", cancel); }
|
|
63
|
-
} else {
|
|
64
|
-
stopToolInstall = provisionToolsInBackground(args);
|
|
65
|
-
const app = await startup(args, input);
|
|
66
|
-
if (args.controlSocket) closeControl = await listenControl(args.controlSocket, () => sessionControl(app.session));
|
|
67
|
-
if (serverMode) {
|
|
68
|
-
input.restore();
|
|
69
|
-
await runAppServer(app);
|
|
70
|
-
} else if (acpMode) {
|
|
71
|
-
input.restore();
|
|
72
|
-
await runAcpServer(app);
|
|
73
|
-
} else if (reviewMode) {
|
|
74
|
-
input.restore();
|
|
75
|
-
try { await runReview(app, reviewScopeFromText(args.prompt)); }
|
|
76
|
-
finally { await closeAppSession(app); }
|
|
77
|
-
} else {
|
|
78
|
-
if (args.print) {
|
|
79
|
-
// With a prompt already given, stdin is optional context: wait briefly for it.
|
|
80
|
-
args.prompt = mergePrintPrompt(args.prompt,
|
|
81
|
-
await readPipedStdin(process.stdin, args.prompt.length > 0 ? { firstByteMs: 3_000 } : {}));
|
|
82
|
-
}
|
|
83
|
-
if (args.prompt) {
|
|
84
|
-
input.restore();
|
|
85
|
-
await runPrompt(app, args.prompt);
|
|
86
|
-
} else {
|
|
87
|
-
input.phase("Loading conversation");
|
|
88
|
-
await runRepl(app, input.handoff);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
} catch (error) {
|
|
93
|
-
console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1;
|
|
94
|
-
} finally {
|
|
95
|
-
stopToolInstall();
|
|
96
|
-
await closeControl?.();
|
|
97
|
-
process.off("exit", input.restore);
|
|
98
|
-
input.restore();
|
|
99
|
-
}
|