winter-super-cli 2026.6.24 → 2026.6.27
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/CHANGELOG.md +28 -5
- package/README.md +85 -0
- package/package.json +5 -1
- package/resources/local/gsap-skills/.claude-plugin/marketplace.json +20 -0
- package/resources/local/gsap-skills/.claude-plugin/plugin.json +6 -0
- package/resources/local/gsap-skills/.cursor-plugin/marketplace.json +13 -0
- package/resources/local/gsap-skills/.cursor-plugin/plugin.json +22 -0
- package/resources/local/gsap-skills/.github/copilot-instructions.md +17 -0
- package/resources/local/gsap-skills/.github/instructions/react.instructions.md +15 -0
- package/resources/local/gsap-skills/.github/instructions/scrolltrigger.instructions.md +18 -0
- package/resources/local/gsap-skills/AGENTS.md +27 -0
- package/resources/local/gsap-skills/CLAUDE.md +1 -0
- package/resources/local/gsap-skills/GEMINI.md +1 -0
- package/resources/local/gsap-skills/LICENSE +21 -0
- package/resources/local/gsap-skills/README.md +163 -0
- package/resources/local/gsap-skills/assets/gsap-green.svg +7 -0
- package/resources/local/gsap-skills/assets/gsap-icon-inverted.svg +15 -0
- package/resources/local/gsap-skills/assets/gsap-icon-square.svg +1 -0
- package/resources/local/gsap-skills/assets/gsap-white.svg +7 -0
- package/resources/local/gsap-skills/examples/README.md +29 -0
- package/resources/local/gsap-skills/examples/nuxt/app/app.vue +3 -0
- package/resources/local/gsap-skills/examples/nuxt/app/composables/useGSAP.ts +91 -0
- package/resources/local/gsap-skills/examples/nuxt/app/pages/index.vue +55 -0
- package/resources/local/gsap-skills/examples/nuxt/nuxt.config.ts +4 -0
- package/resources/local/gsap-skills/examples/nuxt/package.json +18 -0
- package/resources/local/gsap-skills/examples/react/App.jsx +46 -0
- package/resources/local/gsap-skills/examples/react/index.html +12 -0
- package/resources/local/gsap-skills/examples/react/main.jsx +9 -0
- package/resources/local/gsap-skills/examples/react/package.json +21 -0
- package/resources/local/gsap-skills/examples/react/vite.config.js +7 -0
- package/resources/local/gsap-skills/examples/vanilla/index.html +33 -0
- package/resources/local/gsap-skills/examples/vanilla/main.js +36 -0
- package/resources/local/gsap-skills/examples/vue/app.vue +47 -0
- package/resources/local/gsap-skills/examples/vue/index.html +15 -0
- package/resources/local/gsap-skills/examples/vue/main.js +9 -0
- package/resources/local/gsap-skills/examples/vue/package.json +19 -0
- package/resources/local/gsap-skills/examples/vue/vite.config.js +7 -0
- package/resources/local/gsap-skills/skills/gsap-core/SKILL.md +254 -0
- package/resources/local/gsap-skills/skills/gsap-frameworks/SKILL.md +266 -0
- package/resources/local/gsap-skills/skills/gsap-performance/SKILL.md +79 -0
- package/resources/local/gsap-skills/skills/gsap-plugins/SKILL.md +433 -0
- package/resources/local/gsap-skills/skills/gsap-react/SKILL.md +136 -0
- package/resources/local/gsap-skills/skills/gsap-scrolltrigger/SKILL.md +296 -0
- package/resources/local/gsap-skills/skills/gsap-timeline/SKILL.md +107 -0
- package/resources/local/gsap-skills/skills/gsap-utils/SKILL.md +284 -0
- package/resources/local/gsap-skills/skills/llms.txt +39 -0
- package/resources/local/hermes-agent-core/AGENTS.md +1132 -0
- package/resources/local/hermes-agent-core/LICENSE +21 -0
- package/resources/local/hermes-agent-core/README.md +215 -0
- package/resources/local/hermes-agent-core/docs/2026-05-07-s6-overlay-dynamic-subagent-gateways.md +434 -0
- package/resources/local/hermes-agent-core/hermes-already-has-routines.md +160 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/DESCRIPTION.md +3 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/claude-code/SKILL.md +745 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/codex/SKILL.md +130 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/hermes-agent/SKILL.md +1021 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/kanban-codex-lane/SKILL.md +277 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/kanban-codex-lane/templates/pmb-codex-lane-prompt.md +57 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/opencode/SKILL.md +219 -0
- package/resources/local/hermes-agent-core/skills/github/DESCRIPTION.md +3 -0
- package/resources/local/hermes-agent-core/skills/github/codebase-inspection/SKILL.md +116 -0
- package/resources/local/hermes-agent-core/skills/github/github-auth/SKILL.md +247 -0
- package/resources/local/hermes-agent-core/skills/github/github-auth/scripts/gh-env.sh +66 -0
- package/resources/local/hermes-agent-core/skills/github/github-code-review/SKILL.md +481 -0
- package/resources/local/hermes-agent-core/skills/github/github-code-review/references/review-output-template.md +74 -0
- package/resources/local/hermes-agent-core/skills/github/github-issues/SKILL.md +370 -0
- package/resources/local/hermes-agent-core/skills/github/github-issues/templates/bug-report.md +35 -0
- package/resources/local/hermes-agent-core/skills/github/github-issues/templates/feature-request.md +31 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/SKILL.md +367 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/references/ci-troubleshooting.md +183 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/references/conventional-commits.md +71 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/templates/pr-body-bugfix.md +35 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/templates/pr-body-feature.md +33 -0
- package/resources/local/hermes-agent-core/skills/github/github-repo-management/SKILL.md +516 -0
- package/resources/local/hermes-agent-core/skills/github/github-repo-management/references/github-api-cheatsheet.md +161 -0
- package/resources/local/hermes-agent-core/skills/mcp/DESCRIPTION.md +3 -0
- package/resources/local/hermes-agent-core/skills/mcp/native-mcp/SKILL.md +357 -0
- package/resources/local/hermes-agent-core/skills/software-development/debugging-hermes-tui-commands/SKILL.md +152 -0
- package/resources/local/hermes-agent-core/skills/software-development/hermes-agent-skill-authoring/SKILL.md +165 -0
- package/resources/local/hermes-agent-core/skills/software-development/hermes-s6-container-supervision/SKILL.md +176 -0
- package/resources/local/hermes-agent-core/skills/software-development/node-inspect-debugger/SKILL.md +319 -0
- package/resources/local/hermes-agent-core/skills/software-development/plan/SKILL.md +58 -0
- package/resources/local/hermes-agent-core/skills/software-development/python-debugpy/SKILL.md +375 -0
- package/resources/local/hermes-agent-core/skills/software-development/requesting-code-review/SKILL.md +280 -0
- package/resources/local/hermes-agent-core/skills/software-development/spike/SKILL.md +197 -0
- package/resources/local/hermes-agent-core/skills/software-development/subagent-driven-development/SKILL.md +352 -0
- package/resources/local/hermes-agent-core/skills/software-development/subagent-driven-development/references/context-budget-discipline.md +53 -0
- package/resources/local/hermes-agent-core/skills/software-development/subagent-driven-development/references/gates-taxonomy.md +93 -0
- package/resources/local/hermes-agent-core/skills/software-development/systematic-debugging/SKILL.md +367 -0
- package/resources/local/hermes-agent-core/skills/software-development/test-driven-development/SKILL.md +343 -0
- package/resources/local/hermes-agent-core/skills/software-development/writing-plans/SKILL.md +297 -0
- package/resources/local/manifest.json +12 -0
- package/rule.md +2 -0
- package/scripts/audit-pack.js +5 -0
- package/scripts/smoke-browser.js +53 -0
- package/scripts/smoke-package.js +38 -4
- package/skill.md +36 -4
- package/skills/gsap.md +26 -0
- package/skills/hermes-agent.md +17 -0
- package/src/agent/agent-definitions.js +4 -4
- package/src/agent/runtime.js +179 -5
- package/src/agent/subagent-child.js +44 -0
- package/src/ai/capability-scorecard.js +193 -14
- package/src/ai/hermes-core.js +77 -0
- package/src/ai/model-capabilities.js +42 -2
- package/src/ai/prompts/system-prompt.js +18 -2
- package/src/ai/small-model-amplifier.js +35 -7
- package/src/ai/workflow-selector.js +22 -1
- package/src/cli/commands.js +46 -2
- package/src/cli/config.js +45 -6
- package/src/cli/context-loader.js +253 -9
- package/src/cli/conversation-format.js +5 -0
- package/src/cli/input-controller.js +79 -10
- package/src/cli/prompt-builder.js +47 -8
- package/src/cli/repl-commands.js +115 -0
- package/src/cli/repl.js +343 -85
- package/src/cli/slash-commands.js +4 -2
- package/src/cli/tui.js +133 -37
- package/src/mcp/client.js +54 -11
- package/src/mcp/presets.js +114 -0
- package/src/tools/agent.js +316 -25
- package/src/tools/executor.js +412 -12
- package/src/tools/permission.js +20 -17
- package/winter.d.ts +112 -10
package/src/tools/executor.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { promises as fs } from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import { exec, execFile } from 'child_process';
|
|
8
|
+
import { exec, execFile, spawn } from 'child_process';
|
|
9
9
|
import { promisify } from 'util';
|
|
10
10
|
import { diffLines } from 'diff';
|
|
11
11
|
import { withRetry } from './retry.js';
|
|
@@ -30,7 +30,14 @@ export class ToolExecutor {
|
|
|
30
30
|
constructor(repl) {
|
|
31
31
|
this.repl = repl;
|
|
32
32
|
this.projectPath = repl?.projectPath || process.cwd();
|
|
33
|
-
this.allowedCommands = [
|
|
33
|
+
this.allowedCommands = [
|
|
34
|
+
'git', 'npm', 'npx', 'node', 'python', 'code', 'pnpm', 'yarn', 'bun', 'pip', 'cargo', 'rustc',
|
|
35
|
+
'echo', 'printf', 'cat', 'ls', 'dir', 'type', 'copy', 'mkdir', 'get-childitem', 'set-content',
|
|
36
|
+
'get-content', 'test-path', 'get-date',
|
|
37
|
+
'ping', 'test-connection', 'curl', 'wget', 'iwr', 'irm', 'invoke-webrequest', 'invoke-restmethod',
|
|
38
|
+
'nslookup', 'resolve-dnsname', 'tracert', 'traceroute', 'pathping', 'dig', 'ipconfig', 'ifconfig',
|
|
39
|
+
'ip', 'netstat', 'speedtest', 'speedtest-cli', 'measure-command',
|
|
40
|
+
];
|
|
34
41
|
this.blockedPatterns = [
|
|
35
42
|
{ pattern: /\brm\s+-[^\n;|&]*r[^\n;|&]*f\b|\brm\s+-[^\n;|&]*f[^\n;|&]*r\b/i, label: 'rm -rf' },
|
|
36
43
|
{ pattern: /\bremove-item\b[^\n;|&]*(?:-recurse\b[^\n;|&]*-force\b|-force\b[^\n;|&]*-recurse\b)/i, label: 'Remove-Item -Recurse -Force' },
|
|
@@ -207,11 +214,11 @@ export class ToolExecutor {
|
|
|
207
214
|
{
|
|
208
215
|
type: 'function',
|
|
209
216
|
name: 'MCP',
|
|
210
|
-
description: 'Call a configured MCP server tool by name. Use for external integrations and IDE-like tools. Discover available MCP tools via the MCP tool with server name and tool=list. Also, tools from MCP servers are exposed with mcp__<server>__<tool> naming for direct IDE integration (e.g. mcp__vscode__open_file).',
|
|
217
|
+
description: 'Call a configured MCP server tool by name. Use for external integrations and IDE-like tools. Discover available MCP tools via the MCP tool with server name and tool=list. For live Chrome debugging, use server chrome-devtools with tools such as new_page, navigate_page, take_snapshot, take_screenshot, evaluate_script, list_console_messages, list_network_requests, and performance trace tools. Also, tools from MCP servers are exposed with mcp__<server>__<tool> naming for direct IDE integration (e.g. mcp__vscode__open_file).',
|
|
211
218
|
parameters: {
|
|
212
219
|
type: 'object',
|
|
213
220
|
properties: {
|
|
214
|
-
server: { type: 'string', description: 'Configured MCP server name (e.g. vscode)' },
|
|
221
|
+
server: { type: 'string', description: 'Configured MCP server name (e.g. vscode, chrome-devtools)' },
|
|
215
222
|
tool: { type: 'string', description: 'MCP tool name, or set to "list" to discover all tools from a server' },
|
|
216
223
|
arguments: { type: 'object', description: 'Tool arguments' },
|
|
217
224
|
},
|
|
@@ -334,18 +341,68 @@ export class ToolExecutor {
|
|
|
334
341
|
{
|
|
335
342
|
type: 'function',
|
|
336
343
|
name: 'Agent',
|
|
337
|
-
description: 'Spawn
|
|
344
|
+
description: 'Spawn and await a real isolated subagent conversation with scoped tools, timeout, execution, verification, and result passing.',
|
|
338
345
|
parameters: {
|
|
339
346
|
type: 'object',
|
|
340
347
|
properties: {
|
|
341
348
|
task: { type: 'string', description: 'Task description for the agent' },
|
|
349
|
+
role: { type: 'string', description: 'Agent role/profile, e.g. general, debug, review, design, research, swe' },
|
|
350
|
+
context: { type: 'string', description: 'Parent context to pass into the subagent' },
|
|
351
|
+
tools: { type: 'array', items: { type: 'string' }, description: 'Optional restricted tool allowlist for this subagent' },
|
|
352
|
+
process_isolation: { type: 'boolean', description: 'Run subagent in a child process. Defaults true in live REPL.' },
|
|
342
353
|
max_steps: { type: 'number', description: 'Maximum execution steps (default: 10, max: 25)' },
|
|
354
|
+
timeout_ms: { type: 'number', description: 'Timeout for the subagent run' },
|
|
343
355
|
provider: { type: 'string', description: 'AI provider to use for this agent' },
|
|
344
356
|
cwd: { type: 'string', description: 'Working directory' },
|
|
345
357
|
},
|
|
346
358
|
required: ['task']
|
|
347
359
|
}
|
|
348
360
|
},
|
|
361
|
+
{
|
|
362
|
+
type: 'function',
|
|
363
|
+
name: 'DelegateTask',
|
|
364
|
+
description: 'Delegate a task to a real subagent and wait for summary, changed files, tool evidence, usage, and errors. Alias of Agent with clearer delegation semantics.',
|
|
365
|
+
parameters: {
|
|
366
|
+
type: 'object',
|
|
367
|
+
properties: {
|
|
368
|
+
goal: { type: 'string', description: 'Goal or task for the delegated subagent' },
|
|
369
|
+
task: { type: 'string', description: 'Task description for the delegated subagent' },
|
|
370
|
+
role: { type: 'string', description: 'Agent role/profile' },
|
|
371
|
+
context: { type: 'string', description: 'Parent context to pass into the subagent' },
|
|
372
|
+
tools: { type: 'array', items: { type: 'string' }, description: 'Restricted tool allowlist' },
|
|
373
|
+
process_isolation: { type: 'boolean', description: 'Run subagent in a child process. Defaults true in live REPL.' },
|
|
374
|
+
max_steps: { type: 'number', description: 'Maximum execution steps' },
|
|
375
|
+
timeout_ms: { type: 'number', description: 'Timeout for the delegated run' },
|
|
376
|
+
provider: { type: 'string', description: 'AI provider to use for this subagent' },
|
|
377
|
+
cwd: { type: 'string', description: 'Working directory' },
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
type: 'function',
|
|
383
|
+
name: 'ParallelAgent',
|
|
384
|
+
description: 'Run multiple real delegated subagents concurrently and aggregate their summaries, changed files, tool evidence, and failures.',
|
|
385
|
+
parameters: {
|
|
386
|
+
type: 'object',
|
|
387
|
+
properties: {
|
|
388
|
+
tasks: {
|
|
389
|
+
type: 'array',
|
|
390
|
+
items: {
|
|
391
|
+
type: 'object',
|
|
392
|
+
properties: {
|
|
393
|
+
goal: { type: 'string' },
|
|
394
|
+
task: { type: 'string' },
|
|
395
|
+
role: { type: 'string' },
|
|
396
|
+
tools: { type: 'array', items: { type: 'string' } },
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
concurrency: { type: 'number', description: 'Maximum subagents to run at once, capped at 6' },
|
|
401
|
+
timeout_ms: { type: 'number', description: 'Timeout per subagent' },
|
|
402
|
+
},
|
|
403
|
+
required: ['tasks']
|
|
404
|
+
}
|
|
405
|
+
},
|
|
349
406
|
{
|
|
350
407
|
type: 'function',
|
|
351
408
|
name: 'InsertText',
|
|
@@ -388,6 +445,37 @@ export class ToolExecutor {
|
|
|
388
445
|
required: ['url']
|
|
389
446
|
}
|
|
390
447
|
},
|
|
448
|
+
{
|
|
449
|
+
type: 'function',
|
|
450
|
+
name: 'VisibleBrowser',
|
|
451
|
+
description: 'Launch and control a real visible Chrome/Chromium browser with Puppeteer. Use when the user asks Winter to actually click, type, navigate, inspect, or screenshot a page and chrome-devtools MCP is unavailable. Actions: open, navigate, click, type, evaluate, screenshot, snapshot.',
|
|
452
|
+
parameters: {
|
|
453
|
+
type: 'object',
|
|
454
|
+
properties: {
|
|
455
|
+
url: { type: 'string', description: 'URL to open or navigate to. Defaults to about:blank.' },
|
|
456
|
+
action: { type: 'string', description: 'open, navigate, click, type, evaluate, screenshot, or snapshot' },
|
|
457
|
+
selector: { type: 'string', description: 'CSS selector for click/type/snapshot targeting' },
|
|
458
|
+
text: { type: 'string', description: 'Text to type for action=type' },
|
|
459
|
+
script: { type: 'string', description: 'JavaScript expression/function body for action=evaluate' },
|
|
460
|
+
wait_for: { type: 'string', description: 'Optional CSS selector to wait for after navigation/action' },
|
|
461
|
+
screenshot_path: { type: 'string', description: 'Optional output PNG path for action=screenshot' },
|
|
462
|
+
keep_open: { type: 'boolean', description: 'Leave the browser open after action. Default false.' },
|
|
463
|
+
browser: { type: 'string', description: 'chrome or chromium. Defaults to chrome.' }
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
type: 'function',
|
|
469
|
+
name: 'OpenBrowser',
|
|
470
|
+
description: 'Open Chrome or the default browser visibly for the user. Use this for requests like "mở chrome", "open Chrome", or "open this URL in browser". Do not use Bash/Start-Process for this.',
|
|
471
|
+
parameters: {
|
|
472
|
+
type: 'object',
|
|
473
|
+
properties: {
|
|
474
|
+
url: { type: 'string', description: 'URL to open. Defaults to about:blank.' },
|
|
475
|
+
browser: { type: 'string', description: 'chrome or default. Defaults to chrome.' },
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
},
|
|
391
479
|
{
|
|
392
480
|
type: 'function',
|
|
393
481
|
name: 'WebFetch',
|
|
@@ -532,6 +620,10 @@ export class ToolExecutor {
|
|
|
532
620
|
return await this.parallelExecute(input.tools ?? input.calls ?? [], { cwd });
|
|
533
621
|
case 'BrowserDebug':
|
|
534
622
|
return await this.browserDebug(input.url ?? input.uri, input.action);
|
|
623
|
+
case 'VisibleBrowser':
|
|
624
|
+
return await this.visibleBrowser(input, cwd);
|
|
625
|
+
case 'OpenBrowser':
|
|
626
|
+
return await this.openBrowser(input.url ?? input.uri ?? input.href, input.browser);
|
|
535
627
|
case 'WebFetch':
|
|
536
628
|
return await this.webFetch(input.url ?? input.uri ?? input.href, input.prompt ?? input.query ?? input.extract);
|
|
537
629
|
case 'WebSearch':
|
|
@@ -549,7 +641,35 @@ export class ToolExecutor {
|
|
|
549
641
|
case 'AskUserQuestion':
|
|
550
642
|
return await this.interactiveTool.askQuestion(input.questions ?? input.question);
|
|
551
643
|
case 'Agent':
|
|
552
|
-
return await this.agentTool.run(input.task, {
|
|
644
|
+
return await this.agentTool.run(input.task, {
|
|
645
|
+
role: input.role ?? input.agent,
|
|
646
|
+
context: input.context,
|
|
647
|
+
tools: input.tools,
|
|
648
|
+
processIsolation: input.process_isolation ?? input.processIsolation,
|
|
649
|
+
maxSteps: input.max_steps ?? input.maxSteps,
|
|
650
|
+
timeoutMs: input.timeout_ms ?? input.timeoutMs,
|
|
651
|
+
provider: input.provider,
|
|
652
|
+
cwd: input.cwd,
|
|
653
|
+
});
|
|
654
|
+
case 'DelegateTask':
|
|
655
|
+
return await this.agentTool.run(input.task ?? input.goal, {
|
|
656
|
+
role: input.role ?? input.agent,
|
|
657
|
+
context: input.context,
|
|
658
|
+
tools: input.tools,
|
|
659
|
+
processIsolation: input.process_isolation ?? input.processIsolation,
|
|
660
|
+
maxSteps: input.max_steps ?? input.maxSteps,
|
|
661
|
+
timeoutMs: input.timeout_ms ?? input.timeoutMs,
|
|
662
|
+
provider: input.provider,
|
|
663
|
+
cwd: input.cwd,
|
|
664
|
+
});
|
|
665
|
+
case 'ParallelAgent':
|
|
666
|
+
return await this.agentTool.runParallel(input.tasks, {
|
|
667
|
+
concurrency: input.concurrency,
|
|
668
|
+
timeoutMs: input.timeout_ms ?? input.timeoutMs,
|
|
669
|
+
processIsolation: input.process_isolation ?? input.processIsolation,
|
|
670
|
+
provider: input.provider,
|
|
671
|
+
cwd: input.cwd,
|
|
672
|
+
});
|
|
553
673
|
case 'InsertText':
|
|
554
674
|
return await this.insertTextTool.insert(this.resolveInputPath(input.file_path ?? input.path ?? input.file, cwd), input.insert_text ?? input.text ?? input.content, input);
|
|
555
675
|
case 'StrReplaceAll':
|
|
@@ -572,7 +692,7 @@ export class ToolExecutor {
|
|
|
572
692
|
return {
|
|
573
693
|
success: false,
|
|
574
694
|
error: `Unknown tool: ${toolName}`,
|
|
575
|
-
availableTools: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep', 'TaskCreate', 'TaskUpdate', 'TaskList', 'MCP', 'Parallel', 'BrowserDebug', 'WebFetch', 'WebSearch', 'WebArchive', 'HtmlEffectiveness', 'NotebookRead', 'NotebookEdit', 'TodoWrite', 'TodoList', 'ScheduleWakeup', 'AskUserQuestion', 'Agent', 'InsertText', 'StrReplaceAll'],
|
|
695
|
+
availableTools: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep', 'TaskCreate', 'TaskUpdate', 'TaskList', 'MCP', 'Parallel', 'OpenBrowser', 'VisibleBrowser', 'BrowserDebug', 'WebFetch', 'WebSearch', 'WebArchive', 'HtmlEffectiveness', 'NotebookRead', 'NotebookEdit', 'TodoWrite', 'TodoList', 'ScheduleWakeup', 'AskUserQuestion', 'Agent', 'DelegateTask', 'ParallelAgent', 'InsertText', 'StrReplaceAll'],
|
|
576
696
|
recovery: 'Call one of the available tools. For file writes use Write with { "file_path": "...", "content": "..." }. For shell commands use Bash with { "command": "..." }.',
|
|
577
697
|
};
|
|
578
698
|
}
|
|
@@ -716,15 +836,36 @@ export class ToolExecutor {
|
|
|
716
836
|
return { success: true, coerced: true, args: { notebook_path: notebookPath, cell_id: cellId, new_source: newSource } };
|
|
717
837
|
}
|
|
718
838
|
|
|
719
|
-
if (toolName === 'WebFetch' || toolName === 'WebArchive' || toolName === 'BrowserDebug') {
|
|
839
|
+
if (toolName === 'WebFetch' || toolName === 'WebArchive' || toolName === 'BrowserDebug' || toolName === 'VisibleBrowser') {
|
|
720
840
|
const url = pick('url', 'uri', 'href');
|
|
721
|
-
if (!url) {
|
|
841
|
+
if (!url && toolName !== 'VisibleBrowser') {
|
|
722
842
|
return { success: false, error: 'url is required', recovery: `Example: ${toolName} {"url":"https://example.com"}` };
|
|
723
843
|
}
|
|
724
844
|
const next = { ...args, url };
|
|
725
845
|
return { success: true, coerced: true, args: next };
|
|
726
846
|
}
|
|
727
847
|
|
|
848
|
+
if (toolName === 'OpenBrowser') {
|
|
849
|
+
const url = pick('url', 'uri', 'href') || 'about:blank';
|
|
850
|
+
const browser = pick('browser', 'app') || 'chrome';
|
|
851
|
+
return { success: true, coerced: true, args: { ...args, url, browser } };
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
if (toolName === 'Agent' || toolName === 'DelegateTask') {
|
|
855
|
+
const task = pick('task', 'goal', 'prompt', 'description');
|
|
856
|
+
if (!task) {
|
|
857
|
+
return { success: false, error: 'task is required', recovery: `Example: ${toolName} {"task":"Inspect the failing test and report the first blocker"}` };
|
|
858
|
+
}
|
|
859
|
+
return { success: true, coerced: true, args: { ...args, task } };
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (toolName === 'ParallelAgent') {
|
|
863
|
+
if (!Array.isArray(args.tasks) || args.tasks.length === 0) {
|
|
864
|
+
return { success: false, error: 'tasks array is required', recovery: 'Example: ParallelAgent {"tasks":[{"goal":"Review auth"},{"goal":"Inspect tests"}]}' };
|
|
865
|
+
}
|
|
866
|
+
return { success: true, coerced: true, args };
|
|
867
|
+
}
|
|
868
|
+
|
|
728
869
|
if (toolName === 'WebSearch') {
|
|
729
870
|
const query = pick('query', 'q', 'search', 'search_query', 'searchQuery');
|
|
730
871
|
if (!query) {
|
|
@@ -752,9 +893,20 @@ export class ToolExecutor {
|
|
|
752
893
|
const baseCommand = this.getBaseCommand(text);
|
|
753
894
|
if (!baseCommand) return { success: true };
|
|
754
895
|
|
|
896
|
+
if (/^(?:get-command|start-process|start|open|xdg-open)$/i.test(baseCommand) && /\b(chrome|browser|google chrome)\b/i.test(text)) {
|
|
897
|
+
return {
|
|
898
|
+
success: false,
|
|
899
|
+
error: `Use OpenBrowser instead of shell command for browser launch: ${baseCommand}`,
|
|
900
|
+
recovery: 'Call OpenBrowser {"browser":"chrome","url":"about:blank"} for "mở chrome", or OpenBrowser {"browser":"chrome","url":"https://example.com"} for a specific URL.',
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
|
|
755
904
|
const cfg = await this.getRuntimeConfig();
|
|
756
905
|
const permissionCommands = cfg.permissions?.allowlist?.commands || [];
|
|
757
906
|
const sandbox = cfg.sandbox || {};
|
|
907
|
+
if (sandbox.enabled === false) {
|
|
908
|
+
return { success: true };
|
|
909
|
+
}
|
|
758
910
|
const sandboxCommands = sandbox.enabled === false ? [] : (sandbox.allowedCommands || this.allowedCommands);
|
|
759
911
|
const allowed = new Set([
|
|
760
912
|
...this.allowedCommands,
|
|
@@ -814,14 +966,19 @@ export class ToolExecutor {
|
|
|
814
966
|
const summary = {
|
|
815
967
|
success: result.success !== false,
|
|
816
968
|
};
|
|
817
|
-
for (const key of ['error', 'path', 'count', 'exitCode', 'server', 'tool']) {
|
|
969
|
+
for (const key of ['error', 'path', 'count', 'exitCode', 'server', 'tool', 'url', 'title', 'action', 'visible', 'controlled', 'keptOpen', 'agentId', 'role', 'status', 'summary', 'processIsolated', 'childPid']) {
|
|
818
970
|
if (result[key] !== undefined) summary[key] = result[key];
|
|
819
971
|
}
|
|
820
972
|
if (typeof result.stdout === 'string') summary.stdout = result.stdout.slice(0, 1000);
|
|
821
973
|
if (typeof result.stderr === 'string') summary.stderr = result.stderr.slice(0, 1000);
|
|
822
974
|
if (typeof result.content === 'string') summary.content = result.content.slice(0, 1000);
|
|
975
|
+
if (typeof result.domSnippet === 'string') summary.domSnippet = result.domSnippet.slice(0, 1000);
|
|
976
|
+
if (Array.isArray(result.consoleErrors)) summary.consoleErrors = result.consoleErrors.slice(-10);
|
|
977
|
+
if (Array.isArray(result.networkErrors)) summary.networkErrors = result.networkErrors.slice(-10);
|
|
823
978
|
if (Array.isArray(result.files)) summary.files = result.files.slice(0, 20);
|
|
824
979
|
if (Array.isArray(result.matches)) summary.matches = result.matches.slice(0, 20);
|
|
980
|
+
if (Array.isArray(result.changedFiles)) summary.changedFiles = result.changedFiles.slice(0, 20);
|
|
981
|
+
if (Array.isArray(result.toolSummaries)) summary.toolSummaries = result.toolSummaries.slice(0, 20);
|
|
825
982
|
return summary;
|
|
826
983
|
}
|
|
827
984
|
|
|
@@ -879,10 +1036,32 @@ export class ToolExecutor {
|
|
|
879
1036
|
|
|
880
1037
|
const retryPolicy = await this.getRetryPolicy();
|
|
881
1038
|
try {
|
|
882
|
-
const result = await withRetry(() => client.callTool(toolName, argumentsObject),
|
|
1039
|
+
const result = await withRetry(() => client.callTool(toolName, argumentsObject), {
|
|
1040
|
+
...retryPolicy,
|
|
1041
|
+
retryable: error => error?.code !== 'MCP_REQUEST_TIMEOUT',
|
|
1042
|
+
});
|
|
883
1043
|
return { success: true, server: serverName, tool: toolName, result };
|
|
884
1044
|
} catch (error) {
|
|
885
|
-
|
|
1045
|
+
if (error?.code === 'MCP_REQUEST_TIMEOUT') {
|
|
1046
|
+
await client.close();
|
|
1047
|
+
this.mcpClients.delete(serverName);
|
|
1048
|
+
return {
|
|
1049
|
+
success: false,
|
|
1050
|
+
error: error.message,
|
|
1051
|
+
code: error.code,
|
|
1052
|
+
server: serverName,
|
|
1053
|
+
tool: toolName,
|
|
1054
|
+
timeoutMs: error.timeoutMs,
|
|
1055
|
+
recovery: `MCP server "${serverName}" did not answer "${toolName}" before the timeout. Do not blindly retry the same call. First run /mcp tools ${serverName} to confirm the server responds, narrow the tool arguments, or increase this server's requestTimeoutMs if the tool is expected to be slow.`,
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
return {
|
|
1059
|
+
success: false,
|
|
1060
|
+
error: error.message,
|
|
1061
|
+
server: serverName,
|
|
1062
|
+
tool: toolName,
|
|
1063
|
+
recovery: `MCP tool "${toolName}" failed on server "${serverName}". Check the server stderr/logs and compare the arguments with /mcp tools ${serverName}; retry only after correcting the failing input.`,
|
|
1064
|
+
};
|
|
886
1065
|
}
|
|
887
1066
|
}
|
|
888
1067
|
|
|
@@ -974,6 +1153,20 @@ export class ToolExecutor {
|
|
|
974
1153
|
searchweb: 'WebSearch',
|
|
975
1154
|
internetsearch: 'WebSearch',
|
|
976
1155
|
googlesearch: 'WebSearch',
|
|
1156
|
+
openbrowser: 'OpenBrowser',
|
|
1157
|
+
open_browser: 'OpenBrowser',
|
|
1158
|
+
browseropen: 'OpenBrowser',
|
|
1159
|
+
openchrome: 'OpenBrowser',
|
|
1160
|
+
launchchrome: 'OpenBrowser',
|
|
1161
|
+
visiblebrowser: 'VisibleBrowser',
|
|
1162
|
+
visible_browser: 'VisibleBrowser',
|
|
1163
|
+
browsercontrol: 'VisibleBrowser',
|
|
1164
|
+
browser_control: 'VisibleBrowser',
|
|
1165
|
+
browseraction: 'VisibleBrowser',
|
|
1166
|
+
chromecontrol: 'VisibleBrowser',
|
|
1167
|
+
chrome_control: 'VisibleBrowser',
|
|
1168
|
+
clickbrowser: 'VisibleBrowser',
|
|
1169
|
+
browserclick: 'VisibleBrowser',
|
|
977
1170
|
browserdebug: 'BrowserDebug',
|
|
978
1171
|
browser: 'BrowserDebug',
|
|
979
1172
|
browserinspect: 'BrowserDebug',
|
|
@@ -1016,6 +1209,13 @@ export class ToolExecutor {
|
|
|
1016
1209
|
agent: 'Agent',
|
|
1017
1210
|
subagent: 'Agent',
|
|
1018
1211
|
agentrun: 'Agent',
|
|
1212
|
+
delegatetask: 'DelegateTask',
|
|
1213
|
+
delegate: 'DelegateTask',
|
|
1214
|
+
taskdelegate: 'DelegateTask',
|
|
1215
|
+
spawnagent: 'DelegateTask',
|
|
1216
|
+
parallelagent: 'ParallelAgent',
|
|
1217
|
+
multiagent: 'ParallelAgent',
|
|
1218
|
+
parallelagents: 'ParallelAgent',
|
|
1019
1219
|
};
|
|
1020
1220
|
return aliases[normalized] || raw;
|
|
1021
1221
|
}
|
|
@@ -1045,6 +1245,7 @@ export class ToolExecutor {
|
|
|
1045
1245
|
case 'WebFetch':
|
|
1046
1246
|
case 'WebArchive':
|
|
1047
1247
|
case 'BrowserDebug':
|
|
1248
|
+
case 'VisibleBrowser':
|
|
1048
1249
|
return { url: value };
|
|
1049
1250
|
case 'TaskCreate':
|
|
1050
1251
|
case 'TodoWrite':
|
|
@@ -1052,6 +1253,7 @@ export class ToolExecutor {
|
|
|
1052
1253
|
case 'ScheduleWakeup':
|
|
1053
1254
|
return { prompt: value };
|
|
1054
1255
|
case 'Agent':
|
|
1256
|
+
case 'DelegateTask':
|
|
1055
1257
|
return { task: value };
|
|
1056
1258
|
default:
|
|
1057
1259
|
return { input: value };
|
|
@@ -2076,6 +2278,204 @@ export class ToolExecutor {
|
|
|
2076
2278
|
}
|
|
2077
2279
|
}
|
|
2078
2280
|
|
|
2281
|
+
async visibleBrowser(input = {}, cwd = this.projectPath) {
|
|
2282
|
+
const url = String(input.url ?? input.uri ?? input.href ?? 'about:blank');
|
|
2283
|
+
const action = String(input.action || (url ? 'open' : 'snapshot')).toLowerCase();
|
|
2284
|
+
const selector = input.selector || input.css || input.target;
|
|
2285
|
+
const text = input.text ?? input.value ?? input.input;
|
|
2286
|
+
const script = input.script ?? input.js ?? input.code;
|
|
2287
|
+
const waitFor = input.wait_for ?? input.waitFor;
|
|
2288
|
+
const keepOpen = input.keep_open === true || input.keepOpen === true;
|
|
2289
|
+
const browserName = String(input.browser || 'chrome').toLowerCase();
|
|
2290
|
+
const screenshotPath = input.screenshot_path ?? input.screenshotPath;
|
|
2291
|
+
|
|
2292
|
+
let puppeteer;
|
|
2293
|
+
try {
|
|
2294
|
+
puppeteer = (await import('puppeteer')).default;
|
|
2295
|
+
} catch {
|
|
2296
|
+
return {
|
|
2297
|
+
success: false,
|
|
2298
|
+
error: 'Thư viện puppeteer chưa được cài đặt.',
|
|
2299
|
+
recovery: 'Run Bash {"command":"npm install puppeteer --no-save"} or configure chrome-devtools MCP, then retry VisibleBrowser.',
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
let browser;
|
|
2304
|
+
try {
|
|
2305
|
+
const launchOptions = {
|
|
2306
|
+
headless: false,
|
|
2307
|
+
defaultViewport: null,
|
|
2308
|
+
args: ['--start-maximized'],
|
|
2309
|
+
};
|
|
2310
|
+
if (browserName === 'chrome') {
|
|
2311
|
+
launchOptions.channel = 'chrome';
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
browser = await puppeteer.launch(launchOptions).catch(async error => {
|
|
2315
|
+
if (launchOptions.channel) {
|
|
2316
|
+
const fallbackOptions = { ...launchOptions };
|
|
2317
|
+
delete fallbackOptions.channel;
|
|
2318
|
+
return await puppeteer.launch(fallbackOptions);
|
|
2319
|
+
}
|
|
2320
|
+
throw error;
|
|
2321
|
+
});
|
|
2322
|
+
|
|
2323
|
+
const page = await browser.newPage();
|
|
2324
|
+
const consoleLogs = [];
|
|
2325
|
+
const networkErrors = [];
|
|
2326
|
+
page.on('console', msg => {
|
|
2327
|
+
if (['error', 'warning'].includes(msg.type())) {
|
|
2328
|
+
consoleLogs.push(`[${msg.type()}] ${msg.text()}`);
|
|
2329
|
+
}
|
|
2330
|
+
});
|
|
2331
|
+
page.on('requestfailed', request => {
|
|
2332
|
+
networkErrors.push(`${request.method()} ${request.url()} - ${request.failure()?.errorText}`);
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2335
|
+
if (url && url !== 'about:blank') {
|
|
2336
|
+
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
2337
|
+
} else {
|
|
2338
|
+
await page.goto('about:blank', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
let actionResult = null;
|
|
2342
|
+
if (waitFor) {
|
|
2343
|
+
await page.waitForSelector(waitFor, { timeout: 15000 });
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
if (action === 'navigate' || action === 'open') {
|
|
2347
|
+
actionResult = { navigated: page.url() };
|
|
2348
|
+
} else if (action === 'click') {
|
|
2349
|
+
if (!selector) throw new Error('selector is required for VisibleBrowser action=click');
|
|
2350
|
+
await page.waitForSelector(selector, { timeout: 15000 });
|
|
2351
|
+
await page.click(selector);
|
|
2352
|
+
actionResult = { clicked: selector };
|
|
2353
|
+
} else if (action === 'type' || action === 'fill') {
|
|
2354
|
+
if (!selector) throw new Error('selector is required for VisibleBrowser action=type');
|
|
2355
|
+
await page.waitForSelector(selector, { timeout: 15000 });
|
|
2356
|
+
await page.click(selector, { clickCount: 3 });
|
|
2357
|
+
await page.type(selector, String(text ?? ''), { delay: 10 });
|
|
2358
|
+
actionResult = { typed: selector, text: String(text ?? '') };
|
|
2359
|
+
} else if (action === 'evaluate' || action === 'eval') {
|
|
2360
|
+
if (!script) throw new Error('script is required for VisibleBrowser action=evaluate');
|
|
2361
|
+
actionResult = await page.evaluate(source => (0, eval)(source), String(script));
|
|
2362
|
+
} else if (action === 'screenshot') {
|
|
2363
|
+
const outputPath = screenshotPath
|
|
2364
|
+
? this.resolveInputPath(screenshotPath, cwd)
|
|
2365
|
+
: path.join(cwd, '.winter', `visible-browser-${Date.now()}.png`);
|
|
2366
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
2367
|
+
await page.screenshot({ path: outputPath, fullPage: true });
|
|
2368
|
+
actionResult = { screenshotPath: outputPath };
|
|
2369
|
+
} else if (action === 'snapshot') {
|
|
2370
|
+
actionResult = selector
|
|
2371
|
+
? await page.$eval(selector, el => el.outerHTML.slice(0, 3000))
|
|
2372
|
+
: await page.evaluate(() => document.body?.innerHTML?.slice(0, 3000) || '');
|
|
2373
|
+
} else {
|
|
2374
|
+
throw new Error(`Unsupported VisibleBrowser action: ${action}`);
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
const title = await page.title().catch(() => '');
|
|
2378
|
+
const currentUrl = page.url();
|
|
2379
|
+
const domSnippet = await page.evaluate(() => document.body?.innerText?.slice(0, 1600) || '').catch(() => '');
|
|
2380
|
+
|
|
2381
|
+
if (!keepOpen) {
|
|
2382
|
+
await browser.close();
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
return {
|
|
2386
|
+
success: true,
|
|
2387
|
+
visible: true,
|
|
2388
|
+
controlled: true,
|
|
2389
|
+
browser: browserName,
|
|
2390
|
+
action,
|
|
2391
|
+
url: currentUrl,
|
|
2392
|
+
title,
|
|
2393
|
+
actionResult,
|
|
2394
|
+
consoleErrors: consoleLogs.slice(-10),
|
|
2395
|
+
networkErrors: networkErrors.slice(-10),
|
|
2396
|
+
domSnippet,
|
|
2397
|
+
keptOpen: keepOpen,
|
|
2398
|
+
};
|
|
2399
|
+
} catch (error) {
|
|
2400
|
+
if (browser && !keepOpen) {
|
|
2401
|
+
await browser.close().catch(() => {});
|
|
2402
|
+
}
|
|
2403
|
+
return {
|
|
2404
|
+
success: false,
|
|
2405
|
+
error: error.message,
|
|
2406
|
+
url,
|
|
2407
|
+
action,
|
|
2408
|
+
recovery: 'Use a valid URL and CSS selector. If Chrome launch fails, install Chrome or retry with browser:"chromium"; for persistent live control use chrome-devtools MCP.',
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
buildBrowserLaunchCommand(url = 'about:blank', browser = 'chrome', platform = process.platform) {
|
|
2414
|
+
const targetUrl = String(url || 'about:blank');
|
|
2415
|
+
const targetBrowser = String(browser || 'chrome').toLowerCase();
|
|
2416
|
+
|
|
2417
|
+
if (platform === 'win32') {
|
|
2418
|
+
if (targetBrowser === 'default') {
|
|
2419
|
+
return { command: 'cmd', args: ['/c', 'start', '', targetUrl] };
|
|
2420
|
+
}
|
|
2421
|
+
return { command: 'cmd', args: ['/c', 'start', '', 'chrome', targetUrl] };
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
if (platform === 'darwin') {
|
|
2425
|
+
if (targetBrowser === 'default') {
|
|
2426
|
+
return { command: 'open', args: [targetUrl] };
|
|
2427
|
+
}
|
|
2428
|
+
return { command: 'open', args: ['-a', 'Google Chrome', targetUrl] };
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
if (targetBrowser === 'default') {
|
|
2432
|
+
return { command: 'xdg-open', args: [targetUrl] };
|
|
2433
|
+
}
|
|
2434
|
+
return { command: 'google-chrome', args: [targetUrl] };
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
async openBrowser(url = 'about:blank', browser = 'chrome') {
|
|
2438
|
+
const targetUrl = String(url || 'about:blank');
|
|
2439
|
+
const targetBrowser = String(browser || 'chrome').toLowerCase();
|
|
2440
|
+
const launch = this.buildBrowserLaunchCommand(targetUrl, targetBrowser);
|
|
2441
|
+
|
|
2442
|
+
return await new Promise(resolve => {
|
|
2443
|
+
let child;
|
|
2444
|
+
try {
|
|
2445
|
+
child = spawn(launch.command, launch.args, {
|
|
2446
|
+
detached: true,
|
|
2447
|
+
stdio: 'ignore',
|
|
2448
|
+
windowsHide: false,
|
|
2449
|
+
});
|
|
2450
|
+
} catch (error) {
|
|
2451
|
+
resolve({
|
|
2452
|
+
success: false,
|
|
2453
|
+
error: error.message,
|
|
2454
|
+
recovery: 'Install Chrome or retry with OpenBrowser {"browser":"default","url":"about:blank"}.',
|
|
2455
|
+
});
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
child.once('error', error => {
|
|
2460
|
+
resolve({
|
|
2461
|
+
success: false,
|
|
2462
|
+
error: error.message,
|
|
2463
|
+
recovery: 'Install Chrome or retry with OpenBrowser {"browser":"default","url":"about:blank"}.',
|
|
2464
|
+
});
|
|
2465
|
+
});
|
|
2466
|
+
child.once('spawn', () => {
|
|
2467
|
+
child.unref();
|
|
2468
|
+
resolve({
|
|
2469
|
+
success: true,
|
|
2470
|
+
browser: targetBrowser,
|
|
2471
|
+
url: targetUrl,
|
|
2472
|
+
command: launch.command,
|
|
2473
|
+
args: launch.args,
|
|
2474
|
+
});
|
|
2475
|
+
});
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2079
2479
|
async htmlEffectivenessCompile(input, cwd) {
|
|
2080
2480
|
const inputPath = this.resolveInputPath(input.input_path ?? input.inputPath ?? input.input, cwd);
|
|
2081
2481
|
const outputPath = this.resolveInputPath(input.output_path ?? input.outputPath ?? input.output, cwd);
|
package/src/tools/permission.js
CHANGED
|
@@ -13,18 +13,20 @@ export class PermissionManager {
|
|
|
13
13
|
const permissions = cfg.permissions || {};
|
|
14
14
|
const allowlist = permissions.allowlist || {};
|
|
15
15
|
|
|
16
|
-
return {
|
|
17
|
-
promptByDefault: permissions.promptByDefault !== false,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
return {
|
|
17
|
+
promptByDefault: permissions.promptByDefault !== false,
|
|
18
|
+
fullAccess: cfg.sandbox?.enabled === false,
|
|
19
|
+
allowedTools: new Set([...(allowlist.tools || []), ...DEFAULT_ALLOWED]),
|
|
20
|
+
allowedCommands: new Set(allowlist.commands || []),
|
|
21
|
+
allowedServers: new Set(allowlist.mcpServers || []),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async isAllowedTool(toolName) {
|
|
26
|
+
const policy = await this.getPolicy();
|
|
27
|
+
if (policy.fullAccess) return true;
|
|
28
|
+
return policy.allowedTools.has(toolName);
|
|
29
|
+
}
|
|
28
30
|
|
|
29
31
|
async shouldPromptForTool(toolName) {
|
|
30
32
|
const policy = await this.getPolicy();
|
|
@@ -37,11 +39,12 @@ export class PermissionManager {
|
|
|
37
39
|
return await this.shouldPromptForTool(toolName);
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
async isMcpServerAllowed(serverName) {
|
|
41
|
-
if (!serverName) return false;
|
|
42
|
-
const policy = await this.getPolicy();
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
async isMcpServerAllowed(serverName) {
|
|
43
|
+
if (!serverName) return false;
|
|
44
|
+
const policy = await this.getPolicy();
|
|
45
|
+
if (policy.fullAccess) return true;
|
|
46
|
+
return policy.allowedServers.has(serverName);
|
|
47
|
+
}
|
|
45
48
|
|
|
46
49
|
async allowTool(toolName) {
|
|
47
50
|
if (!this.config?.load || !this.config?.save) return;
|