openwork 0.1.1-rc.2 → 0.1.1-rc.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.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # openwork
2
2
 
3
+ [![CI](https://github.com/langchain-ai/openwork/actions/workflows/ci.yml/badge.svg)](https://github.com/langchain-ai/openwork/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/openwork.svg)](https://www.npmjs.com/package/openwork)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
3
7
  A tactical agent interface for [deepagentsjs](https://github.com/langchain-ai/deepagentsjs) - an opinionated harness for building deep agents with filesystem capabilities, planning, and subagent delegation.
4
8
 
5
9
  ![openwork screenshot](docs/screenshot.png)
package/out/main/index.js CHANGED
@@ -6,6 +6,7 @@ const deepagents = require("deepagents");
6
6
  const Store = require("electron-store");
7
7
  const fs$1 = require("fs/promises");
8
8
  const fs = require("fs");
9
+ const os = require("os");
9
10
  const anthropic = require("@langchain/anthropic");
10
11
  const openai = require("@langchain/openai");
11
12
  const initSqlJs = require("sql.js");
@@ -96,23 +97,83 @@ function notifyRenderer(threadId, workspacePath) {
96
97
  });
97
98
  }
98
99
  }
100
+ const OPENWORK_DIR = path.join(os.homedir(), ".openwork");
101
+ const ENV_FILE = path.join(OPENWORK_DIR, ".env");
102
+ const ENV_VAR_NAMES = {
103
+ anthropic: "ANTHROPIC_API_KEY",
104
+ openai: "OPENAI_API_KEY"
105
+ };
106
+ function getOpenworkDir() {
107
+ if (!fs.existsSync(OPENWORK_DIR)) {
108
+ fs.mkdirSync(OPENWORK_DIR, { recursive: true });
109
+ }
110
+ return OPENWORK_DIR;
111
+ }
112
+ function getDbPath() {
113
+ return path.join(getOpenworkDir(), "openwork.sqlite");
114
+ }
115
+ function getCheckpointDbPath() {
116
+ return path.join(getOpenworkDir(), "langgraph.sqlite");
117
+ }
118
+ function getEnvFilePath() {
119
+ return ENV_FILE;
120
+ }
121
+ function parseEnvFile() {
122
+ const envPath = getEnvFilePath();
123
+ if (!fs.existsSync(envPath)) return {};
124
+ const content = fs.readFileSync(envPath, "utf-8");
125
+ const result = {};
126
+ for (const line of content.split("\n")) {
127
+ const trimmed = line.trim();
128
+ if (!trimmed || trimmed.startsWith("#")) continue;
129
+ const eqIndex = trimmed.indexOf("=");
130
+ if (eqIndex > 0) {
131
+ const key = trimmed.slice(0, eqIndex).trim();
132
+ const value = trimmed.slice(eqIndex + 1).trim();
133
+ result[key] = value;
134
+ }
135
+ }
136
+ return result;
137
+ }
138
+ function writeEnvFile(env) {
139
+ getOpenworkDir();
140
+ const lines = Object.entries(env).filter(([_, v]) => v).map(([k, v]) => `${k}=${v}`);
141
+ fs.writeFileSync(getEnvFilePath(), lines.join("\n") + "\n");
142
+ }
143
+ function getApiKey(provider) {
144
+ const envVarName = ENV_VAR_NAMES[provider];
145
+ if (!envVarName) return void 0;
146
+ const env = parseEnvFile();
147
+ if (env[envVarName]) return env[envVarName];
148
+ return process.env[envVarName];
149
+ }
150
+ function setApiKey(provider, apiKey) {
151
+ const envVarName = ENV_VAR_NAMES[provider];
152
+ if (!envVarName) return;
153
+ const env = parseEnvFile();
154
+ env[envVarName] = apiKey;
155
+ writeEnvFile(env);
156
+ process.env[envVarName] = apiKey;
157
+ }
158
+ function deleteApiKey(provider) {
159
+ const envVarName = ENV_VAR_NAMES[provider];
160
+ if (!envVarName) return;
161
+ const env = parseEnvFile();
162
+ delete env[envVarName];
163
+ writeEnvFile(env);
164
+ delete process.env[envVarName];
165
+ }
166
+ function hasApiKey(provider) {
167
+ return !!getApiKey(provider);
168
+ }
99
169
  const store = new Store({
100
- name: "openwork-settings",
101
- encryptionKey: "openwork-encryption-key-v1"
102
- // In production, derive from machine ID
170
+ name: "settings",
171
+ cwd: getOpenworkDir()
103
172
  });
104
173
  const PROVIDERS = [
105
174
  { id: "anthropic", name: "Anthropic" },
106
- { id: "openai", name: "OpenAI" },
107
- { id: "google", name: "Google" }
175
+ { id: "openai", name: "OpenAI" }
108
176
  ];
109
- const ENV_VAR_MAP = {
110
- anthropic: "ANTHROPIC_API_KEY",
111
- openai: "OPENAI_API_KEY",
112
- google: "GOOGLE_API_KEY",
113
- ollama: ""
114
- // Ollama doesn't need API key
115
- };
116
177
  const AVAILABLE_MODELS = [
117
178
  // Anthropic Claude 4.5 series (latest as of Jan 2026)
118
179
  {
@@ -120,7 +181,7 @@ const AVAILABLE_MODELS = [
120
181
  name: "Claude Opus 4.5",
121
182
  provider: "anthropic",
122
183
  model: "claude-opus-4-5-20251101",
123
- description: "Most capable, excels at complex reasoning and coding",
184
+ description: "Premium model with maximum intelligence",
124
185
  available: true
125
186
  },
126
187
  {
@@ -128,7 +189,7 @@ const AVAILABLE_MODELS = [
128
189
  name: "Claude Sonnet 4.5",
129
190
  provider: "anthropic",
130
191
  model: "claude-sonnet-4-5-20250929",
131
- description: "Balanced performance and efficiency, great for agents",
192
+ description: "Best balance of intelligence, speed, and cost for agents",
132
193
  available: true
133
194
  },
134
195
  {
@@ -136,7 +197,24 @@ const AVAILABLE_MODELS = [
136
197
  name: "Claude Haiku 4.5",
137
198
  provider: "anthropic",
138
199
  model: "claude-haiku-4-5-20251001",
139
- description: "Fast and cost-effective for real-time tasks",
200
+ description: "Fastest model with near-frontier intelligence",
201
+ available: true
202
+ },
203
+ // Anthropic Claude legacy models
204
+ {
205
+ id: "claude-opus-4-1-20250805",
206
+ name: "Claude Opus 4.1",
207
+ provider: "anthropic",
208
+ model: "claude-opus-4-1-20250805",
209
+ description: "Previous generation premium model with extended thinking",
210
+ available: true
211
+ },
212
+ {
213
+ id: "claude-sonnet-4-20250514",
214
+ name: "Claude Sonnet 4",
215
+ provider: "anthropic",
216
+ model: "claude-sonnet-4-20250514",
217
+ description: "Fast and capable previous generation model",
140
218
  available: true
141
219
  },
142
220
  // OpenAI GPT-5 series (latest as of Jan 2026)
@@ -229,15 +307,6 @@ const AVAILABLE_MODELS = [
229
307
  model: "gpt-4o-mini",
230
308
  description: "Cost-efficient variant with faster response times",
231
309
  available: true
232
- },
233
- // Google Gemini 3 series (latest as of Jan 2026)
234
- {
235
- id: "gemini-3-flash-preview",
236
- name: "Gemini 3 Flash",
237
- provider: "google",
238
- model: "gemini-3-flash-preview",
239
- description: "Fast, 3x faster than 2.5 Pro with 1M context",
240
- available: true
241
310
  }
242
311
  ];
243
312
  function registerModelHandlers(ipcMain) {
@@ -256,22 +325,14 @@ function registerModelHandlers(ipcMain) {
256
325
  ipcMain.handle(
257
326
  "models:setApiKey",
258
327
  async (_event, { provider, apiKey }) => {
259
- store.set(`apiKeys.${provider}`, apiKey);
260
- const envVar = ENV_VAR_MAP[provider];
261
- if (envVar) {
262
- process.env[envVar] = apiKey;
263
- }
328
+ setApiKey(provider, apiKey);
264
329
  }
265
330
  );
266
331
  ipcMain.handle("models:getApiKey", async (_event, provider) => {
267
- return store.get(`apiKeys.${provider}`, null);
332
+ return getApiKey(provider) ?? null;
268
333
  });
269
334
  ipcMain.handle("models:deleteApiKey", async (_event, provider) => {
270
- store.delete(`apiKeys.${provider}`);
271
- const envVar = ENV_VAR_MAP[provider];
272
- if (envVar) {
273
- delete process.env[envVar];
274
- }
335
+ deleteApiKey(provider);
275
336
  });
276
337
  ipcMain.handle("models:listProviders", async () => {
277
338
  return PROVIDERS.map((provider) => ({
@@ -432,18 +493,6 @@ function registerModelHandlers(ipcMain) {
432
493
  }
433
494
  );
434
495
  }
435
- function hasApiKey(provider) {
436
- const storedKey = store.get(`apiKeys.${provider}`);
437
- if (storedKey) return true;
438
- const envVar = ENV_VAR_MAP[provider];
439
- return envVar ? !!process.env[envVar] : false;
440
- }
441
- function getApiKey(provider) {
442
- const storedKey = store.get(`apiKeys.${provider}`);
443
- if (storedKey) return storedKey;
444
- const envVar = ENV_VAR_MAP[provider];
445
- return envVar ? process.env[envVar] : void 0;
446
- }
447
496
  function getDefaultModel() {
448
497
  return store.get("defaultModel", "claude-sonnet-4-5-20250929");
449
498
  }
@@ -877,8 +926,7 @@ function getSystemPrompt(workspacePath) {
877
926
  let checkpointer = null;
878
927
  async function getCheckpointer() {
879
928
  if (!checkpointer) {
880
- const dbPath = path.join(electron.app.getPath("userData"), "langgraph.sqlite");
881
- checkpointer = new SqlJsSaver(dbPath);
929
+ checkpointer = new SqlJsSaver(getCheckpointDbPath());
882
930
  await checkpointer.initialize();
883
931
  }
884
932
  return checkpointer;
@@ -906,8 +954,6 @@ function getModelInstance(modelId) {
906
954
  model,
907
955
  openAIApiKey: apiKey
908
956
  });
909
- } else if (model.startsWith("gemini")) {
910
- throw new Error("Gemini support coming soon");
911
957
  }
912
958
  return model;
913
959
  }
@@ -938,7 +984,6 @@ async function createAgentRuntime(options) {
938
984
  console.log("[Runtime] Deep agent created with FilesystemBackend at:", workspacePath);
939
985
  return agent;
940
986
  }
941
- const getDbPath = () => path.join(electron.app.getPath("userData"), "openwork.sqlite");
942
987
  let db = null;
943
988
  let saveTimer = null;
944
989
  let dirty = false;
@@ -34262,7 +34262,13 @@ var BaseMessage = class extends Serializable {
34262
34262
  function isOpenAIToolCallArray(value) {
34263
34263
  return Array.isArray(value) && value.every((v2) => typeof v2.index === "number");
34264
34264
  }
34265
- function _mergeDicts(left, right) {
34265
+ const DEFAULT_MERGE_IGNORE_KEYS = [
34266
+ "index",
34267
+ "created",
34268
+ "timestamp"
34269
+ ];
34270
+ function _mergeDicts(left, right, options) {
34271
+ const ignoreKeys = options?.ignoreKeys ?? DEFAULT_MERGE_IGNORE_KEYS;
34266
34272
  if (left === void 0 && right === void 0) return void 0;
34267
34273
  if (left === void 0 || right === void 0) return left ?? right;
34268
34274
  const merged = { ...left };
@@ -34277,15 +34283,18 @@ function _mergeDicts(left, right) {
34277
34283
  "model_provider"
34278
34284
  ].includes(key2)) {
34279
34285
  if (value) merged[key2] = value;
34280
- } else merged[key2] += value;
34281
- else if (typeof merged[key2] === "number") merged[key2] = merged[key2] + value;
34282
- else if (typeof merged[key2] === "object" && !Array.isArray(merged[key2])) merged[key2] = _mergeDicts(merged[key2], value);
34283
- else if (Array.isArray(merged[key2])) merged[key2] = _mergeLists(merged[key2], value);
34286
+ } else if (ignoreKeys.includes(key2)) continue;
34287
+ else merged[key2] += value;
34288
+ else if (typeof merged[key2] === "number") {
34289
+ if (ignoreKeys.includes(key2)) continue;
34290
+ merged[key2] = merged[key2] + value;
34291
+ } else if (typeof merged[key2] === "object" && !Array.isArray(merged[key2])) merged[key2] = _mergeDicts(merged[key2], value, options);
34292
+ else if (Array.isArray(merged[key2])) merged[key2] = _mergeLists(merged[key2], value, options);
34284
34293
  else if (merged[key2] === value) continue;
34285
34294
  else console.warn(`field[${key2}] already exists in this message chunk and value has unsupported type.`);
34286
34295
  return merged;
34287
34296
  }
34288
- function _mergeLists(left, right) {
34297
+ function _mergeLists(left, right, options) {
34289
34298
  if (left === void 0 && right === void 0) return void 0;
34290
34299
  else if (left === void 0 || right === void 0) return left || right;
34291
34300
  else {
@@ -34298,22 +34307,22 @@ function _mergeLists(left, right) {
34298
34307
  const eitherItemMissingID = !("id" in leftItem) || !leftItem?.id || !("id" in item) || !item?.id;
34299
34308
  return isObject && indiciesMatch && (idsMatch || eitherItemMissingID);
34300
34309
  });
34301
- if (toMerge !== -1 && typeof merged[toMerge] === "object" && merged[toMerge] !== null) merged[toMerge] = _mergeDicts(merged[toMerge], item);
34310
+ if (toMerge !== -1 && typeof merged[toMerge] === "object" && merged[toMerge] !== null) merged[toMerge] = _mergeDicts(merged[toMerge], item, options);
34302
34311
  else merged.push(item);
34303
34312
  } else if (typeof item === "object" && item !== null && "text" in item && item.text === "") continue;
34304
34313
  else merged.push(item);
34305
34314
  return merged;
34306
34315
  }
34307
34316
  }
34308
- function _mergeObj(left, right) {
34317
+ function _mergeObj(left, right, options) {
34309
34318
  if (left === void 0 && right === void 0) return void 0;
34310
34319
  if (left === void 0 || right === void 0) return left ?? right;
34311
34320
  else if (typeof left !== typeof right) throw new Error(`Cannot merge objects of different types.
34312
34321
  Left ${typeof left}
34313
34322
  Right ${typeof right}`);
34314
34323
  else if (typeof left === "string" && typeof right === "string") return left + right;
34315
- else if (Array.isArray(left) && Array.isArray(right)) return _mergeLists(left, right);
34316
- else if (typeof left === "object" && typeof right === "object") return _mergeDicts(left, right);
34324
+ else if (Array.isArray(left) && Array.isArray(right)) return _mergeLists(left, right, options);
34325
+ else if (typeof left === "object" && typeof right === "object") return _mergeDicts(left, right, options);
34317
34326
  else if (left === right) return left;
34318
34327
  else throw new Error(`Can not merge objects of different types.
34319
34328
  Left ${left}
@@ -47969,12 +47978,14 @@ const STATUS_NO_RETRY$1 = [
47969
47978
  409
47970
47979
  ];
47971
47980
  const defaultFailedAttemptHandler = (error) => {
47972
- if (error.message.startsWith("Cancel") || error.message.startsWith("AbortError") || error.name === "AbortError") throw error;
47973
- if (error?.code === "ECONNABORTED") throw error;
47974
- const status = error?.response?.status ?? error?.status;
47981
+ if (typeof error !== "object" || error === null) return;
47982
+ if ("message" in error && typeof error.message === "string" && (error.message.startsWith("Cancel") || error.message.startsWith("AbortError")) || "name" in error && typeof error.name === "string" && error.name === "AbortError") throw error;
47983
+ if ("code" in error && typeof error.code === "string" && error.code === "ECONNABORTED") throw error;
47984
+ const status = "response" in error && typeof error.response === "object" && error.response !== null && "status" in error.response && typeof error.response.status === "number" ? error.response.status : void 0;
47975
47985
  if (status && STATUS_NO_RETRY$1.includes(+status)) throw error;
47976
- if (error?.error?.code === "insufficient_quota") {
47977
- const err = new Error(error?.message);
47986
+ const code2 = "error" in error && typeof error.error === "object" && error.error !== null && "code" in error.error && typeof error.error.code === "string" ? error.error.code : void 0;
47987
+ if (code2 === "insufficient_quota") {
47988
+ const err = new Error("message" in error && typeof error.message === "string" ? error.message : "Insufficient quota");
47978
47989
  err.name = "InsufficientQuotaError";
47979
47990
  throw err;
47980
47991
  }
@@ -57172,6 +57183,7 @@ __export(messages_exports, {
57172
57183
  BaseMessageChunk: () => BaseMessageChunk,
57173
57184
  ChatMessage: () => ChatMessage,
57174
57185
  ChatMessageChunk: () => ChatMessageChunk,
57186
+ DEFAULT_MERGE_IGNORE_KEYS: () => DEFAULT_MERGE_IGNORE_KEYS,
57175
57187
  FunctionMessage: () => FunctionMessage,
57176
57188
  FunctionMessageChunk: () => FunctionMessageChunk,
57177
57189
  HumanMessage: () => HumanMessage,
@@ -73456,20 +73468,15 @@ function AnthropicIcon({ className }) {
73456
73468
  function OpenAIIcon({ className }) {
73457
73469
  return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855l-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023l-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135l-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08L8.704 5.46a.795.795 0 0 0-.393.681zm1.097-2.365l2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5z" }) });
73458
73470
  }
73459
- function GoogleIcon({ className }) {
73460
- return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z" }) });
73461
- }
73462
73471
  const PROVIDER_ICONS = {
73463
73472
  anthropic: AnthropicIcon,
73464
73473
  openai: OpenAIIcon,
73465
- google: GoogleIcon,
73466
73474
  ollama: () => null
73467
73475
  // No icon for ollama yet
73468
73476
  };
73469
73477
  const FALLBACK_PROVIDERS = [
73470
73478
  { id: "anthropic", name: "Anthropic", hasApiKey: false },
73471
- { id: "openai", name: "OpenAI", hasApiKey: false },
73472
- { id: "google", name: "Google AI", hasApiKey: false }
73479
+ { id: "openai", name: "OpenAI", hasApiKey: false }
73473
73480
  ];
73474
73481
  function ModelSwitcher() {
73475
73482
  const [open, setOpen] = reactExports.useState(false);
@@ -73587,29 +73594,31 @@ function ModelSwitcher() {
73587
73594
  )
73588
73595
  ] })
73589
73596
  ) : (
73590
- // Show models list
73591
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-0.5", children: [
73592
- filteredModels.map((model) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
73593
- "button",
73594
- {
73595
- onClick: () => handleModelSelect(model.id),
73596
- className: cn(
73597
- "w-full flex items-center gap-1.5 px-2 py-1 rounded-sm text-xs transition-colors text-left font-mono",
73598
- currentModel === model.id ? "bg-muted text-foreground" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
73599
- ),
73600
- children: [
73601
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate", children: model.id }),
73602
- currentModel === model.id && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 shrink-0 text-foreground" })
73603
- ]
73604
- },
73605
- model.id
73606
- )),
73607
- filteredModels.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground px-2 py-4", children: "No models available" }),
73597
+ // Show models list with scrollable area
73598
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-[200px]", children: [
73599
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "overflow-y-auto flex-1 space-y-0.5", children: [
73600
+ filteredModels.map((model) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
73601
+ "button",
73602
+ {
73603
+ onClick: () => handleModelSelect(model.id),
73604
+ className: cn(
73605
+ "w-full flex items-center gap-1.5 px-2 py-1 rounded-sm text-xs transition-colors text-left font-mono",
73606
+ currentModel === model.id ? "bg-muted text-foreground" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
73607
+ ),
73608
+ children: [
73609
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate", children: model.id }),
73610
+ currentModel === model.id && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 shrink-0 text-foreground" })
73611
+ ]
73612
+ },
73613
+ model.id
73614
+ )),
73615
+ filteredModels.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground px-2 py-4", children: "No models available" })
73616
+ ] }),
73608
73617
  selectedProvider?.hasApiKey && /* @__PURE__ */ jsxRuntimeExports.jsxs(
73609
73618
  "button",
73610
73619
  {
73611
73620
  onClick: () => handleConfigureApiKey(selectedProvider),
73612
- className: "w-full flex items-center gap-2 px-2 py-1.5 rounded-sm text-xs text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors mt-2",
73621
+ className: "w-full flex items-center gap-2 px-2 py-1.5 rounded-sm text-xs text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors mt-2 border-t border-border pt-2",
73613
73622
  children: [
73614
73623
  /* @__PURE__ */ jsxRuntimeExports.jsx(Key, { className: "size-3.5" }),
73615
73624
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Edit API Key" })
@@ -74676,7 +74685,7 @@ function ChatContainer({ threadId }) {
74676
74685
  "button",
74677
74686
  {
74678
74687
  type: "button",
74679
- className: "inline-flex items-center justify-center rounded-md border border-border bg-background px-2 h-7 text-xs gap-1.5 text-amber-500 hover:bg-accent/50 disabled:opacity-50 disabled:cursor-not-allowed",
74688
+ className: "inline-flex items-center justify-center rounded-md border border-border bg-background px-2 h-7 text-xs gap-1.5 text-amber-500 hover:bg-accent/50 transition-color duration-100 disabled:opacity-50 disabled:cursor-not-allowed",
74680
74689
  onClick: handleSelectWorkspaceFromEmptyState,
74681
74690
  children: [
74682
74691
  /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "size-3.5" }),
@@ -75120,7 +75129,7 @@ function TaskItem({ todo }) {
75120
75129
  function FilesContent() {
75121
75130
  const { workspaceFiles, workspacePath, currentThreadId, setWorkspacePath, setWorkspaceFiles } = useAppStore();
75122
75131
  const [syncing, setSyncing] = reactExports.useState(false);
75123
- const [syncSuccess, _setSyncSuccess] = reactExports.useState(false);
75132
+ const [syncSuccess] = reactExports.useState(false);
75124
75133
  reactExports.useEffect(() => {
75125
75134
  async function loadWorkspace() {
75126
75135
  if (currentThreadId) {
@@ -75136,6 +75145,19 @@ function FilesContent() {
75136
75145
  }
75137
75146
  loadWorkspace();
75138
75147
  }, [currentThreadId, setWorkspacePath, setWorkspaceFiles]);
75148
+ reactExports.useEffect(() => {
75149
+ if (!currentThreadId) return;
75150
+ const cleanup = window.api.workspace.onFilesChanged(async (data) => {
75151
+ if (data.threadId === currentThreadId) {
75152
+ console.log("[FilesContent] Files changed, reloading...", data);
75153
+ const result = await window.api.workspace.loadFromDisk(currentThreadId);
75154
+ if (result.success && result.files) {
75155
+ setWorkspaceFiles(result.files);
75156
+ }
75157
+ }
75158
+ });
75159
+ return cleanup;
75160
+ }, [currentThreadId, setWorkspaceFiles]);
75139
75161
  async function handleSelectFolder() {
75140
75162
  if (!currentThreadId) return;
75141
75163
  setSyncing(true);
@@ -75535,7 +75557,7 @@ function App() {
75535
75557
  },
75536
75558
  children: [
75537
75559
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "app-badge-name", children: "OPENWORK" }),
75538
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "app-badge-version", children: "0.1.1-rc.2" })
75560
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "app-badge-version", children: "0.1.1-rc.4" })
75539
75561
  ]
75540
75562
  }
75541
75563
  ),
@@ -652,6 +652,10 @@
652
652
  height: 180px;
653
653
  }
654
654
 
655
+ .h-\[200px\] {
656
+ height: 200px;
657
+ }
658
+
655
659
  .h-full {
656
660
  height: 100%;
657
661
  }
@@ -951,6 +955,10 @@
951
955
  overflow-x: auto;
952
956
  }
953
957
 
958
+ .overflow-y-auto {
959
+ overflow-y: auto;
960
+ }
961
+
954
962
  .rounded {
955
963
  border-radius: .25rem;
956
964
  }
@@ -1462,6 +1470,10 @@
1462
1470
  padding-top: calc(var(--spacing) * 0);
1463
1471
  }
1464
1472
 
1473
+ .pt-2 {
1474
+ padding-top: calc(var(--spacing) * 2);
1475
+ }
1476
+
1465
1477
  .pr-2 {
1466
1478
  padding-right: calc(var(--spacing) * 2);
1467
1479
  }
@@ -1795,6 +1807,11 @@
1795
1807
  transition-duration: var(--tw-duration, var(--default-transition-duration));
1796
1808
  }
1797
1809
 
1810
+ .duration-100 {
1811
+ --tw-duration: .1s;
1812
+ transition-duration: .1s;
1813
+ }
1814
+
1798
1815
  .duration-200 {
1799
1816
  --tw-duration: .2s;
1800
1817
  transition-duration: .2s;
@@ -7,8 +7,8 @@
7
7
  http-equiv="Content-Security-Policy"
8
8
  content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
9
9
  />
10
- <script type="module" crossorigin src="./assets/index-Dyv8tZ_T.js"></script>
11
- <link rel="stylesheet" crossorigin href="./assets/index-D2W2biEe.css">
10
+ <script type="module" crossorigin src="./assets/index-BttVUwrw.js"></script>
11
+ <link rel="stylesheet" crossorigin href="./assets/index-DjlJs7Yy.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openwork",
3
- "version": "0.1.1-rc.2",
3
+ "version": "0.1.1-rc.4",
4
4
  "description": "A tactical agent interface for deepagentsjs",
5
5
  "main": "./out/main/index.js",
6
6
  "files": [
@@ -47,12 +47,12 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "electron": "^39.2.6",
50
- "@langchain/anthropic": "^1.3.7",
51
- "@langchain/core": "^1.1.12",
50
+ "@langchain/anthropic": "^1.3.10",
51
+ "@langchain/core": "1.1.15",
52
52
  "@langchain/langgraph": "^1.0.15",
53
53
  "@langchain/langgraph-checkpoint": "^1.0.0",
54
54
  "@langchain/langgraph-sdk": "^1.5.3",
55
- "@langchain/openai": "^1.2.1",
55
+ "@langchain/openai": "^1.2.2",
56
56
  "@radix-ui/react-context-menu": "^2.2.16",
57
57
  "@radix-ui/react-dialog": "^1.1.15",
58
58
  "@radix-ui/react-dropdown-menu": "^2.1.16",