@chatcode/cco-llm-chatcode-config 0.1.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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/README.zh.md +133 -0
  4. package/cordis.patch.yml +4 -0
  5. package/cordis.web.patch.yml +12 -0
  6. package/docs/chatcode-login.md +88 -0
  7. package/docs/chatcode-login.zh.md +179 -0
  8. package/docs/chatcode-models.md +29 -0
  9. package/docs/chatcode-models.zh.md +29 -0
  10. package/docs/chatcode-reporting.md +96 -0
  11. package/docs/chatcode-reporting.zh.md +96 -0
  12. package/docs/decisions/2026-08-31-chatcode-model-source.md +39 -0
  13. package/docs/decisions/2026-08-31-chatcode-model-source.zh.md +39 -0
  14. package/docs/decisions/2026-09-16-actual-model-adapter-routing.md +31 -0
  15. package/docs/decisions/2026-09-16-actual-model-adapter-routing.zh.md +31 -0
  16. package/lib/client.js +469 -0
  17. package/lib/index.d.ts +263 -0
  18. package/lib/index.d.ts.map +1 -0
  19. package/lib/index.js +4873 -0
  20. package/lib/index.js.map +1 -0
  21. package/lib/startup-gate-BaCbWaKH.js +164 -0
  22. package/lib/startup-gate-BaCbWaKH.js.map +1 -0
  23. package/lib/web-startup.d.ts +9 -0
  24. package/lib/web-startup.d.ts.map +1 -0
  25. package/lib/web-startup.js +20 -0
  26. package/lib/web-startup.js.map +1 -0
  27. package/package.json +121 -0
  28. package/vendor/README.md +7 -0
  29. package/vendor/dsh-llm-pi-ai/LICENSE +21 -0
  30. package/vendor/dsh-llm-pi-ai/README.i18n.yaml +6 -0
  31. package/vendor/dsh-llm-pi-ai/README.md +238 -0
  32. package/vendor/dsh-llm-pi-ai/README.zh.md +238 -0
  33. package/vendor/dsh-llm-pi-ai/package.json +65 -0
  34. package/vendor/dsh-llm-pi-ai/src/adapter.ts +434 -0
  35. package/vendor/dsh-llm-pi-ai/src/auth.ts +241 -0
  36. package/vendor/dsh-llm-pi-ai/src/catalog.ts +908 -0
  37. package/vendor/dsh-llm-pi-ai/src/config.ts +478 -0
  38. package/vendor/dsh-llm-pi-ai/src/context.ts +349 -0
  39. package/vendor/dsh-llm-pi-ai/src/discovery.ts +284 -0
  40. package/vendor/dsh-llm-pi-ai/src/index.ts +336 -0
  41. package/vendor/dsh-llm-pi-ai/src/invariant.ts +30 -0
  42. package/vendor/dsh-llm-pi-ai/src/login.ts +161 -0
  43. package/vendor/dsh-llm-pi-ai/src/provider.ts +192 -0
  44. package/vendor/dsh-llm-pi-ai/src/replay.ts +249 -0
  45. package/vendor/dsh-llm-pi-ai/src/stream.ts +232 -0
  46. package/vendor/dsh-llm-pi-ai/tests/adapter.e2e.ts +168 -0
  47. package/vendor/dsh-llm-pi-ai/tests/adapter.spec.ts +1034 -0
  48. package/vendor/dsh-llm-pi-ai/tests/assemble.ts +32 -0
  49. package/vendor/dsh-llm-pi-ai/tests/auth-double.ts +39 -0
  50. package/vendor/dsh-llm-pi-ai/tests/auth.spec.ts +221 -0
  51. package/vendor/dsh-llm-pi-ai/tests/catalog.spec.ts +1220 -0
  52. package/vendor/dsh-llm-pi-ai/tests/config.spec.ts +111 -0
  53. package/vendor/dsh-llm-pi-ai/tests/context.spec.ts +474 -0
  54. package/vendor/dsh-llm-pi-ai/tests/convert.spec.ts +922 -0
  55. package/vendor/dsh-llm-pi-ai/tests/discovery.spec.ts +374 -0
  56. package/vendor/dsh-llm-pi-ai/tests/dynamic-config.spec.ts +241 -0
  57. package/vendor/dsh-llm-pi-ai/tests/fixtures/qr-code.png +0 -0
  58. package/vendor/dsh-llm-pi-ai/tests/loader-composition.spec.ts +244 -0
  59. package/vendor/dsh-llm-pi-ai/tests/login.spec.ts +198 -0
  60. package/vendor/dsh-llm-pi-ai/tests/mock-server.ts +82 -0
  61. package/vendor/dsh-llm-pi-ai/tests/provider-apis.e2e.ts +266 -0
  62. package/vendor/dsh-llm-pi-ai/tests/sdk-options.spec.ts +106 -0
  63. package/vendor/dsh-llm-pi-ai/tsconfig.json +4 -0
  64. package/vendor/dsh-llm-pi-ai/tsconfig.upstream.json +51 -0
@@ -0,0 +1,29 @@
1
+ # 使用 ChatCode CLI 模型
2
+
3
+ [English](chatcode-models.md) | 中文
4
+
5
+ 如果 ChatCode CLI 已有可用模型连接,而你希望在 Web 或 TUI 中选择它们,请使用本指南。旧配置文件必须包含 `customModels` 数组,显式提供模型 ID、基础 URL 和 API 密钥。插件会在自己的 ChatCode CLI 设置段不存在时导入该数组。
6
+
7
+ 当前兄弟代码库布局不需要、也不应把此 bundle 安装进 profile。直接从主库启动,源码包装器会为两个入口自动加载此 bundle,且不会修改 profile 清单:
8
+
9
+ ```sh
10
+ cd /Users/shiguifeng/IdeaProjects/chonggou/deepseek-harness
11
+ pnpm cco web
12
+ pnpm cco cli
13
+ ```
14
+
15
+ 插件会把 `~/.chatcode-cli/settings.json` 导入 `$CHATCODE_CLI_HOME/settings.yaml` 的 `llm-chatcode-config` 设置段。打开对话模型选择器,在“自定义模型”分组中选择导入模型。已有设置段(包括 `customModels: []`)始终优先,因此旧配置文件不会覆盖后续编辑。
16
+
17
+ 插件会先把公开选择解析为实际提供方模型 ID,再据此选择请求实现。ID 中只要包含 `deepseek`(不区分大小写),就使用原生 DeepSeek 适配器以及该条目的协议、`baseUrl` 和 `apiKey`;其 Anthropic Messages 请求携带 `x-api-key`,OpenAI 请求携带 `Authorization: Bearer`。其他 ID 均使用 pi-ai,其 Anthropic 路径继续携带 `Authorization: Bearer`,且不发送 `x-api-key`。
18
+
19
+ 如果使用其他文件,请新建可选 patch,并通过源码命令传入,例如 `pnpm cco web --patch ./chatcode-models.override.yml`。将示例替换为 Host 路径:
20
+
21
+ ```yaml
22
+ - id: llm-chatcode-config
23
+ config:
24
+ settingsPath: 'C:/Users/Owner/.chatcode-cli/settings.json'
25
+ ```
26
+
27
+ 导入后,请编辑 `$CHATCODE_CLI_HOME/settings.yaml` 中 `llm-chatcode-config` 下的 `customModels`;合法更新会刷新后续模型发现和请求,无需重启 Host。旧配置文件缺失时,profile 会继续运行,并在下次启动时重试导入。任一非法条目都会拒绝整次设置更新;插件不会只发布其中有效的子集。输出上限过大会在提供方请求前失败。HTTP 网关不会加密提示词或凭据;支持时请使用 HTTPS。
28
+
29
+ [包参考文档](../README.zh.md)定义协议映射、token 限制优先级、路由身份和不支持的设置。
@@ -0,0 +1,96 @@
1
+ # ChatCode operations reporting
2
+
3
+ English | [中文](chatcode-reporting.zh.md)
4
+
5
+ ## Summary
6
+
7
+ `dsh-llm-chatcode-config` derives operations records from committed ChatCode CLI Session events and sends them through the authenticated ChatCode backend. One Cordis row owns model import, account access, and reporting, while the implementation keeps reporting in `src/reporting/`. Reporting never reads custom-model API keys and never changes model, tool, or Session results.
8
+
9
+ ## Configuration
10
+
11
+ `reporting.enabled` defaults to `true` for direct mounts and bundle installations. Set it to `false` to disable all reporting; each sink remains independently configurable when reporting is enabled:
12
+
13
+ ```yaml
14
+ - id: llm-chatcode-config
15
+ config:
16
+ auth:
17
+ apiBaseUrl: https://chatcode.chinaunicom.cn
18
+ requestTimeoutMs: 5000
19
+ cvpChatCodeApiUrl: https://chatcode.chinaunicom.cn/cvp
20
+ reporting:
21
+ enabled: true
22
+ codeSave: true
23
+ conversationSync: true
24
+ chatCodeSession: true
25
+ codeBatchItems: 50
26
+ codeBatchChars: 524288
27
+ codeRetryDelayMs: 5000
28
+ codeOutboxDir: ''
29
+ includeSubagentConversationSync: false
30
+ modelKindRules: []
31
+ ```
32
+
33
+ `auth.apiBaseUrl` is the authentication backend root. `cvpChatCodeApiUrl` is the independent root for generated-code, conversation-sync, ChatCode message, and model-source configuration requests; it corresponds to `CVP_CHATCODE_API_URL` in `yuanjing-wanma-cli` and currently defaults to `http://127.0.0.1:8080`. Set it to `https://chatcode.chinaunicom.cn/cvp` for the production CVP server. Reporting shares `auth.requestTimeoutMs`. An empty `codeOutboxDir` resolves beneath `$CHATCODE_CLI_HOME/chatcode-reporting/code-save-outbox`; a configured value must be absolute. The plugin loads categories 0, 1, and 3 from ChatCode system configuration and classifies the actual normalized model `baseUrl`; missing or conflicting entries use category `2`. `modelKindRules` supplies ordered exact `provider` and `model` overrides before URL classification.
34
+
35
+ The bundle value is explicit in [`cordis.patch.yml`](../cordis.patch.yml). Edit `config.cvpChatCodeApiUrl` there for the sibling-checkout launcher, or override the complete row config through a later Cordis patch.
36
+
37
+ ## Event processing
38
+
39
+ The plugin installs reporting only when both credential and Session services are available. It listens to `session/created`, `session/event`, and `session/disposed`; each Session has an independent promise queue, ChatCode chat id, turn state, and tool-call deduplication set. Network latency does not enter `session/flush`. Session disposal and plugin shutdown drain work that is already queued.
40
+
41
+ ```text
42
+ committed Session event
43
+ |
44
+ v
45
+ per-Session projector
46
+ | | |
47
+ v v v
48
+ code outbox conversation sync ChatCode message chain
49
+ | | |
50
+ +-------------+------------------+
51
+ |
52
+ v
53
+ shared authenticated HTTP
54
+ ```
55
+
56
+ ## Generated code
57
+
58
+ The code sink accepts reportable fenced code from assistant text and code from successful `write`, `edit`, and `str_replace_editor` mutations. PTC `tool/ptc-dispatch-start` and `tool/ptc-dispatch` events use the same extraction rules. Failed tools, read-only tools, output-labelled fences, and result-like unlabelled fences do not contribute code.
59
+
60
+ Each code block is written atomically to an owner-only JSON file before upload to `/wanma/to/openai/v2/save-code`. The worker batches within `codeBatchItems` and `codeBatchChars`, deletes files only after backend acknowledgement, and retries retained files after `codeRetryDelayMs`. Malformed durable records move into the outbox `invalid` directory instead of being acknowledged.
61
+
62
+ ## Conversation synchronization
63
+
64
+ The conversation sink posts to `/wanma/api/v1/conversations/sync`. It derives the conversation id, working directory, creation time, first direct-user title, actual provider model name, message UUID, event time, turn number, and committed assistant usage from Session data. The Agent's initial route and each Web `model/selection` seed the model metadata before system or direct-user messages are sent; `request/header` and assistant sources then confirm the route used by the model. It synchronizes system, direct-user, assistant, tool-call, and tool-result records immediately without buffering them; only `user/message` events whose source is a direct user become human questions. A `turn/end` sends an empty message list with `isComplete: true` after earlier messages have been serialized.
65
+
66
+ Top-level Sessions synchronize by default. Subagent synchronization requires `includeSubagentConversationSync: true`; generated code from subagents is unaffected by this setting.
67
+
68
+ ## ChatCode message chain
69
+
70
+ The first top-level model response with non-zero usage creates a ChatCode chat through `/chatcode/session/create`. The plugin sends one `/chatcode/session/addMsgRecord` request for each committed `assistant/message`, using that response's `inputTokens` and `outputTokens` without adding cache counters or combining later responses in the turn. The first reportable response carries the direct-user question. Ordinary and failed tools create no zero-token rows. A response waits for its requested tools to settle; successful supported mutations append their code records to that response, and Session completion releases any response still waiting for a tool result. Every row includes the actual provider model name, actual model service `baseUrl`, and the resolved `modelKind`.
71
+
72
+ Subagent Sessions do not create a user-visible ChatCode chain. The message endpoint has no client idempotency key, so the plugin serializes requests per Session and does not retry an ambiguous failure.
73
+
74
+ ## Authentication and failure behavior
75
+
76
+ The transport receives the bundle's `ChatCodeAuthService` directly. It obtains access tokens through `accessToken()` and safe account labels through `status()`. Conversation and ChatCode message endpoints receive Bearer authentication; the generated-code endpoint receives the service's original `Authorization` format. Every request has a unique `X-Request-Id`, preserves any path in `cvpChatCodeApiUrl`, requires HTTPS except for loopback testing, and uses the configured timeout.
77
+
78
+ Conversation and ChatCode message failures emit one warning per Session and operation without rejecting model or tool work. Generated-code failures retain the durable outbox and schedule another attempt. Shutdown waits for queued Session operations and one bounded code delivery attempt; records without an acknowledgement remain available to the next process.
79
+
80
+ ## Verification
81
+
82
+ Run the package checks after changing reporting behavior:
83
+
84
+ ```text
85
+ pnpm run typecheck
86
+ pnpm test
87
+ pnpm run build
88
+ ```
89
+
90
+ The package tests cover code extraction, durable acknowledgement and quarantine, response-level token reporting, code-tool merging, subagent exclusion, authorization header formats, model-source URL classification, model import, and authentication.
91
+
92
+ ## Known limitations
93
+
94
+ - Conversation synchronization has no durable outbox and does not retry failed requests.
95
+ - ChatCode message requests are not retried when the server may have committed them.
96
+ - Model-source classification falls back to category `2` when the ChatCode system settings are unavailable or give a conflicting match.
@@ -0,0 +1,96 @@
1
+ # ChatCode 运营数据上报
2
+
3
+ [English](chatcode-reporting.md) | 中文
4
+
5
+ ## 概述
6
+
7
+ `dsh-llm-chatcode-config` 从已提交的 ChatCode CLI Session 事件派生运营记录,并通过已鉴权的 ChatCode 后端发送。一个 Cordis 配置行同时承载模型导入、账号访问和上报,内部实现仍隔离在 `src/reporting/`。上报不会读取自定义模型 API key,也不会改变模型、工具或 Session 结果。
8
+
9
+ ## 配置
10
+
11
+ 直接挂载和安装 bundle 时,`reporting.enabled` 均默认为 `true`。将它设置为 `false` 可关闭全部上报;启用上报时,每个数据出口仍可独立配置:
12
+
13
+ ```yaml
14
+ - id: llm-chatcode-config
15
+ config:
16
+ auth:
17
+ apiBaseUrl: https://chatcode.chinaunicom.cn
18
+ requestTimeoutMs: 5000
19
+ cvpChatCodeApiUrl: https://chatcode.chinaunicom.cn/cvp
20
+ reporting:
21
+ enabled: true
22
+ codeSave: true
23
+ conversationSync: true
24
+ chatCodeSession: true
25
+ codeBatchItems: 50
26
+ codeBatchChars: 524288
27
+ codeRetryDelayMs: 5000
28
+ codeOutboxDir: ''
29
+ includeSubagentConversationSync: false
30
+ modelKindRules: []
31
+ ```
32
+
33
+ `auth.apiBaseUrl` 是鉴权后端根地址。`cvpChatCodeApiUrl` 是生成代码、会话同步、ChatCode 消息和模型来源配置请求使用的独立根地址,对应 `yuanjing-wanma-cli` 中的 `CVP_CHATCODE_API_URL`,当前默认值为 `http://127.0.0.1:8080`。连接生产 CVP 服务时,将它设置为 `https://chatcode.chinaunicom.cn/cvp`。上报复用 `auth.requestTimeoutMs`。空的 `codeOutboxDir` 解析到 `$CHATCODE_CLI_HOME/chatcode-reporting/code-save-outbox`;配置值必须是绝对路径。插件从 ChatCode 系统配置加载分类 0、1、3,并按实际模型 `baseUrl` 的归一化结果分类;配置缺失或冲突时使用分类 `2`。`modelKindRules` 在 URL 分类前提供按顺序匹配的精确 `provider` 和 `model` 覆盖。
34
+
35
+ bundle 在 [`cordis.patch.yml`](../cordis.patch.yml) 中显式配置该地址。兄弟仓库启动方式可直接修改其中的 `config.cvpChatCodeApiUrl`,也可以通过后加载的 Cordis patch 覆盖完整配置行。
36
+
37
+ ## 事件处理
38
+
39
+ 插件只在凭据服务和 Session 服务同时可用时安装上报。它监听 `session/created`、`session/event` 和 `session/disposed`;每个 Session 拥有独立的 Promise 队列、ChatCode chat id、轮次状态和工具调用去重集合。网络延迟不会进入 `session/flush`。Session 释放和插件关闭会排空已经入队的任务。
40
+
41
+ ```text
42
+ committed Session event
43
+ |
44
+ v
45
+ per-Session projector
46
+ | | |
47
+ v v v
48
+ code outbox conversation sync ChatCode message chain
49
+ | | |
50
+ +-------------+------------------+
51
+ |
52
+ v
53
+ shared authenticated HTTP
54
+ ```
55
+
56
+ ## 生成代码
57
+
58
+ 代码出口接收助手文本中可上报的 fenced code,以及成功 `write`、`edit` 和 `str_replace_editor` 修改产生的代码。PTC 的 `tool/ptc-dispatch-start` 与 `tool/ptc-dispatch` 事件使用同一提取规则。失败工具、只读工具、标记为输出的代码围栏,以及形似结果的无标签围栏不会贡献代码。
59
+
60
+ 每个代码块在上传到 `/wanma/to/openai/v2/save-code` 前,先原子写入仅属主可访问的 JSON 文件。worker 在 `codeBatchItems` 和 `codeBatchChars` 范围内组批,只在后端确认后删除文件,并在 `codeRetryDelayMs` 后重试保留文件。格式错误的持久化记录会移入 outbox 的 `invalid` 目录,不会被确认。
61
+
62
+ ## 会话同步
63
+
64
+ 会话出口向 `/wanma/api/v1/conversations/sync` 发起请求。它从 Session 数据派生会话 id、工作目录、创建时间、首条直接用户消息标题、实际服务模型名、消息 UUID、事件时间、轮次和已提交的助手用量。Agent 的初始路由和每次 Web `model/selection` 会在系统消息或直接用户消息发送前写入模型元数据,随后由 `request/header` 和助手消息来源确认模型实际使用的路由。它不暂存消息,仍会立即同步系统、直接用户、助手、工具调用和工具结果记录;只有来源为直接用户的 `user/message` 事件会成为人工问题。`turn/end` 会在前序消息串行发送后,以空消息列表和 `isComplete: true` 标记完成。
65
+
66
+ 默认只同步顶层 Session。同步子代理需要设置 `includeSubagentConversationSync: true`;该配置不影响子代理生成代码的统计。
67
+
68
+ ## ChatCode 消息链
69
+
70
+ 首条具有非零用量的顶层模型 response 通过 `/chatcode/session/create` 创建 ChatCode chat。插件为每个已提交的 `assistant/message` 发送一次 `/chatcode/session/addMsgRecord`,只使用该 response 的 `inputTokens` 和 `outputTokens`,不会叠加缓存 Token,也不会合并同一轮的后续 response。首个可上报 response 携带直接用户问题。普通工具和失败工具不会产生 0 Token 行。response 会等待自身发起的工具结束;成功且受支持的修改会把代码记录追加到该 response,Session 完成时会释放仍在等待工具结果的 response。每条记录都包含实际服务模型名、实际模型服务 `baseUrl` 和解析后的 `modelKind`。
71
+
72
+ 子代理 Session 不创建用户可见的 ChatCode 消息链。消息接口没有客户端幂等键,因此插件按 Session 串行发送请求,并且不会重试结果不确定的失败。
73
+
74
+ ## 鉴权与失败行为
75
+
76
+ 传输层直接接收 bundle 的 `ChatCodeAuthService`。它通过 `accessToken()` 获取访问令牌,通过 `status()` 获取安全账号标签。会话和 ChatCode 消息接口使用 Bearer 鉴权;生成代码接口保留服务原有的 `Authorization` 格式。每个请求都有唯一 `X-Request-Id`,保留 `cvpChatCodeApiUrl` 中已有的路径,除回环测试外均要求 HTTPS,并使用配置的超时时间。
77
+
78
+ 会话与 ChatCode 消息失败会按 Session 和操作记录一次 warning,不会拒绝模型或工具工作。生成代码失败会保留持久化 outbox 并安排下一次尝试。关闭过程等待已入队的 Session 操作和一次有界代码发送;未得到确认的记录留给下一个进程。
79
+
80
+ ## 验证
81
+
82
+ 修改上报行为后运行包级检查:
83
+
84
+ ```text
85
+ pnpm run typecheck
86
+ pnpm test
87
+ pnpm run build
88
+ ```
89
+
90
+ 包测试覆盖代码提取、持久化确认与隔离、response 级 Token 上报、代码工具合并、子代理排除、鉴权头格式、模型来源 URL 分类、模型导入和账号鉴权。
91
+
92
+ ## 已知限制
93
+
94
+ - 会话同步没有持久化 outbox,也不会重试失败请求。
95
+ - 当服务端可能已经提交时,ChatCode 消息请求不会重试。
96
+ - ChatCode 系统配置不可用或匹配冲突时,模型来源分类会回退为 `2`。
@@ -0,0 +1,39 @@
1
+ # Agent Note: settings-owned ChatCode model sources
2
+
3
+ Status: implemented
4
+
5
+ English | [中文](2026-08-31-chatcode-model-source.zh.md)
6
+
7
+ ## Problem
8
+
9
+ ChatCode CLI settings combine model connections and credentials, and their token fields include a context-capacity fallback through `maxTokens`. Users need those entries to become editable Harness settings, including plaintext API keys, without changing Harness source. Anthropic-compatible entries also use Bearer authentication rather than the ordinary API-key header.
10
+
11
+ ## Decision
12
+
13
+ [The ChatCode bundle](../../README.md) owns the `llm-chatcode-config` Harness settings namespace. Absence of that user section triggers one complete import from the selected legacy Host file; presence, including an explicitly empty catalog, makes Harness settings authoritative and suppresses future imports. The settings provider writes only this namespace, preserving sibling settings. A missing or rejected legacy source writes nothing, contributes no models, and retries on the next startup.
14
+
15
+ The user section stores API keys in plaintext, while its schema marks them secret so settings wire descriptions redact their values. Resolved profiles contain no credentials. Private credential maps supply either the native DeepSeek key or pi-ai authentication, and isolated pi-ai auth storage prevents unrelated Harness login records from contributing authentication. Legacy parser diagnostics never quote source values. Explicit keys and URLs prevent ambient credentials from silently authenticating another gateway. [Actual-model adapter routing](2026-09-16-actual-model-adapter-routing.md) owns the transport choice and header semantics.
16
+
17
+ The stable public route `chatcode-custom`, displayed as `自定义模型`, contains every configured model. Private dispatch identity depends on protocol, normalized URL, and exact model ID, excluding keys, labels, and array positions. Because the public selection has only a provider and model ID, duplicate model IDs fail instead of selecting a connection arbitrarily. Source `maxTokens` supplies the output ceiling and historical context fallback. Requests above the ceiling fail instead of silently changing logged parameters.
18
+
19
+ Settings changes rebuild an immutable private dispatch snapshot and atomically update route registration. The next call observes the new models, endpoints, and credentials without restart. A prepared or running call retains the profiles and authentication from its starting snapshot, preventing mixed old and new connection state.
20
+
21
+ MiniMax M2 imports on OpenAI Chat Completions request `reasoning_split: true`. DMXAPI otherwise duplicates its reasoning in tagged answer content and a separate reasoning field. Request-level separation keeps live chunks, persisted messages, and replay aligned without deleting literal tags from arbitrary model output; previously recorded messages remain unchanged.
22
+
23
+ ## Alternatives considered
24
+
25
+ **Keep reading the legacy JSON at every activation.** This prevents Harness settings from becoming the user-owned source and requires restart after every edit.
26
+
27
+ **Depend on the installed `@deepseek-ai/dsh-llm-pi-ai`.** The ChatCode adapter needs request headers, isolated authentication, resolved private profiles, and `reasoning_split`. Depending on installation-specific exports couples activation to a matching Harness release and requires upstream source changes.
28
+
29
+ **Implement HTTP and SSE directly in this plugin.** The copied pi-ai adapter and the native DeepSeek adapter already retain transport, cancellation, tool arguments, replay, and error translation, so another plugin-owned protocol implementation would duplicate them.
30
+
31
+ **Register every private connection as a public provider.** This makes the model selector render one provider group per `customModels` entry and repeats each entry label. One public provider preserves the foreign array as one catalog while private routes retain connection-specific dispatch.
32
+
33
+ **Use credential hashes as private route IDs.** Credential-derived IDs change on rotation and put secret-derived identifiers in process state. Stable non-secret private identity preserves prepared dispatch through ordinary edits.
34
+
35
+ ## Consequences
36
+
37
+ The bundle composes with existing providers without changing Harness source, the loop, global credentials, or default selection. A missing or rejected first-import source leaves those providers usable and contributes no user-defined model. A syntactically invalid whole settings document remains a settings-provider startup failure because the external plugin cannot run before that provider. Configured models expose text input; DeepSeek-native routes also expose that adapter's reasoning choices. The package owns updates to its copied pi-ai source and pins the native DeepSeek peer version it verifies.
38
+
39
+ Loader tests exercise first import, sibling-section preservation, live route changes, prepared-call snapshots, and both real protocol clients against local HTTP fixtures. The headless snapshot retains the imported adapter for model resolution and replays provider output through the normal session log. Real-provider tests require an explicit source path and never automatically call models from a user's home file.
@@ -0,0 +1,39 @@
1
+ # Agent Note: 由设置持有的 ChatCode 模型来源
2
+
3
+ Status: implemented
4
+
5
+ [English](2026-08-31-chatcode-model-source.md) | 中文
6
+
7
+ ## 问题
8
+
9
+ ChatCode CLI 设置将模型连接和凭据放在一起,token 字段还包含经由 `maxTokens` 的上下文容量回退。用户需要在不修改 Harness 源码的前提下,把这些条目(包括明文 API key)变成可编辑的 Harness 设置。兼容 Anthropic 的条目还使用 Bearer 鉴权,而非普通 API 密钥请求头。
10
+
11
+ ## 决策
12
+
13
+ [ChatCode bundle](../../README.zh.md)持有 Harness 的 `llm-chatcode-config` 设置命名空间。用户配置段不存在时,插件从所选旧 Host 文件执行一次完整导入;配置段一旦存在(包括显式空目录),Harness 设置就成为权威来源,并抑制后续导入。设置提供方只写这个命名空间并保留其他配置段。旧来源缺失或被拒绝时,插件不写配置、不贡献模型,并在下次启动重试。
14
+
15
+ 用户配置段以明文存储 API key,但 schema 将其标记为 secret,因此设置 wire 描述会脱敏。解析后的 profile 不包含凭据。私有凭据映射分别提供原生 DeepSeek 密钥或 pi-ai 鉴权,隔离的 pi-ai 鉴权存储阻止无关 Harness 登录记录参与鉴权。旧文件解析诊断不引用来源值。显式密钥和 URL 要求避免环境凭据静默为其他网关鉴权。[实际模型适配器路由](2026-09-16-actual-model-adapter-routing.zh.md)持有传输选择和请求头语义。
16
+
17
+ 稳定的公开路由 `chatcode-custom` 显示为“自定义模型”,并包含所有已配置模型。私有分派身份由协议、规范化 URL 和精确模型 ID 决定,不包含密钥、标签和数组位置。由于公开选择只包含提供方和模型 ID,重复模型 ID 会失败,不会任意选择连接。来源 `maxTokens` 提供输出上限和历史上下文回退。超过上限的请求会失败,不会静默改变已记录的参数。
18
+
19
+ 设置修改会重建不可变私有分派快照,并原子更新路由注册。下一次调用无需重启即可观察新模型、端点和凭据。已经 prepare 或正在执行的调用保留开始时的 profile 和鉴权,避免混用新旧连接状态。
20
+
21
+ 通过 OpenAI Chat Completions 导入的 MiniMax M2 模型请求 `reasoning_split: true`。DMXAPI 在未分离时会把思考同时放入带标签的回答正文和独立思考字段。请求级分离使实时分片、持久消息和回放保持一致,无需从任意模型输出中删除字面标签;已记录的消息保持不变。
22
+
23
+ ## 考虑过的替代方案
24
+
25
+ **每次激活都读取旧 JSON。** 这会阻止 Harness 设置成为用户持有的来源,而且每次编辑后都需要重启。
26
+
27
+ **依赖安装环境中的 `@deepseek-ai/dsh-llm-pi-ai`。** ChatCode 适配器需要请求 header、隔离鉴权、已解析私有 profile 和 `reasoning_split`。依赖安装版本的导出会把激活绑定到匹配的 Harness 发行版,并要求上游源码改动。
28
+
29
+ **在本插件中直接实现 HTTP 和 SSE。** 复制的 pi-ai 适配器与原生 DeepSeek 适配器已经保留传输、取消、工具参数、回放和错误转换,再实现一套插件自有协议只会形成重复。
30
+
31
+ **将每个私有连接注册为公开提供方。** 这会让模型选择器为每个 `customModels` 条目渲染一个提供方分组,并重复显示条目标签。一个公开提供方能将外部数组保留为一个目录,私有路由仍负责连接专属的分派。
32
+
33
+ **使用凭据哈希作为私有路由 ID。** 由凭据派生的 ID 会在轮换时改变,并将密钥派生标识写入进程状态。稳定的非秘密私有身份可在普通编辑期间保持已预备调用的分派。
34
+
35
+ ## 影响
36
+
37
+ bundle 可与现有提供方组合,不修改 Harness 源码、循环、全局凭据或默认选择。首次导入来源缺失或被拒绝时,现有提供方仍可使用,bundle 不贡献任何用户自定义模型。整个设置文档存在 YAML 语法错误时,设置提供方仍会启动失败,因为外部插件无法先于该提供方运行。已配置模型公开文本输入;DeepSeek 原生路由还会公开该适配器的推理强度。包负责更新复制的 pi-ai 源码,并固定经过验证的原生 DeepSeek peer 版本。
38
+
39
+ Loader 测试验证首次导入、保留其他配置段、动态路由修改、已 prepare 调用快照,以及通过本地 HTTP 夹具运行的两种真实协议客户端。headless 快照保留导入适配器以解析模型,并通过正常会话日志回放提供方输出。真实提供方测试要求显式来源路径,绝不会自动调用用户主目录文件中的模型。
@@ -0,0 +1,31 @@
1
+ # Agent Note: actual model names select request adapters
2
+
3
+ Status: implemented
4
+
5
+ English | [中文](2026-09-16-actual-model-adapter-routing.zh.md)
6
+
7
+ ## Problem
8
+
9
+ ChatCode publishes public logical model IDs separately from the actual provider model IDs sent on the wire. Provider labels and public IDs do not reliably identify DeepSeek models, while routing every imported model through pi-ai prevents DeepSeek models from using the Harness native DeepSeek protocols.
10
+
11
+ ## Decision
12
+
13
+ The aggregate adapter resolves a public selection to its private route before choosing the request implementation. An actual provider model ID containing `deepseek`, case-insensitively, uses `@deepseek-ai/dsh-llm-deepseek`; every other model uses the bundled pi-ai adapter. Display names, provider labels, and public logical IDs do not affect this decision.
14
+
15
+ The native route maps imported OpenAI entries to DeepSeek Chat Completions and imported Anthropic entries to DeepSeek Messages. It builds an immutable connection snapshot from the entry's normalized base URL, validated literal API key, context capacity, output limit, and retry policy. Prepared calls retain that snapshot across settings refreshes.
16
+
17
+ DeepSeek Chat Completions sends the key as `Authorization: Bearer`; DeepSeek Messages sends it as `x-api-key`. Non-DeepSeek Anthropic models retain pi-ai's `Authorization: Bearer` behavior. A managed DeepSeek row without a usable key remains selectable but fails at request time with `MISSING_CREDENTIAL`, matching the existing managed-credential failure point.
18
+
19
+ ## Alternatives considered
20
+
21
+ **Route on the public logical ID or display name.** Managed catalogs may use neutral logical IDs and labels for DeepSeek provider models, so this would miss valid native routes.
22
+
23
+ **Route on the `provider` field.** Custom and managed sources use that field inconsistently, and the actual wire model name is the requirement's stable input.
24
+
25
+ **Add a user-visible adapter switch.** The requirement defines deterministic automatic routing, so another setting would permit configurations that contradict it.
26
+
27
+ **Keep every model on pi-ai.** This preserves the former authentication behavior but does not provide the native DeepSeek request path.
28
+
29
+ ## Consequences
30
+
31
+ DeepSeek-named Anthropic connections change their authentication header from `Authorization: Bearer` to `x-api-key`; their gateways must accept DeepSeek Messages semantics. Other Anthropic connections do not change. Loader and managed-catalog tests send both paths to local HTTP fixtures and assert the actual provider model ID, endpoint suffix, and authentication header.
@@ -0,0 +1,31 @@
1
+ # Agent Note: 实际模型名称选择请求适配器
2
+
3
+ Status: implemented
4
+
5
+ [English](2026-09-16-actual-model-adapter-routing.md) | 中文
6
+
7
+ ## 问题
8
+
9
+ ChatCode 会分别发布公开逻辑模型 ID 和实际发送到 wire 的提供方模型 ID。提供方标签和公开 ID 无法可靠识别 DeepSeek 模型,而让所有导入模型都走 pi-ai 会阻止 DeepSeek 模型使用 Harness 原生 DeepSeek 协议。
10
+
11
+ ## 决策
12
+
13
+ 聚合适配器先把公开选择解析为私有路由,再选择请求实现。实际提供方模型 ID 中只要包含 `deepseek`(不区分大小写),就使用 `@deepseek-ai/dsh-llm-deepseek`;其他模型均使用 bundle 内的 pi-ai 适配器。显示名称、提供方标签和公开逻辑 ID 不参与此决策。
14
+
15
+ 原生路由把导入的 OpenAI 条目映射到 DeepSeek Chat Completions,把导入的 Anthropic 条目映射到 DeepSeek Messages。它根据条目的规范化基础 URL、已校验明文 API key、上下文容量、输出限制和重试策略构建不可变连接快照。已 prepare 的调用在设置刷新后继续保留该快照。
16
+
17
+ DeepSeek Chat Completions 通过 `Authorization: Bearer` 发送密钥;DeepSeek Messages 通过 `x-api-key` 发送密钥。非 DeepSeek 的 Anthropic 模型保留 pi-ai 的 `Authorization: Bearer` 行为。缺少可用密钥的托管 DeepSeek 条目仍可选择,但会在请求时以 `MISSING_CREDENTIAL` 失败,与现有托管凭据的失败时点一致。
18
+
19
+ ## 考虑过的替代方案
20
+
21
+ **按公开逻辑 ID 或显示名称路由。** 托管目录可能为 DeepSeek 提供方模型使用中性逻辑 ID 和标签,因此这种方法会漏掉有效的原生路由。
22
+
23
+ **按 `provider` 字段路由。** 自定义来源和托管来源对该字段的使用并不一致,而实际 wire 模型名称才是需求规定的稳定输入。
24
+
25
+ **增加用户可见的适配器开关。** 需求定义了确定性的自动路由,增加设置会允许与该规则矛盾的配置。
26
+
27
+ **让所有模型继续走 pi-ai。** 这会保留原有鉴权行为,但不能提供原生 DeepSeek 请求路径。
28
+
29
+ ## 影响
30
+
31
+ DeepSeek 命名的 Anthropic 连接会把鉴权请求头从 `Authorization: Bearer` 改为 `x-api-key`;对应网关必须接受 DeepSeek Messages 语义。其他 Anthropic 连接不变。Loader 和托管目录测试会把两条路径发送到本地 HTTP 夹具,并断言实际提供方模型 ID、端点后缀和鉴权请求头。