@code-yeongyu/senpi 2026.8.6 → 2026.8.9

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 (154) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/core/agent-session.d.ts +10 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +71 -50
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/extensions/builtin/bash-timeout/index.d.ts +2 -2
  7. package/dist/core/extensions/builtin/bash-timeout/index.d.ts.map +1 -1
  8. package/dist/core/extensions/builtin/bash-timeout/index.js +17 -20
  9. package/dist/core/extensions/builtin/bash-timeout/index.js.map +1 -1
  10. package/dist/core/extensions/builtin/bash-timeout/timeout.d.ts +7 -12
  11. package/dist/core/extensions/builtin/bash-timeout/timeout.d.ts.map +1 -1
  12. package/dist/core/extensions/builtin/bash-timeout/timeout.js +11 -19
  13. package/dist/core/extensions/builtin/bash-timeout/timeout.js.map +1 -1
  14. package/dist/core/extensions/builtin/goal/cache-warm.d.ts +2 -0
  15. package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
  17. package/dist/core/extensions/builtin/goal/monitor-continuation-types.d.ts +1 -0
  18. package/dist/core/extensions/builtin/goal/monitor-continuation-types.d.ts.map +1 -1
  19. package/dist/core/extensions/builtin/goal/monitor-continuation-types.js.map +1 -1
  20. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  21. package/dist/core/extensions/builtin/goal/monitor-continuation.js +76 -35
  22. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  23. package/dist/core/extensions/builtin/goal/prompt.d.ts +1 -1
  24. package/dist/core/extensions/builtin/goal/prompt.d.ts.map +1 -1
  25. package/dist/core/extensions/builtin/goal/prompt.js +21 -7
  26. package/dist/core/extensions/builtin/goal/prompt.js.map +1 -1
  27. package/dist/core/extensions/builtin/goal/wait-progress.d.ts +3 -1
  28. package/dist/core/extensions/builtin/goal/wait-progress.d.ts.map +1 -1
  29. package/dist/core/extensions/builtin/goal/wait-progress.js +27 -3
  30. package/dist/core/extensions/builtin/goal/wait-progress.js.map +1 -1
  31. package/dist/core/extensions/builtin/goal/wait-ticker.d.ts +4 -3
  32. package/dist/core/extensions/builtin/goal/wait-ticker.d.ts.map +1 -1
  33. package/dist/core/extensions/builtin/goal/wait-ticker.js +6 -6
  34. package/dist/core/extensions/builtin/goal/wait-ticker.js.map +1 -1
  35. package/dist/core/extensions/builtin/import-repro.d.ts.map +1 -1
  36. package/dist/core/extensions/builtin/import-repro.js +9 -0
  37. package/dist/core/extensions/builtin/import-repro.js.map +1 -1
  38. package/dist/core/extensions/builtin/model-fallback/settings.d.ts.map +1 -1
  39. package/dist/core/extensions/builtin/model-fallback/settings.js +21 -1
  40. package/dist/core/extensions/builtin/model-fallback/settings.js.map +1 -1
  41. package/dist/core/extensions/builtin/resumption-channel-event.d.ts +13 -0
  42. package/dist/core/extensions/builtin/resumption-channel-event.d.ts.map +1 -0
  43. package/dist/core/extensions/builtin/resumption-channel-event.js +13 -0
  44. package/dist/core/extensions/builtin/resumption-channel-event.js.map +1 -0
  45. package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
  46. package/dist/core/extensions/builtin/terminal/extension.js +20 -0
  47. package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
  48. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +5 -1
  49. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  50. package/dist/core/extensions/builtin/terminal/session-bundle.js +19 -2
  51. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  52. package/dist/core/extensions/builtin/terminal/tools/bash.d.ts.map +1 -1
  53. package/dist/core/extensions/builtin/terminal/tools/bash.js +24 -10
  54. package/dist/core/extensions/builtin/terminal/tools/bash.js.map +1 -1
  55. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +2 -0
  56. package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
  58. package/dist/core/extensions/builtin/terminal/tools/foreground-window.d.ts +11 -0
  59. package/dist/core/extensions/builtin/terminal/tools/foreground-window.d.ts.map +1 -0
  60. package/dist/core/extensions/builtin/terminal/tools/foreground-window.js +16 -0
  61. package/dist/core/extensions/builtin/terminal/tools/foreground-window.js.map +1 -0
  62. package/dist/core/extensions/builtin/terminal/tools/sleep-wait.d.ts +28 -0
  63. package/dist/core/extensions/builtin/terminal/tools/sleep-wait.d.ts.map +1 -0
  64. package/dist/core/extensions/builtin/terminal/tools/sleep-wait.js +75 -0
  65. package/dist/core/extensions/builtin/terminal/tools/sleep-wait.js.map +1 -0
  66. package/dist/core/extensions/builtin/tps.d.ts.map +1 -1
  67. package/dist/core/extensions/builtin/tps.js +3 -3
  68. package/dist/core/extensions/builtin/tps.js.map +1 -1
  69. package/dist/core/retry-fallback/chains.d.ts +13 -1
  70. package/dist/core/retry-fallback/chains.d.ts.map +1 -1
  71. package/dist/core/retry-fallback/chains.js +80 -8
  72. package/dist/core/retry-fallback/chains.js.map +1 -1
  73. package/dist/core/retry-fallback/controller.d.ts +2 -0
  74. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  75. package/dist/core/retry-fallback/controller.js.map +1 -1
  76. package/dist/core/retry-fallback/expansion.d.ts +46 -0
  77. package/dist/core/retry-fallback/expansion.d.ts.map +1 -0
  78. package/dist/core/retry-fallback/expansion.js +113 -0
  79. package/dist/core/retry-fallback/expansion.js.map +1 -0
  80. package/dist/core/retry-fallback/hint-policy.d.ts +14 -0
  81. package/dist/core/retry-fallback/hint-policy.d.ts.map +1 -1
  82. package/dist/core/retry-fallback/hint-policy.js +14 -0
  83. package/dist/core/retry-fallback/hint-policy.js.map +1 -1
  84. package/dist/core/retry-fallback/settings.d.ts +6 -0
  85. package/dist/core/retry-fallback/settings.d.ts.map +1 -1
  86. package/dist/core/retry-fallback/settings.js +12 -10
  87. package/dist/core/retry-fallback/settings.js.map +1 -1
  88. package/dist/core/retry-fallback/validate.d.ts.map +1 -1
  89. package/dist/core/retry-fallback/validate.js +14 -1
  90. package/dist/core/retry-fallback/validate.js.map +1 -1
  91. package/dist/main.d.ts.map +1 -1
  92. package/dist/main.js +18 -3
  93. package/dist/main.js.map +1 -1
  94. package/dist/modes/interactive/components/favorite-models-selector.d.ts +4 -0
  95. package/dist/modes/interactive/components/favorite-models-selector.d.ts.map +1 -1
  96. package/dist/modes/interactive/components/favorite-models-selector.js +23 -7
  97. package/dist/modes/interactive/components/favorite-models-selector.js.map +1 -1
  98. package/dist/modes/interactive/components/model-selector.d.ts +1 -0
  99. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  100. package/dist/modes/interactive/components/model-selector.js +11 -7
  101. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  102. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  103. package/dist/modes/interactive/interactive-mode.js +25 -6
  104. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  105. package/dist/modes/interactive/model-search-rank.d.ts +13 -0
  106. package/dist/modes/interactive/model-search-rank.d.ts.map +1 -0
  107. package/dist/modes/interactive/model-search-rank.js +200 -0
  108. package/dist/modes/interactive/model-search-rank.js.map +1 -0
  109. package/dist/modes/interactive/model-search.d.ts +0 -5
  110. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  111. package/dist/modes/interactive/model-search.js +0 -9
  112. package/dist/modes/interactive/model-search.js.map +1 -1
  113. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  114. package/dist/modes/rpc/session-command-router.js +3 -2
  115. package/dist/modes/rpc/session-command-router.js.map +1 -1
  116. package/dist/modes/rpc/session-registry.d.ts +1 -1
  117. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  118. package/dist/modes/rpc/session-registry.js +3 -3
  119. package/dist/modes/rpc/session-registry.js.map +1 -1
  120. package/docs/settings.md +1 -1
  121. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +36 -0
  122. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  123. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/resumption-channel.ts +29 -0
  124. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +10 -0
  125. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +26 -1
  126. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +2 -1
  127. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.d.ts.map +1 -1
  128. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js +4 -7
  129. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js.map +1 -1
  130. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  131. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  132. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  133. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  134. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  135. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  136. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  137. package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  138. package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
  139. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  140. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  141. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  142. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  143. package/node_modules/@earendil-works/pi-ai/dist/utils/visible-text.d.ts +4 -0
  144. package/node_modules/@earendil-works/pi-ai/dist/utils/visible-text.d.ts.map +1 -0
  145. package/node_modules/@earendil-works/pi-ai/dist/utils/visible-text.js +8 -0
  146. package/node_modules/@earendil-works/pi-ai/dist/utils/visible-text.js.map +1 -0
  147. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  148. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.d.ts +12 -0
  149. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.d.ts.map +1 -1
  150. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js +34 -2
  151. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js.map +1 -1
  152. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  153. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  154. package/package.json +6 -6
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI } from "../../types.ts";
2
- export type { BashTimeoutDefaults, BashToolInputLike, EffectiveBashTimeouts } from "./timeout.ts";
3
- export { applyBashTimeout, BASH_DEFAULT_TIMEOUT_SECONDS, BASH_MAX_TIMEOUT_SECONDS, buildBashTimeoutPrompt, resolveBashTimeoutDefaults, resolveEffectiveBashTimeouts, } from "./timeout.ts";
2
+ export type { BashTimeoutDefaults, BashToolInputLike } from "./timeout.ts";
3
+ export { applyBashTimeout, BASH_DEFAULT_TIMEOUT_SECONDS, BASH_MAX_TIMEOUT_SECONDS, buildBashTimeoutPrompt, resolveBashTimeoutDefaults, } from "./timeout.ts";
4
4
  export default function bashTimeoutExtension(pi: ExtensionAPI): void;
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAWrE,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EACN,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,4BAA4B,GAC5B,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA+BnE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAWrE,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EACN,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,GAC1B,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA2BnE"}
@@ -1,36 +1,33 @@
1
1
  import { isAnthropicBashEnabled } from "../anthropic-bash/index.js";
2
- import { applyBashTimeout, buildBashTimeoutPrompt, resolveBashTimeoutDefaults, resolveEffectiveBashTimeouts, } from "./timeout.js";
3
- export { applyBashTimeout, BASH_DEFAULT_TIMEOUT_SECONDS, BASH_MAX_TIMEOUT_SECONDS, buildBashTimeoutPrompt, resolveBashTimeoutDefaults, resolveEffectiveBashTimeouts, } from "./timeout.js";
2
+ import { resolveForegroundWindowSeconds } from "../terminal/tools/foreground-window.js";
3
+ import { applyBashTimeout, buildBashTimeoutPrompt, resolveBashTimeoutDefaults, } from "./timeout.js";
4
+ export { applyBashTimeout, BASH_DEFAULT_TIMEOUT_SECONDS, BASH_MAX_TIMEOUT_SECONDS, buildBashTimeoutPrompt, resolveBashTimeoutDefaults, } from "./timeout.js";
4
5
  export default function bashTimeoutExtension(pi) {
5
6
  const env = typeof process !== "undefined" ? process.env : {};
6
- const baseDefaults = resolveBashTimeoutDefaults(env);
7
- let effective = resolveEffectiveBashTimeouts(baseDefaults, undefined);
8
- /**
9
- * Native Anthropic bash replaces the PTY `bash` tool, and the terminal
10
- * extension steps aside with it — so nothing implements the cache-deadline
11
- * detach. Advertising a cache ceiling there would promise behavior that
12
- * cannot happen, so the budget only applies while the PTY tool is live.
13
- */
14
- const resolveBudget = (ctx) => {
15
- if (isAnthropicBashEnabled() && ctx?.model?.api === "anthropic-messages")
16
- return undefined;
17
- return ctx?.getPromptCacheSafeWaitSeconds?.();
18
- };
19
- pi.on("tool_call", async (event, ctx) => {
7
+ const defaults = resolveBashTimeoutDefaults(env);
8
+ pi.on("tool_call", async (event) => {
20
9
  if (event.toolName !== "bash")
21
10
  return;
22
- effective = resolveEffectiveBashTimeouts(baseDefaults, resolveBudget(ctx));
23
11
  const input = event.input;
24
- const updated = applyBashTimeout(input, effective);
12
+ const updated = applyBashTimeout(input, defaults);
25
13
  if (updated !== input) {
26
14
  const timeout = updated.timeout;
27
15
  if (timeout !== undefined)
28
16
  input.timeout = timeout;
29
17
  }
30
18
  });
19
+ /**
20
+ * Native Anthropic bash replaces the PTY `bash` tool and the terminal
21
+ * extension steps aside with it, so nothing implements the auto-detach the
22
+ * policy would otherwise advertise.
23
+ */
24
+ const resolveWindow = (ctx) => {
25
+ if (isAnthropicBashEnabled() && ctx?.model?.api === "anthropic-messages")
26
+ return undefined;
27
+ return resolveForegroundWindowSeconds(env);
28
+ };
31
29
  pi.on("before_agent_start", async (event, ctx) => {
32
- effective = resolveEffectiveBashTimeouts(baseDefaults, resolveBudget(ctx));
33
- return { systemPrompt: `${event.systemPrompt}${buildBashTimeoutPrompt(effective)}` };
30
+ return { systemPrompt: `${event.systemPrompt}${buildBashTimeoutPrompt(defaults, resolveWindow(ctx))}` };
34
31
  });
35
32
  }
36
33
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,OAAO,EACN,gBAAgB,EAEhB,sBAAsB,EACtB,0BAA0B,EAC1B,4BAA4B,GAC5B,MAAM,cAAc,CAAC;AAGtB,OAAO,EACN,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,4BAA4B,GAC5B,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAgB;IAC5D,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,MAAM,YAAY,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAC;IACrD,IAAI,SAAS,GAAG,4BAA4B,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IAEtE;;;;;OAKG;IACH,MAAM,aAAa,GAAG,CAAC,GAAiC,EAAsB,EAAE;QAC/E,IAAI,sBAAsB,EAAE,IAAI,GAAG,EAAE,KAAK,EAAE,GAAG,KAAK,oBAAoB;YAAE,OAAO,SAAS,CAAC;QAC3F,OAAO,GAAG,EAAE,6BAA6B,EAAE,EAAE,CAAC;IAC/C,CAAC,CAAC;IAEF,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM;YAAE,OAAO;QACtC,SAAS,GAAG,4BAA4B,CAAC,YAAY,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3E,MAAM,KAAK,GAAG,KAAK,CAAC,KAA0B,CAAC;QAC/C,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QACnD,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;YAChC,IAAI,OAAO,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;QACpD,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,oBAAoB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAChD,SAAS,GAAG,4BAA4B,CAAC,YAAY,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3E,OAAO,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC,YAAY,GAAG,sBAAsB,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;IACtF,CAAC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { isAnthropicBashEnabled } from \"../anthropic-bash/index.ts\";\n\nimport {\n\tapplyBashTimeout,\n\ttype BashToolInputLike,\n\tbuildBashTimeoutPrompt,\n\tresolveBashTimeoutDefaults,\n\tresolveEffectiveBashTimeouts,\n} from \"./timeout.ts\";\n\nexport type { BashTimeoutDefaults, BashToolInputLike, EffectiveBashTimeouts } from \"./timeout.ts\";\nexport {\n\tapplyBashTimeout,\n\tBASH_DEFAULT_TIMEOUT_SECONDS,\n\tBASH_MAX_TIMEOUT_SECONDS,\n\tbuildBashTimeoutPrompt,\n\tresolveBashTimeoutDefaults,\n\tresolveEffectiveBashTimeouts,\n} from \"./timeout.ts\";\n\nexport default function bashTimeoutExtension(pi: ExtensionAPI): void {\n\tconst env = typeof process !== \"undefined\" ? process.env : {};\n\tconst baseDefaults = resolveBashTimeoutDefaults(env);\n\tlet effective = resolveEffectiveBashTimeouts(baseDefaults, undefined);\n\n\t/**\n\t * Native Anthropic bash replaces the PTY `bash` tool, and the terminal\n\t * extension steps aside with it so nothing implements the cache-deadline\n\t * detach. Advertising a cache ceiling there would promise behavior that\n\t * cannot happen, so the budget only applies while the PTY tool is live.\n\t */\n\tconst resolveBudget = (ctx: ExtensionContext | undefined): number | undefined => {\n\t\tif (isAnthropicBashEnabled() && ctx?.model?.api === \"anthropic-messages\") return undefined;\n\t\treturn ctx?.getPromptCacheSafeWaitSeconds?.();\n\t};\n\n\tpi.on(\"tool_call\", async (event, ctx) => {\n\t\tif (event.toolName !== \"bash\") return;\n\t\teffective = resolveEffectiveBashTimeouts(baseDefaults, resolveBudget(ctx));\n\t\tconst input = event.input as BashToolInputLike;\n\t\tconst updated = applyBashTimeout(input, effective);\n\t\tif (updated !== input) {\n\t\t\tconst timeout = updated.timeout;\n\t\t\tif (timeout !== undefined) input.timeout = timeout;\n\t\t}\n\t});\n\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\teffective = resolveEffectiveBashTimeouts(baseDefaults, resolveBudget(ctx));\n\t\treturn { systemPrompt: `${event.systemPrompt}${buildBashTimeoutPrompt(effective)}` };\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,8BAA8B,EAAE,MAAM,wCAAwC,CAAC;AAExF,OAAO,EACN,gBAAgB,EAEhB,sBAAsB,EACtB,0BAA0B,GAC1B,MAAM,cAAc,CAAC;AAGtB,OAAO,EACN,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,GAC1B,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAgB;IAC5D,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,MAAM,QAAQ,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAC;IAEjD,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAClC,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM;YAAE,OAAO;QACtC,MAAM,KAAK,GAAG,KAAK,CAAC,KAA0B,CAAC;QAC/C,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAClD,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;YAChC,IAAI,OAAO,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;QACpD,CAAC;IACF,CAAC,CAAC,CAAC;IAEH;;;;OAIG;IACH,MAAM,aAAa,GAAG,CAAC,GAAiC,EAAsB,EAAE;QAC/E,IAAI,sBAAsB,EAAE,IAAI,GAAG,EAAE,KAAK,EAAE,GAAG,KAAK,oBAAoB;YAAE,OAAO,SAAS,CAAC;QAC3F,OAAO,8BAA8B,CAAC,GAAG,CAAC,CAAC;IAC5C,CAAC,CAAC;IAEF,EAAE,CAAC,EAAE,CAAC,oBAAoB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAChD,OAAO,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC,YAAY,GAAG,sBAAsB,CAAC,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC;IACzG,CAAC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { isAnthropicBashEnabled } from \"../anthropic-bash/index.ts\";\nimport { resolveForegroundWindowSeconds } from \"../terminal/tools/foreground-window.ts\";\n\nimport {\n\tapplyBashTimeout,\n\ttype BashToolInputLike,\n\tbuildBashTimeoutPrompt,\n\tresolveBashTimeoutDefaults,\n} from \"./timeout.ts\";\n\nexport type { BashTimeoutDefaults, BashToolInputLike } from \"./timeout.ts\";\nexport {\n\tapplyBashTimeout,\n\tBASH_DEFAULT_TIMEOUT_SECONDS,\n\tBASH_MAX_TIMEOUT_SECONDS,\n\tbuildBashTimeoutPrompt,\n\tresolveBashTimeoutDefaults,\n} from \"./timeout.ts\";\n\nexport default function bashTimeoutExtension(pi: ExtensionAPI): void {\n\tconst env = typeof process !== \"undefined\" ? process.env : {};\n\tconst defaults = resolveBashTimeoutDefaults(env);\n\n\tpi.on(\"tool_call\", async (event) => {\n\t\tif (event.toolName !== \"bash\") return;\n\t\tconst input = event.input as BashToolInputLike;\n\t\tconst updated = applyBashTimeout(input, defaults);\n\t\tif (updated !== input) {\n\t\t\tconst timeout = updated.timeout;\n\t\t\tif (timeout !== undefined) input.timeout = timeout;\n\t\t}\n\t});\n\n\t/**\n\t * Native Anthropic bash replaces the PTY `bash` tool and the terminal\n\t * extension steps aside with it, so nothing implements the auto-detach the\n\t * policy would otherwise advertise.\n\t */\n\tconst resolveWindow = (ctx: ExtensionContext | undefined): number | undefined => {\n\t\tif (isAnthropicBashEnabled() && ctx?.model?.api === \"anthropic-messages\") return undefined;\n\t\treturn resolveForegroundWindowSeconds(env);\n\t};\n\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\treturn { systemPrompt: `${event.systemPrompt}${buildBashTimeoutPrompt(defaults, resolveWindow(ctx))}` };\n\t});\n}\n"]}
@@ -1,5 +1,5 @@
1
- export declare const BASH_DEFAULT_TIMEOUT_SECONDS = 120;
2
- export declare const BASH_MAX_TIMEOUT_SECONDS = 600;
1
+ export declare const BASH_DEFAULT_TIMEOUT_SECONDS = 1800;
2
+ export declare const BASH_MAX_TIMEOUT_SECONDS = 1800;
3
3
  export interface BashTimeoutDefaults {
4
4
  defaultSeconds: number;
5
5
  maxSeconds: number;
@@ -12,17 +12,12 @@ export interface BashToolInputLike {
12
12
  [key: string]: unknown;
13
13
  }
14
14
  export declare function applyBashTimeout<TInput extends BashToolInputLike>(input: TInput, defaults: BashTimeoutDefaults): TInput;
15
- export interface EffectiveBashTimeouts extends BashTimeoutDefaults {
16
- cacheCapped?: boolean;
17
- }
18
15
  /**
19
- * Lower the recommended maximum to the prompt-cache safe-wait budget, so the
20
- * model is never told to block past the point where the cache expires. The two
21
- * `min()` expressions are the complete policy: a budget below the injected
22
- * default pulls that default down with it, keeping the prompt, the injected
23
- * value, and the terminal detach deadline consistent.
16
+ * `foregroundWindowSeconds` is `undefined` when no PTY bash tool is live (native
17
+ * Anthropic bash replaces it). Nothing implements auto-detach then, so the
18
+ * policy must not promise it a prompt that describes impossible behavior is
19
+ * worse than a silent one.
24
20
  */
25
- export declare function resolveEffectiveBashTimeouts(defaults: BashTimeoutDefaults, safeWaitSeconds: number | undefined): Required<EffectiveBashTimeouts>;
26
- export declare function buildBashTimeoutPrompt(defaults: EffectiveBashTimeouts): string;
21
+ export declare function buildBashTimeoutPrompt(defaults: BashTimeoutDefaults, foregroundWindowSeconds?: number): string;
27
22
  export {};
28
23
  //# sourceMappingURL=timeout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/timeout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,MAAM,WAAW,mBAAmB;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AASlD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,CAM5E;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,iBAAiB,EAChE,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,mBAAmB,GAC3B,MAAM,CAMR;AAED,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IACjE,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC3C,QAAQ,EAAE,mBAAmB,EAC7B,eAAe,EAAE,MAAM,GAAG,SAAS,GACjC,QAAQ,CAAC,qBAAqB,CAAC,CAMjC;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAM9E"}
1
+ {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/timeout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,MAAM,WAAW,mBAAmB;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AASlD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,CAM5E;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,iBAAiB,EAChE,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,mBAAmB,GAC3B,MAAM,CAMR;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,uBAAuB,CAAC,EAAE,MAAM,GAAG,MAAM,CAO9G"}
@@ -1,5 +1,5 @@
1
- export const BASH_DEFAULT_TIMEOUT_SECONDS = 120;
2
- export const BASH_MAX_TIMEOUT_SECONDS = 600;
1
+ export const BASH_DEFAULT_TIMEOUT_SECONDS = 1800;
2
+ export const BASH_MAX_TIMEOUT_SECONDS = 1800;
3
3
  function parsePositiveInt(value) {
4
4
  if (!value)
5
5
  return undefined;
@@ -23,24 +23,16 @@ export function applyBashTimeout(input, defaults) {
23
23
  return input;
24
24
  }
25
25
  /**
26
- * Lower the recommended maximum to the prompt-cache safe-wait budget, so the
27
- * model is never told to block past the point where the cache expires. The two
28
- * `min()` expressions are the complete policy: a budget below the injected
29
- * default pulls that default down with it, keeping the prompt, the injected
30
- * value, and the terminal detach deadline consistent.
26
+ * `foregroundWindowSeconds` is `undefined` when no PTY bash tool is live (native
27
+ * Anthropic bash replaces it). Nothing implements auto-detach then, so the
28
+ * policy must not promise it a prompt that describes impossible behavior is
29
+ * worse than a silent one.
31
30
  */
32
- export function resolveEffectiveBashTimeouts(defaults, safeWaitSeconds) {
33
- if (safeWaitSeconds === undefined || safeWaitSeconds >= defaults.maxSeconds) {
34
- return { ...defaults, cacheCapped: false };
35
- }
36
- const maxSeconds = Math.min(defaults.maxSeconds, safeWaitSeconds);
37
- return { defaultSeconds: Math.min(defaults.defaultSeconds, maxSeconds), maxSeconds, cacheCapped: true };
38
- }
39
- export function buildBashTimeoutPrompt(defaults) {
31
+ export function buildBashTimeoutPrompt(defaults, foregroundWindowSeconds) {
40
32
  const minutes = (seconds) => (seconds % 60 === 0 ? `${seconds / 60} min` : `${seconds}s`);
41
- const maxReason = defaults.cacheCapped
42
- ? ` This ceiling is the prompt cache lifetime of the active model minus a safety buffer: blocking past it expires the cache and forces a full re-read on the next request. A foreground command still running at that point is handed to a background session alive instead of being killed; those sessions stay live, so stop the ones you no longer need with \`kill_bash\`.`
43
- : "";
44
- return `\n## Bash Tool Timeout Policy\n\nThe \`bash\` tool enforces timeouts even when you omit the \`timeout\` parameter:\n\n- Default timeout: ${defaults.defaultSeconds}s (${minutes(defaults.defaultSeconds)}). Applied automatically when you do not set \`timeout\`.\n- Recommended maximum timeout: ${defaults.maxSeconds}s (${minutes(defaults.maxSeconds)}).${maxReason} Explicit \`timeout\` values are preserved because different hosts may use different timeout units.\n- For long-running commands (builds, installs, test suites), set an explicit \`timeout\` that fits the workload. Do not assume commands run forever.\n- For commands that legitimately need to run beyond the recommended maximum, start them with \`run_in_background: true\` and watch the decisive output with \`monitor\` instead of raising the timeout.\n`;
33
+ const detachRules = foregroundWindowSeconds === undefined
34
+ ? ""
35
+ : `\n- Foreground blocking stops at the ~${foregroundWindowSeconds}s window. A command still running then auto-detaches alive to a background session with a \`bash_id\` and keeps running until it exits, hits the kill deadline, or is stopped with \`kill_bash\`.\n- Completion arrives automatically as a notification carrying the exit status and output tail, so end your turn rather than poll. Use \`bash_output\` only for a midpoint peek.`;
36
+ return `\n## Bash Tool Timeout Policy\n\nThe \`bash\` tool's \`timeout\` parameter is the process kill deadline, not how long you wait for output: the command is killed when it reaches the deadline.\n\n- Default timeout: ${defaults.defaultSeconds}s (${minutes(defaults.defaultSeconds)}). Applied automatically when you do not set \`timeout\`.\n- Recommended maximum timeout: ${defaults.maxSeconds}s (${minutes(defaults.maxSeconds)}). Explicit \`timeout\` values are preserved because different hosts may use different timeout units.${detachRules}\n- Waiting on an observable condition (a log line, a CI check, a server coming up) belongs to \`monitor({command, filter})\`, never a foreground \`sleep\` or poll loop.\n- Sessions started with \`run_in_background: true\` ignore \`timeout\` and live until exit or \`kill_bash\`.\n`;
45
37
  }
46
38
  //# sourceMappingURL=timeout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/timeout.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAChD,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAS5C,SAAS,gBAAgB,CAAC,KAAyB;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9D,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,GAAY;IACtD,MAAM,EAAE,+BAA+B,EAAE,cAAc,EAAE,2BAA2B,EAAE,UAAU,EAAE,GAAG,GAAG,CAAC;IACzG,MAAM,cAAc,GAAG,gBAAgB,CAAC,cAAc,CAAC,IAAI,4BAA4B,CAAC;IACxF,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,IAAI,wBAAwB,CAAC;IACxE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACpD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC;AACvC,CAAC;AAQD,MAAM,UAAU,gBAAgB,CAC/B,KAAa,EACb,QAA6B;IAE7B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,cAAc,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAMD;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC3C,QAA6B,EAC7B,eAAmC;IAEnC,IAAI,eAAe,KAAK,SAAS,IAAI,eAAe,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC7E,OAAO,EAAE,GAAG,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IAClE,OAAO,EAAE,cAAc,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,EAAE,UAAU,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AACzG,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAA+B;IACrE,MAAM,OAAO,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,CAAC,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;IAC1G,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW;QACrC,CAAC,CAAC,6WAA6W;QAC/W,CAAC,CAAC,EAAE,CAAC;IACN,OAAO,4IAA4I,QAAQ,CAAC,cAAc,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,6FAA6F,QAAQ,CAAC,UAAU,MAAM,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,SAAS,scAAsc,CAAC;AACxzB,CAAC","sourcesContent":["export const BASH_DEFAULT_TIMEOUT_SECONDS = 120;\nexport const BASH_MAX_TIMEOUT_SECONDS = 600;\n\nexport interface BashTimeoutDefaults {\n\tdefaultSeconds: number;\n\tmaxSeconds: number;\n}\n\ntype EnvLike = Record<string, string | undefined>;\n\nfunction parsePositiveInt(value: string | undefined): number | undefined {\n\tif (!value) return undefined;\n\tconst parsed = Number.parseInt(value, 10);\n\tif (!Number.isFinite(parsed) || parsed <= 0) return undefined;\n\treturn parsed;\n}\n\nexport function resolveBashTimeoutDefaults(env: EnvLike): BashTimeoutDefaults {\n\tconst { PI_BASH_DEFAULT_TIMEOUT_SECONDS: defaultTimeout, PI_BASH_MAX_TIMEOUT_SECONDS: maxTimeout } = env;\n\tconst defaultSeconds = parsePositiveInt(defaultTimeout) ?? BASH_DEFAULT_TIMEOUT_SECONDS;\n\tconst rawMax = parsePositiveInt(maxTimeout) ?? BASH_MAX_TIMEOUT_SECONDS;\n\tconst maxSeconds = Math.max(rawMax, defaultSeconds);\n\treturn { defaultSeconds, maxSeconds };\n}\n\nexport interface BashToolInputLike {\n\tcommand: string;\n\ttimeout?: number;\n\t[key: string]: unknown;\n}\n\nexport function applyBashTimeout<TInput extends BashToolInputLike>(\n\tinput: TInput,\n\tdefaults: BashTimeoutDefaults,\n): TInput {\n\tconst current = input.timeout;\n\tif (current === undefined || current <= 0) {\n\t\treturn { ...input, timeout: defaults.defaultSeconds };\n\t}\n\treturn input;\n}\n\nexport interface EffectiveBashTimeouts extends BashTimeoutDefaults {\n\tcacheCapped?: boolean;\n}\n\n/**\n * Lower the recommended maximum to the prompt-cache safe-wait budget, so the\n * model is never told to block past the point where the cache expires. The two\n * `min()` expressions are the complete policy: a budget below the injected\n * default pulls that default down with it, keeping the prompt, the injected\n * value, and the terminal detach deadline consistent.\n */\nexport function resolveEffectiveBashTimeouts(\n\tdefaults: BashTimeoutDefaults,\n\tsafeWaitSeconds: number | undefined,\n): Required<EffectiveBashTimeouts> {\n\tif (safeWaitSeconds === undefined || safeWaitSeconds >= defaults.maxSeconds) {\n\t\treturn { ...defaults, cacheCapped: false };\n\t}\n\tconst maxSeconds = Math.min(defaults.maxSeconds, safeWaitSeconds);\n\treturn { defaultSeconds: Math.min(defaults.defaultSeconds, maxSeconds), maxSeconds, cacheCapped: true };\n}\n\nexport function buildBashTimeoutPrompt(defaults: EffectiveBashTimeouts): string {\n\tconst minutes = (seconds: number): string => (seconds % 60 === 0 ? `${seconds / 60} min` : `${seconds}s`);\n\tconst maxReason = defaults.cacheCapped\n\t\t? ` This ceiling is the prompt cache lifetime of the active model minus a safety buffer: blocking past it expires the cache and forces a full re-read on the next request. A foreground command still running at that point is handed to a background session alive instead of being killed; those sessions stay live, so stop the ones you no longer need with \\`kill_bash\\`.`\n\t\t: \"\";\n\treturn `\\n## Bash Tool Timeout Policy\\n\\nThe \\`bash\\` tool enforces timeouts even when you omit the \\`timeout\\` parameter:\\n\\n- Default timeout: ${defaults.defaultSeconds}s (${minutes(defaults.defaultSeconds)}). Applied automatically when you do not set \\`timeout\\`.\\n- Recommended maximum timeout: ${defaults.maxSeconds}s (${minutes(defaults.maxSeconds)}).${maxReason} Explicit \\`timeout\\` values are preserved because different hosts may use different timeout units.\\n- For long-running commands (builds, installs, test suites), set an explicit \\`timeout\\` that fits the workload. Do not assume commands run forever.\\n- For commands that legitimately need to run beyond the recommended maximum, start them with \\`run_in_background: true\\` and watch the decisive output with \\`monitor\\` instead of raising the timeout.\\n`;\n}\n"]}
1
+ {"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/timeout.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AACjD,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAS7C,SAAS,gBAAgB,CAAC,KAAyB;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9D,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,GAAY;IACtD,MAAM,EAAE,+BAA+B,EAAE,cAAc,EAAE,2BAA2B,EAAE,UAAU,EAAE,GAAG,GAAG,CAAC;IACzG,MAAM,cAAc,GAAG,gBAAgB,CAAC,cAAc,CAAC,IAAI,4BAA4B,CAAC;IACxF,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,IAAI,wBAAwB,CAAC;IACxE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACpD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC;AACvC,CAAC;AAQD,MAAM,UAAU,gBAAgB,CAC/B,KAAa,EACb,QAA6B;IAE7B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,cAAc,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAA6B,EAAE,uBAAgC;IACrG,MAAM,OAAO,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,CAAC,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;IAC1G,MAAM,WAAW,GAChB,uBAAuB,KAAK,SAAS;QACpC,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,yCAAyC,uBAAuB,oXAAoX,CAAC;IACzb,OAAO,wNAAwN,QAAQ,CAAC,cAAc,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,6FAA6F,QAAQ,CAAC,UAAU,MAAM,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,wGAAwG,WAAW,2RAA2R,CAAC;AAC9zB,CAAC","sourcesContent":["export const BASH_DEFAULT_TIMEOUT_SECONDS = 1800;\nexport const BASH_MAX_TIMEOUT_SECONDS = 1800;\n\nexport interface BashTimeoutDefaults {\n\tdefaultSeconds: number;\n\tmaxSeconds: number;\n}\n\ntype EnvLike = Record<string, string | undefined>;\n\nfunction parsePositiveInt(value: string | undefined): number | undefined {\n\tif (!value) return undefined;\n\tconst parsed = Number.parseInt(value, 10);\n\tif (!Number.isFinite(parsed) || parsed <= 0) return undefined;\n\treturn parsed;\n}\n\nexport function resolveBashTimeoutDefaults(env: EnvLike): BashTimeoutDefaults {\n\tconst { PI_BASH_DEFAULT_TIMEOUT_SECONDS: defaultTimeout, PI_BASH_MAX_TIMEOUT_SECONDS: maxTimeout } = env;\n\tconst defaultSeconds = parsePositiveInt(defaultTimeout) ?? BASH_DEFAULT_TIMEOUT_SECONDS;\n\tconst rawMax = parsePositiveInt(maxTimeout) ?? BASH_MAX_TIMEOUT_SECONDS;\n\tconst maxSeconds = Math.max(rawMax, defaultSeconds);\n\treturn { defaultSeconds, maxSeconds };\n}\n\nexport interface BashToolInputLike {\n\tcommand: string;\n\ttimeout?: number;\n\t[key: string]: unknown;\n}\n\nexport function applyBashTimeout<TInput extends BashToolInputLike>(\n\tinput: TInput,\n\tdefaults: BashTimeoutDefaults,\n): TInput {\n\tconst current = input.timeout;\n\tif (current === undefined || current <= 0) {\n\t\treturn { ...input, timeout: defaults.defaultSeconds };\n\t}\n\treturn input;\n}\n\n/**\n * `foregroundWindowSeconds` is `undefined` when no PTY bash tool is live (native\n * Anthropic bash replaces it). Nothing implements auto-detach then, so the\n * policy must not promise it a prompt that describes impossible behavior is\n * worse than a silent one.\n */\nexport function buildBashTimeoutPrompt(defaults: BashTimeoutDefaults, foregroundWindowSeconds?: number): string {\n\tconst minutes = (seconds: number): string => (seconds % 60 === 0 ? `${seconds / 60} min` : `${seconds}s`);\n\tconst detachRules =\n\t\tforegroundWindowSeconds === undefined\n\t\t\t? \"\"\n\t\t\t: `\\n- Foreground blocking stops at the ~${foregroundWindowSeconds}s window. A command still running then auto-detaches alive to a background session with a \\`bash_id\\` and keeps running until it exits, hits the kill deadline, or is stopped with \\`kill_bash\\`.\\n- Completion arrives automatically as a notification carrying the exit status and output tail, so end your turn rather than poll. Use \\`bash_output\\` only for a midpoint peek.`;\n\treturn `\\n## Bash Tool Timeout Policy\\n\\nThe \\`bash\\` tool's \\`timeout\\` parameter is the process kill deadline, not how long you wait for output: the command is killed when it reaches the deadline.\\n\\n- Default timeout: ${defaults.defaultSeconds}s (${minutes(defaults.defaultSeconds)}). Applied automatically when you do not set \\`timeout\\`.\\n- Recommended maximum timeout: ${defaults.maxSeconds}s (${minutes(defaults.maxSeconds)}). Explicit \\`timeout\\` values are preserved because different hosts may use different timeout units.${detachRules}\\n- Waiting on an observable condition (a log line, a CI check, a server coming up) belongs to \\`monitor({command, filter})\\`, never a foreground \\`sleep\\` or poll loop.\\n- Sessions started with \\`run_in_background: true\\` ignore \\`timeout\\` and live until exit or \\`kill_bash\\`.\\n`;\n}\n"]}
@@ -25,6 +25,8 @@ export interface GoalCacheWarmupEntryData {
25
25
  /** Actual wait in milliseconds; present on the `resumed` phase only. */
26
26
  readonly waitedMs?: number;
27
27
  readonly activeMonitorCount: number;
28
+ /** Full source-keyed snapshot; absent on entries written before resumption channels were generalized. */
29
+ readonly channelCounts?: Readonly<Record<string, number>>;
28
30
  readonly cache?: GoalCacheWarmMetrics;
29
31
  }
30
32
  export declare function estimateCacheWarmMetrics(model: Model<Api> | undefined, env: NodeJS.ProcessEnv, lastTurnUsage: Pick<TokenUsageSnapshot, "cacheRead" | "cacheWrite"> | undefined): GoalCacheWarmMetrics | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"cache-warm.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAe,MAAM,uBAAuB,CAAC;AAErE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,4EAA4E;AAC5E,eAAO,MAAM,4BAA4B,sBAAsB,CAAC;AAEhE,4EAA4E;AAC5E,MAAM,WAAW,oBAAoB;IACpC,mEAAmE;IACnE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,qGAAqG;IACrG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC;CACtC;AAID,wBAAgB,wBAAwB,CACvC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EAC7B,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,aAAa,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,GAAG,YAAY,CAAC,GAAG,SAAS,GAC7E,oBAAoB,GAAG,SAAS,CAalC;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CASrD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAItD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD"}
1
+ {"version":3,"file":"cache-warm.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAe,MAAM,uBAAuB,CAAC;AAErE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,4EAA4E;AAC5E,eAAO,MAAM,4BAA4B,sBAAsB,CAAC;AAEhE,4EAA4E;AAC5E,MAAM,WAAW,oBAAoB;IACpC,mEAAmE;IACnE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,qGAAqG;IACrG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,yGAAyG;IACzG,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC;CACtC;AAID,wBAAgB,wBAAwB,CACvC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EAC7B,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,aAAa,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,GAAG,YAAY,CAAC,GAAG,SAAS,GAC7E,oBAAoB,GAAG,SAAS,CAalC;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CASrD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAItD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"cache-warm.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model, ProviderEnv } from \"@earendil-works/pi-ai\";\nimport { resolvePromptCacheTtlSeconds } from \"@earendil-works/pi-ai\";\nimport type { TokenUsageSnapshot } from \"./types.ts\";\n\n/** Custom session-entry type carrying the cache-warm continuation story. */\nexport const GOAL_CACHE_WARMUP_ENTRY_TYPE = \"goal-cache-warmup\";\n\n/** Cache context captured when a monitor-wait continuation is scheduled. */\nexport interface GoalCacheWarmMetrics {\n\t/** Prompt-cache TTL of the active model in seconds, when known. */\n\treadonly ttlSeconds?: number;\n\t/** Tokens sitting warm in the provider prompt cache after the last turn. */\n\treadonly cachedTokens: number;\n\t/** Estimated USD saved by re-reading those tokens from cache instead of paying a cold input read. */\n\treadonly estimatedSavedUsd?: number;\n}\n\nexport type GoalCacheWarmupPhase = \"scheduled\" | \"resumed\";\n\n/**\n * Durable payload appended as a `goal-cache-warmup` custom entry and carried by\n * the `goal_continuation_scheduled` / `goal_continuation_resumed` pi-events, so\n * external consumers (for example omo-desktop-app) can render the story later.\n */\nexport interface GoalCacheWarmupEntryData {\n\treadonly phase: GoalCacheWarmupPhase;\n\treadonly goalId: string;\n\t/** Planned continuation delay in milliseconds. */\n\treadonly delayMs: number;\n\t/** Actual wait in milliseconds; present on the `resumed` phase only. */\n\treadonly waitedMs?: number;\n\treadonly activeMonitorCount: number;\n\treadonly cache?: GoalCacheWarmMetrics;\n}\n\nconst TOKENS_PER_PRICE_UNIT = 1_000_000;\n\nexport function estimateCacheWarmMetrics(\n\tmodel: Model<Api> | undefined,\n\tenv: NodeJS.ProcessEnv,\n\tlastTurnUsage: Pick<TokenUsageSnapshot, \"cacheRead\" | \"cacheWrite\"> | undefined,\n): GoalCacheWarmMetrics | undefined {\n\tconst cachedTokens = clampTokens(lastTurnUsage?.cacheRead) + clampTokens(lastTurnUsage?.cacheWrite);\n\tconst ttlSeconds = model === undefined ? undefined : resolvePromptCacheTtlSeconds(model, toProviderEnv(env));\n\tif (ttlSeconds === undefined && cachedTokens === 0) return undefined;\n\tconst estimatedSavedUsd =\n\t\tmodel !== undefined && cachedTokens > 0\n\t\t\t? (Math.max(0, model.cost.input - model.cost.cacheRead) * cachedTokens) / TOKENS_PER_PRICE_UNIT\n\t\t\t: undefined;\n\treturn {\n\t\tcachedTokens,\n\t\t...(ttlSeconds !== undefined ? { ttlSeconds } : {}),\n\t\t...(estimatedSavedUsd !== undefined ? { estimatedSavedUsd } : {}),\n\t};\n}\n\nexport function formatWarmTokenCount(tokens: number): string {\n\tif (tokens >= 1_000_000) return `${trimTrailingZero((tokens / 1_000_000).toFixed(1))}M`;\n\tif (tokens >= 1000) return `${trimTrailingZero((tokens / 1000).toFixed(1))}K`;\n\treturn String(Math.max(0, Math.trunc(tokens)));\n}\n\nexport function formatWakeDuration(ms: number): string {\n\tconst seconds = Math.round(ms / 1000);\n\tif (seconds < 60) return `${seconds}s`;\n\tconst minutes = Math.floor(seconds / 60);\n\tconst restSeconds = seconds % 60;\n\tif (minutes < 60) return restSeconds === 0 ? `${minutes}m` : `${minutes}m ${restSeconds}s`;\n\tconst hours = Math.floor(minutes / 60);\n\tconst restMinutes = minutes % 60;\n\treturn restMinutes === 0 ? `${hours}h` : `${hours}h ${restMinutes}m`;\n}\n\nexport function formatCacheTtl(seconds: number): string {\n\tif (seconds % 3600 === 0) return `${seconds / 3600}h`;\n\tif (seconds % 60 === 0) return `${seconds / 60}m`;\n\treturn `${seconds}s`;\n}\n\nexport function formatSavedUsd(value: number): string {\n\tif (value < 0.0005) return \"<$0.001\";\n\tif (value < 1) return `$${value.toFixed(3)}`;\n\treturn `$${value.toFixed(2)}`;\n}\n\nfunction trimTrailingZero(value: string): string {\n\treturn value.endsWith(\".0\") ? value.slice(0, -2) : value;\n}\n\nfunction clampTokens(value: number | undefined): number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;\n}\n\nfunction toProviderEnv(env: NodeJS.ProcessEnv): ProviderEnv {\n\tconst resolved: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tif (value !== undefined) resolved[key] = value;\n\t}\n\treturn resolved;\n}\n"]}
1
+ {"version":3,"file":"cache-warm.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model, ProviderEnv } from \"@earendil-works/pi-ai\";\nimport { resolvePromptCacheTtlSeconds } from \"@earendil-works/pi-ai\";\nimport type { TokenUsageSnapshot } from \"./types.ts\";\n\n/** Custom session-entry type carrying the cache-warm continuation story. */\nexport const GOAL_CACHE_WARMUP_ENTRY_TYPE = \"goal-cache-warmup\";\n\n/** Cache context captured when a monitor-wait continuation is scheduled. */\nexport interface GoalCacheWarmMetrics {\n\t/** Prompt-cache TTL of the active model in seconds, when known. */\n\treadonly ttlSeconds?: number;\n\t/** Tokens sitting warm in the provider prompt cache after the last turn. */\n\treadonly cachedTokens: number;\n\t/** Estimated USD saved by re-reading those tokens from cache instead of paying a cold input read. */\n\treadonly estimatedSavedUsd?: number;\n}\n\nexport type GoalCacheWarmupPhase = \"scheduled\" | \"resumed\";\n\n/**\n * Durable payload appended as a `goal-cache-warmup` custom entry and carried by\n * the `goal_continuation_scheduled` / `goal_continuation_resumed` pi-events, so\n * external consumers (for example omo-desktop-app) can render the story later.\n */\nexport interface GoalCacheWarmupEntryData {\n\treadonly phase: GoalCacheWarmupPhase;\n\treadonly goalId: string;\n\t/** Planned continuation delay in milliseconds. */\n\treadonly delayMs: number;\n\t/** Actual wait in milliseconds; present on the `resumed` phase only. */\n\treadonly waitedMs?: number;\n\treadonly activeMonitorCount: number;\n\t/** Full source-keyed snapshot; absent on entries written before resumption channels were generalized. */\n\treadonly channelCounts?: Readonly<Record<string, number>>;\n\treadonly cache?: GoalCacheWarmMetrics;\n}\n\nconst TOKENS_PER_PRICE_UNIT = 1_000_000;\n\nexport function estimateCacheWarmMetrics(\n\tmodel: Model<Api> | undefined,\n\tenv: NodeJS.ProcessEnv,\n\tlastTurnUsage: Pick<TokenUsageSnapshot, \"cacheRead\" | \"cacheWrite\"> | undefined,\n): GoalCacheWarmMetrics | undefined {\n\tconst cachedTokens = clampTokens(lastTurnUsage?.cacheRead) + clampTokens(lastTurnUsage?.cacheWrite);\n\tconst ttlSeconds = model === undefined ? undefined : resolvePromptCacheTtlSeconds(model, toProviderEnv(env));\n\tif (ttlSeconds === undefined && cachedTokens === 0) return undefined;\n\tconst estimatedSavedUsd =\n\t\tmodel !== undefined && cachedTokens > 0\n\t\t\t? (Math.max(0, model.cost.input - model.cost.cacheRead) * cachedTokens) / TOKENS_PER_PRICE_UNIT\n\t\t\t: undefined;\n\treturn {\n\t\tcachedTokens,\n\t\t...(ttlSeconds !== undefined ? { ttlSeconds } : {}),\n\t\t...(estimatedSavedUsd !== undefined ? { estimatedSavedUsd } : {}),\n\t};\n}\n\nexport function formatWarmTokenCount(tokens: number): string {\n\tif (tokens >= 1_000_000) return `${trimTrailingZero((tokens / 1_000_000).toFixed(1))}M`;\n\tif (tokens >= 1000) return `${trimTrailingZero((tokens / 1000).toFixed(1))}K`;\n\treturn String(Math.max(0, Math.trunc(tokens)));\n}\n\nexport function formatWakeDuration(ms: number): string {\n\tconst seconds = Math.round(ms / 1000);\n\tif (seconds < 60) return `${seconds}s`;\n\tconst minutes = Math.floor(seconds / 60);\n\tconst restSeconds = seconds % 60;\n\tif (minutes < 60) return restSeconds === 0 ? `${minutes}m` : `${minutes}m ${restSeconds}s`;\n\tconst hours = Math.floor(minutes / 60);\n\tconst restMinutes = minutes % 60;\n\treturn restMinutes === 0 ? `${hours}h` : `${hours}h ${restMinutes}m`;\n}\n\nexport function formatCacheTtl(seconds: number): string {\n\tif (seconds % 3600 === 0) return `${seconds / 3600}h`;\n\tif (seconds % 60 === 0) return `${seconds / 60}m`;\n\treturn `${seconds}s`;\n}\n\nexport function formatSavedUsd(value: number): string {\n\tif (value < 0.0005) return \"<$0.001\";\n\tif (value < 1) return `$${value.toFixed(3)}`;\n\treturn `$${value.toFixed(2)}`;\n}\n\nfunction trimTrailingZero(value: string): string {\n\treturn value.endsWith(\".0\") ? value.slice(0, -2) : value;\n}\n\nfunction clampTokens(value: number | undefined): number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;\n}\n\nfunction toProviderEnv(env: NodeJS.ProcessEnv): ProviderEnv {\n\tconst resolved: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tif (value !== undefined) resolved[key] = value;\n\t}\n\treturn resolved;\n}\n"]}
@@ -16,6 +16,7 @@ export type ContinuingGoalContinuationVerdict = Extract<GoalContinuationVerdict,
16
16
  kind: "continue";
17
17
  }>;
18
18
  export type DelayedContinuationKind = GoalWaitKind;
19
+ export type ResumptionChannelCounts = Readonly<Record<string, number>>;
19
20
  export type GoalContinuationAdmission = {
20
21
  readonly goal: Goal;
21
22
  readonly admitted: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"monitor-continuation-types.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IAC1D,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,iCAAiC,GAAG,OAAO,CAAC,uBAAuB,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AACvG,MAAM,MAAM,uBAAuB,GAAG,YAAY,CAAC;AAEnD,MAAM,MAAM,yBAAyB,GAAG;IACvC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC3B,CAAC"}
1
+ {"version":3,"file":"monitor-continuation-types.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IAC1D,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,iCAAiC,GAAG,OAAO,CAAC,uBAAuB,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AACvG,MAAM,MAAM,uBAAuB,GAAG,YAAY,CAAC;AACnD,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvE,MAAM,MAAM,yBAAyB,GAAG;IACvC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC3B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"monitor-continuation-types.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { AgentEndEvent, ExtensionContext } from \"../../types.ts\";\nimport type { GoalContinuationVerdict } from \"./continuation.ts\";\nimport type { Goal } from \"./types.ts\";\nimport type { GoalWaitKind } from \"./wait-progress.ts\";\n\nexport interface AgentEndOptions {\n\treadonly ctx: ExtensionContext;\n\treadonly goal: Goal | null;\n\treadonly messages: readonly AgentMessage[];\n}\n\nexport interface SystemAbortOptions extends AgentEndOptions {\n\treadonly event: AgentEndEvent;\n\treadonly willRetry: boolean;\n}\n\nexport type ContinuingGoalContinuationVerdict = Extract<GoalContinuationVerdict, { kind: \"continue\" }>;\nexport type DelayedContinuationKind = GoalWaitKind;\n\nexport type GoalContinuationAdmission = {\n\treadonly goal: Goal;\n\treadonly admitted: boolean;\n};\n"]}
1
+ {"version":3,"file":"monitor-continuation-types.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { AgentEndEvent, ExtensionContext } from \"../../types.ts\";\nimport type { GoalContinuationVerdict } from \"./continuation.ts\";\nimport type { Goal } from \"./types.ts\";\nimport type { GoalWaitKind } from \"./wait-progress.ts\";\n\nexport interface AgentEndOptions {\n\treadonly ctx: ExtensionContext;\n\treadonly goal: Goal | null;\n\treadonly messages: readonly AgentMessage[];\n}\n\nexport interface SystemAbortOptions extends AgentEndOptions {\n\treadonly event: AgentEndEvent;\n\treadonly willRetry: boolean;\n}\n\nexport type ContinuingGoalContinuationVerdict = Extract<GoalContinuationVerdict, { kind: \"continue\" }>;\nexport type DelayedContinuationKind = GoalWaitKind;\nexport type ResumptionChannelCounts = Readonly<Record<string, number>>;\n\nexport type GoalContinuationAdmission = {\n\treadonly goal: Goal;\n\treadonly admitted: boolean;\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"monitor-continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAwBrE,OAAO,KAAK,EACX,eAAe,EAIf,kBAAkB,EAClB,MAAM,iCAAiC,CAAC;AAKzC,OAAO,KAAK,EAAE,IAAI,EAAsB,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,eAAO,MAAM,kCAAkC,SAAU,CAAC;AAC1D,eAAO,MAAM,iCAAiC,gCAAgC,CAAC;AAC/E,eAAO,MAAM,+BAA+B,8BAA8B,CAAC;AAC3E,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAE1E,qBAAa,4BAA4B;;IAyBxC,YACC,EAAE,EAAE,YAAY,EAChB,qBAAqB,GAAE,MAAM,OAAqB,EAClD,uBAAuB,GAAE,MAAM,IAAe,EAC9C,UAAU,CAAC,EAAE,cAAc,EAM3B;IAED,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAuBjC;IAEK,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAsDlE;IAEK,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAaxE;IAEK,iBAAiB,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAK1D;IAED,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAOhC;IAED,mGAAmG;IACnG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAkBrC;IAED,6FAA6F;IAC7F,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAW3D;IAED,sEAAsE;IACtE,cAAc,IAAI,IAAI,CAIrB;IAED,mGAAmG;IACnG,uBAAuB,IAAI,IAAI,CAE9B;IAED,OAAO,IAAI,IAAI,CAUd;CAuND"}
1
+ {"version":3,"file":"monitor-continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAyBrE,OAAO,KAAK,EACX,eAAe,EAKf,kBAAkB,EAClB,MAAM,iCAAiC,CAAC;AAKzC,OAAO,KAAK,EAAE,IAAI,EAAsB,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,eAAO,MAAM,kCAAkC,SAAU,CAAC;AAC1D,eAAO,MAAM,iCAAiC,gCAAgC,CAAC;AAC/E,eAAO,MAAM,+BAA+B,8BAA8B,CAAC;AAC3E,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAE1E,qBAAa,4BAA4B;;IA0BxC,YACC,EAAE,EAAE,YAAY,EAChB,qBAAqB,GAAE,MAAM,OAAqB,EAClD,uBAAuB,GAAE,MAAM,IAAe,EAC9C,UAAU,CAAC,EAAE,cAAc,EAO3B;IAED,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CASjC;IAEK,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAsDlE;IAEK,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAaxE;IAEK,iBAAiB,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAK1D;IAED,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAOhC;IAED,mGAAmG;IACnG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAkBrC;IAED,6FAA6F;IAC7F,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAW3D;IAED,sEAAsE;IACtE,cAAc,IAAI,IAAI,CAIrB;IAED,mGAAmG;IACnG,uBAAuB,IAAI,IAAI,CAE9B;IAED,OAAO,IAAI,IAAI,CAUd;CAkRD"}
@@ -1,4 +1,5 @@
1
1
  import { isTerminalMonitorStateEvent, TERMINAL_MONITOR_STATE_EVENT } from "../monitor-state-event.js";
2
+ import { isResumptionChannelStateEvent, RESUMPTION_CHANNEL_STATE_EVENT } from "../resumption-channel-event.js";
2
3
  import { estimateCacheWarmMetrics, GOAL_CACHE_WARMUP_ENTRY_TYPE, } from "./cache-warm.js";
3
4
  import { continuationTurnUsedTools, evaluateGoalContinuation, GOAL_USER_GRACE_DELAY_MS, hasGoalContinuationProgress, hashAssistantText, normalizeAssistantText, } from "./continuation.js";
4
5
  import { lastAssistantMessage } from "./last-assistant-message.js";
@@ -16,12 +17,13 @@ export class MonitorAwareGoalContinuation {
16
17
  #isContinuationPending;
17
18
  #markContinuationPending;
18
19
  #waitTicker;
19
- #activeMonitorCount = 0;
20
+ #channelCounts = new Map();
21
+ #hasStarted = false;
20
22
  #ctx;
21
23
  #goal = null;
22
24
  #timer;
23
25
  #scheduledContinuationKind;
24
- #unsubscribeMonitorState;
26
+ #channelStateUnsubscribers = [];
25
27
  #lastAgentEndMessages = [];
26
28
  #consecutiveLengthRecoveries = new Map();
27
29
  #recentNormalizedOutputHashes = [];
@@ -40,33 +42,19 @@ export class MonitorAwareGoalContinuation {
40
42
  this.#isContinuationPending = isContinuationPending;
41
43
  this.#markContinuationPending = markContinuationPending;
42
44
  this.#waitTicker = waitTicker;
45
+ this.#subscribeToChannelState();
43
46
  }
44
47
  start(ctx) {
45
48
  this.#cancelTimer();
46
- this.#unsubscribeMonitorState?.();
47
49
  this.#ctx = ctx;
48
- this.#activeMonitorCount = 0;
50
+ if (this.#hasStarted)
51
+ this.#channelCounts.clear();
52
+ else
53
+ this.#hasStarted = true;
49
54
  this.#goal = null;
50
55
  this.#lastAgentEndMessages = [];
51
56
  this.#directInputHolds.clear();
52
57
  this.#resetContinuationState();
53
- const events = this.#pi.events;
54
- if (events === undefined)
55
- return;
56
- this.#unsubscribeMonitorState = events.on(TERMINAL_MONITOR_STATE_EVENT, (data) => {
57
- if (!isTerminalMonitorStateEvent(data))
58
- return;
59
- this.#activeMonitorCount = data.activeCount;
60
- if (data.activeCount === 0) {
61
- if (this.#scheduledContinuationKind === "monitor")
62
- this.#cancelTimer();
63
- this.#resetToollessContinuationStreak();
64
- return;
65
- }
66
- if (this.#scheduledContinuationKind === "monitor") {
67
- this.#waitTicker?.setActiveMonitorCount(data.activeCount);
68
- }
69
- });
70
58
  }
71
59
  async afterAgentEnd(options) {
72
60
  if (options.goal?.id !== this.#goal?.id)
@@ -115,7 +103,7 @@ export class MonitorAwareGoalContinuation {
115
103
  }
116
104
  if (immediateVerdict.kind === "deny" && immediateVerdict.reason === "not-eligible")
117
105
  return goal;
118
- if (this.#activeMonitorCount === 0) {
106
+ if (this.#activeChannelCount() === 0) {
119
107
  this.#cancelTimer();
120
108
  const admission = await this.#admitAndQueue(options.ctx, goal, "immediate", options.messages);
121
109
  return admission.goal;
@@ -132,7 +120,7 @@ export class MonitorAwareGoalContinuation {
132
120
  this.#lastTurnUsage = collectAssistantUsage([...options.messages]);
133
121
  if (options.willRetry || options.goal?.status !== "active")
134
122
  return options.goal;
135
- if (this.#activeMonitorCount > 0)
123
+ if (this.#activeChannelCount() > 0)
136
124
  this.#schedule(options.goal, "monitor");
137
125
  else if (lastAssistantMessage(options.messages)?.stopReason === "error") {
138
126
  this.#pendingSystemRecovery = options;
@@ -201,11 +189,12 @@ export class MonitorAwareGoalContinuation {
201
189
  }
202
190
  dispose() {
203
191
  this.#cancelTimer();
204
- this.#unsubscribeMonitorState?.();
205
- this.#unsubscribeMonitorState = undefined;
192
+ for (const unsubscribe of this.#channelStateUnsubscribers)
193
+ unsubscribe();
194
+ this.#channelStateUnsubscribers = [];
206
195
  this.#ctx = undefined;
207
196
  this.#goal = null;
208
- this.#activeMonitorCount = 0;
197
+ this.#channelCounts.clear();
209
198
  this.#lastAgentEndMessages = [];
210
199
  this.#directInputHolds.clear();
211
200
  this.#resetContinuationState();
@@ -216,19 +205,22 @@ export class MonitorAwareGoalContinuation {
216
205
  const delayMs = kind === "monitor" ? GOAL_MONITOR_CONTINUATION_DELAY_MS : GOAL_USER_GRACE_DELAY_MS;
217
206
  if (kind === "monitor") {
218
207
  const cache = estimateCacheWarmMetrics(this.#ctx?.model, process.env, this.#lastTurnUsage);
208
+ const channelCounts = this.#channelCountSnapshot();
219
209
  this.#scheduledCache = cache;
220
210
  this.#scheduledAtMs = Date.now();
221
211
  this.#pi.events?.emit(GOAL_CONTINUATION_SCHEDULED_EVENT, {
222
212
  goalId: goal.id,
223
213
  delayMs,
224
- activeMonitorCount: this.#activeMonitorCount,
214
+ activeMonitorCount: this.#activeTerminalMonitorCount(),
215
+ channelCounts,
225
216
  cache,
226
217
  });
227
218
  this.#appendWarmupEntry({
228
219
  phase: "scheduled",
229
220
  goalId: goal.id,
230
221
  delayMs,
231
- activeMonitorCount: this.#activeMonitorCount,
222
+ activeMonitorCount: this.#activeTerminalMonitorCount(),
223
+ channelCounts,
232
224
  ...(cache !== undefined ? { cache } : {}),
233
225
  });
234
226
  }
@@ -251,7 +243,7 @@ export class MonitorAwareGoalContinuation {
251
243
  kind,
252
244
  remainingMs: delayMs,
253
245
  totalMs: kind === "monitor" ? GOAL_MONITOR_CONTINUATION_DELAY_MS : GOAL_USER_GRACE_DELAY_MS,
254
- activeMonitorCount: this.#activeMonitorCount,
246
+ channelCounts: this.#channelCountSnapshot(),
255
247
  });
256
248
  }
257
249
  this.#timer = setTimeout(() => {
@@ -277,16 +269,18 @@ export class MonitorAwareGoalContinuation {
277
269
  const goal = this.#goal;
278
270
  if (ctx === undefined || goal?.status !== "active" || !ctx.isIdle() || ctx.hasPendingMessages())
279
271
  return;
280
- if (kind === "monitor" && this.#activeMonitorCount === 0)
272
+ if (kind === "monitor" && this.#activeChannelCount() === 0)
281
273
  return;
282
274
  const admission = await this.#admitAndQueue(ctx, goal, kind === "monitor" ? "monitorDelayed" : "userGrace", this.#lastAgentEndMessages);
283
275
  if (kind !== "monitor" || !admission.admitted)
284
276
  return;
277
+ const channelCounts = this.#channelCountSnapshot();
285
278
  this.#pi.events?.emit(GOAL_CONTINUATION_RESUMED_EVENT, {
286
279
  goalId: goal.id,
287
280
  delayMs,
288
281
  waitedMs,
289
- activeMonitorCount: this.#activeMonitorCount,
282
+ activeMonitorCount: this.#activeTerminalMonitorCount(),
283
+ channelCounts,
290
284
  cache,
291
285
  });
292
286
  this.#appendWarmupEntry({
@@ -294,7 +288,8 @@ export class MonitorAwareGoalContinuation {
294
288
  goalId: goal.id,
295
289
  delayMs,
296
290
  waitedMs,
297
- activeMonitorCount: this.#activeMonitorCount,
291
+ activeMonitorCount: this.#activeTerminalMonitorCount(),
292
+ channelCounts,
298
293
  ...(cache !== undefined ? { cache } : {}),
299
294
  });
300
295
  }
@@ -339,17 +334,17 @@ export class MonitorAwareGoalContinuation {
339
334
  let content = verdict.prompt === "minimal" ? buildTruncationRecoveryPrompt() : buildContinuationPrompt(goal);
340
335
  if (!verdict.stallNotice)
341
336
  return content;
342
- const monitorsActive = this.#activeMonitorCount > 0;
337
+ const liveSources = this.#liveChannelSources();
343
338
  this.#pi.events?.emit(GOAL_MONITOR_STALL_EVENT, {
344
339
  goalId: goal.id,
345
340
  consecutiveContinuations: this.#toollessContinuationStreak,
346
341
  toolless: true,
347
342
  });
348
343
  if (ctx.hasUI) {
349
- const context = monitorsActive ? "while monitors stayed active" : "without tool use";
344
+ const context = liveSources.length > 0 ? `while ${liveSources.join(", ")} channels stayed active` : "without tool use";
350
345
  ctx.ui.notify(`Goal continuation repeated ${this.#toollessContinuationStreak} toolless turns ${context} - injected a stall check.`, "info");
351
346
  }
352
- content = `${buildGoalStallNotice(this.#toollessContinuationStreak, { monitorsActive })}\n\n${content}`;
347
+ content = `${buildGoalStallNotice(this.#toollessContinuationStreak, { liveSources })}\n\n${content}`;
353
348
  return content;
354
349
  }
355
350
  /** A tool-using turn is forward progress, so it clears the repetition window instead of extending it. */
@@ -381,6 +376,52 @@ export class MonitorAwareGoalContinuation {
381
376
  return;
382
377
  this.#consecutiveLengthRecoveries.delete(goal.id);
383
378
  }
379
+ #subscribeToChannelState() {
380
+ const events = this.#pi.events;
381
+ if (events === undefined)
382
+ return;
383
+ this.#channelStateUnsubscribers.push(events.on(TERMINAL_MONITOR_STATE_EVENT, (data) => {
384
+ if (!isTerminalMonitorStateEvent(data))
385
+ return;
386
+ this.#setChannelCount("terminal-monitor", data.activeCount);
387
+ }), events.on(RESUMPTION_CHANNEL_STATE_EVENT, (data) => {
388
+ if (!isResumptionChannelStateEvent(data))
389
+ return;
390
+ this.#setChannelCount(data.source, data.activeCount);
391
+ }));
392
+ }
393
+ #setChannelCount(source, activeCount) {
394
+ const previousTotal = this.#activeChannelCount();
395
+ this.#channelCounts.set(source, activeCount);
396
+ const nextTotal = this.#activeChannelCount();
397
+ if (previousTotal > 0 && nextTotal === 0) {
398
+ if (this.#scheduledContinuationKind === "monitor")
399
+ this.#cancelTimer();
400
+ this.#resetToollessContinuationStreak();
401
+ return;
402
+ }
403
+ if (this.#scheduledContinuationKind === "monitor") {
404
+ this.#waitTicker?.setChannelCounts(this.#channelCountSnapshot());
405
+ }
406
+ }
407
+ #activeChannelCount() {
408
+ let total = 0;
409
+ for (const count of this.#channelCounts.values())
410
+ total += count;
411
+ return total;
412
+ }
413
+ #activeTerminalMonitorCount() {
414
+ return this.#channelCounts.get("terminal-monitor") ?? 0;
415
+ }
416
+ #channelCountSnapshot() {
417
+ return Object.fromEntries([...this.#channelCounts.entries()].sort(([left], [right]) => left.localeCompare(right)));
418
+ }
419
+ #liveChannelSources() {
420
+ return [...this.#channelCounts.entries()]
421
+ .filter(([, count]) => count > 0)
422
+ .map(([source]) => source)
423
+ .sort();
424
+ }
384
425
  #resetContinuationState() {
385
426
  this.#pendingSystemRecovery = undefined;
386
427
  this.#consecutiveLengthRecoveries.clear();