Merge commit 'refs/codex/pr1006/master' into worktree/pr1006-merge-20260731

# Conflicts:
#	docs/architecture.i18n.yaml
#	docs/config-catalog.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/core.i18n.yaml
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
#	examples/headless-agent/cordis.yml
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/llm/llm-deepseek/README.md
#	packages/llm/llm-deepseek/README.zh.md
#	packages/llm/llm-deepseek/src/adapter.ts
#	packages/llm/llm-deepseek/src/index.ts
#	packages/llm/llm-deepseek/tests/adapter.spec.ts
#	packages/llm/llm/README.i18n.yaml
#	packages/llm/llm/README.md
#	packages/llm/llm/README.zh.md
#	packages/subagent/subagent-dsh-sdk/README.i18n.yaml
#	packages/ui/jsonrpc/README.i18n.yaml
This commit is contained in:
Tianyi Cui
2026-07-31 01:55:19 +08:00
664 changed files with 19076 additions and 2562 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/architecture.md
architecture.md: ef93b47702cdc8a2a6c68dc07edd629f0ab43170
architecture.zh.md: 4f1f05a3ce10f5de7edf192ff8d04e43a4dcb5c9
architecture.md: c6e14fac6436b2401509aaf8bb20ccaf29aeeafc
architecture.zh.md: 2e85f25eb3f40f58c8ffbfa7691bb793638c8b37

View File

@@ -46,6 +46,8 @@ Harnesses are [Cordis](cordis-primer.md) contexts; packages contribute services,
| `ctx.sessionPersistence` | [`session-persistence/`](../packages/session-persistence/README.md) | durable session-log storage |
| `ctx.sessionQuery` | [`session-query/`](../packages/session-query/README.md) | live-preferred exact/filter/trace queries over SQLite FTS, workspace-authorized model tools |
| `ctx.sessionTitle` | [`session-title/`](../packages/session-title/README.md) | log-backed fallbacks, one optional asynchronous provider |
| `ctx.settings` | [`settings/`](../packages/settings/README.md) | per-plugin user-settings namespaces layered over composition entries |
| `ctx.credentials` | [`credentials/`](../packages/credentials/README.md) | named secret references resolved per operation, never inlined in configuration |
| `ctx.directoryPicker` | [`host/directory-picker`](../packages/host/directory-picker/README.md) | GUI-host directory picking (`native`/`browse` interactions) |
| `ctx.typert` | [`typert/registry`](../packages/typert/registry/README.md) | runtime registry for generated package reflection and live Zod schemas |
| `ctx.invariants` | [`support/invariants`](../packages/support/invariants/README.md) | package-name-selected registry of package-owned runtime checks |

View File

@@ -46,6 +46,8 @@
| `ctx.sessionPersistence` | [`session-persistence/`](../packages/session-persistence/README.md) | 会话日志的持久化存储 |
| `ctx.sessionQuery` | [`session-query/`](../packages/session-query/README.md) | 基于 SQLite 全文搜索的实时优先精确检索/过滤/追踪、经工作区授权的模型工具 |
| `ctx.sessionTitle` | [`session-title/`](../packages/session-title/README.md) | 基于日志的回退标题和单个可选异步提供方 |
| `ctx.settings` | [`settings/`](../packages/settings/README.md) | 按插件划分的用户设置命名空间,分层叠加在装配条目之上 |
| `ctx.credentials` | [`credentials/`](../packages/credentials/README.md) | 具名密钥引用,按操作解析,绝不内联进配置 |
| `ctx.directoryPicker` | [`host/directory-picker`](../packages/host/directory-picker/README.md) | GUI 宿主目录选取(`native``browse` 交互) |
| `ctx.typert` | [`typert/registry`](../packages/typert/registry/README.md) | 生成的包反射和实时 Zod schema 的运行时注册表 |
| `ctx.invariants` | [`support/invariants`](../packages/support/invariants/README.md) | 按包名筛选包自有运行时检查的注册表 |

View File

@@ -41,6 +41,10 @@ flowchart LR
pkg_settings["settings"]
svc_settings["ctx.settings<br/>User-settings seam"]
pkg_settings_local["settings-local"]
pkg_apiproxy["apiproxy"]
pkg_credentials["credentials"]
svc_credentials["ctx.credentials<br/>Credential seam"]
pkg_credentials_local["credentials-local"]
pkg_session_telemetry["session-telemetry"]
svc_telemetry["ctx.telemetry<br/>Session telemetry seam"]
pkg_session_telemetry_otel["session-telemetry-otel"]
@@ -52,7 +56,6 @@ flowchart LR
svc_storageDomain["ctx.storageDomain<br/>Domain data facility"]
pkg_workspace["workspace"]
svc_workspace["ctx.workspace<br/>Workspace entity registry"]
pkg_apiproxy["apiproxy"]
svc_sessionQuery["ctx.sessionQuery<br/>Session reads, traces, filters, and search"]
pkg_session_reference["session-reference"]
pkg_tool_session_query["tool-session-query"]
@@ -174,6 +177,8 @@ flowchart LR
pkg_compact --> svc_compact
pkg_compact_basic --> svc_compact
pkg_compact_tool_result_prune --> svc_toolResultPrune
pkg_credentials --> svc_credentials
pkg_credentials_local --> svc_credentials
pkg_directory_picker --> svc_directoryPicker
pkg_directory_picker_browse --> svc_directoryPicker
pkg_directory_picker_native --> svc_directoryPicker
@@ -258,6 +263,9 @@ flowchart LR
svc_codeRuntime --> pkg_tools
svc_commands --> pkg_tui
svc_compact --> pkg_compact_basic
svc_credentials --> pkg_apiproxy
svc_credentials --> pkg_llm_deepseek
svc_credentials --> pkg_llm_pi_ai
svc_directoryPicker --> pkg_apiproxy
svc_fs --> pkg_tool_fs
svc_httpServer --> pkg_connection
@@ -296,6 +304,9 @@ flowchart LR
svc_sessions --> pkg_session_query
svc_sessions --> pkg_session_query_sqlite
svc_sessions --> pkg_subagent_inprocess
svc_settings --> pkg_apiproxy
svc_settings --> pkg_llm_deepseek
svc_settings --> pkg_llm_pi_ai
svc_skills --> pkg_tool_skill
svc_spillStore --> pkg_spill_policy
svc_storage --> pkg_storage_domain
@@ -346,7 +357,8 @@ flowchart LR
| `ctx.invariants` | `core` | [`invariants`](../packages/support/invariants) | - | [`session`](../packages/core/session), [`agent`](../packages/core/agent), [`scope`](../packages/core/scope), [`agent-loop`](../packages/core/agent-loop) | - | Companion subpaths register owner-local checks; the service owns selection, uniqueness, child fibers, and package-attributed failures. |
| `ctx.typert` | `core` | [`typert-registry`](../packages/typert/registry) | - | [`typert-loader`](../packages/typert/loader) | - | Plugins register live zod contributions directly or through dsh-typert-loader; runtime consumers query schemas and reflection metadata at their own edges. |
| `ctx.sessionPersistence` | `seam` | [`session-persistence`](../packages/session-persistence/session-persistence) | [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | [`agent-loop`](../packages/core/agent-loop), [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. |
| `ctx.settings` | `seam` | [`settings`](../packages/settings/settings) | [`settings-local`](../packages/settings/settings-local) | - | - | Plugins register namespace schemas and resolve layered values; providers store the raw document. No production consumer is migrated yet. |
| `ctx.settings` | `seam` | [`settings`](../packages/settings/settings) | [`settings-local`](../packages/settings/settings-local) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), `apiproxy` | - | Plugins register namespace schemas and resolve layered values; providers store the raw document. The LLM adapters register their entry config as the composition base under the user section; the web gateway serves redacted layered descriptors and writes the user layer. |
| `ctx.credentials` | `seam` | [`credentials`](../packages/credentials/credentials) | [`credentials-local`](../packages/credentials/credentials-local) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), `apiproxy` | - | Configuration carries references to secrets; providers own the values. Consumers resolve per operation, so a rotated credential reaches the very next request; the web gateway exposes value-free views and write-only storage. |
| `ctx.telemetry` | `seam` | [`session-telemetry`](../packages/telemetry/session-telemetry) | [`session-telemetry-otel`](../packages/telemetry/session-telemetry-otel) | - | - | The seam captures, redacts, and hands session records to one backend; nothing else consumes the service — its output leaves the process. |
| `ctx.storage` | `seam` | [`storage`](../packages/storage/storage) | [`storage-json`](../packages/storage/storage-json), [`storage-sqlite`](../packages/storage/storage-sqlite) | [`storage-domain`](../packages/storage/storage-domain) | - | Backends register side by side under names; data forms (domain first) mount on the hub and translate typed operations into opaque KV-unit primitives. |
| `ctx.storageDomain` | `core` | [`storage-domain`](../packages/storage/storage-domain) | - | [`workspace`](../packages/workspace/workspace) | - | Waits for every configured backend, then publishes the domain form as one lifecycle-bound service for typed durable state. |

View File

@@ -407,6 +407,24 @@ export interface ToolResultPruneConfig {
Source: [`packages/compact/compact-tool-result-prune/src/types.ts:4`](../packages/compact/compact-tool-result-prune/src/types.ts)
## `@deepseek-ai/dsh-credentials-local`
```ts config-catalog
/** Plugin config: file location and hot-reload behavior. */
export interface Config {
/** Credentials document path; defaults to `.env` under the harness home. */
path?: string
/** Harness home used when `path` is omitted; defaults to `$DSH_HOME` or `~/.dsh`. */
dshHome?: string
/** Watch the document and hot-publish external edits; defaults to true. */
watch?: boolean
/** Watcher write-settle window in milliseconds; defaults to 100. */
debounceMs?: number
}
```
Source: [`packages/credentials/credentials-local/src/index.ts:26`](../packages/credentials/credentials-local/src/index.ts)
## `@deepseek-ai/dsh-fs-local`
```ts config-catalog
@@ -602,15 +620,18 @@ Requires: `llm`
```ts config-catalog
/**
* Plugin config, validated by the same-named schemastery schema. Every field
* is optional in yml: credentials/endpoint fall back to the environment (a
* missing API key fails plugin load, not the first call), omitted thinking
* mode uses the provider default, and omitted reasoning effort resolves to
* `high`.
* Plugin config, validated by the same-named schemastery schema and doubling
* as the `llm-deepseek` settings-section shape. Every field is optional in
* yml: a missing API key resolves through {@link Config.apiKeyEnv} at each
* request (a request without any key fails with `MISSING_CREDENTIAL`, not at
* plugin load), omitted thinking mode uses the provider default, and omitted
* reasoning effort resolves to `high`.
*/
export interface Config {
/** API key; falls back to $DEEPSEEK_API_KEY. Required one way or the other. */
/** Literal API key; prefer {@link apiKeyEnv} so no secret enters configuration files. */
apiKey?: string
/** Credential reference (environment-variable name) resolved per request; defaults to `DEEPSEEK_API_KEY`. */
apiKeyEnv?: string
/** Endpoint base; falls back to $DEEPSEEK_BASE_URL, then the public API. */
baseURL?: string
/** Deployment thinking policy; `disabled` limits every conversation request to `off`. */
@@ -644,25 +665,29 @@ export interface DeepSeekCatalogModel {
Depends on: [`RetryPolicyConfig`](../packages/llm/llm/src/index.ts)
Source: [`packages/llm/llm-deepseek/src/index.ts:46`](../packages/llm/llm-deepseek/src/index.ts)
Source: [`packages/llm/llm-deepseek/src/index.ts:60`](../packages/llm/llm-deepseek/src/index.ts)
## `@deepseek-ai/dsh-llm-pi-ai`
Requires: `llm`
```ts config-catalog
/** Plugin configuration: the non-empty provider profiles this instance owns. */
/** Plugin configuration: the provider routes this instance owns. */
export interface Config {
/** Non-empty set of pi-ai provider routes this adapter instance owns. */
providers: PiAiProviderProfile[]
/**
* pi-ai provider routes, keyed by provider. An empty (or omitted) dict is
* the dormant settings-driven posture: the adapter mounts with no routes
* and registers them the moment a settings section supplies profiles.
*/
providers?: Record<string, PiAiProviderProfile>
}
/** Configuration for one pi-ai provider route. */
/** Configuration for one pi-ai provider route; the `providers` dict key IS the route. */
export interface PiAiProviderProfile {
/** pi-ai provider catalog name and Harness route key. */
provider: string
/** Provider credential; when absent pi-ai uses its provider-native ambient discovery. */
/** Literal provider credential; prefer {@link apiKeyEnv}. With both absent pi-ai uses its provider-native ambient discovery. */
apiKey?: string
/** Credential reference (environment-variable name) resolved per request through `ctx.credentials`. */
apiKeyEnv?: string
/** Override the selected catalog model's endpoint without changing its protocol metadata. */
baseURL?: string
/** Provider request headers; Harness attribution wins reserved names. */
@@ -688,7 +713,7 @@ export interface PiAiProviderProfile {
Depends on: `CacheRetention` (`@earendil-works/pi-ai`) · `ModelThinkingLevel` (`@earendil-works/pi-ai`) · [`RetryPolicyConfig`](../packages/llm/llm/src/index.ts) · `ThinkingBudgets` (`@earendil-works/pi-ai`) · `Transport` (`@earendil-works/pi-ai`)
Source: [`packages/llm/llm-pi-ai/src/config.ts:54`](../packages/llm/llm-pi-ai/src/config.ts)
Source: [`packages/llm/llm-pi-ai/src/config.ts:62`](../packages/llm/llm-pi-ai/src/config.ts)
## `@deepseek-ai/dsh-llm-replay`
@@ -1486,7 +1511,7 @@ export interface Config {
* fails.
*/
cwd?: string
/** Provider route the child runtime initializes with (default `deepseek`). */
/** Provider route the child runtime initializes with (default `deepseek-official`). */
provider: string
/** Model the child runtime initializes with (default `deepseek-v4-flash`). */
model: string
@@ -1684,8 +1709,10 @@ Source: [`packages/fs/tool-fs/src/index.ts:24`](../packages/fs/tool-fs/src/index
Requires: `tools` · `systemPrompt` · `bash`
```ts config-catalog
/** Plugin config (all optional — `Config` supplies the defaults). */
/** Plugin config; over-cap glob sampling is an explicit deployment choice and the remaining fields have defaults. */
export interface Config {
/** Whether an over-cap `glob` page is sampled across top-level entries instead of taking the modification-time head. */
sampleOverCapGlobResults: boolean
/** Max paths one `glob` call retains inline; later paths go to the formatted spill file. */
globMaxResults?: number
/** Max flat matches one `grep` call retains inline; later matches go to the formatted spill file. */
@@ -1909,7 +1936,7 @@ export interface Config {
}
```
Source: [`packages/web/tool-web/src/index.ts:35`](../packages/web/tool-web/src/index.ts)
Source: [`packages/web/tool-web/src/index.ts:37`](../packages/web/tool-web/src/index.ts)
## `@deepseek-ai/dsh-tool-workflow`
@@ -1955,7 +1982,7 @@ export interface Config {
export type ToolPresentationMode = 'native' | 'code' | 'both'
```
Source: [`packages/core/tools/src/index.ts:578`](../packages/core/tools/src/index.ts)
Source: [`packages/core/tools/src/index.ts:582`](../packages/core/tools/src/index.ts)
## `@deepseek-ai/dsh-tui`
@@ -2298,6 +2325,7 @@ Abstract service classes — a deployment loads a concrete implementation packag
- `@deepseek-ai/dsh-bash` — abstract `BashExecutor` ([`packages/bash/bash/src/index.ts`](../packages/bash/bash/src/index.ts))
- `@deepseek-ai/dsh-code-runtime` — abstract `CodeRuntime` ([`packages/code-runtime/code-runtime/src/index.ts`](../packages/code-runtime/code-runtime/src/index.ts))
- `@deepseek-ai/dsh-compact` — abstract `CompactService` ([`packages/compact/compact/src/index.ts`](../packages/compact/compact/src/index.ts))
- `@deepseek-ai/dsh-credentials` — abstract `Credentials` ([`packages/credentials/credentials/src/index.ts`](../packages/credentials/credentials/src/index.ts))
- `@deepseek-ai/dsh-fs` — abstract `FileSystem` ([`packages/fs/fs/src/index.ts`](../packages/fs/fs/src/index.ts))
- `@deepseek-ai/dsh-host-directory-picker` — abstract `DirectoryPicker` ([`packages/host/directory-picker/src/index.ts`](../packages/host/directory-picker/src/index.ts))
- `@deepseek-ai/dsh-sandbox` — abstract `SandboxProvider` ([`packages/sandbox/sandbox/src/index.ts`](../packages/sandbox/sandbox/src/index.ts))
@@ -2317,7 +2345,9 @@ Imported as libraries by other packages; a `cordis.yml` cannot load them.
- `@deepseek-ai/dsh-acp-snapshot` ([`packages/support/acp-snapshot/src/index.ts`](../packages/support/acp-snapshot/src/index.ts))
- `@deepseek-ai/dsh-agent-loop-testkit` ([`packages/support/agent-loop-testkit/src/index.ts`](../packages/support/agent-loop-testkit/src/index.ts))
- `@deepseek-ai/dsh-app-boot` ([`packages/ui/app-boot/src/index.ts`](../packages/ui/app-boot/src/index.ts))
- `@deepseek-ai/dsh-atomic-write` ([`packages/util/atomic-write/src/index.ts`](../packages/util/atomic-write/src/index.ts))
- `@deepseek-ai/dsh-brand` ([`packages/util/brand/src/index.ts`](../packages/util/brand/src/index.ts))
- `@deepseek-ai/dsh-client-schema-form` ([`packages/client/schema-form/src/index.ts`](../packages/client/schema-form/src/index.ts))
- `@deepseek-ai/dsh-client-test-runtime` ([`packages/client/test-runtime/src/index.ts`](../packages/client/test-runtime/src/index.ts))
- `@deepseek-ai/dsh-client-ui-primitives` ([`packages/client/ui-primitives/src/index.ts`](../packages/client/ui-primitives/src/index.ts))
- `@deepseek-ai/dsh-client-ui-slots` ([`packages/client/ui-slots/src/index.ts`](../packages/client/ui-slots/src/index.ts))

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
adding-a-tool.md: d06e3d8e3c7da1f71a55bf9c4f56cd4b2cc03697
adding-a-tool.zh.md: 53f608eba3b26b124f873990fa13ce1572c0baf2
# pnpm run verify-translation-pairing --write docs/cookbook/adding-a-tool.md
adding-a-tool.md: a85de0feeeee307ac645f8c2967bb44521d059a8
adding-a-tool.zh.md: 8e4e6a1128f4f2ad3b4d42c2b88edaf4d8d89af1

View File

@@ -78,6 +78,7 @@ Both methods return a **`card`-tagged render intent** — pick the card kind tha
- `generic` supplies an optional title and content.
- `terminal` supplies raw output and optional exit metadata; each UI renders its capable or fallback view.
- `diff` supplies applied hunks, often derived by `output.presentationMeta` and carried in persisted `result.meta` so replay reproduces them. Mutation tools keep a diff result because the completed view replaces the pending card.
- `web` supplies a completed web retrieval, discriminated by `kind: 'search' | 'fetch'` (the structured search sources or the fetch summary), derived from `result.meta`; it carries no body copy, so a UI without the `web` capability falls back to the raw result content. (tool-web `web_search`/`web_fetch`.)
Hard rules (they bite if broken):

View File

@@ -78,6 +78,7 @@ producer 提供同步的 `cancel`、在资源清理后 settle 且不 reject 的
- `generic` 提供可选的标题和内容。
- `terminal` 提供原始输出和可选的退出元数据;各 UI 根据自身能力渲染对应视图或回退视图。
- `diff` 提供已应用的 hunk通常由 `output.presentationMeta` 派生并通过持久化的 `result.meta` 携带,使回放能重现它们。变更类工具保留 diff 结果,因为完成后的视图会替换 pending 卡片。
- `web` 提供已完成的 web 检索,以 `kind: 'search' | 'fetch'` 区分(结构化的搜索来源或抓取摘要),由 `result.meta` 派生;它不携带正文副本,因此不具备 `web` 能力的 UI 回退到原始结果内容。tool-web `web_search``web_fetch`。)
硬性规则(违反会出问题):

View File

@@ -439,6 +439,32 @@ A command was registered or unregistered. This is an unfiltered registry notific
Source: [`packages/ui/commands/src/index.ts:154`](../../packages/ui/commands/src/index.ts)
## `credentials/*`
### `credentials/updated` — emit
Committed change to a provider-managed credential source: a `set`, an `unset`, or an external edit observed in storage. Ambient process-environment changes are not observable and never emit. Listener failures are contained and logged — a sync throw and an async rejection alike — without changing the committed operation's outcome, except `INVARIANT`-coded failures, which rethrow after every listener ran; that rethrow reaches the emitter only from synchronous listeners, so invariant checks on this event must not be async functions.
```ts cordis-catalog
/**
* Committed change to a provider-managed credential source: a `set`, an
* `unset`, or an external edit observed in storage. Ambient
* process-environment changes are not observable and never emit. Listener
* failures are contained and logged — a sync throw and an async rejection
* alike — without changing the committed operation's outcome, except
* `INVARIANT`-coded failures, which rethrow after every listener ran;
* that rethrow reaches the emitter only from synchronous listeners, so
* invariant checks on this event must not be async functions.
* @param ref - the reference whose stored value changed.
* @mode emit
*/
'credentials/updated'(ref: CredentialRef): void
```
Types: [CredentialRef](../core-data-structures/credentials.md)
Source: [`packages/credentials/credentials/src/index.ts:67`](../../packages/credentials/credentials/src/index.ts)
## `domain/*`
### `domain/changed` — emit
@@ -545,6 +571,25 @@ Source: [`packages/goal/goal/src/domain.ts:135`](../../packages/goal/goal/src/do
## `llm/*`
### `llm/adapters-updated` — emit
The provider topology changed: an adapter registered or unregistered routes, or the configurable-provider directory gained or lost entries. This is a payload-free registry notification fired at each commit point (including registration disposal); consumers re-read `listProviders()`, `listModels()`, or `listConfigurableProviders()` for the new state. Observer failures are contained and cannot veto the registry mutation.
```ts cordis-catalog
/**
* The provider topology changed: an adapter registered or unregistered
* routes, or the configurable-provider directory gained or lost entries.
* This is a payload-free registry notification fired at each commit point
* (including registration disposal); consumers re-read `listProviders()`,
* `listModels()`, or `listConfigurableProviders()` for the new state.
* Observer failures are contained and cannot veto the registry mutation.
* @mode emit
*/
'llm/adapters-updated'(): void
```
Source: [`packages/llm/llm/src/index.ts:70`](../../packages/llm/llm/src/index.ts)
### `llm/stream` — waterfall
Waterfall around every streaming model call (retry, replay, routing). Bound to the LlmService; call `next()` to reach the resolved adapter's stream, or yield your own chunks to short-circuit.
@@ -567,7 +612,7 @@ Waterfall around every streaming model call (retry, replay, routing). Bound to t
Types: [GenerateOptions](../core-data-structures/core.md) · [LlmService](../core-data-structures/llm-streaming.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
Source: [`packages/llm/llm/src/index.ts:58`](../../packages/llm/llm/src/index.ts)
Source: [`packages/llm/llm/src/index.ts:59`](../../packages/llm/llm/src/index.ts)
## `session/*`
@@ -661,6 +706,29 @@ Source: [`packages/core/session/src/index.ts:103`](../../packages/core/session/s
## `settings/*`
### `settings/document-updated` — emit
One registered namespace's RAW user section changed, whether or not the resolved value did. `settings/updated` is the consumer-facing event and stays deep-equal-gated; this one exists for configuration surfaces, which must learn that a field went from inherited to overridden (same resolved value, different meaning) and that their held revision is stale. Listener containment matches `settings/updated`.
```ts cordis-catalog
/**
* One registered namespace's RAW user section changed, whether or not the
* resolved value did. `settings/updated` is the consumer-facing event and
* stays deep-equal-gated; this one exists for configuration surfaces,
* which must learn that a field went from inherited to overridden (same
* resolved value, different meaning) and that their held revision is
* stale. Listener containment matches `settings/updated`.
* @param ns - the namespace whose stored section changed.
* @param revision - the namespace's new revision.
* @mode emit
*/
'settings/document-updated'(ns: SettingsNamespace, revision: number): void
```
Types: [SettingsNamespace](../core-data-structures/settings.md)
Source: [`packages/settings/settings/src/index.ts:150`](../../packages/settings/settings/src/index.ts)
### `settings/updated` — emit
Committed change to one registered namespace's resolved value. Emitted after the provider persisted (for `update`) or published (`provider`) the change; never emitted when the resolved value is deep-equal. Listener failures are contained and logged — a sync throw and an async rejection alike — except `INVARIANT`-coded failures, which rethrow after every listener ran; that rethrow reaches the emitter only from synchronous listeners, so invariant checks on this event must not be async functions.
@@ -686,7 +754,7 @@ Committed change to one registered namespace's resolved value. Emitted after the
Types: [SettingsNamespace](../core-data-structures/settings.md) · [SettingsUpdateSource](../core-data-structures/settings.md)
Source: [`packages/settings/settings/src/index.ts:108`](../../packages/settings/settings/src/index.ts)
Source: [`packages/settings/settings/src/index.ts:137`](../../packages/settings/settings/src/index.ts)
## `skills/*`
@@ -870,7 +938,7 @@ A tool was registered or unregistered, or a scoped restriction changed (the avai
'tools/change'(): void
```
Source: [`packages/core/tools/src/index.ts:156`](../../packages/core/tools/src/index.ts)
Source: [`packages/core/tools/src/index.ts:160`](../../packages/core/tools/src/index.ts)
### `tools/code-dispatch-log` — waterfall
@@ -894,7 +962,7 @@ Shape the DURABLE LOG COPY of one `run_code` sub-dispatch outcome before the bri
Types: [CodeDispatchLog](../core-data-structures/tools.md) · [ContentBlock](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md) · [ToolRegistry](../core-data-structures/tools.md)
Source: [`packages/core/tools/src/index.ts:138`](../../packages/core/tools/src/index.ts)
Source: [`packages/core/tools/src/index.ts:142`](../../packages/core/tools/src/index.ts)
### `tools/execute` — waterfall
@@ -916,7 +984,7 @@ Around-dispatch waterfall for timeout, retry, or metrics. `next()` returns a nor
Types: [Scoped](../core-data-structures/scope.md) · [ToolDispatchExecution](../core-data-structures/tools.md) · [ToolExecutionResult](../core-data-structures/tools.md) · [ToolRegistry](../core-data-structures/tools.md)
Source: [`packages/core/tools/src/index.ts:113`](../../packages/core/tools/src/index.ts)
Source: [`packages/core/tools/src/index.ts:117`](../../packages/core/tools/src/index.ts)
### `tools/post-execute` — waterfall
@@ -939,7 +1007,7 @@ Accept, replace, enrich, or block a normalized dispatch result. `next()` accepts
Types: [PostToolDecision](../core-data-structures/tools.md) · [Scoped](../core-data-structures/scope.md) · [ToolExecution](../core-data-structures/tools.md) · [ToolExecutionResult](../core-data-structures/tools.md) · [ToolRegistry](../core-data-structures/tools.md)
Source: [`packages/core/tools/src/index.ts:125`](../../packages/core/tools/src/index.ts)
Source: [`packages/core/tools/src/index.ts:129`](../../packages/core/tools/src/index.ts)
### `tools/pre-execute` — waterfall
@@ -960,7 +1028,7 @@ Allow, deny, or ask before dispatch. `next()` delegates to allow; missing approv
Types: [PreToolDecision](../core-data-structures/tools.md) · [Scoped](../core-data-structures/scope.md) · [ToolExecution](../core-data-structures/tools.md) · [ToolRegistry](../core-data-structures/tools.md)
Source: [`packages/core/tools/src/index.ts:102`](../../packages/core/tools/src/index.ts)
Source: [`packages/core/tools/src/index.ts:106`](../../packages/core/tools/src/index.ts)
### `tools/result` — emit
@@ -979,7 +1047,7 @@ Observe the frozen, lossless-JSON final outcome. Listener failures are contained
Types: [Scoped](../core-data-structures/scope.md) · [ToolExecution](../core-data-structures/tools.md) · [ToolExecutionResult](../core-data-structures/tools.md) · [ToolRegistry](../core-data-structures/tools.md)
Source: [`packages/core/tools/src/index.ts:146`](../../packages/core/tools/src/index.ts)
Source: [`packages/core/tools/src/index.ts:150`](../../packages/core/tools/src/index.ts)
## `workflow/*`

View File

@@ -488,6 +488,52 @@ Types: [CompactionResult](../core-data-structures/compaction.md) · [CompactionT
Source: [`packages/compact/compact/src/index.ts:54`](../../packages/compact/compact/src/index.ts)
## `ctx.credentials` — `Credentials` (abstract seam)
Abstract credential service. Providers implement the four operations over their source layers; one seam-wide rule binds them all: an empty stored value is absent everywhere — `resolve` skips it, `describe` reports it unconfigured — so a blank never masquerades as a configured secret.
```ts cordis-catalog
/**
* Resolve one reference to its current value. Resolution is per call:
* consumers re-resolve at each operation and must not cache across
* operations — that per-operation read is what makes a changed credential
* reach the next operation without a restart.
* @param ref - the reference to resolve.
* @returns the value and its source, or `undefined` while unconfigured.
*/
abstract resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined>
/**
* Describe one reference for configuration surfaces without exposing the
* value.
* @param ref - the reference to describe.
* @returns configured state, supplying source, and writability.
*/
abstract describe(ref: CredentialRef): Promise<CredentialInfo>
/**
* Durably store one value in the provider-managed writable source. Rejects
* while a read-only source shadows the reference — the write would appear
* to succeed while resolution keeps returning the shadowing value — and
* rejects an empty value (use {@link unset}).
* @param ref - the reference to store.
* @param value - the non-empty secret value.
*/
abstract set(ref: CredentialRef, value: string): Promise<void>
/**
* Remove one reference from the provider-managed writable source; removing
* an absent reference is a no-op. Rejects while a read-only source shadows
* the reference, like {@link set}.
* @param ref - the reference to remove.
*/
abstract unset(ref: CredentialRef): Promise<void>
```
Types: [CredentialInfo](../core-data-structures/credentials.md) · [CredentialRef](../core-data-structures/credentials.md) · [ResolvedCredential](../core-data-structures/credentials.md)
Source: [`packages/credentials/credentials/src/index.ts:77`](../../packages/credentials/credentials/src/index.ts)
## `ctx.directoryPicker` — `DirectoryPicker` (abstract seam)
Abstract directory-picking service. Subclass, implement `capability()`, and load the subclass as a plugin — it registers as `ctx.directoryPicker` (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior). The capability object must be stable for the service lifetime: consumers may capture it across calls.
@@ -744,9 +790,9 @@ The abstract `llm` service: an adapter registry plus a streaming model-call surf
* Disposed with the fiber.
* @param providers - every provider route this adapter should serve.
* @param adapter - the adapter that streams calls for those providers.
* @returns the disposer that unregisters all of them.
* @returns the disposer, carrying {@link AdapterRegistrationHandle.replace}.
*/
registerAdapter(providers: string[], adapter: LlmAdapter): () => void
registerAdapter(providers: string[], adapter: LlmAdapter): AdapterRegistrationHandle
/**
* Describe provider routes with a registered adapter.
@@ -754,6 +800,22 @@ registerAdapter(providers: string[], adapter: LlmAdapter): () => void
*/
listProviders(): LlmProviderInfo[]
/**
* Declare provider routes an adapter plugin can activate through
* configuration. Registration is all-or-nothing: an empty list, invalid
* entry, or a provider already declared by any registration throws
* `LlmError` without registering the rest. Disposed with the fiber.
* @param entries - every configurable provider this plugin owns.
* @returns the disposer that withdraws all of them.
*/
registerConfigurableProviders(entries: readonly LlmConfigurableProvider[]): () => void
/**
* List every declared configurable provider, registered or dormant.
* @returns detached directory entries in declaration order.
*/
listConfigurableProviders(): LlmConfigurableProvider[]
/**
* Resolve the retry policy captured when one provider route was registered.
* @param provider - registered provider route to inspect.
@@ -818,9 +880,9 @@ async prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<Prepared
stream(options: GenerateOptions): AsyncIterable<StreamChunk>
```
Types: [GenerateOptions](../core-data-structures/core.md) · [LlmAdapter](../core-data-structures/llm-streaming.md) · [LlmCallConfig](../core-data-structures/core.md) · [LlmModelInfo](../core-data-structures/core.md) · [LlmProviderInfo](../core-data-structures/core.md) · [LlmResolvedModelInfo](../core-data-structures/core.md) · [PreparedLlmCall](../core-data-structures/llm-streaming.md) · [ResolvedRetryPolicy](../core-data-structures/llm-streaming.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
Types: [AdapterRegistrationHandle](../core-data-structures/core.md) · [GenerateOptions](../core-data-structures/core.md) · [LlmAdapter](../core-data-structures/llm-streaming.md) · [LlmCallConfig](../core-data-structures/core.md) · [LlmConfigurableProvider](../core-data-structures/core.md) · [LlmModelInfo](../core-data-structures/core.md) · [LlmProviderInfo](../core-data-structures/core.md) · [LlmResolvedModelInfo](../core-data-structures/core.md) · [PreparedLlmCall](../core-data-structures/llm-streaming.md) · [ResolvedRetryPolicy](../core-data-structures/llm-streaming.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
Source: [`packages/llm/llm/src/index.ts:193`](../../packages/llm/llm/src/index.ts)
Source: [`packages/llm/llm/src/index.ts:229`](../../packages/llm/llm/src/index.ts)
## `ctx.permission` — `PermissionService`
@@ -908,7 +970,7 @@ set(agent: Agent, active: boolean): 'committed' | 'queued' | 'cancelled' | 'noop
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/plan/plan-mode/src/index.ts:179`](../../packages/plan/plan-mode/src/index.ts)
Source: [`packages/plan/plan-mode/src/index.ts:182`](../../packages/plan/plan-mode/src/index.ts)
## `ctx.pty` — `PtyService`
@@ -1657,10 +1719,13 @@ Abstract settings service. Providers implement raw-document storage (`load`/`per
register<T>(ns: SettingsNamespace, schema: z<T>, options?: SettingsRegisterOptions<T>): SettingsScope<T>
/**
* Describe every registered namespace for configuration surfaces.
* Describe every registered namespace for configuration surfaces, including
* the composition `base` and raw user layers so a form can mark which fields
* the user overrode (presence in `user`) and what a reset returns to.
* @param options - redaction switch; wire surfaces must redact.
* @returns one descriptor per registered namespace, in registration order.
*/
describe(): SettingsDescriptor[]
describe(options?: SettingsDescribeOptions): SettingsDescriptor[]
/**
* Read one registered namespace's resolved value.
@@ -1677,8 +1742,10 @@ get(ns: SettingsNamespace): unknown
* merging over the previous write's committed section.
* @param ns - the registered namespace to update.
* @param patch - plain-object patch over the user section.
* @param expectedRevision - the descriptor `revision` the caller read; a
* namespace that moved past it rejects with {@link SettingsConflictError}.
*/
async update(ns: SettingsNamespace, patch: object): Promise<void>
async update(ns: SettingsNamespace, patch: object, expectedRevision?: number): Promise<void>
/**
* Replace one registered namespace's user section wholesale, validate,
@@ -1687,13 +1754,29 @@ async update(ns: SettingsNamespace, patch: object): Promise<void>
* merge-only patch cannot express (`replace({})` re-inherits everything).
* @param ns - the registered namespace to replace.
* @param section - the complete next user section.
* @param expectedRevision - the descriptor `revision` the caller read; a
* namespace that moved past it rejects with {@link SettingsConflictError}.
*/
async replace(ns: SettingsNamespace, section: object): Promise<void>
async replace(ns: SettingsNamespace, section: object, expectedRevision?: number): Promise<void>
/**
* Apply path-addressed edits to one registered namespace's user section,
* validate, persist, then commit and emit. The ops are applied to the
* section as it stands when the write reaches the front of the queue, so a
* caller never has to restate fields it did not touch — and, crucially,
* cannot delete fields it never saw. This is the write path for any caller
* holding a redacted view; `replace` remains the wholesale reset.
* @param ns - the registered namespace to edit.
* @param ops - ordered path edits; later ops observe earlier ones.
* @param expectedRevision - the descriptor `revision` the caller read; a
* namespace that moved past it rejects with {@link SettingsConflictError}.
*/
async mutate(ns: SettingsNamespace, ops: readonly SettingsPathOp[], expectedRevision?: number): Promise<void>
```
Types: [SettingsDescriptor](../core-data-structures/settings.md) · [SettingsNamespace](../core-data-structures/settings.md) · [SettingsRegisterOptions](../core-data-structures/settings.md) · [SettingsScope](../core-data-structures/settings.md)
Types: [SettingsDescribeOptions](../core-data-structures/settings.md) · [SettingsDescriptor](../core-data-structures/settings.md) · [SettingsNamespace](../core-data-structures/settings.md) · [SettingsPathOp](../core-data-structures/settings.md) · [SettingsRegisterOptions](../core-data-structures/settings.md) · [SettingsScope](../core-data-structures/settings.md)
Source: [`packages/settings/settings/src/index.ts:250`](../../packages/settings/settings/src/index.ts)
Source: [`packages/settings/settings/src/index.ts:365`](../../packages/settings/settings/src/index.ts)
## `ctx.skills` — `SkillService`
@@ -2230,7 +2313,7 @@ async execute(exec: ToolExecutionInput): Promise<ToolExecutionResult>
Types: [ScopeKey](../core-data-structures/scope.md) · [ToolDefinition](../core-data-structures/tools.md) · [ToolExecutionInput](../core-data-structures/tools.md) · [ToolExecutionMode](../core-data-structures/tools.md) · [ToolExecutionResult](../core-data-structures/tools.md) · [ToolGuard](../core-data-structures/tools.md) · [ToolRestriction](../core-data-structures/tools.md) · [ToolSchema](../core-data-structures/tools.md)
Source: [`packages/core/tools/src/index.ts:700`](../../packages/core/tools/src/index.ts)
Source: [`packages/core/tools/src/index.ts:704`](../../packages/core/tools/src/index.ts)
## `ctx.tui` — `TuiExtensionService` (abstract seam)
@@ -2340,7 +2423,7 @@ async ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>
Types: [AskUserQuestionAnswer](../core-data-structures/user-interaction.md) · [AskUserQuestionRequest](../core-data-structures/user-interaction.md) · [UserInteractionProvider](../core-data-structures/user-interaction.md)
Source: [`packages/ui/user-interaction/src/index.ts:50`](../../packages/ui/user-interaction/src/index.ts)
Source: [`packages/ui/user-interaction/src/index.ts:51`](../../packages/ui/user-interaction/src/index.ts)
## `ctx.web` — `WebService`

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/core.md
core.md: bfec31f36e9f620e35e99b4fa1cf0574a732dc76
core.zh.md: d09f60184c6765d14c23e68e18d11a75ca5bac5f
core.md: 5ed6a47c5488005d41fdac9349e4c9d1c550d13d
core.zh.md: 1b16b7ec994c6fccd6fedf1508dec6b1b057edf3

View File

@@ -25,6 +25,7 @@ Everything else is documented on a **sub-page**, not here. The rule that draws t
| [session.md](session.md) | the full `SessionEventMap` variant catalog, `TurnTrigger`/`TurnEndReason`, `deriveMessages()`, execution enclosure, and standalone events |
| [persistence.md](persistence.md) | the durability seam: `SessionPersistence`, JSONL + SQLite backends, `session/flush`, crash recovery, `SessionHeader` |
| [settings.md](settings.md) | the user-settings seam: `SettingsNamespace` registration, layered resolution (defaults → composition `base` → user document), owner scopes, hot commits |
| [credentials.md](credentials.md) | the credential seam: `CredentialRef` references (never values) in configuration, per-operation resolution, UI-safe `CredentialInfo`, provider source layers |
| [session-query.md](session-query.md) | logical records, bounded exact-event reads, relationship traces, semantic filters/documents, and full-text result pages |
| [session-title.md](session-title.md) | durable title snapshots, source provenance, and the asynchronous provider contract |
| [system-prompt.md](system-prompt.md) | per-assembly context, tool-provider results, prompt sections, and cooperative assembly |
@@ -182,6 +183,34 @@ Source: [`packages/llm/llm/src/types.ts`](../../packages/llm/llm/src/types.ts)
Provider and model discovery uses small provider-neutral descriptors. A model catalog is advisory: routing still keys on a registered provider, and an adapter may accept unlisted model ids.
Registering an adapter returns a handle: the disposer, plus the atomic route replacement a plugin whose route set is user-configurable needs.
```ts type-equiv
/**
* What {@link LlmService.registerAdapter} returns: the disposer, plus an
* atomic route replacement for the same adapter instance.
*/
interface AdapterRegistrationHandle {
/** Release every route this registration currently holds. */
(): void
/**
* Replace this registration's routes with `providers`, keeping the same
* adapter instance. The candidate set is validated in full first — a
* conflict with another adapter, an invalid name, or bad provider metadata
* throws and leaves the current routes untouched — and the swap itself is
* one synchronous section, so no request can observe a gap. An empty array
* is legal here (a settings section that emptied holds zero routes while
* staying registered), unlike an empty initial registration.
*
* Throws `LlmError` with code `REGISTRATION_DISPOSED` once the registration
* has been released: its routes are gone and its disposer has already run,
* so anything registered afterwards would have no owner left to release it.
* @param providers - the complete next route set for this registration.
*/
replace(providers: string[]): void
}
```
```ts type-equiv
/** Display metadata for one registered provider route. */
interface LlmProviderInfo {
@@ -192,6 +221,30 @@ interface LlmProviderInfo {
}
```
Adapter plugins additionally declare which routes *could* run through `registerConfigurableProviders()`, addressing each one's user-settings section, so configuration surfaces can offer dormant providers before any route registers.
```ts type-equiv
/**
* One provider route an adapter plugin can activate through configuration,
* whether or not the route is currently registered. Configuration surfaces
* merge this directory with `listProviders()` to offer every configurable
* provider alongside its live/dormant state.
*/
interface LlmConfigurableProvider {
/** Provider route key this entry activates when configured. */
provider: string
/** Human-readable provider name for configuration surfaces. */
displayName: string
/** User-settings namespace whose section configures this provider. */
settingsNs: string
/**
* Path from that namespace's section root to this provider's profile
* object; empty when the whole section is the profile.
*/
settingsPath: readonly string[]
}
```
```ts type-equiv
/** One adapter-discovered model; catalog membership is advisory, not request validation. */
interface LlmModelInfo {

View File

@@ -25,6 +25,7 @@ harness 是一个微内核:一个极小的核心加上众多插件。大多数
| [session.md](session.md) | 完整的 `SessionEventMap` 变体目录、`TurnTrigger`/`TurnEndReason``deriveMessages()`、执行封闭与独立事件 |
| [persistence.md](persistence.md) | 持久性 seam`SessionPersistence`、JSONL + SQLite 后端、`session/flush`、崩溃恢复、`SessionHeader` |
| [settings.md](settings.md) | 用户设置 seam`SettingsNamespace` 注册、分层解析(默认值 → 组合 `base` → 用户文档、owner scope、热提交 |
| [credentials.md](credentials.md) | 凭据 seam配置中的 `CredentialRef` 引用(绝不含值)、按操作解析、对 UI 安全的 `CredentialInfo`、provider 来源层 |
| [session-query.md](session-query.md) | 逻辑记录、有界精确事件读取、关系追踪、语义筛选器/文档与全文检索结果页 |
| [session-title.md](session-title.md) | 持久标题快照、来源 provenance 与异步提供方契约 |
| [system-prompt.md](system-prompt.md) | 逐次组装的上下文、工具提供方结果、提示词段落与协作式组装 |
@@ -188,6 +189,34 @@ interface MessageSourceMap {
提供方与模型发现使用小型、提供方无关的描述符。模型目录仅供参考:路由仍以已注册提供方为键,适配器也可以接受未列出的模型 id。
注册适配器会返回一个句柄:既是释放器,也带有原子的路由替换——路由集合由用户配置决定的插件正需要它。
```ts type-equiv
/**
* What {@link LlmService.registerAdapter} returns: the disposer, plus an
* atomic route replacement for the same adapter instance.
*/
interface AdapterRegistrationHandle {
/** Release every route this registration currently holds. */
(): void
/**
* Replace this registration's routes with `providers`, keeping the same
* adapter instance. The candidate set is validated in full first — a
* conflict with another adapter, an invalid name, or bad provider metadata
* throws and leaves the current routes untouched — and the swap itself is
* one synchronous section, so no request can observe a gap. An empty array
* is legal here (a settings section that emptied holds zero routes while
* staying registered), unlike an empty initial registration.
*
* Throws `LlmError` with code `REGISTRATION_DISPOSED` once the registration
* has been released: its routes are gone and its disposer has already run,
* so anything registered afterwards would have no owner left to release it.
* @param providers - the complete next route set for this registration.
*/
replace(providers: string[]): void
}
```
```ts type-equiv
/** Display metadata for one registered provider route. */
interface LlmProviderInfo {
@@ -198,6 +227,30 @@ interface LlmProviderInfo {
}
```
适配器插件还会通过 `registerConfigurableProviders()` 声明哪些路由*可以*运行,并指明每条路由的用户设置分节,使配置界面能在任何路由注册之前就呈现休眠的提供方。
```ts type-equiv
/**
* One provider route an adapter plugin can activate through configuration,
* whether or not the route is currently registered. Configuration surfaces
* merge this directory with `listProviders()` to offer every configurable
* provider alongside its live/dormant state.
*/
interface LlmConfigurableProvider {
/** Provider route key this entry activates when configured. */
provider: string
/** Human-readable provider name for configuration surfaces. */
displayName: string
/** User-settings namespace whose section configures this provider. */
settingsNs: string
/**
* Path from that namespace's section root to this provider's profile
* object; empty when the whole section is the profile.
*/
settingsPath: readonly string[]
}
```
```ts type-equiv
/** One adapter-discovered model; catalog membership is advisory, not request validation. */
interface LlmModelInfo {

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/credentials.md
credentials.md: 3f6fcd127d01e2c49e17c70c002bebe9f363e951
credentials.zh.md: b5d2d9e164a85ce090790635c438b768cae4c9ca

View File

@@ -0,0 +1,50 @@
# User Credentials
English | [中文](credentials.zh.md)
The credential seam of [dsh-credentials](../../packages/credentials/credentials) keeps secrets out of configuration: settings sections and `cordis.yml` entries carry *references* (environment-variable names), providers such as [dsh-credentials-local](../../packages/credentials/credentials-local) own the values, and consumers resolve a reference once per operation — the LLM adapters resolve once per model request, so a rotated credential reaches the very next request without any restart. One seam-wide rule binds every provider: an empty stored value is absent everywhere.
Source: [`packages/credentials/credentials/src/index.ts`](../../packages/credentials/credentials/src/index.ts)
## Identity
A reference names one credential as a POSIX-style environment-variable name. The brand keeps references from mixing with other cross-boundary strings; construction validates the shell-identifier shape.
```ts type-equiv
/** Nominal reference to one credential: a POSIX-style environment-variable name. */
type CredentialRef = Branded<'CredentialRef'>
```
## Resolution
`resolve(ref)` returns the value with the provider-defined source layer that supplied it, or `undefined` while unconfigured. Consumers re-resolve at each operation and never cache across operations — that per-operation read is the hot-update mechanism.
```ts type-equiv
/** One resolved credential value and the source layer that supplied it. */
interface ResolvedCredential {
/** The non-empty secret value. */
value: string
/** Provider-defined source layer id (the local provider uses `env` and `file`). */
source: string
}
```
## Description
`describe(ref)` answers configuration surfaces without ever exposing a value: whether the reference resolves, from which layer, and whether `set` would currently succeed. The local provider reports a reference supplied by the live process environment as `writable: false` — a write would appear to succeed while resolution kept returning the shadowing value, so the seam rejects it and the UI can render the reference read-only up front.
```ts type-equiv
/** Source and writability facts for one reference, safe for configuration UIs — never the value. */
interface CredentialInfo {
/** Whether {@link Credentials.resolve} would currently return a value. */
configured: boolean
/** Source layer currently supplying the value; absent while unconfigured. */
source?: string
/** Whether {@link Credentials.set} would currently succeed for this reference. */
writable: boolean
}
```
## Change commits
`credentials/updated (ref)` fires after a committed change to a provider-managed source — a `set`, an `unset`, or an external edit observed in storage. Ambient process-environment changes are not observable and never emit. Consumers do not need the event (they re-resolve per operation); it exists for configuration surfaces refreshing a "configured" badge.

View File

@@ -0,0 +1,50 @@
# 用户凭据
[English](credentials.md) | 中文
[dsh-credentials](../../packages/credentials/credentials) 的凭据 seam 把机密挡在配置之外settings 分节与 `cordis.yml` 条目携带的是*引用*(环境变量名),值归 [dsh-credentials-local](../../packages/credentials/credentials-local) 这类 provider 所有消费方每个操作解析一次引用——LLM 适配器每次模型请求解析一次,因此轮换后的凭据无需任何重启即可作用于紧随其后的下一次请求。一条 seam 级规则约束每个 provider空的存储值在任何地方都视为不存在。
Source: [`packages/credentials/credentials/src/index.ts`](../../packages/credentials/credentials/src/index.ts)
## 标识
引用以 POSIX 风格环境变量名命名一条凭据。brand 使引用不与其他跨边界字符串混用;构造时校验 shell 标识符形态。
```ts type-equiv
/** Nominal reference to one credential: a POSIX-style environment-variable name. */
type CredentialRef = Branded<'CredentialRef'>
```
## 解析
`resolve(ref)` 返回值,连同供出该值、由 provider 定义的来源层;未配置期间返回 `undefined`。消费方在每个操作中重新解析,绝不跨操作缓存——这次按操作进行的读取正是热更新机制。
```ts type-equiv
/** One resolved credential value and the source layer that supplied it. */
interface ResolvedCredential {
/** The non-empty secret value. */
value: string
/** Provider-defined source layer id (the local provider uses `env` and `file`). */
source: string
}
```
## 描述
`describe(ref)` 在绝不暴露值的前提下回应配置界面:引用当前是否可解析、来自哪一层、`set` 当前能否成功。本地 provider 把由活跃进程环境供值的引用报告为 `writable: false`——那样的写入会表面成功而解析持续返回遮蔽值,因此 seam 直接拒绝,界面也得以提前把该引用渲染为只读。
```ts type-equiv
/** Source and writability facts for one reference, safe for configuration UIs — never the value. */
interface CredentialInfo {
/** Whether {@link Credentials.resolve} would currently return a value. */
configured: boolean
/** Source layer currently supplying the value; absent while unconfigured. */
source?: string
/** Whether {@link Credentials.set} would currently succeed for this reference. */
writable: boolean
}
```
## 变更提交
`credentials/updated (ref)` 在 provider 管理的来源发生已提交变更后触发——`set`、`unset` 或在存储中观察到的外部编辑。进程环境自身的变化不可观测,永不发出事件。消费方不需要该事件(它们按操作重新解析);它服务于配置界面刷新「已配置」徽标。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/settings.md
settings.md: 381b36b3ff2f45a2090a2a2eac0f700bd00270c4
settings.zh.md: bc6547db3b05c5a78f112462ae205d848f93da60
settings.md: 1cabfae5d8dc72a9cd79341d250ee79820693872
settings.zh.md: d63a1384646fa38199e7d65e9f0504f0440597be

View File

@@ -73,7 +73,7 @@ interface SettingsScope<T> {
## Descriptors
`describe()` serializes every registered namespace for configuration surfaces: the schemastery `toJSON()` envelope drives schema-rendered forms, and the resolved value fills them.
`describe()` serializes every registered namespace for configuration surfaces: the schemastery `toJSON()` envelope drives schema-rendered forms, the resolved value fills them, and the detached `base`/`user` layers let a form mark user-overridden fields by presence. `describe({ redactSecrets: true })` — mandatory on every wire surface — strips `role('secret')` fields from all three layers and enumerates their `{path, set}` slots so a page can render write-only inputs without ever receiving a secret.
```ts type-equiv
/** One registered namespace as surfaced to configuration UIs. */
@@ -84,8 +84,49 @@ interface SettingsDescriptor {
schema: unknown
/** Current resolved value. */
value: unknown
/**
* Monotonic revision of the raw user section this descriptor was read at.
* Send it back as `expectedRevision` on a write to refuse a stale one.
*/
revision: number
/** Registrant's composition `base` layer (detached), when one was declared. */
base?: unknown
/**
* Raw user section from the stored document (detached), when one exists and
* is well-formed; a field's presence here is what marks it user-overridden.
*/
user?: unknown
/** Owner's declared effect timing. */
applies: SettingsApplies
/** Schema-declared secret positions; present only under `redactSecrets`. */
secrets?: RedactedSecret[]
}
```
A caller that holds only the redacted descriptor cannot safely rebuild a section, so removals travel as path ops instead. Each descriptor also carries a `revision` over the raw section; a write may send it back as `expectedRevision`, and one that no longer matches is refused rather than applied over the writer that landed first.
```ts type-equiv
/**
* One path-addressed edit to a namespace's user section. Path mutation exists
* for a caller holding an INCOMPLETE view of the section — a configuration UI
* reads the redacted descriptor, which by construction never received the
* `role('secret')` fields. Such a caller can name the field it means without
* restating the section: a wholesale `replace` rebuilt from a redacted
* document silently deletes every secret the wire never returned.
*/
type SettingsPathOp =
| { op: 'set'; path: readonly string[]; value: unknown }
| { op: 'unset'; path: readonly string[] }
```
```ts type-equiv
/** Options for {@link Settings.describe}. */
interface SettingsDescribeOptions {
/**
* Strip `role('secret')` fields from `value`/`base`/`user` and enumerate
* them in each descriptor's `secrets`. Every wire surface MUST pass this;
* the verbatim default exists for same-process configuration UIs only.
*/
redactSecrets?: boolean
}
```

View File

@@ -73,7 +73,7 @@ interface SettingsScope<T> {
## 描述符
`describe()` 为配置界面序列化每个已注册 namespaceschemastery 的 `toJSON()` 信封驱动 schema 渲染的表单,解析值填充表单。
`describe()` 为配置界面序列化每个已注册 namespaceschemastery 的 `toJSON()` 信封驱动 schema 渲染的表单,解析值填充表单,分离出的 `base`/`user` 层让表单按字段是否出现在 user 层标注「用户已覆盖」。`describe({ redactSecrets: true })`——每个 wire 面都必须传入——从三层剥离 `role('secret')` 字段并枚举其 `{path, set}` 槽位,页面因此能渲染只写输入框而永远收不到机密值
```ts type-equiv
/** One registered namespace as surfaced to configuration UIs. */
@@ -84,8 +84,49 @@ interface SettingsDescriptor {
schema: unknown
/** Current resolved value. */
value: unknown
/**
* Monotonic revision of the raw user section this descriptor was read at.
* Send it back as `expectedRevision` on a write to refuse a stale one.
*/
revision: number
/** Registrant's composition `base` layer (detached), when one was declared. */
base?: unknown
/**
* Raw user section from the stored document (detached), when one exists and
* is well-formed; a field's presence here is what marks it user-overridden.
*/
user?: unknown
/** Owner's declared effect timing. */
applies: SettingsApplies
/** Schema-declared secret positions; present only under `redactSecrets`. */
secrets?: RedactedSecret[]
}
```
只持有脱敏 descriptor 的调用方无法安全地重建分节,因此删除改以路径 op 传递。每个 descriptor 还携带针对原始分节的 `revision`;写入可以把它作为 `expectedRevision` 送回,不再匹配的写入会被拒绝,而不是覆盖在先落地的那个写方之上。
```ts type-equiv
/**
* One path-addressed edit to a namespace's user section. Path mutation exists
* for a caller holding an INCOMPLETE view of the section — a configuration UI
* reads the redacted descriptor, which by construction never received the
* `role('secret')` fields. Such a caller can name the field it means without
* restating the section: a wholesale `replace` rebuilt from a redacted
* document silently deletes every secret the wire never returned.
*/
type SettingsPathOp =
| { op: 'set'; path: readonly string[]; value: unknown }
| { op: 'unset'; path: readonly string[] }
```
```ts type-equiv
/** Options for {@link Settings.describe}. */
interface SettingsDescribeOptions {
/**
* Strip `role('secret')` fields from `value`/`base`/`user` and enumerate
* them in each descriptor's `secrets`. Every wire surface MUST pass this;
* the verbatim default exists for same-process configuration UIs only.
*/
redactSecrets?: boolean
}
```

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/tools.md
tools.md: dad7f7421caa94940801407fd4ef7fd936eb05c9
tools.zh.md: 8386e5870e665e90ee0dbada8cb98084281001a7
tools.md: 3c94f1093001e8c65365cc5baa50c1393d53b3ee
tools.zh.md: 7a6aad81c4cfe83be8625411e4313d0c36018821

View File

@@ -447,7 +447,7 @@ type ObjectJsonSchema = JsonSchemaNode & { type: 'object' }
How a tool wants its call shown in a UI (an editor tool-call card, a CLI log line), provider-neutral so a tool describes itself without depending on any client protocol. `presentCall`/`presentResult` return a **`card`-tagged render intent** — a discriminated union a UI bridge switches on:
- `ToolCallView` (pending): `{ card: 'generic', title, kind?, rawInput?, content?, locations? }` (the default card; `locations` is `{ path, line? }[]` files the call reads/modifies, for editor follow-along), `{ card: 'terminal', title, description?, cwd? }` (a shell command → a terminal card), or `{ card: 'diff', title, diffs, locations? }` (a file create/modify → an inline diff card; `diffs` is `{ path, oldText, newText }[]`, `oldText: null` for a new file).
- `ToolResultView` (completed): `{ card: 'generic', title?, content? }`, `{ card: 'terminal', title?, output?, exitCode?, signal? }` (the captured run output + exit; a capable UI shows an exit-status pill, while another may derive a fenced ` ```console ` fallback), or `{ card: 'diff', title?, diffs }` (a completed file mutation → the change to show, typically the applied hunks with context lines computed from the before/after content, or a whole-file diff when there is no before-image). Completed views replace pending views, so mutation tools return a diff result even when it duplicates the call-time snippet.
- `ToolResultView` (completed): `{ card: 'generic', title?, content? }`, `{ card: 'terminal', title?, output?, exitCode?, signal? }` (the captured run output + exit; a capable UI shows an exit-status pill, while another may derive a fenced ` ```console ` fallback), `{ card: 'diff', title?, diffs }` (a completed file mutation → the change to show, typically the applied hunks with context lines computed from the before/after content, or a whole-file diff when there is no before-image), or `{ card: 'web', kind: 'search' | 'fetch', title?, … }` (a completed web retrieval; `kind: 'search'` carries the structured `sources`/`answer?`/`truncated`, `kind: 'fetch'` carries `url`/`statusCode`/`truncated`, and a UI without the `web` capability falls back to the raw result content — the body is not duplicated into the view). Completed views replace pending views, so mutation tools return a diff result even when it duplicates the call-time snippet.
`ToolCallKind` (`'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'fetch' | 'other'`) picks an icon on a generic card. `FileLocation` (`{ path, line? }`) and `FileDiff` (`{ path, oldText, newText }`) are the shared file-card vocabulary. The design is pinned in [the render-intent-union Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md); the TUI and host/client runtime project this neutral vocabulary into their own views.

View File

@@ -447,7 +447,7 @@ type ObjectJsonSchema = JsonSchemaNode & { type: 'object' }
工具希望其调用在 UI 中如何呈现编辑器工具调用卡片、CLI命令行界面日志行提供方无关使工具在不依赖任何客户端协议的情况下描述自身。`presentCall`/`presentResult` 返回一个 **`card` 标签的渲染意图**——一个可辨识联合类型UI 桥接层据此分发:
- `ToolCallView`(待执行):`{ card: 'generic', title, kind?, rawInput?, content?, locations? }`(默认卡片;`locations` 是 `{ path, line? }[]`,表示调用读取/修改的文件,供编辑器跟随)、`{ card: 'terminal', title, description?, cwd? }`shell 命令→终端卡片)、或 `{ card: 'diff', title, diffs, locations? }`(文件创建/修改→行内 diff 卡片;`diffs` 是 `{ path, oldText, newText }[]`,新文件时 `oldText: null`)。
- `ToolResultView`(已完成):`{ card: 'generic', title?, content? }`、`{ card: 'terminal', title?, output?, exitCode?, signal? }`(捕获的运行输出 + 退出状态;有能力的 UI 显示退出状态标签,其他 UI 可以派生围栏 ` ```console ` 回退)、`{ card: 'diff', title?, diffs }`(已完成的文件变更→要展示的变更,通常是从变更前后内容计算出带上下文行的已应用 hunk或在没有前像时的整文件 diff。已完成视图会替换待执行视图因此变更工具即使与调用时的片段重复也要返回 diff 结果。
- `ToolResultView`(已完成):`{ card: 'generic', title?, content? }`、`{ card: 'terminal', title?, output?, exitCode?, signal? }`(捕获的运行输出 + 退出状态;有能力的 UI 显示退出状态标签,其他 UI 可以派生围栏 ` ```console ` 回退)、`{ card: 'diff', title?, diffs }`(已完成的文件变更→要展示的变更,通常是从变更前后内容计算出带上下文行的已应用 hunk或在没有前像时的整文件 diff、或 `{ card: 'web', kind: 'search' | 'fetch', title?, … }`(已完成的 web 检索;`kind: 'search'` 携带结构化的 `sources`/`answer?`/`truncated``kind: 'fetch'` 携带 `url`/`statusCode`/`truncated`,不具备 `web` 能力的 UI 回退到原始结果内容——正文不会重复进视图)。已完成视图会替换待执行视图,因此变更工具即使与调用时的片段重复也要返回 diff 结果。
`ToolCallKind``'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'fetch' | 'other'`)用于为通用卡片选择图标。`FileLocation``{ path, line? }`)与 `FileDiff``{ path, oldText, newText }`)是共享的文件卡片词汇。该设计由[渲染意图联合类型 Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md)固定TUI 和 host/client 运行时将这套中性词汇投影为各自的视图。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
user-interaction.md: 798a9790f424683775284a98421be08e6e1399e3
user-interaction.zh.md: 12bfcffe4fe4caaacb54e90126eac55e333d64a5
# pnpm run verify-translation-pairing --write docs/core-data-structures/user-interaction.md
user-interaction.md: 2483382eee07d379b13d456149096c8afab70e4b
user-interaction.zh.md: 57f57277975597fff712705cc741ae8e78643642

View File

@@ -20,6 +20,30 @@ interface AskUserQuestionOption {
}
```
## Presentation intent
`AskUserQuestionIntent` is the optional declaration that a question IS a decision of a known shape. It is tagged on `kind` so intents can be added; a UI that does not recognise a tag renders the generic option list. An intent shapes presentation only — a UI honouring it answers with the same option labels a generic UI would send, so the caller reads one answer shape either way. `approve` names the affirmative option instead of relying on option order. `ask()` rejects the two assertions no type can carry: an `approve` naming none of its own question's options, and an intent on a question with no `detail`.
```ts type-equiv
/**
* A caller-declared presentation intent: the question IS a decision of this
* shape, so a UI that recognises the tag may present it as such instead of as a
* generic option list. Tagged so further intents can be added; a UI that does
* not know a tag renders the generic flow, and the answer encoding is identical
* either way — an intent shapes presentation only, never the protocol.
*/
type AskUserQuestionIntent = {
/** A plan submitted for review: `detail` is the plan markdown `ask()` requires, and the decision approves or declines it. */
kind: 'plan-review'
/**
* The option label that approves the plan; every other option declines it.
* Named rather than positional so no UI infers the verdict from option order.
* An `approve` naming no option of its own question is rejected at `ask()`.
*/
approve: string
}
```
## Question item
`AskUserQuestionItem` is one question in a request. The caller supplies a stable `id`, which is echoed back with the answer so batched questions remain routable. Optional `detail` carries supporting text that providers render with the question but keep out of selectable option labels.
@@ -39,6 +63,8 @@ interface AskUserQuestionItem {
options?: AskUserQuestionOption[]
/** Whether more than one option may be selected. Defaults to single-select. */
multiSelect?: boolean
/** Optional presentation intent for capable UIs; absent asks for the generic option list. */
intent?: AskUserQuestionIntent
}
```

View File

@@ -20,6 +20,30 @@ interface AskUserQuestionOption {
}
```
## 呈现意图
`AskUserQuestionIntent` 是一项可选声明:某个问题本身就是一次已知形状的决定。它按 `kind` 打标签,因此意图可以扩充;不认识某个标签的 UI 渲染通用选项列表。意图只塑造呈现 —— 遵循它的 UI 回答的仍是通用 UI 会发送的那些 option label因此调用方两种情况下读到的都是同一种回答形态。`approve` 指名肯定选项,而不依赖选项顺序。有两项断言是任何类型都承载不了的,`ask()` 会拒绝它们:`approve` 未命中该问题自身的任一选项,以及意图落在没有 `detail` 的问题上。
```ts type-equiv
/**
* A caller-declared presentation intent: the question IS a decision of this
* shape, so a UI that recognises the tag may present it as such instead of as a
* generic option list. Tagged so further intents can be added; a UI that does
* not know a tag renders the generic flow, and the answer encoding is identical
* either way — an intent shapes presentation only, never the protocol.
*/
type AskUserQuestionIntent = {
/** A plan submitted for review: `detail` is the plan markdown `ask()` requires, and the decision approves or declines it. */
kind: 'plan-review'
/**
* The option label that approves the plan; every other option declines it.
* Named rather than positional so no UI infers the verdict from option order.
* An `approve` naming no option of its own question is rejected at `ask()`.
*/
approve: string
}
```
## 问题条目
`AskUserQuestionItem` 是请求中的一个问题。调用方提供稳定的 `id`,它会随答案原样返回,使批量问题仍可路由。可选的 `detail` 携带辅助文本;提供方会将其随问题渲染,但不会放入可选 option label。
@@ -39,6 +63,8 @@ interface AskUserQuestionItem {
options?: AskUserQuestionOption[]
/** Whether more than one option may be selected. Defaults to single-select. */
multiSelect?: boolean
/** Optional presentation intent for capable UIs; absent asks for the generic option list. */
intent?: AskUserQuestionIntent
}
```

View File

@@ -26,17 +26,20 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/types.ts:406`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `approval/request` | `waterfall` | [`packages/ui/user-approval/src/index.ts:30`](../packages/ui/user-approval/src/index.ts) | [`user-approval`](../packages/ui/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `apiproxy` |
| `commands/change` | `emit` | [`packages/ui/commands/src/index.ts:154`](../packages/ui/commands/src/index.ts) | [`commands`](../packages/ui/commands) (`events.dispatch`) | `apiproxy`, [`tui`](../packages/ui/tui) |
| `credentials/updated` | `emit` | [`packages/credentials/credentials/src/index.ts:67`](../packages/credentials/credentials/src/index.ts) | [`credentials`](../packages/credentials/credentials) (`events.dispatch`) | `apiproxy`, [`credentials`](../packages/credentials/credentials) |
| `domain/changed` | `emit` | [`packages/storage/storage-domain/src/events.ts:46`](../packages/storage/storage-domain/src/events.ts) | [`storage-domain`](../packages/storage/storage-domain) (`emit`) | `apiproxy`, [`storage-domain`](../packages/storage/storage-domain), [`workspace`](../packages/workspace/workspace) |
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:62`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`), [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
| `fs/observed` | `emit` | [`packages/fs/fs/src/index.ts:71`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`emit`), [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) (`emit`) | [`fs-policy`](../packages/fs/fs-policy), [`skill-local`](../packages/skill/skill-local) |
| `fs/write-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:54`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`), [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
| `goal/changed` | `emit` | [`packages/goal/goal/src/domain.ts:135`](../packages/goal/goal/src/domain.ts) | [`goal`](../packages/goal/goal) (`emit`) | [`goal-session`](../packages/goal/goal-session) |
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:58`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`llm-replay`](../packages/support/llm-replay), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-title`](../packages/session-title/session-title) |
| `llm/adapters-updated` | `emit` | [`packages/llm/llm/src/index.ts:70`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`events.dispatch`) | `apiproxy`, [`llm`](../packages/llm/llm) |
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:59`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`llm-replay`](../packages/support/llm-replay), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-title`](../packages/session-title/session-title) |
| `session/created` | `emit` | [`packages/core/session/src/index.ts:71`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | `apiproxy`, [`compact`](../packages/compact/compact), [`goal`](../packages/goal/goal), [`hook-protocol`](../packages/hooks/hook-protocol), [`jsonrpc`](../packages/ui/jsonrpc), [`llm-retry`](../packages/llm/llm-retry), [`plan-mode`](../packages/plan/plan-mode), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-telemetry`](../packages/telemetry/session-telemetry), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| `session/disposed` | `emit` | [`packages/core/session/src/index.ts:81`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `apiproxy`, [`session-persistence`](../packages/session-persistence/session-persistence), [`session-projection-cache`](../packages/session-projection/session-projection-cache), [`session-telemetry`](../packages/telemetry/session-telemetry), [`session-title`](../packages/session-title/session-title) |
| `session/event` | `emit` | [`packages/core/session/src/index.ts:93`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/acp/acp), `apiproxy`, [`cli-demo`](../packages/examples/cli-demo), [`compact`](../packages/compact/compact), [`compact-basic`](../packages/compact/compact-basic), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hook-protocol`](../packages/hooks/hook-protocol), [`jsonrpc`](../packages/ui/jsonrpc), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-projection`](../packages/session-projection/session-projection), [`session-projection-cache`](../packages/session-projection/session-projection-cache), [`session-telemetry`](../packages/telemetry/session-telemetry), [`session-title`](../packages/session-title/session-title), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`tui`](../packages/ui/tui), [`user-approval`](../packages/ui/user-approval), [`workspace-context`](../packages/context/workspace-context) |
| `session/flush` | `parallel` | [`packages/core/session/src/index.ts:103`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`session-persistence`](../packages/session-persistence/session-persistence), [`session-telemetry`](../packages/telemetry/session-telemetry) |
| `settings/updated` | `emit` | [`packages/settings/settings/src/index.ts:108`](../packages/settings/settings/src/index.ts) | [`settings`](../packages/settings/settings) (`events.dispatch`) | [`settings`](../packages/settings/settings) |
| `settings/document-updated` | `emit` | [`packages/settings/settings/src/index.ts:150`](../packages/settings/settings/src/index.ts) | [`settings`](../packages/settings/settings) (`events.dispatch`) | `apiproxy` |
| `settings/updated` | `emit` | [`packages/settings/settings/src/index.ts:137`](../packages/settings/settings/src/index.ts) | [`settings`](../packages/settings/settings) (`events.dispatch`) | [`settings`](../packages/settings/settings) |
| `skills/change` | `emit` | [`packages/skill/skill/src/index.ts:188`](../packages/skill/skill/src/index.ts) | [`skill`](../packages/skill/skill) (`events.dispatch`) | [`tui`](../packages/ui/tui) |
| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:140`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`jsonrpc`](../packages/ui/jsonrpc), [`subagent`](../packages/subagent/subagent) |
| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:114`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`subagent`](../packages/subagent/subagent), [`tool-subagent`](../packages/subagent/tool-subagent) |
@@ -45,12 +48,12 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:29`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | [`agent`](../packages/core/agent), [`system-prompt`](../packages/core/system-prompt) |
| `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:35`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - |
| `telemetry/record` | `waterfall` | [`packages/telemetry/session-telemetry/src/index.ts:41`](../packages/telemetry/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/telemetry/session-telemetry) (`waterfall`) | - |
| `tools/change` | `emit` | [`packages/core/tools/src/index.ts:156`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`emit`) | - |
| `tools/code-dispatch-log` | `waterfall` | [`packages/core/tools/src/index.ts:138`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`spill-policy`](../packages/spill/spill-policy) |
| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:113`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`timeout-policy`](../packages/timeout/timeout-policy) |
| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:125`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search), [`workspace-context`](../packages/context/workspace-context) |
| `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:102`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`tool-tasks`](../packages/tasks/tool-tasks) |
| `tools/result` | `emit` | [`packages/core/tools/src/index.ts:146`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`workspace-context`](../packages/context/workspace-context) |
| `tools/change` | `emit` | [`packages/core/tools/src/index.ts:160`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`emit`) | - |
| `tools/code-dispatch-log` | `waterfall` | [`packages/core/tools/src/index.ts:142`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`spill-policy`](../packages/spill/spill-policy) |
| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:117`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`timeout-policy`](../packages/timeout/timeout-policy) |
| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:129`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search), [`workspace-context`](../packages/context/workspace-context) |
| `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:106`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`tool-tasks`](../packages/tasks/tool-tasks) |
| `tools/result` | `emit` | [`packages/core/tools/src/index.ts:150`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`workspace-context`](../packages/context/workspace-context) |
| `workflow/agent-end` | `emit` | [`packages/workflow/workflow/src/index.ts:81`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`workflow`](../packages/workflow/workflow) |
| `workflow/agent-start` | `emit` | [`packages/workflow/workflow/src/index.ts:70`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`workflow`](../packages/workflow/workflow) |
| `workflow/end` | `emit` | [`packages/workflow/workflow/src/index.ts:91`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`workflow`](../packages/workflow/workflow) |
@@ -63,11 +66,14 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| Event string | Dispatchers | Listeners |
| --- | --- | --- |
| `commands/changed` | `runtime` (`emit`) | `ui-command` |
| `connection/reset` | `runtime` (`emit`) | `ui-command` |
| `connection/reset` | `runtime` (`emit`) | `ui-command`, `ui-models` |
| `credentials/changed` | `runtime` (`emit`) | `ui-models` |
| `internal/dispatch` | - | [`commands`](../packages/ui/commands), [`compact`](../packages/compact/compact), [`fs`](../packages/fs/fs), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm-retry`](../packages/llm/llm-retry), [`permission`](../packages/ui/permission), [`plan-mode`](../packages/plan/plan-mode), [`pty-local`](../packages/pty/pty-local), `runtime`, [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`subagent`](../packages/subagent/subagent), [`time-context`](../packages/context/time-context), [`tool-todo`](../packages/todo/tool-todo), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval), [`workflow`](../packages/workflow/workflow) |
| `internal/plugin` | - | `hmr`, `loader`, `modules`, `webserver` |
| `internal/status` | - | [`agent`](../packages/core/agent) |
| `locale/change` | `locale` (`emit`) | `locale`, `ui-models`, `ui-settings-general` |
| `models/changed` | `runtime` (`emit`) | `ui-models` |
| `settings/changed` | `runtime` (`emit`) | `ui-models` |
| `slash/input-begin-command` | - | `ui-conversation` |
| `slash/input-consume-token` | - | `ui-conversation` |
| `slash/input-insert-reference` | - | `ui-conversation` |

View File

@@ -96,6 +96,7 @@
| Cookbook | 实操手册 | | | 文档标题用语 |
| context | 上下文 | | | |
| counterpart | 对侧文件 | | 对应物、配对物 | 双语配对语境;泛指"另一侧"时可写「另一侧」 |
| configurable-provider directory | 可配置提供方目录 | | | llm seam 中 `registerConfigurableProviders()` 维护的目录;沿用 Service Catalog →「服务目录」先例 |
| context compaction | 上下文压缩 | 上下文压缩context compaction | | |
| contract | 契约 | | | 如:`pairing contract``配对契约` |
| Cordis config entry | Cordis 配置项 | | | 指 `cordis.yml` 插件列表中的一项;插件实现本身写`Cordis 插件` |
@@ -103,6 +104,7 @@
| coverage | 覆盖率 | | | |
| crash recovery | 崩溃恢复 | | | |
| deploy root | 部署根目录 | | | |
| dormant | 休眠 | | 睡眠、蛰伏 | 指已声明可配置但当前未注册路由的提供方 |
| durability | 持久性 | | | |
| feature requirement | 功能依赖 | | | 功能或功能选项通过 `requires` 声明的关系 |
| ergonomics | 易用性 / 开发体验 | | 人体工学 | API 或面向模型的接口用「易用性」;工具链或开发者工作流用「开发体验」 |
@@ -119,6 +121,7 @@
| fenced code block | 围栏代码块 | | | 沿用 MDN 中文翻译 |
| fingerprint | 指纹 | | | 通用内容指纹;双语配对机制使用 sidecar record 记录两侧 blob hash |
| finish reason | 结束原因 | | | |
| fold | 折叠区 | | | 配置界面语境默认收起的字段分区collapsed →「收起」)|
| foreground run | 前台运行 | | | |
| freshness | 新鲜度 | | | 沿用 MDN 中文翻译;在本项目中指译文相对源文的同步状态 |
| hook | 钩子 | | | |
@@ -166,6 +169,7 @@
| serving surface | 对外服务接口 | | | |
| session | 会话 | | | |
| session event | 会话事件 | | | |
| setup card | 设置卡片 | | | 首次运行时代替行卡直接展开的配置卡 |
| sidecar record | 伴随记录 | | 旁挂记录 | 指与文档同目录的伴随记录文件 |
| smoke test | 冒烟测试 | | | |
| snapshot | 快照 | | | |

View File

@@ -8,6 +8,7 @@ Inter-package dependencies among the `@deepseek-ai/dsh-*` harness packages, deri
```mermaid
flowchart TD
subgraph group_util["packages/util"]
pkg_atomic_write["atomic-write"]
pkg_brand["brand"]
pkg_native_command["native-command"]
pkg_paths["paths"]
@@ -143,6 +144,7 @@ flowchart TD
pkg_client_locale["client-locale"]
pkg_client_modules["client-modules"]
pkg_client_runtime["client-runtime"]
pkg_client_schema_form["client-schema-form"]
pkg_client_test_runtime["client-test-runtime"]
pkg_client_ui_command["client-ui-command"]
pkg_client_ui_conversation["client-ui-conversation"]
@@ -177,6 +179,10 @@ flowchart TD
pkg_tmux_context["tmux-context"]
pkg_workspace_context["workspace-context"]
end
subgraph group_credentials["packages/credentials"]
pkg_credentials["credentials"]
pkg_credentials_local["credentials-local"]
end
subgraph group_examples["packages/examples"]
pkg_acp_demo["acp-demo"]
pkg_agent_spine_demo["agent-spine-demo"]
@@ -261,6 +267,7 @@ flowchart TD
subgraph group_workspace["packages/workspace"]
pkg_workspace["workspace"]
end
pkg_atomic_write --> pkg_invariants
pkg_brand --> pkg_invariants
pkg_native_command --> pkg_invariants
pkg_paths --> pkg_invariants
@@ -273,6 +280,7 @@ flowchart TD
pkg_loader_smoke --> pkg_invariants
pkg_client_modules --> pkg_invariants
pkg_client_runtime --> pkg_invariants
pkg_client_schema_form --> pkg_invariants
pkg_client_ui_primitives --> pkg_invariants
pkg_client_ui_slots --> pkg_invariants
pkg_client_web --> pkg_invariants
@@ -302,9 +310,6 @@ flowchart TD
pkg_client_test_runtime --> pkg_client_ui_slots
pkg_client_test_runtime --> pkg_client_web_react
pkg_client_test_runtime --> pkg_invariants
pkg_client_ui_models --> pkg_client_runtime
pkg_client_ui_models --> pkg_client_ui_slots
pkg_client_ui_models --> pkg_invariants
pkg_client_ui_settings --> pkg_client_runtime
pkg_client_ui_settings --> pkg_client_ui_primitives
pkg_client_ui_settings --> pkg_client_ui_slots
@@ -315,6 +320,8 @@ flowchart TD
pkg_client_ui_workspace --> pkg_client_ui_primitives
pkg_client_ui_workspace --> pkg_client_ui_slots
pkg_client_ui_workspace --> pkg_invariants
pkg_credentials --> pkg_brand
pkg_credentials --> pkg_invariants
pkg_helper --> pkg_brand
pkg_helper --> pkg_invariants
pkg_helper --> pkg_subprocess
@@ -333,11 +340,15 @@ flowchart TD
pkg_subprocess_local --> pkg_subprocess
pkg_typert_loader --> pkg_invariants
pkg_typert_loader --> pkg_typert_registry
pkg_llm_deepseek --> pkg_credentials
pkg_llm_deepseek --> pkg_invariants
pkg_llm_deepseek --> pkg_llm
pkg_llm_deepseek --> pkg_settings
pkg_llm_deepseek --> pkg_timeout
pkg_llm_pi_ai --> pkg_credentials
pkg_llm_pi_ai --> pkg_invariants
pkg_llm_pi_ai --> pkg_llm
pkg_llm_pi_ai --> pkg_settings
pkg_llm_pi_ai --> pkg_timeout
pkg_session --> pkg_brand
pkg_session --> pkg_invariants
@@ -348,6 +359,13 @@ flowchart TD
pkg_system_prompt --> pkg_scope
pkg_web --> pkg_invariants
pkg_web --> pkg_llm
pkg_client_ui_models --> pkg_client_connection
pkg_client_ui_models --> pkg_client_runtime
pkg_client_ui_models --> pkg_client_schema_form
pkg_client_ui_models --> pkg_client_ui_primitives
pkg_client_ui_models --> pkg_client_ui_slots
pkg_client_ui_models --> pkg_client_web_react
pkg_client_ui_models --> pkg_invariants
pkg_client_ui_question --> pkg_client_locale
pkg_client_ui_question --> pkg_invariants
pkg_client_ui_settings_general --> pkg_client_locale
@@ -371,6 +389,10 @@ flowchart TD
pkg_client_ui_theme --> pkg_client_ui_primitives
pkg_client_ui_theme --> pkg_client_ui_slots
pkg_client_ui_theme --> pkg_invariants
pkg_credentials_local --> pkg_atomic_write
pkg_credentials_local --> pkg_credentials
pkg_credentials_local --> pkg_invariants
pkg_credentials_local --> pkg_paths
pkg_host_directory_picker_browse --> pkg_client_locale
pkg_host_directory_picker_browse --> pkg_client_runtime
pkg_host_directory_picker_browse --> pkg_client_ui_primitives
@@ -386,6 +408,7 @@ flowchart TD
pkg_lsp --> pkg_llm
pkg_sandbox --> pkg_invariants
pkg_sandbox --> pkg_llm
pkg_settings_local --> pkg_atomic_write
pkg_settings_local --> pkg_invariants
pkg_settings_local --> pkg_paths
pkg_settings_local --> pkg_settings
@@ -999,6 +1022,7 @@ flowchart TD
| Package | Group | Depends on |
| --- | --- | --- |
| [`invariants`](../packages/support/invariants) | `support` | — |
| [`atomic-write`](../packages/util/atomic-write) | `util` | [`invariants`](../packages/support/invariants) |
| [`brand`](../packages/util/brand) | `util` | [`invariants`](../packages/support/invariants) |
| [`native-command`](../packages/util/native-command) | `util` | [`invariants`](../packages/support/invariants) |
| [`paths`](../packages/util/paths) | `util` | [`invariants`](../packages/support/invariants) |
@@ -1011,6 +1035,7 @@ flowchart TD
| [`loader-smoke`](../packages/support/loader-smoke) | `support` | [`invariants`](../packages/support/invariants) |
| [`client-modules`](../packages/client/modules) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-runtime`](../packages/client/runtime) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-schema-form`](../packages/client/schema-form) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-ui-primitives`](../packages/client/ui-primitives) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-ui-slots`](../packages/client/ui-slots) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-web`](../packages/client/web) | `client` | [`invariants`](../packages/support/invariants) |
@@ -1029,10 +1054,10 @@ flowchart TD
| [`client-hmr`](../packages/client/hmr) | `client` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
| [`client-locale`](../packages/client/locale) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-test-runtime`](../packages/client/test-runtime) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`invariants`](../packages/support/invariants) |
| [`client-ui-models`](../packages/client/ui-models) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`client-ui-primitives`](../packages/client/ui-primitives), [`invariants`](../packages/support/invariants) |
| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`credentials`](../packages/credentials/credentials) | `credentials` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
| [`helper`](../packages/sdk/helper) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess) |
| [`telemetry`](../packages/sdk/telemetry) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths) |
| [`settings`](../packages/settings/settings) | `settings` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
@@ -1041,21 +1066,23 @@ flowchart TD
| [`storage-sqlite`](../packages/storage/storage-sqlite) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
| [`subprocess-local`](../packages/subprocess/subprocess-local) | `subprocess` | [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess) |
| [`typert-loader`](../packages/typert/loader) | `typert` | [`invariants`](../packages/support/invariants), [`typert-registry`](../packages/typert/registry) |
| [`llm-deepseek`](../packages/llm/llm-deepseek) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout) |
| [`llm-pi-ai`](../packages/llm/llm-pi-ai) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout) |
| [`llm-deepseek`](../packages/llm/llm-deepseek) | `llm` | [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings), [`timeout`](../packages/util/timeout) |
| [`llm-pi-ai`](../packages/llm/llm-pi-ai) | `llm` | [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings), [`timeout`](../packages/util/timeout) |
| [`session`](../packages/core/session) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`system-prompt`](../packages/core/system-prompt) | `core` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`web`](../packages/web/web) | `web` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`client-ui-models`](../packages/client/ui-models) | `client` | [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`client-schema-form`](../packages/client/schema-form), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`invariants`](../packages/support/invariants) |
| [`client-ui-question`](../packages/client/ui-question) | `client` | [`client-locale`](../packages/client/locale), [`invariants`](../packages/support/invariants) |
| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-slash`](../packages/client/ui-slash) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-theme`](../packages/client/ui-theme) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`credentials-local`](../packages/credentials/credentials-local) | `credentials` | [`atomic-write`](../packages/util/atomic-write), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths) |
| [`host-directory-picker-browse`](../packages/host/directory-picker-browse) | `host` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/support/invariants) |
| [`host-directory-picker-native`](../packages/host/directory-picker-native) | `host` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/support/invariants) |
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`sandbox`](../packages/sandbox/sandbox) | `sandbox` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`settings-local`](../packages/settings/settings-local) | `settings` | [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`settings`](../packages/settings/settings) |
| [`settings-local`](../packages/settings/settings-local) | `settings` | [`atomic-write`](../packages/util/atomic-write), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`settings`](../packages/settings/settings) |
| [`token-meter`](../packages/llm/token-meter) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`agent`](../packages/core/agent) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`bash`](../packages/bash/bash) | `bash` | [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`subprocess`](../packages/subprocess/subprocess) |

View File

@@ -23,7 +23,7 @@ This table connects model-visible tool names to the plugin package and service s
| `@deepseek-ai/dsh-tool-bash-persistent` | `bash` | `ctx.tools`, `ctx.pty`, `an owning Agent at execution time` | `tool/call`, `PTY shell state`, `tool/result` | - | One owner-isolated persistent bash tool; deployment composition supplies the PTY backend and may override the model-facing environment description. |
| `@deepseek-ai/dsh-tool-str-replace-editor` | `str_replace_editor` | `ctx.tools`, `ctx.fs` | `tool/call`, `fs/observed after successful file operations`, `tool/result` | - | Standalone view/create/unique literal replace/line insert tool over the filesystem seam; it composes with any shell or terminal surface. |
| `@deepseek-ai/dsh-tool-fs` | `edit`, `read`, `write` | `ctx.tools`, `ctx.fs`, `ctx.systemPrompt` | `tool/call`, `fs/write-intent or fs/edit-intent for mutations`, `fs/observed after successful file operations`, `tool/result` | - | The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin. |
| `@deepseek-ai/dsh-tool-fs-search` | `glob`, `grep` | `ctx.tools`, `ctx.bash`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find `rg`, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments. |
| `@deepseek-ai/dsh-tool-fs-search` | `glob`, `grep` | `ctx.tools`, `ctx.bash`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find `rg`, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). The catalog uses `sampleOverCapGlobResults: true`; deployments must choose that behavior explicitly. Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments. |
| `@deepseek-ai/dsh-tool-pty` | `terminal_close`, `terminal_list`, `terminal_open`, `terminal_read`, `terminal_send`, `terminal_signal` | `ctx.tools`, `ctx.pty`, `ctx.systemPrompt`, `ctx.tasks at call time for run_in_background` | `tool/call`, `tool/result` | - | The six terminal tools are opt-in and complement one-shot bash/filesystem tools. `terminal_send(run_in_background: true)` registers with `ctx.tasks`; TUI, named key sequences, BEL, resize, auto-start, and cross-agent sharing are absent from the schema. |
| `@deepseek-ai/dsh-tool-goal` | `create_goal`, `get_goal`, `update_goal` | `ctx.tools`, `ctx.agents`, `ctx.goals`, `ctx.systemPrompt`, `a calling Agent in an authorized open turn` | `tool/call`, `user/message goal snapshot for mutations`, `tool/result` | - | create, edit, pause, and resume require direct-human root authority; complete and blocked also accept the exact current goal round. The default blocked lower bound is three admitted rounds. |
| `@deepseek-ai/dsh-tool-lsp` | `lsp` | `ctx.tools`, `ctx.lsp`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | The lsp tool keeps provider selection and language-server subprocesses behind ctx.lsp, so its model-visible schema stays stable across providers. Requires a registered provider (e.g. `@deepseek-ai/dsh-lsp-local`) at runtime; without one, a query returns the structured `LSP_UNAVAILABLE` error rather than changing the schema. |
@@ -472,7 +472,7 @@ The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-policy` (an `
### `glob`
Find files whose paths match a glob pattern. Returns matching paths sorted by modification time, including hidden and ignored files (VCS metadata directories are excluded). Returns the first 100 paths inline; a capped result reports where the complete list was saved.
Find files whose paths match a glob pattern. Returns matching file paths — never directories — including hidden and ignored files (VCS metadata directories are excluded). Up to 100 paths come back in modification-time order; a larger result instead returns 100 paths sampled across top-level entries, says so, and reports where the complete sorted list was saved. This tool does not enumerate directory entries.
```json
{
@@ -480,7 +480,7 @@ Find files whose paths match a glob pattern. Returns matching paths sorted by mo
"properties": {
"pattern": {
"type": "string",
"description": "Glob pattern to match file paths against (e.g. \"**/*.ts\", \"src/**/*.test.js\")."
"description": "Glob pattern to match file paths against (e.g. \"**/*.ts\", \"src/**/*.test.js\"). A pattern with no \"/\" matches the basename at any depth, so \"*\" and \"*.ts\" both search the whole tree; include a separator to anchor the depth."
},
"path": {
"type": "string",
@@ -524,7 +524,7 @@ Search file contents with a ripgrep regular expression. Returns matching lines w
Source: [`packages/fs/tool-fs-search/src/index.ts`](../packages/fs/tool-fs-search/src/index.ts)
glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find `rg`, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments.
glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find `rg`, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). The catalog uses `sampleOverCapGlobResults: true`; deployments must choose that behavior explicitly. Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments.
## `@deepseek-ai/dsh-tool-pty`

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/user/guide/config.md
config.md: 00d103be990f3e5af315ab7667771b01be96ba5d
config.zh.md: a296619c3f3d68726efc4fc13703a510719fc008
config.md: 0e2e0e7e7077adcacfaada1d038a0b1e63fcc0cd
config.zh.md: 850a841286fe77db9169738b0b155f008205a1a8

View File

@@ -30,7 +30,7 @@ A minimal configuration is a list of plugin entries:
config:
agents:
- id: main
provider: deepseek
provider: deepseek-official
model: deepseek-v4-flash
```

View File

@@ -30,7 +30,7 @@ Harness 使用 `cordis.yml` 描述 Agent 加载哪些插件以及每个插件的
config:
agents:
- id: main
provider: deepseek
provider: deepseek-official
model: deepseek-v4-flash
```

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/user/guide/index.md
index.md: 72d822f6377089f56abda4383f393eed30a6709a
index.zh.md: 442cda48072c3f6bf9579e880c0ab381b799966f
index.md: cefb978019c21a24e9fb57f96ab8e0fee82dc812
index.zh.md: 2f1298b5bf5ce44f7d653154b263cb533a3b3ba4

View File

@@ -20,7 +20,7 @@ Harness implements every capability an AI agent needs—including LLM calls, too
config:
agents:
- id: main
provider: deepseek
provider: deepseek-official
model: deepseek-v4-flash
# Select the interactive front door

View File

@@ -20,7 +20,7 @@ Harness 将一个 AI Agent智能体 所需要的所有能力——LLM 调
config:
agents:
- id: main
provider: deepseek
provider: deepseek-official
model: deepseek-v4-flash
# Select the interactive front door