pi-mono/packages/durable/test/examples/14-chat.ts
Mario Zechner b56702ad34 feat(durable): extensions and per-conversation agents (Package 21)
- Registry of named extensions (tools, sections, hooks, wraps, tasks) with install/uninstall;
  defineExtension, defineTool, section, hook, wrapTool, wrapSection
- pi.agent document (model, thinking level, extensions, tools filter, instructions, cwd),
  configure(), Conversation.agent()/configure(), agent option on root/createConversation/fork,
  task-owned conversations copy their owner's agent
- Harness-wide HarnessSettings read at every use; env function built per use
- Hooks and the agent resolved per task phase; tool calls resolved with the tools filter applied
- ToolRegistration<TParameters, TDetails> types execute() args from parameters
- HarnessOptions.conversationCreated; Tx.submissionByRequest() and Tx.createSubmission()
- FileSystem.id; edit/write serialize per file system id and canonical path
- CodingTools extension (read, write, edit, bash)
- Examples 06-31 on the new API, README and CHANGELOG
2026-10-01 01:28:22 +02:00

45 lines
2 KiB
TypeScript

// A chat turn.
// Run from packages/durable:
// node --conditions=source --experimental-strip-types test/examples/14-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";
import { AssistantEntry, createRegistry, defineExtension, Harness, MemoryStorage, section } from "../../src/index.ts";
const context = BACKGROUND_CONTEXT;
// The faux provider stands in for a real model (see 16-real-model.ts).
const faux = fauxProvider();
const models = createModels();
models.setProvider(faux.provider);
faux.setResponses([fauxAssistantMessage("Paris.")]);
const registry = createRegistry();
registry.install(
defineExtension({
name: "terse",
sections: [section("preamble", () => "You answer in one word.", { tag: false })],
}),
);
const harness = await Harness.open(new MemoryStorage(), { models, registry }, context);
const root = await harness.root(context, { agent: { model: { provider: "faux", modelId: "faux-1" } } });
// submit() durably admits user input and returns a Submission. The built-in
// pi.generation task prepares the system prompt from the agent's sections,
// calls the model, and appends the answer; wait() resolves once the input is
// answered or has failed.
const capital = await root.submit({ type: "input", content: "Capital of France?" }, context);
const answered = await capital.wait(context);
if (answered.status === "done" && answered.type === "input") {
const entry = await root.commit((tx) => tx.entry(AssistantEntry, answered.answer), context);
const reply = entry?.model?.[0];
console.log("answer:", reply?.role === "assistant" ? reply.content : reply);
}
// The transcript holds the user input, the positional system prompt, and the answer.
const transcript = await root.entries({}, 10, undefined, context);
console.log(
"transcript:",
[...transcript.items].reverse().map((entry) => entry.kind),
);
await harness.close(context);