docs(credentials): record the third-review contracts across READMEs, catalogs, and a new Agent Note

Both provider READMEs state what actually holds: credentials-local now
documents the physical-line editor, the read-modify-write under the
writer lock, and a Security boundary section saying plainly that the file
mode stops other OS users and not the model. sandbox-policy documents
readDenyPaths and its per-backend enforcement. The llm READMEs carry the
registration handle, pi-ai's credential-miss semantics, and DeepSeek's
same-generation snapshot; app-boot and the CLI README stop describing
$DSH_HOME/.env as an environment layer.

A new Agent Note records the round (and the prior seam note cross-links
it); the sandbox and core catalog pages gain readDenyPaths and
AdapterRegistrationHandle with their manifest entries. The headless
missing-credential snapshot re-records for the reworded guidance, pi-ai
gains the Loader-composition guard its twin already had, and the
deliberate provider symmetry is marked for the clone detector.
This commit is contained in:
Yichen Jiang
2026-07-30 16:37:28 +08:00
parent 9626c15c6b
commit e7894f4152
31 changed files with 212 additions and 54 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/core-data-structures/core.md
core.md: 5e1049a131cfdbf2368350dbc199aebceebf71ba
core.zh.md: fbb95c1dfa40cc05d9e1f3a4c6ef32c11cab0ec7
core.md: 5c79f454f50a059d72a592df45d504ee78835e0b
core.zh.md: 258517c625822bdbd64138baf3df186b075bb5c6

View File

@@ -183,6 +183,30 @@ 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.
* @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 {

View File

@@ -189,6 +189,30 @@ 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.
* @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 {

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
sandbox.md: 9bc05fa06f22fdc9ac9e8aacd482c1e7c2f2edec
sandbox.zh.md: 9a52f126758fe0e7988715c7824e963bd6e6ea84
# pnpm run verify-translation-pairing --write docs/core-data-structures/sandbox.md
sandbox.md: 566ac0edc0ba0600e2a1b5ecf18cc34e05e910ec
sandbox.zh.md: 24d8fbfc6c952246278192b5bed7cdc09223e7db

View File

@@ -40,7 +40,7 @@ type SandboxEnforcement = 'full' | 'partial'
## Per-call policy
The complete execution policy is resolved and carried per capability call. It includes `danger-full-access` so a consumer can resolve policy once before deciding whether to bypass confinement. Normal tool calls derive `workspaceRoot` from the calling session's immutable cwd; deployment configuration is the agentless fallback. The root is canonicalized with filesystem semantics before lexical normalization, so a cwd containing `symlink/..` identifies the directory where a spawned process actually runs.
The complete execution policy is resolved and carried per capability call. It includes `danger-full-access` so a consumer can resolve policy once before deciding whether to bypass confinement. Normal tool calls derive `workspaceRoot` from the calling session's immutable cwd; deployment configuration is the agentless fallback. The root is canonicalized with filesystem semantics before lexical normalization, so a cwd containing `symlink/..` identifies the directory where a spawned process actually runs. `readDenyPaths` names paths a confined execution must not read whatever its mode permits — the harness credential document by default — and backends that cannot express such a denial report `partial` enforcement rather than claiming a boundary the process lacks.
```ts type-equiv
/**
@@ -53,6 +53,18 @@ interface SandboxExecutionPolicy {
mode: SandboxMode
/** Absolute root directory `workspace-write` may write under. */
workspaceRoot: string
/**
* Absolute paths a confined execution must not READ, whatever the mode
* otherwise permits — the harness's own credential document is the
* motivating case, which is why these are exact paths rather than roots:
* denying the whole harness home would also take away the model's
* documented access to its own session log. Not every backend can express
* a read denial (a Landlock allow-list granting `/` cannot subtract from
* itself), so {@link ConfinedArgv.enforcement} drops to `partial` when a
* denial is requested and the selected backend cannot apply it. Never a
* boundary under `danger-full-access`, which confines nothing at all.
*/
readDenyPaths?: readonly string[]
}
```

View File

@@ -40,7 +40,7 @@ type SandboxEnforcement = 'full' | 'partial'
## 逐调用策略
完整执行策略会按每次能力调用解析并携带。它包括 `danger-full-access`,因此消费方可以只解析一次策略,再决定是否绕过约束。普通工具调用从调用会话的不可变 cwd 派生 `workspaceRoot`;部署配置是没有 agent智能体时的回退值。root 会先按文件系统语义规范化,再做词法规范化,因此包含 `symlink/..` 的 cwd 会标识所生成进程实际运行的目录。
完整执行策略会按每次能力调用解析并携带。它包括 `danger-full-access`,因此消费方可以只解析一次策略,再决定是否绕过约束。普通工具调用从调用会话的不可变 cwd 派生 `workspaceRoot`;部署配置是没有 agent智能体时的回退值。root 会先按文件系统语义规范化,再做词法规范化,因此包含 `symlink/..` 的 cwd 会标识所生成进程实际运行的目录。`readDenyPaths` 点名受限执行无论其模式允许什么都不得读取的路径——默认是 harness 凭据文档——无法表达此类拒绝的后端会把强制执行报为 `partial`,而不是声称一条该进程其实并不具备的边界。
```ts type-equiv
/**
@@ -53,6 +53,18 @@ interface SandboxExecutionPolicy {
mode: SandboxMode
/** Absolute root directory `workspace-write` may write under. */
workspaceRoot: string
/**
* Absolute paths a confined execution must not READ, whatever the mode
* otherwise permits — the harness's own credential document is the
* motivating case, which is why these are exact paths rather than roots:
* denying the whole harness home would also take away the model's
* documented access to its own session log. Not every backend can express
* a read denial (a Landlock allow-list granting `/` cannot subtract from
* itself), so {@link ConfinedArgv.enforcement} drops to `partial` when a
* denial is requested and the selected backend cannot apply it. Never a
* boundary under `danger-full-access`, which confines nothing at all.
*/
readDenyPaths?: readonly string[]
}
```