@tonyclaw/agent-inspector 3.0.6 → 3.0.8

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 (70) hide show
  1. package/.output/cli.js +245 -171
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-DsvO6QyJ.js → CompareDrawer-Bth_Ywyt.js} +1 -1
  4. package/.output/public/assets/ProxyViewerContainer-C73EU9Jq.js +106 -0
  5. package/.output/public/assets/ReplayDialog-C2YYAuTe.js +1 -0
  6. package/.output/public/assets/{RequestAnatomy-m1q-uVTl.js → RequestAnatomy-8k9dAX2j.js} +1 -1
  7. package/.output/public/assets/ResponseView-DEL6JcXm.js +3 -0
  8. package/.output/public/assets/StreamingChunkSequence-DngmoR9j.js +1 -0
  9. package/.output/public/assets/{_sessionId-CWP4pxOU.js → _sessionId-4vk9RMZ5.js} +1 -1
  10. package/.output/public/assets/index-DBEB1rHe.css +1 -0
  11. package/.output/public/assets/{index-zfZUz67K.js → index-DINLvnm9.js} +1 -1
  12. package/.output/public/assets/index-LPIGiUMP.js +1 -0
  13. package/.output/public/assets/{json-viewer-_4r1LBBE.js → json-viewer-CH5dvS2Y.js} +1 -1
  14. package/.output/public/assets/{main-C_nEHNUM.js → main-GWkZn4xy.js} +2 -2
  15. package/.output/server/{_sessionId-CTqCAmUF.mjs → _sessionId-EgsjdkN7.mjs} +3 -3
  16. package/.output/server/_ssr/{CompareDrawer-D6b6TZ-O.mjs → CompareDrawer-CaM8FYuD.mjs} +3 -3
  17. package/.output/server/_ssr/{ProxyViewerContainer-Dp7Va9_T.mjs → ProxyViewerContainer-EJIogcGd.mjs} +580 -344
  18. package/.output/server/_ssr/{ReplayDialog-D_KWQ1GY.mjs → ReplayDialog-BPpJ2-q8.mjs} +58 -23
  19. package/.output/server/_ssr/{RequestAnatomy-CPubXjMX.mjs → RequestAnatomy-DeQAWnZx.mjs} +2 -2
  20. package/.output/server/_ssr/{ResponseView-CsTiNoOE.mjs → ResponseView-Dshm8iQ7.mjs} +2 -2
  21. package/.output/server/_ssr/{StreamingChunkSequence-Bgbzt23U.mjs → StreamingChunkSequence-32yIU2hn.mjs} +30 -20
  22. package/.output/server/_ssr/{index-P1gSpuM5.mjs → index-XRcx9blU.mjs} +2 -2
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-10ciVGnc.mjs → json-viewer-C0aX5rqC.mjs} +2 -2
  25. package/.output/server/_ssr/{router-BJJ0VhsB.mjs → router-BI-Mvxz9.mjs} +441 -172
  26. package/.output/server/_tanstack-start-manifest_v-jKScklL_.mjs +4 -0
  27. package/.output/server/index.mjs +73 -73
  28. package/package.json +1 -1
  29. package/src/assets/IDE/opencode.svg +1 -1
  30. package/src/assets/IDE/xiaomimimo.svg +1 -1
  31. package/src/cli/detect-tools.ts +2 -27
  32. package/src/cli/templates/codex-skill-onboard.ts +1 -1
  33. package/src/cli/templates/skill-onboard.ts +2 -3
  34. package/src/cli.ts +9 -86
  35. package/src/components/ProxyViewer.tsx +68 -21
  36. package/src/components/ProxyViewerContainer.tsx +30 -19
  37. package/src/components/alerts/AlertsDialog.tsx +3 -3
  38. package/src/components/clients/ClientLogo.tsx +22 -2
  39. package/src/components/ecosystem/AgentLabDialog.tsx +215 -12
  40. package/src/components/groups/GroupsDialog.tsx +31 -43
  41. package/src/components/providers/ImportWizardDialog.tsx +52 -102
  42. package/src/components/providers/ProviderCard.tsx +4 -4
  43. package/src/components/providers/ProvidersPanel.tsx +82 -68
  44. package/src/components/providers/SettingsDialog.tsx +11 -12
  45. package/src/components/proxy-viewer/AgentTraceSummary.tsx +15 -23
  46. package/src/components/proxy-viewer/CompareDrawer.tsx +1 -1
  47. package/src/components/proxy-viewer/ConversationHeader.tsx +2 -2
  48. package/src/components/proxy-viewer/ReplayDialog.tsx +65 -22
  49. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +33 -20
  50. package/src/lib/apiClient.ts +43 -10
  51. package/src/lib/ecosystemContract.ts +32 -0
  52. package/src/lib/export-logs.ts +37 -3
  53. package/src/proxy/ecosystemRegistry.ts +244 -0
  54. package/src/proxy/ecosystemTasks.ts +153 -0
  55. package/src/proxy/platformCommands.ts +212 -0
  56. package/src/proxy/socketTracker.ts +84 -143
  57. package/src/routes/api/ecosystem.packages.$packageId.help.ts +16 -0
  58. package/src/routes/api/ecosystem.packages.$packageId.install.ts +16 -0
  59. package/src/routes/api/ecosystem.packages.$packageId.runner-presets.ts +19 -0
  60. package/src/routes/api/ecosystem.packages.$packageId.upgrade.ts +16 -0
  61. package/src/routes/api/ecosystem.packages.ts +1 -248
  62. package/src/routes/api/ecosystem.tasks.$taskId.ts +16 -0
  63. package/src/routes/api/ecosystem.tasks.ts +12 -0
  64. package/.output/public/assets/ProxyViewerContainer-CfP-komD.js +0 -106
  65. package/.output/public/assets/ReplayDialog-CQlLX1Uo.js +0 -1
  66. package/.output/public/assets/ResponseView-DuLLeYSh.js +0 -3
  67. package/.output/public/assets/StreamingChunkSequence-CN4aRFBx.js +0 -1
  68. package/.output/public/assets/index-0pt1umWP.css +0 -1
  69. package/.output/public/assets/index-DSUEOPbC.js +0 -1
  70. package/.output/server/_tanstack-start-manifest_v-CltlYL3I.mjs +0 -4
@@ -0,0 +1,4 @@
1
+ const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/__root.tsx", "children": ["/", "/api/alerts", "/api/config", "/api/groups", "/api/health", "/api/logs", "/api/mcp", "/api/models", "/api/providers", "/api/runs", "/api/sessions", "/proxy/$", "/session/$sessionId", "/api/ecosystem/packages", "/api/ecosystem/tasks", "/api/knowledge/candidates", "/api/knowledge/project-context", "/api/knowledge/search", "/api/knowledge/sessions/$sessionId/candidates"], "preloads": ["/assets/main-GWkZn4xy.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-LPIGiUMP.js", "/assets/ProxyViewerContainer-C73EU9Jq.js"] }, "/api/alerts": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/alerts.ts", "children": ["/api/alerts/summary"] }, "/api/config": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.ts", "children": ["/api/config/paths"] }, "/api/groups": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.ts", "children": ["/api/groups/$groupId"] }, "/api/health": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/health.ts" }, "/api/logs": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.ts", "children": ["/api/logs/$id", "/api/logs/import", "/api/logs/stream"] }, "/api/mcp": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/mcp.ts" }, "/api/models": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/models.ts" }, "/api/providers": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.ts", "children": ["/api/providers/$providerId", "/api/providers/export", "/api/providers/import", "/api/providers/scan"] }, "/api/runs": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/runs.ts", "children": ["/api/runs/$runId"] }, "/api/sessions": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/sessions.ts" }, "/proxy/$": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/proxy/$.ts" }, "/session/$sessionId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/session/$sessionId.tsx", "assets": [], "preloads": ["/assets/_sessionId-4vk9RMZ5.js", "/assets/ProxyViewerContainer-C73EU9Jq.js"] }, "/api/alerts/summary": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/alerts.summary.ts" }, "/api/config/paths": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.paths.ts" }, "/api/ecosystem/packages": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/ecosystem.packages.ts", "children": ["/api/ecosystem/packages/$packageId/help", "/api/ecosystem/packages/$packageId/install", "/api/ecosystem/packages/$packageId/runner-presets", "/api/ecosystem/packages/$packageId/upgrade"] }, "/api/ecosystem/tasks": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/ecosystem.tasks.ts", "children": ["/api/ecosystem/tasks/$taskId"] }, "/api/groups/$groupId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.$groupId.ts", "children": ["/api/groups/$groupId/evidence", "/api/groups/$groupId/sessions"] }, "/api/knowledge/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.ts", "children": ["/api/knowledge/candidates/$candidateId"] }, "/api/knowledge/project-context": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.project-context.ts" }, "/api/knowledge/search": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.search.ts" }, "/api/logs/$id": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.ts", "children": ["/api/logs/$id/body", "/api/logs/$id/chunks", "/api/logs/$id/replay"] }, "/api/logs/import": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.import.ts" }, "/api/logs/stream": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.stream.ts" }, "/api/providers/$providerId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.ts", "children": ["/api/providers/$providerId/model-metadata", "/api/providers/$providerId/test"] }, "/api/providers/export": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.export.ts" }, "/api/providers/import": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.import.ts" }, "/api/providers/scan": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.scan.ts" }, "/api/runs/$runId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/runs.$runId.ts", "children": ["/api/runs/$runId/evidence"] }, "/api/ecosystem/tasks/$taskId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/ecosystem.tasks.$taskId.ts" }, "/api/groups/$groupId/evidence": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.$groupId.evidence.ts" }, "/api/groups/$groupId/sessions": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.$groupId.sessions.ts" }, "/api/knowledge/candidates/$candidateId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.ts", "children": ["/api/knowledge/candidates/$candidateId/promote"] }, "/api/logs/$id/body": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.body.ts" }, "/api/logs/$id/chunks": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.chunks.ts" }, "/api/logs/$id/replay": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.replay.ts" }, "/api/providers/$providerId/model-metadata": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.model-metadata.ts" }, "/api/providers/$providerId/test": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.ts", "children": ["/api/providers/$providerId/test/log"] }, "/api/runs/$runId/evidence": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/runs.$runId.evidence.ts" }, "/api/ecosystem/packages/$packageId/help": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/ecosystem.packages.$packageId.help.ts" }, "/api/ecosystem/packages/$packageId/install": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/ecosystem.packages.$packageId.install.ts" }, "/api/ecosystem/packages/$packageId/runner-presets": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/ecosystem.packages.$packageId.runner-presets.ts" }, "/api/ecosystem/packages/$packageId/upgrade": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/ecosystem.packages.$packageId.upgrade.ts" }, "/api/knowledge/candidates/$candidateId/promote": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.promote.ts" }, "/api/knowledge/sessions/$sessionId/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.sessions.$sessionId.candidates.ts" }, "/api/providers/$providerId/test/log": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.log.ts" } }, "clientEntry": "/assets/main-GWkZn4xy.js" });
2
+ export {
3
+ tsrStartManifest
4
+ };
@@ -35,117 +35,117 @@ const headers = ((m) => function headersRouteRule(event) {
35
35
  }
36
36
  });
37
37
  const assets = {
38
- "/assets/CompareDrawer-DsvO6QyJ.js": {
39
- "type": "text/javascript; charset=utf-8",
40
- "etag": '"4bbf-neNUNNWS2aRLlwwzlzUnpCV9X7Q"',
41
- "mtime": "2026-07-07T03:16:53.329Z",
42
- "size": 19391,
43
- "path": "../public/assets/CompareDrawer-DsvO6QyJ.js"
38
+ "/assets/alibaba-TTwafVwX.svg": {
39
+ "type": "image/svg+xml",
40
+ "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
41
+ "mtime": "2026-07-07T06:50:23.987Z",
42
+ "size": 5915,
43
+ "path": "../public/assets/alibaba-TTwafVwX.svg"
44
44
  },
45
- "/assets/index-0pt1umWP.css": {
46
- "type": "text/css; charset=utf-8",
47
- "etag": '"2281f-b6adbSBuuyRm1qef8XcY8zI8Tvw"',
48
- "mtime": "2026-07-07T03:16:53.329Z",
49
- "size": 141343,
50
- "path": "../public/assets/index-0pt1umWP.css"
45
+ "/assets/json-viewer-CH5dvS2Y.js": {
46
+ "type": "text/javascript; charset=utf-8",
47
+ "etag": '"1ae6-mkJVggyfXvFwp12XQ48FkTNkg0s"',
48
+ "mtime": "2026-07-07T06:50:23.989Z",
49
+ "size": 6886,
50
+ "path": "../public/assets/json-viewer-CH5dvS2Y.js"
51
51
  },
52
- "/assets/index-DSUEOPbC.js": {
52
+ "/assets/index-LPIGiUMP.js": {
53
53
  "type": "text/javascript; charset=utf-8",
54
- "etag": '"74-OxAYH3UQoN48eatHx4yL09xqgvs"',
55
- "mtime": "2026-07-07T03:16:53.329Z",
54
+ "etag": '"74-Av6m82h27Vs/kt5ZU7JNsr6Ibp8"',
55
+ "mtime": "2026-07-07T06:50:23.987Z",
56
56
  "size": 116,
57
- "path": "../public/assets/index-DSUEOPbC.js"
57
+ "path": "../public/assets/index-LPIGiUMP.js"
58
58
  },
59
- "/assets/index-zfZUz67K.js": {
60
- "type": "text/javascript; charset=utf-8",
61
- "etag": '"1cd2c-L9qOZ3f8PNUAGiT0rnnoST6YWfg"',
62
- "mtime": "2026-07-07T03:16:53.329Z",
63
- "size": 118060,
64
- "path": "../public/assets/index-zfZUz67K.js"
59
+ "/assets/index-DBEB1rHe.css": {
60
+ "type": "text/css; charset=utf-8",
61
+ "etag": '"2332d-uhZ5O8YO18nm1uKrz7yr4vHuhE8"',
62
+ "mtime": "2026-07-07T06:50:23.987Z",
63
+ "size": 144173,
64
+ "path": "../public/assets/index-DBEB1rHe.css"
65
65
  },
66
66
  "/assets/minimax-BPMzvuL-.jpeg": {
67
67
  "type": "image/jpeg",
68
68
  "etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
69
- "mtime": "2026-07-07T03:16:53.328Z",
69
+ "mtime": "2026-07-07T06:50:23.987Z",
70
70
  "size": 6918,
71
71
  "path": "../public/assets/minimax-BPMzvuL-.jpeg"
72
72
  },
73
- "/assets/json-viewer-_4r1LBBE.js": {
74
- "type": "text/javascript; charset=utf-8",
75
- "etag": '"1ae6-Irtd3+8DFA2Z/Oz+6RoXClR8clw"',
76
- "mtime": "2026-07-07T03:16:53.329Z",
77
- "size": 6886,
78
- "path": "../public/assets/json-viewer-_4r1LBBE.js"
79
- },
80
- "/assets/ReplayDialog-CQlLX1Uo.js": {
73
+ "/assets/CompareDrawer-Bth_Ywyt.js": {
81
74
  "type": "text/javascript; charset=utf-8",
82
- "etag": '"31d6-QUb3xhIgyzB/nFNkjGPh1V87D+8"',
83
- "mtime": "2026-07-07T03:16:53.329Z",
84
- "size": 12758,
85
- "path": "../public/assets/ReplayDialog-CQlLX1Uo.js"
75
+ "etag": '"4bc3-qiALj2irD3Gvo7RyA4h+9S7S53c"',
76
+ "mtime": "2026-07-07T06:50:23.989Z",
77
+ "size": 19395,
78
+ "path": "../public/assets/CompareDrawer-Bth_Ywyt.js"
86
79
  },
87
- "/assets/main-C_nEHNUM.js": {
80
+ "/assets/ReplayDialog-C2YYAuTe.js": {
88
81
  "type": "text/javascript; charset=utf-8",
89
- "etag": '"512fd-52gkbjgsqFGOTrK9SwcjAR0fqb4"',
90
- "mtime": "2026-07-07T03:16:53.329Z",
91
- "size": 332541,
92
- "path": "../public/assets/main-C_nEHNUM.js"
82
+ "etag": '"3347-yIlDlrQkom3XWNTlzq0+7EmeA0A"',
83
+ "mtime": "2026-07-07T06:50:23.989Z",
84
+ "size": 13127,
85
+ "path": "../public/assets/ReplayDialog-C2YYAuTe.js"
93
86
  },
94
- "/assets/RequestAnatomy-m1q-uVTl.js": {
87
+ "/assets/RequestAnatomy-8k9dAX2j.js": {
95
88
  "type": "text/javascript; charset=utf-8",
96
- "etag": '"43f2-kRHIkcpQkfNXmUqtzlXz5Bl+zf0"',
97
- "mtime": "2026-07-07T03:16:53.329Z",
89
+ "etag": '"43f2-fP26iw3SWnM8J81HJ/BcWXo7hS4"',
90
+ "mtime": "2026-07-07T06:50:23.989Z",
98
91
  "size": 17394,
99
- "path": "../public/assets/RequestAnatomy-m1q-uVTl.js"
92
+ "path": "../public/assets/RequestAnatomy-8k9dAX2j.js"
100
93
  },
101
- "/assets/ResponseView-DuLLeYSh.js": {
94
+ "/assets/StreamingChunkSequence-DngmoR9j.js": {
102
95
  "type": "text/javascript; charset=utf-8",
103
- "etag": '"47fa-l3OUJeUgO0oj1UT9vgXjilhwNEw"',
104
- "mtime": "2026-07-07T03:16:53.329Z",
105
- "size": 18426,
106
- "path": "../public/assets/ResponseView-DuLLeYSh.js"
96
+ "etag": '"e95-KW7/jzgQdbeTfEOjFSon9HoG0/4"',
97
+ "mtime": "2026-07-07T06:50:23.989Z",
98
+ "size": 3733,
99
+ "path": "../public/assets/StreamingChunkSequence-DngmoR9j.js"
107
100
  },
108
- "/assets/alibaba-TTwafVwX.svg": {
109
- "type": "image/svg+xml",
110
- "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
111
- "mtime": "2026-07-07T03:16:53.328Z",
112
- "size": 5915,
113
- "path": "../public/assets/alibaba-TTwafVwX.svg"
101
+ "/assets/ResponseView-DEL6JcXm.js": {
102
+ "type": "text/javascript; charset=utf-8",
103
+ "etag": '"47fc-gYa2hl2X5GBV+v+AJgkfgrKYfQU"',
104
+ "mtime": "2026-07-07T06:50:23.989Z",
105
+ "size": 18428,
106
+ "path": "../public/assets/ResponseView-DEL6JcXm.js"
114
107
  },
115
108
  "/assets/zhipuai-BPNAnxo-.svg": {
116
109
  "type": "image/svg+xml",
117
110
  "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
118
- "mtime": "2026-07-07T03:16:53.328Z",
111
+ "mtime": "2026-07-07T06:50:23.987Z",
119
112
  "size": 11256,
120
113
  "path": "../public/assets/zhipuai-BPNAnxo-.svg"
121
114
  },
122
- "/assets/_sessionId-CWP4pxOU.js": {
115
+ "/assets/_sessionId-4vk9RMZ5.js": {
123
116
  "type": "text/javascript; charset=utf-8",
124
- "etag": '"36c-J74Y+Qo3HXpibkQqcgDBULVLGW4"',
125
- "mtime": "2026-07-07T03:16:53.329Z",
117
+ "etag": '"36c-6R/8anu4Vx/I0RvLfHtbU1UcEvs"',
118
+ "mtime": "2026-07-07T06:50:23.987Z",
126
119
  "size": 876,
127
- "path": "../public/assets/_sessionId-CWP4pxOU.js"
128
- },
129
- "/assets/StreamingChunkSequence-CN4aRFBx.js": {
130
- "type": "text/javascript; charset=utf-8",
131
- "etag": '"dd6-2UvXJrnPm4g6fFFFYJ49Epv69bE"',
132
- "mtime": "2026-07-07T03:16:53.329Z",
133
- "size": 3542,
134
- "path": "../public/assets/StreamingChunkSequence-CN4aRFBx.js"
120
+ "path": "../public/assets/_sessionId-4vk9RMZ5.js"
135
121
  },
136
122
  "/assets/qwen-CONDcHqt.png": {
137
123
  "type": "image/png",
138
124
  "etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
139
- "mtime": "2026-07-07T03:16:53.328Z",
125
+ "mtime": "2026-07-07T06:50:23.987Z",
140
126
  "size": 357059,
141
127
  "path": "../public/assets/qwen-CONDcHqt.png"
142
128
  },
143
- "/assets/ProxyViewerContainer-CfP-komD.js": {
129
+ "/assets/index-DINLvnm9.js": {
130
+ "type": "text/javascript; charset=utf-8",
131
+ "etag": '"1cd2c-wzEx8p1FStgS+8WpXtaTlrU0YzI"',
132
+ "mtime": "2026-07-07T06:50:23.989Z",
133
+ "size": 118060,
134
+ "path": "../public/assets/index-DINLvnm9.js"
135
+ },
136
+ "/assets/main-GWkZn4xy.js": {
137
+ "type": "text/javascript; charset=utf-8",
138
+ "etag": '"512fd-QsLmdrg3ZNPjkYZZhQKsAA53Elc"',
139
+ "mtime": "2026-07-07T06:50:23.987Z",
140
+ "size": 332541,
141
+ "path": "../public/assets/main-GWkZn4xy.js"
142
+ },
143
+ "/assets/ProxyViewerContainer-C73EU9Jq.js": {
144
144
  "type": "text/javascript; charset=utf-8",
145
- "etag": '"b4969-/yQK6/qgiUKGLd4XgJTPqx6f2BU"',
146
- "mtime": "2026-07-07T03:16:53.329Z",
147
- "size": 739689,
148
- "path": "../public/assets/ProxyViewerContainer-CfP-komD.js"
145
+ "etag": '"b5994-2p3NxcYQXaIsMhEXsT/sG65Tz6o"',
146
+ "mtime": "2026-07-07T06:50:23.989Z",
147
+ "size": 743828,
148
+ "path": "../public/assets/ProxyViewerContainer-C73EU9Jq.js"
149
149
  }
150
150
  };
151
151
  function readAsset(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/agent-inspector",
3
- "version": "3.0.6",
3
+ "version": "3.0.8",
4
4
  "type": "module",
5
5
  "description": "Agent observability and knowledge capture layer for AI coding tools.",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- <svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>opencode</title><path d="M16 6H8v12h8V6zm4 16H4V2h16v20z"></path></svg>
1
+ <svg fill="#7dd3fc" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>OpenCode</title><path d="M16 6H8v12h8V6zm4 16H4V2h16v20z"></path></svg>
@@ -1 +1 @@
1
- <svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>XiaomiMiMo</title><path d="M.958 15.936a.459.459 0 01.459.44v2.729a.46.46 0 01-.918 0v-2.729a.459.459 0 01.459-.44zm4.814-2.035a.46.46 0 01.553.45v4.754a.458.458 0 11-.918 0V15.48L3.74 17.202a.462.462 0 01-.655.016.462.462 0 01-.065-.082L.628 14.67a.459.459 0 01.658-.637l2.124 2.187 2.127-2.188a.46.46 0 01.235-.13zm2.068.004a.46.46 0 01.458.445v4.755a.46.46 0 01-.458.458.459.459 0 01-.458-.458V14.35a.459.459 0 01.458-.445zm1.973 2.014a.46.46 0 01.46.457v2.729a.46.46 0 01-.784.324.46.46 0 01-.134-.324v-2.729a.46.46 0 01.458-.458zm.002-2.045a.458.458 0 01.328.157l2.127 2.19 2.125-2.19a.459.459 0 01.784.318v4.756a.46.46 0 01-.455.458.46.46 0 01-.458-.458V15.48l-1.667 1.723a.46.46 0 01-.65.008l-.005-.005c0-.002-.002-.002-.004-.003l-2.455-2.534a.46.46 0 01-.008-.667.461.461 0 01.338-.128zm6.797 1.206a.46.46 0 01.53.651A1.966 1.966 0 0019.81 18.4a.462.462 0 01.623.18.46.46 0 01-.181.624 2.863 2.863 0 01-1.38.353l-.142-.004a2.88 2.88 0 01-2.393-4.263.461.461 0 01.274-.21zm.864-.931a2.884 2.884 0 013.915 3.914.46.46 0 01-.402.24l-.057-.004a.458.458 0 01-.164-.055.46.46 0 01-.182-.622 1.967 1.967 0 00-2.669-2.67.459.459 0 11-.441-.803zM9.59 6.368c1.481 0 1.696 1.202 1.696 1.654v2.648h-.917v-.432c-.26.346-.792.535-1.36.535-.133 0-1.289-.03-1.384-1.136-.082-.932.675-1.61 2.053-1.61h.691c0-.563-.367-.886-.983-.886-.44.013-.864.174-1.2.458l-.36-.664c.484-.379 1.012-.567 1.764-.567zm4.427.1c1.263 0 2.082.97 2.083 2.15 0 1.181-.824 2.154-2.083 2.154-1.26 0-2.084-.972-2.084-2.152 0-1.18.82-2.153 2.084-2.153zm6.801.015c.68 0 1.202.465 1.197 1.548v2.642H21.1V8.29c0-.312-.002-.98-.63-.98s-.628.667-.628.838v2.524h-.89V8.148c0-.17-.001-.838-.63-.838-.628 0-.628.668-.628.98v2.383h-.917v-4.03h.917V7a1.22 1.22 0 01.947-.516c.398 0 .76.193.982.686a1.321 1.321 0 011.195-.686zm-18.093.872l1.457-1.772H5.32L3.311 8.07l2.14 2.602H4.24L2.725 8.796 1.21 10.672H0L2.138 8.07.13 5.583h1.138l1.458 1.772zm4.149 3.317h-.916V6.644h.916v4.028zm16.99 0h-.916V6.644h.916v4.028zM9.925 8.71c-1.055 0-1.359.412-1.326.742.032.329.324.537.757.537a1.013 1.013 0 001.014-.968l.002-.31h-.447zM14.018 7.3c-.663 0-1.184.487-1.184 1.32 0 .832.52 1.32 1.184 1.32.662 0 1.182-.49 1.182-1.32 0-.832-.52-1.32-1.182-1.32zM6.417 5.001a.568.568 0 01.587.582.588.588 0 01-1.175 0A.57.57 0 016.417 5zm16.991 0a.57.57 0 01.592.582.588.588 0 01-1.174 0 .57.57 0 01.357-.542.572.572 0 01.225-.04z"></path></svg>
1
+ <svg fill="#ff6900" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>MiMo Code</title><path d="M.958 15.936a.459.459 0 01.459.44v2.729a.46.46 0 01-.918 0v-2.729a.459.459 0 01.459-.44zm4.814-2.035a.46.46 0 01.553.45v4.754a.458.458 0 11-.918 0V15.48L3.74 17.202a.462.462 0 01-.655.016.462.462 0 01-.065-.082L.628 14.67a.459.459 0 01.658-.637l2.124 2.187 2.127-2.188a.46.46 0 01.235-.13zm2.068.004a.46.46 0 01.458.445v4.755a.46.46 0 01-.458.458.459.459 0 01-.458-.458V14.35a.459.459 0 01.458-.445zm1.973 2.014a.46.46 0 01.46.457v2.729a.46.46 0 01-.784.324.46.46 0 01-.134-.324v-2.729a.46.46 0 01.458-.458zm.002-2.045a.458.458 0 01.328.157l2.127 2.19 2.125-2.19a.459.459 0 01.784.318v4.756a.46.46 0 01-.455.458.46.46 0 01-.458-.458V15.48l-1.667 1.723a.46.46 0 01-.65.008l-.005-.005c0-.002-.002-.002-.004-.003l-2.455-2.534a.46.46 0 01-.008-.667.461.461 0 01.338-.128zm6.797 1.206a.46.46 0 01.53.651A1.966 1.966 0 0019.81 18.4a.462.462 0 01.623.18.46.46 0 01-.181.624 2.863 2.863 0 01-1.38.353l-.142-.004a2.88 2.88 0 01-2.393-4.263.461.461 0 01.274-.21zm.864-.931a2.884 2.884 0 013.915 3.914.46.46 0 01-.402.24l-.057-.004a.458.458 0 01-.164-.055.46.46 0 01-.182-.622 1.967 1.967 0 00-2.669-2.67.459.459 0 11-.441-.803zM9.59 6.368c1.481 0 1.696 1.202 1.696 1.654v2.648h-.917v-.432c-.26.346-.792.535-1.36.535-.133 0-1.289-.03-1.384-1.136-.082-.932.675-1.61 2.053-1.61h.691c0-.563-.367-.886-.983-.886-.44.013-.864.174-1.2.458l-.36-.664c.484-.379 1.012-.567 1.764-.567zm4.427.1c1.263 0 2.082.97 2.083 2.15 0 1.181-.824 2.154-2.083 2.154-1.26 0-2.084-.972-2.084-2.152 0-1.18.82-2.153 2.084-2.153zm6.801.015c.68 0 1.202.465 1.197 1.548v2.642H21.1V8.29c0-.312-.002-.98-.63-.98s-.628.667-.628.838v2.524h-.89V8.148c0-.17-.001-.838-.63-.838-.628 0-.628.668-.628.98v2.383h-.917v-4.03h.917V7a1.22 1.22 0 01.947-.516c.398 0 .76.193.982.686a1.321 1.321 0 011.195-.686zm-18.093.872l1.457-1.772H5.32L3.311 8.07l2.14 2.602H4.24L2.725 8.796 1.21 10.672H0L2.138 8.07.13 5.583h1.138l1.458 1.772zm4.149 3.317h-.916V6.644h.916v4.028zm16.99 0h-.916V6.644h.916v4.028zM9.925 8.71c-1.055 0-1.359.412-1.326.742.032.329.324.537.757.537a1.013 1.013 0 001.014-.968l.002-.31h-.447zM14.018 7.3c-.663 0-1.184.487-1.184 1.32 0 .832.52 1.32 1.184 1.32.662 0 1.182-.49 1.182-1.32 0-.832-.52-1.32-1.182-1.32zM6.417 5.001a.568.568 0 01.587.582.588.588 0 01-1.175 0A.57.57 0 016.417 5zm16.991 0a.57.57 0 01.592.582.588.588 0 01-1.174 0 .57.57 0 01.357-.542.572.572 0 01.225-.04z"></path></svg>
@@ -10,10 +10,10 @@
10
10
  * `curl` example in that case.
11
11
  */
12
12
 
13
- import { execFileSync } from "node:child_process";
14
13
  import { existsSync } from "node:fs";
15
14
  import { join } from "node:path";
16
15
  import { homedir } from "node:os";
16
+ import { resolveExecutableOnPath } from "../proxy/platformCommands.js";
17
17
 
18
18
  export type DetectedTool = {
19
19
  readonly found: true;
@@ -35,33 +35,8 @@ export type DetectedToolEntry = {
35
35
  readonly result: ToolDetectionResult;
36
36
  };
37
37
 
38
- /**
39
- * Run `command -v <bin>` (bash) or `where <bin>` (cmd) and return the first
40
- * line of stdout. Returns `null` if the binary isn't on PATH. We use the
41
- * platform's own lookup so we don't have to maintain a hard-coded list of
42
- * candidate install dirs.
43
- */
44
38
  function which(bin: string): string | null {
45
- try {
46
- if (process.platform === "win32") {
47
- const out = execFileSync("where", [bin], {
48
- encoding: "utf8",
49
- timeout: 3000,
50
- windowsHide: true,
51
- stdio: ["ignore", "pipe", "ignore"],
52
- });
53
- const first = out.split(/\r?\n/).find((line) => line.trim().length > 0);
54
- return first?.trim() ?? null;
55
- }
56
- const out = execFileSync("sh", ["-c", `command -v ${bin}`], {
57
- encoding: "utf8",
58
- timeout: 3000,
59
- stdio: ["ignore", "pipe", "ignore"],
60
- });
61
- return out.trim() || null;
62
- } catch {
63
- return null;
64
- }
39
+ return resolveExecutableOnPath(bin);
65
40
  }
66
41
 
67
42
  function tryDir(path: string): string | null {
@@ -70,7 +70,7 @@ Test-Path (Join-Path $env:USERPROFILE ".codex\\config.toml")
70
70
  \`\`\`
71
71
 
72
72
  \`\`\`bash
73
- command -v agent-inspector || true
73
+ node -e "const fs=require('node:fs'),p=require('node:path');const names=process.platform==='win32'?['agent-inspector.cmd','agent-inspector.exe','agent-inspector']:['agent-inspector'];const found=(process.env.PATH||'').split(p.delimiter).some(d=>names.some(n=>fs.existsSync(p.join(d,n))));console.log(found?'agent-inspector: present':'agent-inspector: missing')"
74
74
  agent-inspector onboard --status
75
75
  test -d "$HOME/.codex" && echo "codex home: present" || echo "codex home: missing"
76
76
  test -f "$HOME/.codex/config.toml" && echo "config: present" || echo "config: missing"
@@ -208,14 +208,13 @@ if ((Test-Path $projMcp) -and (Select-String -Path $projMcp -Pattern 'agent-insp
208
208
  \`\`\`bash
209
209
  # Unix / macOS / WSL
210
210
  node --version # expect >= 18
211
- command -v claude >/dev/null && echo "claude-code: present" || echo "claude-code: not detected"
211
+ node -e "const fs=require('node:fs'),p=require('node:path');const names=process.platform==='win32'?['claude.cmd','claude.exe','claude']:['claude'];const found=(process.env.PATH||'').split(p.delimiter).some(d=>names.some(n=>fs.existsSync(p.join(d,n))));console.log(found?'claude-code: present':'claude-code: not detected')"
212
212
  \`\`\`
213
213
 
214
214
  \`\`\`powershell
215
215
  # Windows PowerShell — single-quoted so $env: expands correctly
216
216
  node --version # expect >= 18
217
- $null = Get-Command claude -ErrorAction SilentlyContinue
218
- if ($null) { Write-Host 'claude-code: present' } else { Write-Host 'claude-code: not detected' }
217
+ node -e "const fs=require('node:fs'),p=require('node:path');const names=process.platform==='win32'?['claude.cmd','claude.exe','claude']:['claude'];const found=(process.env.PATH||'').split(p.delimiter).some(d=>names.some(n=>fs.existsSync(p.join(d,n))));console.log(found?'claude-code: present':'claude-code: not detected')"
219
218
  \`\`\`
220
219
 
221
220
  > **PAUSE** — if Node is older than 18, ask the user to install a newer version (https://nodejs.org) before continuing. Use \`AskUserQuestion\` with header \`Node version\`.
package/src/cli.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Buffer } from "node:buffer";
3
- import { spawn, execSync, type ChildProcess } from "node:child_process";
3
+ import { spawn, type ChildProcess } from "node:child_process";
4
4
  import { createConnection } from "node:net";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { dirname, join, resolve as resolvePath } from "node:path";
@@ -19,6 +19,7 @@ import {
19
19
  type ServerOutputFilterContext,
20
20
  } from "./cli/startupOutput.js";
21
21
  import { startIdentityProxy } from "./proxy/identityProxy.js";
22
+ import { findPidsByPort, killPid, openUrlCommand } from "./proxy/platformCommands.js";
22
23
 
23
24
  const __filename = fileURLToPath(import.meta.url);
24
25
  const __dirname = dirname(__filename);
@@ -134,26 +135,9 @@ async function waitForInspectorHealthy(
134
135
  }
135
136
 
136
137
  function openBrowser(targetUrl: string): void {
137
- let command: string[] | undefined;
138
- // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
139
- switch (process.platform) {
140
- case "darwin":
141
- command = ["open", targetUrl];
142
- break;
143
- case "linux":
144
- command = ["xdg-open", targetUrl];
145
- break;
146
- case "win32":
147
- command = ["cmd", "/c", "start", "", targetUrl];
148
- break;
149
- default:
150
- // Unsupported platform - do nothing
151
- break;
152
- }
153
- if (command === undefined) return;
154
- const [bin, ...cmdArgs] = command;
155
- if (bin === undefined) return;
156
- const browserProcess = spawn(bin, cmdArgs, {
138
+ const command = openUrlCommand(targetUrl);
139
+ if (command === null) return;
140
+ const browserProcess = spawn(command.command, command.args, {
157
141
  stdio: "ignore",
158
142
  detached: true,
159
143
  windowsHide: true,
@@ -444,71 +428,10 @@ async function runStart(args: string[]): Promise<void> {
444
428
  * Check if a port is in use and kill the process using it
445
429
  */
446
430
  function killProcessOnPort(targetPort: number): void {
447
- const platform = process.platform;
448
-
449
- try {
450
- let pids: number[] = [];
451
-
452
- if (platform === "win32") {
453
- // Windows: use netstat to find PID, then taskkill
454
- const output = execSync(`netstat -ano | findstr :${targetPort}`, {
455
- encoding: "utf8",
456
- timeout: 5000,
457
- windowsHide: true,
458
- });
459
- const lines = output.trim().split("\n");
460
- for (const line of lines) {
461
- const parts = line.trim().split(/\s+/);
462
- if (parts.length >= 5) {
463
- const localAddress = parts[1] ?? "";
464
- const pidStr = parts[4] ?? "";
465
- if (localAddress !== "" && pidStr !== "" && localAddress.includes(`:${targetPort}`)) {
466
- const pid = parseInt(pidStr, 10);
467
- if (!isNaN(pid) && pid > 0) {
468
- pids.push(pid);
469
- }
470
- }
471
- }
472
- }
473
- // Remove duplicates
474
- pids = [...new Set(pids)];
475
-
476
- for (const pid of pids) {
477
- try {
478
- console.log(`Killing process ${pid} on port ${targetPort}...`);
479
- execSync(`taskkill /PID ${pid} /F`, {
480
- encoding: "utf8",
481
- timeout: 5000,
482
- windowsHide: true,
483
- });
484
- } catch {
485
- // Process may have already exited
486
- }
487
- }
488
- } else {
489
- // Unix-like: use lsof
490
- const output = execSync(`lsof -ti:${targetPort}`, { encoding: "utf8", timeout: 5000 });
491
- const lines = output.trim().split("\n");
492
- for (const line of lines) {
493
- const pid = parseInt(line.trim(), 10);
494
- if (!isNaN(pid) && pid > 0) {
495
- pids.push(pid);
496
- }
497
- }
498
- // Remove duplicates
499
- pids = [...new Set(pids)];
500
-
501
- for (const pid of pids) {
502
- try {
503
- console.log(`Killing process ${pid} on port ${targetPort}...`);
504
- execSync(`kill -9 ${pid}`, { encoding: "utf8", timeout: 5000 });
505
- } catch {
506
- // Process may have already exited
507
- }
508
- }
509
- }
510
- } catch {
511
- // No process found on port, which is fine
431
+ const pids = findPidsByPort(targetPort);
432
+ for (const pid of pids) {
433
+ console.log(`Killing process ${pid} on port ${targetPort}...`);
434
+ killPid(pid);
512
435
  }
513
436
  }
514
437
 
@@ -1294,12 +1294,12 @@ function BrandHeader({
1294
1294
  if (compact) {
1295
1295
  return (
1296
1296
  <div className="bg-white/[0.035] shadow-[inset_0_1px_0_rgba(255,255,255,0.045),0_16px_44px_rgba(0,0,0,0.18)] rounded-[8px] p-3">
1297
- <div className="flex items-start justify-between gap-3">
1298
- <div className="flex min-w-0 items-start gap-2">
1297
+ <div className="flex flex-wrap items-start justify-between gap-3">
1298
+ <div className="flex min-w-fit items-start gap-2">
1299
1299
  <CrabFamily compact={true} entrancePhase={crabEntrancePhase} />
1300
- <div className="min-w-0">
1301
- <div className="flex min-w-0 items-baseline gap-2">
1302
- <span className="truncate text-base font-bold">Inspector</span>
1300
+ <div className="min-w-fit">
1301
+ <div className="flex min-w-fit flex-wrap items-baseline gap-x-2 gap-y-0.5">
1302
+ <span className="whitespace-nowrap text-base font-bold">Agent Inspector</span>
1303
1303
  <span className="hidden shrink-0 font-mono text-[11px] font-semibold text-muted-foreground sm:inline">
1304
1304
  v{packageJson.version}
1305
1305
  </span>
@@ -1324,13 +1324,13 @@ function BrandHeader({
1324
1324
  }
1325
1325
 
1326
1326
  return (
1327
- <div className="grid grid-cols-[1fr_auto_1fr] items-start gap-3 pb-6">
1328
- <div />
1329
- <h1 className="flex min-w-0 flex-col items-center gap-2 text-center">
1330
- <span className="flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap">
1327
+ <div className="flex flex-wrap items-start justify-between gap-x-4 gap-y-3 pb-6">
1328
+ <div className="min-w-10 flex-1" aria-hidden="true" />
1329
+ <h1 className="flex min-w-fit flex-col items-center gap-2 text-center">
1330
+ <span className="flex max-w-full flex-wrap items-end justify-center gap-x-2 gap-y-1">
1331
1331
  <CrabFamily compact={false} entrancePhase={crabEntrancePhase} />
1332
- <span className="flex min-w-0 items-baseline gap-2 pl-1">
1333
- <span className="truncate text-lg font-bold">Agent Inspector</span>
1332
+ <span className="flex min-w-fit flex-wrap items-baseline justify-center gap-x-2 gap-y-0.5 pl-1">
1333
+ <span className="whitespace-nowrap text-lg font-bold">Agent Inspector</span>
1334
1334
  <span className="shrink-0 font-mono text-xs font-semibold text-muted-foreground">
1335
1335
  v{packageJson.version}
1336
1336
  </span>
@@ -1338,12 +1338,12 @@ function BrandHeader({
1338
1338
  <Plus className="size-4 shrink-0 text-muted-foreground/70" aria-hidden="true" />
1339
1339
  <McpLogo className="size-10 shrink-0" />
1340
1340
  </span>
1341
- <span className="max-w-[calc(100vw-7rem)] text-sm font-semibold text-muted-foreground">
1341
+ <span className="max-w-full text-sm font-semibold text-muted-foreground">
1342
1342
  Local First. Evidence First.
1343
1343
  </span>
1344
1344
  <McpReadyBadge />
1345
1345
  </h1>
1346
- <div className="justify-self-end flex items-center gap-1">
1346
+ <div className="flex flex-1 justify-end gap-1">
1347
1347
  <AlertsDialog />
1348
1348
  <GroupsDialog />
1349
1349
  <AgentLabDialog currentSessionId={currentSessionId} logCount={logCount} />
@@ -1737,8 +1737,27 @@ export function ProxyViewer({
1737
1737
  const logListRef = useRef<HTMLDivElement>(null);
1738
1738
  const logListWrapperRef = useRef<HTMLDivElement>(null);
1739
1739
  const importInputRef = useRef<HTMLInputElement>(null);
1740
+ const exportAbortRef = useRef<AbortController | null>(null);
1741
+ const exportRequestIdRef = useRef(0);
1740
1742
  useKeyboardNavigation(logListRef, logListWrapperRef, { pageWide: true });
1741
1743
 
1744
+ const cancelTransientTasks = useCallback(() => {
1745
+ exportRequestIdRef.current += 1;
1746
+ exportAbortRef.current?.abort();
1747
+ exportAbortRef.current = null;
1748
+ setExporting(null);
1749
+ setExportError(null);
1750
+ }, []);
1751
+
1752
+ useEffect(
1753
+ () => () => {
1754
+ exportRequestIdRef.current += 1;
1755
+ exportAbortRef.current?.abort();
1756
+ exportAbortRef.current = null;
1757
+ },
1758
+ [],
1759
+ );
1760
+
1742
1761
  useEffect(() => {
1743
1762
  const perCrabDuration = 400;
1744
1763
  const startDelay = 50;
@@ -1762,11 +1781,18 @@ export function ProxyViewer({
1762
1781
 
1763
1782
  const handleExport = useCallback(
1764
1783
  async (mode: ExportMode) => {
1784
+ exportAbortRef.current?.abort();
1785
+ const controller = new AbortController();
1786
+ exportAbortRef.current = controller;
1787
+ const requestId = exportRequestIdRef.current + 1;
1788
+ exportRequestIdRef.current = requestId;
1765
1789
  setExporting(mode);
1766
1790
  setExportError(null);
1767
1791
  try {
1768
- const result = await exportLogsAsZip(logs, mode);
1792
+ const result = await exportLogsAsZip(logs, mode, { signal: controller.signal });
1793
+ if (exportRequestIdRef.current !== requestId) return;
1769
1794
  if (!result.ok) {
1795
+ if (result.canceled === true) return;
1770
1796
  if (onNotify !== undefined) {
1771
1797
  onNotify({ message: result.message, tone: "error" });
1772
1798
  } else {
@@ -1774,7 +1800,10 @@ export function ProxyViewer({
1774
1800
  }
1775
1801
  }
1776
1802
  } finally {
1777
- setExporting(null);
1803
+ if (exportRequestIdRef.current === requestId) {
1804
+ exportAbortRef.current = null;
1805
+ setExporting(null);
1806
+ }
1778
1807
  }
1779
1808
  },
1780
1809
  [logs, onNotify],
@@ -1819,8 +1848,26 @@ export function ProxyViewer({
1819
1848
  // Close the compare drawer when the user changes the session or model
1820
1849
  // filter, since the predecessor relationship may no longer be meaningful.
1821
1850
  useEffect(() => {
1851
+ cancelTransientTasks();
1852
+ setComparePair(null);
1853
+ }, [cancelTransientTasks, selectedSession, selectedModel]);
1854
+
1855
+ const handleClearCurrentView = useCallback(() => {
1856
+ cancelTransientTasks();
1857
+ setImportStatus(null);
1858
+ setImportError(null);
1859
+ setComparePair(null);
1860
+ onClearAll();
1861
+ }, [cancelTransientTasks, onClearAll]);
1862
+
1863
+ const handleReloadCurrentSession = useCallback(() => {
1864
+ if (onReloadSession === undefined) return;
1865
+ cancelTransientTasks();
1866
+ setImportStatus(null);
1867
+ setImportError(null);
1822
1868
  setComparePair(null);
1823
- }, [selectedSession, selectedModel]);
1869
+ onReloadSession();
1870
+ }, [cancelTransientTasks, onReloadSession]);
1824
1871
 
1825
1872
  const closeCompare = useCallback(() => {
1826
1873
  setComparePair(null);
@@ -1927,7 +1974,7 @@ export function ProxyViewer({
1927
1974
  {onReloadSession !== undefined && (
1928
1975
  <button
1929
1976
  type="button"
1930
- onClick={onReloadSession}
1977
+ onClick={handleReloadCurrentSession}
1931
1978
  disabled={isLoading}
1932
1979
  className="bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
1933
1980
  title="Load stored logs for this session"
@@ -1938,7 +1985,7 @@ export function ProxyViewer({
1938
1985
  )}
1939
1986
  <button
1940
1987
  type="button"
1941
- onClick={onClearAll}
1988
+ onClick={handleClearCurrentView}
1942
1989
  disabled={!canClearCurrentView}
1943
1990
  className={cn(
1944
1991
  "bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
@@ -2128,7 +2175,7 @@ export function ProxyViewer({
2128
2175
  {onReloadSession !== undefined && (
2129
2176
  <button
2130
2177
  type="button"
2131
- onClick={onReloadSession}
2178
+ onClick={handleReloadCurrentSession}
2132
2179
  disabled={isLoading}
2133
2180
  className="bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
2134
2181
  title="Reload this session from stored logs"
@@ -2139,7 +2186,7 @@ export function ProxyViewer({
2139
2186
  )}
2140
2187
  <button
2141
2188
  type="button"
2142
- onClick={onClearAll}
2189
+ onClick={handleClearCurrentView}
2143
2190
  disabled={!canClearCurrentView}
2144
2191
  className="bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
2145
2192
  title="Clear the current view. Stored logs remain reloadable."
@@ -2190,7 +2237,7 @@ export function ProxyViewer({
2190
2237
  {onReloadSession !== undefined && (
2191
2238
  <button
2192
2239
  type="button"
2193
- onClick={onReloadSession}
2240
+ onClick={handleReloadCurrentSession}
2194
2241
  className="bg-white/[0.045] hover:bg-white/[0.075] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground"
2195
2242
  >
2196
2243
  <RefreshCw className="size-3.5" />