@bastani/atomic 0.9.5-alpha.8 → 0.9.5-alpha.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 (207) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/builtin/cursor/package.json +2 -2
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/CHANGELOG.md +11 -0
  5. package/dist/builtin/mcp/README.md +6 -6
  6. package/dist/builtin/mcp/direct-tools.ts +41 -23
  7. package/dist/builtin/mcp/index.ts +31 -4
  8. package/dist/builtin/mcp/init.ts +4 -43
  9. package/dist/builtin/mcp/metadata-hydration.ts +37 -0
  10. package/dist/builtin/mcp/package.json +1 -1
  11. package/dist/builtin/mcp/proxy-info-modes.ts +67 -14
  12. package/dist/builtin/mcp/startup-warmup.ts +98 -0
  13. package/dist/builtin/mcp/tool-result-renderer.ts +3 -3
  14. package/dist/builtin/subagents/CHANGELOG.md +12 -0
  15. package/dist/builtin/subagents/package.json +1 -1
  16. package/dist/builtin/subagents/src/extension/index.ts +14 -25
  17. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +89 -0
  18. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +23 -18
  19. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +20 -10
  20. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +13 -3
  21. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +10 -2
  22. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +28 -1
  23. package/dist/builtin/subagents/src/tui/render-widget.ts +29 -0
  24. package/dist/builtin/web-access/package.json +1 -1
  25. package/dist/builtin/workflows/CHANGELOG.md +18 -0
  26. package/dist/builtin/workflows/README.md +1 -1
  27. package/dist/builtin/workflows/builtin/goal-runner.ts +39 -1
  28. package/dist/builtin/workflows/package.json +1 -1
  29. package/dist/builtin/workflows/skills/impeccable/SKILL.md +16 -13
  30. package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +1 -0
  31. package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +58 -0
  32. package/dist/builtin/workflows/skills/impeccable/reference/android.md +40 -0
  33. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +2 -0
  34. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +2 -0
  35. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +139 -0
  36. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +69 -62
  37. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +21 -7
  38. package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
  39. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +92 -0
  40. package/dist/builtin/workflows/skills/impeccable/reference/init.md +29 -7
  41. package/dist/builtin/workflows/skills/impeccable/reference/ios.md +45 -0
  42. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +25 -1
  43. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +23 -1
  44. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +2 -1
  45. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +75 -14
  46. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +1 -1
  47. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +31 -0
  48. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +64 -0
  49. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +118 -11
  50. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +16 -11
  51. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +66 -0
  52. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +42 -10
  53. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
  54. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +4 -6
  55. package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +660 -0
  56. package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +516 -0
  57. package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +1764 -0
  58. package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +61 -0
  59. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +4 -2
  60. package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +2 -2
  61. package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +13 -16
  62. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +17 -11
  63. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +104 -37
  64. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +9 -0
  65. package/dist/builtin/workflows/src/durable/backend.ts +12 -0
  66. package/dist/builtin/workflows/src/durable/dbos-backend.ts +17 -1
  67. package/dist/builtin/workflows/src/durable/resume-catalog.ts +12 -0
  68. package/dist/builtin/workflows/src/durable/resume-runtime.ts +5 -2
  69. package/dist/builtin/workflows/src/durable/types.ts +16 -0
  70. package/dist/builtin/workflows/src/engine/primitives/task.ts +1 -0
  71. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +1 -2
  72. package/dist/builtin/workflows/src/engine/run-returned-status.ts +50 -16
  73. package/dist/builtin/workflows/src/engine/run.ts +20 -19
  74. package/dist/builtin/workflows/src/engine/runtime.ts +5 -0
  75. package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
  76. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +9 -2
  77. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +137 -27
  78. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +22 -6
  79. package/dist/builtin/workflows/src/extension/public-types.ts +1 -1
  80. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +13 -1
  81. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +19 -4
  82. package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +9 -0
  83. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +48 -23
  84. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +20 -8
  85. package/dist/builtin/workflows/src/extension/workflow-tool.ts +24 -8
  86. package/dist/builtin/workflows/src/runs/background/status.ts +4 -3
  87. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +29 -8
  88. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +2 -1
  89. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +3 -2
  90. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +155 -20
  91. package/dist/builtin/workflows/src/runs/shared/worktree-types.ts +6 -0
  92. package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -1
  93. package/dist/builtin/workflows/src/shared/returned-run-status.ts +116 -0
  94. package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +2 -0
  95. package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +21 -3
  96. package/dist/builtin/workflows/src/tui/graph-view-state.ts +3 -0
  97. package/dist/builtin/workflows/src/tui/graph-view-types.ts +3 -0
  98. package/dist/builtin/workflows/src/tui/status-list.ts +22 -15
  99. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +4 -4
  100. package/dist/core/agent-session-auto-compaction.d.ts +23 -3
  101. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  102. package/dist/core/agent-session-auto-compaction.js +129 -17
  103. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  104. package/dist/core/agent-session-events.d.ts.map +1 -1
  105. package/dist/core/agent-session-events.js +7 -0
  106. package/dist/core/agent-session-events.js.map +1 -1
  107. package/dist/core/agent-session-methods.d.ts +6 -3
  108. package/dist/core/agent-session-methods.d.ts.map +1 -1
  109. package/dist/core/agent-session-methods.js.map +1 -1
  110. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  111. package/dist/core/agent-session-prompt.js +1 -1
  112. package/dist/core/agent-session-prompt.js.map +1 -1
  113. package/dist/core/agent-session.d.ts +4 -2
  114. package/dist/core/agent-session.d.ts.map +1 -1
  115. package/dist/core/agent-session.js +4 -2
  116. package/dist/core/agent-session.js.map +1 -1
  117. package/dist/core/extensions/loader-virtual-modules.d.ts +8 -1
  118. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  119. package/dist/core/extensions/loader-virtual-modules.js +2 -1
  120. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  121. package/dist/core/footer-data-provider.d.ts +6 -0
  122. package/dist/core/footer-data-provider.d.ts.map +1 -1
  123. package/dist/core/footer-data-provider.js +72 -29
  124. package/dist/core/footer-data-provider.js.map +1 -1
  125. package/dist/core/openai-responses-payload-sanitizer.d.ts +1 -0
  126. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  127. package/dist/core/openai-responses-payload-sanitizer.js +24 -9
  128. package/dist/core/openai-responses-payload-sanitizer.js.map +1 -1
  129. package/dist/core/slash-commands.d.ts +2 -0
  130. package/dist/core/slash-commands.d.ts.map +1 -1
  131. package/dist/core/slash-commands.js +141 -0
  132. package/dist/core/slash-commands.js.map +1 -1
  133. package/dist/index.d.ts +1 -0
  134. package/dist/index.d.ts.map +1 -1
  135. package/dist/index.js +1 -0
  136. package/dist/index.js.map +1 -1
  137. package/dist/main-deferred-startup.d.ts.map +1 -1
  138. package/dist/main-deferred-startup.js +2 -2
  139. package/dist/main-deferred-startup.js.map +1 -1
  140. package/dist/main-early-input.d.ts.map +1 -1
  141. package/dist/main-early-input.js +7 -0
  142. package/dist/main-early-input.js.map +1 -1
  143. package/dist/main.d.ts.map +1 -1
  144. package/dist/main.js +1 -1
  145. package/dist/main.js.map +1 -1
  146. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  147. package/dist/modes/interactive/components/chat-session-host-actions.js +0 -1
  148. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  149. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  150. package/dist/modes/interactive/interactive-agent-events.js +1 -0
  151. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  152. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  153. package/dist/modes/interactive/interactive-autocomplete.js +21 -9
  154. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  155. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  156. package/dist/modes/interactive/interactive-deferred-startup.js +41 -28
  157. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  158. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  159. package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -0
  160. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  161. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  162. package/dist/modes/interactive/interactive-input-handling.js +41 -6
  163. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  164. package/dist/modes/interactive/interactive-mode-base.d.ts +3 -0
  165. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  166. package/dist/modes/interactive/interactive-mode-base.js +3 -0
  167. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  168. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  169. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  170. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  171. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  172. package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -2
  173. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  174. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  175. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  176. package/dist/modes/interactive/interactive-model-routing.js +3 -1
  177. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  178. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  179. package/dist/modes/interactive/interactive-process-lifecycle.js +0 -3
  180. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  181. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  182. package/dist/modes/interactive/interactive-render-chat.js +15 -1
  183. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  184. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  185. package/dist/modes/interactive/interactive-session-routing.js +5 -1
  186. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  187. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  188. package/dist/modes/interactive/interactive-startup.js +20 -21
  189. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  190. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  191. package/dist/modes/interactive/theme/global-theme.js +26 -2
  192. package/dist/modes/interactive/theme/global-theme.js.map +1 -1
  193. package/dist/utils/fs-watch.d.ts +24 -1
  194. package/dist/utils/fs-watch.d.ts.map +1 -1
  195. package/dist/utils/fs-watch.js +60 -5
  196. package/dist/utils/fs-watch.js.map +1 -1
  197. package/docs/compaction.md +5 -1
  198. package/docs/development.md +4 -0
  199. package/docs/extensions.md +5 -0
  200. package/docs/json.md +1 -1
  201. package/docs/quickstart.md +1 -1
  202. package/docs/settings.md +1 -1
  203. package/docs/subagents.md +4 -0
  204. package/docs/windows.md +4 -0
  205. package/docs/workflows.md +8 -6
  206. package/npm-shrinkwrap.json +23 -23
  207. package/package.json +2 -2
@@ -5,41 +5,160 @@ import { createGitEnvironment } from "@bastani/atomic";
5
5
  import type { GitResult, GitWorktreeSetupOptions, GitWorktreeSetupResult } from "./worktree-types.js";
6
6
 
7
7
  const DISABLED_GIT_HOOKS_PATH = process.platform === "win32" ? "NUL" : "/dev/null";
8
+ const GIT_COMMAND_TIMEOUT_MS = 60_000;
9
+ const GIT_READ_ONLY_PROBE_TIMEOUT_ATTEMPTS = 2;
8
10
 
9
- export function runGit(cwd: string, args: string[]): GitResult {
10
- const result = spawnSync("git", [
11
+ export type GitRunner = (cwd: string, args: readonly string[]) => GitResult;
12
+
13
+ let gitRunnerOverride: GitRunner | undefined;
14
+
15
+ function gitCommandArgs(args: readonly string[]): string[] {
16
+ return [
11
17
  "-c",
12
18
  `core.hooksPath=${DISABLED_GIT_HOOKS_PATH}`,
13
19
  "-c",
14
20
  "core.fsmonitor=false",
15
21
  ...args,
16
- ], {
22
+ ];
23
+ }
24
+
25
+ function gitCommandArgv(args: readonly string[]): string[] {
26
+ return ["git", ...gitCommandArgs(args)];
27
+ }
28
+
29
+ function withGitResultDiagnostics(result: GitResult, cwd: string, args: readonly string[]): GitResult {
30
+ return {
31
+ ...result,
32
+ argv: result.argv ?? gitCommandArgv(args),
33
+ cwd: result.cwd ?? cwd,
34
+ timeoutMs: result.timeoutMs ?? GIT_COMMAND_TIMEOUT_MS,
35
+ };
36
+ }
37
+
38
+ function runGitProcess(cwd: string, args: readonly string[]): GitResult {
39
+ const startedAt = Date.now();
40
+ const result = spawnSync("git", gitCommandArgs(args), {
17
41
  cwd,
18
42
  encoding: "utf-8",
19
- env: createGitEnvironment({ GIT_OPTIONAL_LOCKS: "0" }),
20
- timeout: 5000,
43
+ env: createGitEnvironment({
44
+ GIT_OPTIONAL_LOCKS: "0",
45
+ GIT_TERMINAL_PROMPT: "0",
46
+ GCM_INTERACTIVE: "never",
47
+ }),
48
+ timeout: GIT_COMMAND_TIMEOUT_MS,
21
49
  });
22
50
  return {
23
51
  stdout: result.stdout ?? "",
24
52
  stderr: result.stderr ?? "",
25
- status: result.status,
53
+ status: result.status ?? null,
54
+ signal: result.signal ?? null,
55
+ elapsedMs: Math.max(0, Date.now() - startedAt),
26
56
  ...(result.error === undefined ? {} : { error: result.error }),
27
57
  };
28
58
  }
29
59
 
30
- export function runGitChecked(cwd: string, args: string[]): string {
31
- const result = runGit(cwd, args);
32
- if (result.status !== 0) {
33
- const command = `git -C ${cwd} ${args.join(" ")}`;
34
- const message = result.stderr.trim() || result.stdout.trim() || `${command} failed`;
35
- throw new Error(message);
60
+ export function withGitRunnerForTest<T>(runner: GitRunner, callback: () => T): T {
61
+ const previous = gitRunnerOverride;
62
+ gitRunnerOverride = runner;
63
+ try {
64
+ return callback();
65
+ } finally {
66
+ gitRunnerOverride = previous;
36
67
  }
68
+ }
69
+
70
+ export function runGit(cwd: string, args: readonly string[]): GitResult {
71
+ return withGitResultDiagnostics((gitRunnerOverride ?? runGitProcess)(cwd, args), cwd, args);
72
+ }
73
+
74
+ export function runGitChecked(cwd: string, args: readonly string[]): string {
75
+ const result = runGit(cwd, args);
76
+ if (result.status !== 0) throw new Error(gitFailureMessage(result));
37
77
  return result.stdout;
38
78
  }
39
79
 
80
+ function gitErrorCode(error: Error): string | undefined {
81
+ return "code" in error ? String((error as Error & { readonly code?: unknown }).code) : undefined;
82
+ }
83
+
84
+ export function isGitTimeoutResult(result: GitResult): boolean {
85
+ const error = result.error;
86
+ if (error === undefined) return false;
87
+ const code = gitErrorCode(error)?.toUpperCase();
88
+ const message = error.message.toLowerCase();
89
+ return code === "ETIMEDOUT" || message.includes("etimedout") || message.includes("timed out");
90
+ }
91
+
92
+ function formatGitDiagnosticArg(value: string): string {
93
+ if (/^[A-Za-z0-9_./:@%+=,\\-]+$/.test(value)) return value;
94
+ return JSON.stringify(value);
95
+ }
96
+
97
+ function formatGitCommand(argv: readonly string[]): string {
98
+ return argv.map(formatGitDiagnosticArg).join(" ");
99
+ }
100
+
101
+ function gitDiagnosticSuffix(result: GitResult): string {
102
+ const details: string[] = [];
103
+ if (result.argv !== undefined && result.argv.length > 0) details.push(`command: ${formatGitCommand(result.argv)}`);
104
+ if (result.cwd !== undefined) details.push(`cwd: ${result.cwd}`);
105
+ if (result.timeoutMs !== undefined) details.push(`timeout: ${result.timeoutMs}ms`);
106
+ if (result.elapsedMs !== undefined) details.push(`elapsed: ${result.elapsedMs}ms`);
107
+ details.push(`status: ${result.status === null ? "null" : result.status}`);
108
+ if (result.signal !== undefined) details.push(`signal: ${result.signal ?? "null"}`);
109
+ if (result.attempts !== undefined && result.attempts > 1) details.push(`attempts: ${result.attempts}`);
110
+ return details.length === 0 ? "" : ` (${details.join("; ")})`;
111
+ }
112
+
40
113
  export function gitFailureMessage(result: GitResult): string {
41
- if (result.error !== undefined) return result.error.message;
42
- return result.stderr.trim() || result.stdout.trim() || `git exited with status ${result.status}`;
114
+ if (result.error !== undefined) {
115
+ const code = gitErrorCode(result.error);
116
+ if (isGitTimeoutResult(result)) {
117
+ return `git command timed out after ${result.timeoutMs ?? GIT_COMMAND_TIMEOUT_MS}ms${code === undefined ? "" : ` (${code})`}: ${result.error.message}${gitDiagnosticSuffix(result)}`;
118
+ }
119
+ return `${code === undefined ? result.error.message : `${code}: ${result.error.message}`}${gitDiagnosticSuffix(result)}`;
120
+ }
121
+ return `${result.stderr.trim() || result.stdout.trim() || `git exited with status ${result.status}`}${gitDiagnosticSuffix(result)}`;
122
+ }
123
+
124
+ function runGitReadOnlyProbe(cwd: string, args: readonly string[]): GitResult {
125
+ let attempts = 1;
126
+ let result = runGit(cwd, args);
127
+ while (attempts < GIT_READ_ONLY_PROBE_TIMEOUT_ATTEMPTS && result.status !== 0 && isGitTimeoutResult(result)) {
128
+ attempts += 1;
129
+ result = runGit(cwd, args);
130
+ }
131
+ return attempts > 1 ? { ...result, attempts } : result;
132
+ }
133
+
134
+ export interface GitWorktreeSetupCache {
135
+ get(options: GitWorktreeSetupOptions): GitWorktreeSetupResult;
136
+ }
137
+
138
+ function gitWorktreeSetupCacheKey(options: GitWorktreeSetupOptions): string {
139
+ return JSON.stringify([
140
+ path.resolve(options.cwd),
141
+ options.gitWorktreeDir.trim(),
142
+ options.baseBranch?.trim() ?? "",
143
+ ]);
144
+ }
145
+
146
+ export function createGitWorktreeSetupCache(): GitWorktreeSetupCache {
147
+ const results = new Map<string, GitWorktreeSetupResult>();
148
+ return {
149
+ get(options) {
150
+ const key = gitWorktreeSetupCacheKey(options);
151
+ const cached = results.get(key);
152
+ if (cached !== undefined) return cached;
153
+ const setup = setupGitWorktree(options);
154
+ results.set(key, setup);
155
+ return setup;
156
+ },
157
+ };
158
+ }
159
+
160
+ export function setupGitWorktreeCached(options: GitWorktreeSetupOptions, cache?: GitWorktreeSetupCache): GitWorktreeSetupResult {
161
+ return cache?.get(options) ?? setupGitWorktree(options);
43
162
  }
44
163
 
45
164
  function quoteShellArg(value: string): string {
@@ -122,22 +241,38 @@ function pathExistsSync(value: string): boolean {
122
241
  }
123
242
 
124
243
  function repositoryRootForGitWorktree(cwd: string): string {
125
- const result = runGit(cwd, ["rev-parse", "--show-toplevel"]);
244
+ const result = runGitReadOnlyProbe(cwd, ["rev-parse", "--show-toplevel"]);
126
245
  if (result.status !== 0) {
246
+ if (isGitTimeoutResult(result)) {
247
+ throw new Error(`Timed out while checking the Git repository for gitWorktreeDir from ${cwd}. Git reported: ${gitFailureMessage(result)}`);
248
+ }
127
249
  throw new Error(`gitWorktreeDir requires the workflow to be invoked from inside a Git repository. Start from a Git checkout or omit gitWorktreeDir. Git reported: ${gitFailureMessage(result)}`);
128
250
  }
129
251
  return result.stdout.trim();
130
252
  }
131
253
 
132
- function gitTopLevel(cwd: string): string | undefined {
133
- const result = runGit(cwd, ["rev-parse", "--show-toplevel"]);
134
- if (result.status !== 0) return undefined;
254
+ export function gitTopLevelFromResult(result: GitResult, cwd: string, description: string): string | undefined {
255
+ if (result.status !== 0) {
256
+ if (isGitTimeoutResult(result)) {
257
+ throw new Error(`Timed out while validating ${description}. Git reported: ${gitFailureMessage(result)}`);
258
+ }
259
+ return undefined;
260
+ }
135
261
  return gitPathFromOutput(result.stdout, cwd);
136
262
  }
137
263
 
264
+ function gitTopLevel(cwd: string): string | undefined {
265
+ return gitTopLevelFromResult(runGitReadOnlyProbe(cwd, ["rev-parse", "--show-toplevel"]), cwd, `gitWorktreeDir ${cwd}`);
266
+ }
267
+
138
268
  function gitCommonDirForWorktree(cwd: string): string {
139
- const result = runGit(cwd, ["rev-parse", "--git-common-dir"]);
140
- if (result.status !== 0) throw new Error(gitFailureMessage(result));
269
+ const result = runGitReadOnlyProbe(cwd, ["rev-parse", "--git-common-dir"]);
270
+ if (result.status !== 0) {
271
+ if (isGitTimeoutResult(result)) {
272
+ throw new Error(`Timed out while validating Git common directory for gitWorktreeDir ${cwd}. Git reported: ${gitFailureMessage(result)}`);
273
+ }
274
+ throw new Error(`Failed to validate Git common directory for gitWorktreeDir ${cwd}. Git reported: ${gitFailureMessage(result)}`);
275
+ }
141
276
  const gitPath = gitPathFromOutput(result.stdout, cwd);
142
277
  if (gitPath === undefined) throw new Error("git rev-parse --git-common-dir returned an empty path");
143
278
  return gitPath;
@@ -65,7 +65,13 @@ export interface GitResult {
65
65
  stdout: string;
66
66
  stderr: string;
67
67
  status: number | null;
68
+ signal?: NodeJS.Signals | null;
68
69
  error?: Error;
70
+ argv?: readonly string[];
71
+ cwd?: string;
72
+ timeoutMs?: number;
73
+ elapsedMs?: number;
74
+ attempts?: number;
69
75
  }
70
76
 
71
77
  export interface GitWorktreeSetupOptions {
@@ -1,4 +1,4 @@
1
- export { setupGitWorktree } from "./worktree-git.js";
1
+ export { createGitWorktreeSetupCache, setupGitWorktree, setupGitWorktreeCached } from "./worktree-git.js";
2
2
  export {
3
3
  cleanupWorktrees,
4
4
  createWorktrees,
@@ -16,3 +16,4 @@ export type {
16
16
  WorktreeSetup,
17
17
  WorktreeTaskCwdConflict,
18
18
  } from "./worktree-types.js";
19
+ export type { GitWorktreeSetupCache } from "./worktree-git.js";
@@ -0,0 +1,116 @@
1
+ import type { RunEndMetadata } from "./store-public-types.js";
2
+ import type { RunSnapshot, RunStatus, StageSnapshot, WorkflowFailureKind } from "./store-types.js";
3
+ import type { WorkflowOutputValues } from "./types.js";
4
+
5
+ const RETURNED_BLOCKED_STATUSES = new Set([
6
+ "blocked",
7
+ "needs_human",
8
+ "incomplete",
9
+ "auth_blocked",
10
+ "active",
11
+ ]);
12
+
13
+ export interface StructuredRecoverableWorkflowFailure {
14
+ readonly error: string;
15
+ readonly metadata: RunEndMetadata;
16
+ }
17
+
18
+ export function normalizeReturnedWorkflowStatus(status: unknown): string | undefined {
19
+ if (typeof status !== "string") return undefined;
20
+ const normalized = status.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
21
+ return normalized.length > 0 ? normalized : undefined;
22
+ }
23
+
24
+ export function isReturnedBlockedWorkflowStatus(status: string): boolean {
25
+ return RETURNED_BLOCKED_STATUSES.has(status);
26
+ }
27
+
28
+ export function isReturnedResumableBlockedWorkflowStatus(status: string): boolean {
29
+ return status !== "blocked" && isReturnedBlockedWorkflowStatus(status);
30
+ }
31
+
32
+ export function actionableReturnedStatusText(result: WorkflowOutputValues | undefined): string | undefined {
33
+ return stringResultField(result, "summary") ?? stringResultField(result, "remaining_work") ?? stringResultField(result, "result");
34
+ }
35
+
36
+ export function structuredRecoverableWorkflowFailure(
37
+ run: Pick<RunSnapshot,
38
+ | "error"
39
+ | "failureKind"
40
+ | "failureCode"
41
+ | "failureRecoverability"
42
+ | "failureDisposition"
43
+ | "failureMessage"
44
+ | "failedStageId"
45
+ | "retryAfterMs"
46
+ | "stages"
47
+ >,
48
+ ): StructuredRecoverableWorkflowFailure | undefined {
49
+ const runFailure = structuredRecoverableFailureFromSnapshot(run);
50
+ if (runFailure !== undefined) return runFailure;
51
+ if (run.failedStageId === undefined) return undefined;
52
+
53
+ const failedStage = run.stages.find((stage) => stage.id === run.failedStageId);
54
+ return failedStage === undefined
55
+ ? undefined
56
+ : structuredRecoverableFailureFromSnapshot(failedStage, failedStage.id);
57
+ }
58
+
59
+ export function structuredRecoverableWorkflowFailureText(
60
+ run: Pick<RunSnapshot,
61
+ | "error"
62
+ | "failureKind"
63
+ | "failureCode"
64
+ | "failureRecoverability"
65
+ | "failureDisposition"
66
+ | "failureMessage"
67
+ | "failedStageId"
68
+ | "retryAfterMs"
69
+ | "stages"
70
+ >,
71
+ ): string | undefined {
72
+ return structuredRecoverableWorkflowFailure(run)?.error;
73
+ }
74
+
75
+ export function effectiveRunStatus(run: RunSnapshot): RunStatus {
76
+ if (run.status !== "completed") return run.status;
77
+ if (structuredRecoverableWorkflowFailure(run) !== undefined) return "blocked";
78
+ const returnedStatus = normalizeReturnedWorkflowStatus(run.result?.["status"]);
79
+ if (returnedStatus === "failed") return "failed";
80
+ if (returnedStatus !== undefined && isReturnedBlockedWorkflowStatus(returnedStatus)) return "blocked";
81
+ return run.status;
82
+ }
83
+
84
+ function structuredRecoverableFailureFromSnapshot(
85
+ snapshot: Pick<RunSnapshot | StageSnapshot, "error" | "failureKind" | "failureCode" | "failureRecoverability" | "failureDisposition" | "failureMessage" | "retryAfterMs">,
86
+ failedStageId?: string,
87
+ ): StructuredRecoverableWorkflowFailure | undefined {
88
+ if (snapshot.failureDisposition !== "active_blocked") return undefined;
89
+ if (snapshot.failureRecoverability !== "recoverable") return undefined;
90
+ if (!isRecoverableProviderFailureKind(snapshot.failureKind)) return undefined;
91
+ const error = snapshot.error ?? snapshot.failureMessage ?? "Workflow is blocked by a recoverable provider failure.";
92
+ return {
93
+ error,
94
+ metadata: {
95
+ failureKind: snapshot.failureKind,
96
+ ...(snapshot.failureCode !== undefined ? { failureCode: snapshot.failureCode } : {}),
97
+ failureRecoverability: snapshot.failureRecoverability,
98
+ failureDisposition: snapshot.failureDisposition,
99
+ failureMessage: snapshot.failureMessage ?? error,
100
+ ...(failedStageId !== undefined ? { failedStageId } : {}),
101
+ resumable: true,
102
+ ...(snapshot.retryAfterMs !== undefined ? { retryAfterMs: snapshot.retryAfterMs } : {}),
103
+ },
104
+ };
105
+ }
106
+
107
+ function isRecoverableProviderFailureKind(kind: WorkflowFailureKind | undefined): kind is "auth" | "rate_limit" | "provider" {
108
+ return kind === "auth" || kind === "rate_limit" || kind === "provider";
109
+ }
110
+
111
+
112
+
113
+ function stringResultField(result: WorkflowOutputValues | undefined, key: string): string | undefined {
114
+ const value = result?.[key];
115
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
116
+ }
@@ -186,6 +186,8 @@ const PROVIDER_UNAVAILABLE_PHRASES: readonly TokenMatch[] = [
186
186
  ["provider", "error"],
187
187
  ["model", "unavailable"],
188
188
  ["503"],
189
+ ["git", "command", "timed", "out"],
190
+ ["spawnsync", "git", "etimedout"],
189
191
  ];
190
192
 
191
193
  const AUTH_CONTEXT = new Set([
@@ -5,6 +5,7 @@ import {
5
5
  OVERLAY_LINE_COUNT,
6
6
  OVERLAY_VERTICAL_MARGIN_ROWS,
7
7
  } from "./graph-view-constants.js";
8
+ import { WORKFLOW_STATUS_KEY } from "./workflow-status.js";
8
9
  import { GraphViewState } from "./graph-view-state.js";
9
10
  import { renderOutlinePill } from "./header.js";
10
11
  import { NODE_H, NODE_W } from "./layout.js";
@@ -78,12 +79,25 @@ export abstract class GraphViewRenderHelpers extends GraphViewState {
78
79
  * the header band: `backgroundPanel` chrome, outlined accent pill
79
80
  * flush-left, hints flowing right on the centre row.
80
81
  */
82
+ protected _externalStatusText(): string | null {
83
+ const entries = Array.from(this.footerData?.getExtensionStatuses() ?? [])
84
+ .filter(([key, value]) => (
85
+ value.trim().length > 0 &&
86
+ key !== WORKFLOW_STATUS_KEY &&
87
+ !key.startsWith(`${WORKFLOW_STATUS_KEY}:`)
88
+ ))
89
+ .sort(([a], [b]) => a.localeCompare(b));
90
+ if (entries.length === 0) return null;
91
+ return entries.map(([, value]) => value.trim()).join(" · ");
92
+ }
93
+
81
94
  protected _renderStatusline(width: number): string[] {
82
95
  const t = this.graphTheme;
83
96
  const chromeBg = hexBg(t.backgroundPanel);
84
97
  const text = hexToAnsi(t.text);
85
98
  const muted = hexToAnsi(t.textMuted);
86
99
  const dim = hexToAnsi(t.dim);
100
+ const accent = hexToAnsi(t.accent);
87
101
 
88
102
  const {
89
103
  top,
@@ -92,13 +106,17 @@ export abstract class GraphViewRenderHelpers extends GraphViewState {
92
106
  visibleWidth: pillW,
93
107
  } = renderOutlinePill(MODE_PILL_LABEL, t.accent, chromeBg);
94
108
 
95
- // Hints — `<key> <label>` separated by ` · `.
109
+ // Hints — `<key> <label>` separated by ` · `. When other extensions
110
+ // publish status text (for example the async subagent widget), include it
111
+ // ahead of the controls so fullscreen workflow overlays do not hide it.
96
112
  const sep = `${chromeBg} ${dim}·${RESET}${chromeBg} `;
97
- const segments = HINT_KEYS.map(
113
+ const statusText = this._externalStatusText();
114
+ const statusSegment = statusText ? [`${accent}${statusText}${RESET}${chromeBg}`] : [];
115
+ const hintSegments = HINT_KEYS.map(
98
116
  ({ key, label }) =>
99
117
  `${text}${BOLD}${key}${RESET}${chromeBg} ${muted}${label}${RESET}${chromeBg}`,
100
118
  );
101
- const hintsStyledRaw = segments.join(sep);
119
+ const hintsStyledRaw = [...statusSegment, ...hintSegments].join(sep);
102
120
 
103
121
  const leftEdgePad = 1;
104
122
  const rightEdgePad = 2;
@@ -1,3 +1,4 @@
1
+ import type { ReadonlyFooterDataProvider } from "@bastani/atomic";
1
2
  import type { Store } from "../shared/store.js";
2
3
  import type {
3
4
  PendingPrompt,
@@ -63,6 +64,7 @@ export abstract class GraphViewState {
63
64
  protected getViewportRows?: () => number | undefined;
64
65
  protected requestRender?: () => void;
65
66
  protected piKeybindings?: unknown;
67
+ protected footerData?: ReadonlyFooterDataProvider;
66
68
 
67
69
  /** Active HIL prompt state, set when `_rebuildLayout` sees a new prompt id. */
68
70
  protected promptState: PromptCardState | null = null;
@@ -102,6 +104,7 @@ export abstract class GraphViewState {
102
104
  this.getViewportRows = opts.getViewportRows;
103
105
  this.requestRender = opts.requestRender;
104
106
  this.piKeybindings = opts.piKeybindings;
107
+ this.footerData = opts.footerData;
105
108
 
106
109
  this._unsubscribe = this.store.subscribe((snap) => {
107
110
  this.currentSnapshot = snap;
@@ -1,3 +1,4 @@
1
+ import type { ReadonlyFooterDataProvider } from "@bastani/atomic";
1
2
  import type { Store } from "../shared/store.js";
2
3
  import type { GraphTheme } from "./graph-theme.js";
3
4
 
@@ -66,4 +67,6 @@ export interface GraphViewOpts {
66
67
  requestRender?: () => void;
67
68
  /** Host Pi keybindings manager used by run-level prompt cards. */
68
69
  piKeybindings?: unknown;
70
+ /** Host footer/status provider used to surface non-workflow extension statuses inside the fullscreen graph overlay. */
71
+ footerData?: ReadonlyFooterDataProvider;
69
72
  }
@@ -27,6 +27,7 @@
27
27
  */
28
28
 
29
29
  import type { RunSnapshot, StageSnapshot, StageStatus } from "../shared/store-types.js";
30
+ import { effectiveRunStatus } from "../shared/returned-run-status.js";
30
31
  import { elapsedRunMs, elapsedStageMs } from "../shared/timing.js";
31
32
  import type { GraphTheme } from "./graph-theme.js";
32
33
  import { fmtDuration } from "./status-helpers.js";
@@ -162,7 +163,7 @@ function renderRunEntry(
162
163
  function runAccent(run: RunSnapshot, theme?: GraphTheme): string {
163
164
  if (!theme) return "#000000";
164
165
  if (isQuitRun(run)) return theme.warning;
165
- switch (run.status) {
166
+ switch (effectiveRunStatus(run)) {
166
167
  case "completed": return theme.success;
167
168
  case "running": return theme.warning;
168
169
  case "paused": return theme.warning;
@@ -178,7 +179,7 @@ function runAccent(run: RunSnapshot, theme?: GraphTheme): string {
178
179
 
179
180
  function runTrailing(run: RunSnapshot, theme?: GraphTheme): { text: string; fg?: string } | undefined {
180
181
  if (isQuitRun(run)) return { text: "○ quit", fg: theme?.warning };
181
- switch (run.status) {
182
+ switch (effectiveRunStatus(run)) {
182
183
  case "completed": return { text: "✓ completed", fg: theme?.success };
183
184
  case "running": return { text: "● running", fg: theme?.warning };
184
185
  case "paused": return { text: "❚❚ paused", fg: theme?.warning };
@@ -212,7 +213,7 @@ function runCardMeta(run: RunSnapshot, now: number): string {
212
213
  : undefined;
213
214
 
214
215
  if (isQuitRun(run)) return "resumable via /workflow resume";
215
- if (run.status === "running") {
216
+ if (effectiveRunStatus(run) === "running") {
216
217
  if (isChain) parts.push(`${done}/${total}`);
217
218
  const labels = runningStageLabels(run);
218
219
  if (labels) parts.push(labels);
@@ -220,7 +221,7 @@ function runCardMeta(run: RunSnapshot, now: number): string {
220
221
  return parts.join(" · ");
221
222
  }
222
223
 
223
- if (run.status === "paused") {
224
+ if (effectiveRunStatus(run) === "paused") {
224
225
  if (isChain) parts.push(`${done}/${total}`);
225
226
  const labels = pausedStageLabels(run);
226
227
  if (labels) parts.push(labels);
@@ -228,7 +229,7 @@ function runCardMeta(run: RunSnapshot, now: number): string {
228
229
  return parts.join(" · ");
229
230
  }
230
231
 
231
- if (run.status === "failed" || run.status === "killed") {
232
+ if (effectiveRunStatus(run) === "failed" || effectiveRunStatus(run) === "killed") {
232
233
  const failed = run.stages.find((s) => s.status === "failed");
233
234
  if (failed && isChain) parts.push(`failed at ${failed.name}`);
234
235
  else if (failed) parts.push(failed.name);
@@ -239,7 +240,7 @@ function runCardMeta(run: RunSnapshot, now: number): string {
239
240
  return parts.join(" · ");
240
241
  }
241
242
 
242
- if (run.status === "completed" || run.status === "skipped" || run.status === "cancelled" || run.status === "blocked") {
243
+ if (["completed", "skipped", "cancelled", "blocked"].includes(effectiveRunStatus(run))) {
243
244
  if (run.exitReason !== undefined && run.exitReason.length > 0) parts.push(run.exitReason);
244
245
  if (!isChain && run.stages[0]) parts.push(run.stages[0].name);
245
246
  const dur = lastStageDuration(run, now);
@@ -292,7 +293,7 @@ function stageCells(run: RunSnapshot): Array<{ status: StageStatus }> {
292
293
  }
293
294
 
294
295
  function stageStatusFromRun(run: RunSnapshot): StageStatus {
295
- switch (run.status) {
296
+ switch (effectiveRunStatus(run)) {
296
297
  case "completed": return "completed";
297
298
  case "skipped": return "skipped";
298
299
  case "cancelled": return "skipped";
@@ -324,22 +325,26 @@ interface Counts {
324
325
  paused: number;
325
326
  quit: number;
326
327
  completed: number;
328
+ blocked: number;
327
329
  failed: number;
328
330
  pending: number;
329
331
  }
330
332
 
331
333
  function countBuckets(runs: readonly RunSnapshot[]): Counts {
332
- const c: Counts = { active: 0, paused: 0, quit: 0, completed: 0, failed: 0, pending: 0 };
334
+ const c: Counts = { active: 0, paused: 0, quit: 0, completed: 0, blocked: 0, failed: 0, pending: 0 };
333
335
  for (const r of runs) {
336
+ const status = effectiveRunStatus(r);
334
337
  if (isQuitRun(r)) c.quit++;
335
338
  else if (r.endedAt === undefined) {
336
- if (r.status === "pending") c.pending++;
337
- else if (r.status === "paused") c.paused++;
338
- else if (r.status === "running") c.active++;
339
- else if (r.status === "completed") c.completed++;
339
+ if (status === "pending") c.pending++;
340
+ else if (status === "paused") c.paused++;
341
+ else if (status === "running") c.active++;
342
+ else if (status === "completed") c.completed++;
343
+ else if (status === "blocked") c.blocked++;
340
344
  else c.failed++;
341
- } else if (r.status === "completed") c.completed++;
342
- else if (r.status === "skipped" || r.status === "cancelled" || r.status === "blocked") c.completed++;
345
+ } else if (status === "completed") c.completed++;
346
+ else if (status === "blocked") c.blocked++;
347
+ else if (status === "skipped" || status === "cancelled") c.completed++;
343
348
  else c.failed++;
344
349
  }
345
350
  return c;
@@ -348,6 +353,7 @@ function countBuckets(runs: readonly RunSnapshot[]): Counts {
348
353
  function themedBadges(c: Counts, theme: GraphTheme): FlatBandBadge[] {
349
354
  const out: FlatBandBadge[] = [];
350
355
  if (c.completed > 0) out.push({ text: `✓ ${c.completed}`, fg: theme.success });
356
+ if (c.blocked > 0) out.push({ text: `↑ ${c.blocked} blocked`, fg: theme.warning });
351
357
  if (c.active > 0) out.push({ text: `● ${c.active}`, fg: theme.warning });
352
358
  // Keep the word label: the pause glyph is less familiar than the other
353
359
  // status glyphs, so this intentional asymmetry improves scanability.
@@ -361,6 +367,7 @@ function themedBadges(c: Counts, theme: GraphTheme): FlatBandBadge[] {
361
367
  function plainBadges(c: Counts): FlatBandBadge[] {
362
368
  const out: FlatBandBadge[] = [];
363
369
  if (c.completed > 0) out.push({ text: `✓ ${c.completed}` });
370
+ if (c.blocked > 0) out.push({ text: `↑ ${c.blocked} blocked` });
364
371
  if (c.active > 0) out.push({ text: `● ${c.active}` });
365
372
  // Keep the word label: the pause glyph is less familiar than the other
366
373
  // status glyphs, so this intentional asymmetry improves scanability.
@@ -393,7 +400,7 @@ function emptyStateLine(theme?: GraphTheme): string {
393
400
 
394
401
  function statusIconForRun(run: RunSnapshot): string {
395
402
  if (isQuitRun(run)) return "○";
396
- switch (run.status) {
403
+ switch (effectiveRunStatus(run)) {
397
404
  case "completed": return "✓";
398
405
  case "skipped": return "⊘";
399
406
  case "cancelled": return "⊘";
@@ -32,10 +32,9 @@ import type { StageSnapshot, StoreSnapshot } from "../shared/store-types.js";
32
32
  import { expandWorkflowGraph } from "../shared/expanded-workflow-graph.js";
33
33
  import { WORKFLOW_STATUS_KEY } from "./workflow-status.js";
34
34
  /**
35
- * Surface used to write Pi's footer/status tag while the attach pane is
36
- * mounted. Passing `undefined` clears the slot required on dispose so
37
- * the `pi-workflows/<workflow>[/<stage>]` tag does NOT linger in every
38
- * subsequent chat message after the overlay is closed.
35
+ * Surface for the overlay footer/status tag. Passing `undefined` on dispose
36
+ * prevents `pi-workflows/<workflow>[/<stage>]` from lingering in later chat
37
+ * messages after the overlay closes.
39
38
  * cross-ref: @bastani/atomic docs/extensions.md
40
39
  * §Widgets, Status, and Footer (`ctx.ui.setStatus`).
41
40
  */
@@ -134,6 +133,7 @@ export class WorkflowAttachPane implements Component {
134
133
  initialFocusedStageId,
135
134
  getViewportRows: this.getViewportRows,
136
135
  piKeybindings: this.piKeybindings,
136
+ footerData: this.footerData,
137
137
  requestRender: () => {
138
138
  if (this.mode !== "graph") return;
139
139
  this.hostRequestRender?.();
@@ -1,7 +1,18 @@
1
1
  import type { AssistantMessage } from "@earendil-works/pi-ai/compat";
2
2
  import type { AgentSessionInternalSurface as AgentSession } from "./agent-session-methods.ts";
3
+ /**
4
+ * Upper bound on consecutive automatic continuations of a response that was
5
+ * truncated at the output-token cap ("length") while the context is still
6
+ * below the compaction budget. Each continuation regenerates the cut-off turn,
7
+ * so a model that insists on emitting more than its per-turn output cap can
8
+ * still terminate instead of looping forever.
9
+ */
10
+ export declare const MAX_LENGTH_CONTINUATION_ATTEMPTS = 3;
11
+ export declare const MAX_OUTPUT_BUDGET_ERROR_CONTINUATION_ATTEMPTS = 1;
3
12
  export declare function _checkCompaction(this: AgentSession, assistantMessage: AssistantMessage, skipAbortedCheck?: boolean): Promise<void>;
4
13
  export declare function _isCopilotServerCapBelowSelectedContextWindow(this: AgentSession, assistantMessage: AssistantMessage): boolean;
14
+ export declare function isRetryWorthyLengthStop(assistantMessage: AssistantMessage): boolean;
15
+ export declare function isRetryWorthyOutputBudgetError(assistantMessage: AssistantMessage): boolean;
5
16
  export declare function shouldRetryAfterThresholdCompaction(assistantMessage: AssistantMessage): boolean;
6
17
  /**
7
18
  * Internal: remove an incomplete assistant from retry context before auto-continuing after compaction.
@@ -10,23 +21,32 @@ export declare function _dropTrailingAutoCompactionRetryAssistantIfPresent(this:
10
21
  /**
11
22
  * Internal: schedule a live post-event continuation probe after compaction_end listeners can flush queues.
12
23
  */
13
- export declare function _schedulePostAutoCompactionContinuationProbe(this: AgentSession, reason: "overflow" | "threshold", willRetry: boolean): void;
14
- export declare function _awaitPendingOverflowPostCompactionContinuation(this: AgentSession): Promise<void>;
24
+ export declare function _schedulePostAutoCompactionContinuationProbe(this: AgentSession, _reason: "overflow" | "threshold", willRetry: boolean): void;
25
+ export declare function _awaitPendingPostCompactionContinuation(this: AgentSession): Promise<void>;
15
26
  /**
16
27
  * Internal: resume generation after successful auto-compaction only when active work remains.
17
28
  */
18
29
  export declare function _resumeAfterAutoCompaction(this: AgentSession): Promise<void>;
30
+ /**
31
+ * Internal: resume a response that was truncated at the output-token cap
32
+ * ("length") when the context does not warrant compaction. The generation is
33
+ * continued directly so the model finishes the work it was cut off from, rather
34
+ * than dead-ending on the truncation. Bounded by MAX_LENGTH_CONTINUATION_ATTEMPTS
35
+ * so a turn that keeps exceeding the per-turn output cap can still terminate.
36
+ */
37
+ export declare function _resumeAfterLengthTruncation(this: AgentSession): void;
19
38
  export declare function _runAutoCompaction(this: AgentSession, reason: "overflow" | "threshold", willRetry: boolean): Promise<void>;
20
39
  /**
21
40
  * Toggle auto-compaction setting.
22
41
  */
23
42
  export declare const agentSessionAutoCompactionMethods: {
24
- _awaitPendingOverflowPostCompactionContinuation: typeof _awaitPendingOverflowPostCompactionContinuation;
43
+ _awaitPendingPostCompactionContinuation: typeof _awaitPendingPostCompactionContinuation;
25
44
  _checkCompaction: typeof _checkCompaction;
26
45
  _isCopilotServerCapBelowSelectedContextWindow: typeof _isCopilotServerCapBelowSelectedContextWindow;
27
46
  _dropTrailingAutoCompactionRetryAssistantIfPresent: typeof _dropTrailingAutoCompactionRetryAssistantIfPresent;
28
47
  _schedulePostAutoCompactionContinuationProbe: typeof _schedulePostAutoCompactionContinuationProbe;
29
48
  _resumeAfterAutoCompaction: typeof _resumeAfterAutoCompaction;
49
+ _resumeAfterLengthTruncation: typeof _resumeAfterLengthTruncation;
30
50
  _runAutoCompaction: typeof _runAutoCompaction;
31
51
  };
32
52
  //# sourceMappingURL=agent-session-auto-compaction.d.ts.map