@tonyclaw/agent-inspector 3.0.30 → 3.0.32

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-CONqySRK.js} +1 -1
  3. package/.output/public/assets/{InspectorPet-mAzeGmEE.js → InspectorPet-B6oMiG2V.js} +1 -1
  4. package/.output/public/assets/ProxyViewerContainer-Ce_WtVu2.js +126 -0
  5. package/.output/public/assets/{ReplayDialog-DodiTL6E.js → ReplayDialog-BauSX4Hj.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-B3IauToI.js → RequestAnatomy-gyMOB2oK.js} +1 -1
  7. package/.output/public/assets/ResponseView-CUQvY1eb.js +2 -0
  8. package/.output/public/assets/{StreamingChunkSequence-vLDOVQM_.js → StreamingChunkSequence-CE6R5CKU.js} +1 -1
  9. package/.output/public/assets/{_sessionId-BFH4TOaI.js → _sessionId-BW1YYf5W.js} +1 -1
  10. package/.output/public/assets/{_sessionId-Cj_HIFZx.js → _sessionId-DRRig28i.js} +1 -1
  11. package/.output/public/assets/{index-DkDdKvLl.js → index-BTor51ZC.js} +3 -3
  12. package/.output/public/assets/{index-Ch7uqnCT.js → index-BzL-q9eN.js} +1 -1
  13. package/.output/public/assets/{index-Bmj2lcWE.js → index-CbFGlzA-.js} +1 -1
  14. package/.output/public/assets/index-D17dU-mX.css +1 -0
  15. package/.output/public/assets/{index-ZlhqCrSg.js → index-iN9QtGKF.js} +1 -1
  16. package/.output/public/assets/{json-viewer-BdrnVQu-.js → json-viewer-cR8mD7Tl.js} +1 -1
  17. package/.output/public/assets/{jszip.min-CS_nt_o1.js → jszip.min-aHTREmPV.js} +1 -1
  18. package/.output/server/_libs/lucide-react.mjs +55 -55
  19. package/.output/server/{_sessionId-C5S3pGZd.mjs → _sessionId-CuTfM9Wy.mjs} +2 -2
  20. package/.output/server/{_sessionId-g6D69lKq.mjs → _sessionId-RGCARWul.mjs} +1 -1
  21. package/.output/server/_ssr/{CompareDrawer-CqUmGHal.mjs → CompareDrawer-mUnxKFwp.mjs} +3 -3
  22. package/.output/server/_ssr/{InspectorPet-DI0UJd01.mjs → InspectorPet-J0mv5NOp.mjs} +1 -1
  23. package/.output/server/_ssr/{ProxyViewerContainer-CvsUmbJ3.mjs → ProxyViewerContainer-C7Wx7SjJ.mjs} +457 -47
  24. package/.output/server/_ssr/{ReplayDialog-DS48dpFG.mjs → ReplayDialog-C_zNrriu.mjs} +4 -4
  25. package/.output/server/_ssr/{RequestAnatomy-DxYVQU1C.mjs → RequestAnatomy-lvR2Uz9j.mjs} +3 -3
  26. package/.output/server/_ssr/{ResponseView-BLXW73eq.mjs → ResponseView-CRcqekgn.mjs} +3 -14
  27. package/.output/server/_ssr/{StreamingChunkSequence-DD0iFuy0.mjs → StreamingChunkSequence-CJLR-ZYV.mjs} +3 -3
  28. package/.output/server/_ssr/{index-RHVJnU2p.mjs → index-BhL5Yn1J.mjs} +1 -1
  29. package/.output/server/_ssr/{index-B10LIaK0.mjs → index-urTy0DU2.mjs} +2 -2
  30. package/.output/server/_ssr/index.mjs +2 -2
  31. package/.output/server/_ssr/{json-viewer-LDu2XBaK.mjs → json-viewer-BcooIEu5.mjs} +3 -3
  32. package/.output/server/_ssr/{router-GKvrwifJ.mjs → router-D-PJYphE.mjs} +5382 -4524
  33. package/.output/server/{_tanstack-start-manifest_v-Df2n4mbT.mjs → _tanstack-start-manifest_v-DhxDbp_1.mjs} +1 -1
  34. package/.output/server/index.mjs +92 -92
  35. package/package.json +1 -1
  36. package/src/components/pi-agent/PiAgentPanel.tsx +229 -34
  37. package/src/components/pi-agent/piAgentChatLogic.ts +287 -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-BTor51ZC.js"], scripts: [{ attrs: { type: "module", async: true, src: "/assets/index-BTor51ZC.js" } }] }, "/": { filePath: "C:/Users/claw/workspace/agent-inspector/src/routes/index.tsx", children: void 0, preloads: ["/assets/index-BzL-q9eN.js", "/assets/index-CbFGlzA-.js", "/assets/ProxyViewerContainer-Ce_WtVu2.js"] }, "/session/$sessionId": { filePath: "C:/Users/claw/workspace/agent-inspector/src/routes/session/$sessionId.tsx", children: void 0, preloads: ["/assets/_sessionId-BW1YYf5W.js", "/assets/_sessionId-DRRig28i.js", "/assets/ProxyViewerContainer-Ce_WtVu2.js"] } } });
2
2
  export {
3
3
  tsrStartManifest
4
4
  };
@@ -20,149 +20,149 @@ const assets = {
20
20
  "/assets/alibaba-TTwafVwX.svg": {
21
21
  "type": "image/svg+xml",
22
22
  "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
23
- "mtime": "2026-07-13T07:00:08.222Z",
23
+ "mtime": "2026-07-13T11:13:02.709Z",
24
24
  "size": 5915,
25
25
  "path": "../public/assets/alibaba-TTwafVwX.svg"
26
26
  },
27
+ "/assets/index-BzL-q9eN.js": {
28
+ "type": "text/javascript; charset=utf-8",
29
+ "etag": '"9f-j9T0jLOXzrjGIoX2azq1sN5LmhQ"',
30
+ "mtime": "2026-07-13T11:13:02.709Z",
31
+ "size": 159,
32
+ "path": "../public/assets/index-BzL-q9eN.js"
33
+ },
27
34
  "/assets/agent-inspector-58K1_MsC.ico": {
28
35
  "type": "image/vnd.microsoft.icon",
29
36
  "etag": '"38fa-ZD8kPABHYjPIkcH82FMY6iZM7fI"',
30
- "mtime": "2026-07-13T07:00:08.222Z",
37
+ "mtime": "2026-07-13T11:13:02.709Z",
31
38
  "size": 14586,
32
39
  "path": "../public/assets/agent-inspector-58K1_MsC.ico"
33
40
  },
34
- "/assets/CompareDrawer-B6cXJohL.js": {
41
+ "/assets/CompareDrawer-CONqySRK.js": {
35
42
  "type": "text/javascript; charset=utf-8",
36
- "etag": '"502d-cw9Ev8bfggHZi+M1qBW0qZDzNiQ"',
37
- "mtime": "2026-07-13T07:00:08.224Z",
43
+ "etag": '"502d-gLRzTmttWySPHLLcJdpVsBrNXn8"',
44
+ "mtime": "2026-07-13T11:13:02.709Z",
38
45
  "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"
46
+ "path": "../public/assets/CompareDrawer-CONqySRK.js"
47
47
  },
48
- "/assets/index-Bmj2lcWE.js": {
48
+ "/assets/index-CbFGlzA-.js": {
49
49
  "type": "text/javascript; charset=utf-8",
50
- "etag": '"265-ovZBLL82L0gywohZJ8lAz/zSxjk"',
51
- "mtime": "2026-07-13T07:00:08.223Z",
50
+ "etag": '"265-FLMWvI0ex+bfipSvja5anvedWxs"',
51
+ "mtime": "2026-07-13T11:13:02.709Z",
52
52
  "size": 613,
53
- "path": "../public/assets/index-Bmj2lcWE.js"
53
+ "path": "../public/assets/index-CbFGlzA-.js"
54
54
  },
55
- "/assets/index-Ch7uqnCT.js": {
56
- "type": "text/javascript; charset=utf-8",
57
- "etag": '"9f-rb9KvturDI7E9oSB94OWRPTvM/0"',
58
- "mtime": "2026-07-13T07:00:08.223Z",
59
- "size": 159,
60
- "path": "../public/assets/index-Ch7uqnCT.js"
55
+ "/assets/index-D17dU-mX.css": {
56
+ "type": "text/css; charset=utf-8",
57
+ "etag": '"2a4f0-2SuF12/r8Z0Yq4rQKaI4iHNWq5k"',
58
+ "mtime": "2026-07-13T11:13:02.709Z",
59
+ "size": 173296,
60
+ "path": "../public/assets/index-D17dU-mX.css"
61
61
  },
62
- "/assets/index-ZlhqCrSg.js": {
62
+ "/assets/json-viewer-cR8mD7Tl.js": {
63
63
  "type": "text/javascript; charset=utf-8",
64
- "etag": '"1cd2d-fEkp1Cqfh0Dyt2yNc7H7d4dfk1g"',
65
- "mtime": "2026-07-13T07:00:08.224Z",
66
- "size": 118061,
67
- "path": "../public/assets/index-ZlhqCrSg.js"
64
+ "etag": '"1ae7-AbetjxR6hVFkIQXTfeAOvu4bC/s"',
65
+ "mtime": "2026-07-13T11:13:02.710Z",
66
+ "size": 6887,
67
+ "path": "../public/assets/json-viewer-cR8mD7Tl.js"
68
68
  },
69
- "/assets/json-viewer-BdrnVQu-.js": {
69
+ "/assets/index-BTor51ZC.js": {
70
70
  "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"
71
+ "etag": '"5c73b-D3nclFsUFRVy0AsHntbLIFgkGG4"',
72
+ "mtime": "2026-07-13T11:13:02.709Z",
73
+ "size": 378683,
74
+ "path": "../public/assets/index-BTor51ZC.js"
75
75
  },
76
76
  "/assets/minimax-BPMzvuL-.jpeg": {
77
77
  "type": "image/jpeg",
78
78
  "etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
79
- "mtime": "2026-07-13T07:00:08.222Z",
79
+ "mtime": "2026-07-13T11:13:02.709Z",
80
80
  "size": 6918,
81
81
  "path": "../public/assets/minimax-BPMzvuL-.jpeg"
82
82
  },
83
- "/assets/jszip.min-CS_nt_o1.js": {
83
+ "/assets/jszip.min-aHTREmPV.js": {
84
84
  "type": "text/javascript; charset=utf-8",
85
- "etag": '"17b79-1vTAxxxUgtMmQ7CCDtn68tMLw8Y"',
86
- "mtime": "2026-07-13T07:00:08.224Z",
85
+ "etag": '"17b79-SA/wL3+DfMcOGSesR1WGtkD50ek"',
86
+ "mtime": "2026-07-13T11:13:02.709Z",
87
87
  "size": 97145,
88
- "path": "../public/assets/jszip.min-CS_nt_o1.js"
88
+ "path": "../public/assets/jszip.min-aHTREmPV.js"
89
89
  },
90
- "/assets/qwen-mMn3f5ul.webp": {
91
- "type": "image/webp",
92
- "etag": '"1b1a-Tw+b2ynhJQl9WztaK3ajSL85yaU"',
93
- "mtime": "2026-07-13T07:00:08.222Z",
94
- "size": 6938,
95
- "path": "../public/assets/qwen-mMn3f5ul.webp"
90
+ "/assets/index-iN9QtGKF.js": {
91
+ "type": "text/javascript; charset=utf-8",
92
+ "etag": '"1cd2d-yY07ols7Lh8PZjqee6rue6TD5F4"',
93
+ "mtime": "2026-07-13T11:13:02.710Z",
94
+ "size": 118061,
95
+ "path": "../public/assets/index-iN9QtGKF.js"
96
96
  },
97
- "/assets/ReplayDialog-DodiTL6E.js": {
97
+ "/assets/ReplayDialog-BauSX4Hj.js": {
98
98
  "type": "text/javascript; charset=utf-8",
99
- "etag": '"3348-O7YlRpS7IEuQu5D6EGA6zkByuG4"',
100
- "mtime": "2026-07-13T07:00:08.224Z",
99
+ "etag": '"3348-92ia9UOlUcxCkb1MxNXS45f5cpc"',
100
+ "mtime": "2026-07-13T11:13:02.709Z",
101
101
  "size": 13128,
102
- "path": "../public/assets/ReplayDialog-DodiTL6E.js"
102
+ "path": "../public/assets/ReplayDialog-BauSX4Hj.js"
103
103
  },
104
- "/assets/RequestAnatomy-B3IauToI.js": {
104
+ "/assets/RequestAnatomy-gyMOB2oK.js": {
105
105
  "type": "text/javascript; charset=utf-8",
106
- "etag": '"43f3-z6h4uv5pxIs7S4ZlBCuVe+p0iFI"',
107
- "mtime": "2026-07-13T07:00:08.224Z",
106
+ "etag": '"43f3-8/JKIc/8w2TkyiqtlSo8zP8gRac"',
107
+ "mtime": "2026-07-13T11:13:02.709Z",
108
108
  "size": 17395,
109
- "path": "../public/assets/RequestAnatomy-B3IauToI.js"
110
- },
111
- "/assets/ResponseView-Hn-5ordK.js": {
112
- "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"
117
- },
118
- "/assets/index-DkDdKvLl.js": {
119
- "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"
124
- },
125
- "/assets/StreamingChunkSequence-vLDOVQM_.js": {
126
- "type": "text/javascript; charset=utf-8",
127
- "etag": '"e90-teTSjsZjxzoUSsoYs4mPjE5NX7k"',
128
- "mtime": "2026-07-13T07:00:08.224Z",
129
- "size": 3728,
130
- "path": "../public/assets/StreamingChunkSequence-vLDOVQM_.js"
109
+ "path": "../public/assets/RequestAnatomy-gyMOB2oK.js"
131
110
  },
132
111
  "/assets/zhipuai-BPNAnxo-.svg": {
133
112
  "type": "image/svg+xml",
134
113
  "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
135
- "mtime": "2026-07-13T07:00:08.223Z",
114
+ "mtime": "2026-07-13T11:13:02.709Z",
136
115
  "size": 11256,
137
116
  "path": "../public/assets/zhipuai-BPNAnxo-.svg"
138
117
  },
139
- "/assets/_sessionId-Cj_HIFZx.js": {
118
+ "/assets/StreamingChunkSequence-CE6R5CKU.js": {
140
119
  "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"
120
+ "etag": '"e90-US3jYU7aEjXewNEAPq8XJ1qDo74"',
121
+ "mtime": "2026-07-13T11:13:02.710Z",
122
+ "size": 3728,
123
+ "path": "../public/assets/StreamingChunkSequence-CE6R5CKU.js"
124
+ },
125
+ "/assets/ResponseView-CUQvY1eb.js": {
126
+ "type": "text/javascript; charset=utf-8",
127
+ "etag": '"44d2-CBY/wtT6qszOPiwl6ctzIdptQ00"',
128
+ "mtime": "2026-07-13T11:13:02.710Z",
129
+ "size": 17618,
130
+ "path": "../public/assets/ResponseView-CUQvY1eb.js"
145
131
  },
146
- "/assets/_sessionId-BFH4TOaI.js": {
132
+ "/assets/_sessionId-BW1YYf5W.js": {
147
133
  "type": "text/javascript; charset=utf-8",
148
- "etag": '"a2-GJYB3m0XRuSxKzNpYkRm1Lu86aY"',
149
- "mtime": "2026-07-13T07:00:08.223Z",
134
+ "etag": '"a2-rgqEWmSpzelhSCOKIKM5jd6cfyg"',
135
+ "mtime": "2026-07-13T11:13:02.709Z",
150
136
  "size": 162,
151
- "path": "../public/assets/_sessionId-BFH4TOaI.js"
137
+ "path": "../public/assets/_sessionId-BW1YYf5W.js"
138
+ },
139
+ "/assets/qwen-mMn3f5ul.webp": {
140
+ "type": "image/webp",
141
+ "etag": '"1b1a-Tw+b2ynhJQl9WztaK3ajSL85yaU"',
142
+ "mtime": "2026-07-13T11:13:02.709Z",
143
+ "size": 6938,
144
+ "path": "../public/assets/qwen-mMn3f5ul.webp"
145
+ },
146
+ "/assets/_sessionId-DRRig28i.js": {
147
+ "type": "text/javascript; charset=utf-8",
148
+ "etag": '"36d-QaI3dXdamdshvrXnLUVUl7l7XzA"',
149
+ "mtime": "2026-07-13T11:13:02.709Z",
150
+ "size": 877,
151
+ "path": "../public/assets/_sessionId-DRRig28i.js"
152
152
  },
153
- "/assets/InspectorPet-mAzeGmEE.js": {
153
+ "/assets/InspectorPet-B6oMiG2V.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-uvpyqFJk/uS3TO5cy6jFFFPTp7o"',
156
+ "mtime": "2026-07-13T11:13:02.710Z",
157
157
  "size": 530672,
158
- "path": "../public/assets/InspectorPet-mAzeGmEE.js"
158
+ "path": "../public/assets/InspectorPet-B6oMiG2V.js"
159
159
  },
160
- "/assets/ProxyViewerContainer-CkFWv_Nm.js": {
160
+ "/assets/ProxyViewerContainer-Ce_WtVu2.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": '"a451b-ixWpKfz2X9eDioKMSjZvvbakkks"',
163
+ "mtime": "2026-07-13T11:13:02.710Z",
164
+ "size": 673051,
165
+ "path": "../public/assets/ProxyViewerContainer-Ce_WtVu2.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.32",
4
4
  "type": "module",
5
5
  "description": "Agent observability and knowledge capture layer for AI coding tools.",
6
6
  "license": "MIT",
@@ -1,7 +1,19 @@
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_SCOPE_PILLS,
8
+ PI_AGENT_STARTERS,
9
+ type PiAgentConversationMessage,
10
+ type PiAgentEvidenceView,
11
+ type PiAgentMetricView,
12
+ type PiAgentResponseView,
13
+ } from "./piAgentChatLogic";
4
14
  import { useProviders } from "../../lib/useProviders";
15
+ import { dispatchLogFocusRequest } from "../proxy-viewer/logFocus";
16
+ import { AnswerMarkdown } from "../proxy-viewer/AnswerMarkdown";
5
17
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
6
18
 
7
19
  type PiAgentPanelProps = {
@@ -49,25 +61,115 @@ function sortProviderBySource<T extends { name: string; source?: "company" | "pe
49
61
  });
50
62
  }
51
63
 
64
+ function nextMessageId(role: "user" | "assistant"): string {
65
+ return `${role}-${String(Date.now())}-${String(Math.random()).slice(2)}`;
66
+ }
67
+
68
+ function MetricStrip({ metrics }: { metrics: readonly PiAgentMetricView[] }): JSX.Element | null {
69
+ if (metrics.length === 0) return null;
70
+ return (
71
+ <div className="mt-2 grid grid-cols-2 gap-1.5">
72
+ {metrics.slice(0, 8).map((metric) => (
73
+ <div key={metric.key} className="rounded-sm bg-black/20 px-2 py-1">
74
+ <div className="truncate text-[9px] uppercase text-muted-foreground">{metric.label}</div>
75
+ <div className="truncate font-mono text-[11px] text-cyan-50">{metric.value}</div>
76
+ </div>
77
+ ))}
78
+ </div>
79
+ );
80
+ }
81
+
82
+ function EvidenceCard({ evidence }: { evidence: PiAgentEvidenceView }): JSX.Element {
83
+ const openLog = (): void => {
84
+ if (evidence.logId === null) return;
85
+ dispatchLogFocusRequest({ logId: evidence.logId, tab: "anatomy" });
86
+ };
87
+ const title =
88
+ evidence.logId === null
89
+ ? `${evidence.source}:${evidence.id}`
90
+ : `${evidence.source}:log #${String(evidence.logId)}`;
91
+
92
+ return (
93
+ <button
94
+ type="button"
95
+ onClick={openLog}
96
+ disabled={evidence.logId === null}
97
+ 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"
98
+ >
99
+ <div className="flex min-w-0 items-center justify-between gap-2">
100
+ <span className="truncate font-mono text-[10px] text-cyan-100">{title}</span>
101
+ {evidence.score !== null && (
102
+ <span className="shrink-0 font-mono text-[9px] text-muted-foreground">
103
+ {evidence.score.toFixed(1)}
104
+ </span>
105
+ )}
106
+ </div>
107
+ <div className="mt-1 flex flex-wrap gap-x-2 gap-y-1 text-[10px] text-muted-foreground">
108
+ {evidence.providerName !== null && <span>{evidence.providerName}</span>}
109
+ {evidence.model !== null && <span>{evidence.model}</span>}
110
+ {evidence.status !== null && <span>status {String(evidence.status)}</span>}
111
+ {evidence.elapsedMs !== null && <span>{String(evidence.elapsedMs)} ms</span>}
112
+ </div>
113
+ <p className="mt-1 line-clamp-3 text-[11px] leading-4 text-foreground/85">
114
+ {evidence.excerpt}
115
+ </p>
116
+ </button>
117
+ );
118
+ }
119
+
120
+ function AssistantMessage({ response }: { response: PiAgentResponseView }): JSX.Element {
121
+ return (
122
+ <div className="rounded-md bg-black/25 p-3 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.05)]">
123
+ <AnswerMarkdown text={response.answer} />
124
+ <div className="mt-2 break-words font-mono text-[10px] text-cyan-100/70">
125
+ {response.providerName} / {response.model} / {response.protocol} /{" "}
126
+ {String(response.logCount)} matched logs
127
+ </div>
128
+ <MetricStrip metrics={response.summaryMetrics} />
129
+ <MetricStrip metrics={response.queryMetrics} />
130
+ {response.evidence.length > 0 && (
131
+ <div className="mt-2 space-y-1.5">
132
+ {response.evidence.slice(0, 6).map((evidence) => (
133
+ <EvidenceCard key={`${evidence.source}-${evidence.id}`} evidence={evidence} />
134
+ ))}
135
+ </div>
136
+ )}
137
+ </div>
138
+ );
139
+ }
140
+
141
+ function ConversationMessage({ message }: { message: PiAgentConversationMessage }): JSX.Element {
142
+ if (message.role === "assistant" && message.response !== null) {
143
+ return <AssistantMessage response={message.response} />;
144
+ }
145
+ return (
146
+ <div className="rounded-md bg-cyan-400/10 px-3 py-2 text-xs leading-5 text-cyan-50">
147
+ {message.content}
148
+ </div>
149
+ );
150
+ }
151
+
52
152
  export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps): JSX.Element {
53
153
  const { providers } = useProviders();
54
154
  const piProviders = useMemo(
55
155
  () => sortProviderBySource(providers.filter(providerSupportsPiAgent)),
56
156
  [providers],
57
157
  );
58
- const [question, setQuestion] = useState(DEFAULT_QUESTION);
158
+ const [conversation, setConversation] = useState(() => ({
159
+ ...createEmptyPiAgentConversation(),
160
+ question: DEFAULT_QUESTION,
161
+ }));
59
162
  const [providerId, setProviderId] = useState<string>("__auto__");
60
163
  const [model, setModel] = useState<string>("__auto__");
61
- const [answer, setAnswer] = useState<string>("");
62
- const [status, setStatus] = useState<string>("");
63
164
  const [loading, setLoading] = useState(false);
64
165
 
65
166
  const selectedProvider = piProviders.find((provider) => provider.id === providerId);
66
167
  const modelOptions = selectedProvider?.models ?? [];
168
+ const question = conversation.question;
67
169
  const canAsk = question.trim() !== "" && piProviders.length > 0 && !loading;
68
170
 
69
171
  const handleQuestionChange = (event: ChangeEvent<HTMLTextAreaElement>): void => {
70
- setQuestion(event.currentTarget.value);
172
+ setConversation((current) => ({ ...current, question: event.currentTarget.value }));
71
173
  };
72
174
 
73
175
  const handleProviderChange = (value: string): void => {
@@ -75,18 +177,40 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
75
177
  setModel("__auto__");
76
178
  };
77
179
 
180
+ const resetConversation = (): void => {
181
+ setConversation({ ...createEmptyPiAgentConversation(), question: DEFAULT_QUESTION });
182
+ };
183
+
184
+ const setStarter = (questionText: string): void => {
185
+ setConversation((current) => ({ ...current, question: questionText, error: null }));
186
+ };
187
+
78
188
  const askPiAgent = (): void => {
79
189
  if (!canAsk) return;
190
+ const trimmedQuestion = question.trim();
191
+ const history = buildPiAgentHistory(conversation.messages);
192
+ const userMessage: PiAgentConversationMessage = {
193
+ id: nextMessageId("user"),
194
+ role: "user",
195
+ content: trimmedQuestion,
196
+ response: null,
197
+ };
198
+
80
199
  setLoading(true);
81
- setStatus("");
82
- setAnswer("");
200
+ setConversation((current) => ({
201
+ ...current,
202
+ messages: [...current.messages, userMessage],
203
+ question: "",
204
+ error: null,
205
+ }));
83
206
 
84
207
  const body = {
85
- question: question.trim(),
208
+ question: trimmedQuestion,
209
+ history,
86
210
  ...(currentSessionId !== null ? { sessionId: currentSessionId } : {}),
87
211
  ...(providerId !== "__auto__" ? { providerId } : {}),
88
212
  ...(model !== "__auto__" ? { model } : {}),
89
- logLimit: 24,
213
+ logLimit: 16,
90
214
  };
91
215
 
92
216
  void fetch("/api/pi-agent", {
@@ -100,26 +224,39 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
100
224
  .then(async (response) => {
101
225
  const raw = await response.text();
102
226
  if (!response.ok) {
103
- setStatus(`Pi Agent failed: HTTP ${String(response.status)}`);
104
- setAnswer(raw);
227
+ setConversation((current) => ({
228
+ ...current,
229
+ error: `Pi Agent failed: HTTP ${String(response.status)} ${raw}`,
230
+ }));
105
231
  return;
106
232
  }
107
233
  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);
234
+ const normalized = normalizePiAgentResponse(parsedJson);
235
+ if (normalized === null) {
236
+ setConversation((current) => ({
237
+ ...current,
238
+ error: "Pi Agent returned an unexpected response.",
239
+ }));
112
240
  return;
113
241
  }
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);
242
+ setConversation((current) => ({
243
+ ...current,
244
+ messages: [
245
+ ...current.messages,
246
+ {
247
+ id: nextMessageId("assistant"),
248
+ role: "assistant",
249
+ content: normalized.answer,
250
+ response: normalized,
251
+ },
252
+ ],
253
+ }));
120
254
  })
121
255
  .catch((err: unknown) => {
122
- setStatus(err instanceof Error ? err.message : "Pi Agent request failed.");
256
+ setConversation((current) => ({
257
+ ...current,
258
+ error: err instanceof Error ? err.message : "Pi Agent request failed.",
259
+ }));
123
260
  })
124
261
  .finally(() => {
125
262
  setLoading(false);
@@ -133,11 +270,42 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
133
270
  <div className="min-w-0 flex-1">
134
271
  <div className="text-xs font-semibold text-foreground">Pi Agent</div>
135
272
  <div className="text-[11px] text-muted-foreground">
136
- {currentSessionId === null ? "Inspector-wide analysis" : "Session analysis"}
273
+ {currentSessionId === null ? "Ask saved Inspector data" : "Ask this session"}
137
274
  </div>
138
275
  </div>
276
+ <button
277
+ type="button"
278
+ onClick={resetConversation}
279
+ className="inline-flex size-7 items-center justify-center rounded-md bg-black/25 text-muted-foreground transition-colors hover:text-foreground"
280
+ title="Reset conversation"
281
+ >
282
+ <RotateCcw className="size-3.5" />
283
+ </button>
139
284
  </div>
140
285
 
286
+ {conversation.messages.length === 0 && (
287
+ <div className="mt-3 rounded-md bg-black/[0.18] px-3 py-2 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.045)]">
288
+ <p className="text-[11px] leading-5 text-muted-foreground">
289
+ ChatBI for Inspector memory: Pi searches saved traffic, sessions, tools, Provider
290
+ signals, alerts, runs, groups, and knowledge candidates, then answers with linked
291
+ evidence.
292
+ </p>
293
+ <div className="mt-2 grid gap-1.5">
294
+ {PI_AGENT_SCOPE_PILLS.map((pill) => (
295
+ <div
296
+ key={pill.id}
297
+ className="rounded-sm bg-cyan-400/[0.06] px-2 py-1.5 shadow-[inset_0_0_0_1px_rgba(103,232,249,0.08)]"
298
+ >
299
+ <div className="text-[10px] font-medium text-cyan-50">{pill.label}</div>
300
+ <div className="mt-0.5 text-[9px] leading-4 text-muted-foreground">
301
+ {pill.description}
302
+ </div>
303
+ </div>
304
+ ))}
305
+ </div>
306
+ </div>
307
+ )}
308
+
141
309
  <div className="mt-3 grid grid-cols-2 gap-2">
142
310
  <Select value={providerId} onValueChange={handleProviderChange}>
143
311
  <SelectTrigger className="h-8 border-0 bg-black/25 text-xs shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)]">
@@ -167,11 +335,43 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
167
335
  </Select>
168
336
  </div>
169
337
 
338
+ {conversation.messages.length === 0 && (
339
+ <div className="mt-3 flex flex-wrap gap-1.5">
340
+ {PI_AGENT_STARTERS.map((starter) => (
341
+ <button
342
+ key={starter.id}
343
+ type="button"
344
+ onClick={() => {
345
+ setStarter(starter.question);
346
+ }}
347
+ 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"
348
+ >
349
+ <Sparkles className="size-3" />
350
+ <span>{starter.label}</span>
351
+ </button>
352
+ ))}
353
+ </div>
354
+ )}
355
+
356
+ {conversation.messages.length > 0 && (
357
+ <div className="mt-3 max-h-96 space-y-2 overflow-y-auto pr-1">
358
+ {conversation.messages.map((message) => (
359
+ <ConversationMessage key={message.id} message={message} />
360
+ ))}
361
+ {loading && (
362
+ <div className="inline-flex items-center gap-2 rounded-md bg-black/25 px-3 py-2 text-xs text-muted-foreground">
363
+ <Loader2 className="size-3.5 animate-spin" />
364
+ <span>Searching Inspector...</span>
365
+ </div>
366
+ )}
367
+ </div>
368
+ )}
369
+
170
370
  <textarea
171
371
  value={question}
172
372
  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..."
373
+ 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"
374
+ placeholder="Ask Pi Agent across saved Inspector data..."
175
375
  />
176
376
 
177
377
  <button
@@ -184,19 +384,14 @@ export function PiAgentPanel({ currentSessionId, logCount }: PiAgentPanelProps):
184
384
  <span>{loading ? "Thinking..." : "Ask Pi"}</span>
185
385
  </button>
186
386
 
387
+ {conversation.error !== null && (
388
+ <p className="mt-2 break-words text-[11px] leading-5 text-red-200">{conversation.error}</p>
389
+ )}
187
390
  {piProviders.length === 0 && (
188
391
  <p className="mt-2 text-[11px] leading-5 text-muted-foreground">
189
392
  Configure a Provider with Responses, Chat, or Anthropic URL to enable Pi Agent.
190
393
  </p>
191
394
  )}
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
395
  {logCount === 0 && (
201
396
  <p className="mt-2 text-[11px] leading-5 text-muted-foreground">
202
397
  No logs are visible in this scope yet; Pi Agent can still answer Provider setup questions.