tarsk 0.4.27 → 0.4.28

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 (82) hide show
  1. package/dist/index.js +438 -148
  2. package/dist/public/assets/account-view-DcpSNB3N.js +1 -0
  3. package/dist/public/assets/alert-dialog-CQRWdGnn.js +1 -0
  4. package/dist/public/assets/api-CH5gAPs5.js +1 -0
  5. package/dist/public/assets/{browser-tab-DlzO5cMP.js → browser-tab-CiAz6nc1.js} +1 -1
  6. package/dist/public/assets/chat-input-container-C6U_WFFu.js +21 -0
  7. package/dist/public/assets/context-menu-A9p22S7d.js +1 -0
  8. package/dist/public/assets/conversation-history-view-CD_uVRNY.js +1 -0
  9. package/dist/public/assets/{dialogs-config-DZFG2IQM.js → dialogs-config-vkfELnGl.js} +3 -3
  10. package/dist/public/assets/diff-view-CjbcDB6m.js +3 -0
  11. package/dist/public/assets/explorer-tab-view-BfycKUUW.js +2 -0
  12. package/dist/public/assets/explorer-tree-DTFYhczJ.js +1 -0
  13. package/dist/public/assets/explorer-view-C0KcZ8NQ.js +1 -0
  14. package/dist/public/assets/history-view-Dn0hQIKU.js +1 -0
  15. package/dist/public/assets/index-Bdde81Uo.css +1 -0
  16. package/dist/public/assets/index-DKQVo4Vz.js +29 -0
  17. package/dist/public/assets/markdown-renderer-CNuyXHcy.js +10 -0
  18. package/dist/public/assets/mcp-server-card-DBEBe3Qh.js +1 -0
  19. package/dist/public/assets/onboarding-DslO5AS6.js +1 -0
  20. package/dist/public/assets/onboarding-dialog-CE-GhoDt.js +1 -0
  21. package/dist/public/assets/{page-toolbar-TvYsDwUY.js → page-toolbar-CloU_Kvo.js} +1 -1
  22. package/dist/public/assets/{project-settings-view-C7GW_R0m.js → project-settings-view-CxD9JZoB.js} +1 -1
  23. package/dist/public/assets/providers-list-view-CNxVBJlM.js +1 -0
  24. package/dist/public/assets/radio-group-Co0Lg6se.js +1 -0
  25. package/dist/public/assets/react-vendor-DyE9sO8V.js +22 -0
  26. package/dist/public/assets/{resizable-DzrIos_Y.js → resizable-dToeUeVD.js} +1 -1
  27. package/dist/public/assets/{run-stop-button-k1LeM0M-.js → run-stop-button-DZ4JFwdL.js} +2 -2
  28. package/dist/public/assets/settings-instructions-view-DWzAH2yN.js +1 -0
  29. package/dist/public/assets/{settings-mcp-servers-view-Becs_RxN.js → settings-mcp-servers-view-q6gGMMVX.js} +4 -4
  30. package/dist/public/assets/settings-models-view-BDDFQWWC.js +1 -0
  31. package/dist/public/assets/settings-rules-view-DsEwgDFr.js +8 -0
  32. package/dist/public/assets/{settings-skills-view-aPv03-bO.js → settings-skills-view-DV40_L1o.js} +2 -2
  33. package/dist/public/assets/{settings-slash-commands-view-DLMWXQXA.js → settings-slash-commands-view-BEk3BgOY.js} +1 -1
  34. package/dist/public/assets/settings-subagents-view-d3P4ylDX.js +2 -0
  35. package/dist/public/assets/settings-view-ClTMPafQ.js +2 -0
  36. package/dist/public/assets/{side-panel-container-oMcXib2A.js → side-panel-container-BAYoMt7B.js} +2 -2
  37. package/dist/public/assets/{skeleton-Bc0U0oGc.js → skeleton-DveUQR4w.js} +1 -1
  38. package/dist/public/assets/standard-list-item-BLlgm1qW.js +1 -0
  39. package/dist/public/assets/store-BcwWHqYr.js +4 -0
  40. package/dist/public/assets/{tab-context-CCvlrC0o.js → tab-context-DWD20a4z.js} +1 -1
  41. package/dist/public/assets/tabs-CgvpwOBj.js +1 -0
  42. package/dist/public/assets/{terminal-panel-GuO2XygM.js → terminal-panel-CkIe2d2K.js} +2 -2
  43. package/dist/public/assets/textarea-C2UIAa7y.js +1 -0
  44. package/dist/public/assets/todos-view-C-awG1dZ.js +1 -0
  45. package/dist/public/assets/{use-toast-DqmC-mB5.js → use-toast-yY7qQuQP.js} +1 -1
  46. package/dist/public/assets/{utils-Cy2s2TEb.js → utils-BKss67Vg.js} +1 -1
  47. package/dist/public/assets/{whisper-wasm-Clnf-l-9.js → whisper-wasm-B7iuRPFr.js} +1 -1
  48. package/dist/public/ide/antigravity.svg +1 -0
  49. package/dist/public/ide/devin.svg +1 -0
  50. package/dist/public/index.html +22 -21
  51. package/package.json +1 -1
  52. package/dist/public/assets/account-view-BFhjAYmz.js +0 -1
  53. package/dist/public/assets/alert-dialog--tUzAWFm.js +0 -1
  54. package/dist/public/assets/api-BBbysRIh.js +0 -1
  55. package/dist/public/assets/chat-input-container-B_qVOGzW.js +0 -21
  56. package/dist/public/assets/context-menu-D5slxFcc.js +0 -1
  57. package/dist/public/assets/conversation-history-view-B4zHwyrw.js +0 -1
  58. package/dist/public/assets/diff-view-fOVqrHQW.js +0 -3
  59. package/dist/public/assets/explorer-tab-view-MLrWJ1Y0.js +0 -2
  60. package/dist/public/assets/explorer-tree-8TyBYuC7.js +0 -1
  61. package/dist/public/assets/explorer-view-5U70mfAJ.js +0 -1
  62. package/dist/public/assets/history-view-BR8scGnM.js +0 -1
  63. package/dist/public/assets/index-0BuHfewk.css +0 -1
  64. package/dist/public/assets/index-BTbiyooe.js +0 -37
  65. package/dist/public/assets/mcp-server-card-DupwHcKT.js +0 -1
  66. package/dist/public/assets/onboarding-Cn5RqOCa.js +0 -1
  67. package/dist/public/assets/onboarding-dialog-BXsvjU1t.js +0 -1
  68. package/dist/public/assets/providers-list-view-AVt5I1WB.js +0 -1
  69. package/dist/public/assets/radio-group-Mifbn8sU.js +0 -1
  70. package/dist/public/assets/react-vendor-Bao-KYKs.js +0 -22
  71. package/dist/public/assets/settings-instructions-view-CgfWSPmK.js +0 -1
  72. package/dist/public/assets/settings-models-view-BqdD_7GR.js +0 -1
  73. package/dist/public/assets/settings-rules-view-D7j5OHUi.js +0 -8
  74. package/dist/public/assets/settings-subagents-view-DBYzNE4W.js +0 -2
  75. package/dist/public/assets/settings-view-CccwUKkf.js +0 -2
  76. package/dist/public/assets/standard-list-item-D2lbPXa1.js +0 -1
  77. package/dist/public/assets/store-CwvBC-YP.js +0 -2
  78. package/dist/public/assets/tabs-Mb_cjIfE.js +0 -1
  79. package/dist/public/assets/textarea-DpXOZLt8.js +0 -1
  80. package/dist/public/assets/todos-view-C4GMi1Np.js +0 -1
  81. /package/dist/public/assets/{dist-BI23Ei33.js → dist-Bjt_i1zi.js} +0 -0
  82. /package/dist/public/assets/{monaco-DDfTQM3i.js → monaco-SWcefg0q.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);
@@ -4140,16 +4148,56 @@ var ModelManager = class {
4140
4148
  };
4141
4149
 
4142
4150
  // src/features/providers/provider-resolver.ts
4151
+ function getProviderApiKeyEnvName(providerEnv) {
4152
+ if (providerEnv.length === 0) {
4153
+ return "";
4154
+ }
4155
+ const explicitApiKeyEnv = providerEnv.find((envName) => /api_key$/i.test(envName));
4156
+ return explicitApiKeyEnv ?? providerEnv[providerEnv.length - 1];
4157
+ }
4158
+ function getProviderApiUrlEnvName(providerName) {
4159
+ return `${providerName.toUpperCase().replace(/-/g, "_")}_API_URL`;
4160
+ }
4161
+ function getProviderResourceName(providerEnv) {
4162
+ const explicitResourceEnv = providerEnv.find((envName) => /resource_name$/i.test(envName));
4163
+ return explicitResourceEnv ?? providerEnv[0] ?? "";
4164
+ }
4165
+ function resolveAzureApiUrl(providerEnv) {
4166
+ const resourceEnvName = getProviderResourceName(providerEnv);
4167
+ const resourceName = process.env[resourceEnvName];
4168
+ if (!resourceName) {
4169
+ return "";
4170
+ }
4171
+ return `https://${resourceName}.openai.azure.com/openai/v1`;
4172
+ }
4173
+ function resolveTemplate(template, envValues) {
4174
+ let hasMissingValue = false;
4175
+ const resolved = template.replace(/\$\{([A-Z0-9_]+)\}/gi, (_match, envName) => {
4176
+ const value = envValues[envName] ?? process.env[envName] ?? "";
4177
+ if (!value) {
4178
+ hasMissingValue = true;
4179
+ }
4180
+ return value;
4181
+ });
4182
+ return hasMissingValue ? "" : resolved;
4183
+ }
4143
4184
  async function resolveProviderConfig(providerName) {
4144
4185
  const slug = providerName.toLowerCase();
4145
4186
  const catalogProvider = await getCatalogProvider(slug);
4146
4187
  if (!catalogProvider) return null;
4147
- return catalogProvider;
4188
+ const apiKeyEnv = getProviderApiKeyEnvName(catalogProvider.env ?? []);
4189
+ const apiUrlEnvName = getProviderApiUrlEnvName(slug);
4190
+ const api = slug === "azure" ? resolveAzureApiUrl(catalogProvider.env ?? []) : process.env[apiUrlEnvName] || (catalogProvider.api ? resolveTemplate(catalogProvider.api, process.env) : "");
4191
+ return {
4192
+ ...catalogProvider,
4193
+ api,
4194
+ apiKeyEnv
4195
+ };
4148
4196
  }
4149
4197
  async function resolveProviderKeyName(providerName) {
4150
4198
  const slug = providerName.toLowerCase();
4151
4199
  const catalogProvider = await getCatalogProvider(slug);
4152
- if (catalogProvider?.env?.[0]) return catalogProvider.env[0];
4200
+ if (catalogProvider?.env?.length) return getProviderApiKeyEnvName(catalogProvider.env);
4153
4201
  return "";
4154
4202
  }
4155
4203
 
@@ -4346,7 +4394,7 @@ function createGenerateImageTool(options) {
4346
4394
  details: void 0
4347
4395
  };
4348
4396
  }
4349
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
4397
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
4350
4398
  if (!apiKey) {
4351
4399
  const providerKeys = await metadataManager.getProviderKeys();
4352
4400
  apiKey = providerKeys[providerConfig.id];
@@ -4784,8 +4832,12 @@ function createToolSearchTool(options) {
4784
4832
  let matched = [];
4785
4833
  if (parsed.mode === "select") {
4786
4834
  for (const name of parsed.names) {
4787
- const tool = deferredTools.get(name);
4788
- if (tool) matched.push(tool);
4835
+ for (const [toolName, tool] of deferredTools.entries()) {
4836
+ if (toolName.toLowerCase() === name) {
4837
+ matched.push(tool);
4838
+ break;
4839
+ }
4840
+ }
4789
4841
  }
4790
4842
  } else {
4791
4843
  const candidates = [];
@@ -5039,6 +5091,7 @@ var EventQueue = class {
5039
5091
  finalContent = "";
5040
5092
  errorOccurred = false;
5041
5093
  toolCallCount = 0;
5094
+ lastErrorMessage;
5042
5095
  /**
5043
5096
  * Processes Pi Agent events and transforms them into AgentEvent format
5044
5097
  */
@@ -5107,6 +5160,9 @@ var EventQueue = class {
5107
5160
  } else if (event.type === "agent_end") {
5108
5161
  const messages = event.messages;
5109
5162
  const lastAssistant = [...messages].reverse().find((m) => "role" in m && m.role === "assistant");
5163
+ if (lastAssistant && "errorMessage" in lastAssistant && typeof lastAssistant.errorMessage === "string") {
5164
+ this.lastErrorMessage = lastAssistant.errorMessage;
5165
+ }
5110
5166
  if (lastAssistant) {
5111
5167
  const extracted = extractTextFromAssistantMessage(lastAssistant);
5112
5168
  if (extracted && extracted !== this.finalContent) {
@@ -5220,14 +5276,14 @@ async function executeSubagent(options) {
5220
5276
  if (!providerConfig) {
5221
5277
  throw new Error(`Unknown provider for subagent: ${providerName}`);
5222
5278
  }
5223
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
5279
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
5224
5280
  if (!apiKey) {
5225
5281
  const providerKeys = await metadataManager.getProviderKeys();
5226
5282
  apiKey = providerKeys[providerConfig.id];
5227
5283
  }
5228
5284
  if (!apiKey) {
5229
5285
  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.`
5286
+ `No API key found for subagent provider: ${providerName}. Set ${providerConfig.apiKeyEnv ?? "an API key"} in environment variables or configuration.`
5231
5287
  );
5232
5288
  }
5233
5289
  const resolvedModel = resolveModel(providerName, model, providerConfig);
@@ -6115,10 +6171,94 @@ async function loadAgentSystemPrompt(threadPath, tools, skills, planMode, agents
6115
6171
  var cachedStreamFn = (model, context, options) => {
6116
6172
  return streamSimple(model, context, { ...options, cacheRetention: "long" });
6117
6173
  };
6174
+ function summarizeAssistantResponse(response) {
6175
+ const lines = response.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
6176
+ const salient = lines.filter(
6177
+ (line) => /^(#+\s|[-*]\s|\d+\.\s|```|changed|created|updated|deleted|fixed|implemented|added|removed|renamed|moved|tested|next|summary|result)/i.test(
6178
+ line
6179
+ ) || line.length <= 180
6180
+ );
6181
+ const summary = (salient.length > 0 ? salient : lines).slice(0, 12).join("\n");
6182
+ return summary.length > 3e3 ? `${summary.slice(0, 3e3)}\u2026` : summary;
6183
+ }
6184
+ function getAssistantHistoryContent(exchange, isMostRecent) {
6185
+ if (isMostRecent) {
6186
+ return exchange.assistantResponse;
6187
+ }
6188
+ return exchange.assistantSummary ?? summarizeAssistantResponse(exchange.assistantResponse);
6189
+ }
6190
+ function buildAssistantHistoryText(content, isMostRecent) {
6191
+ if (isMostRecent) {
6192
+ return content;
6193
+ }
6194
+ return `[Earlier assistant response summarized to reduce context tokens]
6195
+ ${content}`;
6196
+ }
6197
+ function serializeLogValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
6198
+ if (value === null || value === void 0) {
6199
+ return value;
6200
+ }
6201
+ const valueType = typeof value;
6202
+ if (valueType === "string" || valueType === "number" || valueType === "boolean") {
6203
+ return value;
6204
+ }
6205
+ if (valueType === "bigint" || valueType === "symbol") {
6206
+ return "[Primitive]";
6207
+ }
6208
+ if (valueType === "function") {
6209
+ return "[Function]";
6210
+ }
6211
+ if (value instanceof Error) {
6212
+ const serializedError = {
6213
+ name: value.name,
6214
+ message: value.message
6215
+ };
6216
+ if (value.stack) {
6217
+ serializedError.stack = value.stack;
6218
+ }
6219
+ if ("cause" in value) {
6220
+ serializedError.cause = serializeLogValue(
6221
+ value.cause,
6222
+ depth + 1,
6223
+ seen
6224
+ );
6225
+ }
6226
+ const errorObject = value;
6227
+ for (const [key, nestedValue] of Object.entries(errorObject)) {
6228
+ if (key in serializedError) continue;
6229
+ serializedError[key] = serializeLogValue(nestedValue, depth + 1, seen);
6230
+ }
6231
+ return serializedError;
6232
+ }
6233
+ if (Array.isArray(value)) {
6234
+ if (depth >= 4) {
6235
+ return "[Truncated]";
6236
+ }
6237
+ return value.slice(0, 50).map((item) => serializeLogValue(item, depth + 1, seen));
6238
+ }
6239
+ if (valueType === "object") {
6240
+ if (seen.has(value)) {
6241
+ return "[Circular]";
6242
+ }
6243
+ if (depth >= 4) {
6244
+ return "[Truncated]";
6245
+ }
6246
+ seen.add(value);
6247
+ const serializedObject = {};
6248
+ const plainObject = value;
6249
+ for (const [key, nestedValue] of Object.entries(plainObject).slice(0, 50)) {
6250
+ serializedObject[key] = serializeLogValue(nestedValue, depth + 1, seen);
6251
+ }
6252
+ return serializedObject;
6253
+ }
6254
+ return "[Unsupported value]";
6255
+ }
6118
6256
  function buildHistoryMessages(history) {
6119
6257
  const messages = [];
6120
6258
  const now = Date.now();
6121
- for (const exchange of history) {
6259
+ history.forEach((exchange, index) => {
6260
+ const isMostRecent = index === history.length - 1;
6261
+ const assistantContent = getAssistantHistoryContent(exchange, isMostRecent);
6122
6262
  messages.push({
6123
6263
  role: "user",
6124
6264
  content: exchange.userMessage,
@@ -6126,7 +6266,9 @@ function buildHistoryMessages(history) {
6126
6266
  });
6127
6267
  messages.push({
6128
6268
  role: "assistant",
6129
- content: [{ type: "text", text: exchange.assistantResponse }],
6269
+ content: [
6270
+ { type: "text", text: buildAssistantHistoryText(assistantContent, isMostRecent) }
6271
+ ],
6130
6272
  api: "",
6131
6273
  provider: "",
6132
6274
  model: "",
@@ -6141,7 +6283,7 @@ function buildHistoryMessages(history) {
6141
6283
  stopReason: "stop",
6142
6284
  timestamp: now
6143
6285
  });
6144
- }
6286
+ });
6145
6287
  return messages;
6146
6288
  }
6147
6289
  var PiExecutorImpl = class {
@@ -6171,7 +6313,7 @@ var PiExecutorImpl = class {
6171
6313
  if (!providerConfig) {
6172
6314
  throw new Error(`Unknown provider: ${providerName}`);
6173
6315
  }
6174
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
6316
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
6175
6317
  let apiKeySource = "environment";
6176
6318
  if (!apiKey) {
6177
6319
  try {
@@ -6187,7 +6329,7 @@ var PiExecutorImpl = class {
6187
6329
  }
6188
6330
  if (!apiKey) {
6189
6331
  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.`
6332
+ `No API key found for provider: ${providerName}. Set ${providerConfig.apiKeyEnv ?? "an API key"} in environment variables or configuration.`
6191
6333
  );
6192
6334
  }
6193
6335
  if (apiKey.includes(":") && apiKey.length > 100) {
@@ -6302,15 +6444,27 @@ ${decoded}
6302
6444
 
6303
6445
  ${userPrompt}`;
6304
6446
  }
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) => ({
6447
+ const promptRequest = {
6448
+ provider: providerName,
6449
+ model,
6450
+ cwd,
6451
+ prompt: finalPrompt,
6452
+ promptLength: finalPrompt.length,
6453
+ promptPreview: finalPrompt.substring(0, 200) + (finalPrompt.length > 200 ? "..." : ""),
6454
+ imageCount: images.length,
6455
+ images: images.map((img) => ({
6456
+ mimeType: img.mimeType,
6457
+ dataLength: img.data?.length ?? 0
6458
+ })),
6459
+ attachments: (context.attachments ?? []).map((a) => ({
6308
6460
  name: a.name,
6309
6461
  mimeType: a.mimeType,
6310
6462
  contentLength: a.content?.length ?? 0
6311
- })),
6312
- "images \u2192",
6313
- images.map((img) => ({ mimeType: img.mimeType, dataLength: img.data?.length ?? 0 }))
6463
+ }))
6464
+ };
6465
+ console.log(
6466
+ `[ai] Attachments: ${context.attachments?.length ?? 0} total, ${images.length} image(s), ${textAttachments.length} text file(s)`,
6467
+ promptRequest
6314
6468
  );
6315
6469
  const promptDone = agent.prompt(finalPrompt, images.length > 0 ? images : void 0).then(() => {
6316
6470
  console.log(`[ai] Agent prompt completed successfully`);
@@ -6319,7 +6473,8 @@ ${userPrompt}`;
6319
6473
  }).catch((err) => {
6320
6474
  const errMessage = err instanceof Error ? err.message : String(err);
6321
6475
  console.error(`[ai] Agent prompt failed:`, {
6322
- error: errMessage,
6476
+ request: promptRequest,
6477
+ error: serializeLogValue(err),
6323
6478
  errorType: err instanceof Error ? err.constructor.name : typeof err
6324
6479
  });
6325
6480
  if (!eventQueue.errorOccurred) {
@@ -6373,12 +6528,20 @@ ${userPrompt}`;
6373
6528
  };
6374
6529
  } else {
6375
6530
  console.log("[ai] \u26A0\uFE0F Agent ended with no text content and no tool calls.");
6531
+ const upstreamError = eventQueue.lastErrorMessage ? { message: eventQueue.lastErrorMessage } : void 0;
6532
+ console.error("[ai] Agent ended without text content:", {
6533
+ request: promptRequest,
6534
+ upstreamError,
6535
+ finalContentLength: eventQueue.finalContent.length,
6536
+ toolCallCount: eventQueue.toolCallCount
6537
+ });
6376
6538
  yield {
6377
6539
  type: "error",
6378
6540
  content: `The model ${model} from ${providerName} did not provide a response to your prompt. Check your API key and balance and try again.`,
6379
6541
  error: {
6380
6542
  code: "NO_CONTENT_NO_TOOLS",
6381
- message: `The model ${model} from ${providerName} did not provide a response to your prompt.`
6543
+ message: `The model ${model} from ${providerName} did not provide a response to your prompt.`,
6544
+ details: upstreamError
6382
6545
  }
6383
6546
  };
6384
6547
  }
@@ -6485,6 +6648,63 @@ var ProcessingStateManagerImpl = class {
6485
6648
  }
6486
6649
  };
6487
6650
 
6651
+ // src/core/env-manager.ts
6652
+ import { promises as fs } from "fs";
6653
+ import { join as join11 } from "path";
6654
+ function updateRuntimeEnv(values) {
6655
+ for (const [key, value] of Object.entries(values)) {
6656
+ process.env[key] = value;
6657
+ }
6658
+ }
6659
+ async function updateEnvFile(keyNames) {
6660
+ const envPath = join11(process.cwd(), ".env");
6661
+ let content = "";
6662
+ try {
6663
+ content = await fs.readFile(envPath, "utf-8");
6664
+ } catch {
6665
+ }
6666
+ const lines = content.split("\n");
6667
+ const envMap = {};
6668
+ for (const line of lines) {
6669
+ const trimmed = line.trim();
6670
+ if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
6671
+ const index = trimmed.indexOf("=");
6672
+ const key = trimmed.substring(0, index).trim();
6673
+ const value = trimmed.substring(index + 1).trim();
6674
+ envMap[key] = value;
6675
+ }
6676
+ }
6677
+ for (const [key, value] of Object.entries(keyNames)) {
6678
+ if (key) {
6679
+ envMap[key] = value;
6680
+ }
6681
+ }
6682
+ const newLines = [];
6683
+ for (const [key, value] of Object.entries(envMap)) {
6684
+ newLines.push(`${key}=${value}`);
6685
+ }
6686
+ await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
6687
+ }
6688
+ async function readEnvFile() {
6689
+ const envPath = join11(process.cwd(), ".env");
6690
+ const envMap = {};
6691
+ try {
6692
+ const content = await fs.readFile(envPath, "utf-8");
6693
+ const lines = content.split("\n");
6694
+ for (const line of lines) {
6695
+ const trimmed = line.trim();
6696
+ if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
6697
+ const index = trimmed.indexOf("=");
6698
+ const key = trimmed.substring(0, index).trim();
6699
+ const value = trimmed.substring(index + 1).trim();
6700
+ envMap[key] = value;
6701
+ }
6702
+ }
6703
+ } catch {
6704
+ }
6705
+ return envMap;
6706
+ }
6707
+
6488
6708
  // src/features/ask-user/ask-user.routes.ts
6489
6709
  import { Hono } from "hono";
6490
6710
 
@@ -6659,7 +6879,7 @@ import { randomUUID as randomUUID4 } from "crypto";
6659
6879
 
6660
6880
  // src/features/skills/skills.manager.ts
6661
6881
  import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
6662
- import { join as join11 } from "path";
6882
+ import { join as join12 } from "path";
6663
6883
  import { existsSync as existsSync9 } from "fs";
6664
6884
  import { homedir as homedir5 } from "os";
6665
6885
  function parseFrontmatter(markdown) {
@@ -6707,10 +6927,10 @@ function parseFrontmatter(markdown) {
6707
6927
  return { metadata, content };
6708
6928
  }
6709
6929
  function getGlobalSkillsDir() {
6710
- return join11(homedir5(), ".agents", "skills");
6930
+ return join12(homedir5(), ".agents", "skills");
6711
6931
  }
6712
6932
  function getProjectSkillsDir(threadPath) {
6713
- return join11(threadPath, ".agents", "skills");
6933
+ return join12(threadPath, ".agents", "skills");
6714
6934
  }
6715
6935
  function validateSkillName(name) {
6716
6936
  if (!name || name.length === 0 || name.length > 64) {
@@ -6763,8 +6983,8 @@ var SkillManager = class {
6763
6983
  for (const entry of entries) {
6764
6984
  if (!entry.isDirectory()) continue;
6765
6985
  const skillDirName = entry.name;
6766
- const skillPath = join11(dir, skillDirName);
6767
- const skillFilePath = join11(skillPath, "SKILL.md");
6986
+ const skillPath = join12(dir, skillDirName);
6987
+ const skillFilePath = join12(skillPath, "SKILL.md");
6768
6988
  if (!existsSync9(skillFilePath)) {
6769
6989
  console.warn(`Skipping skill directory ${skillDirName}: SKILL.md not found`);
6770
6990
  continue;
@@ -6944,7 +7164,7 @@ async function activateSkills(allSkills, taskDescription, thread, options) {
6944
7164
 
6945
7165
  // src/features/agents/agents.manager.ts
6946
7166
  import { readdir as readdir5, readFile as readFile7 } from "fs/promises";
6947
- import { join as join12 } from "path";
7167
+ import { join as join13 } from "path";
6948
7168
  import { existsSync as existsSync10 } from "fs";
6949
7169
  import { homedir as homedir6 } from "os";
6950
7170
  function parseFrontmatter2(markdown) {
@@ -7002,10 +7222,10 @@ function validateDescription2(description) {
7002
7222
  return !!description && description.length > 0 && description.length <= 1024;
7003
7223
  }
7004
7224
  function getGlobalAgentsDir() {
7005
- return join12(homedir6(), ".agents", "agents");
7225
+ return join13(homedir6(), ".agents", "agents");
7006
7226
  }
7007
7227
  function getProjectAgentsDir(threadPath) {
7008
- return join12(threadPath, ".agents", "agents");
7228
+ return join13(threadPath, ".agents", "agents");
7009
7229
  }
7010
7230
  var AgentsManager = class {
7011
7231
  /**
@@ -7037,8 +7257,8 @@ var AgentsManager = class {
7037
7257
  for (const entry of entries) {
7038
7258
  if (!entry.isDirectory()) continue;
7039
7259
  const agentDirName = entry.name;
7040
- const agentPath = join12(dir, agentDirName);
7041
- const agentFilePath = join12(agentPath, "AGENT.md");
7260
+ const agentPath = join13(dir, agentDirName);
7261
+ const agentFilePath = join13(agentPath, "AGENT.md");
7042
7262
  if (!existsSync10(agentFilePath)) {
7043
7263
  console.warn(`[agents] Skipping agent directory ${agentDirName}: AGENT.md not found`);
7044
7264
  continue;
@@ -7225,7 +7445,7 @@ function extractAssistantContent(events, fallback) {
7225
7445
  // src/features/chat/chat-post.route.ts
7226
7446
  init_database();
7227
7447
  import { readFile as readFile8, unlink } from "fs/promises";
7228
- import { join as join13 } from "path";
7448
+ import { join as join14 } from "path";
7229
7449
 
7230
7450
  // src/features/project-todos/project-todos.database.ts
7231
7451
  init_database();
@@ -8218,7 +8438,7 @@ ${result.output}`;
8218
8438
  try {
8219
8439
  const todo = await getTodoByThreadId(db, threadId);
8220
8440
  if (todo && todo.status === "Plan") {
8221
- const planFilePath = join13(threadPath, `${todo.id}-plan.md`);
8441
+ const planFilePath = join14(threadPath, `${todo.id}-plan.md`);
8222
8442
  try {
8223
8443
  const planContent = await readFile8(planFilePath, "utf-8");
8224
8444
  await updateTodo(db, todo.id, { description: planContent.trim() });
@@ -8430,13 +8650,15 @@ async function updateMessage(db, messageId, content, events, imageUrl) {
8430
8650
  if (hasMessageUsage) {
8431
8651
  if (event.type === "message" && event.message?.usage) {
8432
8652
  const usage = event.message.usage;
8433
- inputTokens += usage.input ?? 0;
8434
- outputTokens += usage.output ?? 0;
8653
+ const tokens = calculateCostEquivalentTokens(usage);
8654
+ inputTokens += tokens.inputTokens;
8655
+ outputTokens += tokens.outputTokens;
8435
8656
  }
8436
8657
  } else {
8437
8658
  if (event.type === "toolcall_delta" && event.partial?.usage) {
8438
- inputTokens += event.partial.usage.input ?? 0;
8439
- outputTokens += event.partial.usage.output ?? 0;
8659
+ const tokens = calculateCostEquivalentTokens(event.partial.usage);
8660
+ inputTokens += tokens.inputTokens;
8661
+ outputTokens += tokens.outputTokens;
8440
8662
  }
8441
8663
  }
8442
8664
  }
@@ -8467,6 +8689,34 @@ async function updateMessage(db, messageId, content, events, imageUrl) {
8467
8689
  throw error;
8468
8690
  }
8469
8691
  }
8692
+ function calculateCostEquivalentTokens(usage) {
8693
+ const input = usage.input ?? 0;
8694
+ const output = usage.output ?? 0;
8695
+ const cacheRead = usage.cacheRead ?? 0;
8696
+ const cacheWrite = usage.cacheWrite ?? 0;
8697
+ const cachedInput = cacheRead + cacheWrite;
8698
+ const uncachedInput = input >= cachedInput ? input - cachedInput : input;
8699
+ const inputCost = usage.cost?.input ?? 0;
8700
+ const cacheCost = (usage.cost?.cacheRead ?? 0) + (usage.cost?.cacheWrite ?? 0);
8701
+ if (uncachedInput > 0 && inputCost > 0 && cacheCost > 0) {
8702
+ const inputCostPerToken = inputCost / uncachedInput;
8703
+ const costEquivalentCachedInput = cacheCost / inputCostPerToken;
8704
+ return {
8705
+ inputTokens: Math.round(uncachedInput + costEquivalentCachedInput),
8706
+ outputTokens: output
8707
+ };
8708
+ }
8709
+ if (input >= cachedInput) {
8710
+ return {
8711
+ inputTokens: uncachedInput + cachedInput,
8712
+ outputTokens: output
8713
+ };
8714
+ }
8715
+ return {
8716
+ inputTokens: input,
8717
+ outputTokens: output
8718
+ };
8719
+ }
8470
8720
  async function getConversationHistory(db, threadId) {
8471
8721
  try {
8472
8722
  const result = await db.execute(
@@ -9760,6 +10010,7 @@ var MetadataManager = class {
9760
10010
  await setState(this.db, "selectedThreadId", state.selectedThreadId);
9761
10011
  await setState(this.db, "enabledModels", state.enabledModels);
9762
10012
  await setState(this.db, "enabledImageModels", state.enabledImageModels);
10013
+ await setState(this.db, "providerEnvValues", state.providerEnvValues);
9763
10014
  await setState(this.db, "onboardingCompleted", state.onboardingCompleted);
9764
10015
  const encryptedKeys = {};
9765
10016
  for (const [provider, key] of Object.entries(state.providerKeys)) {
@@ -9792,6 +10043,7 @@ var MetadataManager = class {
9792
10043
  const onboardingCompleted = allState.onboardingCompleted || false;
9793
10044
  const enabledModels = allState.enabledModels || {};
9794
10045
  const enabledImageModels = allState.enabledImageModels || {};
10046
+ const providerEnvValues = allState.providerEnvValues || {};
9795
10047
  const encryptedKeys = allState.providerKeys || {};
9796
10048
  const providerKeys = {};
9797
10049
  let needsMigration = false;
@@ -9824,6 +10076,7 @@ var MetadataManager = class {
9824
10076
  selectedThreadId,
9825
10077
  onboardingCompleted,
9826
10078
  providerKeys,
10079
+ providerEnvValues,
9827
10080
  enabledModels,
9828
10081
  enabledImageModels
9829
10082
  };
@@ -9902,6 +10155,36 @@ var MetadataManager = class {
9902
10155
  throw new Error(`Failed to get provider keys: ${errorMessage}`);
9903
10156
  }
9904
10157
  }
10158
+ /**
10159
+ * Save provider environment values (non-secret values such as Azure resource name)
10160
+ * @param values - Environment values to merge into metadata state
10161
+ */
10162
+ async saveProviderEnvValues(values) {
10163
+ try {
10164
+ const state = await this.loadState();
10165
+ state.providerEnvValues = {
10166
+ ...state.providerEnvValues,
10167
+ ...values
10168
+ };
10169
+ await this.saveState(state);
10170
+ } catch (error) {
10171
+ throw new Error(`Failed to save provider environment values: ${String(error)}`);
10172
+ }
10173
+ }
10174
+ /**
10175
+ * Get provider environment values stored in metadata state
10176
+ * @returns Record of environment variable names to values
10177
+ */
10178
+ async getProviderEnvValues() {
10179
+ try {
10180
+ const state = await this.loadState();
10181
+ return state.providerEnvValues;
10182
+ } catch (error) {
10183
+ const errorMessage = error instanceof Error ? error.message : String(error);
10184
+ console.error("[MetadataManager] Failed to get provider environment values:", errorMessage);
10185
+ throw new Error(`Failed to get provider environment values: ${errorMessage}`);
10186
+ }
10187
+ }
9905
10188
  /**
9906
10189
  * Set the selected thread ID
9907
10190
  * @param threadId - Thread ID to select (or null to deselect)
@@ -10086,7 +10369,7 @@ async function handleGetAvailableModels(c, modelManager) {
10086
10369
  const bEnabled = enabledModelIds.has(b.id) ? 0 : 1;
10087
10370
  return aEnabled - bEnabled;
10088
10371
  });
10089
- return c.json({ provider, models });
10372
+ return c.json({ provider, models, enabledModelIds: [...enabledModelIds] });
10090
10373
  } catch (error) {
10091
10374
  const message = error instanceof Error ? error.message : "Unknown error";
10092
10375
  return c.json({ error: message }, 500);
@@ -10958,10 +11241,10 @@ async function handleCheckRunning(c, projectManager) {
10958
11241
 
10959
11242
  // src/core/run-command-detector.ts
10960
11243
  import { readFile as readFile9 } from "fs/promises";
10961
- import { join as join14 } from "path";
11244
+ import { join as join15 } from "path";
10962
11245
  async function detectPackageManager(projectPath) {
10963
11246
  try {
10964
- const packageJsonPath = join14(projectPath, "package.json");
11247
+ const packageJsonPath = join15(projectPath, "package.json");
10965
11248
  const content = await readFile9(packageJsonPath, "utf-8");
10966
11249
  const packageJson = JSON.parse(content);
10967
11250
  if (packageJson.packageManager) {
@@ -10977,7 +11260,7 @@ async function detectPackageManager(projectPath) {
10977
11260
  }
10978
11261
  async function detectRunScripts(projectPath) {
10979
11262
  try {
10980
- const packageJsonPath = join14(projectPath, "package.json");
11263
+ const packageJsonPath = join15(projectPath, "package.json");
10981
11264
  const content = await readFile9(packageJsonPath, "utf-8");
10982
11265
  const packageJson = JSON.parse(content);
10983
11266
  const scripts = packageJson.scripts ?? {};
@@ -11024,7 +11307,7 @@ async function suggestRunCommand(projectPath) {
11024
11307
 
11025
11308
  // src/features/projects/projects-package-scripts.route.ts
11026
11309
  import { readFile as readFile10 } from "fs/promises";
11027
- import { join as join15 } from "path";
11310
+ import { join as join16 } from "path";
11028
11311
  import { glob } from "glob";
11029
11312
  function formatScriptName(scriptName) {
11030
11313
  return scriptName.replace(/[-:_]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
@@ -11052,7 +11335,7 @@ async function findPackageScripts(projectPath) {
11052
11335
  const packageManager = await detectPackageManager(projectPath);
11053
11336
  for (const filePath of packageJsonFiles) {
11054
11337
  try {
11055
- const fullPath = join15(projectPath, filePath);
11338
+ const fullPath = join16(projectPath, filePath);
11056
11339
  const content = await readFile10(fullPath, "utf-8");
11057
11340
  const packageJson = JSON.parse(content);
11058
11341
  if (packageJson.scripts) {
@@ -11101,7 +11384,7 @@ async function handleGetPackageScripts(c, projectManager) {
11101
11384
 
11102
11385
  // src/features/projects/projects-ai-files.route.ts
11103
11386
  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";
11387
+ import { join as join17, extname as extname3, basename } from "path";
11105
11388
  import { existsSync as existsSync11 } from "fs";
11106
11389
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
11107
11390
  ".git",
@@ -11120,8 +11403,8 @@ async function buildFullTree(dirPath, relativeDirPath) {
11120
11403
  try {
11121
11404
  const entries = await readdir6(dirPath, { withFileTypes: true });
11122
11405
  for (const entry of entries) {
11123
- const entryRelPath = join16(relativeDirPath, entry.name);
11124
- const entryAbsPath = join16(dirPath, entry.name);
11406
+ const entryRelPath = join17(relativeDirPath, entry.name);
11407
+ const entryAbsPath = join17(dirPath, entry.name);
11125
11408
  if (entry.isDirectory()) {
11126
11409
  const children = await buildFullTree(entryAbsPath, entryRelPath);
11127
11410
  nodes.push({
@@ -11149,8 +11432,8 @@ async function buildMarkdownFilteredTree(dirPath, relativeDirPath) {
11149
11432
  try {
11150
11433
  const entries = await readdir6(dirPath, { withFileTypes: true });
11151
11434
  for (const entry of entries) {
11152
- const entryRelPath = join16(relativeDirPath, entry.name);
11153
- const entryAbsPath = join16(dirPath, entry.name);
11435
+ const entryRelPath = join17(relativeDirPath, entry.name);
11436
+ const entryAbsPath = join17(dirPath, entry.name);
11154
11437
  if (entry.isDirectory()) {
11155
11438
  if (IGNORED_DIRS.has(entry.name)) continue;
11156
11439
  const children = await buildMarkdownFilteredTree(entryAbsPath, entryRelPath);
@@ -11185,8 +11468,8 @@ async function buildAIFileTree(projectPath) {
11185
11468
  { key: "agents", label: "Agents" }
11186
11469
  ];
11187
11470
  for (const { key, label } of agentsFolders) {
11188
- const relPath = join16(".agents", key);
11189
- const absPath = join16(projectPath, relPath);
11471
+ const relPath = join17(".agents", key);
11472
+ const absPath = join17(projectPath, relPath);
11190
11473
  const exists = existsSync11(absPath);
11191
11474
  if (exists) {
11192
11475
  const children = await buildFullTree(absPath, relPath);
@@ -11221,7 +11504,7 @@ async function buildAIFileTree(projectPath) {
11221
11504
  if (entry.name === ".agents" || entry.name === "AGENTS.md" || IGNORED_DIRS.has(entry.name))
11222
11505
  continue;
11223
11506
  const entryRelPath = entry.name;
11224
- const entryAbsPath = join16(projectPath, entry.name);
11507
+ const entryAbsPath = join17(projectPath, entry.name);
11225
11508
  if (entry.isFile() && MARKDOWN_EXTS.has(extname3(entry.name))) {
11226
11509
  nodes.push({
11227
11510
  id: entryRelPath,
@@ -11248,7 +11531,7 @@ async function buildAIFileTree(projectPath) {
11248
11531
  }
11249
11532
  function validateFilePath(projectPath, filePath) {
11250
11533
  if (!filePath) return null;
11251
- const abs = join16(projectPath, filePath);
11534
+ const abs = join17(projectPath, filePath);
11252
11535
  if (!abs.startsWith(projectPath + "/") && abs !== projectPath) return null;
11253
11536
  return abs;
11254
11537
  }
@@ -11374,7 +11657,7 @@ async function handleSaveAIFile(c, projectManager) {
11374
11657
  if (!absPath) {
11375
11658
  return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
11376
11659
  }
11377
- const parentDir = join16(absPath, "..");
11660
+ const parentDir = join17(absPath, "..");
11378
11661
  await mkdir(parentDir, { recursive: true });
11379
11662
  await writeFile2(absPath, content, "utf-8");
11380
11663
  return c.json({ success: true, path: filePath });
@@ -11474,10 +11757,10 @@ async function handleCreateSkill(c, projectManager) {
11474
11757
  if (!project) {
11475
11758
  return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
11476
11759
  }
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");
11760
+ const skillRelPath = join17(".agents", "skills", name);
11761
+ const skillAbsPath = join17(project.path, skillRelPath);
11762
+ const skillFileRelPath = join17(skillRelPath, "SKILL.md");
11763
+ const skillFileAbsPath = join17(skillAbsPath, "SKILL.md");
11481
11764
  if (existsSync11(skillAbsPath)) {
11482
11765
  return c.json(
11483
11766
  { error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
@@ -11579,7 +11862,7 @@ function createProjectRoutes(projectManager, threadManager) {
11579
11862
 
11580
11863
  // src/features/projects/projects.manager.ts
11581
11864
  init_utils();
11582
- import { join as join19 } from "path";
11865
+ import { join as join20 } from "path";
11583
11866
  import { realpathSync as realpathSync2 } from "fs";
11584
11867
  import { rm as rm3 } from "fs/promises";
11585
11868
 
@@ -11800,13 +12083,13 @@ var ProcessManager = class extends EventEmitter {
11800
12083
  // src/features/projects/projects.creator.ts
11801
12084
  init_utils();
11802
12085
  import { randomUUID as randomUUID7 } from "crypto";
11803
- import { basename as basename2, join as join18, relative as relative5 } from "path";
12086
+ import { basename as basename2, join as join19, relative as relative5 } from "path";
11804
12087
  import { access as access3, stat as stat3, readdir as readdir8 } from "fs/promises";
11805
12088
 
11806
12089
  // src/features/scaffold/scaffold.runner.ts
11807
12090
  init_utils();
11808
12091
  import { existsSync as existsSync12 } from "fs";
11809
- import { join as join17 } from "path";
12092
+ import { join as join18 } from "path";
11810
12093
  import { mkdir as mkdir2, readdir as readdir7, stat as stat2, rename, rm as rm2, writeFile as writeFile3 } from "fs/promises";
11811
12094
  import { createInterface as createInterface2 } from "readline";
11812
12095
 
@@ -12528,7 +12811,7 @@ function loadCatalog() {
12528
12811
  }
12529
12812
  async function writeAgentsMd(projectPath, agentsMd) {
12530
12813
  if (!agentsMd) return;
12531
- const agentsPath = join17(projectPath, "AGENTS.md");
12814
+ const agentsPath = join18(projectPath, "AGENTS.md");
12532
12815
  if (existsSync12(agentsPath)) return;
12533
12816
  await writeFile3(agentsPath, agentsMd, "utf-8");
12534
12817
  }
@@ -12686,7 +12969,7 @@ async function* runCommandStreaming(cwd, command) {
12686
12969
  }
12687
12970
  }
12688
12971
  async function* createScaffoldedProjectStreaming(options) {
12689
- const projectPath = join17(options.parentDir, options.threadId);
12972
+ const projectPath = join18(options.parentDir, options.threadId);
12690
12973
  if (!existsSync12(options.parentDir)) {
12691
12974
  yield {
12692
12975
  type: "result",
@@ -12776,7 +13059,7 @@ async function* createScaffoldedProjectStreaming(options) {
12776
13059
  }
12777
13060
  try {
12778
13061
  const projectName2 = getProjectName(options.projectName);
12779
- const projectSubDir = join17(projectPath, projectName2);
13062
+ const projectSubDir = join18(projectPath, projectName2);
12780
13063
  try {
12781
13064
  const subDirStat = await stat2(projectSubDir);
12782
13065
  if (subDirStat.isDirectory()) {
@@ -12786,8 +13069,8 @@ async function* createScaffoldedProjectStreaming(options) {
12786
13069
  };
12787
13070
  const items = await readdir7(projectSubDir);
12788
13071
  for (const item of items) {
12789
- const oldPath = join17(projectSubDir, item);
12790
- const newPath = join17(projectPath, item);
13072
+ const oldPath = join18(projectSubDir, item);
13073
+ const newPath = join18(projectPath, item);
12791
13074
  await rename(oldPath, newPath);
12792
13075
  }
12793
13076
  await rm2(projectSubDir, { recursive: true, force: true });
@@ -13255,7 +13538,7 @@ var ProjectCreator = class {
13255
13538
  return name;
13256
13539
  }
13257
13540
  generateThreadPath(_projectId, threadId) {
13258
- return join18(this.rootFolder, threadId);
13541
+ return join19(this.rootFolder, threadId);
13259
13542
  }
13260
13543
  async *createProjectFromFolder() {
13261
13544
  await loadUtils();
@@ -13405,19 +13688,19 @@ var ProjectCreator = class {
13405
13688
  }
13406
13689
  async findAllPackageJsonFiles(rootPath, currentPath, setupScripts) {
13407
13690
  try {
13408
- await access3(join18(currentPath, "package.json"));
13691
+ await access3(join19(currentPath, "package.json"));
13409
13692
  const relativePath = relative5(rootPath, currentPath);
13410
13693
  let installCommand = "";
13411
13694
  try {
13412
- await access3(join18(currentPath, "yarn.lock"));
13695
+ await access3(join19(currentPath, "yarn.lock"));
13413
13696
  installCommand = "yarn install";
13414
13697
  } catch {
13415
13698
  try {
13416
- await access3(join18(currentPath, "bun.lock"));
13699
+ await access3(join19(currentPath, "bun.lock"));
13417
13700
  installCommand = "bun install";
13418
13701
  } catch {
13419
13702
  try {
13420
- await access3(join18(currentPath, "pnpm-lock.yaml"));
13703
+ await access3(join19(currentPath, "pnpm-lock.yaml"));
13421
13704
  installCommand = "pnpm install";
13422
13705
  } catch {
13423
13706
  installCommand = "npm install";
@@ -13434,7 +13717,7 @@ var ProjectCreator = class {
13434
13717
  try {
13435
13718
  const entries = await readdir8(currentPath);
13436
13719
  for (const entry of entries) {
13437
- const entryPath = join18(currentPath, entry);
13720
+ const entryPath = join19(currentPath, entry);
13438
13721
  try {
13439
13722
  const stats = await stat3(entryPath);
13440
13723
  if (stats.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
@@ -13774,7 +14057,7 @@ var ProjectManagerImpl = class {
13774
14057
  const session = this.terminalSessionManager.getOrCreateSession(threadId, thread.path);
13775
14058
  let workingDir;
13776
14059
  if (cwd) {
13777
- workingDir = cwd.startsWith("/") ? cwd : join19(thread.path, cwd);
14060
+ workingDir = cwd.startsWith("/") ? cwd : join20(thread.path, cwd);
13778
14061
  this.terminalSessionManager.updateWorkingDirectory(threadId, workingDir);
13779
14062
  } else {
13780
14063
  workingDir = session.currentWorkingDirectory;
@@ -13977,80 +14260,59 @@ ___CWD___%s
13977
14260
  // src/features/providers/providers.routes.ts
13978
14261
  import { Hono as Hono7 } from "hono";
13979
14262
 
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}`);
14263
+ // src/features/providers/providers-get.route.ts
14264
+ function getProviderApiKeyEnvName2(providerEnv) {
14265
+ if (providerEnv.length === 0) {
14266
+ return "";
14009
14267
  }
14010
- await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
14268
+ const explicitApiKeyEnv = providerEnv.find((envName) => /api_key$/i.test(envName));
14269
+ return explicitApiKeyEnv ?? providerEnv[providerEnv.length - 1];
14011
14270
  }
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 {
14271
+ function getProviderResourceName2(providerEnv) {
14272
+ const explicitResourceEnv = providerEnv.find((envName) => /resource_name$/i.test(envName));
14273
+ return explicitResourceEnv ?? providerEnv[0] ?? "";
14274
+ }
14275
+ function resolveAzureApiUrl2(providerEnv, envValues) {
14276
+ const resourceEnvName = getProviderResourceName2(providerEnv);
14277
+ const resourceName = envValues[resourceEnvName] ?? "";
14278
+ if (!resourceName) {
14279
+ return "";
14028
14280
  }
14029
- return envMap;
14281
+ return `https://${resourceName}.openai.azure.com/openai/v1`;
14030
14282
  }
14031
-
14032
- // src/features/providers/providers-get.route.ts
14033
14283
  async function getProviders(c, metadataManager) {
14034
14284
  const keys = await metadataManager.getProviderKeys();
14285
+ const persistedEnvValues = await metadataManager.getProviderEnvValues();
14035
14286
  const envFileKeys = await readEnvFile();
14036
14287
  const catalog = await getModelsCatalog();
14037
14288
  const providersWithKeys = [];
14038
14289
  if (catalog) {
14039
14290
  for (const [catalogId, catalogProvider] of Object.entries(catalog)) {
14040
- const keyName = catalogProvider.env?.[0] || "";
14291
+ const envNames = catalogProvider.env ?? [];
14292
+ const keyName = getProviderApiKeyEnvName2(envNames);
14293
+ const envValues = {};
14294
+ for (const envName of envNames) {
14295
+ envValues[envName] = envFileKeys[envName] ?? process.env[envName] ?? persistedEnvValues[envName] ?? "";
14296
+ }
14041
14297
  const envFileKey = keyName ? envFileKeys[keyName] : "";
14042
14298
  const processEnvKey = keyName ? process.env[keyName] : "";
14043
14299
  const storedKey = keys[catalogProvider.name] || keys[catalogId];
14044
14300
  const hasEnvKey = Boolean(envFileKey || processEnvKey);
14045
14301
  const resolvedKey = storedKey ?? envFileKey ?? processEnvKey ?? "";
14046
14302
  const isEnv = !storedKey && hasEnvKey && resolvedKey !== "";
14303
+ const api = catalogId === "azure" ? resolveAzureApiUrl2(envNames, envValues) : catalogProvider.api ?? "";
14304
+ if (keyName && !envValues[keyName]) {
14305
+ envValues[keyName] = resolvedKey;
14306
+ }
14047
14307
  providersWithKeys.push({
14048
14308
  name: catalogProvider.name ?? catalogId,
14049
14309
  slug: catalogId,
14050
14310
  url: catalogProvider.doc ?? "",
14051
14311
  keyName,
14052
14312
  authType: "APIKey",
14053
- api: catalogProvider.api ?? "",
14313
+ api,
14314
+ env: envNames,
14315
+ envValues,
14054
14316
  apiKey: resolvedKey,
14055
14317
  isEnv
14056
14318
  });
@@ -14069,6 +14331,7 @@ async function postProviderKeys(c, metadataManager) {
14069
14331
  await metadataManager.saveProviderKey(providerName, apiKey);
14070
14332
  const keyName = await resolveProviderKeyName(providerName);
14071
14333
  if (keyName) {
14334
+ updateRuntimeEnv({ [keyName]: apiKey });
14072
14335
  await updateEnvFile({ [keyName]: apiKey });
14073
14336
  }
14074
14337
  return c.json({ success: true });
@@ -14093,6 +14356,7 @@ async function postProviderBulkKeys(c, metadataManager) {
14093
14356
  }
14094
14357
  }
14095
14358
  if (Object.keys(envUpdates).length > 0) {
14359
+ updateRuntimeEnv(envUpdates);
14096
14360
  await updateEnvFile(envUpdates);
14097
14361
  }
14098
14362
  return c.json({ success: true });
@@ -14101,6 +14365,28 @@ async function postProviderBulkKeys(c, metadataManager) {
14101
14365
  }
14102
14366
  }
14103
14367
 
14368
+ // src/features/providers/providers-post-env.route.ts
14369
+ async function postProviderEnv(c, metadataManager) {
14370
+ try {
14371
+ const { values } = await c.req.json();
14372
+ if (!values || typeof values !== "object") {
14373
+ return c.json({ error: "values object is required" }, 400);
14374
+ }
14375
+ const envValues = {};
14376
+ for (const [key, value] of Object.entries(values)) {
14377
+ if (typeof value === "string") {
14378
+ envValues[key] = value;
14379
+ }
14380
+ }
14381
+ await metadataManager.saveProviderEnvValues(envValues);
14382
+ updateRuntimeEnv(envValues);
14383
+ await updateEnvFile(envValues);
14384
+ return c.json({ success: true });
14385
+ } catch (error) {
14386
+ return c.json({ error: `Failed to save provider environment: ${String(error)}` }, 500);
14387
+ }
14388
+ }
14389
+
14104
14390
  // src/features/models/model-info-openrouter.ts
14105
14391
  async function getOpenRouterCredits(apiKey) {
14106
14392
  try {
@@ -14196,13 +14482,24 @@ async function getProviderCredits(c, metadataManager) {
14196
14482
  // src/features/providers/providers-get-logo.route.ts
14197
14483
  var LOGO_BASE_URL = "https://models.tarsk.io/svg";
14198
14484
  var SLUG_PATTERN = /^[a-z0-9._-]+$/i;
14485
+ var logoCache = /* @__PURE__ */ new Map();
14486
+ var LOGO_CACHE_TTL_MS = 15 * 60 * 1e3;
14199
14487
  async function getProviderLogo(c) {
14200
14488
  const slug = c.req.param("slug");
14201
14489
  if (!slug || !SLUG_PATTERN.test(slug)) {
14202
14490
  return c.body(null, 404);
14203
14491
  }
14492
+ const cached = logoCache.get(slug);
14493
+ if (cached && Date.now() < cached.expires) {
14494
+ return c.body(cached.svg, 200, {
14495
+ "Content-Type": "image/svg+xml",
14496
+ "Cache-Control": "public, max-age=86400"
14497
+ });
14498
+ }
14204
14499
  try {
14205
- const response = await fetch(`${LOGO_BASE_URL}/${slug}.svg`, { signal: AbortSignal.timeout(5e3) });
14500
+ const response = await fetch(`${LOGO_BASE_URL}/${slug}.svg`, {
14501
+ signal: AbortSignal.timeout(5e3)
14502
+ });
14206
14503
  if (!response.ok) {
14207
14504
  return c.body(null, 404);
14208
14505
  }
@@ -14210,6 +14507,7 @@ async function getProviderLogo(c) {
14210
14507
  if (!svg.includes("<svg")) {
14211
14508
  return c.body(null, 404);
14212
14509
  }
14510
+ logoCache.set(slug, { svg, expires: Date.now() + LOGO_CACHE_TTL_MS });
14213
14511
  return c.body(svg, 200, {
14214
14512
  "Content-Type": "image/svg+xml",
14215
14513
  "Cache-Control": "public, max-age=86400"
@@ -14262,6 +14560,7 @@ function createProviderRoutes(metadataManager) {
14262
14560
  router.get("/", (c) => getProviders(c, metadataManager));
14263
14561
  router.post("/keys", (c) => postProviderKeys(c, metadataManager));
14264
14562
  router.post("/bulk-keys", (c) => postProviderBulkKeys(c, metadataManager));
14563
+ router.post("/env", (c) => postProviderEnv(c, metadataManager));
14265
14564
  router.get("/:name/credits", (c) => getProviderCredits(c, metadataManager));
14266
14565
  router.get("/:slug/logo", (c) => getProviderLogo(c));
14267
14566
  router.post("/open-external", (c) => openExternalUrl(c));
@@ -14512,7 +14811,7 @@ function createScaffoldRoutes(projectManager) {
14512
14811
 
14513
14812
  // src/features/slash-commands/slash-commands.manager.ts
14514
14813
  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";
14814
+ import { join as join21, basename as basename3, extname as extname4, relative as relative6 } from "path";
14516
14815
  import { existsSync as existsSync13 } from "fs";
14517
14816
  import { homedir as homedir7 } from "os";
14518
14817
  function slugify(filename) {
@@ -14593,14 +14892,14 @@ var SlashCommandManager = class _SlashCommandManager {
14593
14892
  const commands = /* @__PURE__ */ new Map();
14594
14893
  const globalDir = getGlobalCommandsDir();
14595
14894
  if (existsSync13(globalDir)) {
14596
- const globalCommands = await this.loadCommandsFromDir(globalDir, "global");
14895
+ const globalCommands = await this.loadCommandsFromDir(globalDir, threadPath, "global");
14597
14896
  for (const cmd of globalCommands) {
14598
14897
  commands.set(cmd.name, cmd);
14599
14898
  }
14600
14899
  }
14601
14900
  const projectDir = getProjectCommandsDir(threadPath);
14602
14901
  if (existsSync13(projectDir)) {
14603
- const projectCommands = await this.loadCommandsFromDir(projectDir, "project");
14902
+ const projectCommands = await this.loadCommandsFromDir(projectDir, threadPath, "project");
14604
14903
  for (const cmd of projectCommands) {
14605
14904
  if (!_SlashCommandManager.BUILT_IN_COMMANDS.has(cmd.name)) {
14606
14905
  commands.set(cmd.name, cmd);
@@ -14617,7 +14916,7 @@ var SlashCommandManager = class _SlashCommandManager {
14617
14916
  /**
14618
14917
  * Load commands from a specific directory
14619
14918
  */
14620
- async loadCommandsFromDir(dir, scope) {
14919
+ async loadCommandsFromDir(dir, threadPath, scope) {
14621
14920
  const commands = [];
14622
14921
  try {
14623
14922
  const files = await readdir9(dir);
@@ -14634,7 +14933,7 @@ var SlashCommandManager = class _SlashCommandManager {
14634
14933
  content,
14635
14934
  metadata,
14636
14935
  scope,
14637
- filePath
14936
+ filePath: scope === "project" ? relative6(threadPath, filePath) : filePath
14638
14937
  });
14639
14938
  }
14640
14939
  } catch (error) {
@@ -15855,7 +16154,7 @@ async function handleDeleteThread(c, threadManager) {
15855
16154
  // src/core/project-inspector.ts
15856
16155
  import { readFile as readFile13, readdir as readdir10 } from "fs/promises";
15857
16156
  import { existsSync as existsSync14 } from "fs";
15858
- import { join as join23, basename as basename4, relative as relative6 } from "path";
16157
+ import { join as join23, basename as basename4, relative as relative7 } from "path";
15859
16158
  import { glob as glob2 } from "glob";
15860
16159
 
15861
16160
  // src/features/project-scripts/project-scripts.database.ts
@@ -16205,7 +16504,7 @@ function buildRunCommand(scriptName, workspace, packageManager, repoType, projec
16205
16504
  return `npm run ${scriptName} --workspace=${workspace.relativePath}`;
16206
16505
  }
16207
16506
  if (!isRoot) {
16208
- const relPath = relative6(projectPath, workspace.folder);
16507
+ const relPath = relative7(projectPath, workspace.folder);
16209
16508
  return `cd ${relPath} && ${runCmd}`;
16210
16509
  }
16211
16510
  return runCmd;
@@ -17964,7 +18263,7 @@ import { completeSimple } from "@mariozechner/pi-ai";
17964
18263
  async function resolveModelAndKey(provider, modelId, metadataManager) {
17965
18264
  const providerConfig = await resolveProviderConfig(provider);
17966
18265
  if (!providerConfig) return null;
17967
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
18266
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
17968
18267
  if (!apiKey) {
17969
18268
  const providerKeys = await metadataManager.getProviderKeys();
17970
18269
  apiKey = providerKeys[providerConfig.id];
@@ -19894,15 +20193,6 @@ async function gitSyncBranchHandler(c, metadataManager) {
19894
20193
  400
19895
20194
  );
19896
20195
  }
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
20196
  const { spawnProcess: spawnProcess2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
19907
20197
  return new Promise((resolve6) => {
19908
20198
  console.log(`[sync-branch] Checking branch status...`);
@@ -20038,13 +20328,12 @@ async function gitSyncBranchHandler(c, metadataManager) {
20038
20328
  }
20039
20329
  pullProc.on("close", (code) => {
20040
20330
  if (code === 0) {
20041
- console.log(
20042
- `[sync-branch] \u2713 Successfully synced ${currentBranch} with ${defaultBranch}`
20043
- );
20331
+ const message = currentBranch === defaultBranch ? `Successfully pulled latest changes from origin/${defaultBranch}` : `Successfully synced ${currentBranch} with ${defaultBranch}`;
20332
+ console.log(`[sync-branch] \u2713 ${message}`);
20044
20333
  resolve6(
20045
20334
  c.json({
20046
20335
  success: true,
20047
- message: `Successfully synced ${currentBranch} with ${defaultBranch}`,
20336
+ message,
20048
20337
  branch: currentBranch,
20049
20338
  defaultBranch
20050
20339
  })
@@ -21204,7 +21493,7 @@ async function generateImage(c, metadataManager, conversationManager, threadMana
21204
21493
  if (!providerConfig) {
21205
21494
  return c.json({ error: `Unknown provider: ${provider}` }, 400);
21206
21495
  }
21207
- let apiKey = providerConfig.env && providerConfig.env.length > 0 ? process.env[providerConfig.env[0]] : void 0;
21496
+ let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
21208
21497
  if (!apiKey) {
21209
21498
  const providerKeys = await metadataManager.getProviderKeys();
21210
21499
  apiKey = providerKeys[providerConfig.id];
@@ -21631,6 +21920,7 @@ async function startTarskServer(options) {
21631
21920
  const conversationManager = new ConversationManagerImpl(dataDir);
21632
21921
  const agentExecutor = new PiExecutorImpl(metadataManager);
21633
21922
  await metadataManager.initialize();
21923
+ updateRuntimeEnv(await metadataManager.getProviderEnvValues());
21634
21924
  await conversationManager.initialize();
21635
21925
  app.get("/health", (c) => {
21636
21926
  return c.json({