@ttfw/envoi 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/README.md +238 -0
  2. package/dist/commands/app.d.ts +2 -0
  3. package/dist/commands/app.d.ts.map +1 -0
  4. package/dist/commands/app.js +31 -0
  5. package/dist/commands/app.js.map +1 -0
  6. package/dist/commands/autonomy.d.ts +6 -0
  7. package/dist/commands/autonomy.d.ts.map +1 -0
  8. package/dist/commands/autonomy.js +89 -0
  9. package/dist/commands/autonomy.js.map +1 -0
  10. package/dist/commands/builder.d.ts +13 -0
  11. package/dist/commands/builder.d.ts.map +1 -0
  12. package/dist/commands/builder.js +142 -0
  13. package/dist/commands/builder.js.map +1 -0
  14. package/dist/commands/idea.d.ts +12 -0
  15. package/dist/commands/idea.d.ts.map +1 -0
  16. package/dist/commands/idea.js +79 -0
  17. package/dist/commands/idea.js.map +1 -0
  18. package/dist/commands/init.d.ts +18 -0
  19. package/dist/commands/init.d.ts.map +1 -0
  20. package/dist/commands/init.js +423 -0
  21. package/dist/commands/init.js.map +1 -0
  22. package/dist/commands/mode.d.ts +13 -0
  23. package/dist/commands/mode.d.ts.map +1 -0
  24. package/dist/commands/mode.js +96 -0
  25. package/dist/commands/mode.js.map +1 -0
  26. package/dist/commands/onboard.d.ts +37 -0
  27. package/dist/commands/onboard.d.ts.map +1 -0
  28. package/dist/commands/onboard.js +743 -0
  29. package/dist/commands/onboard.js.map +1 -0
  30. package/dist/commands/pr-note.d.ts +8 -0
  31. package/dist/commands/pr-note.d.ts.map +1 -0
  32. package/dist/commands/pr-note.js +27 -0
  33. package/dist/commands/pr-note.js.map +1 -0
  34. package/dist/commands/undo.d.ts +7 -0
  35. package/dist/commands/undo.d.ts.map +1 -0
  36. package/dist/commands/undo.js +59 -0
  37. package/dist/commands/undo.js.map +1 -0
  38. package/dist/commands/update.d.ts +24 -0
  39. package/dist/commands/update.d.ts.map +1 -0
  40. package/dist/commands/update.js +248 -0
  41. package/dist/commands/update.js.map +1 -0
  42. package/dist/constants/report_codes.d.ts +29 -0
  43. package/dist/constants/report_codes.d.ts.map +1 -0
  44. package/dist/constants/report_codes.js +69 -0
  45. package/dist/constants/report_codes.js.map +1 -0
  46. package/dist/index.d.ts +3 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +675 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/lib/autonomy.d.ts +16 -0
  51. package/dist/lib/autonomy.d.ts.map +1 -0
  52. package/dist/lib/autonomy.js +38 -0
  53. package/dist/lib/autonomy.js.map +1 -0
  54. package/dist/lib/blocked.d.ts +87 -0
  55. package/dist/lib/blocked.d.ts.map +1 -0
  56. package/dist/lib/blocked.js +134 -0
  57. package/dist/lib/blocked.js.map +1 -0
  58. package/dist/lib/branding.d.ts +13 -0
  59. package/dist/lib/branding.d.ts.map +1 -0
  60. package/dist/lib/branding.js +19 -0
  61. package/dist/lib/branding.js.map +1 -0
  62. package/dist/lib/claude.d.ts +42 -0
  63. package/dist/lib/claude.d.ts.map +1 -0
  64. package/dist/lib/claude.js +291 -0
  65. package/dist/lib/claude.js.map +1 -0
  66. package/dist/lib/config.d.ts +71 -0
  67. package/dist/lib/config.d.ts.map +1 -0
  68. package/dist/lib/config.js +410 -0
  69. package/dist/lib/config.js.map +1 -0
  70. package/dist/lib/diff.d.ts +150 -0
  71. package/dist/lib/diff.d.ts.map +1 -0
  72. package/dist/lib/diff.js +257 -0
  73. package/dist/lib/diff.js.map +1 -0
  74. package/dist/lib/doctor.d.ts +67 -0
  75. package/dist/lib/doctor.d.ts.map +1 -0
  76. package/dist/lib/doctor.js +211 -0
  77. package/dist/lib/doctor.js.map +1 -0
  78. package/dist/lib/fingerprint.d.ts +27 -0
  79. package/dist/lib/fingerprint.d.ts.map +1 -0
  80. package/dist/lib/fingerprint.js +116 -0
  81. package/dist/lib/fingerprint.js.map +1 -0
  82. package/dist/lib/fs.d.ts +93 -0
  83. package/dist/lib/fs.d.ts.map +1 -0
  84. package/dist/lib/fs.js +179 -0
  85. package/dist/lib/fs.js.map +1 -0
  86. package/dist/lib/git.d.ts +177 -0
  87. package/dist/lib/git.d.ts.map +1 -0
  88. package/dist/lib/git.js +355 -0
  89. package/dist/lib/git.js.map +1 -0
  90. package/dist/lib/git_branching.d.ts +84 -0
  91. package/dist/lib/git_branching.d.ts.map +1 -0
  92. package/dist/lib/git_branching.js +327 -0
  93. package/dist/lib/git_branching.js.map +1 -0
  94. package/dist/lib/gitignore.d.ts +26 -0
  95. package/dist/lib/gitignore.d.ts.map +1 -0
  96. package/dist/lib/gitignore.js +119 -0
  97. package/dist/lib/gitignore.js.map +1 -0
  98. package/dist/lib/guardrails.d.ts +232 -0
  99. package/dist/lib/guardrails.d.ts.map +1 -0
  100. package/dist/lib/guardrails.js +323 -0
  101. package/dist/lib/guardrails.js.map +1 -0
  102. package/dist/lib/history.d.ts +110 -0
  103. package/dist/lib/history.d.ts.map +1 -0
  104. package/dist/lib/history.js +236 -0
  105. package/dist/lib/history.js.map +1 -0
  106. package/dist/lib/index.d.ts +29 -0
  107. package/dist/lib/index.d.ts.map +1 -0
  108. package/dist/lib/index.js +29 -0
  109. package/dist/lib/index.js.map +1 -0
  110. package/dist/lib/json-extract.d.ts +42 -0
  111. package/dist/lib/json-extract.d.ts.map +1 -0
  112. package/dist/lib/json-extract.js +201 -0
  113. package/dist/lib/json-extract.js.map +1 -0
  114. package/dist/lib/judge.d.ts +237 -0
  115. package/dist/lib/judge.d.ts.map +1 -0
  116. package/dist/lib/judge.js +501 -0
  117. package/dist/lib/judge.js.map +1 -0
  118. package/dist/lib/lock.d.ts +79 -0
  119. package/dist/lib/lock.d.ts.map +1 -0
  120. package/dist/lib/lock.js +254 -0
  121. package/dist/lib/lock.js.map +1 -0
  122. package/dist/lib/migration.d.ts +9 -0
  123. package/dist/lib/migration.d.ts.map +1 -0
  124. package/dist/lib/migration.js +74 -0
  125. package/dist/lib/migration.js.map +1 -0
  126. package/dist/lib/paths.d.ts +18 -0
  127. package/dist/lib/paths.d.ts.map +1 -0
  128. package/dist/lib/paths.js +27 -0
  129. package/dist/lib/paths.js.map +1 -0
  130. package/dist/lib/preflight.d.ts +33 -0
  131. package/dist/lib/preflight.d.ts.map +1 -0
  132. package/dist/lib/preflight.js +177 -0
  133. package/dist/lib/preflight.js.map +1 -0
  134. package/dist/lib/prompt_budget.d.ts +18 -0
  135. package/dist/lib/prompt_budget.d.ts.map +1 -0
  136. package/dist/lib/prompt_budget.js +36 -0
  137. package/dist/lib/prompt_budget.js.map +1 -0
  138. package/dist/lib/report.d.ts +102 -0
  139. package/dist/lib/report.d.ts.map +1 -0
  140. package/dist/lib/report.js +347 -0
  141. package/dist/lib/report.js.map +1 -0
  142. package/dist/lib/reviewer-flow.d.ts +80 -0
  143. package/dist/lib/reviewer-flow.d.ts.map +1 -0
  144. package/dist/lib/reviewer-flow.js +138 -0
  145. package/dist/lib/reviewer-flow.js.map +1 -0
  146. package/dist/lib/reviewer.d.ts +53 -0
  147. package/dist/lib/reviewer.d.ts.map +1 -0
  148. package/dist/lib/reviewer.js +199 -0
  149. package/dist/lib/reviewer.js.map +1 -0
  150. package/dist/lib/risk.d.ts +127 -0
  151. package/dist/lib/risk.d.ts.map +1 -0
  152. package/dist/lib/risk.js +192 -0
  153. package/dist/lib/risk.js.map +1 -0
  154. package/dist/lib/rollback.d.ts +143 -0
  155. package/dist/lib/rollback.d.ts.map +1 -0
  156. package/dist/lib/rollback.js +244 -0
  157. package/dist/lib/rollback.js.map +1 -0
  158. package/dist/lib/schema.d.ts +47 -0
  159. package/dist/lib/schema.d.ts.map +1 -0
  160. package/dist/lib/schema.js +91 -0
  161. package/dist/lib/schema.js.map +1 -0
  162. package/dist/lib/scope.d.ts +89 -0
  163. package/dist/lib/scope.d.ts.map +1 -0
  164. package/dist/lib/scope.js +135 -0
  165. package/dist/lib/scope.js.map +1 -0
  166. package/dist/lib/self_update.d.ts +13 -0
  167. package/dist/lib/self_update.d.ts.map +1 -0
  168. package/dist/lib/self_update.js +172 -0
  169. package/dist/lib/self_update.js.map +1 -0
  170. package/dist/lib/state.d.ts +143 -0
  171. package/dist/lib/state.d.ts.map +1 -0
  172. package/dist/lib/state.js +258 -0
  173. package/dist/lib/state.js.map +1 -0
  174. package/dist/lib/tick.d.ts +310 -0
  175. package/dist/lib/tick.d.ts.map +1 -0
  176. package/dist/lib/tick.js +424 -0
  177. package/dist/lib/tick.js.map +1 -0
  178. package/dist/lib/transport.d.ts +145 -0
  179. package/dist/lib/transport.d.ts.map +1 -0
  180. package/dist/lib/transport.js +237 -0
  181. package/dist/lib/transport.js.map +1 -0
  182. package/dist/lib/verdict_labels.d.ts +5 -0
  183. package/dist/lib/verdict_labels.d.ts.map +1 -0
  184. package/dist/lib/verdict_labels.js +25 -0
  185. package/dist/lib/verdict_labels.js.map +1 -0
  186. package/dist/lib/verify-safety.d.ts +63 -0
  187. package/dist/lib/verify-safety.d.ts.map +1 -0
  188. package/dist/lib/verify-safety.js +123 -0
  189. package/dist/lib/verify-safety.js.map +1 -0
  190. package/dist/lib/verify.d.ts +139 -0
  191. package/dist/lib/verify.d.ts.map +1 -0
  192. package/dist/lib/verify.js +311 -0
  193. package/dist/lib/verify.js.map +1 -0
  194. package/dist/lib/workspace_state.d.ts +79 -0
  195. package/dist/lib/workspace_state.d.ts.map +1 -0
  196. package/dist/lib/workspace_state.js +283 -0
  197. package/dist/lib/workspace_state.js.map +1 -0
  198. package/dist/runner/builder.d.ts +58 -0
  199. package/dist/runner/builder.d.ts.map +1 -0
  200. package/dist/runner/builder.js +775 -0
  201. package/dist/runner/builder.js.map +1 -0
  202. package/dist/runner/builder_parse.d.ts +37 -0
  203. package/dist/runner/builder_parse.d.ts.map +1 -0
  204. package/dist/runner/builder_parse.js +76 -0
  205. package/dist/runner/builder_parse.js.map +1 -0
  206. package/dist/runner/index.d.ts +9 -0
  207. package/dist/runner/index.d.ts.map +1 -0
  208. package/dist/runner/index.js +7 -0
  209. package/dist/runner/index.js.map +1 -0
  210. package/dist/runner/loop.d.ts +51 -0
  211. package/dist/runner/loop.d.ts.map +1 -0
  212. package/dist/runner/loop.js +221 -0
  213. package/dist/runner/loop.js.map +1 -0
  214. package/dist/runner/orchestrator.d.ts +67 -0
  215. package/dist/runner/orchestrator.d.ts.map +1 -0
  216. package/dist/runner/orchestrator.js +376 -0
  217. package/dist/runner/orchestrator.js.map +1 -0
  218. package/dist/runner/tick.d.ts +10 -0
  219. package/dist/runner/tick.d.ts.map +1 -0
  220. package/dist/runner/tick.js +1639 -0
  221. package/dist/runner/tick.js.map +1 -0
  222. package/dist/types/blocked.d.ts +52 -0
  223. package/dist/types/blocked.d.ts.map +1 -0
  224. package/dist/types/blocked.js +8 -0
  225. package/dist/types/blocked.js.map +1 -0
  226. package/dist/types/builder.d.ts +25 -0
  227. package/dist/types/builder.d.ts.map +1 -0
  228. package/dist/types/builder.js +7 -0
  229. package/dist/types/builder.js.map +1 -0
  230. package/dist/types/claude.d.ts +86 -0
  231. package/dist/types/claude.d.ts.map +1 -0
  232. package/dist/types/claude.js +48 -0
  233. package/dist/types/claude.js.map +1 -0
  234. package/dist/types/config.d.ts +384 -0
  235. package/dist/types/config.d.ts.map +1 -0
  236. package/dist/types/config.js +7 -0
  237. package/dist/types/config.js.map +1 -0
  238. package/dist/types/index.d.ts +18 -0
  239. package/dist/types/index.d.ts.map +1 -0
  240. package/dist/types/index.js +8 -0
  241. package/dist/types/index.js.map +1 -0
  242. package/dist/types/lock.d.ts +21 -0
  243. package/dist/types/lock.d.ts.map +1 -0
  244. package/dist/types/lock.js +8 -0
  245. package/dist/types/lock.js.map +1 -0
  246. package/dist/types/preflight.d.ts +49 -0
  247. package/dist/types/preflight.d.ts.map +1 -0
  248. package/dist/types/preflight.js +8 -0
  249. package/dist/types/preflight.js.map +1 -0
  250. package/dist/types/report.d.ts +161 -0
  251. package/dist/types/report.d.ts.map +1 -0
  252. package/dist/types/report.js +8 -0
  253. package/dist/types/report.js.map +1 -0
  254. package/dist/types/reviewer.d.ts +66 -0
  255. package/dist/types/reviewer.d.ts.map +1 -0
  256. package/dist/types/reviewer.js +5 -0
  257. package/dist/types/reviewer.js.map +1 -0
  258. package/dist/types/state.d.ts +124 -0
  259. package/dist/types/state.d.ts.map +1 -0
  260. package/dist/types/state.js +20 -0
  261. package/dist/types/state.js.map +1 -0
  262. package/dist/types/task.d.ts +117 -0
  263. package/dist/types/task.d.ts.map +1 -0
  264. package/dist/types/task.js +7 -0
  265. package/dist/types/task.js.map +1 -0
  266. package/dist/types/workspace_state.d.ts +125 -0
  267. package/dist/types/workspace_state.d.ts.map +1 -0
  268. package/dist/types/workspace_state.js +10 -0
  269. package/dist/types/workspace_state.js.map +1 -0
  270. package/envoi.config.json +191 -0
  271. package/package.json +52 -0
  272. package/relais/prompts/.gitkeep +0 -0
  273. package/relais/prompts/builder.system.txt +13 -0
  274. package/relais/prompts/builder.user.txt +15 -0
  275. package/relais/prompts/orchestrator.system.txt +37 -0
  276. package/relais/prompts/orchestrator.user.txt +34 -0
  277. package/relais/prompts/reviewer.system.txt +33 -0
  278. package/relais/prompts/reviewer.user.txt +35 -0
  279. package/relais/schemas/.gitkeep +0 -0
  280. package/relais/schemas/builder_result.schema.json +29 -0
  281. package/relais/schemas/report.schema.json +195 -0
  282. package/relais/schemas/reviewer_result.schema.json +70 -0
  283. package/relais/schemas/task.schema.json +155 -0
@@ -0,0 +1,424 @@
1
+ /**
2
+ * Tick runner with transport stall handling and retry policy.
3
+ *
4
+ * Provides functions to handle transport stalls during ORCHESTRATE or BUILD phases.
5
+ * When a stall is detected:
6
+ * 1. Check if repo is dirty
7
+ * 2. Rollback if needed
8
+ * 3. Return BLOCKED_TRANSPORT_STALLED with evidence
9
+ *
10
+ * Retry policy (M21):
11
+ * - Attempt 1: Retry same task unchanged
12
+ * - Attempt 2: Retry with degraded settings
13
+ * - Attempt 3+: Block and require human action
14
+ */
15
+ import { rollbackToCommit } from './rollback.js';
16
+ import { isWorktreeClean } from './git.js';
17
+ import { resetRetryState, recordTransportStall } from './state.js';
18
+ /**
19
+ * Maximum retry attempts before blocking.
20
+ */
21
+ export const MAX_RETRY_ATTEMPTS = 3;
22
+ /**
23
+ * Recovery prompt to prepend when retrying after a transport stall.
24
+ *
25
+ * This prompt reminds the model that previous work may have been lost
26
+ * and to read files before making assumptions about their state.
27
+ */
28
+ export const RECOVERY_PROMPT = `IMPORTANT: This is a retry after a transport stall. The previous attempt may have been interrupted.
29
+
30
+ Do NOT assume your previous edits were applied. The connection may have stalled:
31
+ - Before any changes were made
32
+ - In the middle of changes (partial writes)
33
+ - After changes were made but before confirmation
34
+
35
+ BEFORE making any edits:
36
+ 1. Read the relevant files to verify their current state
37
+ 2. Check if your intended changes already exist
38
+ 3. Proceed based on the actual file contents, not your memory of what you tried to do
39
+
40
+ If the task appears completed, verify and report success. If not, complete it from the current state.`;
41
+ /**
42
+ * Returns the recovery prompt to prepend when retrying.
43
+ *
44
+ * The recovery prompt is used on any retry (retry_unchanged or retry_degraded)
45
+ * to remind the model that previous work may have been lost.
46
+ *
47
+ * @param retryCount - Current retry count (0 = first attempt, no recovery prompt)
48
+ * @returns Recovery prompt string if retrying, empty string for first attempt
49
+ */
50
+ export function getRecoveryPrompt(retryCount) {
51
+ if (retryCount < 1) {
52
+ return '';
53
+ }
54
+ return RECOVERY_PROMPT;
55
+ }
56
+ /**
57
+ * Builds the full prompt with optional recovery prefix.
58
+ *
59
+ * If retrying after a stall, prepends the recovery prompt to the original prompt.
60
+ *
61
+ * @param originalPrompt - The original prompt content
62
+ * @param retryCount - Current retry count (0 = first attempt)
63
+ * @returns Full prompt with recovery prefix if applicable
64
+ */
65
+ export function buildPromptWithRecovery(originalPrompt, retryCount) {
66
+ const recovery = getRecoveryPrompt(retryCount);
67
+ if (!recovery) {
68
+ return originalPrompt;
69
+ }
70
+ return `${recovery}\n\n---\n\n${originalPrompt}`;
71
+ }
72
+ /**
73
+ * Computes the retry action based on the current retry count.
74
+ *
75
+ * Retry policy:
76
+ * - retry_count 0 or 1 → retry_unchanged (first failure, try again)
77
+ * - retry_count 2 → retry_degraded (second failure, use safer settings)
78
+ * - retry_count >= 3 → block (third failure, require human)
79
+ *
80
+ * @param retryCount - Current retry count from state (before this attempt)
81
+ * @returns The action to take
82
+ */
83
+ export function getRetryAction(retryCount) {
84
+ if (retryCount < 1) {
85
+ return 'retry_unchanged';
86
+ }
87
+ else if (retryCount < 2) {
88
+ return 'retry_degraded';
89
+ }
90
+ else {
91
+ return 'block';
92
+ }
93
+ }
94
+ /**
95
+ * Computes the full retry decision including degraded settings if needed.
96
+ *
97
+ * @param retryCount - Current retry count from state
98
+ * @param originalMaxTurns - Original max_turns from config (default 50)
99
+ * @param originalDiffLimits - Original diff limits from config
100
+ * @returns Full retry decision with settings and reason
101
+ */
102
+ export function computeRetryDecision(retryCount, originalMaxTurns = 50, originalDiffLimits = { max_files_touched: 20, max_lines_changed: 500 }) {
103
+ const action = getRetryAction(retryCount);
104
+ const newRetryCount = retryCount + 1;
105
+ if (action === 'retry_unchanged') {
106
+ return {
107
+ action,
108
+ retry_count: newRetryCount,
109
+ reason: `Retry attempt ${newRetryCount}/${MAX_RETRY_ATTEMPTS}: retrying unchanged`,
110
+ };
111
+ }
112
+ if (action === 'retry_degraded') {
113
+ const degraded_settings = computeDegradedSettings(originalMaxTurns, originalDiffLimits);
114
+ return {
115
+ action,
116
+ retry_count: newRetryCount,
117
+ degraded_settings,
118
+ reason: `Retry attempt ${newRetryCount}/${MAX_RETRY_ATTEMPTS}: retrying with degraded settings (max_turns=${degraded_settings.max_turns}, max_files=${degraded_settings.diff_limits.max_files_touched}, max_lines=${degraded_settings.diff_limits.max_lines_changed})`,
119
+ };
120
+ }
121
+ // action === 'block'
122
+ return {
123
+ action,
124
+ retry_count: newRetryCount,
125
+ reason: `Retry limit reached (${MAX_RETRY_ATTEMPTS} attempts). Blocking for human intervention.`,
126
+ };
127
+ }
128
+ /**
129
+ * Computes degraded settings for retry attempt 2.
130
+ *
131
+ * Applies conservative reductions:
132
+ * - max_turns: 50% of original, minimum 5
133
+ * - max_files_touched: 50% of original, minimum 5
134
+ * - max_lines_changed: 50% of original, minimum 100
135
+ * - prefer_patch_mode: true
136
+ *
137
+ * @param originalMaxTurns - Original max_turns from config
138
+ * @param originalDiffLimits - Original diff limits from config
139
+ * @returns Degraded settings
140
+ */
141
+ export function computeDegradedSettings(originalMaxTurns, originalDiffLimits) {
142
+ return {
143
+ max_turns: Math.max(5, Math.floor(originalMaxTurns / 2)),
144
+ diff_limits: {
145
+ max_files_touched: Math.max(5, Math.floor(originalDiffLimits.max_files_touched / 2)),
146
+ max_lines_changed: Math.max(100, Math.floor(originalDiffLimits.max_lines_changed / 2)),
147
+ },
148
+ prefer_patch_mode: true,
149
+ };
150
+ }
151
+ /**
152
+ * Checks if retry is allowed based on current retry count.
153
+ *
154
+ * @param retryCount - Current retry count
155
+ * @returns true if retry is allowed, false if blocked
156
+ */
157
+ export function canRetry(retryCount) {
158
+ return retryCount < MAX_RETRY_ATTEMPTS - 1;
159
+ }
160
+ /**
161
+ * Formats retry decision as human-readable message.
162
+ *
163
+ * @param decision - The retry decision
164
+ * @returns Formatted message
165
+ */
166
+ export function formatRetryDecision(decision) {
167
+ const lines = [decision.reason];
168
+ if (decision.degraded_settings) {
169
+ lines.push('Degraded settings:');
170
+ lines.push(` - max_turns: ${decision.degraded_settings.max_turns}`);
171
+ lines.push(` - max_files: ${decision.degraded_settings.diff_limits.max_files_touched}`);
172
+ lines.push(` - max_lines: ${decision.degraded_settings.diff_limits.max_lines_changed}`);
173
+ lines.push(` - patch_mode: ${decision.degraded_settings.prefer_patch_mode}`);
174
+ }
175
+ if (decision.action === 'block') {
176
+ lines.push('');
177
+ lines.push('Human action required: Review logs, fix issues, then reset retry state.');
178
+ }
179
+ return lines.join('\n');
180
+ }
181
+ /**
182
+ * Applies degraded settings to a EnvoiConfig.
183
+ *
184
+ * Used during retry attempt 2 to run with more conservative settings.
185
+ * Creates a new config object without mutating the original.
186
+ *
187
+ * Settings applied:
188
+ * - builder.claude_code.max_turns: reduced to degraded value
189
+ * - diff_limits.default_max_files_touched: reduced to degraded value
190
+ * - diff_limits.default_max_lines_changed: reduced to degraded value
191
+ * - builder.default_mode: set to 'patch' if allow_patch_mode is true and prefer_patch_mode is set
192
+ *
193
+ * @param config - Original EnvoiConfig
194
+ * @param degraded - Degraded settings to apply
195
+ * @returns New config with degraded settings applied
196
+ */
197
+ export function applyDegradedConfig(config, degraded) {
198
+ return {
199
+ ...config,
200
+ builder: {
201
+ ...config.builder,
202
+ // Prefer patch mode if available and degraded settings request it
203
+ default_mode: degraded.prefer_patch_mode && config.builder.allow_patch_mode
204
+ ? 'patch'
205
+ : config.builder.default_mode,
206
+ claude_code: {
207
+ ...config.builder.claude_code,
208
+ max_turns: degraded.max_turns,
209
+ },
210
+ },
211
+ diff_limits: {
212
+ ...config.diff_limits,
213
+ default_max_files_touched: degraded.diff_limits.max_files_touched,
214
+ default_max_lines_changed: degraded.diff_limits.max_lines_changed,
215
+ },
216
+ };
217
+ }
218
+ /**
219
+ * Extracts relevant settings from config for computing degraded settings.
220
+ *
221
+ * @param config - EnvoiConfig to extract from
222
+ * @returns Object with max_turns and diff_limits for degradation computation
223
+ */
224
+ export function extractDegradationInputs(config) {
225
+ return {
226
+ max_turns: config.builder.claude_code.max_turns,
227
+ diff_limits: {
228
+ max_files_touched: config.diff_limits.default_max_files_touched,
229
+ max_lines_changed: config.diff_limits.default_max_lines_changed,
230
+ },
231
+ };
232
+ }
233
+ /**
234
+ * Convenience function to degrade a config based on retry count.
235
+ *
236
+ * If retry action is 'retry_degraded', applies degraded settings to config.
237
+ * Otherwise, returns the original config unchanged.
238
+ *
239
+ * @param config - Original config
240
+ * @param retryCount - Current retry count
241
+ * @returns Degraded config if appropriate, original config otherwise
242
+ */
243
+ export function getDegradedConfigIfNeeded(config, retryCount) {
244
+ const action = getRetryAction(retryCount);
245
+ if (action !== 'retry_degraded') {
246
+ return config;
247
+ }
248
+ const inputs = extractDegradationInputs(config);
249
+ const degraded = computeDegradedSettings(inputs.max_turns, inputs.diff_limits);
250
+ return applyDegradedConfig(config, degraded);
251
+ }
252
+ /**
253
+ * Determines if retry state should be reset based on tick outcome.
254
+ *
255
+ * Retry state is reset on:
256
+ * - SUCCESS: Task completed successfully
257
+ * - STOP: Task failed with a STOP code (not a transport issue)
258
+ *
259
+ * Retry state is NOT reset on:
260
+ * - BLOCKED_STALL: Transport stall, may retry
261
+ * - BLOCKED_OTHER: Other blocked conditions
262
+ *
263
+ * @param outcome - The tick outcome
264
+ * @returns true if retry state should be reset
265
+ */
266
+ export function shouldResetRetryState(outcome) {
267
+ return outcome === 'SUCCESS' || outcome === 'STOP';
268
+ }
269
+ /**
270
+ * Updates tick state based on completion outcome.
271
+ *
272
+ * On SUCCESS or STOP:
273
+ * - Resets retry_count to 0
274
+ * - Clears last_error_kind and last_request_id
275
+ *
276
+ * On BLOCKED_STALL:
277
+ * - Records the stall (increments retry_count, sets error fields)
278
+ *
279
+ * On BLOCKED_OTHER:
280
+ * - No changes to retry state
281
+ *
282
+ * @param state - Current tick state
283
+ * @param outcome - The tick outcome
284
+ * @param stallInfo - Stall info if outcome is BLOCKED_STALL
285
+ * @returns Updated tick state
286
+ */
287
+ export function updateStateForOutcome(state, outcome, stallInfo) {
288
+ if (shouldResetRetryState(outcome)) {
289
+ return resetRetryState(state);
290
+ }
291
+ if (outcome === 'BLOCKED_STALL' && stallInfo) {
292
+ return recordTransportStall(state, stallInfo.errorKind, stallInfo.requestId);
293
+ }
294
+ // BLOCKED_OTHER - no changes
295
+ return state;
296
+ }
297
+ /**
298
+ * Convenience function to handle successful tick completion.
299
+ *
300
+ * Resets all retry state fields to clear recovery mode.
301
+ * Call this when a tick completes with SUCCESS verdict.
302
+ *
303
+ * @param state - Current tick state
304
+ * @returns Updated state with retry fields cleared
305
+ */
306
+ export function handleTickSuccess(state) {
307
+ return resetRetryState(state);
308
+ }
309
+ /**
310
+ * Convenience function to handle tick failure with STOP code.
311
+ *
312
+ * Resets retry state because STOP is a definitive failure,
313
+ * not a transient transport issue that might benefit from retry.
314
+ *
315
+ * @param state - Current tick state
316
+ * @returns Updated state with retry fields cleared
317
+ */
318
+ export function handleTickStop(state) {
319
+ return resetRetryState(state);
320
+ }
321
+ /**
322
+ * Convenience function to handle transport stall.
323
+ *
324
+ * Increments retry_count and records error information.
325
+ * Call this when a tick is blocked due to transport stall.
326
+ *
327
+ * @param state - Current tick state
328
+ * @param requestId - Request ID from the stall (for debugging)
329
+ * @returns Updated state with incremented retry count
330
+ */
331
+ export function handleTickStall(state, requestId) {
332
+ return recordTransportStall(state, 'transport_stalled', requestId);
333
+ }
334
+ /**
335
+ * Handles a transport stall during tick execution.
336
+ *
337
+ * When a stall is detected:
338
+ * 1. Checks if the git worktree is dirty
339
+ * 2. If dirty, rolls back to the base commit
340
+ * 3. Returns a BLOCKED result with stall evidence
341
+ *
342
+ * @param stallError - The structured stall error
343
+ * @param baseCommit - The commit to rollback to if needed
344
+ * @param options - Optional handling options
345
+ * @returns StallHandlingResult with rollback status and stall evidence
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * const result = await invokeWithStallDetection(config, invocation, 'BUILD');
350
+ * if (!result.ok) {
351
+ * const stallResult = await handleTransportStall(result.error, baseCommit);
352
+ * // stallResult.blockedCode === 'BLOCKED_TRANSPORT_STALLED'
353
+ * }
354
+ * ```
355
+ */
356
+ export async function handleTransportStall(stallError, baseCommit, options = {}) {
357
+ // Check if worktree is dirty
358
+ const cleanCheck = isWorktreeClean();
359
+ const wasDirty = !cleanCheck;
360
+ let rollbackPerformed = false;
361
+ let rollbackResult = null;
362
+ // If dirty and rollback not skipped, perform rollback
363
+ if (wasDirty && !options.skipRollback) {
364
+ rollbackResult = rollbackToCommit(baseCommit);
365
+ rollbackPerformed = rollbackResult.ok;
366
+ }
367
+ return {
368
+ status: 'BLOCKED',
369
+ blockedCode: 'BLOCKED_TRANSPORT_STALLED',
370
+ stage: stallError.stage,
371
+ requestId: stallError.request_id,
372
+ rawError: stallError.raw_error,
373
+ rollbackPerformed,
374
+ rollbackResult,
375
+ wasDirty,
376
+ baseCommit,
377
+ };
378
+ }
379
+ /**
380
+ * Checks if a stall occurred and handles it if so.
381
+ *
382
+ * This is a convenience wrapper that combines stall detection with handling.
383
+ *
384
+ * @param error - Any error that might be a stall
385
+ * @param stage - The stage where the error occurred
386
+ * @param baseCommit - The commit to rollback to if needed
387
+ * @param options - Optional handling options
388
+ * @returns StallHandlingResult if stall detected, null otherwise
389
+ */
390
+ export async function checkAndHandleStall(stallError, baseCommit, options = {}) {
391
+ if (!stallError) {
392
+ return null;
393
+ }
394
+ return handleTransportStall(stallError, baseCommit, options);
395
+ }
396
+ /**
397
+ * Creates a human-readable message for a stall handling result.
398
+ *
399
+ * @param result - The stall handling result
400
+ * @returns Formatted message string
401
+ */
402
+ export function formatStallResult(result) {
403
+ const lines = [
404
+ `Transport stall detected during ${result.stage}`,
405
+ `Status: ${result.blockedCode}`,
406
+ ];
407
+ if (result.requestId) {
408
+ lines.push(`Request ID: ${result.requestId}`);
409
+ }
410
+ if (result.wasDirty) {
411
+ if (result.rollbackPerformed) {
412
+ lines.push(`Rollback: performed to ${result.baseCommit.substring(0, 7)}`);
413
+ }
414
+ else {
415
+ lines.push(`Rollback: skipped (repo was dirty)`);
416
+ }
417
+ }
418
+ else {
419
+ lines.push(`Rollback: not needed (repo was clean)`);
420
+ }
421
+ lines.push(`Error: ${result.rawError.substring(0, 100)}${result.rawError.length > 100 ? '...' : ''}`);
422
+ return lines.join('\n');
423
+ }
424
+ //# sourceMappingURL=tick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tick.js","sourceRoot":"","sources":["../../src/lib/tick.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Transport layer utilities for Claude invocations.
3
+ *
4
+ * Provides stall detection and timeout wrapper for reliable
5
+ * handling of connection issues, timeouts, and transport failures.
6
+ */
7
+ import type { ClaudeCodeCliConfig } from '../types/config.js';
8
+ import type { ClaudeInvocation, ClaudeResponse } from '../types/claude.js';
9
+ import type { TransportStallError, TransportStallStage } from '../types/preflight.js';
10
+ /**
11
+ * Result of stall pattern detection.
12
+ */
13
+ export interface StallDetectionResult {
14
+ /** Whether a stall pattern was detected */
15
+ stalled: boolean;
16
+ /** Request ID if found in error output */
17
+ request_id: string | null;
18
+ /** Which pattern was matched, if any */
19
+ matched_pattern: string | null;
20
+ }
21
+ /**
22
+ * Detects transport stall patterns in error output.
23
+ *
24
+ * Checks for known patterns that indicate connection issues:
25
+ * - "Connection stalled" - Cursor/CLI connection issue
26
+ * - "streamFromAgentBackend" - Backend stream failure
27
+ * - "ECONNRESET" - Connection reset by peer
28
+ * - "ETIMEDOUT" - Connection timeout
29
+ * - "socket hang up" - Socket disconnection
30
+ *
31
+ * Also extracts Request ID if present for debugging.
32
+ *
33
+ * @param error - Error message or stderr output to analyze
34
+ * @returns Detection result with stalled flag and optional request_id
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const result = isTransportStall('Connection stalled. Request ID: abc123');
39
+ * // result.stalled === true
40
+ * // result.request_id === 'abc123'
41
+ * ```
42
+ */
43
+ export declare function isTransportStall(error: string): StallDetectionResult;
44
+ /**
45
+ * Creates a TransportStallError from error information.
46
+ *
47
+ * @param stage - The stage where the stall occurred (ORCHESTRATE or BUILD)
48
+ * @param rawError - The raw error message
49
+ * @param requestId - Optional request ID extracted from error
50
+ * @returns Structured TransportStallError
51
+ */
52
+ export declare function createTransportStallError(stage: TransportStallStage, rawError: string, requestId?: string | null): TransportStallError;
53
+ /**
54
+ * Result type for invokeWithStallDetection.
55
+ * Either a successful ClaudeResponse or a TransportStallError.
56
+ */
57
+ export type InvokeResult = {
58
+ ok: true;
59
+ response: ClaudeResponse;
60
+ } | {
61
+ ok: false;
62
+ error: TransportStallError;
63
+ };
64
+ /**
65
+ * Invokes Claude Code CLI with stall detection.
66
+ *
67
+ * Wraps the standard invokeClaudeCode function and:
68
+ * 1. Catches timeout errors and converts to TransportStallError
69
+ * 2. Detects stall patterns in error output
70
+ * 3. Returns structured error for transport failures
71
+ *
72
+ * @param config - Claude Code CLI configuration
73
+ * @param invocation - Invocation parameters
74
+ * @param stage - The stage (ORCHESTRATE or BUILD) for error context
75
+ * @returns InvokeResult with either response or stall error
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const result = await invokeWithStallDetection(config, invocation, 'BUILD');
80
+ * if (result.ok) {
81
+ * console.log('Success:', result.response.result);
82
+ * } else {
83
+ * console.log('Stall:', result.error.raw_error);
84
+ * }
85
+ * ```
86
+ */
87
+ export declare function invokeWithStallDetection(config: ClaudeCodeCliConfig, invocation: ClaudeInvocation, stage: TransportStallStage): Promise<InvokeResult>;
88
+ /**
89
+ * Result of normalizing an error for transport stall detection.
90
+ */
91
+ export interface NormalizedError {
92
+ /** Whether this error represents a transport stall */
93
+ isStall: boolean;
94
+ /** Structured stall error if isStall is true */
95
+ stallError: TransportStallError | null;
96
+ /** The original error wrapped as Error */
97
+ originalError: Error;
98
+ /** Raw error message extracted from the error */
99
+ message: string;
100
+ }
101
+ /**
102
+ * Normalizes any error type into a consistent format for stall detection.
103
+ *
104
+ * Handles:
105
+ * - ClaudeError: extracts message + stderr, checks for timeout
106
+ * - Error: extracts message
107
+ * - string: wraps in Error
108
+ * - unknown: converts to string and wraps in Error
109
+ *
110
+ * @param error - Any error type (ClaudeError, Error, string, unknown)
111
+ * @param stage - The stage where the error occurred (ORCHESTRATE or BUILD)
112
+ * @returns Normalized error with stall detection result
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * try {
117
+ * await invokeClaudeCode(config, invocation);
118
+ * } catch (error) {
119
+ * const normalized = normalizeTransportError(error, 'BUILD');
120
+ * if (normalized.isStall) {
121
+ * handleStall(normalized.stallError);
122
+ * } else {
123
+ * throw normalized.originalError;
124
+ * }
125
+ * }
126
+ * ```
127
+ */
128
+ /**
129
+ * Type guard to check if an error is a TransportStallError.
130
+ *
131
+ * @param error - Any value to check
132
+ * @returns True if the error is a TransportStallError
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const result = await invokeWithStallDetection(config, invocation, 'BUILD');
137
+ * if (!result.ok && isTransportStallError(result.error)) {
138
+ * console.log('Stage:', result.error.stage);
139
+ * console.log('Request ID:', result.error.request_id);
140
+ * }
141
+ * ```
142
+ */
143
+ export declare function isTransportStallError(error: unknown): error is TransportStallError;
144
+ export declare function normalizeTransportError(error: unknown, stage: TransportStallStage): NormalizedError;
145
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/lib/transport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAoBtF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wCAAwC;IACxC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,CA2BpE;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAM,GAAG,IAAW,GAC9B,mBAAmB,CAarB;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,GACtC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,mBAAmB,EAC3B,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,YAAY,CAAC,CAyCvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACvC,0CAA0C;IAC1C,aAAa,EAAE,KAAK,CAAC;IACrB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAYlF;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,mBAAmB,GACzB,eAAe,CAmCjB"}