@probelabs/visor 0.1.186-ee → 0.1.187-ee

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 (194) hide show
  1. package/defaults/assistant.yaml +31 -0
  2. package/defaults/code-talk.yaml +32 -0
  3. package/defaults/skills/code-explorer.yaml +6 -0
  4. package/dist/agent-protocol/task-live-update-slack.d.ts +10 -0
  5. package/dist/agent-protocol/task-live-update-slack.d.ts.map +1 -1
  6. package/dist/agent-protocol/task-live-updates.d.ts +10 -0
  7. package/dist/agent-protocol/task-live-updates.d.ts.map +1 -1
  8. package/dist/agent-protocol/tasks-cli-handler.d.ts.map +1 -1
  9. package/dist/agent-protocol/trace-serializer.d.ts +43 -1
  10. package/dist/agent-protocol/trace-serializer.d.ts.map +1 -1
  11. package/dist/agent-protocol/track-execution.d.ts.map +1 -1
  12. package/dist/ai-review-service.d.ts +22 -1
  13. package/dist/ai-review-service.d.ts.map +1 -1
  14. package/dist/cli-main.d.ts +1 -0
  15. package/dist/cli-main.d.ts.map +1 -1
  16. package/dist/cli.d.ts.map +1 -1
  17. package/dist/defaults/assistant.yaml +31 -0
  18. package/dist/defaults/code-talk.yaml +32 -0
  19. package/dist/defaults/skills/code-explorer.yaml +6 -0
  20. package/dist/docs/telemetry-setup.md +18 -2
  21. package/dist/email/polling-runner.d.ts +1 -0
  22. package/dist/email/polling-runner.d.ts.map +1 -1
  23. package/dist/frontends/email-frontend.d.ts.map +1 -1
  24. package/dist/frontends/slack-frontend.d.ts.map +1 -1
  25. package/dist/frontends/teams-frontend.d.ts.map +1 -1
  26. package/dist/frontends/telegram-frontend.d.ts.map +1 -1
  27. package/dist/frontends/whatsapp-frontend.d.ts.map +1 -1
  28. package/dist/generated/config-schema.d.ts +16 -8
  29. package/dist/generated/config-schema.d.ts.map +1 -1
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +7658 -1460
  33. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  34. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  35. package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
  36. package/dist/providers/script-check-provider.d.ts.map +1 -1
  37. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  38. package/dist/runners/mcp-server-runner.d.ts.map +1 -1
  39. package/dist/scheduler/scheduler.d.ts.map +1 -1
  40. package/dist/sdk/a2a-frontend-7EGXSULE.mjs +1751 -0
  41. package/dist/sdk/a2a-frontend-7EGXSULE.mjs.map +1 -0
  42. package/dist/sdk/{a2a-frontend-OI4OVSKC.mjs → a2a-frontend-QVKJ7MVW.mjs} +3 -3
  43. package/dist/sdk/{check-provider-registry-TQR32QBD.mjs → check-provider-registry-IGNTM4MW.mjs} +5 -5
  44. package/dist/sdk/{check-provider-registry-7T2AEWLU.mjs → check-provider-registry-QUUL6JI2.mjs} +14 -11
  45. package/dist/sdk/chunk-2LLKENYD.mjs +33 -0
  46. package/dist/sdk/chunk-2LLKENYD.mjs.map +1 -0
  47. package/dist/sdk/{chunk-XX6WT332.mjs → chunk-6DDNEBZ7.mjs} +2 -1
  48. package/dist/sdk/{chunk-XX6WT332.mjs.map → chunk-6DDNEBZ7.mjs.map} +1 -1
  49. package/dist/sdk/chunk-6LQEODDA.mjs +391 -0
  50. package/dist/sdk/chunk-6LQEODDA.mjs.map +1 -0
  51. package/dist/sdk/chunk-77AKSSW2.mjs +175 -0
  52. package/dist/sdk/chunk-77AKSSW2.mjs.map +1 -0
  53. package/dist/sdk/chunk-7QI6F66G.mjs +145 -0
  54. package/dist/sdk/chunk-7QI6F66G.mjs.map +1 -0
  55. package/dist/sdk/{chunk-UM7LGO2P.mjs → chunk-BBLBZVWC.mjs} +3 -3
  56. package/dist/sdk/chunk-BVEATT5V.mjs +2495 -0
  57. package/dist/sdk/chunk-BVEATT5V.mjs.map +1 -0
  58. package/dist/sdk/{chunk-SRU5TFNY.mjs → chunk-D6O5SCPC.mjs} +2 -1
  59. package/dist/sdk/{chunk-SRU5TFNY.mjs.map → chunk-D6O5SCPC.mjs.map} +1 -1
  60. package/dist/sdk/chunk-H2ZWNJJO.mjs +204 -0
  61. package/dist/sdk/chunk-H2ZWNJJO.mjs.map +1 -0
  62. package/dist/sdk/{chunk-TAOGXCWT.mjs → chunk-HMVD3AEC.mjs} +16 -16
  63. package/dist/sdk/{chunk-LWH6CVLZ.mjs → chunk-HQCCT6WP.mjs} +570 -259
  64. package/dist/sdk/chunk-HQCCT6WP.mjs.map +1 -0
  65. package/dist/sdk/chunk-HW4MPCOW.mjs +691 -0
  66. package/dist/sdk/chunk-HW4MPCOW.mjs.map +1 -0
  67. package/dist/sdk/chunk-PGBV3VZB.mjs +122 -0
  68. package/dist/sdk/chunk-PGBV3VZB.mjs.map +1 -0
  69. package/dist/sdk/chunk-PZADRRLX.mjs +739 -0
  70. package/dist/sdk/chunk-PZADRRLX.mjs.map +1 -0
  71. package/dist/sdk/chunk-QT766ZLD.mjs +217 -0
  72. package/dist/sdk/chunk-QT766ZLD.mjs.map +1 -0
  73. package/dist/sdk/chunk-RQDLVMER.mjs +1507 -0
  74. package/dist/sdk/chunk-RQDLVMER.mjs.map +1 -0
  75. package/dist/sdk/chunk-TL3ZTYFY.mjs +141 -0
  76. package/dist/sdk/chunk-TL3ZTYFY.mjs.map +1 -0
  77. package/dist/sdk/{chunk-YBXNG75V.mjs → chunk-YTG5DN72.mjs} +103 -28
  78. package/dist/sdk/chunk-YTG5DN72.mjs.map +1 -0
  79. package/dist/sdk/chunk-YU3VR5SZ.mjs +749 -0
  80. package/dist/sdk/chunk-YU3VR5SZ.mjs.map +1 -0
  81. package/dist/sdk/chunk-ZCBVXBKG.mjs +1150 -0
  82. package/dist/sdk/chunk-ZCBVXBKG.mjs.map +1 -0
  83. package/dist/sdk/{config-BCYAVJ5S.mjs → config-BI5ZXG6D.mjs} +2 -2
  84. package/dist/sdk/{config-TVU5RWR5.mjs → config-H5IOLBFA.mjs} +2 -2
  85. package/dist/sdk/{dist-PN5UHL6A.mjs → dist-YLO6HUFD.mjs} +238 -65
  86. package/dist/sdk/dist-YLO6HUFD.mjs.map +1 -0
  87. package/dist/sdk/{email-frontend-6JU4L33L.mjs → email-frontend-6O454VL5.mjs} +18 -3
  88. package/dist/sdk/{email-frontend-ECHQCFTR.mjs.map → email-frontend-6O454VL5.mjs.map} +1 -1
  89. package/dist/sdk/{email-frontend-ECHQCFTR.mjs → email-frontend-JE7H3LBC.mjs} +2 -2
  90. package/dist/sdk/failure-condition-evaluator-7WHZUYDC.mjs +18 -0
  91. package/dist/sdk/github-frontend-RGK4DPRI.mjs +1394 -0
  92. package/dist/sdk/github-frontend-RGK4DPRI.mjs.map +1 -0
  93. package/dist/sdk/{host-RM6UJEPP.mjs → host-UZESVA6Z.mjs} +8 -8
  94. package/dist/sdk/{host-OJSMCLKK.mjs → host-YWXWROIE.mjs} +6 -6
  95. package/dist/sdk/oauth2-token-cache-OS7WHLDI.mjs +13 -0
  96. package/dist/sdk/{routing-NNQQSLWA.mjs → routing-PHHRW2C7.mjs} +2 -2
  97. package/dist/sdk/routing-TYXT3STD.mjs +26 -0
  98. package/dist/sdk/{schedule-tool-7IQI6MID.mjs → schedule-tool-ITD445EO.mjs} +5 -5
  99. package/dist/sdk/{schedule-tool-25C5NMDG.mjs → schedule-tool-ODMIDBMT.mjs} +14 -11
  100. package/dist/sdk/{schedule-tool-handler-IYBNJLTV.mjs → schedule-tool-handler-5WPHFBOG.mjs} +5 -5
  101. package/dist/sdk/{schedule-tool-handler-VWGTVSB6.mjs → schedule-tool-handler-IQSKBFLS.mjs} +14 -11
  102. package/dist/sdk/schedule-tool-handler-IQSKBFLS.mjs.map +1 -0
  103. package/dist/sdk/sdk.d.mts +14 -2
  104. package/dist/sdk/sdk.d.ts +14 -2
  105. package/dist/sdk/sdk.js +1926 -642
  106. package/dist/sdk/sdk.js.map +1 -1
  107. package/dist/sdk/sdk.mjs +13 -10
  108. package/dist/sdk/sdk.mjs.map +1 -1
  109. package/dist/sdk/session-registry-CENXWIFR.mjs +12 -0
  110. package/dist/sdk/session-registry-CENXWIFR.mjs.map +1 -0
  111. package/dist/sdk/{slack-frontend-K3TPGERM.mjs → slack-frontend-H7PX5GE7.mjs} +2 -2
  112. package/dist/sdk/slack-frontend-JMX5MUVZ.mjs +1015 -0
  113. package/dist/sdk/slack-frontend-JMX5MUVZ.mjs.map +1 -0
  114. package/dist/sdk/task-evaluator-4QG4CYQA.mjs +264 -0
  115. package/dist/sdk/task-evaluator-4QG4CYQA.mjs.map +1 -0
  116. package/dist/sdk/teams-frontend-6MDAN7OR.mjs +355 -0
  117. package/dist/sdk/teams-frontend-6MDAN7OR.mjs.map +1 -0
  118. package/dist/sdk/{teams-frontend-6RRW533K.mjs → teams-frontend-HCT4MTY4.mjs} +2 -2
  119. package/dist/sdk/telegram-frontend-NLX2ZHS4.mjs +599 -0
  120. package/dist/sdk/telegram-frontend-NLX2ZHS4.mjs.map +1 -0
  121. package/dist/sdk/{telegram-frontend-5UA77YAT.mjs → telegram-frontend-YQRYDKPN.mjs} +2 -2
  122. package/dist/sdk/trace-helpers-AM2QE5UZ.mjs +31 -0
  123. package/dist/sdk/trace-helpers-AM2QE5UZ.mjs.map +1 -0
  124. package/dist/sdk/trace-serializer-4TOIAVQL.mjs +32 -0
  125. package/dist/sdk/trace-serializer-4TOIAVQL.mjs.map +1 -0
  126. package/dist/sdk/{track-execution-3CHMGEPH.mjs → track-execution-BULXL2XD.mjs} +2 -2
  127. package/dist/sdk/track-execution-VJJVJH6K.mjs +320 -0
  128. package/dist/sdk/track-execution-VJJVJH6K.mjs.map +1 -0
  129. package/dist/sdk/utcp-check-provider-WDTSQ7DZ.mjs +17 -0
  130. package/dist/sdk/utcp-check-provider-WDTSQ7DZ.mjs.map +1 -0
  131. package/dist/sdk/whatsapp-frontend-3EMMNC7R.mjs +451 -0
  132. package/dist/sdk/whatsapp-frontend-3EMMNC7R.mjs.map +1 -0
  133. package/dist/sdk/{workflow-check-provider-FOWDJUO3.mjs → workflow-check-provider-OVZDCL4E.mjs} +5 -5
  134. package/dist/sdk/workflow-check-provider-OVZDCL4E.mjs.map +1 -0
  135. package/dist/sdk/{workflow-check-provider-MRP3C2NC.mjs → workflow-check-provider-XOWQYWTC.mjs} +14 -11
  136. package/dist/sdk/workflow-check-provider-XOWQYWTC.mjs.map +1 -0
  137. package/dist/session-registry.d.ts +2 -7
  138. package/dist/session-registry.d.ts.map +1 -1
  139. package/dist/slack/client.d.ts +8 -0
  140. package/dist/slack/client.d.ts.map +1 -1
  141. package/dist/slack/markdown.d.ts.map +1 -1
  142. package/dist/slack/slack-output-adapter.d.ts.map +1 -1
  143. package/dist/slack/socket-runner.d.ts +1 -0
  144. package/dist/slack/socket-runner.d.ts.map +1 -1
  145. package/dist/state-machine/dispatch/debounce-manager.d.ts +44 -0
  146. package/dist/state-machine/dispatch/debounce-manager.d.ts.map +1 -0
  147. package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
  148. package/dist/state-machine-execution-engine.d.ts +1 -0
  149. package/dist/state-machine-execution-engine.d.ts.map +1 -1
  150. package/dist/teams/webhook-runner.d.ts +1 -0
  151. package/dist/teams/webhook-runner.d.ts.map +1 -1
  152. package/dist/telegram/polling-runner.d.ts +1 -0
  153. package/dist/telegram/polling-runner.d.ts.map +1 -1
  154. package/dist/types/config.d.ts +14 -2
  155. package/dist/types/config.d.ts.map +1 -1
  156. package/dist/utils/child-process-error-handler.d.ts +19 -0
  157. package/dist/utils/child-process-error-handler.d.ts.map +1 -0
  158. package/dist/utils/user-facing-error.d.ts +4 -0
  159. package/dist/utils/user-facing-error.d.ts.map +1 -0
  160. package/dist/utils/workspace-manager.d.ts +5 -0
  161. package/dist/utils/workspace-manager.d.ts.map +1 -1
  162. package/dist/utils/worktree-cleanup.d.ts +28 -0
  163. package/dist/utils/worktree-cleanup.d.ts.map +1 -1
  164. package/dist/utils/worktree-manager.d.ts +6 -0
  165. package/dist/utils/worktree-manager.d.ts.map +1 -1
  166. package/dist/whatsapp/webhook-runner.d.ts +1 -0
  167. package/dist/whatsapp/webhook-runner.d.ts.map +1 -1
  168. package/dist/workflow-executor.d.ts +7 -0
  169. package/dist/workflow-executor.d.ts.map +1 -1
  170. package/package.json +3 -3
  171. package/dist/sdk/chunk-LWH6CVLZ.mjs.map +0 -1
  172. package/dist/sdk/chunk-YBXNG75V.mjs.map +0 -1
  173. package/dist/sdk/dist-PN5UHL6A.mjs.map +0 -1
  174. /package/dist/sdk/{a2a-frontend-OI4OVSKC.mjs.map → a2a-frontend-QVKJ7MVW.mjs.map} +0 -0
  175. /package/dist/sdk/{check-provider-registry-7T2AEWLU.mjs.map → check-provider-registry-IGNTM4MW.mjs.map} +0 -0
  176. /package/dist/sdk/{check-provider-registry-TQR32QBD.mjs.map → check-provider-registry-QUUL6JI2.mjs.map} +0 -0
  177. /package/dist/sdk/{chunk-UM7LGO2P.mjs.map → chunk-BBLBZVWC.mjs.map} +0 -0
  178. /package/dist/sdk/{chunk-TAOGXCWT.mjs.map → chunk-HMVD3AEC.mjs.map} +0 -0
  179. /package/dist/sdk/{config-BCYAVJ5S.mjs.map → config-BI5ZXG6D.mjs.map} +0 -0
  180. /package/dist/sdk/{config-TVU5RWR5.mjs.map → config-H5IOLBFA.mjs.map} +0 -0
  181. /package/dist/sdk/{email-frontend-6JU4L33L.mjs.map → email-frontend-JE7H3LBC.mjs.map} +0 -0
  182. /package/dist/sdk/{routing-NNQQSLWA.mjs.map → failure-condition-evaluator-7WHZUYDC.mjs.map} +0 -0
  183. /package/dist/sdk/{host-OJSMCLKK.mjs.map → host-UZESVA6Z.mjs.map} +0 -0
  184. /package/dist/sdk/{host-RM6UJEPP.mjs.map → host-YWXWROIE.mjs.map} +0 -0
  185. /package/dist/sdk/{schedule-tool-25C5NMDG.mjs.map → oauth2-token-cache-OS7WHLDI.mjs.map} +0 -0
  186. /package/dist/sdk/{schedule-tool-7IQI6MID.mjs.map → routing-PHHRW2C7.mjs.map} +0 -0
  187. /package/dist/sdk/{schedule-tool-handler-IYBNJLTV.mjs.map → routing-TYXT3STD.mjs.map} +0 -0
  188. /package/dist/sdk/{schedule-tool-handler-VWGTVSB6.mjs.map → schedule-tool-ITD445EO.mjs.map} +0 -0
  189. /package/dist/sdk/{workflow-check-provider-FOWDJUO3.mjs.map → schedule-tool-ODMIDBMT.mjs.map} +0 -0
  190. /package/dist/sdk/{workflow-check-provider-MRP3C2NC.mjs.map → schedule-tool-handler-5WPHFBOG.mjs.map} +0 -0
  191. /package/dist/sdk/{slack-frontend-K3TPGERM.mjs.map → slack-frontend-H7PX5GE7.mjs.map} +0 -0
  192. /package/dist/sdk/{teams-frontend-6RRW533K.mjs.map → teams-frontend-HCT4MTY4.mjs.map} +0 -0
  193. /package/dist/sdk/{telegram-frontend-5UA77YAT.mjs.map → telegram-frontend-YQRYDKPN.mjs.map} +0 -0
  194. /package/dist/sdk/{track-execution-3CHMGEPH.mjs.map → track-execution-BULXL2XD.mjs.map} +0 -0
@@ -0,0 +1,2495 @@
1
+ import {
2
+ init_logger,
3
+ logger
4
+ } from "./chunk-6E625R3C.mjs";
5
+ import {
6
+ __esm
7
+ } from "./chunk-J7LXIPZS.mjs";
8
+
9
+ // src/agent-protocol/trace-serializer.ts
10
+ import * as fs from "fs";
11
+ import * as readline from "readline";
12
+ import * as path from "path";
13
+ function resolveBackendConfig(overrides) {
14
+ const explicit = process.env.VISOR_TRACE_BACKEND;
15
+ return {
16
+ type: overrides?.type || explicit || "auto",
17
+ grafanaUrl: overrides?.grafanaUrl || process.env.GRAFANA_URL,
18
+ grafanaDatasourceId: overrides?.grafanaDatasourceId || process.env.GRAFANA_TEMPO_DATASOURCE_ID,
19
+ jaegerUrl: overrides?.jaegerUrl || process.env.JAEGER_URL,
20
+ traceDir: overrides?.traceDir || process.env.VISOR_TRACE_DIR || "output/traces",
21
+ authToken: overrides?.authToken || process.env.GRAFANA_TOKEN,
22
+ targetTraceId: overrides?.targetTraceId
23
+ };
24
+ }
25
+ function getAutoBackendOrder() {
26
+ const sink = (process.env.VISOR_TELEMETRY_SINK || "").trim().toLowerCase();
27
+ const hasRemoteHints = !!process.env.GRAFANA_URL || !!process.env.JAEGER_URL || !!process.env.GRAFANA_TEMPO_DATASOURCE_ID || !!process.env.OTEL_EXPORTER_OTLP_ENDPOINT || !!process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;
28
+ if (sink === "file") return ["file", "grafana", "jaeger"];
29
+ if (sink === "otlp" || hasRemoteHints) return ["grafana", "jaeger", "file"];
30
+ return ["file", "grafana", "jaeger"];
31
+ }
32
+ function getBackendOrder(cfg) {
33
+ if (cfg.type === "grafana") return ["grafana"];
34
+ if (cfg.type === "jaeger") return ["jaeger"];
35
+ if (cfg.type === "file") return ["file"];
36
+ return getAutoBackendOrder();
37
+ }
38
+ function isTraceFilePath(ref) {
39
+ return ref.includes("/") || ref.endsWith(".ndjson");
40
+ }
41
+ function parseOTLPResponse(data) {
42
+ const spans = [];
43
+ if (data.batches) {
44
+ for (const batch of data.batches) {
45
+ for (const ss of batch.scopeSpans || []) {
46
+ for (const s of ss.spans || []) {
47
+ spans.push(normalizeOTLPSpan(s));
48
+ }
49
+ }
50
+ }
51
+ return spans;
52
+ }
53
+ if (data.data && Array.isArray(data.data)) {
54
+ for (const trace of data.data) {
55
+ for (const s of trace.spans || []) {
56
+ spans.push(normalizeJaegerSpan(s, trace.traceID));
57
+ }
58
+ }
59
+ return spans;
60
+ }
61
+ return spans;
62
+ }
63
+ function normalizeOTLPSpan(s) {
64
+ const startNs = parseInt(s.startTimeUnixNano || "0", 10);
65
+ const endNs = parseInt(s.endTimeUnixNano || "0", 10);
66
+ const traceId = decodeOTLPId(s.traceId);
67
+ const spanId = decodeOTLPId(s.spanId);
68
+ const parentSpanId = s.parentSpanId ? decodeOTLPId(s.parentSpanId) : void 0;
69
+ const attributes = {};
70
+ for (const attr of s.attributes || []) {
71
+ const val = attr.value;
72
+ if (val.stringValue !== void 0) attributes[attr.key] = val.stringValue;
73
+ else if (val.intValue !== void 0) attributes[attr.key] = parseInt(val.intValue, 10);
74
+ else if (val.boolValue !== void 0) attributes[attr.key] = val.boolValue;
75
+ else if (val.doubleValue !== void 0) attributes[attr.key] = val.doubleValue;
76
+ }
77
+ const events = [];
78
+ for (const evt of s.events || []) {
79
+ const evtAttrs = {};
80
+ for (const a of evt.attributes || []) {
81
+ const v = a.value;
82
+ if (v.stringValue !== void 0) evtAttrs[a.key] = v.stringValue;
83
+ else if (v.intValue !== void 0) evtAttrs[a.key] = parseInt(v.intValue, 10);
84
+ }
85
+ events.push({ name: evt.name, attributes: evtAttrs });
86
+ }
87
+ return {
88
+ traceId,
89
+ spanId,
90
+ parentSpanId,
91
+ name: s.name || "unknown",
92
+ startTimeMs: startNs / 1e6,
93
+ endTimeMs: endNs / 1e6,
94
+ durationMs: (endNs - startNs) / 1e6,
95
+ attributes,
96
+ events,
97
+ status: s.status?.code === 2 ? "error" : "ok"
98
+ };
99
+ }
100
+ function normalizeJaegerSpan(s, traceId) {
101
+ const attributes = {};
102
+ for (const tag of s.tags || []) {
103
+ attributes[tag.key] = tag.value;
104
+ }
105
+ const events = [];
106
+ for (const log of s.logs || []) {
107
+ const evtAttrs = {};
108
+ for (const f of log.fields || []) evtAttrs[f.key] = f.value;
109
+ events.push({ name: evtAttrs["event"] || "log", attributes: evtAttrs });
110
+ }
111
+ const startUs = s.startTime || 0;
112
+ const durationUs = s.duration || 0;
113
+ return {
114
+ traceId,
115
+ spanId: s.spanID,
116
+ parentSpanId: s.references?.find((r) => r.refType === "CHILD_OF")?.spanID,
117
+ name: s.operationName || "unknown",
118
+ startTimeMs: startUs / 1e3,
119
+ endTimeMs: (startUs + durationUs) / 1e3,
120
+ durationMs: durationUs / 1e3,
121
+ attributes,
122
+ events,
123
+ status: attributes["otel.status_code"] === "ERROR" || attributes["error"] === true ? "error" : "ok"
124
+ };
125
+ }
126
+ function decodeOTLPId(id) {
127
+ if (!id) return "";
128
+ if (/^[0-9a-f]+$/i.test(id)) return id.toLowerCase();
129
+ try {
130
+ return Buffer.from(id, "base64").toString("hex");
131
+ } catch {
132
+ return id;
133
+ }
134
+ }
135
+ function parseLocalNDJSONSpans(spans) {
136
+ return spans.map((s) => {
137
+ const startMs = timeValueToMs(s.startTime || [0, 0]);
138
+ const endMs = timeValueToMs(s.endTime || s.startTime || [0, 0]);
139
+ const events = (s.events || []).map((e) => ({
140
+ name: e.name,
141
+ attributes: e.attributes || {}
142
+ }));
143
+ return {
144
+ traceId: s.traceId || "",
145
+ spanId: s.spanId || "",
146
+ parentSpanId: s.parentSpanId || void 0,
147
+ name: s.name || "unknown",
148
+ startTimeMs: startMs,
149
+ endTimeMs: endMs,
150
+ durationMs: endMs - startMs,
151
+ attributes: s.attributes || {},
152
+ events,
153
+ status: s.status?.code === 2 ? "error" : "ok"
154
+ };
155
+ });
156
+ }
157
+ function timeValueToMs(tv) {
158
+ return tv[0] * 1e3 + tv[1] / 1e6;
159
+ }
160
+ async function fetchTraceSpans(traceRef, config) {
161
+ const cfg = resolveBackendConfig(config);
162
+ const backendOrder = getBackendOrder(cfg);
163
+ const traceId = isTraceFilePath(traceRef) ? await readTraceIdFromFile(traceRef) : traceRef;
164
+ for (const backend of backendOrder) {
165
+ if (backend === "grafana" && traceId) {
166
+ const spans = await fetchFromGrafanaTempo(traceId, cfg);
167
+ if (spans && spans.length > 0) return spans;
168
+ continue;
169
+ }
170
+ if (backend === "jaeger" && traceId) {
171
+ const spans = await fetchFromJaeger(traceId, cfg);
172
+ if (spans && spans.length > 0) return spans;
173
+ continue;
174
+ }
175
+ if (backend === "file") {
176
+ const spans = await fetchFromLocalFiles(traceRef, cfg);
177
+ if (spans && spans.length > 0) return spans;
178
+ continue;
179
+ }
180
+ }
181
+ return [];
182
+ }
183
+ async function resolveTraceSpans(traceIdOrPath, backendConfig, fallbackTraceId) {
184
+ const cfg = resolveBackendConfig(backendConfig);
185
+ const backendOrder = getBackendOrder(cfg);
186
+ const isFilePath = isTraceFilePath(traceIdOrPath);
187
+ const localTracePath = isFilePath ? traceIdOrPath : void 0;
188
+ const remoteTraceId = fallbackTraceId || (!isFilePath ? traceIdOrPath : await readTraceIdFromFile(traceIdOrPath) || void 0);
189
+ const preferLocalFirst = backendOrder[0] === "file";
190
+ logger.debug(
191
+ `[TraceSerializer] serializeTraceForPrompt ref=${traceIdOrPath} remoteTraceId=${remoteTraceId || "-"} backendOrder=${backendOrder.join(">")}`
192
+ );
193
+ if (preferLocalFirst && localTracePath) {
194
+ logger.debug(`[TraceSerializer] Trying local trace file first: ${localTracePath}`);
195
+ const spans = await fetchTraceSpans(localTracePath, {
196
+ ...cfg,
197
+ type: "file",
198
+ targetTraceId: remoteTraceId
199
+ });
200
+ if (spans.length > 0) {
201
+ return { spans, source: "file", remoteTraceId, localTracePath };
202
+ }
203
+ }
204
+ if (remoteTraceId) {
205
+ logger.debug(`[TraceSerializer] Trying remote trace backends for trace_id=${remoteTraceId}`);
206
+ const remoteCfg = resolveBackendConfig(backendConfig);
207
+ const remoteOrder = getBackendOrder(remoteCfg).filter((kind) => kind !== "file");
208
+ for (const backend of remoteOrder) {
209
+ if (backend === "grafana") {
210
+ const spans = await fetchFromGrafanaTempo(remoteTraceId, remoteCfg);
211
+ if (spans && spans.length > 0) {
212
+ return { spans, source: "grafana", remoteTraceId, localTracePath };
213
+ }
214
+ continue;
215
+ }
216
+ if (backend === "jaeger") {
217
+ const spans = await fetchFromJaeger(remoteTraceId, remoteCfg);
218
+ if (spans && spans.length > 0) {
219
+ return { spans, source: "jaeger", remoteTraceId, localTracePath };
220
+ }
221
+ }
222
+ }
223
+ }
224
+ if (localTracePath) {
225
+ logger.debug(`[TraceSerializer] Falling back to local trace file: ${localTracePath}`);
226
+ const spans = await fetchTraceSpans(localTracePath, {
227
+ ...cfg,
228
+ type: "file",
229
+ targetTraceId: remoteTraceId
230
+ });
231
+ if (spans.length > 0) {
232
+ return { spans, source: "file", remoteTraceId, localTracePath };
233
+ }
234
+ }
235
+ return { spans: [], source: null, remoteTraceId, localTracePath };
236
+ }
237
+ async function fetchFromGrafanaTempo(traceId, cfg) {
238
+ let grafanaUrl = cfg.grafanaUrl;
239
+ if (!grafanaUrl) {
240
+ const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
241
+ if (otlpEndpoint) {
242
+ const url = new URL(otlpEndpoint);
243
+ const host = url.hostname;
244
+ for (const port of ["3000", "8001", "80"]) {
245
+ try {
246
+ const testUrl = `http://${host}:${port}/api/health`;
247
+ const resp = await httpGet(testUrl, cfg.authToken, 2e3);
248
+ if (resp && resp.includes('"database"')) {
249
+ grafanaUrl = `http://${host}:${port}`;
250
+ break;
251
+ }
252
+ } catch {
253
+ }
254
+ }
255
+ }
256
+ }
257
+ if (!grafanaUrl) return null;
258
+ try {
259
+ let dsId = cfg.grafanaDatasourceId;
260
+ if (!dsId) {
261
+ const dsResp = await httpGet(`${grafanaUrl}/api/datasources`, cfg.authToken);
262
+ if (dsResp) {
263
+ const datasources = JSON.parse(dsResp);
264
+ const tempo = datasources.find((d) => d.type === "tempo");
265
+ if (tempo) dsId = tempo.id;
266
+ }
267
+ }
268
+ if (!dsId) return null;
269
+ const traceUrl = `${grafanaUrl}/api/datasources/proxy/${dsId}/api/traces/${traceId}`;
270
+ const resp = await httpGet(traceUrl, cfg.authToken);
271
+ if (!resp) return null;
272
+ const data = JSON.parse(resp);
273
+ return parseOTLPResponse(data);
274
+ } catch (err) {
275
+ logger.debug(`[TraceSerializer] Grafana Tempo fetch failed: ${err}`);
276
+ return null;
277
+ }
278
+ }
279
+ async function fetchFromJaeger(traceId, cfg) {
280
+ let jaegerUrl = cfg.jaegerUrl;
281
+ if (!jaegerUrl) {
282
+ const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
283
+ const host = otlpEndpoint ? new URL(otlpEndpoint).hostname : "localhost";
284
+ for (const port of ["16686"]) {
285
+ try {
286
+ const testUrl = `http://${host}:${port}/api/services`;
287
+ const resp = await httpGet(testUrl, void 0, 2e3);
288
+ if (resp && resp.includes('"data"')) {
289
+ jaegerUrl = `http://${host}:${port}`;
290
+ break;
291
+ }
292
+ } catch {
293
+ }
294
+ }
295
+ }
296
+ if (!jaegerUrl) return null;
297
+ try {
298
+ const traceUrl = `${jaegerUrl}/api/traces/${traceId}`;
299
+ const resp = await httpGet(traceUrl, cfg.authToken);
300
+ if (!resp) return null;
301
+ const data = JSON.parse(resp);
302
+ return parseOTLPResponse(data);
303
+ } catch (err) {
304
+ logger.debug(`[TraceSerializer] Jaeger fetch failed: ${err}`);
305
+ return null;
306
+ }
307
+ }
308
+ async function fetchFromLocalFiles(traceRef, cfg) {
309
+ const traceFile = isTraceFilePath(traceRef) ? traceRef : await findTraceFile(traceRef, cfg.traceDir);
310
+ if (!traceFile) return null;
311
+ try {
312
+ const targetTraceId = cfg.targetTraceId || (!isTraceFilePath(traceRef) ? traceRef : await readTraceIdFromFile(traceFile) || void 0);
313
+ const rawSpans = [];
314
+ const rl = readline.createInterface({
315
+ input: fs.createReadStream(traceFile, { encoding: "utf8" }),
316
+ crlfDelay: Infinity
317
+ });
318
+ try {
319
+ for await (const line of rl) {
320
+ const trimmed = line.trim();
321
+ if (!trimmed) continue;
322
+ try {
323
+ const parsed = JSON.parse(trimmed);
324
+ if (!targetTraceId || parsed.traceId === targetTraceId) {
325
+ rawSpans.push(parsed);
326
+ }
327
+ } catch {
328
+ }
329
+ }
330
+ } finally {
331
+ rl.close();
332
+ }
333
+ if (rawSpans.length === 0) return [];
334
+ return parseLocalNDJSONSpans(rawSpans);
335
+ } catch (err) {
336
+ logger.debug(`[TraceSerializer] Local file parse failed: ${err}`);
337
+ return null;
338
+ }
339
+ }
340
+ async function httpGet(url, authToken, timeoutMs) {
341
+ try {
342
+ const controller = new AbortController();
343
+ const timeout = setTimeout(() => controller.abort(), timeoutMs || 1e4);
344
+ const headers = {};
345
+ if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
346
+ const resp = await fetch(url, {
347
+ signal: controller.signal,
348
+ headers
349
+ });
350
+ clearTimeout(timeout);
351
+ if (!resp.ok) return null;
352
+ return await resp.text();
353
+ } catch {
354
+ return null;
355
+ }
356
+ }
357
+ async function findTraceFile(traceId, traceDir) {
358
+ const dir = traceDir || process.env.VISOR_TRACE_DIR || "output/traces";
359
+ if (!fs.existsSync(dir)) return null;
360
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith(".ndjson"));
361
+ for (const file of files) {
362
+ const filePath = path.join(dir, file);
363
+ try {
364
+ const rl = readline.createInterface({
365
+ input: fs.createReadStream(filePath, { encoding: "utf8" }),
366
+ crlfDelay: Infinity
367
+ });
368
+ try {
369
+ for await (const line of rl) {
370
+ if (!line.trim()) continue;
371
+ const parsed = JSON.parse(line);
372
+ if (parsed.traceId === traceId) {
373
+ rl.close();
374
+ return filePath;
375
+ }
376
+ }
377
+ } finally {
378
+ rl.close();
379
+ }
380
+ } catch {
381
+ }
382
+ }
383
+ return null;
384
+ }
385
+ async function readTraceIdFromFile(traceFile) {
386
+ try {
387
+ const rl = readline.createInterface({
388
+ input: fs.createReadStream(traceFile, { encoding: "utf8" }),
389
+ crlfDelay: Infinity
390
+ });
391
+ for await (const line of rl) {
392
+ const trimmed = line.trim();
393
+ if (!trimmed) continue;
394
+ try {
395
+ const parsed = JSON.parse(trimmed);
396
+ if (typeof parsed.traceId === "string" && parsed.traceId) {
397
+ rl.close();
398
+ return parsed.traceId;
399
+ }
400
+ } catch {
401
+ }
402
+ }
403
+ } catch {
404
+ return null;
405
+ }
406
+ return null;
407
+ }
408
+ function isNoiseSpan(span) {
409
+ return NOISE_SPAN_NAMES.has(span.name);
410
+ }
411
+ function isWrapperSpan(span) {
412
+ return WRAPPER_SPAN_NAMES.has(span.name);
413
+ }
414
+ function buildSpanTree(spans) {
415
+ const filtered = spans.filter((s) => !isNoiseSpan(s));
416
+ const nodeMap = /* @__PURE__ */ new Map();
417
+ for (const span of filtered) {
418
+ nodeMap.set(span.spanId, { span, children: [] });
419
+ }
420
+ let root;
421
+ const orphans = [];
422
+ for (const span of filtered) {
423
+ const node = nodeMap.get(span.spanId);
424
+ if (!span.parentSpanId) {
425
+ root = node;
426
+ } else {
427
+ let parentId = span.parentSpanId;
428
+ while (parentId && !nodeMap.has(parentId)) {
429
+ const parentSpan = spans.find((s) => s.spanId === parentId);
430
+ parentId = parentSpan?.parentSpanId;
431
+ }
432
+ if (parentId) {
433
+ const parent = nodeMap.get(parentId);
434
+ if (parent) parent.children.push(node);
435
+ } else if (!root) {
436
+ root = node;
437
+ } else {
438
+ orphans.push(node);
439
+ }
440
+ }
441
+ }
442
+ if (!root) {
443
+ const sorted = [...nodeMap.values()].sort((a, b) => b.span.durationMs - a.span.durationMs);
444
+ root = sorted[0] || { span: filtered[0], children: [] };
445
+ }
446
+ if (orphans.length > 0) {
447
+ root.children.push(...orphans);
448
+ }
449
+ const sortChildren = (node) => {
450
+ node.children.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
451
+ node.children.forEach(sortChildren);
452
+ };
453
+ sortChildren(root);
454
+ const unwrap = (node) => {
455
+ node.children = node.children.map(unwrap);
456
+ const newChildren = [];
457
+ for (const child of node.children) {
458
+ if (isWrapperSpan(child.span)) {
459
+ newChildren.push(...child.children);
460
+ } else {
461
+ newChildren.push(child);
462
+ }
463
+ }
464
+ node.children = newChildren;
465
+ return node;
466
+ };
467
+ unwrap(root);
468
+ const removeDelegateEchos = (node) => {
469
+ const hasDelegateChild = node.children.some((c) => c.span.name === "search.delegate");
470
+ if (hasDelegateChild) {
471
+ node.children = node.children.filter((c) => {
472
+ if (c.span.name !== "probe.event.tool.result") return true;
473
+ const toolName = c.span.attributes["tool.name"];
474
+ return toolName !== "search";
475
+ });
476
+ }
477
+ node.children.forEach(removeDelegateEchos);
478
+ };
479
+ removeDelegateEchos(root);
480
+ return root;
481
+ }
482
+ function dedupeKey(text) {
483
+ return text.replace(/\s+/g, " ").trim().slice(0, 100).toLowerCase();
484
+ }
485
+ function dedupeOrRegister(ctx, kind, text, spanName) {
486
+ if (!text || text.length < 20) return null;
487
+ const key = dedupeKey(text);
488
+ if (!key) return null;
489
+ const map = ctx[kind];
490
+ const existing = map.get(key);
491
+ if (existing && existing !== spanName) {
492
+ return existing;
493
+ }
494
+ const otherMap = kind === "outputs" ? ctx.intents : ctx.outputs;
495
+ const crossRef = otherMap.get(key);
496
+ if (crossRef && crossRef !== spanName) {
497
+ map.set(key, spanName);
498
+ return crossRef;
499
+ }
500
+ map.set(key, spanName);
501
+ return null;
502
+ }
503
+ function normalizeSpanName(name) {
504
+ return name.replace(/^child:\s*/, "");
505
+ }
506
+ function isChildSpan(span) {
507
+ return span.name.startsWith("child: ") || span.attributes["visor.sandbox.child_span"] === true;
508
+ }
509
+ function getConcreteCheckName(span) {
510
+ const attrs = span.attributes || {};
511
+ const normalizedName = normalizeSpanName(span.name || "");
512
+ const checkId = attrs["visor.check.id"];
513
+ if (checkId) return `visor.check.${String(checkId).replace(/^visor\.check\./, "")}`;
514
+ const lifecycle = getLifecycleSpanInfo(normalizedName);
515
+ if (lifecycle?.kind === "check") return lifecycle.baseName;
516
+ const match = normalizedName.match(/^visor\.check\.([^.]+)$/);
517
+ if (match) return `visor.check.${match[1]}`;
518
+ return null;
519
+ }
520
+ function getLifecycleSpanInfo(name) {
521
+ const normalizedName = normalizeSpanName(name);
522
+ const checkMatch = normalizedName.match(
523
+ /^visor\.check\.([^.]+)\.(started|completed|failed|progress)$/
524
+ );
525
+ if (checkMatch) {
526
+ return {
527
+ baseName: `visor.check.${checkMatch[1]}`,
528
+ phase: checkMatch[2],
529
+ kind: "check"
530
+ };
531
+ }
532
+ const sandboxChildMatch = normalizedName.match(
533
+ /^visor\.sandbox\.child\.(started|waiting|completed|failed)$/
534
+ );
535
+ if (sandboxChildMatch) {
536
+ return {
537
+ baseName: "visor.sandbox.child",
538
+ phase: sandboxChildMatch[1],
539
+ kind: "sandbox-child"
540
+ };
541
+ }
542
+ const engineerMatch = normalizedName.match(
543
+ /^visor\.engineer\.(started|sandbox_resolved|child_spawned|waiting_on_child|completed|failed|progress)$/
544
+ );
545
+ if (engineerMatch) {
546
+ return {
547
+ baseName: "visor.engineer",
548
+ phase: engineerMatch[1],
549
+ kind: "engineer"
550
+ };
551
+ }
552
+ const genericMatch = normalizedName.match(
553
+ /^(ai\.request|search\.delegate|visor\.ai_check|probe\.ai_request|probe\.search_delegate)\.(started|completed|failed)$/
554
+ );
555
+ if (genericMatch) {
556
+ const baseName = genericMatch[1] === "probe.ai_request" ? "ai.request" : genericMatch[1] === "probe.search_delegate" ? "search.delegate" : genericMatch[1];
557
+ return {
558
+ baseName,
559
+ phase: genericMatch[2],
560
+ kind: "generic"
561
+ };
562
+ }
563
+ return null;
564
+ }
565
+ function parseTaskStatusSnapshot(raw) {
566
+ if (!raw || !raw.includes("<task_status>")) return [];
567
+ const items = [];
568
+ const taskRegex = /<task\s+id="([^"]+)"\s+status="([^"]+)"(?:\s+priority="[^"]*")?>([\s\S]*?)<\/task>/g;
569
+ let match;
570
+ while ((match = taskRegex.exec(raw)) !== null) {
571
+ items.push({
572
+ id: match[1],
573
+ status: match[2],
574
+ title: match[3].replace(/\s+/g, " ").trim()
575
+ });
576
+ }
577
+ return items;
578
+ }
579
+ function parseTaskIds(value) {
580
+ if (!value) return [];
581
+ return String(value).split(",").map((part) => part.trim()).filter(Boolean);
582
+ }
583
+ function parseBatchItemsJson(value) {
584
+ if (!value) return [];
585
+ try {
586
+ const parsed = JSON.parse(String(value));
587
+ if (!Array.isArray(parsed)) return [];
588
+ return parsed.filter((item) => item && typeof item.id === "string");
589
+ } catch {
590
+ return [];
591
+ }
592
+ }
593
+ function isTaskTelemetrySpan(span) {
594
+ const toolName = span.attributes["tool.name"] || span.attributes["visor.tool.name"];
595
+ if (toolName === "task") return true;
596
+ return /(?:^probe\.event\.)?task\.[a-z_]+$/.test(normalizeSpanName(span.name));
597
+ }
598
+ function summarizeTaskTelemetrySpans(spans) {
599
+ const ordered = [...spans].sort((a, b) => {
600
+ if (a.startTimeMs !== b.startTimeMs) return a.startTimeMs - b.startTimeMs;
601
+ if (a.endTimeMs !== b.endTimeMs) return a.endTimeMs - b.endTimeMs;
602
+ return a.spanId.localeCompare(b.spanId);
603
+ });
604
+ const tasks = /* @__PURE__ */ new Map();
605
+ let tasksEnabled = false;
606
+ let snapshotCount = 0;
607
+ let eventCount = 0;
608
+ let sawSnapshot = false;
609
+ const upsertTask = (id, patch) => {
610
+ const existing = tasks.get(id) || { id, title: "", status: "pending" };
611
+ const next = {
612
+ id,
613
+ title: patch.title ?? existing.title,
614
+ status: patch.status ?? existing.status
615
+ };
616
+ tasks.set(id, next);
617
+ };
618
+ for (const span of ordered) {
619
+ const name = normalizeSpanName(span.name);
620
+ const attrs = span.attributes;
621
+ const toolName = attrs["tool.name"] || attrs["visor.tool.name"];
622
+ if (toolName === "task") {
623
+ const snapshot = parseTaskStatusSnapshot(String(attrs["tool.result"] || ""));
624
+ if (snapshot.length > 0) {
625
+ tasksEnabled = true;
626
+ snapshotCount += 1;
627
+ sawSnapshot = true;
628
+ tasks.clear();
629
+ for (const task of snapshot) {
630
+ tasks.set(task.id, {
631
+ id: task.id,
632
+ title: task.title,
633
+ status: task.status
634
+ });
635
+ }
636
+ }
637
+ }
638
+ const taskEventMatch = name.match(/(?:^probe\.event\.)?task\.([a-z_]+)$/);
639
+ if (!taskEventMatch) continue;
640
+ tasksEnabled = true;
641
+ eventCount += 1;
642
+ const eventType = taskEventMatch[1];
643
+ const taskId = attrs["task.id"] ? String(attrs["task.id"]) : "";
644
+ const taskTitle = attrs["task.title"] ? String(attrs["task.title"]).trim() : "";
645
+ const newStatus = attrs["task.new_status"] ? String(attrs["task.new_status"]) : "";
646
+ const taskIds = parseTaskIds(attrs["task.ids"]);
647
+ switch (eventType) {
648
+ case "created":
649
+ if (taskId) upsertTask(taskId, { title: taskTitle, status: "pending" });
650
+ break;
651
+ case "updated":
652
+ if (taskId) {
653
+ upsertTask(taskId, {
654
+ title: taskTitle || void 0,
655
+ status: newStatus && newStatus !== "unchanged" ? newStatus : void 0
656
+ });
657
+ }
658
+ break;
659
+ case "completed":
660
+ if (taskId) upsertTask(taskId, { title: taskTitle || void 0, status: "completed" });
661
+ break;
662
+ case "deleted":
663
+ if (taskId) upsertTask(taskId, { title: taskTitle || void 0, status: "deleted" });
664
+ break;
665
+ case "batch_created": {
666
+ const items = parseBatchItemsJson(attrs["task.items_json"]);
667
+ if (items.length > 0) {
668
+ for (const item of items)
669
+ upsertTask(item.id, { title: item.title || "", status: item.status || "pending" });
670
+ } else {
671
+ for (const id of taskIds) upsertTask(id, { status: "pending" });
672
+ }
673
+ break;
674
+ }
675
+ case "batch_updated": {
676
+ const items = parseBatchItemsJson(attrs["task.items_json"]);
677
+ if (items.length > 0) {
678
+ for (const item of items)
679
+ upsertTask(item.id, {
680
+ title: item.title || void 0,
681
+ status: item.status || void 0
682
+ });
683
+ } else {
684
+ for (const id of taskIds) {
685
+ upsertTask(id, {
686
+ status: newStatus && newStatus !== "unchanged" ? newStatus : void 0
687
+ });
688
+ }
689
+ }
690
+ break;
691
+ }
692
+ case "batch_completed": {
693
+ const items = parseBatchItemsJson(attrs["task.items_json"]);
694
+ if (items.length > 0) {
695
+ for (const item of items)
696
+ upsertTask(item.id, { title: item.title || void 0, status: "completed" });
697
+ } else {
698
+ for (const id of taskIds) upsertTask(id, { status: "completed" });
699
+ }
700
+ break;
701
+ }
702
+ case "batch_deleted": {
703
+ const items = parseBatchItemsJson(attrs["task.items_json"]);
704
+ if (items.length > 0) {
705
+ for (const item of items)
706
+ upsertTask(item.id, { title: item.title || void 0, status: "deleted" });
707
+ } else {
708
+ for (const id of taskIds) upsertTask(id, { status: "deleted" });
709
+ }
710
+ break;
711
+ }
712
+ default:
713
+ break;
714
+ }
715
+ }
716
+ if (!tasksEnabled) return null;
717
+ return {
718
+ tasksEnabled,
719
+ source: sawSnapshot ? "snapshot" : "events",
720
+ eventCount,
721
+ snapshotCount,
722
+ tasks: [...tasks.values()].sort((a, b) => a.id.localeCompare(b.id)),
723
+ scopes: []
724
+ };
725
+ }
726
+ function titleCaseWords(raw) {
727
+ return raw.split(/[\s_-]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
728
+ }
729
+ function getTaskScopeCheckId(span) {
730
+ const concreteName = getConcreteCheckName(span);
731
+ if (!concreteName) return null;
732
+ return concreteName.replace(/^visor\.check\./, "");
733
+ }
734
+ function isTaskScopeNode(span) {
735
+ const checkId = getTaskScopeCheckId(span);
736
+ if (checkId) return !TASK_SCOPE_EXCLUDED_CHECKS.has(checkId);
737
+ const normalized = normalizeSpanName(span.name);
738
+ return normalized === "search.delegate" || normalized.startsWith("engineer.");
739
+ }
740
+ function getTaskScopeLabel(span) {
741
+ if (!span) return ROOT_TASK_SCOPE_LABEL;
742
+ const checkId = getTaskScopeCheckId(span);
743
+ if (checkId) {
744
+ if (checkId === "explore-code") return "Code Explorer";
745
+ return titleCaseWords(checkId);
746
+ }
747
+ const normalized = normalizeSpanName(span.name);
748
+ if (normalized === "search.delegate") return "Search Delegate";
749
+ if (normalized.startsWith("engineer.")) {
750
+ return `Engineer ${titleCaseWords(normalized.slice("engineer.".length))}`;
751
+ }
752
+ return titleCaseWords(normalized);
753
+ }
754
+ function collectTaskSpansForScope(node, stopAtNestedScopes) {
755
+ const spans = [];
756
+ const visit = (current, isRoot) => {
757
+ if (!isRoot && stopAtNestedScopes && isTaskScopeNode(current.span)) return;
758
+ if (isTaskTelemetrySpan(current.span)) spans.push(current.span);
759
+ for (const child of current.children) visit(child, false);
760
+ };
761
+ visit(node, true);
762
+ return spans;
763
+ }
764
+ function findImmediateNestedTaskScopeNodes(node) {
765
+ const scopes = [];
766
+ const visit = (current, isRoot) => {
767
+ if (!isRoot && isTaskScopeNode(current.span)) {
768
+ scopes.push(current);
769
+ return;
770
+ }
771
+ for (const child of current.children) visit(child, false);
772
+ };
773
+ visit(node, true);
774
+ return scopes;
775
+ }
776
+ function isSubAgentScope(span) {
777
+ const checkId = getTaskScopeCheckId(span);
778
+ if (checkId && SUB_AGENT_SCOPE_CHECKS.has(checkId)) return true;
779
+ const normalized = normalizeSpanName(span.name);
780
+ return normalized.startsWith("engineer.") || normalized === "search.delegate";
781
+ }
782
+ function getSubAgentStatus(node) {
783
+ const normalized = normalizeSpanName(node.span.name);
784
+ if (normalized.endsWith(".completed")) return "completed";
785
+ if (normalized.endsWith(".started") || normalized.endsWith(".progress")) return "in_progress";
786
+ let hasCompleted = false;
787
+ let hasProgress = false;
788
+ const visit = (current) => {
789
+ const n = normalizeSpanName(current.span.name);
790
+ if (n.endsWith(".completed")) hasCompleted = true;
791
+ if (n.endsWith(".progress")) hasProgress = true;
792
+ for (const child of current.children) visit(child);
793
+ };
794
+ visit(node);
795
+ if (hasCompleted) return "completed";
796
+ if (hasProgress) return "in_progress";
797
+ return "pending";
798
+ }
799
+ function buildProbeTaskScopeSummary(node, label, isRootScope = false, allSpans) {
800
+ const nestedScopeNodes = findImmediateNestedTaskScopeNodes(node);
801
+ if (!isRootScope && isSubAgentScope(node.span)) {
802
+ const localTasks = summarizeTaskTelemetrySpans(collectTaskSpansForScope(node, true));
803
+ const hasMeaningfulTitles = localTasks && localTasks.tasks.some(
804
+ (t) => t.title && t.title.length > 3 && !/^(task|engineer|code.?explorer)\s*/i.test(t.title)
805
+ );
806
+ if (hasMeaningfulTitles) {
807
+ return {
808
+ label: isRootScope ? ROOT_TASK_SCOPE_LABEL : label,
809
+ source: localTasks.source,
810
+ eventCount: localTasks.eventCount,
811
+ snapshotCount: localTasks.snapshotCount,
812
+ tasks: localTasks.tasks,
813
+ children: []
814
+ };
815
+ }
816
+ const status = getSubAgentStatus(node);
817
+ const scopeLabel = getTaskScopeLabel(node.span);
818
+ return {
819
+ label: isRootScope ? ROOT_TASK_SCOPE_LABEL : label,
820
+ source: "events",
821
+ eventCount: 0,
822
+ snapshotCount: 0,
823
+ tasks: [{ id: `__scope_${node.span.spanId}`, title: scopeLabel, status, synthetic: true }],
824
+ children: []
825
+ };
826
+ }
827
+ const local = summarizeTaskTelemetrySpans(collectTaskSpansForScope(node, true));
828
+ const children = [];
829
+ const subAgentGroups = /* @__PURE__ */ new Map();
830
+ const nonSubAgentNodes = [];
831
+ for (const child of nestedScopeNodes) {
832
+ if (isSubAgentScope(child.span)) {
833
+ const checkId = getTaskScopeCheckId(child.span) || normalizeSpanName(child.span.name);
834
+ const group = subAgentGroups.get(checkId) || [];
835
+ group.push(child);
836
+ subAgentGroups.set(checkId, group);
837
+ } else {
838
+ nonSubAgentNodes.push(child);
839
+ }
840
+ }
841
+ for (const child of nonSubAgentNodes) {
842
+ const childScope = buildProbeTaskScopeSummary(
843
+ child,
844
+ getTaskScopeLabel(child.span),
845
+ false,
846
+ allSpans
847
+ );
848
+ if (childScope) children.push(childScope);
849
+ }
850
+ for (const [checkId, nodes] of subAgentGroups) {
851
+ const scopeLabel = checkId === "engineer-task" ? "Engineer" : checkId === "explore-code" ? "Code Explorer" : titleCaseWords(checkId);
852
+ const tasks = [];
853
+ for (const n of nodes) {
854
+ const iterScope = buildProbeTaskScopeSummary(n, scopeLabel, false, allSpans);
855
+ if (iterScope && iterScope.tasks.length > 0) {
856
+ tasks.push(...iterScope.tasks);
857
+ } else {
858
+ const status = getSubAgentStatus(n);
859
+ tasks.push({
860
+ id: `__scope_${n.span.spanId}`,
861
+ title: scopeLabel,
862
+ status,
863
+ synthetic: true
864
+ });
865
+ }
866
+ }
867
+ const dedupedTasks = [];
868
+ for (const task of tasks) {
869
+ const prev = dedupedTasks[dedupedTasks.length - 1];
870
+ if (prev && prev.title === task.title && prev.status === task.status) continue;
871
+ dedupedTasks.push(task);
872
+ }
873
+ children.push({
874
+ label: scopeLabel,
875
+ source: "events",
876
+ eventCount: nodes.length,
877
+ snapshotCount: 0,
878
+ tasks: dedupedTasks,
879
+ children: []
880
+ });
881
+ }
882
+ if (!local && children.length === 0) return null;
883
+ return {
884
+ label: isRootScope ? ROOT_TASK_SCOPE_LABEL : label,
885
+ source: local?.source ?? "events",
886
+ eventCount: local?.eventCount ?? 0,
887
+ snapshotCount: local?.snapshotCount ?? 0,
888
+ tasks: local?.tasks ?? [],
889
+ children
890
+ };
891
+ }
892
+ function buildTaskSummaryRoot(spans) {
893
+ const nodeMap = /* @__PURE__ */ new Map();
894
+ for (const span of spans) {
895
+ nodeMap.set(span.spanId, { span, children: [] });
896
+ }
897
+ const roots = [];
898
+ for (const span of spans) {
899
+ const node = nodeMap.get(span.spanId);
900
+ if (span.parentSpanId && nodeMap.has(span.parentSpanId)) {
901
+ nodeMap.get(span.parentSpanId).children.push(node);
902
+ } else {
903
+ roots.push(node);
904
+ }
905
+ }
906
+ const sortChildren = (node) => {
907
+ node.children.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
908
+ node.children.forEach(sortChildren);
909
+ };
910
+ roots.forEach(sortChildren);
911
+ return {
912
+ span: {
913
+ traceId: spans[0]?.traceId || "",
914
+ spanId: "__task-root__",
915
+ name: "__task_root__",
916
+ startTimeMs: roots[0]?.span.startTimeMs || 0,
917
+ endTimeMs: roots[roots.length - 1]?.span.endTimeMs || 0,
918
+ durationMs: 0,
919
+ attributes: {},
920
+ events: [],
921
+ status: "ok"
922
+ },
923
+ children: roots
924
+ };
925
+ }
926
+ function flattenTaskScopes(scopes) {
927
+ const seen = /* @__PURE__ */ new Set();
928
+ const items = [];
929
+ const visit = (scope) => {
930
+ for (const task of scope.tasks) {
931
+ if (task.synthetic) continue;
932
+ if (seen.has(task.id)) continue;
933
+ seen.add(task.id);
934
+ items.push(task);
935
+ }
936
+ for (const child of scope.children) visit(child);
937
+ };
938
+ for (const scope of scopes) visit(scope);
939
+ return items;
940
+ }
941
+ function buildTemporalTaskScopes(spans) {
942
+ const ordered = [...spans].sort((a, b) => {
943
+ if (a.startTimeMs !== b.startTimeMs) return a.startTimeMs - b.startTimeMs;
944
+ if (a.endTimeMs !== b.endTimeMs) return a.endTimeMs - b.endTimeMs;
945
+ return a.spanId.localeCompare(b.spanId);
946
+ });
947
+ const grouped = /* @__PURE__ */ new Map();
948
+ const scopeOrder = [];
949
+ const ensureGroup = (label) => {
950
+ if (!grouped.has(label)) {
951
+ grouped.set(label, []);
952
+ scopeOrder.push(label);
953
+ }
954
+ return grouped.get(label);
955
+ };
956
+ for (let i = 0; i < ordered.length; i += 1) {
957
+ const span = ordered[i];
958
+ if (!isTaskTelemetrySpan(span)) continue;
959
+ let label = ROOT_TASK_SCOPE_LABEL;
960
+ for (let j = i - 1; j >= 0; j -= 1) {
961
+ if (isTaskScopeNode(ordered[j])) {
962
+ label = getTaskScopeLabel(ordered[j]);
963
+ break;
964
+ }
965
+ }
966
+ ensureGroup(label).push(span);
967
+ }
968
+ if (grouped.size === 0) return [];
969
+ const rootSummary = summarizeTaskTelemetrySpans(grouped.get(ROOT_TASK_SCOPE_LABEL) || []);
970
+ const children = [];
971
+ for (const label of scopeOrder) {
972
+ if (label === ROOT_TASK_SCOPE_LABEL) continue;
973
+ const summary = summarizeTaskTelemetrySpans(grouped.get(label) || []);
974
+ if (!summary) continue;
975
+ children.push({
976
+ label,
977
+ source: summary.source,
978
+ eventCount: summary.eventCount,
979
+ snapshotCount: summary.snapshotCount,
980
+ tasks: summary.tasks,
981
+ children: []
982
+ });
983
+ }
984
+ return [
985
+ {
986
+ label: ROOT_TASK_SCOPE_LABEL,
987
+ source: rootSummary?.source ?? "events",
988
+ eventCount: rootSummary?.eventCount ?? 0,
989
+ snapshotCount: rootSummary?.snapshotCount ?? 0,
990
+ tasks: rootSummary?.tasks ?? [],
991
+ children
992
+ }
993
+ ];
994
+ }
995
+ function formatTaskStatusMarker(status) {
996
+ switch (status) {
997
+ case "completed":
998
+ return "[x]";
999
+ case "in_progress":
1000
+ return "[~]";
1001
+ case "blocked":
1002
+ case "failed":
1003
+ case "error":
1004
+ return "[!]";
1005
+ case "cancelled":
1006
+ case "canceled":
1007
+ case "deleted":
1008
+ return "[-]";
1009
+ default:
1010
+ return "[ ]";
1011
+ }
1012
+ }
1013
+ function buildRenderContext(allSpans) {
1014
+ const realSpanNames = /* @__PURE__ */ new Set();
1015
+ let hasChildWorkSpans = false;
1016
+ for (const span of allSpans) {
1017
+ const normalizedName = normalizeSpanName(span.name);
1018
+ const lifecycle = getLifecycleSpanInfo(normalizedName);
1019
+ if (!lifecycle) {
1020
+ const checkName = getConcreteCheckName(span);
1021
+ if (checkName) realSpanNames.add(checkName);
1022
+ else realSpanNames.add(normalizedName);
1023
+ }
1024
+ if (isChildSpan(span) && !lifecycle) {
1025
+ hasChildWorkSpans = true;
1026
+ }
1027
+ }
1028
+ return { realSpanNames, hasChildWorkSpans };
1029
+ }
1030
+ function shouldSkipLifecycleSpan(span, renderContext) {
1031
+ const lifecycle = getLifecycleSpanInfo(span.name);
1032
+ if (!lifecycle) return false;
1033
+ if (lifecycle.kind === "sandbox-child") {
1034
+ return renderContext.hasChildWorkSpans;
1035
+ }
1036
+ return renderContext.realSpanNames.has(lifecycle.baseName);
1037
+ }
1038
+ async function serializeTraceForPrompt(traceIdOrPath, maxChars, backendConfig, taskResponse, fallbackTraceId) {
1039
+ const { spans } = await resolveTraceSpans(traceIdOrPath, backendConfig, fallbackTraceId);
1040
+ if (spans.length === 0) {
1041
+ return "(no trace data available)";
1042
+ }
1043
+ const tree = buildSpanTree(spans);
1044
+ const routeIntentTopic = extractRouteIntentTopic(spans);
1045
+ const fullOutput = (maxChars ?? 4e3) > 1e5;
1046
+ return renderSpanYaml(tree, spans, {
1047
+ maxChars: maxChars ?? 4e3,
1048
+ fallbackIntent: routeIntentTopic,
1049
+ fullOutput,
1050
+ taskResponse
1051
+ });
1052
+ }
1053
+ function renderTraceTreeFromSpans(spans, maxChars, taskResponse) {
1054
+ const tree = buildSpanTree(spans);
1055
+ const routeIntentTopic = extractRouteIntentTopic(spans);
1056
+ const fullOutput = (maxChars ?? 4e3) > 1e5;
1057
+ return renderSpanYaml(tree, spans, {
1058
+ maxChars: maxChars ?? 4e3,
1059
+ fallbackIntent: routeIntentTopic,
1060
+ fullOutput,
1061
+ taskResponse
1062
+ });
1063
+ }
1064
+ function formatTaskStatus(status) {
1065
+ return status === "completed" ? "[x]" : status === "in_progress" ? "[~]" : status === "blocked" || status === "failed" || status === "error" ? "[!]" : status === "deleted" || status === "cancelled" || status === "canceled" ? "[-]" : "[ ]";
1066
+ }
1067
+ function formatTaskLabel(task) {
1068
+ const title = String(task.title || "").trim();
1069
+ return title || task.id || task.status;
1070
+ }
1071
+ function scopeHasRenderableTasks(scope) {
1072
+ if (scope.tasks.some((task) => !task.synthetic)) return true;
1073
+ return scope.children.some((child) => scopeHasRenderableTasks(child));
1074
+ }
1075
+ function appendTaskScopeLines(lines, scope, depth = 0) {
1076
+ if (!scopeHasRenderableTasks(scope)) return;
1077
+ const prefix = " ".repeat(depth);
1078
+ if (depth > 0 || scope.label !== ROOT_TASK_SCOPE_LABEL) {
1079
+ lines.push(`${prefix}${scope.label}`);
1080
+ }
1081
+ for (const task of scope.tasks.filter((task2) => !task2.synthetic)) {
1082
+ lines.push(`${prefix} ${formatTaskStatus(task.status)} ${formatTaskLabel(task)}`);
1083
+ }
1084
+ for (const child of scope.children) {
1085
+ appendTaskScopeLines(lines, child, depth + 1);
1086
+ }
1087
+ }
1088
+ function buildTraceHeaderLines(traceData, taskSummary) {
1089
+ const lines = [`Trace source: ${traceData.source || "unknown"}`];
1090
+ if (!taskSummary || taskSummary.tasks.length === 0) {
1091
+ lines.push("Tasks: no task telemetry found in this trace");
1092
+ return lines;
1093
+ }
1094
+ const eventsSuffix = taskSummary.snapshotCount > 0 ? `, ${taskSummary.eventCount} task events, ${taskSummary.snapshotCount} snapshots` : `, ${taskSummary.eventCount} task events`;
1095
+ lines.push(`Tasks: ${taskSummary.tasks.length} tracked (${taskSummary.source}${eventsSuffix})`);
1096
+ if (taskSummary.scopes.length > 0) {
1097
+ for (const scope of taskSummary.scopes) {
1098
+ appendTaskScopeLines(lines, scope);
1099
+ }
1100
+ } else {
1101
+ for (const task of taskSummary.tasks) {
1102
+ lines.push(` ${formatTaskStatus(task.status)} ${formatTaskLabel(task)}`);
1103
+ }
1104
+ }
1105
+ return lines;
1106
+ }
1107
+ async function buildTraceReport(traceIdOrPath, maxChars, backendConfig, taskResponse, fallbackTraceId) {
1108
+ const traceData = await resolveTraceSpans(traceIdOrPath, backendConfig, fallbackTraceId);
1109
+ if (traceData.spans.length === 0) {
1110
+ return null;
1111
+ }
1112
+ const taskSummary = extractProbeTaskSummary(traceData.spans);
1113
+ const headerLines = buildTraceHeaderLines(traceData, taskSummary);
1114
+ const headerText = headerLines.join("\n");
1115
+ const tree = renderTraceTreeFromSpans(traceData.spans, maxChars, taskResponse);
1116
+ return {
1117
+ traceData,
1118
+ tree,
1119
+ taskSummary,
1120
+ headerText,
1121
+ text: `${headerText}
1122
+
1123
+ ${tree}`
1124
+ };
1125
+ }
1126
+ function extractProbeTaskSummary(spans) {
1127
+ const overall = summarizeTaskTelemetrySpans(spans);
1128
+ if (!overall) return null;
1129
+ const rootScope = buildProbeTaskScopeSummary(
1130
+ buildTaskSummaryRoot(spans),
1131
+ ROOT_TASK_SCOPE_LABEL,
1132
+ true,
1133
+ spans
1134
+ );
1135
+ let scopes = rootScope ? [rootScope] : [];
1136
+ if (scopes.length === 0 || scopes[0].children.length === 0) {
1137
+ const temporalScopes = buildTemporalTaskScopes(spans);
1138
+ if (temporalScopes.length > 0 && temporalScopes[0].children.length > 0) {
1139
+ scopes = temporalScopes;
1140
+ }
1141
+ }
1142
+ return {
1143
+ ...overall,
1144
+ tasks: scopes.length > 0 ? flattenTaskScopes(scopes) : overall.tasks,
1145
+ scopes
1146
+ };
1147
+ }
1148
+ function renderSpanTree(tree, opts) {
1149
+ const maxChars = opts?.maxChars ?? 4e3;
1150
+ const maxDepth = opts?.maxDepth ?? 20;
1151
+ const lines = [];
1152
+ const dedup = { outputs: /* @__PURE__ */ new Map(), intents: /* @__PURE__ */ new Map() };
1153
+ renderNode(
1154
+ tree,
1155
+ "",
1156
+ true,
1157
+ 0,
1158
+ maxDepth,
1159
+ lines,
1160
+ void 0,
1161
+ opts?.fallbackIntent,
1162
+ opts?.fullOutput,
1163
+ dedup
1164
+ );
1165
+ let result = lines.join("\n");
1166
+ if (result.length > maxChars) {
1167
+ result = result.slice(0, maxChars - 20) + "\n... (truncated)";
1168
+ }
1169
+ return result;
1170
+ }
1171
+ function renderTraceTree(tree, opts) {
1172
+ const convert = (node) => ({
1173
+ span: {
1174
+ traceId: node.span?.traceId || "",
1175
+ spanId: node.span?.spanId || "",
1176
+ parentSpanId: node.span?.parentSpanId,
1177
+ name: node.span?.name || node.checkId || "unknown",
1178
+ startTimeMs: timeValueToMs(node.span?.startTime || [0, 0]),
1179
+ endTimeMs: timeValueToMs(node.span?.endTime || [0, 0]),
1180
+ durationMs: node.span?.duration || 0,
1181
+ attributes: node.span?.attributes || {},
1182
+ events: (node.span?.events || []).map((e) => ({
1183
+ name: e.name,
1184
+ attributes: e.attributes || {}
1185
+ })),
1186
+ status: node.status === "error" ? "error" : "ok"
1187
+ },
1188
+ children: (node.children || []).map(convert)
1189
+ });
1190
+ return renderSpanTree(convert(tree), opts);
1191
+ }
1192
+ function renderSpanYaml(tree, allSpans, opts) {
1193
+ const fullOutput = opts?.fullOutput ?? false;
1194
+ const maxLen = fullOutput ? 1e5 : 120;
1195
+ const dedup = { outputs: /* @__PURE__ */ new Map(), intents: /* @__PURE__ */ new Map() };
1196
+ const renderContext = buildRenderContext(allSpans);
1197
+ const lines = [];
1198
+ renderYamlNode(tree, 0, lines, dedup, renderContext, opts?.fallbackIntent, fullOutput, maxLen);
1199
+ if (opts?.taskResponse) {
1200
+ while (lines.length > 0 && /^\s*output:\s*=\s*\S+/.test(lines[lines.length - 1])) {
1201
+ lines.pop();
1202
+ }
1203
+ const ml = fullOutput ? 1e5 : 500;
1204
+ const text = opts.taskResponse.replace(/\*\*/g, "").replace(/`/g, "").trim();
1205
+ if (fullOutput) {
1206
+ lines.push(" response: |");
1207
+ for (const line of text.split("\n")) {
1208
+ lines.push(` ${line}`);
1209
+ }
1210
+ } else {
1211
+ const truncated = truncate(text.replace(/\n/g, " "), ml);
1212
+ lines.push(` response: ${truncated}`);
1213
+ if (text.length > ml) {
1214
+ lines.push(" # use --full for complete response");
1215
+ }
1216
+ }
1217
+ }
1218
+ return lines.join("\n");
1219
+ }
1220
+ function renderYamlNode(node, indent, lines, dedup, renderContext, fallbackIntent, fullOutput, maxLen, parentSpan) {
1221
+ if (shouldSkipLifecycleSpan(node.span, renderContext)) {
1222
+ for (const child of node.children) {
1223
+ renderYamlNode(
1224
+ child,
1225
+ indent,
1226
+ lines,
1227
+ dedup,
1228
+ renderContext,
1229
+ fallbackIntent,
1230
+ fullOutput,
1231
+ maxLen,
1232
+ parentSpan
1233
+ );
1234
+ }
1235
+ return;
1236
+ }
1237
+ const pad = " ".repeat(indent);
1238
+ const attrs = node.span.attributes;
1239
+ const duration = formatDurationMs(node.span.durationMs);
1240
+ const rawName = node.span.name;
1241
+ const name = normalizeSpanName(rawName);
1242
+ const lifecycle = getLifecycleSpanInfo(name);
1243
+ const childSuffix = isChildSpan(node.span) ? " [child]" : "";
1244
+ const ml = maxLen ?? 120;
1245
+ const parentCheckId = parentSpan?.attributes["visor.check.id"];
1246
+ const parentCheckName = parentCheckId ? String(parentCheckId).replace(/^visor\.check\./, "") : void 0;
1247
+ const displayName = name === "ai.request" && parentCheckName ? parentCheckName : String(attrs["visor.check.id"] || name).replace(/^visor\.check\./, "");
1248
+ if (lifecycle) {
1249
+ if (lifecycle.kind === "check") {
1250
+ const cleanName = lifecycle.baseName.replace(/^visor\.check\./, "");
1251
+ lines.push(`${pad}${cleanName}${childSuffix} [${lifecycle.phase}] \u2014 ${duration}`);
1252
+ return;
1253
+ }
1254
+ if (lifecycle.kind === "sandbox-child") {
1255
+ const checkName = attrs["visor.check.name"] ? ` ${String(attrs["visor.check.name"])}` : "";
1256
+ lines.push(
1257
+ `${pad}sandbox.child${checkName}${childSuffix} [${lifecycle.phase}] \u2014 ${duration}`
1258
+ );
1259
+ return;
1260
+ }
1261
+ if (lifecycle.kind === "engineer") {
1262
+ const sandbox = attrs["visor.sandbox.selected"] || attrs["visor.sandbox.name"];
1263
+ const sandboxSuffix = sandbox ? ` sandbox=${String(sandbox)}` : "";
1264
+ lines.push(`${pad}engineer${childSuffix} [${lifecycle.phase}]${sandboxSuffix} \u2014 ${duration}`);
1265
+ return;
1266
+ }
1267
+ lines.push(`${pad}${lifecycle.baseName}${childSuffix} [${lifecycle.phase}] \u2014 ${duration}`);
1268
+ return;
1269
+ }
1270
+ const toolName = attrs["tool.name"] || attrs["visor.tool.name"];
1271
+ if (toolName) {
1272
+ const toolInput = extractToolInput(String(toolName), attrs);
1273
+ const toolResultLen = attrs["tool.result.length"] || attrs["tool.result.count"];
1274
+ const tn = String(toolName);
1275
+ const taskSnapshot = tn === "task" ? parseTaskStatusSnapshot(String(attrs["tool.result"] || "")) : [];
1276
+ if (tn === "task" && taskSnapshot.length > 0) {
1277
+ lines.push(`${pad}tasks snapshot${childSuffix}:`);
1278
+ for (const task of taskSnapshot) {
1279
+ lines.push(
1280
+ `${pad} ${formatTaskStatusMarker(task.status)} ${task.id}: ${truncate(task.title, 100)}`
1281
+ );
1282
+ }
1283
+ return;
1284
+ }
1285
+ if (tn === "task") {
1286
+ return;
1287
+ }
1288
+ const isSearchTool = tn === "search" || tn === "searchCode" || tn === "search_code";
1289
+ const numLen = toolResultLen ? Number(toolResultLen) : -1;
1290
+ const noResults = isSearchTool && numLen >= 0 && numLen < 500;
1291
+ const resultSize = noResults ? " \u2192 no results" : toolResultLen ? ` \u2192 ${formatSize(numLen)}` : "";
1292
+ const successMark = attrs["tool.success"] === false ? " \u2717" : "";
1293
+ lines.push(`${pad}- ${tn}(${toolInput})${childSuffix}${resultSize}${successMark}`);
1294
+ return;
1295
+ }
1296
+ if (name === "search.delegate.dedup") {
1297
+ const query = attrs["dedup.query"] || "";
1298
+ const action = attrs["dedup.action"] || "?";
1299
+ const reason = attrs["dedup.reason"] || "";
1300
+ const rewritten = attrs["dedup.rewritten"] || "";
1301
+ const error = attrs["dedup.error"] || "";
1302
+ const prevCount = attrs["dedup.previous_count"] || "0";
1303
+ let detail = `${action}`;
1304
+ if (rewritten) detail += ` \u2192 "${truncate(String(rewritten), 60)}"`;
1305
+ if (reason) detail += ` (${truncate(String(reason), 80)})`;
1306
+ if (error) detail += ` [error: ${truncate(String(error), 120)}]`;
1307
+ lines.push(
1308
+ `${pad}dedup("${truncate(String(query), 60)}") [${prevCount} prior]: ${detail} \u2014 ${duration}`
1309
+ );
1310
+ return;
1311
+ }
1312
+ if (name === "search.delegate") {
1313
+ const query = attrs["search.query"] || "";
1314
+ const rewritten = attrs["search.query.rewritten"] || "";
1315
+ const output = attrs["search.delegate.output"] || "";
1316
+ const outputLen = attrs["search.delegate.output_length"] || "";
1317
+ let header = `search.delegate("${truncate(String(query), 80)}")${childSuffix}`;
1318
+ if (rewritten) header += ` \u2192 rewritten: "${truncate(String(rewritten), 60)}"`;
1319
+ header += ` \u2014 ${duration}`;
1320
+ lines.push(`${pad}${header}:`);
1321
+ if (output) {
1322
+ try {
1323
+ const parsed = JSON.parse(String(output));
1324
+ if (parsed.confidence) {
1325
+ let confLine = `confidence: ${parsed.confidence}`;
1326
+ if (parsed.reason) confLine += ` \u2014 ${truncate(String(parsed.reason), 100)}`;
1327
+ lines.push(`${pad} ${confLine}`);
1328
+ }
1329
+ if (parsed.searches && Array.isArray(parsed.searches) && parsed.searches.length > 0) {
1330
+ lines.push(`${pad} searches (${parsed.searches.length}):`);
1331
+ for (const s of parsed.searches) {
1332
+ const outcome = s.had_results ? "\u2713" : "\u2717";
1333
+ lines.push(
1334
+ `${pad} ${outcome} "${truncate(String(s.query || ""), 60)}" in ${truncate(String(s.path || "."), 40)}`
1335
+ );
1336
+ }
1337
+ }
1338
+ if (parsed.groups && Array.isArray(parsed.groups) && parsed.groups.length > 0) {
1339
+ lines.push(`${pad} groups (${parsed.groups.length}):`);
1340
+ for (const g of parsed.groups) {
1341
+ const fileCount = g.files?.length || 0;
1342
+ lines.push(`${pad} - ${truncate(String(g.reason || ""), 80)} (${fileCount} files)`);
1343
+ }
1344
+ }
1345
+ } catch {
1346
+ if (outputLen) lines.push(`${pad} output: ${outputLen} chars`);
1347
+ }
1348
+ }
1349
+ for (const child of node.children) {
1350
+ renderYamlNode(
1351
+ child,
1352
+ indent + 1,
1353
+ lines,
1354
+ dedup,
1355
+ renderContext,
1356
+ fallbackIntent,
1357
+ fullOutput,
1358
+ maxLen,
1359
+ node.span
1360
+ );
1361
+ }
1362
+ return;
1363
+ }
1364
+ if (name === "ai.request") {
1365
+ const model = attrs["ai.model"] || attrs["gen_ai.request.model"] || "?";
1366
+ const tokensIn = attrs["ai.input_length"] || attrs["gen_ai.usage.input_tokens"] || "";
1367
+ const tokensOut = attrs["gen_ai.usage.output_tokens"] || "";
1368
+ const tokenParts = [];
1369
+ if (tokensIn) tokenParts.push(`${tokensIn} in`);
1370
+ if (tokensOut) tokenParts.push(`${tokensOut} out`);
1371
+ const tokenStr = tokenParts.length > 0 ? ` \u2014 ${tokenParts.join(", ")}` : "";
1372
+ const hasChildren2 = node.children.length > 0;
1373
+ lines.push(
1374
+ `${pad}ai: ${model}${childSuffix} \u2014 ${duration}${tokenStr}${hasChildren2 ? ":" : ""}`
1375
+ );
1376
+ const aiInput = String(attrs["ai.input"] || "");
1377
+ let intent = extractAIIntent(aiInput, ml);
1378
+ if (!intent && parentSpan) {
1379
+ const promptPreview = String(
1380
+ parentSpan.attributes["visor.provider.request.prompt.preview"] || ""
1381
+ );
1382
+ if (promptPreview) intent = extractAIIntent(promptPreview, ml);
1383
+ if (!intent) {
1384
+ const inputOutputs = String(parentSpan.attributes["visor.check.input.outputs"] || "");
1385
+ if (inputOutputs) {
1386
+ try {
1387
+ const o = JSON.parse(inputOutputs);
1388
+ const t = o["route-intent"]?.topic;
1389
+ if (t) intent = truncate(String(t), ml);
1390
+ } catch {
1391
+ }
1392
+ }
1393
+ }
1394
+ }
1395
+ if (!intent && fallbackIntent && parentSpan?.name !== "search.delegate") {
1396
+ intent = fallbackIntent;
1397
+ }
1398
+ if (intent) {
1399
+ const intentRef = dedupeOrRegister(dedup, "intents", intent, displayName);
1400
+ if (intentRef) {
1401
+ lines.push(`${pad} intent: = ${intentRef}`);
1402
+ } else {
1403
+ lines.push(`${pad} intent: ${intent}`);
1404
+ }
1405
+ }
1406
+ for (const child of node.children) {
1407
+ renderYamlNode(
1408
+ child,
1409
+ indent + 1,
1410
+ lines,
1411
+ dedup,
1412
+ renderContext,
1413
+ fallbackIntent,
1414
+ fullOutput,
1415
+ maxLen,
1416
+ node.span
1417
+ );
1418
+ }
1419
+ if (parentSpan) {
1420
+ const checkOutput = String(parentSpan.attributes["visor.check.output"] || "");
1421
+ if (checkOutput) {
1422
+ renderYamlOutput(
1423
+ checkOutput,
1424
+ `${pad} `,
1425
+ "output",
1426
+ displayName,
1427
+ dedup,
1428
+ lines,
1429
+ fullOutput,
1430
+ ml
1431
+ );
1432
+ }
1433
+ }
1434
+ return;
1435
+ }
1436
+ if (name === "visor.run") {
1437
+ const source = attrs["visor.run.source"] || "";
1438
+ const visorVersion = attrs["visor.version"] || "";
1439
+ const probeVersion = attrs["probe.version"] || "";
1440
+ const slackUser = attrs["slack.user_id"] || "";
1441
+ lines.push(`${pad}visor.run:`);
1442
+ lines.push(`${pad} trace_id: ${node.span.traceId}`);
1443
+ if (visorVersion) lines.push(`${pad} visor: ${visorVersion}`);
1444
+ if (probeVersion) lines.push(`${pad} probe: ${probeVersion}`);
1445
+ if (source) lines.push(`${pad} source: ${source}`);
1446
+ if (slackUser) lines.push(`${pad} slack_user: ${slackUser}`);
1447
+ lines.push(`${pad} duration: ${duration}`);
1448
+ for (const child of node.children) {
1449
+ renderYamlNode(
1450
+ child,
1451
+ indent + 1,
1452
+ lines,
1453
+ dedup,
1454
+ renderContext,
1455
+ fallbackIntent,
1456
+ fullOutput,
1457
+ maxLen,
1458
+ node.span
1459
+ );
1460
+ }
1461
+ return;
1462
+ }
1463
+ const checkId = attrs["visor.check.id"];
1464
+ const checkType = attrs["visor.check.type"];
1465
+ const concreteCheckName = getConcreteCheckName({ name, attributes: attrs });
1466
+ if (checkId || concreteCheckName) {
1467
+ const cleanName = String(checkId || concreteCheckName || name).replace(/^visor\.check\./, "");
1468
+ const errMark2 = node.span.status === "error" ? " \u2717" : "";
1469
+ lines.push(`${pad}${cleanName}${childSuffix}:${errMark2}`);
1470
+ if (checkType) lines.push(`${pad} type: ${checkType}`);
1471
+ lines.push(`${pad} duration: ${duration}`);
1472
+ const inputContext = String(attrs["visor.check.input.context"] || "");
1473
+ const inputOutputs = String(attrs["visor.check.input.outputs"] || "");
1474
+ const question = extractQuestionFromContext(inputContext, inputOutputs);
1475
+ if (question || inputOutputs && inputOutputs !== "{}") {
1476
+ renderYamlInput(inputOutputs, `${pad} `, lines, fullOutput, ml, question);
1477
+ }
1478
+ for (const child of node.children) {
1479
+ renderYamlNode(
1480
+ child,
1481
+ indent + 1,
1482
+ lines,
1483
+ dedup,
1484
+ renderContext,
1485
+ fallbackIntent,
1486
+ fullOutput,
1487
+ maxLen,
1488
+ node.span
1489
+ );
1490
+ }
1491
+ const hasDirectAiChild = node.children.some((c) => c.span.name === "ai.request");
1492
+ if (!hasDirectAiChild) {
1493
+ const output = String(attrs["visor.check.output"] || "");
1494
+ if (output) {
1495
+ renderYamlOutput(output, `${pad} `, "output", cleanName, dedup, lines, fullOutput, ml);
1496
+ }
1497
+ }
1498
+ return;
1499
+ }
1500
+ const taskEventMatch = name.match(/(?:^probe\.event\.)?task\.([a-z_]+)$/);
1501
+ if (taskEventMatch) {
1502
+ const eventType = taskEventMatch[1];
1503
+ const taskId = attrs["task.id"] ? String(attrs["task.id"]) : "";
1504
+ const taskTitle = attrs["task.title"] ? truncate(String(attrs["task.title"]), 80) : "";
1505
+ const taskIds = attrs["task.ids"] ? truncate(String(attrs["task.ids"]), 120) : "";
1506
+ const count = attrs["task.count"] ? Number(attrs["task.count"]) : void 0;
1507
+ const totalCount = attrs["task.total_count"] ? Number(attrs["task.total_count"]) : void 0;
1508
+ const incompleteCount = attrs["task.incomplete_count"] ? Number(attrs["task.incomplete_count"]) : void 0;
1509
+ const completedCount = attrs["task.completed_count"] ? Number(attrs["task.completed_count"]) : void 0;
1510
+ const remaining = attrs["task.incomplete_remaining"] ? Number(attrs["task.incomplete_remaining"]) : void 0;
1511
+ const newStatus = attrs["task.new_status"] ? String(attrs["task.new_status"]) : "";
1512
+ const fieldsUpdated = attrs["task.fields_updated"] ? truncate(String(attrs["task.fields_updated"]), 80) : "";
1513
+ const error = attrs["task.error"] ? truncate(String(attrs["task.error"]), 100) : "";
1514
+ const action = attrs["task.action"] ? String(attrs["task.action"]) : "";
1515
+ switch (eventType) {
1516
+ case "session_started":
1517
+ lines.push(`${pad}tasks enabled \u2014 ${duration}`);
1518
+ return;
1519
+ case "created":
1520
+ lines.push(`${pad}[ ] ${taskId}${taskTitle ? `: ${taskTitle}` : ""} \u2014 ${duration}`);
1521
+ return;
1522
+ case "batch_created": {
1523
+ const totalSuffix = totalCount !== void 0 ? `, total ${totalCount}` : "";
1524
+ lines.push(
1525
+ `${pad}tasks created (${count ?? "?"}${totalSuffix}): ${taskIds || "unknown"} \u2014 ${duration}`
1526
+ );
1527
+ return;
1528
+ }
1529
+ case "updated": {
1530
+ const statusSuffix = newStatus && newStatus !== "unchanged" ? ` -> ${newStatus}` : "";
1531
+ const fieldSuffix = fieldsUpdated ? ` [${fieldsUpdated}]` : "";
1532
+ lines.push(`${pad}task updated: ${taskId}${statusSuffix}${fieldSuffix} \u2014 ${duration}`);
1533
+ return;
1534
+ }
1535
+ case "batch_updated":
1536
+ lines.push(`${pad}tasks updated (${count ?? "?"}): ${taskIds || "unknown"} \u2014 ${duration}`);
1537
+ return;
1538
+ case "completed": {
1539
+ const remainingSuffix = remaining !== void 0 ? ` (${remaining} remaining)` : "";
1540
+ lines.push(
1541
+ `${pad}[x] ${taskId}${taskTitle ? `: ${taskTitle}` : ""}${remainingSuffix} \u2014 ${duration}`
1542
+ );
1543
+ return;
1544
+ }
1545
+ case "batch_completed": {
1546
+ const remainingSuffix = remaining !== void 0 ? `, ${remaining} remaining` : "";
1547
+ lines.push(
1548
+ `${pad}tasks completed (${count ?? "?"}${remainingSuffix}): ${taskIds || "unknown"} \u2014 ${duration}`
1549
+ );
1550
+ return;
1551
+ }
1552
+ case "deleted":
1553
+ lines.push(`${pad}task deleted: ${taskId} \u2014 ${duration}`);
1554
+ return;
1555
+ case "batch_deleted":
1556
+ lines.push(`${pad}tasks deleted (${count ?? "?"}): ${taskIds || "unknown"} \u2014 ${duration}`);
1557
+ return;
1558
+ case "listed": {
1559
+ const stats = [];
1560
+ if (totalCount !== void 0) stats.push(`${totalCount} total`);
1561
+ if (incompleteCount !== void 0) stats.push(`${incompleteCount} incomplete`);
1562
+ if (completedCount !== void 0) stats.push(`${completedCount} completed`);
1563
+ lines.push(
1564
+ `${pad}tasks listed${stats.length > 0 ? ` (${stats.join(", ")})` : ""} \u2014 ${duration}`
1565
+ );
1566
+ return;
1567
+ }
1568
+ case "validation_error":
1569
+ case "error":
1570
+ lines.push(`${pad}task.${eventType}: ${error || "unknown error"} \u2014 ${duration}`);
1571
+ return;
1572
+ case "unknown_action":
1573
+ lines.push(`${pad}task.unknown_action: ${action || "unknown"} \u2014 ${duration}`);
1574
+ return;
1575
+ default:
1576
+ lines.push(`${pad}task.${eventType}${taskId ? `: ${taskId}` : ""} \u2014 ${duration}`);
1577
+ return;
1578
+ }
1579
+ }
1580
+ const errMark = node.span.status === "error" ? " \u2717" : "";
1581
+ const hasChildren = node.children.length > 0;
1582
+ lines.push(`${pad}${name}${childSuffix} \u2014 ${duration}${errMark}${hasChildren ? ":" : ""}`);
1583
+ for (const child of node.children) {
1584
+ renderYamlNode(
1585
+ child,
1586
+ indent + 1,
1587
+ lines,
1588
+ dedup,
1589
+ renderContext,
1590
+ fallbackIntent,
1591
+ fullOutput,
1592
+ maxLen,
1593
+ node.span
1594
+ );
1595
+ }
1596
+ }
1597
+ function renderYamlOutput(rawOutput, pad, label, spanName, dedup, lines, fullOutput, maxLen) {
1598
+ const ml = maxLen ?? 120;
1599
+ let obj;
1600
+ try {
1601
+ obj = JSON.parse(rawOutput);
1602
+ } catch {
1603
+ obj = parseTruncatedJson(rawOutput);
1604
+ }
1605
+ if (obj === null || obj === void 0 || typeof obj !== "object") return;
1606
+ if (typeof obj === "object" && !Array.isArray(obj)) {
1607
+ const keys = Object.keys(obj);
1608
+ if (keys.length === 1 && typeof obj[keys[0]] === "object" && obj[keys[0]] !== null && !Array.isArray(obj[keys[0]])) {
1609
+ obj = obj[keys[0]];
1610
+ }
1611
+ const objKeys = Object.keys(obj);
1612
+ if (objKeys.length === 1 && objKeys[0] === "text" && typeof obj.text === "string") {
1613
+ const text = obj.text.replace(/\*\*/g, "").replace(/`/g, "").trim();
1614
+ const flat = text.replace(/\n/g, " ");
1615
+ const preview2 = fullOutput ? flat : truncate(flat, ml);
1616
+ const ref2 = dedupeOrRegister(dedup, "outputs", truncate(flat, 100), spanName);
1617
+ if (ref2) {
1618
+ lines.push(`${pad}${label}: = ${ref2}`);
1619
+ } else {
1620
+ lines.push(`${pad}${label}: ${preview2}`);
1621
+ }
1622
+ return;
1623
+ }
1624
+ }
1625
+ const preview = formatJsonPreview(obj, 200);
1626
+ if (!preview) return;
1627
+ const ref = dedupeOrRegister(dedup, "outputs", preview, spanName);
1628
+ if (ref) {
1629
+ lines.push(`${pad}${label}: = ${ref}`);
1630
+ return;
1631
+ }
1632
+ renderYamlValue(obj, pad, label, lines, fullOutput, ml);
1633
+ }
1634
+ function renderYamlValue(val, pad, key, lines, fullOutput, maxLen, depth) {
1635
+ const ml = maxLen ?? 120;
1636
+ const d = depth ?? 0;
1637
+ if (val === null || val === void 0) return;
1638
+ if (typeof val === "boolean" || typeof val === "number") {
1639
+ lines.push(`${pad}${key}: ${val}`);
1640
+ return;
1641
+ }
1642
+ if (typeof val === "string") {
1643
+ if (val.startsWith("{") || val.startsWith("[")) return;
1644
+ const clean = val.replace(/\*\*/g, "").replace(/`/g, "").trim();
1645
+ if (fullOutput && clean.length > 100 && clean.includes("\n")) {
1646
+ lines.push(`${pad}${key}: |`);
1647
+ for (const line of clean.split("\n").slice(0, fullOutput ? 500 : 5)) {
1648
+ lines.push(`${pad} ${line}`);
1649
+ }
1650
+ } else {
1651
+ const flat = clean.replace(/\n/g, " ");
1652
+ const truncVal = fullOutput ? flat : truncate(flat, ml);
1653
+ lines.push(`${pad}${key}: ${truncVal}`);
1654
+ }
1655
+ return;
1656
+ }
1657
+ if (Array.isArray(val)) {
1658
+ if (val.length === 0) {
1659
+ lines.push(`${pad}${key}: []`);
1660
+ return;
1661
+ }
1662
+ if (val.every((v) => typeof v === "string") && val.join(", ").length < ml) {
1663
+ lines.push(`${pad}${key}: [${val.join(", ")}]`);
1664
+ return;
1665
+ }
1666
+ const maxItems = fullOutput ? 20 : 3;
1667
+ lines.push(`${pad}${key}:`);
1668
+ for (let i = 0; i < Math.min(val.length, maxItems); i++) {
1669
+ const item = val[i];
1670
+ if (typeof item === "object" && item !== null) {
1671
+ const entries = Object.entries(item).filter(([k]) => k !== "raw" && k !== "tags");
1672
+ if (entries.length > 0) {
1673
+ const [firstKey, firstVal] = entries[0];
1674
+ if (firstVal === null || firstVal === void 0 || typeof firstVal !== "object") {
1675
+ const sv = typeof firstVal === "string" ? fullOutput ? firstVal.split("\n")[0] : truncate(firstVal.split("\n")[0], ml) : String(firstVal ?? "");
1676
+ lines.push(`${pad} - ${firstKey}: ${sv}`);
1677
+ } else {
1678
+ lines.push(`${pad} - ${firstKey}:`);
1679
+ for (const [ck, cv] of Object.entries(firstVal)) {
1680
+ if (ck === "raw" || ck === "skills" || ck === "tags") continue;
1681
+ renderYamlValue(cv, `${pad} `, ck, lines, fullOutput, ml, d + 2);
1682
+ }
1683
+ }
1684
+ for (let j = 1; j < entries.length; j++) {
1685
+ const [k, v] = entries[j];
1686
+ renderYamlValue(v, `${pad} `, k, lines, fullOutput, ml, d + 1);
1687
+ }
1688
+ }
1689
+ } else {
1690
+ lines.push(`${pad} - ${truncate(String(item), ml)}`);
1691
+ }
1692
+ }
1693
+ if (val.length > maxItems) {
1694
+ lines.push(`${pad} # ... ${val.length - maxItems} more`);
1695
+ }
1696
+ return;
1697
+ }
1698
+ if (typeof val === "object") {
1699
+ if (d > 3) {
1700
+ const keys = Object.keys(val);
1701
+ lines.push(`${pad}${key}: {${keys.slice(0, 4).join(", ")}${keys.length > 4 ? ", ..." : ""}}`);
1702
+ return;
1703
+ }
1704
+ lines.push(`${pad}${key}:`);
1705
+ for (const [k, v] of Object.entries(val)) {
1706
+ if (k === "raw" || k === "tags") continue;
1707
+ renderYamlValue(v, `${pad} `, k, lines, fullOutput, ml, d + 1);
1708
+ }
1709
+ }
1710
+ }
1711
+ function extractQuestionFromContext(contextStr, inputOutputsStr) {
1712
+ if (!contextStr) return void 0;
1713
+ try {
1714
+ const ctx = JSON.parse(contextStr);
1715
+ const outputs = ctx.outputs || {};
1716
+ const routeIntent = outputs["route-intent"];
1717
+ if (routeIntent) {
1718
+ const topic = routeIntent.topic || routeIntent.intent || routeIntent.question;
1719
+ if (topic && typeof topic === "string") return topic;
1720
+ if (typeof routeIntent === "string") return routeIntent;
1721
+ }
1722
+ const args = ctx.args || {};
1723
+ if (args.topic && typeof args.topic === "string") return args.topic;
1724
+ if (args.question && typeof args.question === "string") return args.question;
1725
+ if (args.intent && typeof args.intent === "string") return args.intent;
1726
+ for (const key of Object.keys(outputs)) {
1727
+ const val = outputs[key];
1728
+ if (typeof val === "object" && val !== null) {
1729
+ if (val.topic && typeof val.topic === "string") {
1730
+ try {
1731
+ const depOutputs = JSON.parse(inputOutputsStr);
1732
+ if (depOutputs[key]) continue;
1733
+ } catch {
1734
+ }
1735
+ return val.topic;
1736
+ }
1737
+ }
1738
+ }
1739
+ } catch {
1740
+ const topicMatch = contextStr.match(/"topic"\s*:\s*"([^"]+)"/);
1741
+ if (topicMatch) return topicMatch[1];
1742
+ }
1743
+ return void 0;
1744
+ }
1745
+ function renderYamlInput(inputOutputsStr, pad, lines, fullOutput, maxLen, question) {
1746
+ const ml = maxLen ?? 120;
1747
+ if (question) {
1748
+ lines.push(`${pad}input: ${truncate(question, fullOutput ? 1e5 : ml)}`);
1749
+ }
1750
+ try {
1751
+ const inputs = JSON.parse(inputOutputsStr);
1752
+ if (typeof inputs !== "object" || inputs === null) return;
1753
+ const keys = Object.keys(inputs);
1754
+ if (keys.length === 0) return;
1755
+ if (!question) lines.push(`${pad}input:`);
1756
+ for (const key of keys) {
1757
+ const val = inputs[key];
1758
+ if (typeof val === "object" && val !== null) {
1759
+ renderYamlValue(val, `${pad} `, key, lines, fullOutput, ml, 0);
1760
+ } else {
1761
+ lines.push(`${pad} ${key}: ${truncate(String(val), ml)}`);
1762
+ }
1763
+ }
1764
+ } catch {
1765
+ }
1766
+ }
1767
+ function renderNode(node, prefix, isLast, depth, maxDepth, lines, parentSpan, fallbackIntent, fullOutput, dedup) {
1768
+ if (depth > maxDepth) return;
1769
+ const hasChildren = node.children.length > 0;
1770
+ const connector = depth === 0 ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1771
+ const { line: formatted, output: deferredOutput } = formatSpanLine(
1772
+ node.span,
1773
+ parentSpan,
1774
+ fallbackIntent,
1775
+ fullOutput,
1776
+ dedup,
1777
+ hasChildren
1778
+ );
1779
+ if (formatted) {
1780
+ lines.push(`${prefix}${connector}${formatted}`);
1781
+ }
1782
+ const childPrefix = depth === 0 ? "" : formatted ? prefix + (isLast ? " " : "\u2502 ") : prefix;
1783
+ for (let i = 0; i < node.children.length; i++) {
1784
+ const isChildLast = i === node.children.length - 1 && !deferredOutput;
1785
+ renderNode(
1786
+ node.children[i],
1787
+ childPrefix,
1788
+ isChildLast,
1789
+ depth + (formatted ? 1 : 0),
1790
+ maxDepth,
1791
+ lines,
1792
+ node.span,
1793
+ fallbackIntent,
1794
+ fullOutput,
1795
+ dedup
1796
+ );
1797
+ }
1798
+ if (deferredOutput) {
1799
+ lines.push(`${childPrefix}\u2514\u2500\u2192 ${deferredOutput}`);
1800
+ }
1801
+ }
1802
+ function formatSpanLine(span, parentSpan, fallbackIntent, fullOutput, dedup, hasChildren) {
1803
+ const duration = formatDurationMs(span.durationMs);
1804
+ const attrs = span.attributes;
1805
+ const name = span.name;
1806
+ const previewLimit = fullOutput ? 1e4 : 120;
1807
+ const displayName = String(attrs["visor.check.id"] || name).replace(/^visor\.check\./, "");
1808
+ const dedupeOutputStr = (rawOutput, label) => {
1809
+ if (!rawOutput) return "";
1810
+ if (!dedup) return rawOutput;
1811
+ const ref = dedupeOrRegister(dedup, "outputs", rawOutput, label);
1812
+ if (ref) return `(= ${ref})`;
1813
+ return rawOutput;
1814
+ };
1815
+ const dedupeIntentStr = (rawIntent, label) => {
1816
+ if (!rawIntent || !dedup) return rawIntent ? ` \u{1F4AC} ${rawIntent}` : "";
1817
+ const ref = dedupeOrRegister(dedup, "intents", rawIntent, label);
1818
+ if (ref) return ` \u{1F4AC} (= ${ref})`;
1819
+ return ` \u{1F4AC} ${rawIntent}`;
1820
+ };
1821
+ const makeResult = (line, outputPreview) => {
1822
+ if (!outputPreview) return { line };
1823
+ if (hasChildren) {
1824
+ return { line, output: outputPreview };
1825
+ }
1826
+ return { line: `${line} \u2192 ${outputPreview}` };
1827
+ };
1828
+ const toolName = attrs["tool.name"] || attrs["visor.tool.name"];
1829
+ if (toolName) {
1830
+ const toolInput = extractToolInput(String(toolName), attrs);
1831
+ const toolResultLen = attrs["tool.result.length"] || attrs["tool.result.count"];
1832
+ const toolSuccess = attrs["tool.success"];
1833
+ const tn = String(toolName);
1834
+ const isSearchTool = tn === "search" || tn === "searchCode" || tn === "search_code";
1835
+ const numLen = toolResultLen ? Number(toolResultLen) : -1;
1836
+ const noResults = isSearchTool && numLen >= 0 && numLen < 500;
1837
+ const resultSize = noResults ? "no results" : toolResultLen ? formatSize(numLen) : "";
1838
+ const durStr = Number(attrs["tool.duration_ms"]) > 0 ? ` (${formatDurationMs(Number(attrs["tool.duration_ms"]))})` : "";
1839
+ let successMark = toolSuccess === false ? " \u2717" : "";
1840
+ if (tn === "bash") {
1841
+ const toolResult = String(attrs["tool.result"] || "");
1842
+ const exitMatch = toolResult.match(/Exit Code: (\S+)/);
1843
+ const sigMatch = toolResult.match(/Signal: (\S+)/);
1844
+ if (sigMatch && sigMatch[1] !== "null") {
1845
+ successMark = ` [${sigMatch[1]}]`;
1846
+ } else if (exitMatch && exitMatch[1] !== "0" && exitMatch[1] !== "null") {
1847
+ successMark = ` [exit ${exitMatch[1]}]`;
1848
+ }
1849
+ }
1850
+ return {
1851
+ line: `${toolName}(${toolInput})${durStr}${resultSize ? ` \u2192 ${resultSize}` : ""}${successMark}`
1852
+ };
1853
+ }
1854
+ if (name === "search.delegate.dedup") {
1855
+ const query = attrs["dedup.query"] || "";
1856
+ const action = attrs["dedup.action"] || "?";
1857
+ const reason = attrs["dedup.reason"] || "";
1858
+ const rewritten = attrs["dedup.rewritten"] || "";
1859
+ const error = attrs["dedup.error"] || "";
1860
+ let detail = `${action}`;
1861
+ if (rewritten) detail += ` \u2192 "${truncate(String(rewritten), 50)}"`;
1862
+ if (reason) detail += ` \u2014 ${truncate(String(reason), 60)}`;
1863
+ if (error) detail += ` [error: ${truncate(String(error), 80)}]`;
1864
+ return { line: `dedup("${truncate(String(query), 50)}") ${detail} (${duration})` };
1865
+ }
1866
+ if (name === "search.delegate") {
1867
+ const query = attrs["search.query"] || "";
1868
+ const rewritten = attrs["search.query.rewritten"] || "";
1869
+ const output = attrs["search.delegate.output"] || "";
1870
+ let suffix = "";
1871
+ try {
1872
+ const parsed = JSON.parse(String(output));
1873
+ const parts = [];
1874
+ if (parsed.confidence) parts.push(parsed.confidence);
1875
+ if (parsed.groups?.length) parts.push(`${parsed.groups.length} groups`);
1876
+ if (parsed.searches?.length) parts.push(`${parsed.searches.length} searches`);
1877
+ if (parts.length > 0) suffix = ` \u2192 ${parts.join(", ")}`;
1878
+ } catch {
1879
+ }
1880
+ const rewriteStr = rewritten ? ` \u2192 "${truncate(String(rewritten), 40)}"` : "";
1881
+ return {
1882
+ line: `search.delegate("${truncate(String(query), 60)}"${rewriteStr}) (${duration})${suffix}`
1883
+ };
1884
+ }
1885
+ if (name === "ai.request") {
1886
+ const model = attrs["ai.model"] || attrs["gen_ai.request.model"] || "?";
1887
+ const tokensIn = attrs["ai.input_length"] || attrs["gen_ai.usage.input_tokens"] || "";
1888
+ const tokensOut = attrs["gen_ai.usage.output_tokens"] || "";
1889
+ const tokenParts = [];
1890
+ if (tokensIn) tokenParts.push(`${tokensIn} in`);
1891
+ if (tokensOut) tokenParts.push(`${tokensOut} out`);
1892
+ const tokenStr = tokenParts.length > 0 ? ` [${tokenParts.join(", ")}]` : "";
1893
+ const aiInput = String(attrs["ai.input"] || "");
1894
+ let intent = extractAIIntent(aiInput, previewLimit);
1895
+ if (!intent && parentSpan) {
1896
+ const promptPreview = String(
1897
+ parentSpan.attributes["visor.provider.request.prompt.preview"] || ""
1898
+ );
1899
+ if (promptPreview) {
1900
+ intent = extractAIIntent(promptPreview, previewLimit);
1901
+ }
1902
+ if (!intent) {
1903
+ const inputOutputs = String(parentSpan.attributes["visor.check.input.outputs"] || "");
1904
+ if (inputOutputs) {
1905
+ try {
1906
+ const outputs = JSON.parse(inputOutputs);
1907
+ const topic = outputs["route-intent"]?.topic;
1908
+ if (topic) intent = truncate(String(topic), 150);
1909
+ } catch {
1910
+ }
1911
+ }
1912
+ }
1913
+ }
1914
+ if (!intent && fallbackIntent && parentSpan?.name !== "search.delegate") {
1915
+ intent = fallbackIntent;
1916
+ }
1917
+ const intentStr = intent ? dedupeIntentStr(intent, displayName) : "";
1918
+ let outputPreview = "";
1919
+ if (parentSpan) {
1920
+ const checkOutput = String(parentSpan.attributes["visor.check.output"] || "");
1921
+ if (checkOutput) {
1922
+ const preview = extractOutputPreview(checkOutput, previewLimit);
1923
+ if (preview) outputPreview = dedupeOutputStr(preview, displayName);
1924
+ }
1925
+ }
1926
+ const mainLine = `ai ${model} (${duration})${tokenStr}${intentStr}`;
1927
+ return makeResult(mainLine, outputPreview);
1928
+ }
1929
+ const checkId = attrs["visor.check.id"];
1930
+ const checkType = attrs["visor.check.type"];
1931
+ if (checkId || name.startsWith("visor.check.")) {
1932
+ const cleanName = String(checkId || name).replace(/^visor\.check\./, "");
1933
+ const typeStr = checkType ? ` [${checkType}]` : "";
1934
+ const inputOutputs = String(attrs["visor.check.input.outputs"] || "");
1935
+ let inputStr = "";
1936
+ if (inputOutputs && inputOutputs !== "{}") {
1937
+ inputStr = " " + formatInputPreview(inputOutputs, previewLimit);
1938
+ }
1939
+ const output = String(attrs["visor.check.output"] || "");
1940
+ let outputPreview = "";
1941
+ if (output) {
1942
+ const preview = extractOutputPreview(output, previewLimit);
1943
+ if (preview) outputPreview = dedupeOutputStr(preview, cleanName);
1944
+ }
1945
+ const mainLine = `${cleanName}${typeStr} (${duration})${inputStr}`;
1946
+ return makeResult(mainLine, outputPreview);
1947
+ }
1948
+ if (name === "visor.run") {
1949
+ const source = attrs["visor.run.source"] || "";
1950
+ const sourceStr = source ? ` (${source})` : "";
1951
+ return { line: `visor.run${sourceStr} (${duration})` };
1952
+ }
1953
+ if (name === "probe.event.negotiated_timeout.observer" || name === "negotiated_timeout.observer") {
1954
+ let detail = "";
1955
+ const attrDecision = attrs["observer.decision"] || attrs["decision_reason"];
1956
+ if (attrDecision) {
1957
+ const reason = attrs["observer.reason"] || attrs["decision_reason"] || "";
1958
+ if (String(attrDecision) === "extended" || attrs["granted_ms"]) {
1959
+ const grantedMin = attrs["observer.granted_min"] || attrs["granted_min"] || (attrs["granted_ms"] ? Math.round(Number(attrs["granted_ms"]) / 6e4) : "?");
1960
+ detail = `extended +${grantedMin}min`;
1961
+ if (reason) detail += ` (${truncate(String(reason), 60)})`;
1962
+ const used = attrs["observer.extensions_used"] || attrs["extensions_used"];
1963
+ const max = attrs["observer.max_requests"] || attrs["max_requests"];
1964
+ if (used) detail += ` [${used}/${max || "?"} used]`;
1965
+ } else if (String(attrDecision) === "exhausted") {
1966
+ detail = "budget exhausted";
1967
+ } else {
1968
+ detail = `declined`;
1969
+ if (reason) detail += `: ${truncate(String(reason), 60)}`;
1970
+ }
1971
+ }
1972
+ if (!detail && span.events.length > 0) {
1973
+ for (const evt of span.events) {
1974
+ const evtName = evt.name || "";
1975
+ const ea = evt.attributes;
1976
+ if (evtName.includes("observer_extended")) {
1977
+ const grantedMin = ea["granted_min"] || (ea["granted_ms"] ? Math.round(Number(ea["granted_ms"]) / 6e4) : "?");
1978
+ detail = `extended +${grantedMin}min`;
1979
+ if (ea["decision_reason"]) detail += ` (${truncate(String(ea["decision_reason"]), 60)})`;
1980
+ if (ea["extensions_used"])
1981
+ detail += ` [${ea["extensions_used"]}/${ea["max_requests"] || "?"} used]`;
1982
+ break;
1983
+ }
1984
+ if (evtName.includes("observer_declined")) {
1985
+ detail = "declined";
1986
+ if (ea["decision_reason"]) detail += `: ${truncate(String(ea["decision_reason"]), 60)}`;
1987
+ break;
1988
+ }
1989
+ if (evtName.includes("observer_exhausted")) {
1990
+ const used = ea["extensions_used"] || "?";
1991
+ const max = ea["max_requests"] || "?";
1992
+ detail = `budget exhausted [${used}/${max} extensions]`;
1993
+ break;
1994
+ }
1995
+ if (evtName.includes("observer_invoked") && !detail) {
1996
+ const elapsed = ea["elapsed_min"] || "?";
1997
+ const tools = ea["active_tools_count"] || 0;
1998
+ detail = `${elapsed}min elapsed, ${tools} active tools`;
1999
+ }
2000
+ }
2001
+ }
2002
+ if (!detail) {
2003
+ const elapsed = attrs["elapsed_min"];
2004
+ const activeTools = attrs["active_tools_count"] || attrs["active_tools"];
2005
+ if (elapsed) detail += `${elapsed}min elapsed`;
2006
+ if (activeTools)
2007
+ detail += detail ? `, ${activeTools} active tools` : `${activeTools} active tools`;
2008
+ }
2009
+ const label = detail ? `timeout.observer: ${detail}` : "timeout.observer";
2010
+ return { line: `${label} (${duration})` };
2011
+ }
2012
+ if (name.includes("negotiated_timeout.observer_")) {
2013
+ const suffix = name.replace(/.*negotiated_timeout\.observer_/, "");
2014
+ const reason = attrs["decision_reason"] || "";
2015
+ if (suffix === "extended") {
2016
+ const grantedMin = attrs["granted_min"] || (attrs["granted_ms"] ? Math.round(Number(attrs["granted_ms"]) / 6e4) : "?");
2017
+ const used = attrs["extensions_used"] || "?";
2018
+ const max = attrs["max_requests"] || "?";
2019
+ const reasonStr = reason ? ` (${truncate(String(reason), 60)})` : "";
2020
+ return { line: `timeout.extended: +${grantedMin}min${reasonStr} [${used}/${max} used]` };
2021
+ }
2022
+ if (suffix === "declined") {
2023
+ const reasonStr = reason ? `: ${truncate(String(reason), 60)}` : "";
2024
+ return { line: `timeout.declined${reasonStr}` };
2025
+ }
2026
+ if (suffix === "exhausted") {
2027
+ const used = attrs["extensions_used"] || "?";
2028
+ const max = attrs["max_requests"] || "?";
2029
+ return { line: `timeout.exhausted [${used}/${max} extensions, budget depleted]` };
2030
+ }
2031
+ if (suffix === "invoked") {
2032
+ const elapsed = attrs["elapsed_min"] || "?";
2033
+ const tools = attrs["active_tools_count"] || 0;
2034
+ return { line: `timeout.observer invoked (${elapsed}min elapsed, ${tools} active tools)` };
2035
+ }
2036
+ return { line: `timeout.${suffix} (${duration})` };
2037
+ }
2038
+ if (name.includes("negotiated_timeout.abort_summary")) {
2039
+ const summaryLen = attrs["summary_length"] || attrs["summary.length"];
2040
+ const lenStr = summaryLen ? ` \u2192 ${formatSize(Number(summaryLen))}` : "";
2041
+ return { line: `timeout.abort_summary (${duration})${lenStr}` };
2042
+ }
2043
+ if (name.includes("graceful_stop.initiated") || name.includes("graceful_stop.invoked")) {
2044
+ const reason = attrs["graceful_stop.reason"] || attrs["reason"] || "";
2045
+ const reasonStr = reason ? `: ${truncate(String(reason), 80)}` : "";
2046
+ return { line: `graceful_stop${reasonStr} (${duration})` };
2047
+ }
2048
+ const taskEventMatch = name.match(/(?:^probe\.event\.)?task\.([a-z_]+)$/);
2049
+ if (taskEventMatch) {
2050
+ const eventType = taskEventMatch[1];
2051
+ const taskId = attrs["task.id"] ? String(attrs["task.id"]) : "";
2052
+ const taskTitle = attrs["task.title"] ? truncate(String(attrs["task.title"]), 80) : "";
2053
+ const taskIds = attrs["task.ids"] ? truncate(String(attrs["task.ids"]), 120) : "";
2054
+ const count = attrs["task.count"] ? Number(attrs["task.count"]) : void 0;
2055
+ const totalCount = attrs["task.total_count"] ? Number(attrs["task.total_count"]) : void 0;
2056
+ const incompleteCount = attrs["task.incomplete_count"] ? Number(attrs["task.incomplete_count"]) : void 0;
2057
+ const completedCount = attrs["task.completed_count"] ? Number(attrs["task.completed_count"]) : void 0;
2058
+ const remaining = attrs["task.incomplete_remaining"] ? Number(attrs["task.incomplete_remaining"]) : void 0;
2059
+ const newStatus = attrs["task.new_status"] ? String(attrs["task.new_status"]) : "";
2060
+ const fieldsUpdated = attrs["task.fields_updated"] ? truncate(String(attrs["task.fields_updated"]), 80) : "";
2061
+ const error = attrs["task.error"] ? truncate(String(attrs["task.error"]), 100) : "";
2062
+ const action = attrs["task.action"] ? String(attrs["task.action"]) : "";
2063
+ const tail = ` (${duration})`;
2064
+ switch (eventType) {
2065
+ case "session_started":
2066
+ return { line: `tasks enabled${tail}` };
2067
+ case "created":
2068
+ return { line: `[ ] ${taskId}${taskTitle ? `: ${taskTitle}` : ""}${tail}` };
2069
+ case "batch_created": {
2070
+ const totalSuffix = totalCount !== void 0 ? `, total ${totalCount}` : "";
2071
+ return {
2072
+ line: `tasks created (${count ?? "?"}${totalSuffix}): ${taskIds || "unknown"}${tail}`
2073
+ };
2074
+ }
2075
+ case "updated": {
2076
+ const statusSuffix = newStatus && newStatus !== "unchanged" ? ` -> ${newStatus}` : "";
2077
+ const fieldSuffix = fieldsUpdated ? ` [${fieldsUpdated}]` : "";
2078
+ return { line: `task updated: ${taskId}${statusSuffix}${fieldSuffix}${tail}` };
2079
+ }
2080
+ case "batch_updated":
2081
+ return { line: `tasks updated (${count ?? "?"}): ${taskIds || "unknown"}${tail}` };
2082
+ case "completed": {
2083
+ const remainingSuffix = remaining !== void 0 ? ` (${remaining} remaining)` : "";
2084
+ return {
2085
+ line: `[x] ${taskId}${taskTitle ? `: ${taskTitle}` : ""}${remainingSuffix}${tail}`
2086
+ };
2087
+ }
2088
+ case "batch_completed": {
2089
+ const remainingSuffix = remaining !== void 0 ? `, ${remaining} remaining` : "";
2090
+ return {
2091
+ line: `tasks completed (${count ?? "?"}${remainingSuffix}): ${taskIds || "unknown"}${tail}`
2092
+ };
2093
+ }
2094
+ case "deleted":
2095
+ return { line: `task deleted: ${taskId}${tail}` };
2096
+ case "batch_deleted":
2097
+ return { line: `tasks deleted (${count ?? "?"}): ${taskIds || "unknown"}${tail}` };
2098
+ case "listed": {
2099
+ const stats = [];
2100
+ if (totalCount !== void 0) stats.push(`${totalCount} total`);
2101
+ if (incompleteCount !== void 0) stats.push(`${incompleteCount} incomplete`);
2102
+ if (completedCount !== void 0) stats.push(`${completedCount} completed`);
2103
+ return { line: `tasks listed${stats.length > 0 ? ` (${stats.join(", ")})` : ""}${tail}` };
2104
+ }
2105
+ case "validation_error":
2106
+ case "error":
2107
+ return { line: `task.${eventType}: ${error || "unknown error"}${tail}` };
2108
+ case "unknown_action":
2109
+ return { line: `task.unknown_action: ${action || "unknown"}${tail}` };
2110
+ default:
2111
+ return { line: `task.${eventType}${taskId ? `: ${taskId}` : ""}${tail}` };
2112
+ }
2113
+ }
2114
+ return { line: `${name} (${duration})${span.status === "error" ? " \u2717" : ""}` };
2115
+ }
2116
+ function parseWorkspacePath(fullPath) {
2117
+ const wsMatch = fullPath.match(/\/visor-workspaces\/[^/]+\/([^/]+)(?:\/(.+))?/);
2118
+ if (wsMatch) {
2119
+ return { repo: wsMatch[1], filePath: wsMatch[2] };
2120
+ }
2121
+ const wtMatch = fullPath.match(/\.visor\/worktrees\/worktrees\/[^/]+\/(.+)/);
2122
+ if (wtMatch) {
2123
+ const segs = wtMatch[1].split("/");
2124
+ return { repo: segs[0], filePath: segs.length > 1 ? segs.slice(1).join("/") : void 0 };
2125
+ }
2126
+ return null;
2127
+ }
2128
+ function extractToolInput(toolName, attrs) {
2129
+ const result = String(attrs["tool.result"] || "");
2130
+ const explicitInput = String(attrs["tool.input"] || "");
2131
+ if (explicitInput) return truncate(explicitInput, 80);
2132
+ switch (toolName) {
2133
+ case "search": {
2134
+ const patMatch = result.match(/Pattern: (.+)/);
2135
+ const pathMatch = result.match(/Path: (\S+)/);
2136
+ const pattern = patMatch ? patMatch[1].trim() : "";
2137
+ const workspace = pathMatch ? parseWorkspacePath(pathMatch[1]) : null;
2138
+ const parts = [];
2139
+ if (pattern) parts.push(`"${truncate(pattern, 50)}"`);
2140
+ if (workspace?.repo) parts.push(workspace.repo);
2141
+ return parts.join(", ");
2142
+ }
2143
+ case "extract": {
2144
+ const fileMatch = result.match(/Files to extract:\n\s*(\S+)/);
2145
+ if (fileMatch) {
2146
+ const fullPath = fileMatch[1];
2147
+ const workspace = parseWorkspacePath(fullPath);
2148
+ if (workspace) {
2149
+ const parts = [];
2150
+ parts.push(workspace.filePath || workspace.repo || fullPath.split("/").pop() || "");
2151
+ if (workspace.repo) parts.push(workspace.repo);
2152
+ return parts.join(", ");
2153
+ }
2154
+ const segs = fullPath.split("/");
2155
+ return segs.length > 2 ? segs.slice(-2).join("/") : segs[segs.length - 1];
2156
+ }
2157
+ return "";
2158
+ }
2159
+ case "bash": {
2160
+ const cmdMatch = result.match(/^Command: (.+)/);
2161
+ if (cmdMatch) {
2162
+ let cmd = cmdMatch[1].trim();
2163
+ const pipes = cmd.split(/\s*\|\s*/);
2164
+ if (pipes.length > 2) {
2165
+ cmd = `${pipes[0]} | ... (${pipes.length} stages)`;
2166
+ }
2167
+ return truncate(cmd, 80);
2168
+ }
2169
+ const deniedMatch = result.match(/^Permission denied: Component "([^"]+)"/);
2170
+ if (deniedMatch) {
2171
+ return truncate(deniedMatch[1], 60) + " [denied]";
2172
+ }
2173
+ return "";
2174
+ }
2175
+ case "listFiles": {
2176
+ const pathMatch = result.match(/^(\S+):/);
2177
+ if (pathMatch) {
2178
+ const parts = pathMatch[1].split("/");
2179
+ return parts[parts.length - 1] || "";
2180
+ }
2181
+ return "";
2182
+ }
2183
+ default:
2184
+ return truncate(explicitInput, 60);
2185
+ }
2186
+ }
2187
+ function extractAIIntent(input, maxLen = 150) {
2188
+ if (!input || input.length < 20) return "";
2189
+ const qMatch = input.match(/<question>([\s\S]*?)<\/question>/);
2190
+ if (qMatch) return truncate(qMatch[1].trim(), maxLen);
2191
+ const crMatch = input.match(/## Current Request\s*\n(?:User: )?(.+)/);
2192
+ if (crMatch) return truncate(crMatch[1].trim(), maxLen);
2193
+ const userMatch = input.match(/(?:^|\n)User: (.+)/);
2194
+ if (userMatch) return truncate(userMatch[1].trim(), maxLen);
2195
+ const pmMatch = input.match(/Primary message[^:]*:\s*\n(.+)/);
2196
+ if (pmMatch) return truncate(pmMatch[1].trim(), maxLen);
2197
+ return "";
2198
+ }
2199
+ function formatJsonPreview(obj, maxLen) {
2200
+ if (obj === null || obj === void 0) return "";
2201
+ if (typeof obj !== "object") return truncate(String(obj), maxLen);
2202
+ if (Array.isArray(obj)) {
2203
+ if (obj.length === 0) return "[]";
2204
+ const first = typeof obj[0] === "object" && obj[0] !== null ? obj[0].project_id || obj[0].id || obj[0].name || Object.keys(obj[0])[0] || "..." : String(obj[0]);
2205
+ return `[${obj.length}] ${truncate(String(first), 30)}${obj.length > 1 ? ", ..." : ""}`;
2206
+ }
2207
+ const parts = [];
2208
+ let len = 2;
2209
+ for (const [key, val] of Object.entries(obj)) {
2210
+ if (key === "raw" || key === "tags") continue;
2211
+ let valStr;
2212
+ if (val === null || val === void 0) continue;
2213
+ if (typeof val === "boolean") valStr = String(val);
2214
+ else if (typeof val === "number") valStr = String(val);
2215
+ else if (typeof val === "string") {
2216
+ if (val.startsWith("{") || val.startsWith("[")) continue;
2217
+ const clean = val.replace(/\*\*/g, "").replace(/^#+\s*/gm, "").replace(/`/g, "").trim();
2218
+ valStr = `"${truncate(clean.split("\n")[0], Math.min(80, maxLen / 3))}"`;
2219
+ } else if (Array.isArray(val)) {
2220
+ if (val.every((item) => typeof item === "string") && val.join(", ").length < Math.min(120, maxLen / 2)) {
2221
+ valStr = `[${val.join(", ")}]`;
2222
+ } else {
2223
+ valStr = `[${val.length}]`;
2224
+ }
2225
+ } else if (typeof val === "object") valStr = `{${Object.keys(val).length} keys}`;
2226
+ else valStr = "...";
2227
+ const part = `${key}: ${valStr}`;
2228
+ if (len + part.length + 2 > maxLen) {
2229
+ parts.push("...");
2230
+ break;
2231
+ }
2232
+ parts.push(part);
2233
+ len += part.length + 2;
2234
+ }
2235
+ return `{${parts.join(", ")}}`;
2236
+ }
2237
+ function extractOutputPreview(output, maxLen = 120) {
2238
+ try {
2239
+ const obj = JSON.parse(output);
2240
+ return formatJsonPreview(obj, maxLen);
2241
+ } catch {
2242
+ return extractTruncatedJsonPreview(output, maxLen);
2243
+ }
2244
+ }
2245
+ function parseTruncatedJson(input) {
2246
+ let pos = 0;
2247
+ const len = input.length;
2248
+ function skipWhitespace() {
2249
+ while (pos < len && " \n\r".includes(input[pos])) pos++;
2250
+ }
2251
+ function parseString() {
2252
+ if (input[pos] !== '"') return "";
2253
+ pos++;
2254
+ let result = "";
2255
+ while (pos < len) {
2256
+ const ch = input[pos];
2257
+ if (ch === "\\" && pos + 1 < len) {
2258
+ const next = input[pos + 1];
2259
+ if (next === "n") {
2260
+ result += "\n";
2261
+ pos += 2;
2262
+ continue;
2263
+ }
2264
+ if (next === "t") {
2265
+ result += " ";
2266
+ pos += 2;
2267
+ continue;
2268
+ }
2269
+ if (next === '"') {
2270
+ result += '"';
2271
+ pos += 2;
2272
+ continue;
2273
+ }
2274
+ if (next === "\\") {
2275
+ result += "\\";
2276
+ pos += 2;
2277
+ continue;
2278
+ }
2279
+ result += next;
2280
+ pos += 2;
2281
+ continue;
2282
+ }
2283
+ if (ch === '"') {
2284
+ pos++;
2285
+ return result;
2286
+ }
2287
+ result += ch;
2288
+ pos++;
2289
+ }
2290
+ return result;
2291
+ }
2292
+ function parseNumber() {
2293
+ const start = pos;
2294
+ if (input[pos] === "-") pos++;
2295
+ while (pos < len && input[pos] >= "0" && input[pos] <= "9") pos++;
2296
+ if (pos < len && input[pos] === ".") {
2297
+ pos++;
2298
+ while (pos < len && input[pos] >= "0" && input[pos] <= "9") pos++;
2299
+ }
2300
+ return Number(input.slice(start, pos));
2301
+ }
2302
+ function parseValue() {
2303
+ skipWhitespace();
2304
+ if (pos >= len) return void 0;
2305
+ const ch = input[pos];
2306
+ if (ch === '"') return parseString();
2307
+ if (ch === "{") return parseObject();
2308
+ if (ch === "[") return parseArray();
2309
+ if (ch === "t" && input.slice(pos, pos + 4) === "true") {
2310
+ pos += 4;
2311
+ return true;
2312
+ }
2313
+ if (ch === "f" && input.slice(pos, pos + 5) === "false") {
2314
+ pos += 5;
2315
+ return false;
2316
+ }
2317
+ if (ch === "n" && input.slice(pos, pos + 4) === "null") {
2318
+ pos += 4;
2319
+ return null;
2320
+ }
2321
+ if (ch === "-" || ch >= "0" && ch <= "9") return parseNumber();
2322
+ return void 0;
2323
+ }
2324
+ function parseObject() {
2325
+ const obj = {};
2326
+ pos++;
2327
+ skipWhitespace();
2328
+ while (pos < len && input[pos] !== "}") {
2329
+ skipWhitespace();
2330
+ if (pos >= len || input[pos] !== '"') break;
2331
+ const key = parseString();
2332
+ skipWhitespace();
2333
+ if (pos >= len || input[pos] !== ":") {
2334
+ obj[key] = void 0;
2335
+ break;
2336
+ }
2337
+ pos++;
2338
+ const val = parseValue();
2339
+ if (val !== void 0) obj[key] = val;
2340
+ skipWhitespace();
2341
+ if (pos < len && input[pos] === ",") pos++;
2342
+ }
2343
+ if (pos < len && input[pos] === "}") pos++;
2344
+ return obj;
2345
+ }
2346
+ function parseArray() {
2347
+ const arr = [];
2348
+ pos++;
2349
+ skipWhitespace();
2350
+ while (pos < len && input[pos] !== "]") {
2351
+ const val = parseValue();
2352
+ if (val !== void 0) arr.push(val);
2353
+ else break;
2354
+ skipWhitespace();
2355
+ if (pos < len && input[pos] === ",") pos++;
2356
+ skipWhitespace();
2357
+ }
2358
+ if (pos < len && input[pos] === "]") pos++;
2359
+ return arr;
2360
+ }
2361
+ return parseValue();
2362
+ }
2363
+ function extractTruncatedJsonPreview(output, maxLen) {
2364
+ if (!output.startsWith("{") && !output.startsWith("[")) return "";
2365
+ const parsed = parseTruncatedJson(output);
2366
+ if (!parsed || typeof parsed !== "object") return "";
2367
+ return formatJsonPreview(parsed, maxLen);
2368
+ }
2369
+ function formatInputPreview(inputOutputsStr, maxLen) {
2370
+ if (!inputOutputsStr) return "";
2371
+ try {
2372
+ const inputs = JSON.parse(inputOutputsStr);
2373
+ if (typeof inputs !== "object" || inputs === null) return "";
2374
+ const keys = Object.keys(inputs);
2375
+ if (keys.length === 0) return "";
2376
+ const parts = [];
2377
+ let len = 2;
2378
+ for (const key of keys) {
2379
+ const val = inputs[key];
2380
+ let valStr;
2381
+ if (typeof val === "object" && val !== null) {
2382
+ const vkeys = Object.keys(val);
2383
+ if (vkeys.length <= 3) {
2384
+ valStr = `{${vkeys.join(", ")}}`;
2385
+ } else {
2386
+ valStr = `{${vkeys.slice(0, 2).join(", ")}, ...${vkeys.length} keys}`;
2387
+ }
2388
+ } else {
2389
+ valStr = truncate(String(val), 30);
2390
+ }
2391
+ const part = `${key}: ${valStr}`;
2392
+ if (len + part.length + 2 > maxLen) {
2393
+ parts.push("...");
2394
+ break;
2395
+ }
2396
+ parts.push(part);
2397
+ len += part.length + 2;
2398
+ }
2399
+ return `\u2190 {${parts.join(", ")}}`;
2400
+ } catch {
2401
+ return "";
2402
+ }
2403
+ }
2404
+ function extractRouteIntentTopic(spans) {
2405
+ const riSpan = spans.find((s) => s.attributes["visor.check.id"] === "route-intent");
2406
+ if (riSpan) {
2407
+ const output = String(riSpan.attributes["visor.check.output"] || "");
2408
+ if (output) {
2409
+ try {
2410
+ const obj = JSON.parse(output);
2411
+ if (obj.topic) return truncate(String(obj.topic), 150);
2412
+ } catch {
2413
+ }
2414
+ }
2415
+ }
2416
+ const classifySpan = spans.find((s) => s.attributes["visor.check.id"] === "classify");
2417
+ if (classifySpan) {
2418
+ const output = String(classifySpan.attributes["visor.check.output"] || "");
2419
+ if (output) {
2420
+ try {
2421
+ const obj = JSON.parse(output);
2422
+ if (obj.topic) return truncate(String(obj.topic), 150);
2423
+ } catch {
2424
+ }
2425
+ }
2426
+ }
2427
+ return void 0;
2428
+ }
2429
+ function formatSize(chars) {
2430
+ if (chars < 1e3) return `${chars} chars`;
2431
+ return `${(chars / 1e3).toFixed(1)}k chars`;
2432
+ }
2433
+ function formatDurationMs(ms) {
2434
+ if (ms < 0) return "0ms";
2435
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
2436
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2437
+ const mins = Math.floor(ms / 6e4);
2438
+ const secs = Math.round(ms % 6e4 / 1e3);
2439
+ return `${mins}m ${secs}s`;
2440
+ }
2441
+ function truncate(str, max) {
2442
+ if (typeof str !== "string") return "";
2443
+ if (str.length <= max) return str;
2444
+ const tail = Math.min(100, Math.floor(max / 3));
2445
+ const head = max - tail - 19;
2446
+ if (head < 10) return str.slice(0, max - 3) + "...";
2447
+ return str.slice(0, head) + " ...[truncated]... " + str.slice(-tail);
2448
+ }
2449
+ var NOISE_SPAN_NAMES, WRAPPER_SPAN_NAMES, ROOT_TASK_SCOPE_LABEL, TASK_SCOPE_EXCLUDED_CHECKS, SUB_AGENT_SCOPE_CHECKS;
2450
+ var init_trace_serializer = __esm({
2451
+ "src/agent-protocol/trace-serializer.ts"() {
2452
+ init_logger();
2453
+ NOISE_SPAN_NAMES = /* @__PURE__ */ new Set([
2454
+ "engine.state.init",
2455
+ "engine.state.waveplanning",
2456
+ "engine.state.planready",
2457
+ "visor.sandbox.stopAll"
2458
+ ]);
2459
+ WRAPPER_SPAN_NAMES = /* @__PURE__ */ new Set([
2460
+ "engine.state.leveldispatch",
2461
+ "visor.ai_check",
2462
+ "probe.delegation"
2463
+ ]);
2464
+ ROOT_TASK_SCOPE_LABEL = "Main Agent";
2465
+ TASK_SCOPE_EXCLUDED_CHECKS = /* @__PURE__ */ new Set([
2466
+ "chat",
2467
+ "route-intent",
2468
+ "classify",
2469
+ "build-config",
2470
+ "generate-response",
2471
+ "setup-projects",
2472
+ "checkout-docs",
2473
+ "route-projects",
2474
+ "project-items",
2475
+ "checkout-projects"
2476
+ ]);
2477
+ SUB_AGENT_SCOPE_CHECKS = /* @__PURE__ */ new Set(["engineer-task", "explore-code"]);
2478
+ }
2479
+ });
2480
+
2481
+ export {
2482
+ fetchTraceSpans,
2483
+ resolveTraceSpans,
2484
+ findTraceFile,
2485
+ readTraceIdFromFile,
2486
+ serializeTraceForPrompt,
2487
+ buildTraceHeaderLines,
2488
+ buildTraceReport,
2489
+ extractProbeTaskSummary,
2490
+ renderSpanTree,
2491
+ renderTraceTree,
2492
+ renderSpanYaml,
2493
+ init_trace_serializer
2494
+ };
2495
+ //# sourceMappingURL=chunk-BVEATT5V.mjs.map