# Client and exported types When you build a tool around Hankweave – a dashboard that reads run state, a script that parses the event stream, a reporter that walks per-codon transcripts – you need the same shapes the server itself uses. Rather than define stubs by hand, you can import Hankweave's own TypeScript types and Zod schemas from the published package. This page is the reference for that surface: what the package exports, what it deliberately does not, and how to use the exports for the common reading tasks. If you are looking for WebSocket client code instead, that lives in [WebSocket quickstart](/integrate/websocket-quickstart). ## What Hankweave exports The published `hankweave` package has exactly three public import paths. Two of them, `./schemas` and `./types`, are the library surface for external consumers; the third is the CLI entry and carries no types at all. | Export | Purpose | Useful shape or symbols | | ----------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `.` | The CLI entry at `dist/index.js`. | It has no `types` condition and no published `dist/index.d.ts`; an import from `hankweave` therefore has no typed library surface and raises TypeScript diagnostic TS7016 unless suppressed. | | `./schemas` | Zod schemas and inferred types for server events, per-codon log messages, and content blocks. | `serverEventSchema`, `ServerEvent`, event-data schemas, category classifiers, `logMessageSchema`, and content-block schemas and types. | | `./types` | TypeScript types and utilities for state, configuration, branded IDs, and execution data. | `HankweaveState`, configuration types, `CodonId` and related constructors, `getCodonCost`, `getCodonTokens`, and `isTerminalCodonStatus`. | The `.` row explains a common surprise: importing from bare `hankweave` gives you no typed surface, because the CLI entry has no `types` condition. Repository-relative imports such as `hankweave/server/*` are not additional package exports either. Everything you can legitimately import comes from `hankweave/schemas` or `hankweave/types`, and the next two sections catalog each path. ## `hankweave/schemas` – events and log messages The `hankweave/schemas` entry point contains the complete `ServerEvent` union, selected named schemas for validating individual event shapes, and the schemas for per-codon log messages and their content blocks. The table groups the exports by the job they do: | Category | Exported symbols | Use | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | Events | `serverEventSchema`, `ServerEvent`; individual event schemas and types for `CodonStartedEvent`, `CodonCompletedEvent`, `CodonExtendedEvent`, `AssistantActionEvent`, `ToolResultEvent`, `FileUpdatedEvent`, `ErrorEvent`, `TokenUsageEvent`, `SentinelLoadedEvent`, `SentinelOutputEvent`, `SentinelErrorEvent`, `SentinelTriggeredEvent`, `SentinelUnloadedEvent`, `RigSetupCompletedEvent`, `RigSetupFailedEvent`, `RigOutputEvent`, `LoopIterationCompletedEvent`, `InfoEvent`, `BudgetSummaryEvent`, and `ServerReadyEvent` | The union has 36 members. Individual event schemas are exported for 20 members; the other 16 are available through the union and inferred type. | | Event data | `assistantActionEventDataSchema`, `codonStartedEventDataSchema`, `codonCompletedEventDataSchema`, `tokenUsageEventDataSchema`, `toolResultEventDataSchema`, `rigSetupCompletedEventDataSchema`, `rigSetupFailedEventDataSchema`, `sentinelLoadedEventDataSchema`, `sentinelOutputEventDataSchema`, `sentinelUnloadedEventDataSchema`, and `loopIterationCompletedEventDataSchema` | Parse a selected event's `data` object without parsing the surrounding event again. | | Classifiers | `isServerStateEvent`, `isAgenticBackboneEvent`, `isSentinelEvent`, `isConnectionStateEvent` | Each function is a type guard that narrows a `ServerEvent` to its category union. | | Log messages | `logMessageSchema`, `LogMessage`, `assistantMessageSchema`, `AssistantMessage`, `resultMessageSchema`, `ResultMessage`, `systemMessageSchema`, `SystemMessage`, `userMessageSchema`, `UserMessage` | Parse per-codon agent-session JSONL and distinguish assistant, result, system, and user messages. | | Content blocks | `textContentSchema` / `TextContent`, `thinkingContentSchema` / `ThinkingContent`, `toolUseContentSchema` / `ToolUseContent`, `toolResultContentSchema` / `ToolResultContent`, and `messageContentSchema` | Validate content inside a log message. | Two details in that table are worth keeping in mind. The union has 36 members, but individual schemas exist for only 20 of them; the rest are reachable through the union and its inferred type. And the event-data schemas let you parse a selected event's `data` object on its own, without re-parsing the surrounding event. A consumer that needs several event and log-message symbols pulls them all from the same public path: ```ts import { serverEventSchema, type ServerEvent, type CodonCompletedEvent, type AssistantActionEvent, type ToolResultEvent, logMessageSchema, type LogMessage, type AssistantMessage, type ResultMessage, type ToolUseContent, type ThinkingContent, type TextContent, isServerStateEvent, isAgenticBackboneEvent, isSentinelEvent, isConnectionStateEvent, } from "hankweave/schemas"; ``` ## `hankweave/types` – state, config, and utilities The second entry point covers the rest of the vocabulary: state and execution records, configuration shapes, branded IDs, and a small set of runtime helpers. One term matters before the table: a **hank** is the work definition Hankweave loads, the `hank.json` file whose required `hank` array lists the codons and loops that form the immutable logic sequence. Several configuration types below describe values read from that file. | Category | Exported symbols | Useful constraint or example | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | State and execution | `HankweaveState`, `Run`, `CodonExecution`, `ExecutionCodonEntry`, `CodonStatus`, `CompletedCodon`, `FailedCodon`, `CompletingSentinelsCodon`, `InitializingCodon`, `PreparingCodon`, `RunningCodon`, `SkippedCodon`, `StartingCodon`, `StartingConditions`, `SentinelState`, `TokenUsage`, `FailureReason` | `CodonStatus` is `preparing \| starting \| initializing \| running \| completing-sentinels \| completed \| failed \| skipped`. `ExecutionCodonEntry` is the shape used in the state's `executionPlan` array. | | Configuration | `Codon`, `CodonConfig`, `HankFile`, `HankMeta`, `HankweaveConfig`, `Loop`, `RigSetupItem`, `RigShellCommand` | These types describe values read from Hankweave configuration and hank files. | | Branded IDs | `CodonId`, `RunId`, `SessionId`, `EventId` | Each constructor accepts a string and returns its corresponding branded string type, such as `CodonId("my-codon")`. | | Utilities | `getCodonCost(execution)`, `getCodonTokens(execution)`, `isTerminalCodonStatus(status)` | The cost and token helpers read a `CodonExecution`; the terminal-status guard recognizes `completed`, `failed`, and `skipped`. | The state row carries the two constraints you will hit first: `CodonStatus` is exactly `preparing | starting | initializing | running | completing-sentinels | completed | failed | skipped`, and `ExecutionCodonEntry` is the element type of the state's `executionPlan` array. Because this path mixes type-only exports with runtime values (the ID constructors and the utility functions), it is good practice to keep the two import kinds separate: ```ts import type { HankweaveState, Run, CodonExecution, CompletedCodon, FailedCodon, CodonStatus, TokenUsage, } from "hankweave/types"; import { CodonId, RunId, isTerminalCodonStatus, getCodonCost, getCodonTokens, } from "hankweave/types"; ``` ## What is not exported The public package surface is closed: `.`, `./schemas`, and `./types` are the only public paths. Everything else that might look importable is not, and the failure modes are worth knowing in advance. > **Pitfall:** `from "@hankweave/types"` and `from "hankweave/server/*"` do not resolve in the published package. Use `from "hankweave/types"` for state, configuration, IDs, and utilities, and `from "hankweave/schemas"` for events and log messages. Three specific non-exports cause most of the confusion: * `@hankweave/types` does not exist. The package is the unscoped `hankweave`; `@southbridgeai/hankweave` is the private repository name, not an install target. * Deep imports such as `hankweave/server/websocket-log-reader` and other `hankweave/server/*` paths are internal rather than public API. * The command definitions in `command-schemas.ts` are internal, not package exports. The 14-command reference, including `server.force_shutdown`, belongs to [the WebSocket protocol](/integrate/protocol). Likewise, `TestWSClient` from the test suite is not a public export. > **VersionNote:** Since 0.6.2, `./schemas` and `./types` have been public exports. Earlier versions had no importable type surface; consumers of those versions must define their own type stubs. ## Use the exported types The remainder of this page walks through the reading tasks these exports support, in the order you are likely to meet them: reading state, parsing and filtering events, computing cost, constructing IDs, and parsing transcripts. First, install the unscoped package: ```sh npm install hankweave@0.10.0 # or bun add hankweave@0.10.0 ``` Then import each shape from the path that owns it: ```ts import { serverEventSchema, type ServerEvent } from "hankweave/schemas"; import type { HankweaveState, Run, CodonStatus } from "hankweave/types"; import { CodonId, isTerminalCodonStatus, getCodonCost, } from "hankweave/types"; ``` Two environment details affect whether these imports work. The schemas are Zod v3 objects, and `zod` is a regular dependency of `hankweave@0.10.0` rather than a peer dependency, so it arrives transitively; the package does not re-export Zod. And the verified consumer settings for these package exports are `module: "Node16"`, `moduleResolution: "Node16"`, `target: "ES2022"`, `strict: true`, and `types: ["node"]`. The `typecheck-exports` fixture is the recorded check that all of this holds: it typechecks the imports against the published package and runs its parser against real `state.json` and `events.jsonl` files. Its clean output is the evidence that `hankweave/schemas` and `hankweave/types` resolve as documented: ``` bun install v1.3.14 (0d9b296a) Resolved, downloaded and extracted [216] warn: incorrect peer dependency "zod@3.25.76" warn: incorrect peer dependency "@anthropic-ai/sdk@0.91.1" + @types/node@22.20.1 (v26.4.1 available) + typescript@5.9.3 (v7.0.2 available) + hankweave@0.10.0 267 packages installed [1217.ms] Blocked 2 postinstalls. Run `bun pm untrusted` for details. typecheck-exports: clean exit=0 ``` ### Read state with the exported types `HankweaveState` provides the type for parsed `state.json` data, and `isTerminalCodonStatus` identifies the `completed`, `failed`, and `skipped` codons. Together they are enough to walk every run and pick out finished work: ```ts import type { HankweaveState } from "hankweave/types"; import { isTerminalCodonStatus } from "hankweave/types"; import { readFile } from "node:fs/promises"; const statePath = "state.json"; const state: HankweaveState = JSON.parse(await readFile(statePath, "utf8")); for (const run of state.runs) { for (const codon of run.codons) { if (isTerminalCodonStatus(codon.status)) { console.log(codon.status); } } } ``` ### Parse events with Zod For the event stream, parse each non-empty JSONL line with `serverEventSchema`. The result is a `ServerEvent` narrowed from the 36-member union, so downstream code gets full type discrimination on `event.type`: ```ts import { serverEventSchema, type ServerEvent } from "hankweave/schemas"; declare const line: string; const event: ServerEvent = serverEventSchema.parse(JSON.parse(line)); ``` ### Filter events by category When you only care about one category, filter a `ServerEvent[]` with an exported classifier. Each classifier is a type guard, so the filtered array is narrowed to that category's union rather than staying `ServerEvent[]`: ```ts import { isServerStateEvent, type ServerEvent } from "hankweave/schemas"; declare const events: ServerEvent[]; const serverStateEvents = events.filter(isServerStateEvent); ``` The same pattern applies to `isAgenticBackboneEvent`, `isSentinelEvent`, and `isConnectionStateEvent`. ### Compute codon cost without double-counting Cost is the easiest place to get wrong answers from otherwise correct parsing, so the rules come before the code. For state data, call `getCodonCost` on a `CodonExecution`. For an event-stream reported-codon-cost view, establish run boundaries from `state.transition`, retain the final `codon.completed` cost for each `(runId, codonId)`, and keep continuation runs and loop runtime IDs distinct. Deduplicate retransmitted event IDs before retaining or summing records. The completion cost is the authoritative persisted cost and includes accumulated retry cost; do not add `token.usage`, retry, or extension totals again. This view is not a provider invoice and does not establish accounting for interrupted or skipped work, unpriced routes, sentinels, or health checks. `budget.summary` is a runtime, state-based summary rather than a provider invoice or a guarantee of retry-inclusive accounting. The implementation below applies those rules: it tracks the current run from `state.transition` events, skips retransmitted event IDs, and keeps only the final `codon.completed` record per `(runId, codonId)` before summing. ```ts import { type ServerEvent } from "hankweave/schemas"; export function reportedCodonCosts(events: ServerEvent[]): Map { let currentRunId: string | undefined; const costs = new Map(); const seenEventIds = new Set(); for (const event of events) { if (seenEventIds.has(event.id)) continue; seenEventIds.add(event.id); if (event.type === "state.transition") { if (event.data.transitionType === "RunStarted") { currentRunId = event.data.runId; } else if (["RunCompleted", "RunFailed", "RunCrashed"].includes(event.data.transitionType)) { currentRunId = undefined; } } if (event.type === "codon.completed" && currentRunId) { const key = `${currentRunId}:${event.data.codonId}`; costs.set(key, event.data.cost); } } return costs; } // Sum only after the final record for each (runId, codonId) is retained. export function reportedTotal(events: ServerEvent[]): number { return [...reportedCodonCosts(events).values()].reduce((total, cost) => total + cost, 0); } ``` For state data, the direct utility remains the shorter lookup: ```ts import { getCodonCost, isTerminalCodonStatus, type CodonExecution, } from "hankweave/types"; declare const codon: CodonExecution; const cost = getCodonCost(codon); const terminal = isTerminalCodonStatus(codon.status); ``` ### Construct IDs without mixing them Construct a branded ID where a string becomes a Hankweave identifier. The branded type prevents accidental mixing of codon IDs, run IDs, and plain strings during type checking. ```ts import { CodonId, RunId } from "hankweave/types"; const cid = CodonId("my-codon"); const rid = RunId("my-run"); ``` ### Parse per-codon transcripts Use `logMessageSchema` for an agent-session transcript when the consumer needs to distinguish assistant messages, tool results, system messages, and user messages. ```ts import { logMessageSchema, type AssistantMessage, type ResultMessage, } from "hankweave/schemas"; declare const line: string; const message = logMessageSchema.parse(JSON.parse(line)); ``` ## From client-library snippets to real exports WebSocket client code with event buffering, `waitForEvent`, and connection retries belongs to [WebSocket quickstart](/integrate/websocket-quickstart), not this exports reference. The command catalog belongs to [the WebSocket protocol](/integrate/protocol). This page shows the import side of those patterns: when client code from those pages handles events or state, the types behind it come from `hankweave/schemas` or `hankweave/types`, never from a deep server import.