winter-super-cli 2026.6.26 → 2026.6.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.
- package/CHANGELOG.md +28 -5
- package/README.md +66 -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 +206 -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 +16 -2
- package/src/ai/small-model-amplifier.js +35 -7
- package/src/ai/workflow-selector.js +22 -1
- package/src/cli/commands.js +21 -1
- package/src/cli/config.js +42 -4
- 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 +45 -8
- package/src/cli/repl-commands.js +123 -2
- package/src/cli/repl.js +183 -87
- package/src/cli/slash-commands.js +3 -1
- package/src/cli/tui.js +133 -37
- package/src/mcp/client.js +46 -5
- package/src/tools/agent.js +316 -25
- package/src/tools/executor.js +314 -11
- package/src/tools/permission.js +20 -17
- package/winter.d.ts +112 -10
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
import { ToolExecutor } from '../src/tools/executor.js';
|
|
5
|
+
|
|
6
|
+
const html = [
|
|
7
|
+
'<html>',
|
|
8
|
+
'<head><title>Winter Visible Smoke</title></head>',
|
|
9
|
+
'<body>',
|
|
10
|
+
'<button id="ok">OK</button>',
|
|
11
|
+
'<script>',
|
|
12
|
+
'document.querySelector("#ok").addEventListener("click",()=>document.body.dataset.clicked="yes")',
|
|
13
|
+
'</script>',
|
|
14
|
+
'</body>',
|
|
15
|
+
'</html>',
|
|
16
|
+
].join('');
|
|
17
|
+
|
|
18
|
+
const tools = new ToolExecutor({
|
|
19
|
+
projectPath: process.cwd(),
|
|
20
|
+
config: { async load() { return {}; } },
|
|
21
|
+
session: null,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const browserDebug = await tools.execute('BrowserDebug', {
|
|
25
|
+
url: `data:text/html,${encodeURIComponent(html)}`,
|
|
26
|
+
action: 'document.title',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (browserDebug.success === false) {
|
|
30
|
+
console.error(`BrowserDebug smoke failed: ${browserDebug.error}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const visibleBrowser = await tools.execute('VisibleBrowser', {
|
|
35
|
+
url: `data:text/html,${encodeURIComponent(html)}`,
|
|
36
|
+
action: 'click',
|
|
37
|
+
selector: '#ok',
|
|
38
|
+
browser: 'chrome',
|
|
39
|
+
keep_open: false,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (visibleBrowser.success === false) {
|
|
43
|
+
console.error(`VisibleBrowser smoke failed: ${visibleBrowser.error}`);
|
|
44
|
+
console.error(visibleBrowser.recovery || '');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (visibleBrowser.visible !== true || visibleBrowser.controlled !== true || visibleBrowser.title !== 'Winter Visible Smoke') {
|
|
49
|
+
console.error(`VisibleBrowser smoke returned incomplete evidence: ${JSON.stringify(visibleBrowser, null, 2)}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log('Browser smoke passed: BrowserDebug and VisibleBrowser controlled a real Chromium page.');
|
package/scripts/smoke-package.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
4
|
+
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
5
6
|
import path from 'node:path';
|
|
6
7
|
import process from 'node:process';
|
|
7
|
-
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
|
|
10
10
|
const root = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
|
|
@@ -14,7 +14,7 @@ const MAX_BUFFER = 50 * 1024 * 1024;
|
|
|
14
14
|
|
|
15
15
|
function run(command, args, options = {}) {
|
|
16
16
|
const label = [path.basename(command), ...args].join(' ');
|
|
17
|
-
console.log(`\n
|
|
17
|
+
console.log(`\n> ${label}`);
|
|
18
18
|
return execFileSync(command, args, {
|
|
19
19
|
cwd: root,
|
|
20
20
|
encoding: 'utf8',
|
|
@@ -27,7 +27,7 @@ function run(command, args, options = {}) {
|
|
|
27
27
|
function runNpm(args) {
|
|
28
28
|
if (!isWin) return run('npm', args);
|
|
29
29
|
const commandLine = ['npm', ...args].join(' ');
|
|
30
|
-
console.log(`\n
|
|
30
|
+
console.log(`\n> ${commandLine}`);
|
|
31
31
|
return execFileSync(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', commandLine], {
|
|
32
32
|
cwd: root,
|
|
33
33
|
encoding: 'utf8',
|
|
@@ -42,6 +42,12 @@ function assertIncludes(output, expected, label) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
function assertExists(filePath, label) {
|
|
46
|
+
if (!existsSync(filePath)) {
|
|
47
|
+
throw new Error(`${label} is missing from installed package: ${filePath}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
45
51
|
function printOutput(output) {
|
|
46
52
|
const text = String(output || '').trim();
|
|
47
53
|
if (text) console.log(text);
|
|
@@ -76,6 +82,34 @@ try {
|
|
|
76
82
|
}
|
|
77
83
|
console.log(`\nPackage dry-run passed: ${pack.filename} (${pack.files?.length || 0} files)`);
|
|
78
84
|
|
|
85
|
+
const tempRoot = mkdtempSync(path.join(tmpdir(), 'winter-package-smoke-'));
|
|
86
|
+
try {
|
|
87
|
+
const realPackOutput = runNpm(['pack', '--json', '--pack-destination', tempRoot]);
|
|
88
|
+
const [realPack] = JSON.parse(realPackOutput);
|
|
89
|
+
const tarball = path.join(tempRoot, realPack.filename);
|
|
90
|
+
assertExists(tarball, 'packed tarball');
|
|
91
|
+
|
|
92
|
+
const installRoot = path.join(tempRoot, 'install');
|
|
93
|
+
printOutput(runNpm(['install', '--prefix', installRoot, tarball, '--ignore-scripts']));
|
|
94
|
+
const installedRoot = path.join(installRoot, 'node_modules', 'winter-super-cli');
|
|
95
|
+
const installedBin = path.join(installedRoot, 'bin', 'winter.js');
|
|
96
|
+
|
|
97
|
+
assertExists(installedBin, 'installed CLI entrypoint');
|
|
98
|
+
assertExists(path.join(installedRoot, 'resources', 'local', 'gsap-skills'), 'gsap skills resource');
|
|
99
|
+
assertExists(path.join(installedRoot, 'resources', 'local', 'hermes-agent-core'), 'Hermes core resource');
|
|
100
|
+
assertExists(path.join(installedRoot, 'resources', 'local', 'page-agent'), 'Page Agent resource');
|
|
101
|
+
assertExists(path.join(installedRoot, 'resources', 'local', 'ecc'), 'ECC resource');
|
|
102
|
+
assertExists(path.join(installedRoot, 'skills', 'coding.md'), 'packaged coding skill');
|
|
103
|
+
assertExists(path.join(installedRoot, 'rules', 'default.md'), 'packaged rule');
|
|
104
|
+
|
|
105
|
+
const installedVersion = run(nodeCmd, [installedBin, '--version'], { cwd: installRoot });
|
|
106
|
+
printOutput(installedVersion);
|
|
107
|
+
assertIncludes(installedVersion, 'Winter CLI v', 'installed package version smoke');
|
|
108
|
+
console.log('\nInstalled package smoke passed.');
|
|
109
|
+
} finally {
|
|
110
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
|
|
79
113
|
console.log('\nSmoke package gate passed.');
|
|
80
114
|
} catch (error) {
|
|
81
115
|
console.error(`\nSmoke package gate failed: ${error.message}`);
|
package/skill.md
CHANGED
|
@@ -16,23 +16,55 @@ File này định nghĩa cách Winter chọn và áp dụng skill. Không chỉ
|
|
|
16
16
|
- **security**: Protect secrets, validate inputs, avoid unsafe shell/file operations.
|
|
17
17
|
- **performance**: Measure or reason from the hot path before optimizing.
|
|
18
18
|
|
|
19
|
-
## Available Local Skills (
|
|
19
|
+
## Available Local Skills (48)
|
|
20
|
+
- claude-code
|
|
21
|
+
- codebase-inspection
|
|
22
|
+
- codex
|
|
20
23
|
- codex-primary-runtime
|
|
21
24
|
- coding
|
|
22
|
-
- coding.md
|
|
23
25
|
- debug
|
|
24
|
-
-
|
|
26
|
+
- debugging-hermes-tui-commands
|
|
27
|
+
- DESCRIPTION
|
|
25
28
|
- design
|
|
29
|
+
- github-auth
|
|
30
|
+
- github-code-review
|
|
31
|
+
- github-issues
|
|
32
|
+
- github-pr-workflow
|
|
33
|
+
- github-repo-management
|
|
34
|
+
- gsap
|
|
35
|
+
- gsap-core
|
|
36
|
+
- gsap-frameworks
|
|
37
|
+
- gsap-performance
|
|
38
|
+
- gsap-plugins
|
|
39
|
+
- gsap-react
|
|
40
|
+
- gsap-scrolltrigger
|
|
41
|
+
- gsap-timeline
|
|
42
|
+
- gsap-utils
|
|
43
|
+
- hermes-agent
|
|
44
|
+
- hermes-agent-skill-authoring
|
|
45
|
+
- hermes-s6-container-supervision
|
|
46
|
+
- kanban-codex-lane
|
|
26
47
|
- learned
|
|
48
|
+
- llms.txt
|
|
49
|
+
- native-mcp
|
|
50
|
+
- node-inspect-debugger
|
|
51
|
+
- opencode
|
|
27
52
|
- performance
|
|
53
|
+
- plan
|
|
54
|
+
- python-debugpy
|
|
28
55
|
- refactor
|
|
56
|
+
- requesting-code-review
|
|
29
57
|
- security
|
|
30
58
|
- skill-creator
|
|
59
|
+
- spike
|
|
60
|
+
- subagent-driven-development
|
|
61
|
+
- systematic-debugging
|
|
31
62
|
- test
|
|
32
|
-
- test
|
|
63
|
+
- test-driven-development
|
|
33
64
|
- vercel-react-best-practices
|
|
34
65
|
- vibefigma
|
|
35
66
|
- web-design-guidelines
|
|
67
|
+
- writing-plans
|
|
36
68
|
|
|
37
69
|
## When To Apply
|
|
38
70
|
- Code change: coding + test, thêm debug nếu có lỗi cụ thể.
|
package/skills/gsap.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# GSAP Animation
|
|
2
|
+
|
|
3
|
+
Winter packaged skill for GSAP animation work.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Use this skill when the user asks for JavaScript animation, React/Vue/Svelte animation, scroll-driven animation, timelines, motion paths, SVG animation, Draggable, Flip, SplitText, ScrollTrigger, or asks which animation library to use.
|
|
8
|
+
|
|
9
|
+
Before implementing non-trivial animation, inspect the bundled GSAP resource index at `resources/local/gsap-skills/skills/llms.txt`, then read the relevant `SKILL.md` under `resources/local/gsap-skills/skills/`.
|
|
10
|
+
|
|
11
|
+
## Prompts
|
|
12
|
+
|
|
13
|
+
- Build a GSAP animation using the relevant GSAP skill resource first
|
|
14
|
+
- Add ScrollTrigger with cleanup and refresh behavior
|
|
15
|
+
- Implement React GSAP with `@gsap/react`, scoped refs, and proper cleanup
|
|
16
|
+
- Optimize animation performance using transform and opacity properties
|
|
17
|
+
|
|
18
|
+
## Rules
|
|
19
|
+
|
|
20
|
+
- Prefer GSAP for JavaScript animation when the user has not chosen another animation library.
|
|
21
|
+
- Install GSAP from the public `gsap` npm package; do not require Club GSAP, private registry, auth tokens, or `.npmrc`.
|
|
22
|
+
- Register plugins once before use.
|
|
23
|
+
- Prefer timelines for sequenced animation instead of chained delays.
|
|
24
|
+
- For React, prefer `@gsap/react` and scoped `useGSAP`; otherwise use `gsap.context()` and revert cleanup.
|
|
25
|
+
- For ScrollTrigger, scope triggers, clean up on unmount, and call `ScrollTrigger.refresh()` after layout changes when needed.
|
|
26
|
+
- Prefer transforms and `autoAlpha` over layout-heavy properties for smooth animation.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Hermes Agent Core
|
|
2
|
+
|
|
3
|
+
Use when a task involves Winter core behavior, agent loops, tools, skills, memory, subagents, TUI, gateways, scheduled automation, or making small models act more capable.
|
|
4
|
+
|
|
5
|
+
## Operating Rules
|
|
6
|
+
|
|
7
|
+
- Apply Hermes-style closed learning loops: after difficult work, identify reusable procedures and failure modes.
|
|
8
|
+
- Prefer skill lifecycle thinking: create, update, or recommend a skill when a workflow repeats.
|
|
9
|
+
- Use session search/compression patterns: preserve high-signal decisions and verification evidence, not long transcripts.
|
|
10
|
+
- Delegate independent workstreams through subagents or parallel tools when the task can be split safely.
|
|
11
|
+
- Treat MCP/tools as a gateway: allowlist, diagnose, timeout, retry carefully, and require concrete tool-result evidence.
|
|
12
|
+
- For UI/TUI work, keep rendering, command registry, tool progress, interrupts, model state, and session state as explicit separable surfaces.
|
|
13
|
+
- For scheduled/background tasks, define trigger, injected context, verification command, delivery path, and failure handling.
|
|
14
|
+
|
|
15
|
+
## Local Reference
|
|
16
|
+
|
|
17
|
+
Read `resources/local/hermes-agent-core/AGENTS.md` and the matching `resources/local/hermes-agent-core/skills/**/SKILL.md` file before changing non-trivial Winter agent/core behavior.
|
|
@@ -3,13 +3,13 @@ import { pathToFileURL } from 'url';
|
|
|
3
3
|
import { promises as fs } from 'fs';
|
|
4
4
|
|
|
5
5
|
const DEFAULT_TOOL_SETS = {
|
|
6
|
-
general: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep', 'OpenBrowser', 'BrowserDebug', 'WebFetch', 'WebSearch', 'Parallel', 'Agent'],
|
|
6
|
+
general: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep', 'OpenBrowser', 'VisibleBrowser', 'BrowserDebug', 'WebFetch', 'WebSearch', 'Parallel', 'Agent', 'DelegateTask'],
|
|
7
7
|
plan: ['Read', 'Grep', 'Glob', 'WebFetch', 'WebSearch', 'Parallel'],
|
|
8
8
|
review: ['Read', 'Grep', 'Glob', 'Bash', 'WebFetch'],
|
|
9
|
-
debug: ['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob', 'OpenBrowser', 'BrowserDebug', 'WebFetch', 'Parallel'],
|
|
10
|
-
design: ['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob', 'OpenBrowser', 'BrowserDebug', 'WebFetch'],
|
|
9
|
+
debug: ['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob', 'OpenBrowser', 'VisibleBrowser', 'BrowserDebug', 'WebFetch', 'Parallel'],
|
|
10
|
+
design: ['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob', 'OpenBrowser', 'VisibleBrowser', 'BrowserDebug', 'WebFetch'],
|
|
11
11
|
research: ['Read', 'Grep', 'Glob', 'WebFetch', 'WebSearch', 'Parallel'],
|
|
12
|
-
swe: ['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob', 'OpenBrowser', 'BrowserDebug', 'Parallel'],
|
|
12
|
+
swe: ['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob', 'OpenBrowser', 'VisibleBrowser', 'BrowserDebug', 'Parallel'],
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const BUILTIN_AGENTS = [
|
package/src/agent/runtime.js
CHANGED
|
@@ -23,12 +23,21 @@ export class AgentRuntime {
|
|
|
23
23
|
let reachedToolLimit = true;
|
|
24
24
|
let usedTools = false;
|
|
25
25
|
let usedMutatingTools = false;
|
|
26
|
+
let autoVerified = false;
|
|
27
|
+
let autoVerificationPassed = false;
|
|
28
|
+
let autoVerificationFailures = 0;
|
|
26
29
|
const toolSummaries = [];
|
|
30
|
+
const executedTools = [];
|
|
31
|
+
const changedFiles = new Set();
|
|
27
32
|
const totalUsage = {};
|
|
28
33
|
const toolSignatureHistory = [];
|
|
34
|
+
const allowedToolNames = Array.isArray(tools) && tools.length > 0
|
|
35
|
+
? new Set(tools.map(tool => tool.name || tool.function?.name).filter(Boolean).map(name => repl.tools.normalizeToolName(name)))
|
|
36
|
+
: null;
|
|
29
37
|
const executionProfile = repl.selectExecutionProfile(messages, { enableTools: true });
|
|
30
38
|
const requireToolEvidence = repl.actionRequiresTools(messages);
|
|
31
39
|
let noToolActionRetries = 0;
|
|
40
|
+
let unfinishedActionRetries = 0;
|
|
32
41
|
const sessionContext = repl.session?.getContext?.() || {};
|
|
33
42
|
const profile = sessionContext.workflowProfile || 'general';
|
|
34
43
|
const depth = /deep/i.test(profile) ? 'deep' : 'standard';
|
|
@@ -57,15 +66,12 @@ export class AgentRuntime {
|
|
|
57
66
|
toolPromptOnly: forceTextToolFallback,
|
|
58
67
|
requireToolEvidence: requireToolEvidence && !usedTools,
|
|
59
68
|
usedMutatingTools: usedMutatingTools,
|
|
69
|
+
deferFinalContent: this.shouldVerifyBeforeFinal(messages, usedMutatingTools, autoVerificationPassed),
|
|
60
70
|
}, startedAt, totalUsage);
|
|
61
71
|
|
|
62
72
|
const assistantMsg = turn.assistantMsg || {};
|
|
63
73
|
const toolCalls = turn.toolCalls || [];
|
|
64
74
|
|
|
65
|
-
if (turn.finalContent && toolCalls.length === 0) {
|
|
66
|
-
finalContent = turn.finalContent;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
75
|
if (toolCalls.length === 0) {
|
|
70
76
|
if (turn.finishReason === 'tool_evidence_required') {
|
|
71
77
|
noToolActionRetries++;
|
|
@@ -90,6 +96,80 @@ export class AgentRuntime {
|
|
|
90
96
|
finalContent = '';
|
|
91
97
|
continue;
|
|
92
98
|
}
|
|
99
|
+
if (turn.finalContent && this.shouldVerifyBeforeFinal(messages, usedMutatingTools, autoVerificationPassed)) {
|
|
100
|
+
autoVerified = true;
|
|
101
|
+
const verification = await this.runVerificationTools({
|
|
102
|
+
messages,
|
|
103
|
+
toolSummaries,
|
|
104
|
+
startedAt,
|
|
105
|
+
totalUsage,
|
|
106
|
+
});
|
|
107
|
+
autoVerificationPassed = verification.passed;
|
|
108
|
+
|
|
109
|
+
if (!verification.passed) {
|
|
110
|
+
autoVerificationFailures++;
|
|
111
|
+
if (autoVerificationFailures >= 3) {
|
|
112
|
+
messages.push({
|
|
113
|
+
role: 'user',
|
|
114
|
+
content: [
|
|
115
|
+
'Verification is still failing after multiple repair attempts.',
|
|
116
|
+
'Stop making unsupported success claims. Give the user a concise status with the exact failing commands and remaining blocker.',
|
|
117
|
+
].join('\n'),
|
|
118
|
+
});
|
|
119
|
+
finalContent = await repl.requestFinalAnswer(messages, toolSummaries, startedAt, totalUsage);
|
|
120
|
+
reachedToolLimit = false;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
messages.push({
|
|
125
|
+
role: 'assistant',
|
|
126
|
+
content: assistantMsg.content || '',
|
|
127
|
+
});
|
|
128
|
+
messages.push({
|
|
129
|
+
role: 'user',
|
|
130
|
+
content: this.buildVerificationRepairPrompt(verification, autoVerificationFailures),
|
|
131
|
+
});
|
|
132
|
+
finalContent = '';
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
messages.push({
|
|
137
|
+
role: 'assistant',
|
|
138
|
+
content: assistantMsg.content || '',
|
|
139
|
+
});
|
|
140
|
+
finalContent = await repl.requestFinalAnswer(messages, toolSummaries, startedAt, totalUsage);
|
|
141
|
+
reachedToolLimit = false;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
if (
|
|
145
|
+
turn.finalContent &&
|
|
146
|
+
requireToolEvidence &&
|
|
147
|
+
usedTools &&
|
|
148
|
+
!usedMutatingTools &&
|
|
149
|
+
repl.responseIndicatesUnfinishedAction?.(turn.finalContent)
|
|
150
|
+
) {
|
|
151
|
+
unfinishedActionRetries++;
|
|
152
|
+
if (unfinishedActionRetries > 3) {
|
|
153
|
+
finalContent = 'Chưa hoàn thành: model chỉ trả lời trạng thái sau khi inspect, chưa thực hiện thay đổi. Winter đã dừng để tránh báo tiến độ giả.';
|
|
154
|
+
console.log(`\n${colors.yellow}${finalContent}${colors.reset}\n`);
|
|
155
|
+
reachedToolLimit = false;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
messages.push({
|
|
159
|
+
role: 'assistant',
|
|
160
|
+
content: assistantMsg.content || '',
|
|
161
|
+
});
|
|
162
|
+
messages.push({
|
|
163
|
+
role: 'user',
|
|
164
|
+
content: repl.buildUnfinishedActionCorrection(messages, turn.finalContent),
|
|
165
|
+
});
|
|
166
|
+
forceTextToolFallback = true;
|
|
167
|
+
finalContent = '';
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (turn.finalContent) {
|
|
171
|
+
finalContent = turn.finalContent;
|
|
172
|
+
}
|
|
93
173
|
if (turn.finishReason === 'length') {
|
|
94
174
|
console.log(`\n${colors.yellow}ℹ Phản hồi bị cắt cụt do hết token. Đang tự động tiếp tục...${colors.reset}`);
|
|
95
175
|
messages.push({
|
|
@@ -136,6 +216,24 @@ export class AgentRuntime {
|
|
|
136
216
|
for (const tc of toolCalls) {
|
|
137
217
|
const { toolName, toolArgs } = tc;
|
|
138
218
|
const canonicalToolName = repl.tools.normalizeToolName(toolName);
|
|
219
|
+
if (allowedToolNames && !allowedToolNames.has(canonicalToolName)) {
|
|
220
|
+
const result = {
|
|
221
|
+
success: false,
|
|
222
|
+
error: `Tool ${canonicalToolName} is not allowed for this agent.`,
|
|
223
|
+
recovery: `Allowed tools: ${[...allowedToolNames].join(', ')}`,
|
|
224
|
+
};
|
|
225
|
+
const promptToolResult = await repl.buildPromptToolResultForModel(canonicalToolName, result);
|
|
226
|
+
messages.push({
|
|
227
|
+
role: 'tool',
|
|
228
|
+
tool_call_id: tc.id || `tool-${Date.now()}`,
|
|
229
|
+
content: JSON.stringify(promptToolResult),
|
|
230
|
+
});
|
|
231
|
+
const summary = repl.formatToolResultForConsole(canonicalToolName, result);
|
|
232
|
+
if (summary) {
|
|
233
|
+
toolSummaries.push(`${canonicalToolName}: ${summary}`);
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
139
237
|
if (getMutatingToolNames().has(canonicalToolName)) {
|
|
140
238
|
usedMutatingTools = true;
|
|
141
239
|
}
|
|
@@ -201,6 +299,15 @@ export class AgentRuntime {
|
|
|
201
299
|
: { success: false, error: 'Tool call is missing a tool name' };
|
|
202
300
|
if (repl.spinner) repl.spinner.stop();
|
|
203
301
|
}
|
|
302
|
+
executedTools.push({ tool: canonicalToolName, args: enrichedArgs, success: result?.success !== false });
|
|
303
|
+
if (getMutatingToolNames().has(canonicalToolName)) {
|
|
304
|
+
for (const key of ['file_path', 'filePath', 'path', 'file', 'notebook_path']) {
|
|
305
|
+
if (typeof enrichedArgs?.[key] === 'string' && enrichedArgs[key].trim()) {
|
|
306
|
+
changedFiles.add(enrichedArgs[key]);
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
204
311
|
const promptToolResult = await repl.buildPromptToolResultForModel(canonicalToolName, result);
|
|
205
312
|
messages.push({
|
|
206
313
|
role: 'tool',
|
|
@@ -254,6 +361,100 @@ export class AgentRuntime {
|
|
|
254
361
|
console.log(`\n${colors.yellow}${finalContent}${colors.reset}\n`);
|
|
255
362
|
}
|
|
256
363
|
|
|
257
|
-
return {
|
|
364
|
+
return {
|
|
365
|
+
finalContent,
|
|
366
|
+
usedTools,
|
|
367
|
+
usedMutatingTools,
|
|
368
|
+
autoVerified,
|
|
369
|
+
autoVerificationPassed,
|
|
370
|
+
toolSummaries,
|
|
371
|
+
executedTools,
|
|
372
|
+
changedFiles: [...changedFiles],
|
|
373
|
+
usage: totalUsage,
|
|
374
|
+
messages,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
shouldVerifyBeforeFinal(messages = [], usedMutatingTools = false, verificationPassed = false) {
|
|
379
|
+
if (!usedMutatingTools || verificationPassed) return false;
|
|
380
|
+
return this.repl.shouldAutoVerifyAfterTools?.(this.repl.getLatestUserText(messages), true) === true;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async runVerificationTools({ messages, toolSummaries, startedAt, totalUsage }) {
|
|
384
|
+
const repl = this.repl;
|
|
385
|
+
const commands = await repl.inferVerificationCommands?.(repl.getLatestUserText(messages));
|
|
386
|
+
const uniqueCommands = [...new Set((commands || []).filter(Boolean))].slice(0, 3);
|
|
387
|
+
if (uniqueCommands.length === 0) {
|
|
388
|
+
return { passed: true, details: [] };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (repl.spinner) repl.spinner.stop();
|
|
392
|
+
console.log(`\n${colors.cyan}=== Auto verification before final answer ===${colors.reset}`);
|
|
393
|
+
|
|
394
|
+
const details = [];
|
|
395
|
+
for (const command of uniqueCommands) {
|
|
396
|
+
if (repl.spinner) {
|
|
397
|
+
repl.spinner.update(`Verifying: ${command}`);
|
|
398
|
+
repl.spinner.start();
|
|
399
|
+
}
|
|
400
|
+
const result = await repl.tools.execute('Bash', { command }, { cwd: repl.projectPath });
|
|
401
|
+
if (repl.spinner) repl.spinner.stop();
|
|
402
|
+
|
|
403
|
+
const passed = result?.success !== false;
|
|
404
|
+
details.push({
|
|
405
|
+
cmd: command,
|
|
406
|
+
passed,
|
|
407
|
+
output: result?.stdout || result?.stderr || result?.error || '',
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
messages.push({
|
|
411
|
+
role: 'user',
|
|
412
|
+
content: [
|
|
413
|
+
'[Winter auto-verification tool result]',
|
|
414
|
+
`Tool: Bash`,
|
|
415
|
+
`Command: ${command}`,
|
|
416
|
+
`Status: ${passed ? 'passed' : 'failed'}`,
|
|
417
|
+
String(result?.stdout || result?.stderr || result?.error || '').slice(0, 6000),
|
|
418
|
+
].filter(Boolean).join('\n'),
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
const summary = repl.formatToolResultForConsole('Bash', result) || `${command}: ${passed ? 'passed' : 'failed'}`;
|
|
422
|
+
toolSummaries.push(`Bash: ${summary}`);
|
|
423
|
+
console.log(renderToolPanel({
|
|
424
|
+
toolName: '$ Bash',
|
|
425
|
+
summary,
|
|
426
|
+
success: passed,
|
|
427
|
+
colors,
|
|
428
|
+
title: 'Auto Verification',
|
|
429
|
+
}));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (startedAt && totalUsage) {
|
|
433
|
+
// Keep the parameters intentionally used: callers pass the same timing and
|
|
434
|
+
// usage objects as normal tool turns so future instrumentation can attach here.
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
passed: details.every(item => item.passed),
|
|
439
|
+
details,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
buildVerificationRepairPrompt(verification, failureCount = 1) {
|
|
444
|
+
const failures = (verification?.details || [])
|
|
445
|
+
.filter(item => !item.passed)
|
|
446
|
+
.map(item => [
|
|
447
|
+
`Command: ${item.cmd}`,
|
|
448
|
+
String(item.output || '').slice(0, 5000),
|
|
449
|
+
].join('\n'))
|
|
450
|
+
.join('\n\n---\n\n');
|
|
451
|
+
|
|
452
|
+
return [
|
|
453
|
+
`RUNTIME VERIFICATION FAILED before final answer (repair attempt ${failureCount}/3).`,
|
|
454
|
+
'',
|
|
455
|
+
failures || 'Verification failed, but no output was captured.',
|
|
456
|
+
'',
|
|
457
|
+
'Fix the first hard failure now with tools. Inspect the error path, patch the smallest root cause, and do not provide a final success answer until verification passes.',
|
|
458
|
+
].join('\n');
|
|
258
459
|
}
|
|
259
460
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { WinterREPL } from '../cli/repl.js';
|
|
2
|
+
import { AgentTool } from '../tools/agent.js';
|
|
3
|
+
|
|
4
|
+
async function runSubagent(message = {}) {
|
|
5
|
+
const options = message.options || {};
|
|
6
|
+
const repl = new WinterREPL({
|
|
7
|
+
projectPath: options.projectPath || options.cwd || process.cwd(),
|
|
8
|
+
sessionId: options.sessionId,
|
|
9
|
+
version: options.version,
|
|
10
|
+
});
|
|
11
|
+
repl.readlineClosed = true;
|
|
12
|
+
repl.running = true;
|
|
13
|
+
repl.showInputPrompt = () => {};
|
|
14
|
+
repl.closeInputBox = () => {};
|
|
15
|
+
repl.shouldPromptForToolPermission = async () => false;
|
|
16
|
+
|
|
17
|
+
await repl.ai.init?.();
|
|
18
|
+
const tool = new AgentTool(repl);
|
|
19
|
+
const result = await tool.executeSubagent(message.task, {
|
|
20
|
+
...options,
|
|
21
|
+
processIsolation: false,
|
|
22
|
+
process_isolation: false,
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
...result,
|
|
26
|
+
workspaceIsolated: options.workspaceIsolation === true,
|
|
27
|
+
workspacePath: options.projectPath,
|
|
28
|
+
parentProjectPath: options.parentProjectPath,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
process.on('message', async message => {
|
|
33
|
+
if (message?.type !== 'run') return;
|
|
34
|
+
try {
|
|
35
|
+
const result = await runSubagent(message);
|
|
36
|
+
process.send?.({ type: 'result', result });
|
|
37
|
+
} catch (error) {
|
|
38
|
+
process.send?.({
|
|
39
|
+
type: 'error',
|
|
40
|
+
error: error.message,
|
|
41
|
+
stack: error.stack,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|