dawlad-paperclip-direction 0.2.1 → 0.2.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/manifest.js CHANGED
@@ -11,7 +11,7 @@ var route = (routeKey, method, path) => ({
11
11
  var manifest = {
12
12
  id: "dawlad.direction",
13
13
  apiVersion: 1,
14
- version: "0.2.1",
14
+ version: "0.2.2",
15
15
  displayName: "Direction",
16
16
  description: "Horizon, campaigns, milestones and one queue of decisions: where Dawlad is heading and what's in the way.",
17
17
  author: "Aly Solamon",
package/dist/ui/index.js CHANGED
@@ -216,8 +216,31 @@ html:has([data-dx-page="decisions"]) ol[data-slot="breadcrumb-list"]:has(a[href$
216
216
  .dx-err { color: var(--dx-red); font-size: 13px; }
217
217
  .dx-tabs-line { display: flex; gap: 6px; }
218
218
  /* Decisions: documents beside the card */
219
- .dx-dq.beside { grid-template-columns: 260px minmax(360px, 1fr) minmax(380px, 44%); }
220
- .dx-dq.beside.wide { grid-template-columns: minmax(340px, .8fr) minmax(480px, 1.6fr); }
219
+ .dx-dq.beside { grid-template-columns: 250px minmax(0, 1fr) minmax(0, 1.05fr); }
220
+ .dx-dq.beside.wide { grid-template-columns: minmax(0, .8fr) minmax(0, 1.6fr); }
221
+ .dx-dq.beside.closed { grid-template-columns: 250px minmax(0, 1fr) 44px; }
222
+ .dx-dq-main { min-width: 0; }
223
+ .dx-pane-rail { border-left: 1px solid var(--dx-line); display: flex; flex-direction: column; align-items: center; gap: 10px; padding-top: 12px; position: sticky; top: 0; height: calc(100vh - 60px); }
224
+ .dx-pane-rail-t { writing-mode: vertical-rl; font-size: 12px; color: var(--dx-muted); cursor: pointer; letter-spacing: .04em; }
225
+ .dx-pane-rail-t:hover { color: var(--dx-fg); }
226
+ .dx-menu-anchor { position: relative; flex: none; }
227
+ .dx-menu.right { left: auto; right: 0; }
228
+ .dx-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 30; width: 340px; max-width: calc(100vw - 32px); background: var(--dx-card); border: 1px solid var(--dx-line); border-radius: 10px; box-shadow: 0 8px 28px rgb(0 0 0 / .14); display: flex; flex-direction: column; max-height: 70vh; }
229
+ .dx-menu-search { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--dx-line); color: var(--dx-muted); }
230
+ .dx-menu-search input { border: 0; outline: none; background: transparent; color: var(--dx-fg); font-size: 13.5px; flex: 1; min-width: 0; }
231
+ .dx-menu-body { overflow: auto; padding: 4px 6px 8px; }
232
+ .dx-menu-label { font-size: 12px; color: var(--dx-muted); padding: 10px 8px 4px; }
233
+ .dx-menu-item { display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left; border: 0; background: transparent; color: var(--dx-fg); padding: 7px 8px; border-radius: 6px; cursor: pointer; font-size: 13.5px; }
234
+ .dx-menu-item:hover, .dx-menu-item:focus-visible { background: var(--dx-soft2); outline: none; }
235
+ .dx-menu-item svg { margin-top: 3px; flex: none; }
236
+ .dx-menu-item .t { display: flex; flex-direction: column; min-width: 0; flex: 1; }
237
+ .dx-menu-item .t > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
238
+ .dx-menu-item .s { font-size: 11.5px; color: var(--dx-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
239
+ .dx-menu-item .ck { color: var(--dx-fg); font-size: 12px; }
240
+ .dx-run-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; border-top: 1px solid var(--dx-line); border-bottom: 1px solid var(--dx-line); padding: 12px 0; margin-bottom: 16px; }
241
+ .dx-run-stats b { display: block; font-size: 16px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
242
+ .dx-run-stats span { font-size: 12px; color: var(--dx-muted); }
243
+ .dx-run-h { font-size: 14px; font-weight: 600; margin: 0 0 8px; }
221
244
  .dx-dq.beside.wide .dx-dq-list { display: none; }
222
245
  .dx-dq-foot { margin-top: 10px; border-top: 1px solid var(--dx-line); padding: 10px 8px 0; font-size: 12px; color: var(--dx-muted); cursor: pointer; }
223
246
  .dx-dq-foot:hover { color: var(--dx-fg); }
@@ -227,7 +250,8 @@ html:has([data-dx-page="decisions"]) ol[data-slot="breadcrumb-list"]:has(a[href$
227
250
  .dx-pill.lane-this-week { color: var(--dx-amber); background: var(--dx-amber-bg); border-color: transparent; }
228
251
  .dx-pill.lane-needs-work { color: var(--dx-amber); border-color: var(--dx-amber-line); }
229
252
  .dx-pane { border-left: 1px solid var(--dx-line); display: flex; flex-direction: column; min-width: 0; position: sticky; top: 0; height: calc(100vh - 60px); background: var(--dx-bg); }
230
- .dx-pane-tabs { display: flex; align-items: center; gap: 4px; padding: 10px 12px; border-bottom: 1px solid var(--dx-line); overflow-x: auto; flex: none; }
253
+ .dx-pane-head { display: flex; align-items: center; gap: 4px; padding: 10px 12px; border-bottom: 1px solid var(--dx-line); flex: none; min-width: 0; }
254
+ .dx-pane-tabs { display: flex; align-items: center; gap: 4px; overflow-x: auto; min-width: 0; flex: 0 1 auto; scrollbar-width: none; }
231
255
  .dx-pane-tab { display: inline-flex; align-items: center; gap: 6px; padding: 5px 6px 5px 9px; border-radius: 6px; font-size: 13px; color: var(--dx-muted); cursor: pointer; max-width: 220px; flex: none; }
232
256
  .dx-pane-tab .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
233
257
  .dx-pane-tab.on { background: var(--dx-soft2); color: var(--dx-fg); }
@@ -236,7 +260,7 @@ html:has([data-dx-page="decisions"]) ol[data-slot="breadcrumb-list"]:has(a[href$
236
260
  .dx-icon { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 6px; border: 0; background: transparent; color: var(--dx-muted); cursor: pointer; flex: none; }
237
261
  .dx-icon:hover, .dx-icon.on { background: var(--dx-soft2); color: var(--dx-fg); }
238
262
  .dx-icon:focus-visible, .dx-pane-tab:focus-visible { outline: 2px solid var(--dx-blue); outline-offset: 1px; }
239
- .dx-pane-add { border-bottom: 1px solid var(--dx-line); padding: 6px 14px 10px; max-height: 40%; overflow: auto; flex: none; }
263
+ .dx-pane-add-unused { border-bottom: 1px solid var(--dx-line); padding: 6px 14px 10px; max-height: 40%; overflow: auto; flex: none; }
240
264
  .dx-pane-pick { display: flex; align-items: center; gap: 8px; padding: 6px 6px; border-radius: 6px; cursor: pointer; font-size: 13px; }
241
265
  .dx-pane-pick:hover { background: var(--dx-soft); }
242
266
  .dx-pane-pick .t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@@ -249,13 +273,15 @@ html:has([data-dx-page="decisions"]) ol[data-slot="breadcrumb-list"]:has(a[href$
249
273
  .dx-convo { padding: 2px 0 12px 22px; display: grid; gap: 12px; }
250
274
  .dx-convo-c { border-left: 2px solid var(--dx-line); padding-left: 10px; }
251
275
  @media (max-width: 1200px) {
252
- .dx-dq.beside, .dx-dq.beside.wide { grid-template-columns: 240px minmax(0, 1fr); }
276
+ .dx-dq.beside, .dx-dq.beside.wide, .dx-dq.beside.closed { grid-template-columns: 240px minmax(0, 1fr); }
277
+ .dx-pane-rail { grid-column: 1 / -1; flex-direction: row; height: auto; position: static; border-left: 0; border-top: 1px solid var(--dx-line); padding: 8px 12px; }
278
+ .dx-pane-rail-t { writing-mode: horizontal-tb; }
253
279
  .dx-dq.beside.wide .dx-dq-list { display: flex; }
254
280
  .dx-pane { grid-column: 1 / -1; position: static; height: 75vh; border-left: 0; border-top: 1px solid var(--dx-line); }
255
281
  .dx-wide-btn { display: none; }
256
282
  }
257
283
  @media (max-width: 760px) {
258
- .dx-dq.beside, .dx-dq.beside.wide { grid-template-columns: minmax(0, 1fr); margin: -16px; }
284
+ .dx-dq.beside, .dx-dq.beside.wide, .dx-dq.beside.closed { grid-template-columns: minmax(0, 1fr); margin: -16px; }
259
285
  .dx-dq-list { border-right: 0; border-bottom: 1px solid var(--dx-line); max-height: 40vh; }
260
286
  .dx-dq-main { padding: 16px; }
261
287
  }
@@ -2147,7 +2173,7 @@ import { useEffect as useEffect4, useMemo as useMemo4, useState as useState7 } f
2147
2173
  import { MarkdownBlock as MarkdownBlock2, usePluginAction as usePluginAction2, usePluginData as usePluginData2 } from "@paperclipai/plugin-sdk/ui";
2148
2174
 
2149
2175
  // src/ui/beside.tsx
2150
- import { useEffect as useEffect3, useMemo as useMemo3, useState as useState6 } from "react";
2176
+ import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState6 } from "react";
2151
2177
  import { MarkdownBlock } from "@paperclipai/plugin-sdk/ui";
2152
2178
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2153
2179
  var get = async (path) => {
@@ -2223,30 +2249,76 @@ var Ico = {
2223
2249
  out: /* @__PURE__ */ jsx7("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx7("path", { d: "M14 4h6v6M20 4l-9 9M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5" }) }),
2224
2250
  doc: /* @__PURE__ */ jsx7("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx7("path", { d: "M14 3H6a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8z M14 3v5h5 M8 13h8M8 17h6" }) }),
2225
2251
  file: /* @__PURE__ */ jsx7("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx7("path", { d: "M4 5h16v14H4z M4 9h16 M9 9v10" }) }),
2252
+ search: /* @__PURE__ */ jsxs7("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2253
+ /* @__PURE__ */ jsx7("circle", { cx: "11", cy: "11", r: "7" }),
2254
+ /* @__PURE__ */ jsx7("path", { d: "m20 20-3.5-3.5" })
2255
+ ] }),
2256
+ run: /* @__PURE__ */ jsx7("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx7("path", { d: "M3 12h4l3-8 4 16 3-8h4" }) }),
2226
2257
  phone: /* @__PURE__ */ jsx7("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", children: /* @__PURE__ */ jsx7("path", { d: "M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3.1 19.5 19.5 0 0 1-6-6A19.8 19.8 0 0 1 2.1 4.2 2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7c.1 1 .4 1.9.7 2.8a2 2 0 0 1-.5 2.1L8.1 9.9a16 16 0 0 0 6 6l1.3-1.3a2 2 0 0 1 2.1-.4c.9.3 1.8.6 2.8.7a2 2 0 0 1 1.7 2z" }) })
2227
2258
  };
2228
- function BesidePane({ d, wide, setWide }) {
2259
+ function useRuns(issueId, companyId) {
2260
+ const [runs, setRuns] = useState6([]);
2261
+ const [agents, setAgents] = useState6([]);
2262
+ useEffect3(() => {
2263
+ if (!issueId) return;
2264
+ let live = true;
2265
+ get(`/issues/${issueId}/runs`).then((r) => live && setRuns(r.filter((x) => !["queued"].includes(x.status)).slice(0, 4))).catch(() => {
2266
+ });
2267
+ if (companyId) get(`/companies/${companyId}/agents`).then((a) => live && setAgents(a)).catch(() => {
2268
+ });
2269
+ return () => {
2270
+ live = false;
2271
+ };
2272
+ }, [issueId, companyId]);
2273
+ return { runs, agentName: (id) => agents.find((a) => a.id === id)?.name ?? "Agent" };
2274
+ }
2275
+ var minutes = (a, b) => a && b ? Math.max(1, Math.round((Date.parse(b) - Date.parse(a)) / 6e4)) : null;
2276
+ function BesidePane({ d, wide, setWide, onClose }) {
2229
2277
  const go = useGo("decide");
2230
2278
  const { all, issue, linked, loading, error } = useResources(d);
2279
+ const { runs, agentName } = useRuns(d.issueId, issue?.companyId ?? null);
2231
2280
  const [open, setOpen] = useState6([]);
2232
2281
  const [active, setActive] = useState6(null);
2233
- const [adding, setAdding] = useState6(false);
2282
+ const [menu, setMenu] = useState6(false);
2283
+ const [right, setRight] = useState6(false);
2284
+ const [q, setQ] = useState6("");
2234
2285
  const [asked, setAsked] = useState6(false);
2286
+ const menuRef = useRef2(null);
2235
2287
  useEffect3(() => {
2236
2288
  if (loading) return;
2237
2289
  const have = new Set(all.map((r) => r.key));
2238
2290
  let first = [...linked.docs, ...linked.files].filter((k) => have.has(k));
2239
2291
  if (!first.length) {
2240
2292
  const own = all.filter((r) => r.where === "task");
2241
- const pick = own.find((r) => r.primary) ?? own.find((r) => r.kind === "file" && /html/.test(r.file?.contentType ?? "")) ?? own.find((r) => r.kind === "doc");
2242
- first = pick ? [pick.key] : [];
2293
+ const pick2 = own.find((r) => r.primary) ?? own.find((r) => r.kind === "file" && /html/.test(r.file?.contentType ?? "")) ?? own.find((r) => r.kind === "doc");
2294
+ first = pick2 ? [pick2.key] : [];
2243
2295
  }
2244
2296
  setOpen([...new Set(first)]);
2245
2297
  setActive(first[0] ?? null);
2246
2298
  }, [loading, d.key]);
2247
- const missingLinks = [...linked.docs, ...linked.files].filter((k) => !all.some((r) => r.key === k));
2248
- const byKey = useMemo3(() => new Map(all.map((r) => [r.key, r])), [all]);
2299
+ useEffect3(() => {
2300
+ if (!menu) return;
2301
+ const away = (e) => {
2302
+ if (menuRef.current && !menuRef.current.contains(e.target)) setMenu(false);
2303
+ };
2304
+ const esc = (e) => {
2305
+ if (e.key === "Escape") setMenu(false);
2306
+ };
2307
+ document.addEventListener("mousedown", away);
2308
+ document.addEventListener("keydown", esc);
2309
+ return () => {
2310
+ document.removeEventListener("mousedown", away);
2311
+ document.removeEventListener("keydown", esc);
2312
+ };
2313
+ }, [menu]);
2314
+ const runRes = runs.map((r) => {
2315
+ const id = r.runId ?? r.id ?? "";
2316
+ const took = minutes(r.startedAt ?? r.createdAt, r.finishedAt);
2317
+ return { key: `run:${id}`, kind: "run", title: `${agentName(r.agentId)}'s run ${id.slice(0, 8)}`, sub: `${ago(r.createdAt)} \xB7 ${r.status}${took ? ` \xB7 ${took} min` : ""} \xB7 read, made, changed`, issue, where: "task", run: { ...r, id, agentName: agentName(r.agentId) } };
2318
+ });
2319
+ const byKey = useMemo3(() => new Map([...all, ...runRes].map((r) => [r.key, r])), [all, runs]);
2249
2320
  const cur = active ? byKey.get(active) : void 0;
2321
+ const missingLinks = [...linked.docs, ...linked.files].filter((k) => !all.some((r) => r.key === k));
2250
2322
  const toggle = (k) => {
2251
2323
  if (open.includes(k)) {
2252
2324
  const rest = open.filter((x) => x !== k);
@@ -2257,6 +2329,12 @@ function BesidePane({ d, wide, setWide }) {
2257
2329
  setActive(k);
2258
2330
  }
2259
2331
  };
2332
+ const pick = (k) => {
2333
+ if (!open.includes(k)) setOpen([...open, k]);
2334
+ setActive(k);
2335
+ setMenu(false);
2336
+ setQ("");
2337
+ };
2260
2338
  const askForEvidence = async () => {
2261
2339
  if (!issue) return;
2262
2340
  await fetch(`/api/issues/${issue.id}/comments`, {
@@ -2269,59 +2347,78 @@ function BesidePane({ d, wide, setWide }) {
2269
2347
  });
2270
2348
  setAsked(true);
2271
2349
  };
2350
+ const match = (r) => !q || `${r.title} ${r.sub}`.toLowerCase().includes(q.toLowerCase());
2272
2351
  const groups = [
2273
- { label: issue ? `This task \xB7 ${issue.identifier}` : "This task", list: all.filter((r) => r.where === "task") },
2274
- { label: "Parent task", list: all.filter((r) => r.where === "parent") },
2275
- { label: "Linked from the card", list: all.filter((r) => r.where === "linked") }
2276
- ].filter((g) => g.list.length);
2352
+ { label: "Open", list: open.map((k) => byKey.get(k)).filter((r) => !!r) },
2353
+ { label: issue ? `Task documents \xB7 ${issue.identifier}` : "Task documents", list: all.filter((r) => r.where === "task" && !open.includes(r.key)) },
2354
+ { label: "Parent task", list: all.filter((r) => r.where === "parent" && !open.includes(r.key)) },
2355
+ { label: "Linked from the card", list: all.filter((r) => r.where === "linked" && !open.includes(r.key)) },
2356
+ { label: "Runs", list: runRes.filter((r) => !open.includes(r.key)) }
2357
+ ].map((g) => ({ ...g, list: g.list.filter(match) })).filter((g) => g.list.length);
2358
+ const icon = (r) => r.kind === "doc" ? Ico.doc : r.kind === "run" ? Ico.run : Ico.file;
2359
+ const talkTo = d.waitingAgentId && d.waitingName && d.waitingName !== "Jev" ? { id: d.waitingAgentId, name: d.waitingName } : null;
2277
2360
  return /* @__PURE__ */ jsxs7("aside", { className: "dx-pane", children: [
2278
- /* @__PURE__ */ jsxs7("div", { className: "dx-pane-tabs", children: [
2279
- open.map((k) => {
2361
+ /* @__PURE__ */ jsxs7("div", { className: "dx-pane-head", children: [
2362
+ /* @__PURE__ */ jsx7("div", { className: "dx-pane-tabs", children: open.map((k) => {
2280
2363
  const r = byKey.get(k);
2281
2364
  return r && /* @__PURE__ */ jsxs7("span", { className: `dx-pane-tab${k === active ? " on" : ""}`, onClick: () => setActive(k), title: r.title, children: [
2282
- r.kind === "doc" ? Ico.doc : Ico.file,
2365
+ icon(r),
2283
2366
  /* @__PURE__ */ jsx7("span", { className: "t", children: r.title }),
2284
2367
  /* @__PURE__ */ jsx7("button", { "aria-label": `Close ${r.title}`, onClick: (e) => {
2285
2368
  e.stopPropagation();
2286
2369
  toggle(k);
2287
2370
  }, children: Ico.x })
2288
2371
  ] }, k);
2289
- }),
2372
+ }) }),
2373
+ /* @__PURE__ */ jsxs7("div", { className: "dx-menu-anchor", ref: menuRef, children: [
2374
+ /* @__PURE__ */ jsx7("button", { className: `dx-icon${menu ? " on" : ""}`, "aria-label": "Add a tab", "aria-expanded": menu, title: "Add a document, file or run", onClick: (e) => {
2375
+ const x = e.currentTarget.getBoundingClientRect();
2376
+ setRight(x.left + 360 > window.innerWidth - 16);
2377
+ setMenu(!menu);
2378
+ }, children: Ico.plus }),
2379
+ menu && /* @__PURE__ */ jsxs7("div", { className: `dx-menu${right ? " right" : ""}`, role: "menu", children: [
2380
+ /* @__PURE__ */ jsxs7("label", { className: "dx-menu-search", children: [
2381
+ Ico.search,
2382
+ /* @__PURE__ */ jsx7("input", { autoFocus: true, placeholder: "Search tabs and resources\u2026", value: q, onChange: (e) => setQ(e.target.value) })
2383
+ ] }),
2384
+ /* @__PURE__ */ jsxs7("div", { className: "dx-menu-body", children: [
2385
+ groups.map((g) => /* @__PURE__ */ jsxs7("div", { children: [
2386
+ /* @__PURE__ */ jsx7("div", { className: "dx-menu-label", children: g.label }),
2387
+ g.list.map((r) => /* @__PURE__ */ jsxs7("button", { className: "dx-menu-item", role: "menuitem", onClick: () => g.label === "Open" ? (setActive(r.key), setMenu(false)) : pick(r.key), children: [
2388
+ icon(r),
2389
+ /* @__PURE__ */ jsxs7("span", { className: "t", children: [
2390
+ /* @__PURE__ */ jsx7("span", { children: r.title }),
2391
+ /* @__PURE__ */ jsx7("span", { className: "s", children: r.sub })
2392
+ ] }),
2393
+ g.label === "Open" && /* @__PURE__ */ jsx7("span", { className: "ck", children: "\u2713" })
2394
+ ] }, r.key))
2395
+ ] }, g.label)),
2396
+ (!q || /talk|ask|call|jev/i.test(q) || talkTo && talkTo.name.toLowerCase().includes(q.toLowerCase())) && /* @__PURE__ */ jsxs7("div", { children: [
2397
+ /* @__PURE__ */ jsx7("div", { className: "dx-menu-label", children: "Talk" }),
2398
+ talkTo && /* @__PURE__ */ jsx7(TalkMenuItem, { d, agentId: talkTo.id, label: `Talk it through with ${talkTo.name}`, sub: "a call \xB7 knows this decision, its documents and runs", onDone: () => setMenu(false) }),
2399
+ /* @__PURE__ */ jsx7(TalkMenuItem, { d, agentId: null, label: "Ask Jev about this decision", sub: "a call \xB7 the bigger picture", onDone: () => setMenu(false) })
2400
+ ] }),
2401
+ !groups.length && q && /* @__PURE__ */ jsxs7("div", { className: "dx-muted dx-small", style: { padding: "8px 10px" }, children: [
2402
+ 'Nothing matches "',
2403
+ q,
2404
+ '".'
2405
+ ] })
2406
+ ] })
2407
+ ] })
2408
+ ] }),
2290
2409
  /* @__PURE__ */ jsx7("span", { style: { flex: 1 } }),
2291
- /* @__PURE__ */ jsx7("button", { className: `dx-icon${adding ? " on" : ""}`, "aria-label": "Add a tab", title: "Add a document or file", onClick: () => setAdding(!adding), children: Ico.plus }),
2292
2410
  /* @__PURE__ */ jsx7("button", { className: "dx-icon dx-wide-btn", "aria-label": wide ? "Narrow the panel" : "Widen the panel", title: wide ? "Narrow" : "Widen", onClick: () => setWide(!wide), children: wide ? Ico.narrow : Ico.wide }),
2293
- cur && /* @__PURE__ */ jsx7("a", { className: "dx-icon", title: "Open in the task", ...go.hostLink(cur.kind === "doc" ? `/issues/${cur.issue.identifier}#document-${cur.doc.key}` : `/issues/${cur.issue.identifier}`), children: Ico.out })
2294
- ] }),
2295
- adding && /* @__PURE__ */ jsxs7("div", { className: "dx-pane-add", children: [
2296
- groups.map((g) => /* @__PURE__ */ jsxs7("div", { children: [
2297
- /* @__PURE__ */ jsx7("div", { className: "dx-group-label", style: { margin: "6px 0 2px" }, children: g.label }),
2298
- g.list.map((r) => /* @__PURE__ */ jsxs7("div", { className: "dx-pane-pick", onClick: () => {
2299
- toggle(r.key);
2300
- setAdding(false);
2301
- }, children: [
2302
- r.kind === "doc" ? Ico.doc : Ico.file,
2303
- /* @__PURE__ */ jsx7("span", { className: "t", children: r.title }),
2304
- /* @__PURE__ */ jsx7("span", { className: "dx-muted dx-xs", children: r.sub }),
2305
- open.includes(r.key) && /* @__PURE__ */ jsx7("span", { className: "dx-xs", children: "\u2713" })
2306
- ] }, r.key))
2307
- ] }, g.label)),
2308
- !groups.length && /* @__PURE__ */ jsx7("div", { className: "dx-muted dx-small", children: "Nothing on this task or its parent yet." })
2411
+ cur && /* @__PURE__ */ jsx7("a", { className: "dx-icon", title: "Open in the task", ...go.hostLink(cur.kind === "doc" ? `/issues/${cur.issue.identifier}#document-${cur.doc.key}` : cur.kind === "run" ? `/agents/${cur.run.agentId}/runs/${cur.run.id}` : `/issues/${cur.issue.identifier}`), children: Ico.out }),
2412
+ /* @__PURE__ */ jsx7("button", { className: "dx-icon", "aria-label": "Hide the panel", title: "Hide the panel", onClick: onClose, children: Ico.x })
2309
2413
  ] }),
2310
2414
  /* @__PURE__ */ jsxs7("div", { className: `dx-pane-body${cur?.kind === "file" && /html|pdf/.test(cur.file.contentType) ? " flush" : ""}`, children: [
2311
2415
  loading ? /* @__PURE__ */ jsx7("div", { className: "dx-muted dx-small", children: "Loading what this rests on\u2026" }) : error ? /* @__PURE__ */ jsxs7("div", { className: "dx-err", children: [
2312
2416
  "Couldn't load the task's documents: ",
2313
2417
  error
2314
- ] }) : cur ? /* @__PURE__ */ jsx7(Viewer, { r: cur }) : /* @__PURE__ */ jsxs7("div", { className: "dx-pane-empty", children: [
2315
- /* @__PURE__ */ jsxs7("b", { children: [
2316
- "Nothing is attached to ",
2317
- issue?.identifier ?? "this decision",
2318
- "."
2319
- ] }),
2320
- /* @__PURE__ */ jsxs7("span", { className: "dx-muted dx-small", children: [
2321
- "The card should link the documents it rests on. ",
2322
- d.waitingName ? `${d.waitingName} gets a note on the task.` : ""
2323
- ] }),
2324
- issue && (asked ? /* @__PURE__ */ jsx7("span", { className: "dx-small", children: "Asked. The agent wakes with your note." }) : /* @__PURE__ */ jsxs7("button", { className: "dx-btn", onClick: askForEvidence, children: [
2418
+ ] }) : cur ? cur.kind === "run" ? /* @__PURE__ */ jsx7(RunView, { r: cur }) : /* @__PURE__ */ jsx7(Viewer, { r: cur }) : /* @__PURE__ */ jsxs7("div", { className: "dx-pane-empty", children: [
2419
+ /* @__PURE__ */ jsx7("b", { children: "Nothing is open." }),
2420
+ /* @__PURE__ */ jsx7("span", { className: "dx-muted dx-small", children: all.length ? "Use + to open a document, file or run from this task." : `Nothing is attached to ${issue?.identifier ?? "this decision"}. The card should link the documents it rests on.` }),
2421
+ !all.length && issue && (asked ? /* @__PURE__ */ jsx7("span", { className: "dx-small", children: "Asked. The agent wakes with your note." }) : /* @__PURE__ */ jsxs7("button", { className: "dx-btn", onClick: askForEvidence, children: [
2325
2422
  "Ask ",
2326
2423
  d.waitingName ?? "the agent",
2327
2424
  " to attach the evidence"
@@ -2339,6 +2436,86 @@ function BesidePane({ d, wide, setWide }) {
2339
2436
  ] })
2340
2437
  ] });
2341
2438
  }
2439
+ function PaneRail({ onOpen }) {
2440
+ return /* @__PURE__ */ jsxs7("aside", { className: "dx-pane-rail", children: [
2441
+ /* @__PURE__ */ jsx7("button", { className: "dx-icon", "aria-label": "Show the documents panel", title: "Show documents, files and runs", onClick: onOpen, children: Ico.doc }),
2442
+ /* @__PURE__ */ jsx7("span", { className: "dx-pane-rail-t", onClick: onOpen, children: "Documents" })
2443
+ ] });
2444
+ }
2445
+ function TalkMenuItem({ d, agentId, label, sub, onDone }) {
2446
+ const start = useTalk(d, agentId);
2447
+ return /* @__PURE__ */ jsxs7("button", { className: "dx-menu-item", role: "menuitem", onClick: () => {
2448
+ void start();
2449
+ onDone();
2450
+ }, children: [
2451
+ Ico.phone,
2452
+ /* @__PURE__ */ jsxs7("span", { className: "t", children: [
2453
+ /* @__PURE__ */ jsx7("span", { children: label }),
2454
+ /* @__PURE__ */ jsx7("span", { className: "s", children: sub })
2455
+ ] })
2456
+ ] });
2457
+ }
2458
+ function RunView({ r }) {
2459
+ const run = r.run;
2460
+ const [detail, setDetail] = useState6(null);
2461
+ const [posted, setPosted] = useState6(null);
2462
+ const go = useGo("decide");
2463
+ useEffect3(() => {
2464
+ let live = true;
2465
+ get(`/heartbeat-runs/${run.id}`).then((x) => live && setDetail(x)).catch(() => {
2466
+ });
2467
+ get(`/issues/${r.issue.id}/comments`).then((c) => live && setPosted(c.filter((x) => x.createdByRunId === run.id))).catch(() => live && setPosted([]));
2468
+ return () => {
2469
+ live = false;
2470
+ };
2471
+ }, [run.id]);
2472
+ const u = detail?.usageJson ?? null;
2473
+ const took = minutes(detail?.startedAt ?? run.createdAt, detail?.finishedAt ?? run.finishedAt);
2474
+ const k = (n) => n == null ? "\u2014" : n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
2475
+ const verb = run.status === "succeeded" ? "finished" : run.status;
2476
+ return /* @__PURE__ */ jsxs7("article", { children: [
2477
+ /* @__PURE__ */ jsxs7("h2", { style: { fontSize: 18, fontWeight: 600, margin: "0 0 2px" }, children: [
2478
+ run.agentName,
2479
+ "'s run ",
2480
+ verb,
2481
+ took ? ` in ${took} min` : ""
2482
+ ] }),
2483
+ /* @__PURE__ */ jsxs7("div", { className: "dx-muted dx-xs", style: { marginBottom: 14 }, children: [
2484
+ "Run ",
2485
+ run.id.slice(0, 8),
2486
+ " \xB7 ",
2487
+ new Date(run.createdAt).toLocaleString(),
2488
+ run.errorCode ? ` \xB7 ${run.errorCode}` : ""
2489
+ ] }),
2490
+ /* @__PURE__ */ jsxs7("div", { className: "dx-run-stats", children: [
2491
+ /* @__PURE__ */ jsxs7("div", { children: [
2492
+ /* @__PURE__ */ jsx7("b", { children: u?.model ?? "\u2014" }),
2493
+ /* @__PURE__ */ jsx7("span", { children: "model" })
2494
+ ] }),
2495
+ /* @__PURE__ */ jsxs7("div", { children: [
2496
+ /* @__PURE__ */ jsx7("b", { children: k((u?.inputTokens ?? 0) + (u?.cachedInputTokens ?? 0) || void 0) }),
2497
+ /* @__PURE__ */ jsx7("span", { children: "tokens read" })
2498
+ ] }),
2499
+ /* @__PURE__ */ jsxs7("div", { children: [
2500
+ /* @__PURE__ */ jsx7("b", { children: k(u?.outputTokens) }),
2501
+ /* @__PURE__ */ jsx7("span", { children: "tokens written" })
2502
+ ] }),
2503
+ /* @__PURE__ */ jsxs7("div", { children: [
2504
+ /* @__PURE__ */ jsx7("b", { children: u?.costUsd != null ? `$${u.costUsd.toFixed(2)}` : u?.billingType === "subscription_included" ? "$0" : "\u2014" }),
2505
+ /* @__PURE__ */ jsx7("span", { children: u?.billingType === "subscription_included" ? "on your subscription" : "cost" })
2506
+ ] })
2507
+ ] }),
2508
+ /* @__PURE__ */ jsx7("h3", { className: "dx-run-h", children: "What it posted" }),
2509
+ posted == null ? /* @__PURE__ */ jsx7("div", { className: "dx-muted dx-small", children: "Loading\u2026" }) : posted.length ? posted.map((c) => /* @__PURE__ */ jsx7("div", { className: "dx-doc", children: /* @__PURE__ */ jsx7(MarkdownBlock, { content: c.body }) }, c.id)) : /* @__PURE__ */ jsxs7("div", { className: "dx-muted dx-small", children: [
2510
+ "Nothing on this task from this run.",
2511
+ run.status !== "succeeded" ? " It did not finish." : ""
2512
+ ] }),
2513
+ /* @__PURE__ */ jsx7("div", { className: "dx-row", style: { gap: 8, marginTop: 16 }, children: /* @__PURE__ */ jsxs7("a", { className: "dx-btn", ...go.hostLink(`/agents/${run.agentId}/runs/${run.id}`), children: [
2514
+ Ico.run,
2515
+ " Open the full log"
2516
+ ] }) })
2517
+ ] });
2518
+ }
2342
2519
  function Viewer({ r }) {
2343
2520
  if (r.kind === "doc") {
2344
2521
  const doc = r.doc;
@@ -2479,24 +2656,29 @@ function askDock(timeoutMs = 250) {
2479
2656
  b.postMessage({ type: "ping" });
2480
2657
  });
2481
2658
  }
2482
- function TalkButton({ d, agentId, label }) {
2659
+ function useTalk(d, agentId) {
2483
2660
  const go = useGo("decide");
2484
- const [busy, setBusy] = useState6(false);
2485
- const start = async () => {
2486
- setBusy(true);
2661
+ return async () => {
2487
2662
  const decision = { issueId: d.issueId, interactionId: d.source === "interaction" ? d.id : null, approvalId: d.source === "approval" ? d.id : null };
2488
2663
  const m = { type: "start", ...agentId ? { entityType: "agent", entityId: agentId } : {}, path: d.identifier ? `/issues/${d.identifier}` : void 0, decision };
2489
2664
  if (await askDock()) {
2490
2665
  const b = new BroadcastChannel(BUS);
2491
2666
  b.postMessage(m);
2492
2667
  b.close();
2493
- } else {
2494
- const q = new URLSearchParams({ start: "1", ...agentId ? { entityType: "agent", entityId: agentId } : {}, ...d.issueId ? { decisionIssue: d.issueId } : {}, ...decision.interactionId ? { decisionInteraction: decision.interactionId } : {}, ...decision.approvalId ? { decisionApproval: decision.approvalId } : {} });
2495
- go.host(`/call?${q}`);
2668
+ return;
2496
2669
  }
2497
- setTimeout(() => setBusy(false), 1500);
2670
+ const q = new URLSearchParams({ start: "1", ...agentId ? { entityType: "agent", entityId: agentId } : {}, ...d.issueId ? { decisionIssue: d.issueId } : {}, ...decision.interactionId ? { decisionInteraction: decision.interactionId } : {}, ...decision.approvalId ? { decisionApproval: decision.approvalId } : {} });
2671
+ go.host(`/call?${q}`);
2498
2672
  };
2499
- return /* @__PURE__ */ jsxs7("button", { className: "dx-btn", disabled: busy, onClick: start, children: [
2673
+ }
2674
+ function TalkButton({ d, agentId, label }) {
2675
+ const start = useTalk(d, agentId);
2676
+ const [busy, setBusy] = useState6(false);
2677
+ return /* @__PURE__ */ jsxs7("button", { className: "dx-btn", disabled: busy, onClick: async () => {
2678
+ setBusy(true);
2679
+ await start();
2680
+ setTimeout(() => setBusy(false), 1500);
2681
+ }, children: [
2500
2682
  Ico.phone,
2501
2683
  " ",
2502
2684
  label
@@ -2527,6 +2709,20 @@ function DecisionsPage({ companyId }) {
2527
2709
  const [filter, setFilter] = useState7("waiting");
2528
2710
  const [answered, setAnswered] = useState7([]);
2529
2711
  const [wide, setWide] = useState7(false);
2712
+ const [pane, setPaneState] = useState7(() => {
2713
+ try {
2714
+ return localStorage.getItem("dx-decisions-pane") !== "closed";
2715
+ } catch {
2716
+ return true;
2717
+ }
2718
+ });
2719
+ const setPane = (v) => {
2720
+ setPaneState(v);
2721
+ try {
2722
+ localStorage.setItem("dx-decisions-pane", v ? "open" : "closed");
2723
+ } catch {
2724
+ }
2725
+ };
2530
2726
  const alarms = useAlarms(companyId);
2531
2727
  const forget = usePluginAction2("refresh-decisions");
2532
2728
  const items = (data?.items ?? []).filter((x) => !answered.includes(x.key)).filter((x) => !q || `${x.title} ${x.identifier} ${x.kindLabel}`.toLowerCase().includes(q.toLowerCase()));
@@ -2537,7 +2733,7 @@ function DecisionsPage({ companyId }) {
2537
2733
  if (error) return /* @__PURE__ */ jsx8(ErrorBox, { error });
2538
2734
  const groups = ["now", "this week", "whenever", "needs work"].map((b) => ({ b, list: items.filter((x) => (x.lane ?? (x.bucket === "today" ? "now" : x.bucket)) === b) }));
2539
2735
  const pick = (k) => go.to(`/${encodeURIComponent(k)}`);
2540
- return /* @__PURE__ */ jsxs8("div", { className: `dx-dq beside${wide ? " wide" : ""}`, children: [
2736
+ return /* @__PURE__ */ jsxs8("div", { className: `dx-dq beside${pane ? wide ? " wide" : "" : " closed"}`, children: [
2541
2737
  /* @__PURE__ */ jsxs8("div", { className: "dx-dq-list", children: [
2542
2738
  /* @__PURE__ */ jsxs8("div", { className: "dx-row", style: { padding: "0 4px 10px" }, children: [
2543
2739
  /* @__PURE__ */ jsx8("b", { style: { fontSize: 15 }, children: "Queue" }),
@@ -2580,7 +2776,10 @@ function DecisionsPage({ companyId }) {
2580
2776
  setAnswered((a) => [...a, sel.key]);
2581
2777
  void forget({}).then(() => refresh());
2582
2778
  } }, sel.key) : /* @__PURE__ */ jsx8("div", { className: "dx-empty", style: { marginTop: 40 }, children: "Nothing is waiting on you. New decisions from the team land here." }) }),
2583
- sel && /* @__PURE__ */ jsx8(BesidePane, { d: sel, wide, setWide }, sel.key)
2779
+ sel && (pane ? /* @__PURE__ */ jsx8(BesidePane, { d: sel, wide, setWide, onClose: () => {
2780
+ setPane(false);
2781
+ setWide(false);
2782
+ } }, sel.key) : /* @__PURE__ */ jsx8(PaneRail, { onOpen: () => setPane(true) }))
2584
2783
  ] });
2585
2784
  }
2586
2785
  function Decision({ d, companyId, pos, total, onPrev, onNext, onDone }) {
@@ -3221,6 +3420,13 @@ function DecisionsLink({ context }) {
3221
3420
  const t = setInterval(refresh, 6e4);
3222
3421
  return () => clearInterval(t);
3223
3422
  }, [refresh]);
3423
+ useEffect5(() => {
3424
+ if (document.getElementById("dx-one-decisions")) return;
3425
+ const st = document.createElement("style");
3426
+ st.id = "dx-one-decisions";
3427
+ st.textContent = 'aside nav a[href$="/decisions"] { display: none !important; }';
3428
+ document.head.appendChild(st);
3429
+ }, []);
3224
3430
  return /* @__PURE__ */ jsx9(SideLink, { base: "decide", label: "Decisions", icon: Icon.list, badge: data?.items.length ?? null });
3225
3431
  }
3226
3432
  function CampaignTab({ context }) {