From d335e5bf71b24140176af983296fff75fa4e9b64 Mon Sep 17 00:00:00 2001 From: zenchantlive Date: Tue, 24 Mar 2026 15:39:19 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20orchestrator=20button=20+=20Pi=20SDK=20s?= =?UTF-8?q?ession=20error=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20-=20Move=20leftSidebarMode=20from=20URL=20state=20to=20lo?= =?UTF-8?q?cal=20useState=20in=20unified-shell,=20=20=20=20=20avoiding=20f?= =?UTF-8?q?orce-dynamic=20router=20round-trip=20that=20made=20the=20button?= =?UTF-8?q?=20appear=20broken=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20-=20Replace=20fileURLToPath(new=20URL(...,?= =?UTF-8?q?=20import.meta.url))=20with=20process.cwd()=20=20=20=20=20in=20?= =?UTF-8?q?bb-pi-bootstrap.ts=20=E2=80=94=20import.meta.url=20is=20a=20web?= =?UTF-8?q?pack://=20URL=20in=20Next.js,=20=20=20=20=20causing=20cross-rea?= =?UTF-8?q?lm=20TypeError=20when=20passed=20to=20Node.js=20fileURLToPath()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + AGENTS.md | 87 + .../2026-03-05-bb-daemon-attachment-model.md | 64 + docs/manual-test-phase-1-worker-spawning.md | 179 + docs/plans/2026-03-05-embedded-pi-prd.md | 1080 +++++ docs/plans/2026-03-05-embedded-pi-roadmap.md | 373 ++ .../2026-03-06-phase-1-worker-spawning.md | 419 ++ .../2026-03-06-phase-2-archetype-configs.md | 496 +++ .../2026-03-07-phase-3-agent-orchestration.md | 1143 ++++++ .../2026-03-07-phase-4-launch-anywhere.md | 137 + .../2026-03-07-phase-5-agent-presence.md | 76 + .../2026-03-07-phase-6-runtime-hardening.md | 75 + docs/plans/2026-03-07-phase-7-tests.md | 95 + docs/plans/2026-03-08-phase-4-handoff.md | 159 + .../2026-03-08-phase-4-implementation.md | 1092 +++++ package-lock.json | 3576 ++++++++++++++++- package.json | 4 + src/app/api/runtime/agents/history/route.ts | 38 + src/app/api/runtime/agents/route.ts | 42 + src/app/api/runtime/bootstrap/route.ts | 25 + src/app/api/runtime/events/route.ts | 16 + src/app/api/runtime/launch/route.ts | 55 + src/app/api/runtime/orchestrator/route.ts | 24 + src/app/api/runtime/prompt/route.ts | 29 + src/app/api/runtime/spawn/route.ts | 49 + src/app/api/runtime/status/route.ts | 8 + src/app/api/runtime/stream/route.ts | 80 + src/app/api/runtime/worker-status/route.ts | 37 + src/app/layout.tsx | 4 +- src/app/page.tsx | 57 + src/cli/beadboard-cli.ts | 127 +- src/components/activity/activity-panel.tsx | 297 +- .../activity/contextual-right-panel.tsx | 29 +- src/components/agents/agent-action-row.tsx | 83 + src/components/agents/agent-assign-button.tsx | 79 + src/components/agents/agent-picker-popup.tsx | 120 + src/components/agents/agent-spawn-button.tsx | 132 + src/components/agents/agent-status-panel.tsx | 158 + src/components/agents/hooks/index.ts | 3 + .../agents/hooks/use-agent-status.ts | 68 + .../agents/hooks/use-spawn-agent.ts | 41 + src/components/agents/index.ts | 6 + src/components/graph/assignment-panel.tsx | 20 +- .../onboarding/onboarding-wizard.tsx | 113 + src/components/shared/left-panel-new.tsx | 404 ++ src/components/shared/left-panel.tsx | 134 +- src/components/shared/orchestrator-panel.tsx | 118 + src/components/shared/runtime-console.tsx | 101 + src/components/shared/unified-shell.tsx | 278 +- src/components/social/social-card.tsx | 505 +-- src/components/social/social-page.tsx | 544 +-- src/components/swarm/agent-inspector.tsx | 484 +++ src/components/swarm/agent-picker.tsx | 131 + src/components/swarm/swarm-workspace.tsx | 8 +- src/components/swarm/template-inspector.tsx | 25 +- src/hooks/use-url-state.ts | 34 +- src/lib/agent-instance.ts | 72 + src/lib/agent-persistence.ts | 93 + src/lib/agent-workspace.ts | 41 + src/lib/bb-daemon.ts | 219 + src/lib/bb-pi-bootstrap.ts | 194 + src/lib/dolt-client.ts | 34 +- src/lib/embedded-daemon.ts | 156 + src/lib/embedded-runtime.ts | 238 ++ src/lib/orchestrator-chat.ts | 159 + src/lib/pi-daemon-adapter.ts | 276 ++ src/lib/pi-runtime-detection.ts | 103 + src/lib/server/beads-fs.ts | 315 +- src/lib/social-cards.ts | 8 + src/lib/types-swarm.ts | 22 +- src/lib/types.ts | 16 +- src/lib/worker-session-manager.ts | 505 +++ src/tui/bb-agent-tui.ts | 483 +++ src/tui/bb-daemon-tui.ts | 63 + src/tui/system-prompt.ts | 222 + src/tui/tools/bb-assign-agent.ts | 45 + src/tui/tools/bb-bead-crud.ts | 318 ++ src/tui/tools/bb-create-agent.ts | 88 + src/tui/tools/bb-create-archetype.ts | 88 + src/tui/tools/bb-create-template.ts | 106 + src/tui/tools/bb-delete-agent.ts | 56 + src/tui/tools/bb-delete-archetype.ts | 64 + src/tui/tools/bb-delete-template.ts | 64 + src/tui/tools/bb-deviation.ts | 39 + src/tui/tools/bb-dolt-read.ts | 50 + src/tui/tools/bb-list-agents.ts | 45 + src/tui/tools/bb-list-archetypes.ts | 45 + src/tui/tools/bb-list-templates.ts | 45 + src/tui/tools/bb-mailbox.ts | 105 + src/tui/tools/bb-presence.ts | 42 + src/tui/tools/bb-spawn-template.ts | 167 + src/tui/tools/bb-spawn-worker.ts | 168 + src/tui/tools/bb-update-agent.ts | 74 + src/tui/tools/bb-update-archetype.ts | 74 + src/tui/tools/bb-update-template.ts | 96 + src/tui/tools/bb-worker-results.ts | 116 + src/tui/tools/bb-worker-status.ts | 107 + src/tui/tools/types.ts | 9 + 98 files changed, 17851 insertions(+), 944 deletions(-) create mode 100644 docs/adr/2026-03-05-bb-daemon-attachment-model.md create mode 100644 docs/manual-test-phase-1-worker-spawning.md create mode 100644 docs/plans/2026-03-05-embedded-pi-prd.md create mode 100644 docs/plans/2026-03-05-embedded-pi-roadmap.md create mode 100644 docs/plans/2026-03-06-phase-1-worker-spawning.md create mode 100644 docs/plans/2026-03-06-phase-2-archetype-configs.md create mode 100644 docs/plans/2026-03-07-phase-3-agent-orchestration.md create mode 100644 docs/plans/2026-03-07-phase-4-launch-anywhere.md create mode 100644 docs/plans/2026-03-07-phase-5-agent-presence.md create mode 100644 docs/plans/2026-03-07-phase-6-runtime-hardening.md create mode 100644 docs/plans/2026-03-07-phase-7-tests.md create mode 100644 docs/plans/2026-03-08-phase-4-handoff.md create mode 100644 docs/plans/2026-03-08-phase-4-implementation.md create mode 100644 src/app/api/runtime/agents/history/route.ts create mode 100644 src/app/api/runtime/agents/route.ts create mode 100644 src/app/api/runtime/bootstrap/route.ts create mode 100644 src/app/api/runtime/events/route.ts create mode 100644 src/app/api/runtime/launch/route.ts create mode 100644 src/app/api/runtime/orchestrator/route.ts create mode 100644 src/app/api/runtime/prompt/route.ts create mode 100644 src/app/api/runtime/spawn/route.ts create mode 100644 src/app/api/runtime/status/route.ts create mode 100644 src/app/api/runtime/stream/route.ts create mode 100644 src/app/api/runtime/worker-status/route.ts create mode 100644 src/components/agents/agent-action-row.tsx create mode 100644 src/components/agents/agent-assign-button.tsx create mode 100644 src/components/agents/agent-picker-popup.tsx create mode 100644 src/components/agents/agent-spawn-button.tsx create mode 100644 src/components/agents/agent-status-panel.tsx create mode 100644 src/components/agents/hooks/index.ts create mode 100644 src/components/agents/hooks/use-agent-status.ts create mode 100644 src/components/agents/hooks/use-spawn-agent.ts create mode 100644 src/components/agents/index.ts create mode 100644 src/components/onboarding/onboarding-wizard.tsx create mode 100644 src/components/shared/left-panel-new.tsx create mode 100644 src/components/shared/orchestrator-panel.tsx create mode 100644 src/components/shared/runtime-console.tsx create mode 100644 src/components/swarm/agent-inspector.tsx create mode 100644 src/components/swarm/agent-picker.tsx create mode 100644 src/lib/agent-instance.ts create mode 100644 src/lib/agent-persistence.ts create mode 100644 src/lib/agent-workspace.ts create mode 100644 src/lib/bb-daemon.ts create mode 100644 src/lib/bb-pi-bootstrap.ts create mode 100644 src/lib/embedded-daemon.ts create mode 100644 src/lib/embedded-runtime.ts create mode 100644 src/lib/orchestrator-chat.ts create mode 100644 src/lib/pi-daemon-adapter.ts create mode 100644 src/lib/pi-runtime-detection.ts create mode 100644 src/lib/worker-session-manager.ts create mode 100644 src/tui/bb-agent-tui.ts create mode 100644 src/tui/bb-daemon-tui.ts create mode 100644 src/tui/system-prompt.ts create mode 100644 src/tui/tools/bb-assign-agent.ts create mode 100644 src/tui/tools/bb-bead-crud.ts create mode 100644 src/tui/tools/bb-create-agent.ts create mode 100644 src/tui/tools/bb-create-archetype.ts create mode 100644 src/tui/tools/bb-create-template.ts create mode 100644 src/tui/tools/bb-delete-agent.ts create mode 100644 src/tui/tools/bb-delete-archetype.ts create mode 100644 src/tui/tools/bb-delete-template.ts create mode 100644 src/tui/tools/bb-deviation.ts create mode 100644 src/tui/tools/bb-dolt-read.ts create mode 100644 src/tui/tools/bb-list-agents.ts create mode 100644 src/tui/tools/bb-list-archetypes.ts create mode 100644 src/tui/tools/bb-list-templates.ts create mode 100644 src/tui/tools/bb-mailbox.ts create mode 100644 src/tui/tools/bb-presence.ts create mode 100644 src/tui/tools/bb-spawn-template.ts create mode 100644 src/tui/tools/bb-spawn-worker.ts create mode 100644 src/tui/tools/bb-update-agent.ts create mode 100644 src/tui/tools/bb-update-archetype.ts create mode 100644 src/tui/tools/bb-update-template.ts create mode 100644 src/tui/tools/bb-worker-results.ts create mode 100644 src/tui/tools/bb-worker-status.ts create mode 100644 src/tui/tools/types.ts diff --git a/.gitignore b/.gitignore index 07b8f93..df15a73 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,7 @@ test-*.ts .qodo/ .gemini/ .kilocode/ + +# Dolt database files (added by bd init) +.dolt/ +*.db diff --git a/AGENTS.md b/AGENTS.md index 62121f9..3c534bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -208,3 +208,90 @@ Dolt SQL server at `127.0.0.1:3307`. Read path: `readIssuesViaDolt()` → Dolt ( networkingMode=mirrored ``` Then `wsl --shutdown`. Not required for single-platform setups. + + +## Issue Tracking with bd (beads) + +**IMPORTANT**: This project uses **bd (beads)** for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods. + +### Why bd? + +- Dependency-aware: Track blockers and relationships between issues +- Git-friendly: Dolt-powered version control with native sync +- Agent-optimized: JSON output, ready work detection, discovered-from links +- Prevents duplicate tracking systems and confusion + +### Quick Start + +**Check for ready work:** + +```bash +bd ready --json +``` + +**Create new issues:** + +```bash +bd create "Issue title" --description="Detailed context" -t bug|feature|task -p 0-4 --json +bd create "Issue title" --description="What this issue is about" -p 1 --deps discovered-from:bd-123 --json +``` + +**Claim and update:** + +```bash +bd update --claim --json +bd update bd-42 --priority 1 --json +``` + +**Complete work:** + +```bash +bd close bd-42 --reason "Completed" --json +``` + +### Issue Types + +- `bug` - Something broken +- `feature` - New functionality +- `task` - Work item (tests, docs, refactoring) +- `epic` - Large feature with subtasks +- `chore` - Maintenance (dependencies, tooling) + +### Priorities + +- `0` - Critical (security, data loss, broken builds) +- `1` - High (major features, important bugs) +- `2` - Medium (default, nice-to-have) +- `3` - Low (polish, optimization) +- `4` - Backlog (future ideas) + +### Workflow for AI Agents + +1. **Check ready work**: `bd ready` shows unblocked issues +2. **Claim your task atomically**: `bd update --claim` +3. **Work on it**: Implement, test, document +4. **Discover new work?** Create linked issue: + - `bd create "Found bug" --description="Details about what was found" -p 1 --deps discovered-from:` +5. **Complete**: `bd close --reason "Done"` + +### Auto-Sync + +bd automatically syncs via Dolt: + +- Each write auto-commits to Dolt history +- Use `bd dolt push`/`bd dolt pull` for remote sync +- No manual export/import needed! + +### Important Rules + +- ✅ Use bd for ALL task tracking +- ✅ Always use `--json` flag for programmatic use +- ✅ Link discovered work with `discovered-from` dependencies +- ✅ Check `bd ready` before asking "what should I work on?" +- ❌ Do NOT create markdown TODO lists +- ❌ Do NOT use external issue trackers +- ❌ Do NOT duplicate tracking systems + +For more details, see README.md and docs/QUICKSTART.md. + + diff --git a/docs/adr/2026-03-05-bb-daemon-attachment-model.md b/docs/adr/2026-03-05-bb-daemon-attachment-model.md new file mode 100644 index 0000000..57e8ce1 --- /dev/null +++ b/docs/adr/2026-03-05-bb-daemon-attachment-model.md @@ -0,0 +1,64 @@ +# ADR: BeadBoard Daemon Attachment Model + +- Date: 2026-03-05 +- Status: Accepted +- Scope: Host-resident daemon topology for BeadBoard runtime execution + +## Context + +BeadBoard is evolving from a coordination surface into a coordination-plus-execution product. The product now needs a durable runtime anchor that survives browser reloads, keeps project runtime state on the user's machine, and can eventually serve both local and remote frontends. + +Earlier embedded runtime scaffolding inside the Next app is useful as a transition, but it is not the target architecture. + +## Decision + +BeadBoard will use a **user-owned, host-resident `bb` daemon** as the execution anchor. + +This means: + +- each user runs their own BeadBoard daemon on their own machine +- the daemon is long-lived while the host machine is on +- runtime ownership stays with the user and the user's environment +- the frontend attaches to daemon state and control APIs instead of owning execution directly +- this is **not a centralized hosted runtime** model for the current product architecture + +## Attachment model + +The canonical relationship is: + +1. the user starts `bb daemon start` +2. the daemon becomes the durable runtime anchor for project execution +3. a frontend attaches to daemon APIs and event streams +4. the frontend acts as a client/control surface, not the runtime owner + +The phrase **frontend attaches to daemon** is intentional. It applies whether the frontend is: + +- running locally on the same machine +- deployed remotely by the user +- eventually deployed in a different frontend environment that still points at the same daemon + +## Local vs remote frontend + +Near-term implementation may use local co-residency and in-process attachment seams for simplicity. + +Long-term architecture still treats the frontend and daemon as distinct roles: + +- **daemon** = execution, orchestration, durable runtime state +- **frontend** = interaction, inspection, control, visualization + +Remote attachment is therefore an extension of the same architecture, not a different one. + +## Non-decision: later B2B hosting + +A future B2B deployment may run `bb` on a cloud VM. That is a separate later PRD and does not change the current architectural standard: + +- current product direction is still user-owned daemon first +- later hosted or VM-backed deployment must build on the same daemon attachment contract rather than replacing it with a centralized runtime assumption + +## Consequences + +- daemon lifecycle becomes a first-class CLI/runtime concern +- Next.js runtime routes should evolve into daemon-backed adapters +- the frontend must gradually become a daemon client rather than an in-app runtime owner +- Pi integration should sit behind a BeadBoard-owned daemon/runtime boundary +- persistence, reconnect behavior, and event streaming should attach to the daemon contract rather than ephemeral browser state diff --git a/docs/manual-test-phase-1-worker-spawning.md b/docs/manual-test-phase-1-worker-spawning.md new file mode 100644 index 0000000..437ffa7 --- /dev/null +++ b/docs/manual-test-phase-1-worker-spawning.md @@ -0,0 +1,179 @@ +# Phase 1: Worker Spawning - Manual E2E Test + +**Date:** 2026-03-06 +**Status:** Ready for manual testing + +--- + +## Prerequisites + +1. ✅ Dev server is running (user has it running) +2. ✅ Pi SDK is available (`bb daemon bootstrap-pi` should have been run) +3. ✅ All code changes are committed and applied + +--- + +## Test Scenarios + +### Scenario 1: Basic Worker Spawn + +**Steps:** +1. Open BeadBoard in browser (http://localhost:3000) +2. Open left panel (orchestrator mode) +3. Send this prompt to orchestrator: + ``` + Spawn a worker to read the README.md file and tell me what it says. + ``` +4. Observe the response and check: + - [ ] Orchestrator calls `bb_spawn_worker` tool + - [ ] Worker spawns with task context + - [ ] `worker.spawned` event appears in runtime console (with "Worker" badge) + - [ ] Worker event shows task ID: "read-the-readme.md-file-and-tell-me-what-it-says" + - [ ] Worker status tool response is shown + +**Expected Result:** Worker appears in console with "Worker" badge, shows "WORKING" status. + +--- + +### Scenario 2: Worker Status Check + +**Steps:** +1. From left panel, send: + ``` + Check the status of the worker you just spawned. + ``` +2. Observe: + - [ ] Orchestrator calls `bb_worker_status` tool + - [ ] Worker status is displayed with correct emoji + - [ ] Shows "WORKING", "COMPLETED", or "FAILED" as appropriate + - [ ] Task ID matches the spawned task + +**Expected Result:** Current worker status shown with helpful message. + +--- + +### Scenario 3: Worker Completion + +**Steps:** +1. Wait for the worker to complete (should happen within ~30 seconds for README read) +2. Observe: + - [ ] `worker.updated` or `worker.completed` event appears + - [ ] If completed: shows "COMPLETED" with ✅ emoji + - [ ] Result summary is shown (first 200 chars) + - [ ] Worker no longer shows as "WORKING" + +**Expected Result:** Worker successfully completes and result is displayed. + +--- + +### Scenario 4: Multiple Workers + +**Steps:** +1. Send prompt to orchestrator: + ``` + Spawn 3 workers in parallel: + - Worker 1: Read package.json + - Worker 2: List all files in src/ + - Worker 3: Read .env.example file + ``` +2. Observe: + - [ ] Three separate `worker.spawned` events appear + - [ ] All three workers show "WORKING" status + - [ ] Each worker has a unique ID + - [ ] Task contexts are correct for each + +**Expected Result:** Multiple workers run in parallel, each with unique identity and task. + +--- + +### Scenario 5: Worker with Archetype + +**Steps:** +1. Send prompt to orchestrator: + ``` + Spawn a worker with archetype "coder" to add a new test file. + ``` +2. Observe: + - [ ] Worker spawns with "Archetype: coder" in the detail + - [ ] Worker system prompt includes the archetype context + +**Expected Result:** Worker behavior is guided by archetype (though actual behavior is same - this is v1). + +--- + +### Scenario 6: Worker Error Handling + +**Steps:** +1. Send prompt to orchestrator with invalid task: + ``` + Spawn a worker to read a file that does not exist: /nonexistent/path/to/file.txt + ``` +2. Observe: + - [ ] Worker attempts the task + - [ ] Worker fails and reports error + - [ ] `worker.failed` event appears with ❌ emoji + - [ ] Error message explains what went wrong + +**Expected Result:** Worker failures are captured and reported clearly. + +--- + +### Scenario 7: Runtime Console Worker Badge + +**Steps:** +1. Spawn a worker +2. Look at runtime console (bottom panel) +3. Observe: + - [ ] Worker events have a purple "Worker" badge + - [ ] Orchestrator events do NOT have the badge + - [ ] Badge says "Worker Agent Event" on hover + +**Expected Result:** Visual distinction between orchestrator and worker events is clear. + +--- + +### Scenario 8: Left Panel Chat Integration + +**Steps:** +1. Observe the orchestrator conversation in left panel +2. During worker spawn, check: + - [ ] Tool calls appear inline (like they do for `bb_dolt_read`) + - [ ] Worker spawn response includes worker ID + - [ ] Chat remains readable and not cluttered + +**Expected Result:** Orchestrator chat surface handles worker interactions natively. + +--- + +## Success Criteria + +A scenario passes when: +- Worker events appear in runtime console +- Worker events have "Worker" badge +- Status changes (spawning → working → completed) are visible +- Tool calls are logged and returned to orchestrator +- Multiple workers can run in parallel +- Worker completion/failure is captured + +--- + +## Bug Report Form + +If any test fails, capture: +``` +Test Scenario: [number] +Steps Taken: [what you did] +Expected Result: [what should happen] +Actual Result: [what actually happened] +Error Messages: [any errors in console] +Screenshot/Notes: [any additional details] +``` + +--- + +## After Testing + +Once all scenarios pass: +1. Review success criteria in `docs/plans/2026-03-06-phase-1-worker-spawning.md` +2. Update roadmap to mark Phase 1 as complete +3. Move to Phase 2: Archetype-backed execution configs diff --git a/docs/plans/2026-03-05-embedded-pi-prd.md b/docs/plans/2026-03-05-embedded-pi-prd.md new file mode 100644 index 0000000..e12c737 --- /dev/null +++ b/docs/plans/2026-03-05-embedded-pi-prd.md @@ -0,0 +1,1080 @@ +# BeadBoard Embedded Pi PRD + +**Date:** 2026-03-05 +**Status:** PRD + active implementation reference +**Branch:** `docs/embedded-pi-prd` +**Owner:** Pi +**Scope:** Canonical planning document for embedded Pi in BeadBoard. This document supersedes ad hoc notes for this feature and should be treated as the single source of truth for implementation planning and review. +**Implementation roadmap / shipped status:** `docs/plans/2026-03-05-embedded-pi-roadmap.md` + +--- + +## 0. Why this document exists + +BeadBoard already has a meaningful multi-agent coordination product surface: + +- activity/timeline view (replaces deprecated sessions hub concept) +- a mailbox model with categories like `HANDOFF`, `BLOCKED`, and `INFO` +- an agent registry +- reservations and liveness +- mission/swarm concepts +- archetypes and templates +- a graph view, social/task view, and contextual inspectors +- a runtime manager and global install model + +What it does **not** yet do is let the user launch a real agent from the frontend and have that agent actually execute work under BeadBoard's runtime and UI model. + +This PRD defines how **Pi** should become BeadBoard's first embedded execution runtime. + +This document started as a planning-only PRD. It is still the canonical product/architecture target, but implementation has now begun. + +For shipped work and remaining gaps, see: +- `docs/plans/2026-03-05-embedded-pi-roadmap.md` + +As of 2026-03-06, the project has already shipped a meaningful Embedded Pi foundation, including: +- managed Pi bootstrap/runtime plumbing +- embedded project orchestrator session creation +- frontend prompt submission from the left panel +- realtime runtime telemetry in the app +- chat-style orchestrator transcript foundation in the left panel +- BeadBoard-aware tool execution from the embedded orchestrator + +This PRD remains the canonical target state; the roadmap tracks what has already been done versus what still remains. + +--- + +## 1. Product summary + +BeadBoard should evolve from a coordination-and-visibility layer into a coordination-plus-execution layer. + +In v1, the first execution runtime will be **Pi**. + +BeadBoard should ship with a **BeadBoard-owned embedded Pi system** that: + +- runs under BeadBoard's runtime manager +- is installed and versioned as part of BeadBoard's global runtime +- maintains project-scoped state and context +- exposes a long-lived **orchestrator Pi** per project +- can spawn **worker Pi instances** to perform actual work +- maps BeadBoard **archetypes** to real executable agent types +- uses BeadBoard **templates** as the default recommended launch structure +- integrates into BeadBoard's existing UI instead of bolting on a parallel product + +Pi in BeadBoard should not feel like “an assistant tab.” +It should feel like BeadBoard gained a native execution substrate. + +The BeadBoard frontend should ultimately act as a client of the host-resident `bb` daemon rather than the owner of runtime execution. + +--- + +## 2. Core product thesis + +### 2.1 What BeadBoard already is +BeadBoard is not merely a task board. It is already a control plane for: + +- work topology +- agent coordination +- task ownership and reservations +- structured handoffs +- mission/swarm composition +- graph-aware planning +- real-time operational visibility + +### 2.2 What is missing +Today, a user can shape work, assign archetypes, inspect missions, and coordinate agent-like structures in the UI, but cannot launch a real autonomous worker from the frontend to do the work. + +The current user workaround is effectively: + +1. invoke an external coding agent manually +2. tell it to use the BeadBoard driver skill +3. tell it what task to work on +4. watch the coordination data appear back inside BeadBoard + +That means BeadBoard already has the **coordination shell**, but the actual execution loop lives outside the product. + +### 2.3 What embedded Pi changes +Embedded Pi brings execution *into* the BeadBoard operating model. + +After this feature, the user should be able to: + +- launch work from the frontend anywhere there is work +- talk to the project's long-lived orchestrator +- ask the orchestrator to dispatch worker agents +- let the orchestrator choose archetypes and worker instances +- observe worker lifecycle and decisions in BeadBoard's native surfaces +- steer or intervene without leaving BeadBoard + +--- + +## 3. Goals + +### 3.1 Product goals +- Make BeadBoard capable of launching real autonomous work from the frontend +- Preserve BeadBoard's existing coordination semantics instead of bypassing them +- Make embedded Pi feel native to the app, not like an external plugin panel +- Treat archetypes as real execution concepts, not just UI labels +- Make templates the default recommended orchestration structure +- Make orchestration explainable and inspectable +- Preserve extensibility so BeadBoard can support runtimes other than Pi later + +### 3.2 Technical goals +- Run Pi under the existing BeadBoard runtime-manager/global-runtime model +- Keep BeadBoard runtime ownership global, but state/project context project-scoped +- Define a stable BeadBoard-to-Pi contract instead of exposing raw Pi internals to the frontend +- Use one long-lived orchestrator Pi per project +- Support multiple worker Pi instances per project +- Integrate worker lifecycle into BeadBoard sessions, timeline, and mail/coordination surfaces +- Define a clean runtime abstraction for future non-Pi runtimes + +### 3.3 UX goals +- Let users launch Pi anywhere there is work +- Preserve existing UI strengths instead of fighting them +- Avoid overloading the right panel with full agent conversation responsibilities +- Give the orchestrator a persistent home that fits the app's shell +- Keep agent execution visible through ambient telemetry and native UI state + +--- + +## 4. Non-goals for v1 + +- Supporting multiple execution runtimes on day one +- Making every Pi conversation exposed directly as raw Pi protocol UI +- Replacing BeadBoard's existing coordination semantics with Pi-native abstractions +- Treating agent execution as a separate standalone page/app inside BeadBoard +- Finalizing a perfect permanent naming ontology for all agent instances +- Solving all long-term memory/persona/runtime policy customization in v1 +- Building implementation in this session + +--- + +## 5. Foundational constraints + +### 5.1 This must fit BeadBoard as it exists +The design must work with the existing BeadBoard shell and interaction patterns, including: + +- `UnifiedShell` +- left panel navigation +- middle-panel views (`social`, `graph`, etc.) +- right panel contextual inspection +- activity/timeline conversation model +- mission inspector and swarm controls +- URL-state-driven app behavior +- runtime-manager/global install strategy + +### 5.2 Do not treat Pi as the product abstraction +Pi is the first execution runtime, not the top-level product model. + +BeadBoard's frontend should speak in BeadBoard concepts: +- projects +- missions +- tasks +- archetypes +- templates +- agent types +- agent instances +- sessions +- launch policies + +Pi should sit behind a BeadBoard-owned runtime boundary. + +### 5.3 Extensibility is the highest-order architectural value +Jordan explicitly stated that extensibility is critically important to the app in general. + +Therefore: +- archetypes must matter to execution +- runtime abstraction must exist even if only Pi is supported initially +- launch policy should not hard-code a small finite forever-roster model +- UI and data models should separate agent type, agent instance, and runtime backend + +--- + +## 6. Canonical runtime model + +### 6.1 Runtime ownership +BeadBoard owns the runtime globally. + +Embedded Pi should run as part of the BeadBoard-managed runtime installed under the existing global runtime model: +- `~/.beadboard/runtime/` +- stable launch via BeadBoard's command shims and runtime metadata + +### 6.2 State scope +Although runtime ownership is global, the embedded Pi system should use **project-scoped state and context**. + +That means: +- project-specific orchestrator state +- project-specific worker sessions +- project-specific launch history +- project-specific task/mission awareness +- project-specific mail/coordination mapping + +### 6.3 V1 runtime support policy +V1 should support **Pi only**, but the architecture must leave room for other runtimes later. + +Therefore BeadBoard should plan for: +- a runtime adapter/driver boundary +- runtime-specific spawn and session handling +- a future where Pi is the first runtime, not the final only runtime + +--- + +## 7. Canonical agent model + +### 7.1 Long-lived orchestrator +Each project should have **one long-lived orchestrator Pi**. + +The orchestrator is responsible for: +- receiving launch requests +- interpreting task/mission/template context +- selecting archetypes +- deciding when to duplicate worker types +- deciding when to deviate from templates +- emitting explanations and approval requests +- monitoring worker progress and coordination state + +### 7.2 Archetypes as executable agent types +Archetypes should correspond to real executable agent types. + +This is a critical product decision. +Archetypes are not merely descriptive metadata or frontend labels. They should define launchable worker categories. + +Implication: +- creating more archetypes creates more agent types BeadBoard can use +- archetypes become part of execution topology, not just planning UI + +### 7.3 Worker instances +Actual executing workers should be **runtime instances** of archetype-backed agent types. + +If an archetype-backed worker is already busy and more capacity is needed, BeadBoard should create an additional worker instance of that archetype. + +This gives a clear scaling model: +- archetype = stable executable type +- worker instance = concrete runtime spawn of that type + +### 7.4 Identity model +The design must distinguish between at least three layers: + +1. **Archetype / agent type** + - stable + - user-facing + - reusable + - configurable + +2. **Worker instance** + - runtime-bound + - task/mission attached + - duplicate-able + - ephemeral or long-lived by policy + +3. **Conversation/session** + - communication surface + - may outlive a single UI focus state + - must be represented in BeadBoard's session model + +These must not collapse into one muddy concept. + +--- + +## 8. Template policy + +### 8.1 Default rule +Templates are **highly recommended by default**. + +The orchestrator should treat the selected or implied template as the preferred launch structure unless it clearly does not make sense. + +### 8.2 Orchestrator flexibility +The orchestrator should be allowed to deviate from the template when necessary. +Examples: +- task graph reality conflicts with template assumptions +- required archetype is unavailable or duplicated incorrectly by default template +- concurrency need exceeds template composition +- blocker/graph state requires a different rollout order + +### 8.3 Deviation policy +Template deviations must: +- always be explained and recorded +- sometimes require confirmation + +#### Explanation policy +Every significant deviation should write one canonical structured record that can be rendered in multiple surfaces. + +#### Confirmation policy +If the deviation is large or materially changes the launch composition: +- ask for confirmation unless auto mode is enabled + +### 8.4 Canonical rendering surfaces for deviations +One canonical structured record should feed: +- timeline / activity surfaces +- social/graph/activity conversation surfaces +- mission/swarm inspectors +- any future orchestration-specific views + +--- + +## 9. Pi configuration model + +### 9.1 BeadBoard-owned Pi, not personal Pi +The embedded Pi system must be fully separate from the user's personal Pi config. + +This means it must not rely on: +- `~/.pi/agent/` as production BeadBoard runtime identity +- personal Pi extensions/skills as BeadBoard runtime config +- personal Pi sessions as BeadBoard state + +BeadBoard needs its own: +- Pi runtime/config root +- identity files +- extensions +- skills +- session/state directories + +### 9.2 Layered config policy +Archetypes should be allowed to influence Pi behavior, but in a layered and controlled way. + +Recommended layering: +1. BeadBoard global runtime defaults +2. project/orchestrator defaults +3. archetype-level overrides +4. task/mission-specific launch parameters + +### 9.3 Allowed archetype influence +Archetypes should be able to shape at least: +- system prompt / identity fragments +- tool access policy +- preferred model / reasoning level +- task-launch posture +- safe behavioral hints + +But this should be constrained by guardrails and fallback defaults. + +--- + +## 10. UI architecture + +## 10.1 Existing UI constraint +BeadBoard already has a meaningful UI shell with: +- left panel navigation and epic structure +- middle-panel work views +- right-panel contextual inspection +- existing drawers and inspector patterns + +The embedded Pi design must fit this existing shell. + +## 10.2 Primary UI principle +Pi spawning should be available **anywhere there is work**. + +That includes at minimum: +- task cards +- graph nodes +- epic contexts +- swarm/mission views +- blocked/triage interactions +- activity view contexts + +## 10.3 Left sidebar proposal +The strongest current planning direction is: + +### Left sidebar becomes dual-mode +The left sidebar should support: +- **Epic / navigation mode** +- **Main orchestrator chat mode** + +This aligns with the app better than forcing the orchestrator into the right panel. + +### Why this is preferable +The right panel is already needed for rich contextual detail. Making it the primary home of full Pi conversation would overload it and force tradeoffs between inspection and execution. + +The left panel, by contrast, is a better place for: +- control-plane interaction +- long-lived orchestrator chat +- dispatch and steering +- project-level planning and spawn coordination + +### Compact/expanded behavior +The left rail's orchestrator mode should be compact by default but expandable into a richer thread in the same surface. + +## 10.4 Middle panel proposal +The middle panel should remain the main work surface. + +Epic selection/filtering should be available directly in the middle-panel flow on all views, so that using the left sidebar for orchestrator interaction does not destroy structural task scoping. + +## 10.5 Right panel proposal +The right panel should remain focused on contextual detail, including: +- task thread detail +- mission detail +- command feed detail +- swarm/mission inspector detail +- worker-specific or task-specific contextual insight + +The right panel should not be the default main home for orchestrator chat. + +## 10.6 Bottom console proposal +A bottom console is still useful, but not as the primary conversation home. + +It should act as a **runtime telemetry console** and show things like: +- orchestrator decisions +- worker spawn events +- launch attempts +- template deviations +- approval-needed events +- worker failure/completion status +- runtime/system messages + +So the shell becomes: +- **left** = orchestrator / navigation +- **middle** = work views +- **right** = contextual detail +- **bottom** = live runtime console/telemetry + +This is the most holistic shell model currently identified. + +--- + +## 11. Conversation model + +### 11.1 Conversation should not be a separate app +The embedded Pi system should not feel like a disconnected chat assistant page. + +Conversation should be integrated into existing BeadBoard structures. + +### 11.2 Three conversation scopes +The design should support at least three scopes of interaction: + +1. **Orchestrator conversation** + - project-level + - launch, dispatch, policy, explanation, intervention + +2. **Worker conversation** + - task- or mission-local + - status, steering, redirect, review, intervention + +3. **System/execution event stream** + - not freeform conversation + - runtime console and event surfaces + +### 11.3 Sessions hub role +The sessions hub should become the deeper social/agent conversation and monitoring surface. + +Main shell launch controls start/focus agent work, while sessions hub provides: +- deep conversation history +- worker monitoring +- handoff inspection +- worker redirection and coordination review + +--- + +## 12. Spawn model + +### 12.1 Default launch behavior +The primary launch path should be **orchestrator-mediated**. + +The default flow is: +1. user acts on a task/mission/epic/swarm from the frontend +2. BeadBoard packages context and sends a request to the orchestrator +3. orchestrator uses template-first logic and current graph/task state +4. orchestrator chooses archetypes and worker count +5. orchestrator requests worker creation through the runtime layer +6. BeadBoard reflects the resulting worker instances back into native surfaces + +### 12.2 Direct launch behavior +Manual/direct worker launch should also be possible as a lower-level mode for power users and debugging. + +### 12.3 Launch should be local to work surfaces +The user should be able to start agent work from anywhere work appears. + +That means launch affordances should exist in: +- social/task cards +- DAG/graph nodes +- swarm views +- mission inspectors +- sessions contexts +- blocked triage contexts + +### 12.4 Launch should package context automatically +The app should not require the user to manually restate task context in natural language every time. + +A launch event should automatically include relevant state like: +- task id +- epic/mission context +- template id +- archetype hints +- graph blockers/unlocks +- project root +- current coordination/mailbox status if relevant + +--- + +## 13. Surface-by-surface UX behavior + +### 13.1 Social/task cards +Social/task cards should support: +- inspect/select (default click) +- explicit agent-launch affordance +- ask orchestrator +- assign archetype and execute +- blocked/unblocked context-aware actions + +Task click should remain inspect-first. Pi launch should be an explicit secondary action. + +### 13.2 DAG / graph view +Graph view should support Pi launch especially well because users are reasoning over dependency structure there. + +Likely launch actions: +- ask orchestrator about this node +- unblock this chain +- start implementation +- review this branch +- investigate dependency bottleneck + +Graph launches should include graph-context packaging. + +### 13.3 Swarm / mission views +Swarm and mission views should be orchestrator-native surfaces. + +These are where users are most likely to: +- apply templates +- deploy archetype mixes +- launch mission execution +- add/duplicate workers +- reason about mission structure + +### 13.4 Sessions hub +Sessions hub should primarily support: +- monitoring running agents +- steering existing workers +- intervention and communication +- reading handoffs/blockers/info messages +- inspecting agent history + +### 13.5 Timeline / activity surfaces +These should support reactive interactions like: +- retry failed launch +- inspect decision explanation +- re-open conversation from runtime event +- ask orchestrator to explain or re-plan + +--- + +## 14. Communication architecture + +### 14.1 Frontend should not speak raw Pi +The frontend should communicate with a BeadBoard-owned runtime/service layer, not directly with raw Pi internals. + +### 14.2 Recommended communication path +**Frontend → BeadBoard server/runtime layer → embedded Pi runtime(s)** + +This allows BeadBoard to: +- preserve its own product abstractions +- own session and runtime routing +- normalize event shapes for the frontend +- swap or add runtimes later + +### 14.3 Canonical BeadBoard concepts the frontend should send +The frontend should request things in BeadBoard-native terms, such as: +- launch mission from template +- ask orchestrator about task +- spawn worker of archetype X on task Y +- duplicate reviewer archetype instance +- steer worker session +- stop worker +- approve template deviation + +### 14.4 Canonical event categories BeadBoard should emit back +The BeadBoard runtime should emit BeadBoard-native events like: +- orchestrator planning +- spawn requested +- worker started +- worker idle/working/blocked/completed/failed +- deviation proposed +- deviation approved/rejected +- mailbox/handoff generated +- session updated + +--- + +## 15. Data model additions (conceptual) + +The PRD does not finalize exact schemas, but implementation should likely add or formalize concepts equivalent to: + +- **agent type** + - archetype-backed executable definition + +- **agent instance** + - runtime worker/orchestrator instance + +- **runtime backend** + - Pi in v1, others later + +- **launch request** + - frontend or orchestrator-originated execution request + +- **launch plan** + - computed orchestration decision set + +- **template deviation record** + - structured explanation and approval state + +- **runtime event** + - execution lifecycle event for telemetry/timeline/sessions + +- **session binding** + - mapping between worker/orchestrator and BeadBoard's session/social surfaces + +--- + +## 16. Presence, liveness, and status model + +Presence should come from runtime state, not just whether a chat is open. + +Recommended runtime presence states include: +- idle +- planning +- launching +- working +- waiting +- blocked +- completed +- failed +- stale + +These presence signals should appear in: +- sessions hub +- mission/swarm views +- task/social cards where relevant +- graph node overlays where relevant +- right-panel contextual views +- bottom telemetry console + +--- + +## 17. Naming policy + +### 17.1 What should be stable +Stable user-facing concepts should be: +- archetype/agent type +- orchestrator identity per project + +### 17.2 What can vary +Worker instances may duplicate when needed and should not create permanent conceptual chaos. + +The implementation should prefer a naming and display model where: +- the stable archetype identity is visible +- duplicates are understandable as instances of that archetype + +The exact final naming scheme is not resolved in this PRD, but the conceptual distinction is mandatory. + +--- + +## 18. Risks and failure modes + +### 18.1 UI overload risk +If the feature tries to jam full agent conversation into the already-busy right panel, the app will become harder to use rather than more powerful. + +**Mitigation:** left-side orchestrator + right-side contextual detail + bottom telemetry. + +### 18.2 Archetypes staying “fake” risk +If archetypes do not actually map to execution concepts, the runtime model and UI model will drift apart. + +**Mitigation:** archetypes explicitly become executable agent types. + +### 18.3 Runtime/model lock-in risk +If Pi-specific assumptions are baked directly into frontend semantics, later runtime extensibility becomes painful. + +**Mitigation:** BeadBoard-native runtime adapter boundary. + +### 18.4 Unexplained orchestration risk +If the orchestrator silently deviates from templates or spawns unexpected workers, trust drops. + +**Mitigation:** canonical structured explanation + approval flow. + +### 18.5 Identity pollution risk +If every worker spawn becomes a confusing permanent identity, sessions and agent surfaces become noisy. + +**Mitigation:** separate agent type from agent instance. + +--- + +## 19. Implementation workstreams + +This section enumerates what must be designed and built later. It is intentionally comprehensive. + +### Workstream A — Runtime integration +- define BeadBoard-owned embedded Pi runtime root and process model +- integrate with BeadBoard runtime manager +- define orchestrator process lifecycle +- define worker spawn lifecycle +- define health/restart/shutdown behavior + +### Workstream B — Pi config system +- design BeadBoard-specific Pi config root +- define orchestrator identity/config +- define worker archetype-to-Pi config mapping +- define layered override model +- define project-scoped state directories + +### Workstream C — Runtime adapter/API layer +- define BeadBoard-to-Pi adapter contract +- normalize request/response/event shapes for frontend +- abstract runtime backend for future non-Pi support + +### Workstream D — Agent model and data model +- formalize agent type vs agent instance vs session +- formalize template deviation record +- formalize runtime events and state transitions +- connect runtime state to sessions hub/timeline/mission surfaces + +### Workstream E — Orchestrator logic +- define template-first orchestration behavior +- define deviation heuristics +- define approval thresholds and auto mode behavior +- define archetype duplication policy + +### Workstream F — Frontend launch UX +- add launch affordances anywhere there is work +- add orchestrator mode to left sidebar +- restore middle-panel epic flow where needed +- integrate contextual launch actions into social/graph/swarm/sessions surfaces + +### Workstream G — Conversation UX +- define orchestrator conversation surface in left sidebar +- define worker conversation/focus behavior in activity panel and contextual panels +- define how conversations and runtime events interact + +### Workstream H — Bottom runtime console +- define runtime console data model and UX +- stream spawn/planning/approval/failure/completion messages there +- connect to timeline/state model + +### Workstream I — Contract/spec documentation +- create frontend/runtime/RPC interface spec +- create event contract spec +- create session continuity and restart behavior spec +- create implementation-phase design docs if needed + +### Workstream J — Verification plan +- prove launch from frontend +- prove orchestrator spawning workers +- prove sessions/timeline integration +- prove template deviation explanation/confirmation +- prove project-scoped orchestrator behavior + +--- + +## 20. Proposed implementation phases + +### Phase 0 — Documentation and contract alignment +- approve this PRD +- identify related docs/ADRs that need to align later +- define contract/spec deliverables + +### Phase 1 — Runtime substrate +- add runtime adapter boundary +- add embedded Pi runtime ownership under BeadBoard runtime manager +- establish project-scoped orchestrator + +### Phase 2 — Agent model and contract +- implement archetype-backed agent-type model +- add worker instance lifecycle +- define event model + +### Phase 3 — Frontend launch plumbing +- add launch affordances to existing work surfaces +- wire left orchestrator panel mode +- wire bottom runtime console + +### Phase 4 — Sessions and observability integration +- make orchestrator/workers first-class in sessions/timeline/surfaces +- show presence and runtime state everywhere appropriate + +### Phase 5 — Template-first orchestration behavior +- implement template-based launch planning +- implement deviation logging and confirmation behavior + +### Phase 6 — Direct/manual controls and hardening +- manual direct worker launch +- duplicate worker controls +- stop/retry/intervene flows +- reliability and UX polish + +--- + +## 21. Verification and test strategy + +This feature must be implemented with a rigorous, evidence-first test strategy. + +No implementation phase should be considered complete based on demos, screenshots, or manual spot checks alone. Each major workstream must ship with automated verification at the correct level of the stack, plus targeted manual validation for cross-surface UX behavior. + +### 21.1 Test philosophy +- prefer failing tests before implementation for behavior changes where practical +- test contracts at the BeadBoard boundary, not Pi internals alone +- verify both happy path and failure path behavior +- verify restart/reconnect/reload scenarios, not just first-run behavior +- verify multi-surface consistency: launch in one surface must appear correctly in others +- verify that explanation, approval, and deviation logic is observable and auditable +- verify runtime isolation from personal Pi config + +### 21.2 Required automated test layers + +#### A. Unit tests +Unit tests must cover pure logic and deterministic policy code, including: +- runtime path resolution and project-scoped state path derivation +- archetype-to-agent-type mapping +- layered config merge logic +- template selection and deviation classification logic +- approval threshold logic +- runtime event normalization and state transition reducers +- display labeling logic for agent types vs instances + +#### B. Contract tests +Contract tests must validate the BeadBoard-owned runtime adapter boundary, including: +- launch request schema validation +- orchestrator spawn request contract +- worker lifecycle event schema +- session binding event contract +- template deviation record shape +- approval request / approval response flow +- error payload contract for runtime failures and timeouts + +These tests must protect BeadBoard's product-facing API from accidental Pi-specific leakage. + +#### C. Integration tests +Integration tests must validate server/runtime behavior across real module boundaries, including: +- project orchestrator creation and reuse +- worker spawn under orchestrator control +- duplicate worker spawn for busy archetypes +- launch from task context with packaged metadata +- launch from graph context with dependency metadata +- template application and recorded deviation generation +- session creation and runtime event persistence/mapping +- stop/retry/reconnect behavior + +#### D. UI component/integration tests +UI tests must cover the shell and critical interaction surfaces, including: +- left sidebar epic mode ↔ orchestrator mode switching +- launch affordance visibility on task cards, graph nodes, swarm surfaces, and sessions surfaces +- right panel remaining contextual while orchestrator lives elsewhere +- bottom console showing runtime telemetry events in order +- activity panel reflecting running orchestrator/worker state +- approval-needed UI for major deviations +- deep-link/URL-state restoration of relevant surfaces + +#### E. End-to-end tests +End-to-end tests must cover complete user journeys, including: +- launch work from a task card through orchestrator-mediated spawn +- launch work from graph node with dependency-aware context +- launch a mission/template flow that creates multiple workers +- orchestrator deviation requiring approval, then approval continuing execution +- worker failure surfacing in console, sessions, and contextual UI +- browser refresh / reconnect while orchestrator and workers continue running +- switching projects without cross-project state contamination + +### 21.3 Required failure-path coverage +The test suite must explicitly cover at least these failure modes: +- Pi runtime unavailable at launch time +- orchestrator crash and restart +- worker spawn failure +- worker timeout / stale worker state +- malformed or partial runtime events +- lost frontend connection / SSE reconnect +- duplicate event delivery / idempotency handling +- approval request issued, then expired or rejected +- project path missing or invalid +- BeadBoard runtime accidentally attempting to read personal Pi config + +### 21.4 Required cross-surface consistency assertions +Tests must assert that a single runtime action is reflected consistently across multiple surfaces. At minimum: +- launch from social card appears in bottom console, activity panel, and relevant contextual detail +- launch from graph node updates graph presence state, sessions state, and runtime console +- deviation record appears identically in timeline/activity, sessions context, and mission/swarm context +- worker completion/failure updates all subscribed surfaces consistently + +### 21.5 Required non-functional verification +The implementation must include verification for: +- runtime isolation from personal Pi config and sessions +- project-scoped state isolation between two active projects +- orchestrator persistence across page reloads and frontend reconnects +- acceptable event latency for launch/state transitions under normal local development conditions +- graceful degradation when runtime is unhealthy + +### 21.6 Manual validation checklist +Automated coverage is mandatory, but manual validation is still required for: +- shell ergonomics across left/middle/right/bottom surfaces +- readability of orchestration explanations and deviation prompts +- whether the orchestrator feels native rather than bolted-on +- whether the right panel remains useful while agents are active +- whether launch-anywhere interactions feel coherent across views + +### 21.7 Definition of done for testing +No implementation phase is done unless: +- new behavior is covered by automated tests at the appropriate layer +- failure-path tests exist for the new runtime behavior +- cross-surface propagation is verified where applicable +- manual validation checklist items for that phase have been exercised +- all relevant tests pass in CI on the branch before merge + +--- + +## 22. Acceptance criteria for the feature direction + +A future implementation should not be considered complete unless all of the following are true: + +### Runtime + architecture +- BeadBoard owns a Pi runtime under its runtime-manager model +- each project has one long-lived orchestrator Pi +- worker Pi instances can be spawned under orchestrator control +- runtime architecture leaves room for future non-Pi backends + +### Agent model +- archetypes map to real executable agent types +- duplicate worker instances of an archetype can be created when needed +- agent type, agent instance, and session are distinct concepts in the design + +### UX + product behavior +- user can initiate Pi launch anywhere there is work +- orchestrator has a persistent native home in the shell +- right panel remains useful for contextual detail +- runtime telemetry is visible continuously in a bottom console or equivalent +- activity panel reflects real runtime-backed agent activity + +### Orchestration behavior +- templates are used by default +- orchestrator may deviate when needed +- deviations are always explained +- major deviations require confirmation unless auto mode is enabled + +### Product fit +- embedded Pi feels like a native extension of BeadBoard's operating model +- the feature does not read as “an assistant tab bolted onto a dashboard” + +--- + +## 23. Open questions intentionally deferred + +These are real questions, but they do not block approval of this PRD. + +- exact worker instance naming/display policy +- exact schema definitions for runtime objects +- exact placement and visual style of bottom console +- exact keyboard/modeless interactions for orchestrator chat mode +- exact persistence policy for worker sessions across app/runtime restarts +- exact guardrails for archetype-level Pi overrides + +These should be resolved in follow-on design/implementation planning, not by weakening the core architecture here. + +--- + +## 24. Future: Unified Settings System (Phase 8+) + +**Status:** Not started, deferred to post-Phase 7 + +### Overview + +A comprehensive settings system for both CLI and frontend that gives users full control over BeadBoard's behavior, model providers, authentication, and runtime configuration. + +### CLI Settings (`bb`) + +Currently supports: +- `bb config set model ` - Set default model + +**Needed:** +- Full settings file at `~/.beadboard/settings.json` or project-level `.beadboard/settings.json` +- Settings for: + - Default provider (openai, anthropic, google, openrouter, etc.) + - Default model per provider + - API keys (or reference to auth.json) + - Default archetype for workers + - Default template preferences + - Worker limits (max concurrent workers) + - Timeout settings + - Logging verbosity + - Shell path (for Windows compatibility) + - Runtime version preferences +- Commands: + - `bb config list` - Show all settings + - `bb config get ` - Get specific setting + - `bb config set ` - Set setting + - `bb config unset ` - Reset to default + - `bb config import ` - Import settings + - `bb config export ` - Export settings + +### Frontend Settings + +**Needed:** +- Settings panel accessible from UI (gear icon or dedicated view) +- Sections: + - **Provider/Model Selection** + - Choose provider (OpenAI, Anthropic, Google, OpenRouter, etc.) + - Choose model from provider + - Set default for orchestrator vs workers + - **Authentication** + - Login/logout for each provider + - API key management (add/edit/delete keys) + - Secure storage (not in plaintext) + - **Orchestrator Settings** + - Default worker archetype + - Max concurrent workers + - Timeout preferences + - Auto-spawn behavior + - **UI Preferences** + - Theme (dark/light/custom) + - Default view (social/graph) + - Console minimization preference + - Notification settings + - **Project Settings** + - Project workspace path + - Default project on load + - Per-project overrides + - **Runtime Settings** + - Pi version + - Bootstrap behavior (auto/manual) + - Log level + - Debug mode + +### Settings Hierarchy + +Priority order (highest wins): +1. Command-line flags (`--model`, `--provider`) +2. Project-level settings (`.beadboard/settings.json`) +3. User-level settings (`~/.beadboard/settings.json`) +4. Default values + +### Security Considerations + +- API keys should never be stored in plaintext in settings files +- Use system keychain where available (Keychain on macOS, Credential Manager on Windows, Secret Service on Linux) +- Fallback to encrypted file storage if keychain unavailable +- Frontend should never expose full API keys (show masked version) + +### Implementation Notes + +- Settings should be accessible from both CLI and frontend +- Changes in frontend should reflect in CLI settings and vice versa +- Settings API should be consistent between both surfaces +- Consider using a schema-driven settings system for validation + +### Why This Matters + +- Users need to switch models/providers without editing config files manually +- Multi-provider support requires per-provider authentication +- Power users want fine-grained control over runtime behavior +- Teams may want project-level settings for consistency + +--- + +## 24. Final recommendation + +BeadBoard should adopt the following canonical direction: + +- global BeadBoard-owned runtime +- project-scoped orchestrator and worker state +- one long-lived orchestrator Pi per project +- archetypes as real executable agent types +- duplicated worker instances when an archetype is already in use +- template-first orchestration +- logged and confirmable deviations +- launch affordances anywhere there is work +- left sidebar as orchestrator/epic dual-mode surface +- right panel preserved for contextual detail +- bottom console for runtime telemetry +- Pi-only runtime in v1 behind a future-extensible runtime abstraction + +This is the most holistic, extensible, and BeadBoard-native planning direction identified in this session. diff --git a/docs/plans/2026-03-05-embedded-pi-roadmap.md b/docs/plans/2026-03-05-embedded-pi-roadmap.md new file mode 100644 index 0000000..f32ee9c --- /dev/null +++ b/docs/plans/2026-03-05-embedded-pi-roadmap.md @@ -0,0 +1,373 @@ +# BeadBoard Embedded Pi Roadmap + +**Date:** 2026-03-05 +**Last Updated:** 2026-03-07 +**Companion PRD:** `docs/plans/2026-03-05-embedded-pi-prd.md` +**Purpose:** Track what has already shipped for Embedded Pi in BeadBoard, what is partially complete, and what remains to reach the full PRD vision. + +--- + +## Current status + +**Phase 3 COMPLETE - Testing in progress.** + +We now have: +- ✅ Working embedded orchestrator with BeadBoard tools +- ✅ Worker spawning with numbered instances (Engineer 01, etc.) +- ✅ Agent types (architect, engineer, reviewer, tester, investigator, shipper) +- ✅ Template-based team spawning +- ✅ **Bead-required workflow** - every worker task has a bead +- ✅ **Async worker coordination** - non-blocking spawn, check status, read results +- ✅ **File verification pattern** - orchestrator reads actual files to verify work + +**Currently testing:** +- Worker claims bead, updates, closes correctly +- Orchestrator can check worker status mid-task +- Orchestrator can get results and verify via file reads + +**Biggest remaining gaps:** +- Phase 4: Launch-anywhere UX (spawn from task cards, graph nodes) +- Phase 5: Agent presence in social/graph views +- Tests and verification + +--- + +## What is done + +### Done: Runtime substrate / managed Pi foundation +- Managed Pi bootstrap exists via `src/lib/bb-pi-bootstrap.ts` +- Pi runtime detection exists via `src/lib/pi-runtime-detection.ts` +- BeadBoard-specific Pi settings + agent dir setup exist +- Embedded daemon/orchestrator substrate exists via: + - `src/lib/embedded-daemon.ts` + - `src/lib/bb-daemon.ts` + - `src/lib/pi-daemon-adapter.ts` + +### Done: Orchestrator session integration +- Per-project orchestrator session can be created and reused +- Pi SDK session is initialized through the embedded adapter +- The orchestrator can receive prompts from the frontend +- The orchestrator can execute BeadBoard tools from the frontend path + +### Done: BeadBoard-aware orchestrator context +- Dynamic system prompt exists in `src/tui/system-prompt.ts` +- Prompt includes: + - active tasks + - archetypes + - templates +- Deviation recording tool exists: + - `src/tui/tools/bb-deviation.ts` + +### Done: Frontend prompt + telemetry plumbing +- Prompt route exists: `src/app/api/runtime/prompt/route.ts` +- Runtime event stream exists: `src/app/api/runtime/stream/route.ts` +- Runtime events endpoint exists: `src/app/api/runtime/events/route.ts` +- Left-panel orchestrator UI exists and can submit prompts +- Bottom runtime console exists and is now minimizable + +### Done: Left-panel orchestrator chat UX foundation +- Left panel supports orchestrator mode +- Left panel now renders chat-style bubbles instead of raw telemetry cards +- User prompts can appear immediately in chat +- Assistant replies are projected from Pi session/runtime events +- Session/runtime errors are kept out of the main chat transcript surface + +### Done: Realtime / event-ingestion hardening +- Duplicate runtime-event ingestion was debugged and deduped in the app shell +- Activity panel merging was deduped to stop repeated React key collisions +- Prompt path was changed so frontend requests return immediately instead of blocking on full agent completion +- Runtime stream now continuously surfaces new daemon events without requiring manual refresh for each turn + +--- + +## Partially complete + +### Partial: Sessions / conversation model +What works now: +- one embedded project orchestrator +- left-panel conversation surface +- runtime console telemetry + +What is still missing: +- robust multi-session model +- explicit worker-session UI +- Full activity panel integration for orchestrator + worker histories +- clearer separation of orchestrator conversation vs mission/worker conversation surfaces + +### Partial: Runtime observability +What works now: +- tool execution visibility +- runtime stream +- prompt submission + visible orchestrator progress + +What is still missing: +- stronger stuck-session diagnostics +- clearer "thinking vs waiting vs blocked vs completed" state presentation +- better recovery/restart UX when a live session fails + +### Partial: Launch plumbing +What works now: +- orchestrator prompt flow in the left panel +- UI-triggered launch route exists + +What is still missing: +- launch-anywhere completion across all PRD surfaces +- more complete task/graph/swarm/mission launch affordances +- better contextual launch packaging per surface + +--- + +## Remaining roadmap + +## Phase 1 - Worker agents / sub-agents +**Status:** ✅ DONE (2026-03-06) +**Plan:** `docs/plans/2026-03-06-phase-1-worker-spawning.md` + +### Shipped +- ✅ Worker spawning tool (`bb_spawn_worker`) +- ✅ Worker status tool (`bb_worker_status`) +- ✅ Worker session manager with isolated sessions +- ✅ Worker events in runtime console with "Worker" badge +- ✅ Worker lifecycle (spawning → working → completed/failed) +- ✅ Multiple parallel workers supported +- ✅ Archetype parameter support +- ✅ Worker results merge back to orchestrator chat + +--- + +## Phase 2 - Archetypes as executable agent types +**Status:** ✅ DONE (2026-03-06) +**Plan:** `docs/plans/2026-03-06-phase-2-archetype-configs.md` + +### Shipped +- ✅ Archetype CRUD tools (`bb_list_archetypes`, `bb_create_archetype`, etc.) +- ✅ Template CRUD tools (`bb_list_templates`, `bb_create_template`, etc.) +- ✅ Worker session manager loads archetype config +- ✅ Capabilities mapped to tool access (full vs read-only) +- ✅ System prompt injection per archetype + +--- + +## Phase 3 - Agent-based orchestration +**Status:** ✅ DONE (2026-03-07) - **Testing in progress** +**Plan:** `docs/plans/2026-03-07-phase-3-agent-orchestration.md` + +### Shipped +- ✅ Renamed "archetype" → "agent" everywhere user-facing +- ✅ Agent instances with numbered display names (Engineer 01, etc.) +- ✅ Agent status panel in right panel +- ✅ Agent state persistence (`.beads/agents.jsonl`) +- ✅ Template spawning tool (`bb_spawn_team`) +- ✅ Natural language task descriptions (no task_id required) +- ✅ Auto-template selection from description keywords +- ✅ Decision tree in orchestrator prompt +- ✅ Agent assignment to beads (`bb_assign_agent`) + +### Additional (2026-03-07) +- ✅ **Bead-required workflow** - every worker task has a bead + - `bb_create`, `bb_update`, `bb_close`, `bb_show`, `bb_ready` tools + - Workers must claim bead → update progress → close with summary +- ✅ **Async worker coordination** - non-blocking spawn + - `bb_worker_results` tool - get results from completed workers + - Orchestrator can check status mid-task + - Continue conversation while workers run +- ✅ **File verification pattern** - orchestrator reads actual files + - Not just result strings, but actual implementation + - Makes orchestrator a true reviewer + +--- + +## Phase 4 - Multi-surface launch-anywhere UX +**Status:** Partially done + +### Remaining work +1. Complete launch affordances on: + - task cards + - graph nodes + - swarm views + - mission inspectors + - sessions contexts + - blocked triage contexts +2. Ensure each launch path packages the right local context automatically +3. Make orchestrator interactions feel consistent across surfaces + +--- + +## Phase 5 - Agent presence in social/graph/activity views +**Status:** Not done + +### Remaining work +1. Make orchestrator + worker sessions visible in social cards, graph nodes, and activity panel +2. Support switching between active workers via left-panel orchestrator +3. Preserve longer conversation history cleanly +4. Add intervention / redirection UX for active worker sessions + +--- + +## Phase 6 - Runtime hardening and persistence +**Status:** Partially done + +### Remaining work +1. Reduce drift between TUI Pi loader path and embedded Pi loader path +2. Harden reconnect/restart behavior for embedded sessions +3. Improve stuck/hung agent diagnostics +4. Clarify true host-daemon vs in-process lifecycle direction +5. Strengthen project-scoped persistence and restoration guarantees + +--- + +## Phase 7 — Tests and verification +**Status:** Mostly not done + +### Remaining work +1. Unit coverage for runtime path resolution / event projection / chat projection +2. Contract tests for adapter and runtime event schemas +3. Integration tests for orchestrator session creation + prompt flow +4. UI tests for left-panel orchestrator chat behavior +5. End-to-end tests for prompt → tool → reply flow +6. Failure-path tests for runtime import/session/tool errors + +--- + +## Phase 8 — Unified Settings System (Future) +**Status:** Not started, documented in PRD Section 24 + +### Goal +Comprehensive settings for CLI and frontend: model selection, provider auth, UI preferences, runtime config. + +### See +`docs/plans/2026-03-05-embedded-pi-prd.md` Section 24 for full requirements. + +--- + +## Phase 9 — Holistic Skill Update (After All Phases Complete) +**Status:** Not started, depends on Phases 1-8 + +### Goal +Update `skills/beadboard-driver/` to reflect the new agent-based architecture. + +### Why This Is Needed +The skill documentation was written before Phase 1-3 decisions: +- Archetypes were renamed to Agents +- Agent instances get numbered display names (Engineer 01, etc.) +- Templates are how teams are composed +- Workers spawn via `bb_spawn_worker(description)` not `bd create` +- Natural language task descriptions, not task_id requirements + +### Files to Update + +**Core Skill:** +- `skills/beadboard-driver/SKILL.md` - Main runbook + +**References:** +- `skills/beadboard-driver/references/archetypes-templates-swarms.md` - Rename archetypes → agents +- `skills/beadboard-driver/references/command-matrix.md` - Add new agent tools +- `skills/beadboard-driver/references/agent-state-liveness.md` - Update for numbered instances +- `skills/beadboard-driver/references/session-lifecycle.md` - Update worker spawn flow +- `skills/beadboard-driver/references/coordination-system.md` - May need updates +- `skills/beadboard-driver/references/creating-beads.md` - May need updates + +### Key Changes to Document + +1. **Agent Types (was Archetypes)** + - 6 built-in: architect, engineer, reviewer, tester, investigator, shipper + - CRUD tools: `bb_list_agents`, `bb_create_agent`, etc. + - Each has capabilities that determine tool access + +2. **Agent Instances** + - Numbered display names: "Engineer 01", "Engineer 02" + - Unique instance IDs: `{type}-{number}-{random}` + - Status panel shows active instances + +3. **Templates** + - Named compositions: feature-dev, bug-fix, etc. + - Spawn via `bb_spawn_team(description)` or `bb_spawn_team(description, template)` + - Auto-select template from description keywords + +4. **Worker Spawning** + - Natural language: `bb_spawn_worker(description: "Fix the login bug")` + - No task_id required - auto-generated from description + - Optional `bead_id` to assign to existing bead + +5. **Orchestrator Decision Tree** + - Small task → spawn 1 agent + - Medium task → spawn 2-3 agents + - Large task → use template + +### Scripts to Review +- `scripts/generate-agent-name.mjs` - May need update for new naming +- `scripts/session-preflight.mjs` - May reference old concepts + +### Effort +~2-3 hours + +--- + +## Suggested next build order + +1. **Worker spawning tool + worker session model** +2. **Archetype-backed execution config** +3. **Template-first orchestration behavior** +4. **Activity panel integration for orchestrator/workers** +5. **Launch-anywhere UX completion** +6. **Runtime hardening + automated tests** + +--- + +## Files most relevant to current Embedded Pi implementation + +### Core runtime +- `src/lib/bb-daemon.ts` +- `src/lib/embedded-daemon.ts` +- `src/lib/pi-daemon-adapter.ts` +- `src/lib/pi-runtime-detection.ts` +- `src/lib/bb-pi-bootstrap.ts` + +### TUI / shared Pi integration references +- `src/tui/bb-agent-tui.ts` +- `src/tui/system-prompt.ts` +- `src/tui/tools/bb-dolt-read.ts` +- `src/tui/tools/bb-deviation.ts` +- `src/tui/tools/bb-mailbox.ts` +- `src/tui/tools/bb-presence.ts` +- `src/tui/tools/bb-spawn-worker.ts` +- `src/tui/tools/bb-spawn-template.ts` +- `src/tui/tools/bb-worker-status.ts` +- `src/tui/tools/bb-worker-results.ts` +- `src/tui/tools/bb-bead-crud.ts` +- `src/tui/tools/bb-list-agents.ts` +- `src/tui/tools/bb-create-agent.ts` +- `src/tui/tools/bb-assign-agent.ts` + +### Frontend surfaces +- `src/components/shared/orchestrator-panel.tsx` +- `src/components/shared/runtime-console.tsx` +- `src/components/shared/unified-shell.tsx` +- `src/components/shared/left-panel-new.tsx` +- `src/lib/orchestrator-chat.ts` + +--- + +## Summary + +**Phase 3 COMPLETE - Testing in progress.** + +What is proven now: +- ✅ Embedded orchestrator runtime +- ✅ Frontend prompt path +- ✅ Realtime telemetry +- ✅ Left-panel orchestrator chat +- ✅ BeadBoard-aware tool execution +- ✅ Worker spawning with numbered instances +- ✅ Agent types with capabilities +- ✅ Template-based team spawning +- ✅ Bead-required workflow +- ✅ Async worker coordination + +What remains: +- Phase 4: Launch-anywhere UX (spawn from task cards, graph nodes) +- Phase 5: Agent presence in social/graph views +- Phase 6: Runtime hardening +- Phase 7: Tests and verification +- Phase 8: Unified Settings +- Phase 9: Holistic skill update diff --git a/docs/plans/2026-03-06-phase-1-worker-spawning.md b/docs/plans/2026-03-06-phase-1-worker-spawning.md new file mode 100644 index 0000000..28abe4b --- /dev/null +++ b/docs/plans/2026-03-06-phase-1-worker-spawning.md @@ -0,0 +1,419 @@ +# Phase 1: Worker Agent Spawning + +**Date:** 2026-03-06 +**Status:** Ready for implementation +**PRD Reference:** `docs/plans/2026-03-05-embedded-pi-prd.md` +**Roadmap Reference:** `docs/plans/2026-03-05-embedded-pi-roadmap.md` + +--- + +## Goal + +Enable the orchestrator Pi to spawn real worker Pi instances for parallel task execution. + +This is the biggest gap between "embedded chat" and "true BeadBoard execution substrate." + +--- + +## Current State + +- One orchestrator Pi per project (working) +- Orchestrator can receive prompts and execute tools +- BeadBoard tools exist: `bb_dolt_read`, `bb_mailbox`, `bb_presence`, `bb_deviation` +- Runtime events flow to frontend via SSE +- Left panel shows orchestrator chat + +--- + +## Target State + +- Orchestrator can spawn worker Pi instances +- Each worker has isolated session/context +- Workers execute tasks independently +- Worker status/progress visible in UI +- Worker results merge back to orchestrator + +--- + +## Architecture + +### Session Model + +``` +Project +├── Orchestrator Session (long-lived) +│ └── Spawns workers, coordinates, reviews results +│ +├── Worker Session 1 (task-scoped, ephemeral) +│ └── Executes specific task, reports back +│ +├── Worker Session 2 (task-scoped, ephemeral) +│ └── Executes specific task, reports back +│ +└── Worker Session N... +``` + +### Key Distinctions + +| Aspect | Orchestrator | Worker | +|--------|--------------|--------| +| Lifetime | Long-lived (project-scoped) | Ephemeral (task-scoped) | +| Context | Full project context | Task-specific context | +| Tools | All tools + spawn tool | Subset (no spawn) | +| Status | Always visible | Visible while running | +| Session | Reused across prompts | Created per task, destroyed on completion | + +--- + +## Implementation Tasks + +### Task 1: Worker Session Manager + +**File:** `src/lib/worker-session-manager.ts` + +**Purpose:** Manage worker Pi session lifecycle separate from orchestrator. + +**What to build:** +```typescript +interface WorkerSession { + id: string; + projectId: string; + taskId: string; + status: 'spawning' | 'working' | 'completed' | 'failed'; + session: any; // Pi SDK session + createdAt: string; + completedAt: string | null; + result: string | null; + error: string | null; +} + +class WorkerSessionManager { + private workers = new Map(); + + async spawnWorker(params: { + projectRoot: string; + taskId: string; + taskContext: string; + archetype?: string; + }): Promise; + + getWorker(workerId: string): WorkerSession | undefined; + listWorkers(projectRoot: string): WorkerSession[]; + terminateWorker(workerId: string): Promise; + waitForWorker(workerId: string): Promise; +} +``` + +**Test file:** `tests/lib/worker-session-manager.test.ts` + +**Commands:** +```bash +cd /home/clawdbot/clawd/repos/beadboard +touch src/lib/worker-session-manager.ts +touch tests/lib/worker-session-manager.test.ts +``` + +--- + +### Task 2: Worker Spawning Tool + +**File:** `src/tui/tools/bb-spawn-worker.ts` + +**Purpose:** Tool that orchestrator calls to spawn a worker. + +**What to build:** +```typescript +import { Type } from '@sinclair/typebox'; +import type { CustomAgentTool } from '@mariozechner/pi-coding-agent'; + +export function createSpawnWorkerTool(projectRoot: string): CustomAgentTool { + return { + name: 'bb_spawn_worker', + label: 'Spawn Worker Agent', + description: 'Spawn a worker agent to execute a specific task in parallel. The worker will work independently and report back results.', + parameters: Type.Object({ + task_id: Type.String({ description: 'The ID of the task for the worker to work on' }), + task_context: Type.String({ description: 'Context/instructions for the worker' }), + archetype: Type.Optional(Type.String({ description: 'Optional archetype for worker behavior (e.g., "coder", "reviewer", "tester")' })), + }), + async execute(_toolCallId, params: any) { + // 1. Validate task exists + // 2. Spawn worker session via WorkerSessionManager + // 3. Emit worker.spawned event + // 4. Return worker ID and status + }, + }; +} +``` + +**Test file:** `tests/tui/tools/bb-spawn-worker.test.ts` + +**Commands:** +```bash +cd /home/clawdbot/clawd/repos/beadboard +touch src/tui/tools/bb-spawn-worker.ts +touch tests/tui/tools/bb-spawn-worker.test.ts +``` + +--- + +### Task 3: Worker Status Tool + +**File:** `src/tui/tools/bb-worker-status.ts` + +**Purpose:** Tool for orchestrator to check worker status. + +**What to build:** +```typescript +export function createWorkerStatusTool(projectRoot: string): CustomAgentTool { + return { + name: 'bb_worker_status', + label: 'Check Worker Status', + description: 'Check the status of a spawned worker agent.', + parameters: Type.Object({ + worker_id: Type.String({ description: 'The ID of the worker to check' }), + }), + async execute(_toolCallId, params: any) { + // Return worker status, progress, result (if completed) + }, + }; +} +``` + +**Test file:** `tests/tui/tools/bb-worker-status.test.ts` + +**Commands:** +```bash +cd /home/clawdbot/clawd/repos/beadboard +touch src/tui/tools/bb-worker-status.ts +touch tests/tui/tools/bb-worker-status.test.ts +``` + +--- + +### Task 4: Update Pi Daemon Adapter for Workers + +**File:** `src/lib/pi-daemon-adapter.ts` + +**Changes:** +1. Import worker tools +2. Pass worker session manager reference +3. Add worker events to session subscription + +**Specific edits:** + +After line 64 (tools array), add: +```typescript +// Import worker tools +const { createSpawnWorkerTool } = await import('../tui/tools/bb-spawn-worker'); +const { createWorkerStatusTool } = await import('../tui/tools/bb-worker-status'); +``` + +In customTools array, add: +```typescript +{ tool: createSpawnWorkerTool(projectRoot) }, +{ tool: createWorkerStatusTool(projectRoot) }, +``` + +--- + +### Task 5: Runtime Event Types for Workers + +**File:** `src/lib/embedded-runtime.ts` + +**Already has:** +- `worker.spawned` +- `worker.updated` +- `worker.completed` +- `worker.failed` + +**Verify these are used correctly in event emission.** + +--- + +### Task 6: Worker Events in Daemon + +**File:** `src/lib/embedded-daemon.ts` + +**Add helper method:** +```typescript +appendWorkerEvent(projectRoot: string, workerId: string, event: { + kind: 'worker.spawned' | 'worker.updated' | 'worker.completed' | 'worker.failed'; + title: string; + detail: string; + status?: RuntimeConsoleEvent['status']; +}): void { + this.appendEvent(projectRoot, { + kind: event.kind, + title: event.title, + detail: event.detail, + status: event.status, + metadata: { workerId }, + }); +} +``` + +--- + +### Task 7: Frontend Worker Status Display + +**File:** `src/components/shared/runtime-console.tsx` + +**Changes:** +1. Add worker event rendering (distinct from orchestrator events) +2. Show worker spawn/complete/fail with visual indicators +3. Display worker ID and task association + +**File:** `src/components/shared/orchestrator-panel.tsx` + +**Changes:** +1. Show active workers count +2. List workers with status badges +3. Click to expand worker details + +--- + +### Task 8: Worker Isolation + +**File:** `src/lib/worker-session-manager.ts` + +**Ensure:** +1. Workers use separate session from orchestrator +2. Worker context is task-scoped, not project-scoped +3. Worker cannot spawn more workers (no recursion) +4. Worker results are captured, not lost + +**Worker system prompt:** +```typescript +const workerPrompt = ` +You are a worker agent for BeadBoard. Your job is to execute a specific task. + +Task ID: ${taskId} +Task Context: ${taskContext} + +Rules: +- Focus only on this task +- Report progress via bb_presence tool +- When complete, summarize what you did +- If blocked, report why +- You cannot spawn more workers +`; +``` + +--- + +### Task 9: Integration Tests + +**File:** `tests/integration/worker-spawning.test.ts` + +**Test scenarios:** +1. Orchestrator spawns worker successfully +2. Worker executes task and reports completion +3. Worker failure is captured and reported +4. Multiple workers can run in parallel +5. Worker status tool returns correct state +6. Events flow to frontend correctly + +**Commands:** +```bash +cd /home/clawdbot/clawd/repos/beadboard +mkdir -p tests/integration +touch tests/integration/worker-spawning.test.ts +``` + +--- + +### Task 10: Manual E2E Test + +**Steps:** +1. Start BeadBoard dev server +2. Open left panel orchestrator chat +3. Send prompt: "Spawn a worker to read the README.md and summarize it" +4. Verify: + - `worker.spawned` event appears in console + - Worker status shows in UI + - Worker completes with result + - `worker.completed` event appears + - Orchestrator receives result summary + +--- + +## Files Summary + +| File | Action | Purpose | +|------|--------|---------| +| `src/lib/worker-session-manager.ts` | Create | Manage worker lifecycle | +| `src/tui/tools/bb-spawn-worker.ts` | Create | Tool for spawning | +| `src/tui/tools/bb-worker-status.ts` | Create | Tool for status checks | +| `src/lib/pi-daemon-adapter.ts` | Edit | Add worker tools | +| `src/lib/embedded-daemon.ts` | Edit | Add worker event helper | +| `src/components/shared/runtime-console.tsx` | Edit | Display worker events | +| `src/components/shared/orchestrator-panel.tsx` | Edit | Show worker list | +| `tests/lib/worker-session-manager.test.ts` | Create | Unit tests | +| `tests/tui/tools/bb-spawn-worker.test.ts` | Create | Tool tests | +| `tests/tui/tools/bb-worker-status.test.ts` | Create | Tool tests | +| `tests/integration/worker-spawning.test.ts` | Create | Integration tests | + +--- + +## Success Criteria + +- [ ] Orchestrator can call `bb_spawn_worker` tool +- [ ] Worker session is created and tracked +- [ ] Worker executes task independently +- [ ] Worker events appear in runtime console +- [ ] Worker status is queryable via `bb_worker_status` +- [ ] Worker completion/failure is captured +- [ ] Multiple workers can run in parallel +- [ ] Workers cannot spawn more workers +- [ ] All tests pass + +--- + +## Risks + +| Risk | Mitigation | +|------|------------| +| Worker context bleeds into orchestrator | Separate session objects, isolated state | +| Too many workers spawn | Limit max concurrent workers per project | +| Worker hangs | Add timeout, auto-terminate stuck workers | +| Events duplicate | Use dedupe logic already in place | + +--- + +## Estimated Effort + +- Tasks 1-3 (Core): 2-3 hours +- Tasks 4-6 (Integration): 1-2 hours +- Tasks 7-8 (UI + Isolation): 2-3 hours +- Tasks 9-10 (Testing): 1-2 hours + +**Total:** 6-10 hours + +--- + +## Execution Order + +Recommended sequence: +1. Task 1 → 2 → 3 (Core tools) +2. Task 4 → 5 → 6 (Integration) +3. Task 8 (Isolation - critical before testing) +4. Task 7 (UI) +5. Task 9 → 10 (Testing) + +--- + +## Dependencies + +- Pi SDK (already integrated) +- Existing daemon/adapter infrastructure +- Runtime event system (already working) + +--- + +## Next After Phase 1 + +Once workers can spawn and execute: +- **Phase 2:** Archetype-backed execution configs +- **Phase 3:** Template-first orchestration with workers +- **Phase 5:** Show workers in social/graph views diff --git a/docs/plans/2026-03-06-phase-2-archetype-configs.md b/docs/plans/2026-03-06-phase-2-archetype-configs.md new file mode 100644 index 0000000..378d1cc --- /dev/null +++ b/docs/plans/2026-03-06-phase-2-archetype-configs.md @@ -0,0 +1,496 @@ +# Phase 2: Archetype Execution Configs + +**Date:** 2026-03-06 +**Status:** Ready for implementation +**PRD Reference:** `docs/plans/2026-03-05-embedded-pi-prd.md` +**Depends on:** Phase 1 (Worker Spawning) ✅ + +--- + +## Goal + +1. Link existing archetype system to worker behavior +2. Give orchestrator CRUD tools for archetypes and templates + +--- + +## Current State + +- **Frontend** has full archetype system: + - Schema: `AgentArchetype` with `id`, `name`, `systemPrompt`, `capabilities[]`, `color` + - Storage: `.beads/archetypes/*.json` + - UI: `archetype-inspector.tsx` for create/edit/clone + - Seed archetypes: architect, coder, reviewer, tester, researcher +- **Templates** also exist: + - Schema: `SwarmTemplate` with `team: { archetypeId, count }[]` + - Storage: `.beads/templates/*.json` +- **Backend** (`beads-fs.ts`) has all CRUD functions +- **Worker spawning** passes `archetype` but doesn't use it +- **Orchestrator** has NO tools to manage archetypes/templates + +--- + +## Target State + +- Orchestrator can CRUD archetypes and templates via tools +- Workers load archetype config and behave accordingly: + - `capabilities` → which tools worker gets + - `systemPrompt` → injected into worker prompt + +--- + +## Capability → Tool Mapping + +| Capability | Tools Granted | +|------------|---------------| +| `coding`, `implementation` | read, bash, edit, write, dolt-read | +| `planning`, `design_docs` | read, dolt-read (read-only) | +| `review`, `arch_review` | read, dolt-read (read-only) | +| `testing` | read, bash, edit, write, dolt-read | +| `research` | read, dolt-read, bash (limited) | +| All others | read, dolt-read (default read-only) | + +**Rule:** If `capabilities` includes `coding` or `implementation` or `testing` → full tools. Otherwise → read-only. + +--- + +## Implementation + +### Task 1: Create Archetype CRUD Tools + +**File:** `src/tui/tools/bb-list-archetypes.ts` + +```typescript +import { createTool } from '@mariozechner/pi-coding-agent'; +import { getArchetypes } from '../../lib/server/beads-fs'; + +export function bbListArchetypes(projectRoot: string) { + return createTool('bb_list_archetypes', { + description: 'List all available archetypes. Returns id, name, description, capabilities for each.', + parameters: {}, + handler: async () => { + const archetypes = await getArchetypes(); + return { + archetypes: archetypes.map(a => ({ + id: a.id, + name: a.name, + description: a.description, + capabilities: a.capabilities, + color: a.color, + isBuiltIn: a.isBuiltIn, + })), + }; + }, + }); +} +``` + +**File:** `src/tui/tools/bb-create-archetype.ts` + +```typescript +import { createTool } from '@mariozechner/pi-coding-agent'; +import { saveArchetype } from '../../lib/server/beads-fs'; +import { z } from 'zod'; + +export function bbCreateArchetype(projectRoot: string) { + return createTool('bb_create_archetype', { + description: 'Create a new archetype. Requires name, description, systemPrompt, capabilities, color.', + parameters: z.object({ + name: z.string().describe('Display name for the archetype'), + description: z.string().describe('What this archetype does'), + systemPrompt: z.string().describe('System prompt injected into workers with this archetype'), + capabilities: z.array(z.string()).describe('List of capabilities (e.g., ["coding", "testing"])'), + color: z.string().default('#3b82f6').describe('Hex color for display'), + }), + handler: async (params) => { + const archetype = await saveArchetype({ + name: params.name, + description: params.description, + systemPrompt: params.systemPrompt, + capabilities: params.capabilities, + color: params.color, + isBuiltIn: false, + }); + return { ok: true, archetype }; + }, + }); +} +``` + +**File:** `src/tui/tools/bb-update-archetype.ts` + +```typescript +import { createTool } from '@mariozechner/pi-coding-agent'; +import { saveArchetype } from '../../lib/server/beads-fs'; +import { z } from 'zod'; + +export function bbUpdateArchetype(projectRoot: string) { + return createTool('bb_update_archetype', { + description: 'Update an existing archetype. Cannot modify built-in archetypes.', + parameters: z.object({ + id: z.string().describe('Archetype ID to update'), + name: z.string().optional().describe('New display name'), + description: z.string().optional().describe('New description'), + systemPrompt: z.string().optional().describe('New system prompt'), + capabilities: z.array(z.string()).optional().describe('New capabilities list'), + color: z.string().optional().describe('New hex color'), + }), + handler: async (params) => { + const archetype = await saveArchetype({ + id: params.id, + name: params.name ?? '', + description: params.description ?? '', + systemPrompt: params.systemPrompt ?? '', + capabilities: params.capabilities ?? [], + color: params.color ?? '#3b82f6', + }); + return { ok: true, archetype }; + }, + }); +} +``` + +**File:** `src/tui/tools/bb-delete-archetype.ts` + +```typescript +import { createTool } from '@mariozechner/pi-coding-agent'; +import { deleteArchetype } from '../../lib/server/beads-fs'; +import { z } from 'zod'; + +export function bbDeleteArchetype(projectRoot: string) { + return createTool('bb_delete_archetype', { + description: 'Delete an archetype. Cannot delete built-in archetypes.', + parameters: z.object({ + id: z.string().describe('Archetype ID to delete'), + }), + handler: async (params) => { + await deleteArchetype(params.id); + return { ok: true, deletedId: params.id }; + }, + }); +} +``` + +--- + +### Task 2: Create Template CRUD Tools + +**File:** `src/tui/tools/bb-list-templates.ts` + +```typescript +import { createTool } from '@mariozechner/pi-coding-agent'; +import { getTemplates } from '../../lib/server/beads-fs'; + +export function bbListTemplates(projectRoot: string) { + return createTool('bb_list_templates', { + description: 'List all swarm templates. Returns team composition for each.', + parameters: {}, + handler: async () => { + const templates = await getTemplates(); + return { + templates: templates.map(t => ({ + id: t.id, + name: t.name, + description: t.description, + team: t.team, + isBuiltIn: t.isBuiltIn, + })), + }; + }, + }); +} +``` + +**File:** `src/tui/tools/bb-create-template.ts` + +```typescript +import { createTool } from '@mariozechner/pi-coding-agent'; +import { saveTemplate } from '../../lib/server/beads-fs'; +import { z } from 'zod'; + +export function bbCreateTemplate(projectRoot: string) { + return createTool('bb_create_template', { + description: 'Create a new swarm template. Defines team composition by archetype.', + parameters: z.object({ + name: z.string().describe('Display name for the template'), + description: z.string().describe('What this template is for'), + team: z.array(z.object({ + archetypeId: z.string().describe('Archetype ID'), + count: z.number().describe('Number of workers with this archetype'), + })).describe('Team composition'), + color: z.string().default('#f59e0b').describe('Hex color for display'), + }), + handler: async (params) => { + const template = await saveTemplate({ + name: params.name, + description: params.description, + team: params.team, + color: params.color, + isBuiltIn: false, + }); + return { ok: true, template }; + }, + }); +} +``` + +**File:** `src/tui/tools/bb-update-template.ts` + +```typescript +import { createTool } from '@mariozechner/pi-coding-agent'; +import { saveTemplate } from '../../lib/server/beads-fs'; +import { z } from 'zod'; + +export function bbUpdateTemplate(projectRoot: string) { + return createTool('bb_update_template', { + description: 'Update an existing swarm template.', + parameters: z.object({ + id: z.string().describe('Template ID to update'), + name: z.string().optional().describe('New display name'), + description: z.string().optional().describe('New description'), + team: z.array(z.object({ + archetypeId: z.string(), + count: z.number(), + })).optional().describe('New team composition'), + color: z.string().optional().describe('New hex color'), + }), + handler: async (params) => { + const template = await saveTemplate({ + id: params.id, + name: params.name ?? '', + description: params.description ?? '', + team: params.team ?? [], + color: params.color ?? '#f59e0b', + }); + return { ok: true, template }; + }, + }); +} +``` + +**File:** `src/tui/tools/bb-delete-template.ts` + +```typescript +import { createTool } from '@mariozechner/pi-coding-agent'; +import { deleteTemplate } from '../../lib/server/beads-fs'; +import { z } from 'zod'; + +export function bbDeleteTemplate(projectRoot: string) { + return createTool('bb_delete_template', { + description: 'Delete a swarm template. Cannot delete built-in templates.', + parameters: z.object({ + id: z.string().describe('Template ID to delete'), + }), + handler: async (params) => { + await deleteTemplate(params.id); + return { ok: true, deletedId: params.id }; + }, + }); +} +``` + +--- + +### Task 3: Link Archetype to Worker Behavior + +**File:** `src/lib/worker-session-manager.ts` + +**Changes:** + +1. Import archetype loading: +```typescript +import { getArchetypes, type AgentArchetype } from './server/beads-fs'; +``` + +2. Add capability → tool mapping: +```typescript +function getToolsForCapabilities(capabilities: string[]): { + allowEdit: boolean; + allowWrite: boolean; + allowBash: boolean; +} { + const fullAccess = ['coding', 'implementation', 'testing']; + const hasFullAccess = capabilities.some(c => fullAccess.includes(c)); + + if (hasFullAccess) { + return { allowEdit: true, allowWrite: true, allowBash: true }; + } + + // Read-only for planning, review, research + return { allowEdit: false, allowWrite: false, allowBash: false }; +} +``` + +3. Load archetype in `createWorkerSession`: +```typescript +async createWorkerSession( + worker: WorkerInfo, + taskContext: string, + archetypeId?: string +): Promise { + // Load archetype config + let archetype: AgentArchetype | undefined; + if (archetypeId) { + const archetypes = await getArchetypes(); + archetype = archetypes.find(a => a.id === archetypeId); + } + + const capabilities = archetype?.capabilities ?? []; + const toolAccess = getToolsForCapabilities(capabilities); + + // Build tools based on capabilities + const tools = []; + tools.push(this.sdk.createReadTool(this.projectRoot)); + tools.push(this.sdk.createMailboxTool(this.projectRoot)); + tools.push(this.sdk.createPresenceTool(this.projectRoot)); + + if (toolAccess.allowBash) { + tools.push(this.sdk.createBashTool(this.projectRoot)); + } + if (toolAccess.allowEdit) { + tools.push(this.sdk.createEditTool(this.projectRoot)); + } + if (toolAccess.allowWrite) { + tools.push(this.sdk.createWriteTool(this.projectRoot)); + } + + // Always allow dolt-read for context + const { createDoltReadTool } = await import('../tui/tools/bb-dolt-read'); + tools.push(createDoltReadTool(this.projectRoot)); + + // Build prompt with archetype system prompt + const systemPrompt = this.buildWorkerPrompt( + worker.taskId, + taskContext, + archetype?.systemPrompt + ); + + // ... rest of session creation +} +``` + +4. Update `buildWorkerPrompt` to accept optional archetype prompt: +```typescript +buildWorkerPrompt(taskId: string, taskContext: string, archetypePrompt?: string): string { + return `You are a BeadBoard worker agent. + +Task ID: ${taskId} + +${taskContext} + +${archetypePrompt ? `## Your Specialization\n\n${archetypePrompt}` : ''} + +## Instructions + +Complete your assigned task. Report progress. Ask for help if blocked. +`; +} +``` + +--- + +### Task 4: Register Tools in Orchestrator + +**File:** `src/lib/pi-daemon-adapter.ts` + +**Changes:** + +Add imports and register tools: +```typescript +import { bbListArchetypes } from '../tui/tools/bb-list-archetypes'; +import { bbCreateArchetype } from '../tui/tools/bb-create-archetype'; +import { bbUpdateArchetype } from '../tui/tools/bb-update-archetype'; +import { bbDeleteArchetype } from '../tui/tools/bb-delete-archetype'; +import { bbListTemplates } from '../tui/tools/bb-list-templates'; +import { bbCreateTemplate } from '../tui/tools/bb-create-template'; +import { bbUpdateTemplate } from '../tui/tools/bb-update-template'; +import { bbDeleteTemplate } from '../tui/tools/bb-delete-template'; + +// In createTools(): +tools.push( + bbListArchetypes(this.projectRoot), + bbCreateArchetype(this.projectRoot), + bbUpdateArchetype(this.projectRoot), + bbDeleteArchetype(this.projectRoot), + bbListTemplates(this.projectRoot), + bbCreateTemplate(this.projectRoot), + bbUpdateTemplate(this.projectRoot), + bbDeleteTemplate(this.projectRoot), +); +``` + +--- + +### Task 5: Tests + +**File:** `tests/tui/tools/bb-archetype-crud.test.ts` + +Test: +- List archetypes returns seed data +- Create archetype saves to `.beads/archetypes/` +- Update archetype modifies file +- Delete archetype removes file +- Cannot delete built-in archetypes + +**File:** `tests/tui/tools/bb-template-crud.test.ts` + +Test: +- List templates returns seed data +- Create template saves to `.beads/templates/` +- Update template modifies file +- Delete template removes file +- Cannot delete built-in templates + +**File:** `tests/lib/worker-session-manager.test.ts` + +Test: +- Coder archetype gets full tools +- Reviewer archetype gets read-only tools +- Unknown archetype defaults to read-only +- Archetype prompt injected into system prompt + +--- + +## Files Summary + +| File | Action | +|------|--------| +| `src/tui/tools/bb-list-archetypes.ts` | Create | +| `src/tui/tools/bb-create-archetype.ts` | Create | +| `src/tui/tools/bb-update-archetype.ts` | Create | +| `src/tui/tools/bb-delete-archetype.ts` | Create | +| `src/tui/tools/bb-list-templates.ts` | Create | +| `src/tui/tools/bb-create-template.ts` | Create | +| `src/tui/tools/bb-update-template.ts` | Create | +| `src/tui/tools/bb-delete-template.ts` | Create | +| `src/lib/worker-session-manager.ts` | Edit | +| `src/lib/pi-daemon-adapter.ts` | Edit | +| `tests/tui/tools/bb-archetype-crud.test.ts` | Create | +| `tests/tui/tools/bb-template-crud.test.ts` | Create | +| `tests/lib/worker-session-manager.test.ts` | Create | + +--- + +## Estimated Effort + +2-3 hours + +--- + +## Success Criteria + +- [ ] Orchestrator can list/create/update/delete archetypes +- [ ] Orchestrator can list/create/update/delete templates +- [ ] Worker with coder archetype gets edit/write/bash tools +- [ ] Worker with reviewer archetype gets read-only tools +- [ ] Archetype systemPrompt injected into worker prompt +- [ ] All tests pass + +--- + +## Future Enhancements (Not Now) + +- Fine-grained capability → tool mapping config +- Custom tool sets per archetype +- Model selection per archetype +- Archetype inheritance diff --git a/docs/plans/2026-03-07-phase-3-agent-orchestration.md b/docs/plans/2026-03-07-phase-3-agent-orchestration.md new file mode 100644 index 0000000..e61752b --- /dev/null +++ b/docs/plans/2026-03-07-phase-3-agent-orchestration.md @@ -0,0 +1,1143 @@ +# Phase 3: Agent-Based Orchestration Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Transform BeadBoard from archetype-based to agent-based orchestration where the orchestrator spawns, coordinates, and manages numbered agent instances that users can see in real-time. + +**Architecture:** +- Rename "archetypes" to "agents" everywhere user-facing +- Agents = typed workers with specific prompts/configs (what archetypes were) +- Agent instances = running copies, numbered while active (e.g., "Engineer 01", "Engineer 02") +- Templates = named agent compositions (e.g., "Feature Dev" = Architect + 2x Engineer + Reviewer) +- Right panel shows active agent instances with status + numbers +- Orchestrator picks which agents to spawn based on task scope + +**Tech Stack:** +- Next.js, TypeScript, Pi SDK +- BeadBoard's existing beads/epics system +- Runtime console events for agent status + +--- + +## Rename: Archetype → Agent + +### Task 1: Rename in Code (Internal) + +**Files:** +- Modify: `src/lib/types-swarm.ts` - rename `AgentArchetype` → `AgentType` +- Modify: `src/lib/server/beads-fs.ts` - update SEED_ARCHETYPES comments +- Modify: `src/lib/worker-session-manager.ts` - update type imports and comments + +**Step 1: Rename AgentArchetype type** + +```typescript +// src/lib/types-swarm.ts +export interface AgentType { + id: string; + name: string; + description: string; + systemPrompt: string; + capabilities: string[]; + color: string; + icon?: string; + createdAt: string; + updatedAt: string; + isBuiltIn: boolean; +} +``` + +**Step 2: Update worker-session-manager imports** + +```typescript +// src/lib/worker-session-manager.ts line 4 +import type { AgentType } from './types-swarm'; +``` + +**Step 3: Commit** + +```bash +git add src/lib/types-swarm.ts src/lib/worker-session-manager.ts +git commit -m "refactor: rename AgentArchetype → AgentType internally" +``` + +--- + +### Task 2: Rename in Tools (User-Facing) + +**Files:** +- Modify: `src/tui/tools/bb-list-archetypes.ts` → rename to `bb-list-agents.ts` +- Modify: `src/tui/tools/bb-create-archetype.ts` → rename to `bb-create-agent.ts` +- Modify: `src/tui/tools/bb-update-archetype.ts` → rename to `bb-update-agent.ts` +- Modify: `src/tui/tools/bb-delete-archetype.ts` → rename to `bb-delete-agent.ts` +- Modify: `src/tui/tools/bb-spawn-worker.ts` - update archetype param description +- Modify: `src/lib/pi-daemon-adapter.ts` - update imports + +**Step 1: Rename bb-list-archetypes.ts** + +```typescript +// src/tui/tools/bb-list-agents.ts +import { Type } from '@sinclair/typebox'; +import type { CustomAgentTool } from '@mariozechner/pi-coding-agent'; +import { getAgentTypes } from '../../lib/server/beads-fs'; + +export function createListAgentsTool(projectRoot: string): CustomAgentTool { + return { + name: 'bb_list_agents', + label: 'List Agent Types', + description: 'List all available agent types. Returns id, name, description, capabilities for each. Agent types are the kinds of workers the orchestrator can spawn.', + parameters: Type.Object({}), + async execute() { + // ... same as existing, but rename "archetype" → "agent" in output + }, + }; +} +``` + +**Step 2: Rename other archetype tools similarly** + +Create new files with "agent" naming, delete old "archetype" files. + +**Step 3: Update bb-spawn-worker.ts param description** + +```typescript +// src/tui/tools/bb-spawn-worker.ts line 11 +archetype: Type.Optional(Type.String({ + description: 'Agent type to spawn. Options: "architect", "engineer", "reviewer", "tester", "investigator", "shipper". Default: engineer.' +})), +``` + +**Step 4: Update pi-daemon-adapter.ts imports** + +```typescript +// src/lib/pi-daemon-adapter.ts - update import paths +import { createListAgentsTool } from '../tui/tools/bb-list-agents'; +// ... update all 8 tool imports +``` + +**Step 5: Commit** + +```bash +git add src/tui/tools/ src/lib/pi-daemon-adapter.ts +git commit -m "refactor: rename archetype tools → agent tools" +``` + +--- + +### Task 3: Rename in Database Seed + +**Files:** +- Modify: `src/lib/server/beads-fs.ts` - rename SEED_ARCHETYPES → SEED_AGENTS + +**Step 1: Rename constants** + +```typescript +// src/lib/server/beads-fs.ts +const SEED_AGENTS: AgentType[] = [ /* same content */ ]; +const SEED_TEMPLATES: SwarmTemplate[] = [ /* same content */ ]; +``` + +**Step 2: Rename functions** + +```typescript +export async function getAgentTypes(projectRoot: string = process.cwd()): Promise { + // ... same implementation +} +``` + +**Step 3: Update callers** + +```bash +grep -rn "getArchetypes" src/ | grep -v test +# Update each to getAgentTypes +``` + +**Step 4: Commit** + +```bash +git add src/lib/server/beads-fs.ts +git commit -m "refactor: rename getArchetypes → getAgentTypes" +``` + +--- + +### Task 4: Rename in UI Components + +**Files:** +- Modify: `src/components/swarm/archetype-inspector.tsx` → `agent-inspector.tsx` +- Modify: `src/components/swarm/telemetry-grid.tsx` - update archetype props +- Modify: Any other UI files referencing "archetype" + +**Step 1: Rename archetype-inspector.tsx** + +```bash +mv src/components/swarm/archetype-inspector.tsx src/components/swarm/agent-inspector.tsx +``` + +**Step 2: Update internal references** + +```typescript +// In agent-inspector.tsx +// Change "Archetype" → "Agent" in all display text +``` + +**Step 3: Commit** + +```bash +git add src/components/swarm/ +git commit -m "refactor: rename archetype UI → agent UI" +``` + +--- + +## Agent Instance Display + +### Task 5: Design Agent Instance Model + +**Files:** +- Create: `src/lib/agent-instance.ts` + +**Step 1: Define AgentInstance type** + +```typescript +// src/lib/agent-instance.ts + +export interface AgentInstance { + id: string; // unique instance ID (e.g., "engineer-01-abc123") + agentTypeId: string; // what kind of agent (e.g., "engineer") + displayName: string; // e.g., "Engineer 01" + status: 'spawning' | 'working' | 'idle' | 'completed' | 'failed'; + currentBeadId?: string; // bead they're working on + startedAt: string; + completedAt?: string; + result?: string; + error?: string; +} + +export interface AgentStatus { + totalActive: number; + byType: Record; // { "engineer": 2, "architect": 1 } + instances: AgentInstance[]; +} +``` + +**Step 2: Create helper functions** + +```typescript +export function generateInstanceId(agentTypeId: string, existingCount: number): string { + const suffix = String(existingCount + 1).padStart(2, '0'); + const random = Math.random().toString(36).slice(2, 8); + return `${agentTypeId}-${suffix}-${random}`; +} + +export function getDisplayName(agentTypeId: string, instanceNumber: number, agentTypeName: string): string { + const num = String(instanceNumber).padStart(2, '0'); + return `${agentTypeName} ${num}`; +} +``` + +**Step 3: Commit** + +```bash +git add src/lib/agent-instance.ts +git commit -m "feat: add AgentInstance model for tracking running agents" +``` + +--- + +### Task 6: Integrate with Worker Session Manager + +**Files:** +- Modify: `src/lib/worker-session-manager.ts` + +**Step 1: Import AgentInstance** + +```typescript +import { generateInstanceId, getDisplayName, type AgentInstance } from './agent-instance'; +``` + +**Step 2: Add instance tracking to worker session** + +```typescript +// In WorkerSession interface, add: +agentInstanceId: string; +agentTypeId: string; +displayName: string; +``` + +**Step 3: Generate instance ID when spawning** + +```typescript +// In spawnWorker(), when creating worker: +const existingCount = [...this.workers.values()].filter(w => w.agentTypeId === archetype).length; +const agentType = archetype || 'engineer'; +const agentTypeName = getAgentTypeName(agentType); // lookup name + +worker.agentInstanceId = generateInstanceId(agentType, existingCount); +worker.agentTypeId = agentType; +worker.displayName = getDisplayName(agentType, existingCount + 1, agentTypeName); +``` + +**Step 4: Add AgentInstance to event emissions** + +```typescript +// When emitting worker events, include instance info: +embeddedPiDaemon.appendWorkerEvent(projectRoot, worker.id, { + kind: 'worker.spawned', + title: `${worker.displayName} started`, + detail: `Agent working on task ${taskId}`, + status: 'working', + metadata: { + workerId: worker.id, + agentInstanceId: worker.agentInstanceId, + agentTypeId: worker.agentTypeId, + displayName: worker.displayName, + taskId, + }, +}); +``` + +**Step 5: Commit** + +```bash +git add src/lib/worker-session-manager.ts +git commit -m "feat: track agent instances with numbered display names" +``` + +--- + +### Task 7: Create Agent Status Panel + +**Files:** +- Create: `src/components/agents/agent-status-panel.tsx` + +**Step 1: Define AgentStatusPanel component** + +```typescript +// src/components/agents/agent-status-panel.tsx +'use client'; + +import { useEffect, useState } from 'react'; +import { AgentInstance, type AgentStatus } from '../../lib/agent-instance'; + +interface AgentStatusPanelProps { + projectRoot: string; +} + +export function AgentStatusPanel({ projectRoot }: AgentStatusPanelProps) { + const [status, setStatus] = useState(null); + + useEffect(() => { + // Poll for agent status updates + const poll = setInterval(async () => { + const res = await fetch(`/api/runtime/agents?projectRoot=${encodeURIComponent(projectRoot)}`); + const data = await res.json(); + if (data.ok) setStatus(data.status); + }, 2000); + return () => clearInterval(poll); + }, [projectRoot]); + + if (!status) return
Loading agents...
; + + return ( +
+ {/* Active Agents Section */} +
+

+ Active Agents ({status.totalActive}) +

+ + {status.instances.length === 0 ? ( +

No active agents

+ ) : ( +
+ {status.instances.map(instance => ( + + ))} +
+ )} +
+ + {/* Available Agents Section */} +
+

+ Available Agents +

+ +
+
+ ); +} + +function AgentInstanceCard({ instance }: { instance: AgentInstance }) { + const statusColors = { + spawning: 'bg-yellow-500', + working: 'bg-cyan-500', + idle: 'bg-gray-500', + completed: 'bg-green-500', + failed: 'bg-red-500', + }; + + return ( +
+
+ {instance.displayName} + {instance.currentBeadId && ( + + → {instance.currentBeadId} + + )} +
+ ); +} +``` + +**Step 2: Create API endpoint for agent status** + +```typescript +// src/app/api/runtime/agents/route.ts +import { NextResponse } from 'next/server'; +import { workerSessionManager } from '../../../../lib/worker-session-manager'; + +export const dynamic = 'force-dynamic'; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const projectRoot = searchParams.get('projectRoot'); + + if (!projectRoot) { + return NextResponse.json({ ok: false, error: 'projectRoot required' }); + } + + const workers = workerSessionManager.listWorkers(projectRoot); + + const instances = workers.map(w => ({ + id: w.agentInstanceId, + agentTypeId: w.agentTypeId, + displayName: w.displayName, + status: w.status, + currentBeadId: w.currentBeadId, + startedAt: w.createdAt, + })); + + const byType: Record = {}; + for (const w of workers) { + byType[w.agentTypeId] = (byType[w.agentTypeId] || 0) + 1; + } + + return NextResponse.json({ + ok: true, + status: { + totalActive: workers.length, + byType, + instances, + }, + }); +} +``` + +**Step 3: Commit** + +```bash +git add src/components/agents/ src/app/api/runtime/agents/ +git commit -m "feat: add agent status panel and API" +``` + +--- + +## Orchestrator Behavior + +### Task 8: Update System Prompt for Agent-Based Thinking + +**Files:** +- Modify: `src/tui/system-prompt.ts` - update orchestrator prompt + +**Step 1: Update the prompt template** + +```typescript +// Add this section to the system prompt: + +## Agent System + +You can spawn agent workers to accomplish tasks in parallel. Agents are typed workers with specific capabilities: + +- **architect**: System design, work decomposition, technical decisions +- **engineer**: Implementation, coding, testing, debugging +- **reviewer**: Code review, quality analysis (read-only, does not modify code) +- **tester**: Test design and implementation +- **investigator**: Debugging, root cause analysis (read-only unless implementing fix) +- **shipper**: Deployment, CI/CD, release management + +### Spawning Agents + +Use `bb_spawn_worker` to spawn an agent for a specific task/bead. You can spawn multiple agents in parallel. + +### Agent Instances + +When you spawn an agent, it gets a numbered instance (e.g., "Engineer 01", "Engineer 02"). The right panel shows all active agent instances with their status. + +### Templates + +Templates are named compositions of agents: +- **feature-dev**: architect + 2x engineer + reviewer + tester +- **bug-fix**: investigator + engineer + tester +- **greenfield**: architect + 3x engineer + tester + shipper + +Use templates for large efforts (epics). For small tasks, spawn individual agents directly. + +### Deviation + +If you modify or ignore a template, use `bb_record_deviation` to explain why. +``` + +**Step 2: Commit** + +```bash +git add src/tui/system-prompt.ts +git commit -m "feat: update orchestrator prompt for agent-based thinking" +``` + +--- + +### Task 9: Add Template Selection Tool + +**Files:** +- Create: `src/tui/tools/bb-spawn-template.ts` + +**Step 1: Create spawn-template tool** + +```typescript +import { Type } from '@sinclair/typebox'; +import type { CustomAgentTool } from '@mariozechner/pi-coding-agent'; +import { getTemplates } from '../../lib/server/beads-fs'; +import { workerSessionManager } from '../../lib/worker-session-manager'; + +export function createSpawnTemplateTool(projectRoot: string): CustomAgentTool { + return { + name: 'bb_spawn_template', + label: 'Spawn Template Team', + description: 'Spawn a team of agents based on a template. Creates an epic with beads assigned to appropriate agent types. Use for larger efforts.', + parameters: Type.Object({ + template_id: Type.String({ description: 'Template ID to use (e.g., "feature-dev", "bug-fix", "greenfield", "full-squad")' }), + epic_title: Type.String({ description: 'Title for the epic/task being worked on' }), + epic_description: Type.Optional(Type.String({ description: 'Detailed description of what needs to be done' })), + }), + async execute(_toolCallId, params) { + const { template_id, epic_title, epic_description } = params; + + // Load template + const templates = await getTemplates(projectRoot); + const template = templates.find(t => t.id === template_id); + + if (!template) { + return { + content: [{ type: 'text', text: `Template "${template_id}" not found. Available: ${templates.map(t => t.id).join(', ')}` }], + isError: true, + }; + } + + // For each agent in template, spawn a worker + const spawned = []; + for (const member of template.team) { + for (let i = 0; i < member.count; i++) { + const worker = await workerSessionManager.spawnWorker({ + projectRoot, + taskId: `${epic_title}-${member.archetypeId}-${i+1}`, + taskContext: epic_description || `Part of ${epic_title} using ${template.name} template`, + archetype: member.archetypeId, + }); + spawned.push(worker); + } + } + + return { + content: [{ + type: 'text', + text: `Spawned ${spawned.length} agents from template "${template.name}"!\n\n` + + template.team.map(m => `- ${m.count}x ${m.archetypeId}`).join('\n') + '\n\n' + + `Active agents: ${spawned.map(s => s.displayName).join(', ')}` + }], + details: { template, spawned: spawned.map(s => ({ id: s.id, displayName: s.displayName })) }, + }; + }, + }; +} +``` + +**Step 2: Register in pi-daemon-adapter.ts** + +```typescript +import { createSpawnTemplateTool } from '../tui/tools/bb-spawn-template'; + +// In createTools(): +tools.push({ tool: createSpawnTemplateTool(projectRoot) }); +``` + +**Step 3: Commit** + +```bash +git add src/tui/tools/bb-spawn-template.ts src/lib/pi-daemon-adapter.ts +git commit -m "feat: add template spawning tool" +``` + +--- + +### Task 10: Integration Tests + +**Files:** +- Create: `tests/integration/agent-spawning.test.ts` + +**Step 1: Write integration test** + +```typescript +import { describe, it, expect, beforeEach } from 'vitest'; + +describe('Agent Spawning', () => { + const projectRoot = process.cwd(); + + it('spawns agent with correct instance ID', async () => { + const { workerSessionManager } = await import('../../src/lib/worker-session-manager'); + + const worker = await workerSessionManager.spawnWorker({ + projectRoot, + taskId: 'test-task-1', + taskContext: 'Test task', + archetype: 'engineer', + }); + + expect(worker.displayName).toMatch(/Engineer \d+/); + expect(worker.agentTypeId).toBe('engineer'); + expect(worker.agentInstanceId).toBeDefined(); + }); + + it('numbers instances correctly', async () => { + const { workerSessionManager } = await import('../../src/lib/worker-session-manager'); + + // Spawn 3 engineers + const workers = []; + for (let i = 0; i < 3; i++) { + workers.push(await workerSessionManager.spawnWorker({ + projectRoot, + taskId: `test-task-${i}`, + taskContext: 'Test', + archetype: 'engineer', + })); + } + + // Check numbering + const engineerWorkers = workers.filter(w => w.agentTypeId === 'engineer'); + const numbers = engineerWorkers.map(w => w.displayName); + expect(numbers).toContain('Engineer 01'); + expect(numbers).toContain('Engineer 02'); + expect(numbers).toContain('Engineer 03'); + }); + + it('spawns from template', async () => { + const { workerSessionManager } = await import('../../src/lib/worker-session-manager'); + const { getTemplates } = await import('../../src/lib/server/beads-fs'); + + const templates = await getTemplates(projectRoot); + const featureDev = templates.find(t => t.id === 'feature-dev'); + + expect(featureDev).toBeDefined(); + // Should spawn 5 agents: 1 architect + 2 engineer + 1 reviewer + 1 tester + expect(featureDev.team.reduce((sum, m) => sum + m.count, 0)).toBe(5); + }); +}); +``` + +**Step 2: Run tests** + +```bash +cd /home/clawdbot/clawd/repos/beadboard && npx vitest run tests/integration/agent-spawning.test.ts +``` + +**Step 3: Commit** + +```bash +git add tests/integration/agent-spawning.test.ts +git commit -m "test: add agent spawning integration tests" +``` + +--- + +## Agent-Bead Relationship + +### Task 11: Add Agent Assignment to Beads + +**Files:** +- Modify: `src/lib/types.ts` - add `agentTypeId` to BeadIssue +- Modify: `src/lib/server/beads-fs.ts` - update bead creation/queries +- Create: `src/tui/tools/bb-assign-agent.ts` - assign agent to bead +- Modify: `src/components/social/social-card.tsx` - show agent badge + +**Step 1: Add agentTypeId to BeadIssue type** + +```typescript +// src/lib/types.ts - in BeadIssue interface +export interface BeadIssue { + // ... existing fields + agentTypeId?: string; // Which agent type is assigned + agentInstanceId?: string; // Which specific instance (if running) +} +``` + +**Step 2: Create bb_assign_agent tool** + +```typescript +// src/tui/tools/bb-assign-agent.ts +import { Type } from '@sinclair/typebox'; +import type { CustomAgentTool } from '@mariozechner/pi-coding-agent'; + +export function createAssignAgentTool(projectRoot: string): CustomAgentTool { + return { + name: 'bb_assign_agent', + label: 'Assign Agent to Bead', + description: 'Assign an agent type to a bead. This signals which kind of agent should work on this task.', + parameters: Type.Object({ + bead_id: Type.String({ description: 'Bead ID to assign agent to' }), + agent_type: Type.String({ description: 'Agent type ID (e.g., "engineer", "architect", "reviewer")' }), + }), + async execute(_toolCallId, params) { + const { bead_id, agent_type } = params; + + // Update bead with agent type + // This writes to .beads/issues.jsonl via the beads system + + return { + content: [{ + type: 'text', + text: `Assigned ${agent_type} to bead ${bead_id}. When an agent of this type is spawned, it will pick up this bead.`, + }], + details: { bead_id, agent_type }, + }; + }, + }; +} +``` + +**Step 3: Update social card to show agent badge** + +```typescript +// In social-card.tsx, add agent badge display +{issue.agentTypeId && ( +
+ {issue.agentTypeId} +
+)} +``` + +**Step 4: Commit** + +```bash +git add src/lib/types.ts src/tui/tools/bb-assign-agent.ts src/components/social/ +git commit -m "feat: add agent assignment to beads" +``` + +--- + +## Orchestrator Decision Logic + +### Task 12: Implement Orchestrator Decision Tree + +**Files:** +- Modify: `src/tui/system-prompt.ts` - add decision tree to prompt +- Modify: `src/tui/tools/bb-spawn-worker.ts` - add scope analysis hint + +**Step 1: Add decision tree to system prompt** + +```typescript +// In system-prompt.ts, add this section: + +## Task Scope Decision Tree + +Before spawning agents, assess task scope: + +### Small Task (Single Agent) +- Bug fix with known cause +- Single file change +- Quick refactor +- Single test addition + +**Action:** Spawn 1 agent directly. Example: `bb_spawn_worker` with `archetype: "engineer"` + +### Medium Task (2-3 Agents) +- Feature with clear design +- Bug investigation + fix +- Code review + fixes + +**Action:** Spawn 2-3 agents based on template or custom composition. + +### Large Task (Use Template) +- New feature from scratch +- System redesign +- Multi-component changes + +**Action:** Use `bb_spawn_template` with appropriate template: +- `feature-dev` for new features +- `bug-fix` for debugging issues +- `greenfield` for new projects +- `full-squad` for complex multi-domain work + +### Epic Creation +If the task requires multiple beads with dependencies: +1. Use `bb_create_epic` to create the epic +2. Use `bb_create` to create child beads +3. Assign agent types to each bead with `bb_assign_agent` +4. Spawn agents to work on unblocked beads + +**Example Flow:** +``` +User: "Build user authentication system" + +Orchestrator: +1. "This is a large task. Creating epic 'User Authentication'." +2. Creates epic + decomposes into beads: + - BEAD-001: Design auth schema [architect] + - BEAD-002: Implement JWT service [engineer] + - BEAD-003: Implement refresh tokens [engineer] + - BEAD-004: Write auth tests [tester] + - BEAD-005: Review implementation [reviewer] +3. Spawns Architect 01 to start on BEAD-001 +4. When BEAD-001 done, spawns Engineer 01 and 02 for BEAD-002 and BEAD-003 +5. And so on... +``` +``` + +**Step 2: Commit** + +```bash +git add src/tui/system-prompt.ts +git commit -m "feat: add orchestrator decision tree for agent spawning" +``` + +--- + +## Right Panel Integration + +### Task 13: Wire Agent Panel into Right Panel + +**Files:** +- Modify: `src/components/shared/right-panel.tsx` - add agents tab +- Modify: `src/components/activity/contextual-right-panel.tsx` - include agent status +- Modify: `src/hooks/use-url-state.ts` - add agents tab state + +**Step 1: Add agents tab to right panel** + +```typescript +// In right-panel.tsx, add tab system +const TABS = ['details', 'agents', 'activity'] as const; +type RightPanelTab = typeof TABS[number]; + +export function RightPanel({ issues, selectedTask, ... }: RightPanelProps) { + const [activeTab, setActiveTab] = useState('details'); + + return ( +
+ {/* Tab bar */} +
+ {TABS.map(tab => ( + + ))} +
+ + {/* Tab content */} +
+ {activeTab === 'details' && } + {activeTab === 'agents' && } + {activeTab === 'activity' && } +
+
+ ); +} +``` + +**Step 2: Update ContextualRightPanel** + +```typescript +// Include AgentStatusPanel when showing swarm/epic context +{contextType === 'swarm' && ( +
+ + {/* ... other swarm context */} +
+)} +``` + +**Step 3: Commit** + +```bash +git add src/components/shared/right-panel.tsx src/components/activity/contextual-right-panel.tsx +git commit -m "feat: wire agent status panel into right panel tabs" +``` + +--- + +## Agent State Persistence + +### Task 14: Persist Agent Instances to Disk + +**Files:** +- Create: `src/lib/agent-persistence.ts` +- Modify: `src/lib/worker-session-manager.ts` - load/save on changes +- Create: `src/app/api/runtime/agents/history/route.ts` + +**Step 1: Create persistence layer** + +```typescript +// src/lib/agent-persistence.ts +import fs from 'fs/promises'; +import path from 'path'; +import type { AgentInstance } from './agent-instance'; + +const AGENTS_FILE = (projectRoot: string) => path.join(projectRoot, '.beads', 'agents.jsonl'); + +export async function loadAgentInstances(projectRoot: string): Promise { + try { + const content = await fs.readFile(AGENTS_FILE(projectRoot), 'utf-8'); + return content.trim().split('\n').filter(Boolean).map(line => JSON.parse(line)); + } catch { + return []; + } +} + +export async function saveAgentInstance(projectRoot: string, instance: AgentInstance): Promise { + const line = JSON.stringify(instance) + '\n'; + await fs.appendFile(AGENTS_FILE(projectRoot), line, 'utf-8'); +} + +export async function updateAgentInstance(projectRoot: string, instance: AgentInstance): Promise { + // Load all, update the matching one, rewrite + const instances = await loadAgentInstances(projectRoot); + const idx = instances.findIndex(i => i.id === instance.id); + if (idx >= 0) { + instances[idx] = instance; + await fs.writeFile( + AGENTS_FILE(projectRoot), + instances.map(i => JSON.stringify(i)).join('\n') + '\n', + 'utf-8' + ); + } +} + +export async function getActiveInstances(projectRoot: string): Promise { + const all = await loadAgentInstances(projectRoot); + return all.filter(i => i.status === 'working' || i.status === 'spawning' || i.status === 'idle'); +} + +export async function getRecentInstances(projectRoot: string, limit = 20): Promise { + const all = await loadAgentInstances(projectRoot); + return all + .filter(i => i.status === 'completed' || i.status === 'failed') + .sort((a, b) => new Date(b.completedAt || 0).getTime() - new Date(a.completedAt || 0).getTime()) + .slice(0, limit); +} +``` + +**Step 2: Integrate with worker-session-manager** + +```typescript +// In worker-session-manager.ts +import { saveAgentInstance, updateAgentInstance } from './agent-persistence'; + +// After spawning worker: +await saveAgentInstance(projectRoot, { + id: worker.agentInstanceId, + agentTypeId: worker.agentTypeId, + displayName: worker.displayName, + status: worker.status, + startedAt: worker.createdAt, +}); + +// On status change: +await updateAgentInstance(projectRoot, instance); +``` + +**Step 3: Create history API endpoint** + +```typescript +// src/app/api/runtime/agents/history/route.ts +import { NextResponse } from 'next/server'; +import { getRecentInstances } from '../../../../../lib/agent-persistence'; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const projectRoot = searchParams.get('projectRoot'); + + if (!projectRoot) { + return NextResponse.json({ ok: false, error: 'projectRoot required' }); + } + + const recent = await getRecentInstances(projectRoot, 50); + return NextResponse.json({ ok: true, instances: recent }); +} +``` + +**Step 4: Commit** + +```bash +git add src/lib/agent-persistence.ts src/lib/worker-session-manager.ts src/app/api/runtime/agents/history/ +git commit -m "feat: persist agent instances to disk" +``` + +--- + +## Agent History + +### Task 15: Show Completed Agents in Panel + +**Files:** +- Modify: `src/components/agents/agent-status-panel.tsx` - add history section +- Create: `src/components/agents/agent-history-list.tsx` + +**Step 1: Add history section to AgentStatusPanel** + +```typescript +// In agent-status-panel.tsx, add after active agents: + +const [history, setHistory] = useState([]); + +// Load history +useEffect(() => { + const loadHistory = async () => { + const res = await fetch(`/api/runtime/agents/history?projectRoot=${encodeURIComponent(projectRoot)}`); + const data = await res.json(); + if (data.ok) setHistory(data.instances); + }; + loadHistory(); + const interval = setInterval(loadHistory, 10000); + return () => clearInterval(interval); +}, [projectRoot]); + +// In render: +
+

+ Recent Completions ({history.length}) +

+
+ {history.slice(0, 10).map(instance => ( +
+ + {instance.status === 'completed' ? '✓' : '✗'} + + {instance.displayName} + {instance.currentBeadId && ( + → {instance.currentBeadId} + )} +
+ ))} +
+
+``` + +**Step 2: Commit** + +```bash +git add src/components/agents/ +git commit -m "feat: show completed agent history in status panel" +``` + +--- + +## Updated Files Summary + +| File | Action | +|------|--------| +| **Rename (Tasks 1-4)** || +| `src/lib/types-swarm.ts` | Modify: rename AgentArchetype → AgentType | +| `src/lib/server/beads-fs.ts` | Modify: rename getArchetypes → getAgentTypes | +| `src/tui/tools/bb-list-agents.ts` | Create (rename from bb-list-archetypes) | +| `src/tui/tools/bb-create-agent.ts` | Create (rename) | +| `src/tui/tools/bb-update-agent.ts` | Create (rename) | +| `src/tui/tools/bb-delete-agent.ts` | Create (rename) | +| `src/tui/tools/bb-list-archetypes.ts` | Delete | +| `src/tui/tools/bb-create-archetype.ts` | Delete | +| `src/tui/tools/bb-update-archetype.ts` | Delete | +| `src/tui/tools/bb-delete-archetype.ts` | Delete | +| `src/lib/pi-daemon-adapter.ts` | Modify: update imports | +| `src/components/swarm/agent-inspector.tsx` | Create (rename) | +| `src/components/swarm/archetype-inspector.tsx` | Delete | +| **Agent Instances (Tasks 5-7)** || +| `src/lib/agent-instance.ts` | Create | +| `src/lib/worker-session-manager.ts` | Modify: add instance tracking | +| `src/components/agents/agent-status-panel.tsx` | Create | +| `src/app/api/runtime/agents/route.ts` | Create | +| **Orchestrator (Tasks 8-9)** || +| `src/tui/system-prompt.ts` | Modify: update prompt + decision tree | +| `src/tui/tools/bb-spawn-template.ts` | Create | +| `src/tui/tools/bb-spawn-worker.ts` | Modify: update descriptions | +| **Tests (Task 10)** || +| `tests/integration/agent-spawning.test.ts` | Create | +| **Agent-Bead (Task 11)** || +| `src/lib/types.ts` | Modify: add agentTypeId to BeadIssue | +| `src/tui/tools/bb-assign-agent.ts` | Create | +| `src/components/social/social-card.tsx` | Modify: show agent badge | +| **Decision Tree (Task 12)** || +| (included in Task 8) | | +| **Right Panel (Task 13)** || +| `src/components/shared/right-panel.tsx` | Modify: add agents tab | +| `src/components/activity/contextual-right-panel.tsx` | Modify: include agents | +| **Persistence (Task 14)** || +| `src/lib/agent-persistence.ts` | Create | +| `src/app/api/runtime/agents/history/route.ts` | Create | +| **History (Task 15)** || +| `src/components/agents/agent-history-list.tsx` | Create | + +**Total: 26 files (14 create, 10 modify, 5 delete)** + +--- + +## Estimated Effort + +**10-12 hours** + +| Phase | Tasks | Effort | +|-------|-------|--------| +| Rename | 1-4 | 2h | +| Agent Instances | 5-7 | 2h | +| Orchestrator | 8-9 | 1.5h | +| Tests | 10 | 1h | +| Agent-Bead | 11 | 1.5h | +| Decision Tree | 12 | 0.5h | +| Right Panel | 13 | 1h | +| Persistence | 14 | 1.5h | +| History | 15 | 1h | + +--- + +## Success Criteria + +- [ ] "Archetype" renamed to "Agent" in all user-facing text +- [ ] Agent tools work: list, create, update, delete +- [ ] Spawning a worker creates numbered instance (Engineer 01, etc.) +- [ ] Right panel has Agents tab showing active + history +- [ ] Template spawning spawns correct number of agents +- [ ] Orchestrator uses decision tree for scope assessment +- [ ] Beads can have agentTypeId assigned +- [ ] Agent instances persist across restarts +- [ ] History shows recent completions +- [ ] Tests pass + +--- + +## Plan Complete + +**Saved to:** `docs/plans/2026-03-07-phase-3-agent-orchestration.md` + +**Two execution options:** + +**1. Subagent-Driven (this session)** - I dispatch fresh subagent per task, review between tasks, fast iteration + +**2. Parallel Session (separate)** - Open new session with executing-plans, batch execution with checkpoints + +**Which approach?** diff --git a/docs/plans/2026-03-07-phase-4-launch-anywhere.md b/docs/plans/2026-03-07-phase-4-launch-anywhere.md new file mode 100644 index 0000000..69971b0 --- /dev/null +++ b/docs/plans/2026-03-07-phase-4-launch-anywhere.md @@ -0,0 +1,137 @@ +# Phase 4 - Multi-Surface Launch-Anywhere UX + +**Status:** Planning +**Created:** 2026-03-07 +**Goal:** Add spawn affordances to all UI surfaces so users can launch agents from anywhere + +--- + +## Current Surfaces + +| Surface | Location | Purpose | +|---------|----------|---------| +| Social cards | `src/components/social/` | Show beads with status, labels, assignments | +| Graph nodes | `src/components/graph/` | Show beads in dependency graph | +| Right panel | `src/components/shared/right-panel.tsx` | Contextual details when bead selected | +| Agent status panel | `src/components/agents/agent-status-panel.tsx` | Show active agents (already has spawn in orchestrator) | + +--- + +## Discovery Tasks + +### Task 1: Analyze Social Card Components +- Find all social card components +- Identify current actions/buttons +- Determine where spawn button fits +- Document available context (beadId, status, assignee, labels) + +### Task 2: Analyze Graph Node Components +- Find all graph node components +- Identify current interactions +- Determine where spawn button fits +- Document available context + +### Task 3: Analyze Right Panel +- Find contextual details components +- Identify where spawn makes sense +- Document available context + +--- + +## Implementation Plan + +### Step 1: Create Spawn Button Component +Create reusable spawn button that: +- Accepts beadId and context +- Shows agent type selector dropdown +- Calls orchestrator to spawn agent +- Shows loading state +- Links to Agent Status panel + +**File:** `src/components/shared/spawn-agent-button.tsx` + +### Step 2: Add to Social Cards +- Add spawn button to social card +- Pass beadId from card data +- Show when status is "open" or "in_progress" with no assignee + +**Files:** `src/components/social/social-card.tsx` + +### Step 3: Add to Graph Nodes +- Add spawn button to graph node context menu or hover +- Pass beadId from node data +- Show for unassigned beads + +**Files:** `src/components/graph/graph-view.tsx`, node components + +### Step 4: Add to Right Panel +- Add spawn section when viewing bead details +- Show spawn options for task/epic beads + +**Files:** `src/components/shared/right-panel.tsx` + +### Step 5: Wire Spawn Actions to Orchestrator +- Create API endpoint or event for spawn requests from UI +- Orchestrator receives spawn request and executes +- Update agent status panel + +**Files:** `src/app/api/runtime/spawn/route.ts` or extend existing + +--- + +## UI/UX Considerations + +### Spawn Button Placement +- **Social card:** Bottom of card, next to status badge +- **Graph node:** Context menu on right-click, or hover tooltip +- **Right panel:** New section above details, when bead is unassigned + +### Spawn Flow +1. User clicks "Spawn Agent" button +2. Dropdown shows agent types (Engineer, Reviewer, etc.) +3. User selects type or "Auto" (let orchestrator decide) +4. Button shows spawning state +5. Agent appears in status panel +6. Button changes to "View Agent" link + +### Context Packaging +When spawning from a surface, package: +- beadId +- bead title/description +- bead status +- current assignee (if any) +- relevant labels + +--- + +## Blocked Items + +None identified yet. + +--- + +## Success Criteria + +- [ ] Spawn button appears on social cards for unassigned beads +- [ ] Spawn option available on graph nodes +- [ ] Right panel shows spawn section for task details +- [ ] Spawning from any surface creates bead and agent +- [ ] Agent status panel updates with new agent +- [ ] User can continue chatting with orchestrator while agent works + +--- + +## Estimated Effort + +4-6 hours + +--- + +## Next Steps + +1. ✅ Create plan document +2. 🔲 Run discovery on social card components +3. 🔲 Run discovery on graph node components +4. 🔲 Run discovery on right panel components +5. 🔲 Create spawn button component +6. 🔲 Integrate into surfaces diff --git a/docs/plans/2026-03-07-phase-5-agent-presence.md b/docs/plans/2026-03-07-phase-5-agent-presence.md new file mode 100644 index 0000000..0339872 --- /dev/null +++ b/docs/plans/2026-03-07-phase-5-agent-presence.md @@ -0,0 +1,76 @@ +# Phase 5 - Agent Presence in Social/Graph Views + +**Status:** Planning +**Created:** 2026-03-07 +**Goal:** Show active agents in social cards, graph nodes, and make agent activity visible across the UI + +--- + +## Current State + +- Agent status panel exists in right panel (for epic/swarm context) +- Agents have display names (Engineer 01, etc.) +- Agent instances tracked in `.beads/agents.jsonl` +- No visibility in main social/graph views + +--- + +## Implementation Plan + +### Step 1: Agent Badge on Social Cards +When a bead has an active agent assigned, show: +- Agent icon/avatar +- Display name (Engineer 01) +- Status indicator (working, blocked, etc.) + +**Files:** +- `src/components/social/social-card.tsx` - add agent badge +- `src/lib/types.ts` - ensure agentInstanceId on BeadIssue + +### Step 2: Agent Indicator on Graph Nodes +Show agent presence on graph nodes: +- Small icon when agent assigned +- Tooltip shows agent name and status +- Color coding by status + +**Files:** +- `src/components/graph/graph-view.tsx` +- Node rendering logic + +### Step 3: Agent Activity in Activity Panel +If activity panel exists, show agent events: +- "Engineer 01 started working on BEAD-001" +- "Reviewer 01 completed review of BEAD-005" + +**Files:** +- Check if activity panel still exists or needs rebuilding + +### Step 4: Agent Filter/View +Add filter option to show only beads with active agents: +- "Show my agents" filter in social view +- Highlight beads with active work + +**Files:** +- `src/components/social/social-page.tsx` +- Filter controls + +--- + +## Blocked Items + +None identified. + +--- + +## Success Criteria + +- [ ] Social cards show agent badge when agent assigned +- [ ] Graph nodes show agent indicator +- [ ] Agent status visible at a glance +- [ ] Can filter by "has active agent" + +--- + +## Estimated Effort + +2-3 hours diff --git a/docs/plans/2026-03-07-phase-6-runtime-hardening.md b/docs/plans/2026-03-07-phase-6-runtime-hardening.md new file mode 100644 index 0000000..f8a4bfa --- /dev/null +++ b/docs/plans/2026-03-07-phase-6-runtime-hardening.md @@ -0,0 +1,75 @@ +# Phase 6 - Runtime Hardening + +**Status:** Planning +**Created:** 2026-03-07 +**Goal:** Improve robustness of embedded Pi runtime, reconnect behavior, and error recovery + +--- + +## Current Issues + +1. Session disconnect requires manual restart +2. Stuck/hung agents have no clear diagnostics +3. Drift between TUI Pi loader and embedded Pi loader +4. No automatic recovery from failures + +--- + +## Implementation Plan + +### Step 1: Session Health Monitoring +- Add heartbeat check for orchestrator session +- Detect when session is unresponsive +- Show clear status in UI + +**Files:** +- `src/lib/pi-daemon-adapter.ts` - health check +- `src/lib/embedded-daemon.ts` - monitoring + +### Step 2: Automatic Reconnect +- On session disconnect, attempt reconnect +- Preserve conversation history +- Show reconnect status to user + +**Files:** +- `src/lib/pi-daemon-adapter.ts` +- `src/components/shared/left-panel.tsx` - reconnect UI + +### Step 3: Stuck Agent Diagnostics +- Detect agents stuck in "spawning" for too long +- Provide diagnostic information +- Allow user to cancel/retry + +**Files:** +- `src/lib/worker-session-manager.ts` +- `src/components/agents/agent-status-panel.tsx` + +### Step 4: Error Recovery UX +- Clear error messages when things fail +- Retry buttons for failed operations +- Logs for debugging + +**Files:** +- Error handling across runtime components +- UI for error display + +--- + +## Blocked Items + +None identified. + +--- + +## Success Criteria + +- [ ] Session health monitored and shown in UI +- [ ] Automatic reconnect on disconnect +- [ ] Stuck agents detected and reported +- [ ] Clear error messages with recovery options + +--- + +## Estimated Effort + +3-4 hours diff --git a/docs/plans/2026-03-07-phase-7-tests.md b/docs/plans/2026-03-07-phase-7-tests.md new file mode 100644 index 0000000..a9f1d16 --- /dev/null +++ b/docs/plans/2026-03-07-phase-7-tests.md @@ -0,0 +1,95 @@ +# Phase 7 - Tests and Verification + +**Status:** Planning +**Created:** 2026-03-07 +**Goal:** Add comprehensive tests for embedded Pi runtime and agent system + +--- + +## Test Categories + +### 1. Unit Tests + +**Runtime Path Resolution:** +- `pi-runtime-detection.test.ts` - SDK detection, path resolution +- `bb-pi-bootstrap.test.ts` - bootstrap process + +**Event Projection:** +- `embedded-daemon.test.ts` - event emission, deduplication +- `orchestrator-chat.test.ts` - message projection + +**Worker Session:** +- `worker-session-manager.test.ts` - spawn, status, lifecycle + +### 2. Contract Tests + +**Adapter Schemas:** +- Pi SDK adapter input/output contracts +- Runtime event schema validation + +### 3. Integration Tests + +**Orchestrator Flow:** +- Session creation +- Prompt submission +- Tool execution +- Response handling + +**Worker Flow:** +- Worker spawn +- Bead claim/update/close +- Result retrieval + +### 4. UI Tests + +**Left Panel:** +- Chat rendering +- Message projection +- Error display + +--- + +## Implementation Plan + +### Step 1: Unit Test Setup +- Configure test runner (Jest or Vitest) +- Create test utilities for mocking Pi SDK + +**Files:** +- `tests/setup.ts` +- `tests/mocks/pi-sdk.ts` + +### Step 2: Runtime Unit Tests +- Test path detection +- Test bootstrap process +- Test event deduplication + +### Step 3: Worker Unit Tests +- Test spawn flow +- Test status tracking +- Test result collection + +### Step 4: Integration Tests +- Test full orchestrator flow +- Test worker-to-bead workflow + +--- + +## Blocked Items + +None identified. + +--- + +## Success Criteria + +- [ ] Unit tests for runtime components pass +- [ ] Unit tests for worker session manager pass +- [ ] Integration tests for orchestrator flow pass +- [ ] Integration tests for worker flow pass + +--- + +## Estimated Effort + +4-5 hours diff --git a/docs/plans/2026-03-08-phase-4-handoff.md b/docs/plans/2026-03-08-phase-4-handoff.md new file mode 100644 index 0000000..0d65740 --- /dev/null +++ b/docs/plans/2026-03-08-phase-4-handoff.md @@ -0,0 +1,159 @@ +# Phase 4 Handoff Summary + +**Branch:** `docs/embedded-pi-prd` +**PR:** https://github.com/zenchantlive/beadboard/pull/new/docs/embedded-pi-prd + +--- + +## Current Status + +### Phase 4: Launch-Anywhere UX - **IN PROGRESS** + +**Completed by subagents (9 of 17 tasks):** +| Task | Description | Status | +|------|-------------|--------| +| 1 | useAgentStatus hook | ✅ Done | +| 2 | useSpawnAgent hook | ✅ Done | +| 3 | Hooks index | ✅ Done | +| 4 | AgentPickerPopup | ✅ Done | +| 5 | AgentAssignButton | ✅ Done | +| 6 | AgentSpawnButton | ✅ Done | +| 7 | AgentActionRow | ✅ Done | +| 8 | Agents index | ✅ Done | +| 9 | SocialCard integration | ✅ Done | +| 10 | GraphNodeCard integration | ⏳ NOT DONE | +| 11 | BlockedTriageModal integration | ⏳ NOT DONE | +| 12 | Spawn API endpoint | ✅ Done | +| 13 | Assign-agent API | ✅ Done | +| 14 | Update useAgentStatus (real data) | ⏳ NOT DONE | +| 15 | Worker status API | ✅ Done | +| 16 | BeadIssue type update | ✅ Done | +| 17 | Testing | ⏳ NOT DONE | + +### Remaining Tasks + +**Task 10: Add AgentActionRow to GraphNodeCard** +- File: `src/components/graph/graph-node-card.tsx` +- Add `import { AgentActionRow } from '../agents';` +- Add `projectRoot` prop +- Replace old rocket with `` + +**Task 11: Add AgentActionRow to BlockedTriageModal** +- File: `src/components/shared/blocked-triage-modal.tsx` +- Add import and component + +**Task 14: Update useAgentStatus to fetch real data** +- File: `src/components/agents/hooks/use-agent-status.ts` +- Poll `/api/runtime/worker-status?beadId=X` every 5 seconds + +**Task 17: Test the complete flow** + +--- + +## Files Created (Phase 4) + +``` +src/components/agents/ +├── agent-action-row.tsx # Combined assign + spawn +├── agent-assign-button.tsx # 👤 Icon + picker +├── agent-spawn-button.tsx # 🚀 Icon with colors +├── agent-picker-popup.tsx # Agent selection dropdown +├── index.ts # Exports +└── hooks/ + ├── use-agent-status.ts # Worker status tracking + ├── use-spawn-agent.ts # Spawn API calls + └── index.ts + +src/app/api/runtime/ +├── spawn/route.ts # POST to spawn worker +└── worker-status/route.ts # GET worker status by beadId + +src/app/api/beads/ +└── assign-agent/route.ts # POST to assign agent type +``` + +## Files Modified (Phase 4) + +``` +src/components/social/social-card.tsx # Integrated AgentActionRow +src/components/social/social-page.tsx # Pass projectRoot +src/lib/social-cards.ts # Added agentTypeId +src/lib/types.ts # Added agentTypeId/agentInstanceId +``` + +--- + +## How the Two-Icon System Works + +**Icon 1: 👤 Assign (UserPlus)** +- Opens agent picker popup +- Select agent type → assigns to bead +- Planning action (no spawn) + +**Icon 2: 🚀 Spawn (Rocket)** +- **Blue** = Ready to spawn (has agentTypeId) +- **Green pulsing** = Worker active +- **Red** = Worker blocked +- **Checkmark** = Worker completed +- Click spawns the worker + +--- + +## Rocket Colors + +| Color | State | Meaning | +|-------|-------|---------| +| Hidden | No agentTypeId | No agent assigned | +| Blue | idle + agentTypeId | Ready to spawn | +| Blue pulsing | spawning | Spawning in progress | +| Green pulsing | working | Worker actively working | +| Red | blocked | Worker stuck | +| Green checkmark | completed | Worker finished | + +--- + +## Next Session Tasks + +1. **Task 10:** Add AgentActionRow to graph-node-card.tsx +2. **Task 11:** Add AgentActionRow to blocked-triage-modal.tsx +3. **Task 14:** Make useAgentStatus poll real API +4. **Task 17:** Test complete flow + +--- + +## Key Files to Read + +| File | Purpose | +|------|---------| +| `docs/plans/2026-03-08-phase-4-implementation.md` | Full implementation plan | +| `docs/plans/2026-03-05-embedded-pi-roadmap.md` | Overall roadmap | +| `src/components/agents/agent-action-row.tsx` | Main component | +| `src/components/agents/hooks/use-agent-status.ts` | Status tracking | +| `src/app/api/runtime/spawn/route.ts` | Spawn API | + +--- + +## Pre-existing TypeScript Errors (Not Related to Phase 4) + +``` +src/components/shared/left-panel-new.tsx - LeftPanelFilters not exported +src/components/shared/unified-shell.tsx - Type mismatches +src/tui/tools/bb-deviation.ts - RuntimeEventKind mismatch +``` + +These existed before Phase 4 work and should be fixed separately. + +--- + +## Commit History (Phase 4) + +``` +4a7425c feat: integrate AgentActionRow into SocialCard +547b565 feat: add agents components index +209807e feat: add AgentSpawnButton with color states +aedeb07 feat: add spawn API endpoint +20a3eb1 feat: add worker-status API endpoint +09928a8 feat: add useSpawnAgent hook +4e03654 feat: add useAgentStatus hook interface +d84770c docs: add Phase 4 implementation plan +``` diff --git a/docs/plans/2026-03-08-phase-4-implementation.md b/docs/plans/2026-03-08-phase-4-implementation.md new file mode 100644 index 0000000..e21ee60 --- /dev/null +++ b/docs/plans/2026-03-08-phase-4-implementation.md @@ -0,0 +1,1092 @@ +# Phase 4: Launch-Anywhere UX Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Add spawn affordances to all UI surfaces with a reusable two-icon system (assign + spawn) that works on social cards, graph nodes, and blocked triage modal. + +**Architecture:** Create reusable components (`AgentActionRow`, `AgentAssignButton`, `AgentSpawnButton`) and hooks (`useAgentStatus`, `useSpawnAgent`) in `src/components/agents/`. Each surface imports `AgentActionRow` which handles the full assign → spawn flow. Icon colors reflect agent/worker status (blue=ready, green=working, red=blocked). + +**Tech Stack:** React, TypeScript, Lucide icons, existing agent types from `types-swarm.ts` + +--- + +## Prerequisites + +- Phase 3 complete (agents, workers, beads work) +- `bb_spawn_worker` tool exists and works +- Worker session manager tracks status + +--- + +## Task 1: Create useAgentStatus Hook + +**Files:** +- Create: `src/components/agents/hooks/use-agent-status.ts` + +**Step 1: Create hook file with interface** + +```typescript +// src/components/agents/hooks/use-agent-status.ts +import { useState, useEffect } from 'react'; + +export type WorkerStatus = 'idle' | 'spawning' | 'working' | 'blocked' | 'completed' | 'failed'; + +export interface AgentStatus { + agentTypeId?: string; + workerStatus: WorkerStatus; + workerDisplayName?: string; + isLoading: boolean; +} + +export function useAgentStatus(beadId: string): AgentStatus { + const [status, setStatus] = useState({ + workerStatus: 'idle', + isLoading: true, + }); + + useEffect(() => { + // TODO: Fetch from agent status API + setStatus({ workerStatus: 'idle', isLoading: false }); + }, [beadId]); + + return status; +} +``` + +**Step 2: Commit** + +```bash +git add src/components/agents/hooks/use-agent-status.ts +git commit -m "feat: add useAgentStatus hook interface" +``` + +--- + +## Task 2: Create useSpawnAgent Hook + +**Files:** +- Create: `src/components/agents/hooks/use-spawn-agent.ts` + +**Step 1: Create spawn hook** + +```typescript +// src/components/agents/hooks/use-spawn-agent.ts +import { useState } from 'react'; + +export interface SpawnResult { + success: boolean; + workerId?: string; + displayName?: string; + error?: string; +} + +export function useSpawnAgent(projectRoot: string) { + const [isSpawning, setIsSpawning] = useState(false); + + const spawn = async (beadId: string, agentTypeId: string): Promise => { + setIsSpawning(true); + try { + const response = await fetch('/api/runtime/spawn', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ projectRoot, beadId, agentTypeId }), + }); + const data = await response.json(); + + if (!data.ok) { + return { success: false, error: data.error }; + } + + return { + success: true, + workerId: data.workerId, + displayName: data.displayName, + }; + } catch (error) { + return { success: false, error: String(error) }; + } finally { + setIsSpawning(false); + } + }; + + return { spawn, isSpawning }; +} +``` + +**Step 2: Commit** + +```bash +git add src/components/agents/hooks/use-spawn-agent.ts +git commit -m "feat: add useSpawnAgent hook" +``` + +--- + +## Task 3: Create hooks index + +**Files:** +- Create: `src/components/agents/hooks/index.ts` + +**Step 1: Create index file** + +```typescript +// src/components/agents/hooks/index.ts +export { useAgentStatus, type AgentStatus, type WorkerStatus } from './use-agent-status'; +export { useSpawnAgent, type SpawnResult } from './use-spawn-agent'; +``` + +**Step 2: Commit** + +```bash +git add src/components/agents/hooks/index.ts +git commit -m "feat: add agents hooks index" +``` + +--- + +## Task 4: Create AgentPickerPopup Component + +**Files:** +- Create: `src/components/agents/agent-picker-popup.tsx` + +**Step 1: Create picker popup** + +```typescript +// src/components/agents/agent-picker-popup.tsx +'use client'; + +import { useEffect, useRef } from 'react'; +import { Rocket, Brain, Wrench, Search, CheckCircle, FlaskConical, Upload } from 'lucide-react'; +import type { AgentArchetype } from '../../lib/types-swarm'; + +export interface AgentPickerPopupProps { + isOpen: boolean; + onClose: () => void; + agents: AgentArchetype[]; + selectedAgentId?: string; + onSelect: (agentId: string) => void; + onSpawn?: (agentId: string) => void; + position?: { x: number; y: number }; +} + +const AGENT_ICONS: Record = { + architect: , + engineer: , + investigator: , + reviewer: , + tester: , + shipper: , +}; + +export function AgentPickerPopup({ + isOpen, + onClose, + agents, + selectedAgentId, + onSelect, + onSpawn, + position, +}: AgentPickerPopupProps) { + const ref = useRef(null); + + useEffect(() => { + const handleClickOutside = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + onClose(); + } + }; + if (isOpen) { + document.addEventListener('mousedown', handleClickOutside); + } + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [isOpen, onClose]); + + if (!isOpen) return null; + + const style = position + ? { position: 'absolute' as const, left: position.x, top: position.y + 8 } + : {}; + + return ( +
+ {/* Orchestrator option */} + + +
+ + {/* Agent types */} + {agents.map((agent) => ( + + ))} + + {/* Spawn button */} + {onSpawn && selectedAgentId && ( + <> +
+ + + )} +
+ ); +} +``` + +**Step 2: Commit** + +```bash +git add src/components/agents/agent-picker-popup.tsx +git commit -m "feat: add AgentPickerPopup component" +``` + +--- + +## Task 5: Create AgentAssignButton Component + +**Files:** +- Create: `src/components/agents/agent-assign-button.tsx` + +**Step 1: Create assign button** + +```typescript +// src/components/agents/agent-assign-button.tsx +'use client'; + +import { useState } from 'react'; +import { UserPlus } from 'lucide-react'; +import { AgentPickerPopup } from './agent-picker-popup'; +import type { AgentArchetype } from '../../lib/types-swarm'; + +export interface AgentAssignButtonProps { + beadId: string; + agents: AgentArchetype[]; + currentAgentTypeId?: string; + onAssign: (agentTypeId: string) => void; + size?: 'sm' | 'md'; + disabled?: boolean; +} + +export function AgentAssignButton({ + beadId, + agents, + currentAgentTypeId, + onAssign, + size = 'sm', + disabled = false, +}: AgentAssignButtonProps) { + const [isOpen, setIsOpen] = useState(false); + + const sizeClasses = size === 'sm' + ? 'h-6 w-6' + : 'h-7 w-7'; + + const iconSize = size === 'sm' + ? 'w-3 h-3' + : 'w-3.5 h-3.5'; + + const isAssigned = !!currentAgentTypeId; + const assignedAgent = agents.find(a => a.id === currentAgentTypeId); + const bgColor = isAssigned && assignedAgent + ? `${assignedAgent.color}30` + : 'var(--surface-tertiary)'; + const iconColor = isAssigned && assignedAgent + ? assignedAgent.color + : 'var(--text-tertiary)'; + + return ( +
+ + + setIsOpen(false)} + agents={agents} + selectedAgentId={currentAgentTypeId} + onSelect={(agentId) => { + onAssign(agentId); + setIsOpen(false); + }} + /> +
+ ); +} +``` + +**Step 2: Commit** + +```bash +git add src/components/agents/agent-assign-button.tsx +git commit -m "feat: add AgentAssignButton component" +``` + +--- + +## Task 6: Create AgentSpawnButton Component + +**Files:** +- Create: `src/components/agents/agent-spawn-button.tsx` + +**Step 1: Create spawn button with color states** + +```typescript +// src/components/agents/agent-spawn-button.tsx +'use client'; + +import { Rocket, CheckCircle, AlertCircle, Loader2 } from 'lucide-react'; +import type { WorkerStatus } from './hooks/use-agent-status'; + +export interface AgentSpawnButtonProps { + beadId: string; + agentTypeId?: string; + workerStatus: WorkerStatus; + workerDisplayName?: string; + workerError?: string; + onSpawn: () => void; + size?: 'sm' | 'md'; + disabled?: boolean; +} + +const STATUS_CONFIG: Record = { + idle: { + icon: , + color: '#6b7280', + bgColor: 'rgba(107, 114, 128, 0.1)', + borderColor: 'rgba(107, 114, 128, 0.3)', + title: 'No agent assigned', + }, + spawning: { + icon: , + color: '#3b82f6', + bgColor: 'rgba(59, 130, 246, 0.1)', + borderColor: 'rgba(59, 130, 246, 0.3)', + title: 'Spawning...', + pulsing: true, + }, + working: { + icon: , + color: '#22c55e', + bgColor: 'rgba(34, 197, 94, 0.1)', + borderColor: 'rgba(34, 197, 94, 0.3)', + title: 'Working', + pulsing: true, + }, + blocked: { + icon: , + color: '#ef4444', + bgColor: 'rgba(239, 68, 68, 0.1)', + borderColor: 'rgba(239, 68, 68, 0.3)', + title: 'Blocked', + }, + completed: { + icon: , + color: '#22c55e', + bgColor: 'rgba(34, 197, 94, 0.1)', + borderColor: 'rgba(34, 197, 94, 0.3)', + title: 'Completed', + }, + failed: { + icon: , + color: '#ef4444', + bgColor: 'rgba(239, 68, 68, 0.1)', + borderColor: 'rgba(239, 68, 68, 0.3)', + title: 'Failed', + }, +}; + +export function AgentSpawnButton({ + beadId, + agentTypeId, + workerStatus, + workerDisplayName, + workerError, + onSpawn, + size = 'sm', + disabled = false, +}: AgentSpawnButtonProps) { + const config = STATUS_CONFIG[workerStatus]; + const sizeClasses = size === 'sm' ? 'h-6 w-6' : 'h-7 w-7'; + + // No agent assigned - don't show button + if (!agentTypeId && workerStatus === 'idle') { + return null; + } + + const canSpawn = workerStatus === 'idle' && agentTypeId; + const showTooltip = workerStatus === 'working' || workerStatus === 'blocked' || workerStatus === 'completed'; + + return ( +
+ + + {/* Tooltip for active workers */} + {showTooltip && ( +
+
+

+ {workerDisplayName || 'Agent'} +

+

+ {workerStatus} +

+ {workerError && ( +

+ {workerError} +

+ )} +
+
+ )} +
+ ); +} +``` + +**Step 2: Commit** + +```bash +git add src/components/agents/agent-spawn-button.tsx +git commit -m "feat: add AgentSpawnButton with color states" +``` + +--- + +## Task 7: Create AgentActionRow Component + +**Files:** +- Create: `src/components/agents/agent-action-row.tsx` + +**Step 1: Create combined action row** + +```typescript +// src/components/agents/agent-action-row.tsx +'use client'; + +import { AgentAssignButton } from './agent-assign-button'; +import { AgentSpawnButton } from './agent-spawn-button'; +import { useAgentStatus, useSpawnAgent } from './hooks'; +import type { AgentArchetype } from '../../lib/types-swarm'; + +export interface AgentActionRowProps { + beadId: string; + beadStatus: string; + agents: AgentArchetype[]; + projectRoot: string; + currentAgentTypeId?: string; + onAgentAssigned?: (agentTypeId: string) => void; + onAgentSpawned?: (workerId: string, displayName: string) => void; + size?: 'sm' | 'md'; +} + +export function AgentActionRow({ + beadId, + beadStatus, + agents, + projectRoot, + currentAgentTypeId, + onAgentAssigned, + onAgentSpawned, + size = 'sm', +}: AgentActionRowProps) { + const { workerStatus, workerDisplayName, workerError } = useAgentStatus(beadId); + const { spawn, isSpawning } = useSpawnAgent(projectRoot); + + const handleAssign = async (agentTypeId: string) => { + // Call API to assign agent type to bead + try { + const response = await fetch('/api/beads/assign-agent', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ beadId, agentTypeId }), + }); + if (response.ok && onAgentAssigned) { + onAgentAssigned(agentTypeId); + } + } catch (error) { + console.error('Failed to assign agent:', error); + } + }; + + const handleSpawn = async () => { + if (!currentAgentTypeId) return; + + const result = await spawn(beadId, currentAgentTypeId); + if (result.success && onAgentSpawned) { + onAgentSpawned(result.workerId!, result.displayName!); + } + }; + + // Don't show for closed beads + if (beadStatus === 'closed') { + return null; + } + + return ( +
+ + +
+ ); +} +``` + +**Step 2: Commit** + +```bash +git add src/components/agents/agent-action-row.tsx +git commit -m "feat: add AgentActionRow combining assign + spawn" +``` + +--- + +## Task 8: Create agents index + +**Files:** +- Create: `src/components/agents/index.ts` + +**Step 1: Create index exports** + +```typescript +// src/components/agents/index.ts +export { AgentActionRow, type AgentActionRowProps } from './agent-action-row'; +export { AgentAssignButton, type AgentAssignButtonProps } from './agent-assign-button'; +export { AgentSpawnButton, type AgentSpawnButtonProps } from './agent-spawn-button'; +export { AgentPickerPopup, type AgentPickerPopupProps } from './agent-picker-popup'; +export * from './hooks'; +``` + +**Step 2: Commit** + +```bash +git add src/components/agents/index.ts +git commit -m "feat: add agents components index" +``` + +--- + +## Task 9: Add AgentActionRow to SocialCard + +**Files:** +- Modify: `src/components/social/social-card.tsx` + +**Step 1: Import and add AgentActionRow** + +Find the actions area in social-card.tsx (around the rocket button) and replace with: + +```typescript +// Add import at top +import { AgentActionRow } from '../agents'; + +// In the component props, ensure these are available: +// - projectRoot: string +// - archetypes: AgentArchetype[] + +// Find the button area (around line 369) and add: +{projectRoot && ( + +)} +``` + +**Step 2: Commit** + +```bash +git add src/components/social/social-card.tsx +git commit -m "feat: add AgentActionRow to SocialCard" +``` + +--- + +## Task 10: Add AgentActionRow to GraphNodeCard + +**Files:** +- Modify: `src/components/graph/graph-node-card.tsx` + +**Step 1: Import and add AgentActionRow** + +```typescript +// Add import +import { AgentActionRow } from '../agents'; + +// Find the actions area in the node card and add: + +``` + +**Step 2: Commit** + +```bash +git add src/components/graph/graph-node-card.tsx +git commit -m "feat: add AgentActionRow to GraphNodeCard" +``` + +--- + +## Task 11: Add AgentActionRow to BlockedTriageModal + +**Files:** +- Modify: `src/components/shared/blocked-triage-modal.tsx` + +**Step 1: Import and add AgentActionRow** + +```typescript +// Add import +import { AgentActionRow } from '../agents'; + +// In each task row, add the action row: + +``` + +**Step 2: Commit** + +```bash +git add src/components/shared/blocked-triage-modal.tsx +git commit -m "feat: add AgentActionRow to BlockedTriageModal" +``` + +--- + +## Task 12: Create Spawn API Endpoint + +**Files:** +- Create: `src/app/api/runtime/spawn/route.ts` + +**Step 1: Create spawn API** + +```typescript +// src/app/api/runtime/spawn/route.ts +import { NextResponse } from 'next/server'; +import { workerSessionManager } from '../../../../lib/worker-session-manager'; + +export async function POST(request: Request) { + try { + const { projectRoot, beadId, agentTypeId } = await request.json(); + + if (!projectRoot || !beadId || !agentTypeId) { + return NextResponse.json({ + ok: false, + error: 'projectRoot, beadId, and agentTypeId are required', + }); + } + + // Spawn worker via session manager + const worker = await workerSessionManager.spawnWorker({ + projectRoot, + taskId: beadId, + taskContext: `Work on ${beadId}`, + agentType: agentTypeId, + beadId, + }); + + return NextResponse.json({ + ok: true, + workerId: worker.id, + displayName: worker.displayName, + agentTypeId: worker.agentTypeId, + }); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return NextResponse.json({ ok: false, error: message }); + } +} +``` + +**Step 2: Commit** + +```bash +git add src/app/api/runtime/spawn/route.ts +git commit -m "feat: add spawn API endpoint" +``` + +--- + +## Task 13: Create Assign Agent API Endpoint + +**Files:** +- Create: `src/app/api/beads/assign-agent/route.ts` + +**Step 1: Create assign API** + +```typescript +// src/app/api/beads/assign-agent/route.ts +import { NextResponse } from 'next/server'; +import { execFileSync } from 'child_process'; + +export async function POST(request: Request) { + try { + const { beadId, agentTypeId } = await request.json(); + + if (!beadId || !agentTypeId) { + return NextResponse.json({ + ok: false, + error: 'beadId and agentTypeId are required', + }); + } + + // Use bd CLI to add agent label + execFileSync('bd', [ + 'label', + 'add', + beadId, + `agent:${agentTypeId}`, + ], { + encoding: 'utf-8', + timeout: 10000, + }); + + return NextResponse.json({ ok: true, beadId, agentTypeId }); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return NextResponse.json({ ok: false, error: message }); + } +} +``` + +**Step 2: Commit** + +```bash +git add src/app/api/beads/assign-agent/route.ts +git commit -m "feat: add assign-agent API endpoint" +``` + +--- + +## Task 14: Update useAgentStatus to fetch real data + +**Files:** +- Modify: `src/components/agents/hooks/use-agent-status.ts` + +**Step 1: Implement real status fetching** + +```typescript +// src/components/agents/hooks/use-agent-status.ts +import { useState, useEffect } from 'react'; + +export type WorkerStatus = 'idle' | 'spawning' | 'working' | 'blocked' | 'completed' | 'failed'; + +export interface AgentStatus { + agentTypeId?: string; + workerStatus: WorkerStatus; + workerDisplayName?: string; + workerError?: string; + isLoading: boolean; +} + +export function useAgentStatus(beadId: string): AgentStatus { + const [status, setStatus] = useState({ + workerStatus: 'idle', + isLoading: true, + }); + + useEffect(() => { + let cancelled = false; + + const fetchStatus = async () => { + try { + const response = await fetch(`/api/runtime/worker-status?beadId=${encodeURIComponent(beadId)}`); + const data = await response.json(); + + if (!cancelled) { + setStatus({ + agentTypeId: data.agentTypeId, + workerStatus: data.workerStatus || 'idle', + workerDisplayName: data.workerDisplayName, + workerError: data.workerError, + isLoading: false, + }); + } + } catch (error) { + if (!cancelled) { + setStatus({ workerStatus: 'idle', isLoading: false }); + } + } + }; + + fetchStatus(); + const interval = setInterval(fetchStatus, 5000); // Poll every 5s + + return () => { + cancelled = true; + clearInterval(interval); + }; + }, [beadId]); + + return status; +} +``` + +**Step 2: Commit** + +```bash +git add src/components/agents/hooks/use-agent-status.ts +git commit -m "feat: implement real status fetching in useAgentStatus" +``` + +--- + +## Task 15: Create Worker Status API + +**Files:** +- Create: `src/app/api/runtime/worker-status/route.ts` + +**Step 1: Create status API** + +```typescript +// src/app/api/runtime/worker-status/route.ts +import { NextResponse } from 'next/server'; +import { workerSessionManager } from '../../../../lib/worker-session-manager'; + +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const beadId = searchParams.get('beadId'); + + if (!beadId) { + return NextResponse.json({ ok: false, error: 'beadId required' }); + } + + // Find worker for this bead + const workers = workerSessionManager.getAllWorkers(); + const worker = workers.find(w => w.beadId === beadId); + + if (!worker) { + return NextResponse.json({ + ok: true, + workerStatus: 'idle', + agentTypeId: null, + }); + } + + return NextResponse.json({ + ok: true, + workerStatus: worker.status, + workerDisplayName: worker.displayName, + workerError: worker.error, + agentTypeId: worker.agentTypeId, + }); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return NextResponse.json({ ok: false, error: message }); + } +} +``` + +**Step 2: Commit** + +```bash +git add src/app/api/runtime/worker-status/route.ts +git commit -m "feat: add worker-status API endpoint" +``` + +--- + +## Task 16: Add agentTypeId to BeadIssue type + +**Files:** +- Modify: `src/lib/types.ts` + +**Step 1: Add agentTypeId field** + +```typescript +// In BeadIssue interface, add: +agentTypeId?: string; +agentInstanceId?: string; +``` + +**Step 2: Commit** + +```bash +git add src/lib/types.ts +git commit -m "feat: add agentTypeId and agentInstanceId to BeadIssue" +``` + +--- + +## Task 17: Test the complete flow + +**Step 1: Run TypeScript check** + +```bash +cd /home/clawdbot/clawd/repos/beadboard +npx tsc --noEmit +``` + +Expected: No errors related to agents components + +**Step 2: Test in browser** + +1. Open app +2. Go to social view +3. Click 👤 on a card → picker opens +4. Select Engineer → badge appears, 🚀 turns blue +5. Click 🚀 → spawns worker → 🚀 turns green +6. Hover 🚀 → shows worker name and status + +**Step 3: Commit test results** + +```bash +git add -A +git commit -m "test: Phase 4 launch-anywhere UX complete" +``` + +--- + +## Success Criteria + +- [ ] Social cards show 👤 assign button + colored 🚀 spawn button +- [ ] Graph nodes show same two-icon system +- [ ] Blocked triage modal has agent actions +- [ ] Clicking 👤 opens agent picker +- [ ] Selecting agent updates UI (badge + blue rocket) +- [ ] Clicking 🚀 spawns worker +- [ ] Rocket colors: blue=ready, green=working, red=blocked, gray=done +- [ ] Tooltips show worker status on hover +- [ ] Orchestrator is an option in agent picker + +--- + +## Estimated Effort + +6-8 hours + +--- + +## Blocked Items + +None identified. + +--- + +## Files Summary + +| File | Action | +|------|--------| +| `src/components/agents/hooks/use-agent-status.ts` | Create | +| `src/components/agents/hooks/use-spawn-agent.ts` | Create | +| `src/components/agents/hooks/index.ts` | Create | +| `src/components/agents/agent-picker-popup.tsx` | Create | +| `src/components/agents/agent-assign-button.tsx` | Create | +| `src/components/agents/agent-spawn-button.tsx` | Create | +| `src/components/agents/agent-action-row.tsx` | Create | +| `src/components/agents/index.ts` | Create | +| `src/components/social/social-card.tsx` | Modify | +| `src/components/graph/graph-node-card.tsx` | Modify | +| `src/components/shared/blocked-triage-modal.tsx` | Modify | +| `src/app/api/runtime/spawn/route.ts` | Create | +| `src/app/api/beads/assign-agent/route.ts` | Create | +| `src/app/api/runtime/worker-status/route.ts` | Create | +| `src/lib/types.ts` | Modify | diff --git a/package-lock.json b/package-lock.json index a537020..c99f812 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "0.1.0", "license": "MIT", "dependencies": { + "@mariozechner/pi-agent-core": "^0.57.1", + "@mariozechner/pi-ai": "^0.57.1", + "@mariozechner/pi-coding-agent": "^0.57.1", "@radix-ui/react-avatar": "^1.1.11", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", @@ -22,6 +25,7 @@ "@remotion/google-fonts": "^4.0.422", "@remotion/tailwind": "^4.0.422", "@remotion/zod-types": "^4.0.422", + "@sinclair/typebox": "^0.34.48", "@xyflow/react": "^12.10.0", "chokidar": "^5.0.0", "class-variance-authority": "^0.7.1", @@ -36,8 +40,13 @@ "remotion": "^4.0.422", "tailwind-merge": "^3.4.1", "tailwindcss-animate": "^1.0.7", + "tsx": "^4.21.0", "zod": "3.22.3" }, + "bin": { + "bb": "bin/beadboard.js", + "beadboard": "bin/beadboard.js" + }, "devDependencies": { "@playwright/test": "^1.58.2", "@remotion/cli": "^4.0.422", @@ -51,7 +60,6 @@ "playwright": "^1.58.2", "postcss": "^8.5.6", "tailwindcss": "^3.4.17", - "tsx": "^4.21.0", "typescript": "^5.7.2" } }, @@ -67,6 +75,700 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.1005.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1005.0.tgz", + "integrity": "sha512-IV5vZ6H46ZNsTxsFWkbrJkg+sPe6+3m90k7EejgB/AFCb/YQuseH0+I3B57ew+zoOaXJU71KDPBwsIiMSsikVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/credential-provider-node": "^3.972.19", + "@aws-sdk/eventstream-handler-node": "^3.972.10", + "@aws-sdk/middleware-eventstream": "^3.972.7", + "@aws-sdk/middleware-host-header": "^3.972.7", + "@aws-sdk/middleware-logger": "^3.972.7", + "@aws-sdk/middleware-recursion-detection": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.20", + "@aws-sdk/middleware-websocket": "^3.972.12", + "@aws-sdk/region-config-resolver": "^3.972.7", + "@aws-sdk/token-providers": "3.1005.0", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@aws-sdk/util-user-agent-browser": "^3.972.7", + "@aws-sdk/util-user-agent-node": "^3.973.5", + "@smithy/config-resolver": "^4.4.10", + "@smithy/core": "^3.23.9", + "@smithy/eventstream-serde-browser": "^4.2.11", + "@smithy/eventstream-serde-config-resolver": "^4.3.11", + "@smithy/eventstream-serde-node": "^4.2.11", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/hash-node": "^4.2.11", + "@smithy/invalid-dependency": "^4.2.11", + "@smithy/middleware-content-length": "^4.2.11", + "@smithy/middleware-endpoint": "^4.4.23", + "@smithy/middleware-retry": "^4.4.40", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.39", + "@smithy/util-defaults-mode-node": "^4.2.42", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.973.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.19.tgz", + "integrity": "sha512-56KePyOcZnKTWCd89oJS1G6j3HZ9Kc+bh/8+EbvtaCCXdP6T7O7NzCiPuHRhFLWnzXIaXX3CxAz0nI5My9spHQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/xml-builder": "^3.972.10", + "@smithy/core": "^3.23.9", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.17.tgz", + "integrity": "sha512-MBAMW6YELzE1SdkOniqr51mrjapQUv8JXSGxtwRjQV0mwVDutVsn22OPAUt4RcLRvdiHQmNBDEFP9iTeSVCOlA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.19.tgz", + "integrity": "sha512-9EJROO8LXll5a7eUFqu48k6BChrtokbmgeMWmsH7lBb6lVbtjslUYz/ShLi+SHkYzTomiGBhmzTW7y+H4BxsnA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/types": "^3.973.5", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/util-stream": "^4.5.17", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.18.tgz", + "integrity": "sha512-vthIAXJISZnj2576HeyLBj4WTeX+I7PwWeRkbOa0mVX39K13SCGxCgOFuKj2ytm9qTlLOmXe4cdEnroteFtJfw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/credential-provider-env": "^3.972.17", + "@aws-sdk/credential-provider-http": "^3.972.19", + "@aws-sdk/credential-provider-login": "^3.972.18", + "@aws-sdk/credential-provider-process": "^3.972.17", + "@aws-sdk/credential-provider-sso": "^3.972.18", + "@aws-sdk/credential-provider-web-identity": "^3.972.18", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/types": "^3.973.5", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.18.tgz", + "integrity": "sha512-kINzc5BBxdYBkPZ0/i1AMPMOk5b5QaFNbYMElVw5QTX13AKj6jcxnv/YNl9oW9mg+Y08ti19hh01HhyEAxsSJQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.19.tgz", + "integrity": "sha512-yDWQ9dFTr+IMxwanFe7+tbN5++q8psZBjlUwOiCXn1EzANoBgtqBwcpYcHaMGtn0Wlfj4NuXdf2JaEx1lz5RaQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.17", + "@aws-sdk/credential-provider-http": "^3.972.19", + "@aws-sdk/credential-provider-ini": "^3.972.18", + "@aws-sdk/credential-provider-process": "^3.972.17", + "@aws-sdk/credential-provider-sso": "^3.972.18", + "@aws-sdk/credential-provider-web-identity": "^3.972.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.17.tgz", + "integrity": "sha512-c8G8wT1axpJDgaP3xzcy+q8Y1fTi9A2eIQJvyhQ9xuXrUZhlCfXbC0vM9bM1CUXiZppFQ1p7g0tuUMvil/gCPg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.18.tgz", + "integrity": "sha512-YHYEfj5S2aqInRt5ub8nDOX8vAxgMvd84wm2Y3WVNfFa/53vOv9T7WOAqXI25qjj3uEcV46xxfqdDQk04h5XQA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/token-providers": "3.1005.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.18.tgz", + "integrity": "sha512-OqlEQpJ+J3T5B96qtC1zLLwkBloechP+fezKbCH0sbd2cCc0Ra55XpxWpk/hRj69xAOYtHvoC4orx6eTa4zU7g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.10.tgz", + "integrity": "sha512-g2Z9s6Y4iNh0wICaEqutgYgt/Pmhv5Ev9G3eKGFe2w9VuZDhc76vYdop6I5OocmpHV79d4TuLG+JWg5rQIVDVA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/eventstream-codec": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.7.tgz", + "integrity": "sha512-VWndapHYCfwLgPpCb/xwlMKG4imhFzKJzZcKOEioGn7OHY+6gdr0K7oqy1HZgbLa3ACznZ9fku+DzmAi8fUC0g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.7.tgz", + "integrity": "sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.7.tgz", + "integrity": "sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.7.tgz", + "integrity": "sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.20.tgz", + "integrity": "sha512-3kNTLtpUdeahxtnJRnj/oIdLAUdzTfr9N40KtxNhtdrq+Q1RPMdCJINRXq37m4t5+r3H70wgC3opW46OzFcZYA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@smithy/core": "^3.23.9", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-retry": "^4.2.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-websocket": { + "version": "3.972.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.12.tgz", + "integrity": "sha512-iyPP6FVDKe/5wy5ojC0akpDFG1vX3FeCUU47JuwN8xfvT66xlEI8qUJZPtN55TJVFzzWZJpWL78eqUE31md08Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-format-url": "^3.972.7", + "@smithy/eventstream-codec": "^4.2.11", + "@smithy/eventstream-serde-browser": "^4.2.11", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.996.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.8.tgz", + "integrity": "sha512-6HlLm8ciMW8VzfB80kfIx16PBA9lOa9Dl+dmCBi78JDhvGlx3I7Rorwi5PpVRkL31RprXnYna3yBf6UKkD/PqA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/middleware-host-header": "^3.972.7", + "@aws-sdk/middleware-logger": "^3.972.7", + "@aws-sdk/middleware-recursion-detection": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.20", + "@aws-sdk/region-config-resolver": "^3.972.7", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@aws-sdk/util-user-agent-browser": "^3.972.7", + "@aws-sdk/util-user-agent-node": "^3.973.5", + "@smithy/config-resolver": "^4.4.10", + "@smithy/core": "^3.23.9", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/hash-node": "^4.2.11", + "@smithy/invalid-dependency": "^4.2.11", + "@smithy/middleware-content-length": "^4.2.11", + "@smithy/middleware-endpoint": "^4.4.23", + "@smithy/middleware-retry": "^4.4.40", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.39", + "@smithy/util-defaults-mode-node": "^4.2.42", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.7.tgz", + "integrity": "sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/config-resolver": "^4.4.10", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1005.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1005.0.tgz", + "integrity": "sha512-vMxd+ivKqSxU9bHx5vmAlFKDAkjGotFU56IOkDa5DaTu1WWwbcse0yFHEm9I537oVvodaiwMl3VBwgHfzQ2rvw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.5.tgz", + "integrity": "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", + "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-endpoints": "^3.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.972.7.tgz", + "integrity": "sha512-V+PbnWfUl93GuFwsOHsAq7hY/fnm9kElRqR8IexIJr5Rvif9e614X5sGSyz3mVSf1YAZ+VTy63W1/pGdA55zyA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/querystring-builder": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.7.tgz", + "integrity": "sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.5.tgz", + "integrity": "sha512-Dyy38O4GeMk7UQ48RupfHif//gqnOPbq/zlvRssc11E2mClT+aUfc3VS2yD8oLtzqO3RsqQ9I3gOBB4/+HjPOw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "^3.972.20", + "@aws-sdk/types": "^3.973.5", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.10.tgz", + "integrity": "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "fast-xml-parser": "5.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -103,6 +805,25 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz", + "integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/@csstools/cascade-layer-name-parser": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.13.tgz", @@ -889,7 +1610,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -906,7 +1626,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -923,7 +1642,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -940,7 +1658,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -957,7 +1674,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -974,7 +1690,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -991,7 +1706,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1008,7 +1722,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1025,7 +1738,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1042,7 +1754,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1059,7 +1770,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1076,7 +1786,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1093,7 +1802,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1110,7 +1818,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1127,7 +1834,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1144,7 +1850,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1161,7 +1866,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1178,7 +1882,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1195,7 +1898,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1212,7 +1914,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1229,7 +1930,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1246,7 +1946,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1263,7 +1962,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1280,7 +1978,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1297,7 +1994,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1314,7 +2010,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1506,6 +2201,50 @@ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", "license": "MIT" }, + "node_modules/@google/genai": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.44.0.tgz", + "integrity": "sha512-kRt9ZtuXmz+tLlcNntN/VV4LRdpl6ZOu5B1KbfNgfR65db15O6sUQcwnwLka8sT/V6qysD93fWrgJHF2L7dA9A==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@google/genai/node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -2024,6 +2763,69 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -2069,6 +2871,569 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mariozechner/clipboard": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.2.tgz", + "integrity": "sha512-IHQpksNjo7EAtGuHFU+tbWDp5LarH3HU/8WiB9O70ZEoBPHOg0/6afwSLK0QyNMMmx4Bpi/zl6+DcBXe95nWYA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@mariozechner/clipboard-darwin-arm64": "0.3.2", + "@mariozechner/clipboard-darwin-universal": "0.3.2", + "@mariozechner/clipboard-darwin-x64": "0.3.2", + "@mariozechner/clipboard-linux-arm64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-arm64-musl": "0.3.2", + "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-x64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-x64-musl": "0.3.2", + "@mariozechner/clipboard-win32-arm64-msvc": "0.3.2", + "@mariozechner/clipboard-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@mariozechner/clipboard-darwin-arm64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.2.tgz", + "integrity": "sha512-uBf6K7Je1ihsgvmWxA8UCGCeI+nbRVRXoarZdLjl6slz94Zs1tNKFZqx7aCI5O1i3e0B6ja82zZ06BWrl0MCVw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-universal": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.2.tgz", + "integrity": "sha512-mxSheKTW2U9LsBdXy0SdmdCAE5HqNS9QUmpNHLnfJ+SsbFKALjEZc5oRrVMXxGQSirDvYf5bjmRyT0QYYonnlg==", + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-x64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.2.tgz", + "integrity": "sha512-U1BcVEoidvwIp95+HJswSW+xr28EQiHR7rZjH6pn8Sja5yO4Yoe3yCN0Zm8Lo72BbSOK/fTSq0je7CJpaPCspg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha512-BsinwG3yWTIjdgNCxsFlip7LkfwPk+ruw/aFCXHUg/fb5XC/Ksp+YMQ7u0LUtiKzIv/7LMXgZInJQH6gxbAaqQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-musl/-/clipboard-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha512-0/Gi5Xq2V6goXBop19ePoHvXsmJD9SzFlO3S+d6+T2b+BlPcpOu3Oa0wTjl+cZrLAAEzA86aPNBI+VVAFDFPKw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-riscv64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.2.tgz", + "integrity": "sha512-2AFFiXB24qf0zOZsxI1GJGb9wQGlOJyN6UwoXqmKS3dpQi/l6ix30IzDDA4c4ZcCcx4D+9HLYXhC1w7Sov8pXA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha512-v6fVnsn7WMGg73Dab8QMwyFce7tzGfgEixKgzLP8f1GJqkJZi5zO4k4FOHzSgUufgLil63gnxvMpjWkgfeQN7A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.2.tgz", + "integrity": "sha512-xVUtnoMQ8v2JVyfJLKKXACA6avdnchdbBkTsZs8BgJQo29qwCp5NIHAUO8gbJ40iaEGToW5RlmVk2M9V0HsHEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha512-AEgg95TNi8TGgak2wSXZkXKCvAUTjWoU1Pqb0ON7JHrX78p616XUFNTJohtIon3e0w6k0pYPZeCuqRCza/Tqeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha512-tGRuYpZwDOD7HBrCpyRuhGnHHSCknELvqwKKUG4JSfSB7JIU7LKRh6zx6fMUOQd8uISK35TjFg5UcNih+vJhFA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/jiti": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.5.tgz", + "integrity": "sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==", + "license": "MIT", + "dependencies": { + "std-env": "^3.10.0", + "yoctocolors": "^2.1.2" + }, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/@mariozechner/pi-agent-core": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.57.1.tgz", + "integrity": "sha512-WXsBbkNWOObFGHkhixaT8GXJpHDd3+fn8QntYF+4R8Sa9WB90ENXWidO6b7vcKX+JX0jjO5dIsQxmzosARJKlg==", + "license": "MIT", + "dependencies": { + "@mariozechner/pi-ai": "^0.57.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-ai": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.57.1.tgz", + "integrity": "sha512-Bd/J4a3YpdzJVyHLih0vDSdB0QPL4ti0XsAwtHOK/8eVhB0fHM1CpcgIrcBFJ23TMcKXMi0qamz18ERfp8tmgg==", + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "^0.73.0", + "@aws-sdk/client-bedrock-runtime": "^3.983.0", + "@google/genai": "^1.40.0", + "@mistralai/mistralai": "1.14.1", + "@sinclair/typebox": "^0.34.41", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "chalk": "^5.6.2", + "openai": "6.26.0", + "partial-json": "^0.1.7", + "proxy-agent": "^6.5.0", + "undici": "^7.19.1", + "zod-to-json-schema": "^3.24.6" + }, + "bin": { + "pi-ai": "dist/cli.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-ai/node_modules/@anthropic-ai/sdk": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.73.0.tgz", + "integrity": "sha512-URURVzhxXGJDGUGFunIOtBlSl7KWvZiAAKY/ttTkZAkXT9bTPqdk2eK0b8qqSxXpikh3QKPnPYpiyX98zf5ebw==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@mariozechner/pi-ai/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@mariozechner/pi-ai/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@mariozechner/pi-ai/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@mariozechner/pi-ai/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/@mariozechner/pi-ai/node_modules/openai": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz", + "integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@mariozechner/pi-ai/node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@mariozechner/pi-ai/node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@mariozechner/pi-ai/node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + }, + "node_modules/@mariozechner/pi-coding-agent": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.57.1.tgz", + "integrity": "sha512-u5MQEduj68rwVIsRsqrWkJYiJCyPph/a6bMoJAQKo1sb+Pc17Y/ojwa+wGssnUMjEB38AQKofWTVe8NFEpSWNw==", + "license": "MIT", + "dependencies": { + "@mariozechner/jiti": "^2.6.2", + "@mariozechner/pi-agent-core": "^0.57.1", + "@mariozechner/pi-ai": "^0.57.1", + "@mariozechner/pi-tui": "^0.57.1", + "@silvia-odwyer/photon-node": "^0.3.4", + "chalk": "^5.5.0", + "cli-highlight": "^2.1.11", + "diff": "^8.0.2", + "extract-zip": "^2.0.1", + "file-type": "^21.1.1", + "glob": "^13.0.1", + "hosted-git-info": "^9.0.2", + "ignore": "^7.0.5", + "marked": "^15.0.12", + "minimatch": "^10.2.3", + "proper-lockfile": "^4.1.2", + "strip-ansi": "^7.1.0", + "undici": "^7.19.1", + "yaml": "^2.8.2" + }, + "bin": { + "pi": "dist/cli.js" + }, + "engines": { + "node": ">=20.6.0" + }, + "optionalDependencies": { + "@mariozechner/clipboard": "^0.3.2" + } + }, + "node_modules/@mariozechner/pi-coding-agent/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@mariozechner/pi-coding-agent/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@mariozechner/pi-coding-agent/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@mariozechner/pi-coding-agent/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@mariozechner/pi-coding-agent/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@mariozechner/pi-tui": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.57.1.tgz", + "integrity": "sha512-cjoRghLbeAHV0tTJeHgZXaryUi5zzBZofeZ7uJun1gztnckLLRjoVeaPTujNlc5BIfyKvFqhh1QWCZng/MXlpg==", + "license": "MIT", + "dependencies": { + "@types/mime-types": "^2.1.4", + "chalk": "^5.5.0", + "get-east-asian-width": "^1.3.0", + "marked": "^15.0.12", + "mime-types": "^3.0.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "optionalDependencies": { + "koffi": "^2.9.0" + } + }, + "node_modules/@mariozechner/pi-tui/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@mariozechner/pi-tui/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mariozechner/pi-tui/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@mistralai/mistralai": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.14.1.tgz", + "integrity": "sha512-IiLmmZFCCTReQgPAT33r7KQ1nYo5JPdvGkrkZqA8qQ2qB1GHgs5LoP5K2ICyrjnpw2n8oSxMM/VP+liiKcGNlQ==", + "dependencies": { + "ws": "^8.18.0", + "zod": "^3.25.0 || ^4.0.0", + "zod-to-json-schema": "^3.24.1" + } + }, + "node_modules/@mistralai/mistralai/node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@mistralai/mistralai/node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@mistralai/mistralai/node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -2301,6 +3666,16 @@ "node": ">=12.4.0" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@playwright/test": { "version": "1.58.2", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", @@ -2317,6 +3692,70 @@ "node": ">=18" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -4974,6 +6413,668 @@ "dev": true, "license": "MIT" }, + "node_modules/@silvia-odwyer/photon-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", + "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", + "license": "Apache-2.0" + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "license": "MIT" + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.11.tgz", + "integrity": "sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.10.tgz", + "integrity": "sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.23.9", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.9.tgz", + "integrity": "sha512-1Vcut4LEL9HZsdpI0vFiRYIsaoPwZLjAxnVQDUMQK8beMS+EYPLDQCXtbzfxmM5GzSgjfe2Q9M7WaXwIMQllyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.12", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.11.tgz", + "integrity": "sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.11.tgz", + "integrity": "sha512-Sf39Ml0iVX+ba/bgMPxaXWAAFmHqYLTmbjAPfLPLY8CrYkRDEqZdUsKC1OwVMCdJXfAt0v4j49GIJ8DoSYAe6w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-hex-encoding": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.11.tgz", + "integrity": "sha512-3rEpo3G6f/nRS7fQDsZmxw/ius6rnlIpz4UX6FlALEzz8JoSxFmdBt0SZnthis+km7sQo6q5/3e+UJcuQivoXA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.11.tgz", + "integrity": "sha512-XeNIA8tcP/GDWnnKkO7qEm/bg0B/bP9lvIXZBXcGZwZ+VYM8h8k9wuDvUODtdQ2Wcp2RcBkPTCSMmaniVHrMlA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.11.tgz", + "integrity": "sha512-fzbCh18rscBDTQSCrsp1fGcclLNF//nJyhjldsEl/5wCYmgpHblv5JSppQAyQI24lClsFT0wV06N1Porn0IsEw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.11.tgz", + "integrity": "sha512-MJ7HcI+jEkqoWT5vp+uoVaAjBrmxBtKhZTeynDRG/seEjJfqyg3SiqMMqyPnAMzmIfLaeJ/uiuSDP/l9AnMy/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.13", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.13.tgz", + "integrity": "sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.11", + "@smithy/querystring-builder": "^4.2.11", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.11.tgz", + "integrity": "sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.11.tgz", + "integrity": "sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.11.tgz", + "integrity": "sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.23", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.23.tgz", + "integrity": "sha512-UEFIejZy54T1EJn2aWJ45voB7RP2T+IRzUqocIdM6GFFa5ClZncakYJfcYnoXt3UsQrZZ9ZRauGm77l9UCbBLw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.9", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-middleware": "^4.2.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.40", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.40.tgz", + "integrity": "sha512-YhEMakG1Ae57FajERdHNZ4ShOPIY7DsgV+ZoAxo/5BT0KIe+f6DDU2rtIymNNFIj22NJfeeI6LWIifrwM0f+rA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/service-error-classification": "^4.2.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.12.tgz", + "integrity": "sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.11.tgz", + "integrity": "sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.11.tgz", + "integrity": "sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.14.tgz", + "integrity": "sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/querystring-builder": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.11.tgz", + "integrity": "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.11.tgz", + "integrity": "sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.11.tgz", + "integrity": "sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "@smithy/util-uri-escape": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.11.tgz", + "integrity": "sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.11.tgz", + "integrity": "sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.6.tgz", + "integrity": "sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.11.tgz", + "integrity": "sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-uri-escape": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.3.tgz", + "integrity": "sha512-7k4UxjSpHmPN2AxVhvIazRSzFQjWnud3sOsXcFStzagww17j1cFQYqTSiQ8xuYK3vKLR1Ni8FzuT3VlKr3xCNw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.9", + "@smithy/middleware-endpoint": "^4.4.23", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-stream": "^4.5.17", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.0.tgz", + "integrity": "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.11.tgz", + "integrity": "sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.39", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.39.tgz", + "integrity": "sha512-ui7/Ho/+VHqS7Km2wBw4/Ab4RktoiSshgcgpJzC4keFPs6tLJS4IQwbeahxQS3E/w98uq6E1mirCH/id9xIXeQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.42", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.42.tgz", + "integrity": "sha512-QDA84CWNe8Akpj15ofLO+1N3Rfg8qa2K5uX0y6HnOp4AnRYRgWrKx/xzbYNbVF9ZsyJUYOfcoaN3y93wA/QJ2A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.10", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.2.tgz", + "integrity": "sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.11.tgz", + "integrity": "sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.11.tgz", + "integrity": "sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.17", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.17.tgz", + "integrity": "sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -4983,6 +7084,35 @@ "tslib": "^2.8.0" } }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -5104,6 +7234,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.19.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", @@ -5133,6 +7269,12 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -5905,6 +8047,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -5969,11 +8120,22 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -6313,7 +8475,26 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, "node_modules/baseline-browser-mapping": { @@ -6325,6 +8506,15 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/basic-ftp": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", + "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -6334,6 +8524,15 @@ "node": "*" } }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -6346,6 +8545,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -6411,6 +8616,12 @@ "node": "*" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6509,7 +8720,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -6564,12 +8774,65 @@ "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", "license": "MIT" }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -6583,7 +8846,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -6596,7 +8858,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/commander": { @@ -6896,6 +9157,15 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -7019,6 +9289,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/degenerator/node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -7050,6 +9346,15 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "license": "Apache-2.0" }, + "node_modules/diff": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -7097,6 +9402,21 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", @@ -7107,7 +9427,6 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/emojis-list": { @@ -7337,7 +9656,6 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -7397,6 +9715,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint": { "version": "9.39.2", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", @@ -7780,7 +10129,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -7828,7 +10176,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -7866,6 +10213,12 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -7964,6 +10317,37 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fast-xml-builder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz", + "integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", + "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "fast-xml-builder": "^1.0.0", + "strnum": "^2.1.2" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -7982,6 +10366,29 @@ "pend": "~1.2.0" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -7995,6 +10402,24 @@ "node": ">=16.0.0" } }, + "node_modules/file-type": { + "version": "21.3.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.1.tgz", + "integrity": "sha512-SrzXX46I/zsRDjTb82eucsGg0ODq2NpGDp4HcsFKApPy8P8vACjpJRDoGGMfEzhFC0ry61ajd7f72J3603anBA==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -8061,6 +10486,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fraction.js": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", @@ -8161,6 +10626,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -8180,6 +10674,27 @@ "node": ">= 0.4" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -8262,7 +10777,6 @@ "version": "4.13.6", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", - "dev": true, "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -8271,6 +10785,46 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -8289,6 +10843,42 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "license": "BSD-2-Clause" }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -8319,6 +10909,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/google-auth-library": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.1.tgz", + "integrity": "sha512-5awwuLrzNol+pFDmKJd0dKtZ0fPLAtoA5p7YO4ODsDu6ONJUVqbYwvv8y2ZBO5MBNp9TJXigB19710kYpBPdtA==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "7.1.3", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -8439,6 +11055,62 @@ "node": ">= 0.4" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -8476,6 +11148,26 @@ "postcss": "^8.1.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -8527,6 +11219,15 @@ "node": ">= 0.4" } }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -8729,6 +11430,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", @@ -9019,6 +11729,21 @@ "node": ">= 0.4" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -9075,6 +11800,15 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -9088,6 +11822,19 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "license": "MIT" }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -9129,6 +11876,27 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -9149,6 +11917,17 @@ "node": ">=6" } }, + "node_modules/koffi": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.15.1.tgz", + "integrity": "sha512-mnc0C0crx/xMSljb5s9QbnLrlFHprioFO1hkXyuSuO/QtbpLDa0l/uM21944UfQunMKmp3/r789DTDxVyyH6aA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://liberapay.com/Koromix" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -9318,6 +12097,18 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -9438,6 +12229,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/motion-dom": { "version": "11.18.1", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", @@ -9551,6 +12351,15 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "license": "MIT" }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/next": { "version": "15.5.7", "resolved": "https://registry.npmjs.org/next/-/next-15.5.7.tgz", @@ -9632,6 +12441,44 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -9908,6 +12755,57 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -9938,6 +12836,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "license": "MIT" + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -9963,6 +12888,31 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -10941,6 +13891,84 @@ "react-is": "^16.13.1" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", @@ -11198,6 +14226,15 @@ "react-dom": ">=16.8.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -11240,12 +14277,20 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -11256,6 +14301,88 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -11609,6 +14736,44 @@ "dev": true, "license": "MIT" }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", @@ -11668,6 +14833,12 @@ "dev": true, "license": "MIT" }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -11682,6 +14853,89 @@ "node": ">= 0.4" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", @@ -11795,6 +15049,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -11827,6 +15118,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", + "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/strtok3": { + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/style-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", @@ -11892,7 +15211,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -12261,6 +15579,24 @@ "node": ">=8.0" } }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", @@ -12270,6 +15606,12 @@ "punycode": "^2.1.0" } }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", @@ -12325,7 +15667,6 @@ "version": "4.21.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "~0.27.0", @@ -12446,6 +15787,18 @@ "node": ">=14.17" } }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -12465,6 +15818,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -12616,6 +15978,15 @@ "node": ">=10.13.0" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -12879,6 +16250,83 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -12906,6 +16354,15 @@ } } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -12927,6 +16384,33 @@ "url": "https://github.com/sponsors/eemeli" } }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -12950,6 +16434,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "3.22.3", "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", diff --git a/package.json b/package.json index 2d80e53..a2cd088 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,9 @@ "video:thumbnail": "remotion still src/video/index.ts Main out/thumbnail.png --frame=60" }, "dependencies": { + "@mariozechner/pi-agent-core": "^0.57.1", + "@mariozechner/pi-ai": "^0.57.1", + "@mariozechner/pi-coding-agent": "^0.57.1", "@radix-ui/react-avatar": "^1.1.11", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", @@ -32,6 +35,7 @@ "@remotion/google-fonts": "^4.0.422", "@remotion/tailwind": "^4.0.422", "@remotion/zod-types": "^4.0.422", + "@sinclair/typebox": "^0.34.48", "@xyflow/react": "^12.10.0", "chokidar": "^5.0.0", "class-variance-authority": "^0.7.1", diff --git a/src/app/api/runtime/agents/history/route.ts b/src/app/api/runtime/agents/history/route.ts new file mode 100644 index 0000000..6c2ed28 --- /dev/null +++ b/src/app/api/runtime/agents/history/route.ts @@ -0,0 +1,38 @@ +import { NextResponse } from 'next/server'; +import { workerSessionManager, type WorkerSession } from '../../../../../lib/worker-session-manager'; + +export const dynamic = 'force-dynamic'; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const projectRoot = searchParams.get('projectRoot'); + + if (!projectRoot) { + return NextResponse.json({ ok: false, error: 'projectRoot required' }); + } + + // Get completed/failed workers as history + const workers = workerSessionManager.listWorkers(projectRoot); + const history = workers + .filter((w: WorkerSession) => w.status === 'completed' || w.status === 'failed') + .sort((a: WorkerSession, b: WorkerSession) => + new Date(b.completedAt || 0).getTime() - new Date(a.completedAt || 0).getTime() + ) + .slice(0, 50) + .map((w: WorkerSession) => ({ + id: w.agentInstanceId || w.id, + agentTypeId: w.agentTypeId || 'unknown', + displayName: w.displayName || `Worker ${w.id}`, + status: w.status, + currentBeadId: w.taskId, + startedAt: w.createdAt, + completedAt: w.completedAt, + result: w.result, + error: w.error, + })); + + return NextResponse.json({ + ok: true, + instances: history, + }); +} diff --git a/src/app/api/runtime/agents/route.ts b/src/app/api/runtime/agents/route.ts new file mode 100644 index 0000000..fb093df --- /dev/null +++ b/src/app/api/runtime/agents/route.ts @@ -0,0 +1,42 @@ +import { NextResponse } from 'next/server'; +import { workerSessionManager, type WorkerSession } from '../../../../lib/worker-session-manager'; + +export const dynamic = 'force-dynamic'; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const projectRoot = searchParams.get('projectRoot'); + + if (!projectRoot) { + return NextResponse.json({ ok: false, error: 'projectRoot required' }); + } + + const workers = workerSessionManager.listWorkers(projectRoot); + + const instances = workers.map((w: WorkerSession) => ({ + id: w.agentInstanceId || w.id, + agentTypeId: w.agentTypeId || 'unknown', + displayName: w.displayName || `Worker ${w.id}`, + status: w.status, + currentBeadId: w.taskId, + startedAt: w.createdAt, + completedAt: w.completedAt, + result: w.result, + error: w.error, + })); + + const byType: Record = {}; + for (const w of workers) { + const typeId = w.agentTypeId || 'unknown'; + byType[typeId] = (byType[typeId] || 0) + 1; + } + + return NextResponse.json({ + ok: true, + status: { + totalActive: workers.filter((w: WorkerSession) => w.status === 'working' || w.status === 'spawning').length, + byType, + instances, + }, + }); +} diff --git a/src/app/api/runtime/bootstrap/route.ts b/src/app/api/runtime/bootstrap/route.ts new file mode 100644 index 0000000..c5c766d --- /dev/null +++ b/src/app/api/runtime/bootstrap/route.ts @@ -0,0 +1,25 @@ +import { NextResponse } from 'next/server'; +import { bootstrapManagedPi } from '../../../../lib/bb-pi-bootstrap'; + +export const dynamic = 'force-dynamic'; + +export async function POST(): Promise { + try { + const result = await bootstrapManagedPi(); + + return NextResponse.json({ + ok: true, + managedRoot: result.managedRoot, + sdkPath: result.sdkPath, + agentDir: result.agentDir, + alreadyInstalled: result.alreadyInstalled, + created: result.created, + }); + } catch (error) { + console.error('[Bootstrap API] Error:', error); + return NextResponse.json( + { ok: false, error: error instanceof Error ? error.message : 'Bootstrap failed' }, + { status: 500 } + ); + } +} diff --git a/src/app/api/runtime/events/route.ts b/src/app/api/runtime/events/route.ts new file mode 100644 index 0000000..edf33ea --- /dev/null +++ b/src/app/api/runtime/events/route.ts @@ -0,0 +1,16 @@ +import { NextResponse } from 'next/server'; +import { bbDaemon } from '../../../../lib/bb-daemon'; + +export const dynamic = 'force-dynamic'; + +export async function GET(request: Request): Promise { + const { searchParams } = new URL(request.url); + const projectRoot = searchParams.get('projectRoot'); + + if (!projectRoot) { + return NextResponse.json({ ok: false, error: 'projectRoot is required' }, { status: 400 }); + } + + await bbDaemon.ensureRunning(); + return NextResponse.json({ ok: true, lifecycle: bbDaemon.getLifecycle(), data: bbDaemon.listEvents(projectRoot) }); +} diff --git a/src/app/api/runtime/launch/route.ts b/src/app/api/runtime/launch/route.ts new file mode 100644 index 0000000..a63c480 --- /dev/null +++ b/src/app/api/runtime/launch/route.ts @@ -0,0 +1,55 @@ +import { NextResponse } from 'next/server'; +import { bbDaemon } from '../../../../lib/bb-daemon'; +import type { LaunchSurface } from '../../../../lib/embedded-runtime'; +import type { readIssuesFromDisk as ReadIssuesFromDisk } from '../../../../lib/read-issues'; + +export const dynamic = 'force-dynamic'; + +interface LaunchDeps { + readIssues?: typeof ReadIssuesFromDisk; +} + +function isLaunchSurface(value: string): value is LaunchSurface { + return ['social', 'graph', 'swarm', 'sessions', 'activity', 'task'].includes(value); +} + +export async function handleRuntimeLaunchPost(request: Request, deps: LaunchDeps = {}): Promise { + try { + const body = await request.json(); + const projectRoot = typeof body?.projectRoot === 'string' ? body.projectRoot.trim() : ''; + const taskId = typeof body?.taskId === 'string' ? body.taskId.trim() : ''; + const origin = typeof body?.origin === 'string' && isLaunchSurface(body.origin) ? body.origin : null; + const swarmId = typeof body?.swarmId === 'string' ? body.swarmId : null; + + if (!projectRoot || !taskId || !origin) { + return NextResponse.json({ ok: false, error: 'projectRoot, taskId, and origin are required' }, { status: 400 }); + } + + const read = deps.readIssues ?? (await import('../../../../lib/read-issues')).readIssuesFromDisk; + const issues = await read({ projectRoot, preferBd: true }); + const issue = issues.find((entry) => entry.id === taskId); + + if (!issue) { + return NextResponse.json({ ok: false, error: 'task not found' }, { status: 404 }); + } + + const lifecycle = await bbDaemon.ensureRunning(); + const result = await bbDaemon.launchFromIssue({ + projectRoot, + issue, + origin, + swarmId, + }); + + return NextResponse.json({ ok: true, lifecycle, data: result }); + } catch (error) { + return NextResponse.json( + { ok: false, error: error instanceof Error ? error.message : 'Invalid request' }, + { status: 400 }, + ); + } +} + +export async function POST(request: Request): Promise { + return handleRuntimeLaunchPost(request); +} diff --git a/src/app/api/runtime/orchestrator/route.ts b/src/app/api/runtime/orchestrator/route.ts new file mode 100644 index 0000000..67e19db --- /dev/null +++ b/src/app/api/runtime/orchestrator/route.ts @@ -0,0 +1,24 @@ +import { NextResponse } from 'next/server'; +import { bbDaemon } from '../../../../lib/bb-daemon'; + +export const dynamic = 'force-dynamic'; + +export async function POST(request: Request): Promise { + try { + const body = await request.json(); + const projectRoot = typeof body?.projectRoot === 'string' ? body.projectRoot.trim() : ''; + + if (!projectRoot) { + return NextResponse.json({ ok: false, error: 'projectRoot is required' }, { status: 400 }); + } + + const lifecycle = await bbDaemon.ensureRunning(); + const orchestrator = await bbDaemon.ensureOrchestrator(projectRoot); + return NextResponse.json({ ok: true, lifecycle, data: orchestrator }); + } catch (error) { + return NextResponse.json( + { ok: false, error: error instanceof Error ? error.message : 'Invalid request' }, + { status: 400 }, + ); + } +} diff --git a/src/app/api/runtime/prompt/route.ts b/src/app/api/runtime/prompt/route.ts new file mode 100644 index 0000000..a44f235 --- /dev/null +++ b/src/app/api/runtime/prompt/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from 'next/server'; +import { bbDaemon } from '../../../../lib/bb-daemon'; + +export const dynamic = 'force-dynamic'; + +export async function POST(request: Request): Promise { + try { + const body = await request.json(); + const projectRoot = typeof body?.projectRoot === 'string' ? body.projectRoot.trim() : ''; + const text = typeof body?.text === 'string' ? body.text.trim() : ''; + + if (!projectRoot || !text) { + return NextResponse.json({ ok: false, error: 'projectRoot and text are required' }, { status: 400 }); + } + + await bbDaemon.ensureRunning(); + + if (typeof (bbDaemon as any).prompt === 'function') { + void (bbDaemon as any).prompt(projectRoot, text); + } + + return NextResponse.json({ ok: true }); + } catch (error) { + return NextResponse.json( + { ok: false, error: error instanceof Error ? error.message : 'Invalid request' }, + { status: 400 }, + ); + } +} diff --git a/src/app/api/runtime/spawn/route.ts b/src/app/api/runtime/spawn/route.ts new file mode 100644 index 0000000..c449523 --- /dev/null +++ b/src/app/api/runtime/spawn/route.ts @@ -0,0 +1,49 @@ +// src/app/api/runtime/spawn/route.ts +import { NextResponse } from 'next/server'; +import { workerSessionManager } from '../../../../lib/worker-session-manager'; + +export async function POST(request: Request) { + try { + const body = await request.json(); + const { projectRoot, beadId, agentTypeId } = body; + + // Validate required fields + if (!projectRoot) { + return NextResponse.json( + { ok: false, error: 'projectRoot is required' }, + { status: 400 } + ); + } + if (!beadId) { + return NextResponse.json( + { ok: false, error: 'beadId is required' }, + { status: 400 } + ); + } + if (!agentTypeId) { + return NextResponse.json( + { ok: false, error: 'agentTypeId is required' }, + { status: 400 } + ); + } + + // Spawn worker via session manager + const worker = await workerSessionManager.spawnWorker({ + projectRoot, + taskId: beadId, + taskContext: `Work on ${beadId}`, + agentType: agentTypeId, + beadId, + }); + + return NextResponse.json({ + ok: true, + workerId: worker.id, + displayName: worker.displayName, + agentTypeId: worker.agentTypeId, + }); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return NextResponse.json({ ok: false, error: message }); + } +} diff --git a/src/app/api/runtime/status/route.ts b/src/app/api/runtime/status/route.ts new file mode 100644 index 0000000..1885a82 --- /dev/null +++ b/src/app/api/runtime/status/route.ts @@ -0,0 +1,8 @@ +import { NextResponse } from 'next/server'; +import { bbDaemon } from '../../../../lib/bb-daemon'; + +export const dynamic = 'force-dynamic'; + +export async function GET(): Promise { + return NextResponse.json(bbDaemon.getStatus()); +} diff --git a/src/app/api/runtime/stream/route.ts b/src/app/api/runtime/stream/route.ts new file mode 100644 index 0000000..ff5d837 --- /dev/null +++ b/src/app/api/runtime/stream/route.ts @@ -0,0 +1,80 @@ +import { bbDaemon } from '../../../../lib/bb-daemon'; + +export const dynamic = 'force-dynamic'; + +const encoder = new TextEncoder(); +const HEARTBEAT_MS = 15_000; +const POLL_MS = 250; + +function toRuntimeSseFrame(event: unknown): string { + return `event: runtime\ndata: ${JSON.stringify(event)}\n\n`; +} + +export async function GET(request: Request): Promise { + const { searchParams } = new URL(request.url); + const projectRoot = searchParams.get('projectRoot'); + + if (!projectRoot) { + return Response.json({ ok: false, error: 'projectRoot is required' }, { status: 400 }); + } + + await bbDaemon.start(); + + let cleanup = () => {}; + const stream = new ReadableStream({ + start(controller) { + let closed = false; + const write = (payload: string) => { + if (!closed) controller.enqueue(encoder.encode(payload)); + }; + + write(': connected\n\n'); + + const seenIds = new Set(); + const seed = bbDaemon.listEvents(projectRoot); + for (const event of seed) { + seenIds.add(event.id); + write(toRuntimeSseFrame(event)); + } + + const poll = setInterval(() => { + const current = bbDaemon.listEvents(projectRoot); + const unseen = current.filter((event) => !seenIds.has(event.id)); + for (const event of unseen.reverse()) { + seenIds.add(event.id); + write(toRuntimeSseFrame(event)); + } + }, POLL_MS); + + const heartbeat = setInterval(() => { + write(': heartbeat\n\n'); + }, HEARTBEAT_MS); + + const close = () => { + if (closed) return; + closed = true; + clearInterval(heartbeat); + clearInterval(poll); + request.signal.removeEventListener('abort', close); + try { + controller.close(); + } catch {} + }; + + cleanup = close; + request.signal.addEventListener('abort', close); + }, + cancel() { + cleanup(); + return Promise.resolve(); + }, + }); + + return new Response(stream, { + headers: { + 'Content-Type': 'text/event-stream; charset=utf-8', + 'Cache-Control': 'no-cache, no-transform', + Connection: 'keep-alive', + }, + }); +} diff --git a/src/app/api/runtime/worker-status/route.ts b/src/app/api/runtime/worker-status/route.ts new file mode 100644 index 0000000..1d4d871 --- /dev/null +++ b/src/app/api/runtime/worker-status/route.ts @@ -0,0 +1,37 @@ +// src/app/api/runtime/worker-status/route.ts +import { NextResponse } from 'next/server'; +import { workerSessionManager } from '../../../../lib/worker-session-manager'; + +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const beadId = searchParams.get('beadId'); + + if (!beadId) { + return NextResponse.json({ ok: false, error: 'beadId required' }, { status: 400 }); + } + + // Find worker for this bead + const workers = workerSessionManager.getAllWorkers(); + const worker = workers.find(w => w.beadId === beadId); + + if (!worker) { + return NextResponse.json({ + ok: true, + workerStatus: 'idle', + agentTypeId: null, + }); + } + + return NextResponse.json({ + ok: true, + workerStatus: worker.status, + workerDisplayName: worker.displayName, + workerError: worker.error, + agentTypeId: worker.agentTypeId, + }); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return NextResponse.json({ ok: false, error: message }); + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 575e93f..443ce27 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -15,8 +15,8 @@ export const metadata: Metadata = { export default function RootLayout({ children }: { children: ReactNode }) { return ( - - {children} + + {children} ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index 3bad47a..7f333e1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,8 +1,14 @@ import { Suspense } from 'react'; import { UnifiedShell } from '../components/shared/unified-shell'; +import { OnboardingWizard } from '../components/onboarding/onboarding-wizard'; import { readIssuesForScope } from '../lib/aggregate-read'; import { resolveProjectScope } from '../lib/project-scope'; import { listProjects } from '../lib/registry'; +import { bbDaemon } from '../lib/bb-daemon'; +import { detectPiRuntimeStrategy } from '../lib/pi-runtime-detection'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import os from 'node:os'; export const dynamic = 'force-dynamic'; @@ -10,10 +16,45 @@ interface PageProps { searchParams?: Promise>; } +async function checkOnboardingStatus(): Promise<{ + hasProjects: boolean; + piInstalled: boolean; + hasAuth: boolean; +}> { + // Check if any projects exist + const registryProjects = await listProjects(); + const hasProjects = registryProjects.length > 0; + + // Check if Pi is installed + const piResolution = await detectPiRuntimeStrategy(); + const piInstalled = piResolution.installState === 'ready'; + + // Check if auth exists + const authPath = path.join(os.homedir(), '.beadboard', 'runtime', 'pi', 'agent', 'auth.json'); + let hasAuth = false; + try { + const authContent = await fs.readFile(authPath, 'utf8'); + const auth = JSON.parse(authContent); + hasAuth = Object.keys(auth.providers || {}).length > 0; + } catch { + hasAuth = false; + } + + return { hasProjects, piInstalled, hasAuth }; +} + export default async function Page({ searchParams }: PageProps) { const params = (await searchParams) ?? {}; const requestedProjectKey = typeof params.project === 'string' ? params.project : null; const requestedMode = typeof params.mode === 'string' ? params.mode : null; + + // Check if we should skip onboarding (user has seen it or explicitly skipped) + const skipOnboarding = params.onboarded === 'true' || params.skip === 'true'; + + // Check onboarding status + const onboardingStatus = await checkOnboardingStatus(); + const needsOnboarding = !skipOnboarding && (!onboardingStatus.hasProjects || !onboardingStatus.piInstalled); + const registryProjects = await listProjects(); const scope = resolveProjectScope({ currentProjectRoot: process.cwd(), @@ -30,6 +71,22 @@ export default async function Page({ searchParams }: PageProps) { skipAgentFilter: true, }); + // Start daemon in background + void bbDaemon.ensureRunning(); + + // Show onboarding wizard if needed + if (needsOnboarding) { + return ( + + + + ); + } + return ( } } +function renderDaemonHelpText(): string { + return [ + 'Usage: bb daemon [options]', + '', + 'Commands:', + ' start Start the BeadBoard daemon lifecycle', + ' status Show daemon lifecycle and project status', + ' stop Stop the BeadBoard daemon lifecycle', + ' bootstrap Install the BeadBoard agent runtime', + ' tui Open the interactive BeadBoard agent TUI', + '', + 'TUI options:', + ' --project-root Run the bb agent in an explicit external project workspace', + ' --project-key Run the bb agent in a registered BeadBoard project workspace', + ].join('\n'); +} + +async function runDaemonCli(argv: string[], asJson: boolean): Promise { + const subcommand = argv[0]; + const projectRootFlagIndex = argv.indexOf('--project-root'); + const projectKeyFlagIndex = argv.indexOf('--project-key'); + const projectRoot = projectRootFlagIndex >= 0 ? argv[projectRootFlagIndex + 1] : undefined; + const projectKey = projectKeyFlagIndex >= 0 ? argv[projectKeyFlagIndex + 1] : undefined; + if (!subcommand || subcommand === '--help' || subcommand === '-h' || subcommand === 'help') { + return { ok: true, command: 'daemon help', text: renderDaemonHelpText() }; + } + + if (subcommand === 'start') { + const lifecycle = await bbDaemon.start(); + const status = bbDaemon.getStatus(); + const runtimeMode = status.piRuntime ? `${status.piRuntime.mode} / ${status.piRuntime.installState}` : 'unknown'; + return { + ok: true, + command: 'daemon start', + text: `✓ BeadBoard daemon started (${lifecycle.status}) using ${runtimeMode}`, + lifecycle, + status, + }; + } + + if (subcommand === 'status') { + const status = bbDaemon.getStatus(); + const runtimeMode = status.piRuntime ? `${status.piRuntime.mode} / ${status.piRuntime.installState}` : 'unknown'; + return { + ok: true, + command: 'daemon status', + text: `Daemon: ${status.lifecycle.status} (${status.daemon.projectCount} projects) · Agent runtime: ${runtimeMode}`, + status, + }; + } + + if (subcommand === 'stop') { + const lifecycle = await bbDaemon.stop(); + return { + ok: true, + command: 'daemon stop', + text: `✓ BeadBoard daemon stopped (${lifecycle.status})`, + lifecycle, + status: bbDaemon.getStatus(), + }; + } + + if (subcommand === 'bootstrap' || subcommand === 'bootstrap-pi') { + const result = await bootstrapManagedPi(); + return { + ok: true, + command: 'daemon bootstrap', + text: result.alreadyInstalled + ? `✓ BeadBoard agent runtime ready at ${result.managedRoot}` + : `✓ BeadBoard agent runtime installed at ${result.managedRoot}`, + bootstrap: result, + }; + } + + if (subcommand === 'tui') { + await bbDaemon.ensureRunning(); + return { + ok: true, + command: 'daemon tui', + text: 'Starting interactive BeadBoard agent TUI...', + preview: renderDaemonTuiText(), + status: bbDaemon.getStatus(), + planned: false, + interactive: true, + projectRoot: projectRoot ?? null, + projectKey: projectKey ?? null, + }; + } + + const error = { code: 'CLI_ERROR', message: `Unknown daemon command: ${subcommand}` }; + if (asJson) { + return { ok: false, command: `daemon ${subcommand}`, error }; + } + return { ok: false, command: `daemon ${subcommand}`, text: `Error: ${error.message}`, error }; +} + function parseVersion(env: NodeJS.ProcessEnv): string { const raw = (env.BB_RUNTIME_VERSION || env.npm_package_version || '0.1.0').trim(); return raw.startsWith('v') ? raw.slice(1) : raw; @@ -297,6 +397,11 @@ export async function runCli(argv: string[], env: NodeJS.ProcessEnv = process.en return runAgentCli(subArgs, asJson); } + if (command === 'daemon') { + const subArgs = commandIndex >= 0 ? args.slice(commandIndex + 1) : []; + return runDaemonCli(subArgs, asJson); + } + if (command === 'doctor') { return { ok: true, @@ -343,7 +448,8 @@ export async function runCli(argv: string[], env: NodeJS.ProcessEnv = process.en return { ok: true, command: 'help', - usage: 'beadboard [--json] [--yes]', + usage: 'beadboard [--json] [--yes]', + text: renderHelpText(), }; } @@ -356,6 +462,7 @@ function renderHelpText(): string { ' beadboard start [--dolt] Start BeadBoard runtime (optionally start Dolt first)', ' beadboard open Open BeadBoard in browser', ' beadboard status [--json] Show runtime + bd diagnostics', + ' beadboard daemon Control the BeadBoard daemon lifecycle', ' beadboard agent Run coordination commands (register/send/inbox/ack/reserve/...)', '', 'Management Commands:', @@ -371,6 +478,24 @@ function renderHelpText(): string { async function main() { const argv = process.argv.slice(2); const asJson = argv.includes('--json'); + const isDaemonTui = argv[0] === 'daemon' && argv[1] === 'tui' && !asJson; + + if (isDaemonTui) { + const projectRootFlagIndex = argv.indexOf('--project-root'); + const projectKeyFlagIndex = argv.indexOf('--project-key'); + const projectRoot = projectRootFlagIndex >= 0 ? argv[projectRootFlagIndex + 1] : undefined; + const projectKey = projectKeyFlagIndex >= 0 ? argv[projectKeyFlagIndex + 1] : undefined; + + await bbDaemon.ensureRunning(); + await runBbAgentTui({ + cwd: process.cwd(), + projectRoot, + projectKey, + testMode: process.env.BB_TUI_TEST_MODE === '1', + }); + return; + } + const result = await runCli(argv); if (!asJson && result.command === 'help') { process.stdout.write(`${renderHelpText()}\n`); diff --git a/src/components/activity/activity-panel.tsx b/src/components/activity/activity-panel.tsx index ccf077e..c772967 100644 --- a/src/components/activity/activity-panel.tsx +++ b/src/components/activity/activity-panel.tsx @@ -24,24 +24,24 @@ export type EventTone = { idClass: string; }; -interface AgentRosterEntry { - name: string; - status: AgentStatus; - lastSeen: string | null; - beadId: string; -} - -interface CoordMessage { - message_id: string; - bead_id: string; - from_agent: string; - to_agent: string; - category: 'HANDOFF' | 'BLOCKED' | 'DECISION' | 'INFO'; - subject: string; - state: 'unread' | 'read' | 'acked'; - created_at: string; - acked_at: string | null; -} +interface AgentRosterEntry { + name: string; + status: AgentStatus; + lastSeen: string | null; + beadId: string; +} + +interface CoordMessage { + message_id: string; + bead_id: string; + from_agent: string; + to_agent: string; + category: 'HANDOFF' | 'BLOCKED' | 'DECISION' | 'INFO'; + subject: string; + state: 'unread' | 'read' | 'acked'; + created_at: string; + acked_at: string | null; +} interface ActivityPanelProps { issues: BeadIssue[]; @@ -51,6 +51,21 @@ interface ActivityPanelProps { const AGENT_LABEL = 'gt:agent'; +function mergeUniqueActivities(existing: ActivityEvent[], incoming: ActivityEvent[]): ActivityEvent[] { + const seen = new Set(); + const merged: ActivityEvent[] = []; + + for (const event of [...incoming, ...existing]) { + if (seen.has(event.id)) continue; + seen.add(event.id); + merged.push(event); + } + + return merged + .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()) + .slice(0, 50); +} + // Determine agent status based on last activity function deriveAgentStatus(lastSeenAt: string | null): AgentStatus { if (!lastSeenAt) return 'dead'; @@ -152,23 +167,23 @@ function getAgentTone(status: AgentStatus): AgentTone { } // reopened=blue, closed=amber, created/opened=green, others semantic -export function getEventTone(kind: string): EventTone { - const normalized = kind.toLowerCase(); - const byKind: Record = { - coord_send: { - label: 'Coord Send', - labelClass: 'text-[#D4A574]', - dotClass: 'bg-[#D4A574]', - cardClass: 'bg-[var(--status-in-progress)]', - idClass: 'text-[#DAB891]', - }, - coord_ack: { - label: 'Coord Ack', - labelClass: 'text-[#7CB97A]', - dotClass: 'bg-[#7CB97A]', - cardClass: 'bg-[var(--status-ready)]', - idClass: 'text-[#9ACB98]', - }, +export function getEventTone(kind: string): EventTone { + const normalized = kind.toLowerCase(); + const byKind: Record = { + coord_send: { + label: 'Coord Send', + labelClass: 'text-[#D4A574]', + dotClass: 'bg-[#D4A574]', + cardClass: 'bg-[var(--status-in-progress)]', + idClass: 'text-[#DAB891]', + }, + coord_ack: { + label: 'Coord Ack', + labelClass: 'text-[#7CB97A]', + dotClass: 'bg-[#7CB97A]', + cardClass: 'bg-[var(--status-ready)]', + idClass: 'text-[#9ACB98]', + }, created: { label: 'Created', labelClass: 'text-[#7CB97A]', @@ -270,95 +285,95 @@ export function getInitials(name: string): string { return name.split(/[-_\s]/).map(p => p[0]).join('').toUpperCase().slice(0, 2); } -export function ActivityPanel({ issues, collapsed = false, projectRoot }: ActivityPanelProps) { - const [activities, setActivities] = useState([]); - const [coordActivities, setCoordActivities] = useState([]); - const [reservationByAgent, setReservationByAgent] = useState>({}); - const [isLoading, setIsLoading] = useState(true); +export function ActivityPanel({ issues, collapsed = false, projectRoot }: ActivityPanelProps) { + const [activities, setActivities] = useState([]); + const [coordActivities, setCoordActivities] = useState([]); + const [reservationByAgent, setReservationByAgent] = useState>({}); + const [isLoading, setIsLoading] = useState(true); const agentRoster = useMemo(() => buildAgentRoster(issues), [issues]); - // Fetch activity history - useEffect(() => { - async function fetchActivity() { + // Fetch activity history + useEffect(() => { + async function fetchActivity() { try { const response = await fetch('/api/activity'); if (response.ok) { const data = await response.json(); - setActivities(data.slice(0, 50)); // Limit to 50 events - } - } catch (error) { - console.error('[ActivityPanel] Failed to fetch activity:', error); - } finally { - setIsLoading(false); - } - } - - fetchActivity(); - }, []); - - useEffect(() => { - const fetchCoordination = async () => { - if (agentRoster.length === 0) { - setCoordActivities([]); - setReservationByAgent({}); - return; - } - - // Use batch endpoints to reduce API calls from 2N to 2 - const agentNames = agentRoster.map(a => a.name).join(','); - - const [mailResponse, reservationsResponse] = await Promise.all([ - fetch(`/api/agents/mail/batch?agents=${encodeURIComponent(agentNames)}&limit=15`), - fetch(`/api/agents/reservations/batch?agents=${encodeURIComponent(agentNames)}`), - ]); - - const mailPayload = await mailResponse.json().catch(() => ({ ok: false, data: [] })); - const reservationsPayload = await reservationsResponse.json().catch(() => ({ ok: false, data: [] })); - - // Collect all messages from all agents - const uniqueMessages = new Map(); - if (mailPayload.ok && mailPayload.data) { - for (const entry of mailPayload.data) { - for (const message of (entry.messages ?? [])) { - uniqueMessages.set(message.message_id, message); - } - } - } - - const mapped = [...uniqueMessages.values()] - .map((message) => ({ - id: `coord-${message.message_id}`, - kind: (message.state === 'acked' ? 'coord_ack' : 'coord_send') as ActivityEvent['kind'], - beadId: message.bead_id, - beadTitle: `${message.category}: ${message.subject}`, - timestamp: message.state === 'acked' && message.acked_at ? message.acked_at : message.created_at, - actor: message.state === 'acked' ? message.to_agent : message.from_agent, - projectId: projectRoot, - projectName: 'beadboard', - payload: {}, - })) - .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()) - .slice(0, 25); - - // Build reservation map - const reservationMap: Record = {}; - if (reservationsPayload.ok && reservationsPayload.data) { - for (const entry of reservationsPayload.data) { - reservationMap[entry.agent] = entry.scope; - } - } - - setCoordActivities(mapped); - setReservationByAgent(reservationMap); - }; - - void fetchCoordination(); - const timer = setInterval(() => { - void fetchCoordination(); - }, 15000); - return () => clearInterval(timer); - }, [agentRoster, projectRoot]); + setActivities((prev) => mergeUniqueActivities(prev, data)); + } + } catch (error) { + console.error('[ActivityPanel] Failed to fetch activity:', error); + } finally { + setIsLoading(false); + } + } + + fetchActivity(); + }, []); + + useEffect(() => { + const fetchCoordination = async () => { + if (agentRoster.length === 0) { + setCoordActivities([]); + setReservationByAgent({}); + return; + } + + // Use batch endpoints to reduce API calls from 2N to 2 + const agentNames = agentRoster.map(a => a.name).join(','); + + const [mailResponse, reservationsResponse] = await Promise.all([ + fetch(`/api/agents/mail/batch?agents=${encodeURIComponent(agentNames)}&limit=15`), + fetch(`/api/agents/reservations/batch?agents=${encodeURIComponent(agentNames)}`), + ]); + + const mailPayload = await mailResponse.json().catch(() => ({ ok: false, data: [] })); + const reservationsPayload = await reservationsResponse.json().catch(() => ({ ok: false, data: [] })); + + // Collect all messages from all agents + const uniqueMessages = new Map(); + if (mailPayload.ok && mailPayload.data) { + for (const entry of mailPayload.data) { + for (const message of (entry.messages ?? [])) { + uniqueMessages.set(message.message_id, message); + } + } + } + + const mapped = [...uniqueMessages.values()] + .map((message) => ({ + id: `coord-${message.message_id}`, + kind: (message.state === 'acked' ? 'coord_ack' : 'coord_send') as ActivityEvent['kind'], + beadId: message.bead_id, + beadTitle: `${message.category}: ${message.subject}`, + timestamp: message.state === 'acked' && message.acked_at ? message.acked_at : message.created_at, + actor: message.state === 'acked' ? message.to_agent : message.from_agent, + projectId: projectRoot, + projectName: 'beadboard', + payload: {}, + })) + .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()) + .slice(0, 25); + + // Build reservation map + const reservationMap: Record = {}; + if (reservationsPayload.ok && reservationsPayload.data) { + for (const entry of reservationsPayload.data) { + reservationMap[entry.agent] = entry.scope; + } + } + + setCoordActivities(mapped); + setReservationByAgent(reservationMap); + }; + + void fetchCoordination(); + const timer = setInterval(() => { + void fetchCoordination(); + }, 15000); + return () => clearInterval(timer); + }, [agentRoster, projectRoot]); // Subscribe to real-time activity useEffect(() => { @@ -371,7 +386,7 @@ export function ActivityPanel({ issues, collapsed = false, projectRoot }: Activi console.log('[ActivityPanel] Received activity event:', data); // data IS the activity event directly (not wrapped in { event: ... }) if (data?.beadId) { - setActivities(prev => [data, ...prev].slice(0, 50)); + setActivities(prev => mergeUniqueActivities(prev, [data])); } } catch (e) { // Ignore parse errors @@ -387,13 +402,11 @@ export function ActivityPanel({ issues, collapsed = false, projectRoot }: Activi }; }, [projectRoot]); - const activeAgents = agentRoster.filter(a => a.status === 'active').length; - const mergedActivities = useMemo( - () => [...coordActivities, ...activities] - .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()) - .slice(0, 50), - [activities, coordActivities], - ); + const activeAgents = agentRoster.filter(a => a.status === 'active').length; + const mergedActivities = useMemo( + () => mergeUniqueActivities(coordActivities, activities), + [activities, coordActivities], + ); if (collapsed) { return (
@@ -425,7 +438,7 @@ export function ActivityPanel({ issues, collapsed = false, projectRoot }: Activi {/* Activity Pulses */}
- {mergedActivities.slice(0, 8).map((act) => ( + {mergedActivities.slice(0, 8).map((act) => (
{agent.name}
- - {agent.status} - - {reservationByAgent[agent.name] ? ( - - {reservationByAgent[agent.name]} - - ) : null} - - {agent.lastSeen ? formatRelativeTime(agent.lastSeen) : 'N/A'} - + + {agent.status} + + {reservationByAgent[agent.name] ? ( + + {reservationByAgent[agent.name]} + + ) : null} + + {agent.lastSeen ? formatRelativeTime(agent.lastSeen) : 'N/A'} +
@@ -508,13 +521,13 @@ export function ActivityPanel({ issues, collapsed = false, projectRoot }: Activi
SYNCING...
- ) : mergedActivities.length === 0 ? ( + ) : mergedActivities.length === 0 ? (

VOID_STREAM_NULL

) : (
- {mergedActivities.map((activity) => { + {mergedActivities.map((activity) => { const eventTone = getEventTone(activity.kind); return (
diff --git a/src/components/activity/contextual-right-panel.tsx b/src/components/activity/contextual-right-panel.tsx index 2108555..9b8a216 100644 --- a/src/components/activity/contextual-right-panel.tsx +++ b/src/components/activity/contextual-right-panel.tsx @@ -7,6 +7,7 @@ import { ActivityPanel } from './activity-panel'; import { SwarmCommandFeed } from './swarm-command-feed'; import { ThreadDrawer } from '../shared/thread-drawer'; import { MissionInspector } from '../mission/mission-inspector'; +import { AgentStatusPanel } from '../agents/agent-status-panel'; import { useSwarmList } from '../../hooks/use-swarm-list'; import { useUrlState } from '../../hooks/use-url-state'; @@ -58,6 +59,10 @@ export function ContextualRightPanel({ epicId, taskId, swarmId, issues, projectR
)} + {/* Agent Status for active epic */} +
+ +
setSwarmId(null)} - onAssign={async () => {}} - /> +
+ {/* Agent Status for active swarm */} +
+ +
+
+ setSwarmId(null)} + onAssign={async () => {}} + /> +
+
); } diff --git a/src/components/agents/agent-action-row.tsx b/src/components/agents/agent-action-row.tsx new file mode 100644 index 0000000..39b3a48 --- /dev/null +++ b/src/components/agents/agent-action-row.tsx @@ -0,0 +1,83 @@ +// src/components/agents/agent-action-row.tsx +'use client'; + +import { AgentAssignButton } from './agent-assign-button'; +import { AgentSpawnButton } from './agent-spawn-button'; +import { useAgentStatus, useSpawnAgent } from './hooks'; +import type { AgentArchetype } from '../../lib/types-swarm'; + +export interface AgentActionRowProps { + beadId: string; + beadStatus: string; + agents: AgentArchetype[]; + projectRoot: string; + currentAgentTypeId?: string; + onAgentAssigned?: (agentTypeId: string) => void; + onAgentSpawned?: (workerId: string, displayName: string) => void; + size?: 'sm' | 'md'; +} + +export function AgentActionRow({ + beadId, + beadStatus, + agents, + projectRoot, + currentAgentTypeId, + onAgentAssigned, + onAgentSpawned, + size = 'sm', +}: AgentActionRowProps) { + const { workerStatus, workerDisplayName, workerError } = useAgentStatus(beadId); + const { spawn, isSpawning } = useSpawnAgent(projectRoot); + + const handleAssign = async (agentTypeId: string) => { + // Call API to assign agent type to bead + try { + const response = await fetch('/api/beads/assign-agent', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ beadId, agentTypeId }), + }); + if (response.ok && onAgentAssigned) { + onAgentAssigned(agentTypeId); + } + } catch (error) { + console.error('Failed to assign agent:', error); + } + }; + + const handleSpawn = async () => { + if (!currentAgentTypeId) return; + + const result = await spawn(beadId, currentAgentTypeId); + if (result.success && onAgentSpawned) { + onAgentSpawned(result.workerId!, result.displayName!); + } + }; + + // Don't show for closed beads + if (beadStatus === 'closed') { + return null; + } + + return ( +
+ + +
+ ); +} diff --git a/src/components/agents/agent-assign-button.tsx b/src/components/agents/agent-assign-button.tsx new file mode 100644 index 0000000..f468386 --- /dev/null +++ b/src/components/agents/agent-assign-button.tsx @@ -0,0 +1,79 @@ +// src/components/agents/agent-assign-button.tsx +'use client'; + +import { useState } from 'react'; +import { UserPlus } from 'lucide-react'; +import { AgentPickerPopup } from './agent-picker-popup'; +import type { AgentArchetype } from '../../lib/types-swarm'; + +export interface AgentAssignButtonProps { + beadId: string; + agents: AgentArchetype[]; + currentAgentTypeId?: string; + onAssign: (agentTypeId: string) => void; + size?: 'sm' | 'md'; + disabled?: boolean; +} + +export function AgentAssignButton({ + beadId, + agents, + currentAgentTypeId, + onAssign, + size = 'sm', + disabled = false, +}: AgentAssignButtonProps) { + const [isOpen, setIsOpen] = useState(false); + + const sizeClasses = size === 'sm' + ? 'h-6 w-6' + : 'h-7 w-7'; + + const iconSize = size === 'sm' + ? 'w-3 h-3' + : 'w-3.5 h-3.5'; + + const isAssigned = !!currentAgentTypeId; + const assignedAgent = agents.find(a => a.id === currentAgentTypeId); + const bgColor = isAssigned && assignedAgent + ? `${assignedAgent.color}30` + : 'var(--surface-tertiary)'; + const iconColor = isAssigned && assignedAgent + ? assignedAgent.color + : 'var(--text-tertiary)'; + + return ( +
+ + + setIsOpen(false)} + agents={agents} + selectedAgentId={currentAgentTypeId} + onSelect={(agentId) => { + onAssign(agentId); + setIsOpen(false); + }} + /> +
+ ); +} diff --git a/src/components/agents/agent-picker-popup.tsx b/src/components/agents/agent-picker-popup.tsx new file mode 100644 index 0000000..28f79f5 --- /dev/null +++ b/src/components/agents/agent-picker-popup.tsx @@ -0,0 +1,120 @@ +// src/components/agents/agent-picker-popup.tsx +'use client'; + +import { useEffect, useRef } from 'react'; +import { Rocket, Brain, Wrench, Search, CheckCircle, FlaskConical, Upload } from 'lucide-react'; +import type { AgentArchetype } from '../../lib/types-swarm'; + +export interface AgentPickerPopupProps { + isOpen: boolean; + onClose: () => void; + agents: AgentArchetype[]; + selectedAgentId?: string; + onSelect: (agentId: string) => void; + onSpawn?: (agentId: string) => void; + position?: { x: number; y: number }; +} + +const AGENT_ICONS: Record = { + architect: , + engineer: , + investigator: , + reviewer: , + tester: , + shipper: , +}; + +export function AgentPickerPopup({ + isOpen, + onClose, + agents, + selectedAgentId, + onSelect, + onSpawn, + position, +}: AgentPickerPopupProps) { + const ref = useRef(null); + + useEffect(() => { + const handleClickOutside = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + onClose(); + } + }; + if (isOpen) { + document.addEventListener('mousedown', handleClickOutside); + } + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [isOpen, onClose]); + + if (!isOpen) return null; + + const style = position + ? { position: 'absolute' as const, left: position.x, top: position.y + 8 } + : {}; + + return ( +
+ {/* Orchestrator option */} + + +
+ + {/* Agent types */} + {agents.map((agent) => ( + + ))} + + {/* Spawn button */} + {onSpawn && selectedAgentId && ( + <> +
+ + + )} +
+ ); +} diff --git a/src/components/agents/agent-spawn-button.tsx b/src/components/agents/agent-spawn-button.tsx new file mode 100644 index 0000000..1e57ce3 --- /dev/null +++ b/src/components/agents/agent-spawn-button.tsx @@ -0,0 +1,132 @@ +// src/components/agents/agent-spawn-button.tsx +'use client'; + +import { Rocket, CheckCircle, AlertCircle, Loader2 } from 'lucide-react'; +import type { WorkerStatus } from './hooks/use-agent-status'; + +export interface AgentSpawnButtonProps { + beadId: string; + agentTypeId?: string; + workerStatus: WorkerStatus; + workerDisplayName?: string; + workerError?: string; + onSpawn: () => void; + size?: 'sm' | 'md'; + disabled?: boolean; +} + +const STATUS_CONFIG: Record = { + idle: { + icon: , + color: '#6b7280', + bgColor: 'rgba(107, 114, 128, 0.1)', + borderColor: 'rgba(107, 114, 128, 0.3)', + title: 'No agent assigned', + }, + spawning: { + icon: , + color: '#3b82f6', + bgColor: 'rgba(59, 130, 246, 0.1)', + borderColor: 'rgba(59, 130, 246, 0.3)', + title: 'Spawning...', + pulsing: true, + }, + working: { + icon: , + color: '#22c55e', + bgColor: 'rgba(34, 197, 94, 0.1)', + borderColor: 'rgba(34, 197, 94, 0.3)', + title: 'Working', + pulsing: true, + }, + blocked: { + icon: , + color: '#ef4444', + bgColor: 'rgba(239, 68, 68, 0.1)', + borderColor: 'rgba(239, 68, 68, 0.3)', + title: 'Blocked', + }, + completed: { + icon: , + color: '#22c55e', + bgColor: 'rgba(34, 197, 94, 0.1)', + borderColor: 'rgba(34, 197, 94, 0.3)', + title: 'Completed', + }, + failed: { + icon: , + color: '#ef4444', + bgColor: 'rgba(239, 68, 68, 0.1)', + borderColor: 'rgba(239, 68, 68, 0.3)', + title: 'Failed', + }, +}; + +export function AgentSpawnButton({ + beadId, + agentTypeId, + workerStatus, + workerDisplayName, + workerError, + onSpawn, + size = 'sm', + disabled = false, +}: AgentSpawnButtonProps) { + const config = STATUS_CONFIG[workerStatus]; + const sizeClasses = size === 'sm' ? 'h-6 w-6' : 'h-7 w-7'; + + // No agent assigned - don't show button + if (!agentTypeId && workerStatus === 'idle') { + return null; + } + + const canSpawn = workerStatus === 'idle' && agentTypeId; + const showTooltip = workerStatus === 'working' || workerStatus === 'blocked' || workerStatus === 'completed'; + + return ( +
+ + + {/* Tooltip for active workers */} + {showTooltip && ( +
+
+

+ {workerDisplayName || 'Agent'} +

+

+ {workerStatus} +

+ {workerError && ( +

+ {workerError} +

+ )} +
+
+ )} +
+ ); +} diff --git a/src/components/agents/agent-status-panel.tsx b/src/components/agents/agent-status-panel.tsx new file mode 100644 index 0000000..94baa0d --- /dev/null +++ b/src/components/agents/agent-status-panel.tsx @@ -0,0 +1,158 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import type { AgentInstance, AgentStatus } from '../../lib/agent-instance'; +import { Activity, CheckCircle, XCircle, Loader2, Users } from 'lucide-react'; + +interface AgentStatusPanelProps { + projectRoot: string; +} + +export function AgentStatusPanel({ projectRoot }: AgentStatusPanelProps) { + const [status, setStatus] = useState(null); + const [history, setHistory] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + // Poll for agent status updates + const poll = async () => { + try { + const [statusRes, historyRes] = await Promise.all([ + fetch(`/api/runtime/agents?projectRoot=${encodeURIComponent(projectRoot)}`), + fetch(`/api/runtime/agents/history?projectRoot=${encodeURIComponent(projectRoot)}`), + ]); + const statusData = await statusRes.json(); + const historyData = await historyRes.json(); + + if (statusData.ok) setStatus(statusData.status); + if (historyData.ok) setHistory(historyData.instances); + } catch (error) { + console.error('Failed to fetch agent status:', error); + } finally { + setLoading(false); + } + }; + + poll(); + const interval = setInterval(poll, 2000); + return () => clearInterval(interval); + }, [projectRoot]); + + if (loading) { + return ( +
+ +
+ ); + } + + return ( +
+ {/* Active Agents Section */} +
+

+ + Active Agents ({status?.totalActive || 0}) +

+ + {!status || status.instances.length === 0 ? ( +

+ No active agents. Spawn an agent to work on a task. +

+ ) : ( +
+ {status.instances.map(instance => ( + + ))} +
+ )} +
+ + {/* Recent Completions Section */} + {history.length > 0 && ( +
+

+ + Recent Completions +

+
+ {history.slice(0, 10).map(instance => ( + + ))} +
+
+ )} + + {/* Summary by Type */} + {status && Object.keys(status.byType).length > 0 && ( +
+
+ By Type +
+
+ {Object.entries(status.byType).map(([type, count]) => ( +
+ {type} + {count} +
+ ))} +
+
+ )} +
+ ); +} + +function AgentInstanceCard({ instance }: { instance: AgentInstance }) { + const statusConfig = { + spawning: { color: 'bg-yellow-500', icon: Loader2, animate: true }, + working: { color: 'bg-cyan-500', icon: Activity, animate: false }, + idle: { color: 'bg-gray-500', icon: Activity, animate: false }, + completed: { color: 'bg-green-500', icon: CheckCircle, animate: false }, + failed: { color: 'bg-red-500', icon: XCircle, animate: false }, + }; + + const config = statusConfig[instance.status]; + const Icon = config.icon; + + return ( +
+
+
+
+ {instance.displayName} + + {instance.status} + +
+ {instance.currentBeadId && ( +
+ → {instance.currentBeadId} +
+ )} +
+ +
+ ); +} + +function AgentHistoryItem({ instance }: { instance: AgentInstance }) { + const isSuccess = instance.status === 'completed'; + + return ( +
+ {isSuccess ? ( + + ) : ( + + )} + {instance.displayName} + {instance.currentBeadId && ( + → {instance.currentBeadId} + )} +
+ ); +} diff --git a/src/components/agents/hooks/index.ts b/src/components/agents/hooks/index.ts new file mode 100644 index 0000000..2677fcb --- /dev/null +++ b/src/components/agents/hooks/index.ts @@ -0,0 +1,3 @@ +// src/components/agents/hooks/index.ts +export { useAgentStatus, type AgentStatus, type WorkerStatus } from './use-agent-status'; +export { useSpawnAgent, type SpawnResult } from './use-spawn-agent'; diff --git a/src/components/agents/hooks/use-agent-status.ts b/src/components/agents/hooks/use-agent-status.ts new file mode 100644 index 0000000..f32fde0 --- /dev/null +++ b/src/components/agents/hooks/use-agent-status.ts @@ -0,0 +1,68 @@ +// src/components/agents/hooks/use-agent-status.ts +import { useState, useEffect, useRef } from 'react'; + +export type WorkerStatus = 'idle' | 'spawning' | 'working' | 'blocked' | 'completed' | 'failed'; + +export interface AgentStatus { + agentTypeId?: string; + workerStatus: WorkerStatus; + workerDisplayName?: string; + workerError?: string; + isLoading: boolean; +} + +const POLL_INTERVAL_MS = 5000; + +export function useAgentStatus(beadId: string): AgentStatus { + const [status, setStatus] = useState({ + workerStatus: 'idle', + isLoading: true, + }); + const intervalRef = useRef(null); + + const fetchStatus = async () => { + if (!beadId) return; + + try { + const response = await fetch(`/api/runtime/worker-status?beadId=${encodeURIComponent(beadId)}`); + if (!response.ok) { + // If API returns 404 or error, no worker exists yet + setStatus({ workerStatus: 'idle', isLoading: false }); + return; + } + + const data = await response.json(); + setStatus({ + workerStatus: data.status || 'idle', + workerDisplayName: data.displayName, + workerError: data.error, + agentTypeId: data.agentTypeId, + isLoading: false, + }); + } catch (error) { + console.error('Failed to fetch worker status:', error); + setStatus({ workerStatus: 'idle', isLoading: false }); + } + }; + + useEffect(() => { + if (!beadId) { + setStatus({ workerStatus: 'idle', isLoading: false }); + return; + } + + // Initial fetch + fetchStatus(); + + // Set up polling + intervalRef.current = setInterval(fetchStatus, POLL_INTERVAL_MS); + + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + }; + }, [beadId]); + + return status; +} diff --git a/src/components/agents/hooks/use-spawn-agent.ts b/src/components/agents/hooks/use-spawn-agent.ts new file mode 100644 index 0000000..e4f4a51 --- /dev/null +++ b/src/components/agents/hooks/use-spawn-agent.ts @@ -0,0 +1,41 @@ +// src/components/agents/hooks/use-spawn-agent.ts +import { useState } from 'react'; + +export interface SpawnResult { + success: boolean; + workerId?: string; + displayName?: string; + error?: string; +} + +export function useSpawnAgent(projectRoot: string) { + const [isSpawning, setIsSpawning] = useState(false); + + const spawn = async (beadId: string, agentTypeId: string): Promise => { + setIsSpawning(true); + try { + const response = await fetch('/api/runtime/spawn', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ projectRoot, beadId, agentTypeId }), + }); + const data = await response.json(); + + if (!data.ok) { + return { success: false, error: data.error }; + } + + return { + success: true, + workerId: data.workerId, + displayName: data.displayName, + }; + } catch (error) { + return { success: false, error: String(error) }; + } finally { + setIsSpawning(false); + } + }; + + return { spawn, isSpawning }; +} diff --git a/src/components/agents/index.ts b/src/components/agents/index.ts new file mode 100644 index 0000000..cc8f339 --- /dev/null +++ b/src/components/agents/index.ts @@ -0,0 +1,6 @@ +// src/components/agents/index.ts +export { AgentActionRow, type AgentActionRowProps } from './agent-action-row'; +export { AgentAssignButton, type AgentAssignButtonProps } from './agent-assign-button'; +export { AgentSpawnButton, type AgentSpawnButtonProps } from './agent-spawn-button'; +export { AgentPickerPopup, type AgentPickerPopupProps } from './agent-picker-popup'; +export * from './hooks'; diff --git a/src/components/graph/assignment-panel.tsx b/src/components/graph/assignment-panel.tsx index 441e4e4..ef78544 100644 --- a/src/components/graph/assignment-panel.tsx +++ b/src/components/graph/assignment-panel.tsx @@ -2,9 +2,9 @@ import React, { useState, useMemo } from 'react'; import { Zap, Users, FileCode2, Loader2, UserPlus, Clock, AlertCircle, ChevronDown, ChevronRight, Blocks, Layers } from 'lucide-react'; -import { ArchetypeInspector } from '../swarm/archetype-inspector'; +import { AgentInspector } from '../swarm/agent-inspector'; import { TemplateInspector } from '../swarm/template-inspector'; -import { ArchetypePicker } from '../swarm/archetype-picker'; +import { AgentPicker } from '../swarm/agent-picker'; import { TemplatePicker } from '../swarm/template-picker'; import { useArchetypes } from '../../hooks/use-archetypes'; import { useTemplates } from '../../hooks/use-templates'; @@ -219,8 +219,8 @@ export function AssignmentPanel({ selectedIssue, projectRoot, issues, epicId, on }); }; - const getArchetypeCountInTeam = (template: SwarmTemplate, archetypeId: string): number => { - return template.team.filter(member => member.archetypeId === archetypeId).length; + const getArchetypeCountInTeam = (template: SwarmTemplate, agentTypeId: string): number => { + return template.team.filter(member => member.agentTypeId === agentTypeId).length; }; const renderTaskItem = (issue: BeadIssue, showAssignButton: boolean = false, archetypeBadges: AgentArchetype[] = []) => ( @@ -290,7 +290,7 @@ export function AssignmentPanel({ selectedIssue, projectRoot, issues, epicId, on
- setShowArchetypeList(false)} @@ -361,12 +361,12 @@ export function AssignmentPanel({ selectedIssue, projectRoot, issues, epicId, on
Team Roster
- {Array.from(new Set(epicTemplate.team.map(m => m.archetypeId))).map(archetypeId => { - const archetype = archetypes.find((a: AgentArchetype) => a.id === archetypeId); - const count = getArchetypeCountInTeam(epicTemplate, archetypeId); + {Array.from(new Set(epicTemplate.team.map(m => m.agentTypeId))).map(agentTypeId => { + const archetype = archetypes.find((a: AgentArchetype) => a.id === agentTypeId); + const count = getArchetypeCountInTeam(epicTemplate, agentTypeId); if (!archetype) return null; return ( -
+
{inspectingArchetypeId !== null && ( - a.id === inspectingArchetypeId)} onClose={() => setInspectingArchetypeId(null)} onSave={saveArchetype} diff --git a/src/components/onboarding/onboarding-wizard.tsx b/src/components/onboarding/onboarding-wizard.tsx new file mode 100644 index 0000000..6e3d0a0 --- /dev/null +++ b/src/components/onboarding/onboarding-wizard.tsx @@ -0,0 +1,113 @@ +'use client'; + +import { useState } from 'react'; +import { CheckCircle, ArrowRight, Play, MessageSquare, GitBranch, Zap } from 'lucide-react'; + +interface OnboardingWizardProps { + hasProjects: boolean; + piInstalled: boolean; + hasAuth: boolean; +} + +export function OnboardingWizard({ hasProjects }: OnboardingWizardProps) { + const [isLoading, setIsLoading] = useState(false); + + const handleSkipToApp = () => { + window.location.href = '/?onboarded=true'; + }; + + return ( +
+
+
+ {/* Header */} +
+

+ Welcome to BeadBoard +

+

+ Multi-agent swarm coordination for dependency-constrained work +

+
+ + {/* Features */} +
+
+
+ +
+
+

Orchestrator Chat

+

+ Left panel has a built-in AI orchestrator. Just send a message to get started. +

+
+
+ +
+
+ +
+
+

Spawn Workers

+

+ Tell the orchestrator to spawn workers for parallel task execution. +

+
+
+ +
+
+ +
+
+

Task Graphs

+

+ Visualize dependencies and coordinate work across your project. +

+
+
+
+ + {/* Quick Start */} +
+

+ Everything is ready +

+

+ The agent runtime will install automatically when you send your first message. + No manual setup required! +

+
+ + {/* Project hint if needed */} + {!hasProjects && ( +
+

+ Tip: Add a project to coordinate work: +

+ + bb project add /path/to/your/project + +
+ )} + + {/* Actions */} +
+ +
+ +

+ You can also run bb --help in terminal for CLI commands +

+
+
+
+ ); +} diff --git a/src/components/shared/left-panel-new.tsx b/src/components/shared/left-panel-new.tsx new file mode 100644 index 0000000..735da53 --- /dev/null +++ b/src/components/shared/left-panel-new.tsx @@ -0,0 +1,404 @@ +'use client'; + +import { useMemo, useState } from 'react'; +import { ChevronDown, ChevronRight, Folder, FolderOpen, Pencil, Rocket, Star } from 'lucide-react'; + +import type { RuntimeInstance } from '../../lib/embedded-runtime'; +import type { BeadIssue } from '../../lib/types'; +import { cn } from '../../lib/utils'; +import { useUrlState, type LeftPanelFilters, type LeftPanelStatusFilter, type LeftPanelPriorityFilter, type LeftPanelPresetFilter, type LeftSidebarMode, type ViewType } from '../../hooks/use-url-state'; +export type { LeftPanelFilters } from '../../hooks/use-url-state'; +import { OrchestratorPanel } from './orchestrator-panel'; + +interface EpicEntry { + epic: BeadIssue; + children: BeadIssue[]; + blockedCount: number; + activeCount: number; + readyCount: number; + deferredCount: number; + doneCount: number; + agentBlockedCount: number; + latestTimestamp: string; +} + +export interface LeftPanelProps { + issues: BeadIssue[]; + selectedEpicId?: string | null; + onEpicSelect?: (epicId: string | null) => void; + onEpicEdit?: (epicId: string) => void; + filters: LeftPanelFilters; + onFiltersChange: (filters: LeftPanelFilters) => void; + onAssignMode?: (epicId: string) => void; + sidebarMode?: LeftSidebarMode; + onSidebarModeChange?: (mode: LeftSidebarMode) => void; + orchestrator?: RuntimeInstance; + orchestratorThread?: import('../../lib/orchestrator-chat').OrchestratorChatMessage[]; + projectRoot?: string; +} + +function mapStatus(task: BeadIssue): LeftPanelStatusFilter { + if (task.status === 'open') return 'ready'; + if (task.status === 'in_progress') return 'in_progress'; + if (task.status === 'blocked') return 'blocked'; + if (task.status === 'deferred') return 'deferred'; + if (task.status === 'closed' || task.status === 'tombstone') return 'done'; + return 'all'; +} + +const views = [ + { id: 'social', label: 'Social' }, + { id: 'graph', label: 'Graph' }, +] as const; + +function isTaskMatch(task: BeadIssue, filters: LeftPanelFilters): boolean { + if (filters.query.trim()) { + const query = filters.query.toLowerCase(); + if (!task.title.toLowerCase().includes(query) && !task.id.toLowerCase().includes(query)) { + return false; + } + } + + if (filters.status !== 'all') { + if (mapStatus(task) !== filters.status) return false; + } + + if (filters.priority !== 'all') { + const priorityMap: Record = { 0: 'P0', 1: 'P1', 2: 'P2', 3: 'P3', 4: 'P4' }; + if (priorityMap[task.priority] !== filters.priority) return false; + } + + if (filters.preset === 'active') { + if (task.status !== 'open' && task.status !== 'in_progress') return false; + } + + if (filters.preset === 'blocked_agents') { + if (!task.labels.includes('gt:agent') && !task.labels.includes('agent:blocked')) return false; + } + + if (filters.hideClosed) { + if (task.status === 'closed' || task.status === 'tombstone') return false; + } + + return true; +} + +function rowTone(entry: EpicEntry): string { + const { epic } = entry; + if (epic.status === 'closed') return 'bg-[var(--surface-tertiary)]'; + return 'bg-[var(--surface-quaternary)]'; +} + +function shouldHideEpicEntry(params: { + epicStatus: BeadIssue['status']; + matchedChildrenCount: number; + totalChildrenCount: number; + isSelected: boolean; + filters: LeftPanelFilters; +}): boolean { + const { epicStatus, matchedChildrenCount, totalChildrenCount, isSelected, filters } = params; + const hasTaskFilters = + filters.query.trim().length > 0 || + filters.status !== 'all' || + filters.priority !== 'all' || + filters.preset !== 'all'; + const epicClosed = epicStatus === 'closed' || epicStatus === 'tombstone'; + const noVisibleChildren = matchedChildrenCount === 0 && totalChildrenCount > 0; + const hiddenByTaskFilters = hasTaskFilters && noVisibleChildren; + const hiddenByHideClosed = filters.hideClosed && noVisibleChildren; + const hiddenByEpicClosed = filters.hideClosed && epicClosed; + + if (hiddenByEpicClosed) { + return true; + } + + return !isSelected && (hiddenByTaskFilters || hiddenByHideClosed); +} + +export function LeftPanel({ + issues, + selectedEpicId, + filters, + onFiltersChange, + sidebarMode = 'epics', + onSidebarModeChange, + orchestrator, + orchestratorThread, + projectRoot, + onEpicSelect, +}: LeftPanelProps) { + const urlState = useUrlState(); + const { view, setView } = urlState; + const [expanded, setExpanded] = useState>({}); + + const entries = useMemo(() => { + const epicMap = new Map(); + const childrenMap = new Map(); + + for (const issue of issues) { + if (issue.labels.includes('gt:agent')) continue; + + const parentEdge = issue.dependencies.find((dep) => dep.type === 'parent'); + if (parentEdge) { + const children = childrenMap.get(parentEdge.target) ?? []; + children.push(issue); + childrenMap.set(parentEdge.target, children); + } else if (issue.issue_type === 'epic') { + epicMap.set(issue.id, { + epic: issue, + children: [], + blockedCount: 0, + activeCount: 0, + readyCount: 0, + deferredCount: 0, + doneCount: 0, + agentBlockedCount: 0, + latestTimestamp: issue.updated_at ?? issue.created_at ?? '', + }); + } + } + + for (const entry of epicMap.values()) { + entry.children = childrenMap.get(entry.epic.id) ?? []; + entry.blockedCount = entry.children.filter((t) => t.status === 'blocked').length; + entry.activeCount = entry.children.filter((t) => t.status === 'in_progress').length; + entry.readyCount = entry.children.filter((t) => t.status === 'open').length; + entry.deferredCount = entry.children.filter((t) => t.status === 'deferred').length; + entry.doneCount = entry.children.filter((t) => t.status === 'closed' || t.status === 'tombstone').length; + entry.agentBlockedCount = entry.children.filter((t) => t.labels.includes('agent:blocked')).length; + } + + return Array.from(epicMap.values()) + .filter((entry) => !shouldHideEpicEntry({ + epicStatus: entry.epic.status, + matchedChildrenCount: entry.children.length, + totalChildrenCount: entry.children.length, + isSelected: selectedEpicId === entry.epic.id, + filters, + })) + .sort((a, b) => b.latestTimestamp.localeCompare(a.latestTimestamp)); + }, [issues, selectedEpicId, filters]); + + const handleEpicClick = (epicId: string) => { + setExpanded((prev) => ({ ...prev, [epicId]: !prev[epicId] })); + onEpicSelect?.(epicId); + }; + + return ( + + ); +} diff --git a/src/components/shared/left-panel.tsx b/src/components/shared/left-panel.tsx index eec2cfa..14607cd 100644 --- a/src/components/shared/left-panel.tsx +++ b/src/components/shared/left-panel.tsx @@ -3,9 +3,11 @@ import { useMemo, useState } from 'react'; import { ChevronDown, ChevronRight, Folder, FolderOpen, Pencil, Rocket, Star } from 'lucide-react'; +import type { RuntimeConsoleEvent, RuntimeInstance } from '../../lib/embedded-runtime'; import type { BeadIssue } from '../../lib/types'; import { cn } from '../../lib/utils'; -import { useUrlState, type ViewType } from '../../hooks/use-url-state'; +import { useUrlState, type LeftSidebarMode, type ViewType } from '../../hooks/use-url-state'; +import { OrchestratorPanel } from './orchestrator-panel'; export type LeftPanelStatusFilter = 'all' | 'ready' | 'in_progress' | 'blocked' | 'deferred' | 'done'; export type LeftPanelPriorityFilter = 'all' | 'P0' | 'P1' | 'P2' | 'P3' | 'P4'; @@ -27,9 +29,14 @@ export interface LeftPanelProps { filters: LeftPanelFilters; onFiltersChange: (filters: LeftPanelFilters) => void; onAssignMode?: (epicId: string) => void; + sidebarMode?: LeftSidebarMode; + onSidebarModeChange?: (mode: LeftSidebarMode) => void; + orchestrator?: RuntimeInstance; + orchestratorThread?: RuntimeConsoleEvent[]; + projectRoot?: string; } -interface EpicEntry { +interface EpicEntry { epic: BeadIssue; children: BeadIssue[]; blockedCount: number; @@ -38,34 +45,34 @@ interface EpicEntry { deferredCount: number; doneCount: number; agentBlockedCount: number; - latestTimestamp: string; -} - -export function shouldHideEpicEntry(params: { - epicStatus: BeadIssue['status']; - matchedChildrenCount: number; - totalChildrenCount: number; - isSelected: boolean; - filters: LeftPanelFilters; -}): boolean { - const { epicStatus, matchedChildrenCount, totalChildrenCount, isSelected, filters } = params; - const hasTaskFilters = - filters.query.trim().length > 0 || - filters.status !== 'all' || - filters.priority !== 'all' || - filters.preset !== 'all'; - const epicClosed = epicStatus === 'closed' || epicStatus === 'tombstone'; - const noVisibleChildren = matchedChildrenCount === 0 && totalChildrenCount > 0; - const hiddenByTaskFilters = hasTaskFilters && noVisibleChildren; - const hiddenByHideClosed = filters.hideClosed && noVisibleChildren; - const hiddenByEpicClosed = filters.hideClosed && epicClosed; - - if (hiddenByEpicClosed) { - return true; - } - - return !isSelected && (hiddenByTaskFilters || hiddenByHideClosed); -} + latestTimestamp: string; +} + +export function shouldHideEpicEntry(params: { + epicStatus: BeadIssue['status']; + matchedChildrenCount: number; + totalChildrenCount: number; + isSelected: boolean; + filters: LeftPanelFilters; +}): boolean { + const { epicStatus, matchedChildrenCount, totalChildrenCount, isSelected, filters } = params; + const hasTaskFilters = + filters.query.trim().length > 0 || + filters.status !== 'all' || + filters.priority !== 'all' || + filters.preset !== 'all'; + const epicClosed = epicStatus === 'closed' || epicStatus === 'tombstone'; + const noVisibleChildren = matchedChildrenCount === 0 && totalChildrenCount > 0; + const hiddenByTaskFilters = hasTaskFilters && noVisibleChildren; + const hiddenByHideClosed = filters.hideClosed && noVisibleChildren; + const hiddenByEpicClosed = filters.hideClosed && epicClosed; + + if (hiddenByEpicClosed) { + return true; + } + + return !isSelected && (hiddenByTaskFilters || hiddenByHideClosed); +} function mapStatus(task: BeadIssue): LeftPanelStatusFilter { if (task.status === 'open') return 'ready'; @@ -200,12 +207,24 @@ function isTaskMatch(task: BeadIssue, filters: LeftPanelFilters): boolean { return true; } -export function LeftPanel({ issues, selectedEpicId, onEpicSelect, onEpicEdit, filters, onFiltersChange, onAssignMode }: LeftPanelProps) { +export function LeftPanel({ + issues, + selectedEpicId, + onEpicSelect, + onEpicEdit, + filters, + onFiltersChange, + onAssignMode, + sidebarMode = 'epics', + onSidebarModeChange, + orchestrator, + orchestratorThread = [], +}: LeftPanelProps) { const { view, setView } = useUrlState(); const entries = useMemo(() => buildEntries(issues), [issues]); const [expanded, setExpanded] = useState>({}); - const views: Array<{ id: ViewType; label: string }> = [ + const views: Array<{ id: ViewType; label: string }> = [ { id: 'social', label: 'Social' }, { id: 'graph', label: 'Graph' }, ]; @@ -316,11 +335,40 @@ export function LeftPanel({ issues, selectedEpicId, onEpicSelect, onEpicEdit, fi
-

Navigation / Epics

+
+ {([ + { id: 'epics', label: 'Epics' }, + { id: 'orchestrator', label: 'Orchestrator' }, + ] as Array<{ id: LeftSidebarMode; label: string }>).map((item) => { + const active = sidebarMode === item.id; + return ( + + ); + })} +
+ +

+ {sidebarMode === 'orchestrator' ? 'Project Orchestrator' : 'Navigation / Epics'} +

- {entries.map((entry) => { + {sidebarMode === 'orchestrator' ? ( + orchestrator ? : null + ) : entries.map((entry) => { const { epic, children, @@ -343,15 +391,15 @@ export function LeftPanel({ issues, selectedEpicId, onEpicSelect, onEpicEdit, fi const laneColor = blockedCount > 0 ? 'var(--accent-danger)' : activeCount > 0 ? 'var(--accent-warning)' : 'var(--accent-success)'; const rowBackground = rowTone(entry); - if (shouldHideEpicEntry({ - epicStatus: epic.status, - matchedChildrenCount: matchedChildren.length, - totalChildrenCount: total, - isSelected, - filters, - })) { - return null; - } + if (shouldHideEpicEntry({ + epicStatus: epic.status, + matchedChildrenCount: matchedChildren.length, + totalChildrenCount: total, + isSelected, + filters, + })) { + return null; + } return (
diff --git a/src/components/shared/orchestrator-panel.tsx b/src/components/shared/orchestrator-panel.tsx new file mode 100644 index 0000000..73d8f62 --- /dev/null +++ b/src/components/shared/orchestrator-panel.tsx @@ -0,0 +1,118 @@ +'use client'; + +import { useEffect, useMemo, useState } from 'react'; +import { Send } from 'lucide-react'; +import type { RuntimeInstance } from '../../lib/embedded-runtime'; +import type { OrchestratorChatMessage } from '../../lib/orchestrator-chat'; + +export interface OrchestratorPanelProps { + orchestrator: RuntimeInstance; + thread: OrchestratorChatMessage[]; + projectRoot?: string; +} + +export function OrchestratorPanel({ orchestrator, thread, projectRoot }: OrchestratorPanelProps) { + const [input, setInput] = useState(''); + const [submitting, setSubmitting] = useState(false); + const [optimisticMessages, setOptimisticMessages] = useState([]); + + useEffect(() => { + setOptimisticMessages((current) => + current.filter((pending) => !thread.some((message) => message.role === 'user' && message.text === pending.text)) + ); + }, [thread]); + + const visibleThread = useMemo(() => [...thread, ...optimisticMessages], [thread, optimisticMessages]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!input.trim() || submitting || !projectRoot) return; + + setSubmitting(true); + const text = input.trim(); + setInput(''); + setOptimisticMessages((current) => [ + ...current, + { + id: `optimistic-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, + role: 'user', + text, + timestamp: new Date().toISOString(), + }, + ]); + + try { + await fetch('/api/runtime/prompt', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ projectRoot, text }) + }); + } finally { + setSubmitting(false); + } + }; + + return ( +
+
+

Main Orchestrator

+
+
+

{orchestrator.label}

+

Long-lived project control plane for Pi launches

+
+ + {orchestrator.status} + +
+
+ +
+
+ {visibleThread.map((message) => ( +
+
+

{message.text}

+
+
+ ))} + {visibleThread.length === 0 ? ( +

+ No orchestrator messages yet. +

+ ) : null} +
+
+ + {projectRoot && ( +
+
+ setInput(e.target.value)} + placeholder="Ask the orchestrator..." + className="w-full rounded-md border border-[var(--border-subtle)] bg-[var(--surface-tertiary)] px-3 py-2 pr-10 text-sm placeholder-[var(--text-tertiary)] focus:border-[var(--brand-primary)] focus:outline-none" + disabled={submitting} + /> + +
+
+ )} +
+ ); +} diff --git a/src/components/shared/runtime-console.tsx b/src/components/shared/runtime-console.tsx new file mode 100644 index 0000000..50a466d --- /dev/null +++ b/src/components/shared/runtime-console.tsx @@ -0,0 +1,101 @@ +'use client'; + +import { useState } from 'react'; +import { ChevronDown, ChevronUp, TerminalSquare } from 'lucide-react'; + +import { cn } from '../../lib/utils'; +import type { RuntimeConsoleEvent } from '../../lib/embedded-runtime'; + +export interface RuntimeConsoleProps { + events: RuntimeConsoleEvent[]; + daemonStatus?: string | null; +} + +function statusTone(status?: RuntimeConsoleEvent['status']): string { + if (status === 'failed' || status === 'blocked') return 'text-red-300'; + if (status === 'completed') return 'text-emerald-300'; + if (status === 'planning' || status === 'launching') return 'text-amber-300'; + return 'text-cyan-200'; +} + +function isWorkerEvent(event: RuntimeConsoleEvent): boolean { + return ( + event.kind === 'worker.spawned' || + event.kind === 'worker.updated' || + event.kind === 'worker.completed' || + event.kind === 'worker.failed' + ); +} + +function formatTimestamp(timestamp: string): string { + const date = new Date(timestamp); + return Number.isNaN(date.getTime()) ? timestamp : date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); +} + +export function RuntimeConsole({ events, daemonStatus }: RuntimeConsoleProps) { + const [isMinimized, setIsMinimized] = useState(false); + + return ( +
+
+
+
+

Runtime Console

+

Live orchestrator and worker telemetry

+
+
+
+
+ +
+ + {!isMinimized && ( +
+ {events.map((event) => ( +
+
+
+ {isWorkerEvent(event) && ( + + Worker + + )} + + {event.kind} + + {event.actorLabel ? ( + {event.actorLabel} + ) : null} +
+ {formatTimestamp(event.timestamp)} +
+

{event.title}

+

{event.detail}

+
+ ))} + {events.length === 0 ? ( +

+ No runtime events yet. +

+ ) : null} +
+ )} +
+ ); +} diff --git a/src/components/shared/unified-shell.tsx b/src/components/shared/unified-shell.tsx index f651701..7e3cee5 100644 --- a/src/components/shared/unified-shell.tsx +++ b/src/components/shared/unified-shell.tsx @@ -3,27 +3,30 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { X } from 'lucide-react'; import { useRouter } from 'next/navigation'; -import type { BeadIssue } from '../../lib/types'; -import type { ProjectScopeOption } from '../../lib/project-scope'; +import type { BeadIssue } from '../../lib/types'; +import type { ProjectScopeOption } from '../../lib/project-scope'; +import { buildLaunchRequest, createLaunchConsoleEvents, createOrchestratorInstance, type RuntimeConsoleEvent, type RuntimeStatus } from '../../lib/embedded-runtime'; import { TopBar } from './top-bar'; -import { LeftPanel, type LeftPanelFilters } from './left-panel'; +import { LeftPanel, type LeftPanelFilters } from './left-panel-new'; import { RightPanel } from './right-panel'; import { MobileNav } from './mobile-nav'; import { ThreadDrawer } from './thread-drawer'; import { ResizeHandle } from './resize-handle'; -import { useUrlState } from '../../hooks/use-url-state'; +import { RuntimeConsole } from './runtime-console'; +import { useUrlState, type LeftSidebarMode } from '../../hooks/use-url-state'; import { usePanelResize } from '../../hooks/use-panel-resize'; import { SmartDag } from '../graph/smart-dag'; import { SocialPage } from '../social/social-page'; import { buildSocialCards } from '../../lib/social-cards'; import { ContextualRightPanel } from '../activity/contextual-right-panel'; import { AssignmentPanel } from '../graph/assignment-panel'; -import { TelemetryStrip } from './telemetry-strip'; -import { useSwarmList } from '../../hooks/use-swarm-list'; -import { useBeadsSubscription } from '../../hooks/use-beads-subscription'; -import { useBdHealth } from '../../hooks/use-bd-health'; -import { BlockedTriageModal } from './blocked-triage-modal'; -import { deriveBlockedIds } from '../../lib/kanban'; +import { TelemetryStrip } from './telemetry-strip'; +import { useSwarmList } from '../../hooks/use-swarm-list'; +import { useBeadsSubscription } from '../../hooks/use-beads-subscription'; +import { useBdHealth } from '../../hooks/use-bd-health'; +import { BlockedTriageModal } from './blocked-triage-modal'; +import { deriveBlockedIds } from '../../lib/kanban'; +import { projectOrchestratorChat } from '../../lib/orchestrator-chat'; export interface UnifiedShellProps { issues: BeadIssue[]; @@ -33,13 +36,27 @@ export interface UnifiedShellProps { projectScopeMode: 'single' | 'aggregate'; } +function mergeUniqueRuntimeEvents(existing: RuntimeConsoleEvent[], incoming: RuntimeConsoleEvent[]): RuntimeConsoleEvent[] { + const seen = new Set(); + const merged: RuntimeConsoleEvent[] = []; + + for (const event of [...incoming, ...existing]) { + if (seen.has(event.id)) continue; + seen.add(event.id); + merged.push(event); + } + + return merged.slice(0, 40); +} + export function UnifiedShell({ issues: initialIssues, projectRoot, projectScopeOptions, }: UnifiedShellProps) { const router = useRouter(); - const { view, taskId, setTaskId, swarmId, graphTab, panel, drawer, setDrawer, epicId, setEpicId, blockedOnly } = useUrlState(); + const { view, taskId, setTaskId, swarmId, graphTab, drawer, setDrawer, epicId, setEpicId, blockedOnly } = useUrlState(); + const [leftSidebarMode, setLeftSidebarMode] = useState('epics'); // Subscribe to SSE for real-time updates on ALL views const { issues } = useBeadsSubscription(initialIssues, projectRoot); @@ -66,29 +83,32 @@ export function UnifiedShell({ }, []); const [customRightPanel, setCustomRightPanel] = useState(null); + const [orchestrator, setOrchestrator] = useState(() => createOrchestratorInstance(projectRoot)); + const [runtimeEvents, setRuntimeEvents] = useState([]); + const [daemonLifecycle, setDaemonLifecycle] = useState<{ status: RuntimeStatus | 'stopped' | 'starting' | 'stopping' | 'failed' } | null>(null); // Assign mode state for graph view const [assignMode, setAssignMode] = useState(false); const [selectedAssignIssue, setSelectedAssignIssue] = useState(null); -// Remember last non-telemetry state for minimize button - const [lastTaskId, setLastTaskId] = useState(null); - const [lastAssignMode, setLastAssignMode] = useState(false); - - // Blocked triage modal state - const [blockedTriageOpen, setBlockedTriageOpen] = useState(false); - const handleOpenBlockedTriage = useCallback(() => setBlockedTriageOpen(true), []); - const handleCloseBlockedTriage = useCallback(() => setBlockedTriageOpen(false), []); +// Remember last non-telemetry state for minimize button + const [lastTaskId, setLastTaskId] = useState(null); + const [lastAssignMode, setLastAssignMode] = useState(false); - const socialCards = useMemo(() => buildSocialCards(issues), [issues]); - const blockedIds = useMemo(() => deriveBlockedIds(issues), [issues]); - const blockedCount = useMemo(() => { - return issues.filter(i => i.status === 'blocked' || blockedIds.has(i.id)).length; - }, [issues, blockedIds]); - const { swarms: swarmCards } = useSwarmList(projectRoot); - const bdHealth = useBdHealth(projectRoot); - - const selectedSocialCard = taskId ? socialCards.find(c => c.id === taskId) : null; + // Blocked triage modal state + const [blockedTriageOpen, setBlockedTriageOpen] = useState(false); + const handleOpenBlockedTriage = useCallback(() => setBlockedTriageOpen(true), []); + const handleCloseBlockedTriage = useCallback(() => setBlockedTriageOpen(false), []); + + const socialCards = useMemo(() => buildSocialCards(issues), [issues]); + const blockedIds = useMemo(() => deriveBlockedIds(issues), [issues]); + const blockedCount = useMemo(() => { + return issues.filter(i => i.status === 'blocked' || blockedIds.has(i.id)).length; + }, [issues, blockedIds]); + const { swarms: swarmCards } = useSwarmList(projectRoot); + const bdHealth = useBdHealth(projectRoot); + + const selectedSocialCard = taskId ? socialCards.find(c => c.id === taskId) : null; const selectedSwarmCard = swarmId ? swarmCards.find(c => c.swarmId === swarmId) : null; const selectedIssue = taskId ? issues.find((issue) => issue.id === taskId) ?? null : null; @@ -131,6 +151,116 @@ export function UnifiedShell({ setAssignMode(true); }, [setTaskId]); + useEffect(() => { + let cancelled = false; + + async function bootstrapRuntime() { + try { + const [statusResponse, orchestratorResponse, eventsResponse] = await Promise.all([ + fetch('/api/runtime/status'), + fetch('/api/runtime/orchestrator', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ projectRoot }), + }), + fetch(`/api/runtime/events?projectRoot=${encodeURIComponent(projectRoot)}`), + ]); + + const statusPayload = await statusResponse.json().catch(() => null); + const orchestratorPayload = await orchestratorResponse.json().catch(() => null); + const eventsPayload = await eventsResponse.json().catch(() => null); + + if (cancelled) { + return; + } + + if (statusResponse.ok && statusPayload?.lifecycle) { + setDaemonLifecycle(statusPayload.lifecycle); + } + if (orchestratorResponse.ok && orchestratorPayload?.ok && orchestratorPayload.data) { + setOrchestrator(orchestratorPayload.data); + } + if (orchestratorPayload?.lifecycle) { + setDaemonLifecycle(orchestratorPayload.lifecycle); + } + if (eventsResponse.ok && eventsPayload?.ok && Array.isArray(eventsPayload.data)) { + setRuntimeEvents((current) => mergeUniqueRuntimeEvents(current, eventsPayload.data)); + } + } catch { + // Runtime bootstrap is best-effort during early integration. + } + } + + void bootstrapRuntime(); + return () => { + cancelled = true; + }; + }, [projectRoot]); + + useEffect(() => { + // daemon lifecycle and runtime events should come from the daemon stream, not local shell ownership + const source = new EventSource(`/api/runtime/stream?projectRoot=${encodeURIComponent(projectRoot)}`); + const onRuntime = (event: MessageEvent) => { + try { + const payload = JSON.parse(event.data) as RuntimeConsoleEvent; + setRuntimeEvents((current) => mergeUniqueRuntimeEvents(current, [payload])); + } catch { + // Ignore malformed runtime frames. + } + }; + + source.addEventListener('runtime', onRuntime as EventListener); + + return () => { + source.removeEventListener('runtime', onRuntime as EventListener); + source.close(); + }; + }, [projectRoot]); + + const handleAskOrchestrator = useCallback(async (issueId: string) => { + const issue = issues.find((entry) => entry.id === issueId); + if (!issue) { + return; + } + + const optimisticRequest = buildLaunchRequest({ + issue, + origin: 'social', + projectRoot, + swarmId, + }); + const optimisticEvents = createLaunchConsoleEvents(optimisticRequest); + setLeftSidebarMode('orchestrator'); + setRuntimeEvents((current) => mergeUniqueRuntimeEvents(current, optimisticEvents)); + + try { + const response = await fetch('/api/runtime/launch', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + projectRoot, + taskId: issueId, + origin: 'social', + swarmId, + }), + }); + const payload = await response.json().catch(() => null); + if (response.ok && payload?.ok) { + if (payload.lifecycle) { + setDaemonLifecycle(payload.lifecycle); + } + if (payload.data?.orchestrator) { + setOrchestrator(payload.data.orchestrator); + } + if (Array.isArray(payload.data?.events)) { + setRuntimeEvents((current) => mergeUniqueRuntimeEvents(current, payload.data.events)); + } + } + } catch { + // Keep optimistic console events visible; bridge hardening comes in later phases. + } + }, [issues, projectRoot, setLeftSidebarMode, swarmId]); + // Minimize: restore last clicked thing (task or assign mode) const handleMinimize = useCallback(() => { if (lastTaskId) { @@ -156,23 +286,23 @@ export function UnifiedShell({ // Chat Mode Logic: If a card is selected (drawer='open'), we show Chat popup const isChatOpen = drawer === 'open' && (!!taskId || !!swarmId || !!epicId); - const selectedEpic = epicId ? issues.find((issue) => issue.id === epicId && issue.issue_type === 'epic') ?? null : null; - const drawerTitle = selectedSocialCard?.title || selectedSwarmCard?.title || selectedEpic?.title || ''; - const drawerId = taskId || swarmId || epicId || ''; - const selectedItem = selectedEpic ?? selectedIssue; - - useEffect(() => { - if (!filters.hideClosed || !epicId) { - return; - } - const epic = issues.find((issue) => issue.id === epicId && issue.issue_type === 'epic'); - if (!epic) { - return; - } - if (epic.status === 'closed' || epic.status === 'tombstone') { - setEpicId(null); - } - }, [filters.hideClosed, epicId, issues, setEpicId]); + const selectedEpic = epicId ? issues.find((issue) => issue.id === epicId && issue.issue_type === 'epic') ?? null : null; + const drawerTitle = selectedSocialCard?.title || selectedSwarmCard?.title || selectedEpic?.title || ''; + const drawerId = taskId || swarmId || epicId || ''; + const selectedItem = selectedEpic ?? selectedIssue; + + useEffect(() => { + if (!filters.hideClosed || !epicId) { + return; + } + const epic = issues.find((issue) => issue.id === epicId && issue.issue_type === 'epic'); + if (!epic) { + return; + } + if (epic.status === 'closed' || epic.status === 'tombstone') { + setEpicId(null); + } + }, [filters.hideClosed, epicId, issues, setEpicId]); // Panel resize hook const { leftWidth, rightWidth, handleLeftResize, handleRightResize } = usePanelResize(); @@ -214,6 +344,7 @@ export function UnifiedShell({ projectRoot={projectRoot} swarmId={swarmId ?? undefined} onRocketClick={handleSocialRocket} + onAskOrchestrator={handleAskOrchestrator} /> ); } @@ -262,16 +393,16 @@ export function UnifiedShell({ return (
{/* TOP BAR: 3rem fixed */} - i.issue_type !== 'epic').length} - criticalAlerts={blockedCount} - busyCount={issues.filter(i => i.status === 'in_progress').length} - idleCount={0} - actor={actor} - onActorChange={handleActorChange} - onLaunchSwarm={() => { setTaskId(null); setAssignMode(true); }} - onOpenBlockedTriage={handleOpenBlockedTriage} - /> + i.issue_type !== 'epic').length} + criticalAlerts={blockedCount} + busyCount={issues.filter(i => i.status === 'in_progress').length} + idleCount={0} + actor={actor} + onActorChange={handleActorChange} + onLaunchSwarm={() => { setTaskId(null); setAssignMode(true); }} + onOpenBlockedTriage={handleOpenBlockedTriage} + /> {!bdHealth.loading && !bdHealth.healthy ? (
BD setup issue: {bdHealth.message} @@ -293,6 +424,11 @@ export function UnifiedShell({ filters={filters} onFiltersChange={setFilters} onAssignMode={(epicId) => { setEpicId(epicId); setTaskId(null); setAssignMode(true); }} + sidebarMode={leftSidebarMode} + onSidebarModeChange={setLeftSidebarMode} + orchestrator={orchestrator} + orchestratorThread={projectOrchestratorChat(runtimeEvents)} + projectRoot={projectRoot} />
@@ -344,20 +480,22 @@ export function UnifiedShell({
) : null} -{/* MOBILE NAV: Bottom tab bar */} - - - {/* BLOCKED TRIAGE MODAL */} - { - setTaskId(taskId); - handleCloseBlockedTriage(); - }} - /> -
+ + +{/* MOBILE NAV: Bottom tab bar */} + + + {/* BLOCKED TRIAGE MODAL */} + { + setTaskId(taskId); + handleCloseBlockedTriage(); + }} + /> +
); } diff --git a/src/components/social/social-card.tsx b/src/components/social/social-card.tsx index b03e5e2..bc1147e 100644 --- a/src/components/social/social-card.tsx +++ b/src/components/social/social-card.tsx @@ -1,33 +1,36 @@ import { useState } from 'react'; import type { KeyboardEvent, MouseEventHandler } from 'react'; -import { Clock3, GitBranch, Link2, MessageCircle, MessageSquare, Rocket, UserPlus } from 'lucide-react'; - -import { Badge } from '@/components/ui/badge'; -import { cn } from '@/lib/utils'; - -import type { SocialCard as SocialCardData, AgentStatus } from '../../lib/social-cards'; -import { AgentAvatar } from '../shared/agent-avatar'; -import { useArchetypePicker } from '../../hooks/use-archetype-picker'; -import type { AgentArchetype } from '../../lib/types-swarm'; - +import { Clock3, GitBranch, Link2, MessageCircle, MessageSquare, UserPlus } from 'lucide-react'; + +import { Badge } from '@/components/ui/badge'; +import { cn } from '@/lib/utils'; + +import type { SocialCard as SocialCardData, AgentStatus } from '../../lib/social-cards'; +import { AgentAvatar } from '../shared/agent-avatar'; +import { AgentActionRow } from '../agents'; +import { useArchetypePicker } from '../../hooks/use-archetype-picker'; +import type { AgentArchetype } from '../../lib/types-swarm'; + interface SocialCardProps { - data: SocialCardData; - className?: string; - selected?: boolean; - onClick?: MouseEventHandler; - onJumpToGraph?: (id: string) => void; - onJumpToActivity?: (id: string) => void; - onOpenThread?: () => void; - description?: string; - updatedLabel?: string; - dependencyCount?: number; - commentCount?: number; - unreadCount?: number; - blockedByDetails?: Array<{ id: string; title: string; epic?: string }>; - unblocksDetails?: Array<{ id: string; title: string; epic?: string }>; - archetypes?: AgentArchetype[]; - swarmId?: string; + data: SocialCardData; + className?: string; + selected?: boolean; + onClick?: MouseEventHandler; + onJumpToGraph?: (id: string) => void; + onJumpToActivity?: (id: string) => void; + onOpenThread?: () => void; + description?: string; + updatedLabel?: string; + dependencyCount?: number; + commentCount?: number; + unreadCount?: number; + blockedByDetails?: Array<{ id: string; title: string; epic?: string }>; + unblocksDetails?: Array<{ id: string; title: string; epic?: string }>; + archetypes?: AgentArchetype[]; + projectRoot?: string; + swarmId?: string; onLaunchSwarm?: () => void; + onAskOrchestrator?: () => void; agentUnreadByName?: Record; agentMessagesByName?: Record; onAckMessage?: (agent: string, messageId: string) => Promise | void; } - -function handleCardKeyDown(event: KeyboardEvent, onClick?: MouseEventHandler) { - if (!onClick) return; - if (event.key !== 'Enter' && event.key !== ' ') return; - event.preventDefault(); - onClick(event as unknown as Parameters>[0]); -} - -function statusVisual(status: SocialCardData['status']) { - if (status === 'blocked') { - return { - border: 'var(--accent-danger)', - badgeBg: 'var(--status-blocked)', - badgeText: '#ffd5df', - chipText: 'Blocked', - }; - } - - if (status === 'in_progress') { - return { - border: 'var(--accent-warning)', - badgeBg: 'var(--status-in-progress)', - badgeText: '#ffe5c7', - chipText: 'Active', - }; - } - - if (status === 'ready') { - return { - border: 'var(--accent-success)', - badgeBg: 'var(--status-ready)', - badgeText: '#d6ffe7', - chipText: 'Ready', - }; - } - - return { - border: 'var(--border-default)', - badgeBg: 'var(--status-closed)', - badgeText: 'var(--text-tertiary)', - chipText: 'Closed', - }; -} - + +function handleCardKeyDown(event: KeyboardEvent, onClick?: MouseEventHandler) { + if (!onClick) return; + if (event.key !== 'Enter' && event.key !== ' ') return; + event.preventDefault(); + onClick(event as unknown as Parameters>[0]); +} + +function statusVisual(status: SocialCardData['status']) { + if (status === 'blocked') { + return { + border: 'var(--accent-danger)', + badgeBg: 'var(--status-blocked)', + badgeText: '#ffd5df', + chipText: 'Blocked', + }; + } + + if (status === 'in_progress') { + return { + border: 'var(--accent-warning)', + badgeBg: 'var(--status-in-progress)', + badgeText: '#ffe5c7', + chipText: 'Active', + }; + } + + if (status === 'ready') { + return { + border: 'var(--accent-success)', + badgeBg: 'var(--status-ready)', + badgeText: '#d6ffe7', + chipText: 'Ready', + }; + } + + return { + border: 'var(--border-default)', + badgeBg: 'var(--status-closed)', + badgeText: 'var(--text-tertiary)', + chipText: 'Closed', + }; +} + function dependencyPanel( - title: string, - color: string, - details: Array<{ id: string; title: string; epic?: string }>, -) { - if (details.length === 0) return null; - - return ( -
-

- {title} -

-
- {details.slice(0, 1).map((item) => ( -
-
- - {item.id} -
-

{item.title}

- {item.epic ? ( -

↳ {item.epic}

- ) : null} -
- ))} -
- {details.length > 1 ?

+{details.length - 1} more

: null} -
- ); + title: string, + color: string, + details: Array<{ id: string; title: string; epic?: string }>, +) { + if (details.length === 0) return null; + + return ( +
+

+ {title} +

+
+ {details.slice(0, 1).map((item) => ( +
+
+ + {item.id} +
+

{item.title}

+ {item.epic ? ( +

↳ {item.epic}

+ ) : null} +
+ ))} +
+ {details.length > 1 ?

+{details.length - 1} more

: null} +
+ ); } function categoryBadgeClass(category: 'HANDOFF' | 'BLOCKED' | 'DECISION' | 'INFO'): string { @@ -127,23 +130,24 @@ function categoryBadgeClass(category: 'HANDOFF' | 'BLOCKED' | 'DECISION' | 'INFO } export function SocialCard({ - data, - className, - selected = false, - onClick, - onJumpToGraph, - onJumpToActivity, - onOpenThread, - description, - updatedLabel = 'just now', - dependencyCount, - commentCount, - unreadCount = 0, - blockedByDetails = [], - unblocksDetails = [], + data, + className, + selected = false, + onClick, + onJumpToGraph, + onJumpToActivity, + description, + updatedLabel = 'just now', + dependencyCount, + commentCount, + unreadCount = 0, + blockedByDetails = [], + unblocksDetails = [], archetypes = [], + projectRoot, swarmId, onLaunchSwarm, + onAskOrchestrator, agentUnreadByName = {}, agentMessagesByName = {}, agentReservationsByName = {}, @@ -155,52 +159,52 @@ export function SocialCard({ const isSwarmHighlighted = swarmId && data.id.includes(swarmId); const [expandedAgent, setExpandedAgent] = useState(null); const [ackingMessageId, setAckingMessageId] = useState(null); - - return ( -
handleCardKeyDown(event, onClick)} - role="button" - tabIndex={0} - aria-label={`Open ${data.title}`} - className={cn( - 'group relative flex min-h-[290px] cursor-pointer flex-col rounded-[14px] border px-3.5 py-3 text-left transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-info)]', - isSwarmHighlighted && 'ring-2 ring-blue-500', - className, - )} - style={{ - background: 'var(--surface-quaternary)', - borderColor: selected ? status.border : 'var(--border-default)', - boxShadow: selected - ? `0 0 0 2px ${status.border}, 0 20px 40px -20px rgba(0,0,0,0.6)` - : '0 4px 12px -6px rgba(0,0,0,0.4)', - }} - > -
-
- - {status.chipText} - - {data.priority} - {data.id} - {unreadCount > 0 ? ( - - {unreadCount} - - ) : null} -
-
- -

{data.title}

-

- {description || 'No summary provided yet.'} -

- -
- {dependencyPanel('Blocked By', 'var(--accent-danger)', blockedByDetails)} - {dependencyPanel('Unblocks', 'var(--accent-success)', unblocksDetails)} -
- + + return ( +
handleCardKeyDown(event, onClick)} + role="button" + tabIndex={0} + aria-label={`Open ${data.title}`} + className={cn( + 'group relative flex min-h-[290px] cursor-pointer flex-col rounded-[14px] border px-3.5 py-3 text-left transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-info)]', + isSwarmHighlighted && 'ring-2 ring-blue-500', + className, + )} + style={{ + background: 'var(--surface-quaternary)', + borderColor: selected ? status.border : 'var(--border-default)', + boxShadow: selected + ? `0 0 0 2px ${status.border}, 0 20px 40px -20px rgba(0,0,0,0.6)` + : '0 4px 12px -6px rgba(0,0,0,0.4)', + }} + > +
+
+ + {status.chipText} + + {data.priority} + {data.id} + {unreadCount > 0 ? ( + + {unreadCount} + + ) : null} +
+
+ +

{data.title}

+

+ {description || 'No summary provided yet.'} +

+ +
+ {dependencyPanel('Blocked By', 'var(--accent-danger)', blockedByDetails)} + {dependencyPanel('Unblocks', 'var(--accent-success)', unblocksDetails)} +
+
{data.agents.slice(0, 3).map((agent) => { const unreadCount = agentUnreadByName[agent.name] ?? 0; @@ -286,87 +290,98 @@ export function SocialCard({ ) : null}
) : null} - - {showAssign && ( -
e.stopPropagation()}> - - -
- )} - -
-
- - stage active -
- -
-
- - -
- -
- - - {onLaunchSwarm ? ( - - ) : null} -
-
-
-
- ); -} + + {showAssign && ( +
e.stopPropagation()}> + + +
+ )} + +
+
+ + stage active +
+ +
+
+ + +
+ +
+ + + {onAskOrchestrator ? ( + + ) : null} + {projectRoot && archetypes.length > 0 ? ( + + ) : null} +
+
+
+
+ ); +} diff --git a/src/components/social/social-page.tsx b/src/components/social/social-page.tsx index 7d7f26e..10a9404 100644 --- a/src/components/social/social-page.tsx +++ b/src/components/social/social-page.tsx @@ -2,22 +2,23 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; - -import type { BeadIssue } from '../../lib/types'; -import type { ProjectScopeOption } from '../../lib/project-scope'; -import { buildSocialCards } from '../../lib/social-cards'; -import { SocialCard } from './social-card'; -import { useArchetypes } from '../../hooks/use-archetypes'; - + +import type { BeadIssue } from '../../lib/types'; +import type { ProjectScopeOption } from '../../lib/project-scope'; +import { buildSocialCards } from '../../lib/social-cards'; +import { SocialCard } from './social-card'; +import { useArchetypes } from '../../hooks/use-archetypes'; + interface SocialPageProps { - issues: BeadIssue[]; - selectedId?: string; - onSelect: (id: string) => void; - projectScopeOptions?: ProjectScopeOption[]; - blockedOnly?: boolean; - projectRoot: string; - swarmId?: string; - onRocketClick?: () => void; + issues: BeadIssue[]; + selectedId?: string; + onSelect: (id: string) => void; + projectScopeOptions?: ProjectScopeOption[]; + blockedOnly?: boolean; + projectRoot: string; + swarmId?: string; + onRocketClick?: () => void; + onAskOrchestrator?: (issueId: string) => void; } interface CoordMessage { @@ -30,137 +31,138 @@ interface CoordMessage { state: 'unread' | 'read' | 'acked'; requires_ack: boolean; } - -type SectionKey = 'ready' | 'in_progress' | 'blocked' | 'deferred' | 'done'; - -const SECTION_LABEL: Record = { - ready: 'Ready', - in_progress: 'In Progress', - blocked: 'Blocked', - deferred: 'Deferred', - done: 'Done', -}; - -const SECTION_COLOR: Record = { - ready: 'var(--ui-accent-ready)', - in_progress: 'var(--ui-accent-warning)', - blocked: 'var(--ui-accent-blocked)', - deferred: 'var(--ui-accent-info)', - done: 'var(--ui-text-muted)', -}; - -function bucketForStatus(status: string): SectionKey { - if (status === 'ready') return 'ready'; - if (status === 'in_progress') return 'in_progress'; - if (status === 'blocked') return 'blocked'; - if (status === 'closed') return 'done'; - return 'deferred'; -} - -function formatRelative(timestamp: string): string { - const then = new Date(timestamp); - const now = new Date(); - const diffMins = Math.floor((now.getTime() - then.getTime()) / 60000); - - if (diffMins < 1) return 'just now'; - if (diffMins < 60) return `${diffMins}m ago`; - - const diffHours = Math.floor(diffMins / 60); - if (diffHours < 24) return `${diffHours}h ago`; - - const diffDays = Math.floor(diffHours / 24); - return `${diffDays}d ago`; -} - + +type SectionKey = 'ready' | 'in_progress' | 'blocked' | 'deferred' | 'done'; + +const SECTION_LABEL: Record = { + ready: 'Ready', + in_progress: 'In Progress', + blocked: 'Blocked', + deferred: 'Deferred', + done: 'Done', +}; + +const SECTION_COLOR: Record = { + ready: 'var(--ui-accent-ready)', + in_progress: 'var(--ui-accent-warning)', + blocked: 'var(--ui-accent-blocked)', + deferred: 'var(--ui-accent-info)', + done: 'var(--ui-text-muted)', +}; + +function bucketForStatus(status: string): SectionKey { + if (status === 'ready') return 'ready'; + if (status === 'in_progress') return 'in_progress'; + if (status === 'blocked') return 'blocked'; + if (status === 'closed') return 'done'; + return 'deferred'; +} + +function formatRelative(timestamp: string): string { + const then = new Date(timestamp); + const now = new Date(); + const diffMins = Math.floor((now.getTime() - then.getTime()) / 60000); + + if (diffMins < 1) return 'just now'; + if (diffMins < 60) return `${diffMins}m ago`; + + const diffHours = Math.floor(diffMins / 60); + if (diffHours < 24) return `${diffHours}h ago`; + + const diffDays = Math.floor(diffHours / 24); + return `${diffDays}d ago`; +} + export function SocialPage({ - issues, - selectedId, - onSelect, - projectScopeOptions = [], - blockedOnly = false, - projectRoot, - swarmId, - onRocketClick, -}: SocialPageProps) { - const router = useRouter(); - const searchParams = useSearchParams(); - const cards = useMemo(() => buildSocialCards(issues), [issues]); - const { archetypes } = useArchetypes(projectRoot); - - const navigateWithParams = (updates: Record) => { - const next = new URLSearchParams(searchParams.toString()); - for (const [key, value] of Object.entries(updates)) { - if (!value) next.delete(key); - else next.set(key, value); - } - const query = next.toString(); - router.push(query ? `/?${query}` : '/', { scroll: false }); - }; - - const issueById = useMemo(() => { - const map = new Map(); - for (const issue of issues) map.set(issue.id, issue); - return map; - }, [issues]); - const epicTitleById = useMemo(() => { - const map = new Map(); - for (const issue of issues) { - if (issue.issue_type === 'epic') { - map.set(issue.id, issue.title); - } - } - return map; - }, [issues]); - - const toDependencyDetails = (ids: string[]) => - ids.map((id) => { - const depIssue = issueById.get(id); - const parentEpicId = depIssue?.dependencies.find((dep) => dep.type === 'parent')?.target; - return { - id, - title: depIssue?.title ?? id, - epic: parentEpicId ? epicTitleById.get(parentEpicId) : undefined, - }; - }); - - const orderedCards = useMemo( - () => [...cards].sort((a, b) => b.lastActivity.getTime() - a.lastActivity.getTime()), - [cards], - ); - - const visibleCards = useMemo( - () => (blockedOnly ? orderedCards.filter((card) => card.status === 'blocked') : orderedCards), - [blockedOnly, orderedCards], - ); - - const grouped = useMemo(() => { - const map: Record = { - ready: [], - in_progress: [], - blocked: [], - deferred: [], - done: [], - }; - - for (const card of visibleCards) { - map[bucketForStatus(card.status)].push(card); - } - - return map; - }, [visibleCards]); + issues, + selectedId, + onSelect, + projectScopeOptions = [], + blockedOnly = false, + projectRoot, + swarmId, + onRocketClick, + onAskOrchestrator, +}: SocialPageProps) { + const router = useRouter(); + const searchParams = useSearchParams(); + const cards = useMemo(() => buildSocialCards(issues), [issues]); + const { archetypes } = useArchetypes(projectRoot); + + const navigateWithParams = (updates: Record) => { + const next = new URLSearchParams(searchParams.toString()); + for (const [key, value] of Object.entries(updates)) { + if (!value) next.delete(key); + else next.set(key, value); + } + const query = next.toString(); + router.push(query ? `/?${query}` : '/', { scroll: false }); + }; + + const issueById = useMemo(() => { + const map = new Map(); + for (const issue of issues) map.set(issue.id, issue); + return map; + }, [issues]); + const epicTitleById = useMemo(() => { + const map = new Map(); + for (const issue of issues) { + if (issue.issue_type === 'epic') { + map.set(issue.id, issue.title); + } + } + return map; + }, [issues]); + + const toDependencyDetails = (ids: string[]) => + ids.map((id) => { + const depIssue = issueById.get(id); + const parentEpicId = depIssue?.dependencies.find((dep) => dep.type === 'parent')?.target; + return { + id, + title: depIssue?.title ?? id, + epic: parentEpicId ? epicTitleById.get(parentEpicId) : undefined, + }; + }); + + const orderedCards = useMemo( + () => [...cards].sort((a, b) => b.lastActivity.getTime() - a.lastActivity.getTime()), + [cards], + ); + + const visibleCards = useMemo( + () => (blockedOnly ? orderedCards.filter((card) => card.status === 'blocked') : orderedCards), + [blockedOnly, orderedCards], + ); + + const grouped = useMemo(() => { + const map: Record = { + ready: [], + in_progress: [], + blocked: [], + deferred: [], + done: [], + }; + + for (const card of visibleCards) { + map[bucketForStatus(card.status)].push(card); + } + + return map; + }, [visibleCards]); const [expandedSections, setExpandedSections] = useState>({ - ready: false, - in_progress: false, - blocked: false, - deferred: false, - done: false, - }); + ready: false, + in_progress: false, + blocked: false, + deferred: false, + done: false, + }); const [collapsedSections, setCollapsedSections] = useState>({ - ready: false, - in_progress: false, - blocked: false, - deferred: true, - done: true, + ready: false, + in_progress: false, + blocked: false, + deferred: true, + done: true, }); const [agentMessagesByName, setAgentMessagesByName] = useState>({}); const [agentUnreadByName, setAgentUnreadByName] = useState>({}); @@ -243,100 +245,102 @@ export function SocialPage({ }); await refreshCoordination(); }; - - return ( -
-
-
-
-

Social Stream

-

Task Activity Command Feed

-
-
- - {projectScopeOptions.length} scopes - - - {visibleCards.length} tasks - -
-
- -
- {(Object.keys(SECTION_LABEL) as SectionKey[]).map((key) => { - const cardsForSection = grouped[key]; - return ( -
-
-

- {SECTION_LABEL[key]} -

- - {cardsForSection.length} - - {(key === 'deferred' || key === 'done') ? ( - - ) : null} -
- - {collapsedSections[key] ? ( -

- {cardsForSection.length === 0 - ? `No tasks in ${SECTION_LABEL[key].toLowerCase()}.` - : `${cardsForSection.length} tasks hidden.`} -

- ) : ( -
- {(expandedSections[key] ? cardsForSection : cardsForSection.slice(0, 3)).map((card) => { - const issue = issueById.get(card.id); - const commentCount = typeof issue?.metadata?.commentCount === 'number' ? issue.metadata.commentCount : 0; - const unreadCount = typeof issue?.metadata?.unreadCount === 'number' ? issue.metadata.unreadCount : 0; - const dependencyCount = issue?.dependencies.length ?? card.blocks.length + card.unblocks.length; - - return ( + + return ( +
+
+
+
+

Social Stream

+

Task Activity Command Feed

+
+
+ + {projectScopeOptions.length} scopes + + + {visibleCards.length} tasks + +
+
+ +
+ {(Object.keys(SECTION_LABEL) as SectionKey[]).map((key) => { + const cardsForSection = grouped[key]; + return ( +
+
+

+ {SECTION_LABEL[key]} +

+ + {cardsForSection.length} + + {(key === 'deferred' || key === 'done') ? ( + + ) : null} +
+ + {collapsedSections[key] ? ( +

+ {cardsForSection.length === 0 + ? `No tasks in ${SECTION_LABEL[key].toLowerCase()}.` + : `${cardsForSection.length} tasks hidden.`} +

+ ) : ( +
+ {(expandedSections[key] ? cardsForSection : cardsForSection.slice(0, 3)).map((card) => { + const issue = issueById.get(card.id); + const commentCount = typeof issue?.metadata?.commentCount === 'number' ? issue.metadata.commentCount : 0; + const unreadCount = typeof issue?.metadata?.unreadCount === 'number' ? issue.metadata.unreadCount : 0; + const dependencyCount = issue?.dependencies.length ?? card.blocks.length + card.unblocks.length; + + return ( onSelect(card.id)} - onJumpToGraph={(id) => - navigateWithParams({ - view: 'graph', - graphTab: 'flow', - task: id, - swarm: null, - right: 'open', - panel: 'open', - drawer: 'closed', - }) - } - onJumpToActivity={(id) => - navigateWithParams({ - task: id, - right: 'open', - panel: 'open', - drawer: 'closed', - }) - } - onOpenThread={() => onSelect(card.id)} - description={issue?.description ?? undefined} - updatedLabel={issue ? formatRelative(issue.updated_at) : 'just now'} - dependencyCount={dependencyCount} - commentCount={commentCount} - unreadCount={unreadCount} - blockedByDetails={toDependencyDetails(card.unblocks)} - unblocksDetails={toDependencyDetails(card.blocks)} - archetypes={archetypes} - swarmId={swarmId} + key={card.id} + data={card} + selected={selectedId === card.id} + onClick={() => onSelect(card.id)} + onJumpToGraph={(id) => + navigateWithParams({ + view: 'graph', + graphTab: 'flow', + task: id, + swarm: null, + right: 'open', + panel: 'open', + drawer: 'closed', + }) + } + onJumpToActivity={(id) => + navigateWithParams({ + task: id, + right: 'open', + panel: 'open', + drawer: 'closed', + }) + } + onOpenThread={() => onSelect(card.id)} + description={issue?.description ?? undefined} + updatedLabel={issue ? formatRelative(issue.updated_at) : 'just now'} + dependencyCount={dependencyCount} + commentCount={commentCount} + unreadCount={unreadCount} + blockedByDetails={toDependencyDetails(card.unblocks)} + unblocksDetails={toDependencyDetails(card.blocks)} + archetypes={archetypes} + projectRoot={projectRoot} + swarmId={swarmId} onLaunchSwarm={onRocketClick} + onAskOrchestrator={() => onAskOrchestrator?.(card.id)} agentUnreadByName={agentUnreadByName} agentMessagesByName={agentMessagesByName} agentReservationsByName={agentReservationsByName} @@ -344,38 +348,38 @@ export function SocialPage({ /> ); })} - {cardsForSection.length === 0 ? ( -

- No tasks in this lane. -

- ) : null} -
- )} - - {!collapsedSections[key] && cardsForSection.length > 3 ? ( -
- -
- ) : null} -
- ); - })} -
- - {visibleCards.length === 0 ? ( -

- No blocked tasks right now. -

- ) : null} -
-
- ); -} + {cardsForSection.length === 0 ? ( +

+ No tasks in this lane. +

+ ) : null} +
+ )} + + {!collapsedSections[key] && cardsForSection.length > 3 ? ( +
+ +
+ ) : null} +
+ ); + })} +
+ + {visibleCards.length === 0 ? ( +

+ No blocked tasks right now. +

+ ) : null} +
+
+ ); +} diff --git a/src/components/swarm/agent-inspector.tsx b/src/components/swarm/agent-inspector.tsx new file mode 100644 index 0000000..210ea3b --- /dev/null +++ b/src/components/swarm/agent-inspector.tsx @@ -0,0 +1,484 @@ +"use client"; + +import React, { useState, useEffect, useMemo } from 'react'; +import { X, Save, ShieldAlert, Trash2, Plus, Copy, Palette, Smile } from 'lucide-react'; +import type { AgentArchetype } from '../../lib/types-swarm'; + +const COLOR_PRESETS = [ + '#3b82f6', '#2563eb', '#1d4ed8', '#0ea5e9', '#06b6d4', + '#10b981', '#059669', '#22c55e', '#84cc16', '#a3e635', + '#8b5cf6', '#7c3aed', '#a855f7', '#c084fc', '#e879f9', + '#ef4444', '#dc2626', '#f97316', '#fb923c', '#fbbf24', + '#ec4899', '#db2777', '#f472b6', '#f9a8d4', '#fda4af', + '#6366f1', '#64748b', '#78716c', '#57534e', '#1e293b', +]; + +const EMOJI_PRESETS = [ + '🏗️', '⚙️', '🔍', '🧪', '🚀', '🤖', '👨‍💻', '👩‍💻', '🧙‍♂️', '🧙‍♀️', + '🔧', '📝', '🎯', '⚡', '🛡️', '📊', '🗂️', '💡', '🔮', '🧩', + '⭐', '🔥', '💎', '🚦', '🎪', '🎨', '🎭', '🃏', '👑', '🏆', + '🦅', '🐺', '🦁', '🐻', '🦊', '🐙', '🐝', '🦋', '🌿', '🌊', +]; + +const SUGGESTED_CAPABILITIES = [ + 'coding', 'testing', 'debugging', 'refactoring', 'documentation', + 'code_review', 'system_design', 'architecture', 'planning', 'analysis', + 'research', 'investigation', 'deployment', 'ci_cd', 'monitoring', + 'security', 'performance', 'optimization', 'integration', 'migration', + 'data_analysis', 'automation', 'scripting', 'api_design', 'database', + 'frontend', 'backend', 'devops', 'qa', 'mentoring', +]; + +interface AgentInspectorProps { + archetype?: AgentArchetype; + onClose: () => void; + onSave: (data: Partial) => Promise; + onDelete?: (id: string) => Promise; + onClone?: (archetype: AgentArchetype) => Promise; +} + +export function AgentInspector({ archetype, onClose, onSave, onDelete, onClone }: AgentInspectorProps) { + const isNew = !archetype; + + const [name, setName] = useState(archetype?.name || ''); + const [description, setDescription] = useState(archetype?.description || ''); + const [systemPrompt, setSystemPrompt] = useState(archetype?.systemPrompt || ''); + const [capabilities, setCapabilities] = useState(archetype?.capabilities || []); + const [color, setColor] = useState(archetype?.color || '#3b82f6'); + const [icon, setIcon] = useState(archetype?.icon || ''); + const [newCapability, setNewCapability] = useState(''); + const [capabilityFilter, setCapabilityFilter] = useState(''); + const [isSaving, setIsSaving] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + const [isCloning, setIsCloning] = useState(false); + const [error, setError] = useState(null); + const [showEmojiPicker, setShowEmojiPicker] = useState(false); + const [showColorPicker, setShowColorPicker] = useState(false); + const [showCapabilityDropdown, setShowCapabilityDropdown] = useState(false); + + useEffect(() => { + if (archetype) { + setName(archetype.name); + setDescription(archetype.description); + setSystemPrompt(archetype.systemPrompt); + setCapabilities(archetype.capabilities); + setColor(archetype.color); + setIcon(archetype.icon || ''); + } + }, [archetype]); + + const filteredSuggestions = useMemo(() => { + return SUGGESTED_CAPABILITIES.filter( + cap => + cap.includes(capabilityFilter.toLowerCase()) && + !capabilities.includes(cap) + ).slice(0, 6); + }, [capabilityFilter, capabilities]); + + const handleAddCapability = (cap?: string) => { + const toAdd = cap || newCapability.trim(); + if (toAdd && !capabilities.includes(toAdd.toLowerCase())) { + setCapabilities([...capabilities, toAdd.toLowerCase()]); + setNewCapability(''); + setCapabilityFilter(''); + setShowCapabilityDropdown(false); + } + }; + + const handleRemoveCapability = (index: number) => { + setCapabilities(capabilities.filter((_, i) => i !== index)); + }; + + const handleSave = async () => { + if (!name.trim() || !systemPrompt.trim()) { + setError('Name and System Prompt are required'); + return; + } + + setIsSaving(true); + setError(null); + + try { + await onSave({ + id: archetype?.id, + name: name.trim(), + description: description.trim(), + systemPrompt: systemPrompt.trim(), + capabilities, + color, + icon: icon || undefined, + isBuiltIn: archetype?.isBuiltIn + }); + onClose(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to save'); + } finally { + setIsSaving(false); + } + }; + + const handleDelete = async () => { + if (!archetype || !onDelete) return; + + if (!confirm(`Delete archetype "${archetype.name}"? This cannot be undone.`)) return; + + setIsDeleting(true); + setError(null); + + try { + await onDelete(archetype.id); + onClose(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to delete'); + } finally { + setIsDeleting(false); + } + }; + + const handleClone = async () => { + if (!archetype || !onClone) return; + + setIsCloning(true); + setError(null); + + try { + await onClone(archetype); + onClose(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to clone'); + } finally { + setIsCloning(false); + } + }; + + const displayChar = icon || name.charAt(0) || '?'; + + return ( +
+
+
+
+
+ {displayChar} +
+
+

+ {isNew ? 'New Archetype' : name || 'Edit Archetype'} +

+ {!isNew && ( +

{archetype.id}

+ )} +
+
+ +
+ + {error && ( +
+ + {error} +
+ )} + +
+
+
+ + setName(e.target.value)} + className="w-full px-3 py-2 rounded-lg bg-[var(--ui-bg-soft)] border border-[var(--ui-border-soft)] text-[var(--ui-text-primary)] placeholder:text-[var(--ui-text-muted)] focus:outline-none focus:ring-2 focus:ring-blue-500/50" + placeholder="e.g., Code Reviewer" + /> +
+
+ + setDescription(e.target.value)} + className="w-full px-3 py-2 rounded-lg bg-[var(--ui-bg-soft)] border border-[var(--ui-border-soft)] text-[var(--ui-text-primary)] placeholder:text-[var(--ui-text-muted)] focus:outline-none focus:ring-2 focus:ring-blue-500/50" + placeholder="Brief description" + /> +
+
+ +
+ +