@chrok/dsh-braid 0.0.0-stage → 0.3.2

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/client.js ADDED
@@ -0,0 +1,732 @@
1
+ window.__ModuleLoader__.load({ id: "@chrok/dsh-braid", factory: (require) => { var module = { exports: {} }; var exports = module.exports;
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // integrations/dsh/client/index.tsx
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ apply: () => apply,
25
+ inject: () => inject
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // integrations/dsh/panel-contract.ts
30
+ var PANEL_PACKAGE = "@chrok/dsh-braid";
31
+ function parseRequest(value, method) {
32
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid Braid panel request");
33
+ const r = value;
34
+ const id2 = (key) => typeof r[key] === "string" && r[key].length > 0 && r[key].length <= 1024;
35
+ if (!id2("sessionId") || r.jobId !== void 0 && !id2("jobId")) throw new Error("Invalid session or job ID");
36
+ if (r.createdBefore !== void 0 && (!Number.isSafeInteger(r.createdBefore) || Number(r.createdBefore) < 0)) throw new Error("Invalid call timestamp");
37
+ if (method !== "watch" && !id2("jobId")) throw new Error("A job ID is required");
38
+ if (method === "control" && (!["cancel", "resume"].includes(String(r.action)) || !Number.isSafeInteger(r.revision) || Number(r.revision) < 0 || !Array.isArray(r.executionIds) || r.executionIds.length > 1e4 || r.executionIds.some((v) => typeof v !== "string" || !v || v.length > 1024))) throw new Error("Invalid Braid control request");
39
+ if (method === "detail" && (!id2("executionId") || !Number.isSafeInteger(r.offset) || Number(r.offset) < 0)) throw new Error("Invalid execution or offset");
40
+ return value;
41
+ }
42
+ var panelDescriptors = ["watch", "control", "detail"].map((method) => ({
43
+ id: `${PANEL_PACKAGE}#braidPanel/${method}`,
44
+ service: "braidPanel",
45
+ namespace: "braidPanel",
46
+ method,
47
+ invocation: { kind: "direct" },
48
+ parameters: [{ name: "request", wire: "request", source: "json", codec: {
49
+ mode: "strict",
50
+ typeSymbol: `${PANEL_PACKAGE}/${method}Request`,
51
+ create: () => ({ parse: (value) => parseRequest(value, method) })
52
+ } }],
53
+ result: { mode: "src-json" },
54
+ ...method === "watch" ? { mode: "stream", cancellation: { parameter: "signal" } } : {}
55
+ }));
56
+
57
+ // integrations/dsh/client/Panel.tsx
58
+ var import_react = require("react");
59
+
60
+ // integrations/dsh/client/layout.ts
61
+ function layoutGraph(nodes, edges) {
62
+ const rank = new Map(nodes.map((node) => [node.id, 0]));
63
+ const incoming = new Map(nodes.map((node) => [node.id, 0]));
64
+ const outgoing = new Map(nodes.map((node) => [node.id, []]));
65
+ for (const edge of edges) {
66
+ if (edge.feedback || !rank.has(edge.from) || !rank.has(edge.to)) continue;
67
+ incoming.set(edge.to, incoming.get(edge.to) + 1);
68
+ outgoing.get(edge.from).push(edge.to);
69
+ }
70
+ const queue = nodes.filter((node) => incoming.get(node.id) === 0).map((node) => node.id);
71
+ for (let i = 0; i < queue.length; i++) for (const to of outgoing.get(queue[i])) {
72
+ rank.set(to, Math.max(rank.get(to), rank.get(queue[i]) + 1));
73
+ incoming.set(to, incoming.get(to) - 1);
74
+ if (incoming.get(to) === 0) queue.push(to);
75
+ }
76
+ const columns = /* @__PURE__ */ new Map();
77
+ const positions = new Map(nodes.map((node) => {
78
+ const row = rank.get(node.id), column = columns.get(row) ?? 0;
79
+ columns.set(row, column + 1);
80
+ return [node.id, { x: 32 + column * 186, y: 28 + row * 124 }];
81
+ }));
82
+ return {
83
+ positions,
84
+ width: Math.max(280, ...[...columns.values()].map((count) => count * 186 + 32)),
85
+ height: Math.max(160, ...[...rank.values()].map((row) => (row + 1) * 124 + 28))
86
+ };
87
+ }
88
+
89
+ // integrations/dsh/client/Panel.tsx
90
+ var import_jsx_runtime = require("react/jsx-runtime");
91
+ var number = (n) => n.toLocaleString();
92
+ var message = (error) => error instanceof Error ? error.message : String(error);
93
+ function Status({ status }) {
94
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "br-status", "data-status": status, children: status });
95
+ }
96
+ function Graph({ job, selected, select }) {
97
+ const layout = (0, import_react.useMemo)(() => layoutGraph(job.nodes, job.edges), [job.nodes, job.edges]);
98
+ const [zoom, setZoom] = (0, import_react.useState)(1);
99
+ const arrow = (0, import_react.useId)().replaceAll(":", "");
100
+ const selectedButton = (0, import_react.useRef)(null);
101
+ (0, import_react.useEffect)(() => {
102
+ selectedButton.current?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
103
+ }, [selected]);
104
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "br-section", "aria-label": "Execution graph", children: [
105
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-section-head", children: [
106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "Execution graph" }),
107
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-toolbar", children: [
108
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { "aria-label": "Zoom out", disabled: zoom <= 0.5, onClick: () => setZoom((v) => Math.max(0.5, v - 0.25)), children: "\u2212" }),
109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { "aria-label": "Reset zoom", onClick: () => setZoom(1), children: [
110
+ Math.round(zoom * 100),
111
+ "%"
112
+ ] }),
113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { "aria-label": "Zoom in", disabled: zoom >= 1.5, onClick: () => setZoom((v) => Math.min(1.5, v + 0.25)), children: "+" })
114
+ ] })
115
+ ] }),
116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "br-graph", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: layout.width * zoom, height: layout.height * zoom }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-graph-canvas", style: { width: layout.width, height: layout.height, transform: `scale(${zoom})`, transformOrigin: "top left" }, children: [
117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: layout.width, height: layout.height, "aria-hidden": "true", children: [
118
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("marker", { id: arrow, viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "5", markerHeight: "5", orient: "auto-start-reverse", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M 0 0 L 10 5 L 0 10 z", style: { fill: "var(--br-muted)" } }) }) }),
119
+ job.edges.map((edge, i) => {
120
+ const a = layout.positions.get(edge.from), b = layout.positions.get(edge.to);
121
+ if (!a || !b) return null;
122
+ const x = a.x + 77, y = a.y + 72, endX = b.x + 77, endY = b.y;
123
+ const d = edge.feedback ? `M ${a.x} ${a.y + 36} C 6 ${a.y + 36}, 6 ${b.y + 36}, ${b.x} ${b.y + 36}` : `M ${x} ${y} C ${x} ${y + 26}, ${endX} ${endY - 26}, ${endX} ${endY}`;
124
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
125
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d, strokeDasharray: edge.feedback ? "4 4" : void 0, markerEnd: `url(#${arrow})`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("title", { children: [
126
+ edge.from,
127
+ " \u2192 ",
128
+ edge.to,
129
+ edge.choice ? ` (${edge.choice})` : "",
130
+ edge.feedback ? ` \xB7 loop ${edge.feedback}` : "",
131
+ edge.executionId ? ` \xB7 ${edge.executionId}` : ""
132
+ ] }) }),
133
+ edge.choice && !edge.feedback && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: (x + endX) / 2 + 5, y: (y + endY) / 2 - 4, children: edge.choice.slice(0, 22) })
134
+ ] }, i);
135
+ })
136
+ ] }),
137
+ job.nodes.map((node) => {
138
+ const p = layout.positions.get(node.id);
139
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { ref: selected === node.id ? selectedButton : void 0, className: "br-node", style: { left: p.x, top: p.y }, "aria-pressed": selected === node.id, onClick: () => select(node.id), title: `${node.id} \xB7 ${node.type} \xB7 ${node.status}`, children: [
140
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: node.type }),
141
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: node.id }),
142
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Status, { status: node.status })
143
+ ] }, node.id);
144
+ })
145
+ ] }) }) }),
146
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-legend", children: [
147
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Solid \xB7 dependency" }),
148
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Dashed \xB7 loop feedback" }),
149
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Click a node to inspect" })
150
+ ] }),
151
+ job.loops.map((loop) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "br-note", children: [
152
+ "Loop ",
153
+ loop.id,
154
+ " \xB7 entry ",
155
+ loop.entry,
156
+ " \xB7 maximum ",
157
+ loop.maxIterations,
158
+ " iterations"
159
+ ] }, loop.id))
160
+ ] });
161
+ }
162
+ function ExecutionDetail({ api, sessionId, job, execution }) {
163
+ const [detail, setDetail] = (0, import_react.useState)();
164
+ const [error, setError] = (0, import_react.useState)("");
165
+ const [offset, setOffset] = (0, import_react.useState)(0);
166
+ const [retry, setRetry] = (0, import_react.useState)(0);
167
+ (0, import_react.useEffect)(() => {
168
+ let active = true;
169
+ setDetail(void 0);
170
+ setError("");
171
+ void api.detail({ sessionId, jobId: job.jobId, executionId: execution.executionId, offset }).then((value) => {
172
+ if (active) setDetail(value);
173
+ }, (e) => {
174
+ if (active) setError(message(e));
175
+ });
176
+ return () => {
177
+ active = false;
178
+ };
179
+ }, [api, sessionId, job.jobId, job.status, execution.executionId, execution.status, offset, retry]);
180
+ const p = execution.progress;
181
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-detail-grid", children: [
183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Execution" }),
185
+ execution.executionId
186
+ ] }),
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Revision / iteration" }),
189
+ "r",
190
+ execution.revision,
191
+ " / ",
192
+ execution.iteration ?? "\u2014"
193
+ ] }),
194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
195
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Elapsed" }),
196
+ execution.latencyMs === void 0 ? "\u2014" : `${(execution.latencyMs / 1e3).toFixed(1)}s`
197
+ ] }),
198
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Worker" }),
200
+ p?.phase ?? execution.status
201
+ ] }),
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
204
+ "Context (",
205
+ p?.contextSource ?? "unreported",
206
+ ")"
207
+ ] }),
208
+ p ? `${number(p.contextTokens)}${p.contextWindow ? ` / ${number(p.contextWindow)}` : ""}` : "\u2014"
209
+ ] }),
210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Tool calls / rounds" }),
212
+ p ? `${p.toolCalls} / ${p.toolRounds}` : "\u2014"
213
+ ] })
214
+ ] }),
215
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { role: "alert", children: [
216
+ error,
217
+ " ",
218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setRetry((v) => v + 1), children: "Retry output" })
219
+ ] }),
220
+ !detail && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "br-muted", role: "status", children: "Loading output\u2026" }),
221
+ detail && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
222
+ detail.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", children: detail.error }),
223
+ detail.decision && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
224
+ "Decision: ",
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: detail.decision })
226
+ ] }),
227
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "br-output", "aria-label": "Execution output", children: detail.output || (execution.status === "running" ? "Output will appear when this execution finishes." : "No output for this execution.") }),
228
+ detail.total > 32768 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-toolbar", children: [
229
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: offset === 0, onClick: () => setOffset(Math.max(0, offset - 32768)), children: "Previous output" }),
230
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("small", { className: "br-muted", children: [
231
+ number(detail.offset),
232
+ "\u2013",
233
+ number(detail.next),
234
+ " / ",
235
+ number(detail.total),
236
+ " characters"
237
+ ] }),
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: detail.next >= detail.total, onClick: () => setOffset(detail.next), children: "Next output" })
239
+ ] }),
240
+ detail.workspace && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "Workspace and recovery refs" }),
242
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "br-output", children: detail.workspace })
243
+ ] })
244
+ ] })
245
+ ] });
246
+ }
247
+ function JobView({ job, api, sessionId, connected, act, busy, target }) {
248
+ const [selected, setSelected] = (0, import_react.useState)(() => job.executions.find((e) => e.executionId === target?.executionId)?.id ?? target?.nodeId ?? (target?.executionId ? "" : job.nodes[0]?.id ?? ""));
249
+ const [chosenExecution, setChosenExecution] = (0, import_react.useState)(target?.executionId ?? "");
250
+ const [tab, setTab] = (0, import_react.useState)("output");
251
+ const executions = job.executions.filter((e) => e.id === selected);
252
+ const execution = chosenExecution ? executions.find((e) => e.executionId === chosenExecution) : executions.at(-1);
253
+ const paused = execution && job.pausedExecutionIds.includes(execution.executionId);
254
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
255
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-stats", children: [
256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
257
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
258
+ job.executions.filter((e) => e.status === "completed").length,
259
+ "/",
260
+ job.executions.length
261
+ ] }),
262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Executions complete" })
263
+ ] }),
264
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
265
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: number(job.usage.inputTokens) }),
266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Input tokens" })
267
+ ] }),
268
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: number(job.usage.outputTokens) }),
270
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Output tokens" })
271
+ ] }),
272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
273
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: number(job.usage.cacheReadTokens + job.usage.cacheWriteTokens) }),
274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Cache tokens" })
275
+ ] })
276
+ ] }),
277
+ job.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "br-notice", role: "alert", children: job.error }),
278
+ job.pausedExecutionIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-notice", children: [
279
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
280
+ job.pausedExecutionIds.length,
281
+ " execution(s) paused"
282
+ ] }),
283
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "br-note", children: "Inspect the output, then resume outgoing scheduling. Job deadlines continue while paused." }),
284
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "br-primary", disabled: !connected || busy || job.status !== "running", onClick: () => act("resume", job.pausedExecutionIds), children: "Resume all paused" })
285
+ ] }),
286
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Graph, { job, selected, select: (id2) => {
287
+ setSelected(id2);
288
+ setChosenExecution("");
289
+ setTab("output");
290
+ } }),
291
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "br-section", "aria-label": "Execution details", children: [
292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-section-head", children: [
293
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: selected || "Execution details" }),
294
+ execution && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Status, { status: paused ? "waiting" : execution.status })
295
+ ] }),
296
+ chosenExecution && !execution && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { role: "status", children: [
297
+ "The requested execution (",
298
+ chosenExecution,
299
+ ") is no longer available. Select a node to inspect its current history."
300
+ ] }),
301
+ executions.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "br-muted", children: "Execution history" }),
303
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "br-select", "aria-label": "Execution history", value: execution?.executionId ?? "", onChange: (e) => setChosenExecution(e.target.value), children: executions.map((e) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: e.executionId, children: [
304
+ e.executionId,
305
+ " \xB7 ",
306
+ e.status,
307
+ e.iteration === void 0 ? "" : ` \xB7 iteration ${e.iteration}`
308
+ ] }, e.executionId)) })
309
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "br-muted", children: "This node has not run yet." }),
310
+ paused && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "br-primary", disabled: !connected || busy || job.status !== "running", onClick: () => act("resume", [execution.executionId]), children: "Resume this execution" }),
311
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-tabs", children: [
312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { "aria-pressed": tab === "output", onClick: () => setTab("output"), children: "Output & details" }),
313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { "aria-pressed": tab === "events", onClick: () => setTab("events"), children: [
314
+ "Event log (",
315
+ job.events.length,
316
+ ")"
317
+ ] })
318
+ ] }),
319
+ tab === "output" && execution && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExecutionDetail, { api, sessionId, job, execution }, execution.executionId),
320
+ tab === "events" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
321
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "br-note", children: "Latest 80 lifecycle events. Full results remain available through braid_status." }),
322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "br-events", children: [...job.events].reverse().map((event) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
323
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("time", { children: new Date(event.timestamp).toLocaleTimeString([], { hour12: false }) }),
324
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: event.message }),
326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("small", { children: [
327
+ "#",
328
+ event.sequence,
329
+ event.nodeId ? ` \xB7 ${event.nodeId}` : "",
330
+ event.executionId ? ` \xB7 ${event.executionId}` : ""
331
+ ] })
332
+ ] })
333
+ ] }, event.sequence)) })
334
+ ] }),
335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "br-note", children: [
336
+ "Cache read ",
337
+ number(job.usage.cacheReadTokens),
338
+ " \xB7 cache write ",
339
+ number(job.usage.cacheWriteTokens),
340
+ ". Usage is reported by DSH; monetary cost is not estimated."
341
+ ] })
342
+ ] })
343
+ ] });
344
+ }
345
+ function BraidPanelView(props) {
346
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SessionPanel, { ...props }, props.sessionId);
347
+ }
348
+ function SessionPanel({ sessionId, api, target }) {
349
+ const [jobId, setJobId] = (0, import_react.useState)(target?.jobId);
350
+ const [createdBefore, setCreatedBefore] = (0, import_react.useState)(target?.createdBefore);
351
+ const [frame, setFrame] = (0, import_react.useState)();
352
+ const [connected, setConnected] = (0, import_react.useState)(false);
353
+ const [error, setError] = (0, import_react.useState)("");
354
+ const [actionError, setActionError] = (0, import_react.useState)("");
355
+ const [busy, setBusy] = (0, import_react.useState)(false);
356
+ const [retry, setRetry] = (0, import_react.useState)(0);
357
+ (0, import_react.useEffect)(() => {
358
+ const controller = new AbortController();
359
+ setConnected(false);
360
+ setFrame(void 0);
361
+ setError("");
362
+ setActionError("");
363
+ void (async () => {
364
+ for (let attempt = 0; !controller.signal.aborted; attempt++) {
365
+ try {
366
+ for await (const value of api.watch({ sessionId, ...jobId ? { jobId } : {}, ...createdBefore === void 0 ? {} : { createdBefore } }, controller.signal)) {
367
+ if (controller.signal.aborted) return;
368
+ setFrame(value);
369
+ setConnected(true);
370
+ setError("");
371
+ attempt = 0;
372
+ }
373
+ if (!controller.signal.aborted) throw new Error("Braid connection closed");
374
+ } catch (e) {
375
+ if (controller.signal.aborted) return;
376
+ setConnected(false);
377
+ setError(`${message(e)}${attempt < 2 ? " \xB7 reconnecting\u2026" : ""}`);
378
+ if (attempt >= 2) return;
379
+ await new Promise((resolve) => {
380
+ const done = () => {
381
+ clearTimeout(timer);
382
+ controller.signal.removeEventListener("abort", done);
383
+ resolve();
384
+ };
385
+ const timer = setTimeout(done, 1e3 * 2 ** attempt);
386
+ controller.signal.addEventListener("abort", done, { once: true });
387
+ });
388
+ }
389
+ }
390
+ })();
391
+ return () => {
392
+ controller.abort();
393
+ };
394
+ }, [api, sessionId, jobId, createdBefore, retry]);
395
+ const job = frame?.job;
396
+ const [actionOwner, setActionOwner] = (0, import_react.useState)("");
397
+ const act = async (action, executionIds) => {
398
+ if (!job || !connected || busy) return;
399
+ const owner = job.jobId;
400
+ setBusy(true);
401
+ setActionOwner(owner);
402
+ setActionError("");
403
+ try {
404
+ await api.control({ sessionId, jobId: owner, action, executionIds, revision: job.revision });
405
+ } catch (e) {
406
+ setActionError(message(e));
407
+ } finally {
408
+ setBusy(false);
409
+ }
410
+ };
411
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "braid-panel", children: [
412
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "br-header", children: [
413
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "br-eyebrow", children: "Agent orchestration" }),
414
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-heading", children: [
415
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: "Braid" }),
416
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { role: "status", children: connected ? "\u25CF Live" : error ? "\u25CB Disconnected" : "Connecting\u2026" })
417
+ ] }),
418
+ frame && frame.rows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
419
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "br-select", "aria-label": "Braid job", disabled: busy, value: job?.jobId ?? "", onChange: (e) => {
420
+ setJobId(e.target.value);
421
+ setCreatedBefore(void 0);
422
+ setActionError("");
423
+ }, children: frame.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: row.jobId, children: [
424
+ row.handle,
425
+ " \xB7 ",
426
+ row.status,
427
+ " \xB7 ",
428
+ row.goal.slice(0, 70)
429
+ ] }, row.jobId)) }),
430
+ job && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
431
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "br-goal", children: job.goal }),
432
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-toolbar", children: [
433
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Status, { status: job.phase === "waiting" ? "waiting" : job.status }),
434
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("small", { className: "br-muted", children: [
435
+ "r",
436
+ job.revision,
437
+ " \xB7 ",
438
+ job.nodes.length,
439
+ " nodes"
440
+ ] }),
441
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "br-danger", style: { marginLeft: "auto" }, disabled: !connected || busy || job.status !== "running", onClick: () => void act("cancel", []), children: busy ? "Working\u2026" : "Cancel job" })
442
+ ] })
443
+ ] })
444
+ ] })
445
+ ] }),
446
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-notice", role: "alert", children: [
447
+ error,
448
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-toolbar", children: [
449
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setRetry((v) => v + 1), children: "Reconnect" }),
450
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => {
451
+ setJobId(void 0);
452
+ setCreatedBefore(void 0);
453
+ setRetry((v) => v + 1);
454
+ }, children: "Latest job" })
455
+ ] })
456
+ ] }),
457
+ actionError && actionOwner === job?.jobId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "br-notice", role: "alert", children: actionError }),
458
+ connected && frame?.rows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "br-empty", children: [
459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "No Braid jobs in this session" }),
460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Ask the agent to use Braid for a parallel or multi-step task. Its graph, execution history and results will appear here." })
461
+ ] }),
462
+ job && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
463
+ JobView,
464
+ {
465
+ job,
466
+ api,
467
+ sessionId,
468
+ connected,
469
+ act: (action, ids) => void act(action, ids),
470
+ busy,
471
+ target: target?.jobId && [job.jobId, job.handle].includes(target.jobId) ? target : void 0
472
+ },
473
+ job.jobId
474
+ )
475
+ ] });
476
+ }
477
+
478
+ // integrations/dsh/client/styles.ts
479
+ var styles = `
480
+ .braid-panel{--br-bg:var(--dsw-alias-bg-base,#fff);--br-fg:var(--dsw-alias-label-primary,#222b38);--br-soft:var(--dsw-alias-bg-module-platform,#f4f6f8);--br-line:var(--dsw-alias-border-l2,#dce2e8);--br-muted:var(--dsw-alias-label-secondary,#667384);--br-accent:#336dbe;--br-green:var(--dsw-alias-state-success-primary,#18794e);--br-red:var(--dsw-alias-state-error-primary,#ba3340);color:var(--br-fg);background:var(--br-bg);font:inherit;font-size:13px;line-height:1.5;box-sizing:border-box;height:100%;overflow:auto;min-width:0;container-type:inline-size}
481
+ .braid-panel *{box-sizing:border-box}.braid-panel button,.braid-panel select{font:inherit;color:inherit}.braid-panel button,.braid-panel select{border:1px solid var(--br-line);border-radius:7px;background:var(--br-bg);padding:6px 10px}.braid-panel button{cursor:pointer}.braid-panel button:hover:not(:disabled){border-color:var(--br-accent)}.braid-panel button:focus-visible,.braid-panel select:focus-visible{outline:2px solid var(--br-accent);outline-offset:2px}.braid-panel button:disabled{opacity:.45;cursor:default}.braid-panel .br-primary{background:var(--br-accent);color:white;border-color:transparent}.braid-panel .br-danger{color:var(--br-red)}
482
+ .br-header{padding:20px 18px 12px;border-bottom:1px solid var(--br-line)}.br-heading{display:flex;align-items:center;gap:9px}.br-heading h2{font-size:18px;letter-spacing:-.4px;margin:0}.br-heading small{margin-left:auto;color:var(--br-muted)}.br-eyebrow{text-transform:uppercase;letter-spacing:1.4px;font-size:10px;color:var(--br-muted);margin-bottom:5px}.br-select{width:100%;margin:14px 0 8px}.br-goal{margin:8px 0;overflow-wrap:anywhere}.br-muted{color:var(--br-muted)}.br-toolbar{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.br-status{display:inline-flex;align-items:center;gap:5px;font-size:11px;white-space:nowrap}.br-status:before{content:'';width:6px;height:6px;border-radius:50%;background:var(--br-muted)}.br-status[data-status=running]:before,.br-status[data-status=runnable]:before{background:var(--br-accent)}.br-status[data-status=completed]:before{background:var(--br-green)}.br-status[data-status=failed]:before,.br-status[data-status=cancelled]:before{background:var(--br-red)}.br-status[data-status=waiting]:before{background:#ba861a}
483
+ .br-stats{display:grid;grid-template-columns:repeat(4,1fr);padding:14px 18px;gap:8px;border-bottom:1px solid var(--br-line)}.br-stats strong{display:block;font-size:16px;font-variant-numeric:tabular-nums}.br-stats span{font-size:10px;color:var(--br-muted)}.br-section{padding:14px 18px;border-bottom:1px solid var(--br-line)}.br-section h3{font-size:12px;margin:0 0 10px}.br-section-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:10px}.br-section-head h3{margin:0}.br-section-head button{padding:2px 8px}.br-graph{overflow:auto;background:var(--br-soft);border:1px solid var(--br-line);border-radius:9px;max-height:420px}.br-graph-canvas{position:relative}.br-graph svg{position:absolute;inset:0;overflow:visible}.br-graph path{fill:none;stroke:var(--br-muted);stroke-width:1.2}.br-graph text{font-size:10px;fill:var(--br-muted)}.br-node{position:absolute;display:flex;flex-direction:column;align-items:flex-start;justify-content:center;width:154px;height:72px;text-align:left;gap:2px;box-shadow:0 2px 5px #00000006}.br-node[aria-pressed=true]{outline:2px solid var(--br-accent);outline-offset:2px}.br-node strong{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.br-node small{font-size:10px;color:var(--br-muted)}.br-legend{display:flex;gap:12px;flex-wrap:wrap;font-size:10px;color:var(--br-muted);margin-top:8px}.br-note{font-size:11px;color:var(--br-muted);margin:8px 0 0;overflow-wrap:anywhere}
484
+ .br-notice{margin:12px 18px;padding:10px 12px;border:1px solid var(--br-line);border-radius:8px;background:var(--br-soft);overflow-wrap:anywhere}.br-notice[role=alert]{color:var(--br-red)}.br-empty{padding:48px 24px;text-align:center}.br-empty h3{font-size:15px}.br-empty p{color:var(--br-muted);max-width:36em;margin:12px auto}.br-detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px 16px;font-size:11px;margin:12px 0}.br-detail-grid span{color:var(--br-muted);display:block}.br-output{white-space:pre-wrap;overflow-wrap:anywhere;background:var(--br-soft);border:1px solid var(--br-line);padding:12px;border-radius:8px;max-height:340px;overflow:auto;font:11px/1.65 ui-monospace,SFMono-Regular,monospace;margin:8px 0}.br-tabs{display:flex;gap:5px;margin:14px 0 10px}.br-tabs button[aria-pressed=true]{border-color:var(--br-accent);color:var(--br-accent)}.br-events{list-style:none;padding:0;margin:0;max-height:320px;overflow:auto}.br-events li{padding:8px 0;display:grid;grid-template-columns:64px 1fr;gap:8px;border-bottom:1px solid var(--br-line);font-size:11px}.br-events time{color:var(--br-muted);font-variant-numeric:tabular-nums}.br-events p{margin:0;overflow-wrap:anywhere}.br-events small{color:var(--br-muted)}.br-header-action{font:inherit;border:0;border-radius:6px;padding:5px 9px;background:transparent;color:inherit;cursor:pointer}.br-header-action:hover{background:#8882}
485
+ @container (max-width:330px){.br-stats{grid-template-columns:repeat(2,1fr)}.br-header,.br-section{padding-left:12px;padding-right:12px}}
486
+ @media(prefers-color-scheme:dark){.braid-panel{--br-bg:var(--dsw-alias-bg-base,#171b22);--br-fg:var(--dsw-alias-label-primary,#e0e6ee);--br-soft:var(--dsw-alias-bg-module-platform,#202630);--br-line:var(--dsw-alias-border-l2,#354050);--br-muted:var(--dsw-alias-label-secondary,#a0adbe);--br-accent:#80b6ff;--br-green:var(--dsw-alias-state-success-primary,#69d6a0);--br-red:var(--dsw-alias-state-error-primary,#ff919a)}.braid-panel .br-primary{color:#162336}}
487
+ [data-theme=dark] .braid-panel,.dark .braid-panel{--br-bg:var(--dsw-alias-bg-base,#171b22);--br-fg:var(--dsw-alias-label-primary,#e0e6ee);--br-soft:var(--dsw-alias-bg-module-platform,#202630);--br-line:var(--dsw-alias-border-l2,#354050);--br-muted:var(--dsw-alias-label-secondary,#a0adbe);--br-accent:#80b6ff;--br-green:var(--dsw-alias-state-success-primary,#69d6a0);--br-red:var(--dsw-alias-state-error-primary,#ff919a)}
488
+ `;
489
+ var toolStyles = `
490
+ .br-tool-call{font:inherit;font-size:12px;color:var(--dsw-alias-label-primary,inherit);border:1px solid var(--dsw-alias-border-l2,#8884);border-radius:9px;margin:8px 0;overflow:hidden}
491
+ .br-tool-call button{font:inherit;color:inherit;cursor:pointer}.br-tool-call button:focus-visible{outline:2px solid #639be6;outline-offset:-2px}.br-tool-call button:disabled{cursor:default;opacity:.6}
492
+ .br-tool-open{display:flex;width:100%;align-items:center;gap:10px;padding:12px;border:0;background:var(--dsw-alias-bg-module-platform,#8881);text-align:left}.br-tool-open:hover:not(:disabled){background:var(--dsw-alias-fill-l1,#8882)}.br-tool-open>span:last-child{margin-left:auto}
493
+ .br-tool-main{display:flex;flex-wrap:wrap;align-items:baseline;gap:3px 8px;min-width:0}.br-tool-target{overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary,inherit)}.br-tool-summary{flex-basis:100%;font-size:11px}.br-tool-call[data-state=failed] .br-tool-summary,.br-tool-error{color:var(--dsw-alias-state-error-primary,#c34e59)}
494
+ .br-tool-actions{display:flex;gap:8px;padding:6px 12px}.br-tool-actions button{border:0;background:transparent;font-size:11px;text-decoration:underline;text-underline-offset:3px;padding:3px 0}.br-tool-details{padding:8px 12px;border-top:1px solid var(--dsw-alias-border-l2,#8884)}.br-tool-details pre{font:11px/1.6 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;max-height:220px;overflow:auto}.br-tool-error{padding:0 12px;white-space:pre-wrap;overflow-wrap:anywhere;font-size:11px}
495
+ .br-tool-note{margin:4px 12px;color:var(--dsw-alias-label-secondary,inherit);font-size:10px;opacity:.8}.br-tool-changes{display:flex;flex-wrap:wrap;gap:5px;padding:8px 12px}.br-tool-changes span{padding:3px 6px;border-radius:4px;background:var(--dsw-alias-bg-module-platform,#8881);font-size:10px;overflow-wrap:anywhere}.br-tool-jobs{list-style:none;margin:0;padding:6px 12px;max-height:230px;overflow:auto}.br-tool-jobs button{display:flex;gap:12px;width:100%;padding:6px 0;border:0;background:transparent;text-align:left}.br-tool-jobs span{margin-left:auto}
496
+ `;
497
+
498
+ // integrations/dsh/client/navigation.ts
499
+ var braidToolNames = ["braid", "braid_status", "braid_update", "braid_resume", "braid_cancel"];
500
+ function record(value) {
501
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
502
+ }
503
+ function json(raw) {
504
+ try {
505
+ return record(JSON.parse(raw));
506
+ } catch {
507
+ return {};
508
+ }
509
+ }
510
+ var id = (value) => typeof value === "string" && value.trim() && value.length <= 1024 ? value : void 0;
511
+ function readNavigation(value) {
512
+ const source = record(value), target = {};
513
+ for (const key of ["jobId", "nodeId", "executionId"]) {
514
+ const text = id(source[key]);
515
+ if (text !== void 0) target[key] = text;
516
+ }
517
+ if (typeof source.createdBefore === "number" && Number.isSafeInteger(source.createdBefore) && source.createdBefore >= 0)
518
+ target.createdBefore = source.createdBefore;
519
+ return target;
520
+ }
521
+ function toolData(props) {
522
+ const args = json(props.phase === "result" ? props.block.call?.argsRaw ?? "" : props.phase === "start" ? props.block.argsRaw : "");
523
+ let result = {};
524
+ if (props.phase === "result" && !props.block.isError) for (const content of props.block.content) {
525
+ if (content.type !== "text") continue;
526
+ const value = json(content.text);
527
+ if (Object.keys(value).length > 0) {
528
+ result = value;
529
+ break;
530
+ }
531
+ }
532
+ return { args, result };
533
+ }
534
+ function toolNavigation(toolName, props) {
535
+ if (!braidToolNames.some((name) => name === toolName) || props.phase === "preparing") return null;
536
+ const { args, result } = toolData(props);
537
+ const jobId = id(result.canonicalJobId) ?? id(result.jobId) ?? id(args.jobId);
538
+ if (!jobId) return toolName === "braid_status" && !("jobId" in args || "nodeId" in args || "executionId" in args) ? {} : null;
539
+ const target = { jobId };
540
+ const node = record(result.node);
541
+ const executionId = id(args.executionId) ?? id(node.executionId) ?? (toolName === "braid_resume" && Array.isArray(args.executionIds) && args.executionIds.length === 1 ? id(args.executionIds[0]) : void 0);
542
+ const nodeId = id(args.nodeId) ?? id(node.id);
543
+ if (executionId) target.executionId = executionId;
544
+ if (nodeId) target.nodeId = nodeId;
545
+ const time = props.block.time;
546
+ if (Number.isSafeInteger(time) && time >= 0) target.createdBefore = time;
547
+ return target;
548
+ }
549
+ var array = (value) => Array.isArray(value) ? value : [];
550
+ var revision = (value) => typeof value === "number" && Number.isSafeInteger(value) ? `r${value}` : "";
551
+ var names = (values) => {
552
+ const ids = values.map((value) => id(value) ?? id(record(value).id)).filter(Boolean);
553
+ return ids.slice(0, 8).join(", ") + (ids.length > 8 ? ` \u2026 (+${ids.length - 8})` : "");
554
+ };
555
+ function toolPresentation(toolName, props) {
556
+ const { args, result } = toolData(props);
557
+ const label = id(result.handle) ?? id(args.jobId) ?? id(result.jobId) ?? "";
558
+ const model = { label, summary: "", changes: [], jobs: [] };
559
+ const failed = props.phase === "result" && props.block.isError;
560
+ const settled = props.phase === "result";
561
+ if (toolName === "braid_update") {
562
+ const revisions = [revision(args.expectedRevision), revision(result.revision)].filter(Boolean).join(" \u2192 ");
563
+ model.summary = `${failed ? "Update rejected \xB7 not applied" : settled ? "Graph updated" : "Updating graph"}${revisions ? ` \xB7 ${revisions}` : ""}`;
564
+ for (const [key, title] of [["upsertNodes", "Upsert nodes"], ["removeNodeIds", "Remove nodes"], ["resume", "Resume executions"]]) {
565
+ const values = array(args[key]);
566
+ if (values.length) model.changes.push(`${title}: ${names(values)}`);
567
+ }
568
+ for (const [key, title] of [["addEdges", "Add edges"], ["removeEdges", "Remove edges"]]) {
569
+ const values = array(args[key]);
570
+ if (values.length) model.changes.push(`${title}: ${values.length}`);
571
+ }
572
+ if ("loops" in args) model.changes.push(`Replace loops: ${array(args.loops).length}`);
573
+ if ("promptTemplates" in args) model.changes.push(`Prompt templates: ${Object.keys(record(args.promptTemplates)).length}`);
574
+ } else if (toolName === "braid_resume") {
575
+ model.summary = failed ? "Resume rejected" : `${settled ? "Resumed" : "Resuming"} ${array(args.executionIds).length} execution(s)`;
576
+ const ids = names(array(args.executionIds));
577
+ if (ids) model.changes.push(ids);
578
+ if (Array.isArray(result.pausedExecutionIds)) model.changes.push(`${result.pausedExecutionIds.length} still paused \xB7 ${revision(result.revision)}`);
579
+ } else if (toolName === "braid_cancel") {
580
+ model.summary = failed ? "Cancellation failed" : !settled ? "Requesting cancellation" : result.cancelled === false ? "Job already finished" : "Cancellation requested";
581
+ } else if (toolName === "braid_status" && Array.isArray(result.jobs)) {
582
+ model.summary = `${result.jobs.length} job(s) at this call`;
583
+ model.jobs = result.jobs.flatMap((value) => {
584
+ const row = record(value), jobId = id(row.jobId);
585
+ return jobId ? [{ label: id(row.handle) ?? jobId, status: id(row.status) ?? "", target: { jobId, createdBefore: props.block.time } }] : [];
586
+ });
587
+ } else if (toolName === "braid_status") {
588
+ const execution = record(result.execution), node = record(result.node);
589
+ const executions = Object.values(record(execution.executions));
590
+ const details = id(node.status) ? [`execution ${id(node.status)}`, id(execution.status) ? `graph ${id(execution.status)}` : void 0, revision(execution.revision)] : [id(execution.status) ?? id(result.status), revision(execution.revision)];
591
+ if (executions.length) details.push(`${executions.filter((value) => record(value).status === "completed").length}/${executions.length} executions complete`);
592
+ if (Array.isArray(execution.pausedExecutionIds) && execution.pausedExecutionIds.length) details.push(`${execution.pausedExecutionIds.length} paused`);
593
+ model.summary = failed ? "Status query failed" : details.filter(Boolean).join(" \xB7 ") || (settled ? "Recorded status" : "Reading status");
594
+ } else {
595
+ const graph = record(result.graph), nodes = array(graph.nodes).length || array(args.nodes).length;
596
+ model.summary = failed ? "Submission failed" : `${settled ? "Graph submitted" : "Starting graph"}${nodes ? ` \xB7 ${nodes} nodes` : ""}`;
597
+ }
598
+ if (result.truncated === true) model.changes.push("Large result saved to file; view fullOutputPath in tool details");
599
+ return model;
600
+ }
601
+
602
+ // integrations/dsh/client/ToolCall.tsx
603
+ var import_jsx_runtime2 = require("react/jsx-runtime");
604
+ function BraidToolCall(props) {
605
+ const { toolName, phase, block, openBraid, useDisclosure, inspect } = props;
606
+ const { expanded, toggle } = useDisclosure();
607
+ const target = toolNavigation(toolName, props);
608
+ const model = toolPresentation(toolName, props);
609
+ const failed = phase === "result" && block.isError;
610
+ const state = failed ? "failed" : phase === "result" ? "completed" : phase === "preparing" ? "preparing" : "running";
611
+ const raw = phase === "result" ? block.call?.argsRaw : phase === "start" ? block.argsRaw : void 0;
612
+ const output = phase === "result" ? block.content.filter((c) => c.type === "text").map((c) => c.text).join("\n") : "";
613
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "br-tool-call", "data-state": state, children: [
614
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
615
+ "button",
616
+ {
617
+ type: "button",
618
+ className: "br-tool-open",
619
+ disabled: !target,
620
+ onClick: () => {
621
+ if (target) openBraid(target);
622
+ },
623
+ "aria-label": `Open Braid view for ${toolName}`,
624
+ title: target ? "Open Braid graph and execution details" : "Waiting for a Braid job receipt",
625
+ children: [
626
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": "true", children: "\u2387" }),
627
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "br-tool-main", children: [
628
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: toolName }),
629
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "br-tool-target", children: [
630
+ model.label,
631
+ target?.executionId ? ` / ${target.executionId}` : target?.nodeId ? ` / ${target.nodeId}` : ""
632
+ ] }),
633
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "br-tool-summary", children: model.summary })
634
+ ] }),
635
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": "true", children: "\u2197" })
636
+ ]
637
+ }
638
+ ),
639
+ model.changes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "br-tool-changes", "aria-label": failed ? "Changes not applied" : "Operation details", children: model.changes.map((change, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: change }, i)) }),
640
+ model.jobs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "br-tool-jobs", children: model.jobs.map((job) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", onClick: () => openBraid(job.target), "aria-label": `View ${job.label}`, children: [
641
+ job.label,
642
+ " ",
643
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: job.status }),
644
+ " \u2197"
645
+ ] }) }, job.target.jobId)) }),
646
+ phase === "result" && target && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "br-tool-note", children: "Recorded result \xB7 open the graph for live state" }),
647
+ failed && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "alert", className: "br-tool-error", children: block.error?.reason ?? block.error?.code ?? (output.slice(0, 600) || "Braid tool call failed. Expand the result for details.") }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "br-tool-actions", children: [
649
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", "aria-expanded": expanded, onClick: toggle, children: [
650
+ expanded ? "Hide" : "Show",
651
+ " tool details"
652
+ ] }),
653
+ inspect && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", onClick: inspect, children: "Inspect call" })
654
+ ] }),
655
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "br-tool-details", children: [
656
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: "Arguments" }),
657
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { children: raw ?? "Arguments are being prepared\u2026" }),
658
+ phase === "result" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
659
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: "Result" }),
660
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { children: output || "No text result." })
661
+ ] })
662
+ ] })
663
+ ] });
664
+ }
665
+
666
+ // integrations/dsh/client/index.tsx
667
+ var import_jsx_runtime3 = require("react/jsx-runtime");
668
+ var inject = ["remote", "slots", "sidebarRight", "sidebarRightTabs"];
669
+ async function apply(ctx) {
670
+ await ctx.remote.$mount({ package: PANEL_PACKAGE, descriptors: panelDescriptors });
671
+ ctx.inject(["remote.braidPanel"], (ctx2) => {
672
+ const remote = ctx2.remote.braidPanel;
673
+ const sidebar = ctx2.sidebarRight;
674
+ const api = {
675
+ watch: (request, signal) => remote.watch(request, signal),
676
+ control: async (request) => {
677
+ const result = await remote.control(request);
678
+ if (!result.ok) throw result.error;
679
+ return result.value;
680
+ },
681
+ detail: async (request) => {
682
+ const result = await remote.detail(request);
683
+ if (!result.ok) throw result.error;
684
+ return result.value;
685
+ }
686
+ };
687
+ ctx2.effect(() => {
688
+ const style = document.createElement("style");
689
+ style.dataset.plugin = PANEL_PACKAGE;
690
+ style.textContent = styles + toolStyles;
691
+ document.head.append(style);
692
+ return () => {
693
+ style.remove();
694
+ };
695
+ });
696
+ ctx2.effect(() => ctx2.sidebarRightTabs.register({
697
+ id: PANEL_PACKAGE,
698
+ kind: "braid",
699
+ title: () => "Braid",
700
+ guide: [{ id: "braid", order: 50, title: () => "Braid", description: () => "Live agent graphs, execution details and controls" }]
701
+ }));
702
+ ctx2.effect(() => ctx2.slots.inject("sidebar.right.pane.tab", () => ctx2.slots.register({
703
+ name: "sidebar.right.pane.tab",
704
+ key: PANEL_PACKAGE,
705
+ inject: (sessionId) => ({ api, sessionId })
706
+ }, PanelSeat)));
707
+ ctx2.effect(() => ctx2.slots.inject("conversation.session.header.actions", () => ctx2.slots.register({
708
+ name: "conversation.session.header.actions",
709
+ id: "braid",
710
+ order: 25,
711
+ inject: () => ({ open: () => {
712
+ sidebar.openTab("braid");
713
+ } })
714
+ }, HeaderAction)));
715
+ for (const key of braidToolNames) ctx2.effect(() => ctx2.slots.inject("tool.call.toolview", () => ctx2.slots.register({
716
+ name: "tool.call.toolview",
717
+ key,
718
+ inject: (sessionId) => ({ openBraid: (target) => {
719
+ sidebar.openTabIn(sessionId, "braid", { params: target });
720
+ } })
721
+ }, BraidToolCall)));
722
+ });
723
+ }
724
+ function HeaderAction({ open }) {
725
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "br-header-action", onClick: open, title: "Open Braid graph and execution details", "aria-label": "Open Braid panel", children: "\u2387 Braid" });
726
+ }
727
+ function PanelSeat({ sessionId, api, useTabInfo }) {
728
+ const { tab: { navigation } } = useTabInfo();
729
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BraidPanelView, { sessionId, api, target: readNavigation(navigation.params) }, navigation.revision);
730
+ }
731
+ return module.exports; } });
732
+ //# sourceMappingURL=client.js.map