research-copilot 0.2.2 → 0.2.4

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.
Files changed (72) hide show
  1. package/app/out/main/index.mjs +124 -49
  2. package/app/out/preload/index.js +6 -0
  3. package/app/out/renderer/assets/{MilkdownMarkdownEditor-jaF-aGPn.js → MilkdownMarkdownEditor-G1iOHwc_.js} +50 -50
  4. package/app/out/renderer/assets/{arc-C1kBmvvR.js → arc-4dzvPWwv.js} +1 -1
  5. package/app/out/renderer/assets/{blockDiagram-c4efeb88-Do93X2rs.js → blockDiagram-c4efeb88-Cj5lBciZ.js} +8 -8
  6. package/app/out/renderer/assets/{c4Diagram-c83219d4-DgxxcZWC.js → c4Diagram-c83219d4-D4wJF4Nk.js} +3 -3
  7. package/app/out/renderer/assets/{channel-Co_M0Svj.js → channel-DRHGMFu6.js} +1 -1
  8. package/app/out/renderer/assets/{classDiagram-beda092f-CQlHgE6H.js → classDiagram-beda092f-DrBIQ4Da.js} +6 -6
  9. package/app/out/renderer/assets/{classDiagram-v2-2358418a-CkGG3aI2.js → classDiagram-v2-2358418a-yBhKnZh4.js} +10 -10
  10. package/app/out/renderer/assets/{clone-C18Y6dgC.js → clone-ChFuWsyG.js} +1 -1
  11. package/app/out/renderer/assets/{createText-1719965b-DGRc6nys.js → createText-1719965b-dtk9hA3P.js} +2 -2
  12. package/app/out/renderer/assets/{edges-96097737-BXvJ4fAK.js → edges-96097737-D_e1x_h6.js} +3 -3
  13. package/app/out/renderer/assets/{erDiagram-0228fc6a-CXjPp0pt.js → erDiagram-0228fc6a-CrZBuvPZ.js} +5 -5
  14. package/app/out/renderer/assets/{flowDb-c6c81e3f-CNhpbtw_.js → flowDb-c6c81e3f-DtZC9Fq3.js} +1 -1
  15. package/app/out/renderer/assets/{flowDiagram-50d868cf-KZ_BUCPA.js → flowDiagram-50d868cf-VzlFN6Qf.js} +12 -12
  16. package/app/out/renderer/assets/{flowDiagram-v2-4f6560a1-IMv50KZP.js → flowDiagram-v2-4f6560a1-BWNPZGSS.js} +12 -12
  17. package/app/out/renderer/assets/{flowchart-elk-definition-6af322e1-BFwFiPvq.js → flowchart-elk-definition-6af322e1-BRS2deVY.js} +6 -6
  18. package/app/out/renderer/assets/{ganttDiagram-a2739b55-D0-ehN-T.js → ganttDiagram-a2739b55-DRChnjlR.js} +3 -3
  19. package/app/out/renderer/assets/{gitGraphDiagram-82fe8481-DUyIR0Dv.js → gitGraphDiagram-82fe8481-BVfXECHD.js} +2 -2
  20. package/app/out/renderer/assets/{graph-DnTq2_3F.js → graph-Dl3bczvk.js} +1 -1
  21. package/app/out/renderer/assets/{index-5325376f-CBwuFbRF.js → index-5325376f-DjPany76.js} +6 -6
  22. package/app/out/renderer/assets/{index-u0FZRZON.js → index-6EerDbdL.js} +4 -4
  23. package/app/out/renderer/assets/{index-BHcU72Rm.js → index-B9tygoLQ.js} +3 -3
  24. package/app/out/renderer/assets/{index-DWU4ia28.js → index-BNdWI-dg.js} +6 -6
  25. package/app/out/renderer/assets/{index-D6r8msaQ.js → index-BPwe457Y.js} +3 -3
  26. package/app/out/renderer/assets/{index-DuhageEr.js → index-BQHeBYdD.js} +3 -3
  27. package/app/out/renderer/assets/{index-C1oXjI4L.js → index-BUUZSVeh.js} +3 -3
  28. package/app/out/renderer/assets/{index-Diy30-34.js → index-Bg-eDX1p.js} +4 -4
  29. package/app/out/renderer/assets/{index-CKXwBmK7.js → index-BkB91HdR.js} +5 -5
  30. package/app/out/renderer/assets/{index-BB-a1ajC.js → index-BlpusrHE.js} +136 -19
  31. package/app/out/renderer/assets/{index-gH-w4EHk.js → index-C226G81f.js} +3 -3
  32. package/app/out/renderer/assets/{index-DZbrRR7w.js → index-C4rx4LuD.js} +6 -6
  33. package/app/out/renderer/assets/{index-BpKrXGYD.js → index-C8-8M1o_.js} +3 -3
  34. package/app/out/renderer/assets/{index-CjffvluT.js → index-CLSRcr1i.js} +6 -6
  35. package/app/out/renderer/assets/{index-7hDGClrI.js → index-CMSALyTS.js} +3 -3
  36. package/app/out/renderer/assets/{index-yanwpi6t.js → index-CU9ZRB66.js} +6 -6
  37. package/app/out/renderer/assets/{index-bMe3RSkw.js → index-CgEKFTyJ.js} +6 -6
  38. package/app/out/renderer/assets/{index-D6jljsup.js → index-CllWzrq7.js} +3 -3
  39. package/app/out/renderer/assets/{index-ESFHcvWy.js → index-DHmQz4fM.js} +3 -3
  40. package/app/out/renderer/assets/{index-h_fNksib.js → index-DxziAmqO.js} +3 -3
  41. package/app/out/renderer/assets/{index-COZSDrEw.js → index-HZn90B-L.js} +6 -6
  42. package/app/out/renderer/assets/{index-CT1HtzVp.css → index-dNBQ09OL.css} +60 -0
  43. package/app/out/renderer/assets/{index-BQ7qz1CD.js → index-dvpto11c.js} +3 -3
  44. package/app/out/renderer/assets/{index-BVYoMX5H.js → index-hVorRCxO.js} +3 -3
  45. package/app/out/renderer/assets/{index-JT8OCsRP.js → index-nk-me1QW.js} +1 -1
  46. package/app/out/renderer/assets/{infoDiagram-8eee0895-Qra4japr.js → infoDiagram-8eee0895-CRzw5OpC.js} +2 -2
  47. package/app/out/renderer/assets/{journeyDiagram-c64418c1-BTN9SgOL.js → journeyDiagram-c64418c1-CYFcrwy9.js} +4 -4
  48. package/app/out/renderer/assets/{layout-DGrHHJdN.js → layout-2qUs4rWy.js} +2 -2
  49. package/app/out/renderer/assets/{line-DXtxdS2B.js → line-zEaIEY7C.js} +1 -1
  50. package/app/out/renderer/assets/{linear-CexrSQK6.js → linear-B-lut2jS.js} +1 -1
  51. package/app/out/renderer/assets/{mindmap-definition-8da855dc-pvG2hzEB.js → mindmap-definition-8da855dc--G6w66OU.js} +3 -3
  52. package/app/out/renderer/assets/{pieDiagram-a8764435-D_neFVMq.js → pieDiagram-a8764435-B1aj6YO1.js} +3 -3
  53. package/app/out/renderer/assets/{quadrantDiagram-1e28029f-C47W3UMp.js → quadrantDiagram-1e28029f-WcQ-5B6J.js} +3 -3
  54. package/app/out/renderer/assets/{requirementDiagram-08caed73-DW4Bo_fu.js → requirementDiagram-08caed73-DVS_F7Ld.js} +5 -5
  55. package/app/out/renderer/assets/{sankeyDiagram-a04cb91d-D_3PD7JI.js → sankeyDiagram-a04cb91d-C9DI8I5c.js} +2 -2
  56. package/app/out/renderer/assets/{sequenceDiagram-c5b8d532-BW6nGtuQ.js → sequenceDiagram-c5b8d532-BbjlXU_R.js} +3 -3
  57. package/app/out/renderer/assets/{stateDiagram-1ecb1508-CDgBJ3-T.js → stateDiagram-1ecb1508-D-RBhWue.js} +6 -6
  58. package/app/out/renderer/assets/{stateDiagram-v2-c2b004d7-CBw5TtXo.js → stateDiagram-v2-c2b004d7-5WeKw5B9.js} +10 -10
  59. package/app/out/renderer/assets/{styles-b4e223ce-DeeiEsuW.js → styles-b4e223ce-FiETMPKg.js} +1 -1
  60. package/app/out/renderer/assets/{styles-ca3715f6-CMpiebrG.js → styles-ca3715f6-DOyTWqN-.js} +1 -1
  61. package/app/out/renderer/assets/{styles-d45a18b0-CZe9hU7H.js → styles-d45a18b0-D2yoeLkD.js} +4 -4
  62. package/app/out/renderer/assets/{svgDrawCommon-b86b1483-CmJZfZzJ.js → svgDrawCommon-b86b1483-7THlOYFk.js} +1 -1
  63. package/app/out/renderer/assets/{timeline-definition-faaaa080-Beo2kiiz.js → timeline-definition-faaaa080-BVp4ikHz.js} +3 -3
  64. package/app/out/renderer/assets/{xychartDiagram-f5964ef8-DYmo7moz.js → xychartDiagram-f5964ef8-Cdp51x2C.js} +5 -5
  65. package/app/out/renderer/index.html +2 -2
  66. package/lib/skills/builtin/paper-revision/SKILL.md +467 -0
  67. package/lib/skills/builtin/paper-revision/references/evidence-strengthening.md +101 -0
  68. package/lib/skills/builtin/paper-revision/references/framing-patterns.md +119 -0
  69. package/lib/skills/builtin/paper-revision/references/reviewer-attack-catalog.md +171 -0
  70. package/lib/skills/builtin/paper-revision/references/venue-strategies.md +114 -0
  71. package/lib/skills/builtin/paper-writing/SKILL.md +1 -0
  72. package/package.json +4 -4
@@ -6841,7 +6841,8 @@ async function createCoordinator(config) {
6841
6841
  const routerByProvider = {
6842
6842
  anthropic: "claude-haiku-4-5-20251001",
6843
6843
  openai: "gpt-5.4-nano",
6844
- "openai-codex": "gpt-5.4-nano",
6844
+ "openai-codex": "gpt-5.4-mini",
6845
+ // nano not available in openai-codex provider
6845
6846
  google: "gemini-2.0-flash-lite"
6846
6847
  };
6847
6848
  let mainProvider = null;
@@ -7039,7 +7040,7 @@ The conversation continues below.`,
7039
7040
  });
7040
7041
  }
7041
7042
  async function clearSessionMemory() {
7042
- agent.clearMessages();
7043
+ agent.reset();
7043
7044
  compactionSummary = void 0;
7044
7045
  }
7045
7046
  async function maybeGenerateSummary() {
@@ -7096,7 +7097,7 @@ ${historyText}`,
7096
7097
  if (process.env.ENABLE_LOCAL_COMPUTE === "1") {
7097
7098
  probeStaticProfile().then((profile) => {
7098
7099
  const envGuidance = generateAgentGuidance(profile);
7099
- agent.setSystemPrompt(baseSystemPrompt + "\n\n" + envGuidance);
7100
+ agent.state.systemPrompt = baseSystemPrompt + "\n\n" + envGuidance;
7100
7101
  }).catch(() => {
7101
7102
  });
7102
7103
  }
@@ -7155,7 +7156,7 @@ ${historyText}`,
7155
7156
 
7156
7157
  ${agentMdContent}`;
7157
7158
  }
7158
- agent.setSystemPrompt(enrichedSystem);
7159
+ agent.state.systemPrompt = enrichedSystem;
7159
7160
  const contextParts = [];
7160
7161
  if (summaryContext) contextParts.push(summaryContext);
7161
7162
  if (skillSummariesPrompt) contextParts.push(skillSummariesPrompt);
@@ -7233,6 +7234,10 @@ ${message}` : message;
7233
7234
  }
7234
7235
  },
7235
7236
  clearSessionMemory,
7237
+ /** Stop the current LLM turn without tearing down tools. */
7238
+ abort() {
7239
+ agent.abort();
7240
+ },
7236
7241
  async destroy() {
7237
7242
  agent.abort();
7238
7243
  await destroyResearchTools();
@@ -7905,11 +7910,11 @@ function getCachedMarkdown(filePath, projectPath) {
7905
7910
  const mtime = stat.mtimeMs;
7906
7911
  const cacheDir = join(projectPath, PATHS.documentCache);
7907
7912
  const cacheKey2 = getCacheKey(filePath, mtime);
7908
- const cachePath2 = join(cacheDir, cacheKey2);
7909
- if (!existsSync(cachePath2)) {
7913
+ const cachePath = join(cacheDir, cacheKey2);
7914
+ if (!existsSync(cachePath)) {
7910
7915
  return null;
7911
7916
  }
7912
- const entry = JSON.parse(readFileSync(cachePath2, "utf-8"));
7917
+ const entry = JSON.parse(readFileSync(cachePath, "utf-8"));
7913
7918
  if (entry.sourcePath !== filePath || entry.sourceMtime !== mtime) {
7914
7919
  return null;
7915
7920
  }
@@ -7924,14 +7929,14 @@ function setCachedMarkdown(filePath, markdown, projectPath) {
7924
7929
  const mtime = stat.mtimeMs;
7925
7930
  const cacheDir = ensureCacheDir(projectPath);
7926
7931
  const cacheKey2 = getCacheKey(filePath, mtime);
7927
- const cachePath2 = join(cacheDir, cacheKey2);
7932
+ const cachePath = join(cacheDir, cacheKey2);
7928
7933
  const entry = {
7929
7934
  sourcePath: filePath,
7930
7935
  sourceMtime: mtime,
7931
7936
  markdown,
7932
7937
  cachedAt: (/* @__PURE__ */ new Date()).toISOString()
7933
7938
  };
7934
- writeFileSync(cachePath2, JSON.stringify(entry, null, 2), "utf-8");
7939
+ writeFileSync(cachePath, JSON.stringify(entry, null, 2), "utf-8");
7935
7940
  } catch (err) {
7936
7941
  console.warn("[document-cache] Failed to cache markdown:", err);
7937
7942
  }
@@ -8122,35 +8127,36 @@ const SKIP_DIRS = /* @__PURE__ */ new Set([
8122
8127
  ".venv",
8123
8128
  "venv"
8124
8129
  ]);
8125
- let cachedFiles = [];
8126
- let cachedProjectPath = "";
8127
- let lastRefreshAt = 0;
8128
- let refreshPromise = null;
8129
- async function getFileList(projectPath) {
8130
- if (projectPath !== cachedProjectPath) {
8131
- cachedFiles = [];
8132
- cachedProjectPath = projectPath;
8133
- lastRefreshAt = 0;
8130
+ const caches$1 = /* @__PURE__ */ new Map();
8131
+ function getOrCreateCache(projectPath) {
8132
+ let c = caches$1.get(projectPath);
8133
+ if (!c) {
8134
+ c = { files: [], lastRefreshAt: 0, refreshPromise: null };
8135
+ caches$1.set(projectPath, c);
8134
8136
  }
8137
+ return c;
8138
+ }
8139
+ async function getFileList(projectPath) {
8140
+ const c = getOrCreateCache(projectPath);
8135
8141
  const now = Date.now();
8136
- if (cachedFiles.length > 0 && now - lastRefreshAt < REFRESH_THROTTLE_MS) {
8137
- return cachedFiles;
8142
+ if (c.files.length > 0 && now - c.lastRefreshAt < REFRESH_THROTTLE_MS) {
8143
+ return c.files;
8138
8144
  }
8139
- if (refreshPromise) return refreshPromise;
8140
- refreshPromise = refreshFileList(projectPath).finally(() => {
8141
- refreshPromise = null;
8145
+ if (c.refreshPromise) return c.refreshPromise;
8146
+ c.refreshPromise = refreshFileList(projectPath, c).finally(() => {
8147
+ c.refreshPromise = null;
8142
8148
  });
8143
- return refreshPromise;
8149
+ return c.refreshPromise;
8144
8150
  }
8145
- async function refreshFileList(projectPath) {
8151
+ async function refreshFileList(projectPath, c) {
8146
8152
  try {
8147
8153
  const files = await gitLsFiles(projectPath);
8148
- cachedFiles = files.slice(0, MAX_FILES);
8154
+ c.files = files.slice(0, MAX_FILES);
8149
8155
  } catch {
8150
- cachedFiles = walkFilesSync(projectPath);
8156
+ c.files = walkFilesSync(projectPath);
8151
8157
  }
8152
- lastRefreshAt = Date.now();
8153
- return cachedFiles;
8158
+ c.lastRefreshAt = Date.now();
8159
+ return c.files;
8154
8160
  }
8155
8161
  function gitLsFiles(cwd) {
8156
8162
  return new Promise((resolve2, reject) => {
@@ -8200,20 +8206,24 @@ function walk(root, rel, depth, out) {
8200
8206
  }
8201
8207
  }
8202
8208
  }
8203
- let cache = null;
8204
- let cachePath = "";
8209
+ const caches = /* @__PURE__ */ new Map();
8205
8210
  function getEntityCache(projectPath) {
8206
- if (cache && cachePath === projectPath) return cache;
8207
- cache = {
8211
+ let c = caches.get(projectPath);
8212
+ if (c) return c;
8213
+ c = {
8208
8214
  notes: listNotes(projectPath),
8209
8215
  papers: listLiterature(projectPath),
8210
8216
  data: listData(projectPath)
8211
8217
  };
8212
- cachePath = projectPath;
8213
- return cache;
8218
+ caches.set(projectPath, c);
8219
+ return c;
8214
8220
  }
8215
- function invalidateEntityCache() {
8216
- cache = null;
8221
+ function invalidateEntityCache(projectPath) {
8222
+ if (projectPath) {
8223
+ caches.delete(projectPath);
8224
+ } else {
8225
+ caches.clear();
8226
+ }
8217
8227
  }
8218
8228
  const SCORE_MATCH = 16;
8219
8229
  const BONUS_FIRST_CHAR = 8;
@@ -9115,7 +9125,7 @@ async function ensureCoordinator(state, win, model, options) {
9115
9125
  if ((tool === "artifact-create" || tool === "artifact-update") && result && typeof result === "object" && "success" in result) {
9116
9126
  const r2 = result;
9117
9127
  if (r2.success) {
9118
- invalidateEntityCache();
9128
+ invalidateEntityCache(runProjectPath);
9119
9129
  if (tool === "artifact-create") {
9120
9130
  safeSend(win, "agent:entity-created", {
9121
9131
  type: r2.data?.type || "artifact",
@@ -9345,7 +9355,7 @@ function registerIpcHandlers() {
9345
9355
  });
9346
9356
  handleWindow("agent:stop", ({ state }) => {
9347
9357
  if (state.coordinator) {
9348
- state.coordinator.agent.stop();
9358
+ state.coordinator.abort();
9349
9359
  }
9350
9360
  });
9351
9361
  handleWindow("agent:clear-memory", async ({ state }) => {
@@ -9371,15 +9381,25 @@ function registerIpcHandlers() {
9371
9381
  });
9372
9382
  handleWindow("cmd:delete", ({ state }, id) => {
9373
9383
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
9374
- return deleteEntity(id, state.projectPath);
9384
+ const result = deleteEntity(id, state.projectPath);
9385
+ invalidateEntityCache(state.projectPath);
9386
+ return result;
9375
9387
  });
9376
9388
  handleWindow("cmd:artifact-create", ({ state }, input) => {
9377
9389
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
9378
- return artifactCreate(input, { sessionId: state.sessionId, projectPath: state.projectPath });
9390
+ const result = artifactCreate(input, { sessionId: state.sessionId, projectPath: state.projectPath });
9391
+ if (result && typeof result === "object" && "success" in result && result.success) {
9392
+ invalidateEntityCache(state.projectPath);
9393
+ }
9394
+ return result;
9379
9395
  });
9380
9396
  handleWindow("cmd:artifact-update", ({ state }, artifactId, patch) => {
9381
9397
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
9382
- return artifactUpdate(state.projectPath, artifactId, patch);
9398
+ const result = artifactUpdate(state.projectPath, artifactId, patch);
9399
+ if (result && typeof result === "object" && "success" in result && result.success) {
9400
+ invalidateEntityCache(state.projectPath);
9401
+ }
9402
+ return result;
9383
9403
  });
9384
9404
  handleWindow("cmd:artifact-get", ({ state }, artifactId) => {
9385
9405
  if (!state.projectPath) return null;
@@ -9395,7 +9415,11 @@ function registerIpcHandlers() {
9395
9415
  });
9396
9416
  handleWindow("cmd:artifact-delete", ({ state }, artifactId) => {
9397
9417
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
9398
- return artifactDelete(state.projectPath, artifactId);
9418
+ const result = artifactDelete(state.projectPath, artifactId);
9419
+ if (result && typeof result === "object" && "success" in result && result.success) {
9420
+ invalidateEntityCache(state.projectPath);
9421
+ }
9422
+ return result;
9399
9423
  });
9400
9424
  handleWindow("cmd:turn-explain-get", ({ state }) => {
9401
9425
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
@@ -9741,16 +9765,56 @@ function registerIpcHandlers() {
9741
9765
  return null;
9742
9766
  }
9743
9767
  });
9768
+ handleWindow("chat:export", async ({ win, state }) => {
9769
+ if (!state.projectPath || !state.sessionId) {
9770
+ return { success: false, error: "No project open" };
9771
+ }
9772
+ const file = join(state.projectPath, PATHS.sessions, `${state.sessionId}.jsonl`);
9773
+ if (!existsSync(file)) {
9774
+ return { success: false, error: "No chat history found" };
9775
+ }
9776
+ const lines = readFileSync(file, "utf-8").split("\n").filter(Boolean);
9777
+ if (lines.length === 0) {
9778
+ return { success: false, error: "Chat history is empty" };
9779
+ }
9780
+ const projectName = basename(state.projectPath);
9781
+ const mdParts = [`# Chat Export — ${projectName}
9782
+ `];
9783
+ mdParts.push(`> Exported on ${(/* @__PURE__ */ new Date()).toLocaleString()}
9784
+ `);
9785
+ for (const line of lines) {
9786
+ try {
9787
+ const msg = JSON.parse(line);
9788
+ const time = msg.timestamp ? new Date(msg.timestamp).toLocaleString() : "";
9789
+ const roleLabel = msg.role === "user" ? "**You**" : msg.role === "assistant" ? "**Assistant**" : "**System**";
9790
+ mdParts.push(`---
9791
+
9792
+ ### ${roleLabel}
9793
+ <sub>${time}</sub>
9794
+
9795
+ ${msg.content}
9796
+ `);
9797
+ } catch {
9798
+ }
9799
+ }
9800
+ const markdown = mdParts.join("\n");
9801
+ const result = await dialog.showSaveDialog(win, {
9802
+ title: "Export Chat History",
9803
+ defaultPath: join(app.getPath("documents"), `${projectName}-chat-export.md`),
9804
+ filters: [{ name: "Markdown", extensions: ["md"] }]
9805
+ });
9806
+ if (result.canceled || !result.filePath) {
9807
+ return { success: false, error: "Cancelled" };
9808
+ }
9809
+ writeFileSync(result.filePath, markdown, "utf-8");
9810
+ return { success: true, path: result.filePath };
9811
+ });
9744
9812
  handleWindow("project:pick-folder", async ({ win, state }) => {
9745
9813
  const result = await dialog.showOpenDialog(win, {
9746
9814
  properties: ["openDirectory", "createDirectory"]
9747
9815
  });
9748
9816
  if (!result.canceled && result.filePaths[0]) {
9749
9817
  if (state.coordinator) {
9750
- try {
9751
- state.coordinator.agent.stop();
9752
- } catch {
9753
- }
9754
9818
  try {
9755
9819
  await state.coordinator.destroy();
9756
9820
  } catch {
@@ -9792,6 +9856,7 @@ function registerIpcHandlers() {
9792
9856
  }).catch(() => {
9793
9857
  });
9794
9858
  }
9859
+ win.setTitle(basename(state.projectPath));
9795
9860
  return { projectPath: state.projectPath, sessionId: state.sessionId };
9796
9861
  }
9797
9862
  return null;
@@ -9801,8 +9866,7 @@ function registerIpcHandlers() {
9801
9866
  try {
9802
9867
  if (state.coordinator) {
9803
9868
  try {
9804
- ;
9805
- state.coordinator.agent.stop();
9869
+ state.coordinator.abort();
9806
9870
  } catch {
9807
9871
  }
9808
9872
  }
@@ -9817,6 +9881,8 @@ function registerIpcHandlers() {
9817
9881
  state.realtimeBuffer.reset();
9818
9882
  state.projectPath = "";
9819
9883
  state.sessionId = crypto.randomUUID();
9884
+ const win = BrowserWindow.getAllWindows().find((w) => windowStates.get(w.webContents.id) === state);
9885
+ if (win) win.setTitle("Research Pilot");
9820
9886
  state.currentModel = "openai:gpt-5.4";
9821
9887
  state.currentReasoningEffort = "medium";
9822
9888
  state.currentAuthMode = "none";
@@ -10012,6 +10078,15 @@ function buildMenu() {
10012
10078
  }
10013
10079
  },
10014
10080
  { type: "separator" },
10081
+ {
10082
+ label: "Export Chat…",
10083
+ accelerator: "CmdOrCtrl+Shift+E",
10084
+ click: () => {
10085
+ const target = BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0];
10086
+ if (target) target.webContents.send("menu:export-chat");
10087
+ }
10088
+ },
10089
+ { type: "separator" },
10015
10090
  {
10016
10091
  label: "Close Project",
10017
10092
  accelerator: "CmdOrCtrl+Shift+K",
@@ -143,6 +143,12 @@ const api = {
143
143
  setEnabledSkills: (enabledSkills) => electron.ipcRenderer.invoke("skills:set-enabled", enabledSkills),
144
144
  uploadSkill: (fileName, base64Data) => electron.ipcRenderer.invoke("skills:upload", fileName, base64Data),
145
145
  convertFileToText: (fileName, base64Data) => electron.ipcRenderer.invoke("file:convert-to-text", fileName, base64Data),
146
+ exportChat: () => electron.ipcRenderer.invoke("chat:export"),
147
+ onExportChat: (cb) => {
148
+ const handler = () => cb();
149
+ electron.ipcRenderer.on("menu:export-chat", handler);
150
+ return () => electron.ipcRenderer.removeListener("menu:export-chat", handler);
151
+ },
146
152
  saveMessage: (sessionId, msg) => electron.ipcRenderer.invoke("session:save-message", sessionId, msg),
147
153
  loadMessages: (sessionId, offset, limit) => electron.ipcRenderer.invoke("session:load-messages", sessionId, offset, limit),
148
154
  getMessageCount: (sessionId) => electron.ipcRenderer.invoke("session:get-total-count", sessionId),