langchain_agentx_stream_ui 0.8.1 → 0.8.3

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/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  fixDiagramSvg,
25
25
  markEventIdSeen,
26
26
  shouldSkipDuplicateEvent
27
- } from "./chunk-MI4NGIDF.js";
27
+ } from "./chunk-TP6OOI6E.js";
28
28
  import {
29
29
  DefaultToolBody,
30
30
  InteractionBusContext,
@@ -33,7 +33,7 @@ import {
33
33
  getNoopInteractionBus,
34
34
  resolveToolBody,
35
35
  useInteractionBus
36
- } from "./chunk-VYXHVGJU.js";
36
+ } from "./chunk-H6UK3ZFV.js";
37
37
  import "./chunk-DP7V33X7.js";
38
38
  import {
39
39
  CollapseKind,
@@ -121,7 +121,7 @@ import {
121
121
  useSessionStatus,
122
122
  useSessionViewOptions,
123
123
  useTimeline
124
- } from "./chunk-XEIQBTD2.js";
124
+ } from "./chunk-I62OIBGA.js";
125
125
  import {
126
126
  AGENT_PROGRESS_GUTTER,
127
127
  AGENT_PROGRESS_INDENT,
@@ -155,7 +155,7 @@ import {
155
155
  isCancelToolResultError,
156
156
  isToolResultCancelled,
157
157
  resolveToolCancelDisplayText
158
- } from "./chunk-GLUQUP3A.js";
158
+ } from "./chunk-Z5EWWIP3.js";
159
159
  import {
160
160
  BodyBlockList,
161
161
  DiffView,
@@ -3068,8 +3068,21 @@ function findContainerByLoopSession(tree, loopSessionId) {
3068
3068
  return findContainerIdByLoopSession(tree, loopSessionId);
3069
3069
  }
3070
3070
  var WORKFLOW_DEFAULT_LOOP_SESSION_ID = "workflow-loop-default";
3071
+ var HOOK_LOOP_SESSION_EVENT_TYPES = /* @__PURE__ */ new Set(["hook-progress", "hook-finished"]);
3072
+ function readHookPayloadSessionId(event) {
3073
+ if (!HOOK_LOOP_SESSION_EVENT_TYPES.has(event.event_type)) return null;
3074
+ const data = event.data;
3075
+ if (!data || typeof data !== "object" || Array.isArray(data)) return null;
3076
+ const sessionId = data.session_id;
3077
+ if (typeof sessionId === "string" && sessionId.trim()) {
3078
+ return sessionId.trim();
3079
+ }
3080
+ return null;
3081
+ }
3071
3082
  function resolveAgentLoopSessionId(event, activeLoopSessionId, containerTree) {
3072
3083
  if (event.session_id) return event.session_id;
3084
+ const hookSessionId = readHookPayloadSessionId(event);
3085
+ if (hookSessionId) return hookSessionId;
3073
3086
  if (isWorkflowStructureEventType(event.event_type)) return null;
3074
3087
  if (event.event_type === "task-list-snapshot") return null;
3075
3088
  const derived = deriveLoopSessionIdFromAgentEvent(event);
@@ -6134,6 +6147,639 @@ var DEMO_WORKFLOW_SCALE = {
6134
6147
  evictCompletedLoops: true
6135
6148
  };
6136
6149
 
6150
+ // src/view/mcp/McpPanel.tsx
6151
+ import {
6152
+ useCallback as useCallback6,
6153
+ useEffect as useEffect12,
6154
+ useRef as useRef6,
6155
+ useState as useState8
6156
+ } from "react";
6157
+ import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
6158
+ var STATE_META = {
6159
+ connecting: { icon: "\u27F3", label: "\u8FDE\u63A5\u4E2D", tone: "running" },
6160
+ connected: { icon: "\u25CF", label: "\u5DF2\u8FDE\u63A5", tone: "success" },
6161
+ failed: { icon: "\u2717", label: "\u8FDE\u63A5\u5931\u8D25", tone: "error" },
6162
+ disconnected: { icon: "\u25CB", label: "\u672A\u8FDE\u63A5", tone: "muted" },
6163
+ reconnecting: { icon: "\u27F3", label: "\u91CD\u8FDE\u4E2D", tone: "running" },
6164
+ needs_auth: { icon: "\u26A0", label: "\u9700\u8981\u9274\u6743", tone: "warning" },
6165
+ disabled: { icon: "\u2298", label: "\u5DF2\u505C\u7528", tone: "muted" }
6166
+ };
6167
+ var ADJ_META = {
6168
+ allowed: { icon: "\u2713", label: "\u5C06\u8FDB\u6C60", cls: "lax-mcp-adj--allowed" },
6169
+ denied: { icon: "\u2717", label: "\u88AB deny \u5265\u79BB", cls: "lax-mcp-adj--denied" },
6170
+ uncovered: { icon: "\u26A0", label: "\u672A\u8986\u76D6 \u2192 \u4E0D\u8FDB\u6C60", cls: "lax-mcp-adj--uncovered" }
6171
+ };
6172
+ var EMPTY_FORM = {
6173
+ name: "",
6174
+ transport: "stdio",
6175
+ command: "",
6176
+ argsText: "",
6177
+ envText: "",
6178
+ cwd: "",
6179
+ url: "",
6180
+ headersText: ""
6181
+ };
6182
+ function errorMessage(err) {
6183
+ return err instanceof Error ? err.message : String(err);
6184
+ }
6185
+ function splitLines(text) {
6186
+ return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
6187
+ }
6188
+ function parseKeyValueLines(text) {
6189
+ const lines = splitLines(text);
6190
+ if (lines.length === 0) return void 0;
6191
+ const record = {};
6192
+ for (const line of lines) {
6193
+ const eq = line.indexOf("=");
6194
+ if (eq <= 0) continue;
6195
+ record[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
6196
+ }
6197
+ return Object.keys(record).length > 0 ? record : void 0;
6198
+ }
6199
+ function toSpec(status) {
6200
+ return {
6201
+ name: status.name,
6202
+ transport: status.transport,
6203
+ command: status.command,
6204
+ args: status.args,
6205
+ env: status.env,
6206
+ cwd: status.cwd,
6207
+ url: status.url,
6208
+ headers: status.headers,
6209
+ enabled: status.enabled
6210
+ };
6211
+ }
6212
+ function buildSpec(form) {
6213
+ const spec = { name: form.name.trim(), transport: form.transport, enabled: true };
6214
+ if (form.transport === "stdio") {
6215
+ spec.command = form.command.trim();
6216
+ const args = form.argsText.trim().split(/\s+/).filter(Boolean);
6217
+ if (args.length > 0) spec.args = args;
6218
+ const env = parseKeyValueLines(form.envText);
6219
+ if (env) spec.env = env;
6220
+ if (form.cwd.trim()) spec.cwd = form.cwd.trim();
6221
+ } else {
6222
+ spec.url = form.url.trim();
6223
+ const headers = parseKeyValueLines(form.headersText);
6224
+ if (headers) spec.headers = headers;
6225
+ }
6226
+ return spec;
6227
+ }
6228
+ function McpPanel({ isOpen, onClose, client, demoServerSpec }) {
6229
+ const panelRef = useRef6(null);
6230
+ const [loading, setLoading] = useState8(true);
6231
+ const [loadError, setLoadError] = useState8(null);
6232
+ const [servers, setServers] = useState8([]);
6233
+ const [summary, setSummary] = useState8(null);
6234
+ const [allowText, setAllowText] = useState8("");
6235
+ const [denyText, setDenyText] = useState8("");
6236
+ const [rulesSaving, setRulesSaving] = useState8(false);
6237
+ const [rulesError, setRulesError] = useState8(null);
6238
+ const [toolsLoads, setToolsLoads] = useState8({});
6239
+ const [expanded, setExpanded] = useState8({});
6240
+ const [busy, setBusy] = useState8(null);
6241
+ const [formOpen, setFormOpen] = useState8(false);
6242
+ const [form, setForm] = useState8(EMPTY_FORM);
6243
+ const [formError, setFormError] = useState8(null);
6244
+ const refresh = useCallback6(
6245
+ async (showLoading = false) => {
6246
+ if (showLoading) setLoading(true);
6247
+ try {
6248
+ const [serversRes, summaryRes, rulesRes] = await Promise.all([
6249
+ client.listServers(),
6250
+ client.getSummary(),
6251
+ client.getRules()
6252
+ ]);
6253
+ setServers(serversRes.servers);
6254
+ setSummary(summaryRes);
6255
+ setAllowText(rulesRes.allow.join("\n"));
6256
+ setDenyText(rulesRes.deny.join("\n"));
6257
+ setLoadError(null);
6258
+ } catch (err) {
6259
+ setLoadError(errorMessage(err));
6260
+ } finally {
6261
+ if (showLoading) setLoading(false);
6262
+ }
6263
+ },
6264
+ [client]
6265
+ );
6266
+ const loadTools = useCallback6(
6267
+ async (name) => {
6268
+ setToolsLoads((prev) => ({ ...prev, [name]: { status: "loading", tools: [] } }));
6269
+ try {
6270
+ const res = await client.listTools(name);
6271
+ setToolsLoads((prev) => ({ ...prev, [name]: { status: "ok", tools: res.tools } }));
6272
+ } catch (err) {
6273
+ setToolsLoads((prev) => ({
6274
+ ...prev,
6275
+ [name]: { status: "error", error: errorMessage(err), tools: [] }
6276
+ }));
6277
+ }
6278
+ },
6279
+ [client]
6280
+ );
6281
+ useEffect12(() => {
6282
+ if (!isOpen) return;
6283
+ void refresh(true);
6284
+ const onKeyDown = (event) => {
6285
+ if (event.key === "Escape") onClose();
6286
+ };
6287
+ const prevOverflow = document.body.style.overflow;
6288
+ document.body.style.overflow = "hidden";
6289
+ window.addEventListener("keydown", onKeyDown);
6290
+ panelRef.current?.focus();
6291
+ return () => {
6292
+ window.removeEventListener("keydown", onKeyDown);
6293
+ document.body.style.overflow = prevOverflow;
6294
+ };
6295
+ }, [isOpen, refresh, onClose]);
6296
+ const trapTabFocus = (event) => {
6297
+ if (event.key !== "Tab" || panelRef.current === null) return;
6298
+ const focusables = Array.from(
6299
+ panelRef.current.querySelectorAll(
6300
+ "button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href]"
6301
+ )
6302
+ ).filter((el) => el.offsetParent !== null);
6303
+ if (focusables.length === 0) return;
6304
+ const first = focusables[0];
6305
+ const last = focusables[focusables.length - 1];
6306
+ const active = document.activeElement;
6307
+ if (event.shiftKey && active === first) {
6308
+ event.preventDefault();
6309
+ last.focus();
6310
+ } else if (!event.shiftKey && active === last) {
6311
+ event.preventDefault();
6312
+ first.focus();
6313
+ }
6314
+ };
6315
+ const runAction = async (key, fn) => {
6316
+ if (busy !== null) return;
6317
+ setBusy(key);
6318
+ try {
6319
+ await fn();
6320
+ } catch (err) {
6321
+ setLoadError(errorMessage(err));
6322
+ } finally {
6323
+ setBusy(null);
6324
+ }
6325
+ };
6326
+ const saveServerList = (next, after) => runAction("put-servers", async () => {
6327
+ await client.putServers(next);
6328
+ after?.();
6329
+ await refresh();
6330
+ });
6331
+ const submitForm = () => {
6332
+ const spec = buildSpec(form);
6333
+ if (spec.name === "") {
6334
+ setFormError("name \u4E0D\u80FD\u4E3A\u7A7A");
6335
+ return;
6336
+ }
6337
+ if (servers.some((s) => s.name === spec.name)) {
6338
+ setFormError(`server "${spec.name}" \u5DF2\u5B58\u5728`);
6339
+ return;
6340
+ }
6341
+ if (spec.transport === "stdio" && !spec.command) {
6342
+ setFormError("stdio server \u9700\u8981 command");
6343
+ return;
6344
+ }
6345
+ if (spec.transport !== "stdio" && !spec.url) {
6346
+ setFormError(`${spec.transport} server \u9700\u8981 url`);
6347
+ return;
6348
+ }
6349
+ setFormError(null);
6350
+ void saveServerList([...servers.map(toSpec), spec], () => {
6351
+ setFormOpen(false);
6352
+ setForm(EMPTY_FORM);
6353
+ });
6354
+ };
6355
+ const deleteServer = (name) => saveServerList(
6356
+ servers.filter((s) => s.name !== name).map(toSpec)
6357
+ );
6358
+ const toggleEnabled = (server) => runAction(`toggle:${server.name}`, async () => {
6359
+ if (server.enabled) await client.disconnectServer(server.name);
6360
+ else await client.connectServer(server.name);
6361
+ await refresh();
6362
+ });
6363
+ const reconnectServer = (name) => runAction(`reconnect:${name}`, async () => {
6364
+ await client.reconnectServer(name);
6365
+ await refresh();
6366
+ });
6367
+ const toggleTools = (name) => {
6368
+ const next = !expanded[name];
6369
+ setExpanded((prev) => ({ ...prev, [name]: next }));
6370
+ if (next && !toolsLoads[name]) void loadTools(name);
6371
+ };
6372
+ const saveRules = async () => {
6373
+ if (rulesSaving) return;
6374
+ setRulesSaving(true);
6375
+ setRulesError(null);
6376
+ try {
6377
+ const res = await client.putRules({ allow: splitLines(allowText), deny: splitLines(denyText) });
6378
+ setAllowText(res.allow.join("\n"));
6379
+ setDenyText(res.deny.join("\n"));
6380
+ setToolsLoads({});
6381
+ for (const name of Object.keys(expanded)) {
6382
+ if (expanded[name]) void loadTools(name);
6383
+ }
6384
+ await refresh();
6385
+ } catch (err) {
6386
+ setRulesError(errorMessage(err));
6387
+ } finally {
6388
+ setRulesSaving(false);
6389
+ }
6390
+ };
6391
+ if (!isOpen) return null;
6392
+ const toolsInPool = summary !== null ? summary.tools_total - summary.tools_blocked : null;
6393
+ return /* @__PURE__ */ jsx20(
6394
+ "div",
6395
+ {
6396
+ className: "lax-mcp-overlay",
6397
+ role: "dialog",
6398
+ "aria-modal": "true",
6399
+ "aria-label": "MCP Servers",
6400
+ "data-testid": "lax-mcp-overlay",
6401
+ onClick: (event) => {
6402
+ if (event.target === event.currentTarget) onClose();
6403
+ },
6404
+ children: /* @__PURE__ */ jsxs17(
6405
+ "div",
6406
+ {
6407
+ className: "lax-mcp-panel",
6408
+ ref: panelRef,
6409
+ tabIndex: -1,
6410
+ "data-testid": "lax-mcp-panel",
6411
+ onKeyDown: trapTabFocus,
6412
+ onClick: (event) => event.stopPropagation(),
6413
+ children: [
6414
+ /* @__PURE__ */ jsxs17("header", { className: "lax-mcp-header", children: [
6415
+ /* @__PURE__ */ jsx20("span", { className: "lax-mcp-title", children: "MCP Servers" }),
6416
+ /* @__PURE__ */ jsx20(
6417
+ "span",
6418
+ {
6419
+ className: `lax-mcp-ready${summary?.ready ? " is-ready" : " is-not-ready"}`,
6420
+ "aria-live": "polite",
6421
+ "data-testid": "lax-mcp-ready",
6422
+ children: summary?.ready ? "\u25CF \u5C31\u7EEA" : "\u26A0 \u672A\u5C31\u7EEA"
6423
+ }
6424
+ ),
6425
+ /* @__PURE__ */ jsx20("button", { type: "button", className: "lax-mcp-close", "aria-label": "\u5173\u95ED", onClick: onClose, children: "\xD7" })
6426
+ ] }),
6427
+ loadError !== null && /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-error", role: "alert", "data-testid": "lax-mcp-error", children: [
6428
+ /* @__PURE__ */ jsx20("span", { className: "lax-mcp-error-text", children: loadError }),
6429
+ /* @__PURE__ */ jsx20("button", { type: "button", className: "lax-mcp-btn", onClick: () => void refresh(true), children: "\u91CD\u8BD5" })
6430
+ ] }),
6431
+ /* @__PURE__ */ jsx20("div", { className: "lax-mcp-body", children: loading ? /* @__PURE__ */ jsx20("div", { className: "lax-mcp-loading", "data-testid": "lax-mcp-loading", children: "\u52A0\u8F7D\u4E2D\u2026" }) : /* @__PURE__ */ jsxs17(Fragment5, { children: [
6432
+ /* @__PURE__ */ jsxs17("section", { className: "lax-mcp-section", "aria-label": "server \u914D\u7F6E", children: [
6433
+ /* @__PURE__ */ jsx20("div", { className: "lax-mcp-section-title", children: "\u2460 Server \u914D\u7F6E" }),
6434
+ /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-config-actions", children: [
6435
+ /* @__PURE__ */ jsx20(
6436
+ "button",
6437
+ {
6438
+ type: "button",
6439
+ className: "lax-mcp-btn",
6440
+ onClick: () => {
6441
+ setFormOpen((v) => !v);
6442
+ setFormError(null);
6443
+ },
6444
+ children: "\uFF0B \u6DFB\u52A0 server"
6445
+ }
6446
+ ),
6447
+ demoServerSpec !== void 0 && /* @__PURE__ */ jsxs17(
6448
+ "button",
6449
+ {
6450
+ type: "button",
6451
+ className: "lax-mcp-btn",
6452
+ disabled: servers.some((s) => s.name === demoServerSpec.name),
6453
+ onClick: () => void saveServerList([...servers.map(toSpec), demoServerSpec]),
6454
+ children: [
6455
+ "\u26A1 \u6DFB\u52A0\u6F14\u793A server\uFF08",
6456
+ demoServerSpec.name,
6457
+ "\uFF09"
6458
+ ]
6459
+ }
6460
+ )
6461
+ ] }),
6462
+ formOpen && /* @__PURE__ */ jsx20(
6463
+ ServerForm,
6464
+ {
6465
+ form,
6466
+ setForm,
6467
+ error: formError,
6468
+ busy: busy !== null,
6469
+ onSubmit: submitForm,
6470
+ onCancel: () => {
6471
+ setFormOpen(false);
6472
+ setFormError(null);
6473
+ }
6474
+ }
6475
+ )
6476
+ ] }),
6477
+ /* @__PURE__ */ jsxs17("section", { className: "lax-mcp-section", "aria-label": "\u8FDE\u63A5\u72B6\u6001\u4E0E\u5DE5\u5177\u6E05\u5355", children: [
6478
+ /* @__PURE__ */ jsx20("div", { className: "lax-mcp-section-title", children: "\u2461 \u8FDE\u63A5\u72B6\u6001 / \u2462 \u5DE5\u5177\u6E05\u5355" }),
6479
+ servers.length === 0 ? /* @__PURE__ */ jsx20("div", { className: "lax-mcp-empty", "data-testid": "lax-mcp-empty", children: "\u5C1A\u672A\u914D\u7F6E MCP server" }) : servers.map((server) => /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-server", "data-server": server.name, children: [
6480
+ /* @__PURE__ */ jsx20(
6481
+ McpServerRow,
6482
+ {
6483
+ server,
6484
+ busy: busy !== null,
6485
+ expanded: expanded[server.name] === true,
6486
+ onReconnect: () => reconnectServer(server.name),
6487
+ onToggleEnabled: () => toggleEnabled(server),
6488
+ onDelete: () => deleteServer(server.name),
6489
+ onToggleTools: () => toggleTools(server.name)
6490
+ }
6491
+ ),
6492
+ expanded[server.name] === true && /* @__PURE__ */ jsx20(ToolsGroup, { load: toolsLoads[server.name] })
6493
+ ] }, server.name))
6494
+ ] }),
6495
+ /* @__PURE__ */ jsxs17("section", { className: "lax-mcp-section", "aria-label": "\u89C4\u5219\u88C1\u51B3", children: [
6496
+ /* @__PURE__ */ jsx20("div", { className: "lax-mcp-section-title", children: "\u2463 \u89C4\u5219\u88C1\u51B3" }),
6497
+ /* @__PURE__ */ jsx20(
6498
+ McpRuleEditor,
6499
+ {
6500
+ allowText,
6501
+ denyText,
6502
+ onAllowChange: setAllowText,
6503
+ onDenyChange: setDenyText,
6504
+ saving: rulesSaving,
6505
+ error: rulesError,
6506
+ disabled: servers.length === 0,
6507
+ inPool: toolsInPool,
6508
+ total: summary?.tools_total ?? null,
6509
+ blocked: summary?.tools_blocked ?? null,
6510
+ onSave: () => void saveRules()
6511
+ }
6512
+ )
6513
+ ] })
6514
+ ] }) })
6515
+ ]
6516
+ }
6517
+ )
6518
+ }
6519
+ );
6520
+ }
6521
+ function McpServerRow(props) {
6522
+ const { server, busy, expanded, onReconnect, onToggleEnabled, onDelete, onToggleTools } = props;
6523
+ const meta = STATE_META[server.state];
6524
+ const rowBusy = server.state === "connecting" || server.state === "reconnecting";
6525
+ return /* @__PURE__ */ jsxs17("div", { className: `lax-mcp-server-row lax-mcp-server-row--${server.state}`, children: [
6526
+ /* @__PURE__ */ jsx20("span", { className: `lax-mcp-state-icon tone-${meta.tone}`, role: "img", "aria-label": meta.label, children: meta.icon }),
6527
+ /* @__PURE__ */ jsx20("span", { className: "lax-mcp-server-name", children: server.name }),
6528
+ /* @__PURE__ */ jsx20("span", { className: "lax-mcp-server-meta", children: server.transport }),
6529
+ /* @__PURE__ */ jsx20("span", { className: "lax-mcp-server-meta", children: server.state === "connected" ? server.tool_count === 0 ? "\u5DF2\u8FDE\u63A5 \xB7 \u672A\u66B4\u9732\u5DE5\u5177" : `${server.tool_count ?? "\u2013"} tool` : null }),
6530
+ /* @__PURE__ */ jsxs17("span", { className: "lax-mcp-server-actions", children: [
6531
+ /* @__PURE__ */ jsx20(
6532
+ "button",
6533
+ {
6534
+ type: "button",
6535
+ className: "lax-mcp-btn",
6536
+ disabled: busy || server.state !== "connected",
6537
+ "aria-expanded": expanded,
6538
+ onClick: onToggleTools,
6539
+ children: expanded ? "\u25BE \u5DE5\u5177" : "\u25B8 \u5DE5\u5177"
6540
+ }
6541
+ ),
6542
+ /* @__PURE__ */ jsx20("button", { type: "button", className: "lax-mcp-btn", disabled: busy || rowBusy, onClick: onReconnect, children: "\u21BB \u91CD\u8FDE" }),
6543
+ /* @__PURE__ */ jsx20(
6544
+ "button",
6545
+ {
6546
+ type: "button",
6547
+ className: "lax-mcp-btn",
6548
+ disabled: busy || rowBusy,
6549
+ onClick: onToggleEnabled,
6550
+ children: server.enabled ? "\u23FB \u505C\u7528" : "\u23FB \u542F\u7528"
6551
+ }
6552
+ ),
6553
+ /* @__PURE__ */ jsx20("button", { type: "button", className: "lax-mcp-btn", disabled: busy, onClick: onDelete, children: "\u{1F5D1} \u5220\u9664" }),
6554
+ server.state === "needs_auth" && /* @__PURE__ */ jsx20(
6555
+ "button",
6556
+ {
6557
+ type: "button",
6558
+ className: "lax-mcp-btn",
6559
+ disabled: true,
6560
+ title: "OAuth \u767B\u5F55\uFF08\u9884\u7559\uFF0C\u672A\u5B9E\u73B0\uFF09",
6561
+ children: "\u{1F511} OAuth \u767B\u5F55"
6562
+ }
6563
+ )
6564
+ ] }),
6565
+ server.state === "failed" && server.error != null && server.error !== "" && /* @__PURE__ */ jsx20("div", { className: "lax-mcp-server-error", children: server.error })
6566
+ ] });
6567
+ }
6568
+ function ToolsGroup({ load }) {
6569
+ if (load === void 0 || load.status === "loading") {
6570
+ return /* @__PURE__ */ jsx20("div", { className: "lax-mcp-tools lax-mcp-tools--loading", "data-testid": "lax-mcp-tools-loading", children: "\u6E05\u5355\u62C9\u53D6\u4E2D\u2026" });
6571
+ }
6572
+ if (load.status === "error") {
6573
+ return /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-tools lax-mcp-tools--error", role: "alert", children: [
6574
+ "\u53D1\u73B0\u5931\u8D25\uFF1A",
6575
+ load.error
6576
+ ] });
6577
+ }
6578
+ if (load.tools.length === 0) {
6579
+ return /* @__PURE__ */ jsx20("div", { className: "lax-mcp-tools", children: "\u5DF2\u8FDE\u63A5\uFF0C\u4F46\u8BE5 server \u672A\u66B4\u9732\u5DE5\u5177" });
6580
+ }
6581
+ return /* @__PURE__ */ jsx20("div", { className: "lax-mcp-tools", children: load.tools.map((tool) => /* @__PURE__ */ jsx20(McpToolRow, { tool }, tool.model_name)) });
6582
+ }
6583
+ function McpToolRow({ tool }) {
6584
+ const adj = ADJ_META[tool.adjudication];
6585
+ return /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-tool-row", "data-adjudication": tool.adjudication, children: [
6586
+ /* @__PURE__ */ jsx20("span", { className: "lax-mcp-tool-name", children: tool.model_name }),
6587
+ tool.destructive_hint === true ? /* @__PURE__ */ jsx20("span", { className: "lax-mcp-tool-hint tone-error", children: "\u5199\u64CD\u4F5C" }) : tool.read_only_hint === true ? /* @__PURE__ */ jsx20("span", { className: "lax-mcp-tool-hint tone-success", children: "readOnly" }) : null,
6588
+ /* @__PURE__ */ jsxs17("span", { className: `lax-mcp-adj ${adj.cls}`, children: [
6589
+ adj.icon,
6590
+ " ",
6591
+ adj.label
6592
+ ] })
6593
+ ] });
6594
+ }
6595
+ function McpRuleEditor(props) {
6596
+ const {
6597
+ allowText,
6598
+ denyText,
6599
+ onAllowChange,
6600
+ onDenyChange,
6601
+ saving,
6602
+ error,
6603
+ disabled,
6604
+ inPool,
6605
+ total,
6606
+ blocked,
6607
+ onSave
6608
+ } = props;
6609
+ return /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-rules", "data-testid": "lax-mcp-rules", children: [
6610
+ /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-rules-grid", children: [
6611
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6612
+ /* @__PURE__ */ jsx20("span", { children: "allow\uFF08\u6BCF\u884C\u4E00\u4E2A\u5DE5\u5177\u540D\uFF09" }),
6613
+ /* @__PURE__ */ jsx20(
6614
+ "textarea",
6615
+ {
6616
+ className: "lax-mcp-textarea",
6617
+ value: allowText,
6618
+ onChange: (event) => onAllowChange(event.target.value),
6619
+ placeholder: "mcp__demo__echo"
6620
+ }
6621
+ )
6622
+ ] }),
6623
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6624
+ /* @__PURE__ */ jsx20("span", { children: "deny\uFF08\u6BCF\u884C\u4E00\u4E2A\u5DE5\u5177\u540D\uFF09" }),
6625
+ /* @__PURE__ */ jsx20(
6626
+ "textarea",
6627
+ {
6628
+ className: "lax-mcp-textarea",
6629
+ value: denyText,
6630
+ onChange: (event) => onDenyChange(event.target.value),
6631
+ placeholder: "mcp__demo__write"
6632
+ }
6633
+ )
6634
+ ] })
6635
+ ] }),
6636
+ /* @__PURE__ */ jsx20("div", { className: "lax-mcp-hint", children: "\u9ED8\u8BA4\u5168 deny\uFF08fail closed\uFF09\uFF1A\u672A\u8986\u76D6\u89C4\u5219\u7684\u5DE5\u5177\u4E0D\u4F1A\u8FDB\u5165\u4F1A\u8BDD\u3002\u89C4\u5219\u53D8\u66F4\u5BF9\u65B0\u4F1A\u8BDD\u751F\u6548\u3002" }),
6637
+ /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-rules-preview", "data-testid": "lax-mcp-rules-preview", children: [
6638
+ "\u88C1\u51B3\u9884\u89C8\uFF1A",
6639
+ inPool !== null && total !== null ? `${inPool}/${total} \u8FDB\u6C60` : "\u2013",
6640
+ blocked !== null ? ` \xB7 ${blocked} \u88AB\u62E6\u622A\uFF08deny / \u672A\u8986\u76D6\uFF09` : ""
6641
+ ] }),
6642
+ error !== null && /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-inline-error", role: "alert", children: [
6643
+ "\u4FDD\u5B58\u5931\u8D25\uFF1A",
6644
+ error
6645
+ ] }),
6646
+ /* @__PURE__ */ jsx20(
6647
+ "button",
6648
+ {
6649
+ type: "button",
6650
+ className: "lax-mcp-btn lax-mcp-btn--primary",
6651
+ disabled: disabled || saving,
6652
+ onClick: onSave,
6653
+ children: saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u89C4\u5219"
6654
+ }
6655
+ )
6656
+ ] });
6657
+ }
6658
+ function ServerForm(props) {
6659
+ const { form, setForm, error, busy, onSubmit, onCancel } = props;
6660
+ const set = (patch) => setForm((prev) => ({ ...prev, ...patch }));
6661
+ const isStdio = form.transport === "stdio";
6662
+ return /* @__PURE__ */ jsxs17(
6663
+ "form",
6664
+ {
6665
+ className: "lax-mcp-form",
6666
+ onSubmit: (event) => {
6667
+ event.preventDefault();
6668
+ onSubmit();
6669
+ },
6670
+ children: [
6671
+ /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-form-row", children: [
6672
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6673
+ /* @__PURE__ */ jsx20("span", { children: "name" }),
6674
+ /* @__PURE__ */ jsx20(
6675
+ "input",
6676
+ {
6677
+ className: "lax-mcp-input",
6678
+ value: form.name,
6679
+ onChange: (event) => set({ name: event.target.value }),
6680
+ placeholder: "demo"
6681
+ }
6682
+ )
6683
+ ] }),
6684
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6685
+ /* @__PURE__ */ jsx20("span", { children: "transport" }),
6686
+ /* @__PURE__ */ jsxs17(
6687
+ "select",
6688
+ {
6689
+ className: "lax-mcp-select",
6690
+ value: form.transport,
6691
+ onChange: (event) => set({ transport: event.target.value }),
6692
+ children: [
6693
+ /* @__PURE__ */ jsx20("option", { value: "stdio", children: "stdio" }),
6694
+ /* @__PURE__ */ jsx20("option", { value: "sse", children: "sse" }),
6695
+ /* @__PURE__ */ jsx20("option", { value: "http", children: "http" })
6696
+ ]
6697
+ }
6698
+ )
6699
+ ] })
6700
+ ] }),
6701
+ isStdio ? /* @__PURE__ */ jsxs17(Fragment5, { children: [
6702
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6703
+ /* @__PURE__ */ jsx20("span", { children: "command" }),
6704
+ /* @__PURE__ */ jsx20(
6705
+ "input",
6706
+ {
6707
+ className: "lax-mcp-input",
6708
+ value: form.command,
6709
+ onChange: (event) => set({ command: event.target.value }),
6710
+ placeholder: "python"
6711
+ }
6712
+ )
6713
+ ] }),
6714
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6715
+ /* @__PURE__ */ jsx20("span", { children: "args\uFF08\u7A7A\u683C\u5206\u9694\uFF09" }),
6716
+ /* @__PURE__ */ jsx20(
6717
+ "input",
6718
+ {
6719
+ className: "lax-mcp-input",
6720
+ value: form.argsText,
6721
+ onChange: (event) => set({ argsText: event.target.value }),
6722
+ placeholder: "-u echo_server.py"
6723
+ }
6724
+ )
6725
+ ] }),
6726
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6727
+ /* @__PURE__ */ jsx20("span", { children: "env\uFF08\u6BCF\u884C KEY=VALUE\uFF09" }),
6728
+ /* @__PURE__ */ jsx20(
6729
+ "textarea",
6730
+ {
6731
+ className: "lax-mcp-textarea",
6732
+ value: form.envText,
6733
+ onChange: (event) => set({ envText: event.target.value })
6734
+ }
6735
+ )
6736
+ ] }),
6737
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6738
+ /* @__PURE__ */ jsx20("span", { children: "cwd" }),
6739
+ /* @__PURE__ */ jsx20(
6740
+ "input",
6741
+ {
6742
+ className: "lax-mcp-input",
6743
+ value: form.cwd,
6744
+ onChange: (event) => set({ cwd: event.target.value })
6745
+ }
6746
+ )
6747
+ ] })
6748
+ ] }) : /* @__PURE__ */ jsxs17(Fragment5, { children: [
6749
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6750
+ /* @__PURE__ */ jsx20("span", { children: "url" }),
6751
+ /* @__PURE__ */ jsx20(
6752
+ "input",
6753
+ {
6754
+ className: "lax-mcp-input",
6755
+ value: form.url,
6756
+ onChange: (event) => set({ url: event.target.value }),
6757
+ placeholder: "http://localhost:9000/sse"
6758
+ }
6759
+ )
6760
+ ] }),
6761
+ /* @__PURE__ */ jsxs17("label", { className: "lax-mcp-field", children: [
6762
+ /* @__PURE__ */ jsx20("span", { children: "headers\uFF08\u6BCF\u884C KEY=VALUE\uFF09" }),
6763
+ /* @__PURE__ */ jsx20(
6764
+ "textarea",
6765
+ {
6766
+ className: "lax-mcp-textarea",
6767
+ value: form.headersText,
6768
+ onChange: (event) => set({ headersText: event.target.value })
6769
+ }
6770
+ )
6771
+ ] })
6772
+ ] }),
6773
+ error !== null && /* @__PURE__ */ jsx20("div", { className: "lax-mcp-inline-error", role: "alert", children: error }),
6774
+ /* @__PURE__ */ jsxs17("div", { className: "lax-mcp-form-row", children: [
6775
+ /* @__PURE__ */ jsx20("button", { type: "submit", className: "lax-mcp-btn lax-mcp-btn--primary", disabled: busy, children: "\u4FDD\u5B58" }),
6776
+ /* @__PURE__ */ jsx20("button", { type: "button", className: "lax-mcp-btn", onClick: onCancel, children: "\u53D6\u6D88" })
6777
+ ] })
6778
+ ]
6779
+ }
6780
+ );
6781
+ }
6782
+
6137
6783
  // src/view/tools/groupParallelTools.ts
6138
6784
  function buildTimelineEntries(rootIds, byId, options = {}) {
6139
6785
  const filtered = options.hideStandalonePermissions ? rootIds.filter((id) => byId[id]?.kind !== "permission") : [...rootIds];
@@ -6171,18 +6817,18 @@ function buildTimelineEntries(rootIds, byId, options = {}) {
6171
6817
  }
6172
6818
 
6173
6819
  // src/view/nodes/SubAgentNode.tsx
6174
- import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
6820
+ import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
6175
6821
  function SubAgentBlock({ nodeId }) {
6176
6822
  const node = useNodeTyped(nodeId, "subagent");
6177
6823
  const childIds = useChildren(nodeId);
6178
6824
  const registry = useNodeRegistry();
6179
6825
  if (!node) return null;
6180
- return /* @__PURE__ */ jsxs17("div", { className: "lax-subagent-block", "data-status": node.status, children: [
6181
- /* @__PURE__ */ jsxs17("div", { className: "lax-subagent-block__header", children: [
6826
+ return /* @__PURE__ */ jsxs18("div", { className: "lax-subagent-block", "data-status": node.status, children: [
6827
+ /* @__PURE__ */ jsxs18("div", { className: "lax-subagent-block__header", children: [
6182
6828
  "SubAgent: ",
6183
6829
  node.subagentId
6184
6830
  ] }),
6185
- /* @__PURE__ */ jsx20("div", { className: "lax-subagent-block__children", children: childIds.map((childId) => /* @__PURE__ */ jsx20(TimelineItem, { nodeId: childId, registry }, childId)) })
6831
+ /* @__PURE__ */ jsx21("div", { className: "lax-subagent-block__children", children: childIds.map((childId) => /* @__PURE__ */ jsx21(TimelineItem, { nodeId: childId, registry }, childId)) })
6186
6832
  ] });
6187
6833
  }
6188
6834
 
@@ -6256,6 +6902,7 @@ export {
6256
6902
  MAX_PREVIEW_LINES,
6257
6903
  MIN_HINT_DISPLAY_MS,
6258
6904
  MarkdownBlock,
6905
+ McpPanel,
6259
6906
  MermaidDiagram,
6260
6907
  MissingApplicationKindError,
6261
6908
  MultiAgentSession,