@cookielab.io/klovi 1.1.0 → 2.0.0

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.
@@ -40924,7 +40924,7 @@ var init_all = __esm(() => {
40924
40924
  var favicon_default = "./favicon-39pjvakn.svg";
40925
40925
 
40926
40926
  // src/frontend/App.tsx
40927
- var import_react18 = __toESM(require_react(), 1);
40927
+ var import_react19 = __toESM(require_react(), 1);
40928
40928
  var import_client = __toESM(require_client(), 1);
40929
40929
 
40930
40930
  // src/frontend/hooks/useFetch.ts
@@ -40937,9 +40937,10 @@ function useFetch(url, deps) {
40937
40937
  const retry = import_react.useCallback(() => setRetryCount((c) => c + 1), []);
40938
40938
  import_react.useEffect(() => {
40939
40939
  let cancelled = false;
40940
+ const controller = new AbortController;
40940
40941
  setLoading(true);
40941
40942
  setError(null);
40942
- fetch(url).then((r) => {
40943
+ fetch(url, { signal: controller.signal }).then((r) => {
40943
40944
  if (!r.ok)
40944
40945
  throw new Error(`HTTP ${r.status}`);
40945
40946
  return r.json();
@@ -40949,13 +40950,14 @@ function useFetch(url, deps) {
40949
40950
  setLoading(false);
40950
40951
  }
40951
40952
  }).catch((e) => {
40952
- if (!cancelled) {
40953
+ if (!cancelled && !controller.signal.aborted) {
40953
40954
  setError(e instanceof Error ? e.message : String(e));
40954
40955
  setLoading(false);
40955
40956
  }
40956
40957
  });
40957
40958
  return () => {
40958
40959
  cancelled = true;
40960
+ controller.abort();
40959
40961
  };
40960
40962
  }, [url, retryCount, ...deps]);
40961
40963
  return { data, loading, error, retry };
@@ -41422,6 +41424,44 @@ function Layout({ sidebar, hideSidebar, onSearchClick, children }) {
41422
41424
  }, undefined, true, undefined, this);
41423
41425
  }
41424
41426
 
41427
+ // src/frontend/hooks/useSessionData.ts
41428
+ function buildSessionUrl(sessionId, project) {
41429
+ return `/api/sessions/${encodeURIComponent(sessionId)}?project=${encodeURIComponent(project)}`;
41430
+ }
41431
+ function buildSubAgentSessionUrl(sessionId, project, agentId) {
41432
+ return `/api/sessions/${encodeURIComponent(sessionId)}/subagents/${encodeURIComponent(agentId)}?project=${encodeURIComponent(project)}`;
41433
+ }
41434
+ function useSessionData(sessionId, project) {
41435
+ return useFetch(buildSessionUrl(sessionId, project), [sessionId, project]);
41436
+ }
41437
+ function useSubAgentSessionData(sessionId, project, agentId) {
41438
+ return useFetch(buildSubAgentSessionUrl(sessionId, project, agentId), [
41439
+ sessionId,
41440
+ project,
41441
+ agentId
41442
+ ]);
41443
+ }
41444
+
41445
+ // src/frontend/components/ui/FetchError.tsx
41446
+ var jsx_dev_runtime5 = __toESM(require_jsx_dev_runtime(), 1);
41447
+ function FetchError({ error, onRetry, showPrefix = false }) {
41448
+ return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("div", {
41449
+ className: "fetch-error",
41450
+ children: [
41451
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("span", {
41452
+ className: "fetch-error-message",
41453
+ children: showPrefix ? `Error: ${error}` : error
41454
+ }, undefined, false, undefined, this),
41455
+ onRetry ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("button", {
41456
+ type: "button",
41457
+ className: "btn btn-sm",
41458
+ onClick: onRetry,
41459
+ children: "Retry"
41460
+ }, undefined, false, undefined, this) : null
41461
+ ]
41462
+ }, undefined, true, undefined, this);
41463
+ }
41464
+
41425
41465
  // src/frontend/utils/time.ts
41426
41466
  function formatTimestamp(iso) {
41427
41467
  const date = new Date(iso);
@@ -41499,7 +41539,7 @@ function formatTime(iso) {
41499
41539
 
41500
41540
  // src/frontend/components/ui/ErrorBoundary.tsx
41501
41541
  var import_react3 = __toESM(require_react(), 1);
41502
- var jsx_dev_runtime5 = __toESM(require_jsx_dev_runtime(), 1);
41542
+ var jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
41503
41543
 
41504
41544
  class ErrorBoundary extends import_react3.Component {
41505
41545
  state = { error: null };
@@ -41514,17 +41554,17 @@ class ErrorBoundary extends import_react3.Component {
41514
41554
  if (!error)
41515
41555
  return this.props.children;
41516
41556
  if (this.props.inline) {
41517
- return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("div", {
41557
+ return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
41518
41558
  className: "error-card",
41519
41559
  children: [
41520
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("div", {
41560
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
41521
41561
  className: "error-card-header",
41522
41562
  children: [
41523
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("span", {
41563
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
41524
41564
  className: "error-card-title",
41525
41565
  children: "Failed to render"
41526
41566
  }, undefined, false, undefined, this),
41527
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("button", {
41567
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("button", {
41528
41568
  type: "button",
41529
41569
  className: "btn btn-sm",
41530
41570
  onClick: this.retry,
@@ -41532,13 +41572,13 @@ class ErrorBoundary extends import_react3.Component {
41532
41572
  }, undefined, false, undefined, this)
41533
41573
  ]
41534
41574
  }, undefined, true, undefined, this),
41535
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("details", {
41575
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("details", {
41536
41576
  className: "error-card-details",
41537
41577
  children: [
41538
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("summary", {
41578
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("summary", {
41539
41579
  children: "Error details"
41540
41580
  }, undefined, false, undefined, this),
41541
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("pre", {
41581
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("pre", {
41542
41582
  children: error.stack || error.message
41543
41583
  }, undefined, false, undefined, this)
41544
41584
  ]
@@ -41546,18 +41586,18 @@ class ErrorBoundary extends import_react3.Component {
41546
41586
  ]
41547
41587
  }, undefined, true, undefined, this);
41548
41588
  }
41549
- return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("div", {
41589
+ return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
41550
41590
  className: "error-view",
41551
41591
  children: [
41552
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("div", {
41592
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
41553
41593
  className: "error-view-title",
41554
41594
  children: "Something went wrong"
41555
41595
  }, undefined, false, undefined, this),
41556
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("div", {
41596
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
41557
41597
  className: "error-view-message",
41558
41598
  children: error.message
41559
41599
  }, undefined, false, undefined, this),
41560
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV("button", {
41600
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("button", {
41561
41601
  type: "button",
41562
41602
  className: "btn btn-primary",
41563
41603
  onClick: this.retry,
@@ -41899,12 +41939,12 @@ function productionCreate(_, jsx, jsxs) {
41899
41939
  return key ? fn(type, props, key) : fn(type, props);
41900
41940
  }
41901
41941
  }
41902
- function developmentCreate(filePath, jsxDEV6) {
41942
+ function developmentCreate(filePath, jsxDEV7) {
41903
41943
  return create2;
41904
41944
  function create2(node, type, props, key) {
41905
41945
  const isStaticChildren = Array.isArray(props.children);
41906
41946
  const point3 = pointStart(node);
41907
- return jsxDEV6(type, props, key, isStaticChildren, {
41947
+ return jsxDEV7(type, props, key, isStaticChildren, {
41908
41948
  columnNumber: point3 ? point3.column - 1 : undefined,
41909
41949
  fileName: filePath,
41910
41950
  lineNumber: point3 ? point3.line : undefined
@@ -53821,23 +53861,23 @@ function useFontSize() {
53821
53861
  }
53822
53862
 
53823
53863
  // src/frontend/components/ui/CodeBlock.tsx
53824
- var jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
53864
+ var jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
53825
53865
  function CodeBlock({ language, children }) {
53826
53866
  const { resolved: theme } = useTheme();
53827
53867
  const lang = language || "text";
53828
53868
  const style2 = theme === "dark" ? one_dark_default : one_light_default;
53829
- return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
53869
+ return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
53830
53870
  className: "code-block-wrapper",
53831
53871
  children: [
53832
- language && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
53872
+ language && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
53833
53873
  className: "code-block-header",
53834
- children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
53874
+ children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
53835
53875
  children: language
53836
53876
  }, undefined, false, undefined, this)
53837
53877
  }, undefined, false, undefined, this),
53838
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
53878
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
53839
53879
  className: "code-block-content",
53840
- children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(prism_default2, {
53880
+ children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(prism_default2, {
53841
53881
  language: lang,
53842
53882
  style: style2,
53843
53883
  customStyle: {
@@ -53855,7 +53895,7 @@ function CodeBlock({ language, children }) {
53855
53895
  }
53856
53896
 
53857
53897
  // src/frontend/components/ui/MarkdownRenderer.tsx
53858
- var jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
53898
+ var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
53859
53899
  var FILE_REF_RE = /@([\w./-]+\.\w+)/g;
53860
53900
  function renderTextWithFileRefs(text7) {
53861
53901
  const parts = [];
@@ -53866,7 +53906,7 @@ function renderTextWithFileRefs(text7) {
53866
53906
  if (match.index > last) {
53867
53907
  parts.push(text7.slice(last, match.index));
53868
53908
  }
53869
- parts.push(/* @__PURE__ */ jsx_dev_runtime7.jsxDEV("code", {
53909
+ parts.push(/* @__PURE__ */ jsx_dev_runtime8.jsxDEV("code", {
53870
53910
  className: "file-ref",
53871
53911
  children: [
53872
53912
  "@",
@@ -53880,18 +53920,18 @@ function renderTextWithFileRefs(text7) {
53880
53920
  return text7;
53881
53921
  if (last < text7.length)
53882
53922
  parts.push(text7.slice(last));
53883
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(jsx_dev_runtime7.Fragment, {
53923
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(jsx_dev_runtime8.Fragment, {
53884
53924
  children: parts
53885
53925
  }, undefined, false, undefined, this);
53886
53926
  }
53887
53927
  function MarkdownRenderer({ content: content3 }) {
53888
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
53928
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("div", {
53889
53929
  className: "markdown-content",
53890
- children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Markdown, {
53930
+ children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Markdown, {
53891
53931
  remarkPlugins: [remarkGfm],
53892
53932
  components: {
53893
53933
  p({ children }) {
53894
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("p", {
53934
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("p", {
53895
53935
  children: import_react8.default.Children.map(children, (child) => typeof child === "string" ? renderTextWithFileRefs(child) : child)
53896
53936
  }, undefined, false, undefined, this);
53897
53937
  },
@@ -53900,19 +53940,19 @@ function MarkdownRenderer({ content: content3 }) {
53900
53940
  const text7 = String(children).replace(/\n$/, "");
53901
53941
  if (!match && !text7.includes(`
53902
53942
  `)) {
53903
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("code", {
53943
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("code", {
53904
53944
  className,
53905
53945
  ...props,
53906
53946
  children
53907
53947
  }, undefined, false, undefined, this);
53908
53948
  }
53909
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(CodeBlock, {
53949
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(CodeBlock, {
53910
53950
  language: match?.[1],
53911
53951
  children: text7
53912
53952
  }, undefined, false, undefined, this);
53913
53953
  },
53914
53954
  a({ href, children, ...props }) {
53915
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("a", {
53955
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("a", {
53916
53956
  href,
53917
53957
  target: "_blank",
53918
53958
  rel: "noopener noreferrer",
@@ -53948,9 +53988,6 @@ function groupContentBlocks(blocks) {
53948
53988
  }
53949
53989
 
53950
53990
  // src/frontend/utils/model.ts
53951
- function isClaudeModel(model) {
53952
- return model.startsWith("claude-");
53953
- }
53954
53991
  function shortModel(model) {
53955
53992
  const claudeMatch = model.match(/claude-(opus|sonnet|haiku)-(\d+)(?:-(\d{1,2}))?(?:-\d{8,})?$/);
53956
53993
  if (claudeMatch) {
@@ -53980,29 +54017,29 @@ function shortModel(model) {
53980
54017
 
53981
54018
  // src/frontend/components/ui/CollapsibleSection.tsx
53982
54019
  var import_react9 = __toESM(require_react(), 1);
53983
- var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
54020
+ var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
53984
54021
  function CollapsibleSection({
53985
54022
  title,
53986
54023
  defaultOpen = false,
53987
54024
  children
53988
54025
  }) {
53989
54026
  const [open, setOpen] = import_react9.useState(defaultOpen);
53990
- return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("div", {
54027
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV("div", {
53991
54028
  className: "collapsible",
53992
54029
  children: [
53993
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("button", {
54030
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV("button", {
53994
54031
  type: "button",
53995
54032
  className: "collapsible-header",
53996
54033
  onClick: () => setOpen(!open),
53997
54034
  children: [
53998
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("span", {
54035
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV("span", {
53999
54036
  className: `collapsible-chevron ${open ? "open" : ""}`,
54000
54037
  children: "▶"
54001
54038
  }, undefined, false, undefined, this),
54002
54039
  title
54003
54040
  ]
54004
54041
  }, undefined, true, undefined, this),
54005
- open && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("div", {
54042
+ open && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV("div", {
54006
54043
  className: "collapsible-content",
54007
54044
  children
54008
54045
  }, undefined, false, undefined, this)
@@ -54010,8 +54047,61 @@ function CollapsibleSection({
54010
54047
  }, undefined, true, undefined, this);
54011
54048
  }
54012
54049
 
54050
+ // src/server/plugins/shared/text-utils.ts
54051
+ function truncate(text7, maxLength) {
54052
+ return text7.length <= maxLength ? text7 : `${text7.slice(0, maxLength)}...`;
54053
+ }
54054
+
54055
+ // src/server/plugins/codex-cli/extractors.ts
54056
+ var codexSummaryExtractors = {
54057
+ command_execution: (i) => truncate(String(i.command || ""), 80),
54058
+ file_change: (i) => {
54059
+ const changes = i.changes;
54060
+ if (Array.isArray(changes) && changes.length > 0) {
54061
+ return String(changes[0].path || "");
54062
+ }
54063
+ return "";
54064
+ },
54065
+ web_search: (i) => truncate(String(i.query || ""), 60)
54066
+ };
54067
+ var codexInputFormatters = {
54068
+ command_execution: (i) => String(i.command || ""),
54069
+ file_change: (i) => {
54070
+ const changes = i.changes;
54071
+ if (Array.isArray(changes)) {
54072
+ return changes.map((c2) => `${c2.kind || "change"}: ${c2.path || ""}`).join(`
54073
+ `);
54074
+ }
54075
+ return JSON.stringify(i, null, 2);
54076
+ },
54077
+ web_search: (i) => `Query: ${String(i.query || "")}`
54078
+ };
54079
+
54080
+ // src/server/plugins/opencode/extractors.ts
54081
+ var openCodeSummaryExtractors = {};
54082
+ var openCodeInputFormatters = {};
54083
+
54084
+ // src/frontend/plugin-registry.ts
54085
+ var pluginRegistry = new Map;
54086
+ function registerFrontendPlugin(id, plugin) {
54087
+ pluginRegistry.set(id, plugin);
54088
+ }
54089
+ function getFrontendPlugin(id) {
54090
+ return pluginRegistry.get(id);
54091
+ }
54092
+ registerFrontendPlugin("codex-cli", {
54093
+ displayName: "Codex",
54094
+ summaryExtractors: codexSummaryExtractors,
54095
+ inputFormatters: codexInputFormatters
54096
+ });
54097
+ registerFrontendPlugin("opencode", {
54098
+ displayName: "OpenCode",
54099
+ summaryExtractors: openCodeSummaryExtractors,
54100
+ inputFormatters: openCodeInputFormatters
54101
+ });
54102
+
54013
54103
  // src/frontend/components/ui/DiffView.tsx
54014
- var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
54104
+ var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
54015
54105
  function formatDiff(oldString, newString) {
54016
54106
  const lines = [];
54017
54107
  if (oldString !== "") {
@@ -54033,18 +54123,18 @@ function DiffView({ filePath, oldString, newString }) {
54033
54123
  const { resolved: theme } = useTheme();
54034
54124
  const diff2 = formatDiff(oldString, newString);
54035
54125
  const style2 = theme === "dark" ? one_dark_default : one_light_default;
54036
- return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV("div", {
54126
+ return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
54037
54127
  className: "diff-view-wrapper",
54038
54128
  children: [
54039
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV("div", {
54129
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
54040
54130
  className: "diff-view-header",
54041
- children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV("span", {
54131
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("span", {
54042
54132
  children: filePath
54043
54133
  }, undefined, false, undefined, this)
54044
54134
  }, undefined, false, undefined, this),
54045
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV("div", {
54135
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
54046
54136
  className: "diff-view-content",
54047
- children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(prism_default2, {
54137
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(prism_default2, {
54048
54138
  language: "diff",
54049
54139
  style: style2,
54050
54140
  customStyle: {
@@ -54160,7 +54250,7 @@ function hasCodeStructure(text7) {
54160
54250
 
54161
54251
  // src/frontend/components/ui/ImageLightbox.tsx
54162
54252
  var import_react10 = __toESM(require_react(), 1);
54163
- var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
54253
+ var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
54164
54254
  function ImageLightbox({ src, onClose }) {
54165
54255
  const [visible, setVisible] = import_react10.useState(false);
54166
54256
  const handleClose = import_react10.useCallback(() => {
@@ -54180,10 +54270,10 @@ function ImageLightbox({ src, onClose }) {
54180
54270
  document.addEventListener("keydown", handleKey);
54181
54271
  return () => document.removeEventListener("keydown", handleKey);
54182
54272
  }, [handleClose]);
54183
- return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
54273
+ return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("div", {
54184
54274
  className: `lightbox-overlay ${visible ? "lightbox-visible" : ""}`,
54185
54275
  onClick: handleClose,
54186
- children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("img", {
54276
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("img", {
54187
54277
  className: "lightbox-image",
54188
54278
  src,
54189
54279
  alt: "Full size preview"
@@ -54192,7 +54282,7 @@ function ImageLightbox({ src, onClose }) {
54192
54282
  }
54193
54283
 
54194
54284
  // src/frontend/components/message/SmartToolOutput.tsx
54195
- var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
54285
+ var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
54196
54286
  function SmartToolOutput({ output, isError, resultImages }) {
54197
54287
  const truncated = truncateOutput(output);
54198
54288
  const wasTruncated = output.length > MAX_OUTPUT_LENGTH;
@@ -54201,33 +54291,33 @@ function SmartToolOutput({ output, isError, resultImages }) {
54201
54291
  const closeLightbox = import_react11.useCallback(() => setLightboxSrc(null), []);
54202
54292
  if (!output && (!resultImages || resultImages.length === 0))
54203
54293
  return null;
54204
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("div", {
54294
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54205
54295
  children: [
54206
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("div", {
54296
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54207
54297
  className: "tool-section-label",
54208
54298
  children: "Output"
54209
54299
  }, undefined, false, undefined, this),
54210
- output && (detectedLang && !isError ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(CodeBlock, {
54300
+ output && (detectedLang && !isError ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(CodeBlock, {
54211
54301
  language: detectedLang,
54212
54302
  children: truncated
54213
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("div", {
54303
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54214
54304
  className: `tool-call-output ${isError ? "tool-call-error" : ""}`,
54215
54305
  children: truncated
54216
54306
  }, undefined, false, undefined, this)),
54217
- wasTruncated && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("div", {
54307
+ wasTruncated && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54218
54308
  className: "tool-call-truncated",
54219
54309
  children: "... (truncated)"
54220
54310
  }, undefined, false, undefined, this),
54221
- resultImages && resultImages.length > 0 && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("div", {
54311
+ resultImages && resultImages.length > 0 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54222
54312
  className: "tool-result-images",
54223
- children: resultImages.map((img, i) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("img", {
54313
+ children: resultImages.map((img, i) => /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("img", {
54224
54314
  className: "tool-result-image",
54225
54315
  src: `data:${img.mediaType};base64,${img.data}`,
54226
54316
  alt: `Tool result ${i + 1}`,
54227
54317
  onClick: () => setLightboxSrc(`data:${img.mediaType};base64,${img.data}`)
54228
54318
  }, i, false, undefined, this))
54229
54319
  }, undefined, false, undefined, this),
54230
- lightboxSrc && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(ImageLightbox, {
54320
+ lightboxSrc && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ImageLightbox, {
54231
54321
  src: lightboxSrc,
54232
54322
  onClose: closeLightbox
54233
54323
  }, undefined, false, undefined, this)
@@ -54236,25 +54326,25 @@ function SmartToolOutput({ output, isError, resultImages }) {
54236
54326
  }
54237
54327
 
54238
54328
  // src/frontend/components/message/BashToolContent.tsx
54239
- var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
54329
+ var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
54240
54330
  function BashToolContent({ call }) {
54241
54331
  const command = String(call.input.command || "");
54242
- return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(jsx_dev_runtime12.Fragment, {
54332
+ return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
54243
54333
  children: [
54244
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54334
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54245
54335
  style: { marginBottom: 8 },
54246
54336
  children: [
54247
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54337
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54248
54338
  className: "tool-section-label",
54249
54339
  children: "Command"
54250
54340
  }, undefined, false, undefined, this),
54251
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(CodeBlock, {
54341
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(CodeBlock, {
54252
54342
  language: "bash",
54253
54343
  children: command
54254
54344
  }, undefined, false, undefined, this)
54255
54345
  ]
54256
54346
  }, undefined, true, undefined, this),
54257
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(SmartToolOutput, {
54347
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(SmartToolOutput, {
54258
54348
  output: call.result,
54259
54349
  isError: call.isError,
54260
54350
  resultImages: call.resultImages
@@ -54264,38 +54354,46 @@ function BashToolContent({ call }) {
54264
54354
  }
54265
54355
 
54266
54356
  // src/frontend/components/message/ToolCall.tsx
54267
- var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
54357
+ var jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
54268
54358
  var MAX_OUTPUT_LENGTH = 5000;
54269
54359
  var MAX_CONTENT_LENGTH = 2000;
54270
54360
  var MAX_THINKING_PREVIEW = 100;
54271
54361
  function isEditWithDiff(call) {
54272
54362
  return call.name === "Edit" && typeof call.input.old_string === "string" && typeof call.input.new_string === "string";
54273
54363
  }
54274
- function isJsonFallbackInput(call) {
54275
- return !(call.name in INPUT_FORMATTERS);
54364
+ function hasInputFormatter(call, pluginId) {
54365
+ if (pluginId) {
54366
+ const plugin = getFrontendPlugin(pluginId);
54367
+ if (plugin?.inputFormatters[call.name])
54368
+ return true;
54369
+ }
54370
+ return call.name in INPUT_FORMATTERS;
54371
+ }
54372
+ function isJsonFallbackInput(call, pluginId) {
54373
+ return !hasInputFormatter(call, pluginId);
54276
54374
  }
54277
- function DefaultToolContent({ call }) {
54278
- const formattedInput = formatToolInput(call);
54279
- const jsonInput = isJsonFallbackInput(call);
54280
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
54375
+ function DefaultToolContent({ call, pluginId }) {
54376
+ const formattedInput = formatToolInput(call, pluginId);
54377
+ const jsonInput = isJsonFallbackInput(call, pluginId);
54378
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
54281
54379
  children: [
54282
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54380
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54283
54381
  style: { marginBottom: 8 },
54284
54382
  children: [
54285
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54383
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54286
54384
  className: "tool-section-label",
54287
54385
  children: "Input"
54288
54386
  }, undefined, false, undefined, this),
54289
- jsonInput ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(CodeBlock, {
54387
+ jsonInput ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(CodeBlock, {
54290
54388
  language: "json",
54291
54389
  children: formattedInput
54292
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54390
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54293
54391
  className: "tool-call-input",
54294
54392
  children: formattedInput
54295
54393
  }, undefined, false, undefined, this)
54296
54394
  ]
54297
54395
  }, undefined, true, undefined, this),
54298
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(SmartToolOutput, {
54396
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(SmartToolOutput, {
54299
54397
  output: call.result,
54300
54398
  isError: call.isError,
54301
54399
  resultImages: call.resultImages
@@ -54303,41 +54401,41 @@ function DefaultToolContent({ call }) {
54303
54401
  ]
54304
54402
  }, undefined, true, undefined, this);
54305
54403
  }
54306
- function ToolCall({ call, sessionId, project }) {
54307
- const summary = getToolSummary(call);
54404
+ function ToolCall({ call, sessionId, project, pluginId }) {
54405
+ const summary = getToolSummary(call, pluginId);
54308
54406
  const mcpServer = getMcpServer(call.name);
54309
54407
  const skillName = getSkillName(call);
54310
54408
  const hasSubAgent = call.name === "Task" && call.subAgentId && sessionId && project;
54311
54409
  const displayName = hasSubAgent ? "Sub-Agent" : mcpServer ? call.name.split("__").slice(1).join("__").replace(/__/g, " > ") : skillName ?? call.name;
54312
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54410
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54313
54411
  className: "tool-call",
54314
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(CollapsibleSection, {
54315
- title: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("span", {
54412
+ children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(CollapsibleSection, {
54413
+ title: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54316
54414
  children: [
54317
- mcpServer && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("span", {
54415
+ mcpServer && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54318
54416
  className: "tool-mcp-server",
54319
54417
  children: mcpServer
54320
54418
  }, undefined, false, undefined, this),
54321
- skillName && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("span", {
54419
+ skillName && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54322
54420
  className: "tool-skill-badge",
54323
54421
  children: "skill"
54324
54422
  }, undefined, false, undefined, this),
54325
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("span", {
54423
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54326
54424
  className: "tool-call-name",
54327
54425
  children: displayName
54328
54426
  }, undefined, false, undefined, this),
54329
- summary && !skillName && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("span", {
54427
+ summary && !skillName && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54330
54428
  className: "tool-call-summary",
54331
54429
  children: [
54332
54430
  " — ",
54333
54431
  summary
54334
54432
  ]
54335
54433
  }, undefined, true, undefined, this),
54336
- call.isError && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("span", {
54434
+ call.isError && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54337
54435
  className: "tool-call-error",
54338
54436
  children: " (error)"
54339
54437
  }, undefined, false, undefined, this),
54340
- hasSubAgent && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("a", {
54438
+ hasSubAgent && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("a", {
54341
54439
  className: "subagent-link",
54342
54440
  href: `#/${project}/${sessionId}/subagent/${call.subAgentId}`,
54343
54441
  onClick: (e) => e.stopPropagation(),
@@ -54345,14 +54443,15 @@ function ToolCall({ call, sessionId, project }) {
54345
54443
  }, undefined, false, undefined, this)
54346
54444
  ]
54347
54445
  }, undefined, true, undefined, this),
54348
- children: isEditWithDiff(call) ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(DiffView, {
54446
+ children: isEditWithDiff(call) ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(DiffView, {
54349
54447
  filePath: String(call.input.file_path || ""),
54350
54448
  oldString: String(call.input.old_string),
54351
54449
  newString: String(call.input.new_string)
54352
- }, undefined, false, undefined, this) : call.name === "Bash" ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(BashToolContent, {
54353
- call
54354
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(DefaultToolContent, {
54450
+ }, undefined, false, undefined, this) : call.name === "Bash" ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(BashToolContent, {
54355
54451
  call
54452
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(DefaultToolContent, {
54453
+ call,
54454
+ pluginId
54356
54455
  }, undefined, false, undefined, this)
54357
54456
  }, undefined, false, undefined, this)
54358
54457
  }, undefined, false, undefined, this);
@@ -54371,7 +54470,7 @@ function getSkillName(call) {
54371
54470
  function getAskUserQuestionSummary(input) {
54372
54471
  if (Array.isArray(input.questions) && input.questions.length > 0) {
54373
54472
  const q2 = input.questions[0];
54374
- return truncate(String(q2.question || ""), 60);
54473
+ return truncate2(String(q2.question || ""), 60);
54375
54474
  }
54376
54475
  return "";
54377
54476
  }
@@ -54380,16 +54479,16 @@ var fileSummaryExtractors = {
54380
54479
  Write: (i) => String(i.file_path || ""),
54381
54480
  Edit: (i) => String(i.file_path || ""),
54382
54481
  Glob: (i) => String(i.pattern || ""),
54383
- Grep: (i) => truncate(String(i.pattern || ""), 60),
54482
+ Grep: (i) => truncate2(String(i.pattern || ""), 60),
54384
54483
  NotebookEdit: (i) => String(i.notebook_path || ""),
54385
54484
  NotebookRead: (i) => String(i.notebook_path || "")
54386
54485
  };
54387
54486
  var shellSummaryExtractors = {
54388
- Bash: (i) => truncate(String(i.command || ""), 80)
54487
+ Bash: (i) => truncate2(String(i.command || ""), 80)
54389
54488
  };
54390
54489
  var agentSummaryExtractors = {
54391
- Task: (i) => truncate(String(i.description || ""), 60),
54392
- TaskCreate: (i) => truncate(String(i.subject || ""), 60),
54490
+ Task: (i) => truncate2(String(i.description || ""), 60),
54491
+ TaskCreate: (i) => truncate2(String(i.subject || ""), 60),
54393
54492
  TaskUpdate: (i) => `#${i.taskId || "?"}${i.status ? ` → ${i.status}` : ""}`,
54394
54493
  TaskList: () => "List all tasks",
54395
54494
  TaskGet: (i) => `#${i.taskId || "?"}`,
@@ -54398,11 +54497,11 @@ var agentSummaryExtractors = {
54398
54497
  KillShell: (i) => String(i.task_id || i.shell_id || ""),
54399
54498
  EnterPlanMode: () => "Enter plan mode",
54400
54499
  ExitPlanMode: () => "Exit plan mode",
54401
- TodoWrite: (i) => truncate(String(i.subject || ""), 60)
54500
+ TodoWrite: (i) => truncate2(String(i.subject || ""), 60)
54402
54501
  };
54403
54502
  var webSummaryExtractors = {
54404
- WebFetch: (i) => truncate(String(i.url || ""), 60),
54405
- WebSearch: (i) => truncate(String(i.query || ""), 60)
54503
+ WebFetch: (i) => truncate2(String(i.url || ""), 60),
54504
+ WebSearch: (i) => truncate2(String(i.query || ""), 60)
54406
54505
  };
54407
54506
  var interactionSummaryExtractors = {
54408
54507
  AskUserQuestion: (i) => getAskUserQuestionSummary(i),
@@ -54415,7 +54514,13 @@ var SUMMARY_EXTRACTORS = {
54415
54514
  ...webSummaryExtractors,
54416
54515
  ...interactionSummaryExtractors
54417
54516
  };
54418
- function getToolSummary(call) {
54517
+ function getToolSummary(call, pluginId) {
54518
+ if (pluginId) {
54519
+ const plugin = getFrontendPlugin(pluginId);
54520
+ const pluginExtractor = plugin?.summaryExtractors[call.name];
54521
+ if (pluginExtractor)
54522
+ return pluginExtractor(call.input);
54523
+ }
54419
54524
  if (call.name.startsWith("mcp__")) {
54420
54525
  return call.name.split("__").slice(2).join(" > ") || "";
54421
54526
  }
@@ -54451,7 +54556,7 @@ function formatWriteInput(input) {
54451
54556
  parts.push(`File: ${input.file_path}`);
54452
54557
  if (input.content)
54453
54558
  parts.push(`Content:
54454
- ${truncate(String(input.content), MAX_CONTENT_LENGTH)}`);
54559
+ ${truncate2(String(input.content), MAX_CONTENT_LENGTH)}`);
54455
54560
  return parts.join(`
54456
54561
 
54457
54562
  `);
@@ -54490,7 +54595,7 @@ function formatNotebookEditInput(input) {
54490
54595
  parts.push(`Mode: ${input.edit_mode}`);
54491
54596
  if (input.new_source)
54492
54597
  parts.push(`Source:
54493
- ${truncate(String(input.new_source), MAX_CONTENT_LENGTH)}`);
54598
+ ${truncate2(String(input.new_source), MAX_CONTENT_LENGTH)}`);
54494
54599
  return parts.join(`
54495
54600
  `) || JSON.stringify(input, null, 2);
54496
54601
  }
@@ -54551,11 +54656,17 @@ var INPUT_FORMATTERS = {
54551
54656
  ...agentInputFormatters,
54552
54657
  ...interactionInputFormatters
54553
54658
  };
54554
- function formatToolInput(call) {
54659
+ function formatToolInput(call, pluginId) {
54660
+ if (pluginId) {
54661
+ const plugin = getFrontendPlugin(pluginId);
54662
+ const pluginFormatter = plugin?.inputFormatters[call.name];
54663
+ if (pluginFormatter)
54664
+ return pluginFormatter(call.input);
54665
+ }
54555
54666
  const formatter = INPUT_FORMATTERS[call.name];
54556
54667
  return formatter ? formatter(call.input) : JSON.stringify(call.input, null, 2);
54557
54668
  }
54558
- function truncate(s2, max) {
54669
+ function truncate2(s2, max) {
54559
54670
  if (s2.length <= max)
54560
54671
  return s2;
54561
54672
  return `${s2.slice(0, max)}...`;
@@ -54567,28 +54678,28 @@ function truncateOutput(s2) {
54567
54678
  }
54568
54679
 
54569
54680
  // src/frontend/components/message/ThinkingBlock.tsx
54570
- var jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
54681
+ var jsx_dev_runtime15 = __toESM(require_jsx_dev_runtime(), 1);
54571
54682
  function ThinkingBlock({ block }) {
54572
54683
  const preview = block.text.length > MAX_THINKING_PREVIEW ? `${block.text.slice(0, MAX_THINKING_PREVIEW)}...` : block.text;
54573
- return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54684
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54574
54685
  className: "thinking-block",
54575
- children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(CollapsibleSection, {
54576
- title: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54686
+ children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(CollapsibleSection, {
54687
+ title: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54577
54688
  children: [
54578
- /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54689
+ /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54579
54690
  style: { color: "var(--text-muted)" },
54580
54691
  children: "Thinking:"
54581
54692
  }, undefined, false, undefined, this),
54582
54693
  " ",
54583
- /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54694
+ /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54584
54695
  className: "tool-call-summary",
54585
54696
  children: preview
54586
54697
  }, undefined, false, undefined, this)
54587
54698
  ]
54588
54699
  }, undefined, true, undefined, this),
54589
- children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54700
+ children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54590
54701
  className: "thinking-content",
54591
- children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(MarkdownRenderer, {
54702
+ children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(MarkdownRenderer, {
54592
54703
  content: block.text
54593
54704
  }, undefined, false, undefined, this)
54594
54705
  }, undefined, false, undefined, this)
@@ -54597,42 +54708,43 @@ function ThinkingBlock({ block }) {
54597
54708
  }
54598
54709
 
54599
54710
  // src/frontend/components/message/AssistantMessage.tsx
54600
- var jsx_dev_runtime15 = __toESM(require_jsx_dev_runtime(), 1);
54601
- function renderGroup(group, sessionId, project) {
54711
+ var jsx_dev_runtime16 = __toESM(require_jsx_dev_runtime(), 1);
54712
+ function renderGroup(group, sessionId, project, pluginId) {
54602
54713
  return group.map((block, i) => {
54603
54714
  if (block.type === "thinking") {
54604
- return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(ThinkingBlock, {
54715
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(ThinkingBlock, {
54605
54716
  block: block.block
54606
54717
  }, `thinking-${i}`, false, undefined, this);
54607
54718
  }
54608
54719
  if (block.type === "text") {
54609
- return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(MarkdownRenderer, {
54720
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MarkdownRenderer, {
54610
54721
  content: block.text
54611
54722
  }, `text-${i}`, false, undefined, this);
54612
54723
  }
54613
- return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(ToolCall, {
54724
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(ToolCall, {
54614
54725
  call: block.call,
54615
54726
  sessionId,
54616
- project
54727
+ project,
54728
+ pluginId
54617
54729
  }, `tool-${i}`, false, undefined, this);
54618
54730
  });
54619
54731
  }
54620
54732
  function UsageFooter({ usage }) {
54621
- return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54733
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54622
54734
  className: "token-usage",
54623
54735
  children: [
54624
54736
  usage.inputTokens.toLocaleString(),
54625
54737
  " in / ",
54626
54738
  usage.outputTokens.toLocaleString(),
54627
54739
  " out",
54628
- usage.cacheReadTokens && usage.cacheReadTokens > 0 && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54740
+ usage.cacheReadTokens && usage.cacheReadTokens > 0 && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54629
54741
  children: [
54630
54742
  " · ",
54631
54743
  usage.cacheReadTokens.toLocaleString(),
54632
54744
  " cache read"
54633
54745
  ]
54634
54746
  }, undefined, true, undefined, this),
54635
- usage.cacheCreationTokens && usage.cacheCreationTokens > 0 && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54747
+ usage.cacheCreationTokens && usage.cacheCreationTokens > 0 && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54636
54748
  children: [
54637
54749
  " · ",
54638
54750
  usage.cacheCreationTokens.toLocaleString(),
@@ -54646,7 +54758,8 @@ function AssistantMessage({
54646
54758
  turn,
54647
54759
  visibleSubSteps,
54648
54760
  sessionId,
54649
- project
54761
+ project,
54762
+ pluginId
54650
54763
  }) {
54651
54764
  const groups = groupContentBlocks(turn.contentBlocks);
54652
54765
  const limit = visibleSubSteps !== undefined ? visibleSubSteps : groups.length;
@@ -54657,21 +54770,21 @@ function AssistantMessage({
54657
54770
  const introGroup = hasNonText && firstIsText ? visibleGroups[0] : null;
54658
54771
  const treeGroups = hasNonText ? introGroup ? visibleGroups.slice(1) : visibleGroups : [];
54659
54772
  const flatGroups = hasNonText ? [] : visibleGroups;
54660
- return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54773
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54661
54774
  className: "turn",
54662
54775
  children: [
54663
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54776
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54664
54777
  className: "turn-header",
54665
54778
  children: [
54666
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54779
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54667
54780
  className: "turn-badge turn-badge-assistant",
54668
54781
  children: "Assistant"
54669
54782
  }, undefined, false, undefined, this),
54670
- turn.model && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54783
+ turn.model && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54671
54784
  className: "turn-badge turn-badge-model",
54672
54785
  children: shortModel(turn.model)
54673
54786
  }, undefined, false, undefined, this),
54674
- turn.timestamp && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("time", {
54787
+ turn.timestamp && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("time", {
54675
54788
  className: "turn-timestamp",
54676
54789
  dateTime: turn.timestamp,
54677
54790
  "data-tooltip": formatFullDateTime(turn.timestamp),
@@ -54679,25 +54792,25 @@ function AssistantMessage({
54679
54792
  }, undefined, false, undefined, this)
54680
54793
  ]
54681
54794
  }, undefined, true, undefined, this),
54682
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54795
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54683
54796
  className: "message message-assistant",
54684
54797
  children: [
54685
- introGroup && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54798
+ introGroup && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54686
54799
  className: isPresentation && treeGroups.length === 0 ? "step-enter" : "",
54687
- children: renderGroup(introGroup, sessionId, project)
54800
+ children: renderGroup(introGroup, sessionId, project, pluginId)
54688
54801
  }, undefined, false, undefined, this),
54689
- treeGroups.length > 0 && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54802
+ treeGroups.length > 0 && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54690
54803
  className: "exec-tree",
54691
- children: treeGroups.map((group, i) => /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54804
+ children: treeGroups.map((group, i) => /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54692
54805
  className: `tree-node${isPresentation && i === treeGroups.length - 1 ? " step-enter" : ""}`,
54693
- children: renderGroup(group, sessionId, project)
54806
+ children: renderGroup(group, sessionId, project, pluginId)
54694
54807
  }, i, false, undefined, this))
54695
54808
  }, undefined, false, undefined, this),
54696
- flatGroups.map((group, i) => /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54809
+ flatGroups.map((group, i) => /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54697
54810
  className: isPresentation && i === flatGroups.length - 1 ? "step-enter" : "",
54698
- children: renderGroup(group, sessionId, project)
54811
+ children: renderGroup(group, sessionId, project, pluginId)
54699
54812
  }, i, false, undefined, this)),
54700
- turn.usage && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(UsageFooter, {
54813
+ turn.usage && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(UsageFooter, {
54701
54814
  usage: turn.usage
54702
54815
  }, undefined, false, undefined, this)
54703
54816
  ]
@@ -54707,7 +54820,7 @@ function AssistantMessage({
54707
54820
  }
54708
54821
 
54709
54822
  // src/frontend/components/message/UserMessage.tsx
54710
- var jsx_dev_runtime16 = __toESM(require_jsx_dev_runtime(), 1);
54823
+ var jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
54711
54824
  var STATUS_RE = /^\[.+\]$/;
54712
54825
  var PLAN_PREFIX = "Implement the following plan";
54713
54826
  function UserMessage({
@@ -54718,14 +54831,14 @@ function UserMessage({
54718
54831
  project
54719
54832
  }) {
54720
54833
  if (turn.bashInput !== undefined) {
54721
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54834
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54722
54835
  className: "status-notice bash-input-notice",
54723
54836
  children: [
54724
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54837
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54725
54838
  className: "bash-input-prompt",
54726
54839
  children: "$"
54727
54840
  }, undefined, false, undefined, this),
54728
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("code", {
54841
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("code", {
54729
54842
  className: "bash-input-command",
54730
54843
  children: turn.bashInput
54731
54844
  }, undefined, false, undefined, this)
@@ -54733,11 +54846,11 @@ function UserMessage({
54733
54846
  }, undefined, true, undefined, this);
54734
54847
  }
54735
54848
  if (turn.ideOpenedFile !== undefined) {
54736
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54849
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54737
54850
  className: "status-notice ide-opened-file-notice",
54738
54851
  children: [
54739
54852
  "Opened ",
54740
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("code", {
54853
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("code", {
54741
54854
  className: "ide-opened-file-path",
54742
54855
  children: turn.ideOpenedFile
54743
54856
  }, undefined, false, undefined, this)
@@ -54746,7 +54859,7 @@ function UserMessage({
54746
54859
  }
54747
54860
  const isStatus = STATUS_RE.test(turn.text.trim());
54748
54861
  if (isStatus) {
54749
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54862
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54750
54863
  className: "status-notice",
54751
54864
  children: turn.text
54752
54865
  }, undefined, false, undefined, this);
@@ -54756,27 +54869,27 @@ function UserMessage({
54756
54869
  const showImplLink = implSessionId && project && !isPlanMessage;
54757
54870
  const role = isSubAgent ? "Root Agent" : "User";
54758
54871
  const badgeClass = isSubAgent ? "turn-badge-agent" : "turn-badge-user";
54759
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54872
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54760
54873
  className: "turn",
54761
54874
  children: [
54762
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54875
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54763
54876
  className: "turn-header",
54764
54877
  children: [
54765
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54878
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54766
54879
  className: `turn-badge ${badgeClass}`,
54767
54880
  children: role
54768
54881
  }, undefined, false, undefined, this),
54769
- showPlanLink && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("a", {
54882
+ showPlanLink && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("a", {
54770
54883
  className: "subagent-link",
54771
54884
  href: `#/${project}/${planSessionId}`,
54772
54885
  children: "View planning session"
54773
54886
  }, undefined, false, undefined, this),
54774
- showImplLink && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("a", {
54887
+ showImplLink && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("a", {
54775
54888
  className: "subagent-link",
54776
54889
  href: `#/${project}/${implSessionId}`,
54777
54890
  children: "View implementation session"
54778
54891
  }, undefined, false, undefined, this),
54779
- turn.timestamp && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("time", {
54892
+ turn.timestamp && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("time", {
54780
54893
  className: "turn-timestamp",
54781
54894
  dateTime: turn.timestamp,
54782
54895
  "data-tooltip": formatFullDateTime(turn.timestamp),
@@ -54784,28 +54897,28 @@ function UserMessage({
54784
54897
  }, undefined, false, undefined, this)
54785
54898
  ]
54786
54899
  }, undefined, true, undefined, this),
54787
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54900
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54788
54901
  className: `message ${isSubAgent ? "message-root-agent" : "message-user"}`,
54789
54902
  children: [
54790
- turn.command && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54903
+ turn.command && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54791
54904
  className: "command-call",
54792
54905
  children: [
54793
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54906
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54794
54907
  className: "tool-skill-badge",
54795
54908
  children: "skill"
54796
54909
  }, undefined, false, undefined, this),
54797
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54910
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54798
54911
  className: "command-call-label",
54799
54912
  children: turn.command.name
54800
54913
  }, undefined, false, undefined, this)
54801
54914
  ]
54802
54915
  }, undefined, true, undefined, this),
54803
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MarkdownRenderer, {
54916
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(MarkdownRenderer, {
54804
54917
  content: turn.text
54805
54918
  }, undefined, false, undefined, this),
54806
- turn.attachments && turn.attachments.length > 0 && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54919
+ turn.attachments && turn.attachments.length > 0 && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54807
54920
  className: "attachments",
54808
- children: turn.attachments.map((a, i) => /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54921
+ children: turn.attachments.map((a, i) => /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54809
54922
  className: "attachment-badge",
54810
54923
  children: [
54811
54924
  "image/",
@@ -54820,14 +54933,14 @@ function UserMessage({
54820
54933
  }
54821
54934
 
54822
54935
  // src/frontend/components/message/MessageList.tsx
54823
- var jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
54824
- function renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project, isSubAgent, planSessionId, implSessionId) {
54936
+ var jsx_dev_runtime18 = __toESM(require_jsx_dev_runtime(), 1);
54937
+ function renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project, pluginId, isSubAgent, planSessionId, implSessionId) {
54825
54938
  const activeClass = isActive ? "active-message" : "";
54826
54939
  switch (turn.kind) {
54827
54940
  case "user":
54828
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54941
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54829
54942
  className: isActive ? "active-message step-enter" : "",
54830
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(UserMessage, {
54943
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(UserMessage, {
54831
54944
  turn,
54832
54945
  isSubAgent,
54833
54946
  planSessionId,
@@ -54836,27 +54949,28 @@ function renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project,
54836
54949
  }, undefined, false, undefined, this)
54837
54950
  }, undefined, false, undefined, this);
54838
54951
  case "assistant":
54839
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54952
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54840
54953
  className: activeClass,
54841
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(AssistantMessage, {
54954
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(AssistantMessage, {
54842
54955
  turn,
54843
54956
  visibleSubSteps: visibleSubSteps?.get(index2),
54844
54957
  sessionId,
54845
- project
54958
+ project,
54959
+ pluginId
54846
54960
  }, undefined, false, undefined, this)
54847
54961
  }, undefined, false, undefined, this);
54848
54962
  case "system":
54849
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54963
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54850
54964
  className: `turn ${activeClass}`,
54851
54965
  children: [
54852
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54966
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54853
54967
  className: "turn-header",
54854
54968
  children: [
54855
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54969
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("span", {
54856
54970
  className: "turn-badge turn-badge-system",
54857
54971
  children: "System"
54858
54972
  }, undefined, false, undefined, this),
54859
- turn.timestamp && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("time", {
54973
+ turn.timestamp && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("time", {
54860
54974
  className: "turn-timestamp",
54861
54975
  dateTime: turn.timestamp,
54862
54976
  "data-tooltip": formatFullDateTime(turn.timestamp),
@@ -54864,26 +54978,26 @@ function renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project,
54864
54978
  }, undefined, false, undefined, this)
54865
54979
  ]
54866
54980
  }, undefined, true, undefined, this),
54867
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54981
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54868
54982
  className: "message message-system",
54869
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(MarkdownRenderer, {
54983
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(MarkdownRenderer, {
54870
54984
  content: turn.text
54871
54985
  }, undefined, false, undefined, this)
54872
54986
  }, undefined, false, undefined, this)
54873
54987
  ]
54874
54988
  }, undefined, true, undefined, this);
54875
54989
  case "parse_error":
54876
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54990
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54877
54991
  className: `turn ${activeClass}`,
54878
54992
  children: [
54879
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54993
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54880
54994
  className: "turn-header",
54881
54995
  children: [
54882
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54996
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("span", {
54883
54997
  className: "turn-badge turn-badge-error",
54884
54998
  children: "Parse Error"
54885
54999
  }, undefined, false, undefined, this),
54886
- turn.lineNumber > 0 && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
55000
+ turn.lineNumber > 0 && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("span", {
54887
55001
  className: "parse-error-line",
54888
55002
  children: [
54889
55003
  "line ",
@@ -54892,24 +55006,24 @@ function renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project,
54892
55006
  }, undefined, true, undefined, this)
54893
55007
  ]
54894
55008
  }, undefined, true, undefined, this),
54895
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
55009
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54896
55010
  className: "message message-parse-error",
54897
55011
  children: [
54898
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
55012
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54899
55013
  className: "parse-error-type",
54900
55014
  children: turn.errorType === "json_parse" ? "Invalid JSON" : "Invalid Structure"
54901
55015
  }, undefined, false, undefined, this),
54902
- turn.errorDetails && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
55016
+ turn.errorDetails && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54903
55017
  className: "parse-error-details",
54904
55018
  children: turn.errorDetails
54905
55019
  }, undefined, false, undefined, this),
54906
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("details", {
55020
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("details", {
54907
55021
  className: "parse-error-raw",
54908
55022
  children: [
54909
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("summary", {
55023
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("summary", {
54910
55024
  children: "Raw content"
54911
55025
  }, undefined, false, undefined, this),
54912
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("pre", {
55026
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("pre", {
54913
55027
  children: turn.rawLine
54914
55028
  }, undefined, false, undefined, this)
54915
55029
  ]
@@ -54928,6 +55042,7 @@ function MessageList({
54928
55042
  visibleSubSteps,
54929
55043
  sessionId,
54930
55044
  project,
55045
+ pluginId,
54931
55046
  isSubAgent,
54932
55047
  planSessionId,
54933
55048
  implSessionId
@@ -54937,56 +55052,43 @@ function MessageList({
54937
55052
  return false;
54938
55053
  return !STATUS_RE2.test(t.text.trim());
54939
55054
  });
54940
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
55055
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54941
55056
  className: "message-list",
54942
55057
  children: turns.map((turn, index2) => {
54943
55058
  const isActive = visibleSubSteps ? index2 === turns.length - 1 : false;
54944
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(ErrorBoundary, {
55059
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(ErrorBoundary, {
54945
55060
  inline: true,
54946
- children: renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project, isSubAgent, planSessionId, index2 === firstUserTurnIndex ? implSessionId : undefined)
55061
+ children: renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project, pluginId, isSubAgent, planSessionId, index2 === firstUserTurnIndex ? implSessionId : undefined)
54947
55062
  }, turn.uuid || index2, false, undefined, this);
54948
55063
  })
54949
55064
  }, undefined, false, undefined, this);
54950
55065
  }
54951
55066
 
54952
55067
  // src/frontend/components/message/SubAgentView.tsx
54953
- var jsx_dev_runtime18 = __toESM(require_jsx_dev_runtime(), 1);
55068
+ var jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
54954
55069
  function SubAgentView({ sessionId, project, agentId }) {
54955
- const { data, loading, error, retry } = useFetch(`/api/sessions/${sessionId}/subagents/${agentId}?project=${encodeURIComponent(project)}`, [sessionId, project, agentId]);
55070
+ const { data, loading, error, retry } = useSubAgentSessionData(sessionId, project, agentId);
54956
55071
  if (loading)
54957
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55072
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
54958
55073
  className: "loading",
54959
55074
  children: "Loading sub-agent conversation..."
54960
55075
  }, undefined, false, undefined, this);
54961
- if (error) {
54962
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54963
- className: "fetch-error",
54964
- children: [
54965
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("span", {
54966
- className: "fetch-error-message",
54967
- children: [
54968
- "Error: ",
54969
- error
54970
- ]
54971
- }, undefined, true, undefined, this),
54972
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("button", {
54973
- type: "button",
54974
- className: "btn btn-sm",
54975
- onClick: retry,
54976
- children: "Retry"
54977
- }, undefined, false, undefined, this)
54978
- ]
54979
- }, undefined, true, undefined, this);
54980
- }
55076
+ if (error)
55077
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(FetchError, {
55078
+ error,
55079
+ onRetry: retry,
55080
+ showPrefix: true
55081
+ }, undefined, false, undefined, this);
54981
55082
  if (!data?.session || data.session.turns.length === 0)
54982
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55083
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
54983
55084
  className: "subagent-empty",
54984
55085
  children: "No sub-agent conversation data available."
54985
55086
  }, undefined, false, undefined, this);
54986
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(MessageList, {
55087
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(MessageList, {
54987
55088
  turns: data.session.turns,
54988
55089
  sessionId,
54989
55090
  project,
55091
+ pluginId: data.session.pluginId,
54990
55092
  isSubAgent: true
54991
55093
  }, undefined, false, undefined, this);
54992
55094
  }
@@ -54998,25 +55100,25 @@ function projectDisplayName(project) {
54998
55100
  }
54999
55101
 
55000
55102
  // src/frontend/components/project/HiddenProjectList.tsx
55001
- var jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
55103
+ var jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
55002
55104
  function HiddenProjectList({ hiddenIds, onUnhide, onBack }) {
55003
55105
  const { data, loading, error, retry } = useFetch("/api/projects", []);
55004
55106
  const projects = data?.projects ?? [];
55005
55107
  const hidden = projects.filter((p) => hiddenIds.has(p.encodedPath));
55006
55108
  if (loading)
55007
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55109
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55008
55110
  className: "loading",
55009
55111
  children: "Loading..."
55010
55112
  }, undefined, false, undefined, this);
55011
55113
  if (error) {
55012
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55114
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55013
55115
  className: "fetch-error",
55014
55116
  children: [
55015
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("span", {
55117
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("span", {
55016
55118
  className: "fetch-error-message",
55017
55119
  children: error
55018
55120
  }, undefined, false, undefined, this),
55019
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("button", {
55121
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("button", {
55020
55122
  type: "button",
55021
55123
  className: "btn btn-sm",
55022
55124
  onClick: retry,
@@ -55025,40 +55127,40 @@ function HiddenProjectList({ hiddenIds, onUnhide, onBack }) {
55025
55127
  ]
55026
55128
  }, undefined, true, undefined, this);
55027
55129
  }
55028
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55130
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55029
55131
  className: "hidden-projects-page",
55030
55132
  children: [
55031
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55133
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55032
55134
  className: "back-btn",
55033
55135
  onClick: onBack,
55034
55136
  children: "← Back to projects"
55035
55137
  }, undefined, false, undefined, this),
55036
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("h2", {
55138
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("h2", {
55037
55139
  style: { margin: "16px 0 12px", fontSize: "1.1rem", color: "var(--text-primary)" },
55038
55140
  children: "Hidden Projects"
55039
55141
  }, undefined, false, undefined, this),
55040
- hidden.length === 0 ? /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55142
+ hidden.length === 0 ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55041
55143
  className: "empty-state",
55042
55144
  children: [
55043
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55145
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55044
55146
  className: "empty-state-title",
55045
55147
  children: "No hidden projects"
55046
55148
  }, undefined, false, undefined, this),
55047
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("p", {
55149
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("p", {
55048
55150
  children: "Projects you hide will appear here"
55049
55151
  }, undefined, false, undefined, this)
55050
55152
  ]
55051
- }, undefined, true, undefined, this) : hidden.map((project) => /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55153
+ }, undefined, true, undefined, this) : hidden.map((project) => /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55052
55154
  className: "list-item list-item-with-action",
55053
55155
  children: [
55054
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55156
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55055
55157
  className: "list-item-content",
55056
55158
  children: [
55057
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55159
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55058
55160
  className: "list-item-title",
55059
55161
  children: projectDisplayName(project)
55060
55162
  }, undefined, false, undefined, this),
55061
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55163
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55062
55164
  className: "list-item-meta",
55063
55165
  children: [
55064
55166
  project.sessionCount,
@@ -55068,7 +55170,7 @@ function HiddenProjectList({ hiddenIds, onUnhide, onBack }) {
55068
55170
  }, undefined, true, undefined, this)
55069
55171
  ]
55070
55172
  }, undefined, true, undefined, this),
55071
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("button", {
55173
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("button", {
55072
55174
  type: "button",
55073
55175
  className: "btn btn-sm",
55074
55176
  onClick: () => onUnhide(project.encodedPath),
@@ -55080,304 +55182,133 @@ function HiddenProjectList({ hiddenIds, onUnhide, onBack }) {
55080
55182
  }, undefined, true, undefined, this);
55081
55183
  }
55082
55184
 
55083
- // src/frontend/components/project/ProjectList.tsx
55185
+ // src/frontend/components/search/SearchModal.tsx
55084
55186
  var import_react12 = __toESM(require_react(), 1);
55085
- var jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
55086
- function ProjectList({
55087
- onSelect,
55088
- selected,
55089
- hiddenIds,
55090
- onHide,
55091
- onShowHidden
55092
- }) {
55093
- const { data, loading, error, retry } = useFetch("/api/projects", []);
55094
- const [filter, setFilter] = import_react12.useState("");
55095
- const projects = data?.projects ?? [];
55096
- const filtered = projects.filter((p) => !hiddenIds.has(p.encodedPath) && (p.name.toLowerCase().includes(filter.toLowerCase()) || p.encodedPath.toLowerCase().includes(filter.toLowerCase())));
55097
- if (loading)
55098
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55099
- className: "loading",
55100
- children: "Loading projects..."
55101
- }, undefined, false, undefined, this);
55102
- if (error) {
55103
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55104
- className: "fetch-error",
55187
+
55188
+ // src/frontend/utils/plugin.ts
55189
+ var PLUGIN_NAMES = {
55190
+ "claude-code": "Claude Code",
55191
+ "codex-cli": "Codex",
55192
+ opencode: "OpenCode"
55193
+ };
55194
+ function pluginDisplayName(pluginId) {
55195
+ return PLUGIN_NAMES[pluginId] ?? pluginId;
55196
+ }
55197
+
55198
+ // src/frontend/components/search/SearchModal.tsx
55199
+ var jsx_dev_runtime21 = __toESM(require_jsx_dev_runtime(), 1);
55200
+ var MAX_RESULTS = 20;
55201
+ function matchesQuery(result, query) {
55202
+ const q2 = query.toLowerCase();
55203
+ return result.firstMessage.toLowerCase().includes(q2) || result.projectName.toLowerCase().includes(q2) || result.gitBranch.toLowerCase().includes(q2);
55204
+ }
55205
+ function SearchModal({ sessions, onSelect, onClose }) {
55206
+ const [query, setQuery] = import_react12.useState("");
55207
+ const [highlightedIndex, setHighlightedIndex] = import_react12.useState(0);
55208
+ const inputRef = import_react12.useRef(null);
55209
+ const resultsRef = import_react12.useRef(null);
55210
+ const filtered = query ? sessions.filter((s2) => matchesQuery(s2, query)).slice(0, MAX_RESULTS) : sessions.slice(0, MAX_RESULTS);
55211
+ import_react12.useEffect(() => {
55212
+ inputRef.current?.focus();
55213
+ }, []);
55214
+ import_react12.useEffect(() => {
55215
+ const container = resultsRef.current;
55216
+ if (!container)
55217
+ return;
55218
+ const items = container.querySelectorAll("[data-search-item]");
55219
+ const item = items[highlightedIndex];
55220
+ if (item) {
55221
+ item.scrollIntoView({ block: "nearest" });
55222
+ }
55223
+ }, [highlightedIndex]);
55224
+ const handleSelect = import_react12.useCallback((result) => {
55225
+ onSelect(result.encodedPath, result.sessionId);
55226
+ }, [onSelect]);
55227
+ const handleKeyDown = import_react12.useCallback((e) => {
55228
+ switch (e.key) {
55229
+ case "ArrowDown":
55230
+ e.preventDefault();
55231
+ setHighlightedIndex((prev) => Math.min(prev + 1, filtered.length - 1));
55232
+ break;
55233
+ case "ArrowUp":
55234
+ e.preventDefault();
55235
+ setHighlightedIndex((prev) => Math.max(prev - 1, 0));
55236
+ break;
55237
+ case "Enter":
55238
+ e.preventDefault();
55239
+ if (filtered[highlightedIndex]) {
55240
+ handleSelect(filtered[highlightedIndex]);
55241
+ }
55242
+ break;
55243
+ case "Escape":
55244
+ e.preventDefault();
55245
+ onClose();
55246
+ break;
55247
+ }
55248
+ }, [filtered, highlightedIndex, handleSelect, onClose]);
55249
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55250
+ className: "search-overlay",
55251
+ onMouseDown: onClose,
55252
+ children: /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55253
+ className: "search-modal",
55254
+ onMouseDown: (e) => e.stopPropagation(),
55105
55255
  children: [
55106
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("span", {
55107
- className: "fetch-error-message",
55108
- children: error
55256
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55257
+ className: "search-input-wrapper",
55258
+ children: /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("input", {
55259
+ ref: inputRef,
55260
+ className: "search-input",
55261
+ type: "text",
55262
+ placeholder: "Search sessions...",
55263
+ value: query,
55264
+ onChange: (e) => {
55265
+ setQuery(e.target.value);
55266
+ setHighlightedIndex(0);
55267
+ },
55268
+ onKeyDown: handleKeyDown
55269
+ }, undefined, false, undefined, this)
55109
55270
  }, undefined, false, undefined, this),
55110
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("button", {
55111
- type: "button",
55112
- className: "btn btn-sm",
55113
- onClick: retry,
55114
- children: "Retry"
55115
- }, undefined, false, undefined, this)
55116
- ]
55117
- }, undefined, true, undefined, this);
55118
- }
55119
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55120
- children: [
55121
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("input", {
55122
- className: "filter-input",
55123
- placeholder: "Filter projects...",
55124
- value: filter,
55125
- onChange: (e) => setFilter(e.target.value)
55126
- }, undefined, false, undefined, this),
55127
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55128
- className: "list-section-title",
55129
- children: [
55130
- "Projects (",
55131
- filtered.length,
55132
- ")"
55133
- ]
55134
- }, undefined, true, undefined, this),
55135
- filtered.map((project) => /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55136
- className: `list-item list-item-with-action ${selected === project.encodedPath ? "active" : ""}`,
55137
- onClick: () => onSelect(project),
55138
- children: [
55139
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55140
- className: "list-item-content",
55141
- children: [
55142
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55143
- className: "list-item-title",
55144
- children: projectDisplayName(project)
55145
- }, undefined, false, undefined, this),
55146
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55147
- className: "list-item-meta",
55148
- children: [
55149
- project.sessionCount,
55150
- " session",
55151
- project.sessionCount !== 1 ? "s" : "",
55152
- " · ",
55153
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("time", {
55154
- dateTime: project.lastActivity,
55155
- title: formatFullDateTime(project.lastActivity),
55156
- children: formatRelativeTime(project.lastActivity)
55157
- }, undefined, false, undefined, this)
55158
- ]
55159
- }, undefined, true, undefined, this)
55160
- ]
55161
- }, undefined, true, undefined, this),
55162
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("button", {
55163
- type: "button",
55164
- className: "btn-hide",
55165
- title: "Hide project",
55166
- onClick: (e) => {
55167
- e.stopPropagation();
55168
- onHide(project.encodedPath);
55169
- },
55170
- children: "×"
55171
- }, undefined, false, undefined, this)
55172
- ]
55173
- }, project.encodedPath, true, undefined, this)),
55174
- filtered.length === 0 && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55175
- className: "empty-list-message",
55176
- children: "No projects found"
55177
- }, undefined, false, undefined, this),
55178
- hiddenIds.size > 0 && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55179
- className: "hidden-projects-link",
55180
- onClick: onShowHidden,
55181
- children: [
55182
- hiddenIds.size,
55183
- " hidden project",
55184
- hiddenIds.size !== 1 ? "s" : ""
55185
- ]
55186
- }, undefined, true, undefined, this)
55187
- ]
55188
- }, undefined, true, undefined, this);
55189
- }
55190
-
55191
- // src/frontend/components/project/SessionList.tsx
55192
- var jsx_dev_runtime21 = __toESM(require_jsx_dev_runtime(), 1);
55193
- function SessionList({ project, onSelect, onBack, selectedId }) {
55194
- const { data, loading, error, retry } = useFetch(`/api/projects/${encodeURIComponent(project.encodedPath)}/sessions`, [project.encodedPath]);
55195
- const sessions = data?.sessions ?? [];
55196
- const parts = project.name.split("/").filter(Boolean);
55197
- const displayName = parts.slice(-2).join("/");
55198
- return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55199
- children: [
55200
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55201
- className: "back-btn",
55202
- onClick: onBack,
55203
- children: "← Projects"
55204
- }, undefined, false, undefined, this),
55205
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55206
- className: "list-section-title",
55207
- children: displayName
55208
- }, undefined, false, undefined, this),
55209
- loading && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55210
- className: "loading",
55211
- children: "Loading sessions..."
55212
- }, undefined, false, undefined, this),
55213
- error && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55214
- className: "fetch-error",
55215
- children: [
55216
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55217
- className: "fetch-error-message",
55218
- children: error
55219
- }, undefined, false, undefined, this),
55220
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("button", {
55221
- type: "button",
55222
- className: "btn btn-sm",
55223
- onClick: retry,
55224
- children: "Retry"
55225
- }, undefined, false, undefined, this)
55226
- ]
55227
- }, undefined, true, undefined, this),
55228
- !loading && !error && sessions.map((session) => /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55229
- className: `list-item ${selectedId === session.sessionId ? "active" : ""}${session.sessionType ? ` ${session.sessionType}` : ""}`,
55230
- onClick: () => onSelect(session),
55231
- children: [
55232
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55233
- className: "list-item-title",
55234
- children: session.firstMessage || session.slug
55235
- }, undefined, false, undefined, this),
55236
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55237
- className: "list-item-meta",
55238
- children: [
55239
- session.sessionType && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55240
- className: `session-type-badge ${session.sessionType}`,
55241
- children: session.sessionType === "plan" ? "Plan" : "Impl"
55242
- }, undefined, false, undefined, this),
55243
- " ",
55244
- session.model && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55245
- children: [
55246
- shortModel(session.model),
55247
- " · "
55248
- ]
55249
- }, undefined, true, undefined, this),
55250
- session.gitBranch && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55251
- children: [
55252
- session.gitBranch,
55253
- " · "
55254
- ]
55255
- }, undefined, true, undefined, this),
55256
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("time", {
55257
- dateTime: session.timestamp,
55258
- title: formatFullDateTime(session.timestamp),
55259
- children: formatTime(session.timestamp)
55260
- }, undefined, false, undefined, this)
55261
- ]
55262
- }, undefined, true, undefined, this)
55263
- ]
55264
- }, session.sessionId, true, undefined, this)),
55265
- !loading && !error && sessions.length === 0 && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55266
- className: "empty-list-message",
55267
- children: "No sessions found"
55268
- }, undefined, false, undefined, this)
55269
- ]
55270
- }, undefined, true, undefined, this);
55271
- }
55272
-
55273
- // src/frontend/components/search/SearchModal.tsx
55274
- var import_react13 = __toESM(require_react(), 1);
55275
- var jsx_dev_runtime22 = __toESM(require_jsx_dev_runtime(), 1);
55276
- var MAX_RESULTS = 20;
55277
- function matchesQuery(result, query) {
55278
- const q2 = query.toLowerCase();
55279
- return result.firstMessage.toLowerCase().includes(q2) || result.projectName.toLowerCase().includes(q2) || result.gitBranch.toLowerCase().includes(q2);
55280
- }
55281
- function SearchModal({ sessions, onSelect, onClose }) {
55282
- const [query, setQuery] = import_react13.useState("");
55283
- const [highlightedIndex, setHighlightedIndex] = import_react13.useState(0);
55284
- const inputRef = import_react13.useRef(null);
55285
- const resultsRef = import_react13.useRef(null);
55286
- const filtered = query ? sessions.filter((s2) => matchesQuery(s2, query)).slice(0, MAX_RESULTS) : sessions.slice(0, MAX_RESULTS);
55287
- import_react13.useEffect(() => {
55288
- inputRef.current?.focus();
55289
- }, []);
55290
- import_react13.useEffect(() => {
55291
- const container = resultsRef.current;
55292
- if (!container)
55293
- return;
55294
- const items = container.querySelectorAll("[data-search-item]");
55295
- const item = items[highlightedIndex];
55296
- if (item) {
55297
- item.scrollIntoView({ block: "nearest" });
55298
- }
55299
- }, [highlightedIndex]);
55300
- const handleSelect = import_react13.useCallback((result) => {
55301
- onSelect(result.encodedPath, result.sessionId);
55302
- }, [onSelect]);
55303
- const handleKeyDown = import_react13.useCallback((e) => {
55304
- switch (e.key) {
55305
- case "ArrowDown":
55306
- e.preventDefault();
55307
- setHighlightedIndex((prev) => Math.min(prev + 1, filtered.length - 1));
55308
- break;
55309
- case "ArrowUp":
55310
- e.preventDefault();
55311
- setHighlightedIndex((prev) => Math.max(prev - 1, 0));
55312
- break;
55313
- case "Enter":
55314
- e.preventDefault();
55315
- if (filtered[highlightedIndex]) {
55316
- handleSelect(filtered[highlightedIndex]);
55317
- }
55318
- break;
55319
- case "Escape":
55320
- e.preventDefault();
55321
- onClose();
55322
- break;
55323
- }
55324
- }, [filtered, highlightedIndex, handleSelect, onClose]);
55325
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55326
- className: "search-overlay",
55327
- onMouseDown: onClose,
55328
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55329
- className: "search-modal",
55330
- onMouseDown: (e) => e.stopPropagation(),
55331
- children: [
55332
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55333
- className: "search-input-wrapper",
55334
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("input", {
55335
- ref: inputRef,
55336
- className: "search-input",
55337
- type: "text",
55338
- placeholder: "Search sessions...",
55339
- value: query,
55340
- onChange: (e) => {
55341
- setQuery(e.target.value);
55342
- setHighlightedIndex(0);
55343
- },
55344
- onKeyDown: handleKeyDown
55345
- }, undefined, false, undefined, this)
55346
- }, undefined, false, undefined, this),
55347
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55271
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55348
55272
  className: "search-results",
55349
55273
  ref: resultsRef,
55350
- children: filtered.length === 0 ? /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55274
+ children: filtered.length === 0 ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55351
55275
  className: "search-empty",
55352
55276
  children: "No results found"
55353
- }, undefined, false, undefined, this) : filtered.map((result, index2) => /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55277
+ }, undefined, false, undefined, this) : filtered.map((result, index2) => /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55354
55278
  className: `search-result-item ${index2 === highlightedIndex ? "highlighted" : ""}`,
55355
55279
  "data-search-item": true,
55356
55280
  onClick: () => handleSelect(result),
55357
55281
  onMouseEnter: () => setHighlightedIndex(index2),
55358
55282
  children: [
55359
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55283
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55360
55284
  className: "search-result-title",
55361
55285
  children: [
55362
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("span", {
55286
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55363
55287
  children: result.firstMessage
55364
55288
  }, undefined, false, undefined, this),
55365
- result.sessionType && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("span", {
55289
+ result.sessionType && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55366
55290
  className: `session-type-badge ${result.sessionType}`,
55367
55291
  children: result.sessionType
55368
55292
  }, undefined, false, undefined, this)
55369
55293
  ]
55370
55294
  }, undefined, true, undefined, this),
55371
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55295
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55372
55296
  className: "search-result-meta",
55373
55297
  children: [
55374
55298
  result.projectName,
55375
- " · ",
55376
- shortModel(result.model),
55377
- result.gitBranch ? ` · ${result.gitBranch}` : "",
55378
- " ·",
55299
+ result.pluginId && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(jsx_dev_runtime21.Fragment, {
55300
+ children: [
55301
+ " ",
55302
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55303
+ className: "plugin-badge",
55304
+ children: pluginDisplayName(result.pluginId)
55305
+ }, undefined, false, undefined, this)
55306
+ ]
55307
+ }, undefined, true, undefined, this),
55308
+ " ",
55309
+ "·",
55379
55310
  " ",
55380
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("time", {
55311
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("time", {
55381
55312
  dateTime: result.timestamp,
55382
55313
  title: formatFullDateTime(result.timestamp),
55383
55314
  children: formatRelativeTime(result.timestamp)
@@ -55387,28 +55318,28 @@ function SearchModal({ sessions, onSelect, onClose }) {
55387
55318
  ]
55388
55319
  }, `${result.encodedPath}-${result.sessionId}`, true, undefined, this))
55389
55320
  }, undefined, false, undefined, this),
55390
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55321
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("div", {
55391
55322
  className: "search-footer",
55392
55323
  children: [
55393
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("span", {
55324
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55394
55325
  children: [
55395
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("kbd", {
55326
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("kbd", {
55396
55327
  children: "↑↓"
55397
55328
  }, undefined, false, undefined, this),
55398
55329
  " navigate"
55399
55330
  ]
55400
55331
  }, undefined, true, undefined, this),
55401
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("span", {
55332
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55402
55333
  children: [
55403
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("kbd", {
55334
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("kbd", {
55404
55335
  children: "↵"
55405
55336
  }, undefined, false, undefined, this),
55406
55337
  " open"
55407
55338
  ]
55408
55339
  }, undefined, true, undefined, this),
55409
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("span", {
55340
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("span", {
55410
55341
  children: [
55411
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("kbd", {
55342
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("kbd", {
55412
55343
  children: "esc"
55413
55344
  }, undefined, false, undefined, this),
55414
55345
  " close"
@@ -55422,12 +55353,12 @@ function SearchModal({ sessions, onSelect, onClose }) {
55422
55353
  }
55423
55354
 
55424
55355
  // src/frontend/components/session/PresentationShell.tsx
55425
- var import_react16 = __toESM(require_react(), 1);
55356
+ var import_react15 = __toESM(require_react(), 1);
55426
55357
 
55427
55358
  // src/frontend/hooks/useKeyboard.ts
55428
- var import_react14 = __toESM(require_react(), 1);
55359
+ var import_react13 = __toESM(require_react(), 1);
55429
55360
  function useKeyboard(handlers2, active) {
55430
- import_react14.useEffect(() => {
55361
+ import_react13.useEffect(() => {
55431
55362
  if (!active)
55432
55363
  return;
55433
55364
  function handleKeyDown(e) {
@@ -55470,7 +55401,7 @@ function useKeyboard(handlers2, active) {
55470
55401
  }
55471
55402
 
55472
55403
  // src/frontend/hooks/usePresentationMode.ts
55473
- var import_react15 = __toESM(require_react(), 1);
55404
+ var import_react14 = __toESM(require_react(), 1);
55474
55405
  function countSubSteps(turn) {
55475
55406
  if (turn.kind !== "assistant")
55476
55407
  return 1;
@@ -55478,10 +55409,10 @@ function countSubSteps(turn) {
55478
55409
  return Math.max(groupContentBlocks(a.contentBlocks).length, 1);
55479
55410
  }
55480
55411
  function usePresentationMode(turns) {
55481
- const [active, setActive] = import_react15.useState(false);
55482
- const [fullscreen, setFullscreen] = import_react15.useState(false);
55483
- const [currentStep, setCurrentStep] = import_react15.useState(0);
55484
- const steps = import_react15.useMemo(() => {
55412
+ const [active, setActive] = import_react14.useState(false);
55413
+ const [fullscreen, setFullscreen] = import_react14.useState(false);
55414
+ const [currentStep, setCurrentStep] = import_react14.useState(0);
55415
+ const steps = import_react14.useMemo(() => {
55485
55416
  const result = [];
55486
55417
  for (let i = 0;i < turns.length; i++) {
55487
55418
  const sub = countSubSteps(turns[i]);
@@ -55492,7 +55423,7 @@ function usePresentationMode(turns) {
55492
55423
  return result;
55493
55424
  }, [turns]);
55494
55425
  const totalSteps = steps.length;
55495
- const turnBoundaries = import_react15.useMemo(() => {
55426
+ const turnBoundaries = import_react14.useMemo(() => {
55496
55427
  const boundaries = [];
55497
55428
  for (let i = 0;i < steps.length; i++) {
55498
55429
  const next2 = steps[i + 1];
@@ -55502,7 +55433,7 @@ function usePresentationMode(turns) {
55502
55433
  }
55503
55434
  return boundaries;
55504
55435
  }, [steps]);
55505
- const { visibleTurns, visibleSubSteps } = import_react15.useMemo(() => {
55436
+ const { visibleTurns, visibleSubSteps } = import_react14.useMemo(() => {
55506
55437
  if (!active || steps.length === 0) {
55507
55438
  return { visibleTurns: turns, visibleSubSteps: new Map };
55508
55439
  }
@@ -55516,22 +55447,22 @@ function usePresentationMode(turns) {
55516
55447
  subSteps.set(maxTurnIndex, step.subStep + 1);
55517
55448
  return { visibleTurns: visible, visibleSubSteps: subSteps };
55518
55449
  }, [active, currentStep, steps, turns]);
55519
- const enter = import_react15.useCallback(() => {
55450
+ const enter = import_react14.useCallback(() => {
55520
55451
  setActive(true);
55521
55452
  setCurrentStep(0);
55522
55453
  }, []);
55523
- const exit3 = import_react15.useCallback(() => {
55454
+ const exit3 = import_react14.useCallback(() => {
55524
55455
  setActive(false);
55525
55456
  setFullscreen(false);
55526
55457
  setCurrentStep(0);
55527
55458
  }, []);
55528
- const next = import_react15.useCallback(() => {
55459
+ const next = import_react14.useCallback(() => {
55529
55460
  setCurrentStep((s2) => Math.min(s2 + 1, steps.length - 1));
55530
55461
  }, [steps.length]);
55531
- const prev = import_react15.useCallback(() => {
55462
+ const prev = import_react14.useCallback(() => {
55532
55463
  setCurrentStep((s2) => Math.max(s2 - 1, 0));
55533
55464
  }, []);
55534
- const nextTurn = import_react15.useCallback(() => {
55465
+ const nextTurn = import_react14.useCallback(() => {
55535
55466
  setCurrentStep((s2) => {
55536
55467
  for (const b of turnBoundaries) {
55537
55468
  if (b > s2)
@@ -55540,7 +55471,7 @@ function usePresentationMode(turns) {
55540
55471
  return s2;
55541
55472
  });
55542
55473
  }, [turnBoundaries]);
55543
- const prevTurn = import_react15.useCallback(() => {
55474
+ const prevTurn = import_react14.useCallback(() => {
55544
55475
  setCurrentStep((s2) => {
55545
55476
  for (let i = turnBoundaries.length - 1;i >= 0; i--) {
55546
55477
  if (turnBoundaries[i] < s2)
@@ -55549,7 +55480,7 @@ function usePresentationMode(turns) {
55549
55480
  return s2;
55550
55481
  });
55551
55482
  }, [turnBoundaries]);
55552
- const toggleFullscreen = import_react15.useCallback(() => {
55483
+ const toggleFullscreen = import_react14.useCallback(() => {
55553
55484
  setFullscreen((f) => !f);
55554
55485
  }, []);
55555
55486
  return {
@@ -55570,17 +55501,18 @@ function usePresentationMode(turns) {
55570
55501
  }
55571
55502
 
55572
55503
  // src/frontend/components/session/PresentationShell.tsx
55573
- var jsx_dev_runtime23 = __toESM(require_jsx_dev_runtime(), 1);
55504
+ var jsx_dev_runtime22 = __toESM(require_jsx_dev_runtime(), 1);
55574
55505
  function PresentationShell({
55575
55506
  turns,
55576
55507
  onExit,
55577
55508
  sessionId,
55578
55509
  project,
55510
+ pluginId,
55579
55511
  isSubAgent
55580
55512
  }) {
55581
- const scrollRef = import_react16.useRef(null);
55513
+ const scrollRef = import_react15.useRef(null);
55582
55514
  const presentation = usePresentationMode(turns);
55583
- import_react16.useEffect(() => {
55515
+ import_react15.useEffect(() => {
55584
55516
  if (turns.length > 0 && !presentation.active) {
55585
55517
  presentation.enter();
55586
55518
  }
@@ -55594,28 +55526,29 @@ function PresentationShell({
55594
55526
  onFullscreen: presentation.toggleFullscreen
55595
55527
  }, presentation.active);
55596
55528
  const currentStep = presentation.currentStep;
55597
- import_react16.useEffect(() => {
55529
+ import_react15.useEffect(() => {
55598
55530
  if (currentStep >= 0 && scrollRef.current) {
55599
55531
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
55600
55532
  }
55601
55533
  }, [currentStep]);
55602
55534
  const progress = presentation.totalSteps > 0 ? (presentation.currentStep + 1) / presentation.totalSteps * 100 : 0;
55603
- return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("div", {
55535
+ return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55604
55536
  className: `presentation-mode ${presentation.fullscreen ? "fullscreen" : ""}`,
55605
55537
  ref: scrollRef,
55606
55538
  style: { overflowY: "auto", height: "calc(100vh - 92px)" },
55607
55539
  children: [
55608
- /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(MessageList, {
55540
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(MessageList, {
55609
55541
  turns: presentation.visibleTurns,
55610
55542
  visibleSubSteps: presentation.visibleSubSteps,
55611
55543
  sessionId,
55612
55544
  project,
55545
+ pluginId,
55613
55546
  isSubAgent
55614
55547
  }, undefined, false, undefined, this),
55615
- /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("div", {
55548
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55616
55549
  className: "presentation-progress",
55617
55550
  children: [
55618
- /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("span", {
55551
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("span", {
55619
55552
  children: [
55620
55553
  "Step ",
55621
55554
  presentation.currentStep + 1,
@@ -55623,14 +55556,14 @@ function PresentationShell({
55623
55556
  presentation.totalSteps
55624
55557
  ]
55625
55558
  }, undefined, true, undefined, this),
55626
- /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("div", {
55559
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55627
55560
  className: "presentation-progress-bar",
55628
- children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("div", {
55561
+ children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
55629
55562
  className: "presentation-progress-fill",
55630
55563
  style: { width: `${progress}%` }
55631
55564
  }, undefined, false, undefined, this)
55632
55565
  }, undefined, false, undefined, this),
55633
- /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("span", {
55566
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("span", {
55634
55567
  style: { fontSize: "0.75rem" },
55635
55568
  children: "← → step · ↑ ↓ message · Esc exit · F fullscreen"
55636
55569
  }, undefined, false, undefined, this)
@@ -55641,132 +55574,108 @@ function PresentationShell({
55641
55574
  }
55642
55575
 
55643
55576
  // src/frontend/components/session/SessionPresentation.tsx
55644
- var jsx_dev_runtime24 = __toESM(require_jsx_dev_runtime(), 1);
55577
+ var jsx_dev_runtime23 = __toESM(require_jsx_dev_runtime(), 1);
55645
55578
  function SessionPresentation({ sessionId, project, onExit }) {
55646
- const { data, loading, error, retry } = useFetch(`/api/sessions/${sessionId}?project=${encodeURIComponent(project)}`, [sessionId, project]);
55579
+ const { data, loading, error, retry } = useSessionData(sessionId, project);
55647
55580
  if (loading)
55648
- return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("div", {
55581
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("div", {
55649
55582
  className: "loading",
55650
55583
  children: "Loading session..."
55651
55584
  }, undefined, false, undefined, this);
55652
- if (error) {
55653
- return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("div", {
55654
- className: "fetch-error",
55655
- children: [
55656
- /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("span", {
55657
- className: "fetch-error-message",
55658
- children: [
55659
- "Error: ",
55660
- error
55661
- ]
55662
- }, undefined, true, undefined, this),
55663
- /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("button", {
55664
- type: "button",
55665
- className: "btn btn-sm",
55666
- onClick: retry,
55667
- children: "Retry"
55668
- }, undefined, false, undefined, this)
55669
- ]
55670
- }, undefined, true, undefined, this);
55671
- }
55585
+ if (error)
55586
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(FetchError, {
55587
+ error,
55588
+ onRetry: retry,
55589
+ showPrefix: true
55590
+ }, undefined, false, undefined, this);
55672
55591
  if (!data?.session)
55673
55592
  return null;
55674
- return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(PresentationShell, {
55593
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(PresentationShell, {
55675
55594
  turns: data.session.turns,
55676
55595
  onExit,
55677
55596
  sessionId,
55678
- project
55597
+ project,
55598
+ pluginId: data.session.pluginId
55679
55599
  }, undefined, false, undefined, this);
55680
55600
  }
55681
55601
 
55682
55602
  // src/frontend/components/session/SessionView.tsx
55683
- var jsx_dev_runtime25 = __toESM(require_jsx_dev_runtime(), 1);
55684
- function SessionView({ sessionId, project }) {
55685
- const { data, loading, error, retry } = useFetch(`/api/sessions/${sessionId}?project=${encodeURIComponent(project)}`, [sessionId, project]);
55603
+ var jsx_dev_runtime24 = __toESM(require_jsx_dev_runtime(), 1);
55604
+ function SessionView({ sessionId, project, gitBranch }) {
55605
+ const { data, loading, error, retry } = useSessionData(sessionId, project);
55686
55606
  if (loading)
55687
- return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV("div", {
55607
+ return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("div", {
55688
55608
  className: "loading",
55689
55609
  children: "Loading session..."
55690
55610
  }, undefined, false, undefined, this);
55691
- if (error) {
55692
- return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV("div", {
55693
- className: "fetch-error",
55694
- children: [
55695
- /* @__PURE__ */ jsx_dev_runtime25.jsxDEV("span", {
55696
- className: "fetch-error-message",
55697
- children: [
55698
- "Error: ",
55699
- error
55700
- ]
55701
- }, undefined, true, undefined, this),
55702
- /* @__PURE__ */ jsx_dev_runtime25.jsxDEV("button", {
55703
- type: "button",
55704
- className: "btn btn-sm",
55705
- onClick: retry,
55706
- children: "Retry"
55707
- }, undefined, false, undefined, this)
55708
- ]
55709
- }, undefined, true, undefined, this);
55710
- }
55611
+ if (error)
55612
+ return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(FetchError, {
55613
+ error,
55614
+ onRetry: retry,
55615
+ showPrefix: true
55616
+ }, undefined, false, undefined, this);
55711
55617
  if (!data?.session)
55712
55618
  return null;
55713
55619
  const session = data.session;
55714
- return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(MessageList, {
55715
- turns: session.turns,
55716
- sessionId,
55717
- project,
55718
- planSessionId: session.planSessionId,
55719
- implSessionId: session.implSessionId
55720
- }, undefined, false, undefined, this);
55721
- }
55722
-
55723
- // src/frontend/components/session/SubAgentPresentation.tsx
55724
- var jsx_dev_runtime26 = __toESM(require_jsx_dev_runtime(), 1);
55620
+ return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(jsx_dev_runtime24.Fragment, {
55621
+ children: [
55622
+ gitBranch && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("div", {
55623
+ className: "session-branch-bar",
55624
+ children: [
55625
+ /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("span", {
55626
+ className: "session-branch-icon",
55627
+ children: "⎇"
55628
+ }, undefined, false, undefined, this),
55629
+ " ",
55630
+ gitBranch
55631
+ ]
55632
+ }, undefined, true, undefined, this),
55633
+ /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(MessageList, {
55634
+ turns: session.turns,
55635
+ sessionId,
55636
+ project,
55637
+ pluginId: session.pluginId,
55638
+ planSessionId: session.planSessionId,
55639
+ implSessionId: session.implSessionId
55640
+ }, undefined, false, undefined, this)
55641
+ ]
55642
+ }, undefined, true, undefined, this);
55643
+ }
55644
+
55645
+ // src/frontend/components/session/SubAgentPresentation.tsx
55646
+ var jsx_dev_runtime25 = __toESM(require_jsx_dev_runtime(), 1);
55725
55647
  function SubAgentPresentation({
55726
55648
  sessionId,
55727
55649
  project,
55728
55650
  agentId,
55729
55651
  onExit
55730
55652
  }) {
55731
- const { data, loading, error, retry } = useFetch(`/api/sessions/${sessionId}/subagents/${agentId}?project=${encodeURIComponent(project)}`, [sessionId, project, agentId]);
55653
+ const { data, loading, error, retry } = useSubAgentSessionData(sessionId, project, agentId);
55732
55654
  if (loading)
55733
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55655
+ return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV("div", {
55734
55656
  className: "loading",
55735
55657
  children: "Loading sub-agent conversation..."
55736
55658
  }, undefined, false, undefined, this);
55737
- if (error) {
55738
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55739
- className: "fetch-error",
55740
- children: [
55741
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("span", {
55742
- className: "fetch-error-message",
55743
- children: [
55744
- "Error: ",
55745
- error
55746
- ]
55747
- }, undefined, true, undefined, this),
55748
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("button", {
55749
- type: "button",
55750
- className: "btn btn-sm",
55751
- onClick: retry,
55752
- children: "Retry"
55753
- }, undefined, false, undefined, this)
55754
- ]
55755
- }, undefined, true, undefined, this);
55756
- }
55659
+ if (error)
55660
+ return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(FetchError, {
55661
+ error,
55662
+ onRetry: retry,
55663
+ showPrefix: true
55664
+ }, undefined, false, undefined, this);
55757
55665
  if (!data?.session || data.session.turns.length === 0)
55758
55666
  return null;
55759
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(PresentationShell, {
55667
+ return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(PresentationShell, {
55760
55668
  turns: data.session.turns,
55761
55669
  onExit,
55762
55670
  sessionId,
55763
55671
  project,
55672
+ pluginId: data.session.pluginId,
55764
55673
  isSubAgent: true
55765
55674
  }, undefined, false, undefined, this);
55766
55675
  }
55767
55676
 
55768
55677
  // src/frontend/hooks/useHiddenProjects.ts
55769
- var import_react17 = __toESM(require_react(), 1);
55678
+ var import_react16 = __toESM(require_react(), 1);
55770
55679
  var STORAGE_KEY = "klovi-hidden-projects";
55771
55680
  function loadHiddenIds() {
55772
55681
  try {
@@ -55788,8 +55697,8 @@ function persistHiddenIds(ids) {
55788
55697
  localStorage.setItem(STORAGE_KEY, JSON.stringify(store));
55789
55698
  }
55790
55699
  function useHiddenProjects() {
55791
- const [hiddenIds, setHiddenIds] = import_react17.useState(() => loadHiddenIds());
55792
- const hide = import_react17.useCallback((encodedPath) => {
55700
+ const [hiddenIds, setHiddenIds] = import_react16.useState(() => loadHiddenIds());
55701
+ const hide = import_react16.useCallback((encodedPath) => {
55793
55702
  setHiddenIds((prev) => {
55794
55703
  const next = new Set(prev);
55795
55704
  next.add(encodedPath);
@@ -55797,7 +55706,7 @@ function useHiddenProjects() {
55797
55706
  return next;
55798
55707
  });
55799
55708
  }, []);
55800
- const unhide = import_react17.useCallback((encodedPath) => {
55709
+ const unhide = import_react16.useCallback((encodedPath) => {
55801
55710
  setHiddenIds((prev) => {
55802
55711
  const next = new Set(prev);
55803
55712
  next.delete(encodedPath);
@@ -55805,12 +55714,38 @@ function useHiddenProjects() {
55805
55714
  return next;
55806
55715
  });
55807
55716
  }, []);
55808
- const isHidden = import_react17.useCallback((encodedPath) => hiddenIds.has(encodedPath), [hiddenIds]);
55717
+ const isHidden = import_react16.useCallback((encodedPath) => hiddenIds.has(encodedPath), [hiddenIds]);
55809
55718
  return { hiddenIds, hide, unhide, isHidden };
55810
55719
  }
55811
55720
 
55812
- // src/frontend/App.tsx
55813
- var jsx_dev_runtime27 = __toESM(require_jsx_dev_runtime(), 1);
55721
+ // src/frontend/hooks/useViewState.ts
55722
+ var import_react17 = __toESM(require_react(), 1);
55723
+
55724
+ // src/shared/session-id.ts
55725
+ var SESSION_ID_SEPARATOR = "::";
55726
+ function parseSessionId(sessionId) {
55727
+ const separatorIdx = sessionId.indexOf(SESSION_ID_SEPARATOR);
55728
+ if (separatorIdx === -1) {
55729
+ return { pluginId: null, rawSessionId: sessionId };
55730
+ }
55731
+ return {
55732
+ pluginId: sessionId.slice(0, separatorIdx),
55733
+ rawSessionId: sessionId.slice(separatorIdx + SESSION_ID_SEPARATOR.length)
55734
+ };
55735
+ }
55736
+
55737
+ // src/frontend/view-state.ts
55738
+ function getResumeCommand(pluginId, encodedSessionId) {
55739
+ const rawSessionId = parseSessionId(encodedSessionId).rawSessionId;
55740
+ switch (pluginId) {
55741
+ case "claude-code":
55742
+ return `claude --resume ${rawSessionId}`;
55743
+ case "codex-cli":
55744
+ return `codex resume ${rawSessionId}`;
55745
+ default:
55746
+ return;
55747
+ }
55748
+ }
55814
55749
  function viewToHash(view) {
55815
55750
  if (view.kind === "hidden")
55816
55751
  return "#/hidden";
@@ -55822,6 +55757,33 @@ function viewToHash(view) {
55822
55757
  return `#/${view.project.encodedPath}/${view.sessionId}/subagent/${view.agentId}`;
55823
55758
  return "#/";
55824
55759
  }
55760
+ async function loadProject(encodedPath) {
55761
+ const res = await fetch("/api/projects");
55762
+ if (!res.ok)
55763
+ return;
55764
+ const data = await res.json();
55765
+ return data.projects.find((p) => p.encodedPath === encodedPath);
55766
+ }
55767
+ async function loadProjectSession(project, sessionId) {
55768
+ const res = await fetch(`/api/projects/${encodeURIComponent(project.encodedPath)}/sessions`);
55769
+ if (!res.ok)
55770
+ return;
55771
+ const data = await res.json();
55772
+ return data.sessions.find((s2) => s2.sessionId === sessionId);
55773
+ }
55774
+ async function resolveProjectAndSession(encodedPath, sessionId) {
55775
+ try {
55776
+ const project = await loadProject(encodedPath);
55777
+ if (!project)
55778
+ return null;
55779
+ const session = await loadProjectSession(project, sessionId);
55780
+ if (!session)
55781
+ return null;
55782
+ return { project, session };
55783
+ } catch {
55784
+ return null;
55785
+ }
55786
+ }
55825
55787
  async function restoreFromHash() {
55826
55788
  const hash = window.location.hash.replace(/^#\/?/, "");
55827
55789
  if (!hash)
@@ -55832,11 +55794,11 @@ async function restoreFromHash() {
55832
55794
  const encodedPath = parts[0];
55833
55795
  const sessionId = parts[1];
55834
55796
  const isSubAgent = parts[2] === "subagent" && parts[3];
55797
+ if (!encodedPath)
55798
+ return { kind: "home" };
55835
55799
  let project;
55836
55800
  try {
55837
- const res = await fetch("/api/projects");
55838
- const data = await res.json();
55839
- project = data.projects.find((p) => p.encodedPath === encodedPath);
55801
+ project = await loadProject(encodedPath);
55840
55802
  } catch {
55841
55803
  return { kind: "home" };
55842
55804
  }
@@ -55848,9 +55810,7 @@ async function restoreFromHash() {
55848
55810
  return { kind: "subagent", project, sessionId, agentId: parts[3], presenting: false };
55849
55811
  }
55850
55812
  try {
55851
- const res = await fetch(`/api/projects/${encodedPath}/sessions`);
55852
- const data = await res.json();
55853
- const session = data.sessions.find((s2) => s2.sessionId === sessionId);
55813
+ const session = await loadProjectSession(project, sessionId);
55854
55814
  if (session) {
55855
55815
  return { kind: "session", project, session, presenting: false };
55856
55816
  }
@@ -55881,111 +55841,328 @@ function getHeaderInfo(view) {
55881
55841
  }
55882
55842
  return { title: "Klovi", breadcrumb: "" };
55883
55843
  }
55884
- function getSidebarContent(view, selectProject, hiddenIds, hide, goHidden, selectSession, goHome) {
55844
+
55845
+ // src/frontend/hooks/useViewState.ts
55846
+ function useViewState() {
55847
+ const [view, setView] = import_react17.useState({ kind: "home" });
55848
+ const [ready, setReady] = import_react17.useState(false);
55849
+ import_react17.useEffect(() => {
55850
+ restoreFromHash().then((v2) => {
55851
+ setView(v2);
55852
+ setReady(true);
55853
+ });
55854
+ }, []);
55855
+ import_react17.useEffect(() => {
55856
+ if (!ready)
55857
+ return;
55858
+ const newHash = viewToHash(view);
55859
+ if (window.location.hash !== newHash) {
55860
+ history.pushState(null, "", newHash);
55861
+ }
55862
+ }, [view, ready]);
55863
+ import_react17.useEffect(() => {
55864
+ const handler = () => {
55865
+ restoreFromHash().then(setView);
55866
+ };
55867
+ window.addEventListener("popstate", handler);
55868
+ return () => window.removeEventListener("popstate", handler);
55869
+ }, []);
55870
+ const selectProject = import_react17.useCallback((project) => {
55871
+ setView({ kind: "project", project });
55872
+ }, []);
55873
+ const selectSession = import_react17.useCallback((session) => {
55874
+ setView((current) => {
55875
+ if (current.kind === "project" || current.kind === "session") {
55876
+ return {
55877
+ kind: "session",
55878
+ project: current.project,
55879
+ session,
55880
+ presenting: false
55881
+ };
55882
+ }
55883
+ return current;
55884
+ });
55885
+ }, []);
55886
+ const goHome = import_react17.useCallback(() => setView({ kind: "home" }), []);
55887
+ const goHidden = import_react17.useCallback(() => setView({ kind: "hidden" }), []);
55888
+ const canPresent = view.kind === "session" || view.kind === "subagent";
55889
+ const togglePresentation = import_react17.useCallback(() => {
55890
+ setView((current) => {
55891
+ if (current.kind === "session" || current.kind === "subagent") {
55892
+ return { ...current, presenting: !current.presenting };
55893
+ }
55894
+ return current;
55895
+ });
55896
+ }, []);
55897
+ return {
55898
+ view,
55899
+ ready,
55900
+ setView,
55901
+ selectProject,
55902
+ selectSession,
55903
+ goHome,
55904
+ goHidden,
55905
+ canPresent,
55906
+ togglePresentation
55907
+ };
55908
+ }
55909
+
55910
+ // src/frontend/components/project/ProjectList.tsx
55911
+ var import_react18 = __toESM(require_react(), 1);
55912
+ var jsx_dev_runtime26 = __toESM(require_jsx_dev_runtime(), 1);
55913
+ function ProjectList({
55914
+ onSelect,
55915
+ selected,
55916
+ hiddenIds,
55917
+ onHide,
55918
+ onShowHidden
55919
+ }) {
55920
+ const { data, loading, error, retry } = useFetch("/api/projects", []);
55921
+ const [filter, setFilter] = import_react18.useState("");
55922
+ const projects = data?.projects ?? [];
55923
+ const filtered = projects.filter((p) => !hiddenIds.has(p.encodedPath) && (p.name.toLowerCase().includes(filter.toLowerCase()) || p.encodedPath.toLowerCase().includes(filter.toLowerCase())));
55924
+ if (loading)
55925
+ return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55926
+ className: "loading",
55927
+ children: "Loading projects..."
55928
+ }, undefined, false, undefined, this);
55929
+ if (error) {
55930
+ return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55931
+ className: "fetch-error",
55932
+ children: [
55933
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("span", {
55934
+ className: "fetch-error-message",
55935
+ children: error
55936
+ }, undefined, false, undefined, this),
55937
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("button", {
55938
+ type: "button",
55939
+ className: "btn btn-sm",
55940
+ onClick: retry,
55941
+ children: "Retry"
55942
+ }, undefined, false, undefined, this)
55943
+ ]
55944
+ }, undefined, true, undefined, this);
55945
+ }
55946
+ return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55947
+ children: [
55948
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("input", {
55949
+ className: "filter-input",
55950
+ placeholder: "Filter projects...",
55951
+ value: filter,
55952
+ onChange: (e) => setFilter(e.target.value)
55953
+ }, undefined, false, undefined, this),
55954
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55955
+ className: "list-section-title",
55956
+ children: [
55957
+ "Projects (",
55958
+ filtered.length,
55959
+ ")"
55960
+ ]
55961
+ }, undefined, true, undefined, this),
55962
+ filtered.map((project) => /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55963
+ className: `list-item list-item-with-action ${selected === project.encodedPath ? "active" : ""}`,
55964
+ onClick: () => onSelect(project),
55965
+ children: [
55966
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55967
+ className: "list-item-content",
55968
+ children: [
55969
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55970
+ className: "list-item-title",
55971
+ children: projectDisplayName(project)
55972
+ }, undefined, false, undefined, this),
55973
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
55974
+ className: "list-item-meta",
55975
+ children: [
55976
+ project.sessionCount,
55977
+ " session",
55978
+ project.sessionCount !== 1 ? "s" : "",
55979
+ " · ",
55980
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("time", {
55981
+ dateTime: project.lastActivity,
55982
+ title: formatFullDateTime(project.lastActivity),
55983
+ children: formatRelativeTime(project.lastActivity)
55984
+ }, undefined, false, undefined, this)
55985
+ ]
55986
+ }, undefined, true, undefined, this)
55987
+ ]
55988
+ }, undefined, true, undefined, this),
55989
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("button", {
55990
+ type: "button",
55991
+ className: "btn-hide",
55992
+ title: "Hide project",
55993
+ onClick: (e) => {
55994
+ e.stopPropagation();
55995
+ onHide(project.encodedPath);
55996
+ },
55997
+ children: "×"
55998
+ }, undefined, false, undefined, this)
55999
+ ]
56000
+ }, project.encodedPath, true, undefined, this)),
56001
+ filtered.length === 0 && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
56002
+ className: "empty-list-message",
56003
+ children: "No projects found"
56004
+ }, undefined, false, undefined, this),
56005
+ hiddenIds.size > 0 && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
56006
+ className: "hidden-projects-link",
56007
+ onClick: onShowHidden,
56008
+ children: [
56009
+ hiddenIds.size,
56010
+ " hidden project",
56011
+ hiddenIds.size !== 1 ? "s" : ""
56012
+ ]
56013
+ }, undefined, true, undefined, this)
56014
+ ]
56015
+ }, undefined, true, undefined, this);
56016
+ }
56017
+
56018
+ // src/frontend/components/project/SessionList.tsx
56019
+ var jsx_dev_runtime27 = __toESM(require_jsx_dev_runtime(), 1);
56020
+ function SessionList({ project, onSelect, onBack, selectedId }) {
56021
+ const { data, loading, error, retry } = useFetch(`/api/projects/${encodeURIComponent(project.encodedPath)}/sessions`, [project.encodedPath]);
56022
+ const sessions = data?.sessions ?? [];
56023
+ const parts = project.name.split("/").filter(Boolean);
56024
+ const displayName = parts.slice(-2).join("/");
56025
+ return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56026
+ children: [
56027
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56028
+ className: "back-btn",
56029
+ onClick: onBack,
56030
+ children: "← Projects"
56031
+ }, undefined, false, undefined, this),
56032
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56033
+ className: "list-section-title",
56034
+ children: displayName
56035
+ }, undefined, false, undefined, this),
56036
+ loading && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56037
+ className: "loading",
56038
+ children: "Loading sessions..."
56039
+ }, undefined, false, undefined, this),
56040
+ error && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56041
+ className: "fetch-error",
56042
+ children: [
56043
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("span", {
56044
+ className: "fetch-error-message",
56045
+ children: error
56046
+ }, undefined, false, undefined, this),
56047
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("button", {
56048
+ type: "button",
56049
+ className: "btn btn-sm",
56050
+ onClick: retry,
56051
+ children: "Retry"
56052
+ }, undefined, false, undefined, this)
56053
+ ]
56054
+ }, undefined, true, undefined, this),
56055
+ !loading && !error && sessions.map((session) => /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56056
+ className: `list-item ${selectedId === session.sessionId ? "active" : ""}${session.sessionType ? ` ${session.sessionType}` : ""}`,
56057
+ onClick: () => onSelect(session),
56058
+ children: [
56059
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56060
+ className: "list-item-title",
56061
+ children: session.firstMessage || session.slug
56062
+ }, undefined, false, undefined, this),
56063
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56064
+ className: "list-item-meta",
56065
+ children: [
56066
+ session.pluginId && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("span", {
56067
+ className: "plugin-badge",
56068
+ children: pluginDisplayName(session.pluginId)
56069
+ }, undefined, false, undefined, this),
56070
+ " ",
56071
+ session.sessionType && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("span", {
56072
+ className: `session-type-badge ${session.sessionType}`,
56073
+ children: session.sessionType === "plan" ? "Plan" : "Impl"
56074
+ }, undefined, false, undefined, this),
56075
+ " ",
56076
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("time", {
56077
+ dateTime: session.timestamp,
56078
+ title: formatFullDateTime(session.timestamp),
56079
+ children: formatTime(session.timestamp)
56080
+ }, undefined, false, undefined, this)
56081
+ ]
56082
+ }, undefined, true, undefined, this)
56083
+ ]
56084
+ }, session.sessionId, true, undefined, this)),
56085
+ !loading && !error && sessions.length === 0 && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56086
+ className: "empty-list-message",
56087
+ children: "No sessions found"
56088
+ }, undefined, false, undefined, this)
56089
+ ]
56090
+ }, undefined, true, undefined, this);
56091
+ }
56092
+
56093
+ // src/frontend/sidebar-content.tsx
56094
+ var jsx_dev_runtime28 = __toESM(require_jsx_dev_runtime(), 1);
56095
+ function getSidebarContent(view, hiddenIds, actions) {
55885
56096
  if (view.kind === "home" || view.kind === "hidden") {
55886
- return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(ProjectList, {
55887
- onSelect: selectProject,
56097
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(ProjectList, {
56098
+ onSelect: actions.selectProject,
55888
56099
  hiddenIds,
55889
- onHide: hide,
55890
- onShowHidden: goHidden
56100
+ onHide: actions.hide,
56101
+ onShowHidden: actions.goHidden
55891
56102
  }, undefined, false, undefined, this);
55892
56103
  }
55893
56104
  if (view.kind === "project") {
55894
- return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(SessionList, {
56105
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(SessionList, {
55895
56106
  project: view.project,
55896
- onSelect: selectSession,
55897
- onBack: goHome
56107
+ onSelect: actions.selectSession,
56108
+ onBack: actions.goHome
55898
56109
  }, undefined, false, undefined, this);
55899
56110
  }
55900
56111
  if (view.kind === "subagent") {
55901
- return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(SessionList, {
56112
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(SessionList, {
55902
56113
  project: view.project,
55903
- onSelect: selectSession,
55904
- onBack: goHome,
56114
+ onSelect: actions.selectSession,
56115
+ onBack: actions.goHome,
55905
56116
  selectedId: view.sessionId
55906
56117
  }, undefined, false, undefined, this);
55907
56118
  }
55908
- return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(SessionList, {
56119
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(SessionList, {
55909
56120
  project: view.project,
55910
- onSelect: selectSession,
55911
- onBack: goHome,
56121
+ onSelect: actions.selectSession,
56122
+ onBack: actions.goHome,
55912
56123
  selectedId: view.session.sessionId
55913
56124
  }, undefined, false, undefined, this);
55914
56125
  }
56126
+
56127
+ // src/frontend/App.tsx
56128
+ var jsx_dev_runtime29 = __toESM(require_jsx_dev_runtime(), 1);
55915
56129
  function App() {
55916
56130
  const { setting: themeSetting, cycle: cycleTheme } = useTheme();
55917
56131
  const { size: fontSize, increase, decrease } = useFontSize();
55918
56132
  const { hiddenIds, hide, unhide } = useHiddenProjects();
55919
- const [view, setView] = import_react18.useState({ kind: "home" });
55920
- const [ready, setReady] = import_react18.useState(false);
55921
- const [searchOpen, setSearchOpen] = import_react18.useState(false);
55922
- const [searchSessions, setSearchSessions] = import_react18.useState([]);
55923
- import_react18.useEffect(() => {
55924
- restoreFromHash().then((v2) => {
55925
- setView(v2);
55926
- setReady(true);
55927
- });
55928
- }, []);
55929
- import_react18.useEffect(() => {
55930
- if (!ready)
55931
- return;
55932
- const newHash = viewToHash(view);
55933
- if (window.location.hash !== newHash) {
55934
- history.pushState(null, "", newHash);
55935
- }
55936
- }, [view, ready]);
55937
- import_react18.useEffect(() => {
55938
- const handler = () => {
55939
- restoreFromHash().then(setView);
55940
- };
55941
- window.addEventListener("popstate", handler);
55942
- return () => window.removeEventListener("popstate", handler);
55943
- }, []);
55944
- const selectProject = (project) => {
55945
- setView({ kind: "project", project });
55946
- };
55947
- const selectSession = (session) => {
55948
- if (view.kind === "project" || view.kind === "session") {
55949
- setView({
55950
- kind: "session",
55951
- project: view.project,
55952
- session,
55953
- presenting: false
55954
- });
55955
- }
55956
- };
55957
- const goHome = () => setView({ kind: "home" });
55958
- const goHidden = () => setView({ kind: "hidden" });
55959
- const canPresent = view.kind === "session" || view.kind === "subagent";
55960
- const togglePresentation = import_react18.useCallback(() => {
55961
- if (view.kind === "session" || view.kind === "subagent") {
55962
- setView({ ...view, presenting: !view.presenting });
55963
- }
55964
- }, [view]);
55965
- const fetchSearchSessions = import_react18.useCallback(() => {
56133
+ const {
56134
+ view,
56135
+ ready,
56136
+ setView,
56137
+ selectProject,
56138
+ selectSession,
56139
+ goHome,
56140
+ goHidden,
56141
+ canPresent,
56142
+ togglePresentation
56143
+ } = useViewState();
56144
+ const [searchOpen, setSearchOpen] = import_react19.useState(false);
56145
+ const [searchSessions, setSearchSessions] = import_react19.useState([]);
56146
+ const fetchSearchSessions = import_react19.useCallback(() => {
55966
56147
  fetch("/api/search/sessions").then((res) => res.json()).then((data) => setSearchSessions(data.sessions)).catch(() => {});
55967
56148
  }, []);
55968
- const openSearch = import_react18.useCallback(() => {
56149
+ const openSearch = import_react19.useCallback(() => {
55969
56150
  setSearchOpen(true);
55970
56151
  fetchSearchSessions();
55971
56152
  }, [fetchSearchSessions]);
55972
- const handleSearchSelect = import_react18.useCallback(async (encodedPath, sessionId) => {
56153
+ const handleSearchSelect = import_react19.useCallback(async (encodedPath, sessionId) => {
55973
56154
  setSearchOpen(false);
55974
- try {
55975
- const [projectsRes, sessionsRes] = await Promise.all([
55976
- fetch("/api/projects"),
55977
- fetch(`/api/projects/${encodedPath}/sessions`)
55978
- ]);
55979
- const projectsData = await projectsRes.json();
55980
- const sessionsData = await sessionsRes.json();
55981
- const project = projectsData.projects.find((p) => p.encodedPath === encodedPath);
55982
- const session = sessionsData.sessions.find((s2) => s2.sessionId === sessionId);
55983
- if (project && session) {
55984
- setView({ kind: "session", project, session, presenting: false });
55985
- }
55986
- } catch {}
55987
- }, []);
55988
- import_react18.useEffect(() => {
56155
+ const resolved = await resolveProjectAndSession(encodedPath, sessionId);
56156
+ if (resolved) {
56157
+ setView({
56158
+ kind: "session",
56159
+ project: resolved.project,
56160
+ session: resolved.session,
56161
+ presenting: false
56162
+ });
56163
+ }
56164
+ }, [setView]);
56165
+ import_react19.useEffect(() => {
55989
56166
  function handleCmdK(e) {
55990
56167
  if ((e.metaKey || e.ctrlKey) && e.key === "k") {
55991
56168
  e.preventDefault();
@@ -55999,7 +56176,7 @@ function App() {
55999
56176
  window.addEventListener("keydown", handleCmdK);
56000
56177
  return () => window.removeEventListener("keydown", handleCmdK);
56001
56178
  }, [fetchSearchSessions]);
56002
- import_react18.useEffect(() => {
56179
+ import_react19.useEffect(() => {
56003
56180
  function handleKeyDown(e) {
56004
56181
  if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)
56005
56182
  return;
@@ -56027,30 +56204,36 @@ function App() {
56027
56204
  return () => window.removeEventListener("keydown", handleKeyDown);
56028
56205
  }, [canPresent, togglePresentation, increase, decrease]);
56029
56206
  const { title: headerTitle, breadcrumb } = getHeaderInfo(view);
56030
- const sidebarContent = getSidebarContent(view, selectProject, hiddenIds, hide, goHidden, selectSession, goHome);
56031
- const isPresenting = canPresent && view.presenting;
56207
+ const sidebarContent = getSidebarContent(view, hiddenIds, {
56208
+ selectProject,
56209
+ selectSession,
56210
+ goHome,
56211
+ goHidden,
56212
+ hide
56213
+ });
56214
+ const isPresenting = view.kind === "session" || view.kind === "subagent" ? view.presenting : false;
56032
56215
  if (!ready) {
56033
- return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56216
+ return /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
56034
56217
  className: "loading",
56035
56218
  children: "Loading..."
56036
56219
  }, undefined, false, undefined, this);
56037
56220
  }
56038
- return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(jsx_dev_runtime27.Fragment, {
56221
+ return /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(jsx_dev_runtime29.Fragment, {
56039
56222
  children: [
56040
- searchOpen && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(SearchModal, {
56223
+ searchOpen && /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SearchModal, {
56041
56224
  sessions: searchSessions,
56042
56225
  onSelect: handleSearchSelect,
56043
56226
  onClose: () => setSearchOpen(false)
56044
56227
  }, undefined, false, undefined, this),
56045
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Layout, {
56228
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(Layout, {
56046
56229
  sidebar: sidebarContent,
56047
56230
  hideSidebar: isPresenting,
56048
56231
  onSearchClick: openSearch,
56049
56232
  children: [
56050
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Header, {
56233
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(Header, {
56051
56234
  title: headerTitle,
56052
56235
  breadcrumb,
56053
- copyCommand: view.kind === "session" && isClaudeModel(view.session.model) ? `claude --resume ${view.session.sessionId}` : undefined,
56236
+ copyCommand: view.kind === "session" ? getResumeCommand(view.session.pluginId, view.session.sessionId) : undefined,
56054
56237
  backHref: view.kind === "subagent" ? `#/${view.project.encodedPath}/${view.sessionId}` : undefined,
56055
56238
  sessionType: view.kind === "session" ? view.session.sessionType : undefined,
56056
56239
  themeSetting,
@@ -56062,63 +56245,64 @@ function App() {
56062
56245
  onTogglePresentation: togglePresentation,
56063
56246
  showPresentationToggle: canPresent
56064
56247
  }, undefined, false, undefined, this),
56065
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(ErrorBoundary, {
56248
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(ErrorBoundary, {
56066
56249
  children: [
56067
- view.kind === "home" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(jsx_dev_runtime27.Fragment, {
56250
+ view.kind === "home" && /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(jsx_dev_runtime29.Fragment, {
56068
56251
  children: [
56069
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56252
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
56070
56253
  className: "empty-state",
56071
56254
  children: [
56072
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("img", {
56255
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("img", {
56073
56256
  src: favicon_default,
56074
56257
  alt: "",
56075
56258
  width: "64",
56076
56259
  height: "64",
56077
56260
  className: "empty-state-logo"
56078
56261
  }, undefined, false, undefined, this),
56079
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56262
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
56080
56263
  className: "empty-state-title",
56081
56264
  children: "Welcome to Klovi"
56082
56265
  }, undefined, false, undefined, this),
56083
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("p", {
56266
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("p", {
56084
56267
  children: "Select a project from the sidebar to browse your AI coding sessions"
56085
56268
  }, undefined, false, undefined, this)
56086
56269
  ]
56087
56270
  }, undefined, true, undefined, this),
56088
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(DashboardStats, {}, undefined, false, undefined, this)
56271
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(DashboardStats, {}, undefined, false, undefined, this)
56089
56272
  ]
56090
56273
  }, undefined, true, undefined, this),
56091
- view.kind === "hidden" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(HiddenProjectList, {
56274
+ view.kind === "hidden" && /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(HiddenProjectList, {
56092
56275
  hiddenIds,
56093
56276
  onUnhide: unhide,
56094
56277
  onBack: goHome
56095
56278
  }, undefined, false, undefined, this),
56096
- view.kind === "project" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56279
+ view.kind === "project" && /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
56097
56280
  className: "empty-state",
56098
56281
  children: [
56099
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("div", {
56282
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
56100
56283
  className: "empty-state-title",
56101
56284
  children: "Select a session"
56102
56285
  }, undefined, false, undefined, this),
56103
- /* @__PURE__ */ jsx_dev_runtime27.jsxDEV("p", {
56286
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("p", {
56104
56287
  children: "Choose a conversation from the sidebar"
56105
56288
  }, undefined, false, undefined, this)
56106
56289
  ]
56107
56290
  }, undefined, true, undefined, this),
56108
- view.kind === "session" && (view.presenting ? /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(SessionPresentation, {
56291
+ view.kind === "session" && (view.presenting ? /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SessionPresentation, {
56109
56292
  sessionId: view.session.sessionId,
56110
56293
  project: view.project.encodedPath,
56111
56294
  onExit: togglePresentation
56112
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(SessionView, {
56295
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SessionView, {
56113
56296
  sessionId: view.session.sessionId,
56114
- project: view.project.encodedPath
56297
+ project: view.project.encodedPath,
56298
+ gitBranch: view.session.gitBranch
56115
56299
  }, undefined, false, undefined, this)),
56116
- view.kind === "subagent" && (view.presenting ? /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(SubAgentPresentation, {
56300
+ view.kind === "subagent" && (view.presenting ? /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SubAgentPresentation, {
56117
56301
  sessionId: view.sessionId,
56118
56302
  project: view.project.encodedPath,
56119
56303
  agentId: view.agentId,
56120
56304
  onExit: togglePresentation
56121
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(SubAgentView, {
56305
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SubAgentView, {
56122
56306
  sessionId: view.sessionId,
56123
56307
  project: view.project.encodedPath,
56124
56308
  agentId: view.agentId
@@ -56131,4 +56315,4 @@ function App() {
56131
56315
  }, undefined, true, undefined, this);
56132
56316
  }
56133
56317
  var root4 = import_client.createRoot(document.getElementById("root"));
56134
- root4.render(/* @__PURE__ */ jsx_dev_runtime27.jsxDEV(App, {}, undefined, false, undefined, this));
56318
+ root4.render(/* @__PURE__ */ jsx_dev_runtime29.jsxDEV(App, {}, undefined, false, undefined, this));