Skip to content

QVAC-18183 feat[bc]: CLI cancel bridge + cancelHandler retirement#2074

Merged
simon-iribarren merged 10 commits into
tetherto:mainfrom
simon-iribarren:feat/qvac-18183-cli-cancel-bridge-cancelhandler-retirement
May 15, 2026
Merged

QVAC-18183 feat[bc]: CLI cancel bridge + cancelHandler retirement#2074
simon-iribarren merged 10 commits into
tetherto:mainfrom
simon-iribarren:feat/qvac-18183-cli-cancel-bridge-cancelhandler-retirement

Conversation

@simon-iribarren

@simon-iribarren simon-iribarren commented May 15, 2026

Copy link
Copy Markdown
Contributor

🎯 What problem does this PR solve?

  • This is the final sub-PR for QVAC-18183 and the release gate for SDK 0.11.0. The earlier QVAC-18183 sub-PRs put every cancellable handler onto the new RequestRegistry; this PR closes three remaining gaps so the new cancel surface is actually usable end-to-end — and retires three pieces of pre-registry baggage on the way.
  • CLI cancel bridge. The Workbench Stop button (and every req.aborted over qvac serve) was non-functional: the SDK exposed cancel({ requestId }) but no route handler bound it to req.on('close'). Long inference jobs ran to completion even after the client disconnected, wasting GPU and blocking concurrent requests behind cancel-policy gates.
  • cancelHandler.ts 5-arm dispatcher. Pre-registry baggage. The handler still narrowed on operation: "inference" | "embedding" | "transcription" | "translation" | "downloadAsset" and called into per-kind shim code. With every kind now on the registry, that switch is dead weight and an explicit "do not extend" sign for future contributors.
  • Legacy compat-fallback in server/bare/ops/cancel.ts. Lines 49-71 still called addon.cancel() directly for "non-migrated kinds" — but there are no non-migrated kinds as of the prior sub-PR. Dead code.
  • Cross-RPC typed-error instanceof was lying. Server threw RequestRejectedByPolicyError; client received a generic RPCError with the right message but the wrong prototype. Documented in error-handling.mdc as if it worked; it didn't.

📝 How does it solve it?

  • CLI cancel bridge (packages/cli/src/serve/core/cancel-bridge.ts, new). Single bindClientDisconnectCancel(req, res, requestId, logger) helper attaches req.once('close', ...), short-circuits if res.writableEnded (so a normal completion is never a spurious cancel), calls sdkCancel({ requestId }), and swallows the rejection. Wired into all four OpenAI routes: chat, embeddings, transcriptions, translations.
  • Decorated-promise requestId (packages/cli/src/serve/core/sdk.ts). The CLI-internal sdkCompletion / sdkEmbed / sdkTranscribe wrappers expose the requestId synchronously on the returned promise, riding the SDK-side decoration that already existed on embed / transcribe and was extended in the earlier QVAC-18183 sub-PRs to loadModel / downloadAsset. The route handler can therefore bind the disconnect listener on the same tick as the dispatch.
  • Decorated-promise requestId for cancellable RAG ops (packages/sdk/client/api/rag.ts). ragIngest, ragSaveEmbeddings, and ragReindex — the three RAG operations that register with RequestRegistry server-side (per beginRagContext at server/rpc/handlers/rag.ts:152,175,216) — now generate a client-side requestId, thread it onto the wire envelope (the wire schema already supported the optional field), and decorate the returned promise. This closes the migration story for the removed cancel({operation:"rag", workspace?}) arm: callers hold onto op.requestId from ragIngest(...) etc. and call cancel({ requestId }). The non-cancellable RAG ops (ragChunk, ragSearch, ragDeleteEmbeddings, ragListWorkspaces, ragCloseWorkspace, ragDeleteWorkspace) intentionally stay non-decorated — they don't register with the registry, so a requestId would point at nothing.
  • cancelHandler.ts shrunk to 2-arm pass-through (packages/sdk/server/rpc/handlers/cancelHandler.ts). Option A.2 (shrink) rather than A.1 (delete) because the handler-registry shape requires a function per RPC method; the file now does only schema dispatch into getRequestRegistry().cancel({ requestId }) or cancelByModelId({ modelId, kind? }). ~80 LOC removed.
  • Legacy compat-fallback removed (packages/sdk/server/bare/ops/cancel.ts). Lines 49-71 on the parent commit (addon.cancel() direct-call) deleted; broad-cancel is now a single RequestRegistry walk.
  • Schema collapse (packages/sdk/schemas/cancel.ts). Wire envelope narrows from a 5-arm discriminated union to a 2-arm union ({ operation: "request", requestId }, { operation: "broad", modelId, kind? }). The per-kind public-API sugars for "inference" and "embeddings" are preserved via thin transforms in client/api/cancel.ts:normalizeCancelParams; the two shapes that can't be mechanically back-mapped (downloadAsset keyed off downloadKey, rag had no key at all) are removed, which is the source of the [bc] tag below.
  • Typed-error reconstruction (packages/sdk/client/rpc/rpc-error.ts, rpc-client.ts, schemas/error.ts, utils/errors-server.ts, index.ts). errorResponseSchema carries an optional typedFields: Record<string, unknown>. QvacErrorBase subclasses that need to survive RPC implement toErrorResponseFields(); the client reconstructs them via a keyed map in rpc-error.ts. Registered today: RequestIdConflictError, RequestNotFoundError, RequestRejectedByPolicyError — all re-exported from @qvac/sdk so err instanceof RequestRejectedByPolicyError actually narrows. The map is keyed by response.name, which is the SCREAMING_SNAKE error-code name (REQUEST_REJECTED_BY_POLICY), not the JS class name — JSDoc on the map spells out the maintenance contract so the next class added doesn't trip the same wire.
  • Docs. request-lifecycle-system.mdc no longer claims a fallback to addon.cancel() for non-migrated kinds (this PR closes that gap). error-handling.mdc documents the reconstructor maintenance contract. request-lifecycle-primitives.mdc aligns with the new handler shape.
  • Milestone-ID scrub (separate commit on this PR). Removes private planning-artifact identifiers (M3a/M3b/M3c/M3d/M1-era/M2-era) from Cursor rules + JSDoc comments + test docstrings — 22 hits across 13 files, comment-only (no behaviour change). Those names live in tasks/release-0.11.0-planning/ and have no meaning to anyone reading the qvac monorepo six months from now; replacements use the public release version (0.11.0), the word legacy, or describe the current state of the system in plain English. bun lint + typecheck clean.

🧪 How was it tested?

  • New unit tests (18 cases total):
    • packages/cli/test/cancel-bridge.test.ts — 4 cases: happy path, res.writableEnded short-circuit, swallowed sdkCancel rejections, once semantics (no double-fire on a second close).
    • packages/sdk/test/unit/error-typed-fields.test.ts — 6 cases: typedFields population for each registered class, proper omission for non-registered (ModelNotLoadedError) and client-constructed (InferenceCancelledError) errors, plain Error envelope.
    • packages/sdk/test/unit/rpc-error-reconstruct.test.ts — 7 cases: round-trip reconstruction for all three registered classes, fallback to RPCError on unknown names, plain-Error fallthrough, defensive defaulting on missing typedFields, remote stack/timestamp attachment.
    • packages/sdk/test/unit/request-id-wire.test.ts — added 1 new case for ragSaveEmbeddings envelope requestId forwarding, matching the existing ragIngest / ragReindex coverage. (Client-side decoration is signature-guaranteed by TypeScript — every caller reading op.requestId synchronously is typecheck-asserted; the schema test pins the wire envelope.)
  • Existing unit suites green:
    • packages/cli: 284 tests pass (excluding the pre-existing test/verify-bundle.test.ts which is blocked by the Cannot find module 'semver' typecheck failure in src/verify/bundle/abi.ts — that error lives on upstream/main (53eec2f83) and is unaffected by this PR).
    • packages/sdk: 10 unit tests pass; bun run lint (eslint + tsc) clean.
  • Behavioural smoke (manual): brought up qvac serve against a llama.cpp model, fired a long completion via curl, killed the curl mid-stream, confirmed the worker stopped decoding within one decode tick (logged cancelled requestId=... and the RequestRegistry slot freed). Confirmed a normal completion does not trigger sdkCancel (the res.writableEnded short-circuit).

💥 Breaking Changes

Two public-API cancel(...) call shapes are removed from @qvac/sdk in 0.11.0. They never carried a requestId (and therefore can't be mechanically back-mapped onto the new wire envelope) — callers must migrate to the requestId-targeted cancel path (for downloadAsset) or the broad-cancel-by-modelId escape hatch (for rag).

1. cancel({ operation: "downloadAsset", downloadKey, clearCache }) removed.

The replacement is the requestId exposed synchronously on the decorated promise returned by downloadAsset(...). The clearCache flag is honoured on the requestId path.

BEFORE:

import { downloadAsset, cancel } from "@qvac/sdk";

const op = downloadAsset({ assetSrc, onProgress });
// ...some time later, user clicks Cancel:
await cancel({ operation: "downloadAsset", downloadKey: assetSrc.key, clearCache: true });

AFTER:

import { downloadAsset, cancel } from "@qvac/sdk";

const op = downloadAsset({ assetSrc, onProgress });
// `op.requestId` is available synchronously on the decorated promise:
await cancel({ requestId: op.requestId, clearCache: true });

2. cancel({ operation: "rag", workspace? }) removed.

The three cancellable RAG operations (ragIngest, ragSaveEmbeddings, ragReindex) now return decorated promises in 0.11.0 — the requestId is exposed synchronously on the returned handle so callers can target a specific in-flight RAG op with cancel({ requestId }). The remaining RAG client APIs (ragChunk, ragSearch, ragDeleteEmbeddings, workspace lifecycle) intentionally do not decorate — they're fast-path operations that don't register with the server-side request registry. For "cancel everything RAG" sweeps without a requestId to hand, use the broad-cancel-by-modelId escape hatch.

BEFORE:

import { ragIngest, cancel } from "@qvac/sdk";

ragIngest({ workspace: "my-workspace", documents });
// later:
await cancel({ operation: "rag", workspace: "my-workspace" });

AFTER: — by requestId (primary path)

import { ragIngest, cancel } from "@qvac/sdk";

const op = ragIngest({ workspace: "my-workspace", documents });
// `op.requestId` is available synchronously on the decorated promise:
await cancel({ requestId: op.requestId });

AFTER: — broad cancel (escape hatch, no requestId to hand)

import { cancel } from "@qvac/sdk";

// Cancel every in-flight RAG operation running on the embedding model:
await cancel({ modelId: ragEmbeddingModelId, kind: "rag" });

Preserved (NOT breaking) — every other call shape still works. normalizeCancelParams translates the two most common legacy sugars to the new wire envelope at the client boundary:

import { cancel } from "@qvac/sdk";

await cancel({ operation: "inference", modelId: "model-123" });   // -> {operation:"broad",modelId,kind:"completion"}
await cancel({ operation: "embeddings", modelId: "model-123" });  // -> {operation:"broad",modelId,kind:"embeddings"}
await cancel({ modelId: "model-123" });                            // new sugar (broad)
await cancel({ modelId: "model-123", kind: "completion" });        // new sugar (broad)
await cancel({ requestId: "rid-1" });                              // primary path

🔌 API Changes

1. Decorated-promise requestId on every cancellable long-running SDK call — exposed synchronously on the returned promise so the caller can bind cancellation listeners on the same tick as the dispatch. The pattern was introduced in earlier sub-PRs for embed / transcribe and extended in the prior QVAC-18183 sub-PRs to loadModel / downloadAsset. This PR extends it once more to the three cancellable RAG operations (ragIngest, ragSaveEmbeddings, ragReindex) and matches the CompletionRun.requestId field that completion(...) has exposed since the first QVAC-18183 sub-PR landed:

import {
  completion,
  loadModel,
  embed,
  transcribe,
  downloadAsset,
  ragIngest,
  ragSaveEmbeddings,
  ragReindex,
  cancel,
} from "@qvac/sdk";

const run = completion({ /* ... */ });
console.log(run.requestId);              // CompletionRun.requestId (sync)
const final = await run.final;

const handle = embed({ modelId, text: "hello" });
console.log(handle.requestId);           // decorated Promise<...> & { requestId } (sync)
const { embedding } = await handle;

const op = downloadAsset({ assetSrc, onProgress });
abortController.signal.addEventListener("abort", () => {
  void cancel({ requestId: op.requestId });
});
await op;

const ingest = ragIngest({ workspace: "my-workspace", documents });
console.log(ingest.requestId);           // new in 0.11.0: rag ops decorate too
// later, from a Stop button handler:
void cancel({ requestId: ingest.requestId });
await ingest;

The non-cancellable RAG ops (ragChunk, ragSearch, ragDeleteEmbeddings, ragListWorkspaces, ragCloseWorkspace, ragDeleteWorkspace) intentionally do not decorate — they're fast-path operations that don't register with the server-side request registry, so there's no in-flight context for a requestId to point at.

2. Typed-error instanceof across the RPC boundary. Three server-thrown classes are re-exported from @qvac/sdk and now actually round-trip with their constructor fields preserved:

import {
  completion,
  RequestRejectedByPolicyError,
  RequestIdConflictError,
  RequestNotFoundError,
} from "@qvac/sdk";

try {
  const run = completion({ /* ... */ });
  await run.final;
} catch (err) {
  if (err instanceof RequestRejectedByPolicyError) {
    // err.requestId, err.kind, err.modelId, err.reason are all populated
    // from the server-side throw — instanceof narrows and typed fields
    // survive the RPC envelope.
    console.warn(`policy rejection on ${err.modelId}: ${err.reason}`);
    return;
  }
  throw err;
}

3. New broad-cancel sugar cancel({ modelId, kind? }). Replaces the per-kind dispatcher arms with a single explicit broad-cancel shape:

import { cancel } from "@qvac/sdk";

// Cancel every completion currently running on a specific model:
await cancel({ modelId: "llama-3.2-1b", kind: "completion" });

// Cancel everything currently running on a specific model (model-unload path):
await cancel({ modelId: "llama-3.2-1b" });

@simon-iribarren simon-iribarren requested review from a team as code owners May 15, 2026 09:14
@simon-iribarren simon-iribarren force-pushed the feat/qvac-18183-cli-cancel-bridge-cancelhandler-retirement branch from 7cd08bc to c9f5555 Compare May 15, 2026 09:25
@simon-iribarren simon-iribarren changed the title QVAC-18183 feat[api]: CLI cancel bridge + cancelHandler retirement QVAC-18183 feat[bc]: CLI cancel bridge + cancelHandler retirement May 15, 2026
@simon-iribarren simon-iribarren force-pushed the feat/qvac-18183-cli-cancel-bridge-cancelhandler-retirement branch from c9f5555 to abacedd Compare May 15, 2026 09:32
M3d — the final sub-PR for QVAC-18183 and the release gate for SDK
0.11.0. Six deliverables (plus a late seventh — decorated-promise
RAG client APIs — added when consumer-surface audit surfaced the
gap), all coupled to the new request-lifecycle system landed in
M3a–M3c.

1. CLI cancel bridge. Every OpenAI-compatible route in `qvac serve`
   (chat, embeddings, transcriptions, translations) now binds
   `req.on('close')` to `sdkCancel({ requestId })` via a single shared
   helper `core/cancel-bridge.ts`. The `requestId` is exposed
   synchronously from `sdkCompletion` / `sdkEmbed` / `sdkTranscribe`
   on the returned decorated promise — the SDK guarantees it's
   available before the inference promise settles. The Workbench
   Stop button now disconnects an HTTP request and the worker
   actually stops decoding instead of silently running to completion.

2. cancelHandler.ts retirement. The 5-arm dispatcher
   (`inference` / `embedding` / `transcription` / `translation` /
   `downloadAsset`) is gone. The handler is now a 2-arm pass-through
   that delegates to `RequestRegistry.cancel({ requestId })` or
   `cancelByModelId({ modelId, kind? })`. Option A.2 (shrink to
   pass-through) was chosen over A.1 (delete entirely) because the
   handler-registry shape requires a handler function per RPC method;
   the file now exists only to bridge the wire schema into the
   registry call.

3. M1 compat-fallback removed. The pre-registry `addon.cancel()`
   fallback in `server/bare/ops/cancel.ts` (lines 49-71 on the parent
   commit) is gone. Every cancellable handler is on the registry as
   of M3a–M3c; broad cancel is a single registry walk.

4. Schema collapse (BREAKING). The wire envelope for `cancel`
   narrows from a 5-arm discriminated union to a 2-arm union
   (`{ operation: "request", requestId }` and
   `{ operation: "broad", modelId, kind? }`). The per-kind public-API
   sugars for `"inference"` and `"embeddings"` are preserved via
   thin transforms in `client/api/cancel.ts:normalizeCancelParams`,
   so the two most common legacy call shapes keep working unchanged.
   The two shapes that *can't* be mechanically back-mapped are removed:
     - `cancel({ operation: "downloadAsset", downloadKey, clearCache })`
       — REMOVED. The old envelope keyed off `downloadKey`, the new
       path keys off `requestId`. Migration: hold onto the
       `requestId` exposed on the decorated promise returned by
       `downloadAsset(...)` and call `cancel({ requestId, clearCache })`.
     - `cancel({ operation: "rag", workspace? })` — REMOVED. The old
       envelope had no `requestId`; in M3d the three cancellable RAG
       client APIs (`ragIngest`, `ragSaveEmbeddings`, `ragReindex`)
       now return decorated promises so callers can migrate to
       `cancel({ requestId: op.requestId })`. The non-cancellable
       RAG ops (`ragChunk`, `ragSearch`, etc.) intentionally do not
       decorate — they don't register with the request registry
       server-side.
   Known affected consumer:
     - qvac-app-workbench-desktop/workbench-worker/services/
       provider-service.ts:91 constructs `{operation:"downloadAsset"}`.
     - qvac-app-workbench-desktop/workbench-worker/handlers/
       rag-handler.ts:169 constructs `{operation:"rag"}`.
   These need a coordinated migration before workbench-desktop bumps
   to SDK 0.11.0; tracking as a follow-up Asana task.

5. Typed-error reconstruction. The cross-RPC `instanceof` story is
   now honest: `errorResponseSchema` carries an optional
   `typedFields` envelope, `QvacErrorBase` subclasses that need to
   survive RPC implement `toErrorResponseFields()`, and the client
   reconstructs them via a keyed map in `client/rpc/rpc-error.ts`.
   Registered today: `RequestIdConflictError`, `RequestNotFoundError`,
   `RequestRejectedByPolicyError` — all re-exported from
   `@qvac/sdk` so consumers can `instanceof` them. Bring-up bug
   caught in test: `QvacErrorBase.name` is the SCREAMING_SNAKE
   error-code name (e.g. `REQUEST_REJECTED_BY_POLICY`), not the JS
   class name; the reconstructor map keys off that exact value and
   the maintenance contract in the JSDoc spells this out so the next
   class added doesn't trip the same wire.

6. Docs. `request-lifecycle-system.mdc` no longer claims a fallback
   to `addon.cancel()` for non-migrated kinds (M3d closes that gap).
   `error-handling.mdc` documents the reconstructor maintenance
   contract. `request-lifecycle-primitives.mdc` aligns with the new
   handler shape. CLI serve workspace rule documents the cancel
   bridge.

7. Decorated-promise RAG (late add). Three RAG client APIs
   (`ragIngest`, `ragSaveEmbeddings`, `ragReindex`) now generate a
   client-side `requestId`, thread it onto the wire envelope, and
   return `Promise<...> & { requestId: string }`. The wire schema
   already supported the optional `requestId` field (added in M3b/M3c);
   this PR closes the client-side wiring gap so the migration story
   for the removed `cancel({operation:"rag"})` arm is honest —
   callers actually have a `requestId` to hand to `cancel(...)`. The
   other six RAG client APIs (chunk/search/delete/list/close/
   deleteWorkspace) intentionally stay non-decorated because their
   handlers don't call `beginRagContext` — they're fast-path
   operations not subject to mid-flight cancellation.

Tests:
- `packages/cli/test/cancel-bridge.test.ts` — 4 cases covering happy
  path, `res.writableEnded` short-circuit, swallowed rejections, and
  `once` semantics.
- `packages/sdk/test/unit/error-typed-fields.test.ts` — 6 cases
  covering `typedFields` population for each registered class and
  proper omission for non-registered / client-constructed errors.
- `packages/sdk/test/unit/rpc-error-reconstruct.test.ts` — 7 cases
  covering round-trip reconstruction, fallback to `RPCError` on
  unknown names, plain-Error fallthrough, defensive defaulting on
  missing `typedFields`, and remote stack/timestamp attachment.
- `packages/sdk/test/unit/request-id-wire.test.ts` — extended with
  1 new case for `ragSaveEmbeddings` envelope `requestId` forwarding,
  matching the existing `ragIngest` / `ragReindex` coverage. Client-
  side decoration is signature-guaranteed by TypeScript; the schema
  test pins the wire envelope.

Net: 284 CLI unit tests pass, 10 SDK unit tests pass, plus the 18
new M3d cases. Lint + typecheck clean. The pre-existing `semver`
typecheck error in `packages/cli/src/verify/bundle/abi.ts` lives on
`upstream/main` and is not introduced or affected by this PR.

Tag justification: `[bc]` not `[api]`. Two public-API `cancel(...)`
call shapes (`downloadAsset`, `rag`) are removed because the old
wire envelopes had no `requestId` to mechanically back-map. The
RAG migration is supported by deliverable 7 (decorated promises on
`ragIngest` / `ragSaveEmbeddings` / `ragReindex`); the `downloadAsset`
migration was already supported via the decorated `downloadAsset(...)`
promise shipped in M3b/M3c. Per the SDK content-tag rules `[api]`
and `[bc]` don't combine; the title carries `[bc]` and the PR body
also documents the additive `[api]`-shaped surfaces (decorated-
promise `requestId`, typed-error reconstruction, new broad-cancel
sugar) under their own section.
@simon-iribarren simon-iribarren force-pushed the feat/qvac-18183-cli-cancel-bridge-cancelhandler-retirement branch from 064bdec to 0279a56 Compare May 15, 2026 09:50
simon-iribarren added a commit to simon-iribarren/qvac that referenced this pull request May 15, 2026
The M3a/M3b/M3c/M3d milestone identifiers live in private planning
artifacts under `tasks/release-0.11.0-planning/` and have no meaning
to anyone reading the qvac monorepo. The previous M3d PR leaked
them into Cursor rules and JSDoc comments that future contributors
would read with zero context for what those mean.

Self-review on PR tetherto#2074 surfaced 13 hits across 9 files. All
rewrites either drop the milestone tag entirely or rephrase to
reference the public release version (0.11.0) or describe the
current state of the system in plain English ("legacy" / "as of"
/ "current"). No behaviour changes; lint + typecheck clean.

Doc hits (6):
- .cursor/rules/sdk/docs/request-lifecycle-system.mdc lines 77, 195, 196
- .cursor/rules/sdk/request-lifecycle-primitives.mdc lines 19, 247, 374

Source hits (7):
- packages/sdk/client/rpc/rpc-error.ts: "bug fixed in M3d alongside ..." -> "bug fixed alongside ..."
- packages/sdk/schemas/cancel.ts: "M2-era per-kind discriminator arms" / "M3d release-gate cleanup" -> "legacy per-kind discriminator arms" / "0.11.0 cleanup"
- packages/sdk/server/bare/ops/cancel.ts: "M1-era pre-registry addon-cancel fallback was removed in M3d" -> "legacy pre-registry addon-cancel fallback was removed in 0.11.0"
- packages/sdk/server/rpc/handler-registry.ts: "After the M3d wire-schema collapse" -> "After the 0.11.0 wire-schema collapse"
- packages/sdk/server/rpc/handlers/cancel-delegated.ts: "After M3d the cancel envelope ..." -> "After the 0.11.0 wire-schema collapse the cancel envelope ..."
- packages/sdk/server/rpc/handlers/cancelHandler.ts: "retired in 0.11.0 (M3d)" -> "retired in 0.11.0"
- packages/sdk/server/rpc/handlers/load-model/download-manager.ts: "Added in M3d to support ..." -> "Added in 0.11.0 to support ..."

Pre-existing M3a leaks in three test files (`request-registry.test.ts`,
`request-lifecycle-logging.test.ts`, `with-request-context.test.ts`) and
M2 references in `kv-cache-system.mdc` are out of scope for this PR —
they predate the M3d branch and belong to a separate scrub.
The M3a/M3b/M3c/M3d/M1-era/M2-era milestone identifiers live in
private planning artifacts under `tasks/release-0.11.0-planning/`
and have no meaning to anyone reading the qvac monorepo. The
previous M3d PR leaked them into Cursor rules, JSDoc comments, and
test docstrings that future contributors would read with zero
context for what those mean.

Self-review on PR tetherto#2074 surfaced 22 hits across 13 files. All
rewrites either drop the milestone tag entirely or rephrase to
reference the public release version (0.11.0) or describe the
current state of the system in plain English ("legacy" / "as of"
/ "current"). No behaviour changes; lint + typecheck clean.

Doc hits (11):
- .cursor/rules/sdk/docs/request-lifecycle-system.mdc lines 77, 195, 196
- .cursor/rules/sdk/request-lifecycle-primitives.mdc lines 19, 247, 374
- .cursor/rules/sdk/docs/kv-cache-system.mdc lines 91, 96, 102, 104, 112

Source hits (7):
- packages/sdk/client/rpc/rpc-error.ts: "bug fixed in M3d alongside ..." -> "bug fixed alongside ..."
- packages/sdk/schemas/cancel.ts: "M2-era per-kind discriminator arms" / "M3d release-gate cleanup" -> "legacy per-kind discriminator arms" / "0.11.0 cleanup"
- packages/sdk/server/bare/ops/cancel.ts: "M1-era pre-registry addon-cancel fallback was removed in M3d" -> "legacy pre-registry addon-cancel fallback was removed in 0.11.0"
- packages/sdk/server/rpc/handler-registry.ts: "After the M3d wire-schema collapse" -> "After the 0.11.0 wire-schema collapse"
- packages/sdk/server/rpc/handlers/cancel-delegated.ts: "After M3d the cancel envelope ..." -> "After the 0.11.0 wire-schema collapse the cancel envelope ..."
- packages/sdk/server/rpc/handlers/cancelHandler.ts: "retired in 0.11.0 (M3d)" -> "retired in 0.11.0"
- packages/sdk/server/rpc/handlers/load-model/download-manager.ts: "Added in M3d to support ..." -> "Added in 0.11.0 to support ..."

Test docstring hits (4):
- packages/sdk/test/unit/runtime/request-registry.test.ts: "Option A in the M3a brief" parenthetical dropped; "pre-M3a behaviour" -> "documented behaviour"
- packages/sdk/test/unit/runtime/request-lifecycle-logging.test.ts: "part of the M3a contract" -> "part of the 0.11.0 request-lifecycle contract"
- packages/sdk/test/unit/runtime/with-request-context.test.ts: "Covers the M3a brief's Deliverable 3 acceptance criteria" -> "Covers the acceptance criteria"
@simon-iribarren simon-iribarren force-pushed the feat/qvac-18183-cli-cancel-bridge-cancelhandler-retirement branch from 993f657 to 7399fe2 Compare May 15, 2026 10:53
@lauripiisang

Copy link
Copy Markdown
Contributor

got type errors on trying to run cancel download test on local desktop via /pr-test skill:

Log [producer] ▶️ Test download-cancel-isolation started by consumer-desktop-Lauris-MacBook-Pro.local-1778843688597 [producer] 🚀 Batch orchestration started [producer] 📋 Total tests: 0 [producer] ⏳ Waiting for consumers to register (timeout: 30s)... [producer] [consumer-desktop] [sdk:server] 🐻 Hello from Bare [consumer-desktop] [sdk:server] Parsed RPC configuration from arguments [consumer-desktop] [sdk:server] Running in desktop mode, connecting to IPC socket: /var/folders/m6/tt9tc20n0rlc9q82bq9416_r0000gn/T/qvac-worker-35166-mp6tlqk4-0577.sock [consumer-desktop] [sdk:server] Connecting to IPC socket at /var/folders/m6/tt9tc20n0rlc9q82bq9416_r0000gn/T/qvac-worker-35166-mp6tlqk4-0577.sock [consumer-desktop] [sdk:server] Bare worker started and listening for RPC requests [consumer-desktop] [sdk:client] ✅ Loaded config from: /Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/fixtures/qvac.config.e2e.json [consumer-desktop] [sdk:client] 📦 Initializing SDK config [consumer-desktop] [sdk:client] 📱 Runtime context: { runtime: 'node', platform: 'darwin' } [consumer-desktop] [sdk:server] Connected to IPC server [consumer-desktop] [sdk:server] ✅ Registry download max retries set to: 5 [consumer-desktop] [sdk:server] ✅ Registry stream timeout set to: 120000ms [consumer-desktop] [sdk:client] ✅ Initialization complete [consumer-desktop] [sdk:server] [request-lifecycle] begin requestId=97726ce8-608b-49fb-a4ee-52fc76413133 kind=downloadAsset modelId=- state=running [consumer-desktop] [sdk:server] Loading from registry: ggerganov/whisper.cpp/resolve/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin [consumer-desktop] [sdk:server] [request-lifecycle] begin requestId=cb38ec25-486a-4fa5-b4ee-e68e7fcd222f kind=downloadAsset modelId=- state=running [consumer-desktop] [sdk:server] Loading from registry: qvac_models_compiled/ocr/2026-02-12/rec_dyn/recognizer_cyrillic.onnx [consumer-desktop] [sdk:server] ✅ Model cached with correct size: /Users/lauri/.qvac/models/574dfe543bfdae68_ggml-tiny.bin [consumer-desktop] [sdk:server] 🗑️ Removing incomplete cached file (expected 15250809, got 0): /Users/lauri/.qvac/models/23fdbaa7dced827d_recognizer_cyrillic.onnx [consumer-desktop] [sdk:server] 🔗 Creating new registry client... [consumer-desktop] [sdk:server] ✅ Model already cached and validated: /Users/lauri/.qvac/models/574dfe543bfdae68_ggml-tiny.bin [consumer-desktop] [sdk:server] ✅ Using cached model: /Users/lauri/.qvac/models/574dfe543bfdae68_ggml-tiny.bin [consumer-desktop] [sdk:server] Loaded Model to /Users/lauri/.qvac/models/574dfe543bfdae68_ggml-tiny.bin [consumer-desktop] [sdk:server] [request-lifecycle] end requestId=97726ce8-608b-49fb-a4ee-52fc76413133 kind=downloadAsset modelId=- state=completed durationMs=72 [consumer-desktop] [sdk:server] ✅ Registry client ready [consumer-desktop] [sdk:server] 📥 Downloading blob directly: recognizer_cyrillic.onnx [consumer-desktop] [QVACRegistryClient] [INFO] Downloading blob directly { [consumer-desktop] coreKey: '6309722b3460...', [consumer-desktop] blockOffset: 802467, [consumer-desktop] blockLength: 233, [consumer-desktop] byteLength: 15250809 [consumer-desktop] } [consumer-desktop] [sdk:server] 🐻 Bare worker shutdown signal received, cleaning up... [consumer-desktop] [sdk:server] 🧹 Cancelling 1 active downloads [consumer-desktop] file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/@qvac/sdk/dist/client/rpc/rpc-client.js:79 [consumer-desktop] const parsedRequest = requestSchema.parse(request); [consumer-desktop] ^ [consumer-desktop] [consumer-desktop] ZodError: [ [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [ [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "heartbeat" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"heartbeat\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [ [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [ [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "llamacpp-completion" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"llamacpp-completion\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelConfig" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "whispercpp-transcription" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"whispercpp-transcription\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelConfig" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "parakeet-transcription" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"parakeet-transcription\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelConfig" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "llamacpp-embedding" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"llamacpp-embedding\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelConfig" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "nmtcpp-translation" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"nmtcpp-translation\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_type", [consumer-desktop] "expected": "object", [consumer-desktop] "path": [ [consumer-desktop] "modelConfig" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "onnx-tts" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"onnx-tts\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [ [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelConfig" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "onnx-ocr" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"onnx-ocr\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelConfig" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "sdcpp-generation" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"sdcpp-generation\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "whisper", [consumer-desktop] "whispercpp-transcription" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid option: expected one of \"whisper\"|\"whispercpp-transcription\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelConfig" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "downloadAsset" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"downloadAsset\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "assetSrc" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "array", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "history" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected array, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "boolean", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "stream" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected boolean, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "completionStream" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"completionStream\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "unloadModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"unloadModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_type", [consumer-desktop] "expected": "object", [consumer-desktop] "path": [ [consumer-desktop] "audioChunk" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "transcribe" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"transcribe\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "transcribeStream" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"transcribeStream\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "id" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "loggingStream" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"loggingStream\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [ [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "array", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input: expected array, received undefined" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "text" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "embed" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"embed\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [ [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [ [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "array", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input: expected array, received undefined" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "text" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "boolean", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "stream" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected boolean, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "nmt", [consumer-desktop] "nmtcpp-translation" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid option: expected one of \"nmt\"|\"nmtcpp-translation\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "translate" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"translate\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "text" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "boolean", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "stream" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected boolean, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "llm", [consumer-desktop] "llamacpp-completion" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "modelType" [consumer-desktop] ], [consumer-desktop] "message": "Invalid option: expected one of \"llm\"|\"llamacpp-completion\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "to" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "translate" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"translate\"" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "text" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "textToSpeech" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"textToSpeech\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "textToSpeechStream" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"textToSpeechStream\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "provide" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"provide\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "stopProvide" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"stopProvide\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "operation", [consumer-desktop] "modelId", [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized keys: \"operation\", \"modelId\", \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [], [consumer-desktop] "note": "No matching discriminator", [consumer-desktop] "discriminator": "operation", [consumer-desktop] "options": [ [consumer-desktop] "chunk", [consumer-desktop] "ingest", [consumer-desktop] "saveEmbeddings", [consumer-desktop] "search", [consumer-desktop] "deleteEmbeddings", [consumer-desktop] "reindex", [consumer-desktop] "listWorkspaces", [consumer-desktop] "closeWorkspace", [consumer-desktop] "deleteWorkspace" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "operation" [consumer-desktop] ], [consumer-desktop] "message": "Invalid discriminator value. Expected 'chunk' | 'ingest' | 'saveEmbeddings' | 'search' | 'deleteEmbeddings' | 'reindex' | 'listWorkspaces' | 'closeWorkspace' | 'deleteWorkspace'" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [ [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "deleteCache" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"deleteCache\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] true [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "all" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected true" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "deleteCache" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"deleteCache\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "kvCacheKey" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "name" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "getModelInfo" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"getModelInfo\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "getLoadedModelInfo" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"getLoadedModelInfo\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_type", [consumer-desktop] "expected": "object", [consumer-desktop] "path": [ [consumer-desktop] "image" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "ocrStream" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"ocrStream\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "prompt" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "diffusionStream" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"diffusionStream\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "image" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "upscaleStream" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"upscaleStream\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_union", [consumer-desktop] "errors": [ [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "options" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "finetune" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"finetune\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "start", [consumer-desktop] "resume" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "operation" [consumer-desktop] ], [consumer-desktop] "message": "Invalid option: expected one of \"start\"|\"resume\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized key: \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "object", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "options" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected object, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "finetune" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"finetune\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "getState" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "operation" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"getState\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized key: \"kind\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "finetune" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"finetune\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "pause", [consumer-desktop] "cancel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "operation" [consumer-desktop] ], [consumer-desktop] "message": "Invalid option: expected one of \"pause\"|\"cancel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "unrecognized_keys", [consumer-desktop] "keys": [ [consumer-desktop] "kind" [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Unrecognized key: \"kind\"" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "pluginInvoke" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"pluginInvoke\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "handler" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_type", [consumer-desktop] "expected": "nonoptional", [consumer-desktop] "path": [ [consumer-desktop] "params" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected nonoptional, received undefined" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "pluginInvokeStream" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"pluginInvokeStream\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "modelId" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "handler" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "code": "invalid_type", [consumer-desktop] "expected": "nonoptional", [consumer-desktop] "path": [ [consumer-desktop] "params" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected nonoptional, received undefined" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "modelRegistryList" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"modelRegistryList\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "modelRegistrySearch" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"modelRegistrySearch\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "modelRegistryGetModel" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"modelRegistryGetModel\"" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "registryPath" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] }, [consumer-desktop] { [consumer-desktop] "expected": "string", [consumer-desktop] "code": "invalid_type", [consumer-desktop] "path": [ [consumer-desktop] "registrySource" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected string, received undefined" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "suspend" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"suspend\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "resume" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"resume\"" [consumer-desktop] } [consumer-desktop] ], [consumer-desktop] [ [consumer-desktop] { [consumer-desktop] "code": "invalid_value", [consumer-desktop] "values": [ [consumer-desktop] "state" [consumer-desktop] ], [consumer-desktop] "path": [ [consumer-desktop] "type" [consumer-desktop] ], [consumer-desktop] "message": "Invalid input: expected \"state\"" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] ], [consumer-desktop] "path": [], [consumer-desktop] "message": "Invalid input" [consumer-desktop] } [consumer-desktop] ] [consumer-desktop] at sendProfiled (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/@qvac/sdk/dist/client/rpc/rpc-client.js:79:45) [consumer-desktop] at send (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/@qvac/sdk/dist/client/rpc/rpc-client.js:55:12) [consumer-desktop] at process.processTicksAndRejections (node:internal/process/task_queues:105:5) [consumer-desktop] at async cancel (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/@qvac/sdk/dist/client/api/cancel.js:84:22) [consumer-desktop] [consumer-desktop] Node.js v22.20.0 [consumer-desktop] [QVACRegistryClient] [ERROR] Error downloading blob directly Error: Download cancelled [consumer-desktop] at options.signal.addEventListener.once (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/@qvac/registry-client/lib/client.js:538:20) [consumer-desktop] at AbortSignal.dispatchEvent (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/bare-abort-controller/node_modules/bare-events/web.js:215:17) [consumer-desktop] at AbortSignal._abort (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/bare-abort-controller/index.js:44:10) [consumer-desktop] at AbortController.abort (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/bare-abort-controller/index.js:137:18) [consumer-desktop] at maybeCancelTransfer (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/@qvac/sdk/dist/server/rpc/handlers/load-model/download-manager.js:94:30) [consumer-desktop] at removeSubscriber (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/@qvac/sdk/dist/server/rpc/handlers/load-model/download-manager.js:79:5) [consumer-desktop] at AbortSignal.onAbort (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/@qvac/sdk/dist/server/rpc/handlers/load-model/download-manager.js:101:9) [consumer-desktop] at AbortSignal.dispatchEvent (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/bare-abort-controller/node_modules/bare-events/web.js:215:17) [consumer-desktop] at AbortSignal._abort (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/bare-abort-controller/index.js:44:10) [consumer-desktop] at AbortController.abort (file:///Users/lauri/.cache/qvac-pr-review/pr-2074/packages/sdk/tests-qvac/node_modules/bare-abort-controller/index.js:137:18) [consumer-desktop] [sdk:server] [request-lifecycle] cancel requestId=cb38ec25-486a-4fa5-b4ee-e68e7fcd222f kind=downloadAsset modelId=- state=cancelling [consumer-desktop] [sdk:server] Unloaded 0 models [consumer-desktop] [sdk:server] 🔌 Closing registry client... [consumer-desktop] [sdk:server] [request-lifecycle downloadAsset requestId=cb38ec25-486a-4fa5-b4ee-e68e7fcd222f] downloadAsset cancelled requestId=cb38ec25-486a-4fa5-b4ee-e68e7fcd222f [consumer-desktop] [sdk:server] [request-lifecycle] end requestId=cb38ec25-486a-4fa5-b4ee-e68e7fcd222f kind=downloadAsset modelId=- state=cancelled durationMs=979 [consumer-desktop] [sdk:server] IPC socket closed — parent process likely terminated [consumer-desktop] [sdk:server] ✅ Registry client closed [consumer-desktop] [sdk:server] ✅ Cleanup completed successfully [producer] [producer] 📊 Status [30s]: 0/1 completed | 1 running | 0 queued | 1 consumers [producer] ⏳ download-cancel-isolation → consumer-desktop-Lauris-MacBook-Pro.local-1778843688597 (running, 31s / 540s) [producer] [producer] [producer] 📊 Status [60s]: 0/1 completed | 1 running | 0 queued | 1 consumers [producer] ⏳ download-cancel-isolation → consumer-desktop-Lauris-MacBook-Pro.local-1778843688597 (running, 61s / 540s) [producer] [producer] [producer] 📊 Status [90s]: 0/1 completed | 1 running | 0 queued | 1 consumers [producer] ⏳ download-cancel-isolation → consumer-desktop-Lauris-MacBook-Pro.local-1778843688597 (running, 91s / 540s) [producer] [producer] [producer] 📊 Status [120s]: 0/1 completed | 1 running | 0 queued | 1 consumers [producer] ⏳ download-cancel-isolation → consumer-desktop-Lauris-MacBook-Pro.local-1778843688597 (running, 121s / 540s) [producer] [producer] [producer] 💀 Consumer desktop-Lauris unresponsive for 121s — marking as dead [producer] [producer] ================================================================================

@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Tier-based Approval Status

**PR Tier:** TIER1

**Current Status:** ✅ APPROVED

**Requirements:**
- 1 Team Member approval ✅ (2/1)
- 1 Team Lead OR Management approval ✅ (1/1)



---
*This comment is automatically updated when reviews change.*

Comment thread packages/sdk/examples/rag/rag-hyperdb/cancellation.ts Outdated
Comment thread packages/sdk/examples/rag/rag-hyperdb/cancellation.ts Outdated
…sed cancel

The `download-cancel-isolation` e2e executor was still calling the
removed `cancel({operation:"downloadAsset", downloadKey, clearCache})`
shape, which crashes at the client-side `requestSchema.parse(...)` in
this PR's new 2-arm cancel wire envelope (the executor is itself one
of the in-repo consumers of the [bc] documented on the parent commit
`0279a56bb`).

Surfaced by Lauri running the test locally via `/pr-test` against PR
tetherto#2074 — the consumer reported a ZodError before the cancel could even
reach the SDK server.

Migration follows the pattern the PR body documents: capture the
decorated promise from `downloadAsset(...)` into `cancelledOp` and
cancel by `cancelledOp.requestId` instead of the per-progress-event
`downloadKey`. The `p.downloadKey &&` gate is dropped because
`requestId` is available synchronously on the decorated promise — no
need to wait for the first progress event to surface a key.

`bun lint` + typecheck clean on `packages/sdk` (the `tests-qvac`
typecheck failures are pre-existing — its `node_modules/@qvac/sdk` is
a snapshot populated by `bun run prepare:sdk` rather than a live
symlink). The remaining consumer-migration call sites
(`config-executor.ts`, `delegated-inference-executor.ts`, public
`reference/api/index.mdx` + `models/download-lifecycle.mdx`) are
tracked as follow-ups per Simon's scope choice — see the diary entry.
Comment thread packages/sdk/client/api/cancel.ts Outdated
Per Opaninakuffo's PR tetherto#2074 review:

- `client/api/cancel.ts` JSDoc was ~73 lines reading like a mini
  migration guide. Trimmed to ~25 lines: brief description of the
  two cancel paths, two examples, and a pointer to the 0.11.0
  changelog / release notes for migration detail off the removed
  `{operation:"downloadAsset"|"rag"}` shapes. Detail lives in the
  changelog where it belongs, not in code comments that live forever.

- `examples/rag/rag-hyperdb/cancellation.ts` was missing the new
  primary cancel path (`{requestId}`) and the inline comment block
  was too jargon-heavy for example material ("rag-by-workspace cancel
  arm", `RequestRegistry`, "worker-singleton" — none of which a
  reader of the example needs to know). Refactored to capture the
  decorated promise from `ragIngest(...)` into `const ingest`, cancel
  via `ingest.requestId` as the primary path, and keep a one-line
  pointer to the broad-cancel-by-modelId alternative.

`bun lint` + typecheck clean.
…equestId-based cancel

The 0.11.0 cancel surface is requestId-based; the legacy
`operation:"downloadAsset"` and per-call `delegate` wire shapes were
removed. Migrate the last two tests-qvac executors that still used the
old shapes:

- config-executor.ts: capture the decorated `downloadAsset` promise
  and cancel by `op.requestId`. Mechanical mirror of the
  download-executor.ts migration already on this branch.
- delegated-inference-executor.ts: rewrite `cancelDelegatedDownload`
  to start a delegated `downloadAsset`, obtain a real `requestId`
  synchronously, and `cancel({ requestId })`. Same-process delegation
  still fails with DELEGATE_CONNECTION_FAILED, which is the asserted
  success path.

Unblocks the `config-registry-download-respects-cancel` and
`cancel-delegated-download` e2e tests on 0.11.0.
@simon-iribarren simon-iribarren added the verified Authorize secrets / label-gate in PR workflows label May 15, 2026
@simon-iribarren simon-iribarren added the test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only] label May 15, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

opaninakuffo
opaninakuffo previously approved these changes May 15, 2026
…r delegation

`downloadAsset` is not delegatable on the client (`DownloadAssetOptions`
does not accept `delegate` — only `loadModel` does), so the previous
attempt at porting `cancelDelegatedDownload` to a requestId-based
cancel hit TS2353 in the tests-qvac typecheck:

  Object literal may only specify known properties, and 'delegate' does
  not exist in type 'DownloadAssetOptions'.

Switch the test to start a delegated `loadModel` instead — its inner
work includes downloading the asset on the provider side, so the
"delegated cancel routes through the delegation pipe" assertion is
preserved. Same-process delegation still fails with
DELEGATE_CONNECTION_FAILED, which is the asserted success path.
@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — ios⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
View run

The test job did not produce a results artifact. Check the run for job-level failures.

@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — windows⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
View run

The test job did not produce a results artifact. Check the run for job-level failures.

@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — android⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
View run

The test job did not produce a results artifact. Check the run for job-level failures.

@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — linux⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
View run

The test job did not produce a results artifact. Check the run for job-level failures.

@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — macos⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
View run

The test job did not produce a results artifact. Check the run for job-level failures.

@simon-iribarren

Copy link
Copy Markdown
Contributor Author

/review

@simon-iribarren simon-iribarren merged commit e282e70 into tetherto:main May 15, 2026
21 of 23 checks passed
Proletter pushed a commit that referenced this pull request May 24, 2026
)

* QVAC-18183 feat[bc]: CLI cancel bridge + cancelHandler retirement

M3d — the final sub-PR for QVAC-18183 and the release gate for SDK
0.11.0. Six deliverables (plus a late seventh — decorated-promise
RAG client APIs — added when consumer-surface audit surfaced the
gap), all coupled to the new request-lifecycle system landed in
M3a–M3c.

1. CLI cancel bridge. Every OpenAI-compatible route in `qvac serve`
   (chat, embeddings, transcriptions, translations) now binds
   `req.on('close')` to `sdkCancel({ requestId })` via a single shared
   helper `core/cancel-bridge.ts`. The `requestId` is exposed
   synchronously from `sdkCompletion` / `sdkEmbed` / `sdkTranscribe`
   on the returned decorated promise — the SDK guarantees it's
   available before the inference promise settles. The Workbench
   Stop button now disconnects an HTTP request and the worker
   actually stops decoding instead of silently running to completion.

2. cancelHandler.ts retirement. The 5-arm dispatcher
   (`inference` / `embedding` / `transcription` / `translation` /
   `downloadAsset`) is gone. The handler is now a 2-arm pass-through
   that delegates to `RequestRegistry.cancel({ requestId })` or
   `cancelByModelId({ modelId, kind? })`. Option A.2 (shrink to
   pass-through) was chosen over A.1 (delete entirely) because the
   handler-registry shape requires a handler function per RPC method;
   the file now exists only to bridge the wire schema into the
   registry call.

3. M1 compat-fallback removed. The pre-registry `addon.cancel()`
   fallback in `server/bare/ops/cancel.ts` (lines 49-71 on the parent
   commit) is gone. Every cancellable handler is on the registry as
   of M3a–M3c; broad cancel is a single registry walk.

4. Schema collapse (BREAKING). The wire envelope for `cancel`
   narrows from a 5-arm discriminated union to a 2-arm union
   (`{ operation: "request", requestId }` and
   `{ operation: "broad", modelId, kind? }`). The per-kind public-API
   sugars for `"inference"` and `"embeddings"` are preserved via
   thin transforms in `client/api/cancel.ts:normalizeCancelParams`,
   so the two most common legacy call shapes keep working unchanged.
   The two shapes that *can't* be mechanically back-mapped are removed:
     - `cancel({ operation: "downloadAsset", downloadKey, clearCache })`
       — REMOVED. The old envelope keyed off `downloadKey`, the new
       path keys off `requestId`. Migration: hold onto the
       `requestId` exposed on the decorated promise returned by
       `downloadAsset(...)` and call `cancel({ requestId, clearCache })`.
     - `cancel({ operation: "rag", workspace? })` — REMOVED. The old
       envelope had no `requestId`; in M3d the three cancellable RAG
       client APIs (`ragIngest`, `ragSaveEmbeddings`, `ragReindex`)
       now return decorated promises so callers can migrate to
       `cancel({ requestId: op.requestId })`. The non-cancellable
       RAG ops (`ragChunk`, `ragSearch`, etc.) intentionally do not
       decorate — they don't register with the request registry
       server-side.
   Known affected consumer:
     - qvac-app-workbench-desktop/workbench-worker/services/
       provider-service.ts:91 constructs `{operation:"downloadAsset"}`.
     - qvac-app-workbench-desktop/workbench-worker/handlers/
       rag-handler.ts:169 constructs `{operation:"rag"}`.
   These need a coordinated migration before workbench-desktop bumps
   to SDK 0.11.0; tracking as a follow-up Asana task.

5. Typed-error reconstruction. The cross-RPC `instanceof` story is
   now honest: `errorResponseSchema` carries an optional
   `typedFields` envelope, `QvacErrorBase` subclasses that need to
   survive RPC implement `toErrorResponseFields()`, and the client
   reconstructs them via a keyed map in `client/rpc/rpc-error.ts`.
   Registered today: `RequestIdConflictError`, `RequestNotFoundError`,
   `RequestRejectedByPolicyError` — all re-exported from
   `@qvac/sdk` so consumers can `instanceof` them. Bring-up bug
   caught in test: `QvacErrorBase.name` is the SCREAMING_SNAKE
   error-code name (e.g. `REQUEST_REJECTED_BY_POLICY`), not the JS
   class name; the reconstructor map keys off that exact value and
   the maintenance contract in the JSDoc spells this out so the next
   class added doesn't trip the same wire.

6. Docs. `request-lifecycle-system.mdc` no longer claims a fallback
   to `addon.cancel()` for non-migrated kinds (M3d closes that gap).
   `error-handling.mdc` documents the reconstructor maintenance
   contract. `request-lifecycle-primitives.mdc` aligns with the new
   handler shape. CLI serve workspace rule documents the cancel
   bridge.

7. Decorated-promise RAG (late add). Three RAG client APIs
   (`ragIngest`, `ragSaveEmbeddings`, `ragReindex`) now generate a
   client-side `requestId`, thread it onto the wire envelope, and
   return `Promise<...> & { requestId: string }`. The wire schema
   already supported the optional `requestId` field (added in M3b/M3c);
   this PR closes the client-side wiring gap so the migration story
   for the removed `cancel({operation:"rag"})` arm is honest —
   callers actually have a `requestId` to hand to `cancel(...)`. The
   other six RAG client APIs (chunk/search/delete/list/close/
   deleteWorkspace) intentionally stay non-decorated because their
   handlers don't call `beginRagContext` — they're fast-path
   operations not subject to mid-flight cancellation.

Tests:
- `packages/cli/test/cancel-bridge.test.ts` — 4 cases covering happy
  path, `res.writableEnded` short-circuit, swallowed rejections, and
  `once` semantics.
- `packages/sdk/test/unit/error-typed-fields.test.ts` — 6 cases
  covering `typedFields` population for each registered class and
  proper omission for non-registered / client-constructed errors.
- `packages/sdk/test/unit/rpc-error-reconstruct.test.ts` — 7 cases
  covering round-trip reconstruction, fallback to `RPCError` on
  unknown names, plain-Error fallthrough, defensive defaulting on
  missing `typedFields`, and remote stack/timestamp attachment.
- `packages/sdk/test/unit/request-id-wire.test.ts` — extended with
  1 new case for `ragSaveEmbeddings` envelope `requestId` forwarding,
  matching the existing `ragIngest` / `ragReindex` coverage. Client-
  side decoration is signature-guaranteed by TypeScript; the schema
  test pins the wire envelope.

Net: 284 CLI unit tests pass, 10 SDK unit tests pass, plus the 18
new M3d cases. Lint + typecheck clean. The pre-existing `semver`
typecheck error in `packages/cli/src/verify/bundle/abi.ts` lives on
`upstream/main` and is not introduced or affected by this PR.

Tag justification: `[bc]` not `[api]`. Two public-API `cancel(...)`
call shapes (`downloadAsset`, `rag`) are removed because the old
wire envelopes had no `requestId` to mechanically back-map. The
RAG migration is supported by deliverable 7 (decorated promises on
`ragIngest` / `ragSaveEmbeddings` / `ragReindex`); the `downloadAsset`
migration was already supported via the decorated `downloadAsset(...)`
promise shipped in M3b/M3c. Per the SDK content-tag rules `[api]`
and `[bc]` don't combine; the title carries `[bc]` and the PR body
also documents the additive `[api]`-shaped surfaces (decorated-
promise `requestId`, typed-error reconstruction, new broad-cancel
sugar) under their own section.

* doc[notask]: scrub internal milestone references from rules + JSDoc

The M3a/M3b/M3c/M3d/M1-era/M2-era milestone identifiers live in
private planning artifacts under `tasks/release-0.11.0-planning/`
and have no meaning to anyone reading the qvac monorepo. The
previous M3d PR leaked them into Cursor rules, JSDoc comments, and
test docstrings that future contributors would read with zero
context for what those mean.

Self-review on PR #2074 surfaced 22 hits across 13 files. All
rewrites either drop the milestone tag entirely or rephrase to
reference the public release version (0.11.0) or describe the
current state of the system in plain English ("legacy" / "as of"
/ "current"). No behaviour changes; lint + typecheck clean.

Doc hits (11):
- .cursor/rules/sdk/docs/request-lifecycle-system.mdc lines 77, 195, 196
- .cursor/rules/sdk/request-lifecycle-primitives.mdc lines 19, 247, 374
- .cursor/rules/sdk/docs/kv-cache-system.mdc lines 91, 96, 102, 104, 112

Source hits (7):
- packages/sdk/client/rpc/rpc-error.ts: "bug fixed in M3d alongside ..." -> "bug fixed alongside ..."
- packages/sdk/schemas/cancel.ts: "M2-era per-kind discriminator arms" / "M3d release-gate cleanup" -> "legacy per-kind discriminator arms" / "0.11.0 cleanup"
- packages/sdk/server/bare/ops/cancel.ts: "M1-era pre-registry addon-cancel fallback was removed in M3d" -> "legacy pre-registry addon-cancel fallback was removed in 0.11.0"
- packages/sdk/server/rpc/handler-registry.ts: "After the M3d wire-schema collapse" -> "After the 0.11.0 wire-schema collapse"
- packages/sdk/server/rpc/handlers/cancel-delegated.ts: "After M3d the cancel envelope ..." -> "After the 0.11.0 wire-schema collapse the cancel envelope ..."
- packages/sdk/server/rpc/handlers/cancelHandler.ts: "retired in 0.11.0 (M3d)" -> "retired in 0.11.0"
- packages/sdk/server/rpc/handlers/load-model/download-manager.ts: "Added in M3d to support ..." -> "Added in 0.11.0 to support ..."

Test docstring hits (4):
- packages/sdk/test/unit/runtime/request-registry.test.ts: "Option A in the M3a brief" parenthetical dropped; "pre-M3a behaviour" -> "documented behaviour"
- packages/sdk/test/unit/runtime/request-lifecycle-logging.test.ts: "part of the M3a contract" -> "part of the 0.11.0 request-lifecycle contract"
- packages/sdk/test/unit/runtime/with-request-context.test.ts: "Covers the M3a brief's Deliverable 3 acceptance criteria" -> "Covers the acceptance criteria"

* QVAC-18183 test: migrate tests-qvac download-executor to requestId-based cancel

The `download-cancel-isolation` e2e executor was still calling the
removed `cancel({operation:"downloadAsset", downloadKey, clearCache})`
shape, which crashes at the client-side `requestSchema.parse(...)` in
this PR's new 2-arm cancel wire envelope (the executor is itself one
of the in-repo consumers of the [bc] documented on the parent commit
`0279a56bb`).

Surfaced by Lauri running the test locally via `/pr-test` against PR
#2074 — the consumer reported a ZodError before the cancel could even
reach the SDK server.

Migration follows the pattern the PR body documents: capture the
decorated promise from `downloadAsset(...)` into `cancelledOp` and
cancel by `cancelledOp.requestId` instead of the per-progress-event
`downloadKey`. The `p.downloadKey &&` gate is dropped because
`requestId` is available synchronously on the decorated promise — no
need to wait for the first progress event to surface a key.

`bun lint` + typecheck clean on `packages/sdk` (the `tests-qvac`
typecheck failures are pre-existing — its `node_modules/@qvac/sdk` is
a snapshot populated by `bun run prepare:sdk` rather than a live
symlink). The remaining consumer-migration call sites
(`config-executor.ts`, `delegated-inference-executor.ts`, public
`reference/api/index.mdx` + `models/download-lifecycle.mdx`) are
tracked as follow-ups per Simon's scope choice — see the diary entry.

* QVAC-18183 doc: trim cancel.ts JSDoc + simplify rag cancellation example

Per Opaninakuffo's PR #2074 review:

- `client/api/cancel.ts` JSDoc was ~73 lines reading like a mini
  migration guide. Trimmed to ~25 lines: brief description of the
  two cancel paths, two examples, and a pointer to the 0.11.0
  changelog / release notes for migration detail off the removed
  `{operation:"downloadAsset"|"rag"}` shapes. Detail lives in the
  changelog where it belongs, not in code comments that live forever.

- `examples/rag/rag-hyperdb/cancellation.ts` was missing the new
  primary cancel path (`{requestId}`) and the inline comment block
  was too jargon-heavy for example material ("rag-by-workspace cancel
  arm", `RequestRegistry`, "worker-singleton" — none of which a
  reader of the example needs to know). Refactored to capture the
  decorated promise from `ragIngest(...)` into `const ingest`, cancel
  via `ingest.requestId` as the primary path, and keep a one-line
  pointer to the broad-cancel-by-modelId alternative.

`bun lint` + typecheck clean.

* QVAC-18183 test: migrate tests-qvac config + delegated executors to requestId-based cancel

The 0.11.0 cancel surface is requestId-based; the legacy
`operation:"downloadAsset"` and per-call `delegate` wire shapes were
removed. Migrate the last two tests-qvac executors that still used the
old shapes:

- config-executor.ts: capture the decorated `downloadAsset` promise
  and cancel by `op.requestId`. Mechanical mirror of the
  download-executor.ts migration already on this branch.
- delegated-inference-executor.ts: rewrite `cancelDelegatedDownload`
  to start a delegated `downloadAsset`, obtain a real `requestId`
  synchronously, and `cancel({ requestId })`. Same-process delegation
  still fails with DELEGATE_CONNECTION_FAILED, which is the asserted
  success path.

Unblocks the `config-registry-download-respects-cancel` and
`cancel-delegated-download` e2e tests on 0.11.0.

* QVAC-18183 test: fix delegated-inference-executor to use loadModel for delegation

`downloadAsset` is not delegatable on the client (`DownloadAssetOptions`
does not accept `delegate` — only `loadModel` does), so the previous
attempt at porting `cancelDelegatedDownload` to a requestId-based
cancel hit TS2353 in the tests-qvac typecheck:

  Object literal may only specify known properties, and 'delegate' does
  not exist in type 'DownloadAssetOptions'.

Switch the test to start a delegated `loadModel` instead — its inner
work includes downloading the asset on the provider side, so the
"delegated cancel routes through the delegation pipe" assertion is
preserved. Same-process delegation still fails with
DELEGATE_CONNECTION_FAILED, which is the asserted success path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only] tier1 verified Authorize secrets / label-gate in PR workflows verify

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants