From f5c7666f6b859a0a8d2eda8375c3580ad8c17b86 Mon Sep 17 00:00:00 2001 From: oss-sync Date: Wed, 3 Jun 2026 05:08:00 +0000 Subject: [PATCH] feat: initial public release (MAESTRO) --- .env.example | 3 + .gitignore | 41 + AGENTS.md | 134 + CHANGELOG.md | 19 + CONTRIBUTING.md | 78 + Dockerfile | 72 + GEMINI.md | 38 + LICENSE | 201 + NOTICE | 7 + README.md | 67 + bench/fixtures/notes.md | 7 + bench/fixtures/sales.xlsx | Bin 0 -> 6696 bytes bench/fixtures/web/announcement.html | 22 + bench/tasks/composite-mini-report.yaml | 105 + bench/tasks/reflection-smoke.yaml | 115 + config.yaml.example | 395 + deploy/maestro.service | 25 + docker-compose.yml | 36 + docs/aao-gateway-overview.md | 239 + docs/architecture.md | 85 + docs/bench.md | 212 + docs/configuration.md | 229 + docs/context-flow.md | 235 + docs/design/README.md | 151 + docs/design/colors_and_type.css | 128 + .../admin-legacy/ChatPane.jsx | 128 + .../admin-legacy/DetailPanel.jsx | 134 + .../admin-legacy/Primitives.jsx | 86 + .../ui_kits_reference/admin-legacy/README.md | 12 + .../admin-legacy/TaskList.jsx | 92 + .../ui_kits_reference/admin-legacy/TopBar.jsx | 62 + .../ui_kits_reference/admin-legacy/index.html | 231 + .../ui_kits_reference/admin/ChatPane.jsx | 197 + .../ui_kits_reference/admin/DetailPanel.jsx | 134 + .../ui_kits_reference/admin/Primitives.jsx | 169 + docs/design/ui_kits_reference/admin/README.md | 34 + .../ui_kits_reference/admin/SchedulesPage.jsx | 427 + .../ui_kits_reference/admin/SettingsPage.jsx | 318 + .../ui_kits_reference/admin/TaskList.jsx | 199 + .../design/ui_kits_reference/admin/TopBar.jsx | 57 + .../ui_kits_reference/admin/UsersPage.jsx | 313 + .../design/ui_kits_reference/admin/index.html | 399 + .../design/ui_kits_reference/compare-cta.html | 31 + docs/getting-started.md | 86 + docs/maintenance-checklist.md | 554 ++ docs/mcp.md | 250 + docs/operations/bash-sandbox-provisioning.md | 101 + docs/skills.md | 87 + docs/ssh.md | 996 +++ docs/tools/annotateimage.md | 54 + docs/tools/bash.md | 87 + docs/tools/brainstorm.md | 162 + docs/tools/browse-sessions.md | 48 + docs/tools/browseweb.md | 287 + docs/tools/checklist.md | 42 + docs/tools/downloadfile.md | 50 + docs/tools/getyoutubetranscript.md | 51 + docs/tools/listpieces.md | 77 + docs/tools/listuserassets.md | 53 + docs/tools/notes.md | 97 + docs/tools/office.md | 150 + docs/tools/readimage.md | 38 + docs/tools/readusermemory.md | 51 + docs/tools/readusertemplate.md | 85 + docs/tools/renderusertemplate.md | 108 + docs/tools/runuserscript.md | 130 + docs/tools/searchamazon.md | 41 + docs/tools/searchknowledge.md | 103 + docs/tools/searchmicrosoftlearn.md | 112 + docs/tools/searchplaces.md | 49 + docs/tools/slide.md | 115 + docs/tools/spawnsubtask.md | 59 + docs/tools/sqlite.md | 42 + docs/tools/ssh-console-tools.md | 148 + docs/tools/ssh-tools.md | 271 + docs/tools/transcribeaudio.md | 57 + docs/tools/updatedashboardwidget.md | 43 + docs/tools/updateusermemory.md | 81 + docs/tools/webfetch.md | 69 + docs/tools/websearch.md | 51 + docs/tools/writeuserscript.md | 139 + docs/tools/writeusertemplate.md | 133 + docs/tools/xsearch.md | 134 + docs/user-folder-layout.md | 199 + package-lock.json | 7370 +++++++++++++++++ package.json | 77 + pieces/SCHEMA.md | 98 + pieces/brainstorming.yaml | 121 + pieces/chat.yaml | 73 + pieces/data-process.yaml | 128 + pieces/game-tweet-generator.yaml | 86 + pieces/general.yaml | 184 + pieces/help.yaml | 100 + pieces/office-process.yaml | 119 + pieces/piece-builder.yaml | 155 + pieces/research-sub.yaml | 110 + pieces/research.yaml | 206 + pieces/slide.yaml | 192 + pieces/sns-research.yaml | 147 + pieces/ssh-console.yaml | 76 + pieces/ssh-ops.yaml | 131 + pieces/x-ai-digest.yaml | 197 + runtime/python-requirements.txt | 37 + scripts/bench-run.ts | 162 + scripts/build-all.sh | 52 + scripts/build-bench-fixtures.ts | 43 + scripts/gateway.sh | 162 + scripts/generate-version.sh | 14 + scripts/install-twitter-cli.sh | 97 + scripts/lint-pieces.mjs | 90 + scripts/migrate-config.sh | 41 + scripts/prebake-python.sh | 78 + scripts/prepare.sh | 133 + scripts/server.sh | 126 + scripts/setup-novnc.sh | 98 + scripts/setup-repo.sh | 113 + scripts/setup.sh | 275 + scripts/validate-help-docs.mjs | 91 + scripts/vapid-rotate.ts | 49 + .../v1-gateway-server-with-keys.yaml | 41 + .../config-migration/v1-mcp-and-ssh.yaml | 29 + .../v1-multi-worker-with-proxy.yaml | 42 + .../config-migration/v1-single-ollama.yaml | 19 + src/bench/fixture-server.ts | 83 + src/bench/grader.test.ts | 222 + src/bench/grader.ts | 266 + src/bench/judge.ts | 147 + src/bench/runner.ts | 189 + src/bench/summary.ts | 104 + src/bench/types.ts | 96 + src/bridge/admin-api.test.ts | 91 + src/bridge/admin-api.ts | 51 + .../admin-gateway-api.budget-rate.test.ts | 402 + .../admin-gateway-api.metric-labels.test.ts | 136 + src/bridge/admin-gateway-api.test.ts | 247 + src/bridge/admin-gateway-api.ts | 554 ++ src/bridge/admin-gateway-status-api.ts | 72 + src/bridge/auth-login.html | 335 + src/bridge/auth-pending.html | 221 + src/bridge/auth.test.ts | 174 + src/bridge/auth.ts | 636 ++ src/bridge/branding-api.test.ts | 211 + src/bridge/branding-api.ts | 244 + src/bridge/browser-api.test.ts | 318 + src/bridge/browser-api.ts | 272 + src/bridge/browser-session-api.test.ts | 277 + src/bridge/browser-session-api.ts | 483 ++ src/bridge/config-api.test.ts | 509 ++ src/bridge/config-api.ts | 260 + src/bridge/console-admin-api.test.ts | 42 + src/bridge/console-admin-api.ts | 27 + src/bridge/console-ws-api.test.ts | 356 + src/bridge/console-ws-api.ts | 291 + src/bridge/dashboard-api.test.ts | 335 + src/bridge/dashboard-api.ts | 277 + src/bridge/dashboard-workers.test.ts | 173 + src/bridge/dashboard-workers.ts | 121 + src/bridge/gateway-mount.test.ts | 515 ++ src/bridge/gateway-mount.ts | 488 ++ src/bridge/job-events.ts | 46 + src/bridge/local-api-helpers.ts | 61 + src/bridge/local-files-api.ts | 176 + src/bridge/local-tasks-api.test.ts | 913 ++ src/bridge/local-tasks-api.ts | 670 ++ src/bridge/mcp-api.test.ts | 327 + src/bridge/mcp-api.ts | 418 + src/bridge/memory-api.test.ts | 245 + src/bridge/memory-api.ts | 194 + src/bridge/notes-api.test.ts | 203 + src/bridge/notes-api.ts | 168 + src/bridge/notifications-api.test.ts | 250 + src/bridge/notifications-api.ts | 291 + src/bridge/novnc-proxy.ts | 170 + src/bridge/pieces-api.test.ts | 499 ++ src/bridge/pieces-api.ts | 356 + src/bridge/reflection-api.test.ts | 414 + src/bridge/reflection-api.ts | 315 + src/bridge/scheduled-tasks-api.test.ts | 534 ++ src/bridge/scheduled-tasks-api.ts | 345 + src/bridge/server.test.ts | 290 + src/bridge/server.ts | 1190 +++ src/bridge/share-api.test.ts | 186 + src/bridge/share-api.ts | 174 + src/bridge/shutdown.test.ts | 110 + src/bridge/shutdown.ts | 128 + src/bridge/skills-api.ts | 420 + src/bridge/skills-git-install.test.ts | 46 + src/bridge/skills-git-install.ts | 363 + src/bridge/ssh-api.test.ts | 787 ++ src/bridge/ssh-api.ts | 1450 ++++ src/bridge/subtask-activity-api.test.ts | 277 + src/bridge/subtask-activity-api.ts | 94 + src/bridge/subtask-files-api.ts | 102 + src/bridge/tools-api.test.ts | 229 + src/bridge/tools-api.ts | 311 + src/bridge/user-folder-api.agents-md.test.ts | 110 + src/bridge/user-folder-api.test.ts | 1109 +++ src/bridge/user-folder-api.ts | 992 +++ src/bridge/users-api.ts | 64 + src/bridge/validation.test.ts | 100 + src/bridge/validation.ts | 142 + src/bridge/visibility.test.ts | 86 + src/bridge/visibility.ts | 48 + src/bridge/yaml-patch.test.ts | 176 + src/bridge/yaml-patch.ts | 169 + src/config-auth.test.ts | 67 + src/config-manager.test.ts | 275 + src/config-manager.ts | 312 + src/config-normalize.test.ts | 547 ++ src/config-normalize.ts | 505 ++ src/config.test.ts | 589 ++ src/config.ts | 893 ++ src/crypto/sessions.test.ts | 51 + src/crypto/sessions.ts | 61 + src/db/browser-session-migration.test.ts | 83 + src/db/browser-session-repo.test.ts | 89 + src/db/browser-session-repo.ts | 173 + src/db/dashboard-widgets-repository.test.ts | 86 + src/db/migrate.gateway-2b.test.ts | 104 + src/db/migrate.notes.test.ts | 59 + src/db/migrate.reflection-columns.test.ts | 413 + src/db/migrate.test.ts | 204 + src/db/migrate.ts | 534 ++ src/db/repository-auth.test.ts | 267 + src/db/repository.gateway-keys.test.ts | 195 + src/db/repository.gateway-usage.test.ts | 182 + src/db/repository.test.ts | 1655 ++++ src/db/repository.ts | 3490 ++++++++ src/db/schema.sql | 592 ++ src/engine/agent-loop-console.test.ts | 164 + src/engine/agent-loop.notes-inject.test.ts | 133 + src/engine/agent-loop.test.ts | 2368 ++++++ src/engine/agent-loop.ts | 2373 ++++++ src/engine/agent-loop.user-agents.test.ts | 221 + src/engine/backend-probes.test.ts | 452 + src/engine/backend-probes.ts | 530 ++ src/engine/backend-status-registry.test.ts | 433 + src/engine/backend-status-registry.ts | 370 + src/engine/browser-launch.test.ts | 166 + src/engine/browser-launch.ts | 87 + src/engine/browser-recorder.test.ts | 270 + src/engine/browser-recorder.ts | 190 + src/engine/browser-session-auth.test.ts | 95 + src/engine/browser-session-auth.ts | 47 + src/engine/browser-session-expiry.test.ts | 46 + src/engine/browser-session-expiry.ts | 40 + src/engine/browser-session.test.ts | 130 + src/engine/browser-session.ts | 389 + src/engine/context-manager.test.ts | 187 + src/engine/context-manager.ts | 217 + src/engine/context/atomic-json.test.ts | 142 + src/engine/context/atomic-json.ts | 113 + src/engine/context/cache-key.ts | 123 + src/engine/context/file-read-dedup.test.ts | 169 + src/engine/context/file-read-dedup.ts | 70 + src/engine/context/history-compactor.test.ts | 250 + src/engine/context/history-compactor.ts | 287 + src/engine/context/invalidation.ts | 43 + src/engine/context/memory-delta.test.ts | 292 + src/engine/context/memory-delta.ts | 243 + src/engine/context/memory-handoff.test.ts | 205 + src/engine/context/memory-handoff.ts | 252 + src/engine/context/path-normalize.test.ts | 82 + src/engine/context/path-normalize.ts | Bin 0 -> 3616 bytes src/engine/context/prompt-guard.test.ts | 276 + src/engine/context/prompt-guard.ts | 231 + src/engine/context/token-estimate.ts | 91 + src/engine/context/tool-result-cache.test.ts | 244 + src/engine/context/tool-result-cache.ts | 112 + src/engine/context/workspace-memory.test.ts | 192 + src/engine/context/workspace-memory.ts | 836 ++ src/engine/llm-stream.test.ts | 178 + src/engine/llm-stream.ts | 205 + src/engine/local-context.test.ts | 80 + src/engine/local-context.ts | 82 + src/engine/notes-inject.test.ts | 94 + src/engine/notes-inject.ts | 82 + src/engine/piece-catalog.test.ts | 131 + src/engine/piece-catalog.ts | 112 + src/engine/piece-classifier.test.ts | 64 + src/engine/piece-classifier.ts | 116 + src/engine/piece-runner.test.ts | 1113 +++ src/engine/piece-runner.ts | 1767 ++++ .../reflection/activity-summarizer.test.ts | 36 + src/engine/reflection/activity-summarizer.ts | 79 + src/engine/reflection/applier.fuzz.test.ts | 347 + src/engine/reflection/applier.test.ts | 652 ++ src/engine/reflection/applier.ts | 338 + src/engine/reflection/drift-detect.test.ts | 111 + src/engine/reflection/drift-detect.ts | 56 + src/engine/reflection/llm-client.test.ts | 74 + src/engine/reflection/llm-client.ts | 59 + src/engine/reflection/load-inputs.test.ts | 285 + src/engine/reflection/load-inputs.ts | 191 + src/engine/reflection/piece-writer.test.ts | 227 + src/engine/reflection/piece-writer.ts | 124 + src/engine/reflection/reflection-prompt.ts | 59 + src/engine/reflection/reflection-runner.ts | 249 + src/engine/reflection/reflection-schema.ts | 47 + src/engine/reflection/retention.test.ts | 273 + src/engine/reflection/retention.ts | 312 + src/engine/reflection/revisions.ts | 20 + .../reflection/semantic-validator.test.ts | 247 + src/engine/reflection/semantic-validator.ts | 233 + src/engine/reflection/silent-fork.test.ts | 186 + src/engine/reflection/silent-fork.ts | 76 + src/engine/reflection/snapshot.test.ts | 525 ++ src/engine/reflection/snapshot.ts | 539 ++ src/engine/reflection/types.ts | 62 + src/engine/reflection/user-lock.test.ts | 69 + src/engine/reflection/user-lock.ts | 34 + src/engine/skills-scanner.test.ts | 276 + src/engine/skills-scanner.ts | 200 + src/engine/skills.test.ts | 413 + src/engine/skills.ts | 193 + src/engine/strip-thinking.ts | 17 + .../tools/__fixtures__/slide/all-layouts.json | 11 + src/engine/tools/amazon.ts | 292 + src/engine/tools/app-docs.test.ts | 301 + src/engine/tools/app-docs.ts | 718 ++ src/engine/tools/brainstorm.test.ts | 88 + src/engine/tools/brainstorm.ts | 151 + .../tools/browser-frame-chain.e2e.test.ts | 118 + src/engine/tools/browser.test.ts | 468 ++ src/engine/tools/browser.ts | 1830 ++++ src/engine/tools/checklist.test.ts | 437 + src/engine/tools/checklist.ts | 271 + src/engine/tools/dashboard.test.ts | 114 + src/engine/tools/dashboard.ts | 115 + src/engine/tools/data.ts | 229 + src/engine/tools/docs.test.ts | 98 + src/engine/tools/docs.ts | 185 + src/engine/tools/image.test.ts | 347 + src/engine/tools/image.ts | 464 ++ src/engine/tools/index.test.ts | 22 + src/engine/tools/index.ts | 672 ++ src/engine/tools/knowledge.test.ts | 175 + src/engine/tools/knowledge.ts | 477 ++ src/engine/tools/maps.ts | 748 ++ src/engine/tools/mission.test.ts | 79 + src/engine/tools/mission.ts | 124 + src/engine/tools/ms-learn.ts | 564 ++ src/engine/tools/notes.test.ts | 79 + src/engine/tools/notes.ts | 190 + src/engine/tools/office-types.ts | 81 + src/engine/tools/office.test.ts | 361 + src/engine/tools/office.ts | 2268 +++++ src/engine/tools/orchestration.ts | 80 + src/engine/tools/pieces.test.ts | 131 + src/engine/tools/pieces.ts | 310 + src/engine/tools/raw-save.test.ts | 59 + src/engine/tools/raw-save.ts | 102 + src/engine/tools/review.test.ts | 144 + src/engine/tools/review.ts | 457 + src/engine/tools/sandbox.test.ts | 260 + src/engine/tools/sandbox.ts | 168 + src/engine/tools/shared/html.test.ts | 54 + src/engine/tools/shared/html.ts | 32 + src/engine/tools/shared/ssrf.test.ts | 76 + src/engine/tools/shared/ssrf.ts | 84 + src/engine/tools/skills.test.ts | 494 ++ src/engine/tools/skills.ts | 373 + src/engine/tools/slide.test.ts | 49 + src/engine/tools/slide.ts | 27 + src/engine/tools/slide/add-slide.test.ts | 124 + src/engine/tools/slide/add-slide.ts | 152 + src/engine/tools/slide/build-pptx.test.ts | 63 + src/engine/tools/slide/build-pptx.ts | 80 + src/engine/tools/slide/layouts.test.ts | 181 + src/engine/tools/slide/layouts.ts | 438 + src/engine/tools/slide/reset-slides.test.ts | 23 + src/engine/tools/slide/reset-slides.ts | 25 + src/engine/tools/slide/set-theme.test.ts | 42 + src/engine/tools/slide/set-theme.ts | 58 + src/engine/tools/slide/state.test.ts | 73 + src/engine/tools/slide/state.ts | 115 + src/engine/tools/slide/themes.test.ts | 33 + src/engine/tools/slide/themes.ts | 62 + src/engine/tools/speech.ts | 161 + src/engine/tools/ssh-console.test.ts | 556 ++ src/engine/tools/ssh-console.ts | 712 ++ src/engine/tools/ssh.e2e.test.ts | 465 ++ src/engine/tools/ssh.test.ts | 651 ++ src/engine/tools/ssh.ts | 1086 +++ src/engine/tools/structured-blocks.ts | 98 + src/engine/tools/user-folder.test.ts | 1195 +++ src/engine/tools/user-folder.ts | 1008 +++ src/engine/tools/web.test.ts | 382 + src/engine/tools/web.ts | 1019 +++ src/engine/tools/x.test.ts | 543 ++ src/engine/tools/x.ts | 872 ++ src/engine/tools/youtube.ts | 539 ++ src/gateway/auth.dual-lookup.test.ts | 290 + src/gateway/auth.test.ts | Bin 0 -> 7560 bytes src/gateway/auth.ts | 397 + src/gateway/bootstrap.metrics.test.ts | 151 + src/gateway/bootstrap.test.ts | 75 + src/gateway/bootstrap.ts | 366 + src/gateway/budget-check.test.ts | 132 + src/gateway/budget-check.ts | 149 + src/gateway/config-migration.test.ts | 452 + src/gateway/config-migration.ts | 335 + src/gateway/config.test.ts | 254 + src/gateway/config.ts | 364 + src/gateway/health-endpoint.test.ts | 158 + src/gateway/health-endpoint.ts | 155 + src/gateway/key-cache.test.ts | 207 + src/gateway/key-cache.ts | 190 + src/gateway/key-format.test.ts | 139 + src/gateway/key-format.ts | 140 + src/gateway/models-endpoint.test.ts | 24 + src/gateway/models-endpoint.ts | 43 + src/gateway/period.test.ts | 60 + src/gateway/period.ts | 44 + src/gateway/phase2a-integration.test.ts | 154 + src/gateway/rate-limiter.test.ts | 350 + src/gateway/rate-limiter.ts | 287 + src/gateway/router.test.ts | 281 + src/gateway/router.ts | 203 + src/gateway/server.metrics-endpoint.test.ts | 74 + src/gateway/server.ts | 199 + src/gateway/shared-dependencies.test.ts | 272 + src/gateway/shared-dependencies.ts | 432 + src/gateway/stream-proxy.metrics.test.ts | 262 + src/gateway/stream-proxy.test.ts | 613 ++ src/gateway/stream-proxy.ts | 805 ++ .../stream-proxy.usage-extraction.test.ts | 335 + src/git/workspace-manager.test.ts | 39 + src/git/workspace-manager.ts | 88 + src/index.ts | 21 + src/llm/openai-compat.test.ts | 760 ++ src/llm/openai-compat.ts | 686 ++ src/logger.ts | 28 + src/main.test.ts | 39 + src/main.ts | 49 + src/mcp/aggregator.test.ts | 80 + src/mcp/aggregator.ts | 78 + src/mcp/binary-saver.test.ts | 88 + src/mcp/binary-saver.ts | 101 + src/mcp/client-factory.ts | 61 + src/mcp/config.test.ts | 13 + src/mcp/config.ts | 28 + src/mcp/crypto.test.ts | 113 + src/mcp/crypto.ts | 71 + src/mcp/discovery.test.ts | 70 + src/mcp/discovery.ts | 150 + src/mcp/integration.test.ts | 350 + src/mcp/oauth-routes.test.ts | 158 + src/mcp/oauth-routes.ts | 184 + src/mcp/raw-logger.test.ts | 203 + src/mcp/raw-logger.ts | 115 + src/mcp/redact.test.ts | 31 + src/mcp/redact.ts | 31 + src/mcp/registry.test.ts | 185 + src/mcp/registry.ts | 208 + src/mcp/ssrf-strict.pinned.test.ts | 27 + src/mcp/ssrf-strict.test.ts | 67 + src/mcp/ssrf-strict.ts | 22 + src/mcp/testing/mock-mcp-server.ts | 254 + src/mcp/token-manager.test.ts | 136 + src/mcp/token-manager.ts | 182 + src/mcp/tool-adapter.test.ts | 60 + src/mcp/tool-adapter.ts | 75 + src/mcp/tool-cache.ts | 87 + src/mcp/tool-executor.test.ts | 127 + src/mcp/tool-executor.ts | 136 + src/mcp/types.ts | 90 + src/metrics/gateway-metrics.test.ts | 94 + src/metrics/gateway-metrics.ts | 223 + src/metrics/http-handler.test.ts | 170 + src/metrics/http-handler.ts | 133 + src/metrics/registry.test.ts | 44 + src/metrics/registry.ts | 97 + src/metrics/tool-name-allowlist.test.ts | 61 + src/metrics/tool-name-allowlist.ts | 114 + src/metrics/worker-metrics.test.ts | 67 + src/metrics/worker-metrics.ts | 104 + src/net/ssrf-strict.test.ts | 127 + src/net/ssrf-strict.ts | 270 + src/notes/fm.test.ts | 92 + src/notes/fm.ts | 106 + src/notes/notes-repository.test.ts | 216 + src/notes/notes-repository.ts | 190 + src/notes/notes-service.test.ts | 366 + src/notes/notes-service.ts | 401 + src/progress/event-log.test.ts | 208 + src/progress/event-log.ts | 313 + src/progress/local-reporter.test.ts | 180 + src/progress/local-reporter.ts | 155 + src/progress/log-format.test.ts | 56 + src/progress/log-format.ts | 81 + src/push-service.load.test.ts | 137 + src/push-service.test.ts | 256 + src/push-service.ts | 241 + src/scheduler.test.ts | 478 ++ src/scheduler.ts | 447 + src/scheduling.ts | 38 + src/scripts/migrate-config.test.ts | 127 + src/scripts/migrate-config.ts | 290 + src/ssh/abuse-repo.test.ts | 199 + src/ssh/abuse-repo.ts | 241 + src/ssh/access.test.ts | 190 + src/ssh/access.ts | 90 + src/ssh/admin-rate-limit.test.ts | 64 + src/ssh/admin-rate-limit.ts | 73 + src/ssh/algorithms.test.ts | 99 + src/ssh/algorithms.ts | 87 + src/ssh/audit-repo.test.ts | 144 + src/ssh/audit-repo.ts | 201 + src/ssh/config.test.ts | 35 + src/ssh/config.ts | 103 + src/ssh/connection-repo.test.ts | 329 + src/ssh/connection-repo.ts | 473 ++ src/ssh/console-deny-check.test.ts | 47 + src/ssh/console-deny-check.ts | 61 + src/ssh/console-protocol.ts | 58 + src/ssh/console-registry.test.ts | 175 + src/ssh/console-registry.ts | 149 + src/ssh/console-session.test.ts | 184 + src/ssh/console-session.ts | 342 + src/ssh/crypto.test.ts | 302 + src/ssh/crypto.ts | 271 + src/ssh/deny-list.test.ts | 201 + src/ssh/deny-list.ts | 229 + src/ssh/grants-repo.test.ts | 256 + src/ssh/grants-repo.ts | 200 + src/ssh/maintenance.test.ts | 41 + src/ssh/maintenance.ts | 98 + src/ssh/output.test.ts | 160 + src/ssh/output.ts | 105 + src/ssh/path-policy.test.ts | 242 + src/ssh/path-policy.ts | 221 + src/ssh/recovery.ts | 29 + src/ssh/ring-buffer.test.ts | 42 + src/ssh/ring-buffer.ts | 55 + src/ssh/session-test-server.ts | 386 + src/ssh/session.test.ts | 322 + src/ssh/session.ts | 766 ++ src/ssh/ssrf.test.ts | 103 + src/ssh/ssrf.ts | 86 + src/title-generation.test.ts | 29 + src/title-generation.ts | 28 + src/user-folder/frontmatter.test.ts | 189 + src/user-folder/frontmatter.ts | 143 + src/user-folder/memory.test.ts | 367 + src/user-folder/memory.ts | 551 ++ src/user-folder/paths.notes.test.ts | 20 + src/user-folder/paths.test.ts | 94 + src/user-folder/paths.ts | 125 + src/user-folder/pets.ts | 492 ++ src/user-folder/recording-flush.test.ts | 187 + src/user-folder/recording-flush.ts | 103 + src/user-folder/recording-to-run.e2e.test.ts | 128 + src/user-folder/script-compiler.test.ts | 263 + src/user-folder/script-compiler.ts | 204 + src/user-folder/script-orchestrator.ts | 195 + src/user-folder/script-runner-child.ts | 163 + src/user-folder/script-runner.test.ts | 376 + src/user-folder/script-runner.ts | 446 + src/user-folder/session-loader.ts | 92 + src/user-folder/template-renderer.ts | 32 + src/user-folder/trash-cleanup.test.ts | 183 + src/user-folder/trash-cleanup.ts | 167 + src/vapid-store.test.ts | 123 + src/vapid-store.ts | 150 + src/worker-bootstrap.test.ts | 74 + src/worker-bootstrap.ts | 294 + src/worker-manager.test.ts | 8 + src/worker-manager.ts | 177 + src/worker.metrics.test.ts | 59 + src/worker.reflection-dispatch.test.ts | 114 + src/worker.reflection-enqueue.test.ts | 307 + src/worker.test.ts | 360 + src/worker.ts | 1877 +++++ src/worker/sticky-backend.test.ts | 133 + src/worker/sticky-backend.ts | 81 + test-smoke.ts | 95 + .../fixtures/browser-macros/click-and-read.js | 31 + .../fixtures/browser-macros/no-frontmatter.js | 14 + tests/fixtures/scripts/circular.js | 10 + tests/fixtures/scripts/expected-compiled.js | 25 + tests/fixtures/scripts/logs-to-stderr.js | 6 + tests/fixtures/scripts/return-42.js | 2 + tests/fixtures/scripts/throws.js | 2 + tests/fixtures/scripts/timeout.js | 7 + tsconfig.json | 20 + ui/index.html | 43 + ui/package-lock.json | 4662 +++++++++++ ui/package.json | 38 + ui/postcss.config.js | 6 + ui/public/apple-touch-icon-180.png | Bin 0 -> 4702 bytes ui/public/favicon.svg | 17 + ui/public/icon-192.png | Bin 0 -> 4678 bytes ui/public/icon-512.png | Bin 0 -> 26587 bytes ui/public/icon-maskable-512.png | Bin 0 -> 15828 bytes ui/public/manifest.webmanifest | 32 + ui/public/sw.js | 137 + ui/src/App.tsx | 665 ++ ui/src/api.ts | 1259 +++ .../components/activity/ActivityEventCard.tsx | 64 + .../components/activity/ActivityTimeline.tsx | 26 + .../browser/BrowserSessionPanel.tsx | 159 + ui/src/components/browser/PipButton.tsx | 71 + .../browser/SaveRecordingButton.tsx | 118 + ui/src/components/chat/ChatMessage.tsx | 414 + ui/src/components/chat/ChatPane.tsx | 442 + ui/src/components/chat/MovementGroup.tsx | 199 + ui/src/components/chat/SubtaskInlineCard.tsx | 154 + ui/src/components/chat/ToolCallsSection.tsx | 195 + ui/src/components/chat/thinkingUtils.ts | 16 + .../components/create/AttachmentDropzone.tsx | 59 + ui/src/components/create/CreateTaskDialog.tsx | 423 + ui/src/components/create/ScheduleFields.tsx | 110 + .../components/dashboard/AddWidgetDialog.tsx | 113 + .../components/dashboard/MarkdownWidget.tsx | 80 + .../components/dashboard/NodeStatusWidget.tsx | 131 + ui/src/components/dashboard/SideInfoPanel.tsx | 82 + .../dashboard/TaskListWithSidePanel.tsx | 60 + ui/src/components/dashboard/WidgetTabBar.tsx | 103 + .../dashboard/WorkerStatusWidget.tsx | 223 + .../components/detail/ContextUsageGauge.tsx | 61 + .../detail/ContinueWithPieceDialog.tsx | 179 + ui/src/components/detail/DetailHeader.tsx | 255 + ui/src/components/detail/DetailPanel.tsx | 292 + ui/src/components/detail/ReflectionBadge.tsx | 49 + ui/src/components/detail/tabs/BrowserTab.tsx | 158 + ui/src/components/detail/tabs/ConsoleTab.tsx | 37 + ui/src/components/detail/tabs/FilesTab.tsx | 23 + ui/src/components/detail/tabs/OutputTab.tsx | 35 + ui/src/components/detail/tabs/OverviewTab.tsx | 323 + ui/src/components/detail/tabs/ProgressTab.tsx | 54 + .../detail/tabs/SubtaskActivitySection.tsx | 76 + .../components/detail/tabs/SubtasksPanel.tsx | 211 + ui/src/components/detail/tabs/TimelineTab.tsx | 35 + ui/src/components/detail/tabs/TraceTab.tsx | 509 ++ .../detail/tabs/console/ConsoleHeader.tsx | 40 + .../detail/tabs/console/MobileKeyboardBar.tsx | 60 + .../tabs/console/ScrollToBottomButton.tsx | 39 + .../detail/tabs/console/TerminalView.tsx | 104 + .../detail/tabs/console/keys.test.ts | 27 + ui/src/components/detail/tabs/console/keys.ts | 11 + .../components/embed/AmazonProductsCard.tsx | 67 + .../components/embed/AmazonProductsDetail.tsx | 92 + ui/src/components/embed/EmbedBlock.tsx | 86 + ui/src/components/embed/EmbedModal.tsx | 67 + ui/src/components/embed/MapPlacesCard.tsx | 43 + ui/src/components/embed/MapPlacesDetail.tsx | 125 + ui/src/components/embed/XPostsCard.tsx | 66 + ui/src/components/embed/XPostsDetail.tsx | 77 + ui/src/components/embed/YouTubeVideosCard.tsx | 70 + .../components/embed/YouTubeVideosDetail.tsx | 67 + ui/src/components/embed/types.ts | 76 + ui/src/components/files/FileBrowser.tsx | 280 + ui/src/components/files/FilePreview.tsx | 791 ++ ui/src/components/layout/NavDrawer.tsx | 218 + ui/src/components/layout/ResizeHandle.tsx | 87 + ui/src/components/layout/TopBar.tsx | 170 + .../layout/VerticalResizeHandle.tsx | 74 + ui/src/components/list/FilterBar.tsx | 187 + ui/src/components/list/RailPanel.tsx | 86 + ui/src/components/list/TaskListItem.tsx | 68 + ui/src/components/list/TaskListPanel.tsx | 148 + ui/src/components/pets/ChatPetOverlay.tsx | 139 + ui/src/components/pets/PetSprite.tsx | 109 + ui/src/components/pets/ToolSpark.tsx | 109 + .../components/settings/AskSubtasksForm.tsx | 26 + ui/src/components/settings/BrandingForm.tsx | 237 + .../settings/BrowserSettingsForm.tsx | 73 + ui/src/components/settings/ConfigForm.tsx | 285 + ui/src/components/settings/ContextForm.tsx | 58 + ui/src/components/settings/ExecutionForm.tsx | 68 + .../settings/GatewayKeyCreateDialog.tsx | 154 + .../settings/GatewayKeyRawKeyDialog.tsx | 163 + .../settings/GatewayKeyUsagePanel.tsx | 150 + .../settings/GatewayKeysSection.tsx | 354 + .../components/settings/GatewayServerForm.tsx | 381 + ui/src/components/settings/HelpText.tsx | 3 + .../settings/KnowledgeNamespacesForm.tsx | 71 + ui/src/components/settings/LlmWorkersForm.tsx | 392 + ui/src/components/settings/McpForm.tsx | 92 + .../settings/MemoryLearningForm.tsx | 983 +++ ui/src/components/settings/MetricsForm.tsx | 110 + ui/src/components/settings/ModelSelect.tsx | 155 + .../components/settings/MovementAccordion.tsx | 112 + ui/src/components/settings/MovementForm.tsx | 93 + .../components/settings/NamespaceEditor.tsx | 107 + .../components/settings/NotificationsForm.tsx | 437 + .../components/settings/PathsStorageForm.tsx | 82 + ui/src/components/settings/PieceEditor.tsx | 304 + ui/src/components/settings/PieceMetaForm.tsx | 86 + .../components/settings/PreferencesForm.tsx | 61 + ui/src/components/settings/ReflectionForm.tsx | 209 + ui/src/components/settings/RulesTable.tsx | 86 + ui/src/components/settings/SafetyForm.tsx | 64 + .../components/settings/SearchFilterForm.tsx | 52 + ui/src/components/settings/SecretInput.tsx | 191 + .../components/settings/SettingsSidebar.tsx | 143 + ui/src/components/settings/SkillsForm.tsx | 419 + ui/src/components/settings/SshAuditLog.tsx | 188 + ui/src/components/settings/SshConfigForm.tsx | 290 + ui/src/components/settings/SshForm.tsx | 75 + .../settings/SshGlobalConnectionsForm.tsx | 449 + ui/src/components/settings/SshGrantsForm.tsx | 417 + .../settings/SshMasterKeyRotationForm.tsx | 222 + .../components/settings/StringArrayEditor.tsx | 52 + ui/src/components/settings/ToolTagInput.tsx | 279 + .../components/settings/ToolsExternalForm.tsx | 130 + ui/src/components/settings/ToolsForm.tsx | 358 + ui/src/components/settings/ToolsMediaForm.tsx | 139 + ui/src/components/settings/ToolsWebForm.tsx | 102 + ui/src/components/settings/WorkspaceForm.tsx | 65 + ui/src/components/settings/formUtils.tsx | 38 + ui/src/components/settings/types.ts | 5 + ui/src/components/shared/EmptyState.tsx | 64 + ui/src/components/shared/LoadingSpinner.tsx | 8 + ui/src/components/shared/Skeleton.tsx | 55 + ui/src/components/shared/StatChip.tsx | 24 + ui/src/components/shared/StatusBadge.tsx | 18 + .../userfolder/AddBrowserSessionDialog.tsx | 167 + .../components/userfolder/AgentsMdPanel.tsx | 105 + .../userfolder/BrowserSessionsPanel.tsx | 105 + ui/src/components/userfolder/FileTree.tsx | 154 + .../userfolder/McpConnectionsPanel.tsx | 129 + ui/src/components/userfolder/McpPanel.tsx | 461 ++ .../components/userfolder/McpServersPanel.tsx | 518 ++ .../userfolder/MonacoFileEditor.tsx | 170 + ui/src/components/userfolder/NewFileForm.tsx | 138 + ui/src/components/userfolder/NotesPanel.tsx | 354 + ui/src/components/userfolder/PetsPanel.tsx | 501 ++ .../userfolder/SaveAsScriptDialog.tsx | 329 + .../userfolder/ScriptDiffReview.tsx | 163 + ui/src/components/userfolder/SkillsPanel.tsx | 11 + .../userfolder/SshConnectionForm.tsx | 410 + .../userfolder/SshConnectionsPanel.tsx | 521 ++ .../userfolder/SshHostKeyDialog.tsx | 117 + .../userfolder/SshPublicKeyDialog.tsx | 94 + .../userfolder/SubscriptionsPanel.tsx | 535 ++ .../components/userfolder/UserFolderTab.tsx | 538 ++ ui/src/content/help/01-intro.md | 68 + ui/src/content/help/02-tasks.md | 85 + ui/src/content/help/03-running.md | 65 + ui/src/content/help/04-results.md | 62 + ui/src/content/help/05-pieces.md | 72 + ui/src/content/help/06-schedules.md | 61 + ui/src/content/help/07-notifications.md | 69 + ui/src/content/help/08-troubleshooting.md | 63 + ui/src/content/help/09-userfolder.md | 105 + ui/src/content/help/10-subtasks.md | 69 + ui/src/content/help/11-skills.md | 76 + ui/src/content/help/12-memory.md | 81 + ui/src/content/help/13-mcp.md | 112 + ui/src/content/help/14-ssh.md | 86 + ui/src/content/help/15-llm-gateway.md | 71 + ui/src/content/help/16-tools.md | 58 + ui/src/content/help/17-settings.md | 124 + ui/src/content/help/18-reflection.md | 81 + ui/src/content/help/19-admin.md | 120 + ui/src/hooks/useActivePet.ts | 74 + ui/src/hooks/useBranding.ts | 153 + ui/src/hooks/useConfig.ts | 7 + ui/src/hooks/useConsoleSession.ts | 138 + ui/src/hooks/useDashboardWidgets.ts | 56 + ui/src/hooks/useEdgeSwipe.test.ts | 34 + ui/src/hooks/useEdgeSwipe.ts | 74 + ui/src/hooks/useFileBrowser.ts | 47 + ui/src/hooks/useFilePreview.ts | 77 + ui/src/hooks/useJobStream.ts | 116 + ui/src/hooks/useLocalFiles.ts | 18 + ui/src/hooks/useLocalStorageState.ts | 43 + ui/src/hooks/useNodeAnimationState.ts | 42 + ui/src/hooks/useNodeStatus.ts | 42 + ui/src/hooks/usePetFrameAnalysis.ts | 87 + ui/src/hooks/usePieces.ts | 16 + ui/src/hooks/useSidePanelLayout.ts | 33 + ui/src/hooks/useSubtaskActivities.ts | 21 + ui/src/hooks/useSwipeNav.ts | 60 + ui/src/hooks/useTaskDetail.ts | 53 + ui/src/hooks/useTaskList.ts | 14 + ui/src/hooks/useTaskNotifications.ts | 155 + ui/src/hooks/useTaskOperations.ts | 61 + ui/src/hooks/useToast.ts | 24 + ui/src/hooks/useTools.ts | 7 + ui/src/hooks/useUrlState.ts | 53 + ui/src/hooks/useWorkerStatus.ts | 20 + ui/src/index.css | 685 ++ ui/src/lib/constants.ts | 23 + ui/src/lib/help-content.ts | 32 + ui/src/lib/help.ts | 138 + ui/src/lib/linkified-text.tsx | 47 + ui/src/lib/markdown-text.tsx | 126 + ui/src/lib/notifications.test.ts | 294 + ui/src/lib/notifications.ts | 169 + ui/src/lib/output-path-detect.test.ts | 135 + ui/src/lib/output-path-detect.ts | 104 + ui/src/lib/output-preview-context.tsx | 111 + ui/src/lib/pets/petState.test.ts | 88 + ui/src/lib/pets/petState.ts | 57 + ui/src/lib/pets/toolIconMap.ts | 14 + ui/src/lib/push-subscribe.test.ts | 126 + ui/src/lib/push-subscribe.ts | 121 + ui/src/lib/queryClient.ts | 11 + ui/src/lib/ssh-console-types.ts | 29 + ui/src/lib/ssh-types.ts | 86 + ui/src/lib/streamFieldExtract.test.ts | 53 + ui/src/lib/streamFieldExtract.ts | 104 + ui/src/lib/unsavedGuard.ts | 62 + ui/src/lib/urlState.ts | 167 + ui/src/lib/usePictureInPicture.ts | 198 + ui/src/lib/utils.ts | 248 + ui/src/main.tsx | 19 + ui/src/pages/AdminCaptchaPage.tsx | 157 + ui/src/pages/HelpPage.tsx | 154 + ui/src/pages/PiecesPage.tsx | 320 + ui/src/pages/SchedulesPage.tsx | 1278 +++ ui/src/pages/SettingsPage.tsx | 85 + ui/src/pages/SharedView.tsx | 389 + ui/src/pages/UsersPage.tsx | 531 ++ ui/src/sw/push-handler.test.ts | 77 + ui/src/sw/push-handler.ts | 65 + ui/src/vite-env.d.ts | 2 + ui/tailwind.config.js | 53 + ui/tsconfig.json | 14 + ui/vite.config.ts | 30 + 823 files changed, 184150 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 Dockerfile create mode 100644 GEMINI.md create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README.md create mode 100644 bench/fixtures/notes.md create mode 100644 bench/fixtures/sales.xlsx create mode 100644 bench/fixtures/web/announcement.html create mode 100644 bench/tasks/composite-mini-report.yaml create mode 100644 bench/tasks/reflection-smoke.yaml create mode 100644 config.yaml.example create mode 100644 deploy/maestro.service create mode 100644 docker-compose.yml create mode 100644 docs/aao-gateway-overview.md create mode 100644 docs/architecture.md create mode 100644 docs/bench.md create mode 100644 docs/configuration.md create mode 100644 docs/context-flow.md create mode 100644 docs/design/README.md create mode 100644 docs/design/colors_and_type.css create mode 100644 docs/design/ui_kits_reference/admin-legacy/ChatPane.jsx create mode 100644 docs/design/ui_kits_reference/admin-legacy/DetailPanel.jsx create mode 100644 docs/design/ui_kits_reference/admin-legacy/Primitives.jsx create mode 100644 docs/design/ui_kits_reference/admin-legacy/README.md create mode 100644 docs/design/ui_kits_reference/admin-legacy/TaskList.jsx create mode 100644 docs/design/ui_kits_reference/admin-legacy/TopBar.jsx create mode 100644 docs/design/ui_kits_reference/admin-legacy/index.html create mode 100644 docs/design/ui_kits_reference/admin/ChatPane.jsx create mode 100644 docs/design/ui_kits_reference/admin/DetailPanel.jsx create mode 100644 docs/design/ui_kits_reference/admin/Primitives.jsx create mode 100644 docs/design/ui_kits_reference/admin/README.md create mode 100644 docs/design/ui_kits_reference/admin/SchedulesPage.jsx create mode 100644 docs/design/ui_kits_reference/admin/SettingsPage.jsx create mode 100644 docs/design/ui_kits_reference/admin/TaskList.jsx create mode 100644 docs/design/ui_kits_reference/admin/TopBar.jsx create mode 100644 docs/design/ui_kits_reference/admin/UsersPage.jsx create mode 100644 docs/design/ui_kits_reference/admin/index.html create mode 100644 docs/design/ui_kits_reference/compare-cta.html create mode 100644 docs/getting-started.md create mode 100644 docs/maintenance-checklist.md create mode 100644 docs/mcp.md create mode 100644 docs/operations/bash-sandbox-provisioning.md create mode 100644 docs/skills.md create mode 100644 docs/ssh.md create mode 100644 docs/tools/annotateimage.md create mode 100644 docs/tools/bash.md create mode 100644 docs/tools/brainstorm.md create mode 100644 docs/tools/browse-sessions.md create mode 100644 docs/tools/browseweb.md create mode 100644 docs/tools/checklist.md create mode 100644 docs/tools/downloadfile.md create mode 100644 docs/tools/getyoutubetranscript.md create mode 100644 docs/tools/listpieces.md create mode 100644 docs/tools/listuserassets.md create mode 100644 docs/tools/notes.md create mode 100644 docs/tools/office.md create mode 100644 docs/tools/readimage.md create mode 100644 docs/tools/readusermemory.md create mode 100644 docs/tools/readusertemplate.md create mode 100644 docs/tools/renderusertemplate.md create mode 100644 docs/tools/runuserscript.md create mode 100644 docs/tools/searchamazon.md create mode 100644 docs/tools/searchknowledge.md create mode 100644 docs/tools/searchmicrosoftlearn.md create mode 100644 docs/tools/searchplaces.md create mode 100644 docs/tools/slide.md create mode 100644 docs/tools/spawnsubtask.md create mode 100644 docs/tools/sqlite.md create mode 100644 docs/tools/ssh-console-tools.md create mode 100644 docs/tools/ssh-tools.md create mode 100644 docs/tools/transcribeaudio.md create mode 100644 docs/tools/updatedashboardwidget.md create mode 100644 docs/tools/updateusermemory.md create mode 100644 docs/tools/webfetch.md create mode 100644 docs/tools/websearch.md create mode 100644 docs/tools/writeuserscript.md create mode 100644 docs/tools/writeusertemplate.md create mode 100644 docs/tools/xsearch.md create mode 100644 docs/user-folder-layout.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pieces/SCHEMA.md create mode 100644 pieces/brainstorming.yaml create mode 100644 pieces/chat.yaml create mode 100644 pieces/data-process.yaml create mode 100644 pieces/game-tweet-generator.yaml create mode 100644 pieces/general.yaml create mode 100644 pieces/help.yaml create mode 100644 pieces/office-process.yaml create mode 100644 pieces/piece-builder.yaml create mode 100644 pieces/research-sub.yaml create mode 100644 pieces/research.yaml create mode 100644 pieces/slide.yaml create mode 100644 pieces/sns-research.yaml create mode 100644 pieces/ssh-console.yaml create mode 100644 pieces/ssh-ops.yaml create mode 100644 pieces/x-ai-digest.yaml create mode 100644 runtime/python-requirements.txt create mode 100644 scripts/bench-run.ts create mode 100755 scripts/build-all.sh create mode 100644 scripts/build-bench-fixtures.ts create mode 100755 scripts/gateway.sh create mode 100755 scripts/generate-version.sh create mode 100755 scripts/install-twitter-cli.sh create mode 100644 scripts/lint-pieces.mjs create mode 100755 scripts/migrate-config.sh create mode 100755 scripts/prebake-python.sh create mode 100755 scripts/prepare.sh create mode 100755 scripts/server.sh create mode 100755 scripts/setup-novnc.sh create mode 100755 scripts/setup-repo.sh create mode 100755 scripts/setup.sh create mode 100644 scripts/validate-help-docs.mjs create mode 100644 scripts/vapid-rotate.ts create mode 100644 src/__fixtures__/config-migration/v1-gateway-server-with-keys.yaml create mode 100644 src/__fixtures__/config-migration/v1-mcp-and-ssh.yaml create mode 100644 src/__fixtures__/config-migration/v1-multi-worker-with-proxy.yaml create mode 100644 src/__fixtures__/config-migration/v1-single-ollama.yaml create mode 100644 src/bench/fixture-server.ts create mode 100644 src/bench/grader.test.ts create mode 100644 src/bench/grader.ts create mode 100644 src/bench/judge.ts create mode 100644 src/bench/runner.ts create mode 100644 src/bench/summary.ts create mode 100644 src/bench/types.ts create mode 100644 src/bridge/admin-api.test.ts create mode 100644 src/bridge/admin-api.ts create mode 100644 src/bridge/admin-gateway-api.budget-rate.test.ts create mode 100644 src/bridge/admin-gateway-api.metric-labels.test.ts create mode 100644 src/bridge/admin-gateway-api.test.ts create mode 100644 src/bridge/admin-gateway-api.ts create mode 100644 src/bridge/admin-gateway-status-api.ts create mode 100644 src/bridge/auth-login.html create mode 100644 src/bridge/auth-pending.html create mode 100644 src/bridge/auth.test.ts create mode 100644 src/bridge/auth.ts create mode 100644 src/bridge/branding-api.test.ts create mode 100644 src/bridge/branding-api.ts create mode 100644 src/bridge/browser-api.test.ts create mode 100644 src/bridge/browser-api.ts create mode 100644 src/bridge/browser-session-api.test.ts create mode 100644 src/bridge/browser-session-api.ts create mode 100644 src/bridge/config-api.test.ts create mode 100644 src/bridge/config-api.ts create mode 100644 src/bridge/console-admin-api.test.ts create mode 100644 src/bridge/console-admin-api.ts create mode 100644 src/bridge/console-ws-api.test.ts create mode 100644 src/bridge/console-ws-api.ts create mode 100644 src/bridge/dashboard-api.test.ts create mode 100644 src/bridge/dashboard-api.ts create mode 100644 src/bridge/dashboard-workers.test.ts create mode 100644 src/bridge/dashboard-workers.ts create mode 100644 src/bridge/gateway-mount.test.ts create mode 100644 src/bridge/gateway-mount.ts create mode 100644 src/bridge/job-events.ts create mode 100644 src/bridge/local-api-helpers.ts create mode 100644 src/bridge/local-files-api.ts create mode 100644 src/bridge/local-tasks-api.test.ts create mode 100644 src/bridge/local-tasks-api.ts create mode 100644 src/bridge/mcp-api.test.ts create mode 100644 src/bridge/mcp-api.ts create mode 100644 src/bridge/memory-api.test.ts create mode 100644 src/bridge/memory-api.ts create mode 100644 src/bridge/notes-api.test.ts create mode 100644 src/bridge/notes-api.ts create mode 100644 src/bridge/notifications-api.test.ts create mode 100644 src/bridge/notifications-api.ts create mode 100644 src/bridge/novnc-proxy.ts create mode 100644 src/bridge/pieces-api.test.ts create mode 100644 src/bridge/pieces-api.ts create mode 100644 src/bridge/reflection-api.test.ts create mode 100644 src/bridge/reflection-api.ts create mode 100644 src/bridge/scheduled-tasks-api.test.ts create mode 100644 src/bridge/scheduled-tasks-api.ts create mode 100644 src/bridge/server.test.ts create mode 100644 src/bridge/server.ts create mode 100644 src/bridge/share-api.test.ts create mode 100644 src/bridge/share-api.ts create mode 100644 src/bridge/shutdown.test.ts create mode 100644 src/bridge/shutdown.ts create mode 100644 src/bridge/skills-api.ts create mode 100644 src/bridge/skills-git-install.test.ts create mode 100644 src/bridge/skills-git-install.ts create mode 100644 src/bridge/ssh-api.test.ts create mode 100644 src/bridge/ssh-api.ts create mode 100644 src/bridge/subtask-activity-api.test.ts create mode 100644 src/bridge/subtask-activity-api.ts create mode 100644 src/bridge/subtask-files-api.ts create mode 100644 src/bridge/tools-api.test.ts create mode 100644 src/bridge/tools-api.ts create mode 100644 src/bridge/user-folder-api.agents-md.test.ts create mode 100644 src/bridge/user-folder-api.test.ts create mode 100644 src/bridge/user-folder-api.ts create mode 100644 src/bridge/users-api.ts create mode 100644 src/bridge/validation.test.ts create mode 100644 src/bridge/validation.ts create mode 100644 src/bridge/visibility.test.ts create mode 100644 src/bridge/visibility.ts create mode 100644 src/bridge/yaml-patch.test.ts create mode 100644 src/bridge/yaml-patch.ts create mode 100644 src/config-auth.test.ts create mode 100644 src/config-manager.test.ts create mode 100644 src/config-manager.ts create mode 100644 src/config-normalize.test.ts create mode 100644 src/config-normalize.ts create mode 100644 src/config.test.ts create mode 100644 src/config.ts create mode 100644 src/crypto/sessions.test.ts create mode 100644 src/crypto/sessions.ts create mode 100644 src/db/browser-session-migration.test.ts create mode 100644 src/db/browser-session-repo.test.ts create mode 100644 src/db/browser-session-repo.ts create mode 100644 src/db/dashboard-widgets-repository.test.ts create mode 100644 src/db/migrate.gateway-2b.test.ts create mode 100644 src/db/migrate.notes.test.ts create mode 100644 src/db/migrate.reflection-columns.test.ts create mode 100644 src/db/migrate.test.ts create mode 100644 src/db/migrate.ts create mode 100644 src/db/repository-auth.test.ts create mode 100644 src/db/repository.gateway-keys.test.ts create mode 100644 src/db/repository.gateway-usage.test.ts create mode 100644 src/db/repository.test.ts create mode 100644 src/db/repository.ts create mode 100644 src/db/schema.sql create mode 100644 src/engine/agent-loop-console.test.ts create mode 100644 src/engine/agent-loop.notes-inject.test.ts create mode 100644 src/engine/agent-loop.test.ts create mode 100644 src/engine/agent-loop.ts create mode 100644 src/engine/agent-loop.user-agents.test.ts create mode 100644 src/engine/backend-probes.test.ts create mode 100644 src/engine/backend-probes.ts create mode 100644 src/engine/backend-status-registry.test.ts create mode 100644 src/engine/backend-status-registry.ts create mode 100644 src/engine/browser-launch.test.ts create mode 100644 src/engine/browser-launch.ts create mode 100644 src/engine/browser-recorder.test.ts create mode 100644 src/engine/browser-recorder.ts create mode 100644 src/engine/browser-session-auth.test.ts create mode 100644 src/engine/browser-session-auth.ts create mode 100644 src/engine/browser-session-expiry.test.ts create mode 100644 src/engine/browser-session-expiry.ts create mode 100644 src/engine/browser-session.test.ts create mode 100644 src/engine/browser-session.ts create mode 100644 src/engine/context-manager.test.ts create mode 100644 src/engine/context-manager.ts create mode 100644 src/engine/context/atomic-json.test.ts create mode 100644 src/engine/context/atomic-json.ts create mode 100644 src/engine/context/cache-key.ts create mode 100644 src/engine/context/file-read-dedup.test.ts create mode 100644 src/engine/context/file-read-dedup.ts create mode 100644 src/engine/context/history-compactor.test.ts create mode 100644 src/engine/context/history-compactor.ts create mode 100644 src/engine/context/invalidation.ts create mode 100644 src/engine/context/memory-delta.test.ts create mode 100644 src/engine/context/memory-delta.ts create mode 100644 src/engine/context/memory-handoff.test.ts create mode 100644 src/engine/context/memory-handoff.ts create mode 100644 src/engine/context/path-normalize.test.ts create mode 100644 src/engine/context/path-normalize.ts create mode 100644 src/engine/context/prompt-guard.test.ts create mode 100644 src/engine/context/prompt-guard.ts create mode 100644 src/engine/context/token-estimate.ts create mode 100644 src/engine/context/tool-result-cache.test.ts create mode 100644 src/engine/context/tool-result-cache.ts create mode 100644 src/engine/context/workspace-memory.test.ts create mode 100644 src/engine/context/workspace-memory.ts create mode 100644 src/engine/llm-stream.test.ts create mode 100644 src/engine/llm-stream.ts create mode 100644 src/engine/local-context.test.ts create mode 100644 src/engine/local-context.ts create mode 100644 src/engine/notes-inject.test.ts create mode 100644 src/engine/notes-inject.ts create mode 100644 src/engine/piece-catalog.test.ts create mode 100644 src/engine/piece-catalog.ts create mode 100644 src/engine/piece-classifier.test.ts create mode 100644 src/engine/piece-classifier.ts create mode 100644 src/engine/piece-runner.test.ts create mode 100644 src/engine/piece-runner.ts create mode 100644 src/engine/reflection/activity-summarizer.test.ts create mode 100644 src/engine/reflection/activity-summarizer.ts create mode 100644 src/engine/reflection/applier.fuzz.test.ts create mode 100644 src/engine/reflection/applier.test.ts create mode 100644 src/engine/reflection/applier.ts create mode 100644 src/engine/reflection/drift-detect.test.ts create mode 100644 src/engine/reflection/drift-detect.ts create mode 100644 src/engine/reflection/llm-client.test.ts create mode 100644 src/engine/reflection/llm-client.ts create mode 100644 src/engine/reflection/load-inputs.test.ts create mode 100644 src/engine/reflection/load-inputs.ts create mode 100644 src/engine/reflection/piece-writer.test.ts create mode 100644 src/engine/reflection/piece-writer.ts create mode 100644 src/engine/reflection/reflection-prompt.ts create mode 100644 src/engine/reflection/reflection-runner.ts create mode 100644 src/engine/reflection/reflection-schema.ts create mode 100644 src/engine/reflection/retention.test.ts create mode 100644 src/engine/reflection/retention.ts create mode 100644 src/engine/reflection/revisions.ts create mode 100644 src/engine/reflection/semantic-validator.test.ts create mode 100644 src/engine/reflection/semantic-validator.ts create mode 100644 src/engine/reflection/silent-fork.test.ts create mode 100644 src/engine/reflection/silent-fork.ts create mode 100644 src/engine/reflection/snapshot.test.ts create mode 100644 src/engine/reflection/snapshot.ts create mode 100644 src/engine/reflection/types.ts create mode 100644 src/engine/reflection/user-lock.test.ts create mode 100644 src/engine/reflection/user-lock.ts create mode 100644 src/engine/skills-scanner.test.ts create mode 100644 src/engine/skills-scanner.ts create mode 100644 src/engine/skills.test.ts create mode 100644 src/engine/skills.ts create mode 100644 src/engine/strip-thinking.ts create mode 100644 src/engine/tools/__fixtures__/slide/all-layouts.json create mode 100644 src/engine/tools/amazon.ts create mode 100644 src/engine/tools/app-docs.test.ts create mode 100644 src/engine/tools/app-docs.ts create mode 100644 src/engine/tools/brainstorm.test.ts create mode 100644 src/engine/tools/brainstorm.ts create mode 100644 src/engine/tools/browser-frame-chain.e2e.test.ts create mode 100644 src/engine/tools/browser.test.ts create mode 100644 src/engine/tools/browser.ts create mode 100644 src/engine/tools/checklist.test.ts create mode 100644 src/engine/tools/checklist.ts create mode 100644 src/engine/tools/dashboard.test.ts create mode 100644 src/engine/tools/dashboard.ts create mode 100644 src/engine/tools/data.ts create mode 100644 src/engine/tools/docs.test.ts create mode 100644 src/engine/tools/docs.ts create mode 100644 src/engine/tools/image.test.ts create mode 100644 src/engine/tools/image.ts create mode 100644 src/engine/tools/index.test.ts create mode 100644 src/engine/tools/index.ts create mode 100644 src/engine/tools/knowledge.test.ts create mode 100644 src/engine/tools/knowledge.ts create mode 100644 src/engine/tools/maps.ts create mode 100644 src/engine/tools/mission.test.ts create mode 100644 src/engine/tools/mission.ts create mode 100644 src/engine/tools/ms-learn.ts create mode 100644 src/engine/tools/notes.test.ts create mode 100644 src/engine/tools/notes.ts create mode 100644 src/engine/tools/office-types.ts create mode 100644 src/engine/tools/office.test.ts create mode 100644 src/engine/tools/office.ts create mode 100644 src/engine/tools/orchestration.ts create mode 100644 src/engine/tools/pieces.test.ts create mode 100644 src/engine/tools/pieces.ts create mode 100644 src/engine/tools/raw-save.test.ts create mode 100644 src/engine/tools/raw-save.ts create mode 100644 src/engine/tools/review.test.ts create mode 100644 src/engine/tools/review.ts create mode 100644 src/engine/tools/sandbox.test.ts create mode 100644 src/engine/tools/sandbox.ts create mode 100644 src/engine/tools/shared/html.test.ts create mode 100644 src/engine/tools/shared/html.ts create mode 100644 src/engine/tools/shared/ssrf.test.ts create mode 100644 src/engine/tools/shared/ssrf.ts create mode 100644 src/engine/tools/skills.test.ts create mode 100644 src/engine/tools/skills.ts create mode 100644 src/engine/tools/slide.test.ts create mode 100644 src/engine/tools/slide.ts create mode 100644 src/engine/tools/slide/add-slide.test.ts create mode 100644 src/engine/tools/slide/add-slide.ts create mode 100644 src/engine/tools/slide/build-pptx.test.ts create mode 100644 src/engine/tools/slide/build-pptx.ts create mode 100644 src/engine/tools/slide/layouts.test.ts create mode 100644 src/engine/tools/slide/layouts.ts create mode 100644 src/engine/tools/slide/reset-slides.test.ts create mode 100644 src/engine/tools/slide/reset-slides.ts create mode 100644 src/engine/tools/slide/set-theme.test.ts create mode 100644 src/engine/tools/slide/set-theme.ts create mode 100644 src/engine/tools/slide/state.test.ts create mode 100644 src/engine/tools/slide/state.ts create mode 100644 src/engine/tools/slide/themes.test.ts create mode 100644 src/engine/tools/slide/themes.ts create mode 100644 src/engine/tools/speech.ts create mode 100644 src/engine/tools/ssh-console.test.ts create mode 100644 src/engine/tools/ssh-console.ts create mode 100644 src/engine/tools/ssh.e2e.test.ts create mode 100644 src/engine/tools/ssh.test.ts create mode 100644 src/engine/tools/ssh.ts create mode 100644 src/engine/tools/structured-blocks.ts create mode 100644 src/engine/tools/user-folder.test.ts create mode 100644 src/engine/tools/user-folder.ts create mode 100644 src/engine/tools/web.test.ts create mode 100644 src/engine/tools/web.ts create mode 100644 src/engine/tools/x.test.ts create mode 100644 src/engine/tools/x.ts create mode 100644 src/engine/tools/youtube.ts create mode 100644 src/gateway/auth.dual-lookup.test.ts create mode 100644 src/gateway/auth.test.ts create mode 100644 src/gateway/auth.ts create mode 100644 src/gateway/bootstrap.metrics.test.ts create mode 100644 src/gateway/bootstrap.test.ts create mode 100644 src/gateway/bootstrap.ts create mode 100644 src/gateway/budget-check.test.ts create mode 100644 src/gateway/budget-check.ts create mode 100644 src/gateway/config-migration.test.ts create mode 100644 src/gateway/config-migration.ts create mode 100644 src/gateway/config.test.ts create mode 100644 src/gateway/config.ts create mode 100644 src/gateway/health-endpoint.test.ts create mode 100644 src/gateway/health-endpoint.ts create mode 100644 src/gateway/key-cache.test.ts create mode 100644 src/gateway/key-cache.ts create mode 100644 src/gateway/key-format.test.ts create mode 100644 src/gateway/key-format.ts create mode 100644 src/gateway/models-endpoint.test.ts create mode 100644 src/gateway/models-endpoint.ts create mode 100644 src/gateway/period.test.ts create mode 100644 src/gateway/period.ts create mode 100644 src/gateway/phase2a-integration.test.ts create mode 100644 src/gateway/rate-limiter.test.ts create mode 100644 src/gateway/rate-limiter.ts create mode 100644 src/gateway/router.test.ts create mode 100644 src/gateway/router.ts create mode 100644 src/gateway/server.metrics-endpoint.test.ts create mode 100644 src/gateway/server.ts create mode 100644 src/gateway/shared-dependencies.test.ts create mode 100644 src/gateway/shared-dependencies.ts create mode 100644 src/gateway/stream-proxy.metrics.test.ts create mode 100644 src/gateway/stream-proxy.test.ts create mode 100644 src/gateway/stream-proxy.ts create mode 100644 src/gateway/stream-proxy.usage-extraction.test.ts create mode 100644 src/git/workspace-manager.test.ts create mode 100644 src/git/workspace-manager.ts create mode 100644 src/index.ts create mode 100644 src/llm/openai-compat.test.ts create mode 100644 src/llm/openai-compat.ts create mode 100644 src/logger.ts create mode 100644 src/main.test.ts create mode 100644 src/main.ts create mode 100644 src/mcp/aggregator.test.ts create mode 100644 src/mcp/aggregator.ts create mode 100644 src/mcp/binary-saver.test.ts create mode 100644 src/mcp/binary-saver.ts create mode 100644 src/mcp/client-factory.ts create mode 100644 src/mcp/config.test.ts create mode 100644 src/mcp/config.ts create mode 100644 src/mcp/crypto.test.ts create mode 100644 src/mcp/crypto.ts create mode 100644 src/mcp/discovery.test.ts create mode 100644 src/mcp/discovery.ts create mode 100644 src/mcp/integration.test.ts create mode 100644 src/mcp/oauth-routes.test.ts create mode 100644 src/mcp/oauth-routes.ts create mode 100644 src/mcp/raw-logger.test.ts create mode 100644 src/mcp/raw-logger.ts create mode 100644 src/mcp/redact.test.ts create mode 100644 src/mcp/redact.ts create mode 100644 src/mcp/registry.test.ts create mode 100644 src/mcp/registry.ts create mode 100644 src/mcp/ssrf-strict.pinned.test.ts create mode 100644 src/mcp/ssrf-strict.test.ts create mode 100644 src/mcp/ssrf-strict.ts create mode 100644 src/mcp/testing/mock-mcp-server.ts create mode 100644 src/mcp/token-manager.test.ts create mode 100644 src/mcp/token-manager.ts create mode 100644 src/mcp/tool-adapter.test.ts create mode 100644 src/mcp/tool-adapter.ts create mode 100644 src/mcp/tool-cache.ts create mode 100644 src/mcp/tool-executor.test.ts create mode 100644 src/mcp/tool-executor.ts create mode 100644 src/mcp/types.ts create mode 100644 src/metrics/gateway-metrics.test.ts create mode 100644 src/metrics/gateway-metrics.ts create mode 100644 src/metrics/http-handler.test.ts create mode 100644 src/metrics/http-handler.ts create mode 100644 src/metrics/registry.test.ts create mode 100644 src/metrics/registry.ts create mode 100644 src/metrics/tool-name-allowlist.test.ts create mode 100644 src/metrics/tool-name-allowlist.ts create mode 100644 src/metrics/worker-metrics.test.ts create mode 100644 src/metrics/worker-metrics.ts create mode 100644 src/net/ssrf-strict.test.ts create mode 100644 src/net/ssrf-strict.ts create mode 100644 src/notes/fm.test.ts create mode 100644 src/notes/fm.ts create mode 100644 src/notes/notes-repository.test.ts create mode 100644 src/notes/notes-repository.ts create mode 100644 src/notes/notes-service.test.ts create mode 100644 src/notes/notes-service.ts create mode 100644 src/progress/event-log.test.ts create mode 100644 src/progress/event-log.ts create mode 100644 src/progress/local-reporter.test.ts create mode 100644 src/progress/local-reporter.ts create mode 100644 src/progress/log-format.test.ts create mode 100644 src/progress/log-format.ts create mode 100644 src/push-service.load.test.ts create mode 100644 src/push-service.test.ts create mode 100644 src/push-service.ts create mode 100644 src/scheduler.test.ts create mode 100644 src/scheduler.ts create mode 100644 src/scheduling.ts create mode 100644 src/scripts/migrate-config.test.ts create mode 100644 src/scripts/migrate-config.ts create mode 100644 src/ssh/abuse-repo.test.ts create mode 100644 src/ssh/abuse-repo.ts create mode 100644 src/ssh/access.test.ts create mode 100644 src/ssh/access.ts create mode 100644 src/ssh/admin-rate-limit.test.ts create mode 100644 src/ssh/admin-rate-limit.ts create mode 100644 src/ssh/algorithms.test.ts create mode 100644 src/ssh/algorithms.ts create mode 100644 src/ssh/audit-repo.test.ts create mode 100644 src/ssh/audit-repo.ts create mode 100644 src/ssh/config.test.ts create mode 100644 src/ssh/config.ts create mode 100644 src/ssh/connection-repo.test.ts create mode 100644 src/ssh/connection-repo.ts create mode 100644 src/ssh/console-deny-check.test.ts create mode 100644 src/ssh/console-deny-check.ts create mode 100644 src/ssh/console-protocol.ts create mode 100644 src/ssh/console-registry.test.ts create mode 100644 src/ssh/console-registry.ts create mode 100644 src/ssh/console-session.test.ts create mode 100644 src/ssh/console-session.ts create mode 100644 src/ssh/crypto.test.ts create mode 100644 src/ssh/crypto.ts create mode 100644 src/ssh/deny-list.test.ts create mode 100644 src/ssh/deny-list.ts create mode 100644 src/ssh/grants-repo.test.ts create mode 100644 src/ssh/grants-repo.ts create mode 100644 src/ssh/maintenance.test.ts create mode 100644 src/ssh/maintenance.ts create mode 100644 src/ssh/output.test.ts create mode 100644 src/ssh/output.ts create mode 100644 src/ssh/path-policy.test.ts create mode 100644 src/ssh/path-policy.ts create mode 100644 src/ssh/recovery.ts create mode 100644 src/ssh/ring-buffer.test.ts create mode 100644 src/ssh/ring-buffer.ts create mode 100644 src/ssh/session-test-server.ts create mode 100644 src/ssh/session.test.ts create mode 100644 src/ssh/session.ts create mode 100644 src/ssh/ssrf.test.ts create mode 100644 src/ssh/ssrf.ts create mode 100644 src/title-generation.test.ts create mode 100644 src/title-generation.ts create mode 100644 src/user-folder/frontmatter.test.ts create mode 100644 src/user-folder/frontmatter.ts create mode 100644 src/user-folder/memory.test.ts create mode 100644 src/user-folder/memory.ts create mode 100644 src/user-folder/paths.notes.test.ts create mode 100644 src/user-folder/paths.test.ts create mode 100644 src/user-folder/paths.ts create mode 100644 src/user-folder/pets.ts create mode 100644 src/user-folder/recording-flush.test.ts create mode 100644 src/user-folder/recording-flush.ts create mode 100644 src/user-folder/recording-to-run.e2e.test.ts create mode 100644 src/user-folder/script-compiler.test.ts create mode 100644 src/user-folder/script-compiler.ts create mode 100644 src/user-folder/script-orchestrator.ts create mode 100644 src/user-folder/script-runner-child.ts create mode 100644 src/user-folder/script-runner.test.ts create mode 100644 src/user-folder/script-runner.ts create mode 100644 src/user-folder/session-loader.ts create mode 100644 src/user-folder/template-renderer.ts create mode 100644 src/user-folder/trash-cleanup.test.ts create mode 100644 src/user-folder/trash-cleanup.ts create mode 100644 src/vapid-store.test.ts create mode 100644 src/vapid-store.ts create mode 100644 src/worker-bootstrap.test.ts create mode 100644 src/worker-bootstrap.ts create mode 100644 src/worker-manager.test.ts create mode 100644 src/worker-manager.ts create mode 100644 src/worker.metrics.test.ts create mode 100644 src/worker.reflection-dispatch.test.ts create mode 100644 src/worker.reflection-enqueue.test.ts create mode 100644 src/worker.test.ts create mode 100644 src/worker.ts create mode 100644 src/worker/sticky-backend.test.ts create mode 100644 src/worker/sticky-backend.ts create mode 100644 test-smoke.ts create mode 100644 tests/fixtures/browser-macros/click-and-read.js create mode 100644 tests/fixtures/browser-macros/no-frontmatter.js create mode 100644 tests/fixtures/scripts/circular.js create mode 100644 tests/fixtures/scripts/expected-compiled.js create mode 100644 tests/fixtures/scripts/logs-to-stderr.js create mode 100644 tests/fixtures/scripts/return-42.js create mode 100644 tests/fixtures/scripts/throws.js create mode 100644 tests/fixtures/scripts/timeout.js create mode 100644 tsconfig.json create mode 100644 ui/index.html create mode 100644 ui/package-lock.json create mode 100644 ui/package.json create mode 100644 ui/postcss.config.js create mode 100644 ui/public/apple-touch-icon-180.png create mode 100644 ui/public/favicon.svg create mode 100644 ui/public/icon-192.png create mode 100644 ui/public/icon-512.png create mode 100644 ui/public/icon-maskable-512.png create mode 100644 ui/public/manifest.webmanifest create mode 100644 ui/public/sw.js create mode 100644 ui/src/App.tsx create mode 100644 ui/src/api.ts create mode 100644 ui/src/components/activity/ActivityEventCard.tsx create mode 100644 ui/src/components/activity/ActivityTimeline.tsx create mode 100644 ui/src/components/browser/BrowserSessionPanel.tsx create mode 100644 ui/src/components/browser/PipButton.tsx create mode 100644 ui/src/components/browser/SaveRecordingButton.tsx create mode 100644 ui/src/components/chat/ChatMessage.tsx create mode 100644 ui/src/components/chat/ChatPane.tsx create mode 100644 ui/src/components/chat/MovementGroup.tsx create mode 100644 ui/src/components/chat/SubtaskInlineCard.tsx create mode 100644 ui/src/components/chat/ToolCallsSection.tsx create mode 100644 ui/src/components/chat/thinkingUtils.ts create mode 100644 ui/src/components/create/AttachmentDropzone.tsx create mode 100644 ui/src/components/create/CreateTaskDialog.tsx create mode 100644 ui/src/components/create/ScheduleFields.tsx create mode 100644 ui/src/components/dashboard/AddWidgetDialog.tsx create mode 100644 ui/src/components/dashboard/MarkdownWidget.tsx create mode 100644 ui/src/components/dashboard/NodeStatusWidget.tsx create mode 100644 ui/src/components/dashboard/SideInfoPanel.tsx create mode 100644 ui/src/components/dashboard/TaskListWithSidePanel.tsx create mode 100644 ui/src/components/dashboard/WidgetTabBar.tsx create mode 100644 ui/src/components/dashboard/WorkerStatusWidget.tsx create mode 100644 ui/src/components/detail/ContextUsageGauge.tsx create mode 100644 ui/src/components/detail/ContinueWithPieceDialog.tsx create mode 100644 ui/src/components/detail/DetailHeader.tsx create mode 100644 ui/src/components/detail/DetailPanel.tsx create mode 100644 ui/src/components/detail/ReflectionBadge.tsx create mode 100644 ui/src/components/detail/tabs/BrowserTab.tsx create mode 100644 ui/src/components/detail/tabs/ConsoleTab.tsx create mode 100644 ui/src/components/detail/tabs/FilesTab.tsx create mode 100644 ui/src/components/detail/tabs/OutputTab.tsx create mode 100644 ui/src/components/detail/tabs/OverviewTab.tsx create mode 100644 ui/src/components/detail/tabs/ProgressTab.tsx create mode 100644 ui/src/components/detail/tabs/SubtaskActivitySection.tsx create mode 100644 ui/src/components/detail/tabs/SubtasksPanel.tsx create mode 100644 ui/src/components/detail/tabs/TimelineTab.tsx create mode 100644 ui/src/components/detail/tabs/TraceTab.tsx create mode 100644 ui/src/components/detail/tabs/console/ConsoleHeader.tsx create mode 100644 ui/src/components/detail/tabs/console/MobileKeyboardBar.tsx create mode 100644 ui/src/components/detail/tabs/console/ScrollToBottomButton.tsx create mode 100644 ui/src/components/detail/tabs/console/TerminalView.tsx create mode 100644 ui/src/components/detail/tabs/console/keys.test.ts create mode 100644 ui/src/components/detail/tabs/console/keys.ts create mode 100644 ui/src/components/embed/AmazonProductsCard.tsx create mode 100644 ui/src/components/embed/AmazonProductsDetail.tsx create mode 100644 ui/src/components/embed/EmbedBlock.tsx create mode 100644 ui/src/components/embed/EmbedModal.tsx create mode 100644 ui/src/components/embed/MapPlacesCard.tsx create mode 100644 ui/src/components/embed/MapPlacesDetail.tsx create mode 100644 ui/src/components/embed/XPostsCard.tsx create mode 100644 ui/src/components/embed/XPostsDetail.tsx create mode 100644 ui/src/components/embed/YouTubeVideosCard.tsx create mode 100644 ui/src/components/embed/YouTubeVideosDetail.tsx create mode 100644 ui/src/components/embed/types.ts create mode 100644 ui/src/components/files/FileBrowser.tsx create mode 100644 ui/src/components/files/FilePreview.tsx create mode 100644 ui/src/components/layout/NavDrawer.tsx create mode 100644 ui/src/components/layout/ResizeHandle.tsx create mode 100644 ui/src/components/layout/TopBar.tsx create mode 100644 ui/src/components/layout/VerticalResizeHandle.tsx create mode 100644 ui/src/components/list/FilterBar.tsx create mode 100644 ui/src/components/list/RailPanel.tsx create mode 100644 ui/src/components/list/TaskListItem.tsx create mode 100644 ui/src/components/list/TaskListPanel.tsx create mode 100644 ui/src/components/pets/ChatPetOverlay.tsx create mode 100644 ui/src/components/pets/PetSprite.tsx create mode 100644 ui/src/components/pets/ToolSpark.tsx create mode 100644 ui/src/components/settings/AskSubtasksForm.tsx create mode 100644 ui/src/components/settings/BrandingForm.tsx create mode 100644 ui/src/components/settings/BrowserSettingsForm.tsx create mode 100644 ui/src/components/settings/ConfigForm.tsx create mode 100644 ui/src/components/settings/ContextForm.tsx create mode 100644 ui/src/components/settings/ExecutionForm.tsx create mode 100644 ui/src/components/settings/GatewayKeyCreateDialog.tsx create mode 100644 ui/src/components/settings/GatewayKeyRawKeyDialog.tsx create mode 100644 ui/src/components/settings/GatewayKeyUsagePanel.tsx create mode 100644 ui/src/components/settings/GatewayKeysSection.tsx create mode 100644 ui/src/components/settings/GatewayServerForm.tsx create mode 100644 ui/src/components/settings/HelpText.tsx create mode 100644 ui/src/components/settings/KnowledgeNamespacesForm.tsx create mode 100644 ui/src/components/settings/LlmWorkersForm.tsx create mode 100644 ui/src/components/settings/McpForm.tsx create mode 100644 ui/src/components/settings/MemoryLearningForm.tsx create mode 100644 ui/src/components/settings/MetricsForm.tsx create mode 100644 ui/src/components/settings/ModelSelect.tsx create mode 100644 ui/src/components/settings/MovementAccordion.tsx create mode 100644 ui/src/components/settings/MovementForm.tsx create mode 100644 ui/src/components/settings/NamespaceEditor.tsx create mode 100644 ui/src/components/settings/NotificationsForm.tsx create mode 100644 ui/src/components/settings/PathsStorageForm.tsx create mode 100644 ui/src/components/settings/PieceEditor.tsx create mode 100644 ui/src/components/settings/PieceMetaForm.tsx create mode 100644 ui/src/components/settings/PreferencesForm.tsx create mode 100644 ui/src/components/settings/ReflectionForm.tsx create mode 100644 ui/src/components/settings/RulesTable.tsx create mode 100644 ui/src/components/settings/SafetyForm.tsx create mode 100644 ui/src/components/settings/SearchFilterForm.tsx create mode 100644 ui/src/components/settings/SecretInput.tsx create mode 100644 ui/src/components/settings/SettingsSidebar.tsx create mode 100644 ui/src/components/settings/SkillsForm.tsx create mode 100644 ui/src/components/settings/SshAuditLog.tsx create mode 100644 ui/src/components/settings/SshConfigForm.tsx create mode 100644 ui/src/components/settings/SshForm.tsx create mode 100644 ui/src/components/settings/SshGlobalConnectionsForm.tsx create mode 100644 ui/src/components/settings/SshGrantsForm.tsx create mode 100644 ui/src/components/settings/SshMasterKeyRotationForm.tsx create mode 100644 ui/src/components/settings/StringArrayEditor.tsx create mode 100644 ui/src/components/settings/ToolTagInput.tsx create mode 100644 ui/src/components/settings/ToolsExternalForm.tsx create mode 100644 ui/src/components/settings/ToolsForm.tsx create mode 100644 ui/src/components/settings/ToolsMediaForm.tsx create mode 100644 ui/src/components/settings/ToolsWebForm.tsx create mode 100644 ui/src/components/settings/WorkspaceForm.tsx create mode 100644 ui/src/components/settings/formUtils.tsx create mode 100644 ui/src/components/settings/types.ts create mode 100644 ui/src/components/shared/EmptyState.tsx create mode 100644 ui/src/components/shared/LoadingSpinner.tsx create mode 100644 ui/src/components/shared/Skeleton.tsx create mode 100644 ui/src/components/shared/StatChip.tsx create mode 100644 ui/src/components/shared/StatusBadge.tsx create mode 100644 ui/src/components/userfolder/AddBrowserSessionDialog.tsx create mode 100644 ui/src/components/userfolder/AgentsMdPanel.tsx create mode 100644 ui/src/components/userfolder/BrowserSessionsPanel.tsx create mode 100644 ui/src/components/userfolder/FileTree.tsx create mode 100644 ui/src/components/userfolder/McpConnectionsPanel.tsx create mode 100644 ui/src/components/userfolder/McpPanel.tsx create mode 100644 ui/src/components/userfolder/McpServersPanel.tsx create mode 100644 ui/src/components/userfolder/MonacoFileEditor.tsx create mode 100644 ui/src/components/userfolder/NewFileForm.tsx create mode 100644 ui/src/components/userfolder/NotesPanel.tsx create mode 100644 ui/src/components/userfolder/PetsPanel.tsx create mode 100644 ui/src/components/userfolder/SaveAsScriptDialog.tsx create mode 100644 ui/src/components/userfolder/ScriptDiffReview.tsx create mode 100644 ui/src/components/userfolder/SkillsPanel.tsx create mode 100644 ui/src/components/userfolder/SshConnectionForm.tsx create mode 100644 ui/src/components/userfolder/SshConnectionsPanel.tsx create mode 100644 ui/src/components/userfolder/SshHostKeyDialog.tsx create mode 100644 ui/src/components/userfolder/SshPublicKeyDialog.tsx create mode 100644 ui/src/components/userfolder/SubscriptionsPanel.tsx create mode 100644 ui/src/components/userfolder/UserFolderTab.tsx create mode 100644 ui/src/content/help/01-intro.md create mode 100644 ui/src/content/help/02-tasks.md create mode 100644 ui/src/content/help/03-running.md create mode 100644 ui/src/content/help/04-results.md create mode 100644 ui/src/content/help/05-pieces.md create mode 100644 ui/src/content/help/06-schedules.md create mode 100644 ui/src/content/help/07-notifications.md create mode 100644 ui/src/content/help/08-troubleshooting.md create mode 100644 ui/src/content/help/09-userfolder.md create mode 100644 ui/src/content/help/10-subtasks.md create mode 100644 ui/src/content/help/11-skills.md create mode 100644 ui/src/content/help/12-memory.md create mode 100644 ui/src/content/help/13-mcp.md create mode 100644 ui/src/content/help/14-ssh.md create mode 100644 ui/src/content/help/15-llm-gateway.md create mode 100644 ui/src/content/help/16-tools.md create mode 100644 ui/src/content/help/17-settings.md create mode 100644 ui/src/content/help/18-reflection.md create mode 100644 ui/src/content/help/19-admin.md create mode 100644 ui/src/hooks/useActivePet.ts create mode 100644 ui/src/hooks/useBranding.ts create mode 100644 ui/src/hooks/useConfig.ts create mode 100644 ui/src/hooks/useConsoleSession.ts create mode 100644 ui/src/hooks/useDashboardWidgets.ts create mode 100644 ui/src/hooks/useEdgeSwipe.test.ts create mode 100644 ui/src/hooks/useEdgeSwipe.ts create mode 100644 ui/src/hooks/useFileBrowser.ts create mode 100644 ui/src/hooks/useFilePreview.ts create mode 100644 ui/src/hooks/useJobStream.ts create mode 100644 ui/src/hooks/useLocalFiles.ts create mode 100644 ui/src/hooks/useLocalStorageState.ts create mode 100644 ui/src/hooks/useNodeAnimationState.ts create mode 100644 ui/src/hooks/useNodeStatus.ts create mode 100644 ui/src/hooks/usePetFrameAnalysis.ts create mode 100644 ui/src/hooks/usePieces.ts create mode 100644 ui/src/hooks/useSidePanelLayout.ts create mode 100644 ui/src/hooks/useSubtaskActivities.ts create mode 100644 ui/src/hooks/useSwipeNav.ts create mode 100644 ui/src/hooks/useTaskDetail.ts create mode 100644 ui/src/hooks/useTaskList.ts create mode 100644 ui/src/hooks/useTaskNotifications.ts create mode 100644 ui/src/hooks/useTaskOperations.ts create mode 100644 ui/src/hooks/useToast.ts create mode 100644 ui/src/hooks/useTools.ts create mode 100644 ui/src/hooks/useUrlState.ts create mode 100644 ui/src/hooks/useWorkerStatus.ts create mode 100644 ui/src/index.css create mode 100644 ui/src/lib/constants.ts create mode 100644 ui/src/lib/help-content.ts create mode 100644 ui/src/lib/help.ts create mode 100644 ui/src/lib/linkified-text.tsx create mode 100644 ui/src/lib/markdown-text.tsx create mode 100644 ui/src/lib/notifications.test.ts create mode 100644 ui/src/lib/notifications.ts create mode 100644 ui/src/lib/output-path-detect.test.ts create mode 100644 ui/src/lib/output-path-detect.ts create mode 100644 ui/src/lib/output-preview-context.tsx create mode 100644 ui/src/lib/pets/petState.test.ts create mode 100644 ui/src/lib/pets/petState.ts create mode 100644 ui/src/lib/pets/toolIconMap.ts create mode 100644 ui/src/lib/push-subscribe.test.ts create mode 100644 ui/src/lib/push-subscribe.ts create mode 100644 ui/src/lib/queryClient.ts create mode 100644 ui/src/lib/ssh-console-types.ts create mode 100644 ui/src/lib/ssh-types.ts create mode 100644 ui/src/lib/streamFieldExtract.test.ts create mode 100644 ui/src/lib/streamFieldExtract.ts create mode 100644 ui/src/lib/unsavedGuard.ts create mode 100644 ui/src/lib/urlState.ts create mode 100644 ui/src/lib/usePictureInPicture.ts create mode 100644 ui/src/lib/utils.ts create mode 100644 ui/src/main.tsx create mode 100644 ui/src/pages/AdminCaptchaPage.tsx create mode 100644 ui/src/pages/HelpPage.tsx create mode 100644 ui/src/pages/PiecesPage.tsx create mode 100644 ui/src/pages/SchedulesPage.tsx create mode 100644 ui/src/pages/SettingsPage.tsx create mode 100644 ui/src/pages/SharedView.tsx create mode 100644 ui/src/pages/UsersPage.tsx create mode 100644 ui/src/sw/push-handler.test.ts create mode 100644 ui/src/sw/push-handler.ts create mode 100644 ui/src/vite-env.d.ts create mode 100644 ui/tailwind.config.js create mode 100644 ui/tsconfig.json create mode 100644 ui/vite.config.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..cf8c548 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +GITEA_API_TOKEN=your-gitea-token +GITEA_WEBHOOK_SECRET=your-webhook-secret +OLLAMA_BASE_URL=http://localhost:11434/v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..919079e --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +node_modules/ +dist/ +data/ +.env +config.yaml +.superpowers/ +*.db +*.db-wal +*.db-shm +input/ +output/ +logs/ +.worktrees/ +.claude/ +.playwright-mcp/ +.superpowers/ +orch.pid +.server.pid +src/generated/ +.worktrees/ +vendor/ +# Added by code-review-graph +.code-review-graph/ + +# Local debugging / scratch scripts (kept out of tree) +scripts/dump_payload.mjs + +# Benchmark output (run results / copied workspaces) +bench/results/ +.context/ + +data/secrets/master.key +data/browser-sessions/* +!data/browser-sessions/.gitkeep +.gstack/ + +# Core dumps from native crashes (sqlite/playwright/sharp). These contain raw +# process memory — including the decrypted master key, SSH private keys and the +# session secret — so they must never be committed. +core +core.* diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..444579b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,134 @@ +# AGENTS.md — Codebase orientation for contributors + +This is a map of the MAESTRO codebase for contributors (and AI coding agents). +For build/test/PR mechanics see [CONTRIBUTING.md](CONTRIBUTING.md); for the +request lifecycle in depth see [docs/architecture.md](docs/architecture.md). + +## Working norms + +- State conclusions and corrections directly; don't pad with reflexive agreement. +- Before editing code, understand the blast radius of a change (callers, tests). + If a change is large or risky, describe the approach before implementing. +- Investigate reported bugs by reproducing the actual behavior before concluding. + +## Execution flow + +``` +UI POST + → bridge/server.ts (Express API) + → Repository (SQLite: jobs table) + → Worker.poll() picks up a queued job + → piece-runner.ts: loads pieces/*.yaml + → agent-loop.ts: ReAct loop (LLM ↔ tool calls, up to safety.maxIterations) + → each movement completes → `transition` (intermediate) / `complete` (terminal) + → job finishes → DB update / progress comment +``` + +## Main layers (`src/`) + +- **`engine/piece-runner.ts`** — loads `pieces/*.yaml`, runs movements in order; + carries verify-movement feedback into the next execute; loop-detection aborts on + excessive repeat visits; `transition.lessons` accumulates cross-movement lessons. +- **`engine/agent-loop.ts`** — the ReAct loop for one movement. Intermediate hops + use the `transition` tool; termination (success/aborted/needs_user_input) uses the + `complete` tool. `complete.result` is the only user-visible final output. A + `ContextManager` tracks token usage from LLM `usage` responses and fires + warn/prompt/force_transition at thresholds. +- **`engine/context-manager.ts`** — threshold-based context-usage monitoring; can + auto-detect a model's context limit from the provider API. +- **`engine/piece-classifier.ts`** — LLM-based piece selection from the task text and + all piece descriptions. +- **`engine/tools/index.ts`** — dynamically loads and dispatches all tool modules. +- **`llm/openai-compat.ts`** — OpenAI-compatible (Ollama/vLLM/…) SSE streaming client; + accumulates `tool_calls` deltas into `LLMEvent`s. Retry via `provider.retry`. +- **`worker.ts` / `worker-manager.ts`** — Workers poll the DB for jobs matching their + `profiles`/`task_classes` and run them; multiple workers run concurrently and are + rebuilt on config change. +- **`config.ts` / `config-manager.ts`** — single `config.yaml`; snake_case YAML ↔ + camelCase code via `transformKeys`; runtime read/write with optimistic locking and + change events. +- **`db/repository.ts`** — SQLite (better-sqlite3). Manages `jobs`, `local_tasks`, + `local_task_comments`, `audit_log`, etc. Schema: `db/schema.sql`. +- **`bridge/server.ts`** — Express API server. Submodules: `pieces-api`, `config-api`, + `tools-api`, `scheduled-tasks-api`, `admin-api`, `share-api`, `browser-api`, + `subtask-activity-api`, and more. +- **`scheduler.ts`** — cron-style scheduled tasks (daily/weekly/monthly/cron/once). +- **`bridge/auth.ts`** — Passport OAuth2 (Google / Gitea). `requireAuth` allows active + users; `requireAdmin` allows admins. Auth is optional (unset = no auth). +- **`gateway/`** — optional LLM gateway (a proxy with virtual keys, budgets, and + Prometheus metrics). Note: its env vars use the `AAO_*` prefix and the + `aao_gateway` connection type for historical reasons (AAO = the gateway). + +## `pieces/*.yaml` — task definitions + +Each piece is an array of `movements`. Per movement: `allowed_tools`, `edit` +(Write/Edit permission), and `rules` (transition conditions). Tools not in +`allowed_tools` are not offered to the LLM. + +**Movement transition principles:** `transition` is for intermediate hops only +(`rules[].next` lists allowed targets); termination uses `complete`. `default_next` +is an engine-internal sentinel (context-overflow forced transition, ASK fallback). +Progressive pressure warns on repeat visits and aborts past a threshold. + +## Tool modules + +| Module | Tools | +|--------|-------| +| `core.ts` | Read / Write / Edit / Bash / Glob / Grep | +| `web.ts` | WebSearch / WebFetch / DownloadFile | +| `image.ts` | ReadImage / AnnotateImage | +| `office.ts` | ReadPdf / ReadExcel / ReadDocx / ReadPPTX / PdfToImages / Split… | +| `data.ts` | SQLite | +| `review.ts` | BatchReviewTextWithLLM / MergeReviewedResults | +| `browser.ts` | BrowseWeb (Playwright) | +| `knowledge.ts` | SearchKnowledge / ListDocuments / IngestDocument / … | +| `orchestration.ts` | SpawnSubTask | +| `x.ts`, `maps.ts`, `youtube.ts`, `amazon.ts`, `speech.ts`, `ms-learn.ts` | optional integrations | +| `checklist.ts` | CreateChecklist / CheckItem / GetChecklist | +| `pieces.ts` | ListPieces / GetPiece / CreatePiece / UpdatePiece | +| `skills.ts` | ReadSkill / ListSkills / InstallSkill (META_TOOL) | +| `docs.ts` | ReadToolDoc (META_TOOL) | +| `ssh.ts` | SSH execution / transfer | + +`raw-save.ts` and `structured-blocks.ts` are helper modules (not registered tools). + +Tool descriptions are kept to one sentence (they ride every LLM call); detailed +guidance lives in `docs/tools/.md` and is fetched via `ReadToolDoc`. + +## Bash sandbox + +The Bash tool runs inside a bwrap sandbox (filesystem confined to the task +workspace, env scrubbed, network unshared) when available, with a hardened +whitelist fallback otherwise. `safety.bash_sandbox` selects the mode +(`auto`/`always`/`off`). Runtime `pip`/`npm install` is rejected; Python packages +are pre-baked from `runtime/python-requirements.txt`. See +[docs/operations/bash-sandbox-provisioning.md](docs/operations/bash-sandbox-provisioning.md). + +## Workspace layout (per job) + +``` +{worktree_dir}/local/{taskId}/ + input/ uploads & DownloadFile output + output/ artifacts (the main Write/Edit-allowed area) + logs/ activity.log, history files + subtasks/ SpawnSubTask results + skills/ skill files materialized by ReadSkill +``` + +## DB migrations + +`db/schema.sql` is the initial schema. New columns are applied idempotently in +`db/migrate.ts` (`PRAGMA table_info` → existence check → `ALTER TABLE ADD COLUMN`). +Update both `schema.sql` and `migrate.ts`. + +## Tests + +Backend tests live next to their source as `*.test.ts` (vitest auto-discovers). + +## Adding a tool + +1. Export `TOOL_DEFS` and an `executeTool` from `src/engine/tools/.ts`. +2. Register the dynamic import in `tools/index.ts`. +3. Add the tool name to the using piece's `allowed_tools`. +4. Add `docs/tools/.md`. See `docs/maintenance-checklist.md` for the full + list of places that must stay in sync. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2593d6c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +All notable changes to MAESTRO are documented here. The format is loosely based +on [Keep a Changelog](https://keepachangelog.com/), and the project aims to +follow semantic versioning. + +## v0.1.0 — Initial public release (2026-06-02) + +First open-source release of MAESTRO, an agent orchestration platform: + +- Runs tasks against any OpenAI-compatible LLM endpoint (Ollama, vLLM, …). +- LLM-classified task routing into **Pieces** (YAML workflows) of **movements**. +- Sandboxed tool runtime (Read/Write/Edit/Bash/Glob/Grep, Office, Web, Browser, + Image, Data/SQLite, Knowledge/RAG, SSH, MCP, sub-tasks, and more). +- Bash tool sandbox (bwrap-based filesystem/network/env isolation with a + hardened fallback) and a declarative pre-baked Python toolchain. +- Optional LLM Gateway (virtual keys, budgets, metrics), reflection-based + learning, scheduled tasks, and a React web UI. +- Apache-2.0 licensed. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..54e007b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,78 @@ +# Contributing to MAESTRO + +Thanks for your interest in contributing! This guide covers how to build, run, +test, and submit changes. + +## Prerequisites + +- **Node.js 22+** +- An **OpenAI-compatible LLM endpoint** for running the app (e.g. [Ollama](https://ollama.com/) at `http://localhost:11434/v1`, or vLLM). Not required just to build/test. +- Optional, for the Bash tool sandbox: `bwrap` (bubblewrap) with unprivileged + user namespaces, plus `python3`/`pip` for the pre-baked tool packages. + +## Setup + +```bash +git clone maestro +cd maestro +npm ci # backend deps +npm --prefix ui ci # UI deps +cp config.yaml.example config.yaml # then edit provider/workers +``` + +## Build & run + +```bash +scripts/build-all.sh # builds backend (dist/) and UI (ui/dist/) +scripts/server.sh start # build + start with PID management +scripts/server.sh logs # tail logs +scripts/server.sh stop +# open http://localhost:9876 +``` + +`scripts/build-all.sh` also pre-bakes the Python packages the Bash sandbox uses +(`runtime/python-requirements.txt`). Pass `--skip-python` to skip that step, or +run `scripts/prebake-python.sh` separately (may need `sudo` to write to the +system Python). See `docs/operations/bash-sandbox-provisioning.md`. + +During UI development, `cd ui && npm run dev` runs Vite with HMR. + +## Tests + +```bash +npm test # all backend tests (vitest) +npx vitest run src/engine/tools/core.test.ts # a single file +``` + +- Backend tests live next to their source as `*.test.ts` (vitest auto-discovers). +- DOM-dependent UI tests need a browser-like environment and may not run in a + headless sandbox. + +## Conventions + +- **Config keys are snake_case in YAML** (`max_concurrency`) and **camelCase in + code** (`maxConcurrency`); `src/config.ts`'s `transformKeys` converts between them. +- New config options must be reflected in `config.yaml.example` **and** + `docs/configuration.md`. +- New tools: add a module under `src/engine/tools/`, register it in + `tools/index.ts`, list it in the relevant Piece's `allowed_tools`, and add a + one-line description plus `docs/tools/.md`. See `docs/maintenance-checklist.md`. +- DB schema changes: update `src/db/schema.sql` and add an idempotent migration in + `src/db/migrate.ts`. + +## Architecture + +See `AGENTS.md` for a contributor-oriented architecture overview and +`docs/architecture.md` for the execution flow in depth. + +## Submitting changes + +1. Branch from `main` (e.g. `feat/...`, `fix/...`). +2. Keep changes focused; add/adjust tests for behavior you change. +3. Ensure `npx tsc --noEmit` is clean and the relevant tests pass. +4. Open a pull request describing the change and how you verified it. + +## License + +By contributing, you agree that your contributions are licensed under the +project's [Apache-2.0](LICENSE) license. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4da9f58 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,72 @@ +FROM node:22-alpine AS builder + +WORKDIR /app + +# 依存関係のインストール +COPY package.json package-lock.json* ./ +COPY ui/package.json ui/package-lock.json* ./ui/ +RUN npm ci --ignore-scripts +RUN npm --prefix ui ci --ignore-scripts + +# noVNC スタンドアロン (vnc.html を含む Web 配布物) を取得。 +# npm の @novnc/novnc は lib のみで vnc.html を含まないため、 +# Browser タブの iframe 用に GitHub から tarball を取得する。 +ARG NOVNC_VERSION=1.6.0 +RUN apk add --no-cache --virtual .novnc-fetch curl tar \ + && mkdir -p /app/vendor/noVNC \ + && curl -fSL "https://github.com/novnc/noVNC/archive/refs/tags/v${NOVNC_VERSION}.tar.gz" \ + | tar -xz -C /app/vendor/noVNC --strip-components=1 \ + && test -f /app/vendor/noVNC/vnc.html \ + && apk del .novnc-fetch + +# TypeScript ビルド +COPY tsconfig.json ./ +COPY src ./src +COPY ui ./ui +RUN npm run build:server +RUN npm run build:ui + +# --- ランタイムステージ --- +FROM node:22-alpine AS runtime + +RUN apk add --no-cache \ + git \ + ca-certificates \ + tzdata \ + bash \ + bubblewrap \ + python3 \ + py3-pip + +# Pre-bake python packages into the system site-packages (read-only bind-mounted +# into every bash sandbox). Runtime `pip install` is intentionally unsupported. +COPY runtime/python-requirements.txt /tmp/python-requirements.txt +RUN pip3 install --no-cache-dir --break-system-packages -r /tmp/python-requirements.txt \ + && rm /tmp/python-requirements.txt + +WORKDIR /app + +# 本番依存のみインストール +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev --ignore-scripts + +# ビルド済み成果物をコピー +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/ui/dist ./ui/dist +COPY --from=builder /app/vendor ./vendor +# schema.sql は dist に含まれないため個別コピー +COPY src/db/schema.sql ./dist/db/schema.sql + +# デフォルト設定 +COPY config.yaml ./ + +# データ永続化ディレクトリ +RUN mkdir -p /data /workspaces + +ENV NODE_ENV=production \ + PORT=9876 \ + DB_PATH=/data/maestro.db + +EXPOSE 9876 + +CMD ["node", "dist/index.js"] diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..0a3e5c8 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,38 @@ + +## MCP Tools: code-review-graph + +**IMPORTANT: This project has a knowledge graph. ALWAYS use the +code-review-graph MCP tools BEFORE using Grep/Glob/Read to explore +the codebase.** The graph is faster, cheaper (fewer tokens), and gives +you structural context (callers, dependents, test coverage) that file +scanning cannot. + +### When to use graph tools FIRST + +- **Exploring code**: `semantic_search_nodes` or `query_graph` instead of Grep +- **Understanding impact**: `get_impact_radius` instead of manually tracing imports +- **Code review**: `detect_changes` + `get_review_context` instead of reading entire files +- **Finding relationships**: `query_graph` with callers_of/callees_of/imports_of/tests_for +- **Architecture questions**: `get_architecture_overview` + `list_communities` + +Fall back to Grep/Glob/Read **only** when the graph doesn't cover what you need. + +### Key Tools + +| Tool | Use when | +|------|----------| +| `detect_changes` | Reviewing code changes — gives risk-scored analysis | +| `get_review_context` | Need source snippets for review — token-efficient | +| `get_impact_radius` | Understanding blast radius of a change | +| `get_affected_flows` | Finding which execution paths are impacted | +| `query_graph` | Tracing callers, callees, imports, tests, dependencies | +| `semantic_search_nodes` | Finding functions/classes by name or keyword | +| `get_architecture_overview` | Understanding high-level codebase structure | +| `refactor_tool` | Planning renames, finding dead code | + +### Workflow + +1. The graph auto-updates on file changes (via hooks). +2. Use `detect_changes` for code review. +3. Use `get_affected_flows` to understand impact. +4. Use `query_graph` pattern="tests_for" to check coverage. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cd44858 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative + Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 MAESTRO contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..15e1873 --- /dev/null +++ b/NOTICE @@ -0,0 +1,7 @@ +MAESTRO +Copyright 2026 MAESTRO contributors + +This product includes software developed as part of the MAESTRO project. + +Licensed under the Apache License, Version 2.0. See the LICENSE file for the +full license text. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b12a63 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# MAESTRO + +![License](https://img.shields.io/badge/license-Apache--2.0-blue) + +**MAESTRO** — タスクを LLM 駆動で実行するエージェントオーケストレーションプラットフォーム。タスクの種類を LLM が自動判定し、適切なワークフロー(**Piece**)で処理する。ツールはサンドボックス化されたランタイムで実行され、ワークスペース・ファイル・進捗を Web UI で管理できる。 + +OpenAI 互換の LLM エンドポイント([Ollama](https://ollama.com/) / vLLM など)があれば単体で動作する。 + +## 主な機能 + +- **タスク自動ルーティング** — タスク本文を LLM が分類し、最適な Piece(YAML ワークフロー)へ振り分け。 +- **Piece × Movement** — ReAct ループで LLM とツールが対話しながら、段階的にタスクを進める。 +- **豊富なツール群** — ファイル操作(Read/Write/Edit/Bash/Glob/Grep)、Office(PDF/Excel/Docx/PPTX)、Web 取得、ブラウザ操作(Playwright)、画像、SQLite、ナレッジ検索(RAG)、SSH、サブタスク並列実行、MCP 連携、ほか。 +- **Bash サンドボックス** — bwrap によるファイルシステム/ネットワーク/環境変数の隔離(不在時は強化版 whitelist にフォールバック)。Python パッケージはプリベイク。 +- **LLM Gateway(任意)** — 仮想キー・予算・メトリクス付きの LLM プロキシ。複数 GPU/チームでの共有運用に対応。 +- **学習(Reflection)・定期タスク・タスク共有・OAuth 認証(Google/Gitea)** — いずれも任意で有効化。 +- **Web UI** — タスク作成・進捗・成果物プレビュー・設定編集・スキル/Piece 管理。 + +## クイックスタート + +### Docker(最短) + +```bash +cp .env.example .env # OLLAMA_BASE_URL などを設定 +docker compose up -d +# http://localhost:9876 を開く +``` + +LLM エンドポイントは `.env` / `config.yaml` で指定する(既定は `http://localhost:11434/v1`)。 + +### ソースから + +```bash +git clone https://gitea.example.com/your-org/maestro.git +cd maestro +npm ci && npm --prefix ui ci +cp config.yaml.example config.yaml # provider / workers を編集 +scripts/build-all.sh +scripts/server.sh start # http://localhost:9876 +``` + +詳しい手順は **[docs/getting-started.md](docs/getting-started.md)** を参照。 + +## 必要要件 + +- **Node.js 22+** +- **OpenAI 互換の LLM エンドポイント**(Ollama / vLLM など) +- 任意(Bash サンドボックス用): `bwrap`(bubblewrap, 非特権 user namespace)+ `python3`/`pip` + +## ドキュメント + +- **[docs/getting-started.md](docs/getting-started.md)** — インストール・初回起動・最初のタスク・認証/サンドボックスの有効化 +- **[docs/configuration.md](docs/configuration.md)** — `config.yaml` の全設定項目リファレンス +- **[docs/architecture.md](docs/architecture.md)** — 実行フロー・Piece/Movement・ツール・DB・サンドボックス +- **[docs/tools/](docs/tools/)** — 各ツールの詳細 +- **[docs/operations/bash-sandbox-provisioning.md](docs/operations/bash-sandbox-provisioning.md)** — 本番でのサンドボックス有効化手順 +- **[AGENTS.md](AGENTS.md)** / **[CONTRIBUTING.md](CONTRIBUTING.md)** — コントリビュータ向け + +## サーバー管理 + +```bash +scripts/server.sh start | stop | restart | status | logs +``` + +## ライセンス + +[Apache-2.0](LICENSE)。 diff --git a/bench/fixtures/notes.md b/bench/fixtures/notes.md new file mode 100644 index 0000000..3254b36 --- /dev/null +++ b/bench/fixtures/notes.md @@ -0,0 +1,7 @@ +# チーム注意事項 (2026 Q1) + +- 売上の集計単位は必ず Q1 (1-3 月) ベース。複数 Q を混在させない +- 公開資料には数値の出典 (シート名・行範囲) を必ず併記する +- レポートは 100 行以内に収める。冗長な説明より要点重視 +- 想定読者は経営層なので業界用語の濫用は避ける +- 「次アクション」は具体的な担当者・期限を含める形で書くこと diff --git a/bench/fixtures/sales.xlsx b/bench/fixtures/sales.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..af055622885fdd28984a0ee8579081854f9cad33 GIT binary patch literal 6696 zcmai32Q-{*(S;tv(-81K!d1mIhs4JnNMM3$2j)Fpp z{MPWEqk|jN!OcYLkrUJv!tG^mSMpTFp^cX)bpH-$>6RiML`xggmR;3HQHVep^^)PD zo8ErjGVMvi{)XfFn+K$vFNEPf86DJWH8J2%R0`s$Q5r&II8!4)d&;_lw3ht18(G*o zfzA}J<@ZRE791qz9_5Cl-ujjuJ1hwXLu2sOmn96|YHM-6ZG%upV}O`;Atz{D?F`qUi*=;QgGG6_v01wyn}Ns>8r@oUYO(1NmQZVTO9 z|5S`>k9B&Ys$5|xCrsdifcFp|`YoP$`-KeTV<|XW#yi~E8>osix+k?ujK&z9igf_< zn@@t6fWMtt2jJfyuz#9Y`Bh?z82{iLa+cOwBfe`F!nD?xbBAQ|Xs!IAIA`b@x}k?; z@dB;-UztGpnF%EMzYZqyHxn1Aoh#3k&1JT>UxBAqk$+RVhJph4Wy5`8S(4bQ+Rlq7 z1@{Fl9lvx?z|P8RxSl(6Q{e7<76;nun4FH_P56^|DZChKtXSub@0NY*{Sq;2F`9dH zcw9v^WEgz|;REmc&a9mYIRKd9%vgL*xp=9|7N^S0iiJH*ar)#6-I(492bu0QcT}Ep z#*+1_^;ydR2gg<;Jl@A5-n@mo9rXk(hZNQfNKMtV7M$K55wvqKbX-k>A68LBgo))INoW-F&^c|w}x#8r_>kNgVtjwA;E8_cwstbJvE$(#F5S;IW(MmG4 z=XG>1LxY$-^>NHj_(s@-hkQ}482Kk_Xh=r9?0!-9PXnAwgMVW-gr!{Pg4sGGvowD< z^K^8vedy?Dd+{V(5pC1XOC$?F#Vy}A@GwvWCb$9vjSTnELaJ(%?Z8&T_NV*Wv&nHck27p4>V zPVeH!lAt01bo#gjQsg6$R{=$=?5NO=6o~eX^%V3Qd$Q|74_QXPpsfMa$}4e426V2! z1TOeD-kaXTGzk`sUO7|@VaW*l$U~DZW{x2bF=vebyr`?x={ozt_2`uZL6IRIVU*5S zH<{xBpyZFnCx-_V9~TpOO_*RkOxx5;0!N_!u0rZZTZ?Oq|D9-pOM)(_akYX%-To|7 z{Gc?17m`SEEEE)~f7t$n+`iy5IeE@;ikGNsZeLgLF%QAvGa_!VA@cqx$gKrm! ziah8hF{e&<8$_f=$;ZtFF9@4>c8R3a@do(KX^%AUNT>W z04pZ)Vrym>SUHvQ^1m7$(q^SHv8mB%bZ&~6PZWD!TWtk1Rw*Cs2HIqNhLMlH!Fa;P z6za2^O$8J>Znmk)QX}bo{G#(Ith#AS+8D>8YCJhL;0po5tY5SThyB)aUI1?WTjutn zYjdiWp42y^>iqX-`Oo+YmdGJW`#9O4G^k&s&9!WD4H5`pl)L%zl@) z{I;HGhza9xN_11=y;X7!Qgllt_%K3w-a?*NG!N{EC@C3TgAbG|t&_GvWDZh@(o`Zm zaE1+Ke7OP<=FJ#NYby^O5ERsL7KT&wm8)E0n!3C=q$|Mn*{0BEt^&}RseZ)k#TAd?TNFuGuH2JcrI!A~>OHH49 zfDs2I5ng5LLO~IORW~*b6VmKJE;t}Qy;X17gfwf=Len8Q0znetR;GS8NC9aAGrWBV zoB)wYz=yXUB$``#C+zwhBn06(*emItunBSw>#;ogMv0{{s>XZ1;kN!N6RtMZN;hFB z_)*D03=mmR<>3>IsBft=tW}^z1s#@~eAS_~EO3g)JM*Wx<}!&Zl4Hv7-I`)K6EG*j zHjxm3!3LM3`dFs;PTudiLenFv#>X=B%|21*SCxI)RLLzo6OWNqT7evcWS79ss^iC`n_*SHnr zAaPme!rO`}d64HbodqM?*(5n8L}qf*d~47B?M~WbT2eI@s95KI$XAWl);LV9`WUu& z%z!t(M-9eJyGs`NjoP`LmJ8}9*Pn8m^uKD?Q#<-bkQyLP4pjBa8$iQFUQ@hye?a>Jxqg-h_R)sbL_+MBcIBq?jH3 zP)nKar*L4ZP1?;F_3NS{H+Ci9lym#6)+Q~^bbM|KA-NK3db>q}fQ0UE3^6CM?*1b0 zMrz`@_24ZMaRzq{B*`zx1&Tpw{*K~ZuJwFRX_=Y1__-EC_H1}q+t?_>7i-}*qC=>Vb8E`rA4nkt@^%H*0Fw}fjTmlpunLTZFnc4J*7U&*e(J(^vDgg8L@{&f+?5UJ8 znIMxRmMKnMyXd-$>E5bGw(7n$$szgC2U)C5Q(r5sIYYf~XDne~^5wFc0-h?*K+Aqi zun}vpKk6dHzP-huX%Gn~aL0J&TRe0xvi|u?H2z#>sKrb+6W7p~6?3sSP)3|k{TY8w zkiCmum`vyc3o=DL;7j2x+4&(mL4p?qO~Kj^1l;^NzG!~U3j*!qEEd3tfiby04-NPZ zg~EiO0|E3x_U>z$mpn;e{Pu-89v~=Z zE!kh{iI`dW-0Q~=R8yAvWvOgKez?S_PEsYY&!P7=N#qV2ii_tAP4S2y%UI3@!3Oj+ zqD~u04gx*4>?NLf;Ar(iNn7j|o~fMJlb_u+sQpxIf7+d)h+9PX!>o6r`6Zu=e15ud zQ0gp-#prOf?iZQ`6b9EkXfDqWy8v|ESDNG-SR$B^U9A`%*TZJn+s~Y!@BzGvJ>ESV5LT-FBBLpWfPx|X#1-|u%hfwY#jhul-9M${rVm()+247uZivlh zVmtPkrVt|@^qiPSKU!I#Sr6{>ZB5W`_1XsUXydYN)8W~|dfISGRFqFYk>|9!50?Q| zc1~>drT`tFPGnQhOeCz;j4D|9g#i1VDG1_m(~Z?N!PirV6&ZR8)iKa` z5=(d_kN1uKgs@S?AQz1cs)nIAH3HMS=Q9*O)~!8W%0}9Pc`-Y+zS$8IgpJGs4@t|z zTZ468dNU-K*$Zq31|S@v`uf~EjR6!bs=b_mN|E>a(p9@p6>wHF%b4lcO#BF80Jag4LvmO(^MJ z$xNPR#pVi@P27=!x>W)PQOL%dkBg&A@07*a2oD*|F*#yMuw8Z)#W_DcbV0AFb(bVHJ%^PSL6UJOAA654%oRtT z7b*^t`gajM{yw%c9rm)OI!aT_=*&4bjv)|Z+6gDM%ucbB_hW_!XzeV*XY}MnPt`N? zK%fvd1uWrq_N;UH5OH(*B@5;1(w z^Ch9?)Vz+>I}_zAUo?z*k|Nn(?)dJqzpV;_lY!iQZOC>eCOc19x)i?}pG7g`)|asE zFSlF`p38}XAs%4tf!rz4kW&T!uPN>J$PW5*Jm)=yIHvNFg~9hZx#sSp;d4C+u(6KR zDoo`NO!8O{02d#jQI9659K*#iNl4tM^7neXO5Yj6e5mHf9&|U3I+09D4oy0-Ea5gxX_tve7_NZq$(tLGcQhh5Ufvtay*5QSQsM`>gBZ1&^4JScNMScP2A4K`k zK9tF{?+g95Mi>^T?~(NR@mPJAi#`SwYmpV_#o67#o68;Pj|abs^}~84_ec; zh#F{$D+yy@hGR4t3$Hwj;-YpT4BoE(g8LSqxwa{WZD63v>2dFLo|B=LQ};caxh;#X zaLjlp^Yy;yDEfX$6@$n@vEb9k=kNFG_CZv~ud?6oZ`(@#u(+M>vvVM}z0HpOdCSh7 z^4su>{gs%O5VfEOe2lklJyUl2zzuU8&=z4?IfD`}d6AeOHCN7E73jZjM&^!Y>Mo8> zf4&=;;Y}w5A~#KD9ecsXL!91YyZcx~4NbeB@Xk4>T zDJd(5*L;mqB4r_f#Q`B)9h=qOO#xKG$xs~T^;81(2uxhNH8SxUO#cv&*846g>upJE z!-Km92rizT6N80}VO>3l+x<+Do7_HUb3+`@1LKTq!@0j%yHC3h(0oNC9re7VVjoL8 z{X*-eI1tW(VL#+6+lQ6TDate68PKkc`$<|?0wO(;>DofJ9^${J%goUQdXcXf%|4{=Le#iJo&YaQC(|a%lfE`as-Qp?_^xiR zMo`7BZ#Hom?#!TK*ntWRPfy>P5KleXR8Jlj7AcL+NHT9zKsTT&L|CZKrbHct$uiWX zGz!ox=c1}@Miv=OC{tna4Cpx(yq9lPI98qzoxCv>t;tiP?ktIeOW$4XOx}eu6K25e z;fp2!wUgtN;udw8qUrQ-V+qb9%gLK*sNjzelEERjLI}KILF9s*lFxUfy3&B0p)sM7 zdGan=jEyv*?hv&#Yc_ni+Dg~XsI9O~u^VkNisq80c^j(yVx%^<8*S-0>b`07xN$Ox zk?J5>2@QFQx*IGDFL|!CS?a+YgLDC_*Zk8DWz`vtRw3UuG7Dpp?v_y&%+ShDP<=*> z;b5q{#0{Dr+G1=s17{JcsRo|)CR%q{W(HCCaB-l18S=}1M?c;f#ovIga#?)7t&|cC zo!OskK3q8IDWl;&3}1YDYg9nVp;AE9Re}a@2Xd14f$%@Gq-O`px==Blky#?V91efi zZDJCY9NT#Dy5PrT8k_N$RCeUIMyga6_?TeMgoR4X+nR~n2Yar7bOGmik}lPgojKK% zqNL#^6ebQ!KyMc{>w~ERY9&2oYMb-xOW7=M7Ymu1mVOfRjt9atCzZF zm7wlt_cRWzzuU)2F2rwLeuO$MqGW_4-G`l3Zh1Z3UD{62!*3xDjk;FYD#zSuc-%U= zpr||b@uNG~?5jhcl&F|l^OOJmJ`rjU%{|@_W?Ei`W zbME{`Uq}9%=9kl}DfB1&&xZIL-iegB{wkF#SzBER + + + +2026年4月 社内発表 + + +

2026年4月 社内発表

+
+

新製品 ProductG 発表

+

当社は新製品 ProductG の正式販売を開始します。市場投入は 2026年Q2 を予定しています。

+
+
+

研究開発投資の増額

+

2026 会計年度の R&D 予算を前年比 15% 増額することが取締役会で承認されました。

+
+
+

新オフィス開設

+

東京・大阪・福岡の 3 拠点に加え、新たに名古屋オフィスを 2026年6月 に開設します。

+
+ + diff --git a/bench/tasks/composite-mini-report.yaml b/bench/tasks/composite-mini-report.yaml new file mode 100644 index 0000000..89c0844 --- /dev/null +++ b/bench/tasks/composite-mini-report.yaml @@ -0,0 +1,105 @@ +id: composite-mini-report +title: 3 ソース統合 + チェックリスト + 形式厳守 + +piece_hint: chat +timeout_minutes: 12 + +fixtures: + - source: fixtures/sales.xlsx + dest: input/sales.xlsx + - source: fixtures/notes.md + dest: input/notes.md + - source: fixtures/web/announcement.html + dest: web/announcement.html + +prompt: | + 以下の手順で `output/report.md` にミニレポートを作ってください。 + + ## 必須手順 (順守すること) + 1. 最初に CreateChecklist で進めるべき TODO を全部登録する (最低 4 項目) + 2. 各 TODO を進めるたびに CheckItem で完了マークを付ける + 3. 完了前に GetChecklist で進捗を確認する + + ## 情報源 + - `input/sales.xlsx` の Sheet1 から「2026年Q1 売上トップ3 商品」を抽出 + - `http://127.0.0.1:{WEB_PORT}/announcement.html` から発表内容を抽出 + - `input/notes.md` からチーム注意事項を抽出 + + ## 出力 `output/report.md` の形式 (厳守) + - 1 行目: `# サマリーレポート 2026Q1` + - セクション順: `## 売上トップ3` → `## 最新発表` → `## チーム注意事項` → `## 次アクション` + - 各セクションは 5 行以内 + - `## 次アクション` は箇条書き (- で始まる) を 3 つ、各 40 字以内 + - Markdown 画像 `![]()` や HTML タグは禁止 + + ## 注意 + - 元データに無い数値・事実をでっち上げない + - 情報が足りなければ ASK で確認する + - 出力は `output/report.md` のみ、他のファイルを作らない + +expected: + must_use_tools: [ReadExcel, WebFetch, Read, Write, CreateChecklist, CheckItem, GetChecklist] + forbidden_tool_for_ext: + Read: ['.xlsx', '.docx', '.pptx', '.xls', '.doc', '.ppt'] + must_produce_files: [output/report.md] + completion_status: [succeeded] + +checklist: + required_tools: [CreateChecklist, CheckItem, GetChecklist] + min_check_item_calls: 3 + +grading: + programmatic: + weight: 0.7 + constraints: + - type: file_first_line_equals + file: output/report.md + line: '# サマリーレポート 2026Q1' + - type: file_must_contain_in_order + file: output/report.md + sections: ['## 売上トップ3', '## 最新発表', '## チーム注意事項', '## 次アクション'] + - type: file_section_max_lines + file: output/report.md + section: 売上トップ3 + max: 5 + - type: file_section_max_lines + file: output/report.md + section: 最新発表 + max: 5 + - type: file_section_max_lines + file: output/report.md + section: チーム注意事項 + max: 5 + - type: file_line_starts_with + file: output/report.md + prefix: '-' + min_lines: 3 + section: 次アクション + - type: file_line_max_chars + file: output/report.md + max: 40 + section: 次アクション + - type: file_no_pattern + file: output/report.md + pattern: '!\[' + - type: file_no_pattern + file: output/report.md + pattern: '<[a-zA-Z][^>]*>' + + llm_judge: + weight: 0.3 + rubrics: + - name: factual_grounding + prompt: | + レポート内の売上トップ3 / 発表内容 / 注意事項 が、与えられた 3 ソース (sales.xlsx, + announcement.html, notes.md) に忠実か。捏造や混同があれば減点。 + max_score: 10 + - name: actions_quality + prompt: | + 「次アクション」3 項目が、3 ソースの内容を踏まえた具体的・行動可能なものか。 + 抽象的すぎる、ソースと無関係な内容は減点。 + max_score: 10 + - name: synthesis + prompt: | + 3 ソースの統合がレポート全体として論理的に整合しているか。 + max_score: 10 diff --git a/bench/tasks/reflection-smoke.yaml b/bench/tasks/reflection-smoke.yaml new file mode 100644 index 0000000..ed3b312 --- /dev/null +++ b/bench/tasks/reflection-smoke.yaml @@ -0,0 +1,115 @@ +# reflection-smoke.yaml +# +# Smoke test for the reflection / Hermes-mode system. +# +# DESIGN NOTE — why this is a single-step task +# ───────────────────────────────────────────── +# The ideal reflection bench is a two-run sequence: +# Run 1: submit a task + negative feedback → reflection fires → memory +# entry "feedback_user_prefers_terse_output" is written. +# Run 2: submit a second task → the reflection-produced memory entry +# appears in the system prompt → response is demonstrably terse. +# +# The current bench harness (src/bench/runner.ts) does not support multi-run +# sequences or DB assertions (reflection_metrics, memory tables). The grader +# (src/bench/grader.ts) only evaluates: +# A — tool calls from activity.log +# B — checklist tool usage +# C — file output constraints (file_first_line_equals, file_no_pattern, etc.) +# D — LLM judge rubrics against output files +# +# Therefore this YAML exercises a single task whose prompt explicitly carries +# the lesson ("one-line terse reply") that reflection would have injected into +# the system prompt on a second run. The programmatic constraints enforce the +# structural signature of a terse reply, and the LLM judge validates content +# quality. This gives a useful regression gate even without multi-step support. +# +# FULL TWO-RUN FLOW (for manual / integration testing) +# ───────────────────────────────────────────────────── +# 1. Start orchestrator with reflection.enabled: true and a reflection worker. +# 2. Submit a chat task with body: +# "Summarise the Pythagorean theorem." +# The agent will produce a verbose multi-paragraph response. +# 3. Rate that task feedback_rating='bad' via the UI. +# 4. Wait ~60 s. A task_kind='reflection' job should appear in the jobs table +# with outcome='applied' in reflection_metrics. +# Verify: SELECT outcome FROM reflection_metrics ORDER BY created_at DESC LIMIT 1; +# 5. In data/users//memory/, confirm a file like +# feedback_user_prefers_terse_output.md exists. +# 6. Submit a second task: "Summarise the Pythagorean theorem." +# The reflection memory should now be in the system prompt. +# The response should be ≤ 3 sentences with no "Certainly!" preamble. +# +# HOW THIS FILE IS DISCOVERED +# ─────────────────────────── +# The bench runner (scripts/bench-run.ts) does: +# glob("bench/tasks/*.yaml") +# No registration step is needed. Drop this file and it is automatically +# included in `npm run bench` and `npm run bench -- --task=reflection-smoke`. + +id: reflection-smoke +title: Reflection smoke — terse reply under explicit lesson +piece_hint: chat +timeout_minutes: 5 + +prompt: | + IMPORTANT USER PREFERENCE (simulating a reflection-injected memory entry): + The user prefers terse, one-line replies with no preamble phrases such as + "Certainly!", "Of course!", "Sure!", "Great question!", or "Happy to help!". + + Task: What is the Pythagorean theorem? + + Instructions: + 1. Write your answer to `output/answer.md`. + 2. The answer MUST be a single Markdown line (no headings, no bullet lists). + 3. The line MUST NOT start with a preamble phrase. + 4. The line MUST be 120 characters or fewer. + +expected: + must_use_tools: [Write] + forbidden_tools: [Bash] + must_produce_files: [output/answer.md] + completion_status: [succeeded] + +grading: + programmatic: + weight: 0.6 + constraints: + # The output must be a single non-empty line — no second non-empty line. + - type: file_no_pattern + file: output/answer.md + pattern: '(?m)^.+\n\n?.+' + # Must not contain heading markers. + - type: file_no_pattern + file: output/answer.md + pattern: '^#' + # Must not start with common preamble phrases. + - type: file_no_pattern + file: output/answer.md + pattern: '(?i)^(certainly|of course|sure[,!]|great question|happy to help|absolutely)[!,.]' + # Must not use bullet / numbered lists. + - type: file_no_pattern + file: output/answer.md + pattern: '(?m)^[-*\d]' + # Each line ≤ 120 chars (the single content line). + - type: file_line_max_chars + file: output/answer.md + max: 120 + + llm_judge: + weight: 0.4 + rubrics: + - name: terseness + prompt: | + The output should be a single terse line (no preamble, no bullet list, + no heading) that correctly states the Pythagorean theorem. + Score 10 if the answer is ≤ 2 short sentences, factually correct, and + starts directly with the mathematical content (e.g. "In a right triangle…" + or "a² + b² = c²…"). + Deduct points proportionally for verbosity, preamble phrases, or inaccuracy. + max_score: 10 + - name: factual_accuracy + prompt: | + Does the answer correctly state the Pythagorean theorem + (a² + b² = c² for a right triangle)? Score 10 for correct, 0 for wrong. + max_score: 10 diff --git a/config.yaml.example b/config.yaml.example new file mode 100644 index 0000000..aabfdb4 --- /dev/null +++ b/config.yaml.example @@ -0,0 +1,395 @@ +# MAESTRO 設定ファイル (v2 layout) +# +# このファイルを config.yaml にコピーして編集してください: +# cp config.yaml.example config.yaml +# +# ─── v2 への移行 (v1 → v2) ───────────────────────────────────── +# 旧構造 (provider.* / 平置きの worktree_dir 等) は 1 リリース分だけ +# 読み取り互換が残っています。手元の config.yaml が旧形式の場合は: +# +# scripts/migrate-config.sh --dry-run # 変換後を確認 +# scripts/migrate-config.sh # in-place で書き換え (.bak を自動保存) +# +# v3.0 で v1 形式は起動 fatal になる予定です。 + +# v2 schema バージョン (必須)。 +# - 2 : このリリースの正規形 (= 本ファイル) +# - 1 / 未指定 : v1 互換読み取り + 起動 warning +# - その他 : 起動 fatal (typo / 未来形式の混入防止) +config_version: 2 + +# ─── LLM ───────────────────────────────────────────────────── +# ジョブ実行時に LLM 呼び出し先として使う接続群と、retry / timeout / metrics。 +llm: + timeout_minutes: 10 # 1 リクエスト全体の上限 (分)。default 10 + + retry: + max_attempts: 3 + backoff_ms: [2000, 5000, 15000] # 429 / 5xx / 一時的接続失敗時の待機 ms + retryable_status: [429, 500, 502, 503, 504] + + # workers[] — この AAO がジョブ実行時に呼ぶ接続先。 + # + # connection_type: + # direct — Ollama / llama.cpp / vLLM 等の OpenAI 互換 backend に直接接続 + # aao_gateway — 別 AAO Gateway 経由で接続 (Gateway Key 必須) + # + # トップレベル `gateway.*` (この AAO 自身が gateway として動く設定) と + # 単語衝突を避けるため、worker 側は `aao_gateway` と prefix 付き。 + # + # model はワーカーごとに明示。`default_model` は廃止された。 + # roles: 用途別 (auto / fast / quality / title / reflection 等) のフィルタ。 + # max_concurrency: ワーカー単位の並列度。 + # vlm: true で画像入力に対応 (ReadImage は VLM ワーカーを優先)。 + workers: + - id: local-ollama + connection_type: direct + endpoint: http://localhost:11434/v1 + model: qwen3:32b + roles: [auto, fast, quality] + max_concurrency: 1 + enabled: true + vlm: false + + # 例: 別 AAO Gateway 越しに共有 GPU プールを使う + # - id: team-gateway + # connection_type: aao_gateway + # endpoint: http://gateway.example.com:9876/v1 + # api_key: ${TEAM_AAO_GATEWAY_KEY} # gateway 発行の sk-aao-*** virtual key + # model: qwen3:32b + # roles: [quality] + # max_concurrency: 2 + # enabled: true + + # 例: タイトル生成専用ワーカー (chat ジョブは受け付けない) + # - id: title-worker + # connection_type: direct + # endpoint: http://localhost:11434/v1 + # model: qwen3:8b + # roles: [title] + # max_concurrency: 4 + # enabled: true + + # 例: Reflection 専用ワーカー (cheap モデルで memory 更新を回す) + # - id: reflector + # connection_type: direct + # endpoint: http://localhost:11434/v1 + # model: qwen3:8b + # roles: [reflection] + # max_concurrency: 1 + # enabled: true + + # Prometheus exporter (worker side). default で enabled。 + # /metrics が bridge HTTP server (PORT, default 9876) に mount される。 + # access control: default では localhost (127.0.0.1 / ::1) のみ通る。 + # 本番では (a) bearer_token を設定するか、(b) allowed_hosts で前段 IP を許可。 + # env: AAO_WORKER_METRICS_BEARER_TOKEN / AAO_WORKER_METRICS_ALLOWED_HOSTS (CSV) でも上書き可。 + metrics: + enabled: true + prefix: aao_worker # /^[a-z][a-z0-9_]*$/ + # bearer_token: env:AAO_WORKER_METRICS_BEARER_TOKEN + # allowed_hosts: + # - 127.0.0.1 + # - ::1 + # - localhost + # # - 0.0.0.0 # 全許可。前段で firewall を必ず使う運用前提 + +# ─── AAO Gateway Server ────────────────────────────────────── +# この AAO 自身を OpenAI 互換 LLM Gateway として公開する設定。 +# 有効化すると `/v1/chat/completions` などが同 process で立ち上がる。 +# +# Virtual Keys (sk-aao-*** 形式) は **admin REST API での発行を推奨**: +# POST /api/admin/gateway/keys +# config.yaml の virtual_keys[] は bootstrap / backup 用途のみで、 +# DB に自動 import される (source='config-import')。rotation は admin API 経由でのみ。 +# +# 同 process / separate process の deploy 方法は docs/aao-gateway-overview.md を参照。 +# UI からは Settings → LLM → Gateway Server で全て編集可能。 +# gateway: +# enabled: false # true で同 process gateway が即時起動 +# # (ConfigManager hot reload 対応、再起動不要) +# listen_port: 4000 # separate-deploy 時のみ有効 (default LiteLLM 互換) +# request_timeout_sec: 600 # 1 リクエスト全体 (streaming 込み) +# upstream_timeout_sec: 30 # 各 upstream fetch の TTFB 上限 +# shutdown_graceful_sec: 30 # SIGTERM 後、in-flight SSE の drain 上限秒 +# +# backends: +# - id: gpu-a # `x-aao-backend-id` / `/v1/models` に出る ID +# endpoint: http://gpu-a:11434/v1 +# model: qwen3:32b # 厳密一致 routing +# max_slots: 2 # llama-server -np と合わせる +# api_key: ${GPU_A_API_KEY} # backend が bearer 必須な場合のみ +# - id: gpu-b +# endpoint: http://gpu-b:11434/v1 +# model: qwen3:32b +# max_slots: 2 +# +# # Bootstrap / Backup 専用 virtual_keys (新規発行は admin API 経由を推奨)。 +# # virtual_keys: +# # - key: ${TEAM_ALPHA_KEY} # 起動時に DB へ idempotent import +# # team: alpha +# # allowed_models: [qwen3:32b] +# # # tokens_budget: 1000000 # 月次 token 上限 (UTC 月初に reset) +# # # rate_limit_rpm: 60 # 1 分あたり最大リクエスト数 +# +# # Prometheus exporter (gateway side)。default enabled。 +# # team / key_prefix / backend ラベルが出るので auth 必須運用 (default localhost のみ)。 +# # env: AAO_GATEWAY_METRICS_BEARER_TOKEN / AAO_GATEWAY_METRICS_ALLOWED_HOSTS (CSV) で上書き可。 +# # metrics: +# # enabled: true +# # prefix: aao_gateway +# # bearer_token: env:AAO_GATEWAY_METRICS_BEARER_TOKEN +# # allowed_hosts: +# # - 127.0.0.1 +# # - ::1 +# # # - 0.0.0.0 # 前段 firewall 必須 + +# ─── Storage / Paths ───────────────────────────────────────── +# 旧 worktree_dir / custom_pieces_dir / user_folder_root / +# tools.task_upload_max_size_mb / tools.trash_retention_days +# は normalizer により storage.* に集約された。 +storage: + worktree_dir: ./data/workspaces # ジョブ実行時の作業ディレクトリのベース + # custom_pieces_dir: ./custom-pieces # リポジトリ内の pieces/ に加えて読みに行く Piece dir (任意) + user_folder_root: ./data/users # {root}/{userId}/ 配下に AGENTS.md/scripts/notes 等を保存 + task_upload_max_size_mb: 50 # /api/local/tasks と /comments の body 上限 (MB) + # base64 で乗るので実ファイル目安は値 × 0.75。範囲 [1, 1000] + trash_retention_days: 30 # data/users/{userId}/trash/ の自動 sweep (起動時 + 24h 周期) + # 0 で sweep 毎に全削除 + +# ─── Execution ─────────────────────────────────────────────── +# ジョブ全体の並列度・movement 上限・ジョブ retry。 +concurrency: 4 # 全 worker 合算の最大並列ジョブ数 (env: CONCURRENCY) +max_movements: 200 # 1 ジョブ内の最大 movement 数 (loop 防止) +retry: + max_attempts: 3 # ジョブ失敗時の最大再試行回数 + backoff_seconds: [60, 300, 900] # 各 attempt 間の待機秒 + +# ASK (ユーザーへの質問) 制御 +ask: + max_per_job: 2 # 1 Job あたりの ASK 上限 + +# Subtask 制御 +subtasks: + max_depth: 2 # SpawnSubTask のネスト最大深度 + max_per_parent: 10 # 1 ジョブが生成できるサブタスクの最大数 + +# ─── Context (LLM コンテキスト管理) ─────────────────────────── +# context: +# limit_tokens: 128000 # 省略時は Ollama API で自動取得、それも失敗なら 128000 +# thresholds: +# - ratio: 0.7 +# action: warn +# - ratio: 0.85 +# action: prompt +# - ratio: 0.95 +# action: force_transition + +# ─── Safety (エージェント自爆防止) ──────────────────────────── +# safety: +# max_iterations: 200 # 1 movement 内の最大イテレーション +# max_revisits: 3 # 同一 movement の最大再訪問 +# prompt_guard_ratio: 0.8 # コンテキスト上限の何 % まで prompt を許容するか (0.5–0.95) +# history_summarization: # 古い turn を構造化要約に置換して粘る (Opencode 方式) +# enabled: true # default true +# tail_turns: 2 # 末尾何 turn を必ず保護するか +# preserve_recent_budget: 8000 # 末尾保護の最大トークン数 +# bash_unrestricted: false # true: コマンドホワイトリストを撤廃し任意コマンド実行可。 +# # 代わりに bwrap サンドボックスで workspace 単位の +# # ファイルシステム隔離を強制 (タスク間の横断アクセス防止)。 +# # 前提: コンテナで user namespace が有効 (nesting=1)。 +# # 起動時に bwrap の動作確認を行い、失敗時はエラー終了。 +# # Bash サンドボックス機構: +# # auto (既定) bwrap があれば sandbox、無ければ hardened-whitelist にフォールバック +# # always sandbox を強制。bwrap 不在なら起動失敗(本番推奨) +# # off bwrap を使わない(env スクラブは維持)。デバッグ用、非推奨 +# bash_sandbox: auto + +# ─── Search Filter (WebSearch の機密情報漏洩防止) ───────────── +# search_filter: +# blocked_patterns: # カスタムブロックパターン (完全一致で除去) +# - secret-project +# - internal-codename +# auto_block: # 自動検出 (default: 全 true) +# private_ip: true # 10.* / 172.16-31.* / 192.168.* / 127.* +# internal_domain: true # .local / .internal / .lan / .intranet / .corp / .home +# email: true +# phone: true # 日本の電話番号 + +# ─── Browser Runtime (BrowseWeb / BrowserAction) ────────────── +# browser: +# page_timeout: 60000 # ms +# action_timeout: 30000 # ms +# captcha_solve: novnc # 'skip' (default) / 'novnc' +# max_captcha_pages: 5 +# channel: chrome # 'chromium' (default) / 'chrome' / 'msedge' +# executable_path: /usr/bin/google-chrome # channel と排他 + +# ─── Tools (Web & Search / Media / External / Legacy) ──────── +# UI 上は 5 カテゴリに分かれて編集可能 (Web & Search / Browser Runtime / +# Media & Documents / External Services / Legacy Knowledge)。YAML は +# 互換のため `tools` 1 ブロックで管理。 +tools: + # Web & Search + searxng_url: http://localhost:8080 # WebSearch フォールバック先 (通常は Playwright + Google) + webfetch_timeout: 30 # WebFetch / DownloadFile timeout (sec) + # websearch_timeout: 15 + # webfetch_allowed_hosts: # SSRF 例外 (private IP / .local 等を許可する場合) + # - my-internal-host.local + + # Media & Documents + # vision_model: qwen2-vl:8b-instruct # ReadImage 用 VLM (provider と別エンドポイントなら vision_base_url) + # vision_base_url: http://localhost:11434/v1 + # vision_timeout: 60 + # vision_max_tokens: 1024 + # ocr_model: glm-ocr # OCR 用モデル (vision_base_url の server に問い合わせる) + # office_excel_max_size_mb: 10 # ReadExcel 上限 (default 10) + # office_docx_max_size_mb: 10 # ReadDocx 上限 + # office_pdf_max_size_mb: 10 # ReadPdf 上限 + # office_pptx_max_size_mb: 50 # ReadPPTX 上限 + # office_pptx_max_uncompressed_mb: 200 # PPTX ZIP 展開後上限 (zip-bomb 検知) + # speech_server_url: http://localhost:8000/v1 + # speech_timeout: 300 + # speech_language: ja + + # External Services + # x_cli_command: ["twitter"] # twitter-cli 実行コマンド + # x_timeout: 90 + # x_auth_token: "..." # 任意: auth_token cookie + # x_ct0: "..." # 任意: ct0 cookie + # x_proxy: http://127.0.0.1:7890 # 任意: twitter-cli 用 proxy + # x_chrome_profile: "Profile 2" # Chrome cookie 抽出 profile + # x_download_media: auto # 'auto' (default) / 'never' + # x_download_video: thumbnail # 'thumbnail' (default) / 'full' / 'never' + # x_media_max_mb: 25 + # x_media_fetch_timeout_seconds: 15 + # google_maps_api_key: "..." # 未設定なら Nominatim / OSRM + # maps_timeout: 30 + # amazon_affiliate_tag: "your-tag-22" + # keepa_api_key: "..." + + # User scripts (RunUserScript) + # user_scripts_enabled: false # true で許可。plain runtime は Node --permission で sandbox 化 + # user_scripts_allow_userids: # 未指定 = 全ユーザー許可 (user_scripts_enabled に従う) + # - alice-id + # - bob-id + + # Legacy Knowledge (DKS) — 新規 namespace 追加は MCP 経由を推奨 + # knowledge_service_url: http://dks-server:8100 # 未設定で knowledge ツール無効 + # knowledge_namespaces: + # product-a-support: + # api_key: "sk-product-a-xxx" + # contract-review: + # api_key: "sk-contract-yyy" + +# ── Shared Knowledge Notes ─────────────────────────────────── +# data/users/{userId}/notes/ のノートをシステムプロンプトに自動注入する設定。 +# notes: +# inject: +# per_note_max_kb: 8 # 日本語コンテンツは 4 推奨 +# total_max_kb: 32 +# over_budget_strategy: skip_remaining # truncate_last / skip_remaining (default) / degrade_to_search + +# ─── 認証 (オプション) ──────────────────────────────────────── +# 未設定なら認証なしで動作 (従来互換)。 +# auth: +# session_secret: "ランダムな文字列を設定してください" +# session_max_age: 86400000 # 24h (ms) +# secure_cookie: false # HTTPS 環境では true +# admin_emails: +# - "admin@example.com" +# # primary_provider: gitea # 'google' | 'gitea'。両方有効時に明示 +# providers: +# google: +# client_id: "" +# client_secret: "" +# callback_url: "http://localhost:3000/auth/google/callback" +# gitea: +# client_id: "" +# client_secret: "" +# base_url: "https://gitea.example.com" +# callback_url: "http://localhost:3000/auth/gitea/callback" + +# ─── Branding (オプション) ──────────────────────────────────── +# config.yaml / data/branding/ は .gitignore 済みで git pull 影響なし。 +# Settings → System → Branding で GUI 編集可 (admin)。 +# branding: +# app_name: "My Team AI" +# primary_color: "#2563eb" +# login_page_title: "My Team AI" +# logo_url: "/branding/logo-abc123.svg" +# favicon_url: "/branding/favicon-def456.png" +# footer_text: "© 2026 Your Team" + +# ─── Secrets ───────────────────────────────────────────────── +# secrets: +# master_key_path: ./data/secrets/master.key # 32-byte key, auto-generated on first start (mode 0600) + +# ─── Reflection ("Hermes" mode) ────────────────────────────── +# default OFF。ON にすると毎ジョブ完了後に user memory を LLM が自動更新する。 +# snapshot は data/users/{userId}/.reflection-history/ に残り UI から revert 可。 +# reflection: +# enabled: false +# max_memory_changes_per_job: 3 +# piece_edit_cooldown_hours: 24 +# snapshot_retention_days: 90 +# per_user_daily_budget_tokens: 200000 + +# ─── MCP (Model Context Protocol) ──────────────────────────── +# Individual servers は admin UI (global) または各ユーザー (self-hosted) で管理。 +# MCP_ENCRYPTION_KEY env (64 hex chars) が必須。 +# mcp: +# call_timeout_seconds: 60 +# max_binary_size_mb: 20 +# max_output_files_per_job: 10 +# max_output_size_mb_per_job: 200 +# tool_cache_ttl_seconds: 600 +# oauth_pending_ttl_minutes: 10 +# # allow_private_addresses: false # 自前 MCP server を private 網に置く場合 true + +# ─── SSH (off by default) ──────────────────────────────────── +# 有効化手順は docs/ssh.md / config.yaml.example のコメント (旧版) を参照。 +# Operator runbook: docs/ssh.md +# Sample piece: pieces/ssh-ops.yaml +# +# ssh: +# enabled: false +# +# # allow_private_addresses: false # global default。admin は per-connection で grant 可 +# # call_timeout_seconds: 30 +# # max_output_bytes: 32768 +# # max_upload_size_mb: 100 +# # max_download_size_mb: 100 +# # audit_retention_days: 90 +# # admin_bypasses_grants: true +# # abuse_window_minutes: 10 +# # abuse_failure_threshold: 5 +# # abuse_lock_minutes: 30 +# +# # ── Interactive SSH Console (live PTY-backed shell) ── +# console: +# enabled: false # true で SshConsole* tools + UI Terminal タブを公開 +# idle_timeout_seconds: 1800 # 30min I/O-less = auto close +# max_session_duration_seconds: 14400 # 4h hard cap +# scrollback_bytes: 524288 # 512KB scrollback / session +# max_sessions_per_connection: 3 +# max_input_bytes_per_send: 16384 +# auto_inject_screen_lines: 24 +# default_cols: 120 +# default_rows: 32 + + +# ── Browser Notifications V2 (Web Push) ─────────────────────────────── +# Requires HTTPS hosting + (for iOS) PWA installation on the client side. +# notifications: +# push: +# enabled: false # true で V2 を有効化 +# vapid_subject: "https://maestro.example.com/" # RFC 8292 — operations URL preferred +# vapid_current_path: "./data/secrets/vapid.json" # 自動生成 (mode 0600) +# vapid_history_dir: "./data/secrets/vapid-history" +# payload_max_bytes: 3072 # JSON byte length cap (上限 4096) +# queue_concurrency: 8 +# per_send_timeout_ms: 10000 +# +# 起動時に vapid_current_path に鍵が無ければ自動生成、mode 0600 で保存。 +# 鍵をローテーションする場合: npm run vapid-rotate diff --git a/deploy/maestro.service b/deploy/maestro.service new file mode 100644 index 0000000..693f7aa --- /dev/null +++ b/deploy/maestro.service @@ -0,0 +1,25 @@ +[Unit] +Description=MAESTRO +After=network.target + +[Service] +Type=simple +User=maestro +WorkingDirectory=/opt/maestro +ExecStart=/usr/bin/node dist/index.js +Restart=on-failure +RestartSec=10 +EnvironmentFile=/opt/maestro/.env + +# Logging +StandardOutput=journal +StandardError=journal +SyslogIdentifier=maestro + +# Security hardening +NoNewPrivileges=true +ProtectSystem=strict +ReadWritePaths=/opt/maestro/data /var/lib/maestro/workspaces + +[Install] +WantedBy=multi-user.target diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0e8e893 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,36 @@ +services: + maestro: + build: + context: . + dockerfile: Dockerfile + image: maestro:latest + container_name: maestro + restart: unless-stopped + ports: + - "9876:9876" + env_file: + - .env + environment: + - NODE_ENV=production + - PORT=9876 + - DB_PATH=/data/maestro.db + - WORKTREE_DIR=/workspaces + volumes: + # SQLite DB 永続化 + - maestro-data:/data + # エージェントワークスペース永続化 + - maestro-workspaces:/workspaces + # 設定ファイル (任意でホストからマウント) + # - ./config.yaml:/app/config.yaml:ro + healthcheck: + test: ["CMD", "node", "-e", "fetch('http://localhost:9876/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + +volumes: + maestro-data: + driver: local + maestro-workspaces: + driver: local diff --git a/docs/aao-gateway-overview.md b/docs/aao-gateway-overview.md new file mode 100644 index 0000000..42916e5 --- /dev/null +++ b/docs/aao-gateway-overview.md @@ -0,0 +1,239 @@ +# AAO Gateway モード — 機能概要 (2026-05-20 時点) + +このドキュメントは「LiteLLM Proxy 代替として AAO に追加された Gateway 機能」が今回の一連の作業でどう変わったかを日本語で解説します。技術詳細の設計 doc は末尾の関連ドキュメントセクションを参照。 + +--- + +## TL;DR (3 行) + +- **AAO 自身が OpenAI 互換 LLM Gateway として動けるようになりました。** 他の AAO や任意の OpenAI クライアントから `/v1/chat/completions` を叩けます。 +- **設定 UI のトグル 1 つで on/off** (同一プロセス内で起動・停止)。別プロセスで動かす運用も従来通り可能 (advanced)。 +- 仮想 API キーごとに **月次 token 予算 + RPM レート制限**、Prometheus でリアルタイム監視。Telemetry 外部送信ゼロ。 + +--- + +## なぜこれを作ったか + +LiteLLM Proxy には次の懸念がありました: + +1. **Telemetry**: デフォルトで匿名利用データが外部送信される (opt-out 可だが要設定)。 +2. **License 変更リスク**: MIT → BSL/SSPL への変更前例があり、組織で長期運用するには不確実性が高い。 +3. **追加依存**: Python サービス + Redis (任意) + DB を、AAO とは別に運用する必要がある。 + +これらを **「AAO 単一バイナリ + 既存 SQLite + telemetry 完全ゼロ」** で代替するのが本機能の目的です。LiteLLM 固有の高度機能 (sticky routing / canary / cost USD / OpenTelemetry / Slack アラート / multi-region federation 等) は **意図的にスコープ外**にして、シンプルな in-house ゲートウェイに振り切りました。 + +--- + +## どの機能が、どのタイミングで入ったか + +時系列に沿って、追加された機能とユーザーへの影響を解説します。 + +### Phase 1: Gateway モードの土台 (PR #326) + +| 項目 | 内容 | +|---|---| +| 起動方法 | `AAO_MODE=gateway` を環境変数指定すると、worker / scheduler / UI を起動せず、軽量に gateway サーバだけ立つ | +| エンドポイント | `/v1/chat/completions` (SSE ストリーミング) + `/v1/models` + `/health` (LiteLLM 互換 JSON) | +| 認証 | Bearer Token (config.yaml に static な virtual keys) | +| ルーティング | 複数 backend (llama-server / vLLM など) を **least-busy** (進行中リクエスト数が一番少ない backend) で振り分け | +| 安全停止 | SIGTERM 時に進行中 SSE を `shutdown_graceful_sec` (デフォルト 30s) 待って drain、`gateway_shutdown` SSE event でクライアントに retry を促す | + +### Phase 2a: 仮想キーを DB で管理 + Admin REST API (PR #330) + +- API キー形式: `sk-aao-` (32 文字、23 bytes エントロピー、SHA-256 hash で DB 保存) +- **raw key は発行直後の 1 回だけ表示**、以降は prefix しか見えない (LiteLLM 同様の安全設計) +- Admin REST API で発行 / 一覧 / rotate / soft delete (revoked_at) +- config.yaml の static key は起動時に自動で DB に移行 +- `team` フィールドでテナント分離 + +### Phase 2b: 予算とレート制限 + UI 一式 (PR #332) + +| 機能 | 実装 | +|---|---| +| 月次 token 予算 | キーごとに `tokens_budget`。UTC 月初に counter リセット。超過した次のリクエストで 429 を返す (post-hoc enforcement) | +| RPM レート制限 | キーごとに `rate_limit_rpm`。sliding 60s window、in-memory カウンタ + 30s 周期で DB flush | +| 使用量集計 | `gateway_key_usage` テーブルに (key_id, period_start) 単位で tokens_in / tokens_out / requests を記録 | +| UI | Settings → Tools → **"Gateway Keys"** タブ。発行・無効化・rotate・月次グラフ | + +### Phase 3a: Polish bundle (PR #333) + +Phase 1-2b で残った INVESTIGATE 8 件をまとめて解消: + +1. Orphan key 検出時の警告ログ +2. `Authorization: Bearer ...` の regex を RFC 6750 厳密に +3. 5 秒 LRU の key auth cache (DB 負荷削減) +4. config 由来 key の drift resync (config.yaml を書き換えた時の DB 整合) +5. PATCH で revoked key を編集しようとしたら 409 conflict +6. Dead field cleanup +7. MAX_TRACKED_KEYS LRU eviction (rate limiter) +8. **SSE error の sentinel 化**: `gateway_shutdown` / `gateway_timeout` / `budget_exhausted` / `rate_limited` の 4 種をクライアントが識別可能に + +### Phase 3b: Prometheus exporter (PR #335) + +- **Gateway 11 metrics**: requests_total / tokens_total / backend_busy_slots / key_cache_size / latency histogram など +- **Worker 6 metrics**: jobs_total / piece_runs / queue depth など +- 全 metric に **per-team label** を載せて、テナント単位の利用量を分離可視化 +- `/metrics` は **デフォルトで localhost 限定** (`127.0.0.1` / `::1` allowlist)、外部 Prometheus 用に bearer token opt-in +- cardinality 暴走を防ぐため、label を 1 桁オーダーに抑える discipline + +### cleanup (PR #337) + +- AAO の LLM クライアント (`openai-compat.ts`) で gateway sentinel SSE error を parse + +### ops (PR #340) + +- `scripts/gateway.sh start | stop | status | logs` (`.gateway.pid` 管理、`logs/gateway.log`) +- `AAO_CONFIG` env で config.yaml path を override 可能 (両モード共通) +- `GATEWAY_PORT` env で listen port を override 可能 +- DB 共有設計 (worker と gateway は同一 SQLite を WAL で共有可能) を doc に明記 + +### Phase 3c: UI 制御 + 同プロセス default ← **本セッションで完了 (PR #341)** + +ここが今回の最大の変化です。これまで「gateway 専用プロセスを別 port で起動する」モデルだったのを抜本的に変更: + +| Before (Phase 1-3b) | After (Phase 3c) | +|---|---| +| `AAO_MODE=gateway` で gateway 専用プロセスを別 port (例: 9877) に起動 | 通常の worker AAO の **同一プロセス・同一 port** で動作 | +| Worker UI と gateway を物理的に分離 | UI のトグル 1 つで gateway を mount / unmount | +| 各 AAO に gateway 用の追加デプロイが必要 | 既存 AAO を起動して UI から有効化するだけ | + +#### 新しい設定 UI (Settings → Tools → "Gateway Server") + +- **Enable Gateway** トグル: チェックで gateway 起動、外して停止 +- **Backends list** (フォーム編集): + - Endpoint URL (http/https) + - Backend ID (任意の文字列、ルーティング識別子) + - Max slots (並列処理上限) + - API key (backend 側に必要な場合) +- 編集 + Save で **hot reload**: 進行中 SSE は `gateway_shutdown` event で drain、新接続から新 config で動作 +- **リアルタイム状態 badge**: `running` / `disabled` / `misconfigured` を 3 秒周期で表示 + +#### 動作モード + +- **Gateway 有効時**: `/v1/*` paths が gateway sub-app にルーティングされ、`/health` は LiteLLM 互換 JSON を返す +- **Gateway 無効時**: `/v1/*` は 404、`/health` は bridge の `{status:'ok'}` (Docker healthcheck 等の既存利用に影響なし) + +#### Phase 3c で重要だった 8 件のバグ修正 + +レビューで critical な問題が見つかり、すべて修正済み: + +1. **prom-client メトリクス重複登録クラッシュ**: gateway 設定変更 (= bounce) 1 回ごとに `Counter` を新規登録 → 2 回目で throw して bridge プロセスごと死ぬ問題。`WeakMap>` で memoize して解決 +2. **BackendStatusRegistry が worker 用の list を見ていた**: 同プロセス mode で gateway も worker と同じ registry を共有していたため、gateway 専用の backend が status 不明 → routing blind / `/health` 空 / metrics 0。Gateway は自前 registry を `gateway.backends[]` 上に build する設計に +3. **`/health` LiteLLM 互換が壊れていた**: bridge の既存 `/health` (`{status:'ok'}`) が mountGateway より先に登録されていて、LiteLLM 互換 JSON が返らない問題。`classifyGatewayPath` を 3 値 (`gateway-only` / `gateway-when-enabled` / `false`) に拡張し、Express middleware の登録順序を修正 +4. **transition 中の config が dropped される**: `state === 'starting' | 'stopping'` 中に新規 config 適用が落ちる問題。`pendingConfig` queue + mutex chain replay で対応 (実用上は mutex serialize で到達不能だが、防御として実装) +5. **stop() が state を misconfigured のまま残す**: 公開 stop API が state を `disabled` にリセットしない問題 +6. **configsEquivalent の比較が不安定**: `JSON.stringify` の key 順依存で偽 bounce が起きる問題。stable stringify に +7. **listenPort が env 依存**: 実 listen port と表示 port が乖離する可能性。`CoreServerOptions.listenPort` で配線 +8. **`api_key` 入力時に `${VAR}` env 参照が黙って literal 保存される**: amber warning text を form に表示 + +これらは round-1 / round-2 の adversarial review で発見・修正。テストでは隠蔽されやすいバグ (例: 1 番は `beforeEach` で fresh Registry を使うと検出できない) を含むため、shared-registry pattern の test を追加で書いて検証しています。 + +--- + +## どう使うか (運用ガイド) + +### パターン A: 単一 AAO で gateway を有効化 (Phase 3c 以降の推奨) + +``` +1. scripts/server.sh start で AAO を通常起動 +2. UI に admin としてログイン +3. Settings → Tools → "Gateway Server" を開く +4. "Enable Gateway" にチェック → Backends list を入力 → Save +5. Settings → Tools → "Gateway Keys" で API キーを発行 (raw key は発行直後のみ表示) +6. 他の AAO や OpenAI クライアントから: + base_url: http://this-aao:9876/v1 + api_key: sk-aao-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` + +### パターン B: Gateway 専用サーバとしてデプロイ (advanced) + +GPU サーバが多い、または worker UI を運用したくない場合: + +```bash +AAO_MODE=gateway scripts/gateway.sh start +# AAO_CONFIG=/etc/aao/gateway.yaml で別 config を指定可能 +# worker mode と同じ DB を共有可能 (キー管理が両モードで一貫) +``` + +### パターン C: 既存 LiteLLM Proxy からの乗り換え + +互換性のポイント: + +- Endpoint path: `/v1/chat/completions` ・ `/v1/models` ・ `/health` 同じ +- Response header `x-litellm-model-id` をそのまま発行 (vendor-neutral な `x-aao-backend-id` も同値で同時発行) +- Key format: `sk-aao-*` (LiteLLM の `sk-*` から prefix を変更、長さ・エントロピー同等) +- `/health` の JSON shape: `{healthy_endpoints, unhealthy_endpoints, healthy_count, unhealthy_count}` で LiteLLM 互換 + +クライアント側の `parseLiteLLMHealth` 等の既存コードは無修正で動作します。 + +--- + +## 監視 + +`/metrics` で Prometheus 形式メトリクスを取得: + +``` +# Gateway 系 +aao_gateway_requests_total{backend="llm-a",team="ops",status="ok"} 142 +aao_gateway_tokens_total{backend="llm-a",team="ops",direction="out"} 95821 +aao_gateway_backend_busy_slots{backend="llm-a"} 2 + +# Worker 系 (same-process mode 時に同じ endpoint から) +aao_worker_jobs_total{piece="chat",status="succeeded"} 38 +``` + +外部 Prometheus からスクレイプする場合は config で `provider.metrics.bearer_token` を設定し、IP allowlist を緩和。 + +--- + +## 意図的にやっていない機能 (実需確認まで保留) + +| 機能 | 保留理由 | +|---|---| +| Sticky routing (cache hit 最適化) | 1 backend 構成 + cache hit 率 実測無しでは ROI 不明 | +| Canary routing (model rollout) | 現状 model A→B 切り替え予定なし | +| Token → USD コスト換算 | tokens 数で十分か admin の要望次第 | +| Audit log テーブル | compliance 要件無し時点では不要 | +| Pre-reserve budget (並列 burst) | 実際に N×max_tokens overshoot が観測されたら検討 | +| OpenTelemetry trace | 単一 org deploy では ROI 低い | +| Slack / Email アラート | Prometheus Alertmanager で代用想定 | +| Multi-AAO federation | Prometheus federation で代用想定 | + + +--- + +## 次のステップ + + +- Backend ルーティングの動作 (least-busy の精度) +- SSE drain の挙動 (大量同時接続 + graceful shutdown) +- Budget / RPM の境界値 (オフバイワン無いか) +- Prometheus metrics の cardinality / scrape duration +- UI hot reload の race condition +- LiteLLM 互換性 (既存 client コードが無修正で動くか) + +を検証。dogfooding で観測した問題から Phase 4 のスコープを決めます。 + +--- + +## 関連ドキュメント + +- In-product help: `ui/src/content/help/11-llm-gateway.md` +- INVESTIGATE backlog (open follow-up issues): Gitea issue #338 + +--- + +## マージ済み PR 一覧 + +| Phase | PR | merge commit | 日付 | +|---|---|---|---| +| 1 | #326 | `178baa9` | 2026-05-18 | +| 2a | #330 | `78a796d` | 2026-05-18 | +| 2b | #332 | `b0569c7` | 2026-05-19 | +| 3a | #333 | `30e9d78` | 2026-05-19 | +| 3b | #335 | `9efc60f` | 2026-05-19 | +| cleanup | #337 | `bcfdd41` | 2026-05-20 | +| ops | #340 | `13bd3cd` | 2026-05-20 | +| **3c** | **#341** | **`561ff24`** | **2026-05-20 (本セッション)** | + +累計コード追加: 約 12,500 行 (テスト含む)、テスト件数 2720 (ベースライン 2707 から +13)。 diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..69b2916 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,85 @@ +# Architecture Overview + +MAESTRO は、ユーザーが投げたタスクを LLM 駆動のワークフロー(Piece)で実行する +エージェントオーケストレーターである。コントリビュータ向けのコードマップは +[../AGENTS.md](../AGENTS.md) も参照。 + +## 実行フロー + +``` +UI (POST /api/local/tasks) + → bridge/server.ts (Express API) + → Repository (SQLite: jobs テーブルに enqueue) + → Worker.poll() が queued ジョブを取得 + → piece-classifier.ts: LLM がタスクを分類し Piece を選択 + → piece-runner.ts: pieces/*.yaml を読み、movements を順に実行 + → agent-loop.ts: 1 movement の ReAct ループ (LLM ↔ tool calls) + ├─ 中間遷移: transition ツール + └─ 終了: complete ツール (success / aborted / needs_user_input) + → ジョブ完了: DB 更新 + 進捗コメント。成果物は workspace/output/ +``` + +1. **API 受付** — `bridge/server.ts` がタスクを受け、`Repository` 経由で `jobs` テーブルに `queued` で登録する。 +2. **ワーカー** — `worker.ts` が DB をポーリングし、自分の `profiles`/`task_classes` に合致するジョブを取得(複数ワーカーが並走)。 +3. **分類** — `piece-classifier.ts` がタスク本文と全 Piece の description を LLM に渡し、最適な Piece を選ぶ。 +4. **Piece 実行** — `piece-runner.ts` が Piece の movements を順に回す。verify movement のフィードバックは次の execute に引き継がれ、`transition.lessons` で movement 間の教訓が蓄積される。 +5. **ReAct ループ** — `agent-loop.ts` が 1 movement 内で LLM とツールを往復させる。`ContextManager` が LLM の `usage` からトークン使用量を追跡し、閾値(70/85/95%)で warn / prompt / force_transition を発火する。 + +## Piece と Movement + +- **Piece** = `pieces/*.yaml`。`movements` 配列で構成。 +- 各 **Movement** は `allowed_tools`(LLM に提示するツール)、`edit`(Write/Edit 可否)、`rules`(遷移条件)を持つ。`allowed_tools` 外のツールは LLM から見えない。 +- **遷移**: 中間ホップは `transition`(`rules[].next` に列挙した宛先のみ選択可)、終了は `complete`。`complete.result` がユーザーに見える唯一の最終出力。 +- **`default_next`** はエンジン内部の sentinel(コンテキスト溢れ時の強制遷移、ASK 上限時のフォールバック)。 +- **Progressive pressure**: 同一 movement への連続訪問が増えると警告を注入し、閾値超過で ABORT。 + +## ツールランタイム + +ツールは `src/engine/tools/*.ts` のモジュール群。`tools/index.ts` が動的にロードし dispatch する。各ツールは 1 行の description(毎 LLM 呼び出しに乗るため簡潔に)を持ち、詳細手順は `docs/tools/.md`(`ReadToolDoc` で取得)に置く。主なモジュールは [../AGENTS.md](../AGENTS.md#tool-modules) の一覧を参照。 + +Read 系ツールは並列実行される。Write/Edit は movement の `edit: true` のときのみ提示され、書き込みは主に `workspace/output/` に限られる。 + +## Bash サンドボックス + +エージェントの Bash 実行は、利用可能なら **bwrap サンドボックス**で隔離する: + +- **ファイルシステム**: タスクの workspace のみ rw bind、`/usr` 等は ro、他タスクの workspace やホスト `/home` は不可視。 +- **環境変数**: `--clearenv` + 最小 allowlist のみ注入(シークレット env はサンドボックス内から見えない)。 +- **ネットワーク**: `--unshare-net` で遮断(外向き通信は SSRF ガード付きの WebFetch/MCP に集約)。 +- **各 Bash コールは独立**したサンドボックス(揮発 `/tmp`・毎回新名前空間)。永続するのは workspace のみ。 + +`safety.bash_sandbox` でモードを選ぶ(`auto`/`always`/`off`)。bwrap 不在時は **hardened フォールバック**(コマンド許可リスト + パススコープ検査 + env スクラブ付き exec)になる。実行時 `pip`/`npm install` は全モードで拒否され、Python パッケージは `runtime/python-requirements.txt` からプリベイクされる。詳細は [operations/bash-sandbox-provisioning.md](operations/bash-sandbox-provisioning.md)。 + +## ワークスペース構造(ジョブ実行時) + +``` +{worktree_dir}/local/{taskId}/ + input/ アップロード・DownloadFile の保存先 + output/ 成果物(Write/Edit が許可される主な場所) + logs/ activity.log / 各種履歴 + subtasks/ SpawnSubTask の結果 + skills/ ReadSkill で materialize されたスキルファイル +``` + +## データベース + +SQLite(better-sqlite3)。`db/schema.sql` が初期スキーマ。追加カラムは `db/migrate.ts` で +`PRAGMA table_info` → 存在チェック → `ALTER TABLE ADD COLUMN` のパターンで冪等に適用する +(バージョン管理テーブルは使わない)。主なテーブル: `jobs` / `local_tasks` / +`local_task_comments` / `audit_log` ほか。 + +## ジョブのライフサイクル + +`queued` → `dispatching` → `running` → `succeeded` / `failed` / `waiting_human`(ASK 回答待ち)/ `waiting_subtasks`(並列サブタスク待ち)。失敗時は `retry` で再 `queued`(最大 `retry.max_attempts` 回)。 + +## オプションのサブシステム + +- **LLM Gateway**(`src/gateway/`) — MAESTRO 自身を OpenAI 互換 LLM プロキシとして公開(仮想キー・予算・Prometheus メトリクス)。複数 GPU/チーム共有向け。env/接続種別が `AAO_*`/`aao_gateway` の歴史的接頭辞を使う。 +- **MCP** — Model Context Protocol サーバー連携(`MCP_ENCRYPTION_KEY` 必須)。 +- **Reflection** — ジョブ完了ごとにユーザーメモリを LLM が自動更新(既定 OFF、revert 可)。 +- **認証** — Passport による Google/Gitea OAuth(任意)。`private`/`org`/`public` の可視性モデル。 +- **スケジューラ** — cron 式の定期タスク。 + +## フロントエンド + +React + Vite + TailwindCSS + @tanstack/react-query。`ui/src/App.tsx` がルート。2 カラム(list + detail)レイアウトで、タスク一覧・スケジュール・設定・スキル/Piece 管理を扱う。 diff --git a/docs/bench.md b/docs/bench.md new file mode 100644 index 0000000..07338ec --- /dev/null +++ b/docs/bench.md @@ -0,0 +1,212 @@ +# ベンチマーク (`npm run bench`) + +エージェントの **ツールコール能力 / 命令追従性 / 頭の良さ / チェックリスト使用 / 効率** を、1 つの統合タスクから多軸で計測するためのフレームワーク。 + +モデル変更・piece 改修・ツール追加などの前後で同じタスクを走らせ、品質回帰を検出する用途を想定している。 + +--- + +## 1. 前提 + +| 項目 | 必須 | +|------|------| +| `scripts/server.sh start`(またはそれ相当)でオーケストレータが起動していること | ✅ | +| `config.yaml` の `provider` が動作する LLM を指している(タスク実行 + judge の両方で使う) | ✅ | +| 初回のみ `npm run bench:fixtures` で `bench/fixtures/sales.xlsx` を生成 | ✅ | + +ベンチランナーは外部ネットに依存しない。`fixtures/web/*` はランナー内蔵の **localhost HTTP サーバ** が配信する(起動時にランダムポートを取り、`{WEB_PORT}` トークンで prompt に注入)。 + +--- + +## 2. 使い方 + +```bash +# 全タスク +npm run bench + +# 単一タスク +npm run bench -- --task=composite-mini-report + +# 別ホスト/ポートのオーケストレータ向け +npm run bench -- --server=http://127.0.0.1:9876 + +# LLM judge を skip (axis D を 1.0 固定にして programmatic だけで採点) +BENCH_JUDGE=off npm run bench + +# judge を別エンドポイント・別モデルにする +BENCH_JUDGE_ENDPOINT=https://api.example.com/v1 \ +BENCH_JUDGE_MODEL=gpt-oss:20b \ +npm run bench +``` + +実行が終わると `bench/results//` に書き出される(`run_id` は ISO タイムスタンプ)。 + +--- + +## 3. 出力の見方 + +``` +bench/results/2026-05-01T03-22-11Z/ + summary.md # ← ここを最初に見る + composite-mini-report/ + result.json # 完全な採点 + raw データ + workspace/ + logs/activity.log # エージェントが書いたログ + output/report.md # エージェントの成果物 +``` + +`summary.md` の冒頭: + +``` +# Bench run @ 2026-05-01T03:22:11.000Z + +**Overall: 73 / 100** + +| Task | Status | Total | A | B | C | D | +| ---------------------- | ---------- | ----: | --: | --: | --: | --: | +| composite-mini-report | succeeded | 73 | 90% | 100%| 70% | 60% | +``` + +各タスクの詳細セクションでは axis ごとに ✓/✗ 内訳とツールコールの全シーケンスが折りたたみで見られる。 + +`result.json` は CI から機械可読に扱える形式。 + +--- + +## 4. 採点軸(重み 100 点満点) + +| 軸 | 重み | 何を見るか | 判定 | +|----|----:|-----------|------| +| **A. Tools** | 30 | `must_use_tools` を呼んだか / `forbidden_tools` を避けたか / `forbidden_tool_for_ext` (例: Read 禁止 .xlsx) | プログラム | +| **B. Checklist** | 15 | `CreateChecklist` / `CheckItem×N` / `GetChecklist` の使用 | プログラム | +| **C. Instructions** | 30 | 出力ファイル名・1 行目固定・セクション順・行数・文字数・禁止パターンなど | プログラム | +| **D. Reasoning** | 25 | 内容の妥当性・統合の質・「次アクション」の具体性など | LLM judge ルーブリック | +| _補助: Efficiency_ | – | duration / prompt tokens(summary に数値表示のみ) | – | + +`Total = A×30 + B×15 + C×30 + D×25` を 0..100 に正規化。 + +`completion_status: [succeeded, waiting_human, failed, aborted, cancelled]` で受理する終了状態を指定。デフォルトは `[succeeded]` のみ。**failure でも grader は走り部分スコアが出る**。 + +--- + +## 5. 既存タスク + +### `composite-mini-report` + +3 ソース(Excel / Web / Markdown)を統合してミニレポートを書かせるタスク。1 本で全軸が動く。 + +- 必須ツール: `ReadExcel` / `WebFetch` / `Read` / `Write` / `CreateChecklist` / `CheckItem` (≥3 回) / `GetChecklist` +- 禁止: `.xlsx` を `Read` で開く(バイナリ混入防止 — issue #189 と同じ罠) +- 出力 `output/report.md` に厳格な形式制約(1 行目固定、セクション順、各セクション 5 行以内、「次アクション」3 件 40 字以内、画像・HTML 禁止) +- judge ルーブリック: `factual_grounding` / `actions_quality` / `synthesis` + +`bench/tasks/composite-mini-report.yaml` を参考実装としてそのまま使える。 + +--- + +## 6. タスクを追加する + +`bench/tasks/*.yaml` を作るだけで自動的に拾われる。スキーマは `src/bench/types.ts` の `BenchTask` を参照。最小例: + +```yaml +id: my-task +title: 短いタスク説明 +piece_hint: chat # piece 名 (省略時は chat) +timeout_minutes: 5 + +fixtures: # 任意 + - source: fixtures/data.txt # bench/ ルート相対 + dest: input/data.txt # input/ に置けば attachments としてアップロード + - source: fixtures/web/page.html + dest: web/page.html # web/ に置けば fixture HTTP server が配信 + +prompt_tokens: # 任意。prompt 内の {KEY} を実行時に置換 + CUSTOM_KEY: foo +prompt: | + http://127.0.0.1:{WEB_PORT}/page.html を読み、… {CUSTOM_KEY} … + +expected: + must_use_tools: [WebFetch, Write] + forbidden_tools: [Bash] + forbidden_tool_for_ext: + Read: ['.xlsx'] + must_produce_files: [output/answer.md] + completion_status: [succeeded] + +checklist: # 任意。指定すると軸 B が有効化される + required_tools: [CreateChecklist, CheckItem, GetChecklist] + min_check_item_calls: 3 + +grading: + programmatic: + constraints: + - { type: file_first_line_equals, file: output/answer.md, line: '# Title' } + - { type: file_must_contain_in_order, file: output/answer.md, sections: ['## A', '## B'] } + - { type: file_section_max_lines, file: output/answer.md, section: A, max: 5 } + - { type: file_line_starts_with, file: output/answer.md, prefix: '-', min_lines: 3, section: B } + - { type: file_line_max_chars, file: output/answer.md, max: 40, section: B } + - { type: file_no_pattern, file: output/answer.md, pattern: '!\[' } + + llm_judge: # 任意。指定しないと軸 D は 1.0 固定 + rubrics: + - name: relevance + prompt: 出力が prompt の意図と整合しているか + max_score: 10 +``` + +### プログラム制約の種類 + +| `type` | 意味 | +|--------|------| +| `file_first_line_equals` | ファイル 1 行目が完全一致するか | +| `file_must_contain_in_order` | 指定文字列が指定の順序で出現するか | +| `file_line_starts_with` | (任意セクション内で) 指定 prefix で始まる行が `min_lines` 以上あるか | +| `file_line_max_chars` | (任意セクション内で) 各行の文字数が `max` 以下か | +| `file_section_max_lines` | 指定セクションの非空行が `max` 以下か | +| `file_no_pattern` | 正規表現 (multiline) にマッチしないか | + +`section` は `## ヘッダ` の `ヘッダ` 部分(`##` は付けない)。指定無しならファイル全体が対象。 + +--- + +## 7. 内部構造 + +``` +bench/ + fixtures/ + tasks/ + results/ # gitignored +src/bench/ + types.ts # BenchTask / BenchResult / 制約スキーマ + fixture-server.ts # localhost HTTP fixture server + runner.ts # /api/local/tasks に投入 + ポーリング + ログ収集 + grader.ts # 軸 A/B/C のプログラム採点 + judge.ts # 軸 D の LLM judge 呼び出し + JSON parse + summary.ts # bench/results//summary.md 書き出し + grader.test.ts +scripts/ + bench-run.ts # CLI エントリ (`npm run bench`) + build-bench-fixtures.ts # sales.xlsx 生成 (`npm run bench:fixtures`) +``` + +ベンチランナーは既存の `/api/local/tasks` API を使うだけで、orchestrator 内部とは疎結合になっている。新しい piece やツールを追加してもベンチ側は基本変更不要。 + +--- + +## 8. トラブルシューティング + +| 症状 | 原因 / 対処 | +|------|-------------| +| `runner failed for ...: fetch failed` | `scripts/server.sh start` が立っていない、または `--server=` で指定したポートが違う | +| 全タスクで axis D が 0.0 | judge LLM のレスポンスが JSON parse 失敗。`bench/results//summary.md` の reasoning details を確認。OS の事情で短い応答しか返ってこないモデルなら `BENCH_JUDGE_MODEL` を別モデルに切り替える | +| タイムアウトで status が固まる | タスク YAML の `timeout_minutes` を伸ばす。プロバイダ側 `provider.timeoutMinutes` も併せて確認 | +| 同じタスクで毎回スコアが揺れる | LLM judge が確率的なため。programmatic 軸だけ見る・複数回平均を取る運用が無難 | +| `bench/results/` がコミットに乗ってしまった | `.gitignore` 済みだが、過去に追跡されていた場合は `git rm -r --cached bench/results` | + +--- + +## 9. 参考: 関連 issue / 機能 + +- #156 — このベンチマーク自体 +- #189 — `Read` で xlsx を開かない仕様(composite-mini-report の `forbidden_tool_for_ext` 罠と直結) +- #190 — preflight ログ表示の整理(activity.log を grader が読みやすいことの恩恵) diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..b82294d --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,229 @@ +# Configuration Reference + +MAESTRO は単一の `config.yaml`(`config.yaml.example` をコピーして作成)で設定する。 + +- **YAML キーは snake_case**(`max_concurrency`)、コード内は camelCase。`src/config.ts` の `transformKeys` が変換する。 +- 一部は**環境変数で上書き**できる([末尾参照](#environment-variable-overrides))。 +- `config_version: 2` が現行スキーマ。 + +> 値の一次ソースは `config.yaml.example`(コメント付き)と `src/config.ts`。本リファレンスは各項目の意味をまとめたもの。 + +--- + +## `llm` — ジョブ実行時の LLM 接続 + +| キー | 既定 | 意味 | +|------|------|------| +| `timeout_minutes` | 10 | 1 リクエスト全体の上限(分)。 | +| `retry.max_attempts` | 3 | 429/5xx/一時接続失敗時の再試行回数。 | +| `retry.backoff_ms` | [2000,5000,15000] | 各再試行の待機(ms)。 | +| `retry.retryable_status` | [429,500,502,503,504] | 再試行対象の HTTP ステータス。 | + +### `llm.workers[]` — ジョブ実行に使う接続先(必須) + +| キー | 意味 | +|------|------| +| `id` | ワーカー識別子。 | +| `connection_type` | `direct`(Ollama/vLLM 等の OpenAI 互換 backend に直結)/ `aao_gateway`(別 Gateway 経由、Gateway Key 必須)。 | +| `endpoint` | OpenAI 互換 API のベース URL(例 `http://localhost:11434/v1`)。 | +| `model` | 使用モデル名(ワーカーごとに明示。`default_model` は廃止)。 | +| `api_key` | `aao_gateway` 時の virtual key 等(任意)。 | +| `roles` | 用途フィルタ: `auto`/`fast`/`quality`/`title`/`reflection` 等。`[title]` のみ=タイトル生成専用。 | +| `max_concurrency` | このワーカーの並列度。 | +| `vlm` | `true` で画像入力対応(ReadImage は VLM ワーカーを優先)。 | +| `enabled` | 有効/無効。 | + +### `llm.metrics` — Prometheus exporter(worker 側) + +| キー | 既定 | 意味 | +|------|------|------| +| `enabled` | true | `/metrics`(bridge HTTP, 既定 `PORT=9876`)に mount。 | +| `prefix` | `aao_worker` | メトリクス名 prefix。 | +| `bearer_token` | — | 設定すると Bearer 認証必須(`env:NAME` 形式可)。 | +| `allowed_hosts` | localhost のみ | 許可元 IP。本番は bearer か allowlist を必ず設定。 | + +--- + +## `gateway` — LLM Gateway サーバー(任意) + +MAESTRO 自身を OpenAI 互換の LLM Gateway として公開する(仮想キー・予算・メトリクス付き、複数 GPU/チーム共有向け)。**env 変数や接続種別が `AAO_*` / `aao_gateway` という歴史的な接頭辞を使う点に注意(AAO = この Gateway 機能の旧称)。** 詳細は [aao-gateway-overview.md](aao-gateway-overview.md)。 + +| キー | 既定 | 意味 | +|------|------|------| +| `enabled` | false | true で同 process gateway が起動(hot reload 対応)。 | +| `listen_port` | 4000 | separate-deploy 時のみ。 | +| `request_timeout_sec` | 600 | 1 リクエスト全体(streaming 込み)。 | +| `upstream_timeout_sec` | 30 | 各 upstream の TTFB 上限。 | +| `shutdown_graceful_sec` | 30 | SIGTERM 後の SSE drain 上限。 | +| `backends[]` | — | `id`/`endpoint`/`model`/`max_slots`/`api_key`。model 厳密一致で routing。 | +| `virtual_keys[]` | — | bootstrap/backup 用(`key`/`team`/`allowed_models`/`tokens_budget`/`rate_limit_rpm`)。新規発行は admin API 推奨。 | +| `metrics` | enabled | `prefix: aao_gateway`、team/key_prefix/backend ラベル。auth 必須運用。 | + +Virtual Key の発行・rotation は admin REST API(`POST /api/admin/gateway/keys`)または UI(Settings → LLM → Gateway Server)で行う。 + +--- + +## `storage` — パス・容量 + +| キー | 既定 | 意味 | +|------|------|------| +| `worktree_dir` | ./data/workspaces | ジョブ作業ディレクトリのベース。 | +| `custom_pieces_dir` | — | リポジトリ内 `pieces/` に加えて読む Piece dir(任意)。 | +| `user_folder_root` | ./data/users | `{root}/{userId}/` に AGENTS.md/scripts/notes 等を保存。 | +| `task_upload_max_size_mb` | 50 | タスク/コメント body 上限(base64 込み。範囲 1–1000)。 | +| `trash_retention_days` | 30 | `trash/` の自動 sweep。0 で都度全削除。 | + +--- + +## Execution — 並列度・上限・再試行 + +| キー | 既定 | 意味 | +|------|------|------| +| `concurrency` | 4 | 全ワーカー合算の最大並列ジョブ数(env `CONCURRENCY`)。 | +| `max_movements` | 200 | 1 ジョブ内の最大 movement 数(loop 防止)。 | +| `retry.max_attempts` | 3 | ジョブ失敗時の最大再試行。 | +| `retry.backoff_seconds` | [60,300,900] | 各 attempt 間の待機秒。 | +| `ask.max_per_job` | 2 | 1 ジョブの ASK(ユーザー質問)上限。 | +| `subtasks.max_depth` | 2 | SpawnSubTask のネスト最大深度。 | +| `subtasks.max_per_parent` | 10 | 1 ジョブが生成できるサブタスク最大数。 | + +--- + +## `context` — LLM コンテキスト管理 + +| キー | 既定 | 意味 | +|------|------|------| +| `limit_tokens` | 自動取得→128000 | コンテキスト上限。省略時はプロバイダ API から自動取得。 | +| `thresholds[]` | 0.7=warn / 0.85=prompt / 0.95=force_transition | 使用率閾値ごとの動作。 | + +--- + +## `safety` — 暴走防止・Bash サンドボックス + +| キー | 既定 | 意味 | +|------|------|------| +| `max_iterations` | 200 | 1 movement 内の最大イテレーション。 | +| `max_revisits` | 3 | 同一 movement の最大再訪問。超過で ABORT。 | +| `prompt_guard_ratio` | 0.8 | プロンプトがコンテキスト上限の何%まで許容するか(0.5–0.95)。 | +| `history_summarization.enabled` | true | 古い turn を構造化要約に置換して粘る。 | +| `history_summarization.tail_turns` | 2 | 末尾何 turn を保護するか。 | +| `history_summarization.preserve_recent_budget` | 8000 | 末尾保護の最大トークン。 | +| `bash_unrestricted` | false | true で Bash のコマンド許可リストを撤廃(**サンドボックス機構は別途 `bash_sandbox` が制御**)。 | +| `bash_sandbox` | auto | Bash 隔離機構: `auto`(bwrap あれば使用、無ければ hardened-whitelist)/ `always`(bwrap 強制・不在なら起動失敗、本番推奨)/ `off`(bwrap 不使用、env スクラブは維持)。詳細 [operations/bash-sandbox-provisioning.md](operations/bash-sandbox-provisioning.md)。 | + +--- + +## `search_filter` — WebSearch の機密情報漏洩防止 + +| キー | 既定 | 意味 | +|------|------|------| +| `blocked_patterns[]` | — | 完全一致で検索クエリから除去するパターン。 | +| `auto_block.private_ip` | true | 10/172.16-31/192.168/127.* を自動ブロック。 | +| `auto_block.internal_domain` | true | `.local`/`.internal`/`.lan`/`.intranet`/`.corp`/`.home`。 | +| `auto_block.email` / `phone` | true | メール/電話番号。 | + +--- + +## `browser` — BrowseWeb ランタイム + +| キー | 既定 | 意味 | +|------|------|------| +| `page_timeout` | 60000 | ページ遷移 timeout(ms)。 | +| `action_timeout` | 30000 | アクション timeout(ms)。 | +| `captcha_solve` | skip | `skip` / `novnc`(人手 CAPTCHA 解決)。 | +| `max_captcha_pages` | 5 | CAPTCHA ページ上限。 | +| `channel` | chromium | `chromium`/`chrome`/`msedge`。 | +| `executable_path` | — | ブラウザ実行ファイル(channel と排他)。 | + +--- + +## `tools` — ツール設定 + +UI 上は Web & Search / Browser / Media & Documents / External Services / Legacy Knowledge に分かれるが YAML は `tools` 1 ブロック。主な項目: + +**Web & Search**: `searxng_url`(WebSearch フォールバック先), `webfetch_timeout`(sec), `websearch_timeout`, `webfetch_allowed_hosts[]`(SSRF 例外: private IP/.local を許可する場合)。 + +**Media & Documents**: `vision_model`/`vision_base_url`/`vision_timeout`/`vision_max_tokens`(ReadImage 用 VLM), `ocr_model`, `office_{excel,docx,pdf}_max_size_mb`(既定 10), `office_pptx_max_size_mb`(50), `office_pptx_max_uncompressed_mb`(200, zip-bomb 検知), `speech_server_url`/`speech_timeout`/`speech_language`。 + +**External Services**: `x_*`(Twitter/X CLI 連携: `x_cli_command`/`x_auth_token`/`x_ct0`/`x_proxy`/`x_download_*` 等), `google_maps_api_key`/`maps_timeout`(未設定で Nominatim/OSRM), `amazon_affiliate_tag`/`keepa_api_key`。 + +**User scripts**: `user_scripts_enabled`(RunUserScript。plain runtime は Node `--permission` で sandbox 化), `user_scripts_allow_userids[]`。 + +**Legacy Knowledge**: `knowledge_service_url`(未設定で knowledge ツール無効), `knowledge_namespaces`(namespace ごとの api_key)。新規 namespace は MCP 経由を推奨。 + +--- + +## `notes` — Shared Knowledge Notes 注入 + +`data/users/{userId}/notes/` のノートをシステムプロンプトに注入。`inject.per_note_max_kb`(日本語は 4 推奨), `inject.total_max_kb`, `inject.over_budget_strategy`(`truncate_last`/`skip_remaining`/`degrade_to_search`)。 + +--- + +## `auth` — 認証(任意) + +未設定なら認証なしで動作。 + +| キー | 意味 | +|------|------| +| `session_secret` | セッション署名鍵(ランダム文字列)。 | +| `session_max_age` | セッション有効期間(ms, 既定 86400000=24h)。 | +| `secure_cookie` | HTTPS 環境では true。 | +| `admin_emails[]` | admin ロールにするメール。 | +| `primary_provider` | `google` / `gitea`(両方有効時に明示)。 | +| `providers.google` | `client_id`/`client_secret`/`callback_url`。 | +| `providers.gitea` | `client_id`/`client_secret`/`base_url`/`callback_url`。ログイン時に Gitea org を取得し可視性に利用。 | + +--- + +## `branding`(任意) + +`app_name`/`primary_color`/`login_page_title`/`logo_url`/`favicon_url`/`footer_text`。Settings → System → Branding(admin)で GUI 編集可。`config.yaml`・`data/branding/` は gitignore 済み。 + +--- + +## `secrets` + +`master_key_path`(既定 `./data/secrets/master.key`, 32 byte, 初回起動で自動生成・mode 0600)。SSH 鍵・MCP トークン等の暗号化に使う。 + +--- + +## `reflection` — 学習(既定 OFF) + +ON で毎ジョブ完了後にユーザーメモリを LLM が自動更新(snapshot は revert 可)。`enabled`, `max_memory_changes_per_job`(3), `piece_edit_cooldown_hours`(24), `snapshot_retention_days`(90), `per_user_daily_budget_tokens`(200000)。 + +--- + +## `mcp` — Model Context Protocol + +サーバーは admin UI(global)/各ユーザー(self-hosted)で管理。**`MCP_ENCRYPTION_KEY` env(64 hex)が必須。** `call_timeout_seconds`(60), `max_binary_size_mb`(20), `max_output_files_per_job`(10), `max_output_size_mb_per_job`(200), `tool_cache_ttl_seconds`(600), `oauth_pending_ttl_minutes`(10), `allow_private_addresses`(private 網の MCP server 用、既定 false)。 + +--- + +## `ssh` — SSH ツール(既定 OFF) + +`enabled`, `allow_private_addresses`(global 既定、admin は per-connection grant 可), `call_timeout_seconds`(30), `max_output_bytes`(32768), `max_{upload,download}_size_mb`(100), `audit_retention_days`(90), `admin_bypasses_grants`(true), abuse 検知(`abuse_window_minutes`/`abuse_failure_threshold`/`abuse_lock_minutes`)。 + +**Interactive Console**(`ssh.console`): `enabled`, `idle_timeout_seconds`(1800), `max_session_duration_seconds`(14400), `scrollback_bytes`(524288), `max_sessions_per_connection`(3) ほか。手順は [ssh.md](ssh.md)。 + +--- + +## `notifications.push` — Web Push(V2, 任意) + +HTTPS ホスティング必須(iOS は PWA インストール)。`enabled`, `vapid_subject`(RFC 8292), `vapid_current_path`(自動生成・mode 0600), `vapid_history_dir`, `payload_max_bytes`(3072, 上限 4096), `queue_concurrency`(8), `per_send_timeout_ms`(10000)。鍵ローテーション: `npm run vapid-rotate`。 + +--- + +## Environment variable overrides + +一部設定は環境変数で上書きできる: + +| 環境変数 | 上書き対象 | +|----------|------------| +| `OLLAMA_BASE_URL` | LLM エンドポイント | +| `OLLAMA_MODEL` | モデル名 | +| `WORKTREE_DIR` | `storage.worktree_dir` | +| `CONCURRENCY` | `concurrency` | +| `DB_PATH` | SQLite DB パス | +| `PORT` | bridge HTTP ポート(既定 9876) | +| `LOG_LEVEL` | `debug`/`info`/`warn`/`error`(既定 info) | +| `MCP_ENCRYPTION_KEY` | MCP/SSH 秘密の暗号化鍵(MCP 利用時必須) | diff --git a/docs/context-flow.md b/docs/context-flow.md new file mode 100644 index 0000000..b94b3ad --- /dev/null +++ b/docs/context-flow.md @@ -0,0 +1,235 @@ +# コンテキスト構築と溢れ時動作 + +この資料は、新規メッセージ送信時に何が LLM へ渡るか、movement 間で何が引き継がれるか、コンテキストが逼迫した時にどう動くかをまとめたものです。 + +## 全体像 + +```mermaid +flowchart TD + U[UI / API からタスク・コメント送信] --> DB[(SQLite jobs / comments)] + DB --> W[Worker が job を取得] + W --> C[会話コンテキストを組み立て] + C --> P[piece-runner が Piece を実行] + P --> M[Movement 開始] + M --> A[agent-loop: LLM に送信] + A --> T{LLM 応答} + T -->|tool_call| X[許可されたツールを実行] + X --> H[ツール結果を movement 内履歴へ追加] + H --> A + T -->|transition| N{next_step} + N -->|次 movement| P + N -->|COMPLETE| S[Job succeeded] + N -->|ASK| Q[waiting_human] + N -->|ABORT| F[failed / aborted] + N -->|WAIT_SUBTASKS| ST[waiting_subtasks] +``` + +## 新規メッセージで送られる内容 + +ローカルタスクでは、Worker が `job.instruction` をそのまま送るのではなく、現在時刻、直近コメント、workspace 状況、タスク本文をまとめた `enrichedInstruction` を作ります。 + +```mermaid +flowchart TD + J[job.instruction] --> E[enrichedInstruction] + Time[現在日時ブロック] --> E + Comments[直近コメント最大5件] --> Trunc[各コメント最大500文字に切り詰め] + Trunc --> E + Files[input/ と output/ のファイル一覧] --> E + E --> Piece[runPiece] +``` + +含まれるもの: + +| 種別 | 内容 | +|------|------| +| 現在時刻 | `buildTimeContextBlock()` の結果 | +| これまでのやり取り | Local task comments の末尾 5 件 | +| コメント本文 | 1 コメントあたり最大 500 文字 | +| workspace 状況 | `input/` と `output/` のファイル一覧 | +| タスク本文 | 現在の `job.instruction` | + +含まれないもの: + +- すべての過去コメント全文 +- 過去 movement の LLM 会話履歴全文 +- `logs/` 配下のログ全文 +- `input/` / `output/` のファイル本文そのもの + +ファイル本文が必要な場合は、movement 内で `Read` などのツールを使って取得します。 + +## Movement 内で送られる内容 + +各 movement は、system prompt と user prompt から開始します。movement の中では、LLM がツールを呼ぶたびに assistant の tool call と tool result が同じ movement の `messages` に追加され、次の LLM 呼び出しへ再送されます。 + +```mermaid +sequenceDiagram + participant R as piece-runner + participant L as agent-loop + participant M as LLM + participant T as Tool + + R->>L: movement + enrichedInstruction + L->>M: system prompt + user prompt + M-->>L: tool_call + L->>T: executeTool + T-->>L: tool result + L->>M: これまでの messages + tool result + M-->>L: transition(next_step, summary, lessons) + L-->>R: MovementResult +``` + +movement 内で保持されるもの: + +- system prompt +- user prompt +- LLM の tool call +- ツール結果 +- `ReadImage` などが返した画像コンテキスト +- transition までのリマインド文 + +movement が終わると、この `messages` 全体は次 movement にそのまま渡されません。 + +## Movement 間で引き継がれる内容 + +movement 間では、会話履歴全文ではなく、限定された要約情報だけが引き継がれます。 + +```mermaid +flowchart LR + M1[Movement A] --> TR[transition] + TR --> Lessons[lessons / summary] + Lessons --> Log[logs/lessons.jsonl] + Lessons --> Inject[次 movement の prompt に注入] + Inject --> M2[Movement B] +``` + +主な引き継ぎ: + +| 引き継ぎ元 | 次 movement への渡り方 | +|------------|------------------------| +| transition の `lessons` | `## 前のステップで得た教訓` として注入 | +| `lessons` 未指定で COMPLETE | `summary` / output を lesson として扱う | +| verify 系の指摘 | 対象 movement へフィードバックとして追記 | +| チェックリスト | `logs/checklists/*.json` から現在状態を注入 | +| workspace の変更状況 | 一部の verify 後に git status / diff 抜粋を付加 | + +Lessons は最大 2000 文字程度に抑えられ、古いものから削られます。 + +## コンテキスト逼迫時の動作 + +`ContextManager` は LLM の usage が取れる場合は `prompt_tokens`、取れない場合は文字数推定で使用率を見ます。 + +デフォルト閾値: + +| 使用率 | action | 動作 | +|--------|--------|------| +| 70% | `warn` | progress に警告を記録 | +| 85% | `prompt` | 「作業をまとめて transition してください」という user message を追加 | +| 95% | `force_transition` | 現 movement を強制的に `defaultNext` へ進める。`defaultNext` がなければ `ABORT` | +| 99% | exhausted 判定 | 内部判定用。直接の圧縮処理ではない | + +```mermaid +flowchart TD + LLM[LLM 応答完了] --> Usage{usage あり?} + Usage -->|あり| Tokens[prompt_tokens を記録] + Usage -->|なし・3 iteration 以降| Chars[messages 文字数から推定] + Tokens --> Ratio[context 使用率を計算] + Chars --> Ratio + Ratio --> Warn{70%以上?} + Warn -->|warn 未発火| W[warn action] + Ratio --> Prompt{85%以上?} + Prompt -->|prompt 未発火| P[transition 促しを messages に追加] + Ratio --> Force{95%以上?} + Force -->|force_transition 未発火| F[defaultNext へ強制遷移] +``` + +現状の重要点: + +- コンテキスト逼迫時に、会話全体を自動要約して同じ movement を継続する処理はありません。 +- `force_transition` は「完了した」と判断するのではなく、`movement.defaultNext` へ進める機械的な退避です。 +- `defaultNext` が `COMPLETE` の movement では、結果として完了扱いになる場合があります。 +- `defaultNext` がない場合は `ABORT` になります。 + +## ツール出力の切り詰め + +大きなファイルやコマンド出力をそのまま入れると movement 内の `messages` が膨らむため、一部ツールは残コンテキストに応じて出力を自動切り詰めします。 + +```mermaid +flowchart TD + Tool[Read / Bash / Office 系ツール] --> Budget[残コンテキストから tool result 予算を計算] + Budget --> Fit{予算内?} + Fit -->|Yes| Full[全文または要求範囲を返す] + Fit -->|No| Cut[先頭側を返し、自動切り詰め注記を付ける] + Cut --> Hint[続きを読む offset / byte_offset / grep 等を案内] +``` + +予算計算: + +- `ContextManager` がある場合: `getAvailableTokens()` の 50% を 1 回の tool result 予算にする +- 予算の上限: 60,000 tokens +- 予算の下限: 最低返却 tokens を確保 +- `Read` は行指向なら行境界、改行が少ないファイルは byte/char 境界で切ります +- `Bash` は `head` / `tail` / `grep` / `awk` / `sed` などで絞る案内を付けます + +## よくあるパターン + +### 1. 通常の新規タスク + +```mermaid +flowchart TD + Create[タスク作成] --> Recent[直近コメント最大5件 + workspace 状況] + Recent --> Initial[initial_movement] + Initial --> Tools[必要なファイルだけツールで読む] + Tools --> Transition[transition] + Transition --> Next[次 movement] +``` + +この場合、過去コメント全文やファイル本文は最初から全部送られません。 + +### 2. ユーザーへの ASK 後に再開 + +```mermaid +flowchart TD + Ask[transition: ASK] --> Wait[Job waiting_human] + Wait --> Reply[ユーザーが返信] + Reply --> Queue[Job queued] + Queue --> Worker[Worker が再取得] + Worker --> Context[直近コメント最大5件を再構築] + Context --> Resume[resumeMovement から再開] +``` + +ASK 再開時も、基本は直近コメントから再構築します。前回 movement 内の LLM メッセージ全文を保存して再投入する方式ではありません。 + +### 3. 長いファイルを読む + +```mermaid +flowchart TD + Read[Read large file] --> Budget{tool result 予算} + Budget --> Truncated[自動切り詰め] + Truncated --> Notice[続きの読み方を表示] + Notice --> Targeted[必要範囲だけ再 Read / Grep] +``` + +長文を読む場合は、全文を一度に入れるより、検索や範囲指定で必要箇所を絞る前提です。 + +### 4. コンテキストが 95% を超える + +```mermaid +flowchart TD + High[Context 95%以上] --> Force[force_transition] + Force --> HasDefault{defaultNext あり?} + HasDefault -->|Yes| Next[defaultNext へ移動] + HasDefault -->|No| Abort[ABORT] + Next --> Runner[piece-runner が遷移判定を続行] +``` + +これは現状の退避動作です。圧縮サマリを作って同じ movement を継続する動作は未実装です。 + +## 参照実装 + +| 内容 | ファイル | +|------|----------| +| 新規タスクの `enrichedInstruction` 構築 | `src/worker.ts` | +| Piece / movement 遷移、Lessons 注入 | `src/engine/piece-runner.ts` | +| movement 内の LLM messages と tool result 追加 | `src/engine/agent-loop.ts` | +| コンテキスト閾値管理 | `src/engine/context-manager.ts` | +| Read / Bash 等の tool result 切り詰め | `src/engine/tools/core.ts` | diff --git a/docs/design/README.md b/docs/design/README.md new file mode 100644 index 0000000..e27b10f --- /dev/null +++ b/docs/design/README.md @@ -0,0 +1,151 @@ +# Agent Orchestrator — Design System + +A local, single-tenant agent orchestration platform. Users submit natural-language tasks via a small Japanese-language admin UI; an LLM classifier routes each task into a named **Piece** (workflow), which runs a multi-step **Movement** chain against local Ollama workers and emits progress/results back to the UI as chat-style comments. + +## Product at a glance + +- **One product, one surface:** a React + Vite + TailwindCSS admin dashboard at `/ui/` on the orchestrator server (`http://this-machine:9876/ui/`). Mobile (single-column), tablet (list + chat), desktop (list + chat + detail) layouts. +- **Language:** Japanese throughout (UI labels, empty states, toasts). Latin/mono treatment reserved for identifiers, status keywords, version tags, wordmark. +- **Primary objects:** Task → Job → Movement → Tool call. Status kanban: `queued / running / waiting_human / waiting_subtasks / retry / succeeded / failed / cancelled`. +- **Interaction model:** a threaded chat with progress cards interleaved between user requests and agent results (green = result, amber = ASK, blue bubble = user, grey pill card = progress). + +## Sources consulted +- **Codebase (read-only local mount):** `gitea-agent-orchestrator/` + - `ui/tailwind.config.js`, `ui/src/index.css` — tokens, fonts + - `ui/src/lib/utils.ts` — `statusTone()`, `relativeTime()`, activity parsing + - `ui/src/components/**` — TopBar, FilterBar, TaskListItem, ChatPane, ChatMessage, DetailHeader, OverviewTab, ProgressTab, CreateTaskDialog, EmptyState, StatChip, StatusBadge, LoadingSpinner + - `ui/public/favicon.svg` — the "hub + 3 agents" mark + - `README.md` (root) — product narrative, piece list, tool registry +- **No Figma, no slide templates, no marketing site** were provided; this design system documents the existing admin UI only. + +## Index + +| File | Purpose | +|---|---| +| `README.md` | This document | +| `SKILL.md` | Agent Skill wrapper (portable to Claude Code) | +| `colors_and_type.css` | CSS variables for color/type/spacing/radii/shadow | +| `assets/logo.svg` | 32×32 brand mark (blue rounded square, orchestrator hub + 3 agent nodes) | +| `assets/wordmark.svg` | Logo + "AGENT ORCHESTRATOR" mono wordmark lockup | +| `preview/*.html` | Atomic design-system cards (registered in review) | +| `ui_kits/admin/` | High-fidelity React recreation of the admin dashboard | + +--- + +## Content fundamentals + +**Voice.** Terse, functional, Japanese. Almost no marketing flourish. Labels are nouns or imperative verbs — "新しい依頼" (new request), "Task 作成" (create Task), "詳細" (details), "送信" (send), "共有停止" (stop sharing). + +**Person.** Neither "あなた" nor "私" — the UI talks about objects, not people. Empty state uses a numbered list of actions ("左パネルからタスクを選択する" — "select a task from the left panel") rather than second-person. + +**Case & casing.** +- Japanese sentences where there's human prose. +- English identifiers kept English, capitalized as in code: **Tasks / Schedules / Settings / Users** (TopBar navs), **Inbox / Running / Waiting / Subtasks / Retry / Done / Failed / Cancelled** (status columns). These are not translated, even in a Japanese UI. +- Meta labels are SMALL-CAPS UPPERCASE with wide tracking in the mono face: `STEP`, `TOOL`, `PREVIEW`, `FINAL`, `ASK`, `LOG`. +- "Agent Orchestrator" wordmark: uppercase, mono, letter-spacing: 0.16em, blue-600. +- Product name "agent-orchestrator" in kebab-case in docs; UI header is Title Case. + +**Example strings** (verbatim from code): +- `新しい依頼` (new request) — primary CTA +- `スレッドを選択してください` (please select a thread) — empty state title +- `左の一覧から選ぶと、会話、進捗、成果物を追えます。` — empty state description +- `メッセージを入力... (Ctrl+Enter で送信)` — composer placeholder +- `まだ進行情報がありません。` (no progress info yet) +- `(activity.log がまだありません)` — empty log fallback +- `良かった` / `改善が必要` — feedback thumbs labels + +**Numbers & units.** Counts render as ` 件` (items), ` 実行中` (running), ` 待機` (waiting). Relative time in Japanese: `たった今 / N分前 / N時間前 / N日前`. Durations mix units: `ms`, `s`, `m Ns`. + +**Tone.** Operator-facing, not consumer-facing. No exclamation marks, no emoji in UI chrome. Emoji appear only as **domain signals inside agent content**: 👍 / 👎 on feedback buttons, 📋 on checklist progress cards. Unicode symbols (☑ ✗ ⊘ ☐ ▶) are used as list markers inside agent-emitted checklists. Do not introduce new emoji outside these established spots. + +--- + +## Visual foundations + +**Palette.** A near-monochromatic slate neutral scale (Tailwind `slate-50…900`) carrying the entire surface, with **#2563eb (blue-600)** as the only brand color for primary actions, active states, focus rings, and the logo. Semantic status pills add pastel bg / deep fg pairs: green (running/success), amber (waiting/retry), indigo (subtasks), red (failed), blue (succeeded/queued edge cases), slate (queued/cancelled). All defined verbatim in `statusTone()` in `ui/src/lib/utils.ts`. + +**Type.** `IBM Plex Sans JP` for everything, `IBM Plex Mono` for identifiers, log output, version tags, wordmark, cron expressions, and the micro-label uppercase treatment. Body is **13px** — small and dense. Titles jump to 18px (detail) or 20px (dialog); chat bubbles are 14px leading-relaxed. Mobile forces input `font-size: 16px !important` to prevent iOS auto-zoom. + - *Font substitution note:* IBM Plex is already loaded from Google Fonts in the codebase; no substitution required. + +**Weight.** Heavy. 700 ("bold") is the workhorse — buttons, pill labels, status chips, even 10px/11px micro labels. 800 ("extra-bold") is reserved for titles and primary CTAs. 400/500 appear in body copy only. + +**Spacing.** Tailwind 4px scale. Gutters between panels are `8px` (`p-2 gap-2`). Cards pad `16px` (`p-4`). Buttons pad `6px 10px` (small chips), `8px 16px` (primary). The desktop layout is a 3-column grid: `clamp(240px, 22vw, 280px)` list / flexible chat / `clamp(280px, 26vw, 440px)` detail (or `clamp(360px, 30vw, 560px)` wide). + +**Backgrounds.** Flat solid colors — **never gradients**. App root is `#f3f6fb` (between slate-50 and slate-100); content cards are white. Activity log switches to an inverted surface: `bg-slate-900 text-slate-100` as a "terminal" zone. No illustrations, no patterns, no photos, no blur/glassmorphism. + +**Animation.** Minimal and purposeful. Only three motion idioms: +1. `transition-colors` on hover/active states (Tailwind default ~150ms). +2. `animate-pulse` on a 2px blue dot while a job is running. +3. `animate-spin` on the loading spinner (2px slate-200 border, blue-600 top-border). +No fades, no slides, no springs, no bounces. Expand/collapse caret rotates 90° (`rotate-90`) on click. + +**Hover states.** Buttons and list items darken one step: transparent → `bg-slate-100`, `bg-blue-600` → `bg-blue-700`, border-slate-200 → border-slate-300. Text links: `hover:underline` only on small text actions. No scale, no shadow-lift. + +**Press / active states.** Active navigation uses **filled accent**: `bg-blue-600 text-white`. Active filter pills use **tinted** style: `border-blue-600 bg-blue-50 text-blue-700`. Active list item: `border-blue-500 bg-blue-50`. No shrink, no darken-further. + +**Focus.** `focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500`. Inputs on focus: `focus:border-blue-400 focus:ring-2 focus:ring-blue-100`. + +**Borders.** 1px, `#e2e8f0` (slate-200) by default. Active states promote to `slate-300` (hover) or `blue-500/600` (selected). Chat bubbles have no border on the user (blue-filled) bubble but do on ask/result bubbles to soften the pastel. + +**Shadows.** Two tiers only: +- `shadow-sm` — resting card (every panel, chip, list item). Nearly invisible, but cards in Vite have it. +- `shadow-2xl` — Dialog overlay only. +No colored shadows, no inner shadows, no glows. + +**Radii (heavy rounding).** The product reads "rounded" first, flat second. +- `rounded-lg` (8px): small selects, small buttons, rotating caret container, log surface. +- `rounded-xl` (12px): **default** — cards, buttons, inputs, list items, panels. +- `rounded-2xl` (16px): dialogs, chat bubbles (tail reduced to `rounded-br-md` / `rounded-bl-md`). +- `rounded-full` (9999px): status pills, filter tabs, avatar, pulse dot. + +**Transparency & blur.** Modal overlay uses `bg-slate-900/50` or `bg-black/40` — straight alpha, no backdrop-blur. No frosted chrome anywhere. + +**Cards.** White fill, 1px slate-200 border, `rounded-xl`, `shadow-sm`. Padded `p-3` or `p-4`. The admin list panel is itself a card; list items inside are nested mini-cards with the same recipe. + +**Chat bubbles.** +- User: `rounded-2xl rounded-br-md`, `bg-blue-600 text-white`, `shadow`. +- Agent ASK: `rounded-2xl rounded-bl-md`, `bg-amber-50 border border-amber-200`, `shadow-sm`. +- Agent RESULT: `rounded-2xl rounded-bl-md`, `bg-green-50 border border-green-200`, `shadow-sm`, renders Markdown. +- Progress card: centered, `bg-slate-50 border border-slate-200 rounded-xl`, 12px slate-500 text, click-to-expand. + +**Protection gradients vs capsules.** Never gradients. Always capsules/pills for chips and status, always rectangular cards for containers. + +**Imagery vibe.** The brand has no photography. The single branded image is the `favicon.svg`: a rounded blue square with a white central "hub" and three satellite nodes connected by thin 55%-opacity white lines — a literal orchestrator-connecting-workers glyph. Keep this as the only decorative asset unless explicitly asked. + +**Layout rules.** Fixed TopBar at top (white, slate-200 bottom border). Main content fills remaining `h-dvh` and is `overflow-hidden` at the root — panels handle their own scroll. Toasts slide in at the top-center (`mx-4 mt-2`). Dialogs center-screen, `max-width: min(860px, 92vw)`, `max-height: 88dvh`, scroll internally. + +--- + +## Iconography + +**No icon font, no icon library dependency.** Icons are inline SVG written directly in components, drawn at `w-3.5 h-3.5`, `w-4 h-4`, or `w-5 h-5`, stroke-based, `stroke-width="2"`, `strokeLinecap="round"`, `strokeLinejoin="round"`, `fill="none"`. Style is close to **Lucide / Feather** — 2px stroke, round caps, 24×24 viewBox, minimal. Example glyphs in source: magnifying-glass (search), paperclip (attach), cross (close), VNC monitor square, chevron-right caret. + +**Substitution policy.** When extending the system, prefer **Lucide** (`https://unpkg.com/lucide-static`) or hand-write a 2px-stroke, round-cap, 24×24 SVG inline. Do **not** introduce Heroicons solid, Material, or Phosphor — those break the line-weight consistency. + +**Unicode glyphs** are used functionally inside agent-generated content: +- `☑ ✗ ⊘ ☐` — checklist item states (done / failed / skipped / pending) +- `▶` — expand caret (rotates to down) +- `×` — close buttons within attachment chips +- `✕` — mobile dialog close +- `+` / `+` — add/create indicators +- `·` — meta separator (`worker: … · mode: …`) + +**Emoji.** Deliberately limited: +- 👍 / 👎 — feedback rating only +- 📋 — checklist progress card header only + +Do not introduce new emoji. When agent markdown renders emoji, the `prose` plugin styles them at 14px inline — do not restyle. + +**Logo usage.** The 32×32 favicon is the only mark. Minimum size 16×16. Clear space: `x/4` on all sides where `x` is the square's edge. Do not recolor the blue fill; if placing on blue, invert to a white square with blue contents. + +--- + +## Component notes (see `ui_kits/admin/`) + +The admin UI kit recreates: TopBar, FilterBar, TaskListItem, TaskListPanel, ChatPane, ChatMessage (user/ask/result/progress variants), DetailPanel with tab pills, StatusBadge, StatChip, EmptyState, LoadingSpinner, CreateTaskDialog, and the composite desktop 3-column layout. + +## Caveats + +- No external brand guide, marketing site, or Figma was provided — this system is derived strictly from the live admin UI source. +- No printed/decorative imagery exists in the codebase; the only "brand asset" is the favicon logo. +- Fonts load from Google Fonts CDN (same as production); no local TTFs needed. diff --git a/docs/design/colors_and_type.css b/docs/design/colors_and_type.css new file mode 100644 index 0000000..877d070 --- /dev/null +++ b/docs/design/colors_and_type.css @@ -0,0 +1,128 @@ +/* Agent Orchestrator — Colors & Type + Extracted from gitea-agent-orchestrator/ui (Tailwind config + index.css + usage). + Palette: slate neutrals + #2563eb blue accent, with pastel semantic chips. +*/ + +@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&family=IBM+Plex+Sans+JP:wght@400;500;600;700;800&display=swap'); + +:root { + /* —— Brand / Accent ———————————————————————— */ + --accent: #2563eb; /* blue-600 — primary action, active tab, focus ring */ + --accent-deep: #1d4ed8; /* blue-700 — hover on primary button */ + --accent-50: #eff6ff; + --accent-100: #dbeafe; + --accent-200: #bfdbfe; + --accent-500: #3b82f6; + --accent-700: #1d4ed8; + + /* —— Ink / Neutrals (slate scale) ————————————— */ + --ink: #0f172a; /* slate-900 — primary text */ + --muted: #64748b; /* slate-500 — secondary text */ + --slate-50: #f8fafc; /* body surface */ + --slate-100: #f1f5f9; /* scrollbar track, chip bg */ + --slate-200: #e2e8f0; /* default border */ + --slate-300: #cbd5e1; + --slate-400: #94a3b8; + --slate-500: #64748b; + --slate-600: #475569; + --slate-700: #334155; + --slate-800: #1e293b; + --slate-900: #0f172a; + --app-bg: #f3f6fb; /* root bg (index.html) */ + + /* —— Semantic status tones (from statusTone()) ———— + bg / fg pairs used in status pills and cards. */ + --status-running-bg: #dcfce7; --status-running-fg: #166534; /* green */ + --status-waiting-bg: #fef9c3; --status-waiting-fg: #854d0e; /* amber */ + --status-subtasks-bg: #e0e7ff; --status-subtasks-fg: #3730a3; /* indigo */ + --status-failed-bg: #fee2e2; --status-failed-fg: #b91c1c; /* red */ + --status-succeeded-bg: #dbeafe; --status-succeeded-fg: #1e40af; /* blue */ + --status-retry-bg: #fef3c7; --status-retry-fg: #92400e; /* amber-deep */ + --status-queued-bg: #e2e8f0; --status-queued-fg: #475569; /* slate */ + + /* Message bubbles (ChatMessage.tsx) */ + --bubble-user-bg: #2563eb; --bubble-user-fg: #ffffff; + --bubble-ask-bg: #fffbeb; --bubble-ask-border: #fde68a; /* amber-50/200 */ + --bubble-result-bg: #f0fdf4; --bubble-result-border: #bbf7d0; /* green-50/200 */ + + /* Log / terminal surface (ProgressTab) */ + --log-bg: #0f172a; + --log-fg: #f1f5f9; + + /* —— Typography ——————————————————————————— */ + --font-sans: 'IBM Plex Sans JP', 'Hiragino Sans', -apple-system, BlinkMacSystemFont, sans-serif; + --font-mono: 'IBM Plex Mono', ui-monospace, Menlo, monospace; + + /* UI base is small & dense — 13px body, mobile auto-zooms to 16. */ + --fs-10: 10px; /* micro labels, version tag */ + --fs-11: 11px; /* meta, pill labels, timestamps */ + --fs-12: 12px; /* secondary body, nav labels */ + --fs-13: 13px; /* DEFAULT body */ + --fs-14: 14px; /* chat bubble body */ + --fs-15: 15px; /* chat header */ + --fs-18: 18px; /* detail title */ + --fs-20: 20px; /* dialog title (xl) */ + + --fw-regular: 400; + --fw-medium: 500; + --fw-bold: 700; /* used aggressively — even small chips are bold */ + --fw-extra: 800; /* titles and headers */ + + /* —— Radii (very rounded) ————————————————————— */ + --radius-sm: 8px; /* rounded-lg — small buttons, selects, log surface */ + --radius-md: 12px; /* rounded-xl — DEFAULT card/button/input — everywhere */ + --radius-lg: 16px; /* rounded-2xl — dialogs, chat bubbles */ + --radius-pill: 9999px; /* status pills, filter tabs, avatar */ + + /* —— Shadow system ————————————————————————— */ + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); /* card resting */ + --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25); /* modal */ + + /* —— Spacing scale (Tailwind units × 4) ————————— */ + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-8: 32px; + + /* —— Borders ——————————————————————————————— */ + --border-default: 1px solid var(--slate-200); + --border-active: 1px solid var(--accent); + + /* —— Semantic aliases ———————————————————————— */ + --fg-1: var(--slate-900); + --fg-2: var(--slate-600); + --fg-3: var(--slate-500); + --fg-muted: var(--slate-400); + --bg-1: #ffffff; + --bg-2: var(--slate-50); + --bg-app: var(--app-bg); + --border: var(--slate-200); +} + +/* —— Base type roles ————————————————————————— */ +html, body { font-family: var(--font-sans); color: var(--fg-1); background: var(--bg-app); } +body { font-size: var(--fs-13); } + +.h1 { font-size: var(--fs-20); font-weight: var(--fw-extra); color: var(--fg-1); letter-spacing: -0.01em; } +.h2 { font-size: var(--fs-18); font-weight: var(--fw-extra); color: var(--fg-1); } +.h3 { font-size: var(--fs-15); font-weight: var(--fw-bold); color: var(--fg-1); } +.h4 { font-size: var(--fs-13); font-weight: var(--fw-bold); color: var(--fg-1); } +.p { font-size: var(--fs-13); color: var(--fg-2); line-height: 1.55; } +.meta { font-size: var(--fs-11); color: var(--fg-3); } +.micro { font-size: var(--fs-10); color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: var(--fw-bold); } +.mono { font-family: var(--font-mono); } +.code { font-family: var(--font-mono); font-size: var(--fs-12); background: var(--slate-100); padding: 2px 6px; border-radius: 6px; } + +/* The signature "Agent Orchestrator" wordmark style used in TopBar */ +.wordmark { + font-family: var(--font-mono); + font-size: var(--fs-11); + font-weight: var(--fw-bold); + color: var(--accent); + text-transform: uppercase; + letter-spacing: 0.16em; +} diff --git a/docs/design/ui_kits_reference/admin-legacy/ChatPane.jsx b/docs/design/ui_kits_reference/admin-legacy/ChatPane.jsx new file mode 100644 index 0000000..fd8cd80 --- /dev/null +++ b/docs/design/ui_kits_reference/admin-legacy/ChatPane.jsx @@ -0,0 +1,128 @@ +// ChatPane — mirrors ui/src/components/chat/* with user/ask/result/progress bubbles +function Bubble({ role, children, footer }) { + const isUser = role === 'user'; + const style = { + maxWidth: '85%', + padding: '10px 14px', + borderRadius: 16, + fontSize: 13, + lineHeight: 1.55, + whiteSpace: 'pre-wrap', + wordBreak: 'break-word', + }; + if (isUser) { + Object.assign(style, { background: '#2563eb', color: '#fff', borderBottomRightRadius: 4, alignSelf: 'flex-end' }); + } else if (role === 'ask') { + Object.assign(style, { background: '#fef9c3', color: '#854d0e', border: '1px solid #fde68a', borderBottomLeftRadius: 4 }); + } else if (role === 'result') { + Object.assign(style, { background: '#ecfdf5', color: '#065f46', border: '1px solid #a7f3d0', borderBottomLeftRadius: 4 }); + } else { + Object.assign(style, { background: '#fff', color: '#0f172a', border: '1px solid #e2e8f0', borderBottomLeftRadius: 4 }); + } + return ( +
+
{children}
+ {footer &&
{footer}
} +
+ ); +} + +function ProgressBubble({ text }) { + return ( +
+ + {text} +
+ ); +} + +function ChatHeader({ task, onOpenDetail, detailOpen }) { + return ( +
+
+
+ TASK #{task.id} +
+
+ {task.title} +
+
+
+ + +
+
+ ); +} + +function Composer({ onSend }) { + const [text, setText] = React.useState(''); + const send = () => { if (!text.trim()) return; onSend(text.trim()); setText(''); }; + return ( +
+
+ +