Skip to content

setup consent, connection details, transient-failure recovery, stream prelude retry, doctor/installer features - #9

Merged
ZepiGit merged 6 commits into
mainfrom
claude/new-session-k0ft6w
Sep 27, 2026
Merged

ZepiGit merged 6 commits into
mainfrom
claude/new-session-k0ft6w

Conversation

@ZepiGit

@ZepiGit ZepiGit commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

Implements the approved product changes 7.1–7.4 (per-harness consent, copyable connection details, manual base-URL/API-key entry, docs), verified fixes in the bundled proxy found during the deep dive, a recovery package for transient upstream failures (the "sending Continue fixes it" report), and nine follow-up features, each reviewed before merge.

Per-harness consent in zcode-kit setup

  • One y/n question per detected harness; only y configures, n never deletes; without a terminal nothing is configured silently. Unattended selection with --harness <list> / ZCODE_KIT_HARNESSES (none, unknown ids are errors).
  • Decisions stored per harness under generated/harness-choices/ through the setup transaction; update / doctor --fix refresh only consented or kit-owned integrations; --reask asks again.
  • New: setup --select shows one numbered list instead (1,3, all, none): chosen = configured, the other detected = skipped. Needs a terminal; ignored next to an explicit selection.
  • MCP bridge needs separate consent (explicit selection or y after the note; --no-mcp is never stored as consent).
  • Ctrl-C exits 130 and applies only the answers given; a failing harness is undone via savepoints and setup exits 20; installers treat 20/130 as installed-with-warning (install.ps1 now also resets $LASTEXITCODE afterwards).

Connection details (7.1 / 7.3)

  • setup, proxy start and proxy status print base URLs, key and model ids of the verified own proxy; the full key only on an interactive terminal.
  • New: proxy status --json / status --json: one object (health, pid, base URLs, routes, model ids and their source, quota while the own proxy runs); never the key.

doctor

  • New: decision-file checks (unreadable = FAIL with the way out; undetected/unknown = SKIP) and doctor --forget <harness> (transactional; integration untouched).
  • New: doctor --upstream (opt-in, at most three unauthenticated GETs): compares the kit's gateway with the provider configuration the ZCode client receives (remote release or legacy provider list; reference client 3.14.3 as fallback). Live-verified: gateways match; start-plan upstream does not offer GLM-5.3 (informational).

Proxy: transient-failure recovery

  • Pre-output ladder on the same account (initial + 3): connect failures, drops before a response, 500/502/503/504/524/529, 429 and the gateway codes the official client retries; account re-checked before every retry; Retry-After up to 15 s; terminal verdicts, captcha challenges, request errors and anything after output are never retried.
  • New: stream prelude retry — a 200 stream that fails before its first content event (transient error event, cut connection) is retried like a failed request; data-only frames classified like the translators; already-inflated bodies read as is; client abort cancels at once; 15 s per attempt, 256 KiB held.
  • New: start-plan takes a fresh captcha token for every retry after a response, a post-write drop or a failed prelude.
  • New: ordered transport parity — a drop after the request was written is re-sent on the same account like on the fetch path; the quota failover never replays it on another account; client aborts are AbortErrors.
  • Native Anthropic streams that end early get one parseable event: error (api_error) instead of a silent truncation.
  • New: account metadata that cannot be written (store lock held elsewhere, transient I/O) is kept and rewritten in the background (0.25/1/4/15 s), with a bounded final write on shutdown; accounts doctor warns runtime_changes_unsaved.

Installers

  • New: install.sh repeat installs no longer need rsync: a portable tar/find mirror copies first (through a file), then removes what the release dropped, never touching the key, user config, Bun path, node_modules, backups, logs or generated.
  • New: Windows CI tests for install.ps1 repeat install (robocopy) and setup exit 20/130/1.

Docs

README, harness guides, proxy README (all in en/de/es/ja/zh-CN), Account Rotator docs (en/de), CLI usage.

Verification

  • npm test: 286 tests, 0 failures, 12 platform skips (locally; Linux and Windows in CI).
  • Proxy suite with Bun 1.4.2: 1282 pass; the one failure (startServer binds … ::1) is pre-existing and caused by the test container lacking IPv6. tsc --noEmit clean.
  • Fresh-profile installer runs (piped, interactive, Ctrl-C) on Linux.
  • Independent reviews of each package (protocol, final code review, documentation counter-check); every finding fixed or refuted in the commits.

…r fixes

Consent (setup):
- one y/n question per detected harness ("Configure ZCode as a provider
  with its supported models in <HARNESS>? [y/n]"); only y configures, n
  skips and never deletes; without a terminal undecided harnesses are
  skipped unless selected with --harness <list> or ZCODE_KIT_HARNESSES
  (none skips all; unknown ids are errors)
- decisions stored per harness under generated/harness-choices/ through
  the setup transaction (rollback removes them); update and doctor --fix
  refresh only consented or kit-owned integrations; --reask asks again
- one prompter for all questions (typed-ahead answers stay in order),
  Ctrl-C aborts with exit 130 and applies only the answers given
- a failing harness no longer takes the others down: its partial writes
  are undone via transaction savepoints, setup exits 1, installers warn
  and continue
- MCP bridge registration follows consent; doctor reports declined
  harnesses as SKIP instead of FAIL

Connection details (manual client setup):
- setup, proxy start (also when already running) and proxy status print
  the real base URLs, the local key and the model ids of the verified own
  proxy; a stopped proxy is labelled as configuration only, a foreign
  listener withholds the details
- the full key is shown only on an interactive terminal (stdin and
  stdout TTY, not CI); logs, pipes, JSON and installer logs get a
  redaction marker; status never creates or rotates the key

Proxy:
- account rotator: non-inference operations no longer move the sticky
  account; the effective identity no longer depends on userId
- ordered transport: socket open is abortable; SNI only for hostnames
- upstream errors: no quota retry after bytes reached the client
- captcha verify header masked in debug logs

Docs: README (+ de/es/ja/zh-CN), harness guides (+ translations), proxy
README intros, CLI usage; tests for consent, connection details,
transaction savepoints and the proxy fixes.
…recisely

Counter-check findings (documentation vs. code): the README sentence
about unattended runs now names the refresh of an integration the kit
created before it asked (no consent recorded, no MCP), the manual entry
paragraph no longer calls the kit's own step "automatic configuration",
setup.mjs no longer claims to wire every detected harness, and one
pre-existing nuance in the Chinese proxy section (start lock is never
taken over, not "kept") is corrected. Same wording in de/es/ja/zh-CN.
…h an error; setup: separate MCP consent, exit code 20

Trigger: errors that disappeared when the user sent a new "Continue"
message. The proxy retried only four connect-level error codes; every
other transient failure ended the harness turn although a fresh request
would have succeeded, and a natively passed-through Anthropic stream
that the gateway cut off simply stopped.

Proxy:
- pre-output transient ladder (shared by chat and /v1/responses): thrown
  connect failures and connection drops before a response (reset, pipe,
  timeout; never postWrite, TLS or abort) and HTTP 500/502/503/504/524/529
  and 429 without a recognised gateway envelope are re-dispatched on the
  same account, initial + 3 retries with growing, abortable waits; a
  numeric Retry-After is honoured up to 15 s and surfaced above it. Gateway
  business envelopes, request/auth/model errors, streams and anything
  after output are never retried; the 1005/1113 schedule and the captcha
  layer keep their own single attempts. ZCODE_PROXY_TRANSIENT_RETRY_UNIT_MS
  sets the base delay (0 = no waits, off = connect-only as before) and is
  passed through by proxy start/restart.
- ordered transport: an upstream close before response headers now
  carries postWrite (it was replayable by the quota failover); nested
  postWrite causes are recognised; the error listener stays attached on
  abort; IPv6 literals get a bare host and no SNI.
- native Anthropic streams that end without message_stop, or whose read
  fails, get one terminal `event: error` frame (upstream_incomplete /
  upstream_stream_error); nothing is replayed, no message_stop invented.
- captcha retry re-checks the client signal before solving and before
  the resend; account list marks the inference account as active even
  after a lookup served by another profile; one shared sensitive-header
  set for debug and dump output.

Kit:
- MCP bridge consent is recorded separately from provider consent: an
  explicit selection, or a y after the MCP note; integrate, a refresh or a
  stored decision without the flag never register the bridge.
- setup exits 20 (not 1) when a harness failed and the others were
  configured; installers and update treat 20 as partial success and 130
  (Ctrl-C) as "finish later", both still start the proxy / the shim.
- setup withholds connection details when a foreign service holds the
  port; the details say when the model list came from configuration;
  [::1] listeners keep their URL.
- transaction backups use a monotonic counter (no name reuse after
  restoreSince); undo and decision recording cannot abort the run;
  integrate warns when a decision cannot be recorded; install.sh proves
  /dev/tty can be opened and gives setup /dev/null without a terminal.

Docs (en + de/es/ja/zh-CN): error modes, retry knob, stream termination,
MCP consent, exit code 20. Tests: transient-retry, sse-terminal, ordered
transport pre-header EOF, nested postWrite, captcha abort, rotator active
marker, MCP consent paths, backup collision, connection-details markers.
@ZepiGit ZepiGit changed the title setup: per-harness consent, copyable connection details, proxy rotator fixes setup: per-harness consent, connection details, transient-failure recovery, proxy rotator fixes Sep 27, 2026
…e stream end; setup: --no-mcp never records MCP consent

Review findings on the second package (independent review and
counter-check), each verified against the code before the change.

Proxy
- transient ladder: a gateway envelope now decides by its code. The
  codes the official client retries (500, 1120, 1230, 1234, 1302, 1303,
  1305, 1312, 2007, 3002) are retried with any HTTP status, terminal
  verdicts (quota, balance, captcha, model, authorization,
  authentication, 1210) never, any other code follows the HTTP status.
  A captcha challenge header is handed to the captcha layer even on a
  transient status (no repeated sends of a spent token). A TLS code
  anywhere in the error chain blocks a retry. Content-type checks are
  case-insensitive. Retry-After accepts an HTTP-date. The account is
  re-checked before every retry, and a refused retry hands back the
  last response intact.
- error mapping keeps a bounded numeric Retry-After on 503 and 529 as
  well as 429, so a delay the ladder surfaced reaches the client.
- native Anthropic streams are forwarded frame by frame: an unfinished
  last frame (the usual shape of a cut connection) is dropped so the
  appended `event: error` frame stays parseable. The frame uses the
  standard `api_error` type and names the cause in its message. A
  gzip/deflate/br-encoded native stream is decoded before the monitor
  and forwarded identity-encoded, so the guarantee also holds for
  clients that accept compression.

Kit
- `setup --harness X --no-mcp` no longer stores MCP consent; a later
  plain setup never registers the declined bridge.
- `integrate` keeps an MCP consent recorded earlier instead of clearing
  it.
- connection details bracket every IPv6 literal, not only ::1.

Docs (en + de/es/ja/zh-CN): retry policy wording (codes, budget vs the
official client, Retry-After statuses, account re-check, ordered
transport), stream termination (api_error, dropped partial frame),
quota failover only when the rotator has another account.

Checked and refuted: CRLF frames were already recognised (`$` with the
m flag matches before CR in Node and Bun); tests now pin it.
…ate; kit: status --json, setup --select, doctor --forget/--upstream, rsync-free update

Proxy
- Stream prelude retry: a 200 event stream is read until its first
  content event. A transient error event (overloaded, api/rate-limit
  error, a retryable gateway code) or an end/read failure in that window
  is retried by the pre-output ladder on the same account, as the
  official client does; a content event hands on the held prelude and the
  untouched rest. Data-only frames are classified by their JSON type like
  the translators; bodies the transport already inflated are read as they
  are; a client abort cancels the upstream at once; bounds 15 s per
  attempt and 256 KiB held. Gateway codes shared in gateway-codes.ts.
- Start-plan: a retry after a response, a post-write drop or a failed
  prelude takes a fresh pooled captcha token; a never-connected attempt
  keeps it; a mint failure keeps the previous one.
- Ordered transport parity: a failure after the request was written and
  before any response is a drop, re-sent on the same account like a reset
  on the fetch path; the quota failover still never replays it on another
  account; a client abort is an AbortError and never retried.
- Account metadata persistence: a failed write (store lock held by
  another process, transient I/O) stays unsaved and is rewritten in the
  background after 0.25/1/4/15 s instead of being dropped; each new change
  restarts the schedule, a busy 3-pass cap continues in the background,
  SIGINT/SIGTERM and the memory restart make one bounded final write;
  `accounts doctor` warns runtime_changes_unsaved.

Kit
- `proxy status --json` / `status --json`: one object (health, pid, base
  URLs, routes, model ids and their source, quota); never the key.
- `setup --select`: one numbered list of detected harnesses (numbers,
  all, none); chosen = configured, the other detected = skipped; MCP only
  after the note and without --no-mcp; ignored next to an explicit
  selection or without a terminal.
- doctor: unreadable decision files FAIL with the way out, decisions for
  undetected or unknown harnesses SKIP; `doctor --forget <harness>`
  removes one decision through a transaction (integration untouched).
- `doctor --upstream` (opt-in): compares the kit's gateway with the
  provider configuration the ZCode client receives (remote release or
  legacy provider list; reference client 3.14.3 when the configured
  version gets no plan data); no credentials, no redirects, one 8 s budget.
- install.sh: repeat installs no longer need rsync; a portable tar/find
  mirror copies first, then removes what the release dropped, never
  touching the key, user config, Bun path, node_modules, backups, logs or
  generated (at any depth).

Tests: stream-prelude, persistence retry, prelude/captcha integration,
abort during the prelude, status --json, --select (pty), --forget with
rollback, doctor --upstream (both shapes, boundaries, drift against the
proxy constants), repeat install (portable and default), and Windows CI
tests for install.ps1 repeat install and setup exit 20/130/1.
Docs (en + de/es/ja/zh-CN).

Reviews before this commit: protocol review of the proxy part (double
decompression in translation mode, data-only frames, shutdown write,
schedule re-arm, byte bound — fixed) and a test-matrix review of the kit
part (one time budget for both upstream requests — fixed).
… safety, prelude edge cases)

CI (first run on Linux and Windows):
- install.ps1 left $LASTEXITCODE at setup's 20/130 after reporting the
  installation as complete; callers checking it saw a failure. It is now
  reset to 0 once the installation succeeded.
- Installer tests: the rsync fixture now has older installed files
  (rsync's size+mtime quick check skipped same-second, same-size files);
  the robocopy fixture ships proxy/ like a real release (/MIR purges a
  directory the release no longer ships, protected files inside included).

Final review:
- install.sh portable mirror: the archive goes through a file, so a
  failed or partial copy step stops before any delete (no pipefail in sh).
- Memory restart: close() is bounded to 6 s so the 3 s metadata write
  still runs inside the 10 s exit cap.
- Prelude gate: a body that cannot be decoded is handed on instead of
  retried; frames with mixed line endings split exactly like the frame-end
  scanner; the ladder stops at once when the client left during the prelude.
- doctor --upstream: the response size cap applies while reading; the
  reference-version fallback runs only for covered plans; at most three
  requests (docs corrected in all languages).
- doctor --forget: the setup lock is released even if the transaction
  cannot start; setup --select answers are trimmed.

Documentation counter-check: --select needs a terminal and yields to an
explicit selection; status --json carries quota only while the kit's own
proxy runs (exit 0 only then) — README in all five languages, CLI usage.
@ZepiGit ZepiGit changed the title setup: per-harness consent, connection details, transient-failure recovery, proxy rotator fixes setup consent, connection details, transient-failure recovery, stream prelude retry, doctor/installer features Sep 27, 2026
@ZepiGit
ZepiGit marked this pull request as ready for review September 27, 2026 18:07
@ZepiGit
ZepiGit merged commit ef8c921 into main Sep 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant