Commit Graph
53 Commits
Author SHA1 Message Date
Tianyi Cui eb9e7c19eb Merge remote-tracking branch 'origin/master' into worktree/ci-native-windows-20260808
# Conflicts:
#	.agents/notes/archived/process/2026-07-27-wine-windows-gates-experiment.i18n.yaml
#	.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.i18n.yaml
#	.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.zh.md
#	.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.i18n.yaml
#	.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.zh.md
2026-08-09 13:26:11 +08:00
xjt a7af54a8d3 docs(i18n): standardize contract terminology 2026-08-09 11:33:14 +08:00
Tianyi Cui da45d66b14 Merge remote-tracking branch 'origin/master' into worktree/ci-native-windows-20260808
# Conflicts:
#	vendor/README.md
2026-08-09 02:11:02 +08:00
Tianyi Cui aa0ca6c836 docs: anchor each subsystem page to its package group; make group READMEs thin tables
core.md read as a type grab-bag: LLM wire vocabulary up front, the agent/loop story buried, and no correspondence to packages/core. It now opens on the packages/core control spine — the package-by-package loop map with a Page column into session/system-prompt/tools/scope — and keeps only what the spine group declares plus the repo-wide patterns: the Agent handle with its delivery/cancellation/interception contracts, the SessionEvent envelope, branded ids, the …Map pattern. The conversation vocabulary (Message/ContentBlock, the model request, adapters — 17 type-equiv blocks) moves to llm-streaming.md, which now declares packages/llm end-to-end; the duplicate ContentBlockMap paste near its seam section folds into the moved section, and the manifest, LINK_MAP, README table rows, website label (Core data structures → Core), and inbound anchors follow.

Every packages/<group>/README pair is now a thin front door in one shape: a why-first intro (bash's seam-pattern-first paragraph rewritten as 'shell execution for the agent'), the package table, and a closing pointer to the owning docs/subsystems page — the bash-style table stays the load-bearing middle. Load-bearing trailing paragraphs relocate rather than vanish: the fs no-timeout rationale becomes a filesystem.md section (both languages), session's four sectioned tables merge into one 12-row table, examples' legacy-bin H2 collapses to a pointer at jsonrpc-demo's README, and design rationale that already lives in an Agent Note or subsystem page is now linked instead of restated. All 40 pair records re-recorded.
2026-08-09 01:32:39 +08:00
Tianyi Cui 702e2d024a fix(windows): canonicalize native watch paths 2026-08-08 19:29:43 +08:00
Yichen Jiang bce4a038c9 Merge remote-tracking branch 'origin/master' into claude/unified-environment-credentials-c8841a
# Conflicts:
#	docs/config-catalog.md
#	packages/llm/llm-pi-ai/src/config.ts
#	packages/llm/llm-pi-ai/src/index.ts
#	packages/llm/llm-pi-ai/tests/sdk-options.spec.ts
2026-08-06 16:52:19 +08:00
Yichen Jiang a358d9d68a Merge remote-tracking branch 'origin/master' into claude/unified-environment-credentials-c8841a
# Conflicts:
#	examples/headless-agent/tests/headless.snapshot.ts
#	examples/headless-agent/tests/snapshots/missing-credential/stream-json.expected.jsonl
#	packages/llm/llm-deepseek/tests/adapter.spec.ts
2026-08-06 10:52:32 +08:00
Yichen Jiang 17c86bbb25 Merge remote-tracking branch 'origin/master' into claude/web-llm-pi-ai-config-385e24
# Conflicts:
#	packages/llm/llm-pi-ai/tests/adapter.spec.ts
#	packages/llm/llm/README.i18n.yaml
2026-08-06 10:47:36 +08:00
_Kerman 488b8df547 Merge remote-tracking branch 'origin/master' into xtr/react-loop-simplification
# Conflicts:
#	.agents/notes/implemented/architecture/2026-07-25-web-client-session-scope-and-provide-channel.i18n.yaml
#	docs/architecture.i18n.yaml
#	docs/architecture.md
#	docs/architecture.zh.md
#	docs/cookbook/extension-cookbook.i18n.yaml
#	docs/cookbook/extension-cookbook.md
#	docs/cookbook/extension-cookbook.zh.md
#	docs/core-data-structures/llm-streaming.i18n.yaml
#	docs/core-data-structures/session.i18n.yaml
#	docs/defensive-patterns.i18n.yaml
#	packages/acp/acp/README.i18n.yaml
#	packages/client/runtime/README.i18n.yaml
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-goal/README.i18n.yaml
#	packages/compact/compact-basic/README.i18n.yaml
#	packages/context/README.i18n.yaml
#	packages/context/README.md
#	packages/context/README.zh.md
#	packages/context/session-reference/README.i18n.yaml
#	packages/context/session-reference/README.md
#	packages/context/session-reference/README.zh.md
#	packages/context/tmux-context/README.i18n.yaml
#	packages/core/session/README.i18n.yaml
#	packages/core/session/README.md
#	packages/core/session/README.zh.md
#	packages/goal/command-goal/README.i18n.yaml
#	packages/goal/goal-session/README.i18n.yaml
#	packages/goal/goal-session/README.zh.md
#	packages/guard/README.i18n.yaml
#	packages/guard/README.md
#	packages/guard/README.zh.md
#	packages/guard/repeat-tool-guard/README.i18n.yaml
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/README.md
#	packages/host/apiproxy/README.zh.md
#	packages/plan/plan-mode/README.i18n.yaml
#	packages/sdk/sdk-client/README.i18n.yaml
#	packages/sdk/sdk-client/README.md
#	packages/sdk/sdk-client/README.zh.md
#	packages/session-persistence/session-persistence/README.i18n.yaml
#	packages/subagent/subagent-dsh-sdk/README.i18n.yaml
#	python/sdk/README.i18n.yaml
2026-08-05 20:43:30 +08:00
Yichen Jiang 73fce861e5 fix(llm): let a catalog route keep the auth its provider actually declares
pi-ai resolves a request's apiKey override only through a provider that
declares an api-key method: resolveProviderAuth short-circuits to that
method when the override is present, and otherwise falls through to the
credential store and then to ambient discovery. A provider with no
api-key method at all therefore resolves to nothing, and the request
fails with "Provider is not configured" before any network I/O.

Two routes hit that. openai-codex ships OAuth alone, so moving off the
/compat dispatch broke a profile that names a key for it — the old path
handed the token straight to the provider. And a catalog route naming an
api was being rebuilt with the harness's own auth, so `openai: {api:
openai-completions}` stopped reading OPENAI_API_KEY, contradicting the
documented promise that omitting a credential keeps provider-native
discovery.

Auth is now one decision for both constructions. A catalog route keeps
its installed provider's auth, through an api override too: which
environment a provider reads belongs to the provider, not to the wire
format its models speak. A catalog provider with no api-key method gets
the harness method beside its own, but only when the profile names a
credential — a keyless codex profile keeps the honest refusal, since
this adapter holds no OAuth store to resolve through.

Materialization now spreads the installed entry instead of enumerating
the result, so a Model field this package does not model survives a
pi-ai upgrade; headers went missing from an nvidia route exactly that
way once already. providerInfo reports the configured displayName, which
also joins the registration facts so a rename re-registers rather than
leaving the old label in every selector. A refused registration swap
gets its own diagnostic naming the route, matching the directory swap
beside it.

The README documented endpoint interrogation this layer does not
implement, and still described unknown providers as kept-last-good after
they became legal declarations refused at the write point. The Agent
Note claimed per-model reasoning configurability the schema never had,
required capacities the route now defaults, and stated an apiKey
override that short-circuits unconditionally.
2026-08-05 18:54:47 +08:00
Yichen Jiang f376ee23d1 fix(llm): size unknown models and refuse a section that cannot be served
Three defects surfaced while driving the Models page.

A hand-declared model needed an explicit contextWindow and maxTokens,
but a provider listing usually returns ids and nothing else — so the
page happily wrote a profile the adapter then rejected, which took the
whole namespace down silently. Capacities now fall back to the route's
`defaultContextWindow` (262,144) and `defaultMaxTokens` (32,768). Both
are guesses by construction, which is why they are route fields a
deployment corrects once rather than constants buried in the adapter;
the fallback sizes the model and never becomes a per-request cap.

That silent failure was the second defect. A schema-valid profile the
adapter could not serve was stored and only rejected later, disabling
every route in the namespace with nothing said. `dsh-settings` gains an
optional `validate` on registration — a check for what a schema cannot
express — and `llm-pi-ai` refuses an unserviceable section at the write
that produced it. A stored section that fails keeps the namespace's last
good value, as a schema failure already did, so an externally edited
document still cannot strand the owner. The plugin's own last-good
fallback goes with it: nothing reaching it can fail any more.

Third, a model with no reasoning metadata advertised the single level
`off`, which pi-ai translates to *omitting* the reasoning option — the
same request naming no effort produces. Selecting it disabled nothing,
so a provider whose default is to think kept thinking with `off` shown
as selected. Such a model now reports no reasoning capability at all,
which is the seam's way of saying the control is unavailable, and the
per-model `reasoning` flag is gone: without a thinkingLevelMap to spell
levels it could only invent them.

The protocol table narrows to the three a hand-declared route reaches
today, most-reached first so a surface offering a choice defaults to the
one gateways actually speak.
2026-08-05 18:54:23 +08:00
Yichen Jiang 38d3d83cc8 Merge remote-tracking branch 'origin/master' into claude/unified-environment-credentials-c8841a
# Conflicts:
#	apps/cli/README.i18n.yaml
#	apps/cli/README.md
#	apps/cli/README.zh.md
#	docs/user/guide/config.i18n.yaml
#	packages/client/ui-models/src/client/ModelsSection.module.css
#	packages/cordis/repository-plugin/README.i18n.yaml
#	packages/cordis/repository-plugin/README.md
#	packages/cordis/repository-plugin/README.zh.md
#	packages/credentials/README.i18n.yaml
#	packages/credentials/README.md
#	packages/credentials/README.zh.md
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/llm/llm-deepseek/README.zh.md
#	packages/ui/app-boot/README.i18n.yaml
#	packages/ui/app-boot/README.md
#	packages/ui/app-boot/README.zh.md
#	packages/ui/app-boot/src/index.ts
#	packages/ui/app-boot/tests/config-reload.spec.ts
#	packages/ui/app-boot/tests/personal-config.spec.ts
2026-08-05 18:25:14 +08:00
_Kerman 857bf8c5f8 Merge remote-tracking branch 'origin/master' into xtr/react-loop-simplification
# Conflicts:
#	packages/host/apiproxy/README.i18n.yaml
2026-08-05 16:22:54 +08:00
Turtle 8af3babaea docs: rescan rebased documentation hierarchy 2026-08-05 16:16:57 +08:00
Turtle dd8d446286 docs: apply hierarchy across the corpus 2026-08-05 16:13:28 +08:00
Yichen Jiang 69d8621e2e test: close the per-file coverage gaps this PR opened
The layered-env reader gained an unreadable-layer path, a default
reporter, and two absent-layer arms with no cases; the credential store
gained two error paths that must not be mistaken for an absent file.

The platform arms and the `linePos` guard cannot be reached from a POSIX
test run — the first is covered by the native Windows job, the second
only satisfies an optional type that `prettyErrors` always fills — so
both carry a v8 ignore naming why.
2026-08-05 13:20:56 +08:00
Yichen Jiang 33e3871c77 Merge remote-tracking branch 'origin/master' into claude/unified-environment-credentials-c8841a
# Conflicts:
#	.agents/notes/implemented/architecture/2026-07-24-web-config-tree-boot-and-transport-layering.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-24-web-config-tree-boot-and-transport-layering.zh.md
#	.agents/notes/implemented/architecture/2026-07-29-request-level-llm-config-credentials.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-30-credential-boundaries-and-atomic-registration.i18n.yaml
#	apps/cli/package.json
#	apps/cli/src/web.ts
#	examples/mcp-memory/README.i18n.yaml
#	packages/cordis/repository-plugin/README.i18n.yaml
#	packages/credentials/credentials-local/README.i18n.yaml
#	packages/credentials/credentials-local/README.zh.md
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/web/web-search-deepseek/README.i18n.yaml
2026-08-05 12:40:20 +08:00
Yichen Jiang a2497a0dc1 Merge remote-tracking branch 'origin/master' into worktree/open-settings-config-file
# Conflicts:
#	packages/client/connection/README.i18n.yaml
#	packages/client/connection/README.md
#	packages/client/connection/README.zh.md
2026-08-05 11:05:52 +08:00
_Kerman 423f7517b6 Merge remote-tracking branch 'origin/master' into xtr/react-loop-simplification
# Conflicts:
#	packages/core/agent/package.json
2026-08-05 10:46:01 +08:00
imccyu ca27512529 fix(packages): omit source publication payloads 2026-08-05 01:15:19 +08:00
Yichen Jiang 4f717f2da7 fix(web): address settings document review 2026-08-04 17:31:36 +08:00
Yichen Jiang 8c2970e70e fix(config): trust the invoking project, and stop leaking what it must not decide
Review found five real defects in the configuration-source work, all confirmed
against the code rather than argued:

1. The note claimed --config outranks settings.yaml. It does not: the settings
   seam registers a plugin's cordis entry config as the `base` layer and the
   user section layers over it, and the seam cannot tell a shipped value from a
   --config one. The note now states shipped reality and names --config-replace
   as the lever for a deployment that must win. Separately, a literal `apiKey`
   in settings outranked both the environment and .credentials.yaml — the field
   is removed, so configuration carries a reference and nothing else.
2. DEEPSEEK_SEARCH_BASE_URL was functionally deleted: the shipped inline went
   away without the provider learning to read it. It now resolves from the
   environment snapshot, as the README always claimed.
3. The bootstrap deny list missed the interpreter start-up hooks. BASH_ENV is
   the sharpest: `bash -c` sources it on every bash tool call, so a project
   .env could run a file of its choosing before every command. The list now
   covers BASH_ENV and its per-language siblings, the Git hook commands, and
   the remaining preload and CA variables, organised by what a variable does
   rather than which runtime owns it.
4. YAML parse errors quoted the offending source line — which in a credentials
   document is the secret — into boot stderr and the watcher's logger. Only the
   error code and position are reported now, in credentials-local and
   settings-local alike, pinned by a test that asserts the secret is absent.
5. 0600 governed only files the harness wrote. A hand-created 0644 document was
   read normally. POSIX now checks the mode before reading contents, at boot
   and on every reload; Windows has no mode to inspect and is skipped rather
   than faked.

The project a session is launched in is trusted by default, with no prompt and
no stored trust record: it may supply its own endpoint, ordinary variables, and
a key ranked below the managed store. Trust stops at the harness itself — a
discovered file still cannot set DSH_PERMISSION_MODE, PATH, BASH_ENV, or the
rest, because those take effect with no user action, before any turn, outside
the permission policy and the sandbox.
2026-08-04 17:16:11 +08:00
Yichen Jiang e31b7221e7 feat(web): open the local settings file 2026-08-04 16:59:37 +08:00
_Kerman e0988abc74 Merge remote-tracking branch 'origin/master' into xtr/react-loop-simplification
# Conflicts:
#	.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md
#	.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.zh.md
#	.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-sandbox.md
#	.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md
#	.agents/notes/implemented/feature/2026-07-27-tmux-location-context.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-20-public-agent-stop-surface.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-28-remove-synthetic-log-only-turns.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-30-private-agent-send.i18n.yaml
#	docs/architecture.i18n.yaml
#	docs/architecture.md
#	docs/architecture.zh.md
#	docs/config-catalog.md
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/compaction.i18n.yaml
#	docs/core-data-structures/core.i18n.yaml
#	docs/core-data-structures/core.md
#	docs/core-data-structures/core.zh.md
#	docs/core-data-structures/llm-streaming.i18n.yaml
#	docs/core-data-structures/llm-streaming.md
#	docs/core-data-structures/llm-streaming.zh.md
#	docs/core-data-structures/session.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	docs/persistence-catalog.md
#	examples/acp-agent/tests/goal-snapshots/goal-session/session.expected.jsonl
#	examples/acp-agent/tests/snapshots/advanced-toolchain/session.1.jsonl
#	examples/acp-agent/tests/snapshots/advanced-toolchain/session.2.jsonl
#	examples/acp-agent/tests/snapshots/advanced-toolchain/session.jsonl
#	examples/acp-agent/tests/snapshots/bash-spill/session.jsonl
#	examples/acp-agent/tests/snapshots/bash-tool-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/both-mode-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/cancel-tool-calls/session.jsonl
#	examples/acp-agent/tests/snapshots/cancel/session.jsonl
#	examples/acp-agent/tests/snapshots/code-mode-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/code-mode-workspace-context/session.jsonl
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
#	examples/acp-agent/tests/snapshots/empty-response-retry/session.jsonl
#	examples/acp-agent/tests/snapshots/error-finish/session.jsonl
#	examples/acp-agent/tests/snapshots/escalation-approved/session.jsonl
#	examples/acp-agent/tests/snapshots/escalation-rejected/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-edit/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-escalation-approved/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-glob-sampling/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-policy-reject/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-read-window/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-read/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-write-overwrite/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-write/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-invalid-matcher/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-posttool-block/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-posttool-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-pretool-deny/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-promptsubmit-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-stop-continue/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-invalid-matcher/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-posttool-block/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-posttool-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-pretool-block/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-promptsubmit-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-stop-continue/session.jsonl
#	examples/acp-agent/tests/snapshots/lsp-definition/session.jsonl
#	examples/acp-agent/tests/snapshots/multi-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/packed-chunks/session.jsonl
#	examples/acp-agent/tests/snapshots/parallel-tool-calls/session.jsonl
#	examples/acp-agent/tests/snapshots/pty-tools/session.jsonl
#	examples/acp-agent/tests/snapshots/repeat-tool-guard/session.jsonl
#	examples/acp-agent/tests/snapshots/session-query-spill/session.jsonl
#	examples/acp-agent/tests/snapshots/session-sandbox-root/session.jsonl
#	examples/acp-agent/tests/snapshots/session-title-after-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/skill-load/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/session.2.jsonl
#	examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-fork/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-fork/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.2.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-multi/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-multi/session.2.jsonl
#	examples/acp-agent/tests/snapshots/subagent-multi/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-spawn/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-spawn/session.jsonl
#	examples/acp-agent/tests/snapshots/text-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/todo-write/session.jsonl
#	examples/acp-agent/tests/snapshots/tool-call-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/web-fetch/session.jsonl
#	examples/acp-agent/tests/snapshots/workflow-run/session.1.jsonl
#	examples/acp-agent/tests/snapshots/workflow-run/session.jsonl
#	examples/acp-agent/tests/snapshots/workspace-context/session.jsonl
#	examples/acp-agent/tests/snapshots/workspace-edit/session.jsonl
#	examples/headless-agent/tests/semantic-checkpoint-snapshots/tool-outcome-unknown/session.expected.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.1.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.2.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/goal-tools/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/missing-credential/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/provider-retry/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/pty-tools/session.jsonl
#	examples/headless-agent/tests/snapshots/pty-tools/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/ralph-loop/stream-json.expected.jsonl
#	examples/headless-agent/tests/subagent-inheritance-snapshots/parent-override/child.expected.jsonl
#	examples/headless-agent/tests/subagent-inheritance-snapshots/parent-override/parent.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/bash-tool/notifications.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/bash-tool/session.jsonl
#	examples/jsonrpc-agent/tests/snapshots/persistent-tools/notifications.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/persistent-tools/session.jsonl
#	examples/jsonrpc-agent/tests/snapshots/subagent-spawn/notifications.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/subagent-spawn/session.1.jsonl
#	examples/jsonrpc-agent/tests/snapshots/subagent-spawn/session.jsonl
#	examples/jsonrpc-agent/tests/snapshots/text-turn/notifications.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/text-turn/session.jsonl
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/src/client/sessions/request-inspection.ts
#	packages/compact/compact-basic/README.i18n.yaml
#	packages/compact/compact-basic/README.md
#	packages/compact/compact-basic/README.zh.md
#	packages/compact/compact-basic/src/index.ts
#	packages/context/time-context/tests/time-context.spec.ts
#	packages/context/tmux-context/README.i18n.yaml
#	packages/context/tmux-context/tests/tmux-context.spec.ts
#	packages/context/workspace-context/tests/workspace-context.spec.ts
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/core/agent-loop/README.i18n.yaml
#	packages/core/agent-loop/README.md
#	packages/core/agent-loop/README.zh.md
#	packages/core/agent-loop/src/agent.ts
#	packages/core/agent/README.i18n.yaml
#	packages/core/agent/README.md
#	packages/core/agent/README.zh.md
#	packages/core/agent/src/types.ts
#	packages/core/session/README.i18n.yaml
#	packages/core/session/README.md
#	packages/core/session/README.zh.md
#	packages/fs/tool-str-replace-editor/tests/tools.spec.ts
#	packages/goal/command-goal/tests/command-goal.spec.ts
#	packages/goal/goal/tests/goal.spec.ts
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/README.md
#	packages/host/apiproxy/README.zh.md
#	packages/host/apiproxy/src/api/index.ts
#	packages/host/apiproxy/tests/api-proxy-workspace.spec.ts
#	packages/llm/llm/README.i18n.yaml
#	packages/llm/llm/README.md
#	packages/llm/llm/README.zh.md
#	packages/llm/llm/src/index.ts
#	packages/pty/pty-local/tests/index.spec.ts
#	packages/pty/pty-local/tests/local.spec.ts
#	packages/pty/pty/tests/service.spec.ts
#	packages/pty/tool-bash-persistent/tests/loader-composition.spec.ts
#	packages/pty/tool-bash-persistent/tests/tools.spec.ts
#	packages/pty/tool-pty/tests/loader-composition.spec.ts
#	packages/pty/tool-pty/tests/tools.spec.ts
#	packages/session-persistence/session-checkpoint-policy/tests/crash-recovery.e2e.ts
#	packages/skill/tool-skill/tests/tool-skill.spec.ts
#	packages/tasks/tasks-local/tests/tasks.spec.ts
#	packages/ui/tui/README.i18n.yaml
#	packages/ui/tui/tests/tui.spec.ts
#	packages/ui/user-approval/src/index.ts
#	packages/ui/user-approval/tests/approval.spec.ts
2026-07-31 22:16:40 +08:00
_Kerman afedf18ccf fix inbox lifecycle downstream contracts 2026-07-31 22:00:39 +08:00
Tianyi Cui e9096c891c Merge latest master into worktree/pr884-simplify 2026-07-31 11:00:07 +08:00
Tianyi Cui afb05b4049 fix(settings-local): never steal writer locks 2026-07-31 01:27:46 +08:00
Tianyi Cui f9f8148e79 refactor(credentials,llm): remove speculative mutation and route lifecycle 2026-07-31 01:27:46 +08:00
Yichen Jiang 8c0d626cd5 Merge worktree-llm-dynamic-config (884, with latest master) into worktree-llm-web-config
# Conflicts:
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/core.i18n.yaml
#	packages/client/runtime/README.i18n.yaml
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/README.md
#	packages/host/apiproxy/README.zh.md
2026-07-30 23:32:31 +08:00
Yichen Jiang 54c60f4079 fix(llm,settings): refuse post-disposal route replacement and teardown notifications
Two lifecycle holes the registry and the consumer helper left open.

`AdapterRegistrationHandle.replace` had no liveness guard: after the handle's
disposer ran, a replace put routes back into the registry with nothing left to
release them, so the adapter leaked permanently. `owned` being empty cannot
carry that fact, because `replace([])` is the legal empty-section state, so the
disposer records it explicitly.

`installSettingsSection`'s watcher lacked the guard its own disposer carries:
a stored change landing while the consumer unloads reached `onChange`, which
re-registers routes against a fiber whose resources are being released.

Also documents `withFileLock` in the atomic-write README (it claimed one
export), records the age-based lock takeover as a known limitation, and lists
ctx.settings and ctx.credentials in the architecture capability table.
2026-07-30 23:23:18 +08:00
Yichen Jiang b6004aca52 test(settings,apiproxy): cover the path-write and revision paths the wire reaches
The mutate seam's root-path ops, its non-array rejection, and the recursion
into an existing nested object were unexercised, as was the whole containment
side of the settings/document-updated fan-out and the before-snapshot repair
of a section a hand edit left non-object. The settings.mutate route had no
round trip at all: neither the client method nor the handler entry ran.
2026-07-30 21:25:16 +08:00
Yichen Jiang e6483f0afc feat(settings): detect stale writers with a revision, and announce raw changes
The remaining P1 from the #939 review, plus the P2 it shares a mechanism with.

Nothing carried a version, so two tabs editing one namespace silently
overwrote each other — reproduced as tab B's `reasoning` lost to tab A's
older draft. The seam's per-namespace write queue orders writes; it cannot
tell a fresh writer from one replaying a snapshot a predecessor superseded.

Each namespace now carries a monotonic `revision` over its RAW section. A
write may send `expectedRevision`, checked at the FRONT of the queue (not at
call time, which would race the very predecessor it guards against); a
mismatch rejects with `SettingsConflictError` → `settings-conflict` on the
wire, carrying both revisions. The editor captures the revision it opened at
and, on conflict, asks the user to reopen rather than replaying its snapshot.

The same counter fixes the missing broadcast. `settings/updated` is gated on
the resolved value — correct for consumers, wrong for configuration surfaces:
storing an override equal to the composition base leaves the resolved value
alone while changing what the document says (the field is now overridden, not
inherited) and moving every open editor's revision. `settings/document-updated
(ns, revision)` fires on any raw-section change, in-process or external, and
`host/settings-changed` now rides it.

That event also closes the stale model picker: editing a provider's `models`
changes no route, so `llm/adapters-updated` never fired and an open picker
kept serving the old catalog. A change to an exposed provider namespace now
emits `host/models-changed` too — that namespace holds the catalog.

Docs: both sides of the five touched README pairs, a type-equiv block for
`SettingsPathOp`, and an Agent Note recording what the plane exposes and who
may overwrite what. The deferred wire-redaction gaps (secrets behind
union/intersection/transform, `.default(...)` in the served envelope, schema
text in rejection messages, `new Function` rehydration, pi-ai's `headers`) are
recorded as TODO(settings-wire-redaction) and in Known Limitations rather than
half-fixed.
2026-07-30 19:24:21 +08:00
Yichen Jiang 9f996be8e3 fix(web-config): close the wire boundary, the redacted-replace data loss, and three P2s
Five findings from the #939 review, each reproduced before being fixed.

**Configuration reads are as privileged as writes.** `settings.describe`
returns every exposed namespace's configuration and `credentials.describe`
reports whether an arbitrary environment-variable name is configured and from
where — reconnaissance no anonymous caller should have. Both join
PRIVILEGED_METHODS, so the whole configuration plane is loopback-only until
real authentication exists; `trustedHosts` was never authentication. The model
catalog stays reachable: it carries no endpoints or key state, and a LAN
client's model picker legitimately needs it. Asserted over a real HTTP server,
because the Host header a browser actually sends is what decides this.

**The proxy serves only namespaces a registered model provider addresses.**
The settings seam is general — any plugin may register one — but the Web
configuration plane is the model-provider surface. Without the gate, every
future `settings.register()` would silently become remotely readable and
writable configuration. An unregistered namespace and an unexposed one answer
identically, so no caller can enumerate the registry one probe at a time.

**Path-addressed writes replace the redacted-document rebuild.** The editor
reads the REDACTED descriptor, so rebuilding a section from it and replacing
wholesale deleted every literal secret the wire never returned — reproduced as
`{baseURL, reasoning}` in, stored `apiKey` gone out. `settings.mutate` applies
set/unset ops to the section as it stands at the front of the seam's write
queue, and the client names only fields it can see, so an unseen secret is
untouched by construction rather than by care.

P2s in the same pass: `llm/adapters-updated` now contains async listener
rejections (an uncontained one escaped as unhandledRejection, contradicting
the documented "observer failures are contained"); llm-deepseek's retry-policy
swap uses the atomic `registration.replace` instead of dispose-then-register,
which published `[]` then `["deepseek-official"]` so an observer saw the
provider disappear and come back; and a transport rejection no longer strands
the page in `loading` or a card in `busy`, with removal failures surfaced on
the page banner instead of swallowed.
2026-07-30 18:30:15 +08:00
Yichen Jiang b73e1811ff Merge branch 'worktree-llm-dynamic-config' into worktree-llm-web-config 2026-07-30 17:39:26 +08:00
Yichen Jiang caf2db0929 Merge branch 'worktree-config-settings-seam' into worktree-llm-dynamic-config
# Conflicts:
#	packages/settings/settings-local/src/index.ts
2026-07-30 17:39:07 +08:00
Yichen Jiang 0a263941ef Merge branch 'worktree-llm-dynamic-config' into worktree-llm-web-config
# Conflicts:
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/core.i18n.yaml
#	docs/event-producer-consumer.md
#	examples/headless-agent/tests/headless.snapshot.ts
#	examples/headless-agent/tests/snapshots/missing-credential/stream-json.expected.jsonl
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/llm/llm-deepseek/src/index.ts
#	packages/llm/llm-pi-ai/README.i18n.yaml
#	packages/llm/llm-pi-ai/src/index.ts
#	packages/llm/llm/README.i18n.yaml
#	packages/llm/llm/src/index.ts
2026-07-30 17:22:44 +08:00
Tianyi Cui 07e3d15d42 docs(settings): mark deferred review follow-ups 2026-07-30 17:07:29 +08:00
Yichen Jiang d91f0227e6 fix(settings): keep installSettingsSection quiet when its consumer unloads
The helper's cleanup ran the same fallback for two different events. A
settings provider detaching leaves the consumer running, so falling back
to the composition entry and re-judging derived facts is right. The
consumer's own unload ran it too — re-registering routes and touching
resources the teardown was releasing. The disposer now checks the
consumer fiber's own state and returns when it is unloading or disposed.
2026-07-30 15:52:51 +08:00
Yichen Jiang 90c3118302 fix(credentials-local): one operation chain, read-modify-write under the shared writer lock, and a quote-aware line editor
Review round three, credentials half. dsh-atomic-write grows the
cross-process writer-lock primitive (withFileLock: wx sentinel, bounded
backoff, stale takeover via onStaleBreak, deadline failure) plus a dirMode
option, and settings-local migrates its private copy to it; both providers
now create harness-home directories 0700.

credentials-local reuses the reviewed settings-local shape: watcher
reloads and line edits share one settled operation chain; every write
re-reads the document under the lock and publishes unobserved external
entries before editing, so an edit inside the debounce window (or another
process's write) can never be overwritten; the watcher's ready signal
queues one reconcile closing the startup gap.

The line editor is now physical-line aware: continuation lines of a
quoted multi-line value are never mistaken for assignments, untouched
lines keep their exact bytes (CRLF included), an edited line keeps its
own terminator, and appends use the document's dominant ending. A
multi-line entry reports writable: false, matching what set() would do.

The Credentials base class owns a contained notifyUpdated fan-out:
providers publish only after the commit, every listener runs, sync throws
and async rejections are logged without failing the committed write, and
INVARIANT-coded failures rethrow after the fan-out.
2026-07-30 15:40:09 +08:00
Yichen Jiang 483199d47a Merge branch 'worktree-llm-dynamic-config' into worktree-llm-web-config
# Conflicts:
#	apps/cli/cordis.yml
#	apps/web/tests/snapshots/code-mode-round/session.jsonl
#	apps/web/tests/snapshots/cordis-tool-round/session.jsonl
#	apps/web/tests/snapshots/fresh-round-trip/session.jsonl
#	apps/web/tests/snapshots/lifecycle-chrome/session.jsonl
#	apps/web/tests/snapshots/live-interactions/session.jsonl
#	apps/web/tests/snapshots/navigation-panes/seed.jsonl
#	apps/web/tests/snapshots/question-composer/session.jsonl
#	apps/web/tests/snapshots/seeded-history/seed.jsonl
#	apps/web/tests/snapshots/steering/session.jsonl
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/core.i18n.yaml
#	docs/core-data-structures/settings.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	examples/acp-agent/tests/snapshots/workspace-context/session.jsonl
#	packages/client/connection/README.i18n.yaml
#	packages/client/connection/src/index.ts
#	packages/client/connection/tests/node-half.spec.ts
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/README.md
#	packages/client/runtime/README.zh.md
#	packages/client/runtime/src/client/index.ts
#	packages/client/runtime/tests/fake-api.ts
#	packages/client/ui-models/README.i18n.yaml
#	packages/examples/tui-demo/README.i18n.yaml
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/package.json
#	packages/host/apiproxy/src/api-proxy.ts
#	packages/host/apiproxy/src/api/rpc.schema.ts
#	packages/host/apiproxy/src/api/rpc.ts
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/llm/llm-deepseek/README.zh.md
#	packages/llm/llm-pi-ai/README.i18n.yaml
#	packages/llm/llm/README.i18n.yaml
#	packages/llm/llm/README.zh.md
#	packages/sdk/sdk-client/README.i18n.yaml
#	packages/settings/settings/README.i18n.yaml
#	packages/settings/settings/README.md
#	packages/settings/settings/README.zh.md
#	packages/subagent/subagent-dsh-sdk/README.i18n.yaml
#	packages/subagent/subagent-dsh-sdk/README.zh.md
#	packages/support/llm-replay/README.i18n.yaml
#	packages/ui/jsonrpc/README.i18n.yaml
#	packages/ui/jsonrpc/README.zh.md
#	packages/ui/tui/tests/snapshots/model-selector.expected.txt
#	packages/ui/tui/tests/snapshots/model-switching.expected.txt
#	packages/ui/tui/tests/snapshots/resume-sessions.expected.txt
#	packages/ui/tui/tests/snapshots/status-diagnostics-narrow.expected.txt
#	packages/ui/tui/tests/snapshots/status-diagnostics.expected.txt
#	packages/ui/tui/tests/tui.snapshot.ts
#	pnpm-lock.yaml
#	python/sdk/README.i18n.yaml
#	scripts/snapshots/translation-prompt-v4/request-response.expected.json
2026-07-30 15:18:26 +08:00
Yichen Jiang 8ff6b23214 Merge branch 'worktree-config-settings-seam' into worktree-llm-dynamic-config
# Conflicts:
#	docs/capability-seams.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/core.i18n.yaml
#	docs/module-graph.md
#	examples/tui-agent/cordis.yml
#	packages/README.i18n.yaml
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/llm/llm-deepseek/README.zh.md
#	packages/llm/llm-pi-ai/README.i18n.yaml
#	packages/llm/llm-pi-ai/README.zh.md
#	packages/settings/settings-local/src/index.ts
#	packages/util/README.i18n.yaml
#	packages/util/README.md
#	packages/util/README.zh.md
#	scripts/doc-budgets.manifest.json
2026-07-30 14:33:36 +08:00
Yichen Jiang 2b379799ba test(settings): make third-review specs conform to strict optional and misused-promise contracts
Drop the explicit-undefined base fixture exactOptionalPropertyTypes
forbids (the repository trusts TypeScript at typed same-process seams —
no test for an input the static interface excludes; coverage holds), and
reshape the async-listener containment fixture as an unknown-returning
function: the earlier inline cast was silently stripped by the staged
oxlint fixer, leaving a shape the next lint pass rejects.
2026-07-30 14:13:15 +08:00
Yichen Jiang 3b1b912518 docs(settings): third-review contracts across READMEs, catalogs, and the write-path integrity note
The seam README states the JSON-shaped write boundary, watch-disposer
quiescence, async listener containment, and the drained teardown; the
provider README rewrites Behavior around the operation chain,
read-modify-write, writer lock, ready reconcile, and leaf-level YAML
diffs, and updates Known Limitations to the residual guarantees.
A new Agent Note records the round's decisions and supersedes the
original note's deferred-lockfile alternative (cross-linked in place).
Chinese counterparts updated pair-by-pair (three briefed minimal
updates, one whole-document translation); type-equiv, config, cordis,
and module-graph catalogs re-recorded.
2026-07-30 14:09:04 +08:00
Yichen Jiang 85a3a158dd fix(settings-local): one operation chain, read-modify-write under a writer lock, and diff-shaped YAML edits
Review round three found the provider's write path could destroy state it
never observed:

- Watcher reloads and document writes ran on two independent promise
  chains, and a write rendered the whole next document from the cached
  text. An external edit still inside the debounce window (or missed
  outright) was overwritten, and the follow-up reload no-oped because the
  post-rename content matched the cache — the edit vanished without a
  trace. Reloads and writes now share one operation chain, and every write
  starts by reconciling the on-disk text into the seam before rendering,
  so unobserved sibling sections survive and publish first. An unparsable
  on-disk document fails the write loud instead of being overwritten.
- The initial load raced the watcher's own setup: a change written between
  that read and the watcher becoming active never fired an event. The
  watcher's ready signal now queues one reconcile, closing the gap.
- Two processes sharing a harness home rendered from independent caches,
  last writer winning. Writes now hold a wx-created <file>.lock sibling
  around the read-render-rename cycle with bounded backoff, a crashed-
  holder stale takeover, and a deadline failure; readers stay lock-free
  because the rename commit is atomic.
- renderYaml replaced the whole namespace node, dropping every comment
  inside the section. The next section now lands as a leaf-level diff
  (set changed values, delete removed keys), so comments, anchors, and
  formatting survive on every untouched node and on the key of every
  changed pair; arrays still replace wholesale when unequal.
2026-07-30 13:39:22 +08:00
Yichen Jiang bdc6d95d56 fix(settings): close third-review watcher-lifecycle and write-boundary gaps
Review round three found four seam defects:

- A watch() disposer only removed the observer from the set; an invocation
  already chained onto the watcher tail still ran after disposal. Watchers
  now carry an active flag checked when a queued invocation would start,
  and the service dispose drain awaits started invocations (pendingTails)
  beside the write queues, so disposal is quiescent.
- The settings/updated manual fan-out caught only synchronous throws; an
  async listener rejection escaped as an unhandled rejection. Thenable
  returns are now contained through the shared listener diagnostic, and
  the event contract documents that the INVARIANT rethrow serves
  synchronous listeners only.
- structuredClone admitted Dates, Maps, BigInts, and cycles that YAML/JSON
  storage silently distorts on reload (a Date lands as a timestamp string,
  a Map as a plain map, a BigInt as a number). The write snapshot is now a
  single-pass cloneJsonShaped walk that rejects non-JSON values with their
  path before anything persists.
- mergeLayers' per-entry undefined guard became dead code once the clone
  strips undefined entries at the boundary; removed, with the sparse-patch
  contract restated at its enforcement point.
2026-07-30 13:29:52 +08:00
Yichen Jiang 51415debe5 docs: bilingual config-plane documentation, regenerated catalogs, and the web-config-plane Agent Note 2026-07-30 10:53:39 +08:00
Yichen Jiang 353f5c0a39 build(settings): bundle the package root and invariant companion independently 2026-07-30 10:40:45 +08:00
Yichen Jiang a5c8136cb3 feat(settings): layered descriptors and structural secret redaction
describe() now carries each namespace's detached composition base and raw
user section beside the resolved value — presence in the user layer is how
a form marks a field user-overridden — and describe({redactSecrets:true})
strips role('secret') fields from every layer while enumerating their
{path,set} slots, so a wire surface has no slot that can carry a secret.
The pure redactSecrets(schema,value) walker (object/dict/array containers,
secret-role subtree as opaque leaf, inputs never mutated) is exported for
any other wire; the README's no-redaction Known Limitation is discharged.
2026-07-29 16:50:05 +08:00
Yichen Jiang b0a2011d95 docs: bilingual credentials/settings-consumer documentation, catalogs, and gates
New credentials data-structure page (type-equiv manifested), group README,
rewritten llm-deepseek/llm-pi-ai READMEs (dynamic configuration, dict
profiles, credential chain), capability-seams/service-role registration,
Agent Note (bilingual), demo compositions mounting settings-local +
credentials-local with no inline key plumbing, installSettingsSection
consumer helper on the settings seam (deduplicating both adapters' wiring),
jscpd symmetry markers for the provider twins, runtime-closure additions for
python/sdk-runtime, and doc-budget ceilings AGENTS.md 1750→1755 /
packages/README.md 850→865 for the structural one-line group rows.
2026-07-29 14:20:06 +08:00
Yichen Jiang ba37180946 feat(util): extract dsh-atomic-write and migrate settings-local writes
writeFileAtomic: exclusive-create random-suffix temp + rename carrying the
caller-stated mode; settings-local persistSection now consumes it. The
credentials-local store shares it next.
2026-07-29 12:59:41 +08:00