docs(durable): renumber examples

This commit is contained in:
Mario Zechner 2026-10-22 10:56:50 +00:00
parent 92ab536024
commit b9fb2b854b
29 changed files with 55 additions and 55 deletions

View file

@ -594,7 +594,7 @@ stream.start(async (events) => {
});
```
Events are derived from commits, one batch per commit, and apply on top of the snapshot. Message and tool updates carry deltas: text and thinking appends, appended tool-call argument text, and output trims and appends. When a consumer falls more than 100 batches behind, it receives a fresh `snapshot` event instead. See `test/examples/19-json.ts` for the full stream of one run.
Events are derived from commits, one batch per commit, and apply on top of the snapshot. Message and tool updates carry deltas: text and thinking appends, appended tool-call argument text, and output trims and appends. When a consumer falls more than 100 batches behind, it receives a fresh `snapshot` event instead. See `test/examples/18-json.ts` for the full stream of one run.
## Hooks
@ -670,8 +670,8 @@ Owned work belongs to its owner:
The examples show both patterns as product code:
- [`22-subagent-foreground.ts`](test/examples/22-subagent-foreground.ts): the tool above, returning the child's answer. The UI finds the child through the call's `details` and prints the child's events indented under the call.
- [`23-subagent-background.ts`](test/examples/23-subagent-background.ts): persistent subagents behind one `subagent` tool that spawns, messages (steer or follow-up), waits for, stops, and lists them. Each child is owned by a background anchor task, so the parent's Esc and idle waits never reach it. Each message is delivered by a background reporter task that posts the answer back to the parent as a follow-up input once it arrives; request IDs keep a restart from sending a message or a report twice.
- [`21-subagent-foreground.ts`](test/examples/21-subagent-foreground.ts): the tool above, returning the child's answer. The UI finds the child through the call's `details` and prints the child's events indented under the call.
- [`22-subagent-background.ts`](test/examples/22-subagent-background.ts): persistent subagents behind one `subagent` tool that spawns, messages (steer or follow-up), waits for, stops, and lists them. Each child is owned by a background anchor task, so the parent's Esc and idle waits never reach it. Each message is delivered by a background reporter task that posts the answer back to the parent as a follow-up input once it arrives; request IDs keep a restart from sending a message or a report twice.
## Child Tasks
@ -698,7 +698,7 @@ decide: async (task, runtime, context) => {
- **Finishing:** a task that finishes while work it owns is still running is `completing`: its outcome is decided, but it becomes terminal, and `waitForTask()` returns, only once that work is done. A failed or aborted outcome aborts that work first.
- **Aborting:** abort runs bottom-up. Aborting a task aborts the work it owns first, and its own abort handler starts only once that work is done, so each task undoes its own effects.
[`24-child-tasks.ts`](test/examples/24-child-tasks.ts) runs a checkout with four payments: a declined card, a cancelled checkout, and a restart while the payments run.
[`23-child-tasks.ts`](test/examples/23-child-tasks.ts) runs a checkout with four payments: a declined card, a cancelled checkout, and a restart while the payments run.
## Task Graph
@ -714,7 +714,7 @@ graph.start(async (value) => {
});
```
A task appears with the commit that creates it and leaves with the commit that makes it terminal. Statuses are the committed ones: `pending`, `running`, `waiting` (with `on` and `policy`), and `completing` (with the held outcome's status). After a restart, tasks that were `running` show as `pending` until they run again. Whether a pending task is blocked by a missing definition is not part of the graph; `harness.inspect()` reports that. The graph lists live tasks only: once a subagent's owner task is terminal, a later task in its conversation is a top-level node, and the conversation's `ConversationRecord.owner` (also in its view's `conversation`) links it to its parent. [`24-child-tasks.ts`](test/examples/24-child-tasks.ts) prints the checkout's tree while its payments run.
A task appears with the commit that creates it and leaves with the commit that makes it terminal. Statuses are the committed ones: `pending`, `running`, `waiting` (with `on` and `policy`), and `completing` (with the held outcome's status). After a restart, tasks that were `running` show as `pending` until they run again. Whether a pending task is blocked by a missing definition is not part of the graph; `harness.inspect()` reports that. The graph lists live tasks only: once a subagent's owner task is terminal, a later task in its conversation is a top-level node, and the conversation's `ConversationRecord.owner` (also in its view's `conversation`) links it to its parent. [`23-child-tasks.ts`](test/examples/23-child-tasks.ts) prints the checkout's tree while its payments run.
## Your Own State
@ -739,7 +739,7 @@ await root.commit(async (tx) => {
console.log(await harness.snapshot(Todos, root.id, context));
```
`harness.watchDoc()` watches one document like the view above. `HarnessOptions.conversationCreated(tx, conversation)` runs in every commit that creates or forks a conversation, including a tool's raw `tx.createConversation()`, so every conversation gets your documents; `init` in `createConversation()`, `fork()`, and `root()` writes per-call data in the same commit. An extension's tools, sections, and hooks read their own documents through `api` or `input.read`, and treat an absent one as its default ([`11-extension-state.ts`](test/examples/11-extension-state.ts)).
`harness.watchDoc()` watches one document like the view above. `HarnessOptions.conversationCreated(tx, conversation)` runs in every commit that creates or forks a conversation, including a tool's raw `tx.createConversation()`, so every conversation gets your documents; `init` in `createConversation()`, `fork()`, and `root()` writes per-call data in the same commit. An extension's tools, sections, and hooks read their own documents through `api` or `input.read`, and treat an absent one as its default ([`10-extension-state.ts`](test/examples/10-extension-state.ts)).
## Usage and Cost
@ -832,29 +832,29 @@ A failure closes the Harness by itself; `closed` settles once that close is done
Runnable examples live in [`test/examples`](test/examples). Run one from this package directory with:
```bash
node --conditions=source --experimental-strip-types test/examples/14-chat.ts
node --conditions=source --experimental-strip-types test/examples/13-chat.ts
```
| Example | Shows |
|---|---|
| [14-chat](test/examples/14-chat.ts) | One question and answer |
| [16-real-model](test/examples/16-real-model.ts) | Streaming an answer from OpenAI |
| [17-coding-tools](test/examples/17-coding-tools.ts) | A tool-using turn on JSONL storage |
| [18-print](test/examples/18-print.ts) | Print mode: submit a prompt, print the answer |
| [19-json](test/examples/19-json.ts) | JSON mode: agent events or raw view operations, on SQLite, JSONL, or memory |
| [20-inbox](test/examples/20-inbox.ts) | Steers, follow-ups, writes, and withdrawal while busy |
| [21-late-join](test/examples/21-late-join.ts) | Attaching a view and an event stream mid-run |
| [22-subagent-foreground](test/examples/22-subagent-foreground.ts) | A replay-safe subagent tool whose child the call owns, with the child's events under the call |
| [23-subagent-background](test/examples/23-subagent-background.ts) | Persistent subagents: spawn, steer, stop, list, answers reported back, restart-safe |
| [24-child-tasks](test/examples/24-child-tasks.ts) | A checkout that owns and waits for four payments: failFast, abort, restart |
| [25-compaction](test/examples/25-compaction.ts) | A long chat compacted in the background, manually, and after a context overflow |
| [26-coding-agent](test/examples/26-coding-agent.ts) | CodingTools, live settings from a settings object, an environment that follows the conversation's directory |
| [27-plan-mode](test/examples/27-plan-mode.ts) | A read-only plan mode as an extension with its own document, switched with `configure()` |
| [28-reviewer](test/examples/28-reviewer.ts) | A reviewer conversation with its own model, extensions, tools, directory, and review loop |
| [29-sandbox-per-conversation](test/examples/29-sandbox-per-conversation.ts) | An environment per conversation, looked up from an app document |
| [30-tool-override](test/examples/30-tool-override.ts) | A same-name bash for some conversations, and a wrapper that times whichever bash won |
| [31-reload-and-restart](test/examples/31-reload-and-restart.ts) | Reloading an extension mid-call, and stored choices surviving a restart |
| [00](test/examples/00-conversation.ts)–[13](test/examples/13-recovery.ts) | The layers underneath: sessions, documents, forks, watches, the Harness, agent configuration, reload, extension state, tasks, recovery |
| [13-chat](test/examples/13-chat.ts) | One question and answer |
| [15-real-model](test/examples/15-real-model.ts) | Streaming an answer from OpenAI |
| [16-coding-tools](test/examples/16-coding-tools.ts) | A tool-using turn on JSONL storage |
| [17-print](test/examples/17-print.ts) | Print mode: submit a prompt, print the answer |
| [18-json](test/examples/18-json.ts) | JSON mode: agent events or raw view operations, on SQLite, JSONL, or memory |
| [19-inbox](test/examples/19-inbox.ts) | Steers, follow-ups, writes, and withdrawal while busy |
| [20-late-join](test/examples/20-late-join.ts) | Attaching a view and an event stream mid-run |
| [21-subagent-foreground](test/examples/21-subagent-foreground.ts) | A replay-safe subagent tool whose child the call owns, with the child's events under the call |
| [22-subagent-background](test/examples/22-subagent-background.ts) | Persistent subagents: spawn, steer, stop, list, answers reported back, restart-safe |
| [23-child-tasks](test/examples/23-child-tasks.ts) | A checkout that owns and waits for four payments: failFast, abort, restart |
| [24-compaction](test/examples/24-compaction.ts) | A long chat compacted in the background, manually, and after a context overflow |
| [25-coding-agent](test/examples/25-coding-agent.ts) | CodingTools, live settings from a settings object, an environment that follows the conversation's directory |
| [26-plan-mode](test/examples/26-plan-mode.ts) | A read-only plan mode as an extension with its own document, switched with `configure()` |
| [27-reviewer](test/examples/27-reviewer.ts) | A reviewer conversation with its own model, extensions, tools, directory, and review loop |
| [28-sandbox-per-conversation](test/examples/28-sandbox-per-conversation.ts) | An environment per conversation, looked up from an app document |
| [29-tool-override](test/examples/29-tool-override.ts) | A same-name bash for some conversations, and a wrapper that times whichever bash won |
| [30-reload-and-restart](test/examples/30-reload-and-restart.ts) | Reloading an extension mid-call, and stored choices surviving a restart |
| [00](test/examples/00-conversation.ts)–[12](test/examples/12-recovery.ts) | The layers underneath: sessions, documents, forks, watches, the Harness, agent configuration, reload, extension state, tasks, recovery |
Examples that call OpenAI need `OPENAI_API_KEY`; most use the faux provider otherwise.

View file

@ -13,7 +13,7 @@ const session = createSession(new MemoryStorage());
// all of them exist or none do.
// A task definition needs a name, a version, the task's starting state, a
// handler for every phase, and an abort handler (12-tasks.ts runs a task).
// handler for every phase, and an abort handler (11-tasks.ts runs a task).
// This example only creates the task record; a plain Session never runs it.
const Supervisor = defineTask<null, { phase: "ready" }, null>({
name: "example.supervisor",

View file

@ -1,6 +1,6 @@
// Serialize asynchronous document work with a watch.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/05-watches.ts
// node --conditions=source --experimental-strip-types test/examples/04-watches.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { createSession, defineDoc, MemoryStorage } from "../../src/index.ts";

View file

@ -1,6 +1,6 @@
// Open a Harness with a registry of extensions.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/06-harness.ts
// node --conditions=source --experimental-strip-types test/examples/05-harness.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { Type } from "@earendil-works/pi-ai";
import { createModels } from "@earendil-works/pi-ai/models";

View file

@ -1,6 +1,6 @@
// Per-conversation agent choices and Harness-wide settings.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/07-configuration.ts
// node --conditions=source --experimental-strip-types test/examples/06-configuration.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { Type } from "@earendil-works/pi-ai";
import { createModels } from "@earendil-works/pi-ai/models";

View file

@ -1,6 +1,6 @@
// Conversation handles, typed entries, created conversations, and forks.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/08-harness-conversations.ts
// node --conditions=source --experimental-strip-types test/examples/07-harness-conversations.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { createModels } from "@earendil-works/pi-ai/models";
import { createRegistry, defineEntry, Harness, MemoryStorage } from "../../src/index.ts";

View file

@ -1,6 +1,6 @@
// Transcript history and model context.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/09-context.ts
// node --conditions=source --experimental-strip-types test/examples/08-context.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import type { AssistantMessage, Message, StopReason, ToolResultMessage } from "@earendil-works/pi-ai";
import { createModels } from "@earendil-works/pi-ai/models";

View file

@ -1,6 +1,6 @@
// Reload extension code through the registry.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/10-registry-reload.ts
// node --conditions=source --experimental-strip-types test/examples/09-registry-reload.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { Type } from "@earendil-works/pi-ai";
import { createModels } from "@earendil-works/pi-ai/models";

View file

@ -1,6 +1,6 @@
// An extension that keeps its own per-conversation document.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/11-extension-state.ts
// node --conditions=source --experimental-strip-types test/examples/10-extension-state.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { Type } from "@earendil-works/pi-ai";
import { createModels } from "@earendil-works/pi-ai/models";

View file

@ -1,6 +1,6 @@
// Run a durable task.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/12-tasks.ts
// node --conditions=source --experimental-strip-types test/examples/11-tasks.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { createModels } from "@earendil-works/pi-ai/models";
import { createRegistry, defineExtension, defineTask, Harness, MemoryStorage } from "../../src/index.ts";
@ -52,7 +52,7 @@ registry.install(defineExtension({ name: "payments", tasks: [Payment] }));
const harness = await Harness.open(new MemoryStorage(), { models: createModels(), registry }, context);
const root = await harness.root(context);
// Every task names its owner. This one belongs to the conversation; a task can
// also own child tasks and wait for them (24-child-tasks.ts).
// also own child tasks and wait for them (23-child-tasks.ts).
const paymentId = await root.commit(
(tx) => tx.createTask(Payment, { amount: 5 }, { ownership: { kind: "conversation" } }),
context,

View file

@ -1,6 +1,6 @@
// Close, reopen, and continue where a task stopped.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/13-recovery.ts
// node --conditions=source --experimental-strip-types test/examples/12-recovery.ts
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -1,6 +1,6 @@
// A chat turn.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/14-chat.ts
// node --conditions=source --experimental-strip-types test/examples/13-chat.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { createModels } from "@earendil-works/pi-ai/models";
import { fauxAssistantMessage, fauxProvider } from "@earendil-works/pi-ai/providers/faux";
@ -8,7 +8,7 @@ import { AssistantEntry, createRegistry, defineExtension, Harness, MemoryStorage
const context = BACKGROUND_CONTEXT;
// The faux provider stands in for a real model (see 16-real-model.ts).
// The faux provider stands in for a real model (see 15-real-model.ts).
const faux = fauxProvider();
const models = createModels();
models.setProvider(faux.provider);

View file

@ -1,6 +1,6 @@
// System prompt sections and per-conversation instructions.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/15-system-prompt.ts
// node --conditions=source --experimental-strip-types test/examples/14-system-prompt.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { createModels } from "@earendil-works/pi-ai/models";
import { fauxAssistantMessage, fauxProvider } from "@earendil-works/pi-ai/providers/faux";

View file

@ -1,6 +1,6 @@
// A real model: stream an answer from OpenAI.
// Run from packages/durable (needs OPENAI_API_KEY):
// node --conditions=source --experimental-strip-types test/examples/16-real-model.ts
// node --conditions=source --experimental-strip-types test/examples/15-real-model.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { createModels } from "@earendil-works/pi-ai/models";
import { openaiProvider } from "@earendil-works/pi-ai/providers/openai";

View file

@ -2,7 +2,7 @@
// `cat` of /tmp/1gb.txt (create it first to measure; without it that call ends in an error result). The storage
// directory is left behind for inspection.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/17-coding-tools.ts
// node --conditions=source --experimental-strip-types test/examples/16-coding-tools.ts
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -1,7 +1,7 @@
// Print mode: submit one prompt and print its answer, like `pi -p`. The host awaits its own Submission, not global
// idle. Uses OpenAI when OPENAI_API_KEY is set, and a scripted faux model otherwise.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/18-print.ts "What is in this directory?"
// node --conditions=source --experimental-strip-types test/examples/17-print.ts "What is in this directory?"
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import type { AssistantMessage } from "@earendil-works/pi-ai";
import { createModels } from "@earendil-works/pi-ai/models";

View file

@ -5,7 +5,7 @@
// neither is deleted, and the path is printed to stderr at the end.
// Uses OpenAI when OPENAI_API_KEY is set, and a scripted faux model otherwise.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/19-json.ts --events "What is in this directory?"
// node --conditions=source --experimental-strip-types test/examples/18-json.ts --events "What is in this directory?"
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -1,6 +1,6 @@
// The inbox: what happens to submissions while a conversation is busy.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/20-inbox.ts
// node --conditions=source --experimental-strip-types test/examples/19-inbox.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import type { FauxResponseStep } from "@earendil-works/pi-ai";
import { createModels } from "@earendil-works/pi-ai/models";

View file

@ -1,7 +1,7 @@
// Late join: a client attaches while a run is already underway. It gets the current state first, the conversation
// view or the snapshot event, and then only what changes after that.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/21-late-join.ts
// node --conditions=source --experimental-strip-types test/examples/20-late-join.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { Type } from "@earendil-works/pi-ai";
import { createModels } from "@earendil-works/pi-ai/models";

View file

@ -3,7 +3,7 @@
// details and shows its events under the call.
// Uses OpenAI when OPENAI_API_KEY is set, and a scripted faux model otherwise.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/22-subagent-foreground.ts
// node --conditions=source --experimental-strip-types test/examples/21-subagent-foreground.ts
import type { Context } from "@earendil-works/chord";
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { type AssistantMessage, type FauxResponseStep, Type } from "@earendil-works/pi-ai";

View file

@ -4,7 +4,7 @@
// survives a restart: the example closes the Harness while a subagent works and reopens it.
// Uses OpenAI when OPENAI_API_KEY is set, and a scripted faux model otherwise.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/23-subagent-background.ts
// node --conditions=source --experimental-strip-types test/examples/22-subagent-background.ts
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -1,7 +1,7 @@
// A task that owns child tasks: a checkout charges four payments at once and waits for them.
// The task graph view prints the checkout's tree while its payments run.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/24-child-tasks.ts
// node --conditions=source --experimental-strip-types test/examples/23-child-tasks.ts
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -1,6 +1,6 @@
// Compaction: a long trip-planning chat whose older messages are summarized so the model context stays small.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/25-compaction.ts
// node --conditions=source --experimental-strip-types test/examples/24-compaction.ts
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import type { AssistantMessage, Message, TranscriptContext } from "@earendil-works/pi-ai";
import { createModels } from "@earendil-works/pi-ai/models";

View file

@ -1,7 +1,7 @@
// A small coding agent: the built-in coding tools, a prompt that knows the working directory, settings backed by the
// app's settings file, and an environment that follows the conversation's directory.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/26-coding-agent.ts
// node --conditions=source --experimental-strip-types test/examples/25-coding-agent.ts
import { mkdir, mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -1,7 +1,7 @@
// Plan mode: the user switches a conversation into a read-only planning mode, the agent writes a plan into the
// extension's own document, and switching back restores the full tool set.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/27-plan-mode.ts
// node --conditions=source --experimental-strip-types test/examples/26-plan-mode.ts
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -1,7 +1,7 @@
// A reviewer agent next to the main one: a cheaper model, a review role and loop, read-only tools, and its own
// checkout of the project.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/28-reviewer.ts
// node --conditions=source --experimental-strip-types test/examples/27-reviewer.ts
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -2,7 +2,7 @@
// environment function looks it up for every tool call. Here a sandbox is a directory; a hosted product would return
// an ExecutionEnv that runs inside the conversation's container.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/29-sandbox-per-conversation.ts
// node --conditions=source --experimental-strip-types test/examples/28-sandbox-per-conversation.ts
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -1,7 +1,7 @@
// Replace a tool for some conversations, and decorate whichever tool wins: a bash that runs inside a Python
// virtualenv, and a wrapper that times every bash call.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/30-tool-override.ts
// node --conditions=source --experimental-strip-types test/examples/29-tool-override.ts
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

View file

@ -1,7 +1,7 @@
// Reload an extension while a call runs, then restart the process. Running work finishes on the code it started
// with; stored choices are names, so they survive a restart and bind to whatever code the new process installs.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/31-reload-and-restart.ts
// node --conditions=source --experimental-strip-types test/examples/30-reload-and-restart.ts
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";