@uniqueli/openwork 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/out/main/index.js CHANGED
@@ -107,7 +107,9 @@ const ENV_VAR_NAMES = {
107
107
  anthropic: "ANTHROPIC_API_KEY",
108
108
  openai: "OPENAI_API_KEY",
109
109
  google: "GOOGLE_API_KEY",
110
- custom: "CUSTOM_API_KEY"
110
+ ollama: ""
111
+ // Ollama doesn't require an API key
112
+ // Custom providers have their own env var pattern
111
113
  };
112
114
  function getOpenworkDir() {
113
115
  if (!fs.existsSync(OPENWORK_DIR)) {
@@ -156,7 +158,7 @@ function parseEnvFile() {
156
158
  }
157
159
  function writeEnvFile(env) {
158
160
  getOpenworkDir();
159
- const lines = Object.entries(env).filter(([_, v]) => v).map(([k, v]) => `${k}=${v}`);
161
+ const lines = Object.entries(env).filter((entry) => entry[1]).map(([k, v]) => `${k}=${v}`);
160
162
  fs.writeFileSync(getEnvFilePath(), lines.join("\n") + "\n");
161
163
  }
162
164
  function getApiKey(provider) {
@@ -271,8 +273,7 @@ const store = new Store({
271
273
  const PROVIDERS = [
272
274
  { id: "anthropic", name: "Anthropic" },
273
275
  { id: "openai", name: "OpenAI" },
274
- { id: "google", name: "Google" },
275
- { id: "custom", name: "Custom API" }
276
+ { id: "google", name: "Google" }
276
277
  ];
277
278
  const AVAILABLE_MODELS = [
278
279
  // Anthropic Claude 4.5 series (latest as of Jan 2026)
@@ -417,6 +418,14 @@ const AVAILABLE_MODELS = [
417
418
  description: "State-of-the-art reasoning and multimodal understanding",
418
419
  available: true
419
420
  },
421
+ {
422
+ id: "gemini-3-flash-preview",
423
+ name: "Gemini 3 Flash Preview",
424
+ provider: "google",
425
+ model: "gemini-3-flash-preview",
426
+ description: "Fast frontier-class model with low latency and cost",
427
+ available: true
428
+ },
420
429
  {
421
430
  id: "gemini-2.5-pro",
422
431
  name: "Gemini 2.5 Pro",
@@ -440,41 +449,27 @@ const AVAILABLE_MODELS = [
440
449
  model: "gemini-2.5-flash-lite",
441
450
  description: "Fast, low-cost, high-performance model",
442
451
  available: true
443
- },
444
- // Custom API
445
- {
446
- id: "custom",
447
- name: "Custom API",
448
- provider: "custom",
449
- model: "custom",
450
- description: "Use your own OpenAI-compatible API endpoint",
451
- available: true
452
452
  }
453
453
  ];
454
454
  function registerModelHandlers(ipcMain) {
455
455
  ipcMain.handle("models:list", async () => {
456
456
  const customConfigs = getCustomApiConfigs();
457
- const models = AVAILABLE_MODELS.filter((m) => m.id !== "custom");
457
+ const models = AVAILABLE_MODELS.map((model) => ({
458
+ ...model,
459
+ available: hasApiKey(model.provider)
460
+ }));
458
461
  for (const config of customConfigs) {
459
462
  const modelId = config.model || `custom-${config.id}`;
460
463
  models.push({
461
464
  id: modelId,
462
465
  name: config.model || config.name,
463
- // Display the model name or config name
464
466
  provider: config.id,
465
- // Use config ID as provider ID (dynamic)
466
467
  model: modelId,
467
468
  description: `${config.name} - ${config.baseUrl}`,
468
469
  available: true
469
470
  });
470
471
  }
471
- return models.map((model) => {
472
- const isCustom = customConfigs.some((c) => c.id === model.provider);
473
- return {
474
- ...model,
475
- available: isCustom ? true : hasApiKey(model.provider)
476
- };
477
- });
472
+ return models;
478
473
  });
479
474
  ipcMain.handle("models:getDefault", async () => {
480
475
  return store.get("defaultModel", "claude-sonnet-4-5-20250929");
@@ -482,12 +477,9 @@ function registerModelHandlers(ipcMain) {
482
477
  ipcMain.handle("models:setDefault", async (_event, modelId) => {
483
478
  store.set("defaultModel", modelId);
484
479
  });
485
- ipcMain.handle(
486
- "models:setApiKey",
487
- async (_event, { provider, apiKey }) => {
488
- setApiKey(provider, apiKey);
489
- }
490
- );
480
+ ipcMain.handle("models:setApiKey", async (_event, { provider, apiKey }) => {
481
+ setApiKey(provider, apiKey);
482
+ });
491
483
  ipcMain.handle("models:getApiKey", async (_event, provider) => {
492
484
  return getApiKey(provider) ?? null;
493
485
  });
@@ -495,17 +487,15 @@ function registerModelHandlers(ipcMain) {
495
487
  deleteApiKey(provider);
496
488
  });
497
489
  ipcMain.handle("models:listProviders", async () => {
498
- const standardProviders = PROVIDERS.filter((p) => p.id !== "custom").map((provider) => ({
490
+ const standardProviders = PROVIDERS.map((provider) => ({
499
491
  ...provider,
500
492
  hasApiKey: hasApiKey(provider.id)
501
493
  }));
502
494
  const customConfigs = getCustomApiConfigs();
503
495
  const customProviders = customConfigs.map((config) => ({
504
496
  id: config.id,
505
- // Dynamic provider ID
506
497
  name: config.name,
507
498
  hasApiKey: true
508
- // Custom configs always have their API key
509
499
  }));
510
500
  return [...standardProviders, ...customProviders];
511
501
  });
@@ -1356,6 +1346,10 @@ function getModelInstance(modelId) {
1356
1346
  cleanApiKeyLength: cleanApiKey?.length,
1357
1347
  apiKeyPrefix: cleanApiKey?.substring(0, 10)
1358
1348
  });
1349
+ if (cleanApiKey) {
1350
+ process.env.OPENAI_API_KEY = cleanApiKey;
1351
+ console.log("[Runtime] Set OPENAI_API_KEY environment variable for deepagents compatibility");
1352
+ }
1359
1353
  try {
1360
1354
  const chatModel = new openai.ChatOpenAI({
1361
1355
  model: matchingConfig.model || model,
@@ -1363,9 +1357,10 @@ function getModelInstance(modelId) {
1363
1357
  configuration: {
1364
1358
  baseURL: matchingConfig.baseUrl,
1365
1359
  defaultHeaders: {
1366
- "Authorization": `Bearer ${cleanApiKey}`
1360
+ Authorization: `Bearer ${cleanApiKey}`
1367
1361
  }
1368
1362
  },
1363
+ temperature: 0.3,
1369
1364
  timeout: 6e4,
1370
1365
  maxRetries: 2
1371
1366
  });
@@ -1384,7 +1379,8 @@ function getModelInstance(modelId) {
1384
1379
  }
1385
1380
  return new anthropic.ChatAnthropic({
1386
1381
  model,
1387
- anthropicApiKey: apiKey
1382
+ anthropicApiKey: apiKey,
1383
+ temperature: 0.3
1388
1384
  });
1389
1385
  } else if (model.startsWith("gpt") || model.startsWith("o1") || model.startsWith("o3") || model.startsWith("o4")) {
1390
1386
  const apiKey = getApiKey("openai");
@@ -1394,7 +1390,8 @@ function getModelInstance(modelId) {
1394
1390
  }
1395
1391
  return new openai.ChatOpenAI({
1396
1392
  model,
1397
- openAIApiKey: apiKey
1393
+ openAIApiKey: apiKey,
1394
+ temperature: 0.3
1398
1395
  });
1399
1396
  } else if (model.startsWith("gemini")) {
1400
1397
  const apiKey = getApiKey("google");
@@ -1404,7 +1401,8 @@ function getModelInstance(modelId) {
1404
1401
  }
1405
1402
  return new googleGenai.ChatGoogleGenerativeAI({
1406
1403
  model,
1407
- apiKey
1404
+ apiKey,
1405
+ temperature: 0.3
1408
1406
  });
1409
1407
  }
1410
1408
  return model;
@@ -1626,100 +1624,32 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
1626
1624
  const activeRuns = /* @__PURE__ */ new Map();
1627
1625
  function registerAgentHandlers(ipcMain) {
1628
1626
  console.log("[Agent] Registering agent handlers...");
1629
- ipcMain.on(
1630
- "agent:invoke",
1631
- async (event, { threadId, message }) => {
1632
- const channel = `agent:stream:${threadId}`;
1633
- const window = electron.BrowserWindow.fromWebContents(event.sender);
1634
- console.log("[Agent] Received invoke request:", {
1635
- threadId,
1636
- message: message.substring(0, 50)
1637
- });
1638
- if (!window) {
1639
- console.error("[Agent] No window found");
1640
- return;
1641
- }
1642
- const existingController = activeRuns.get(threadId);
1643
- if (existingController) {
1644
- console.log("[Agent] Aborting existing stream for thread:", threadId);
1645
- existingController.abort();
1646
- activeRuns.delete(threadId);
1647
- }
1648
- const abortController = new AbortController();
1649
- activeRuns.set(threadId, abortController);
1650
- const onWindowClosed = () => {
1651
- console.log("[Agent] Window closed, aborting stream for thread:", threadId);
1652
- abortController.abort();
1653
- };
1654
- window.once("closed", onWindowClosed);
1655
- try {
1656
- const thread = getThread(threadId);
1657
- const metadata = thread?.metadata ? JSON.parse(thread.metadata) : {};
1658
- const workspacePath = metadata.workspacePath;
1659
- const currentModel = metadata.currentModel;
1660
- if (!workspacePath) {
1661
- window.webContents.send(channel, {
1662
- type: "error",
1663
- error: "WORKSPACE_REQUIRED",
1664
- message: "Please select a workspace folder before sending messages."
1665
- });
1666
- return;
1667
- }
1668
- const agent = await createAgentRuntime({
1669
- threadId,
1670
- workspacePath,
1671
- modelId: currentModel
1672
- });
1673
- const humanMessage = new messages.HumanMessage(message);
1674
- const stream = await agent.stream(
1675
- { messages: [humanMessage] },
1676
- {
1677
- configurable: { thread_id: threadId },
1678
- signal: abortController.signal,
1679
- streamMode: ["messages", "values"],
1680
- recursionLimit: 1e3
1681
- }
1682
- );
1683
- for await (const chunk of stream) {
1684
- if (abortController.signal.aborted) break;
1685
- const [mode, data] = chunk;
1686
- window.webContents.send(channel, {
1687
- type: "stream",
1688
- mode,
1689
- data: JSON.parse(JSON.stringify(data))
1690
- });
1691
- }
1692
- if (!abortController.signal.aborted) {
1693
- window.webContents.send(channel, { type: "done" });
1694
- }
1695
- } catch (error) {
1696
- const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("Controller is already closed"));
1697
- if (!isAbortError) {
1698
- console.error("[Agent] Error:", error);
1699
- window.webContents.send(channel, {
1700
- type: "error",
1701
- error: error instanceof Error ? error.message : "Unknown error"
1702
- });
1703
- }
1704
- } finally {
1705
- window.removeListener("closed", onWindowClosed);
1706
- activeRuns.delete(threadId);
1707
- }
1708
- }
1709
- );
1710
- ipcMain.on(
1711
- "agent:resume",
1712
- async (event, {
1627
+ ipcMain.on("agent:invoke", async (event, { threadId, message, modelId }) => {
1628
+ const channel = `agent:stream:${threadId}`;
1629
+ const window = electron.BrowserWindow.fromWebContents(event.sender);
1630
+ console.log("[Agent] Received invoke request:", {
1713
1631
  threadId,
1714
- command
1715
- }) => {
1716
- const channel = `agent:stream:${threadId}`;
1717
- const window = electron.BrowserWindow.fromWebContents(event.sender);
1718
- console.log("[Agent] Received resume request:", { threadId, command });
1719
- if (!window) {
1720
- console.error("[Agent] No window found for resume");
1721
- return;
1722
- }
1632
+ message: message.substring(0, 50),
1633
+ modelId
1634
+ });
1635
+ if (!window) {
1636
+ console.error("[Agent] No window found");
1637
+ return;
1638
+ }
1639
+ const existingController = activeRuns.get(threadId);
1640
+ if (existingController) {
1641
+ console.log("[Agent] Aborting existing stream for thread:", threadId);
1642
+ existingController.abort();
1643
+ activeRuns.delete(threadId);
1644
+ }
1645
+ const abortController = new AbortController();
1646
+ activeRuns.set(threadId, abortController);
1647
+ const onWindowClosed = () => {
1648
+ console.log("[Agent] Window closed, aborting stream for thread:", threadId);
1649
+ abortController.abort();
1650
+ };
1651
+ window.once("closed", onWindowClosed);
1652
+ try {
1723
1653
  const thread = getThread(threadId);
1724
1654
  const metadata = thread?.metadata ? JSON.parse(thread.metadata) : {};
1725
1655
  const workspacePath = metadata.workspacePath;
@@ -1727,32 +1657,157 @@ function registerAgentHandlers(ipcMain) {
1727
1657
  if (!workspacePath) {
1728
1658
  window.webContents.send(channel, {
1729
1659
  type: "error",
1730
- error: "Workspace path is required"
1660
+ error: "WORKSPACE_REQUIRED",
1661
+ message: "Please select a workspace folder before sending messages."
1731
1662
  });
1732
1663
  return;
1733
1664
  }
1734
- const existingController = activeRuns.get(threadId);
1735
- if (existingController) {
1736
- existingController.abort();
1737
- activeRuns.delete(threadId);
1738
- }
1739
- const abortController = new AbortController();
1740
- activeRuns.set(threadId, abortController);
1741
- try {
1742
- const agent = await createAgentRuntime({
1743
- threadId,
1744
- workspacePath,
1745
- modelId: currentModel
1746
- });
1747
- const config = {
1665
+ const agent = await createAgentRuntime({
1666
+ threadId,
1667
+ workspacePath,
1668
+ modelId: currentModel || modelId
1669
+ });
1670
+ const humanMessage = new messages.HumanMessage(message);
1671
+ const stream = await agent.stream(
1672
+ { messages: [humanMessage] },
1673
+ {
1748
1674
  configurable: { thread_id: threadId },
1749
1675
  signal: abortController.signal,
1750
1676
  streamMode: ["messages", "values"],
1751
1677
  recursionLimit: 1e3
1752
- };
1753
- const decisionType = command?.resume?.decision || "approve";
1754
- const resumeValue = { decisions: [{ type: decisionType }] };
1755
- const stream = await agent.stream(new langgraph.Command({ resume: resumeValue }), config);
1678
+ }
1679
+ );
1680
+ for await (const chunk of stream) {
1681
+ if (abortController.signal.aborted) break;
1682
+ const [mode, data] = chunk;
1683
+ window.webContents.send(channel, {
1684
+ type: "stream",
1685
+ mode,
1686
+ data: JSON.parse(JSON.stringify(data))
1687
+ });
1688
+ }
1689
+ if (!abortController.signal.aborted) {
1690
+ window.webContents.send(channel, { type: "done" });
1691
+ }
1692
+ } catch (error) {
1693
+ const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("Controller is already closed"));
1694
+ if (!isAbortError) {
1695
+ console.error("[Agent] Error:", error);
1696
+ window.webContents.send(channel, {
1697
+ type: "error",
1698
+ error: error instanceof Error ? error.message : "Unknown error"
1699
+ });
1700
+ }
1701
+ } finally {
1702
+ window.removeListener("closed", onWindowClosed);
1703
+ activeRuns.delete(threadId);
1704
+ }
1705
+ });
1706
+ ipcMain.on("agent:resume", async (event, { threadId, command, modelId }) => {
1707
+ const channel = `agent:stream:${threadId}`;
1708
+ const window = electron.BrowserWindow.fromWebContents(event.sender);
1709
+ console.log("[Agent] Received resume request:", { threadId, command, modelId });
1710
+ if (!window) {
1711
+ console.error("[Agent] No window found for resume");
1712
+ return;
1713
+ }
1714
+ const thread = getThread(threadId);
1715
+ const metadata = thread?.metadata ? JSON.parse(thread.metadata) : {};
1716
+ const workspacePath = metadata.workspacePath;
1717
+ const currentModel = metadata.currentModel;
1718
+ if (!workspacePath) {
1719
+ window.webContents.send(channel, {
1720
+ type: "error",
1721
+ error: "Workspace path is required"
1722
+ });
1723
+ return;
1724
+ }
1725
+ const existingController = activeRuns.get(threadId);
1726
+ if (existingController) {
1727
+ existingController.abort();
1728
+ activeRuns.delete(threadId);
1729
+ }
1730
+ const abortController = new AbortController();
1731
+ activeRuns.set(threadId, abortController);
1732
+ try {
1733
+ const agent = await createAgentRuntime({
1734
+ threadId,
1735
+ workspacePath,
1736
+ modelId: currentModel || modelId
1737
+ });
1738
+ const config = {
1739
+ configurable: { thread_id: threadId },
1740
+ signal: abortController.signal,
1741
+ streamMode: ["messages", "values"],
1742
+ recursionLimit: 1e3
1743
+ };
1744
+ const decisionType = command?.resume?.decision || "approve";
1745
+ const resumeValue = { decisions: [{ type: decisionType }] };
1746
+ const stream = await agent.stream(new langgraph.Command({ resume: resumeValue }), config);
1747
+ for await (const chunk of stream) {
1748
+ if (abortController.signal.aborted) break;
1749
+ const [mode, data] = chunk;
1750
+ window.webContents.send(channel, {
1751
+ type: "stream",
1752
+ mode,
1753
+ data: JSON.parse(JSON.stringify(data))
1754
+ });
1755
+ }
1756
+ if (!abortController.signal.aborted) {
1757
+ window.webContents.send(channel, { type: "done" });
1758
+ }
1759
+ } catch (error) {
1760
+ const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("Controller is already closed"));
1761
+ if (!isAbortError) {
1762
+ console.error("[Agent] Resume error:", error);
1763
+ window.webContents.send(channel, {
1764
+ type: "error",
1765
+ error: error instanceof Error ? error.message : "Unknown error"
1766
+ });
1767
+ }
1768
+ } finally {
1769
+ activeRuns.delete(threadId);
1770
+ }
1771
+ });
1772
+ ipcMain.on("agent:interrupt", async (event, { threadId, decision }) => {
1773
+ const channel = `agent:stream:${threadId}`;
1774
+ const window = electron.BrowserWindow.fromWebContents(event.sender);
1775
+ if (!window) {
1776
+ console.error("[Agent] No window found for interrupt response");
1777
+ return;
1778
+ }
1779
+ const thread = getThread(threadId);
1780
+ const metadata = thread?.metadata ? JSON.parse(thread.metadata) : {};
1781
+ const workspacePath = metadata.workspacePath;
1782
+ const currentModel = metadata.currentModel;
1783
+ if (!workspacePath) {
1784
+ window.webContents.send(channel, {
1785
+ type: "error",
1786
+ error: "Workspace path is required"
1787
+ });
1788
+ return;
1789
+ }
1790
+ const existingController = activeRuns.get(threadId);
1791
+ if (existingController) {
1792
+ existingController.abort();
1793
+ activeRuns.delete(threadId);
1794
+ }
1795
+ const abortController = new AbortController();
1796
+ activeRuns.set(threadId, abortController);
1797
+ try {
1798
+ const agent = await createAgentRuntime({
1799
+ threadId,
1800
+ workspacePath,
1801
+ modelId: currentModel
1802
+ });
1803
+ const config = {
1804
+ configurable: { thread_id: threadId },
1805
+ signal: abortController.signal,
1806
+ streamMode: ["messages", "values"],
1807
+ recursionLimit: 1e3
1808
+ };
1809
+ if (decision.type === "approve") {
1810
+ const stream = await agent.stream(null, config);
1756
1811
  for await (const chunk of stream) {
1757
1812
  if (abortController.signal.aborted) break;
1758
1813
  const [mode, data] = chunk;
@@ -1765,90 +1820,22 @@ function registerAgentHandlers(ipcMain) {
1765
1820
  if (!abortController.signal.aborted) {
1766
1821
  window.webContents.send(channel, { type: "done" });
1767
1822
  }
1768
- } catch (error) {
1769
- const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("Controller is already closed"));
1770
- if (!isAbortError) {
1771
- console.error("[Agent] Resume error:", error);
1772
- window.webContents.send(channel, {
1773
- type: "error",
1774
- error: error instanceof Error ? error.message : "Unknown error"
1775
- });
1776
- }
1777
- } finally {
1778
- activeRuns.delete(threadId);
1779
- }
1780
- }
1781
- );
1782
- ipcMain.on(
1783
- "agent:interrupt",
1784
- async (event, { threadId, decision }) => {
1785
- const channel = `agent:stream:${threadId}`;
1786
- const window = electron.BrowserWindow.fromWebContents(event.sender);
1787
- if (!window) {
1788
- console.error("[Agent] No window found for interrupt response");
1789
- return;
1823
+ } else if (decision.type === "reject") {
1824
+ window.webContents.send(channel, { type: "done" });
1790
1825
  }
1791
- const thread = getThread(threadId);
1792
- const metadata = thread?.metadata ? JSON.parse(thread.metadata) : {};
1793
- const workspacePath = metadata.workspacePath;
1794
- const currentModel = metadata.currentModel;
1795
- if (!workspacePath) {
1826
+ } catch (error) {
1827
+ const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("Controller is already closed"));
1828
+ if (!isAbortError) {
1829
+ console.error("[Agent] Interrupt error:", error);
1796
1830
  window.webContents.send(channel, {
1797
1831
  type: "error",
1798
- error: "Workspace path is required"
1799
- });
1800
- return;
1801
- }
1802
- const existingController = activeRuns.get(threadId);
1803
- if (existingController) {
1804
- existingController.abort();
1805
- activeRuns.delete(threadId);
1806
- }
1807
- const abortController = new AbortController();
1808
- activeRuns.set(threadId, abortController);
1809
- try {
1810
- const agent = await createAgentRuntime({
1811
- threadId,
1812
- workspacePath,
1813
- modelId: currentModel
1832
+ error: error instanceof Error ? error.message : "Unknown error"
1814
1833
  });
1815
- const config = {
1816
- configurable: { thread_id: threadId },
1817
- signal: abortController.signal,
1818
- streamMode: ["messages", "values"],
1819
- recursionLimit: 1e3
1820
- };
1821
- if (decision.type === "approve") {
1822
- const stream = await agent.stream(null, config);
1823
- for await (const chunk of stream) {
1824
- if (abortController.signal.aborted) break;
1825
- const [mode, data] = chunk;
1826
- window.webContents.send(channel, {
1827
- type: "stream",
1828
- mode,
1829
- data: JSON.parse(JSON.stringify(data))
1830
- });
1831
- }
1832
- if (!abortController.signal.aborted) {
1833
- window.webContents.send(channel, { type: "done" });
1834
- }
1835
- } else if (decision.type === "reject") {
1836
- window.webContents.send(channel, { type: "done" });
1837
- }
1838
- } catch (error) {
1839
- const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("Controller is already closed"));
1840
- if (!isAbortError) {
1841
- console.error("[Agent] Interrupt error:", error);
1842
- window.webContents.send(channel, {
1843
- type: "error",
1844
- error: error instanceof Error ? error.message : "Unknown error"
1845
- });
1846
- }
1847
- } finally {
1848
- activeRuns.delete(threadId);
1849
1834
  }
1835
+ } finally {
1836
+ activeRuns.delete(threadId);
1850
1837
  }
1851
- );
1838
+ });
1852
1839
  ipcMain.handle("agent:cancel", async (_event, { threadId }) => {
1853
1840
  const controller = activeRuns.get(threadId);
1854
1841
  if (controller) {
@@ -1919,28 +1906,25 @@ function registerThreadHandlers(ipcMain) {
1919
1906
  title
1920
1907
  };
1921
1908
  });
1922
- ipcMain.handle(
1923
- "threads:update",
1924
- async (_event, { threadId, updates }) => {
1925
- const updateData = {};
1926
- if (updates.title !== void 0) updateData.title = updates.title;
1927
- if (updates.status !== void 0) updateData.status = updates.status;
1928
- if (updates.metadata !== void 0)
1929
- updateData.metadata = JSON.stringify(updates.metadata);
1930
- if (updates.thread_values !== void 0) updateData.thread_values = JSON.stringify(updates.thread_values);
1931
- const row = updateThread(threadId, updateData);
1932
- if (!row) throw new Error("Thread not found");
1933
- return {
1934
- thread_id: row.thread_id,
1935
- created_at: new Date(row.created_at),
1936
- updated_at: new Date(row.updated_at),
1937
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1938
- status: row.status,
1939
- thread_values: row.thread_values ? JSON.parse(row.thread_values) : void 0,
1940
- title: row.title
1941
- };
1942
- }
1943
- );
1909
+ ipcMain.handle("threads:update", async (_event, { threadId, updates }) => {
1910
+ const updateData = {};
1911
+ if (updates.title !== void 0) updateData.title = updates.title;
1912
+ if (updates.status !== void 0) updateData.status = updates.status;
1913
+ if (updates.metadata !== void 0) updateData.metadata = JSON.stringify(updates.metadata);
1914
+ if (updates.thread_values !== void 0)
1915
+ updateData.thread_values = JSON.stringify(updates.thread_values);
1916
+ const row = updateThread(threadId, updateData);
1917
+ if (!row) throw new Error("Thread not found");
1918
+ return {
1919
+ thread_id: row.thread_id,
1920
+ created_at: new Date(row.created_at),
1921
+ updated_at: new Date(row.updated_at),
1922
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1923
+ status: row.status,
1924
+ thread_values: row.thread_values ? JSON.parse(row.thread_values) : void 0,
1925
+ title: row.title
1926
+ };
1927
+ });
1944
1928
  ipcMain.handle("threads:delete", async (_event, threadId) => {
1945
1929
  console.log("[Threads] Deleting thread:", threadId);
1946
1930
  deleteThread(threadId);
@@ -1976,27 +1960,6 @@ function registerThreadHandlers(ipcMain) {
1976
1960
  return generateTitle(message);
1977
1961
  });
1978
1962
  }
1979
- const originalConsoleError = console.error;
1980
- console.error = (...args) => {
1981
- const message = args.map((a) => String(a)).join(" ");
1982
- if (message.includes("Controller is already closed") || message.includes("ERR_INVALID_STATE") || message.includes("StreamMessagesHandler") && message.includes("aborted")) {
1983
- return;
1984
- }
1985
- originalConsoleError.apply(console, args);
1986
- };
1987
- process.on("uncaughtException", (error) => {
1988
- if (error.message?.includes("Controller is already closed") || error.message?.includes("aborted")) {
1989
- return;
1990
- }
1991
- originalConsoleError("Uncaught exception:", error);
1992
- });
1993
- process.on("unhandledRejection", (reason) => {
1994
- const message = reason instanceof Error ? reason.message : String(reason);
1995
- if (message?.includes("Controller is already closed") || message?.includes("aborted")) {
1996
- return;
1997
- }
1998
- originalConsoleError("Unhandled rejection:", reason);
1999
- });
2000
1963
  let mainWindow = null;
2001
1964
  const isDev = !electron.app.isPackaged;
2002
1965
  function createWindow() {