Runtime
pi-durable is a library, not a service
You host the process. Harness.open() binds one storage and one agent runtime. There is no daemon, no HTTP server, and no built-in Discord integration.
Harness owns one storage and the task scheduler. It runs generation tasks, tool tasks, compactions, and any custom tasks you install through the registry.
Conversation is an append-only transcript with its own agent settings. submit() admits an input and returns a Submission. wait() resolves when the answer is committed — or when the input is unanswered, with a reason.
viewState() is a read-only projection of committed state. It is not a source of truth and can be dropped at any time.
Storage is single-writer. One process owns a SQLite file. There is no cross-process lock, so two replicas against the same file will corrupt the assumptions, not just the data.
src/open.tspi-durable 1.3.0
import { Harness } from "@earendil-works/pi-durable";
import { openNodeSqliteStorage } from
"@earendil-works/pi-durable/storage/sqlite/node";
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
const harness = await Harness.open(
await openNodeSqliteStorage("./data/agent.sqlite"),
{ models, registry, env },
BACKGROUND_CONTEXT,
);
// one conversation per Discord thread
const conversation = await harness.createConversation({
ownership: { kind: "ownerless" },
agent: {
model: { provider: "deepseek", modelId: "deepseek-flash" },
cwd: "/workspace/project",
},
}, BACKGROUND_CONTEXT);
const submission = await conversation.submit({
type: "input",
content: "run the tests and fix the failure",
requestId: message.id,
}, BACKGROUND_CONTEXT);
const settled = await submission.wait(BACKGROUND_CONTEXT);
// settled.status: "done" | "unanswered"
Harness.openOne storage, one scheduler, one registry of tools and tasks.
ConversationOne thread's transcript and agent settings.
SubmissionDurable input with an idempotency key and a terminal status.
resume()Schedules tasks left pending by the previous process.