@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
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Scheduler — cron con Croner sobre HiveDB.
|
|
3
|
+
*
|
|
4
|
+
* Soporta jobs recurrentes y de una sola vez. La persistencia pasó de SQLite a
|
|
5
|
+
* las colecciones `cronJobs` / `taskRuns` de HiveDB en 0.1.5.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export { CronScheduler } from "./CronScheduler";
|
|
9
|
-
|
|
8
|
+
export { CronScheduler } from "./CronScheduler.ts";
|
|
9
|
+
// `executeScheduledTask` dejó de ser público: la ejecución entra por
|
|
10
|
+
// `createTaskHandler()`, que es lo que el scheduler engancha.
|
|
11
|
+
export { createTaskHandler, notifyTaskCompletion, setSchedulerForCleanup } from "./integration.ts";
|
|
10
12
|
export type {
|
|
11
13
|
CronJob,
|
|
12
14
|
TaskRun,
|
|
@@ -19,4 +21,4 @@ export type {
|
|
|
19
21
|
TaskStatus,
|
|
20
22
|
TaskRunStatus,
|
|
21
23
|
CronerOptions,
|
|
22
|
-
} from "./types";
|
|
24
|
+
} from "./types.ts";
|
|
@@ -7,18 +7,20 @@
|
|
|
7
7
|
|
|
8
8
|
import type { CronJob, CronJobExecutionResult } from "./types";
|
|
9
9
|
import { logger } from "../utils/logger";
|
|
10
|
-
import {
|
|
11
|
-
import { buildAgentLoop } from "../agent/AgentRunner";
|
|
10
|
+
import { buildAgentLoop } from "../agent/agent-loop";
|
|
12
11
|
import { resolveAgentId } from "../storage/onboarding";
|
|
13
12
|
import { sendToUserChannel } from "../gateway/channel-notify";
|
|
14
|
-
import {
|
|
13
|
+
import { getNarration } from "../events/tool-narration.ts";
|
|
14
|
+
import { addMessage } from "../agent/conversation-store";
|
|
15
15
|
import { resolveBestChannel } from "../tools/cron/index";
|
|
16
|
+
import { col } from "../storage/hive";
|
|
17
|
+
import type { UserDoc, CronJobDoc } from "../storage/collections";
|
|
16
18
|
|
|
17
19
|
const log = logger.child("SchedulerIntegration");
|
|
18
20
|
|
|
19
|
-
let _scheduler: { runCleanup(): void } | null = null;
|
|
21
|
+
let _scheduler: { runCleanup(): Promise<void> } | null = null;
|
|
20
22
|
|
|
21
|
-
export function setSchedulerForCleanup(scheduler: { runCleanup(): void }): void {
|
|
23
|
+
export function setSchedulerForCleanup(scheduler: { runCleanup(): Promise<void> }): void {
|
|
22
24
|
_scheduler = scheduler;
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -32,7 +34,7 @@ export function setSchedulerForCleanup(scheduler: { runCleanup(): void }): void
|
|
|
32
34
|
* 4. Executes the tool if tool_name is specified
|
|
33
35
|
* 5. Returns the agent response
|
|
34
36
|
*/
|
|
35
|
-
|
|
37
|
+
async function executeScheduledTask(job: CronJob): Promise<CronJobExecutionResult> {
|
|
36
38
|
log.info(`[execute] Processing job "${job.name}" (${job.id})`);
|
|
37
39
|
|
|
38
40
|
try {
|
|
@@ -52,7 +54,7 @@ export async function executeScheduledTask(job: CronJob): Promise<CronJobExecuti
|
|
|
52
54
|
|
|
53
55
|
if (payload._internal === true && (payload as any).action === "cleanup") {
|
|
54
56
|
if (_scheduler) {
|
|
55
|
-
_scheduler.runCleanup();
|
|
57
|
+
await _scheduler.runCleanup();
|
|
56
58
|
} else {
|
|
57
59
|
log.warn("[execute] Cleanup job fired but scheduler instance not available");
|
|
58
60
|
}
|
|
@@ -71,18 +73,19 @@ export async function executeScheduledTask(job: CronJob): Promise<CronJobExecuti
|
|
|
71
73
|
};
|
|
72
74
|
|
|
73
75
|
let targetAgentId: string | null = job.agent_id || null;
|
|
74
|
-
|
|
76
|
+
|
|
75
77
|
if (!targetAgentId) {
|
|
76
|
-
targetAgentId = resolveAgentId(null);
|
|
78
|
+
targetAgentId = await resolveAgentId(null);
|
|
77
79
|
log.debug(`[execute] No agent specified, routing to Coordinator: ${targetAgentId}`);
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
const usersCol = await col<UserDoc>("users");
|
|
83
|
+
const userEntry = (await usersCol.scan({ limit: 1 }))[0];
|
|
84
|
+
const user = userEntry ? {
|
|
85
|
+
id: userEntry.doc.id,
|
|
86
|
+
timezone: userEntry.doc.timezone || "UTC",
|
|
87
|
+
language: userEntry.doc.language,
|
|
88
|
+
} : undefined;
|
|
86
89
|
|
|
87
90
|
const userTimezone = user?.timezone || "UTC";
|
|
88
91
|
const userLanguage = user?.language || "en";
|
|
@@ -124,7 +127,24 @@ ${prompt || `Execute tool: ${job.tool_name}`}`;
|
|
|
124
127
|
|
|
125
128
|
const agentChannel = (job.channel && job.channel !== "system")
|
|
126
129
|
? job.channel
|
|
127
|
-
: resolveBestChannel(user?.id || "");
|
|
130
|
+
: await resolveBestChannel(user?.id || "");
|
|
131
|
+
|
|
132
|
+
// Narrate progress to the user's channel as the task runs, same as a
|
|
133
|
+
// live chat turn (server.ts onStep) — without this, scheduled tasks
|
|
134
|
+
// execute silently and only the final completion message is sent.
|
|
135
|
+
const onStep = async (step: { type: string; message?: string; toolName?: string }) => {
|
|
136
|
+
if (!agentChannel) return;
|
|
137
|
+
try {
|
|
138
|
+
if (step.type === "tool_call" && step.toolName) {
|
|
139
|
+
await sendToUserChannel(agentChannel, user?.id || "", getNarration(step.toolName));
|
|
140
|
+
} else if (step.type === "text" && step.message) {
|
|
141
|
+
const trimmed = step.message.trim();
|
|
142
|
+
if (trimmed) await sendToUserChannel(agentChannel, user?.id || "", trimmed);
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
log.warn(`[execute] Narration send failed: ${(err as Error).message}`);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
128
148
|
|
|
129
149
|
const messages = [{ role: "user", content: contextPrompt }];
|
|
130
150
|
const stream = agentLoop.stream({ messages }, {
|
|
@@ -136,6 +156,7 @@ ${prompt || `Execute tool: ${job.tool_name}`}`;
|
|
|
136
156
|
system_prompt: undefined,
|
|
137
157
|
raw_user_message: contextPrompt,
|
|
138
158
|
},
|
|
159
|
+
onStep,
|
|
139
160
|
});
|
|
140
161
|
|
|
141
162
|
let response = "";
|
|
@@ -194,22 +215,21 @@ export async function notifyTaskCompletion(
|
|
|
194
215
|
response?: string,
|
|
195
216
|
error?: string
|
|
196
217
|
): Promise<void> {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
const task = db.query(
|
|
200
|
-
"SELECT channel, agent_id FROM cron_jobs WHERE id = ?"
|
|
201
|
-
).get(taskId) as { channel: string; agent_id: string | null } | undefined;
|
|
218
|
+
const cronJobsCol = await col<CronJobDoc>("cronJobs");
|
|
219
|
+
const taskEntry = await cronJobsCol.get(taskId);
|
|
202
220
|
|
|
203
|
-
if (!
|
|
221
|
+
if (!taskEntry) {
|
|
204
222
|
log.warn(`[notify] Job "${taskId}" not found`);
|
|
205
223
|
return;
|
|
206
224
|
}
|
|
225
|
+
const task = taskEntry.doc;
|
|
207
226
|
|
|
208
|
-
const
|
|
209
|
-
const
|
|
227
|
+
const usersCol = await col<UserDoc>("users");
|
|
228
|
+
const userEntry = (await usersCol.scan({ limit: 1 }))[0];
|
|
229
|
+
const userId = userEntry?.id || "";
|
|
210
230
|
|
|
211
231
|
const explicitChannel = task.channel && task.channel !== "system" ? task.channel : undefined;
|
|
212
|
-
const notifyChannel = resolveBestChannel(userId, explicitChannel) || "webchat";
|
|
232
|
+
const notifyChannel = (await resolveBestChannel(userId, explicitChannel)) || "webchat";
|
|
213
233
|
log.info(`[notifyTaskCompletion] task.channel=${task.channel} explicit=${explicitChannel} resolved=${notifyChannel}`);
|
|
214
234
|
|
|
215
235
|
const status = success ? "✅" : "❌";
|
|
@@ -220,7 +240,7 @@ export async function notifyTaskCompletion(
|
|
|
220
240
|
log.info(`[notify] Sending notification to ${notifyChannel}: "${message.slice(0, 50)}..."`);
|
|
221
241
|
|
|
222
242
|
try {
|
|
223
|
-
addMessage(userId, "assistant", message, { channel: notifyChannel });
|
|
243
|
+
await addMessage(userId, "assistant", message, { channel: notifyChannel });
|
|
224
244
|
} catch (e) {
|
|
225
245
|
log.warn(`[notify] Failed to persist notification to DB: ${(e as Error).message}`);
|
|
226
246
|
}
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
import { CronScheduler } from "./CronScheduler.ts";
|
|
3
|
-
import { getTestDb, setupTestDb, teardownTestDb } from "../../../../test/setup-db.ts";
|
|
1
|
+
process.env.HIVE_DB_PATH = ":memory:";
|
|
4
2
|
|
|
5
|
-
describe
|
|
6
|
-
|
|
7
|
-
setupTestDb();
|
|
8
|
-
});
|
|
3
|
+
import { describe, expect, it } from "bun:test";
|
|
4
|
+
import { CronScheduler } from "./CronScheduler.ts";
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
// `new CronScheduler(db, handler)` pasó a `new CronScheduler(handler)`: los jobs
|
|
7
|
+
// viven en la colección `cronJobs` de HiveDB y el scheduler la abre solo, así
|
|
8
|
+
// que ya no recibe un handle de base.
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const scheduler = new CronScheduler(
|
|
10
|
+
describe("CronScheduler", () => {
|
|
11
|
+
it("creates a scheduler instance with a handler", () => {
|
|
12
|
+
const scheduler = new CronScheduler(async () => ({ success: true }));
|
|
17
13
|
expect(scheduler).toBeDefined();
|
|
18
14
|
});
|
|
19
15
|
});
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* All names use "CronJob" terminology (formerly ScheduledTask).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { Database } from "bun:sqlite";
|
|
9
8
|
import type { Cron } from "croner";
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -24,7 +23,7 @@ export type TaskStatus = "active" | "paused" | "completed" | "failed" | "cancell
|
|
|
24
23
|
export type TaskRunStatus = "running" | "success" | "failed" | "timeout";
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
|
-
* CronJob as stored in
|
|
26
|
+
* CronJob as stored in the `cronJobs` HiveDB collection
|
|
28
27
|
*/
|
|
29
28
|
export interface CronJob {
|
|
30
29
|
id: string;
|
|
@@ -48,6 +47,8 @@ export interface CronJob {
|
|
|
48
47
|
run_count: number;
|
|
49
48
|
error_count: number;
|
|
50
49
|
last_error: string | null;
|
|
50
|
+
misfire_policy?: "skip" | "fire_once";
|
|
51
|
+
misfire_grace_min?: number;
|
|
51
52
|
created_at: string;
|
|
52
53
|
updated_at: string;
|
|
53
54
|
last_run_at: string | null;
|
|
@@ -90,6 +91,8 @@ export interface CreateCronJobInput {
|
|
|
90
91
|
max_runs?: number | null;
|
|
91
92
|
protect?: boolean;
|
|
92
93
|
interval_sec?: number | null;
|
|
94
|
+
misfire_policy?: "skip" | "fire_once";
|
|
95
|
+
misfire_grace_min?: number;
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
/**
|
|
@@ -113,6 +116,8 @@ export interface UpdateCronJobInput {
|
|
|
113
116
|
protect?: boolean;
|
|
114
117
|
interval_sec?: number | null;
|
|
115
118
|
status?: TaskStatus;
|
|
119
|
+
misfire_policy?: "skip" | "fire_once";
|
|
120
|
+
misfire_grace_min?: number;
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
/**
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: a2ui_dashboard
|
|
3
|
+
description: "Create real-time interactive dashboards using A2UI v0.9 protocol with dynamic data binding and live updates"
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
author: Hive Team
|
|
6
|
+
icon: "📊"
|
|
7
|
+
category: a2ui
|
|
8
|
+
permissions:
|
|
9
|
+
- a2ui_write
|
|
10
|
+
dependencies: []
|
|
11
|
+
tools: [a2ui_create_surface, a2ui_update_components, a2ui_update_data_model, a2ui_delete_surface]
|
|
12
|
+
|
|
13
|
+
# Structured skill fields
|
|
14
|
+
triggers:
|
|
15
|
+
- "dashboard A2UI"
|
|
16
|
+
- "panel de control A2UI"
|
|
17
|
+
- "A2UI dashboard"
|
|
18
|
+
- "mostrar métricas A2UI"
|
|
19
|
+
- "A2UI metrics"
|
|
20
|
+
- "dashboard interactivo A2UI"
|
|
21
|
+
- "interactive dashboard"
|
|
22
|
+
- "A2UI dashboard en tiempo real"
|
|
23
|
+
- "real-time dashboard A2UI"
|
|
24
|
+
- "mostrar datos A2UI"
|
|
25
|
+
- "visualizar datos con A2UI"
|
|
26
|
+
|
|
27
|
+
preferred_agents: []
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- step: 1
|
|
31
|
+
action: a2ui_create_surface
|
|
32
|
+
instruction: "Create an A2UI surface for the dashboard. Set theme with primaryColor matching project branding."
|
|
33
|
+
params:
|
|
34
|
+
surfaceId: "Descriptive ID (e.g. 'project_dashboard', 'metrics_dashboard')"
|
|
35
|
+
catalogId: "https://a2ui.org/specification/v0_9/basic_catalog.json"
|
|
36
|
+
theme: "{ primaryColor: '#10B981', agentDisplayName: 'Dashboard' }"
|
|
37
|
+
output: surface_created
|
|
38
|
+
|
|
39
|
+
- step: 2
|
|
40
|
+
action: a2ui_update_components
|
|
41
|
+
instruction: "Build dashboard layout using Row, Column, Card, and Text components. Use weight for proportional sizing."
|
|
42
|
+
params:
|
|
43
|
+
surfaceId: "Same surfaceId from step 1"
|
|
44
|
+
components: "Array of A2UI component definitions for dashboard layout"
|
|
45
|
+
output: dashboard_layout
|
|
46
|
+
|
|
47
|
+
- step: 3
|
|
48
|
+
action: a2ui_update_data_model
|
|
49
|
+
instruction: "Populate dashboard with initial data using JSON Pointer paths. All dynamic values should use path bindings."
|
|
50
|
+
params:
|
|
51
|
+
surfaceId: "Same surfaceId"
|
|
52
|
+
path: "/"
|
|
53
|
+
value: "Full data model object with all metric values"
|
|
54
|
+
output: data_populated
|
|
55
|
+
|
|
56
|
+
- step: 4
|
|
57
|
+
action: a2ui_update_data_model
|
|
58
|
+
instruction: "Update specific data model paths to refresh dashboard metrics in real-time."
|
|
59
|
+
params:
|
|
60
|
+
surfaceId: "Same surfaceId"
|
|
61
|
+
path: "/metrics/completionRate"
|
|
62
|
+
value: "Updated value"
|
|
63
|
+
output: metrics_updated
|
|
64
|
+
|
|
65
|
+
- step: 5
|
|
66
|
+
action: a2ui_delete_surface
|
|
67
|
+
instruction: "Delete the dashboard surface when no longer needed."
|
|
68
|
+
params:
|
|
69
|
+
surfaceId: "Same surfaceId"
|
|
70
|
+
output: surface_deleted
|
|
71
|
+
|
|
72
|
+
rules:
|
|
73
|
+
- "Always call a2ui_create_surface BEFORE a2ui_update_components"
|
|
74
|
+
- "Components MUST include a root component with id='root'"
|
|
75
|
+
- "Use Row with weight for side-by-side metrics (e.g. weight: 1 vs weight: 3)"
|
|
76
|
+
- "Use Card to group related metrics together"
|
|
77
|
+
- "Use Text with usageHint: 'h2' for section titles, 'h1' for dashboard title"
|
|
78
|
+
- "Use Text with usageHint: 'caption' for labels"
|
|
79
|
+
- "Use data binding { path: '/metrics/name' } for all dynamic values"
|
|
80
|
+
- "Update metrics with a2ui_update_data_model using specific JSON Pointer paths"
|
|
81
|
+
- "Use a2ui_update_data_model with path: '/' to replace entire data model if needed"
|
|
82
|
+
- "Delete surfaces with a2ui_delete_surface when dashboard is no longer needed"
|
|
83
|
+
- "Use A2UI for both static and live dashboards"
|
|
84
|
+
|
|
85
|
+
output_format:
|
|
86
|
+
structure: a2ui_dashboard
|
|
87
|
+
sections:
|
|
88
|
+
- "surface_creation"
|
|
89
|
+
- "dashboard_layout"
|
|
90
|
+
- "data_model"
|
|
91
|
+
- "live_updates"
|
|
92
|
+
- "cleanup"
|
|
93
|
+
max_length: "Structured JSON components"
|
|
94
|
+
|
|
95
|
+
examples:
|
|
96
|
+
- user_input: "mostrá el dashboard del proyecto"
|
|
97
|
+
expected_behavior: "a2ui_create_surface(surfaceId:'project_dash', theme:{primaryColor:'#10B981'}) → a2ui_update_components with Row[Column[Card[metrics]],Column[tasks]] → a2ui_update_data_model(path:'/', value:{metrics:{...}})"
|
|
98
|
+
|
|
99
|
+
- user_input: "actualizá las métricas del dashboard"
|
|
100
|
+
expected_behavior: "a2ui_update_data_model(surfaceId:'project_dash', path:'/metrics/completionRate', value:75)"
|
|
101
|
+
|
|
102
|
+
- user_input: "show a real-time dashboard with server metrics"
|
|
103
|
+
expected_behavior: "a2ui_create_surface → a2ui_update_components with Cards for each metric → a2ui_update_data_model → periodic updates with a2ui_update_data_model"
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
# A2UI Dashboard Skill
|
|
107
|
+
|
|
108
|
+
## Cuándo se Activa
|
|
109
|
+
|
|
110
|
+
Para crear dashboards interactivos en tiempo real usando A2UI v0.9. Usar cuando se necesita:
|
|
111
|
+
- Métricas que se actualizan dinámicamente
|
|
112
|
+
- Dashboards con data binding
|
|
113
|
+
- Paneles con Cards, Rows, Columns
|
|
114
|
+
- Visualización de datos que cambia en tiempo real
|
|
115
|
+
|
|
116
|
+
## Herramientas Disponibles
|
|
117
|
+
|
|
118
|
+
| Tool | Qué hace | Cuándo usarla |
|
|
119
|
+
|------|----------|---------------|
|
|
120
|
+
| `a2ui_create_surface` | Crea la superficie A2UI | Siempre primero |
|
|
121
|
+
| `a2ui_update_components` | Envía componentes | Para layout del dashboard |
|
|
122
|
+
| `a2ui_update_data_model` | Actualiza datos | Para métricas dinámicas |
|
|
123
|
+
| `a2ui_delete_surface` | Elimina la superficie | Al cerrar dashboard |
|
|
124
|
+
|
|
125
|
+
## Flujo Obligatorio
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
1. a2ui_create_surface(surfaceId, catalogId, theme)
|
|
129
|
+
2. a2ui_update_components(surfaceId, components[])
|
|
130
|
+
3. a2ui_update_data_model(surfaceId, path, value) // datos iniciales
|
|
131
|
+
4. [actualizar métricas con a2ui_update_data_model según necesidad]
|
|
132
|
+
5. a2ui_delete_surface(surfaceId) // al terminar
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Patrón de Dashboard Típico
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
[
|
|
139
|
+
{"id": "root", "component": "Column", "children": ["title", "metrics_row", "tasks_list"]},
|
|
140
|
+
{"id": "title", "component": "Text", "text": "Dashboard de Proyecto", "variant": "h1"},
|
|
141
|
+
|
|
142
|
+
{"id": "metrics_row", "component": "Row", "children": ["card1", "card2", "card3"]},
|
|
143
|
+
{"id": "card1", "component": "Card", "child": "card1_content", "weight": 1},
|
|
144
|
+
{"id": "card1_content", "component": "Column", "children": ["card1_label", "card1_value"]},
|
|
145
|
+
{"id": "card1_label", "component": "Text", "text": "Completado", "variant": "caption"},
|
|
146
|
+
{"id": "card1_value", "component": "Text", "text": {"path": "/metrics/completionRate"}, "variant": "h2"},
|
|
147
|
+
|
|
148
|
+
{"id": "tasks_list", "component": "List", "children": {"path": "/tasks", "componentId": "task_template"}},
|
|
149
|
+
{"id": "task_template", "component": "Card", "child": "task_content"},
|
|
150
|
+
{"id": "task_content", "component": "Column", "children": ["task_name", "task_status"]},
|
|
151
|
+
{"id": "task_name", "component": "Text", "text": {"path": "/name"}},
|
|
152
|
+
{"id": "task_status", "component": "Text", "text": {"path": "/status"}, "variant": "caption"}
|
|
153
|
+
]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Actualización en Tiempo Real
|
|
157
|
+
|
|
158
|
+
Para actualizar métricas específicas sin reenviar componentes:
|
|
159
|
+
```json
|
|
160
|
+
a2ui_update_data_model(surfaceId: "dash", path: "/metrics/completionRate", value: 85)
|
|
161
|
+
a2ui_update_data_model(surfaceId: "dash", path: "/metrics/totalTasks", value: 24)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Para reemplazar todo el data model:
|
|
165
|
+
```json
|
|
166
|
+
a2ui_update_data_model(surfaceId: "dash", path: "/", value: {metrics: {completionRate: 90, totalTasks: 25}, tasks: [...]})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Mejores Prácticas
|
|
170
|
+
|
|
171
|
+
- Usar `weight` en Row/Column para proporciones (weight:1 vs weight:3 = 25% vs 75%)
|
|
172
|
+
- Agrupar métricas en Cards para separación visual
|
|
173
|
+
- Usar `usageHint: "caption"` para labels, `"h1"/"h2"` para valores
|
|
174
|
+
- Bind todos los valores dinámicos con `{ path: "/..." }`
|
|
175
|
+
- Actualizar métricas con `a2ui_update_data_model` path específico
|
|
176
|
+
- Eliminar surfaces al terminar para evitar memory leaks
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: a2ui_form
|
|
3
|
+
description: "Create rich interactive forms using A2UI v0.9 protocol with validation, data binding, and multi-step flows"
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
author: Hive Team
|
|
6
|
+
icon: "📝"
|
|
7
|
+
category: a2ui
|
|
8
|
+
permissions:
|
|
9
|
+
- a2ui_write
|
|
10
|
+
dependencies: []
|
|
11
|
+
tools: [a2ui_create_surface, a2ui_update_components, a2ui_update_data_model, a2ui_delete_surface]
|
|
12
|
+
|
|
13
|
+
# Structured skill fields
|
|
14
|
+
triggers:
|
|
15
|
+
- "crear formulario A2UI"
|
|
16
|
+
- "create A2UI form"
|
|
17
|
+
- "formulario interactivo A2UI"
|
|
18
|
+
- "A2UI form"
|
|
19
|
+
- "pedir datos con A2UI"
|
|
20
|
+
- "collect data A2UI"
|
|
21
|
+
- "formulario con validación"
|
|
22
|
+
- "form with validation"
|
|
23
|
+
- "formulario multi-paso"
|
|
24
|
+
- "multi-step form A2UI"
|
|
25
|
+
- "form dinámico A2UI"
|
|
26
|
+
- "dynamic form A2UI"
|
|
27
|
+
|
|
28
|
+
preferred_agents: []
|
|
29
|
+
|
|
30
|
+
steps:
|
|
31
|
+
- step: 1
|
|
32
|
+
action: a2ui_create_surface
|
|
33
|
+
instruction: "Create an A2UI surface with a unique surfaceId and catalog. Set theme with primaryColor and agentDisplayName."
|
|
34
|
+
params:
|
|
35
|
+
surfaceId: "Unique identifier (e.g. 'contact_form', 'signup_form')"
|
|
36
|
+
catalogId: "https://a2ui.org/specification/v0_9/basic_catalog.json"
|
|
37
|
+
theme: "{ primaryColor: '#3B82F6', agentDisplayName: 'Asistente' }"
|
|
38
|
+
output: surface_created
|
|
39
|
+
|
|
40
|
+
- step: 2
|
|
41
|
+
action: a2ui_update_components
|
|
42
|
+
instruction: "Send the form layout as a flat list of A2UI components. Must include a 'root' component. Use Column/Row for layout, TextField for inputs, Button for submit."
|
|
43
|
+
params:
|
|
44
|
+
surfaceId: "Same surfaceId from step 1"
|
|
45
|
+
components: "Array of A2UI component definitions"
|
|
46
|
+
output: components_sent
|
|
47
|
+
|
|
48
|
+
- step: 3
|
|
49
|
+
action: a2ui_update_data_model
|
|
50
|
+
instruction: "Populate initial form values using JSON Pointer paths (e.g. '/form/name'). Sets default values and enables two-way binding."
|
|
51
|
+
params:
|
|
52
|
+
surfaceId: "Same surfaceId"
|
|
53
|
+
path: "/form"
|
|
54
|
+
value: "{ name: '', email: '' }"
|
|
55
|
+
output: data_populated
|
|
56
|
+
|
|
57
|
+
- step: 4
|
|
58
|
+
action: a2ui_delete_surface
|
|
59
|
+
instruction: "Delete the A2UI surface when the form is completed or no longer needed."
|
|
60
|
+
params:
|
|
61
|
+
surfaceId: "Same surfaceId"
|
|
62
|
+
output: surface_deleted
|
|
63
|
+
|
|
64
|
+
rules:
|
|
65
|
+
- "Always call a2ui_create_surface BEFORE a2ui_update_components"
|
|
66
|
+
- "Components MUST include a root component with id='root'"
|
|
67
|
+
- "Use data binding with { path: '/form/field' } for TextField values to enable two-way binding"
|
|
68
|
+
- "Add checks (validation) to TextField: [{ call: 'required', args: { value: { path: '/form/email' } }, message: 'Email is required' }]"
|
|
69
|
+
- "Button actions use event format: { event: { name: 'submit_form', context: { email: { path: '/form/email' } } } }"
|
|
70
|
+
- "Use Column for vertical layouts, Row for horizontal layouts"
|
|
71
|
+
- "Set weight on Row/Column children for proportional sizing (e.g. weight: 1 vs weight: 3)"
|
|
72
|
+
- "Delete surfaces with a2ui_delete_surface when no longer needed to prevent memory leaks"
|
|
73
|
+
- "Use A2UI forms for every structured data-entry flow"
|
|
74
|
+
|
|
75
|
+
output_format:
|
|
76
|
+
structure: a2ui_form
|
|
77
|
+
sections:
|
|
78
|
+
- "surface_creation"
|
|
79
|
+
- "component_layout"
|
|
80
|
+
- "data_model"
|
|
81
|
+
- "cleanup"
|
|
82
|
+
max_length: "Structured JSON components"
|
|
83
|
+
|
|
84
|
+
examples:
|
|
85
|
+
- user_input: "creá un formulario de contacto"
|
|
86
|
+
expected_behavior: "a2ui_create_surface(surfaceId:'contact_form', theme:{primaryColor:'#3B82F6',agentDisplayName:'Asistente'}) → a2ui_update_components(surfaceId:'contact_form', components:[root,Column,hdr,name_fld,email_fld,msg_fld,submit_btn]) → a2ui_update_data_model(surfaceId:'contact_form', path:'/form', value:{name:'',email:'',message:''})"
|
|
87
|
+
|
|
88
|
+
- user_input: "I need a signup form with email validation"
|
|
89
|
+
expected_behavior: "a2ui_create_surface → a2ui_update_components with TextField({value:{path:'/form/email'}, checks:[{call:'required',...},{call:'email',...}]}) → a2ui_update_data_model"
|
|
90
|
+
|
|
91
|
+
- user_input: "creá un formulario multi-paso"
|
|
92
|
+
expected_behavior: "a2ui_create_surface → a2ui_update_components with Tabs+Column per step → a2ui_update_data_model for all fields"
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
# A2UI Form Skill
|
|
96
|
+
|
|
97
|
+
## Cuándo se Activa
|
|
98
|
+
|
|
99
|
+
Para crear formularios interactivos ricos usando el protocolo A2UI v0.9. Usar cuando se necesita:
|
|
100
|
+
- Validación de campos (required, email, regex)
|
|
101
|
+
- Data binding dinámico
|
|
102
|
+
- Formularios multi-paso
|
|
103
|
+
- Choice pickers, sliders, checkboxes
|
|
104
|
+
- Formularios con acciones personalizadas
|
|
105
|
+
|
|
106
|
+
## Herramientas Disponibles
|
|
107
|
+
|
|
108
|
+
| Tool | Qué hace | Cuándo usarla |
|
|
109
|
+
|------|----------|---------------|
|
|
110
|
+
| `a2ui_create_surface` | Crea la superficie A2UI | Siempre primero |
|
|
111
|
+
| `a2ui_update_components` | Envía componentes | Después de crear surface |
|
|
112
|
+
| `a2ui_update_data_model` | Actualiza datos | Para valores iniciales o dinámicos |
|
|
113
|
+
| `a2ui_delete_surface` | Elimina la superficie | Al terminar |
|
|
114
|
+
|
|
115
|
+
## Flujo Obligatorio
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
1. a2ui_create_surface(surfaceId, catalogId, theme)
|
|
119
|
+
2. a2ui_update_components(surfaceId, components[])
|
|
120
|
+
3. a2ui_update_data_model(surfaceId, path, value) // opcional, para datos iniciales
|
|
121
|
+
4. [esperar acción del usuario]
|
|
122
|
+
5. a2ui_delete_surface(surfaceId) // al terminar
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Componentes Disponibles
|
|
126
|
+
|
|
127
|
+
| Componente | Descripción | Props clave |
|
|
128
|
+
|------------|-------------|-------------|
|
|
129
|
+
| `Column` | Layout vertical | `children`, `distribution`, `alignment` |
|
|
130
|
+
| `Row` | Layout horizontal | `children`, `distribution`, `alignment` |
|
|
131
|
+
| `Text` | Texto | `text`, `usageHint` (h1-h5, body, caption, code) |
|
|
132
|
+
| `Button` | Botón | `child`, `variant`, `action` |
|
|
133
|
+
| `TextField` | Campo de texto | `label`, `value`, `variant` (shortText/longText/number/obscured), `validationRegexp`, `checks`, `action` |
|
|
134
|
+
| `CheckBox` | Checkbox | `label`, `value` |
|
|
135
|
+
| `ChoicePicker` | Selector múltiple | `options`, `value` (DynamicStringList), `variant` (mutuallyExclusive/multipleSelection), `displayStyle`, `filterable`, `action` |
|
|
136
|
+
| `Slider` | Slider numérico | `value`, `minValue`, `maxValue` |
|
|
137
|
+
| `DateTimeInput` | Fecha/hora | `value`, `enableDate`, `enableTime` |
|
|
138
|
+
| `Card` | Tarjeta | `child` |
|
|
139
|
+
| `Divider` | Separador | `axis` |
|
|
140
|
+
| `Image` | Imagen | `url`, `fit` |
|
|
141
|
+
| `Tabs` | Pestañas | `tabItems` |
|
|
142
|
+
|
|
143
|
+
## Data Binding
|
|
144
|
+
|
|
145
|
+
- Literal: `"texto directo"` o número
|
|
146
|
+
- Path: `{ "path": "/form/name" }` — se resuelve contra el data model
|
|
147
|
+
- Function call: `{ "call": "formatDate", "args": {...} }`
|
|
148
|
+
|
|
149
|
+
## Cuándo disparan acciones los inputs
|
|
150
|
+
|
|
151
|
+
| Componente | Cuándo dispara | Formato de action |
|
|
152
|
+
|------------|---------------|-------------------|
|
|
153
|
+
| `Button` | Al hacer click | `{name: "...", context: {...}}` o `{event: {name: "...", context: {...}}}` |
|
|
154
|
+
| `TextField` | Al perder foco (blur) o presionar Enter (en shortText) | `{name: "...", context: {...}}` |
|
|
155
|
+
| `ChoicePicker` | Inmediatamente al seleccionar/deseleccionar | `{name: "...", context: {...}}` |
|
|
156
|
+
| `Slider` | Al soltar el slider (onValueCommit) | `{name: "...", context: {...}}` |
|
|
157
|
+
| `CheckBox` | Al cambiar estado | — (solo two-way binding) |
|
|
158
|
+
| `DateTimeInput` | Al cambiar valor | — (solo two-way binding) |
|
|
159
|
+
|
|
160
|
+
**Nota**: Tanto `{name: "...", context: {...}}` (directo) como `{event: {name: "...", context: {...}}}` (con wrapper) son formatos válidos.
|
|
161
|
+
|
|
162
|
+
**Nota**: Para ChoicePicker usa siempre `selections: {path: "..."}` (no `value`) para two-way binding.
|
|
163
|
+
|
|
164
|
+
## Validación (checks)
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
"checks": [
|
|
168
|
+
{ "call": "required", "args": { "value": { "path": "/form/email" } }, "message": "Email is required" },
|
|
169
|
+
{ "call": "email", "args": { "value": { "path": "/form/email" } }, "message": "Invalid email" },
|
|
170
|
+
{ "call": "regex", "args": { "value": { "path": "/form/phone" }, "pattern": "^\\d{10}$" }, "message": "10 digits required" }
|
|
171
|
+
]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Ejemplo: Formulario de Contacto
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
// 1. Create surface
|
|
178
|
+
a2ui_create_surface(surfaceId: "contact_form", catalogId: "https://a2ui.org/specification/v0_9/basic_catalog.json", theme: {primaryColor: "#3B82F6", agentDisplayName: "Asistente"})
|
|
179
|
+
|
|
180
|
+
// 2. Send components
|
|
181
|
+
a2ui_update_components(surfaceId: "contact_form", components: [
|
|
182
|
+
{"id": "root", "component": "Column", "children": ["header","name_field","email_field","msg_field","submit_btn"]},
|
|
183
|
+
{id: "header", component: "Text", text: "Contacto", variant: "h2"},
|
|
184
|
+
{id: "name_field", component: "TextField", label: "Nombre", value: {path: "/form/name"}, variant: "shortText"},
|
|
185
|
+
{id: "email_field", component: "TextField", label: "Email", value: {path: "/form/email"}, variant: "shortText", validationRegexp: "^[^@]+@[^@]+\\.[^@]+$", checks: [{call: "required", args: {value: {path: "/form/email"}}, message: "Email obligatorio"}, {call: "email", args: {value: {path: "/form/email"}}, message: "Email inválido"}]},
|
|
186
|
+
{id: "msg_field", component: "TextField", label: "Mensaje", value: {path: "/form/message"}, variant: "longText"},
|
|
187
|
+
{id: "submit_label", component: "Text", text: "Enviar"},
|
|
188
|
+
{id: "submit_btn", component: "Button", child: "submit_label", variant: "primary", action: {event: {name: "submit_contact", context: {name: {path: "/form/name"}, email: {path: "/form/email"}, message: {path: "/form/message"}}}}
|
|
189
|
+
])
|
|
190
|
+
|
|
191
|
+
// 3. Initialize data model
|
|
192
|
+
a2ui_update_data_model(surfaceId: "contact_form", path: "/form", value: {name: "", email: "", message: ""})
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Mejores Prácticas
|
|
196
|
+
|
|
197
|
+
- Siempre incluir un componente `root` con id="root"
|
|
198
|
+
- Usar `{ path: "/..." }` para data binding en TextField values
|
|
199
|
+
- Agregar `checks` para validación de campos obligatorios
|
|
200
|
+
- Usar `variant: "primary"` para botones principales
|
|
201
|
+
- Eliminar surfaces con `a2ui_delete_surface` al terminar
|
|
202
|
+
- Usar formularios A2UI para toda captura estructurada de datos
|