Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ It is the same trick `scripts/check.ts` already uses for the client bundle, at f
- `src/stream-hook.ts` · `src/fetch-patch.ts` · `src/tool-fallback.ts` — the `llm/stream` hook, the fetch interceptor, and the free-tier `read`/`bash` fallback.
- `src/utils/request-shape.ts` — the fetch patch's PURE decisions, extracted so each branch is unit-testable on its own: which plane a URL belongs to, which credential header a protocol reads, the URL and header source of a `fetch` input, the model id and tool names a JSON body carries, and the Anthropic version constant. Everything here is a function of its arguments — no context, no globals, no I/O. The rule it encodes: **a predicate that lives in a closure cannot be exercised alone**, so its branches survive until some integration path happens to take them. `hasSessionHeader` and `isOpenCodeRequest` were exported and tested; their six private siblings were not, and that was the difference.
- `src/key-capture.ts` · `src/go-discovery.ts` · `src/usage.ts` · `src/usage-contract.ts` — credential extraction, quota discovery, the usage service, and the shared shape.
- `src/catalog-data.ts` · `src/models-catalog.ts` · `src/models-discovery.ts` — the generated static shim, the OpenCode-catalog parser with SWR refresh, and discovery decoration. `catalog-data.ts` is **generated**; never hand-edit it.
- `src/catalog-data.ts` · `src/models-catalog.ts` · `src/models-discovery.ts` — the generated static shim, the OpenCode-catalog parser with SWR refresh, and the Settings discovery wrapper (behind `enrichModels`; off, Fetch is pi's list and `NO_DISCOVERY` passes through): on a plane the plugin serves, Settings → Models → Fetch answers pi's list plus the catalog's servable, non-deprecated models pi lacks (D3, P3), and the catalog alone while pi-ai restarts. `catalog-data.ts` is **generated**; never hand-edit it.
- A note on the sweep that found the two entries around here, because it nearly produced a false one: grepping for a symbol's callers while EXCLUDING the file that defines it makes every same-file call look like no call at all. `resolveGoKeyForRef` reads as unreferenced that way and is not — `resolveGoApiKey` calls it, and the usage service calls that. (An earlier version of this line said that is "how the Go endpoint keeps Zen keys away from itself"; there is no such exclusion, and the note below records why.) Check the defining file too before believing an export is dead.

**The general form, because this shape recurred twice in one session**: a sweep that resolves a name in the wrong CONTEXT reports a clean tree as broken. Grepping for callers without the defining file makes same-file calls look absent; resolving a doc's relative links from the repository root instead of from the file's own directory makes every link look dead. Both produced a list of confident findings and both were entirely wrong. A third instance is reading a UTC timestamp as local time and concluding that a workflow had not run since the previous evening, when it had run twenty minutes earlier — same shape, wrong context, confident conclusion. Before acting on a sweep, check that the thing being searched is the thing the claim is about, including the clock it is written in.
Expand Down
9 changes: 5 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ What the main files are for:

## The real-host harness

`test/support/host.ts` builds the host from the packages this repository installs, at the versions `test/support/host-versions.ts` names (pi 1.1.0 with the harness patch, the `@deepseek-ai` cluster at the published host's tag). The routing work is judged against it: the contract tests pin the host facts the design rests on that the host can answer in-process (`docs/improvement-plan.md`: F1, F2, F4, F5, F8, F10–F16, F18 and F20, each by number; F6 and F19's auth half (constructing a `PiAiAdapter`) are first exercised by P-R1; F19's export half is pinned in `host-shapes` (`recordKeyFor`); F17 is honoured, not pinned — tier B fixes the apply order and the binary e2e infers it; F9 is a fact about opencode's own client, outside this host), and the characterization rows record what today's plugin does.
`test/support/host.ts` builds the host from the packages this repository installs, at the versions `test/support/host-versions.ts` names (pi 1.1.0 with the harness patch, the `@deepseek-ai` cluster at the published host's tag). The routing work is judged against it: the contract tests pin the host facts the design rests on that the host can answer in-process (`docs/improvement-plan.md`: F1, F2, F4, F5, F8, F10–F16, F18 and F20, each by number; F6 and F19's auth half (constructing a `PiAiAdapter`) are first exercised by P-R1; F19's export half is pinned in `host-shapes` (`recordKeyFor`); F17 is honoured, not pinned — tier B fixes the apply order and the binary e2e infers it; F9 is a fact about opencode's own client, outside this host; F21 is pinned by P3 in `host-discovery`, the pi-ai restart and contract cases), and the characterization rows record what today's plugin does.

Two tiers, both in-process:

- **Tier A — `mountTierA`.** A bare cordis `Context`, `LlmRuntime`, and `dsh-llm-pi-ai` mounted as a plugin with `{ providers }` (and optionally this plugin). No loader and no files. Use it for the `internal/config` waterfall and pi-ai's credential paths — anything that needs a fiber and the registry and nothing that reads a profile (`host-waterfall`, `host-shapes`, `host-credentials`). Mounted here (`plugin`), this plugin is the real one: tier A has no loader, so `mountTierA` swaps the plugin's host-module source for `hostModulesForTests` through the seam in `src/cordis-context.ts` (`replaceHostModuleSource`), restores it on dispose, and the route claim serves both planes as it does on the real host (`host-harness`, `host-planes`). pi-ai is mounted first there, so the claim restarts it once. Characterize the plugin as it ships in tier B, which keeps the default source.
- **Tier B — `bootTierB`.** dsh-app-boot's `boot()` with a temporary HOME, a test bundle and a profile patch, the way the harness's own composition tests boot one. Use it for the config editor, `settings.describe`, the loader's volatile commit and the characterization of the plugin as it ships (`host-editor-contracts`, `host-characterization`). Its rows carry **package names**, resolved by a stub module loader, not `cordis:` builtins: the plugin's default host-module source finds pi-ai's entry by `options.name === "@deepseek-ai/dsh-llm-pi-ai"` (`piAiEntry` in `src/host/host-modules.ts`), so a builtin-named row would leave the route claim without pi-ai's module. The loader starts rows in parallel, so tier B makes the order explicit: `order: "plugin-first"` (the default, and what the published binary does) or `"pi-ai-first"`.
- **Tier A — `mountTierA`.** A bare cordis `Context`, `LlmRuntime`, and `dsh-llm-pi-ai` mounted as a plugin with `{ providers }` (and optionally this plugin). No loader and no files. Use it for the `internal/config` waterfall and pi-ai's credential paths — anything that needs a fiber and the registry and nothing that reads a profile (`host-waterfall`, `host-shapes`, `host-credentials`). Mounted here (`plugin`), this plugin is the real one: tier A has no loader, so `mountTierA` swaps the plugin's host-module source for `hostModulesForTests` through the seam in `src/cordis-context.ts` (`replaceHostModuleSource`), restores it on dispose, and the route claim serves both planes as it does on the real host (`host-harness`, `host-planes`). pi-ai is mounted first there, so the claim restarts it once. Pass `hostModules: null` to keep the plugin's default source instead: with no loader it cannot find pi-ai's module, so the claim stands down and pi-ai keeps the planes (`host-discovery`'s stood-down case). Characterize the plugin as it ships in tier B, which keeps the default source.
- **Tier B — `bootTierB`.** dsh-app-boot's `boot()` with a temporary HOME, a test bundle and a profile patch, the way the harness's own composition tests boot one. Use it for the config editor, `settings.describe`, the loader's volatile commit and the characterization of the plugin as it ships (`host-editor-contracts`, `host-characterization`, `host-discovery`). Its rows carry **package names**, resolved by a stub module loader, not `cordis:` builtins: the plugin's default host-module source finds pi-ai's entry by `options.name === "@deepseek-ai/dsh-llm-pi-ai"` (`piAiEntry` in `src/host/host-modules.ts`), so a builtin-named row would leave the route claim without pi-ai's module. The loader starts rows in parallel, so tier B makes the order explicit: `order: "plugin-first"` (the default, and what the published binary does) or `"pi-ai-first"`.

What every case does:

Expand All @@ -78,7 +78,8 @@ What every case does:

`.github/workflows/ci.yml`'s `e2e-host` job installs the published `@deepseek-ai/dsh` (at `HOST_VERSIONS.dsh`) into a throwaway prefix, builds this checkout, and runs `test/e2e/host-binary.e2e.ts`. That file creates a temporary HOME whose profile links this checkout and the probe bundle (`test/e2e/host/`) through relative `link:` paths, stores dummy keys in the temporary home's credentials document (`$DSH_HOME/.credentials.yaml`, shared by every profile under that home), boots `dsh --profile e2e --no-open --port 0`, and reads what `test/e2e/host/probe-plugin.mjs` measured. It asserts the rows `S0_TODAY` records, which P-R1 flipped to the plugin owning both planes.

- **Cells.** `pinned` forces pi, cordis and the loader to `HOST_VERSIONS` with npm `overrides` (the published host declares cordis and the loader as tilde ranges), applies the harness patch to pi with `patch -p1`, asserts all three installed versions (a cached prefix included), then asserts every row. `floating` takes the pi npm resolves for the published host. It skips only the per-model rows (a newer pi's own entry can move a model's metadata); the listings before and after a turn, the save rows (the catalog-unknown id is accepted on the first try) and the row that the plane provider is built from the host prefix's own pi are asserted in both cells, since the plugin owns both planes and none of them depends on which ids pi describes. Its probe JSON is the record.
- **Cells.** `pinned` forces pi, cordis and the loader to `HOST_VERSIONS` with npm `overrides` (the published host declares cordis and the loader as tilde ranges), applies the harness patch to pi with `patch -p1`, asserts all three installed versions (a cached prefix included), then asserts every row. `floating` takes the pi npm resolves for the published host. It skips the per-model rows (a newer pi's own entry can move a model's metadata) and the Settings fetch's order (pi's list first); the listings before and after a turn, the save rows (the catalog-unknown id is accepted on the first try) and the row that the plane provider is built from the host prefix's own pi are asserted in both cells, since the plugin owns both planes and none of them depends on which ids pi describes. Its probe JSON is the record.
- **Settings' fetch.** The probe also records what Settings → Models → Fetch offers on each plane, through the gateway's Remote face. Both cells assert the whole servable catalog, each id once, in the Remote's five fields; the pinned cell also asserts the order, pi's own list first, then the catalog models pi lacks.
- **Triggers.** Dispatch and the nightly run take both cells. A pull request takes `pinned`, and only when it touches the host seam — `src/host/**` (the route claim, the plane adapter, the Settings graft, the host-module loaders) and the modules whose behaviour depends on the real loader and apply order beside it, `src/lifecycle.ts`, `src/identity.ts`, `src/cordis-context.ts` and `src/models-discovery.ts`, and the two modules that still install host-level patches until they move under `src/host/` (`src/fetch-patch.ts`, the global fetch wrapper whose order the probe reads, and `src/stream-hook.ts`) (every other row producer, such as the pure plane model and profile in `src/utils/`, `src/key-capture.ts`, `src/models-catalog.ts` or `src/config.ts`, is covered on each pull request by the in-process characterization of the same `S0_TODAY` rows, and by the nightly run) — `cordis.patch.yml`, `test/e2e/host/**`, `test/e2e/host-binary.e2e.ts`, `test/support/host.ts` (the rows the binary e2e asserts), `test/support/host-versions.ts` or `patches/` (what the job installs and patches), `.github/workflows/ci.yml`, `vitest.e2e.config.ts` or `vite.config.ts` (the job's own steps and runner config; `vite.config.ts` also holds the pack config that builds `lib/index.mjs`, the bundle only this job loads into a real host, and the test settings the e2e config inherits) (scoped by a step: a workflow-level path filter would silence every other job; a diff the step cannot compute fails it rather than skipping the job). `package.json` is left out because every dependency bump touches it, so a change to its `test:e2e:host` script alone is exercised only by the nightly run. It needs no secret.

To run it locally, install the host into a prefix outside the checkout, then point the suite at it (placeholder paths; `pnpm` must be on `PATH`). The first line reads the versions from `test/support/host-versions.ts`, the same way the CI job does, so the snippet never names one itself:
Expand Down
Loading
Loading