Commit f44ed09
authored
* feat(serve): introduce ServeErrorKind and BridgeTimeoutError (#4175 Wave 3 PR 13)
Lay the type foundation for `/workspace/preflight` and `/workspace/env` (and
the eventual MCP guardrails route) so cells emitted by all three share a
closed `errorKind` taxonomy:
- `SERVE_ERROR_KINDS` literal-list + `ServeErrorKind` union — the seven
values from #4175 (`missing_binary`, `blocked_egress`, `auth_env_error`,
`init_timeout`, `protocol_error`, `missing_file`, `parse_error`).
- `BridgeTimeoutError` typed class — `withTimeout` now rejects with this
rather than a plain `Error`, letting `mapDomainErrorToErrorKind` recognize
init / heartbeat / extMethod timeouts via `instanceof` instead of
regex-matching message strings. Message format is preserved bit-for-bit.
- `mapDomainErrorToErrorKind` helper — one place to classify
`BridgeTimeoutError`, `SkillError`, fs ENOENT/EACCES/EPERM, ModelConfigError
subclasses (recognized by `name` field — they aren't on the public surface
of `@qwen-code/qwen-code-core`), `SyntaxError`, plus message-regex fallbacks
for legacy throw sites (`agent channel closed`, missing CLI entry path).
- `ServeStatusCell.errorKind` tightened from open `string` to the closed
`ServeErrorKind` union. Backward compatible — PR 12 never assigned the
field.
- SDK mirrors: `DAEMON_ERROR_KINDS` const + `DaemonErrorKind` type;
`DaemonStatusCell.errorKind` tightened.
Tests: 11 new unit tests in `status.test.ts` covering each mapping rule plus
the BridgeTimeoutError shape.
No route changes; no behavior changes for any existing path.
* feat(serve): add buildEnvStatusFromProcess helper (#4175 Wave 3 PR 13)
Pure helper that constructs the `/workspace/env` payload from `process.*`
state. No I/O, no ACP roundtrip, no globals beyond `process.env`. The route
itself lands in the next commit.
- `ServeEnvKind` discriminant: `runtime | platform | sandbox | proxy | env_var`
- `ServeEnvCell extends ServeStatusCell` with `name` + optional `present` /
`value`. Cells with `kind: 'env_var'` are presence-only — `value` is
ALWAYS omitted to keep secret env vars off the wire even by accident.
- `ServeWorkspaceEnvStatus` envelope: `{ v, workspaceCwd, initialized: true,
acpChannelLive, cells, errors? }`. `initialized` is structurally `true`
because env answers from the daemon process directly; `acpChannelLive`
reports whether a child is up but does not change the payload shape.
Whitelist policy:
- Auth/secret keys (presence-only): OPENAI/ANTHROPIC/GEMINI/GOOGLE/DASHSCOPE/
OPENROUTER `_API_KEY`, `QWEN_SERVER_TOKEN`.
- Non-secret keys (also presence-only for shape uniformity): base URLs, locale,
TZ, NODE_EXTRA_CA_CERTS, QWEN_CLI_ENTRY.
- Proxy vars (`HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY`/`ALL_PROXY` + lowercase
variants): credentials stripped via `redactProxyCredentials`, then
`URL().host` so the wire only carries `host:port`. NO_PROXY is a host list
rather than a URL so we pass the redacted form verbatim.
SDK mirrors: `DaemonEnvKind`, `DaemonEnvCell`, `DaemonWorkspaceEnvStatus`.
Tests: 9 unit tests covering the proxy-credential redaction, lowercase env
fallback, NO_PROXY pass-through, presence-only `env_var` invariant
(`'value' in cell === false`), whitelist enforcement, runtime tag detection,
and envelope shape.
* feat(serve): add GET /workspace/env route (#4175 Wave 3 PR 13)
Wire `buildEnvStatusFromProcess` from the previous commit through the
bridge, server, and SDK so remote clients can pre-flight the daemon's
runtime environment without spawning an ACP child.
- `workspace_env` capability tag (always advertised on a current daemon).
- `bridge.getWorkspaceEnvStatus()` answers entirely from `process.*` —
the route never consults ACP. `acpChannelLive` reflects whether a child
exists but does not change the payload, so an idle daemon and a busy
one return the same env shape.
- `app.get('/workspace/env', ...)` mirrors PR 12's one-liner pattern.
- SDK: `DaemonClient.workspaceEnv()` returning `DaemonWorkspaceEnvStatus`.
- Docs: bullet in `docs/users/qwen-serve.md` calling out the
presence-only redaction policy and the no-ACP-spawn guarantee.
Tests: server-level (env returned + `'value' in env_var === false`
assertion), bridge-level (idle and live both answer locally without
hitting ACP extMethod), SDK-level (recording-fetch round-trip on
`/workspace/env`). The `workspace_env` tag is added to the
`EXPECTED_STAGE1_FEATURES` capability list assertion.
* feat(serve): add /workspace/preflight daemon-cells path (#4175 Wave 3 PR 13)
Wire the preflight route. Daemon-level cells are populated unconditionally
from `process.*` and `node:fs`; ACP-level cells fall back to `not_started`
placeholders when no child is alive so a poll never spawns one.
- `workspace_preflight` capability tag.
- `ServePreflightKind` discriminant (12 values: node_version, cli_entry,
workspace_dir, ripgrep, git, npm — daemon-level — plus auth, mcp_discovery,
skills, providers, tool_registry, egress — ACP-level).
- `ServePreflightCell extends ServeStatusCell` with `locality: 'daemon' | 'acp'`
+ free-form `detail`. `ServeWorkspacePreflightStatus` envelope.
- `createIdleAcpPreflightCells()` factory: emits the six ACP-level cells with
`status: 'not_started'` + a uniform `hint` so the bridge can stitch them in
alongside daemon cells without ever calling ACP.
- `bridge.getWorkspacePreflightStatus()`:
- Daemon cells via `buildDaemonPreflightCells` (Promise.all over Node-version,
CLI-entry resolution mirroring `defaultSpawnChannelFactory`, `fs.stat` on
`boundWorkspace` with ENOENT/EACCES/EPERM mapped to `missing_file`,
best-effort `canUseRipgrep` / `getGitVersion` / `getNpmVersion` warnings).
- ACP cells via `requestWorkspaceStatus` — idle factory returns the
`not_started` placeholders; live path delegates to ACP via the
`qwen/status/workspace/preflight` ext method (handler lands in next
commit). Bridge-side timeout / channel-close while consulting ACP folds
into envelope `errors[]` with `mapDomainErrorToErrorKind` classification;
daemon cells still render.
- `app.get('/workspace/preflight', ...)` route + JSDoc bullet.
- SDK: `DaemonPreflightKind` / `DaemonPreflightCell` / `DaemonWorkspacePreflightStatus`
mirrors; `DaemonClient.workspacePreflight()`.
Tests: server-level (route returns the bridge payload), bridge-level (idle
returns 6 daemon + 6 ACP `not_started` cells without spawning a channel),
SDK-level (`workspacePreflight()` round-trip). Capability test updated.
* feat(serve): wire ACP-side preflight cells (#4175 Wave 3 PR 13)
Populate the six ACP-level preflight cells inside the ACP child so
`/workspace/preflight` returns real values for live sessions.
- `extMethod(qwen/status/workspace/preflight, ...)` dispatches to a new
`buildAcpPreflightCells(config)` private method.
- Five cell builders, each returning a `ServePreflightCell` with
`locality: 'acp'`:
- `auth`: `validateAuthMethod(authType, config)` returning non-null
string → `auth_env_error`. Missing auth method → warning. Throws
classified via `mapDomainErrorToErrorKind` with `auth_env_error`
fallback.
- `mcp_discovery`: rolls up `getMCPDiscoveryState()` + per-server
`getMCPServerStatus(name)` counts. `connecting > 0` or in-progress
discovery → warning + `init_timeout`; `disconnected > 0` post-discovery
→ error + `protocol_error`.
- `skills`: `SkillManager.listSkills()`; SkillError throws are mapped
via the helper (`PARSE_ERROR` → `parse_error`, `FILE_ERROR` →
`missing_file`).
- `providers`: `getAllConfiguredModels()`; empty list with a configured
`authType` → warning + `auth_env_error`. ModelConfigError throws map
to `auth_env_error`.
- `tool_registry`: null registry → error + `protocol_error`. Otherwise
surfaces tool count.
- `egress`: stays `not_started`. PR 14 plugs in the real probe.
- `errorCell` private helper extended with optional `errorKind` parameter;
defaults to `mapDomainErrorToErrorKind(error)` so existing call sites
(`mcp` / `skills` / `providers` envelope errors) automatically gain
classification.
Tests: 2 new acpAgent tests — preflight returns the six expected ACP cells
with correct locality + statuses; preflight surfaces a `SkillError`
(`PARSE_ERROR`) on the `skills` cell as `errorKind: 'parse_error'`. The
core `vi.mock` block adds a SkillError class for `instanceof` matching
inside `mapDomainErrorToErrorKind`.
* docs(serve): preflight and env protocol section (#4175 Wave 3 PR 13)
Document `/workspace/env` and `/workspace/preflight` end-to-end:
- Common-cell shape: tighten `errorKind` from open `string` to the closed
`DaemonErrorKind` enum (seven literals from #4175). Add an explicit
redaction-policy paragraph covering env-var presence-only, proxy
host:port reduction, and the whitelisted-secrets list.
- Capability-tag list: add `workspace_env` and `workspace_preflight`.
- New `### GET /workspace/env` section with sample payload, `DaemonEnvKind`
/ `DaemonEnvCell` types, and the redaction-policy paragraph spelling
out which secret env vars are enumerated and how proxy URLs are
reduced to `host:port`.
- New `### GET /workspace/preflight` section with idle sample payload,
`DaemonPreflightKind` / `DaemonPreflightCell` types, the seven-value
`errorKind` semantics table, and the bridge-error fallback contract
(mid-request ACP channel close → cells drop to `not_started` + envelope
carries one `errors[]` entry).
- Source-layout table: extend the `status.ts` row to mention the new
`ServeErrorKind` / `BridgeTimeoutError` / `mapDomainErrorToErrorKind`
surface; add a new `envSnapshot.ts` row.
1 parent f84ddd4 commit f44ed09
23 files changed
Lines changed: 2577 additions & 127 deletions
File tree
- docs
- developers
- users
- integration-tests/cli
- packages
- cli/src
- acp-integration
- serve
- utils
- sdk-typescript
- src
- daemon
- test/unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
175 | 175 | | |
176 | 176 | | |
177 | 177 | | |
| 178 | + | |
| 179 | + | |
178 | 180 | | |
179 | 181 | | |
180 | 182 | | |
| |||
189 | 191 | | |
190 | 192 | | |
191 | 193 | | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
192 | 203 | | |
193 | 204 | | |
194 | 205 | | |
195 | 206 | | |
196 | | - | |
| 207 | + | |
197 | 208 | | |
198 | 209 | | |
199 | 210 | | |
200 | 211 | | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
201 | 218 | | |
202 | 219 | | |
203 | | - | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
204 | 224 | | |
205 | 225 | | |
206 | 226 | | |
| |||
283 | 303 | | |
284 | 304 | | |
285 | 305 | | |
286 | | - | |
287 | | - | |
288 | | - | |
289 | | - | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
290 | 531 | | |
291 | 532 | | |
292 | 533 | | |
| |||
714 | 955 | | |
715 | 956 | | |
716 | 957 | | |
717 | | - | |
718 | | - | |
719 | | - | |
720 | | - | |
721 | | - | |
722 | | - | |
723 | | - | |
724 | | - | |
725 | | - | |
726 | | - | |
727 | | - | |
728 | | - | |
729 | | - | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
| 964 | + | |
| 965 | + | |
| 966 | + | |
| 967 | + | |
| 968 | + | |
| 969 | + | |
| 970 | + | |
| 971 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
44 | | - | |
45 | | - | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
46 | 73 | | |
47 | 74 | | |
48 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
208 | 208 | | |
209 | 209 | | |
210 | 210 | | |
| 211 | + | |
| 212 | + | |
211 | 213 | | |
212 | 214 | | |
213 | 215 | | |
| |||
0 commit comments