agents 0.30.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/harness/pi/index.d.ts +3 -0
- package/dist/harness/pi/index.js +15 -5
- package/dist/harness/pi/index.js.map +1 -1
- package/docs/get-current-agent.md +4 -7
- package/docs/harnesses/pi.md +1 -1
- package/docs/human-in-the-loop.md +3 -7
- package/docs/index.md +2 -2
- package/docs/mcp-client.md +2 -28
- package/docs/mcp-servers.md +121 -414
- package/docs/mcp-transports.md +30 -289
- package/docs/routing.md +0 -2
- package/docs/securing-mcp-servers.md +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -509,8 +509,9 @@ export default {
|
|
|
509
509
|
} satisfies ExportedHandler;
|
|
510
510
|
```
|
|
511
511
|
|
|
512
|
-
|
|
513
|
-
|
|
512
|
+
Install `@modelcontextprotocol/server` alongside `agents`. See
|
|
513
|
+
[Creating MCP Servers](../../docs/agents/mcp-servers.md) for authentication,
|
|
514
|
+
elicitation, and keeping state in bindings.
|
|
514
515
|
|
|
515
516
|
### Using MCP Tools
|
|
516
517
|
|
|
@@ -108,6 +108,9 @@ type PiHarnessContext = {
|
|
|
108
108
|
* alarm. Without it the harness still works, but pi's timer keeps the
|
|
109
109
|
* object in memory, and billed, while a task sleeps, and the harness logs
|
|
110
110
|
* a warning when it opens pi.
|
|
111
|
+
*
|
|
112
|
+
* The harness reads pi's wake times against `Date.now()`, so do not pass
|
|
113
|
+
* `now` to `Harness.open`.
|
|
111
114
|
*/
|
|
112
115
|
readonly onWake: (wake: Wake) => void;
|
|
113
116
|
};
|
package/dist/harness/pi/index.js
CHANGED
|
@@ -361,23 +361,33 @@ async function _wakeStep() {
|
|
|
361
361
|
/**
|
|
362
362
|
* Wait while pi has work running, up to the wait budget. With `onWake`, a
|
|
363
363
|
* change ends each sleep early, and so does pi closing; without it, the
|
|
364
|
-
* sleep is `pollMs`.
|
|
365
|
-
*
|
|
364
|
+
* sleep is `pollMs`. A task sleeping beside the running work, such as a
|
|
365
|
+
* retry backoff, also ends the sleep when it is due, and the wait calls
|
|
366
|
+
* `pi.resume()` to run it: with `onWake`, pi arms no timer for it, and
|
|
367
|
+
* the wake job's alarm is not set for it while the job waits. Whether the
|
|
368
|
+
* wait ended, with pi idle or at the budget, rather than failing. Rejects
|
|
369
|
+
* with a memory-limit reset.
|
|
366
370
|
*/
|
|
367
371
|
async function _waitWhileActive(pi) {
|
|
368
372
|
const deadline = Date.now() + _classPrivateFieldGet2(_waitBudgetMs, this);
|
|
369
373
|
try {
|
|
370
|
-
|
|
371
|
-
|
|
374
|
+
let wake = pi.wake();
|
|
375
|
+
while (wake.active) {
|
|
376
|
+
const now = Date.now();
|
|
377
|
+
const left = deadline - now;
|
|
372
378
|
if (left <= 0) return true;
|
|
379
|
+
let sleep = _classPrivateFieldGet2(_onWakeWired, this) ? left : Math.min(left, _classPrivateFieldGet2(_pollMs, this));
|
|
380
|
+
if (wake.at !== void 0) sleep = Math.min(sleep, wake.at - now);
|
|
373
381
|
await new Promise((resolve) => {
|
|
374
|
-
const timer = setTimeout(resolve,
|
|
382
|
+
const timer = setTimeout(resolve, sleep);
|
|
375
383
|
_classPrivateFieldSet2(_changed, this, () => {
|
|
376
384
|
clearTimeout(timer);
|
|
377
385
|
resolve();
|
|
378
386
|
});
|
|
379
387
|
});
|
|
380
388
|
_classPrivateFieldSet2(_changed, this, void 0);
|
|
389
|
+
if (wake.at !== void 0 && Date.now() >= wake.at) pi.resume();
|
|
390
|
+
wake = pi.wake();
|
|
381
391
|
}
|
|
382
392
|
return true;
|
|
383
393
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/harness/pi/session-store.ts","../../../src/harness/pi/harness.ts","../../../src/harness/pi/skills.ts"],"sourcesContent":["import type { SqliteStorage } from \"@earendil-works/pi-durable/storage/sqlite\";\nimport { openDurableObjectSqliteStorage } from \"@earendil-works/pi-durable/storage/sqlite/cloudflare\";\n\n/**\n * pi's session store on a Durable Object's SQLite database.\n *\n * pi owns its sessions: conversations, entries, tasks, submissions, and\n * documents live in pi's own schema, created and migrated by pi's portable\n * `SqliteStorage` through pi's own Durable Object adapter. The store keeps\n * pi's tables under a prefix, so they cannot collide with the SDK's or the\n * host's tables in the same object.\n *\n * It is the Durable Object counterpart of `agents/sessions` for a harness\n * that brings its own session model, and has nothing pi-harness specific in\n * it: any harness built on pi-durable can open its storage with it.\n */\nexport type PiSessionStoreOptions = {\n /**\n * Prefix for every table and index pi creates. Must not start with `_cf_`,\n * which Durable Objects reserve. Default `pi_`.\n */\n readonly prefix?: string;\n};\n\nconst DEFAULT_PREFIX = \"pi_\";\n\n/** Open pi-durable's storage over this object's SQLite database. */\nexport function openPiSessionStore(\n storage: DurableObjectStorage,\n options: PiSessionStoreOptions = {}\n): Promise<SqliteStorage> {\n const prefix = options.prefix ?? DEFAULT_PREFIX;\n // pi validates the prefix as a SQLite identifier; Durable Objects also\n // reserve `_cf_` in any case, which pi does not know about.\n if (prefix.toLowerCase().startsWith(\"_cf_\")) {\n return Promise.reject(\n new Error(\"The pi session store prefix must not start with _cf_\")\n );\n }\n return openDurableObjectSqliteStorage(storage, { prefix });\n}\n","import type { Api, Model, ModelThinkingLevel } from \"@earendil-works/pi-ai\";\nimport {\n LiveDoc,\n ROOT_CONVERSATION_ID,\n watchEvents,\n type AgentEventStream,\n type Conversation,\n type ConversationId,\n type AgentChange,\n type EntryRecord,\n type Harness,\n type HarnessInspection,\n type ModelRef,\n type SettledSubmissionRecord,\n type TaskId,\n type UserInput,\n type Wake\n} from \"@earendil-works/pi-durable\";\nimport type { SqliteStorage } from \"@earendil-works/pi-durable/storage/sqlite\";\nimport {\n LifecycleCapability,\n type CapabilityStartContext,\n type LifecycleJobContext,\n type LifecycleJobOutcome\n} from \"../../lifecycle\";\nimport { isDurableObjectMemoryLimitReset } from \"../../retries\";\nimport {\n BACKGROUND_CONTEXT,\n withAbortSignal,\n type Context\n} from \"@earendil-works/chord/context\";\nimport { openPiSessionStore } from \"./session-store\";\nimport type {\n PiOperationResult,\n PiPendingOperation,\n PiPromptResponse,\n PiReceipt,\n PiSessionId,\n PiSessionInfo,\n PiSessionOptions,\n PiSubmitOptions\n} from \"./types\";\n\nconst BG = BACKGROUND_CONTEXT;\n\n/** The root session's id. */\nexport const ROOT_SESSION: PiSessionId = String(ROOT_CONVERSATION_ID);\n\n/**\n * The wake timings. They suit a real deployment; only tests change them,\n * with `setWakeTimingForTests`, so a suite does not sit on the real\n * heartbeat.\n *\n * Longest one wake waits on pi. It waits inside an alarm invocation, which\n * has a 15 minute wall-time limit, so a longer run is waited on across\n * several alarms.\n */\nconst WAIT_BUDGET_MS = 10 * 60_000;\n\n/**\n * The wake job's heartbeat while it waits, and how often it re-checks work\n * it cannot wait on, such as background tasks. If the object is evicted, the\n * job is still due and its alarm restarts the object.\n */\nconst HEARTBEAT_MS = 30_000;\n\n/**\n * How often a wait re-reads `pi.wake()` when the factory did not pass\n * `onWake`, so nothing tells the harness when it changes.\n */\nconst POLL_MS = 1_000;\n\n/**\n * The longest a wake job whose dispatch keeps failing waits before it tries\n * again. Its delay starts at the heartbeat and doubles up to this.\n */\nconst MAX_WAKE_BACKOFF_MS = 10 * 60_000;\n\n/**\n * Wake dispatches that failed in a row. Stored, so the backoff holds across\n * restarts, and outside the job: a push that saved it in the job's payload\n * could overwrite a submit's due-now push.\n */\nconst WAKE_ERRORS_KEY = \"pi-harness:wake-errors\";\n\nlet setWakeTiming: (harness: PiHarness, timing: WakeTiming) => void;\n\n/**\n * @internal Shorten a harness's wake timings, so a test suite does not sit\n * on the real heartbeat. Not exported from `agents/harness/pi`.\n */\nexport function setWakeTimingForTests(\n harness: PiHarness,\n timing: WakeTiming\n): void {\n setWakeTiming(harness, timing);\n}\n\nconst WAKE_FN = \"wake\";\n\n/**\n * The one wake job. Before Pi 1.4 there was one per session, with ids\n * `pi-wake:<session>`; a left-over one hands over to this one.\n */\nconst WAKE_JOB_ID = \"pi-wake\";\n\n/**\n * What the factory is handed: the things only the harness can build.\n * Everything else `Harness.open` takes (models, registry, settings, env,\n * onReport) the factory builds itself.\n */\nexport type PiHarnessContext = {\n /** pi's storage over this object's SQLite, its migrations already run. */\n readonly storage: SqliteStorage;\n /** Background context, for the open itself. */\n readonly context: Context;\n /**\n * Pass this to `Harness.open` as its `onWake` option. pi calls it when it\n * next needs to run, and the harness sets this object's alarm for then.\n *\n * With it, pi sets no in-memory timer for a sleeping task (a retry\n * backoff, a poll, a `sleepUntil`), so the object can hibernate until the\n * alarm. Without it the harness still works, but pi's timer keeps the\n * object in memory, and billed, while a task sleeps, and the harness logs\n * a warning when it opens pi.\n */\n readonly onWake: (wake: Wake) => void;\n};\n\n/**\n * Opens pi's `Harness`. The harness adopts what it returns. Pass `onWake`\n * through to `Harness.open`.\n *\n * ```ts\n * harness: async ({ storage, context, onWake }) => {\n * const registry = createRegistry();\n * registry.install(await skills(sources));\n * return Harness.open(storage, { models, registry, onWake }, context);\n * }\n * ```\n */\nexport type PiHarnessFactory = (\n context: PiHarnessContext\n) => Harness | Promise<Harness>;\n\n/**\n * A model, as pi-ai describes one: `createAI`'s `ai(\"@cf/…\")` returns one.\n *\n * Only its `provider` and `id` are used. pi stores that reference with\n * the session and resolves it, at each request, against the `Models` the\n * factory opened pi with. So the model must be one those `Models` list,\n * and options passed to `ai(id, options)` here, such as `fallback`, are not\n * applied.\n */\nexport type PiModel = Pick<Model<Api>, \"provider\" | \"id\">;\n\n/**\n * Applied to a session the first time it is created. Without a model, a\n * session's generation fails as unanswered until `session.setModel` sets one.\n */\nexport type PiSessionDefaults = {\n /**\n * Model for new sessions, by its `provider` and `id`; it must be on the\n * `Models` the factory opens pi with (see `PiModel`). Change one\n * session's with `session.setModel`.\n */\n readonly model?: PiModel;\n readonly thinkingLevel?: ModelThinkingLevel;\n};\n\n/** How long the wake waits on pi, and how often it checks. */\ntype WakeTiming = {\n /** Longest one wake waits inside an alarm. Default 600_000. */\n readonly waitBudgetMs?: number;\n /** Heartbeat while waiting. Default 30_000. */\n readonly heartbeatMs?: number;\n /** How often a wait re-reads `pi.wake()` without `onWake`. Default 1_000. */\n readonly pollMs?: number;\n};\n\n/** `PiHarness`'s options. Only `harness`, which opens pi, is required. */\nexport type PiHarnessOptions = {\n /** Opens pi's `Harness` over the storage this object prepared. */\n readonly harness: PiHarnessFactory;\n /** What a new session starts with. */\n readonly defaults?: PiSessionDefaults;\n};\n\ntype Opened = {\n readonly pi: Harness;\n readonly storage: SqliteStorage;\n};\n\n/** pi-durable's stored reference to a pi-ai model. */\nfunction modelRef(model: PiModel): ModelRef {\n return { provider: model.provider, modelId: model.id };\n}\n\nfunction conversationId(session: PiSessionId): ConversationId {\n const id = Number(session);\n if (!Number.isSafeInteger(id) || id < 1) {\n throw new Error(`Invalid pi session ${JSON.stringify(session)}`);\n }\n return id as ConversationId;\n}\n\n/** The text of an assistant entry, for an operation's result. */\nfunction assistantText(entry: EntryRecord | undefined): string {\n const message = entry?.model?.[0];\n if (message?.role !== \"assistant\") return \"\";\n return message.content\n .map((part) => (part.type === \"text\" ? part.text : \"\"))\n .join(\"\");\n}\n\nfunction signalContext(signal: AbortSignal | undefined): Context {\n return signal ? withAbortSignal(signal, BG) : BG;\n}\n\n/**\n * pi-durable hosted in a Durable Object, behind a small harness interface:\n * `harness.prompt()`, `harness.sessions`, `harness.session(id)`. How a session reaches a\n * client (sockets, SSE, RPC) is the host's glue, built on `session.events()`.\n *\n * pi owns everything about a run: the transcript, the inbox of steers and\n * follow-ups, generation and tool tasks, retries, crash recovery, and the\n * live view. It keeps all of it in its own tables in this object's SQLite\n * database (see `session-store.ts`).\n *\n * What pi cannot do on a Durable Object is wake itself: its scheduler runs\n * in memory, and an evicted object has no memory. The harness is that wake,\n * with one Lifecycle job driven by `pi.wake()`. Input goes to pi once, in\n * `submit()`, after the job is scheduled. While pi has work running, the\n * job waits on it, rescheduling itself as a heartbeat. While pi's tasks\n * sleep, the job is due when the first one wakes; pi tells the harness\n * through the `onWake` the factory passes to `Harness.open`. When pi has\n * nothing to do, the job completes. An eviction mid-run leaves the job due,\n * so its alarm restarts the object, pi reopens and resumes its own tasks,\n * and the job waits again.\n *\n * @beta The API may change between releases.\n */\nexport class PiHarness extends LifecycleCapability {\n readonly sessions: PiSessions;\n readonly #options: PiHarnessOptions;\n /** The in-memory wait on pi's running work, inside an alarm's work. */\n #waiting: Promise<void> | undefined;\n /** Resolves the current wait's sleep early: `pi.wake()` changed. */\n #changed: (() => void) | undefined;\n /** Submissions between their wake and pi's admission. */\n #admitting = 0;\n /** Whether pi has called `onWake`, so the factory passed it. */\n #onWakeWired = false;\n #waitBudgetMs = WAIT_BUDGET_MS;\n #heartbeatMs = HEARTBEAT_MS;\n #pollMs = POLL_MS;\n #opening: Promise<Opened> | undefined;\n /** Settles once the last pi that closed on its own has closed. */\n #lastClose: Promise<unknown> = Promise.resolve();\n /** Every pi that has begun closing, on its own or by `dispose()`. */\n readonly #closing = new WeakSet<Harness>();\n\n static {\n setWakeTiming = (harness, timing) => harness.#setWakeTiming(timing);\n }\n\n constructor(options: PiHarnessOptions) {\n super(\"pi-harness\");\n this.#options = options;\n this.sessions = new PiSessions(this);\n }\n\n #setWakeTiming(timing: WakeTiming): void {\n for (const [name, value] of Object.entries(timing)) {\n if (!Number.isFinite(value) || value <= 0) {\n throw new Error(`PiHarness timing.${name} must be a positive number`);\n }\n }\n this.#waitBudgetMs = timing.waitBudgetMs ?? this.#waitBudgetMs;\n this.#heartbeatMs = timing.heartbeatMs ?? this.#heartbeatMs;\n this.#pollMs = timing.pollMs ?? this.#pollMs;\n }\n\n // ── Lifecycle ────────────────────────────────────────────────────────────\n\n override async onStart(_context: CapabilityStartContext): Promise<void> {\n await this.#open();\n }\n\n async onJob(\n context: LifecycleJobContext\n ): Promise<LifecycleJobOutcome | void> {\n if (context.job.fn !== WAKE_FN) return;\n let outcome: LifecycleJobOutcome;\n if (context.job.id !== WAKE_JOB_ID) {\n // A per-session wake from before Pi 1.4: hand over to the one wake.\n await this.#wake();\n outcome = undefined;\n } else {\n outcome = await this.#wakeStep();\n }\n // It got through, so a failing wake's backoff starts over.\n const kv = this.lifecycle.storage.kv;\n if (kv.get(WAKE_ERRORS_KEY) !== undefined) kv.delete(WAKE_ERRORS_KEY);\n return outcome;\n }\n\n /**\n * A wake dispatch that failed through Lifecycle's retries, as when pi\n * cannot be opened. Lifecycle would complete the job, and pi's work would\n * never be woken again, so try again later: at the heartbeat, doubling\n * while the failures continue, up to 10 minutes.\n */\n onJobError(\n context: LifecycleJobContext,\n error: unknown\n ): LifecycleJobOutcome | void {\n if (context.job.fn !== WAKE_FN) return;\n console.error(\n `pi wake job \"${context.job.id}\" failed after ${context.attempt} ` +\n \"attempts; retrying with backoff\",\n error\n );\n this.#wakeError(error);\n const kv = this.lifecycle.storage.kv;\n const errors = (kv.get<number>(WAKE_ERRORS_KEY) ?? 0) + 1;\n kv.put(WAKE_ERRORS_KEY, errors);\n const delay = Math.min(\n this.#heartbeatMs * 2 ** (errors - 1),\n MAX_WAKE_BACKOFF_MS\n );\n return { rescheduleAt: Date.now() + delay };\n }\n\n /** Close pi's in-memory resources. Durable state is untouched. */\n async dispose(): Promise<void> {\n const opening = this.#opening;\n this.#opening = undefined;\n const opened = await opening?.catch(() => undefined);\n // Closing calls #piClosing, which also ends a wait sleeping on this pi.\n await opened?.pi.close(BG);\n }\n\n // ── The harness interface ────────────────────────────────────────────────\n\n /** A handle on one session. No I/O until you call it. */\n session(id: PiSessionId = ROOT_SESSION): PiSession {\n return new PiSession(this, id);\n }\n\n /** Submit a prompt and wait for its answer. */\n prompt(\n input: UserInput,\n options: PiSubmitOptions = {}\n ): Promise<PiPromptResponse> {\n return this.session(options.session).prompt(input, options);\n }\n\n /** Durably submit a prompt. Resolves before the model runs. */\n submit(input: UserInput, options: PiSubmitOptions = {}): Promise<PiReceipt> {\n return this.session(options.session).submit(input, options);\n }\n\n /** Stop one operation, or everything running in a session. */\n abort(\n options: PiSessionOptions & { readonly operationId?: string } = {}\n ): Promise<boolean> {\n return this.session(options.session).abort(options.operationId);\n }\n\n /**\n * Run again the tasks pi's crash-loop guard parked in a session's work.\n * False when nothing was parked.\n */\n retry(options: PiSessionOptions = {}): Promise<boolean> {\n return this.session(options.session).retry();\n }\n\n wait(\n operationId: string,\n options: PiSessionOptions & { readonly signal?: AbortSignal } = {}\n ): Promise<PiOperationResult> {\n return this.session(options.session).wait(operationId, options.signal);\n }\n\n /** The session's active transcript, as pi's entries. */\n messages(options: PiSessionOptions = {}): Promise<EntryRecord[]> {\n return this.session(options.session).messages();\n }\n\n /** Submissions pi has not settled yet, oldest first. */\n async pending(options: PiSessionOptions = {}): Promise<PiPendingOperation[]> {\n const { pi } = await this.#open();\n const id =\n options.session === undefined\n ? undefined\n : conversationId(options.session);\n const inspection = await pi.inspect(BG);\n const parked = await this.#parked(inspection);\n return inspection.submissions\n .filter(\n (record) =>\n record.requestId !== undefined &&\n (id === undefined || record.conversationId === id)\n )\n .map((record) => ({\n operationId: record.requestId as string,\n session: String(record.conversationId),\n status:\n record.status === \"queued\"\n ? \"queued\"\n : parked.some((task) => task.sessions.has(record.conversationId))\n ? \"parked\"\n : \"running\"\n }));\n }\n\n /** The opened pi Harness, for anything the interface does not cover. */\n async pi(): Promise<Harness> {\n return (await this.#open()).pi;\n }\n\n // ── Used by PiSession and PiSessions ─────────────────────────────────────\n\n /** @internal */\n async conversation(session: PiSessionId): Promise<Conversation> {\n const { pi } = await this.#open();\n const conversation = await pi.conversation(conversationId(session), BG);\n if (!conversation) throw new Error(`Unknown pi session ${session}`);\n return conversation;\n }\n\n /** @internal */\n async storage(): Promise<SqliteStorage> {\n return (await this.#open()).storage;\n }\n\n /** @internal */\n async enqueue(\n session: PiSessionId,\n input: UserInput,\n options: PiSubmitOptions\n ): Promise<PiReceipt> {\n const operationId = options.operationId ?? crypto.randomUUID();\n const { storage } = await this.#open();\n const conversation = await this.conversation(session);\n const accepted = await this.#admit(async () => {\n // The one admission. pi deduplicates by request id.\n const accepted =\n (await storage.submissionByRequest(\n conversation.id,\n operationId,\n BG\n )) === undefined;\n await conversation.submit(\n {\n type: \"input\",\n content: input,\n whenBusy: options.whenBusy ?? \"followUp\",\n requestId: operationId,\n ...(options.entry ? { entry: options.entry } : {})\n },\n BG\n );\n return accepted;\n });\n return { operationId, session, accepted };\n }\n\n /** @internal Run again the tasks pi's restart limit parked in a session. */\n async retryParked(session: PiSessionId): Promise<boolean> {\n const { pi } = await this.#open();\n const id = conversationId(session);\n const parked = (await this.#parked(await pi.inspect(BG))).filter((task) =>\n task.sessions.has(id)\n );\n if (parked.length === 0) return false;\n await this.#admit(async () => {\n for (const task of parked) await pi.retryTask(task.id, BG);\n });\n return true;\n }\n\n /** @internal Withdraw a queued input, or abort the run it joined. */\n async withdraw(session: PiSessionId, operationId: string): Promise<boolean> {\n const { pi, storage } = await this.#open();\n const id = conversationId(session);\n const record = await storage.submissionByRequest(id, operationId, BG);\n if (!record || record.status === \"done\" || record.status === \"unanswered\") {\n return false;\n }\n const withdrawn = await pi.abortSubmission(record.id, BG, id);\n if (withdrawn === \"already_placed\") {\n await (await pi.conversation(id, BG))?.abort(BG);\n }\n return withdrawn !== \"settled\" && withdrawn !== \"not_found\";\n }\n\n /** @internal Wait for pi to settle an operation, by its request id. */\n async settled(\n session: PiSessionId,\n operationId: string,\n signal?: AbortSignal\n ): Promise<PiOperationResult> {\n const conversation = await this.conversation(session);\n const context = signalContext(signal);\n const submission = await this.#findSubmission(\n conversation.id,\n operationId,\n context\n );\n if (!submission) {\n return {\n operationId,\n session,\n status: \"unanswered\",\n reason: \"not_found\"\n };\n }\n return this.#result(\n session,\n operationId,\n await submission.wait(context),\n context\n );\n }\n\n // ── The wake ─────────────────────────────────────────────────────────────\n\n /**\n * Hand pi work inside the wake. The wake is scheduled first, so if the\n * object dies after pi has the work, that job restarts it; while `hand`\n * is in flight the step will not complete. The wake steps again after, so\n * it sees the work even if it just completed.\n */\n async #admit<T>(hand: () => Promise<T>): Promise<T> {\n this.#admitting++;\n let result: T;\n try {\n await this.#wake();\n result = await hand();\n } finally {\n this.#admitting--;\n }\n await this.#wake();\n return result;\n }\n\n /** Schedule the wake job now, or at `time`. */\n #wake(time = Date.now()): Promise<unknown> {\n // A push made while the job is dispatching supersedes that dispatch's\n // outcome, so a submit is never lost to a wake that is completing.\n return this.lifecycle.jobs.push({\n id: WAKE_JOB_ID,\n fn: WAKE_FN,\n time,\n singleflight: true,\n recoveryLoop: true\n });\n }\n\n /** Schedule the wake job for what `pi.wake()` says, if pi needs one. */\n async #wakeFor(wake: Wake): Promise<void> {\n if (wake.active) await this.#wake();\n else if (wake.at !== undefined) await this.#wake(wake.at);\n }\n\n /**\n * pi's `onWake`, handed to the factory. pi calls it after open and\n * whenever `pi.wake()` changes. It must not block, so the job push is\n * left running.\n */\n readonly #onWake = (wake: Wake): void => {\n this.#onWakeWired = true;\n this.#changed?.();\n // A running wait re-reads pi.wake() and steps again when it ends.\n if (this.#waiting) return;\n this.#wakeFor(wake).catch((error: unknown) => this.#wakeError(error));\n };\n\n /**\n * One run of the wake job. It never admits or replays anything: it reads\n * `pi.wake()`, which is all of pi's sessions at once. Running work is\n * waited on; a sleep reschedules the job for when it ends; no work\n * completes the job. pi does all the work in between.\n */\n async #wakeStep(): Promise<LifecycleJobOutcome> {\n const heartbeat = { rescheduleAt: Date.now() + this.#heartbeatMs };\n if (this.#waiting) return heartbeat;\n const { pi } = await this.#open();\n // The alarm may be for a sleeping task: make due ones runnable.\n pi.resume();\n const wake = pi.wake();\n if (wake.active) {\n const wait = this.#waitWhileActive(pi).then(\n (ended) => {\n this.#waiting = undefined;\n // Step again now: pi may be sleeping, or have nothing left. After\n // a failed wait, as when pi's Session fails, the heartbeat that is\n // already due steps instead, so a pi that keeps failing is not\n // reopened in a loop.\n if (ended) void this.#wake();\n },\n (error: unknown) => {\n // A memory-limit reset: the alarm's breaker backs the job off.\n this.#waiting = undefined;\n throw error;\n }\n );\n this.#waiting = wait;\n // The wait runs past this dispatch, inside the alarm's work, so the\n // object stays alive for it, and a memory-limit reset it ends with\n // counts against the alarm's breaker. The heartbeat covers an\n // eviction.\n if (!this.lifecycle.trackAlarmWork(wait)) wait.catch(() => {});\n return heartbeat;\n }\n // A submit between its wake and pi's admission: check again later.\n if (this.#admitting > 0) return heartbeat;\n if (wake.at !== undefined) return { rescheduleAt: wake.at };\n // Nothing to do. A task parked by pi's restart limit is not work: it\n // waits for `retry()`.\n return undefined;\n }\n\n /**\n * Wait while pi has work running, up to the wait budget. With `onWake`, a\n * change ends each sleep early, and so does pi closing; without it, the\n * sleep is `pollMs`. Whether the wait ended, with pi idle or at the\n * budget, rather than failing. Rejects with a memory-limit reset.\n */\n async #waitWhileActive(pi: Harness): Promise<boolean> {\n const deadline = Date.now() + this.#waitBudgetMs;\n try {\n // Once pi begins closing, pi.wake() throws (SessionFailed after a\n // storage error), which is what ends this loop: a closed pi's wake does\n // not go inactive. Closing kicks `#changed` only so the loop reads it\n // again now rather than at the end of its sleep.\n while (pi.wake().active) {\n const left = deadline - Date.now();\n if (left <= 0) return true;\n await new Promise<void>((resolve) => {\n const timer = setTimeout(\n resolve,\n this.#onWakeWired ? left : Math.min(left, this.#pollMs)\n );\n this.#changed = () => {\n clearTimeout(timer);\n resolve();\n };\n });\n this.#changed = undefined;\n }\n return true;\n } catch (error) {\n // A pi that began closing is expected to throw; it is no longer the\n // harness's pi, and Pi reports a failure itself (`session_failed`).\n if (!this.#closing.has(pi)) this.#wakeError(error);\n // Only Lifecycle's alarm breaker can act on a memory-limit reset,\n // including one that failed pi's Session (SessionFailed's cause).\n if (isDurableObjectMemoryLimitReset(error)) throw error;\n return false;\n }\n }\n\n #wakeError(error: unknown): void {\n this.lifecycle.events.emit(\"pi:wake_error\", {\n error: error instanceof Error ? error.message : String(error)\n });\n }\n\n // ── pi ───────────────────────────────────────────────────────────────────\n\n /**\n * pi, opened. Waits for Lifecycle startup first, so pi is only ever\n * opened inside it: startup holds the input gate (`blockConcurrencyWhile`)\n * and awaits the open in `onStart`, so an open begun before startup would\n * be awaited behind a closed gate, with its timers and I/O held back, and\n * never finish.\n *\n * pi stays open until `dispose()`, or until it closes on its own: any\n * storage error fails pi's Session for good. Then the next call, or the\n * wake job's next step, opens pi again once the failed pi has closed, and\n * pi recovers from what it had committed. Closing waits for running task\n * code to return, so code that ignores its abort signal delays the reopen.\n * An open that fails is not kept either, so the next call tries again.\n */\n async #open(): Promise<Opened> {\n await this.lifecycle.ready();\n if (this.#opening === undefined) {\n const opening: Promise<Opened> = this.#doOpen((pi, closed) =>\n this.#piClosing(opening, pi, closed)\n ).catch((error: unknown) => {\n if (this.#opening === opening) this.#opening = undefined;\n throw error;\n });\n this.#opening = opening;\n }\n return this.#opening;\n }\n\n async #doOpen(\n onClose: (pi: Harness, closed: Promise<unknown>) => void\n ): Promise<Opened> {\n // A pi that closed on its own closes its storage last, after its tasks\n // have returned; let it finish before another opens over the same\n // database.\n await this.#lastClose;\n const storage = await openPiSessionStore(this.lifecycle.storage);\n const pi = await this.#options.harness({\n storage,\n context: BG,\n onWake: this.#onWake\n });\n try {\n const closed = pi.closed;\n // Called synchronously when pi begins closing, unlike `closed`, so a\n // closed pi is never handed out. It must not call pi.\n pi.subscribeClose(() => onClose(pi, closed));\n await pi.root(BG, { agent: this.agentDefaults() });\n // Continue whatever the last pi left, in this isolate or the last one:\n // pi reconciles tasks that were running to pending and schedules them\n // again.\n pi.resume();\n // pi calls onWake right after open, from a microtask.\n await new Promise((resolve) => setTimeout(resolve, 0));\n if (!this.#onWakeWired) {\n console.warn(\n \"PiHarness: the harness factory did not pass onWake to Harness.open, \" +\n \"so pi's timers keep this Durable Object in memory while a task \" +\n \"sleeps. Pass it: harness: ({ storage, context, onWake }) => \" +\n \"Harness.open(storage, { ...options, onWake }, context)\"\n );\n }\n // Whatever the last pi left gets a wake, including work whose wake\n // failed out. With `onWake`, pi has already said so; this covers a\n // factory without it. The job id is fixed, so this is idempotent.\n await this.#wakeFor(pi.wake());\n } catch (error) {\n // pi is open and running its tasks: close it, so the next open does\n // not start a second pi over the same database beside it.\n pi.close(BG).catch(() => {});\n throw error;\n }\n return { pi, storage };\n }\n\n /**\n * pi began closing: on its own, as a storage error makes it, or by\n * `dispose()`.\n */\n #piClosing(\n opening: Promise<Opened>,\n pi: Harness,\n closed: Promise<unknown>\n ): void {\n this.#closing.add(pi);\n // End a wait sleeping on this pi now: pi will not call onWake again.\n this.#changed?.();\n // `dispose()` took its pi out of `#opening` already, and an open begun\n // since must stay.\n if (this.#opening !== opening) return;\n // Drop it, so the next call opens pi again once this one has closed.\n this.#opening = undefined;\n this.#lastClose = closed;\n }\n\n /** @internal The per-conversation defaults supported by pi-durable. */\n agentDefaults(): AgentChange {\n const defaults = this.#options.defaults;\n return {\n ...(defaults?.model === undefined\n ? {}\n : { model: modelRef(defaults.model) }),\n ...(defaults?.thinkingLevel === undefined\n ? {}\n : { thinkingLevel: defaults.thinkingLevel })\n };\n }\n\n /**\n * Tasks pi's restart limit parked, each with the sessions whose work it\n * is: its own, and every session that owns that one, such as the session\n * of a tool that started a subagent.\n */\n async #parked(\n inspection: HarnessInspection\n ): Promise<{ id: TaskId; sessions: Set<ConversationId> }[]> {\n const { storage } = await this.#open();\n const parked: { id: TaskId; sessions: Set<ConversationId> }[] = [];\n for (const { record, state } of inspection.tasks) {\n if (state.kind !== \"blocked\" || state.reason !== \"restart_limit\") {\n continue;\n }\n const sessions = new Set<ConversationId>();\n let id: ConversationId | undefined = record.conversationId;\n while (id !== undefined && !sessions.has(id)) {\n sessions.add(id);\n id = (await storage.conversation(id, BG))?.owner?.conversationId;\n }\n parked.push({ id: record.id, sessions });\n }\n return parked;\n }\n\n async #findSubmission(\n id: ConversationId,\n operationId: string,\n context: Context\n ) {\n const { pi, storage } = await this.#open();\n // Read from pi's storage: the Harness only reacquires submissions by id.\n const record = await storage.submissionByRequest(id, operationId, context);\n return record ? pi.submission(record.id, context) : undefined;\n }\n\n async #result(\n session: PiSessionId,\n operationId: string,\n settled: SettledSubmissionRecord,\n context: Context\n ): Promise<PiOperationResult> {\n if (settled.status === \"unanswered\") {\n return {\n operationId,\n session,\n status: \"unanswered\",\n reason: settled.reason\n };\n }\n if (settled.type !== \"input\")\n return { operationId, session, status: \"done\" };\n const conversation = await this.conversation(session);\n const page = await conversation.entries(\n { minEntryId: settled.answer, maxEntryId: settled.answer },\n 1,\n undefined,\n context\n );\n return {\n operationId,\n session,\n status: \"done\",\n text: assistantText(page.items[0])\n };\n }\n}\n\n/** One pi conversation, addressed through the harness. */\nexport class PiSession {\n readonly #harness: PiHarness;\n readonly id: PiSessionId;\n\n constructor(harness: PiHarness, id: PiSessionId) {\n this.#harness = harness;\n this.id = id;\n }\n\n /** Durably submit a prompt. Resolves before the model runs. */\n submit(input: UserInput, options: PiSubmitOptions = {}): Promise<PiReceipt> {\n return this.#harness.enqueue(this.id, input, options);\n }\n\n /** Submit and wait for the answer and the updated transcript. */\n async prompt(\n input: UserInput,\n options: PiSubmitOptions = {}\n ): Promise<PiPromptResponse> {\n const receipt = await this.submit(input, options);\n const result = await this.wait(receipt.operationId);\n return { ...result, messages: await this.messages() };\n }\n\n /** Join the running work after its current tool round. */\n steer(input: UserInput, options: Omit<PiSubmitOptions, \"whenBusy\"> = {}) {\n return this.submit(input, { ...options, whenBusy: \"steer\" });\n }\n\n /** Wait for an operation to settle. Aborting `signal` stops only the wait. */\n wait(operationId: string, signal?: AbortSignal): Promise<PiOperationResult> {\n return this.#harness.settled(this.id, operationId, signal);\n }\n\n /**\n * Withdraw one queued operation (or abort the run it joined), or, with no\n * id, abort the session: pi withdraws queued inputs and aborts the\n * running work.\n */\n async abort(operationId?: string): Promise<boolean> {\n if (operationId !== undefined) {\n return this.#harness.withdraw(this.id, operationId);\n }\n const conversation = await this.#harness.conversation(this.id);\n await conversation.abort(BG);\n return true;\n }\n\n /**\n * Run again the tasks pi's crash-loop guard (`settings.restartLimit`)\n * parked in this session's work, including subagent sessions it owns.\n * False when nothing was parked.\n */\n retry(): Promise<boolean> {\n return this.#harness.retryParked(this.id);\n }\n\n /** Start a new context, optionally from a handoff note. */\n async reset(handoff?: string): Promise<void> {\n await (await this.#harness.conversation(this.id)).reset(handoff, BG);\n }\n\n /**\n * Change this session's model, by its `provider` and `id`; it must be on\n * the `Models` the factory opened pi with (see `PiModel`).\n */\n async setModel(model: PiModel): Promise<void> {\n await (\n await this.#harness.conversation(this.id)\n ).configure({ model: modelRef(model) }, BG);\n }\n\n /** The active transcript, as pi's entries since the newest reset. */\n async messages(): Promise<EntryRecord[]> {\n const view = await (await this.#harness.conversation(this.id)).context(BG);\n return [...view.entries];\n }\n\n /** pi's agent events for this session: a snapshot, then one batch per commit. */\n async events(context: Context = BG): Promise<AgentEventStream> {\n const pi = await this.#harness.pi();\n return watchEvents(pi, conversationId(this.id), context);\n }\n\n async busy(): Promise<boolean> {\n const pi = await this.#harness.pi();\n const live = await pi.snapshot(LiveDoc, conversationId(this.id), BG);\n return live?.run !== undefined;\n }\n}\n\n/** Every pi conversation in this object. */\nexport class PiSessions {\n readonly #harness: PiHarness;\n\n constructor(harness: PiHarness) {\n this.#harness = harness;\n }\n\n get(id: PiSessionId): PiSession {\n return this.#harness.session(id);\n }\n\n /** A new top-level session, configured like the root. */\n async create(): Promise<PiSession> {\n const pi = await this.#harness.pi();\n const conversation = await pi.createConversation(\n {\n ownership: { kind: \"ownerless\" },\n agent: this.#harness.agentDefaults()\n },\n BG\n );\n return this.#harness.session(String(conversation.id));\n }\n\n /** A new session that sees `from`'s history up to its newest entry. */\n async fork(from: PiSessionId): Promise<PiSession> {\n const conversation = await this.#harness.conversation(from);\n const newest = await conversation.entries({}, 1, undefined, BG);\n const at = newest.items[0];\n if (!at) throw new Error(`Session ${from} has no entries to fork from`);\n const fork = await conversation.fork(\n at.id,\n { ownership: { kind: \"ownerless\" } },\n BG\n );\n return this.#harness.session(String(fork.id));\n }\n\n /**\n * Every conversation, including ones subagent tools own. Read from pi's\n * storage directly: the Harness has no conversation listing.\n */\n async list(): Promise<PiSessionInfo[]> {\n const storage = await this.#harness.storage();\n const pi = await this.#harness.pi();\n const sessions: PiSessionInfo[] = [];\n let cursor: Parameters<SqliteStorage[\"scanConversations\"]>[2];\n for (;;) {\n const page = await storage.scanConversations({}, 100, cursor, BG);\n for (const record of page.items) {\n const live = await pi.snapshot(LiveDoc, record.id, BG);\n sessions.push({\n id: String(record.id),\n ...(record.parent\n ? { parent: String(record.parent.conversationId) }\n : {}),\n busy: live?.run !== undefined\n });\n }\n if (page.next === undefined) return sessions;\n cursor = page.next;\n }\n }\n}\n","import { Type } from \"@earendil-works/pi-ai\";\nimport type {\n Extension,\n ToolExecutionResult,\n ToolRegistration\n} from \"@earendil-works/pi-durable\";\nimport type {\n SkillContent,\n SkillDescriptor,\n SkillResourceDescriptor,\n SkillSource\n} from \"../../skills\";\n\n/** Skills resolved from `agents/skills` sources for one process lifetime. */\nexport type ResolvedSkills = {\n /** Changes when any source's fingerprint changes. */\n readonly fingerprint: string;\n /** Model-facing activation tools; empty when there are no skills. */\n readonly tools: readonly ToolRegistration[];\n /** System-prompt catalog, or null when there are no skills. */\n readonly catalog: string | null;\n readonly warnings: readonly string[];\n};\n\ntype ResolvedSkill = {\n readonly descriptor: SkillDescriptor;\n readonly content: SkillContent;\n readonly source: SkillSource;\n};\n\nexport function skillsFingerprint(sources: readonly SkillSource[]): string {\n return sources\n .map((source) => `${source.id}:${source.fingerprint}`)\n .join(\"|\");\n}\n\nfunction textResult(text: string): ToolExecutionResult {\n return { output: [{ type: \"text\", text }] };\n}\n\nfunction attributes(\n pairs: ReadonlyArray<readonly [string, string | undefined]>\n) {\n return pairs\n .filter((pair): pair is readonly [string, string] => pair[1] !== undefined)\n .map(([key, value]) => ` ${key}=\"${value.replace(/\"/g, \""\")}\"`)\n .join(\"\");\n}\n\nfunction describeResource(resource: SkillResourceDescriptor): string {\n const details = [\n resource.kind,\n resource.encoding ?? \"text\",\n resource.mimeType,\n resource.size === undefined ? undefined : `${resource.size} bytes`\n ].filter((value): value is string => value !== undefined);\n return `- ${resource.path} (${details.join(\", \")})`;\n}\n\nfunction renderSkillContent(skill: ResolvedSkill): string {\n const resources = skill.content.resources ?? [];\n const lines = [\n `<skill_content${attributes([\n [\"name\", skill.descriptor.name],\n [\"version\", skill.descriptor.version]\n ])}>`,\n skill.content.body.trim()\n ];\n if (resources.length > 0) {\n lines.push(\"<skill_resources>\");\n for (const resource of resources) {\n lines.push(\n ` <file${attributes([\n [\"kind\", resource.kind],\n [\"encoding\", resource.encoding ?? \"text\"],\n [\n \"size\",\n resource.size === undefined ? undefined : String(resource.size)\n ]\n ])}>${resource.path}</file>`\n );\n }\n lines.push(\"</skill_resources>\");\n }\n lines.push(\"</skill_content>\", \"\");\n lines.push(\n resources.length === 0\n ? \"No bundled resources.\"\n : [\"Bundled resources:\", ...resources.map(describeResource)].join(\"\\n\")\n );\n return lines.join(\"\\n\");\n}\n\n/**\n * Resolve `agents/skills` sources into a system-prompt catalog and two\n * tools.\n *\n * The first source to list a name wins, as in `SkillRegistry`. pi-durable\n * has no skill concept of its own, so the model activates skills through\n * `activate_skill` and reads bundled files through `read_skill_resource` —\n * the same tools `@cloudflare/think` offers, so skills authored for one\n * framework work in the other.\n */\nexport async function resolveSkillSources(\n sources: readonly SkillSource[]\n): Promise<ResolvedSkills> {\n const fingerprint = skillsFingerprint(sources);\n const warnings: string[] = [];\n const resolved = new Map<string, ResolvedSkill>();\n for (const source of sources) {\n let descriptors: SkillDescriptor[];\n try {\n descriptors = await source.list();\n } catch (error) {\n warnings.push(\n `Skill source \"${source.id}\" failed to list skills: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n continue;\n }\n for (const descriptor of descriptors) {\n if (resolved.has(descriptor.name)) {\n warnings.push(\n `Skill \"${descriptor.name}\" from source \"${source.id}\" is shadowed by an earlier source`\n );\n continue;\n }\n let content: SkillContent | null;\n try {\n content = await source.load(descriptor.name);\n } catch (error) {\n warnings.push(\n `Skill \"${descriptor.name}\" from source \"${source.id}\" failed to load: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n continue;\n }\n if (!content) continue;\n resolved.set(descriptor.name, { descriptor, content, source });\n }\n }\n\n const visible = [...resolved.values()].filter(\n (skill) => skill.descriptor.metadata?.[\"disable-model-invocation\"] !== true\n );\n if (visible.length === 0) {\n return { fingerprint, tools: [], catalog: null, warnings };\n }\n\n const names = visible.map((skill) => skill.descriptor.name);\n const nameSchema = Type.Union(names.map((name) => Type.Literal(name)));\n const byName = new Map(\n visible.map((skill) => [skill.descriptor.name, skill])\n );\n\n const activateParameters = Type.Object({ name: nameSchema });\n const activateSkill: ToolRegistration<typeof activateParameters> = {\n name: \"activate_skill\",\n description:\n \"Activate a skill by name. Use this when the user's task matches one of the available skills; the response contains the skill's full instructions.\",\n parameters: activateParameters,\n replay: \"safe\",\n async execute(input) {\n const skill = byName.get(input.name);\n return textResult(\n skill ? renderSkillContent(skill) : `Skill not found: ${input.name}`\n );\n }\n };\n\n const resourceParameters = Type.Object({\n name: Type.Optional(nameSchema),\n path: Type.String({ minLength: 1 })\n });\n const readResource: ToolRegistration<typeof resourceParameters> = {\n name: \"read_skill_resource\",\n description:\n \"Read a file bundled with a skill, such as a reference document or template. Provide the skill name and the file's path from the skill's resource list.\",\n parameters: resourceParameters,\n replay: \"safe\",\n async execute(input) {\n const target = resolveResourceTarget(byName, input.name, input.path);\n if (!target) {\n return textResult(\n `Skill resource not found: ${input.name ? `${input.name}/` : \"\"}${input.path}`\n );\n }\n const resource = await target.skill.source\n .readResource?.(target.skill.descriptor.name, target.path)\n .catch(() => null);\n if (!resource) {\n return textResult(\n `Skill resource not found: ${target.skill.descriptor.name}/${target.path}`\n );\n }\n return textResult(\n [\n `<skill_resource${attributes([\n [\"name\", target.skill.descriptor.name],\n [\"path\", resource.path],\n [\"kind\", resource.kind],\n [\"encoding\", resource.encoding ?? \"text\"],\n [\"mimeType\", resource.mimeType]\n ])}>`,\n resource.content,\n \"</skill_resource>\"\n ].join(\"\\n\")\n );\n }\n };\n\n const catalog = [\n \"Available skills. When a task matches a skill, use activate_skill with its name before proceeding.\",\n \"\",\n ...visible.map(\n (skill) => `- ${skill.descriptor.name}: ${skill.descriptor.description}`\n )\n ].join(\"\\n\");\n\n return {\n fingerprint,\n tools: [activateSkill, readResource],\n catalog,\n warnings\n };\n}\n\nfunction resolveResourceTarget(\n byName: ReadonlyMap<string, ResolvedSkill>,\n name: string | undefined,\n path: string\n): { readonly skill: ResolvedSkill; readonly path: string } | undefined {\n if (path.split(\"/\").some((segment) => segment === \"..\")) return undefined;\n if (name !== undefined) {\n const skill = byName.get(name);\n return skill && hasResource(skill, path) ? { skill, path } : undefined;\n }\n // A qualified path names the skill in its first segment.\n const [head, ...rest] = path.split(\"/\");\n const skill = head === undefined ? undefined : byName.get(head);\n const relative = rest.join(\"/\");\n return skill && relative && hasResource(skill, relative)\n ? { skill, path: relative }\n : undefined;\n}\n\nfunction hasResource(skill: ResolvedSkill, path: string): boolean {\n return (skill.content.resources ?? []).some(\n (resource) => resource.path === path\n );\n}\n\n/**\n * `agents/skills` sources as a pi extension, named `agents.skills`: the\n * activation tools and a `skills` system prompt section. Install it on\n * pi's registry in the harness factory.\n *\n * ```ts\n * registry.install(await skills(sources));\n * ```\n */\nexport async function skills(\n sources: readonly SkillSource[]\n): Promise<Extension> {\n const resolved = await resolveSkillSources(sources);\n for (const warning of resolved.warnings) {\n console.warn(`pi skills: ${warning}`);\n }\n const catalog = resolved.catalog;\n return {\n name: \"agents.skills\",\n tools: resolved.tools,\n sections: catalog === null ? [] : [{ key: \"skills\", render: () => catalog }]\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/harness/pi/session-store.ts","../../../src/harness/pi/harness.ts","../../../src/harness/pi/skills.ts"],"sourcesContent":["import type { SqliteStorage } from \"@earendil-works/pi-durable/storage/sqlite\";\nimport { openDurableObjectSqliteStorage } from \"@earendil-works/pi-durable/storage/sqlite/cloudflare\";\n\n/**\n * pi's session store on a Durable Object's SQLite database.\n *\n * pi owns its sessions: conversations, entries, tasks, submissions, and\n * documents live in pi's own schema, created and migrated by pi's portable\n * `SqliteStorage` through pi's own Durable Object adapter. The store keeps\n * pi's tables under a prefix, so they cannot collide with the SDK's or the\n * host's tables in the same object.\n *\n * It is the Durable Object counterpart of `agents/sessions` for a harness\n * that brings its own session model, and has nothing pi-harness specific in\n * it: any harness built on pi-durable can open its storage with it.\n */\nexport type PiSessionStoreOptions = {\n /**\n * Prefix for every table and index pi creates. Must not start with `_cf_`,\n * which Durable Objects reserve. Default `pi_`.\n */\n readonly prefix?: string;\n};\n\nconst DEFAULT_PREFIX = \"pi_\";\n\n/** Open pi-durable's storage over this object's SQLite database. */\nexport function openPiSessionStore(\n storage: DurableObjectStorage,\n options: PiSessionStoreOptions = {}\n): Promise<SqliteStorage> {\n const prefix = options.prefix ?? DEFAULT_PREFIX;\n // pi validates the prefix as a SQLite identifier; Durable Objects also\n // reserve `_cf_` in any case, which pi does not know about.\n if (prefix.toLowerCase().startsWith(\"_cf_\")) {\n return Promise.reject(\n new Error(\"The pi session store prefix must not start with _cf_\")\n );\n }\n return openDurableObjectSqliteStorage(storage, { prefix });\n}\n","import type { Api, Model, ModelThinkingLevel } from \"@earendil-works/pi-ai\";\nimport {\n LiveDoc,\n ROOT_CONVERSATION_ID,\n watchEvents,\n type AgentEventStream,\n type Conversation,\n type ConversationId,\n type AgentChange,\n type EntryRecord,\n type Harness,\n type HarnessInspection,\n type ModelRef,\n type SettledSubmissionRecord,\n type TaskId,\n type UserInput,\n type Wake\n} from \"@earendil-works/pi-durable\";\nimport type { SqliteStorage } from \"@earendil-works/pi-durable/storage/sqlite\";\nimport {\n LifecycleCapability,\n type CapabilityStartContext,\n type LifecycleJobContext,\n type LifecycleJobOutcome\n} from \"../../lifecycle\";\nimport { isDurableObjectMemoryLimitReset } from \"../../retries\";\nimport {\n BACKGROUND_CONTEXT,\n withAbortSignal,\n type Context\n} from \"@earendil-works/chord/context\";\nimport { openPiSessionStore } from \"./session-store\";\nimport type {\n PiOperationResult,\n PiPendingOperation,\n PiPromptResponse,\n PiReceipt,\n PiSessionId,\n PiSessionInfo,\n PiSessionOptions,\n PiSubmitOptions\n} from \"./types\";\n\nconst BG = BACKGROUND_CONTEXT;\n\n/** The root session's id. */\nexport const ROOT_SESSION: PiSessionId = String(ROOT_CONVERSATION_ID);\n\n/**\n * The wake timings. They suit a real deployment; only tests change them,\n * with `setWakeTimingForTests`, so a suite does not sit on the real\n * heartbeat.\n *\n * Longest one wake waits on pi. It waits inside an alarm invocation, which\n * has a 15 minute wall-time limit, so a longer run is waited on across\n * several alarms.\n */\nconst WAIT_BUDGET_MS = 10 * 60_000;\n\n/**\n * The wake job's heartbeat while it waits, and how often it re-checks work\n * it cannot wait on, such as background tasks. If the object is evicted, the\n * job is still due and its alarm restarts the object.\n */\nconst HEARTBEAT_MS = 30_000;\n\n/**\n * How often a wait re-reads `pi.wake()` when the factory did not pass\n * `onWake`, so nothing tells the harness when it changes.\n */\nconst POLL_MS = 1_000;\n\n/**\n * The longest a wake job whose dispatch keeps failing waits before it tries\n * again. Its delay starts at the heartbeat and doubles up to this.\n */\nconst MAX_WAKE_BACKOFF_MS = 10 * 60_000;\n\n/**\n * Wake dispatches that failed in a row. Stored, so the backoff holds across\n * restarts, and outside the job: a push that saved it in the job's payload\n * could overwrite a submit's due-now push.\n */\nconst WAKE_ERRORS_KEY = \"pi-harness:wake-errors\";\n\nlet setWakeTiming: (harness: PiHarness, timing: WakeTiming) => void;\n\n/**\n * @internal Shorten a harness's wake timings, so a test suite does not sit\n * on the real heartbeat. Not exported from `agents/harness/pi`.\n */\nexport function setWakeTimingForTests(\n harness: PiHarness,\n timing: WakeTiming\n): void {\n setWakeTiming(harness, timing);\n}\n\nconst WAKE_FN = \"wake\";\n\n/**\n * The one wake job. Before Pi 1.4 there was one per session, with ids\n * `pi-wake:<session>`; a left-over one hands over to this one.\n */\nconst WAKE_JOB_ID = \"pi-wake\";\n\n/**\n * What the factory is handed: the things only the harness can build.\n * Everything else `Harness.open` takes (models, registry, settings, env,\n * onReport) the factory builds itself.\n */\nexport type PiHarnessContext = {\n /** pi's storage over this object's SQLite, its migrations already run. */\n readonly storage: SqliteStorage;\n /** Background context, for the open itself. */\n readonly context: Context;\n /**\n * Pass this to `Harness.open` as its `onWake` option. pi calls it when it\n * next needs to run, and the harness sets this object's alarm for then.\n *\n * With it, pi sets no in-memory timer for a sleeping task (a retry\n * backoff, a poll, a `sleepUntil`), so the object can hibernate until the\n * alarm. Without it the harness still works, but pi's timer keeps the\n * object in memory, and billed, while a task sleeps, and the harness logs\n * a warning when it opens pi.\n *\n * The harness reads pi's wake times against `Date.now()`, so do not pass\n * `now` to `Harness.open`.\n */\n readonly onWake: (wake: Wake) => void;\n};\n\n/**\n * Opens pi's `Harness`. The harness adopts what it returns. Pass `onWake`\n * through to `Harness.open`.\n *\n * ```ts\n * harness: async ({ storage, context, onWake }) => {\n * const registry = createRegistry();\n * registry.install(await skills(sources));\n * return Harness.open(storage, { models, registry, onWake }, context);\n * }\n * ```\n */\nexport type PiHarnessFactory = (\n context: PiHarnessContext\n) => Harness | Promise<Harness>;\n\n/**\n * A model, as pi-ai describes one: `createAI`'s `ai(\"@cf/…\")` returns one.\n *\n * Only its `provider` and `id` are used. pi stores that reference with\n * the session and resolves it, at each request, against the `Models` the\n * factory opened pi with. So the model must be one those `Models` list,\n * and options passed to `ai(id, options)` here, such as `fallback`, are not\n * applied.\n */\nexport type PiModel = Pick<Model<Api>, \"provider\" | \"id\">;\n\n/**\n * Applied to a session the first time it is created. Without a model, a\n * session's generation fails as unanswered until `session.setModel` sets one.\n */\nexport type PiSessionDefaults = {\n /**\n * Model for new sessions, by its `provider` and `id`; it must be on the\n * `Models` the factory opens pi with (see `PiModel`). Change one\n * session's with `session.setModel`.\n */\n readonly model?: PiModel;\n readonly thinkingLevel?: ModelThinkingLevel;\n};\n\n/** How long the wake waits on pi, and how often it checks. */\ntype WakeTiming = {\n /** Longest one wake waits inside an alarm. Default 600_000. */\n readonly waitBudgetMs?: number;\n /** Heartbeat while waiting. Default 30_000. */\n readonly heartbeatMs?: number;\n /** How often a wait re-reads `pi.wake()` without `onWake`. Default 1_000. */\n readonly pollMs?: number;\n};\n\n/** `PiHarness`'s options. Only `harness`, which opens pi, is required. */\nexport type PiHarnessOptions = {\n /** Opens pi's `Harness` over the storage this object prepared. */\n readonly harness: PiHarnessFactory;\n /** What a new session starts with. */\n readonly defaults?: PiSessionDefaults;\n};\n\ntype Opened = {\n readonly pi: Harness;\n readonly storage: SqliteStorage;\n};\n\n/** pi-durable's stored reference to a pi-ai model. */\nfunction modelRef(model: PiModel): ModelRef {\n return { provider: model.provider, modelId: model.id };\n}\n\nfunction conversationId(session: PiSessionId): ConversationId {\n const id = Number(session);\n if (!Number.isSafeInteger(id) || id < 1) {\n throw new Error(`Invalid pi session ${JSON.stringify(session)}`);\n }\n return id as ConversationId;\n}\n\n/** The text of an assistant entry, for an operation's result. */\nfunction assistantText(entry: EntryRecord | undefined): string {\n const message = entry?.model?.[0];\n if (message?.role !== \"assistant\") return \"\";\n return message.content\n .map((part) => (part.type === \"text\" ? part.text : \"\"))\n .join(\"\");\n}\n\nfunction signalContext(signal: AbortSignal | undefined): Context {\n return signal ? withAbortSignal(signal, BG) : BG;\n}\n\n/**\n * pi-durable hosted in a Durable Object, behind a small harness interface:\n * `harness.prompt()`, `harness.sessions`, `harness.session(id)`. How a session reaches a\n * client (sockets, SSE, RPC) is the host's glue, built on `session.events()`.\n *\n * pi owns everything about a run: the transcript, the inbox of steers and\n * follow-ups, generation and tool tasks, retries, crash recovery, and the\n * live view. It keeps all of it in its own tables in this object's SQLite\n * database (see `session-store.ts`).\n *\n * What pi cannot do on a Durable Object is wake itself: its scheduler runs\n * in memory, and an evicted object has no memory. The harness is that wake,\n * with one Lifecycle job driven by `pi.wake()`. Input goes to pi once, in\n * `submit()`, after the job is scheduled. While pi has work running, the\n * job waits on it, rescheduling itself as a heartbeat. While pi's tasks\n * sleep, the job is due when the first one wakes; pi tells the harness\n * through the `onWake` the factory passes to `Harness.open`. When pi has\n * nothing to do, the job completes. An eviction mid-run leaves the job due,\n * so its alarm restarts the object, pi reopens and resumes its own tasks,\n * and the job waits again.\n *\n * @beta The API may change between releases.\n */\nexport class PiHarness extends LifecycleCapability {\n readonly sessions: PiSessions;\n readonly #options: PiHarnessOptions;\n /** The in-memory wait on pi's running work, inside an alarm's work. */\n #waiting: Promise<void> | undefined;\n /** Resolves the current wait's sleep early: `pi.wake()` changed. */\n #changed: (() => void) | undefined;\n /** Submissions between their wake and pi's admission. */\n #admitting = 0;\n /** Whether pi has called `onWake`, so the factory passed it. */\n #onWakeWired = false;\n #waitBudgetMs = WAIT_BUDGET_MS;\n #heartbeatMs = HEARTBEAT_MS;\n #pollMs = POLL_MS;\n #opening: Promise<Opened> | undefined;\n /** Settles once the last pi that closed on its own has closed. */\n #lastClose: Promise<unknown> = Promise.resolve();\n /** Every pi that has begun closing, on its own or by `dispose()`. */\n readonly #closing = new WeakSet<Harness>();\n\n static {\n setWakeTiming = (harness, timing) => harness.#setWakeTiming(timing);\n }\n\n constructor(options: PiHarnessOptions) {\n super(\"pi-harness\");\n this.#options = options;\n this.sessions = new PiSessions(this);\n }\n\n #setWakeTiming(timing: WakeTiming): void {\n for (const [name, value] of Object.entries(timing)) {\n if (!Number.isFinite(value) || value <= 0) {\n throw new Error(`PiHarness timing.${name} must be a positive number`);\n }\n }\n this.#waitBudgetMs = timing.waitBudgetMs ?? this.#waitBudgetMs;\n this.#heartbeatMs = timing.heartbeatMs ?? this.#heartbeatMs;\n this.#pollMs = timing.pollMs ?? this.#pollMs;\n }\n\n // ── Lifecycle ────────────────────────────────────────────────────────────\n\n override async onStart(_context: CapabilityStartContext): Promise<void> {\n await this.#open();\n }\n\n async onJob(\n context: LifecycleJobContext\n ): Promise<LifecycleJobOutcome | void> {\n if (context.job.fn !== WAKE_FN) return;\n let outcome: LifecycleJobOutcome;\n if (context.job.id !== WAKE_JOB_ID) {\n // A per-session wake from before Pi 1.4: hand over to the one wake.\n await this.#wake();\n outcome = undefined;\n } else {\n outcome = await this.#wakeStep();\n }\n // It got through, so a failing wake's backoff starts over.\n const kv = this.lifecycle.storage.kv;\n if (kv.get(WAKE_ERRORS_KEY) !== undefined) kv.delete(WAKE_ERRORS_KEY);\n return outcome;\n }\n\n /**\n * A wake dispatch that failed through Lifecycle's retries, as when pi\n * cannot be opened. Lifecycle would complete the job, and pi's work would\n * never be woken again, so try again later: at the heartbeat, doubling\n * while the failures continue, up to 10 minutes.\n */\n onJobError(\n context: LifecycleJobContext,\n error: unknown\n ): LifecycleJobOutcome | void {\n if (context.job.fn !== WAKE_FN) return;\n console.error(\n `pi wake job \"${context.job.id}\" failed after ${context.attempt} ` +\n \"attempts; retrying with backoff\",\n error\n );\n this.#wakeError(error);\n const kv = this.lifecycle.storage.kv;\n const errors = (kv.get<number>(WAKE_ERRORS_KEY) ?? 0) + 1;\n kv.put(WAKE_ERRORS_KEY, errors);\n const delay = Math.min(\n this.#heartbeatMs * 2 ** (errors - 1),\n MAX_WAKE_BACKOFF_MS\n );\n return { rescheduleAt: Date.now() + delay };\n }\n\n /** Close pi's in-memory resources. Durable state is untouched. */\n async dispose(): Promise<void> {\n const opening = this.#opening;\n this.#opening = undefined;\n const opened = await opening?.catch(() => undefined);\n // Closing calls #piClosing, which also ends a wait sleeping on this pi.\n await opened?.pi.close(BG);\n }\n\n // ── The harness interface ────────────────────────────────────────────────\n\n /** A handle on one session. No I/O until you call it. */\n session(id: PiSessionId = ROOT_SESSION): PiSession {\n return new PiSession(this, id);\n }\n\n /** Submit a prompt and wait for its answer. */\n prompt(\n input: UserInput,\n options: PiSubmitOptions = {}\n ): Promise<PiPromptResponse> {\n return this.session(options.session).prompt(input, options);\n }\n\n /** Durably submit a prompt. Resolves before the model runs. */\n submit(input: UserInput, options: PiSubmitOptions = {}): Promise<PiReceipt> {\n return this.session(options.session).submit(input, options);\n }\n\n /** Stop one operation, or everything running in a session. */\n abort(\n options: PiSessionOptions & { readonly operationId?: string } = {}\n ): Promise<boolean> {\n return this.session(options.session).abort(options.operationId);\n }\n\n /**\n * Run again the tasks pi's crash-loop guard parked in a session's work.\n * False when nothing was parked.\n */\n retry(options: PiSessionOptions = {}): Promise<boolean> {\n return this.session(options.session).retry();\n }\n\n wait(\n operationId: string,\n options: PiSessionOptions & { readonly signal?: AbortSignal } = {}\n ): Promise<PiOperationResult> {\n return this.session(options.session).wait(operationId, options.signal);\n }\n\n /** The session's active transcript, as pi's entries. */\n messages(options: PiSessionOptions = {}): Promise<EntryRecord[]> {\n return this.session(options.session).messages();\n }\n\n /** Submissions pi has not settled yet, oldest first. */\n async pending(options: PiSessionOptions = {}): Promise<PiPendingOperation[]> {\n const { pi } = await this.#open();\n const id =\n options.session === undefined\n ? undefined\n : conversationId(options.session);\n const inspection = await pi.inspect(BG);\n const parked = await this.#parked(inspection);\n return inspection.submissions\n .filter(\n (record) =>\n record.requestId !== undefined &&\n (id === undefined || record.conversationId === id)\n )\n .map((record) => ({\n operationId: record.requestId as string,\n session: String(record.conversationId),\n status:\n record.status === \"queued\"\n ? \"queued\"\n : parked.some((task) => task.sessions.has(record.conversationId))\n ? \"parked\"\n : \"running\"\n }));\n }\n\n /** The opened pi Harness, for anything the interface does not cover. */\n async pi(): Promise<Harness> {\n return (await this.#open()).pi;\n }\n\n // ── Used by PiSession and PiSessions ─────────────────────────────────────\n\n /** @internal */\n async conversation(session: PiSessionId): Promise<Conversation> {\n const { pi } = await this.#open();\n const conversation = await pi.conversation(conversationId(session), BG);\n if (!conversation) throw new Error(`Unknown pi session ${session}`);\n return conversation;\n }\n\n /** @internal */\n async storage(): Promise<SqliteStorage> {\n return (await this.#open()).storage;\n }\n\n /** @internal */\n async enqueue(\n session: PiSessionId,\n input: UserInput,\n options: PiSubmitOptions\n ): Promise<PiReceipt> {\n const operationId = options.operationId ?? crypto.randomUUID();\n const { storage } = await this.#open();\n const conversation = await this.conversation(session);\n const accepted = await this.#admit(async () => {\n // The one admission. pi deduplicates by request id.\n const accepted =\n (await storage.submissionByRequest(\n conversation.id,\n operationId,\n BG\n )) === undefined;\n await conversation.submit(\n {\n type: \"input\",\n content: input,\n whenBusy: options.whenBusy ?? \"followUp\",\n requestId: operationId,\n ...(options.entry ? { entry: options.entry } : {})\n },\n BG\n );\n return accepted;\n });\n return { operationId, session, accepted };\n }\n\n /** @internal Run again the tasks pi's restart limit parked in a session. */\n async retryParked(session: PiSessionId): Promise<boolean> {\n const { pi } = await this.#open();\n const id = conversationId(session);\n const parked = (await this.#parked(await pi.inspect(BG))).filter((task) =>\n task.sessions.has(id)\n );\n if (parked.length === 0) return false;\n await this.#admit(async () => {\n for (const task of parked) await pi.retryTask(task.id, BG);\n });\n return true;\n }\n\n /** @internal Withdraw a queued input, or abort the run it joined. */\n async withdraw(session: PiSessionId, operationId: string): Promise<boolean> {\n const { pi, storage } = await this.#open();\n const id = conversationId(session);\n const record = await storage.submissionByRequest(id, operationId, BG);\n if (!record || record.status === \"done\" || record.status === \"unanswered\") {\n return false;\n }\n const withdrawn = await pi.abortSubmission(record.id, BG, id);\n if (withdrawn === \"already_placed\") {\n await (await pi.conversation(id, BG))?.abort(BG);\n }\n return withdrawn !== \"settled\" && withdrawn !== \"not_found\";\n }\n\n /** @internal Wait for pi to settle an operation, by its request id. */\n async settled(\n session: PiSessionId,\n operationId: string,\n signal?: AbortSignal\n ): Promise<PiOperationResult> {\n const conversation = await this.conversation(session);\n const context = signalContext(signal);\n const submission = await this.#findSubmission(\n conversation.id,\n operationId,\n context\n );\n if (!submission) {\n return {\n operationId,\n session,\n status: \"unanswered\",\n reason: \"not_found\"\n };\n }\n return this.#result(\n session,\n operationId,\n await submission.wait(context),\n context\n );\n }\n\n // ── The wake ─────────────────────────────────────────────────────────────\n\n /**\n * Hand pi work inside the wake. The wake is scheduled first, so if the\n * object dies after pi has the work, that job restarts it; while `hand`\n * is in flight the step will not complete. The wake steps again after, so\n * it sees the work even if it just completed.\n */\n async #admit<T>(hand: () => Promise<T>): Promise<T> {\n this.#admitting++;\n let result: T;\n try {\n await this.#wake();\n result = await hand();\n } finally {\n this.#admitting--;\n }\n await this.#wake();\n return result;\n }\n\n /** Schedule the wake job now, or at `time`. */\n #wake(time = Date.now()): Promise<unknown> {\n // A push made while the job is dispatching supersedes that dispatch's\n // outcome, so a submit is never lost to a wake that is completing.\n return this.lifecycle.jobs.push({\n id: WAKE_JOB_ID,\n fn: WAKE_FN,\n time,\n singleflight: true,\n recoveryLoop: true\n });\n }\n\n /** Schedule the wake job for what `pi.wake()` says, if pi needs one. */\n async #wakeFor(wake: Wake): Promise<void> {\n if (wake.active) await this.#wake();\n else if (wake.at !== undefined) await this.#wake(wake.at);\n }\n\n /**\n * pi's `onWake`, handed to the factory. pi calls it after open and\n * whenever `pi.wake()` changes. It must not block, so the job push is\n * left running.\n */\n readonly #onWake = (wake: Wake): void => {\n this.#onWakeWired = true;\n this.#changed?.();\n // A running wait re-reads pi.wake() and steps again when it ends.\n if (this.#waiting) return;\n this.#wakeFor(wake).catch((error: unknown) => this.#wakeError(error));\n };\n\n /**\n * One run of the wake job. It never admits or replays anything: it reads\n * `pi.wake()`, which is all of pi's sessions at once. Running work is\n * waited on; a sleep reschedules the job for when it ends; no work\n * completes the job. pi does all the work in between.\n */\n async #wakeStep(): Promise<LifecycleJobOutcome> {\n const heartbeat = { rescheduleAt: Date.now() + this.#heartbeatMs };\n if (this.#waiting) return heartbeat;\n const { pi } = await this.#open();\n // The alarm may be for a sleeping task: make due ones runnable.\n pi.resume();\n const wake = pi.wake();\n if (wake.active) {\n const wait = this.#waitWhileActive(pi).then(\n (ended) => {\n this.#waiting = undefined;\n // Step again now: pi may be sleeping, or have nothing left. After\n // a failed wait, as when pi's Session fails, the heartbeat that is\n // already due steps instead, so a pi that keeps failing is not\n // reopened in a loop.\n if (ended) void this.#wake();\n },\n (error: unknown) => {\n // A memory-limit reset: the alarm's breaker backs the job off.\n this.#waiting = undefined;\n throw error;\n }\n );\n this.#waiting = wait;\n // The wait runs past this dispatch, inside the alarm's work, so the\n // object stays alive for it, and a memory-limit reset it ends with\n // counts against the alarm's breaker. The heartbeat covers an\n // eviction.\n if (!this.lifecycle.trackAlarmWork(wait)) wait.catch(() => {});\n return heartbeat;\n }\n // A submit between its wake and pi's admission: check again later.\n if (this.#admitting > 0) return heartbeat;\n if (wake.at !== undefined) return { rescheduleAt: wake.at };\n // Nothing to do. A task parked by pi's restart limit is not work: it\n // waits for `retry()`.\n return undefined;\n }\n\n /**\n * Wait while pi has work running, up to the wait budget. With `onWake`, a\n * change ends each sleep early, and so does pi closing; without it, the\n * sleep is `pollMs`. A task sleeping beside the running work, such as a\n * retry backoff, also ends the sleep when it is due, and the wait calls\n * `pi.resume()` to run it: with `onWake`, pi arms no timer for it, and\n * the wake job's alarm is not set for it while the job waits. Whether the\n * wait ended, with pi idle or at the budget, rather than failing. Rejects\n * with a memory-limit reset.\n */\n async #waitWhileActive(pi: Harness): Promise<boolean> {\n const deadline = Date.now() + this.#waitBudgetMs;\n try {\n // Once pi begins closing, pi.wake() throws (SessionFailed after a\n // storage error), which is what ends this loop: a closed pi's wake does\n // not go inactive. Closing kicks `#changed` only so the loop reads it\n // again now rather than at the end of its sleep.\n let wake = pi.wake();\n while (wake.active) {\n const now = Date.now();\n const left = deadline - now;\n if (left <= 0) return true;\n let sleep = this.#onWakeWired ? left : Math.min(left, this.#pollMs);\n // `at` is a sleep not yet due on pi's clock, so this is positive.\n // That holds because pi keeps its default clock, `Date.now`; see\n // `PiHarnessContext`.\n if (wake.at !== undefined) sleep = Math.min(sleep, wake.at - now);\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, sleep);\n this.#changed = () => {\n clearTimeout(timer);\n resolve();\n };\n });\n this.#changed = undefined;\n // A sleep beside the running work came due: make it runnable.\n if (wake.at !== undefined && Date.now() >= wake.at) pi.resume();\n wake = pi.wake();\n }\n return true;\n } catch (error) {\n // A pi that began closing is expected to throw; it is no longer the\n // harness's pi, and Pi reports a failure itself (`session_failed`).\n if (!this.#closing.has(pi)) this.#wakeError(error);\n // Only Lifecycle's alarm breaker can act on a memory-limit reset,\n // including one that failed pi's Session (SessionFailed's cause).\n if (isDurableObjectMemoryLimitReset(error)) throw error;\n return false;\n }\n }\n\n #wakeError(error: unknown): void {\n this.lifecycle.events.emit(\"pi:wake_error\", {\n error: error instanceof Error ? error.message : String(error)\n });\n }\n\n // ── pi ───────────────────────────────────────────────────────────────────\n\n /**\n * pi, opened. Waits for Lifecycle startup first, so pi is only ever\n * opened inside it: startup holds the input gate (`blockConcurrencyWhile`)\n * and awaits the open in `onStart`, so an open begun before startup would\n * be awaited behind a closed gate, with its timers and I/O held back, and\n * never finish.\n *\n * pi stays open until `dispose()`, or until it closes on its own: any\n * storage error fails pi's Session for good. Then the next call, or the\n * wake job's next step, opens pi again once the failed pi has closed, and\n * pi recovers from what it had committed. Closing waits for running task\n * code to return, so code that ignores its abort signal delays the reopen.\n * An open that fails is not kept either, so the next call tries again.\n */\n async #open(): Promise<Opened> {\n await this.lifecycle.ready();\n if (this.#opening === undefined) {\n const opening: Promise<Opened> = this.#doOpen((pi, closed) =>\n this.#piClosing(opening, pi, closed)\n ).catch((error: unknown) => {\n if (this.#opening === opening) this.#opening = undefined;\n throw error;\n });\n this.#opening = opening;\n }\n return this.#opening;\n }\n\n async #doOpen(\n onClose: (pi: Harness, closed: Promise<unknown>) => void\n ): Promise<Opened> {\n // A pi that closed on its own closes its storage last, after its tasks\n // have returned; let it finish before another opens over the same\n // database.\n await this.#lastClose;\n const storage = await openPiSessionStore(this.lifecycle.storage);\n const pi = await this.#options.harness({\n storage,\n context: BG,\n onWake: this.#onWake\n });\n try {\n const closed = pi.closed;\n // Called synchronously when pi begins closing, unlike `closed`, so a\n // closed pi is never handed out. It must not call pi.\n pi.subscribeClose(() => onClose(pi, closed));\n await pi.root(BG, { agent: this.agentDefaults() });\n // Continue whatever the last pi left, in this isolate or the last one:\n // pi reconciles tasks that were running to pending and schedules them\n // again.\n pi.resume();\n // pi calls onWake right after open, from a microtask.\n await new Promise((resolve) => setTimeout(resolve, 0));\n if (!this.#onWakeWired) {\n console.warn(\n \"PiHarness: the harness factory did not pass onWake to Harness.open, \" +\n \"so pi's timers keep this Durable Object in memory while a task \" +\n \"sleeps. Pass it: harness: ({ storage, context, onWake }) => \" +\n \"Harness.open(storage, { ...options, onWake }, context)\"\n );\n }\n // Whatever the last pi left gets a wake, including work whose wake\n // failed out. With `onWake`, pi has already said so; this covers a\n // factory without it. The job id is fixed, so this is idempotent.\n await this.#wakeFor(pi.wake());\n } catch (error) {\n // pi is open and running its tasks: close it, so the next open does\n // not start a second pi over the same database beside it.\n pi.close(BG).catch(() => {});\n throw error;\n }\n return { pi, storage };\n }\n\n /**\n * pi began closing: on its own, as a storage error makes it, or by\n * `dispose()`.\n */\n #piClosing(\n opening: Promise<Opened>,\n pi: Harness,\n closed: Promise<unknown>\n ): void {\n this.#closing.add(pi);\n // End a wait sleeping on this pi now: pi will not call onWake again.\n this.#changed?.();\n // `dispose()` took its pi out of `#opening` already, and an open begun\n // since must stay.\n if (this.#opening !== opening) return;\n // Drop it, so the next call opens pi again once this one has closed.\n this.#opening = undefined;\n this.#lastClose = closed;\n }\n\n /** @internal The per-conversation defaults supported by pi-durable. */\n agentDefaults(): AgentChange {\n const defaults = this.#options.defaults;\n return {\n ...(defaults?.model === undefined\n ? {}\n : { model: modelRef(defaults.model) }),\n ...(defaults?.thinkingLevel === undefined\n ? {}\n : { thinkingLevel: defaults.thinkingLevel })\n };\n }\n\n /**\n * Tasks pi's restart limit parked, each with the sessions whose work it\n * is: its own, and every session that owns that one, such as the session\n * of a tool that started a subagent.\n */\n async #parked(\n inspection: HarnessInspection\n ): Promise<{ id: TaskId; sessions: Set<ConversationId> }[]> {\n const { storage } = await this.#open();\n const parked: { id: TaskId; sessions: Set<ConversationId> }[] = [];\n for (const { record, state } of inspection.tasks) {\n if (state.kind !== \"blocked\" || state.reason !== \"restart_limit\") {\n continue;\n }\n const sessions = new Set<ConversationId>();\n let id: ConversationId | undefined = record.conversationId;\n while (id !== undefined && !sessions.has(id)) {\n sessions.add(id);\n id = (await storage.conversation(id, BG))?.owner?.conversationId;\n }\n parked.push({ id: record.id, sessions });\n }\n return parked;\n }\n\n async #findSubmission(\n id: ConversationId,\n operationId: string,\n context: Context\n ) {\n const { pi, storage } = await this.#open();\n // Read from pi's storage: the Harness only reacquires submissions by id.\n const record = await storage.submissionByRequest(id, operationId, context);\n return record ? pi.submission(record.id, context) : undefined;\n }\n\n async #result(\n session: PiSessionId,\n operationId: string,\n settled: SettledSubmissionRecord,\n context: Context\n ): Promise<PiOperationResult> {\n if (settled.status === \"unanswered\") {\n return {\n operationId,\n session,\n status: \"unanswered\",\n reason: settled.reason\n };\n }\n if (settled.type !== \"input\")\n return { operationId, session, status: \"done\" };\n const conversation = await this.conversation(session);\n const page = await conversation.entries(\n { minEntryId: settled.answer, maxEntryId: settled.answer },\n 1,\n undefined,\n context\n );\n return {\n operationId,\n session,\n status: \"done\",\n text: assistantText(page.items[0])\n };\n }\n}\n\n/** One pi conversation, addressed through the harness. */\nexport class PiSession {\n readonly #harness: PiHarness;\n readonly id: PiSessionId;\n\n constructor(harness: PiHarness, id: PiSessionId) {\n this.#harness = harness;\n this.id = id;\n }\n\n /** Durably submit a prompt. Resolves before the model runs. */\n submit(input: UserInput, options: PiSubmitOptions = {}): Promise<PiReceipt> {\n return this.#harness.enqueue(this.id, input, options);\n }\n\n /** Submit and wait for the answer and the updated transcript. */\n async prompt(\n input: UserInput,\n options: PiSubmitOptions = {}\n ): Promise<PiPromptResponse> {\n const receipt = await this.submit(input, options);\n const result = await this.wait(receipt.operationId);\n return { ...result, messages: await this.messages() };\n }\n\n /** Join the running work after its current tool round. */\n steer(input: UserInput, options: Omit<PiSubmitOptions, \"whenBusy\"> = {}) {\n return this.submit(input, { ...options, whenBusy: \"steer\" });\n }\n\n /** Wait for an operation to settle. Aborting `signal` stops only the wait. */\n wait(operationId: string, signal?: AbortSignal): Promise<PiOperationResult> {\n return this.#harness.settled(this.id, operationId, signal);\n }\n\n /**\n * Withdraw one queued operation (or abort the run it joined), or, with no\n * id, abort the session: pi withdraws queued inputs and aborts the\n * running work.\n */\n async abort(operationId?: string): Promise<boolean> {\n if (operationId !== undefined) {\n return this.#harness.withdraw(this.id, operationId);\n }\n const conversation = await this.#harness.conversation(this.id);\n await conversation.abort(BG);\n return true;\n }\n\n /**\n * Run again the tasks pi's crash-loop guard (`settings.restartLimit`)\n * parked in this session's work, including subagent sessions it owns.\n * False when nothing was parked.\n */\n retry(): Promise<boolean> {\n return this.#harness.retryParked(this.id);\n }\n\n /** Start a new context, optionally from a handoff note. */\n async reset(handoff?: string): Promise<void> {\n await (await this.#harness.conversation(this.id)).reset(handoff, BG);\n }\n\n /**\n * Change this session's model, by its `provider` and `id`; it must be on\n * the `Models` the factory opened pi with (see `PiModel`).\n */\n async setModel(model: PiModel): Promise<void> {\n await (\n await this.#harness.conversation(this.id)\n ).configure({ model: modelRef(model) }, BG);\n }\n\n /** The active transcript, as pi's entries since the newest reset. */\n async messages(): Promise<EntryRecord[]> {\n const view = await (await this.#harness.conversation(this.id)).context(BG);\n return [...view.entries];\n }\n\n /** pi's agent events for this session: a snapshot, then one batch per commit. */\n async events(context: Context = BG): Promise<AgentEventStream> {\n const pi = await this.#harness.pi();\n return watchEvents(pi, conversationId(this.id), context);\n }\n\n async busy(): Promise<boolean> {\n const pi = await this.#harness.pi();\n const live = await pi.snapshot(LiveDoc, conversationId(this.id), BG);\n return live?.run !== undefined;\n }\n}\n\n/** Every pi conversation in this object. */\nexport class PiSessions {\n readonly #harness: PiHarness;\n\n constructor(harness: PiHarness) {\n this.#harness = harness;\n }\n\n get(id: PiSessionId): PiSession {\n return this.#harness.session(id);\n }\n\n /** A new top-level session, configured like the root. */\n async create(): Promise<PiSession> {\n const pi = await this.#harness.pi();\n const conversation = await pi.createConversation(\n {\n ownership: { kind: \"ownerless\" },\n agent: this.#harness.agentDefaults()\n },\n BG\n );\n return this.#harness.session(String(conversation.id));\n }\n\n /** A new session that sees `from`'s history up to its newest entry. */\n async fork(from: PiSessionId): Promise<PiSession> {\n const conversation = await this.#harness.conversation(from);\n const newest = await conversation.entries({}, 1, undefined, BG);\n const at = newest.items[0];\n if (!at) throw new Error(`Session ${from} has no entries to fork from`);\n const fork = await conversation.fork(\n at.id,\n { ownership: { kind: \"ownerless\" } },\n BG\n );\n return this.#harness.session(String(fork.id));\n }\n\n /**\n * Every conversation, including ones subagent tools own. Read from pi's\n * storage directly: the Harness has no conversation listing.\n */\n async list(): Promise<PiSessionInfo[]> {\n const storage = await this.#harness.storage();\n const pi = await this.#harness.pi();\n const sessions: PiSessionInfo[] = [];\n let cursor: Parameters<SqliteStorage[\"scanConversations\"]>[2];\n for (;;) {\n const page = await storage.scanConversations({}, 100, cursor, BG);\n for (const record of page.items) {\n const live = await pi.snapshot(LiveDoc, record.id, BG);\n sessions.push({\n id: String(record.id),\n ...(record.parent\n ? { parent: String(record.parent.conversationId) }\n : {}),\n busy: live?.run !== undefined\n });\n }\n if (page.next === undefined) return sessions;\n cursor = page.next;\n }\n }\n}\n","import { Type } from \"@earendil-works/pi-ai\";\nimport type {\n Extension,\n ToolExecutionResult,\n ToolRegistration\n} from \"@earendil-works/pi-durable\";\nimport type {\n SkillContent,\n SkillDescriptor,\n SkillResourceDescriptor,\n SkillSource\n} from \"../../skills\";\n\n/** Skills resolved from `agents/skills` sources for one process lifetime. */\nexport type ResolvedSkills = {\n /** Changes when any source's fingerprint changes. */\n readonly fingerprint: string;\n /** Model-facing activation tools; empty when there are no skills. */\n readonly tools: readonly ToolRegistration[];\n /** System-prompt catalog, or null when there are no skills. */\n readonly catalog: string | null;\n readonly warnings: readonly string[];\n};\n\ntype ResolvedSkill = {\n readonly descriptor: SkillDescriptor;\n readonly content: SkillContent;\n readonly source: SkillSource;\n};\n\nexport function skillsFingerprint(sources: readonly SkillSource[]): string {\n return sources\n .map((source) => `${source.id}:${source.fingerprint}`)\n .join(\"|\");\n}\n\nfunction textResult(text: string): ToolExecutionResult {\n return { output: [{ type: \"text\", text }] };\n}\n\nfunction attributes(\n pairs: ReadonlyArray<readonly [string, string | undefined]>\n) {\n return pairs\n .filter((pair): pair is readonly [string, string] => pair[1] !== undefined)\n .map(([key, value]) => ` ${key}=\"${value.replace(/\"/g, \""\")}\"`)\n .join(\"\");\n}\n\nfunction describeResource(resource: SkillResourceDescriptor): string {\n const details = [\n resource.kind,\n resource.encoding ?? \"text\",\n resource.mimeType,\n resource.size === undefined ? undefined : `${resource.size} bytes`\n ].filter((value): value is string => value !== undefined);\n return `- ${resource.path} (${details.join(\", \")})`;\n}\n\nfunction renderSkillContent(skill: ResolvedSkill): string {\n const resources = skill.content.resources ?? [];\n const lines = [\n `<skill_content${attributes([\n [\"name\", skill.descriptor.name],\n [\"version\", skill.descriptor.version]\n ])}>`,\n skill.content.body.trim()\n ];\n if (resources.length > 0) {\n lines.push(\"<skill_resources>\");\n for (const resource of resources) {\n lines.push(\n ` <file${attributes([\n [\"kind\", resource.kind],\n [\"encoding\", resource.encoding ?? \"text\"],\n [\n \"size\",\n resource.size === undefined ? undefined : String(resource.size)\n ]\n ])}>${resource.path}</file>`\n );\n }\n lines.push(\"</skill_resources>\");\n }\n lines.push(\"</skill_content>\", \"\");\n lines.push(\n resources.length === 0\n ? \"No bundled resources.\"\n : [\"Bundled resources:\", ...resources.map(describeResource)].join(\"\\n\")\n );\n return lines.join(\"\\n\");\n}\n\n/**\n * Resolve `agents/skills` sources into a system-prompt catalog and two\n * tools.\n *\n * The first source to list a name wins, as in `SkillRegistry`. pi-durable\n * has no skill concept of its own, so the model activates skills through\n * `activate_skill` and reads bundled files through `read_skill_resource` —\n * the same tools `@cloudflare/think` offers, so skills authored for one\n * framework work in the other.\n */\nexport async function resolveSkillSources(\n sources: readonly SkillSource[]\n): Promise<ResolvedSkills> {\n const fingerprint = skillsFingerprint(sources);\n const warnings: string[] = [];\n const resolved = new Map<string, ResolvedSkill>();\n for (const source of sources) {\n let descriptors: SkillDescriptor[];\n try {\n descriptors = await source.list();\n } catch (error) {\n warnings.push(\n `Skill source \"${source.id}\" failed to list skills: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n continue;\n }\n for (const descriptor of descriptors) {\n if (resolved.has(descriptor.name)) {\n warnings.push(\n `Skill \"${descriptor.name}\" from source \"${source.id}\" is shadowed by an earlier source`\n );\n continue;\n }\n let content: SkillContent | null;\n try {\n content = await source.load(descriptor.name);\n } catch (error) {\n warnings.push(\n `Skill \"${descriptor.name}\" from source \"${source.id}\" failed to load: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n continue;\n }\n if (!content) continue;\n resolved.set(descriptor.name, { descriptor, content, source });\n }\n }\n\n const visible = [...resolved.values()].filter(\n (skill) => skill.descriptor.metadata?.[\"disable-model-invocation\"] !== true\n );\n if (visible.length === 0) {\n return { fingerprint, tools: [], catalog: null, warnings };\n }\n\n const names = visible.map((skill) => skill.descriptor.name);\n const nameSchema = Type.Union(names.map((name) => Type.Literal(name)));\n const byName = new Map(\n visible.map((skill) => [skill.descriptor.name, skill])\n );\n\n const activateParameters = Type.Object({ name: nameSchema });\n const activateSkill: ToolRegistration<typeof activateParameters> = {\n name: \"activate_skill\",\n description:\n \"Activate a skill by name. Use this when the user's task matches one of the available skills; the response contains the skill's full instructions.\",\n parameters: activateParameters,\n replay: \"safe\",\n async execute(input) {\n const skill = byName.get(input.name);\n return textResult(\n skill ? renderSkillContent(skill) : `Skill not found: ${input.name}`\n );\n }\n };\n\n const resourceParameters = Type.Object({\n name: Type.Optional(nameSchema),\n path: Type.String({ minLength: 1 })\n });\n const readResource: ToolRegistration<typeof resourceParameters> = {\n name: \"read_skill_resource\",\n description:\n \"Read a file bundled with a skill, such as a reference document or template. Provide the skill name and the file's path from the skill's resource list.\",\n parameters: resourceParameters,\n replay: \"safe\",\n async execute(input) {\n const target = resolveResourceTarget(byName, input.name, input.path);\n if (!target) {\n return textResult(\n `Skill resource not found: ${input.name ? `${input.name}/` : \"\"}${input.path}`\n );\n }\n const resource = await target.skill.source\n .readResource?.(target.skill.descriptor.name, target.path)\n .catch(() => null);\n if (!resource) {\n return textResult(\n `Skill resource not found: ${target.skill.descriptor.name}/${target.path}`\n );\n }\n return textResult(\n [\n `<skill_resource${attributes([\n [\"name\", target.skill.descriptor.name],\n [\"path\", resource.path],\n [\"kind\", resource.kind],\n [\"encoding\", resource.encoding ?? \"text\"],\n [\"mimeType\", resource.mimeType]\n ])}>`,\n resource.content,\n \"</skill_resource>\"\n ].join(\"\\n\")\n );\n }\n };\n\n const catalog = [\n \"Available skills. When a task matches a skill, use activate_skill with its name before proceeding.\",\n \"\",\n ...visible.map(\n (skill) => `- ${skill.descriptor.name}: ${skill.descriptor.description}`\n )\n ].join(\"\\n\");\n\n return {\n fingerprint,\n tools: [activateSkill, readResource],\n catalog,\n warnings\n };\n}\n\nfunction resolveResourceTarget(\n byName: ReadonlyMap<string, ResolvedSkill>,\n name: string | undefined,\n path: string\n): { readonly skill: ResolvedSkill; readonly path: string } | undefined {\n if (path.split(\"/\").some((segment) => segment === \"..\")) return undefined;\n if (name !== undefined) {\n const skill = byName.get(name);\n return skill && hasResource(skill, path) ? { skill, path } : undefined;\n }\n // A qualified path names the skill in its first segment.\n const [head, ...rest] = path.split(\"/\");\n const skill = head === undefined ? undefined : byName.get(head);\n const relative = rest.join(\"/\");\n return skill && relative && hasResource(skill, relative)\n ? { skill, path: relative }\n : undefined;\n}\n\nfunction hasResource(skill: ResolvedSkill, path: string): boolean {\n return (skill.content.resources ?? []).some(\n (resource) => resource.path === path\n );\n}\n\n/**\n * `agents/skills` sources as a pi extension, named `agents.skills`: the\n * activation tools and a `skills` system prompt section. Install it on\n * pi's registry in the harness factory.\n *\n * ```ts\n * registry.install(await skills(sources));\n * ```\n */\nexport async function skills(\n sources: readonly SkillSource[]\n): Promise<Extension> {\n const resolved = await resolveSkillSources(sources);\n for (const warning of resolved.warnings) {\n console.warn(`pi skills: ${warning}`);\n }\n const catalog = resolved.catalog;\n return {\n name: \"agents.skills\",\n tools: resolved.tools,\n sections: catalog === null ? [] : [{ key: \"skills\", render: () => catalog }]\n };\n}\n"],"mappings":"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"}
|
|
@@ -142,9 +142,10 @@ context:
|
|
|
142
142
|
|
|
143
143
|
```typescript
|
|
144
144
|
import { RpcTarget } from "cloudflare:workers";
|
|
145
|
+
import { Agent, getCurrentAgent } from "agents";
|
|
145
146
|
|
|
146
147
|
class HostCallbackBridge extends RpcTarget {
|
|
147
|
-
constructor(private agent:
|
|
148
|
+
constructor(private agent: MyAgent) {
|
|
148
149
|
super();
|
|
149
150
|
}
|
|
150
151
|
|
|
@@ -155,9 +156,9 @@ class HostCallbackBridge extends RpcTarget {
|
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
|
|
158
|
-
export class
|
|
159
|
+
export class MyAgent extends Agent {
|
|
159
160
|
async handleSandboxCallback() {
|
|
160
|
-
const { agent } = getCurrentAgent<
|
|
161
|
+
const { agent } = getCurrentAgent<MyAgent>();
|
|
161
162
|
// `agent` is available again.
|
|
162
163
|
}
|
|
163
164
|
}
|
|
@@ -166,10 +167,6 @@ export class MyMcpAgent extends McpAgent {
|
|
|
166
167
|
Context restored this way has `connection`, `request`, and `email` unset. It
|
|
167
168
|
is not tied to live client I/O.
|
|
168
169
|
|
|
169
|
-
Server-initiated MCP requests (`elicitInput`, `createMessage`, and `listRoots`)
|
|
170
|
-
on `McpAgent` do not require this indirection because the MCP transport retains
|
|
171
|
-
its owning agent.
|
|
172
|
-
|
|
173
170
|
## API reference
|
|
174
171
|
|
|
175
172
|
The agents package exports one main function for context management:
|
package/docs/harnesses/pi.md
CHANGED
|
@@ -246,7 +246,7 @@ With `keep`, the kept inputs are not work to Pi: its `wake()` counts tasks, not
|
|
|
246
246
|
|
|
247
247
|
## How recovery works
|
|
248
248
|
|
|
249
|
-
The harness schedules one lifecycle job, driven by Pi's `wake()`. While Pi has work running, the job waits on it and refreshes itself as a heartbeat. While Pi's tasks sleep, such as a retry backoff, the job is due when the first one wakes. Pi says when through the `onWake` the factory passes to `Harness.open`, and sets no in-memory timer of its own, so the object can hibernate until then. Without `onWake` the harness still works, but Pi's timer keeps the object in memory, and billed, while a task sleeps, and the harness logs a warning. When Pi has nothing to do, the job completes. If the object is evicted or crashes, the job is still due, so its alarm restarts the object. Pi reopens its stored state and resumes the session's work: a replay-safe tool call runs again, and an unsafe one is reported to the model as interrupted.
|
|
249
|
+
The harness schedules one lifecycle job, driven by Pi's `wake()`. While Pi has work running, the job waits on it and refreshes itself as a heartbeat. While Pi's tasks sleep, such as a retry backoff, the job is due when the first one wakes. Pi says when through the `onWake` the factory passes to `Harness.open`, and sets no in-memory timer of its own, so the object can hibernate until then. A task that sleeps while other work runs, such as a compaction retry during a generation, is resumed from the job's wait when it is due. Without `onWake` the harness still works, but Pi's timer keeps the object in memory, and billed, while a task sleeps, and the harness logs a warning. When Pi has nothing to do, the job completes. If the object is evicted or crashes, the job is still due, so its alarm restarts the object. Pi reopens its stored state and resumes the session's work: a replay-safe tool call runs again, and an unsafe one is reported to the model as interrupted.
|
|
250
250
|
|
|
251
251
|
A task that keeps crashing the object would restart it forever, so Pi parks a task cut off by a crash or eviction `settings.restartLimit` times in a row (default 5) without progress. A parked task is not work: the job completes and the object can hibernate. Its operations show as `parked` in `pending()`, and `wait()` keeps waiting. If your factory passes `onReport` to `Harness.open`, Pi reports it there as `task_parked`. Once you have dealt with the cause, `session.retry()` runs it again and wakes the harness; `session.abort()` gives up on it instead.
|
|
252
252
|
|
|
@@ -32,7 +32,6 @@ Agents SDK supports multiple human-in-the-loop patterns. Choose based on your us
|
|
|
32
32
|
| OpenAI Agents SDK | `needsApproval` | Using OpenAI's agent SDK with conditional approval | [openai-sdk/human-in-the-loop/](https://github.com/cloudflare/agents/tree/main/openai-sdk/human-in-the-loop) |
|
|
33
33
|
| Client-side tools | `onToolCall` | Tools that need browser APIs or user interaction | Pattern below |
|
|
34
34
|
| Stateless servers | Stateless Elicitation | Current MCP tools requesting structured input | [examples/mcp-elicitation-mrtr/](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation-mrtr) |
|
|
35
|
-
| Legacy servers | Legacy Elicitation | Existing sessionful MCP deployments | [examples/mcp-elicitation/](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation) |
|
|
36
35
|
|
|
37
36
|
### Decision Guide
|
|
38
37
|
|
|
@@ -40,7 +39,7 @@ Agents SDK supports multiple human-in-the-loop patterns. Choose based on your us
|
|
|
40
39
|
Is this part of a multi-step workflow?
|
|
41
40
|
├── Yes → Use Workflow Approval (waitForApproval)
|
|
42
41
|
└── No → Are you building an MCP server?
|
|
43
|
-
├── Yes → Use MCP Elicitation (
|
|
42
|
+
├── Yes → Use MCP Elicitation (inputRequired)
|
|
44
43
|
└── No → Is this an AI chat interaction?
|
|
45
44
|
├── Yes → Does the tool need browser APIs?
|
|
46
45
|
│ ├── Yes → Use onToolCall (client-side execution)
|
|
@@ -460,7 +459,7 @@ See the complete example: [openai-sdk/human-in-the-loop/](https://github.com/clo
|
|
|
460
459
|
|
|
461
460
|
### MCP Elicitation
|
|
462
461
|
|
|
463
|
-
|
|
462
|
+
MCP servers built with `createMcpHandler` use multi-round-trip requests (MRTR) for elicitation. A handler returns `inputRequired(...)`; the client gathers the requested input and retries with SDK-managed state. No Worker remains suspended while the user responds.
|
|
464
463
|
|
|
465
464
|
```typescript
|
|
466
465
|
import {
|
|
@@ -517,8 +516,6 @@ The MCP client renders the JSON Schema form and the original operation remains p
|
|
|
517
516
|
|
|
518
517
|
See the [Stateless Elicitation example](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation-mrtr).
|
|
519
518
|
|
|
520
|
-
**Legacy Elicitation** in existing Legacy deployments uses pushed `elicitation/create` requests over a stateful transport. See the explicitly legacy [examples/mcp-elicitation/](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation) example when retaining `McpAgent` or `createLegacyMcpHandler` with `WorkerTransport`.
|
|
521
|
-
|
|
522
519
|
## State Patterns for Approvals
|
|
523
520
|
|
|
524
521
|
Track pending approvals in agent state for UI rendering and persistence:
|
|
@@ -640,10 +637,9 @@ async submitForApproval(request: ApprovalRequest): Promise<string> {
|
|
|
640
637
|
| AIChatAgent tools | [guides/human-in-the-loop/](https://github.com/cloudflare/agents/tree/main/guides/human-in-the-loop) | Chat tool approval with needsApproval + onToolCall |
|
|
641
638
|
| OpenAI Agents SDK | [openai-sdk/human-in-the-loop/](https://github.com/cloudflare/agents/tree/main/openai-sdk/human-in-the-loop) | Conditional tool approval with modal |
|
|
642
639
|
| Stateless Elicitation | [examples/mcp-elicitation-mrtr/](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation-mrtr) | Stateless multi-round input |
|
|
643
|
-
| Legacy Elicitation | [examples/mcp-elicitation/](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation) | Stateful pushed input requests |
|
|
644
640
|
|
|
645
641
|
For detailed API documentation, see:
|
|
646
642
|
|
|
647
643
|
- [Workflows](./workflows.md) - `waitForApproval()`, `approveWorkflow()`, `rejectWorkflow()`
|
|
648
|
-
- [MCP Servers](./mcp-servers.md) - `inputRequired()`
|
|
644
|
+
- [MCP Servers](./mcp-servers.md) - `inputRequired()` elicitation
|
|
649
645
|
- [Callable Methods](./callable-methods.md) - `@callable()` decorator for approval endpoints
|
package/docs/index.md
CHANGED
|
@@ -149,10 +149,10 @@ Building your own harness? Sessions stores the conversation, Streams holds durab
|
|
|
149
149
|
|
|
150
150
|
## MCP (Model Context Protocol)
|
|
151
151
|
|
|
152
|
-
- [Creating MCP Servers](./mcp-servers.md) - Build MCP servers with `
|
|
152
|
+
- [Creating MCP Servers](./mcp-servers.md) - Build stateless MCP servers with `createMcpHandler`
|
|
153
153
|
- [Securing MCP Servers](./securing-mcp-servers.md) - OAuth and authentication for MCP
|
|
154
154
|
- [Connecting to MCP Servers](./mcp-client.md) - `MCPClientManager` on a Durable Object, or `addMcpServer()` on an Agent
|
|
155
|
-
- [MCP Transports](./mcp-transports.md) -
|
|
155
|
+
- [MCP Transports](./mcp-transports.md) - Streamable HTTP on the server, and the transports an Agent can connect with
|
|
156
156
|
|
|
157
157
|
## Operations
|
|
158
158
|
|
package/docs/mcp-client.md
CHANGED
|
@@ -51,19 +51,6 @@ callback URL to `registerServer()` and route that request to the same named
|
|
|
51
51
|
Durable Object. The manager persists the URL and only intercepts a callback
|
|
52
52
|
whose origin and pathname match it.
|
|
53
53
|
|
|
54
|
-
An HTTP-only manager does not need `env`. Pass the Durable Object environment
|
|
55
|
-
when the catalog can contain RPC servers so persisted binding names can be
|
|
56
|
-
resolved after a wake:
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
readonly mcp = new MCPClientManager("my-object", "1.0.0", {
|
|
60
|
-
env: this.env
|
|
61
|
-
});
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
If an RPC row exists without `env`, startup logs a warning identifying the
|
|
65
|
-
server and does not recreate that connection.
|
|
66
|
-
|
|
67
54
|
`Agent` installs this same capability object directly. Existing `this.mcp`,
|
|
68
55
|
`addMcpServer()`, `removeMcpServer()`, and `getMcpServers()` APIs remain
|
|
69
56
|
available.
|
|
@@ -188,7 +175,7 @@ These options are persisted and used when reconnecting after hibernation or afte
|
|
|
188
175
|
|
|
189
176
|
### Protocol negotiation and elicitation
|
|
190
177
|
|
|
191
|
-
Agents uses the MCP v2 client and automatically negotiates the protocol version for every connection. It uses `server/discover` with Stateless servers and falls back to the `initialize` handshake on the same connection for Legacy Streamable HTTP
|
|
178
|
+
Agents uses the MCP v2 client and automatically negotiates the protocol version for every connection. It uses `server/discover` with Stateless servers and falls back to the `initialize` handshake on the same connection for Legacy Streamable HTTP and SSE servers.
|
|
192
179
|
|
|
193
180
|
MCP servers can request input from the client during `callTool`, `getPrompt`, or `readResource`. Stateless Elicitation returns an `input_required` result and completes through MRTR: the MCP SDK invokes the configured `elicitation/create` handler, sends its response, and continues the original request. Agents does not expose an intermediate continuation; the original call stays pending while human input is collected and eventually resolves to the ordinary tool, prompt, or resource result. Legacy Elicitation on Legacy servers continues to use pushed `elicitation/create` requests. Both generations share the same handlers.
|
|
194
181
|
|
|
@@ -243,8 +230,6 @@ MCP server URLs are validated before connection to prevent Server-Side Request F
|
|
|
243
230
|
|
|
244
231
|
Loopback development URLs such as `localhost`, `127.0.0.1`, and `::1` are allowed.
|
|
245
232
|
|
|
246
|
-
If you need to connect to another internal MCP server, use the [RPC transport](./mcp-transports.md) with a Durable Object binding instead of HTTP.
|
|
247
|
-
|
|
248
233
|
### Return Value
|
|
249
234
|
|
|
250
235
|
`addMcpServer()` returns the connection state:
|
|
@@ -666,17 +651,6 @@ async addMcpServer(
|
|
|
666
651
|
| { id: string; state: "authenticating"; authUrl: string }
|
|
667
652
|
>
|
|
668
653
|
|
|
669
|
-
// RPC transport (Durable Object binding — no HTTP overhead)
|
|
670
|
-
async addMcpServer(
|
|
671
|
-
name: string,
|
|
672
|
-
binding: DurableObjectNamespace,
|
|
673
|
-
options?: {
|
|
674
|
-
props?: Record<string, unknown>; // passed to the McpAgent's onStart(props)
|
|
675
|
-
client?: ClientOptions;
|
|
676
|
-
retry?: RetryOptions;
|
|
677
|
-
}
|
|
678
|
-
): Promise<{ id: string; state: "ready" }>
|
|
679
|
-
|
|
680
654
|
// Legacy signature (still supported)
|
|
681
655
|
async addMcpServer(
|
|
682
656
|
name: string,
|
|
@@ -689,7 +663,7 @@ async addMcpServer(
|
|
|
689
663
|
|
|
690
664
|
Add and connect to an MCP server. Throws if connection or discovery fails.
|
|
691
665
|
|
|
692
|
-
`callbackHost` is automatically derived from the incoming HTTP request or WebSocket connection URI — you almost never need to set it explicitly. It is only needed when the auto-detected host does not match your desired OAuth callback origin (for example, behind a reverse proxy).
|
|
666
|
+
`callbackHost` is automatically derived from the incoming HTTP request or WebSocket connection URI — you almost never need to set it explicitly. It is only needed when the auto-detected host does not match your desired OAuth callback origin (for example, behind a reverse proxy). See [MCP Transports](./mcp-transports.md) for the transport options.
|
|
693
667
|
|
|
694
668
|
Calling `addMcpServer` is idempotent when both the server name **and** URL match an existing active connection — the existing connection is returned without creating a duplicate. This makes it safe to call in `onStart()` without worrying about duplicate connections on restart.
|
|
695
669
|
|