stageflow 0.32.0 → 0.35.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 (335) hide show
  1. package/README.md +4 -3
  2. package/dist/agent/adapterSpill.d.ts +14 -0
  3. package/dist/agent/adapterSpill.js +83 -0
  4. package/dist/agent/agentBackend.d.ts +2 -2
  5. package/dist/agent/agentBackend.js +2 -2
  6. package/dist/agent/claudeAdapter.d.ts +13 -2
  7. package/dist/agent/claudeAdapter.js +50 -6
  8. package/dist/agent/cursorProvider.d.ts +3 -0
  9. package/dist/agent/cursorProvider.js +23 -6
  10. package/dist/agent/fakeAgent.d.ts +3 -3
  11. package/dist/agent/fakeAgent.js +0 -6
  12. package/dist/agent/piAdapter.d.ts +2 -2
  13. package/dist/agent/piAdapter.js +116 -15
  14. package/dist/agent/piIsolatedMcp.d.ts +1 -0
  15. package/dist/agent/piIsolatedMcp.js +19 -1
  16. package/dist/agent/piIsolatedMcpProbe.d.ts +11 -0
  17. package/dist/agent/piIsolatedMcpProbe.js +20 -0
  18. package/dist/agent/port.d.ts +2 -2
  19. package/dist/agent/providerAuth.d.ts +6 -0
  20. package/dist/agent/providerAuth.js +86 -17
  21. package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -6
  22. package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +13 -9
  23. package/dist/agent/stageTurnDriver.d.ts +33 -0
  24. package/dist/agent/stageTurnDriver.js +64 -0
  25. package/dist/agent/toolOutputBudget.d.ts +46 -0
  26. package/dist/agent/toolOutputBudget.js +97 -0
  27. package/dist/agent/toolOutputBudgetExtension.d.ts +22 -0
  28. package/dist/agent/toolOutputBudgetExtension.js +141 -0
  29. package/dist/agent/toolOutputSpill.d.ts +34 -0
  30. package/dist/agent/toolOutputSpill.js +85 -0
  31. package/dist/browser/agentBrowserLiveViewRelay.d.ts +41 -0
  32. package/dist/browser/agentBrowserLiveViewRelay.js +801 -0
  33. package/dist/browser/anchor.d.ts +2 -0
  34. package/dist/browser/anchor.js +2 -1
  35. package/dist/browser/auditSink.d.ts +8 -0
  36. package/dist/browser/browserHost.d.ts +29 -0
  37. package/dist/browser/browserSweep.d.ts +4 -1
  38. package/dist/browser/browserSweep.js +7 -0
  39. package/dist/browser/browserTeardown.d.ts +1 -0
  40. package/dist/browser/browserTeardown.js +32 -2
  41. package/dist/browser/containerBrowserEndpoint.d.ts +11 -0
  42. package/dist/browser/containerBrowserEndpoint.js +57 -0
  43. package/dist/browser/containerBrowserHost.d.ts +24 -0
  44. package/dist/browser/containerBrowserHost.js +147 -0
  45. package/dist/browser/dockerSandboxOrchestrator.d.ts +26 -0
  46. package/dist/browser/dockerSandboxOrchestrator.js +142 -0
  47. package/dist/browser/fakeLiveViewRelay.d.ts +15 -0
  48. package/dist/browser/fakeLiveViewRelay.js +94 -0
  49. package/dist/browser/fakeLiveViewSource.d.ts +6 -0
  50. package/dist/browser/fakeLiveViewSource.js +16 -0
  51. package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -1
  52. package/dist/browser/fakeRemoteBrowserHost.js +2 -1
  53. package/dist/browser/fakeSandboxOrchestrator.d.ts +9 -0
  54. package/dist/browser/fakeSandboxOrchestrator.js +44 -0
  55. package/dist/browser/gateHandoff.d.ts +0 -2
  56. package/dist/browser/gateHandoff.js +5 -11
  57. package/dist/browser/hostCapabilities.d.ts +23 -0
  58. package/dist/browser/hostCapabilities.js +23 -0
  59. package/dist/browser/humanLogin.d.ts +2 -2
  60. package/dist/browser/humanLogin.js +11 -2
  61. package/dist/browser/liveViewPersisted.d.ts +11 -0
  62. package/dist/browser/liveViewPersisted.js +22 -0
  63. package/dist/browser/liveViewRelay.d.ts +76 -0
  64. package/dist/browser/liveViewRelay.js +1 -0
  65. package/dist/browser/liveViewSessions.d.ts +25 -0
  66. package/dist/browser/liveViewSessions.js +139 -0
  67. package/dist/browser/liveViewSource.d.ts +25 -0
  68. package/dist/browser/liveViewSource.js +7 -0
  69. package/dist/browser/liveViewTickets.d.ts +46 -0
  70. package/dist/browser/liveViewTickets.js +122 -0
  71. package/dist/browser/localBrowserHost.d.ts +10 -0
  72. package/dist/browser/localBrowserHost.js +53 -7
  73. package/dist/browser/ownerScope.d.ts +10 -0
  74. package/dist/browser/ownerScope.js +6 -0
  75. package/dist/browser/profileStore.d.ts +5 -2
  76. package/dist/browser/profileStore.js +3 -5
  77. package/dist/browser/sandboxOrchestrator.d.ts +60 -0
  78. package/dist/browser/sandboxOrchestrator.js +8 -0
  79. package/dist/browser/sessionApiSandboxOrchestrator.d.ts +34 -0
  80. package/dist/browser/sessionApiSandboxOrchestrator.js +108 -0
  81. package/dist/browser/stageBrowserEnv.d.ts +7 -0
  82. package/dist/browser/stageBrowserEnv.js +53 -15
  83. package/dist/browser/stageHandoff.d.ts +47 -0
  84. package/dist/browser/stageHandoff.js +101 -0
  85. package/dist/browser/stageProfileLock.d.ts +1 -0
  86. package/dist/browser/stageProfileLock.js +1 -3
  87. package/dist/browser/volumeProfileStore.d.ts +7 -0
  88. package/dist/browser/volumeProfileStore.js +35 -0
  89. package/dist/cli/browserCommand.d.ts +3 -1
  90. package/dist/cli/browserCommand.js +25 -20
  91. package/dist/cli/resolveCliProjectRoot.d.ts +7 -0
  92. package/dist/cli/resolveCliProjectRoot.js +27 -0
  93. package/dist/cli/runCommand.d.ts +1 -1
  94. package/dist/cli/runCommand.js +59 -17
  95. package/dist/cli/skillsCommand.d.ts +20 -0
  96. package/dist/cli/skillsCommand.js +74 -31
  97. package/dist/cli/validateCommand.d.ts +1 -1
  98. package/dist/cli/validateCommand.js +22 -4
  99. package/dist/cli.js +4 -4
  100. package/dist/config/browseCatalog.d.ts +2 -0
  101. package/dist/config/browseCatalog.js +6 -0
  102. package/dist/config/catalogRelativePath.d.ts +4 -0
  103. package/dist/config/catalogRelativePath.js +37 -9
  104. package/dist/config/createStage.d.ts +1 -0
  105. package/dist/config/createStage.js +11 -2
  106. package/dist/config/createTask.d.ts +30 -0
  107. package/dist/config/createTask.js +161 -0
  108. package/dist/config/createTrigger.d.ts +15 -0
  109. package/dist/config/createTrigger.js +32 -25
  110. package/dist/config/draftPackage.d.ts +41 -0
  111. package/dist/config/draftPackage.js +216 -23
  112. package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
  113. package/dist/config/ensureCatalogScanRoot.js +6 -2
  114. package/dist/config/findTaskInCatalog.d.ts +33 -0
  115. package/dist/config/findTaskInCatalog.js +69 -0
  116. package/dist/config/hostConfig.d.ts +21 -0
  117. package/dist/config/hostConfig.js +113 -1
  118. package/dist/config/importTask.d.ts +19 -0
  119. package/dist/config/importTask.js +133 -0
  120. package/dist/config/installSkillRequest.d.ts +40 -0
  121. package/dist/config/installSkillRequest.js +286 -0
  122. package/dist/config/listConfig.d.ts +0 -16
  123. package/dist/config/listConfig.js +0 -3
  124. package/dist/config/loadOutcome.d.ts +11 -6
  125. package/dist/config/loadPipeline.js +0 -1
  126. package/dist/config/loadStage.js +2 -0
  127. package/dist/config/loadStageflowManifest.js +4 -1
  128. package/dist/config/loadTask.js +2 -0
  129. package/dist/config/loadTrigger.js +2 -0
  130. package/dist/config/mergePipelineIncludes.js +3 -0
  131. package/dist/config/normalizePipelineStageEntry.js +10 -1
  132. package/dist/config/printTargetYaml.js +0 -1
  133. package/dist/config/publishDraftPackage.d.ts +5 -1
  134. package/dist/config/publishDraftPackage.js +4 -1
  135. package/dist/config/resolveCatalogRoots.d.ts +4 -1
  136. package/dist/config/resolveCatalogRoots.js +18 -1
  137. package/dist/config/skillUsageIndex.d.ts +6 -0
  138. package/dist/config/skillUsageIndex.js +39 -0
  139. package/dist/config/stageBrowser.js +13 -0
  140. package/dist/config/triggerAdapterStatus.d.ts +11 -0
  141. package/dist/config/triggerAdapterStatus.js +90 -0
  142. package/dist/config/triggerCatalog.d.ts +41 -2
  143. package/dist/config/triggerCatalog.js +107 -15
  144. package/dist/config/updateTask.d.ts +30 -0
  145. package/dist/config/updateTask.js +119 -0
  146. package/dist/config/updateTrigger.d.ts +38 -0
  147. package/dist/config/updateTrigger.js +220 -0
  148. package/dist/config/validateCatalog.d.ts +6 -2
  149. package/dist/config/validateCatalog.js +48 -7
  150. package/dist/config/workshopCatalogFilenames.d.ts +37 -0
  151. package/dist/config/workshopCatalogFilenames.js +125 -0
  152. package/dist/config/workshopCatalogRepair.d.ts +7 -0
  153. package/dist/config/workshopCatalogRepair.js +144 -0
  154. package/dist/config/yamlParsePosition.d.ts +4 -0
  155. package/dist/config/yamlParsePosition.js +9 -0
  156. package/dist/hosted/containment.d.ts +3 -0
  157. package/dist/hosted/containment.js +36 -0
  158. package/dist/hosted/errors.d.ts +21 -0
  159. package/dist/hosted/errors.js +30 -0
  160. package/dist/hosted/packageSkills.d.ts +3 -0
  161. package/dist/hosted/packageSkills.js +80 -0
  162. package/dist/hosted/runtime.d.ts +12 -0
  163. package/dist/hosted/runtime.js +596 -0
  164. package/dist/hosted/sequencer.d.ts +28 -0
  165. package/dist/hosted/sequencer.js +129 -0
  166. package/dist/hosted/translate.d.ts +3 -0
  167. package/dist/hosted/translate.js +43 -0
  168. package/dist/hosted/types.d.ts +108 -0
  169. package/dist/hosted/types.js +10 -0
  170. package/dist/index.d.ts +9 -1
  171. package/dist/index.js +6 -0
  172. package/dist/mcp/getStartedTool.js +15 -6
  173. package/dist/mcp/triggerTools.js +2 -8
  174. package/dist/operatorAgent/agentPort.d.ts +11 -0
  175. package/dist/operatorAgent/agentPort.js +12 -0
  176. package/dist/operatorAgent/index.d.ts +1 -0
  177. package/dist/operatorAgent/index.js +1 -0
  178. package/dist/operatorAgent/piModel.js +26 -6
  179. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  180. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +4 -2
  181. package/dist/operatorAgent/profiles/workshopAuthorTools.js +103 -16
  182. package/dist/package-meta.d.ts +1 -1
  183. package/dist/package-meta.js +1 -1
  184. package/dist/project/ensureProjectRootsFromEnv.d.ts +3 -0
  185. package/dist/project/ensureProjectRootsFromEnv.js +22 -0
  186. package/dist/project/globalHome.d.ts +1 -0
  187. package/dist/project/globalHome.js +18 -0
  188. package/dist/project/workspaceCatalog.d.ts +10 -0
  189. package/dist/project/workspaceCatalog.js +91 -0
  190. package/dist/projection/projectRun.d.ts +0 -1
  191. package/dist/projection/projectRun.js +0 -1
  192. package/dist/runstore/backup.js +14 -4
  193. package/dist/runstore/port.d.ts +11 -1
  194. package/dist/runstore/restore.js +5 -2
  195. package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -0
  196. package/dist/runstore/sqlite/SqliteRunStore.js +29 -9
  197. package/dist/runstore/sqlite/migrations/012-trigger-fires.d.ts +7 -0
  198. package/dist/runstore/sqlite/migrations/012-trigger-fires.js +18 -0
  199. package/dist/runstore/sqlite/migrations/index.js +2 -0
  200. package/dist/runstore/stageSnapshot.d.ts +3 -1
  201. package/dist/runstore/stageSnapshot.js +19 -0
  202. package/dist/runstore/workspaceLayout.js +27 -0
  203. package/dist/runstore/writeContainedArtifact.d.ts +1 -0
  204. package/dist/runstore/writeContainedArtifact.js +52 -0
  205. package/dist/runtime/containerLimits.d.ts +0 -3
  206. package/dist/runtime/containerLimits.js +0 -5
  207. package/dist/runtime/credentialBinding.d.ts +3 -0
  208. package/dist/runtime/credentialBinding.js +23 -6
  209. package/dist/runtime/emailSource.js +20 -28
  210. package/dist/runtime/githubPollSource.js +20 -28
  211. package/dist/runtime/pipelineRunner.d.ts +4 -4
  212. package/dist/runtime/pipelineScheduler.d.ts +2 -2
  213. package/dist/runtime/pipelineScheduler.js +3 -0
  214. package/dist/runtime/resumeReconstruct.d.ts +2 -2
  215. package/dist/runtime/resumeTimedOut.d.ts +2 -2
  216. package/dist/runtime/runManager.d.ts +18 -2
  217. package/dist/runtime/runManager.js +95 -30
  218. package/dist/runtime/runRetentionSweep.d.ts +0 -2
  219. package/dist/runtime/runRetentionSweep.js +0 -8
  220. package/dist/runtime/runRetryCoordinator.d.ts +2 -2
  221. package/dist/runtime/scheduleSource.js +15 -24
  222. package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
  223. package/dist/runtime/stageAttemptBootstrap.js +10 -1
  224. package/dist/runtime/stageProcessLauncher.js +2 -2
  225. package/dist/runtime/stageRoots.js +12 -1
  226. package/dist/runtime/stageRunner.d.ts +2 -2
  227. package/dist/runtime/stageRunner.js +8 -2
  228. package/dist/runtime/stageWorker.js +2 -2
  229. package/dist/runtime/triggerPort.d.ts +5 -3
  230. package/dist/runtime/triggerRunner.d.ts +1 -0
  231. package/dist/runtime/triggerRunner.js +27 -20
  232. package/dist/runtime/webhookAccept.d.ts +25 -0
  233. package/dist/runtime/webhookAccept.js +56 -0
  234. package/dist/server/bootstrap.d.ts +7 -2
  235. package/dist/server/bootstrap.js +15 -1
  236. package/dist/server/controlToken.d.ts +1 -1
  237. package/dist/server/controlToken.js +18 -2
  238. package/dist/server/healthSurfaces.d.ts +12 -0
  239. package/dist/server/healthSurfaces.js +27 -6
  240. package/dist/server/http.d.ts +19 -10
  241. package/dist/server/http.js +605 -85
  242. package/dist/server/liveViewPath.d.ts +9 -0
  243. package/dist/server/liveViewPath.js +8 -0
  244. package/dist/server/liveViewRoutes.d.ts +58 -0
  245. package/dist/server/liveViewRoutes.js +565 -0
  246. package/dist/server/mcpHost.d.ts +4 -2
  247. package/dist/server/mcpHost.js +2 -0
  248. package/dist/server/readCatalogFile.d.ts +29 -0
  249. package/dist/server/readCatalogFile.js +215 -0
  250. package/dist/server/triggerRoutes.d.ts +1 -0
  251. package/dist/server/triggerRoutes.js +200 -73
  252. package/dist/tools/readEmail.d.ts +3 -3
  253. package/dist/tools/writeStageArtifact.d.ts +9 -0
  254. package/dist/tools/writeStageArtifact.js +2 -47
  255. package/dist/types/pipeline.d.ts +1 -1
  256. package/dist/types/stage.d.ts +1 -1
  257. package/dist/types/stageflowManifest.d.ts +1 -1
  258. package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  259. package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  260. package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  261. package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  262. package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  263. package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  264. package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  265. package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  266. package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  267. package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  268. package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  269. package/dist/ui/assets/index-BoFPkKFg.js +256 -0
  270. package/dist/ui/assets/{index-Bv4p1aPK.css → index-CIPbbBUi.css} +1 -1
  271. package/dist/ui/index.html +4 -4
  272. package/dist/workshop/autosave.d.ts +2 -2
  273. package/dist/workshop/autosave.js +4 -6
  274. package/dist/workshop/buildStore.d.ts +2 -0
  275. package/dist/workshop/chatAttachments.d.ts +27 -0
  276. package/dist/workshop/chatAttachments.js +98 -0
  277. package/dist/workshop/chatTurn.d.ts +5 -2
  278. package/dist/workshop/chatTurn.js +22 -18
  279. package/dist/workshop/sessionMutations.d.ts +17 -0
  280. package/dist/workshop/sessionMutations.js +107 -0
  281. package/dist/workshop/sessionStore.d.ts +10 -0
  282. package/dist/workshop/sessionStore.js +52 -0
  283. package/dist/workshop/workshopPin.d.ts +4 -3
  284. package/dist/workshop/workshopPin.js +26 -6
  285. package/dist/workshop/workshopTranscriptTurn.d.ts +8 -0
  286. package/dist/workshop/workshopTranscriptTurn.js +46 -0
  287. package/examples/README.md +1 -1
  288. package/examples/archify-on-pr/README.md +1 -1
  289. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +0 -1
  290. package/examples/archify-on-pr/author-diagrams.yaml +1 -0
  291. package/examples/conditional-fork/branch-a.yaml +1 -1
  292. package/examples/conditional-fork/decide.yaml +2 -1
  293. package/examples/feature-loop/{implement.yaml → feature-implement.yaml} +1 -1
  294. package/examples/feature-loop/feature-loop.pipeline.yaml +10 -10
  295. package/examples/feature-loop/{plan.yaml → feature-plan.yaml} +1 -1
  296. package/examples/feature-loop/{review.yaml → feature-review.yaml} +1 -1
  297. package/examples/feedback-loop/{implement.yaml → feedback-implement.yaml} +1 -1
  298. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +9 -9
  299. package/examples/feedback-loop/feedback-loop.pipeline.yaml +9 -9
  300. package/examples/feedback-loop/{plan.yaml → feedback-plan.yaml} +1 -1
  301. package/examples/feedback-loop/{review.yaml → feedback-review.yaml} +1 -1
  302. package/examples/generic-fan-in/{research.yaml → fan-in-research.yaml} +1 -1
  303. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +3 -3
  304. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +3 -3
  305. package/examples/hello-world/{research.yaml → hello-research.yaml} +1 -1
  306. package/examples/hello-world/hello.pipeline.yaml +2 -2
  307. package/examples/mcp-hitl-tour/{review.yaml → mcp-hitl-review.yaml} +1 -1
  308. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +3 -3
  309. package/examples/oss-issue-contribution/oss-explain-fix.yaml +1 -0
  310. package/examples/oss-issue-contribution/oss-explain-issue.yaml +1 -0
  311. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +0 -2
  312. package/examples/stageflow.yaml +6 -0
  313. package/examples/triggers/sample-github-pr.trigger.yaml +12 -0
  314. package/examples/triggers/sample-inbox-email.trigger.yaml +14 -0
  315. package/examples/triggers/sample-manual-hello.trigger.yaml +5 -0
  316. package/examples/triggers/sample-nightly-hello.trigger.yaml +8 -0
  317. package/examples/triggers/sample-signed-webhook.trigger.yaml +11 -0
  318. package/examples/triggers-readiness/README.md +48 -0
  319. package/examples/triggers-readiness/readiness-emit.yaml +18 -0
  320. package/examples/triggers-readiness/readiness.pipeline.yaml +4 -0
  321. package/examples/triggers-readiness/readiness.task.yaml +6 -0
  322. package/examples/triggers-readiness/stageflow.yaml +13 -0
  323. package/examples/triggers-readiness/triggers/readiness-dynamic-manual.trigger.yaml +4 -0
  324. package/examples/triggers-readiness/triggers/readiness-email.trigger.yaml +12 -0
  325. package/examples/triggers-readiness/triggers/readiness-github-pr.trigger.yaml +12 -0
  326. package/examples/triggers-readiness/triggers/readiness-manual.trigger.yaml +5 -0
  327. package/examples/triggers-readiness/triggers/readiness-schedule.trigger.yaml +8 -0
  328. package/examples/triggers-readiness/triggers/readiness-webhook.trigger.yaml +11 -0
  329. package/package.json +9 -1
  330. package/skills/stageflow/references/control-surface.md +1 -1
  331. package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
  332. package/skills/stageflow-run/SKILL.md +1 -1
  333. package/skills/stageflow-run/references/mcp-call.md +1 -1
  334. package/skills/stageflow-setup/SKILL.md +1 -1
  335. package/dist/ui/assets/index-59cjF0OA.js +0 -133
@@ -0,0 +1,64 @@
1
+ /** Follow-up turns sent when the agent ends its turn without an accepted envelope. */
2
+ export const DEFAULT_EMIT_REMINDERS = 2;
3
+ const PROVIDER_ERROR_MAX_CHARS = 500;
4
+ function lastAssistant(messages) {
5
+ for (let i = messages.length - 1; i >= 0; i--) {
6
+ const m = messages[i];
7
+ if (m && typeof m === "object" && m.role === "assistant")
8
+ return m;
9
+ }
10
+ return undefined;
11
+ }
12
+ /** Trimmed, length-capped provider error text for a stage failure reason. */
13
+ export function formatProviderError(text) {
14
+ const t = text?.trim() ? text.trim() : "the model provider returned an error";
15
+ return t.length > PROVIDER_ERROR_MAX_CHARS ? `${t.slice(0, PROVIDER_ERROR_MAX_CHARS)}…` : t;
16
+ }
17
+ /**
18
+ * Error text of the last assistant turn when it ended on a provider error (bad key, auth,
19
+ * model unavailable, or a transient error Pi already gave up retrying). Undefined otherwise.
20
+ */
21
+ export function lastTurnProviderError(messages) {
22
+ const m = lastAssistant(messages);
23
+ if (!m || m.stopReason !== "error")
24
+ return undefined;
25
+ return formatProviderError(typeof m.errorMessage === "string" ? m.errorMessage : undefined);
26
+ }
27
+ function lastTurnAborted(messages) {
28
+ return lastAssistant(messages)?.stopReason === "aborted";
29
+ }
30
+ export function emitReminderPrompt(emitToolName, attempt, max, rejectedEmit) {
31
+ const lines = [
32
+ `Reminder ${attempt} of ${max}: your turn ended without an accepted ${emitToolName} call, so this stage has not finished.`,
33
+ ];
34
+ if (rejectedEmit !== undefined) {
35
+ lines.push(`Your last ${emitToolName} call was rejected: ${rejectedEmit}`);
36
+ lines.push(`Fix that and call ${emitToolName} again.`);
37
+ }
38
+ else {
39
+ lines.push(`Continue the stage's work if any is left, then call ${emitToolName}.`);
40
+ }
41
+ lines.push(`If the stage cannot be completed, call ${emitToolName} with status "failure" and a summary that says why. Do not end your turn without calling it.`);
42
+ return lines.join("\n");
43
+ }
44
+ /**
45
+ * Called after a turn returns. While no envelope was accepted, nudge the agent with a short
46
+ * follow-up turn, up to `maxReminders` times. Stops at once when an envelope is captured,
47
+ * the stage is stopping, the turn was aborted, or the turn ended on a provider error.
48
+ */
49
+ export async function remindUntilEmitted(session, capture, options) {
50
+ const max = options.maxReminders ?? DEFAULT_EMIT_REMINDERS;
51
+ let reminders = 0;
52
+ for (;;) {
53
+ if (capture.envelope)
54
+ return { reminders };
55
+ const providerError = lastTurnProviderError(session.messages);
56
+ if (providerError !== undefined)
57
+ return { reminders, providerError };
58
+ if (options.shouldStop() || lastTurnAborted(session.messages) || reminders >= max) {
59
+ return { reminders };
60
+ }
61
+ reminders++;
62
+ await session.prompt(emitReminderPrompt(options.emitToolName, reminders, max, capture.error));
63
+ }
64
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Per-result decision: keep a tool result inline, or spill it to a stage
3
+ * artifact. Pure; see toolOutputBudgetExtension for the Pi wiring.
4
+ */
5
+ export declare const TOOL_OUTPUT_HARD_CEILING_BYTES: number;
6
+ export declare const BYTES_PER_TOKEN = 3;
7
+ export declare const RESERVE_MIN_TOKENS = 16000;
8
+ export declare const RESERVE_FRACTION = 0.25;
9
+ export declare const PER_RESULT_MAX_FRACTION = 0.3;
10
+ export declare const UNKNOWN_USAGE_INLINE_MAX_BYTES: number;
11
+ export declare const IMAGE_BLOCK_EST_TOKENS = 1500;
12
+ export declare const PREVIEW_MAX_BYTES: number;
13
+ export type BudgetInput = {
14
+ resultBytes: number;
15
+ contextWindow: number;
16
+ knownTokens: number | null;
17
+ turnLedgerTokens: number;
18
+ };
19
+ export type SpillReason = "exceeds_remaining" | "exceeds_per_result_cap" | "unknown_usage_fallback" | "adapter_truncated";
20
+ export type BudgetDecision = {
21
+ kind: "inline";
22
+ estTokens: number;
23
+ } | {
24
+ kind: "spill";
25
+ estTokens: number;
26
+ reason: SpillReason;
27
+ };
28
+ export declare function estimateTokens(bytes: number): number;
29
+ export declare function decideToolOutput(input: BudgetInput): BudgetDecision;
30
+ export declare function utf8ByteLength(text: string): number;
31
+ export declare function truncateUtf8(text: string, maxBytes: number): string;
32
+ export declare function tryParseJson(text: string): {
33
+ ok: true;
34
+ value: unknown;
35
+ } | {
36
+ ok: false;
37
+ };
38
+ export declare function describeJsonShape(value: unknown): string;
39
+ /**
40
+ * Short model-facing preview of a spilled body. `body` is the normalized text
41
+ * that was written to disk, so preview line numbers match what `read` pages.
42
+ */
43
+ export declare function buildPreview(body: string, options?: {
44
+ json?: unknown;
45
+ maxBytes?: number;
46
+ }): string;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Per-result decision: keep a tool result inline, or spill it to a stage
3
+ * artifact. Pure; see toolOutputBudgetExtension for the Pi wiring.
4
+ */
5
+ export const TOOL_OUTPUT_HARD_CEILING_BYTES = 8 * 1024 * 1024;
6
+ export const BYTES_PER_TOKEN = 3;
7
+ export const RESERVE_MIN_TOKENS = 16_000;
8
+ export const RESERVE_FRACTION = 0.25;
9
+ export const PER_RESULT_MAX_FRACTION = 0.3;
10
+ export const UNKNOWN_USAGE_INLINE_MAX_BYTES = 50 * 1024;
11
+ export const IMAGE_BLOCK_EST_TOKENS = 1_500;
12
+ export const PREVIEW_MAX_BYTES = 2 * 1024;
13
+ export function estimateTokens(bytes) {
14
+ return Math.ceil(Math.max(0, bytes) / BYTES_PER_TOKEN);
15
+ }
16
+ export function decideToolOutput(input) {
17
+ const estTokens = estimateTokens(input.resultBytes);
18
+ const window = input.contextWindow;
19
+ if (input.knownTokens === null || !Number.isFinite(window) || window <= 0) {
20
+ return input.resultBytes <= UNKNOWN_USAGE_INLINE_MAX_BYTES
21
+ ? { kind: "inline", estTokens }
22
+ : { kind: "spill", estTokens, reason: "unknown_usage_fallback" };
23
+ }
24
+ if (estTokens > PER_RESULT_MAX_FRACTION * window) {
25
+ return { kind: "spill", estTokens, reason: "exceeds_per_result_cap" };
26
+ }
27
+ const reserve = Math.max(RESERVE_MIN_TOKENS, RESERVE_FRACTION * window);
28
+ const remaining = window - input.knownTokens - input.turnLedgerTokens - reserve;
29
+ if (estTokens > remaining) {
30
+ return { kind: "spill", estTokens, reason: "exceeds_remaining" };
31
+ }
32
+ return { kind: "inline", estTokens };
33
+ }
34
+ export function utf8ByteLength(text) {
35
+ return Buffer.byteLength(text, "utf8");
36
+ }
37
+ export function truncateUtf8(text, maxBytes) {
38
+ const buffer = Buffer.from(text, "utf8");
39
+ if (buffer.length <= maxBytes)
40
+ return text;
41
+ let end = Math.max(0, Math.floor(maxBytes));
42
+ while (end > 0 && (buffer[end] & 0xc0) === 0x80)
43
+ end--;
44
+ return buffer.subarray(0, end).toString("utf8");
45
+ }
46
+ export function tryParseJson(text) {
47
+ const trimmed = text.trim();
48
+ if (!(trimmed.startsWith("{") || trimmed.startsWith("[")))
49
+ return { ok: false };
50
+ try {
51
+ return { ok: true, value: JSON.parse(trimmed) };
52
+ }
53
+ catch {
54
+ return { ok: false };
55
+ }
56
+ }
57
+ const SHAPE_MAX_KEYS = 20;
58
+ function describeValue(value) {
59
+ if (Array.isArray(value))
60
+ return `array(${value.length})`;
61
+ if (value === null)
62
+ return "null";
63
+ if (typeof value === "object") {
64
+ return `object(${Object.keys(value).length} keys)`;
65
+ }
66
+ if (typeof value === "string")
67
+ return `string(${value.length} chars)`;
68
+ return typeof value;
69
+ }
70
+ export function describeJsonShape(value) {
71
+ if (Array.isArray(value)) {
72
+ const first = value.length > 0 ? `; first item: ${describeValue(value[0])}` : "";
73
+ return `JSON array with ${value.length} items${first}`;
74
+ }
75
+ if (value !== null && typeof value === "object") {
76
+ const entries = Object.entries(value);
77
+ const shown = entries
78
+ .slice(0, SHAPE_MAX_KEYS)
79
+ .map(([key, child]) => `${key}: ${describeValue(child)}`);
80
+ const more = entries.length > SHAPE_MAX_KEYS
81
+ ? `, … ${entries.length - SHAPE_MAX_KEYS} more keys`
82
+ : "";
83
+ return `JSON object { ${shown.join(", ")}${more} }`;
84
+ }
85
+ return `JSON ${describeValue(value)}`;
86
+ }
87
+ /**
88
+ * Short model-facing preview of a spilled body. `body` is the normalized text
89
+ * that was written to disk, so preview line numbers match what `read` pages.
90
+ */
91
+ export function buildPreview(body, options = {}) {
92
+ const maxBytes = options.maxBytes ?? PREVIEW_MAX_BYTES;
93
+ const shape = options.json !== undefined ? `Shape: ${describeJsonShape(options.json)}\n` : "";
94
+ const head = truncateUtf8(body, Math.max(0, maxBytes - utf8ByteLength(shape)));
95
+ const ellipsis = head.length < body.length ? "\n…" : "";
96
+ return `${shape}Preview:\n${head}${ellipsis}`;
97
+ }
@@ -0,0 +1,22 @@
1
+ import type { ExtensionFactory } from "@earendil-works/pi-coding-agent";
2
+ import { type SpillReason } from "./toolOutputBudget.js";
3
+ import { type SpilledToolOutput } from "./toolOutputSpill.js";
4
+ export declare const STAGEFLOW_TOOL_OUTPUT_EXTENSION_NAME = "stageflow-tool-output";
5
+ export type ToolOutputSpillEvent = {
6
+ toolName: string;
7
+ toolCallId: string;
8
+ reason: SpillReason;
9
+ originalBytes: number;
10
+ estTokens: number;
11
+ runRelativePath?: string;
12
+ error?: string;
13
+ };
14
+ export type ToolOutputBudgetOptions = {
15
+ runWorkspaceDir: string;
16
+ stageId: string;
17
+ attempt: number;
18
+ exempt: ReadonlySet<string>;
19
+ onSpill?: (event: ToolOutputSpillEvent) => void;
20
+ };
21
+ export declare function formatSpillNotice(spilled: SpilledToolOutput, estTokens: number, lostReason?: string): string;
22
+ export declare function createToolOutputBudgetExtension(options: ToolOutputBudgetOptions): ExtensionFactory;
@@ -0,0 +1,141 @@
1
+ import { buildPreview, decideToolOutput, estimateTokens, IMAGE_BLOCK_EST_TOKENS, truncateUtf8, UNKNOWN_USAGE_INLINE_MAX_BYTES, utf8ByteLength, } from "./toolOutputBudget.js";
2
+ import { spillToolOutput } from "./toolOutputSpill.js";
3
+ import { discardAdapterSpill, readAdapterSpill } from "./adapterSpill.js";
4
+ export const STAGEFLOW_TOOL_OUTPUT_EXTENSION_NAME = "stageflow-tool-output";
5
+ export function formatSpillNotice(spilled, estTokens, lostReason) {
6
+ return [
7
+ `[Tool output saved as stage artifact: ${spilled.runRelativePath}`,
8
+ ` Read path: ${spilled.absolutePath}`,
9
+ ` Size: ${spilled.bytes} bytes, ${spilled.lines} lines (~${estTokens} tokens). Too large to include inline.`,
10
+ lostReason !== undefined
11
+ ? ` The MCP adapter cut this output and the rest is not available (${lostReason}). This file holds only what was returned.`
12
+ : undefined,
13
+ spilled.wrapped
14
+ ? ` Long lines were wrapped at 4 KiB so the file can be paged; the breaks are not part of the data.`
15
+ : undefined,
16
+ spilled.hasLongLines
17
+ ? ` Some JSON lines are long string values; page them with bash (jq, cut -c) rather than read.`
18
+ : undefined,
19
+ ` It is already a stage artifact: read it with \`read\` (offset/limit) or grep it, and list`,
20
+ ` this path in the envelope artifacts if the stage output depends on it. Do not copy it.]`,
21
+ ]
22
+ .filter((line) => line !== undefined)
23
+ .join("\n");
24
+ }
25
+ function joinText(content) {
26
+ return content
27
+ .filter((block) => block.type === "text")
28
+ .map((block) => block.text)
29
+ .join("\n");
30
+ }
31
+ export function createToolOutputBudgetExtension(options) {
32
+ return (pi) => {
33
+ let turnLedgerTokens = 0;
34
+ pi.on("message_end", async (event) => {
35
+ if (event.message.role === "assistant") {
36
+ turnLedgerTokens = 0;
37
+ }
38
+ return undefined;
39
+ });
40
+ pi.on("tool_result", async (event, ctx) => {
41
+ if (options.exempt.has(event.toolName))
42
+ return undefined;
43
+ if (event.parentToolCallId !== undefined)
44
+ return undefined;
45
+ const images = event.content.filter((block) => block.type === "image");
46
+ turnLedgerTokens += images.length * IMAGE_BLOCK_EST_TOKENS;
47
+ const visible = joinText(event.content);
48
+ // A result the MCP adapter already cut is incomplete inline: always spill
49
+ // it, from the adapter's full-output file when that file checks out.
50
+ const adapterSpill = await readAdapterSpill(visible, event.details);
51
+ const text = adapterSpill.kind === "full" ? adapterSpill.text : visible;
52
+ const resultBytes = utf8ByteLength(text);
53
+ const usage = ctx.getContextUsage();
54
+ const decision = adapterSpill.kind === "none"
55
+ ? decideToolOutput({
56
+ resultBytes,
57
+ contextWindow: usage?.contextWindow ?? 0,
58
+ knownTokens: usage?.tokens ?? null,
59
+ turnLedgerTokens,
60
+ })
61
+ : { kind: "spill", estTokens: estimateTokens(resultBytes), reason: "adapter_truncated" };
62
+ if (decision.kind === "inline") {
63
+ turnLedgerTokens += decision.estTokens;
64
+ return undefined;
65
+ }
66
+ let replacement;
67
+ let spillDetails;
68
+ let fullOutputPath;
69
+ try {
70
+ const spilled = await spillToolOutput({
71
+ runWorkspaceDir: options.runWorkspaceDir,
72
+ stageId: options.stageId,
73
+ attempt: options.attempt,
74
+ toolCallId: event.toolCallId,
75
+ toolName: event.toolName,
76
+ text,
77
+ });
78
+ if (adapterSpill.kind === "full") {
79
+ await discardAdapterSpill(adapterSpill.sourcePath);
80
+ fullOutputPath = spilled.absolutePath;
81
+ }
82
+ const notice = formatSpillNotice(spilled, decision.estTokens, adapterSpill.kind === "lost" ? adapterSpill.reason : undefined);
83
+ replacement = `${notice}\n\n${buildPreview(spilled.body, spilled.json !== undefined ? { json: spilled.json } : {})}`;
84
+ spillDetails = {
85
+ spilled: true,
86
+ path: spilled.runRelativePath,
87
+ bytes: spilled.bytes,
88
+ lines: spilled.lines,
89
+ originalBytes: resultBytes,
90
+ estTokens: decision.estTokens,
91
+ reason: decision.reason,
92
+ ...(adapterSpill.kind === "lost" ? { incomplete: true } : {}),
93
+ };
94
+ options.onSpill?.({
95
+ toolName: event.toolName,
96
+ toolCallId: event.toolCallId,
97
+ reason: decision.reason,
98
+ originalBytes: resultBytes,
99
+ estTokens: decision.estTokens,
100
+ runRelativePath: spilled.runRelativePath,
101
+ });
102
+ }
103
+ catch (err) {
104
+ const message = err instanceof Error ? err.message : String(err);
105
+ replacement = `${truncateUtf8(text, UNKNOWN_USAGE_INLINE_MAX_BYTES)}\n\n[Tool output truncated: original ${resultBytes} bytes. Saving the full output as a stage artifact failed: ${message}]`;
106
+ spillDetails = {
107
+ spilled: false,
108
+ originalBytes: resultBytes,
109
+ estTokens: decision.estTokens,
110
+ reason: decision.reason,
111
+ error: message,
112
+ };
113
+ options.onSpill?.({
114
+ toolName: event.toolName,
115
+ toolCallId: event.toolCallId,
116
+ reason: decision.reason,
117
+ originalBytes: resultBytes,
118
+ estTokens: decision.estTokens,
119
+ error: message,
120
+ });
121
+ }
122
+ turnLedgerTokens += estimateTokens(utf8ByteLength(replacement));
123
+ const previousDetails = event.details !== null && typeof event.details === "object"
124
+ ? event.details
125
+ : {};
126
+ const outputGuard = previousDetails.outputGuard;
127
+ return {
128
+ content: [{ type: "text", text: replacement }, ...images],
129
+ details: {
130
+ ...previousDetails,
131
+ ...(fullOutputPath !== undefined &&
132
+ outputGuard !== null &&
133
+ typeof outputGuard === "object"
134
+ ? { outputGuard: { ...outputGuard, fullOutputPath } }
135
+ : {}),
136
+ stageflowToolOutput: spillDetails,
137
+ },
138
+ };
139
+ });
140
+ };
141
+ }
@@ -0,0 +1,34 @@
1
+ export declare const TOOL_OUTPUT_ARTIFACT_DIR = "tool-output";
2
+ export declare const SPILL_WRAP_LINE_BYTES: number;
3
+ export type SpillToolOutputOptions = {
4
+ runWorkspaceDir: string;
5
+ stageId: string;
6
+ attempt: number;
7
+ toolCallId: string;
8
+ toolName: string;
9
+ text: string;
10
+ };
11
+ export type SpilledToolOutput = {
12
+ runRelativePath: string;
13
+ absolutePath: string;
14
+ body: string;
15
+ bytes: number;
16
+ lines: number;
17
+ format: "json" | "text";
18
+ wrapped: boolean;
19
+ hasLongLines: boolean;
20
+ json?: unknown;
21
+ };
22
+ export declare function sanitizeNamePart(value: string): string;
23
+ export declare function wrapLongLines(text: string, maxLineBytes?: number): {
24
+ text: string;
25
+ wrapped: boolean;
26
+ };
27
+ export declare function normalizeSpillBody(text: string): {
28
+ body: string;
29
+ format: "json" | "text";
30
+ wrapped: boolean;
31
+ hasLongLines: boolean;
32
+ json?: unknown;
33
+ };
34
+ export declare function spillToolOutput(options: SpillToolOutputOptions): Promise<SpilledToolOutput>;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Writes an oversized tool result to the stage attempt's artifacts directory
3
+ * as `tool-output/<toolCallId>-<toolName>.<json|txt>`, normalized so the
4
+ * builtin `read` tool can page it by line.
5
+ */
6
+ import { resolveArtifactTarget } from "../runstore/workspaceLayout.js";
7
+ import { writeContainedArtifact } from "../runstore/writeContainedArtifact.js";
8
+ import { tryParseJson, utf8ByteLength } from "./toolOutputBudget.js";
9
+ export const TOOL_OUTPUT_ARTIFACT_DIR = "tool-output";
10
+ export const SPILL_WRAP_LINE_BYTES = 4 * 1024;
11
+ const NAME_PART_MAX = 64;
12
+ export function sanitizeNamePart(value) {
13
+ const cleaned = value.replace(/[^A-Za-z0-9_.-]+/g, "_").replace(/^\.+/, "_");
14
+ const capped = cleaned.slice(0, NAME_PART_MAX);
15
+ return capped.length > 0 ? capped : "_";
16
+ }
17
+ function codePointUtf8Bytes(codePoint) {
18
+ if (codePoint < 0x80)
19
+ return 1;
20
+ if (codePoint < 0x800)
21
+ return 2;
22
+ if (codePoint < 0x10000)
23
+ return 3;
24
+ return 4;
25
+ }
26
+ export function wrapLongLines(text, maxLineBytes = SPILL_WRAP_LINE_BYTES) {
27
+ let wrapped = false;
28
+ const out = [];
29
+ for (const line of text.split("\n")) {
30
+ if (utf8ByteLength(line) <= maxLineBytes) {
31
+ out.push(line);
32
+ continue;
33
+ }
34
+ wrapped = true;
35
+ // Cut by code points so a chunk never splits a surrogate pair.
36
+ let start = 0;
37
+ let offset = 0;
38
+ let bytes = 0;
39
+ for (const ch of line) {
40
+ const size = codePointUtf8Bytes(ch.codePointAt(0));
41
+ if (bytes + size > maxLineBytes && offset > start) {
42
+ out.push(line.slice(start, offset));
43
+ start = offset;
44
+ bytes = 0;
45
+ }
46
+ bytes += size;
47
+ offset += ch.length;
48
+ }
49
+ out.push(line.slice(start));
50
+ }
51
+ return { text: out.join("\n"), wrapped };
52
+ }
53
+ export function normalizeSpillBody(text) {
54
+ const parsed = tryParseJson(text);
55
+ if (parsed.ok) {
56
+ // JSON stays valid: long string values are not wrapped.
57
+ const body = JSON.stringify(parsed.value, null, 2);
58
+ return {
59
+ body,
60
+ format: "json",
61
+ wrapped: false,
62
+ json: parsed.value,
63
+ hasLongLines: body.split("\n").some((line) => utf8ByteLength(line) > SPILL_WRAP_LINE_BYTES),
64
+ };
65
+ }
66
+ const { text: body, wrapped } = wrapLongLines(text);
67
+ return { body, format: "text", wrapped, hasLongLines: false };
68
+ }
69
+ export async function spillToolOutput(options) {
70
+ const normalized = normalizeSpillBody(options.text);
71
+ const fileName = `${sanitizeNamePart(options.toolCallId)}-${sanitizeNamePart(options.toolName)}.${normalized.format === "json" ? "json" : "txt"}`;
72
+ const { absolutePath, runRelativePath, artifactsDir } = resolveArtifactTarget(options.runWorkspaceDir, options.stageId, options.attempt, `${TOOL_OUTPUT_ARTIFACT_DIR}/${fileName}`);
73
+ await writeContainedArtifact(options.runWorkspaceDir, artifactsDir, absolutePath, normalized.body);
74
+ return {
75
+ runRelativePath,
76
+ absolutePath,
77
+ body: normalized.body,
78
+ bytes: utf8ByteLength(normalized.body),
79
+ lines: normalized.body.split("\n").length,
80
+ format: normalized.format,
81
+ wrapped: normalized.wrapped,
82
+ hasLongLines: normalized.hasLongLines,
83
+ ...(normalized.json !== undefined ? { json: normalized.json } : {}),
84
+ };
85
+ }
@@ -0,0 +1,41 @@
1
+ import type { BrowserRunner } from "./browserHost.js";
2
+ import type { LiveViewRelay } from "./liveViewRelay.js";
3
+ export declare const DEFAULT_MAX_INPUT_BATCH = 64;
4
+ export declare const DEFAULT_MAX_INPUT_EVENTS_PER_SECOND = 500;
5
+ export declare const DEFAULT_REOPEN_MIN_INTERVAL_MS = 2000;
6
+ export type LiveViewRelayErrorCode = "stream_port_unavailable" | "stream_connect_failed";
7
+ export declare class LiveViewRelayError extends Error {
8
+ readonly code: LiveViewRelayErrorCode;
9
+ constructor(code: LiveViewRelayErrorCode, message: string);
10
+ }
11
+ export type WebSocketLike = {
12
+ readonly readyState: number;
13
+ onopen: ((event: unknown) => void) | null;
14
+ onmessage: ((event: {
15
+ data: unknown;
16
+ }) => void) | null;
17
+ onerror: ((event: unknown) => void) | null;
18
+ onclose: ((event: unknown) => void) | null;
19
+ send(data: string): void;
20
+ close(): void;
21
+ };
22
+ export type WebSocketConstructor = new (url: string) => WebSocketLike;
23
+ export type AgentBrowserLiveViewRelayOptions = {
24
+ runner: BrowserRunner;
25
+ WebSocketImpl?: WebSocketConstructor;
26
+ maxBatch?: number;
27
+ maxEventsPerSecond?: number;
28
+ now?: () => number;
29
+ /** Pause between re-target polls of the tab list and stream reconnects. */
30
+ retryDelayMs?: number;
31
+ /** How long a fresh stream connection may stay frameless before one restart nudge. */
32
+ firstFrameTimeoutMs?: number;
33
+ /** An open confirm or prompt nobody answers is dismissed after this long. */
34
+ dialogTimeoutMs?: number;
35
+ /** Minimum gap between two reopenTab() calls on one session. */
36
+ reopenMinIntervalMs?: number;
37
+ /** Timer seam for the first-frame nudge and the dialog time-out; returns a cancel function. */
38
+ schedule?: (fn: () => void, ms: number) => () => void;
39
+ };
40
+ export declare function validEvent(event: unknown): boolean;
41
+ export declare function createAgentBrowserLiveViewRelay(options: AgentBrowserLiveViewRelayOptions): LiveViewRelay;