@cookielab.io/klovi 1.0.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_react16 = __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: {
@@ -54061,6 +54151,9 @@ function DiffView({ filePath, oldString, newString }) {
54061
54151
  }, undefined, true, undefined, this);
54062
54152
  }
54063
54153
 
54154
+ // src/frontend/components/message/SmartToolOutput.tsx
54155
+ var import_react11 = __toESM(require_react(), 1);
54156
+
54064
54157
  // src/frontend/utils/format-detector.ts
54065
54158
  function detectOutputFormat(output) {
54066
54159
  const trimmed = output.trim();
@@ -54155,68 +54248,103 @@ function hasCodeStructure(text7) {
54155
54248
  return lines.length >= 2 && /^(export|import|const|let|var|function|class)\s/.test(text7);
54156
54249
  }
54157
54250
 
54251
+ // src/frontend/components/ui/ImageLightbox.tsx
54252
+ var import_react10 = __toESM(require_react(), 1);
54253
+ var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
54254
+ function ImageLightbox({ src, onClose }) {
54255
+ const [visible, setVisible] = import_react10.useState(false);
54256
+ const handleClose = import_react10.useCallback(() => {
54257
+ setVisible(false);
54258
+ setTimeout(onClose, 200);
54259
+ }, [onClose]);
54260
+ import_react10.useEffect(() => {
54261
+ requestAnimationFrame(() => {
54262
+ requestAnimationFrame(() => setVisible(true));
54263
+ });
54264
+ }, []);
54265
+ import_react10.useEffect(() => {
54266
+ const handleKey = (e) => {
54267
+ if (e.key === "Escape")
54268
+ handleClose();
54269
+ };
54270
+ document.addEventListener("keydown", handleKey);
54271
+ return () => document.removeEventListener("keydown", handleKey);
54272
+ }, [handleClose]);
54273
+ return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("div", {
54274
+ className: `lightbox-overlay ${visible ? "lightbox-visible" : ""}`,
54275
+ onClick: handleClose,
54276
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("img", {
54277
+ className: "lightbox-image",
54278
+ src,
54279
+ alt: "Full size preview"
54280
+ }, undefined, false, undefined, this)
54281
+ }, undefined, false, undefined, this);
54282
+ }
54283
+
54158
54284
  // src/frontend/components/message/SmartToolOutput.tsx
54159
- var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
54285
+ var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
54160
54286
  function SmartToolOutput({ output, isError, resultImages }) {
54161
54287
  const truncated = truncateOutput(output);
54162
54288
  const wasTruncated = output.length > MAX_OUTPUT_LENGTH;
54163
54289
  const detectedLang = truncated ? detectOutputFormat(truncated) : null;
54290
+ const [lightboxSrc, setLightboxSrc] = import_react11.useState(null);
54291
+ const closeLightbox = import_react11.useCallback(() => setLightboxSrc(null), []);
54164
54292
  if (!output && (!resultImages || resultImages.length === 0))
54165
54293
  return null;
54166
- return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
54294
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54167
54295
  children: [
54168
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
54296
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54169
54297
  className: "tool-section-label",
54170
54298
  children: "Output"
54171
54299
  }, undefined, false, undefined, this),
54172
- output && (detectedLang && !isError ? /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(CodeBlock, {
54300
+ output && (detectedLang && !isError ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(CodeBlock, {
54173
54301
  language: detectedLang,
54174
54302
  children: truncated
54175
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
54303
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54176
54304
  className: `tool-call-output ${isError ? "tool-call-error" : ""}`,
54177
54305
  children: truncated
54178
54306
  }, undefined, false, undefined, this)),
54179
- wasTruncated && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
54307
+ wasTruncated && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54180
54308
  className: "tool-call-truncated",
54181
54309
  children: "... (truncated)"
54182
54310
  }, undefined, false, undefined, this),
54183
- resultImages && resultImages.length > 0 && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
54311
+ resultImages && resultImages.length > 0 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54184
54312
  className: "tool-result-images",
54185
- children: resultImages.map((img, i) => /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("a", {
54186
- href: `data:${img.mediaType};base64,${img.data}`,
54187
- target: "_blank",
54188
- rel: "noopener noreferrer",
54189
- children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("img", {
54190
- className: "tool-result-image",
54191
- src: `data:${img.mediaType};base64,${img.data}`,
54192
- alt: `Tool result ${i + 1}`
54193
- }, undefined, false, undefined, this)
54313
+ children: resultImages.map((img, i) => /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("img", {
54314
+ className: "tool-result-image",
54315
+ src: `data:${img.mediaType};base64,${img.data}`,
54316
+ alt: `Tool result ${i + 1}`,
54317
+ onClick: () => setLightboxSrc(`data:${img.mediaType};base64,${img.data}`)
54194
54318
  }, i, false, undefined, this))
54319
+ }, undefined, false, undefined, this),
54320
+ lightboxSrc && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ImageLightbox, {
54321
+ src: lightboxSrc,
54322
+ onClose: closeLightbox
54195
54323
  }, undefined, false, undefined, this)
54196
54324
  ]
54197
54325
  }, undefined, true, undefined, this);
54198
54326
  }
54199
54327
 
54200
54328
  // src/frontend/components/message/BashToolContent.tsx
54201
- var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
54329
+ var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
54202
54330
  function BashToolContent({ call }) {
54203
54331
  const command = String(call.input.command || "");
54204
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(jsx_dev_runtime11.Fragment, {
54332
+ return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
54205
54333
  children: [
54206
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("div", {
54334
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54207
54335
  style: { marginBottom: 8 },
54208
54336
  children: [
54209
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV("div", {
54337
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54210
54338
  className: "tool-section-label",
54211
54339
  children: "Command"
54212
54340
  }, undefined, false, undefined, this),
54213
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(CodeBlock, {
54341
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(CodeBlock, {
54214
54342
  language: "bash",
54215
54343
  children: command
54216
54344
  }, undefined, false, undefined, this)
54217
54345
  ]
54218
54346
  }, undefined, true, undefined, this),
54219
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(SmartToolOutput, {
54347
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(SmartToolOutput, {
54220
54348
  output: call.result,
54221
54349
  isError: call.isError,
54222
54350
  resultImages: call.resultImages
@@ -54226,38 +54354,46 @@ function BashToolContent({ call }) {
54226
54354
  }
54227
54355
 
54228
54356
  // src/frontend/components/message/ToolCall.tsx
54229
- var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
54357
+ var jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
54230
54358
  var MAX_OUTPUT_LENGTH = 5000;
54231
54359
  var MAX_CONTENT_LENGTH = 2000;
54232
54360
  var MAX_THINKING_PREVIEW = 100;
54233
54361
  function isEditWithDiff(call) {
54234
54362
  return call.name === "Edit" && typeof call.input.old_string === "string" && typeof call.input.new_string === "string";
54235
54363
  }
54236
- function isJsonFallbackInput(call) {
54237
- 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);
54238
54374
  }
54239
- function DefaultToolContent({ call }) {
54240
- const formattedInput = formatToolInput(call);
54241
- const jsonInput = isJsonFallbackInput(call);
54242
- return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(jsx_dev_runtime12.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, {
54243
54379
  children: [
54244
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54380
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54245
54381
  style: { marginBottom: 8 },
54246
54382
  children: [
54247
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54383
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54248
54384
  className: "tool-section-label",
54249
54385
  children: "Input"
54250
54386
  }, undefined, false, undefined, this),
54251
- jsonInput ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(CodeBlock, {
54387
+ jsonInput ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(CodeBlock, {
54252
54388
  language: "json",
54253
54389
  children: formattedInput
54254
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54390
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54255
54391
  className: "tool-call-input",
54256
54392
  children: formattedInput
54257
54393
  }, undefined, false, undefined, this)
54258
54394
  ]
54259
54395
  }, undefined, true, undefined, this),
54260
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(SmartToolOutput, {
54396
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(SmartToolOutput, {
54261
54397
  output: call.result,
54262
54398
  isError: call.isError,
54263
54399
  resultImages: call.resultImages
@@ -54265,41 +54401,41 @@ function DefaultToolContent({ call }) {
54265
54401
  ]
54266
54402
  }, undefined, true, undefined, this);
54267
54403
  }
54268
- function ToolCall({ call, sessionId, project }) {
54269
- const summary = getToolSummary(call);
54404
+ function ToolCall({ call, sessionId, project, pluginId }) {
54405
+ const summary = getToolSummary(call, pluginId);
54270
54406
  const mcpServer = getMcpServer(call.name);
54271
54407
  const skillName = getSkillName(call);
54272
54408
  const hasSubAgent = call.name === "Task" && call.subAgentId && sessionId && project;
54273
54409
  const displayName = hasSubAgent ? "Sub-Agent" : mcpServer ? call.name.split("__").slice(1).join("__").replace(/__/g, " > ") : skillName ?? call.name;
54274
- return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("div", {
54410
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54275
54411
  className: "tool-call",
54276
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(CollapsibleSection, {
54277
- title: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("span", {
54412
+ children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(CollapsibleSection, {
54413
+ title: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54278
54414
  children: [
54279
- mcpServer && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("span", {
54415
+ mcpServer && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54280
54416
  className: "tool-mcp-server",
54281
54417
  children: mcpServer
54282
54418
  }, undefined, false, undefined, this),
54283
- skillName && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("span", {
54419
+ skillName && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54284
54420
  className: "tool-skill-badge",
54285
54421
  children: "skill"
54286
54422
  }, undefined, false, undefined, this),
54287
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("span", {
54423
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54288
54424
  className: "tool-call-name",
54289
54425
  children: displayName
54290
54426
  }, undefined, false, undefined, this),
54291
- summary && !skillName && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("span", {
54427
+ summary && !skillName && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54292
54428
  className: "tool-call-summary",
54293
54429
  children: [
54294
54430
  " — ",
54295
54431
  summary
54296
54432
  ]
54297
54433
  }, undefined, true, undefined, this),
54298
- call.isError && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("span", {
54434
+ call.isError && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54299
54435
  className: "tool-call-error",
54300
54436
  children: " (error)"
54301
54437
  }, undefined, false, undefined, this),
54302
- hasSubAgent && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV("a", {
54438
+ hasSubAgent && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("a", {
54303
54439
  className: "subagent-link",
54304
54440
  href: `#/${project}/${sessionId}/subagent/${call.subAgentId}`,
54305
54441
  onClick: (e) => e.stopPropagation(),
@@ -54307,14 +54443,15 @@ function ToolCall({ call, sessionId, project }) {
54307
54443
  }, undefined, false, undefined, this)
54308
54444
  ]
54309
54445
  }, undefined, true, undefined, this),
54310
- children: isEditWithDiff(call) ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(DiffView, {
54446
+ children: isEditWithDiff(call) ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(DiffView, {
54311
54447
  filePath: String(call.input.file_path || ""),
54312
54448
  oldString: String(call.input.old_string),
54313
54449
  newString: String(call.input.new_string)
54314
- }, undefined, false, undefined, this) : call.name === "Bash" ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(BashToolContent, {
54315
- call
54316
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(DefaultToolContent, {
54450
+ }, undefined, false, undefined, this) : call.name === "Bash" ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(BashToolContent, {
54317
54451
  call
54452
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(DefaultToolContent, {
54453
+ call,
54454
+ pluginId
54318
54455
  }, undefined, false, undefined, this)
54319
54456
  }, undefined, false, undefined, this)
54320
54457
  }, undefined, false, undefined, this);
@@ -54333,7 +54470,7 @@ function getSkillName(call) {
54333
54470
  function getAskUserQuestionSummary(input) {
54334
54471
  if (Array.isArray(input.questions) && input.questions.length > 0) {
54335
54472
  const q2 = input.questions[0];
54336
- return truncate(String(q2.question || ""), 60);
54473
+ return truncate2(String(q2.question || ""), 60);
54337
54474
  }
54338
54475
  return "";
54339
54476
  }
@@ -54342,16 +54479,16 @@ var fileSummaryExtractors = {
54342
54479
  Write: (i) => String(i.file_path || ""),
54343
54480
  Edit: (i) => String(i.file_path || ""),
54344
54481
  Glob: (i) => String(i.pattern || ""),
54345
- Grep: (i) => truncate(String(i.pattern || ""), 60),
54482
+ Grep: (i) => truncate2(String(i.pattern || ""), 60),
54346
54483
  NotebookEdit: (i) => String(i.notebook_path || ""),
54347
54484
  NotebookRead: (i) => String(i.notebook_path || "")
54348
54485
  };
54349
54486
  var shellSummaryExtractors = {
54350
- Bash: (i) => truncate(String(i.command || ""), 80)
54487
+ Bash: (i) => truncate2(String(i.command || ""), 80)
54351
54488
  };
54352
54489
  var agentSummaryExtractors = {
54353
- Task: (i) => truncate(String(i.description || ""), 60),
54354
- TaskCreate: (i) => truncate(String(i.subject || ""), 60),
54490
+ Task: (i) => truncate2(String(i.description || ""), 60),
54491
+ TaskCreate: (i) => truncate2(String(i.subject || ""), 60),
54355
54492
  TaskUpdate: (i) => `#${i.taskId || "?"}${i.status ? ` → ${i.status}` : ""}`,
54356
54493
  TaskList: () => "List all tasks",
54357
54494
  TaskGet: (i) => `#${i.taskId || "?"}`,
@@ -54360,11 +54497,11 @@ var agentSummaryExtractors = {
54360
54497
  KillShell: (i) => String(i.task_id || i.shell_id || ""),
54361
54498
  EnterPlanMode: () => "Enter plan mode",
54362
54499
  ExitPlanMode: () => "Exit plan mode",
54363
- TodoWrite: (i) => truncate(String(i.subject || ""), 60)
54500
+ TodoWrite: (i) => truncate2(String(i.subject || ""), 60)
54364
54501
  };
54365
54502
  var webSummaryExtractors = {
54366
- WebFetch: (i) => truncate(String(i.url || ""), 60),
54367
- WebSearch: (i) => truncate(String(i.query || ""), 60)
54503
+ WebFetch: (i) => truncate2(String(i.url || ""), 60),
54504
+ WebSearch: (i) => truncate2(String(i.query || ""), 60)
54368
54505
  };
54369
54506
  var interactionSummaryExtractors = {
54370
54507
  AskUserQuestion: (i) => getAskUserQuestionSummary(i),
@@ -54377,7 +54514,13 @@ var SUMMARY_EXTRACTORS = {
54377
54514
  ...webSummaryExtractors,
54378
54515
  ...interactionSummaryExtractors
54379
54516
  };
54380
- 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
+ }
54381
54524
  if (call.name.startsWith("mcp__")) {
54382
54525
  return call.name.split("__").slice(2).join(" > ") || "";
54383
54526
  }
@@ -54413,7 +54556,7 @@ function formatWriteInput(input) {
54413
54556
  parts.push(`File: ${input.file_path}`);
54414
54557
  if (input.content)
54415
54558
  parts.push(`Content:
54416
- ${truncate(String(input.content), MAX_CONTENT_LENGTH)}`);
54559
+ ${truncate2(String(input.content), MAX_CONTENT_LENGTH)}`);
54417
54560
  return parts.join(`
54418
54561
 
54419
54562
  `);
@@ -54452,7 +54595,7 @@ function formatNotebookEditInput(input) {
54452
54595
  parts.push(`Mode: ${input.edit_mode}`);
54453
54596
  if (input.new_source)
54454
54597
  parts.push(`Source:
54455
- ${truncate(String(input.new_source), MAX_CONTENT_LENGTH)}`);
54598
+ ${truncate2(String(input.new_source), MAX_CONTENT_LENGTH)}`);
54456
54599
  return parts.join(`
54457
54600
  `) || JSON.stringify(input, null, 2);
54458
54601
  }
@@ -54513,11 +54656,17 @@ var INPUT_FORMATTERS = {
54513
54656
  ...agentInputFormatters,
54514
54657
  ...interactionInputFormatters
54515
54658
  };
54516
- 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
+ }
54517
54666
  const formatter = INPUT_FORMATTERS[call.name];
54518
54667
  return formatter ? formatter(call.input) : JSON.stringify(call.input, null, 2);
54519
54668
  }
54520
- function truncate(s2, max) {
54669
+ function truncate2(s2, max) {
54521
54670
  if (s2.length <= max)
54522
54671
  return s2;
54523
54672
  return `${s2.slice(0, max)}...`;
@@ -54529,28 +54678,28 @@ function truncateOutput(s2) {
54529
54678
  }
54530
54679
 
54531
54680
  // src/frontend/components/message/ThinkingBlock.tsx
54532
- var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
54681
+ var jsx_dev_runtime15 = __toESM(require_jsx_dev_runtime(), 1);
54533
54682
  function ThinkingBlock({ block }) {
54534
54683
  const preview = block.text.length > MAX_THINKING_PREVIEW ? `${block.text.slice(0, MAX_THINKING_PREVIEW)}...` : block.text;
54535
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54684
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54536
54685
  className: "thinking-block",
54537
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(CollapsibleSection, {
54538
- title: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("span", {
54686
+ children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(CollapsibleSection, {
54687
+ title: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54539
54688
  children: [
54540
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("span", {
54689
+ /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54541
54690
  style: { color: "var(--text-muted)" },
54542
54691
  children: "Thinking:"
54543
54692
  }, undefined, false, undefined, this),
54544
54693
  " ",
54545
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("span", {
54694
+ /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54546
54695
  className: "tool-call-summary",
54547
54696
  children: preview
54548
54697
  }, undefined, false, undefined, this)
54549
54698
  ]
54550
54699
  }, undefined, true, undefined, this),
54551
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV("div", {
54700
+ children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54552
54701
  className: "thinking-content",
54553
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(MarkdownRenderer, {
54702
+ children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(MarkdownRenderer, {
54554
54703
  content: block.text
54555
54704
  }, undefined, false, undefined, this)
54556
54705
  }, undefined, false, undefined, this)
@@ -54559,42 +54708,43 @@ function ThinkingBlock({ block }) {
54559
54708
  }
54560
54709
 
54561
54710
  // src/frontend/components/message/AssistantMessage.tsx
54562
- var jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
54563
- function renderGroup(group, sessionId, project) {
54711
+ var jsx_dev_runtime16 = __toESM(require_jsx_dev_runtime(), 1);
54712
+ function renderGroup(group, sessionId, project, pluginId) {
54564
54713
  return group.map((block, i) => {
54565
54714
  if (block.type === "thinking") {
54566
- return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(ThinkingBlock, {
54715
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(ThinkingBlock, {
54567
54716
  block: block.block
54568
54717
  }, `thinking-${i}`, false, undefined, this);
54569
54718
  }
54570
54719
  if (block.type === "text") {
54571
- return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(MarkdownRenderer, {
54720
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MarkdownRenderer, {
54572
54721
  content: block.text
54573
54722
  }, `text-${i}`, false, undefined, this);
54574
54723
  }
54575
- return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(ToolCall, {
54724
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(ToolCall, {
54576
54725
  call: block.call,
54577
54726
  sessionId,
54578
- project
54727
+ project,
54728
+ pluginId
54579
54729
  }, `tool-${i}`, false, undefined, this);
54580
54730
  });
54581
54731
  }
54582
54732
  function UsageFooter({ usage }) {
54583
- return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54733
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54584
54734
  className: "token-usage",
54585
54735
  children: [
54586
54736
  usage.inputTokens.toLocaleString(),
54587
54737
  " in / ",
54588
54738
  usage.outputTokens.toLocaleString(),
54589
54739
  " out",
54590
- usage.cacheReadTokens && usage.cacheReadTokens > 0 && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54740
+ usage.cacheReadTokens && usage.cacheReadTokens > 0 && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54591
54741
  children: [
54592
54742
  " · ",
54593
54743
  usage.cacheReadTokens.toLocaleString(),
54594
54744
  " cache read"
54595
54745
  ]
54596
54746
  }, undefined, true, undefined, this),
54597
- usage.cacheCreationTokens && usage.cacheCreationTokens > 0 && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54747
+ usage.cacheCreationTokens && usage.cacheCreationTokens > 0 && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54598
54748
  children: [
54599
54749
  " · ",
54600
54750
  usage.cacheCreationTokens.toLocaleString(),
@@ -54608,7 +54758,8 @@ function AssistantMessage({
54608
54758
  turn,
54609
54759
  visibleSubSteps,
54610
54760
  sessionId,
54611
- project
54761
+ project,
54762
+ pluginId
54612
54763
  }) {
54613
54764
  const groups = groupContentBlocks(turn.contentBlocks);
54614
54765
  const limit = visibleSubSteps !== undefined ? visibleSubSteps : groups.length;
@@ -54619,21 +54770,21 @@ function AssistantMessage({
54619
54770
  const introGroup = hasNonText && firstIsText ? visibleGroups[0] : null;
54620
54771
  const treeGroups = hasNonText ? introGroup ? visibleGroups.slice(1) : visibleGroups : [];
54621
54772
  const flatGroups = hasNonText ? [] : visibleGroups;
54622
- return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54773
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54623
54774
  className: "turn",
54624
54775
  children: [
54625
- /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54776
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54626
54777
  className: "turn-header",
54627
54778
  children: [
54628
- /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54779
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54629
54780
  className: "turn-badge turn-badge-assistant",
54630
54781
  children: "Assistant"
54631
54782
  }, undefined, false, undefined, this),
54632
- turn.model && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("span", {
54783
+ turn.model && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54633
54784
  className: "turn-badge turn-badge-model",
54634
54785
  children: shortModel(turn.model)
54635
54786
  }, undefined, false, undefined, this),
54636
- turn.timestamp && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("time", {
54787
+ turn.timestamp && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("time", {
54637
54788
  className: "turn-timestamp",
54638
54789
  dateTime: turn.timestamp,
54639
54790
  "data-tooltip": formatFullDateTime(turn.timestamp),
@@ -54641,25 +54792,25 @@ function AssistantMessage({
54641
54792
  }, undefined, false, undefined, this)
54642
54793
  ]
54643
54794
  }, undefined, true, undefined, this),
54644
- /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54795
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54645
54796
  className: "message message-assistant",
54646
54797
  children: [
54647
- introGroup && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54798
+ introGroup && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54648
54799
  className: isPresentation && treeGroups.length === 0 ? "step-enter" : "",
54649
- children: renderGroup(introGroup, sessionId, project)
54800
+ children: renderGroup(introGroup, sessionId, project, pluginId)
54650
54801
  }, undefined, false, undefined, this),
54651
- treeGroups.length > 0 && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54802
+ treeGroups.length > 0 && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54652
54803
  className: "exec-tree",
54653
- children: treeGroups.map((group, i) => /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54804
+ children: treeGroups.map((group, i) => /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54654
54805
  className: `tree-node${isPresentation && i === treeGroups.length - 1 ? " step-enter" : ""}`,
54655
- children: renderGroup(group, sessionId, project)
54806
+ children: renderGroup(group, sessionId, project, pluginId)
54656
54807
  }, i, false, undefined, this))
54657
54808
  }, undefined, false, undefined, this),
54658
- flatGroups.map((group, i) => /* @__PURE__ */ jsx_dev_runtime14.jsxDEV("div", {
54809
+ flatGroups.map((group, i) => /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54659
54810
  className: isPresentation && i === flatGroups.length - 1 ? "step-enter" : "",
54660
- children: renderGroup(group, sessionId, project)
54811
+ children: renderGroup(group, sessionId, project, pluginId)
54661
54812
  }, i, false, undefined, this)),
54662
- turn.usage && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(UsageFooter, {
54813
+ turn.usage && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(UsageFooter, {
54663
54814
  usage: turn.usage
54664
54815
  }, undefined, false, undefined, this)
54665
54816
  ]
@@ -54669,7 +54820,7 @@ function AssistantMessage({
54669
54820
  }
54670
54821
 
54671
54822
  // src/frontend/components/message/UserMessage.tsx
54672
- var jsx_dev_runtime15 = __toESM(require_jsx_dev_runtime(), 1);
54823
+ var jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
54673
54824
  var STATUS_RE = /^\[.+\]$/;
54674
54825
  var PLAN_PREFIX = "Implement the following plan";
54675
54826
  function UserMessage({
@@ -54680,14 +54831,14 @@ function UserMessage({
54680
54831
  project
54681
54832
  }) {
54682
54833
  if (turn.bashInput !== undefined) {
54683
- return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54834
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54684
54835
  className: "status-notice bash-input-notice",
54685
54836
  children: [
54686
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54837
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54687
54838
  className: "bash-input-prompt",
54688
54839
  children: "$"
54689
54840
  }, undefined, false, undefined, this),
54690
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("code", {
54841
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("code", {
54691
54842
  className: "bash-input-command",
54692
54843
  children: turn.bashInput
54693
54844
  }, undefined, false, undefined, this)
@@ -54695,11 +54846,11 @@ function UserMessage({
54695
54846
  }, undefined, true, undefined, this);
54696
54847
  }
54697
54848
  if (turn.ideOpenedFile !== undefined) {
54698
- return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54849
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54699
54850
  className: "status-notice ide-opened-file-notice",
54700
54851
  children: [
54701
54852
  "Opened ",
54702
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("code", {
54853
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("code", {
54703
54854
  className: "ide-opened-file-path",
54704
54855
  children: turn.ideOpenedFile
54705
54856
  }, undefined, false, undefined, this)
@@ -54708,7 +54859,7 @@ function UserMessage({
54708
54859
  }
54709
54860
  const isStatus = STATUS_RE.test(turn.text.trim());
54710
54861
  if (isStatus) {
54711
- return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54862
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54712
54863
  className: "status-notice",
54713
54864
  children: turn.text
54714
54865
  }, undefined, false, undefined, this);
@@ -54718,27 +54869,27 @@ function UserMessage({
54718
54869
  const showImplLink = implSessionId && project && !isPlanMessage;
54719
54870
  const role = isSubAgent ? "Root Agent" : "User";
54720
54871
  const badgeClass = isSubAgent ? "turn-badge-agent" : "turn-badge-user";
54721
- return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54872
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54722
54873
  className: "turn",
54723
54874
  children: [
54724
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54875
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54725
54876
  className: "turn-header",
54726
54877
  children: [
54727
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54878
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54728
54879
  className: `turn-badge ${badgeClass}`,
54729
54880
  children: role
54730
54881
  }, undefined, false, undefined, this),
54731
- showPlanLink && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("a", {
54882
+ showPlanLink && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("a", {
54732
54883
  className: "subagent-link",
54733
54884
  href: `#/${project}/${planSessionId}`,
54734
54885
  children: "View planning session"
54735
54886
  }, undefined, false, undefined, this),
54736
- showImplLink && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("a", {
54887
+ showImplLink && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("a", {
54737
54888
  className: "subagent-link",
54738
54889
  href: `#/${project}/${implSessionId}`,
54739
54890
  children: "View implementation session"
54740
54891
  }, undefined, false, undefined, this),
54741
- turn.timestamp && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("time", {
54892
+ turn.timestamp && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("time", {
54742
54893
  className: "turn-timestamp",
54743
54894
  dateTime: turn.timestamp,
54744
54895
  "data-tooltip": formatFullDateTime(turn.timestamp),
@@ -54746,28 +54897,28 @@ function UserMessage({
54746
54897
  }, undefined, false, undefined, this)
54747
54898
  ]
54748
54899
  }, undefined, true, undefined, this),
54749
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54900
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54750
54901
  className: `message ${isSubAgent ? "message-root-agent" : "message-user"}`,
54751
54902
  children: [
54752
- turn.command && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54903
+ turn.command && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54753
54904
  className: "command-call",
54754
54905
  children: [
54755
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54906
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54756
54907
  className: "tool-skill-badge",
54757
54908
  children: "skill"
54758
54909
  }, undefined, false, undefined, this),
54759
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54910
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54760
54911
  className: "command-call-label",
54761
54912
  children: turn.command.name
54762
54913
  }, undefined, false, undefined, this)
54763
54914
  ]
54764
54915
  }, undefined, true, undefined, this),
54765
- /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(MarkdownRenderer, {
54916
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(MarkdownRenderer, {
54766
54917
  content: turn.text
54767
54918
  }, undefined, false, undefined, this),
54768
- turn.attachments && turn.attachments.length > 0 && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("div", {
54919
+ turn.attachments && turn.attachments.length > 0 && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54769
54920
  className: "attachments",
54770
- children: turn.attachments.map((a, i) => /* @__PURE__ */ jsx_dev_runtime15.jsxDEV("span", {
54921
+ children: turn.attachments.map((a, i) => /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54771
54922
  className: "attachment-badge",
54772
54923
  children: [
54773
54924
  "image/",
@@ -54782,14 +54933,14 @@ function UserMessage({
54782
54933
  }
54783
54934
 
54784
54935
  // src/frontend/components/message/MessageList.tsx
54785
- var jsx_dev_runtime16 = __toESM(require_jsx_dev_runtime(), 1);
54786
- 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) {
54787
54938
  const activeClass = isActive ? "active-message" : "";
54788
54939
  switch (turn.kind) {
54789
54940
  case "user":
54790
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54941
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54791
54942
  className: isActive ? "active-message step-enter" : "",
54792
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(UserMessage, {
54943
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(UserMessage, {
54793
54944
  turn,
54794
54945
  isSubAgent,
54795
54946
  planSessionId,
@@ -54798,27 +54949,28 @@ function renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project,
54798
54949
  }, undefined, false, undefined, this)
54799
54950
  }, undefined, false, undefined, this);
54800
54951
  case "assistant":
54801
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54952
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54802
54953
  className: activeClass,
54803
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(AssistantMessage, {
54954
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(AssistantMessage, {
54804
54955
  turn,
54805
54956
  visibleSubSteps: visibleSubSteps?.get(index2),
54806
54957
  sessionId,
54807
- project
54958
+ project,
54959
+ pluginId
54808
54960
  }, undefined, false, undefined, this)
54809
54961
  }, undefined, false, undefined, this);
54810
54962
  case "system":
54811
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54963
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54812
54964
  className: `turn ${activeClass}`,
54813
54965
  children: [
54814
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54966
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54815
54967
  className: "turn-header",
54816
54968
  children: [
54817
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54969
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("span", {
54818
54970
  className: "turn-badge turn-badge-system",
54819
54971
  children: "System"
54820
54972
  }, undefined, false, undefined, this),
54821
- turn.timestamp && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("time", {
54973
+ turn.timestamp && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("time", {
54822
54974
  className: "turn-timestamp",
54823
54975
  dateTime: turn.timestamp,
54824
54976
  "data-tooltip": formatFullDateTime(turn.timestamp),
@@ -54826,26 +54978,26 @@ function renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project,
54826
54978
  }, undefined, false, undefined, this)
54827
54979
  ]
54828
54980
  }, undefined, true, undefined, this),
54829
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54981
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54830
54982
  className: "message message-system",
54831
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MarkdownRenderer, {
54983
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(MarkdownRenderer, {
54832
54984
  content: turn.text
54833
54985
  }, undefined, false, undefined, this)
54834
54986
  }, undefined, false, undefined, this)
54835
54987
  ]
54836
54988
  }, undefined, true, undefined, this);
54837
54989
  case "parse_error":
54838
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54990
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54839
54991
  className: `turn ${activeClass}`,
54840
54992
  children: [
54841
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
54993
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54842
54994
  className: "turn-header",
54843
54995
  children: [
54844
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
54996
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("span", {
54845
54997
  className: "turn-badge turn-badge-error",
54846
54998
  children: "Parse Error"
54847
54999
  }, undefined, false, undefined, this),
54848
- turn.lineNumber > 0 && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("span", {
55000
+ turn.lineNumber > 0 && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("span", {
54849
55001
  className: "parse-error-line",
54850
55002
  children: [
54851
55003
  "line ",
@@ -54854,24 +55006,24 @@ function renderTurn(turn, index2, isActive, visibleSubSteps, sessionId, project,
54854
55006
  }, undefined, true, undefined, this)
54855
55007
  ]
54856
55008
  }, undefined, true, undefined, this),
54857
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
55009
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54858
55010
  className: "message message-parse-error",
54859
55011
  children: [
54860
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
55012
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54861
55013
  className: "parse-error-type",
54862
55014
  children: turn.errorType === "json_parse" ? "Invalid JSON" : "Invalid Structure"
54863
55015
  }, undefined, false, undefined, this),
54864
- turn.errorDetails && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
55016
+ turn.errorDetails && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54865
55017
  className: "parse-error-details",
54866
55018
  children: turn.errorDetails
54867
55019
  }, undefined, false, undefined, this),
54868
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("details", {
55020
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("details", {
54869
55021
  className: "parse-error-raw",
54870
55022
  children: [
54871
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("summary", {
55023
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("summary", {
54872
55024
  children: "Raw content"
54873
55025
  }, undefined, false, undefined, this),
54874
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("pre", {
55026
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("pre", {
54875
55027
  children: turn.rawLine
54876
55028
  }, undefined, false, undefined, this)
54877
55029
  ]
@@ -54890,6 +55042,7 @@ function MessageList({
54890
55042
  visibleSubSteps,
54891
55043
  sessionId,
54892
55044
  project,
55045
+ pluginId,
54893
55046
  isSubAgent,
54894
55047
  planSessionId,
54895
55048
  implSessionId
@@ -54899,56 +55052,43 @@ function MessageList({
54899
55052
  return false;
54900
55053
  return !STATUS_RE2.test(t.text.trim());
54901
55054
  });
54902
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV("div", {
55055
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
54903
55056
  className: "message-list",
54904
55057
  children: turns.map((turn, index2) => {
54905
55058
  const isActive = visibleSubSteps ? index2 === turns.length - 1 : false;
54906
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(ErrorBoundary, {
55059
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(ErrorBoundary, {
54907
55060
  inline: true,
54908
- 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)
54909
55062
  }, turn.uuid || index2, false, undefined, this);
54910
55063
  })
54911
55064
  }, undefined, false, undefined, this);
54912
55065
  }
54913
55066
 
54914
55067
  // src/frontend/components/message/SubAgentView.tsx
54915
- var jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
55068
+ var jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
54916
55069
  function SubAgentView({ sessionId, project, agentId }) {
54917
- 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);
54918
55071
  if (loading)
54919
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
55072
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
54920
55073
  className: "loading",
54921
55074
  children: "Loading sub-agent conversation..."
54922
55075
  }, undefined, false, undefined, this);
54923
- if (error) {
54924
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
54925
- className: "fetch-error",
54926
- children: [
54927
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("span", {
54928
- className: "fetch-error-message",
54929
- children: [
54930
- "Error: ",
54931
- error
54932
- ]
54933
- }, undefined, true, undefined, this),
54934
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("button", {
54935
- type: "button",
54936
- className: "btn btn-sm",
54937
- onClick: retry,
54938
- children: "Retry"
54939
- }, undefined, false, undefined, this)
54940
- ]
54941
- }, undefined, true, undefined, this);
54942
- }
55076
+ if (error)
55077
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(FetchError, {
55078
+ error,
55079
+ onRetry: retry,
55080
+ showPrefix: true
55081
+ }, undefined, false, undefined, this);
54943
55082
  if (!data?.session || data.session.turns.length === 0)
54944
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV("div", {
55083
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
54945
55084
  className: "subagent-empty",
54946
55085
  children: "No sub-agent conversation data available."
54947
55086
  }, undefined, false, undefined, this);
54948
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(MessageList, {
55087
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(MessageList, {
54949
55088
  turns: data.session.turns,
54950
55089
  sessionId,
54951
55090
  project,
55091
+ pluginId: data.session.pluginId,
54952
55092
  isSubAgent: true
54953
55093
  }, undefined, false, undefined, this);
54954
55094
  }
@@ -54960,25 +55100,25 @@ function projectDisplayName(project) {
54960
55100
  }
54961
55101
 
54962
55102
  // src/frontend/components/project/HiddenProjectList.tsx
54963
- var jsx_dev_runtime18 = __toESM(require_jsx_dev_runtime(), 1);
55103
+ var jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
54964
55104
  function HiddenProjectList({ hiddenIds, onUnhide, onBack }) {
54965
55105
  const { data, loading, error, retry } = useFetch("/api/projects", []);
54966
55106
  const projects = data?.projects ?? [];
54967
55107
  const hidden = projects.filter((p) => hiddenIds.has(p.encodedPath));
54968
55108
  if (loading)
54969
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55109
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
54970
55110
  className: "loading",
54971
55111
  children: "Loading..."
54972
55112
  }, undefined, false, undefined, this);
54973
55113
  if (error) {
54974
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55114
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
54975
55115
  className: "fetch-error",
54976
55116
  children: [
54977
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("span", {
55117
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("span", {
54978
55118
  className: "fetch-error-message",
54979
55119
  children: error
54980
55120
  }, undefined, false, undefined, this),
54981
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("button", {
55121
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("button", {
54982
55122
  type: "button",
54983
55123
  className: "btn btn-sm",
54984
55124
  onClick: retry,
@@ -54987,40 +55127,40 @@ function HiddenProjectList({ hiddenIds, onUnhide, onBack }) {
54987
55127
  ]
54988
55128
  }, undefined, true, undefined, this);
54989
55129
  }
54990
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55130
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
54991
55131
  className: "hidden-projects-page",
54992
55132
  children: [
54993
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55133
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
54994
55134
  className: "back-btn",
54995
55135
  onClick: onBack,
54996
55136
  children: "← Back to projects"
54997
55137
  }, undefined, false, undefined, this),
54998
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("h2", {
55138
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("h2", {
54999
55139
  style: { margin: "16px 0 12px", fontSize: "1.1rem", color: "var(--text-primary)" },
55000
55140
  children: "Hidden Projects"
55001
55141
  }, undefined, false, undefined, this),
55002
- hidden.length === 0 ? /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55142
+ hidden.length === 0 ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55003
55143
  className: "empty-state",
55004
55144
  children: [
55005
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55145
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55006
55146
  className: "empty-state-title",
55007
55147
  children: "No hidden projects"
55008
55148
  }, undefined, false, undefined, this),
55009
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("p", {
55149
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("p", {
55010
55150
  children: "Projects you hide will appear here"
55011
55151
  }, undefined, false, undefined, this)
55012
55152
  ]
55013
- }, undefined, true, undefined, this) : hidden.map((project) => /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55153
+ }, undefined, true, undefined, this) : hidden.map((project) => /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55014
55154
  className: "list-item list-item-with-action",
55015
55155
  children: [
55016
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55156
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55017
55157
  className: "list-item-content",
55018
55158
  children: [
55019
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55159
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55020
55160
  className: "list-item-title",
55021
55161
  children: projectDisplayName(project)
55022
55162
  }, undefined, false, undefined, this),
55023
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("div", {
55163
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55024
55164
  className: "list-item-meta",
55025
55165
  children: [
55026
55166
  project.sessionCount,
@@ -55030,7 +55170,7 @@ function HiddenProjectList({ hiddenIds, onUnhide, onBack }) {
55030
55170
  }, undefined, true, undefined, this)
55031
55171
  ]
55032
55172
  }, undefined, true, undefined, this),
55033
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV("button", {
55173
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("button", {
55034
55174
  type: "button",
55035
55175
  className: "btn btn-sm",
55036
55176
  onClick: () => onUnhide(project.encodedPath),
@@ -55042,198 +55182,20 @@ function HiddenProjectList({ hiddenIds, onUnhide, onBack }) {
55042
55182
  }, undefined, true, undefined, this);
55043
55183
  }
55044
55184
 
55045
- // src/frontend/components/project/ProjectList.tsx
55046
- var import_react10 = __toESM(require_react(), 1);
55047
- var jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
55048
- function ProjectList({
55049
- onSelect,
55050
- selected,
55051
- hiddenIds,
55052
- onHide,
55053
- onShowHidden
55054
- }) {
55055
- const { data, loading, error, retry } = useFetch("/api/projects", []);
55056
- const [filter, setFilter] = import_react10.useState("");
55057
- const projects = data?.projects ?? [];
55058
- const filtered = projects.filter((p) => !hiddenIds.has(p.encodedPath) && (p.name.toLowerCase().includes(filter.toLowerCase()) || p.encodedPath.toLowerCase().includes(filter.toLowerCase())));
55059
- if (loading)
55060
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55061
- className: "loading",
55062
- children: "Loading projects..."
55063
- }, undefined, false, undefined, this);
55064
- if (error) {
55065
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55066
- className: "fetch-error",
55067
- children: [
55068
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("span", {
55069
- className: "fetch-error-message",
55070
- children: error
55071
- }, undefined, false, undefined, this),
55072
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("button", {
55073
- type: "button",
55074
- className: "btn btn-sm",
55075
- onClick: retry,
55076
- children: "Retry"
55077
- }, undefined, false, undefined, this)
55078
- ]
55079
- }, undefined, true, undefined, this);
55080
- }
55081
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55082
- children: [
55083
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("input", {
55084
- className: "filter-input",
55085
- placeholder: "Filter projects...",
55086
- value: filter,
55087
- onChange: (e) => setFilter(e.target.value)
55088
- }, undefined, false, undefined, this),
55089
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55090
- className: "list-section-title",
55091
- children: [
55092
- "Projects (",
55093
- filtered.length,
55094
- ")"
55095
- ]
55096
- }, undefined, true, undefined, this),
55097
- filtered.map((project) => /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55098
- className: `list-item list-item-with-action ${selected === project.encodedPath ? "active" : ""}`,
55099
- onClick: () => onSelect(project),
55100
- children: [
55101
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55102
- className: "list-item-content",
55103
- children: [
55104
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55105
- className: "list-item-title",
55106
- children: projectDisplayName(project)
55107
- }, undefined, false, undefined, this),
55108
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55109
- className: "list-item-meta",
55110
- children: [
55111
- project.sessionCount,
55112
- " session",
55113
- project.sessionCount !== 1 ? "s" : "",
55114
- " · ",
55115
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("time", {
55116
- dateTime: project.lastActivity,
55117
- title: formatFullDateTime(project.lastActivity),
55118
- children: formatRelativeTime(project.lastActivity)
55119
- }, undefined, false, undefined, this)
55120
- ]
55121
- }, undefined, true, undefined, this)
55122
- ]
55123
- }, undefined, true, undefined, this),
55124
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("button", {
55125
- type: "button",
55126
- className: "btn-hide",
55127
- title: "Hide project",
55128
- onClick: (e) => {
55129
- e.stopPropagation();
55130
- onHide(project.encodedPath);
55131
- },
55132
- children: "×"
55133
- }, undefined, false, undefined, this)
55134
- ]
55135
- }, project.encodedPath, true, undefined, this)),
55136
- filtered.length === 0 && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55137
- className: "empty-list-message",
55138
- children: "No projects found"
55139
- }, undefined, false, undefined, this),
55140
- hiddenIds.size > 0 && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV("div", {
55141
- className: "hidden-projects-link",
55142
- onClick: onShowHidden,
55143
- children: [
55144
- hiddenIds.size,
55145
- " hidden project",
55146
- hiddenIds.size !== 1 ? "s" : ""
55147
- ]
55148
- }, undefined, true, undefined, this)
55149
- ]
55150
- }, undefined, true, undefined, this);
55151
- }
55152
-
55153
- // src/frontend/components/project/SessionList.tsx
55154
- var jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
55155
- function SessionList({ project, onSelect, onBack, selectedId }) {
55156
- const { data, loading, error, retry } = useFetch(`/api/projects/${encodeURIComponent(project.encodedPath)}/sessions`, [project.encodedPath]);
55157
- const sessions = data?.sessions ?? [];
55158
- const parts = project.name.split("/").filter(Boolean);
55159
- const displayName = parts.slice(-2).join("/");
55160
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55161
- children: [
55162
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55163
- className: "back-btn",
55164
- onClick: onBack,
55165
- children: "← Projects"
55166
- }, undefined, false, undefined, this),
55167
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55168
- className: "list-section-title",
55169
- children: displayName
55170
- }, undefined, false, undefined, this),
55171
- loading && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55172
- className: "loading",
55173
- children: "Loading sessions..."
55174
- }, undefined, false, undefined, this),
55175
- error && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55176
- className: "fetch-error",
55177
- children: [
55178
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("span", {
55179
- className: "fetch-error-message",
55180
- children: error
55181
- }, undefined, false, undefined, this),
55182
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("button", {
55183
- type: "button",
55184
- className: "btn btn-sm",
55185
- onClick: retry,
55186
- children: "Retry"
55187
- }, undefined, false, undefined, this)
55188
- ]
55189
- }, undefined, true, undefined, this),
55190
- !loading && !error && sessions.map((session) => /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55191
- className: `list-item ${selectedId === session.sessionId ? "active" : ""}${session.sessionType ? ` ${session.sessionType}` : ""}`,
55192
- onClick: () => onSelect(session),
55193
- children: [
55194
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55195
- className: "list-item-title",
55196
- children: session.firstMessage || session.slug
55197
- }, undefined, false, undefined, this),
55198
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55199
- className: "list-item-meta",
55200
- children: [
55201
- session.sessionType && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("span", {
55202
- className: `session-type-badge ${session.sessionType}`,
55203
- children: session.sessionType === "plan" ? "Plan" : "Impl"
55204
- }, undefined, false, undefined, this),
55205
- " ",
55206
- session.model && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("span", {
55207
- children: [
55208
- shortModel(session.model),
55209
- " · "
55210
- ]
55211
- }, undefined, true, undefined, this),
55212
- session.gitBranch && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("span", {
55213
- children: [
55214
- session.gitBranch,
55215
- " · "
55216
- ]
55217
- }, undefined, true, undefined, this),
55218
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("time", {
55219
- dateTime: session.timestamp,
55220
- title: formatFullDateTime(session.timestamp),
55221
- children: formatTime(session.timestamp)
55222
- }, undefined, false, undefined, this)
55223
- ]
55224
- }, undefined, true, undefined, this)
55225
- ]
55226
- }, session.sessionId, true, undefined, this)),
55227
- !loading && !error && sessions.length === 0 && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV("div", {
55228
- className: "empty-list-message",
55229
- children: "No sessions found"
55230
- }, undefined, false, undefined, this)
55231
- ]
55232
- }, undefined, true, undefined, this);
55185
+ // src/frontend/components/search/SearchModal.tsx
55186
+ var import_react12 = __toESM(require_react(), 1);
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;
55233
55196
  }
55234
55197
 
55235
55198
  // src/frontend/components/search/SearchModal.tsx
55236
- var import_react11 = __toESM(require_react(), 1);
55237
55199
  var jsx_dev_runtime21 = __toESM(require_jsx_dev_runtime(), 1);
55238
55200
  var MAX_RESULTS = 20;
55239
55201
  function matchesQuery(result, query) {
@@ -55241,15 +55203,15 @@ function matchesQuery(result, query) {
55241
55203
  return result.firstMessage.toLowerCase().includes(q2) || result.projectName.toLowerCase().includes(q2) || result.gitBranch.toLowerCase().includes(q2);
55242
55204
  }
55243
55205
  function SearchModal({ sessions, onSelect, onClose }) {
55244
- const [query, setQuery] = import_react11.useState("");
55245
- const [highlightedIndex, setHighlightedIndex] = import_react11.useState(0);
55246
- const inputRef = import_react11.useRef(null);
55247
- const resultsRef = import_react11.useRef(null);
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);
55248
55210
  const filtered = query ? sessions.filter((s2) => matchesQuery(s2, query)).slice(0, MAX_RESULTS) : sessions.slice(0, MAX_RESULTS);
55249
- import_react11.useEffect(() => {
55211
+ import_react12.useEffect(() => {
55250
55212
  inputRef.current?.focus();
55251
55213
  }, []);
55252
- import_react11.useEffect(() => {
55214
+ import_react12.useEffect(() => {
55253
55215
  const container = resultsRef.current;
55254
55216
  if (!container)
55255
55217
  return;
@@ -55259,10 +55221,10 @@ function SearchModal({ sessions, onSelect, onClose }) {
55259
55221
  item.scrollIntoView({ block: "nearest" });
55260
55222
  }
55261
55223
  }, [highlightedIndex]);
55262
- const handleSelect = import_react11.useCallback((result) => {
55224
+ const handleSelect = import_react12.useCallback((result) => {
55263
55225
  onSelect(result.encodedPath, result.sessionId);
55264
55226
  }, [onSelect]);
55265
- const handleKeyDown = import_react11.useCallback((e) => {
55227
+ const handleKeyDown = import_react12.useCallback((e) => {
55266
55228
  switch (e.key) {
55267
55229
  case "ArrowDown":
55268
55230
  e.preventDefault();
@@ -55334,10 +55296,17 @@ function SearchModal({ sessions, onSelect, onClose }) {
55334
55296
  className: "search-result-meta",
55335
55297
  children: [
55336
55298
  result.projectName,
55337
- " · ",
55338
- shortModel(result.model),
55339
- result.gitBranch ? ` · ${result.gitBranch}` : "",
55340
- " ·",
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
+ "·",
55341
55310
  " ",
55342
55311
  /* @__PURE__ */ jsx_dev_runtime21.jsxDEV("time", {
55343
55312
  dateTime: result.timestamp,
@@ -55384,12 +55353,12 @@ function SearchModal({ sessions, onSelect, onClose }) {
55384
55353
  }
55385
55354
 
55386
55355
  // src/frontend/components/session/PresentationShell.tsx
55387
- var import_react14 = __toESM(require_react(), 1);
55356
+ var import_react15 = __toESM(require_react(), 1);
55388
55357
 
55389
55358
  // src/frontend/hooks/useKeyboard.ts
55390
- var import_react12 = __toESM(require_react(), 1);
55359
+ var import_react13 = __toESM(require_react(), 1);
55391
55360
  function useKeyboard(handlers2, active) {
55392
- import_react12.useEffect(() => {
55361
+ import_react13.useEffect(() => {
55393
55362
  if (!active)
55394
55363
  return;
55395
55364
  function handleKeyDown(e) {
@@ -55432,7 +55401,7 @@ function useKeyboard(handlers2, active) {
55432
55401
  }
55433
55402
 
55434
55403
  // src/frontend/hooks/usePresentationMode.ts
55435
- var import_react13 = __toESM(require_react(), 1);
55404
+ var import_react14 = __toESM(require_react(), 1);
55436
55405
  function countSubSteps(turn) {
55437
55406
  if (turn.kind !== "assistant")
55438
55407
  return 1;
@@ -55440,10 +55409,10 @@ function countSubSteps(turn) {
55440
55409
  return Math.max(groupContentBlocks(a.contentBlocks).length, 1);
55441
55410
  }
55442
55411
  function usePresentationMode(turns) {
55443
- const [active, setActive] = import_react13.useState(false);
55444
- const [fullscreen, setFullscreen] = import_react13.useState(false);
55445
- const [currentStep, setCurrentStep] = import_react13.useState(0);
55446
- const steps = import_react13.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(() => {
55447
55416
  const result = [];
55448
55417
  for (let i = 0;i < turns.length; i++) {
55449
55418
  const sub = countSubSteps(turns[i]);
@@ -55454,7 +55423,7 @@ function usePresentationMode(turns) {
55454
55423
  return result;
55455
55424
  }, [turns]);
55456
55425
  const totalSteps = steps.length;
55457
- const turnBoundaries = import_react13.useMemo(() => {
55426
+ const turnBoundaries = import_react14.useMemo(() => {
55458
55427
  const boundaries = [];
55459
55428
  for (let i = 0;i < steps.length; i++) {
55460
55429
  const next2 = steps[i + 1];
@@ -55464,7 +55433,7 @@ function usePresentationMode(turns) {
55464
55433
  }
55465
55434
  return boundaries;
55466
55435
  }, [steps]);
55467
- const { visibleTurns, visibleSubSteps } = import_react13.useMemo(() => {
55436
+ const { visibleTurns, visibleSubSteps } = import_react14.useMemo(() => {
55468
55437
  if (!active || steps.length === 0) {
55469
55438
  return { visibleTurns: turns, visibleSubSteps: new Map };
55470
55439
  }
@@ -55478,22 +55447,22 @@ function usePresentationMode(turns) {
55478
55447
  subSteps.set(maxTurnIndex, step.subStep + 1);
55479
55448
  return { visibleTurns: visible, visibleSubSteps: subSteps };
55480
55449
  }, [active, currentStep, steps, turns]);
55481
- const enter = import_react13.useCallback(() => {
55450
+ const enter = import_react14.useCallback(() => {
55482
55451
  setActive(true);
55483
55452
  setCurrentStep(0);
55484
55453
  }, []);
55485
- const exit3 = import_react13.useCallback(() => {
55454
+ const exit3 = import_react14.useCallback(() => {
55486
55455
  setActive(false);
55487
55456
  setFullscreen(false);
55488
55457
  setCurrentStep(0);
55489
55458
  }, []);
55490
- const next = import_react13.useCallback(() => {
55459
+ const next = import_react14.useCallback(() => {
55491
55460
  setCurrentStep((s2) => Math.min(s2 + 1, steps.length - 1));
55492
55461
  }, [steps.length]);
55493
- const prev = import_react13.useCallback(() => {
55462
+ const prev = import_react14.useCallback(() => {
55494
55463
  setCurrentStep((s2) => Math.max(s2 - 1, 0));
55495
55464
  }, []);
55496
- const nextTurn = import_react13.useCallback(() => {
55465
+ const nextTurn = import_react14.useCallback(() => {
55497
55466
  setCurrentStep((s2) => {
55498
55467
  for (const b of turnBoundaries) {
55499
55468
  if (b > s2)
@@ -55502,7 +55471,7 @@ function usePresentationMode(turns) {
55502
55471
  return s2;
55503
55472
  });
55504
55473
  }, [turnBoundaries]);
55505
- const prevTurn = import_react13.useCallback(() => {
55474
+ const prevTurn = import_react14.useCallback(() => {
55506
55475
  setCurrentStep((s2) => {
55507
55476
  for (let i = turnBoundaries.length - 1;i >= 0; i--) {
55508
55477
  if (turnBoundaries[i] < s2)
@@ -55511,7 +55480,7 @@ function usePresentationMode(turns) {
55511
55480
  return s2;
55512
55481
  });
55513
55482
  }, [turnBoundaries]);
55514
- const toggleFullscreen = import_react13.useCallback(() => {
55483
+ const toggleFullscreen = import_react14.useCallback(() => {
55515
55484
  setFullscreen((f) => !f);
55516
55485
  }, []);
55517
55486
  return {
@@ -55538,11 +55507,12 @@ function PresentationShell({
55538
55507
  onExit,
55539
55508
  sessionId,
55540
55509
  project,
55510
+ pluginId,
55541
55511
  isSubAgent
55542
55512
  }) {
55543
- const scrollRef = import_react14.useRef(null);
55513
+ const scrollRef = import_react15.useRef(null);
55544
55514
  const presentation = usePresentationMode(turns);
55545
- import_react14.useEffect(() => {
55515
+ import_react15.useEffect(() => {
55546
55516
  if (turns.length > 0 && !presentation.active) {
55547
55517
  presentation.enter();
55548
55518
  }
@@ -55556,7 +55526,7 @@ function PresentationShell({
55556
55526
  onFullscreen: presentation.toggleFullscreen
55557
55527
  }, presentation.active);
55558
55528
  const currentStep = presentation.currentStep;
55559
- import_react14.useEffect(() => {
55529
+ import_react15.useEffect(() => {
55560
55530
  if (currentStep >= 0 && scrollRef.current) {
55561
55531
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
55562
55532
  }
@@ -55572,6 +55542,7 @@ function PresentationShell({
55572
55542
  visibleSubSteps: presentation.visibleSubSteps,
55573
55543
  sessionId,
55574
55544
  project,
55545
+ pluginId,
55575
55546
  isSubAgent
55576
55547
  }, undefined, false, undefined, this),
55577
55548
  /* @__PURE__ */ jsx_dev_runtime22.jsxDEV("div", {
@@ -55605,81 +55576,70 @@ function PresentationShell({
55605
55576
  // src/frontend/components/session/SessionPresentation.tsx
55606
55577
  var jsx_dev_runtime23 = __toESM(require_jsx_dev_runtime(), 1);
55607
55578
  function SessionPresentation({ sessionId, project, onExit }) {
55608
- const { data, loading, error, retry } = useFetch(`/api/sessions/${sessionId}?project=${encodeURIComponent(project)}`, [sessionId, project]);
55579
+ const { data, loading, error, retry } = useSessionData(sessionId, project);
55609
55580
  if (loading)
55610
55581
  return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("div", {
55611
55582
  className: "loading",
55612
55583
  children: "Loading session..."
55613
55584
  }, undefined, false, undefined, this);
55614
- if (error) {
55615
- return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("div", {
55616
- className: "fetch-error",
55617
- children: [
55618
- /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("span", {
55619
- className: "fetch-error-message",
55620
- children: [
55621
- "Error: ",
55622
- error
55623
- ]
55624
- }, undefined, true, undefined, this),
55625
- /* @__PURE__ */ jsx_dev_runtime23.jsxDEV("button", {
55626
- type: "button",
55627
- className: "btn btn-sm",
55628
- onClick: retry,
55629
- children: "Retry"
55630
- }, undefined, false, undefined, this)
55631
- ]
55632
- }, undefined, true, undefined, this);
55633
- }
55585
+ if (error)
55586
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(FetchError, {
55587
+ error,
55588
+ onRetry: retry,
55589
+ showPrefix: true
55590
+ }, undefined, false, undefined, this);
55634
55591
  if (!data?.session)
55635
55592
  return null;
55636
55593
  return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(PresentationShell, {
55637
55594
  turns: data.session.turns,
55638
55595
  onExit,
55639
55596
  sessionId,
55640
- project
55597
+ project,
55598
+ pluginId: data.session.pluginId
55641
55599
  }, undefined, false, undefined, this);
55642
55600
  }
55643
55601
 
55644
55602
  // src/frontend/components/session/SessionView.tsx
55645
55603
  var jsx_dev_runtime24 = __toESM(require_jsx_dev_runtime(), 1);
55646
- function SessionView({ sessionId, project }) {
55647
- const { data, loading, error, retry } = useFetch(`/api/sessions/${sessionId}?project=${encodeURIComponent(project)}`, [sessionId, project]);
55604
+ function SessionView({ sessionId, project, gitBranch }) {
55605
+ const { data, loading, error, retry } = useSessionData(sessionId, project);
55648
55606
  if (loading)
55649
55607
  return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("div", {
55650
55608
  className: "loading",
55651
55609
  children: "Loading session..."
55652
55610
  }, undefined, false, undefined, this);
55653
- if (error) {
55654
- return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("div", {
55655
- className: "fetch-error",
55656
- children: [
55657
- /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("span", {
55658
- className: "fetch-error-message",
55659
- children: [
55660
- "Error: ",
55661
- error
55662
- ]
55663
- }, undefined, true, undefined, this),
55664
- /* @__PURE__ */ jsx_dev_runtime24.jsxDEV("button", {
55665
- type: "button",
55666
- className: "btn btn-sm",
55667
- onClick: retry,
55668
- children: "Retry"
55669
- }, undefined, false, undefined, this)
55670
- ]
55671
- }, undefined, true, undefined, this);
55672
- }
55611
+ if (error)
55612
+ return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(FetchError, {
55613
+ error,
55614
+ onRetry: retry,
55615
+ showPrefix: true
55616
+ }, undefined, false, undefined, this);
55673
55617
  if (!data?.session)
55674
55618
  return null;
55675
55619
  const session = data.session;
55676
- return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(MessageList, {
55677
- turns: session.turns,
55678
- sessionId,
55679
- project,
55680
- planSessionId: session.planSessionId,
55681
- implSessionId: session.implSessionId
55682
- }, undefined, false, undefined, this);
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);
55683
55643
  }
55684
55644
 
55685
55645
  // src/frontend/components/session/SubAgentPresentation.tsx
@@ -55690,32 +55650,18 @@ function SubAgentPresentation({
55690
55650
  agentId,
55691
55651
  onExit
55692
55652
  }) {
55693
- 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);
55694
55654
  if (loading)
55695
55655
  return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV("div", {
55696
55656
  className: "loading",
55697
55657
  children: "Loading sub-agent conversation..."
55698
55658
  }, undefined, false, undefined, this);
55699
- if (error) {
55700
- return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV("div", {
55701
- className: "fetch-error",
55702
- children: [
55703
- /* @__PURE__ */ jsx_dev_runtime25.jsxDEV("span", {
55704
- className: "fetch-error-message",
55705
- children: [
55706
- "Error: ",
55707
- error
55708
- ]
55709
- }, undefined, true, undefined, this),
55710
- /* @__PURE__ */ jsx_dev_runtime25.jsxDEV("button", {
55711
- type: "button",
55712
- className: "btn btn-sm",
55713
- onClick: retry,
55714
- children: "Retry"
55715
- }, undefined, false, undefined, this)
55716
- ]
55717
- }, undefined, true, undefined, this);
55718
- }
55659
+ if (error)
55660
+ return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(FetchError, {
55661
+ error,
55662
+ onRetry: retry,
55663
+ showPrefix: true
55664
+ }, undefined, false, undefined, this);
55719
55665
  if (!data?.session || data.session.turns.length === 0)
55720
55666
  return null;
55721
55667
  return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(PresentationShell, {
@@ -55723,12 +55669,13 @@ function SubAgentPresentation({
55723
55669
  onExit,
55724
55670
  sessionId,
55725
55671
  project,
55672
+ pluginId: data.session.pluginId,
55726
55673
  isSubAgent: true
55727
55674
  }, undefined, false, undefined, this);
55728
55675
  }
55729
55676
 
55730
55677
  // src/frontend/hooks/useHiddenProjects.ts
55731
- var import_react15 = __toESM(require_react(), 1);
55678
+ var import_react16 = __toESM(require_react(), 1);
55732
55679
  var STORAGE_KEY = "klovi-hidden-projects";
55733
55680
  function loadHiddenIds() {
55734
55681
  try {
@@ -55750,8 +55697,8 @@ function persistHiddenIds(ids) {
55750
55697
  localStorage.setItem(STORAGE_KEY, JSON.stringify(store));
55751
55698
  }
55752
55699
  function useHiddenProjects() {
55753
- const [hiddenIds, setHiddenIds] = import_react15.useState(() => loadHiddenIds());
55754
- const hide = import_react15.useCallback((encodedPath) => {
55700
+ const [hiddenIds, setHiddenIds] = import_react16.useState(() => loadHiddenIds());
55701
+ const hide = import_react16.useCallback((encodedPath) => {
55755
55702
  setHiddenIds((prev) => {
55756
55703
  const next = new Set(prev);
55757
55704
  next.add(encodedPath);
@@ -55759,7 +55706,7 @@ function useHiddenProjects() {
55759
55706
  return next;
55760
55707
  });
55761
55708
  }, []);
55762
- const unhide = import_react15.useCallback((encodedPath) => {
55709
+ const unhide = import_react16.useCallback((encodedPath) => {
55763
55710
  setHiddenIds((prev) => {
55764
55711
  const next = new Set(prev);
55765
55712
  next.delete(encodedPath);
@@ -55767,12 +55714,38 @@ function useHiddenProjects() {
55767
55714
  return next;
55768
55715
  });
55769
55716
  }, []);
55770
- const isHidden = import_react15.useCallback((encodedPath) => hiddenIds.has(encodedPath), [hiddenIds]);
55717
+ const isHidden = import_react16.useCallback((encodedPath) => hiddenIds.has(encodedPath), [hiddenIds]);
55771
55718
  return { hiddenIds, hide, unhide, isHidden };
55772
55719
  }
55773
55720
 
55774
- // src/frontend/App.tsx
55775
- var jsx_dev_runtime26 = __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
+ }
55776
55749
  function viewToHash(view) {
55777
55750
  if (view.kind === "hidden")
55778
55751
  return "#/hidden";
@@ -55784,6 +55757,33 @@ function viewToHash(view) {
55784
55757
  return `#/${view.project.encodedPath}/${view.sessionId}/subagent/${view.agentId}`;
55785
55758
  return "#/";
55786
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
+ }
55787
55787
  async function restoreFromHash() {
55788
55788
  const hash = window.location.hash.replace(/^#\/?/, "");
55789
55789
  if (!hash)
@@ -55794,11 +55794,11 @@ async function restoreFromHash() {
55794
55794
  const encodedPath = parts[0];
55795
55795
  const sessionId = parts[1];
55796
55796
  const isSubAgent = parts[2] === "subagent" && parts[3];
55797
+ if (!encodedPath)
55798
+ return { kind: "home" };
55797
55799
  let project;
55798
55800
  try {
55799
- const res = await fetch("/api/projects");
55800
- const data = await res.json();
55801
- project = data.projects.find((p) => p.encodedPath === encodedPath);
55801
+ project = await loadProject(encodedPath);
55802
55802
  } catch {
55803
55803
  return { kind: "home" };
55804
55804
  }
@@ -55810,9 +55810,7 @@ async function restoreFromHash() {
55810
55810
  return { kind: "subagent", project, sessionId, agentId: parts[3], presenting: false };
55811
55811
  }
55812
55812
  try {
55813
- const res = await fetch(`/api/projects/${encodedPath}/sessions`);
55814
- const data = await res.json();
55815
- const session = data.sessions.find((s2) => s2.sessionId === sessionId);
55813
+ const session = await loadProjectSession(project, sessionId);
55816
55814
  if (session) {
55817
55815
  return { kind: "session", project, session, presenting: false };
55818
55816
  }
@@ -55843,111 +55841,328 @@ function getHeaderInfo(view) {
55843
55841
  }
55844
55842
  return { title: "Klovi", breadcrumb: "" };
55845
55843
  }
55846
- 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) {
55847
56096
  if (view.kind === "home" || view.kind === "hidden") {
55848
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(ProjectList, {
55849
- onSelect: selectProject,
56097
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(ProjectList, {
56098
+ onSelect: actions.selectProject,
55850
56099
  hiddenIds,
55851
- onHide: hide,
55852
- onShowHidden: goHidden
56100
+ onHide: actions.hide,
56101
+ onShowHidden: actions.goHidden
55853
56102
  }, undefined, false, undefined, this);
55854
56103
  }
55855
56104
  if (view.kind === "project") {
55856
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(SessionList, {
56105
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(SessionList, {
55857
56106
  project: view.project,
55858
- onSelect: selectSession,
55859
- onBack: goHome
56107
+ onSelect: actions.selectSession,
56108
+ onBack: actions.goHome
55860
56109
  }, undefined, false, undefined, this);
55861
56110
  }
55862
56111
  if (view.kind === "subagent") {
55863
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(SessionList, {
56112
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(SessionList, {
55864
56113
  project: view.project,
55865
- onSelect: selectSession,
55866
- onBack: goHome,
56114
+ onSelect: actions.selectSession,
56115
+ onBack: actions.goHome,
55867
56116
  selectedId: view.sessionId
55868
56117
  }, undefined, false, undefined, this);
55869
56118
  }
55870
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(SessionList, {
56119
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(SessionList, {
55871
56120
  project: view.project,
55872
- onSelect: selectSession,
55873
- onBack: goHome,
56121
+ onSelect: actions.selectSession,
56122
+ onBack: actions.goHome,
55874
56123
  selectedId: view.session.sessionId
55875
56124
  }, undefined, false, undefined, this);
55876
56125
  }
56126
+
56127
+ // src/frontend/App.tsx
56128
+ var jsx_dev_runtime29 = __toESM(require_jsx_dev_runtime(), 1);
55877
56129
  function App() {
55878
56130
  const { setting: themeSetting, cycle: cycleTheme } = useTheme();
55879
56131
  const { size: fontSize, increase, decrease } = useFontSize();
55880
56132
  const { hiddenIds, hide, unhide } = useHiddenProjects();
55881
- const [view, setView] = import_react16.useState({ kind: "home" });
55882
- const [ready, setReady] = import_react16.useState(false);
55883
- const [searchOpen, setSearchOpen] = import_react16.useState(false);
55884
- const [searchSessions, setSearchSessions] = import_react16.useState([]);
55885
- import_react16.useEffect(() => {
55886
- restoreFromHash().then((v2) => {
55887
- setView(v2);
55888
- setReady(true);
55889
- });
55890
- }, []);
55891
- import_react16.useEffect(() => {
55892
- if (!ready)
55893
- return;
55894
- const newHash = viewToHash(view);
55895
- if (window.location.hash !== newHash) {
55896
- history.pushState(null, "", newHash);
55897
- }
55898
- }, [view, ready]);
55899
- import_react16.useEffect(() => {
55900
- const handler = () => {
55901
- restoreFromHash().then(setView);
55902
- };
55903
- window.addEventListener("popstate", handler);
55904
- return () => window.removeEventListener("popstate", handler);
55905
- }, []);
55906
- const selectProject = (project) => {
55907
- setView({ kind: "project", project });
55908
- };
55909
- const selectSession = (session) => {
55910
- if (view.kind === "project" || view.kind === "session") {
55911
- setView({
55912
- kind: "session",
55913
- project: view.project,
55914
- session,
55915
- presenting: false
55916
- });
55917
- }
55918
- };
55919
- const goHome = () => setView({ kind: "home" });
55920
- const goHidden = () => setView({ kind: "hidden" });
55921
- const canPresent = view.kind === "session" || view.kind === "subagent";
55922
- const togglePresentation = import_react16.useCallback(() => {
55923
- if (view.kind === "session" || view.kind === "subagent") {
55924
- setView({ ...view, presenting: !view.presenting });
55925
- }
55926
- }, [view]);
55927
- const fetchSearchSessions = import_react16.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(() => {
55928
56147
  fetch("/api/search/sessions").then((res) => res.json()).then((data) => setSearchSessions(data.sessions)).catch(() => {});
55929
56148
  }, []);
55930
- const openSearch = import_react16.useCallback(() => {
56149
+ const openSearch = import_react19.useCallback(() => {
55931
56150
  setSearchOpen(true);
55932
56151
  fetchSearchSessions();
55933
56152
  }, [fetchSearchSessions]);
55934
- const handleSearchSelect = import_react16.useCallback(async (encodedPath, sessionId) => {
56153
+ const handleSearchSelect = import_react19.useCallback(async (encodedPath, sessionId) => {
55935
56154
  setSearchOpen(false);
55936
- try {
55937
- const [projectsRes, sessionsRes] = await Promise.all([
55938
- fetch("/api/projects"),
55939
- fetch(`/api/projects/${encodedPath}/sessions`)
55940
- ]);
55941
- const projectsData = await projectsRes.json();
55942
- const sessionsData = await sessionsRes.json();
55943
- const project = projectsData.projects.find((p) => p.encodedPath === encodedPath);
55944
- const session = sessionsData.sessions.find((s2) => s2.sessionId === sessionId);
55945
- if (project && session) {
55946
- setView({ kind: "session", project, session, presenting: false });
55947
- }
55948
- } catch {}
55949
- }, []);
55950
- import_react16.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(() => {
55951
56166
  function handleCmdK(e) {
55952
56167
  if ((e.metaKey || e.ctrlKey) && e.key === "k") {
55953
56168
  e.preventDefault();
@@ -55961,7 +56176,7 @@ function App() {
55961
56176
  window.addEventListener("keydown", handleCmdK);
55962
56177
  return () => window.removeEventListener("keydown", handleCmdK);
55963
56178
  }, [fetchSearchSessions]);
55964
- import_react16.useEffect(() => {
56179
+ import_react19.useEffect(() => {
55965
56180
  function handleKeyDown(e) {
55966
56181
  if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)
55967
56182
  return;
@@ -55989,30 +56204,36 @@ function App() {
55989
56204
  return () => window.removeEventListener("keydown", handleKeyDown);
55990
56205
  }, [canPresent, togglePresentation, increase, decrease]);
55991
56206
  const { title: headerTitle, breadcrumb } = getHeaderInfo(view);
55992
- const sidebarContent = getSidebarContent(view, selectProject, hiddenIds, hide, goHidden, selectSession, goHome);
55993
- 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;
55994
56215
  if (!ready) {
55995
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
56216
+ return /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
55996
56217
  className: "loading",
55997
56218
  children: "Loading..."
55998
56219
  }, undefined, false, undefined, this);
55999
56220
  }
56000
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(jsx_dev_runtime26.Fragment, {
56221
+ return /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(jsx_dev_runtime29.Fragment, {
56001
56222
  children: [
56002
- searchOpen && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(SearchModal, {
56223
+ searchOpen && /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SearchModal, {
56003
56224
  sessions: searchSessions,
56004
56225
  onSelect: handleSearchSelect,
56005
56226
  onClose: () => setSearchOpen(false)
56006
56227
  }, undefined, false, undefined, this),
56007
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Layout, {
56228
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(Layout, {
56008
56229
  sidebar: sidebarContent,
56009
56230
  hideSidebar: isPresenting,
56010
56231
  onSearchClick: openSearch,
56011
56232
  children: [
56012
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Header, {
56233
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(Header, {
56013
56234
  title: headerTitle,
56014
56235
  breadcrumb,
56015
- 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,
56016
56237
  backHref: view.kind === "subagent" ? `#/${view.project.encodedPath}/${view.sessionId}` : undefined,
56017
56238
  sessionType: view.kind === "session" ? view.session.sessionType : undefined,
56018
56239
  themeSetting,
@@ -56024,63 +56245,64 @@ function App() {
56024
56245
  onTogglePresentation: togglePresentation,
56025
56246
  showPresentationToggle: canPresent
56026
56247
  }, undefined, false, undefined, this),
56027
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(ErrorBoundary, {
56248
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(ErrorBoundary, {
56028
56249
  children: [
56029
- view.kind === "home" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(jsx_dev_runtime26.Fragment, {
56250
+ view.kind === "home" && /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(jsx_dev_runtime29.Fragment, {
56030
56251
  children: [
56031
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
56252
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
56032
56253
  className: "empty-state",
56033
56254
  children: [
56034
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("img", {
56255
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("img", {
56035
56256
  src: favicon_default,
56036
56257
  alt: "",
56037
56258
  width: "64",
56038
56259
  height: "64",
56039
56260
  className: "empty-state-logo"
56040
56261
  }, undefined, false, undefined, this),
56041
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
56262
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
56042
56263
  className: "empty-state-title",
56043
56264
  children: "Welcome to Klovi"
56044
56265
  }, undefined, false, undefined, this),
56045
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("p", {
56266
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("p", {
56046
56267
  children: "Select a project from the sidebar to browse your AI coding sessions"
56047
56268
  }, undefined, false, undefined, this)
56048
56269
  ]
56049
56270
  }, undefined, true, undefined, this),
56050
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(DashboardStats, {}, undefined, false, undefined, this)
56271
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(DashboardStats, {}, undefined, false, undefined, this)
56051
56272
  ]
56052
56273
  }, undefined, true, undefined, this),
56053
- view.kind === "hidden" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(HiddenProjectList, {
56274
+ view.kind === "hidden" && /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(HiddenProjectList, {
56054
56275
  hiddenIds,
56055
56276
  onUnhide: unhide,
56056
56277
  onBack: goHome
56057
56278
  }, undefined, false, undefined, this),
56058
- view.kind === "project" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
56279
+ view.kind === "project" && /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
56059
56280
  className: "empty-state",
56060
56281
  children: [
56061
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("div", {
56282
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("div", {
56062
56283
  className: "empty-state-title",
56063
56284
  children: "Select a session"
56064
56285
  }, undefined, false, undefined, this),
56065
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV("p", {
56286
+ /* @__PURE__ */ jsx_dev_runtime29.jsxDEV("p", {
56066
56287
  children: "Choose a conversation from the sidebar"
56067
56288
  }, undefined, false, undefined, this)
56068
56289
  ]
56069
56290
  }, undefined, true, undefined, this),
56070
- view.kind === "session" && (view.presenting ? /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(SessionPresentation, {
56291
+ view.kind === "session" && (view.presenting ? /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SessionPresentation, {
56071
56292
  sessionId: view.session.sessionId,
56072
56293
  project: view.project.encodedPath,
56073
56294
  onExit: togglePresentation
56074
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(SessionView, {
56295
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SessionView, {
56075
56296
  sessionId: view.session.sessionId,
56076
- project: view.project.encodedPath
56297
+ project: view.project.encodedPath,
56298
+ gitBranch: view.session.gitBranch
56077
56299
  }, undefined, false, undefined, this)),
56078
- view.kind === "subagent" && (view.presenting ? /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(SubAgentPresentation, {
56300
+ view.kind === "subagent" && (view.presenting ? /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SubAgentPresentation, {
56079
56301
  sessionId: view.sessionId,
56080
56302
  project: view.project.encodedPath,
56081
56303
  agentId: view.agentId,
56082
56304
  onExit: togglePresentation
56083
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(SubAgentView, {
56305
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime29.jsxDEV(SubAgentView, {
56084
56306
  sessionId: view.sessionId,
56085
56307
  project: view.project.encodedPath,
56086
56308
  agentId: view.agentId
@@ -56093,4 +56315,4 @@ function App() {
56093
56315
  }, undefined, true, undefined, this);
56094
56316
  }
56095
56317
  var root4 = import_client.createRoot(document.getElementById("root"));
56096
- root4.render(/* @__PURE__ */ jsx_dev_runtime26.jsxDEV(App, {}, undefined, false, undefined, this));
56318
+ root4.render(/* @__PURE__ */ jsx_dev_runtime29.jsxDEV(App, {}, undefined, false, undefined, this));