@tonyclaw/agent-inspector 3.0.30 → 3.0.31

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 (44) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-B6cXJohL.js → CompareDrawer-DB2KggLE.js} +1 -1
  3. package/.output/public/assets/{InspectorPet-mAzeGmEE.js → InspectorPet-CfwDw6kD.js} +1 -1
  4. package/.output/public/assets/ProxyViewerContainer-PcrO96_F.js +126 -0
  5. package/.output/public/assets/{ReplayDialog-DodiTL6E.js → ReplayDialog-3Pl7EKjp.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-B3IauToI.js → RequestAnatomy-BcPjcHLX.js} +1 -1
  7. package/.output/public/assets/ResponseView-DOx51wV3.js +2 -0
  8. package/.output/public/assets/{StreamingChunkSequence-vLDOVQM_.js → StreamingChunkSequence-LkYJWM1A.js} +1 -1
  9. package/.output/public/assets/{_sessionId-BFH4TOaI.js → _sessionId-BSg32vC6.js} +1 -1
  10. package/.output/public/assets/{_sessionId-Cj_HIFZx.js → _sessionId-C0CA68QV.js} +1 -1
  11. package/.output/public/assets/{index-Bmj2lcWE.js → index-BpBUqdAB.js} +1 -1
  12. package/.output/public/assets/{index-Ch7uqnCT.js → index-D93dCgAQ.js} +1 -1
  13. package/.output/public/assets/{index-ZlhqCrSg.js → index-FV5sxqx3.js} +1 -1
  14. package/.output/public/assets/{index-DkDdKvLl.js → index-VUsF4nhf.js} +3 -3
  15. package/.output/public/assets/index-hyQHZeQI.css +1 -0
  16. package/.output/public/assets/{json-viewer-BdrnVQu-.js → json-viewer-WctIbAh_.js} +1 -1
  17. package/.output/public/assets/{jszip.min-CS_nt_o1.js → jszip.min-DnSIoTix.js} +1 -1
  18. package/.output/server/_libs/lucide-react.mjs +55 -55
  19. package/.output/server/{_sessionId-C5S3pGZd.mjs → _sessionId-BHBK3VbF.mjs} +2 -2
  20. package/.output/server/{_sessionId-g6D69lKq.mjs → _sessionId-CyuJ6Kfl.mjs} +1 -1
  21. package/.output/server/_ssr/{CompareDrawer-CqUmGHal.mjs → CompareDrawer-27amlv_a.mjs} +3 -3
  22. package/.output/server/_ssr/{InspectorPet-DI0UJd01.mjs → InspectorPet-CQnGXfLs.mjs} +1 -1
  23. package/.output/server/_ssr/{ProxyViewerContainer-CvsUmbJ3.mjs → ProxyViewerContainer-DlAl0uF-.mjs} +426 -47
  24. package/.output/server/_ssr/{ReplayDialog-DS48dpFG.mjs → ReplayDialog-5m2jtlLa.mjs} +4 -4
  25. package/.output/server/_ssr/{RequestAnatomy-DxYVQU1C.mjs → RequestAnatomy--1WtQNY9.mjs} +3 -3
  26. package/.output/server/_ssr/{ResponseView-BLXW73eq.mjs → ResponseView-CgbqS8sr.mjs} +3 -14
  27. package/.output/server/_ssr/{StreamingChunkSequence-DD0iFuy0.mjs → StreamingChunkSequence-BwpnopWx.mjs} +3 -3
  28. package/.output/server/_ssr/{index-B10LIaK0.mjs → index-CNVi1TzT.mjs} +2 -2
  29. package/.output/server/_ssr/{index-RHVJnU2p.mjs → index-DMCjH_4V.mjs} +1 -1
  30. package/.output/server/_ssr/index.mjs +2 -2
  31. package/.output/server/_ssr/{json-viewer-LDu2XBaK.mjs → json-viewer-DNGN2uoe.mjs} +3 -3
  32. package/.output/server/_ssr/{router-GKvrwifJ.mjs → router-CgsqLnk5.mjs} +5382 -4524
  33. package/.output/server/{_tanstack-start-manifest_v-Df2n4mbT.mjs → _tanstack-start-manifest_v-BH5f_kWV.mjs} +1 -1
  34. package/.output/server/index.mjs +89 -89
  35. package/package.json +1 -1
  36. package/src/components/pi-agent/PiAgentPanel.tsx +205 -34
  37. package/src/components/pi-agent/piAgentChatLogic.ts +263 -0
  38. package/src/components/ui/transient-toast.tsx +6 -5
  39. package/src/lib/piAgentContract.ts +80 -0
  40. package/src/services/piAgent.ts +131 -102
  41. package/src/services/piAgentRetrieval.ts +1180 -0
  42. package/.output/public/assets/ProxyViewerContainer-CkFWv_Nm.js +0 -126
  43. package/.output/public/assets/ResponseView-Hn-5ordK.js +0 -3
  44. package/.output/public/assets/index-CgJj-HBC.css +0 -1
@@ -1,4 +1,4 @@
1
- const tsrStartManifest = () => ({ routes: { __root__: { filePath: "C:/Users/claw/workspace/agent-inspector/src/routes/__root.tsx", children: ["/", "/livez", "/readyz", "/api/alerts", "/api/config", "/api/groups", "/api/health", "/api/logs", "/api/mcp", "/api/models", "/api/pi-agent", "/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/ecosystem/recipes/$recipeId/run", "/api/knowledge/sessions/$sessionId/candidates"], preloads: ["/assets/index-DkDdKvLl.js"], scripts: [{ attrs: { type: "module", async: true, src: "/assets/index-DkDdKvLl.js" } }] }, "/": { filePath: "C:/Users/claw/workspace/agent-inspector/src/routes/index.tsx", children: void 0, preloads: ["/assets/index-Ch7uqnCT.js", "/assets/index-Bmj2lcWE.js", "/assets/ProxyViewerContainer-CkFWv_Nm.js"] }, "/session/$sessionId": { filePath: "C:/Users/claw/workspace/agent-inspector/src/routes/session/$sessionId.tsx", children: void 0, preloads: ["/assets/_sessionId-BFH4TOaI.js", "/assets/_sessionId-Cj_HIFZx.js", "/assets/ProxyViewerContainer-CkFWv_Nm.js"] } } });
1
+ const tsrStartManifest = () => ({ routes: { __root__: { filePath: "C:/Users/claw/workspace/agent-inspector/src/routes/__root.tsx", children: ["/", "/livez", "/readyz", "/api/alerts", "/api/config", "/api/groups", "/api/health", "/api/logs", "/api/mcp", "/api/models", "/api/pi-agent", "/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/ecosystem/recipes/$recipeId/run", "/api/knowledge/sessions/$sessionId/candidates"], preloads: ["/assets/index-VUsF4nhf.js"], scripts: [{ attrs: { type: "module", async: true, src: "/assets/index-VUsF4nhf.js" } }] }, "/": { filePath: "C:/Users/claw/workspace/agent-inspector/src/routes/index.tsx", children: void 0, preloads: ["/assets/index-D93dCgAQ.js", "/assets/index-BpBUqdAB.js", "/assets/ProxyViewerContainer-PcrO96_F.js"] }, "/session/$sessionId": { filePath: "C:/Users/claw/workspace/agent-inspector/src/routes/session/$sessionId.tsx", children: void 0, preloads: ["/assets/_sessionId-BSg32vC6.js", "/assets/_sessionId-C0CA68QV.js", "/assets/ProxyViewerContainer-PcrO96_F.js"] } } });
2
2
  export {
3
3
  tsrStartManifest
4
4
  };
@@ -17,152 +17,152 @@ const headers = ((m) => function headersRouteRule(event) {
17
17
  }
18
18
  });
19
19
  const assets = {
20
- "/assets/alibaba-TTwafVwX.svg": {
21
- "type": "image/svg+xml",
22
- "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
23
- "mtime": "2026-07-13T07:00:08.222Z",
24
- "size": 5915,
25
- "path": "../public/assets/alibaba-TTwafVwX.svg"
26
- },
27
20
  "/assets/agent-inspector-58K1_MsC.ico": {
28
21
  "type": "image/vnd.microsoft.icon",
29
22
  "etag": '"38fa-ZD8kPABHYjPIkcH82FMY6iZM7fI"',
30
- "mtime": "2026-07-13T07:00:08.222Z",
23
+ "mtime": "2026-07-13T10:47:31.253Z",
31
24
  "size": 14586,
32
25
  "path": "../public/assets/agent-inspector-58K1_MsC.ico"
33
26
  },
34
- "/assets/CompareDrawer-B6cXJohL.js": {
35
- "type": "text/javascript; charset=utf-8",
36
- "etag": '"502d-cw9Ev8bfggHZi+M1qBW0qZDzNiQ"',
37
- "mtime": "2026-07-13T07:00:08.224Z",
38
- "size": 20525,
39
- "path": "../public/assets/CompareDrawer-B6cXJohL.js"
40
- },
41
- "/assets/index-CgJj-HBC.css": {
42
- "type": "text/css; charset=utf-8",
43
- "etag": '"29db6-atYjPI+M1MxwGEFWDWjramrrNys"',
44
- "mtime": "2026-07-13T07:00:08.223Z",
45
- "size": 171446,
46
- "path": "../public/assets/index-CgJj-HBC.css"
27
+ "/assets/alibaba-TTwafVwX.svg": {
28
+ "type": "image/svg+xml",
29
+ "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
30
+ "mtime": "2026-07-13T10:47:31.259Z",
31
+ "size": 5915,
32
+ "path": "../public/assets/alibaba-TTwafVwX.svg"
47
33
  },
48
- "/assets/index-Bmj2lcWE.js": {
34
+ "/assets/CompareDrawer-DB2KggLE.js": {
49
35
  "type": "text/javascript; charset=utf-8",
50
- "etag": '"265-ovZBLL82L0gywohZJ8lAz/zSxjk"',
51
- "mtime": "2026-07-13T07:00:08.223Z",
52
- "size": 613,
53
- "path": "../public/assets/index-Bmj2lcWE.js"
36
+ "etag": '"502d-ApUnPM1wAyTe7X+OXAvo6ZKXdnI"',
37
+ "mtime": "2026-07-13T10:47:31.259Z",
38
+ "size": 20525,
39
+ "path": "../public/assets/CompareDrawer-DB2KggLE.js"
54
40
  },
55
- "/assets/index-Ch7uqnCT.js": {
41
+ "/assets/index-D93dCgAQ.js": {
56
42
  "type": "text/javascript; charset=utf-8",
57
- "etag": '"9f-rb9KvturDI7E9oSB94OWRPTvM/0"',
58
- "mtime": "2026-07-13T07:00:08.223Z",
43
+ "etag": '"9f-2Cgmy7sqFHRl4qxjS8qj2OsjKTA"',
44
+ "mtime": "2026-07-13T10:47:31.259Z",
59
45
  "size": 159,
60
- "path": "../public/assets/index-Ch7uqnCT.js"
46
+ "path": "../public/assets/index-D93dCgAQ.js"
61
47
  },
62
- "/assets/index-ZlhqCrSg.js": {
48
+ "/assets/index-FV5sxqx3.js": {
63
49
  "type": "text/javascript; charset=utf-8",
64
- "etag": '"1cd2d-fEkp1Cqfh0Dyt2yNc7H7d4dfk1g"',
65
- "mtime": "2026-07-13T07:00:08.224Z",
50
+ "etag": '"1cd2d-W9uBDL5Yg1YwdB87fKetBtRGcJ0"',
51
+ "mtime": "2026-07-13T10:47:31.259Z",
66
52
  "size": 118061,
67
- "path": "../public/assets/index-ZlhqCrSg.js"
53
+ "path": "../public/assets/index-FV5sxqx3.js"
68
54
  },
69
- "/assets/json-viewer-BdrnVQu-.js": {
55
+ "/assets/index-BpBUqdAB.js": {
70
56
  "type": "text/javascript; charset=utf-8",
71
- "etag": '"1ae7-I7jhJ30hNE0Y1azQqAlD0+Un2eY"',
72
- "mtime": "2026-07-13T07:00:08.224Z",
73
- "size": 6887,
74
- "path": "../public/assets/json-viewer-BdrnVQu-.js"
57
+ "etag": '"265-45l8J5XIE6XEr8Wi3KpRkFeoh5I"',
58
+ "mtime": "2026-07-13T10:47:31.259Z",
59
+ "size": 613,
60
+ "path": "../public/assets/index-BpBUqdAB.js"
75
61
  },
76
62
  "/assets/minimax-BPMzvuL-.jpeg": {
77
63
  "type": "image/jpeg",
78
64
  "etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
79
- "mtime": "2026-07-13T07:00:08.222Z",
65
+ "mtime": "2026-07-13T10:47:31.259Z",
80
66
  "size": 6918,
81
67
  "path": "../public/assets/minimax-BPMzvuL-.jpeg"
82
68
  },
83
- "/assets/jszip.min-CS_nt_o1.js": {
69
+ "/assets/index-VUsF4nhf.js": {
70
+ "type": "text/javascript; charset=utf-8",
71
+ "etag": '"5c73b-7JUqFxaIlB3ZdS/v36ktSl2BJw4"',
72
+ "mtime": "2026-07-13T10:47:31.259Z",
73
+ "size": 378683,
74
+ "path": "../public/assets/index-VUsF4nhf.js"
75
+ },
76
+ "/assets/jszip.min-DnSIoTix.js": {
84
77
  "type": "text/javascript; charset=utf-8",
85
- "etag": '"17b79-1vTAxxxUgtMmQ7CCDtn68tMLw8Y"',
86
- "mtime": "2026-07-13T07:00:08.224Z",
78
+ "etag": '"17b79-ZUSlppgiQp1e7tXQ/88J/xbGjvY"',
79
+ "mtime": "2026-07-13T10:47:31.259Z",
87
80
  "size": 97145,
88
- "path": "../public/assets/jszip.min-CS_nt_o1.js"
81
+ "path": "../public/assets/jszip.min-DnSIoTix.js"
89
82
  },
90
83
  "/assets/qwen-mMn3f5ul.webp": {
91
84
  "type": "image/webp",
92
85
  "etag": '"1b1a-Tw+b2ynhJQl9WztaK3ajSL85yaU"',
93
- "mtime": "2026-07-13T07:00:08.222Z",
86
+ "mtime": "2026-07-13T10:47:31.259Z",
94
87
  "size": 6938,
95
88
  "path": "../public/assets/qwen-mMn3f5ul.webp"
96
89
  },
97
- "/assets/ReplayDialog-DodiTL6E.js": {
90
+ "/assets/RequestAnatomy-BcPjcHLX.js": {
98
91
  "type": "text/javascript; charset=utf-8",
99
- "etag": '"3348-O7YlRpS7IEuQu5D6EGA6zkByuG4"',
100
- "mtime": "2026-07-13T07:00:08.224Z",
101
- "size": 13128,
102
- "path": "../public/assets/ReplayDialog-DodiTL6E.js"
103
- },
104
- "/assets/RequestAnatomy-B3IauToI.js": {
105
- "type": "text/javascript; charset=utf-8",
106
- "etag": '"43f3-z6h4uv5pxIs7S4ZlBCuVe+p0iFI"',
107
- "mtime": "2026-07-13T07:00:08.224Z",
92
+ "etag": '"43f3-02dtTm4Lalgly1NP+G3bQZNTIEU"',
93
+ "mtime": "2026-07-13T10:47:31.259Z",
108
94
  "size": 17395,
109
- "path": "../public/assets/RequestAnatomy-B3IauToI.js"
95
+ "path": "../public/assets/RequestAnatomy-BcPjcHLX.js"
110
96
  },
111
- "/assets/ResponseView-Hn-5ordK.js": {
97
+ "/assets/json-viewer-WctIbAh_.js": {
112
98
  "type": "text/javascript; charset=utf-8",
113
- "etag": '"4765-WGeg2KC/B+YFsv/byZDIblTV6Jw"',
114
- "mtime": "2026-07-13T07:00:08.224Z",
115
- "size": 18277,
116
- "path": "../public/assets/ResponseView-Hn-5ordK.js"
99
+ "etag": '"1ae7-8Y8Lwcw6ENvgS7u9jjAzjF8yQno"',
100
+ "mtime": "2026-07-13T10:47:31.259Z",
101
+ "size": 6887,
102
+ "path": "../public/assets/json-viewer-WctIbAh_.js"
117
103
  },
118
- "/assets/index-DkDdKvLl.js": {
104
+ "/assets/index-hyQHZeQI.css": {
105
+ "type": "text/css; charset=utf-8",
106
+ "etag": '"2a3f8-IEHXdaT77oEfGYjOkxtRShKivC4"',
107
+ "mtime": "2026-07-13T10:47:31.259Z",
108
+ "size": 173048,
109
+ "path": "../public/assets/index-hyQHZeQI.css"
110
+ },
111
+ "/assets/ReplayDialog-3Pl7EKjp.js": {
119
112
  "type": "text/javascript; charset=utf-8",
120
- "etag": '"5c73b-6lRQ1Z9qsH+1vgbWE1HByEZQ6og"',
121
- "mtime": "2026-07-13T07:00:08.223Z",
122
- "size": 378683,
123
- "path": "../public/assets/index-DkDdKvLl.js"
113
+ "etag": '"3348-iqJhiWvskRPxGy8RhiA8Nt27AFg"',
114
+ "mtime": "2026-07-13T10:47:31.259Z",
115
+ "size": 13128,
116
+ "path": "../public/assets/ReplayDialog-3Pl7EKjp.js"
124
117
  },
125
- "/assets/StreamingChunkSequence-vLDOVQM_.js": {
118
+ "/assets/StreamingChunkSequence-LkYJWM1A.js": {
126
119
  "type": "text/javascript; charset=utf-8",
127
- "etag": '"e90-teTSjsZjxzoUSsoYs4mPjE5NX7k"',
128
- "mtime": "2026-07-13T07:00:08.224Z",
120
+ "etag": '"e90-SOZV+Bcn83aLa4VgOxSHKugV6bQ"',
121
+ "mtime": "2026-07-13T10:47:31.259Z",
129
122
  "size": 3728,
130
- "path": "../public/assets/StreamingChunkSequence-vLDOVQM_.js"
123
+ "path": "../public/assets/StreamingChunkSequence-LkYJWM1A.js"
124
+ },
125
+ "/assets/_sessionId-C0CA68QV.js": {
126
+ "type": "text/javascript; charset=utf-8",
127
+ "etag": '"36d-LYtbJmIdKM+cDe90XVDOgP7/MAM"',
128
+ "mtime": "2026-07-13T10:47:31.259Z",
129
+ "size": 877,
130
+ "path": "../public/assets/_sessionId-C0CA68QV.js"
131
131
  },
132
132
  "/assets/zhipuai-BPNAnxo-.svg": {
133
133
  "type": "image/svg+xml",
134
134
  "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
135
- "mtime": "2026-07-13T07:00:08.223Z",
135
+ "mtime": "2026-07-13T10:47:31.259Z",
136
136
  "size": 11256,
137
137
  "path": "../public/assets/zhipuai-BPNAnxo-.svg"
138
138
  },
139
- "/assets/_sessionId-Cj_HIFZx.js": {
139
+ "/assets/ResponseView-DOx51wV3.js": {
140
140
  "type": "text/javascript; charset=utf-8",
141
- "etag": '"36d-QmAi+22R+Gk0W/OFOzm/yAkIAso"',
142
- "mtime": "2026-07-13T07:00:08.223Z",
143
- "size": 877,
144
- "path": "../public/assets/_sessionId-Cj_HIFZx.js"
141
+ "etag": '"44d2-bGLmkapctcEQ4R5OCL+HDoXIQZo"',
142
+ "mtime": "2026-07-13T10:47:31.259Z",
143
+ "size": 17618,
144
+ "path": "../public/assets/ResponseView-DOx51wV3.js"
145
145
  },
146
- "/assets/_sessionId-BFH4TOaI.js": {
146
+ "/assets/_sessionId-BSg32vC6.js": {
147
147
  "type": "text/javascript; charset=utf-8",
148
- "etag": '"a2-GJYB3m0XRuSxKzNpYkRm1Lu86aY"',
149
- "mtime": "2026-07-13T07:00:08.223Z",
148
+ "etag": '"a2-0iEGyiTy0huWc2qQkaL259X8XRM"',
149
+ "mtime": "2026-07-13T10:47:31.259Z",
150
150
  "size": 162,
151
- "path": "../public/assets/_sessionId-BFH4TOaI.js"
151
+ "path": "../public/assets/_sessionId-BSg32vC6.js"
152
152
  },
153
- "/assets/InspectorPet-mAzeGmEE.js": {
153
+ "/assets/InspectorPet-CfwDw6kD.js": {
154
154
  "type": "text/javascript; charset=utf-8",
155
- "etag": '"818f0-uaK3aIxZ+dvyP3LPSnSoqDIcbaw"',
156
- "mtime": "2026-07-13T07:00:08.224Z",
155
+ "etag": '"818f0-1onMqGupoLPaPaWYQIqjZMuCrpE"',
156
+ "mtime": "2026-07-13T10:47:31.259Z",
157
157
  "size": 530672,
158
- "path": "../public/assets/InspectorPet-mAzeGmEE.js"
158
+ "path": "../public/assets/InspectorPet-CfwDw6kD.js"
159
159
  },
160
- "/assets/ProxyViewerContainer-CkFWv_Nm.js": {
160
+ "/assets/ProxyViewerContainer-PcrO96_F.js": {
161
161
  "type": "text/javascript; charset=utf-8",
162
- "etag": '"a244d-LfaXpedBiaFfTNFTFL9RFHIBarc"',
163
- "mtime": "2026-07-13T07:00:08.224Z",
164
- "size": 664653,
165
- "path": "../public/assets/ProxyViewerContainer-CkFWv_Nm.js"
162
+ "etag": '"a409b-H3FWsPUin57mm2BZ8sKTuwjvVdo"',
163
+ "mtime": "2026-07-13T10:47:31.259Z",
164
+ "size": 671899,
165
+ "path": "../public/assets/ProxyViewerContainer-PcrO96_F.js"
166
166
  }
167
167
  };
168
168
  function readAsset(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/agent-inspector",
3
- "version": "3.0.30",
3
+ "version": "3.0.31",
4
4
  "type": "module",
5
5
  "description": "Agent observability and knowledge capture layer for AI coding tools.",
6
6
  "license": "MIT",
@@ -1,7 +1,18 @@
1
- import { BrainCircuit, Loader2, Send } from "lucide-react";
1
+ import { BrainCircuit, Loader2, RotateCcw, Send, Sparkles } from "lucide-react";
2
2
  import { useMemo, useState, type ChangeEvent, type JSX } from "react";
3
- import { PiAgentResponseSchema } from "../../lib/piAgentContract";
3
+ import {
4
+ buildPiAgentHistory,
5
+ createEmptyPiAgentConversation,
6
+ normalizePiAgentResponse,
7
+ PI_AGENT_STARTERS,
8
+ type PiAgentConversationMessage,
9
+ type PiAgentEvidenceView,
10
+ type PiAgentMetricView,
11
+ type PiAgentResponseView,
12
+ } from "./piAgentChatLogic";
4
13
  import { useProviders } from "../../lib/useProviders";
14
+ import { dispatchLogFocusRequest } from "../proxy-viewer/logFocus";
15
+ import { AnswerMarkdown } from "../proxy-viewer/AnswerMarkdown";
5
16
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
6
17
 
7
18
  type PiAgentPanelProps = {
@@ -49,25 +60,115 @@ function sortProviderBySource<T extends { name: string; source?: "company" | "pe
49
60
  });
50
61
  }
51
62
 
63
+ function nextMessageId(role: "user" | "assistant"): string {
64
+ return `${role}-${String(Date.now())}-${String(Math.random()).slice(2)}`;
65
+ }
66
+
67
+ function MetricStrip({ metrics }: { metrics: readonly PiAgentMetricView[] }): JSX.Element | null {
68
+ if (metrics.length === 0) return null;
69
+ return (
70
+ <div className="mt-2 grid grid-cols-2 gap-1.5">
71
+ {metrics.slice(0, 8).map((metric) => (
72
+ <div key={metric.key} className="rounded-sm bg-black/20 px-2 py-1">
73
+ <div className="truncate text-[9px] uppercase text-muted-foreground">{metric.label}</div>
74
+ <div className="truncate font-mono text-[11px] text-cyan-50">{metric.value}</div>
75
+ </div>
76
+ ))}
77
+ </div>
78
+ );
79
+ }
80
+
81
+ function EvidenceCard({ evidence }: { evidence: PiAgentEvidenceView }): JSX.Element {
82
+ const openLog = (): void => {
83
+ if (evidence.logId === null) return;
84
+ dispatchLogFocusRequest({ logId: evidence.logId, tab: "anatomy" });
85
+ };
86
+ const title =
87
+ evidence.logId === null
88
+ ? `${evidence.source}:${evidence.id}`
89
+ : `${evidence.source}:log #${String(evidence.logId)}`;
90
+
91
+ return (
92
+ <button
93
+ type="button"
94
+ onClick={openLog}
95
+ disabled={evidence.logId === null}
96
+ className="w-full rounded-sm bg-black/20 px-2 py-2 text-left shadow-[inset_0_0_0_1px_rgba(255,255,255,0.05)] transition-colors hover:bg-black/30 disabled:cursor-default disabled:hover:bg-black/20"
97
+ >
98
+ <div className="flex min-w-0 items-center justify-between gap-2">
99
+ <span className="truncate font-mono text-[10px] text-cyan-100">{title}</span>
100
+ {evidence.score !== null && (
101
+ <span className="shrink-0 font-mono text-[9px] text-muted-foreground">
102
+ {evidence.score.toFixed(1)}
103
+ </span>
104
+ )}
105
+ </div>
106
+ <div className="mt-1 flex flex-wrap gap-x-2 gap-y-1 text-[10px] text-muted-foreground">
107
+ {evidence.providerName !== null && <span>{evidence.providerName}</span>}
108
+ {evidence.model !== null && <span>{evidence.model}</span>}
109
+ {evidence.status !== null && <span>status {String(evidence.status)}</span>}
110
+ {evidence.elapsedMs !== null && <span>{String(evidence.elapsedMs)} ms</span>}
111
+ </div>
112
+ <p className="mt-1 line-clamp-3 text-[11px] leading-4 text-foreground/85">
113
+ {evidence.excerpt}
114
+ </p>
115
+ </button>
116
+ );
117
+ }
118
+
119
+ function AssistantMessage({ response }: { response: PiAgentResponseView }): JSX.Element {
120
+ return (
121
+ <div className="rounded-md bg-black/25 p-3 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.05)]">
122
+ <AnswerMarkdown text={response.answer} />
123
+ <div className="mt-2 break-words font-mono text-[10px] text-cyan-100/70">
124
+ {response.providerName} / {response.model} / {response.protocol} /{" "}
125
+ {String(response.logCount)} matched logs
126
+ </div>
127
+ <MetricStrip metrics={response.summaryMetrics} />
128
+ <MetricStrip metrics={response.queryMetrics} />
129
+ {response.evidence.length > 0 && (
130
+ <div className="mt-2 space-y-1.5">
131
+ {response.evidence.slice(0, 6).map((evidence) => (
132
+ <EvidenceCard key={`${evidence.source}-${evidence.id}`} evidence={evidence} />
133
+ ))}
134
+ </div>
135
+ )}
136
+ </div>
137
+ );
138
+ }
139
+
140
+ function ConversationMessage({ message }: { message: PiAgentConversationMessage }): JSX.Element {
141
+ if (message.role === "assistant" && message.response !== null) {
142
+ return <AssistantMessage response={message.response} />;
143
+ }
144
+ return (
145
+ <div className="rounded-md bg-cyan-400/10 px-3 py-2 text-xs leading-5 text-cyan-50">
146
+ {message.content}
147
+ </div>
148
+ );
149
+ }
150
+
52
151
  export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps): JSX.Element {
53
152
  const { providers } = useProviders();
54
153
  const piProviders = useMemo(
55
154
  () => sortProviderBySource(providers.filter(providerSupportsPiAgent)),
56
155
  [providers],
57
156
  );
58
- const [question, setQuestion] = useState(DEFAULT_QUESTION);
157
+ const [conversation, setConversation] = useState(() => ({
158
+ ...createEmptyPiAgentConversation(),
159
+ question: DEFAULT_QUESTION,
160
+ }));
59
161
  const [providerId, setProviderId] = useState<string>("__auto__");
60
162
  const [model, setModel] = useState<string>("__auto__");
61
- const [answer, setAnswer] = useState<string>("");
62
- const [status, setStatus] = useState<string>("");
63
163
  const [loading, setLoading] = useState(false);
64
164
 
65
165
  const selectedProvider = piProviders.find((provider) => provider.id === providerId);
66
166
  const modelOptions = selectedProvider?.models ?? [];
167
+ const question = conversation.question;
67
168
  const canAsk = question.trim() !== "" && piProviders.length > 0 && !loading;
68
169
 
69
170
  const handleQuestionChange = (event: ChangeEvent<HTMLTextAreaElement>): void => {
70
- setQuestion(event.currentTarget.value);
171
+ setConversation((current) => ({ ...current, question: event.currentTarget.value }));
71
172
  };
72
173
 
73
174
  const handleProviderChange = (value: string): void => {
@@ -75,18 +176,40 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
75
176
  setModel("__auto__");
76
177
  };
77
178
 
179
+ const resetConversation = (): void => {
180
+ setConversation({ ...createEmptyPiAgentConversation(), question: DEFAULT_QUESTION });
181
+ };
182
+
183
+ const setStarter = (questionText: string): void => {
184
+ setConversation((current) => ({ ...current, question: questionText, error: null }));
185
+ };
186
+
78
187
  const askPiAgent = (): void => {
79
188
  if (!canAsk) return;
189
+ const trimmedQuestion = question.trim();
190
+ const history = buildPiAgentHistory(conversation.messages);
191
+ const userMessage: PiAgentConversationMessage = {
192
+ id: nextMessageId("user"),
193
+ role: "user",
194
+ content: trimmedQuestion,
195
+ response: null,
196
+ };
197
+
80
198
  setLoading(true);
81
- setStatus("");
82
- setAnswer("");
199
+ setConversation((current) => ({
200
+ ...current,
201
+ messages: [...current.messages, userMessage],
202
+ question: "",
203
+ error: null,
204
+ }));
83
205
 
84
206
  const body = {
85
- question: question.trim(),
207
+ question: trimmedQuestion,
208
+ history,
86
209
  ...(currentSessionId !== null ? { sessionId: currentSessionId } : {}),
87
210
  ...(providerId !== "__auto__" ? { providerId } : {}),
88
211
  ...(model !== "__auto__" ? { model } : {}),
89
- logLimit: 24,
212
+ logLimit: 16,
90
213
  };
91
214
 
92
215
  void fetch("/api/pi-agent", {
@@ -100,26 +223,39 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
100
223
  .then(async (response) => {
101
224
  const raw = await response.text();
102
225
  if (!response.ok) {
103
- setStatus(`Pi Agent failed: HTTP ${String(response.status)}`);
104
- setAnswer(raw);
226
+ setConversation((current) => ({
227
+ ...current,
228
+ error: `Pi Agent failed: HTTP ${String(response.status)} ${raw}`,
229
+ }));
105
230
  return;
106
231
  }
107
232
  const parsedJson: unknown = JSON.parse(raw);
108
- const parsed = PiAgentResponseSchema.safeParse(parsedJson);
109
- if (!parsed.success) {
110
- setStatus("Pi Agent returned an unexpected response.");
111
- setAnswer(raw);
233
+ const normalized = normalizePiAgentResponse(parsedJson);
234
+ if (normalized === null) {
235
+ setConversation((current) => ({
236
+ ...current,
237
+ error: "Pi Agent returned an unexpected response.",
238
+ }));
112
239
  return;
113
240
  }
114
- setStatus(
115
- `${parsed.data.providerName} / ${parsed.data.model} / ${parsed.data.protocol} / ${String(
116
- parsed.data.logCount,
117
- )} logs`,
118
- );
119
- setAnswer(parsed.data.answer);
241
+ setConversation((current) => ({
242
+ ...current,
243
+ messages: [
244
+ ...current.messages,
245
+ {
246
+ id: nextMessageId("assistant"),
247
+ role: "assistant",
248
+ content: normalized.answer,
249
+ response: normalized,
250
+ },
251
+ ],
252
+ }));
120
253
  })
121
254
  .catch((err: unknown) => {
122
- setStatus(err instanceof Error ? err.message : "Pi Agent request failed.");
255
+ setConversation((current) => ({
256
+ ...current,
257
+ error: err instanceof Error ? err.message : "Pi Agent request failed.",
258
+ }));
123
259
  })
124
260
  .finally(() => {
125
261
  setLoading(false);
@@ -133,9 +269,17 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
133
269
  <div className="min-w-0 flex-1">
134
270
  <div className="text-xs font-semibold text-foreground">Pi Agent</div>
135
271
  <div className="text-[11px] text-muted-foreground">
136
- {currentSessionId === null ? "Inspector-wide analysis" : "Session analysis"}
272
+ {currentSessionId === null ? "Inspector-wide ChatBI" : "Session ChatBI"}
137
273
  </div>
138
274
  </div>
275
+ <button
276
+ type="button"
277
+ onClick={resetConversation}
278
+ className="inline-flex size-7 items-center justify-center rounded-md bg-black/25 text-muted-foreground transition-colors hover:text-foreground"
279
+ title="Reset conversation"
280
+ >
281
+ <RotateCcw className="size-3.5" />
282
+ </button>
139
283
  </div>
140
284
 
141
285
  <div className="mt-3 grid grid-cols-2 gap-2">
@@ -167,11 +311,43 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
167
311
  </Select>
168
312
  </div>
169
313
 
314
+ {conversation.messages.length === 0 && (
315
+ <div className="mt-3 flex flex-wrap gap-1.5">
316
+ {PI_AGENT_STARTERS.map((starter) => (
317
+ <button
318
+ key={starter.id}
319
+ type="button"
320
+ onClick={() => {
321
+ setStarter(starter.question);
322
+ }}
323
+ className="inline-flex h-7 items-center gap-1.5 rounded-sm bg-black/25 px-2 text-[11px] text-cyan-50 transition-colors hover:bg-cyan-400/12"
324
+ >
325
+ <Sparkles className="size-3" />
326
+ <span>{starter.label}</span>
327
+ </button>
328
+ ))}
329
+ </div>
330
+ )}
331
+
332
+ {conversation.messages.length > 0 && (
333
+ <div className="mt-3 max-h-96 space-y-2 overflow-y-auto pr-1">
334
+ {conversation.messages.map((message) => (
335
+ <ConversationMessage key={message.id} message={message} />
336
+ ))}
337
+ {loading && (
338
+ <div className="inline-flex items-center gap-2 rounded-md bg-black/25 px-3 py-2 text-xs text-muted-foreground">
339
+ <Loader2 className="size-3.5 animate-spin" />
340
+ <span>Searching Inspector...</span>
341
+ </div>
342
+ )}
343
+ </div>
344
+ )}
345
+
170
346
  <textarea
171
347
  value={question}
172
348
  onChange={handleQuestionChange}
173
- className="mt-2 min-h-24 w-full resize-y rounded-md bg-black/25 px-3 py-2 text-xs leading-5 text-foreground outline-none shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-ring"
174
- placeholder="Ask Pi Agent about this session..."
349
+ className="mt-3 min-h-20 w-full resize-y rounded-md bg-black/25 px-3 py-2 text-xs leading-5 text-foreground outline-none shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-ring"
350
+ placeholder="Ask Pi Agent across saved Inspector data..."
175
351
  />
176
352
 
177
353
  <button
@@ -184,19 +360,14 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
184
360
  <span>{loading ? "Thinking..." : "Ask Pi"}</span>
185
361
  </button>
186
362
 
363
+ {conversation.error !== null && (
364
+ <p className="mt-2 break-words text-[11px] leading-5 text-red-200">{conversation.error}</p>
365
+ )}
187
366
  {piProviders.length === 0 && (
188
367
  <p className="mt-2 text-[11px] leading-5 text-muted-foreground">
189
368
  Configure a Provider with Responses, Chat, or Anthropic URL to enable Pi Agent.
190
369
  </p>
191
370
  )}
192
- {status !== "" && (
193
- <p className="mt-2 break-words font-mono text-[10px] text-cyan-100/70">{status}</p>
194
- )}
195
- {answer !== "" && (
196
- <div className="mt-3 max-h-64 overflow-y-auto whitespace-pre-wrap rounded-md bg-black/25 p-3 text-xs leading-5 text-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.05)]">
197
- {answer}
198
- </div>
199
- )}
200
371
  {logCount === 0 && (
201
372
  <p className="mt-2 text-[11px] leading-5 text-muted-foreground">
202
373
  No logs are visible in this scope yet; Pi Agent can still answer Provider setup questions.