@cosmicstack/mercury-agent 0.2.4 → 0.2.6

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
@@ -1706,16 +1706,16 @@ var CLIChannel = class extends BaseChannel {
1706
1706
  }
1707
1707
  }
1708
1708
  async prompt(question) {
1709
- return new Promise((resolve9) => {
1710
- this.rl?.question(question, (answer) => resolve9(answer.trim()));
1709
+ return new Promise((resolve10) => {
1710
+ this.rl?.question(question, (answer) => resolve10(answer.trim()));
1711
1711
  });
1712
1712
  }
1713
1713
  async askPermission(prompt) {
1714
- return new Promise((resolve9) => {
1714
+ return new Promise((resolve10) => {
1715
1715
  console.log("");
1716
1716
  console.log(chalk2.yellow(` \u26A0 ${prompt}`));
1717
1717
  this.rl?.question(chalk2.yellow(" > "), (answer) => {
1718
- resolve9(answer.trim());
1718
+ resolve10(answer.trim());
1719
1719
  });
1720
1720
  });
1721
1721
  }
@@ -1978,15 +1978,15 @@ var TelegramChannel = class extends BaseChannel {
1978
1978
  reply_markup: keyboard
1979
1979
  });
1980
1980
  }
1981
- return new Promise((resolve9) => {
1982
- this.pendingApprovals.set(`${id}:yes`, () => resolve9("yes"));
1983
- this.pendingApprovals.set(`${id}:always`, () => resolve9("always"));
1984
- this.pendingApprovals.set(`${id}:no`, () => resolve9("no"));
1981
+ return new Promise((resolve10) => {
1982
+ this.pendingApprovals.set(`${id}:yes`, () => resolve10("yes"));
1983
+ this.pendingApprovals.set(`${id}:always`, () => resolve10("always"));
1984
+ this.pendingApprovals.set(`${id}:no`, () => resolve10("no"));
1985
1985
  setTimeout(() => {
1986
1986
  this.pendingApprovals.delete(`${id}:yes`);
1987
1987
  this.pendingApprovals.delete(`${id}:always`);
1988
1988
  this.pendingApprovals.delete(`${id}:no`);
1989
- resolve9("no");
1989
+ resolve10("no");
1990
1990
  }, 12e4);
1991
1991
  });
1992
1992
  }
@@ -2304,6 +2304,7 @@ var PermissionManager = class {
2304
2304
  elevatedCommands = /* @__PURE__ */ new Set();
2305
2305
  pendingApprovals = /* @__PURE__ */ new Set();
2306
2306
  currentChannelType = "cli";
2307
+ tempScopes = [];
2307
2308
  constructor() {
2308
2309
  this.cwd = process.cwd();
2309
2310
  this.manifest = this.load();
@@ -2334,8 +2335,8 @@ var PermissionManager = class {
2334
2335
  clearElevation() {
2335
2336
  this.elevatedCommands.clear();
2336
2337
  }
2337
- isElevated(tool24) {
2338
- if (this.elevatedCommands.has(tool24)) return true;
2338
+ isElevated(tool25) {
2339
+ if (this.elevatedCommands.has(tool25)) return true;
2339
2340
  return false;
2340
2341
  }
2341
2342
  isShellElevated() {
@@ -2394,12 +2395,18 @@ var PermissionManager = class {
2394
2395
  }
2395
2396
  const resolved = resolve(path3);
2396
2397
  const scope = this.findScope(resolved);
2398
+ const tempScope = this.findTempScope(resolved);
2397
2399
  if (scope) {
2398
2400
  if (mode === "read" && scope.read) return { allowed: true };
2399
2401
  if (mode === "write" && scope.write) return { allowed: true };
2400
2402
  return { allowed: false, reason: `Permission denied: ${mode} access to ${path3} (scope has ${mode}=false)` };
2401
2403
  }
2402
- return await this.requestScope(resolved, mode);
2404
+ if (tempScope) {
2405
+ if (mode === "read" && tempScope.read) return { allowed: true };
2406
+ if (mode === "write" && tempScope.write) return { allowed: true };
2407
+ return { allowed: false, reason: `Permission denied: ${mode} access to ${path3}` };
2408
+ }
2409
+ return { allowed: false, reason: `Permission denied for ${mode} access to ${path3}` };
2403
2410
  }
2404
2411
  async checkShellCommand(command) {
2405
2412
  if (this.autoApproveAll) {
@@ -2430,7 +2437,7 @@ var PermissionManager = class {
2430
2437
  if (hasPathTraversal) {
2431
2438
  const scopeCheck = await this.checkFsAccess(hasPathTraversal, "write");
2432
2439
  if (!scopeCheck.allowed) {
2433
- return { allowed: false, reason: scopeCheck.reason, needsApproval: true };
2440
+ return { allowed: false, reason: `No permission to access ${hasPathTraversal}. Use approve_scope tool with path="${hasPathTraversal}" and mode="write" to request access.`, needsApproval: false };
2434
2441
  }
2435
2442
  }
2436
2443
  }
@@ -2501,22 +2508,39 @@ var PermissionManager = class {
2501
2508
  }
2502
2509
  return void 0;
2503
2510
  }
2504
- async requestScope(path3, mode) {
2511
+ async requestScopeExternal(path3, mode) {
2505
2512
  if (!this.askHandler) {
2506
- return { allowed: false, reason: `No permission to ${mode} ${path3}. Add scope to ~/.mercury/permissions.yaml` };
2513
+ return { allowed: false, reason: `Permission denied for ${mode} access to ${path3}` };
2507
2514
  }
2508
- const response = await this.askHandler(
2509
- `Mercury needs ${mode} access to ${path3}. Allow? (y/n/always): `
2510
- );
2511
- if (response.toLowerCase() === "always") {
2515
+ const prompt = `Mercury needs ${mode} access to:
2516
+ ${path3}
2517
+
2518
+ Allow access?`;
2519
+ const response = await this.askHandler(prompt);
2520
+ if (response === "always") {
2512
2521
  this.addScope(path3, mode === "read", mode === "write");
2513
2522
  return { allowed: true };
2514
2523
  }
2515
- if (response.toLowerCase() === "y" || response.toLowerCase() === "yes") {
2524
+ if (response === "yes") {
2525
+ this.addTempScope(path3, mode === "read", mode === "write");
2516
2526
  return { allowed: true };
2517
2527
  }
2518
2528
  return { allowed: false, reason: `Permission denied for ${mode} access to ${path3}` };
2519
2529
  }
2530
+ addTempScope(path3, read, write) {
2531
+ const resolved = resolve(path3);
2532
+ this.tempScopes.push({ path: resolved, read, write });
2533
+ logger.info({ path: resolved, read, write }, "Temp permission scope added (session only)");
2534
+ }
2535
+ findTempScope(resolvedPath) {
2536
+ for (const scope of this.tempScopes) {
2537
+ const scopeResolved = resolve(scope.path.replace(/^~/, homedir2()));
2538
+ if (resolvedPath === scopeResolved || resolvedPath.startsWith(scopeResolved + sep)) {
2539
+ return scope;
2540
+ }
2541
+ }
2542
+ return void 0;
2543
+ }
2520
2544
  matchPattern(command, pattern) {
2521
2545
  const regexStr = "^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$";
2522
2546
  try {
@@ -2583,7 +2607,8 @@ function createReadFileTool(permissions) {
2583
2607
  const resolved = resolve2(path3);
2584
2608
  const check = await permissions.checkFsAccess(resolved, "read");
2585
2609
  if (!check.allowed) {
2586
- return `Error: ${check.reason}`;
2610
+ const parentDir = resolve2(resolved, "..");
2611
+ return `Error: Permission denied for read access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="read" to request access from the user.`;
2587
2612
  }
2588
2613
  if (!existsSync7(resolved)) {
2589
2614
  return `Error: File not found: ${resolved}`;
@@ -2620,7 +2645,8 @@ function createWriteFileTool(permissions) {
2620
2645
  const resolved = resolve3(path3);
2621
2646
  const check = await permissions.checkFsAccess(resolved, "write");
2622
2647
  if (!check.allowed) {
2623
- return `Error: ${check.reason}`;
2648
+ const parentDir = resolve3(resolved, "..");
2649
+ return `Error: Permission denied for write access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="write" to request access from the user.`;
2624
2650
  }
2625
2651
  if (!existsSync8(resolved)) {
2626
2652
  return `Error: File not found: ${resolved}. Use create_file to create new files.`;
@@ -2651,7 +2677,8 @@ function createCreateFileTool(permissions) {
2651
2677
  const resolved = resolve4(path3);
2652
2678
  const check = await permissions.checkFsAccess(resolved, "write");
2653
2679
  if (!check.allowed) {
2654
- return `Error: ${check.reason}`;
2680
+ const parentDir = resolve4(resolved, "..");
2681
+ return `Error: Permission denied for write access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="write" to request access from the user.`;
2655
2682
  }
2656
2683
  if (existsSync9(resolved)) {
2657
2684
  return `Error: File already exists: ${resolved}. Use write_file to modify existing files.`;
@@ -2685,7 +2712,7 @@ function createListDirTool(permissions) {
2685
2712
  const resolved = resolve5(path3);
2686
2713
  const check = await permissions.checkFsAccess(resolved, "read");
2687
2714
  if (!check.allowed) {
2688
- return `Error: ${check.reason}`;
2715
+ return `Error: Permission denied for read access to ${resolved}. Use the approve_scope tool with path="${resolved}" and mode="read" to request access from the user.`;
2689
2716
  }
2690
2717
  if (!existsSync10(resolved)) {
2691
2718
  return `Error: Directory not found: ${resolved}`;
@@ -2743,7 +2770,8 @@ function createDeleteFileTool(permissions) {
2743
2770
  const resolved = resolve6(path3);
2744
2771
  const check = await permissions.checkFsAccess(resolved, "write");
2745
2772
  if (!check.allowed) {
2746
- return `Error: ${check.reason}`;
2773
+ const parentDir = resolve6(resolved, "..");
2774
+ return `Error: Permission denied for write access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="write" to request access from the user.`;
2747
2775
  }
2748
2776
  if (!existsSync11(resolved)) {
2749
2777
  return `Error: File not found: ${resolved}`;
@@ -2779,7 +2807,8 @@ function createEditFileTool(permissions) {
2779
2807
  const resolved = resolve7(path3);
2780
2808
  const fsCheck = await permissions.checkFsAccess(resolved, "write");
2781
2809
  if (!fsCheck.allowed) {
2782
- return `Error: ${fsCheck.reason}`;
2810
+ const parentDir = resolve7(resolved, "..");
2811
+ return `Error: Permission denied for write access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="write" to request access from the user.`;
2783
2812
  }
2784
2813
  try {
2785
2814
  const content = readFileSync9(resolved, "utf-8");
@@ -2820,7 +2849,8 @@ function createSendFileTool(permissions, sendFile) {
2820
2849
  const resolved = resolve8(path3);
2821
2850
  const check = await permissions.checkFsAccess(resolved, "read");
2822
2851
  if (!check.allowed) {
2823
- return `Error: ${check.reason}`;
2852
+ const parentDir = resolve8(resolved, "..");
2853
+ return `Error: Permission denied for read access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="read" to request access from the user.`;
2824
2854
  }
2825
2855
  if (!existsSync12(resolved)) {
2826
2856
  return `Error: File not found: ${resolved}`;
@@ -2844,18 +2874,40 @@ function createSendFileTool(permissions, sendFile) {
2844
2874
  });
2845
2875
  }
2846
2876
 
2847
- // src/capabilities/shell/run-command.ts
2877
+ // src/capabilities/filesystem/approve-scope.ts
2848
2878
  import { tool as tool8 } from "ai";
2849
2879
  import { z as z8 } from "zod";
2880
+ import { resolve as resolve9 } from "path";
2881
+ function createApproveScopeTool(permissions) {
2882
+ return tool8({
2883
+ description: 'Request user approval to access a directory outside current scopes. Use this when a file tool returns a permission denied error. The user gets an approval prompt (Allow/Always/Deny buttons on Telegram, yes/always/no on CLI). "Allow" grants session-only access. "Always" persists to disk. After approval, retry the original file operation.',
2884
+ parameters: z8.object({
2885
+ path: z8.string().describe("The directory path to request access to"),
2886
+ mode: z8.enum(["read", "write"]).describe("The access mode needed")
2887
+ }),
2888
+ execute: async ({ path: path3, mode }) => {
2889
+ const resolved = resolve9(path3);
2890
+ const result = await permissions.requestScopeExternal(resolved, mode);
2891
+ if (result.allowed) {
2892
+ return `Access approved for ${mode} access to ${resolved}. You can now retry the file operation.`;
2893
+ }
2894
+ return `Access denied for ${mode} access to ${resolved}. The user did not approve scope access.`;
2895
+ }
2896
+ });
2897
+ }
2898
+
2899
+ // src/capabilities/shell/run-command.ts
2900
+ import { tool as tool9 } from "ai";
2901
+ import { z as z9 } from "zod";
2850
2902
  import { execSync } from "child_process";
2851
2903
  function createRunCommandTool(permissions) {
2852
- return tool8({
2904
+ return tool9({
2853
2905
  description: `Run a shell command. Commands run in the current working directory unless an absolute path is given.
2854
2906
  Blocked commands (sudo, rm -rf /, etc.) are never executed.
2855
2907
  Auto-approved commands (ls, cat, git status, curl, etc.) run without asking.
2856
2908
  Other commands require user approval \u2014 tell the user what command you want to run and ask for confirmation. If they say "yes", try again. If they say "always", use the approve_command tool.`,
2857
- parameters: z8.object({
2858
- command: z8.string().describe("The shell command to execute")
2909
+ parameters: z9.object({
2910
+ command: z9.string().describe("The shell command to execute")
2859
2911
  }),
2860
2912
  execute: async ({ command }) => {
2861
2913
  const check = await permissions.checkShellCommand(command);
@@ -2895,13 +2947,13 @@ Error: ${stderr}`;
2895
2947
  }
2896
2948
 
2897
2949
  // src/capabilities/shell/approve-command.ts
2898
- import { tool as tool9 } from "ai";
2899
- import { z as z9 } from "zod";
2950
+ import { tool as tool10 } from "ai";
2951
+ import { z as z10 } from "zod";
2900
2952
  function createApproveCommandTool(permissions) {
2901
- return tool9({
2953
+ return tool10({
2902
2954
  description: 'Permanently approve a command type so it runs without asking in the future. Use this when the user says "always" or "always approve" for a command. For example, if the user says "always approve curl", call this with command="curl".',
2903
- parameters: z9.object({
2904
- command: z9.string().describe('The base command to permanently approve (e.g. "curl", "docker", "npm")')
2955
+ parameters: z10.object({
2956
+ command: z10.string().describe('The base command to permanently approve (e.g. "curl", "docker", "npm")')
2905
2957
  }),
2906
2958
  execute: async ({ command }) => {
2907
2959
  const baseCmd = command.trim().split(/\s+/)[0];
@@ -2912,15 +2964,15 @@ function createApproveCommandTool(permissions) {
2912
2964
  }
2913
2965
 
2914
2966
  // src/capabilities/skills/install-skill.ts
2915
- import { tool as tool10 } from "ai";
2916
- import { z as z10 } from "zod";
2967
+ import { tool as tool11 } from "ai";
2968
+ import { z as z11 } from "zod";
2917
2969
  import { parse as parseYaml4 } from "yaml";
2918
2970
  function createInstallSkillTool(skillLoader) {
2919
- return tool10({
2971
+ return tool11({
2920
2972
  description: "Install a new skill by providing SKILL.md markdown content or a URL. The content must have YAML frontmatter (---) with at least name and description fields.",
2921
- parameters: z10.object({
2922
- content: z10.string().optional().describe("Raw SKILL.md markdown content with YAML frontmatter"),
2923
- url: z10.string().optional().describe("URL to fetch a SKILL.md from")
2973
+ parameters: z11.object({
2974
+ content: z11.string().optional().describe("Raw SKILL.md markdown content with YAML frontmatter"),
2975
+ url: z11.string().optional().describe("URL to fetch a SKILL.md from")
2924
2976
  }),
2925
2977
  execute: async ({ content, url }) => {
2926
2978
  let skillContent;
@@ -2960,12 +3012,12 @@ function createInstallSkillTool(skillLoader) {
2960
3012
  }
2961
3013
 
2962
3014
  // src/capabilities/skills/list-skills.ts
2963
- import { tool as tool11 } from "ai";
2964
- import { z as z11 } from "zod";
3015
+ import { tool as tool12 } from "ai";
3016
+ import { z as z12 } from "zod";
2965
3017
  function createListSkillsTool(skillLoader) {
2966
- return tool11({
3018
+ return tool12({
2967
3019
  description: "List all installed skills with their names and descriptions.",
2968
- parameters: z11.object({}),
3020
+ parameters: z12.object({}),
2969
3021
  execute: async () => {
2970
3022
  const skills = skillLoader.getDiscovered();
2971
3023
  if (skills.length === 0) {
@@ -2977,13 +3029,13 @@ function createListSkillsTool(skillLoader) {
2977
3029
  }
2978
3030
 
2979
3031
  // src/capabilities/skills/use-skill.ts
2980
- import { tool as tool12 } from "ai";
2981
- import { z as z12 } from "zod";
3032
+ import { tool as tool13 } from "ai";
3033
+ import { z as z13 } from "zod";
2982
3034
  function createUseSkillTool(skillLoader, permissions) {
2983
- return tool12({
3035
+ return tool13({
2984
3036
  description: "Load and invoke a skill by name. Returns the skill's full instructions which should be followed as guidance for the current task.",
2985
- parameters: z12.object({
2986
- name: z12.string().describe("Name of the skill to invoke")
3037
+ parameters: z13.object({
3038
+ name: z13.string().describe("Name of the skill to invoke")
2987
3039
  }),
2988
3040
  execute: async ({ name }) => {
2989
3041
  const skill = skillLoader.load(name);
@@ -3010,18 +3062,18 @@ Allowed tools: ${skill["allowed-tools"].join(", ")}`;
3010
3062
  }
3011
3063
 
3012
3064
  // src/capabilities/scheduler/schedule-task.ts
3013
- import { tool as tool13 } from "ai";
3014
- import { z as z13 } from "zod";
3065
+ import { tool as tool14 } from "ai";
3066
+ import { z as z14 } from "zod";
3015
3067
  import cron2 from "node-cron";
3016
3068
  function createScheduleTaskTool(scheduler, getContext) {
3017
- return tool13({
3069
+ return tool14({
3018
3070
  description: 'Schedule a task. Use "cron" for recurring tasks (e.g. "0 9 * * *" for daily at 9am) or "delay_seconds" for one-shot delayed tasks (e.g. 15 for "remind me in 15 seconds"). Provide exactly one of cron or delay_seconds.',
3019
- parameters: z13.object({
3020
- cron: z13.string().optional().describe('Cron expression for recurring tasks (e.g. "0 9 * * *" for daily at 9am)'),
3021
- delay_seconds: z13.number().optional().describe('Delay in seconds for one-shot tasks (e.g. 15 for "remind me in 15 seconds")'),
3022
- description: z13.string().describe("Human-readable description of what this task does"),
3023
- prompt: z13.string().optional().describe("Prompt to send to the agent when the task fires"),
3024
- skill_name: z13.string().optional().describe("Name of a skill to invoke when the task fires")
3071
+ parameters: z14.object({
3072
+ cron: z14.string().optional().describe('Cron expression for recurring tasks (e.g. "0 9 * * *" for daily at 9am)'),
3073
+ delay_seconds: z14.number().optional().describe('Delay in seconds for one-shot tasks (e.g. 15 for "remind me in 15 seconds")'),
3074
+ description: z14.string().describe("Human-readable description of what this task does"),
3075
+ prompt: z14.string().optional().describe("Prompt to send to the agent when the task fires"),
3076
+ skill_name: z14.string().optional().describe("Name of a skill to invoke when the task fires")
3025
3077
  }),
3026
3078
  execute: async ({ cron: cronExpr, delay_seconds, description, prompt, skill_name }) => {
3027
3079
  if (!cronExpr && !delay_seconds) {
@@ -3074,12 +3126,12 @@ function createScheduleTaskTool(scheduler, getContext) {
3074
3126
  }
3075
3127
 
3076
3128
  // src/capabilities/scheduler/list-tasks.ts
3077
- import { tool as tool14 } from "ai";
3078
- import { z as z14 } from "zod";
3129
+ import { tool as tool15 } from "ai";
3130
+ import { z as z15 } from "zod";
3079
3131
  function createListTasksTool(scheduler) {
3080
- return tool14({
3132
+ return tool15({
3081
3133
  description: "List all scheduled tasks with their cron expressions and descriptions.",
3082
- parameters: z14.object({}),
3134
+ parameters: z15.object({}),
3083
3135
  execute: async () => {
3084
3136
  const manifests = scheduler.getManifests();
3085
3137
  if (manifests.length === 0) {
@@ -3094,13 +3146,13 @@ function createListTasksTool(scheduler) {
3094
3146
  }
3095
3147
 
3096
3148
  // src/capabilities/scheduler/cancel-task.ts
3097
- import { tool as tool15 } from "ai";
3098
- import { z as z15 } from "zod";
3149
+ import { tool as tool16 } from "ai";
3150
+ import { z as z16 } from "zod";
3099
3151
  function createCancelTaskTool(scheduler) {
3100
- return tool15({
3152
+ return tool16({
3101
3153
  description: "Cancel and remove a scheduled task by its ID.",
3102
- parameters: z15.object({
3103
- id: z15.string().describe("ID of the scheduled task to cancel")
3154
+ parameters: z16.object({
3155
+ id: z16.string().describe("ID of the scheduled task to cancel")
3104
3156
  }),
3105
3157
  execute: async ({ id }) => {
3106
3158
  const manifests = scheduler.getManifests();
@@ -3116,12 +3168,12 @@ function createCancelTaskTool(scheduler) {
3116
3168
  }
3117
3169
 
3118
3170
  // src/capabilities/system/budget-status.ts
3119
- import { tool as tool16 } from "ai";
3120
- import { z as z16 } from "zod";
3171
+ import { tool as tool17 } from "ai";
3172
+ import { z as z17 } from "zod";
3121
3173
  function createBudgetStatusTool(tokenBudget) {
3122
- return tool16({
3174
+ return tool17({
3123
3175
  description: "Check the current token budget status \u2014 how many tokens have been used today, how many remain, and what percentage is consumed.",
3124
- parameters: z16.object({}),
3176
+ parameters: z17.object({}),
3125
3177
  execute: async () => {
3126
3178
  return tokenBudget.getStatusText();
3127
3179
  }
@@ -3129,14 +3181,14 @@ function createBudgetStatusTool(tokenBudget) {
3129
3181
  }
3130
3182
 
3131
3183
  // src/capabilities/git/git-status.ts
3132
- import { tool as tool17 } from "ai";
3133
- import { z as z17 } from "zod";
3184
+ import { tool as tool18 } from "ai";
3185
+ import { z as z18 } from "zod";
3134
3186
  import { execSync as execSync2 } from "child_process";
3135
3187
  function createGitStatusTool() {
3136
- return tool17({
3188
+ return tool18({
3137
3189
  description: "Show the working tree status. Returns staged, unstaged, and untracked files.",
3138
- parameters: z17.object({
3139
- path: z17.string().optional().describe("Path to check (defaults to current directory)")
3190
+ parameters: z18.object({
3191
+ path: z18.string().optional().describe("Path to check (defaults to current directory)")
3140
3192
  }),
3141
3193
  execute: async ({ path: path3 }) => {
3142
3194
  try {
@@ -3152,15 +3204,15 @@ function createGitStatusTool() {
3152
3204
  }
3153
3205
 
3154
3206
  // src/capabilities/git/git-diff.ts
3155
- import { tool as tool18 } from "ai";
3156
- import { z as z18 } from "zod";
3207
+ import { tool as tool19 } from "ai";
3208
+ import { z as z19 } from "zod";
3157
3209
  import { execSync as execSync3 } from "child_process";
3158
3210
  function createGitDiffTool() {
3159
- return tool18({
3211
+ return tool19({
3160
3212
  description: "Show changes between commits, commit and working tree, etc. Shows what has been modified.",
3161
- parameters: z18.object({
3162
- path: z18.string().optional().describe("File or directory to diff"),
3163
- staged: z18.boolean().optional().describe("Show staged changes (cached) instead of unstaged")
3213
+ parameters: z19.object({
3214
+ path: z19.string().optional().describe("File or directory to diff"),
3215
+ staged: z19.boolean().optional().describe("Show staged changes (cached) instead of unstaged")
3164
3216
  }),
3165
3217
  execute: async ({ path: path3, staged }) => {
3166
3218
  try {
@@ -3179,15 +3231,15 @@ function createGitDiffTool() {
3179
3231
  }
3180
3232
 
3181
3233
  // src/capabilities/git/git-log.ts
3182
- import { tool as tool19 } from "ai";
3183
- import { z as z19 } from "zod";
3234
+ import { tool as tool20 } from "ai";
3235
+ import { z as z20 } from "zod";
3184
3236
  import { execSync as execSync4 } from "child_process";
3185
3237
  function createGitLogTool() {
3186
- return tool19({
3238
+ return tool20({
3187
3239
  description: "Show commit logs. Returns recent commit history with hash, author, date, and message.",
3188
- parameters: z19.object({
3189
- count: z19.number().optional().describe("Number of commits to show (default 10)"),
3190
- path: z19.string().optional().describe("File or directory to show log for")
3240
+ parameters: z20.object({
3241
+ count: z20.number().optional().describe("Number of commits to show (default 10)"),
3242
+ path: z20.string().optional().describe("File or directory to show log for")
3191
3243
  }),
3192
3244
  execute: async ({ count, path: path3 }) => {
3193
3245
  try {
@@ -3205,14 +3257,14 @@ function createGitLogTool() {
3205
3257
  }
3206
3258
 
3207
3259
  // src/capabilities/git/git-add.ts
3208
- import { tool as tool20 } from "ai";
3209
- import { z as z20 } from "zod";
3260
+ import { tool as tool21 } from "ai";
3261
+ import { z as z21 } from "zod";
3210
3262
  import { execSync as execSync5 } from "child_process";
3211
3263
  function createGitAddTool() {
3212
- return tool20({
3264
+ return tool21({
3213
3265
  description: "Add file contents to the index (staging area). Prepares files for commit.",
3214
- parameters: z20.object({
3215
- paths: z20.array(z20.string()).describe("File paths to stage")
3266
+ parameters: z21.object({
3267
+ paths: z21.array(z21.string()).describe("File paths to stage")
3216
3268
  }),
3217
3269
  execute: async ({ paths }) => {
3218
3270
  try {
@@ -3227,14 +3279,14 @@ function createGitAddTool() {
3227
3279
  }
3228
3280
 
3229
3281
  // src/capabilities/git/git-commit.ts
3230
- import { tool as tool21 } from "ai";
3231
- import { z as z21 } from "zod";
3282
+ import { tool as tool22 } from "ai";
3283
+ import { z as z22 } from "zod";
3232
3284
  import { execSync as execSync6 } from "child_process";
3233
3285
  function createGitCommitTool() {
3234
- return tool21({
3286
+ return tool22({
3235
3287
  description: "Record changes to the repository. Creates a new commit with staged changes.",
3236
- parameters: z21.object({
3237
- message: z21.string().describe("Commit message")
3288
+ parameters: z22.object({
3289
+ message: z22.string().describe("Commit message")
3238
3290
  }),
3239
3291
  execute: async ({ message }) => {
3240
3292
  try {
@@ -3253,15 +3305,15 @@ function createGitCommitTool() {
3253
3305
  }
3254
3306
 
3255
3307
  // src/capabilities/git/git-push.ts
3256
- import { tool as tool22 } from "ai";
3257
- import { z as z22 } from "zod";
3308
+ import { tool as tool23 } from "ai";
3309
+ import { z as z23 } from "zod";
3258
3310
  import { execSync as execSync7 } from "child_process";
3259
3311
  function createGitPushTool(permissions) {
3260
- return tool22({
3312
+ return tool23({
3261
3313
  description: "Push commits to a remote repository. This modifies a remote and requires approval.",
3262
- parameters: z22.object({
3263
- remote: z22.string().optional().describe("Remote name (default: origin)"),
3264
- branch: z22.string().optional().describe("Branch name (default: current branch)")
3314
+ parameters: z23.object({
3315
+ remote: z23.string().optional().describe("Remote name (default: origin)"),
3316
+ branch: z23.string().optional().describe("Branch name (default: current branch)")
3265
3317
  }),
3266
3318
  execute: async ({ remote, branch }) => {
3267
3319
  const cmd = `git push ${remote || "origin"} ${branch || ""}`.trim();
@@ -3286,8 +3338,8 @@ Ask the user for permission. If they approve, try again. If they say "always", u
3286
3338
  }
3287
3339
 
3288
3340
  // src/capabilities/web/fetch-url.ts
3289
- import { tool as tool23 } from "ai";
3290
- import { z as z23 } from "zod";
3341
+ import { tool as tool24 } from "ai";
3342
+ import { z as z24 } from "zod";
3291
3343
  var MAX_CONTENT_LENGTH = 15e3;
3292
3344
  function stripHtml(html) {
3293
3345
  let text = html;
@@ -3320,11 +3372,11 @@ function stripHtml(html) {
3320
3372
  return text;
3321
3373
  }
3322
3374
  function createFetchUrlTool() {
3323
- return tool23({
3375
+ return tool24({
3324
3376
  description: "Fetch a URL and return its content as text. Strips HTML to readable markdown-like format. Useful for reading documentation, APIs, or web pages.",
3325
- parameters: z23.object({
3326
- url: z23.string().describe("The URL to fetch"),
3327
- format: z23.enum(["text", "markdown"]).optional().describe("Output format (default: markdown)")
3377
+ parameters: z24.object({
3378
+ url: z24.string().describe("The URL to fetch"),
3379
+ format: z24.enum(["text", "markdown"]).optional().describe("Output format (default: markdown)")
3328
3380
  }),
3329
3381
  execute: async ({ url, format }) => {
3330
3382
  const outputFormat = format ?? "markdown";
@@ -3413,6 +3465,7 @@ var CapabilityRegistry = class {
3413
3465
  if (this.sendFileHandler) {
3414
3466
  this.tools.send_file = createSendFileTool(this.permissions, this.sendFileHandler);
3415
3467
  }
3468
+ this.tools.approve_scope = createApproveScopeTool(this.permissions);
3416
3469
  logger.info("Filesystem tools registered");
3417
3470
  }
3418
3471
  if (manifest.capabilities.shell.enabled) {
@@ -4257,7 +4310,7 @@ async function runWithWatchdog(agentFn) {
4257
4310
  await attempt();
4258
4311
  }
4259
4312
  function sleep(ms) {
4260
- return new Promise((resolve9) => setTimeout(resolve9, ms));
4313
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
4261
4314
  }
4262
4315
 
4263
4316
  // src/index.ts
@@ -4291,10 +4344,10 @@ function splashScreen() {
4291
4344
  }
4292
4345
  async function ask(prompt) {
4293
4346
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
4294
- return new Promise((resolve9) => {
4347
+ return new Promise((resolve10) => {
4295
4348
  rl.question(prompt, (answer) => {
4296
4349
  rl.close();
4297
- resolve9(answer.trim());
4350
+ resolve10(answer.trim());
4298
4351
  });
4299
4352
  });
4300
4353
  }