agents 0.30.1 → 0.32.0

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.
Files changed (182) hide show
  1. package/README.md +2 -1
  2. package/dist/{agent-routing-CDbpVjYq.d.ts → agent-routing-vVMA7m8Y.d.ts} +69 -608
  3. package/dist/agent-routing.d.ts +1 -1
  4. package/dist/agent-tool-types.d.ts +1 -1
  5. package/dist/{agent-tools-C8DH8Yii.d.ts → agent-tools-gGXXkf1z.d.ts} +2 -2
  6. package/dist/agent-tools.d.ts +1 -1
  7. package/dist/agent-tools.js +0 -1
  8. package/dist/agent-tools.js.map +1 -1
  9. package/dist/{ai-iXw0uOGj.d.ts → ai-CDn3Abax.d.ts} +30 -4
  10. package/dist/{ai-sdk-DRPh2rRa.js → ai-sdk-Kon40i0q.js} +2 -36
  11. package/dist/ai-sdk-Kon40i0q.js.map +1 -0
  12. package/dist/browser/ai.d.ts +1 -1
  13. package/dist/browser/ai.js +16 -3
  14. package/dist/browser/ai.js.map +1 -1
  15. package/dist/browser/index.d.ts +64 -16
  16. package/dist/browser/index.js +4 -4
  17. package/dist/browser/tools/ai-sdk.d.ts +7 -5
  18. package/dist/browser/tools/ai-sdk.js +4 -4
  19. package/dist/browser/tools/ai-sdk.js.map +1 -1
  20. package/dist/browser/tools/pi.d.ts +9 -15
  21. package/dist/browser/tools/pi.js +6 -14
  22. package/dist/browser/tools/pi.js.map +1 -1
  23. package/dist/browser/tools/tanstack-ai.d.ts +48 -25
  24. package/dist/browser/tools/tanstack-ai.js +8 -4
  25. package/dist/browser/tools/tanstack-ai.js.map +1 -1
  26. package/dist/{browser-DccPw_wW.js → browser-1eUbPvC2.js} +413 -14
  27. package/dist/browser-1eUbPvC2.js.map +1 -0
  28. package/dist/{browser-DlXhSAdf.d.ts → browser-D9RNpJuT.d.ts} +254 -20
  29. package/dist/browser-tool-BhjCcNVE.d.ts +71 -0
  30. package/dist/browser-tool-DRan7Lk6.js +497 -0
  31. package/dist/browser-tool-DRan7Lk6.js.map +1 -0
  32. package/dist/capability-id-B3J8Nok_.js +13 -0
  33. package/dist/capability-id-B3J8Nok_.js.map +1 -0
  34. package/dist/catalog-BpOejOsE.js +239 -0
  35. package/dist/catalog-BpOejOsE.js.map +1 -0
  36. package/dist/catalog-BpiVV1OF.d.ts +173 -0
  37. package/dist/chat/index.d.ts +2 -2
  38. package/dist/chat-sdk/index.d.ts +3 -3
  39. package/dist/chat-sdk/index.js +1 -1
  40. package/dist/client-4OlMIJx-.js.map +1 -1
  41. package/dist/{client-BnYbNT3z.js → client-DMWujQJ1.js} +68 -244
  42. package/dist/client-DMWujQJ1.js.map +1 -0
  43. package/dist/{client-C27CtRZr.d.ts → client-eSfgYvd3.d.ts} +9 -7
  44. package/dist/client.d.ts +1 -1
  45. package/dist/{cloudflare-Dzvc7V2N.js → cloudflare-CUUm_9q9.js} +26 -4
  46. package/dist/cloudflare-CUUm_9q9.js.map +1 -0
  47. package/dist/{connector-DAXBOIIi.js → connector-CM-uuxKM.js} +96 -8
  48. package/dist/connector-CM-uuxKM.js.map +1 -0
  49. package/dist/{connector-20DJ6p4k.d.ts → connector-DZ71EjE-.d.ts} +109 -9
  50. package/dist/{do-oauth-client-provider-DfsUcpc3.d.ts → do-oauth-client-provider-DtNqv7oj.d.ts} +26 -2
  51. package/dist/{index-YSKgfgg9.d.ts → emitter-BJZtCVjQ.d.ts} +6 -140
  52. package/dist/emitter-DPOVcCln.js +15 -0
  53. package/dist/emitter-DPOVcCln.js.map +1 -0
  54. package/dist/experimental/channels/web/index.js +1 -1
  55. package/dist/experimental/webmcp.js +1 -1
  56. package/dist/{handler-stateless-Bly_nSH3.js → handler-stateless-B5hU3dxK.js} +35 -2
  57. package/dist/handler-stateless-B5hU3dxK.js.map +1 -0
  58. package/dist/{handler-stateless-DxYpJ_XF.d.ts → handler-stateless-C90kD5_R.d.ts} +3 -47
  59. package/dist/harness/container/index.d.ts +15 -15
  60. package/dist/harness/container/index.js +0 -1
  61. package/dist/harness/container/index.js.map +1 -1
  62. package/dist/harness/container/runtime/index.js +2 -2
  63. package/dist/harness/opencode/index.js +0 -1
  64. package/dist/harness/opencode/index.js.map +1 -1
  65. package/dist/harness/pi/index.d.ts +15 -7
  66. package/dist/harness/pi/index.js +74 -151
  67. package/dist/harness/pi/index.js.map +1 -1
  68. package/dist/harness/think/index.d.ts +2 -1
  69. package/dist/{images-xaDv2Fqe.js → images-CfuRCjwq.js} +1 -5
  70. package/dist/images-CfuRCjwq.js.map +1 -0
  71. package/dist/{index-DCxk3OSJ.d.ts → index-8k4qsX1T.d.ts} +2 -28
  72. package/dist/index.d.ts +15 -16
  73. package/dist/index.js +3 -3
  74. package/dist/internal-hosts-Kfp5A392.d.ts +230 -0
  75. package/dist/internal-kmw70xwR.js.map +1 -1
  76. package/dist/invoker-BC1SZ3q1.js +26 -0
  77. package/dist/invoker-BC1SZ3q1.js.map +1 -0
  78. package/dist/invoker-DoLFPJZV.d.ts +21 -0
  79. package/dist/markdown-BZ-VLtA1.js +86 -0
  80. package/dist/markdown-BZ-VLtA1.js.map +1 -0
  81. package/dist/mcp/client/do-oauth-client-provider.d.ts +4 -2
  82. package/dist/mcp/client/do-oauth-client-provider.js +15 -1
  83. package/dist/mcp/client/do-oauth-client-provider.js.map +1 -1
  84. package/dist/mcp/client/index.d.ts +15 -1
  85. package/dist/mcp/client/index.js +2 -2
  86. package/dist/mcp/client/x402.d.ts +7 -5
  87. package/dist/mcp/client/x402.js +34 -1
  88. package/dist/mcp/client/x402.js.map +1 -1
  89. package/dist/mcp/index.d.ts +561 -27
  90. package/dist/mcp/index.js +180 -3
  91. package/dist/mcp/index.js.map +1 -1
  92. package/dist/mcp/server/index.d.ts +1 -1
  93. package/dist/mcp/server/index.js +1 -1
  94. package/dist/models/ai-sdk/index.d.ts +2 -2
  95. package/dist/models/ai-sdk/index.js +1 -1
  96. package/dist/models/opencode/index.d.ts +2 -2
  97. package/dist/models/opencode/index.js +1 -1
  98. package/dist/models/pi-ai/index.d.ts +3 -28
  99. package/dist/models/pi-ai/index.js +160 -18
  100. package/dist/models/pi-ai/index.js.map +1 -1
  101. package/dist/observability/ai/index.js +1 -2
  102. package/dist/observability/ai/index.js.map +1 -1
  103. package/dist/observability/index.d.ts +170 -9
  104. package/dist/observability/index.js +386 -11
  105. package/dist/observability/index.js.map +1 -1
  106. package/dist/protocol-BEgrJYC5.d.ts +29 -29
  107. package/dist/react.d.ts +2 -2
  108. package/dist/resource-B59OV2a2.js +325 -0
  109. package/dist/resource-B59OV2a2.js.map +1 -0
  110. package/dist/routing/index.d.ts +1 -1
  111. package/dist/script-CFDLTk5b.d.ts +91 -0
  112. package/dist/script-workspace-BpdAYq1R.d.ts +41 -0
  113. package/dist/serializable.d.ts +1 -1
  114. package/dist/{session-store-yvwq1f9x.js → session-7CDgVE0z.js} +455 -33
  115. package/dist/session-7CDgVE0z.js.map +1 -0
  116. package/dist/{session-store-_IK6dV-w.d.ts → session-store-B7Ca-u1t.d.ts} +8 -1
  117. package/dist/settings-Dk2il2Hf.js.map +1 -1
  118. package/dist/skill-DRBcIBjC.d.ts +221 -0
  119. package/dist/skills/index.d.ts +275 -36
  120. package/dist/skills/index.js +265 -1534
  121. package/dist/skills/index.js.map +1 -1
  122. package/dist/skills/runner.d.ts +37 -0
  123. package/dist/skills/runner.js +912 -0
  124. package/dist/skills/runner.js.map +1 -0
  125. package/dist/skills/tools/ai-sdk.d.ts +36 -0
  126. package/dist/skills/tools/ai-sdk.js +94 -0
  127. package/dist/skills/tools/ai-sdk.js.map +1 -0
  128. package/dist/source-9RQzlEhM.d.ts +112 -0
  129. package/dist/{src-DfLzdwlK.js → src-DoGNJ4Ud.js} +21 -6
  130. package/dist/src-DoGNJ4Ud.js.map +1 -0
  131. package/dist/sub-routing.d.ts +12 -12
  132. package/dist/tool-CTp72Jb_.d.ts +10 -10
  133. package/dist/tool-CxKsvxjm.js.map +1 -1
  134. package/dist/tool-DkT_DJBm.js.map +1 -1
  135. package/dist/tool-DoVberpN.d.ts +22 -22
  136. package/dist/tool-helpers-BI7ifO2S.js +255 -0
  137. package/dist/tool-helpers-BI7ifO2S.js.map +1 -0
  138. package/dist/{transport-CPh7YGiw.d.ts → transport-5moD2pW5.d.ts} +1 -43
  139. package/dist/types-D2naO3rA.d.ts +49 -0
  140. package/dist/vite.d.ts +2 -2
  141. package/dist/vite.js +10 -258
  142. package/dist/vite.js.map +1 -1
  143. package/dist/voice/index.d.ts +1 -1
  144. package/dist/websockets/index.js +1 -1
  145. package/dist/{websockets-CyoeosdO.js → websockets-C6SJtWCJ.js} +1 -2
  146. package/dist/{websockets-CyoeosdO.js.map → websockets-C6SJtWCJ.js.map} +1 -1
  147. package/dist/workflow-types.d.ts +1 -1
  148. package/dist/workflows.d.ts +1 -1
  149. package/dist/workflows.js +0 -1
  150. package/dist/workflows.js.map +1 -1
  151. package/docs/browse-the-web.md +247 -413
  152. package/docs/harnesses/pi.md +36 -30
  153. package/docs/index.md +4 -4
  154. package/docs/mcp-client.md +25 -1
  155. package/docs/mcp-servers.md +26 -7
  156. package/docs/models-pi-ai.md +9 -11
  157. package/docs/models.md +1 -8
  158. package/docs/observability.md +5 -3
  159. package/docs/securing-mcp-servers.md +113 -302
  160. package/package.json +31 -17
  161. package/dist/ai-sdk-DRPh2rRa.js.map +0 -1
  162. package/dist/browser-DccPw_wW.js.map +0 -1
  163. package/dist/browser-tool-B1mhifin.js +0 -616
  164. package/dist/browser-tool-B1mhifin.js.map +0 -1
  165. package/dist/browser-tool-Cxjk5sgG.d.ts +0 -92
  166. package/dist/client-BnYbNT3z.js.map +0 -1
  167. package/dist/cloudflare-Dzvc7V2N.js.map +0 -1
  168. package/dist/connector-DAXBOIIi.js.map +0 -1
  169. package/dist/handler-stateless-Bly_nSH3.js.map +0 -1
  170. package/dist/images-xaDv2Fqe.js.map +0 -1
  171. package/dist/index-iv9FTXX9.d.ts +0 -311
  172. package/dist/invoker-CG0_p_Wq.d.ts +0 -20
  173. package/dist/invoker-CHMnoxIA.js +0 -57
  174. package/dist/invoker-CHMnoxIA.js.map +0 -1
  175. package/dist/session-store-yvwq1f9x.js.map +0 -1
  176. package/dist/skills/compile.d.ts +0 -40
  177. package/dist/skills/compile.js +0 -65
  178. package/dist/skills/compile.js.map +0 -1
  179. package/dist/src-DfLzdwlK.js.map +0 -1
  180. package/dist/tool-helpers-O5BDVEAY.js +0 -58
  181. package/dist/tool-helpers-O5BDVEAY.js.map +0 -1
  182. package/skills-module.d.ts +0 -22
@@ -1,8 +1,8 @@
1
1
  import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "../../classPrivateFieldGet2-DZBYAB34.js";
2
2
  import { t as _classPrivateMethodInitSpec } from "../../classPrivateMethodInitSpec-qMjJ6sHQ.js";
3
3
  import { t as LifecycleCapability } from "../../capability-DBAKc6UZ.js";
4
- import "../../lifecycle-v1rKHV2C.js";
5
4
  import { isDurableObjectMemoryLimitReset } from "../../retries.js";
5
+ import { i as renderSkillResource, n as formatSkillDiagnostic, r as loadSkillCatalog } from "../../catalog-BpOejOsE.js";
6
6
  import { Type } from "@earendil-works/pi-ai";
7
7
  import { LiveDoc, ROOT_CONVERSATION_ID, watchEvents } from "@earendil-works/pi-durable";
8
8
  import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/chord/context";
@@ -112,8 +112,6 @@ var _onWake = /* @__PURE__ */ new WeakMap();
112
112
  * nothing to do, the job completes. An eviction mid-run leaves the job due,
113
113
  * so its alarm restarts the object, pi reopens and resumes its own tasks,
114
114
  * and the job waits again.
115
- *
116
- * @beta The API may change between releases.
117
115
  */
118
116
  var PiHarness = class extends LifecycleCapability {
119
117
  constructor(options) {
@@ -211,11 +209,27 @@ var PiHarness = class extends LifecycleCapability {
211
209
  const id = options.session === void 0 ? void 0 : conversationId(options.session);
212
210
  const inspection = await pi.inspect(BG);
213
211
  const parked = await _assertClassBrand(_PiHarness_brand, this, _parked).call(this, inspection);
214
- return inspection.submissions.filter((record) => record.requestId !== void 0 && (id === void 0 || record.conversationId === id)).map((record) => ({
215
- operationId: record.requestId,
216
- session: String(record.conversationId),
217
- status: record.status === "queued" ? "queued" : parked.some((task) => task.sessions.has(record.conversationId)) ? "parked" : "running"
218
- }));
212
+ return inspection.submissions.flatMap((record) => {
213
+ if (record.requestId === void 0) return [];
214
+ if (id !== void 0 && record.conversationId !== id) return [];
215
+ let status;
216
+ switch (record.status) {
217
+ case "queued":
218
+ status = "queued";
219
+ break;
220
+ case "placed":
221
+ status = parked.some((task) => task.sessions.has(record.conversationId)) ? "parked" : "running";
222
+ break;
223
+ case "done":
224
+ case "unanswered": return [];
225
+ default: return record;
226
+ }
227
+ return [{
228
+ operationId: record.requestId,
229
+ session: String(record.conversationId),
230
+ status
231
+ }];
232
+ });
219
233
  }
220
234
  /** The opened pi Harness, for anything the interface does not cover. */
221
235
  async pi() {
@@ -270,9 +284,16 @@ var PiHarness = class extends LifecycleCapability {
270
284
  const id = conversationId(session);
271
285
  const record = await storage.submissionByRequest(id, operationId, BG);
272
286
  if (!record || record.status === "done" || record.status === "unanswered") return false;
273
- const withdrawn = await pi.abortSubmission(record.id, BG, id);
274
- if (withdrawn === "already_placed") await (await pi.conversation(id, BG))?.abort(BG);
275
- return withdrawn !== "settled" && withdrawn !== "not_found";
287
+ const outcome = await pi.abortSubmission(record.id, BG, id);
288
+ switch (outcome) {
289
+ case "aborted": return true;
290
+ case "already_placed":
291
+ await (await pi.conversation(id, BG))?.abort(BG);
292
+ return true;
293
+ case "settled":
294
+ case "not_found": return false;
295
+ default: return outcome;
296
+ }
276
297
  }
277
298
  /** @internal Wait for pi to settle an operation, by its request id. */
278
299
  async settled(session, operationId, signal) {
@@ -634,182 +655,84 @@ var PiSessions = class {
634
655
  };
635
656
  //#endregion
636
657
  //#region src/harness/pi/skills.ts
637
- function skillsFingerprint(sources) {
638
- return sources.map((source) => `${source.id}:${source.fingerprint}`).join("|");
639
- }
640
658
  function textResult(text) {
641
659
  return { output: [{
642
660
  type: "text",
643
661
  text
644
662
  }] };
645
663
  }
646
- function attributes(pairs) {
647
- return pairs.filter((pair) => pair[1] !== void 0).map(([key, value]) => ` ${key}="${value.replace(/"/g, """)}"`).join("");
648
- }
649
- function describeResource(resource) {
650
- const details = [
651
- resource.kind,
652
- resource.encoding ?? "text",
653
- resource.mimeType,
654
- resource.size === void 0 ? void 0 : `${resource.size} bytes`
655
- ].filter((value) => value !== void 0);
656
- return `- ${resource.path} (${details.join(", ")})`;
657
- }
658
- function renderSkillContent(skill) {
659
- const resources = skill.content.resources ?? [];
660
- const lines = [`<skill_content${attributes([["name", skill.descriptor.name], ["version", skill.descriptor.version]])}>`, skill.content.body.trim()];
661
- if (resources.length > 0) {
662
- lines.push("<skill_resources>");
663
- for (const resource of resources) lines.push(` <file${attributes([
664
- ["kind", resource.kind],
665
- ["encoding", resource.encoding ?? "text"],
666
- ["size", resource.size === void 0 ? void 0 : String(resource.size)]
667
- ])}>${resource.path}</file>`);
668
- lines.push("</skill_resources>");
669
- }
670
- lines.push("</skill_content>", "");
671
- lines.push(resources.length === 0 ? "No bundled resources." : ["Bundled resources:", ...resources.map(describeResource)].join("\n"));
672
- return lines.join("\n");
664
+ /** A failed tool call: pi sends it to the model as an error result. */
665
+ function errorResult(error) {
666
+ return {
667
+ output: [{
668
+ type: "text",
669
+ text: error.message
670
+ }],
671
+ isError: true
672
+ };
673
673
  }
674
674
  /**
675
- * Resolve `agents/skills` sources into a system-prompt catalog and two
676
- * tools.
677
- *
678
- * The first source to list a name wins, as in `SkillRegistry`. pi-durable
679
- * has no skill concept of its own, so the model activates skills through
680
- * `activate_skill` and reads bundled files through `read_skill_resource` —
681
- * the same tools `@cloudflare/think` offers, so skills authored for one
682
- * framework work in the other.
675
+ * Model-facing tools for a catalog: `activate_skill` and
676
+ * `read_skill_resource`. These match the tools `@cloudflare/think` offers, so
677
+ * a skill behaves the same in both frameworks.
683
678
  */
684
- async function resolveSkillSources(sources) {
685
- const fingerprint = skillsFingerprint(sources);
686
- const warnings = [];
687
- const resolved = /* @__PURE__ */ new Map();
688
- for (const source of sources) {
689
- let descriptors;
690
- try {
691
- descriptors = await source.list();
692
- } catch (error) {
693
- warnings.push(`Skill source "${source.id}" failed to list skills: ${error instanceof Error ? error.message : String(error)}`);
694
- continue;
695
- }
696
- for (const descriptor of descriptors) {
697
- if (resolved.has(descriptor.name)) {
698
- warnings.push(`Skill "${descriptor.name}" from source "${source.id}" is shadowed by an earlier source`);
699
- continue;
700
- }
701
- let content;
702
- try {
703
- content = await source.load(descriptor.name);
704
- } catch (error) {
705
- warnings.push(`Skill "${descriptor.name}" from source "${source.id}" failed to load: ${error instanceof Error ? error.message : String(error)}`);
706
- continue;
707
- }
708
- if (!content) continue;
709
- resolved.set(descriptor.name, {
710
- descriptor,
711
- content,
712
- source
713
- });
714
- }
715
- }
716
- const visible = [...resolved.values()].filter((skill) => skill.descriptor.metadata?.["disable-model-invocation"] !== true);
717
- if (visible.length === 0) return {
718
- fingerprint,
719
- tools: [],
720
- catalog: null,
721
- warnings
722
- };
723
- const names = visible.map((skill) => skill.descriptor.name);
679
+ function skillTools(catalog) {
680
+ const names = catalog.skills.map((entry) => entry.skill.name);
681
+ if (names.length === 0) return [];
724
682
  const nameSchema = Type.Union(names.map((name) => Type.Literal(name)));
725
- const byName = new Map(visible.map((skill) => [skill.descriptor.name, skill]));
726
- const activateSkill = {
683
+ return [{
727
684
  name: "activate_skill",
728
685
  description: "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.",
729
686
  parameters: Type.Object({ name: nameSchema }),
730
687
  replay: "safe",
731
688
  async execute(input) {
732
- const skill = byName.get(input.name);
733
- return textResult(skill ? renderSkillContent(skill) : `Skill not found: ${input.name}`);
689
+ const activated = catalog.activate(input.name);
690
+ return activated.ok ? textResult(activated.value) : errorResult(activated.error);
734
691
  }
735
- };
736
- const readResource = {
692
+ }, {
737
693
  name: "read_skill_resource",
738
- description: "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.",
694
+ description: "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, or a qualified path like skill-name/references/file.md.",
739
695
  parameters: Type.Object({
740
696
  name: Type.Optional(nameSchema),
741
697
  path: Type.String({ minLength: 1 })
742
698
  }),
743
699
  replay: "safe",
744
700
  async execute(input) {
745
- const target = resolveResourceTarget(byName, input.name, input.path);
746
- if (!target) return textResult(`Skill resource not found: ${input.name ? `${input.name}/` : ""}${input.path}`);
747
- const resource = await target.skill.source.readResource?.(target.skill.descriptor.name, target.path).catch(() => null);
748
- if (!resource) return textResult(`Skill resource not found: ${target.skill.descriptor.name}/${target.path}`);
749
- return textResult([
750
- `<skill_resource${attributes([
751
- ["name", target.skill.descriptor.name],
752
- ["path", resource.path],
753
- ["kind", resource.kind],
754
- ["encoding", resource.encoding ?? "text"],
755
- ["mimeType", resource.mimeType]
756
- ])}>`,
757
- resource.content,
758
- "</skill_resource>"
759
- ].join("\n"));
701
+ const read = await catalog.readResource(input.name === void 0 ? { path: input.path } : {
702
+ name: input.name,
703
+ path: input.path
704
+ });
705
+ return read.ok ? textResult(renderSkillResource(read.value)) : errorResult(read.error);
760
706
  }
761
- };
762
- const catalog = [
763
- "Available skills. When a task matches a skill, use activate_skill with its name before proceeding.",
764
- "",
765
- ...visible.map((skill) => `- ${skill.descriptor.name}: ${skill.descriptor.description}`)
766
- ].join("\n");
767
- return {
768
- fingerprint,
769
- tools: [activateSkill, readResource],
770
- catalog,
771
- warnings
772
- };
773
- }
774
- function resolveResourceTarget(byName, name, path) {
775
- if (path.split("/").some((segment) => segment === "..")) return void 0;
776
- if (name !== void 0) {
777
- const skill = byName.get(name);
778
- return skill && hasResource(skill, path) ? {
779
- skill,
780
- path
781
- } : void 0;
782
- }
783
- const [head, ...rest] = path.split("/");
784
- const skill = head === void 0 ? void 0 : byName.get(head);
785
- const relative = rest.join("/");
786
- return skill && relative && hasResource(skill, relative) ? {
787
- skill,
788
- path: relative
789
- } : void 0;
790
- }
791
- function hasResource(skill, path) {
792
- return (skill.content.resources ?? []).some((resource) => resource.path === path);
707
+ }];
793
708
  }
794
709
  /**
795
710
  * `agents/skills` sources as a pi extension, named `agents.skills`: the
796
711
  * activation tools and a `skills` system prompt section. Install it on
797
- * pi's registry in the harness factory.
712
+ * pi's registry in the harness factory. Sources are read once, when the
713
+ * extension is built.
798
714
  *
799
715
  * ```ts
800
- * registry.install(await skills(sources));
716
+ * import * as agentSkills from "agents/skills";
717
+ *
718
+ * registry.install(
719
+ * await skills([agentSkills.workspace(workspace), agentSkills.r2(env.SKILLS)])
720
+ * );
801
721
  * ```
722
+ *
723
+ * @param sources - Skill sources in priority order.
724
+ * @returns The pi extension.
802
725
  */
803
726
  async function skills(sources) {
804
- const resolved = await resolveSkillSources(sources);
805
- for (const warning of resolved.warnings) console.warn(`pi skills: ${warning}`);
806
- const catalog = resolved.catalog;
727
+ const catalog = await loadSkillCatalog(sources);
728
+ for (const diagnostic of catalog.diagnostics) console.warn(`pi skills: ${formatSkillDiagnostic(diagnostic)}`);
729
+ const prompt = catalog.prompt();
807
730
  return {
808
731
  name: "agents.skills",
809
- tools: resolved.tools,
810
- sections: catalog === null ? [] : [{
732
+ tools: skillTools(catalog),
733
+ sections: prompt === null ? [] : [{
811
734
  key: "skills",
812
- render: () => catalog
735
+ render: () => prompt
813
736
  }]
814
737
  };
815
738
  }
@@ -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 * 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, \"&quot;\")}\"`)\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":["exhaustive"],"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 */\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.flatMap((record): PiPendingOperation[] => {\n if (record.requestId === undefined) return [];\n if (id !== undefined && record.conversationId !== id) return [];\n let status: PiPendingOperation[\"status\"];\n switch (record.status) {\n case \"queued\":\n status = \"queued\";\n break;\n case \"placed\":\n status = parked.some((task) =>\n task.sessions.has(record.conversationId)\n )\n ? \"parked\"\n : \"running\";\n break;\n case \"done\":\n case \"unanswered\":\n // Settled. pi's inspection holds only unsettled submissions.\n return [];\n default: {\n // A status added by a newer pi: decide what it reports.\n const exhaustive: never = record;\n return exhaustive;\n }\n }\n return [\n {\n operationId: record.requestId,\n session: String(record.conversationId),\n status\n }\n ];\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 outcome = await pi.abortSubmission(record.id, BG, id);\n switch (outcome) {\n case \"aborted\":\n return true;\n case \"already_placed\":\n await (await pi.conversation(id, BG))?.abort(BG);\n return true;\n case \"settled\":\n case \"not_found\":\n return false;\n default: {\n // An outcome added by a newer pi: decide whether it withdrew.\n const exhaustive: never = outcome;\n return exhaustive;\n }\n }\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 {\n formatSkillDiagnostic,\n loadSkillCatalog,\n renderSkillResource,\n type SkillCatalog\n} from \"../../skills/catalog\";\nimport type { SkillSource } from \"../../skills/source\";\n\nfunction textResult(text: string): ToolExecutionResult {\n return { output: [{ type: \"text\", text }] };\n}\n\n/** A failed tool call: pi sends it to the model as an error result. */\nfunction errorResult(error: Error): ToolExecutionResult {\n return { output: [{ type: \"text\", text: error.message }], isError: true };\n}\n\n/**\n * Model-facing tools for a catalog: `activate_skill` and\n * `read_skill_resource`. These match the tools `@cloudflare/think` offers, so\n * a skill behaves the same in both frameworks.\n */\nfunction skillTools(catalog: SkillCatalog): ToolRegistration[] {\n const names = catalog.skills.map((entry) => entry.skill.name);\n if (names.length === 0) return [];\n const nameSchema = Type.Union(names.map((name) => Type.Literal(name)));\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 activated = catalog.activate(input.name);\n return activated.ok\n ? textResult(activated.value)\n : errorResult(activated.error);\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, or a qualified path like skill-name/references/file.md.\",\n parameters: resourceParameters,\n replay: \"safe\",\n async execute(input) {\n const read = await catalog.readResource(\n input.name === undefined\n ? { path: input.path }\n : { name: input.name, path: input.path }\n );\n return read.ok\n ? textResult(renderSkillResource(read.value))\n : errorResult(read.error);\n }\n };\n\n return [activateSkill, readResource];\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. Sources are read once, when the\n * extension is built.\n *\n * ```ts\n * import * as agentSkills from \"agents/skills\";\n *\n * registry.install(\n * await skills([agentSkills.workspace(workspace), agentSkills.r2(env.SKILLS)])\n * );\n * ```\n *\n * @param sources - Skill sources in priority order.\n * @returns The pi extension.\n */\nexport async function skills(\n sources: readonly SkillSource[]\n): Promise<Extension> {\n const catalog = await loadSkillCatalog(sources);\n for (const diagnostic of catalog.diagnostics) {\n console.warn(`pi skills: ${formatSkillDiagnostic(diagnostic)}`);\n }\n const prompt = catalog.prompt();\n return {\n name: \"agents.skills\",\n tools: skillTools(catalog),\n sections: prompt === null ? [] : [{ key: \"skills\", render: () => prompt }]\n };\n}\n"],"mappings":"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"}
@@ -82,7 +82,8 @@ type ThinkHarnessListener = (session: string, event: ThinkSessionEvent) => void;
82
82
  //#endregion
83
83
  //#region src/harness/think/records.d.ts
84
84
  /** What an operation asks the harness to do, as stored. */
85
- type OperationInput = /** Place these user messages, then answer them. */ | {
85
+ type OperationInput =
86
+ | /** Place these user messages, then answer them. */ {
86
87
  readonly kind: "messages";
87
88
  readonly messages: readonly UIMessage[];
88
89
  } /** Record a tool result or approval, then continue the turn it unblocks. */