Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Henri Bonamy commited on
Commit ·
3ce798d
1
Parent(s): 1181d83
added code panel, tool approval, tool calls logging
Browse files- agent/prompts/system_prompt.yaml +1 -1
- configs/main_agent_config.json +1 -1
- frontend/package-lock.json +437 -0
- frontend/package.json +3 -0
- frontend/src/components/Chat/ApprovalFlow.tsx +157 -0
- frontend/src/components/Chat/ChatInput.tsx +73 -38
- frontend/src/components/Chat/MessageBubble.tsx +71 -32
- frontend/src/components/Chat/MessageList.tsx +113 -31
- frontend/src/components/CodePanel/CodePanel.tsx +74 -0
- frontend/src/components/Layout/AppLayout.tsx +203 -145
- frontend/src/components/SessionSidebar/SessionSidebar.tsx +120 -65
- frontend/src/hooks/useAgentWebSocket.ts +39 -16
- frontend/src/store/agentStore.ts +22 -1
- frontend/src/store/layoutStore.ts +23 -0
- frontend/src/theme.ts +14 -9
- frontend/src/types/agent.ts +9 -0
agent/prompts/system_prompt.yaml
CHANGED
|
@@ -77,7 +77,7 @@ system_prompt: |
|
|
| 77 |
|
| 78 |
- Be concise and direct.
|
| 79 |
- Don't flatter the user.
|
| 80 |
-
-
|
| 81 |
- If you are limited in a task, offer alternatives.
|
| 82 |
- Don't thank the user when he provides results.
|
| 83 |
- Explain what you're doing for non-trivial operations.
|
|
|
|
| 77 |
|
| 78 |
- Be concise and direct.
|
| 79 |
- Don't flatter the user.
|
| 80 |
+
- Never use emojis nor exclamation points.
|
| 81 |
- If you are limited in a task, offer alternatives.
|
| 82 |
- Don't thank the user when he provides results.
|
| 83 |
- Explain what you're doing for non-trivial operations.
|
configs/main_agent_config.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
"save_sessions": true,
|
| 4 |
"session_dataset_repo": "akseljoonas/hf-agent-sessions",
|
| 5 |
"yolo_mode": false,
|
| 6 |
-
"confirm_cpu_jobs":
|
| 7 |
"auto_file_upload": false,
|
| 8 |
"mcpServers": {
|
| 9 |
"hf-mcp-server": {
|
|
|
|
| 3 |
"save_sessions": true,
|
| 4 |
"session_dataset_repo": "akseljoonas/hf-agent-sessions",
|
| 5 |
"yolo_mode": false,
|
| 6 |
+
"confirm_cpu_jobs": true,
|
| 7 |
"auto_file_upload": false,
|
| 8 |
"mcpServers": {
|
| 9 |
"hf-mcp-server": {
|
frontend/package-lock.json
CHANGED
|
@@ -15,12 +15,15 @@
|
|
| 15 |
"react": "^18.3.1",
|
| 16 |
"react-dom": "^18.3.1",
|
| 17 |
"react-markdown": "^9.0.1",
|
|
|
|
|
|
|
| 18 |
"zustand": "^5.0.0"
|
| 19 |
},
|
| 20 |
"devDependencies": {
|
| 21 |
"@eslint/js": "^9.13.0",
|
| 22 |
"@types/react": "^18.3.12",
|
| 23 |
"@types/react-dom": "^18.3.1",
|
|
|
|
| 24 |
"@vitejs/plugin-react": "^4.3.3",
|
| 25 |
"eslint": "^9.13.0",
|
| 26 |
"eslint-plugin-react-hooks": "^5.0.0",
|
|
@@ -1818,6 +1821,12 @@
|
|
| 1818 |
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
| 1819 |
"license": "MIT"
|
| 1820 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1821 |
"node_modules/@types/prop-types": {
|
| 1822 |
"version": "15.7.15",
|
| 1823 |
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
|
@@ -1845,6 +1854,16 @@
|
|
| 1845 |
"@types/react": "^18.0.0"
|
| 1846 |
}
|
| 1847 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1848 |
"node_modules/@types/react-transition-group": {
|
| 1849 |
"version": "4.4.12",
|
| 1850 |
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
|
@@ -2856,6 +2875,19 @@
|
|
| 2856 |
"dev": true,
|
| 2857 |
"license": "MIT"
|
| 2858 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2859 |
"node_modules/fdir": {
|
| 2860 |
"version": "6.5.0",
|
| 2861 |
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
|
@@ -2931,6 +2963,14 @@
|
|
| 2931 |
"dev": true,
|
| 2932 |
"license": "ISC"
|
| 2933 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2934 |
"node_modules/fsevents": {
|
| 2935 |
"version": "2.3.3",
|
| 2936 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
@@ -3013,6 +3053,19 @@
|
|
| 3013 |
"node": ">= 0.4"
|
| 3014 |
}
|
| 3015 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3016 |
"node_modules/hast-util-to-jsx-runtime": {
|
| 3017 |
"version": "2.3.6",
|
| 3018 |
"resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
|
|
@@ -3053,6 +3106,38 @@
|
|
| 3053 |
"url": "https://opencollective.com/unified"
|
| 3054 |
}
|
| 3055 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3056 |
"node_modules/hoist-non-react-statics": {
|
| 3057 |
"version": "3.3.2",
|
| 3058 |
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
|
@@ -3373,6 +3458,20 @@
|
|
| 3373 |
"loose-envify": "cli.js"
|
| 3374 |
}
|
| 3375 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3376 |
"node_modules/lru-cache": {
|
| 3377 |
"version": "5.1.1",
|
| 3378 |
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
|
@@ -3383,6 +3482,44 @@
|
|
| 3383 |
"yallist": "^3.0.2"
|
| 3384 |
}
|
| 3385 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3386 |
"node_modules/mdast-util-from-markdown": {
|
| 3387 |
"version": "2.0.2",
|
| 3388 |
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
|
|
@@ -3407,6 +3544,107 @@
|
|
| 3407 |
"url": "https://opencollective.com/unified"
|
| 3408 |
}
|
| 3409 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3410 |
"node_modules/mdast-util-mdx-expression": {
|
| 3411 |
"version": "2.0.1",
|
| 3412 |
"resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
|
|
@@ -3605,6 +3843,127 @@
|
|
| 3605 |
"micromark-util-types": "^2.0.0"
|
| 3606 |
}
|
| 3607 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3608 |
"node_modules/micromark-factory-destination": {
|
| 3609 |
"version": "2.0.1",
|
| 3610 |
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
|
|
@@ -4238,6 +4597,15 @@
|
|
| 4238 |
"node": ">= 0.8.0"
|
| 4239 |
}
|
| 4240 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4241 |
"node_modules/prop-types": {
|
| 4242 |
"version": "15.8.1",
|
| 4243 |
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
@@ -4345,6 +4713,26 @@
|
|
| 4345 |
"node": ">=0.10.0"
|
| 4346 |
}
|
| 4347 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4348 |
"node_modules/react-transition-group": {
|
| 4349 |
"version": "4.4.5",
|
| 4350 |
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
|
@@ -4361,6 +4749,40 @@
|
|
| 4361 |
"react-dom": ">=16.6.0"
|
| 4362 |
}
|
| 4363 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4364 |
"node_modules/remark-parse": {
|
| 4365 |
"version": "11.0.0",
|
| 4366 |
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
|
|
@@ -4394,6 +4816,21 @@
|
|
| 4394 |
"url": "https://opencollective.com/unified"
|
| 4395 |
}
|
| 4396 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4397 |
"node_modules/resolve": {
|
| 4398 |
"version": "1.22.11",
|
| 4399 |
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
|
|
|
| 15 |
"react": "^18.3.1",
|
| 16 |
"react-dom": "^18.3.1",
|
| 17 |
"react-markdown": "^9.0.1",
|
| 18 |
+
"react-syntax-highlighter": "^16.1.0",
|
| 19 |
+
"remark-gfm": "^4.0.1",
|
| 20 |
"zustand": "^5.0.0"
|
| 21 |
},
|
| 22 |
"devDependencies": {
|
| 23 |
"@eslint/js": "^9.13.0",
|
| 24 |
"@types/react": "^18.3.12",
|
| 25 |
"@types/react-dom": "^18.3.1",
|
| 26 |
+
"@types/react-syntax-highlighter": "^15.5.13",
|
| 27 |
"@vitejs/plugin-react": "^4.3.3",
|
| 28 |
"eslint": "^9.13.0",
|
| 29 |
"eslint-plugin-react-hooks": "^5.0.0",
|
|
|
|
| 1821 |
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
| 1822 |
"license": "MIT"
|
| 1823 |
},
|
| 1824 |
+
"node_modules/@types/prismjs": {
|
| 1825 |
+
"version": "1.26.5",
|
| 1826 |
+
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz",
|
| 1827 |
+
"integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==",
|
| 1828 |
+
"license": "MIT"
|
| 1829 |
+
},
|
| 1830 |
"node_modules/@types/prop-types": {
|
| 1831 |
"version": "15.7.15",
|
| 1832 |
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
|
|
|
| 1854 |
"@types/react": "^18.0.0"
|
| 1855 |
}
|
| 1856 |
},
|
| 1857 |
+
"node_modules/@types/react-syntax-highlighter": {
|
| 1858 |
+
"version": "15.5.13",
|
| 1859 |
+
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz",
|
| 1860 |
+
"integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==",
|
| 1861 |
+
"dev": true,
|
| 1862 |
+
"license": "MIT",
|
| 1863 |
+
"dependencies": {
|
| 1864 |
+
"@types/react": "*"
|
| 1865 |
+
}
|
| 1866 |
+
},
|
| 1867 |
"node_modules/@types/react-transition-group": {
|
| 1868 |
"version": "4.4.12",
|
| 1869 |
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
|
|
|
| 2875 |
"dev": true,
|
| 2876 |
"license": "MIT"
|
| 2877 |
},
|
| 2878 |
+
"node_modules/fault": {
|
| 2879 |
+
"version": "1.0.4",
|
| 2880 |
+
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
|
| 2881 |
+
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
|
| 2882 |
+
"license": "MIT",
|
| 2883 |
+
"dependencies": {
|
| 2884 |
+
"format": "^0.2.0"
|
| 2885 |
+
},
|
| 2886 |
+
"funding": {
|
| 2887 |
+
"type": "github",
|
| 2888 |
+
"url": "https://github.com/sponsors/wooorm"
|
| 2889 |
+
}
|
| 2890 |
+
},
|
| 2891 |
"node_modules/fdir": {
|
| 2892 |
"version": "6.5.0",
|
| 2893 |
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
|
|
|
| 2963 |
"dev": true,
|
| 2964 |
"license": "ISC"
|
| 2965 |
},
|
| 2966 |
+
"node_modules/format": {
|
| 2967 |
+
"version": "0.2.2",
|
| 2968 |
+
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
|
| 2969 |
+
"integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
|
| 2970 |
+
"engines": {
|
| 2971 |
+
"node": ">=0.4.x"
|
| 2972 |
+
}
|
| 2973 |
+
},
|
| 2974 |
"node_modules/fsevents": {
|
| 2975 |
"version": "2.3.3",
|
| 2976 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
|
|
| 3053 |
"node": ">= 0.4"
|
| 3054 |
}
|
| 3055 |
},
|
| 3056 |
+
"node_modules/hast-util-parse-selector": {
|
| 3057 |
+
"version": "4.0.0",
|
| 3058 |
+
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
|
| 3059 |
+
"integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
|
| 3060 |
+
"license": "MIT",
|
| 3061 |
+
"dependencies": {
|
| 3062 |
+
"@types/hast": "^3.0.0"
|
| 3063 |
+
},
|
| 3064 |
+
"funding": {
|
| 3065 |
+
"type": "opencollective",
|
| 3066 |
+
"url": "https://opencollective.com/unified"
|
| 3067 |
+
}
|
| 3068 |
+
},
|
| 3069 |
"node_modules/hast-util-to-jsx-runtime": {
|
| 3070 |
"version": "2.3.6",
|
| 3071 |
"resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
|
|
|
|
| 3106 |
"url": "https://opencollective.com/unified"
|
| 3107 |
}
|
| 3108 |
},
|
| 3109 |
+
"node_modules/hastscript": {
|
| 3110 |
+
"version": "9.0.1",
|
| 3111 |
+
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
|
| 3112 |
+
"integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
|
| 3113 |
+
"license": "MIT",
|
| 3114 |
+
"dependencies": {
|
| 3115 |
+
"@types/hast": "^3.0.0",
|
| 3116 |
+
"comma-separated-tokens": "^2.0.0",
|
| 3117 |
+
"hast-util-parse-selector": "^4.0.0",
|
| 3118 |
+
"property-information": "^7.0.0",
|
| 3119 |
+
"space-separated-tokens": "^2.0.0"
|
| 3120 |
+
},
|
| 3121 |
+
"funding": {
|
| 3122 |
+
"type": "opencollective",
|
| 3123 |
+
"url": "https://opencollective.com/unified"
|
| 3124 |
+
}
|
| 3125 |
+
},
|
| 3126 |
+
"node_modules/highlight.js": {
|
| 3127 |
+
"version": "10.7.3",
|
| 3128 |
+
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
| 3129 |
+
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
|
| 3130 |
+
"license": "BSD-3-Clause",
|
| 3131 |
+
"engines": {
|
| 3132 |
+
"node": "*"
|
| 3133 |
+
}
|
| 3134 |
+
},
|
| 3135 |
+
"node_modules/highlightjs-vue": {
|
| 3136 |
+
"version": "1.0.0",
|
| 3137 |
+
"resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz",
|
| 3138 |
+
"integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==",
|
| 3139 |
+
"license": "CC0-1.0"
|
| 3140 |
+
},
|
| 3141 |
"node_modules/hoist-non-react-statics": {
|
| 3142 |
"version": "3.3.2",
|
| 3143 |
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
|
|
|
| 3458 |
"loose-envify": "cli.js"
|
| 3459 |
}
|
| 3460 |
},
|
| 3461 |
+
"node_modules/lowlight": {
|
| 3462 |
+
"version": "1.20.0",
|
| 3463 |
+
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
|
| 3464 |
+
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
|
| 3465 |
+
"license": "MIT",
|
| 3466 |
+
"dependencies": {
|
| 3467 |
+
"fault": "^1.0.0",
|
| 3468 |
+
"highlight.js": "~10.7.0"
|
| 3469 |
+
},
|
| 3470 |
+
"funding": {
|
| 3471 |
+
"type": "github",
|
| 3472 |
+
"url": "https://github.com/sponsors/wooorm"
|
| 3473 |
+
}
|
| 3474 |
+
},
|
| 3475 |
"node_modules/lru-cache": {
|
| 3476 |
"version": "5.1.1",
|
| 3477 |
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
|
|
|
| 3482 |
"yallist": "^3.0.2"
|
| 3483 |
}
|
| 3484 |
},
|
| 3485 |
+
"node_modules/markdown-table": {
|
| 3486 |
+
"version": "3.0.4",
|
| 3487 |
+
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
|
| 3488 |
+
"integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
|
| 3489 |
+
"license": "MIT",
|
| 3490 |
+
"funding": {
|
| 3491 |
+
"type": "github",
|
| 3492 |
+
"url": "https://github.com/sponsors/wooorm"
|
| 3493 |
+
}
|
| 3494 |
+
},
|
| 3495 |
+
"node_modules/mdast-util-find-and-replace": {
|
| 3496 |
+
"version": "3.0.2",
|
| 3497 |
+
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
|
| 3498 |
+
"integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
|
| 3499 |
+
"license": "MIT",
|
| 3500 |
+
"dependencies": {
|
| 3501 |
+
"@types/mdast": "^4.0.0",
|
| 3502 |
+
"escape-string-regexp": "^5.0.0",
|
| 3503 |
+
"unist-util-is": "^6.0.0",
|
| 3504 |
+
"unist-util-visit-parents": "^6.0.0"
|
| 3505 |
+
},
|
| 3506 |
+
"funding": {
|
| 3507 |
+
"type": "opencollective",
|
| 3508 |
+
"url": "https://opencollective.com/unified"
|
| 3509 |
+
}
|
| 3510 |
+
},
|
| 3511 |
+
"node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
|
| 3512 |
+
"version": "5.0.0",
|
| 3513 |
+
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
| 3514 |
+
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
| 3515 |
+
"license": "MIT",
|
| 3516 |
+
"engines": {
|
| 3517 |
+
"node": ">=12"
|
| 3518 |
+
},
|
| 3519 |
+
"funding": {
|
| 3520 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 3521 |
+
}
|
| 3522 |
+
},
|
| 3523 |
"node_modules/mdast-util-from-markdown": {
|
| 3524 |
"version": "2.0.2",
|
| 3525 |
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
|
|
|
|
| 3544 |
"url": "https://opencollective.com/unified"
|
| 3545 |
}
|
| 3546 |
},
|
| 3547 |
+
"node_modules/mdast-util-gfm": {
|
| 3548 |
+
"version": "3.1.0",
|
| 3549 |
+
"resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
|
| 3550 |
+
"integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
|
| 3551 |
+
"license": "MIT",
|
| 3552 |
+
"dependencies": {
|
| 3553 |
+
"mdast-util-from-markdown": "^2.0.0",
|
| 3554 |
+
"mdast-util-gfm-autolink-literal": "^2.0.0",
|
| 3555 |
+
"mdast-util-gfm-footnote": "^2.0.0",
|
| 3556 |
+
"mdast-util-gfm-strikethrough": "^2.0.0",
|
| 3557 |
+
"mdast-util-gfm-table": "^2.0.0",
|
| 3558 |
+
"mdast-util-gfm-task-list-item": "^2.0.0",
|
| 3559 |
+
"mdast-util-to-markdown": "^2.0.0"
|
| 3560 |
+
},
|
| 3561 |
+
"funding": {
|
| 3562 |
+
"type": "opencollective",
|
| 3563 |
+
"url": "https://opencollective.com/unified"
|
| 3564 |
+
}
|
| 3565 |
+
},
|
| 3566 |
+
"node_modules/mdast-util-gfm-autolink-literal": {
|
| 3567 |
+
"version": "2.0.1",
|
| 3568 |
+
"resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
|
| 3569 |
+
"integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
|
| 3570 |
+
"license": "MIT",
|
| 3571 |
+
"dependencies": {
|
| 3572 |
+
"@types/mdast": "^4.0.0",
|
| 3573 |
+
"ccount": "^2.0.0",
|
| 3574 |
+
"devlop": "^1.0.0",
|
| 3575 |
+
"mdast-util-find-and-replace": "^3.0.0",
|
| 3576 |
+
"micromark-util-character": "^2.0.0"
|
| 3577 |
+
},
|
| 3578 |
+
"funding": {
|
| 3579 |
+
"type": "opencollective",
|
| 3580 |
+
"url": "https://opencollective.com/unified"
|
| 3581 |
+
}
|
| 3582 |
+
},
|
| 3583 |
+
"node_modules/mdast-util-gfm-footnote": {
|
| 3584 |
+
"version": "2.1.0",
|
| 3585 |
+
"resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
|
| 3586 |
+
"integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
|
| 3587 |
+
"license": "MIT",
|
| 3588 |
+
"dependencies": {
|
| 3589 |
+
"@types/mdast": "^4.0.0",
|
| 3590 |
+
"devlop": "^1.1.0",
|
| 3591 |
+
"mdast-util-from-markdown": "^2.0.0",
|
| 3592 |
+
"mdast-util-to-markdown": "^2.0.0",
|
| 3593 |
+
"micromark-util-normalize-identifier": "^2.0.0"
|
| 3594 |
+
},
|
| 3595 |
+
"funding": {
|
| 3596 |
+
"type": "opencollective",
|
| 3597 |
+
"url": "https://opencollective.com/unified"
|
| 3598 |
+
}
|
| 3599 |
+
},
|
| 3600 |
+
"node_modules/mdast-util-gfm-strikethrough": {
|
| 3601 |
+
"version": "2.0.0",
|
| 3602 |
+
"resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
|
| 3603 |
+
"integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
|
| 3604 |
+
"license": "MIT",
|
| 3605 |
+
"dependencies": {
|
| 3606 |
+
"@types/mdast": "^4.0.0",
|
| 3607 |
+
"mdast-util-from-markdown": "^2.0.0",
|
| 3608 |
+
"mdast-util-to-markdown": "^2.0.0"
|
| 3609 |
+
},
|
| 3610 |
+
"funding": {
|
| 3611 |
+
"type": "opencollective",
|
| 3612 |
+
"url": "https://opencollective.com/unified"
|
| 3613 |
+
}
|
| 3614 |
+
},
|
| 3615 |
+
"node_modules/mdast-util-gfm-table": {
|
| 3616 |
+
"version": "2.0.0",
|
| 3617 |
+
"resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
|
| 3618 |
+
"integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
|
| 3619 |
+
"license": "MIT",
|
| 3620 |
+
"dependencies": {
|
| 3621 |
+
"@types/mdast": "^4.0.0",
|
| 3622 |
+
"devlop": "^1.0.0",
|
| 3623 |
+
"markdown-table": "^3.0.0",
|
| 3624 |
+
"mdast-util-from-markdown": "^2.0.0",
|
| 3625 |
+
"mdast-util-to-markdown": "^2.0.0"
|
| 3626 |
+
},
|
| 3627 |
+
"funding": {
|
| 3628 |
+
"type": "opencollective",
|
| 3629 |
+
"url": "https://opencollective.com/unified"
|
| 3630 |
+
}
|
| 3631 |
+
},
|
| 3632 |
+
"node_modules/mdast-util-gfm-task-list-item": {
|
| 3633 |
+
"version": "2.0.0",
|
| 3634 |
+
"resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
|
| 3635 |
+
"integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
|
| 3636 |
+
"license": "MIT",
|
| 3637 |
+
"dependencies": {
|
| 3638 |
+
"@types/mdast": "^4.0.0",
|
| 3639 |
+
"devlop": "^1.0.0",
|
| 3640 |
+
"mdast-util-from-markdown": "^2.0.0",
|
| 3641 |
+
"mdast-util-to-markdown": "^2.0.0"
|
| 3642 |
+
},
|
| 3643 |
+
"funding": {
|
| 3644 |
+
"type": "opencollective",
|
| 3645 |
+
"url": "https://opencollective.com/unified"
|
| 3646 |
+
}
|
| 3647 |
+
},
|
| 3648 |
"node_modules/mdast-util-mdx-expression": {
|
| 3649 |
"version": "2.0.1",
|
| 3650 |
"resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
|
|
|
|
| 3843 |
"micromark-util-types": "^2.0.0"
|
| 3844 |
}
|
| 3845 |
},
|
| 3846 |
+
"node_modules/micromark-extension-gfm": {
|
| 3847 |
+
"version": "3.0.0",
|
| 3848 |
+
"resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
|
| 3849 |
+
"integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
|
| 3850 |
+
"license": "MIT",
|
| 3851 |
+
"dependencies": {
|
| 3852 |
+
"micromark-extension-gfm-autolink-literal": "^2.0.0",
|
| 3853 |
+
"micromark-extension-gfm-footnote": "^2.0.0",
|
| 3854 |
+
"micromark-extension-gfm-strikethrough": "^2.0.0",
|
| 3855 |
+
"micromark-extension-gfm-table": "^2.0.0",
|
| 3856 |
+
"micromark-extension-gfm-tagfilter": "^2.0.0",
|
| 3857 |
+
"micromark-extension-gfm-task-list-item": "^2.0.0",
|
| 3858 |
+
"micromark-util-combine-extensions": "^2.0.0",
|
| 3859 |
+
"micromark-util-types": "^2.0.0"
|
| 3860 |
+
},
|
| 3861 |
+
"funding": {
|
| 3862 |
+
"type": "opencollective",
|
| 3863 |
+
"url": "https://opencollective.com/unified"
|
| 3864 |
+
}
|
| 3865 |
+
},
|
| 3866 |
+
"node_modules/micromark-extension-gfm-autolink-literal": {
|
| 3867 |
+
"version": "2.1.0",
|
| 3868 |
+
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
|
| 3869 |
+
"integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
|
| 3870 |
+
"license": "MIT",
|
| 3871 |
+
"dependencies": {
|
| 3872 |
+
"micromark-util-character": "^2.0.0",
|
| 3873 |
+
"micromark-util-sanitize-uri": "^2.0.0",
|
| 3874 |
+
"micromark-util-symbol": "^2.0.0",
|
| 3875 |
+
"micromark-util-types": "^2.0.0"
|
| 3876 |
+
},
|
| 3877 |
+
"funding": {
|
| 3878 |
+
"type": "opencollective",
|
| 3879 |
+
"url": "https://opencollective.com/unified"
|
| 3880 |
+
}
|
| 3881 |
+
},
|
| 3882 |
+
"node_modules/micromark-extension-gfm-footnote": {
|
| 3883 |
+
"version": "2.1.0",
|
| 3884 |
+
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
|
| 3885 |
+
"integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
|
| 3886 |
+
"license": "MIT",
|
| 3887 |
+
"dependencies": {
|
| 3888 |
+
"devlop": "^1.0.0",
|
| 3889 |
+
"micromark-core-commonmark": "^2.0.0",
|
| 3890 |
+
"micromark-factory-space": "^2.0.0",
|
| 3891 |
+
"micromark-util-character": "^2.0.0",
|
| 3892 |
+
"micromark-util-normalize-identifier": "^2.0.0",
|
| 3893 |
+
"micromark-util-sanitize-uri": "^2.0.0",
|
| 3894 |
+
"micromark-util-symbol": "^2.0.0",
|
| 3895 |
+
"micromark-util-types": "^2.0.0"
|
| 3896 |
+
},
|
| 3897 |
+
"funding": {
|
| 3898 |
+
"type": "opencollective",
|
| 3899 |
+
"url": "https://opencollective.com/unified"
|
| 3900 |
+
}
|
| 3901 |
+
},
|
| 3902 |
+
"node_modules/micromark-extension-gfm-strikethrough": {
|
| 3903 |
+
"version": "2.1.0",
|
| 3904 |
+
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
|
| 3905 |
+
"integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
|
| 3906 |
+
"license": "MIT",
|
| 3907 |
+
"dependencies": {
|
| 3908 |
+
"devlop": "^1.0.0",
|
| 3909 |
+
"micromark-util-chunked": "^2.0.0",
|
| 3910 |
+
"micromark-util-classify-character": "^2.0.0",
|
| 3911 |
+
"micromark-util-resolve-all": "^2.0.0",
|
| 3912 |
+
"micromark-util-symbol": "^2.0.0",
|
| 3913 |
+
"micromark-util-types": "^2.0.0"
|
| 3914 |
+
},
|
| 3915 |
+
"funding": {
|
| 3916 |
+
"type": "opencollective",
|
| 3917 |
+
"url": "https://opencollective.com/unified"
|
| 3918 |
+
}
|
| 3919 |
+
},
|
| 3920 |
+
"node_modules/micromark-extension-gfm-table": {
|
| 3921 |
+
"version": "2.1.1",
|
| 3922 |
+
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
|
| 3923 |
+
"integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
|
| 3924 |
+
"license": "MIT",
|
| 3925 |
+
"dependencies": {
|
| 3926 |
+
"devlop": "^1.0.0",
|
| 3927 |
+
"micromark-factory-space": "^2.0.0",
|
| 3928 |
+
"micromark-util-character": "^2.0.0",
|
| 3929 |
+
"micromark-util-symbol": "^2.0.0",
|
| 3930 |
+
"micromark-util-types": "^2.0.0"
|
| 3931 |
+
},
|
| 3932 |
+
"funding": {
|
| 3933 |
+
"type": "opencollective",
|
| 3934 |
+
"url": "https://opencollective.com/unified"
|
| 3935 |
+
}
|
| 3936 |
+
},
|
| 3937 |
+
"node_modules/micromark-extension-gfm-tagfilter": {
|
| 3938 |
+
"version": "2.0.0",
|
| 3939 |
+
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
|
| 3940 |
+
"integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
|
| 3941 |
+
"license": "MIT",
|
| 3942 |
+
"dependencies": {
|
| 3943 |
+
"micromark-util-types": "^2.0.0"
|
| 3944 |
+
},
|
| 3945 |
+
"funding": {
|
| 3946 |
+
"type": "opencollective",
|
| 3947 |
+
"url": "https://opencollective.com/unified"
|
| 3948 |
+
}
|
| 3949 |
+
},
|
| 3950 |
+
"node_modules/micromark-extension-gfm-task-list-item": {
|
| 3951 |
+
"version": "2.1.0",
|
| 3952 |
+
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
|
| 3953 |
+
"integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
|
| 3954 |
+
"license": "MIT",
|
| 3955 |
+
"dependencies": {
|
| 3956 |
+
"devlop": "^1.0.0",
|
| 3957 |
+
"micromark-factory-space": "^2.0.0",
|
| 3958 |
+
"micromark-util-character": "^2.0.0",
|
| 3959 |
+
"micromark-util-symbol": "^2.0.0",
|
| 3960 |
+
"micromark-util-types": "^2.0.0"
|
| 3961 |
+
},
|
| 3962 |
+
"funding": {
|
| 3963 |
+
"type": "opencollective",
|
| 3964 |
+
"url": "https://opencollective.com/unified"
|
| 3965 |
+
}
|
| 3966 |
+
},
|
| 3967 |
"node_modules/micromark-factory-destination": {
|
| 3968 |
"version": "2.0.1",
|
| 3969 |
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
|
|
|
|
| 4597 |
"node": ">= 0.8.0"
|
| 4598 |
}
|
| 4599 |
},
|
| 4600 |
+
"node_modules/prismjs": {
|
| 4601 |
+
"version": "1.30.0",
|
| 4602 |
+
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
| 4603 |
+
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
|
| 4604 |
+
"license": "MIT",
|
| 4605 |
+
"engines": {
|
| 4606 |
+
"node": ">=6"
|
| 4607 |
+
}
|
| 4608 |
+
},
|
| 4609 |
"node_modules/prop-types": {
|
| 4610 |
"version": "15.8.1",
|
| 4611 |
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
|
|
| 4713 |
"node": ">=0.10.0"
|
| 4714 |
}
|
| 4715 |
},
|
| 4716 |
+
"node_modules/react-syntax-highlighter": {
|
| 4717 |
+
"version": "16.1.0",
|
| 4718 |
+
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz",
|
| 4719 |
+
"integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==",
|
| 4720 |
+
"license": "MIT",
|
| 4721 |
+
"dependencies": {
|
| 4722 |
+
"@babel/runtime": "^7.28.4",
|
| 4723 |
+
"highlight.js": "^10.4.1",
|
| 4724 |
+
"highlightjs-vue": "^1.0.0",
|
| 4725 |
+
"lowlight": "^1.17.0",
|
| 4726 |
+
"prismjs": "^1.30.0",
|
| 4727 |
+
"refractor": "^5.0.0"
|
| 4728 |
+
},
|
| 4729 |
+
"engines": {
|
| 4730 |
+
"node": ">= 16.20.2"
|
| 4731 |
+
},
|
| 4732 |
+
"peerDependencies": {
|
| 4733 |
+
"react": ">= 0.14.0"
|
| 4734 |
+
}
|
| 4735 |
+
},
|
| 4736 |
"node_modules/react-transition-group": {
|
| 4737 |
"version": "4.4.5",
|
| 4738 |
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
|
|
|
| 4749 |
"react-dom": ">=16.6.0"
|
| 4750 |
}
|
| 4751 |
},
|
| 4752 |
+
"node_modules/refractor": {
|
| 4753 |
+
"version": "5.0.0",
|
| 4754 |
+
"resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz",
|
| 4755 |
+
"integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==",
|
| 4756 |
+
"license": "MIT",
|
| 4757 |
+
"dependencies": {
|
| 4758 |
+
"@types/hast": "^3.0.0",
|
| 4759 |
+
"@types/prismjs": "^1.0.0",
|
| 4760 |
+
"hastscript": "^9.0.0",
|
| 4761 |
+
"parse-entities": "^4.0.0"
|
| 4762 |
+
},
|
| 4763 |
+
"funding": {
|
| 4764 |
+
"type": "github",
|
| 4765 |
+
"url": "https://github.com/sponsors/wooorm"
|
| 4766 |
+
}
|
| 4767 |
+
},
|
| 4768 |
+
"node_modules/remark-gfm": {
|
| 4769 |
+
"version": "4.0.1",
|
| 4770 |
+
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
|
| 4771 |
+
"integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
|
| 4772 |
+
"license": "MIT",
|
| 4773 |
+
"dependencies": {
|
| 4774 |
+
"@types/mdast": "^4.0.0",
|
| 4775 |
+
"mdast-util-gfm": "^3.0.0",
|
| 4776 |
+
"micromark-extension-gfm": "^3.0.0",
|
| 4777 |
+
"remark-parse": "^11.0.0",
|
| 4778 |
+
"remark-stringify": "^11.0.0",
|
| 4779 |
+
"unified": "^11.0.0"
|
| 4780 |
+
},
|
| 4781 |
+
"funding": {
|
| 4782 |
+
"type": "opencollective",
|
| 4783 |
+
"url": "https://opencollective.com/unified"
|
| 4784 |
+
}
|
| 4785 |
+
},
|
| 4786 |
"node_modules/remark-parse": {
|
| 4787 |
"version": "11.0.0",
|
| 4788 |
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
|
|
|
|
| 4816 |
"url": "https://opencollective.com/unified"
|
| 4817 |
}
|
| 4818 |
},
|
| 4819 |
+
"node_modules/remark-stringify": {
|
| 4820 |
+
"version": "11.0.0",
|
| 4821 |
+
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
|
| 4822 |
+
"integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
|
| 4823 |
+
"license": "MIT",
|
| 4824 |
+
"dependencies": {
|
| 4825 |
+
"@types/mdast": "^4.0.0",
|
| 4826 |
+
"mdast-util-to-markdown": "^2.0.0",
|
| 4827 |
+
"unified": "^11.0.0"
|
| 4828 |
+
},
|
| 4829 |
+
"funding": {
|
| 4830 |
+
"type": "opencollective",
|
| 4831 |
+
"url": "https://opencollective.com/unified"
|
| 4832 |
+
}
|
| 4833 |
+
},
|
| 4834 |
"node_modules/resolve": {
|
| 4835 |
"version": "1.22.11",
|
| 4836 |
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
frontend/package.json
CHANGED
|
@@ -17,12 +17,15 @@
|
|
| 17 |
"react": "^18.3.1",
|
| 18 |
"react-dom": "^18.3.1",
|
| 19 |
"react-markdown": "^9.0.1",
|
|
|
|
|
|
|
| 20 |
"zustand": "^5.0.0"
|
| 21 |
},
|
| 22 |
"devDependencies": {
|
| 23 |
"@eslint/js": "^9.13.0",
|
| 24 |
"@types/react": "^18.3.12",
|
| 25 |
"@types/react-dom": "^18.3.1",
|
|
|
|
| 26 |
"@vitejs/plugin-react": "^4.3.3",
|
| 27 |
"eslint": "^9.13.0",
|
| 28 |
"eslint-plugin-react-hooks": "^5.0.0",
|
|
|
|
| 17 |
"react": "^18.3.1",
|
| 18 |
"react-dom": "^18.3.1",
|
| 19 |
"react-markdown": "^9.0.1",
|
| 20 |
+
"react-syntax-highlighter": "^16.1.0",
|
| 21 |
+
"remark-gfm": "^4.0.1",
|
| 22 |
"zustand": "^5.0.0"
|
| 23 |
},
|
| 24 |
"devDependencies": {
|
| 25 |
"@eslint/js": "^9.13.0",
|
| 26 |
"@types/react": "^18.3.12",
|
| 27 |
"@types/react-dom": "^18.3.1",
|
| 28 |
+
"@types/react-syntax-highlighter": "^15.5.13",
|
| 29 |
"@vitejs/plugin-react": "^4.3.3",
|
| 30 |
"eslint": "^9.13.0",
|
| 31 |
"eslint-plugin-react-hooks": "^5.0.0",
|
frontend/src/components/Chat/ApprovalFlow.tsx
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState, useCallback, useEffect } from 'react';
|
| 2 |
+
import { Box, Typography, Button, TextField, Divider } from '@mui/material';
|
| 3 |
+
import { useAgentStore } from '@/store/agentStore';
|
| 4 |
+
import { useLayoutStore } from '@/store/layoutStore';
|
| 5 |
+
|
| 6 |
+
interface ApprovalFlowProps {
|
| 7 |
+
sessionId: string;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
export default function ApprovalFlow({ sessionId }: ApprovalFlowProps) {
|
| 11 |
+
const { pendingApprovals, setPendingApprovals, setPanelContent } = useAgentStore();
|
| 12 |
+
const { setRightPanelOpen, setLeftSidebarOpen } = useLayoutStore();
|
| 13 |
+
const [currentIndex, setCurrentIndex] = useState(0);
|
| 14 |
+
const [feedback, setFeedback] = useState('');
|
| 15 |
+
const [decisions, setDecisions] = useState<Array<{ tool_call_id: string; approved: boolean; feedback: string | null }>>([]);
|
| 16 |
+
|
| 17 |
+
// Reset local state when a new batch of approvals arrives
|
| 18 |
+
useEffect(() => {
|
| 19 |
+
setCurrentIndex(0);
|
| 20 |
+
setFeedback('');
|
| 21 |
+
setDecisions([]);
|
| 22 |
+
}, [pendingApprovals]);
|
| 23 |
+
|
| 24 |
+
// Sync right panel with current tool
|
| 25 |
+
useEffect(() => {
|
| 26 |
+
if (!pendingApprovals || currentIndex >= pendingApprovals.tools.length) return;
|
| 27 |
+
|
| 28 |
+
const tool = pendingApprovals.tools[currentIndex];
|
| 29 |
+
const args = tool.arguments as any;
|
| 30 |
+
|
| 31 |
+
if (tool.tool === 'hf_jobs' && (args.operation === 'run' || args.operation === 'scheduled run') && args.script) {
|
| 32 |
+
setPanelContent({
|
| 33 |
+
title: 'Compute Job Script',
|
| 34 |
+
content: args.script,
|
| 35 |
+
language: 'python',
|
| 36 |
+
parameters: args
|
| 37 |
+
});
|
| 38 |
+
setRightPanelOpen(true);
|
| 39 |
+
setLeftSidebarOpen(false);
|
| 40 |
+
} else if (tool.tool === 'hf_repo_files' && args.operation === 'upload' && args.content) {
|
| 41 |
+
setPanelContent({
|
| 42 |
+
title: `File Upload: ${args.path || 'unnamed'}`,
|
| 43 |
+
content: args.content,
|
| 44 |
+
parameters: args
|
| 45 |
+
});
|
| 46 |
+
setRightPanelOpen(true);
|
| 47 |
+
setLeftSidebarOpen(false);
|
| 48 |
+
} else {
|
| 49 |
+
// For other tools, just show parameters in the panel
|
| 50 |
+
setPanelContent({
|
| 51 |
+
title: `Tool: ${tool.tool}`,
|
| 52 |
+
content: '',
|
| 53 |
+
parameters: args
|
| 54 |
+
});
|
| 55 |
+
}
|
| 56 |
+
}, [currentIndex, pendingApprovals, setPanelContent, setRightPanelOpen, setLeftSidebarOpen]);
|
| 57 |
+
|
| 58 |
+
const handleResolve = useCallback(async (approved: boolean) => {
|
| 59 |
+
if (!pendingApprovals) return;
|
| 60 |
+
|
| 61 |
+
const currentTool = pendingApprovals.tools[currentIndex];
|
| 62 |
+
const newDecisions = [
|
| 63 |
+
...decisions,
|
| 64 |
+
{
|
| 65 |
+
tool_call_id: currentTool.tool_call_id,
|
| 66 |
+
approved,
|
| 67 |
+
feedback: approved ? null : feedback || 'Rejected by user',
|
| 68 |
+
},
|
| 69 |
+
];
|
| 70 |
+
|
| 71 |
+
if (currentIndex < pendingApprovals.tools.length - 1) {
|
| 72 |
+
setDecisions(newDecisions);
|
| 73 |
+
setCurrentIndex(currentIndex + 1);
|
| 74 |
+
setFeedback('');
|
| 75 |
+
} else {
|
| 76 |
+
// All tools in batch resolved, submit to backend
|
| 77 |
+
try {
|
| 78 |
+
await fetch('/api/approve', {
|
| 79 |
+
method: 'POST',
|
| 80 |
+
headers: { 'Content-Type': 'application/json' },
|
| 81 |
+
body: JSON.stringify({
|
| 82 |
+
session_id: sessionId,
|
| 83 |
+
approvals: newDecisions,
|
| 84 |
+
}),
|
| 85 |
+
});
|
| 86 |
+
setPendingApprovals(null);
|
| 87 |
+
} catch (e) {
|
| 88 |
+
console.error('Approval submission failed:', e);
|
| 89 |
+
}
|
| 90 |
+
}
|
| 91 |
+
}, [sessionId, pendingApprovals, currentIndex, feedback, decisions, setPendingApprovals]);
|
| 92 |
+
|
| 93 |
+
if (!pendingApprovals || currentIndex >= pendingApprovals.tools.length) return null;
|
| 94 |
+
|
| 95 |
+
const currentTool = pendingApprovals.tools[currentIndex];
|
| 96 |
+
|
| 97 |
+
return (
|
| 98 |
+
<Box sx={{
|
| 99 |
+
mt: 0,
|
| 100 |
+
mb: 4,
|
| 101 |
+
px: 2,
|
| 102 |
+
width: '100%',
|
| 103 |
+
alignSelf: 'center'
|
| 104 |
+
}}>
|
| 105 |
+
<Typography variant="subtitle2" sx={{ fontFamily: 'monospace', mb: 2, fontWeight: 600 }}>
|
| 106 |
+
ACTION REQUIRED ({currentIndex + 1}/{pendingApprovals.count}) : The agent wants to execute <Box component="span" sx={{ color: 'primary.main' }}>{currentTool.tool}</Box>
|
| 107 |
+
</Typography>
|
| 108 |
+
|
| 109 |
+
<Box component="pre" sx={{
|
| 110 |
+
bgcolor: 'background.default',
|
| 111 |
+
p: 1.5,
|
| 112 |
+
borderRadius: 0.5,
|
| 113 |
+
fontSize: '0.75rem',
|
| 114 |
+
fontFamily: 'monospace',
|
| 115 |
+
overflow: 'auto',
|
| 116 |
+
maxHeight: 150,
|
| 117 |
+
mb: 2,
|
| 118 |
+
border: 1,
|
| 119 |
+
borderColor: 'divider'
|
| 120 |
+
}}>
|
| 121 |
+
{JSON.stringify(currentTool.arguments, null, 2)}
|
| 122 |
+
</Box>
|
| 123 |
+
|
| 124 |
+
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
| 125 |
+
<TextField
|
| 126 |
+
fullWidth
|
| 127 |
+
size="small"
|
| 128 |
+
placeholder="Feedback for rejection (optional)"
|
| 129 |
+
value={feedback}
|
| 130 |
+
onChange={(e) => setFeedback(e.target.value)}
|
| 131 |
+
variant="outlined"
|
| 132 |
+
sx={{
|
| 133 |
+
flex: 1,
|
| 134 |
+
'& .MuiOutlinedInput-root': { fontFamily: 'monospace', fontSize: '0.8rem', height: '36px' }
|
| 135 |
+
}}
|
| 136 |
+
/>
|
| 137 |
+
|
| 138 |
+
<Button
|
| 139 |
+
variant="outlined"
|
| 140 |
+
color="error"
|
| 141 |
+
onClick={() => handleResolve(false)}
|
| 142 |
+
sx={{ fontFamily: 'monospace', height: '36px', px: 3 }}
|
| 143 |
+
>
|
| 144 |
+
REJECT
|
| 145 |
+
</Button>
|
| 146 |
+
<Button
|
| 147 |
+
variant="contained"
|
| 148 |
+
color="success"
|
| 149 |
+
onClick={() => handleResolve(true)}
|
| 150 |
+
sx={{ color: 'white', fontFamily: 'monospace', height: '36px', px: 3 }}
|
| 151 |
+
>
|
| 152 |
+
APPROVE
|
| 153 |
+
</Button>
|
| 154 |
+
</Box>
|
| 155 |
+
</Box>
|
| 156 |
+
);
|
| 157 |
+
}
|
frontend/src/components/Chat/ChatInput.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import { useState, useCallback, KeyboardEvent } from 'react';
|
| 2 |
-
import { Box, TextField, IconButton, CircularProgress } from '@mui/material';
|
| 3 |
-
import
|
| 4 |
|
| 5 |
interface ChatInputProps {
|
| 6 |
onSend: (text: string) => void;
|
|
@@ -30,47 +30,82 @@ export default function ChatInput({ onSend, disabled = false }: ChatInputProps)
|
|
| 30 |
return (
|
| 31 |
<Box
|
| 32 |
sx={{
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
}}
|
| 38 |
>
|
| 39 |
-
<Box sx={{
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
variant="outlined"
|
| 50 |
-
size="small"
|
| 51 |
-
sx={{
|
| 52 |
-
'& .MuiOutlinedInput-root': {
|
| 53 |
-
bgcolor: 'background.default',
|
| 54 |
-
},
|
| 55 |
-
}}
|
| 56 |
-
/>
|
| 57 |
-
<IconButton
|
| 58 |
-
onClick={handleSend}
|
| 59 |
-
disabled={disabled || !input.trim()}
|
| 60 |
-
color="primary"
|
| 61 |
sx={{
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
|
|
|
| 70 |
}}
|
| 71 |
>
|
| 72 |
-
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
</Box>
|
| 75 |
</Box>
|
| 76 |
);
|
|
|
|
| 1 |
import { useState, useCallback, KeyboardEvent } from 'react';
|
| 2 |
+
import { Box, TextField, IconButton, CircularProgress, Typography } from '@mui/material';
|
| 3 |
+
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
|
| 4 |
|
| 5 |
interface ChatInputProps {
|
| 6 |
onSend: (text: string) => void;
|
|
|
|
| 30 |
return (
|
| 31 |
<Box
|
| 32 |
sx={{
|
| 33 |
+
pb: 4,
|
| 34 |
+
pt: 2,
|
| 35 |
+
position: 'relative',
|
| 36 |
+
zIndex: 10,
|
| 37 |
}}
|
| 38 |
>
|
| 39 |
+
<Box sx={{ maxWidth: 'md', mx: 'auto', width: '100%', px: 2 }}>
|
| 40 |
+
{/* Input Label and Divider */}
|
| 41 |
+
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1, gap: 2 }}>
|
| 42 |
+
<Typography variant="caption" sx={{ fontFamily: 'monospace', fontWeight: 600, color: 'text.secondary', letterSpacing: '0.05em' }}>
|
| 43 |
+
INPUT
|
| 44 |
+
</Typography>
|
| 45 |
+
<Box sx={{ flex: 1, height: '1px', bgcolor: 'divider' }} />
|
| 46 |
+
</Box>
|
| 47 |
+
|
| 48 |
+
<Box
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
sx={{
|
| 50 |
+
display: 'flex',
|
| 51 |
+
gap: 1,
|
| 52 |
+
alignItems: 'center',
|
| 53 |
+
bgcolor: 'background.paper',
|
| 54 |
+
borderRadius: 1,
|
| 55 |
+
boxShadow: 4,
|
| 56 |
+
p: 1,
|
| 57 |
+
border: 1,
|
| 58 |
+
borderColor: 'divider',
|
| 59 |
}}
|
| 60 |
>
|
| 61 |
+
<TextField
|
| 62 |
+
fullWidth
|
| 63 |
+
multiline
|
| 64 |
+
maxRows={6}
|
| 65 |
+
value={input}
|
| 66 |
+
onChange={(e) => setInput(e.target.value)}
|
| 67 |
+
onKeyDown={handleKeyDown}
|
| 68 |
+
placeholder="Ask anything"
|
| 69 |
+
disabled={disabled}
|
| 70 |
+
variant="outlined"
|
| 71 |
+
size="small"
|
| 72 |
+
sx={{
|
| 73 |
+
'& .MuiOutlinedInput-root': {
|
| 74 |
+
padding: '9px 12px',
|
| 75 |
+
lineHeight: '1.4',
|
| 76 |
+
fontFamily: 'monospace',
|
| 77 |
+
},
|
| 78 |
+
'& .MuiInputBase-input': {
|
| 79 |
+
fontFamily: 'monospace',
|
| 80 |
+
},
|
| 81 |
+
'& .MuiOutlinedInput-notchedOutline': {
|
| 82 |
+
border: 'none',
|
| 83 |
+
},
|
| 84 |
+
}}
|
| 85 |
+
/>
|
| 86 |
+
<IconButton
|
| 87 |
+
onClick={handleSend}
|
| 88 |
+
disabled={disabled || !input.trim()}
|
| 89 |
+
sx={{
|
| 90 |
+
border: 1,
|
| 91 |
+
borderColor: 'divider',
|
| 92 |
+
borderRadius: 1,
|
| 93 |
+
color: 'text.secondary',
|
| 94 |
+
transition: 'all 0.2s',
|
| 95 |
+
'&:hover': {
|
| 96 |
+
borderColor: 'primary.main',
|
| 97 |
+
color: 'primary.main',
|
| 98 |
+
bgcolor: 'transparent',
|
| 99 |
+
},
|
| 100 |
+
'&.Mui-disabled': {
|
| 101 |
+
borderColor: 'divider',
|
| 102 |
+
opacity: 0.5,
|
| 103 |
+
},
|
| 104 |
+
}}
|
| 105 |
+
>
|
| 106 |
+
{disabled ? <CircularProgress size={24} color="inherit" /> : <ArrowUpwardIcon fontSize="small" />}
|
| 107 |
+
</IconButton>
|
| 108 |
+
</Box>
|
| 109 |
</Box>
|
| 110 |
</Box>
|
| 111 |
);
|
frontend/src/components/Chat/MessageBubble.tsx
CHANGED
|
@@ -1,8 +1,6 @@
|
|
| 1 |
import { Box, Paper, Typography, Chip } from '@mui/material';
|
| 2 |
import ReactMarkdown from 'react-markdown';
|
| 3 |
-
import
|
| 4 |
-
import SmartToyIcon from '@mui/icons-material/SmartToy';
|
| 5 |
-
import BuildIcon from '@mui/icons-material/Build';
|
| 6 |
import type { Message } from '@/types/agent';
|
| 7 |
|
| 8 |
interface MessageBubbleProps {
|
|
@@ -13,16 +11,10 @@ export default function MessageBubble({ message }: MessageBubbleProps) {
|
|
| 13 |
const isUser = message.role === 'user';
|
| 14 |
const isTool = message.role === 'tool';
|
| 15 |
|
| 16 |
-
const getIcon = () => {
|
| 17 |
-
if (isUser) return <PersonIcon fontSize="small" />;
|
| 18 |
-
if (isTool) return <BuildIcon fontSize="small" />;
|
| 19 |
-
return <SmartToyIcon fontSize="small" />;
|
| 20 |
-
};
|
| 21 |
-
|
| 22 |
const getBgColor = () => {
|
| 23 |
-
if (isUser) return '
|
| 24 |
if (isTool) return 'background.default';
|
| 25 |
-
return '
|
| 26 |
};
|
| 27 |
|
| 28 |
return (
|
|
@@ -36,31 +28,59 @@ export default function MessageBubble({ message }: MessageBubbleProps) {
|
|
| 36 |
<Paper
|
| 37 |
elevation={0}
|
| 38 |
sx={{
|
| 39 |
-
p: 2,
|
| 40 |
maxWidth: isTool ? '100%' : '80%',
|
| 41 |
width: isTool ? '100%' : 'auto',
|
| 42 |
bgcolor: getBgColor(),
|
| 43 |
-
border: 1,
|
| 44 |
borderColor: 'divider',
|
|
|
|
| 45 |
}}
|
| 46 |
>
|
| 47 |
-
|
| 48 |
-
{
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
<
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
<Box
|
| 65 |
sx={{
|
| 66 |
'& p': { m: 0 },
|
|
@@ -84,15 +104,34 @@ export default function MessageBubble({ message }: MessageBubbleProps) {
|
|
| 84 |
p: 0,
|
| 85 |
},
|
| 86 |
'& a': {
|
| 87 |
-
color: '
|
|
|
|
| 88 |
},
|
| 89 |
'& ul, & ol': {
|
| 90 |
pl: 2,
|
| 91 |
my: 1,
|
| 92 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
}}
|
| 94 |
>
|
| 95 |
-
<ReactMarkdown>{message.content}</ReactMarkdown>
|
| 96 |
</Box>
|
| 97 |
</Paper>
|
| 98 |
</Box>
|
|
|
|
| 1 |
import { Box, Paper, Typography, Chip } from '@mui/material';
|
| 2 |
import ReactMarkdown from 'react-markdown';
|
| 3 |
+
import remarkGfm from 'remark-gfm';
|
|
|
|
|
|
|
| 4 |
import type { Message } from '@/types/agent';
|
| 5 |
|
| 6 |
interface MessageBubbleProps {
|
|
|
|
| 11 |
const isUser = message.role === 'user';
|
| 12 |
const isTool = message.role === 'tool';
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
const getBgColor = () => {
|
| 15 |
+
if (isUser) return 'background.paper';
|
| 16 |
if (isTool) return 'background.default';
|
| 17 |
+
return 'transparent';
|
| 18 |
};
|
| 19 |
|
| 20 |
return (
|
|
|
|
| 28 |
<Paper
|
| 29 |
elevation={0}
|
| 30 |
sx={{
|
| 31 |
+
p: isTool ? 2 : isUser ? 1.5 : 1,
|
| 32 |
maxWidth: isTool ? '100%' : '80%',
|
| 33 |
width: isTool ? '100%' : 'auto',
|
| 34 |
bgcolor: getBgColor(),
|
| 35 |
+
border: (!isUser && !isTool) ? 0 : 1,
|
| 36 |
borderColor: 'divider',
|
| 37 |
+
borderRadius: isUser ? 2 : undefined,
|
| 38 |
}}
|
| 39 |
>
|
| 40 |
+
{isTool && (
|
| 41 |
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
|
| 42 |
+
<Typography variant="caption" color="text.secondary">
|
| 43 |
+
Tool
|
| 44 |
+
</Typography>
|
| 45 |
+
{message.toolName && (
|
| 46 |
+
<Chip
|
| 47 |
+
label={message.toolName}
|
| 48 |
+
size="small"
|
| 49 |
+
variant="outlined"
|
| 50 |
+
sx={{ ml: 1, height: 20, fontSize: '0.7rem' }}
|
| 51 |
+
/>
|
| 52 |
+
)}
|
| 53 |
+
</Box>
|
| 54 |
+
)}
|
| 55 |
+
|
| 56 |
+
{/* Persisted Trace Logs */}
|
| 57 |
+
{message.trace && message.trace.length > 0 && (
|
| 58 |
+
<Box
|
| 59 |
+
sx={{
|
| 60 |
+
bgcolor: 'background.default',
|
| 61 |
+
borderRadius: 1,
|
| 62 |
+
p: 1.5,
|
| 63 |
+
border: 1,
|
| 64 |
+
borderColor: 'divider',
|
| 65 |
+
width: '100%',
|
| 66 |
+
mb: 2,
|
| 67 |
+
}}
|
| 68 |
+
>
|
| 69 |
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
| 70 |
+
{message.trace.map((log) => (
|
| 71 |
+
<Typography
|
| 72 |
+
key={log.id}
|
| 73 |
+
variant="caption"
|
| 74 |
+
component="div"
|
| 75 |
+
sx={{ color: 'common.white', fontFamily: 'monospace', fontSize: '0.75rem' }}
|
| 76 |
+
>
|
| 77 |
+
> {log.text}
|
| 78 |
+
</Typography>
|
| 79 |
+
))}
|
| 80 |
+
</Box>
|
| 81 |
+
</Box>
|
| 82 |
+
)}
|
| 83 |
+
|
| 84 |
<Box
|
| 85 |
sx={{
|
| 86 |
'& p': { m: 0 },
|
|
|
|
| 104 |
p: 0,
|
| 105 |
},
|
| 106 |
'& a': {
|
| 107 |
+
color: 'inherit',
|
| 108 |
+
textDecoration: 'underline',
|
| 109 |
},
|
| 110 |
'& ul, & ol': {
|
| 111 |
pl: 2,
|
| 112 |
my: 1,
|
| 113 |
},
|
| 114 |
+
'& table': {
|
| 115 |
+
borderCollapse: 'collapse',
|
| 116 |
+
width: '100%',
|
| 117 |
+
my: 2,
|
| 118 |
+
fontSize: '0.875rem',
|
| 119 |
+
},
|
| 120 |
+
'& th': {
|
| 121 |
+
borderBottom: '1px solid',
|
| 122 |
+
borderColor: 'divider',
|
| 123 |
+
textAlign: 'left',
|
| 124 |
+
p: 1,
|
| 125 |
+
bgcolor: 'action.hover',
|
| 126 |
+
},
|
| 127 |
+
'& td': {
|
| 128 |
+
borderBottom: '1px solid',
|
| 129 |
+
borderColor: 'divider',
|
| 130 |
+
p: 1,
|
| 131 |
+
},
|
| 132 |
}}
|
| 133 |
>
|
| 134 |
+
<ReactMarkdown remarkPlugins={[remarkGfm]}>{message.content}</ReactMarkdown>
|
| 135 |
</Box>
|
| 136 |
</Paper>
|
| 137 |
</Box>
|
frontend/src/components/Chat/MessageList.tsx
CHANGED
|
@@ -1,6 +1,9 @@
|
|
| 1 |
import { useEffect, useRef } from 'react';
|
| 2 |
-
import { Box, Typography
|
|
|
|
|
|
|
| 3 |
import MessageBubble from './MessageBubble';
|
|
|
|
| 4 |
import type { Message } from '@/types/agent';
|
| 5 |
|
| 6 |
interface MessageListProps {
|
|
@@ -8,13 +11,51 @@ interface MessageListProps {
|
|
| 8 |
isProcessing: boolean;
|
| 9 |
}
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
export default function MessageList({ messages, isProcessing }: MessageListProps) {
|
| 12 |
const bottomRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
// Auto-scroll to bottom when new messages arrive
|
| 15 |
useEffect(() => {
|
| 16 |
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
|
| 17 |
-
}, [messages]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
return (
|
| 20 |
<Box
|
|
@@ -24,36 +65,77 @@ export default function MessageList({ messages, isProcessing }: MessageListProps
|
|
| 24 |
p: 2,
|
| 25 |
display: 'flex',
|
| 26 |
flexDirection: 'column',
|
| 27 |
-
gap: 2,
|
| 28 |
}}
|
| 29 |
>
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
</Box>
|
| 58 |
);
|
| 59 |
-
}
|
|
|
|
| 1 |
import { useEffect, useRef } from 'react';
|
| 2 |
+
import { Box, Typography } from '@mui/material';
|
| 3 |
+
import { useAgentStore } from '@/store/agentStore';
|
| 4 |
+
import { useSessionStore } from '@/store/sessionStore';
|
| 5 |
import MessageBubble from './MessageBubble';
|
| 6 |
+
import ApprovalFlow from './ApprovalFlow';
|
| 7 |
import type { Message } from '@/types/agent';
|
| 8 |
|
| 9 |
interface MessageListProps {
|
|
|
|
| 11 |
isProcessing: boolean;
|
| 12 |
}
|
| 13 |
|
| 14 |
+
const TechnicalIndicator = () => (
|
| 15 |
+
<Box
|
| 16 |
+
component="span"
|
| 17 |
+
sx={{
|
| 18 |
+
color: 'primary.main',
|
| 19 |
+
fontFamily: 'monospace',
|
| 20 |
+
fontWeight: 'bold',
|
| 21 |
+
fontSize: '1.2rem',
|
| 22 |
+
lineHeight: 0,
|
| 23 |
+
display: 'inline-block',
|
| 24 |
+
verticalAlign: 'middle',
|
| 25 |
+
width: '1em',
|
| 26 |
+
letterSpacing: '-3px',
|
| 27 |
+
transform: 'scale(0.6) translateY(-2px)',
|
| 28 |
+
'&::after': {
|
| 29 |
+
content: '""',
|
| 30 |
+
animation: 'dots 2s steps(4, end) infinite',
|
| 31 |
+
},
|
| 32 |
+
'@keyframes dots': {
|
| 33 |
+
'0%': { content: '""' },
|
| 34 |
+
'25%': { content: '"."' },
|
| 35 |
+
'50%': { content: '".."' },
|
| 36 |
+
'75%, 100%': { content: '"..."' },
|
| 37 |
+
},
|
| 38 |
+
}}
|
| 39 |
+
/>
|
| 40 |
+
);
|
| 41 |
+
|
| 42 |
export default function MessageList({ messages, isProcessing }: MessageListProps) {
|
| 43 |
const bottomRef = useRef<HTMLDivElement>(null);
|
| 44 |
+
const traceBoxRef = useRef<HTMLDivElement>(null);
|
| 45 |
+
const { traceLogs } = useAgentStore();
|
| 46 |
+
const { activeSessionId } = useSessionStore();
|
| 47 |
|
| 48 |
// Auto-scroll to bottom when new messages arrive
|
| 49 |
useEffect(() => {
|
| 50 |
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
|
| 51 |
+
}, [messages, isProcessing, traceLogs]);
|
| 52 |
+
|
| 53 |
+
// Auto-scroll trace box
|
| 54 |
+
useEffect(() => {
|
| 55 |
+
if (traceBoxRef.current) {
|
| 56 |
+
traceBoxRef.current.scrollTop = traceBoxRef.current.scrollHeight;
|
| 57 |
+
}
|
| 58 |
+
}, [traceLogs]);
|
| 59 |
|
| 60 |
return (
|
| 61 |
<Box
|
|
|
|
| 65 |
p: 2,
|
| 66 |
display: 'flex',
|
| 67 |
flexDirection: 'column',
|
|
|
|
| 68 |
}}
|
| 69 |
>
|
| 70 |
+
<Box sx={{ maxWidth: 'md', mx: 'auto', width: '100%', display: 'flex', flexDirection: 'column', gap: 2 }}>
|
| 71 |
+
{messages.length === 0 && traceLogs.length === 0 && !isProcessing ? (
|
| 72 |
+
<Box
|
| 73 |
+
sx={{
|
| 74 |
+
flex: 1,
|
| 75 |
+
display: 'flex',
|
| 76 |
+
alignItems: 'center',
|
| 77 |
+
justifyContent: 'center',
|
| 78 |
+
py: 8,
|
| 79 |
+
}}
|
| 80 |
+
>
|
| 81 |
+
<Typography color="text.secondary" sx={{ fontFamily: 'monospace' }}>
|
| 82 |
+
Awaiting input…
|
| 83 |
+
</Typography>
|
| 84 |
+
</Box>
|
| 85 |
+
) : (
|
| 86 |
+
messages.map((message) => (
|
| 87 |
+
<MessageBubble key={message.id} message={message} />
|
| 88 |
+
))
|
| 89 |
+
)}
|
| 90 |
+
|
| 91 |
+
{isProcessing && (
|
| 92 |
+
<Box sx={{ width: '100%', mb: 2 }}>
|
| 93 |
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1, px: 0.5 }}>
|
| 94 |
+
<Typography variant="caption" color="text.secondary" sx={{ fontFamily: 'monospace', fontWeight: 600 }}>
|
| 95 |
+
Thinking
|
| 96 |
+
</Typography>
|
| 97 |
+
<TechnicalIndicator />
|
| 98 |
+
</Box>
|
| 99 |
+
|
| 100 |
+
{traceLogs.length > 0 && (
|
| 101 |
+
<Box
|
| 102 |
+
sx={{
|
| 103 |
+
bgcolor: 'background.default',
|
| 104 |
+
borderRadius: 1,
|
| 105 |
+
p: 2,
|
| 106 |
+
border: 1,
|
| 107 |
+
borderColor: 'divider',
|
| 108 |
+
width: '100%',
|
| 109 |
+
fontFamily: 'monospace',
|
| 110 |
+
maxHeight: 120,
|
| 111 |
+
overflowY: 'auto',
|
| 112 |
+
}}
|
| 113 |
+
ref={traceBoxRef}
|
| 114 |
+
>
|
| 115 |
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
| 116 |
+
{traceLogs.map((log) => (
|
| 117 |
+
<Box key={log.id}>
|
| 118 |
+
<Typography
|
| 119 |
+
variant="caption"
|
| 120 |
+
component="div"
|
| 121 |
+
sx={{ color: 'common.white', fontFamily: 'monospace' }}
|
| 122 |
+
>
|
| 123 |
+
> {log.text}
|
| 124 |
+
</Typography>
|
| 125 |
+
</Box>
|
| 126 |
+
))}
|
| 127 |
+
</Box>
|
| 128 |
+
</Box>
|
| 129 |
+
)}
|
| 130 |
+
</Box>
|
| 131 |
+
)}
|
| 132 |
+
|
| 133 |
+
{activeSessionId && (
|
| 134 |
+
<ApprovalFlow sessionId={activeSessionId} />
|
| 135 |
+
)}
|
| 136 |
+
|
| 137 |
+
<div ref={bottomRef} />
|
| 138 |
+
</Box>
|
| 139 |
</Box>
|
| 140 |
);
|
| 141 |
+
}
|
frontend/src/components/CodePanel/CodePanel.tsx
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Box, Typography, IconButton } from '@mui/material';
|
| 2 |
+
import CloseIcon from '@mui/icons-material/Close';
|
| 3 |
+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
| 4 |
+
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
| 5 |
+
import { useAgentStore } from '@/store/agentStore';
|
| 6 |
+
import { useLayoutStore } from '@/store/layoutStore';
|
| 7 |
+
|
| 8 |
+
export default function CodePanel() {
|
| 9 |
+
const { panelContent } = useAgentStore();
|
| 10 |
+
const { setRightPanelOpen } = useLayoutStore();
|
| 11 |
+
|
| 12 |
+
return (
|
| 13 |
+
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column', bgcolor: 'background.paper' }}>
|
| 14 |
+
{/* Header - Always Visible */}
|
| 15 |
+
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
| 16 |
+
<Typography variant="caption" sx={{ fontFamily: 'monospace', fontWeight: 600, color: 'text.secondary', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
| 17 |
+
{panelContent?.title || 'Code Panel'}
|
| 18 |
+
</Typography>
|
| 19 |
+
<IconButton size="small" onClick={() => setRightPanelOpen(false)}>
|
| 20 |
+
<CloseIcon fontSize="small" />
|
| 21 |
+
</IconButton>
|
| 22 |
+
</Box>
|
| 23 |
+
|
| 24 |
+
{!panelContent ? (
|
| 25 |
+
<Box sx={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', p: 4 }}>
|
| 26 |
+
<Typography variant="body2" color="text.secondary" sx={{ fontFamily: 'monospace', opacity: 0.5 }}>
|
| 27 |
+
NO DATA LOADED
|
| 28 |
+
</Typography>
|
| 29 |
+
</Box>
|
| 30 |
+
) : (
|
| 31 |
+
<Box sx={{ flex: 1, overflow: 'auto', bgcolor: 'background.default' }}>
|
| 32 |
+
{panelContent.content ? (
|
| 33 |
+
panelContent.language === 'python' ? (
|
| 34 |
+
<SyntaxHighlighter
|
| 35 |
+
language="python"
|
| 36 |
+
style={vscDarkPlus}
|
| 37 |
+
customStyle={{
|
| 38 |
+
margin: 0,
|
| 39 |
+
padding: '16px',
|
| 40 |
+
background: 'transparent',
|
| 41 |
+
fontSize: '0.8rem',
|
| 42 |
+
fontFamily: '"JetBrains Mono", monospace',
|
| 43 |
+
}}
|
| 44 |
+
wrapLines={true}
|
| 45 |
+
wrapLongLines={true}
|
| 46 |
+
>
|
| 47 |
+
{panelContent.content}
|
| 48 |
+
</SyntaxHighlighter>
|
| 49 |
+
) : (
|
| 50 |
+
<Box sx={{ p: 2 }}>
|
| 51 |
+
<Box component="pre" sx={{
|
| 52 |
+
m: 0,
|
| 53 |
+
fontFamily: 'monospace',
|
| 54 |
+
fontSize: '0.8rem',
|
| 55 |
+
color: 'text.primary',
|
| 56 |
+
whiteSpace: 'pre-wrap',
|
| 57 |
+
wordBreak: 'break-all'
|
| 58 |
+
}}>
|
| 59 |
+
<code>{panelContent.content}</code>
|
| 60 |
+
</Box>
|
| 61 |
+
</Box>
|
| 62 |
+
)
|
| 63 |
+
) : (
|
| 64 |
+
<Box sx={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', p: 4, opacity: 0.5 }}>
|
| 65 |
+
<Typography variant="caption" sx={{ fontFamily: 'monospace' }}>
|
| 66 |
+
NO CONTENT TO DISPLAY
|
| 67 |
+
</Typography>
|
| 68 |
+
</Box>
|
| 69 |
+
)}
|
| 70 |
+
</Box>
|
| 71 |
+
)}
|
| 72 |
+
</Box>
|
| 73 |
+
);
|
| 74 |
+
}
|
frontend/src/components/Layout/AppLayout.tsx
CHANGED
|
@@ -1,33 +1,72 @@
|
|
| 1 |
-
import { useState, useCallback } from 'react';
|
| 2 |
import {
|
| 3 |
Box,
|
| 4 |
-
|
| 5 |
-
Toolbar,
|
| 6 |
Typography,
|
| 7 |
IconButton,
|
| 8 |
-
Drawer,
|
| 9 |
-
Chip,
|
| 10 |
} from '@mui/material';
|
| 11 |
import MenuIcon from '@mui/icons-material/Menu';
|
| 12 |
-
import
|
| 13 |
-
import
|
| 14 |
-
import
|
| 15 |
|
| 16 |
import { useSessionStore } from '@/store/sessionStore';
|
| 17 |
import { useAgentStore } from '@/store/agentStore';
|
|
|
|
| 18 |
import { useAgentWebSocket } from '@/hooks/useAgentWebSocket';
|
| 19 |
import SessionSidebar from '@/components/SessionSidebar/SessionSidebar';
|
|
|
|
| 20 |
import ChatInput from '@/components/Chat/ChatInput';
|
| 21 |
import MessageList from '@/components/Chat/MessageList';
|
| 22 |
-
import
|
| 23 |
|
| 24 |
const DRAWER_WIDTH = 280;
|
| 25 |
|
| 26 |
export default function AppLayout() {
|
| 27 |
-
const [mobileOpen, setMobileOpen] = useState(false);
|
| 28 |
-
|
| 29 |
const { activeSessionId } = useSessionStore();
|
| 30 |
-
const { isConnected, isProcessing, getMessages } = useAgentStore();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
const messages = activeSessionId ? getMessages(activeSessionId) : [];
|
| 33 |
|
|
@@ -37,31 +76,18 @@ export default function AppLayout() {
|
|
| 37 |
onError: (error) => console.error('Agent error:', error),
|
| 38 |
});
|
| 39 |
|
| 40 |
-
const handleDrawerToggle = () => {
|
| 41 |
-
setMobileOpen(!mobileOpen);
|
| 42 |
-
};
|
| 43 |
-
|
| 44 |
-
const handleUndo = useCallback(async () => {
|
| 45 |
-
if (!activeSessionId) return;
|
| 46 |
-
try {
|
| 47 |
-
await fetch(`/api/undo/${activeSessionId}`, { method: 'POST' });
|
| 48 |
-
} catch (e) {
|
| 49 |
-
console.error('Undo failed:', e);
|
| 50 |
-
}
|
| 51 |
-
}, [activeSessionId]);
|
| 52 |
-
|
| 53 |
-
const handleCompact = useCallback(async () => {
|
| 54 |
-
if (!activeSessionId) return;
|
| 55 |
-
try {
|
| 56 |
-
await fetch(`/api/compact/${activeSessionId}`, { method: 'POST' });
|
| 57 |
-
} catch (e) {
|
| 58 |
-
console.error('Compact failed:', e);
|
| 59 |
-
}
|
| 60 |
-
}, [activeSessionId]);
|
| 61 |
-
|
| 62 |
const handleSendMessage = useCallback(
|
| 63 |
async (text: string) => {
|
| 64 |
if (!activeSessionId || !text.trim()) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
try {
|
| 66 |
await fetch('/api/submit', {
|
| 67 |
method: 'POST',
|
|
@@ -75,148 +101,180 @@ export default function AppLayout() {
|
|
| 75 |
console.error('Send failed:', e);
|
| 76 |
}
|
| 77 |
},
|
| 78 |
-
[activeSessionId]
|
| 79 |
);
|
| 80 |
|
| 81 |
-
const drawer = <SessionSidebar onClose={() => setMobileOpen(false)} />;
|
| 82 |
-
|
| 83 |
return (
|
| 84 |
<Box sx={{ display: 'flex', width: '100%', height: '100%' }}>
|
| 85 |
-
{/*
|
| 86 |
-
<AppBar
|
| 87 |
-
position="fixed"
|
| 88 |
-
sx={{
|
| 89 |
-
width: { md: `calc(100% - ${DRAWER_WIDTH}px)` },
|
| 90 |
-
ml: { md: `${DRAWER_WIDTH}px` },
|
| 91 |
-
bgcolor: 'background.paper',
|
| 92 |
-
borderBottom: 1,
|
| 93 |
-
borderColor: 'divider',
|
| 94 |
-
}}
|
| 95 |
-
elevation={0}
|
| 96 |
-
>
|
| 97 |
-
<Toolbar>
|
| 98 |
-
<IconButton
|
| 99 |
-
color="inherit"
|
| 100 |
-
edge="start"
|
| 101 |
-
onClick={handleDrawerToggle}
|
| 102 |
-
sx={{ mr: 2, display: { md: 'none' } }}
|
| 103 |
-
>
|
| 104 |
-
<MenuIcon />
|
| 105 |
-
</IconButton>
|
| 106 |
-
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
|
| 107 |
-
HF Agent
|
| 108 |
-
</Typography>
|
| 109 |
-
<Chip
|
| 110 |
-
icon={
|
| 111 |
-
<FiberManualRecordIcon
|
| 112 |
-
sx={{
|
| 113 |
-
fontSize: 12,
|
| 114 |
-
color: isConnected ? 'success.main' : 'error.main',
|
| 115 |
-
}}
|
| 116 |
-
/>
|
| 117 |
-
}
|
| 118 |
-
label={isConnected ? 'Connected' : 'Disconnected'}
|
| 119 |
-
size="small"
|
| 120 |
-
variant="outlined"
|
| 121 |
-
sx={{ mr: 2 }}
|
| 122 |
-
/>
|
| 123 |
-
<IconButton
|
| 124 |
-
onClick={handleUndo}
|
| 125 |
-
disabled={!activeSessionId || isProcessing}
|
| 126 |
-
title="Undo last turn"
|
| 127 |
-
>
|
| 128 |
-
<UndoIcon />
|
| 129 |
-
</IconButton>
|
| 130 |
-
<IconButton
|
| 131 |
-
onClick={handleCompact}
|
| 132 |
-
disabled={!activeSessionId || isProcessing}
|
| 133 |
-
title="Compact context"
|
| 134 |
-
>
|
| 135 |
-
<CompressIcon />
|
| 136 |
-
</IconButton>
|
| 137 |
-
</Toolbar>
|
| 138 |
-
</AppBar>
|
| 139 |
-
|
| 140 |
-
{/* Sidebar Drawer */}
|
| 141 |
<Box
|
| 142 |
component="nav"
|
| 143 |
-
sx={{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
>
|
| 145 |
-
{/* Mobile drawer */}
|
| 146 |
<Drawer
|
| 147 |
-
variant="
|
| 148 |
-
open={mobileOpen}
|
| 149 |
-
onClose={handleDrawerToggle}
|
| 150 |
-
ModalProps={{ keepMounted: true }}
|
| 151 |
-
sx={{
|
| 152 |
-
display: { xs: 'block', md: 'none' },
|
| 153 |
-
'& .MuiDrawer-paper': {
|
| 154 |
-
boxSizing: 'border-box',
|
| 155 |
-
width: DRAWER_WIDTH,
|
| 156 |
-
},
|
| 157 |
-
}}
|
| 158 |
-
>
|
| 159 |
-
{drawer}
|
| 160 |
-
</Drawer>
|
| 161 |
-
{/* Desktop drawer */}
|
| 162 |
-
<Drawer
|
| 163 |
-
variant="permanent"
|
| 164 |
sx={{
|
| 165 |
display: { xs: 'none', md: 'block' },
|
| 166 |
'& .MuiDrawer-paper': {
|
| 167 |
boxSizing: 'border-box',
|
| 168 |
width: DRAWER_WIDTH,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
},
|
| 170 |
}}
|
| 171 |
-
open
|
| 172 |
>
|
| 173 |
-
|
| 174 |
</Drawer>
|
| 175 |
</Box>
|
| 176 |
|
| 177 |
-
{/* Main Content */}
|
| 178 |
<Box
|
| 179 |
-
component="main"
|
| 180 |
sx={{
|
| 181 |
flexGrow: 1,
|
| 182 |
-
width: { md: `calc(100% - ${DRAWER_WIDTH}px)` },
|
| 183 |
height: '100%',
|
| 184 |
display: 'flex',
|
| 185 |
flexDirection: 'column',
|
|
|
|
|
|
|
|
|
|
| 186 |
}}
|
| 187 |
>
|
| 188 |
-
|
| 189 |
-
{
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
/>
|
| 196 |
-
</>
|
| 197 |
-
) : (
|
| 198 |
-
<Box
|
| 199 |
-
sx={{
|
| 200 |
-
flex: 1,
|
| 201 |
-
display: 'flex',
|
| 202 |
-
alignItems: 'center',
|
| 203 |
-
justifyContent: 'center',
|
| 204 |
-
flexDirection: 'column',
|
| 205 |
-
gap: 2,
|
| 206 |
-
}}
|
| 207 |
-
>
|
| 208 |
-
<Typography variant="h5" color="text.secondary">
|
| 209 |
-
No session selected
|
| 210 |
-
</Typography>
|
| 211 |
-
<Typography variant="body2" color="text.secondary">
|
| 212 |
-
Create a new session from the sidebar to get started
|
| 213 |
-
</Typography>
|
| 214 |
</Box>
|
| 215 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
</Box>
|
| 217 |
|
| 218 |
-
{/*
|
| 219 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
</Box>
|
| 221 |
);
|
| 222 |
}
|
|
|
|
| 1 |
+
import { useState, useCallback, useRef, useEffect } from 'react';
|
| 2 |
import {
|
| 3 |
Box,
|
| 4 |
+
Drawer,
|
|
|
|
| 5 |
Typography,
|
| 6 |
IconButton,
|
|
|
|
|
|
|
| 7 |
} from '@mui/material';
|
| 8 |
import MenuIcon from '@mui/icons-material/Menu';
|
| 9 |
+
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
| 10 |
+
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
| 11 |
+
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
|
| 12 |
|
| 13 |
import { useSessionStore } from '@/store/sessionStore';
|
| 14 |
import { useAgentStore } from '@/store/agentStore';
|
| 15 |
+
import { useLayoutStore } from '@/store/layoutStore';
|
| 16 |
import { useAgentWebSocket } from '@/hooks/useAgentWebSocket';
|
| 17 |
import SessionSidebar from '@/components/SessionSidebar/SessionSidebar';
|
| 18 |
+
import CodePanel from '@/components/CodePanel/CodePanel';
|
| 19 |
import ChatInput from '@/components/Chat/ChatInput';
|
| 20 |
import MessageList from '@/components/Chat/MessageList';
|
| 21 |
+
import type { Message } from '@/types/agent';
|
| 22 |
|
| 23 |
const DRAWER_WIDTH = 280;
|
| 24 |
|
| 25 |
export default function AppLayout() {
|
|
|
|
|
|
|
| 26 |
const { activeSessionId } = useSessionStore();
|
| 27 |
+
const { isConnected, isProcessing, getMessages, addMessage } = useAgentStore();
|
| 28 |
+
const {
|
| 29 |
+
isLeftSidebarOpen,
|
| 30 |
+
isRightPanelOpen,
|
| 31 |
+
rightPanelWidth,
|
| 32 |
+
setRightPanelWidth,
|
| 33 |
+
toggleLeftSidebar,
|
| 34 |
+
toggleRightPanel
|
| 35 |
+
} = useLayoutStore();
|
| 36 |
+
|
| 37 |
+
const isResizing = useRef(false);
|
| 38 |
+
|
| 39 |
+
const startResizing = useCallback((e: React.MouseEvent) => {
|
| 40 |
+
e.preventDefault();
|
| 41 |
+
isResizing.current = true;
|
| 42 |
+
document.addEventListener('mousemove', handleMouseMove);
|
| 43 |
+
document.addEventListener('mouseup', stopResizing);
|
| 44 |
+
document.body.style.cursor = 'col-resize';
|
| 45 |
+
}, []);
|
| 46 |
+
|
| 47 |
+
const stopResizing = useCallback(() => {
|
| 48 |
+
isResizing.current = false;
|
| 49 |
+
document.removeEventListener('mousemove', handleMouseMove);
|
| 50 |
+
document.removeEventListener('mouseup', stopResizing);
|
| 51 |
+
document.body.style.cursor = 'default';
|
| 52 |
+
}, []);
|
| 53 |
+
|
| 54 |
+
const handleMouseMove = useCallback((e: MouseEvent) => {
|
| 55 |
+
if (!isResizing.current) return;
|
| 56 |
+
const newWidth = window.innerWidth - e.clientX;
|
| 57 |
+
const maxWidth = window.innerWidth * 0.8;
|
| 58 |
+
const minWidth = 300;
|
| 59 |
+
if (newWidth > minWidth && newWidth < maxWidth) {
|
| 60 |
+
setRightPanelWidth(newWidth);
|
| 61 |
+
}
|
| 62 |
+
}, [setRightPanelWidth]);
|
| 63 |
+
|
| 64 |
+
useEffect(() => {
|
| 65 |
+
return () => {
|
| 66 |
+
document.removeEventListener('mousemove', handleMouseMove);
|
| 67 |
+
document.removeEventListener('mouseup', stopResizing);
|
| 68 |
+
};
|
| 69 |
+
}, [handleMouseMove, stopResizing]);
|
| 70 |
|
| 71 |
const messages = activeSessionId ? getMessages(activeSessionId) : [];
|
| 72 |
|
|
|
|
| 76 |
onError: (error) => console.error('Agent error:', error),
|
| 77 |
});
|
| 78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
const handleSendMessage = useCallback(
|
| 80 |
async (text: string) => {
|
| 81 |
if (!activeSessionId || !text.trim()) return;
|
| 82 |
+
|
| 83 |
+
const userMsg: Message = {
|
| 84 |
+
id: `user_${Date.now()}`,
|
| 85 |
+
role: 'user',
|
| 86 |
+
content: text.trim(),
|
| 87 |
+
timestamp: new Date().toISOString(),
|
| 88 |
+
};
|
| 89 |
+
addMessage(activeSessionId, userMsg);
|
| 90 |
+
|
| 91 |
try {
|
| 92 |
await fetch('/api/submit', {
|
| 93 |
method: 'POST',
|
|
|
|
| 101 |
console.error('Send failed:', e);
|
| 102 |
}
|
| 103 |
},
|
| 104 |
+
[activeSessionId, addMessage]
|
| 105 |
);
|
| 106 |
|
|
|
|
|
|
|
| 107 |
return (
|
| 108 |
<Box sx={{ display: 'flex', width: '100%', height: '100%' }}>
|
| 109 |
+
{/* Left Sidebar Drawer */}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
<Box
|
| 111 |
component="nav"
|
| 112 |
+
sx={{
|
| 113 |
+
width: { md: isLeftSidebarOpen ? DRAWER_WIDTH : 0 },
|
| 114 |
+
flexShrink: { md: 0 },
|
| 115 |
+
transition: isResizing.current ? 'none' : 'width 0.2s',
|
| 116 |
+
overflow: 'hidden',
|
| 117 |
+
}}
|
| 118 |
>
|
|
|
|
| 119 |
<Drawer
|
| 120 |
+
variant="persistent"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
sx={{
|
| 122 |
display: { xs: 'none', md: 'block' },
|
| 123 |
'& .MuiDrawer-paper': {
|
| 124 |
boxSizing: 'border-box',
|
| 125 |
width: DRAWER_WIDTH,
|
| 126 |
+
borderRight: '1px solid',
|
| 127 |
+
borderColor: 'divider',
|
| 128 |
+
top: '40px', // Below logo bar
|
| 129 |
+
height: 'calc(100% - 40px)',
|
| 130 |
},
|
| 131 |
}}
|
| 132 |
+
open={isLeftSidebarOpen}
|
| 133 |
>
|
| 134 |
+
<SessionSidebar />
|
| 135 |
</Drawer>
|
| 136 |
</Box>
|
| 137 |
|
| 138 |
+
{/* Main Content Area */}
|
| 139 |
<Box
|
|
|
|
| 140 |
sx={{
|
| 141 |
flexGrow: 1,
|
|
|
|
| 142 |
height: '100%',
|
| 143 |
display: 'flex',
|
| 144 |
flexDirection: 'column',
|
| 145 |
+
transition: isResizing.current ? 'none' : 'width 0.2s',
|
| 146 |
+
position: 'relative',
|
| 147 |
+
overflow: 'hidden',
|
| 148 |
}}
|
| 149 |
>
|
| 150 |
+
{/* Top Header Bar (Fixed) */}
|
| 151 |
+
<Box sx={{
|
| 152 |
+
height: '60px',
|
| 153 |
+
px: 1,
|
| 154 |
+
display: 'flex',
|
| 155 |
+
alignItems: 'center',
|
| 156 |
+
borderBottom: 1,
|
| 157 |
+
borderColor: 'divider',
|
| 158 |
+
bgcolor: 'background.default',
|
| 159 |
+
zIndex: 1200,
|
| 160 |
+
}}>
|
| 161 |
+
<IconButton onClick={toggleLeftSidebar} size="small">
|
| 162 |
+
{isLeftSidebarOpen ? <ChevronLeftIcon /> : <MenuIcon />}
|
| 163 |
+
</IconButton>
|
| 164 |
+
|
| 165 |
+
<Box sx={{ flex: 1, display: 'flex', justifyContent: 'center' }}>
|
| 166 |
+
<img
|
| 167 |
+
src="/hf-logo-white.png"
|
| 168 |
+
alt="Hugging Face"
|
| 169 |
+
style={{ height: '40px', objectFit: 'contain' }}
|
| 170 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
</Box>
|
| 172 |
+
|
| 173 |
+
<IconButton
|
| 174 |
+
onClick={toggleRightPanel}
|
| 175 |
+
size="small"
|
| 176 |
+
sx={{ visibility: isRightPanelOpen ? 'hidden' : 'visible' }}
|
| 177 |
+
>
|
| 178 |
+
<MenuIcon />
|
| 179 |
+
</IconButton>
|
| 180 |
+
</Box>
|
| 181 |
+
|
| 182 |
+
<Box
|
| 183 |
+
component="main"
|
| 184 |
+
sx={{
|
| 185 |
+
flexGrow: 1,
|
| 186 |
+
display: 'flex',
|
| 187 |
+
flexDirection: 'column',
|
| 188 |
+
overflow: 'hidden',
|
| 189 |
+
}}
|
| 190 |
+
>
|
| 191 |
+
{activeSessionId ? (
|
| 192 |
+
<>
|
| 193 |
+
<MessageList messages={messages} isProcessing={isProcessing} />
|
| 194 |
+
<ChatInput
|
| 195 |
+
onSend={handleSendMessage}
|
| 196 |
+
disabled={isProcessing || !isConnected}
|
| 197 |
+
/>
|
| 198 |
+
</>
|
| 199 |
+
) : (
|
| 200 |
+
<Box
|
| 201 |
+
sx={{
|
| 202 |
+
flex: 1,
|
| 203 |
+
display: 'flex',
|
| 204 |
+
alignItems: 'center',
|
| 205 |
+
justifyContent: 'center',
|
| 206 |
+
flexDirection: 'column',
|
| 207 |
+
gap: 2,
|
| 208 |
+
}}
|
| 209 |
+
>
|
| 210 |
+
<Typography variant="h5" color="text.secondary" sx={{ fontFamily: 'monospace' }}>
|
| 211 |
+
NO SESSION SELECTED
|
| 212 |
+
</Typography>
|
| 213 |
+
<Typography variant="body2" color="text.secondary" sx={{ fontFamily: 'monospace' }}>
|
| 214 |
+
Initialize a session via the sidebar
|
| 215 |
+
</Typography>
|
| 216 |
+
</Box>
|
| 217 |
+
)}
|
| 218 |
+
</Box>
|
| 219 |
</Box>
|
| 220 |
|
| 221 |
+
{/* Resize Handle */}
|
| 222 |
+
{isRightPanelOpen && (
|
| 223 |
+
<Box
|
| 224 |
+
onMouseDown={startResizing}
|
| 225 |
+
sx={{
|
| 226 |
+
width: '4px',
|
| 227 |
+
cursor: 'col-resize',
|
| 228 |
+
bgcolor: 'divider',
|
| 229 |
+
display: 'flex',
|
| 230 |
+
alignItems: 'center',
|
| 231 |
+
justifyContent: 'center',
|
| 232 |
+
transition: 'background-color 0.2s',
|
| 233 |
+
zIndex: 1300,
|
| 234 |
+
overflow: 'hidden',
|
| 235 |
+
'&:hover': {
|
| 236 |
+
bgcolor: 'primary.main',
|
| 237 |
+
},
|
| 238 |
+
}}
|
| 239 |
+
>
|
| 240 |
+
<DragIndicatorIcon
|
| 241 |
+
sx={{
|
| 242 |
+
fontSize: '0.8rem',
|
| 243 |
+
color: 'text.secondary',
|
| 244 |
+
pointerEvents: 'none',
|
| 245 |
+
}}
|
| 246 |
+
/>
|
| 247 |
+
</Box>
|
| 248 |
+
)}
|
| 249 |
+
|
| 250 |
+
{/* Right Panel Drawer */}
|
| 251 |
+
<Box
|
| 252 |
+
component="nav"
|
| 253 |
+
sx={{
|
| 254 |
+
width: { md: isRightPanelOpen ? rightPanelWidth : 0 },
|
| 255 |
+
flexShrink: { md: 0 },
|
| 256 |
+
transition: isResizing.current ? 'none' : 'width 0.2s',
|
| 257 |
+
overflow: 'hidden',
|
| 258 |
+
}}
|
| 259 |
+
>
|
| 260 |
+
<Drawer
|
| 261 |
+
anchor="right"
|
| 262 |
+
variant="persistent"
|
| 263 |
+
sx={{
|
| 264 |
+
display: { xs: 'none', md: 'block' },
|
| 265 |
+
'& .MuiDrawer-paper': {
|
| 266 |
+
boxSizing: 'border-box',
|
| 267 |
+
width: rightPanelWidth,
|
| 268 |
+
borderLeft: 'none',
|
| 269 |
+
top: '40px', // Below logo bar
|
| 270 |
+
height: 'calc(100% - 40px)',
|
| 271 |
+
},
|
| 272 |
+
}}
|
| 273 |
+
open={isRightPanelOpen}
|
| 274 |
+
>
|
| 275 |
+
<CodePanel />
|
| 276 |
+
</Drawer>
|
| 277 |
+
</Box>
|
| 278 |
</Box>
|
| 279 |
);
|
| 280 |
}
|
frontend/src/components/SessionSidebar/SessionSidebar.tsx
CHANGED
|
@@ -11,10 +11,11 @@ import {
|
|
| 11 |
Button,
|
| 12 |
Divider,
|
| 13 |
Chip,
|
|
|
|
| 14 |
} from '@mui/material';
|
| 15 |
-
import AddIcon from '@mui/icons-material/Add';
|
| 16 |
import DeleteIcon from '@mui/icons-material/Delete';
|
| 17 |
-
import
|
|
|
|
| 18 |
import { useSessionStore } from '@/store/sessionStore';
|
| 19 |
import { useAgentStore } from '@/store/agentStore';
|
| 20 |
|
|
@@ -22,10 +23,37 @@ interface SessionSidebarProps {
|
|
| 22 |
onClose?: () => void;
|
| 23 |
}
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
export default function SessionSidebar({ onClose }: SessionSidebarProps) {
|
| 26 |
const { sessions, activeSessionId, createSession, deleteSession, switchSession } =
|
| 27 |
useSessionStore();
|
| 28 |
-
const { clearMessages } = useAgentStore();
|
| 29 |
|
| 30 |
const handleNewSession = useCallback(async () => {
|
| 31 |
try {
|
|
@@ -60,30 +88,46 @@ export default function SessionSidebar({ onClose }: SessionSidebarProps) {
|
|
| 60 |
[switchSession, onClose]
|
| 61 |
);
|
| 62 |
|
| 63 |
-
const
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
}
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
| 71 |
};
|
| 72 |
|
| 73 |
return (
|
| 74 |
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
| 75 |
{/* Header */}
|
| 76 |
-
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}>
|
| 77 |
-
<
|
| 78 |
-
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
<Button
|
| 81 |
fullWidth
|
| 82 |
-
variant="
|
| 83 |
-
startIcon={<AddIcon />}
|
| 84 |
onClick={handleNewSession}
|
|
|
|
| 85 |
>
|
| 86 |
-
|
| 87 |
</Button>
|
| 88 |
</Box>
|
| 89 |
|
|
@@ -91,70 +135,81 @@ export default function SessionSidebar({ onClose }: SessionSidebarProps) {
|
|
| 91 |
<Box sx={{ flex: 1, overflow: 'auto' }}>
|
| 92 |
{sessions.length === 0 ? (
|
| 93 |
<Box sx={{ p: 3, textAlign: 'center' }}>
|
| 94 |
-
<
|
| 95 |
-
|
| 96 |
-
No sessions yet
|
| 97 |
</Typography>
|
| 98 |
<Typography variant="caption" color="text.secondary">
|
| 99 |
-
|
| 100 |
</Typography>
|
| 101 |
</Box>
|
| 102 |
) : (
|
| 103 |
<List disablePadding>
|
| 104 |
-
{[...sessions].reverse().map((session) =>
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
'&
|
| 113 |
bgcolor: 'action.selected',
|
|
|
|
|
|
|
|
|
|
| 114 |
},
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
<Typography
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
</Typography>
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
/>
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
size="small"
|
| 140 |
-
onClick={(e) => handleDeleteSession(session.id, e)}
|
| 141 |
-
>
|
| 142 |
-
<DeleteIcon fontSize="small" />
|
| 143 |
-
</IconButton>
|
| 144 |
-
</ListItemSecondaryAction>
|
| 145 |
-
</ListItemButton>
|
| 146 |
-
</ListItem>
|
| 147 |
-
))}
|
| 148 |
</List>
|
| 149 |
)}
|
| 150 |
</Box>
|
| 151 |
|
| 152 |
{/* Footer */}
|
| 153 |
<Divider />
|
| 154 |
-
<Box sx={{ p: 2 }}>
|
| 155 |
-
<Typography variant="caption" color="text.secondary">
|
| 156 |
-
{sessions.length}
|
| 157 |
</Typography>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
</Box>
|
| 159 |
</Box>
|
| 160 |
);
|
|
|
|
| 11 |
Button,
|
| 12 |
Divider,
|
| 13 |
Chip,
|
| 14 |
+
Tooltip,
|
| 15 |
} from '@mui/material';
|
|
|
|
| 16 |
import DeleteIcon from '@mui/icons-material/Delete';
|
| 17 |
+
import UndoIcon from '@mui/icons-material/Undo';
|
| 18 |
+
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
|
| 19 |
import { useSessionStore } from '@/store/sessionStore';
|
| 20 |
import { useAgentStore } from '@/store/agentStore';
|
| 21 |
|
|
|
|
| 23 |
onClose?: () => void;
|
| 24 |
}
|
| 25 |
|
| 26 |
+
const StatusDiode = ({ connected }: { connected: boolean }) => (
|
| 27 |
+
<Box
|
| 28 |
+
sx={{
|
| 29 |
+
width: 8,
|
| 30 |
+
height: 8,
|
| 31 |
+
borderRadius: '50%',
|
| 32 |
+
bgcolor: connected ? 'success.main' : 'error.main',
|
| 33 |
+
boxShadow: connected ? '0 0 0 0 rgba(46, 160, 67, 0.7)' : 'none',
|
| 34 |
+
animation: connected ? 'pulse 2s infinite' : 'none',
|
| 35 |
+
'@keyframes pulse': {
|
| 36 |
+
'0%': {
|
| 37 |
+
transform: 'scale(0.95)',
|
| 38 |
+
boxShadow: '0 0 0 0 rgba(46, 160, 67, 0.7)',
|
| 39 |
+
},
|
| 40 |
+
'70%': {
|
| 41 |
+
transform: 'scale(1)',
|
| 42 |
+
boxShadow: '0 0 0 4px rgba(46, 160, 67, 0)',
|
| 43 |
+
},
|
| 44 |
+
'100%': {
|
| 45 |
+
transform: 'scale(0.95)',
|
| 46 |
+
boxShadow: '0 0 0 0 rgba(46, 160, 67, 0)',
|
| 47 |
+
},
|
| 48 |
+
},
|
| 49 |
+
}}
|
| 50 |
+
/>
|
| 51 |
+
);
|
| 52 |
+
|
| 53 |
export default function SessionSidebar({ onClose }: SessionSidebarProps) {
|
| 54 |
const { sessions, activeSessionId, createSession, deleteSession, switchSession } =
|
| 55 |
useSessionStore();
|
| 56 |
+
const { clearMessages, isConnected, isProcessing } = useAgentStore();
|
| 57 |
|
| 58 |
const handleNewSession = useCallback(async () => {
|
| 59 |
try {
|
|
|
|
| 88 |
[switchSession, onClose]
|
| 89 |
);
|
| 90 |
|
| 91 |
+
const handleUndo = useCallback(async () => {
|
| 92 |
+
if (!activeSessionId) return;
|
| 93 |
+
try {
|
| 94 |
+
await fetch(`/api/undo/${activeSessionId}`, { method: 'POST' });
|
| 95 |
+
} catch (e) {
|
| 96 |
+
console.error('Undo failed:', e);
|
| 97 |
}
|
| 98 |
+
}, [activeSessionId]);
|
| 99 |
+
|
| 100 |
+
const formatTime = (dateString: string) => {
|
| 101 |
+
return new Date(dateString).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
| 102 |
};
|
| 103 |
|
| 104 |
return (
|
| 105 |
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
| 106 |
{/* Header */}
|
| 107 |
+
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
| 108 |
+
<Box sx={{ mb: 2 }}>
|
| 109 |
+
<img
|
| 110 |
+
src="/hf-log-only-white.png"
|
| 111 |
+
alt="HF Agent"
|
| 112 |
+
style={{ height: '32px', objectFit: 'contain' }}
|
| 113 |
+
/>
|
| 114 |
+
</Box>
|
| 115 |
+
|
| 116 |
+
{/* System Info / Status */}
|
| 117 |
+
<Box sx={{ mb: 2, display: 'flex', alignItems: 'center', gap: 1 }}>
|
| 118 |
+
<Typography variant="caption" color="text.secondary" sx={{ fontFamily: 'monospace' }}>
|
| 119 |
+
{isConnected ? 'Connected' : 'Disconnected'}
|
| 120 |
+
</Typography>
|
| 121 |
+
<StatusDiode connected={isConnected} />
|
| 122 |
+
</Box>
|
| 123 |
+
|
| 124 |
<Button
|
| 125 |
fullWidth
|
| 126 |
+
variant="outlined"
|
|
|
|
| 127 |
onClick={handleNewSession}
|
| 128 |
+
sx={{ justifyContent: 'center' }}
|
| 129 |
>
|
| 130 |
+
Create Session
|
| 131 |
</Button>
|
| 132 |
</Box>
|
| 133 |
|
|
|
|
| 135 |
<Box sx={{ flex: 1, overflow: 'auto' }}>
|
| 136 |
{sessions.length === 0 ? (
|
| 137 |
<Box sx={{ p: 3, textAlign: 'center' }}>
|
| 138 |
+
<Typography variant="body2" color="text.secondary" sx={{ fontFamily: 'monospace' }}>
|
| 139 |
+
NO ACTIVE SESSIONS
|
|
|
|
| 140 |
</Typography>
|
| 141 |
<Typography variant="caption" color="text.secondary">
|
| 142 |
+
Initialize a new session to begin
|
| 143 |
</Typography>
|
| 144 |
</Box>
|
| 145 |
) : (
|
| 146 |
<List disablePadding>
|
| 147 |
+
{[...sessions].reverse().map((session, index) => {
|
| 148 |
+
const sessionNumber = sessions.length - index;
|
| 149 |
+
return (
|
| 150 |
+
<ListItem key={session.id} disablePadding divider>
|
| 151 |
+
<ListItemButton
|
| 152 |
+
selected={session.id === activeSessionId}
|
| 153 |
+
onClick={() => handleSelectSession(session.id)}
|
| 154 |
+
sx={{
|
| 155 |
+
'&.Mui-selected': {
|
| 156 |
bgcolor: 'action.selected',
|
| 157 |
+
'&:hover': {
|
| 158 |
+
bgcolor: 'action.selected',
|
| 159 |
+
},
|
| 160 |
},
|
| 161 |
+
}}
|
| 162 |
+
>
|
| 163 |
+
<ListItemText
|
| 164 |
+
primary={
|
| 165 |
+
<Typography variant="body2" sx={{ fontFamily: 'monospace', fontWeight: 600 }}>
|
| 166 |
+
SESSION {String(sessionNumber).padStart(3, '0')}
|
| 167 |
+
</Typography>
|
| 168 |
+
}
|
| 169 |
+
secondary={
|
| 170 |
+
<Typography variant="caption" sx={{ fontFamily: 'monospace', display: 'flex', alignItems: 'center', gap: 1 }}>
|
| 171 |
+
<span style={{ color: session.isActive ? 'var(--mui-palette-success-main)' : 'var(--mui-palette-text-secondary)' }}>
|
| 172 |
+
{session.isActive ? 'RUNNING' : 'STOPPED'}
|
| 173 |
+
</span>
|
| 174 |
+
<span>·</span>
|
| 175 |
+
<span>{formatTime(session.createdAt)}</span>
|
| 176 |
</Typography>
|
| 177 |
+
}
|
| 178 |
+
/>
|
| 179 |
+
<ListItemSecondaryAction>
|
| 180 |
+
<IconButton
|
| 181 |
+
edge="end"
|
| 182 |
+
size="small"
|
| 183 |
+
onClick={(e) => handleDeleteSession(session.id, e)}
|
| 184 |
+
>
|
| 185 |
+
<DeleteIcon fontSize="small" />
|
| 186 |
+
</IconButton>
|
| 187 |
+
</ListItemSecondaryAction>
|
| 188 |
+
</ListItemButton>
|
| 189 |
+
</ListItem>
|
| 190 |
+
);
|
| 191 |
+
})}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
</List>
|
| 193 |
)}
|
| 194 |
</Box>
|
| 195 |
|
| 196 |
{/* Footer */}
|
| 197 |
<Divider />
|
| 198 |
+
<Box sx={{ p: 2, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
| 199 |
+
<Typography variant="caption" color="text.secondary" sx={{ fontFamily: 'monospace' }}>
|
| 200 |
+
{sessions.length} SESSION{sessions.length !== 1 ? 'S' : ''}
|
| 201 |
</Typography>
|
| 202 |
+
<Tooltip title="Undo last turn">
|
| 203 |
+
<span>
|
| 204 |
+
<IconButton
|
| 205 |
+
onClick={handleUndo}
|
| 206 |
+
disabled={!activeSessionId || isProcessing}
|
| 207 |
+
size="small"
|
| 208 |
+
>
|
| 209 |
+
<UndoIcon fontSize="small" />
|
| 210 |
+
</IconButton>
|
| 211 |
+
</span>
|
| 212 |
+
</Tooltip>
|
| 213 |
</Box>
|
| 214 |
</Box>
|
| 215 |
);
|
frontend/src/hooks/useAgentWebSocket.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
| 1 |
import { useCallback, useEffect, useRef } from 'react';
|
| 2 |
import { useAgentStore } from '@/store/agentStore';
|
| 3 |
import { useSessionStore } from '@/store/sessionStore';
|
|
|
|
| 4 |
import type { AgentEvent } from '@/types/events';
|
| 5 |
-
import type { Message } from '@/types/agent';
|
| 6 |
|
| 7 |
const WS_RECONNECT_DELAY = 1000;
|
| 8 |
const WS_MAX_RECONNECT_DELAY = 30000;
|
|
@@ -28,8 +29,14 @@ export function useAgentWebSocket({
|
|
| 28 |
setConnected,
|
| 29 |
setPendingApprovals,
|
| 30 |
setError,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
} = useAgentStore();
|
| 32 |
|
|
|
|
|
|
|
| 33 |
const { setSessionActive } = useSessionStore();
|
| 34 |
|
| 35 |
const handleEvent = useCallback(
|
|
@@ -46,15 +53,18 @@ export function useAgentWebSocket({
|
|
| 46 |
|
| 47 |
case 'processing':
|
| 48 |
setProcessing(true);
|
|
|
|
| 49 |
break;
|
| 50 |
|
| 51 |
case 'assistant_message': {
|
| 52 |
const content = (event.data?.content as string) || '';
|
|
|
|
| 53 |
const message: Message = {
|
| 54 |
id: `msg_${Date.now()}`,
|
| 55 |
role: 'assistant',
|
| 56 |
content,
|
| 57 |
timestamp: new Date().toISOString(),
|
|
|
|
| 58 |
};
|
| 59 |
addMessage(sessionId, message);
|
| 60 |
break;
|
|
@@ -62,16 +72,36 @@ export function useAgentWebSocket({
|
|
| 62 |
|
| 63 |
case 'tool_call': {
|
| 64 |
const toolName = (event.data?.tool as string) || 'unknown';
|
| 65 |
-
const args = event.data?.arguments || {};
|
| 66 |
-
const
|
| 67 |
id: `tool_${Date.now()}`,
|
| 68 |
-
|
| 69 |
-
|
|
|
|
| 70 |
timestamp: new Date().toISOString(),
|
| 71 |
-
toolName,
|
| 72 |
};
|
| 73 |
-
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
console.log('Tool call:', toolName, args);
|
| 76 |
break;
|
| 77 |
}
|
|
@@ -80,14 +110,7 @@ export function useAgentWebSocket({
|
|
| 80 |
const toolName = (event.data?.tool as string) || 'unknown';
|
| 81 |
const output = (event.data?.output as string) || '';
|
| 82 |
const success = event.data?.success as boolean;
|
| 83 |
-
|
| 84 |
-
id: `tool_out_${Date.now()}`,
|
| 85 |
-
role: 'tool',
|
| 86 |
-
content: output,
|
| 87 |
-
timestamp: new Date().toISOString(),
|
| 88 |
-
toolName,
|
| 89 |
-
};
|
| 90 |
-
addMessage(sessionId, message);
|
| 91 |
console.log('Tool output:', toolName, success);
|
| 92 |
break;
|
| 93 |
}
|
|
|
|
| 1 |
import { useCallback, useEffect, useRef } from 'react';
|
| 2 |
import { useAgentStore } from '@/store/agentStore';
|
| 3 |
import { useSessionStore } from '@/store/sessionStore';
|
| 4 |
+
import { useLayoutStore } from '@/store/layoutStore';
|
| 5 |
import type { AgentEvent } from '@/types/events';
|
| 6 |
+
import type { Message, TraceLog } from '@/types/agent';
|
| 7 |
|
| 8 |
const WS_RECONNECT_DELAY = 1000;
|
| 9 |
const WS_MAX_RECONNECT_DELAY = 30000;
|
|
|
|
| 29 |
setConnected,
|
| 30 |
setPendingApprovals,
|
| 31 |
setError,
|
| 32 |
+
addTraceLog,
|
| 33 |
+
clearTraceLogs,
|
| 34 |
+
setPanelContent,
|
| 35 |
+
traceLogs,
|
| 36 |
} = useAgentStore();
|
| 37 |
|
| 38 |
+
const { setRightPanelOpen, setLeftSidebarOpen } = useLayoutStore();
|
| 39 |
+
|
| 40 |
const { setSessionActive } = useSessionStore();
|
| 41 |
|
| 42 |
const handleEvent = useCallback(
|
|
|
|
| 53 |
|
| 54 |
case 'processing':
|
| 55 |
setProcessing(true);
|
| 56 |
+
clearTraceLogs();
|
| 57 |
break;
|
| 58 |
|
| 59 |
case 'assistant_message': {
|
| 60 |
const content = (event.data?.content as string) || '';
|
| 61 |
+
const currentTrace = useAgentStore.getState().traceLogs;
|
| 62 |
const message: Message = {
|
| 63 |
id: `msg_${Date.now()}`,
|
| 64 |
role: 'assistant',
|
| 65 |
content,
|
| 66 |
timestamp: new Date().toISOString(),
|
| 67 |
+
trace: currentTrace.length > 0 ? [...currentTrace] : undefined,
|
| 68 |
};
|
| 69 |
addMessage(sessionId, message);
|
| 70 |
break;
|
|
|
|
| 72 |
|
| 73 |
case 'tool_call': {
|
| 74 |
const toolName = (event.data?.tool as string) || 'unknown';
|
| 75 |
+
const args = (event.data?.arguments as Record<string, any>) || {};
|
| 76 |
+
const log: TraceLog = {
|
| 77 |
id: `tool_${Date.now()}`,
|
| 78 |
+
type: 'call',
|
| 79 |
+
text: `Calling ${toolName} with ${JSON.stringify(args)}`,
|
| 80 |
+
tool: toolName,
|
| 81 |
timestamp: new Date().toISOString(),
|
|
|
|
| 82 |
};
|
| 83 |
+
addTraceLog(log);
|
| 84 |
+
|
| 85 |
+
// Auto-expand Right Panel for specific tools
|
| 86 |
+
if (toolName === 'hf_jobs' && (args.operation === 'run' || args.operation === 'scheduled run') && args.script) {
|
| 87 |
+
setPanelContent({
|
| 88 |
+
title: 'Compute Job Script',
|
| 89 |
+
content: args.script,
|
| 90 |
+
language: 'python'
|
| 91 |
+
});
|
| 92 |
+
setRightPanelOpen(true);
|
| 93 |
+
setLeftSidebarOpen(false);
|
| 94 |
+
} else if (toolName === 'hf_repo_files' && args.operation === 'upload' && args.content) {
|
| 95 |
+
setPanelContent({
|
| 96 |
+
title: `File Upload: ${args.path || 'unnamed'} `,
|
| 97 |
+
content: args.content,
|
| 98 |
+
parameters: args,
|
| 99 |
+
language: args.path?.endsWith('.py') ? 'python' : undefined
|
| 100 |
+
});
|
| 101 |
+
setRightPanelOpen(true);
|
| 102 |
+
setLeftSidebarOpen(false);
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
console.log('Tool call:', toolName, args);
|
| 106 |
break;
|
| 107 |
}
|
|
|
|
| 110 |
const toolName = (event.data?.tool as string) || 'unknown';
|
| 111 |
const output = (event.data?.output as string) || '';
|
| 112 |
const success = event.data?.success as boolean;
|
| 113 |
+
// Only log output to console, not to trace logs per user request
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
console.log('Tool output:', toolName, success);
|
| 115 |
break;
|
| 116 |
}
|
frontend/src/store/agentStore.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
import { create } from 'zustand';
|
| 2 |
-
import type { Message, ApprovalBatch, User } from '@/types/agent';
|
| 3 |
|
| 4 |
interface AgentStore {
|
| 5 |
// State per session (keyed by session ID)
|
|
@@ -9,6 +9,8 @@ interface AgentStore {
|
|
| 9 |
pendingApprovals: ApprovalBatch | null;
|
| 10 |
user: User | null;
|
| 11 |
error: string | null;
|
|
|
|
|
|
|
| 12 |
|
| 13 |
// Actions
|
| 14 |
addMessage: (sessionId: string, message: Message) => void;
|
|
@@ -19,6 +21,9 @@ interface AgentStore {
|
|
| 19 |
setUser: (user: User | null) => void;
|
| 20 |
setError: (error: string | null) => void;
|
| 21 |
getMessages: (sessionId: string) => Message[];
|
|
|
|
|
|
|
|
|
|
| 22 |
}
|
| 23 |
|
| 24 |
export const useAgentStore = create<AgentStore>((set, get) => ({
|
|
@@ -28,6 +33,8 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
|
| 28 |
pendingApprovals: null,
|
| 29 |
user: null,
|
| 30 |
error: null,
|
|
|
|
|
|
|
| 31 |
|
| 32 |
addMessage: (sessionId: string, message: Message) => {
|
| 33 |
set((state) => {
|
|
@@ -73,4 +80,18 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
|
| 73 |
getMessages: (sessionId: string) => {
|
| 74 |
return get().messagesBySession[sessionId] || [];
|
| 75 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
}));
|
|
|
|
| 1 |
import { create } from 'zustand';
|
| 2 |
+
import type { Message, ApprovalBatch, User, TraceLog } from '@/types/agent';
|
| 3 |
|
| 4 |
interface AgentStore {
|
| 5 |
// State per session (keyed by session ID)
|
|
|
|
| 9 |
pendingApprovals: ApprovalBatch | null;
|
| 10 |
user: User | null;
|
| 11 |
error: string | null;
|
| 12 |
+
traceLogs: TraceLog[];
|
| 13 |
+
panelContent: { title: string; content: string; language?: string; parameters?: any } | null;
|
| 14 |
|
| 15 |
// Actions
|
| 16 |
addMessage: (sessionId: string, message: Message) => void;
|
|
|
|
| 21 |
setUser: (user: User | null) => void;
|
| 22 |
setError: (error: string | null) => void;
|
| 23 |
getMessages: (sessionId: string) => Message[];
|
| 24 |
+
addTraceLog: (log: TraceLog) => void;
|
| 25 |
+
clearTraceLogs: () => void;
|
| 26 |
+
setPanelContent: (content: { title: string; content: string; language?: string; parameters?: any } | null) => void;
|
| 27 |
}
|
| 28 |
|
| 29 |
export const useAgentStore = create<AgentStore>((set, get) => ({
|
|
|
|
| 33 |
pendingApprovals: null,
|
| 34 |
user: null,
|
| 35 |
error: null,
|
| 36 |
+
traceLogs: [],
|
| 37 |
+
panelContent: null,
|
| 38 |
|
| 39 |
addMessage: (sessionId: string, message: Message) => {
|
| 40 |
set((state) => {
|
|
|
|
| 80 |
getMessages: (sessionId: string) => {
|
| 81 |
return get().messagesBySession[sessionId] || [];
|
| 82 |
},
|
| 83 |
+
|
| 84 |
+
addTraceLog: (log: TraceLog) => {
|
| 85 |
+
set((state) => ({
|
| 86 |
+
traceLogs: [...state.traceLogs, log],
|
| 87 |
+
}));
|
| 88 |
+
},
|
| 89 |
+
|
| 90 |
+
clearTraceLogs: () => {
|
| 91 |
+
set({ traceLogs: [] });
|
| 92 |
+
},
|
| 93 |
+
|
| 94 |
+
setPanelContent: (content) => {
|
| 95 |
+
set({ panelContent: content });
|
| 96 |
+
},
|
| 97 |
}));
|
frontend/src/store/layoutStore.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { create } from 'zustand';
|
| 2 |
+
|
| 3 |
+
interface LayoutStore {
|
| 4 |
+
isLeftSidebarOpen: boolean;
|
| 5 |
+
isRightPanelOpen: boolean;
|
| 6 |
+
rightPanelWidth: number;
|
| 7 |
+
setLeftSidebarOpen: (open: boolean) => void;
|
| 8 |
+
setRightPanelOpen: (open: boolean) => void;
|
| 9 |
+
setRightPanelWidth: (width: number) => void;
|
| 10 |
+
toggleLeftSidebar: () => void;
|
| 11 |
+
toggleRightPanel: () => void;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
export const useLayoutStore = create<LayoutStore>((set) => ({
|
| 15 |
+
isLeftSidebarOpen: true,
|
| 16 |
+
isRightPanelOpen: false,
|
| 17 |
+
rightPanelWidth: 450,
|
| 18 |
+
setLeftSidebarOpen: (open) => set({ isLeftSidebarOpen: open }),
|
| 19 |
+
setRightPanelOpen: (open) => set({ isRightPanelOpen: open }),
|
| 20 |
+
setRightPanelWidth: (width) => set({ rightPanelWidth: width }),
|
| 21 |
+
toggleLeftSidebar: () => set((state) => ({ isLeftSidebarOpen: !state.isLeftSidebarOpen })),
|
| 22 |
+
toggleRightPanel: () => set((state) => ({ isRightPanelOpen: !state.isRightPanelOpen })),
|
| 23 |
+
}));
|
frontend/src/theme.ts
CHANGED
|
@@ -4,9 +4,9 @@ const theme = createTheme({
|
|
| 4 |
palette: {
|
| 5 |
mode: 'dark',
|
| 6 |
primary: {
|
| 7 |
-
main: '#
|
| 8 |
-
light: '#
|
| 9 |
-
dark: '#
|
| 10 |
},
|
| 11 |
secondary: {
|
| 12 |
main: '#FF9D00',
|
|
@@ -21,7 +21,7 @@ const theme = createTheme({
|
|
| 21 |
},
|
| 22 |
divider: '#30363D',
|
| 23 |
success: {
|
| 24 |
-
main: '#
|
| 25 |
},
|
| 26 |
error: {
|
| 27 |
main: '#F85149',
|
|
@@ -34,7 +34,7 @@ const theme = createTheme({
|
|
| 34 |
},
|
| 35 |
},
|
| 36 |
typography: {
|
| 37 |
-
fontFamily: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
| 38 |
h1: {
|
| 39 |
fontWeight: 600,
|
| 40 |
},
|
|
@@ -59,6 +59,9 @@ const theme = createTheme({
|
|
| 59 |
body2: {
|
| 60 |
fontSize: '0.875rem',
|
| 61 |
},
|
|
|
|
|
|
|
|
|
|
| 62 |
},
|
| 63 |
components: {
|
| 64 |
MuiCssBaseline: {
|
|
@@ -71,7 +74,7 @@ const theme = createTheme({
|
|
| 71 |
},
|
| 72 |
'&::-webkit-scrollbar-thumb': {
|
| 73 |
backgroundColor: '#30363D',
|
| 74 |
-
borderRadius: '
|
| 75 |
},
|
| 76 |
'&::-webkit-scrollbar-track': {
|
| 77 |
backgroundColor: 'transparent',
|
|
@@ -85,8 +88,10 @@ const theme = createTheme({
|
|
| 85 |
MuiButton: {
|
| 86 |
styleOverrides: {
|
| 87 |
root: {
|
| 88 |
-
textTransform: '
|
| 89 |
-
fontWeight:
|
|
|
|
|
|
|
| 90 |
},
|
| 91 |
},
|
| 92 |
},
|
|
@@ -106,7 +111,7 @@ const theme = createTheme({
|
|
| 106 |
},
|
| 107 |
},
|
| 108 |
shape: {
|
| 109 |
-
borderRadius:
|
| 110 |
},
|
| 111 |
});
|
| 112 |
|
|
|
|
| 4 |
palette: {
|
| 5 |
mode: 'dark',
|
| 6 |
primary: {
|
| 7 |
+
main: '#FEE133',
|
| 8 |
+
light: '#FFF066',
|
| 9 |
+
dark: '#B29F24',
|
| 10 |
},
|
| 11 |
secondary: {
|
| 12 |
main: '#FF9D00',
|
|
|
|
| 21 |
},
|
| 22 |
divider: '#30363D',
|
| 23 |
success: {
|
| 24 |
+
main: '#2EA043', // Muted green
|
| 25 |
},
|
| 26 |
error: {
|
| 27 |
main: '#F85149',
|
|
|
|
| 34 |
},
|
| 35 |
},
|
| 36 |
typography: {
|
| 37 |
+
fontFamily: '"IBM Plex Sans", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
| 38 |
h1: {
|
| 39 |
fontWeight: 600,
|
| 40 |
},
|
|
|
|
| 59 |
body2: {
|
| 60 |
fontSize: '0.875rem',
|
| 61 |
},
|
| 62 |
+
button: {
|
| 63 |
+
fontFamily: '"JetBrains Mono", "IBM Plex Sans", monospace',
|
| 64 |
+
},
|
| 65 |
},
|
| 66 |
components: {
|
| 67 |
MuiCssBaseline: {
|
|
|
|
| 74 |
},
|
| 75 |
'&::-webkit-scrollbar-thumb': {
|
| 76 |
backgroundColor: '#30363D',
|
| 77 |
+
borderRadius: '2px',
|
| 78 |
},
|
| 79 |
'&::-webkit-scrollbar-track': {
|
| 80 |
backgroundColor: 'transparent',
|
|
|
|
| 88 |
MuiButton: {
|
| 89 |
styleOverrides: {
|
| 90 |
root: {
|
| 91 |
+
textTransform: 'uppercase',
|
| 92 |
+
fontWeight: 600,
|
| 93 |
+
letterSpacing: '0.05em',
|
| 94 |
+
fontSize: '0.75rem',
|
| 95 |
},
|
| 96 |
},
|
| 97 |
},
|
|
|
|
| 111 |
},
|
| 112 |
},
|
| 113 |
shape: {
|
| 114 |
+
borderRadius: 2,
|
| 115 |
},
|
| 116 |
});
|
| 117 |
|
frontend/src/types/agent.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface Message {
|
|
| 16 |
timestamp: string;
|
| 17 |
toolName?: string;
|
| 18 |
toolCallId?: string;
|
|
|
|
| 19 |
}
|
| 20 |
|
| 21 |
export interface ToolCall {
|
|
@@ -41,6 +42,14 @@ export interface ApprovalBatch {
|
|
| 41 |
count: number;
|
| 42 |
}
|
| 43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
export interface User {
|
| 45 |
authenticated: boolean;
|
| 46 |
username?: string;
|
|
|
|
| 16 |
timestamp: string;
|
| 17 |
toolName?: string;
|
| 18 |
toolCallId?: string;
|
| 19 |
+
trace?: TraceLog[];
|
| 20 |
}
|
| 21 |
|
| 22 |
export interface ToolCall {
|
|
|
|
| 42 |
count: number;
|
| 43 |
}
|
| 44 |
|
| 45 |
+
export interface TraceLog {
|
| 46 |
+
id: string;
|
| 47 |
+
type: 'call' | 'output';
|
| 48 |
+
text: string;
|
| 49 |
+
tool: string;
|
| 50 |
+
timestamp: string;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
export interface User {
|
| 54 |
authenticated: boolean;
|
| 55 |
username?: string;
|