issue-flow 0.13.0 → 0.15.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 (163) hide show
  1. package/dist/agent-AT64RXTA.js +30 -0
  2. package/dist/{analyze-WS47UYGN.js → analyze-O2PYDIFK.js} +21 -18
  3. package/dist/{analyze-WS47UYGN.js.map → analyze-O2PYDIFK.js.map} +1 -1
  4. package/dist/{apply-OS62UWRN.js → apply-SSU6QCD4.js} +14 -11
  5. package/dist/{apply-OS62UWRN.js.map → apply-SSU6QCD4.js.map} +1 -1
  6. package/dist/{bench-SA3BOME5.js → bench-ODFPAGW6.js} +30 -26
  7. package/dist/{bench-SA3BOME5.js.map → bench-ODFPAGW6.js.map} +1 -1
  8. package/dist/{chunk-HJGGNLWI.js → chunk-2ZAM45PZ.js} +9 -9
  9. package/dist/{chunk-QCXPNSEL.js → chunk-3BWZO4YI.js} +2 -2
  10. package/dist/{chunk-J62QSVP6.js → chunk-4RXYFWP7.js} +8 -7
  11. package/dist/{chunk-J62QSVP6.js.map → chunk-4RXYFWP7.js.map} +1 -1
  12. package/dist/{chunk-ARFVHC2F.js → chunk-5ZKBIXET.js} +115 -23
  13. package/dist/chunk-5ZKBIXET.js.map +1 -0
  14. package/dist/{chunk-HEF2RMKN.js → chunk-6E2PEBFT.js} +18 -16
  15. package/dist/{chunk-HEF2RMKN.js.map → chunk-6E2PEBFT.js.map} +1 -1
  16. package/dist/{agent-BXRIEKVM.js → chunk-6FXOU2KR.js} +22 -22
  17. package/dist/chunk-6FXOU2KR.js.map +1 -0
  18. package/dist/{chunk-IYV6KNVF.js → chunk-6QYMSNVA.js} +66 -240
  19. package/dist/chunk-6QYMSNVA.js.map +1 -0
  20. package/dist/{chunk-UOCPC2Y2.js → chunk-6SRE4KD7.js} +4 -4
  21. package/dist/{chunk-MIVHK5YI.js → chunk-A35HE4V7.js} +3 -3
  22. package/dist/{chunk-TM67EHSV.js → chunk-ARWNYXFL.js} +19 -19
  23. package/dist/chunk-ARWNYXFL.js.map +1 -0
  24. package/dist/chunk-AYYMW33T.js +163 -0
  25. package/dist/chunk-AYYMW33T.js.map +1 -0
  26. package/dist/chunk-BMHGRYL6.js +890 -0
  27. package/dist/chunk-BMHGRYL6.js.map +1 -0
  28. package/dist/{chunk-PV4OX5TC.js → chunk-F4OK7KEP.js} +11 -11
  29. package/dist/{chunk-QXDNBYQF.js → chunk-FK4IDY6P.js} +21 -10
  30. package/dist/chunk-FK4IDY6P.js.map +1 -0
  31. package/dist/{chunk-KQHB7ZT3.js → chunk-GSZCQUCY.js} +82 -16
  32. package/dist/chunk-GSZCQUCY.js.map +1 -0
  33. package/dist/{chunk-TX5WIVYP.js → chunk-HAKDLR6N.js} +6 -1
  34. package/dist/chunk-HAKDLR6N.js.map +1 -0
  35. package/dist/{chunk-OPNGPUKJ.js → chunk-HGSYD57B.js} +143 -48
  36. package/dist/chunk-HGSYD57B.js.map +1 -0
  37. package/dist/{chunk-PIVGJM6K.js → chunk-KAHQ6F5C.js} +11 -11
  38. package/dist/{chunk-BHJCGWQX.js → chunk-MEFY4B7T.js} +9 -9
  39. package/dist/{chunk-BQDQTIM7.js → chunk-MGCOQU6N.js} +4 -4
  40. package/dist/{chunk-5TB6KLC2.js → chunk-MROS3SKL.js} +8 -8
  41. package/dist/chunk-NVZMBXTE.js +243 -0
  42. package/dist/chunk-NVZMBXTE.js.map +1 -0
  43. package/dist/{chunk-664Y3NDX.js → chunk-PCN7ZQND.js} +8 -8
  44. package/dist/{chunk-IV5SS4JN.js → chunk-QRCGW2ZO.js} +9 -7
  45. package/dist/{chunk-IV5SS4JN.js.map → chunk-QRCGW2ZO.js.map} +1 -1
  46. package/dist/{chunk-7KD66LIM.js → chunk-RPXDBX6R.js} +3 -3
  47. package/dist/{chunk-SNTTOQAD.js → chunk-RWEXK55D.js} +2 -2
  48. package/dist/{chunk-6FDFRNXZ.js → chunk-TBDP6QPJ.js} +25 -20
  49. package/dist/chunk-TBDP6QPJ.js.map +1 -0
  50. package/dist/chunk-UNQYMR3H.js +25 -0
  51. package/dist/chunk-UNQYMR3H.js.map +1 -0
  52. package/dist/{chunk-HQAHNIAR.js → chunk-VR34TXHF.js} +14 -14
  53. package/dist/{chunk-CNBYCMSU.js → chunk-WM3Y7Q4A.js} +27 -18
  54. package/dist/{chunk-CNBYCMSU.js.map → chunk-WM3Y7Q4A.js.map} +1 -1
  55. package/dist/{chunk-NEZZ6KDR.js → chunk-XDKGA6UX.js} +52 -3
  56. package/dist/chunk-XDKGA6UX.js.map +1 -0
  57. package/dist/{chunk-FZDR7CGH.js → chunk-XG2NYDRA.js} +3 -3
  58. package/dist/{chunk-3XLCNVMD.js → chunk-XLVGPXNL.js} +2 -2
  59. package/dist/{chunk-DIG5XKQQ.js → chunk-XQHFC62U.js} +2 -2
  60. package/dist/{chunk-UJ7XWK7J.js → chunk-XRLRKTQA.js} +16 -14
  61. package/dist/{chunk-UJ7XWK7J.js.map → chunk-XRLRKTQA.js.map} +1 -1
  62. package/dist/{chunk-LZ6GFQAG.js → chunk-ZFIXEYUJ.js} +44 -31
  63. package/dist/chunk-ZFIXEYUJ.js.map +1 -0
  64. package/dist/{chunk-37N2RJW2.js → chunk-ZPIVCM7Q.js} +3 -3
  65. package/dist/{chunk-TVN2JVOF.js → chunk-ZXPASFHG.js} +3 -3
  66. package/dist/cli.js +58 -64
  67. package/dist/cli.js.map +1 -1
  68. package/dist/{config-MEM2IWWN.js → config-UMP6DBJB.js} +12 -9
  69. package/dist/{conventions-WV4NDXGH.js → conventions-DKH7AE4T.js} +14 -11
  70. package/dist/{conventions-WV4NDXGH.js.map → conventions-DKH7AE4T.js.map} +1 -1
  71. package/dist/diagnostics-GOGCLTRS.js +23 -0
  72. package/dist/execute-E3BRMDAQ.js +35 -0
  73. package/dist/{generate-MVQI7A45.js → generate-AGBT7EHX.js} +23 -20
  74. package/dist/{generate-MVQI7A45.js.map → generate-AGBT7EHX.js.map} +1 -1
  75. package/dist/{git-YRVRUN3C.js → git-AC542GU2.js} +4 -2
  76. package/dist/init-LLBVJNPE.js +24 -0
  77. package/dist/{operations-LU6JA3BX.js → operations-JQYK2EMT.js} +17 -14
  78. package/dist/{operations-LU6JA3BX.js.map → operations-JQYK2EMT.js.map} +1 -1
  79. package/dist/{permissions-7NMDFU27.js → permissions-6HL52K54.js} +4 -4
  80. package/dist/plan-HSLWDJEZ.js +34 -0
  81. package/dist/{policy-JSYEK5B2.js → policy-IMRXGG7X.js} +13 -10
  82. package/dist/{policy-JSYEK5B2.js.map → policy-IMRXGG7X.js.map} +1 -1
  83. package/dist/pr-6TCZXG6K.js +39 -0
  84. package/dist/pr-review-6MWJO2NP.js +32 -0
  85. package/dist/prd-D6YDYAFX.js +33 -0
  86. package/dist/{ps-TNE44R5V.js → ps-KUUZ6TUL.js} +11 -8
  87. package/dist/{ps-TNE44R5V.js.map → ps-KUUZ6TUL.js.map} +1 -1
  88. package/dist/{recorder-XGF55WXI.js → recorder-BJ2KIAWT.js} +5 -4
  89. package/dist/registry-SB7XTMGE.js +15 -0
  90. package/dist/{resume-QMEPMSZW.js → resume-QNLQSCR5.js} +44 -40
  91. package/dist/{resume-QMEPMSZW.js.map → resume-QNLQSCR5.js.map} +1 -1
  92. package/dist/review-3KJ4BH2V.js +35 -0
  93. package/dist/{routing-2ZUMZPJB.js → routing-TN7GLMGU.js} +13 -10
  94. package/dist/{routing-2ZUMZPJB.js.map → routing-TN7GLMGU.js.map} +1 -1
  95. package/dist/run-5EHIFF2V.js +54 -0
  96. package/dist/run-5EHIFF2V.js.map +1 -0
  97. package/dist/session-publisher-T7EI3CIP.js +11 -0
  98. package/dist/session-publisher-T7EI3CIP.js.map +1 -0
  99. package/dist/{state-manager-X3PT3FGT.js → state-manager-ZEPWTCA6.js} +3 -3
  100. package/dist/state-manager-ZEPWTCA6.js.map +1 -0
  101. package/dist/{usage-VCFLDA5Y.js → usage-QL563I7F.js} +13 -10
  102. package/dist/{usage-VCFLDA5Y.js.map → usage-QL563I7F.js.map} +1 -1
  103. package/dist/{web-XVHLX62B.js → web-PPSMNTQ6.js} +22 -57
  104. package/dist/{web-XVHLX62B.js.map → web-PPSMNTQ6.js.map} +1 -1
  105. package/package.json +1 -1
  106. package/web/AGENTS.md +154 -11
  107. package/web/public/app.css +343 -110
  108. package/web/public/app.js +436 -27
  109. package/web/public/index.html +44 -1
  110. package/dist/agent-BXRIEKVM.js.map +0 -1
  111. package/dist/chunk-6FDFRNXZ.js.map +0 -1
  112. package/dist/chunk-ARFVHC2F.js.map +0 -1
  113. package/dist/chunk-IHIEXUTC.js +0 -483
  114. package/dist/chunk-IHIEXUTC.js.map +0 -1
  115. package/dist/chunk-IYV6KNVF.js.map +0 -1
  116. package/dist/chunk-KQHB7ZT3.js.map +0 -1
  117. package/dist/chunk-LZ6GFQAG.js.map +0 -1
  118. package/dist/chunk-NEZZ6KDR.js.map +0 -1
  119. package/dist/chunk-OPNGPUKJ.js.map +0 -1
  120. package/dist/chunk-QXDNBYQF.js.map +0 -1
  121. package/dist/chunk-TM67EHSV.js.map +0 -1
  122. package/dist/chunk-TX5WIVYP.js.map +0 -1
  123. package/dist/execute-NITIHHCK.js +0 -31
  124. package/dist/init-OKWEC3EV.js +0 -21
  125. package/dist/plan-LEJJF3DE.js +0 -31
  126. package/dist/pr-HPVUZ7S3.js +0 -36
  127. package/dist/pr-review-R2QZBT3F.js +0 -29
  128. package/dist/prd-5O6GYCC6.js +0 -30
  129. package/dist/registry-LGPE53QD.js +0 -15
  130. package/dist/review-YKWD6SNN.js +0 -32
  131. package/dist/run-YU4TR62R.js +0 -50
  132. /package/dist/{config-MEM2IWWN.js.map → agent-AT64RXTA.js.map} +0 -0
  133. /package/dist/{chunk-HJGGNLWI.js.map → chunk-2ZAM45PZ.js.map} +0 -0
  134. /package/dist/{chunk-QCXPNSEL.js.map → chunk-3BWZO4YI.js.map} +0 -0
  135. /package/dist/{chunk-UOCPC2Y2.js.map → chunk-6SRE4KD7.js.map} +0 -0
  136. /package/dist/{chunk-MIVHK5YI.js.map → chunk-A35HE4V7.js.map} +0 -0
  137. /package/dist/{chunk-PV4OX5TC.js.map → chunk-F4OK7KEP.js.map} +0 -0
  138. /package/dist/{chunk-PIVGJM6K.js.map → chunk-KAHQ6F5C.js.map} +0 -0
  139. /package/dist/{chunk-BHJCGWQX.js.map → chunk-MEFY4B7T.js.map} +0 -0
  140. /package/dist/{chunk-BQDQTIM7.js.map → chunk-MGCOQU6N.js.map} +0 -0
  141. /package/dist/{chunk-5TB6KLC2.js.map → chunk-MROS3SKL.js.map} +0 -0
  142. /package/dist/{chunk-664Y3NDX.js.map → chunk-PCN7ZQND.js.map} +0 -0
  143. /package/dist/{chunk-7KD66LIM.js.map → chunk-RPXDBX6R.js.map} +0 -0
  144. /package/dist/{chunk-SNTTOQAD.js.map → chunk-RWEXK55D.js.map} +0 -0
  145. /package/dist/{chunk-HQAHNIAR.js.map → chunk-VR34TXHF.js.map} +0 -0
  146. /package/dist/{chunk-FZDR7CGH.js.map → chunk-XG2NYDRA.js.map} +0 -0
  147. /package/dist/{chunk-3XLCNVMD.js.map → chunk-XLVGPXNL.js.map} +0 -0
  148. /package/dist/{chunk-DIG5XKQQ.js.map → chunk-XQHFC62U.js.map} +0 -0
  149. /package/dist/{chunk-37N2RJW2.js.map → chunk-ZPIVCM7Q.js.map} +0 -0
  150. /package/dist/{chunk-TVN2JVOF.js.map → chunk-ZXPASFHG.js.map} +0 -0
  151. /package/dist/{execute-NITIHHCK.js.map → config-UMP6DBJB.js.map} +0 -0
  152. /package/dist/{git-YRVRUN3C.js.map → diagnostics-GOGCLTRS.js.map} +0 -0
  153. /package/dist/{init-OKWEC3EV.js.map → execute-E3BRMDAQ.js.map} +0 -0
  154. /package/dist/{permissions-7NMDFU27.js.map → git-AC542GU2.js.map} +0 -0
  155. /package/dist/{plan-LEJJF3DE.js.map → init-LLBVJNPE.js.map} +0 -0
  156. /package/dist/{pr-HPVUZ7S3.js.map → permissions-6HL52K54.js.map} +0 -0
  157. /package/dist/{pr-review-R2QZBT3F.js.map → plan-HSLWDJEZ.js.map} +0 -0
  158. /package/dist/{prd-5O6GYCC6.js.map → pr-6TCZXG6K.js.map} +0 -0
  159. /package/dist/{recorder-XGF55WXI.js.map → pr-review-6MWJO2NP.js.map} +0 -0
  160. /package/dist/{registry-LGPE53QD.js.map → prd-D6YDYAFX.js.map} +0 -0
  161. /package/dist/{review-YKWD6SNN.js.map → recorder-BJ2KIAWT.js.map} +0 -0
  162. /package/dist/{run-YU4TR62R.js.map → registry-SB7XTMGE.js.map} +0 -0
  163. /package/dist/{state-manager-X3PT3FGT.js.map → review-3KJ4BH2V.js.map} +0 -0
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getSessionPublisher
4
+ } from "./chunk-6QYMSNVA.js";
5
+ import {
6
+ writeDiagnostic
7
+ } from "./chunk-AYYMW33T.js";
8
+ import {
9
+ isoNow
10
+ } from "./chunk-XQHFC62U.js";
11
+
12
+ // src/core/verbose.ts
13
+ var _verbose = false;
14
+ function setVerbose(enabled) {
15
+ _verbose = enabled;
16
+ }
17
+ function isVerbose() {
18
+ return _verbose;
19
+ }
20
+ var _onOutput;
21
+ function setOutputCallback(callback) {
22
+ _onOutput = callback;
23
+ }
24
+ function getOutputCallback() {
25
+ return _onOutput;
26
+ }
27
+ var _onStoryUpdate;
28
+ function setStoryUpdateCallback(callback) {
29
+ _onStoryUpdate = callback;
30
+ }
31
+ function getStoryUpdateCallback() {
32
+ return _onStoryUpdate;
33
+ }
34
+ var _onStoryStage;
35
+ function setStoryStageCallback(callback) {
36
+ _onStoryStage = callback;
37
+ }
38
+ function getStoryStageCallback() {
39
+ return _onStoryStage;
40
+ }
41
+ var _onActivity;
42
+ function setActivityCallback(callback) {
43
+ _onActivity = callback;
44
+ }
45
+ function getActivityCallback() {
46
+ return _onActivity;
47
+ }
48
+ var _globalTimeout;
49
+ function setGlobalTimeout(ms) {
50
+ _globalTimeout = ms;
51
+ }
52
+ function getGlobalTimeout() {
53
+ return _globalTimeout;
54
+ }
55
+ var _inactivityTimeout;
56
+ function setInactivityTimeout(ms) {
57
+ _inactivityTimeout = ms;
58
+ }
59
+ function getInactivityTimeout() {
60
+ return _inactivityTimeout;
61
+ }
62
+
63
+ // src/ui/logger.ts
64
+ import chalk from "chalk";
65
+ import ora from "ora";
66
+ function useUnicode() {
67
+ if (process.env.NO_COLOR != null && process.env.NO_COLOR !== "") return false;
68
+ if (!process.stdout.isTTY) return false;
69
+ return true;
70
+ }
71
+ function useColor() {
72
+ if (process.env.NO_COLOR != null && process.env.NO_COLOR !== "") return false;
73
+ if (!process.stdout.isTTY) return false;
74
+ return true;
75
+ }
76
+ function getIcons() {
77
+ if (useUnicode()) {
78
+ return {
79
+ success: "\u2713",
80
+ fail: "\u2717",
81
+ pending: "\u23F3",
82
+ retry: "\u21BB",
83
+ warn: "\u26A0",
84
+ start: "\u25B6",
85
+ end: "\u25A0",
86
+ notReached: "\u25CB",
87
+ tool: "\u25B8",
88
+ connector: "\u2502",
89
+ info: "\xB7"
90
+ };
91
+ }
92
+ return {
93
+ success: "[OK]",
94
+ fail: "[FAIL]",
95
+ pending: "[...]",
96
+ retry: "[RETRY]",
97
+ warn: "[WARN]",
98
+ start: "[START]",
99
+ end: "[END]",
100
+ notReached: "[ ]",
101
+ tool: ">",
102
+ connector: "|",
103
+ info: "-"
104
+ };
105
+ }
106
+ function getTermWidth() {
107
+ return process.stdout.columns ?? 80;
108
+ }
109
+ function emit(line, level = "info") {
110
+ getSessionPublisher().publish({ type: "log", at: isoNow(), level, message: line });
111
+ writeDiagnostic({
112
+ level: level === "warn" ? "warning" : level,
113
+ message: line
114
+ });
115
+ const cb = getOutputCallback();
116
+ if (cb) {
117
+ cb(line);
118
+ } else {
119
+ console.log(line);
120
+ }
121
+ }
122
+ function printSuccess(message) {
123
+ const icons = getIcons();
124
+ if (useColor()) {
125
+ emit(chalk.green(`${icons.success} ${message}`));
126
+ } else {
127
+ emit(`${icons.success} ${message}`);
128
+ }
129
+ }
130
+ function printError(message) {
131
+ const icons = getIcons();
132
+ if (useColor()) {
133
+ emit(chalk.red(`${icons.fail} ${message}`), "error");
134
+ } else {
135
+ emit(`${icons.fail} ${message}`, "error");
136
+ }
137
+ }
138
+ function printWarning(message) {
139
+ const icons = getIcons();
140
+ if (useColor()) {
141
+ emit(chalk.yellow(`${icons.warn} ${message}`), "warn");
142
+ } else {
143
+ emit(`${icons.warn} ${message}`, "warn");
144
+ }
145
+ }
146
+ function printRetry(message) {
147
+ const icons = getIcons();
148
+ if (useColor()) {
149
+ emit(chalk.yellow(`${icons.retry} ${message}`), "warn");
150
+ } else {
151
+ emit(`${icons.retry} ${message}`, "warn");
152
+ }
153
+ }
154
+ function printInfo(message) {
155
+ const icons = getIcons();
156
+ if (useColor()) {
157
+ emit(chalk.blue(`${icons.info} ${message}`));
158
+ } else {
159
+ emit(`${icons.info} ${message}`);
160
+ }
161
+ }
162
+ function createSpinner(message) {
163
+ return ora({
164
+ text: message,
165
+ color: "blue",
166
+ spinner: "dots"
167
+ });
168
+ }
169
+ function formatDuration(totalSeconds) {
170
+ const hours = Math.floor(totalSeconds / 3600);
171
+ const mins = Math.floor(totalSeconds % 3600 / 60);
172
+ const secs = totalSeconds % 60;
173
+ if (hours > 0) {
174
+ return `${hours}h ${mins}m ${secs}s`;
175
+ }
176
+ if (mins > 0) {
177
+ return `${mins}m ${secs}s`;
178
+ }
179
+ return `${secs}s`;
180
+ }
181
+ var ElapsedTimer = class {
182
+ constructor(onTick) {
183
+ this.onTick = onTick;
184
+ this.startTime = Date.now();
185
+ }
186
+ onTick;
187
+ startTime;
188
+ intervalId = null;
189
+ cleanup = () => this.stop();
190
+ start() {
191
+ this.startTime = Date.now();
192
+ this.intervalId = setInterval(() => {
193
+ const seconds = Math.floor((Date.now() - this.startTime) / 1e3);
194
+ this.onTick(formatDuration(seconds));
195
+ }, 1e3);
196
+ if (this.intervalId && typeof this.intervalId === "object" && "unref" in this.intervalId) {
197
+ this.intervalId.unref();
198
+ }
199
+ process.on("exit", this.cleanup);
200
+ return this;
201
+ }
202
+ getElapsedSeconds() {
203
+ return Math.floor((Date.now() - this.startTime) / 1e3);
204
+ }
205
+ stop() {
206
+ if (this.intervalId) {
207
+ clearInterval(this.intervalId);
208
+ this.intervalId = null;
209
+ }
210
+ process.removeListener("exit", this.cleanup);
211
+ return this.getElapsedSeconds();
212
+ }
213
+ };
214
+
215
+ export {
216
+ setVerbose,
217
+ isVerbose,
218
+ setOutputCallback,
219
+ getOutputCallback,
220
+ setStoryUpdateCallback,
221
+ getStoryUpdateCallback,
222
+ setStoryStageCallback,
223
+ getStoryStageCallback,
224
+ setActivityCallback,
225
+ getActivityCallback,
226
+ setGlobalTimeout,
227
+ getGlobalTimeout,
228
+ setInactivityTimeout,
229
+ getInactivityTimeout,
230
+ useUnicode,
231
+ useColor,
232
+ getIcons,
233
+ getTermWidth,
234
+ printSuccess,
235
+ printError,
236
+ printWarning,
237
+ printRetry,
238
+ printInfo,
239
+ createSpinner,
240
+ formatDuration,
241
+ ElapsedTimer
242
+ };
243
+ //# sourceMappingURL=chunk-NVZMBXTE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/verbose.ts","../src/ui/logger.ts"],"sourcesContent":["let _verbose = false;\n\nexport function setVerbose(enabled: boolean): void {\n _verbose = enabled;\n}\n\nexport function isVerbose(): boolean {\n return _verbose;\n}\n\nlet _onOutput: ((line: string) => void) | undefined;\n\n/**\n * Set a global output callback for routing verbose output (e.g., through listr2 task.output).\n * Pass undefined to clear and fall back to direct stderr writes.\n */\nexport function setOutputCallback(callback: ((line: string) => void) | undefined): void {\n _onOutput = callback;\n}\n\nexport function getOutputCallback(): ((line: string) => void) | undefined {\n return _onOutput;\n}\n\nlet _onStoryUpdate: ((stories: import('../types.js').UserStory[]) => void) | undefined;\n\n/**\n * Set a global callback for story progress updates during engine execution.\n * Called after each iteration with the latest user stories state.\n * Pass undefined to clear.\n */\nexport function setStoryUpdateCallback(\n callback: ((stories: import('../types.js').UserStory[]) => void) | undefined,\n): void {\n _onStoryUpdate = callback;\n}\n\nexport function getStoryUpdateCallback():\n | ((stories: import('../types.js').UserStory[]) => void)\n | undefined {\n return _onStoryUpdate;\n}\n\nlet _onStoryStage: ((storyId: string | undefined) => void) | undefined;\n\n/**\n * Set a global callback for the active story of the current `execute`\n * iteration. Called by `core/engine.ts` alongside every `iteration:start`\n * publication, with the same `storyId` (or `undefined` once every story\n * already passes) — the terminal renderer's only source for \"who is\n * executing right now\", never a second, independently computed heuristic.\n * Pass `undefined` to clear.\n */\nexport function setStoryStageCallback(\n callback: ((storyId: string | undefined) => void) | undefined,\n): void {\n _onStoryStage = callback;\n}\n\nexport function getStoryStageCallback(): ((storyId: string | undefined) => void) | undefined {\n return _onStoryStage;\n}\n\nexport interface ActivityNotice {\n tool: string;\n detail?: string;\n}\n\nlet _onActivity: ((notice: ActivityNotice) => void) | undefined;\n\n/**\n * Live tool activity for the clean terminal view. Published in every mode,\n * including non-verbose — the snapshot's `currentActivity` and the dashboard\n * both read the same event.\n */\nexport function setActivityCallback(\n callback: ((notice: ActivityNotice) => void) | undefined,\n): void {\n _onActivity = callback;\n}\n\nexport function getActivityCallback(): ((notice: ActivityNotice) => void) | undefined {\n return _onActivity;\n}\n\nlet _globalTimeout: number | undefined;\n\nexport function setGlobalTimeout(ms: number): void {\n _globalTimeout = ms;\n}\n\nexport function getGlobalTimeout(): number | undefined {\n return _globalTimeout;\n}\n\n/**\n * Silence tolerated before an agent is considered stuck, in milliseconds.\n *\n * Global for the same reason the timeout is: every phase invokes the CLI\n * through the same two functions, and threading it through each one of them\n * would be a parameter with a single value per process. `undefined` leaves the\n * default; `0` turns the watchdog off.\n */\nlet _inactivityTimeout: number | undefined;\n\nexport function setInactivityTimeout(ms: number): void {\n _inactivityTimeout = ms;\n}\n\nexport function getInactivityTimeout(): number | undefined {\n return _inactivityTimeout;\n}\n","import chalk from 'chalk';\nimport ora, { type Ora } from 'ora';\nimport { getSessionPublisher } from '../core/session-publisher.js';\nimport type { SessionLogLevel } from '../core/session-state.js';\nimport { isoNow } from '../core/state-manager.js';\nimport { getOutputCallback } from '../core/verbose.js';\nimport { writeDiagnostic } from '../storage/diagnostics.js';\n\n/**\n * Detect if unicode output is supported.\n */\nexport function useUnicode(): boolean {\n if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '') return false;\n if (!process.stdout.isTTY) return false;\n return true;\n}\n\n/**\n * Detect if color output is supported.\n */\nexport function useColor(): boolean {\n if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '') return false;\n if (!process.stdout.isTTY) return false;\n return true;\n}\n\nexport interface Icons {\n success: string;\n fail: string;\n pending: string;\n retry: string;\n warn: string;\n start: string;\n end: string;\n notReached: string;\n tool: string;\n connector: string;\n info: string;\n}\n\nexport function getIcons(): Icons {\n if (useUnicode()) {\n return {\n success: '\\u2713',\n fail: '\\u2717',\n pending: '\\u23F3',\n retry: '\\u21BB',\n warn: '\\u26A0',\n start: '\\u25B6',\n end: '\\u25A0',\n notReached: '\\u25CB',\n tool: '\\u25B8',\n connector: '\\u2502',\n info: '\\u00B7',\n };\n }\n return {\n success: '[OK]',\n fail: '[FAIL]',\n pending: '[...]',\n retry: '[RETRY]',\n warn: '[WARN]',\n start: '[START]',\n end: '[END]',\n notReached: '[ ]',\n tool: '>',\n connector: '|',\n info: '-',\n };\n}\n\n/**\n * Get the terminal width.\n */\nexport function getTermWidth(): number {\n return process.stdout.columns ?? 80;\n}\n\n/**\n * Emit a line of output. Routes through the global output callback when\n * running inside a listr2 task context, otherwise falls back to console.log.\n *\n * Also forwards the line to the session publisher with its structured level;\n * ANSI codes are stripped by the reducer, so terminal output stays untouched.\n */\nfunction emit(line: string, level: SessionLogLevel = 'info'): void {\n getSessionPublisher().publish({ type: 'log', at: isoNow(), level, message: line });\n writeDiagnostic({\n level: level === 'warn' ? 'warning' : level,\n message: line,\n });\n const cb = getOutputCallback();\n if (cb) {\n cb(line);\n } else {\n console.log(line);\n }\n}\n\nexport function printSuccess(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.green(`${icons.success} ${message}`));\n } else {\n emit(`${icons.success} ${message}`);\n }\n}\n\nexport function printError(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.red(`${icons.fail} ${message}`), 'error');\n } else {\n emit(`${icons.fail} ${message}`, 'error');\n }\n}\n\nexport function printWarning(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.yellow(`${icons.warn} ${message}`), 'warn');\n } else {\n emit(`${icons.warn} ${message}`, 'warn');\n }\n}\n\nexport function printRetry(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.yellow(`${icons.retry} ${message}`), 'warn');\n } else {\n emit(`${icons.retry} ${message}`, 'warn');\n }\n}\n\nexport function printInfo(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.blue(`${icons.info} ${message}`));\n } else {\n emit(`${icons.info} ${message}`);\n }\n}\n\n/**\n * Create an ora spinner with consistent styling.\n */\nexport function createSpinner(message: string): Ora {\n return ora({\n text: message,\n color: 'blue',\n spinner: 'dots',\n });\n}\n\n/**\n * Format a duration in seconds to a human-readable string.\n */\nexport function formatDuration(totalSeconds: number): string {\n const hours = Math.floor(totalSeconds / 3600);\n const mins = Math.floor((totalSeconds % 3600) / 60);\n const secs = totalSeconds % 60;\n\n if (hours > 0) {\n return `${hours}h ${mins}m ${secs}s`;\n }\n if (mins > 0) {\n return `${mins}m ${secs}s`;\n }\n return `${secs}s`;\n}\n\n/**\n * Timer that calls a callback every second with the formatted elapsed time.\n */\nexport class ElapsedTimer {\n private startTime: number;\n private intervalId: ReturnType<typeof setInterval> | null = null;\n private cleanup = () => this.stop();\n\n constructor(private onTick: (elapsed: string) => void) {\n this.startTime = Date.now();\n }\n\n start(): this {\n this.startTime = Date.now();\n this.intervalId = setInterval(() => {\n const seconds = Math.floor((Date.now() - this.startTime) / 1000);\n this.onTick(formatDuration(seconds));\n }, 1_000);\n // Prevent the timer from keeping the process alive\n if (this.intervalId && typeof this.intervalId === 'object' && 'unref' in this.intervalId) {\n this.intervalId.unref();\n }\n process.on('exit', this.cleanup);\n return this;\n }\n\n getElapsedSeconds(): number {\n return Math.floor((Date.now() - this.startTime) / 1000);\n }\n\n stop(): number {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n process.removeListener('exit', this.cleanup);\n return this.getElapsedSeconds();\n }\n}\n"],"mappings":"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","names":[]}
@@ -2,21 +2,21 @@
2
2
  import {
3
3
  DEFAULT_BRANCH_CONVENTION,
4
4
  loadRepositoryPolicy
5
- } from "./chunk-UOCPC2Y2.js";
5
+ } from "./chunk-6SRE4KD7.js";
6
6
  import {
7
7
  loadPolicyConfig
8
- } from "./chunk-CNBYCMSU.js";
8
+ } from "./chunk-WM3Y7Q4A.js";
9
9
  import {
10
10
  getSessionPublisher
11
- } from "./chunk-IYV6KNVF.js";
11
+ } from "./chunk-6QYMSNVA.js";
12
+ import {
13
+ getBaseBranch
14
+ } from "./chunk-GSZCQUCY.js";
12
15
  import {
13
16
  hasUsageData,
14
17
  isoNow,
15
18
  sumUsage
16
- } from "./chunk-DIG5XKQQ.js";
17
- import {
18
- getBaseBranch
19
- } from "./chunk-KQHB7ZT3.js";
19
+ } from "./chunk-XQHFC62U.js";
20
20
 
21
21
  // src/core/session-metrics.ts
22
22
  function elapsedSecondsSince(startedAtMs) {
@@ -294,4 +294,4 @@ export {
294
294
  publishStoryMetrics,
295
295
  resolvePolicyPlaceholders
296
296
  };
297
- //# sourceMappingURL=chunk-664Y3NDX.js.map
297
+ //# sourceMappingURL=chunk-PCN7ZQND.js.map
@@ -1,18 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getShutdownSignal
4
- } from "./chunk-CNBYCMSU.js";
4
+ } from "./chunk-WM3Y7Q4A.js";
5
5
  import {
6
- getSessionPublisher,
7
6
  printRetry
8
- } from "./chunk-IYV6KNVF.js";
7
+ } from "./chunk-NVZMBXTE.js";
9
8
  import {
10
- isoNow
11
- } from "./chunk-DIG5XKQQ.js";
9
+ getSessionPublisher
10
+ } from "./chunk-6QYMSNVA.js";
12
11
  import {
13
12
  fixedBackoffPolicy,
14
13
  withRetry
15
- } from "./chunk-KQHB7ZT3.js";
14
+ } from "./chunk-GSZCQUCY.js";
15
+ import {
16
+ isoNow
17
+ } from "./chunk-XQHFC62U.js";
16
18
  import {
17
19
  classify
18
20
  } from "./chunk-XEYH6ZZI.js";
@@ -81,4 +83,4 @@ export {
81
83
  readFileWithGrace,
82
84
  isTransientFailure
83
85
  };
84
- //# sourceMappingURL=chunk-IV5SS4JN.js.map
86
+ //# sourceMappingURL=chunk-QRCGW2ZO.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/phase-runner.ts","../src/utils/retry.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { type ClassifiedFailure, classify } from '../resilience/errors.js';\nimport { fixedBackoffPolicy, withRetry } from '../resilience/retry.js';\nimport { printRetry } from '../ui/logger.js';\nimport { getSessionPublisher } from './session-publisher.js';\nimport { getShutdownSignal } from './shutdown.js';\nimport { isoNow } from './state-manager.js';\n\nexport interface PhaseAttemptResult {\n ok: boolean;\n /** Whether a failed attempt is worth retrying. Ignored when ok is true. */\n transient?: boolean;\n error?: string;\n}\n\nexport interface PhaseRetryOptions {\n phase: string;\n attempt: (attemptNumber: number) => Promise<PhaseAttemptResult>;\n retryLimit?: number;\n backoffBaseSeconds?: number;\n backoffMaxSeconds?: number;\n /** Cuts a pending backoff short, so a phase stops waiting on shutdown. */\n signal?: AbortSignal;\n}\n\nconst DEFAULT_PHASE_RETRY_LIMIT = 3;\nconst DEFAULT_BACKOFF_BASE_SECONDS = 15;\nconst DEFAULT_BACKOFF_MAX_SECONDS = 120;\n\n/**\n * Bounded retry wrapper for single-shot phases (prd, plan, pr) that invoke\n * the Claude CLI once and validate an artifact. Applied to phases that\n * previously aborted the whole pipeline on the first failure with no chance\n * to recover. Non-transient failures (or exhausting the retry budget) still\n * end the phase — this bounds recovery attempts, it never retries forever.\n *\n * The loop itself is `resilience/retry.ts:withRetry`, shared with the\n * `execute` loop of `core/engine.ts`. What stays here is what is specific to a\n * phase: the caller's `transient` verdict, the `retry` event and the printed\n * line. With no options supplied the effective numbers are the ones this\n * module has always used — 3 attempts, 15s doubling to 120s, no jitter.\n */\nexport async function runPhaseWithRetry(options: PhaseRetryOptions): Promise<PhaseAttemptResult> {\n const retryLimit = options.retryLimit ?? DEFAULT_PHASE_RETRY_LIMIT;\n const backoffBaseSeconds = options.backoffBaseSeconds ?? DEFAULT_BACKOFF_BASE_SECONDS;\n const backoffMaxSeconds = options.backoffMaxSeconds ?? DEFAULT_BACKOFF_MAX_SECONDS;\n\n if (retryLimit < 1) return { ok: false, error: 'Phase never attempted' };\n\n const outcome = await withRetry<PhaseAttemptResult>((attempt) => options.attempt(attempt), {\n policy: fixedBackoffPolicy(retryLimit, backoffBaseSeconds, backoffMaxSeconds),\n // The caller's own signal when it has one, and the process-wide shutdown\n // otherwise: a backoff nobody can interrupt is a `Ctrl+C` that appears to\n // do nothing for two minutes.\n signal: options.signal ?? getShutdownSignal(),\n evaluate: (result) => (result.ok ? null : phaseFailure(result)),\n onAttempt: ({ attempt, failure, willRetry, delayMs }) => {\n if (failure === null || !willRetry) return;\n\n const delaySeconds = delayMs / 1000;\n getSessionPublisher().publish({\n type: 'retry',\n at: isoNow(),\n attempt,\n delaySeconds,\n reason: failure.message,\n kind: failure.kind,\n });\n printRetry(\n `Phase ${options.phase} hit a recoverable failure (attempt ${attempt}/${retryLimit}): ${failure.message}. Retrying in ${delaySeconds}s.`,\n );\n },\n });\n\n return outcome.value;\n}\n\n/**\n * Turn a phase's own verdict into the classified failure `withRetry` decides\n * on.\n *\n * `transient` stays authoritative: the phases build it with\n * `isTransientFailure()` over the CLI's output, but a phase can also fail on\n * something it alone can see — a PRD file that never appeared — and that\n * judgement is not recoverable from the error text. `classify()` is consulted\n * only to name the *kind*, which is what the `retry` event now reports.\n */\nfunction phaseFailure(result: PhaseAttemptResult): ClassifiedFailure {\n const message = result.error ?? 'Phase failed';\n return {\n ...classify({ source: 'agent', stdout: message }),\n message,\n retryable: result.transient === true,\n };\n}\n\n/**\n * Re-read a file a few times with short delays before giving up. Absorbs a\n * brief filesystem-visibility lag between the Claude CLI's Write tool\n * completing and this process observing the result, without spending a full\n * phase retry (and a new Claude invocation) on what may just be a timing gap.\n */\nexport async function readFileWithGrace(\n path: string,\n delaysMs: number[] = [300, 800],\n): Promise<string> {\n for (let i = 0; ; i++) {\n try {\n return await readFile(path, 'utf-8');\n } catch (err) {\n if (i >= delaysMs.length) throw err;\n await new Promise((resolve) => setTimeout(resolve, delaysMs[i]));\n }\n }\n}\n","import { classify } from '../resilience/errors.js';\n\n/**\n * Determine if an agent invocation failure is transient (retryable).\n *\n * This is a thin adapter over `resilience/errors.ts:classify()` — the taxonomy\n * is the single source of truth, and this signature survives only because\n * `core/engine.ts` and the phase commands are written against it. A caller\n * that has more evidence than an exit code and a blob of text (an `errno`, an\n * HTTP status, `timedOut`) should call `classify()` directly and keep it.\n */\nexport function isTransientFailure(exitCode: number, output: string): boolean {\n return classify({ source: 'agent', exitCode, stdout: output }).retryable;\n}\n\n/**\n * Calculate retry delay using exponential backoff.\n *\n * delay = baseSeconds * 2^(attempt-1), capped at maxSeconds\n *\n * @param attempt - The retry attempt number (1-based)\n * @param baseSeconds - Base delay in seconds (default: 30)\n * @param maxSeconds - Maximum delay in seconds (default: 900)\n */\nexport function retryDelaySeconds(\n attempt: number,\n baseSeconds: number = 30,\n maxSeconds: number = 900,\n): number {\n const delay = baseSeconds * 2 ** (attempt - 1);\n return Math.min(delay, maxSeconds);\n}\n\n/**\n * Sleep for a given number of seconds.\n */\nexport function sleep(seconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, seconds * 1000));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AAyBzB,IAAM,4BAA4B;AAClC,IAAM,+BAA+B;AACrC,IAAM,8BAA8B;AAepC,eAAsB,kBAAkB,SAAyD;AAC/F,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,qBAAqB,QAAQ,sBAAsB;AACzD,QAAM,oBAAoB,QAAQ,qBAAqB;AAEvD,MAAI,aAAa,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,wBAAwB;AAEvE,QAAM,UAAU,MAAM,UAA8B,CAAC,YAAY,QAAQ,QAAQ,OAAO,GAAG;AAAA,IACzF,QAAQ,mBAAmB,YAAY,oBAAoB,iBAAiB;AAAA;AAAA;AAAA;AAAA,IAI5E,QAAQ,QAAQ,UAAU,kBAAkB;AAAA,IAC5C,UAAU,CAAC,WAAY,OAAO,KAAK,OAAO,aAAa,MAAM;AAAA,IAC7D,WAAW,CAAC,EAAE,SAAS,SAAS,WAAW,QAAQ,MAAM;AACvD,UAAI,YAAY,QAAQ,CAAC,UAAW;AAEpC,YAAM,eAAe,UAAU;AAC/B,0BAAoB,EAAE,QAAQ;AAAA,QAC5B,MAAM;AAAA,QACN,IAAI,OAAO;AAAA,QACX;AAAA,QACA;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB,MAAM,QAAQ;AAAA,MAChB,CAAC;AACD;AAAA,QACE,SAAS,QAAQ,KAAK,uCAAuC,OAAO,IAAI,UAAU,MAAM,QAAQ,OAAO,iBAAiB,YAAY;AAAA,MACtI;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,QAAQ;AACjB;AAYA,SAAS,aAAa,QAA+C;AACnE,QAAM,UAAU,OAAO,SAAS;AAChC,SAAO;AAAA,IACL,GAAG,SAAS,EAAE,QAAQ,SAAS,QAAQ,QAAQ,CAAC;AAAA,IAChD;AAAA,IACA,WAAW,OAAO,cAAc;AAAA,EAClC;AACF;AAQA,eAAsB,kBACpB,MACA,WAAqB,CAAC,KAAK,GAAG,GACb;AACjB,WAAS,IAAI,KAAK,KAAK;AACrB,QAAI;AACF,aAAO,MAAM,SAAS,MAAM,OAAO;AAAA,IACrC,SAAS,KAAK;AACZ,UAAI,KAAK,SAAS,OAAQ,OAAM;AAChC,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,SAAS,CAAC,CAAC,CAAC;AAAA,IACjE;AAAA,EACF;AACF;;;ACvGO,SAAS,mBAAmB,UAAkB,QAAyB;AAC5E,SAAO,SAAS,EAAE,QAAQ,SAAS,UAAU,QAAQ,OAAO,CAAC,EAAE;AACjE;","names":[]}
1
+ {"version":3,"sources":["../src/core/phase-runner.ts","../src/utils/retry.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { type ClassifiedFailure, classify } from '../resilience/errors.js';\nimport { fixedBackoffPolicy, withRetry } from '../resilience/retry.js';\nimport { printRetry } from '../ui/logger.js';\nimport { getSessionPublisher } from './session-publisher.js';\nimport { getShutdownSignal } from './shutdown.js';\nimport { isoNow } from './state-manager.js';\n\nexport interface PhaseAttemptResult {\n ok: boolean;\n /** Whether a failed attempt is worth retrying. Ignored when ok is true. */\n transient?: boolean;\n error?: string;\n}\n\nexport interface PhaseRetryOptions {\n phase: string;\n attempt: (attemptNumber: number) => Promise<PhaseAttemptResult>;\n retryLimit?: number;\n backoffBaseSeconds?: number;\n backoffMaxSeconds?: number;\n /** Cuts a pending backoff short, so a phase stops waiting on shutdown. */\n signal?: AbortSignal;\n}\n\nconst DEFAULT_PHASE_RETRY_LIMIT = 3;\nconst DEFAULT_BACKOFF_BASE_SECONDS = 15;\nconst DEFAULT_BACKOFF_MAX_SECONDS = 120;\n\n/**\n * Bounded retry wrapper for single-shot phases (prd, plan, pr) that invoke\n * the Claude CLI once and validate an artifact. Applied to phases that\n * previously aborted the whole pipeline on the first failure with no chance\n * to recover. Non-transient failures (or exhausting the retry budget) still\n * end the phase — this bounds recovery attempts, it never retries forever.\n *\n * The loop itself is `resilience/retry.ts:withRetry`, shared with the\n * `execute` loop of `core/engine.ts`. What stays here is what is specific to a\n * phase: the caller's `transient` verdict, the `retry` event and the printed\n * line. With no options supplied the effective numbers are the ones this\n * module has always used — 3 attempts, 15s doubling to 120s, no jitter.\n */\nexport async function runPhaseWithRetry(options: PhaseRetryOptions): Promise<PhaseAttemptResult> {\n const retryLimit = options.retryLimit ?? DEFAULT_PHASE_RETRY_LIMIT;\n const backoffBaseSeconds = options.backoffBaseSeconds ?? DEFAULT_BACKOFF_BASE_SECONDS;\n const backoffMaxSeconds = options.backoffMaxSeconds ?? DEFAULT_BACKOFF_MAX_SECONDS;\n\n if (retryLimit < 1) return { ok: false, error: 'Phase never attempted' };\n\n const outcome = await withRetry<PhaseAttemptResult>((attempt) => options.attempt(attempt), {\n policy: fixedBackoffPolicy(retryLimit, backoffBaseSeconds, backoffMaxSeconds),\n // The caller's own signal when it has one, and the process-wide shutdown\n // otherwise: a backoff nobody can interrupt is a `Ctrl+C` that appears to\n // do nothing for two minutes.\n signal: options.signal ?? getShutdownSignal(),\n evaluate: (result) => (result.ok ? null : phaseFailure(result)),\n onAttempt: ({ attempt, failure, willRetry, delayMs }) => {\n if (failure === null || !willRetry) return;\n\n const delaySeconds = delayMs / 1000;\n getSessionPublisher().publish({\n type: 'retry',\n at: isoNow(),\n attempt,\n delaySeconds,\n reason: failure.message,\n kind: failure.kind,\n });\n printRetry(\n `Phase ${options.phase} hit a recoverable failure (attempt ${attempt}/${retryLimit}): ${failure.message}. Retrying in ${delaySeconds}s.`,\n );\n },\n });\n\n return outcome.value;\n}\n\n/**\n * Turn a phase's own verdict into the classified failure `withRetry` decides\n * on.\n *\n * `transient` stays authoritative: the phases build it with\n * `isTransientFailure()` over the CLI's output, but a phase can also fail on\n * something it alone can see — a PRD file that never appeared — and that\n * judgement is not recoverable from the error text. `classify()` is consulted\n * only to name the *kind*, which is what the `retry` event now reports.\n */\nfunction phaseFailure(result: PhaseAttemptResult): ClassifiedFailure {\n const message = result.error ?? 'Phase failed';\n return {\n ...classify({ source: 'agent', stdout: message }),\n message,\n retryable: result.transient === true,\n };\n}\n\n/**\n * Re-read a file a few times with short delays before giving up. Absorbs a\n * brief filesystem-visibility lag between the Claude CLI's Write tool\n * completing and this process observing the result, without spending a full\n * phase retry (and a new Claude invocation) on what may just be a timing gap.\n */\nexport async function readFileWithGrace(\n path: string,\n delaysMs: number[] = [300, 800],\n): Promise<string> {\n for (let i = 0; ; i++) {\n try {\n return await readFile(path, 'utf-8');\n } catch (err) {\n if (i >= delaysMs.length) throw err;\n await new Promise((resolve) => setTimeout(resolve, delaysMs[i]));\n }\n }\n}\n","import { classify } from '../resilience/errors.js';\n\n/**\n * Determine if an agent invocation failure is transient (retryable).\n *\n * This is a thin adapter over `resilience/errors.ts:classify()` — the taxonomy\n * is the single source of truth, and this signature survives only because\n * `core/engine.ts` and the phase commands are written against it. A caller\n * that has more evidence than an exit code and a blob of text (an `errno`, an\n * HTTP status, `timedOut`) should call `classify()` directly and keep it.\n */\nexport function isTransientFailure(exitCode: number, output: string): boolean {\n return classify({ source: 'agent', exitCode, stdout: output }).retryable;\n}\n\n/**\n * Calculate retry delay using exponential backoff.\n *\n * delay = baseSeconds * 2^(attempt-1), capped at maxSeconds\n *\n * @param attempt - The retry attempt number (1-based)\n * @param baseSeconds - Base delay in seconds (default: 30)\n * @param maxSeconds - Maximum delay in seconds (default: 900)\n */\nexport function retryDelaySeconds(\n attempt: number,\n baseSeconds: number = 30,\n maxSeconds: number = 900,\n): number {\n const delay = baseSeconds * 2 ** (attempt - 1);\n return Math.min(delay, maxSeconds);\n}\n\n/**\n * Sleep for a given number of seconds.\n */\nexport function sleep(seconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, seconds * 1000));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AAyBzB,IAAM,4BAA4B;AAClC,IAAM,+BAA+B;AACrC,IAAM,8BAA8B;AAepC,eAAsB,kBAAkB,SAAyD;AAC/F,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,qBAAqB,QAAQ,sBAAsB;AACzD,QAAM,oBAAoB,QAAQ,qBAAqB;AAEvD,MAAI,aAAa,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,wBAAwB;AAEvE,QAAM,UAAU,MAAM,UAA8B,CAAC,YAAY,QAAQ,QAAQ,OAAO,GAAG;AAAA,IACzF,QAAQ,mBAAmB,YAAY,oBAAoB,iBAAiB;AAAA;AAAA;AAAA;AAAA,IAI5E,QAAQ,QAAQ,UAAU,kBAAkB;AAAA,IAC5C,UAAU,CAAC,WAAY,OAAO,KAAK,OAAO,aAAa,MAAM;AAAA,IAC7D,WAAW,CAAC,EAAE,SAAS,SAAS,WAAW,QAAQ,MAAM;AACvD,UAAI,YAAY,QAAQ,CAAC,UAAW;AAEpC,YAAM,eAAe,UAAU;AAC/B,0BAAoB,EAAE,QAAQ;AAAA,QAC5B,MAAM;AAAA,QACN,IAAI,OAAO;AAAA,QACX;AAAA,QACA;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB,MAAM,QAAQ;AAAA,MAChB,CAAC;AACD;AAAA,QACE,SAAS,QAAQ,KAAK,uCAAuC,OAAO,IAAI,UAAU,MAAM,QAAQ,OAAO,iBAAiB,YAAY;AAAA,MACtI;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,QAAQ;AACjB;AAYA,SAAS,aAAa,QAA+C;AACnE,QAAM,UAAU,OAAO,SAAS;AAChC,SAAO;AAAA,IACL,GAAG,SAAS,EAAE,QAAQ,SAAS,QAAQ,QAAQ,CAAC;AAAA,IAChD;AAAA,IACA,WAAW,OAAO,cAAc;AAAA,EAClC;AACF;AAQA,eAAsB,kBACpB,MACA,WAAqB,CAAC,KAAK,GAAG,GACb;AACjB,WAAS,IAAI,KAAK,KAAK;AACrB,QAAI;AACF,aAAO,MAAM,SAAS,MAAM,OAAO;AAAA,IACrC,SAAS,KAAK;AACZ,UAAI,KAAK,SAAS,OAAQ,OAAM;AAChC,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,SAAS,CAAC,CAAC,CAAC;AAAA,IACjE;AAAA,EACF;AACF;;;ACvGO,SAAS,mBAAmB,UAAkB,QAAyB;AAC5E,SAAO,SAAS,EAAE,QAAQ,SAAS,UAAU,QAAQ,OAAO,CAAC,EAAE;AACjE;","names":[]}
@@ -7,10 +7,10 @@ import {
7
7
  isoNow,
8
8
  loadTaskPlan,
9
9
  saveTaskPlan
10
- } from "./chunk-DIG5XKQQ.js";
10
+ } from "./chunk-XQHFC62U.js";
11
11
  import {
12
12
  executionPlanSchema
13
- } from "./chunk-NEZZ6KDR.js";
13
+ } from "./chunk-XDKGA6UX.js";
14
14
  import {
15
15
  writeFileAtomic
16
16
  } from "./chunk-7Z4Y3VKM.js";
@@ -655,4 +655,4 @@ export {
655
655
  setQueueBranch,
656
656
  setQueuePullRequest
657
657
  };
658
- //# sourceMappingURL=chunk-7KD66LIM.js.map
658
+ //# sourceMappingURL=chunk-RPXDBX6R.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  sessionSnapshotSchema
4
- } from "./chunk-NEZZ6KDR.js";
4
+ } from "./chunk-XDKGA6UX.js";
5
5
 
6
6
  // src/storage/session-file.ts
7
7
  import { readFile, stat as statFile } from "fs/promises";
@@ -27,4 +27,4 @@ async function readSessionFile(filePath) {
27
27
  export {
28
28
  readSessionFile
29
29
  };
30
- //# sourceMappingURL=chunk-SNTTOQAD.js.map
30
+ //# sourceMappingURL=chunk-RWEXK55D.js.map
@@ -3,12 +3,14 @@ import {
3
3
  getTelemetrySessionId,
4
4
  setTelemetrySessionId
5
5
  } from "./chunk-P3LRUNC6.js";
6
+ import {
7
+ redactFailureMessage
8
+ } from "./chunk-UNQYMR3H.js";
6
9
  import {
7
10
  isoNow,
8
11
  loadTaskPlan,
9
- saveTaskPlan,
10
- trimErrorMessage
11
- } from "./chunk-DIG5XKQQ.js";
12
+ saveTaskPlan
13
+ } from "./chunk-XQHFC62U.js";
12
14
  import {
13
15
  classify
14
16
  } from "./chunk-XEYH6ZZI.js";
@@ -97,20 +99,6 @@ function resolveCost(options) {
97
99
  return { status: "estimated", amount, currency: "USD", pricing };
98
100
  }
99
101
 
100
- // src/telemetry/redact.ts
101
- var SECRET = /(?:sk-ant-|sk-|ghp_|gho_|github_pat_|xox[baprs]-)[A-Za-z0-9_-]{8,}/g;
102
- var BEARER = /Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi;
103
- var ENV_ASSIGN = /\b(?:[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH)[A-Z0-9_]*)=([^\s]+)/g;
104
- function redactSecrets(text) {
105
- return text.replace(SECRET, "[redacted]").replace(BEARER, "Bearer [redacted]").replace(
106
- ENV_ASSIGN,
107
- (match, _value) => `${match.slice(0, match.indexOf("="))}=[redacted]`
108
- );
109
- }
110
- function redactFailureMessage(message) {
111
- return trimErrorMessage(redactSecrets(message));
112
- }
113
-
114
102
  // src/telemetry/types.ts
115
103
  var EXECUTION_PURPOSES = [
116
104
  "analyze",
@@ -149,7 +137,7 @@ function resetTelemetryState() {
149
137
  async function resolveConfig(ctx) {
150
138
  if (ctx.config !== void 0) return ctx.config;
151
139
  try {
152
- const { loadTelemetryConfig } = await import("./config-MEM2IWWN.js");
140
+ const { loadTelemetryConfig } = await import("./config-UMP6DBJB.js");
153
141
  ctx.config = await loadTelemetryConfig();
154
142
  return ctx.config;
155
143
  } catch {
@@ -207,6 +195,8 @@ async function beginExecution(input) {
207
195
  failure: null,
208
196
  owner: { pid: process.pid, host: hostname() },
209
197
  ...input.iteration === void 0 ? {} : { iteration: input.iteration },
198
+ ...input.correctionCycle === void 0 ? {} : { correctionCycle: input.correctionCycle },
199
+ ...input.storyIds === void 0 ? {} : { storyIds: input.storyIds },
210
200
  ...input.routingDecision === void 0 || input.routingDecision === null ? {} : { routingDecision: input.routingDecision }
211
201
  };
212
202
  try {
@@ -214,6 +204,12 @@ async function beginExecution(input) {
214
204
  ...plan,
215
205
  executions: cap([...plan.executions ?? [], record], cfg.maxExecutions)
216
206
  }));
207
+ const { getSessionPublisher } = await import("./session-publisher-T7EI3CIP.js");
208
+ getSessionPublisher().publish({
209
+ type: "execution:update",
210
+ at: record.startedAt,
211
+ execution: record
212
+ });
217
213
  return id;
218
214
  } catch {
219
215
  return null;
@@ -271,6 +267,7 @@ async function endExecution(input) {
271
267
  message: redactFailureMessage(input.error ?? status),
272
268
  exitCode: input.exitCode ?? null
273
269
  };
270
+ const completed = { value: null };
274
271
  try {
275
272
  await mutate(ctx.tasksPath, (plan) => {
276
273
  const records = [...plan.executions ?? []];
@@ -314,8 +311,17 @@ async function endExecution(input) {
314
311
  ...input.storyIds === void 0 ? {} : { storyIds: input.storyIds },
315
312
  ...input.stopReason === void 0 ? {} : { stopReason: input.stopReason }
316
313
  };
314
+ completed.value = records[index] ?? null;
317
315
  return { ...plan, executions: records };
318
316
  });
317
+ if (completed.value !== null) {
318
+ const { getSessionPublisher } = await import("./session-publisher-T7EI3CIP.js");
319
+ getSessionPublisher().publish({
320
+ type: "execution:update",
321
+ at: completed.value.finishedAt ?? isoNow(),
322
+ execution: completed.value
323
+ });
324
+ }
319
325
  } catch {
320
326
  }
321
327
  }
@@ -340,7 +346,6 @@ async function recordInvocation(input, invoke, finish) {
340
346
  }
341
347
 
342
348
  export {
343
- redactSecrets,
344
349
  EXECUTION_PURPOSES,
345
350
  DEFAULT_TELEMETRY_CONFIG,
346
351
  bindTelemetry,
@@ -354,4 +359,4 @@ export {
354
359
  endExecution,
355
360
  recordInvocation
356
361
  };
357
- //# sourceMappingURL=chunk-6FDFRNXZ.js.map
362
+ //# sourceMappingURL=chunk-TBDP6QPJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/telemetry/recorder.ts","../src/telemetry/pricing.ts","../src/telemetry/types.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { hostname } from 'node:os';\nimport type { ClaudeUsage } from '../core/metrics.js';\nimport { isoNow, loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { classify } from '../resilience/errors.js';\nimport type { TaskPlan } from '../types.js';\nimport { resolveCost } from './pricing.js';\nimport { redactFailureMessage } from './redact.js';\nimport { getTelemetrySessionId, setTelemetrySessionId } from './session-id.js';\nimport {\n DEFAULT_TELEMETRY_CONFIG,\n type ExecutionPurpose,\n type ExecutionRecord,\n type ExecutionStatus,\n type ExecutionTrigger,\n type NormalizedUsage,\n type StopReason,\n type TelemetryConfig,\n} from './types.js';\n\nexport interface TelemetryContext {\n tasksPath: string;\n sessionId?: string | null;\n config?: TelemetryConfig;\n}\n\nlet context: TelemetryContext | null = null;\nlet discardedCount = 0;\n\nexport function bindTelemetry(next: TelemetryContext | null): void {\n context = next;\n}\n\nexport function getTelemetryContext(): TelemetryContext | null {\n return context;\n}\n\nexport function discardedExecutionCount(): number {\n return discardedCount;\n}\n\nexport function resetTelemetryState(): void {\n context = null;\n discardedCount = 0;\n setTelemetrySessionId(null);\n}\n\nasync function resolveConfig(ctx: TelemetryContext): Promise<TelemetryConfig> {\n if (ctx.config !== undefined) return ctx.config;\n try {\n const { loadTelemetryConfig } = await import('../config.js');\n ctx.config = await loadTelemetryConfig();\n return ctx.config;\n } catch {\n return DEFAULT_TELEMETRY_CONFIG;\n }\n}\n\nfunction cap(records: ExecutionRecord[], max: number): ExecutionRecord[] {\n if (records.length <= max) return records;\n discardedCount += records.length - max;\n return records.slice(records.length - max);\n}\n\nasync function mutate(tasksPath: string, update: (plan: TaskPlan) => TaskPlan): Promise<void> {\n const plan = await loadTaskPlan(tasksPath);\n await saveTaskPlan(tasksPath, update(plan));\n}\n\nexport function usageFromClaude(usage: ClaudeUsage | null | undefined): NormalizedUsage | null {\n if (usage === undefined || usage === null) return null;\n const next: NormalizedUsage = { source: 'provider' };\n if (usage.inputTokens !== undefined) next.inputTokens = usage.inputTokens;\n if (usage.outputTokens !== undefined) next.outputTokens = usage.outputTokens;\n if (usage.cacheReadTokens !== undefined) next.cacheReadTokens = usage.cacheReadTokens;\n if (usage.cacheCreationTokens !== undefined) next.cacheCreationTokens = usage.cacheCreationTokens;\n return next;\n}\n\nexport interface BeginExecutionInput {\n purpose: ExecutionPurpose;\n attempt?: number;\n trigger?: ExecutionTrigger;\n triggerReason?: ExecutionRecord['triggerReason'];\n harness: string;\n provider?: string | null;\n harnessVersion?: string | null;\n modelRequested?: string | null;\n modelResolved?: string | null;\n modelSource?: ExecutionRecord['agent']['model']['source'];\n iteration?: number;\n correctionCycle?: number;\n storyIds?: string[];\n routingDecision?: ExecutionRecord['routingDecision'];\n}\n\nexport async function beginExecution(input: BeginExecutionInput): Promise<string | null> {\n const ctx = context;\n if (ctx === null) return null;\n const cfg = await resolveConfig(ctx);\n if (!cfg.enabled) return null;\n\n const id = randomUUID();\n const record: ExecutionRecord = {\n id,\n sessionId: ctx.sessionId ?? getTelemetrySessionId(),\n purpose: input.purpose,\n attempt: input.attempt ?? 1,\n trigger: input.trigger ?? 'initial',\n triggerReason: input.triggerReason ?? null,\n agent: {\n harness: input.harness,\n provider: input.provider ?? null,\n harnessVersion: input.harnessVersion ?? null,\n model: {\n requested: input.modelRequested ?? null,\n resolved: input.modelResolved ?? null,\n source: input.modelSource ?? (input.modelResolved ? 'provider' : 'unavailable'),\n },\n providerSessionId: null,\n },\n startedAt: isoNow(),\n finishedAt: null,\n durationMs: null,\n usage: null,\n cost: { status: 'unknown', reason: 'not_reported' },\n status: 'running',\n failure: null,\n owner: { pid: process.pid, host: hostname() },\n ...(input.iteration === undefined ? {} : { iteration: input.iteration }),\n ...(input.correctionCycle === undefined ? {} : { correctionCycle: input.correctionCycle }),\n ...(input.storyIds === undefined ? {} : { storyIds: input.storyIds }),\n ...(input.routingDecision === undefined || input.routingDecision === null\n ? {}\n : { routingDecision: input.routingDecision }),\n };\n\n try {\n await mutate(ctx.tasksPath, (plan) => ({\n ...plan,\n executions: cap([...(plan.executions ?? []), record], cfg.maxExecutions),\n }));\n const { getSessionPublisher } = await import('../core/session-publisher.js');\n getSessionPublisher().publish({\n type: 'execution:update',\n at: record.startedAt,\n execution: record,\n });\n return id;\n } catch {\n return null;\n }\n}\n\n/** Wall clock vs CLI envelope: startup the harness does not report. */\nexport function timingFromUsage(\n wallClockMs: number | null,\n usage: ClaudeUsage | null | undefined,\n): Pick<\n ExecutionRecord,\n 'cliDurationMs' | 'harnessStartupMs' | 'apiDurationMs' | 'ttftMs' | 'numTurns'\n> {\n const cli = usage?.cliDurationMs;\n const cliDurationMs = cli === undefined ? null : cli;\n const harnessStartupMs =\n wallClockMs !== null && cli !== undefined ? Math.max(0, wallClockMs - cli) : null;\n return {\n cliDurationMs,\n harnessStartupMs,\n apiDurationMs: usage?.apiDurationMs ?? null,\n ttftMs: usage?.ttftMs ?? null,\n numTurns: usage?.numTurns ?? null,\n };\n}\n\nexport async function attachVerdict(verdict: ExecutionRecord['verdict']): Promise<void> {\n const ctx = context;\n if (ctx === null || verdict == null) return;\n try {\n await mutate(ctx.tasksPath, (plan) => {\n const records = [...(plan.executions ?? [])];\n if (records.length === 0) return plan;\n const index = records.length - 1;\n const current = records[index];\n if (current === undefined) return plan;\n records[index] = { ...current, verdict };\n return { ...plan, executions: records };\n });\n } catch {\n // Observational: a failed write must never change the invocation outcome.\n }\n}\n\nexport interface EndExecutionInput {\n id: string;\n status?: ExecutionStatus;\n usage?: ClaudeUsage | null;\n reportedUsd?: number | null;\n error?: string | null;\n exitCode?: number | null;\n storyIds?: string[];\n cancelled?: boolean;\n timedOut?: boolean;\n modelResolved?: string | null;\n modelSource?: ExecutionRecord['agent']['model']['source'];\n harnessVersion?: string | null;\n providerSessionId?: string | null;\n stopReason?: StopReason | null;\n}\n\nexport async function endExecution(input: EndExecutionInput): Promise<void> {\n const ctx = context;\n if (ctx === null) return;\n const cfg = await resolveConfig(ctx);\n if (!cfg.enabled) return;\n\n const usage = usageFromClaude(input.usage);\n const timedOut = input.timedOut === true || /\\btimed out\\b/i.test(input.error ?? '');\n const cancelled = input.cancelled === true;\n let status: ExecutionStatus = input.status ?? 'completed';\n if (input.status === undefined) {\n if (cancelled) status = 'cancelled';\n else if (timedOut) status = 'timeout';\n else if (input.error) status = 'failed';\n }\n\n const failure =\n status === 'completed'\n ? null\n : {\n kind: classify({\n source: 'agent',\n stdout: input.error ?? '',\n exitCode: input.exitCode,\n timedOut,\n }).kind,\n message: redactFailureMessage(input.error ?? status),\n exitCode: input.exitCode ?? null,\n };\n\n const completed: { value: ExecutionRecord | null } = { value: null };\n try {\n await mutate(ctx.tasksPath, (plan) => {\n const records = [...(plan.executions ?? [])];\n const index = records.findIndex((record) => record.id === input.id);\n if (index === -1) return plan;\n const current = records[index];\n if (current === undefined) return plan;\n const started = Date.parse(current.startedAt);\n const finishedAt = isoNow();\n const modelResolved = input.modelResolved ?? current.agent.model.resolved;\n const modelKey = modelResolved ?? current.agent.model.requested;\n records[index] = {\n ...current,\n agent: {\n ...current.agent,\n harnessVersion: input.harnessVersion ?? current.agent.harnessVersion,\n providerSessionId: input.providerSessionId ?? current.agent.providerSessionId,\n model: {\n ...current.agent.model,\n resolved: modelResolved,\n source:\n input.modelSource ??\n (modelResolved\n ? current.agent.model.source === 'unavailable'\n ? 'provider'\n : current.agent.model.source\n : current.agent.model.source),\n },\n },\n finishedAt,\n durationMs: Number.isFinite(started) ? Math.max(0, Date.now() - started) : null,\n ...timingFromUsage(\n Number.isFinite(started) ? Math.max(0, Date.now() - started) : null,\n input.usage,\n ),\n usage,\n cost: resolveCost({\n reportedUsd: input.reportedUsd ?? input.usage?.costUsd,\n usage,\n modelKey,\n estimate: cfg.pricing.estimate,\n overrides: cfg.pricing.overrides,\n }),\n status,\n failure,\n owner: null,\n ...(input.storyIds === undefined ? {} : { storyIds: input.storyIds }),\n ...(input.stopReason === undefined ? {} : { stopReason: input.stopReason }),\n };\n completed.value = records[index] ?? null;\n return { ...plan, executions: records };\n });\n if (completed.value !== null) {\n const { getSessionPublisher } = await import('../core/session-publisher.js');\n getSessionPublisher().publish({\n type: 'execution:update',\n at: completed.value.finishedAt ?? isoNow(),\n execution: completed.value,\n });\n }\n } catch {\n // Observational: a failed write must never change the invocation outcome.\n }\n}\n\nexport async function recordInvocation<T>(\n input: BeginExecutionInput,\n invoke: () => Promise<T>,\n finish: (result: T) => EndExecutionInput,\n): Promise<T> {\n const id = await beginExecution(input);\n try {\n const result = await invoke();\n if (id !== null) {\n await endExecution({ ...finish(result), id });\n }\n return result;\n } catch (err) {\n if (id !== null) {\n await endExecution({\n id,\n status: 'failed',\n error: err instanceof Error ? err.message : String(err),\n });\n }\n throw err;\n }\n}\n","import { isoNow } from '../core/state-manager.js';\nimport type { CostRecord, NormalizedUsage, PricingSnapshot, TelemetryConfig } from './types.js';\n\nexport const PRICING_TABLE_VERSION = '2026-08-30';\n\ninterface PricingRow {\n inputPerMillion: number;\n outputPerMillion: number;\n cacheReadPerMillion?: number;\n cacheWritePerMillion?: number;\n}\n\n/** Microdollars-per-million would be more precise; dollars keep the snapshot readable. */\nconst TABLE: Record<string, PricingRow> = {\n 'claude-sonnet-4': {\n inputPerMillion: 3,\n outputPerMillion: 15,\n cacheReadPerMillion: 0.3,\n cacheWritePerMillion: 3.75,\n },\n 'claude-sonnet-4-5': {\n inputPerMillion: 3,\n outputPerMillion: 15,\n cacheReadPerMillion: 0.3,\n cacheWritePerMillion: 3.75,\n },\n 'claude-opus-4': {\n inputPerMillion: 15,\n outputPerMillion: 75,\n cacheReadPerMillion: 1.5,\n cacheWritePerMillion: 18.75,\n },\n 'gpt-5': { inputPerMillion: 1.25, outputPerMillion: 10 },\n 'gpt-5.1': { inputPerMillion: 1.25, outputPerMillion: 10 },\n};\n\n/** Aliases a harness accepts in place of a model id. */\nconst ALIASES: Record<string, string> = {\n sonnet: 'claude-sonnet-4-5',\n opus: 'claude-opus-4',\n};\n\n/**\n * Map a resolved model id onto a table key.\n *\n * What reaches here is whatever the harness reported: a dated snapshot\n * (`claude-sonnet-4-5-20250929`), a vendor-prefixed id\n * (`anthropic/claude-sonnet-4-5`) or a configured alias (`sonnet`). An exact\n * lookup misses all three, and the estimate silently degrades to `unknown`.\n * An override is always honoured under the key the user wrote.\n */\nexport function normalizeModelKey(modelKey: string): string {\n const lower = modelKey.trim().toLowerCase();\n const withoutVendor = lower.slice(lower.lastIndexOf('/') + 1);\n const withoutSnapshot = withoutVendor.replace(/-\\d{8}$/, '').replace(/-latest$/, '');\n return ALIASES[withoutSnapshot] ?? withoutSnapshot;\n}\n\nfunction rowFor(\n modelKey: string,\n overrides: TelemetryConfig['pricing']['overrides'],\n): PricingRow | null {\n const normalized = normalizeModelKey(modelKey);\n const override = overrides[modelKey] ?? overrides[normalized];\n const base = TABLE[modelKey] ?? TABLE[normalized];\n if (override === undefined && base === undefined) return null;\n return {\n inputPerMillion: override?.inputPerMillion ?? base?.inputPerMillion ?? 0,\n outputPerMillion: override?.outputPerMillion ?? base?.outputPerMillion ?? 0,\n cacheReadPerMillion: override?.cacheReadPerMillion ?? base?.cacheReadPerMillion,\n cacheWritePerMillion: override?.cacheWritePerMillion ?? base?.cacheWritePerMillion,\n };\n}\n\nfunction dollars(tokens: number | undefined, perMillion: number | undefined): number {\n if (tokens === undefined || perMillion === undefined) return 0;\n return (tokens * perMillion) / 1_000_000;\n}\n\nexport function resolveCost(options: {\n reportedUsd?: number | null;\n usage: NormalizedUsage | null;\n modelKey: string | null;\n estimate: boolean;\n overrides?: TelemetryConfig['pricing']['overrides'];\n}): CostRecord {\n if (options.reportedUsd !== undefined && options.reportedUsd !== null) {\n return { status: 'reported', amount: options.reportedUsd, currency: 'USD' };\n }\n if (!options.estimate) {\n return { status: 'unknown', reason: 'not_reported' };\n }\n if (options.modelKey === null || options.modelKey === '') {\n return { status: 'unknown', reason: 'unknown_model' };\n }\n const row = rowFor(options.modelKey, options.overrides ?? {});\n if (row === null) {\n return { status: 'unknown', reason: 'unknown_model' };\n }\n if (options.usage === null || options.usage.source === 'unavailable') {\n return { status: 'unknown', reason: 'not_reported' };\n }\n const amount =\n dollars(options.usage.inputTokens, row.inputPerMillion) +\n dollars(options.usage.outputTokens, row.outputPerMillion) +\n dollars(options.usage.cacheReadTokens, row.cacheReadPerMillion) +\n dollars(options.usage.cacheCreationTokens, row.cacheWritePerMillion);\n const pricing: PricingSnapshot = {\n tableVersion: PRICING_TABLE_VERSION,\n modelKey: options.modelKey,\n inputPerMillion: row.inputPerMillion,\n outputPerMillion: row.outputPerMillion,\n capturedAt: isoNow(),\n ...(row.cacheReadPerMillion === undefined\n ? {}\n : { cacheReadPerMillion: row.cacheReadPerMillion }),\n ...(row.cacheWritePerMillion === undefined\n ? {}\n : { cacheWritePerMillion: row.cacheWritePerMillion }),\n };\n return { status: 'estimated', amount, currency: 'USD', pricing };\n}\n\nexport function estimateCost(\n usage: NormalizedUsage,\n modelKey: string,\n overrides: TelemetryConfig['pricing']['overrides'] = {},\n): CostRecord {\n return resolveCost({ usage, modelKey, estimate: true, overrides });\n}\n","import type { FailureKind } from '../resilience/errors.js';\n\nexport const EXECUTION_PURPOSES = [\n 'analyze',\n 'generate',\n 'prd',\n 'plan',\n 'execute',\n 'review',\n 'pr',\n 'pr-review',\n 'verify',\n] as const;\n\nexport type ExecutionPurpose = (typeof EXECUTION_PURPOSES)[number];\n\nexport type ExecutionTrigger = 'initial' | 'retry' | 'fallback' | 'correction' | 'escalation';\n\nexport type ExecutionStatus =\n | 'running'\n | 'completed'\n | 'failed'\n | 'timeout'\n | 'cancelled'\n | 'interrupted';\n\nexport type StopReason =\n | 'completed'\n | 'failed'\n | 'timeout'\n | 'cancelled'\n | 'max_attempts'\n | 'max_cost'\n | 'max_duration';\n\nexport type VerdictStatus = 'passed' | 'failed' | 'unverified';\n\nexport interface NormalizedUsage {\n inputTokens?: number;\n outputTokens?: number;\n cacheReadTokens?: number;\n cacheCreationTokens?: number;\n reasoningTokens?: number;\n details?: Record<string, number>;\n source: 'provider' | 'unavailable';\n}\n\nexport interface PricingSnapshot {\n tableVersion: string;\n modelKey: string;\n inputPerMillion: number;\n outputPerMillion: number;\n cacheReadPerMillion?: number;\n cacheWritePerMillion?: number;\n capturedAt: string;\n}\n\nexport type CostRecord =\n | { status: 'reported'; amount: number; currency: 'USD' }\n | { status: 'estimated'; amount: number; currency: 'USD'; pricing: PricingSnapshot }\n | {\n status: 'unknown';\n reason: 'not_reported' | 'no_pricing' | 'unknown_model' | 'subscription' | 'zero_rated';\n };\n\nexport interface ExecutionAgent {\n harness: string;\n provider: string | null;\n harnessVersion?: string | null;\n model: {\n requested: string | null;\n resolved: string | null;\n source: 'provider' | 'config' | 'unavailable';\n };\n providerSessionId: string | null;\n}\n\nexport interface ExecutionOwner {\n pid: number;\n host: string;\n}\n\nexport interface ExecutionVerdict {\n status: VerdictStatus;\n level?: string | null;\n independence?: string | null;\n}\n\nexport interface RoutingDecision {\n selected: string;\n actual?: string;\n candidates?: unknown[];\n reasonCodes?: string[];\n [key: string]: unknown;\n}\n\nexport interface ExecutionRecord {\n id: string;\n sessionId: string | null;\n purpose: ExecutionPurpose;\n attempt: number;\n trigger: ExecutionTrigger;\n triggerReason: FailureKind | null;\n agent: ExecutionAgent;\n startedAt: string;\n finishedAt: string | null;\n durationMs: number | null;\n /** Envelope duration when the CLI reports it. */\n cliDurationMs?: number | null;\n /** wallClock − cliDuration: startup the CLI does not see. */\n harnessStartupMs?: number | null;\n /** Envelope `duration_api_ms` when reported. */\n apiDurationMs?: number | null;\n /** Time to first output, when reported. */\n ttftMs?: number | null;\n numTurns?: number | null;\n usage: NormalizedUsage | null;\n cost: CostRecord;\n status: ExecutionStatus;\n failure: {\n kind: FailureKind;\n message: string;\n exitCode: number | null;\n } | null;\n stopReason?: StopReason | null;\n iteration?: number;\n /** Correction cycle active when this invocation started; 0 is the initial pass. */\n correctionCycle?: number;\n storyIds?: string[];\n owner?: ExecutionOwner | null;\n routingDecision?: RoutingDecision | null;\n verdict?: ExecutionVerdict | null;\n}\n\nexport interface CostTotals {\n reported: number;\n estimated: number;\n unknownExecutions: number;\n}\n\nexport interface ExecutionSummary {\n count: number;\n discarded: number;\n byStatus: Partial<Record<ExecutionStatus, number>>;\n usage: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n reasoningTokens: number;\n };\n totalCost: CostTotals;\n}\n\nexport interface TelemetryConfig {\n enabled: boolean;\n maxExecutions: number;\n pricing: {\n estimate: boolean;\n overrides: Record<string, Partial<PricingSnapshot>>;\n };\n}\n\nexport const DEFAULT_TELEMETRY_CONFIG: TelemetryConfig = {\n enabled: true,\n maxExecutions: 500,\n pricing: { estimate: false, overrides: {} },\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ trimErrorMessage
4
+ } from "./chunk-XQHFC62U.js";
5
+
6
+ // src/telemetry/redact.ts
7
+ var SECRET = /(?:sk-ant-|sk-|ghp_|gho_|github_pat_|xox[baprs]-)[A-Za-z0-9_-]{8,}/g;
8
+ var BEARER = /Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi;
9
+ var SENSITIVE_FIELD = /(["']?(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|password|secret|authorization)["']?\s*[:=]\s*)["']?([^"',\s}]+)/gi;
10
+ var ENV_ASSIGN = /\b(?=[A-Z0-9_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH))[A-Z][A-Z0-9_]*=([^\s]+)/g;
11
+ function redactSecrets(text) {
12
+ return text.replace(SECRET, "[redacted]").replace(BEARER, "Bearer [redacted]").replace(SENSITIVE_FIELD, "$1[redacted]").replace(
13
+ ENV_ASSIGN,
14
+ (match, _value) => `${match.slice(0, match.indexOf("="))}=[redacted]`
15
+ );
16
+ }
17
+ function redactFailureMessage(message) {
18
+ return trimErrorMessage(redactSecrets(message));
19
+ }
20
+
21
+ export {
22
+ redactSecrets,
23
+ redactFailureMessage
24
+ };
25
+ //# sourceMappingURL=chunk-UNQYMR3H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/telemetry/redact.ts"],"sourcesContent":["import { trimErrorMessage } from '../core/state-manager.js';\n\nconst SECRET = /(?:sk-ant-|sk-|ghp_|gho_|github_pat_|xox[baprs]-)[A-Za-z0-9_-]{8,}/g;\nconst BEARER = /Bearer\\s+[A-Za-z0-9\\-._~+/]+=*/gi;\nconst SENSITIVE_FIELD =\n /([\"']?(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|password|secret|authorization)[\"']?\\s*[:=]\\s*)[\"']?([^\"',\\s}]+)/gi;\nconst ENV_ASSIGN =\n /\\b(?=[A-Z0-9_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH))[A-Z][A-Z0-9_]*=([^\\s]+)/g;\n\n/** Strip credentials from a message that will be persisted. */\nexport function redactSecrets(text: string): string {\n return text\n .replace(SECRET, '[redacted]')\n .replace(BEARER, 'Bearer [redacted]')\n .replace(SENSITIVE_FIELD, '$1[redacted]')\n .replace(\n ENV_ASSIGN,\n (match, _value: string) => `${match.slice(0, match.indexOf('='))}=[redacted]`,\n );\n}\n\n/** Sanitize a failure message: secrets out, then the existing 8-line cap. */\nexport function redactFailureMessage(message: string): string {\n return trimErrorMessage(redactSecrets(message));\n}\n"],"mappings":";;;;;;AAEA,IAAM,SAAS;AACf,IAAM,SAAS;AACf,IAAM,kBACJ;AACF,IAAM,aACJ;AAGK,SAAS,cAAc,MAAsB;AAClD,SAAO,KACJ,QAAQ,QAAQ,YAAY,EAC5B,QAAQ,QAAQ,mBAAmB,EACnC,QAAQ,iBAAiB,cAAc,EACvC;AAAA,IACC;AAAA,IACA,CAAC,OAAO,WAAmB,GAAG,MAAM,MAAM,GAAG,MAAM,QAAQ,GAAG,CAAC,CAAC;AAAA,EAClE;AACJ;AAGO,SAAS,qBAAqB,SAAyB;AAC5D,SAAO,iBAAiB,cAAc,OAAO,CAAC;AAChD;","names":[]}