@meshxdata/fops 0.1.47 → 0.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +368 -0
- package/package.json +6 -1
- package/src/commands/lifecycle.js +30 -11
- package/src/plugins/bundled/fops-plugin-azure/lib/commands/test-cmds.js +19 -27
- package/src/plugins/bundled/fops-plugin-azure/lib/pytest-parse.js +21 -6
- package/src/plugins/__test-fixtures__/fake-plugin.js +0 -2
- package/src/plugins/__test-fixtures__/no-register-plugin.js +0 -2
- package/src/plugins/__test-fixtures__/with-register/index.js +0 -2
- package/src/plugins/__test-fixtures__/without-register/index.js +0 -2
- package/src/plugins/bundled/fops-plugin-foundation/test-helpers.js +0 -65
- package/src/web/frontend/index.html +0 -16
- package/src/web/frontend/src/App.jsx +0 -445
- package/src/web/frontend/src/components/ChatView.jsx +0 -910
- package/src/web/frontend/src/components/InputBox.jsx +0 -523
- package/src/web/frontend/src/components/Sidebar.jsx +0 -410
- package/src/web/frontend/src/components/StatusBar.jsx +0 -37
- package/src/web/frontend/src/components/TabBar.jsx +0 -87
- package/src/web/frontend/src/hooks/useWebSocket.js +0 -412
- package/src/web/frontend/src/index.css +0 -78
- package/src/web/frontend/src/main.jsx +0 -6
- package/src/web/frontend/vite.config.js +0 -21
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
2
|
-
import { useWebSocket } from "./hooks/useWebSocket.js";
|
|
3
|
-
import TabBar from "./components/TabBar.jsx";
|
|
4
|
-
import ChatView from "./components/ChatView.jsx";
|
|
5
|
-
import InputBox from "./components/InputBox.jsx";
|
|
6
|
-
import StatusBar from "./components/StatusBar.jsx";
|
|
7
|
-
import Sidebar from "./components/Sidebar.jsx";
|
|
8
|
-
|
|
9
|
-
function buildSmartPlan(goal, contextLines = [], foundationStats = null) {
|
|
10
|
-
const g = String(goal || "").toLowerCase();
|
|
11
|
-
const includesAny = (...terms) => terms.some((t) => g.includes(t));
|
|
12
|
-
|
|
13
|
-
let steps = [];
|
|
14
|
-
let checks = [];
|
|
15
|
-
|
|
16
|
-
if (includesAny("cargo", "landscape", "mesh", "graph", "catalog")) {
|
|
17
|
-
steps = [
|
|
18
|
-
"Lock scope to the target domain/entity names (for example, `cargo` mesh/products).",
|
|
19
|
-
"Inspect current landscape and health to find concrete broken nodes before changing anything.",
|
|
20
|
-
"Open failing products/objects and capture exact failure causes from status + compute logs.",
|
|
21
|
-
"Apply minimal targeted fixes only to impacted entities, then re-run compute.",
|
|
22
|
-
"Confirm recovery with fresh status and sample data checks.",
|
|
23
|
-
];
|
|
24
|
-
checks = [
|
|
25
|
-
"Search and enumerate entities first (`foundation_search`, mesh/product lists).",
|
|
26
|
-
"Validate landscape + product state (`landscape`, `data_product get`, compute status).",
|
|
27
|
-
"Verify success criteria: no FAILED nodes and expected sample rows return.",
|
|
28
|
-
];
|
|
29
|
-
} else if (includesAny("fix", "bug", "regression", "error", "failing", "test")) {
|
|
30
|
-
steps = [
|
|
31
|
-
"Reproduce the issue and pin down exact failing behavior.",
|
|
32
|
-
"Trace root cause in the smallest relevant code/config surface.",
|
|
33
|
-
"Implement the minimal safe fix with clear reasoning.",
|
|
34
|
-
"Add/update regression coverage for the failure mode.",
|
|
35
|
-
"Run focused verification and summarize residual risk.",
|
|
36
|
-
];
|
|
37
|
-
checks = [
|
|
38
|
-
"Before/after reproduction output.",
|
|
39
|
-
"Targeted tests for the touched path.",
|
|
40
|
-
"No unintended behavior regressions in nearby flows.",
|
|
41
|
-
];
|
|
42
|
-
} else if (includesAny("deploy", "release", "up", "down", "docker", "azure", "aws")) {
|
|
43
|
-
steps = [
|
|
44
|
-
"Check environment readiness and current service state.",
|
|
45
|
-
"Execute the requested operation with safe, observable steps.",
|
|
46
|
-
"Watch logs/health signals until state stabilizes.",
|
|
47
|
-
"Rollback or corrective step if health checks fail.",
|
|
48
|
-
"Report final status with follow-up actions.",
|
|
49
|
-
];
|
|
50
|
-
checks = [
|
|
51
|
-
"Service/container health is green.",
|
|
52
|
-
"Critical endpoint smoke checks pass.",
|
|
53
|
-
"Any failed component has a documented next action.",
|
|
54
|
-
];
|
|
55
|
-
} else {
|
|
56
|
-
steps = [
|
|
57
|
-
"Define scope and non-goals from your request.",
|
|
58
|
-
"Inspect current state and constraints in the relevant area.",
|
|
59
|
-
"Draft the smallest execution sequence that achieves the goal.",
|
|
60
|
-
"Execute with checkpoints and quick feedback loops.",
|
|
61
|
-
"Verify outcome against explicit acceptance criteria.",
|
|
62
|
-
];
|
|
63
|
-
checks = [
|
|
64
|
-
"Goal-specific acceptance criteria are met.",
|
|
65
|
-
"No new regressions introduced in adjacent flows.",
|
|
66
|
-
"Clear rollback path exists if needed.",
|
|
67
|
-
];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const statsBits = [];
|
|
71
|
-
const meshes = foundationStats?.meshes?.total ?? foundationStats?.meshes ?? null;
|
|
72
|
-
const products = foundationStats?.dataProducts?.total ?? foundationStats?.dataProducts ?? null;
|
|
73
|
-
const sources = foundationStats?.dataSources?.total ?? foundationStats?.dataSources ?? null;
|
|
74
|
-
if (meshes != null) statsBits.push(`${meshes} mesh${meshes === 1 ? "" : "es"}`);
|
|
75
|
-
if (products != null) statsBits.push(`${products} product${products === 1 ? "" : "s"}`);
|
|
76
|
-
if (sources != null) statsBits.push(`${sources} source${sources === 1 ? "" : "s"}`);
|
|
77
|
-
|
|
78
|
-
return [
|
|
79
|
-
"### Plan",
|
|
80
|
-
`**Goal:** ${goal}`,
|
|
81
|
-
"",
|
|
82
|
-
...(statsBits.length > 0 ? [`**Current landscape:** ${statsBits.join(" · ")}`, ""] : []),
|
|
83
|
-
...(contextLines.length > 0
|
|
84
|
-
? [
|
|
85
|
-
"### Known Context (embeddings/graph)",
|
|
86
|
-
...contextLines.map((line) => `- ${line}`),
|
|
87
|
-
"",
|
|
88
|
-
]
|
|
89
|
-
: []),
|
|
90
|
-
...steps.map((step, i) => `${i + 1}. ${step}`),
|
|
91
|
-
"",
|
|
92
|
-
"### Verification",
|
|
93
|
-
...checks.map((c) => `- ${c}`),
|
|
94
|
-
"",
|
|
95
|
-
"Reply with **run it** and I’ll execute this plan now.",
|
|
96
|
-
].join("\n");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export default function App() {
|
|
100
|
-
const {
|
|
101
|
-
connected,
|
|
102
|
-
sessions,
|
|
103
|
-
activeSession,
|
|
104
|
-
agents,
|
|
105
|
-
messages,
|
|
106
|
-
setMessages,
|
|
107
|
-
streamingText,
|
|
108
|
-
thinkingText,
|
|
109
|
-
statusText,
|
|
110
|
-
toolCalls,
|
|
111
|
-
isStreaming,
|
|
112
|
-
services,
|
|
113
|
-
foundationStats,
|
|
114
|
-
updateAvailable,
|
|
115
|
-
pendingConfirm,
|
|
116
|
-
respondConfirm,
|
|
117
|
-
sendMessage,
|
|
118
|
-
sendOllamaSetup,
|
|
119
|
-
sendStackUp,
|
|
120
|
-
sendStackDown,
|
|
121
|
-
sendBootstrap,
|
|
122
|
-
sendTrain,
|
|
123
|
-
sendLearningEvent,
|
|
124
|
-
appendMessage,
|
|
125
|
-
createSession,
|
|
126
|
-
switchSession,
|
|
127
|
-
closeSession,
|
|
128
|
-
cancelStream,
|
|
129
|
-
} = useWebSocket();
|
|
130
|
-
|
|
131
|
-
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => window.innerWidth < 768);
|
|
132
|
-
const [meshPickerOpen, setMeshPickerOpen] = useState(false);
|
|
133
|
-
const [queuedFollowUps, setQueuedFollowUps] = useState([]);
|
|
134
|
-
const queuedDispatchInFlightRef = useRef(false);
|
|
135
|
-
const activeSessionData = sessions.find((s) => s.id === activeSession);
|
|
136
|
-
|
|
137
|
-
// Fetch entity detail for sidebar tree expansion
|
|
138
|
-
const fetchEntityDetail = useCallback(async (type, identifier) => {
|
|
139
|
-
const res = await fetch(`/api/entity/${encodeURIComponent(type)}/${encodeURIComponent(identifier)}`);
|
|
140
|
-
if (!res.ok) throw new Error("Failed to fetch entity");
|
|
141
|
-
return res.json();
|
|
142
|
-
}, []);
|
|
143
|
-
|
|
144
|
-
// Slash command handler — intercept commands before sending to LLM
|
|
145
|
-
const handleSubmit = useCallback((text) => {
|
|
146
|
-
const raw = String(text || "").trim();
|
|
147
|
-
const normalized = raw.replace(/^\/\s+/, "/").replace(/\s+/g, " ");
|
|
148
|
-
const cmdToken = (normalized.split(/\s+/)[0] || "").toLowerCase().replace(/\?+$/, "");
|
|
149
|
-
const recordLearning = (type, payload = {}) => {
|
|
150
|
-
sendLearningEvent(type, payload, activeSession);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
if (normalized.startsWith("/")) {
|
|
154
|
-
recordLearning("slash_command", { command: cmdToken || normalized });
|
|
155
|
-
} else if (raw) {
|
|
156
|
-
recordLearning("user_intent", { message: raw.slice(0, 500) });
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (cmdToken === "/clear") {
|
|
160
|
-
setMessages([]);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
if (cmdToken === "/agent" && normalized === "/agent") {
|
|
164
|
-
setMessages((prev) => [...prev, { role: "assistant", content: "**Usage**: `/agent <name>`\n\nType `/agents` to see available agents." }]);
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (cmdToken === "/agent" && normalized.length > "/agent".length) {
|
|
168
|
-
const agentName = normalized.slice("/agent".length).trim();
|
|
169
|
-
if (!agentName) {
|
|
170
|
-
setMessages((prev) => [...prev, { role: "assistant", content: "**Usage**: `/agent <name>`\n\nType `/agents` to see available agents." }]);
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
setMessages((prev) => [...prev, { role: "user", content: `Switched to **${agentName}** agent` }]);
|
|
174
|
-
createSession(agentName);
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
if (cmdToken === "/agents") {
|
|
178
|
-
const renderAgents = (list) => {
|
|
179
|
-
const agentList = list.length > 0
|
|
180
|
-
? list.map((a) => `- **${a.name}** — ${a.description || ""}`).join("\n")
|
|
181
|
-
: "No agents available.";
|
|
182
|
-
setMessages((prev) => [...prev, { role: "assistant", content: `### Available Agents\n${agentList}\n\nUse \`/agent <name>\` to open an agent session.` }]);
|
|
183
|
-
};
|
|
184
|
-
if (agents.length > 0) {
|
|
185
|
-
renderAgents(agents);
|
|
186
|
-
} else {
|
|
187
|
-
fetch("/api/agents")
|
|
188
|
-
.then((r) => (r.ok ? r.json() : []))
|
|
189
|
-
.then((list) => renderAgents(Array.isArray(list) ? list : []))
|
|
190
|
-
.catch(() => renderAgents([]));
|
|
191
|
-
}
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
if (cmdToken === "/mesh") {
|
|
195
|
-
const meshItems = foundationStats?.meshes?.items || [];
|
|
196
|
-
if (meshItems.length > 0) {
|
|
197
|
-
setMeshPickerOpen(true);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
sendMessage("Show me the landscape for a mesh. List meshes first if needed.");
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
if (cmdToken === "/help") {
|
|
204
|
-
setMessages((prev) => [...prev, {
|
|
205
|
-
role: "assistant",
|
|
206
|
-
content: "### Commands\n"
|
|
207
|
-
+ "- `/up` — Start Foundation stack\n"
|
|
208
|
-
+ "- `/down` — Stop Foundation stack\n"
|
|
209
|
-
+ "- `/mesh` — Generate landscape for a mesh (pick from list)\n"
|
|
210
|
-
+ "- `/bootstrap` — Create demo data mesh (~5 min)\n"
|
|
211
|
-
+ "- `/plan <goal>` — Draft plan before execution\n"
|
|
212
|
-
+ "- `/agents` — List available agents\n"
|
|
213
|
-
+ "- `/agent <name>` — Open agent in new session\n"
|
|
214
|
-
+ "- `/ollama` — Auto-detect GPU, install Ollama & pull a model\n"
|
|
215
|
-
+ "- `/train` — Fine-tune embedding model on Foundation domain\n"
|
|
216
|
-
+ "- `/clear` — Clear chat\n"
|
|
217
|
-
+ "- `/help` — Show this help\n\n"
|
|
218
|
-
+ "### Keyboard Shortcuts\n"
|
|
219
|
-
+ "- `Ctrl+B` — Toggle sidebar\n"
|
|
220
|
-
+ "- `Enter` — Send message\n"
|
|
221
|
-
+ "- `Shift+Enter` — New line",
|
|
222
|
-
}]);
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
if (cmdToken === "/plan") {
|
|
226
|
-
const goal = normalized.slice("/plan".length).trim();
|
|
227
|
-
if (!goal) {
|
|
228
|
-
setMessages((prev) => [...prev, {
|
|
229
|
-
role: "assistant",
|
|
230
|
-
content: "**Usage**: `/plan <goal>`\n\nExample: `/plan fix watcher stale submitted status and add tests`",
|
|
231
|
-
}]);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
recordLearning("plan_requested", { goal: goal.slice(0, 500) });
|
|
235
|
-
setMessages((prev) => [...prev, { role: "user", content: normalized }]);
|
|
236
|
-
if (activeSession) appendMessage(activeSession, "user", normalized);
|
|
237
|
-
fetch("/api/plan", {
|
|
238
|
-
method: "POST",
|
|
239
|
-
headers: { "Content-Type": "application/json" },
|
|
240
|
-
body: JSON.stringify({ goal }),
|
|
241
|
-
})
|
|
242
|
-
.then(async (r) => {
|
|
243
|
-
const data = await r.json().catch(() => ({}));
|
|
244
|
-
if (!r.ok) throw Object.assign(new Error(data?.error || "plan_failed"), { data });
|
|
245
|
-
return data;
|
|
246
|
-
})
|
|
247
|
-
.then((data) => {
|
|
248
|
-
const plan = String(data?.plan || "").trim();
|
|
249
|
-
const lines = Array.isArray(data?.context) ? data.context.slice(0, 6) : [];
|
|
250
|
-
const content = plan || buildSmartPlan(goal, lines, data?.foundationStats || foundationStats || null);
|
|
251
|
-
setMessages((prev) => [...prev, {
|
|
252
|
-
role: "assistant",
|
|
253
|
-
content,
|
|
254
|
-
}]);
|
|
255
|
-
if (activeSession) appendMessage(activeSession, "assistant", content);
|
|
256
|
-
})
|
|
257
|
-
.catch((err) => {
|
|
258
|
-
const fallbackCtx = Array.isArray(err?.data?.context) ? err.data.context.slice(0, 6) : [];
|
|
259
|
-
const fallbackStats = err?.data?.foundationStats || foundationStats || null;
|
|
260
|
-
const content = buildSmartPlan(goal, fallbackCtx, fallbackStats);
|
|
261
|
-
setMessages((prev) => [...prev, {
|
|
262
|
-
role: "assistant",
|
|
263
|
-
content,
|
|
264
|
-
}]);
|
|
265
|
-
if (activeSession) appendMessage(activeSession, "assistant", content);
|
|
266
|
-
});
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
if (cmdToken === "/exit" || cmdToken === "/quit") {
|
|
270
|
-
// No-op in web — can't close a browser tab
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
if (cmdToken === "/sessions") {
|
|
274
|
-
setMessages((prev) => [...prev, { role: "assistant", content: "`/sessions` is only available in TUI mode." }]);
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
if (cmdToken === "/up") {
|
|
278
|
-
setMessages((prev) => [...prev, { role: "assistant", content: "### Starting stack...\nRunning `docker compose up -d --remove-orphans`" }]);
|
|
279
|
-
sendStackUp();
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
if (cmdToken === "/down") {
|
|
283
|
-
setMessages((prev) => [...prev, { role: "assistant", content: "### Stopping stack...\nRunning `docker compose down --remove-orphans`" }]);
|
|
284
|
-
sendStackDown();
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
if (cmdToken === "/bootstrap") {
|
|
288
|
-
setMessages((prev) => [...prev, { role: "assistant", content: "### Bootstrap\nStarting Foundation bootstrap... (this takes ~5 min)" }]);
|
|
289
|
-
sendBootstrap();
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
if (cmdToken === "/train") {
|
|
293
|
-
setMessages((prev) => [...prev, { role: "assistant", content: "### Embedding Training\nStarting training pipeline..." }]);
|
|
294
|
-
sendTrain();
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
if (cmdToken === "/ollama") {
|
|
298
|
-
setMessages((prev) => [...prev, { role: "assistant", content: "### Ollama Setup\nDetecting environment..." }]);
|
|
299
|
-
sendOllamaSetup();
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
// Catch-all: unrecognized slash commands → show error instead of sending to LLM
|
|
303
|
-
if (normalized.startsWith("/")) {
|
|
304
|
-
const cmd = normalized.split(/\s/)[0];
|
|
305
|
-
setMessages((prev) => [...prev, { role: "assistant", content: `Unknown command \`${cmd}\`. Type \`/help\` to see available commands.` }]);
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
// While streaming, queue follow-ups so users can keep typing naturally.
|
|
309
|
-
if (isStreaming) {
|
|
310
|
-
setQueuedFollowUps((prev) => [...prev, raw]);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
// Regular message → send to LLM
|
|
314
|
-
sendMessage(raw);
|
|
315
|
-
}, [agents, sendMessage, sendOllamaSetup, sendStackUp, sendStackDown, sendBootstrap, sendTrain, sendLearningEvent, appendMessage, createSession, setMessages, foundationStats, activeSession, isStreaming]);
|
|
316
|
-
|
|
317
|
-
// Drain queued follow-ups one by one after each stream completes.
|
|
318
|
-
useEffect(() => {
|
|
319
|
-
if (isStreaming || queuedDispatchInFlightRef.current || queuedFollowUps.length === 0) return;
|
|
320
|
-
queuedDispatchInFlightRef.current = true;
|
|
321
|
-
const [next, ...rest] = queuedFollowUps;
|
|
322
|
-
setQueuedFollowUps(rest);
|
|
323
|
-
sendMessage(next);
|
|
324
|
-
const timer = setTimeout(() => {
|
|
325
|
-
queuedDispatchInFlightRef.current = false;
|
|
326
|
-
}, 2500);
|
|
327
|
-
return () => clearTimeout(timer);
|
|
328
|
-
}, [isStreaming, queuedFollowUps, sendMessage]);
|
|
329
|
-
|
|
330
|
-
useEffect(() => {
|
|
331
|
-
if (isStreaming) {
|
|
332
|
-
queuedDispatchInFlightRef.current = false;
|
|
333
|
-
}
|
|
334
|
-
}, [isStreaming]);
|
|
335
|
-
|
|
336
|
-
useEffect(() => {
|
|
337
|
-
// Keep follow-up queue scoped to the active session tab.
|
|
338
|
-
setQueuedFollowUps([]);
|
|
339
|
-
queuedDispatchInFlightRef.current = false;
|
|
340
|
-
}, [activeSession]);
|
|
341
|
-
|
|
342
|
-
// Keyboard shortcut: Ctrl+B to toggle sidebar; Escape to close mesh picker
|
|
343
|
-
useEffect(() => {
|
|
344
|
-
const handler = (e) => {
|
|
345
|
-
if (e.ctrlKey && e.key === "b") {
|
|
346
|
-
e.preventDefault();
|
|
347
|
-
setSidebarCollapsed((v) => !v);
|
|
348
|
-
}
|
|
349
|
-
if (e.key === "Escape" && meshPickerOpen) {
|
|
350
|
-
setMeshPickerOpen(false);
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
window.addEventListener("keydown", handler);
|
|
354
|
-
return () => window.removeEventListener("keydown", handler);
|
|
355
|
-
}, [meshPickerOpen]);
|
|
356
|
-
|
|
357
|
-
return (
|
|
358
|
-
<div className="flex h-screen bg-[#08080c] noise">
|
|
359
|
-
{/* Sidebar */}
|
|
360
|
-
<Sidebar
|
|
361
|
-
sessions={sessions}
|
|
362
|
-
activeId={activeSession}
|
|
363
|
-
agents={agents}
|
|
364
|
-
services={services}
|
|
365
|
-
foundationStats={foundationStats}
|
|
366
|
-
collapsed={sidebarCollapsed}
|
|
367
|
-
onToggle={() => setSidebarCollapsed((v) => !v)}
|
|
368
|
-
onSwitchSession={switchSession}
|
|
369
|
-
onCreateSession={createSession}
|
|
370
|
-
onFetchEntity={fetchEntityDetail}
|
|
371
|
-
onRunCommand={handleSubmit}
|
|
372
|
-
/>
|
|
373
|
-
|
|
374
|
-
{/* Main content */}
|
|
375
|
-
<div className="flex flex-col flex-1 min-w-0">
|
|
376
|
-
<TabBar
|
|
377
|
-
sessions={sessions}
|
|
378
|
-
activeId={activeSession}
|
|
379
|
-
agents={agents}
|
|
380
|
-
onSwitch={switchSession}
|
|
381
|
-
onAdd={createSession}
|
|
382
|
-
onClose={closeSession}
|
|
383
|
-
/>
|
|
384
|
-
|
|
385
|
-
<div className="h-px bg-[#1a1a28]" />
|
|
386
|
-
|
|
387
|
-
<ChatView
|
|
388
|
-
messages={messages}
|
|
389
|
-
streamingText={streamingText}
|
|
390
|
-
thinkingText={thinkingText}
|
|
391
|
-
statusText={statusText}
|
|
392
|
-
toolCalls={toolCalls}
|
|
393
|
-
pendingConfirm={pendingConfirm}
|
|
394
|
-
onRespondConfirm={respondConfirm}
|
|
395
|
-
/>
|
|
396
|
-
|
|
397
|
-
{meshPickerOpen && (foundationStats?.meshes?.items?.length > 0) && (
|
|
398
|
-
<div className="border-t border-[#1a1a28] bg-[#0c0c12] px-4 py-3">
|
|
399
|
-
<p className="text-xs text-[#6b6b80] mb-2">Pick a mesh to generate landscape</p>
|
|
400
|
-
<div className="flex flex-wrap gap-2">
|
|
401
|
-
{foundationStats.meshes.items.map((mesh) => (
|
|
402
|
-
<button
|
|
403
|
-
key={mesh.identifier}
|
|
404
|
-
type="button"
|
|
405
|
-
onClick={() => {
|
|
406
|
-
setMeshPickerOpen(false);
|
|
407
|
-
const msg = `Show me the landscape for the mesh **${mesh.name || mesh.identifier}** (identifier: ${mesh.identifier}).`;
|
|
408
|
-
setMessages((prev) => [...prev, { role: "user", content: msg }]);
|
|
409
|
-
sendMessage(msg);
|
|
410
|
-
}}
|
|
411
|
-
className="px-3 py-2 rounded-lg text-sm font-mono bg-[#1a1a28] text-[#e0dfe4] hover:bg-[#f97316]/20 hover:text-[#f97316] border border-[#26263a] transition-colors"
|
|
412
|
-
>
|
|
413
|
-
{mesh.name || mesh.identifier}
|
|
414
|
-
</button>
|
|
415
|
-
))}
|
|
416
|
-
<button
|
|
417
|
-
type="button"
|
|
418
|
-
onClick={() => setMeshPickerOpen(false)}
|
|
419
|
-
className="px-3 py-2 rounded-lg text-sm text-[#6b6b80] hover:text-[#e0dfe4] transition-colors"
|
|
420
|
-
>
|
|
421
|
-
Cancel
|
|
422
|
-
</button>
|
|
423
|
-
</div>
|
|
424
|
-
</div>
|
|
425
|
-
)}
|
|
426
|
-
|
|
427
|
-
<InputBox
|
|
428
|
-
onSubmit={handleSubmit}
|
|
429
|
-
onCancel={cancelStream}
|
|
430
|
-
disabled={false}
|
|
431
|
-
streaming={isStreaming}
|
|
432
|
-
queuedCount={queuedFollowUps.length}
|
|
433
|
-
/>
|
|
434
|
-
|
|
435
|
-
<StatusBar
|
|
436
|
-
connected={connected}
|
|
437
|
-
agentName={activeSessionData?.agent}
|
|
438
|
-
statusText={statusText}
|
|
439
|
-
isStreaming={isStreaming}
|
|
440
|
-
updateAvailable={updateAvailable}
|
|
441
|
-
/>
|
|
442
|
-
</div>
|
|
443
|
-
</div>
|
|
444
|
-
);
|
|
445
|
-
}
|