tarsk 0.4.27 → 0.4.29

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 (92) hide show
  1. package/dist/index.js +467 -173
  2. package/dist/public/assets/account-view-YvC0mymF.js +1 -0
  3. package/dist/public/assets/alert-dialog-BFhUZsPe.js +1 -0
  4. package/dist/public/assets/api-DSvSyBH5.js +1 -0
  5. package/dist/public/assets/{browser-tab-DlzO5cMP.js → browser-tab-CCXMlmPO.js} +1 -1
  6. package/dist/public/assets/chat-input-container-VwmGKJFJ.js +21 -0
  7. package/dist/public/assets/codicon-ngg6Pgfi.ttf +0 -0
  8. package/dist/public/assets/context-menu-B6K8j1ha.js +1 -0
  9. package/dist/public/assets/conversation-history-view-DrrLyMbg.js +1 -0
  10. package/dist/public/assets/css.worker-DyDu5ynT.js +89 -0
  11. package/dist/public/assets/{dialogs-config-DZFG2IQM.js → dialogs-config-B91VQpxo.js} +3 -3
  12. package/dist/public/assets/diff-view-C09yOc5d.js +3 -0
  13. package/dist/public/assets/explorer-tab-view-B1DukGdo.js +2 -0
  14. package/dist/public/assets/{explorer-tree-8TyBYuC7.js → explorer-tree-DCeud6YI.js} +1 -1
  15. package/dist/public/assets/explorer-view-BCJqE1-z.js +1 -0
  16. package/dist/public/assets/history-view-Dj4NiScy.js +1 -0
  17. package/dist/public/assets/html.worker-IWNXhnIC.js +502 -0
  18. package/dist/public/assets/index-7TFkb0ll.css +1 -0
  19. package/dist/public/assets/index-BLCecayZ.js +29 -0
  20. package/dist/public/assets/json.worker-c4h5s80L.js +58 -0
  21. package/dist/public/assets/markdown-renderer-9nzN0Pk3.js +10 -0
  22. package/dist/public/assets/{mcp-server-card-DupwHcKT.js → mcp-server-card-BSw_FlEC.js} +1 -1
  23. package/dist/public/assets/monaco-BGyhgQtx.js +1209 -0
  24. package/dist/public/assets/monaco-Br_kD0ds.css +1 -0
  25. package/dist/public/assets/onboarding-ClUE_I5q.js +1 -0
  26. package/dist/public/assets/onboarding-dialog-C2jig8mT.js +1 -0
  27. package/dist/public/assets/page-toolbar-DnVFnCgq.js +1 -0
  28. package/dist/public/assets/{project-settings-view-C7GW_R0m.js → project-settings-view-DWn0ty7b.js} +1 -1
  29. package/dist/public/assets/providers-list-view-B0DqNJYK.js +1 -0
  30. package/dist/public/assets/radio-group-CM_Wwam0.js +1 -0
  31. package/dist/public/assets/react-vendor-nPkCWaAR.js +22 -0
  32. package/dist/public/assets/resizable-CEv7JjTI.js +1 -0
  33. package/dist/public/assets/{run-stop-button-k1LeM0M-.js → run-stop-button-CrjPMC0m.js} +2 -2
  34. package/dist/public/assets/settings-general-view-CwQYmnI2.js +1 -0
  35. package/dist/public/assets/settings-instructions-view-C23spY9Y.js +1 -0
  36. package/dist/public/assets/settings-mcp-servers-view-ZHar6YbE.js +5 -0
  37. package/dist/public/assets/settings-models-view-Cg6WQ4cy.js +1 -0
  38. package/dist/public/assets/settings-rules-view-zAx5JL4w.js +8 -0
  39. package/dist/public/assets/{settings-skills-view-aPv03-bO.js → settings-skills-view-D60tD-OE.js} +2 -2
  40. package/dist/public/assets/{settings-slash-commands-view-DLMWXQXA.js → settings-slash-commands-view-DR-pzulT.js} +1 -1
  41. package/dist/public/assets/{settings-subagents-view-DBYzNE4W.js → settings-subagents-view-InXmwmw9.js} +2 -2
  42. package/dist/public/assets/settings-view-DLRDBuwH.js +2 -0
  43. package/dist/public/assets/side-panel-container-CO3qgRvc.js +2 -0
  44. package/dist/public/assets/skeleton-CXoVAzC1.js +1 -0
  45. package/dist/public/assets/{standard-list-item-D2lbPXa1.js → standard-list-item-DyALhVYe.js} +1 -1
  46. package/dist/public/assets/store-D28g9VUj.js +4 -0
  47. package/dist/public/assets/{tab-context-CCvlrC0o.js → tab-context-C3z7YU59.js} +1 -1
  48. package/dist/public/assets/tabs-sXgL2DZz.js +1 -0
  49. package/dist/public/assets/{terminal-panel-GuO2XygM.js → terminal-panel-EifoA6G0.js} +2 -2
  50. package/dist/public/assets/textarea-Bp3p1-25.js +1 -0
  51. package/dist/public/assets/todos-view-DRL4px6A.js +1 -0
  52. package/dist/public/assets/ts.worker-BwM5Ha3L.js +67719 -0
  53. package/dist/public/assets/use-font-size-BCtvz0bm.js +1 -0
  54. package/dist/public/assets/{use-toast-DqmC-mB5.js → use-toast-BZ79E_dX.js} +1 -1
  55. package/dist/public/assets/{utils-Cy2s2TEb.js → utils-Bhu11Tpg.js} +1 -1
  56. package/dist/public/assets/{whisper-wasm-Clnf-l-9.js → whisper-wasm-C_Ot631g.js} +1 -1
  57. package/dist/public/ide/antigravity.svg +1 -0
  58. package/dist/public/ide/devin.svg +1 -0
  59. package/dist/public/index.html +24 -21
  60. package/package.json +1 -1
  61. package/dist/public/assets/account-view-BFhjAYmz.js +0 -1
  62. package/dist/public/assets/alert-dialog--tUzAWFm.js +0 -1
  63. package/dist/public/assets/api-BBbysRIh.js +0 -1
  64. package/dist/public/assets/chat-input-container-B_qVOGzW.js +0 -21
  65. package/dist/public/assets/context-menu-D5slxFcc.js +0 -1
  66. package/dist/public/assets/conversation-history-view-B4zHwyrw.js +0 -1
  67. package/dist/public/assets/diff-view-fOVqrHQW.js +0 -3
  68. package/dist/public/assets/explorer-tab-view-MLrWJ1Y0.js +0 -2
  69. package/dist/public/assets/explorer-view-5U70mfAJ.js +0 -1
  70. package/dist/public/assets/history-view-BR8scGnM.js +0 -1
  71. package/dist/public/assets/index-0BuHfewk.css +0 -1
  72. package/dist/public/assets/index-BTbiyooe.js +0 -37
  73. package/dist/public/assets/monaco-DDfTQM3i.js +0 -11
  74. package/dist/public/assets/onboarding-Cn5RqOCa.js +0 -1
  75. package/dist/public/assets/onboarding-dialog-BXsvjU1t.js +0 -1
  76. package/dist/public/assets/page-toolbar-TvYsDwUY.js +0 -1
  77. package/dist/public/assets/providers-list-view-AVt5I1WB.js +0 -1
  78. package/dist/public/assets/radio-group-Mifbn8sU.js +0 -1
  79. package/dist/public/assets/react-vendor-Bao-KYKs.js +0 -22
  80. package/dist/public/assets/resizable-DzrIos_Y.js +0 -1
  81. package/dist/public/assets/settings-instructions-view-CgfWSPmK.js +0 -1
  82. package/dist/public/assets/settings-mcp-servers-view-Becs_RxN.js +0 -5
  83. package/dist/public/assets/settings-models-view-BqdD_7GR.js +0 -1
  84. package/dist/public/assets/settings-rules-view-D7j5OHUi.js +0 -8
  85. package/dist/public/assets/settings-view-CccwUKkf.js +0 -2
  86. package/dist/public/assets/side-panel-container-oMcXib2A.js +0 -2
  87. package/dist/public/assets/skeleton-Bc0U0oGc.js +0 -1
  88. package/dist/public/assets/store-CwvBC-YP.js +0 -2
  89. package/dist/public/assets/tabs-Mb_cjIfE.js +0 -1
  90. package/dist/public/assets/textarea-DpXOZLt8.js +0 -1
  91. package/dist/public/assets/todos-view-C4GMi1Np.js +0 -1
  92. /package/dist/public/assets/{dist-BI23Ei33.js → dist-D5DgwfYL.js} +0 -0
package/dist/index.js CHANGED
@@ -769,6 +769,14 @@ var PROGRAMS_CONFIG = {
769
769
  Terminal: {
770
770
  name: "Terminal",
771
771
  appName: "Terminal"
772
+ },
773
+ "Devin Desktop": {
774
+ name: "Devin Desktop",
775
+ appName: ["Devin", "Devin Desktop"]
776
+ },
777
+ Antigravity: {
778
+ name: "Antigravity",
779
+ appName: ["com.google.antigravity-ide", "Antigravity IDE"]
772
780
  }
773
781
  };
774
782
  var AVAILABLE_PROGRAMS = Object.keys(PROGRAMS_CONFIG);
@@ -1820,9 +1828,9 @@ function createGrepTool(cwd, options) {
1820
1828
  const effectiveLimit = Math.max(1, limit ?? DEFAULT_LIMIT2);
1821
1829
  const formatPath = (filePath) => {
1822
1830
  if (isDirectory) {
1823
- const relative7 = path2.relative(searchPath, filePath);
1824
- if (relative7 && !relative7.startsWith("..")) {
1825
- return relative7.replace(/\\/g, "/");
1831
+ const relative8 = path2.relative(searchPath, filePath);
1832
+ if (relative8 && !relative8.startsWith("..")) {
1833
+ return relative8.replace(/\\/g, "/");
1826
1834
  }
1827
1835
  }
1828
1836
  return path2.basename(filePath);
@@ -2175,7 +2183,7 @@ async function invalidateThreadIndex(threadId) {
2175
2183
  }
2176
2184
 
2177
2185
  // src/tools/code-search.ts
2178
- var DEFAULT_LIMIT3 = 100;
2186
+ var DEFAULT_LIMIT3 = 30;
2179
2187
  var codeSearchSchema = Type5.Object({
2180
2188
  query: Type5.Optional(
2181
2189
  Type5.String({
@@ -2189,7 +2197,7 @@ var codeSearchSchema = Type5.Object({
2189
2197
  ),
2190
2198
  filePath: Type5.Optional(
2191
2199
  Type5.String({
2192
- description: "Search by file path pattern, e.g. 'camp.page.ts', 'src/app/camp', or 'src/**/camp/*.ts'. Supports glob wildcards (* and **). Returns all lines from matching files."
2200
+ description: "Search by file path pattern, e.g. 'camp.page.ts', 'src/app/camp', or 'src/**/camp/*.ts'. Supports glob wildcards (* and **). Returns file paths only, no content."
2193
2201
  })
2194
2202
  ),
2195
2203
  fileGlob: Type5.Optional(
@@ -2324,10 +2332,12 @@ function formatOutput(rows, tokens, regex, limit, multiline = false) {
2324
2332
  const matchLimitReached = rows.length >= limit ? rows.length : void 0;
2325
2333
  for (const { file_path, content } of rows) {
2326
2334
  const matchingLines = regex ? findLinesByRegex(content, regex, multiline) : findLinesByTokens(content, tokens);
2335
+ if (matchingLines.length === 0) continue;
2336
+ outputLines.push(`${file_path}:`);
2327
2337
  for (const { line, text } of matchingLines) {
2328
2338
  const { text: truncated, wasTruncated } = truncateLine(text.replace(/\r/g, ""));
2329
2339
  if (wasTruncated) linesTruncated = true;
2330
- outputLines.push(`${file_path}:${line}: ${truncated}`);
2340
+ outputLines.push(` ${line}: ${truncated}`);
2331
2341
  }
2332
2342
  }
2333
2343
  return { outputLines, linesTruncated, matchLimitReached };
@@ -2435,24 +2445,15 @@ function createCodeSearchTool(cwd, threadId) {
2435
2445
  if (filePath && !query && !pattern) {
2436
2446
  const likePattern = filePathToLike(filePath);
2437
2447
  const result = await db.execute(
2438
- `SELECT file_path, content FROM code_files
2448
+ `SELECT file_path FROM code_files
2439
2449
  WHERE thread_id = ?
2440
2450
  AND file_path LIKE ? ESCAPE '\\'
2441
2451
  LIMIT ? OFFSET ?`,
2442
2452
  [threadId, likePattern, limit, offset]
2443
2453
  );
2444
2454
  const rows2 = result.rows;
2445
- const { outputLines: outputLines2, linesTruncated: linesTruncated2, matchLimitReached: matchLimitReached2 } = formatOutput(
2446
- rows2,
2447
- [],
2448
- null,
2449
- limit
2450
- );
2451
- if (outputLines2.length === 0) {
2452
- const names = rows2.map((r) => r.file_path).join("\n");
2453
- return buildResult(names ? names.split("\n") : [], false, void 0, limit, offset);
2454
- }
2455
- return buildResult(outputLines2, linesTruncated2, matchLimitReached2, limit, offset);
2455
+ const names = rows2.map((r) => r.file_path);
2456
+ return buildResult(names, false, void 0, limit, offset);
2456
2457
  }
2457
2458
  if (filePath && (query || pattern)) {
2458
2459
  const likePattern = filePathToLike(filePath);
@@ -2482,7 +2483,7 @@ function createCodeSearchTool(cwd, threadId) {
2482
2483
  const args2 = likeFilter ? [threadId, likeFilter] : [threadId];
2483
2484
  const result = await db.execute(sql, args2);
2484
2485
  const allRows = result.rows;
2485
- const outputLines2 = [];
2486
+ const fileGroups = [];
2486
2487
  let linesTruncated2 = false;
2487
2488
  let matchedFiles = 0;
2488
2489
  for (const { file_path, content } of allRows) {
@@ -2491,10 +2492,18 @@ function createCodeSearchTool(cwd, threadId) {
2491
2492
  matchedFiles++;
2492
2493
  if (matchedFiles <= offset) continue;
2493
2494
  if (matchedFiles > offset + limit) break;
2494
- for (const { line, text } of matchingLines) {
2495
- const { text: truncated, wasTruncated } = truncateLine(text.replace(/\r/g, ""));
2495
+ fileGroups.push({ filePath: file_path, lines: matchingLines });
2496
+ for (const { text } of matchingLines) {
2497
+ const { wasTruncated } = truncateLine(text.replace(/\r/g, ""));
2496
2498
  if (wasTruncated) linesTruncated2 = true;
2497
- outputLines2.push(`${file_path}:${line}: ${truncated}`);
2499
+ }
2500
+ }
2501
+ const outputLines2 = [];
2502
+ for (const group of fileGroups) {
2503
+ outputLines2.push(`${group.filePath}:`);
2504
+ for (const { line, text } of group.lines) {
2505
+ const { text: truncated } = truncateLine(text.replace(/\r/g, ""));
2506
+ outputLines2.push(` ${line}: ${truncated}`);
2498
2507
  }
2499
2508
  }
2500
2509
  const matchLimitReached2 = matchedFiles > offset + limit ? matchedFiles : void 0;
@@ -4140,16 +4149,56 @@ var ModelManager = class {
4140
4149
  };
4141
4150
 
4142
4151
  // src/features/providers/provider-resolver.ts
4152
+ function getProviderApiKeyEnvName(providerEnv) {
4153
+ if (providerEnv.length === 0) {
4154
+ return "";
4155
+ }
4156
+ const explicitApiKeyEnv = providerEnv.find((envName) => /api_key$/i.test(envName));
4157
+ return explicitApiKeyEnv ?? providerEnv[providerEnv.length - 1];
4158
+ }
4159
+ function getProviderApiUrlEnvName(providerName) {
4160
+ return `${providerName.toUpperCase().replace(/-/g, "_")}_API_URL`;
4161
+ }
4162
+ function getProviderResourceName(providerEnv) {
4163
+ const explicitResourceEnv = providerEnv.find((envName) => /resource_name$/i.test(envName));
4164
+ return explicitResourceEnv ?? providerEnv[0] ?? "";
4165
+ }
4166
+ function resolveAzureApiUrl(providerEnv) {
4167
+ const resourceEnvName = getProviderResourceName(providerEnv);
4168
+ const resourceName = process.env[resourceEnvName];
4169
+ if (!resourceName) {
4170
+ return "";
4171
+ }
4172
+ return `https://${resourceName}.openai.azure.com/openai/v1`;
4173
+ }
4174
+ function resolveTemplate(template, envValues) {
4175
+ let hasMissingValue = false;
4176
+ const resolved = template.replace(/\$\{([A-Z0-9_]+)\}/gi, (_match, envName) => {
4177
+ const value = envValues[envName] ?? process.env[envName] ?? "";
4178
+ if (!value) {
4179
+ hasMissingValue = true;
4180
+ }
4181
+ return value;
4182
+ });
4183
+ return hasMissingValue ? "" : resolved;
4184
+ }
4143
4185
  async function resolveProviderConfig(providerName) {
4144
4186
  const slug = providerName.toLowerCase();
4145
4187
  const catalogProvider = await getCatalogProvider(slug);
4146
4188
  if (!catalogProvider) return null;
4147
- return catalogProvider;
4189
+ const apiKeyEnv = getProviderApiKeyEnvName(catalogProvider.env ?? []);
4190
+ const apiUrlEnvName = getProviderApiUrlEnvName(slug);
4191
+ const api = slug === "azure" ? resolveAzureApiUrl(catalogProvider.env ?? []) : process.env[apiUrlEnvName] || (catalogProvider.api ? resolveTemplate(catalogProvider.api, process.env) : "");
4192
+ return {
4193
+ ...catalogProvider,
4194
+ api,
4195
+ apiKeyEnv
4196
+ };
4148
4197
  }
4149
4198
  async function resolveProviderKeyName(providerName) {
4150
4199
  const slug = providerName.toLowerCase();
4151
4200
  const catalogProvider = await getCatalogProvider(slug);
4152
- if (catalogProvider?.env?.[0]) return catalogProvider.env[0];
4201
+ if (catalogProvider?.env?.length) return getProviderApiKeyEnvName(catalogProvider.env);
4153
4202
  return "";
4154
4203
  }
4155
4204
 
@@ -4346,7 +4395,7 @@ function createGenerateImageTool(options) {
4346
4395
  details: void 0
4347
4396
  };
4348
4397
  }
4349
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
4398
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
4350
4399
  if (!apiKey) {
4351
4400
  const providerKeys = await metadataManager.getProviderKeys();
4352
4401
  apiKey = providerKeys[providerConfig.id];
@@ -4784,8 +4833,12 @@ function createToolSearchTool(options) {
4784
4833
  let matched = [];
4785
4834
  if (parsed.mode === "select") {
4786
4835
  for (const name of parsed.names) {
4787
- const tool = deferredTools.get(name);
4788
- if (tool) matched.push(tool);
4836
+ for (const [toolName, tool] of deferredTools.entries()) {
4837
+ if (toolName.toLowerCase() === name) {
4838
+ matched.push(tool);
4839
+ break;
4840
+ }
4841
+ }
4789
4842
  }
4790
4843
  } else {
4791
4844
  const candidates = [];
@@ -5039,6 +5092,7 @@ var EventQueue = class {
5039
5092
  finalContent = "";
5040
5093
  errorOccurred = false;
5041
5094
  toolCallCount = 0;
5095
+ lastErrorMessage;
5042
5096
  /**
5043
5097
  * Processes Pi Agent events and transforms them into AgentEvent format
5044
5098
  */
@@ -5107,6 +5161,9 @@ var EventQueue = class {
5107
5161
  } else if (event.type === "agent_end") {
5108
5162
  const messages = event.messages;
5109
5163
  const lastAssistant = [...messages].reverse().find((m) => "role" in m && m.role === "assistant");
5164
+ if (lastAssistant && "errorMessage" in lastAssistant && typeof lastAssistant.errorMessage === "string") {
5165
+ this.lastErrorMessage = lastAssistant.errorMessage;
5166
+ }
5110
5167
  if (lastAssistant) {
5111
5168
  const extracted = extractTextFromAssistantMessage(lastAssistant);
5112
5169
  if (extracted && extracted !== this.finalContent) {
@@ -5220,14 +5277,14 @@ async function executeSubagent(options) {
5220
5277
  if (!providerConfig) {
5221
5278
  throw new Error(`Unknown provider for subagent: ${providerName}`);
5222
5279
  }
5223
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
5280
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
5224
5281
  if (!apiKey) {
5225
5282
  const providerKeys = await metadataManager.getProviderKeys();
5226
5283
  apiKey = providerKeys[providerConfig.id];
5227
5284
  }
5228
5285
  if (!apiKey) {
5229
5286
  throw new Error(
5230
- `No API key found for subagent provider: ${providerName}. Set ${providerConfig.env && providerConfig.env.length > 0 ? providerConfig.env[0] : "an API key"} in environment variables or configuration.`
5287
+ `No API key found for subagent provider: ${providerName}. Set ${providerConfig.apiKeyEnv ?? "an API key"} in environment variables or configuration.`
5231
5288
  );
5232
5289
  }
5233
5290
  const resolvedModel = resolveModel(providerName, model, providerConfig);
@@ -6115,10 +6172,94 @@ async function loadAgentSystemPrompt(threadPath, tools, skills, planMode, agents
6115
6172
  var cachedStreamFn = (model, context, options) => {
6116
6173
  return streamSimple(model, context, { ...options, cacheRetention: "long" });
6117
6174
  };
6175
+ function summarizeAssistantResponse(response) {
6176
+ const lines = response.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
6177
+ const salient = lines.filter(
6178
+ (line) => /^(#+\s|[-*]\s|\d+\.\s|```|changed|created|updated|deleted|fixed|implemented|added|removed|renamed|moved|tested|next|summary|result)/i.test(
6179
+ line
6180
+ ) || line.length <= 180
6181
+ );
6182
+ const summary = (salient.length > 0 ? salient : lines).slice(0, 12).join("\n");
6183
+ return summary.length > 3e3 ? `${summary.slice(0, 3e3)}\u2026` : summary;
6184
+ }
6185
+ function getAssistantHistoryContent(exchange, isMostRecent) {
6186
+ if (isMostRecent) {
6187
+ return exchange.assistantResponse;
6188
+ }
6189
+ return exchange.assistantSummary ?? summarizeAssistantResponse(exchange.assistantResponse);
6190
+ }
6191
+ function buildAssistantHistoryText(content, isMostRecent) {
6192
+ if (isMostRecent) {
6193
+ return content;
6194
+ }
6195
+ return `[Earlier assistant response summarized to reduce context tokens]
6196
+ ${content}`;
6197
+ }
6198
+ function serializeLogValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
6199
+ if (value === null || value === void 0) {
6200
+ return value;
6201
+ }
6202
+ const valueType = typeof value;
6203
+ if (valueType === "string" || valueType === "number" || valueType === "boolean") {
6204
+ return value;
6205
+ }
6206
+ if (valueType === "bigint" || valueType === "symbol") {
6207
+ return "[Primitive]";
6208
+ }
6209
+ if (valueType === "function") {
6210
+ return "[Function]";
6211
+ }
6212
+ if (value instanceof Error) {
6213
+ const serializedError = {
6214
+ name: value.name,
6215
+ message: value.message
6216
+ };
6217
+ if (value.stack) {
6218
+ serializedError.stack = value.stack;
6219
+ }
6220
+ if ("cause" in value) {
6221
+ serializedError.cause = serializeLogValue(
6222
+ value.cause,
6223
+ depth + 1,
6224
+ seen
6225
+ );
6226
+ }
6227
+ const errorObject = value;
6228
+ for (const [key, nestedValue] of Object.entries(errorObject)) {
6229
+ if (key in serializedError) continue;
6230
+ serializedError[key] = serializeLogValue(nestedValue, depth + 1, seen);
6231
+ }
6232
+ return serializedError;
6233
+ }
6234
+ if (Array.isArray(value)) {
6235
+ if (depth >= 4) {
6236
+ return "[Truncated]";
6237
+ }
6238
+ return value.slice(0, 50).map((item) => serializeLogValue(item, depth + 1, seen));
6239
+ }
6240
+ if (valueType === "object") {
6241
+ if (seen.has(value)) {
6242
+ return "[Circular]";
6243
+ }
6244
+ if (depth >= 4) {
6245
+ return "[Truncated]";
6246
+ }
6247
+ seen.add(value);
6248
+ const serializedObject = {};
6249
+ const plainObject = value;
6250
+ for (const [key, nestedValue] of Object.entries(plainObject).slice(0, 50)) {
6251
+ serializedObject[key] = serializeLogValue(nestedValue, depth + 1, seen);
6252
+ }
6253
+ return serializedObject;
6254
+ }
6255
+ return "[Unsupported value]";
6256
+ }
6118
6257
  function buildHistoryMessages(history) {
6119
6258
  const messages = [];
6120
6259
  const now = Date.now();
6121
- for (const exchange of history) {
6260
+ history.forEach((exchange, index) => {
6261
+ const isMostRecent = index === history.length - 1;
6262
+ const assistantContent = getAssistantHistoryContent(exchange, isMostRecent);
6122
6263
  messages.push({
6123
6264
  role: "user",
6124
6265
  content: exchange.userMessage,
@@ -6126,7 +6267,9 @@ function buildHistoryMessages(history) {
6126
6267
  });
6127
6268
  messages.push({
6128
6269
  role: "assistant",
6129
- content: [{ type: "text", text: exchange.assistantResponse }],
6270
+ content: [
6271
+ { type: "text", text: buildAssistantHistoryText(assistantContent, isMostRecent) }
6272
+ ],
6130
6273
  api: "",
6131
6274
  provider: "",
6132
6275
  model: "",
@@ -6141,7 +6284,7 @@ function buildHistoryMessages(history) {
6141
6284
  stopReason: "stop",
6142
6285
  timestamp: now
6143
6286
  });
6144
- }
6287
+ });
6145
6288
  return messages;
6146
6289
  }
6147
6290
  var PiExecutorImpl = class {
@@ -6171,7 +6314,7 @@ var PiExecutorImpl = class {
6171
6314
  if (!providerConfig) {
6172
6315
  throw new Error(`Unknown provider: ${providerName}`);
6173
6316
  }
6174
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
6317
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
6175
6318
  let apiKeySource = "environment";
6176
6319
  if (!apiKey) {
6177
6320
  try {
@@ -6187,7 +6330,7 @@ var PiExecutorImpl = class {
6187
6330
  }
6188
6331
  if (!apiKey) {
6189
6332
  throw new Error(
6190
- `No API key found for provider: ${providerName}. Set ${providerConfig.env && providerConfig.env.length > 0 ? providerConfig.env[0] : "an API key"} in environment variables or configuration.`
6333
+ `No API key found for provider: ${providerName}. Set ${providerConfig.apiKeyEnv ?? "an API key"} in environment variables or configuration.`
6191
6334
  );
6192
6335
  }
6193
6336
  if (apiKey.includes(":") && apiKey.length > 100) {
@@ -6302,15 +6445,27 @@ ${decoded}
6302
6445
 
6303
6446
  ${userPrompt}`;
6304
6447
  }
6305
- console.log(
6306
- `[ai] Attachments: ${context.attachments?.length ?? 0} total, ${images.length} image(s), ${textAttachments.length} text file(s)`,
6307
- (context.attachments ?? []).map((a) => ({
6448
+ const promptRequest = {
6449
+ provider: providerName,
6450
+ model,
6451
+ cwd,
6452
+ prompt: finalPrompt,
6453
+ promptLength: finalPrompt.length,
6454
+ promptPreview: finalPrompt.substring(0, 200) + (finalPrompt.length > 200 ? "..." : ""),
6455
+ imageCount: images.length,
6456
+ images: images.map((img) => ({
6457
+ mimeType: img.mimeType,
6458
+ dataLength: img.data?.length ?? 0
6459
+ })),
6460
+ attachments: (context.attachments ?? []).map((a) => ({
6308
6461
  name: a.name,
6309
6462
  mimeType: a.mimeType,
6310
6463
  contentLength: a.content?.length ?? 0
6311
- })),
6312
- "images \u2192",
6313
- images.map((img) => ({ mimeType: img.mimeType, dataLength: img.data?.length ?? 0 }))
6464
+ }))
6465
+ };
6466
+ console.log(
6467
+ `[ai] Attachments: ${context.attachments?.length ?? 0} total, ${images.length} image(s), ${textAttachments.length} text file(s)`,
6468
+ promptRequest
6314
6469
  );
6315
6470
  const promptDone = agent.prompt(finalPrompt, images.length > 0 ? images : void 0).then(() => {
6316
6471
  console.log(`[ai] Agent prompt completed successfully`);
@@ -6319,7 +6474,8 @@ ${userPrompt}`;
6319
6474
  }).catch((err) => {
6320
6475
  const errMessage = err instanceof Error ? err.message : String(err);
6321
6476
  console.error(`[ai] Agent prompt failed:`, {
6322
- error: errMessage,
6477
+ request: promptRequest,
6478
+ error: serializeLogValue(err),
6323
6479
  errorType: err instanceof Error ? err.constructor.name : typeof err
6324
6480
  });
6325
6481
  if (!eventQueue.errorOccurred) {
@@ -6373,12 +6529,20 @@ ${userPrompt}`;
6373
6529
  };
6374
6530
  } else {
6375
6531
  console.log("[ai] \u26A0\uFE0F Agent ended with no text content and no tool calls.");
6532
+ const upstreamError = eventQueue.lastErrorMessage ? { message: eventQueue.lastErrorMessage } : void 0;
6533
+ console.error("[ai] Agent ended without text content:", {
6534
+ request: promptRequest,
6535
+ upstreamError,
6536
+ finalContentLength: eventQueue.finalContent.length,
6537
+ toolCallCount: eventQueue.toolCallCount
6538
+ });
6376
6539
  yield {
6377
6540
  type: "error",
6378
6541
  content: `The model ${model} from ${providerName} did not provide a response to your prompt. Check your API key and balance and try again.`,
6379
6542
  error: {
6380
6543
  code: "NO_CONTENT_NO_TOOLS",
6381
- message: `The model ${model} from ${providerName} did not provide a response to your prompt.`
6544
+ message: `The model ${model} from ${providerName} did not provide a response to your prompt.`,
6545
+ details: upstreamError
6382
6546
  }
6383
6547
  };
6384
6548
  }
@@ -6485,6 +6649,63 @@ var ProcessingStateManagerImpl = class {
6485
6649
  }
6486
6650
  };
6487
6651
 
6652
+ // src/core/env-manager.ts
6653
+ import { promises as fs } from "fs";
6654
+ import { join as join11 } from "path";
6655
+ function updateRuntimeEnv(values) {
6656
+ for (const [key, value] of Object.entries(values)) {
6657
+ process.env[key] = value;
6658
+ }
6659
+ }
6660
+ async function updateEnvFile(keyNames) {
6661
+ const envPath = join11(process.cwd(), ".env");
6662
+ let content = "";
6663
+ try {
6664
+ content = await fs.readFile(envPath, "utf-8");
6665
+ } catch {
6666
+ }
6667
+ const lines = content.split("\n");
6668
+ const envMap = {};
6669
+ for (const line of lines) {
6670
+ const trimmed = line.trim();
6671
+ if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
6672
+ const index = trimmed.indexOf("=");
6673
+ const key = trimmed.substring(0, index).trim();
6674
+ const value = trimmed.substring(index + 1).trim();
6675
+ envMap[key] = value;
6676
+ }
6677
+ }
6678
+ for (const [key, value] of Object.entries(keyNames)) {
6679
+ if (key) {
6680
+ envMap[key] = value;
6681
+ }
6682
+ }
6683
+ const newLines = [];
6684
+ for (const [key, value] of Object.entries(envMap)) {
6685
+ newLines.push(`${key}=${value}`);
6686
+ }
6687
+ await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
6688
+ }
6689
+ async function readEnvFile() {
6690
+ const envPath = join11(process.cwd(), ".env");
6691
+ const envMap = {};
6692
+ try {
6693
+ const content = await fs.readFile(envPath, "utf-8");
6694
+ const lines = content.split("\n");
6695
+ for (const line of lines) {
6696
+ const trimmed = line.trim();
6697
+ if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
6698
+ const index = trimmed.indexOf("=");
6699
+ const key = trimmed.substring(0, index).trim();
6700
+ const value = trimmed.substring(index + 1).trim();
6701
+ envMap[key] = value;
6702
+ }
6703
+ }
6704
+ } catch {
6705
+ }
6706
+ return envMap;
6707
+ }
6708
+
6488
6709
  // src/features/ask-user/ask-user.routes.ts
6489
6710
  import { Hono } from "hono";
6490
6711
 
@@ -6659,7 +6880,7 @@ import { randomUUID as randomUUID4 } from "crypto";
6659
6880
 
6660
6881
  // src/features/skills/skills.manager.ts
6661
6882
  import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
6662
- import { join as join11 } from "path";
6883
+ import { join as join12 } from "path";
6663
6884
  import { existsSync as existsSync9 } from "fs";
6664
6885
  import { homedir as homedir5 } from "os";
6665
6886
  function parseFrontmatter(markdown) {
@@ -6707,10 +6928,10 @@ function parseFrontmatter(markdown) {
6707
6928
  return { metadata, content };
6708
6929
  }
6709
6930
  function getGlobalSkillsDir() {
6710
- return join11(homedir5(), ".agents", "skills");
6931
+ return join12(homedir5(), ".agents", "skills");
6711
6932
  }
6712
6933
  function getProjectSkillsDir(threadPath) {
6713
- return join11(threadPath, ".agents", "skills");
6934
+ return join12(threadPath, ".agents", "skills");
6714
6935
  }
6715
6936
  function validateSkillName(name) {
6716
6937
  if (!name || name.length === 0 || name.length > 64) {
@@ -6763,8 +6984,8 @@ var SkillManager = class {
6763
6984
  for (const entry of entries) {
6764
6985
  if (!entry.isDirectory()) continue;
6765
6986
  const skillDirName = entry.name;
6766
- const skillPath = join11(dir, skillDirName);
6767
- const skillFilePath = join11(skillPath, "SKILL.md");
6987
+ const skillPath = join12(dir, skillDirName);
6988
+ const skillFilePath = join12(skillPath, "SKILL.md");
6768
6989
  if (!existsSync9(skillFilePath)) {
6769
6990
  console.warn(`Skipping skill directory ${skillDirName}: SKILL.md not found`);
6770
6991
  continue;
@@ -6944,7 +7165,7 @@ async function activateSkills(allSkills, taskDescription, thread, options) {
6944
7165
 
6945
7166
  // src/features/agents/agents.manager.ts
6946
7167
  import { readdir as readdir5, readFile as readFile7 } from "fs/promises";
6947
- import { join as join12 } from "path";
7168
+ import { join as join13 } from "path";
6948
7169
  import { existsSync as existsSync10 } from "fs";
6949
7170
  import { homedir as homedir6 } from "os";
6950
7171
  function parseFrontmatter2(markdown) {
@@ -7002,10 +7223,10 @@ function validateDescription2(description) {
7002
7223
  return !!description && description.length > 0 && description.length <= 1024;
7003
7224
  }
7004
7225
  function getGlobalAgentsDir() {
7005
- return join12(homedir6(), ".agents", "agents");
7226
+ return join13(homedir6(), ".agents", "agents");
7006
7227
  }
7007
7228
  function getProjectAgentsDir(threadPath) {
7008
- return join12(threadPath, ".agents", "agents");
7229
+ return join13(threadPath, ".agents", "agents");
7009
7230
  }
7010
7231
  var AgentsManager = class {
7011
7232
  /**
@@ -7037,8 +7258,8 @@ var AgentsManager = class {
7037
7258
  for (const entry of entries) {
7038
7259
  if (!entry.isDirectory()) continue;
7039
7260
  const agentDirName = entry.name;
7040
- const agentPath = join12(dir, agentDirName);
7041
- const agentFilePath = join12(agentPath, "AGENT.md");
7261
+ const agentPath = join13(dir, agentDirName);
7262
+ const agentFilePath = join13(agentPath, "AGENT.md");
7042
7263
  if (!existsSync10(agentFilePath)) {
7043
7264
  console.warn(`[agents] Skipping agent directory ${agentDirName}: AGENT.md not found`);
7044
7265
  continue;
@@ -7225,7 +7446,7 @@ function extractAssistantContent(events, fallback) {
7225
7446
  // src/features/chat/chat-post.route.ts
7226
7447
  init_database();
7227
7448
  import { readFile as readFile8, unlink } from "fs/promises";
7228
- import { join as join13 } from "path";
7449
+ import { join as join14 } from "path";
7229
7450
 
7230
7451
  // src/features/project-todos/project-todos.database.ts
7231
7452
  init_database();
@@ -8218,7 +8439,7 @@ ${result.output}`;
8218
8439
  try {
8219
8440
  const todo = await getTodoByThreadId(db, threadId);
8220
8441
  if (todo && todo.status === "Plan") {
8221
- const planFilePath = join13(threadPath, `${todo.id}-plan.md`);
8442
+ const planFilePath = join14(threadPath, `${todo.id}-plan.md`);
8222
8443
  try {
8223
8444
  const planContent = await readFile8(planFilePath, "utf-8");
8224
8445
  await updateTodo(db, todo.id, { description: planContent.trim() });
@@ -8430,13 +8651,15 @@ async function updateMessage(db, messageId, content, events, imageUrl) {
8430
8651
  if (hasMessageUsage) {
8431
8652
  if (event.type === "message" && event.message?.usage) {
8432
8653
  const usage = event.message.usage;
8433
- inputTokens += usage.input ?? 0;
8434
- outputTokens += usage.output ?? 0;
8654
+ const tokens = calculateCostEquivalentTokens(usage);
8655
+ inputTokens += tokens.inputTokens;
8656
+ outputTokens += tokens.outputTokens;
8435
8657
  }
8436
8658
  } else {
8437
8659
  if (event.type === "toolcall_delta" && event.partial?.usage) {
8438
- inputTokens += event.partial.usage.input ?? 0;
8439
- outputTokens += event.partial.usage.output ?? 0;
8660
+ const tokens = calculateCostEquivalentTokens(event.partial.usage);
8661
+ inputTokens += tokens.inputTokens;
8662
+ outputTokens += tokens.outputTokens;
8440
8663
  }
8441
8664
  }
8442
8665
  }
@@ -8467,6 +8690,34 @@ async function updateMessage(db, messageId, content, events, imageUrl) {
8467
8690
  throw error;
8468
8691
  }
8469
8692
  }
8693
+ function calculateCostEquivalentTokens(usage) {
8694
+ const input = usage.input ?? 0;
8695
+ const output = usage.output ?? 0;
8696
+ const cacheRead = usage.cacheRead ?? 0;
8697
+ const cacheWrite = usage.cacheWrite ?? 0;
8698
+ const cachedInput = cacheRead + cacheWrite;
8699
+ const uncachedInput = input >= cachedInput ? input - cachedInput : input;
8700
+ const inputCost = usage.cost?.input ?? 0;
8701
+ const cacheCost = (usage.cost?.cacheRead ?? 0) + (usage.cost?.cacheWrite ?? 0);
8702
+ if (uncachedInput > 0 && inputCost > 0 && cacheCost > 0) {
8703
+ const inputCostPerToken = inputCost / uncachedInput;
8704
+ const costEquivalentCachedInput = cacheCost / inputCostPerToken;
8705
+ return {
8706
+ inputTokens: Math.round(uncachedInput + costEquivalentCachedInput),
8707
+ outputTokens: output
8708
+ };
8709
+ }
8710
+ if (input >= cachedInput) {
8711
+ return {
8712
+ inputTokens: uncachedInput + cachedInput,
8713
+ outputTokens: output
8714
+ };
8715
+ }
8716
+ return {
8717
+ inputTokens: input,
8718
+ outputTokens: output
8719
+ };
8720
+ }
8470
8721
  async function getConversationHistory(db, threadId) {
8471
8722
  try {
8472
8723
  const result = await db.execute(
@@ -9760,6 +10011,7 @@ var MetadataManager = class {
9760
10011
  await setState(this.db, "selectedThreadId", state.selectedThreadId);
9761
10012
  await setState(this.db, "enabledModels", state.enabledModels);
9762
10013
  await setState(this.db, "enabledImageModels", state.enabledImageModels);
10014
+ await setState(this.db, "providerEnvValues", state.providerEnvValues);
9763
10015
  await setState(this.db, "onboardingCompleted", state.onboardingCompleted);
9764
10016
  const encryptedKeys = {};
9765
10017
  for (const [provider, key] of Object.entries(state.providerKeys)) {
@@ -9792,6 +10044,7 @@ var MetadataManager = class {
9792
10044
  const onboardingCompleted = allState.onboardingCompleted || false;
9793
10045
  const enabledModels = allState.enabledModels || {};
9794
10046
  const enabledImageModels = allState.enabledImageModels || {};
10047
+ const providerEnvValues = allState.providerEnvValues || {};
9795
10048
  const encryptedKeys = allState.providerKeys || {};
9796
10049
  const providerKeys = {};
9797
10050
  let needsMigration = false;
@@ -9824,6 +10077,7 @@ var MetadataManager = class {
9824
10077
  selectedThreadId,
9825
10078
  onboardingCompleted,
9826
10079
  providerKeys,
10080
+ providerEnvValues,
9827
10081
  enabledModels,
9828
10082
  enabledImageModels
9829
10083
  };
@@ -9902,6 +10156,36 @@ var MetadataManager = class {
9902
10156
  throw new Error(`Failed to get provider keys: ${errorMessage}`);
9903
10157
  }
9904
10158
  }
10159
+ /**
10160
+ * Save provider environment values (non-secret values such as Azure resource name)
10161
+ * @param values - Environment values to merge into metadata state
10162
+ */
10163
+ async saveProviderEnvValues(values) {
10164
+ try {
10165
+ const state = await this.loadState();
10166
+ state.providerEnvValues = {
10167
+ ...state.providerEnvValues,
10168
+ ...values
10169
+ };
10170
+ await this.saveState(state);
10171
+ } catch (error) {
10172
+ throw new Error(`Failed to save provider environment values: ${String(error)}`);
10173
+ }
10174
+ }
10175
+ /**
10176
+ * Get provider environment values stored in metadata state
10177
+ * @returns Record of environment variable names to values
10178
+ */
10179
+ async getProviderEnvValues() {
10180
+ try {
10181
+ const state = await this.loadState();
10182
+ return state.providerEnvValues;
10183
+ } catch (error) {
10184
+ const errorMessage = error instanceof Error ? error.message : String(error);
10185
+ console.error("[MetadataManager] Failed to get provider environment values:", errorMessage);
10186
+ throw new Error(`Failed to get provider environment values: ${errorMessage}`);
10187
+ }
10188
+ }
9905
10189
  /**
9906
10190
  * Set the selected thread ID
9907
10191
  * @param threadId - Thread ID to select (or null to deselect)
@@ -10086,7 +10370,7 @@ async function handleGetAvailableModels(c, modelManager) {
10086
10370
  const bEnabled = enabledModelIds.has(b.id) ? 0 : 1;
10087
10371
  return aEnabled - bEnabled;
10088
10372
  });
10089
- return c.json({ provider, models });
10373
+ return c.json({ provider, models, enabledModelIds: [...enabledModelIds] });
10090
10374
  } catch (error) {
10091
10375
  const message = error instanceof Error ? error.message : "Unknown error";
10092
10376
  return c.json({ error: message }, 500);
@@ -10542,6 +10826,9 @@ async function handleDeleteProject(c, projectManager) {
10542
10826
  // src/features/projects/projects.open-with.ts
10543
10827
  init_utils();
10544
10828
  import open from "open";
10829
+ function formatOpenError(program, _rawError) {
10830
+ return `${program} could not be started. Maybe it is not installed.`;
10831
+ }
10545
10832
  function validateProgram(program) {
10546
10833
  if (!program) {
10547
10834
  return "Program is required";
@@ -10593,7 +10880,7 @@ var OpenWithHandler = class {
10593
10880
  } catch (error) {
10594
10881
  const message = error instanceof Error ? error.message : String(error);
10595
10882
  console.error("[openWith] open() error:", message);
10596
- throw new Error(`Failed to open ${program}: ${message}`);
10883
+ throw new Error(formatOpenError(program, error));
10597
10884
  }
10598
10885
  }
10599
10886
  /**
@@ -10720,7 +11007,7 @@ async function handleOpenProject(c, projectManager) {
10720
11007
  });
10721
11008
  } catch (error) {
10722
11009
  const errorMessage = error instanceof Error ? error.message : String(error);
10723
- return errorResponse(c, ErrorCodes.OPEN_PROGRAM_ERROR, errorMessage, 500);
11010
+ return errorResponse(c, ErrorCodes.OPEN_PROGRAM_ERROR, errorMessage, 422);
10724
11011
  }
10725
11012
  }
10726
11013
 
@@ -10958,10 +11245,10 @@ async function handleCheckRunning(c, projectManager) {
10958
11245
 
10959
11246
  // src/core/run-command-detector.ts
10960
11247
  import { readFile as readFile9 } from "fs/promises";
10961
- import { join as join14 } from "path";
11248
+ import { join as join15 } from "path";
10962
11249
  async function detectPackageManager(projectPath) {
10963
11250
  try {
10964
- const packageJsonPath = join14(projectPath, "package.json");
11251
+ const packageJsonPath = join15(projectPath, "package.json");
10965
11252
  const content = await readFile9(packageJsonPath, "utf-8");
10966
11253
  const packageJson = JSON.parse(content);
10967
11254
  if (packageJson.packageManager) {
@@ -10977,7 +11264,7 @@ async function detectPackageManager(projectPath) {
10977
11264
  }
10978
11265
  async function detectRunScripts(projectPath) {
10979
11266
  try {
10980
- const packageJsonPath = join14(projectPath, "package.json");
11267
+ const packageJsonPath = join15(projectPath, "package.json");
10981
11268
  const content = await readFile9(packageJsonPath, "utf-8");
10982
11269
  const packageJson = JSON.parse(content);
10983
11270
  const scripts = packageJson.scripts ?? {};
@@ -11024,7 +11311,7 @@ async function suggestRunCommand(projectPath) {
11024
11311
 
11025
11312
  // src/features/projects/projects-package-scripts.route.ts
11026
11313
  import { readFile as readFile10 } from "fs/promises";
11027
- import { join as join15 } from "path";
11314
+ import { join as join16 } from "path";
11028
11315
  import { glob } from "glob";
11029
11316
  function formatScriptName(scriptName) {
11030
11317
  return scriptName.replace(/[-:_]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
@@ -11052,7 +11339,7 @@ async function findPackageScripts(projectPath) {
11052
11339
  const packageManager = await detectPackageManager(projectPath);
11053
11340
  for (const filePath of packageJsonFiles) {
11054
11341
  try {
11055
- const fullPath = join15(projectPath, filePath);
11342
+ const fullPath = join16(projectPath, filePath);
11056
11343
  const content = await readFile10(fullPath, "utf-8");
11057
11344
  const packageJson = JSON.parse(content);
11058
11345
  if (packageJson.scripts) {
@@ -11101,7 +11388,7 @@ async function handleGetPackageScripts(c, projectManager) {
11101
11388
 
11102
11389
  // src/features/projects/projects-ai-files.route.ts
11103
11390
  import { readdir as readdir6, readFile as readFile11, writeFile as writeFile2, mkdir, access as access2, rm } from "fs/promises";
11104
- import { join as join16, extname as extname3, basename } from "path";
11391
+ import { join as join17, extname as extname3, basename } from "path";
11105
11392
  import { existsSync as existsSync11 } from "fs";
11106
11393
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
11107
11394
  ".git",
@@ -11120,8 +11407,8 @@ async function buildFullTree(dirPath, relativeDirPath) {
11120
11407
  try {
11121
11408
  const entries = await readdir6(dirPath, { withFileTypes: true });
11122
11409
  for (const entry of entries) {
11123
- const entryRelPath = join16(relativeDirPath, entry.name);
11124
- const entryAbsPath = join16(dirPath, entry.name);
11410
+ const entryRelPath = join17(relativeDirPath, entry.name);
11411
+ const entryAbsPath = join17(dirPath, entry.name);
11125
11412
  if (entry.isDirectory()) {
11126
11413
  const children = await buildFullTree(entryAbsPath, entryRelPath);
11127
11414
  nodes.push({
@@ -11149,8 +11436,8 @@ async function buildMarkdownFilteredTree(dirPath, relativeDirPath) {
11149
11436
  try {
11150
11437
  const entries = await readdir6(dirPath, { withFileTypes: true });
11151
11438
  for (const entry of entries) {
11152
- const entryRelPath = join16(relativeDirPath, entry.name);
11153
- const entryAbsPath = join16(dirPath, entry.name);
11439
+ const entryRelPath = join17(relativeDirPath, entry.name);
11440
+ const entryAbsPath = join17(dirPath, entry.name);
11154
11441
  if (entry.isDirectory()) {
11155
11442
  if (IGNORED_DIRS.has(entry.name)) continue;
11156
11443
  const children = await buildMarkdownFilteredTree(entryAbsPath, entryRelPath);
@@ -11185,8 +11472,8 @@ async function buildAIFileTree(projectPath) {
11185
11472
  { key: "agents", label: "Agents" }
11186
11473
  ];
11187
11474
  for (const { key, label } of agentsFolders) {
11188
- const relPath = join16(".agents", key);
11189
- const absPath = join16(projectPath, relPath);
11475
+ const relPath = join17(".agents", key);
11476
+ const absPath = join17(projectPath, relPath);
11190
11477
  const exists = existsSync11(absPath);
11191
11478
  if (exists) {
11192
11479
  const children = await buildFullTree(absPath, relPath);
@@ -11221,7 +11508,7 @@ async function buildAIFileTree(projectPath) {
11221
11508
  if (entry.name === ".agents" || entry.name === "AGENTS.md" || IGNORED_DIRS.has(entry.name))
11222
11509
  continue;
11223
11510
  const entryRelPath = entry.name;
11224
- const entryAbsPath = join16(projectPath, entry.name);
11511
+ const entryAbsPath = join17(projectPath, entry.name);
11225
11512
  if (entry.isFile() && MARKDOWN_EXTS.has(extname3(entry.name))) {
11226
11513
  nodes.push({
11227
11514
  id: entryRelPath,
@@ -11248,7 +11535,7 @@ async function buildAIFileTree(projectPath) {
11248
11535
  }
11249
11536
  function validateFilePath(projectPath, filePath) {
11250
11537
  if (!filePath) return null;
11251
- const abs = join16(projectPath, filePath);
11538
+ const abs = join17(projectPath, filePath);
11252
11539
  if (!abs.startsWith(projectPath + "/") && abs !== projectPath) return null;
11253
11540
  return abs;
11254
11541
  }
@@ -11374,7 +11661,7 @@ async function handleSaveAIFile(c, projectManager) {
11374
11661
  if (!absPath) {
11375
11662
  return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
11376
11663
  }
11377
- const parentDir = join16(absPath, "..");
11664
+ const parentDir = join17(absPath, "..");
11378
11665
  await mkdir(parentDir, { recursive: true });
11379
11666
  await writeFile2(absPath, content, "utf-8");
11380
11667
  return c.json({ success: true, path: filePath });
@@ -11474,10 +11761,10 @@ async function handleCreateSkill(c, projectManager) {
11474
11761
  if (!project) {
11475
11762
  return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
11476
11763
  }
11477
- const skillRelPath = join16(".agents", "skills", name);
11478
- const skillAbsPath = join16(project.path, skillRelPath);
11479
- const skillFileRelPath = join16(skillRelPath, "SKILL.md");
11480
- const skillFileAbsPath = join16(skillAbsPath, "SKILL.md");
11764
+ const skillRelPath = join17(".agents", "skills", name);
11765
+ const skillAbsPath = join17(project.path, skillRelPath);
11766
+ const skillFileRelPath = join17(skillRelPath, "SKILL.md");
11767
+ const skillFileAbsPath = join17(skillAbsPath, "SKILL.md");
11481
11768
  if (existsSync11(skillAbsPath)) {
11482
11769
  return c.json(
11483
11770
  { error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
@@ -11579,7 +11866,7 @@ function createProjectRoutes(projectManager, threadManager) {
11579
11866
 
11580
11867
  // src/features/projects/projects.manager.ts
11581
11868
  init_utils();
11582
- import { join as join19 } from "path";
11869
+ import { join as join20 } from "path";
11583
11870
  import { realpathSync as realpathSync2 } from "fs";
11584
11871
  import { rm as rm3 } from "fs/promises";
11585
11872
 
@@ -11800,13 +12087,13 @@ var ProcessManager = class extends EventEmitter {
11800
12087
  // src/features/projects/projects.creator.ts
11801
12088
  init_utils();
11802
12089
  import { randomUUID as randomUUID7 } from "crypto";
11803
- import { basename as basename2, join as join18, relative as relative5 } from "path";
12090
+ import { basename as basename2, join as join19, relative as relative5 } from "path";
11804
12091
  import { access as access3, stat as stat3, readdir as readdir8 } from "fs/promises";
11805
12092
 
11806
12093
  // src/features/scaffold/scaffold.runner.ts
11807
12094
  init_utils();
11808
12095
  import { existsSync as existsSync12 } from "fs";
11809
- import { join as join17 } from "path";
12096
+ import { join as join18 } from "path";
11810
12097
  import { mkdir as mkdir2, readdir as readdir7, stat as stat2, rename, rm as rm2, writeFile as writeFile3 } from "fs/promises";
11811
12098
  import { createInterface as createInterface2 } from "readline";
11812
12099
 
@@ -12528,7 +12815,7 @@ function loadCatalog() {
12528
12815
  }
12529
12816
  async function writeAgentsMd(projectPath, agentsMd) {
12530
12817
  if (!agentsMd) return;
12531
- const agentsPath = join17(projectPath, "AGENTS.md");
12818
+ const agentsPath = join18(projectPath, "AGENTS.md");
12532
12819
  if (existsSync12(agentsPath)) return;
12533
12820
  await writeFile3(agentsPath, agentsMd, "utf-8");
12534
12821
  }
@@ -12686,7 +12973,7 @@ async function* runCommandStreaming(cwd, command) {
12686
12973
  }
12687
12974
  }
12688
12975
  async function* createScaffoldedProjectStreaming(options) {
12689
- const projectPath = join17(options.parentDir, options.threadId);
12976
+ const projectPath = join18(options.parentDir, options.threadId);
12690
12977
  if (!existsSync12(options.parentDir)) {
12691
12978
  yield {
12692
12979
  type: "result",
@@ -12776,7 +13063,7 @@ async function* createScaffoldedProjectStreaming(options) {
12776
13063
  }
12777
13064
  try {
12778
13065
  const projectName2 = getProjectName(options.projectName);
12779
- const projectSubDir = join17(projectPath, projectName2);
13066
+ const projectSubDir = join18(projectPath, projectName2);
12780
13067
  try {
12781
13068
  const subDirStat = await stat2(projectSubDir);
12782
13069
  if (subDirStat.isDirectory()) {
@@ -12786,8 +13073,8 @@ async function* createScaffoldedProjectStreaming(options) {
12786
13073
  };
12787
13074
  const items = await readdir7(projectSubDir);
12788
13075
  for (const item of items) {
12789
- const oldPath = join17(projectSubDir, item);
12790
- const newPath = join17(projectPath, item);
13076
+ const oldPath = join18(projectSubDir, item);
13077
+ const newPath = join18(projectPath, item);
12791
13078
  await rename(oldPath, newPath);
12792
13079
  }
12793
13080
  await rm2(projectSubDir, { recursive: true, force: true });
@@ -13255,7 +13542,7 @@ var ProjectCreator = class {
13255
13542
  return name;
13256
13543
  }
13257
13544
  generateThreadPath(_projectId, threadId) {
13258
- return join18(this.rootFolder, threadId);
13545
+ return join19(this.rootFolder, threadId);
13259
13546
  }
13260
13547
  async *createProjectFromFolder() {
13261
13548
  await loadUtils();
@@ -13405,19 +13692,19 @@ var ProjectCreator = class {
13405
13692
  }
13406
13693
  async findAllPackageJsonFiles(rootPath, currentPath, setupScripts) {
13407
13694
  try {
13408
- await access3(join18(currentPath, "package.json"));
13695
+ await access3(join19(currentPath, "package.json"));
13409
13696
  const relativePath = relative5(rootPath, currentPath);
13410
13697
  let installCommand = "";
13411
13698
  try {
13412
- await access3(join18(currentPath, "yarn.lock"));
13699
+ await access3(join19(currentPath, "yarn.lock"));
13413
13700
  installCommand = "yarn install";
13414
13701
  } catch {
13415
13702
  try {
13416
- await access3(join18(currentPath, "bun.lock"));
13703
+ await access3(join19(currentPath, "bun.lock"));
13417
13704
  installCommand = "bun install";
13418
13705
  } catch {
13419
13706
  try {
13420
- await access3(join18(currentPath, "pnpm-lock.yaml"));
13707
+ await access3(join19(currentPath, "pnpm-lock.yaml"));
13421
13708
  installCommand = "pnpm install";
13422
13709
  } catch {
13423
13710
  installCommand = "npm install";
@@ -13434,7 +13721,7 @@ var ProjectCreator = class {
13434
13721
  try {
13435
13722
  const entries = await readdir8(currentPath);
13436
13723
  for (const entry of entries) {
13437
- const entryPath = join18(currentPath, entry);
13724
+ const entryPath = join19(currentPath, entry);
13438
13725
  try {
13439
13726
  const stats = await stat3(entryPath);
13440
13727
  if (stats.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
@@ -13774,7 +14061,7 @@ var ProjectManagerImpl = class {
13774
14061
  const session = this.terminalSessionManager.getOrCreateSession(threadId, thread.path);
13775
14062
  let workingDir;
13776
14063
  if (cwd) {
13777
- workingDir = cwd.startsWith("/") ? cwd : join19(thread.path, cwd);
14064
+ workingDir = cwd.startsWith("/") ? cwd : join20(thread.path, cwd);
13778
14065
  this.terminalSessionManager.updateWorkingDirectory(threadId, workingDir);
13779
14066
  } else {
13780
14067
  workingDir = session.currentWorkingDirectory;
@@ -13977,80 +14264,59 @@ ___CWD___%s
13977
14264
  // src/features/providers/providers.routes.ts
13978
14265
  import { Hono as Hono7 } from "hono";
13979
14266
 
13980
- // src/core/env-manager.ts
13981
- import { promises as fs } from "fs";
13982
- import { join as join20 } from "path";
13983
- async function updateEnvFile(keyNames) {
13984
- const envPath = join20(process.cwd(), ".env");
13985
- let content = "";
13986
- try {
13987
- content = await fs.readFile(envPath, "utf-8");
13988
- } catch {
13989
- }
13990
- const lines = content.split("\n");
13991
- const envMap = {};
13992
- for (const line of lines) {
13993
- const trimmed = line.trim();
13994
- if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
13995
- const index = trimmed.indexOf("=");
13996
- const key = trimmed.substring(0, index).trim();
13997
- const value = trimmed.substring(index + 1).trim();
13998
- envMap[key] = value;
13999
- }
14000
- }
14001
- for (const [key, value] of Object.entries(keyNames)) {
14002
- if (key) {
14003
- envMap[key] = value;
14004
- }
14005
- }
14006
- const newLines = [];
14007
- for (const [key, value] of Object.entries(envMap)) {
14008
- newLines.push(`${key}=${value}`);
14267
+ // src/features/providers/providers-get.route.ts
14268
+ function getProviderApiKeyEnvName2(providerEnv) {
14269
+ if (providerEnv.length === 0) {
14270
+ return "";
14009
14271
  }
14010
- await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
14272
+ const explicitApiKeyEnv = providerEnv.find((envName) => /api_key$/i.test(envName));
14273
+ return explicitApiKeyEnv ?? providerEnv[providerEnv.length - 1];
14011
14274
  }
14012
- async function readEnvFile() {
14013
- const envPath = join20(process.cwd(), ".env");
14014
- const envMap = {};
14015
- try {
14016
- const content = await fs.readFile(envPath, "utf-8");
14017
- const lines = content.split("\n");
14018
- for (const line of lines) {
14019
- const trimmed = line.trim();
14020
- if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
14021
- const index = trimmed.indexOf("=");
14022
- const key = trimmed.substring(0, index).trim();
14023
- const value = trimmed.substring(index + 1).trim();
14024
- envMap[key] = value;
14025
- }
14026
- }
14027
- } catch {
14275
+ function getProviderResourceName2(providerEnv) {
14276
+ const explicitResourceEnv = providerEnv.find((envName) => /resource_name$/i.test(envName));
14277
+ return explicitResourceEnv ?? providerEnv[0] ?? "";
14278
+ }
14279
+ function resolveAzureApiUrl2(providerEnv, envValues) {
14280
+ const resourceEnvName = getProviderResourceName2(providerEnv);
14281
+ const resourceName = envValues[resourceEnvName] ?? "";
14282
+ if (!resourceName) {
14283
+ return "";
14028
14284
  }
14029
- return envMap;
14285
+ return `https://${resourceName}.openai.azure.com/openai/v1`;
14030
14286
  }
14031
-
14032
- // src/features/providers/providers-get.route.ts
14033
14287
  async function getProviders(c, metadataManager) {
14034
14288
  const keys = await metadataManager.getProviderKeys();
14289
+ const persistedEnvValues = await metadataManager.getProviderEnvValues();
14035
14290
  const envFileKeys = await readEnvFile();
14036
14291
  const catalog = await getModelsCatalog();
14037
14292
  const providersWithKeys = [];
14038
14293
  if (catalog) {
14039
14294
  for (const [catalogId, catalogProvider] of Object.entries(catalog)) {
14040
- const keyName = catalogProvider.env?.[0] || "";
14295
+ const envNames = catalogProvider.env ?? [];
14296
+ const keyName = getProviderApiKeyEnvName2(envNames);
14297
+ const envValues = {};
14298
+ for (const envName of envNames) {
14299
+ envValues[envName] = envFileKeys[envName] ?? process.env[envName] ?? persistedEnvValues[envName] ?? "";
14300
+ }
14041
14301
  const envFileKey = keyName ? envFileKeys[keyName] : "";
14042
14302
  const processEnvKey = keyName ? process.env[keyName] : "";
14043
14303
  const storedKey = keys[catalogProvider.name] || keys[catalogId];
14044
14304
  const hasEnvKey = Boolean(envFileKey || processEnvKey);
14045
14305
  const resolvedKey = storedKey ?? envFileKey ?? processEnvKey ?? "";
14046
14306
  const isEnv = !storedKey && hasEnvKey && resolvedKey !== "";
14307
+ const api = catalogId === "azure" ? resolveAzureApiUrl2(envNames, envValues) : catalogProvider.api ?? "";
14308
+ if (keyName && !envValues[keyName]) {
14309
+ envValues[keyName] = resolvedKey;
14310
+ }
14047
14311
  providersWithKeys.push({
14048
14312
  name: catalogProvider.name ?? catalogId,
14049
14313
  slug: catalogId,
14050
14314
  url: catalogProvider.doc ?? "",
14051
14315
  keyName,
14052
14316
  authType: "APIKey",
14053
- api: catalogProvider.api ?? "",
14317
+ api,
14318
+ env: envNames,
14319
+ envValues,
14054
14320
  apiKey: resolvedKey,
14055
14321
  isEnv
14056
14322
  });
@@ -14069,6 +14335,7 @@ async function postProviderKeys(c, metadataManager) {
14069
14335
  await metadataManager.saveProviderKey(providerName, apiKey);
14070
14336
  const keyName = await resolveProviderKeyName(providerName);
14071
14337
  if (keyName) {
14338
+ updateRuntimeEnv({ [keyName]: apiKey });
14072
14339
  await updateEnvFile({ [keyName]: apiKey });
14073
14340
  }
14074
14341
  return c.json({ success: true });
@@ -14093,6 +14360,7 @@ async function postProviderBulkKeys(c, metadataManager) {
14093
14360
  }
14094
14361
  }
14095
14362
  if (Object.keys(envUpdates).length > 0) {
14363
+ updateRuntimeEnv(envUpdates);
14096
14364
  await updateEnvFile(envUpdates);
14097
14365
  }
14098
14366
  return c.json({ success: true });
@@ -14101,6 +14369,28 @@ async function postProviderBulkKeys(c, metadataManager) {
14101
14369
  }
14102
14370
  }
14103
14371
 
14372
+ // src/features/providers/providers-post-env.route.ts
14373
+ async function postProviderEnv(c, metadataManager) {
14374
+ try {
14375
+ const { values } = await c.req.json();
14376
+ if (!values || typeof values !== "object") {
14377
+ return c.json({ error: "values object is required" }, 400);
14378
+ }
14379
+ const envValues = {};
14380
+ for (const [key, value] of Object.entries(values)) {
14381
+ if (typeof value === "string") {
14382
+ envValues[key] = value;
14383
+ }
14384
+ }
14385
+ await metadataManager.saveProviderEnvValues(envValues);
14386
+ updateRuntimeEnv(envValues);
14387
+ await updateEnvFile(envValues);
14388
+ return c.json({ success: true });
14389
+ } catch (error) {
14390
+ return c.json({ error: `Failed to save provider environment: ${String(error)}` }, 500);
14391
+ }
14392
+ }
14393
+
14104
14394
  // src/features/models/model-info-openrouter.ts
14105
14395
  async function getOpenRouterCredits(apiKey) {
14106
14396
  try {
@@ -14196,13 +14486,24 @@ async function getProviderCredits(c, metadataManager) {
14196
14486
  // src/features/providers/providers-get-logo.route.ts
14197
14487
  var LOGO_BASE_URL = "https://models.tarsk.io/svg";
14198
14488
  var SLUG_PATTERN = /^[a-z0-9._-]+$/i;
14489
+ var logoCache = /* @__PURE__ */ new Map();
14490
+ var LOGO_CACHE_TTL_MS = 15 * 60 * 1e3;
14199
14491
  async function getProviderLogo(c) {
14200
14492
  const slug = c.req.param("slug");
14201
14493
  if (!slug || !SLUG_PATTERN.test(slug)) {
14202
14494
  return c.body(null, 404);
14203
14495
  }
14496
+ const cached = logoCache.get(slug);
14497
+ if (cached && Date.now() < cached.expires) {
14498
+ return c.body(cached.svg, 200, {
14499
+ "Content-Type": "image/svg+xml",
14500
+ "Cache-Control": "public, max-age=86400"
14501
+ });
14502
+ }
14204
14503
  try {
14205
- const response = await fetch(`${LOGO_BASE_URL}/${slug}.svg`, { signal: AbortSignal.timeout(5e3) });
14504
+ const response = await fetch(`${LOGO_BASE_URL}/${slug}.svg`, {
14505
+ signal: AbortSignal.timeout(5e3)
14506
+ });
14206
14507
  if (!response.ok) {
14207
14508
  return c.body(null, 404);
14208
14509
  }
@@ -14210,6 +14511,7 @@ async function getProviderLogo(c) {
14210
14511
  if (!svg.includes("<svg")) {
14211
14512
  return c.body(null, 404);
14212
14513
  }
14514
+ logoCache.set(slug, { svg, expires: Date.now() + LOGO_CACHE_TTL_MS });
14213
14515
  return c.body(svg, 200, {
14214
14516
  "Content-Type": "image/svg+xml",
14215
14517
  "Cache-Control": "public, max-age=86400"
@@ -14262,6 +14564,7 @@ function createProviderRoutes(metadataManager) {
14262
14564
  router.get("/", (c) => getProviders(c, metadataManager));
14263
14565
  router.post("/keys", (c) => postProviderKeys(c, metadataManager));
14264
14566
  router.post("/bulk-keys", (c) => postProviderBulkKeys(c, metadataManager));
14567
+ router.post("/env", (c) => postProviderEnv(c, metadataManager));
14265
14568
  router.get("/:name/credits", (c) => getProviderCredits(c, metadataManager));
14266
14569
  router.get("/:slug/logo", (c) => getProviderLogo(c));
14267
14570
  router.post("/open-external", (c) => openExternalUrl(c));
@@ -14512,7 +14815,7 @@ function createScaffoldRoutes(projectManager) {
14512
14815
 
14513
14816
  // src/features/slash-commands/slash-commands.manager.ts
14514
14817
  import { readdir as readdir9, readFile as readFile12, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
14515
- import { join as join21, basename as basename3, extname as extname4 } from "path";
14818
+ import { join as join21, basename as basename3, extname as extname4, relative as relative6 } from "path";
14516
14819
  import { existsSync as existsSync13 } from "fs";
14517
14820
  import { homedir as homedir7 } from "os";
14518
14821
  function slugify(filename) {
@@ -14593,14 +14896,14 @@ var SlashCommandManager = class _SlashCommandManager {
14593
14896
  const commands = /* @__PURE__ */ new Map();
14594
14897
  const globalDir = getGlobalCommandsDir();
14595
14898
  if (existsSync13(globalDir)) {
14596
- const globalCommands = await this.loadCommandsFromDir(globalDir, "global");
14899
+ const globalCommands = await this.loadCommandsFromDir(globalDir, threadPath, "global");
14597
14900
  for (const cmd of globalCommands) {
14598
14901
  commands.set(cmd.name, cmd);
14599
14902
  }
14600
14903
  }
14601
14904
  const projectDir = getProjectCommandsDir(threadPath);
14602
14905
  if (existsSync13(projectDir)) {
14603
- const projectCommands = await this.loadCommandsFromDir(projectDir, "project");
14906
+ const projectCommands = await this.loadCommandsFromDir(projectDir, threadPath, "project");
14604
14907
  for (const cmd of projectCommands) {
14605
14908
  if (!_SlashCommandManager.BUILT_IN_COMMANDS.has(cmd.name)) {
14606
14909
  commands.set(cmd.name, cmd);
@@ -14617,7 +14920,7 @@ var SlashCommandManager = class _SlashCommandManager {
14617
14920
  /**
14618
14921
  * Load commands from a specific directory
14619
14922
  */
14620
- async loadCommandsFromDir(dir, scope) {
14923
+ async loadCommandsFromDir(dir, threadPath, scope) {
14621
14924
  const commands = [];
14622
14925
  try {
14623
14926
  const files = await readdir9(dir);
@@ -14634,7 +14937,7 @@ var SlashCommandManager = class _SlashCommandManager {
14634
14937
  content,
14635
14938
  metadata,
14636
14939
  scope,
14637
- filePath
14940
+ filePath: scope === "project" ? relative6(threadPath, filePath) : filePath
14638
14941
  });
14639
14942
  }
14640
14943
  } catch (error) {
@@ -15855,7 +16158,7 @@ async function handleDeleteThread(c, threadManager) {
15855
16158
  // src/core/project-inspector.ts
15856
16159
  import { readFile as readFile13, readdir as readdir10 } from "fs/promises";
15857
16160
  import { existsSync as existsSync14 } from "fs";
15858
- import { join as join23, basename as basename4, relative as relative6 } from "path";
16161
+ import { join as join23, basename as basename4, relative as relative7 } from "path";
15859
16162
  import { glob as glob2 } from "glob";
15860
16163
 
15861
16164
  // src/features/project-scripts/project-scripts.database.ts
@@ -16205,7 +16508,7 @@ function buildRunCommand(scriptName, workspace, packageManager, repoType, projec
16205
16508
  return `npm run ${scriptName} --workspace=${workspace.relativePath}`;
16206
16509
  }
16207
16510
  if (!isRoot) {
16208
- const relPath = relative6(projectPath, workspace.folder);
16511
+ const relPath = relative7(projectPath, workspace.folder);
16209
16512
  return `cd ${relPath} && ${runCmd}`;
16210
16513
  }
16211
16514
  return runCmd;
@@ -16828,7 +17131,7 @@ async function handleOpenThread(c, threadManager) {
16828
17131
  c,
16829
17132
  ErrorCodes.OPEN_PROGRAM_ERROR,
16830
17133
  `Unsupported program: ${program}`,
16831
- 500
17134
+ 422
16832
17135
  );
16833
17136
  }
16834
17137
  console.log(
@@ -16848,8 +17151,8 @@ async function handleOpenThread(c, threadManager) {
16848
17151
  return errorResponse(
16849
17152
  c,
16850
17153
  ErrorCodes.OPEN_PROGRAM_ERROR,
16851
- `Failed to open ${program}: ${message}`,
16852
- 500
17154
+ formatOpenError(program, openError),
17155
+ 422
16853
17156
  );
16854
17157
  }
16855
17158
  return successResponse(c, { success: true, message: `Thread opened in ${program}` });
@@ -16859,7 +17162,7 @@ async function handleOpenThread(c, threadManager) {
16859
17162
  c,
16860
17163
  ErrorCodes.OPEN_PROGRAM_ERROR,
16861
17164
  "Failed to open thread",
16862
- 500,
17165
+ 422,
16863
17166
  errorMessage
16864
17167
  );
16865
17168
  }
@@ -17964,7 +18267,7 @@ import { completeSimple } from "@mariozechner/pi-ai";
17964
18267
  async function resolveModelAndKey(provider, modelId, metadataManager) {
17965
18268
  const providerConfig = await resolveProviderConfig(provider);
17966
18269
  if (!providerConfig) return null;
17967
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
18270
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
17968
18271
  if (!apiKey) {
17969
18272
  const providerKeys = await metadataManager.getProviderKeys();
17970
18273
  apiKey = providerKeys[providerConfig.id];
@@ -19894,15 +20197,6 @@ async function gitSyncBranchHandler(c, metadataManager) {
19894
20197
  400
19895
20198
  );
19896
20199
  }
19897
- if (currentBranch === defaultBranch) {
19898
- console.log(`[sync-branch] Already on default branch: ${defaultBranch}`);
19899
- return c.json(
19900
- {
19901
- error: `Already on default branch (${defaultBranch})`
19902
- },
19903
- 400
19904
- );
19905
- }
19906
20200
  const { spawnProcess: spawnProcess2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
19907
20201
  return new Promise((resolve6) => {
19908
20202
  console.log(`[sync-branch] Checking branch status...`);
@@ -20038,13 +20332,12 @@ async function gitSyncBranchHandler(c, metadataManager) {
20038
20332
  }
20039
20333
  pullProc.on("close", (code) => {
20040
20334
  if (code === 0) {
20041
- console.log(
20042
- `[sync-branch] \u2713 Successfully synced ${currentBranch} with ${defaultBranch}`
20043
- );
20335
+ const message = currentBranch === defaultBranch ? `Successfully pulled latest changes from origin/${defaultBranch}` : `Successfully synced ${currentBranch} with ${defaultBranch}`;
20336
+ console.log(`[sync-branch] \u2713 ${message}`);
20044
20337
  resolve6(
20045
20338
  c.json({
20046
20339
  success: true,
20047
- message: `Successfully synced ${currentBranch} with ${defaultBranch}`,
20340
+ message,
20048
20341
  branch: currentBranch,
20049
20342
  defaultBranch
20050
20343
  })
@@ -21204,7 +21497,7 @@ async function generateImage(c, metadataManager, conversationManager, threadMana
21204
21497
  if (!providerConfig) {
21205
21498
  return c.json({ error: `Unknown provider: ${provider}` }, 400);
21206
21499
  }
21207
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
21500
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
21208
21501
  if (!apiKey) {
21209
21502
  const providerKeys = await metadataManager.getProviderKeys();
21210
21503
  apiKey = providerKeys[providerConfig.id];
@@ -21631,6 +21924,7 @@ async function startTarskServer(options) {
21631
21924
  const conversationManager = new ConversationManagerImpl(dataDir);
21632
21925
  const agentExecutor = new PiExecutorImpl(metadataManager);
21633
21926
  await metadataManager.initialize();
21927
+ updateRuntimeEnv(await metadataManager.getProviderEnvValues());
21634
21928
  await conversationManager.initialize();
21635
21929
  app.get("/health", (c) => {
21636
21930
  return c.json({