@johpaz/hive-sdk 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +129 -0
- package/README.md +78 -23
- package/bun.lock +55 -29
- package/bunfig.toml +4 -2
- package/docs/API-AGENTS.md +78 -27
- package/docs/API-CONTEXT-COMPILER.md +31 -34
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +58 -22
- package/docs/HIVE-HARNESS.md +1 -1
- package/docs/INDEX.md +4 -4
- package/docs/TEMPLATE-HIVE-APP.md +10 -10
- package/package.json +17 -12
- package/packages/cli/package.json +2 -2
- package/packages/cli/src/commands/create-app.test.ts +36 -7
- package/packages/cli/src/commands/init.ts +3 -3
- package/packages/cli/src/commands/run.ts +1 -1
- package/packages/cli/src/commands/test.ts +37 -25
- package/packages/cli/src/commands/trace.ts +30 -28
- package/packages/cli/templates/hive-app/.env.example +10 -2
- package/packages/cli/templates/hive-app/README.md +103 -0
- package/packages/cli/templates/hive-app/hive.config.ts +9 -3
- package/packages/cli/templates/hive-app/src/agents/coordinator.ts +8 -1
- package/packages/cli/templates/hive-app/src/main.ts +12 -19
- package/packages/core/package.json +13 -12
- package/packages/core/src/agent/acceptance-checks.ts +172 -0
- package/packages/core/src/agent/agent-catalog.ts +348 -0
- package/packages/core/src/agent/agent-loop.ts +1373 -0
- package/packages/core/src/agent/capability-search.ts +186 -0
- package/packages/core/src/agent/catalog-selector.ts +103 -0
- package/packages/core/src/agent/{Compaction.ts → compaction.ts} +86 -63
- package/packages/core/src/agent/context-compiler.ts +689 -0
- package/packages/core/src/agent/conversation-store.ts +381 -0
- package/packages/core/src/agent/curator.ts +276 -0
- package/packages/core/src/agent/delegation-runtime.ts +241 -0
- package/packages/core/src/agent/goal-runner.ts +323 -0
- package/packages/core/src/agent/index.ts +17 -12
- package/packages/core/src/agent/llm-client.ts +266 -0
- package/packages/core/src/agent/llm-providers/anthropic.ts +264 -0
- package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
- package/packages/core/src/agent/{providers → llm-providers}/gemini.ts +98 -60
- package/packages/core/src/agent/llm-providers/groq.ts +5 -0
- package/packages/core/src/agent/llm-providers/hiveagents.ts +253 -0
- package/packages/core/src/agent/{providers → llm-providers}/interface.ts +73 -13
- package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
- package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
- package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
- package/packages/core/src/agent/llm-providers/modelscope.ts +5 -0
- package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
- package/packages/core/src/agent/{providers → llm-providers}/ollama.ts +31 -5
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +418 -0
- package/packages/core/src/agent/llm-providers/openai.ts +5 -0
- package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
- package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
- package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
- package/packages/core/src/agent/llm-providers/z-ai.ts +5 -0
- package/packages/core/src/agent/minimal-loadout.ts +47 -0
- package/packages/core/src/agent/playbook-selector.ts +119 -0
- package/packages/core/src/agent/{PromptBuilder.ts → prompt-builder.ts} +21 -22
- package/packages/core/src/{harness → agent}/proof-packet.ts +16 -21
- package/packages/core/src/agent/providers/index.ts +35 -16
- package/packages/core/src/agent/reflector.ts +320 -0
- package/packages/core/src/agent/routing-intent.ts +22 -0
- package/packages/core/src/{harness → agent}/run-epoch.ts +4 -3
- package/packages/core/src/{harness → agent}/run-store.ts +142 -81
- package/packages/core/src/agent/{Service.ts → service.ts} +37 -26
- package/packages/core/src/agent/skill-selector.ts +374 -0
- package/packages/core/src/agent/stuck-loop.ts +209 -0
- package/packages/core/src/agent/{selectors/ToolSelector.ts → tool-selector.ts} +188 -178
- package/packages/core/src/{ace/Tracer.ts → agent/tracer.ts} +37 -27
- package/packages/core/src/api/createAgent.test.ts +139 -27
- package/packages/core/src/api/createAgent.ts +232 -44
- package/packages/core/src/artifacts/store.ts +162 -0
- package/packages/core/src/canvas/canvas-manager.ts +161 -0
- package/packages/core/src/canvas/canvas.test.ts +8 -4
- package/packages/core/src/canvas/emitter.ts +131 -80
- package/packages/core/src/canvas/index.ts +1 -3
- package/packages/core/src/channels/base.ts +9 -1
- package/packages/core/src/channels/discord.ts +5 -4
- package/packages/core/src/channels/manager.ts +122 -30
- package/packages/core/src/channels/slack.ts +5 -4
- package/packages/core/src/channels/telegram.ts +36 -6
- package/packages/core/src/channels/webchat.ts +11 -10
- package/packages/core/src/channels/whatsapp.ts +23 -7
- package/packages/core/src/config/index.ts +13 -2
- package/packages/core/src/config/loader.ts +76 -29
- package/packages/core/src/ethics/EthicsGuard.test.ts +90 -36
- package/packages/core/src/ethics/EthicsGuard.ts +51 -47
- package/packages/core/src/events/agent-bus.ts +44 -68
- package/packages/core/src/events/channel-narration.ts +150 -0
- package/packages/core/src/events/narration.ts +82 -0
- package/packages/core/src/events/tool-narration.ts +62 -0
- package/packages/core/src/gateway/delegation-groups.ts +258 -0
- package/packages/core/src/{harness → gateway}/durable-queue.ts +102 -42
- package/packages/core/src/{harness → gateway}/job-store.ts +85 -48
- package/packages/core/src/gateway/lane-queue.ts +173 -0
- package/packages/core/src/gateway/notification-inbox.ts +57 -0
- package/packages/core/src/gateway/server.ts +1 -1
- package/packages/core/src/harness/index.ts +46 -27
- package/packages/core/src/index.ts +33 -27
- package/packages/core/src/mcp/hot-reload.ts +32 -23
- package/packages/core/src/mcp/index.ts +6 -3
- package/packages/core/src/mcp/singleton.ts +1 -4
- package/packages/core/src/mcp/tool-sync.ts +138 -0
- package/packages/core/src/memory/Scratchpad.test.ts +39 -20
- package/packages/core/src/memory/Scratchpad.ts +27 -34
- package/packages/core/src/multimodal/vision-service.ts +44 -38
- package/packages/core/src/resilience/retry.ts +95 -0
- package/packages/core/src/scheduler/CronScheduler.ts +334 -287
- package/packages/core/src/scheduler/index.ts +9 -7
- package/packages/core/src/scheduler/integration.ts +46 -26
- package/packages/core/src/scheduler/scheduler.test.ts +9 -13
- package/packages/core/src/scheduler/types.ts +7 -2
- package/packages/core/src/security/Pairing.ts +1 -1
- package/packages/core/src/skills/bundled/a2ui/a2ui_dashboard/SKILL.md +176 -0
- package/packages/core/src/skills/bundled/a2ui/a2ui_form/SKILL.md +202 -0
- package/packages/core/src/skills/bundled/a2ui/a2ui_interactive/SKILL.md +206 -0
- package/packages/core/src/skills/bundled/agents/agent_spawner/SKILL.md +173 -0
- package/packages/core/src/skills/bundled/agents/memory_manager/SKILL.md +143 -0
- package/packages/core/src/skills/bundled/agents/research_and_remember/SKILL.md +139 -0
- package/packages/core/src/skills/bundled/agents/task_orchestrator/SKILL.md +98 -0
- package/packages/core/src/skills/bundled/api/api_client/SKILL.md +132 -0
- package/packages/core/src/skills/bundled/cli/cli_pipeline/SKILL.md +135 -0
- package/packages/core/src/skills/bundled/cli/cli_safe_exec/SKILL.md +125 -0
- package/packages/core/src/skills/bundled/cli/software_engineering/SKILL.md +23 -0
- package/packages/core/src/skills/bundled/cron_manager/SKILL.md +188 -0
- package/packages/core/src/skills/bundled/cron_reminder/SKILL.md +112 -0
- package/packages/core/src/skills/bundled/filesystem/file_manager/SKILL.md +118 -0
- package/packages/core/src/skills/bundled/filesystem/file_read_and_summarize/SKILL.md +109 -0
- package/packages/core/src/skills/bundled/filesystem/file_writer/SKILL.md +129 -0
- package/packages/core/src/skills/bundled/filesystem/workspace_file_operator/SKILL.md +22 -0
- package/packages/core/src/skills/bundled/office/office_document_manager/SKILL.md +262 -0
- package/packages/core/src/skills/bundled/search_knowledge/capability_discovery/SKILL.md +75 -0
- package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +120 -0
- package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +109 -0
- package/packages/core/src/skills/bundled/web/web_monitor/SKILL.md +127 -0
- package/packages/core/src/skills/bundled/web/web_research/SKILL.md +119 -0
- package/packages/core/src/skills/bundled-data.generated.ts +731 -2678
- package/packages/core/src/skills/skills.test.ts +52 -11
- package/packages/core/src/{harness → storage}/boot-id.ts +5 -2
- package/packages/core/src/storage/bootstrap.ts +151 -0
- package/packages/core/src/storage/causal-events.ts +84 -0
- package/packages/core/src/storage/collections.ts +680 -0
- package/packages/core/src/storage/crypto.ts +205 -74
- package/packages/core/src/{harness/db-helpers.ts → storage/hive.ts} +63 -7
- package/packages/core/src/storage/hivedb.ts +61 -0
- package/packages/core/src/storage/index.ts +111 -18
- package/packages/core/src/storage/model-id.ts +53 -0
- package/packages/core/src/storage/onboarding.ts +540 -972
- package/packages/core/src/storage/reconcile.ts +238 -0
- package/packages/core/src/storage/seed.ts +572 -406
- package/packages/core/src/storage/usage.ts +285 -225
- package/packages/core/src/storage/user-email.ts +11 -0
- package/packages/core/src/swarm/AgentExecutor.ts +1 -1
- package/packages/core/src/swarm/EventBridge.ts +1 -1
- package/packages/core/src/swarm/index.ts +12 -9
- package/packages/core/src/tool-runtime/index.ts +146 -23
- package/packages/core/src/tool-runtime/tool-worker.ts +2 -2
- package/packages/core/src/tool-runtime/worker-tools.ts +27 -0
- package/packages/core/src/{canvas/a2ui-tools.ts → tools/a2ui/index.ts} +17 -8
- package/packages/core/src/tools/agents/get-available-models.ts +36 -54
- package/packages/core/src/tools/agents/index.ts +784 -292
- package/packages/core/src/tools/api/api-request.test.ts +164 -0
- package/packages/core/src/tools/api/api-request.ts +174 -0
- package/packages/core/src/tools/api/index.ts +16 -0
- package/packages/core/src/tools/cli/index.ts +4 -0
- package/packages/core/src/tools/core/index.ts +281 -112
- package/packages/core/src/tools/cron/index.ts +121 -124
- package/packages/core/src/tools/index.ts +63 -78
- package/packages/core/src/tools/office/office-escribir-xlsx.ts +3 -1
- package/packages/core/src/tools/types.ts +3 -1
- package/packages/core/src/tools/web/artifact-inspect.ts +23 -0
- package/packages/core/src/tools/web/browser-backend.ts +129 -0
- package/packages/core/src/tools/web/browser-screenshot.ts +26 -5
- package/packages/core/src/tools/web/browser-service.ts +80 -35
- package/packages/core/src/tools/web/browser-type.ts +3 -8
- package/packages/core/src/tools/web/index.ts +4 -4
- package/packages/core/src/tools/web/webview-backend.ts +412 -0
- package/packages/core/src/voice/index.ts +89 -63
- package/packages/core/src/workers/agent.worker.ts +2 -2
- package/packages/core/src/workers/workers.test.ts +3 -10
- package/scripts/bump-version.ts +248 -0
- package/scripts/generate-skill-bundle.ts +108 -0
- package/test/acceptance-checks.test.ts +403 -0
- package/test/agent-loop-terminal-synthesis.test.ts +32 -0
- package/test/browser-backend.test.ts +308 -0
- package/test/catalog-agents-stay-enabled.test.ts +117 -0
- package/test/causal-events.test.ts +117 -0
- package/test/compaction.test.ts +105 -0
- package/test/context-compiler.test.ts +269 -0
- package/test/curator.test.ts +130 -0
- package/test/durable-queue.test.ts +114 -0
- package/test/harness-barrel.test.ts +64 -0
- package/test/hive-helpers.test.ts +130 -0
- package/test/hivedb-search.test.ts +189 -0
- package/test/internal-turns.test.ts +166 -0
- package/test/job-idempotency.test.ts +68 -0
- package/test/job-retry-backoff.test.ts +184 -0
- package/test/job-store.test.ts +381 -0
- package/test/llm-retry.test.ts +97 -0
- package/test/memory-perf.test.ts +774 -0
- package/test/minimal-loadout.test.ts +78 -0
- package/test/model-catalog.test.ts +105 -0
- package/test/preload.ts +12 -0
- package/test/reflector.test.ts +320 -0
- package/test/retention-cap.test.ts +91 -0
- package/test/retired-capabilities-pruned.test.ts +192 -0
- package/test/run-store.test.ts +355 -0
- package/test/scratchpad.test.ts +74 -0
- package/test/secrets-durability.test.ts +119 -0
- package/test/seed-model-reseed.test.ts +155 -0
- package/test/setup-agent-seed.test.ts +264 -0
- package/test/tool-inventory.test.ts +65 -0
- package/test/tool-runtime.test.ts +258 -0
- package/test/tool-selector-runtime-tools.test.ts +117 -0
- package/test/toon.test.ts +429 -0
- package/tsconfig.json +2 -0
- package/packages/core/src/ace/Curator.ts +0 -158
- package/packages/core/src/ace/Reflector.ts +0 -200
- package/packages/core/src/ace/index.ts +0 -4
- package/packages/core/src/agent/AgentRunner.ts +0 -711
- package/packages/core/src/agent/ContextCompiler.ts +0 -567
- package/packages/core/src/agent/ContextGuard.ts +0 -91
- package/packages/core/src/agent/ConversationStore.ts +0 -254
- package/packages/core/src/agent/Hooks.ts +0 -166
- package/packages/core/src/agent/StuckLoop.ts +0 -133
- package/packages/core/src/agent/providers/LLMClient.ts +0 -149
- package/packages/core/src/agent/providers/anthropic.ts +0 -212
- package/packages/core/src/agent/providers/openai-compat.ts +0 -231
- package/packages/core/src/agent/selectors/PlaybookSelector.ts +0 -121
- package/packages/core/src/agent/selectors/SkillSelector.ts +0 -322
- package/packages/core/src/agent/selectors/index.ts +0 -6
- package/packages/core/src/auth/auth.ts +0 -121
- package/packages/core/src/auth/index.ts +0 -1
- package/packages/core/src/canvas/CanvasManager.ts +0 -390
- package/packages/core/src/canvas/canvas-tools.ts +0 -448
- package/packages/core/src/harness/collections.ts +0 -98
- package/packages/core/src/harness/goal-verifier.ts +0 -141
- package/packages/core/src/harness/harness.test.ts +0 -236
- package/packages/core/src/harness/reconcile.ts +0 -149
- package/packages/core/src/mcp/MCPToolAdapter.ts +0 -176
- package/packages/core/src/multimodal/VisionService.ts +0 -293
- package/packages/core/src/scheduler/dag/AgentExecutor.ts +0 -53
- package/packages/core/src/scheduler/dag/DAGScheduler.ts +0 -250
- package/packages/core/src/scheduler/dag/EventBridge.ts +0 -122
- package/packages/core/src/scheduler/dag/TaskGraph.ts +0 -192
- package/packages/core/src/scheduler/dag/TaskNode.ts +0 -97
- package/packages/core/src/scheduler/dag/TaskResult.ts +0 -22
- package/packages/core/src/scheduler/dag/errors.ts +0 -37
- package/packages/core/src/scheduler/dag/index.ts +0 -26
- package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +0 -97
- package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +0 -21
- package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +0 -46
- package/packages/core/src/storage/HiveDBStorage.ts +0 -64
- package/packages/core/src/storage/SQLiteStorage.ts +0 -414
- package/packages/core/src/storage/hiveSeed.ts +0 -308
- package/packages/core/src/storage/hiveStorage.test.ts +0 -38
- package/packages/core/src/storage/schema.ts +0 -689
- package/packages/core/src/storage/storage.test.ts +0 -37
- package/packages/core/src/swarm/AgentBus.ts +0 -460
- package/packages/core/src/swarm/EventBus.ts +0 -169
- package/packages/core/src/swarm/WorkerPool.ts +0 -236
- package/packages/core/src/tools/bridge-events.ts +0 -26
- package/packages/core/src/tools/canvas/index.ts +0 -375
- package/packages/core/src/tools/codebridge/index.ts +0 -342
- package/packages/core/src/tools/meeting/index.ts +0 -353
- package/packages/core/src/tools/projects/index.ts +0 -37
- package/packages/core/src/tools/projects/project-create.ts +0 -94
- package/packages/core/src/tools/projects/project-done.ts +0 -66
- package/packages/core/src/tools/projects/project-fail.ts +0 -66
- package/packages/core/src/tools/projects/project-list.ts +0 -96
- package/packages/core/src/tools/projects/project-update.ts +0 -72
- package/packages/core/src/tools/projects/task-create.ts +0 -68
- package/packages/core/src/tools/projects/task-evaluate.ts +0 -93
- package/packages/core/src/tools/projects/task-update.ts +0 -93
- package/packages/core/src/tools/voice/index.ts +0 -104
- package/packages/core/src/tools/web/api-request.test.ts +0 -170
- package/packages/core/src/tools/web/api-request.ts +0 -239
- package/test/setup-db.ts +0 -216
- /package/packages/core/src/agent/{NativeTools.ts → native-tools.ts} +0 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TOON Format Utility Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for TOON (Token-Oriented Object Notation) implementation
|
|
5
|
+
* using toon-format-parser library with native compression analysis
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, test, expect } from 'bun:test'
|
|
9
|
+
import {
|
|
10
|
+
stringify,
|
|
11
|
+
formatToolResult,
|
|
12
|
+
formatMCPResponse,
|
|
13
|
+
formatSkillOutput,
|
|
14
|
+
formatContext,
|
|
15
|
+
estimateTokens,
|
|
16
|
+
getCompressionAnalysis,
|
|
17
|
+
withToonFormat,
|
|
18
|
+
type ToonStringifyResult,
|
|
19
|
+
} from '../packages/core/src/utils/toon.ts'
|
|
20
|
+
|
|
21
|
+
describe('TOON Utility', () => {
|
|
22
|
+
describe('stringify()', () => {
|
|
23
|
+
test('convierte objeto simple a TOON', () => {
|
|
24
|
+
const data = { name: 'John', age: 30, city: 'New York' }
|
|
25
|
+
const result = stringify(data)
|
|
26
|
+
|
|
27
|
+
expect(result.content).toBeDefined()
|
|
28
|
+
expect(result.format).toBe('toon')
|
|
29
|
+
expect(result.originalSize).toBeGreaterThan(0)
|
|
30
|
+
expect(result.toonSize).toBeGreaterThan(0)
|
|
31
|
+
expect(typeof result.tokensSaved).toBe('number')
|
|
32
|
+
expect(typeof result.savingsPercent).toBe('number')
|
|
33
|
+
expect(typeof result.costSaved).toBe('number')
|
|
34
|
+
// New complete metrics
|
|
35
|
+
expect(typeof result.jsonBytes).toBe('number')
|
|
36
|
+
expect(typeof result.toonBytes).toBe('number')
|
|
37
|
+
expect(typeof result.savedBytes).toBe('number')
|
|
38
|
+
expect(typeof result.savedPercent).toBe('number')
|
|
39
|
+
expect(typeof result.jsonTokens).toBe('number')
|
|
40
|
+
expect(typeof result.toonTokens).toBe('number')
|
|
41
|
+
expect(typeof result.savedTokensPercent).toBe('number')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('convierte objeto anidado a TOON', () => {
|
|
45
|
+
const data = {
|
|
46
|
+
user: {
|
|
47
|
+
name: 'Alice',
|
|
48
|
+
email: 'alice@example.com',
|
|
49
|
+
preferences: {
|
|
50
|
+
theme: 'dark',
|
|
51
|
+
notifications: true,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
projects: [
|
|
55
|
+
{ id: 1, name: 'Project A', status: 'active' },
|
|
56
|
+
{ id: 2, name: 'Project B', status: 'completed' },
|
|
57
|
+
],
|
|
58
|
+
}
|
|
59
|
+
const result = stringify(data)
|
|
60
|
+
|
|
61
|
+
expect(result.content).toBeDefined()
|
|
62
|
+
expect(result.format).toBe('toon')
|
|
63
|
+
expect(result.toonSize).toBeGreaterThan(0)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('calcula tokens ahorrados correctamente', () => {
|
|
67
|
+
const data = {
|
|
68
|
+
success: true,
|
|
69
|
+
data: {
|
|
70
|
+
emails: [
|
|
71
|
+
{ id: '1', from: 'alice@example.com', subject: 'Hello' },
|
|
72
|
+
{ id: '2', from: 'bob@example.com', subject: 'World' },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
const result = stringify(data)
|
|
77
|
+
|
|
78
|
+
// savingsPercent debería ser >= 0 (usamos Math.max)
|
|
79
|
+
expect(result.savingsPercent).toBeGreaterThanOrEqual(0)
|
|
80
|
+
expect(result.tokensSaved).toBeGreaterThanOrEqual(0)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('calcula costo USD con modelo', () => {
|
|
84
|
+
const data = { message: 'Hello World', count: 42 }
|
|
85
|
+
const result = stringify(data, 'gemini-2.0-flash-exp')
|
|
86
|
+
|
|
87
|
+
expect(result.costSaved).toBeGreaterThanOrEqual(0)
|
|
88
|
+
|
|
89
|
+
// Si hay ahorro de tokens, debería haber ahorro de costo
|
|
90
|
+
if (result.tokensSaved > 0) {
|
|
91
|
+
expect(result.costSaved).toBeGreaterThan(0)
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('maneja arrays grandes', () => {
|
|
96
|
+
const data = Array.from({ length: 100 }, (_, i) => ({
|
|
97
|
+
id: i,
|
|
98
|
+
name: `Item ${i}`,
|
|
99
|
+
value: Math.random(),
|
|
100
|
+
}))
|
|
101
|
+
const result = stringify(data)
|
|
102
|
+
|
|
103
|
+
expect(result.content).toBeDefined()
|
|
104
|
+
expect(result.format).toBe('toon')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('maneja objetos vacíos', () => {
|
|
108
|
+
const result = stringify({})
|
|
109
|
+
|
|
110
|
+
expect(result.content).toBeDefined()
|
|
111
|
+
expect(result.format).toBe('toon')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('maneja valores null y undefined', () => {
|
|
115
|
+
const data = {
|
|
116
|
+
nullValue: null,
|
|
117
|
+
undefinedValue: undefined,
|
|
118
|
+
stringValue: 'test',
|
|
119
|
+
}
|
|
120
|
+
const result = stringify(data)
|
|
121
|
+
|
|
122
|
+
expect(result.content).toBeDefined()
|
|
123
|
+
expect(result.format).toBe('toon')
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('maneja primitivos', () => {
|
|
127
|
+
expect(stringify('hello').content).toBeDefined()
|
|
128
|
+
expect(stringify(42).content).toBeDefined()
|
|
129
|
+
expect(stringify(true).content).toBeDefined()
|
|
130
|
+
expect(stringify(null).content).toBeDefined()
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
describe('formatToolResult()', () => {
|
|
135
|
+
test('formatea resultado de herramienta', () => {
|
|
136
|
+
const toolResult = {
|
|
137
|
+
success: true,
|
|
138
|
+
data: { items: [1, 2, 3] },
|
|
139
|
+
timestamp: new Date().toISOString(),
|
|
140
|
+
}
|
|
141
|
+
const result = formatToolResult(toolResult, 'gpt-4o-mini')
|
|
142
|
+
|
|
143
|
+
expect(typeof result).toBe('string')
|
|
144
|
+
expect(result.length).toBeGreaterThan(0)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test('formatea error de herramienta', () => {
|
|
148
|
+
const errorResult = {
|
|
149
|
+
error: true,
|
|
150
|
+
tool: 'test_tool',
|
|
151
|
+
message: 'Something went wrong',
|
|
152
|
+
timestamp: new Date().toISOString(),
|
|
153
|
+
}
|
|
154
|
+
const result = formatToolResult(errorResult)
|
|
155
|
+
|
|
156
|
+
expect(typeof result).toBe('string')
|
|
157
|
+
expect(result).toContain('error')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('maneja resultado sin modelo', () => {
|
|
161
|
+
const result = formatToolResult({ data: 'test' })
|
|
162
|
+
|
|
163
|
+
expect(typeof result).toBe('string')
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
describe('formatMCPResponse()', () => {
|
|
168
|
+
test('formatea respuesta MCP', () => {
|
|
169
|
+
const mcpResponse = {
|
|
170
|
+
content: [
|
|
171
|
+
{ type: 'text', text: 'Hello from MCP' },
|
|
172
|
+
],
|
|
173
|
+
isError: false,
|
|
174
|
+
}
|
|
175
|
+
const result = formatMCPResponse(mcpResponse, 'gemini-2.0-flash-exp')
|
|
176
|
+
|
|
177
|
+
expect(typeof result).toBe('string')
|
|
178
|
+
expect(result.length).toBeGreaterThan(0)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test('formatea lista de herramientas MCP', () => {
|
|
182
|
+
const toolsResponse = {
|
|
183
|
+
tools: [
|
|
184
|
+
{
|
|
185
|
+
name: 'search_web',
|
|
186
|
+
description: 'Search the web',
|
|
187
|
+
inputSchema: { type: 'object', properties: { query: { type: 'string' } } },
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: 'send_email',
|
|
191
|
+
description: 'Send an email',
|
|
192
|
+
inputSchema: { type: 'object', properties: { to: { type: 'string' } } },
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
}
|
|
196
|
+
const result = formatMCPResponse(toolsResponse)
|
|
197
|
+
|
|
198
|
+
expect(typeof result).toBe('string')
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
describe('formatSkillOutput()', () => {
|
|
203
|
+
test('formatea output de skill', () => {
|
|
204
|
+
const skillOutput = {
|
|
205
|
+
skill: 'pdf',
|
|
206
|
+
result: { pages: 10, text: 'Extracted text...' },
|
|
207
|
+
}
|
|
208
|
+
const result = formatSkillOutput(skillOutput, 'claude-3-5-sonnet')
|
|
209
|
+
|
|
210
|
+
expect(typeof result).toBe('string')
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe('formatContext()', () => {
|
|
215
|
+
test('formatea contexto del agente', () => {
|
|
216
|
+
const contextData = {
|
|
217
|
+
ethics: ['Be helpful', 'Be honest'],
|
|
218
|
+
notes: { key1: 'value1', key2: 'value2' },
|
|
219
|
+
projects: { active: 3, completed: 10 },
|
|
220
|
+
}
|
|
221
|
+
const result = formatContext(contextData)
|
|
222
|
+
|
|
223
|
+
expect(typeof result).toBe('string')
|
|
224
|
+
expect(result.length).toBeGreaterThan(0)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
test('formatea scratchpad data', () => {
|
|
228
|
+
const scratchpadData = {
|
|
229
|
+
currentTask: 'Research hotels',
|
|
230
|
+
lastStep: 'Searched Google',
|
|
231
|
+
nextStep: 'Compare prices',
|
|
232
|
+
}
|
|
233
|
+
const result = formatContext(scratchpadData)
|
|
234
|
+
|
|
235
|
+
expect(typeof result).toBe('string')
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
describe('estimateTokens()', () => {
|
|
240
|
+
test('estima tokens para texto vacío', () => {
|
|
241
|
+
expect(estimateTokens('')).toBe(0)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
test('estima tokens para texto corto', () => {
|
|
245
|
+
expect(estimateTokens('Hello World')).toBe(3) // ceil(11/4) = 3
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('estima tokens para texto largo', () => {
|
|
249
|
+
const text = 'A'.repeat(1000)
|
|
250
|
+
expect(estimateTokens(text)).toBe(250) // 1000/4 = 250
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
test('estima tokens para JSON', () => {
|
|
254
|
+
const json = JSON.stringify({ name: 'test', value: 42 })
|
|
255
|
+
expect(estimateTokens(json)).toBeGreaterThan(0)
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
describe('getCompressionAnalysis()', () => {
|
|
260
|
+
test('retorna análisis de compresión completo', () => {
|
|
261
|
+
const data = {
|
|
262
|
+
users: [
|
|
263
|
+
{ id: 1, name: 'Alice', email: 'alice@example.com' },
|
|
264
|
+
{ id: 2, name: 'Bob', email: 'bob@example.com' },
|
|
265
|
+
],
|
|
266
|
+
metadata: {
|
|
267
|
+
total: 2,
|
|
268
|
+
page: 1,
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
const analysis = getCompressionAnalysis(data)
|
|
272
|
+
|
|
273
|
+
expect(analysis).toBeDefined()
|
|
274
|
+
expect(analysis.jsonBytes).toBeGreaterThan(0)
|
|
275
|
+
expect(analysis.toonBytes).toBeGreaterThan(0)
|
|
276
|
+
expect(analysis.jsonTokens).toBeGreaterThan(0)
|
|
277
|
+
expect(analysis.toonTokens).toBeGreaterThan(0)
|
|
278
|
+
expect(typeof analysis.savedTokens).toBe('number')
|
|
279
|
+
expect(typeof analysis.savedPercent).toBe('number')
|
|
280
|
+
expect(typeof analysis.savedTokensPercent).toBe('number')
|
|
281
|
+
expect(typeof analysis.savedBytes).toBe('number')
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
test('muestra ahorro significativo en datos estructurados', () => {
|
|
285
|
+
const data = Array.from({ length: 50 }, (_, i) => ({
|
|
286
|
+
id: i,
|
|
287
|
+
name: `Item ${i}`,
|
|
288
|
+
description: `Description for item ${i}`,
|
|
289
|
+
}))
|
|
290
|
+
const analysis = getCompressionAnalysis(data)
|
|
291
|
+
|
|
292
|
+
// En datos estructurados grandes, TOON debería ahorrar
|
|
293
|
+
expect(analysis.savedTokens).toBeGreaterThan(0)
|
|
294
|
+
expect(analysis.savedPercent).toBeGreaterThan(0)
|
|
295
|
+
})
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
describe('withToonFormat()', () => {
|
|
299
|
+
test('formatea resultado de función asíncrona', async () => {
|
|
300
|
+
const mockFn = async () => ({ result: 'success', data: [1, 2, 3] })
|
|
301
|
+
const result = await withToonFormat('test_tool', mockFn, 'gpt-4o')
|
|
302
|
+
|
|
303
|
+
expect(typeof result).toBe('string')
|
|
304
|
+
expect(result.length).toBeGreaterThan(0)
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
test('maneja error de función asíncrona', async () => {
|
|
308
|
+
const mockFn = async () => {
|
|
309
|
+
throw new Error('Test error')
|
|
310
|
+
}
|
|
311
|
+
const result = await withToonFormat('failing_tool', mockFn)
|
|
312
|
+
|
|
313
|
+
expect(typeof result).toBe('string')
|
|
314
|
+
expect(result).toContain('error')
|
|
315
|
+
expect(result).toContain('Test error')
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
test('registra duración de ejecución', async () => {
|
|
319
|
+
const mockFn = async () => {
|
|
320
|
+
await new Promise(resolve => setTimeout(resolve, 10))
|
|
321
|
+
return { done: true }
|
|
322
|
+
}
|
|
323
|
+
const result = await withToonFormat('slow_tool', mockFn)
|
|
324
|
+
|
|
325
|
+
expect(typeof result).toBe('string')
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
describe('Integration: Real-world scenarios', () => {
|
|
330
|
+
test('simula flujo real: tool result → LLM message', () => {
|
|
331
|
+
// 1. Tool ejecuta y retorna dato
|
|
332
|
+
const toolResult = {
|
|
333
|
+
success: true,
|
|
334
|
+
url: 'https://example.com',
|
|
335
|
+
content: 'Page content here',
|
|
336
|
+
statusCode: 200,
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// 2. Formatear para LLM
|
|
340
|
+
const formattedForLLM = formatToolResult(toolResult, 'gpt-4o-mini')
|
|
341
|
+
|
|
342
|
+
// 3. Verificar formato
|
|
343
|
+
expect(typeof formattedForLLM).toBe('string')
|
|
344
|
+
expect(formattedForLLM.length).toBeGreaterThan(0)
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
test('simula respuesta MCP compleja', () => {
|
|
348
|
+
const mcpResponse = {
|
|
349
|
+
content: [
|
|
350
|
+
{
|
|
351
|
+
type: 'text',
|
|
352
|
+
text: 'Found 5 emails',
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
type: 'json',
|
|
356
|
+
data: {
|
|
357
|
+
emails: [
|
|
358
|
+
{ id: '1', from: 'a@b.com', subject: 'Test 1' },
|
|
359
|
+
{ id: '2', from: 'c@d.com', subject: 'Test 2' },
|
|
360
|
+
],
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
metadata: {
|
|
365
|
+
server: 'gmail',
|
|
366
|
+
timestamp: new Date().toISOString(),
|
|
367
|
+
},
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const formatted = formatMCPResponse(mcpResponse)
|
|
371
|
+
|
|
372
|
+
expect(typeof formatted).toBe('string')
|
|
373
|
+
expect(formatted.length).toBeGreaterThan(0)
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
test('comparación JSON vs TOON en datos típicos', () => {
|
|
377
|
+
const typicalData = {
|
|
378
|
+
agent: {
|
|
379
|
+
id: 'coordinator',
|
|
380
|
+
name: 'Main Agent',
|
|
381
|
+
status: 'thinking',
|
|
382
|
+
},
|
|
383
|
+
conversation: {
|
|
384
|
+
threadId: 'thread-123',
|
|
385
|
+
messages: [
|
|
386
|
+
{ role: 'user', content: 'Hello' },
|
|
387
|
+
{ role: 'assistant', content: 'Hi there!' },
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
tools: [
|
|
391
|
+
{ name: 'search', status: 'available' },
|
|
392
|
+
{ name: 'browser', status: 'busy' },
|
|
393
|
+
],
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const jsonSize = JSON.stringify(typicalData).length
|
|
397
|
+
const toonResult = stringify(typicalData)
|
|
398
|
+
|
|
399
|
+
expect(toonResult.originalSize).toBe(jsonSize)
|
|
400
|
+
expect(toonResult.toonSize).toBeGreaterThan(0)
|
|
401
|
+
expect(toonResult.savingsPercent).toBeGreaterThanOrEqual(0)
|
|
402
|
+
})
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
describe('Performance', () => {
|
|
406
|
+
test('stringify es rápido para objetos pequeños', () => {
|
|
407
|
+
const data = { key: 'value', num: 42 }
|
|
408
|
+
const start = performance.now()
|
|
409
|
+
stringify(data)
|
|
410
|
+
const duration = performance.now() - start
|
|
411
|
+
|
|
412
|
+
expect(duration).toBeLessThan(10) // < 10ms
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
test('stringify es razonable para objetos grandes', () => {
|
|
416
|
+
const data = Array.from({ length: 500 }, (_, i) => ({
|
|
417
|
+
id: i,
|
|
418
|
+
name: `Item ${i}`,
|
|
419
|
+
description: `Description for item ${i}`,
|
|
420
|
+
}))
|
|
421
|
+
|
|
422
|
+
const start = performance.now()
|
|
423
|
+
stringify(data)
|
|
424
|
+
const duration = performance.now() - start
|
|
425
|
+
|
|
426
|
+
expect(duration).toBeLessThan(100) // < 100ms
|
|
427
|
+
})
|
|
428
|
+
})
|
|
429
|
+
})
|
package/tsconfig.json
CHANGED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ACE Curator — converts reflections into playbook rules.
|
|
3
|
-
*
|
|
4
|
-
* Runs after the Reflector. Performs incremental edits to the playbook:
|
|
5
|
-
* - New insights → new rules
|
|
6
|
-
* - Repeated patterns → increment helpful_count
|
|
7
|
-
* - Contradicted rules → increment harmful_count or deactivate
|
|
8
|
-
* - Deactivate rules where harmful_count > helpful_count
|
|
9
|
-
* - Archive unused workers
|
|
10
|
-
*
|
|
11
|
-
* Never rewrites the whole playbook — only incremental edits.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { logger } from "../utils/logger.ts"
|
|
15
|
-
|
|
16
|
-
const log = logger.child("curator")
|
|
17
|
-
|
|
18
|
-
const DAYS_BEFORE_ARCHIVE = 14 // archive workers not used in N days
|
|
19
|
-
const MAX_HARMFUL_BEFORE_PRUNE = 3
|
|
20
|
-
|
|
21
|
-
/** Entry point — called by reflector.ts after it inserts new reflections */
|
|
22
|
-
export async function runCurator(): Promise<void> {
|
|
23
|
-
try {
|
|
24
|
-
const { getDb } = await import("../storage/SQLiteStorage.ts")
|
|
25
|
-
const db = getDb()
|
|
26
|
-
|
|
27
|
-
// Process unprocessed reflections (those newer than last run)
|
|
28
|
-
const lastProcessed = (db.query<any, []>(
|
|
29
|
-
"SELECT COALESCE(MAX(source_reflection_id), 0) as mid FROM playbook"
|
|
30
|
-
).get() as any)?.mid ?? 0
|
|
31
|
-
|
|
32
|
-
const reflections = (db.query as any)(
|
|
33
|
-
"SELECT * FROM reflections WHERE id > ? ORDER BY id ASC"
|
|
34
|
-
).all(lastProcessed)
|
|
35
|
-
|
|
36
|
-
if (reflections.length === 0) {
|
|
37
|
-
log.debug("[curator] No new reflections to process")
|
|
38
|
-
} else {
|
|
39
|
-
log.info(`[curator] Processing ${reflections.length} new reflections`)
|
|
40
|
-
for (const reflection of reflections) {
|
|
41
|
-
processReflection(db, reflection)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Prune rules where harmful > helpful (consistently bad rules)
|
|
46
|
-
db.query(`
|
|
47
|
-
UPDATE playbook
|
|
48
|
-
SET active = 0, updated_at = unixepoch()
|
|
49
|
-
WHERE active = 1
|
|
50
|
-
AND harmful_count > helpful_count
|
|
51
|
-
AND harmful_count >= ?
|
|
52
|
-
`).run(MAX_HARMFUL_BEFORE_PRUNE)
|
|
53
|
-
|
|
54
|
-
// Archive unused workers
|
|
55
|
-
const cutoff = Math.floor(Date.now() / 1000) - (DAYS_BEFORE_ARCHIVE * 86400)
|
|
56
|
-
const staleworkers = (db.query as any)(`
|
|
57
|
-
SELECT a.id, a.name
|
|
58
|
-
FROM agents a
|
|
59
|
-
WHERE a.role = 'worker'
|
|
60
|
-
AND a.status != 'archived'
|
|
61
|
-
AND a.enabled = 1
|
|
62
|
-
AND (
|
|
63
|
-
SELECT MAX(t.created_at) FROM traces t WHERE t.agent_id = a.id
|
|
64
|
-
) < ?
|
|
65
|
-
`).all(cutoff)
|
|
66
|
-
|
|
67
|
-
for (const worker of staleworkers) {
|
|
68
|
-
db.query(
|
|
69
|
-
"UPDATE agents SET status = 'archived', updated_at = unixepoch() WHERE id = ?"
|
|
70
|
-
).run(worker.id)
|
|
71
|
-
|
|
72
|
-
// Add playbook note about archival
|
|
73
|
-
addOrUpdateRule(db, {
|
|
74
|
-
rule: `Worker '${worker.name}' was archived due to inactivity (>${DAYS_BEFORE_ARCHIVE} days unused).`,
|
|
75
|
-
category: "agent_creation",
|
|
76
|
-
applicable_to: null,
|
|
77
|
-
sourceReflectionId: null,
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
log.info(`[curator] Archived inactive worker: ${worker.name} (${worker.id})`)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
log.info("[curator] Playbook updated")
|
|
84
|
-
} catch (err) {
|
|
85
|
-
log.warn("[curator] Error:", err)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ─── Process a single reflection ─────────────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
function processReflection(db: any, reflection: any): void {
|
|
92
|
-
const category = mapInsightTypeToCategory(reflection.insight_type)
|
|
93
|
-
|
|
94
|
-
// Check if a similar rule already exists (fuzzy check by first 60 chars)
|
|
95
|
-
const prefix = reflection.description.substring(0, 60)
|
|
96
|
-
const existing = (db.query as any)(
|
|
97
|
-
"SELECT id, helpful_count FROM playbook WHERE rule LIKE ? AND active = 1 LIMIT 1"
|
|
98
|
-
).get(`${prefix}%`)
|
|
99
|
-
|
|
100
|
-
if (existing) {
|
|
101
|
-
// Reinforce existing rule
|
|
102
|
-
db.query(
|
|
103
|
-
"UPDATE playbook SET helpful_count = helpful_count + 1, updated_at = unixepoch() WHERE id = ?"
|
|
104
|
-
).run(existing.id)
|
|
105
|
-
return
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Insert new rule
|
|
109
|
-
db.query(`
|
|
110
|
-
INSERT INTO playbook (rule, category, applicable_to, helpful_count, source_reflection_id)
|
|
111
|
-
VALUES (?, ?, ?, 1, ?)
|
|
112
|
-
`).run(
|
|
113
|
-
reflection.description,
|
|
114
|
-
category,
|
|
115
|
-
reflection.affected_tools
|
|
116
|
-
? JSON.stringify(JSON.parse(reflection.affected_tools))
|
|
117
|
-
: null,
|
|
118
|
-
reflection.id,
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function mapInsightTypeToCategory(
|
|
123
|
-
type: string
|
|
124
|
-
): "tool_selection" | "response_quality" | "error_avoidance" | "optimization" | "agent_creation" {
|
|
125
|
-
const map: Record<string, any> = {
|
|
126
|
-
success_pattern: "tool_selection",
|
|
127
|
-
failure_pattern: "error_avoidance",
|
|
128
|
-
optimization: "optimization",
|
|
129
|
-
ethics_violation: "error_avoidance",
|
|
130
|
-
}
|
|
131
|
-
return map[type] ?? "optimization"
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function addOrUpdateRule(
|
|
135
|
-
db: any,
|
|
136
|
-
opts: {
|
|
137
|
-
rule: string
|
|
138
|
-
category: string
|
|
139
|
-
applicable_to: string | null
|
|
140
|
-
sourceReflectionId: number | null
|
|
141
|
-
}
|
|
142
|
-
): void {
|
|
143
|
-
const prefix = opts.rule.substring(0, 60)
|
|
144
|
-
const existing = (db.query as any)(
|
|
145
|
-
"SELECT id FROM playbook WHERE rule LIKE ? LIMIT 1"
|
|
146
|
-
).get(`${prefix}%`)
|
|
147
|
-
|
|
148
|
-
if (existing) {
|
|
149
|
-
db.query(
|
|
150
|
-
"UPDATE playbook SET helpful_count = helpful_count + 1, updated_at = unixepoch() WHERE id = ?"
|
|
151
|
-
).run(existing.id)
|
|
152
|
-
} else {
|
|
153
|
-
db.query(`
|
|
154
|
-
INSERT INTO playbook (rule, category, applicable_to, helpful_count, source_reflection_id)
|
|
155
|
-
VALUES (?, ?, ?, 1, ?)
|
|
156
|
-
`).run(opts.rule, opts.category, opts.applicable_to, opts.sourceReflectionId)
|
|
157
|
-
}
|
|
158
|
-
}
|