issue-flow 0.12.0 → 0.14.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 (197) hide show
  1. package/README.md +79 -274
  2. package/dist/agent-OX3KPHE4.js +30 -0
  3. package/dist/{analyze-OEET7KGE.js → analyze-WIND2SEB.js} +31 -16
  4. package/dist/analyze-WIND2SEB.js.map +1 -0
  5. package/dist/{apply-ZIA3C52S.js → apply-JNOI2LR4.js} +18 -7
  6. package/dist/{apply-ZIA3C52S.js.map → apply-JNOI2LR4.js.map} +1 -1
  7. package/dist/bench-B4VZON7E.js +910 -0
  8. package/dist/bench-B4VZON7E.js.map +1 -0
  9. package/dist/{chunk-FADTCQGM.js → chunk-2IQ6M4ZE.js} +94 -32
  10. package/dist/chunk-2IQ6M4ZE.js.map +1 -0
  11. package/dist/chunk-3BWZO4YI.js +141 -0
  12. package/dist/chunk-3BWZO4YI.js.map +1 -0
  13. package/dist/chunk-3WFZETIM.js +163 -0
  14. package/dist/chunk-3WFZETIM.js.map +1 -0
  15. package/dist/chunk-67YIU2VB.js +362 -0
  16. package/dist/chunk-67YIU2VB.js.map +1 -0
  17. package/dist/{chunk-QCPGHBSZ.js → chunk-6BENHR2I.js} +63 -21
  18. package/dist/chunk-6BENHR2I.js.map +1 -0
  19. package/dist/{chunk-BYZDPR7N.js → chunk-6QYMSNVA.js} +194 -235
  20. package/dist/chunk-6QYMSNVA.js.map +1 -0
  21. package/dist/{chunk-Y5ZDJ4PJ.js → chunk-76MMPJ53.js} +20 -10
  22. package/dist/chunk-76MMPJ53.js.map +1 -0
  23. package/dist/chunk-7Z4Y3VKM.js +26 -0
  24. package/dist/chunk-7Z4Y3VKM.js.map +1 -0
  25. package/dist/{chunk-3QHK4YQA.js → chunk-B72RQZBV.js} +346 -119
  26. package/dist/chunk-B72RQZBV.js.map +1 -0
  27. package/dist/chunk-BEZ5BA5U.js +625 -0
  28. package/dist/chunk-BEZ5BA5U.js.map +1 -0
  29. package/dist/chunk-BRCNQXYQ.js +74 -0
  30. package/dist/chunk-BRCNQXYQ.js.map +1 -0
  31. package/dist/chunk-CGKLE6WR.js +1203 -0
  32. package/dist/chunk-CGKLE6WR.js.map +1 -0
  33. package/dist/chunk-CRGIQFUQ.js +83 -0
  34. package/dist/chunk-CRGIQFUQ.js.map +1 -0
  35. package/dist/{chunk-NS6R37II.js → chunk-F6TOYSYY.js} +141 -12
  36. package/dist/chunk-F6TOYSYY.js.map +1 -0
  37. package/dist/chunk-G5EK2XRN.js +25 -0
  38. package/dist/chunk-G5EK2XRN.js.map +1 -0
  39. package/dist/chunk-HAKDLR6N.js +134 -0
  40. package/dist/chunk-HAKDLR6N.js.map +1 -0
  41. package/dist/chunk-J3SOTUVI.js +298 -0
  42. package/dist/chunk-J3SOTUVI.js.map +1 -0
  43. package/dist/chunk-JN6D3MY6.js +30 -0
  44. package/dist/chunk-JN6D3MY6.js.map +1 -0
  45. package/dist/{chunk-LAFNLLQP.js → chunk-KMRFRXIT.js} +5 -5
  46. package/dist/chunk-KT7JKOXB.js +72 -0
  47. package/dist/chunk-KT7JKOXB.js.map +1 -0
  48. package/dist/chunk-L6HSKTQE.js +77 -0
  49. package/dist/chunk-L6HSKTQE.js.map +1 -0
  50. package/dist/chunk-LBGHI4S2.js +1264 -0
  51. package/dist/chunk-LBGHI4S2.js.map +1 -0
  52. package/dist/chunk-LEW4QM74.js +180 -0
  53. package/dist/chunk-LEW4QM74.js.map +1 -0
  54. package/dist/{chunk-FJP3UQGD.js → chunk-LFEQIZLB.js} +115 -20
  55. package/dist/chunk-LFEQIZLB.js.map +1 -0
  56. package/dist/chunk-OOVG2KBY.js +77 -0
  57. package/dist/chunk-OOVG2KBY.js.map +1 -0
  58. package/dist/{chunk-56KS3TFQ.js → chunk-OR43364P.js} +115 -160
  59. package/dist/chunk-OR43364P.js.map +1 -0
  60. package/dist/chunk-P3LRUNC6.js +16 -0
  61. package/dist/chunk-P3LRUNC6.js.map +1 -0
  62. package/dist/{chunk-TSOLWK7Y.js → chunk-PBTA3QQX.js} +8 -7
  63. package/dist/{chunk-TSOLWK7Y.js.map → chunk-PBTA3QQX.js.map} +1 -1
  64. package/dist/{chunk-MJULIKQU.js → chunk-PHW2YV6N.js} +23 -47
  65. package/dist/chunk-PHW2YV6N.js.map +1 -0
  66. package/dist/{chunk-73ATPIYX.js → chunk-PZTWU277.js} +36 -71
  67. package/dist/chunk-PZTWU277.js.map +1 -0
  68. package/dist/{chunk-M5UCXTIT.js → chunk-QD4VOILZ.js} +22 -18
  69. package/dist/chunk-QD4VOILZ.js.map +1 -0
  70. package/dist/chunk-QJ5UZAH5.js +2718 -0
  71. package/dist/chunk-QJ5UZAH5.js.map +1 -0
  72. package/dist/chunk-QK7WUO7A.js +283 -0
  73. package/dist/chunk-QK7WUO7A.js.map +1 -0
  74. package/dist/{chunk-A4SLHOW7.js → chunk-QZVKRDJY.js} +27 -19
  75. package/dist/chunk-QZVKRDJY.js.map +1 -0
  76. package/dist/chunk-REO3PXXO.js +71 -0
  77. package/dist/chunk-REO3PXXO.js.map +1 -0
  78. package/dist/{chunk-EZATO3DW.js → chunk-RVO7L4FX.js} +594 -17
  79. package/dist/chunk-RVO7L4FX.js.map +1 -0
  80. package/dist/{chunk-XQUSIEKA.js → chunk-SZMHCEYV.js} +63 -14
  81. package/dist/chunk-SZMHCEYV.js.map +1 -0
  82. package/dist/chunk-TYRNTSPJ.js +243 -0
  83. package/dist/chunk-TYRNTSPJ.js.map +1 -0
  84. package/dist/chunk-UJ3L2MS7.js +238 -0
  85. package/dist/chunk-UJ3L2MS7.js.map +1 -0
  86. package/dist/chunk-V7MO5EDS.js +120 -0
  87. package/dist/chunk-V7MO5EDS.js.map +1 -0
  88. package/dist/chunk-WB2X37MO.js +415 -0
  89. package/dist/chunk-WB2X37MO.js.map +1 -0
  90. package/dist/{chunk-NTMMF5UD.js → chunk-X44FKNKP.js} +525 -252
  91. package/dist/chunk-X44FKNKP.js.map +1 -0
  92. package/dist/chunk-XEYH6ZZI.js +289 -0
  93. package/dist/chunk-XEYH6ZZI.js.map +1 -0
  94. package/dist/cli.js +267 -44
  95. package/dist/cli.js.map +1 -1
  96. package/dist/{config-PRJGWW7J.js → config-HMFMCY2M.js} +31 -5
  97. package/dist/contract-JH2WBIO6.js +8 -0
  98. package/dist/conventions-NJF2OPVQ.js +146 -0
  99. package/dist/conventions-NJF2OPVQ.js.map +1 -0
  100. package/dist/diagnostics-THH42ZMP.js +23 -0
  101. package/dist/execute-KFNNZZHI.js +35 -0
  102. package/dist/{generate-EGB4KV2B.js → generate-FW5C6NP7.js} +37 -20
  103. package/dist/{generate-EGB4KV2B.js.map → generate-FW5C6NP7.js.map} +1 -1
  104. package/dist/git-W72QQ6GT.js +33 -0
  105. package/dist/init-HTN4V67Q.js +24 -0
  106. package/dist/{operations-BYT626KQ.js → operations-BCFOBH42.js} +27 -15
  107. package/dist/{operations-BYT626KQ.js.map → operations-BCFOBH42.js.map} +1 -1
  108. package/dist/permissions-4AKNQZHK.js +18 -0
  109. package/dist/plan-SIZLGA4V.js +34 -0
  110. package/dist/{policy-6S4GFC76.js → policy-DT6Q6ZVM.js} +20 -6
  111. package/dist/policy-DT6Q6ZVM.js.map +1 -0
  112. package/dist/pr-OMWCGUOW.js +39 -0
  113. package/dist/pr-review-N24CZPKR.js +32 -0
  114. package/dist/pr-review-N24CZPKR.js.map +1 -0
  115. package/dist/prd-5OFTZAHJ.js +33 -0
  116. package/dist/prd-5OFTZAHJ.js.map +1 -0
  117. package/dist/ps-RFORTCUD.js +93 -0
  118. package/dist/ps-RFORTCUD.js.map +1 -0
  119. package/dist/recorder-KCCPMKQG.js +32 -0
  120. package/dist/recorder-KCCPMKQG.js.map +1 -0
  121. package/dist/registry-GLPXOMFG.js +15 -0
  122. package/dist/registry-GLPXOMFG.js.map +1 -0
  123. package/dist/{resume-PPCNQVVV.js → resume-M5RLKP4I.js} +55 -34
  124. package/dist/{resume-PPCNQVVV.js.map → resume-M5RLKP4I.js.map} +1 -1
  125. package/dist/review-YM4JAABY.js +35 -0
  126. package/dist/review-YM4JAABY.js.map +1 -0
  127. package/dist/routing-NNT5WKGX.js +94 -0
  128. package/dist/routing-NNT5WKGX.js.map +1 -0
  129. package/dist/run-PZUKHPQK.js +54 -0
  130. package/dist/run-PZUKHPQK.js.map +1 -0
  131. package/dist/session-publisher-T7EI3CIP.js +11 -0
  132. package/dist/session-publisher-T7EI3CIP.js.map +1 -0
  133. package/dist/{state-manager-2EDVDBAR.js → state-manager-GQH32C44.js} +4 -2
  134. package/dist/state-manager-GQH32C44.js.map +1 -0
  135. package/dist/usage-V2CHE5PY.js +177 -0
  136. package/dist/usage-V2CHE5PY.js.map +1 -0
  137. package/dist/{web-EF5WIIGG.js → web-EA3KGNFN.js} +45 -30
  138. package/dist/web-EA3KGNFN.js.map +1 -0
  139. package/package.json +6 -2
  140. package/prompts/execute.md +7 -8
  141. package/prompts/plan.md +4 -5
  142. package/prompts/pr.md +2 -2
  143. package/web/AGENTS.md +152 -13
  144. package/web/public/app.css +380 -109
  145. package/web/public/app.js +525 -25
  146. package/web/public/index.html +68 -1
  147. package/dist/analyze-OEET7KGE.js.map +0 -1
  148. package/dist/chunk-3QHK4YQA.js.map +0 -1
  149. package/dist/chunk-42PJFMAR.js +0 -605
  150. package/dist/chunk-42PJFMAR.js.map +0 -1
  151. package/dist/chunk-4JSOYE4G.js +0 -368
  152. package/dist/chunk-4JSOYE4G.js.map +0 -1
  153. package/dist/chunk-56KS3TFQ.js.map +0 -1
  154. package/dist/chunk-73ATPIYX.js.map +0 -1
  155. package/dist/chunk-A4SLHOW7.js.map +0 -1
  156. package/dist/chunk-BAUPQXPE.js +0 -173
  157. package/dist/chunk-BAUPQXPE.js.map +0 -1
  158. package/dist/chunk-BYZDPR7N.js.map +0 -1
  159. package/dist/chunk-CD5ULRVC.js +0 -615
  160. package/dist/chunk-CD5ULRVC.js.map +0 -1
  161. package/dist/chunk-DIDUVL6O.js +0 -1365
  162. package/dist/chunk-DIDUVL6O.js.map +0 -1
  163. package/dist/chunk-EZATO3DW.js.map +0 -1
  164. package/dist/chunk-FADTCQGM.js.map +0 -1
  165. package/dist/chunk-FJP3UQGD.js.map +0 -1
  166. package/dist/chunk-LWRSMTTZ.js +0 -18
  167. package/dist/chunk-LWRSMTTZ.js.map +0 -1
  168. package/dist/chunk-M5UCXTIT.js.map +0 -1
  169. package/dist/chunk-M66SJHQQ.js +0 -206
  170. package/dist/chunk-M66SJHQQ.js.map +0 -1
  171. package/dist/chunk-MJULIKQU.js.map +0 -1
  172. package/dist/chunk-NS6R37II.js.map +0 -1
  173. package/dist/chunk-NTMMF5UD.js.map +0 -1
  174. package/dist/chunk-QCPGHBSZ.js.map +0 -1
  175. package/dist/chunk-XQUSIEKA.js.map +0 -1
  176. package/dist/chunk-Y5ZDJ4PJ.js.map +0 -1
  177. package/dist/execute-XBNVL753.js +0 -18
  178. package/dist/init-AJHCXJBP.js +0 -10
  179. package/dist/plan-QOZ3RIO6.js +0 -21
  180. package/dist/policy-6S4GFC76.js.map +0 -1
  181. package/dist/pr-EBBHD2ZX.js +0 -26
  182. package/dist/pr-review-VVMW5PO4.js +0 -20
  183. package/dist/prd-JQSZF7PR.js +0 -21
  184. package/dist/review-3F6MARWH.js +0 -19
  185. package/dist/run-RXE5GDQO.js +0 -36
  186. package/dist/web-EF5WIIGG.js.map +0 -1
  187. /package/dist/{config-PRJGWW7J.js.map → agent-OX3KPHE4.js.map} +0 -0
  188. /package/dist/{chunk-LAFNLLQP.js.map → chunk-KMRFRXIT.js.map} +0 -0
  189. /package/dist/{execute-XBNVL753.js.map → config-HMFMCY2M.js.map} +0 -0
  190. /package/dist/{init-AJHCXJBP.js.map → contract-JH2WBIO6.js.map} +0 -0
  191. /package/dist/{plan-QOZ3RIO6.js.map → diagnostics-THH42ZMP.js.map} +0 -0
  192. /package/dist/{pr-EBBHD2ZX.js.map → execute-KFNNZZHI.js.map} +0 -0
  193. /package/dist/{pr-review-VVMW5PO4.js.map → git-W72QQ6GT.js.map} +0 -0
  194. /package/dist/{prd-JQSZF7PR.js.map → init-HTN4V67Q.js.map} +0 -0
  195. /package/dist/{review-3F6MARWH.js.map → permissions-4AKNQZHK.js.map} +0 -0
  196. /package/dist/{run-RXE5GDQO.js.map → plan-SIZLGA4V.js.map} +0 -0
  197. /package/dist/{state-manager-2EDVDBAR.js.map → pr-OMWCGUOW.js.map} +0 -0
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getDiagnosticsDir
4
+ } from "./chunk-HAKDLR6N.js";
5
+ import {
6
+ redactSecrets
7
+ } from "./chunk-G5EK2XRN.js";
8
+
9
+ // src/storage/diagnostics.ts
10
+ import { appendFile, mkdir, readdir, readFile, rename, stat, unlink } from "fs/promises";
11
+ import { join } from "path";
12
+ var MAX_FILE_BYTES = 10 * 1024 * 1024;
13
+ var MAX_GENERATIONS = 5;
14
+ var RETENTION_DAYS = 30;
15
+ var bound = {};
16
+ var pending = Promise.resolve();
17
+ var retentionChecked = false;
18
+ var SENSITIVE_KEY = /(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|password|secret|authorization)/i;
19
+ function bindDiagnosticContext(context) {
20
+ bound = { ...bound, ...context };
21
+ }
22
+ function sanitize(value) {
23
+ if (typeof value === "string") return redactSecrets(value);
24
+ if (Array.isArray(value)) return value.map(sanitize);
25
+ if (value !== null && typeof value === "object") {
26
+ return Object.fromEntries(
27
+ Object.entries(value).map(([key, item]) => [
28
+ key,
29
+ SENSITIVE_KEY.test(key) ? "[redacted]" : sanitize(item)
30
+ ])
31
+ );
32
+ }
33
+ return value;
34
+ }
35
+ function diagnosticFile(timestamp) {
36
+ return join(getDiagnosticsDir(), `issue-flow-${timestamp.slice(0, 10)}.jsonl`);
37
+ }
38
+ async function rotate(file, incomingBytes) {
39
+ let size = 0;
40
+ try {
41
+ size = (await stat(file)).size;
42
+ } catch {
43
+ return;
44
+ }
45
+ if (size + incomingBytes < MAX_FILE_BYTES) return;
46
+ try {
47
+ await unlink(`${file}.${MAX_GENERATIONS}`);
48
+ } catch {
49
+ }
50
+ for (let generation = MAX_GENERATIONS - 1; generation >= 1; generation--) {
51
+ try {
52
+ await rename(`${file}.${generation}`, `${file}.${generation + 1}`);
53
+ } catch {
54
+ }
55
+ }
56
+ try {
57
+ await rename(file, `${file}.1`);
58
+ } catch {
59
+ }
60
+ }
61
+ async function enforceRetention(directory) {
62
+ if (retentionChecked) return;
63
+ retentionChecked = true;
64
+ let names;
65
+ try {
66
+ names = await readdir(directory);
67
+ } catch {
68
+ return;
69
+ }
70
+ const cutoff = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
71
+ await Promise.all(
72
+ names.filter((name) => /^issue-flow-\d{4}-\d{2}-\d{2}\.jsonl(?:\.\d+)?$/.test(name)).map(async (name) => {
73
+ const path = join(directory, name);
74
+ try {
75
+ if ((await stat(path)).mtimeMs < cutoff) await unlink(path);
76
+ } catch {
77
+ }
78
+ })
79
+ );
80
+ }
81
+ function writeDiagnostic(input) {
82
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
83
+ const error = input.exception instanceof Error ? input.exception : null;
84
+ const record = {
85
+ ...bound,
86
+ ...input.fields,
87
+ timestamp,
88
+ level: input.level,
89
+ message: redactSecrets(input.message),
90
+ context: input.context === void 0 ? null : sanitize(input.context),
91
+ exception: error === null ? null : {
92
+ name: error.name,
93
+ message: redactSecrets(error.message),
94
+ stack: error.stack ? redactSecrets(error.stack) : null
95
+ }
96
+ };
97
+ const line = `${JSON.stringify(record)}
98
+ `;
99
+ pending = pending.then(async () => {
100
+ const directory = getDiagnosticsDir();
101
+ await mkdir(directory, { recursive: true });
102
+ await enforceRetention(directory);
103
+ const file = diagnosticFile(timestamp);
104
+ await rotate(file, Buffer.byteLength(line));
105
+ await appendFile(file, line, "utf-8");
106
+ }).catch(() => {
107
+ });
108
+ }
109
+ async function flushDiagnostics() {
110
+ await pending;
111
+ }
112
+ async function resetDiagnosticsState() {
113
+ await pending;
114
+ bound = {};
115
+ pending = Promise.resolve();
116
+ retentionChecked = false;
117
+ }
118
+ async function readDiagnostics(options = {}) {
119
+ const directory = getDiagnosticsDir();
120
+ let names;
121
+ try {
122
+ names = (await readdir(directory)).filter((name) => /^issue-flow-\d{4}-\d{2}-\d{2}\.jsonl(?:\.\d+)?$/.test(name)).sort((left, right) => {
123
+ const leftDate = left.slice(11, 21);
124
+ const rightDate = right.slice(11, 21);
125
+ if (leftDate !== rightDate) return rightDate.localeCompare(leftDate);
126
+ const generation = (name) => Number(name.match(/\.jsonl\.(\d+)$/)?.[1] ?? 0);
127
+ return generation(left) - generation(right);
128
+ });
129
+ } catch {
130
+ return [];
131
+ }
132
+ const limit = Math.max(1, Math.min(options.limit ?? 500, 2e3));
133
+ const records = [];
134
+ for (const name of names) {
135
+ let raw;
136
+ try {
137
+ raw = await readFile(join(directory, name), "utf-8");
138
+ } catch {
139
+ continue;
140
+ }
141
+ for (const line of raw.split("\n").reverse()) {
142
+ if (line.trim() === "") continue;
143
+ try {
144
+ const record = JSON.parse(line);
145
+ if (options.sessionId && record.sessionId !== options.sessionId) continue;
146
+ if (options.project && record.project !== options.project) continue;
147
+ records.push(record);
148
+ if (records.length >= limit) return records;
149
+ } catch {
150
+ }
151
+ }
152
+ }
153
+ return records;
154
+ }
155
+
156
+ export {
157
+ bindDiagnosticContext,
158
+ writeDiagnostic,
159
+ flushDiagnostics,
160
+ resetDiagnosticsState,
161
+ readDiagnostics
162
+ };
163
+ //# sourceMappingURL=chunk-3WFZETIM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/storage/diagnostics.ts"],"sourcesContent":["import { appendFile, mkdir, readdir, readFile, rename, stat, unlink } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { redactSecrets } from '../telemetry/redact.js';\nimport { getDiagnosticsDir } from './paths.js';\n\nexport type DiagnosticLevel = 'debug' | 'info' | 'warning' | 'error';\n\nexport interface DiagnosticContext {\n project?: string | null;\n projectRoot?: string | null;\n sessionId?: string | null;\n executionId?: string | null;\n issue?: string | number | null;\n phase?: string | null;\n story?: string | null;\n harness?: string | null;\n model?: string | null;\n}\n\nexport interface DiagnosticRecord extends DiagnosticContext {\n timestamp: string;\n level: DiagnosticLevel;\n message: string;\n context: unknown | null;\n exception: { name: string; message: string; stack: string | null } | null;\n}\n\nconst MAX_FILE_BYTES = 10 * 1024 * 1024;\nconst MAX_GENERATIONS = 5;\nconst RETENTION_DAYS = 30;\nlet bound: DiagnosticContext = {};\nlet pending: Promise<void> = Promise.resolve();\nlet retentionChecked = false;\nconst SENSITIVE_KEY =\n /(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|password|secret|authorization)/i;\n\nexport function bindDiagnosticContext(context: DiagnosticContext): void {\n bound = { ...bound, ...context };\n}\n\nfunction sanitize(value: unknown): unknown {\n if (typeof value === 'string') return redactSecrets(value);\n if (Array.isArray(value)) return value.map(sanitize);\n if (value !== null && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([key, item]) => [\n key,\n SENSITIVE_KEY.test(key) ? '[redacted]' : sanitize(item),\n ]),\n );\n }\n return value;\n}\n\nfunction diagnosticFile(timestamp: string): string {\n return join(getDiagnosticsDir(), `issue-flow-${timestamp.slice(0, 10)}.jsonl`);\n}\n\nasync function rotate(file: string, incomingBytes: number): Promise<void> {\n let size = 0;\n try {\n size = (await stat(file)).size;\n } catch {\n return;\n }\n if (size + incomingBytes < MAX_FILE_BYTES) return;\n try {\n await unlink(`${file}.${MAX_GENERATIONS}`);\n } catch {}\n for (let generation = MAX_GENERATIONS - 1; generation >= 1; generation--) {\n try {\n await rename(`${file}.${generation}`, `${file}.${generation + 1}`);\n } catch {}\n }\n try {\n await rename(file, `${file}.1`);\n } catch {}\n}\n\nasync function enforceRetention(directory: string): Promise<void> {\n if (retentionChecked) return;\n retentionChecked = true;\n let names: string[];\n try {\n names = await readdir(directory);\n } catch {\n return;\n }\n const cutoff = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1_000;\n await Promise.all(\n names\n .filter((name) => /^issue-flow-\\d{4}-\\d{2}-\\d{2}\\.jsonl(?:\\.\\d+)?$/.test(name))\n .map(async (name) => {\n const path = join(directory, name);\n try {\n if ((await stat(path)).mtimeMs < cutoff) await unlink(path);\n } catch {}\n }),\n );\n}\n\nexport function writeDiagnostic(input: {\n level: DiagnosticLevel;\n message: string;\n context?: unknown;\n exception?: unknown;\n fields?: DiagnosticContext;\n}): void {\n const timestamp = new Date().toISOString();\n const error = input.exception instanceof Error ? input.exception : null;\n const record: DiagnosticRecord = {\n ...bound,\n ...input.fields,\n timestamp,\n level: input.level,\n message: redactSecrets(input.message),\n context: input.context === undefined ? null : sanitize(input.context),\n exception:\n error === null\n ? null\n : {\n name: error.name,\n message: redactSecrets(error.message),\n stack: error.stack ? redactSecrets(error.stack) : null,\n },\n };\n const line = `${JSON.stringify(record)}\\n`;\n pending = pending\n .then(async () => {\n const directory = getDiagnosticsDir();\n await mkdir(directory, { recursive: true });\n await enforceRetention(directory);\n const file = diagnosticFile(timestamp);\n await rotate(file, Buffer.byteLength(line));\n await appendFile(file, line, 'utf-8');\n })\n .catch(() => {});\n}\n\nexport async function flushDiagnostics(): Promise<void> {\n await pending;\n}\n\n/** Test seam: diagnostics are process-global like the terminal/session publishers. */\nexport async function resetDiagnosticsState(): Promise<void> {\n await pending;\n bound = {};\n pending = Promise.resolve();\n retentionChecked = false;\n}\n\nexport async function readDiagnostics(\n options: { sessionId?: string; project?: string; limit?: number } = {},\n): Promise<DiagnosticRecord[]> {\n const directory = getDiagnosticsDir();\n let names: string[];\n try {\n names = (await readdir(directory))\n .filter((name) => /^issue-flow-\\d{4}-\\d{2}-\\d{2}\\.jsonl(?:\\.\\d+)?$/.test(name))\n .sort((left, right) => {\n const leftDate = left.slice(11, 21);\n const rightDate = right.slice(11, 21);\n if (leftDate !== rightDate) return rightDate.localeCompare(leftDate);\n const generation = (name: string): number =>\n Number(name.match(/\\.jsonl\\.(\\d+)$/)?.[1] ?? 0);\n return generation(left) - generation(right);\n });\n } catch {\n return [];\n }\n const limit = Math.max(1, Math.min(options.limit ?? 500, 2_000));\n const records: DiagnosticRecord[] = [];\n for (const name of names) {\n let raw: string;\n try {\n raw = await readFile(join(directory, name), 'utf-8');\n } catch {\n continue;\n }\n for (const line of raw.split('\\n').reverse()) {\n if (line.trim() === '') continue;\n try {\n const record = JSON.parse(line) as DiagnosticRecord;\n if (options.sessionId && record.sessionId !== options.sessionId) continue;\n if (options.project && record.project !== options.project) continue;\n records.push(record);\n if (records.length >= limit) return records;\n } catch {}\n }\n }\n return records;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,362 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getTelemetrySessionId,
4
+ setTelemetrySessionId
5
+ } from "./chunk-P3LRUNC6.js";
6
+ import {
7
+ redactFailureMessage
8
+ } from "./chunk-G5EK2XRN.js";
9
+ import {
10
+ isoNow,
11
+ loadTaskPlan,
12
+ saveTaskPlan
13
+ } from "./chunk-J3SOTUVI.js";
14
+ import {
15
+ classify
16
+ } from "./chunk-XEYH6ZZI.js";
17
+
18
+ // src/telemetry/recorder.ts
19
+ import { randomUUID } from "crypto";
20
+ import { hostname } from "os";
21
+
22
+ // src/telemetry/pricing.ts
23
+ var PRICING_TABLE_VERSION = "2026-08-30";
24
+ var TABLE = {
25
+ "claude-sonnet-4": {
26
+ inputPerMillion: 3,
27
+ outputPerMillion: 15,
28
+ cacheReadPerMillion: 0.3,
29
+ cacheWritePerMillion: 3.75
30
+ },
31
+ "claude-sonnet-4-5": {
32
+ inputPerMillion: 3,
33
+ outputPerMillion: 15,
34
+ cacheReadPerMillion: 0.3,
35
+ cacheWritePerMillion: 3.75
36
+ },
37
+ "claude-opus-4": {
38
+ inputPerMillion: 15,
39
+ outputPerMillion: 75,
40
+ cacheReadPerMillion: 1.5,
41
+ cacheWritePerMillion: 18.75
42
+ },
43
+ "gpt-5": { inputPerMillion: 1.25, outputPerMillion: 10 },
44
+ "gpt-5.1": { inputPerMillion: 1.25, outputPerMillion: 10 }
45
+ };
46
+ var ALIASES = {
47
+ sonnet: "claude-sonnet-4-5",
48
+ opus: "claude-opus-4"
49
+ };
50
+ function normalizeModelKey(modelKey) {
51
+ const lower = modelKey.trim().toLowerCase();
52
+ const withoutVendor = lower.slice(lower.lastIndexOf("/") + 1);
53
+ const withoutSnapshot = withoutVendor.replace(/-\d{8}$/, "").replace(/-latest$/, "");
54
+ return ALIASES[withoutSnapshot] ?? withoutSnapshot;
55
+ }
56
+ function rowFor(modelKey, overrides) {
57
+ const normalized = normalizeModelKey(modelKey);
58
+ const override = overrides[modelKey] ?? overrides[normalized];
59
+ const base = TABLE[modelKey] ?? TABLE[normalized];
60
+ if (override === void 0 && base === void 0) return null;
61
+ return {
62
+ inputPerMillion: override?.inputPerMillion ?? base?.inputPerMillion ?? 0,
63
+ outputPerMillion: override?.outputPerMillion ?? base?.outputPerMillion ?? 0,
64
+ cacheReadPerMillion: override?.cacheReadPerMillion ?? base?.cacheReadPerMillion,
65
+ cacheWritePerMillion: override?.cacheWritePerMillion ?? base?.cacheWritePerMillion
66
+ };
67
+ }
68
+ function dollars(tokens, perMillion) {
69
+ if (tokens === void 0 || perMillion === void 0) return 0;
70
+ return tokens * perMillion / 1e6;
71
+ }
72
+ function resolveCost(options) {
73
+ if (options.reportedUsd !== void 0 && options.reportedUsd !== null) {
74
+ return { status: "reported", amount: options.reportedUsd, currency: "USD" };
75
+ }
76
+ if (!options.estimate) {
77
+ return { status: "unknown", reason: "not_reported" };
78
+ }
79
+ if (options.modelKey === null || options.modelKey === "") {
80
+ return { status: "unknown", reason: "unknown_model" };
81
+ }
82
+ const row = rowFor(options.modelKey, options.overrides ?? {});
83
+ if (row === null) {
84
+ return { status: "unknown", reason: "unknown_model" };
85
+ }
86
+ if (options.usage === null || options.usage.source === "unavailable") {
87
+ return { status: "unknown", reason: "not_reported" };
88
+ }
89
+ const amount = dollars(options.usage.inputTokens, row.inputPerMillion) + dollars(options.usage.outputTokens, row.outputPerMillion) + dollars(options.usage.cacheReadTokens, row.cacheReadPerMillion) + dollars(options.usage.cacheCreationTokens, row.cacheWritePerMillion);
90
+ const pricing = {
91
+ tableVersion: PRICING_TABLE_VERSION,
92
+ modelKey: options.modelKey,
93
+ inputPerMillion: row.inputPerMillion,
94
+ outputPerMillion: row.outputPerMillion,
95
+ capturedAt: isoNow(),
96
+ ...row.cacheReadPerMillion === void 0 ? {} : { cacheReadPerMillion: row.cacheReadPerMillion },
97
+ ...row.cacheWritePerMillion === void 0 ? {} : { cacheWritePerMillion: row.cacheWritePerMillion }
98
+ };
99
+ return { status: "estimated", amount, currency: "USD", pricing };
100
+ }
101
+
102
+ // src/telemetry/types.ts
103
+ var EXECUTION_PURPOSES = [
104
+ "analyze",
105
+ "generate",
106
+ "prd",
107
+ "plan",
108
+ "execute",
109
+ "review",
110
+ "pr",
111
+ "pr-review",
112
+ "verify"
113
+ ];
114
+ var DEFAULT_TELEMETRY_CONFIG = {
115
+ enabled: true,
116
+ maxExecutions: 500,
117
+ pricing: { estimate: false, overrides: {} }
118
+ };
119
+
120
+ // src/telemetry/recorder.ts
121
+ var context = null;
122
+ var discardedCount = 0;
123
+ function bindTelemetry(next) {
124
+ context = next;
125
+ }
126
+ function getTelemetryContext() {
127
+ return context;
128
+ }
129
+ function discardedExecutionCount() {
130
+ return discardedCount;
131
+ }
132
+ function resetTelemetryState() {
133
+ context = null;
134
+ discardedCount = 0;
135
+ setTelemetrySessionId(null);
136
+ }
137
+ async function resolveConfig(ctx) {
138
+ if (ctx.config !== void 0) return ctx.config;
139
+ try {
140
+ const { loadTelemetryConfig } = await import("./config-HMFMCY2M.js");
141
+ ctx.config = await loadTelemetryConfig();
142
+ return ctx.config;
143
+ } catch {
144
+ return DEFAULT_TELEMETRY_CONFIG;
145
+ }
146
+ }
147
+ function cap(records, max) {
148
+ if (records.length <= max) return records;
149
+ discardedCount += records.length - max;
150
+ return records.slice(records.length - max);
151
+ }
152
+ async function mutate(tasksPath, update) {
153
+ const plan = await loadTaskPlan(tasksPath);
154
+ await saveTaskPlan(tasksPath, update(plan));
155
+ }
156
+ function usageFromClaude(usage) {
157
+ if (usage === void 0 || usage === null) return null;
158
+ const next = { source: "provider" };
159
+ if (usage.inputTokens !== void 0) next.inputTokens = usage.inputTokens;
160
+ if (usage.outputTokens !== void 0) next.outputTokens = usage.outputTokens;
161
+ if (usage.cacheReadTokens !== void 0) next.cacheReadTokens = usage.cacheReadTokens;
162
+ if (usage.cacheCreationTokens !== void 0) next.cacheCreationTokens = usage.cacheCreationTokens;
163
+ return next;
164
+ }
165
+ async function beginExecution(input) {
166
+ const ctx = context;
167
+ if (ctx === null) return null;
168
+ const cfg = await resolveConfig(ctx);
169
+ if (!cfg.enabled) return null;
170
+ const id = randomUUID();
171
+ const record = {
172
+ id,
173
+ sessionId: ctx.sessionId ?? getTelemetrySessionId(),
174
+ purpose: input.purpose,
175
+ attempt: input.attempt ?? 1,
176
+ trigger: input.trigger ?? "initial",
177
+ triggerReason: input.triggerReason ?? null,
178
+ agent: {
179
+ harness: input.harness,
180
+ provider: input.provider ?? null,
181
+ harnessVersion: input.harnessVersion ?? null,
182
+ model: {
183
+ requested: input.modelRequested ?? null,
184
+ resolved: input.modelResolved ?? null,
185
+ source: input.modelSource ?? (input.modelResolved ? "provider" : "unavailable")
186
+ },
187
+ providerSessionId: null
188
+ },
189
+ startedAt: isoNow(),
190
+ finishedAt: null,
191
+ durationMs: null,
192
+ usage: null,
193
+ cost: { status: "unknown", reason: "not_reported" },
194
+ status: "running",
195
+ failure: null,
196
+ owner: { pid: process.pid, host: hostname() },
197
+ ...input.iteration === void 0 ? {} : { iteration: input.iteration },
198
+ ...input.correctionCycle === void 0 ? {} : { correctionCycle: input.correctionCycle },
199
+ ...input.storyIds === void 0 ? {} : { storyIds: input.storyIds },
200
+ ...input.routingDecision === void 0 || input.routingDecision === null ? {} : { routingDecision: input.routingDecision }
201
+ };
202
+ try {
203
+ await mutate(ctx.tasksPath, (plan) => ({
204
+ ...plan,
205
+ executions: cap([...plan.executions ?? [], record], cfg.maxExecutions)
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
+ });
213
+ return id;
214
+ } catch {
215
+ return null;
216
+ }
217
+ }
218
+ function timingFromUsage(wallClockMs, usage) {
219
+ const cli = usage?.cliDurationMs;
220
+ const cliDurationMs = cli === void 0 ? null : cli;
221
+ const harnessStartupMs = wallClockMs !== null && cli !== void 0 ? Math.max(0, wallClockMs - cli) : null;
222
+ return {
223
+ cliDurationMs,
224
+ harnessStartupMs,
225
+ apiDurationMs: usage?.apiDurationMs ?? null,
226
+ ttftMs: usage?.ttftMs ?? null,
227
+ numTurns: usage?.numTurns ?? null
228
+ };
229
+ }
230
+ async function attachVerdict(verdict) {
231
+ const ctx = context;
232
+ if (ctx === null || verdict == null) return;
233
+ try {
234
+ await mutate(ctx.tasksPath, (plan) => {
235
+ const records = [...plan.executions ?? []];
236
+ if (records.length === 0) return plan;
237
+ const index = records.length - 1;
238
+ const current = records[index];
239
+ if (current === void 0) return plan;
240
+ records[index] = { ...current, verdict };
241
+ return { ...plan, executions: records };
242
+ });
243
+ } catch {
244
+ }
245
+ }
246
+ async function endExecution(input) {
247
+ const ctx = context;
248
+ if (ctx === null) return;
249
+ const cfg = await resolveConfig(ctx);
250
+ if (!cfg.enabled) return;
251
+ const usage = usageFromClaude(input.usage);
252
+ const timedOut = input.timedOut === true || /\btimed out\b/i.test(input.error ?? "");
253
+ const cancelled = input.cancelled === true;
254
+ let status = input.status ?? "completed";
255
+ if (input.status === void 0) {
256
+ if (cancelled) status = "cancelled";
257
+ else if (timedOut) status = "timeout";
258
+ else if (input.error) status = "failed";
259
+ }
260
+ const failure = status === "completed" ? null : {
261
+ kind: classify({
262
+ source: "agent",
263
+ stdout: input.error ?? "",
264
+ exitCode: input.exitCode,
265
+ timedOut
266
+ }).kind,
267
+ message: redactFailureMessage(input.error ?? status),
268
+ exitCode: input.exitCode ?? null
269
+ };
270
+ const completed = { value: null };
271
+ try {
272
+ await mutate(ctx.tasksPath, (plan) => {
273
+ const records = [...plan.executions ?? []];
274
+ const index = records.findIndex((record) => record.id === input.id);
275
+ if (index === -1) return plan;
276
+ const current = records[index];
277
+ if (current === void 0) return plan;
278
+ const started = Date.parse(current.startedAt);
279
+ const finishedAt = isoNow();
280
+ const modelResolved = input.modelResolved ?? current.agent.model.resolved;
281
+ const modelKey = modelResolved ?? current.agent.model.requested;
282
+ records[index] = {
283
+ ...current,
284
+ agent: {
285
+ ...current.agent,
286
+ harnessVersion: input.harnessVersion ?? current.agent.harnessVersion,
287
+ providerSessionId: input.providerSessionId ?? current.agent.providerSessionId,
288
+ model: {
289
+ ...current.agent.model,
290
+ resolved: modelResolved,
291
+ source: input.modelSource ?? (modelResolved ? current.agent.model.source === "unavailable" ? "provider" : current.agent.model.source : current.agent.model.source)
292
+ }
293
+ },
294
+ finishedAt,
295
+ durationMs: Number.isFinite(started) ? Math.max(0, Date.now() - started) : null,
296
+ ...timingFromUsage(
297
+ Number.isFinite(started) ? Math.max(0, Date.now() - started) : null,
298
+ input.usage
299
+ ),
300
+ usage,
301
+ cost: resolveCost({
302
+ reportedUsd: input.reportedUsd ?? input.usage?.costUsd,
303
+ usage,
304
+ modelKey,
305
+ estimate: cfg.pricing.estimate,
306
+ overrides: cfg.pricing.overrides
307
+ }),
308
+ status,
309
+ failure,
310
+ owner: null,
311
+ ...input.storyIds === void 0 ? {} : { storyIds: input.storyIds },
312
+ ...input.stopReason === void 0 ? {} : { stopReason: input.stopReason }
313
+ };
314
+ completed.value = records[index] ?? null;
315
+ return { ...plan, executions: records };
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
+ }
325
+ } catch {
326
+ }
327
+ }
328
+ async function recordInvocation(input, invoke, finish) {
329
+ const id = await beginExecution(input);
330
+ try {
331
+ const result = await invoke();
332
+ if (id !== null) {
333
+ await endExecution({ ...finish(result), id });
334
+ }
335
+ return result;
336
+ } catch (err) {
337
+ if (id !== null) {
338
+ await endExecution({
339
+ id,
340
+ status: "failed",
341
+ error: err instanceof Error ? err.message : String(err)
342
+ });
343
+ }
344
+ throw err;
345
+ }
346
+ }
347
+
348
+ export {
349
+ EXECUTION_PURPOSES,
350
+ DEFAULT_TELEMETRY_CONFIG,
351
+ bindTelemetry,
352
+ getTelemetryContext,
353
+ discardedExecutionCount,
354
+ resetTelemetryState,
355
+ usageFromClaude,
356
+ beginExecution,
357
+ timingFromUsage,
358
+ attachVerdict,
359
+ endExecution,
360
+ recordInvocation
361
+ };
362
+ //# sourceMappingURL=chunk-67YIU2VB.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":";;;;;;;;;;;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;;;ACElB,IAAM,wBAAwB;AAUrC,IAAM,QAAoC;AAAA,EACxC,mBAAmB;AAAA,IACjB,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,sBAAsB;AAAA,EACxB;AAAA,EACA,qBAAqB;AAAA,IACnB,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,sBAAsB;AAAA,EACxB;AAAA,EACA,iBAAiB;AAAA,IACf,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,sBAAsB;AAAA,EACxB;AAAA,EACA,SAAS,EAAE,iBAAiB,MAAM,kBAAkB,GAAG;AAAA,EACvD,WAAW,EAAE,iBAAiB,MAAM,kBAAkB,GAAG;AAC3D;AAGA,IAAM,UAAkC;AAAA,EACtC,QAAQ;AAAA,EACR,MAAM;AACR;AAWO,SAAS,kBAAkB,UAA0B;AAC1D,QAAM,QAAQ,SAAS,KAAK,EAAE,YAAY;AAC1C,QAAM,gBAAgB,MAAM,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC;AAC5D,QAAM,kBAAkB,cAAc,QAAQ,WAAW,EAAE,EAAE,QAAQ,YAAY,EAAE;AACnF,SAAO,QAAQ,eAAe,KAAK;AACrC;AAEA,SAAS,OACP,UACA,WACmB;AACnB,QAAM,aAAa,kBAAkB,QAAQ;AAC7C,QAAM,WAAW,UAAU,QAAQ,KAAK,UAAU,UAAU;AAC5D,QAAM,OAAO,MAAM,QAAQ,KAAK,MAAM,UAAU;AAChD,MAAI,aAAa,UAAa,SAAS,OAAW,QAAO;AACzD,SAAO;AAAA,IACL,iBAAiB,UAAU,mBAAmB,MAAM,mBAAmB;AAAA,IACvE,kBAAkB,UAAU,oBAAoB,MAAM,oBAAoB;AAAA,IAC1E,qBAAqB,UAAU,uBAAuB,MAAM;AAAA,IAC5D,sBAAsB,UAAU,wBAAwB,MAAM;AAAA,EAChE;AACF;AAEA,SAAS,QAAQ,QAA4B,YAAwC;AACnF,MAAI,WAAW,UAAa,eAAe,OAAW,QAAO;AAC7D,SAAQ,SAAS,aAAc;AACjC;AAEO,SAAS,YAAY,SAMb;AACb,MAAI,QAAQ,gBAAgB,UAAa,QAAQ,gBAAgB,MAAM;AACrE,WAAO,EAAE,QAAQ,YAAY,QAAQ,QAAQ,aAAa,UAAU,MAAM;AAAA,EAC5E;AACA,MAAI,CAAC,QAAQ,UAAU;AACrB,WAAO,EAAE,QAAQ,WAAW,QAAQ,eAAe;AAAA,EACrD;AACA,MAAI,QAAQ,aAAa,QAAQ,QAAQ,aAAa,IAAI;AACxD,WAAO,EAAE,QAAQ,WAAW,QAAQ,gBAAgB;AAAA,EACtD;AACA,QAAM,MAAM,OAAO,QAAQ,UAAU,QAAQ,aAAa,CAAC,CAAC;AAC5D,MAAI,QAAQ,MAAM;AAChB,WAAO,EAAE,QAAQ,WAAW,QAAQ,gBAAgB;AAAA,EACtD;AACA,MAAI,QAAQ,UAAU,QAAQ,QAAQ,MAAM,WAAW,eAAe;AACpE,WAAO,EAAE,QAAQ,WAAW,QAAQ,eAAe;AAAA,EACrD;AACA,QAAM,SACJ,QAAQ,QAAQ,MAAM,aAAa,IAAI,eAAe,IACtD,QAAQ,QAAQ,MAAM,cAAc,IAAI,gBAAgB,IACxD,QAAQ,QAAQ,MAAM,iBAAiB,IAAI,mBAAmB,IAC9D,QAAQ,QAAQ,MAAM,qBAAqB,IAAI,oBAAoB;AACrE,QAAM,UAA2B;AAAA,IAC/B,cAAc;AAAA,IACd,UAAU,QAAQ;AAAA,IAClB,iBAAiB,IAAI;AAAA,IACrB,kBAAkB,IAAI;AAAA,IACtB,YAAY,OAAO;AAAA,IACnB,GAAI,IAAI,wBAAwB,SAC5B,CAAC,IACD,EAAE,qBAAqB,IAAI,oBAAoB;AAAA,IACnD,GAAI,IAAI,yBAAyB,SAC7B,CAAC,IACD,EAAE,sBAAsB,IAAI,qBAAqB;AAAA,EACvD;AACA,SAAO,EAAE,QAAQ,aAAa,QAAQ,UAAU,OAAO,QAAQ;AACjE;;;ACvHO,IAAM,qBAAqB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAuJO,IAAM,2BAA4C;AAAA,EACvD,SAAS;AAAA,EACT,eAAe;AAAA,EACf,SAAS,EAAE,UAAU,OAAO,WAAW,CAAC,EAAE;AAC5C;;;AF7IA,IAAI,UAAmC;AACvC,IAAI,iBAAiB;AAEd,SAAS,cAAc,MAAqC;AACjE,YAAU;AACZ;AAEO,SAAS,sBAA+C;AAC7D,SAAO;AACT;AAEO,SAAS,0BAAkC;AAChD,SAAO;AACT;AAEO,SAAS,sBAA4B;AAC1C,YAAU;AACV,mBAAiB;AACjB,wBAAsB,IAAI;AAC5B;AAEA,eAAe,cAAc,KAAiD;AAC5E,MAAI,IAAI,WAAW,OAAW,QAAO,IAAI;AACzC,MAAI;AACF,UAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,sBAAc;AAC3D,QAAI,SAAS,MAAM,oBAAoB;AACvC,WAAO,IAAI;AAAA,EACb,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,IAAI,SAA4B,KAAgC;AACvE,MAAI,QAAQ,UAAU,IAAK,QAAO;AAClC,oBAAkB,QAAQ,SAAS;AACnC,SAAO,QAAQ,MAAM,QAAQ,SAAS,GAAG;AAC3C;AAEA,eAAe,OAAO,WAAmB,QAAqD;AAC5F,QAAM,OAAO,MAAM,aAAa,SAAS;AACzC,QAAM,aAAa,WAAW,OAAO,IAAI,CAAC;AAC5C;AAEO,SAAS,gBAAgB,OAA+D;AAC7F,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,QAAM,OAAwB,EAAE,QAAQ,WAAW;AACnD,MAAI,MAAM,gBAAgB,OAAW,MAAK,cAAc,MAAM;AAC9D,MAAI,MAAM,iBAAiB,OAAW,MAAK,eAAe,MAAM;AAChE,MAAI,MAAM,oBAAoB,OAAW,MAAK,kBAAkB,MAAM;AACtE,MAAI,MAAM,wBAAwB,OAAW,MAAK,sBAAsB,MAAM;AAC9E,SAAO;AACT;AAmBA,eAAsB,eAAe,OAAoD;AACvF,QAAM,MAAM;AACZ,MAAI,QAAQ,KAAM,QAAO;AACzB,QAAM,MAAM,MAAM,cAAc,GAAG;AACnC,MAAI,CAAC,IAAI,QAAS,QAAO;AAEzB,QAAM,KAAK,WAAW;AACtB,QAAM,SAA0B;AAAA,IAC9B;AAAA,IACA,WAAW,IAAI,aAAa,sBAAsB;AAAA,IAClD,SAAS,MAAM;AAAA,IACf,SAAS,MAAM,WAAW;AAAA,IAC1B,SAAS,MAAM,WAAW;AAAA,IAC1B,eAAe,MAAM,iBAAiB;AAAA,IACtC,OAAO;AAAA,MACL,SAAS,MAAM;AAAA,MACf,UAAU,MAAM,YAAY;AAAA,MAC5B,gBAAgB,MAAM,kBAAkB;AAAA,MACxC,OAAO;AAAA,QACL,WAAW,MAAM,kBAAkB;AAAA,QACnC,UAAU,MAAM,iBAAiB;AAAA,QACjC,QAAQ,MAAM,gBAAgB,MAAM,gBAAgB,aAAa;AAAA,MACnE;AAAA,MACA,mBAAmB;AAAA,IACrB;AAAA,IACA,WAAW,OAAO;AAAA,IAClB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,MAAM,EAAE,QAAQ,WAAW,QAAQ,eAAe;AAAA,IAClD,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO,EAAE,KAAK,QAAQ,KAAK,MAAM,SAAS,EAAE;AAAA,IAC5C,GAAI,MAAM,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,MAAM,UAAU;AAAA,IACtE,GAAI,MAAM,oBAAoB,SAAY,CAAC,IAAI,EAAE,iBAAiB,MAAM,gBAAgB;AAAA,IACxF,GAAI,MAAM,aAAa,SAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;AAAA,IACnE,GAAI,MAAM,oBAAoB,UAAa,MAAM,oBAAoB,OACjE,CAAC,IACD,EAAE,iBAAiB,MAAM,gBAAgB;AAAA,EAC/C;AAEA,MAAI;AACF,UAAM,OAAO,IAAI,WAAW,CAAC,UAAU;AAAA,MACrC,GAAG;AAAA,MACH,YAAY,IAAI,CAAC,GAAI,KAAK,cAAc,CAAC,GAAI,MAAM,GAAG,IAAI,aAAa;AAAA,IACzE,EAAE;AACF,UAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,iCAA8B;AAC3E,wBAAoB,EAAE,QAAQ;AAAA,MAC5B,MAAM;AAAA,MACN,IAAI,OAAO;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AACD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,gBACd,aACA,OAIA;AACA,QAAM,MAAM,OAAO;AACnB,QAAM,gBAAgB,QAAQ,SAAY,OAAO;AACjD,QAAM,mBACJ,gBAAgB,QAAQ,QAAQ,SAAY,KAAK,IAAI,GAAG,cAAc,GAAG,IAAI;AAC/E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe,OAAO,iBAAiB;AAAA,IACvC,QAAQ,OAAO,UAAU;AAAA,IACzB,UAAU,OAAO,YAAY;AAAA,EAC/B;AACF;AAEA,eAAsB,cAAc,SAAoD;AACtF,QAAM,MAAM;AACZ,MAAI,QAAQ,QAAQ,WAAW,KAAM;AACrC,MAAI;AACF,UAAM,OAAO,IAAI,WAAW,CAAC,SAAS;AACpC,YAAM,UAAU,CAAC,GAAI,KAAK,cAAc,CAAC,CAAE;AAC3C,UAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,YAAM,QAAQ,QAAQ,SAAS;AAC/B,YAAM,UAAU,QAAQ,KAAK;AAC7B,UAAI,YAAY,OAAW,QAAO;AAClC,cAAQ,KAAK,IAAI,EAAE,GAAG,SAAS,QAAQ;AACvC,aAAO,EAAE,GAAG,MAAM,YAAY,QAAQ;AAAA,IACxC,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAmBA,eAAsB,aAAa,OAAyC;AAC1E,QAAM,MAAM;AACZ,MAAI,QAAQ,KAAM;AAClB,QAAM,MAAM,MAAM,cAAc,GAAG;AACnC,MAAI,CAAC,IAAI,QAAS;AAElB,QAAM,QAAQ,gBAAgB,MAAM,KAAK;AACzC,QAAM,WAAW,MAAM,aAAa,QAAQ,iBAAiB,KAAK,MAAM,SAAS,EAAE;AACnF,QAAM,YAAY,MAAM,cAAc;AACtC,MAAI,SAA0B,MAAM,UAAU;AAC9C,MAAI,MAAM,WAAW,QAAW;AAC9B,QAAI,UAAW,UAAS;AAAA,aACf,SAAU,UAAS;AAAA,aACnB,MAAM,MAAO,UAAS;AAAA,EACjC;AAEA,QAAM,UACJ,WAAW,cACP,OACA;AAAA,IACE,MAAM,SAAS;AAAA,MACb,QAAQ;AAAA,MACR,QAAQ,MAAM,SAAS;AAAA,MACvB,UAAU,MAAM;AAAA,MAChB;AAAA,IACF,CAAC,EAAE;AAAA,IACH,SAAS,qBAAqB,MAAM,SAAS,MAAM;AAAA,IACnD,UAAU,MAAM,YAAY;AAAA,EAC9B;AAEN,QAAM,YAA+C,EAAE,OAAO,KAAK;AACnE,MAAI;AACF,UAAM,OAAO,IAAI,WAAW,CAAC,SAAS;AACpC,YAAM,UAAU,CAAC,GAAI,KAAK,cAAc,CAAC,CAAE;AAC3C,YAAM,QAAQ,QAAQ,UAAU,CAAC,WAAW,OAAO,OAAO,MAAM,EAAE;AAClE,UAAI,UAAU,GAAI,QAAO;AACzB,YAAM,UAAU,QAAQ,KAAK;AAC7B,UAAI,YAAY,OAAW,QAAO;AAClC,YAAM,UAAU,KAAK,MAAM,QAAQ,SAAS;AAC5C,YAAM,aAAa,OAAO;AAC1B,YAAM,gBAAgB,MAAM,iBAAiB,QAAQ,MAAM,MAAM;AACjE,YAAM,WAAW,iBAAiB,QAAQ,MAAM,MAAM;AACtD,cAAQ,KAAK,IAAI;AAAA,QACf,GAAG;AAAA,QACH,OAAO;AAAA,UACL,GAAG,QAAQ;AAAA,UACX,gBAAgB,MAAM,kBAAkB,QAAQ,MAAM;AAAA,UACtD,mBAAmB,MAAM,qBAAqB,QAAQ,MAAM;AAAA,UAC5D,OAAO;AAAA,YACL,GAAG,QAAQ,MAAM;AAAA,YACjB,UAAU;AAAA,YACV,QACE,MAAM,gBACL,gBACG,QAAQ,MAAM,MAAM,WAAW,gBAC7B,aACA,QAAQ,MAAM,MAAM,SACtB,QAAQ,MAAM,MAAM;AAAA,UAC5B;AAAA,QACF;AAAA,QACA;AAAA,QACA,YAAY,OAAO,SAAS,OAAO,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,IAAI;AAAA,QAC3E,GAAG;AAAA,UACD,OAAO,SAAS,OAAO,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,IAAI;AAAA,UAC/D,MAAM;AAAA,QACR;AAAA,QACA;AAAA,QACA,MAAM,YAAY;AAAA,UAChB,aAAa,MAAM,eAAe,MAAM,OAAO;AAAA,UAC/C;AAAA,UACA;AAAA,UACA,UAAU,IAAI,QAAQ;AAAA,UACtB,WAAW,IAAI,QAAQ;AAAA,QACzB,CAAC;AAAA,QACD;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP,GAAI,MAAM,aAAa,SAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;AAAA,QACnE,GAAI,MAAM,eAAe,SAAY,CAAC,IAAI,EAAE,YAAY,MAAM,WAAW;AAAA,MAC3E;AACA,gBAAU,QAAQ,QAAQ,KAAK,KAAK;AACpC,aAAO,EAAE,GAAG,MAAM,YAAY,QAAQ;AAAA,IACxC,CAAC;AACD,QAAI,UAAU,UAAU,MAAM;AAC5B,YAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,iCAA8B;AAC3E,0BAAoB,EAAE,QAAQ;AAAA,QAC5B,MAAM;AAAA,QACN,IAAI,UAAU,MAAM,cAAc,OAAO;AAAA,QACzC,WAAW,UAAU;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAEA,eAAsB,iBACpB,OACA,QACA,QACY;AACZ,QAAM,KAAK,MAAM,eAAe,KAAK;AACrC,MAAI;AACF,UAAM,SAAS,MAAM,OAAO;AAC5B,QAAI,OAAO,MAAM;AACf,YAAM,aAAa,EAAE,GAAG,OAAO,MAAM,GAAG,GAAG,CAAC;AAAA,IAC9C;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,OAAO,MAAM;AACf,YAAM,aAAa;AAAA,QACjB;AAAA,QACA,QAAQ;AAAA,QACR,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;","names":[]}