@earendil-works/pi-agent-core 0.85.0 → 0.86.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 (198) hide show
  1. package/README.md +16 -6
  2. package/dist/agent-loop.d.ts.map +1 -1
  3. package/dist/agent-loop.js +64 -22
  4. package/dist/agent-loop.js.map +1 -1
  5. package/dist/agent.d.ts +5 -3
  6. package/dist/agent.d.ts.map +1 -1
  7. package/dist/agent.js +15 -6
  8. package/dist/agent.js.map +1 -1
  9. package/dist/harness/config.d.ts +1 -1
  10. package/dist/harness/config.d.ts.map +1 -1
  11. package/dist/harness/config.js +10 -2
  12. package/dist/harness/config.js.map +1 -1
  13. package/dist/harness/env/nodejs.d.ts +2 -1
  14. package/dist/harness/env/nodejs.d.ts.map +1 -1
  15. package/dist/harness/env/nodejs.js +98 -27
  16. package/dist/harness/env/nodejs.js.map +1 -1
  17. package/dist/harness/execution/tools.d.ts +1 -1
  18. package/dist/harness/execution/tools.d.ts.map +1 -1
  19. package/dist/harness/execution/tools.js +0 -2
  20. package/dist/harness/execution/tools.js.map +1 -1
  21. package/dist/harness/messages.d.ts.map +1 -1
  22. package/dist/harness/messages.js +1 -0
  23. package/dist/harness/messages.js.map +1 -1
  24. package/dist/harness/pico3/bash.d.ts +10 -0
  25. package/dist/harness/pico3/bash.d.ts.map +1 -0
  26. package/dist/harness/pico3/bash.js +25 -0
  27. package/dist/harness/pico3/bash.js.map +1 -0
  28. package/dist/harness/pico3/bounded.d.ts +21 -0
  29. package/dist/harness/pico3/bounded.d.ts.map +1 -0
  30. package/dist/harness/pico3/bounded.js +98 -0
  31. package/dist/harness/pico3/bounded.js.map +1 -0
  32. package/dist/harness/pico3/chord.d.ts +41 -0
  33. package/dist/harness/pico3/chord.d.ts.map +1 -0
  34. package/dist/harness/pico3/chord.js +130 -0
  35. package/dist/harness/pico3/chord.js.map +1 -0
  36. package/dist/harness/pico3/context.d.ts +16 -0
  37. package/dist/harness/pico3/context.d.ts.map +1 -0
  38. package/dist/harness/pico3/context.js +88 -0
  39. package/dist/harness/pico3/context.js.map +1 -0
  40. package/dist/harness/pico3/harness.d.ts +197 -0
  41. package/dist/harness/pico3/harness.d.ts.map +1 -0
  42. package/dist/harness/pico3/harness.js +654 -0
  43. package/dist/harness/pico3/harness.js.map +1 -0
  44. package/dist/harness/pico3/hooks.d.ts +10 -0
  45. package/dist/harness/pico3/hooks.d.ts.map +1 -0
  46. package/dist/harness/pico3/hooks.js +33 -0
  47. package/dist/harness/pico3/hooks.js.map +1 -0
  48. package/dist/harness/pico3/index.d.ts +19 -0
  49. package/dist/harness/pico3/index.d.ts.map +1 -0
  50. package/dist/harness/pico3/index.js +15 -0
  51. package/dist/harness/pico3/index.js.map +1 -0
  52. package/dist/harness/pico3/jsonl.d.ts +58 -0
  53. package/dist/harness/pico3/jsonl.d.ts.map +1 -0
  54. package/dist/harness/pico3/jsonl.js +325 -0
  55. package/dist/harness/pico3/jsonl.js.map +1 -0
  56. package/dist/harness/pico3/kinds/collapse.d.ts +49 -0
  57. package/dist/harness/pico3/kinds/collapse.d.ts.map +1 -0
  58. package/dist/harness/pico3/kinds/collapse.js +191 -0
  59. package/dist/harness/pico3/kinds/collapse.js.map +1 -0
  60. package/dist/harness/pico3/kinds/entries.d.ts +78 -0
  61. package/dist/harness/pico3/kinds/entries.d.ts.map +1 -0
  62. package/dist/harness/pico3/kinds/entries.js +13 -0
  63. package/dist/harness/pico3/kinds/entries.js.map +1 -0
  64. package/dist/harness/pico3/kinds/frames.d.ts +9 -0
  65. package/dist/harness/pico3/kinds/frames.d.ts.map +1 -0
  66. package/dist/harness/pico3/kinds/frames.js +75 -0
  67. package/dist/harness/pico3/kinds/frames.js.map +1 -0
  68. package/dist/harness/pico3/kinds/generation.d.ts +94 -0
  69. package/dist/harness/pico3/kinds/generation.d.ts.map +1 -0
  70. package/dist/harness/pico3/kinds/generation.js +504 -0
  71. package/dist/harness/pico3/kinds/generation.js.map +1 -0
  72. package/dist/harness/pico3/kinds/job.d.ts +44 -0
  73. package/dist/harness/pico3/kinds/job.d.ts.map +1 -0
  74. package/dist/harness/pico3/kinds/job.js +143 -0
  75. package/dist/harness/pico3/kinds/job.js.map +1 -0
  76. package/dist/harness/pico3/kinds/plugin.d.ts +15 -0
  77. package/dist/harness/pico3/kinds/plugin.d.ts.map +1 -0
  78. package/dist/harness/pico3/kinds/plugin.js +35 -0
  79. package/dist/harness/pico3/kinds/plugin.js.map +1 -0
  80. package/dist/harness/pico3/kinds/post-tools.d.ts +22 -0
  81. package/dist/harness/pico3/kinds/post-tools.d.ts.map +1 -0
  82. package/dist/harness/pico3/kinds/post-tools.js +145 -0
  83. package/dist/harness/pico3/kinds/post-tools.js.map +1 -0
  84. package/dist/harness/pico3/kinds/task-api.d.ts +3 -0
  85. package/dist/harness/pico3/kinds/task-api.d.ts.map +1 -0
  86. package/dist/harness/pico3/kinds/task-api.js +45 -0
  87. package/dist/harness/pico3/kinds/task-api.js.map +1 -0
  88. package/dist/harness/pico3/kinds/tool.d.ts +43 -0
  89. package/dist/harness/pico3/kinds/tool.d.ts.map +1 -0
  90. package/dist/harness/pico3/kinds/tool.js +373 -0
  91. package/dist/harness/pico3/kinds/tool.js.map +1 -0
  92. package/dist/harness/pico3/membrane.d.ts +23 -0
  93. package/dist/harness/pico3/membrane.d.ts.map +1 -0
  94. package/dist/harness/pico3/membrane.js +141 -0
  95. package/dist/harness/pico3/membrane.js.map +1 -0
  96. package/dist/harness/pico3/memory.d.ts +53 -0
  97. package/dist/harness/pico3/memory.d.ts.map +1 -0
  98. package/dist/harness/pico3/memory.js +264 -0
  99. package/dist/harness/pico3/memory.js.map +1 -0
  100. package/dist/harness/pico3/scheduler.d.ts +46 -0
  101. package/dist/harness/pico3/scheduler.d.ts.map +1 -0
  102. package/dist/harness/pico3/scheduler.js +438 -0
  103. package/dist/harness/pico3/scheduler.js.map +1 -0
  104. package/dist/harness/pico3/session.d.ts +263 -0
  105. package/dist/harness/pico3/session.d.ts.map +1 -0
  106. package/dist/harness/pico3/session.js +1347 -0
  107. package/dist/harness/pico3/session.js.map +1 -0
  108. package/dist/harness/pico3/system.d.ts +126 -0
  109. package/dist/harness/pico3/system.d.ts.map +1 -0
  110. package/dist/harness/pico3/system.js +244 -0
  111. package/dist/harness/pico3/system.js.map +1 -0
  112. package/dist/harness/pico3/types.d.ts +973 -0
  113. package/dist/harness/pico3/types.d.ts.map +1 -0
  114. package/dist/harness/pico3/types.js +96 -0
  115. package/dist/harness/pico3/types.js.map +1 -0
  116. package/dist/harness/pico3/view.d.ts +34 -0
  117. package/dist/harness/pico3/view.d.ts.map +1 -0
  118. package/dist/harness/pico3/view.js +417 -0
  119. package/dist/harness/pico3/view.js.map +1 -0
  120. package/dist/harness/runtime/drive/boundary.d.ts.map +1 -1
  121. package/dist/harness/runtime/drive/boundary.js +6 -3
  122. package/dist/harness/runtime/drive/boundary.js.map +1 -1
  123. package/dist/harness/runtime/drive/response.d.ts.map +1 -1
  124. package/dist/harness/runtime/drive/response.js +4 -4
  125. package/dist/harness/runtime/drive/response.js.map +1 -1
  126. package/dist/harness/runtime/drive/retry.d.ts +2 -2
  127. package/dist/harness/runtime/drive/retry.d.ts.map +1 -1
  128. package/dist/harness/runtime/drive/retry.js +3 -6
  129. package/dist/harness/runtime/drive/retry.js.map +1 -1
  130. package/dist/harness/runtime/drive/structural.d.ts.map +1 -1
  131. package/dist/harness/runtime/drive/structural.js +5 -5
  132. package/dist/harness/runtime/drive/structural.js.map +1 -1
  133. package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -1
  134. package/dist/harness/runtime/drive/tool-placement.js +2 -27
  135. package/dist/harness/runtime/drive/tool-placement.js.map +1 -1
  136. package/dist/harness/runtime/drive/tools.d.ts.map +1 -1
  137. package/dist/harness/runtime/drive/tools.js.map +1 -1
  138. package/dist/harness/session/fork-policy.d.ts +20 -12
  139. package/dist/harness/session/fork-policy.d.ts.map +1 -1
  140. package/dist/harness/session/fork-policy.js +45 -12
  141. package/dist/harness/session/fork-policy.js.map +1 -1
  142. package/dist/harness/session/fork.d.ts +0 -2
  143. package/dist/harness/session/fork.d.ts.map +1 -1
  144. package/dist/harness/session/fork.js +20 -68
  145. package/dist/harness/session/fork.js.map +1 -1
  146. package/dist/harness/session/in-memory-storage-state.d.ts +5 -5
  147. package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -1
  148. package/dist/harness/session/in-memory-storage-state.js +99 -39
  149. package/dist/harness/session/in-memory-storage-state.js.map +1 -1
  150. package/dist/harness/session/index.d.ts +1 -1
  151. package/dist/harness/session/index.d.ts.map +1 -1
  152. package/dist/harness/session/index.js +1 -1
  153. package/dist/harness/session/index.js.map +1 -1
  154. package/dist/harness/session/jsonl/fork.d.ts +38 -0
  155. package/dist/harness/session/jsonl/fork.d.ts.map +1 -0
  156. package/dist/harness/session/jsonl/fork.js +264 -0
  157. package/dist/harness/session/jsonl/fork.js.map +1 -0
  158. package/dist/harness/session/jsonl/io.d.ts +14 -0
  159. package/dist/harness/session/jsonl/io.d.ts.map +1 -0
  160. package/dist/harness/session/jsonl/io.js +86 -0
  161. package/dist/harness/session/jsonl/io.js.map +1 -0
  162. package/dist/harness/session/jsonl/legacy-v3.d.ts +33 -8
  163. package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -1
  164. package/dist/harness/session/jsonl/legacy-v3.js +285 -206
  165. package/dist/harness/session/jsonl/legacy-v3.js.map +1 -1
  166. package/dist/harness/session/jsonl/repo.d.ts +2 -1
  167. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  168. package/dist/harness/session/jsonl/repo.js +36 -20
  169. package/dist/harness/session/jsonl/repo.js.map +1 -1
  170. package/dist/harness/session/jsonl/storage.d.ts +4 -5
  171. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  172. package/dist/harness/session/jsonl/storage.js +39 -113
  173. package/dist/harness/session/jsonl/storage.js.map +1 -1
  174. package/dist/harness/session/memory.d.ts +3 -5
  175. package/dist/harness/session/memory.d.ts.map +1 -1
  176. package/dist/harness/session/memory.js +9 -14
  177. package/dist/harness/session/memory.js.map +1 -1
  178. package/dist/harness/session/testing/conformance/session-repo.d.ts +2 -0
  179. package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -1
  180. package/dist/harness/session/testing/conformance/session-repo.js +220 -1
  181. package/dist/harness/session/testing/conformance/session-repo.js.map +1 -1
  182. package/dist/harness/session/testing/index.d.ts +1 -1
  183. package/dist/harness/session/testing/index.d.ts.map +1 -1
  184. package/dist/harness/session/testing/index.js +1 -1
  185. package/dist/harness/session/testing/index.js.map +1 -1
  186. package/dist/harness/session/types.d.ts +1 -0
  187. package/dist/harness/session/types.d.ts.map +1 -1
  188. package/dist/harness/session/types.js.map +1 -1
  189. package/dist/harness/types.d.ts +14 -0
  190. package/dist/harness/types.d.ts.map +1 -1
  191. package/dist/harness/types.js.map +1 -1
  192. package/dist/proxy.d.ts +2 -2
  193. package/dist/proxy.d.ts.map +1 -1
  194. package/dist/proxy.js.map +1 -1
  195. package/dist/types.d.ts +30 -14
  196. package/dist/types.d.ts.map +1 -1
  197. package/dist/types.js.map +1 -1
  198. package/package.json +10 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../src/harness/pico3/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,cAAc,CAAC;AACpD,OAAO,EACN,KAAK,OAAO,EAGZ,KAAK,EAAE,EACP,KAAK,KAAK,EAEV,KAAK,OAAO,EAGZ,KAAK,OAAO,EAEZ,KAAK,IAAI,EAET,MAAM,YAAY,CAAC;AASpB,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IACxF,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,OAAO,CAAC;CACb;AAmDD,qBAAa,SAAS;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyC;IACrE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0C;IACvE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsE;IAElG,YAAY,IAAI,EAAE,aAAa,EAoB9B;IAED,MAAM,SAGL;IACD,IAAI,SAEH;IACD,IAAI,IAAI,MAAM,IAAI,CASjB;IACD,IAAI,SAIH;YAEa,KAAK;YAmBL,eAAe;IAyC7B,OAAO,CAAC,QAAQ;IA2GhB,OAAO,CAAC,KAAK;YAiBC,SAAS;IAgDvB,iGAAiG;IAC3F,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC,CAqBpE;IAID,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAK/C;IACD,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAKjD;YAEa,MAAM;IA6Bd,WAAW,CAAC,cAAc,EAAE,EAAE,GAAG,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB7E;IACD,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,SAAS;IAIjB,qEAAqE;IAC/D,OAAO,kBAIZ;IACD,SAAS,IAAI,OAAO,CAEnB;IACD,IAAI,eAAe,WAElB;CACD","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport { withAbortSignal } from \"@earendil-works/chord/context\";\nimport type { Session, TxImpl } from \"./session.ts\";\nimport {\n\ttype AnyKind,\n\ttype Checkpoint,\n\ttype Completion,\n\ttype Id,\n\ttype Input,\n\tInvocationToken,\n\ttype Invoker,\n\ttype JsonValue,\n\ttype Outcome,\n\ttype Runtime,\n\ttype Step,\n\ttype Task,\n\tTaskContractFault,\n} from \"./types.ts\";\n\ninterface Invocation {\n\ttask: Task;\n\tmode: \"run\" | \"abort\";\n\tcontroller: AbortController;\n\tdone: Promise<void>;\n}\n\nexport interface SchedulerDeps {\n\tsession: Session;\n\tkinds: ReadonlyMap<string, AnyKind>;\n\truntime(task: Task, invoker: Extract<Invoker, { type: \"task\" }>, ctx: Context): Runtime;\n\tonReport(error: unknown): void;\n\tctx: Context;\n}\n\n/**\n * The erased-kind adapter: the one place a concrete `Kind<…>` is called through `AnyKind`\n * and its outputs are validated. A handler that throws or returns an invalid shape is a\n * contract fault, recorded as `{ status: \"faulted\" }` — never a manufactured declared failure.\n */\ntype Handler = (task: Task, rt: Runtime, ctx: Context) => Promise<Step<Checkpoint, JsonValue, JsonValue>>;\nfunction handlerFor(kind: AnyKind, phase: string | undefined): Handler {\n\tconst h = phase === undefined ? kind.initial : kind.phases[phase];\n\tif (typeof h !== \"function\") throw new TaskContractFault(kind.name, `no handler for phase ${phase ?? \"initial\"}`);\n\treturn h as Handler;\n}\nfunction validateStep(kind: AnyKind, step: unknown): Step<Checkpoint, JsonValue, JsonValue> {\n\tif (typeof step !== \"object\" || step === null)\n\t\tthrow new TaskContractFault(kind.name, \"handler returned a non-object step\");\n\tif (\"done\" in step && typeof (step as { done: unknown }).done === \"function\")\n\t\treturn step as Step<Checkpoint, JsonValue, JsonValue>;\n\tif (\"next\" in step) {\n\t\tconst n = (step as { next: unknown }).next;\n\t\tif (typeof n === \"function\") return step as Step<Checkpoint, JsonValue, JsonValue>;\n\t\tif (typeof n === \"object\" && n !== null && typeof (n as { phase?: unknown }).phase === \"string\") {\n\t\t\tif (!((n as { phase: string }).phase in kind.phases))\n\t\t\t\tthrow new TaskContractFault(kind.name, `transition to unknown phase ${(n as { phase: string }).phase}`);\n\t\t\treturn step as Step<Checkpoint, JsonValue, JsonValue>;\n\t\t}\n\t}\n\tthrow new TaskContractFault(kind.name, \"handler returned an invalid step\");\n}\nfunction validateCompletion(kind: AnyKind, c: unknown): Completion<JsonValue, JsonValue> {\n\tif (typeof c === \"object\" && c !== null) {\n\t\tconst s = (c as { status?: unknown }).status;\n\t\tif (s === \"completed\" && \"result\" in c) return c as Completion<JsonValue, JsonValue>;\n\t\tif (s === \"failed\" && \"failure\" in c) return c as Completion<JsonValue, JsonValue>;\n\t}\n\tthrow new TaskContractFault(kind.name, \"closure returned an invalid completion\");\n}\nfunction validateCheckpoint(kind: AnyKind, cp: unknown): Checkpoint {\n\tif (typeof cp !== \"object\" || cp === null || typeof (cp as { phase?: unknown }).phase !== \"string\")\n\t\tthrow new TaskContractFault(kind.name, \"invalid checkpoint\");\n\tconst phase = (cp as { phase: string }).phase;\n\tif (!(phase in kind.phases)) throw new TaskContractFault(kind.name, `checkpoint names unknown phase ${phase}`);\n\tif (kind.inflight?.includes(phase))\n\t\tthrow new TaskContractFault(\n\t\t\tkind.name,\n\t\t\t`transition into in-flight phase ${phase}; write it with rt.commit before the effect instead`,\n\t\t);\n\tJSON.stringify(cp);\n\treturn cp as Checkpoint;\n}\n\nexport class Scheduler {\n\tprivate readonly deps: SchedulerDeps;\n\tprivate enabled = false;\n\tprivate holds = 0;\n\tprivate dirty = false;\n\tprivate draining = false;\n\tprivate readonly invocations = new Map<Id, Invocation>();\n\tprivate readonly taskWaiters = new Map<Id, Set<(t: Task) => void>>();\n\tprivate readonly inputWaiters = new Map<Id, Set<(i: Input) => void>>();\n\tprivate readonly idleWaiters = new Set<{ conversationId: Id | undefined; resolve: () => void }>();\n\n\tconstructor(deps: SchedulerDeps) {\n\t\tthis.deps = deps;\n\t\tdeps.session.listeners.add((r) => {\n\t\t\tfor (const t of r.changes.tasks) {\n\t\t\t\tconst inv = this.invocations.get(t.id);\n\t\t\t\tif (t.abort === true && inv?.mode === \"run\") inv.controller.abort();\n\t\t\t}\n\t\t\tfor (const task of r.changes.tasks)\n\t\t\t\tif (task.status === \"terminal\")\n\t\t\t\t\tthis.taskWaiters.get(task.id)?.forEach((waiter) => {\n\t\t\t\t\t\twaiter(task);\n\t\t\t\t\t});\n\t\t\tfor (const input of r.changes.inputs)\n\t\t\t\tif (input.status === \"done\" || input.status === \"unanswered\")\n\t\t\t\t\tthis.inputWaiters.get(input.id)?.forEach((waiter) => {\n\t\t\t\t\t\twaiter(input);\n\t\t\t\t\t});\n\t\t\tif (r.changes.tasks.length > 0) this.kick();\n\t\t\tthis.checkIdle();\n\t\t});\n\t}\n\n\tresume() {\n\t\tthis.enabled = true;\n\t\tthis.kick();\n\t}\n\tstop() {\n\t\tthis.enabled = false;\n\t}\n\thold(): () => void {\n\t\tthis.holds++;\n\t\tlet released = false;\n\t\treturn () => {\n\t\t\tif (released) return;\n\t\t\treleased = true;\n\t\t\tthis.holds--;\n\t\t\tif (this.holds === 0) this.kick();\n\t\t};\n\t}\n\tkick() {\n\t\tif (!this.enabled) return;\n\t\tthis.dirty = true;\n\t\tif (this.holds === 0 && !this.draining) this.drain().catch((e) => this.deps.onReport(e));\n\t}\n\n\tprivate async drain() {\n\t\tthis.draining = true;\n\t\ttry {\n\t\t\twhile (this.dirty && this.enabled && this.holds === 0) {\n\t\t\t\tthis.dirty = false;\n\t\t\t\tconst reserved = await this.reserveEligible();\n\t\t\t\tif (!this.enabled || this.holds > 0) {\n\t\t\t\t\tthis.dirty = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfor (const { task, mode } of reserved) this.dispatch(task, mode);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.draining = false;\n\t\t\tthis.checkIdle();\n\t\t}\n\t}\n\n\t/** On the line: reserve every eligible task. */\n\tprivate async reserveEligible(): Promise<{ task: Task; mode: \"run\" | \"abort\" }[]> {\n\t\tconst { session } = this.deps;\n\t\tconst out: { task: Task; mode: \"run\" | \"abort\" }[] = [];\n\t\tawait session.commit(\n\t\t\t{ type: \"kernel\" },\n\t\t\tasync (tx) => {\n\t\t\t\tfor (const task of [...session.liveTasks.values()]) {\n\t\t\t\t\tif (!this.deps.kinds.has(task.kind)) continue;\n\t\t\t\t\tconst inv = this.invocations.get(task.id);\n\t\t\t\t\tif (task.abort === true) {\n\t\t\t\t\t\tif (inv?.mode === \"abort\") continue;\n\t\t\t\t\t\tif (inv?.mode === \"run\") continue; // winding down; dispatched after it returns\n\t\t\t\t\t\tconst running: Task = task.status === \"pending\" ? { ...task, status: \"running\" } : task;\n\t\t\t\t\t\tif (running !== task) tx.setTask(running);\n\t\t\t\t\t\tout.push({ task: running, mode: \"abort\" });\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (inv !== undefined) continue;\n\t\t\t\t\tif (task.status === \"pending\") {\n\t\t\t\t\t\tlet ready = true;\n\t\t\t\t\t\tfor (const dep of task.after) {\n\t\t\t\t\t\t\tconst d = await tx.task(dep);\n\t\t\t\t\t\t\tif (d === undefined || d.status !== \"terminal\") {\n\t\t\t\t\t\t\t\tready = false;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!ready) continue;\n\t\t\t\t\t\tconst running: Task = { ...task, status: \"running\" };\n\t\t\t\t\t\ttx.setTask(running);\n\t\t\t\t\t\tout.push({ task: running, mode: \"run\" });\n\t\t\t\t\t} else if (task.status === \"running\") {\n\t\t\t\t\t\tout.push({ task, mode: \"run\" }); // found at open: dispatch into its current phase\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.deps.ctx,\n\t\t);\n\t\treturn out;\n\t}\n\n\tprivate dispatch(task: Task, mode: \"run\" | \"abort\") {\n\t\tconst { session } = this.deps;\n\t\tconst kind = this.deps.kinds.get(task.kind);\n\t\tif (kind === undefined) {\n\t\t\tthis.deps.onReport(new Error(`unknown kind ${task.kind} for task ${task.id}`));\n\t\t\treturn;\n\t\t}\n\t\tconst controller = new AbortController();\n\t\tconst ctx = withAbortSignal(controller.signal, this.deps.ctx);\n\t\tconst done = (async () => {\n\t\t\ttry {\n\t\t\t\tif (mode === \"run\") {\n\t\t\t\t\tconst closure = await this.runPhases(task, kind, ctx);\n\t\t\t\t\tif (closure === undefined || controller.signal.aborted) return;\n\t\t\t\t\tconst lease = this.lease(task, kind, mode, ctx);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait session.commit(\n\t\t\t\t\t\t\tlease.invoker,\n\t\t\t\t\t\t\tasync (tx, lineCtx, control) => {\n\t\t\t\t\t\t\t\tconst current = session.liveTasks.get(task.id);\n\t\t\t\t\t\t\t\tif (current === undefined || current.abort === true) return;\n\t\t\t\t\t\t\t\tconst completion = validateCompletion(\n\t\t\t\t\t\t\t\t\tkind,\n\t\t\t\t\t\t\t\t\tawait (closure as (tx: TxImpl, task: Task, ctx: Context) => unknown)(tx, current, lineCtx),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tconst patched = (await tx.task(task.id)) ?? current;\n\t\t\t\t\t\t\t\tcontrol.setTask({ ...patched, status: \"terminal\", outcome: completion });\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t{ closing: true },\n\t\t\t\t\t\t);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tlease.token.revoke();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst handlerLease = this.lease(task, kind, mode, ctx);\n\t\t\t\t\tlet closure: (tx: TxImpl, task: Task, ctx: Context) => unknown;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tclosure = await (\n\t\t\t\t\t\t\tkind.abort as (\n\t\t\t\t\t\t\t\ttask: Task,\n\t\t\t\t\t\t\t\truntime: Runtime,\n\t\t\t\t\t\t\t\tctx: Context,\n\t\t\t\t\t\t\t) => Promise<(tx: TxImpl, task: Task, ctx: Context) => unknown>\n\t\t\t\t\t\t)(task, handlerLease.runtime, ctx);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\thandlerLease.token.revoke();\n\t\t\t\t\t}\n\t\t\t\t\tconst closureLease = this.lease(task, kind, mode, ctx);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait session.commit(\n\t\t\t\t\t\t\tclosureLease.invoker,\n\t\t\t\t\t\t\tasync (tx, lineCtx, control) => {\n\t\t\t\t\t\t\t\tconst current = session.liveTasks.get(task.id);\n\t\t\t\t\t\t\t\tif (current === undefined) return;\n\t\t\t\t\t\t\t\tconst result = (await closure(tx, current, lineCtx)) as JsonValue;\n\t\t\t\t\t\t\t\tJSON.stringify(result);\n\t\t\t\t\t\t\t\tconst patched = (await tx.task(task.id)) ?? current;\n\t\t\t\t\t\t\t\tcontrol.setTask({ ...patched, status: \"terminal\", outcome: { status: \"aborted\", result } });\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t{ closing: true },\n\t\t\t\t\t\t);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tclosureLease.token.revoke();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (controller.signal.aborted) return;\n\t\t\t\t// Contract fault or unexpected throw: the task ends `faulted`, never as a declared failure.\n\t\t\t\tthis.deps.onReport(error);\n\t\t\t\tawait session\n\t\t\t\t\t.commit(\n\t\t\t\t\t\t{ type: \"kernel\" },\n\t\t\t\t\t\tasync (tx) => {\n\t\t\t\t\t\t\tconst current = session.liveTasks.get(task.id);\n\t\t\t\t\t\t\tif (current === undefined) return;\n\t\t\t\t\t\t\tconst outcome: Outcome = {\n\t\t\t\t\t\t\t\tstatus: \"faulted\",\n\t\t\t\t\t\t\t\terror: error instanceof TaskContractFault ? error.message : `${kind.name}: ${String(error)}`,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\ttx.setTask({ ...current, status: \"terminal\", outcome });\n\t\t\t\t\t\t\t// A faulted turn task: its inputs would otherwise stay placed forever; the turn state is stale.\n\t\t\t\t\t\t\tif (kind.turn) {\n\t\t\t\t\t\t\t\tconst inputs = (current.input as { inputs?: unknown }).inputs;\n\t\t\t\t\t\t\t\tif (Array.isArray(inputs))\n\t\t\t\t\t\t\t\t\tawait tx.resolveInputs(\n\t\t\t\t\t\t\t\t\t\tinputs.filter((i): i is Id => typeof i === \"number\"),\n\t\t\t\t\t\t\t\t\t\t{ status: \"unanswered\", reason: \"failed\", detail: outcome.error },\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\ttx.sticky(current.conversationId).turn = { tools: [] };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\tthis.deps.ctx,\n\t\t\t\t\t\t{ docs: [{ doc: \"sticky\", conversationId: task.conversationId }] },\n\t\t\t\t\t)\n\t\t\t\t\t.catch((e) => this.deps.onReport(e));\n\t\t\t} finally {\n\t\t\t\tthis.invocations.delete(task.id);\n\t\t\t\tconst now = session.liveTasks.get(task.id);\n\t\t\t\tif (now === undefined) await session.retire(task, this.deps.ctx).catch((e) => this.deps.onReport(e));\n\t\t\t\tthis.kick();\n\t\t\t}\n\t\t})();\n\t\tthis.invocations.set(task.id, { task, mode, controller, done });\n\t}\n\n\tprivate lease(task: Task, kind: AnyKind, mode: \"run\" | \"abort\", ctx: Context) {\n\t\tconst token = new InvocationToken(task.id, mode);\n\t\tconst invoker: Extract<Invoker, { type: \"task\" }> = {\n\t\t\ttype: \"task\",\n\t\t\ttoken,\n\t\t\tid: task.id,\n\t\t\tconversationId: task.conversationId,\n\t\t\tkind,\n\t\t\tcore:\n\t\t\t\ttask.kind.startsWith(\"pi.\") &&\n\t\t\t\t[\"pi.generation\", \"pi.tool\", \"pi.post_tools\", \"pi.collapse\"].includes(task.kind),\n\t\t\tmode,\n\t\t};\n\t\treturn { token, invoker, runtime: this.deps.runtime(task, invoker, ctx) };\n\t}\n\n\t/** The phase loop. Each handler gets a capability lease revoked when that handler's promise returns. */\n\tprivate async runPhases(task: Task, kind: AnyKind, ctx: Context): Promise<unknown> {\n\t\tconst { session } = this.deps;\n\t\tlet current = task;\n\t\tfor (;;) {\n\t\t\tconst cp = current.checkpoint;\n\t\t\tconst handler = handlerFor(kind, cp?.phase);\n\t\t\tconst handlerLease = this.lease(task, kind, \"run\", ctx);\n\t\t\tlet step: Step<Checkpoint, JsonValue, JsonValue>;\n\t\t\ttry {\n\t\t\t\tstep = validateStep(kind, await handler(current, handlerLease.runtime, ctx));\n\t\t\t} finally {\n\t\t\t\thandlerLease.token.revoke();\n\t\t\t}\n\t\t\tif (\"done\" in step) return step.done;\n\t\t\tif (ctx.abortSignal?.aborted) return undefined;\n\t\t\tconst transitionLease = this.lease(task, kind, \"run\", ctx);\n\t\t\tlet outcome: { value: \"discard\" | \"retry\" | \"terminal\" | \"advanced\" };\n\t\t\ttry {\n\t\t\t\toutcome = await session.commit(\n\t\t\t\t\ttransitionLease.invoker,\n\t\t\t\t\tasync (tx, lineCtx, control) => {\n\t\t\t\t\t\tconst live = session.liveTasks.get(task.id);\n\t\t\t\t\t\tif (live === undefined || live.abort === true) return \"discard\" as const;\n\t\t\t\t\t\tconst next = typeof step.next === \"function\" ? await step.next(tx, live, lineCtx) : step.next;\n\t\t\t\t\t\tif (next === \"retry\") return \"retry\" as const;\n\t\t\t\t\t\tif (typeof next === \"object\" && next !== null && \"status\" in next) {\n\t\t\t\t\t\t\tconst patched = (await tx.task(task.id)) ?? live;\n\t\t\t\t\t\t\tcontrol.setTask({ ...patched, status: \"terminal\", outcome: validateCompletion(kind, next) });\n\t\t\t\t\t\t\treturn \"terminal\" as const;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst checkpoint = validateCheckpoint(kind, next);\n\t\t\t\t\t\ttx.checkpoint(checkpoint);\n\t\t\t\t\t\tcurrent = { ...live, checkpoint };\n\t\t\t\t\t\treturn \"advanced\" as const;\n\t\t\t\t\t},\n\t\t\t\t\tctx,\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\ttransitionLease.token.revoke();\n\t\t\t}\n\t\t\tif (outcome.value === \"retry\") {\n\t\t\t\tcurrent = session.liveTasks.get(task.id) ?? current;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (outcome.value !== \"advanced\") return undefined;\n\t\t}\n\t}\n\n\t/** Mark, revoke, signal, join. The scheduler then reserves the fresh abort on its next drain. */\n\tasync abortTask(id: Id, ctx: Context): Promise<\"marked\" | \"terminal\"> {\n\t\tconst { session } = this.deps;\n\t\tconst r = await session.commit(\n\t\t\t{ type: \"kernel\" },\n\t\t\tasync (tx) => {\n\t\t\t\tconst task = await tx.task(id);\n\t\t\t\tif (task === undefined) throw new Error(`task ${id} not found`);\n\t\t\t\tif (task.status === \"terminal\") return \"terminal\" as const;\n\t\t\t\tif (task.abort !== true) tx.setTask({ ...task, abort: true });\n\t\t\t\treturn \"marked\" as const;\n\t\t\t},\n\t\t\tctx,\n\t\t);\n\t\tif (r.value === \"terminal\") return \"terminal\";\n\t\tconst inv = this.invocations.get(id);\n\t\tif (inv?.mode === \"run\") {\n\t\t\tinv.controller.abort();\n\t\t\tawait inv.done;\n\t\t}\n\t\tthis.kick();\n\t\treturn \"marked\";\n\t}\n\n\t// --- waiters: registered on the line, atomically with the state read -------------------\n\n\twaitForTask(id: Id, ctx: Context): Promise<Task> {\n\t\treturn this.waiter(ctx, this.taskWaiters, id, (lineCtx) => {\n\t\t\tconst live = this.deps.session.liveTasks.get(id);\n\t\t\treturn live === undefined ? this.deps.session.storage.task(id, lineCtx) : Promise.resolve(undefined);\n\t\t});\n\t}\n\twaitForInput(id: Id, ctx: Context): Promise<Input> {\n\t\treturn this.waiter(ctx, this.inputWaiters, id, async (lineCtx) => {\n\t\t\tconst i = await this.deps.session.storage.input(id, lineCtx);\n\t\t\treturn i !== undefined && (i.status === \"done\" || i.status === \"unanswered\") ? i : undefined;\n\t\t});\n\t}\n\t/** Read state and install the waiter in one line operation; the wait itself happens off-line. */\n\tprivate async waiter<T>(\n\t\tctx: Context,\n\t\ttable: Map<Id, Set<(v: T) => void>>,\n\t\tid: Id,\n\t\tterminal: (ctx: Context) => Promise<T | undefined>,\n\t): Promise<T> {\n\t\tctx.abortSignal?.throwIfAborted();\n\t\tconst r = await this.deps.session.onLine(async (lineCtx): Promise<{ now: T } | { pending: Promise<T> }> => {\n\t\t\tconst now = await terminal(lineCtx);\n\t\t\tif (now !== undefined) return { now };\n\t\t\treturn {\n\t\t\t\tpending: new Promise<T>((resolve, reject) => {\n\t\t\t\t\tconst set = table.get(id) ?? table.set(id, new Set()).get(id)!;\n\t\t\t\t\tconst w = (v: T) => {\n\t\t\t\t\t\tset.delete(w);\n\t\t\t\t\t\tctx.abortSignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\tresolve(v);\n\t\t\t\t\t};\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tset.delete(w);\n\t\t\t\t\t\treject(ctx.abortSignal?.reason ?? new Error(\"aborted\"));\n\t\t\t\t\t};\n\t\t\t\t\tset.add(w);\n\t\t\t\t\tctx.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}),\n\t\t\t};\n\t\t}, ctx);\n\t\treturn \"now\" in r ? r.now : r.pending;\n\t}\n\tasync waitForIdle(conversationId: Id | undefined, ctx: Context): Promise<void> {\n\t\tctx.abortSignal?.throwIfAborted();\n\t\tconst r = await this.deps.session.onLine((): { pending?: Promise<void> } => {\n\t\t\tif (this.isIdle(conversationId)) return {};\n\t\t\treturn {\n\t\t\t\tpending: new Promise<void>((resolve, reject) => {\n\t\t\t\t\tconst w = {\n\t\t\t\t\t\tconversationId,\n\t\t\t\t\t\tresolve: () => {\n\t\t\t\t\t\t\tthis.idleWaiters.delete(w);\n\t\t\t\t\t\t\tctx.abortSignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tthis.idleWaiters.delete(w);\n\t\t\t\t\t\treject(ctx.abortSignal?.reason ?? new Error(\"aborted\"));\n\t\t\t\t\t};\n\t\t\t\t\tthis.idleWaiters.add(w);\n\t\t\t\t\tctx.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}),\n\t\t\t};\n\t\t}, ctx);\n\t\treturn r.pending;\n\t}\n\tprivate isIdle(conversationId: Id | undefined): boolean {\n\t\tfor (const t of this.deps.session.liveTasks.values())\n\t\t\tif ((conversationId === undefined || t.conversationId === conversationId) && !t.background) return false;\n\t\treturn true;\n\t}\n\tprivate checkIdle() {\n\t\tfor (const w of [...this.idleWaiters]) if (this.isIdle(w.conversationId) && !this.draining) w.resolve();\n\t}\n\n\t/** Signal every invocation and wait for them; nothing is written. */\n\tasync joinAll() {\n\t\tthis.enabled = false;\n\t\tfor (const inv of this.invocations.values()) inv.controller.abort();\n\t\tawait Promise.allSettled([...this.invocations.values()].map((invocation) => invocation.done));\n\t}\n\tquiescent(): boolean {\n\t\treturn this.invocations.size === 0;\n\t}\n\tget liveInvocations() {\n\t\treturn this.invocations.size;\n\t}\n}\n"]}
@@ -0,0 +1,438 @@
1
+ import { withAbortSignal } from "@earendil-works/chord/context";
2
+ import { InvocationToken, TaskContractFault, } from "./types.js";
3
+ function handlerFor(kind, phase) {
4
+ const h = phase === undefined ? kind.initial : kind.phases[phase];
5
+ if (typeof h !== "function")
6
+ throw new TaskContractFault(kind.name, `no handler for phase ${phase ?? "initial"}`);
7
+ return h;
8
+ }
9
+ function validateStep(kind, step) {
10
+ if (typeof step !== "object" || step === null)
11
+ throw new TaskContractFault(kind.name, "handler returned a non-object step");
12
+ if ("done" in step && typeof step.done === "function")
13
+ return step;
14
+ if ("next" in step) {
15
+ const n = step.next;
16
+ if (typeof n === "function")
17
+ return step;
18
+ if (typeof n === "object" && n !== null && typeof n.phase === "string") {
19
+ if (!(n.phase in kind.phases))
20
+ throw new TaskContractFault(kind.name, `transition to unknown phase ${n.phase}`);
21
+ return step;
22
+ }
23
+ }
24
+ throw new TaskContractFault(kind.name, "handler returned an invalid step");
25
+ }
26
+ function validateCompletion(kind, c) {
27
+ if (typeof c === "object" && c !== null) {
28
+ const s = c.status;
29
+ if (s === "completed" && "result" in c)
30
+ return c;
31
+ if (s === "failed" && "failure" in c)
32
+ return c;
33
+ }
34
+ throw new TaskContractFault(kind.name, "closure returned an invalid completion");
35
+ }
36
+ function validateCheckpoint(kind, cp) {
37
+ if (typeof cp !== "object" || cp === null || typeof cp.phase !== "string")
38
+ throw new TaskContractFault(kind.name, "invalid checkpoint");
39
+ const phase = cp.phase;
40
+ if (!(phase in kind.phases))
41
+ throw new TaskContractFault(kind.name, `checkpoint names unknown phase ${phase}`);
42
+ if (kind.inflight?.includes(phase))
43
+ throw new TaskContractFault(kind.name, `transition into in-flight phase ${phase}; write it with rt.commit before the effect instead`);
44
+ JSON.stringify(cp);
45
+ return cp;
46
+ }
47
+ export class Scheduler {
48
+ deps;
49
+ enabled = false;
50
+ holds = 0;
51
+ dirty = false;
52
+ draining = false;
53
+ invocations = new Map();
54
+ taskWaiters = new Map();
55
+ inputWaiters = new Map();
56
+ idleWaiters = new Set();
57
+ constructor(deps) {
58
+ this.deps = deps;
59
+ deps.session.listeners.add((r) => {
60
+ for (const t of r.changes.tasks) {
61
+ const inv = this.invocations.get(t.id);
62
+ if (t.abort === true && inv?.mode === "run")
63
+ inv.controller.abort();
64
+ }
65
+ for (const task of r.changes.tasks)
66
+ if (task.status === "terminal")
67
+ this.taskWaiters.get(task.id)?.forEach((waiter) => {
68
+ waiter(task);
69
+ });
70
+ for (const input of r.changes.inputs)
71
+ if (input.status === "done" || input.status === "unanswered")
72
+ this.inputWaiters.get(input.id)?.forEach((waiter) => {
73
+ waiter(input);
74
+ });
75
+ if (r.changes.tasks.length > 0)
76
+ this.kick();
77
+ this.checkIdle();
78
+ });
79
+ }
80
+ resume() {
81
+ this.enabled = true;
82
+ this.kick();
83
+ }
84
+ stop() {
85
+ this.enabled = false;
86
+ }
87
+ hold() {
88
+ this.holds++;
89
+ let released = false;
90
+ return () => {
91
+ if (released)
92
+ return;
93
+ released = true;
94
+ this.holds--;
95
+ if (this.holds === 0)
96
+ this.kick();
97
+ };
98
+ }
99
+ kick() {
100
+ if (!this.enabled)
101
+ return;
102
+ this.dirty = true;
103
+ if (this.holds === 0 && !this.draining)
104
+ this.drain().catch((e) => this.deps.onReport(e));
105
+ }
106
+ async drain() {
107
+ this.draining = true;
108
+ try {
109
+ while (this.dirty && this.enabled && this.holds === 0) {
110
+ this.dirty = false;
111
+ const reserved = await this.reserveEligible();
112
+ if (!this.enabled || this.holds > 0) {
113
+ this.dirty = true;
114
+ break;
115
+ }
116
+ for (const { task, mode } of reserved)
117
+ this.dispatch(task, mode);
118
+ }
119
+ }
120
+ finally {
121
+ this.draining = false;
122
+ this.checkIdle();
123
+ }
124
+ }
125
+ /** On the line: reserve every eligible task. */
126
+ async reserveEligible() {
127
+ const { session } = this.deps;
128
+ const out = [];
129
+ await session.commit({ type: "kernel" }, async (tx) => {
130
+ for (const task of [...session.liveTasks.values()]) {
131
+ if (!this.deps.kinds.has(task.kind))
132
+ continue;
133
+ const inv = this.invocations.get(task.id);
134
+ if (task.abort === true) {
135
+ if (inv?.mode === "abort")
136
+ continue;
137
+ if (inv?.mode === "run")
138
+ continue; // winding down; dispatched after it returns
139
+ const running = task.status === "pending" ? { ...task, status: "running" } : task;
140
+ if (running !== task)
141
+ tx.setTask(running);
142
+ out.push({ task: running, mode: "abort" });
143
+ continue;
144
+ }
145
+ if (inv !== undefined)
146
+ continue;
147
+ if (task.status === "pending") {
148
+ let ready = true;
149
+ for (const dep of task.after) {
150
+ const d = await tx.task(dep);
151
+ if (d === undefined || d.status !== "terminal") {
152
+ ready = false;
153
+ break;
154
+ }
155
+ }
156
+ if (!ready)
157
+ continue;
158
+ const running = { ...task, status: "running" };
159
+ tx.setTask(running);
160
+ out.push({ task: running, mode: "run" });
161
+ }
162
+ else if (task.status === "running") {
163
+ out.push({ task, mode: "run" }); // found at open: dispatch into its current phase
164
+ }
165
+ }
166
+ }, this.deps.ctx);
167
+ return out;
168
+ }
169
+ dispatch(task, mode) {
170
+ const { session } = this.deps;
171
+ const kind = this.deps.kinds.get(task.kind);
172
+ if (kind === undefined) {
173
+ this.deps.onReport(new Error(`unknown kind ${task.kind} for task ${task.id}`));
174
+ return;
175
+ }
176
+ const controller = new AbortController();
177
+ const ctx = withAbortSignal(controller.signal, this.deps.ctx);
178
+ const done = (async () => {
179
+ try {
180
+ if (mode === "run") {
181
+ const closure = await this.runPhases(task, kind, ctx);
182
+ if (closure === undefined || controller.signal.aborted)
183
+ return;
184
+ const lease = this.lease(task, kind, mode, ctx);
185
+ try {
186
+ await session.commit(lease.invoker, async (tx, lineCtx, control) => {
187
+ const current = session.liveTasks.get(task.id);
188
+ if (current === undefined || current.abort === true)
189
+ return;
190
+ const completion = validateCompletion(kind, await closure(tx, current, lineCtx));
191
+ const patched = (await tx.task(task.id)) ?? current;
192
+ control.setTask({ ...patched, status: "terminal", outcome: completion });
193
+ }, ctx, { closing: true });
194
+ }
195
+ finally {
196
+ lease.token.revoke();
197
+ }
198
+ }
199
+ else {
200
+ const handlerLease = this.lease(task, kind, mode, ctx);
201
+ let closure;
202
+ try {
203
+ closure = await kind.abort(task, handlerLease.runtime, ctx);
204
+ }
205
+ finally {
206
+ handlerLease.token.revoke();
207
+ }
208
+ const closureLease = this.lease(task, kind, mode, ctx);
209
+ try {
210
+ await session.commit(closureLease.invoker, async (tx, lineCtx, control) => {
211
+ const current = session.liveTasks.get(task.id);
212
+ if (current === undefined)
213
+ return;
214
+ const result = (await closure(tx, current, lineCtx));
215
+ JSON.stringify(result);
216
+ const patched = (await tx.task(task.id)) ?? current;
217
+ control.setTask({ ...patched, status: "terminal", outcome: { status: "aborted", result } });
218
+ }, ctx, { closing: true });
219
+ }
220
+ finally {
221
+ closureLease.token.revoke();
222
+ }
223
+ }
224
+ }
225
+ catch (error) {
226
+ if (controller.signal.aborted)
227
+ return;
228
+ // Contract fault or unexpected throw: the task ends `faulted`, never as a declared failure.
229
+ this.deps.onReport(error);
230
+ await session
231
+ .commit({ type: "kernel" }, async (tx) => {
232
+ const current = session.liveTasks.get(task.id);
233
+ if (current === undefined)
234
+ return;
235
+ const outcome = {
236
+ status: "faulted",
237
+ error: error instanceof TaskContractFault ? error.message : `${kind.name}: ${String(error)}`,
238
+ };
239
+ tx.setTask({ ...current, status: "terminal", outcome });
240
+ // A faulted turn task: its inputs would otherwise stay placed forever; the turn state is stale.
241
+ if (kind.turn) {
242
+ const inputs = current.input.inputs;
243
+ if (Array.isArray(inputs))
244
+ await tx.resolveInputs(inputs.filter((i) => typeof i === "number"), { status: "unanswered", reason: "failed", detail: outcome.error });
245
+ tx.sticky(current.conversationId).turn = { tools: [] };
246
+ }
247
+ }, this.deps.ctx, { docs: [{ doc: "sticky", conversationId: task.conversationId }] })
248
+ .catch((e) => this.deps.onReport(e));
249
+ }
250
+ finally {
251
+ this.invocations.delete(task.id);
252
+ const now = session.liveTasks.get(task.id);
253
+ if (now === undefined)
254
+ await session.retire(task, this.deps.ctx).catch((e) => this.deps.onReport(e));
255
+ this.kick();
256
+ }
257
+ })();
258
+ this.invocations.set(task.id, { task, mode, controller, done });
259
+ }
260
+ lease(task, kind, mode, ctx) {
261
+ const token = new InvocationToken(task.id, mode);
262
+ const invoker = {
263
+ type: "task",
264
+ token,
265
+ id: task.id,
266
+ conversationId: task.conversationId,
267
+ kind,
268
+ core: task.kind.startsWith("pi.") &&
269
+ ["pi.generation", "pi.tool", "pi.post_tools", "pi.collapse"].includes(task.kind),
270
+ mode,
271
+ };
272
+ return { token, invoker, runtime: this.deps.runtime(task, invoker, ctx) };
273
+ }
274
+ /** The phase loop. Each handler gets a capability lease revoked when that handler's promise returns. */
275
+ async runPhases(task, kind, ctx) {
276
+ const { session } = this.deps;
277
+ let current = task;
278
+ for (;;) {
279
+ const cp = current.checkpoint;
280
+ const handler = handlerFor(kind, cp?.phase);
281
+ const handlerLease = this.lease(task, kind, "run", ctx);
282
+ let step;
283
+ try {
284
+ step = validateStep(kind, await handler(current, handlerLease.runtime, ctx));
285
+ }
286
+ finally {
287
+ handlerLease.token.revoke();
288
+ }
289
+ if ("done" in step)
290
+ return step.done;
291
+ if (ctx.abortSignal?.aborted)
292
+ return undefined;
293
+ const transitionLease = this.lease(task, kind, "run", ctx);
294
+ let outcome;
295
+ try {
296
+ outcome = await session.commit(transitionLease.invoker, async (tx, lineCtx, control) => {
297
+ const live = session.liveTasks.get(task.id);
298
+ if (live === undefined || live.abort === true)
299
+ return "discard";
300
+ const next = typeof step.next === "function" ? await step.next(tx, live, lineCtx) : step.next;
301
+ if (next === "retry")
302
+ return "retry";
303
+ if (typeof next === "object" && next !== null && "status" in next) {
304
+ const patched = (await tx.task(task.id)) ?? live;
305
+ control.setTask({ ...patched, status: "terminal", outcome: validateCompletion(kind, next) });
306
+ return "terminal";
307
+ }
308
+ const checkpoint = validateCheckpoint(kind, next);
309
+ tx.checkpoint(checkpoint);
310
+ current = { ...live, checkpoint };
311
+ return "advanced";
312
+ }, ctx);
313
+ }
314
+ finally {
315
+ transitionLease.token.revoke();
316
+ }
317
+ if (outcome.value === "retry") {
318
+ current = session.liveTasks.get(task.id) ?? current;
319
+ continue;
320
+ }
321
+ if (outcome.value !== "advanced")
322
+ return undefined;
323
+ }
324
+ }
325
+ /** Mark, revoke, signal, join. The scheduler then reserves the fresh abort on its next drain. */
326
+ async abortTask(id, ctx) {
327
+ const { session } = this.deps;
328
+ const r = await session.commit({ type: "kernel" }, async (tx) => {
329
+ const task = await tx.task(id);
330
+ if (task === undefined)
331
+ throw new Error(`task ${id} not found`);
332
+ if (task.status === "terminal")
333
+ return "terminal";
334
+ if (task.abort !== true)
335
+ tx.setTask({ ...task, abort: true });
336
+ return "marked";
337
+ }, ctx);
338
+ if (r.value === "terminal")
339
+ return "terminal";
340
+ const inv = this.invocations.get(id);
341
+ if (inv?.mode === "run") {
342
+ inv.controller.abort();
343
+ await inv.done;
344
+ }
345
+ this.kick();
346
+ return "marked";
347
+ }
348
+ // --- waiters: registered on the line, atomically with the state read -------------------
349
+ waitForTask(id, ctx) {
350
+ return this.waiter(ctx, this.taskWaiters, id, (lineCtx) => {
351
+ const live = this.deps.session.liveTasks.get(id);
352
+ return live === undefined ? this.deps.session.storage.task(id, lineCtx) : Promise.resolve(undefined);
353
+ });
354
+ }
355
+ waitForInput(id, ctx) {
356
+ return this.waiter(ctx, this.inputWaiters, id, async (lineCtx) => {
357
+ const i = await this.deps.session.storage.input(id, lineCtx);
358
+ return i !== undefined && (i.status === "done" || i.status === "unanswered") ? i : undefined;
359
+ });
360
+ }
361
+ /** Read state and install the waiter in one line operation; the wait itself happens off-line. */
362
+ async waiter(ctx, table, id, terminal) {
363
+ ctx.abortSignal?.throwIfAborted();
364
+ const r = await this.deps.session.onLine(async (lineCtx) => {
365
+ const now = await terminal(lineCtx);
366
+ if (now !== undefined)
367
+ return { now };
368
+ return {
369
+ pending: new Promise((resolve, reject) => {
370
+ const set = table.get(id) ?? table.set(id, new Set()).get(id);
371
+ const w = (v) => {
372
+ set.delete(w);
373
+ ctx.abortSignal?.removeEventListener("abort", onAbort);
374
+ resolve(v);
375
+ };
376
+ const onAbort = () => {
377
+ set.delete(w);
378
+ reject(ctx.abortSignal?.reason ?? new Error("aborted"));
379
+ };
380
+ set.add(w);
381
+ ctx.abortSignal?.addEventListener("abort", onAbort, { once: true });
382
+ }),
383
+ };
384
+ }, ctx);
385
+ return "now" in r ? r.now : r.pending;
386
+ }
387
+ async waitForIdle(conversationId, ctx) {
388
+ ctx.abortSignal?.throwIfAborted();
389
+ const r = await this.deps.session.onLine(() => {
390
+ if (this.isIdle(conversationId))
391
+ return {};
392
+ return {
393
+ pending: new Promise((resolve, reject) => {
394
+ const w = {
395
+ conversationId,
396
+ resolve: () => {
397
+ this.idleWaiters.delete(w);
398
+ ctx.abortSignal?.removeEventListener("abort", onAbort);
399
+ resolve();
400
+ },
401
+ };
402
+ const onAbort = () => {
403
+ this.idleWaiters.delete(w);
404
+ reject(ctx.abortSignal?.reason ?? new Error("aborted"));
405
+ };
406
+ this.idleWaiters.add(w);
407
+ ctx.abortSignal?.addEventListener("abort", onAbort, { once: true });
408
+ }),
409
+ };
410
+ }, ctx);
411
+ return r.pending;
412
+ }
413
+ isIdle(conversationId) {
414
+ for (const t of this.deps.session.liveTasks.values())
415
+ if ((conversationId === undefined || t.conversationId === conversationId) && !t.background)
416
+ return false;
417
+ return true;
418
+ }
419
+ checkIdle() {
420
+ for (const w of [...this.idleWaiters])
421
+ if (this.isIdle(w.conversationId) && !this.draining)
422
+ w.resolve();
423
+ }
424
+ /** Signal every invocation and wait for them; nothing is written. */
425
+ async joinAll() {
426
+ this.enabled = false;
427
+ for (const inv of this.invocations.values())
428
+ inv.controller.abort();
429
+ await Promise.allSettled([...this.invocations.values()].map((invocation) => invocation.done));
430
+ }
431
+ quiescent() {
432
+ return this.invocations.size === 0;
433
+ }
434
+ get liveInvocations() {
435
+ return this.invocations.size;
436
+ }
437
+ }
438
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../../src/harness/pico3/scheduler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport { withAbortSignal } from \"@earendil-works/chord/context\";\nimport type { Session, TxImpl } from \"./session.ts\";\nimport {\n\ttype AnyKind,\n\ttype Checkpoint,\n\ttype Completion,\n\ttype Id,\n\ttype Input,\n\tInvocationToken,\n\ttype Invoker,\n\ttype JsonValue,\n\ttype Outcome,\n\ttype Runtime,\n\ttype Step,\n\ttype Task,\n\tTaskContractFault,\n} from \"./types.ts\";\n\ninterface Invocation {\n\ttask: Task;\n\tmode: \"run\" | \"abort\";\n\tcontroller: AbortController;\n\tdone: Promise<void>;\n}\n\nexport interface SchedulerDeps {\n\tsession: Session;\n\tkinds: ReadonlyMap<string, AnyKind>;\n\truntime(task: Task, invoker: Extract<Invoker, { type: \"task\" }>, ctx: Context): Runtime;\n\tonReport(error: unknown): void;\n\tctx: Context;\n}\n\n/**\n * The erased-kind adapter: the one place a concrete `Kind<…>` is called through `AnyKind`\n * and its outputs are validated. A handler that throws or returns an invalid shape is a\n * contract fault, recorded as `{ status: \"faulted\" }` — never a manufactured declared failure.\n */\ntype Handler = (task: Task, rt: Runtime, ctx: Context) => Promise<Step<Checkpoint, JsonValue, JsonValue>>;\nfunction handlerFor(kind: AnyKind, phase: string | undefined): Handler {\n\tconst h = phase === undefined ? kind.initial : kind.phases[phase];\n\tif (typeof h !== \"function\") throw new TaskContractFault(kind.name, `no handler for phase ${phase ?? \"initial\"}`);\n\treturn h as Handler;\n}\nfunction validateStep(kind: AnyKind, step: unknown): Step<Checkpoint, JsonValue, JsonValue> {\n\tif (typeof step !== \"object\" || step === null)\n\t\tthrow new TaskContractFault(kind.name, \"handler returned a non-object step\");\n\tif (\"done\" in step && typeof (step as { done: unknown }).done === \"function\")\n\t\treturn step as Step<Checkpoint, JsonValue, JsonValue>;\n\tif (\"next\" in step) {\n\t\tconst n = (step as { next: unknown }).next;\n\t\tif (typeof n === \"function\") return step as Step<Checkpoint, JsonValue, JsonValue>;\n\t\tif (typeof n === \"object\" && n !== null && typeof (n as { phase?: unknown }).phase === \"string\") {\n\t\t\tif (!((n as { phase: string }).phase in kind.phases))\n\t\t\t\tthrow new TaskContractFault(kind.name, `transition to unknown phase ${(n as { phase: string }).phase}`);\n\t\t\treturn step as Step<Checkpoint, JsonValue, JsonValue>;\n\t\t}\n\t}\n\tthrow new TaskContractFault(kind.name, \"handler returned an invalid step\");\n}\nfunction validateCompletion(kind: AnyKind, c: unknown): Completion<JsonValue, JsonValue> {\n\tif (typeof c === \"object\" && c !== null) {\n\t\tconst s = (c as { status?: unknown }).status;\n\t\tif (s === \"completed\" && \"result\" in c) return c as Completion<JsonValue, JsonValue>;\n\t\tif (s === \"failed\" && \"failure\" in c) return c as Completion<JsonValue, JsonValue>;\n\t}\n\tthrow new TaskContractFault(kind.name, \"closure returned an invalid completion\");\n}\nfunction validateCheckpoint(kind: AnyKind, cp: unknown): Checkpoint {\n\tif (typeof cp !== \"object\" || cp === null || typeof (cp as { phase?: unknown }).phase !== \"string\")\n\t\tthrow new TaskContractFault(kind.name, \"invalid checkpoint\");\n\tconst phase = (cp as { phase: string }).phase;\n\tif (!(phase in kind.phases)) throw new TaskContractFault(kind.name, `checkpoint names unknown phase ${phase}`);\n\tif (kind.inflight?.includes(phase))\n\t\tthrow new TaskContractFault(\n\t\t\tkind.name,\n\t\t\t`transition into in-flight phase ${phase}; write it with rt.commit before the effect instead`,\n\t\t);\n\tJSON.stringify(cp);\n\treturn cp as Checkpoint;\n}\n\nexport class Scheduler {\n\tprivate readonly deps: SchedulerDeps;\n\tprivate enabled = false;\n\tprivate holds = 0;\n\tprivate dirty = false;\n\tprivate draining = false;\n\tprivate readonly invocations = new Map<Id, Invocation>();\n\tprivate readonly taskWaiters = new Map<Id, Set<(t: Task) => void>>();\n\tprivate readonly inputWaiters = new Map<Id, Set<(i: Input) => void>>();\n\tprivate readonly idleWaiters = new Set<{ conversationId: Id | undefined; resolve: () => void }>();\n\n\tconstructor(deps: SchedulerDeps) {\n\t\tthis.deps = deps;\n\t\tdeps.session.listeners.add((r) => {\n\t\t\tfor (const t of r.changes.tasks) {\n\t\t\t\tconst inv = this.invocations.get(t.id);\n\t\t\t\tif (t.abort === true && inv?.mode === \"run\") inv.controller.abort();\n\t\t\t}\n\t\t\tfor (const task of r.changes.tasks)\n\t\t\t\tif (task.status === \"terminal\")\n\t\t\t\t\tthis.taskWaiters.get(task.id)?.forEach((waiter) => {\n\t\t\t\t\t\twaiter(task);\n\t\t\t\t\t});\n\t\t\tfor (const input of r.changes.inputs)\n\t\t\t\tif (input.status === \"done\" || input.status === \"unanswered\")\n\t\t\t\t\tthis.inputWaiters.get(input.id)?.forEach((waiter) => {\n\t\t\t\t\t\twaiter(input);\n\t\t\t\t\t});\n\t\t\tif (r.changes.tasks.length > 0) this.kick();\n\t\t\tthis.checkIdle();\n\t\t});\n\t}\n\n\tresume() {\n\t\tthis.enabled = true;\n\t\tthis.kick();\n\t}\n\tstop() {\n\t\tthis.enabled = false;\n\t}\n\thold(): () => void {\n\t\tthis.holds++;\n\t\tlet released = false;\n\t\treturn () => {\n\t\t\tif (released) return;\n\t\t\treleased = true;\n\t\t\tthis.holds--;\n\t\t\tif (this.holds === 0) this.kick();\n\t\t};\n\t}\n\tkick() {\n\t\tif (!this.enabled) return;\n\t\tthis.dirty = true;\n\t\tif (this.holds === 0 && !this.draining) this.drain().catch((e) => this.deps.onReport(e));\n\t}\n\n\tprivate async drain() {\n\t\tthis.draining = true;\n\t\ttry {\n\t\t\twhile (this.dirty && this.enabled && this.holds === 0) {\n\t\t\t\tthis.dirty = false;\n\t\t\t\tconst reserved = await this.reserveEligible();\n\t\t\t\tif (!this.enabled || this.holds > 0) {\n\t\t\t\t\tthis.dirty = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfor (const { task, mode } of reserved) this.dispatch(task, mode);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.draining = false;\n\t\t\tthis.checkIdle();\n\t\t}\n\t}\n\n\t/** On the line: reserve every eligible task. */\n\tprivate async reserveEligible(): Promise<{ task: Task; mode: \"run\" | \"abort\" }[]> {\n\t\tconst { session } = this.deps;\n\t\tconst out: { task: Task; mode: \"run\" | \"abort\" }[] = [];\n\t\tawait session.commit(\n\t\t\t{ type: \"kernel\" },\n\t\t\tasync (tx) => {\n\t\t\t\tfor (const task of [...session.liveTasks.values()]) {\n\t\t\t\t\tif (!this.deps.kinds.has(task.kind)) continue;\n\t\t\t\t\tconst inv = this.invocations.get(task.id);\n\t\t\t\t\tif (task.abort === true) {\n\t\t\t\t\t\tif (inv?.mode === \"abort\") continue;\n\t\t\t\t\t\tif (inv?.mode === \"run\") continue; // winding down; dispatched after it returns\n\t\t\t\t\t\tconst running: Task = task.status === \"pending\" ? { ...task, status: \"running\" } : task;\n\t\t\t\t\t\tif (running !== task) tx.setTask(running);\n\t\t\t\t\t\tout.push({ task: running, mode: \"abort\" });\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (inv !== undefined) continue;\n\t\t\t\t\tif (task.status === \"pending\") {\n\t\t\t\t\t\tlet ready = true;\n\t\t\t\t\t\tfor (const dep of task.after) {\n\t\t\t\t\t\t\tconst d = await tx.task(dep);\n\t\t\t\t\t\t\tif (d === undefined || d.status !== \"terminal\") {\n\t\t\t\t\t\t\t\tready = false;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!ready) continue;\n\t\t\t\t\t\tconst running: Task = { ...task, status: \"running\" };\n\t\t\t\t\t\ttx.setTask(running);\n\t\t\t\t\t\tout.push({ task: running, mode: \"run\" });\n\t\t\t\t\t} else if (task.status === \"running\") {\n\t\t\t\t\t\tout.push({ task, mode: \"run\" }); // found at open: dispatch into its current phase\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.deps.ctx,\n\t\t);\n\t\treturn out;\n\t}\n\n\tprivate dispatch(task: Task, mode: \"run\" | \"abort\") {\n\t\tconst { session } = this.deps;\n\t\tconst kind = this.deps.kinds.get(task.kind);\n\t\tif (kind === undefined) {\n\t\t\tthis.deps.onReport(new Error(`unknown kind ${task.kind} for task ${task.id}`));\n\t\t\treturn;\n\t\t}\n\t\tconst controller = new AbortController();\n\t\tconst ctx = withAbortSignal(controller.signal, this.deps.ctx);\n\t\tconst done = (async () => {\n\t\t\ttry {\n\t\t\t\tif (mode === \"run\") {\n\t\t\t\t\tconst closure = await this.runPhases(task, kind, ctx);\n\t\t\t\t\tif (closure === undefined || controller.signal.aborted) return;\n\t\t\t\t\tconst lease = this.lease(task, kind, mode, ctx);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait session.commit(\n\t\t\t\t\t\t\tlease.invoker,\n\t\t\t\t\t\t\tasync (tx, lineCtx, control) => {\n\t\t\t\t\t\t\t\tconst current = session.liveTasks.get(task.id);\n\t\t\t\t\t\t\t\tif (current === undefined || current.abort === true) return;\n\t\t\t\t\t\t\t\tconst completion = validateCompletion(\n\t\t\t\t\t\t\t\t\tkind,\n\t\t\t\t\t\t\t\t\tawait (closure as (tx: TxImpl, task: Task, ctx: Context) => unknown)(tx, current, lineCtx),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tconst patched = (await tx.task(task.id)) ?? current;\n\t\t\t\t\t\t\t\tcontrol.setTask({ ...patched, status: \"terminal\", outcome: completion });\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t{ closing: true },\n\t\t\t\t\t\t);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tlease.token.revoke();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst handlerLease = this.lease(task, kind, mode, ctx);\n\t\t\t\t\tlet closure: (tx: TxImpl, task: Task, ctx: Context) => unknown;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tclosure = await (\n\t\t\t\t\t\t\tkind.abort as (\n\t\t\t\t\t\t\t\ttask: Task,\n\t\t\t\t\t\t\t\truntime: Runtime,\n\t\t\t\t\t\t\t\tctx: Context,\n\t\t\t\t\t\t\t) => Promise<(tx: TxImpl, task: Task, ctx: Context) => unknown>\n\t\t\t\t\t\t)(task, handlerLease.runtime, ctx);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\thandlerLease.token.revoke();\n\t\t\t\t\t}\n\t\t\t\t\tconst closureLease = this.lease(task, kind, mode, ctx);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait session.commit(\n\t\t\t\t\t\t\tclosureLease.invoker,\n\t\t\t\t\t\t\tasync (tx, lineCtx, control) => {\n\t\t\t\t\t\t\t\tconst current = session.liveTasks.get(task.id);\n\t\t\t\t\t\t\t\tif (current === undefined) return;\n\t\t\t\t\t\t\t\tconst result = (await closure(tx, current, lineCtx)) as JsonValue;\n\t\t\t\t\t\t\t\tJSON.stringify(result);\n\t\t\t\t\t\t\t\tconst patched = (await tx.task(task.id)) ?? current;\n\t\t\t\t\t\t\t\tcontrol.setTask({ ...patched, status: \"terminal\", outcome: { status: \"aborted\", result } });\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t{ closing: true },\n\t\t\t\t\t\t);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tclosureLease.token.revoke();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (controller.signal.aborted) return;\n\t\t\t\t// Contract fault or unexpected throw: the task ends `faulted`, never as a declared failure.\n\t\t\t\tthis.deps.onReport(error);\n\t\t\t\tawait session\n\t\t\t\t\t.commit(\n\t\t\t\t\t\t{ type: \"kernel\" },\n\t\t\t\t\t\tasync (tx) => {\n\t\t\t\t\t\t\tconst current = session.liveTasks.get(task.id);\n\t\t\t\t\t\t\tif (current === undefined) return;\n\t\t\t\t\t\t\tconst outcome: Outcome = {\n\t\t\t\t\t\t\t\tstatus: \"faulted\",\n\t\t\t\t\t\t\t\terror: error instanceof TaskContractFault ? error.message : `${kind.name}: ${String(error)}`,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\ttx.setTask({ ...current, status: \"terminal\", outcome });\n\t\t\t\t\t\t\t// A faulted turn task: its inputs would otherwise stay placed forever; the turn state is stale.\n\t\t\t\t\t\t\tif (kind.turn) {\n\t\t\t\t\t\t\t\tconst inputs = (current.input as { inputs?: unknown }).inputs;\n\t\t\t\t\t\t\t\tif (Array.isArray(inputs))\n\t\t\t\t\t\t\t\t\tawait tx.resolveInputs(\n\t\t\t\t\t\t\t\t\t\tinputs.filter((i): i is Id => typeof i === \"number\"),\n\t\t\t\t\t\t\t\t\t\t{ status: \"unanswered\", reason: \"failed\", detail: outcome.error },\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\ttx.sticky(current.conversationId).turn = { tools: [] };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\tthis.deps.ctx,\n\t\t\t\t\t\t{ docs: [{ doc: \"sticky\", conversationId: task.conversationId }] },\n\t\t\t\t\t)\n\t\t\t\t\t.catch((e) => this.deps.onReport(e));\n\t\t\t} finally {\n\t\t\t\tthis.invocations.delete(task.id);\n\t\t\t\tconst now = session.liveTasks.get(task.id);\n\t\t\t\tif (now === undefined) await session.retire(task, this.deps.ctx).catch((e) => this.deps.onReport(e));\n\t\t\t\tthis.kick();\n\t\t\t}\n\t\t})();\n\t\tthis.invocations.set(task.id, { task, mode, controller, done });\n\t}\n\n\tprivate lease(task: Task, kind: AnyKind, mode: \"run\" | \"abort\", ctx: Context) {\n\t\tconst token = new InvocationToken(task.id, mode);\n\t\tconst invoker: Extract<Invoker, { type: \"task\" }> = {\n\t\t\ttype: \"task\",\n\t\t\ttoken,\n\t\t\tid: task.id,\n\t\t\tconversationId: task.conversationId,\n\t\t\tkind,\n\t\t\tcore:\n\t\t\t\ttask.kind.startsWith(\"pi.\") &&\n\t\t\t\t[\"pi.generation\", \"pi.tool\", \"pi.post_tools\", \"pi.collapse\"].includes(task.kind),\n\t\t\tmode,\n\t\t};\n\t\treturn { token, invoker, runtime: this.deps.runtime(task, invoker, ctx) };\n\t}\n\n\t/** The phase loop. Each handler gets a capability lease revoked when that handler's promise returns. */\n\tprivate async runPhases(task: Task, kind: AnyKind, ctx: Context): Promise<unknown> {\n\t\tconst { session } = this.deps;\n\t\tlet current = task;\n\t\tfor (;;) {\n\t\t\tconst cp = current.checkpoint;\n\t\t\tconst handler = handlerFor(kind, cp?.phase);\n\t\t\tconst handlerLease = this.lease(task, kind, \"run\", ctx);\n\t\t\tlet step: Step<Checkpoint, JsonValue, JsonValue>;\n\t\t\ttry {\n\t\t\t\tstep = validateStep(kind, await handler(current, handlerLease.runtime, ctx));\n\t\t\t} finally {\n\t\t\t\thandlerLease.token.revoke();\n\t\t\t}\n\t\t\tif (\"done\" in step) return step.done;\n\t\t\tif (ctx.abortSignal?.aborted) return undefined;\n\t\t\tconst transitionLease = this.lease(task, kind, \"run\", ctx);\n\t\t\tlet outcome: { value: \"discard\" | \"retry\" | \"terminal\" | \"advanced\" };\n\t\t\ttry {\n\t\t\t\toutcome = await session.commit(\n\t\t\t\t\ttransitionLease.invoker,\n\t\t\t\t\tasync (tx, lineCtx, control) => {\n\t\t\t\t\t\tconst live = session.liveTasks.get(task.id);\n\t\t\t\t\t\tif (live === undefined || live.abort === true) return \"discard\" as const;\n\t\t\t\t\t\tconst next = typeof step.next === \"function\" ? await step.next(tx, live, lineCtx) : step.next;\n\t\t\t\t\t\tif (next === \"retry\") return \"retry\" as const;\n\t\t\t\t\t\tif (typeof next === \"object\" && next !== null && \"status\" in next) {\n\t\t\t\t\t\t\tconst patched = (await tx.task(task.id)) ?? live;\n\t\t\t\t\t\t\tcontrol.setTask({ ...patched, status: \"terminal\", outcome: validateCompletion(kind, next) });\n\t\t\t\t\t\t\treturn \"terminal\" as const;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst checkpoint = validateCheckpoint(kind, next);\n\t\t\t\t\t\ttx.checkpoint(checkpoint);\n\t\t\t\t\t\tcurrent = { ...live, checkpoint };\n\t\t\t\t\t\treturn \"advanced\" as const;\n\t\t\t\t\t},\n\t\t\t\t\tctx,\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\ttransitionLease.token.revoke();\n\t\t\t}\n\t\t\tif (outcome.value === \"retry\") {\n\t\t\t\tcurrent = session.liveTasks.get(task.id) ?? current;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (outcome.value !== \"advanced\") return undefined;\n\t\t}\n\t}\n\n\t/** Mark, revoke, signal, join. The scheduler then reserves the fresh abort on its next drain. */\n\tasync abortTask(id: Id, ctx: Context): Promise<\"marked\" | \"terminal\"> {\n\t\tconst { session } = this.deps;\n\t\tconst r = await session.commit(\n\t\t\t{ type: \"kernel\" },\n\t\t\tasync (tx) => {\n\t\t\t\tconst task = await tx.task(id);\n\t\t\t\tif (task === undefined) throw new Error(`task ${id} not found`);\n\t\t\t\tif (task.status === \"terminal\") return \"terminal\" as const;\n\t\t\t\tif (task.abort !== true) tx.setTask({ ...task, abort: true });\n\t\t\t\treturn \"marked\" as const;\n\t\t\t},\n\t\t\tctx,\n\t\t);\n\t\tif (r.value === \"terminal\") return \"terminal\";\n\t\tconst inv = this.invocations.get(id);\n\t\tif (inv?.mode === \"run\") {\n\t\t\tinv.controller.abort();\n\t\t\tawait inv.done;\n\t\t}\n\t\tthis.kick();\n\t\treturn \"marked\";\n\t}\n\n\t// --- waiters: registered on the line, atomically with the state read -------------------\n\n\twaitForTask(id: Id, ctx: Context): Promise<Task> {\n\t\treturn this.waiter(ctx, this.taskWaiters, id, (lineCtx) => {\n\t\t\tconst live = this.deps.session.liveTasks.get(id);\n\t\t\treturn live === undefined ? this.deps.session.storage.task(id, lineCtx) : Promise.resolve(undefined);\n\t\t});\n\t}\n\twaitForInput(id: Id, ctx: Context): Promise<Input> {\n\t\treturn this.waiter(ctx, this.inputWaiters, id, async (lineCtx) => {\n\t\t\tconst i = await this.deps.session.storage.input(id, lineCtx);\n\t\t\treturn i !== undefined && (i.status === \"done\" || i.status === \"unanswered\") ? i : undefined;\n\t\t});\n\t}\n\t/** Read state and install the waiter in one line operation; the wait itself happens off-line. */\n\tprivate async waiter<T>(\n\t\tctx: Context,\n\t\ttable: Map<Id, Set<(v: T) => void>>,\n\t\tid: Id,\n\t\tterminal: (ctx: Context) => Promise<T | undefined>,\n\t): Promise<T> {\n\t\tctx.abortSignal?.throwIfAborted();\n\t\tconst r = await this.deps.session.onLine(async (lineCtx): Promise<{ now: T } | { pending: Promise<T> }> => {\n\t\t\tconst now = await terminal(lineCtx);\n\t\t\tif (now !== undefined) return { now };\n\t\t\treturn {\n\t\t\t\tpending: new Promise<T>((resolve, reject) => {\n\t\t\t\t\tconst set = table.get(id) ?? table.set(id, new Set()).get(id)!;\n\t\t\t\t\tconst w = (v: T) => {\n\t\t\t\t\t\tset.delete(w);\n\t\t\t\t\t\tctx.abortSignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\tresolve(v);\n\t\t\t\t\t};\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tset.delete(w);\n\t\t\t\t\t\treject(ctx.abortSignal?.reason ?? new Error(\"aborted\"));\n\t\t\t\t\t};\n\t\t\t\t\tset.add(w);\n\t\t\t\t\tctx.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}),\n\t\t\t};\n\t\t}, ctx);\n\t\treturn \"now\" in r ? r.now : r.pending;\n\t}\n\tasync waitForIdle(conversationId: Id | undefined, ctx: Context): Promise<void> {\n\t\tctx.abortSignal?.throwIfAborted();\n\t\tconst r = await this.deps.session.onLine((): { pending?: Promise<void> } => {\n\t\t\tif (this.isIdle(conversationId)) return {};\n\t\t\treturn {\n\t\t\t\tpending: new Promise<void>((resolve, reject) => {\n\t\t\t\t\tconst w = {\n\t\t\t\t\t\tconversationId,\n\t\t\t\t\t\tresolve: () => {\n\t\t\t\t\t\t\tthis.idleWaiters.delete(w);\n\t\t\t\t\t\t\tctx.abortSignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tthis.idleWaiters.delete(w);\n\t\t\t\t\t\treject(ctx.abortSignal?.reason ?? new Error(\"aborted\"));\n\t\t\t\t\t};\n\t\t\t\t\tthis.idleWaiters.add(w);\n\t\t\t\t\tctx.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}),\n\t\t\t};\n\t\t}, ctx);\n\t\treturn r.pending;\n\t}\n\tprivate isIdle(conversationId: Id | undefined): boolean {\n\t\tfor (const t of this.deps.session.liveTasks.values())\n\t\t\tif ((conversationId === undefined || t.conversationId === conversationId) && !t.background) return false;\n\t\treturn true;\n\t}\n\tprivate checkIdle() {\n\t\tfor (const w of [...this.idleWaiters]) if (this.isIdle(w.conversationId) && !this.draining) w.resolve();\n\t}\n\n\t/** Signal every invocation and wait for them; nothing is written. */\n\tasync joinAll() {\n\t\tthis.enabled = false;\n\t\tfor (const inv of this.invocations.values()) inv.controller.abort();\n\t\tawait Promise.allSettled([...this.invocations.values()].map((invocation) => invocation.done));\n\t}\n\tquiescent(): boolean {\n\t\treturn this.invocations.size === 0;\n\t}\n\tget liveInvocations() {\n\t\treturn this.invocations.size;\n\t}\n}\n"]}