Skip to content

Commit a0c2705

Browse files
authored
feat: workspace file editor, VS Code API-key fix, and English-only docs (#145)
## Related Issue No filed issue. Three problems drove this branch: 1. Every turn on a Codex-signed-in model failed with `Service temporarily unavailable — 400 status code (no body)`. 2. The workspace file editor (added on this branch) never opened, and its reload/save paths were broken. 3. VS Code walled API-key-only users behind a sign-in screen they could not satisfy. ## Problem **Codex turns always failed.** The gateway answers `{"detail":"Unsupported parameter: max_output_tokens"}`. The OpenAI SDK discards that body because it is not an `{"error":{...}}` envelope, so the message degrades to the literal `400 status code (no body)` and nothing in our code ever sees the reason. The completion-budget resolver always produces a value, so the parameter was on every request. The same catalog also advertises an `ultra` reasoning level that `/responses` rejects outright. **The file editor could not open.** Monaco validates every theme color against `/^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/` and *throws* `Illegal value for token color`. Several design tokens (`--color-text-muted`, `--p-selection`) are declared as `rgba()`, so `setTheme` rejected on boot in both light and dark. Behind that: `loadBuffer` disposed the model it then reused (a reload re-enters with the same URI), a reused model never received the re-read text, a save superseded by a reload latched the saving flag true and disabled Save for the session, and save resolved the session at call time so switching sessions wrote the buffer into the wrong workspace. **VS Code sign-in.** The init router reported `not-logged-in` whenever the login check was false even with models configured — and that check is true only for a Pythinker account or a provider OAuth token. A plain API key gives neither, and Skip was component state, so the wall returned on every reload. **Gates could not have caught the editor bugs.** `apps/pythinker-code/dist-web` is generated from `apps/pythinker-web` and read by no typecheck, lint, or test; the check that guarded it only asserted the bundle existed, and nothing invoked it. ## What changed **Provider** - Drop `max_output_tokens` for the Codex gateway, applied once after the request kwargs are assembled so both a configured value and a per-turn budget are covered. - Stop writing the unusable `ultra` reasoning level into the config. **Web UI** - Normalize design tokens to hex before Monaco sees them; a test asserts every palette token in `style.css` matches Monaco's own regex. - Create the editor model once per URI, write pending content into a reused model, attach the change listener after the value is applied so a reload does not land dirty. - Clear the saving flag unconditionally; write back to the session the buffer was opened from. - Stop a settled thinking step animating in the activity header for the rest of the run. **Gateway / desktop / VS Code** - Route-scoped `bodyLimit` on the fs action route (a 10 MiB write is ~13.4 MiB base64-encoded, past Fastify's 1 MiB default), so `FS_TOO_LARGE` is reachable instead of a transport rejection. - Redact the Host bearer token from captured output — it reached both stderr and the pre-readiness failure `Error`. - A configured model now decides `ready` on its own; apply only the newest provider refresh, and invalidate a superseded selection rollback. - Opt-in seeded config profile for the dev extension host. **Build gate** - `copy-web-assets` records a fingerprint of every web build input; `check-web-assets` recomputes it and fails on a stale bundle. Wired into pre-push, the CLI `build`, and `prepack`. `pnpm run build:web` rebuilds and restages in one step. **Docs** - Point the doc skills at paths that exist, drop a bilingual parity check left over from the removed second locale, and remove the Chinese docs tree. ## Checklist - [x] `pnpm test` — 20309 passed, 0 failed (1220 files) - [x] `pnpm run typecheck` — 0 errors (plus both `apps/vscode` configs) - [x] `pnpm run lint` — 0 errors - [x] `node scripts/upstream-sync/check-managed.mjs`, `pnpm run sherif`, `nix build .#pythinker-code` - [x] Every fix mutation-tested: reverting it turns its test red - [x] Changesets added <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added browser-based workspace file editing with conflict-safe saving and file previews. * Expanded web session management, transcript search, subagent details, and execution inspection. * Added automatic model-list refreshes and improved provider/settings management. * Refreshed mobile, desktop, terminal, sidebar, composer, and bundled web interface styling. * **Bug Fixes** * Fixed stale session status, archived-session visibility, thinking animations, failed-turn continuation, editor reloads, and narrow-window layout issues. * Improved Codex compatibility and Windows Git Bash path handling. * **Documentation** * Consolidated documentation in English and updated API and customization guidance. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent cbf7c87 commit a0c2705

452 files changed

Lines changed: 74128 additions & 14389 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/agent-core-dev/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ End-to-end procedures that span the stages. Reach for these before reading the s
2929

3030
- [Align (port `agent-core` → `agent-core-v2`)](align.md): split a v1 class into semantic units, fix each unit's domain / scope / Service / dependencies, then migrate the logic and tests. Use when the task is "move feature X from v1 to v2" or "port `IXxxService` to v2".
3131
- [Commit align (triage a `main` commit against v2)](commit-align.md): given one `main` commit hash + a short note, find the v1 logic it changed, check whether v2 already has the corresponding implementation, bucket it (aligned / partial / missing / not-applicable), and recommend a minimal fix. Use in the `pythinker-code-v2`-catching-up-to-`main` phase, for one commit at a time; escalate to [align.md](align.md) if the gap is a whole domain.
32-
- [Server align (expose `agent-core-v2` over `server-v2`)](server-align.md): wire a v2 domain into `packages/kap-server` over `/api/v2` (native) and `/api/v1` (v1-compatible mirror), keep the wire schema byte-compatible with the established v1 contract by sharing the `@pymodel/protocol` schema, and isolate v1-only behavior in a `<domain>Legacy` edge adapter instead of distorting the native v2 Service. Use when the task is "expose the new v2 Service on the server", "add a route to the `/api/v1` surface", or "keep server-v2 wire-compatible with released v1 clients".
32+
- [Server align (expose `agent-core-v2` over `server-v2`)](server-align.md): wire a v2 domain into `packages/agent-gateway` over `/api/v2` (native) and `/api/v1` (v1-compatible mirror), keep the wire schema byte-compatible with the established v1 contract by sharing the `@pymodel/protocol` schema, and isolate v1-only behavior in a `<domain>Legacy` edge adapter instead of distorting the native v2 Service. Use when the task is "expose the new v2 Service on the server", "add a route to the `/api/v1` surface", or "keep server-v2 wire-compatible with released v1 clients".
3333

3434
## Stages
3535

‎.agents/skills/agent-core-dev/config.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ registerSection(MY_SECTION, MySectionSchema, {
270270
});
271271
```
272272

273-
- A deprecated TOML key is **ignored** (its value no longer applies — the schema only knows the new key) and reports a warning `ConfigDiagnostic` while present; the file is never rewritten, so the warning is the migration guide. Diagnostics are recomputed on every load/reload and surface to clients via `IConfigService.diagnostics()` and `onDidChangeDiagnostics` (kap-server republishes them as the global `event.config.warning` WS event).
273+
- A deprecated TOML key is **ignored** (its value no longer applies — the schema only knows the new key) and reports a warning `ConfigDiagnostic` while present; the file is never rewritten, so the warning is the migration guide. Diagnostics are recomputed on every load/reload and surface to clients via `IConfigService.diagnostics()` and `onDidChangeDiagnostics` (agent-gateway republishes them as the global `event.config.warning` WS event).
274274
- A deprecated env var still **resolves** as a fallback (new var first), with the same warning treatment, and `stripEnvBoundFields` treats it as env-owned for writes.
275275
- See `src/agent/loop/configSection.ts` for a worked example (`max_retries_per_step` → `max_attempts_per_step`).
276276

‎.agents/skills/agent-core-dev/edge-exposure.md‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
How a domain's Services become the wire surface (`/api/v2`) and WebSocket events. This is a **design-time** decision: which Services are exposed, under what public `resource:action` name, and which events stream.
44

5+
> **Implementation note (2026-08):** the current `/api/v2` surface is hand-written route files
6+
> under `packages/agent-gateway/src/routes/v2/`, mounted by `src/routes/registerApiV2Routes.ts`
7+
> (see `packages/agent-gateway/AGENTS.md`). There is no generic `actionMap` dispatcher in
8+
> agent-gateway today; the `resource:action` model and tables below are the original v2 edge
9+
> design. The facade rules (§2, §4), the scope-resolution rule, and the WS-event rules (§5)
10+
> still apply to route-file exposure.
11+
512
The transport (`/api/v2` over HTTP + WS) lives in the **edge** layer (`gateway`/`rpc`/`transport`). It borrows business Services by interface; business code never imports it.
613

714
## 1. The edge model
@@ -45,7 +52,7 @@ A Service method is directly exposable iff **all** hold:
4552
3. Errors are `PythinkerError` (coded).
4653
4. It is a command/query, not a factory, stream, byte-store, or sink.
4754

48-
If any fail → add a wire-safe orchestration method to the owning domain Service (e.g. `IAgentPromptService.submit` settles `{turn_id}` instead of returning the live `PromptHandle`) or compose several domain Services at the edge — kap-server's `routes/prompts.ts` is the reference for edge-side composition.
55+
If any fail → add a wire-safe orchestration method to the owning domain Service (e.g. `IAgentPromptService.submit` settles `{turn_id}` instead of returning the live `PromptHandle`) or compose several domain Services at the edge — agent-gateway's `routes/prompts.ts` is the reference for edge-side composition.
4956

5057
## 3. Per-scope `resource:action` map
5158

@@ -160,7 +167,7 @@ The `eventMap` binds a public event name to the scope's `Event` source (analogou
160167

161168
Session-level `onDidChange` sources (metadata / interactions) carry no payload today, so they are not exposed until there is a concrete consumer.
162169

163-
Safety / reliability (carried over from `packages/server/src/ws/connection.ts` and VSCode's `ChannelServer`):
170+
Safety / reliability (carried over from VSCode's `ChannelServer`, and the legacy v1 server those ideas were ported from):
164171

165172
- request ids + active-request table — `cancel` / `unlisten` disposes them;
166173
- heartbeat — `ping` every 30s, `pong` timeout 10s → `terminate`;

0 commit comments

Comments
 (0)