Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,16 +506,20 @@ More: [Heartbeat guide](docs/guides/heartbeat.md)

### Programmatic Runtime

Heddle is not only a CLI. The npm package exposes two main programmatic layers:
Heddle is not only a CLI. The npm package exposes three main programmatic layers:

- `createConversationEngine`: an alpha API for persisted multi-turn sessions with session storage, compaction, approvals, traces, semantic activity, and custom frontends or local hosts
- `ConversationRunService`: process-local run identity, ordered activity, bounded replay, cancellation, and approval resolution for reconnectable hosted conversations
- `AgentLoopRuntimeService.run(...)`: a lower-level single-run execution loop for hosts that do not need persisted chat or session behavior

Advanced hosts can also reuse lower-level class APIs such as `ToolRegistry`, `ToolExecutionService`, `TraceRecorder`, `TraceConsoleFormatter`, and `ReviewDiffParser` when they intentionally assemble custom runtime or review surfaces.

Other exported primitives include `HeartbeatRunnerAgent.run`, `HeartbeatSchedulerService.runDueTasks`, and `FileHeartbeatTaskService` for scheduled or custom host workflows.

More: [Programmatic hosts](docs/guides/programmatic/README.md)
More: [Programmatic hosts](docs/guides/programmatic/README.md),
[choosing an integration layer](docs/guides/programmatic/integration-layers.md),
and the runnable
[hosted service → HTTP/SSE → browser client example](examples/sdk/05-hosted-agent/README.md).

### Semantic Drift

Expand Down
8 changes: 8 additions & 0 deletions docs/guides/programmatic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ There are two import entry points:
models, awareness) and specialized runtimes (agent loop, heartbeat,
integrations). Reach for this only when the curated surface is not enough.

Before choosing an API, read
[Choose a Programmatic Integration Layer](integration-layers.md). It maps common
host stacks to the smallest useful Heddle boundary and makes the responsibility
split between Heddle and the host explicit for developers and coding agents.

1. **Start here** — stand up a conversation agent.
- [Quickstart](quickstart.md): a minimal persisted conversation with default
text output, in a few lines.
Expand All @@ -33,6 +38,9 @@ There are two import entry points:
- [Conversation engine](conversation-engine.md): engine setup, state roots,
and persisted sessions.
- [Approvals](approvals.md): own policy decisions in the host.
- [Hosted agent stack](../../../examples/sdk/05-hosted-agent/README.md): compose
a transport-neutral run service, Express/SSE API, and browser client while
keeping each layer replaceable.
5. **Advanced: storage** — back Heddle with your own persistence.
- [Result artifacts](result-artifacts.md): save large generated values as
reusable artifacts.
Expand Down
6 changes: 6 additions & 0 deletions docs/guides/programmatic/conversation-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ The replay buffer is intentionally process-local and bounded. Durable final
conversation state remains in the engine's session repository; transports and
cross-process delivery remain host responsibilities.

For a complete runnable host, follow the
[hosted agent stack example](../../../examples/sdk/05-hosted-agent/README.md). It
uses this exact service for account-scoped start/subscribe/cancel, then adds an
Express/SSE adapter and a framework-neutral browser client without moving HTTP,
authentication, or reconnect policy into the conversation core.

## Reading artifacts

Each turn result already includes the artifacts produced by that turn. To review
Expand Down
153 changes: 153 additions & 0 deletions docs/guides/programmatic/integration-layers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Choose a Programmatic Integration Layer

Heddle's curated SDK assumes a Node.js 20+ TypeScript host that wants to build a
conversational agent experience. It intentionally does not assume how that host
is deployed, which server framework it uses, how events are transported, or
which client renders the conversation.

Use this guide before copying an example. The goal is to adopt the lowest layer
that does the needed heavy lifting while leaving existing product architecture
in control.

## Mental model

```text
HOST-OWNED PRODUCT
UI state and rendering
|
transport client and public wire contract optional
|
server adapter, auth, tenancy, rate limits optional
|
application service and composition root
(product session IDs, tools, config, repositories, policy)
======================== HEDDLE SDK BOUNDARY ========================
ConversationRunService
(active run ID, ordered events, bounded replay, cancel, approvals)
|
ConversationEngine
(persisted sessions, turns, compaction, traces, artifacts)
|
agent loop, models, tools, host extensions, MCP
```

The application service above the boundary is host code. The
[`HostedAgentService`](../../../examples/sdk/05-hosted-agent/01-hosted-service/agent-service.ts)
example shows one useful shape, but it is not a required Heddle wrapper and is
not transport infrastructure.

## Responsibility boundary

| Concern | Heddle owns | Host owns |
| --- | --- | --- |
| Conversation semantics | Messages, turns, continuation, compaction, leases, and persisted session behavior | Stable product conversation ID and access policy |
| Agent execution | Model/tool loop, tool registry/execution, host-extension composition, traces, and artifacts | Product tools, system context, model choice, credentials, and capability policy |
| Approvals | Approval request/resolution lifecycle and run integration | Whether an action is allowed, authenticated approver, and approval UI/policy |
| Active runs | Run ID, ordered sequence, process-local replay, subscription, cancellation, and terminal result | Lifetime of the run service, address scope, process routing, draining, and multi-process delivery |
| Persistence | File-backed defaults plus injectable session/artifact repository ports | Production repository implementations, retention, encryption, backup, and tenancy |
| Identity and authorization | No identity-provider assumption | Authentication, tenant/user mapping, authorization for start/subscribe/cancel |
| Transport/API | No HTTP, tRPC, SSE, or WebSocket assumption | Framework, routes/procedures, wire schemas, errors, limits, CORS, and rate limiting |
| Client experience | Semantic activities and terminal run events | Messages, tool rendering, UI state, retries, notifications, and product-specific result presentation |

Do not rebuild Heddle-owned conversation or run behavior in the host. In
particular, avoid replaying product chat history into every prompt, generating a
second run ID, adding another in-process event bus, or treating subscriber
disconnect as implicit cancellation.

## Choose by host assumptions

| What the host already has | Heddle entrypoint | Example to follow |
| --- | --- | --- |
| Nothing beyond a TypeScript process | `runQuickstartConversationCli` | [`01-interactive-chat.ts`](../../../examples/sdk/01-interactive-chat.ts) |
| A local loop that needs product tools or MCP | Quickstart plus tools/host extensions | [`02-add-a-tool.ts`](../../../examples/sdk/02-add-a-tool.ts), [`03-add-an-mcp-server.ts`](../../../examples/sdk/03-add-an-mcp-server.ts) |
| Its own output sink or local UI | `createConversationEngine` + `createConversationTextHost` or host callbacks | [`04-custom-output.ts`](../../../examples/sdk/04-custom-output.ts) |
| A server/worker that owns transport | `createConversationEngine` + `ConversationRunService` | [`05-hosted-agent/01-hosted-service`](../../../examples/sdk/05-hosted-agent/01-hosted-service) |
| Express with REST + SSE | Same core plus a host adapter | [`05-hosted-agent/02-http-sse-api`](../../../examples/sdk/05-hosted-agent/02-http-sse-api) |
| A browser using that exact REST/SSE contract | Host API plus the protocol client | [`05-hosted-agent/03-browser-client`](../../../examples/sdk/05-hosted-agent/03-browser-client) |

For tRPC, Fastify, Hono, Nest, WebSocket, Electron IPC, queues, or another
transport, stop at the hosted-service layer and implement the adapter in the
host's existing framework. Preserve the run lifecycle contract—start,
sequence-based subscribe/replay, explicit cancel, and a terminal event—without
copying Express-specific code.

## Layer-by-layer assumptions

### Quickstart runner

Use `runQuickstartConversationCli` when Heddle may own the prompt loop and
plain-text terminal experience. The host supplies configuration and optional
capabilities. Move deeper when the product needs its own presentation or
lifecycle.

### Conversation engine

Use `createConversationEngine` when the host owns presentation, commands,
approval UI, or session browsing. Heddle still owns the durable conversation
model. Call `engine.turns.submit(...)` for an in-process turn.

### Run service

Add one host-long-lived `ConversationRunService` when request and subscription
lifetimes differ, clients can reconnect, or a turn needs a separately
addressable cancel/approval lifecycle. It is a process-local coordinator above
the engine, not a transport or durable message broker.

### Transport adapter

Add a host-owned adapter when a remote client needs start, subscribe, cancel, or
approval operations. Validate all untrusted wire data and project internal run
results into an explicitly public schema. Authentication and authorization must
happen before resolving the Heddle run address.

### Client protocol and UI

Add a transport client only when it matches the host's API. Keep protocol
parsing, cursors, and abort propagation below product UI state. Keep messages,
tool rendering, retry UX, and product result handling in the application's
normal client architecture.

## Extension points

| Requirement | Extend here |
| --- | --- |
| Domain actions or data access | `ToolDefinition`, toolkits, or host extensions |
| MCP-backed capabilities | `prepareMcpHostExtension` |
| Prompt/system behavior | Engine `systemContext`, turn prompt formatting, or host extensions |
| Model and credentials | Host composition root / `createConversationEngine` config |
| Approval behavior | `ConversationEngineHost.approvals` and the product's policy/UI |
| Session/artifact storage | `ChatSessionRepository` and `ArtifactRepository` |
| Public API fields | Host-owned validation schemas and terminal-result projection |
| REST, tRPC, SSE, WebSocket, or IPC | Host transport adapter above the application service |
| React or other UI state | Product client/application layer above the protocol client |
| Multi-process live delivery | Host-selected shared routing/broker infrastructure |

## Instructions for coding agents

Before implementation, discover and state:

1. the host's server framework and transport;
2. how identity and tenancy are authenticated;
3. how product conversation IDs are persisted;
4. which UI/state layer consumes run events;
5. whether one process owns a run for its lifetime;
6. which repositories, approval policy, tools, and telemetry are production
requirements.

Then select the lowest matching layer, copy only the relevant example stages,
and preserve the dependency direction shown above. Prefer adapting the host's
existing framework over installing the sample stack. Do not move transport,
auth, or UI decisions into Heddle core.

Before handoff, verify at minimum:

- a second turn reuses the same durable Heddle session;
- disconnecting one subscriber does not cancel the run;
- reconnecting after sequence N does not restart the turn or duplicate earlier
events;
- a different authenticated scope cannot subscribe to or cancel the run;
- explicit cancellation reaches a terminal cancelled event;
- only intended public result fields cross the transport boundary.

Continue with the [programmatic guide index](README.md) for API details or the
[numbered SDK examples](../../../examples/sdk/README.md) for runnable code.
4 changes: 4 additions & 0 deletions docs/guides/programmatic/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ This is rung 1 of the programmatic ladder — the smallest working agent. See th
shape output → lifecycle → storage), and [`examples/sdk/`](../../../examples/sdk/README.md)
for runnable versions.

If your product already owns a server, transport, or UI, use the
[integration-layer chooser](integration-layers.md) to skip directly to the
lowest matching boundary instead of copying the terminal quickstart stack.

Use `runQuickstartConversationCli` when you want a working interactive conversation loop
before building a custom UI:

Expand Down
9 changes: 9 additions & 0 deletions examples/sdk/01-interactive-chat.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/**
* Stage 01: the smallest persisted conversational agent.
*
* Prerequisite: a configured Heddle model credential.
* Assumption: Heddle may own the local prompt loop and plain-text output.
* Shows: the default SDK experience before the host customizes capabilities,
* presentation, lifecycle, transport, or storage.
* Run: yarn example:sdk:interactive
*/
import { runQuickstartConversationCli } from '../../src/index.js';

await runQuickstartConversationCli();
10 changes: 8 additions & 2 deletions examples/sdk/02-add-a-tool.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
// Rung 2 — add a capability: hand the agent one of your own tools.
// Run: yarn example:sdk:add-tool
/**
* Stage 02: add one host-owned capability to the quickstart conversation.
*
* Prerequisite: stage 01's credential setup.
* Assumption: the host owns useful domain behavior and exposes it as a tool;
* Heddle owns registration, model invocation, approval integration, and traces.
* Run: yarn example:sdk:add-tool
*/
import { runQuickstartConversationCli, type ToolDefinition } from '../../src/index.js';

const currentTimeTool: ToolDefinition = {
Expand Down
12 changes: 8 additions & 4 deletions examples/sdk/03-add-an-mcp-server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Rung 2 — add a capability: expose an MCP server's tools to the agent.
// prepareMcpHostExtension writes MCP config/catalog under stateRoot, then
// returns a host extension you pass straight to the runner.
// Run: yarn example:sdk:add-mcp (edit the server command for a real server)
/**
* Stage 03: expose an existing MCP server as curated Heddle capabilities.
*
* Prerequisites: stage 01's credential setup plus a runnable MCP server.
* Assumption: the host selects/configures MCP; Heddle prepares its tool catalog,
* approval path, and host extension. Replace the demo command for production.
* Run: yarn example:sdk:add-mcp
*/
import { join } from 'node:path';
import { prepareMcpHostExtension, runQuickstartConversationCli } from '../../src/index.js';

Expand Down
12 changes: 9 additions & 3 deletions examples/sdk/04-custom-output.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// Rung 3 — shape input/output: drive the engine directly and send streaming
// text to your own destination instead of the default terminal writer.
// Run: yarn example:sdk:custom-output
/**
* Stage 04: keep Heddle's conversation engine and replace presentation.
*
* Prerequisite: stage 01's credential setup.
* Assumption: one process owns the turn while the host owns the output sink.
* Shows: the boundary to use for a custom terminal, webhook, log sink, or local
* application view before adding addressable/reconnectable run lifecycle.
* Run: yarn example:sdk:custom-output
*/
import { join } from 'node:path';
import { createConversationEngine, createConversationTextHost } from '../../src/index.js';

Expand Down
29 changes: 29 additions & 0 deletions examples/sdk/05-hosted-agent/01-hosted-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 01 Hosted Service

Start here when a TypeScript server, worker, desktop backend, or other long-lived
process should host a conversational agent without committing to a transport.
See the [full hosted-agent walkthrough](../README.md#01-hosted-service-heddle-engine-host-lifecycle)
for the runnable flow and production checklist.

## Assumptions

- Heddle owns conversation history, turns, tools, traces, artifacts, run events,
replay, and cancellation.
- The host owns authenticated account scope, stable product conversation IDs,
engine configuration, repositories, approval policy, and process lifetime.
- Active run handles and replay may live in one process. A distributed host must
add its own routing/shared-delivery design above this boundary.

## Read in this order

1. [`agent-service.ts`](agent-service.ts) — application-owned scope and lifecycle
composed over Heddle's `ConversationRunService`.
2. [`example-agent.ts`](example-agent.ts) — replaceable local composition and
demo policy.
3. [`run.ts`](run.ts) — disconnect, cursor replay, and explicit cancellation in
one process.

Keep this folder free of HTTP request/response types and UI state. If the host
already uses tRPC, Fastify, Hono, Nest, WebSocket, or IPC, adapt this service
directly in that stack. Continue to [02 HTTP/SSE API](../02-http-sse-api/) only
when Express + REST/SSE matches the host.
Loading
Loading