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.
Files changed (123) hide show
  1. package/CHANGELOG.md +28 -5
  2. package/README.md +85 -0
  3. package/package.json +5 -1
  4. package/resources/local/gsap-skills/.claude-plugin/marketplace.json +20 -0
  5. package/resources/local/gsap-skills/.claude-plugin/plugin.json +6 -0
  6. package/resources/local/gsap-skills/.cursor-plugin/marketplace.json +13 -0
  7. package/resources/local/gsap-skills/.cursor-plugin/plugin.json +22 -0
  8. package/resources/local/gsap-skills/.github/copilot-instructions.md +17 -0
  9. package/resources/local/gsap-skills/.github/instructions/react.instructions.md +15 -0
  10. package/resources/local/gsap-skills/.github/instructions/scrolltrigger.instructions.md +18 -0
  11. package/resources/local/gsap-skills/AGENTS.md +27 -0
  12. package/resources/local/gsap-skills/CLAUDE.md +1 -0
  13. package/resources/local/gsap-skills/GEMINI.md +1 -0
  14. package/resources/local/gsap-skills/LICENSE +21 -0
  15. package/resources/local/gsap-skills/README.md +163 -0
  16. package/resources/local/gsap-skills/assets/gsap-green.svg +7 -0
  17. package/resources/local/gsap-skills/assets/gsap-icon-inverted.svg +15 -0
  18. package/resources/local/gsap-skills/assets/gsap-icon-square.svg +1 -0
  19. package/resources/local/gsap-skills/assets/gsap-white.svg +7 -0
  20. package/resources/local/gsap-skills/examples/README.md +29 -0
  21. package/resources/local/gsap-skills/examples/nuxt/app/app.vue +3 -0
  22. package/resources/local/gsap-skills/examples/nuxt/app/composables/useGSAP.ts +91 -0
  23. package/resources/local/gsap-skills/examples/nuxt/app/pages/index.vue +55 -0
  24. package/resources/local/gsap-skills/examples/nuxt/nuxt.config.ts +4 -0
  25. package/resources/local/gsap-skills/examples/nuxt/package.json +18 -0
  26. package/resources/local/gsap-skills/examples/react/App.jsx +46 -0
  27. package/resources/local/gsap-skills/examples/react/index.html +12 -0
  28. package/resources/local/gsap-skills/examples/react/main.jsx +9 -0
  29. package/resources/local/gsap-skills/examples/react/package.json +21 -0
  30. package/resources/local/gsap-skills/examples/react/vite.config.js +7 -0
  31. package/resources/local/gsap-skills/examples/vanilla/index.html +33 -0
  32. package/resources/local/gsap-skills/examples/vanilla/main.js +36 -0
  33. package/resources/local/gsap-skills/examples/vue/app.vue +47 -0
  34. package/resources/local/gsap-skills/examples/vue/index.html +15 -0
  35. package/resources/local/gsap-skills/examples/vue/main.js +9 -0
  36. package/resources/local/gsap-skills/examples/vue/package.json +19 -0
  37. package/resources/local/gsap-skills/examples/vue/vite.config.js +7 -0
  38. package/resources/local/gsap-skills/skills/gsap-core/SKILL.md +254 -0
  39. package/resources/local/gsap-skills/skills/gsap-frameworks/SKILL.md +266 -0
  40. package/resources/local/gsap-skills/skills/gsap-performance/SKILL.md +79 -0
  41. package/resources/local/gsap-skills/skills/gsap-plugins/SKILL.md +433 -0
  42. package/resources/local/gsap-skills/skills/gsap-react/SKILL.md +136 -0
  43. package/resources/local/gsap-skills/skills/gsap-scrolltrigger/SKILL.md +296 -0
  44. package/resources/local/gsap-skills/skills/gsap-timeline/SKILL.md +107 -0
  45. package/resources/local/gsap-skills/skills/gsap-utils/SKILL.md +284 -0
  46. package/resources/local/gsap-skills/skills/llms.txt +39 -0
  47. package/resources/local/hermes-agent-core/AGENTS.md +1132 -0
  48. package/resources/local/hermes-agent-core/LICENSE +21 -0
  49. package/resources/local/hermes-agent-core/README.md +215 -0
  50. package/resources/local/hermes-agent-core/docs/2026-05-07-s6-overlay-dynamic-subagent-gateways.md +434 -0
  51. package/resources/local/hermes-agent-core/hermes-already-has-routines.md +160 -0
  52. package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/DESCRIPTION.md +3 -0
  53. package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/claude-code/SKILL.md +745 -0
  54. package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/codex/SKILL.md +130 -0
  55. package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/hermes-agent/SKILL.md +1021 -0
  56. package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/kanban-codex-lane/SKILL.md +277 -0
  57. package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/kanban-codex-lane/templates/pmb-codex-lane-prompt.md +57 -0
  58. package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/opencode/SKILL.md +219 -0
  59. package/resources/local/hermes-agent-core/skills/github/DESCRIPTION.md +3 -0
  60. package/resources/local/hermes-agent-core/skills/github/codebase-inspection/SKILL.md +116 -0
  61. package/resources/local/hermes-agent-core/skills/github/github-auth/SKILL.md +247 -0
  62. package/resources/local/hermes-agent-core/skills/github/github-auth/scripts/gh-env.sh +66 -0
  63. package/resources/local/hermes-agent-core/skills/github/github-code-review/SKILL.md +481 -0
  64. package/resources/local/hermes-agent-core/skills/github/github-code-review/references/review-output-template.md +74 -0
  65. package/resources/local/hermes-agent-core/skills/github/github-issues/SKILL.md +370 -0
  66. package/resources/local/hermes-agent-core/skills/github/github-issues/templates/bug-report.md +35 -0
  67. package/resources/local/hermes-agent-core/skills/github/github-issues/templates/feature-request.md +31 -0
  68. package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/SKILL.md +367 -0
  69. package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/references/ci-troubleshooting.md +183 -0
  70. package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/references/conventional-commits.md +71 -0
  71. package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/templates/pr-body-bugfix.md +35 -0
  72. package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/templates/pr-body-feature.md +33 -0
  73. package/resources/local/hermes-agent-core/skills/github/github-repo-management/SKILL.md +516 -0
  74. package/resources/local/hermes-agent-core/skills/github/github-repo-management/references/github-api-cheatsheet.md +161 -0
  75. package/resources/local/hermes-agent-core/skills/mcp/DESCRIPTION.md +3 -0
  76. package/resources/local/hermes-agent-core/skills/mcp/native-mcp/SKILL.md +357 -0
  77. package/resources/local/hermes-agent-core/skills/software-development/debugging-hermes-tui-commands/SKILL.md +152 -0
  78. package/resources/local/hermes-agent-core/skills/software-development/hermes-agent-skill-authoring/SKILL.md +165 -0
  79. package/resources/local/hermes-agent-core/skills/software-development/hermes-s6-container-supervision/SKILL.md +176 -0
  80. package/resources/local/hermes-agent-core/skills/software-development/node-inspect-debugger/SKILL.md +319 -0
  81. package/resources/local/hermes-agent-core/skills/software-development/plan/SKILL.md +58 -0
  82. package/resources/local/hermes-agent-core/skills/software-development/python-debugpy/SKILL.md +375 -0
  83. package/resources/local/hermes-agent-core/skills/software-development/requesting-code-review/SKILL.md +280 -0
  84. package/resources/local/hermes-agent-core/skills/software-development/spike/SKILL.md +197 -0
  85. package/resources/local/hermes-agent-core/skills/software-development/subagent-driven-development/SKILL.md +352 -0
  86. package/resources/local/hermes-agent-core/skills/software-development/subagent-driven-development/references/context-budget-discipline.md +53 -0
  87. package/resources/local/hermes-agent-core/skills/software-development/subagent-driven-development/references/gates-taxonomy.md +93 -0
  88. package/resources/local/hermes-agent-core/skills/software-development/systematic-debugging/SKILL.md +367 -0
  89. package/resources/local/hermes-agent-core/skills/software-development/test-driven-development/SKILL.md +343 -0
  90. package/resources/local/hermes-agent-core/skills/software-development/writing-plans/SKILL.md +297 -0
  91. package/resources/local/manifest.json +12 -0
  92. package/rule.md +2 -0
  93. package/scripts/audit-pack.js +5 -0
  94. package/scripts/smoke-browser.js +53 -0
  95. package/scripts/smoke-package.js +38 -4
  96. package/skill.md +36 -4
  97. package/skills/gsap.md +26 -0
  98. package/skills/hermes-agent.md +17 -0
  99. package/src/agent/agent-definitions.js +4 -4
  100. package/src/agent/runtime.js +179 -5
  101. package/src/agent/subagent-child.js +44 -0
  102. package/src/ai/capability-scorecard.js +193 -14
  103. package/src/ai/hermes-core.js +77 -0
  104. package/src/ai/model-capabilities.js +42 -2
  105. package/src/ai/prompts/system-prompt.js +18 -2
  106. package/src/ai/small-model-amplifier.js +35 -7
  107. package/src/ai/workflow-selector.js +22 -1
  108. package/src/cli/commands.js +46 -2
  109. package/src/cli/config.js +45 -6
  110. package/src/cli/context-loader.js +253 -9
  111. package/src/cli/conversation-format.js +5 -0
  112. package/src/cli/input-controller.js +79 -10
  113. package/src/cli/prompt-builder.js +47 -8
  114. package/src/cli/repl-commands.js +115 -0
  115. package/src/cli/repl.js +343 -85
  116. package/src/cli/slash-commands.js +4 -2
  117. package/src/cli/tui.js +133 -37
  118. package/src/mcp/client.js +54 -11
  119. package/src/mcp/presets.js +114 -0
  120. package/src/tools/agent.js +316 -25
  121. package/src/tools/executor.js +412 -12
  122. package/src/tools/permission.js +20 -17
  123. 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.');
@@ -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 ${label}`);
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 ${commandLine}`);
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 (16)
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
- - debug.md
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.md
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', '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', 'BrowserDebug', 'WebFetch', 'Parallel'],
10
- design: ['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob', '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', 'BrowserDebug', 'Parallel'],
12
+ swe: ['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob', 'OpenBrowser', 'VisibleBrowser', 'BrowserDebug', 'Parallel'],
13
13
  };
14
14
 
15
15
  const BUILTIN_AGENTS = [
@@ -23,9 +23,17 @@ 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;
@@ -57,15 +65,12 @@ export class AgentRuntime {
57
65
  toolPromptOnly: forceTextToolFallback,
58
66
  requireToolEvidence: requireToolEvidence && !usedTools,
59
67
  usedMutatingTools: usedMutatingTools,
68
+ deferFinalContent: this.shouldVerifyBeforeFinal(messages, usedMutatingTools, autoVerificationPassed),
60
69
  }, startedAt, totalUsage);
61
70
 
62
71
  const assistantMsg = turn.assistantMsg || {};
63
72
  const toolCalls = turn.toolCalls || [];
64
73
 
65
- if (turn.finalContent && toolCalls.length === 0) {
66
- finalContent = turn.finalContent;
67
- }
68
-
69
74
  if (toolCalls.length === 0) {
70
75
  if (turn.finishReason === 'tool_evidence_required') {
71
76
  noToolActionRetries++;
@@ -90,6 +95,54 @@ export class AgentRuntime {
90
95
  finalContent = '';
91
96
  continue;
92
97
  }
98
+ if (turn.finalContent && this.shouldVerifyBeforeFinal(messages, usedMutatingTools, autoVerificationPassed)) {
99
+ autoVerified = true;
100
+ const verification = await this.runVerificationTools({
101
+ messages,
102
+ toolSummaries,
103
+ startedAt,
104
+ totalUsage,
105
+ });
106
+ autoVerificationPassed = verification.passed;
107
+
108
+ if (!verification.passed) {
109
+ autoVerificationFailures++;
110
+ if (autoVerificationFailures >= 3) {
111
+ messages.push({
112
+ role: 'user',
113
+ content: [
114
+ 'Verification is still failing after multiple repair attempts.',
115
+ 'Stop making unsupported success claims. Give the user a concise status with the exact failing commands and remaining blocker.',
116
+ ].join('\n'),
117
+ });
118
+ finalContent = await repl.requestFinalAnswer(messages, toolSummaries, startedAt, totalUsage);
119
+ reachedToolLimit = false;
120
+ break;
121
+ }
122
+
123
+ messages.push({
124
+ role: 'assistant',
125
+ content: assistantMsg.content || '',
126
+ });
127
+ messages.push({
128
+ role: 'user',
129
+ content: this.buildVerificationRepairPrompt(verification, autoVerificationFailures),
130
+ });
131
+ finalContent = '';
132
+ continue;
133
+ }
134
+
135
+ messages.push({
136
+ role: 'assistant',
137
+ content: assistantMsg.content || '',
138
+ });
139
+ finalContent = await repl.requestFinalAnswer(messages, toolSummaries, startedAt, totalUsage);
140
+ reachedToolLimit = false;
141
+ break;
142
+ }
143
+ if (turn.finalContent) {
144
+ finalContent = turn.finalContent;
145
+ }
93
146
  if (turn.finishReason === 'length') {
94
147
  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
148
  messages.push({
@@ -136,6 +189,24 @@ export class AgentRuntime {
136
189
  for (const tc of toolCalls) {
137
190
  const { toolName, toolArgs } = tc;
138
191
  const canonicalToolName = repl.tools.normalizeToolName(toolName);
192
+ if (allowedToolNames && !allowedToolNames.has(canonicalToolName)) {
193
+ const result = {
194
+ success: false,
195
+ error: `Tool ${canonicalToolName} is not allowed for this agent.`,
196
+ recovery: `Allowed tools: ${[...allowedToolNames].join(', ')}`,
197
+ };
198
+ const promptToolResult = await repl.buildPromptToolResultForModel(canonicalToolName, result);
199
+ messages.push({
200
+ role: 'tool',
201
+ tool_call_id: tc.id || `tool-${Date.now()}`,
202
+ content: JSON.stringify(promptToolResult),
203
+ });
204
+ const summary = repl.formatToolResultForConsole(canonicalToolName, result);
205
+ if (summary) {
206
+ toolSummaries.push(`${canonicalToolName}: ${summary}`);
207
+ }
208
+ continue;
209
+ }
139
210
  if (getMutatingToolNames().has(canonicalToolName)) {
140
211
  usedMutatingTools = true;
141
212
  }
@@ -201,6 +272,15 @@ export class AgentRuntime {
201
272
  : { success: false, error: 'Tool call is missing a tool name' };
202
273
  if (repl.spinner) repl.spinner.stop();
203
274
  }
275
+ executedTools.push({ tool: canonicalToolName, args: enrichedArgs, success: result?.success !== false });
276
+ if (getMutatingToolNames().has(canonicalToolName)) {
277
+ for (const key of ['file_path', 'filePath', 'path', 'file', 'notebook_path']) {
278
+ if (typeof enrichedArgs?.[key] === 'string' && enrichedArgs[key].trim()) {
279
+ changedFiles.add(enrichedArgs[key]);
280
+ break;
281
+ }
282
+ }
283
+ }
204
284
  const promptToolResult = await repl.buildPromptToolResultForModel(canonicalToolName, result);
205
285
  messages.push({
206
286
  role: 'tool',
@@ -254,6 +334,100 @@ export class AgentRuntime {
254
334
  console.log(`\n${colors.yellow}${finalContent}${colors.reset}\n`);
255
335
  }
256
336
 
257
- return { finalContent, usedTools, usedMutatingTools };
337
+ return {
338
+ finalContent,
339
+ usedTools,
340
+ usedMutatingTools,
341
+ autoVerified,
342
+ autoVerificationPassed,
343
+ toolSummaries,
344
+ executedTools,
345
+ changedFiles: [...changedFiles],
346
+ usage: totalUsage,
347
+ messages,
348
+ };
349
+ }
350
+
351
+ shouldVerifyBeforeFinal(messages = [], usedMutatingTools = false, verificationPassed = false) {
352
+ if (!usedMutatingTools || verificationPassed) return false;
353
+ return this.repl.shouldAutoVerifyAfterTools?.(this.repl.getLatestUserText(messages), true) === true;
354
+ }
355
+
356
+ async runVerificationTools({ messages, toolSummaries, startedAt, totalUsage }) {
357
+ const repl = this.repl;
358
+ const commands = await repl.inferVerificationCommands?.(repl.getLatestUserText(messages));
359
+ const uniqueCommands = [...new Set((commands || []).filter(Boolean))].slice(0, 3);
360
+ if (uniqueCommands.length === 0) {
361
+ return { passed: true, details: [] };
362
+ }
363
+
364
+ if (repl.spinner) repl.spinner.stop();
365
+ console.log(`\n${colors.cyan}=== Auto verification before final answer ===${colors.reset}`);
366
+
367
+ const details = [];
368
+ for (const command of uniqueCommands) {
369
+ if (repl.spinner) {
370
+ repl.spinner.update(`Verifying: ${command}`);
371
+ repl.spinner.start();
372
+ }
373
+ const result = await repl.tools.execute('Bash', { command }, { cwd: repl.projectPath });
374
+ if (repl.spinner) repl.spinner.stop();
375
+
376
+ const passed = result?.success !== false;
377
+ details.push({
378
+ cmd: command,
379
+ passed,
380
+ output: result?.stdout || result?.stderr || result?.error || '',
381
+ });
382
+
383
+ messages.push({
384
+ role: 'user',
385
+ content: [
386
+ '[Winter auto-verification tool result]',
387
+ `Tool: Bash`,
388
+ `Command: ${command}`,
389
+ `Status: ${passed ? 'passed' : 'failed'}`,
390
+ String(result?.stdout || result?.stderr || result?.error || '').slice(0, 6000),
391
+ ].filter(Boolean).join('\n'),
392
+ });
393
+
394
+ const summary = repl.formatToolResultForConsole('Bash', result) || `${command}: ${passed ? 'passed' : 'failed'}`;
395
+ toolSummaries.push(`Bash: ${summary}`);
396
+ console.log(renderToolPanel({
397
+ toolName: '$ Bash',
398
+ summary,
399
+ success: passed,
400
+ colors,
401
+ title: 'Auto Verification',
402
+ }));
403
+ }
404
+
405
+ if (startedAt && totalUsage) {
406
+ // Keep the parameters intentionally used: callers pass the same timing and
407
+ // usage objects as normal tool turns so future instrumentation can attach here.
408
+ }
409
+
410
+ return {
411
+ passed: details.every(item => item.passed),
412
+ details,
413
+ };
414
+ }
415
+
416
+ buildVerificationRepairPrompt(verification, failureCount = 1) {
417
+ const failures = (verification?.details || [])
418
+ .filter(item => !item.passed)
419
+ .map(item => [
420
+ `Command: ${item.cmd}`,
421
+ String(item.output || '').slice(0, 5000),
422
+ ].join('\n'))
423
+ .join('\n\n---\n\n');
424
+
425
+ return [
426
+ `RUNTIME VERIFICATION FAILED before final answer (repair attempt ${failureCount}/3).`,
427
+ '',
428
+ failures || 'Verification failed, but no output was captured.',
429
+ '',
430
+ '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.',
431
+ ].join('\n');
258
432
  }
259
433
  }
@@ -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
+ });