SDKs · 5. SDKCrux packages

SDKCrux is a second SDK monorepo, unrelated to the Crux sdks/ directory, that publishes into the same @cuecrux/ npm scope. Exactly one of its packages is installable from public npm today: @cuecrux/engine-client 1.1.1. The other twenty are either restricted to GitHub Packages, declared public but never published, or private.

This chapter is reference for people who already depend on SDKCrux or need to evaluate it. If you are choosing a client for the Crux Daemon, chapter 0 already answered that: use the in-repo Crux SDKs. Read this chapter for the Engine client, for @cuecrux/memory's API, which is the best ergonomics anywhere in the portfolio, and unpublished, or to work out what you are actually running.

Every defect referenced here is documented with its evidence in chapter 6.

5.1 What you can actually install

Registry state queried 2026-07-27.

PackageManifest versionpublishConfigOn public npmRelease workflow
@cuecrux/engine-client1.1.1registry.npmjs.org, access: publicYes, 1.1.1Two, both workflow_dispatch
@cuecrux/factory0.1.0registry.npmjs.org, access: publicNo, 404One, workflow_dispatch
@cuecrux/memory0.1.0registry.npmjs.org, access: publicNo, 404None
cuecrux-receipt0.1.1access: public, no registry overrideNo, 404None
@cuecrux/policy-cli0.14.1none at allNo, 404None

Four of those five are not on npm at all, and three of the four have no release workflow that could ever put them there. A public publishConfig with no workflow behind it is a declaration of intent, not a release. If you want @cuecrux/memory or cuecrux-receipt, vendoring the source is the only route.

The repository root is private, pnpm@9.12.1, and requires Node ≥ 22 (package.json:2). Registry routing for the internal scope is set in .npmrc: @cuecrux-internal:registry=https://npm.pkg.github.com (.npmrc:2).

5.2 The complete inventory

Twenty-one packages, in three regimes.

Public scope, npm-targeted

PackageVersionPathState
@cuecrux/engine-client1.1.1packages/engine-clientPublished. Typings do not resolve, 6.1. Two dead methods, 6.2
@cuecrux/factory0.1.0packages/factoryNot published. A one-line re-export of packages/internal/factory
@cuecrux/memory0.1.0packages/memoryNot published, no workflow, not in CODEOWNERS, not in the test workspace. One response-shape bug, 6.4
cuecrux-receipt0.1.1packages/cliNot published. Would install broken, 6.5
@cuecrux/policy-cli0.14.1apps/policy-cliNot published. Public scope by accident, and invisible to the publish guard, 6.6

Restricted, GitHub Packages, with an explicit publishConfig

Each of these declares registry: https://npm.pkg.github.com and access: restricted. You need a GitHub Packages token to install any of them.

PackageVersionPathWhat it is
@cuecrux-internal/core1.0.0packages/coreThe shared runtime: 48 export lines covering budgets, canonical JSON, crypto, an encrypted cache, telemetry, retry, tracing, policy, provenance, replay and assurance receipts (index.ts:1). Ships two binaries, cuecrux-gen-sdk-receipt and cuecrux-verify-sdk-receipt
@cuecrux-internal/engine1.0.0packages/engineEngine request/response DTOs as Zod schemas, an EngineClient and EngineHttpError, plus a ./mocks subpath (index.ts:1)
@cuecrux-internal/engine-sdk0.1.1packages/engine-sdkReceipt fetch and verify helpers; re-exports ./verify only (index.ts:1)
@cuecrux-internal/mocks1.0.0packages/mocksMSW handlers and fixtures for /healthz, /search and /v1/answers (index.ts:1)
@cuecrux-internal/watch1.2.0packages/watchOperator utilities: types, env, Postgres helpers, SQL generation, intake, thresholds, telemetry, Prometheus (index.ts:1)
@cuecrux-internal/factory0.1.0packages/internal/factoryFactory job contracts and FactoryClient, the source of truth behind the public @cuecrux/factory (index.ts:1)

No publishConfig at all

These are private: false with no publishing configuration. The @cuecrux-internal/* names are saved from a public accident by the .npmrc scope mapping; @cuecrux/policy-cli is not.

PackageVersionPathWhat it is
@cuecrux-internal/auth0.14.1packages/authOIDC, SAML and session helpers (index.ts:1)
@cuecrux-internal/bff0.15.1packages/bffBackend-for-frontend: policyGuard, engineClient, whyTrust, an operator summary API (index.ts:1)
@cuecrux-internal/cache1.0.0packages/cachegetOrCompute, topicKey, MemorySemanticCache (index.ts:5)
@cuecrux-internal/connectors0.0.1packages/connectorsGoogle Drive and Confluence export, permissions, aggregation (index.ts:1)
@cuecrux-internal/eval-lab0.0.1packages/eval-labrecallAtK, dcgAtK, ndcgAtK, hallucinationFlag, aggregate; binary cuecrux-eval (index.ts:14)
@cuecrux-internal/trust-report0.13.3packages/trust-reportTrust-report DTOs, plugin hooks, HTML and PDF renderers (index.ts:1)
@cuecrux-internal/web0.1.0packages/webPlan-aware UI hints and a ModeSwitch.vue (index.ts:1)
@cuecrux-internal/webcrux0.14.0packages/webcruxresolveFreshnessState, nextStricterMode, daysBetween and a StaleBanner.vue (index.ts:1)
@cuecrux-internal/sdk-core0.0.1packages/internal/coreName and path disagree: it lives at internal/core but is named sdk-core. Only connectors depends on it
@cuecrux-internal/observability0.0.0packages/internal/observabilityprivate: true. Plain index.js, no TypeScript, no build
@cuecrux/policy-cli0.14.1apps/policy-cliA binary named policy-cli, depending on the restricted @cuecrux-internal/core (package.json:2)

5.3 CruxMemory: the two-verb facade

This is the best-designed client API in either estate. It is also not published, has no release workflow, no CODEOWNERS entry, no changelog and no compatibility manifest. Treat it as a design to copy or a source tree to vendor, not a dependency to add.

@cuecrux/memory is generated from the daemon's own /v1/openapi.json, has zero runtime dependencies, and needs a global fetch or an injected one (package.json:11).

class CruxMemory {
  /** Full typed client (facts, sessions, query, receipts), the escape hatch. */
  readonly client: DaemonClient;

  constructor(client: DaemonClient);

  static connect(options?: ConnectOptions): Promise<CruxMemory>;

  remember(content: string, options?: RememberOptions): Promise<Fact>;
  recall(query: string, options?: RecallOptions): Promise<Fact[]>;
  forget(factId: string): Promise<void>;
}

Source: memory.ts:52. ConnectOptions is an alias for DiscoveryOptions (memory.ts:36).

const memory = await CruxMemory.connect();
await memory.remember("Deploys go through cargo-deploy, never bare cargo.");
const hits = await memory.recall("how do we deploy?");

RememberOptions

FieldTypeDefaultMaps to
entitystring"memory"entity
keystringmem-<Date.now()>-<6 random base36 chars> (memory.ts:38)key
confidencenumberomittedconfidence
privatebooleanomittedprivate, the daemon rejects true with a 400 (facts.rs:229); the SDK's own JSDoc says so (memory.ts:12)
horizonClassHorizonClass: one of volatile, medium, stable, noneomittedhorizon_class
actorstringomittedactor: durable authorship, a passport id or agent name
sourceReceiptstringomittedsource_receipt

Source: memory.ts:6.

horizonClass and actor are the reason this facade is more capable than @cuecrux/client on writes: neither Crux SDK can set them.

RecallOptions

FieldTypeMaps to
entitystringentity, exact match
entityPrefixstringentity_prefix, e.g. execplan:
topKnumbertop_k, daemon default 10
tokenBudgetnumbertoken_budget, fills by descending score until exhausted

Source: memory.ts:25.

recall() returns result.facts and discards total_tokens (memory.ts:99). If you set a tokenBudget and want to know how much of it was spent, call memory.client.queryFacts(...) directly instead.

forget(factId) delegates to deleteFact and resolves to void; the daemon's {"deleted": true} body is discarded (memory.ts:103).

Complete export list

Values (index.ts:1): CruxMemory, createDaemonClient, CruxDaemonError, discoverDaemon, DEFAULT_LOCAL_URL, ENV_AGENT_TOKEN, ENV_DAEMON_URL, ENV_REMOTE_URL.

Types: ConnectOptions, RecallOptions, RememberOptions, DaemonClient, DaemonClientOptions, DiscoveredDaemon, DiscoveryOptions, DiscoverySource, Fact, StoreFact, FactQueryResult, FactExportResult, FactQueryParams, ExportFactsParams, SessionState, HorizonClass, TextSearchBody, TextSearchExpandBody, GraphExpandBody, TimeRangeBody, ExpandResultId, and the raw generated components, operations and paths.

ensureFetch is not re-exported here, unlike in @cuecrux/engine-client. It throws Fetch implementation is required. Provide one via the fetchImpl option or run on Node >= 18. (fetch-guard.ts:3).

5.4 The typed daemon client behind the facade

interface DaemonClientOptions {
  baseUrl: string;          // required
  token?: string;           // CRUX_AGENT_TOKEN; held in a closure, never logged
  fetchImpl?: typeof fetch; // tests, polyfills
}

function createDaemonClient(options: DaemonClientOptions): DaemonClient;

Source: client.ts:27. This is the widest daemon coverage of any client: nineteen methods, including the three receipt routes neither Crux SDK touches.

MethodHTTPReturns
healthz()GET /healthzunknown
readyz()GET /readyzunknown
version()GET /v1/versionunknown
storeFact(body)PUT /v1/factsFact
storeFactsBulk(facts)PUT /v1/facts/bulkunknown
getFact(factId)GET /v1/facts/{id}Fact
deleteFact(factId)DELETE /v1/facts/{id}void
getFactsByEntity(entity)GET /v1/facts/entity/{entity}Fact[]: wrong, see 6.4
queryFacts(params?)GET /v1/factsFactQueryResult
exportFacts(params?)GET /v1/facts/exportFactExportResult
getSessionState(sessionId)GET /v1/sessions/{id}/stateSessionState
putSessionState(sessionId, state)PUT /v1/sessions/{id}/stateSessionState
textSearch(body)POST /v1/query/text-searchunknown
textSearchExpand(body)POST /v1/query/text-search/expandunknown
graphExpand(body)POST /v1/query/graph-expandunknown
timeRange(body)POST /v1/query/time-rangeunknown
getReceipt(receiptId, tenantId)GET /v1/receipts/{id}?tenant_id=unknown
getReceiptSignature(receiptId, tenantId)GET /v1/receipts/{id}/signature?tenant_id=unknown
getReceiptVerification(receiptId, tenantId)GET /v1/receipts/{id}/verification?tenant_id=unknown

Source: client.ts:41.

The unknown return types are deliberate and, in a documentation set that values honesty, worth praising: where the recorded OpenAPI leaves a 200 body untyped, the author refused to hand-guess a shape and typed it unknown, with an inline comment saying so (client.ts:147). @cuecrux/client types those same responses because it was written against the daemon rather than its spec. Both choices are legitimate; the second is more useful, the first is more honest about what the spec guarantees.

A note on the receipt routes, since this is the only client that reaches them. A retrieval receipt is a verifiable record of what was stored and retrieved, content-addressed tamper-evidence. It is not an attestation that an agent did anything. Execution receipts are Ed25519-signed; retrieval receipts are BLAKE3 content-addressed and are not signed. Do not collapse the two.

Data shapes, generated from the recorded spec (types.gen.ts:306):

// Fact, required
{ confidence: number; deleted: boolean; entity: string; fact_id: string;
  key: string; stored_at: string; tokens: number; value: string }
// Fact, optional
{ actor?; horizon_class?; private?; reverified_at?; source_receipt?;
  superseded_by?; supersedes?; version? }   // version is int32, starts at 1

// StoreFact, required: entity, key, value
//             optional: actor, confidence, horizon_class, private, source_receipt

FactQueryResult  = { facts: Fact[]; total_tokens: number }
FactExportResult = { facts: Fact[]; has_more: boolean; next_cursor?: string | null }
SessionState     = { session_id; state: unknown; total_tokens; updated_at; expires_at? }
TextSearchBody   = { tenant_id; query; limit?; min_score?; mode?; token_budget?; include_receipt? }
GraphExpandBody  = { tenant_id; seed_artifact_ids: number[]; edge_types?; max_hops?;
                     budget?; min_confidence?; include_state? }
TimeRangeBody    = { tenant_id; start_micros; end_micros; artifact_ids?;
                     include_relations?; limit? }
ExpandResultId   = { doc_id: number; segment_index: number }

Note TextSearchBody.include_receipt, a field neither Crux SDK exposes.

Error model

class CruxDaemonError extends Error {
  readonly status: number;   // HTTP status
  readonly path: string;     // request pathname
  readonly body?: string;    // raw response text, undefined if unreadable
  // name    = 'CruxDaemonError'
  // message = `Crux daemon request failed: ${path} -> ${status}`
}

Source: errors.ts:2. Thrown for every non-2xx (client.ts:102).

There is no error taxonomy here: no 404-to-null convenience, no Problem Details parsing, no retry. Both Crux SDKs are better on this axis. The bearer token is never placed in the message or the body, which is correct.

5.5 Daemon discovery

The one capability no other client has.

function discoverDaemon(options?: DiscoveryOptions): Promise<DiscoveredDaemon>;

interface DiscoveryOptions {
  baseUrl?: string;                              // explicit — skips probing
  token?: string;                                // else CRUX_AGENT_TOKEN
  env?: Record<string, string | undefined>;      // defaults to process.env
  fetchImpl?: typeof fetch;
  probeTimeoutMs?: number;                       // default 750
}

interface DiscoveredDaemon {
  baseUrl: string;
  token?: string;
  source: 'explicit' | 'env' | 'local' | 'remote';
}

Resolution order (discovery.ts:56):

  1. explicit options.baseUrl, with no probe, source: 'explicit'
  2. CRUX_DAEMON_URL, source: 'env'
  3. probe http://127.0.0.1:14800/readyz under AbortSignal.timeout(probeTimeoutMs ?? 750), source: 'local'
  4. CRUX_REMOTE_URL, source: 'remote'
  5. otherwise throw a plain Error naming both environment variables

Exported constants: DEFAULT_LOCAL_URL = http://127.0.0.1:14800, ENV_DAEMON_URL = CRUX_DAEMON_URL, ENV_REMOTE_URL = CRUX_REMOTE_URL, ENV_AGENT_TOKEN = CRUX_AGENT_TOKEN (discovery.ts:4).

Two things to know. The probe checks /readyz, not /healthz, so a running but not ready daemon is treated as absent and discovery falls through to the remote. And step 5 throws a plain Error, not a CruxDaemonError, so instanceof CruxDaemonError will not catch a discovery failure.

5.6 @cuecrux/engine-client, in full

The only installable SDKCrux package. createEngineClient returns an object of nine async methods. Every one throws a bare Error with the message "<operation> failed: <status>" on any non-OK response, no status property, no body.

function createEngineClient(
  baseUrl: string,
  fetchImpl?: typeof fetch,
): { healthz; search; answers; qualityEstimate; qualityBoost; qualityJob; receipt; provenance; trustReport };

function ensureFetch(scope?: { fetch?: typeof fetch }): typeof fetch;

Source: index.ts:96. Also exported as types: paths, components, operations, AnswerReceipt, ProvenanceRecord, TrustReportFormat, QualityBoostType, QualityEstimateRequest, QualityEstimateResponse, QualityBoostRequest, QualityBoostResponse, QualityJobResponse (index.ts:209).

MethodHTTPWorks today
healthz()GET /healthzYes
search(params)GET /v1/searchNo. The route is not served, 6.2
answers(body)POST /v1/answersYes
qualityEstimate(body)POST /v1/quality/estimateYes
qualityBoost(body)POST /v1/quality/boostYes
qualityJob(jobId)GET /v1/quality/jobs/{id}Yes
receipt(answerId)GET /v1/answers/{id}/receiptYes
provenance(artifactId)GET /v1/provenance/{id}Yes
trustReport(answerId, options?)GET /v1/answers/{id}/trust-reportNo. The route is not served, 6.2

Request and response shapes:

// answers(body), request
{ q: string;              // required
  topK?: number; rerankK?: number; mode?: string; freshness?: unknown;
  audience?: unknown; corpusIds?: string[]; filters?: unknown }

// answers(body), response
{ outcome; collapse_report; answerId; answer; citations; usedK;
  retrievedIds; timings; crown; counterfactual; upgrade_hint }

// qualityEstimate(body)
{ boost_type: QualityBoostType; corpus_ids?: string[]; credit_budget?: number }
  -> { estimated_artifacts; estimated_cost_crux; estimated_duration_minutes;
       techniques_applied: string[]; exceeds_budget; capped_artifacts; upgrade_url? }

// qualityBoost(body), credit_budget is REQUIRED here, unlike estimate
{ boost_type: QualityBoostType; corpus_ids?: string[]; credit_budget: number }
  -> { job_id; status: 'queued'; estimated_artifacts; estimated_cost;
       exceeds_budget; capped_artifacts }

// qualityJob(jobId)
  -> { job_id; status; boost_type; estimated_cost; credit_budget;
       counts: Record<string, number>; created_at; updated_at }

// receipt(answerId)
  -> { receiptId; snapshotId; answerId; mode; queryHash; fusion; retrieval;
       selection; timings; receiptHash; knowledgeStateCursor?; signature?; evidence }

// provenance(artifactId)
  -> { artifactId; artifactHash; signer; sigScheme; recordedAt }

type QualityBoostType = 'context_notation' | 'hierarchical' | 'propositions' | 'full_tier3';
type TrustReportFormat = 'html' | 'pdf' | 'json';

Source: index.ts:45. trustReport returns a string for html, a Uint8Array for pdf and unknown for json, and defaults to html, but the route does not exist, so all three paths throw.

Three further facts you need before using it:

It declares a peer dependency it never imports. peerDependencies: { "cross-fetch": "^4.0.0" } (package.json:21) appears nowhere in src/. Installing it is unnecessary; npm will warn if you do not.

It cannot authenticate. See 3.5 for the fetch-wrapping workaround.

Its typings do not resolve. npm install succeeds, import works at runtime, and TypeScript cannot see the module at all. The workaround is 6.1.

5.7 @cuecrux/factory and FactoryClient

@cuecrux/factory is one line (index.ts:2):

export * from '../../internal/factory/src/index';

That is a relative source-tree import across a package boundary, not a workspace dependency. It only works because tsup bundles with dts: { resolve: true }. The package is not published.

The surface it re-exports (index.ts:1): everything in dto.ts, plus generateFactoryOpenApi, FactoryClient, FactoryClientError, FactoryCallOptions and FactoryClientOptions.

interface FactoryClientOptions {
  baseUrl: string;
  apiKey?: string;                  // -> Authorization: Bearer <apiKey>
  timeoutMs?: number;               // default 10_000, floor 1_000
  maxRetries?: number;              // default 2, floor 0
  retryBaseDelayMs?: number;        // default 150, floor 50
  retryMaxDelayMs?: number;         // default 2_000
  defaultHeaders?: Record<string, string>;
  fetchImpl?: typeof fetch;
}

interface FactoryCallOptions {
  requestId?: string;               // else a fresh randomUUID()
  signal?: AbortSignal;
}

class FactoryClient {
  constructor(opts: FactoryClientOptions);
  createJob(args: CreateJobRequest, opts?: FactoryCallOptions): Promise<CreateJobResponse>;
  getJob(id: string, query?: GetJobQuery, opts?: FactoryCallOptions): Promise<GetJobResponse>;
  listJobs(query?: ListJobsQuery, opts?: FactoryCallOptions): Promise<ListJobsResponse>;
  cancelJob(id: string, body?: CancelJobRequest, opts?: FactoryCallOptions): Promise<CancelJobResponse>;
}

class FactoryClientError extends Error {
  readonly statusCode: number;
  readonly requestId: string;
  readonly responseBody?: string;
}

Source: client.ts:22 and client.ts:82.

MethodHTTPRetried
createJobPOST /api/ingest.createNo: deliberately, to avoid duplicate jobs
getJobGET /api/jobs/{id}Yes
listJobsGET /api/jobsYes
cancelJobPOST /api/jobs/{id}/cancelYes

This is the only client in either estate with a retry policy, request-id correlation and schema-validated responses. Its behaviour is documented in 4.8.

DTOs are Zod schemas with OpenAPI annotations. FactoryMode is light, verified or audit. JobStatus is a thirteen-member enum: queued, fetching, scanning, parsing, chunking, embedding, waiting_for_engine, blocked_engine_down, committing, completed, completed_with_errors, failed, dead_lettered (dto.ts:23). Also exported: ArtifactRef, JobTimeline, JobSource, JobCommit, JobIngest, JobFetchSummary, JobEvent, Job, JobResult, JobsSummary, and the four request/response envelope pairs.

Version drift. The changelog documents a 0.1.1 release, but the manifest still reads 0.1.0, as does the public wrapper. The changelog and the manifest disagree.

5.8 cuecrux-receipt and the receipt verifiers

A CLI with one command (cli.ts:52):

cuecrux-receipt verify <answerId> [-b|--base <url>] [-f|--file <path>]
Exit codeMeaning
0The receipt hash matches and the signature verifies
1Verification failed
2An exception was thrown, network, parse or schema

Default base URL http://localhost:3333 (cli.ts:34). Programmatic exports: runVerifyCommand(answerId, options, deps?), createProgram(run?), and the types VerifyOptions and VerifyDeps (cli.ts:7). The deps parameter is a clean injection point for testing, fetchReceipt, verifyReceipt, readFile, log, error.

The verification itself lives in @cuecrux-internal/engine-sdk (verify.ts:19):

type Receipt = { snapshot: {...}; receipt_hash: string;
                 signature_ed25519: string; signer_pubkey_ed25519: string };
type ReceiptVerification = { hashMatches: boolean; signatureValid: boolean };

verifyReceipt(receipt: Receipt): Promise<ReceiptVerification>;   // throws ZodError on malformed input
fetchReceipt(baseUrl: string, answerId: string, fetchImpl?): Promise<Receipt>;

What a passing verification proves, and what it does not. hashMatches says the recorded snapshot canonicalises to the recorded receipt_hash; signatureValid says the recorded Ed25519 signature is valid over that hash under the supplied public key. Together that is tamper-evidence over a record; it does not attest that any agent behaved in any particular way, and it does not establish that the key belongs to who you think it does unless you pinned the key yourself.

Contract mismatch. The receipt shape this verifier accepts (snapshot, receipt_hash, signature_ed25519) is not the shape @cuecrux/engine-client.receipt() returns from the same route (receiptId, snapshotId, receiptHash, signature). Two packages in one repository model one endpoint incompatibly. Piping one into the other throws a ZodError.

5.9 Release and publish machinery

Nineteen workflows. The parts that determine what reaches a registry:

LaneWorkflowTriggerWhat it publishes
Privaterelease-private.ymlPush to main, or dispatch@cuecrux-internal/* to GitHub Packages, only if the repository variable SDK_GH_PACKAGES_ENABLED is 'true'. Nothing documents that variable
Public, generalrelease-public.ymlDispatch only@cuecrux/engine-client, after verify-publish-targets --mode public
Public, engine-clientrelease-public-engine-client.ymlDispatch only@cuecrux/engine-client. Skips verify-publish-targets entirely
Public, factoryrelease-public-factory.ymlDispatch only@cuecrux/factory, with the guard

Secrets come from Vault over GitHub OIDC; public publishing uses npm Trusted Publishing with --provenance and no NPM_TOKEN. That much matches the Crux estate.

What does not match: the public lane publishes from a live working tree with no reproducibility check. There is no second build, no sha256 diff, and no packed-artifact handoff between the build and publish jobs. Compare 2.10, where the Python SDK builds twice and fails the run if the sums differ.

The publish guard, scripts/verify-publish-targets.ts with scripts/verify-lib.ts, enforces:

ModeRule
--mode privateRejects any release name not starting with @cuecrux-internal/ unless explicitly allowlisted, and requires every internal package's publishConfig.registry to contain npm.pkg.github.com
--mode publicRequires --package <name>, rejects any other package in the release set, and requires registry.npmjs.org plus access: 'public'

Its blind spot: workspace packages are loaded from packages/ and packages/internal/ only (verify-lib.ts:22), apps/ is never scanned. See 6.6.

@cuecrux/memory satisfies every public-mode check and there is no workflow that would ever invoke them. No workflow builds, tests or publishes it by name; it is reached only incidentally by pnpm -r build and pnpm -r test. It is also absent from .github/CODEOWNERS.

Useful commands, if you are working in the repository:

pnpm install --frozen-lockfile
pnpm build                 # pnpm -r --parallel build
pnpm -r test               # the only invocation that reaches @cuecrux/memory's tests
pnpm test:api-parity       # byte-compares the recorded Engine spec against ../Engine/openapi.json
pnpm generate:openapi      # re-records the Engine spec from a sibling checkout

pnpm typecheck typechecks exactly one package. pnpm lint is prettier --check ., the ESLint config in the repository is never executed by any script or workflow. pnpm test:api-parity fails today; see 6.8.

5.10 If you already depend on SDKCrux

You depend onRecommendation
@cuecrux/engine-client for answers, receipt, provenance or the quality routesKeep it. Add the auth wrapper from 3.5 and the typings shim from 6.1
@cuecrux/engine-client.search() or .trustReport()These 404. Move to answers() or call the Engine route you actually want
@cuecrux/memory for daemon accessYou are running vendored code. Fix getFactsByEntity per 6.4, or migrate to @cuecrux/client, which covers the same surface minus receipts
@cuecrux/memory specifically for horizonClass, actor or the receipt routesNo published alternative exists. Keep the vendored copy, or call those routes directly
Any @cuecrux-internal/* packageYou hold a GitHub Packages token. Nothing changes; note that six of them declare a types path their build does not emit
cuecrux-receiptYou built it from source. Note the shape mismatch in 5.8 before piping engine-client output into it

For anything new against the Crux Daemon, use @cuecrux/client or corecrux-client.

Sources