Crux v0.3.0
Highlights
-
Deepen the loop-owned execution boundary into a single gateway-closed
LoopRuntimePort, replacing the per-callclientthreading of the oldExecutorSpec/SdkLoopDialectseam.@use-crux/core:- Replace
ExecutorSpecwithLoopRuntimePort(id,describeModel,mapSettings,runTextLoop,runStructuredAttempt,runStream,replayStream). The port is already bound to its SDK client, so each run method takes only anExecutorRequest— there is no per-callclientargument. bind()now returns the client-dependentBoundLoopRuntime(renamed fromBoundLoopOwnedRuntime), which core assembles withid/describeModel/mapSettingsinto the port.- Rename
executorAdapter(spec)(client)toloopRuntimeAdapter(port). - Testing:
fakeExecutor→fakeLoopRuntime(returns{ runtime, calls });executorSpecConformance→loopRuntimePortConformance(harnessprepare()returns{ runtime, model }).
@use-crux/ai:- Add
createAiSdkLoopRuntime(gateway): LoopRuntimePort<LanguageModel>and theAiSdkLoopRuntimetype — the adapter fromSdkGatewayto the core port.aiSdkProviderRuntimeandcreateCruxAi({ gateway })are unchanged.
defineProviderRuntime({ ownership: 'loop-owned', loop })authoring is unchanged except thatloop.bind()now returnsrunTextLoop/runStructuredAttempt/runStream(wasrun/attemptStructured/stream). - Replace
-
Complete the profile-backed
convexAgent()lifecycle around the Convex Agent method surface.- Align thread continuation with Convex Agent: call
continueThread(ctx, target)first, then pass Crux promptinputtothread.generateText(),thread.streamText(),thread.generateObject(), orthread.streamObject(). - Add profile-backed
generateObject()andstreamObject()support, injecting resolved Crux prompt state and prompt output schemas through the same lifecycle/driver boundary as text generation. - Derive public generation args/options/results from upstream Convex Agent method types while omitting Crux-owned
system,prompt,messages, andtools. - Add the
cruxconfig namespace for Crux-owned lifecycle controls:crux.prepare,crux.runtime.store,crux.runtime.namespace,crux.observe,crux.persistence, and advancedcrux.driver. Existing top-levelprepare,store, andnamespaceremain as deprecated compatibility aliases. - Move Crux-only prompt resolution to
agent.crux.resolve()with directagent.resolve()kept as a deprecated compatibility alias. - Deepen the Convex store document contract with a substitutable
ComponentDocumentPort, normalizedConvexStoreDocumentComponent, andcreateInMemoryConvexStoreDocumentComponent()for server/React boundary tests.
- Align thread continuation with Convex Agent: call
-
Add the public observability event spine APIs:
subscribeObservability()for in-process graph-record subscribers andCRUX_OBSERVABILITY_CHANNEL/CruxObservabilityChannelMessagefor Node diagnostics-channel consumers.Remove the legacy runtime instrumentation hook bus.
withTelemetry()now subscribes to the canonical graph-record stream by default, andcreateOtelRecordSubscriber()remains available for custom OTel wiring.Migrate AI agent, Convex swarm/compaction, and ingest parser instrumentation to canonical graph records so they continue emitting observability after the hook bus removal.
TelemetryOptions.recordContentis removed; use the coreobservability.recordInputs/recordOutputspolicy instead.Add observability capture policy controls:
config({ observability: { recordInputs, recordOutputs } }). Disabled input/output artifacts are emitted as reference records with size/hash metadata and no preview.Generation and streaming span-end records now carry
gen.*performance metrics, and@use-crux/otelmaps them to exportedgen_ai.client.*attribute constants.Restore the documented default
withTelemetry()behavior: when no lightweight exporter is configured,@use-crux/otelnow uses the globally registered OpenTelemetry tracer instead of silently dropping spans. -
Deepen the core prompt-resolution pipeline behind one private pass primitive and complete the resolver-port seam.
- Introduce
createPromptResolverPlan(config, ports)— the single private pass primitive.compilePrompt()is now a thin boundary that validates the config, binds ports, and projectsresolve()/inspect()over the plan's onerun(opts, mode)call, so the two projections can never drift across ordering, gating, skills, budget, settings, or inspection. - Add a
TokenizerPort({ count(text) }): every token count the pipeline reports (system parts, prompt text, dropped contexts) now flows through it, so a deterministic counter pins token-budget behavior without depending on the production chars/4 estimate. - Broaden the
skillsport to own registry fetch plus skill-index generation and activation-session creation — the resolution pass no longer imports the skill module directly. - Add
createResolverFakes(): a one-call bundle of deterministic in-memory ports (observability, skills, cache, clock, tokenizer, policy, diagnostics, instrumentation), each also exposed as a named handle for assertions. NewstaticTokenizer()fake. compilePrompt()now returns aPromptResolutionPipeline;CompiledPromptremains as a deprecated alias. New public exports:PromptResolutionPipeline,TokenizerPort,staticTokenizer,createResolverFakes,ResolverFakes,ResolverFakesOptions.- Internal-only refactor of the resolution internals (split port contracts in
resolver/ports.tsfrom their production adapters inresolver/default-ports.ts). No change to theprompt().resolve()/prompt().inspect()runtime behavior or to resolved prompt args.
- Introduce
-
Add
defineSingleTurnProviderBundle()for provider packages that compile single-turn SDK wire hooks into the standard Crux provider runtime and helper factories. -
Stabilize Plan & Tasks task-list state handling: duplicate IDs, removed tasks, discarded lists, terminal transitions, pending/cancelled status derivation, and stale counter repair now resolve through typed lifecycle errors and row-derived state.
Cut over the experimental Plans & Tasks API to the canonical
plan(),tasks(), andtask()surface. Plan and task handles are command handles withget()/list()reads, existing entities are bound withplan.ref()andtasks.ref(), creation tools live atplan.tool()andtasks.tool()with safecreated()accessors, and the oldtasklist, top-level agent/tool factories, and first-match task-list lookup exports are removed from public entrypoints.Add typed task definitions for
tasks({ items }): keyedtask()specs now infer literal task IDs for reads, lifecycle methods, and workers, infer schema-backedcomplete()result payloads, validate completed results at runtime, and reject non-JSON plan/task metadata, list metadata filters, and task results before persistence.Tighten the final beta contract with root-level task lifecycle error exports, schema-input completion typing for transforming result schemas, JSON guard coverage for dropped object properties, and consistent plan-list metadata filtering.
Align React and devtools with the canonical beta surface: React hooks now expose
usePlan()anduseTasks()with ID-or-handle inputs and no publicuseTaskList()alias, while local/devtools plan details project canonical task activity with core task statuses and separate progress messages.Rewrite the public Plans & Tasks docs around the final beta API, including
plan(),tasks(),task(), handle methods, dynamic vs defined ledgers, status derivation, lifecycle errors, React hooks, and guidance on when to useflow()or an external durable runner. -
Deepen Google CachedContent into a single
GoogleCachedContentLifecycle.createGoogle({ cachedContent })now resolves one lifecycle that owns prefix detection, cache keying/reuse, SDK cache operations, and fallback policy, returning a request-ready config patch that bothgenerate()andstream()merge.- Configure the built-in lifecycle with
GoogleCacheConfig(defaultTtlSeconds,maxEntries,onError: 'fallback' | 'throw', or a customGoogleCachedContentCachePort), passfalseto disable, or pass a fully customGoogleCachedContentLifecycle. - Invalid TTL/config values are rejected (per-call TTL overrides fall back to the default; bad
defaultTtlSeconds/maxEntriesthrow a clear error). - New exports:
GoogleCacheConfig,GoogleCacheName,GoogleCachedContentCachePort,GoogleCachedContentErrorMode,GoogleCachedContentLifecycle,GoogleCachedContentOption,GoogleCachedContentPlan,GoogleCachedContentPrepareArgs.
- Configure the built-in lifecycle with
Fixes
-
Refresh npm-facing package documentation and homepage metadata so package pages point users to cruxjs.dev and the core package README presents a concise onboarding path.
Allow
@use-crux/googleconsumers to use either@google/genai1.x or 2.x.Document the single-turn provider bundle authoring path in adapter package READMEs.
-
Add the public observability event spine APIs:
subscribeObservability()for in-process graph-record subscribers andCRUX_OBSERVABILITY_CHANNEL/CruxObservabilityChannelMessagefor Node diagnostics-channel consumers.Remove the legacy runtime instrumentation hook bus.
withTelemetry()now subscribes to the canonical graph-record stream by default, andcreateOtelRecordSubscriber()remains available for custom OTel wiring.Migrate AI agent, Convex swarm/compaction, and ingest parser instrumentation to canonical graph records so they continue emitting observability after the hook bus removal.
TelemetryOptions.recordContentis removed; use the coreobservability.recordInputs/recordOutputspolicy instead.Add observability capture policy controls:
config({ observability: { recordInputs, recordOutputs } }). Disabled input/output artifacts are emitted as reference records with size/hash metadata and no preview.Generation and streaming span-end records now carry
gen.*performance metrics, and@use-crux/otelmaps them to exportedgen_ai.client.*attribute constants.Restore the documented default
withTelemetry()behavior: when no lightweight exporter is configured,@use-crux/otelnow uses the globally registered OpenTelemetry tracer instead of silently dropping spans. -
Reorganize
@use-crux/coreinto package-root domain folders (prompt/,resolver/,runtime/,generation/,tools/,shared/) and split the largest single-file domains into curated barrels plus focused implementation files. The roottypes.tsmega file was drained into the owning domains and reduced to the dependency-free base contracts (AnyModel/AnyToolSet/AnyMessage,FlowToolDef,ModelInfo).This is an internal restructuring only: the public
@use-crux/coreAPI, every package subpath (including./toolsand./tool-middleware), andpackage.jsonexports/typesVersionsare unchanged. No import paths change for consumers.Deepen agent composition internals behind a shared composition runtime that owns composition ids, canonical composition spans, child execution contexts, retry wrapping, and report artifacts for
parallel,pipeline,consensus, andswarm. Public composition factories are unchanged; consensus observability now reports voter agent spans directly under the consensus composition instead of adding a nested parallel composition span. -
Rename the bundled native worker binary from
crux-indexer-workertocrux-static-index-workerto reflect its Static Syntax / Static Index ownership. ThecruxCLI is unchanged and discovers the renamed sibling binary automatically; the only visible change is the binary filename shipped inside the@use-crux/local-<os>-<cpu>platform packages.