openwork 0.1.2 → 0.1.3

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
@@ -117,8 +117,21 @@ function getOpenworkDir() {
117
117
  function getDbPath() {
118
118
  return path.join(getOpenworkDir(), "openwork.sqlite");
119
119
  }
120
- function getCheckpointDbPath() {
121
- return path.join(getOpenworkDir(), "langgraph.sqlite");
120
+ function getThreadCheckpointDir() {
121
+ const dir = path.join(getOpenworkDir(), "threads");
122
+ if (!fs.existsSync(dir)) {
123
+ fs.mkdirSync(dir, { recursive: true });
124
+ }
125
+ return dir;
126
+ }
127
+ function getThreadCheckpointPath(threadId) {
128
+ return path.join(getThreadCheckpointDir(), `${threadId}.sqlite`);
129
+ }
130
+ function deleteThreadCheckpoint(threadId) {
131
+ const path2 = getThreadCheckpointPath(threadId);
132
+ if (fs.existsSync(path2)) {
133
+ fs.unlinkSync(path2);
134
+ }
122
135
  }
123
136
  function getEnvFilePath() {
124
137
  return ENV_FILE;
@@ -593,8 +606,29 @@ class SqlJsSaver extends langgraphCheckpoint.BaseCheckpointSaver {
593
606
  if (this.db) return;
594
607
  const SQL = await initSqlJs();
595
608
  if (fs.existsSync(this.dbPath)) {
596
- const buffer = fs.readFileSync(this.dbPath);
597
- this.db = new SQL.Database(buffer);
609
+ const stats = fs.statSync(this.dbPath);
610
+ const MAX_DB_SIZE = 100 * 1024 * 1024;
611
+ if (stats.size > MAX_DB_SIZE) {
612
+ console.warn(
613
+ `[SqlJsSaver] Database file is too large (${Math.round(stats.size / 1024 / 1024)}MB). Creating fresh database to prevent memory issues.`
614
+ );
615
+ const backupPath = this.dbPath + ".bak." + Date.now();
616
+ try {
617
+ fs.renameSync(this.dbPath, backupPath);
618
+ console.log(`[SqlJsSaver] Old database backed up to: ${backupPath}`);
619
+ } catch (e) {
620
+ console.warn("[SqlJsSaver] Could not backup old database:", e);
621
+ try {
622
+ fs.unlinkSync(this.dbPath);
623
+ } catch (e2) {
624
+ console.error("[SqlJsSaver] Could not delete old database:", e2);
625
+ }
626
+ }
627
+ this.db = new SQL.Database();
628
+ } else {
629
+ const buffer = fs.readFileSync(this.dbPath);
630
+ this.db = new SQL.Database(buffer);
631
+ }
598
632
  } else {
599
633
  const dir = path.dirname(this.dbPath);
600
634
  if (!fs.existsSync(dir)) {
@@ -1152,14 +1186,24 @@ function getSystemPrompt(workspacePath) {
1152
1186
  `;
1153
1187
  return workingDirSection + BASE_SYSTEM_PROMPT;
1154
1188
  }
1155
- let checkpointer = null;
1156
- async function getCheckpointer() {
1189
+ const checkpointers = /* @__PURE__ */ new Map();
1190
+ async function getCheckpointer(threadId) {
1191
+ let checkpointer = checkpointers.get(threadId);
1157
1192
  if (!checkpointer) {
1158
- checkpointer = new SqlJsSaver(getCheckpointDbPath());
1193
+ const dbPath = getThreadCheckpointPath(threadId);
1194
+ checkpointer = new SqlJsSaver(dbPath);
1159
1195
  await checkpointer.initialize();
1196
+ checkpointers.set(threadId, checkpointer);
1160
1197
  }
1161
1198
  return checkpointer;
1162
1199
  }
1200
+ async function closeCheckpointer(threadId) {
1201
+ const checkpointer = checkpointers.get(threadId);
1202
+ if (checkpointer) {
1203
+ await checkpointer.close();
1204
+ checkpointers.delete(threadId);
1205
+ }
1206
+ }
1163
1207
  function getModelInstance(modelId) {
1164
1208
  const model = modelId || getDefaultModel();
1165
1209
  console.log("[Runtime] Using model:", model);
@@ -1197,18 +1241,22 @@ function getModelInstance(modelId) {
1197
1241
  return model;
1198
1242
  }
1199
1243
  async function createAgentRuntime(options) {
1200
- const { modelId, workspacePath } = options;
1244
+ const { threadId, modelId, workspacePath } = options;
1245
+ if (!threadId) {
1246
+ throw new Error("Thread ID is required for checkpointing.");
1247
+ }
1201
1248
  if (!workspacePath) {
1202
1249
  throw new Error(
1203
1250
  "Workspace path is required. Please select a workspace folder before running the agent."
1204
1251
  );
1205
1252
  }
1206
1253
  console.log("[Runtime] Creating agent runtime...");
1254
+ console.log("[Runtime] Thread ID:", threadId);
1207
1255
  console.log("[Runtime] Workspace path:", workspacePath);
1208
1256
  const model = getModelInstance(modelId);
1209
1257
  console.log("[Runtime] Model instance created:", typeof model);
1210
- const checkpointer2 = await getCheckpointer();
1211
- console.log("[Runtime] Checkpointer ready");
1258
+ const checkpointer = await getCheckpointer(threadId);
1259
+ console.log("[Runtime] Checkpointer ready for thread:", threadId);
1212
1260
  const backend = new LocalSandbox({
1213
1261
  rootDir: workspacePath,
1214
1262
  virtualMode: false,
@@ -1231,7 +1279,7 @@ async function createAgentRuntime(options) {
1231
1279
  The workspace root is: ${workspacePath}`;
1232
1280
  const agent = deepagents.createDeepAgent({
1233
1281
  model,
1234
- checkpointer: checkpointer2,
1282
+ checkpointer,
1235
1283
  backend,
1236
1284
  systemPrompt,
1237
1285
  // Custom filesystem prompt for absolute paths (requires deepagents update)
@@ -1447,7 +1495,7 @@ function registerAgentHandlers(ipcMain) {
1447
1495
  });
1448
1496
  return;
1449
1497
  }
1450
- const agent = await createAgentRuntime({ workspacePath });
1498
+ const agent = await createAgentRuntime({ threadId, workspacePath });
1451
1499
  const humanMessage = new messages.HumanMessage(message);
1452
1500
  const stream = await agent.stream(
1453
1501
  { messages: [humanMessage] },
@@ -1467,13 +1515,18 @@ function registerAgentHandlers(ipcMain) {
1467
1515
  data: JSON.parse(JSON.stringify(data))
1468
1516
  });
1469
1517
  }
1470
- window.webContents.send(channel, { type: "done" });
1518
+ if (!abortController.signal.aborted) {
1519
+ window.webContents.send(channel, { type: "done" });
1520
+ }
1471
1521
  } catch (error) {
1472
- console.error("[Agent] Error:", error);
1473
- window.webContents.send(channel, {
1474
- type: "error",
1475
- error: error instanceof Error ? error.message : "Unknown error"
1476
- });
1522
+ const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("Controller is already closed"));
1523
+ if (!isAbortError) {
1524
+ console.error("[Agent] Error:", error);
1525
+ window.webContents.send(channel, {
1526
+ type: "error",
1527
+ error: error instanceof Error ? error.message : "Unknown error"
1528
+ });
1529
+ }
1477
1530
  } finally {
1478
1531
  window.removeListener("closed", onWindowClosed);
1479
1532
  activeRuns.delete(threadId);
@@ -1511,7 +1564,7 @@ function registerAgentHandlers(ipcMain) {
1511
1564
  const abortController = new AbortController();
1512
1565
  activeRuns.set(threadId, abortController);
1513
1566
  try {
1514
- const agent = await createAgentRuntime({ workspacePath });
1567
+ const agent = await createAgentRuntime({ threadId, workspacePath });
1515
1568
  const config = {
1516
1569
  configurable: { thread_id: threadId },
1517
1570
  signal: abortController.signal,
@@ -1530,13 +1583,18 @@ function registerAgentHandlers(ipcMain) {
1530
1583
  data: JSON.parse(JSON.stringify(data))
1531
1584
  });
1532
1585
  }
1533
- window.webContents.send(channel, { type: "done" });
1586
+ if (!abortController.signal.aborted) {
1587
+ window.webContents.send(channel, { type: "done" });
1588
+ }
1534
1589
  } catch (error) {
1535
- console.error("[Agent] Resume error:", error);
1536
- window.webContents.send(channel, {
1537
- type: "error",
1538
- error: error instanceof Error ? error.message : "Unknown error"
1539
- });
1590
+ const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("Controller is already closed"));
1591
+ if (!isAbortError) {
1592
+ console.error("[Agent] Resume error:", error);
1593
+ window.webContents.send(channel, {
1594
+ type: "error",
1595
+ error: error instanceof Error ? error.message : "Unknown error"
1596
+ });
1597
+ }
1540
1598
  } finally {
1541
1599
  activeRuns.delete(threadId);
1542
1600
  }
@@ -1569,7 +1627,7 @@ function registerAgentHandlers(ipcMain) {
1569
1627
  const abortController = new AbortController();
1570
1628
  activeRuns.set(threadId, abortController);
1571
1629
  try {
1572
- const agent = await createAgentRuntime({ workspacePath });
1630
+ const agent = await createAgentRuntime({ threadId, workspacePath });
1573
1631
  const config = {
1574
1632
  configurable: { thread_id: threadId },
1575
1633
  signal: abortController.signal,
@@ -1587,16 +1645,21 @@ function registerAgentHandlers(ipcMain) {
1587
1645
  data: JSON.parse(JSON.stringify(data))
1588
1646
  });
1589
1647
  }
1590
- window.webContents.send(channel, { type: "done" });
1648
+ if (!abortController.signal.aborted) {
1649
+ window.webContents.send(channel, { type: "done" });
1650
+ }
1591
1651
  } else if (decision.type === "reject") {
1592
1652
  window.webContents.send(channel, { type: "done" });
1593
1653
  }
1594
1654
  } catch (error) {
1595
- console.error("[Agent] Interrupt error:", error);
1596
- window.webContents.send(channel, {
1597
- type: "error",
1598
- error: error instanceof Error ? error.message : "Unknown error"
1599
- });
1655
+ const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("Controller is already closed"));
1656
+ if (!isAbortError) {
1657
+ console.error("[Agent] Interrupt error:", error);
1658
+ window.webContents.send(channel, {
1659
+ type: "error",
1660
+ error: error instanceof Error ? error.message : "Unknown error"
1661
+ });
1662
+ }
1600
1663
  } finally {
1601
1664
  activeRuns.delete(threadId);
1602
1665
  }
@@ -1699,19 +1762,24 @@ function registerThreadHandlers(ipcMain) {
1699
1762
  deleteThread(threadId);
1700
1763
  console.log("[Threads] Deleted from metadata store");
1701
1764
  try {
1702
- const checkpointer2 = await getCheckpointer();
1703
- await checkpointer2.deleteThread(threadId);
1704
- console.log("[Threads] Deleted from checkpointer");
1765
+ await closeCheckpointer(threadId);
1766
+ console.log("[Threads] Closed checkpointer");
1705
1767
  } catch (e) {
1706
- console.warn("[Threads] Failed to delete thread from checkpointer:", e);
1768
+ console.warn("[Threads] Failed to close checkpointer:", e);
1769
+ }
1770
+ try {
1771
+ deleteThreadCheckpoint(threadId);
1772
+ console.log("[Threads] Deleted checkpoint file");
1773
+ } catch (e) {
1774
+ console.warn("[Threads] Failed to delete checkpoint file:", e);
1707
1775
  }
1708
1776
  });
1709
1777
  ipcMain.handle("threads:history", async (_event, threadId) => {
1710
1778
  try {
1711
- const checkpointer2 = await getCheckpointer();
1779
+ const checkpointer = await getCheckpointer(threadId);
1712
1780
  const history = [];
1713
1781
  const config = { configurable: { thread_id: threadId } };
1714
- for await (const checkpoint of checkpointer2.list(config, { limit: 50 })) {
1782
+ for await (const checkpoint of checkpointer.list(config, { limit: 50 })) {
1715
1783
  history.push(checkpoint);
1716
1784
  }
1717
1785
  return history;
@@ -1724,6 +1792,27 @@ function registerThreadHandlers(ipcMain) {
1724
1792
  return generateTitle(message);
1725
1793
  });
1726
1794
  }
1795
+ const originalConsoleError = console.error;
1796
+ console.error = (...args) => {
1797
+ const message = args.map((a) => String(a)).join(" ");
1798
+ if (message.includes("Controller is already closed") || message.includes("ERR_INVALID_STATE") || message.includes("StreamMessagesHandler") && message.includes("aborted")) {
1799
+ return;
1800
+ }
1801
+ originalConsoleError.apply(console, args);
1802
+ };
1803
+ process.on("uncaughtException", (error) => {
1804
+ if (error.message?.includes("Controller is already closed") || error.message?.includes("aborted")) {
1805
+ return;
1806
+ }
1807
+ originalConsoleError("Uncaught exception:", error);
1808
+ });
1809
+ process.on("unhandledRejection", (reason) => {
1810
+ const message = reason instanceof Error ? reason.message : String(reason);
1811
+ if (message?.includes("Controller is already closed") || message?.includes("aborted")) {
1812
+ return;
1813
+ }
1814
+ originalConsoleError("Unhandled rejection:", reason);
1815
+ });
1727
1816
  let mainWindow = null;
1728
1817
  const isDev = !electron.app.isPackaged;
1729
1818
  function createWindow() {
@@ -55,13 +55,17 @@
55
55
  --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
56
56
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
57
57
  --color-red-400: oklch(70.4% .191 22.216);
58
+ --color-red-500: oklch(63.7% .237 25.331);
58
59
  --color-orange-400: oklch(75% .183 55.934);
60
+ --color-orange-500: oklch(70.5% .213 47.604);
59
61
  --color-amber-500: oklch(76.9% .188 70.08);
60
62
  --color-yellow-400: oklch(85.2% .199 91.936);
61
63
  --color-yellow-500: oklch(79.5% .184 86.047);
62
64
  --color-yellow-600: oklch(68.1% .162 75.834);
63
65
  --color-green-400: oklch(79.2% .209 151.711);
66
+ --color-green-500: oklch(72.3% .219 149.579);
64
67
  --color-blue-400: oklch(70.7% .165 254.624);
68
+ --color-blue-500: oklch(62.3% .214 259.815);
65
69
  --color-purple-400: oklch(71.4% .203 305.504);
66
70
  --color-pink-400: oklch(71.8% .202 349.761);
67
71
  --color-black: #000;
@@ -455,10 +459,6 @@
455
459
  margin-top: calc(var(--spacing) * 2);
456
460
  }
457
461
 
458
- .mt-3 {
459
- margin-top: calc(var(--spacing) * 3);
460
- }
461
-
462
462
  .mr-1 {
463
463
  margin-right: calc(var(--spacing) * 1);
464
464
  }
@@ -748,6 +748,10 @@
748
748
  width: calc(var(--spacing) * 64);
749
749
  }
750
750
 
751
+ .w-72 {
752
+ width: calc(var(--spacing) * 72);
753
+ }
754
+
751
755
  .w-\[1px\] {
752
756
  width: 1px;
753
757
  }
@@ -1200,16 +1204,6 @@
1200
1204
  }
1201
1205
  }
1202
1206
 
1203
- .bg-amber-500\/20 {
1204
- background-color: #f99c0033;
1205
- }
1206
-
1207
- @supports (color: color-mix(in lab, red, red)) {
1208
- .bg-amber-500\/20 {
1209
- background-color: color-mix(in oklab, var(--color-amber-500) 20%, transparent);
1210
- }
1211
- }
1212
-
1213
1207
  .bg-background {
1214
1208
  background-color: var(--background);
1215
1209
  }
@@ -1282,7 +1276,21 @@
1282
1276
  }
1283
1277
  }
1284
1278
 
1285
- .bg-muted, .bg-muted\/30 {
1279
+ .bg-muted {
1280
+ background-color: var(--muted);
1281
+ }
1282
+
1283
+ .bg-muted-foreground, .bg-muted-foreground\/20 {
1284
+ background-color: var(--muted-foreground);
1285
+ }
1286
+
1287
+ @supports (color: color-mix(in lab, red, red)) {
1288
+ .bg-muted-foreground\/20 {
1289
+ background-color: color-mix(in oklab, var(--muted-foreground) 20%, transparent);
1290
+ }
1291
+ }
1292
+
1293
+ .bg-muted\/30 {
1286
1294
  background-color: var(--muted);
1287
1295
  }
1288
1296
 
@@ -1292,6 +1300,20 @@
1292
1300
  }
1293
1301
  }
1294
1302
 
1303
+ .bg-orange-500 {
1304
+ background-color: var(--color-orange-500);
1305
+ }
1306
+
1307
+ .bg-orange-500\/20 {
1308
+ background-color: #fe6e0033;
1309
+ }
1310
+
1311
+ @supports (color: color-mix(in lab, red, red)) {
1312
+ .bg-orange-500\/20 {
1313
+ background-color: color-mix(in oklab, var(--color-orange-500) 20%, transparent);
1314
+ }
1315
+ }
1316
+
1295
1317
  .bg-popover {
1296
1318
  background-color: var(--popover);
1297
1319
  }
@@ -1316,6 +1338,20 @@
1316
1338
  }
1317
1339
  }
1318
1340
 
1341
+ .bg-red-500 {
1342
+ background-color: var(--color-red-500);
1343
+ }
1344
+
1345
+ .bg-red-500\/20 {
1346
+ background-color: #fb2c3633;
1347
+ }
1348
+
1349
+ @supports (color: color-mix(in lab, red, red)) {
1350
+ .bg-red-500\/20 {
1351
+ background-color: color-mix(in oklab, var(--color-red-500) 20%, transparent);
1352
+ }
1353
+ }
1354
+
1319
1355
  .bg-secondary {
1320
1356
  background-color: var(--secondary);
1321
1357
  }
@@ -1432,6 +1468,20 @@
1432
1468
  background-color: #0000;
1433
1469
  }
1434
1470
 
1471
+ .bg-yellow-500 {
1472
+ background-color: var(--color-yellow-500);
1473
+ }
1474
+
1475
+ .bg-yellow-500\/20 {
1476
+ background-color: #edb20033;
1477
+ }
1478
+
1479
+ @supports (color: color-mix(in lab, red, red)) {
1480
+ .bg-yellow-500\/20 {
1481
+ background-color: color-mix(in oklab, var(--color-yellow-500) 20%, transparent);
1482
+ }
1483
+ }
1484
+
1435
1485
  .bg-gradient-to-b {
1436
1486
  --tw-gradient-position: to bottom in oklab;
1437
1487
  background-image: linear-gradient(var(--tw-gradient-stops));
@@ -1516,10 +1566,6 @@
1516
1566
  padding-inline: calc(var(--spacing) * 6);
1517
1567
  }
1518
1568
 
1519
- .py-0 {
1520
- padding-block: calc(var(--spacing) * 0);
1521
- }
1522
-
1523
1569
  .py-0\.5 {
1524
1570
  padding-block: calc(var(--spacing) * .5);
1525
1571
  }
@@ -1560,6 +1606,10 @@
1560
1606
  padding-top: calc(var(--spacing) * 0);
1561
1607
  }
1562
1608
 
1609
+ .pt-1 {
1610
+ padding-top: calc(var(--spacing) * 1);
1611
+ }
1612
+
1563
1613
  .pt-2 {
1564
1614
  padding-top: calc(var(--spacing) * 2);
1565
1615
  }
@@ -1580,10 +1630,6 @@
1580
1630
  padding-bottom: calc(var(--spacing) * 2);
1581
1631
  }
1582
1632
 
1583
- .pb-4 {
1584
- padding-bottom: calc(var(--spacing) * 4);
1585
- }
1586
-
1587
1633
  .pl-4 {
1588
1634
  padding-left: calc(var(--spacing) * 4);
1589
1635
  }
@@ -1702,6 +1748,10 @@
1702
1748
  color: var(--color-blue-400);
1703
1749
  }
1704
1750
 
1751
+ .text-blue-500 {
1752
+ color: var(--color-blue-500);
1753
+ }
1754
+
1705
1755
  .text-card-foreground {
1706
1756
  color: var(--card-foreground);
1707
1757
  }
@@ -1718,6 +1768,10 @@
1718
1768
  color: var(--color-green-400);
1719
1769
  }
1720
1770
 
1771
+ .text-green-500 {
1772
+ color: var(--color-green-500);
1773
+ }
1774
+
1721
1775
  .text-muted-foreground, .text-muted-foreground\/30 {
1722
1776
  color: var(--muted-foreground);
1723
1777
  }
@@ -1752,6 +1806,10 @@
1752
1806
  color: var(--color-orange-400);
1753
1807
  }
1754
1808
 
1809
+ .text-orange-500 {
1810
+ color: var(--color-orange-500);
1811
+ }
1812
+
1755
1813
  .text-pink-400 {
1756
1814
  color: var(--color-pink-400);
1757
1815
  }
@@ -1776,10 +1834,18 @@
1776
1834
  color: var(--color-red-400);
1777
1835
  }
1778
1836
 
1837
+ .text-red-500 {
1838
+ color: var(--color-red-500);
1839
+ }
1840
+
1779
1841
  .text-secondary-foreground {
1780
1842
  color: var(--secondary-foreground);
1781
1843
  }
1782
1844
 
1845
+ .text-sidebar-accent-foreground {
1846
+ color: var(--sidebar-accent-foreground);
1847
+ }
1848
+
1783
1849
  .text-status-critical {
1784
1850
  color: var(--status-critical);
1785
1851
  }
@@ -1974,16 +2040,6 @@
1974
2040
  }
1975
2041
  }
1976
2042
 
1977
- .hover\:bg-amber-500\/10:hover {
1978
- background-color: #f99c001a;
1979
- }
1980
-
1981
- @supports (color: color-mix(in lab, red, red)) {
1982
- .hover\:bg-amber-500\/10:hover {
1983
- background-color: color-mix(in oklab, var(--color-amber-500) 10%, transparent);
1984
- }
1985
- }
1986
-
1987
2043
  .hover\:bg-background-interactive:hover {
1988
2044
  background-color: var(--background-interactive);
1989
2045
  }
@@ -2106,6 +2162,10 @@
2106
2162
  text-decoration-line: underline;
2107
2163
  }
2108
2164
 
2165
+ .hover\:opacity-80:hover {
2166
+ opacity: .8;
2167
+ }
2168
+
2109
2169
  .hover\:opacity-100:hover {
2110
2170
  opacity: 1;
2111
2171
  }