deepcode-ai 1.1.30 → 1.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2256,6 +2256,9 @@ import { Effect as Effect9 } from "effect";
2256
2256
  import { z as z10 } from "zod";
2257
2257
  import { Effect as Effect10 } from "effect";
2258
2258
  import { z as z11 } from "zod";
2259
+ import { Effect as Effect11 } from "effect";
2260
+ import { z as z12 } from "zod";
2261
+ import { zodToJsonSchema as zodToJsonSchema2 } from "zod-to-json-schema";
2259
2262
  import path14 from "path";
2260
2263
  import { readdir as readdir5, lstat as lstat3 } from "fs/promises";
2261
2264
  var DeepCodeError = class extends Error {
@@ -3682,24 +3685,25 @@ Execute this task using the available tools. Return a summary of what was done.`
3682
3685
  */
3683
3686
  async executeTaskWithLLM(prompt, session, mode, options, taskType) {
3684
3687
  const taskPrompt = this.createInternalPromptMessage(prompt);
3685
- const allowedToolNames = this.allowedToolNamesForTaskType(mode, taskType);
3688
+ const allowedToolNames = this.allowedToolNamesForTaskType(mode, taskType, this.getRevealedTools(session));
3686
3689
  const resolvedModel = session.model ?? resolveConfiguredModelForProvider(this.config, session.provider);
3687
3690
  const toolProfile = resolveModelExecutionProfile(session.provider, resolvedModel);
3688
- const toolDefinitions = this.toolDefinitionsForNames(allowedToolNames, toolProfile.toolSchemaMode);
3689
- const textToolFallbackEnabled = toolDefinitions.length > 0 && toolProfile.toolCallStrategy !== "native";
3690
3691
  const maxTaskIterations = 10;
3691
3692
  let taskIterations = 0;
3692
3693
  let finalAssistantText = "";
3693
3694
  while (taskIterations < maxTaskIterations) {
3694
3695
  taskIterations++;
3695
3696
  this.enforceBudget(session.id);
3697
+ const toolDefinitions = this.toolDefinitionsForNames(allowedToolNames, toolProfile.toolSchemaMode);
3698
+ const textToolFallbackEnabled = toolDefinitions.length > 0 && toolProfile.toolCallStrategy !== "native";
3696
3699
  const chunks = this.providerManager.chat(
3697
3700
  this.messagesForSystemPrompt(
3698
3701
  session,
3699
3702
  this.systemPromptForMode(mode),
3700
3703
  true,
3701
3704
  [taskPrompt],
3702
- textToolFallbackEnabled ? buildFallbackToolCallPrompt(allowedToolNames) : void 0
3705
+ textToolFallbackEnabled ? buildFallbackToolCallPrompt(allowedToolNames) : void 0,
3706
+ mode === "build" ? this.buildDeferredToolsHint(session) : void 0
3703
3707
  ),
3704
3708
  {
3705
3709
  preferredProvider: options.provider ?? session.provider,
@@ -3760,7 +3764,19 @@ ${assistantText}` : assistantText;
3760
3764
  break;
3761
3765
  }
3762
3766
  for (const call of toolCalls) {
3763
- const result = await this.executeTool(call, session, mode, options.signal, allowedToolNames, options.onToolActivity);
3767
+ const result = await this.executeTool(
3768
+ call,
3769
+ session,
3770
+ mode,
3771
+ options.signal,
3772
+ allowedToolNames,
3773
+ options.onToolActivity,
3774
+ (names) => {
3775
+ for (const name of names) {
3776
+ if (this.tools.get(name)) allowedToolNames.add(name);
3777
+ }
3778
+ }
3779
+ );
3764
3780
  this.sessions.addMessage(session.id, {
3765
3781
  role: "tool",
3766
3782
  source: "tool",
@@ -3781,12 +3797,15 @@ ${assistantText}` : assistantText;
3781
3797
  const toolProfile = resolveModelExecutionProfile(session.provider, resolvedModel);
3782
3798
  const baseAllowedToolNames = turnStrategy.allowTools ? this.allowedToolNamesForMode(mode) : /* @__PURE__ */ new Set();
3783
3799
  const allowedToolNames = this.applyToolOverrides(baseAllowedToolNames, options);
3784
- const toolDefinitions = turnStrategy.allowTools ? this.toolDefinitionsForNames(allowedToolNames, toolProfile.toolSchemaMode) : [];
3785
- const textToolFallbackEnabled = toolDefinitions.length > 0 && toolProfile.toolCallStrategy !== "native";
3800
+ for (const name of this.getRevealedTools(session)) {
3801
+ if (this.tools.get(name)) allowedToolNames.add(name);
3802
+ }
3786
3803
  while (iterations < maxIterations) {
3787
3804
  iterations += 1;
3788
3805
  options.onIteration?.(iterations, maxIterations);
3789
3806
  this.enforceBudget(session.id);
3807
+ const toolDefinitions = turnStrategy.allowTools ? this.toolDefinitionsForNames(allowedToolNames, toolProfile.toolSchemaMode) : [];
3808
+ const textToolFallbackEnabled = toolDefinitions.length > 0 && toolProfile.toolCallStrategy !== "native";
3790
3809
  await this.compressContextIfNeeded(session, turnStrategy.systemPrompt, options);
3791
3810
  const chunks = this.providerManager.chat(
3792
3811
  this.messagesForSystemPrompt(
@@ -3794,7 +3813,8 @@ ${assistantText}` : assistantText;
3794
3813
  turnStrategy.systemPrompt,
3795
3814
  turnStrategy.allowTools,
3796
3815
  [],
3797
- textToolFallbackEnabled ? buildFallbackToolCallPrompt(allowedToolNames) : void 0
3816
+ textToolFallbackEnabled ? buildFallbackToolCallPrompt(allowedToolNames) : void 0,
3817
+ mode === "build" ? this.buildDeferredToolsHint(session) : void 0
3798
3818
  ),
3799
3819
  {
3800
3820
  preferredProvider: options.provider ?? session.provider,
@@ -3860,7 +3880,19 @@ ${assistantText}` : assistantText;
3860
3880
  }
3861
3881
  if (toolCalls.length === 0) break;
3862
3882
  for (const call of toolCalls) {
3863
- const result = await this.executeTool(call, session, mode, options.signal, allowedToolNames, options.onToolActivity);
3883
+ const result = await this.executeTool(
3884
+ call,
3885
+ session,
3886
+ mode,
3887
+ options.signal,
3888
+ allowedToolNames,
3889
+ options.onToolActivity,
3890
+ (names) => {
3891
+ for (const name of names) {
3892
+ if (this.tools.get(name)) allowedToolNames.add(name);
3893
+ }
3894
+ }
3895
+ );
3864
3896
  this.sessions.addMessage(session.id, {
3865
3897
  role: "tool",
3866
3898
  source: "tool",
@@ -3890,7 +3922,7 @@ ${assistantText}` : assistantText;
3890
3922
  }
3891
3923
  return { path: entry.path, restored: true };
3892
3924
  }
3893
- async executeTool(call, session, mode, signal, allowedToolNames = this.allowedToolNamesForMode(mode), onToolActivity) {
3925
+ async executeTool(call, session, mode, signal, allowedToolNames = this.allowedToolNamesForMode(mode), onToolActivity, onRevealTools) {
3894
3926
  if (!this.isToolAllowed(call.name, mode)) {
3895
3927
  const modeHint = mode === "plan" ? "Switch to BUILD mode (press Tab in the TUI) to enable this tool." : "";
3896
3928
  return {
@@ -3948,6 +3980,12 @@ ${assistantText}` : assistantText;
3948
3980
  const stack = this.undoStacks.get(session.id) ?? [];
3949
3981
  stack.push({ path: filePath, previousContent });
3950
3982
  this.undoStacks.set(session.id, stack);
3983
+ },
3984
+ revealTools: (names) => {
3985
+ const current = this.getRevealedTools(session);
3986
+ session.metadata.revealedTools = [.../* @__PURE__ */ new Set([...current, ...names])];
3987
+ this.sessions.save(session);
3988
+ onRevealTools?.(names);
3951
3989
  }
3952
3990
  };
3953
3991
  try {
@@ -4032,9 +4070,23 @@ ${assistantText}` : assistantText;
4032
4070
  }
4033
4071
  allowedToolNamesForMode(mode) {
4034
4072
  return new Set(
4035
- this.tools.list().filter((tool) => this.isToolAllowed(tool.name, mode)).map((tool) => tool.name)
4073
+ this.tools.list().filter((tool) => this.isToolAllowed(tool.name, mode) && !tool.deferred).map((tool) => tool.name)
4036
4074
  );
4037
4075
  }
4076
+ getRevealedTools(session) {
4077
+ return Array.isArray(session.metadata.revealedTools) ? session.metadata.revealedTools : [];
4078
+ }
4079
+ buildDeferredToolsHint(session) {
4080
+ const deferred = this.tools.listDeferred();
4081
+ if (deferred.length === 0) return void 0;
4082
+ const revealed = new Set(this.getRevealedTools(session));
4083
+ const unrevealed = deferred.filter((t2) => !revealed.has(t2.name));
4084
+ if (unrevealed.length === 0) return void 0;
4085
+ return [
4086
+ "Deferred tools (not in schema \u2014 call tool_search with a keyword to activate):",
4087
+ ...unrevealed.map((t2) => `- ${t2.name}: ${t2.description.slice(0, 100)}`)
4088
+ ].join("\n");
4089
+ }
4038
4090
  applyToolOverrides(base, options) {
4039
4091
  if (!options.allowedTools && !options.disallowedTools) return base;
4040
4092
  let names = options.allowedTools ? new Set(options.allowedTools) : new Set(base);
@@ -4043,10 +4095,14 @@ ${assistantText}` : assistantText;
4043
4095
  }
4044
4096
  return new Set([...names].filter((n) => base.has(n)));
4045
4097
  }
4046
- allowedToolNamesForTaskType(mode, taskType) {
4098
+ allowedToolNamesForTaskType(mode, taskType, revealedTools) {
4047
4099
  if (taskType === "research") return /* @__PURE__ */ new Set([...PLAN_ALLOWED_TOOLS]);
4048
4100
  if (taskType === "verify") return /* @__PURE__ */ new Set(["read_file", "list_dir", "analyze_code", "search_text", "bash"]);
4049
- return this.allowedToolNamesForMode(mode);
4101
+ const base = this.allowedToolNamesForMode(mode);
4102
+ for (const name of revealedTools ?? []) {
4103
+ if (this.tools.get(name)) base.add(name);
4104
+ }
4105
+ return base;
4050
4106
  }
4051
4107
  toolDefinitionsForNames(names, schemaMode = "full") {
4052
4108
  return this.tools.list().filter((tool) => names.has(tool.name)).map((tool) => ({
@@ -4071,7 +4127,7 @@ ${assistantText}` : assistantText;
4071
4127
  systemPromptForMode(mode) {
4072
4128
  return mode === "plan" ? PLAN_SYSTEM_PROMPT : BUILD_SYSTEM_PROMPT;
4073
4129
  }
4074
- messagesForSystemPrompt(session, systemPrompt, toolsEnabled, extraMessages = [], fallbackToolPrompt) {
4130
+ messagesForSystemPrompt(session, systemPrompt, toolsEnabled, extraMessages = [], fallbackToolPrompt, deferredToolsHint) {
4075
4131
  return [
4076
4132
  {
4077
4133
  id: "mode_system",
@@ -4085,6 +4141,12 @@ ${assistantText}` : assistantText;
4085
4141
  content: this.runtimeContextPrompt(session, toolsEnabled),
4086
4142
  createdAt: session.createdAt
4087
4143
  },
4144
+ ...deferredToolsHint ? [{
4145
+ id: "deferred_tools_system",
4146
+ role: "system",
4147
+ content: deferredToolsHint,
4148
+ createdAt: session.createdAt
4149
+ }] : [],
4088
4150
  ...fallbackToolPrompt ? [{
4089
4151
  id: "tool_fallback_system",
4090
4152
  role: "system",
@@ -4492,6 +4554,9 @@ var ToolRegistry = class {
4492
4554
  list() {
4493
4555
  return [...this.tools.values()];
4494
4556
  }
4557
+ listDeferred() {
4558
+ return [...this.tools.values()].filter((t2) => t2.deferred);
4559
+ }
4495
4560
  descriptions() {
4496
4561
  return this.list().map((tool) => `- ${tool.name}: ${tool.description}`).join("\n");
4497
4562
  }
@@ -4502,6 +4567,7 @@ function adaptMcpTool(client, tool, serverName) {
4502
4567
  name: qualifiedName,
4503
4568
  description: tool.description ?? tool.name,
4504
4569
  parameters: z22.record(z22.unknown()).default({}),
4570
+ deferred: true,
4505
4571
  execute: (args) => Effect3.tryPromise({
4506
4572
  try: () => client.callTool(tool.name, args),
4507
4573
  catch: (e) => e instanceof Error ? e : new Error(String(e))
@@ -6350,6 +6416,7 @@ var ProviderManager = class {
6350
6416
  const isPreferred = providerId === options.preferredProvider;
6351
6417
  const providerModel = isPreferred ? options.model : resolveConfiguredModelForProvider(this.config, providerId);
6352
6418
  if (!isPreferred && !providerModel) continue;
6419
+ validateModelForProvider(providerId, providerModel);
6353
6420
  for (let attempt = 0; attempt <= this.retries; attempt += 1) {
6354
6421
  let emitted = false;
6355
6422
  try {
@@ -6387,6 +6454,7 @@ var ProviderManager = class {
6387
6454
  );
6388
6455
  }
6389
6456
  const model = normalizeProviderModelId(providerId, configuredModel);
6457
+ validateModelForProvider(providerId, model);
6390
6458
  const started = Date.now();
6391
6459
  const controller = new AbortController();
6392
6460
  const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 15e3);
@@ -6417,6 +6485,26 @@ var ProviderManager = class {
6417
6485
  }
6418
6486
  }
6419
6487
  };
6488
+ var OPENROUTER_SUFFIX_RE = /:(?:free|nitro|extended|beta|floor|online)$/i;
6489
+ function validateModelForProvider(providerId, model) {
6490
+ if (!model || providerId === "openrouter") return;
6491
+ if (OPENROUTER_SUFFIX_RE.test(model)) {
6492
+ throw new ProviderError(
6493
+ `Model "${model}" uses an OpenRouter-specific variant suffix (e.g. :free, :nitro). Set provider to "openrouter" instead of "${providerId}".`,
6494
+ providerId
6495
+ );
6496
+ }
6497
+ const slash = model.indexOf("/");
6498
+ if (slash > 0) {
6499
+ const prefix = model.slice(0, slash);
6500
+ if (PROVIDER_IDS.includes(prefix) && prefix !== providerId) {
6501
+ throw new ProviderError(
6502
+ `Model "${model}" belongs to provider "${prefix}" but is being used with "${providerId}". Switch provider to "${prefix}" or use a model native to "${providerId}".`,
6503
+ providerId
6504
+ );
6505
+ }
6506
+ }
6507
+ }
6420
6508
  function backoffMs(attempt) {
6421
6509
  return Math.min(250 * 2 ** attempt, 2e3);
6422
6510
  }
@@ -7750,6 +7838,45 @@ function createTaskTool(subagents, worktree) {
7750
7838
  })
7751
7839
  });
7752
7840
  }
7841
+ function createToolSearchTool(registry) {
7842
+ return defineTool({
7843
+ name: "tool_search",
7844
+ description: "Search and activate deferred tools (MCP integrations) by name or description keyword. Call this before using a tool that is not in the current schema. Matched tools are revealed and available in subsequent calls this session.",
7845
+ parameters: z12.object({
7846
+ query: z12.string().min(1).describe("Keyword to search in tool names and descriptions")
7847
+ }),
7848
+ execute: (args, context) => Effect11.tryPromise({
7849
+ try: async () => {
7850
+ const query = args.query.toLowerCase();
7851
+ const deferred = registry.listDeferred();
7852
+ if (deferred.length === 0) {
7853
+ return "No deferred tools are configured. Add MCP servers in .deepcode/config.json to enable integrations.";
7854
+ }
7855
+ const matches = deferred.filter(
7856
+ (t2) => t2.name.toLowerCase().includes(query) || t2.description.toLowerCase().includes(query)
7857
+ );
7858
+ if (matches.length === 0) {
7859
+ const available = deferred.map((t2) => `- ${t2.name}: ${t2.description.slice(0, 100)}`).join("\n");
7860
+ return `No deferred tools match "${args.query}".
7861
+
7862
+ All available deferred tools:
7863
+ ${available}`;
7864
+ }
7865
+ context.revealTools?.(matches.map((t2) => t2.name));
7866
+ const schemas = matches.map((t2) => ({
7867
+ name: t2.name,
7868
+ description: t2.description,
7869
+ parameters: zodToJsonSchema2(t2.parameters, { target: "jsonSchema7" })
7870
+ }));
7871
+ return [
7872
+ `Revealed ${matches.length} tool(s) \u2014 available for calls in this session:`,
7873
+ JSON.stringify(schemas, null, 2)
7874
+ ].join("\n\n");
7875
+ },
7876
+ catch: (e) => e instanceof Error ? e : new Error(String(e))
7877
+ })
7878
+ });
7879
+ }
7753
7880
  var PROJECT_MARKER2 = ".git";
7754
7881
  var SKIP_DIRS = /* @__PURE__ */ new Set([
7755
7882
  ".git",
@@ -9912,6 +10039,7 @@ async function createRuntime(options) {
9912
10039
  tools.register(tool);
9913
10040
  }
9914
10041
  }
10042
+ tools.register(createToolSearchTool(tools));
9915
10043
  const agent = new Agent(
9916
10044
  providers,
9917
10045
  tools,
@@ -28163,7 +28291,7 @@ function parseVersion2(version) {
28163
28291
  if (!match) return null;
28164
28292
  return [Number(match[1]), Number(match[2]), Number(match[3])];
28165
28293
  }
28166
- var VERSION = "1.1.30".length > 0 ? "1.1.30" : "0.0.0-dev";
28294
+ var VERSION = "1.1.32".length > 0 ? "1.1.32" : "0.0.0-dev";
28167
28295
  var updateCommand = {
28168
28296
  name: "update",
28169
28297
  description: "Check published DeepCode versions",
@@ -29800,6 +29928,8 @@ var AppContainer = ({ cwd, config, provider, model, resumeSessionId }) => {
29800
29928
  const lastSubmittedPromptRef = useRef17(null);
29801
29929
  const runStartedAtRef = useRef17(null);
29802
29930
  const streamingResponseLengthRef = useRef17(0);
29931
+ const pendingTextBufferRef = useRef17("");
29932
+ const taskStreamsBufferRef = useRef17({});
29803
29933
  const drainingQueueRef = useRef17(false);
29804
29934
  const messageQueueRef = useRef17([]);
29805
29935
  const sessionShellAllowlistRef = useRef17(/* @__PURE__ */ new Set());
@@ -30076,6 +30206,28 @@ var AppContainer = ({ cwd, config, provider, model, resumeSessionId }) => {
30076
30206
  }, 250);
30077
30207
  return () => clearInterval(interval);
30078
30208
  }, [isRunning]);
30209
+ useEffect27(() => {
30210
+ const id = setInterval(() => {
30211
+ const text = pendingTextBufferRef.current;
30212
+ if (text) {
30213
+ pendingTextBufferRef.current = "";
30214
+ setPendingAssistantText((prev) => prev + text);
30215
+ }
30216
+ const taskBuf = taskStreamsBufferRef.current;
30217
+ const taskKeys = Object.keys(taskBuf);
30218
+ if (taskKeys.length > 0) {
30219
+ taskStreamsBufferRef.current = {};
30220
+ setTaskStreams((prev) => {
30221
+ const next = { ...prev };
30222
+ for (const taskId of taskKeys) {
30223
+ next[taskId] = (next[taskId] ?? "") + taskBuf[taskId];
30224
+ }
30225
+ return next;
30226
+ });
30227
+ }
30228
+ }, 50);
30229
+ return () => clearInterval(id);
30230
+ }, []);
30079
30231
  useEffect27(() => {
30080
30232
  let mounted = true;
30081
30233
  const initialize = async () => {
@@ -30325,6 +30477,8 @@ var AppContainer = ({ cwd, config, provider, model, resumeSessionId }) => {
30325
30477
  historyManager.addItem({ type: "user", text: prompt }, Date.now());
30326
30478
  lastSubmittedPromptRef.current = prompt;
30327
30479
  setPromptSuggestion(null);
30480
+ pendingTextBufferRef.current = "";
30481
+ taskStreamsBufferRef.current = {};
30328
30482
  setPendingAssistantText("");
30329
30483
  setIsRunning(true);
30330
30484
  setIsReceivingContent(false);
@@ -30345,15 +30499,12 @@ var AppContainer = ({ cwd, config, provider, model, resumeSessionId }) => {
30345
30499
  signal: controller.signal,
30346
30500
  onChunk: (text) => {
30347
30501
  streamingResponseLengthRef.current += text.length;
30348
- setPendingAssistantText((prev) => prev + text);
30502
+ pendingTextBufferRef.current += text;
30349
30503
  setIsReceivingContent(true);
30350
30504
  },
30351
30505
  onChunkForTask: (taskId, text) => {
30352
30506
  streamingResponseLengthRef.current += text.length;
30353
- setTaskStreams((prev) => ({
30354
- ...prev,
30355
- [taskId]: (prev[taskId] ?? "") + text
30356
- }));
30507
+ taskStreamsBufferRef.current[taskId] = (taskStreamsBufferRef.current[taskId] ?? "") + text;
30357
30508
  setIsReceivingContent(true);
30358
30509
  },
30359
30510
  onUsage: (inputTokens, outputTokens) => {
@@ -30409,6 +30560,8 @@ var AppContainer = ({ cwd, config, provider, model, resumeSessionId }) => {
30409
30560
  );
30410
30561
  } finally {
30411
30562
  abortRef.current = null;
30563
+ pendingTextBufferRef.current = "";
30564
+ taskStreamsBufferRef.current = {};
30412
30565
  setPendingAssistantText("");
30413
30566
  setIsRunning(false);
30414
30567
  setLiveToolCalls([]);