@poolzin/pool-bot 2026.3.13 → 2026.3.15
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 +87 -0
- package/dist/agents/checkpoint-manager.js +291 -0
- package/dist/agents/poolbot-tools.js +5 -0
- package/dist/agents/subagent-announce-reliability.js +160 -0
- package/dist/agents/tool-result-truncation.js +299 -0
- package/dist/agents/tools/nodes-file-tool.js +197 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/config-cli.js +60 -0
- package/dist/cron/cron-improvements.js +195 -0
- package/dist/discord/discord-improvements.js +167 -0
- package/dist/gateway/auth-rate-limit.js +19 -0
- package/dist/gateway/auth.js +41 -0
- package/dist/gateway/gateway-improvements.js +294 -0
- package/dist/gateway/node-command-policy.js +7 -2
- package/dist/infra/net/ssrf.js +15 -2
- package/dist/infra/shell-security.js +201 -0
- package/dist/memory/memory-improvements.js +239 -0
- package/dist/node-host/runner.js +146 -79
- package/dist/security/prototype-pollution.js +141 -0
- package/dist/security/webhook-security.js +253 -0
- package/dist/shared/net/ip.js +52 -1
- package/dist/slack/slack-improvements.js +225 -0
- package/dist/telegram/telegram-improvements.js +220 -0
- package/dist/ui-plugins/ui-plugins-improvements.js +191 -0
- package/docs/ANALISE_OPENCLAW_PROFISSIONAL.md +520 -0
- package/docs/competitive-analysis.md +421 -0
- package/docs/implementation-analysis.md +393 -0
- package/docs/plans/2026-03-11-file-operations-security-hardening.md +307 -0
- package/docs/plans/2026-03-11-integracao-projetos-poolbot.md +666 -0
- package/extensions/agency-agents/README.md +301 -0
- package/extensions/agency-agents/agents/CONTRIBUTING.md +353 -0
- package/extensions/agency-agents/agents/README.md +602 -0
- package/extensions/agency-agents/agents/design/design-brand-guardian.md +320 -0
- package/extensions/agency-agents/agents/design/design-image-prompt-engineer.md +234 -0
- package/extensions/agency-agents/agents/design/design-ui-designer.md +381 -0
- package/extensions/agency-agents/agents/design/design-ux-architect.md +467 -0
- package/extensions/agency-agents/agents/design/design-ux-researcher.md +327 -0
- package/extensions/agency-agents/agents/design/design-visual-storyteller.md +147 -0
- package/extensions/agency-agents/agents/design/design-whimsy-injector.md +436 -0
- package/extensions/agency-agents/agents/engineering/engineering-ai-engineer.md +144 -0
- package/extensions/agency-agents/agents/engineering/engineering-backend-architect.md +233 -0
- package/extensions/agency-agents/agents/engineering/engineering-devops-automator.md +374 -0
- package/extensions/agency-agents/agents/engineering/engineering-frontend-developer.md +223 -0
- package/extensions/agency-agents/agents/engineering/engineering-mobile-app-builder.md +491 -0
- package/extensions/agency-agents/agents/engineering/engineering-rapid-prototyper.md +460 -0
- package/extensions/agency-agents/agents/engineering/engineering-security-engineer.md +275 -0
- package/extensions/agency-agents/agents/engineering/engineering-senior-developer.md +174 -0
- package/extensions/agency-agents/agents/examples/README.md +48 -0
- package/extensions/agency-agents/agents/examples/nexus-spatial-discovery.md +852 -0
- package/extensions/agency-agents/agents/examples/workflow-landing-page.md +119 -0
- package/extensions/agency-agents/agents/examples/workflow-startup-mvp.md +155 -0
- package/extensions/agency-agents/agents/integrations/README.md +117 -0
- package/extensions/agency-agents/agents/integrations/aider/README.md +38 -0
- package/extensions/agency-agents/agents/integrations/antigravity/README.md +49 -0
- package/extensions/agency-agents/agents/integrations/claude-code/README.md +31 -0
- package/extensions/agency-agents/agents/integrations/cursor/README.md +38 -0
- package/extensions/agency-agents/agents/integrations/gemini-cli/README.md +36 -0
- package/extensions/agency-agents/agents/integrations/opencode/README.md +58 -0
- package/extensions/agency-agents/agents/integrations/windsurf/README.md +26 -0
- package/extensions/agency-agents/agents/marketing/marketing-app-store-optimizer.md +319 -0
- package/extensions/agency-agents/agents/marketing/marketing-content-creator.md +52 -0
- package/extensions/agency-agents/agents/marketing/marketing-growth-hacker.md +52 -0
- package/extensions/agency-agents/agents/marketing/marketing-instagram-curator.md +111 -0
- package/extensions/agency-agents/agents/marketing/marketing-reddit-community-builder.md +121 -0
- package/extensions/agency-agents/agents/marketing/marketing-social-media-strategist.md +123 -0
- package/extensions/agency-agents/agents/marketing/marketing-tiktok-strategist.md +123 -0
- package/extensions/agency-agents/agents/marketing/marketing-twitter-engager.md +124 -0
- package/extensions/agency-agents/agents/marketing/marketing-wechat-official-account.md +143 -0
- package/extensions/agency-agents/agents/marketing/marketing-xiaohongshu-specialist.md +136 -0
- package/extensions/agency-agents/agents/marketing/marketing-zhihu-strategist.md +160 -0
- package/extensions/agency-agents/agents/product/product-feedback-synthesizer.md +117 -0
- package/extensions/agency-agents/agents/product/product-sprint-prioritizer.md +152 -0
- package/extensions/agency-agents/agents/product/product-trend-researcher.md +157 -0
- package/extensions/agency-agents/agents/project-management/project-management-experiment-tracker.md +196 -0
- package/extensions/agency-agents/agents/project-management/project-management-project-shepherd.md +192 -0
- package/extensions/agency-agents/agents/project-management/project-management-studio-operations.md +198 -0
- package/extensions/agency-agents/agents/project-management/project-management-studio-producer.md +201 -0
- package/extensions/agency-agents/agents/project-management/project-manager-senior.md +133 -0
- package/extensions/agency-agents/agents/scripts/convert.sh +362 -0
- package/extensions/agency-agents/agents/scripts/install.sh +465 -0
- package/extensions/agency-agents/agents/scripts/lint-agents.sh +115 -0
- package/extensions/agency-agents/agents/spatial-computing/macos-spatial-metal-engineer.md +335 -0
- package/extensions/agency-agents/agents/spatial-computing/terminal-integration-specialist.md +68 -0
- package/extensions/agency-agents/agents/spatial-computing/visionos-spatial-engineer.md +52 -0
- package/extensions/agency-agents/agents/spatial-computing/xr-cockpit-interaction-specialist.md +30 -0
- package/extensions/agency-agents/agents/spatial-computing/xr-immersive-developer.md +30 -0
- package/extensions/agency-agents/agents/spatial-computing/xr-interface-architect.md +30 -0
- package/extensions/agency-agents/agents/specialized/agentic-identity-trust.md +367 -0
- package/extensions/agency-agents/agents/specialized/agents-orchestrator.md +365 -0
- package/extensions/agency-agents/agents/specialized/data-analytics-reporter.md +52 -0
- package/extensions/agency-agents/agents/specialized/data-consolidation-agent.md +58 -0
- package/extensions/agency-agents/agents/specialized/lsp-index-engineer.md +312 -0
- package/extensions/agency-agents/agents/specialized/report-distribution-agent.md +63 -0
- package/extensions/agency-agents/agents/specialized/sales-data-extraction-agent.md +65 -0
- package/extensions/agency-agents/agents/strategy/EXECUTIVE-BRIEF.md +95 -0
- package/extensions/agency-agents/agents/strategy/QUICKSTART.md +194 -0
- package/extensions/agency-agents/agents/strategy/coordination/agent-activation-prompts.md +401 -0
- package/extensions/agency-agents/agents/strategy/coordination/handoff-templates.md +357 -0
- package/extensions/agency-agents/agents/strategy/nexus-strategy.md +1110 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-0-discovery.md +178 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-1-strategy.md +238 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-2-foundation.md +278 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-3-build.md +286 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-4-hardening.md +332 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-5-launch.md +277 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-6-operate.md +318 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-enterprise-feature.md +157 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-incident-response.md +217 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-marketing-campaign.md +187 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-startup-mvp.md +154 -0
- package/extensions/agency-agents/agents/support/support-analytics-reporter.md +363 -0
- package/extensions/agency-agents/agents/support/support-executive-summary-generator.md +210 -0
- package/extensions/agency-agents/agents/support/support-finance-tracker.md +440 -0
- package/extensions/agency-agents/agents/support/support-infrastructure-maintainer.md +616 -0
- package/extensions/agency-agents/agents/support/support-legal-compliance-checker.md +586 -0
- package/extensions/agency-agents/agents/support/support-support-responder.md +583 -0
- package/extensions/agency-agents/agents/testing/testing-accessibility-auditor.md +313 -0
- package/extensions/agency-agents/agents/testing/testing-api-tester.md +304 -0
- package/extensions/agency-agents/agents/testing/testing-evidence-collector.md +208 -0
- package/extensions/agency-agents/agents/testing/testing-performance-benchmarker.md +266 -0
- package/extensions/agency-agents/agents/testing/testing-reality-checker.md +236 -0
- package/extensions/agency-agents/agents/testing/testing-test-results-analyzer.md +303 -0
- package/extensions/agency-agents/agents/testing/testing-tool-evaluator.md +392 -0
- package/extensions/agency-agents/agents/testing/testing-workflow-optimizer.md +448 -0
- package/extensions/agency-agents/index.ts +733 -0
- package/extensions/agency-agents/node_modules/.bin/jiti +21 -0
- package/extensions/agency-agents/node_modules/.bin/tsc +21 -0
- package/extensions/agency-agents/node_modules/.bin/tsserver +21 -0
- package/extensions/agency-agents/node_modules/.bin/tsx +21 -0
- package/extensions/agency-agents/node_modules/.bin/vite +21 -0
- package/extensions/agency-agents/node_modules/.bin/vitest +21 -0
- package/extensions/agency-agents/node_modules/.bin/yaml +21 -0
- package/extensions/agency-agents/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/extensions/agency-agents/package.json +25 -0
- package/extensions/agency-agents/poolbot.plugin.json +11 -0
- package/extensions/agency-agents/src/AgencyAgentsService.test.ts +443 -0
- package/extensions/agency-agents/src/AgencyAgentsService.ts +288 -0
- package/extensions/agency-agents/src/types.ts +147 -0
- package/extensions/agency-agents/vitest.config.ts +8 -0
- package/extensions/hexstrike-ai/README.md +98 -0
- package/extensions/hexstrike-ai/node_modules/.bin/tsc +21 -0
- package/extensions/hexstrike-ai/node_modules/.bin/tsserver +21 -0
- package/extensions/hexstrike-ai/package.json +29 -0
- package/extensions/hexstrike-ai/poolbot.plugin.json +31 -0
- package/extensions/hexstrike-ai/src/client.ts +91 -0
- package/extensions/hexstrike-ai/src/index.ts +170 -0
- package/extensions/hexstrike-ai/src/server/hexstrike_mcp.py +5470 -0
- package/extensions/hexstrike-ai/src/server/hexstrike_server.py +17289 -0
- package/extensions/hexstrike-ai/src/server/requirements.txt +84 -0
- package/extensions/hexstrike-ai/src/server-manager.ts +83 -0
- package/extensions/hexstrike-ai/tsconfig.json +20 -0
- package/extensions/page-agent/README.md +159 -0
- package/extensions/page-agent/index.ts +595 -0
- package/extensions/page-agent/node_modules/.bin/jiti +21 -0
- package/extensions/page-agent/node_modules/.bin/playwright +21 -0
- package/extensions/page-agent/node_modules/.bin/tsc +21 -0
- package/extensions/page-agent/node_modules/.bin/tsserver +21 -0
- package/extensions/page-agent/node_modules/.bin/tsx +21 -0
- package/extensions/page-agent/node_modules/.bin/vitest +21 -0
- package/extensions/page-agent/node_modules/.bin/yaml +21 -0
- package/extensions/page-agent/package.json +43 -0
- package/extensions/page-agent/poolbot.plugin.json +24 -0
- package/extensions/page-agent/src/PageAgentService.test.ts +517 -0
- package/extensions/page-agent/src/PageAgentService.ts +636 -0
- package/extensions/page-agent/src/PoolBotPageController.test.ts +358 -0
- package/extensions/page-agent/src/PoolBotPageController.ts +245 -0
- package/extensions/page-agent/src/index.ts +20 -0
- package/extensions/page-agent/src/tools.test.ts +231 -0
- package/extensions/page-agent/src/tools.ts +167 -0
- package/extensions/page-agent/src/types.ts +198 -0
- package/extensions/xyops/README.md +227 -0
- package/extensions/xyops/index.ts +342 -0
- package/extensions/xyops/node_modules/.bin/jiti +21 -0
- package/extensions/xyops/node_modules/.bin/tsc +21 -0
- package/extensions/xyops/node_modules/.bin/tsserver +21 -0
- package/extensions/xyops/node_modules/.bin/tsx +21 -0
- package/extensions/xyops/node_modules/.bin/vitest +21 -0
- package/extensions/xyops/node_modules/.bin/yaml +21 -0
- package/extensions/xyops/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/extensions/xyops/package.json +39 -0
- package/extensions/xyops/poolbot.plugin.json +21 -0
- package/extensions/xyops/src/client.test.ts +467 -0
- package/extensions/xyops/src/client.ts +157 -0
- package/extensions/xyops/src/types.ts +147 -0
- package/extensions/xyops/vitest.config.ts +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Result Truncation & Context Pruning
|
|
3
|
+
*
|
|
4
|
+
* Implements:
|
|
5
|
+
* - Head+tail truncation (preserves diagnostics)
|
|
6
|
+
* - Context pruning for tool-results during soft-trim
|
|
7
|
+
* - Preserve recent turns verbatim
|
|
8
|
+
* - Multimodal + tool-result hints
|
|
9
|
+
*
|
|
10
|
+
* OpenClaw #32384, #30951
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Default truncation config
|
|
14
|
+
* Head+tail approach preserves important diagnostics
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_TRUNCATION_CONFIG = {
|
|
17
|
+
maxToolResultChars: 50_000,
|
|
18
|
+
headChars: 10_000,
|
|
19
|
+
tailChars: 10_000,
|
|
20
|
+
minCharsForTruncation: 20_000,
|
|
21
|
+
preserveErrors: true,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Truncate tool result content with head+tail strategy
|
|
25
|
+
* OpenClaw #32384 - preserves diagnostic information
|
|
26
|
+
*/
|
|
27
|
+
export function truncateToolResult(content, config = DEFAULT_TRUNCATION_CONFIG) {
|
|
28
|
+
const originalLength = content.length;
|
|
29
|
+
// Don't truncate if under threshold
|
|
30
|
+
if (originalLength <= config.minCharsForTruncation) {
|
|
31
|
+
return { truncated: content, wasTruncated: false, originalLength };
|
|
32
|
+
}
|
|
33
|
+
// Don't truncate if under max
|
|
34
|
+
if (originalLength <= config.maxToolResultChars) {
|
|
35
|
+
return { truncated: content, wasTruncated: false, originalLength };
|
|
36
|
+
}
|
|
37
|
+
// Head+tail truncation
|
|
38
|
+
const head = content.slice(0, config.headChars);
|
|
39
|
+
const tail = content.slice(-config.tailChars);
|
|
40
|
+
const omitted = originalLength - config.headChars - config.tailChars;
|
|
41
|
+
const truncated = `${head}\n\n[...${omitted.toLocaleString()} characters omitted...]\n\n${tail}`;
|
|
42
|
+
return { truncated, wasTruncated: true, originalLength };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if a message is a tool result
|
|
46
|
+
*/
|
|
47
|
+
export function isToolResultMessage(message) {
|
|
48
|
+
return message.role === "toolResult";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if a tool result contains an error
|
|
52
|
+
*/
|
|
53
|
+
export function isToolResultError(message) {
|
|
54
|
+
if (!isToolResultMessage(message)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return message.isError === true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract tool result content from message
|
|
61
|
+
*/
|
|
62
|
+
export function extractToolResultContent(message) {
|
|
63
|
+
const content = message.content;
|
|
64
|
+
if (typeof content === "string") {
|
|
65
|
+
return content;
|
|
66
|
+
}
|
|
67
|
+
if (content && typeof content === "object") {
|
|
68
|
+
try {
|
|
69
|
+
return JSON.stringify(content);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return JSON.stringify({ error: "Failed to stringify content" });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Apply truncation to tool result messages
|
|
79
|
+
* Preserves errors by default
|
|
80
|
+
*/
|
|
81
|
+
export function truncateToolResultMessages(messages, config = DEFAULT_TRUNCATION_CONFIG) {
|
|
82
|
+
return messages.map((message) => {
|
|
83
|
+
if (!isToolResultMessage(message)) {
|
|
84
|
+
return message;
|
|
85
|
+
}
|
|
86
|
+
// Preserve errors if configured
|
|
87
|
+
if (config.preserveErrors && isToolResultError(message)) {
|
|
88
|
+
return message;
|
|
89
|
+
}
|
|
90
|
+
const content = extractToolResultContent(message);
|
|
91
|
+
const { truncated, wasTruncated } = truncateToolResult(content, config);
|
|
92
|
+
if (!wasTruncated) {
|
|
93
|
+
return message;
|
|
94
|
+
}
|
|
95
|
+
// Create new message with truncated content
|
|
96
|
+
return {
|
|
97
|
+
...message,
|
|
98
|
+
content: truncated,
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Default context pruning config
|
|
104
|
+
*/
|
|
105
|
+
export const DEFAULT_CONTEXT_PRUNING_CONFIG = {
|
|
106
|
+
keepRecentTurns: 10,
|
|
107
|
+
pruneToolResultsOlderThan: 20,
|
|
108
|
+
keepErrorToolResults: true,
|
|
109
|
+
keepMultimodalContent: true,
|
|
110
|
+
compressToolResults: false,
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Count turns in message history
|
|
114
|
+
* A turn = user message + assistant response
|
|
115
|
+
*/
|
|
116
|
+
export function countTurns(messages) {
|
|
117
|
+
let turns = 0;
|
|
118
|
+
let hasUserMessage = false;
|
|
119
|
+
for (const message of messages) {
|
|
120
|
+
const role = message.role;
|
|
121
|
+
if (role === "user") {
|
|
122
|
+
hasUserMessage = true;
|
|
123
|
+
}
|
|
124
|
+
else if (role === "assistant" && hasUserMessage) {
|
|
125
|
+
turns += 1;
|
|
126
|
+
hasUserMessage = false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Count incomplete turn if there's a user message without response
|
|
130
|
+
if (hasUserMessage) {
|
|
131
|
+
turns += 1;
|
|
132
|
+
}
|
|
133
|
+
return turns;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Prune context for soft-trim while preserving important information
|
|
137
|
+
* OpenClaw #30951 - maintains diagnostic quality
|
|
138
|
+
*/
|
|
139
|
+
export function pruneContextForSoftTrim(messages, config = DEFAULT_CONTEXT_PRUNING_CONFIG) {
|
|
140
|
+
let currentTurn = 0;
|
|
141
|
+
// Process messages in reverse to identify recent turns
|
|
142
|
+
const reversedMessages = [...messages].reverse();
|
|
143
|
+
const keptMessages = [];
|
|
144
|
+
for (const message of reversedMessages) {
|
|
145
|
+
const role = message.role;
|
|
146
|
+
const isToolResult = isToolResultMessage(message);
|
|
147
|
+
const isToolError = isToolResultError(message);
|
|
148
|
+
const hasMultimodal = hasMultimodalContent(message);
|
|
149
|
+
// Always keep user and assistant messages in recent turns
|
|
150
|
+
if (role === "user" || role === "assistant") {
|
|
151
|
+
if (currentTurn < config.keepRecentTurns) {
|
|
152
|
+
keptMessages.unshift(message);
|
|
153
|
+
if (role === "assistant") {
|
|
154
|
+
currentTurn += 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// Older turns: selectively prune
|
|
159
|
+
if (!isToolResult) {
|
|
160
|
+
keptMessages.unshift(message);
|
|
161
|
+
}
|
|
162
|
+
else if (config.keepErrorToolResults && isToolError) {
|
|
163
|
+
keptMessages.unshift(message);
|
|
164
|
+
}
|
|
165
|
+
else if (config.keepMultimodalContent && hasMultimodal) {
|
|
166
|
+
keptMessages.unshift(message);
|
|
167
|
+
}
|
|
168
|
+
else if (config.compressToolResults) {
|
|
169
|
+
// Compress to summary instead of full content
|
|
170
|
+
const compressed = compressToolResult(message);
|
|
171
|
+
keptMessages.unshift(compressed);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
// Tool results: apply pruning rules
|
|
177
|
+
if (isToolResult) {
|
|
178
|
+
if (currentTurn < config.keepRecentTurns) {
|
|
179
|
+
// Keep recent tool results
|
|
180
|
+
keptMessages.unshift(message);
|
|
181
|
+
}
|
|
182
|
+
else if (config.keepErrorToolResults && isToolError) {
|
|
183
|
+
// Always keep errors
|
|
184
|
+
keptMessages.unshift(message);
|
|
185
|
+
}
|
|
186
|
+
else if (config.keepMultimodalContent && hasMultimodal) {
|
|
187
|
+
// Keep multimodal content
|
|
188
|
+
keptMessages.unshift(message);
|
|
189
|
+
}
|
|
190
|
+
else if (config.compressToolResults) {
|
|
191
|
+
// Compress older tool results
|
|
192
|
+
const compressed = compressToolResult(message);
|
|
193
|
+
keptMessages.unshift(compressed);
|
|
194
|
+
}
|
|
195
|
+
// Otherwise: prune (don't add to keptMessages)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return keptMessages;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if a message has multimodal content (images, files, etc.)
|
|
202
|
+
*/
|
|
203
|
+
export function hasMultimodalContent(message) {
|
|
204
|
+
const content = message.content;
|
|
205
|
+
if (!content) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
if (Array.isArray(content)) {
|
|
209
|
+
return content.some((item) => {
|
|
210
|
+
if (!item || typeof item !== "object") {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
const record = item;
|
|
214
|
+
return (typeof record.image === "string" ||
|
|
215
|
+
typeof record.file === "string" ||
|
|
216
|
+
typeof record.data === "string" ||
|
|
217
|
+
typeof record.url === "string");
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Compress tool result to summary (for context pruning)
|
|
224
|
+
*/
|
|
225
|
+
export function compressToolResult(message) {
|
|
226
|
+
if (!isToolResultMessage(message)) {
|
|
227
|
+
return message;
|
|
228
|
+
}
|
|
229
|
+
const toolName = message.toolName;
|
|
230
|
+
const toolNameStr = typeof toolName === "string" && toolName.trim() ? toolName : "unknown";
|
|
231
|
+
const content = extractToolResultContent(message);
|
|
232
|
+
// Create summary instead of full content
|
|
233
|
+
const summary = `[Tool: ${toolNameStr}] - ${content.length.toLocaleString()} characters`;
|
|
234
|
+
return {
|
|
235
|
+
...message,
|
|
236
|
+
content: summary,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Audit compaction quality
|
|
241
|
+
* OpenClaw #30951 - ensures compaction doesn't lose critical information
|
|
242
|
+
*/
|
|
243
|
+
export function auditCompactionQuality(params) {
|
|
244
|
+
const { originalMessages, compactedMessages, summary } = params;
|
|
245
|
+
const issues = [];
|
|
246
|
+
const recommendations = [];
|
|
247
|
+
let score = 100;
|
|
248
|
+
// Check 1: Tool errors preserved
|
|
249
|
+
const originalErrors = originalMessages.filter(isToolResultError).length;
|
|
250
|
+
const compactedErrors = compactedMessages.filter(isToolResultError).length;
|
|
251
|
+
if (compactedErrors < originalErrors) {
|
|
252
|
+
issues.push(`Lost ${originalErrors - compactedErrors} tool error messages`);
|
|
253
|
+
recommendations.push("Preserve all tool errors during compaction");
|
|
254
|
+
score -= 20;
|
|
255
|
+
}
|
|
256
|
+
// Check 2: Recent turns preserved
|
|
257
|
+
const originalTurns = countTurns(originalMessages);
|
|
258
|
+
const compactedTurns = countTurns(compactedMessages);
|
|
259
|
+
if (compactedTurns < originalTurns * 0.3) {
|
|
260
|
+
issues.push("Compaction ratio too aggressive (<30% of turns preserved)");
|
|
261
|
+
recommendations.push("Keep at least 30% of recent turns verbatim");
|
|
262
|
+
score -= 15;
|
|
263
|
+
}
|
|
264
|
+
// Check 3: Summary quality
|
|
265
|
+
if (summary) {
|
|
266
|
+
if (summary.length < 50) {
|
|
267
|
+
issues.push("Summary too short (may lack detail)");
|
|
268
|
+
recommendations.push("Generate more detailed summaries (min 50 chars)");
|
|
269
|
+
score -= 10;
|
|
270
|
+
}
|
|
271
|
+
if (summary.length > 5000) {
|
|
272
|
+
issues.push("Summary too long (defeats compaction purpose)");
|
|
273
|
+
recommendations.push("Keep summaries concise (<5000 chars)");
|
|
274
|
+
score -= 5;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Check 4: Multimodal content
|
|
278
|
+
const originalMultimodal = originalMessages.filter(hasMultimodalContent).length;
|
|
279
|
+
const compactedMultimodal = compactedMessages.filter(hasMultimodalContent).length;
|
|
280
|
+
if (compactedMultimodal < originalMultimodal) {
|
|
281
|
+
issues.push(`Lost ${originalMultimodal - compactedMultimodal} multimodal messages`);
|
|
282
|
+
recommendations.push("Preserve multimodal content during compaction");
|
|
283
|
+
score -= 15;
|
|
284
|
+
}
|
|
285
|
+
// Check 5: Message order
|
|
286
|
+
const lastOriginalRole = originalMessages[originalMessages.length - 1]?.role;
|
|
287
|
+
const lastCompactedRole = compactedMessages[compactedMessages.length - 1]?.role;
|
|
288
|
+
if (lastOriginalRole !== lastCompactedRole) {
|
|
289
|
+
issues.push("Message order may be incorrect (last message role mismatch)");
|
|
290
|
+
recommendations.push("Verify message ordering after compaction");
|
|
291
|
+
score -= 10;
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
score: Math.max(0, score),
|
|
295
|
+
issues,
|
|
296
|
+
recommendations,
|
|
297
|
+
passed: score >= 70,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
import { optionalStringEnum, stringEnum } from "../schema/typebox.js";
|
|
4
|
+
import { jsonResult, readStringParam } from "./common.js";
|
|
5
|
+
import { callGatewayTool, readGatewayCallOptions } from "./gateway.js";
|
|
6
|
+
import { resolveNodeId } from "./nodes-utils.js";
|
|
7
|
+
const FILE_TOOL_ACTIONS = ["read", "write", "exists", "delete", "list"];
|
|
8
|
+
const FileToolSchema = Type.Object({
|
|
9
|
+
action: stringEnum(FILE_TOOL_ACTIONS),
|
|
10
|
+
gatewayUrl: Type.Optional(Type.String()),
|
|
11
|
+
gatewayToken: Type.Optional(Type.String()),
|
|
12
|
+
timeoutMs: Type.Optional(Type.Number()),
|
|
13
|
+
node: Type.Optional(Type.String()),
|
|
14
|
+
// read/write/exists/delete
|
|
15
|
+
path: Type.Optional(Type.String()),
|
|
16
|
+
// read
|
|
17
|
+
encoding: optionalStringEnum(["utf8", "base64", "binary"]),
|
|
18
|
+
maxBytes: Type.Optional(Type.Number()),
|
|
19
|
+
// write
|
|
20
|
+
content: Type.Optional(Type.String()),
|
|
21
|
+
contentBase64: Type.Optional(Type.String()),
|
|
22
|
+
append: Type.Optional(Type.Boolean()),
|
|
23
|
+
createDirs: Type.Optional(Type.Boolean()),
|
|
24
|
+
// list
|
|
25
|
+
recursive: Type.Optional(Type.Boolean()),
|
|
26
|
+
includeHidden: Type.Optional(Type.Boolean()),
|
|
27
|
+
});
|
|
28
|
+
export function createNodesFileTool(_options) {
|
|
29
|
+
return {
|
|
30
|
+
label: "NodesFile",
|
|
31
|
+
name: "nodes_file",
|
|
32
|
+
description: "Read, write, and manage files on paired nodes (remote devices). Supports text and binary content with base64 encoding.",
|
|
33
|
+
parameters: FileToolSchema,
|
|
34
|
+
execute: async (_toolCallId, args) => {
|
|
35
|
+
const params = args;
|
|
36
|
+
const action = readStringParam(params, "action", { required: true });
|
|
37
|
+
const gatewayOpts = readGatewayCallOptions(params);
|
|
38
|
+
try {
|
|
39
|
+
switch (action) {
|
|
40
|
+
case "read": {
|
|
41
|
+
const node = readStringParam(params, "node", { required: true });
|
|
42
|
+
const path = readStringParam(params, "path", { required: true });
|
|
43
|
+
const nodeId = await resolveNodeId(gatewayOpts, node);
|
|
44
|
+
const encoding = typeof params.encoding === "string" ? params.encoding : "utf8";
|
|
45
|
+
const maxBytes = typeof params.maxBytes === "number" && Number.isFinite(params.maxBytes)
|
|
46
|
+
? Math.max(1, Math.floor(params.maxBytes))
|
|
47
|
+
: undefined;
|
|
48
|
+
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
|
49
|
+
nodeId,
|
|
50
|
+
command: "file.read",
|
|
51
|
+
params: {
|
|
52
|
+
path,
|
|
53
|
+
encoding,
|
|
54
|
+
maxBytes,
|
|
55
|
+
},
|
|
56
|
+
idempotencyKey: crypto.randomUUID(),
|
|
57
|
+
});
|
|
58
|
+
const payload = raw?.payload ?? {};
|
|
59
|
+
if (typeof payload === "object" &&
|
|
60
|
+
payload !== null &&
|
|
61
|
+
"content" in payload &&
|
|
62
|
+
typeof payload.content === "string") {
|
|
63
|
+
const typedPayload = payload;
|
|
64
|
+
return jsonResult({
|
|
65
|
+
content: typedPayload.content,
|
|
66
|
+
encoding: typedPayload.encoding ?? encoding,
|
|
67
|
+
size: typedPayload.size,
|
|
68
|
+
truncated: typedPayload.truncated ?? false,
|
|
69
|
+
path,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
throw new Error("Invalid response from file.read");
|
|
73
|
+
}
|
|
74
|
+
case "write": {
|
|
75
|
+
const node = readStringParam(params, "node", { required: true });
|
|
76
|
+
const path = readStringParam(params, "path", { required: true });
|
|
77
|
+
const nodeId = await resolveNodeId(gatewayOpts, node);
|
|
78
|
+
const content = typeof params.content === "string" ? params.content : undefined;
|
|
79
|
+
const contentBase64 = typeof params.contentBase64 === "string" ? params.contentBase64 : undefined;
|
|
80
|
+
if (content === undefined && contentBase64 === undefined) {
|
|
81
|
+
throw new Error("content or contentBase64 required for write action");
|
|
82
|
+
}
|
|
83
|
+
const append = typeof params.append === "boolean" ? params.append : false;
|
|
84
|
+
const createDirs = typeof params.createDirs === "boolean" ? params.createDirs : false;
|
|
85
|
+
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
|
86
|
+
nodeId,
|
|
87
|
+
command: "file.write",
|
|
88
|
+
params: {
|
|
89
|
+
path,
|
|
90
|
+
content,
|
|
91
|
+
contentBase64,
|
|
92
|
+
append,
|
|
93
|
+
createDirs,
|
|
94
|
+
},
|
|
95
|
+
idempotencyKey: crypto.randomUUID(),
|
|
96
|
+
});
|
|
97
|
+
const payload = raw?.payload ?? {};
|
|
98
|
+
if (typeof payload === "object" &&
|
|
99
|
+
payload !== null &&
|
|
100
|
+
"ok" in payload &&
|
|
101
|
+
payload.ok === true) {
|
|
102
|
+
const typedPayload = payload;
|
|
103
|
+
return jsonResult({
|
|
104
|
+
ok: true,
|
|
105
|
+
size: typedPayload.size,
|
|
106
|
+
path: typedPayload.path ?? path,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
throw new Error("Invalid response from file.write");
|
|
110
|
+
}
|
|
111
|
+
case "exists": {
|
|
112
|
+
const node = readStringParam(params, "node", { required: true });
|
|
113
|
+
const path = readStringParam(params, "path", { required: true });
|
|
114
|
+
const nodeId = await resolveNodeId(gatewayOpts, node);
|
|
115
|
+
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
|
116
|
+
nodeId,
|
|
117
|
+
command: "file.exists",
|
|
118
|
+
params: { path },
|
|
119
|
+
idempotencyKey: crypto.randomUUID(),
|
|
120
|
+
});
|
|
121
|
+
const payload = raw?.payload ?? {};
|
|
122
|
+
if (typeof payload === "object" &&
|
|
123
|
+
payload !== null &&
|
|
124
|
+
"exists" in payload &&
|
|
125
|
+
typeof payload.exists === "boolean") {
|
|
126
|
+
const typedPayload = payload;
|
|
127
|
+
return jsonResult({
|
|
128
|
+
exists: typedPayload.exists,
|
|
129
|
+
isFile: typedPayload.isFile,
|
|
130
|
+
isDirectory: typedPayload.isDirectory,
|
|
131
|
+
size: typedPayload.size,
|
|
132
|
+
modified: typedPayload.modified,
|
|
133
|
+
path,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
throw new Error("Invalid response from file.exists");
|
|
137
|
+
}
|
|
138
|
+
case "delete": {
|
|
139
|
+
const node = readStringParam(params, "node", { required: true });
|
|
140
|
+
const path = readStringParam(params, "path", { required: true });
|
|
141
|
+
const nodeId = await resolveNodeId(gatewayOpts, node);
|
|
142
|
+
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
|
143
|
+
nodeId,
|
|
144
|
+
command: "file.delete",
|
|
145
|
+
params: { path },
|
|
146
|
+
idempotencyKey: crypto.randomUUID(),
|
|
147
|
+
});
|
|
148
|
+
const payload = raw?.payload ?? {};
|
|
149
|
+
if (typeof payload === "object" &&
|
|
150
|
+
payload !== null &&
|
|
151
|
+
"ok" in payload &&
|
|
152
|
+
payload.ok === true) {
|
|
153
|
+
return jsonResult({ ok: true, path });
|
|
154
|
+
}
|
|
155
|
+
throw new Error("Invalid response from file.delete");
|
|
156
|
+
}
|
|
157
|
+
case "list": {
|
|
158
|
+
const node = readStringParam(params, "node", { required: true });
|
|
159
|
+
const path = readStringParam(params, "path", { required: true });
|
|
160
|
+
const nodeId = await resolveNodeId(gatewayOpts, node);
|
|
161
|
+
const recursive = typeof params.recursive === "boolean" ? params.recursive : false;
|
|
162
|
+
const includeHidden = typeof params.includeHidden === "boolean" ? params.includeHidden : false;
|
|
163
|
+
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
|
164
|
+
nodeId,
|
|
165
|
+
command: "file.list",
|
|
166
|
+
params: { path, recursive, includeHidden },
|
|
167
|
+
idempotencyKey: crypto.randomUUID(),
|
|
168
|
+
});
|
|
169
|
+
const payload = raw?.payload ?? {};
|
|
170
|
+
if (typeof payload === "object" &&
|
|
171
|
+
payload !== null &&
|
|
172
|
+
"entries" in payload &&
|
|
173
|
+
Array.isArray(payload.entries)) {
|
|
174
|
+
const typedPayload = payload;
|
|
175
|
+
return jsonResult({
|
|
176
|
+
entries: typedPayload.entries,
|
|
177
|
+
path,
|
|
178
|
+
count: typedPayload.entries.length,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
throw new Error("Invalid response from file.list");
|
|
182
|
+
}
|
|
183
|
+
default:
|
|
184
|
+
throw new Error(`Unknown action: ${action}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
const nodeLabel = typeof params.node === "string" && params.node.trim() ? params.node.trim() : "auto";
|
|
189
|
+
const gatewayLabel = gatewayOpts.gatewayUrl && gatewayOpts.gatewayUrl.trim()
|
|
190
|
+
? gatewayOpts.gatewayUrl.trim()
|
|
191
|
+
: "default";
|
|
192
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
193
|
+
throw new Error(`nodes_file (${action}) failed for node=${nodeLabel} via gateway=${gatewayLabel}: ${message}`, { cause: err });
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
package/dist/build-info.json
CHANGED
package/dist/cli/config-cli.js
CHANGED
|
@@ -364,4 +364,64 @@ export function registerConfigCli(program) {
|
|
|
364
364
|
defaultRuntime.exit(1);
|
|
365
365
|
}
|
|
366
366
|
});
|
|
367
|
+
cmd
|
|
368
|
+
.command("validate")
|
|
369
|
+
.description("Validate the current config file without modifying it")
|
|
370
|
+
.option("--json", "Output validation result as JSON", false)
|
|
371
|
+
.action(async (opts) => {
|
|
372
|
+
try {
|
|
373
|
+
const snapshot = await readConfigFileSnapshot();
|
|
374
|
+
const validated = validateConfigObjectWithPlugins(snapshot.parsed);
|
|
375
|
+
if (opts.json) {
|
|
376
|
+
defaultRuntime.log(JSON.stringify({
|
|
377
|
+
valid: validated.ok,
|
|
378
|
+
issues: validated.ok ? [] : validated.issues,
|
|
379
|
+
warnings: validated.warnings,
|
|
380
|
+
legacyIssues: snapshot.legacyIssues,
|
|
381
|
+
}, null, 2));
|
|
382
|
+
defaultRuntime.exit(validated.ok ? 0 : 1);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (validated.ok) {
|
|
386
|
+
defaultRuntime.log(theme.success("✓ Config is valid"));
|
|
387
|
+
if (validated.warnings.length > 0) {
|
|
388
|
+
defaultRuntime.log(theme.warn(`\n${validated.warnings.length} warning(s):`));
|
|
389
|
+
for (const warning of validated.warnings) {
|
|
390
|
+
defaultRuntime.log(` - ${warning.path}: ${warning.message}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (snapshot.legacyIssues.length > 0) {
|
|
394
|
+
defaultRuntime.log(theme.warn(`\n${snapshot.legacyIssues.length} legacy issue(s) detected:`));
|
|
395
|
+
for (const issue of snapshot.legacyIssues) {
|
|
396
|
+
defaultRuntime.log(` - ${issue}`);
|
|
397
|
+
}
|
|
398
|
+
defaultRuntime.log(theme.muted("\nRun 'poolbot config' wizard to migrate."));
|
|
399
|
+
}
|
|
400
|
+
defaultRuntime.exit(0);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
defaultRuntime.error(danger("✗ Config validation failed"));
|
|
404
|
+
for (const issue of validated.issues) {
|
|
405
|
+
defaultRuntime.error(` - ${issue.path}: ${issue.message}`);
|
|
406
|
+
}
|
|
407
|
+
if (validated.warnings.length > 0) {
|
|
408
|
+
defaultRuntime.log(theme.warn(`\n${validated.warnings.length} warning(s):`));
|
|
409
|
+
for (const warning of validated.warnings) {
|
|
410
|
+
defaultRuntime.log(` - ${warning.path}: ${warning.message}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (snapshot.legacyIssues.length > 0) {
|
|
414
|
+
defaultRuntime.log(theme.warn(`\n${snapshot.legacyIssues.length} legacy issue(s) also detected:`));
|
|
415
|
+
for (const issue of snapshot.legacyIssues) {
|
|
416
|
+
defaultRuntime.log(` - ${issue}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
defaultRuntime.exit(1);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
catch (err) {
|
|
423
|
+
defaultRuntime.error(danger(String(err)));
|
|
424
|
+
defaultRuntime.exit(1);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
367
427
|
}
|