research-copilot 0.2.3 → 0.2.5

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 +154 -48
  2. package/app/out/preload/index.js +7 -0
  3. package/app/out/renderer/assets/{MilkdownMarkdownEditor-DjFLwyh4.js → MilkdownMarkdownEditor-2wg63TSu.js} +50 -50
  4. package/app/out/renderer/assets/{arc-7NllWjXF.js → arc-Bpvizsn8.js} +1 -1
  5. package/app/out/renderer/assets/{blockDiagram-c4efeb88-DsOU-_zA.js → blockDiagram-c4efeb88-BopVnbxX.js} +8 -8
  6. package/app/out/renderer/assets/{c4Diagram-c83219d4-U2jdevgc.js → c4Diagram-c83219d4-DYK03hOG.js} +3 -3
  7. package/app/out/renderer/assets/{channel-Cjd8jvi8.js → channel-CmlMCRa3.js} +1 -1
  8. package/app/out/renderer/assets/{classDiagram-beda092f-Yj0hyRiU.js → classDiagram-beda092f-oSGZIrY_.js} +6 -6
  9. package/app/out/renderer/assets/{classDiagram-v2-2358418a-CK0FL8Pk.js → classDiagram-v2-2358418a-BHM8844_.js} +10 -10
  10. package/app/out/renderer/assets/{clone-I16owcK3.js → clone-PITMMgBv.js} +1 -1
  11. package/app/out/renderer/assets/{createText-1719965b-D_GY3BGW.js → createText-1719965b-BN3FKtRi.js} +2 -2
  12. package/app/out/renderer/assets/{edges-96097737-DKD2znJk.js → edges-96097737-76OqZvY1.js} +3 -3
  13. package/app/out/renderer/assets/{erDiagram-0228fc6a-CxrCv4uP.js → erDiagram-0228fc6a-CtXOkjA1.js} +5 -5
  14. package/app/out/renderer/assets/{flowDb-c6c81e3f-8o4wFApi.js → flowDb-c6c81e3f-BCT1eQU9.js} +1 -1
  15. package/app/out/renderer/assets/{flowDiagram-50d868cf-B1eYKlZC.js → flowDiagram-50d868cf-BKR9gSZL.js} +12 -12
  16. package/app/out/renderer/assets/{flowDiagram-v2-4f6560a1-Y4_T8Xi1.js → flowDiagram-v2-4f6560a1-CBGrwW2p.js} +12 -12
  17. package/app/out/renderer/assets/{flowchart-elk-definition-6af322e1-BjDPU2Ol.js → flowchart-elk-definition-6af322e1-CQxHOjqj.js} +6 -6
  18. package/app/out/renderer/assets/{ganttDiagram-a2739b55-CdQAT7up.js → ganttDiagram-a2739b55-BVsEi_1s.js} +3 -3
  19. package/app/out/renderer/assets/{gitGraphDiagram-82fe8481-Bl7KP_G5.js → gitGraphDiagram-82fe8481-BlGuagiw.js} +2 -2
  20. package/app/out/renderer/assets/{graph-Bje_pjwC.js → graph-B-Uy9LVb.js} +1 -1
  21. package/app/out/renderer/assets/{index-5325376f-DB5UfMSQ.js → index-5325376f-DFVM9bEE.js} +6 -6
  22. package/app/out/renderer/assets/{index-BnGN3XMZ.js → index-9DibXU9f.js} +6 -6
  23. package/app/out/renderer/assets/{index-BWkpc_Fb.js → index-B8_hOntP.js} +3 -3
  24. package/app/out/renderer/assets/{index-TtJcntKb.js → index-BRtK-r6R.js} +3 -3
  25. package/app/out/renderer/assets/{index-CU1ei-Q-.js → index-BW7eNq5J.js} +6 -6
  26. package/app/out/renderer/assets/{index-BlSmCt8A.js → index-BcKf35dV.js} +6 -6
  27. package/app/out/renderer/assets/{index-CT1HtzVp.css → index-BllgkT3a.css} +83 -0
  28. package/app/out/renderer/assets/{index-CTrj2aB2.js → index-BrQ_Y-LE.js} +3 -3
  29. package/app/out/renderer/assets/{index-BX5-hWqk.js → index-C0Czl4Cv.js} +3 -3
  30. package/app/out/renderer/assets/{index-BHl_tELv.js → index-CoEolmNf.js} +5 -5
  31. package/app/out/renderer/assets/{index-CISiP4Vc.js → index-DFnA5GRw.js} +255 -63
  32. package/app/out/renderer/assets/{index-CDWVrn8L.js → index-Dk7Wa0Je.js} +3 -3
  33. package/app/out/renderer/assets/{index-UYsogkLv.js → index-DkVOuuVx.js} +6 -6
  34. package/app/out/renderer/assets/{index-bPpSppnq.js → index-DlRaaYEU.js} +3 -3
  35. package/app/out/renderer/assets/{index-K7KmUSPg.js → index-DlUgEE0Z.js} +3 -3
  36. package/app/out/renderer/assets/{index-D9YGyBA9.js → index-Dt57Xb0R.js} +3 -3
  37. package/app/out/renderer/assets/{index-DCr-i28b.js → index-PVPY9npp.js} +3 -3
  38. package/app/out/renderer/assets/{index-CoIRGlb6.js → index-SoluVSbR.js} +3 -3
  39. package/app/out/renderer/assets/{index-DihmTRDX.js → index-cLiUsMmu.js} +6 -6
  40. package/app/out/renderer/assets/{index-hI44wokU.js → index-i5J_uYOr.js} +6 -6
  41. package/app/out/renderer/assets/{index-DIB3jH2J.js → index-j9e1ML36.js} +1 -1
  42. package/app/out/renderer/assets/{index-BbbK94Tl.js → index-jJGUNJNs.js} +4 -4
  43. package/app/out/renderer/assets/{index-DkTtiwdL.js → index-jMygKcX_.js} +3 -3
  44. package/app/out/renderer/assets/{index-DRgsVAez.js → index-rMj6TAlT.js} +4 -4
  45. package/app/out/renderer/assets/{index-DFmh36hv.js → index-wvsLwV9F.js} +3 -3
  46. package/app/out/renderer/assets/{infoDiagram-8eee0895-Dfty_4jM.js → infoDiagram-8eee0895-C6WcmMAB.js} +2 -2
  47. package/app/out/renderer/assets/{journeyDiagram-c64418c1-BzDYpsjx.js → journeyDiagram-c64418c1-wkVqZcQW.js} +4 -4
  48. package/app/out/renderer/assets/{layout-DbHSNFr9.js → layout-C6Q0z3G5.js} +2 -2
  49. package/app/out/renderer/assets/{line-jV7QLjZ_.js → line-BDJIIoze.js} +1 -1
  50. package/app/out/renderer/assets/{linear-CIeuNnVa.js → linear-95dGQxH4.js} +1 -1
  51. package/app/out/renderer/assets/{mindmap-definition-8da855dc-CeDEmTB-.js → mindmap-definition-8da855dc-DWlj4Xm-.js} +3 -3
  52. package/app/out/renderer/assets/{pieDiagram-a8764435-Bpm1FcX3.js → pieDiagram-a8764435-CrdnVfOv.js} +3 -3
  53. package/app/out/renderer/assets/{quadrantDiagram-1e28029f-CEB7PxJa.js → quadrantDiagram-1e28029f-M3YRJdbH.js} +3 -3
  54. package/app/out/renderer/assets/{requirementDiagram-08caed73-CZJU8B_1.js → requirementDiagram-08caed73-C08ql71g.js} +5 -5
  55. package/app/out/renderer/assets/{sankeyDiagram-a04cb91d-JXpwgeE0.js → sankeyDiagram-a04cb91d-C8ADDrm7.js} +2 -2
  56. package/app/out/renderer/assets/{sequenceDiagram-c5b8d532-CtvDQG5X.js → sequenceDiagram-c5b8d532-gNfcaPex.js} +3 -3
  57. package/app/out/renderer/assets/{stateDiagram-1ecb1508-DQRUQm_b.js → stateDiagram-1ecb1508-CbYo7fPM.js} +6 -6
  58. package/app/out/renderer/assets/{stateDiagram-v2-c2b004d7-qWcRpqpV.js → stateDiagram-v2-c2b004d7-C9NIgZa5.js} +10 -10
  59. package/app/out/renderer/assets/{styles-b4e223ce-6DWryYQB.js → styles-b4e223ce-DgvzV6cM.js} +1 -1
  60. package/app/out/renderer/assets/{styles-ca3715f6-CZ9SeO17.js → styles-ca3715f6-Dxd4UtQU.js} +1 -1
  61. package/app/out/renderer/assets/{styles-d45a18b0-Bkmie_Qv.js → styles-d45a18b0-C0jzXR3J.js} +4 -4
  62. package/app/out/renderer/assets/{svgDrawCommon-b86b1483-BWRX1ZPq.js → svgDrawCommon-b86b1483-CjGKGyYF.js} +1 -1
  63. package/app/out/renderer/assets/{timeline-definition-faaaa080-XcgfjspK.js → timeline-definition-faaaa080-CRz7hPUx.js} +3 -3
  64. package/app/out/renderer/assets/{xychartDiagram-f5964ef8-C0CQzgr8.js → xychartDiagram-f5964ef8-BI-asLSS.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
@@ -7040,7 +7040,7 @@ The conversation continues below.`,
7040
7040
  });
7041
7041
  }
7042
7042
  async function clearSessionMemory() {
7043
- agent.clearMessages();
7043
+ agent.reset();
7044
7044
  compactionSummary = void 0;
7045
7045
  }
7046
7046
  async function maybeGenerateSummary() {
@@ -7097,7 +7097,7 @@ ${historyText}`,
7097
7097
  if (process.env.ENABLE_LOCAL_COMPUTE === "1") {
7098
7098
  probeStaticProfile().then((profile) => {
7099
7099
  const envGuidance = generateAgentGuidance(profile);
7100
- agent.setSystemPrompt(baseSystemPrompt + "\n\n" + envGuidance);
7100
+ agent.state.systemPrompt = baseSystemPrompt + "\n\n" + envGuidance;
7101
7101
  }).catch(() => {
7102
7102
  });
7103
7103
  }
@@ -7156,7 +7156,7 @@ ${historyText}`,
7156
7156
 
7157
7157
  ${agentMdContent}`;
7158
7158
  }
7159
- agent.setSystemPrompt(enrichedSystem);
7159
+ agent.state.systemPrompt = enrichedSystem;
7160
7160
  const contextParts = [];
7161
7161
  if (summaryContext) contextParts.push(summaryContext);
7162
7162
  if (skillSummariesPrompt) contextParts.push(skillSummariesPrompt);
@@ -7234,6 +7234,10 @@ ${message}` : message;
7234
7234
  }
7235
7235
  },
7236
7236
  clearSessionMemory,
7237
+ /** Stop the current LLM turn without tearing down tools. */
7238
+ abort() {
7239
+ agent.abort();
7240
+ },
7237
7241
  async destroy() {
7238
7242
  agent.abort();
7239
7243
  await destroyResearchTools();
@@ -7906,11 +7910,11 @@ function getCachedMarkdown(filePath, projectPath) {
7906
7910
  const mtime = stat.mtimeMs;
7907
7911
  const cacheDir = join(projectPath, PATHS.documentCache);
7908
7912
  const cacheKey2 = getCacheKey(filePath, mtime);
7909
- const cachePath2 = join(cacheDir, cacheKey2);
7910
- if (!existsSync(cachePath2)) {
7913
+ const cachePath = join(cacheDir, cacheKey2);
7914
+ if (!existsSync(cachePath)) {
7911
7915
  return null;
7912
7916
  }
7913
- const entry = JSON.parse(readFileSync(cachePath2, "utf-8"));
7917
+ const entry = JSON.parse(readFileSync(cachePath, "utf-8"));
7914
7918
  if (entry.sourcePath !== filePath || entry.sourceMtime !== mtime) {
7915
7919
  return null;
7916
7920
  }
@@ -7925,14 +7929,14 @@ function setCachedMarkdown(filePath, markdown, projectPath) {
7925
7929
  const mtime = stat.mtimeMs;
7926
7930
  const cacheDir = ensureCacheDir(projectPath);
7927
7931
  const cacheKey2 = getCacheKey(filePath, mtime);
7928
- const cachePath2 = join(cacheDir, cacheKey2);
7932
+ const cachePath = join(cacheDir, cacheKey2);
7929
7933
  const entry = {
7930
7934
  sourcePath: filePath,
7931
7935
  sourceMtime: mtime,
7932
7936
  markdown,
7933
7937
  cachedAt: (/* @__PURE__ */ new Date()).toISOString()
7934
7938
  };
7935
- writeFileSync(cachePath2, JSON.stringify(entry, null, 2), "utf-8");
7939
+ writeFileSync(cachePath, JSON.stringify(entry, null, 2), "utf-8");
7936
7940
  } catch (err) {
7937
7941
  console.warn("[document-cache] Failed to cache markdown:", err);
7938
7942
  }
@@ -8123,35 +8127,36 @@ const SKIP_DIRS = /* @__PURE__ */ new Set([
8123
8127
  ".venv",
8124
8128
  "venv"
8125
8129
  ]);
8126
- let cachedFiles = [];
8127
- let cachedProjectPath = "";
8128
- let lastRefreshAt = 0;
8129
- let refreshPromise = null;
8130
- async function getFileList(projectPath) {
8131
- if (projectPath !== cachedProjectPath) {
8132
- cachedFiles = [];
8133
- cachedProjectPath = projectPath;
8134
- 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);
8135
8136
  }
8137
+ return c;
8138
+ }
8139
+ async function getFileList(projectPath) {
8140
+ const c = getOrCreateCache(projectPath);
8136
8141
  const now = Date.now();
8137
- if (cachedFiles.length > 0 && now - lastRefreshAt < REFRESH_THROTTLE_MS) {
8138
- return cachedFiles;
8142
+ if (c.files.length > 0 && now - c.lastRefreshAt < REFRESH_THROTTLE_MS) {
8143
+ return c.files;
8139
8144
  }
8140
- if (refreshPromise) return refreshPromise;
8141
- refreshPromise = refreshFileList(projectPath).finally(() => {
8142
- refreshPromise = null;
8145
+ if (c.refreshPromise) return c.refreshPromise;
8146
+ c.refreshPromise = refreshFileList(projectPath, c).finally(() => {
8147
+ c.refreshPromise = null;
8143
8148
  });
8144
- return refreshPromise;
8149
+ return c.refreshPromise;
8145
8150
  }
8146
- async function refreshFileList(projectPath) {
8151
+ async function refreshFileList(projectPath, c) {
8147
8152
  try {
8148
8153
  const files = await gitLsFiles(projectPath);
8149
- cachedFiles = files.slice(0, MAX_FILES);
8154
+ c.files = files.slice(0, MAX_FILES);
8150
8155
  } catch {
8151
- cachedFiles = walkFilesSync(projectPath);
8156
+ c.files = walkFilesSync(projectPath);
8152
8157
  }
8153
- lastRefreshAt = Date.now();
8154
- return cachedFiles;
8158
+ c.lastRefreshAt = Date.now();
8159
+ return c.files;
8155
8160
  }
8156
8161
  function gitLsFiles(cwd) {
8157
8162
  return new Promise((resolve2, reject) => {
@@ -8201,20 +8206,24 @@ function walk(root, rel, depth, out) {
8201
8206
  }
8202
8207
  }
8203
8208
  }
8204
- let cache = null;
8205
- let cachePath = "";
8209
+ const caches = /* @__PURE__ */ new Map();
8206
8210
  function getEntityCache(projectPath) {
8207
- if (cache && cachePath === projectPath) return cache;
8208
- cache = {
8211
+ let c = caches.get(projectPath);
8212
+ if (c) return c;
8213
+ c = {
8209
8214
  notes: listNotes(projectPath),
8210
8215
  papers: listLiterature(projectPath),
8211
8216
  data: listData(projectPath)
8212
8217
  };
8213
- cachePath = projectPath;
8214
- return cache;
8218
+ caches.set(projectPath, c);
8219
+ return c;
8215
8220
  }
8216
- function invalidateEntityCache() {
8217
- cache = null;
8221
+ function invalidateEntityCache(projectPath) {
8222
+ if (projectPath) {
8223
+ caches.delete(projectPath);
8224
+ } else {
8225
+ caches.clear();
8226
+ }
8218
8227
  }
8219
8228
  const SCORE_MATCH = 16;
8220
8229
  const BONUS_FIRST_CHAR = 8;
@@ -8866,6 +8875,15 @@ function resetUsageTotals(baseDir) {
8866
8875
  writeAtomically(usagePath(baseDir), JSON.stringify(cleared, null, 2));
8867
8876
  return cleared;
8868
8877
  }
8878
+ function compareVersions(a, b) {
8879
+ const pa = a.split(".").map(Number);
8880
+ const pb = b.split(".").map(Number);
8881
+ for (let i = 0; i < 3; i++) {
8882
+ const diff = (pa[i] || 0) - (pb[i] || 0);
8883
+ if (diff !== 0) return diff;
8884
+ }
8885
+ return 0;
8886
+ }
8869
8887
  function formatToolCall(tool, args) {
8870
8888
  const a = args && typeof args === "object" ? args : {};
8871
8889
  const config = getToolRenderConfig(tool);
@@ -9116,7 +9134,7 @@ async function ensureCoordinator(state, win, model, options) {
9116
9134
  if ((tool === "artifact-create" || tool === "artifact-update") && result && typeof result === "object" && "success" in result) {
9117
9135
  const r2 = result;
9118
9136
  if (r2.success) {
9119
- invalidateEntityCache();
9137
+ invalidateEntityCache(runProjectPath);
9120
9138
  if (tool === "artifact-create") {
9121
9139
  safeSend(win, "agent:entity-created", {
9122
9140
  type: r2.data?.type || "artifact",
@@ -9261,6 +9279,29 @@ async function destroyAllCoordinators() {
9261
9279
  function registerIpcHandlers() {
9262
9280
  if (ipcHandlersRegistered) return;
9263
9281
  ipcHandlersRegistered = true;
9282
+ let cachedUpdateInfo = null;
9283
+ const checkForUpdate = async () => {
9284
+ if (cachedUpdateInfo) return cachedUpdateInfo;
9285
+ try {
9286
+ const res = await fetch("https://registry.npmjs.org/research-copilot/latest", {
9287
+ signal: AbortSignal.timeout(5e3)
9288
+ });
9289
+ if (!res.ok) throw new Error(`npm registry returned ${res.status}`);
9290
+ const data = await res.json();
9291
+ const latest = data.version;
9292
+ const current = app.getVersion();
9293
+ const hasUpdate = latest !== current && compareVersions(latest, current) > 0;
9294
+ cachedUpdateInfo = { latest, current, hasUpdate };
9295
+ return cachedUpdateInfo;
9296
+ } catch {
9297
+ const current = app.getVersion();
9298
+ cachedUpdateInfo = { latest: current, current, hasUpdate: false };
9299
+ return cachedUpdateInfo;
9300
+ }
9301
+ };
9302
+ ipcMain.handle("app:check-update", () => checkForUpdate());
9303
+ checkForUpdate().catch(() => {
9304
+ });
9264
9305
  const builtinSkillsRoot = app.isPackaged ? join(process.resourcesPath, "skills") : join(__dirname, "..", "..", "..", "lib", "skills");
9265
9306
  setBuiltinSkillsRoot(builtinSkillsRoot);
9266
9307
  const handleWindow = (channel, handler) => {
@@ -9346,7 +9387,7 @@ function registerIpcHandlers() {
9346
9387
  });
9347
9388
  handleWindow("agent:stop", ({ state }) => {
9348
9389
  if (state.coordinator) {
9349
- state.coordinator.agent.stop();
9390
+ state.coordinator.abort();
9350
9391
  }
9351
9392
  });
9352
9393
  handleWindow("agent:clear-memory", async ({ state }) => {
@@ -9372,15 +9413,25 @@ function registerIpcHandlers() {
9372
9413
  });
9373
9414
  handleWindow("cmd:delete", ({ state }, id) => {
9374
9415
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
9375
- return deleteEntity(id, state.projectPath);
9416
+ const result = deleteEntity(id, state.projectPath);
9417
+ invalidateEntityCache(state.projectPath);
9418
+ return result;
9376
9419
  });
9377
9420
  handleWindow("cmd:artifact-create", ({ state }, input) => {
9378
9421
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
9379
- return artifactCreate(input, { sessionId: state.sessionId, projectPath: state.projectPath });
9422
+ const result = artifactCreate(input, { sessionId: state.sessionId, projectPath: state.projectPath });
9423
+ if (result && typeof result === "object" && "success" in result && result.success) {
9424
+ invalidateEntityCache(state.projectPath);
9425
+ }
9426
+ return result;
9380
9427
  });
9381
9428
  handleWindow("cmd:artifact-update", ({ state }, artifactId, patch) => {
9382
9429
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
9383
- return artifactUpdate(state.projectPath, artifactId, patch);
9430
+ const result = artifactUpdate(state.projectPath, artifactId, patch);
9431
+ if (result && typeof result === "object" && "success" in result && result.success) {
9432
+ invalidateEntityCache(state.projectPath);
9433
+ }
9434
+ return result;
9384
9435
  });
9385
9436
  handleWindow("cmd:artifact-get", ({ state }, artifactId) => {
9386
9437
  if (!state.projectPath) return null;
@@ -9396,7 +9447,11 @@ function registerIpcHandlers() {
9396
9447
  });
9397
9448
  handleWindow("cmd:artifact-delete", ({ state }, artifactId) => {
9398
9449
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
9399
- return artifactDelete(state.projectPath, artifactId);
9450
+ const result = artifactDelete(state.projectPath, artifactId);
9451
+ if (result && typeof result === "object" && "success" in result && result.success) {
9452
+ invalidateEntityCache(state.projectPath);
9453
+ }
9454
+ return result;
9400
9455
  });
9401
9456
  handleWindow("cmd:turn-explain-get", ({ state }) => {
9402
9457
  if (!state.projectPath) return { success: false, error: "No project folder selected." };
@@ -9742,16 +9797,56 @@ function registerIpcHandlers() {
9742
9797
  return null;
9743
9798
  }
9744
9799
  });
9800
+ handleWindow("chat:export", async ({ win, state }) => {
9801
+ if (!state.projectPath || !state.sessionId) {
9802
+ return { success: false, error: "No project open" };
9803
+ }
9804
+ const file = join(state.projectPath, PATHS.sessions, `${state.sessionId}.jsonl`);
9805
+ if (!existsSync(file)) {
9806
+ return { success: false, error: "No chat history found" };
9807
+ }
9808
+ const lines = readFileSync(file, "utf-8").split("\n").filter(Boolean);
9809
+ if (lines.length === 0) {
9810
+ return { success: false, error: "Chat history is empty" };
9811
+ }
9812
+ const projectName = basename(state.projectPath);
9813
+ const mdParts = [`# Chat Export — ${projectName}
9814
+ `];
9815
+ mdParts.push(`> Exported on ${(/* @__PURE__ */ new Date()).toLocaleString()}
9816
+ `);
9817
+ for (const line of lines) {
9818
+ try {
9819
+ const msg = JSON.parse(line);
9820
+ const time = msg.timestamp ? new Date(msg.timestamp).toLocaleString() : "";
9821
+ const roleLabel = msg.role === "user" ? "**You**" : msg.role === "assistant" ? "**Assistant**" : "**System**";
9822
+ mdParts.push(`---
9823
+
9824
+ ### ${roleLabel}
9825
+ <sub>${time}</sub>
9826
+
9827
+ ${msg.content}
9828
+ `);
9829
+ } catch {
9830
+ }
9831
+ }
9832
+ const markdown = mdParts.join("\n");
9833
+ const result = await dialog.showSaveDialog(win, {
9834
+ title: "Export Chat History",
9835
+ defaultPath: join(app.getPath("documents"), `${projectName}-chat-export.md`),
9836
+ filters: [{ name: "Markdown", extensions: ["md"] }]
9837
+ });
9838
+ if (result.canceled || !result.filePath) {
9839
+ return { success: false, error: "Cancelled" };
9840
+ }
9841
+ writeFileSync(result.filePath, markdown, "utf-8");
9842
+ return { success: true, path: result.filePath };
9843
+ });
9745
9844
  handleWindow("project:pick-folder", async ({ win, state }) => {
9746
9845
  const result = await dialog.showOpenDialog(win, {
9747
9846
  properties: ["openDirectory", "createDirectory"]
9748
9847
  });
9749
9848
  if (!result.canceled && result.filePaths[0]) {
9750
9849
  if (state.coordinator) {
9751
- try {
9752
- state.coordinator.agent.stop();
9753
- } catch {
9754
- }
9755
9850
  try {
9756
9851
  await state.coordinator.destroy();
9757
9852
  } catch {
@@ -9793,6 +9888,7 @@ function registerIpcHandlers() {
9793
9888
  }).catch(() => {
9794
9889
  });
9795
9890
  }
9891
+ win.setTitle(basename(state.projectPath));
9796
9892
  return { projectPath: state.projectPath, sessionId: state.sessionId };
9797
9893
  }
9798
9894
  return null;
@@ -9802,8 +9898,7 @@ function registerIpcHandlers() {
9802
9898
  try {
9803
9899
  if (state.coordinator) {
9804
9900
  try {
9805
- ;
9806
- state.coordinator.agent.stop();
9901
+ state.coordinator.abort();
9807
9902
  } catch {
9808
9903
  }
9809
9904
  }
@@ -9818,6 +9913,8 @@ function registerIpcHandlers() {
9818
9913
  state.realtimeBuffer.reset();
9819
9914
  state.projectPath = "";
9820
9915
  state.sessionId = crypto.randomUUID();
9916
+ const win = BrowserWindow.getAllWindows().find((w) => windowStates.get(w.webContents.id) === state);
9917
+ if (win) win.setTitle("Research Pilot");
9821
9918
  state.currentModel = "openai:gpt-5.4";
9822
9919
  state.currentReasoningEffort = "medium";
9823
9920
  state.currentAuthMode = "none";
@@ -10013,6 +10110,15 @@ function buildMenu() {
10013
10110
  }
10014
10111
  },
10015
10112
  { type: "separator" },
10113
+ {
10114
+ label: "Export Chat…",
10115
+ accelerator: "CmdOrCtrl+Shift+E",
10116
+ click: () => {
10117
+ const target = BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0];
10118
+ if (target) target.webContents.send("menu:export-chat");
10119
+ }
10120
+ },
10121
+ { type: "separator" },
10016
10122
  {
10017
10123
  label: "Close Project",
10018
10124
  accelerator: "CmdOrCtrl+Shift+K",
@@ -143,6 +143,13 @@ 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
+ checkForUpdate: () => electron.ipcRenderer.invoke("app:check-update"),
147
+ exportChat: () => electron.ipcRenderer.invoke("chat:export"),
148
+ onExportChat: (cb) => {
149
+ const handler = () => cb();
150
+ electron.ipcRenderer.on("menu:export-chat", handler);
151
+ return () => electron.ipcRenderer.removeListener("menu:export-chat", handler);
152
+ },
146
153
  saveMessage: (sessionId, msg) => electron.ipcRenderer.invoke("session:save-message", sessionId, msg),
147
154
  loadMessages: (sessionId, offset, limit) => electron.ipcRenderer.invoke("session:load-messages", sessionId, offset, limit),
148
155
  getMessageCount: (sessionId) => electron.ipcRenderer.invoke("session:get-total-count", sessionId),