From 77ee3bc426b3857024e9abffbfc6c36c27ed03ad Mon Sep 17 00:00:00 2001 From: oss-sync Date: Wed, 8 Jul 2026 23:35:00 +0000 Subject: [PATCH] sync: update from private repo (ddadfd71) --- .env.example | 6 +- .github/ISSUE_TEMPLATE/config.yml | 4 +- .gitignore | 8 + Dockerfile | 9 +- README.ja.md | 16 +- README.md | 16 +- SECURITY.md | 13 +- config.yaml.example | 9 +- docker-compose.yml | 11 +- docs/docker.ja.md | 7 +- docs/docker.md | 17 +- docs/getting-started.ja.md | 2 +- docs/getting-started.md | 2 +- docs/maintenance-checklist.md | 35 + docs/screenshots/settings.png | Bin 297878 -> 246853 bytes docs/screenshots/spaces.png | Bin 0 -> 253271 bytes docs/screenshots/task-list.png | Bin 197703 -> 207130 bytes docs/screenshots/workspace.png | Bin 387467 -> 333320 bytes docs/security-hardening.ja.md | 6 +- docs/security-hardening.md | 20 +- docs/tools/requestpackage.md | 53 + docs/tools/searchtaskconversation.md | 2 +- docs/tools/skills.md | 4 +- docs/tools/xsearch.md | 30 +- scripts/x-adapter/test_x_adapter.py | 98 + scripts/x-adapter/x_adapter.py | 183 +- .../server-route-order.test.ts.snap | 239 + src/bridge/a2a-subsystem.ts | 74 + src/bridge/a2a/a2a-exec-e2e.test.ts | 216 +- src/bridge/a2a/executor.test.ts | 187 +- src/bridge/a2a/executor.ts | 145 +- src/bridge/a2a/limiter.test.ts | 328 + src/bridge/a2a/limiter.ts | 216 + .../a2a/request-handler-auth-gate.test.ts | 221 +- src/bridge/a2a/request-handler.test.ts | 51 +- src/bridge/a2a/request-handler.ts | 130 +- src/bridge/admin-gateway-keys-mount.ts | 64 + src/bridge/auth-subsystem.ts | 172 + src/bridge/bridge-gateway-mount.ts | 102 + src/bridge/jobs-api.ts | 35 + src/bridge/local-tasks-api.ts | 9 + src/bridge/local-tasks-comments-api.ts | 11 +- .../local-tasks-package-requests-api.test.ts | 184 + .../local-tasks-package-requests-api.ts | 163 + src/bridge/metrics-subsystem.ts | 52 + src/bridge/novnc-mount.ts | 37 + src/bridge/server-route-order.test.ts | 88 + src/bridge/server.ts | 525 +- src/bridge/setup-config-mount.ts | 81 + src/bridge/skills-api.test.ts | 86 + src/bridge/skills-api.ts | 108 +- src/bridge/space-python-packages-api.ts | 79 +- .../space-python-packages-service.test.ts | 51 + src/bridge/space-python-packages-service.ts | 121 + src/bridge/ssh-admin-api.ts | 688 ++ src/bridge/ssh-api-shared.ts | 318 + src/bridge/ssh-api.ts | 1532 +---- src/bridge/ssh-user-api.ts | 589 ++ src/bridge/ui-static.ts | 36 + src/config.a2a-limits.test.ts | 26 + src/config.ts | 10 +- src/db/migrate.ts | 23 + src/db/repositories/a2a.ts | 336 + src/db/repositories/app-shares.ts | 69 + src/db/repositories/audit.ts | 9 + src/db/repositories/calendar.ts | 430 ++ src/db/repositories/gateway.ts | 487 ++ src/db/repositories/jobs.ts | 886 +++ src/db/repositories/llm-usage.ts | 254 + src/db/repositories/local-tasks.ts | 965 +++ src/db/repositories/notifications.ts | 321 + src/db/repositories/provenance.ts | 178 + src/db/repositories/reflection.ts | 143 + src/db/repositories/scheduled-tasks.ts | 305 + src/db/repositories/schema.ts | 538 ++ src/db/repositories/shared.ts | 44 + src/db/repositories/spaces.ts | 495 ++ src/db/repositories/task-search.ts | 144 + src/db/repositories/tool-requests.ts | 395 ++ src/db/repositories/users.ts | 563 ++ src/db/repositories/worker-nodes.ts | 140 + src/db/repository.a2a-revocation.test.ts | 49 + src/db/repository.package-requests.test.ts | 98 + src/db/repository.transcript-only-io.test.ts | 29 + src/db/repository.ts | 5774 +---------------- src/db/schema.sql | 24 + src/engine/agent-loop.ts | 3 + src/engine/agent-loop/context-control.test.ts | 105 + src/engine/agent-loop/context-control.ts | 37 +- src/engine/agent-loop/delegate-runner.ts | 4 +- src/engine/agent-loop/pending-states.test.ts | 15 + src/engine/agent-loop/pending-states.ts | 25 + src/engine/context/prompt-guard.test.ts | 21 + src/engine/context/prompt-guard.ts | 21 +- src/engine/context/token-estimate.test.ts | 23 + src/engine/context/token-estimate.ts | 18 + src/engine/piece-runner.ts | 56 + src/engine/skills.ts | 11 + src/engine/tools/core.test.ts | 48 + src/engine/tools/core.ts | 55 +- src/engine/tools/image.ts | 2 + src/engine/tools/index.ts | 23 +- src/engine/tools/package-request.test.ts | 93 + src/engine/tools/package-request.ts | 146 + .../tools/skills.materialize-swap.test.ts | 79 + src/engine/tools/skills.test.ts | 372 ++ src/engine/tools/skills.ts | 208 +- src/engine/tools/ssh-console-space.test.ts | 24 +- src/engine/tools/task-conversation.test.ts | 67 + src/engine/tools/task-conversation.ts | 49 +- src/engine/tools/text-excerpt.ts | 34 + src/engine/tools/tool-categories.ts | 1 + .../tools/workspace-task-search.test.ts | 17 + src/engine/tools/workspace-task-search.ts | 24 +- src/engine/tools/x.ts | 6 +- src/llm/openai-compat.ts | 11 +- src/progress/delegate-runs.test.ts | 123 + src/progress/delegate-runs.ts | 38 +- src/worker.execute-job-gates.test.ts | 196 + src/worker.ts | 1713 +++-- src/worker/piece-metric-status.test.ts | 21 + src/worker/piece-metric-status.ts | 25 + ui/e2e-auth/sharing-scope.auth.spec.ts | 12 +- ui/e2e/spaces.spec.ts | 4 +- ui/src/api.ts | 2302 +------ ui/src/api/browser-sessions.ts | 88 + ui/src/api/calendar.ts | 145 + ui/src/api/client.ts | 27 + ui/src/api/config.ts | 84 + ui/src/api/delegate.ts | 32 + ui/src/api/folder-files.ts | 30 + ui/src/api/gateway.ts | 164 + ui/src/api/notifications.ts | 107 + ui/src/api/pets.ts | 92 + ui/src/api/pieces.ts | 53 + ui/src/api/reflection.ts | 22 + ui/src/api/share.ts | 157 + ui/src/api/skills.ts | 82 + ui/src/api/space-files.ts | 145 + ui/src/api/spaces.ts | 290 + ui/src/api/subtasks.ts | 40 + ui/src/api/task-files.ts | 191 + ui/src/api/tasks.ts | 369 ++ ui/src/api/tools.ts | 44 + ui/src/api/usage.ts | 57 + ui/src/api/users.ts | 28 + ui/src/api/workers.ts | 118 + ui/src/components/chat/ChatMessage.tsx | 4 +- ui/src/components/chat/ChatPane.tsx | 196 +- ui/src/components/chat/MovementGroup.test.tsx | 55 + ui/src/components/chat/MovementGroup.tsx | 7 +- .../chat/PackageRequestApproval.test.tsx | 91 + .../chat/PackageRequestApproval.tsx | 80 + .../components/chat/ToolCallsSection.test.tsx | 81 + ui/src/components/chat/ToolCallsSection.tsx | 8 +- .../detail/ContextUsageGauge.test.tsx | 41 + .../components/detail/ContextUsageGauge.tsx | 23 +- .../detail/tabs/DelegateRunsSection.test.tsx | 416 +- .../detail/tabs/DelegateRunsSection.tsx | 104 +- ui/src/components/files/FilePreview.test.tsx | 31 +- ui/src/components/files/FilePreview.tsx | 29 +- .../components/settings/SkillsForm.test.tsx | 112 + ui/src/components/settings/SkillsForm.tsx | 27 +- .../spaces/SpaceChatTabBar.test.tsx | 71 + ui/src/components/spaces/SpaceChatTabBar.tsx | 96 + ui/src/components/spaces/SpaceDetail.tsx | 212 +- ui/src/content/help/00-changelog.md | 79 + ui/src/content/help/02-tasks.md | 2 + ui/src/content/help/03-running.md | 8 +- ui/src/content/help/04-results.md | 2 + ui/src/content/help/10-subtasks.md | 8 +- ui/src/content/help/11-skills.md | 6 +- ui/src/content/help/16-tools.md | 4 + ui/src/content/help/21-workspaces.md | 10 + ui/src/content/help/23-a2a.md | 5 +- ui/src/i18n/locales/en/chat.json | 8 + ui/src/i18n/locales/en/detail.json | 19 +- ui/src/i18n/locales/en/spaces.json | 4 +- ui/src/i18n/locales/ja/chat.json | 8 + ui/src/i18n/locales/ja/detail.json | 18 +- ui/src/i18n/locales/ja/spaces.json | 4 +- ui/src/lib/composerAutosize.test.ts | 74 + ui/src/lib/composerAutosize.ts | 23 + ui/src/lib/delegateRuns.test.ts | 136 +- ui/src/lib/delegateRuns.ts | 74 + ui/src/lib/markdown-text.test.tsx | 46 + ui/src/lib/markdown-text.tsx | 13 + 187 files changed, 19918 insertions(+), 10938 deletions(-) create mode 100644 docs/screenshots/spaces.png create mode 100644 docs/tools/requestpackage.md create mode 100644 src/bridge/__snapshots__/server-route-order.test.ts.snap create mode 100644 src/bridge/a2a-subsystem.ts create mode 100644 src/bridge/a2a/limiter.test.ts create mode 100644 src/bridge/a2a/limiter.ts create mode 100644 src/bridge/admin-gateway-keys-mount.ts create mode 100644 src/bridge/auth-subsystem.ts create mode 100644 src/bridge/bridge-gateway-mount.ts create mode 100644 src/bridge/jobs-api.ts create mode 100644 src/bridge/local-tasks-package-requests-api.test.ts create mode 100644 src/bridge/local-tasks-package-requests-api.ts create mode 100644 src/bridge/metrics-subsystem.ts create mode 100644 src/bridge/novnc-mount.ts create mode 100644 src/bridge/server-route-order.test.ts create mode 100644 src/bridge/setup-config-mount.ts create mode 100644 src/bridge/space-python-packages-service.test.ts create mode 100644 src/bridge/space-python-packages-service.ts create mode 100644 src/bridge/ssh-admin-api.ts create mode 100644 src/bridge/ssh-api-shared.ts create mode 100644 src/bridge/ssh-user-api.ts create mode 100644 src/bridge/ui-static.ts create mode 100644 src/config.a2a-limits.test.ts create mode 100644 src/db/repositories/a2a.ts create mode 100644 src/db/repositories/app-shares.ts create mode 100644 src/db/repositories/audit.ts create mode 100644 src/db/repositories/calendar.ts create mode 100644 src/db/repositories/gateway.ts create mode 100644 src/db/repositories/jobs.ts create mode 100644 src/db/repositories/llm-usage.ts create mode 100644 src/db/repositories/local-tasks.ts create mode 100644 src/db/repositories/notifications.ts create mode 100644 src/db/repositories/provenance.ts create mode 100644 src/db/repositories/reflection.ts create mode 100644 src/db/repositories/scheduled-tasks.ts create mode 100644 src/db/repositories/schema.ts create mode 100644 src/db/repositories/shared.ts create mode 100644 src/db/repositories/spaces.ts create mode 100644 src/db/repositories/task-search.ts create mode 100644 src/db/repositories/tool-requests.ts create mode 100644 src/db/repositories/users.ts create mode 100644 src/db/repositories/worker-nodes.ts create mode 100644 src/db/repository.package-requests.test.ts create mode 100644 src/db/repository.transcript-only-io.test.ts create mode 100644 src/engine/tools/package-request.test.ts create mode 100644 src/engine/tools/package-request.ts create mode 100644 src/engine/tools/skills.materialize-swap.test.ts create mode 100644 src/engine/tools/text-excerpt.ts create mode 100644 src/worker.execute-job-gates.test.ts create mode 100644 src/worker/piece-metric-status.test.ts create mode 100644 src/worker/piece-metric-status.ts create mode 100644 ui/src/api/browser-sessions.ts create mode 100644 ui/src/api/calendar.ts create mode 100644 ui/src/api/client.ts create mode 100644 ui/src/api/config.ts create mode 100644 ui/src/api/delegate.ts create mode 100644 ui/src/api/folder-files.ts create mode 100644 ui/src/api/gateway.ts create mode 100644 ui/src/api/notifications.ts create mode 100644 ui/src/api/pets.ts create mode 100644 ui/src/api/pieces.ts create mode 100644 ui/src/api/reflection.ts create mode 100644 ui/src/api/share.ts create mode 100644 ui/src/api/skills.ts create mode 100644 ui/src/api/space-files.ts create mode 100644 ui/src/api/spaces.ts create mode 100644 ui/src/api/subtasks.ts create mode 100644 ui/src/api/task-files.ts create mode 100644 ui/src/api/tasks.ts create mode 100644 ui/src/api/tools.ts create mode 100644 ui/src/api/usage.ts create mode 100644 ui/src/api/users.ts create mode 100644 ui/src/api/workers.ts create mode 100644 ui/src/components/chat/MovementGroup.test.tsx create mode 100644 ui/src/components/chat/PackageRequestApproval.test.tsx create mode 100644 ui/src/components/chat/PackageRequestApproval.tsx create mode 100644 ui/src/components/chat/ToolCallsSection.test.tsx create mode 100644 ui/src/components/detail/ContextUsageGauge.test.tsx create mode 100644 ui/src/components/settings/SkillsForm.test.tsx create mode 100644 ui/src/components/spaces/SpaceChatTabBar.test.tsx create mode 100644 ui/src/components/spaces/SpaceChatTabBar.tsx create mode 100644 ui/src/lib/composerAutosize.test.ts create mode 100644 ui/src/lib/composerAutosize.ts create mode 100644 ui/src/lib/markdown-text.test.tsx diff --git a/.env.example b/.env.example index 1b5b762..e674317 100644 --- a/.env.example +++ b/.env.example @@ -30,8 +30,10 @@ OLLAMA_MODEL=qwen3:32b # # To reach MAESTRO from another machine on a BARE-METAL install, set HOST # explicitly (e.g. 0.0.0.0) -- and enable auth in config.yaml first. -# (The Docker image already sets HOST=0.0.0.0 inside the container; external -# access is gated by the 127.0.0.1:9876 port mapping in docker-compose.yml.) +# (Docker is different: the image sets HOST=0.0.0.0 inside the container AND +# docker-compose.yml publishes the port on all host interfaces by default, so a +# Docker deployment is LAN-reachable out of the box. Enable auth/TLS before +# leaving it exposed; pin the compose mapping to 127.0.0.1:9876:9876 to undo.) # HOST=0.0.0.0 # HTTP(S) listen port (default 9876). diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index db18f92..e0e59be 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ blank_issues_enabled: false contact_links: - name: Security vulnerability - url: https://github.com/your-org/maestro/blob/main/SECURITY.md + url: https://gitea.swallow.wjg.jp/swallow/maestro/src/branch/main/SECURITY.md about: Do not open a public issue for a vulnerability. Follow the security policy instead. - name: Documentation - url: https://github.com/your-org/maestro/tree/main/docs + url: https://gitea.swallow.wjg.jp/swallow/maestro/src/branch/main/docs about: Setup, configuration, and architecture guides. diff --git a/.gitignore b/.gitignore index 73fc08f..d54f0d3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,14 @@ data/ .env.* !.env.example config.yaml +# Real config variants / backups at repo root (config-dogfood.yaml, +# config.yaml.bak-, ...) hold live credentials — never track them. +# Root-anchored: an unanchored `config*.yaml` would also swallow the tracked +# ui/e2e-auth/config.e2e-auth.yaml test fixture. +/config*.yaml +!/config.yaml.example +*.bak-* +feedback/ .superpowers/ *.db *.db-wal diff --git a/Dockerfile b/Dockerfile index 51f5d76..fefa8d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -145,10 +145,11 @@ RUN mkdir -p /app/data /workspaces \ && chown -R node:node /app/data /workspaces config.yaml # HOST=0.0.0.0 is required INSIDE the container so the published port is -# reachable. This is safe because docker-compose maps it to 127.0.0.1:9876 on -# the host — the container is not exposed to the LAN unless the operator changes -# that mapping. The app's own default (when HOST is unset, e.g. bare-metal) is -# 127.0.0.1; see createCoreServer in src/bridge/server.ts. +# reachable. docker-compose.yml publishes that port on ALL host interfaces by +# default, so a Docker deployment is reachable from the LAN out of the box — +# enabling auth/TLS before exposing it is the operator's responsibility (see +# docs/docker.md, SECURITY.md). The app's own default (when HOST is unset, e.g. +# bare-metal) is 127.0.0.1; see createCoreServer in src/bridge/server.ts. ENV NODE_ENV=production \ PORT=9876 \ HOST=0.0.0.0 \ diff --git a/README.ja.md b/README.ja.md index cbee965..1227862 100644 --- a/README.ja.md +++ b/README.ja.md @@ -14,16 +14,20 @@ OpenAI 互換の LLM エンドポイント([Ollama](https://ollama.com/) / vLL ## スクリーンショット -3ペイン構成のワークスペース。左にタスク一覧、中央にライブの会話とレンダリング済み成果物、右に状態・ミッションブリーフ・ファイル・トレースをまとめたサイドパネル: +ワークスペース画面。左端にワークスペース一覧、その隣に選択中ワークスペースのチャット一覧、残りの領域にエージェントとのライブ会話とレンダリング済み成果物が並ぶ。概要・進捗・ファイル・トレースはタブで切り替える: -![タスクワークスペース](docs/screenshots/workspace.png) +![ワークスペース](docs/screenshots/workspace.png)
-ほかのスクリーンショット — タスク一覧・設定 +ほかのスクリーンショット — ワークスペース・チャット一覧・設定 -実行中 / 待機中 / 完了をひと目で把握できるタスク一覧(クイックフィルタ付き): +個人ワークスペースと案件ごとのワークスペースが並ぶ。同じエージェントがどこでも動く: -![タスク一覧](docs/screenshots/task-list.png) +![ワークスペース一覧](docs/screenshots/spaces.png) + +実行中 / 待機中 / 完了をひと目で把握できるチャット一覧(クイックフィルタ付き): + +![チャット一覧](docs/screenshots/task-list.png) 設定画面。`config.yaml` の各セクションがフォーム化(LLM ワーカー・サンドボックス・認証・ツールほか): @@ -54,7 +58,7 @@ docker compose up -d # 初回はビルドしてから起動 `.env` や `config.yaml` は用意しなくても起動する。未設定のまま起動すると UI にセットアップウィザードが開き、そこから LLM の接続先を設定できる。先に指定しておきたい場合は `cp .env.example .env` して `OLLAMA_BASE_URL` / `OLLAMA_MODEL` を設定する。 -Compose は安全のため `127.0.0.1:9876` のみに公開する。別ホストからアクセス可能にする前に OAuth 認証を設定し、TLS 対応のリバースプロキシを配置すること。LLM エンドポイントは `.env` / `config.yaml` で指定する。 +Compose は `9876` を全インターフェースに公開するので、Docker ホストは初期状態から LAN で到達でき、設定するまで認証なし。共有ネットワークに置いたままにする前に、認証・`safety.bash_sandbox: always`・TLS(ネイティブ `server.tls` かリバースプロキシ)を有効化すること。ローカル限定に戻すにはマッピングを `127.0.0.1:9876:9876` に固定する。LLM エンドポイントは `.env` / `config.yaml` で指定する。 Docker の詳細ガイド(Linux のネットワーク・データ永続化・サンドボックス・トラブルシューティング)は **[docs/docker.md](docs/docker.ja.md)**。 diff --git a/README.md b/README.md index 13fe312..58b9468 100644 --- a/README.md +++ b/README.md @@ -14,16 +14,20 @@ It works standalone as long as you have an OpenAI-compatible LLM endpoint ([Olla ## Screenshots -The three-pane workspace — task list on the left, the live conversation and rendered output in the center, and a structured side panel (status, mission brief, files, trace) on the right: +The workspace — a rail of your workspaces on the left, the chats inside the selected one next to it, and the live conversation with the agent's rendered output filling the rest. Overview, progress, files, and the full trace are one tab away: -![Task workspace](docs/screenshots/workspace.png) +![Workspace](docs/screenshots/workspace.png)
-More screenshots — task list and settings +More screenshots — workspaces, chat list, and settings -Task list with at-a-glance status (running / waiting / done) and quick filters: +Your personal workspace and each project workspace sit side by side; the same agent runs in every one: -![Task list](docs/screenshots/task-list.png) +![Workspaces](docs/screenshots/spaces.png) + +Chats with at-a-glance status (running / waiting / done) and quick filters: + +![Chat list](docs/screenshots/task-list.png) Settings: every `config.yaml` section as a form — LLM workers, sandbox, auth, tools, and more: @@ -54,7 +58,7 @@ docker compose up -d # builds on first run, then starts No `.env` or `config.yaml` is needed to start: a fresh `docker compose up` opens a setup wizard in the UI where you point MAESTRO at your LLM. To preset it instead, `cp .env.example .env` and set `OLLAMA_BASE_URL` / `OLLAMA_MODEL`. -For safety, Compose exposes only `127.0.0.1:9876`. Before making it reachable from another host, configure OAuth authentication and place a TLS-enabled reverse proxy in front. Specify the LLM endpoint in `.env` / `config.yaml`. +Compose publishes `9876` on all interfaces, so a Docker host is reachable from your LAN out of the box — and unauthenticated until you configure it. Before leaving it on a shared network, enable authentication, `safety.bash_sandbox: always`, and TLS (native `server.tls` or a reverse proxy); pin the mapping to `127.0.0.1:9876:9876` to keep it local-only. Specify the LLM endpoint in `.env` / `config.yaml`. Full Docker guide (Linux networking, data persistence, the sandbox, troubleshooting): **[docs/docker.md](docs/docker.md)**. diff --git a/SECURITY.md b/SECURITY.md index 5054e1d..86ff917 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,11 +6,14 @@ Security fixes are applied to the latest release and the `main` branch. ## Reporting a Vulnerability -Do not open a public issue for an undisclosed vulnerability. Use the -repository host's private security-reporting feature when available, or contact -the repository owner privately. Include affected versions, impact, reproduction -steps, and any suggested mitigation. Maintainers should acknowledge a report -within seven days and coordinate disclosure after a fix is available. +Do not open a public issue for an undisclosed vulnerability. The repository is +hosted on Gitea, which has no private vulnerability-reporting feature, so +contact the repository owner privately through the hosting platform instead of +filing a public issue. + +Include affected versions, impact, reproduction steps, and any suggested +mitigation. Maintainers should acknowledge a report within seven days and +coordinate disclosure after a fix is available. ## Deployment Baseline diff --git a/config.yaml.example b/config.yaml.example index c88bc7b..8cf7f29 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -462,7 +462,7 @@ tools: # notifications: # push: # enabled: false # true で V2 を有効化 -# vapid_subject: "https://maestro.example.com/" # RFC 8292 — operations URL preferred +# vapid_subject: "https://maestro.example.com/" # RFC 8292 — operations URL preferred (運用サイトの URL か mailto:) # 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) @@ -479,3 +479,10 @@ tools: # enabled: false # issuer: "https://localhost:8080/oidc" # 認可サーバーの issuer(公開 URL) # resource_audience: "https://localhost:8080/a2a" # トークン audience(A2A endpoint) +# limits: +# rate_per_minute: 30 # ingress requests/min per delegation (token bucket) +# max_concurrent_tasks: 5 # simultaneous in-flight A2A tasks per delegation +# max_payload_bytes: 65536 # HTTP body + message size cap +# max_stream_seconds: 600 # max SSE stream duration (task + resubscribe) +# skill_budget_per_hour: 100 # accepted tasks per rolling hour per delegation +# max_concurrent_resubscribe: 5 # simultaneous tasks/resubscribe streams per delegation diff --git a/docker-compose.yml b/docker-compose.yml index a4163fe..def9712 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,15 @@ services: # pool work. Harmless when display_mode is headless. shm_size: "1gb" ports: - # Auth is optional, so keep the default deployment local-only. - - "127.0.0.1:9876:9876" + # Published on ALL host interfaces so a Docker host is reachable from your + # LAN out of the box (the common case for a headless server). This means a + # fresh instance is exposed: the agent API includes the Bash tool, so an + # UNAUTHENTICATED instance on the network is effectively remote code + # execution. Before leaving it up on a shared/untrusted network, enable + # auth (`auth` in config.yaml), `safety.bash_sandbox: always`, and TLS — + # see docs/docker.md "Going beyond localhost". To keep it local-only + # instead, change this to "127.0.0.1:9876:9876". + - "9876:9876" extra_hosts: - "host.docker.internal:host-gateway" # .env is optional: a bare `docker compose up --build` works with zero setup. diff --git a/docs/docker.ja.md b/docs/docker.ja.md index dc7eea3..2a4dd96 100644 --- a/docs/docker.ja.md +++ b/docs/docker.ja.md @@ -15,7 +15,7 @@ docker compose up -d # 初回はイメージをビルドしてから起動 `.env` や `config.yaml` は用意しなくても起動します。未設定のまま起動すると UI にセットアップウィザードが開き、そこから LLM の接続先を設定できます。先に指定しておきたい場合は `cp .env.example .env` して `OLLAMA_BASE_URL` / `OLLAMA_MODEL` を設定します([LLM エンドポイント](#llm-エンドポイント)参照)。 -Compose は UI を `127.0.0.1:9876` だけに公開するので、初期状態では LAN から到達できません。外部公開は[ローカル以外へ出す](#ローカル以外へ出す)を参照。 +Compose は `9876` を**全インターフェース**に公開するので、Docker ホストは初期状態から LAN で到達できます。裏を返すと初期状態は**認証なしで露出**しており、ポートに届く相手はエージェントの Bash ツール経由でホスト上のコードを実行できます。共有ネットワークに置いたままにする前に[ローカル以外へ出す](#ローカル以外へ出す)を必ず読んでください。 ### Windows(WSL2) @@ -94,12 +94,13 @@ bubblewrap は非特権ユーザー名前空間を必要とします。多くの ## ローカル以外へ出す -既定のバインドは意図的にローカル限定です。ネットワークへ公開する前に: +既定で Compose は `9876` を全インターフェースに公開するため、インスタンスは初期状態から LAN で到達でき、**かつ認証なし**です。共有ネットワーク・信頼できないネットワークで常用する前に必ず堅牢化してください: 1. 認証を有効化(OAuth、またはローカルアカウント)。 2. `safety.bash_sandbox: always` を設定。 3. TLS を終端(MAESTRO のネイティブ HTTPS か、前段のリバースプロキシ)。 -4. compose のポートマッピングを `127.0.0.1:9876:9876` から、公開したいインターフェースへ変更。 + +逆に**ローカル限定**に戻したい場合(SSH トンネル経由で使う等)は、compose のポートマッピングを `127.0.0.1:9876:9876` に固定します。 完全な堅牢化チェックリストは [SECURITY.md](../SECURITY.md) と [getting-started.md](getting-started.md) を参照。 diff --git a/docs/docker.md b/docs/docker.md index 11fcf7a..0da06c8 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -20,8 +20,11 @@ opens a setup wizard in the UI where you point MAESTRO at your LLM. To preset th endpoint instead, `cp .env.example .env` and set `OLLAMA_BASE_URL`/`OLLAMA_MODEL` (see [The LLM endpoint](#the-llm-endpoint)). -Compose publishes the UI on `127.0.0.1:9876` only, so a fresh instance is not -reachable from your LAN. See [Going beyond localhost](#going-beyond-localhost). +Compose publishes the UI on `9876` on **all host interfaces**, so a Docker host +is reachable from your LAN out of the box. That also means a fresh instance is +exposed and **unauthenticated** — anyone who can reach the port can run code on +the host via the agent's Bash tool. Read [Going beyond localhost](#going-beyond-localhost) +before leaving it up on a shared network. ### Windows (WSL2) @@ -129,14 +132,16 @@ Rebuild after pulling new code with `docker compose up -d --build`. ## Going beyond localhost -The default binding is intentionally local-only. Before exposing MAESTRO to a -network: +By default Compose publishes `9876` on all interfaces, so the instance is already +reachable from your LAN — **and unauthenticated**. Before you rely on it on any +shared or untrusted network, harden it: 1. Enable authentication (OAuth, or local accounts). 2. Set `safety.bash_sandbox: always`. 3. Terminate TLS — either MAESTRO's native HTTPS or a reverse proxy in front. -4. Change the compose port mapping from `127.0.0.1:9876:9876` to the interface - you intend to serve. + +To keep an instance **local-only** instead (e.g. reach it over an SSH tunnel), +pin the compose port mapping back to `127.0.0.1:9876:9876`. See [SECURITY.md](../SECURITY.md) and [getting-started.md](getting-started.md) for the full hardening checklist. diff --git a/docs/getting-started.ja.md b/docs/getting-started.ja.md index 575b649..9b5c6db 100644 --- a/docs/getting-started.ja.md +++ b/docs/getting-started.ja.md @@ -101,7 +101,7 @@ docker compose up -d # http://localhost:9876 ``` -DB とワークスペースは named volume(`maestro-data` / `maestro-workspaces`)に永続化される。Compose は既定で `127.0.0.1:9876` のみに公開する。`config.yaml` をホストからマウントする場合は `docker-compose.yml` のコメントを参照。 +DB とワークスペースは named volume(`maestro-data` / `maestro-workspaces`)に永続化される。Compose は既定で `9876` を全インターフェースに公開するため、インスタンスは LAN から到達でき、`auth` を設定するまで認証なしのまま。共有ネットワークに置く前に認証・TLS を有効化するか、ローカル限定にしたい場合はマッピングを `127.0.0.1:9876:9876` に固定する。`config.yaml` をホストからマウントする場合は `docker-compose.yml` のコメントを参照。 ### ブラウザ・セットアップウィザード(`config.yaml` を編集しない) diff --git a/docs/getting-started.md b/docs/getting-started.md index f3432f2..3d3f854 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -101,7 +101,7 @@ docker compose up -d # http://localhost:9876 ``` -The DB and workspaces are persisted in named volumes (`maestro-data` / `maestro-workspaces`). By default Compose exposes only `127.0.0.1:9876`. If you want to mount `config.yaml` from the host, see the comments in `docker-compose.yml`. +The DB and workspaces are persisted in named volumes (`maestro-data` / `maestro-workspaces`). By default Compose publishes `9876` on all interfaces, so the instance is reachable from your LAN — and unauthenticated until you configure `auth`. Enable auth/TLS before leaving it on a shared network, or pin the mapping to `127.0.0.1:9876:9876` for local-only. If you want to mount `config.yaml` from the host, see the comments in `docker-compose.yml`. ### Browser setup wizard (no `config.yaml` editing) diff --git a/docs/maintenance-checklist.md b/docs/maintenance-checklist.md index bbdb36a..bdc6d6d 100644 --- a/docs/maintenance-checklist.md +++ b/docs/maintenance-checklist.md @@ -620,6 +620,41 @@ grep -n "a2a_delegations\|a2a_skills" src/db/repository.ts --- +## 16. OSS リリース(`scripts/oss-sync.sh --push`)を実行するとき + +- **スクリーンショットの目視確認(必須)**: `docs/screenshots/*.png` はバイナリのため、release gate(`oss/forbidden.txt` の正規表現スキャン)では**描画内容を検査できない**。スクリーンショットを更新した場合は、push 前に各画像を開いて以下が写り込んでいないか目視で確認する + - 内部ホスト名・URL(Gitea / 社内サービスのアドレスバー等) + - 実在のタスクタイトル・会話内容・ファイル名 + - ユーザー名・メールアドレス・組織名 + - 資格情報やトークンの断片(設定画面のマスク漏れ) +- 撮り直しの手順は README スクリーンショット刷新時のレシピ(隔離 worktree + シードデータ)に従い、実データ環境のキャプチャをそのまま使わない + +--- + +## 17. 承認パーク型 META ツール(RequestTool / RequestPackage)を追加・変更する場合 + +エージェントが「これが欲しい」と申告 → 人が承認 → ジョブ再開、という停車型ツールの配線点。RequestPackage(PR3)は RequestTool を全面ミラーしている。片方だけ直すと不整合になるので、両方を並べて確認すること。 + +**対象ファイル(RequestPackage を例に):** +- `src/engine/tools/package-request.ts` — ツール本体(`TOOL_DEFS` + `executeTool`)。バリデーションは `engine/python-packages.ts` の `parseAndValidateSpec` / `assertNotShadowing` を再利用 +- `src/engine/tools/core.ts` — `ToolContext` に park フラグ(`pendingPackageApproval`)+ recorder(`recordPackageRequest`)+ 補助状態(`installedPackages` / `declinedPackages`) +- `src/engine/tools/index.ts` — module loader / `Object.assign(allDefs, ...)` / **`META_TOOLS` 配列(2 箇所)** / dispatch 分岐 +- `src/engine/tools/tool-categories.ts` — `META_TOOLS` Set にツール名を追加 +- `src/engine/agent-loop/pending-states.ts` — `buildPackageApprovalWaitResult`(park → `next:'WAITING_HUMAN_PACKAGE_REQUEST'`) +- `src/engine/agent-loop.ts` — import + `buildToolApprovalWaitResult` の隣で呼ぶ +- `src/engine/piece-runner.ts` — `WAITING_HUMAN_PACKAGE_REQUEST` sentinel 分岐(waiting_human park)+ **2 つの PieceRunOptions interface** に recorder/補助状態を追加 + ctx 構築で piece/movement を注入 +- `src/worker.ts` — recorder を task/job/space にバインド(feature enabled かつ space 解決可のときのみ)+ waiting_human 分岐で **`wait_reason='package_request'` を永続化**(忘れると resume がマッチせず永久停止) +- **DB(三重ミラー)**: `src/db/schema.sql` / `src/db/migrate.ts` / `src/db/repositories/schema.ts`(`package_requests` テーブル)+ `src/db/repositories/tool-requests.ts`(record/get/list/decide/revert/listDeclined + 型 + rowMapper)+ `src/db/repositories/jobs.ts`(`resumePackageRequestJob` + `createJobIfNoPending` の承認ポーズ判定)。実装は `repositories/*` に置き、`src/db/repository.ts` ファサードに委譲メソッドを足す(`repository.ts` は肥大分割済みの薄いファサード) +- `src/bridge/local-tasks-package-requests-api.ts` — GET 一覧(view 権限)+ POST decide(**task write 権限= owner/admin/space editor のみ**、エージェント自己承認不可)。承認時のインストールは `space-python-packages-service.ts` の共有パスを通す +- `src/bridge/local-tasks-api.ts` — ルート登録 + `LocalTasksApiOptions` に config アクセサ / `server.ts` で配線 +- `ui/src/components/chat/PackageRequestApproval.tsx` + `ChatPane.tsx` + `ui/src/api/tasks.ts` + i18n `chat.json`(ja/en) +- `docs/tools/requestpackage.md`(ReadToolDoc)+ ヘルプ + changelog + +**なぜ必要か:** +停車系は「ジョブ停車=ワーカー解放」で成り立つ(ツール内同期ブロックはデッドロック)。sentinel(`WAITING_HUMAN_*`)は tool → pending-states → agent-loop → piece-runner → **worker(wait_reason 永続化)** の全経路が揃って初めて機能する。承認 API は必ず write 権限ゲートを通し、インストールは共有サービス(bwrap 必須 fail-closed / wheels のみ / 固定 index / shadowing 拒否)経由に限定する。 + +--- + ## 自動検知の可能性 - **ツールモジュール登録漏れ**: `index.ts` と `tools-api.ts` のモジュール一覧を比較するスクリプトで CI チェック可能 diff --git a/docs/screenshots/settings.png b/docs/screenshots/settings.png index cd7c25d5dd6d7345d0a1c6e4c64c10e81da29c7f..84d4f6c573a1395fd8b4712925d2fb015ef7ab58 100644 GIT binary patch literal 246853 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 TuQa7lr%Q>+ix#}o`}}_Z7>;;; literal 297878 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

xgd>14%WC&&I zD|z1@%#ypi#=8^!T^Eq*dBpkjBoOUS7M|^(`rt9I1_Cw%0}j^VJ>val?G)c;bM&}G zTq9_w?AE@n+mlRU^Nm`dF_Lbz+41Dn@X1WI*UIsF^bM+YFC*_n7W5obzKF{h$`AA2 zN)@XP*e$}lYp9CDmQ_Kb*{MT)ekVE5+fEXKjDSW~iL9yVj8pj25B@HF;=Tjhn@)~L z;i}Er7%@{id7kg(lcn!^Euex*N^CgwsvTB`(GzhL!NI{&gv;&1jVbvhb*J@lko1?* z@|Su@PVzr!!8mhDIp2xwG!Q*q{zpD1g4rqsW|qoSKNH*%es-00;qzHdZx6Dlz$7sS zYti0i_N}uJpyp_oY5j2iq<}5Qv=H22mok#_Ko?oqPVlvZ??)t5&sQWiT<&@RICa^S z!0$KRBRD#$d}%`G&iY0&Nx-5sdHr;c&{-m7H|C_p@sAj5I0H*iNru36+Td(T1lJ+y z%*uF1$i$hy%3u|5^VxypL4DZJP@hn%ot*`)Nw+M9cArc;@E&!4TYA1{!4H|} zPWV+Gd+-8m*HwWs&T2XzWZ-A+8&M5^C8SQ3C!_&pdQvnLnDU{8daQu*pkUbPn=a6P z*ONlE_jbfQbgo_-^gWn5X==@=iL=sFuZI^8*5V{xQtYq(w0W!N76Y&DV93BT}b7$6H?DF6KI�MqS6}KDD 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%0D89>mqW)uNy}5n4p|@I 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

P*7a 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

V3VGW#r;Btf87W)H3>ChM07g8JcvK#(HbHz$ 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 CH^j{V diff --git a/docs/screenshots/spaces.png b/docs/screenshots/spaces.png new file mode 100644 index 0000000000000000000000000000000000000000..f1d0327b37cccfe2f0cfd83bcecf6009b2f7818f GIT binary patch literal 253271 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 slxjDTc(odrsf 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%0D!O(;mwo{%><1xuaG3q(Bc*7iGKClUG+J*}Ujj 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∈7jz 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℞pMZ@P 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%0DLQHqw;gW?(wuU{UeG;ZvE|xcY-i#BQOF>3 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
    E3_FN*I=9nn7N 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 diff --git a/ui/src/components/chat/ChatPane.tsx b/ui/src/components/chat/ChatPane.tsx index 3f4e8de..f0d4244 100644 --- a/ui/src/components/chat/ChatPane.tsx +++ b/ui/src/components/chat/ChatPane.tsx @@ -9,9 +9,11 @@ import { groupCommentsByMovement, MovementGroupExpanded } from './MovementGroup' import { SubtaskInlineCard } from './SubtaskInlineCard'; import RotatingTips from './RotatingTips'; import { ToolRequestApproval } from './ToolRequestApproval'; +import { PackageRequestApproval } from './PackageRequestApproval'; import { DelegateLiveConsole } from './DelegateLiveConsole'; import { useJobStream } from '../../hooks/useJobStream'; import { extractStreamingField, CONTENT_FIELD } from '../../lib/streamFieldExtract'; +import { supportsFieldSizing, autosizeTextarea } from '../../lib/composerAutosize'; async function toBase64(file: File): Promise { @@ -48,6 +50,14 @@ export function ChatPane({ task, comments, onSubmit, onCancel, detailTabs, activ const [sendError, setSendError] = useState(null); const scrollRef = useRef(null); const fileInputRef = useRef(null); + const composerRef = useRef(null); + // Firefox など field-sizing 未対応環境だけ JS で高さ追従(判定は初回のみ)。 + const needsAutosizeFallback = useMemo(() => !supportsFieldSizing(), []); + useEffect(() => { + if (!needsAutosizeFallback) return; + const el = composerRef.current; + if (el) autosizeTextarea(el, 192); // 192px ≈ 8行 (max-h-48 と一致させる) + }, [draft, needsAutosizeFallback]); // Snapshot of comments.length at submit start. We hold "submitting" until // (a) the new user comment is reflected in the list AND (b) the job is // visibly busy (= picked up by a worker). Without this, the gap between the @@ -272,13 +282,11 @@ export function ChatPane({ task, comments, onSubmit, onCancel, detailTabs, activ lastBackendId={task.latestJob?.lastBackendId ?? null} /> {/* Header */} -
    -
    -
    -

    {task.title}

    -
    #{task.id} · {task.pieceName}
    -
    -
    +
    +
    +

    {task.title}

    + #{task.id} · {task.pieceName} +
    {isBusy && (
    - {/* Composer */} -
    + {/* Composer — カード+ツールバー型。上段 textarea(自動拡張)、下段に + 添付・コンテキスト残量・送信系を集約。旧: 独立ゲージ行+2行 textarea で + 約114px → 待機時約85px。 */} +
    {hasActiveJob && (
    )} + {sendError && !isBusy && (
    ⚠ {sendError} @@ -475,108 +486,111 @@ export function ChatPane({ task, comments, onSubmit, onCancel, detailTabs, activ
    )} - {attachments.length > 0 && ( -
    - {attachments.map(a => ( - - {a.name} - - - ))} -
    - )} - {/* コンテキスト残量を入力欄の直上に常時表示。概要タブまでスクロールせずに、 - 入力しながら「あとどれくらい書けるか」を把握できる(issue #009)。 */} -
    - -
    -
    - { void handleFiles(e.target.files); e.target.value = ''; }} - /> - +

    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 z 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 ZXJ+WXdo{&i=KpGt9#2<4mvv4FO#o9gy&3=j literal 387467 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%0DFf#qOr?+GrUJvLqpBy7e@I}Z1$-2DawuV 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 diff --git a/docs/security-hardening.ja.md b/docs/security-hardening.ja.md index 9511248..eab879f 100644 --- a/docs/security-hardening.ja.md +++ b/docs/security-hardening.ja.md @@ -8,12 +8,12 @@ MAESTRO は LLM 駆動のタスクを実行し、その中でコード実行・W ## 脅威モデル(一段落) -UI/API に到達できる者はタスクを作成でき、タスクはツール(Bash・Web・ブラウザ・ファイル、そして有効なら SSH/MCP)を実行できます。認証が無ければ、ポートに到達できる者は誰でもホスト上でコードを実行できることになります。だから既定のデプロイは**ローカル限定・認証なし**です。以下はそれを安全に広げる手順です。 +UI/API に到達できる者はタスクを作成でき、タスクはツール(Bash・Web・ブラウザ・ファイル、そして有効なら SSH/MCP)を実行できます。認証が無ければ、ポートに到達できる者は誰でもホスト上でコードを実行できることになります。既定では**認証なし**で、ベアメタルでは localhost にバインドしますが、Docker の既定はポートを LAN に公開します。露出したまま放置する前に固めるのは運用者の責任です。以下がそのチェックリストです。 ## 1. ネットワーク公開 -- アプリは既定で `127.0.0.1` にバインドし(ベアメタル)、Docker Compose は `127.0.0.1:9876` のみを公開します。認証と TLS が整うまでこのままに。 -- 公開する際は、バインド/ポート(`server.port`、Compose のポートマッピング)を意図的に変更し、TLS を前段に置きます(MAESTRO のネイティブ HTTPS `server.tls` か、リバースプロキシ)。 +- アプリはベアメタルでは既定で `127.0.0.1` にバインドしますが、Docker Compose は `9876` を**全インターフェース**に公開するため、Docker デプロイは初期状態から LAN 到達可能・認証なしです。共有ネットワークに置いたままにする前に認証と TLS を有効化してください。ローカル限定に戻すには Compose のマッピングを `127.0.0.1:9876:9876` に固定します(SSH トンネル経由で到達)。 +- loopback を超えて到達可能な間は、常に TLS を前段に置きます(MAESTRO のネイティブ HTTPS `server.tls` か、リバースプロキシ)。 ## 2. 認証 diff --git a/docs/security-hardening.md b/docs/security-hardening.md index 8db38cf..ee7d418 100644 --- a/docs/security-hardening.md +++ b/docs/security-hardening.md @@ -15,18 +15,20 @@ secrets live and their file permissions, see its *Secrets and Data* section. Anyone who can reach the UI/API can create tasks, and a task can run tools (Bash, web, browser, files, and — if enabled — SSH/MCP). Without authentication -that means anyone who can reach the port can run code on the host. The default -deployment is therefore **localhost-only and unauthenticated**; everything below -is about safely widening that. +that means anyone who can reach the port can run code on the host. It ships +**unauthenticated**: on bare metal it binds to localhost, but the Docker default +publishes the port to your LAN — so securing it before you leave it exposed is +on you. Everything below is that checklist. ## 1. Network exposure -- The app binds to `127.0.0.1` by default (bare metal) and Docker Compose - publishes only `127.0.0.1:9876`. Keep it that way until auth and TLS are in - place. -- When you do expose it, change the bind/port deliberately (`server.port`, the - Compose port mapping) and front it with TLS — either MAESTRO's native HTTPS - (`server.tls`) or a reverse proxy. +- The app binds to `127.0.0.1` by default on bare metal, but Docker Compose + publishes `9876` on **all interfaces** by default — so a Docker deployment is + LAN-reachable and unauthenticated out of the box. Enable auth and TLS before + leaving it on any shared network, or pin the Compose mapping to + `127.0.0.1:9876:9876` (and reach it over an SSH tunnel) to keep it local-only. +- Front it with TLS — either MAESTRO's native HTTPS (`server.tls`) or a reverse + proxy — whenever it is reachable beyond loopback. ## 2. Authentication diff --git a/docs/tools/requestpackage.md b/docs/tools/requestpackage.md new file mode 100644 index 0000000..89a5436 --- /dev/null +++ b/docs/tools/requestpackage.md @@ -0,0 +1,53 @@ +# RequestPackage + +Request a Python wheel that is not already available, so an approver can install +it into **this workspace** and let the run continue. + +## When to use it + +You tried to `import somelib` (in `Bash` running `python3`, or `RunUserScript`) +and it failed with `ModuleNotFoundError`, and the library is **not** in the +preinstalled set. Instead of retrying `pip install` (which is blocked in the +sandbox), call: + +``` +RequestPackage({ name: "httpx", reason: "need an async HTTP client for the API calls" }) +RequestPackage({ name: "pandas==2.2.2", reason: "pinned for reproducibility" }) +``` + +- `name` — the package. Either a bare name (`httpx`) or an exact pin + (`httpx==0.27.0`). **Only `name==version` pins are accepted** — ranges + (`>=`, `~=`) and URLs/extras are rejected (the overlay must be reproducible). +- `reason` — a concrete justification. It is shown to the approver. + +## What happens + +- **Interactive runs (a user is watching the chat):** the movement pauses and an + Approve / Deny card appears in the chat. When a task-write user (owner / admin / + space editor) approves, the wheel is installed into this workspace's private + overlay and the run resumes automatically — now `import` works. If denied, you + continue without it. +- **Non-interactive runs (subtasks / scheduled):** the request is recorded (the + user finds it later) and you proceed **without** the package. Don't block on it — + finish with what you have, or `complete({status:"needs_user_input"})` if you + genuinely can't proceed. + +## Rules and limits + +- Wheels only, from a fixed package index. No source builds, no arbitrary index. +- A package that would shadow the standard library or a preinstalled package + (e.g. `os`, `numpy`, `pandas`) is rejected — those are already importable. +- If the package is already installed in this workspace, the tool tells you to + just `import` it (no approval needed). +- The install is per-workspace. Other workspaces do not see it. +- You cannot approve your own request — approval is a human/operator action. + +## After approval + +The run re-enters the same step. Re-run your Python; the import now succeeds. If +you call `RequestPackage` again for the same package, it reports "already +installed". + +Related: preinstalled packages are listed in the error you get from a blocked +`pip install`. Workspace-wide package management (for operators) lives in +**Settings → the workspace's Python packages panel**. diff --git a/docs/tools/searchtaskconversation.md b/docs/tools/searchtaskconversation.md index b070a46..32c84e7 100644 --- a/docs/tools/searchtaskconversation.md +++ b/docs/tools/searchtaskconversation.md @@ -13,7 +13,7 @@ | 名前 | 必須 | 説明 | |------|------|------| -| `query` | ○ | 検索キーワード。部分一致・大文字小文字は無視 | +| `query` | ○ | 検索キーワード。スペース区切りで複数指定すると AND 検索(すべての語を含む発言だけがヒット)。各語は部分一致・大文字小文字は無視 | | `source` | | `comments` / `transcript` / `both`(既定 `both`) | | `author` | | `user` / `agent` / `system` で発言者を絞る | | `kind` | | コメント種別で絞る(request/comment/interjection/result/ask/progress/handoff)。transcript には適用されない | diff --git a/docs/tools/skills.md b/docs/tools/skills.md index 004c5f1..1e058b5 100644 --- a/docs/tools/skills.md +++ b/docs/tools/skills.md @@ -13,8 +13,8 @@ Piece の取得・編集には GetPiece / CreatePiece / UpdatePiece を使う。 ## ツール -- **InstallSkill** — スキルを保存する。通常は `content` に SKILL.md 全文(YAML frontmatter + 本文)を渡す。workspace 内に `SKILL.md` と `scripts/` 等を含むディレクトリを組み立て済みなら `sourcePath`(workspace 内の絶対パス)を渡す。`scope` は `user`(個人 or 共有ワークスペース)か `system`(全ユーザー共有・admin のみ)。 -- **ReadSkill** — スキル本文を取得する。ディレクトリ型スキルは workspace の `skills/{name}/` に展開され、その相対パスでスクリプトを実行できる。 +- **InstallSkill** — スキルを保存する。通常は `content` に SKILL.md 全文(YAML frontmatter + 本文)を渡す。workspace 内に `SKILL.md` と `scripts/` 等を含むディレクトリを組み立て済みなら `sourcePath` に **workspace 相対パス**(例: `output/my-skill`)を渡す。ホスト上のフルパスは不要(パス中に workspace の実配置(`space/{id}/files` 等)が含まれていれば自動で workspace 相対に読み替える)。`scope` は `user`(個人 or 共有ワークスペース)か `system`(全ユーザー共有・admin のみ)。**`name` パラメータは SKILL.md frontmatter の `name:` と完全一致が必須**(不一致だと一覧上の表示名と保存フォルダ名がズレて UI から管理不能になるため、インストール自体を拒否する)。frontmatter に有効な `name:`(`[a-z0-9_-]`)が無い SKILL.md も拒否される。 +- **ReadSkill** — スキル本文を取得する。ディレクトリ型スキルは workspace の `skills/{name}/` に展開され、その相対パスでスクリプトを実行できる。展開済みでもスキル元が更新されていれば、次の ReadSkill でコピーを丸ごと最新版に置き換える(元が未変更なら workspace 内コピーへの手直しは維持される)。 - **ListSkills** — インストール済みスキルの一覧を返す。 ## スコープと可視性(重要) diff --git a/docs/tools/xsearch.md b/docs/tools/xsearch.md index 689f075..9632bff 100644 --- a/docs/tools/xsearch.md +++ b/docs/tools/xsearch.md @@ -87,13 +87,39 @@ XTimeline({ ```js XPostDetail({ - url: "https://twitter.com/.../status/1234567890", - // または status_id: "1234567890" + tweet: "https://x.com/.../status/1234567890", + // tweet ID だけでも、長文記事 URL (https://x.com/.../article/1234567890) でも可 }) ``` 返り値にはリプライツリーが含まれる。議論の流れを追いたいときに使う。 +### X 長文記事(X Articles) + +対象が長文記事ポストの場合、`text` は記事への t.co リンク 1 本だけになる。 +記事の中身は `article` キーに入る: + +```yaml +data: + - id: '1234567890' + text: https://t.co/xxxx # 記事ポストの本文はリンクのみ + article: + title: 記事タイトル + previewText: 冒頭のプレビュー文 + plainText: 記事本文の全文(プレーンテキスト) + publishedAtISO: '2026-07-02T00:04:51+00:00' + coverImageUrl: https://pbs.twimg.com/media/XXXX.jpg +``` + +- `plainText` はデフォルトで 12,000 文字で切り詰め、`plainTextTruncated: true` が付く。 + 全文が必要なら `full_text: true` を渡す +- **記事内の埋め込み画像・動画は `media[]` に合流する**(先頭 20 件まで)。通常の + 投稿メディアと同じく自動ダウンロードされ `localPath` が付くので、そのまま + ReadImage / AnnotateImage に渡せる。動画の扱いは `tools.x_download_video` に従う +- カバー画像(`coverImageUrl`)は自動ダウンロード対象外。必要なら DownloadFile に渡す +- 記事ページ(`x.com/{user}/article/{id}`)を WebFetch で読むことはできない + (ログイン壁で失敗する)。記事は必ずこのツールで取得する + ## 出力フォーマット - デフォルト: 構造化テキスト(投稿者・本文・いいね数等) diff --git a/scripts/x-adapter/test_x_adapter.py b/scripts/x-adapter/test_x_adapter.py index af97222..901fb28 100644 --- a/scripts/x-adapter/test_x_adapter.py +++ b/scripts/x-adapter/test_x_adapter.py @@ -29,6 +29,7 @@ def ns(**kw): check("ref: bare id", xa.parse_tweet_ref("20") == "20") check("ref: x.com url", xa.parse_tweet_ref("https://x.com/jack/status/20") == "20") check("ref: twitter.com url", xa.parse_tweet_ref("https://twitter.com/jack/statuses/20?s=1") == "20") +check("ref: article url", xa.parse_tweet_ref("https://x.com/jack/article/2072471529242407210") == "2072471529242407210") check("ref: junk -> None", xa.parse_tweet_ref("not a tweet") is None) check("ref: empty -> None", xa.parse_tweet_ref("") is None) @@ -162,6 +163,103 @@ check("home: who-to-follow user skipped", xa.parse_home_timeline( {"entryId": "cursor-top", "content": {"cursorType": "Top"}}, ]}]}}}}, limit=10) == []) +# ── find_tweet_result_in_detail / extract_article (X 長文記事) ── +# TweetDetail レスポンスの実構造を模した fixture (2026-07 実データの縮約) +article_result = { + "rest_id": "500", + "legacy": {"full_text": "https://t.co/xxxx", "created_at": "Wed Oct 10 20:19:24 +0000 2018", + "favorite_count": 1, "retweet_count": 0, "reply_count": 0}, + "article": {"article_results": {"result": { + "rest_id": "499", + "title": "記事タイトル", + "preview_text": "プレビュー文", + "plain_text": "本文です。" * 10, + "metadata": {"first_published_at_secs": 1782950691}, + "cover_media": {"media_info": {"original_img_url": "https://pbs.twimg.com/media/COVER.jpg"}}, + }}}, +} +detail_payload = {"data": {"threaded_conversation_with_injections_v2": {"instructions": [ + {"type": "TimelineAddEntries", "entries": [ + {"entryId": "tweet-1", "content": {"itemContent": {"tweet_results": {"result": {"rest_id": "1", "legacy": {}}}}}}, + {"entryId": "tweet-500", "content": {"itemContent": {"tweet_results": {"result": article_result}}}}, + ]}, +]}}} +found = xa.find_tweet_result_in_detail(detail_payload, "500") +check("detail: focal tweet found", found is not None and found.get("rest_id") == "500") +check("detail: missing id -> None", xa.find_tweet_result_in_detail(detail_payload, "999") is None) +check("detail: empty payload -> None", xa.find_tweet_result_in_detail({}, "500") is None) + +# visibility wrapper 越しでも見つかる +wrapped_payload = {"data": {"entries": [{"tweet_results": {"result": { + "__typename": "TweetWithVisibilityResults", "tweet": article_result}}}]} +} +found_w = xa.find_tweet_result_in_detail(wrapped_payload, "500") +check("detail: visibility wrapper unwrapped", found_w is not None and found_w.get("rest_id") == "500") + +art = xa.extract_article(article_result) +check("article: title", art["title"] == "記事タイトル") +check("article: previewText", art["previewText"] == "プレビュー文") +check("article: plainText", art["plainText"] == "本文です。" * 10) +check("article: publishedAtISO", art["publishedAtISO"] == "2026-07-02T00:04:51+00:00") +check("article: coverImageUrl", art["coverImageUrl"] == "https://pbs.twimg.com/media/COVER.jpg") +check("article: not truncated", "plainTextTruncated" not in art) + +# cap: full_text=False では ARTICLE_TEXT_CAP で切る +long_art = {"article": {"article_results": {"result": { + "title": "t", "plain_text": "あ" * (xa.ARTICLE_TEXT_CAP + 100)}}}} +capped = xa.extract_article(long_art) +check("article: capped length", len(capped["plainText"]) == xa.ARTICLE_TEXT_CAP) +check("article: truncated flag", capped.get("plainTextTruncated") is True) +full = xa.extract_article(long_art, full_text=True) +check("article: full_text lifts cap", len(full["plainText"]) == xa.ARTICLE_TEXT_CAP + 100) +check("article: full not truncated", "plainTextTruncated" not in full) + +# 記事なしツイート → None / 壊れた形 → None +check("article: non-article -> None", xa.extract_article(result_legacy) is None) +check("article: junk -> None", xa.extract_article({"article": {"article_results": {"result": "?"}}}) is None) +check("article: non-dict -> None", xa.extract_article(None) is None) + +# plain_text 無し (fieldToggles 未対応時) でも title/preview は返す +no_body = {"article": {"article_results": {"result": {"title": "t2", "preview_text": "p2"}}}} +nb = xa.extract_article(no_body) +check("article: no body still returns meta", nb["title"] == "t2" and "plainText" not in nb) + +# ── extract_article_media (記事内の埋め込み画像・動画) ── +img_entity = {"media_id": "111", "media_info": { + "__typename": "ApiImage", + "original_img_url": "https://pbs.twimg.com/media/IMG1.jpg", + "original_img_width": 1983, "original_img_height": 793}} +vid_entity = {"media_id": "222", "media_info": { + "__typename": "ApiVideo", + "duration_millis": 7658, + "preview_image": {"original_img_url": "https://pbs.twimg.com/amplify_video_thumb/222/img/P.jpg"}, + "variants": [ + {"bit_rate": 2176000, "content_type": "video/mp4", "url": "https://video.twimg.com/a/720.mp4"}, + {"content_type": "application/x-mpegURL", "url": "https://video.twimg.com/a/pl.m3u8"}, + ]}} +gif_entity = {"media_id": "333", "media_info": { + "__typename": "ApiGif", + "preview_image": {"original_img_url": "https://pbs.twimg.com/tweet_video_thumb/G.jpg"}, + "variants": [{"content_type": "video/mp4", "url": "https://video.twimg.com/tweet_video/g.mp4"}]}} +unknown_entity = {"media_id": "444", "media_info": {"__typename": "ApiAudioSpace"}} +art_with_media = {"article": {"article_results": {"result": { + "title": "t", "media_entities": [img_entity, vid_entity, gif_entity, unknown_entity]}}}} +am = xa.extract_article_media(art_with_media) +check("amedia: count (unknown skipped)", len(am) == 3) +check("amedia: photo", am[0] == {"type": "photo", "url": "https://pbs.twimg.com/media/IMG1.jpg"}) +check("amedia: video poster", am[1]["type"] == "video" and am[1]["url"].endswith("P.jpg")) +check("amedia: video variant mapped", am[1]["variants"][0] == + {"url": "https://video.twimg.com/a/720.mp4", "bitrate": 2176000, "contentType": "video/mp4"}) +check("amedia: m3u8 variant kept", am[1]["variants"][1]["contentType"] == "application/x-mpegURL") +check("amedia: gif -> animated_gif", am[2]["type"] == "animated_gif" and am[2]["variants"][0]["url"].endswith("g.mp4")) +check("amedia: non-article -> []", xa.extract_article_media(result_legacy) == []) +check("amedia: no media_entities -> []", xa.extract_article_media(no_body) == []) +check("amedia: non-dict -> []", xa.extract_article_media(None) == []) + +# 件数 cap: ARTICLE_MEDIA_CAP を超えたら切る +many = {"article": {"article_results": {"result": {"media_entities": [img_entity] * (xa.ARTICLE_MEDIA_CAP + 5)}}}} +check("amedia: capped", len(xa.extract_article_media(many)) == xa.ARTICLE_MEDIA_CAP) + # ── emit (optional, needs PyYAML) ── try: import io diff --git a/scripts/x-adapter/x_adapter.py b/scripts/x-adapter/x_adapter.py index 3ffbff1..e69a268 100644 --- a/scripts/x-adapter/x_adapter.py +++ b/scripts/x-adapter/x_adapter.py @@ -27,6 +27,8 @@ - id, text, author{name,screenName,profileImageUrl,id}, metrics{likes,retweets,replies,views}, createdAtISO, isRetweet, media[]{type, url, variants[]{url,bitrate,contentType}} + article{title, previewText, plainText, ...} # X 長文記事のみ (tweet サブコマンド) + # 長文記事の埋め込み画像・動画は media[] に合流する (自動 DL 対象) 注意: twscrape / twikit / httpx は遅延 import (関数内)。pure なマッピング関数だけ なら依存なしで import でき、test_x_adapter.py がネットワーク・依存なしで回る。 @@ -39,7 +41,7 @@ import re import sys from email.utils import parsedate_to_datetime -VERSION = "x-adapter 1.0.0 (twscrape backend)" +VERSION = "x-adapter 1.2.0 (twscrape backend)" # twitter-cli の YAML スキーマバージョン (x.ts は使わないが互換のため踏襲) SCHEMA_VERSION = "1" @@ -48,13 +50,17 @@ SCHEMA_VERSION = "1" # ── pure helpers (依存なし・テスト対象) ────────────────────────────── def parse_tweet_ref(raw: str) -> str | None: - """tweet ID もしくは status URL から数値 ID を取り出す。""" + """tweet ID もしくは status / article URL から数値 ID を取り出す。 + + X の長文記事 (X Articles) は `x.com/{user}/article/{id}` 形式で共有されるが、 + その id はツイート ID と同じ空間なので TweetDetail でそのまま引ける。 + """ s = (raw or "").strip() if not s: return None if s.isdigit(): return s - m = re.search(r"(?:x\.com|twitter\.com)/[^/]+/status(?:es)?/(\d+)", s) + m = re.search(r"(?:x\.com|twitter\.com)/[^/]+/(?:status(?:es)?|article)/(\d+)", s) return m.group(1) if m else None @@ -219,6 +225,113 @@ def parse_graphql_tweet_result(result: dict) -> dict | None: } +# ── X 長文記事 (X Articles) の抽出 ────────────────────────────────── +# +# 長文記事ポストの legacy.full_text は記事への t.co リンク 1 本だけで本文を含まない。 +# 本文は TweetDetail レスポンスの tweet result 直下 `article.article_results.result` +# に入る (plain_text は fieldToggles.withArticlePlainText: true を付けた時のみ)。 + +# plain_text の既定上限 (文字数)。LLM コンテキスト保護。--full-text で解除。 +ARTICLE_TEXT_CAP = 12000 + +# 記事内埋め込みメディアの件数上限。x.ts の downloadTweetMedia は media[] を +# 全件 DL する (件数制限なし・サイズ上限のみ) ので、画像だらけの記事で +# ダウンロードが暴発しないようアダプタ側で頭を抑える。 +ARTICLE_MEDIA_CAP = 20 + + +def find_tweet_result_in_detail(payload, tid: str) -> dict | None: + """TweetDetail レスポンス全体から rest_id == tid の tweet_results.result を探す。 + + conversation スレッド内の別ツイート (リプライ等) も同じ形で並ぶので、 + rest_id の一致で focal tweet を特定する。visibility wrapper は剥がして返す。 + """ + if isinstance(payload, dict): + tr = payload.get("tweet_results") + if isinstance(tr, dict): + r = tr.get("result") + if isinstance(r, dict): + if r.get("__typename") == "TweetWithVisibilityResults": + r = r.get("tweet") if isinstance(r.get("tweet"), dict) else r + if isinstance(r, dict) and str(r.get("rest_id") or "") == str(tid): + return r + for v in payload.values(): + hit = find_tweet_result_in_detail(v, tid) + if hit is not None: + return hit + elif isinstance(payload, list): + for v in payload: + hit = find_tweet_result_in_detail(v, tid) + if hit is not None: + return hit + return None + + +def extract_article(result, full_text: bool = False) -> dict | None: + """tweet result の article.article_results.result から記事情報を YAML dict に。 + + 記事でないツイート・形が崩れている場合は None (呼び出し側は無視して従来出力)。 + """ + art = _dig(result if isinstance(result, dict) else {}, "article", "article_results", "result") + if not isinstance(art, dict): + return None + out: dict = { + "title": art.get("title") or "", + "previewText": art.get("preview_text") or "", + } + plain = art.get("plain_text") + if isinstance(plain, str) and plain: + if not full_text and len(plain) > ARTICLE_TEXT_CAP: + out["plainText"] = plain[:ARTICLE_TEXT_CAP] + out["plainTextTruncated"] = True + else: + out["plainText"] = plain + pub = _dig(art, "metadata", "first_published_at_secs") + if isinstance(pub, (int, float)) and pub > 0: + from datetime import datetime, timezone + out["publishedAtISO"] = datetime.fromtimestamp(int(pub), tz=timezone.utc).isoformat() + cover = _dig(art, "cover_media", "media_info", "original_img_url") + if cover: + out["coverImageUrl"] = cover + return out + + +def extract_article_media(result) -> list: + """記事内の埋め込みメディア (media_entities) を row の media[] と同じ形に変換。 + + media_entities は fieldToggles.withArticleRichContentState: true の時のみ返る。 + row["media"] に合流させることで x.ts の downloadTweetMedia がそのまま + 自動 DL し localPath を付ける (記事ポストの tweet-level media は常に空なので + 衝突しない)。未知の __typename (音声スペース等) は黙ってスキップ。 + """ + art = _dig(result if isinstance(result, dict) else {}, "article", "article_results", "result") + if not isinstance(art, dict): + return [] + out: list = [] + for entity in art.get("media_entities") or []: + if len(out) >= ARTICLE_MEDIA_CAP: + break + info = _dig(_as_dict(entity), "media_info") or {} + tname = info.get("__typename") + if tname == "ApiImage": + url = info.get("original_img_url") + if url: + out.append({"type": "photo", "url": url}) + elif tname in ("ApiVideo", "ApiGif"): + variants = [ + {"url": v.get("url"), "bitrate": v.get("bit_rate") or 0, + "contentType": v.get("content_type") or ""} + for v in (info.get("variants") or []) + if isinstance(v, dict) and v.get("url") + ] + out.append({ + "type": "video" if tname == "ApiVideo" else "animated_gif", + "url": _dig(info, "preview_image", "original_img_url") or "", + "variants": variants, + }) + return out + + def _collect_tweet_results(node, out: list, limit: int) -> None: """entry / module item から tweet_results.result を拾って out に積む。 @@ -405,17 +518,77 @@ async def cmd_user_posts(args) -> None: shutil.rmtree(tmpdir, ignore_errors=True) +async def _tweet_detail_raw_with_article(api, tid: str): + """TweetDetail を fieldToggles.withArticlePlainText 付きで直接叩く。 + + twscrape の tweet_details_raw は fieldToggles を渡せない (_gql_item が + variables/features のみ) ので、同じ op / variables / QueueClient を使い + パラメータだけ足す。X 長文記事の本文 (plain_text) は withArticlePlainText、 + 記事内埋め込みメディア (media_entities) は withArticleRichContentState が + 無いとレスポンスに含まれない (後者は content_state も同乗するが emit しない)。 + """ + from twscrape.api import GQL_URL, OP_TweetDetail + from twscrape.api import GQL_FEATURES # type: ignore[attr-defined] + from twscrape.queue_client import QueueClient + from twscrape.utils import encode_params + + kv = { + "focalTweetId": str(tid), + "with_rux_injections": True, + "includePromotedContent": True, + "withCommunity": True, + "withQuickPromoteEligibilityTweetFields": True, + "withBirdwatchNotes": True, + "withVoice": True, + "withV2Timeline": True, + } + params = { + "variables": kv, + "features": GQL_FEATURES, + "fieldToggles": {"withArticlePlainText": True, "withArticleRichContentState": True}, + } + queue = OP_TweetDetail.split("/")[-1] + async with QueueClient(api.pool, queue, False) as client: + return await client.get(f"{GQL_URL}/{OP_TweetDetail}", params=encode_params(params)) + + async def cmd_tweet(args) -> None: tid = parse_tweet_ref(args.tweet) if not tid: fail(f"x-adapter: could not parse tweet id from '{args.tweet}'") api, tmpdir, shutil = await _make_api() try: - t = await api.tweet_details(int(tid)) + # 記事本文トグル付きの直接呼び出しを優先。twscrape 内部 API の変化などで + # 壊れたら従来の tweet_details に落として XPostDetail 自体は生かす。 + t = None + article = None + article_media: list = [] + try: + from twscrape.models import parse_tweet + rep = await _tweet_detail_raw_with_article(api, tid) + if rep is not None: + t = parse_tweet(rep, int(tid)) + result = find_tweet_result_in_detail(rep.json(), tid) + article = extract_article(result, full_text=bool(args.full_text)) + article_media = extract_article_media(result) + except SystemExit: + raise + except Exception as e: + print(f"x-adapter: article-aware fetch failed, falling back: {type(e).__name__}: {e}", + file=sys.stderr) + if not t: + t = await api.tweet_details(int(tid)) if not t: await _ensure_alive(api) # 認証切れなら nonzero fail(f"x-adapter: tweet {tid} not found or inaccessible") - emit([twscrape_tweet_to_dict(t)]) + row = twscrape_tweet_to_dict(t) + if article: + row["article"] = article + if article_media: + # 記事ポストの tweet-level media は空なので合流しても衝突しない。 + # x.ts の downloadTweetMedia がここを見て自動 DL → localPath 付与する。 + row["media"] = (row.get("media") or []) + article_media + emit([row]) finally: shutil.rmtree(tmpdir, ignore_errors=True) diff --git a/src/bridge/__snapshots__/server-route-order.test.ts.snap b/src/bridge/__snapshots__/server-route-order.test.ts.snap new file mode 100644 index 0000000..e3d2c34 --- /dev/null +++ b/src/bridge/__snapshots__/server-route-order.test.ts.snap @@ -0,0 +1,239 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createCoreServer route registration order > pins the auth-active + ConfigManager boot layer order 1`] = ` +[ + "USE name=query path=^\\/?(?=\\/|$)", + "USE name=expressInit path=^\\/?(?=\\/|$)", + "USE name= path=^\\/?(?=\\/|$)", + "ROUTE get /metrics", + "USE name=jsonParser path=^\\/api\\/config\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/pieces\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/local\\/memory\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/local\\/reflection\\/?(?=\\/|$)", + "USE name=session path=^\\/?(?=\\/|$)", + "USE name=initialize path=^\\/?(?=\\/|$)", + "USE name=authenticate path=^\\/?(?=\\/|$)", + "USE name=router path=^\\/auth\\/?(?=\\/|$)", + "ROUTE get /api/auth/me", + "ROUTE post /api/auth/password", + "USE name=requireAuth path=^\\/api\\/local\\/?(?=\\/|$)", + "USE name=requireAuth path=^\\/api\\/repos\\/?(?=\\/|$)", + "USE name=requireAuth path=^\\/api\\/workers\\/?(?=\\/|$)", + "USE name=requireAdmin path=^\\/api\\/config\\/?(?=\\/|$)", + "USE name=requireAuth path=^\\/api\\/pieces\\/?(?=\\/|$)", + "USE name=requireAuth path=^\\/api\\/usage\\/?(?=\\/|$)", + "USE name=requireAuth path=^\\/api\\/calendar\\/?(?=\\/|$)", + "USE name=requireAuth path=^\\/api\\/scheduled-tasks\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/admin\\/?(?=\\/|$)", + "ROUTE get /api/admin/users", + "ROUTE post /api/admin/users", + "ROUTE post /api/admin/users/:id/password", + "ROUTE patch /api/admin/users/:id", + "ROUTE delete /api/admin/users/:id", + "ROUTE get /api/admin/orgs", + "ROUTE post /api/admin/orgs", + "ROUTE patch /api/admin/orgs/:id", + "ROUTE delete /api/admin/orgs/:id", + "ROUTE post /api/admin/orgs/:id/members", + "ROUTE delete /api/admin/orgs/:id/members/:userId", + "USE name=jsonParser path=^\\/api\\/admin\\/gateway\\/keys\\/?(?=\\/|$)", + "USE name=requireAdmin path=^\\/api\\/admin\\/gateway\\/keys\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/admin\\/gateway\\/keys\\/?(?=\\/|$)", + "ROUTE get /api/users/me/orgs", + "ROUTE patch /api/users/me/preferences", + "ROUTE get /api/users/pickable", + "ROUTE get /api/shared/:token", + "ROUTE get /api/shared/:token/comments", + "ROUTE get /api/shared/:token/files", + "ROUTE get /api/shared/:token/files/content", + "ROUTE get /api/shared/:token/files/raw", + "ROUTE get /api/shared/:token/subtasks/activities", + "ROUTE get /api/shared/:token/subtasks/:jobId/activity", + "ROUTE get /api/shared/:token/subtasks/:jobId/files", + "ROUTE get /api/shared/:token/subtasks/:jobId/files/*", + "ROUTE post /api/local/tasks/:taskId/share", + "ROUTE delete /api/local/tasks/:taskId/share", + "ROUTE get /api/app-share/:token", + "ROUTE get /api/app-share/:token/files/content", + "ROUTE get /api/app-share/:token/files/raw", + "ROUTE get /api/app-share/:token/files/list", + "ROUTE get /", + "ROUTE get /api/version", + "ROUTE get /api/repos", + "ROUTE get /api/local/tasks", + "ROUTE post /api/local/tasks", + "ROUTE get /api/local/tasks/:taskId", + "ROUTE patch /api/local/tasks/:taskId", + "ROUTE delete /api/local/tasks/:taskId", + "ROUTE get /api/local/tasks/:taskId/tool-requests", + "ROUTE post /api/local/tasks/:taskId/tool-requests/:reqId/decide", + "ROUTE put /api/local/tasks/:taskId/feedback", + "ROUTE put /api/local/tasks/:taskId/mission", + "ROUTE get /api/local/tasks/:taskId/comments", + "ROUTE post /api/local/tasks/:taskId/comments", + "ROUTE post /api/local/tasks/:taskId/cancel", + "ROUTE post /api/local/tasks/:taskId/continue", + "ROUTE post /api/local/tasks/:taskId/regenerate-title", + "ROUTE post /api/local/tasks/evaluate-prompt", + "ROUTE get /api/local/tasks/:taskId/stream", + "ROUTE get /api/local/tasks/:taskId/files", + "ROUTE get /api/local/tasks/:taskId/files/content", + "ROUTE get /api/local/tasks/:taskId/files/provenance", + "ROUTE get /api/local/tasks/:taskId/files/raw", + "ROUTE get /api/local/tasks/:taskId/files/office-preview", + "ROUTE put /api/local/tasks/:taskId/files/content", + "ROUTE post /api/local/tasks/:taskId/files/upload", + "ROUTE post /api/local/tasks/:taskId/files/delete", + "ROUTE post /api/local/tasks/:taskId/files/download-zip", + "USE name=router path=^\\/api\\/local\\/tasks\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/tasks\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/usage\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/calendar\\/?(?=\\/|$)", + "ROUTE get /api/local/tasks/:id/subtasks/:jobId/files", + "ROUTE get /api/local/tasks/:id/subtasks/:jobId/files/*", + "ROUTE get /api/jobs/:jobId", + "ROUTE get /api/setup/status", + "ROUTE post /api/setup/probe", + "ROUTE post /api/setup/apply", + "ROUTE get /api/config", + "ROUTE put /api/config", + "ROUTE post /api/config/reload", + "ROUTE get /api/workers", + "ROUTE get /api/workers/:workerId/backends", + "ROUTE get /api/branding", + "USE name=serveStatic path=^\\/branding\\/?(?=\\/|$)", + "ROUTE post /api/branding/upload", + "ROUTE delete /api/branding/upload", + "ROUTE get /api/tools", + "ROUTE get /api/notifications/vapid-public-key", + "ROUTE get /api/notifications/subscriptions", + "ROUTE post /api/notifications/subscriptions", + "ROUTE delete /api/notifications/subscriptions/:id", + "ROUTE get /api/notifications/preferences", + "ROUTE put /api/notifications/preferences", + "ROUTE post /api/notifications/preferences/migrate-from-localstorage", + "ROUTE post /api/notifications/test", + "USE name=jsonParser path=^\\/api\\/local\\/browser\\/sessions\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/browser\\/sessions\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/browser-sessions\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/browser-sessions\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/users\\/me\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/memory\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/reflection\\/?(?=\\/|$)", + "USE name= path=^\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/spaces\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/local\\/dashboard\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/dashboard\\/?(?=\\/|$)", + "USE name= path=^\\/?(?=\\/|$)", + "USE name= path=^\\/?(?=\\/|$)", + "ROUTE get /health", + "USE name=requireAdmin path=^\\/api\\/admin\\/gateway\\/status\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/admin\\/gateway\\/status\\/?(?=\\/|$)", + "USE name=router path=^\\/novnc\\/?(?=\\/|$)", +] +`; + +exports[`createCoreServer route registration order > pins the no-auth minimal boot layer order 1`] = ` +[ + "USE name=query path=^\\/?(?=\\/|$)", + "USE name=expressInit path=^\\/?(?=\\/|$)", + "USE name= path=^\\/?(?=\\/|$)", + "ROUTE get /metrics", + "USE name=jsonParser path=^\\/api\\/config\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/pieces\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/local\\/memory\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/local\\/reflection\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/admin\\/?(?=\\/|$)", + "ROUTE get /api/admin/users", + "ROUTE post /api/admin/users", + "ROUTE post /api/admin/users/:id/password", + "ROUTE patch /api/admin/users/:id", + "ROUTE delete /api/admin/users/:id", + "ROUTE get /api/admin/orgs", + "ROUTE post /api/admin/orgs", + "ROUTE patch /api/admin/orgs/:id", + "ROUTE delete /api/admin/orgs/:id", + "ROUTE post /api/admin/orgs/:id/members", + "ROUTE delete /api/admin/orgs/:id/members/:userId", + "ROUTE get /api/users/me/orgs", + "ROUTE patch /api/users/me/preferences", + "ROUTE get /api/users/pickable", + "ROUTE get /api/shared/:token", + "ROUTE get /api/shared/:token/comments", + "ROUTE get /api/shared/:token/files", + "ROUTE get /api/shared/:token/files/content", + "ROUTE get /api/shared/:token/files/raw", + "ROUTE get /api/shared/:token/subtasks/activities", + "ROUTE get /api/shared/:token/subtasks/:jobId/activity", + "ROUTE get /api/shared/:token/subtasks/:jobId/files", + "ROUTE get /api/shared/:token/subtasks/:jobId/files/*", + "ROUTE post /api/local/tasks/:taskId/share", + "ROUTE delete /api/local/tasks/:taskId/share", + "ROUTE get /api/app-share/:token", + "ROUTE get /api/app-share/:token/files/content", + "ROUTE get /api/app-share/:token/files/raw", + "ROUTE get /api/app-share/:token/files/list", + "ROUTE get /", + "ROUTE get /api/version", + "ROUTE get /api/repos", + "ROUTE get /api/local/tasks", + "ROUTE post /api/local/tasks", + "ROUTE get /api/local/tasks/:taskId", + "ROUTE patch /api/local/tasks/:taskId", + "ROUTE delete /api/local/tasks/:taskId", + "ROUTE get /api/local/tasks/:taskId/tool-requests", + "ROUTE post /api/local/tasks/:taskId/tool-requests/:reqId/decide", + "ROUTE put /api/local/tasks/:taskId/feedback", + "ROUTE put /api/local/tasks/:taskId/mission", + "ROUTE get /api/local/tasks/:taskId/comments", + "ROUTE post /api/local/tasks/:taskId/comments", + "ROUTE post /api/local/tasks/:taskId/cancel", + "ROUTE post /api/local/tasks/:taskId/continue", + "ROUTE post /api/local/tasks/:taskId/regenerate-title", + "ROUTE post /api/local/tasks/evaluate-prompt", + "ROUTE get /api/local/tasks/:taskId/stream", + "ROUTE get /api/local/tasks/:taskId/files", + "ROUTE get /api/local/tasks/:taskId/files/content", + "ROUTE get /api/local/tasks/:taskId/files/provenance", + "ROUTE get /api/local/tasks/:taskId/files/raw", + "ROUTE get /api/local/tasks/:taskId/files/office-preview", + "ROUTE put /api/local/tasks/:taskId/files/content", + "ROUTE post /api/local/tasks/:taskId/files/upload", + "ROUTE post /api/local/tasks/:taskId/files/delete", + "ROUTE post /api/local/tasks/:taskId/files/download-zip", + "USE name=router path=^\\/api\\/local\\/tasks\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/tasks\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/usage\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/calendar\\/?(?=\\/|$)", + "ROUTE get /api/local/tasks/:id/subtasks/:jobId/files", + "ROUTE get /api/local/tasks/:id/subtasks/:jobId/files/*", + "ROUTE get /api/jobs/:jobId", + "ROUTE get /api/branding", + "USE name=serveStatic path=^\\/branding\\/?(?=\\/|$)", + "ROUTE post /api/branding/upload", + "ROUTE delete /api/branding/upload", + "ROUTE get /api/tools", + "ROUTE get /api/notifications/vapid-public-key", + "ROUTE get /api/notifications/subscriptions", + "ROUTE post /api/notifications/subscriptions", + "ROUTE delete /api/notifications/subscriptions/:id", + "ROUTE get /api/notifications/preferences", + "ROUTE put /api/notifications/preferences", + "ROUTE post /api/notifications/preferences/migrate-from-localstorage", + "ROUTE post /api/notifications/test", + "USE name=jsonParser path=^\\/api\\/local\\/browser\\/sessions\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/browser\\/sessions\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/browser-sessions\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/browser-sessions\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/users\\/me\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/memory\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/reflection\\/?(?=\\/|$)", + "USE name= path=^\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/spaces\\/?(?=\\/|$)", + "USE name=jsonParser path=^\\/api\\/local\\/dashboard\\/?(?=\\/|$)", + "USE name=router path=^\\/api\\/local\\/dashboard\\/?(?=\\/|$)", + "ROUTE get /health", + "USE name=router path=^\\/api\\/admin\\/gateway\\/status\\/?(?=\\/|$)", + "USE name=router path=^\\/novnc\\/?(?=\\/|$)", +] +`; diff --git a/src/bridge/a2a-subsystem.ts b/src/bridge/a2a-subsystem.ts new file mode 100644 index 0000000..f9df483 --- /dev/null +++ b/src/bridge/a2a-subsystem.ts @@ -0,0 +1,74 @@ +import express from 'express'; +import { readFileSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { Repository } from '../db/repository.js'; +import { logger } from '../logger.js'; +import { loadConfig } from '../config.js'; +import { requireAdmin, DEFAULT_SESSION_SECRET_PATH } from './auth.js'; +import { registerShutdownHook } from './shutdown.js'; +import { createA2aOidcProvider, mountA2aOidc } from './a2a/oidc-provider.js'; +import { createA2aClientsAdminRouter } from './a2a/a2a-clients-admin-api.js'; +import { createA2aRouter, createSpaceA2aAdminRouter } from './a2a/a2a-api.js'; +import { mountA2aRequestHandler } from './a2a/request-handler.js'; +import { A2aTaskReconciler } from './a2a/reconciler.js'; +import { createUserDelegationsRouter, createAdminDelegationsRouter } from './a2a/delegations-api.js'; + +/** + * A2A OAuth2 Authorization Server + Agent Card + JSON-RPC + admin routers, + * extracted verbatim from createCoreServer (bridge/server.ts). No-op unless + * `a2a.enabled: true` in config.yaml. Registration order relative to the + * other /api/local mounts is preserved by the call site in server.ts. + */ +export function setupA2aSubsystem( + app: express.Express, + deps: { repo: Repository; authActive: boolean }, +): void { + const { repo, authActive } = deps; + + // --- A2A OAuth2 Authorization Server --- + const a2aCfg = (loadConfig() as any).a2a ?? {}; + if (a2aCfg.enabled) { + if (!authActive) { + logger.warn('[a2a-oidc] a2a.enabled but auth is not active; consent flow requires login and will reject all requests'); + } + try { + const sessionSecretPath = loadConfig()?.secrets?.sessionSecretPath ?? DEFAULT_SESSION_SECRET_PATH; + const cookieKeys = [readFileSync(sessionSecretPath, 'utf-8').trim()]; + const secretsDir = dirname(resolve(sessionSecretPath)); + const provider = createA2aOidcProvider({ + repo, + secretsDir, + issuer: a2aCfg.issuer, + resourceAudience: a2aCfg.resourceAudience, + cookieKeys, + }); + mountA2aOidc(app, provider, { repo, resourceAudience: a2aCfg.resourceAudience }); + app.use('/api/admin/a2a/clients', express.json(), requireAdmin, createA2aClientsAdminRouter(repo, authActive)); + // Agent Card ルータ (base card = public, extended card = self-authenticates via Bearer) + // baseUrl は resourceAudience の origin から導く(例: https://maestro.example.com/a2a → https://maestro.example.com) + // version は '1.0.0' 固定(生成済みバージョンファイルが存在しないため定数を使用) + const a2aBaseUrl = (() => { + try { return new URL(String(a2aCfg.resourceAudience)).origin; } catch { return String(a2aCfg.issuer); } + })(); + app.use(createA2aRouter({ provider, repo, baseUrl: a2aBaseUrl, issuer: String(a2aCfg.issuer), version: '1.0.0' })); + // JSON-RPC エンドポイント /a2a(SDK DefaultRequestHandler 経由) + // /a2a は /api 配下でないため requireAuth に掛からない。認証は UserBuilder が担う。 + mountA2aRequestHandler(app, { provider, repo, baseUrl: a2aBaseUrl, issuer: String(a2aCfg.issuer), version: '1.0.0', limits: a2aCfg.limits }); + // bridge は単一プロセス前提。複数プロセスが同一 DB を共有する構成では + // lease が必要になる(将来課題)。 + const a2aReconciler = new A2aTaskReconciler({ repo }); + a2aReconciler.start(); + registerShutdownHook('a2a-reconciler', async () => { a2aReconciler.stop(); }); + logger.info('[a2a] task reconciler started'); + // スペース A2A スキル許可リスト管理 API (/api/local は requireAuth 配下) + app.use('/api/local/spaces', createSpaceA2aAdminRouter(repo, authActive)); + // A2A 委任一覧・取消 API + // /api/local は server.ts 側で requireAuth 済みのため追加ゲート不要 + app.use('/api/local/a2a/delegations', express.json(), createUserDelegationsRouter(repo, authActive)); + app.use('/api/admin/a2a/delegations', express.json(), requireAdmin, createAdminDelegationsRouter(repo, authActive)); + logger.info('[a2a-oidc] a2a authorization server enabled'); + } catch (err) { + logger.warn(`[a2a-oidc] failed to mount a2a authorization server: ${err}`); + } + } +} diff --git a/src/bridge/a2a/a2a-exec-e2e.test.ts b/src/bridge/a2a/a2a-exec-e2e.test.ts index e9ffd4f..4b22371 100644 --- a/src/bridge/a2a/a2a-exec-e2e.test.ts +++ b/src/bridge/a2a/a2a-exec-e2e.test.ts @@ -262,15 +262,17 @@ describe('A2A exec e2e (real token → message/send → job → artifact + negat expect(taskRow.piece_name).toBe('research'); }, 30_000); - it('no bearer → JSON-RPC error, no job created', async () => { + it('no bearer → terminal failed Task, no job created', async () => { const before = jobCountForUser(); const res = await sendMessage(messageSendEnvelope('do research')); - expect(res.status).toBe(200); // jsonRpcHandler は HTTP 200 + JSON-RPC error - const json = await res.json() as { jsonrpc: string; error?: { code: number; message: string }; result?: unknown }; + expect(res.status).toBe(200); + const json = await res.json() as { jsonrpc: string; error?: unknown; result?: { kind?: string; status?: { state: string } } }; expect(json.jsonrpc).toBe('2.0'); - // 未認証 → executor は principal 無しで failed publish → SDK は finalResult 無しで error 化。 - expect(json.error).toBeDefined(); - expect(json.result).toBeUndefined(); + // 未認証 → executor が principal 無しで完全な terminal Task(state=failed) を publish する。 + // (2C-3 Task 4 で SDK ResultManager 互換に修正: 旧挙動は bare status-update が drop され error 化していた) + expect(json.error).toBeUndefined(); + expect(json.result?.kind).toBe('task'); + expect(json.result?.status?.state).toBe('failed'); // ジョブは作られない(最重要のセキュリティ不変条件)。 expect(jobCountForUser()).toBe(before); }, 30_000); @@ -284,12 +286,206 @@ describe('A2A exec e2e (real token → message/send → job → artifact + negat { authorization: `Bearer ${token}` }, ); expect(res.status).toBe(200); - const json = await res.json() as { jsonrpc: string; error?: unknown; result?: { status?: { state: string } } }; + const json = await res.json() as { jsonrpc: string; error?: unknown; result?: { kind?: string; status?: { state: string } } }; expect(json.jsonrpc).toBe('2.0'); - // selectPieceForMessage が null → executor は createLocalTask/createJob 前に failed → error 化。 - expect(json.error).toBeDefined(); - expect(json.result).toBeUndefined(); + // selectPieceForMessage が null → executor は createLocalTask/createJob 前に完全な terminal Task(state=failed) を publish。 + expect(json.error).toBeUndefined(); + expect(json.result?.kind).toBe('task'); + expect(json.result?.status?.state).toBe('failed'); // 委任外スキルでジョブは作られない(fail-closed)。 expect(jobCountForUser()).toBe(before); }, 30_000); }); + +/** + * Task 9 — ガバナンス E2E: 同時実行数上限超過 → rejected Task(rev.1 Critical の再発防止ゲート)。 + * + * maxConcurrentTasks=1 で mount し、該当 grant の非終端 a2a_task を DB に 1 件 seed した状態で + * message/send を叩く。executor の concurrency チェック(tryReserveConcurrency)が + * Math.max(dbCount=1, reserved=0) >= cap=1 を検出し、JSON-RPC result として + * kind='task' / state='rejected' の完全な Task を返す。ジョブは作られない。 + */ +describe('A2A exec e2e: governance — concurrency cap → rejected Task', () => { + let server: http.Server; + let base: string; + let repo: Repository; + + const clientId = 'a2a_gov_e2e'; + const redirectUri = 'https://client.test/cb'; + const userId = 'user-gov'; + const spaceId = 'space-gov'; + + function jobCountForUser(): number { + const row = (repo as any).db + .prepare("SELECT COUNT(*) AS n FROM jobs WHERE owner_id = ?") + .get(userId) as { n: number }; + return row.n; + } + + function sendMessage(body: unknown, headers: Record = {}) { + return fetch(`${base}/a2a`, { + method: 'POST', + headers: { 'content-type': 'application/json', ...headers }, + body: JSON.stringify(body), + }); + } + + async function obtainToken(selectedSpaces: string, selectedSkills: string): Promise { + const audience = `${base}/a2a`; + const client = new Client(); + const verifier = b64url(randomBytes(32)); + const challenge = b64url(createHash('sha256').update(verifier).digest()); + + const authUrl = `${base}/oidc/auth?` + new URLSearchParams({ + client_id: clientId, response_type: 'code', redirect_uri: redirectUri, + scope: 'openid a2a.read', resource: audience, + code_challenge: challenge, code_challenge_method: 'S256', state: 'xyz', + }); + const authRes = await client.get(authUrl); + expect([302, 303]).toContain(authRes.status); + let loc = authRes.headers.get('location')!; + + for (let i = 0; i < 5; i++) { + if (loc.startsWith(redirectUri)) break; + expect(loc).toContain('/oidc/interaction/'); + const uid = new URL(loc, base).pathname.split('/').pop()!; + const confirmRes = await client.postForm(`${base}/oidc/interaction/${uid}/confirm`, { + selectedSpaces, selectedSkills, + }); + expect(confirmRes.status).toBe(200); + const { redirectTo } = await confirmRes.json() as { redirectTo: string }; + expect(redirectTo).toContain('/oidc/auth/'); + const resumeRes = await client.get(redirectTo); + expect([302, 303]).toContain(resumeRes.status); + loc = resumeRes.headers.get('location')!; + } + + expect(loc.startsWith(redirectUri)).toBe(true); + const code = new URL(loc).searchParams.get('code')!; + expect(code).toBeTruthy(); + + const tokenRes = await client.postForm(`${base}/oidc/token`, { + grant_type: 'authorization_code', + code, code_verifier: verifier, client_id: clientId, redirect_uri: redirectUri, + }); + expect(tokenRes.status).toBe(200); + const tokenJson = await tokenRes.json() as { access_token: string }; + expect(tokenJson.access_token).toBeTruthy(); + return tokenJson.access_token; + } + + beforeAll(async () => { + repo = new Repository(':memory:'); + + repo.createA2aClient({ + clientId, name: 'Gov E2E Client', redirectUris: [redirectUri], + secretHash: null, tokenEndpointAuthMethod: 'none', + agentCardUrl: null, issuer: null, keyFingerprint: null, status: 'active', createdBy: 'admin', + }); + + (repo as any).db.prepare( + "INSERT INTO users (id, email, name, avatar_url, role, status, created_at, updated_at) " + + "VALUES (?, ?, ?, NULL, 'user', 'active', '2026-01-01', '2026-01-01')", + ).run(userId, 'ugov@test', 'Gov User'); + + (repo as any).db.prepare( + "INSERT INTO spaces (id, title, kind, owner_id, visibility) VALUES (?, 'Gov Space', 'case', ?, 'private')", + ).run(spaceId, userId); + repo.setSpaceA2aSkills(spaceId, ['research']); + + const secretsDir = mkdtempSync(join(tmpdir(), 'a2a-gov-e2e-')); + const app = express(); + // 擬似ログイン: consent の req.user 用。/a2a は Bearer ベースなので無害。 + app.use((req, _res, next) => { (req as any).user = { id: userId, role: 'user' }; next(); }); + + server = http.createServer(app); + await new Promise(r => server.listen(0, '127.0.0.1', r)); + const port = (server.address() as any).port; + base = `http://127.0.0.1:${port}`; + const audience = `${base}/a2a`; + + const govProvider = createA2aOidcProvider({ + repo, secretsDir, + issuer: `${base}/oidc`, + resourceAudience: audience, + cookieKeys: ['gov-cookie-key'], + }); + (govProvider as any).proxy = false; + + mountA2aOidc(app, govProvider, { repo, resourceAudience: audience }); + // maxConcurrentTasks=1: 非終端タスクが 1 件あれば次のリクエストは rejected になる。 + mountA2aRequestHandler(app, { + provider: govProvider, repo, + baseUrl: base, + issuer: `${base}/oidc`, + version: '1.0.0-test', + limits: { maxConcurrentTasks: 1 }, + }); + }); + + afterAll(() => { server?.close(); }); + + it('concurrency cap exceeded → rejected Task (kind=task, state=rejected), no job created', async () => { + // Step 1: 実 OAuth フローでトークンを取得(delegation 行が DB に生成される)。 + const token = await obtainToken(spaceId, 'research'); + + // Step 2: 生成された delegation の grant_id を取得。 + const delRow = (repo as any).db + .prepare("SELECT grant_id FROM a2a_delegations WHERE user_id = ? ORDER BY rowid DESC LIMIT 1") + .get(userId) as { grant_id: string } | undefined; + expect(delRow?.grant_id).toBeTruthy(); + const grantId = delRow!.grant_id; + + // Step 3: 非終端 a2a_task を 1 件 seed → countNonTerminalA2aTasksByGrant が 1 を返す。 + // maxConcurrentTasks=1 なので Math.max(dbCount=1, reserved=0) >= 1 → 予約失敗。 + repo.saveA2aTask({ + id: randomUUID(), + contextId: null, + jobId: null, + localTaskId: null, + payload: { + id: randomUUID(), + kind: 'task', + contextId: 'ctx-blocker', + status: { state: 'working', timestamp: new Date().toISOString() }, + }, + grantId, + }); + + const before = jobCountForUser(); + + // Step 4: message/send — 同時実行数上限に到達済みなので rejected になるはず。 + const res = await sendMessage( + { + jsonrpc: '2.0', + id: 1, + method: 'message/send', + params: { + message: { + kind: 'message', + messageId: randomUUID(), + role: 'user', + parts: [{ kind: 'text', text: 'do research' }], + }, + }, + }, + { authorization: `Bearer ${token}` }, + ); + expect(res.status).toBe(200); + const json = await res.json() as { + jsonrpc: string; + error?: unknown; + result?: { kind?: string; status?: { state: string } }; + }; + + // Step 5: JSON-RPC error は返らない(SDK ResultManager 互換の完全な Task として返る)。 + // kind=task かつ state=rejected — これが rev.1 Critical が検出できなかったアサーション。 + expect(json.jsonrpc).toBe('2.0'); + expect(json.error).toBeUndefined(); + expect(json.result?.kind).toBe('task'); + expect(json.result?.status?.state).toBe('rejected'); + + // ジョブは作られない(最重要のセキュリティ不変条件)。 + expect(jobCountForUser()).toBe(before); + }, 30_000); +}); diff --git a/src/bridge/a2a/executor.test.ts b/src/bridge/a2a/executor.test.ts index 602333e..05f4fde 100644 --- a/src/bridge/a2a/executor.test.ts +++ b/src/bridge/a2a/executor.test.ts @@ -8,6 +8,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { describe, it, expect, vi } from 'vitest'; import { MaestroA2aExecutor } from './executor.js'; +import { A2aLimiter, DEFAULT_A2A_LIMITS } from './limiter.js'; // ── fake helpers ───────────────────────────────────────────────────────────── @@ -115,6 +116,7 @@ function makeFakeRepo(opts: { requestJobCancel: vi.fn().mockReturnValue(true), cancelA2aLinkedJob: vi.fn().mockReturnValue(true), addAuditLog: vi.fn(), + countNonTerminalA2aTasksByGrant: vi.fn().mockReturnValue(0), // Task 5: revocation recheck — return a live delegation by default so existing tests are unaffected getA2aDelegationByGrantId: vi.fn().mockReturnValue({ id: 'deleg-1', @@ -255,16 +257,20 @@ describe('MaestroA2aExecutor', () => { expect(eventBus.finishedCalled).toBe(true); expect(repo.createLocalTask).not.toHaveBeenCalled(); + // Task 4: pre-initial-Task denial must publish a full Task object (kind:'task') + // so the SDK ResultManager can save and return it. A bare status-update would be + // silently dropped, leaving a blocking message/send with no result. const failedEvent = eventBus.published.find( - (e: any) => e.kind === 'status-update' && e.status?.state === 'failed', + (e: any) => e.kind === 'task' && e.status?.state === 'failed', ); expect(failedEvent).toBeDefined(); - expect((failedEvent as any).final).toBe(true); + // Must NOT be a bare status-update + expect(eventBus.published.some((e: any) => e.kind === 'status-update')).toBe(false); }); }); describe('execute — no principal', () => { - it('context に a2aPrincipal が無い → failed + finished', async () => { + it('context に a2aPrincipal が無い → failed Task (kind:task) + finished', async () => { const repo = makeFakeRepo(); const eventBus = makeFakeEventBus(); const executor = new MaestroA2aExecutor(repo as any, { @@ -277,10 +283,14 @@ describe('MaestroA2aExecutor', () => { await executor.execute(ctx as any, eventBus as any); expect(eventBus.finishedCalled).toBe(true); + // Task 4: pre-initial-Task denial → full Task object, NOT a bare status-update const failedEvent = eventBus.published.find( - (e: any) => e.kind === 'status-update' && e.status?.state === 'failed', + (e: any) => e.kind === 'task' && e.status?.state === 'failed', ); expect(failedEvent).toBeDefined(); + expect(failedEvent).toMatchObject({ id: 'task-1', contextId: 'ctx-1', kind: 'task' }); + // Must NOT be a bare status-update + expect(eventBus.published.some((e: any) => e.kind === 'status-update')).toBe(false); // createLocalTask は呼ばれない expect(repo.createLocalTask).not.toHaveBeenCalled(); }); @@ -309,9 +319,11 @@ describe('MaestroA2aExecutor', () => { }); }); - // Fix 2: execute() が内部エラーを throw → failed status-update + finished()(ストリームハングなし) - describe('execute — internal error (Fix 2)', () => { - it('createLocalTask が throw → failed status-update が publish され finished() が呼ばれる', async () => { + // Fix 2: execute() が内部エラーを throw → failed Task + finished()(ストリームハングなし) + // Task 4: createLocalTask throws before initialTask is published → outer catch fires with + // initialTaskPublished=false → publishTerminal emits kind:'task' (not status-update) + describe('execute — internal error (Fix 2 + Task 4)', () => { + it('createLocalTask が throw → failed Task (kind:task) が publish され finished() が呼ばれる', async () => { const baseRepo = makeFakeRepo(); const repo = { ...baseRepo, @@ -325,9 +337,9 @@ describe('MaestroA2aExecutor', () => { // SSE ストリームがハングしないこと expect(eventBus.finishedCalled).toBe(true); - // generic failed event が publish されること(内部詳細を漏らさない) + // Task 4: pre-initial-Task throw → full Task object so ResultManager can save it const failedEvent = eventBus.published.find( - (e: any) => e.kind === 'status-update' && e.status?.state === 'failed' && e.final === true, + (e: any) => e.kind === 'task' && e.status?.state === 'failed', ); expect(failedEvent).toBeDefined(); // 内部エラーの詳細("DB connection failed")はクライアントに漏れない @@ -524,4 +536,161 @@ describe('MaestroA2aExecutor', () => { expect(repo.requestJobCancel).not.toHaveBeenCalled(); }); }); + + // ── Task 5: ガバナンスガード ───────────────────────────────────────────────── + describe('execute — governance guards (Task 5)', () => { + const NOW_ISO = '2026-07-07T00:00:00.000Z'; + const nowFn = () => NOW_ISO; + + it('payload guard: oversized message → rejected Task (kind:task), createLocalTask not called', async () => { + const repo = makeFakeRepo(); + const eventBus = makeFakeEventBus(); + // maxPayloadBytes: 1 なので userMessage を JSON 化した時点で確実にオーバー + const limiter = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxPayloadBytes: 1 }, () => 0); + const executor = new MaestroA2aExecutor(repo as any, { + pollMs: 0, + sleep: async () => {}, + now: nowFn, + limiter, + }); + const ctx = makeRequestContext({ a2aPrincipal: DEFAULT_PRINCIPAL, messageText: 'hello' }); + + await executor.execute(ctx as any, eventBus as any); + + expect(eventBus.finishedCalled).toBe(true); + // kind:'task' (pre-initial-Task path) with state='rejected' + const rejected = eventBus.published.find( + (e: any) => e.kind === 'task' && e.status?.state === 'rejected', + ); + expect(rejected).toBeDefined(); + // No job row created + expect(repo.createLocalTask).not.toHaveBeenCalled(); + }); + + it('rate guard: rate bucket drained → rejected Task (kind:task), createLocalTask not called', async () => { + const repo = makeFakeRepo(); + const eventBus = makeFakeEventBus(); + const limiter = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, ratePerMinute: 1 }, () => 0); + // Pre-drain: consume the 1 available token so the next call is rejected + limiter.tryConsumeRate('grant-1'); + const executor = new MaestroA2aExecutor(repo as any, { + pollMs: 0, + sleep: async () => {}, + now: nowFn, + limiter, + }); + const ctx = makeRequestContext({ a2aPrincipal: DEFAULT_PRINCIPAL }); + + await executor.execute(ctx as any, eventBus as any); + + expect(eventBus.finishedCalled).toBe(true); + const rejected = eventBus.published.find( + (e: any) => e.kind === 'task' && e.status?.state === 'rejected', + ); + expect(rejected).toBeDefined(); + expect(repo.createLocalTask).not.toHaveBeenCalled(); + }); + + it('concurrency guard: db count at cap → rejected Task (kind:task), createLocalTask not called', async () => { + const repo = makeFakeRepo(); + // Seed the mock so countNonTerminalA2aTasksByGrant returns at-cap value + (repo.countNonTerminalA2aTasksByGrant as any).mockReturnValue(5); + const eventBus = makeFakeEventBus(); + const limiter = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentTasks: 5 }, () => 0); + const executor = new MaestroA2aExecutor(repo as any, { + pollMs: 0, + sleep: async () => {}, + now: nowFn, + limiter, + }); + const ctx = makeRequestContext({ a2aPrincipal: DEFAULT_PRINCIPAL }); + + await executor.execute(ctx as any, eventBus as any); + + expect(eventBus.finishedCalled).toBe(true); + const rejected = eventBus.published.find( + (e: any) => e.kind === 'task' && e.status?.state === 'rejected', + ); + expect(rejected).toBeDefined(); + expect(repo.createLocalTask).not.toHaveBeenCalled(); + }); + + it('skill budget guard: budget exhausted → rejected Task (kind:task), createLocalTask not called', async () => { + const repo = makeFakeRepo(); + const eventBus = makeFakeEventBus(); + const limiter = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, skillBudgetPerHour: 1 }, () => 0); + // Pre-drain: exhaust the 1 available skill budget slot + limiter.tryConsumeSkillBudget('grant-1'); + const executor = new MaestroA2aExecutor(repo as any, { + pollMs: 0, + sleep: async () => {}, + now: nowFn, + limiter, + }); + const ctx = makeRequestContext({ a2aPrincipal: DEFAULT_PRINCIPAL }); + + await executor.execute(ctx as any, eventBus as any); + + expect(eventBus.finishedCalled).toBe(true); + const rejected = eventBus.published.find( + (e: any) => e.kind === 'task' && e.status?.state === 'rejected', + ); + expect(rejected).toBeDefined(); + // Guard fires before createLocalTask — no DB row is created and no cancel is needed + expect(repo.createLocalTask).not.toHaveBeenCalled(); + expect(repo.cancelA2aLinkedJob).not.toHaveBeenCalled(); + }); + + it('happy path with generous limiter → initial submitted Task published', async () => { + const repo = makeFakeRepo(); + const eventBus = makeFakeEventBus(); + // All default limits are generous; task should proceed normally + const limiter = new A2aLimiter({ ...DEFAULT_A2A_LIMITS }, () => Date.now()); + const executor = new MaestroA2aExecutor(repo as any, { + pollMs: 0, + sleep: async () => {}, + now: nowFn, + limiter, + }); + const ctx = makeRequestContext({ a2aPrincipal: DEFAULT_PRINCIPAL }); + + await executor.execute(ctx as any, eventBus as any); + + expect(eventBus.finishedCalled).toBe(true); + const submitted = eventBus.published.find( + (e: any) => e.kind === 'task' && e.status?.state === 'submitted', + ); + expect(submitted).toBeDefined(); + }); + + it('reservation release: skill_budget reject after reservation → slot freed, no leak', async () => { + const repo = makeFakeRepo(); + const eventBus = makeFakeEventBus(); + const limiter = new A2aLimiter( + { ...DEFAULT_A2A_LIMITS, skillBudgetPerHour: 1, maxConcurrentTasks: 2 }, + () => 0, + ); + // Pre-drain skill budget only, leaving concurrency intact + limiter.tryConsumeSkillBudget('grant-1'); + const executor = new MaestroA2aExecutor(repo as any, { + pollMs: 0, + sleep: async () => {}, + now: nowFn, + limiter, + }); + const ctx = makeRequestContext({ a2aPrincipal: DEFAULT_PRINCIPAL }); + + // Execute → concurrency reserved, then skill_budget reject, then finally releases concurrency + await executor.execute(ctx as any, eventBus as any); + + const rejected = eventBus.published.find( + (e: any) => e.kind === 'task' && e.status?.state === 'rejected', + ); + expect(rejected).toBeDefined(); + + // After the exit, the reservation must be freed — new reservation at dbCount=0 should succeed + expect(limiter.tryReserveConcurrency('grant-1', 0)).toBe(true); + limiter.releaseConcurrency('grant-1'); // cleanup + }); + }); }); diff --git a/src/bridge/a2a/executor.ts b/src/bridge/a2a/executor.ts index a6e2cab..39ad80d 100644 --- a/src/bridge/a2a/executor.ts +++ b/src/bridge/a2a/executor.ts @@ -15,6 +15,7 @@ import type { A2aPrincipal } from './token-auth.js'; import { selectPieceForMessage } from './skill-router.js'; import { logger } from '../../logger.js'; import { enumerateOutputArtifacts, finalizeStatusFromJob, TERMINAL_JOB_STATUSES } from './task-finalize.js'; +import { A2aLimiter } from './limiter.js'; export interface MaestroA2aExecutorDeps { /** ポーリング間隔 (ms)。デフォルト 1000。テストでは 0 を注入。 */ @@ -25,6 +26,8 @@ export interface MaestroA2aExecutorDeps { now?: () => string; /** ポーリング上限イテレーション数。デフォルト 600(1s×600 = 10 分)。 */ maxPollIterations?: number; + /** ガバナンスリミッター(Task 5)。省略時はガバナンスチェックをスキップ。 */ + limiter?: A2aLimiter; } // TERMINAL_STATUSES は task-finalize.ts の TERMINAL_JOB_STATUSES を使用(ドリフト防止) @@ -35,6 +38,7 @@ export class MaestroA2aExecutor implements AgentExecutor { private readonly sleep: (ms: number) => Promise; private readonly now: () => string; private readonly maxPollIterations: number; + private readonly limiter?: A2aLimiter; /** A2A taskId → Maestro jobId のマッピング(cancelTask に使用)。 */ private readonly jobIdByA2aTaskId = new Map(); @@ -48,7 +52,14 @@ export class MaestroA2aExecutor implements AgentExecutor { this.pollMs = deps.pollMs ?? 1000; this.sleep = deps.sleep ?? ((ms) => new Promise(r => setTimeout(r, ms))); this.now = deps.now ?? (() => new Date().toISOString()); - this.maxPollIterations = deps.maxPollIterations ?? 600; + this.limiter = deps.limiter; + if (deps.maxPollIterations !== undefined) { + this.maxPollIterations = deps.maxPollIterations; + } else if (deps.limiter && this.pollMs > 0) { + this.maxPollIterations = Math.max(1, Math.ceil(deps.limiter.limits.maxStreamSeconds * 1000 / this.pollMs)); + } else { + this.maxPollIterations = 600; + } } // ── helpers ────────────────────────────────────────────────────────────── @@ -110,6 +121,54 @@ export class MaestroA2aExecutor implements AgentExecutor { eventBus.publish(event); } + /** + * Unified terminal publisher (Task 4). + * + * When the initial Task event has NOT yet been published, the SDK's ResultManager + * has no entry for this taskId, so a bare status-update would be silently dropped. + * In that case we emit a full Task object (`kind:'task'`) which ResultManager saves + * and returns to the blocking caller. Once the initial Task is in the store + * (`initialPublished === true`) we fall back to a status-update (`final: true`), + * the same shape that publishFailedFinal / publishCanceledFinal have always produced. + */ + private publishTerminal( + eventBus: ExecutionEventBus, + taskId: string, + contextId: string, + state: 'failed' | 'rejected' | 'canceled', + reason: string, + initialPublished: boolean, + metadata?: Record, + ): void { + if (!initialPublished) { + const task: Task = { + id: taskId, + contextId, + kind: 'task', + status: { + state, + message: this.makeStatusMessage(reason, contextId, taskId), + timestamp: this.now(), + }, + ...(metadata ? { metadata } : {}), + }; + eventBus.publish(task); // ResultManager saves + returns this terminal Task + return; + } + const ev: TaskStatusUpdateEvent = { + kind: 'status-update', + taskId, + contextId, + status: { + state, + message: this.makeStatusMessage(reason, contextId, taskId), + timestamp: this.now(), + } as TaskStatusUpdateEvent['status'], + final: true, + }; + eventBus.publish(ev); + } + // ── AgentExecutor.execute ───────────────────────────────────────────────── execute = async (requestContext: RequestContext, eventBus: ExecutionEventBus): Promise => { @@ -117,6 +176,12 @@ export class MaestroA2aExecutor implements AgentExecutor { // Fix 2: guard finished() so it is called exactly once regardless of path let finishedCalled = false; + // Task 4: track whether the initial Task event has been published so publishTerminal + // knows whether to emit a full Task object (required by SDK ResultManager for pre-initial + // denial paths) or a bare status-update (valid once the Task is in the store). + let initialTaskPublished = false; + // Task 5: concurrency reservation — must be declared above try so finally can release it. + let reservedGrant: string | null = null; const safeFinished = () => { if (!finishedCalled) { finishedCalled = true; @@ -130,15 +195,31 @@ export class MaestroA2aExecutor implements AgentExecutor { ?.a2aPrincipal as A2aPrincipal | undefined; if (!principal) { - this.publishFailedFinal(eventBus, taskId, contextId, 'unauthorized: no A2A principal'); + this.publishTerminal(eventBus, taskId, contextId, 'failed', 'unauthorized: no A2A principal', initialTaskPublished); safeFinished(); return; } + // 1b. ガバナンス: payload サイズ + レートリミット(scope 算出前に弾く = flood に scope DB 処理させない) + if (this.limiter) { + const L = this.limiter.limits; + const bytes = Buffer.byteLength(JSON.stringify(requestContext.userMessage ?? {}), 'utf8'); + if (bytes > L.maxPayloadBytes) { + await this.rejectGoverned(eventBus, taskId, contextId, principal, `payload too large: ${bytes} > ${L.maxPayloadBytes}`, 'payload', initialTaskPublished); + safeFinished(); + return; + } + if (!this.limiter.tryConsumeRate(principal.grantId)) { + await this.rejectGoverned(eventBus, taskId, contextId, principal, `rate limit exceeded (${L.ratePerMinute}/min)`, 'rate', initialTaskPublished); + safeFinished(); + return; + } + } + // 2. acting user の実際の可視スコープを算出(IDOR 防止・fail-closed) const scope = await computeEffectiveScope(this.repo, principal); if (!scope) { - this.publishFailedFinal(eventBus, taskId, contextId, 'unauthorized: unknown or inactive acting user'); + this.publishTerminal(eventBus, taskId, contextId, 'failed', 'unauthorized: unknown or inactive acting user', initialTaskPublished); safeFinished(); return; } @@ -159,7 +240,26 @@ export class MaestroA2aExecutor implements AgentExecutor { requestedSkillId, }); if (!sel) { - this.publishFailedFinal(eventBus, taskId, contextId, 'out of scope: no matching skill or space'); + this.publishTerminal(eventBus, taskId, contextId, 'failed', 'out of scope: no matching skill or space', initialTaskPublished); + safeFinished(); + return; + } + + // 4b. ガバナンス: 同時実行数チェック(createLocalTask 前に予約 — DB 行作成でカウントが増える前に弾く) + if (this.limiter) { + const dbCount = this.repo.countNonTerminalA2aTasksByGrant(principal.grantId); + if (!this.limiter.tryReserveConcurrency(principal.grantId, dbCount)) { + await this.rejectGoverned(eventBus, taskId, contextId, principal, `concurrency limit reached (${this.limiter.limits.maxConcurrentTasks})`, 'concurrency', initialTaskPublished); + safeFinished(); + return; + } + reservedGrant = principal.grantId; + } + + // 4c. ガバナンス: スキルバジェットチェック(createLocalTask 前 — 拒否時に local_tasks/jobs 行を作らない) + // concurrency 予約済みなので denial 時は finally ブロックが slot を解放する。 + if (this.limiter && !this.limiter.tryConsumeSkillBudget(principal.grantId)) { + await this.rejectGoverned(eventBus, taskId, contextId, principal, `hourly skill budget exhausted (${this.limiter.limits.skillBudgetPerHour}/h)`, 'skill_budget', initialTaskPublished); safeFinished(); return; } @@ -195,7 +295,7 @@ export class MaestroA2aExecutor implements AgentExecutor { localTask.id, ); if (!job) { - this.publishFailedFinal(eventBus, taskId, contextId, 'internal error: job not created'); + this.publishTerminal(eventBus, taskId, contextId, 'failed', 'internal error: job not created', initialTaskPublished); safeFinished(); return; } @@ -236,6 +336,7 @@ export class MaestroA2aExecutor implements AgentExecutor { }, }; eventBus.publish(initialTask); + initialTaskPublished = true; // 8. ポーリングループ let lastStatus: string | null = null; @@ -294,14 +395,18 @@ export class MaestroA2aExecutor implements AgentExecutor { safeFinished(); } catch (err) { // Fix 2: catch unhandled DB / internal errors so the SSE stream never hangs + // Task 4: use publishTerminal so an early throw (before initial Task) still produces a + // valid Task object that SDK ResultManager can save and return to the caller. logger.warn(`[a2a] executor error: ${err}`); if (!finishedCalled) { - this.publishFailedFinal(eventBus, taskId, contextId, 'internal error'); + this.publishTerminal(eventBus, taskId, contextId, 'failed', 'internal error', initialTaskPublished); } } finally { // T6: per-task map cleanup — 全終了経路(terminal/timeout/throw)で確実に実行 this.jobIdByA2aTaskId.delete(taskId); this.contextIdByA2aTaskId.delete(taskId); + // Task 5: concurrency reservation release — every exit path (terminal/timeout/throw/reject-after-reserve) + if (reservedGrant && this.limiter) this.limiter.releaseConcurrency(reservedGrant); // Fix 2: guarantee finished() is called exactly once on every exit path safeFinished(); } @@ -373,6 +478,34 @@ export class MaestroA2aExecutor implements AgentExecutor { } } + // ── governance helper ───────────────────────────────────────────────────── + + /** + * Publishes a terminal `rejected` event and records a best-effort audit log entry. + * Called by the governance guards (payload / rate / concurrency / skill_budget). + */ + private async rejectGoverned( + eventBus: ExecutionEventBus, + taskId: string, + contextId: string, + principal: A2aPrincipal, + reason: string, + limit: string, + initialPublished: boolean, + ): Promise { + logger.info(`[a2a-exec] governance reject limit=${limit} grant=${principal.grantId}`); + this.publishTerminal(eventBus, taskId, contextId, 'rejected', reason, initialPublished); + try { + await this.repo.addAuditLog(null, 'a2a_governance_reject', 'a2a', { + limit, + reason, + grantId: principal.grantId, + clientId: principal.clientId, + actingUserId: principal.actingUserId, + }); + } catch { /* best-effort audit */ } + } + // ── AgentExecutor.cancelTask ────────────────────────────────────────────── cancelTask = async (taskId: string, eventBus: ExecutionEventBus): Promise => { diff --git a/src/bridge/a2a/limiter.test.ts b/src/bridge/a2a/limiter.test.ts new file mode 100644 index 0000000..21f7ce7 --- /dev/null +++ b/src/bridge/a2a/limiter.test.ts @@ -0,0 +1,328 @@ +// @vitest-environment node +import { describe, it, expect } from 'vitest'; +import { A2aLimiter, DEFAULT_A2A_LIMITS, resolveA2aLimits } from './limiter.js'; + +function clock(start: number) { + let t = start; + return { now: () => t, advance: (ms: number) => { t += ms; } }; +} + +// --------------------------------------------------------------------------- +// resolveA2aLimits +// --------------------------------------------------------------------------- +describe('resolveA2aLimits (strict integer≥1)', () => { + it('fills defaults for missing/invalid (brief canonical test)', () => { + const r = resolveA2aLimits({ + ratePerMinute: 7, + maxPayloadBytes: 0, + skillBudgetPerHour: 0.5, + maxConcurrentTasks: '3' as any, + maxStreamSeconds: NaN, + }); + expect(r.ratePerMinute).toBe(7); // valid integer≥1 → accepted + expect(r.maxPayloadBytes).toBe(DEFAULT_A2A_LIMITS.maxPayloadBytes); // 0 rejected + expect(r.skillBudgetPerHour).toBe(DEFAULT_A2A_LIMITS.skillBudgetPerHour); // 0.5 rejected + expect(r.maxConcurrentTasks).toBe(DEFAULT_A2A_LIMITS.maxConcurrentTasks); // "3" rejected + expect(r.maxStreamSeconds).toBe(DEFAULT_A2A_LIMITS.maxStreamSeconds); // NaN rejected + }); + + it('rejects 0 → default', () => { + const r = resolveA2aLimits({ ratePerMinute: 0 }); + expect(r.ratePerMinute).toBe(DEFAULT_A2A_LIMITS.ratePerMinute); + }); + + it('rejects negative → default', () => { + const r = resolveA2aLimits({ ratePerMinute: -5 }); + expect(r.ratePerMinute).toBe(DEFAULT_A2A_LIMITS.ratePerMinute); + }); + + it('rejects NaN → default', () => { + const r = resolveA2aLimits({ maxStreamSeconds: NaN }); + expect(r.maxStreamSeconds).toBe(DEFAULT_A2A_LIMITS.maxStreamSeconds); + }); + + it('rejects non-integer 0.5 → default (no starvation)', () => { + const r = resolveA2aLimits({ ratePerMinute: 0.5 }); + expect(r.ratePerMinute).toBe(DEFAULT_A2A_LIMITS.ratePerMinute); + }); + + it('rejects string "3" → default', () => { + const r = resolveA2aLimits({ maxConcurrentTasks: '3' as any }); + expect(r.maxConcurrentTasks).toBe(DEFAULT_A2A_LIMITS.maxConcurrentTasks); + }); + + it('accepts all valid integer≥1 values', () => { + const r = resolveA2aLimits({ + ratePerMinute: 10, + maxConcurrentTasks: 3, + maxPayloadBytes: 1024, + maxStreamSeconds: 60, + skillBudgetPerHour: 50, + maxConcurrentResubscribe: 2, + }); + expect(r.ratePerMinute).toBe(10); + expect(r.maxConcurrentTasks).toBe(3); + expect(r.maxPayloadBytes).toBe(1024); + expect(r.maxStreamSeconds).toBe(60); + expect(r.skillBudgetPerHour).toBe(50); + expect(r.maxConcurrentResubscribe).toBe(2); + }); + + it('returns defaults for undefined input', () => { + const r = resolveA2aLimits(); + expect(r).toEqual(DEFAULT_A2A_LIMITS); + }); + + it('returns defaults for empty object', () => { + const r = resolveA2aLimits({}); + expect(r).toEqual(DEFAULT_A2A_LIMITS); + }); + + it('DEFAULT_A2A_LIMITS has expected values', () => { + expect(DEFAULT_A2A_LIMITS.ratePerMinute).toBe(30); + expect(DEFAULT_A2A_LIMITS.maxConcurrentTasks).toBe(5); + expect(DEFAULT_A2A_LIMITS.maxPayloadBytes).toBe(65536); + expect(DEFAULT_A2A_LIMITS.maxStreamSeconds).toBe(600); + expect(DEFAULT_A2A_LIMITS.skillBudgetPerHour).toBe(100); + expect(DEFAULT_A2A_LIMITS.maxConcurrentResubscribe).toBe(5); + }); +}); + +// --------------------------------------------------------------------------- +// tryConsumeRate (token bucket) +// --------------------------------------------------------------------------- +describe('tryConsumeRate', () => { + it('allows burst up to ratePerMinute then returns false', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, ratePerMinute: 3 }, c.now); + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(false); + }); + + it('refills over time at ratePerMinute per minute', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, ratePerMinute: 2 }, c.now); + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(false); // exhausted + + c.advance(30_000); // +30s → +1 token + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(false); // exhausted again + + c.advance(60_000); // +60s → +2 tokens, capped at 2 + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(false); + }); + + it('guards negative elapsed (no backward-clock token loss)', () => { + const c = clock(1000); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, ratePerMinute: 1 }, c.now); + lim.tryConsumeRate('g'); // consume 1 → 0 tokens + // simulate slight clock jitter (elapsed < 0 clamped to 0) + c.advance(-500); + expect(lim.tryConsumeRate('g')).toBe(false); // no artificial tokens added + }); + + it('grants are isolated: separate buckets per grantId', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, ratePerMinute: 1 }, c.now); + expect(lim.tryConsumeRate('g1')).toBe(true); + expect(lim.tryConsumeRate('g1')).toBe(false); + expect(lim.tryConsumeRate('g2')).toBe(true); // separate bucket + expect(lim.tryConsumeRate('g2')).toBe(false); + }); +}); + +// --------------------------------------------------------------------------- +// tryConsumeSkillBudget (rolling-hour window) +// --------------------------------------------------------------------------- +describe('tryConsumeSkillBudget', () => { + it('allows up to skillBudgetPerHour then returns false', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, skillBudgetPerHour: 3 }, c.now); + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(false); + }); + + it('frees budget after 1h rolling window', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, skillBudgetPerHour: 2 }, c.now); + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(false); // exhausted + + c.advance(3_600_001); // 1h+1ms: all timestamps expire + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(false); + }); + + it('partial window-free: only expired entries pruned', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, skillBudgetPerHour: 3 }, c.now); + expect(lim.tryConsumeSkillBudget('g')).toBe(true); // t=0 + expect(lim.tryConsumeSkillBudget('g')).toBe(true); // t=0 + c.advance(3_600_001); + // The two t=0 timestamps are now expired; can consume 3 more + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(true); + expect(lim.tryConsumeSkillBudget('g')).toBe(false); + }); + + it('grants are isolated', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, skillBudgetPerHour: 1 }, c.now); + expect(lim.tryConsumeSkillBudget('g1')).toBe(true); + expect(lim.tryConsumeSkillBudget('g1')).toBe(false); + expect(lim.tryConsumeSkillBudget('g2')).toBe(true); + }); +}); + +// --------------------------------------------------------------------------- +// tryReserveConcurrency / releaseConcurrency +// --------------------------------------------------------------------------- +describe('tryReserveConcurrency', () => { + it('caps synchronously and respects the DB backstop (brief canonical test)', () => { + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentTasks: 2 }, () => 0); + expect(lim.tryReserveConcurrency('g', 0)).toBe(true); + expect(lim.tryReserveConcurrency('g', 0)).toBe(true); + expect(lim.tryReserveConcurrency('g', 0)).toBe(false); // 2 reserved + lim.releaseConcurrency('g'); + expect(lim.tryReserveConcurrency('g', 0)).toBe(true); // freed one + + const lim2 = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentTasks: 2 }, () => 0); + expect(lim2.tryReserveConcurrency('g', 2)).toBe(false); // DB already at cap + }); + + it('max(dbCount, reserved) semantics: dbCount=1 + reserved=1 → effective 1, second reserve succeeds', () => { + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentTasks: 2 }, () => 0); + expect(lim.tryReserveConcurrency('g', 1)).toBe(true); // max(1,0)=1 < 2 → ok, reserved=1 + expect(lim.tryReserveConcurrency('g', 1)).toBe(true); // max(1,1)=1 < 2 → ok, reserved=2 + expect(lim.tryReserveConcurrency('g', 1)).toBe(false); // max(1,2)=2 >= 2 → fail + }); + + it('dbCount alone triggers the cap (DB backstop without in-memory)', () => { + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentTasks: 3 }, () => 0); + expect(lim.tryReserveConcurrency('g', 3)).toBe(false); // max(3,0)=3 >= 3 + expect(lim.tryReserveConcurrency('g', 2)).toBe(true); // max(2,0)=2 < 3 → ok + }); + + it('releaseConcurrency decrements and floors at 0', () => { + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentTasks: 3 }, () => 0); + lim.tryReserveConcurrency('g', 0); // reserved=1 + lim.tryReserveConcurrency('g', 0); // reserved=2 + lim.releaseConcurrency('g'); // reserved=1 + lim.releaseConcurrency('g'); // reserved=0 → delete + lim.releaseConcurrency('g'); // noop (already 0) + // After over-release, should still be able to reserve normally + expect(lim.tryReserveConcurrency('g', 0)).toBe(true); + expect(lim.tryReserveConcurrency('g', 0)).toBe(true); + expect(lim.tryReserveConcurrency('g', 0)).toBe(true); + expect(lim.tryReserveConcurrency('g', 0)).toBe(false); + }); + + it('per-grant isolation for concurrency', () => { + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentTasks: 1 }, () => 0); + expect(lim.tryReserveConcurrency('g1', 0)).toBe(true); + expect(lim.tryReserveConcurrency('g1', 0)).toBe(false); + expect(lim.tryReserveConcurrency('g2', 0)).toBe(true); // separate counter + }); +}); + +// --------------------------------------------------------------------------- +// tryAcquireResubscribe / releaseResubscribe +// --------------------------------------------------------------------------- +describe('tryAcquireResubscribe / releaseResubscribe', () => { + it('caps at maxConcurrentResubscribe then returns false', () => { + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentResubscribe: 2 }, () => 0); + expect(lim.tryAcquireResubscribe('g')).toBe(true); + expect(lim.tryAcquireResubscribe('g')).toBe(true); + expect(lim.tryAcquireResubscribe('g')).toBe(false); + }); + + it('releaseResubscribe frees a slot', () => { + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentResubscribe: 1 }, () => 0); + expect(lim.tryAcquireResubscribe('g')).toBe(true); + expect(lim.tryAcquireResubscribe('g')).toBe(false); + lim.releaseResubscribe('g'); + expect(lim.tryAcquireResubscribe('g')).toBe(true); + }); + + it('per-grant isolation', () => { + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentResubscribe: 1 }, () => 0); + expect(lim.tryAcquireResubscribe('g1')).toBe(true); + expect(lim.tryAcquireResubscribe('g1')).toBe(false); + expect(lim.tryAcquireResubscribe('g2')).toBe(true); // separate counter + }); + + it('over-release floors at 0 and does not corrupt subsequent acquires', () => { + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, maxConcurrentResubscribe: 1 }, () => 0); + lim.tryAcquireResubscribe('g'); + lim.releaseResubscribe('g'); + lim.releaseResubscribe('g'); // noop + expect(lim.tryAcquireResubscribe('g')).toBe(true); + expect(lim.tryAcquireResubscribe('g')).toBe(false); + }); +}); + +// --------------------------------------------------------------------------- +// Opportunistic eviction +// --------------------------------------------------------------------------- +describe('eviction', () => { + it('evicts a stale full rate bucket after >1h idle (Map size shrinks)', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, ratePerMinute: 2 }, c.now); + + // Populate two grant buckets + lim.tryConsumeRate('g1'); + lim.tryConsumeRate('g2'); + expect(lim.rateBucketSize()).toBe(2); + + // Advance >1h so both buckets become full and stale + c.advance(3_600_001); + + // Accessing g1 triggers eviction of its stale entry and re-creates it + const result = lim.tryConsumeRate('g1'); + expect(result).toBe(true); // fresh bucket → 1 token consumed + // g1 was evicted then re-created (size stays 2: g1 re-created, g2 still there) + // The key assertion: g1's bucket was deleted then re-added; g2 is still stale + expect(lim.rateBucketSize()).toBe(2); + }); + + it('evicted stale bucket starts with fresh capacity', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, ratePerMinute: 2 }, c.now); + // Exhaust bucket + lim.tryConsumeRate('g'); + lim.tryConsumeRate('g'); + expect(lim.tryConsumeRate('g')).toBe(false); + + // Advance >1h → bucket refills to capacity (2) and becomes stale → eviction + c.advance(3_600_001); + // Both tokens should be available (fresh bucket) + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(true); + expect(lim.tryConsumeRate('g')).toBe(false); + }); + + it('evicts empty skillHits arrays (pruned to empty on successful consume)', () => { + const c = clock(0); + const lim = new A2aLimiter({ ...DEFAULT_A2A_LIMITS, skillBudgetPerHour: 2 }, c.now); + lim.tryConsumeSkillBudget('g'); + expect(lim.skillHitsSize()).toBe(1); + + // Advance >1h so the hit expires; new consume should work + c.advance(3_600_001); + const result = lim.tryConsumeSkillBudget('g'); + expect(result).toBe(true); + // Array had 0 elements after prune + 1 new added → size=1 + expect(lim.skillHitsSize()).toBe(1); + }); +}); diff --git a/src/bridge/a2a/limiter.ts b/src/bridge/a2a/limiter.ts new file mode 100644 index 0000000..7a87a72 --- /dev/null +++ b/src/bridge/a2a/limiter.ts @@ -0,0 +1,216 @@ +/** + * A2aLimiter: process-local rate / budget / concurrency counters for A2A grant enforcement. + * + * IMPORTANT — process-local, resets on restart: + * All counters live in-process memory and are lost when the server restarts. + * This is intentional and acceptable because: + * - Rate / skill-budget counters: brief gaps from restarts are fail-open by design. + * - Concurrency reservation: the durable backstop is the `dbCount` argument passed to + * tryReserveConcurrency(). The in-memory counter is an additional synchronous guard + * against TOCTOU races within a single process instance. + * + * Keyed by grantId (UUID from the a2a_delegations table). + */ + +import { logger } from '../../logger.js'; + +// --------------------------------------------------------------------------- +// Public types +// --------------------------------------------------------------------------- + +export interface A2aLimits { + ratePerMinute: number; + maxConcurrentTasks: number; + maxPayloadBytes: number; + maxStreamSeconds: number; + skillBudgetPerHour: number; + maxConcurrentResubscribe: number; +} + +export const DEFAULT_A2A_LIMITS: A2aLimits = { + ratePerMinute: 30, + maxConcurrentTasks: 5, + maxPayloadBytes: 65536, + maxStreamSeconds: 600, + skillBudgetPerHour: 100, + maxConcurrentResubscribe: 5, +}; + +// --------------------------------------------------------------------------- +// resolveA2aLimits — strict integer≥1 validation +// --------------------------------------------------------------------------- + +function posInt(v: unknown, fallback: number): number { + return typeof v === 'number' && Number.isInteger(v) && v >= 1 ? v : fallback; +} + +export function resolveA2aLimits(p?: Partial>): A2aLimits { + const src = p ?? {}; + const out = {} as A2aLimits; + for (const k of Object.keys(DEFAULT_A2A_LIMITS) as (keyof A2aLimits)[]) { + const resolved = posInt(src[k], DEFAULT_A2A_LIMITS[k]); + if (src[k] !== undefined && resolved !== src[k]) { + logger.warn(`[a2a] invalid limit ${k}=${String(src[k])}, using default ${resolved}`); + } + out[k] = resolved; + } + return out; +} + +// --------------------------------------------------------------------------- +// Internal state shapes +// --------------------------------------------------------------------------- + +interface RateBucket { + tokens: number; + lastRefillMs: number; +} + +const ONE_HOUR_MS = 3_600_000; + +// --------------------------------------------------------------------------- +// A2aLimiter +// --------------------------------------------------------------------------- + +export class A2aLimiter { + readonly limits: A2aLimits; + private readonly _now: () => number; + + /** Token buckets for rate limiting: grantId → { tokens, lastRefillMs } */ + private readonly _rateBuckets = new Map(); + /** Rolling-hour timestamps for skill budget: grantId → timestamp[] */ + private readonly _skillHits = new Map(); + /** In-memory concurrency reservations: grantId → count */ + private readonly _reservations = new Map(); + /** Resubscribe slot counters: grantId → count */ + private readonly _resubscribes = new Map(); + + constructor(limits: A2aLimits, now: () => number = () => Date.now()) { + this.limits = limits; + this._now = now; + } + + // ------------------------------------------------------------------------- + // Rate limiting (token bucket) + // ------------------------------------------------------------------------- + + tryConsumeRate(grantId: string): boolean { + const nowMs = this._now(); + const capacity = this.limits.ratePerMinute; + let bucket = this._rateBuckets.get(grantId); + + if (bucket) { + const elapsed = Math.max(0, nowMs - bucket.lastRefillMs); + bucket.tokens = Math.min(capacity, bucket.tokens + elapsed * capacity / 60_000); + + // Opportunistic eviction: full bucket idle for >1h → drop stale entry. + // The next access will start fresh (same observable effect as a full refill). + if (bucket.tokens >= capacity && elapsed >= ONE_HOUR_MS) { + this._rateBuckets.delete(grantId); + bucket = undefined; + } else { + bucket.lastRefillMs = nowMs; + } + } + + if (!bucket) { + bucket = { tokens: capacity, lastRefillMs: nowMs }; + this._rateBuckets.set(grantId, bucket); + } + + if (bucket.tokens >= 1) { + bucket.tokens -= 1; + return true; + } + return false; + } + + // ------------------------------------------------------------------------- + // Skill budget (rolling-hour window) + // ------------------------------------------------------------------------- + + tryConsumeSkillBudget(grantId: string): boolean { + const nowMs = this._now(); + const cutoff = nowMs - ONE_HOUR_MS; + const existing = this._skillHits.get(grantId) ?? []; + + // Prune timestamps older than 1h (keep only recent ones) + const hits = existing.filter(ts => ts > cutoff); + + if (hits.length < this.limits.skillBudgetPerHour) { + hits.push(nowMs); + this._skillHits.set(grantId, hits); + return true; + } + + // Budget exhausted. Update the pruned array; evict if empty (defensive). + if (hits.length > 0) { + this._skillHits.set(grantId, hits); + } else { + this._skillHits.delete(grantId); + } + return false; + } + + // ------------------------------------------------------------------------- + // Concurrency reservation (synchronous TOCTOU guard) + // ------------------------------------------------------------------------- + + /** + * Try to reserve one concurrency slot for grantId. + * @param dbCount Current running-job count from the DB (durable backstop). + * Returns true and increments the in-memory counter if + * Math.max(dbCount, reservedCount) < maxConcurrentTasks. + * This is a plain synchronous method — no awaits. + */ + tryReserveConcurrency(grantId: string, dbCount: number): boolean { + const reserved = this._reservations.get(grantId) ?? 0; + if (Math.max(dbCount, reserved) >= this.limits.maxConcurrentTasks) return false; + this._reservations.set(grantId, reserved + 1); + return true; + } + + /** Decrement the concurrency reservation for grantId, flooring at 0. */ + releaseConcurrency(grantId: string): void { + const n = this._reservations.get(grantId) ?? 0; + if (n <= 1) { + this._reservations.delete(grantId); + } else { + this._reservations.set(grantId, n - 1); + } + } + + // ------------------------------------------------------------------------- + // Resubscribe slot tracking + // ------------------------------------------------------------------------- + + tryAcquireResubscribe(grantId: string): boolean { + const current = this._resubscribes.get(grantId) ?? 0; + if (current >= this.limits.maxConcurrentResubscribe) return false; + this._resubscribes.set(grantId, current + 1); + return true; + } + + releaseResubscribe(grantId: string): void { + const n = this._resubscribes.get(grantId) ?? 0; + if (n <= 1) { + this._resubscribes.delete(grantId); + } else { + this._resubscribes.set(grantId, n - 1); + } + } + + // ------------------------------------------------------------------------- + // Test hooks (Map size inspection for eviction tests) + // ------------------------------------------------------------------------- + + /** Number of live rate-bucket entries (test hook). */ + rateBucketSize(): number { + return this._rateBuckets.size; + } + + /** Number of live skill-hits entries (test hook). */ + skillHitsSize(): number { + return this._skillHits.size; + } +} diff --git a/src/bridge/a2a/request-handler-auth-gate.test.ts b/src/bridge/a2a/request-handler-auth-gate.test.ts index da67fef..ca68495 100644 --- a/src/bridge/a2a/request-handler-auth-gate.test.ts +++ b/src/bridge/a2a/request-handler-auth-gate.test.ts @@ -14,11 +14,12 @@ * and a stub executor (fastest, no HTTP round-trip needed for unit assertions). * One E2E supertest case covers the full HTTP path. */ -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, vi } from 'vitest'; import express from 'express'; import request from 'supertest'; import { A2AError, + DefaultRequestHandler, InMemoryTaskStore, ServerCallContext, UnauthenticatedUser, @@ -28,6 +29,7 @@ import { buildBaseCard } from './agent-card.js'; import { AuthGatedRequestHandler, mountA2aRequestHandler } from './request-handler.js'; import { A2aAuthenticatedUser } from './user-builder.js'; import type { A2aPrincipal } from './token-auth.js'; +import { A2aLimiter, resolveA2aLimits } from './limiter.js'; // ─── fixtures ───────────────────────────────────────────────────────────────── @@ -134,6 +136,223 @@ describe('AuthGatedRequestHandler.resubscribe', () => { }); }); +// ─── Rate limiting + resubscribe slot cap (Task 7) ──────────────────────────── + +describe('AuthGatedRequestHandler — rate limiting', () => { + function makeHandlerWithLimiter(limiter: A2aLimiter) { + const store = new InMemoryTaskStore(); + const fakeExecutor = { execute: async () => {} } as any; + const baseCard = buildBaseCard(BASE_DEPS); + return new AuthGatedRequestHandler(baseCard, store, fakeExecutor, undefined, limiter); + } + + it('getTask: second call past rate=1 throws A2AError (rate limit exceeded)', async () => { + const limiter = new A2aLimiter(resolveA2aLimits({ ratePerMinute: 1 })); + const h = makeHandlerWithLimiter(limiter); + // First call: auth OK, 1 rate token consumed → falls through to taskNotFound (not rate error) + await h.getTask({ id: 'x' }, AUTH_CTX).catch(() => {}); + // Second call: no token left → rate-limit error + const err = await h.getTask({ id: 'x' }, AUTH_CTX).catch(e => e); + expect(err).toBeInstanceOf(A2AError); + expect(err.code).toBe(-32600); + expect(err.message).toMatch(/rate limit/); + }); + + it('getTask: unauthenticated throws auth error even when budget is fully exhausted', async () => { + const limiter = new A2aLimiter(resolveA2aLimits({ ratePerMinute: 1 })); + // Pre-drain the bucket for grant g1 (the FAKE_PRINCIPAL's grantId) + limiter.tryConsumeRate('g1'); + const h = makeHandlerWithLimiter(limiter); + // Auth check must fire before rate check; message must be 'unauthorized', not 'rate limit' + const err = await h.getTask({ id: 'x' }, UNAUTH_CTX).catch(e => e); + expect(err).toBeInstanceOf(A2AError); + expect(err.message).toMatch(/unauthorized/); + }); + + it('cancelTask: second call past rate=1 throws A2AError (rate limit exceeded)', async () => { + const limiter = new A2aLimiter(resolveA2aLimits({ ratePerMinute: 1 })); + const h = makeHandlerWithLimiter(limiter); + // First call: auth OK, 1 rate token consumed → falls through to taskNotFound (not rate error) + await h.cancelTask({ id: 'x' }, AUTH_CTX).catch(() => {}); + // Second call: no token left → rate-limit error + const err = await h.cancelTask({ id: 'x' }, AUTH_CTX).catch(e => e); + expect(err).toBeInstanceOf(A2AError); + expect(err.code).toBe(-32600); + expect(err.message).toMatch(/rate limit/); + }); +}); + +describe('AuthGatedRequestHandler — resubscribe slot cap (Task 7)', () => { + function makeHandlerWithLimiter(limiter: A2aLimiter) { + const store = new InMemoryTaskStore(); + const fakeExecutor = { execute: async () => {} } as any; + const baseCard = buildBaseCard(BASE_DEPS); + return new AuthGatedRequestHandler(baseCard, store, fakeExecutor, undefined, limiter); + } + + it('resubscribe: second call throws when slot cap (maxConcurrentResubscribe=1) is reached', () => { + const limiter = new A2aLimiter(resolveA2aLimits({ maxConcurrentResubscribe: 1, ratePerMinute: 100 })); + const h = makeHandlerWithLimiter(limiter); + // First resubscribe: slot acquired + const gen1 = h.resubscribe({ id: 'x' }, AUTH_CTX); + expect(gen1).toBeDefined(); + // Second resubscribe: slot full → synchronous throw + expect(() => h.resubscribe({ id: 'x' }, AUTH_CTX)).toThrow(A2AError); + gen1.return?.(undefined); // clean up (async; we don't await) + }); + + it('resubscribe: slot released via finally when generator is iterated and exhausted', async () => { + const limiter = new A2aLimiter(resolveA2aLimits({ maxConcurrentResubscribe: 1, ratePerMinute: 100 })); + const h = makeHandlerWithLimiter(limiter); + // Acquire the slot + const gen1 = h.resubscribe({ id: 'x' }, AUTH_CTX); + // Start the generator — inner store will throw (task not found), triggering finally + await gen1.next().catch(() => {}); + // Slot should now be released; a new resubscribe must not throw + let gen2: AsyncGenerator | undefined; + expect(() => { gen2 = h.resubscribe({ id: 'x' }, AUTH_CTX); }).not.toThrow(); + expect(gen2).toBeDefined(); + await gen2!.next().catch(() => {}); // clean up + }); +}); + +// ─── maxStreamSeconds fires for a quiet stream (Task 7 review fix) ─────────── + +describe('AuthGatedRequestHandler — maxStreamSeconds wall-clock race', () => { + it('quiet stream (no events) completes and releases slot at deadline', async () => { + // Use a fractional maxStreamSeconds (0.05 = 50 ms) for a fast test. + // A2aLimiter accepts raw limits directly — bypassing resolveA2aLimits integer≥1 constraint + // is intentional here so we don't need to wait a full second. + const limiter = new A2aLimiter({ + ratePerMinute: 100, + maxConcurrentTasks: 5, + maxPayloadBytes: 65536, + maxStreamSeconds: 0.05, // 50 ms deadline + skillBudgetPerHour: 100, + maxConcurrentResubscribe: 1, + }); + const store = new InMemoryTaskStore(); + const fakeExecutor = { execute: async () => {} } as any; + const baseCard = buildBaseCard(BASE_DEPS); + const h = new AuthGatedRequestHandler(baseCard, store, fakeExecutor, undefined, limiter); + + // Stub super.resubscribe with a generator that never emits events (quiet live stream). + // This is the exact scenario the timer-race fixes: the old for-await loop would + // hold the slot indefinitely; the new Promise.race releases it at the deadline. + const neverYielding: AsyncGenerator = { + [Symbol.asyncIterator]() { return this; }, + next: () => new Promise(() => {}), // blocks indefinitely + return: async (v: any) => ({ done: true as const, value: v }), + throw: async (e: any): Promise => { throw e; }, + }; + const spy = vi.spyOn(DefaultRequestHandler.prototype, 'resubscribe').mockReturnValue( + neverYielding as any, + ); + + try { + // Slot cap=1: acquire it + const gen = h.resubscribe({ id: 'x' }, AUTH_CTX); + // Kick the generator — suspends inside the 50 ms Promise.race + const firstNext = gen.next(); + // Wait 120 ms (real timers) — the 50 ms deadline fires, race resolves with 'timeout' + await new Promise(res => setTimeout(res, 120)); + // Generator must have completed via the timeout path + const result = await firstNext; + expect(result.done).toBe(true); + // Slot must be released — second resubscribe must not throw + let gen2: AsyncGenerator | undefined; + expect(() => { gen2 = h.resubscribe({ id: 'x' }, AUTH_CTX); }).not.toThrow(); + expect(gen2).toBeDefined(); + await gen2!.return?.(undefined); // cleanup + } finally { + spy.mockRestore(); + } + }, 2000); // generous timeout for the real-timer wait + + it('quiet REAL async generator: deadline still releases slot even though inner .return() never settles', async () => { + // Regression guard for the follow-up fix: a REAL async generator suspended inside its + // body (not at a yield) queues a .return() behind the still-pending .next(), so the + // return never settles for a quiet stream. The old `await it.return()` would hang the + // wrapper forever (never reaching finally → slot leak). The stub in the test above hid + // this because its hand-rolled `return` resolved immediately. Here we use a genuine + // async generator to reproduce the queuing semantics. + const limiter = new A2aLimiter({ + ratePerMinute: 100, + maxConcurrentTasks: 5, + maxPayloadBytes: 65536, + maxStreamSeconds: 0.05, // 50 ms deadline + skillBudgetPerHour: 100, + maxConcurrentResubscribe: 1, + }); + const store = new InMemoryTaskStore(); + const fakeExecutor = { execute: async () => {} } as any; + const baseCard = buildBaseCard(BASE_DEPS); + const h = new AuthGatedRequestHandler(baseCard, store, fakeExecutor, undefined, limiter); + + // A real async generator that suspends forever inside its body. Calling .return() while + // the .next() promise is pending queues the return; it will not settle for this stream. + async function* realQuiet(): AsyncGenerator { + await new Promise(() => {}); // never resolves + yield 1; // unreachable + } + const spy = vi.spyOn(DefaultRequestHandler.prototype, 'resubscribe').mockReturnValue( + realQuiet() as any, + ); + + try { + const gen = h.resubscribe({ id: 'x' }, AUTH_CTX); + const firstNext = gen.next(); + await new Promise(res => setTimeout(res, 120)); // 50 ms deadline fires + // Must complete via the timeout path despite the inner return never settling. + const result = await firstNext; + expect(result.done).toBe(true); + // Slot released → a fresh resubscribe must not throw. + let gen2: AsyncGenerator | undefined; + expect(() => { gen2 = h.resubscribe({ id: 'x' }, AUTH_CTX); }).not.toThrow(); + await gen2!.return?.(undefined); + } finally { + spy.mockRestore(); + } + }, 2000); +}); + +// ─── fail-closed: authenticated but missing grantId is denied when metering is active ── + +describe('AuthGatedRequestHandler — fail-closed on missing grantId', () => { + const NO_GRANT_PRINCIPAL = { ...FAKE_PRINCIPAL, grantId: undefined } as any; + const NO_GRANT_CTX = new ServerCallContext(undefined, new A2aAuthenticatedUser(NO_GRANT_PRINCIPAL)); + + function makeHandlerWithLimiter() { + const store = new InMemoryTaskStore(); + const fakeExecutor = { execute: async () => {} } as any; + const baseCard = buildBaseCard(BASE_DEPS); + const limiter = new A2aLimiter(resolveA2aLimits({ ratePerMinute: 100, maxConcurrentResubscribe: 5 })); + return new AuthGatedRequestHandler(baseCard, store, fakeExecutor, undefined, limiter); + } + + it('getTask denies an authenticated caller with no grantId (limiter active)', async () => { + const h = makeHandlerWithLimiter(); + await expect(h.getTask({ id: 'x' }, NO_GRANT_CTX)).rejects.toMatchObject({ code: -32600 }); + }); + + it('cancelTask denies an authenticated caller with no grantId (limiter active)', async () => { + const h = makeHandlerWithLimiter(); + await expect(h.cancelTask({ id: 'x' }, NO_GRANT_CTX)).rejects.toMatchObject({ code: -32600 }); + }); + + it('resubscribe denies an authenticated caller with no grantId (limiter active)', () => { + const h = makeHandlerWithLimiter(); + expect(() => h.resubscribe({ id: 'x' }, NO_GRANT_CTX)).toThrow(A2AError); + }); + + it('without a limiter, a missing grantId is unmetered (reaches the store, not denied)', async () => { + // No limiter → metering off → must not fail-closed. Store throws taskNotFound (-32001), + // proving the auth+grant gate was passed rather than denied for the missing grant. + const h = makeHandler(); + await expect(h.getTask({ id: 'x' }, NO_GRANT_CTX)).rejects.toMatchObject({ code: -32001 }); + }); +}); + // ─── E2E: tasks/get via HTTP → JSON-RPC error when unauthenticated ─────────── describe('mountA2aRequestHandler – tasks/get auth gate (E2E)', () => { diff --git a/src/bridge/a2a/request-handler.test.ts b/src/bridge/a2a/request-handler.test.ts index 8fc4c82..427c925 100644 --- a/src/bridge/a2a/request-handler.test.ts +++ b/src/bridge/a2a/request-handler.test.ts @@ -142,13 +142,54 @@ describe('mountA2aRequestHandler – smoke', () => { }, }); - // Handler must not 500-crash; SDK returns a JSON-RPC error for unauthenticated + // Handler must not 500-crash and must return a valid JSON-RPC 2.0 envelope. + // message/send is NOT gated by AuthGatedRequestHandler — the executor creates + // the task (returning a result), then fails it asynchronously. So the HTTP + // response carries either a JSON-RPC result or error, never a 500. expect(res.status).not.toBe(500); expect(res.headers['content-type']).toMatch(/json/); - // Assert JSON-RPC error envelope structure - expect(res.body).toHaveProperty('error'); - expect(res.body.error).toHaveProperty('code'); - expect(res.body.error).toHaveProperty('message'); expect(res.body.jsonrpc).toBe('2.0'); + expect('result' in res.body || 'error' in res.body).toBe(true); + }); +}); + +// ─── body size limit ────────────────────────────────────────────────────────── + +describe('mountA2aRequestHandler – body size limit', () => { + it('POST /a2a with body exceeding maxPayloadBytes returns HTTP 413', async () => { + const repo = new Repository(':memory:'); + seedRepo(repo); + + const app = express(); + // Mount with a tiny limit (50 bytes) so a normal JSON-RPC body exceeds it. + mountA2aRequestHandler(app, { + provider: fakeProvider(), + repo, + ...BASE_DEPS, + limits: { maxPayloadBytes: 50 }, + }); + + // This body is well over 50 bytes. + const bigBody = JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'message/send', + params: { + message: { + messageId: 'm1', + role: 'user', + parts: [{ kind: 'text', text: 'hello world — this payload deliberately exceeds the tiny limit' }], + }, + }, + }); + + const res = await request(app) + .post('/a2a') + .set('Content-Type', 'application/json') + .send(bigBody); + + // express.json({ limit }) calls next(err) with a PayloadTooLargeError (status 413) + // before the SDK handler sees the body. Express's default error handler surfaces it. + expect(res.status).toBe(413); }); }); diff --git a/src/bridge/a2a/request-handler.ts b/src/bridge/a2a/request-handler.ts index d5fcd3d..bf999b5 100644 --- a/src/bridge/a2a/request-handler.ts +++ b/src/bridge/a2a/request-handler.ts @@ -7,6 +7,7 @@ * Plan 2A の自前 REST route と競合しない(path が異なる)。 * 両者は同じ `buildExtendedCard` + `computeEffectiveScope` を呼ぶため実装ドリフトは起きない。 */ +import express from 'express'; import type { Express } from 'express'; import { A2AError, DefaultRequestHandler, type ServerCallContext } from '@a2a-js/sdk/server'; import { jsonRpcHandler } from '@a2a-js/sdk/server/express'; @@ -19,6 +20,7 @@ import { buildBaseCard, buildExtendedCard } from './agent-card.js'; import { computeEffectiveScope } from './delegation.js'; import { makeA2aUserBuilder } from './user-builder.js'; import type { A2aPrincipal } from './token-auth.js'; +import { A2aLimiter, resolveA2aLimits, type A2aLimits } from './limiter.js'; export interface MountA2aRequestHandlerDeps { provider: Provider; @@ -26,6 +28,7 @@ export interface MountA2aRequestHandlerDeps { baseUrl: string; issuer: string; version: string; + limits?: Partial; } type CardDeps = { baseUrl: string; issuer: string; version: string }; @@ -41,15 +44,39 @@ type CardDeps = { baseUrl: string; issuer: string; version: string }; * * sendMessage / sendMessageStream はゲートしない:executor.execute() が * principal のチェックを行い、unauthorized ならタスクを failed 状態で終了させる(既存の保護)。 + * + * Task 7: limiter が注入された場合、認証チェックの後に rate-limit と + * resubscribe スロットキャップを適用する(auth → rate → slot の順序を保証)。 */ export class AuthGatedRequestHandler extends DefaultRequestHandler { + private limiter?: A2aLimiter; + + constructor( + agentCard: ConstructorParameters[0], + taskStore: ConstructorParameters[1], + executor: ConstructorParameters[2], + extendedCardProvider?: ConstructorParameters[6], + limiter?: A2aLimiter, + ) { + super(agentCard, taskStore, executor, undefined, undefined, undefined, extendedCardProvider); + this.limiter = limiter; + } + override async getTask(params: Parameters[0], context?: ServerCallContext) { this.requireAuthenticated(context); + const grantId = this.meteredGrantId(context); + if (this.limiter && !this.limiter.tryConsumeRate(grantId!)) { + throw A2AError.invalidRequest('rate limit exceeded'); + } return super.getTask(params, context); } override async cancelTask(params: Parameters[0], context?: ServerCallContext) { this.requireAuthenticated(context); + const grantId = this.meteredGrantId(context); + if (this.limiter && !this.limiter.tryConsumeRate(grantId!)) { + throw A2AError.invalidRequest('rate limit exceeded'); + } return super.cancelTask(params, context); } @@ -57,10 +84,97 @@ export class AuthGatedRequestHandler extends DefaultRequestHandler { * 認証ゲートを同期的に実行してから super.resubscribe() を返す。 * 同期 throw にすることで、JsonRpcTransportHandler がジェネレータを取得する前に * エラーを捕捉できる(SSE ヘッダ送信前に正規の JSON-RPC エラーレスポンスを返せる)。 + * + * limiter 注入時: + * 1. auth check (sync throw) + * 2. rate check (sync throw) + * 3. slot acquire (sync throw if cap reached) + * 4. wrap inner generator — finally releases slot on any exit path */ override resubscribe(params: Parameters[0], context?: ServerCallContext) { + // 1. Auth — must be first and synchronous this.requireAuthenticated(context); - return super.resubscribe(params, context); + // Fail-closed: with a limiter active, an authenticated caller missing a grantId is denied. + const grantId = this.meteredGrantId(context); + + // No limiter → unmetered (e.g. test without limiter / metering off) + if (!this.limiter) { + return super.resubscribe(params, context); + } + + // 2. Rate limit (limiter present ⟹ grantId is defined, else meteredGrantId threw) + if (!this.limiter.tryConsumeRate(grantId!)) { + throw A2AError.invalidRequest('rate limit exceeded'); + } + + // 3. Slot cap — synchronous acquire before returning generator + if (!this.limiter.tryAcquireResubscribe(grantId!)) { + throw A2AError.invalidRequest('too many concurrent resubscribe streams'); + } + + // 4. Wrap inner generator with a real wall-clock race so the stream-duration cap + // fires even when the inner stream emits no events (quiet-stream problem). + // Release slot in finally on every exit path. + const inner = super.resubscribe(params, context); + const limiter = this.limiter; + const maxMs = this.limiter.limits.maxStreamSeconds * 1000; + async function* wrapped() { + // Idempotent release: guards against the JS generator edge case where a consumer + // calls .return() before the first .next() — in that case the generator body's + // finally block does not execute. In practice the SDK SSE path always calls + // .next() before .return(), so this window is unreachable in production. + let released = false; + function release() { + if (!released) { released = true; limiter.releaseResubscribe(grantId!); } + } + const deadline = Date.now() + maxMs; + const it = inner[Symbol.asyncIterator](); + try { + while (true) { + const remaining = deadline - Date.now(); + // Fire-and-forget the inner .return(): a `.return()` issued while a prior + // `.next()` is still pending is queued behind it and does not settle until + // that next() settles — which never happens for a quiet stream (the exact + // case this cap targets). Awaiting it here would suspend `wrapped()` forever, + // skip the `finally` below, and leak the resubscribe slot. Instead we drop the + // slot immediately via `return` (→ finally → release) and let the inner + // generator wind down on its own. + if (remaining <= 0) { void it.return?.(undefined)?.catch(() => {}); return; } + let timer: ReturnType | undefined; + const timeout = new Promise<'timeout'>(res => { + timer = setTimeout(() => res('timeout'), remaining); + }); + const step = await Promise.race([it.next(), timeout]).finally(() => { + if (timer !== undefined) clearTimeout(timer); + }); + if (step === 'timeout') { void it.return?.(undefined)?.catch(() => {}); return; } + if (step.done) return; + yield step.value; + } + } finally { + release(); + } + } + return wrapped() as ReturnType; + } + + private extractGrantId(context?: ServerCallContext): string | undefined { + const p = (context?.user as any)?.a2aPrincipal as A2aPrincipal | undefined; + return p?.grantId; + } + + /** + * Resolve the grantId for metering. When a limiter is active (a2a metering enabled), + * an authenticated caller MUST carry a grantId — a missing one is treated as deny + * (fail-closed), never as "skip the limits". Without a limiter (tests / metering off) + * the caller is simply unmetered and undefined is returned. + */ + private meteredGrantId(context?: ServerCallContext): string | undefined { + const grantId = this.extractGrantId(context); + if (this.limiter && !grantId) { + throw A2AError.invalidRequest('unauthorized: missing delegation grant'); + } + return grantId; } private requireAuthenticated(context?: ServerCallContext): void { @@ -113,8 +227,10 @@ export function makeExtendedCardProvider( * 認証は makeA2aUserBuilder(UserBuilder)が担う。 */ export function mountA2aRequestHandler(app: Express, deps: MountA2aRequestHandlerDeps): void { + const limits = resolveA2aLimits(deps.limits); + const limiter = new A2aLimiter(limits); const taskStore = new SqliteA2aTaskStore(deps.repo); - const executor = new MaestroA2aExecutor(deps.repo); + const executor = new MaestroA2aExecutor(deps.repo, { limiter }); const cardDeps: CardDeps = { baseUrl: deps.baseUrl, issuer: deps.issuer, @@ -126,11 +242,13 @@ export function mountA2aRequestHandler(app: Express, deps: MountA2aRequestHandle baseCard, taskStore, executor, - undefined, // eventBusManager (default) - undefined, // pushNotificationStore (not used) - undefined, // pushNotificationSender (not used) - extendedCardProvider, // 7th arg: extended card via JWT principal + extendedCardProvider, // 4th arg: forwarded to super's 7th slot + limiter, // 5th arg: shared rate/stream limiter (Task 7) ); const userBuilder = makeA2aUserBuilder(deps.provider, deps.repo); + // body-size limit: must precede jsonRpcHandler so oversized bodies are rejected + // before the SDK parser sees them. body-parser skips re-parsing when req.body is + // already set, so the SDK's own express.json() inside jsonRpcHandler won't double-parse. + app.use('/a2a', express.json({ limit: limits.maxPayloadBytes })); app.use('/a2a', jsonRpcHandler({ requestHandler: handler, userBuilder })); } diff --git a/src/bridge/admin-gateway-keys-mount.ts b/src/bridge/admin-gateway-keys-mount.ts new file mode 100644 index 0000000..730a0ef --- /dev/null +++ b/src/bridge/admin-gateway-keys-mount.ts @@ -0,0 +1,64 @@ +import express from 'express'; +import { Repository } from '../db/repository.js'; +import { logger } from '../logger.js'; +import { requireAdmin } from './auth.js'; +import { createAdminGatewayApi } from './admin-gateway-api.js'; + +/** + * AAO Gateway Phase 2a: admin-only CRUD over gateway_virtual_keys, extracted + * verbatim from createCoreServer (bridge/server.ts). Enabled regardless of + * gateway.enabled so an admin can prep keys before flipping the gateway on. + * + * SECURITY: this endpoint mints `sk-aao-*` bearer tokens that grant + * access to the LLM gateway. Unlike the generic admin-api (which + * exposes user-management read paths that are safe to surface in + * auth-disabled local dev), key issuance has direct production + * impact. If we mounted it without `requireAdmin` when auth is off, + * any caller reaching the server could POST to /api/admin/gateway/keys + * and walk away with a valid gateway bearer. Refuse to mount instead + * — operators who want key management MUST configure auth.providers. + */ +export function mountAdminGatewayKeysApi( + app: express.Application, + deps: { repo: Repository; authActive: boolean }, +): void { + const { repo, authActive } = deps; + if (authActive) { + app.use( + '/api/admin/gateway/keys', + express.json({ limit: '4kb' }), + requireAdmin, + createAdminGatewayApi({ + repo, + // Already gated above; pass a passthrough so the router doesn't + // try to double-guard (which would just be a no-op anyway). + requireAdmin: (_req, _res, next) => next(), + getUserId: (req) => { + const u = (req as import('express').Request & { + user?: { id?: string }; + }).user; + return u?.id ?? null; + }, + // Phase 3a F4: when the admin server runs in the same process + // as the gateway, gateway/bootstrap.ts hangs the shared cache + // off the Repository so mutations invalidate live cache state. + // Cross-process setups omit this; the cache's 5s TTL bounds + // the worst-case stale window. + keyCache: (repo as unknown as { __gatewayKeyCache?: import('../gateway/key-cache.js').KeyCache }) + .__gatewayKeyCache, + // Phase 3b post-review: same-process gateway hangs its metrics + // handle on the Repository so admin mutations can drop the + // per-key gauge labels (revoke/rotate/delete). No-op in + // cross-process deploys (the gateway process owns its own + // registry there). + gatewayMetrics: (repo as unknown as { __gatewayMetrics?: import('../metrics/gateway-metrics.js').GatewayMetrics }) + .__gatewayMetrics, + }), + ); + } else { + logger.warn( + '[admin-gateway] /api/admin/gateway/keys NOT mounted (auth disabled). ' + + 'Configure auth.providers in config.yaml to enable virtual key management.', + ); + } +} diff --git a/src/bridge/auth-subsystem.ts b/src/bridge/auth-subsystem.ts new file mode 100644 index 0000000..a37cf9b --- /dev/null +++ b/src/bridge/auth-subsystem.ts @@ -0,0 +1,172 @@ +import express, { Request, Response } from 'express'; +import { Repository } from '../db/repository.js'; +import { logger } from '../logger.js'; +import { loadConfig } from '../config.js'; +import type { AuthConfig } from '../config.js'; +import { ConfigManager } from '../config-manager.js'; +import { resolveBranding } from './branding-api.js'; +import { + setupAuth, + requireAuth, + requireAdmin, + isProviderConfigured, + isLocalEnabled, + buildChangePasswordHandler, + DEFAULT_SESSION_SECRET_PATH, +} from './auth.js'; + +/** + * Auth activation + middleware pipeline, extracted verbatim from + * createCoreServer (bridge/server.ts). Two entry points: + * + * - resolveAuthActivation: decides whether auth is active (fail-closed on a + * partial OAuth config) and seeds the synthetic 'local' user in no-auth mode. + * - mountAuthPipeline: registers session/passport middleware, /auth routes, + * /api/auth/* endpoints and the requireAuth/requireAdmin guards for the + * /api/* namespaces. Call ONLY when auth is active — registration order + * relative to all later mounts is preserved by the call site in server.ts. + */ + +/** Cookie `secure` must be set whenever the user-facing scheme is https — + * via an upstream TLS proxy (secureCookie) OR native TLS termination. */ +export function computeEffectiveSecureCookie(secureCookie: boolean, tlsEnabled: boolean): boolean { + return secureCookie || tlsEnabled; +} + +export function resolveAuthActivation( + repo: Repository, + authConfig: AuthConfig | undefined, +): { authActive: boolean; localEnabled: boolean } { + // Auth activates when a provider is COMPLETELY configured (clientId + + // clientSecret + callbackUrl, plus baseUrl for Gitea). + // + // FAIL CLOSED on a partial config: if the operator clearly INTENDED auth + // (a provider has a client_id) but it's incomplete (typo'd / missing + // secret/callback), we must NOT silently fall back to no-auth — that would + // fail OPEN and expose /api/local, /api/config, etc. without authentication. + // Refuse to start instead, with a clear message. (A bare `auth` block with no + // client_id at all is treated as genuine no-auth mode.) + const _authProviders = authConfig?.providers; + const authUsable = + isProviderConfigured(_authProviders?.google, 'google') || + isProviderConfigured(_authProviders?.gitea, 'gitea'); + // "Intended" = ANY OAuth field is present on a provider (not just client_id). + // Saving only client_secret/callback_url/base_url, or clearing client_id by + // mistake, must still fail closed rather than drop to no-auth. + const _hasAnyField = (p?: { clientId?: string; clientSecret?: string; callbackUrl?: string; baseUrl?: string }) => + !!(p?.clientId || p?.clientSecret || p?.callbackUrl || p?.baseUrl); + const authIntended = _hasAnyField(_authProviders?.google) || _hasAnyField(_authProviders?.gitea); + // Local email+password is a first-class auth mode: when enabled, auth is + // active even without any OAuth provider. + const localEnabled = !!authConfig && isLocalEnabled(authConfig); + // Fail closed on a partial OAuth config ONLY when it would otherwise drop to + // no-auth. If local auth is on, auth is already active (no fail-open), so a + // half-configured OAuth provider just stays inactive rather than aborting boot. + if (authIntended && !authUsable && !localEnabled) { + throw new Error( + '[auth] auth is partially configured: a provider has a client_id but is missing ' + + 'client_secret / callback_url (Gitea also needs base_url). Refusing to start in an ' + + 'insecure no-auth state — complete the provider config or remove it from config.yaml.', + ); + } + const authActive = authUsable || localEnabled; + if (!authActive) { + // No-auth single-user mode: per-user rows are owned by the synthetic + // 'local' id, and several tables FK to users(id) (ssh_user_deks, + // browser_session_profiles, …). Seed the 'local' user row so those inserts + // succeed — without it, creating an SSH connection failed with create_failed. + repo.ensureLocalUser(); + } + return { authActive, localEnabled }; +} + +export function mountAuthPipeline( + app: express.Application, + deps: { + repo: Repository; + authConfig: AuthConfig | undefined; + configManager?: ConfigManager; + localEnabled: boolean; + /** serverCfg.tls.enabled — resolved once in createCoreServer. */ + tlsEnabled: boolean; + }, +): import('./auth.js').UpgradeAuthChecker | undefined { + const { repo, authConfig, configManager, localEnabled, tlsEnabled } = deps; + + // Idempotently seed the shared `local` system admin (id='local', the same + // owner the no-auth path uses) so an existing single-user / no-auth + // deployment gains a login mid-stream and keeps its `local`-owned data. + const bootstrap = authConfig?.local?.bootstrapAdmin; + if (localEnabled && bootstrap?.email && bootstrap?.password) { + repo.upsertLocalSystemAdmin({ email: bootstrap.email, password: bootstrap.password }); + logger.info(`[auth] seeded local system admin id=local email=${bootstrap.email}`); + } + + // Compose effective secureCookie: native TLS termination also requires + // the secure flag on session cookies, even when no upstream proxy is + // present. IMPORTANT: trust-proxy (set near the top of createCoreServer) + // stays keyed on the ORIGINAL opts.authConfig.secureCookie — native TLS + // must NOT enable it. + const effectiveSecureCookie = computeEffectiveSecureCookie( + !!authConfig?.secureCookie, + tlsEnabled, + ); + const authConfigForSetup = authConfig + ? { ...authConfig, secureCookie: effectiveSecureCookie } + : authConfig; + + const auth = setupAuth( + repo, + authConfigForSetup!, + () => { + const b = resolveBranding(configManager); + return { appName: b.appName, loginPageTitle: b.loginPageTitle }; + }, + loadConfig()?.secrets?.sessionSecretPath ?? DEFAULT_SESSION_SECRET_PATH, + ); + const { sessionMiddleware, passportInit, passportSession, authRouter } = auth; + const authenticateUpgrade = auth.authenticateUpgrade; + + // Global session + passport middleware (BEFORE all routes) + app.use(sessionMiddleware); + app.use(passportInit); + app.use(passportSession); + + // Auth routes (unauthenticated access allowed) + app.use('/auth', authRouter); + + // /api/auth/me endpoint + app.get('/api/auth/me', requireAuth, (req: Request, res: Response) => { + res.json(req.user); + }); + + // Self-service password change for local accounts (see auth.ts). + app.post('/api/auth/password', requireAuth, express.json(), buildChangePasswordHandler(repo)); + + // Protect all API routes (except /api/version and /health) + app.use('/api/local', requireAuth); + app.use('/api/repos', requireAuth); + // /api/workers exposes endpoint URLs + proxy backend probing. Without + // auth, unauthenticated callers could (a) enumerate worker endpoints + // and (b) trigger upstream `/v1/models` fetches against any + // attacker-influenced endpoint (SSRF amplifier). Gate behind requireAuth + // — admin-only is unnecessary because the responses already strip + // sensitive fields (apiKey), but anonymous access must be blocked. + app.use('/api/workers', requireAuth); + + // Admin-only routes + app.use('/api/config', requireAdmin); + // /api/pieces: any authenticated user can GET (visibility-filtered); + // per-piece write authz (built-in/global-custom → admin, user-custom → owner) + // is enforced inside pieces-api.ts handlers. + app.use('/api/pieces', requireAuth); + app.use('/api/usage', requireAuth); + // 横断カレンダー: 認証時はログイン必須。可視スペースの絞り込みは + // Repository.getCrossSpaceCalendarMonth が viewer 単位で行う。 + app.use('/api/calendar', requireAuth); + // Scheduled tasks: any authenticated user can create/list (visibility-filtered). + // PATCH/DELETE owner-or-admin enforcement lives in the handlers (Task 14). + app.use('/api/scheduled-tasks', requireAuth); + + return authenticateUpgrade; +} diff --git a/src/bridge/bridge-gateway-mount.ts b/src/bridge/bridge-gateway-mount.ts new file mode 100644 index 0000000..fad853e --- /dev/null +++ b/src/bridge/bridge-gateway-mount.ts @@ -0,0 +1,102 @@ +import express, { Request, Response } from 'express'; +import { Repository } from '../db/repository.js'; +import { logger } from '../logger.js'; +import { loadConfig } from '../config.js'; +import { ConfigManager } from '../config-manager.js'; +import { requireAdmin } from './auth.js'; +import { registerShutdownHook } from './shutdown.js'; +import { mountGateway, type GatewayMountHandle } from './gateway-mount.js'; +import { readGatewayConfig } from '../gateway/config.js'; +import { createAdminGatewayStatusRouter } from './admin-gateway-status-api.js'; +import { resolveListenPort } from '../server/config.js'; + +/** + * Phase 3c — same-process AAO Gateway mount + the bridge `/health` fallback + + * the admin gateway status endpoint, extracted verbatim from createCoreServer + * (bridge/server.ts). The registration order of the three blocks (gateway + * gate BEFORE `/health` BEFORE status) is behavior-relevant and preserved. + */ +export function mountBridgeGateway( + app: express.Express, + deps: { + repo: Repository; + configManager?: ConfigManager; + /** Shared prom-client registry from setupMetricsSubsystem (null when metrics are disabled). */ + sharedPromRegistry: import('prom-client').Registry | null; + authActive: boolean; + listenPort?: number; + }, +): GatewayMountHandle | null { + const { repo, configManager, sharedPromRegistry, authActive, listenPort } = deps; + + // Phase 3c — same-process AAO Gateway mount. Always installs the + // dynamic 404 gate; the gateway sub-app only comes alive when + // `gateway.enabled: true` in config.yaml (or after an admin flips it + // ON via Settings → Gateway Server). Pure no-op when no ConfigManager + // is available (tests / scripts run without one). + let gatewayMount: GatewayMountHandle | null = null; + if (configManager) { + gatewayMount = mountGateway({ + app, + configManager, + repo, + // Per CRITICAL-2: the gateway now owns its own BackendStatusRegistry + // over gateway.backends[] rather than reusing the worker registry + // (which probes provider.workers[] — different IDs). Same-host + // double-probe is intentional and cheap. + // Reuse the worker's prom-client registry so gateway counters + // appear on the same /metrics endpoint (no port collision). + promRegistry: sharedPromRegistry, + metricsPrefix: 'aao_gateway', + }); + // Apply the boot-time config so a server starting with + // `gateway.enabled: true` brings the gateway up immediately — + // no need to re-save config from the UI. + const bootGateway = readGatewayConfig(configManager.getConfig()); + gatewayMount.applyConfig(bootGateway).catch((e) => { + logger.warn(`[bridge-gateway] initial applyConfig threw: ${e instanceof Error ? e.message : String(e)}`); + }); + // Drain in-flight gateway streams on process shutdown so SIGTERM + // doesn't strand SSE clients with a half-written response. + registerShutdownHook('bridge-gateway', async () => { + try { await gatewayMount!.stop(); } catch { /* noop */ } + }); + } else { + logger.info('[bridge-gateway] not mounted (no ConfigManager — hot reload unavailable)'); + } + + // CRITICAL-3 fix: bridge `/health` fallback. Registered AFTER the + // gateway gate so when gateway.enabled=true the gate dispatches + // `/health` into the gateway sub-app (LiteLLM-compat + // `healthy_endpoints` / `unhealthy_endpoints` JSON shape). When the + // gateway is off, the gate falls through to here and the bridge + // answers with the legacy `{status:'ok'}` shape ops scripts rely on. + app.get('/health', (_req: Request, res: Response) => { + res.json({ status: 'ok' }); + }); + + // Admin status endpoint for the Gateway Server UI. Read-only — the + // form drives state changes through PUT /api/config (the + // config-changed listener inside mountGateway picks them up). + // Requires admin when auth is active; in auth-off dev mode the + // endpoint is open like the rest of /api/admin/* health/status reads. + { + // Prefer the explicit listenPort threaded through CoreServerOptions + // (startCoreServer always sets it) over the PORT env-var guess so + // the status endpoint matches what the bridge actually bound. + // env-var fallback is kept for callers that bypass startCoreServer. + const actualPort = listenPort ?? resolveListenPort(process.env['PORT'], loadConfig().server?.port); + const statusRouter = createAdminGatewayStatusRouter({ + mount: gatewayMount, + configManager: configManager ?? null, + workerPort: actualPort, + }); + if (authActive) { + app.use('/api/admin/gateway/status', requireAdmin, statusRouter); + } else { + app.use('/api/admin/gateway/status', statusRouter); + } + } + + return gatewayMount; +} diff --git a/src/bridge/jobs-api.ts b/src/bridge/jobs-api.ts new file mode 100644 index 0000000..46ac48a --- /dev/null +++ b/src/bridge/jobs-api.ts @@ -0,0 +1,35 @@ +import express, { Request, Response } from 'express'; +import { Repository } from '../db/repository.js'; +import { requireAuth } from './auth.js'; + +/** + * Job detail API (`GET /api/jobs/:jobId`), extracted verbatim from + * createCoreServer (bridge/server.ts). Registration order is preserved by + * the mount call site in server.ts. + */ +export function mountJobsApi( + app: express.Application, + deps: { repo: Repository; authActive: boolean }, +): void { + const { repo, authActive } = deps; + + // --- Job detail API --- + // Gate on viewer: getJob returns null for jobs the caller cannot see (Task 10 + 16). + // When auth is inactive, req.user is undefined → repository falls back to 1=1 (no filter). + const jobDetailHandlers: express.RequestHandler[] = []; + if (authActive) jobDetailHandlers.push(requireAuth); + jobDetailHandlers.push(async (req: Request, res: Response) => { + try { + const viewer = (req.user as Express.User | undefined) ?? undefined; + const job = await repo.getJob(req.params.jobId, viewer ? { viewer } : undefined); + if (!job) { + res.status(404).json({ error: 'Job not found' }); + return; + } + res.json(job); + } catch { + res.status(500).json({ error: 'Failed to fetch job' }); + } + }); + app.get('/api/jobs/:jobId', ...jobDetailHandlers); +} diff --git a/src/bridge/local-tasks-api.ts b/src/bridge/local-tasks-api.ts index fc9ba90..cd498ec 100644 --- a/src/bridge/local-tasks-api.ts +++ b/src/bridge/local-tasks-api.ts @@ -5,6 +5,7 @@ import { type LocalTasksDeps } from './local-tasks-shared.js'; import { registerLocalTaskCrudRoutes } from './local-tasks-crud-api.js'; import { registerLocalTaskCommentsRoutes } from './local-tasks-comments-api.js'; import { registerLocalTaskToolRequestRoutes } from './local-tasks-tool-requests-api.js'; +import { registerLocalTaskPackageRequestRoutes } from './local-tasks-package-requests-api.js'; import { registerLocalTaskControlRoutes } from './local-tasks-control-api.js'; import { registerLocalTaskStreamRoutes } from './local-tasks-stream-api.js'; @@ -61,6 +62,13 @@ export interface LocalTasksApiOptions { }) => Promise; /** Timeout (ms) for a prompt-coach evaluation before it is aborted. Default 30000. */ evaluatePromptTimeoutMs?: number; + /** + * Package-request mechanism (PR3): returns the current Python packages config + * so an approval can install into the task's space overlay. Called per + * request so config changes take effect without a restart. When unset, + * approving a package request fails with 400 (feature disabled). + */ + getPythonPackagesConfig?: () => import('../config.js').PythonPackagesConfigShape | undefined; } /** @@ -86,6 +94,7 @@ export function mountLocalTasksApi(app: Application, opts: LocalTasksApiOptions) registerLocalTaskCrudRoutes(app, deps); registerLocalTaskToolRequestRoutes(app, deps); + registerLocalTaskPackageRequestRoutes(app, deps); registerLocalTaskCommentsRoutes(app, deps); registerLocalTaskControlRoutes(app, deps); registerLocalTaskStreamRoutes(app, deps); diff --git a/src/bridge/local-tasks-comments-api.ts b/src/bridge/local-tasks-comments-api.ts index 49a153c..ef5190e 100644 --- a/src/bridge/local-tasks-comments-api.ts +++ b/src/bridge/local-tasks-comments-api.ts @@ -126,8 +126,9 @@ export function registerLocalTaskCommentsRoutes(app: Application, deps: LocalTas // leaves no orphan files in input/) and again after attachments (below) // to close the TOCTOU where the job flips to tool_request mid-upload. const earlyJob = await repo.getLatestJobForIssue(localTaskRepoName(taskId), taskId); - if (earlyJob && earlyJob.status === 'waiting_human' && earlyJob.waitReason === 'tool_request') { - res.status(409).json({ error: '保留中のツール要求があります。先に許可または拒否してください。' }); + if (earlyJob && earlyJob.status === 'waiting_human' + && (earlyJob.waitReason === 'tool_request' || earlyJob.waitReason === 'package_request')) { + res.status(409).json({ error: '保留中の承認要求があります。先に許可または拒否してください。' }); return; } @@ -204,10 +205,10 @@ export function registerLocalTaskCommentsRoutes(app: Application, deps: LocalTas browserSessionProfileId: task!.browserSessionProfileId ?? null, spaceId: task!.spaceId ?? null, }, { blockOnToolRequestPause: true }); - // Blocked by a tool-approval pause → return BEFORE persisting the comment - // so a rejected post leaves no orphan comment tied to no job. + // Blocked by a tool/package-approval pause → return BEFORE persisting the + // comment so a rejected post leaves no orphan comment tied to no job. if (blockedByToolRequest) { - res.status(409).json({ error: '保留中のツール要求があります。先に許可または拒否してください。' }); + res.status(409).json({ error: '保留中の承認要求があります。先に許可または拒否してください。' }); return; } diff --git a/src/bridge/local-tasks-package-requests-api.test.ts b/src/bridge/local-tasks-package-requests-api.test.ts new file mode 100644 index 0000000..88782ad --- /dev/null +++ b/src/bridge/local-tasks-package-requests-api.test.ts @@ -0,0 +1,184 @@ +/** + * Authz + gating tests for /api/local/tasks/:taskId/package-requests(/:id/decide). + * + * These cover the paths that DO NOT trigger a real pip install (view gate, + * write gate, feature gate, deny, already-decided) so the suite stays hermetic + * — no network / no bwrap / no python3. The install/overlay mechanics are + * unit-tested in engine/python-packages.test.ts, and the shared add path in + * space-api.python-packages.test.ts. + */ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { mkdtempSync, rmSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { randomUUID } from 'node:crypto'; +import express from 'express'; +import request from 'supertest'; +import { Repository, localTaskRepoName } from '../db/repository.js'; +import { registerLocalTaskPackageRequestRoutes } from './local-tasks-package-requests-api.js'; +import type { LocalTasksDeps } from './local-tasks-shared.js'; +import type { PythonPackagesConfigShape } from '../config.js'; + +function asUser(u: { id: string; role: 'admin' | 'user' }): Express.User { + return { id: u.id, role: u.role, orgIds: [] } as unknown as Express.User; +} + +function makeApp(repo: Repository, currentUser: Express.User | undefined, pkgCfg?: PythonPackagesConfigShape) { + const app = express(); + app.use((req: express.Request, _res, next) => { if (currentUser) (req as { user?: Express.User }).user = currentUser; next(); }); + const deps = { + repo, + userFolderRoot: tmpdir(), + noAuthOwner: undefined, + opts: { repo, authActive: true, getPythonPackagesConfig: () => pkgCfg }, + } as unknown as LocalTasksDeps; + registerLocalTaskPackageRequestRoutes(app, deps); + return app; +} + +let repo: Repository; +let dir: string; + +beforeEach(() => { + dir = mkdtempSync(join(tmpdir(), 'pkgreq-api-')); + repo = new Repository(join(dir, `${randomUUID()}.db`)); +}); +afterEach(() => { + repo.close(); + rmSync(dir, { recursive: true, force: true }); +}); + +async function seed() { + const owner = repo.createUser({ email: 'o@e.com', name: 'O', role: 'user', status: 'active' }); + const viewer = repo.createUser({ email: 'v@e.com', name: 'V', role: 'user', status: 'active' }); + const stranger = repo.createUser({ email: 's@e.com', name: 'S', role: 'user', status: 'active' }); + // Private space so a non-member truly cannot see the task (→ 404), while a + // viewer member can see but not edit (→ 403). + const space = await repo.createSpace({ kind: 'case', title: 'T', ownerId: owner.id, visibility: 'private' }); + await repo.addSpaceMember({ spaceId: space.id, userId: viewer.id, role: 'viewer' }); + const task = await repo.createLocalTask({ + title: 't', body: 'b', pieceName: 'chat', ownerId: owner.id, visibility: 'private', spaceId: space.id, + }); + const reqId = repo.recordPackageRequest({ + taskId: String(task.id), jobId: null, spaceId: space.id, + spec: 'requests==2.32.3', normalizedName: 'requests', reason: 'http', + }); + return { owner, viewer, stranger, space, task, reqId }; +} + +const ENABLED: PythonPackagesConfigShape = { enabled: true } as PythonPackagesConfigShape; +const DISABLED: PythonPackagesConfigShape = { enabled: false } as PythonPackagesConfigShape; + +describe('package-requests API', () => { + it('GET requires view permission (stranger gets 404/403)', async () => { + const fx = await seed(); + const app = makeApp(repo, asUser(fx.stranger), ENABLED); + const res = await request(app).get(`/api/local/tasks/${fx.task.id}/package-requests`); + expect([403, 404]).toContain(res.status); + }); + + it('GET lists requests for the owner', async () => { + const fx = await seed(); + const app = makeApp(repo, asUser(fx.owner), ENABLED); + const res = await request(app).get(`/api/local/tasks/${fx.task.id}/package-requests`); + expect(res.status).toBe(200); + expect(res.body.packageRequests).toHaveLength(1); + expect(res.body.packageRequests[0].spec).toBe('requests==2.32.3'); + }); + + it('decide rejects a bad decision value', async () => { + const fx = await seed(); + const app = makeApp(repo, asUser(fx.owner), ENABLED); + const res = await request(app).post(`/api/local/tasks/${fx.task.id}/package-requests/${fx.reqId}/decide`).send({ decision: 'maybe' }); + expect(res.status).toBe(400); + }); + + it('a space viewer (editor未満) cannot decide → 403', async () => { + const fx = await seed(); + const app = makeApp(repo, asUser(fx.viewer), ENABLED); + const res = await request(app).post(`/api/local/tasks/${fx.task.id}/package-requests/${fx.reqId}/decide`).send({ decision: 'approve' }); + expect(res.status).toBe(403); + // untouched + expect(repo.getPackageRequest(fx.reqId)?.status).toBe('pending'); + }); + + it('a non-member cannot decide → 404 (task not visible)', async () => { + const fx = await seed(); + const app = makeApp(repo, asUser(fx.stranger), ENABLED); + const res = await request(app).post(`/api/local/tasks/${fx.task.id}/package-requests/${fx.reqId}/decide`).send({ decision: 'deny' }); + expect(res.status).toBe(404); + }); + + it('owner can deny → request denied, resume no-ops without a job', async () => { + const fx = await seed(); + const app = makeApp(repo, asUser(fx.owner), ENABLED); + const res = await request(app).post(`/api/local/tasks/${fx.task.id}/package-requests/${fx.reqId}/decide`).send({ decision: 'deny' }); + expect(res.status).toBe(200); + expect(res.body.decision).toBe('deny'); + expect(repo.getPackageRequest(fx.reqId)?.status).toBe('denied'); + }); + + it('deciding an already-decided request → 409', async () => { + const fx = await seed(); + repo.decidePackageRequest(fx.reqId, { status: 'denied', decidedBy: fx.owner.id }); + const app = makeApp(repo, asUser(fx.owner), ENABLED); + const res = await request(app).post(`/api/local/tasks/${fx.task.id}/package-requests/${fx.reqId}/decide`).send({ decision: 'approve' }); + expect(res.status).toBe(409); + }); + + it('approve with the feature disabled → 400 (no install attempted)', async () => { + const fx = await seed(); + const app = makeApp(repo, asUser(fx.owner), DISABLED); + const res = await request(app).post(`/api/local/tasks/${fx.task.id}/package-requests/${fx.reqId}/decide`).send({ decision: 'approve' }); + expect(res.status).toBe(400); + expect(repo.getPackageRequest(fx.reqId)?.status).toBe('pending'); + }); + + it('admin (not a member) can decide', async () => { + const fx = await seed(); + const admin = repo.createUser({ email: 'a@e.com', name: 'A', role: 'admin', status: 'active' }); + const app = makeApp(repo, asUser(admin), ENABLED); + const res = await request(app).post(`/api/local/tasks/${fx.task.id}/package-requests/${fx.reqId}/decide`).send({ decision: 'deny' }); + expect(res.status).toBe(200); + expect(repo.getPackageRequest(fx.reqId)?.status).toBe('denied'); + }); + + it('deny re-queues a job parked with wait_reason=package_request (park/resume contract)', async () => { + // Regression guard for the worker-park contract: a package-approval pause + // MUST be persisted as wait_reason='package_request' for resume to work. + const fx = await seed(); + const job = await repo.createJob({ + repo: localTaskRepoName(fx.task.id), issueNumber: fx.task.id, + instruction: 'x', pieceName: 'chat', ownerId: fx.owner.id, spaceId: fx.space.id, + }); + repo.getDb().prepare(`UPDATE jobs SET status='waiting_human', wait_reason='package_request' WHERE id = ?`).run(job.id); + const reqId = repo.recordPackageRequest({ + taskId: String(fx.task.id), jobId: job.id, spaceId: fx.space.id, + spec: 'httpx', normalizedName: 'httpx', + }); + const app = makeApp(repo, asUser(fx.owner), ENABLED); + const res = await request(app).post(`/api/local/tasks/${fx.task.id}/package-requests/${reqId}/decide`).send({ decision: 'deny' }); + expect(res.status).toBe(200); + expect(res.body.resumed).toBe(true); + expect((await repo.getJob(job.id))?.status).toBe('queued'); + }); + + it('does NOT resume a job parked without wait_reason=package_request', async () => { + // The wait_reason gate must be exact: a job parked for a different reason is + // never re-queued by resumePackageRequestJob. + const fx = await seed(); + const job = await repo.createJob({ + repo: localTaskRepoName(fx.task.id), issueNumber: fx.task.id, + instruction: 'x', pieceName: 'chat', ownerId: fx.owner.id, spaceId: fx.space.id, + }); + repo.getDb().prepare(`UPDATE jobs SET status='waiting_human', wait_reason='tool_request' WHERE id = ?`).run(job.id); + const reqId = repo.recordPackageRequest({ + taskId: String(fx.task.id), jobId: job.id, spaceId: fx.space.id, spec: 'httpx', normalizedName: 'httpx', + }); + const app = makeApp(repo, asUser(fx.owner), ENABLED); + const res = await request(app).post(`/api/local/tasks/${fx.task.id}/package-requests/${reqId}/decide`).send({ decision: 'deny' }); + expect(res.status).toBe(200); + expect(res.body.resumed).toBe(false); // wait_reason mismatch → not re-queued + expect((await repo.getJob(job.id))?.status).toBe('waiting_human'); + }); +}); diff --git a/src/bridge/local-tasks-package-requests-api.ts b/src/bridge/local-tasks-package-requests-api.ts new file mode 100644 index 0000000..5d0d9cf --- /dev/null +++ b/src/bridge/local-tasks-package-requests-api.ts @@ -0,0 +1,163 @@ +import express, { type Application, type Request, type Response } from 'express'; +import { logger } from '../logger.js'; +import { parseTaskId } from './validation.js'; +import { canViewTask, resolveSpaceAccess } from './local-api-helpers.js'; +import { type LocalTasksDeps, resolveTaskWriteGate } from './local-tasks-shared.js'; +import { + resolvePythonPackagesConfig, + parseAndValidateSpec, + assertNotShadowing, +} from '../engine/python-packages.js'; +import { isBwrapAvailable } from '../engine/tools/sandbox.js'; +import { addPackageToSpace } from './space-python-packages-service.js'; + +/** + * Package-request mechanism (PR3): list a task's recorded Python package + * requests, and approve/deny a pending one. + * + * SECURITY MODEL: + * - Listing needs task VIEW permission. + * - Deciding needs task WRITE permission (owner / admin / space editor) — an + * agent can NEVER approve its own request. + * - Approving installs ONLY through the shared installSpacePackages path + * (bwrap-isolated / wheels-only / fixed index / stdlib+preinstalled + * shadowing guards / index URL never leaked), and re-validates the stored + * spec (defense in depth). The install is idempotent (content-addressed + + * name-deduped), so a double approve never double-installs. + */ +export function registerLocalTaskPackageRequestRoutes(app: Application, deps: LocalTasksDeps): void { + const { repo } = deps; + + // List the package requests recorded for a task (RequestPackage declarations). + app.get('/api/local/tasks/:taskId/package-requests', async (req: Request, res: Response) => { + try { + const taskId = parseTaskId(req.params.taskId); + if (taskId === null) { res.status(400).json({ error: 'Invalid task ID' }); return; } + const viewer = req.user as Express.User | undefined; + const task = await repo.getLocalTask(taskId, viewer ? { viewer } : undefined); + if (!canViewTask(req, res, task, resolveSpaceAccess(repo, task, viewer))) return; + res.json({ packageRequests: repo.listPackageRequestsByTask(String(taskId)) }); + } catch (err) { + logger.error(`Package requests list API error: ${err}`); + res.status(500).json({ error: 'Failed to list package requests' }); + } + }); + + // Approve (install into the space overlay + resume) or deny a pending package + // request. Requires task write permission. + app.post('/api/local/tasks/:taskId/package-requests/:reqId/decide', express.json(), async (req: Request, res: Response) => { + try { + const taskId = parseTaskId(req.params.taskId); + if (taskId === null) { res.status(400).json({ error: 'Invalid task ID' }); return; } + const reqId = String(req.params.reqId); + const decision = (req.body as { decision?: unknown })?.decision; + if (decision !== 'approve' && decision !== 'deny') { + res.status(400).json({ error: 'decision must be "approve" or "deny"' }); + return; + } + const viewer = req.user as Express.User | undefined; + const task = await repo.getLocalTask(taskId, viewer ? { viewer } : undefined); + const gate = await resolveTaskWriteGate(repo, viewer, task ? { ownerId: task.ownerId, spaceId: task.spaceId } : null); + if (gate !== 'allow') { + res.status(gate === 'view-only' ? 403 : 404) + .json({ error: gate === 'view-only' ? 'No permission to decide package requests' : 'Task not found' }); + return; + } + const pr = repo.getPackageRequest(reqId); + if (!pr || pr.taskId !== String(taskId)) { res.status(404).json({ error: 'Package request not found' }); return; } + if (pr.status !== 'pending') { res.status(409).json({ error: `Already decided (${pr.status})` }); return; } + + const decidedBy = viewer?.id ?? null; + + if (decision === 'deny') { + // CAS: if a concurrent approve already claimed it, don't resume-as-denied. + if (!repo.decidePackageRequest(reqId, { status: 'denied', decidedBy })) { + res.status(409).json({ error: 'Already decided' }); + return; + } + const resumed = pr.jobId ? repo.resumePackageRequestJob(pr.jobId) : 0; + res.json({ ok: true, decision, spec: pr.spec, resumed: resumed > 0 }); + return; + } + + // ---- approve ---- + // Validate everything cheap BEFORE claiming so a bad config doesn't burn + // the pending state. + if (!pr.spaceId) { + // Requests are only recorded with a space (worker gate), so this is a + // defensive guard: without a space there is nowhere to persist the + // desired-state / overlay. + res.status(422).json({ error: 'This request has no workspace to install into.' }); + return; + } + const cfg = resolvePythonPackagesConfig(deps.opts.getPythonPackagesConfig?.()); + if (!cfg.enabled) { + res.status(400).json({ error: 'Python package management is disabled (config: python_packages.enabled)' }); + return; + } + // Re-validate the stored spec (defense in depth — the DB holds a string). + let parsed; + try { + parsed = parseAndValidateSpec(pr.spec); + assertNotShadowing(parsed.normalizedName); + } catch (e) { + res.status(400).json({ error: `invalid stored spec: ${(e as Error).message}` }); + return; + } + const bwrapAvailable = await isBwrapAvailable(); + if (!bwrapAvailable) { + // Installs are fail-closed: never run pip without the isolated sandbox. + res.status(503).json({ error: 'The bwrap sandbox is unavailable on this server, so packages cannot be installed safely.' }); + return; + } + + // CLAIM the request (CAS pending→approved) BEFORE installing. This closes + // the race where a concurrent deny marks it denied while we install: if + // the claim loses (another decide won), we abort WITHOUT installing. On + // install failure we revert to pending so it can be retried. + if (!repo.decidePackageRequest(reqId, { status: 'approved', decidedBy })) { + res.status(409).json({ error: 'Already decided' }); + return; + } + + // ownerId is only a fallback arg to spaceKeyFor (ignored when a space id + // is present), but fetch it for parity with the admin form. + const space = await repo.getSpace(pr.spaceId, viewer ? { viewer } : undefined); + let result; + try { + result = await addPackageToSpace({ + repo, + cfg, + spaceId: pr.spaceId, + ownerId: space?.ownerId ?? null, + parsed, + bwrapAvailable, + }); + } catch (e) { + repo.revertPackageRequestToPending(reqId); + throw e; + } + if ('installError' in result || 'tooMany' in result || 'versionConflict' in result) { + // Revert so the (still-parked) job can be re-approved / denied after a fix. + repo.revertPackageRequestToPending(reqId); + if ('tooMany' in result) { + res.status(400).json({ error: `too many packages (max ${cfg.maxPackagesPerSpace})` }); + } else if ('versionConflict' in result) { + res.status(409).json({ error: `already pinned to a different version: ${result.versionConflict}. Change it in Settings → Python first.` }); + } else { + res.status(422).json({ error: result.installError }); + } + return; + } + // `conflict` (already installed) OR `ok` are both success: the overlay + // already contains the wheel, so the request is satisfied. The claim above + // already set status=approved. + const resumed = pr.jobId ? repo.resumePackageRequestJob(pr.jobId) : 0; + logger.info(`[local-tasks] package request approved task=${taskId} spec=${pr.spec} space=${pr.spaceId}`); + res.json({ ok: true, decision, spec: pr.spec, resumed: resumed > 0 }); + } catch (err) { + logger.error(`Package request decide API error: ${err}`); + res.status(500).json({ error: 'Failed to decide package request' }); + } + }); +} diff --git a/src/bridge/metrics-subsystem.ts b/src/bridge/metrics-subsystem.ts new file mode 100644 index 0000000..8d7a697 --- /dev/null +++ b/src/bridge/metrics-subsystem.ts @@ -0,0 +1,52 @@ +import express from 'express'; +import { logger } from '../logger.js'; +import { ConfigManager } from '../config-manager.js'; +import { createWorkerRegistry } from '../metrics/registry.js'; +import { createWorkerMetrics, type WorkerMetrics } from '../metrics/worker-metrics.js'; +import { createMetricsHandler } from '../metrics/http-handler.js'; + +/** + * Phase 3b: Prometheus /metrics endpoint, extracted verbatim from + * createCoreServer (bridge/server.ts). Mounted BEFORE any auth middleware so + * the standard Prometheus scrape job works without a session cookie. + * Restrict scrape access at the reverse proxy / firewall layer (see help + * doc). Disabled by provider.metrics.enabled=false in config.yaml. + */ +export function setupMetricsSubsystem( + app: express.Application, + configManager: ConfigManager | undefined, +): { + workerMetrics: WorkerMetrics | null; + /** Phase 3c: shared prom-client registry so the same-process gateway mount + * can register its gateway_* counters into the same /metrics endpoint + * (one scrape serves both worker and gateway). Null when metrics are + * disabled. */ + sharedPromRegistry: import('prom-client').Registry | null; +} { + const appConfig = (() => { + try { return configManager?.getConfig() ?? null; } catch { return null; } + })(); + const metricsCfg = appConfig?.provider?.metrics; + let workerMetrics: WorkerMetrics | null = null; + let sharedPromRegistry: import('prom-client').Registry | null = null; + if (metricsCfg?.enabled !== false) { + const prefix = metricsCfg?.prefix ?? 'aao_worker'; + sharedPromRegistry = createWorkerRegistry(prefix); + workerMetrics = createWorkerMetrics(sharedPromRegistry, prefix); + // Phase 3b post-review: gate /metrics on (1) bearer token if set, + // (2) otherwise client-IP allowlist (default localhost). Labels + // like `worker_id` / `backend_id` would leak otherwise. + const metricsAuth = { + bearerToken: metricsCfg?.bearerToken, + allowedHosts: metricsCfg?.allowedHosts, + }; + app.get('/metrics', createMetricsHandler(sharedPromRegistry, metricsAuth)); + const authMode = metricsAuth.bearerToken + ? 'bearer' + : `ip-allowlist=${(metricsAuth.allowedHosts ?? ['127.0.0.1', '::1', 'localhost']).join(',')}`; + logger.info(`[bridge] worker metrics enabled prefix=${prefix} auth=${authMode}`); + } else { + logger.info('[bridge] worker metrics disabled (provider.metrics.enabled=false)'); + } + return { workerMetrics, sharedPromRegistry }; +} diff --git a/src/bridge/novnc-mount.ts b/src/bridge/novnc-mount.ts new file mode 100644 index 0000000..f833294 --- /dev/null +++ b/src/bridge/novnc-mount.ts @@ -0,0 +1,37 @@ +import express from 'express'; +import { Repository } from '../db/repository.js'; +import { createNovncRouter, type NovncSessionAuthorizer } from './novnc-proxy.js'; +import { canUserSeeTask } from './visibility.js'; + +/** + * noVNC static hosting + the session authorizer closure, extracted verbatim + * from createCoreServer (bridge/server.ts). + */ +export function mountNovncStatic(app: express.Application): void { + // noVNC static files + app.use('/novnc', createNovncRouter()); +} + +/** + * CAPTCHA Pool は admin、Task Session はタスク visibility で認可。 + * novnc-proxy は Repository を直接知らずに済むよう、判定ロジックは + * ここでクロージャに包んで渡す。 + */ +export function buildNovncSessionAuthorizer(repo: Repository): NovncSessionAuthorizer { + return async (session, user) => { + if (session.kind === 'pool') { + return user.role === 'admin'; + } + if (session.kind === 'task' && session.taskId) { + const taskIdNum = Number(session.taskId); + if (!Number.isFinite(taskIdNum)) return false; + const task = await repo.getLocalTask(taskIdNum); + if (!task) return false; + const spaceAccessible = task.spaceId ? repo.userCanViewSpace(task.spaceId, user) : false; + return canUserSeeTask(user, task, spaceAccessible); + } + // legacy: kind 未設定 / taskId なし → 旧来の owner-or-admin + const isOwner = session.userId === user.id; + return isOwner || user.role === 'admin'; + }; +} diff --git a/src/bridge/server-route-order.test.ts b/src/bridge/server-route-order.test.ts new file mode 100644 index 0000000..a19b608 --- /dev/null +++ b/src/bridge/server-route-order.test.ts @@ -0,0 +1,88 @@ +/** + * Characterization test pinning the TOP-LEVEL Express layer registration + * order of createCoreServer. Express dispatches in registration order, so + * extracting inline route blocks out of server.ts must keep this fingerprint + * byte-identical — any drift means the refactor changed observable routing + * behavior, not just structure. + * + * Two boots are pinned: + * 1. minimal no-auth boot ({ repo, worktreeDir }) — the same shape + * server-subsystems.test.ts uses. + * 2. auth-active boot (local auth) + ConfigManager — exercises the auth + * middleware pipeline, /api/* guards, setup/config mounts and the + * gateway mount/health/status ordering. + */ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdtempSync, rmSync, writeFileSync } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import type { Application } from 'express'; +import { Repository } from '../db/repository.js'; +import { ConfigManager } from '../config-manager.js'; +import { createCoreServer } from './server.js'; +import type { AuthConfig } from '../config.js'; + +interface Layer { + route?: { path: string; methods: Record }; + name?: string; + regexp?: RegExp; +} + +/** Ordered fingerprint of the app's top-level middleware/route layers. */ +function fingerprint(app: Application): string[] { + const stack: Layer[] = (app as unknown as { _router?: { stack: Layer[] } })._router?.stack ?? []; + return stack.map((layer) => { + if (layer.route) { + const methods = Object.keys(layer.route.methods).sort().join(','); + return `ROUTE ${methods} ${layer.route.path}`; + } + return `USE name=${layer.name ?? ''} path=${layer.regexp?.source ?? ''}`; + }); +} + +function mkAuthConfig(): AuthConfig { + return { + sessionSecret: 'test-secret', + sessionMaxAge: 3600_000, + secureCookie: false, + adminEmails: [], + providers: {}, + local: { enabled: true, allowSignup: false }, + } as AuthConfig; +} + +describe('createCoreServer route registration order', () => { + let tempDir: string; + let repo: Repository; + + beforeEach(() => { + tempDir = mkdtempSync(join(tmpdir(), 'route-order-')); + repo = new Repository(join(tempDir, 'db.sqlite')); + }); + + afterEach(() => { + delete process.env.MCP_ENCRYPTION_KEY; + repo.close(); + rmSync(tempDir, { recursive: true, force: true }); + }); + + it('pins the no-auth minimal boot layer order', () => { + const { app } = createCoreServer({ repo, worktreeDir: join(tempDir, 'wt') }); + expect(fingerprint(app)).toMatchSnapshot(); + }); + + it('pins the auth-active + ConfigManager boot layer order', () => { + const configPath = join(tempDir, 'config.yaml'); + writeFileSync(configPath, 'provider:\n workers: []\n'); + const configManager = new ConfigManager(configPath); + const { app, authActive } = createCoreServer({ + repo, + worktreeDir: join(tempDir, 'wt'), + authConfig: mkAuthConfig(), + configManager, + listenPort: 9876, + }); + expect(authActive).toBe(true); + expect(fingerprint(app)).toMatchSnapshot(); + }); +}); diff --git a/src/bridge/server.ts b/src/bridge/server.ts index ac0182b..d18b96f 100644 --- a/src/bridge/server.ts +++ b/src/bridge/server.ts @@ -1,47 +1,42 @@ import express, { Request, Response, NextFunction, RequestHandler } from 'express'; -import { existsSync, readFileSync } from 'fs'; -import { join, resolve, dirname } from 'path'; -import { fileURLToPath } from 'url'; +import { existsSync } from 'fs'; +import { join } from 'path'; import { Repository, BrowserSessionRepo } from '../db/repository.js'; import { logger } from '../logger.js'; import { loadConfig } from '../config.js'; import { ConfigManager } from '../config-manager.js'; -import { mountConfigApi } from './config-api.js'; -import { - mountSetupApi, - buildNoAuthConfigGate, - newNoAuthConfigGateState, - ensureSetupToken, - clearSetupToken, -} from './setup-api.js'; +import { mountSetupAndConfigApis } from './setup-config-mount.js'; import { mountPiecesApi } from './pieces-api.js'; import { resolveSpaceFolder } from '../spaces/folder-resolver.js'; import { mountToolsApi } from './tools-api.js'; import { mountSkillsApi } from './skills-api.js'; import { mountNotificationsApi } from './notifications-api.js'; import { mountScheduledTasksApi } from './scheduled-tasks-api.js'; -import { mountBrandingApi, resolveBranding } from './branding-api.js'; +import { mountBrandingApi } from './branding-api.js'; import { createBrowserApi } from './browser-api.js'; import { createBrowserSessionApi } from './browser-session-api.js'; import { createSubtaskActivityRouter } from './subtask-activity-api.js'; import { createDelegateRunsRouter } from './delegate-runs-api.js'; import { createUsageRouter } from './usage-api.js'; import { SessionManager } from '../engine/browser-session.js'; -import { createNovncRouter, setupNovncWebSocketProxy } from './novnc-proxy.js'; +import { setupNovncWebSocketProxy } from './novnc-proxy.js'; +import { mountNovncStatic, buildNovncSessionAuthorizer } from './novnc-mount.js'; import { setSessionManager } from '../engine/tools/browser.js'; import { setUserFolderToolDeps } from '../engine/tools/user-folder.js'; import { setSkillToolDeps } from '../engine/tools/skills.js'; import { setAppDocsDeps } from '../engine/tools/app-docs.js'; -import { setupAuth, requireAuth, requireAdmin, isProviderConfigured, isLocalEnabled, buildChangePasswordHandler, DEFAULT_SESSION_SECRET_PATH } from './auth.js'; -import { canUserSeeTask } from './visibility.js'; +import { requireAuth, requireAdmin } from './auth.js'; +import { resolveAuthActivation, mountAuthPipeline } from './auth-subsystem.js'; import { mountAdminApi } from './admin-api.js'; -import { createAdminGatewayApi } from './admin-gateway-api.js'; +import { mountAdminGatewayKeysApi } from './admin-gateway-keys-mount.js'; import { mountUsersApi, mountUsersPickableApi } from './users-api.js'; import { mountShareApi } from './share-api.js'; import { mountAppShareApi } from './app-share-api.js'; import { mountMetaApi } from './meta-api.js'; import { securityHeadersMiddleware } from './security-headers.js'; import { mountLocalTasksApi } from './local-tasks-api.js'; +import { mountJobsApi } from './jobs-api.js'; +import { mountUiStatic } from './ui-static.js'; import { findPieceFile } from './pieces-api.js'; import { mountLocalFilesApi } from './local-files-api.js'; import { mountSubtaskFilesApi } from './subtask-files-api.js'; @@ -56,33 +51,23 @@ import { mountAppHarnessApi } from './app-harness-api.js'; import { createCrossCalendarApi } from './cross-calendar-api.js'; import { registerShutdownHook, installSignalHandlers } from './shutdown.js'; import { createBackendStatusRegistry, type BackendStatusRegistry } from '../engine/backend-status-registry.js'; -import { createWorkerRegistry } from '../metrics/registry.js'; -import { createWorkerMetrics, type WorkerMetrics } from '../metrics/worker-metrics.js'; -import { createMetricsHandler } from '../metrics/http-handler.js'; +import type { WorkerMetrics } from '../metrics/worker-metrics.js'; +import { setupMetricsSubsystem } from './metrics-subsystem.js'; import { buildDirectProbe, buildProxyProbe } from '../engine/backend-probes.js'; import { startTrashCleanup } from '../user-folder/trash-cleanup.js'; import { userPiecesDir } from '../user-folder/paths.js'; import { startReflectionRetentionSweep } from '../engine/reflection/retention.js'; import type { AuthConfig } from '../config.js'; import { attachConsoleWs } from './console-ws-api.js'; -import { mountGateway, type GatewayMountHandle } from './gateway-mount.js'; -import { readGatewayConfig } from '../gateway/config.js'; -import { createA2aOidcProvider, mountA2aOidc } from './a2a/oidc-provider.js'; -import { createA2aClientsAdminRouter } from './a2a/a2a-clients-admin-api.js'; -import { createA2aRouter, createSpaceA2aAdminRouter } from './a2a/a2a-api.js'; -import { mountA2aRequestHandler } from './a2a/request-handler.js'; -import { A2aTaskReconciler } from './a2a/reconciler.js'; -import { createUserDelegationsRouter, createAdminDelegationsRouter } from './a2a/delegations-api.js'; -import { createAdminGatewayStatusRouter } from './admin-gateway-status-api.js'; +import type { GatewayMountHandle } from './gateway-mount.js'; +import { mountBridgeGateway } from './bridge-gateway-mount.js'; +import { setupA2aSubsystem } from './a2a-subsystem.js'; import { createServer as createHttpsServer } from 'https'; import { X509Certificate } from 'crypto'; import { mergeServerConfig, resolveListenPort } from '../server/config.js'; import { resolveTlsOptions } from '../net/tls-options.js'; import { createHttpRedirectServer } from '../net/http-redirect.js'; -const __filenameServer = fileURLToPath(import.meta.url); -const __dirnameServer = dirname(__filenameServer); - export interface CoreServerOptions { repo: Repository; worktreeDir?: string; @@ -179,35 +164,7 @@ export function createCoreServer(opts: CoreServerOptions): { // session cookie. Restrict scrape access at the reverse proxy / // firewall layer (see help doc). Disabled by // provider.metrics.enabled=false in config.yaml. - const appConfig = (() => { - try { return opts.configManager?.getConfig() ?? null; } catch { return null; } - })(); - const metricsCfg = appConfig?.provider?.metrics; - let workerMetrics: WorkerMetrics | null = null; - // Phase 3c: hoist the promRegistry handle out of the metrics-enabled - // block so the same-process gateway mount below can register its - // gateway_* counters into the same /metrics endpoint (one scrape - // serves both worker and gateway). Null when metrics are disabled. - let sharedPromRegistry: import('prom-client').Registry | null = null; - if (metricsCfg?.enabled !== false) { - const prefix = metricsCfg?.prefix ?? 'aao_worker'; - sharedPromRegistry = createWorkerRegistry(prefix); - workerMetrics = createWorkerMetrics(sharedPromRegistry, prefix); - // Phase 3b post-review: gate /metrics on (1) bearer token if set, - // (2) otherwise client-IP allowlist (default localhost). Labels - // like `worker_id` / `backend_id` would leak otherwise. - const metricsAuth = { - bearerToken: metricsCfg?.bearerToken, - allowedHosts: metricsCfg?.allowedHosts, - }; - app.get('/metrics', createMetricsHandler(sharedPromRegistry, metricsAuth)); - const authMode = metricsAuth.bearerToken - ? 'bearer' - : `ip-allowlist=${(metricsAuth.allowedHosts ?? ['127.0.0.1', '::1', 'localhost']).join(',')}`; - logger.info(`[bridge] worker metrics enabled prefix=${prefix} auth=${authMode}`); - } else { - logger.info('[bridge] worker metrics disabled (provider.metrics.enabled=false)'); - } + const { workerMetrics, sharedPromRegistry } = setupMetricsSubsystem(app, opts.configManager); // JSON body parser for Config/Pieces/Memory/Reflection API endpoints app.use('/api/config', express.json()); @@ -215,47 +172,10 @@ export function createCoreServer(opts: CoreServerOptions): { app.use('/api/local/memory', express.json()); app.use('/api/local/reflection', express.json()); - // === Auth setup === - // Auth activates when a provider is COMPLETELY configured (clientId + - // clientSecret + callbackUrl, plus baseUrl for Gitea). - // - // FAIL CLOSED on a partial config: if the operator clearly INTENDED auth - // (a provider has a client_id) but it's incomplete (typo'd / missing - // secret/callback), we must NOT silently fall back to no-auth — that would - // fail OPEN and expose /api/local, /api/config, etc. without authentication. - // Refuse to start instead, with a clear message. (A bare `auth` block with no - // client_id at all is treated as genuine no-auth mode.) - const _authProviders = opts.authConfig?.providers; - const authUsable = - isProviderConfigured(_authProviders?.google, 'google') || - isProviderConfigured(_authProviders?.gitea, 'gitea'); - // "Intended" = ANY OAuth field is present on a provider (not just client_id). - // Saving only client_secret/callback_url/base_url, or clearing client_id by - // mistake, must still fail closed rather than drop to no-auth. - const _hasAnyField = (p?: { clientId?: string; clientSecret?: string; callbackUrl?: string; baseUrl?: string }) => - !!(p?.clientId || p?.clientSecret || p?.callbackUrl || p?.baseUrl); - const authIntended = _hasAnyField(_authProviders?.google) || _hasAnyField(_authProviders?.gitea); - // Local email+password is a first-class auth mode: when enabled, auth is - // active even without any OAuth provider. - const localEnabled = !!opts.authConfig && isLocalEnabled(opts.authConfig); - // Fail closed on a partial OAuth config ONLY when it would otherwise drop to - // no-auth. If local auth is on, auth is already active (no fail-open), so a - // half-configured OAuth provider just stays inactive rather than aborting boot. - if (authIntended && !authUsable && !localEnabled) { - throw new Error( - '[auth] auth is partially configured: a provider has a client_id but is missing ' + - 'client_secret / callback_url (Gitea also needs base_url). Refusing to start in an ' + - 'insecure no-auth state — complete the provider config or remove it from config.yaml.', - ); - } - const authActive = authUsable || localEnabled; - if (!authActive) { - // No-auth single-user mode: per-user rows are owned by the synthetic - // 'local' id, and several tables FK to users(id) (ssh_user_deks, - // browser_session_profiles, …). Seed the 'local' user row so those inserts - // succeed — without it, creating an SSH connection failed with create_failed. - repo.ensureLocalUser(); - } + // === Auth setup === (see auth-subsystem.ts) + // Fail-closed activation decision: throws on a partially configured OAuth + // provider; seeds the synthetic 'local' user in no-auth mode. + const { authActive, localEnabled } = resolveAuthActivation(repo, opts.authConfig); let authenticateUpgrade: import('./auth.js').UpgradeAuthChecker | undefined; // Resolve server config ONCE here with the real listen port (threaded in via @@ -277,135 +197,24 @@ export function createCoreServer(opts: CoreServerOptions): { hstsPolicy = serverCfg.tls.enabled && serverCfg.tls.hsts ? 'enable' : 'clear'; if (authActive) { - // Idempotently seed the shared `local` system admin (id='local', the same - // owner the no-auth path uses) so an existing single-user / no-auth - // deployment gains a login mid-stream and keeps its `local`-owned data. - const bootstrap = opts.authConfig?.local?.bootstrapAdmin; - if (localEnabled && bootstrap?.email && bootstrap?.password) { - repo.upsertLocalSystemAdmin({ email: bootstrap.email, password: bootstrap.password }); - logger.info(`[auth] seeded local system admin id=local email=${bootstrap.email}`); - } - - // Compose effective secureCookie: native TLS termination also requires - // the secure flag on session cookies, even when no upstream proxy is - // present. IMPORTANT: trust-proxy (line ~191) stays keyed on the - // ORIGINAL opts.authConfig.secureCookie — native TLS must NOT enable it. - const effectiveSecureCookie = computeEffectiveSecureCookie( - !!opts.authConfig?.secureCookie, - serverCfg.tls.enabled, - ); - const authConfigForSetup = opts.authConfig - ? { ...opts.authConfig, secureCookie: effectiveSecureCookie } - : opts.authConfig; - - const auth = setupAuth( + // Session/passport middleware + /auth routes + /api/* guards + // (see auth-subsystem.ts — registered here, BEFORE all later mounts). + authenticateUpgrade = mountAuthPipeline(app, { repo, - authConfigForSetup!, - () => { - const b = resolveBranding(opts.configManager); - return { appName: b.appName, loginPageTitle: b.loginPageTitle }; - }, - loadConfig()?.secrets?.sessionSecretPath ?? DEFAULT_SESSION_SECRET_PATH, - ); - const { sessionMiddleware, passportInit, passportSession, authRouter } = auth; - authenticateUpgrade = auth.authenticateUpgrade; - - // Global session + passport middleware (BEFORE all routes) - app.use(sessionMiddleware); - app.use(passportInit); - app.use(passportSession); - - // Auth routes (unauthenticated access allowed) - app.use('/auth', authRouter); - - // /api/auth/me endpoint - app.get('/api/auth/me', requireAuth, (req: Request, res: Response) => { - res.json(req.user); + authConfig: opts.authConfig, + configManager: opts.configManager, + localEnabled, + tlsEnabled: serverCfg.tls.enabled, }); - - // Self-service password change for local accounts (see auth.ts). - app.post('/api/auth/password', requireAuth, express.json(), buildChangePasswordHandler(repo)); - - // Protect all API routes (except /api/version and /health) - app.use('/api/local', requireAuth); - app.use('/api/repos', requireAuth); - // /api/workers exposes endpoint URLs + proxy backend probing. Without - // auth, unauthenticated callers could (a) enumerate worker endpoints - // and (b) trigger upstream `/v1/models` fetches against any - // attacker-influenced endpoint (SSRF amplifier). Gate behind requireAuth - // — admin-only is unnecessary because the responses already strip - // sensitive fields (apiKey), but anonymous access must be blocked. - app.use('/api/workers', requireAuth); - - // Admin-only routes - app.use('/api/config', requireAdmin); - // /api/pieces: any authenticated user can GET (visibility-filtered); - // per-piece write authz (built-in/global-custom → admin, user-custom → owner) - // is enforced inside pieces-api.ts handlers. - app.use('/api/pieces', requireAuth); - app.use('/api/usage', requireAuth); - // 横断カレンダー: 認証時はログイン必須。可視スペースの絞り込みは - // Repository.getCrossSpaceCalendarMonth が viewer 単位で行う。 - app.use('/api/calendar', requireAuth); - // Scheduled tasks: any authenticated user can create/list (visibility-filtered). - // PATCH/DELETE owner-or-admin enforcement lives in the handlers (Task 14). - app.use('/api/scheduled-tasks', requireAuth); } // Admin user management API (always mounted; protected by requireAdmin when auth is active) app.use('/api/admin', express.json()); mountAdminApi(app, repo, authActive, loadConfig()?.userFolderRoot ?? './data/users'); - // AAO Gateway Phase 2a: admin-only CRUD over gateway_virtual_keys. - // Enabled regardless of gateway.enabled so an admin can prep keys - // before flipping the gateway on. - // - // SECURITY: this endpoint mints `sk-aao-*` bearer tokens that grant - // access to the LLM gateway. Unlike the generic admin-api (which - // exposes user-management read paths that are safe to surface in - // auth-disabled local dev), key issuance has direct production - // impact. If we mounted it without `requireAdmin` when auth is off, - // any caller reaching the server could POST to /api/admin/gateway/keys - // and walk away with a valid gateway bearer. Refuse to mount instead - // — operators who want key management MUST configure auth.providers. - if (authActive) { - app.use( - '/api/admin/gateway/keys', - express.json({ limit: '4kb' }), - requireAdmin, - createAdminGatewayApi({ - repo, - // Already gated above; pass a passthrough so the router doesn't - // try to double-guard (which would just be a no-op anyway). - requireAdmin: (_req, _res, next) => next(), - getUserId: (req) => { - const u = (req as import('express').Request & { - user?: { id?: string }; - }).user; - return u?.id ?? null; - }, - // Phase 3a F4: when the admin server runs in the same process - // as the gateway, gateway/bootstrap.ts hangs the shared cache - // off the Repository so mutations invalidate live cache state. - // Cross-process setups omit this; the cache's 5s TTL bounds - // the worst-case stale window. - keyCache: (repo as unknown as { __gatewayKeyCache?: import('../gateway/key-cache.js').KeyCache }) - .__gatewayKeyCache, - // Phase 3b post-review: same-process gateway hangs its metrics - // handle on the Repository so admin mutations can drop the - // per-key gauge labels (revoke/rotate/delete). No-op in - // cross-process deploys (the gateway process owns its own - // registry there). - gatewayMetrics: (repo as unknown as { __gatewayMetrics?: import('../metrics/gateway-metrics.js').GatewayMetrics }) - .__gatewayMetrics, - }), - ); - } else { - logger.warn( - '[admin-gateway] /api/admin/gateway/keys NOT mounted (auth disabled). ' + - 'Configure auth.providers in config.yaml to enable virtual key management.', - ); - } + // AAO Gateway Phase 2a: admin-only CRUD over gateway_virtual_keys + // (see admin-gateway-keys-mount.ts — refuses to mount when auth is off). + mountAdminGatewayKeysApi(app, { repo, authActive }); // /api/users/me/* routes — current viewer info (gated by requireAuth when auth is active) mountUsersApi(app, repo, authActive); @@ -422,26 +231,8 @@ export function createCoreServer(opts: CoreServerOptions): { // space-api と同じワークスペース解決(loadConfig().worktreeDir)を使う。 mountAppShareApi(app, repo, worktreeDir ?? loadConfig().worktreeDir ?? './data/worktrees'); - // Redirect root to UI - app.get('/', (_req: Request, res: Response) => { - res.redirect('/ui'); - }); - - const uiDistPath = resolve(join(__dirnameServer, '../../ui/dist')); - if (existsSync(uiDistPath)) { - app.use('/ui', express.static(uiDistPath)); - app.get('/ui/*', (_req, res) => { - res.sendFile(join(uiDistPath, 'index.html')); - }); - // Headless test-harness page for workspace-app E2E (Task 5/6). - // Must be registered BEFORE any SPA catch-all and BEFORE space-api routes - // so that the app-harness auth middleware (mounted below) sees the request. - // The harness JS/CSS chunks are served by the existing /ui static mount - // because Vite builds them with base: '/ui/' → /ui/assets/… paths. - app.get('/app-harness', (_req, res) => { - res.sendFile(join(uiDistPath, 'app-harness.html')); - }); - } + // Root redirect + UI static hosting + app-harness page (see ui-static.ts). + mountUiStatic(app); // Version + repos info endpoints (see meta-api.ts). Mounted here to preserve // ordering relative to the `/api/repos` requireAuth guard registered above. @@ -486,6 +277,8 @@ export function createCoreServer(opts: CoreServerOptions): { getMaxUploadMb: opts.configManager ? () => opts.configManager!.getConfig().tools?.taskUploadMaxSizeMb ?? 50 : () => loadConfig().tools?.taskUploadMaxSizeMb ?? 50, + // Package-request approvals install into the task's space overlay. + getPythonPackagesConfig: () => loadConfig().pythonPackages, }); // --- Local files API --- @@ -498,75 +291,14 @@ export function createCoreServer(opts: CoreServerOptions): { // 横断(全スペース)カレンダー: GET /api/calendar/cross app.use('/api/calendar', createCrossCalendarApi({ repo, authActive })); - // --- A2A OAuth2 Authorization Server --- - const a2aCfg = (loadConfig() as any).a2a ?? {}; - if (a2aCfg.enabled) { - if (!authActive) { - logger.warn('[a2a-oidc] a2a.enabled but auth is not active; consent flow requires login and will reject all requests'); - } - try { - const sessionSecretPath = loadConfig()?.secrets?.sessionSecretPath ?? DEFAULT_SESSION_SECRET_PATH; - const cookieKeys = [readFileSync(sessionSecretPath, 'utf-8').trim()]; - const secretsDir = dirname(resolve(sessionSecretPath)); - const provider = createA2aOidcProvider({ - repo, - secretsDir, - issuer: a2aCfg.issuer, - resourceAudience: a2aCfg.resourceAudience, - cookieKeys, - }); - mountA2aOidc(app, provider, { repo, resourceAudience: a2aCfg.resourceAudience }); - app.use('/api/admin/a2a/clients', express.json(), requireAdmin, createA2aClientsAdminRouter(repo, authActive)); - // Agent Card ルータ (base card = public, extended card = self-authenticates via Bearer) - // baseUrl は resourceAudience の origin から導く(例: https://maestro.example.com/a2a → https://maestro.example.com) - // version は '1.0.0' 固定(生成済みバージョンファイルが存在しないため定数を使用) - const a2aBaseUrl = (() => { - try { return new URL(String(a2aCfg.resourceAudience)).origin; } catch { return String(a2aCfg.issuer); } - })(); - app.use(createA2aRouter({ provider, repo, baseUrl: a2aBaseUrl, issuer: String(a2aCfg.issuer), version: '1.0.0' })); - // JSON-RPC エンドポイント /a2a(SDK DefaultRequestHandler 経由) - // /a2a は /api 配下でないため requireAuth に掛からない。認証は UserBuilder が担う。 - mountA2aRequestHandler(app, { provider, repo, baseUrl: a2aBaseUrl, issuer: String(a2aCfg.issuer), version: '1.0.0' }); - // bridge は単一プロセス前提。複数プロセスが同一 DB を共有する構成では - // lease が必要になる(将来課題)。 - const a2aReconciler = new A2aTaskReconciler({ repo }); - a2aReconciler.start(); - registerShutdownHook('a2a-reconciler', async () => { a2aReconciler.stop(); }); - logger.info('[a2a] task reconciler started'); - // スペース A2A スキル許可リスト管理 API (/api/local は requireAuth 配下) - app.use('/api/local/spaces', createSpaceA2aAdminRouter(repo, authActive)); - // A2A 委任一覧・取消 API - // /api/local は L329 で requireAuth 済みのため追加ゲート不要 - app.use('/api/local/a2a/delegations', express.json(), createUserDelegationsRouter(repo, authActive)); - app.use('/api/admin/a2a/delegations', express.json(), requireAdmin, createAdminDelegationsRouter(repo, authActive)); - logger.info('[a2a-oidc] a2a authorization server enabled'); - } catch (err) { - logger.warn(`[a2a-oidc] failed to mount a2a authorization server: ${err}`); - } - } + // --- A2A OAuth2 Authorization Server (see a2a-subsystem.ts; no-op unless a2a.enabled) --- + setupA2aSubsystem(app, { repo, authActive }); // --- Subtask files API (listing MUST come before wildcard) --- mountSubtaskFilesApi(app, repo); - // --- Job detail API --- - // Gate on viewer: getJob returns null for jobs the caller cannot see (Task 10 + 16). - // When auth is inactive, req.user is undefined → repository falls back to 1=1 (no filter). - const jobDetailHandlers: express.RequestHandler[] = []; - if (authActive) jobDetailHandlers.push(requireAuth); - jobDetailHandlers.push(async (req: Request, res: Response) => { - try { - const viewer = (req.user as Express.User | undefined) ?? undefined; - const job = await repo.getJob(req.params.jobId, viewer ? { viewer } : undefined); - if (!job) { - res.status(404).json({ error: 'Job not found' }); - return; - } - res.json(job); - } catch { - res.status(500).json({ error: 'Failed to fetch job' }); - } - }); - app.get('/api/jobs/:jobId', ...jobDetailHandlers); + // --- Job detail API (see jobs-api.ts) --- + mountJobsApi(app, { repo, authActive }); // NOTE: bridge `/health` (`{status:'ok'}`) is intentionally registered // LATER — see the "bridge /health fallback" block below the gateway @@ -575,58 +307,14 @@ export function createCoreServer(opts: CoreServerOptions): { // able to dispatch `/health` to the gateway sub-app when running // (LiteLLM-shape JSON — CRITICAL-3 fix). - // === Browser setup wizard (fresh no-auth install onboarding) === - // Token lifecycle + endpoints. The no-auth `/api/config` mutation gate - // (Codex P1 #3) must register BEFORE mountConfigApi so it intercepts - // PUT/POST while a setup window is open. See setup-api.ts. + // === Browser setup wizard (fresh no-auth install onboarding) + Config API === + // (see setup-config-mount.ts) if (opts.configManager) { - const setupDataDir = join(process.cwd(), 'data'); - const setupListenPort = - opts.listenPort ?? resolveListenPort(process.env['PORT'], loadConfig().server?.port); - // Synchronously seed the gate state to 'pending' (fail-closed for auth - // writes) BEFORE the async lifecycle below resolves, so a request arriving - // in the boot window can't slip an auth bootstrap through (Codex P1 #2). - const setupGateState = newNoAuthConfigGateState(); - - // Token lifecycle (needs the async setup-lib import). Created only on a - // fresh no-auth install (needs-setup); cleared otherwise so existing - // configured deployments keep their prior open behavior. On failure the - // gate stays 'pending' (fail-closed), never silently open. - void (async () => { - try { - if (authActive) { - clearSetupToken(setupDataDir); - return; - } - const { isLlmConfigured } = await import('../../scripts/setup-lib.mjs'); - const workers = opts.configManager?.getConfig().provider?.workers ?? []; - if (!isLlmConfigured(workers)) { - const token = ensureSetupToken(setupDataDir); - setupGateState.mode = 'token-required'; - logger.info(`[setup] open the UI and enter this setup token: ${token}`); - logger.info('[setup] llm configured=false → wizard active'); - } else { - clearSetupToken(setupDataDir); - setupGateState.mode = 'open'; - logger.info('[setup] llm configured=true → wizard off'); - } - } catch (e) { - logger.warn(`[setup] token lifecycle failed: ${String(e)}`); - } - })(); - - // No-auth /api/config auth-write gate (only mounted in no-auth mode). - if (!authActive) { - app.use('/api/config', buildNoAuthConfigGate(setupDataDir, setupGateState)); - } - - mountSetupApi(app, opts.configManager, { + mountSetupAndConfigApis(app, { + configManager: opts.configManager, authActive, - listenPort: setupListenPort, - dataDir: setupDataDir, + listenPort: opts.listenPort, }); - - mountConfigApi(app, opts.configManager); } // Branding 公開 API (GET は認証不要)。アップロード系は admin のみ。 @@ -800,75 +488,17 @@ export function createCoreServer(opts: CoreServerOptions): { backendStatusRegistry, })); - // Phase 3c — same-process AAO Gateway mount. Always installs the - // dynamic 404 gate; the gateway sub-app only comes alive when - // `gateway.enabled: true` in config.yaml (or after an admin flips it - // ON via Settings → Gateway Server). Pure no-op when no ConfigManager - // is available (tests / scripts run without one). - let gatewayMount: GatewayMountHandle | null = null; - if (opts.configManager) { - gatewayMount = mountGateway({ - app, - configManager: opts.configManager, - repo, - // Per CRITICAL-2: the gateway now owns its own BackendStatusRegistry - // over gateway.backends[] rather than reusing the worker registry - // (which probes provider.workers[] — different IDs). Same-host - // double-probe is intentional and cheap. - // Reuse the worker's prom-client registry so gateway counters - // appear on the same /metrics endpoint (no port collision). - promRegistry: sharedPromRegistry, - metricsPrefix: 'aao_gateway', - }); - // Apply the boot-time config so a server starting with - // `gateway.enabled: true` brings the gateway up immediately — - // no need to re-save config from the UI. - const bootGateway = readGatewayConfig(opts.configManager.getConfig()); - gatewayMount.applyConfig(bootGateway).catch((e) => { - logger.warn(`[bridge-gateway] initial applyConfig threw: ${e instanceof Error ? e.message : String(e)}`); - }); - // Drain in-flight gateway streams on process shutdown so SIGTERM - // doesn't strand SSE clients with a half-written response. - registerShutdownHook('bridge-gateway', async () => { - try { await gatewayMount!.stop(); } catch { /* noop */ } - }); - } else { - logger.info('[bridge-gateway] not mounted (no ConfigManager — hot reload unavailable)'); - } - - // CRITICAL-3 fix: bridge `/health` fallback. Registered AFTER the - // gateway gate so when gateway.enabled=true the gate dispatches - // `/health` into the gateway sub-app (LiteLLM-compat - // `healthy_endpoints` / `unhealthy_endpoints` JSON shape). When the - // gateway is off, the gate falls through to here and the bridge - // answers with the legacy `{status:'ok'}` shape ops scripts rely on. - app.get('/health', (_req: Request, res: Response) => { - res.json({ status: 'ok' }); + // Phase 3c — same-process AAO Gateway mount + bridge /health fallback + + // admin gateway status endpoint (see bridge-gateway-mount.ts; the + // gateway gate must register BEFORE /health — order preserved inside). + const gatewayMount: GatewayMountHandle | null = mountBridgeGateway(app, { + repo, + configManager: opts.configManager, + sharedPromRegistry, + authActive, + listenPort: opts.listenPort, }); - // Admin status endpoint for the Gateway Server UI. Read-only — the - // form drives state changes through PUT /api/config (the - // config-changed listener inside mountGateway picks them up). - // Requires admin when auth is active; in auth-off dev mode the - // endpoint is open like the rest of /api/admin/* health/status reads. - { - // Prefer the explicit listenPort threaded through CoreServerOptions - // (startCoreServer always sets it) over the PORT env-var guess so - // the status endpoint matches what the bridge actually bound. - // env-var fallback is kept for callers that bypass startCoreServer. - const actualPort = opts.listenPort ?? resolveListenPort(process.env['PORT'], loadConfig().server?.port); - const statusRouter = createAdminGatewayStatusRouter({ - mount: gatewayMount, - configManager: opts.configManager ?? null, - workerPort: actualPort, - }); - if (authActive) { - app.use('/api/admin/gateway/status', requireAdmin, statusRouter); - } else { - app.use('/api/admin/gateway/status', statusRouter); - } - } - // Wire user-folder tool deps so RunUserScript / ListUserAssets can decrypt sessions. setUserFolderToolDeps({ sessRepo, @@ -895,37 +525,17 @@ export function createCoreServer(opts: CoreServerOptions): { // Wire app-docs tool deps so GetMyOrchestratorState can introspect DB. setAppDocsDeps({ db: repo.getDb(), userFolderRoot }); - // noVNC static files - app.use('/novnc', createNovncRouter()); - - // CAPTCHA Pool は admin、Task Session はタスク visibility で認可。 - // novnc-proxy は Repository を直接知らずに済むよう、判定ロジックは - // ここでクロージャに包んで渡す。 - const authorizeNovncSession: import('./novnc-proxy.js').NovncSessionAuthorizer = async (session, user) => { - if (session.kind === 'pool') { - return user.role === 'admin'; - } - if (session.kind === 'task' && session.taskId) { - const taskIdNum = Number(session.taskId); - if (!Number.isFinite(taskIdNum)) return false; - const task = await repo.getLocalTask(taskIdNum); - if (!task) return false; - const spaceAccessible = task.spaceId ? repo.userCanViewSpace(task.spaceId, user) : false; - return canUserSeeTask(user, task, spaceAccessible); - } - // legacy: kind 未設定 / taskId なし → 旧来の owner-or-admin - const isOwner = session.userId === user.id; - return isOwner || user.role === 'admin'; - }; + // noVNC static files + session authorizer (see novnc-mount.ts) + mountNovncStatic(app); + const authorizeNovncSession: import('./novnc-proxy.js').NovncSessionAuthorizer = + buildNovncSessionAuthorizer(repo); return { app, browserSessionManager, authenticateUpgrade, authorizeNovncSession, sshConsole, backendStatusRegistry, workerMetrics, gatewayMount, authActive, serverConfig: serverCfg }; } -/** Cookie `secure` must be set whenever the user-facing scheme is https — - * via an upstream TLS proxy (secureCookie) OR native TLS termination. */ -export function computeEffectiveSecureCookie(secureCookie: boolean, tlsEnabled: boolean): boolean { - return secureCookie || tlsEnabled; -} +// Definition moved to auth-subsystem.ts; re-exported here so existing +// importers of bridge/server.js keep working unchanged. +export { computeEffectiveSecureCookie } from './auth-subsystem.js'; /** Heuristic: native TLS + the proxy signal (secure_cookie) likely means a * reverse proxy is also terminating TLS → double-TLS misconfiguration. */ @@ -1004,12 +614,15 @@ export function startCoreServer(opts: CoreServerOptions, port: number = 9876): v opts.workerManager.setWorkerMetrics(workerMetrics); } const finalApp = finalizeServer(app); - // Safe-by-default bind: loopback unless HOST is set explicitly. The agent API - // includes the Bash tool, so an unauthenticated instance reachable on the LAN - // is effectively unauthenticated RCE. Bare-metal / systemd installs inherit - // this loopback default; the Docker image sets HOST=0.0.0.0 (it must bind all - // interfaces inside the container) and is protected by the host-side - // 127.0.0.1:9876 port mapping instead. + // Bind host: loopback unless HOST is set explicitly. The agent API includes + // the Bash tool, so an unauthenticated instance reachable on the LAN is + // effectively unauthenticated RCE. Bare-metal / systemd installs inherit this + // loopback default. The Docker image sets HOST=0.0.0.0 (it must bind all + // interfaces inside the container) and docker-compose.yml publishes the port + // on all host interfaces by default, so a Docker deployment is LAN-reachable + // out of the box — enabling auth/TLS before exposing it is the operator's + // responsibility (see docs/docker.md, SECURITY.md). Restore local-only by + // pinning the compose mapping back to "127.0.0.1:9876:9876". const host = process.env['HOST'] ?? '127.0.0.1'; const isLoopbackBind = host === '127.0.0.1' || host === '::1' || host === 'localhost'; // Use the config snapshot already resolved in createCoreServer (same port, diff --git a/src/bridge/setup-config-mount.ts b/src/bridge/setup-config-mount.ts new file mode 100644 index 0000000..17509bb --- /dev/null +++ b/src/bridge/setup-config-mount.ts @@ -0,0 +1,81 @@ +import express from 'express'; +import { join } from 'path'; +import { logger } from '../logger.js'; +import { loadConfig } from '../config.js'; +import { ConfigManager } from '../config-manager.js'; +import { mountConfigApi } from './config-api.js'; +import { + mountSetupApi, + buildNoAuthConfigGate, + newNoAuthConfigGateState, + ensureSetupToken, + clearSetupToken, +} from './setup-api.js'; +import { resolveListenPort } from '../server/config.js'; + +/** + * Browser setup wizard (fresh no-auth install onboarding) + Config API, + * extracted verbatim from createCoreServer (bridge/server.ts). + * + * Token lifecycle + endpoints. The no-auth `/api/config` mutation gate + * (Codex P1 #3) must register BEFORE mountConfigApi so it intercepts + * PUT/POST while a setup window is open. See setup-api.ts. + */ +export function mountSetupAndConfigApis( + app: express.Application, + deps: { + configManager: ConfigManager; + authActive: boolean; + listenPort?: number; + }, +): void { + const { configManager, authActive, listenPort } = deps; + + const setupDataDir = join(process.cwd(), 'data'); + const setupListenPort = + listenPort ?? resolveListenPort(process.env['PORT'], loadConfig().server?.port); + // Synchronously seed the gate state to 'pending' (fail-closed for auth + // writes) BEFORE the async lifecycle below resolves, so a request arriving + // in the boot window can't slip an auth bootstrap through (Codex P1 #2). + const setupGateState = newNoAuthConfigGateState(); + + // Token lifecycle (needs the async setup-lib import). Created only on a + // fresh no-auth install (needs-setup); cleared otherwise so existing + // configured deployments keep their prior open behavior. On failure the + // gate stays 'pending' (fail-closed), never silently open. + void (async () => { + try { + if (authActive) { + clearSetupToken(setupDataDir); + return; + } + const { isLlmConfigured } = await import('../../scripts/setup-lib.mjs'); + const workers = configManager.getConfig().provider?.workers ?? []; + if (!isLlmConfigured(workers)) { + const token = ensureSetupToken(setupDataDir); + setupGateState.mode = 'token-required'; + logger.info(`[setup] open the UI and enter this setup token: ${token}`); + logger.info('[setup] llm configured=false → wizard active'); + } else { + clearSetupToken(setupDataDir); + setupGateState.mode = 'open'; + logger.info('[setup] llm configured=true → wizard off'); + } + } catch (e) { + logger.warn(`[setup] token lifecycle failed: ${String(e)}`); + } + })(); + + // No-auth /api/config auth-write gate (only mounted in no-auth mode). + if (!authActive) { + app.use('/api/config', buildNoAuthConfigGate(setupDataDir, setupGateState)); + } + + mountSetupApi(app, configManager, { + authActive, + listenPort: setupListenPort, + dataDir: setupDataDir, + }); + + mountConfigApi(app, configManager); +} diff --git a/src/bridge/skills-api.test.ts b/src/bridge/skills-api.test.ts index d17b548..eacc724 100644 --- a/src/bridge/skills-api.test.ts +++ b/src/bridge/skills-api.test.ts @@ -330,6 +330,92 @@ describe('DELETE /api/skills/:name', () => { }); }); +// --------------------------------------------------------------------------- +// Folder/frontmatter name mismatch: the catalog lists skills by their +// frontmatter `name`, but a skill's folder (or flat file) can carry a +// different name (e.g. installed via InstallSkill with a mismatched name). +// DELETE/PUT must resolve such skills by their catalog name instead of +// reconstructing the path from it — otherwise they are listed but can never +// be deleted or edited (404 forever). +// --------------------------------------------------------------------------- + +describe('folder/frontmatter name mismatch', () => { + function addMismatchedUserSkill(userId: string, folderName: string, fmName: string): void { + const dir = join(userRoot, userId, 'skills', folderName); + mkdirSync(dir, { recursive: true }); + writeFileSync(join(dir, 'SKILL.md'), SKILL_MD(fmName)); + } + + it('DELETE removes a user skill whose folder name differs from its frontmatter name', async () => { + addMismatchedUserSkill('user-1', 'folder-name', 'display-name'); + const app = makeApp(makeCatalog(), { id: 'user-1' }); + + // Listed under the frontmatter name. + const list = await request(app).get('/api/skills?scope=user'); + expect(list.body.skills.map((s: { name: string }) => s.name)).toContain('display-name'); + + const res = await request(app).delete('/api/skills/display-name?scope=user'); + expect(res.status).toBe(200); + expect(existsSync(join(userRoot, 'user-1', 'skills', 'folder-name'))).toBe(false); + }); + + it('DELETE removes a system skill whose folder name differs from its frontmatter name (admin)', async () => { + const dir = join(systemDir, 'sys-folder'); + mkdirSync(dir, { recursive: true }); + writeFileSync(join(dir, 'SKILL.md'), SKILL_MD('sys-display')); + + const res = await request(makeApp(makeCatalog(), { id: 'admin-1', role: 'admin' })) + .delete('/api/skills/sys-display?scope=system'); + expect(res.status).toBe(200); + expect(existsSync(dir)).toBe(false); + }); + + it('DELETE removes a flat-file skill whose file name differs from its frontmatter name', async () => { + const userSkillsDir = join(userRoot, 'user-1', 'skills'); + mkdirSync(userSkillsDir, { recursive: true }); + writeFileSync(join(userSkillsDir, 'odd-file.md'), SKILL_MD('flat-display')); + + const res = await request(makeApp(makeCatalog(), { id: 'user-1' })) + .delete('/api/skills/flat-display?scope=user'); + expect(res.status).toBe(200); + expect(existsSync(join(userSkillsDir, 'odd-file.md'))).toBe(false); + }); + + it('DELETE does not fall back to another scope: a user-only name is 404 under scope=system', async () => { + addMismatchedUserSkill('user-1', 'folder-name', 'display-name'); + const res = await request(makeApp(makeCatalog(), { id: 'admin-1', role: 'admin' })) + .delete('/api/skills/display-name?scope=system'); + expect(res.status).toBe(404); + expect(existsSync(join(userRoot, 'user-1', 'skills', 'folder-name'))).toBe(true); + }); + + it('POST rejects content whose frontmatter name differs from the skill name', async () => { + const res = await request(makeApp(makeCatalog(), { id: 'user-1' })) + .post('/api/skills') + .send({ name: 'param-name', scope: 'user', content: SKILL_MD('content-name') }); + expect(res.status).toBe(400); + expect(existsSync(join(userRoot, 'user-1', 'skills', 'param-name'))).toBe(false); + }); + + it('POST rejects content without a valid frontmatter name', async () => { + const res = await request(makeApp(makeCatalog(), { id: 'user-1' })) + .post('/api/skills') + .send({ name: 'no-fm', scope: 'user', content: '# heading only\nno frontmatter' }); + expect(res.status).toBe(400); + expect(existsSync(join(userRoot, 'user-1', 'skills', 'no-fm'))).toBe(false); + }); + + it('PUT updates a skill whose folder name differs from its frontmatter name', async () => { + addMismatchedUserSkill('user-1', 'folder-name', 'display-name'); + const res = await request(makeApp(makeCatalog(), { id: 'user-1' })) + .put('/api/skills/display-name?scope=user') + .send({ content: `---\nname: display-name\ndescription: updated\n---\n\nnew body` }); + expect(res.status).toBe(200); + const saved = readFileSync(join(userRoot, 'user-1', 'skills', 'folder-name', 'SKILL.md'), 'utf-8'); + expect(saved).toContain('new body'); + }); +}); + // --------------------------------------------------------------------------- // Spaces foundation: optional spaceId scoping (mirrors pieces-api) // - no spaceId → per-user skills dir (byte-identical regression) diff --git a/src/bridge/skills-api.ts b/src/bridge/skills-api.ts index b866997..4aeb3c6 100644 --- a/src/bridge/skills-api.ts +++ b/src/bridge/skills-api.ts @@ -1,9 +1,9 @@ import express, { type Application, type Request, type Response, type RequestHandler } from 'express'; import { readFileSync, writeFileSync, unlinkSync, existsSync, mkdirSync, readdirSync, lstatSync, renameSync, rmSync } from 'fs'; -import { join, relative } from 'path'; +import { join, relative, dirname } from 'path'; import { randomBytes } from 'crypto'; import type { SkillCatalog, SkillEntry } from '../engine/skills.js'; -import { VALID_SKILL_NAME } from '../engine/skills.js'; +import { VALID_SKILL_NAME, scanSkillsDir } from '../engine/skills.js'; import { scanSkillContent, scanSkillDirectory, maxSeverity } from '../engine/skills-scanner.js'; import matter from 'gray-matter'; import { logger } from '../logger.js'; @@ -67,6 +67,34 @@ function isAdmin(req: Request): boolean { return user?.role === 'admin'; } +/** + * Locate a skill on disk by its catalog (display) name. The catalog lists + * skills by their SKILL.md frontmatter `name`, which can differ from the + * folder/file name (e.g. installed under a different name). Path-by-name is + * tried first (also covers folders without parseable frontmatter), then the + * directory is scanned for an entry whose frontmatter name matches. All + * returned paths are direct children of `baseDir` by construction. + */ +function resolveSkillPaths( + baseDir: string, + name: string, + source: 'system' | 'user', +): { dirPath: string | null; filePath: string } | null { + const dirPath = join(baseDir, name); + if (existsSync(dirPath) && lstatSync(dirPath).isDirectory()) { + return { dirPath, filePath: join(dirPath, 'SKILL.md') }; + } + const flatPath = join(baseDir, `${name}.md`); + if (existsSync(flatPath) && lstatSync(flatPath).isFile()) { + return { dirPath: null, filePath: flatPath }; + } + const entry = scanSkillsDir(baseDir, source).find(e => e.name === name); + if (entry) { + return { dirPath: entry.dirPath, filePath: entry.filePath }; + } + return null; +} + /** * Recursively list files in a directory, skipping symlinks. * Returns paths relative to `baseDir`. @@ -313,6 +341,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v // System scope requires admin if (scope === 'system' && !isAdmin(req)) { + logger.warn(`[skills-api] create rejected skill=${name} scope=system actor=${getUserId(req)} reason=not_admin`); res.status(403).json({ error: 'Only admins can create system skills' }); return; } @@ -321,6 +350,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v const skillScope = await resolveSkillScope(req); if ('forbidden' in skillScope && skillScope.forbidden) { + logger.warn(`[skills-api] create rejected skill=${name} scope=${scope} actor=${userId} reason=space_not_found`); res.status(404).json({ error: 'Space not found' }); return; } @@ -334,10 +364,35 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v const destDirPath = join(destDir, name); const destFlatPath = join(destDir, `${name}.md`); if (existsSync(destDirPath) || existsSync(destFlatPath)) { + logger.warn(`[skills-api] create rejected skill=${name} scope=${scope} actor=${userId} reason=conflict`); res.status(409).json({ error: 'Skill already exists' }); return; } + // The catalog lists skills by their frontmatter `name`. Require valid + // frontmatter matching the requested name — otherwise the skill would be + // stored under one name but listed under another (or not listed at all), + // making it impossible to manage from the UI. + let fmName = ''; + try { + const parsed = matter(content); + fmName = typeof parsed.data?.name === 'string' ? parsed.data.name : ''; + } catch { + fmName = ''; + } + if (!fmName || !VALID_SKILL_NAME.test(fmName)) { + res.status(400).json({ + error: 'Content must begin with YAML frontmatter containing a valid "name" (lowercase alphanumeric, hyphens, underscores) — otherwise the skill will not appear in the skill list.', + }); + return; + } + if (fmName !== name) { + res.status(400).json({ + error: `Frontmatter name "${fmName}" must match the skill name "${name}".`, + }); + return; + } + // Scan content before writing const findings = scanSkillContent(content); const severity = maxSeverity(findings); @@ -364,6 +419,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v logger.info(`[skills-api] created skill=${name} scope=${scope} actor=${actor} severity=${severity}`); res.status(201).json({ name, scope, severity, findings }); } catch (e) { + logger.error(`[skills-api] create failed skill=${req.body?.name} actor=${getUserId(req)} error=${e}`); res.status(500).json({ error: `Failed to create skill: ${e}` }); } }); @@ -388,6 +444,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v // System scope requires admin if (scope === 'system' && !isAdmin(req)) { + logger.warn(`[skills-api] update rejected skill=${name} scope=system actor=${getUserId(req)} reason=not_admin`); res.status(403).json({ error: 'Only admins can edit system skills' }); return; } @@ -408,6 +465,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v const skillScope = await resolveSkillScope(req); if ('forbidden' in skillScope && skillScope.forbidden) { + logger.warn(`[skills-api] update rejected skill=${name} scope=${scope} actor=${userId} reason=space_not_found`); res.status(404).json({ error: 'Space not found' }); return; } @@ -416,18 +474,13 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v ? skillCatalog.getSystemDir() : userSkillDirFor(skillScope, userId); - // Find the skill file: either flat file or directory with SKILL.md - let targetPath: string | null = null; - const flatPath = join(baseDir, `${name}.md`); - const dirSkillPath = join(baseDir, name, 'SKILL.md'); - - if (existsSync(dirSkillPath)) { - targetPath = dirSkillPath; - } else if (existsSync(flatPath)) { - targetPath = flatPath; - } + // Find the SKILL.md to update: by folder/file name, falling back to a + // frontmatter-name scan (folder and display name can differ). + const resolved = resolveSkillPaths(baseDir, name, scope as 'system' | 'user'); + const targetPath = resolved && existsSync(resolved.filePath) ? resolved.filePath : null; if (!targetPath) { + logger.warn(`[skills-api] update rejected skill=${name} scope=${scope} actor=${userId} reason=not_found`); res.status(404).json({ error: 'Skill not found' }); return; } @@ -461,7 +514,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v const severity = maxSeverity(findings); // Atomic write: tmpfile in same directory as target → rename - const targetDir = targetPath === dirSkillPath ? join(baseDir, name) : baseDir; + const targetDir = dirname(targetPath); const tmpPath = join(targetDir, `.tmp-${randomBytes(8).toString('hex')}.md`); writeFileSync(tmpPath, content, 'utf-8'); renameSync(tmpPath, targetPath); @@ -482,6 +535,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v logger.info(`[skills-api] updated skill=${name} scope=${scope} actor=${actor} severity=${severity}`); res.json({ ok: true, severity, findings }); } catch (e) { + logger.error(`[skills-api] update failed skill=${name} actor=${getUserId(req)} error=${e}`); res.status(500).json({ error: `Failed to update skill: ${e}` }); } }); @@ -506,6 +560,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v // System scope requires admin if (scope === 'system' && !isAdmin(req)) { + logger.warn(`[skills-api] delete rejected skill=${name} scope=system actor=${getUserId(req)} reason=not_admin`); res.status(403).json({ error: 'Only admins can delete system skills' }); return; } @@ -515,6 +570,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v const skillScope = await resolveSkillScope(req); if ('forbidden' in skillScope && skillScope.forbidden) { + logger.warn(`[skills-api] delete rejected skill=${name} scope=${scope} actor=${userId} reason=space_not_found`); res.status(404).json({ error: 'Space not found' }); return; } @@ -523,24 +579,21 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v ? skillCatalog.getSystemDir() : userSkillDirFor(skillScope, userId); - // Find the skill: directory or flat file - const dirPath = join(baseDir, name); - const flatPath = join(baseDir, `${name}.md`); - let deleted = false; - - if (existsSync(dirPath) && lstatSync(dirPath).isDirectory()) { - rmSync(dirPath, { recursive: true, force: true }); - deleted = true; - } else if (existsSync(flatPath) && lstatSync(flatPath).isFile()) { - unlinkSync(flatPath); - deleted = true; - } - - if (!deleted) { + // Find the skill: by folder/file name, falling back to a frontmatter-name + // scan (folder and display name can differ — see resolveSkillPaths). + const resolved = resolveSkillPaths(baseDir, name, scope as 'system' | 'user'); + if (!resolved) { + logger.warn(`[skills-api] delete rejected skill=${name} scope=${scope} actor=${userId} reason=not_found`); res.status(404).json({ error: 'Skill not found' }); return; } + if (resolved.dirPath) { + rmSync(resolved.dirPath, { recursive: true, force: true }); + } else { + unlinkSync(resolved.filePath); + } + // Invalidate cache if (scope === 'system') { skillCatalog.refreshSystem(); @@ -557,6 +610,7 @@ export function mountSkillsApi(app: Application, opts: MountSkillsApiOptions): v logger.info(`[skills-api] deleted skill=${name} scope=${scope} actor=${actor}`); res.json({ ok: true }); } catch (e) { + logger.error(`[skills-api] delete failed skill=${name} actor=${getUserId(req)} error=${e}`); res.status(500).json({ error: `Failed to delete skill: ${e}` }); } }); diff --git a/src/bridge/space-python-packages-api.ts b/src/bridge/space-python-packages-api.ts index 758e207..d90b289 100644 --- a/src/bridge/space-python-packages-api.ts +++ b/src/bridge/space-python-packages-api.ts @@ -13,6 +13,12 @@ import { } from '../engine/python-packages.js'; import { isBwrapAvailable } from '../engine/tools/sandbox.js'; import type { SpaceApiDeps } from './space-api.js'; +import { + readState, + withSpaceLock, + addPackageToSpace, + type PersistedState, +} from './space-python-packages-service.js'; /** * Per-space Python package management (admin form entry point). @@ -27,37 +33,9 @@ import type { SpaceApiDeps } from './space-api.js'; * network-enabled bwrap with no workspace/secret access (see python-packages.ts). */ -interface PersistedPkg { - name: string; // PEP 503 normalized - spec: string; // canonical pip argv, e.g. "requests==2.32.3" - addedAt: string; -} -interface PersistedState { - packages: PersistedPkg[]; - lockHash?: string; - updatedAt?: string; -} - -function readState(json: string | null): PersistedState { - if (!json) return { packages: [] }; - try { - const v = JSON.parse(json); - if (v && Array.isArray(v.packages)) return v as PersistedState; - } catch { - // fall through - } - return { packages: [] }; -} - -// Per-space in-process mutex so two concurrent installs never race the overlay -// or the read-modify-write of the desired-state JSON. -const spaceLocks = new Map>(); -function withSpaceLock(spaceId: string, fn: () => Promise): Promise { - const prev = spaceLocks.get(spaceId) ?? Promise.resolve(); - const next = prev.catch(() => {}).then(fn); - spaceLocks.set(spaceId, next.catch(() => {})); - return next; -} +// PersistedState / readState / withSpaceLock / addPackageToSpace now live in +// space-python-packages-service.ts so the admin form and agent-approval paths +// share ONE mutex + ONE desired-state read-modify-write. export function registerSpacePythonPackagesRoutes(router: Router, deps: SpaceApiDeps): void { const { repo } = deps; @@ -120,40 +98,17 @@ export function registerSpacePythonPackagesRoutes(router: Router, deps: SpaceApi if (!pre.ok) return res.status(503).json({ error: pre.reason }); try { - const result = await withSpaceLock(space.id, async () => { - const state = readState(repo.getSpacePythonPackages(space.id)); - if (state.packages.some((p) => p.name === parsed.normalizedName)) { - return { conflict: true as const }; - } - if (state.packages.length + 1 > c.maxPackagesPerSpace) { - return { tooMany: true as const }; - } - // Rebuild the FULL set (existing + new) into a fresh overlay. - const specs: ParsedSpec[] = [ - ...state.packages.map((p) => parseAndValidateSpec(p.spec)), - parsed, - ]; - const install = await installSpacePackages({ - pkgRoot: c.dir, - spaceKey: spaceKeyFor(space.id, space.ownerId), - specs, - cfg: c, - bwrapAvailable: await isBwrapAvailable(), - }); - if (!install.ok) return { installError: install.error ?? 'install failed' }; - const next: PersistedState = { - packages: [ - ...state.packages, - { name: parsed.normalizedName, spec: parsed.spec, addedAt: new Date().toISOString() }, - ], - lockHash: install.lockHash, - updatedAt: new Date().toISOString(), - }; - repo.setSpacePythonPackages(space.id, JSON.stringify(next)); - return { ok: true as const, state: next }; + const result = await addPackageToSpace({ + repo, + cfg: c, + spaceId: space.id, + ownerId: space.ownerId, + parsed, + bwrapAvailable: await isBwrapAvailable(), }); if ('conflict' in result) return res.status(409).json({ error: `already installed: ${parsed.normalizedName}` }); + if ('versionConflict' in result) return res.status(409).json({ error: `already pinned to a different version: ${result.versionConflict}. Remove it first.` }); if ('tooMany' in result) return res.status(400).json({ error: `too many packages (max ${c.maxPackagesPerSpace})` }); if ('installError' in result) return res.status(422).json({ error: result.installError }); logger.info(`[space-api] python-package added space=${space.id} pkg=${parsed.spec}`); diff --git a/src/bridge/space-python-packages-service.test.ts b/src/bridge/space-python-packages-service.test.ts new file mode 100644 index 0000000..ca0961e --- /dev/null +++ b/src/bridge/space-python-packages-service.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdtempSync, rmSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { randomUUID } from 'node:crypto'; +import { Repository } from '../db/repository.js'; +import { addPackageToSpace } from './space-python-packages-service.js'; +import { resolvePythonPackagesConfig, parseAndValidateSpec } from '../engine/python-packages.js'; + +let repo: Repository; +let dir: string; +const cfg = resolvePythonPackagesConfig({ enabled: true, maxPackagesPerSpace: 30 }); + +beforeEach(() => { + dir = mkdtempSync(join(tmpdir(), 'pkgsvc-')); + repo = new Repository(join(dir, `${randomUUID()}.db`)); +}); +afterEach(() => { + repo.close(); + rmSync(dir, { recursive: true, force: true }); +}); + +async function seedSpaceWith(spec: string): Promise { + const owner = repo.createUser({ email: 'o@e.com', name: 'O', role: 'user', status: 'active' }); + const space = await repo.createSpace({ kind: 'case', title: 'T', ownerId: owner.id, visibility: 'private' }); + const p = parseAndValidateSpec(spec); + repo.setSpacePythonPackages(space.id, JSON.stringify({ packages: [{ name: p.normalizedName, spec: p.spec, addedAt: 'x' }] })); + return space.id; +} + +describe('addPackageToSpace conflict detection (no install path)', () => { + it('same name + same spec → conflict (idempotent, no install attempted)', async () => { + const spaceId = await seedSpaceWith('requests==2.32.3'); + // bwrapAvailable:false would fail-close a real install; but conflict is + // detected BEFORE install, so this stays hermetic. + const out = await addPackageToSpace({ + repo, cfg, spaceId, ownerId: null, + parsed: parseAndValidateSpec('requests==2.32.3'), bwrapAvailable: false, + }); + expect(out).toEqual({ conflict: true }); + }); + + it('same name + DIFFERENT spec → versionConflict with the existing spec', async () => { + const spaceId = await seedSpaceWith('httpx==0.27.0'); + const out = await addPackageToSpace({ + repo, cfg, spaceId, ownerId: null, + parsed: parseAndValidateSpec('httpx==0.28.0'), bwrapAvailable: false, + }); + expect(out).toEqual({ versionConflict: 'httpx==0.27.0' }); + }); +}); diff --git a/src/bridge/space-python-packages-service.ts b/src/bridge/space-python-packages-service.ts new file mode 100644 index 0000000..8903acf --- /dev/null +++ b/src/bridge/space-python-packages-service.ts @@ -0,0 +1,121 @@ +import type { Repository } from '../db/repository.js'; +import { + installSpacePackages, + parseAndValidateSpec, + spaceKeyFor, + type ParsedSpec, + type PythonPackagesConfig, +} from '../engine/python-packages.js'; + +/** + * Shared per-space Python package overlay service. + * + * Both entry points that add a package to a space MUST go through here so they + * share ONE module-level mutex (`spaceLocks`) and ONE desired-state read-modify- + * write path: + * - the admin form (space-python-packages-api.ts) + * - agent approvals (local-tasks-package-requests-api.ts, PR3) + * + * Sharing the mutex is load-bearing, not just DRY: a concurrent admin-add and + * agent-approve on the same space would otherwise race the content-addressed + * overlay build and the `spaces.python_packages` JSON. The persisted desired- + * state is the source of truth; each mutation rebuilds the whole overlay and + * atomically repoints `current` (see python-packages.ts). + */ + +export interface PersistedPkg { + name: string; // PEP 503 normalized + spec: string; // canonical pip argv, e.g. "requests==2.32.3" + addedAt: string; +} +export interface PersistedState { + packages: PersistedPkg[]; + lockHash?: string; + updatedAt?: string; +} + +export function readState(json: string | null): PersistedState { + if (!json) return { packages: [] }; + try { + const v = JSON.parse(json); + if (v && Array.isArray(v.packages)) return v as PersistedState; + } catch { + // fall through + } + return { packages: [] }; +} + +// Per-space in-process mutex so two concurrent installs never race the overlay +// or the read-modify-write of the desired-state JSON. Module-level so it is +// shared across every caller in this process. +const spaceLocks = new Map>(); +export function withSpaceLock(spaceId: string, fn: () => Promise): Promise { + const prev = spaceLocks.get(spaceId) ?? Promise.resolve(); + const next = prev.catch(() => {}).then(fn); + spaceLocks.set(spaceId, next.catch(() => {})); + return next; +} + +export type AddPackageOutcome = + | { ok: true; state: PersistedState } + | { conflict: true } + | { versionConflict: string } // same name, DIFFERENT spec already pinned (existing spec) + | { tooMany: true } + | { installError: string }; + +/** + * Add ONE already-validated package to a space's desired-state and rebuild the + * overlay under the shared per-space lock. Idempotent by name: an existing + * package returns `{ conflict: true }` without touching the overlay. + * + * Callers MUST have validated `parsed` (parseAndValidateSpec + assertNotShadowing) + * and checked the feature is enabled. This function does NOT re-authorize. + */ +export async function addPackageToSpace(params: { + repo: Repository; + cfg: PythonPackagesConfig; + spaceId: string; + ownerId: string | null | undefined; + parsed: ParsedSpec; + bwrapAvailable: boolean; +}): Promise { + const { repo, cfg, spaceId, ownerId, parsed, bwrapAvailable } = params; + return withSpaceLock(spaceId, async () => { + const state = readState(repo.getSpacePythonPackages(spaceId)); + const existing = state.packages.find((p) => p.name === parsed.normalizedName); + if (existing) { + // Same exact spec → idempotent no-op success. Same name but a DIFFERENT + // spec cannot be added (one version per package) — surface it distinctly + // so the caller doesn't falsely report the requested version as installed. + return existing.spec === parsed.spec + ? { conflict: true as const } + : { versionConflict: existing.spec }; + } + if (state.packages.length + 1 > cfg.maxPackagesPerSpace) { + return { tooMany: true as const }; + } + // Rebuild the FULL set (existing + new) into a fresh content-addressed overlay. + const specs: ParsedSpec[] = [ + ...state.packages.map((p) => parseAndValidateSpec(p.spec)), + parsed, + ]; + const install = await installSpacePackages({ + pkgRoot: cfg.dir, + spaceKey: spaceKeyFor(spaceId, ownerId), + specs, + cfg, + bwrapAvailable, + }); + if (!install.ok) return { installError: install.error ?? 'install failed' }; + const next: PersistedState = { + packages: [ + ...state.packages, + { name: parsed.normalizedName, spec: parsed.spec, addedAt: new Date().toISOString() }, + ], + lockHash: install.lockHash, + updatedAt: new Date().toISOString(), + }; + repo.setSpacePythonPackages(spaceId, JSON.stringify(next)); + return { ok: true as const, state: next }; + }); +} diff --git a/src/bridge/ssh-admin-api.ts b/src/bridge/ssh-admin-api.ts new file mode 100644 index 0000000..68a6dd6 --- /dev/null +++ b/src/bridge/ssh-admin-api.ts @@ -0,0 +1,688 @@ +/** + * SSH HTTP layer — admin router (`/api/ssh/admin/*`). + * + * Extracted verbatim from src/bridge/ssh-api.ts (split refactor). Shared + * types / helpers live in ssh-api-shared.ts; the user router lives in + * ssh-user-api.ts. Mounted from src/bridge/server.ts (via ssh-subsystem) + * ONLY when `ssh.enabled=true` AND `MCP_ENCRYPTION_KEY` is configured. + * Every admin write path requires `body.reason` >= 8 chars and is audit + * logged. See the design conventions in the ssh-api.ts barrel header. + */ + +import { Router } from 'express'; + +import type { SshConnectionRepo } from '../ssh/connection-repo.js'; +import type { SshGrant, SshGrantSubjectType } from '../ssh/grants-repo.js'; +import { logger } from '../logger.js'; +import { + type SshApiDeps, + type SshEncryptResult, + validateReason, + maintenance503, + safePort, + safeString, + safeOptionalString, + safePathPrefix, + safeUuid, + safeExpiresAt, + safeSubjectType, + presentConnection, + presentGrant, + safeLimit, + jsonError, + makeSshLocalUserMiddleware, +} from './ssh-api-shared.js'; +// ────────────────────────────────────────────────────────────────────── +// Admin router — /api/ssh/admin/* +// ────────────────────────────────────────────────────────────────────── + +export function createSshAdminRouter(deps: SshApiDeps): Router { + const router = Router(); + router.use(makeSshLocalUserMiddleware(deps.authActive ?? true)); + + // GET /api/ssh/admin/connections + router.get('/connections', deps.requireAdmin, (_req, res) => { + try { + const list = deps.connectionRepo.listAll(); + res.json({ connections: list.map(presentConnection) }); + } catch (e) { + logger.warn(`[ssh:api] admin list connections failed err=${String(e)}`); + jsonError(res, 500, 'list_failed'); + } + }); + + router.get('/connections/:id', deps.requireAdmin, (req, res) => { + try { + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + let publicKey: string | null = null; + try { + publicKey = deps.derivePublicKey(conn.ownerId, conn.privateKeyEnc, conn.passphraseEnc, conn.spaceId); + } catch (e) { + logger.warn(`[ssh:api] admin derive public key failed id=${conn.id} err=${String(e)}`); + } + res.json({ connection: presentConnection(conn), publicKey }); + } catch (e) { + logger.warn(`[ssh:api] admin get connection failed err=${String(e)}`); + jsonError(res, 500, 'get_failed'); + } + }); + + router.patch('/connections/:id/disable', deps.requireAdmin, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const reasonErr = validateReason(req.body?.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + const ok = deps.connectionRepo.disableByAdmin(conn.id, String(req.body.reason), userId); + if (!ok) { jsonError(res, 404, 'not_found'); return; } + deps.auditRepo.beginAndComplete( + { + action: 'ssh.connection.disable', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: conn.ownerId, + actingUserId: userId, + reason: String(req.body.reason), + }, + 'success', + ); + const updated = deps.connectionRepo.resolveConnection(conn.id)!; + res.json({ connection: presentConnection(updated) }); + } catch (e) { + logger.warn(`[ssh:api] admin disable failed err=${String(e)}`); + jsonError(res, 500, 'disable_failed'); + } + }); + + router.patch('/connections/:id/enable', deps.requireAdmin, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const reasonErr = validateReason(req.body?.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + const ok = deps.connectionRepo.enableByAdmin(conn.id); + if (!ok) { jsonError(res, 404, 'not_found'); return; } + deps.auditRepo.beginAndComplete( + { + action: 'ssh.connection.enable', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: conn.ownerId, + actingUserId: userId, + reason: String(req.body.reason), + }, + 'success', + ); + const updated = deps.connectionRepo.resolveConnection(conn.id)!; + res.json({ connection: presentConnection(updated) }); + } catch (e) { + logger.warn(`[ssh:api] admin enable failed err=${String(e)}`); + jsonError(res, 500, 'enable_failed'); + } + }); + + router.delete('/connections/:id', deps.requireAdmin, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const reasonErr = validateReason(req.body?.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + const auditId = deps.auditRepo.begin({ + action: 'ssh.connection.delete', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: conn.ownerId, + actingUserId: userId, + reason: String(req.body.reason), + }); + const ok = deps.connectionRepo.delete(conn.id); + if (!ok) { + deps.auditRepo.complete(auditId, 'failed', { err: 'no_changes' }); + jsonError(res, 404, 'not_found'); + return; + } + deps.auditRepo.complete(auditId, 'success'); + res.json({ ok: true }); + } catch (e) { + logger.warn(`[ssh:api] admin delete failed err=${String(e)}`); + jsonError(res, 500, 'delete_failed'); + } + }); + + router.post('/connections/:id/force-unlock', deps.requireAdmin, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const reasonErr = validateReason(req.body?.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const decision = deps.forceUnlockLimiter.check(userId); + if (!decision.allowed) { + if (decision.retryAfterSeconds !== undefined) { + res.setHeader('Retry-After', String(decision.retryAfterSeconds)); + } + jsonError(res, 429, 'rate_limited', { retryAfterSeconds: decision.retryAfterSeconds }); + return; + } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + const removed = deps.abuseRepo.reset(`conn:${conn.id}`); + deps.auditRepo.beginAndComplete( + { + action: 'ssh.abuse.unlock_manual', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: conn.ownerId, + actingUserId: userId, + reason: String(req.body.reason), + detail: { removed }, + }, + 'success', + ); + res.json({ ok: true, removed }); + } catch (e) { + logger.warn(`[ssh:api] admin force-unlock failed err=${String(e)}`); + jsonError(res, 500, 'unlock_failed'); + } + }); + + // POST /api/ssh/admin/globals — create a global connection (owner_id=NULL). + router.post('/globals', deps.requireAdmin, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const reasonErr = validateReason(req.body?.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const body = req.body ?? {}; + const label = safeString(body.label, 200); + const host = safeString(body.host, 255); + const port = safePort(body.port); + const username = safeString(body.username, 64); + const denyPatterns = safeOptionalString(body.commandDenyPatterns, 4096); + const allowPatterns = safeOptionalString(body.commandAllowPatterns, 4096); + const allowRemoteUnrestricted = body.allowRemoteUnrestricted === true || body.allowRemoteUnrestricted === 1; + const allowPrivateAddresses = body.allowPrivateAddresses === true || body.allowPrivateAddresses === 1; + + // remote_path_prefix is required UNLESS allow_remote_unrestricted is set. + let remotePathPrefix: string | null; + if (allowRemoteUnrestricted) { + // Stored prefix is '/' (sandbox-effectively-disabled). + remotePathPrefix = '/'; + } else { + remotePathPrefix = safePathPrefix(body.remotePathPrefix); + } + + // Keypair source: 'provided' (admin uploads PEM) or 'generate' (orchestrator + // creates a fresh keypair; the public key is returned exactly once). + const keypairSource = body.keypairSource === 'generate' ? 'generate' : 'provided'; + let pemBuf: Buffer; + let passBuf: Buffer | null; + if (keypairSource === 'generate') { + const keyType = body.generateKeyType === 'rsa-4096' ? 'rsa-4096' : 'ed25519'; + const generated = deps.generateKeypair(keyType); + pemBuf = generated.privateKeyPem; + passBuf = null; + } else { + const privateKey = typeof body.privateKeyPem === 'string' ? body.privateKeyPem : null; + const passphrase = typeof body.passphrase === 'string' ? body.passphrase : null; + if (!privateKey) { + jsonError(res, 400, 'invalid_input', { + required: ['label', 'host', 'port (1-65535)', 'username', 'privateKeyPem', 'remotePathPrefix (or allowRemoteUnrestricted=true)'], + }); + return; + } + pemBuf = Buffer.from(privateKey, 'utf8'); + passBuf = passphrase ? Buffer.from(passphrase, 'utf8') : null; + } + + if (!label || !host || port === null || !username || !remotePathPrefix) { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + jsonError(res, 400, 'invalid_input', { + required: ['label', 'host', 'port (1-65535)', 'username', 'privateKeyPem', 'remotePathPrefix (or allowRemoteUnrestricted=true)'], + }); + return; + } + if (denyPatterns === null) { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + jsonError(res, 400, 'invalid_command_deny_patterns'); return; + } + if (allowPatterns === null) { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + jsonError(res, 400, 'invalid_command_allow_patterns'); return; + } + + let encrypted: SshEncryptResult; + try { + encrypted = deps.encryptKeyMaterial(null, pemBuf, passBuf); + } catch (e) { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + logger.warn(`[ssh:api] admin global encrypt failed err=${String(e)}`); + jsonError(res, 500, 'encrypt_failed'); + return; + } + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + + try { + const conn = deps.connectionRepo.create({ + ownerId: null, + label, host, port, username, + privateKeyEnc: encrypted.blob, + passphraseEnc: encrypted.passphraseBlob, + keyVersion: encrypted.keyVersion, + keyFingerprint: encrypted.fingerprint, + remotePathPrefix, + allowRemoteUnrestricted, + allowPrivateAddresses, + commandDenyPatterns: denyPatterns ?? null, + commandAllowPatterns: allowPatterns ?? null, + }); + deps.auditRepo.beginAndComplete( + { + action: 'ssh.connection.upsert', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: null, + actingUserId: userId, + reason: String(body.reason), + detail: { op: 'create', scope: 'global', allowRemoteUnrestricted, allowPrivateAddresses, keypairSource }, + }, + 'success', + ); + res.status(201).json({ + connection: presentConnection(conn), + publicKey: encrypted.publicKey, + }); + } catch (e) { + logger.warn(`[ssh:api] admin global create failed err=${String(e)}`); + jsonError(res, 500, 'create_failed'); + } + }); + + router.patch('/globals/:id', deps.requireAdmin, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const reasonErr = validateReason(req.body?.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + if (conn.ownerId !== null) { jsonError(res, 400, 'not_global'); return; } + const body = req.body ?? {}; + const patch: Parameters[1] = {}; + if (body.label !== undefined) { + const v = safeString(body.label, 200); + if (v === null) { jsonError(res, 400, 'invalid_label'); return; } + patch.label = v; + } + if (body.host !== undefined) { + const v = safeString(body.host, 255); + if (v === null) { jsonError(res, 400, 'invalid_host'); return; } + patch.host = v; + } + if (body.port !== undefined) { + const v = safePort(body.port); + if (v === null) { jsonError(res, 400, 'invalid_port'); return; } + patch.port = v; + } + if (body.username !== undefined) { + const v = safeString(body.username, 64); + if (v === null) { jsonError(res, 400, 'invalid_username'); return; } + patch.username = v; + } + if (body.remotePathPrefix !== undefined) { + const v = safePathPrefix(body.remotePathPrefix); + if (v === null) { jsonError(res, 400, 'invalid_remote_path_prefix'); return; } + patch.remotePathPrefix = v; + } + if (body.commandDenyPatterns !== undefined) { + const v = safeOptionalString(body.commandDenyPatterns, 4096); + if (v === null) { jsonError(res, 400, 'invalid_command_deny_patterns'); return; } + patch.commandDenyPatterns = v ?? null; + } + if (body.commandAllowPatterns !== undefined) { + const v = safeOptionalString(body.commandAllowPatterns, 4096); + if (v === null) { jsonError(res, 400, 'invalid_command_allow_patterns'); return; } + patch.commandAllowPatterns = v ?? null; + } + if (body.allowRemoteUnrestricted !== undefined) { + patch.allowRemoteUnrestricted = body.allowRemoteUnrestricted === true || body.allowRemoteUnrestricted === 1; + } + if (body.allowPrivateAddresses !== undefined) { + patch.allowPrivateAddresses = body.allowPrivateAddresses === true || body.allowPrivateAddresses === 1; + } + if (typeof body.privateKeyPem === 'string') { + let pemBuf = Buffer.from(body.privateKeyPem, 'utf8'); + let passBuf = typeof body.passphrase === 'string' ? Buffer.from(body.passphrase, 'utf8') : null; + try { + const enc = deps.encryptKeyMaterial(null, pemBuf, passBuf); + patch.privateKeyEnc = enc.blob; + patch.passphraseEnc = enc.passphraseBlob; + patch.keyVersion = enc.keyVersion; + patch.keyFingerprint = enc.fingerprint; + } finally { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + } + } + const ok = deps.connectionRepo.update(conn.id, patch); + if (!ok) { jsonError(res, 404, 'not_found'); return; } + deps.auditRepo.beginAndComplete( + { + action: 'ssh.connection.upsert', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: null, + actingUserId: userId, + reason: String(req.body.reason), + detail: { op: 'update', scope: 'global', fields: Object.keys(patch) }, + }, + 'success', + ); + const updated = deps.connectionRepo.resolveConnection(conn.id)!; + res.json({ connection: presentConnection(updated) }); + } catch (e) { + logger.warn(`[ssh:api] admin global patch failed err=${String(e)}`); + jsonError(res, 500, 'update_failed'); + } + }); + + router.delete('/globals/:id', deps.requireAdmin, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const reasonErr = validateReason(req.body?.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + if (conn.ownerId !== null) { jsonError(res, 400, 'not_global'); return; } + const auditId = deps.auditRepo.begin({ + action: 'ssh.connection.delete', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: null, + actingUserId: userId, + reason: String(req.body.reason), + }); + const ok = deps.connectionRepo.delete(conn.id); + if (!ok) { + deps.auditRepo.complete(auditId, 'failed', { err: 'no_changes' }); + jsonError(res, 404, 'not_found'); + return; + } + deps.auditRepo.complete(auditId, 'success'); + res.json({ ok: true }); + } catch (e) { + logger.warn(`[ssh:api] admin global delete failed err=${String(e)}`); + jsonError(res, 500, 'delete_failed'); + } + }); + + router.get('/grants', deps.requireAdmin, (req, res) => { + try { + const limit = safeLimit(req.query.limit, 100, 1000); + const rows = deps.db.prepare( + `SELECT * FROM ssh_connection_grants ORDER BY created_at DESC LIMIT ?`, + ).all(limit) as Array<{ + id: string; connection_id: string; subject_type: SshGrantSubjectType; subject_id: string; + piece_name: string | null; applies_to_all_pieces: number; granted_by_user_id: string; + reason: string; expires_at: string | null; created_at: string; + }>; + const grants = rows.map((r) => ({ + id: r.id, + connectionId: r.connection_id, + subjectType: r.subject_type, + subjectId: r.subject_id, + pieceName: r.piece_name, + appliesToAllPieces: r.applies_to_all_pieces === 1, + grantedByUserId: r.granted_by_user_id, + reason: r.reason, + expiresAt: r.expires_at, + createdAt: r.created_at, + })); + res.json({ grants }); + } catch (e) { + logger.warn(`[ssh:api] admin list grants failed err=${String(e)}`); + jsonError(res, 500, 'list_grants_failed'); + } + }); + + router.post('/grants', deps.requireAdmin, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const body = req.body ?? {}; + const reasonErr = validateReason(body.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const connectionId = safeUuid(body.connectionId); + const subjectType = safeSubjectType(body.subjectType); + const subjectId = safeString(body.subjectId, 128); + const appliesToAllPieces = body.appliesToAllPieces === true || body.appliesToAllPieces === 1; + const pieceName = safeOptionalString(body.pieceName, 64); + const expiresAt = safeExpiresAt(body.expiresAt); + if (!connectionId || !subjectType || !subjectId) { + jsonError(res, 400, 'invalid_input', { + required: ['connectionId', 'subjectType ("user"|"org")', 'subjectId', 'reason'], + }); + return; + } + if (pieceName === null) { jsonError(res, 400, 'invalid_piece_name'); return; } + if (expiresAt === null) { jsonError(res, 400, 'invalid_expires_at'); return; } + if (!appliesToAllPieces && (!pieceName || pieceName.length === 0)) { + jsonError(res, 400, 'piece_name_required_unless_applies_to_all'); return; + } + if (appliesToAllPieces && pieceName !== undefined && pieceName.length > 0) { + jsonError(res, 400, 'piece_name_conflicts_with_applies_to_all'); return; + } + // Connection must exist before granting. + const conn = deps.connectionRepo.resolveConnection(connectionId); + if (!conn) { jsonError(res, 404, 'connection_not_found'); return; } + let grant: SshGrant; + try { + grant = deps.grantsRepo.create({ + connectionId, + subjectType, + subjectId, + pieceName: appliesToAllPieces ? null : (pieceName ?? null), + appliesToAllPieces, + grantedByUserId: userId, + reason: String(body.reason), + expiresAt, + }); + } catch (e) { + jsonError(res, 400, 'grant_create_failed', { detail: String(e) }); return; + } + deps.auditRepo.beginAndComplete( + { + action: 'ssh.grant.create', + entityType: 'ssh_grant', + entityId: grant.id, + connectionId: conn.id, + ownerId: conn.ownerId, + actingUserId: userId, + reason: String(body.reason), + detail: { subjectType, subjectId, pieceName: grant.pieceName, appliesToAllPieces }, + }, + 'success', + ); + res.status(201).json({ grant: presentGrant(grant) }); + } catch (e) { + logger.warn(`[ssh:api] admin grant create failed err=${String(e)}`); + jsonError(res, 500, 'grant_create_failed'); + } + }); + + router.delete('/grants/:id', deps.requireAdmin, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const reasonErr = validateReason(req.body?.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const grant = deps.grantsRepo.getById(req.params.id); + if (!grant) { jsonError(res, 404, 'not_found'); return; } + const ok = deps.grantsRepo.delete(grant.id); + if (!ok) { jsonError(res, 404, 'not_found'); return; } + // Kick any active console-WS viewers whose access depended on this grant. + // user-subject grants map 1:1 to a single userId; org-subject grants are + // deferred (next attach will be denied; existing viewers stay until the + // session ends or the org-member's WS reconnects). + let kicked: number | void = 0; + if (grant.subjectType === 'user' && deps.onAccessRevoked) { + kicked = deps.onAccessRevoked({ connectionId: grant.connectionId, userId: grant.subjectId }); + } + deps.auditRepo.beginAndComplete( + { + action: 'ssh.grant.delete', + entityType: 'ssh_grant', + entityId: grant.id, + connectionId: grant.connectionId, + actingUserId: userId, + reason: String(req.body.reason), + detail: { + subjectType: grant.subjectType, + subjectId: grant.subjectId, + pieceName: grant.pieceName, + viewersKicked: typeof kicked === 'number' ? kicked : 0, + }, + }, + 'success', + ); + res.json({ ok: true }); + } catch (e) { + logger.warn(`[ssh:api] admin grant delete failed err=${String(e)}`); + jsonError(res, 500, 'grant_delete_failed'); + } + }); + + // POST /api/ssh/admin/rotate-master-key — stub for v1 (Phase 5). + // Sets the maintenance flag and audits the intent. The actual DEK re-wrap + // job is deferred to a follow-up PR per Phase 5 design note (line 728). + // Returns 501 in the response body but 202 status so the UI can show a + // banner; UI Phase 6 will key off the maintenance snapshot. + router.post('/rotate-master-key', deps.requireAdmin, (req, res) => { + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const reasonErr = validateReason(req.body?.reason); + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + if (deps.maintenance.isActive()) { + jsonError(res, 409, 'already_in_progress', { snapshot: deps.maintenance.snapshot() }); + return; + } + const jobId = `rotate-${Date.now().toString(36)}`; + deps.maintenance.enter(String(req.body.reason), jobId); + deps.auditRepo.beginAndComplete( + { + action: 'ssh.master_key.rotate.start', + actingUserId: userId, + reason: String(req.body.reason), + detail: { jobId, status: 'stub', note: 'rotation job not implemented in v1; only maintenance flag set' }, + }, + 'success', + ); + res.status(202).json({ + jobId, + status: 'maintenance_set', + detail: 'maintenance flag is now active; actual DEK re-wrap is not implemented in v1', + notImplemented: true, + }); + } catch (e) { + logger.warn(`[ssh:api] admin rotate-master-key failed err=${String(e)}`); + jsonError(res, 500, 'rotate_failed'); + } + }); + + router.get('/rotate-master-key/:jobId', deps.requireAdmin, (req, res) => { + try { + const snap = deps.maintenance.snapshot(); + if (!snap.active || snap.jobId !== req.params.jobId) { + jsonError(res, 404, 'not_found_or_completed', { snapshot: snap }); + return; + } + res.json({ + jobId: snap.jobId, + status: 'in_progress', + startedAt: snap.enteredAt, + progress: { note: 'v1 stub: no rewrap performed' }, + notImplemented: true, + }); + } catch (e) { + logger.warn(`[ssh:api] admin rotate-master-key get failed err=${String(e)}`); + jsonError(res, 500, 'rotate_get_failed'); + } + }); + + // GET /api/ssh/admin/audit — cross-user audit query. + router.get('/audit', deps.requireAdmin, (req, res) => { + try { + const limit = safeLimit(req.query.limit, 100, 1000); + const action = typeof req.query.action === 'string' ? req.query.action : null; + const ownerId = typeof req.query.ownerId === 'string' ? req.query.ownerId : null; + const connectionId = typeof req.query.connectionId === 'string' ? req.query.connectionId : null; + const outcome = typeof req.query.outcome === 'string' ? req.query.outcome : null; + + const where: string[] = []; + const params: unknown[] = []; + if (action) { where.push('action = ?'); params.push(action); } + if (ownerId) { where.push('owner_id = ?'); params.push(ownerId); } + if (connectionId) { where.push('connection_id = ?'); params.push(connectionId); } + if (outcome) { where.push('outcome = ?'); params.push(outcome); } + const whereClause = where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''; + const sql = `SELECT * FROM ssh_audit_log ${whereClause} ORDER BY started_at DESC LIMIT ?`; + params.push(limit); + const rows = deps.db.prepare(sql).all(...params) as Array<{ + id: number; action: string; entity_type: string | null; entity_id: string | null; + connection_id: string | null; owner_id: string | null; acting_user_id: string | null; + job_id: string | null; piece_name: string | null; outcome: string; + reason: string | null; detail: string | null; started_at: string; completed_at: string | null; + }>; + const audit = rows.map((r) => ({ + id: r.id, + action: r.action, + entityType: r.entity_type, + entityId: r.entity_id, + connectionId: r.connection_id, + ownerId: r.owner_id, + actingUserId: r.acting_user_id, + jobId: r.job_id, + pieceName: r.piece_name, + outcome: r.outcome, + reason: r.reason, + detail: r.detail ? JSON.parse(r.detail) : null, + startedAt: r.started_at, + completedAt: r.completed_at, + })); + res.json({ audit }); + } catch (e) { + logger.warn(`[ssh:api] admin audit list failed err=${String(e)}`); + jsonError(res, 500, 'audit_failed'); + } + }); + + return router; +} diff --git a/src/bridge/ssh-api-shared.ts b/src/bridge/ssh-api-shared.ts new file mode 100644 index 0000000..55e6957 --- /dev/null +++ b/src/bridge/ssh-api-shared.ts @@ -0,0 +1,318 @@ +/** + * SSH HTTP layer — shared types & helpers. + * + * Extracted verbatim from src/bridge/ssh-api.ts (split refactor). Holds the + * dependency-injection surface (`SshApiDeps`), the tester contracts, and the + * validation / presentation helpers used by both routers: + * ssh-user-api.ts — createSshUserRouter + * ssh-admin-api.ts — createSshAdminRouter + * The original module remains as a re-export barrel, so external importers + * are unaffected. + */ + +import { Router, type Request, type Response, type NextFunction, type RequestHandler } from 'express'; +import type Database from 'better-sqlite3'; + +import type { SshConnection, SshConnectionRepo, HostKeyVerifyResult } from '../ssh/connection-repo.js'; +import type { SshGrant, SshGrantsRepo, SshGrantSubjectType } from '../ssh/grants-repo.js'; +import type { SshAuditRepo, SshAuditRow } from '../ssh/audit-repo.js'; +import type { SshAbuseRepo } from '../ssh/abuse-repo.js'; +import type { SshAccessResolver } from '../ssh/access.js'; +import type { MaintenanceController } from '../ssh/maintenance.js'; +import type { AdminRateLimiter } from '../ssh/admin-rate-limit.js'; +import { logger } from '../logger.js'; + +// ────────────────────────────────────────────────────────────────────── +// Types +// ────────────────────────────────────────────────────────────────────── + +export interface SshTesterArgs { + connection: SshConnection; + decryptedKey: Buffer; + passphrase: Buffer | null; + timeoutMs: number; +} + +export type SshTesterVerdict = 'first_observe' | 'mismatch' | 'pass' | 'alg_not_allowed'; + +export interface SshTesterResult { + /** SHA256 fingerprint of the observed host key. */ + fingerprint: string; + /** Base64 of the observed host key (wire format). */ + hostKeyB64: string; + hostKeyType: string; + verdict: SshTesterVerdict; +} + +export interface SshTester { + test(args: SshTesterArgs): Promise; +} + +export interface SshEncryptResult { + blob: Buffer; + passphraseBlob: Buffer | null; + keyVersion: number; + fingerprint: string; + publicKey: string; +} + +export interface SshApiDeps { + db: Database.Database; + requireAuth: RequestHandler; + requireAdmin: RequestHandler; + getUserId(req: Request): string | null; + isAdmin(req: Request): boolean; + getOrgIds(req: Request): string[]; + /** + * Spaces foundation (spec §11). Authorizes a `space_id` supplied on create / + * list against the requesting viewer and resolves the space's existence. + * When omitted, space-scoped requests are rejected (no way to authorize + * them) while legacy per-user create/list keeps working. Mirrors mcp-api. + */ + getSpace?: (spaceId: string, viewer?: Express.User) => Promise<{ id: string } | null>; + /** + * Whether the bridge wired the auth subsystem. When `false` (no-auth + * single-user deployment) requests carry no `req.user`, so the router + * synthesizes a stable `local` owner — otherwise every endpoint 401s on + * the null userId and SSH is unusable. Mirrors memory-api / + * user-folder-api. Defaults to `true` for existing (auth-only) callers. + */ + authActive?: boolean; + + connectionRepo: SshConnectionRepo; + grantsRepo: SshGrantsRepo; + auditRepo: SshAuditRepo; + abuseRepo: SshAbuseRepo; + accessResolver: SshAccessResolver; + + maintenance: MaintenanceController; + forceUnlockLimiter: AdminRateLimiter; + + /** + * Encrypt a PEM (and optional passphrase) into the envelope-encrypted blobs + * stored in ssh_connections. ownerId=null = system DEK (global connection), + * ownerId= = user DEK. + */ + encryptKeyMaterial(ownerId: string | null, pem: Buffer, passphrase: Buffer | null, spaceId?: string | null): SshEncryptResult; + + /** + * Decrypt blob using the correct DEK. Buffer must be cleared by caller. + * spaceId (Space-as-Principal P2): when given, the space DEK is tried first + * with a fallback to the owner/system DEK (lets pre-migration owner-encrypted + * blobs of a now-space-owned connection still decrypt). + */ + decryptKeyMaterial(ownerId: string | null, blob: Buffer, spaceId?: string | null): Buffer; + decryptPassphrase(ownerId: string | null, blob: Buffer | null, spaceId?: string | null): Buffer | null; + + /** + * Generate a fresh keypair (no passphrase). The caller is expected to feed + * the returned `privateKeyPem` back through `encryptKeyMaterial` for at-rest + * encryption. + */ + generateKeypair(keyType: 'ed25519' | 'rsa-4096'): { + privateKeyPem: Buffer; + publicKey: string; + }; + + /** + * Derive the OpenSSH-format public key (` `) from the stored + * private-key blob. Handles decrypt + format + buffer-clear internally. + */ + derivePublicKey( + ownerId: string | null, + blob: Buffer, + passphraseBlob: Buffer | null, + spaceId?: string | null, + ): string; + + /** SSH dial helper — injected so tests don't need a real SSH server. */ + sshTester: SshTester; + + /** Connection test timeout. Defaults to 5s. */ + connectionTestTimeoutMs?: number; + + /** + * Optional hook called after a grant is revoked / deleted so the caller can + * kick any active WebSocket viewers that depended on that grant. The hook + * is a no-op pass-through (returns 0) when the SSH console subsystem isn't + * wired (e.g. tests, or `ssh.console.enabled=false`). + * + * Implementation typically calls `SessionRegistry.revokeAccessFor`. + */ + onAccessRevoked?: (args: { connectionId: string; userId: string }) => number | void; +} + +// ────────────────────────────────────────────────────────────────────── +// Helpers +// ────────────────────────────────────────────────────────────────────── + +export const REASON_MIN_CHARS = 8; + +export function validateReason(reason: unknown): string | null { + if (typeof reason !== 'string') return 'reason is required (string >= 8 chars)'; + if (reason.length < REASON_MIN_CHARS) return `reason must be at least ${REASON_MIN_CHARS} characters`; + return null; +} + +export function maintenance503(maintenance: MaintenanceController, res: Response): boolean { + if (!maintenance.isActive()) return false; + res.setHeader('Retry-After', '30'); + res.status(503).json({ + error: 'rotation_in_progress', + detail: 'SSH subsystem is in maintenance — try again shortly.', + snapshot: maintenance.snapshot(), + }); + return true; +} + +export function safePort(p: unknown): number | null { + if (typeof p !== 'number' || !Number.isInteger(p) || p < 1 || p > 65535) return null; + return p; +} + +export function safeString(v: unknown, max = 1024): string | null { + if (typeof v !== 'string') return null; + if (v.length === 0 || v.length > max) return null; + return v; +} + +export function safeOptionalString(v: unknown, max = 1024): string | null | undefined { + if (v === undefined || v === null) return undefined; + if (typeof v !== 'string') return null; + if (v.length > max) return null; + return v; +} + +export function safePathPrefix(v: unknown): string | null { + const s = safeString(v, 1024); + if (s === null) return null; + if (s.length === 0) return null; + // Reject any `..` parent-ref segment using either separator. POSIX and + // Windows-style prefixes (drive letters, UNC, no-leading-slash) are + // all allowed; the runtime validateRemotePath enforces containment. + const segments = s.split(/[\\/]/); + if (segments.includes('..')) return null; + return s; +} + +export function safeFingerprint(v: unknown): string | null { + if (typeof v !== 'string') return null; + // SHA256: — base64 chars + ='s, ~46 chars typical. + if (!/^SHA256:[A-Za-z0-9+/=]{20,80}$/.test(v)) return null; + return v; +} + +export function safeUuid(v: unknown): string | null { + if (typeof v !== 'string') return null; + if (!/^[a-f0-9-]{8,}$/.test(v)) return null; + return v; +} + +export function safeExpiresAt(v: unknown): string | null | undefined { + if (v === undefined || v === null) return undefined; + if (typeof v !== 'string') return null; + // ISO8601 — Date.parse returns NaN for invalid strings. + const t = Date.parse(v); + if (Number.isNaN(t)) return null; + return new Date(t).toISOString(); +} + +export function safeSubjectType(v: unknown): SshGrantSubjectType | null { + return v === 'user' || v === 'org' ? v : null; +} + +/** + * Normalize a space id from a request body or query (spec §11). Accepts both + * `space_id` (snake, wire convention) and `spaceId` (camel). Returns: string + * (a space), null (explicit global / space-less), or undefined (not given). + * Mirrors mcp-api.readSpaceId. + */ +export function readSpaceId(src: Record | undefined): string | null | undefined { + if (!src) return undefined; + const raw = (src.space_id ?? src.spaceId) as unknown; + if (raw === undefined) return undefined; + if (raw === null || raw === '') return null; + // 文字列のみ受理(数値等の coerce を避ける)。 + return typeof raw === 'string' ? raw : undefined; +} + +export function presentConnection(conn: SshConnection): Record { + // Strip the encrypted blob fields and the pending verify token (the token + // is only surfaced on /test responses; subsequent GETs do not expose it + // again — the user must hold onto it). + return { + id: conn.id, + ownerId: conn.ownerId, + label: conn.label, + host: conn.host, + port: conn.port, + username: conn.username, + keyVersion: conn.keyVersion, + keyFingerprint: conn.keyFingerprint, + hostKeyType: conn.hostKeyType, + hostKeyFingerprint: conn.hostKeyFingerprint, + hostKeyRecordedAt: conn.hostKeyRecordedAt, + hostKeyVerifiedAt: conn.hostKeyVerifiedAt, + hostKeyPending: conn.hostKeyPending, + hostKeyPendingFingerprint: conn.hostKeyPendingFingerprint, + hostKeyPendingSource: conn.hostKeyPendingSource, + commandDenyPatterns: conn.commandDenyPatterns, + commandAllowPatterns: conn.commandAllowPatterns, + remotePathPrefix: conn.remotePathPrefix, + allowRemoteUnrestricted: conn.allowRemoteUnrestricted, + allowPrivateAddresses: conn.allowPrivateAddresses, + enabled: conn.enabled, + disabledByAdmin: conn.disabledByAdmin, + disabledByAdminReason: conn.disabledByAdminReason, + disabledByAdminAt: conn.disabledByAdminAt, + disabledByAdminUserId: conn.disabledByAdminUserId, + createdAt: conn.createdAt, + updatedAt: conn.updatedAt, + }; +} + +export function presentGrant(g: SshGrant): Record { + return { ...g }; +} + +export function presentAuditRow(a: SshAuditRow): Record { + return { ...a }; +} + +export function safeLimit(v: unknown, def = 50, max = 500): number { + if (typeof v === 'string') { + const n = Number(v); + if (Number.isInteger(n) && n > 0 && n <= max) return n; + } + if (typeof v === 'number' && Number.isInteger(v) && v > 0 && v <= max) return v; + return def; +} + +export function jsonError(res: Response, status: number, error: string, detail?: unknown): void { + if (detail === undefined) { + res.status(status).json({ error }); + } else { + res.status(status).json({ error, detail }); + } +} + +// ────────────────────────────────────────────────────────────────────── +// User router — /api/ssh/connections + /api/ssh/grants +// ────────────────────────────────────────────────────────────────────── + +/** + * No-auth single-user mode: synthesize a `local` user so the per-user + * connection store has a stable owner and `getUserId`/`isAdmin` resolve. + * Role `admin` keeps the connection-management surface fully usable for the + * lone local operator (in single-user mode the admin/grant checks only ever + * compare against `local`-owned rows, so this never widens cross-user access). + * No-op when auth is active (Passport has already populated `req.user`). + */ +export function makeSshLocalUserMiddleware(authActive: boolean): RequestHandler { + return (req: Request, _res: Response, next: NextFunction) => { + if (!authActive && !(req as { user?: unknown }).user) { + (req as { user?: unknown }).user = { id: 'local', role: 'admin', orgIds: [] }; + } + next(); + }; +} diff --git a/src/bridge/ssh-api.ts b/src/bridge/ssh-api.ts index 3b1d6f2..d0ab435 100644 --- a/src/bridge/ssh-api.ts +++ b/src/bridge/ssh-api.ts @@ -23,1522 +23,20 @@ * cleared on every code path (success, error, validation failure). * - Response shaping: `presentConnection()` strips encrypted blob fields and * the pending host-key token (token only returned on test / observation). + * + * This module is a re-export barrel (split refactor): the implementation + * lives in ssh-api-shared.ts (types & helpers), ssh-user-api.ts + * (createSshUserRouter) and ssh-admin-api.ts (createSshAdminRouter). + * Existing importers keep working unchanged. */ -import { Router, type Request, type Response, type NextFunction, type RequestHandler } from 'express'; -import type Database from 'better-sqlite3'; - -import type { SshConnection, SshConnectionRepo, HostKeyVerifyResult } from '../ssh/connection-repo.js'; -import type { SshGrant, SshGrantsRepo, SshGrantSubjectType } from '../ssh/grants-repo.js'; -import type { SshAuditRepo, SshAuditRow } from '../ssh/audit-repo.js'; -import type { SshAbuseRepo } from '../ssh/abuse-repo.js'; -import type { SshAccessResolver } from '../ssh/access.js'; -import type { MaintenanceController } from '../ssh/maintenance.js'; -import type { AdminRateLimiter } from '../ssh/admin-rate-limit.js'; -import { logger } from '../logger.js'; - -// ────────────────────────────────────────────────────────────────────── -// Types -// ────────────────────────────────────────────────────────────────────── - -export interface SshTesterArgs { - connection: SshConnection; - decryptedKey: Buffer; - passphrase: Buffer | null; - timeoutMs: number; -} - -export type SshTesterVerdict = 'first_observe' | 'mismatch' | 'pass' | 'alg_not_allowed'; - -export interface SshTesterResult { - /** SHA256 fingerprint of the observed host key. */ - fingerprint: string; - /** Base64 of the observed host key (wire format). */ - hostKeyB64: string; - hostKeyType: string; - verdict: SshTesterVerdict; -} - -export interface SshTester { - test(args: SshTesterArgs): Promise; -} - -export interface SshEncryptResult { - blob: Buffer; - passphraseBlob: Buffer | null; - keyVersion: number; - fingerprint: string; - publicKey: string; -} - -export interface SshApiDeps { - db: Database.Database; - requireAuth: RequestHandler; - requireAdmin: RequestHandler; - getUserId(req: Request): string | null; - isAdmin(req: Request): boolean; - getOrgIds(req: Request): string[]; - /** - * Spaces foundation (spec §11). Authorizes a `space_id` supplied on create / - * list against the requesting viewer and resolves the space's existence. - * When omitted, space-scoped requests are rejected (no way to authorize - * them) while legacy per-user create/list keeps working. Mirrors mcp-api. - */ - getSpace?: (spaceId: string, viewer?: Express.User) => Promise<{ id: string } | null>; - /** - * Whether the bridge wired the auth subsystem. When `false` (no-auth - * single-user deployment) requests carry no `req.user`, so the router - * synthesizes a stable `local` owner — otherwise every endpoint 401s on - * the null userId and SSH is unusable. Mirrors memory-api / - * user-folder-api. Defaults to `true` for existing (auth-only) callers. - */ - authActive?: boolean; - - connectionRepo: SshConnectionRepo; - grantsRepo: SshGrantsRepo; - auditRepo: SshAuditRepo; - abuseRepo: SshAbuseRepo; - accessResolver: SshAccessResolver; - - maintenance: MaintenanceController; - forceUnlockLimiter: AdminRateLimiter; - - /** - * Encrypt a PEM (and optional passphrase) into the envelope-encrypted blobs - * stored in ssh_connections. ownerId=null = system DEK (global connection), - * ownerId= = user DEK. - */ - encryptKeyMaterial(ownerId: string | null, pem: Buffer, passphrase: Buffer | null, spaceId?: string | null): SshEncryptResult; - - /** - * Decrypt blob using the correct DEK. Buffer must be cleared by caller. - * spaceId (Space-as-Principal P2): when given, the space DEK is tried first - * with a fallback to the owner/system DEK (lets pre-migration owner-encrypted - * blobs of a now-space-owned connection still decrypt). - */ - decryptKeyMaterial(ownerId: string | null, blob: Buffer, spaceId?: string | null): Buffer; - decryptPassphrase(ownerId: string | null, blob: Buffer | null, spaceId?: string | null): Buffer | null; - - /** - * Generate a fresh keypair (no passphrase). The caller is expected to feed - * the returned `privateKeyPem` back through `encryptKeyMaterial` for at-rest - * encryption. - */ - generateKeypair(keyType: 'ed25519' | 'rsa-4096'): { - privateKeyPem: Buffer; - publicKey: string; - }; - - /** - * Derive the OpenSSH-format public key (` `) from the stored - * private-key blob. Handles decrypt + format + buffer-clear internally. - */ - derivePublicKey( - ownerId: string | null, - blob: Buffer, - passphraseBlob: Buffer | null, - spaceId?: string | null, - ): string; - - /** SSH dial helper — injected so tests don't need a real SSH server. */ - sshTester: SshTester; - - /** Connection test timeout. Defaults to 5s. */ - connectionTestTimeoutMs?: number; - - /** - * Optional hook called after a grant is revoked / deleted so the caller can - * kick any active WebSocket viewers that depended on that grant. The hook - * is a no-op pass-through (returns 0) when the SSH console subsystem isn't - * wired (e.g. tests, or `ssh.console.enabled=false`). - * - * Implementation typically calls `SessionRegistry.revokeAccessFor`. - */ - onAccessRevoked?: (args: { connectionId: string; userId: string }) => number | void; -} - -// ────────────────────────────────────────────────────────────────────── -// Helpers -// ────────────────────────────────────────────────────────────────────── - -const REASON_MIN_CHARS = 8; - -function validateReason(reason: unknown): string | null { - if (typeof reason !== 'string') return 'reason is required (string >= 8 chars)'; - if (reason.length < REASON_MIN_CHARS) return `reason must be at least ${REASON_MIN_CHARS} characters`; - return null; -} - -function maintenance503(maintenance: MaintenanceController, res: Response): boolean { - if (!maintenance.isActive()) return false; - res.setHeader('Retry-After', '30'); - res.status(503).json({ - error: 'rotation_in_progress', - detail: 'SSH subsystem is in maintenance — try again shortly.', - snapshot: maintenance.snapshot(), - }); - return true; -} - -function safePort(p: unknown): number | null { - if (typeof p !== 'number' || !Number.isInteger(p) || p < 1 || p > 65535) return null; - return p; -} - -function safeString(v: unknown, max = 1024): string | null { - if (typeof v !== 'string') return null; - if (v.length === 0 || v.length > max) return null; - return v; -} - -function safeOptionalString(v: unknown, max = 1024): string | null | undefined { - if (v === undefined || v === null) return undefined; - if (typeof v !== 'string') return null; - if (v.length > max) return null; - return v; -} - -function safePathPrefix(v: unknown): string | null { - const s = safeString(v, 1024); - if (s === null) return null; - if (s.length === 0) return null; - // Reject any `..` parent-ref segment using either separator. POSIX and - // Windows-style prefixes (drive letters, UNC, no-leading-slash) are - // all allowed; the runtime validateRemotePath enforces containment. - const segments = s.split(/[\\/]/); - if (segments.includes('..')) return null; - return s; -} - -function safeFingerprint(v: unknown): string | null { - if (typeof v !== 'string') return null; - // SHA256: — base64 chars + ='s, ~46 chars typical. - if (!/^SHA256:[A-Za-z0-9+/=]{20,80}$/.test(v)) return null; - return v; -} - -function safeUuid(v: unknown): string | null { - if (typeof v !== 'string') return null; - if (!/^[a-f0-9-]{8,}$/.test(v)) return null; - return v; -} - -function safeExpiresAt(v: unknown): string | null | undefined { - if (v === undefined || v === null) return undefined; - if (typeof v !== 'string') return null; - // ISO8601 — Date.parse returns NaN for invalid strings. - const t = Date.parse(v); - if (Number.isNaN(t)) return null; - return new Date(t).toISOString(); -} - -function safeSubjectType(v: unknown): SshGrantSubjectType | null { - return v === 'user' || v === 'org' ? v : null; -} - -/** - * Normalize a space id from a request body or query (spec §11). Accepts both - * `space_id` (snake, wire convention) and `spaceId` (camel). Returns: string - * (a space), null (explicit global / space-less), or undefined (not given). - * Mirrors mcp-api.readSpaceId. - */ -function readSpaceId(src: Record | undefined): string | null | undefined { - if (!src) return undefined; - const raw = (src.space_id ?? src.spaceId) as unknown; - if (raw === undefined) return undefined; - if (raw === null || raw === '') return null; - // 文字列のみ受理(数値等の coerce を避ける)。 - return typeof raw === 'string' ? raw : undefined; -} - -function presentConnection(conn: SshConnection): Record { - // Strip the encrypted blob fields and the pending verify token (the token - // is only surfaced on /test responses; subsequent GETs do not expose it - // again — the user must hold onto it). - return { - id: conn.id, - ownerId: conn.ownerId, - label: conn.label, - host: conn.host, - port: conn.port, - username: conn.username, - keyVersion: conn.keyVersion, - keyFingerprint: conn.keyFingerprint, - hostKeyType: conn.hostKeyType, - hostKeyFingerprint: conn.hostKeyFingerprint, - hostKeyRecordedAt: conn.hostKeyRecordedAt, - hostKeyVerifiedAt: conn.hostKeyVerifiedAt, - hostKeyPending: conn.hostKeyPending, - hostKeyPendingFingerprint: conn.hostKeyPendingFingerprint, - hostKeyPendingSource: conn.hostKeyPendingSource, - commandDenyPatterns: conn.commandDenyPatterns, - commandAllowPatterns: conn.commandAllowPatterns, - remotePathPrefix: conn.remotePathPrefix, - allowRemoteUnrestricted: conn.allowRemoteUnrestricted, - allowPrivateAddresses: conn.allowPrivateAddresses, - enabled: conn.enabled, - disabledByAdmin: conn.disabledByAdmin, - disabledByAdminReason: conn.disabledByAdminReason, - disabledByAdminAt: conn.disabledByAdminAt, - disabledByAdminUserId: conn.disabledByAdminUserId, - createdAt: conn.createdAt, - updatedAt: conn.updatedAt, - }; -} - -function presentGrant(g: SshGrant): Record { - return { ...g }; -} - -function presentAuditRow(a: SshAuditRow): Record { - return { ...a }; -} - -function safeLimit(v: unknown, def = 50, max = 500): number { - if (typeof v === 'string') { - const n = Number(v); - if (Number.isInteger(n) && n > 0 && n <= max) return n; - } - if (typeof v === 'number' && Number.isInteger(v) && v > 0 && v <= max) return v; - return def; -} - -function jsonError(res: Response, status: number, error: string, detail?: unknown): void { - if (detail === undefined) { - res.status(status).json({ error }); - } else { - res.status(status).json({ error, detail }); - } -} - -// ────────────────────────────────────────────────────────────────────── -// User router — /api/ssh/connections + /api/ssh/grants -// ────────────────────────────────────────────────────────────────────── - -/** - * No-auth single-user mode: synthesize a `local` user so the per-user - * connection store has a stable owner and `getUserId`/`isAdmin` resolve. - * Role `admin` keeps the connection-management surface fully usable for the - * lone local operator (in single-user mode the admin/grant checks only ever - * compare against `local`-owned rows, so this never widens cross-user access). - * No-op when auth is active (Passport has already populated `req.user`). - */ -function makeSshLocalUserMiddleware(authActive: boolean): RequestHandler { - return (req: Request, _res: Response, next: NextFunction) => { - if (!authActive && !(req as { user?: unknown }).user) { - (req as { user?: unknown }).user = { id: 'local', role: 'admin', orgIds: [] }; - } - next(); - }; -} - -export function createSshUserRouter(deps: SshApiDeps): Router { - const router = Router(); - const testTimeoutMs = deps.connectionTestTimeoutMs ?? 5000; - router.use(makeSshLocalUserMiddleware(deps.authActive ?? true)); - - // GET /api/ssh/connections — list owned (and globals visible via grant). - // For Phase 5 we return: - // - all owned (owner_id == userId) - // - all globals (owner_id IS NULL) — visibility is enforced at tool-call - // time via accessResolver, so listing globals here is informational. - // Admins see all via /api/ssh/admin/connections (separate endpoint). - router.get('/connections', deps.requireAuth, async (req, res) => { - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - - // Space-scoped listing (spec §11): ?spaceId= (or ?space_id=) returns - // ONLY that space's connections, visibility-checked against the viewer. - // Omitting it keeps the legacy per-user list (owned + globals). - const spaceId = readSpaceId(req.query as Record); - if (typeof spaceId === 'string') { - if (!deps.getSpace) { jsonError(res, 400, 'space_scoped_listing_unavailable'); return; } - const space = await deps.getSpace(spaceId, (req as Request & { user?: Express.User }).user); - if (!space) { jsonError(res, 404, 'unknown_or_inaccessible_space'); return; } - const scoped = deps.connectionRepo.listBySpace(spaceId); - res.json({ connections: scoped.map(presentConnection) }); - return; - } - - const owned = deps.connectionRepo.listOwned(userId); - const all = deps.connectionRepo.listAll(); - const globals = all.filter((c) => c.ownerId === null); - // Deduplicate (owned should never overlap globals; safety net). - const seen = new Set(owned.map((c) => c.id)); - const list = [...owned]; - for (const g of globals) { - if (!seen.has(g.id)) { list.push(g); seen.add(g.id); } - } - res.json({ connections: list.map(presentConnection) }); - } catch (e) { - logger.warn(`[ssh:api] list user connections failed err=${String(e)}`); - jsonError(res, 500, 'list_failed'); - } - }); - - // POST /api/ssh/connections — create user-owned. - router.post('/connections', deps.requireAuth, async (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const body = req.body ?? {}; - const label = safeString(body.label, 200); - const host = safeString(body.host, 255); - const port = safePort(body.port); - const username = safeString(body.username, 64); - const remotePathPrefix = safePathPrefix(body.remotePathPrefix); - const denyPatterns = safeOptionalString(body.commandDenyPatterns, 4096); - const allowPatterns = safeOptionalString(body.commandAllowPatterns, 4096); - // User-owned create rejects admin-only flags up front. - if (body.allowRemoteUnrestricted === true || body.allowRemoteUnrestricted === 1) { - jsonError(res, 403, 'allow_remote_unrestricted_admin_only'); return; - } - if (body.allowPrivateAddresses === true || body.allowPrivateAddresses === 1) { - jsonError(res, 403, 'allow_private_addresses_admin_only'); return; - } - - // Keypair source: 'provided' (user uploads PEM, default) or - // 'generate' (orchestrator creates a fresh keypair). In the latter case - // the user gets the public key back exactly once in this response. - const keypairSource = body.keypairSource === 'generate' ? 'generate' : 'provided'; - let pemBuf: Buffer; - let passBuf: Buffer | null; - if (keypairSource === 'generate') { - const keyType = body.generateKeyType === 'rsa-4096' ? 'rsa-4096' : 'ed25519'; - const generated = deps.generateKeypair(keyType); - pemBuf = generated.privateKeyPem; - passBuf = null; - } else { - const privateKey = typeof body.privateKeyPem === 'string' ? body.privateKeyPem : null; - const passphrase = typeof body.passphrase === 'string' ? body.passphrase : null; - if (!privateKey) { - jsonError(res, 400, 'invalid_input', { - required: ['label', 'host', 'port (1-65535)', 'username', 'privateKeyPem', 'remotePathPrefix'], - }); - return; - } - pemBuf = Buffer.from(privateKey, 'utf8'); - passBuf = passphrase ? Buffer.from(passphrase, 'utf8') : null; - } - - if (!label || !host || port === null || !username || !remotePathPrefix) { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - jsonError(res, 400, 'invalid_input', { - required: ['label', 'host', 'port (1-65535)', 'username', 'privateKeyPem', 'remotePathPrefix'], - }); - return; - } - if (denyPatterns === null) { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - jsonError(res, 400, 'invalid_command_deny_patterns'); return; - } - if (allowPatterns === null) { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - jsonError(res, 400, 'invalid_command_allow_patterns'); return; - } - - // Per-space scoping (spec §11). When a space is supplied, authorize it - // against the requesting viewer via getSpace BEFORE encrypting/storing. - // owner_id stays = userId (created_by); space_id gates who may USE the - // connection at runtime (space membership) AND — Space-as-Principal P2 — - // selects the SPACE DEK for at-rest encryption so the key survives member - // turnover. Omitting it = legacy space-less (owner-DEK) connection. - const spaceId = readSpaceId(body as Record); - if (typeof spaceId === 'string') { - if (!deps.getSpace) { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - jsonError(res, 400, 'space_scoped_create_unavailable'); return; - } - const space = await deps.getSpace(spaceId, (req as Request & { user?: Express.User }).user); - if (!space) { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - jsonError(res, 404, 'unknown_or_inaccessible_space'); return; - } - } - - const createSpaceId = typeof spaceId === 'string' ? spaceId : null; - let encrypted: SshEncryptResult; - try { - encrypted = deps.encryptKeyMaterial(userId, pemBuf, passBuf, createSpaceId); - } finally { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - } - const conn = deps.connectionRepo.create({ - ownerId: userId, - spaceId: typeof spaceId === 'string' ? spaceId : null, - label, host, port, username, - privateKeyEnc: encrypted.blob, - passphraseEnc: encrypted.passphraseBlob, - keyVersion: encrypted.keyVersion, - keyFingerprint: encrypted.fingerprint, - remotePathPrefix, - commandDenyPatterns: denyPatterns ?? null, - commandAllowPatterns: allowPatterns ?? null, - }); - deps.auditRepo.beginAndComplete( - { - action: 'ssh.connection.upsert', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: userId, - actingUserId: userId, - detail: { op: 'create', label: conn.label, host: conn.host, port: conn.port, keypairSource }, - }, - 'success', - ); - res.status(201).json({ - connection: presentConnection(conn), - publicKey: encrypted.publicKey, - }); - } catch (e) { - logger.warn(`[ssh:api] create user connection failed err=${String(e)}`); - jsonError(res, 500, 'create_failed'); - } - }); - - router.get('/connections/:id', deps.requireAuth, (req, res) => { - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - // Visibility: owner, admin, or global (info-only). Other users see 404 - // (do not leak existence of someone else's user-owned connection). - if (conn.ownerId !== null && conn.ownerId !== userId && !deps.isAdmin(req)) { - jsonError(res, 404, 'not_found'); return; - } - let publicKey: string | null = null; - try { - publicKey = deps.derivePublicKey(conn.ownerId, conn.privateKeyEnc, conn.passphraseEnc, conn.spaceId); - } catch (e) { - // Don't fail the whole response if key derivation fails (e.g. corrupt - // blob, missing passphrase) — return the connection without it and log. - logger.warn(`[ssh:api] derive public key failed id=${conn.id} err=${String(e)}`); - } - res.json({ connection: presentConnection(conn), publicKey }); - } catch (e) { - logger.warn(`[ssh:api] get user connection failed err=${String(e)}`); - jsonError(res, 500, 'get_failed'); - } - }); - - router.patch('/connections/:id', deps.requireAuth, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - // Only owner may PATCH user-owned. Admin PATCHes globals via admin route. - if (conn.ownerId !== userId) { jsonError(res, 403, 'owner_only'); return; } - const body = req.body ?? {}; - - const patch: Parameters[1] = {}; - if (body.label !== undefined) { - const v = safeString(body.label, 200); - if (v === null) { jsonError(res, 400, 'invalid_label'); return; } - patch.label = v; - } - if (body.host !== undefined) { - const v = safeString(body.host, 255); - if (v === null) { jsonError(res, 400, 'invalid_host'); return; } - patch.host = v; - } - if (body.port !== undefined) { - const v = safePort(body.port); - if (v === null) { jsonError(res, 400, 'invalid_port'); return; } - patch.port = v; - } - if (body.username !== undefined) { - const v = safeString(body.username, 64); - if (v === null) { jsonError(res, 400, 'invalid_username'); return; } - patch.username = v; - } - if (body.remotePathPrefix !== undefined) { - const v = safePathPrefix(body.remotePathPrefix); - if (v === null) { jsonError(res, 400, 'invalid_remote_path_prefix'); return; } - patch.remotePathPrefix = v; - } - if (body.commandDenyPatterns !== undefined) { - const v = safeOptionalString(body.commandDenyPatterns, 4096); - if (v === null) { jsonError(res, 400, 'invalid_command_deny_patterns'); return; } - patch.commandDenyPatterns = v ?? null; - } - if (body.commandAllowPatterns !== undefined) { - const v = safeOptionalString(body.commandAllowPatterns, 4096); - if (v === null) { jsonError(res, 400, 'invalid_command_allow_patterns'); return; } - patch.commandAllowPatterns = v ?? null; - } - // Users cannot toggle admin-only flags. - if (body.allowRemoteUnrestricted !== undefined || body.allowPrivateAddresses !== undefined) { - jsonError(res, 403, 'admin_only_flag'); return; - } - // Key rotation (privateKeyPem in patch). Re-encrypt and bump key_version. - if (typeof body.privateKeyPem === 'string') { - let pemBuf = Buffer.from(body.privateKeyPem, 'utf8'); - let passBuf = typeof body.passphrase === 'string' ? Buffer.from(body.passphrase, 'utf8') : null; - try { - // Re-encrypt under the connection's current DEK domain (space DEK when - // space-owned) so a key rotation keeps the space-ownership property. - const enc = deps.encryptKeyMaterial(userId, pemBuf, passBuf, conn.spaceId); - patch.privateKeyEnc = enc.blob; - patch.passphraseEnc = enc.passphraseBlob; - patch.keyVersion = enc.keyVersion; - patch.keyFingerprint = enc.fingerprint; - } finally { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - } - } - const ok = deps.connectionRepo.update(conn.id, patch); - if (!ok) { jsonError(res, 404, 'not_found'); return; } - const updated = deps.connectionRepo.resolveConnection(conn.id); - deps.auditRepo.beginAndComplete( - { - action: 'ssh.connection.upsert', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: userId, - actingUserId: userId, - detail: { op: 'update', fields: Object.keys(patch) }, - }, - 'success', - ); - res.json({ connection: presentConnection(updated!) }); - } catch (e) { - logger.warn(`[ssh:api] patch user connection failed err=${String(e)}`); - jsonError(res, 500, 'update_failed'); - } - }); - - router.delete('/connections/:id', deps.requireAuth, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - if (conn.ownerId !== userId) { jsonError(res, 403, 'owner_only'); return; } - // Begin audit BEFORE delete so FK(connection_id) is still valid. - // FK has ON DELETE SET NULL; complete() updates by audit_id only. - const auditId = deps.auditRepo.begin({ - action: 'ssh.connection.delete', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: userId, - actingUserId: userId, - detail: { label: conn.label, host: conn.host }, - }); - const ok = deps.connectionRepo.delete(conn.id); - if (!ok) { - deps.auditRepo.complete(auditId, 'failed', { err: 'no_changes' }); - jsonError(res, 404, 'not_found'); - return; - } - deps.auditRepo.complete(auditId, 'success'); - res.json({ ok: true }); - } catch (e) { - logger.warn(`[ssh:api] delete user connection failed err=${String(e)}`); - jsonError(res, 500, 'delete_failed'); - } - }); - - // POST /api/ssh/connections/:id/test — capture-only connect. Decrypts the - // key, dials the host (or asks the injected tester to do so), captures the - // host key, and stores it pending with a fresh verify token. Returns the - // fingerprint + token to the user so they can call /verify-host-key next. - router.post('/connections/:id/test', deps.requireAuth, async (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - if (conn.ownerId !== userId && !deps.isAdmin(req)) { - jsonError(res, 403, 'owner_or_admin_only'); return; - } - let decryptedKey: Buffer | null = null; - let passphrase: Buffer | null = null; - let auditId: number | null = null; - try { - auditId = deps.auditRepo.begin({ - action: 'ssh.connection.host_key.tofu_record', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: conn.ownerId, - actingUserId: userId, - detail: { op: 'test' }, - }); - decryptedKey = deps.decryptKeyMaterial(conn.ownerId, conn.privateKeyEnc, conn.spaceId); - passphrase = deps.decryptPassphrase(conn.ownerId, conn.passphraseEnc, conn.spaceId); - const result = await deps.sshTester.test({ - connection: conn, - decryptedKey, - passphrase, - timeoutMs: testTimeoutMs, - }); - // alg_not_allowed: the observed host key uses a banned algorithm - // (e.g. ssh-rsa with SHA1). Don't store as pending — we'd never accept it. - // Surface the fingerprint so the operator can audit/replace the server's key. - if (result.verdict === 'alg_not_allowed') { - deps.auditRepo.complete(auditId, 'denied', { - verdict: result.verdict, - observedFingerprint: result.fingerprint, - hostKeyType: result.hostKeyType, - }); - res.status(502).json({ - error: 'host_key_alg_not_allowed', - verdict: result.verdict, - fingerprint: result.fingerprint, - hostKeyType: result.hostKeyType, - }); - return; - } - // Store the observation as pending for first_observe and mismatch. - // 'pass' means the key already matched the verified record — no token needed. - let token: string | null = null; - if (result.verdict === 'first_observe' || result.verdict === 'mismatch') { - const stored = deps.connectionRepo.setHostKeyPendingWithToken( - conn.id, - result.hostKeyB64, - result.fingerprint, - result.verdict === 'first_observe' ? 'tofu_record' : 'mismatch', - ); - token = stored?.token ?? null; - } - deps.auditRepo.complete(auditId, 'success', { - verdict: result.verdict, - observedFingerprint: result.fingerprint, - }); - res.json({ - verdict: result.verdict, - fingerprint: result.fingerprint, - hostKeyType: result.hostKeyType, - pendingToken: token, - }); - } catch (e) { - if (auditId !== null) { - deps.auditRepo.complete(auditId, 'failed', { err: String(e) }); - } - logger.warn(`[ssh:api] connection test failed id=${conn.id} err=${String(e)}`); - jsonError(res, 502, 'test_failed', { detail: String(e) }); - } finally { - if (decryptedKey) decryptedKey.fill(0); - if (passphrase) passphrase.fill(0); - } - }); - - // POST /api/ssh/connections/:id/verify-host-key - router.post('/connections/:id/verify-host-key', deps.requireAuth, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - if (conn.ownerId !== userId && !deps.isAdmin(req)) { - jsonError(res, 403, 'owner_or_admin_only'); return; - } - const body = req.body ?? {}; - const fingerprint = safeFingerprint(body.fingerprint); - const token = safeUuid(body.token); - if (!fingerprint || !token) { - jsonError(res, 400, 'invalid_input', { required: ['fingerprint', 'token'] }); - return; - } - const result: HostKeyVerifyResult = deps.connectionRepo.setHostKeyVerified(conn.id, token, fingerprint); - deps.auditRepo.beginAndComplete( - { - action: 'ssh.connection.host_key.verify', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: conn.ownerId, - actingUserId: userId, - detail: { result, fingerprint }, - }, - result === 'verified' ? 'success' : 'failed', - ); - if (result === 'verified') { - const updated = deps.connectionRepo.resolveConnection(conn.id)!; - res.json({ ok: true, connection: presentConnection(updated) }); - } else { - jsonError(res, 409, result); - } - } catch (e) { - logger.warn(`[ssh:api] verify host key failed err=${String(e)}`); - jsonError(res, 500, 'verify_failed'); - } - }); - - // POST /api/ssh/connections/:id/replace-host-key — rotate an already-verified key. - router.post('/connections/:id/replace-host-key', deps.requireAuth, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - if (conn.ownerId !== userId && !deps.isAdmin(req)) { - jsonError(res, 403, 'owner_or_admin_only'); return; - } - const body = req.body ?? {}; - const fingerprint = safeFingerprint(body.fingerprint); - const token = safeUuid(body.token); - const reasonErr = validateReason(body.reason); - if (!fingerprint || !token) { - jsonError(res, 400, 'invalid_input', { required: ['fingerprint', 'token', 'reason'] }); - return; - } - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const result: HostKeyVerifyResult = deps.connectionRepo.replaceHostKey(conn.id, token, fingerprint); - deps.auditRepo.beginAndComplete( - { - action: 'ssh.connection.host_key.replace', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: conn.ownerId, - actingUserId: userId, - reason: String(body.reason), - detail: { result, fingerprint }, - }, - result === 'verified' ? 'success' : 'failed', - ); - if (result === 'verified') { - const updated = deps.connectionRepo.resolveConnection(conn.id)!; - res.json({ ok: true, connection: presentConnection(updated) }); - } else { - jsonError(res, 409, result); - } - } catch (e) { - logger.warn(`[ssh:api] replace host key failed err=${String(e)}`); - jsonError(res, 500, 'replace_failed'); - } - }); - - router.get('/connections/:id/audit', deps.requireAuth, (req, res) => { - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - if (conn.ownerId !== userId && !deps.isAdmin(req)) { - jsonError(res, 403, 'owner_or_admin_only'); return; - } - const limit = safeLimit(req.query.limit); - const rows = deps.auditRepo.listForConnection(conn.id, limit); - res.json({ audit: rows.map(presentAuditRow) }); - } catch (e) { - logger.warn(`[ssh:api] audit list failed err=${String(e)}`); - jsonError(res, 500, 'audit_failed'); - } - }); - - // GET /api/ssh/grants/visible-to-me - router.get('/grants/visible-to-me', deps.requireAuth, (req, res) => { - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const orgIds = deps.getOrgIds(req); - const orgPh = orgIds.length > 0 ? orgIds.map(() => '?').join(',') : null; - const sql = ` - SELECT * FROM ssh_connection_grants - WHERE (subject_type = 'user' AND subject_id = ?) - ${orgPh ? `OR (subject_type = 'org' AND subject_id IN (${orgPh}))` : ''} - ORDER BY created_at DESC - `; - const params: unknown[] = [userId]; - if (orgPh) params.push(...orgIds); - const rows = deps.db.prepare(sql).all(...params) as Array<{ - id: string; connection_id: string; subject_type: SshGrantSubjectType; subject_id: string; - piece_name: string | null; applies_to_all_pieces: number; granted_by_user_id: string; - reason: string; expires_at: string | null; created_at: string; - }>; - const grants = rows.map((r) => ({ - id: r.id, - connectionId: r.connection_id, - subjectType: r.subject_type, - subjectId: r.subject_id, - pieceName: r.piece_name, - appliesToAllPieces: r.applies_to_all_pieces === 1, - grantedByUserId: r.granted_by_user_id, - reason: r.reason, - expiresAt: r.expires_at, - createdAt: r.created_at, - })); - res.json({ grants }); - } catch (e) { - logger.warn(`[ssh:api] list visible grants failed err=${String(e)}`); - jsonError(res, 500, 'list_grants_failed'); - } - }); - - return router; -} - -// ────────────────────────────────────────────────────────────────────── -// Admin router — /api/ssh/admin/* -// ────────────────────────────────────────────────────────────────────── - -export function createSshAdminRouter(deps: SshApiDeps): Router { - const router = Router(); - router.use(makeSshLocalUserMiddleware(deps.authActive ?? true)); - - // GET /api/ssh/admin/connections - router.get('/connections', deps.requireAdmin, (_req, res) => { - try { - const list = deps.connectionRepo.listAll(); - res.json({ connections: list.map(presentConnection) }); - } catch (e) { - logger.warn(`[ssh:api] admin list connections failed err=${String(e)}`); - jsonError(res, 500, 'list_failed'); - } - }); - - router.get('/connections/:id', deps.requireAdmin, (req, res) => { - try { - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - let publicKey: string | null = null; - try { - publicKey = deps.derivePublicKey(conn.ownerId, conn.privateKeyEnc, conn.passphraseEnc, conn.spaceId); - } catch (e) { - logger.warn(`[ssh:api] admin derive public key failed id=${conn.id} err=${String(e)}`); - } - res.json({ connection: presentConnection(conn), publicKey }); - } catch (e) { - logger.warn(`[ssh:api] admin get connection failed err=${String(e)}`); - jsonError(res, 500, 'get_failed'); - } - }); - - router.patch('/connections/:id/disable', deps.requireAdmin, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const reasonErr = validateReason(req.body?.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - const ok = deps.connectionRepo.disableByAdmin(conn.id, String(req.body.reason), userId); - if (!ok) { jsonError(res, 404, 'not_found'); return; } - deps.auditRepo.beginAndComplete( - { - action: 'ssh.connection.disable', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: conn.ownerId, - actingUserId: userId, - reason: String(req.body.reason), - }, - 'success', - ); - const updated = deps.connectionRepo.resolveConnection(conn.id)!; - res.json({ connection: presentConnection(updated) }); - } catch (e) { - logger.warn(`[ssh:api] admin disable failed err=${String(e)}`); - jsonError(res, 500, 'disable_failed'); - } - }); - - router.patch('/connections/:id/enable', deps.requireAdmin, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const reasonErr = validateReason(req.body?.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - const ok = deps.connectionRepo.enableByAdmin(conn.id); - if (!ok) { jsonError(res, 404, 'not_found'); return; } - deps.auditRepo.beginAndComplete( - { - action: 'ssh.connection.enable', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: conn.ownerId, - actingUserId: userId, - reason: String(req.body.reason), - }, - 'success', - ); - const updated = deps.connectionRepo.resolveConnection(conn.id)!; - res.json({ connection: presentConnection(updated) }); - } catch (e) { - logger.warn(`[ssh:api] admin enable failed err=${String(e)}`); - jsonError(res, 500, 'enable_failed'); - } - }); - - router.delete('/connections/:id', deps.requireAdmin, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const reasonErr = validateReason(req.body?.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - const auditId = deps.auditRepo.begin({ - action: 'ssh.connection.delete', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: conn.ownerId, - actingUserId: userId, - reason: String(req.body.reason), - }); - const ok = deps.connectionRepo.delete(conn.id); - if (!ok) { - deps.auditRepo.complete(auditId, 'failed', { err: 'no_changes' }); - jsonError(res, 404, 'not_found'); - return; - } - deps.auditRepo.complete(auditId, 'success'); - res.json({ ok: true }); - } catch (e) { - logger.warn(`[ssh:api] admin delete failed err=${String(e)}`); - jsonError(res, 500, 'delete_failed'); - } - }); - - router.post('/connections/:id/force-unlock', deps.requireAdmin, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const reasonErr = validateReason(req.body?.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const decision = deps.forceUnlockLimiter.check(userId); - if (!decision.allowed) { - if (decision.retryAfterSeconds !== undefined) { - res.setHeader('Retry-After', String(decision.retryAfterSeconds)); - } - jsonError(res, 429, 'rate_limited', { retryAfterSeconds: decision.retryAfterSeconds }); - return; - } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - const removed = deps.abuseRepo.reset(`conn:${conn.id}`); - deps.auditRepo.beginAndComplete( - { - action: 'ssh.abuse.unlock_manual', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: conn.ownerId, - actingUserId: userId, - reason: String(req.body.reason), - detail: { removed }, - }, - 'success', - ); - res.json({ ok: true, removed }); - } catch (e) { - logger.warn(`[ssh:api] admin force-unlock failed err=${String(e)}`); - jsonError(res, 500, 'unlock_failed'); - } - }); - - // POST /api/ssh/admin/globals — create a global connection (owner_id=NULL). - router.post('/globals', deps.requireAdmin, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const reasonErr = validateReason(req.body?.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const body = req.body ?? {}; - const label = safeString(body.label, 200); - const host = safeString(body.host, 255); - const port = safePort(body.port); - const username = safeString(body.username, 64); - const denyPatterns = safeOptionalString(body.commandDenyPatterns, 4096); - const allowPatterns = safeOptionalString(body.commandAllowPatterns, 4096); - const allowRemoteUnrestricted = body.allowRemoteUnrestricted === true || body.allowRemoteUnrestricted === 1; - const allowPrivateAddresses = body.allowPrivateAddresses === true || body.allowPrivateAddresses === 1; - - // remote_path_prefix is required UNLESS allow_remote_unrestricted is set. - let remotePathPrefix: string | null; - if (allowRemoteUnrestricted) { - // Stored prefix is '/' (sandbox-effectively-disabled). - remotePathPrefix = '/'; - } else { - remotePathPrefix = safePathPrefix(body.remotePathPrefix); - } - - // Keypair source: 'provided' (admin uploads PEM) or 'generate' (orchestrator - // creates a fresh keypair; the public key is returned exactly once). - const keypairSource = body.keypairSource === 'generate' ? 'generate' : 'provided'; - let pemBuf: Buffer; - let passBuf: Buffer | null; - if (keypairSource === 'generate') { - const keyType = body.generateKeyType === 'rsa-4096' ? 'rsa-4096' : 'ed25519'; - const generated = deps.generateKeypair(keyType); - pemBuf = generated.privateKeyPem; - passBuf = null; - } else { - const privateKey = typeof body.privateKeyPem === 'string' ? body.privateKeyPem : null; - const passphrase = typeof body.passphrase === 'string' ? body.passphrase : null; - if (!privateKey) { - jsonError(res, 400, 'invalid_input', { - required: ['label', 'host', 'port (1-65535)', 'username', 'privateKeyPem', 'remotePathPrefix (or allowRemoteUnrestricted=true)'], - }); - return; - } - pemBuf = Buffer.from(privateKey, 'utf8'); - passBuf = passphrase ? Buffer.from(passphrase, 'utf8') : null; - } - - if (!label || !host || port === null || !username || !remotePathPrefix) { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - jsonError(res, 400, 'invalid_input', { - required: ['label', 'host', 'port (1-65535)', 'username', 'privateKeyPem', 'remotePathPrefix (or allowRemoteUnrestricted=true)'], - }); - return; - } - if (denyPatterns === null) { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - jsonError(res, 400, 'invalid_command_deny_patterns'); return; - } - if (allowPatterns === null) { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - jsonError(res, 400, 'invalid_command_allow_patterns'); return; - } - - let encrypted: SshEncryptResult; - try { - encrypted = deps.encryptKeyMaterial(null, pemBuf, passBuf); - } catch (e) { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - logger.warn(`[ssh:api] admin global encrypt failed err=${String(e)}`); - jsonError(res, 500, 'encrypt_failed'); - return; - } - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - - try { - const conn = deps.connectionRepo.create({ - ownerId: null, - label, host, port, username, - privateKeyEnc: encrypted.blob, - passphraseEnc: encrypted.passphraseBlob, - keyVersion: encrypted.keyVersion, - keyFingerprint: encrypted.fingerprint, - remotePathPrefix, - allowRemoteUnrestricted, - allowPrivateAddresses, - commandDenyPatterns: denyPatterns ?? null, - commandAllowPatterns: allowPatterns ?? null, - }); - deps.auditRepo.beginAndComplete( - { - action: 'ssh.connection.upsert', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: null, - actingUserId: userId, - reason: String(body.reason), - detail: { op: 'create', scope: 'global', allowRemoteUnrestricted, allowPrivateAddresses, keypairSource }, - }, - 'success', - ); - res.status(201).json({ - connection: presentConnection(conn), - publicKey: encrypted.publicKey, - }); - } catch (e) { - logger.warn(`[ssh:api] admin global create failed err=${String(e)}`); - jsonError(res, 500, 'create_failed'); - } - }); - - router.patch('/globals/:id', deps.requireAdmin, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const reasonErr = validateReason(req.body?.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - if (conn.ownerId !== null) { jsonError(res, 400, 'not_global'); return; } - const body = req.body ?? {}; - const patch: Parameters[1] = {}; - if (body.label !== undefined) { - const v = safeString(body.label, 200); - if (v === null) { jsonError(res, 400, 'invalid_label'); return; } - patch.label = v; - } - if (body.host !== undefined) { - const v = safeString(body.host, 255); - if (v === null) { jsonError(res, 400, 'invalid_host'); return; } - patch.host = v; - } - if (body.port !== undefined) { - const v = safePort(body.port); - if (v === null) { jsonError(res, 400, 'invalid_port'); return; } - patch.port = v; - } - if (body.username !== undefined) { - const v = safeString(body.username, 64); - if (v === null) { jsonError(res, 400, 'invalid_username'); return; } - patch.username = v; - } - if (body.remotePathPrefix !== undefined) { - const v = safePathPrefix(body.remotePathPrefix); - if (v === null) { jsonError(res, 400, 'invalid_remote_path_prefix'); return; } - patch.remotePathPrefix = v; - } - if (body.commandDenyPatterns !== undefined) { - const v = safeOptionalString(body.commandDenyPatterns, 4096); - if (v === null) { jsonError(res, 400, 'invalid_command_deny_patterns'); return; } - patch.commandDenyPatterns = v ?? null; - } - if (body.commandAllowPatterns !== undefined) { - const v = safeOptionalString(body.commandAllowPatterns, 4096); - if (v === null) { jsonError(res, 400, 'invalid_command_allow_patterns'); return; } - patch.commandAllowPatterns = v ?? null; - } - if (body.allowRemoteUnrestricted !== undefined) { - patch.allowRemoteUnrestricted = body.allowRemoteUnrestricted === true || body.allowRemoteUnrestricted === 1; - } - if (body.allowPrivateAddresses !== undefined) { - patch.allowPrivateAddresses = body.allowPrivateAddresses === true || body.allowPrivateAddresses === 1; - } - if (typeof body.privateKeyPem === 'string') { - let pemBuf = Buffer.from(body.privateKeyPem, 'utf8'); - let passBuf = typeof body.passphrase === 'string' ? Buffer.from(body.passphrase, 'utf8') : null; - try { - const enc = deps.encryptKeyMaterial(null, pemBuf, passBuf); - patch.privateKeyEnc = enc.blob; - patch.passphraseEnc = enc.passphraseBlob; - patch.keyVersion = enc.keyVersion; - patch.keyFingerprint = enc.fingerprint; - } finally { - pemBuf.fill(0); - if (passBuf) passBuf.fill(0); - } - } - const ok = deps.connectionRepo.update(conn.id, patch); - if (!ok) { jsonError(res, 404, 'not_found'); return; } - deps.auditRepo.beginAndComplete( - { - action: 'ssh.connection.upsert', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: null, - actingUserId: userId, - reason: String(req.body.reason), - detail: { op: 'update', scope: 'global', fields: Object.keys(patch) }, - }, - 'success', - ); - const updated = deps.connectionRepo.resolveConnection(conn.id)!; - res.json({ connection: presentConnection(updated) }); - } catch (e) { - logger.warn(`[ssh:api] admin global patch failed err=${String(e)}`); - jsonError(res, 500, 'update_failed'); - } - }); - - router.delete('/globals/:id', deps.requireAdmin, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const reasonErr = validateReason(req.body?.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const conn = deps.connectionRepo.resolveConnection(req.params.id); - if (!conn) { jsonError(res, 404, 'not_found'); return; } - if (conn.ownerId !== null) { jsonError(res, 400, 'not_global'); return; } - const auditId = deps.auditRepo.begin({ - action: 'ssh.connection.delete', - entityType: 'ssh_connection', - entityId: conn.id, - connectionId: conn.id, - ownerId: null, - actingUserId: userId, - reason: String(req.body.reason), - }); - const ok = deps.connectionRepo.delete(conn.id); - if (!ok) { - deps.auditRepo.complete(auditId, 'failed', { err: 'no_changes' }); - jsonError(res, 404, 'not_found'); - return; - } - deps.auditRepo.complete(auditId, 'success'); - res.json({ ok: true }); - } catch (e) { - logger.warn(`[ssh:api] admin global delete failed err=${String(e)}`); - jsonError(res, 500, 'delete_failed'); - } - }); - - router.get('/grants', deps.requireAdmin, (req, res) => { - try { - const limit = safeLimit(req.query.limit, 100, 1000); - const rows = deps.db.prepare( - `SELECT * FROM ssh_connection_grants ORDER BY created_at DESC LIMIT ?`, - ).all(limit) as Array<{ - id: string; connection_id: string; subject_type: SshGrantSubjectType; subject_id: string; - piece_name: string | null; applies_to_all_pieces: number; granted_by_user_id: string; - reason: string; expires_at: string | null; created_at: string; - }>; - const grants = rows.map((r) => ({ - id: r.id, - connectionId: r.connection_id, - subjectType: r.subject_type, - subjectId: r.subject_id, - pieceName: r.piece_name, - appliesToAllPieces: r.applies_to_all_pieces === 1, - grantedByUserId: r.granted_by_user_id, - reason: r.reason, - expiresAt: r.expires_at, - createdAt: r.created_at, - })); - res.json({ grants }); - } catch (e) { - logger.warn(`[ssh:api] admin list grants failed err=${String(e)}`); - jsonError(res, 500, 'list_grants_failed'); - } - }); - - router.post('/grants', deps.requireAdmin, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const body = req.body ?? {}; - const reasonErr = validateReason(body.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const connectionId = safeUuid(body.connectionId); - const subjectType = safeSubjectType(body.subjectType); - const subjectId = safeString(body.subjectId, 128); - const appliesToAllPieces = body.appliesToAllPieces === true || body.appliesToAllPieces === 1; - const pieceName = safeOptionalString(body.pieceName, 64); - const expiresAt = safeExpiresAt(body.expiresAt); - if (!connectionId || !subjectType || !subjectId) { - jsonError(res, 400, 'invalid_input', { - required: ['connectionId', 'subjectType ("user"|"org")', 'subjectId', 'reason'], - }); - return; - } - if (pieceName === null) { jsonError(res, 400, 'invalid_piece_name'); return; } - if (expiresAt === null) { jsonError(res, 400, 'invalid_expires_at'); return; } - if (!appliesToAllPieces && (!pieceName || pieceName.length === 0)) { - jsonError(res, 400, 'piece_name_required_unless_applies_to_all'); return; - } - if (appliesToAllPieces && pieceName !== undefined && pieceName.length > 0) { - jsonError(res, 400, 'piece_name_conflicts_with_applies_to_all'); return; - } - // Connection must exist before granting. - const conn = deps.connectionRepo.resolveConnection(connectionId); - if (!conn) { jsonError(res, 404, 'connection_not_found'); return; } - let grant: SshGrant; - try { - grant = deps.grantsRepo.create({ - connectionId, - subjectType, - subjectId, - pieceName: appliesToAllPieces ? null : (pieceName ?? null), - appliesToAllPieces, - grantedByUserId: userId, - reason: String(body.reason), - expiresAt, - }); - } catch (e) { - jsonError(res, 400, 'grant_create_failed', { detail: String(e) }); return; - } - deps.auditRepo.beginAndComplete( - { - action: 'ssh.grant.create', - entityType: 'ssh_grant', - entityId: grant.id, - connectionId: conn.id, - ownerId: conn.ownerId, - actingUserId: userId, - reason: String(body.reason), - detail: { subjectType, subjectId, pieceName: grant.pieceName, appliesToAllPieces }, - }, - 'success', - ); - res.status(201).json({ grant: presentGrant(grant) }); - } catch (e) { - logger.warn(`[ssh:api] admin grant create failed err=${String(e)}`); - jsonError(res, 500, 'grant_create_failed'); - } - }); - - router.delete('/grants/:id', deps.requireAdmin, (req, res) => { - if (maintenance503(deps.maintenance, res)) return; - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const reasonErr = validateReason(req.body?.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - const grant = deps.grantsRepo.getById(req.params.id); - if (!grant) { jsonError(res, 404, 'not_found'); return; } - const ok = deps.grantsRepo.delete(grant.id); - if (!ok) { jsonError(res, 404, 'not_found'); return; } - // Kick any active console-WS viewers whose access depended on this grant. - // user-subject grants map 1:1 to a single userId; org-subject grants are - // deferred (next attach will be denied; existing viewers stay until the - // session ends or the org-member's WS reconnects). - let kicked: number | void = 0; - if (grant.subjectType === 'user' && deps.onAccessRevoked) { - kicked = deps.onAccessRevoked({ connectionId: grant.connectionId, userId: grant.subjectId }); - } - deps.auditRepo.beginAndComplete( - { - action: 'ssh.grant.delete', - entityType: 'ssh_grant', - entityId: grant.id, - connectionId: grant.connectionId, - actingUserId: userId, - reason: String(req.body.reason), - detail: { - subjectType: grant.subjectType, - subjectId: grant.subjectId, - pieceName: grant.pieceName, - viewersKicked: typeof kicked === 'number' ? kicked : 0, - }, - }, - 'success', - ); - res.json({ ok: true }); - } catch (e) { - logger.warn(`[ssh:api] admin grant delete failed err=${String(e)}`); - jsonError(res, 500, 'grant_delete_failed'); - } - }); - - // POST /api/ssh/admin/rotate-master-key — stub for v1 (Phase 5). - // Sets the maintenance flag and audits the intent. The actual DEK re-wrap - // job is deferred to a follow-up PR per Phase 5 design note (line 728). - // Returns 501 in the response body but 202 status so the UI can show a - // banner; UI Phase 6 will key off the maintenance snapshot. - router.post('/rotate-master-key', deps.requireAdmin, (req, res) => { - try { - const userId = deps.getUserId(req); - if (!userId) { jsonError(res, 401, 'unauthorized'); return; } - const reasonErr = validateReason(req.body?.reason); - if (reasonErr) { jsonError(res, 400, reasonErr); return; } - if (deps.maintenance.isActive()) { - jsonError(res, 409, 'already_in_progress', { snapshot: deps.maintenance.snapshot() }); - return; - } - const jobId = `rotate-${Date.now().toString(36)}`; - deps.maintenance.enter(String(req.body.reason), jobId); - deps.auditRepo.beginAndComplete( - { - action: 'ssh.master_key.rotate.start', - actingUserId: userId, - reason: String(req.body.reason), - detail: { jobId, status: 'stub', note: 'rotation job not implemented in v1; only maintenance flag set' }, - }, - 'success', - ); - res.status(202).json({ - jobId, - status: 'maintenance_set', - detail: 'maintenance flag is now active; actual DEK re-wrap is not implemented in v1', - notImplemented: true, - }); - } catch (e) { - logger.warn(`[ssh:api] admin rotate-master-key failed err=${String(e)}`); - jsonError(res, 500, 'rotate_failed'); - } - }); - - router.get('/rotate-master-key/:jobId', deps.requireAdmin, (req, res) => { - try { - const snap = deps.maintenance.snapshot(); - if (!snap.active || snap.jobId !== req.params.jobId) { - jsonError(res, 404, 'not_found_or_completed', { snapshot: snap }); - return; - } - res.json({ - jobId: snap.jobId, - status: 'in_progress', - startedAt: snap.enteredAt, - progress: { note: 'v1 stub: no rewrap performed' }, - notImplemented: true, - }); - } catch (e) { - logger.warn(`[ssh:api] admin rotate-master-key get failed err=${String(e)}`); - jsonError(res, 500, 'rotate_get_failed'); - } - }); - - // GET /api/ssh/admin/audit — cross-user audit query. - router.get('/audit', deps.requireAdmin, (req, res) => { - try { - const limit = safeLimit(req.query.limit, 100, 1000); - const action = typeof req.query.action === 'string' ? req.query.action : null; - const ownerId = typeof req.query.ownerId === 'string' ? req.query.ownerId : null; - const connectionId = typeof req.query.connectionId === 'string' ? req.query.connectionId : null; - const outcome = typeof req.query.outcome === 'string' ? req.query.outcome : null; - - const where: string[] = []; - const params: unknown[] = []; - if (action) { where.push('action = ?'); params.push(action); } - if (ownerId) { where.push('owner_id = ?'); params.push(ownerId); } - if (connectionId) { where.push('connection_id = ?'); params.push(connectionId); } - if (outcome) { where.push('outcome = ?'); params.push(outcome); } - const whereClause = where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''; - const sql = `SELECT * FROM ssh_audit_log ${whereClause} ORDER BY started_at DESC LIMIT ?`; - params.push(limit); - const rows = deps.db.prepare(sql).all(...params) as Array<{ - id: number; action: string; entity_type: string | null; entity_id: string | null; - connection_id: string | null; owner_id: string | null; acting_user_id: string | null; - job_id: string | null; piece_name: string | null; outcome: string; - reason: string | null; detail: string | null; started_at: string; completed_at: string | null; - }>; - const audit = rows.map((r) => ({ - id: r.id, - action: r.action, - entityType: r.entity_type, - entityId: r.entity_id, - connectionId: r.connection_id, - ownerId: r.owner_id, - actingUserId: r.acting_user_id, - jobId: r.job_id, - pieceName: r.piece_name, - outcome: r.outcome, - reason: r.reason, - detail: r.detail ? JSON.parse(r.detail) : null, - startedAt: r.started_at, - completedAt: r.completed_at, - })); - res.json({ audit }); - } catch (e) { - logger.warn(`[ssh:api] admin audit list failed err=${String(e)}`); - jsonError(res, 500, 'audit_failed'); - } - }); - - return router; -} +export type { + SshTesterArgs, + SshTesterVerdict, + SshTesterResult, + SshTester, + SshEncryptResult, + SshApiDeps, +} from './ssh-api-shared.js'; +export { createSshUserRouter } from './ssh-user-api.js'; +export { createSshAdminRouter } from './ssh-admin-api.js'; diff --git a/src/bridge/ssh-user-api.ts b/src/bridge/ssh-user-api.ts new file mode 100644 index 0000000..264083a --- /dev/null +++ b/src/bridge/ssh-user-api.ts @@ -0,0 +1,589 @@ +/** + * SSH HTTP layer — user router (`/api/ssh/connections` + `/api/ssh/grants`). + * + * Extracted verbatim from src/bridge/ssh-api.ts (split refactor). Shared + * types / helpers live in ssh-api-shared.ts; the admin router lives in + * ssh-admin-api.ts. Mounted from src/bridge/server.ts (via ssh-subsystem) + * ONLY when `ssh.enabled=true` AND `MCP_ENCRYPTION_KEY` is configured. + * See the design conventions in the ssh-api.ts barrel header. + */ + +import { Router, type Request } from 'express'; + +import type { SshConnectionRepo, HostKeyVerifyResult } from '../ssh/connection-repo.js'; +import type { SshGrantSubjectType } from '../ssh/grants-repo.js'; +import { logger } from '../logger.js'; +import { + type SshApiDeps, + type SshEncryptResult, + validateReason, + maintenance503, + safePort, + safeString, + safeOptionalString, + safePathPrefix, + safeFingerprint, + safeUuid, + readSpaceId, + presentConnection, + presentAuditRow, + safeLimit, + jsonError, + makeSshLocalUserMiddleware, +} from './ssh-api-shared.js'; + +export function createSshUserRouter(deps: SshApiDeps): Router { + const router = Router(); + const testTimeoutMs = deps.connectionTestTimeoutMs ?? 5000; + router.use(makeSshLocalUserMiddleware(deps.authActive ?? true)); + + // GET /api/ssh/connections — list owned (and globals visible via grant). + // For Phase 5 we return: + // - all owned (owner_id == userId) + // - all globals (owner_id IS NULL) — visibility is enforced at tool-call + // time via accessResolver, so listing globals here is informational. + // Admins see all via /api/ssh/admin/connections (separate endpoint). + router.get('/connections', deps.requireAuth, async (req, res) => { + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + + // Space-scoped listing (spec §11): ?spaceId= (or ?space_id=) returns + // ONLY that space's connections, visibility-checked against the viewer. + // Omitting it keeps the legacy per-user list (owned + globals). + const spaceId = readSpaceId(req.query as Record); + if (typeof spaceId === 'string') { + if (!deps.getSpace) { jsonError(res, 400, 'space_scoped_listing_unavailable'); return; } + const space = await deps.getSpace(spaceId, (req as Request & { user?: Express.User }).user); + if (!space) { jsonError(res, 404, 'unknown_or_inaccessible_space'); return; } + const scoped = deps.connectionRepo.listBySpace(spaceId); + res.json({ connections: scoped.map(presentConnection) }); + return; + } + + const owned = deps.connectionRepo.listOwned(userId); + const all = deps.connectionRepo.listAll(); + const globals = all.filter((c) => c.ownerId === null); + // Deduplicate (owned should never overlap globals; safety net). + const seen = new Set(owned.map((c) => c.id)); + const list = [...owned]; + for (const g of globals) { + if (!seen.has(g.id)) { list.push(g); seen.add(g.id); } + } + res.json({ connections: list.map(presentConnection) }); + } catch (e) { + logger.warn(`[ssh:api] list user connections failed err=${String(e)}`); + jsonError(res, 500, 'list_failed'); + } + }); + + // POST /api/ssh/connections — create user-owned. + router.post('/connections', deps.requireAuth, async (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const body = req.body ?? {}; + const label = safeString(body.label, 200); + const host = safeString(body.host, 255); + const port = safePort(body.port); + const username = safeString(body.username, 64); + const remotePathPrefix = safePathPrefix(body.remotePathPrefix); + const denyPatterns = safeOptionalString(body.commandDenyPatterns, 4096); + const allowPatterns = safeOptionalString(body.commandAllowPatterns, 4096); + // User-owned create rejects admin-only flags up front. + if (body.allowRemoteUnrestricted === true || body.allowRemoteUnrestricted === 1) { + jsonError(res, 403, 'allow_remote_unrestricted_admin_only'); return; + } + if (body.allowPrivateAddresses === true || body.allowPrivateAddresses === 1) { + jsonError(res, 403, 'allow_private_addresses_admin_only'); return; + } + + // Keypair source: 'provided' (user uploads PEM, default) or + // 'generate' (orchestrator creates a fresh keypair). In the latter case + // the user gets the public key back exactly once in this response. + const keypairSource = body.keypairSource === 'generate' ? 'generate' : 'provided'; + let pemBuf: Buffer; + let passBuf: Buffer | null; + if (keypairSource === 'generate') { + const keyType = body.generateKeyType === 'rsa-4096' ? 'rsa-4096' : 'ed25519'; + const generated = deps.generateKeypair(keyType); + pemBuf = generated.privateKeyPem; + passBuf = null; + } else { + const privateKey = typeof body.privateKeyPem === 'string' ? body.privateKeyPem : null; + const passphrase = typeof body.passphrase === 'string' ? body.passphrase : null; + if (!privateKey) { + jsonError(res, 400, 'invalid_input', { + required: ['label', 'host', 'port (1-65535)', 'username', 'privateKeyPem', 'remotePathPrefix'], + }); + return; + } + pemBuf = Buffer.from(privateKey, 'utf8'); + passBuf = passphrase ? Buffer.from(passphrase, 'utf8') : null; + } + + if (!label || !host || port === null || !username || !remotePathPrefix) { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + jsonError(res, 400, 'invalid_input', { + required: ['label', 'host', 'port (1-65535)', 'username', 'privateKeyPem', 'remotePathPrefix'], + }); + return; + } + if (denyPatterns === null) { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + jsonError(res, 400, 'invalid_command_deny_patterns'); return; + } + if (allowPatterns === null) { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + jsonError(res, 400, 'invalid_command_allow_patterns'); return; + } + + // Per-space scoping (spec §11). When a space is supplied, authorize it + // against the requesting viewer via getSpace BEFORE encrypting/storing. + // owner_id stays = userId (created_by); space_id gates who may USE the + // connection at runtime (space membership) AND — Space-as-Principal P2 — + // selects the SPACE DEK for at-rest encryption so the key survives member + // turnover. Omitting it = legacy space-less (owner-DEK) connection. + const spaceId = readSpaceId(body as Record); + if (typeof spaceId === 'string') { + if (!deps.getSpace) { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + jsonError(res, 400, 'space_scoped_create_unavailable'); return; + } + const space = await deps.getSpace(spaceId, (req as Request & { user?: Express.User }).user); + if (!space) { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + jsonError(res, 404, 'unknown_or_inaccessible_space'); return; + } + } + + const createSpaceId = typeof spaceId === 'string' ? spaceId : null; + let encrypted: SshEncryptResult; + try { + encrypted = deps.encryptKeyMaterial(userId, pemBuf, passBuf, createSpaceId); + } finally { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + } + const conn = deps.connectionRepo.create({ + ownerId: userId, + spaceId: typeof spaceId === 'string' ? spaceId : null, + label, host, port, username, + privateKeyEnc: encrypted.blob, + passphraseEnc: encrypted.passphraseBlob, + keyVersion: encrypted.keyVersion, + keyFingerprint: encrypted.fingerprint, + remotePathPrefix, + commandDenyPatterns: denyPatterns ?? null, + commandAllowPatterns: allowPatterns ?? null, + }); + deps.auditRepo.beginAndComplete( + { + action: 'ssh.connection.upsert', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: userId, + actingUserId: userId, + detail: { op: 'create', label: conn.label, host: conn.host, port: conn.port, keypairSource }, + }, + 'success', + ); + res.status(201).json({ + connection: presentConnection(conn), + publicKey: encrypted.publicKey, + }); + } catch (e) { + logger.warn(`[ssh:api] create user connection failed err=${String(e)}`); + jsonError(res, 500, 'create_failed'); + } + }); + + router.get('/connections/:id', deps.requireAuth, (req, res) => { + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + // Visibility: owner, admin, or global (info-only). Other users see 404 + // (do not leak existence of someone else's user-owned connection). + if (conn.ownerId !== null && conn.ownerId !== userId && !deps.isAdmin(req)) { + jsonError(res, 404, 'not_found'); return; + } + let publicKey: string | null = null; + try { + publicKey = deps.derivePublicKey(conn.ownerId, conn.privateKeyEnc, conn.passphraseEnc, conn.spaceId); + } catch (e) { + // Don't fail the whole response if key derivation fails (e.g. corrupt + // blob, missing passphrase) — return the connection without it and log. + logger.warn(`[ssh:api] derive public key failed id=${conn.id} err=${String(e)}`); + } + res.json({ connection: presentConnection(conn), publicKey }); + } catch (e) { + logger.warn(`[ssh:api] get user connection failed err=${String(e)}`); + jsonError(res, 500, 'get_failed'); + } + }); + + router.patch('/connections/:id', deps.requireAuth, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + // Only owner may PATCH user-owned. Admin PATCHes globals via admin route. + if (conn.ownerId !== userId) { jsonError(res, 403, 'owner_only'); return; } + const body = req.body ?? {}; + + const patch: Parameters[1] = {}; + if (body.label !== undefined) { + const v = safeString(body.label, 200); + if (v === null) { jsonError(res, 400, 'invalid_label'); return; } + patch.label = v; + } + if (body.host !== undefined) { + const v = safeString(body.host, 255); + if (v === null) { jsonError(res, 400, 'invalid_host'); return; } + patch.host = v; + } + if (body.port !== undefined) { + const v = safePort(body.port); + if (v === null) { jsonError(res, 400, 'invalid_port'); return; } + patch.port = v; + } + if (body.username !== undefined) { + const v = safeString(body.username, 64); + if (v === null) { jsonError(res, 400, 'invalid_username'); return; } + patch.username = v; + } + if (body.remotePathPrefix !== undefined) { + const v = safePathPrefix(body.remotePathPrefix); + if (v === null) { jsonError(res, 400, 'invalid_remote_path_prefix'); return; } + patch.remotePathPrefix = v; + } + if (body.commandDenyPatterns !== undefined) { + const v = safeOptionalString(body.commandDenyPatterns, 4096); + if (v === null) { jsonError(res, 400, 'invalid_command_deny_patterns'); return; } + patch.commandDenyPatterns = v ?? null; + } + if (body.commandAllowPatterns !== undefined) { + const v = safeOptionalString(body.commandAllowPatterns, 4096); + if (v === null) { jsonError(res, 400, 'invalid_command_allow_patterns'); return; } + patch.commandAllowPatterns = v ?? null; + } + // Users cannot toggle admin-only flags. + if (body.allowRemoteUnrestricted !== undefined || body.allowPrivateAddresses !== undefined) { + jsonError(res, 403, 'admin_only_flag'); return; + } + // Key rotation (privateKeyPem in patch). Re-encrypt and bump key_version. + if (typeof body.privateKeyPem === 'string') { + let pemBuf = Buffer.from(body.privateKeyPem, 'utf8'); + let passBuf = typeof body.passphrase === 'string' ? Buffer.from(body.passphrase, 'utf8') : null; + try { + // Re-encrypt under the connection's current DEK domain (space DEK when + // space-owned) so a key rotation keeps the space-ownership property. + const enc = deps.encryptKeyMaterial(userId, pemBuf, passBuf, conn.spaceId); + patch.privateKeyEnc = enc.blob; + patch.passphraseEnc = enc.passphraseBlob; + patch.keyVersion = enc.keyVersion; + patch.keyFingerprint = enc.fingerprint; + } finally { + pemBuf.fill(0); + if (passBuf) passBuf.fill(0); + } + } + const ok = deps.connectionRepo.update(conn.id, patch); + if (!ok) { jsonError(res, 404, 'not_found'); return; } + const updated = deps.connectionRepo.resolveConnection(conn.id); + deps.auditRepo.beginAndComplete( + { + action: 'ssh.connection.upsert', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: userId, + actingUserId: userId, + detail: { op: 'update', fields: Object.keys(patch) }, + }, + 'success', + ); + res.json({ connection: presentConnection(updated!) }); + } catch (e) { + logger.warn(`[ssh:api] patch user connection failed err=${String(e)}`); + jsonError(res, 500, 'update_failed'); + } + }); + + router.delete('/connections/:id', deps.requireAuth, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + if (conn.ownerId !== userId) { jsonError(res, 403, 'owner_only'); return; } + // Begin audit BEFORE delete so FK(connection_id) is still valid. + // FK has ON DELETE SET NULL; complete() updates by audit_id only. + const auditId = deps.auditRepo.begin({ + action: 'ssh.connection.delete', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: userId, + actingUserId: userId, + detail: { label: conn.label, host: conn.host }, + }); + const ok = deps.connectionRepo.delete(conn.id); + if (!ok) { + deps.auditRepo.complete(auditId, 'failed', { err: 'no_changes' }); + jsonError(res, 404, 'not_found'); + return; + } + deps.auditRepo.complete(auditId, 'success'); + res.json({ ok: true }); + } catch (e) { + logger.warn(`[ssh:api] delete user connection failed err=${String(e)}`); + jsonError(res, 500, 'delete_failed'); + } + }); + + // POST /api/ssh/connections/:id/test — capture-only connect. Decrypts the + // key, dials the host (or asks the injected tester to do so), captures the + // host key, and stores it pending with a fresh verify token. Returns the + // fingerprint + token to the user so they can call /verify-host-key next. + router.post('/connections/:id/test', deps.requireAuth, async (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + if (conn.ownerId !== userId && !deps.isAdmin(req)) { + jsonError(res, 403, 'owner_or_admin_only'); return; + } + let decryptedKey: Buffer | null = null; + let passphrase: Buffer | null = null; + let auditId: number | null = null; + try { + auditId = deps.auditRepo.begin({ + action: 'ssh.connection.host_key.tofu_record', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: conn.ownerId, + actingUserId: userId, + detail: { op: 'test' }, + }); + decryptedKey = deps.decryptKeyMaterial(conn.ownerId, conn.privateKeyEnc, conn.spaceId); + passphrase = deps.decryptPassphrase(conn.ownerId, conn.passphraseEnc, conn.spaceId); + const result = await deps.sshTester.test({ + connection: conn, + decryptedKey, + passphrase, + timeoutMs: testTimeoutMs, + }); + // alg_not_allowed: the observed host key uses a banned algorithm + // (e.g. ssh-rsa with SHA1). Don't store as pending — we'd never accept it. + // Surface the fingerprint so the operator can audit/replace the server's key. + if (result.verdict === 'alg_not_allowed') { + deps.auditRepo.complete(auditId, 'denied', { + verdict: result.verdict, + observedFingerprint: result.fingerprint, + hostKeyType: result.hostKeyType, + }); + res.status(502).json({ + error: 'host_key_alg_not_allowed', + verdict: result.verdict, + fingerprint: result.fingerprint, + hostKeyType: result.hostKeyType, + }); + return; + } + // Store the observation as pending for first_observe and mismatch. + // 'pass' means the key already matched the verified record — no token needed. + let token: string | null = null; + if (result.verdict === 'first_observe' || result.verdict === 'mismatch') { + const stored = deps.connectionRepo.setHostKeyPendingWithToken( + conn.id, + result.hostKeyB64, + result.fingerprint, + result.verdict === 'first_observe' ? 'tofu_record' : 'mismatch', + ); + token = stored?.token ?? null; + } + deps.auditRepo.complete(auditId, 'success', { + verdict: result.verdict, + observedFingerprint: result.fingerprint, + }); + res.json({ + verdict: result.verdict, + fingerprint: result.fingerprint, + hostKeyType: result.hostKeyType, + pendingToken: token, + }); + } catch (e) { + if (auditId !== null) { + deps.auditRepo.complete(auditId, 'failed', { err: String(e) }); + } + logger.warn(`[ssh:api] connection test failed id=${conn.id} err=${String(e)}`); + jsonError(res, 502, 'test_failed', { detail: String(e) }); + } finally { + if (decryptedKey) decryptedKey.fill(0); + if (passphrase) passphrase.fill(0); + } + }); + + // POST /api/ssh/connections/:id/verify-host-key + router.post('/connections/:id/verify-host-key', deps.requireAuth, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + if (conn.ownerId !== userId && !deps.isAdmin(req)) { + jsonError(res, 403, 'owner_or_admin_only'); return; + } + const body = req.body ?? {}; + const fingerprint = safeFingerprint(body.fingerprint); + const token = safeUuid(body.token); + if (!fingerprint || !token) { + jsonError(res, 400, 'invalid_input', { required: ['fingerprint', 'token'] }); + return; + } + const result: HostKeyVerifyResult = deps.connectionRepo.setHostKeyVerified(conn.id, token, fingerprint); + deps.auditRepo.beginAndComplete( + { + action: 'ssh.connection.host_key.verify', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: conn.ownerId, + actingUserId: userId, + detail: { result, fingerprint }, + }, + result === 'verified' ? 'success' : 'failed', + ); + if (result === 'verified') { + const updated = deps.connectionRepo.resolveConnection(conn.id)!; + res.json({ ok: true, connection: presentConnection(updated) }); + } else { + jsonError(res, 409, result); + } + } catch (e) { + logger.warn(`[ssh:api] verify host key failed err=${String(e)}`); + jsonError(res, 500, 'verify_failed'); + } + }); + + // POST /api/ssh/connections/:id/replace-host-key — rotate an already-verified key. + router.post('/connections/:id/replace-host-key', deps.requireAuth, (req, res) => { + if (maintenance503(deps.maintenance, res)) return; + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + if (conn.ownerId !== userId && !deps.isAdmin(req)) { + jsonError(res, 403, 'owner_or_admin_only'); return; + } + const body = req.body ?? {}; + const fingerprint = safeFingerprint(body.fingerprint); + const token = safeUuid(body.token); + const reasonErr = validateReason(body.reason); + if (!fingerprint || !token) { + jsonError(res, 400, 'invalid_input', { required: ['fingerprint', 'token', 'reason'] }); + return; + } + if (reasonErr) { jsonError(res, 400, reasonErr); return; } + const result: HostKeyVerifyResult = deps.connectionRepo.replaceHostKey(conn.id, token, fingerprint); + deps.auditRepo.beginAndComplete( + { + action: 'ssh.connection.host_key.replace', + entityType: 'ssh_connection', + entityId: conn.id, + connectionId: conn.id, + ownerId: conn.ownerId, + actingUserId: userId, + reason: String(body.reason), + detail: { result, fingerprint }, + }, + result === 'verified' ? 'success' : 'failed', + ); + if (result === 'verified') { + const updated = deps.connectionRepo.resolveConnection(conn.id)!; + res.json({ ok: true, connection: presentConnection(updated) }); + } else { + jsonError(res, 409, result); + } + } catch (e) { + logger.warn(`[ssh:api] replace host key failed err=${String(e)}`); + jsonError(res, 500, 'replace_failed'); + } + }); + + router.get('/connections/:id/audit', deps.requireAuth, (req, res) => { + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const conn = deps.connectionRepo.resolveConnection(req.params.id); + if (!conn) { jsonError(res, 404, 'not_found'); return; } + if (conn.ownerId !== userId && !deps.isAdmin(req)) { + jsonError(res, 403, 'owner_or_admin_only'); return; + } + const limit = safeLimit(req.query.limit); + const rows = deps.auditRepo.listForConnection(conn.id, limit); + res.json({ audit: rows.map(presentAuditRow) }); + } catch (e) { + logger.warn(`[ssh:api] audit list failed err=${String(e)}`); + jsonError(res, 500, 'audit_failed'); + } + }); + + // GET /api/ssh/grants/visible-to-me + router.get('/grants/visible-to-me', deps.requireAuth, (req, res) => { + try { + const userId = deps.getUserId(req); + if (!userId) { jsonError(res, 401, 'unauthorized'); return; } + const orgIds = deps.getOrgIds(req); + const orgPh = orgIds.length > 0 ? orgIds.map(() => '?').join(',') : null; + const sql = ` + SELECT * FROM ssh_connection_grants + WHERE (subject_type = 'user' AND subject_id = ?) + ${orgPh ? `OR (subject_type = 'org' AND subject_id IN (${orgPh}))` : ''} + ORDER BY created_at DESC + `; + const params: unknown[] = [userId]; + if (orgPh) params.push(...orgIds); + const rows = deps.db.prepare(sql).all(...params) as Array<{ + id: string; connection_id: string; subject_type: SshGrantSubjectType; subject_id: string; + piece_name: string | null; applies_to_all_pieces: number; granted_by_user_id: string; + reason: string; expires_at: string | null; created_at: string; + }>; + const grants = rows.map((r) => ({ + id: r.id, + connectionId: r.connection_id, + subjectType: r.subject_type, + subjectId: r.subject_id, + pieceName: r.piece_name, + appliesToAllPieces: r.applies_to_all_pieces === 1, + grantedByUserId: r.granted_by_user_id, + reason: r.reason, + expiresAt: r.expires_at, + createdAt: r.created_at, + })); + res.json({ grants }); + } catch (e) { + logger.warn(`[ssh:api] list visible grants failed err=${String(e)}`); + jsonError(res, 500, 'list_grants_failed'); + } + }); + + return router; +} diff --git a/src/bridge/ui-static.ts b/src/bridge/ui-static.ts new file mode 100644 index 0000000..35f09de --- /dev/null +++ b/src/bridge/ui-static.ts @@ -0,0 +1,36 @@ +import express, { Request, Response } from 'express'; +import { existsSync } from 'fs'; +import { join, resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __filenameUiStatic = fileURLToPath(import.meta.url); +const __dirnameUiStatic = dirname(__filenameUiStatic); + +/** + * Root redirect + built-UI static hosting + the headless app-harness page, + * extracted verbatim from createCoreServer (bridge/server.ts). This module + * lives in the same directory as server.ts, so the ../../ui/dist resolution + * is identical at runtime. + */ +export function mountUiStatic(app: express.Application): void { + // Redirect root to UI + app.get('/', (_req: Request, res: Response) => { + res.redirect('/ui'); + }); + + const uiDistPath = resolve(join(__dirnameUiStatic, '../../ui/dist')); + if (existsSync(uiDistPath)) { + app.use('/ui', express.static(uiDistPath)); + app.get('/ui/*', (_req, res) => { + res.sendFile(join(uiDistPath, 'index.html')); + }); + // Headless test-harness page for workspace-app E2E (Task 5/6). + // Must be registered BEFORE any SPA catch-all and BEFORE space-api routes + // so that the app-harness auth middleware (mounted below) sees the request. + // The harness JS/CSS chunks are served by the existing /ui static mount + // because Vite builds them with base: '/ui/' → /ui/assets/… paths. + app.get('/app-harness', (_req, res) => { + res.sendFile(join(uiDistPath, 'app-harness.html')); + }); + } +} diff --git a/src/config.a2a-limits.test.ts b/src/config.a2a-limits.test.ts new file mode 100644 index 0000000..9cf7287 --- /dev/null +++ b/src/config.a2a-limits.test.ts @@ -0,0 +1,26 @@ +// @vitest-environment node +import { describe, it, expect } from 'vitest'; +import { transformKeys } from './config.js'; + +describe('a2a.limits transform', () => { + it('maps all six snake_case keys to camelCase', () => { + const out = transformKeys({ + a2a: { + limits: { + rate_per_minute: 42, + max_concurrent_tasks: 3, + max_payload_bytes: 1024, + max_stream_seconds: 120, + skill_budget_per_hour: 50, + max_concurrent_resubscribe: 4, + }, + }, + }) as any; + expect(out.a2a.limits.ratePerMinute).toBe(42); + expect(out.a2a.limits.maxConcurrentTasks).toBe(3); + expect(out.a2a.limits.maxPayloadBytes).toBe(1024); + expect(out.a2a.limits.maxStreamSeconds).toBe(120); + expect(out.a2a.limits.skillBudgetPerHour).toBe(50); + expect(out.a2a.limits.maxConcurrentResubscribe).toBe(4); + }); +}); diff --git a/src/config.ts b/src/config.ts index 2e61a75..5e71fc1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -550,6 +550,14 @@ export interface AppConfig { enabled?: boolean; issuer?: string; resourceAudience?: string; + limits?: { + ratePerMinute?: number; + maxConcurrentTasks?: number; + maxPayloadBytes?: number; + maxStreamSeconds?: number; + skillBudgetPerHour?: number; + maxConcurrentResubscribe?: number; + }; }; } @@ -624,7 +632,7 @@ function toCamel(s: string): string { return s.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase()); } -function transformKeys(obj: unknown): unknown { +export function transformKeys(obj: unknown): unknown { if (Array.isArray(obj)) return obj.map(transformKeys); if (obj !== null && typeof obj === 'object') { return Object.fromEntries( diff --git a/src/db/migrate.ts b/src/db/migrate.ts index 98ca280..ca07ae8 100644 --- a/src/db/migrate.ts +++ b/src/db/migrate.ts @@ -69,6 +69,29 @@ export function runMigrations(db: Database.Database): void { CREATE INDEX IF NOT EXISTS idx_tool_requests_dedup ON tool_requests (job_id, movement_name, tool_name, status); `); + // Package requests: agent-declared (RequestPackage) requests to install a + // Python wheel into the task's space overlay, pending a task-write approver. + // Keep in sync with schema.sql (fresh path) and Repository.initSchema. + db.exec(` + CREATE TABLE IF NOT EXISTS package_requests ( + id TEXT PRIMARY KEY, + task_id TEXT, + job_id TEXT, + space_id TEXT, + piece_name TEXT, + movement_name TEXT, + spec TEXT NOT NULL, + normalized_name TEXT NOT NULL, + reason TEXT, + status TEXT NOT NULL DEFAULT 'pending', + decided_by TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + decided_at TEXT + ); + CREATE INDEX IF NOT EXISTS idx_package_requests_task ON package_requests (task_id); + CREATE INDEX IF NOT EXISTS idx_package_requests_dedup ON package_requests (job_id, normalized_name, status); + `); + // A2A: oidc-provider 永続化 db.exec(` CREATE TABLE IF NOT EXISTS oidc_models ( diff --git a/src/db/repositories/a2a.ts b/src/db/repositories/a2a.ts new file mode 100644 index 0000000..3e1dec3 --- /dev/null +++ b/src/db/repositories/a2a.ts @@ -0,0 +1,336 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// a2a repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; + +export interface A2aClientRow { + clientId: string; + name: string; + redirectUris: string[]; + secretHash: string | null; + tokenEndpointAuthMethod: string; + agentCardUrl: string | null; + issuer: string | null; + keyFingerprint: string | null; + status: string; + createdBy: string | null; + createdAt?: string; +} + + +export interface A2aDelegationRow { + id: string; + userId: string; + clientId: string; + grantId: string | null; + grantedSpaceIds: string[]; + grantedSkills: string[]; + audience: string | null; + expiresAt: string | null; + revokedAt: string | null; + createdAt?: string; +} + + + export function oidcUpsert(db: Database.Database, model: string, id: string, payload: object, opts: { expiresAt?: number | null; grantId?: string | null; userCode?: string | null; uid?: string | null } = {}): void { + db.prepare(` + INSERT INTO oidc_models (model, id, payload, grant_id, user_code, uid, expires_at, consumed_at) + VALUES (@model, @id, @payload, @grantId, @userCode, @uid, @expiresAt, NULL) + ON CONFLICT(model, id) DO UPDATE SET + payload = excluded.payload, + grant_id = excluded.grant_id, + user_code = excluded.user_code, + uid = excluded.uid, + expires_at = excluded.expires_at + `).run({ + model, id, + payload: JSON.stringify(payload), + grantId: opts.grantId ?? null, + userCode: opts.userCode ?? null, + uid: opts.uid ?? null, + expiresAt: opts.expiresAt ?? null, + }); + } + + + export function oidcFind(db: Database.Database, model: string, id: string): { payload: Record; consumedAt: number | null } | undefined { + const row = db.prepare( + 'SELECT payload, consumed_at FROM oidc_models WHERE model = ? AND id = ?', + ).get(model, id) as { payload: string; consumed_at: number | null } | undefined; + if (!row) return undefined; + return { payload: JSON.parse(row.payload), consumedAt: row.consumed_at }; + } + + + export function oidcFindByUid(db: Database.Database, uid: string): { payload: Record } | undefined { + const row = db.prepare( + "SELECT payload FROM oidc_models WHERE model = 'Session' AND uid = ?", + ).get(uid) as { payload: string } | undefined; + return row ? { payload: JSON.parse(row.payload) } : undefined; + } + + + export function oidcFindByUserCode(db: Database.Database, userCode: string): { payload: Record } | undefined { + const row = db.prepare( + 'SELECT payload FROM oidc_models WHERE user_code = ?', + ).get(userCode) as { payload: string } | undefined; + return row ? { payload: JSON.parse(row.payload) } : undefined; + } + + + export function oidcConsume(db: Database.Database, model: string, id: string, consumedAt: number): void { + db.prepare('UPDATE oidc_models SET consumed_at = ? WHERE model = ? AND id = ?') + .run(consumedAt, model, id); + } + + + export function oidcDestroy(db: Database.Database, model: string, id: string): void { + db.prepare('DELETE FROM oidc_models WHERE model = ? AND id = ?').run(model, id); + } + + + export function oidcRevokeByGrantId(db: Database.Database, grantId: string): void { + db.prepare('DELETE FROM oidc_models WHERE grant_id = ?').run(grantId); + } + + + export function createA2aClient(db: Database.Database, row: A2aClientRow): void { + db.prepare(` + INSERT INTO a2a_clients + (client_id, name, redirect_uris, secret_hash, token_endpoint_auth_method, + agent_card_url, issuer, key_fingerprint, status, created_by) + VALUES (@clientId, @name, @redirectUris, @secretHash, @tokenEndpointAuthMethod, + @agentCardUrl, @issuer, @keyFingerprint, @status, @createdBy) + `).run({ + ...row, + redirectUris: JSON.stringify(row.redirectUris), + }); + } + + + export function getA2aClient(db: Database.Database, clientId: string): A2aClientRow | undefined { + const r = db.prepare('SELECT * FROM a2a_clients WHERE client_id = ?').get(clientId) as any; + if (!r) return undefined; + return { + clientId: r.client_id, name: r.name, redirectUris: JSON.parse(r.redirect_uris), + secretHash: r.secret_hash, tokenEndpointAuthMethod: r.token_endpoint_auth_method, + agentCardUrl: r.agent_card_url, issuer: r.issuer, keyFingerprint: r.key_fingerprint, + status: r.status, createdBy: r.created_by, createdAt: r.created_at, + }; + } + + + export function listA2aClients(db: Database.Database): A2aClientRow[] { + const rows = db.prepare('SELECT * FROM a2a_clients ORDER BY created_at DESC').all() as any[]; + return rows.map(r => ({ + clientId: r.client_id, name: r.name, redirectUris: JSON.parse(r.redirect_uris), + secretHash: r.secret_hash, tokenEndpointAuthMethod: r.token_endpoint_auth_method, + agentCardUrl: r.agent_card_url, issuer: r.issuer, keyFingerprint: r.key_fingerprint, + status: r.status, createdBy: r.created_by, createdAt: r.created_at, + })); + } + + + export function setA2aClientStatus(db: Database.Database, clientId: string, status: 'active' | 'disabled'): void { + db.prepare('UPDATE a2a_clients SET status = ? WHERE client_id = ?').run(status, clientId); + } + + + export function createA2aDelegation(db: Database.Database, row: A2aDelegationRow): void { + db.prepare(` + INSERT INTO a2a_delegations + (id, user_id, client_id, grant_id, granted_space_ids, granted_skills, audience, expires_at, revoked_at) + VALUES (@id, @userId, @clientId, @grantId, @grantedSpaceIds, @grantedSkills, @audience, @expiresAt, @revokedAt) + `).run({ + id: row.id, userId: row.userId, clientId: row.clientId, grantId: row.grantId, + grantedSpaceIds: JSON.stringify(row.grantedSpaceIds), + grantedSkills: JSON.stringify(row.grantedSkills), + audience: row.audience, expiresAt: row.expiresAt, revokedAt: row.revokedAt, + }); + } + + + export function mapDelegation(r: any): A2aDelegationRow { + return { + id: r.id, userId: r.user_id, clientId: r.client_id, grantId: r.grant_id, + grantedSpaceIds: JSON.parse(r.granted_space_ids || '[]'), + grantedSkills: JSON.parse(r.granted_skills || '[]'), + audience: r.audience, expiresAt: r.expires_at, revokedAt: r.revoked_at, createdAt: r.created_at, + }; + } + + + export function getA2aDelegationByGrantId(db: Database.Database, grantId: string): A2aDelegationRow | undefined { + const r = db.prepare('SELECT * FROM a2a_delegations WHERE grant_id = ?').get(grantId); + return r ? mapDelegation(r) : undefined; + } + + + export function listA2aDelegationsForUser(db: Database.Database, userId: string): A2aDelegationRow[] { + return (db.prepare('SELECT * FROM a2a_delegations WHERE user_id = ? ORDER BY created_at DESC').all(userId) as any[]) + .map(r => mapDelegation(r)); + } + + + export function revokeA2aDelegation(db: Database.Database, id: string, revokedAtIso: string): void { + // revoked_at IS NULL ガード: 二重失効で元の失効時刻を上書きしない(監査の forensic 記録を守る) + db + .prepare('UPDATE a2a_delegations SET revoked_at = ? WHERE id = ? AND revoked_at IS NULL') + .run(revokedAtIso, id); + } + + + export function getA2aDelegationById(db: Database.Database, id: string): A2aDelegationRow | undefined { + const r = db.prepare('SELECT * FROM a2a_delegations WHERE id = ?').get(id); + return r ? mapDelegation(r) : undefined; + } + + + export function listLiveA2aDelegationsForClient(db: Database.Database, clientId: string, nowIso: string): A2aDelegationRow[] { + const rows = db.prepare( + `SELECT * FROM a2a_delegations + WHERE client_id = ? AND revoked_at IS NULL + AND (expires_at IS NULL OR expires_at > ?) + ORDER BY created_at DESC`, + ).all(clientId, nowIso); + return (rows as any[]).map((r) => mapDelegation(r)); + } + + + export function listNonTerminalA2aTasksByGrant(db: Database.Database, grantId: string, limit = 500): Array<{ id: string; jobId: string | null; payload: Record }> { + const rows = db.prepare( + `SELECT id, job_id, payload FROM a2a_tasks + WHERE grant_id = ? + AND (json_extract(payload, '$.status.state') NOT IN + ('completed','failed','canceled','rejected') + OR json_extract(payload, '$.status.state') IS NULL) + ORDER BY updated_at + LIMIT ?`, + ).all(grantId, limit); + return (rows as any[]).map((r) => ({ + id: r.id, + jobId: r.job_id ?? null, + payload: JSON.parse(r.payload ?? '{}'), + })); + } + + + export function countNonTerminalA2aTasksByGrant(db: Database.Database, grantId: string): number { + // Concurrency cap counts tasks that currently (or imminently) consume a job slot. + // 'input-required' is excluded ONLY while its linked job is genuinely parked + // (waiting_human) or gone — a parked task holds no live worker/LLM capacity, so it must + // not block new task creation. But if the MAESTRO-side human answers the ASK, the job + // resumes (waiting_human → running) while the a2a_tasks payload may still read + // 'input-required'; that resumed task DOES consume capacity and must be counted. + // Keying the exclusion on the live job status (via LEFT JOIN) instead of the frozen + // payload state closes a concurrency-cap bypass by ASK-park-then-resume. + // NOTE: listNonTerminalA2aTasksByGrant intentionally keeps the 4-state exclusion (no + // input-required) so the revocation cascade can still find and cancel parked tasks. + const row = db.prepare( + `SELECT COUNT(*) AS n FROM a2a_tasks t + LEFT JOIN jobs j ON j.id = t.job_id + WHERE t.grant_id = ? + AND (json_extract(t.payload, '$.status.state') NOT IN + ('completed','failed','canceled','rejected') + OR json_extract(t.payload, '$.status.state') IS NULL) + AND NOT (json_extract(t.payload, '$.status.state') = 'input-required' + AND COALESCE(j.status, 'waiting_human') = 'waiting_human')`, + ).get(grantId); + return (row as { n: number }).n; + } + + + export function cancelA2aLinkedJob(db: Database.Database, jobId: string): boolean { + const res = db.prepare( + `UPDATE jobs SET status = 'cancelled', updated_at = datetime('now') + WHERE id = ? AND status IN + ('queued','dispatching','running','waiting_human','waiting_subtasks','retry')`, + ).run(jobId); + return res.changes > 0; + } + + + export function listA2aDelegationsForUserWithClient(db: Database.Database, userId: string): Array { + const rows = db.prepare( + `SELECT d.*, c.name AS client_name, c.status AS client_status + FROM a2a_delegations d + LEFT JOIN a2a_clients c ON c.client_id = d.client_id + WHERE d.user_id = ? + ORDER BY d.created_at DESC`, + ).all(userId); + return (rows as any[]).map((r) => ({ + ...mapDelegation(r), + clientName: r.client_name ?? null, + clientStatus: r.client_status ?? null, + })); + } + + + export function listA2aDelegationsAllWithClient(db: Database.Database): Array< + A2aDelegationRow & { userId: string; clientName: string | null; clientStatus: string | null } + > { + const rows = db.prepare( + `SELECT d.*, c.name AS client_name, c.status AS client_status + FROM a2a_delegations d + LEFT JOIN a2a_clients c ON c.client_id = d.client_id + ORDER BY d.created_at DESC`, + ).all(); + return (rows as any[]).map((r) => ({ + ...mapDelegation(r), + clientName: r.client_name ?? null, + clientStatus: r.client_status ?? null, + })); + } + + + export function saveA2aTask(db: Database.Database, row: { + id: string; + contextId: string | null; + jobId: string | null; + localTaskId: number | null; + payload: object; + delegationId?: string; + grantId?: string; + actingUserId?: string; + }): void { + db.prepare(` + INSERT INTO a2a_tasks (id, context_id, job_id, local_task_id, payload, updated_at, delegation_id, grant_id, acting_user_id) + VALUES (@id, @contextId, @jobId, @localTaskId, @payload, datetime('now'), @delegationId, @grantId, @actingUserId) + ON CONFLICT(id) DO UPDATE SET + context_id = excluded.context_id, job_id = excluded.job_id, + local_task_id = excluded.local_task_id, payload = excluded.payload, updated_at = datetime('now'), + delegation_id = excluded.delegation_id, grant_id = excluded.grant_id, acting_user_id = excluded.acting_user_id + `).run({ + id: row.id, + contextId: row.contextId, + jobId: row.jobId, + localTaskId: row.localTaskId, + payload: JSON.stringify(row.payload), + delegationId: row.delegationId ?? null, + grantId: row.grantId ?? null, + actingUserId: row.actingUserId ?? null, + }); + } + + + export function loadA2aTask(db: Database.Database, id: string): { payload: Record } | undefined { + const r = db.prepare('SELECT payload FROM a2a_tasks WHERE id = ?').get(id) as { payload: string } | undefined; + return r ? { payload: JSON.parse(r.payload) } : undefined; + } + + + export function getA2aTaskByJobId(db: Database.Database, jobId: string): { id: string; payload: Record } | undefined { + const r = db.prepare('SELECT id, payload FROM a2a_tasks WHERE job_id = ?').get(jobId) as { id: string; payload: string } | undefined; + return r ? { id: r.id, payload: JSON.parse(r.payload) } : undefined; + } + + + export function listNonTerminalA2aTasks(db: Database.Database, limit = 500): Array<{ id: string; jobId: string | null; payload: Record }> { + const rows = db.prepare(` + SELECT id, job_id, payload FROM a2a_tasks + WHERE json_extract(payload, '$.status.state') NOT IN ('completed', 'failed', 'canceled', 'rejected') + OR json_extract(payload, '$.status.state') IS NULL + ORDER BY updated_at + LIMIT ? + `).all(limit) as Array<{ id: string; job_id: string | null; payload: string }>; + return rows.map(r => ({ id: r.id, jobId: r.job_id, payload: JSON.parse(r.payload) })); + } diff --git a/src/db/repositories/app-shares.ts b/src/db/repositories/app-shares.ts new file mode 100644 index 0000000..abaee83 --- /dev/null +++ b/src/db/repositories/app-shares.ts @@ -0,0 +1,69 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// app-shares repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { randomUUID } from 'crypto'; + + // ─── 公開アプリ共有リンク(read-only) ─────────────────────────────── + // + // スペース内の 1 ワークスペースアプリ(apps/{appName}/)を、ログイン不要の + // 公開 URL で read-only 共有するためのトークン。失効後の再発行は新トークンを + // 作る(失効済みトークンは再利用しない)ため、未失効リンクが既にある場合のみ + // 再利用する。 + + /** (spaceId, appName) の未失効リンクがあれば再利用、無ければ新規発行して token を返す。 */ + export function createAppShareLink(db: Database.Database, spaceId: string, appName: string, createdBy: string | null): { token: string } { + const existing = db + .prepare( + `SELECT token FROM app_share_links + WHERE space_id = ? AND app_name = ? AND revoked_at IS NULL + ORDER BY created_at DESC LIMIT 1`, + ) + .get(spaceId, appName) as { token: string } | undefined; + if (existing) return { token: existing.token }; + const token = randomUUID(); + db + .prepare( + `INSERT INTO app_share_links (token, space_id, app_name, created_by) + VALUES (?, ?, ?, ?)`, + ) + .run(token, spaceId, appName, createdBy ?? null); + return { token }; + } + + + /** (spaceId, appName) の最新リンク(有効・無効問わず)を返す。無ければ null。 */ + export function getAppShareLink(db: Database.Database, spaceId: string, appName: string): { token: string; revokedAt: string | null } | null { + const row = db + .prepare( + `SELECT token, revoked_at FROM app_share_links + WHERE space_id = ? AND app_name = ? + ORDER BY created_at DESC LIMIT 1`, + ) + .get(spaceId, appName) as { token: string; revoked_at: string | null } | undefined; + if (!row) return null; + return { token: row.token, revokedAt: row.revoked_at ?? null }; + } + + + /** (spaceId, appName) の未失効リンクを全て失効させる。行が無ければ no-op。 */ + export function revokeAppShareLink(db: Database.Database, spaceId: string, appName: string): void { + db + .prepare( + `UPDATE app_share_links SET revoked_at = datetime('now') + WHERE space_id = ? AND app_name = ? AND revoked_at IS NULL`, + ) + .run(spaceId, appName); + } + + + /** トークンを (spaceId, appName) に解決する。失効済み・不正トークンは null。 */ + export function resolveAppShareToken(db: Database.Database, token: string): { spaceId: string; appName: string } | null { + const row = db + .prepare( + `SELECT space_id, app_name FROM app_share_links + WHERE token = ? AND revoked_at IS NULL`, + ) + .get(token) as { space_id: string; app_name: string } | undefined; + if (!row) return null; + return { spaceId: row.space_id, appName: row.app_name }; + } diff --git a/src/db/repositories/audit.ts b/src/db/repositories/audit.ts new file mode 100644 index 0000000..f03a94a --- /dev/null +++ b/src/db/repositories/audit.ts @@ -0,0 +1,9 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// audit repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; + + export async function addAuditLog(db: Database.Database, jobId: string | null, action: string, actor: string, detail: object): Promise { + db + .prepare('INSERT INTO audit_log (job_id, action, actor, detail) VALUES (?, ?, ?, ?)') + .run(jobId, action, actor, JSON.stringify(detail)); + } diff --git a/src/db/repositories/calendar.ts b/src/db/repositories/calendar.ts new file mode 100644 index 0000000..78d3f02 --- /dev/null +++ b/src/db/repositories/calendar.ts @@ -0,0 +1,430 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// calendar repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { buildVisibilityWhere } from '../../bridge/visibility.js'; +import { spaceColor } from '../../spaces/space-color.js'; +import * as spacesRepo from './spaces.js'; + +/** + * Allowed widget kinds. 'markdown' is the original Side Info Panel widget + * (PR #308). 'node-status' was added in Phase B (2026-05) and ignores + * markdown_content — it renders BackendStatusRegistry data live. The union + * intentionally lives here so the API, tools and UI all share a single + * source of truth. + */ +/** + * スペース・カレンダーの予定エントリ(passive。実行はしない)。 + * spec: docs/superpowers/specs/2026-06-19-space-calendar-design.md §データモデル + */ +export interface CalendarEvent { + id: number; + spaceId: string; + ownerId: string | null; + date: string; // 開始日 YYYY-MM-DD(ローカル日付) + endDate: string | null; // 終了日 YYYY-MM-DD。null = date と同じ(単日) + time: string | null; // 開始 HH:MM。null = 終日 + endTime: string | null; // 終了 HH:MM。null = 終了時刻なし(time が null なら常に null) + title: string; + description: string | null; + createdBy: 'user' | 'agent'; + sourceTaskId: number | null; + createdAt: string; + updatedAt: string; +} + + +export interface CalendarEventRow { + id: number; + space_id: string; + owner_id: string | null; + date: string; + end_date: string | null; + time: string | null; + end_time: string | null; + title: string; + description: string | null; + created_by: string; + source_task_id: number | null; + created_at: string; + updated_at: string; +} + + +/** + * [start, end](両端含む)のローカル暦日を 'YYYY-MM-DD' 配列で返す。 + * end < start は空配列。複数日イベントを各日に展開する集計で使う。 + */ +export function enumerateLocalDays(start: string, end: string): string[] { + if (end < start) return []; + const out: string[] = []; + const [ys, ms, ds] = start.split('-').map(Number); + const [ye, me, de] = end.split('-').map(Number); + let cur = Date.UTC(ys, ms - 1, ds); + const last = Date.UTC(ye, me - 1, de); + // 安全弁: 異常な範囲で無限ループしないよう上限を設ける(最大 ~2 年)。 + for (let i = 0; cur <= last && i < 800; i++) { + const d = new Date(cur); + out.push(`${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}-${String(d.getUTCDate()).padStart(2, '0')}`); + cur += 86_400_000; + } + return out; +} + + +export function rowToCalendarEvent(row: CalendarEventRow): CalendarEvent { + return { + id: row.id, + spaceId: row.space_id, + ownerId: row.owner_id ?? null, + date: row.date, + endDate: row.end_date ?? null, + time: row.time ?? null, + endTime: row.end_time ?? null, + title: row.title, + description: row.description ?? null, + createdBy: row.created_by === 'agent' ? 'agent' : 'user', + sourceTaskId: row.source_task_id ?? null, + createdAt: row.created_at, + updatedAt: row.updated_at, + }; +} + + +/** カレンダー月ビューの1日分の集計カウント。 */ +export interface CalendarDayCounts { + taskCount: number; + eventCount: number; +} + + +/** カレンダー日詳細のタスク行(軽量・最新 job 状態付き)。 */ +export interface CalendarDayTask { + id: number; + title: string; + state: string; + status: string | null; // 最新 job の status(無ければ null) + createdAt: string; +} + + +/** 横断カレンダーの凡例 1 行(スペースとその表示色)。 */ +export interface CrossCalendarSpace { + id: string; + name: string; + color: string; // brand_color、無ければ spaceColor(id) で決定論的に導出 +} + + +/** 横断カレンダー月ビュー。`days[date][spaceId]` = そのスペースのその日の集計。 */ +export interface CrossSpaceCalendarMonth { + days: Record>; + events: CalendarEvent[]; // 各イベントは spaceId を持つ(CalendarEvent.spaceId) + spaces: CrossCalendarSpace[]; +} + + + // ── スペース・カレンダー(予定)───────────────────────────────────── + // + // CRUD はスペース id でスコープ。可視性判定は呼び出し側(space-api が + // getSpace({viewer}) で先にゲートする)に委ねる。日付集計はローカル TZ + // オフセット(分)を SQLite の date(...,'±N minutes') 修飾子で適用する + // (Usage ダッシュボードの localDayOf と同じ「UTC instant をオフセット分 + // ずらして暦日に落とす」方式の SQL 版)。 + + /** ローカル TZ オフセット(分)を SQLite の date() 修飾子文字列に変換する。 */ + export function tzModifier(tzOffsetMin: number): string { + const n = Math.trunc(tzOffsetMin); + return `${n >= 0 ? '+' : '-'}${Math.abs(n)} minutes`; + } + + + export async function createCalendarEvent(db: Database.Database, params: { + spaceId: string; + ownerId?: string | null; + date: string; + endDate?: string | null; + time?: string | null; + endTime?: string | null; + title: string; + description?: string | null; + createdBy?: 'user' | 'agent'; + sourceTaskId?: number | null; + }): Promise { + // 終了日が開始日と同じ(または無効)なら単日として NULL に正規化する。 + const endDate = params.endDate && params.endDate > params.date ? params.endDate : null; + const time = params.time ?? null; + // 終了時刻は開始時刻があるときだけ意味を持つ。開始が終日なら end_time は捨てる。 + const endTime = time ? (params.endTime ?? null) : null; + const result = db + .prepare( + `INSERT INTO calendar_events (space_id, owner_id, date, end_date, time, end_time, title, description, created_by, source_task_id) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` + ) + .run( + params.spaceId, + params.ownerId ?? null, + params.date, + endDate, + time, + endTime, + params.title, + params.description ?? null, + params.createdBy ?? 'user', + params.sourceTaskId ?? null, + ); + const row = db + .prepare(`SELECT * FROM calendar_events WHERE id = ?`) + .get(Number(result.lastInsertRowid)) as CalendarEventRow; + return rowToCalendarEvent(row); + } + + + export async function listCalendarEvents(db: Database.Database, spaceId: string, range?: { from?: string; to?: string }): Promise { + const conditions = ['space_id = ?']; + const args: unknown[] = [spaceId]; + // 複数日対応: イベント [date, COALESCE(end_date,date)] が [from, to] と + // 重なるものを返す(単日 end_date=NULL は date 自身)。 + if (range?.from) { + conditions.push('COALESCE(end_date, date) >= ?'); + args.push(range.from); + } + if (range?.to) { + conditions.push('date <= ?'); + args.push(range.to); + } + const rows = db + .prepare( + `SELECT * FROM calendar_events WHERE ${conditions.join(' AND ')} + ORDER BY date ASC, (time IS NULL) DESC, time ASC, id ASC` + ) + .all(...args) as CalendarEventRow[]; + return rows.map(rowToCalendarEvent); + } + + + export async function getCalendarEvent(db: Database.Database, eventId: number): Promise { + const row = db + .prepare(`SELECT * FROM calendar_events WHERE id = ?`) + .get(eventId) as CalendarEventRow | undefined; + return row ? rowToCalendarEvent(row) : null; + } + + + export async function updateCalendarEvent(db: Database.Database, eventId: number, fields: { date?: string; endDate?: string | null; time?: string | null; endTime?: string | null; title?: string; description?: string | null }): Promise { + const sets: string[] = []; + const args: unknown[] = []; + if (fields.date !== undefined) { + sets.push('date = ?'); + args.push(fields.date); + } + if (fields.endDate !== undefined) { + sets.push('end_date = ?'); + args.push(fields.endDate); + } + // time / end_time は連動。開始を終日(null)にしたら end_time も必ず null に落とす。 + if (fields.time !== undefined || fields.endTime !== undefined) { + const existing = await getCalendarEvent(db, eventId); + const newTime = fields.time !== undefined ? fields.time : (existing?.time ?? null); + let newEndTime = fields.endTime !== undefined ? fields.endTime : (existing?.endTime ?? null); + if (newTime == null) newEndTime = null; + if (fields.time !== undefined) { + sets.push('time = ?'); + args.push(newTime); + } + if (newEndTime !== (existing?.endTime ?? null)) { + sets.push('end_time = ?'); + args.push(newEndTime); + } + } + if (fields.title !== undefined) { + sets.push('title = ?'); + args.push(fields.title); + } + if (fields.description !== undefined) { + sets.push('description = ?'); + args.push(fields.description); + } + if (sets.length === 0) return getCalendarEvent(db, eventId); + sets.push(`updated_at = datetime('now')`); + args.push(eventId); + db.prepare(`UPDATE calendar_events SET ${sets.join(', ')} WHERE id = ?`).run(...args); + return getCalendarEvent(db, eventId); + } + + + export async function deleteCalendarEvent(db: Database.Database, eventId: number): Promise { + db.prepare(`DELETE FROM calendar_events WHERE id = ?`).run(eventId); + } + + + /** + * 月ビューの日別カウント。`days` は date → {taskCount, eventCount}。 + * タスクは local_tasks を space_id で絞り、created_at(UTC)をローカル TZ で + * 暦日にバケツ化した日付が [monthStart, monthEnd] に入るものを数える。 + * イベントは date 列がそのまま暦日(ローカル)なので素直に範囲で絞る。 + * ファイル数は重いので含めない(日詳細でのみ算出)。 + */ + /** + * SQL scope for "tasks belonging to this space's calendar". Normally just + * `space_id = ?`. A PERSONAL space also owns its owner's space-less tasks: + * personal-workspace tasks are stored with `space_id = NULL` (by design), so + * include `(space_id IS NULL AND owner_id = ?)` when a personalOwnerId is + * given. Mirrors worker.ts resolveToolSpaceId (NULL+owner → personal space). + */ + export function calendarTaskScope(spaceId: string, personalOwnerId?: string | null, alias = ''): { clause: string; params: unknown[] } { + const col = alias ? `${alias}.` : ''; + if (personalOwnerId) { + return { + clause: `(${col}space_id = ? OR (${col}space_id IS NULL AND ${col}owner_id = ?))`, + params: [spaceId, personalOwnerId], + }; + } + return { clause: `${col}space_id = ?`, params: [spaceId] }; + } + + + export async function getSpaceCalendarMonth(db: Database.Database, spaceId: string, opts: { monthStart: string; monthEnd: string; tzOffsetMin: number; personalOwnerId?: string | null }): Promise<{ days: Record; events: CalendarEvent[] }> { + const mod = tzModifier(opts.tzOffsetMin); + const days: Record = {}; + const ensure = (d: string): CalendarDayCounts => { + if (!days[d]) days[d] = { taskCount: 0, eventCount: 0 }; + return days[d]!; + }; + + const scope = calendarTaskScope(spaceId, opts.personalOwnerId); + const taskRows = db + .prepare( + `SELECT date(created_at, ?) AS local_day, COUNT(*) AS c + FROM local_tasks + WHERE ${scope.clause} AND date(created_at, ?) BETWEEN ? AND ? + GROUP BY local_day` + ) + .all(mod, ...scope.params, mod, opts.monthStart, opts.monthEnd) as Array<{ local_day: string; c: number }>; + for (const r of taskRows) ensure(r.local_day).taskCount = r.c; + + const events = await listCalendarEvents(db, spaceId, { from: opts.monthStart, to: opts.monthEnd }); + // 複数日イベントは、またがる各日(当月内にクリップ)に 1 件ずつ計上する。 + for (const ev of events) { + const start = ev.date < opts.monthStart ? opts.monthStart : ev.date; + const end = (ev.endDate ?? ev.date) > opts.monthEnd ? opts.monthEnd : (ev.endDate ?? ev.date); + for (const d of enumerateLocalDays(start, end)) ensure(d).eventCount += 1; + } + return { days, events }; + } + + + /** + * 日詳細。tasks = その日(ローカル暦日)に作成された local_tasks(最新 job + * 状態付き)、events = その date の calendar_events。ファイル列挙は API 層 + * (fs スキャン)で行う。 + */ + export async function getSpaceCalendarDay(db: Database.Database, spaceId: string, date: string, tzOffsetMin: number, personalOwnerId?: string | null): Promise<{ tasks: CalendarDayTask[]; events: CalendarEvent[] }> { + const mod = tzModifier(tzOffsetMin); + const scope = calendarTaskScope(spaceId, personalOwnerId, 'lt'); + const taskRows = db + .prepare( + `SELECT lt.id AS id, lt.title AS title, lt.state AS state, lt.created_at AS created_at, + (SELECT j.status FROM jobs j + WHERE j.repo = 'local/task-' || lt.id + ORDER BY j.created_at DESC LIMIT 1) AS status + FROM local_tasks lt + WHERE ${scope.clause} AND date(lt.created_at, ?) = ? + ORDER BY lt.created_at ASC, lt.id ASC` + ) + .all(...scope.params, mod, date) as Array<{ + id: number; title: string; state: string; created_at: string; status: string | null; + }>; + const tasks: CalendarDayTask[] = taskRows.map((r) => ({ + id: r.id, + title: r.title, + state: r.state, + status: r.status ?? null, + createdAt: r.created_at, + })); + const events = await listCalendarEvents(db, spaceId, { from: date, to: date }); + return { tasks, events }; + } + + + /** + * 横断(全スペース)月ビュー。viewer が見られるスペースだけを対象に、 + * 日 × スペースの {taskCount, eventCount} を集計する。可視性は spaces + * 一覧と同じ `buildVisibilityWhere`(listSpaces 経由)で絞るので、 + * 見えないスペースのカウントもイベントも一切返さない。 + * + * クエリ本数を抑えるため、対象 space_id を IN (...) でまとめて 2 本に集約する: + * 1) local_tasks を space_id + ローカル暦日で GROUP BY + * 2) calendar_events を space_id + date で GROUP BY + * さらに当月のイベント本体を 1 本でまとめて取得する(各 CalendarEvent は + * spaceId を持つので UI 側でスペース別に振り分けられる)。 + */ + export async function getCrossSpaceCalendarMonth(db: Database.Database, viewer: Express.User, opts: { monthStart: string; monthEnd: string; tzOffsetMin: number }): Promise { + // 可視スペースの列挙は spaces 一覧とまったく同じ可視性判定を流用する。 + const visibleSpaces = await spacesRepo.listSpaces(db, { viewer }); + const spaces: CrossCalendarSpace[] = visibleSpaces.map((s) => ({ + id: s.id, + name: s.title, + color: s.brandColor ?? spaceColor(s.id), + })); + + const days: Record> = {}; + if (spaces.length === 0) { + return { days, events: [], spaces }; + } + + const ids = spaces.map((s) => s.id); + const placeholders = ids.map(() => '?').join(','); + const mod = tzModifier(opts.tzOffsetMin); + const ensure = (date: string, spaceId: string): CalendarDayCounts => { + const byDay = (days[date] ??= {}); + return (byDay[spaceId] ??= { taskCount: 0, eventCount: 0 }); + }; + + const taskRows = db + .prepare( + `SELECT space_id AS sid, date(created_at, ?) AS local_day, COUNT(*) AS c + FROM local_tasks + WHERE space_id IN (${placeholders}) AND date(created_at, ?) BETWEEN ? AND ? + GROUP BY sid, local_day` + ) + .all(mod, ...ids, mod, opts.monthStart, opts.monthEnd) as Array<{ + sid: string; local_day: string; c: number; + }>; + for (const r of taskRows) ensure(r.local_day, r.sid).taskCount = r.c; + + // 個人スペースのタスクは space_id が NULL(owner_id で紐付く)ため、上の + // `space_id IN (...)` では拾えず全体カレンダーに反映されなかった。per-space カレンダーの + // calendarTaskScope と同じ owner フォールバックで、`space_id IS NULL AND owner_id=個人スペース + // 所有者` のタスクを個人スペース列に計上する。 + for (const ps of visibleSpaces) { + if (ps.kind !== 'personal' || !ps.ownerId) continue; + const personalRows = db + .prepare( + `SELECT date(created_at, ?) AS local_day, COUNT(*) AS c + FROM local_tasks + WHERE space_id IS NULL AND owner_id = ? AND date(created_at, ?) BETWEEN ? AND ? + GROUP BY local_day` + ) + .all(mod, ps.ownerId, mod, opts.monthStart, opts.monthEnd) as Array<{ local_day: string; c: number }>; + for (const r of personalRows) ensure(r.local_day, ps.id).taskCount += r.c; + } + + // 複数日対応: 当月に重なるイベントを 1 本でまとめて取得し、各日(当月内に + // クリップ)に 1 件ずつ計上する。終了日が当月外の単日 end_date=NULL は date 自身。 + const eventRows = db + .prepare( + `SELECT * FROM calendar_events + WHERE space_id IN (${placeholders}) + AND COALESCE(end_date, date) >= ? AND date <= ? + ORDER BY date ASC, (time IS NULL) DESC, time ASC, id ASC` + ) + .all(...ids, opts.monthStart, opts.monthEnd) as CalendarEventRow[]; + const events = eventRows.map(rowToCalendarEvent); + for (const ev of events) { + const start = ev.date < opts.monthStart ? opts.monthStart : ev.date; + const end = (ev.endDate ?? ev.date) > opts.monthEnd ? opts.monthEnd : (ev.endDate ?? ev.date); + for (const d of enumerateLocalDays(start, end)) ensure(d, ev.spaceId).eventCount += 1; + } + + return { days, events, spaces }; + } diff --git a/src/db/repositories/gateway.ts b/src/db/repositories/gateway.ts new file mode 100644 index 0000000..90174c4 --- /dev/null +++ b/src/db/repositories/gateway.ts @@ -0,0 +1,487 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// gateway repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { randomUUID } from 'crypto'; + +// ── AAO Gateway Phase 2a: virtual keys ───────────────────────────────── +// Stored shape returned by all gateway-key repository methods. The raw +// bearer is NEVER persisted or returned — only `keyHash` (sha256) lives +// in the DB, and `keyPrefix` is the human-readable head used in admin UI +// lists (`sk-aao-XXXXXX`). The raw key surfaces exactly once, from the +// admin API on issue / rotate; see src/bridge/admin-gateway-api.ts. + +export type GatewayVirtualKeySource = 'admin' | 'config-import'; + + +export interface GatewayVirtualKey { + id: string; + keyHash: string; + keyPrefix: string; + team: string; + /** Null = no per-key allowlist (any backend.model is accepted). */ + allowedModels: string[] | null; + source: GatewayVirtualKeySource; + createdAt: string; + createdBy: string | null; + /** ISO timestamp if revoked; null while active. */ + revokedAt: string | null; + revokedBy: string | null; + lastUsedAt: string | null; + /** + * Phase 2b: monthly tokens budget. NULL = unlimited. When set, the + * gateway rejects requests with 402 once the current UTC month + * `tokens_in + tokens_out` reaches this number (post-hoc enforcement — + * the offending request that pushes the counter over the limit still + * completes; the next one is rejected). + */ + tokensBudget: number | null; + /** + * Phase 2b: per-key requests-per-minute cap. NULL = unlimited. Enforced + * as a sliding 60-second window in-process; multi-instance setups are + * intentionally NOT synchronized (Phase 3 if needed). + */ + rateLimitRpm: number | null; +} + + +export interface GatewayVirtualKeyRow { + id: string; + key_hash: string; + key_prefix: string; + team: string; + allowed_models: string | null; + source: string; + created_at: string; + created_by: string | null; + revoked_at: string | null; + revoked_by: string | null; + last_used_at: string | null; + tokens_budget: number | null; + rate_limit_rpm: number | null; +} + + +// Phase 2b: monthly usage counter per virtual key. +export interface GatewayKeyUsage { + keyId: string; + /** UTC month bucket as 'YYYY-MM' — see src/gateway/period.ts. */ + periodStart: string; + tokensIn: number; + tokensOut: number; + requests: number; + lastUpdatedAt: string; +} + + +export interface GatewayKeyUsageRow { + key_id: string; + period_start: string; + tokens_in: number; + tokens_out: number; + requests: number; + last_updated_at: string; +} + + +export function rowToGatewayKeyUsage(row: GatewayKeyUsageRow): GatewayKeyUsage { + return { + keyId: row.key_id, + periodStart: row.period_start, + tokensIn: row.tokens_in, + tokensOut: row.tokens_out, + requests: row.requests, + lastUpdatedAt: row.last_updated_at, + }; +} + + +/** + * Coerce an optional limit (tokens_budget / rate_limit_rpm) to either + * a positive integer or null. Anything else (undefined, null, 0, + * negative, NaN, non-number) collapses to null = "no limit" so callers + * can't accidentally persist a value that would silently block all + * traffic. + */ +export function normalizeOptionalPositiveInt(v: unknown): number | null { + if (v === undefined || v === null) return null; + if (typeof v !== 'number' || !Number.isFinite(v) || v <= 0) return null; + return Math.floor(v); +} + + +export function rowToGatewayVirtualKey(row: GatewayVirtualKeyRow): GatewayVirtualKey { + let allowedModels: string[] | null = null; + if (row.allowed_models !== null && row.allowed_models !== '') { + try { + const parsed: unknown = JSON.parse(row.allowed_models); + if (Array.isArray(parsed) && parsed.every(x => typeof x === 'string')) { + allowedModels = parsed; + } + } catch { + // Corrupt JSON: treat as "no allowlist" (safer than fail-open + // because routing has its own backend.model gate; this is just + // the per-key filter on top). + allowedModels = null; + } + } + const source: GatewayVirtualKeySource = + row.source === 'config-import' ? 'config-import' : 'admin'; + // tokens_budget / rate_limit_rpm may legitimately arrive as null (no + // limit). Coerce non-positive integers to null defensively because the + // gateway middleware treats null as "unlimited" — a corrupt `0` would + // otherwise silently block every request. + const tokensBudget = + typeof row.tokens_budget === 'number' && Number.isFinite(row.tokens_budget) && row.tokens_budget > 0 + ? Math.floor(row.tokens_budget) + : null; + const rateLimitRpm = + typeof row.rate_limit_rpm === 'number' && Number.isFinite(row.rate_limit_rpm) && row.rate_limit_rpm > 0 + ? Math.floor(row.rate_limit_rpm) + : null; + return { + id: row.id, + keyHash: row.key_hash, + keyPrefix: row.key_prefix, + team: row.team, + allowedModels, + source, + createdAt: row.created_at, + createdBy: row.created_by, + revokedAt: row.revoked_at, + revokedBy: row.revoked_by, + lastUsedAt: row.last_used_at, + tokensBudget, + rateLimitRpm, + }; +} + + + // ── AAO Gateway Phase 2a: virtual keys ─────────────────────────────── + // + // The gateway auth middleware reads `findGatewayVirtualKeyByHash` on + // every request, so it MUST stay an indexed point lookup. The partial + // index `idx_gateway_keys_hash_active` covers that path. Admin-side + // methods (list/get/revoke/rotate/delete) are not hot. + + /** + * Insert a new virtual key row. Throws on UNIQUE(key_hash) violation — + * callers must hash the raw key first (via src/gateway/key-format.ts) + * and pass the hash here. The raw key is never accepted by the + * Repository on purpose: there is no path that could log it. + * + * `allowedModels` is JSON-encoded when present and stored as NULL when + * omitted — distinct from `[]` which means "lock to zero models". + */ + export function createGatewayVirtualKey(db: Database.Database, params: { + id?: string; + keyHash: string; + keyPrefix: string; + team: string; + allowedModels?: string[] | null; + source?: GatewayVirtualKeySource; + createdBy?: string | null; + createdAt?: string; + /** Phase 2b: optional monthly tokens budget. null/undefined = unlimited. */ + tokensBudget?: number | null; + /** Phase 2b: optional requests-per-minute cap. null/undefined = unlimited. */ + rateLimitRpm?: number | null; + }): GatewayVirtualKey { + const id = params.id ?? randomUUID(); + const allowedJson = + params.allowedModels === null || params.allowedModels === undefined + ? null + : JSON.stringify(params.allowedModels); + const source: GatewayVirtualKeySource = params.source ?? 'admin'; + const createdAt = params.createdAt ?? new Date().toISOString(); + const tokensBudget = normalizeOptionalPositiveInt(params.tokensBudget); + const rateLimitRpm = normalizeOptionalPositiveInt(params.rateLimitRpm); + db + .prepare( + `INSERT INTO gateway_virtual_keys + (id, key_hash, key_prefix, team, allowed_models, source, created_at, created_by, tokens_budget, rate_limit_rpm) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + ) + .run( + id, + params.keyHash, + params.keyPrefix, + params.team, + allowedJson, + source, + createdAt, + params.createdBy ?? null, + tokensBudget, + rateLimitRpm, + ); + const row = db + .prepare(`SELECT * FROM gateway_virtual_keys WHERE id = ?`) + .get(id) as GatewayVirtualKeyRow; + return rowToGatewayVirtualKey(row); + } + + + /** + * Phase 2b: partial update of a virtual key's policy fields. The + * bearer hash, team, source, and creation metadata are immutable here + * (use rotate to change the bearer). Each field is opt-in — undefined + * means "leave alone"; explicit null clears the limit (= unlimited). + * + * Returns the refreshed row. Throws when the id doesn't exist (caller + * is expected to 404 before calling). + */ + export function updateGatewayVirtualKey(db: Database.Database, id: string, patch: { + /** + * Phase 3a follow-up: team is now patchable so the config-migration + * importer can propagate a YAML-side team rename to the DB. Admin + * PATCH never sends this field (the team is intentionally immutable + * via the public API to avoid an admin accidentally rewriting the + * owner of a key); the only caller is importConfigKeysToDb. + */ + team?: string; + tokensBudget?: number | null; + rateLimitRpm?: number | null; + allowedModels?: string[] | null; + }): GatewayVirtualKey { + const sets: string[] = []; + const args: unknown[] = []; + if (Object.prototype.hasOwnProperty.call(patch, 'team')) { + sets.push('team = ?'); + args.push(patch.team); + } + if (Object.prototype.hasOwnProperty.call(patch, 'tokensBudget')) { + sets.push('tokens_budget = ?'); + args.push(normalizeOptionalPositiveInt(patch.tokensBudget)); + } + if (Object.prototype.hasOwnProperty.call(patch, 'rateLimitRpm')) { + sets.push('rate_limit_rpm = ?'); + args.push(normalizeOptionalPositiveInt(patch.rateLimitRpm)); + } + if (Object.prototype.hasOwnProperty.call(patch, 'allowedModels')) { + sets.push('allowed_models = ?'); + args.push( + patch.allowedModels === null || patch.allowedModels === undefined + ? null + : JSON.stringify(patch.allowedModels), + ); + } + if (sets.length > 0) { + args.push(id); + db + .prepare(`UPDATE gateway_virtual_keys SET ${sets.join(', ')} WHERE id = ?`) + .run(...args); + } + const refreshed = findGatewayVirtualKeyById(db, id); + if (!refreshed) { + throw new Error(`updateGatewayVirtualKey: id not found (${id})`); + } + return refreshed; + } + + + /** + * Auth hot path: look up an active (non-revoked) key by SHA-256 hash. + * The partial index covers this query so the planner uses it directly. + * Returns null on miss; never throws. + */ + export function findGatewayVirtualKeyByHash(db: Database.Database, keyHash: string): GatewayVirtualKey | null { + const row = db + .prepare( + `SELECT * FROM gateway_virtual_keys WHERE key_hash = ? AND revoked_at IS NULL`, + ) + .get(keyHash) as GatewayVirtualKeyRow | undefined; + return row ? rowToGatewayVirtualKey(row) : null; + } + + + /** + * Admin lookup by row id. Includes revoked keys (the admin list/detail + * view shows them so an admin can audit a recent revoke). + */ + export function findGatewayVirtualKeyById(db: Database.Database, id: string): GatewayVirtualKey | null { + const row = db + .prepare(`SELECT * FROM gateway_virtual_keys WHERE id = ?`) + .get(id) as GatewayVirtualKeyRow | undefined; + return row ? rowToGatewayVirtualKey(row) : null; + } + + + /** + * Admin list. `activeOnly` filters out revoked rows; `team` narrows by + * team string (exact match). Ordering is `created_at DESC, id DESC` so + * the freshest issuance is first regardless of system clock skew. + */ + export function listGatewayVirtualKeys(db: Database.Database, opts?: { team?: string; activeOnly?: boolean }): GatewayVirtualKey[] { + const where: string[] = []; + const args: unknown[] = []; + if (opts?.team !== undefined) { + where.push('team = ?'); + args.push(opts.team); + } + if (opts?.activeOnly) { + where.push('revoked_at IS NULL'); + } + const sql = + `SELECT * FROM gateway_virtual_keys` + + (where.length > 0 ? ` WHERE ${where.join(' AND ')}` : '') + + ` ORDER BY created_at DESC, id DESC`; + const rows = db.prepare(sql).all(...args) as GatewayVirtualKeyRow[]; + return rows.map(rowToGatewayVirtualKey); + } + + + /** + * Mark a key as revoked. Returns true if the row was active (so the + * caller can return a clean 200) and false if it was already revoked + * or doesn't exist (so the caller can return 404 / 409). Idempotent + * second calls return false. + */ + export function revokeGatewayVirtualKey(db: Database.Database, id: string, revokedBy: string, at?: string): boolean { + const ts = at ?? new Date().toISOString(); + const info = db + .prepare( + `UPDATE gateway_virtual_keys + SET revoked_at = ?, revoked_by = ? + WHERE id = ? AND revoked_at IS NULL`, + ) + .run(ts, revokedBy, id); + return info.changes > 0; + } + + + /** + * Hard delete. The admin API guards `source='config-import'` and + * returns 400 before calling this — but the Repository itself doesn't + * enforce that (tests need to be able to clean up). Returns true if a + * row was deleted. + */ + /** + * Hard-delete a virtual key row. + * + * Defense-in-depth: refuses to delete rows with `source='config-import'` + * by throwing. The admin REST API also rejects this case (returning a + * 400 with a human-readable message), but a future internal caller + * could easily forget — and a hard delete of a config-import row + * would simply be replayed on the next gateway boot when + * importConfigKeysToDb re-imports the entry from config.yaml. That + * recreates the row with a different id, which silently breaks any + * audit history that referenced the previous id and is generally + * confusing operator behavior. Force callers to use `revoke` (soft + * delete) or to remove the entry from config.yaml first. + * + * Returns true when a row was deleted, false when the id didn't + * exist. Throws when the row exists but is config-import. + */ + export function deleteGatewayVirtualKey(db: Database.Database, id: string): boolean { + const row = findGatewayVirtualKeyById(db, id); + if (!row) return false; + if (row.source === 'config-import') { + throw new Error( + `cannot delete config-import virtual key (id=${id}); ` + + "remove the entry from config.yaml's gateway.virtual_keys instead, " + + 'or use revoke for a soft delete', + ); + } + const info = db + .prepare(`DELETE FROM gateway_virtual_keys WHERE id = ?`) + .run(id); + return info.changes > 0; + } + + + /** + * Bump `last_used_at` for an active key. Called from the gateway auth + * middleware on successful match. Per-request volume can be high, so + * callers typically dedup with a 30-second in-memory bucket (see + * src/gateway/auth.ts) before touching the DB. Best-effort: failures + * are swallowed by the caller so a temporary write-lock contention + * never blocks auth. + */ + export function touchGatewayVirtualKeyLastUsed(db: Database.Database, id: string, at?: string): void { + const ts = at ?? new Date().toISOString(); + db + .prepare(`UPDATE gateway_virtual_keys SET last_used_at = ? WHERE id = ?`) + .run(ts, id); + } + + + // ── AAO Gateway Phase 2b: usage tracking ───────────────────────────── + // + // Read path is on the budget enforcement hot loop, so it stays a + // single point lookup over the composite PRIMARY KEY. Write path is + // an UPSERT (`ON CONFLICT … DO UPDATE`) so the gateway can fire-and- + // forget after every chat completion without a pre-read. + + /** + * Point-lookup over the (key_id, period_start) PRIMARY KEY. Returns + * null when there's no row yet (= "no usage in this period"), which + * the caller treats as zero counters. + */ + export function getGatewayKeyUsage(db: Database.Database, keyId: string, periodStart: string): GatewayKeyUsage | null { + const row = db + .prepare( + `SELECT * FROM gateway_key_usage WHERE key_id = ? AND period_start = ?`, + ) + .get(keyId, periodStart) as GatewayKeyUsageRow | undefined; + return row ? rowToGatewayKeyUsage(row) : null; + } + + + /** + * UPSERT: bump the per-(key, period) counters by the supplied deltas. + * All three deltas are clamped at zero so a buggy caller can never + * decrement a counter. `last_updated_at` always reflects the call + * time (or the explicit `at` override) so a downstream sweeper can + * tell when activity stopped. + * + * Called from two places on the gateway hot path: + * 1. stream-proxy's finally block (token deltas from upstream usage) + * 2. rate-limiter's 30-second batch flush (request count only) + * + * The second caller passes `tokensIn=0 tokensOut=0` so the UPSERT + * still creates a row even when no token usage was extracted. + */ + export function incrementGatewayKeyUsage(db: Database.Database, params: { + keyId: string; + period: string; + tokensIn?: number; + tokensOut?: number; + requests?: number; + at?: string; + }): void { + const tIn = Math.max(0, Math.floor(params.tokensIn ?? 0)); + const tOut = Math.max(0, Math.floor(params.tokensOut ?? 0)); + const reqs = Math.max(0, Math.floor(params.requests ?? 0)); + const ts = params.at ?? new Date().toISOString(); + db + .prepare( + `INSERT INTO gateway_key_usage + (key_id, period_start, tokens_in, tokens_out, requests, last_updated_at) + VALUES (?, ?, ?, ?, ?, ?) + ON CONFLICT (key_id, period_start) DO UPDATE SET + tokens_in = tokens_in + excluded.tokens_in, + tokens_out = tokens_out + excluded.tokens_out, + requests = requests + excluded.requests, + last_updated_at = excluded.last_updated_at`, + ) + .run(params.keyId, params.period, tIn, tOut, reqs, ts); + } + + + /** + * Admin view: history of usage rows for a key, freshest period first. + * Default limit 12 covers a full year of monthly buckets — enough for + * the in-UI bar chart and for `GET /:id/usage` to embed history without + * a follow-up call. + */ + export function listGatewayKeyUsagesByKey(db: Database.Database, keyId: string, opts?: { limit?: number }): GatewayKeyUsage[] { + const limit = Math.max(1, Math.min(120, Math.floor(opts?.limit ?? 12))); + const rows = db + .prepare( + `SELECT * FROM gateway_key_usage + WHERE key_id = ? + ORDER BY period_start DESC + LIMIT ?`, + ) + .all(keyId, limit) as GatewayKeyUsageRow[]; + return rows.map(rowToGatewayKeyUsage); + } diff --git a/src/db/repositories/jobs.ts b/src/db/repositories/jobs.ts new file mode 100644 index 0000000..5790b21 --- /dev/null +++ b/src/db/repositories/jobs.ts @@ -0,0 +1,886 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// jobs repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { randomUUID } from 'crypto'; +import { logger } from '../../logger.js'; +import { buildVisibilityWhere } from '../../bridge/visibility.js'; +import { utc, localTaskRepoName } from './shared.js'; + +export type JobStatus = + | 'queued' + | 'dispatching' + | 'running' + | 'succeeded' + | 'failed' + | 'retry' + | 'cancelled' + | 'waiting_human' + | 'waiting_subtasks'; + + +export type JobRole = 'auto' | 'fast' | 'quality' | 'reflection'; + +/** @deprecated Use JobRole instead */ +export type JobProfile = JobRole; + +/** @deprecated Removed — taskClass is no longer a separate concept */ +export type TaskClass = 'auto' | 'low_level' | 'high_level'; + + +export interface Job { + id: string; + repo: string; + issueNumber: number; + prNumber: number | null; + status: JobStatus; + pieceName: string; + currentMovement: string | null; + currentActivity: string | null; + instruction: string; + branchName: string | null; + worktreePath: string | null; + attempt: number; + maxAttempts: number; + nextRetryAt: string | null; + errorSummary: string | null; + abortReason: string | null; + resumeMovement: string | null; + waitReason: string | null; + askCount: number; + workerId: string | null; + /** + * Physical backend id (e.g. LiteLLM deployment name) that handled + * this job's LLM calls when running through a proxy worker. Set on + * the first LLM call and never overwritten — sticky-backend policy + * per design Open Question #3. NULL for direct workers and for jobs + * that haven't issued any proxied LLM call yet. + */ + lastBackendId: string | null; + parentJobId: string | null; + continuedFromJobId: string | null; + subtaskDepth: number; + requiredRole: JobRole; + /** @deprecated Use requiredRole */ + requiredProfile: JobRole; + ownerId: string | null; + visibility: 'private' | 'org' | 'public'; + visibilityScopeOrgId: string | null; + contextPromptTokens: number | null; + contextLimitTokens: number | null; + contextUpdatedAt: string | null; + browserSessionProfileId?: number | null; + taskKind: 'agent' | 'reflection'; + payload: string | null; + /** 所属スペース。親タスク/親ジョブから継承される(Task 5 で設定)。null は個人スペース。 */ + spaceId: string | null; + /** 実行ログ root(計画5)。NULL = 後方互換で workspacePath/logs に解決。サブタスクが親から継承する。 */ + runtimeDir: string | null; + createdAt: string; + updatedAt: string; +} + + +export interface CreateJobParams { + repo: string; + issueNumber: number; + instruction: string; + pieceName?: string; + maxAttempts?: number; + resumeMovement?: string | null; + askCount?: number; + role?: JobRole; + /** @deprecated Use role instead */ + profile?: JobRole; + parentJobId?: string | null; + continuedFromJobId?: string | null; + subtaskDepth?: number; + ownerId?: string | null; + visibility?: 'private' | 'org' | 'public'; + visibilityScopeOrgId?: string | null; + browserSessionProfileId?: number | null; + taskKind?: 'agent' | 'reflection'; + payload?: string; + /** 所属スペース(spec §5.7 親→子継承)。NULL は owner 個人スペースに解決。 */ + spaceId?: string | null; + /** 実行ログ root(計画5)。NULL = 後方互換で workspacePath/logs に解決。 */ + runtimeDir?: string | null; +} + + +export interface SubtaskInfo { + id: string; + issueNumber: number; + status: JobStatus; + instruction: string; + worktreePath: string | null; + createdAt: string; + updatedAt: string; + children?: SubtaskInfo[]; + childCount?: number; + childCompleted?: number; +} + + +export interface JobRow { + id: string; + repo: string; + issue_number: number; + pr_number: number | null; + status: string; + piece_name: string; + current_movement: string | null; + current_activity: string | null; + instruction: string; + branch_name: string | null; + worktree_path: string | null; + attempt: number; + max_attempts: number; + next_retry_at: string | null; + error_summary: string | null; + abort_reason: string | null; + resume_movement: string | null; + wait_reason: string | null; + ask_count: number; + worker_id: string | null; + last_backend_id: string | null; + parent_job_id: string | null; + continued_from_job_id: string | null; + subtask_depth: number; + required_profile: string; + task_class: string; + owner_id: string | null; + visibility: string | null; + visibility_scope_org_id: string | null; + context_prompt_tokens: number | null; + context_limit_tokens: number | null; + context_updated_at: string | null; + browser_session_profile_id: number | null; + task_kind: string; + payload: string | null; + space_id: string | null; + runtime_dir: string | null; + created_at: string; + updated_at: string; +} + + +export function isJobRole(value: string): value is JobRole { + return value === 'auto' || value === 'fast' || value === 'quality' || value === 'reflection'; +} + + +export function normalizeJobRole(value: string | undefined): JobRole { + return value && isJobRole(value) ? value : 'auto'; +} + + +export function deriveJobRole(instruction: string, explicitRole?: JobRole): JobRole { + if (explicitRole) return explicitRole; + const match = /ui_profile:\s*(auto|fast|quality)/i.exec(instruction); + return normalizeJobRole(match?.[1]?.toLowerCase()); +} + + +export function rowToJob(row: JobRow): Job { + return { + id: row.id, + repo: row.repo, + issueNumber: row.issue_number, + prNumber: row.pr_number, + status: row.status as JobStatus, + pieceName: row.piece_name, + currentMovement: row.current_movement, + currentActivity: row.current_activity, + instruction: row.instruction, + branchName: row.branch_name, + worktreePath: row.worktree_path, + attempt: row.attempt, + maxAttempts: row.max_attempts, + nextRetryAt: utc(row.next_retry_at), + errorSummary: row.error_summary, + abortReason: row.abort_reason ?? null, + resumeMovement: row.resume_movement, + waitReason: row.wait_reason ?? null, + askCount: row.ask_count, + workerId: row.worker_id, + lastBackendId: row.last_backend_id ?? null, + parentJobId: row.parent_job_id, + continuedFromJobId: row.continued_from_job_id ?? null, + subtaskDepth: row.subtask_depth ?? 0, + requiredRole: normalizeJobRole(row.required_profile), + requiredProfile: normalizeJobRole(row.required_profile), + ownerId: row.owner_id ?? null, + visibility: (row.visibility === 'org' || row.visibility === 'public' ? row.visibility : 'private'), + visibilityScopeOrgId: row.visibility_scope_org_id ?? null, + contextPromptTokens: row.context_prompt_tokens, + contextLimitTokens: row.context_limit_tokens, + contextUpdatedAt: row.context_updated_at ? utc(row.context_updated_at) : null, + browserSessionProfileId: row.browser_session_profile_id ?? null, + taskKind: row.task_kind === 'reflection' ? 'reflection' : 'agent', + payload: row.payload, + spaceId: row.space_id ?? null, + runtimeDir: row.runtime_dir ?? null, + createdAt: utc(row.created_at), + updatedAt: utc(row.updated_at), + }; +} + + // Non-terminal job states. A task with a job in any of these already has work + // pending/running, so a new user comment should be appended to it rather than + // spawning a second job. waiting_human is excluded on purpose: a comment there + // is the answer that resumes via a fresh job. + const PENDING_JOB_STATES = ['queued', 'dispatching', 'running', 'waiting_subtasks', 'retry'] as const; + + + export function insertJobSync(db: Database.Database, params: CreateJobParams): Job { + const id = randomUUID(); + const now = new Date().toISOString(); + const pieceName = params.pieceName ?? 'chat'; + const maxAttempts = params.maxAttempts ?? 3; + const resumeMovement = params.resumeMovement ?? null; + const askCount = params.askCount ?? 0; + const requiredRole = deriveJobRole(params.instruction, params.role ?? params.profile); + + db + .prepare( + `INSERT INTO jobs (id, repo, issue_number, status, piece_name, required_profile, task_class, instruction, attempt, max_attempts, resume_movement, ask_count, worker_id, parent_job_id, continued_from_job_id, subtask_depth, owner_id, visibility, visibility_scope_org_id, browser_session_profile_id, task_kind, payload, space_id, runtime_dir, created_at, updated_at) + VALUES (@id, @repo, @issueNumber, 'queued', @pieceName, @requiredRole, 'auto', @instruction, 1, @maxAttempts, @resumeMovement, @askCount, NULL, @parentJobId, @continuedFromJobId, @subtaskDepth, @ownerId, @visibility, @visibilityScopeOrgId, @browserSessionProfileId, @taskKind, @payload, @spaceId, @runtimeDir, @now, @now)` + ) + .run({ + id, + repo: params.repo, + issueNumber: params.issueNumber, + pieceName, + instruction: params.instruction, + maxAttempts, + resumeMovement, + askCount, + requiredRole, + parentJobId: params.parentJobId ?? null, + continuedFromJobId: params.continuedFromJobId ?? null, + subtaskDepth: params.subtaskDepth ?? 0, + ownerId: params.ownerId ?? null, + visibility: params.visibility ?? 'private', + visibilityScopeOrgId: params.visibilityScopeOrgId ?? null, + browserSessionProfileId: params.browserSessionProfileId ?? null, + taskKind: params.taskKind ?? 'agent', + payload: params.payload ?? null, + spaceId: params.spaceId ?? null, + runtimeDir: params.runtimeDir ?? null, + now, + }); + + const job = getJobSync(db, id); + if (!job) throw new Error(`createJob: failed to retrieve created job ${id}`); + return job; + } + + + export async function createJob(db: Database.Database, params: CreateJobParams): Promise { + return insertJobSync(db, params); + } + + + /** + * Atomically reuse-or-create. If a non-terminal job already exists for the + * issue, return it (created=false) without inserting; otherwise insert a new + * queued job (created=true). better-sqlite3 transactions run synchronously, so + * the check+insert can't interleave with a concurrent request — this closes + * the TOCTOU race where two near-simultaneous comments each spawned a job, + * leaving the newest (queued) duplicate as the task's latestJob while an older + * job actually ran (status stuck on "Inbox"). + */ + export function createJobIfNoPending(db: Database.Database, params: CreateJobParams, opts?: { blockOnToolRequestPause?: boolean }): { job: Job; created: boolean; blockedByToolRequest?: boolean } { + const states = PENDING_JOB_STATES; + const placeholders = states.map(() => '?').join(','); + const tx = db.transaction((): { job: Job; created: boolean; blockedByToolRequest?: boolean } => { + // Approval-pause guard: atomically refuse to create a normal resume job + // while a job is parked for tool OR package approval (both resolved via + // approve/deny, which re-queues the original). Done inside the same + // transaction as the insert so there is no check-then-act race with the + // engine parking the job. + if (opts?.blockOnToolRequestPause) { + const parked = db + .prepare( + `SELECT * FROM jobs WHERE repo = ? AND issue_number = ? + AND status = 'waiting_human' AND wait_reason IN ('tool_request', 'package_request') + ORDER BY created_at DESC, rowid DESC LIMIT 1`, + ) + .get(params.repo, params.issueNumber) as JobRow | undefined; + if (parked) return { job: rowToJob(parked), created: false, blockedByToolRequest: true }; + } + const existing = db + .prepare( + `SELECT * FROM jobs WHERE repo = ? AND issue_number = ? AND status IN (${placeholders}) + ORDER BY created_at DESC, rowid DESC LIMIT 1`, + ) + .get(params.repo, params.issueNumber, ...states) as JobRow | undefined; + if (existing) return { job: rowToJob(existing), created: false }; + return { job: insertJobSync(db, params), created: true }; + }); + return tx(); + } + + + export async function getJob(db: Database.Database, id: string, opts?: { viewer?: Express.User }): Promise { + const viewerClause = opts?.viewer + ? buildVisibilityWhere(opts.viewer, 'j', { spaceColumn: 'space_id' }) + : { clause: '1=1', params: [] as unknown[] }; + const row = db + .prepare(`SELECT j.* FROM jobs j WHERE j.id = ? AND ${viewerClause.clause}`) + .get(id, ...viewerClause.params) as JobRow | undefined; + return row ? rowToJob(row) : null; + } + + + /** サブジョブ一覧を SubtaskInfo[] に変換。waiting_subtasks の子は再帰的に children を取得する */ + export async function buildSubtaskInfos(db: Database.Database, subJobs: Job[], maxDepth: number = 3): Promise { + return Promise.all(subJobs.map(async (j): Promise => { + const info: SubtaskInfo = { + id: j.id, + issueNumber: j.issueNumber, + status: j.status, + instruction: j.instruction, + worktreePath: j.worktreePath, + createdAt: j.createdAt, + updatedAt: j.updatedAt, + }; + // 再帰: waiting_subtasks のサブタスクは孫タスク情報も取得 + if (j.status === 'waiting_subtasks' && maxDepth > 0) { + const grandChildren = await getSubJobs(db, j.id); + if (grandChildren.length > 0) { + info.children = await buildSubtaskInfos(db, grandChildren, maxDepth - 1); + info.childCount = grandChildren.length; + info.childCompleted = grandChildren.filter(g => + ['succeeded', 'failed', 'cancelled'].includes(g.status) + ).length; + } + } + return info; + })); + } + + + export function getJobSync(db: Database.Database, id: string): Job | null { + const row = db + .prepare('SELECT * FROM jobs WHERE id = ?') + .get(id) as JobRow | undefined; + return row ? rowToJob(row) : null; + } + + + /** + * ジョブの現在のステータスを同期的に取得する。 + * キャンセルチェックなど、非同期が使えない箇所で利用する。 + */ + export function getJobStatusSync(db: Database.Database, id: string): JobStatus | null { + const row = db + .prepare('SELECT status FROM jobs WHERE id = ?') + .get(id) as { status: string } | undefined; + return row ? (row.status as JobStatus) : null; + } + + + /** + * Returns true if `workerId` currently has at least one job with status='running'. + * Used by the Side Info Panel's worker status endpoint. Boolean-only on purpose: + * never expose the job id/title/owner to other users in the shared panel. + */ + export function isWorkerBusy(db: Database.Database, workerId: string): boolean { + const row = db + .prepare(`SELECT 1 AS hit FROM jobs WHERE worker_id = ? AND status = 'running' LIMIT 1`) + .get(workerId) as { hit: number } | undefined; + return !!row; + } + + + /** + * いま実行中 (status='running') のジョブを worker_id ごとにまとめ、占有ユーザーの + * 表示名と種別 (`task_kind`: 'agent' 通常 / 'reflection' 学習 等) を返す。 + * **admin 専用の監視用**(ワーカー / GPU パネルで「誰が・何で GPU を占有しているか」を + * 出す)。一般ユーザーには呼び出し側で渡さない(dashboard-workers の privacy 既定を維持)。 + * + * `user` は `users.name`。null(owner 不明 / no-auth 'local')は owner_id →'unknown' + * の順でフォールバック。`(user, kind)` の組で重複排除し、1 ワーカーに複数ユーザー / + * 複数種別が同居するケースに対応する。 + */ + export function listRunningJobOwnersByWorker(db: Database.Database): Map> { + const rows = db + .prepare( + `SELECT j.worker_id AS workerId, j.owner_id AS ownerId, j.task_kind AS taskKind, u.name AS ownerName + FROM jobs j + LEFT JOIN users u ON u.id = j.owner_id + WHERE j.status = 'running' AND j.worker_id IS NOT NULL`, + ) + .all() as Array<{ workerId: string; ownerId: string | null; taskKind: string | null; ownerName: string | null }>; + const byWorker = new Map>(); + for (const r of rows) { + const user = (r.ownerName && r.ownerName.trim()) || r.ownerId || 'unknown'; + const kind = r.taskKind || 'agent'; + const list = byWorker.get(r.workerId); + if (list) { + if (!list.some((o) => o.user === user && o.kind === kind)) list.push({ user, kind }); + } else { + byWorker.set(r.workerId, [{ user, kind }]); + } + } + return byWorker; + } + + + export async function updateJob(db: Database.Database, id: string, updates: Partial>): Promise { + const setClauses: string[] = ["updated_at = datetime('now')"]; + const params: Record = { id }; + + const fieldMap: Record = { + status: 'status', + pieceName: 'piece_name', + currentMovement: 'current_movement', + currentActivity: 'current_activity', + instruction: 'instruction', + branchName: 'branch_name', + worktreePath: 'worktree_path', + prNumber: 'pr_number', + attempt: 'attempt', + maxAttempts: 'max_attempts', + nextRetryAt: 'next_retry_at', + errorSummary: 'error_summary', + abortReason: 'abort_reason', + resumeMovement: 'resume_movement', + waitReason: 'wait_reason', + askCount: 'ask_count', + workerId: 'worker_id', + lastBackendId: 'last_backend_id', + parentJobId: 'parent_job_id', + subtaskDepth: 'subtask_depth', + requiredRole: 'required_profile', + requiredProfile: 'required_profile', + }; + + for (const [jsKey, dbCol] of Object.entries(fieldMap)) { + const val = (updates as Record)[jsKey]; + if (val !== undefined) { + setClauses.push(`${dbCol} = @${jsKey}`); + params[jsKey] = val; + } + } + + if (setClauses.length === 1) return; // updated_at のみ = 実質変更なし + + db + .prepare(`UPDATE jobs SET ${setClauses.join(', ')} WHERE id = @id`) + .run(params); + } + + + /** ジョブの updated_at のみを更新(ハートビート用)。updateJob は変更フィールドなしだと早期リターンするため別メソッド */ + export function touchJobUpdatedAt(db: Database.Database, id: string): void { + db.prepare("UPDATE jobs SET updated_at = datetime('now') WHERE id = ?").run(id); + } + + + /** + * Re-queue jobs parked with wait_reason='mcp_auth_required' for the given owner. + * Worker re-evaluates required_mcp on next pickup and will pause again if other servers + * are still unauthorized. _serverId is accepted for API symmetry but not used at SQL time + * (filtering by piece's required_mcp happens at the worker side). + * + * Returns the number of jobs actually re-queued. + */ + export function resumeMcpWaitingJobs(db: Database.Database, ownerId: string, _serverId: string): number { + const result = db + .prepare( + `UPDATE jobs + SET status='queued', wait_reason=NULL, updated_at=datetime('now') + WHERE status='waiting_human' AND wait_reason='mcp_auth_required' + AND owner_id = ?`, + ) + .run(ownerId); + return result.changes; + } + + + /** + * Re-queue a job parked with wait_reason='tool_request' after a user + * decided (approve/deny) the pending tool request. The worker resumes from + * `resume_movement` and re-reads the task's granted_tools. Returns the number + * of jobs re-queued (0 if the job is gone or no longer parked for approval). + */ + export function resumeToolRequestJob(db: Database.Database, jobId: string): number { + const result = db + .prepare( + `UPDATE jobs + SET status='queued', wait_reason=NULL, updated_at=datetime('now') + WHERE id = ? AND status='waiting_human' AND wait_reason='tool_request'`, + ) + .run(jobId); + return result.changes; + } + + /** + * Re-queue a job parked with wait_reason='package_request' after a user + * decided (approve/deny) the pending Python package request. On approve the + * package is already installed into the space overlay, so the resumed + * movement re-runs with the wheel importable. Returns the number of jobs + * re-queued (0 if the job is gone or no longer parked for this reason). + */ + export function resumePackageRequestJob(db: Database.Database, jobId: string): number { + const result = db + .prepare( + `UPDATE jobs + SET status='queued', wait_reason=NULL, updated_at=datetime('now') + WHERE id = ? AND status='waiting_human' AND wait_reason='package_request'`, + ) + .run(jobId); + return result.changes; + } + + + export async function lockIssue(db: Database.Database, repo: string, issueNumber: number, jobId: string): Promise { + try { + db + .prepare('INSERT INTO issue_locks (repo, issue_number, job_id) VALUES (?, ?, ?)') + .run(repo, issueNumber, jobId); + return true; + } catch { + return false; + } + } + + + export async function unlockIssue(db: Database.Database, repo: string, issueNumber: number): Promise { + db + .prepare('DELETE FROM issue_locks WHERE repo = ? AND issue_number = ?') + .run(repo, issueNumber); + } + + + export async function deleteJobsForIssue(db: Database.Database, repo: string, issueNumber: number): Promise { + const result = db + .prepare('DELETE FROM jobs WHERE repo = ? AND issue_number = ?') + .run(repo, issueNumber); + db + .prepare('DELETE FROM issue_locks WHERE repo = ? AND issue_number = ?') + .run(repo, issueNumber); + return result.changes; + } + + + export async function claimNextJob(db: Database.Database, workerId: string): Promise { + const row = db.prepare(` + UPDATE jobs + SET status = 'running', worker_id = ?, updated_at = datetime('now') + WHERE id = ( + SELECT j.id + FROM jobs j + JOIN worker_nodes w ON w.worker_id = ? + WHERE j.status = 'queued' + AND w.enabled = 1 + AND w.healthy = 1 + AND instr(w.profile_tags, ',' || j.required_profile || ',') > 0 + AND NOT EXISTS ( + SELECT 1 FROM issue_locks il + WHERE il.repo = j.repo AND il.issue_number = j.issue_number + ) + ORDER BY j.created_at ASC + LIMIT 1 + ) + RETURNING * + `).get(workerId, workerId) as JobRow | undefined; + return row ? rowToJob(row) : null; + } + + + /** + * リトライ待ちジョブの中から next_retry_at を過ぎたものを1件取得して running に遷移 + */ + export async function claimNextRetryJob(db: Database.Database, workerId: string): Promise { + const row = db.prepare(` + UPDATE jobs + SET status = 'running', worker_id = ?, updated_at = datetime('now') + WHERE id = ( + SELECT j.id + FROM jobs j + JOIN worker_nodes w ON w.worker_id = ? + WHERE j.status = 'retry' + AND replace(j.next_retry_at, 'T', ' ') <= datetime('now') + AND w.enabled = 1 + AND w.healthy = 1 + AND instr(w.profile_tags, ',' || j.required_profile || ',') > 0 + AND NOT EXISTS ( + SELECT 1 FROM issue_locks il + WHERE il.repo = j.repo AND il.issue_number = j.issue_number + ) + ORDER BY j.next_retry_at ASC + LIMIT 1 + ) + RETURNING * + `).get(workerId, workerId) as JobRow | undefined; + return row ? rowToJob(row) : null; + } + + + /** + * Read-only peek at the next job this worker WOULD claim (retry-priority, + * then oldest queued), without claiming it. Used by the idle-preferring + * claim gate to learn the next job's role before deciding whether to defer + * to an idler sibling. Mirrors the claimNext*Job WHERE clauses exactly. + */ + export async function peekNextClaimable(db: Database.Database, workerId: string): Promise { + const retry = db.prepare(` + SELECT j.* + FROM jobs j + JOIN worker_nodes w ON w.worker_id = ? + WHERE j.status = 'retry' + AND replace(j.next_retry_at, 'T', ' ') <= datetime('now') + AND w.enabled = 1 + AND w.healthy = 1 + AND instr(w.profile_tags, ',' || j.required_profile || ',') > 0 + AND NOT EXISTS ( + SELECT 1 FROM issue_locks il + WHERE il.repo = j.repo AND il.issue_number = j.issue_number + ) + ORDER BY j.next_retry_at ASC + LIMIT 1 + `).get(workerId) as JobRow | undefined; + if (retry) return rowToJob(retry); + + const queued = db.prepare(` + SELECT j.* + FROM jobs j + JOIN worker_nodes w ON w.worker_id = ? + WHERE j.status = 'queued' + AND w.enabled = 1 + AND w.healthy = 1 + AND instr(w.profile_tags, ',' || j.required_profile || ',') > 0 + AND NOT EXISTS ( + SELECT 1 FROM issue_locks il + WHERE il.repo = j.repo AND il.issue_number = j.issue_number + ) + ORDER BY j.created_at ASC + LIMIT 1 + `).get(workerId) as JobRow | undefined; + return queued ? rowToJob(queued) : null; + } + + + export async function getJobsByStatus(db: Database.Database, status: JobStatus): Promise { + const rows = db + .prepare('SELECT * FROM jobs WHERE status = ? ORDER BY created_at ASC') + .all(status) as JobRow[]; + return rows.map(rowToJob); + } + + + export async function getLatestJobForIssue(db: Database.Database, repo: string, issueNumber: number): Promise { + const row = db + .prepare('SELECT * FROM jobs WHERE repo = ? AND issue_number = ? ORDER BY created_at DESC LIMIT 1') + .get(repo, issueNumber) as JobRow | undefined; + return row ? rowToJob(row) : null; + } + + + export async function updateJobContext(db: Database.Database, jobId: string, payload: { promptTokens: number; limitTokens: number }): Promise { + const updatedAt = new Date().toISOString(); + db + .prepare( + `UPDATE jobs + SET context_prompt_tokens = ?, + context_limit_tokens = ?, + context_updated_at = ? + WHERE id = ?` + ) + .run(payload.promptTokens, payload.limitTokens, updatedAt, jobId); + } + + + /** 起動時に孤立ジョブを回復 */ + export async function recoverOrphanedJobs(db: Database.Database): Promise { + const result = db + .prepare("UPDATE jobs SET status = 'queued', worker_id = NULL, updated_at = datetime('now') WHERE status IN ('running', 'dispatching')") + .run(); + if (result.changes > 0) { + db.prepare('DELETE FROM issue_locks').run(); + logger.warn(`Repository: recovered ${result.changes} orphaned jobs, cleared issue locks`); + } + // waiting_subtasks のジョブで全サブジョブが完了済みのものを再キュー + // 同一 issue_number に複数ジョブがある場合、最新のみで判定する + const subtaskRecovery = db.prepare(` + UPDATE jobs + SET status = 'queued', updated_at = datetime('now') + WHERE status = 'waiting_subtasks' + AND ( + SELECT COUNT(*) FROM ( + SELECT s.status, ROW_NUMBER() OVER ( + PARTITION BY s.issue_number + ORDER BY s.created_at DESC, s.rowid DESC + ) AS rn + FROM jobs s + WHERE s.parent_job_id = jobs.id + ) WHERE rn = 1 + AND status NOT IN ('succeeded','failed','cancelled') + ) = 0 + `).run(); + if (subtaskRecovery.changes > 0) { + logger.warn(`Repository: recovered ${subtaskRecovery.changes} waiting_subtasks jobs`); + } + return result.changes; + } + + + /** + * running/dispatching 状態のまま staleMinutes 以上 updated_at が更新されていないジョブを + * queued に戻す(ランタイム watchdog) + */ + export function recoverStuckRunningJobs(db: Database.Database, staleMinutes: number): number { + const rows = db.prepare(` + UPDATE jobs + SET status = 'queued', + worker_id = NULL, + error_summary = 'Recovered: stuck in running for over ' || ? || ' minutes', + updated_at = datetime('now') + WHERE status IN ('running', 'dispatching') + AND updated_at < datetime('now', '-' || ? || ' minutes') + RETURNING id, repo + `).all(staleMinutes, staleMinutes) as Array<{ id: string; repo: string }>; + if (rows.length > 0) { + // issue ロックも解除 + for (const row of rows) { + db.prepare('DELETE FROM issue_locks WHERE job_id = ?').run(row.id); + } + logger.warn(`Repository: recovered ${rows.length} stuck jobs (stale > ${staleMinutes}min): ${rows.map(r => r.repo).join(', ')}`); + } + return rows.length; + } + + + /** running/dispatching 状態のジョブを全て queued に戻す(graceful shutdown 用) */ + export function requeueRunningJobs(db: Database.Database): number { + const result = db + .prepare("UPDATE jobs SET status = 'queued', worker_id = NULL, updated_at = datetime('now') WHERE status IN ('running', 'dispatching')") + .run(); + if (result.changes > 0) { + db.prepare('DELETE FROM issue_locks').run(); + logger.warn(`Repository: requeued ${result.changes} running jobs, cleared issue locks`); + } + return result.changes; + } + + + export function getDistinctRepos(db: Database.Database): string[] { + const rows = db.prepare('SELECT DISTINCT repo FROM jobs ORDER BY repo').all() as { repo: string }[]; + return rows.map(r => r.repo); + } + + + export function getJobsByRepo(db: Database.Database, repoName: string): Job[] { + const rows = db.prepare('SELECT * FROM jobs WHERE repo = ? ORDER BY created_at DESC').all(repoName) as JobRow[]; + return rows.map(rowToJob); + } + + + /** Issue ごとに最新の Job だけを返す(カンバンUI用) */ + export function getLatestJobsPerIssue(db: Database.Database, repoName: string): Job[] { + const rows = db.prepare(` + SELECT j.* + FROM jobs j + WHERE j.repo = ? + AND j.id = ( + SELECT j2.id + FROM jobs j2 + WHERE j2.repo = j.repo + AND j2.issue_number = j.issue_number + ORDER BY j2.updated_at DESC, j2.created_at DESC, j2.rowid DESC + LIMIT 1 + ) + ORDER BY j.updated_at DESC + `).all(repoName) as JobRow[]; + return rows.map(rowToJob); + } + + + // Cascade a local_task visibility change to all jobs spawned for that task + // and their recursive subtask descendants (repo='subtask/'). + // Returns the number of job rows updated. + export async function updateJobsVisibilityForTask(db: Database.Database, taskId: number, updates: { visibility: 'private' | 'org' | 'public'; visibilityScopeOrgId: string | null }): Promise { + const repoName = localTaskRepoName(taskId); + const now = new Date().toISOString(); + const result = db + .prepare(` + WITH RECURSIVE job_tree(id) AS ( + SELECT id FROM jobs WHERE repo = ? + UNION ALL + SELECT j.id FROM jobs j JOIN job_tree jt ON j.parent_job_id = jt.id + ) + UPDATE jobs + SET visibility = ?, + visibility_scope_org_id = ?, + updated_at = ? + WHERE id IN (SELECT id FROM job_tree) + `) + .run(repoName, updates.visibility, updates.visibilityScopeOrgId, now); + return result.changes; + } + + + export async function getSubJobs(db: Database.Database, parentJobId: string): Promise { + // 同一 issue_number に複数ジョブがある場合(ASK再投入等)、最新のみ返す + // ROW_NUMBER() + rowid で同一 created_at でも一意に決定する + const rows = db + .prepare(` + SELECT * FROM ( + SELECT j.*, ROW_NUMBER() OVER ( + PARTITION BY j.issue_number + ORDER BY j.created_at DESC, j.rowid DESC + ) AS rn + FROM jobs j + WHERE j.parent_job_id = ? + ) WHERE rn = 1 + ORDER BY issue_number ASC + `) + .all(parentJobId) as JobRow[]; + return rows.map(rowToJob); + } + + + /** + * 全サブジョブが終端状態なら親ジョブを再キューに戻す。 + * 再キューできた場合 true を返す。 + */ + export async function requeueParentJobIfAllSubtasksDone(db: Database.Database, parentJobId: string): Promise { + // 同一 issue_number に複数ジョブがある場合、最新のもの(ROW_NUMBER=1)のみで判定する + const result = db.prepare(` + UPDATE jobs + SET status = 'queued', + updated_at = datetime('now') + WHERE id = ? + AND status = 'waiting_subtasks' + AND ( + SELECT COUNT(*) FROM ( + SELECT status, ROW_NUMBER() OVER ( + PARTITION BY issue_number + ORDER BY created_at DESC, rowid DESC + ) AS rn + FROM jobs + WHERE parent_job_id = ? + ) WHERE rn = 1 + AND status NOT IN ('succeeded', 'failed', 'cancelled') + ) = 0 + `).run(parentJobId, parentJobId); + return result.changes > 0; + } + + + /** + * 実行中のジョブをキャンセル状態に変更する。 + * running または dispatching 状態のジョブのみ対象。 + * 戻り値: キャンセル対象ジョブが見つかったら true、見つからなかったら false。 + */ + export function requestJobCancel(db: Database.Database, jobId: string): boolean { + const result = db.prepare(` + UPDATE jobs + SET status = 'cancelled', updated_at = datetime('now') + WHERE id = ? AND status IN ('running', 'dispatching') + `).run(jobId); + return result.changes > 0; + } diff --git a/src/db/repositories/llm-usage.ts b/src/db/repositories/llm-usage.ts new file mode 100644 index 0000000..eb5c8ea --- /dev/null +++ b/src/db/repositories/llm-usage.ts @@ -0,0 +1,254 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// llm-usage repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; + +/** Per-call delta for the per-user daily LLM usage ledger. */ +export interface LlmUsageIncrement { + /** UTC day bucket 'YYYY-MM-DD'. Defaults to today (UTC) when omitted. */ + day?: string; + /** Owner id, or 'local' (no-auth) / 'system' (ownerless) sentinel. */ + userId: string; + source: 'gateway' | 'direct'; + /** Real model name (chunk.model), routing-key fallback, or 'unknown'. */ + model: string; + /** Backend server name (gateway backendId / direct host), or 'unknown'. */ + route: string; + tokensIn?: number; + tokensOut?: number; + requests?: number; + at?: string; +} + + +/** Daily-grouped aggregate row (model/route collapsed) for the usage API. */ +export interface LlmUsageDailyAgg { + day: string; + userId: string; + source: string; + tokensIn: number; + tokensOut: number; + requests: number; +} + + +/** Hour-grain UPSERT input for the v2 usage ledger. */ +export interface LlmUsageHourlyIncrement { + /** UTC hour bucket 'YYYY-MM-DDTHH'. Defaults to the current hour (UTC). */ + hour?: string; + /** Owner id, 'local' / 'system' sentinel, or 'gw:' for downstream gateway consumers. */ + userId: string; + /** + * 'direct' / 'gateway' are this orchestrator's own calls. 'gateway_downstream' + * is traffic the AAO Gateway proxied for an EXTERNAL consumer (recorded from + * the gateway proxy path, keyed by team) — kept distinct so it never merges + * with our own usage. See docs/superpowers/specs/2026-06-19-usage-hourly-profile-and-gateway-downstream-design.md + */ + source: 'gateway' | 'direct' | 'gateway_downstream'; + /** Real model name (chunk.model), routing-key fallback, or 'unknown'. */ + model: string; + /** Backend server name (gateway backendId / direct host), or 'unknown'. */ + route: string; + tokensIn?: number; + tokensOut?: number; + requests?: number; + /** ISO timestamp for last_updated_at + hour default. Defaults to now. */ + at?: string; +} + + +/** + * Raw hour-grain ledger row (no axis collapsed) for the usage API. The API + * re-buckets `hour` into the viewer's local calendar period and groups by + * whichever of source/model/route/user/org the request asked for. + */ +export interface LlmUsageHourlyRow { + hour: string; + userId: string; + source: string; + model: string; + route: string; + tokensIn: number; + tokensOut: number; + requests: number; +} + + + // ── Per-user daily LLM usage (gateway + direct) ────────────────────── + // + // Recorded at the OpenAICompatClient completion boundary for every + // successful chat completion. UPSERT on the (day, user_id, source, + // model, route) grain. Separate lens from gateway_key_usage — never + // summed across the two tables. Spec: + // docs/superpowers/specs/2026-06-11-llm-usage-aggregation-design.md + + /** + * UPSERT: bump per-(day, user, source, model, route) counters. Deltas + * are clamped at zero. `day` defaults to the UTC day of `at` (or now). + * Called once per successful stream completion; `usage`-less completions + * still bump `requests` (tokens 0) so a 0-token request is distinct from + * a failed/aborted one (which is never recorded). + */ + export function incrementLlmUsage(db: Database.Database, params: LlmUsageIncrement): void { + const tIn = Math.max(0, Math.floor(params.tokensIn ?? 0)); + const tOut = Math.max(0, Math.floor(params.tokensOut ?? 0)); + const reqs = Math.max(0, Math.floor(params.requests ?? 1)); + const ts = params.at ?? new Date().toISOString(); + const day = params.day ?? ts.slice(0, 10); + db + .prepare( + `INSERT INTO llm_usage_daily + (day, user_id, source, model, route, tokens_in, tokens_out, requests, last_updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT (day, user_id, source, model, route) DO UPDATE SET + tokens_in = tokens_in + excluded.tokens_in, + tokens_out = tokens_out + excluded.tokens_out, + requests = requests + excluded.requests, + last_updated_at = excluded.last_updated_at`, + ) + .run(day, params.userId, params.source, params.model, params.route, tIn, tOut, reqs, ts); + } + + + /** + * Daily time series for the usage dashboard, grouped by (day, user_id, + * source) with model/route collapsed. `userId` filter scopes a non-admin + * to their own rows; omit it for the admin all-users view (callers can + * collapse user_id afterwards). Inclusive `from`/`to` are 'YYYY-MM-DD'. + */ + export function queryLlmUsageDaily(db: Database.Database, opts: { from: string; to: string; userId?: string }): LlmUsageDailyAgg[] { + const where = ['day >= ?', 'day <= ?']; + const args: unknown[] = [opts.from, opts.to]; + if (opts.userId !== undefined) { + where.push('user_id = ?'); + args.push(opts.userId); + } + const rows = db + .prepare( + `SELECT day, user_id, source, + SUM(tokens_in) AS tokens_in, + SUM(tokens_out) AS tokens_out, + SUM(requests) AS requests + FROM llm_usage_daily + WHERE ${where.join(' AND ')} + GROUP BY day, user_id, source + ORDER BY day ASC`, + ) + .all(...args) as Array<{ + day: string; + user_id: string; + source: string; + tokens_in: number; + tokens_out: number; + requests: number; + }>; + return rows.map((r) => ({ + day: r.day, + userId: r.user_id, + source: r.source, + tokensIn: r.tokens_in, + tokensOut: r.tokens_out, + requests: r.requests, + })); + } + + + /** + * v2 write path: UPSERT per-(hour, user, source, model, route) counters. + * `hour` defaults to the current UTC hour 'YYYY-MM-DDTHH'. Same contract as + * incrementLlmUsage (deltas clamped at zero; usage-less completions still + * bump `requests`). Supersedes incrementLlmUsage as the recorder target. + */ + export function incrementLlmUsageHourly(db: Database.Database, params: LlmUsageHourlyIncrement): void { + const tIn = Math.max(0, Math.floor(params.tokensIn ?? 0)); + const tOut = Math.max(0, Math.floor(params.tokensOut ?? 0)); + const reqs = Math.max(0, Math.floor(params.requests ?? 1)); + const ts = params.at ?? new Date().toISOString(); + const hour = params.hour ?? ts.slice(0, 13); + db + .prepare( + `INSERT INTO llm_usage_hourly + (hour, user_id, source, model, route, tokens_in, tokens_out, requests, last_updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT (hour, user_id, source, model, route) DO UPDATE SET + tokens_in = tokens_in + excluded.tokens_in, + tokens_out = tokens_out + excluded.tokens_out, + requests = requests + excluded.requests, + last_updated_at = excluded.last_updated_at`, + ) + .run(hour, params.userId, params.source, params.model, params.route, tIn, tOut, reqs, ts); + } + + + /** + * Raw hour-grain rows for the usage dashboard v2, no axis collapsed so the + * API can group by any of source/model/route/user/org and re-bucket into the + * viewer's local timezone. `userId` scopes a non-admin to their own rows. + * Inclusive `fromHour`/`toHour` are 'YYYY-MM-DDTHH' (UTC) — callers widen the + * UTC window by ±1 day before filtering precisely against local days. + */ + export function queryLlmUsageHourly(db: Database.Database, opts: { + fromHour: string; + toHour: string; + userId?: string; + }): LlmUsageHourlyRow[] { + const where = ['hour >= ?', 'hour <= ?']; + const args: unknown[] = [opts.fromHour, opts.toHour]; + if (opts.userId !== undefined) { + where.push('user_id = ?'); + args.push(opts.userId); + } + const rows = db + .prepare( + `SELECT hour, user_id, source, model, route, tokens_in, tokens_out, requests + FROM llm_usage_hourly + WHERE ${where.join(' AND ')} + ORDER BY hour ASC`, + ) + .all(...args) as Array<{ + hour: string; + user_id: string; + source: string; + model: string; + route: string; + tokens_in: number; + tokens_out: number; + requests: number; + }>; + return rows.map((r) => ({ + hour: r.hour, + userId: r.user_id, + source: r.source, + model: r.model, + route: r.route, + tokensIn: r.tokens_in, + tokensOut: r.tokens_out, + requests: r.requests, + })); + } + + + /** + * Map every user to a single org label for the usage dashboard's "by org" + * breakdown. Unions Gitea org cache (user_gitea_orgs) and local orgs + * (local_org_members → local_orgs.name); a multi-org user collapses to the + * alphabetically-first org name (MIN) so the breakdown is deterministic. + * Users with no org (and the 'local'/'system' sentinels) are simply absent — + * the API buckets them under 'no-org'. + */ + export function getUsageOrgMap(db: Database.Database): Map { + const rows = db + .prepare( + `SELECT user_id, MIN(org_name) AS org_name FROM ( + SELECT user_id, org_name FROM user_gitea_orgs + UNION ALL + SELECT m.user_id AS user_id, o.name AS org_name + FROM local_org_members m + JOIN local_orgs o ON o.id = m.org_id + ) + GROUP BY user_id`, + ) + .all() as Array<{ user_id: string; org_name: string }>; + const map = new Map(); + for (const r of rows) map.set(r.user_id, r.org_name); + return map; + } diff --git a/src/db/repositories/local-tasks.ts b/src/db/repositories/local-tasks.ts new file mode 100644 index 0000000..dc245cf --- /dev/null +++ b/src/db/repositories/local-tasks.ts @@ -0,0 +1,965 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// local-tasks repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { rmSync, existsSync, realpathSync } from 'fs'; +import { join, normalize, sep } from 'path'; +import { randomUUID } from 'crypto'; +import { logger } from '../../logger.js'; +import { buildVisibilityWhere } from '../../bridge/visibility.js'; +import { buildTitleFromGoal } from '../../title-generation.js'; +import { utc, localTaskRepoName } from './shared.js'; +import { Job, SubtaskInfo, JobRow, rowToJob } from './jobs.js'; +import * as taskSearchRepo from './task-search.js'; +import * as jobsRepo from './jobs.js'; +import * as spacesRepo from './spaces.js'; + +/** + * Shared SQL fragments for LocalTask read queries. + * + * getLocalTask / listLocalTasks / getLocalTaskByShareToken all need to expose + * the owner's display name and the org display name of `visibility_scope_org_id`. + * These constants keep the three queries in sync. + * + * Usage: splice into the SELECT list and the FROM-clause joins, e.g. + * + * SELECT lt.*, ${LOCAL_TASK_DISPLAY_SELECT} + * FROM local_tasks lt + * ${LOCAL_TASK_DISPLAY_JOIN} + * WHERE ... + * + * A correlated subquery (`MIN(org_name)`) is used instead of a JOIN because + * user_gitea_orgs is keyed per-user, and we only need any one display name + * for the org id — this avoids row-multiplication across the join. + */ +export const LOCAL_TASK_DISPLAY_SELECT = ` + u.name AS owner_name, + COALESCE( + (SELECT MIN(org_name) FROM user_gitea_orgs WHERE org_id = lt.visibility_scope_org_id), + (SELECT name FROM local_orgs WHERE id = lt.visibility_scope_org_id) + ) AS visibility_scope_org_name +`.trim(); + +export const LOCAL_TASK_DISPLAY_JOIN = `LEFT JOIN users u ON u.id = lt.owner_id`; + + +export type TitleSource = 'auto' | 'agent' | 'user'; + + +export interface LocalTask { + id: number; + title: string; + /** Provenance of `title`. 'user' is never auto-overwritten by the agent. */ + titleSource: TitleSource; + body: string; + pieceName: string; + profile: 'auto' | 'fast' | 'quality' | string; + outputFormat: 'text' | 'markdown' | 'json' | string; + askPolicy: 'low' | 'high' | string; + priority: 'low' | 'medium' | 'high' | string; + state: 'open' | 'closed' | string; + workspacePath: string | null; + /** 実行先ワークスペースの種類。persistent=スペース共有ツリー / ephemeral=使い捨て。 */ + workspaceMode: 'persistent' | 'ephemeral'; + ownerId: string | null; + ownerName?: string | null; + visibility: 'private' | 'org' | 'public'; + visibilityScopeOrgId: string | null; + visibilityScopeOrgName?: string | null; + /** 所属スペース。null は owner の個人スペース(=現行の data/users/{owner})に解決される。 */ + spaceId: string | null; + /** 実行ログ root(計画5)。null = 後方互換で workspacePath/logs に解決。 */ + runtimeDir: string | null; + createdAt: string; + updatedAt: string; + feedbackRating: 'good' | 'bad' | null; + feedbackTags: string[] | null; + feedbackComment: string | null; + feedbackAt: string | null; + shareToken: string | null; + sharedAt: string | null; + browserSessionProfileId?: number | null; + /** + * Mission Brief: per-task pinned memo. Carries goal / done / open / + * clarifications. Always rendered at the top of every movement's + * system prompt. The LLM updates it via mission_update; the user + * edits it from the Overview tab. + */ + missionBrief: MissionBrief | null; + /** Per-task runtime options (e.g. { mcpDisabled, skillsDisabled }). */ + options: Record; + latestJob?: Job | null; + subtasks?: SubtaskInfo[]; + subtaskCount?: number; + subtaskCompleted?: number; +} + + +/** + * Ordered Mission Brief field list. Single source of truth for parse / update + * / API whitelist / MissionUpdate tool so a future field is a one-line + * addition here (plus its typed interface member). Legacy fields come first so + * JSON key order and title-derivation (goal only) stay unchanged. + */ +export const MISSION_BRIEF_FIELDS = [ + 'goal', + 'done', + 'open', + 'clarifications', + 'user_constraints', + 'decisions', + 'current_focus', +] as const; + + +export type MissionBriefField = (typeof MISSION_BRIEF_FIELDS)[number]; + + +export interface MissionBrief { + goal: string; + done: string; + open: string; + clarifications: string; + /** Durable user-stated constraints ("do not change X"). */ + user_constraints?: string; + /** Decisions made after clarification, with rationale. */ + decisions?: string; + /** What this movement is actively working on right now. */ + current_focus?: string; +} + + +export interface LocalTaskComment { + id: number; + taskId: number; + author: string; + kind: string; + body: string; + /** Filenames saved to the task's input/ dir for this comment (UI download links). */ + attachments: string[]; + createdAt: string; + injectedAt: string | null; +} + + +export interface CreateLocalTaskParams { + title: string; + /** Defaults to 'auto'. Pass 'user' when the caller supplied an explicit title. */ + titleSource?: TitleSource; + body: string; + pieceName?: string; + profile?: 'auto' | 'fast' | 'quality'; + outputFormat?: 'text' | 'markdown' | 'json'; + askPolicy?: 'low' | 'high'; + priority?: 'low' | 'medium' | 'high'; + workspacePath?: string | null; + /** ワークスペースの種類。未指定は 'persistent'(スペース共有)。 */ + workspaceMode?: 'persistent' | 'ephemeral'; + ownerId?: string | null; + visibility?: 'private' | 'org' | 'public'; + visibilityScopeOrgId?: string | null; + browserSessionProfileId?: number | null; + /** 所属スペース。未指定(null)は owner の個人スペースに解決される。 */ + spaceId?: string | null; + /** Per-task runtime options (e.g. { mcpDisabled, skillsDisabled }). Stored as JSON. */ + options?: Record; +} + + +export interface LocalTaskRow { + id: number; + title: string; + title_source: string | null; + body: string; + piece_name: string; + profile: string; + output_format: string; + ask_policy: string; + priority: string; + state: string; + workspace_path: string | null; + workspace_mode: string | null; + runtime_dir: string | null; + owner_id: string | null; + owner_name?: string | null; + visibility: string | null; + visibility_scope_org_id: string | null; + visibility_scope_org_name?: string | null; + space_id: string | null; + created_at: string; + updated_at: string; + feedback_rating: string | null; + feedback_comment: string | null; + feedback_tags: string | null; + feedback_at: string | null; + share_token: string | null; + shared_at: string | null; + mission_brief: string | null; + browser_session_profile_id: number | null; + options: string | null; +} + + +export interface LocalTaskCommentRow { + id: number; + task_id: number; + author: string; + kind: string; + body: string; + attachments: string | null; + created_at: string; + injected_at: string | null; +} + + +export function rowToLocalTask(row: LocalTaskRow): LocalTask { + return { + id: row.id, + title: row.title, + titleSource: (row.title_source as TitleSource | null) ?? 'auto', + body: row.body, + pieceName: row.piece_name, + profile: row.profile, + outputFormat: row.output_format, + askPolicy: row.ask_policy, + priority: row.priority, + state: row.state, + workspacePath: row.workspace_path, + workspaceMode: row.workspace_mode === 'ephemeral' ? 'ephemeral' : 'persistent', + ownerId: row.owner_id ?? null, + ownerName: row.owner_name ?? null, + visibility: (row.visibility ?? 'private') as LocalTask['visibility'], + visibilityScopeOrgId: row.visibility_scope_org_id ?? null, + visibilityScopeOrgName: row.visibility_scope_org_name ?? null, + spaceId: row.space_id ?? null, + runtimeDir: row.runtime_dir ?? null, + createdAt: utc(row.created_at), + updatedAt: utc(row.updated_at), + feedbackRating: (row.feedback_rating as 'good' | 'bad' | null) ?? null, + feedbackTags: row.feedback_tags ? JSON.parse(row.feedback_tags) : null, + feedbackComment: row.feedback_comment ?? null, + feedbackAt: row.feedback_at ? utc(row.feedback_at) : null, + shareToken: row.share_token ?? null, + sharedAt: row.shared_at ? utc(row.shared_at) : null, + browserSessionProfileId: row.browser_session_profile_id ?? null, + missionBrief: parseMissionBrief(row.mission_brief), + options: parseTaskOptions(row.options), + }; +} + + +export function parseMissionBrief(raw: string | null | undefined): MissionBrief | null { + if (!raw) return null; + try { + const parsed = JSON.parse(raw); + if (!parsed || typeof parsed !== 'object') return null; + const brief = {} as MissionBrief; + for (const field of MISSION_BRIEF_FIELDS) { + brief[field] = typeof parsed[field] === 'string' ? parsed[field] : ''; + } + if (MISSION_BRIEF_FIELDS.every((field) => !brief[field])) return null; + return brief; + } catch { + return null; + } +} + + +export function parseTaskOptions(raw: string | null | undefined): Record { + if (!raw) return {}; + try { + const parsed = JSON.parse(raw); + if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { + return parsed as Record; + } + return {}; + } catch { + return {}; + } +} + + +export function rowToLocalTaskComment(row: LocalTaskCommentRow): LocalTaskComment { + return { + id: row.id, + taskId: row.task_id, + author: row.author, + kind: row.kind, + body: row.body, + attachments: parseCommentAttachments(row.attachments), + createdAt: utc(row.created_at), + injectedAt: row.injected_at ? utc(row.injected_at) : null, + }; +} + + +/** Parse the JSON attachments column to a string[]; tolerate null/legacy/garbage. */ +export function parseCommentAttachments(raw: string | null): string[] { + if (!raw) return []; + try { + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? parsed.filter((n): n is string => typeof n === 'string') : []; + } catch { + return []; + } +} + + + /** + * タスクの実効スペースを解決し FolderContext を返す。 + * - space_id があればそのスペース、無ければ owner の個人スペース(無ければ lazy 生成)。 + * - owner も無い(no-auth)の場合は 'local' の個人スペース。 + * 返り値は userPiecesDir/readUserAgentsMd 等にそのまま渡せる { rootDir, leafId }。 + */ + export async function resolveTaskFolderContext(db: Database.Database, taskId: number, userFolderRoot: string): Promise { + const { resolveSpaceFolder } = await import('../../spaces/folder-resolver.js'); + const row = db + .prepare(`SELECT space_id, owner_id FROM local_tasks WHERE id = ?`) + .get(taskId) as { space_id: string | null; owner_id: string | null } | undefined; + const ownerId = row?.owner_id ?? 'local'; + if (row?.space_id) { + const space = await spacesRepo.getSpace(db, row.space_id); + if (space) return resolveSpaceFolder(space, userFolderRoot); + // 参照先スペースが消えている場合は個人スペースにフォールバック + } + const personal = await spacesRepo.ensurePersonalSpace(db, ownerId); + return resolveSpaceFolder(personal, userFolderRoot); + } + + + export async function createLocalTask(db: Database.Database, params: CreateLocalTaskParams): Promise { + const result = db + .prepare( + `INSERT INTO local_tasks (title, title_source, body, piece_name, profile, output_format, ask_policy, priority, workspace_path, workspace_mode, owner_id, visibility, visibility_scope_org_id, browser_session_profile_id, space_id, options) + VALUES (@title, @titleSource, @body, @pieceName, @profile, @outputFormat, @askPolicy, @priority, @workspacePath, @workspaceMode, @ownerId, @visibility, @visibilityScopeOrgId, @browserSessionProfileId, @spaceId, @options)` + ) + .run({ + title: params.title, + titleSource: params.titleSource ?? 'auto', + body: params.body, + pieceName: params.pieceName ?? 'chat', + profile: params.profile ?? 'auto', + outputFormat: params.outputFormat ?? 'markdown', + askPolicy: params.askPolicy ?? 'low', + priority: params.priority ?? 'medium', + workspacePath: params.workspacePath ?? null, + workspaceMode: params.workspaceMode ?? 'persistent', + ownerId: params.ownerId ?? null, + visibility: params.visibility ?? 'private', + visibilityScopeOrgId: params.visibilityScopeOrgId ?? null, + browserSessionProfileId: params.browserSessionProfileId ?? null, + spaceId: params.spaceId ?? null, + options: JSON.stringify(params.options ?? {}), + }); + + const task = await getLocalTask(db, Number(result.lastInsertRowid)); + if (!task) throw new Error('createLocalTask: failed to load inserted task'); + return task; + } + + + export async function getLocalTask(db: Database.Database, taskId: number, opts?: { viewer?: Express.User }): Promise { + const viewerClause = opts?.viewer + ? buildVisibilityWhere(opts.viewer, 'lt', { spaceColumn: 'space_id' }) + : { clause: '1=1', params: [] as unknown[] }; + const row = db + .prepare(` + SELECT lt.*, + ${LOCAL_TASK_DISPLAY_SELECT} + FROM local_tasks lt + ${LOCAL_TASK_DISPLAY_JOIN} + WHERE lt.id = ? AND ${viewerClause.clause} + `) + .get(taskId, ...viewerClause.params) as LocalTaskRow | undefined; + if (!row) return null; + const task = rowToLocalTask(row); + task.latestJob = await jobsRepo.getLatestJobForIssue(db, localTaskRepoName(taskId), taskId); + // サブタスク情報を付与 + if (task.latestJob) { + const subJobs = await jobsRepo.getSubJobs(db, task.latestJob.id); + if (subJobs.length > 0) { + task.subtasks = await jobsRepo.buildSubtaskInfos(db, subJobs); + task.subtaskCount = subJobs.length; + task.subtaskCompleted = subJobs.filter(j => + ['succeeded', 'failed', 'cancelled'].includes(j.status) + ).length; + } + } + return task; + } + + + export async function shareLocalTask(db: Database.Database, taskId: number): Promise { + const existing = await getLocalTask(db, taskId); + if (!existing) throw new Error(`Task ${taskId} not found`); + if (existing.shareToken) return existing.shareToken; + + const token = randomUUID(); + db.prepare( + `UPDATE local_tasks SET share_token = ?, shared_at = datetime('now'), updated_at = datetime('now') WHERE id = ?` + ).run(token, taskId); + return token; + } + + + export async function unshareLocalTask(db: Database.Database, taskId: number): Promise { + db.prepare( + `UPDATE local_tasks SET share_token = NULL, shared_at = NULL, updated_at = datetime('now') WHERE id = ?` + ).run(taskId); + } + + + /** + * Mission Brief: partial-replace update. Only fields explicitly provided + * are written; undefined leaves the field untouched. Passing all-empty + * strings is treated as "clear the brief" (NULL in storage). + * + * Returns the merged brief so callers can echo it back to the client + * without an extra read. + */ + export async function updateMissionBrief(db: Database.Database, taskId: number, patch: Partial): Promise { + return updateMissionBriefSync(db, taskId, patch); + } + + + /** Sync variant used by the engine's MissionBriefIO so it can be called + * from sync paths (e.g. buildSystemPrompt). better-sqlite3 is sync + * underneath anyway. */ + export function updateMissionBriefSync(db: Database.Database, taskId: number, patch: Partial): MissionBrief | null { + const row = db + .prepare(`SELECT mission_brief, title_source FROM local_tasks WHERE id = ?`) + .get(taskId) as { mission_brief: string | null; title_source: string | null } | undefined; + const existing = parseMissionBrief(row?.mission_brief ?? null); + const next = {} as MissionBrief; + for (const field of MISSION_BRIEF_FIELDS) { + next[field] = patch[field] !== undefined ? patch[field] : existing?.[field] ?? ''; + } + const allEmpty = MISSION_BRIEF_FIELDS.every((field) => !next[field]); + const stored = allEmpty ? null : JSON.stringify(next); + + // Derive the task title from the agent's goal (no LLM call). Only when the + // goal value actually changed (agents re-send an unchanged brief across + // iterations — re-deriving every time would churn updated_at and flicker + // the title) and the user hasn't taken manual control (a user edit pins + // title_source='user' and is never overwritten). + const goalChanged = patch.goal !== undefined && patch.goal !== (existing?.goal ?? ''); + const derivedTitle = (goalChanged && (row?.title_source ?? 'auto') !== 'user') + ? buildTitleFromGoal(next.goal) + : ''; + + // Atomic: persist the brief and the derived title as one unit so a crash + // between them can't leave the title out of sync with the goal. + db.transaction(() => { + db.prepare( + `UPDATE local_tasks SET mission_brief = ?, updated_at = datetime('now') WHERE id = ?` + ).run(stored, taskId); + if (derivedTitle) { + db.prepare( + `UPDATE local_tasks SET title = ?, title_source = 'agent' WHERE id = ?` + ).run(derivedTitle, taskId); + } + })(); + return allEmpty ? null : next; + } + + + /** Sync read of the mission brief column. Used by MissionBriefIO.read() */ + export function getMissionBriefSync(db: Database.Database, taskId: number): MissionBrief | null { + const row = db.prepare(`SELECT mission_brief FROM local_tasks WHERE id = ?`).get(taskId) as { mission_brief: string | null } | undefined; + return parseMissionBrief(row?.mission_brief ?? null); + } + + + /** + * Construct a MissionBriefIO bound to a specific local task. The engine + * uses this to thread mission brief read/write into the ToolContext + * without leaking the repository instance into tool code. + */ + export function makeMissionBriefIO(db: Database.Database, taskId: number): import('../../engine/tools/core.js').MissionBriefIO { + return { + read: () => getMissionBriefSync(db, taskId), + update: (patch) => updateMissionBriefSync(db, taskId, patch), + }; + } + + + /** + * Construct a TaskConversationIO bound to a single local task (comments) + + * this run's transcript path. Used by SearchTaskConversation / + * ReadTaskConversation; the binding is what scopes those tools to the current + * task so they can't read another task's logs. + */ + export function makeTaskConversationIO(db: Database.Database, taskId: number, transcriptPath?: string): import('../../engine/tools/core.js').TaskConversationIO { + return { + listComments: () => listLocalTaskComments(db, taskId), + transcriptPath, + }; + } + + + /** + * Transcript-only conversation IO for runs with no backing local_task + * (sub-tasks). Comments are always empty — sub-tasks don't write + * local_task comments — so only the transcript source is active. Mirrors + * makeTaskConversationIO but without a taskId. + */ + export function makeTranscriptOnlyConversationIO(transcriptPath: string | undefined): import('../../engine/tools/core.js').TaskConversationIO { + return { + listComments: async () => [], + transcriptPath, + }; + } + + + export async function getLocalTaskByShareToken(db: Database.Database, token: string): Promise { + const row = db + .prepare(` + SELECT lt.*, + ${LOCAL_TASK_DISPLAY_SELECT} + FROM local_tasks lt + ${LOCAL_TASK_DISPLAY_JOIN} + WHERE lt.share_token = ? + `) + .get(token) as LocalTaskRow | undefined; + if (!row) return null; + const task = rowToLocalTask(row); + task.latestJob = await jobsRepo.getLatestJobForIssue(db, localTaskRepoName(task.id), task.id); + if (task.latestJob) { + const subJobs = await jobsRepo.getSubJobs(db, task.latestJob.id); + if (subJobs.length > 0) { + task.subtasks = await jobsRepo.buildSubtaskInfos(db, subJobs); + task.subtaskCount = subJobs.length; + task.subtaskCompleted = subJobs.filter(j => + ['succeeded', 'failed', 'cancelled'].includes(j.status) + ).length; + } + } + return task; + } + + + export async function listLocalTasks(db: Database.Database, filter?: { ownerId?: string; viewer?: Express.User }): Promise { + // 1. Single JOIN query: local_tasks LEFT JOIN jobs (latest per task via correlated subquery) + const conditions: string[] = []; + const queryParams: unknown[] = []; + if (filter?.ownerId) { + conditions.push('lt.owner_id = ?'); + queryParams.push(filter.ownerId); + } + if (filter?.viewer) { + const w = buildVisibilityWhere(filter.viewer, 'lt', { spaceColumn: 'space_id' }); + conditions.push(w.clause); + queryParams.push(...w.params); + } + const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; + + const joinedRows = db + .prepare(` + SELECT + lt.*, + ${LOCAL_TASK_DISPLAY_SELECT}, + j.id AS job_id, + j.repo AS job_repo, + j.issue_number AS job_issue_number, + j.pr_number AS job_pr_number, + j.status AS job_status, + j.piece_name AS job_piece_name, + j.required_profile AS job_required_profile, + j.task_class AS job_task_class, + j.current_movement AS job_current_movement, + j.current_activity AS job_current_activity, + j.instruction AS job_instruction, + j.branch_name AS job_branch_name, + j.worktree_path AS job_worktree_path, + j.attempt AS job_attempt, + j.max_attempts AS job_max_attempts, + j.next_retry_at AS job_next_retry_at, + j.error_summary AS job_error_summary, + j.abort_reason AS job_abort_reason, + j.resume_movement AS job_resume_movement, + j.wait_reason AS job_wait_reason, + j.ask_count AS job_ask_count, + j.worker_id AS job_worker_id, + j.last_backend_id AS job_last_backend_id, + j.parent_job_id AS job_parent_job_id, + j.continued_from_job_id AS job_continued_from_job_id, + j.subtask_depth AS job_subtask_depth, + j.owner_id AS job_owner_id, + j.visibility AS job_visibility, + j.visibility_scope_org_id AS job_visibility_scope_org_id, + j.created_at AS job_created_at, + j.updated_at AS job_updated_at, + j.context_prompt_tokens AS job_context_prompt_tokens, + j.context_limit_tokens AS job_context_limit_tokens, + j.context_updated_at AS job_context_updated_at, + j.browser_session_profile_id AS job_browser_session_profile_id, + j.task_kind AS job_task_kind, + j.payload AS job_payload + FROM local_tasks lt + ${LOCAL_TASK_DISPLAY_JOIN} + LEFT JOIN jobs j ON j.id = ( + SELECT j2.id FROM jobs j2 + WHERE j2.repo = 'local/task-' || lt.id + AND j2.issue_number = lt.id + ORDER BY j2.created_at DESC, j2.rowid DESC + LIMIT 1 + ) + ${whereClause} + ORDER BY lt.updated_at DESC, lt.id DESC + `) + .all(...queryParams) as Array; + + // Build tasks with latestJob from joined data + const tasks: LocalTask[] = []; + const jobIds: string[] = []; + + for (const row of joinedRows) { + const task = rowToLocalTask(row); + if (row.job_id) { + task.latestJob = rowToJob({ + id: row.job_id, + repo: row.job_repo!, + issue_number: row.job_issue_number!, + pr_number: row.job_pr_number ?? null, + status: row.job_status!, + piece_name: row.job_piece_name!, + current_movement: row.job_current_movement ?? null, + current_activity: row.job_current_activity ?? null, + instruction: row.job_instruction!, + branch_name: row.job_branch_name ?? null, + worktree_path: row.job_worktree_path ?? null, + attempt: row.job_attempt!, + max_attempts: row.job_max_attempts!, + next_retry_at: row.job_next_retry_at ?? null, + error_summary: row.job_error_summary ?? null, + abort_reason: row.job_abort_reason ?? null, + resume_movement: row.job_resume_movement ?? null, + wait_reason: row.job_wait_reason ?? null, + ask_count: row.job_ask_count!, + worker_id: row.job_worker_id ?? null, + last_backend_id: row.job_last_backend_id ?? null, + parent_job_id: row.job_parent_job_id ?? null, + continued_from_job_id: row.job_continued_from_job_id ?? null, + subtask_depth: row.job_subtask_depth ?? 0, + required_profile: row.job_required_profile!, + task_class: row.job_task_class!, + owner_id: row.job_owner_id ?? null, + visibility: row.job_visibility ?? null, + visibility_scope_org_id: row.job_visibility_scope_org_id ?? null, + context_prompt_tokens: row.job_context_prompt_tokens ?? null, + context_limit_tokens: row.job_context_limit_tokens ?? null, + context_updated_at: row.job_context_updated_at ?? null, + browser_session_profile_id: row.job_browser_session_profile_id ?? null, + task_kind: row.job_task_kind ?? 'agent', + payload: row.job_payload ?? null, + // この join は latestJob 表示用。スペース解決は local_tasks.space_id を使うため + // ここでは job の space_id / runtime_dir を選択しておらず null で足りる。 + space_id: null, + runtime_dir: null, + created_at: row.job_created_at!, + updated_at: row.job_updated_at!, + }); + jobIds.push(row.job_id); + } else { + task.latestJob = null; + } + tasks.push(task); + } + + // 2. Single query for all sub-jobs + if (jobIds.length > 0) { + const placeholders = jobIds.map(() => '?').join(', '); + const subJobRows = db + .prepare(` + SELECT * FROM ( + SELECT j.*, ROW_NUMBER() OVER ( + PARTITION BY j.parent_job_id, j.issue_number + ORDER BY j.created_at DESC, j.rowid DESC + ) AS rn + FROM jobs j + WHERE j.parent_job_id IN (${placeholders}) + ) WHERE rn = 1 + ORDER BY parent_job_id, issue_number ASC + `) + .all(...jobIds) as JobRow[]; + + // Group sub-jobs by parent_job_id + const subJobsByParent = new Map(); + for (const row of subJobRows) { + const parentId = row.parent_job_id!; + if (!subJobsByParent.has(parentId)) { + subJobsByParent.set(parentId, []); + } + subJobsByParent.get(parentId)!.push(rowToJob(row)); + } + + // 3. Attach subtask info to tasks + for (const task of tasks) { + if (task.latestJob && subJobsByParent.has(task.latestJob.id)) { + const subJobs = subJobsByParent.get(task.latestJob.id)!; + task.subtasks = await jobsRepo.buildSubtaskInfos(db, subJobs); + task.subtaskCount = subJobs.length; + task.subtaskCompleted = subJobs.filter(j => + ['succeeded', 'failed', 'cancelled'].includes(j.status) + ).length; + } + } + } + + return tasks; + } + + + export async function updateLocalTask(db: Database.Database, taskId: number, updates: Partial>): Promise { + const setClauses: string[] = ["updated_at = datetime('now')"]; + const params: Record = { taskId }; + const fieldMap: Record = { + title: 'title', + titleSource: 'title_source', + body: 'body', + pieceName: 'piece_name', + profile: 'profile', + outputFormat: 'output_format', + askPolicy: 'ask_policy', + priority: 'priority', + state: 'state', + workspacePath: 'workspace_path', + runtimeDir: 'runtime_dir', + visibility: 'visibility', + visibilityScopeOrgId: 'visibility_scope_org_id', + }; + + for (const [jsKey, dbCol] of Object.entries(fieldMap)) { + const val = (updates as Record)[jsKey]; + if (val !== undefined) { + setClauses.push(`${dbCol} = @${jsKey}`); + params[jsKey] = val; + } + } + + if (setClauses.length === 1) return; + + db + .prepare(`UPDATE local_tasks SET ${setClauses.join(', ')} WHERE id = @taskId`) + .run(params); + } + + + export async function updateFeedback(db: Database.Database, taskId: number, feedback: { + rating: 'good' | 'bad'; + tags: string[]; + comment: string | null; + }): Promise { + db + .prepare(` + UPDATE local_tasks + SET feedback_rating = @rating, + feedback_tags = @tags, + feedback_comment = @comment, + feedback_at = datetime('now'), + updated_at = datetime('now') + WHERE id = @taskId + `) + .run({ + taskId, + rating: feedback.rating, + tags: JSON.stringify(feedback.tags), + comment: feedback.comment, + }); + } + + + export async function addLocalTaskComment(db: Database.Database, taskId: number, author: string, body: string, kind: string = 'comment', attachments: string[] = []): Promise { + const attachmentsJson = attachments.length > 0 ? JSON.stringify(attachments) : null; + const result = db + .prepare('INSERT INTO local_task_comments (task_id, author, kind, body, attachments) VALUES (?, ?, ?, ?, ?)') + .run(taskId, author, kind, body, attachmentsJson); + db + .prepare("UPDATE local_tasks SET updated_at = datetime('now') WHERE id = ?") + .run(taskId); + const row = db + .prepare('SELECT * FROM local_task_comments WHERE id = ?') + .get(Number(result.lastInsertRowid)) as LocalTaskCommentRow | undefined; + if (!row) throw new Error('addLocalTaskComment: failed to load inserted comment'); + taskSearchRepo.indexTaskComment(db, row.id, taskId, author, kind, row.created_at, body, attachmentsJson); + return rowToLocalTaskComment(row); + } + + + export async function listLocalTaskComments(db: Database.Database, taskId: number): Promise { + const rows = db + .prepare('SELECT * FROM local_task_comments WHERE task_id = ? ORDER BY created_at ASC, id ASC') + .all(taskId) as LocalTaskCommentRow[]; + return rows.map(rowToLocalTaskComment); + } + + + export async function getUninjectedComments(db: Database.Database, taskId: number, sinceId: number = 0): Promise { + const rows = db + .prepare( + `SELECT * FROM local_task_comments + WHERE task_id = ? AND id > ? AND author = 'user' AND injected_at IS NULL + ORDER BY id ASC` + ) + .all(taskId, sinceId) as LocalTaskCommentRow[]; + return rows.map(rowToLocalTaskComment); + } + + + export function markCommentsInjected(db: Database.Database, commentIds: number[]): void { + if (commentIds.length === 0) return; + const placeholders = commentIds.map(() => '?').join(','); + db + .prepare(`UPDATE local_task_comments SET injected_at = datetime('now') WHERE id IN (${placeholders})`) + .run(...commentIds); + } + + + /** + * Latest agent-authored "result" or "ask" comment for a task. Used by the + * piece handoff feature to surface the previous job's terminal output as + * context to a continuation job's LLM. Returns null when none exist + * (e.g., task has not yet completed any job). + */ + export async function getLatestResultComment(db: Database.Database, taskId: number): Promise<{ body: string; kind: string; createdAt: string } | null> { + const row = db + .prepare( + `SELECT body, kind, created_at + FROM local_task_comments + WHERE task_id = ? AND author = 'agent' AND kind IN ('result', 'ask') + ORDER BY created_at DESC + LIMIT 1` + ) + .get(taskId) as { body: string; kind: string; created_at: string } | undefined; + return row ? { body: row.body, kind: row.kind, createdAt: row.created_at } : null; + } + + + export async function deleteLocalTask(db: Database.Database, taskId: number, worktreeDir?: string): Promise { + const repoName = localTaskRepoName(taskId); + + // タスク存在確認 & workspace_path / space_id / mode / runtime_dir を取得(削除前に必要) + const taskRow = db + .prepare('SELECT workspace_path, space_id, workspace_mode, runtime_dir FROM local_tasks WHERE id = ?') + .get(taskId) as { + workspace_path: string | null; + space_id: string | null; + workspace_mode: string | null; + runtime_dir: string | null; + } | undefined; + if (!taskRow) { + throw new Error(`deleteLocalTask: task ${taskId} not found`); + } + + // running/dispatching なジョブがある場合は削除を拒否 + const activeJob = db + .prepare("SELECT id FROM jobs WHERE repo = ? AND status IN ('running', 'dispatching') LIMIT 1") + .get(repoName) as { id: string } | undefined; + if (activeJob) { + throw new Error(`deleteLocalTask: task ${taskId} has an active job (${activeJob.id})`); + } + + // DB 操作をトランザクションで実行 + const deleteTransaction = db.transaction(() => { + db.prepare('DELETE FROM issue_locks WHERE repo = ?').run(repoName); + db.prepare('DELETE FROM jobs WHERE repo = ?').run(repoName); + db.prepare('DELETE FROM task_comment_index WHERE task_id = ?').run(taskId); + db.prepare('DELETE FROM local_tasks WHERE id = ?').run(taskId); + }); + deleteTransaction(); + + // ワークスペース削除(DB トランザクション外 — ロールバック不可のため)。 + // persistent スペースタスクの workspace_path は **スペース共有ツリー** + // (`{worktreeDir}/space/{spaceId}/files`) で、同じスペースの全会話が共有する。 + // 1 会話の削除でこれを rm すると他会話の output/input まで巻き添えで消える(データ消失) + // ため、共有ツリーは削除しない(DB 行のみ削除)。ephemeral/local の per-task + // ワークスペースは従来どおり削除する。会話固有の runtime_dir + // (`space/{spaceId}/runs/{taskId}` = logs/checklists/raw) は per-会話なので掃除する。 + + // パスヘルパー: normalize(末尾区切り除去)/ 実体 realpath / 末尾セグメント一致。 + const norm = (p: string): string => normalize(p).replace(/[/\\]+$/, ''); + const realOf = (p: string): string => { + const n = norm(p); + try { return existsSync(n) ? realpathSync(n) : n; } catch { return n; } + }; + const hasTail = (path: string, tail: string[]): boolean => { + const segs = path.split(/[/\\]+/); + return segs.length >= tail.length && tail.every((t, i) => segs[segs.length - tail.length + i] === t); + }; + + // 削除は **ホワイトリスト + worktreeDir アンカー** 方式。「共有ツリー形を blacklist して + // 例外を追う」と symlink/normalization のたびに抜け道が増えるため、逆に **設定された + // worktreeDir 配下に root され、かつシステムが作る per-task 形** のものだけを消す。 + // 判定は logical と realpath の **両方** で行い、どちらかが worktree 外/別実体を指す + // symlink なら消さない(共有ツリーや worktree 外データを巻き込まない)。 + // worktreeDir が無い経路(legacy/一部テスト)は安全側に倒して fs 削除を一切しない。 + if (worktreeDir) { + const root = realOf(worktreeDir); + const rooted = (real: string): boolean => real === root || real.startsWith(root + sep); + + // workspace_path: per-task は ephemeral/{taskId} または旧 local/{taskId} のみ。共有ツリー + // (space/{id}/files)・worktree 外・symlink リダイレクトは保持。 + if (taskRow.workspace_path) { + const wsReal = realOf(taskRow.workspace_path); + const perTaskShape = (s: string): boolean => + hasTail(s, ['ephemeral', String(taskId)]) || hasTail(s, ['local', String(taskId)]); + if (rooted(wsReal) && perTaskShape(wsReal) && perTaskShape(norm(taskRow.workspace_path)) && existsSync(wsReal)) { + rmSync(wsReal, { recursive: true, force: true }); + } else { + logger.info(`Repository: deleteLocalTask kept non-per-task workspace ${taskRow.workspace_path} (task ${taskId})`); + } + } + + // runtime_dir: 会話固有 (space/<任意>/runs/{taskId})。worktree 配下に root され、logical/real + // 両方が runs/{taskId} 形のときだけ掃除(共有 files を指す symlink は real 形が一致せず除外)。 + if (taskRow.runtime_dir) { + const rtReal = realOf(taskRow.runtime_dir); + const runsShape = (s: string): boolean => + hasTail(s, ['runs', String(taskId)]) && s.split(/[/\\]+/).slice(-4)[0] === 'space'; + if (rooted(rtReal) && runsShape(rtReal) && runsShape(norm(taskRow.runtime_dir)) && existsSync(rtReal)) { + rmSync(rtReal, { recursive: true, force: true }); + } else { + logger.info(`Repository: deleteLocalTask kept runtime_dir ${taskRow.runtime_dir} (task ${taskId})`); + } + } + } + + logger.info(`Repository: deleted local task ${taskId}`); + } diff --git a/src/db/repositories/notifications.ts b/src/db/repositories/notifications.ts new file mode 100644 index 0000000..9fb82f5 --- /dev/null +++ b/src/db/repositories/notifications.ts @@ -0,0 +1,321 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// notifications repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { randomUUID } from 'crypto'; + +// ── Browser Notifications V2 (Web Push) ────────────────────────────── +// Spec: docs/superpowers/specs/2026-05-28-browser-notifications-v2-webpush.md + +export type NotifyEventType = 'running' | 'succeeded' | 'failed' | 'waiting_human'; + + +export interface PushSubscriptionRecord { + id: string; + userId: string; + endpoint: string; + p256dh: string; + auth: string; + userAgent: string | null; + vapidKeyId: string; + createdAt: string; + lastSuccessAt: string | null; + lastFailureAt: string | null; + failureCount: number; +} + + +export interface UpsertPushSubscriptionInput { + userId: string; + endpoint: string; + p256dh: string; + auth: string; + userAgent?: string | null; + vapidKeyId: string; +} + + +export interface NotificationPrefs { + userId: string; + enabled: boolean; + events: Record; + includeDetails: boolean; + v1Migrated: boolean; + updatedAt: string; +} + + +export type NotificationPrefsUpdate = Partial>; + + + // ── Browser Notifications V2: push_subscriptions ──────────────────── + // Spec: docs/superpowers/specs/2026-05-28-browser-notifications-v2-webpush.md + + /** + * Insert or, on endpoint collision, transfer ownership to the new user. + * endpoint is globally UNIQUE: the same browser logging in as a different + * user re-uses the same push service URL, so we move it rather than fail. + */ + export function upsertPushSubscription(db: Database.Database, input: UpsertPushSubscriptionInput): { id: string } { + const existing = db + .prepare('SELECT id FROM push_subscriptions WHERE endpoint = ?') + .get(input.endpoint) as { id: string } | undefined; + if (existing) { + db + .prepare( + `UPDATE push_subscriptions + SET user_id = ?, p256dh = ?, auth = ?, user_agent = ?, + vapid_key_id = ?, last_success_at = NULL, + last_failure_at = NULL, failure_count = 0 + WHERE id = ?`, + ) + .run( + input.userId, + input.p256dh, + input.auth, + input.userAgent ?? null, + input.vapidKeyId, + existing.id, + ); + return { id: existing.id }; + } + const id = randomUUID(); + db + .prepare( + `INSERT INTO push_subscriptions + (id, user_id, endpoint, p256dh, auth, user_agent, vapid_key_id) + VALUES (?, ?, ?, ?, ?, ?, ?)`, + ) + .run( + id, + input.userId, + input.endpoint, + input.p256dh, + input.auth, + input.userAgent ?? null, + input.vapidKeyId, + ); + return { id }; + } + + + export function listPushSubscriptionsForUser(db: Database.Database, userId: string): PushSubscriptionRecord[] { + const rows = db + .prepare( + `SELECT id, user_id, endpoint, p256dh, auth, user_agent, vapid_key_id, + created_at, last_success_at, last_failure_at, failure_count + FROM push_subscriptions + WHERE user_id = ? + ORDER BY created_at ASC`, + ) + .all(userId) as Array<{ + id: string; + user_id: string; + endpoint: string; + p256dh: string; + auth: string; + user_agent: string | null; + vapid_key_id: string; + created_at: string; + last_success_at: string | null; + last_failure_at: string | null; + failure_count: number; + }>; + return rows.map(rowToPushSubscription); + } + + + export function getPushSubscriptionById(db: Database.Database, id: string): PushSubscriptionRecord | null { + const row = db + .prepare( + `SELECT id, user_id, endpoint, p256dh, auth, user_agent, vapid_key_id, + created_at, last_success_at, last_failure_at, failure_count + FROM push_subscriptions WHERE id = ?`, + ) + .get(id) as Parameters[0] | undefined; + return row ? rowToPushSubscription(row) : null; + } + + + export function deletePushSubscription(db: Database.Database, id: string): void { + db.prepare('DELETE FROM push_subscriptions WHERE id = ?').run(id); + } + + + export function markPushSubscriptionSuccess(db: Database.Database, id: string): void { + db + .prepare( + `UPDATE push_subscriptions + SET last_success_at = datetime('now'), failure_count = 0 + WHERE id = ?`, + ) + .run(id); + } + + + export function markPushSubscriptionFailure(db: Database.Database, id: string): void { + db + .prepare( + `UPDATE push_subscriptions + SET last_failure_at = datetime('now'), + failure_count = failure_count + 1 + WHERE id = ?`, + ) + .run(id); + } + + + // ── Browser Notifications V2: user_notification_prefs ──────────────── + + /** + * Read per-user prefs; create row with defaults if none exists. + * Defaults: enabled=true, all events on, include_details=false, v1_migrated=false. + */ + export function getUserNotificationPrefs(db: Database.Database, userId: string): NotificationPrefs { + const row = db + .prepare( + `SELECT user_id, enabled, event_running, event_succeeded, + event_failed, event_waiting_human, include_details, + v1_migrated, updated_at + FROM user_notification_prefs WHERE user_id = ?`, + ) + .get(userId) as + | { + user_id: string; + enabled: number; + event_running: number; + event_succeeded: number; + event_failed: number; + event_waiting_human: number; + include_details: number; + v1_migrated: number; + updated_at: string; + } + | undefined; + if (!row) { + // Lazily create the default row so subsequent reads/updates are + // a simple UPDATE rather than a conditional insert. + db + .prepare( + `INSERT INTO user_notification_prefs (user_id) VALUES (?) + ON CONFLICT(user_id) DO NOTHING`, + ) + .run(userId); + return { + userId, + enabled: true, + events: { running: true, succeeded: true, failed: true, waiting_human: true }, + includeDetails: false, + v1Migrated: false, + updatedAt: new Date().toISOString(), + }; + } + return { + userId: row.user_id, + enabled: row.enabled !== 0, + events: { + running: row.event_running !== 0, + succeeded: row.event_succeeded !== 0, + failed: row.event_failed !== 0, + waiting_human: row.event_waiting_human !== 0, + }, + includeDetails: row.include_details !== 0, + v1Migrated: row.v1_migrated !== 0, + updatedAt: row.updated_at, + }; + } + + + export function upsertUserNotificationPrefs(db: Database.Database, userId: string, update: NotificationPrefsUpdate): void { + // Ensure a row exists first (lazy default creation matches getUserNotificationPrefs). + db + .prepare(`INSERT OR IGNORE INTO user_notification_prefs (user_id) VALUES (?)`) + .run(userId); + const sets: string[] = []; + const params: Array = []; + if (update.enabled !== undefined) { + sets.push('enabled = ?'); + params.push(update.enabled ? 1 : 0); + } + if (update.events) { + if (update.events.running !== undefined) { + sets.push('event_running = ?'); + params.push(update.events.running ? 1 : 0); + } + if (update.events.succeeded !== undefined) { + sets.push('event_succeeded = ?'); + params.push(update.events.succeeded ? 1 : 0); + } + if (update.events.failed !== undefined) { + sets.push('event_failed = ?'); + params.push(update.events.failed ? 1 : 0); + } + if (update.events.waiting_human !== undefined) { + sets.push('event_waiting_human = ?'); + params.push(update.events.waiting_human ? 1 : 0); + } + } + if (update.includeDetails !== undefined) { + sets.push('include_details = ?'); + params.push(update.includeDetails ? 1 : 0); + } + if (update.v1Migrated !== undefined) { + sets.push('v1_migrated = ?'); + params.push(update.v1Migrated ? 1 : 0); + } + if (sets.length === 0) return; + sets.push("updated_at = datetime('now')"); + params.push(userId); + db + .prepare(`UPDATE user_notification_prefs SET ${sets.join(', ')} WHERE user_id = ?`) + .run(...params); + } + + + /** + * One-time V1 (localStorage) → V2 (server) preferences migration marker. + * Returns true if this call performed the migration mark (caller should + * then apply the localStorage values via upsertUserNotificationPrefs). + * Returns false if already migrated (caller should treat as 409 conflict). + */ + export function markV1MigrationComplete(db: Database.Database, userId: string): boolean { + db + .prepare(`INSERT OR IGNORE INTO user_notification_prefs (user_id) VALUES (?)`) + .run(userId); + const result = db + .prepare( + `UPDATE user_notification_prefs + SET v1_migrated = 1, updated_at = datetime('now') + WHERE user_id = ? AND v1_migrated = 0`, + ) + .run(userId); + return result.changes > 0; + } + + +export function rowToPushSubscription(row: { + id: string; + user_id: string; + endpoint: string; + p256dh: string; + auth: string; + user_agent: string | null; + vapid_key_id: string; + created_at: string; + last_success_at: string | null; + last_failure_at: string | null; + failure_count: number; +}): PushSubscriptionRecord { + return { + id: row.id, + userId: row.user_id, + endpoint: row.endpoint, + p256dh: row.p256dh, + auth: row.auth, + userAgent: row.user_agent, + vapidKeyId: row.vapid_key_id, + createdAt: row.created_at, + lastSuccessAt: row.last_success_at, + lastFailureAt: row.last_failure_at, + failureCount: row.failure_count, + }; +} diff --git a/src/db/repositories/provenance.ts b/src/db/repositories/provenance.ts new file mode 100644 index 0000000..24e37f2 --- /dev/null +++ b/src/db/repositories/provenance.ts @@ -0,0 +1,178 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// provenance repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { escapeLike } from './shared.js'; + +/** DB row shape for workspace_file_provenance. */ +export interface WorkspaceFileProvenanceRow { + space_id: string | null; + workspace_path: string; + rel_path: string; + created_by_task_id: number | null; + created_by_job_id: string | null; + created_by_piece: string | null; + created_by_movement: string | null; + source_kind: string; + first_seen_at: string | null; + last_modified_by_task_id: number | null; + last_modified_by_job_id: string | null; + last_modified_at: string | null; + checksum: string | null; + note: string | null; +} + + +export function rowToFileProvenance( + row: WorkspaceFileProvenanceRow, +): import('../../engine/tools/core.js').FileProvenanceRecord { + return { + relPath: row.rel_path, + sourceKind: (row.source_kind as import('../../engine/tools/core.js').FileProvenanceSourceKind) ?? 'unknown', + createdByTaskId: row.created_by_task_id ?? null, + createdByJobId: row.created_by_job_id ?? null, + createdByPiece: row.created_by_piece ?? null, + createdByMovement: row.created_by_movement ?? null, + firstSeenAt: row.first_seen_at ?? null, + lastModifiedByTaskId: row.last_modified_by_task_id ?? null, + lastModifiedByJobId: row.last_modified_by_job_id ?? null, + lastModifiedAt: row.last_modified_at ?? null, + checksum: row.checksum ?? null, + note: row.note ?? null, + }; +} + + + // ── Workspace file provenance ──────────────────────────────────────────── + // Every read/write is scoped by workspace_path (the shared files-tree root), + // which is the isolation boundary: a query for workspace A can never surface + // workspace B's rows (and thus never another user's task lineage in a shared + // space). schema.sql / migrate.ts / initSchema keep the table in triple sync. + + /** + * Upsert a provenance row for one workspace file. + * - event 'create' with no existing row → inserts the creator identity. + * - existing row (any event) → updates only the last-modified fields, so the + * original creator + source_kind are preserved (Edit / overwrite semantics). + * - event 'modify' with no existing row → backfills a row for a file that + * pre-dates the ledger (source_kind=imported_existing, creator NULL). + */ + export function recordProvenance(db: Database.Database, params: { + workspacePath: string; + relPath: string; + event: 'create' | 'modify'; + sourceKind?: import('../../engine/tools/core.js').FileProvenanceSourceKind; + spaceId?: string | null; + taskId?: number | null; + jobId?: string | null; + piece?: string | null; + movement?: string | null; + checksum?: string | null; + note?: string | null; + }): void { + const now = new Date().toISOString(); + const taskId = params.taskId ?? null; + const jobId = params.jobId ?? null; + const checksum = params.checksum ?? null; + const existing = db + .prepare('SELECT id FROM workspace_file_provenance WHERE workspace_path = ? AND rel_path = ?') + .get(params.workspacePath, params.relPath) as { id: number } | undefined; + + if (existing) { + // Preserve creator + source_kind; only advance the last-modified pointer. + db + .prepare( + `UPDATE workspace_file_provenance + SET last_modified_by_task_id = ?, last_modified_by_job_id = ?, + last_modified_at = ?, checksum = COALESCE(?, checksum) + WHERE id = ?`, + ) + .run(taskId, jobId, now, checksum, existing.id); + return; + } + + // No row yet. A create event knows the creator; a modify event means the + // file pre-dates the ledger → backfill with an unknown creator. + const sourceKind = + params.event === 'create' ? params.sourceKind ?? 'unknown' : 'imported_existing'; + const createdTask = params.event === 'create' ? taskId : null; + const createdJob = params.event === 'create' ? jobId : null; + const createdPiece = params.event === 'create' ? params.piece ?? null : null; + const createdMovement = params.event === 'create' ? params.movement ?? null : null; + db + .prepare( + `INSERT INTO workspace_file_provenance + (space_id, workspace_path, rel_path, created_by_task_id, created_by_job_id, + created_by_piece, created_by_movement, source_kind, first_seen_at, + last_modified_by_task_id, last_modified_by_job_id, last_modified_at, checksum, note) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + ) + .run( + params.spaceId ?? null, + params.workspacePath, + params.relPath, + createdTask, + createdJob, + createdPiece, + createdMovement, + sourceKind, + now, + taskId, + jobId, + now, + checksum, + params.note ?? null, + ); + } + + + export function getProvenance(db: Database.Database, workspacePath: string, relPath: string): import('../../engine/tools/core.js').FileProvenanceRecord | null { + const row = db + .prepare('SELECT * FROM workspace_file_provenance WHERE workspace_path = ? AND rel_path = ?') + .get(workspacePath, relPath) as WorkspaceFileProvenanceRow | undefined; + return row ? rowToFileProvenance(row) : null; + } + + + export function listProvenance(db: Database.Database, workspacePath: string, filters?: import('../../engine/tools/core.js').FileProvenanceListFilters): import('../../engine/tools/core.js').FileProvenanceRecord[] { + const clauses = ['workspace_path = ?']; + const args: unknown[] = [workspacePath]; + if (filters?.pathPrefix) { + clauses.push('rel_path LIKE ? ESCAPE \'\\\''); + args.push(escapeLike(filters.pathPrefix) + '%'); + } + if (filters?.sourceKind) { + clauses.push('source_kind = ?'); + args.push(filters.sourceKind); + } + if (typeof filters?.createdByTaskId === 'number') { + clauses.push('created_by_task_id = ?'); + args.push(filters.createdByTaskId); + } + if (typeof filters?.lastModifiedByTaskId === 'number') { + clauses.push('last_modified_by_task_id = ?'); + args.push(filters.lastModifiedByTaskId); + } + if (filters?.includeUnknown === false) { + clauses.push("source_kind NOT IN ('unknown', 'imported_existing')"); + } + // Cap hard at 500 rows regardless of caller input (bounded output). + const limit = Math.min(Math.max(filters?.limit ?? 200, 1), 500); + const rows = db + .prepare( + `SELECT * FROM workspace_file_provenance + WHERE ${clauses.join(' AND ')} + ORDER BY rel_path ASC + LIMIT ?`, + ) + .all(...args, limit) as WorkspaceFileProvenanceRow[]; + return rows.map(rowToFileProvenance); + } + + + /** Construct a workspace-bound read IO for GetFileProvenance / ListWorkspaceFiles. */ + export function makeFileProvenanceIO(db: Database.Database, workspacePath: string): import('../../engine/tools/core.js').FileProvenanceIO { + return { + get: (relPath) => getProvenance(db, workspacePath, relPath), + list: (filters) => listProvenance(db, workspacePath, filters), + }; + } diff --git a/src/db/repositories/reflection.ts b/src/db/repositories/reflection.ts new file mode 100644 index 0000000..67bc160 --- /dev/null +++ b/src/db/repositories/reflection.ts @@ -0,0 +1,143 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// reflection repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; + + // ── Reflection piece-edit cooldown ────────────────────────────────────────── + + /** + * Records that the reflection pipeline wrote a new version of pieceName for + * userId. snapshotId ties the edit back to the snapshot that triggered it. + */ + export function recordPieceEdit(db: Database.Database, userId: string, pieceName: string, snapshotId: string): void { + db.prepare( + `INSERT INTO reflection_piece_edits (user_id, piece_name, snapshot_id, created_at) + VALUES (?, ?, ?, ?)` + ).run(userId, pieceName, snapshotId, Date.now()); + } + + + /** + * Returns the number of piece edits for (userId, pieceName) that occurred + * within the last sinceMs milliseconds. Used by the cooldown gate in + * piece-writer.ts to prevent over-editing the same piece. + */ + export function countRecentPieceEdits(db: Database.Database, userId: string, pieceName: string, sinceMs: number): number { + return (db.prepare( + `SELECT COUNT(*) AS c FROM reflection_piece_edits + WHERE user_id = ? AND piece_name = ? AND created_at > ?` + ).get(userId, pieceName, Date.now() - sinceMs) as { c: number }).c; + } + + + // ── Reflection metrics ─────────────────────────────────────────────────────── + + /** + * Insert one row into reflection_metrics, optionally bundling a + * reflection_piece_edits row in the same transaction. + * + * When pieceEdit is supplied the two inserts are wrapped in a single + * db.transaction() so the tables stay consistent even if the process + * crashes between them. + */ + export function recordReflectionRun(db: Database.Database, metric: { + reflection_job_id: string; + original_job_id: string | null; + user_id: string; + piece_name: string | null; + outcome: 'applied' | 'partial' | 'abstained' | 'rejected' | 'failed'; + memory_changes: number; + piece_edited: 0 | 1; + tokens_in: number; + tokens_out: number; + duration_ms: number; + }, pieceEdit?: { pieceName: string; snapshotId: string }): void { + const now = Date.now(); + const insertMetric = db.prepare(` + INSERT INTO reflection_metrics + (reflection_job_id, original_job_id, user_id, piece_name, outcome, + memory_changes, piece_edited, tokens_in, tokens_out, duration_ms, created_at) + VALUES + (@reflection_job_id, @original_job_id, @user_id, @piece_name, @outcome, + @memory_changes, @piece_edited, @tokens_in, @tokens_out, @duration_ms, @created_at) + `); + + if (pieceEdit) { + const insertEdit = db.prepare(` + INSERT INTO reflection_piece_edits (user_id, piece_name, snapshot_id, created_at) + VALUES (?, ?, ?, ?) + `); + db.transaction(() => { + insertMetric.run({ ...metric, created_at: now }); + insertEdit.run(metric.user_id, pieceEdit.pieceName, pieceEdit.snapshotId, now); + })(); + } else { + insertMetric.run({ ...metric, created_at: now }); + } + } + + + /** + * Convenience alias for callers that don't need the bundled pieceEdit path. + */ + export function recordReflectionMetric(db: Database.Database, row: { + reflection_job_id: string; + original_job_id: string | null; + user_id: string; + piece_name: string | null; + outcome: 'applied' | 'partial' | 'abstained' | 'rejected' | 'failed'; + memory_changes: number; + piece_edited: 0 | 1; + tokens_in: number; + tokens_out: number; + duration_ms: number; + }): void { + recordReflectionRun(db, row); + } + + + /** + * Aggregate reflection metrics for a user since sinceMs (epoch ms). + * Returns counts per outcome and totals for tokens + piece edits. + */ + export function aggregateReflectionMetrics(db: Database.Database, userId: string, sinceMs: number): { + applied: number; + partial: number; + abstained: number; + rejected: number; + failed: number; + tokensIn: number; + tokensOut: number; + pieceEdits: number; + totalRuns: number; + } { + const rows = db + .prepare( + `SELECT outcome, COUNT(*) AS cnt, + SUM(tokens_in) AS ti, SUM(tokens_out) AS to_, + SUM(piece_edited) AS pe + FROM reflection_metrics + WHERE user_id = ? AND created_at >= ? + GROUP BY outcome`, + ) + .all(userId, sinceMs) as Array<{ + outcome: string; + cnt: number; + ti: number; + to_: number; + pe: number; + }>; + + const result = { + applied: 0, partial: 0, abstained: 0, rejected: 0, failed: 0, + tokensIn: 0, tokensOut: 0, pieceEdits: 0, totalRuns: 0, + }; + for (const r of rows) { + const o = r.outcome as keyof Pick; + if (o in result) (result as Record)[o] = r.cnt; + result.tokensIn += r.ti ?? 0; + result.tokensOut += r.to_ ?? 0; + result.pieceEdits += r.pe ?? 0; + result.totalRuns += r.cnt; + } + return result; + } diff --git a/src/db/repositories/scheduled-tasks.ts b/src/db/repositories/scheduled-tasks.ts new file mode 100644 index 0000000..f587315 --- /dev/null +++ b/src/db/repositories/scheduled-tasks.ts @@ -0,0 +1,305 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// scheduled-tasks repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { buildVisibilityWhere } from '../../bridge/visibility.js'; +import { utc } from './shared.js'; + +export const SCHEDULED_TASK_DISPLAY_SELECT = ` + u.name AS owner_name, + COALESCE( + (SELECT MIN(org_name) FROM user_gitea_orgs WHERE org_id = st.visibility_scope_org_id), + (SELECT name FROM local_orgs WHERE id = st.visibility_scope_org_id) + ) AS visibility_scope_org_name +`.trim(); + +export const SCHEDULED_TASK_DISPLAY_JOIN = `LEFT JOIN users u ON u.id = st.owner_id`; + + +export type ScheduledTaskKind = 'agent' | 'script'; + + +export interface ScheduledTask { + id: number; + title: string | null; + body: string; + pieceName: string; + profile: string; + outputFormat: string; + cronExpression: string; + nextRunAt: string; + lastRunAt: string | null; + lastJobId: string | null; + isActive: boolean; + ownerId: string | null; + ownerName?: string | null; + visibility: 'private' | 'org' | 'public'; + visibilityScopeOrgId: string | null; + visibilityScopeOrgName?: string | null; + browserSessionProfileId?: number | null; + taskKind: ScheduledTaskKind; + scriptName: string | null; + scriptParams: string | null; // JSON-encoded object or null + /** 所属スペース。生成される local_task に継承される。null = owner 個人スペース。 */ + spaceId: string | null; + createdAt: string; + updatedAt: string; +} + + +export interface CreateScheduledTaskParams { + title?: string | null; + body: string; + pieceName?: string; + profile?: string; + outputFormat?: string; + cronExpression: string; + nextRunAt: string; + ownerId?: string | null; + visibility?: 'private' | 'org' | 'public'; + visibilityScopeOrgId?: string | null; + browserSessionProfileId?: number | null; + taskKind?: ScheduledTaskKind; + scriptName?: string | null; + scriptParams?: string | null; + spaceId?: string | null; +} + + +export interface UpdateScheduledTaskParams { + title?: string; + body?: string; + pieceName?: string; + profile?: string; + outputFormat?: string; + cronExpression?: string; + nextRunAt?: string; + lastRunAt?: string; + lastJobId?: string; + isActive?: boolean; + visibility?: 'private' | 'org' | 'public'; + visibilityScopeOrgId?: string | null; + browserSessionProfileId?: number | null; + taskKind?: ScheduledTaskKind; + scriptName?: string | null; + scriptParams?: string | null; +} + + + // ── Scheduled Tasks ────────────────────────────────────────── + + export function mapScheduledTask(row: any): ScheduledTask { + const rawVisibility = row.visibility; + const visibility: ScheduledTask['visibility'] = + rawVisibility === 'org' || rawVisibility === 'public' ? rawVisibility : 'private'; + return { + id: row.id, + title: row.title, + body: row.body, + pieceName: row.piece_name, + profile: row.profile, + outputFormat: row.output_format, + cronExpression: row.cron_expression, + nextRunAt: utc(row.next_run_at), + lastRunAt: utc(row.last_run_at), + lastJobId: row.last_job_id, + isActive: row.is_active === 1, + ownerId: row.owner_id ?? null, + ownerName: row.owner_name ?? null, + visibility, + visibilityScopeOrgId: row.visibility_scope_org_id ?? null, + visibilityScopeOrgName: row.visibility_scope_org_name ?? null, + browserSessionProfileId: row.browser_session_profile_id ?? null, + taskKind: row.task_kind === 'script' ? 'script' : 'agent', + scriptName: row.script_name ?? null, + scriptParams: row.script_params ?? null, + spaceId: row.space_id ?? null, + createdAt: utc(row.created_at), + updatedAt: utc(row.updated_at), + }; + } + + + export async function createScheduledTask(db: Database.Database, params: CreateScheduledTaskParams): Promise { + const result = db + .prepare( + `INSERT INTO scheduled_tasks (title, body, piece_name, profile, output_format, cron_expression, next_run_at, owner_id, visibility, visibility_scope_org_id, browser_session_profile_id, task_kind, script_name, script_params, space_id) + VALUES (@title, @body, @pieceName, @profile, @outputFormat, @cronExpression, @nextRunAt, @ownerId, @visibility, @visibilityScopeOrgId, @browserSessionProfileId, @taskKind, @scriptName, @scriptParams, @spaceId)` + ) + .run({ + title: params.title ?? null, + body: params.body, + pieceName: params.pieceName ?? 'auto', + profile: params.profile ?? 'auto', + outputFormat: params.outputFormat ?? 'markdown', + cronExpression: params.cronExpression, + nextRunAt: params.nextRunAt, + ownerId: params.ownerId ?? null, + visibility: params.visibility ?? 'private', + visibilityScopeOrgId: params.visibilityScopeOrgId ?? null, + browserSessionProfileId: params.browserSessionProfileId ?? null, + taskKind: params.taskKind ?? 'agent', + scriptName: params.scriptName ?? null, + scriptParams: params.scriptParams ?? null, + spaceId: params.spaceId ?? null, + }); + const task = getScheduledTaskSync(db, Number(result.lastInsertRowid)); + if (!task) throw new Error('createScheduledTask: failed to load inserted task'); + return task; + } + + + export function getScheduledTaskSync(db: Database.Database, id: number): ScheduledTask | null { + const row = db + .prepare(` + SELECT st.*, + ${SCHEDULED_TASK_DISPLAY_SELECT} + FROM scheduled_tasks st + ${SCHEDULED_TASK_DISPLAY_JOIN} + WHERE st.id = ? + `) + .get(id) as any; + return row ? mapScheduledTask(row) : null; + } + + + export async function getScheduledTask(db: Database.Database, id: number, opts?: { viewer?: Express.User }): Promise { + const viewerClause = opts?.viewer + ? buildVisibilityWhere(opts.viewer, 'st', { spaceColumn: 'space_id' }) + : { clause: '1=1', params: [] as unknown[] }; + const row = db + .prepare(` + SELECT st.*, + ${SCHEDULED_TASK_DISPLAY_SELECT} + FROM scheduled_tasks st + ${SCHEDULED_TASK_DISPLAY_JOIN} + WHERE st.id = ? AND ${viewerClause.clause} + `) + .get(id, ...viewerClause.params) as any; + return row ? mapScheduledTask(row) : null; + } + + + export async function listScheduledTasks(db: Database.Database, filter?: { viewer?: Express.User; spaceId?: string | null }): Promise { + const conditions: string[] = []; + const queryParams: unknown[] = []; + if (filter?.viewer) { + const w = buildVisibilityWhere(filter.viewer, 'st', { spaceColumn: 'space_id' }); + conditions.push(w.clause); + queryParams.push(...w.params); + } + // Optional per-space scoping (the space's "Schedules" tab). null = only the + // space-less (personal) schedules; a string = exactly that space. + if (filter?.spaceId !== undefined) { + if (filter.spaceId === null) { + conditions.push('st.space_id IS NULL'); + } else { + conditions.push('st.space_id = ?'); + queryParams.push(filter.spaceId); + } + } + const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; + const rows = db + .prepare(` + SELECT st.*, + ${SCHEDULED_TASK_DISPLAY_SELECT} + FROM scheduled_tasks st + ${SCHEDULED_TASK_DISPLAY_JOIN} + ${whereClause} + ORDER BY st.created_at DESC + `) + .all(...queryParams) as any[]; + return rows.map(r => mapScheduledTask(r)); + } + + + /** + * due なスケジュールタスクをアトミックに claim して返す。 + * BEGIN IMMEDIATE で書き込みロックを即座に取得し、 + * 他のスケジューラーインスタンスとの重複実行を防止する。 + * claim されたタスクは next_run_at が遠い未来に設定されるため、 + * 他インスタンスに再取得されない。 + * 呼び出し側が実行後に正しい next_run_at を再設定する。 + */ + export async function getScheduledTasksDue(db: Database.Database): Promise { + // 十分遠い未来(claim マーカー) + const claimMarker = '9999-12-31 23:59:59'; + + // BEGIN IMMEDIATE: トランザクション開始時に RESERVED ロックを取得し、 + // 他の書き込みトランザクションとの競合を防ぐ + const txn = db.transaction(() => { + const rows = db + .prepare( + `SELECT * FROM scheduled_tasks + WHERE is_active = 1 AND next_run_at <= datetime('now') + ORDER BY next_run_at ASC` + ) + .all() as any[]; + + if (rows.length === 0) return []; + + // claim: next_run_at を遠い未来に設定して他インスタンスからの重複取得を防止 + const ids = rows.map((r: any) => r.id); + db + .prepare( + `UPDATE scheduled_tasks + SET next_run_at = ?, updated_at = datetime('now') + WHERE id IN (${ids.map(() => '?').join(',')})` + ) + .run(claimMarker, ...ids); + + return rows; + }); + + const rows = txn.immediate(); + return rows.map((r: any) => mapScheduledTask(r)); + } + + + export async function updateScheduledTask(db: Database.Database, id: number, params: UpdateScheduledTaskParams): Promise { + const sets: string[] = []; + const values: Record = { id }; + + if (params.title !== undefined) { sets.push('title = @title'); values.title = params.title; } + if (params.body !== undefined) { sets.push('body = @body'); values.body = params.body; } + if (params.pieceName !== undefined) { sets.push('piece_name = @pieceName'); values.pieceName = params.pieceName; } + if (params.profile !== undefined) { sets.push('profile = @profile'); values.profile = params.profile; } + if (params.outputFormat !== undefined) { sets.push('output_format = @outputFormat'); values.outputFormat = params.outputFormat; } + if (params.cronExpression !== undefined) { sets.push('cron_expression = @cronExpression'); values.cronExpression = params.cronExpression; } + if (params.nextRunAt !== undefined) { sets.push('next_run_at = @nextRunAt'); values.nextRunAt = params.nextRunAt; } + if (params.lastRunAt !== undefined) { sets.push('last_run_at = @lastRunAt'); values.lastRunAt = params.lastRunAt; } + if (params.lastJobId !== undefined) { sets.push('last_job_id = @lastJobId'); values.lastJobId = params.lastJobId; } + if (params.isActive !== undefined) { sets.push('is_active = @isActive'); values.isActive = params.isActive ? 1 : 0; } + if (params.visibility !== undefined) { sets.push('visibility = @visibility'); values.visibility = params.visibility; } + if (params.visibilityScopeOrgId !== undefined) { + sets.push('visibility_scope_org_id = @visibilityScopeOrgId'); + values.visibilityScopeOrgId = params.visibilityScopeOrgId; + } + if (params.browserSessionProfileId !== undefined) { + sets.push('browser_session_profile_id = @browserSessionProfileId'); + values.browserSessionProfileId = params.browserSessionProfileId; + } + if (params.taskKind !== undefined) { + sets.push('task_kind = @taskKind'); + values.taskKind = params.taskKind; + } + if (params.scriptName !== undefined) { + sets.push('script_name = @scriptName'); + values.scriptName = params.scriptName; + } + if (params.scriptParams !== undefined) { + sets.push('script_params = @scriptParams'); + values.scriptParams = params.scriptParams; + } + + if (sets.length === 0) return getScheduledTaskSync(db, id); + + sets.push("updated_at = datetime('now')"); + db.prepare(`UPDATE scheduled_tasks SET ${sets.join(', ')} WHERE id = @id`).run(values); + return getScheduledTaskSync(db, id); + } + + + export async function deleteScheduledTask(db: Database.Database, id: number): Promise { + const result = db.prepare('DELETE FROM scheduled_tasks WHERE id = ?').run(id); + return result.changes > 0; + } diff --git a/src/db/repositories/schema.ts b/src/db/repositories/schema.ts new file mode 100644 index 0000000..bdeb491 --- /dev/null +++ b/src/db/repositories/schema.ts @@ -0,0 +1,538 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// schema repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { readFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { logger } from '../../logger.js'; +import { ensureTaskCommentFts } from '../migrate.js'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + + export function initSchema(db: Database.Database): void { + runPreSchemaCompatibilityMigrations(db); + const schemaPath = join(__dirname, '..', 'schema.sql'); + const schema = readFileSync(schemaPath, 'utf-8'); + db.exec(schema); + ensureColumn(db, 'jobs', 'required_profile', "TEXT NOT NULL DEFAULT 'auto'"); + ensureColumn(db, 'jobs', 'task_class', "TEXT NOT NULL DEFAULT 'auto'"); + ensureColumn(db, 'worker_nodes', 'profile_tags', "TEXT NOT NULL DEFAULT ',auto,'"); + ensureColumn(db, 'worker_nodes', 'task_class_tags', "TEXT NOT NULL DEFAULT ',auto,'"); + ensureColumn(db, 'worker_nodes', 'available_models', 'TEXT'); + ensureColumn(db, 'worker_nodes', 'max_concurrency', 'INTEGER NOT NULL DEFAULT 1'); + ensureColumn(db, 'worker_nodes', 'last_error', 'TEXT'); + db.exec("CREATE INDEX IF NOT EXISTS idx_jobs_profile_task_class ON jobs (status, required_profile, task_class)"); + db.exec("CREATE INDEX IF NOT EXISTS idx_worker_nodes_health ON worker_nodes (enabled, healthy, last_seen_at)"); + ensureColumn(db, 'jobs', 'parent_job_id', 'TEXT'); + ensureColumn(db, 'jobs', 'subtask_depth', 'INTEGER NOT NULL DEFAULT 0'); + ensureColumn(db, 'jobs', 'wait_reason', 'TEXT'); + ensureColumn(db, 'jobs', 'continued_from_job_id', 'TEXT'); + // Phase A (multi-team GPU pool): physical backend id assigned when + // the worker is a proxy. Sticky once set; never overwritten mid-job. + ensureColumn(db, 'jobs', 'last_backend_id', 'TEXT'); + db.exec("CREATE INDEX IF NOT EXISTS idx_jobs_parent_job_id ON jobs (parent_job_id)"); + ensureColumn(db, 'local_tasks', 'feedback_rating', 'TEXT'); + ensureColumn(db, 'local_tasks', 'feedback_comment', 'TEXT'); + ensureColumn(db, 'local_tasks', 'feedback_tags', 'TEXT'); + ensureColumn(db, 'local_tasks', 'feedback_at', 'TEXT'); + migrateWaitingSubtasksStatus(db); + // Auth migrations: owner_id columns + ensureColumn(db, 'jobs', 'owner_id', 'TEXT'); + ensureColumn(db, 'local_tasks', 'owner_id', 'TEXT'); + // #142: 実行中アクティビティ表示 + ensureColumn(db, 'jobs', 'current_activity', 'TEXT'); + // abortReason 細分化: agent-loop / piece-runner が出す構造化 abort code を保持 + ensureColumn(db, 'jobs', 'abort_reason', 'TEXT'); + // #134: 共有機能 + ensureColumn(db, 'local_tasks', 'share_token', 'TEXT'); + ensureColumn(db, 'local_tasks', 'shared_at', 'TEXT'); + db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_local_tasks_share_token ON local_tasks (share_token)"); + + // Ownership and visibility columns (3 tables) + for (const table of ['local_tasks', 'scheduled_tasks', 'jobs']) { + ensureColumn(db, table, 'owner_id', 'TEXT'); + ensureColumn(db, table, 'visibility', "TEXT NOT NULL DEFAULT 'private'"); + ensureColumn(db, table, 'visibility_scope_org_id', 'TEXT'); + } + + // User preferences + ensureColumn(db, 'users', 'default_visibility', "TEXT NOT NULL DEFAULT 'private'"); + ensureColumn(db, 'users', 'default_visibility_org_id', 'TEXT'); + + // Indexes and user_gitea_orgs table + db.exec(` + CREATE INDEX IF NOT EXISTS idx_local_tasks_owner_id ON local_tasks(owner_id); + CREATE INDEX IF NOT EXISTS idx_local_tasks_visibility ON local_tasks(visibility, visibility_scope_org_id); + CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_owner_id ON scheduled_tasks(owner_id); + CREATE INDEX IF NOT EXISTS idx_jobs_owner_id ON jobs(owner_id); + CREATE INDEX IF NOT EXISTS idx_jobs_visibility ON jobs(visibility, visibility_scope_org_id); + + CREATE TABLE IF NOT EXISTS user_gitea_orgs ( + user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + org_id TEXT NOT NULL, + org_name TEXT NOT NULL, + fetched_at TEXT NOT NULL DEFAULT (datetime('now')), + PRIMARY KEY (user_id, org_id) + ); + CREATE INDEX IF NOT EXISTS idx_user_gitea_orgs_org_id ON user_gitea_orgs(org_id); + `); + + // スペース基盤(計画1)。schema.sql / migrate.ts と三重ミラー。 + db.exec(` + CREATE TABLE IF NOT EXISTS spaces ( + id TEXT PRIMARY KEY, + kind TEXT NOT NULL DEFAULT 'case', + title TEXT NOT NULL, + description TEXT NOT NULL DEFAULT '', + owner_id TEXT, + visibility TEXT NOT NULL DEFAULT 'private', + visibility_scope_org_id TEXT, + status TEXT NOT NULL DEFAULT 'open', + brand_color TEXT, + workspace_dir TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE INDEX IF NOT EXISTS idx_spaces_owner ON spaces(owner_id); + CREATE INDEX IF NOT EXISTS idx_spaces_visibility ON spaces(visibility, visibility_scope_org_id); + CREATE UNIQUE INDEX IF NOT EXISTS idx_spaces_personal_owner ON spaces(owner_id) WHERE kind = 'personal'; + `); + ensureColumn(db, 'local_tasks', 'space_id', 'TEXT'); + ensureColumn(db, 'local_tasks', 'workspace_mode', "TEXT NOT NULL DEFAULT 'persistent'"); + ensureColumn(db, 'jobs', 'space_id', 'TEXT'); + ensureColumn(db, 'scheduled_tasks', 'space_id', 'TEXT'); + + // タスク横断検索用の会話コメント索引(通常テーブルのみ)。schema.sql / migrate.ts と三重ミラー。 + // FTS5 仮想テーブル+同期トリガは src/db/migrate.ts の ensureTaskCommentFts が単一定義箇所として作成する + // (runMigrations() を経由しない bare `new Repository()` 構築でも FTS が確実に存在するように、ここからも呼ぶ)。 + db.exec(` + CREATE TABLE IF NOT EXISTS task_comment_index ( + comment_id INTEGER PRIMARY KEY, + task_id INTEGER NOT NULL, + author TEXT, + kind TEXT, + created_at TEXT, + text TEXT NOT NULL + ); + CREATE INDEX IF NOT EXISTS idx_tci_task ON task_comment_index(task_id); + `); + ensureTaskCommentFts(db); + + // スペース・メンバー(協働者)。schema.sql / migrate.ts と三重ミラー。 + // owner は members 行を持たず owner_id で判定する。 + db.exec(` + CREATE TABLE IF NOT EXISTS space_members ( + space_id TEXT NOT NULL, + user_id TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'editor' CHECK (role IN ('owner','editor','viewer')), + invited_by TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + PRIMARY KEY (space_id, user_id), + FOREIGN KEY (space_id) REFERENCES spaces(id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ); + CREATE INDEX IF NOT EXISTS idx_space_members_user ON space_members (user_id); + `); + + // スペース・カレンダー(予定)。schema.sql / migrate.ts と三重ミラー。 + db.exec(` + CREATE TABLE IF NOT EXISTS calendar_events ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + space_id TEXT NOT NULL, + owner_id TEXT, + date TEXT NOT NULL, + end_date TEXT, + time TEXT, + end_time TEXT, + title TEXT NOT NULL, + description TEXT, + created_by TEXT NOT NULL DEFAULT 'user', + source_task_id INTEGER, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE INDEX IF NOT EXISTS idx_calendar_events_space_date ON calendar_events (space_id, date); + `); + + // スペース招待リンク(再利用トークン)。schema.sql / migrate.ts と三重ミラー。 + db.exec(` + CREATE TABLE IF NOT EXISTS space_invites ( + token TEXT PRIMARY KEY, + space_id TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'viewer' CHECK (role IN ('editor','viewer')), + created_by TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + expires_at TEXT, + revoked_at TEXT, + FOREIGN KEY (space_id) REFERENCES spaces(id) ON DELETE CASCADE + ); + CREATE INDEX IF NOT EXISTS idx_space_invites_space ON space_invites (space_id); + `); + // 公開アプリ共有リンク(read-only)。schema.sql / migrate.ts と三重ミラー。 + db.exec(` + CREATE TABLE IF NOT EXISTS app_share_links ( + token TEXT PRIMARY KEY, + space_id TEXT NOT NULL, + app_name TEXT NOT NULL, + created_by TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + revoked_at TEXT, + FOREIGN KEY (space_id) REFERENCES spaces(id) ON DELETE CASCADE + ); + CREATE INDEX IF NOT EXISTS idx_app_share_links_space_app + ON app_share_links(space_id, app_name); + `); + // ツール要求の記録(RequestTool 能動申告 / 未許可ツール呼び出しの受動捕捉)。 + // schema.sql / migrate.ts と三重ミラー。 + db.exec(` + CREATE TABLE IF NOT EXISTS tool_requests ( + id TEXT PRIMARY KEY, + task_id TEXT, + job_id TEXT, + space_id TEXT, + piece_name TEXT NOT NULL, + movement_name TEXT NOT NULL, + tool_name TEXT NOT NULL, + reason TEXT, + category TEXT NOT NULL DEFAULT 'requested', + status TEXT NOT NULL DEFAULT 'pending', + grant_scope TEXT, + decided_by TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + decided_at TEXT + ); + CREATE INDEX IF NOT EXISTS idx_tool_requests_task ON tool_requests (task_id); + CREATE INDEX IF NOT EXISTS idx_tool_requests_piece ON tool_requests (piece_name); + CREATE INDEX IF NOT EXISTS idx_tool_requests_dedup ON tool_requests (job_id, movement_name, tool_name, status); + `); + // エージェント発 Python パッケージ要求(RequestPackage)。承認で PR2 の + // installSpacePackages を再利用しスペース overlay へ install する。 + // schema.sql / migrate.ts と三重ミラー。 + db.exec(` + CREATE TABLE IF NOT EXISTS package_requests ( + id TEXT PRIMARY KEY, + task_id TEXT, + job_id TEXT, + space_id TEXT, + piece_name TEXT, + movement_name TEXT, + spec TEXT NOT NULL, + normalized_name TEXT NOT NULL, + reason TEXT, + status TEXT NOT NULL DEFAULT 'pending', + decided_by TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + decided_at TEXT + ); + CREATE INDEX IF NOT EXISTS idx_package_requests_task ON package_requests (task_id); + CREATE INDEX IF NOT EXISTS idx_package_requests_dedup ON package_requests (job_id, normalized_name, status); + `); + // ワークスペースファイル来歴台帳。schema.sql / migrate.ts と三重ミラー。 + db.exec(` + CREATE TABLE IF NOT EXISTS workspace_file_provenance ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + space_id TEXT, + workspace_path TEXT NOT NULL, + rel_path TEXT NOT NULL, + created_by_task_id INTEGER, + created_by_job_id TEXT, + created_by_piece TEXT, + created_by_movement TEXT, + source_kind TEXT NOT NULL, + first_seen_at TEXT NOT NULL, + last_modified_by_task_id INTEGER, + last_modified_by_job_id TEXT, + last_modified_at TEXT, + checksum TEXT, + note TEXT, + UNIQUE(workspace_path, rel_path) + ); + CREATE INDEX IF NOT EXISTS idx_wsfile_prov_ws ON workspace_file_provenance (workspace_path); + CREATE INDEX IF NOT EXISTS idx_wsfile_prov_created ON workspace_file_provenance (workspace_path, created_by_task_id); + `); + // Per-space isolation for MCP servers / SSH connections (spaces foundation). + // NULL = global/legacy (resolved globally; Phase 1 is a no-op, no consumer + // reads this yet). Triple-path mirror: schema.sql + migrate.ts. + ensureColumn(db, 'mcp_servers', 'space_id', 'TEXT'); + ensureColumn(db, 'ssh_connections', 'space_id', 'TEXT'); + // workstream 2: ブラウザセッションプロファイルの per-space 化。owner_id は復号 + // DEK のため保持。Triple-path mirror: schema.sql + migrate.ts。 + ensureColumn(db, 'browser_session_profiles', 'space_id', 'TEXT'); + // 計画5: 実行ログ root。NULL = 後方互換で workspace_path/logs に解決。 + ensureColumn(db, 'local_tasks', 'runtime_dir', 'TEXT'); + ensureColumn(db, 'jobs', 'runtime_dir', 'TEXT'); + // Tool-request mechanism: per-task grant overlay (JSON string[]). + ensureColumn(db, 'local_tasks', 'granted_tools', 'TEXT'); + + // Browser session persistence (2026-05) — keep in sync with schema.sql + db.exec(` + CREATE TABLE IF NOT EXISTS user_deks ( + user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, + encrypted_dek BLOB NOT NULL, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE TABLE IF NOT EXISTS browser_session_profiles ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + owner_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + space_id TEXT, + label TEXT NOT NULL, + start_url TEXT NOT NULL, + match_patterns TEXT NOT NULL DEFAULT '[]', + storage_origins TEXT NOT NULL DEFAULT '[]', + logged_in_selector TEXT, + login_url_patterns TEXT NOT NULL DEFAULT '[]', + encrypted_state_blob BLOB, + state_version INTEGER NOT NULL DEFAULT 0, + playwright_version TEXT, + status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','active','expired','revoked','error')), + last_saved_at TEXT, + last_used_at TEXT, + last_validated_at TEXT, + last_error TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE INDEX IF NOT EXISTS idx_bsp_owner ON browser_session_profiles(owner_id); + CREATE INDEX IF NOT EXISTS idx_bsp_space ON browser_session_profiles(space_id); + -- audit log: intentionally no FK — must survive deletion of referenced rows + CREATE TABLE IF NOT EXISTS browser_session_audit ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + ts TEXT NOT NULL DEFAULT (datetime('now')), + actor_user_id TEXT, + profile_id INTEGER, + owner_id TEXT, + action TEXT NOT NULL CHECK (action IN ('create','save','decrypt','use','delete','expire','revoke','test','login_start','login_cancel')), + task_id INTEGER, + job_id TEXT, + result TEXT NOT NULL CHECK (result IN ('success','error')), + reason TEXT + ); + CREATE INDEX IF NOT EXISTS idx_bsa_profile ON browser_session_audit(profile_id); + CREATE INDEX IF NOT EXISTS idx_bsa_actor ON browser_session_audit(actor_user_id); + `); + + ensureColumn(db, 'local_tasks', 'browser_session_profile_id', 'INTEGER REFERENCES browser_session_profiles(id) ON DELETE SET NULL'); + ensureColumn(db, 'scheduled_tasks', 'browser_session_profile_id', 'INTEGER REFERENCES browser_session_profiles(id) ON DELETE SET NULL'); + ensureColumn(db, 'jobs', 'browser_session_profile_id', 'INTEGER REFERENCES browser_session_profiles(id) ON DELETE SET NULL'); + + // E: scheduled_tasks can now run a user script directly (without going + // through the agent / LLM loop). task_kind='agent' (default) keeps the + // pre-existing behavior; task_kind='script' uses script_name + script_params. + ensureColumn(db, 'scheduled_tasks', 'task_kind', "TEXT NOT NULL DEFAULT 'agent' CHECK (task_kind IN ('agent','script'))"); + ensureColumn(db, 'scheduled_tasks', 'script_name', 'TEXT'); + ensureColumn(db, 'scheduled_tasks', 'script_params', 'TEXT'); // JSON-encoded object or NULL + + // F: reflection jobs — task_kind distinguishes agent jobs from reflection + // jobs that run the self-improving-memory pipeline (no LLM task loop). + // payload carries JSON inputs (scope, trigger metadata, etc.). + ensureColumn(db, 'jobs', 'task_kind', "TEXT NOT NULL DEFAULT 'agent'"); + ensureColumn(db, 'jobs', 'payload', 'TEXT'); + + // G: reflection piece-edit cooldown tracking. + // reflection_piece_edits records each time the reflection pipeline writes + // a user's custom piece. The cooldown gate in piece-writer.ts queries + // countRecentPieceEdits to rate-limit piece rewrites. + db.exec(` + CREATE TABLE IF NOT EXISTS reflection_piece_edits ( + user_id TEXT NOT NULL, + piece_name TEXT NOT NULL, + snapshot_id TEXT NOT NULL, + created_at INTEGER NOT NULL, + PRIMARY KEY (user_id, piece_name, created_at) + ); + CREATE INDEX IF NOT EXISTS idx_rpe_user_piece_time + ON reflection_piece_edits (user_id, piece_name, created_at DESC); + `); + + // The Side Info Panel dashboard widget feature was removed (2026-06); + // drop the legacy per-user widget table if an older DB still carries it. + db.exec(`DROP TABLE IF EXISTS user_dashboard_widgets;`); + + // H: reflection_metrics — one row per reflection job, records outcome, + // token usage, memory changes, and whether a piece edit was applied. + // Used by the /api/reflection/metrics endpoint (Phase 7.2) and future + // per-user budget enforcement (Phase 8.2). + db.exec(` + CREATE TABLE IF NOT EXISTS reflection_metrics ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + reflection_job_id TEXT NOT NULL, + original_job_id TEXT, + user_id TEXT NOT NULL, + piece_name TEXT, + outcome TEXT NOT NULL, + memory_changes INTEGER NOT NULL DEFAULT 0, + piece_edited INTEGER NOT NULL DEFAULT 0, + tokens_in INTEGER NOT NULL DEFAULT 0, + tokens_out INTEGER NOT NULL DEFAULT 0, + duration_ms INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL + ); + CREATE INDEX IF NOT EXISTS idx_rm_user_time + ON reflection_metrics (user_id, created_at DESC); + `); + + // I: AAO Gateway Phase 2a — DB-backed virtual keys. + // Mirrors src/db/schema.sql and migrateGatewayVirtualKeys in migrate.ts; + // all three paths must stay in sync (project_db_migration_dual_path). + db.exec(` + CREATE TABLE IF NOT EXISTS gateway_virtual_keys ( + id TEXT PRIMARY KEY, + key_hash TEXT NOT NULL UNIQUE, + key_prefix TEXT NOT NULL, + team TEXT NOT NULL, + allowed_models TEXT, + source TEXT NOT NULL DEFAULT 'admin' CHECK (source IN ('admin','config-import')), + created_at TEXT NOT NULL, + created_by TEXT, + revoked_at TEXT, + revoked_by TEXT, + last_used_at TEXT + ); + CREATE INDEX IF NOT EXISTS idx_gateway_keys_hash_active + ON gateway_virtual_keys (key_hash) + WHERE revoked_at IS NULL; + CREATE INDEX IF NOT EXISTS idx_gateway_keys_team + ON gateway_virtual_keys (team); + `); + + // I.b: AAO Gateway Phase 2b — budget / rate limit columns + + // gateway_key_usage table. Mirrors schema.sql + migrate.ts and uses + // the same PRAGMA-based idempotency pattern. + ensureColumn(db, 'gateway_virtual_keys', 'tokens_budget', 'INTEGER'); + ensureColumn(db, 'gateway_virtual_keys', 'rate_limit_rpm', 'INTEGER'); + db.exec(` + CREATE TABLE IF NOT EXISTS gateway_key_usage ( + key_id TEXT NOT NULL REFERENCES gateway_virtual_keys(id) ON DELETE CASCADE, + period_start TEXT NOT NULL, + tokens_in INTEGER NOT NULL DEFAULT 0, + tokens_out INTEGER NOT NULL DEFAULT 0, + requests INTEGER NOT NULL DEFAULT 0, + last_updated_at TEXT NOT NULL, + PRIMARY KEY (key_id, period_start) + ); + CREATE INDEX IF NOT EXISTS idx_gateway_usage_key + ON gateway_key_usage (key_id); + `); + + // Per-user daily LLM usage (gateway + direct). Mirrors schema.sql + + // migrate.ts (dual-path rule). Separate lens from gateway_key_usage. + // Spec: docs/superpowers/specs/2026-06-11-llm-usage-aggregation-design.md + db.exec(` + CREATE TABLE IF NOT EXISTS llm_usage_daily ( + day TEXT NOT NULL, + user_id TEXT NOT NULL, + source TEXT NOT NULL, + model TEXT NOT NULL, + route TEXT NOT NULL, + tokens_in INTEGER NOT NULL DEFAULT 0, + tokens_out INTEGER NOT NULL DEFAULT 0, + requests INTEGER NOT NULL DEFAULT 0, + last_updated_at TEXT NOT NULL, + PRIMARY KEY (day, user_id, source, model, route) + ); + CREATE INDEX IF NOT EXISTS idx_llm_usage_daily_user_day + ON llm_usage_daily (user_id, day); + `); + + // Usage dashboard v2: hour-grain ledger (supersedes llm_usage_daily as the + // write target; daily kept as frozen archive). Mirrors schema.sql + + // migrate.ts (dual-path). Backfill of the daily archive lives in migrate.ts + // so it runs once on upgrade, not on every fresh initSchema. + // Spec: docs/superpowers/specs/2026-06-11-usage-dashboard-v2-design.md + db.exec(` + CREATE TABLE IF NOT EXISTS llm_usage_hourly ( + hour TEXT NOT NULL, + user_id TEXT NOT NULL, + source TEXT NOT NULL, + model TEXT NOT NULL, + route TEXT NOT NULL, + tokens_in INTEGER NOT NULL DEFAULT 0, + tokens_out INTEGER NOT NULL DEFAULT 0, + requests INTEGER NOT NULL DEFAULT 0, + last_updated_at TEXT NOT NULL, + PRIMARY KEY (hour, user_id, source, model, route) + ); + CREATE INDEX IF NOT EXISTS idx_llm_usage_hourly_user_hour + ON llm_usage_hourly (user_id, hour); + `); + } + + + export function runPreSchemaCompatibilityMigrations(db: Database.Database): void { + const hasBrowserSessionProfiles = db + .prepare("SELECT 1 FROM sqlite_master WHERE type = ? AND name = ?") + .get("table", "browser_session_profiles"); + if (!hasBrowserSessionProfiles) { + return; + } + + const columns = db + .prepare("PRAGMA table_info(browser_session_profiles)") + .all() as Array<{ name: string }>; + if (columns.some((column) => column.name === "space_id")) { + return; + } + + db.exec("ALTER TABLE browser_session_profiles ADD COLUMN space_id TEXT"); + } + + + export function ensureColumn(db: Database.Database, tableName: string, columnName: string, definition: string): void { + const columns = db.prepare(`PRAGMA table_info(${tableName})`).all() as Array<{ name: string }>; + if (columns.some((column) => column.name === columnName)) { + return; + } + db.prepare(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${definition}`).run(); + } + + + export function migrateWaitingSubtasksStatus(db: Database.Database): void { + // Check if jobs table already has waiting_subtasks in its CHECK constraint + const tableInfo = db.prepare( + "SELECT sql FROM sqlite_master WHERE type='table' AND name='jobs'" + ).get() as { sql: string } | undefined; + if (!tableInfo || tableInfo.sql.includes('waiting_subtasks')) return; + + // Recreate jobs table with updated CHECK constraint + logger.info('Repository: migrating jobs table to support waiting_subtasks status...'); + db.transaction(() => { + db.exec(` + CREATE TABLE IF NOT EXISTS jobs_v2 ( + id TEXT PRIMARY KEY, + repo TEXT NOT NULL, + issue_number INTEGER NOT NULL, + pr_number INTEGER, + status TEXT NOT NULL DEFAULT 'queued' + CHECK (status IN ('queued','dispatching','running','succeeded','failed','retry','cancelled','waiting_human','waiting_subtasks')), + piece_name TEXT NOT NULL DEFAULT 'general', + required_profile TEXT NOT NULL DEFAULT 'auto', + task_class TEXT NOT NULL DEFAULT 'auto', + current_movement TEXT, + instruction TEXT NOT NULL DEFAULT '', + branch_name TEXT, + worktree_path TEXT, + attempt INTEGER NOT NULL DEFAULT 1, + max_attempts INTEGER NOT NULL DEFAULT 3, + next_retry_at TEXT, + error_summary TEXT, + resume_movement TEXT, + ask_count INTEGER NOT NULL DEFAULT 0, + worker_id TEXT, + parent_job_id TEXT, + subtask_depth INTEGER NOT NULL DEFAULT 0, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + INSERT INTO jobs_v2 + SELECT id, repo, issue_number, pr_number, status, piece_name, required_profile, task_class, + current_movement, instruction, branch_name, worktree_path, attempt, max_attempts, + next_retry_at, error_summary, resume_movement, ask_count, worker_id, + NULL AS parent_job_id, 0 AS subtask_depth, created_at, updated_at + FROM jobs; + DROP TABLE jobs; + ALTER TABLE jobs_v2 RENAME TO jobs; + `); + })(); + logger.info('Repository: jobs table migration complete'); + } diff --git a/src/db/repositories/shared.ts b/src/db/repositories/shared.ts new file mode 100644 index 0000000..cf5559d --- /dev/null +++ b/src/db/repositories/shared.ts @@ -0,0 +1,44 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// shared repository domain. Facade: src/db/repository.ts (Repository class). + +/** Escape LIKE wildcards so a caller-supplied prefix matches literally. */ +export function escapeLike(s: string): string { + return s.replace(/[\\%_]/g, (c) => '\\' + c); +} + + +export function encodeTags(values: string[]): string { + const unique = Array.from(new Set(values.filter(Boolean))); + return `,${unique.join(',')},`; +} + + +export function decodeTags(raw: string | null): string[] { + if (!raw) return []; + return raw.split(',').map((value) => value.trim()).filter(Boolean); +} + + +export function decodeAvailableModels(raw: string | null): string[] { + if (!raw) return []; + try { + const parsed = JSON.parse(raw) as unknown; + return Array.isArray(parsed) ? parsed.filter((value): value is string => typeof value === 'string') : []; + } catch { + return []; + } +} + + +/** SQLite datetime('now') は UTC だがタイムゾーン情報なし。'Z' を付加して ISO 8601 UTC として明示する */ +export function utc(dt: string | null): string { + if (!dt) return ''; + // 既に Z や +/- オフセットが付いていれば何もしない + if (/[Zz]$/.test(dt) || /[+-]\d{2}:\d{2}$/.test(dt)) return dt; + return dt.replace(' ', 'T') + 'Z'; +} + + +export function localTaskRepoName(taskId: number): string { + return `local/task-${taskId}`; +} diff --git a/src/db/repositories/spaces.ts b/src/db/repositories/spaces.ts new file mode 100644 index 0000000..cd5a4b2 --- /dev/null +++ b/src/db/repositories/spaces.ts @@ -0,0 +1,495 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// spaces repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { randomUUID } from 'crypto'; +import { buildVisibilityWhere, buildSpaceVisibilityWhere } from '../../bridge/visibility.js'; +import { utc } from './shared.js'; + +export interface SpaceRow { + id: string; + kind: string; + title: string; + description: string; + owner_id: string | null; + visibility: string; + visibility_scope_org_id: string | null; + status: string; + brand_color: string | null; + workspace_dir: string | null; + created_at: string; + updated_at: string; +} + + +export interface Space { + id: string; + kind: 'personal' | 'case'; + title: string; + description: string; + ownerId: string | null; + visibility: 'private' | 'org' | 'public'; + visibilityScopeOrgId: string | null; + status: 'open' | 'archived'; + brandColor: string | null; + workspaceDir: string | null; + createdAt: string; + updatedAt: string; +} + + +export type SpaceMemberRoleValue = 'owner' | 'editor' | 'viewer'; + + +/** space_members の 1 行 + users から合成した表示情報。owner_id は別途 owner として扱う。 */ +export interface SpaceMember { + spaceId: string; + userId: string; + role: SpaceMemberRoleValue; + invitedBy: string | null; + createdAt: string; + /** users JOIN 由来の表示用フィールド(行が無い場合 null)。 */ + name: string | null; + email: string | null; + avatarUrl: string | null; +} + + +/** 招待リンクで付与できるロール。owner は付与不可(権限昇格防止)。 */ +export type SpaceInviteRole = 'editor' | 'viewer'; + + +/** space_invites の 1 行。 */ +export interface SpaceInvite { + token: string; + spaceId: string; + role: SpaceInviteRole; + createdBy: string | null; + createdAt: string; + /** ISO 文字列。null = 無期限。 */ + expiresAt: string | null; + /** ISO 文字列。null = 有効。 */ + revokedAt: string | null; +} + + +export interface CreateSpaceParams { + id?: string; + kind: 'personal' | 'case'; + title: string; + description?: string; + ownerId: string | null; + visibility?: 'private' | 'org' | 'public'; + visibilityScopeOrgId?: string | null; + brandColor?: string | null; + workspaceDir?: string | null; +} + + +export function rowToSpace(row: SpaceRow): Space { + return { + id: row.id, + kind: row.kind as Space['kind'], + title: row.title, + description: row.description, + ownerId: row.owner_id, + visibility: row.visibility as Space['visibility'], + visibilityScopeOrgId: row.visibility_scope_org_id, + status: row.status as Space['status'], + brandColor: row.brand_color, + workspaceDir: row.workspace_dir, + createdAt: utc(row.created_at), + updatedAt: utc(row.updated_at), + }; +} + + + export async function createSpace(db: Database.Database, params: CreateSpaceParams): Promise { + const id = params.id ?? randomUUID(); + db + .prepare( + `INSERT INTO spaces (id, kind, title, description, owner_id, visibility, visibility_scope_org_id, status, brand_color, workspace_dir) + VALUES (@id, @kind, @title, @description, @ownerId, @visibility, @visibilityScopeOrgId, 'open', @brandColor, @workspaceDir)` + ) + .run({ + id, + kind: params.kind, + title: params.title, + description: params.description ?? '', + ownerId: params.ownerId, + visibility: params.visibility ?? 'private', + visibilityScopeOrgId: params.visibilityScopeOrgId ?? null, + brandColor: params.brandColor ?? null, + workspaceDir: params.workspaceDir ?? null, + }); + const space = await getSpace(db, id); + if (!space) throw new Error('createSpace: failed to load inserted space'); + return space; + } + + + export async function getSpace(db: Database.Database, id: string, opts?: { viewer?: Express.User }): Promise { + // 個人スペースは所有者のみ可視(admin 含む)。buildSpaceVisibilityWhere が + // kind='personal' を owner_id 一致に限定し、kind='case' は従来通り扱う。 + const viewerClause = opts?.viewer + ? buildSpaceVisibilityWhere(opts.viewer, 's') + : { clause: '1=1', params: [] as unknown[] }; + const row = db + .prepare(`SELECT s.* FROM spaces s WHERE s.id = ? AND ${viewerClause.clause}`) + .get(id, ...viewerClause.params) as SpaceRow | undefined; + return row ? rowToSpace(row) : null; + } + + + export async function listSpaces(db: Database.Database, opts?: { viewer?: Express.User; includeArchived?: boolean }): Promise { + // 個人スペースは所有者のみ可視(admin 含む)。これにより admin の一覧/レールに + // 他ユーザーの個人スペースが混ざらず、UI の誤ワークスペース解決を防ぐ。 + const viewerClause = opts?.viewer + ? buildSpaceVisibilityWhere(opts.viewer, 's') + : { clause: '1=1', params: [] as unknown[] }; + const statusClause = opts?.includeArchived ? '1=1' : "s.status = 'open'"; + const rows = db + .prepare(` + SELECT s.* FROM spaces s + WHERE ${statusClause} AND ${viewerClause.clause} + ORDER BY s.kind = 'personal' DESC, s.updated_at DESC + `) + .all(...viewerClause.params) as SpaceRow[]; + return rows.map(rowToSpace); + } + + + export async function updateSpace(db: Database.Database, id: string, patch: { title?: string; description?: string; brandColor?: string | null; visibility?: 'private' | 'org' | 'public'; visibilityScopeOrgId?: string | null }): Promise { + const sets: string[] = []; + const vals: unknown[] = []; + if (patch.title !== undefined) { sets.push('title = ?'); vals.push(patch.title); } + if (patch.description !== undefined) { sets.push('description = ?'); vals.push(patch.description); } + if (patch.brandColor !== undefined) { sets.push('brand_color = ?'); vals.push(patch.brandColor); } + if (patch.visibility !== undefined) { sets.push('visibility = ?'); vals.push(patch.visibility); } + if (patch.visibilityScopeOrgId !== undefined) { sets.push('visibility_scope_org_id = ?'); vals.push(patch.visibilityScopeOrgId); } + if (sets.length === 0) return getSpace(db, id); + sets.push("updated_at = datetime('now')"); + db.prepare(`UPDATE spaces SET ${sets.join(', ')} WHERE id = ?`).run(...vals, id); + return getSpace(db, id); + } + + + export async function archiveSpace(db: Database.Database, id: string): Promise { + db + .prepare(`UPDATE spaces SET status = 'archived', updated_at = datetime('now') WHERE id = ? AND kind = 'case'`) + .run(id); + } + + + export async function setSpaceWorkspaceDir(db: Database.Database, id: string, workspaceDir: string): Promise { + db + .prepare(`UPDATE spaces SET workspace_dir = ?, updated_at = datetime('now') WHERE id = ?`) + .run(workspaceDir, id); + } + + + /** + * ロールバック専用のハード削除。通常は archiveSpace を使う。 + * 個人スペースは「削除不可」(spec §5.7) なので kind='case' のみ消す。 + * この backstop により、誤って personal id を渡しても個人スペースは保護される。 + */ + export async function hardDeleteSpace(db: Database.Database, id: string): Promise { + const tx = db.transaction((sid: string) => { + // Crypto-shred space-owned credential DEKs EXPLICITLY (Space-as-Principal P2). + // We don't rely on FK ON DELETE CASCADE because migrate.ts intentionally + // omits FK clauses, so a migrated DB's space_ssh_deks would otherwise + // outlive the space. Gate on kind='case' (subquery) so a mistaken personal + // id never shreds a personal space's DEK. Runs before the spaces delete so + // the guard subquery still sees the row. + const isCase = `AND space_id IN (SELECT id FROM spaces WHERE kind = 'case')`; + db.prepare(`DELETE FROM space_ssh_deks WHERE space_id = ? ${isCase}`).run(sid); + db.prepare(`DELETE FROM space_browser_deks WHERE space_id = ? ${isCase}`).run(sid); + db.prepare(`DELETE FROM spaces WHERE id = ? AND kind = 'case'`).run(sid); + }); + tx(id); + } + + + /** + * ユーザーの個人スペースを返す。無ければ生成する(冪等)。 + * UNIQUE INDEX idx_spaces_personal_owner により1ユーザー1個に保たれる。 + * 既存ユーザーの初回アクセス移行を兼ねる(spec §7)。 + * 個人スペースの workspace_dir はこの計画では NULL(フォルダ物理ワイヤリングは計画2)。 + */ + export async function ensurePersonalSpace(db: Database.Database, ownerId: string, displayName?: string): Promise { + const existing = db + .prepare(`SELECT s.* FROM spaces s WHERE s.owner_id = ? AND s.kind = 'personal'`) + .get(ownerId) as SpaceRow | undefined; + if (existing) return rowToSpace(existing); + return createSpace(db, { + kind: 'personal', + title: displayName ? `${displayName} の個人ワークスペース` : '個人ワークスペース', + ownerId, + visibility: 'private', + }); + } + + + // ─── スペース・メンバー(協働者)CRUD ───────────────────────────── + // owner_id は members 行を持たない根オーナー。これらは追加協働者だけを扱う。 + + /** + * メンバーを追加または上書きする(冪等)。既存行があれば role/invited_by を更新。 + * owner_id 本人を渡してはならない(既に owner)。呼び出し側でガードする。 + */ + export async function addSpaceMember(db: Database.Database, params: { + spaceId: string; + userId: string; + role: SpaceMemberRoleValue; + invitedBy?: string | null; + }): Promise { + db + .prepare( + `INSERT INTO space_members (space_id, user_id, role, invited_by) + VALUES (@spaceId, @userId, @role, @invitedBy) + ON CONFLICT(space_id, user_id) DO UPDATE SET + role = excluded.role, + invited_by = excluded.invited_by` + ) + .run({ + spaceId: params.spaceId, + userId: params.userId, + role: params.role, + invitedBy: params.invitedBy ?? null, + }); + } + + + /** スペースのメンバー一覧(users JOIN で表示情報付き)。owner_id は含まない。 */ + export async function listSpaceMembers(db: Database.Database, spaceId: string): Promise { + const rows = db + .prepare(` + SELECT sm.space_id, sm.user_id, sm.role, sm.invited_by, sm.created_at, + u.name AS name, u.email AS email, u.avatar_url AS avatar_url + FROM space_members sm + LEFT JOIN users u ON u.id = sm.user_id + WHERE sm.space_id = ? + ORDER BY sm.created_at ASC, sm.user_id ASC + `) + .all(spaceId) as Array<{ + space_id: string; + user_id: string; + role: string; + invited_by: string | null; + created_at: string; + name: string | null; + email: string | null; + avatar_url: string | null; + }>; + return rows.map((r) => ({ + spaceId: r.space_id, + userId: r.user_id, + role: r.role as SpaceMemberRoleValue, + invitedBy: r.invited_by, + createdAt: r.created_at, + name: r.name, + email: r.email, + avatarUrl: r.avatar_url, + })); + } + + + /** + * 指定ユーザーのメンバーロールを返す。メンバー行が無ければ null。 + * 根オーナー(owner_id)は members 行を持たないため null を返す。 + * 呼び出し側は owner_id を別途 'owner' として扱うこと(canManageSpace 参照)。 + */ + export function getSpaceMemberRole(db: Database.Database, spaceId: string, userId: string): SpaceMemberRoleValue | null { + const row = db + .prepare(`SELECT role FROM space_members WHERE space_id = ? AND user_id = ?`) + .get(spaceId, userId) as { role: string } | undefined; + return row ? (row.role as SpaceMemberRoleValue) : null; + } + + + /** スペースのツール制限ポリシー(JSON 文字列)を返す。未設定なら null。 */ + export function getSpaceToolPolicy(db: Database.Database, spaceId: string): string | null { + const row = db + .prepare(`SELECT tool_policy FROM spaces WHERE id = ?`) + .get(spaceId) as { tool_policy: string | null } | undefined; + return row?.tool_policy ?? null; + } + + + /** スペースのツール制限ポリシーを更新する。null を渡すとクリア。 */ + export function setSpaceToolPolicy(db: Database.Database, spaceId: string, policyJson: string | null): void { + db + .prepare(`UPDATE spaces SET tool_policy = ?, updated_at = datetime('now') WHERE id = ?`) + .run(policyJson, spaceId); + } + + + /** スペースの Python パッケージ desired-state(JSON 文字列)を返す。未設定なら null。 */ + export function getSpacePythonPackages(db: Database.Database, spaceId: string): string | null { + const row = db + .prepare(`SELECT python_packages FROM spaces WHERE id = ?`) + .get(spaceId) as { python_packages: string | null } | undefined; + return row?.python_packages ?? null; + } + + + /** スペースの Python パッケージ desired-state を更新する。null を渡すとクリア。 */ + export function setSpacePythonPackages(db: Database.Database, spaceId: string, packagesJson: string | null): void { + db + .prepare(`UPDATE spaces SET python_packages = ?, updated_at = datetime('now') WHERE id = ?`) + .run(packagesJson, spaceId); + } + + + /** + * 指定 user がスペースの行(タスク/ファイル/ログ等)を VIEW できるか。 + * buildVisibilityWhere の membership OR ブランチと同じルールをコード上で再現する: + * admin → true / space.owner_id === user.id(根オーナー)→ true / + * space_members に (spaceId, user.id) 行あり → true / それ以外 → false。 + * 二次ゲート(canViewTask / canUserSeeTask)が呼び出す。書き込み判定ではない。 + */ + export function userCanViewSpace(db: Database.Database, spaceId: string, user: { id: string; role: string }): boolean { + if (user.role === 'admin') return true; + const owns = db + .prepare(`SELECT 1 FROM spaces WHERE id = ? AND owner_id = ?`) + .get(spaceId, user.id); + if (owns) return true; + const member = db + .prepare(`SELECT 1 FROM space_members WHERE space_id = ? AND user_id = ?`) + .get(spaceId, user.id); + return !!member; + } + + + /** 既存メンバーのロールを変更する。行が無ければ no-op。 */ + export async function updateSpaceMemberRole(db: Database.Database, spaceId: string, userId: string, role: SpaceMemberRoleValue): Promise { + db + .prepare(`UPDATE space_members SET role = ? WHERE space_id = ? AND user_id = ?`) + .run(role, spaceId, userId); + } + + + /** メンバーを除去する。行が無ければ no-op。 */ + export async function removeSpaceMember(db: Database.Database, spaceId: string, userId: string): Promise { + db + .prepare(`DELETE FROM space_members WHERE space_id = ? AND user_id = ?`) + .run(spaceId, userId); + } + + + // ─── スペース招待リンク(再利用トークン)───────────────────────────── + + export function rowToSpaceInvite(row: { + token: string; + space_id: string; + role: string; + created_by: string | null; + created_at: string; + expires_at: string | null; + revoked_at: string | null; + }): SpaceInvite { + return { + token: row.token, + spaceId: row.space_id, + role: row.role as SpaceInviteRole, + createdBy: row.created_by, + createdAt: row.created_at, + expiresAt: row.expires_at, + revokedAt: row.revoked_at, + }; + } + + + /** + * 招待が今この瞬間に有効か(無効化されておらず、期限切れでない)。 + * 期限比較は SQLite と同じ datetime('now')(UTC)基準の ISO 文字列辞書順。 + */ + export function isSpaceInviteValid(db: Database.Database, invite: SpaceInvite | null | undefined): invite is SpaceInvite { + if (!invite) return false; + if (invite.revokedAt) return false; + if (invite.expiresAt) { + const now = db.prepare(`SELECT datetime('now') AS n`).get() as { n: string }; + if (invite.expiresAt <= now.n) return false; + } + return true; + } + + + /** + * スペースの招待リンクを (再)生成する。再利用方針=スペースごとに有効リンク1本。 + * 既存 invite 行は削除してから新トークンを 1 行 insert する。 + * @param expiresInDays 有効日数。未指定/null は無期限。期限は SQLite の + * datetime('now','+N days') で保存し、isSpaceInviteValid と同じ形式に揃える。 + */ + export function createSpaceInvite(db: Database.Database, params: { + spaceId: string; + role: SpaceInviteRole; + createdBy?: string | null; + expiresInDays?: number | null; + }): SpaceInvite { + const token = randomUUID(); + // 整数日数のみ受理(負/0/非整数は無期限扱い)。文字列補間するため厳格に検証する。 + const days = + typeof params.expiresInDays === 'number' && + Number.isInteger(params.expiresInDays) && + params.expiresInDays > 0 + ? params.expiresInDays + : null; + const tx = db.transaction(() => { + db.prepare(`DELETE FROM space_invites WHERE space_id = ?`).run(params.spaceId); + db + .prepare( + `INSERT INTO space_invites (token, space_id, role, created_by, expires_at) + VALUES (@token, @spaceId, @role, @createdBy, + ${days === null ? 'NULL' : `datetime('now', '+${days} days')`})`, + ) + .run({ + token, + spaceId: params.spaceId, + role: params.role, + createdBy: params.createdBy ?? null, + }); + }); + tx(); + return getSpaceInviteByToken(db, token)!; + } + + + /** スペースの現行 invite(有効・無効問わず最新の1本)。無ければ null。 */ + export function getSpaceInvite(db: Database.Database, spaceId: string): SpaceInvite | null { + const row = db + .prepare(`SELECT * FROM space_invites WHERE space_id = ? ORDER BY created_at DESC LIMIT 1`) + .get(spaceId) as Parameters[0] | undefined; + return row ? rowToSpaceInvite(row) : null; + } + + + /** トークンで invite を引く(有効性は呼び出し側で判定)。無ければ null。 */ + export function getSpaceInviteByToken(db: Database.Database, token: string): SpaceInvite | null { + const row = db + .prepare(`SELECT * FROM space_invites WHERE token = ?`) + .get(token) as Parameters[0] | undefined; + return row ? rowToSpaceInvite(row) : null; + } + + + /** スペースの現行 invite を無効化する。行が無ければ no-op。 */ + export function revokeSpaceInvite(db: Database.Database, spaceId: string): void { + db + .prepare( + `UPDATE space_invites SET revoked_at = datetime('now') + WHERE space_id = ? AND revoked_at IS NULL`, + ) + .run(spaceId); + } + + + export function getSpaceA2aSkills(db: Database.Database, spaceId: string): string[] { + const r = db.prepare('SELECT a2a_skills FROM spaces WHERE id = ?').get(spaceId) as { a2a_skills: string | null } | undefined; + if (!r || !r.a2a_skills) return []; + try { const v = JSON.parse(r.a2a_skills); return Array.isArray(v) ? v : []; } catch { return []; } + } + + + export function setSpaceA2aSkills(db: Database.Database, spaceId: string, skills: string[]): void { + db.prepare('UPDATE spaces SET a2a_skills = ? WHERE id = ?').run(JSON.stringify(skills), spaceId); + } diff --git a/src/db/repositories/task-search.ts b/src/db/repositories/task-search.ts new file mode 100644 index 0000000..e2872ae --- /dev/null +++ b/src/db/repositories/task-search.ts @@ -0,0 +1,144 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// task-search repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { logger } from '../../logger.js'; +import { normalizeCommentToIndexText } from '../../engine/task-index/normalize.js'; +import * as localTasksRepo from './local-tasks.js'; + + /** + * Construct a WorkspaceTaskSearchIO scoped to the current task + owner. + * Used by SearchWorkspaceTasks / ReadWorkspaceTaskAround; searchWorkspaceTasks / + * readWorkspaceTaskAround themselves enforce the space/owner scoping, this is + * just the binding closure. fts5Available lets the tool degrade gracefully + * when the FTS5 index table is missing (e.g. pre-migration DB). + */ + export function makeWorkspaceTaskSearchIO(db: Database.Database, currentTaskId: number, ownerId: string): import('../../engine/tools/core.js').WorkspaceTaskSearchIO { + const fts5Available = !!db + .prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='task_comment_fts'") + .get(); + return { + fts5Available, + search: (query, opts) => searchWorkspaceTasks(db, currentTaskId, ownerId, query, opts), + around: (commentId, before, after) => + readWorkspaceTaskAround(db, currentTaskId, ownerId, commentId, before, after), + }; + } + + + /** best-effort に1コメントを索引する。失敗してもコメント書き込みを壊さない。 */ + export function indexTaskComment(db: Database.Database, commentId: number, taskId: number, author: string, kind: string, createdAt: string, body: string, attachments: string | null): void { + try { + const text = normalizeCommentToIndexText(kind, body, attachments); + if (text === null) return; + db.prepare( + `INSERT OR IGNORE INTO task_comment_index (comment_id, task_id, author, kind, created_at, text) + VALUES (?, ?, ?, ?, ?, ?)`, + ).run(commentId, taskId, author, kind, createdAt, text); + } catch (err) { + logger.warn(`[task-index] indexTaskComment failed comment=${commentId}: ${(err as Error).message}`); + } + } + + + /** + * 正規化済みの各語を個別に引用して FTS5 MATCH 文字列を組む(Google 的な暗黙 AND)。 + * 各語をダブルクォートで囲むので、`AND`/`OR`/`NEAR`/`*`/`"` などが語に混じっても + * FTS5 演算子として解釈されず、インジェクションを防ぐ。語同士はこちらが制御する + * ` AND ` でつなぐため「全語を含む」検索になる。呼び出し側で引用符除去済みを前提とする。 + */ + export function buildFtsMatch(terms: string[]): string { + return terms.map((t) => `"${t}"`).join(' AND '); + } + + + export async function searchWorkspaceTasks(db: Database.Database, currentTaskId: number, ownerId: string, query: string, opts: { limit?: number; kind?: string; author?: string; taskId?: number } = {}): Promise> { + const q = (query ?? '').trim(); + if (!q) return []; + const cur = db.prepare('SELECT space_id, owner_id FROM local_tasks WHERE id = ?') + .get(currentTaskId) as { space_id: string | null; owner_id: string | null } | undefined; + if (!cur) return []; + + // スペース帰属確認(owner または space_members メンバー)。非帰属なら空。 + if (cur.space_id) { + const member = db.prepare( + `SELECT 1 FROM spaces WHERE id = @s AND owner_id = @u + UNION SELECT 1 FROM space_members WHERE space_id = @s AND user_id = @u`, + ).get({ s: cur.space_id, u: ownerId }); + if (!member) return []; + } + + const limit = Math.min(Math.max(opts.limit ?? 10, 1), 30); + // Google 的な暗黙 AND: 空白区切りの各語を「すべて含む」ものにヒットさせる。 + // 各語から埋め込み二重引用符を除去(空白化)してトリムし、空になった語は捨てる。 + // これで純粋な記号だけの「語」が AND を汚染して常に0件になるのを防ぐ。 + const terms = q.split(/\s+/).map((t) => t.replace(/"/g, ' ').trim()).filter(Boolean); + if (terms.length === 0) return []; // 引用符・記号のみで実質的な検索語が無い + // trigram は3文字未満の語を MATCH できないため、短い語が1つでもあれば LIKE 経路 + // (語ごとの AND-of-LIKE。LIKE は語長に依存せず動く)にフォールバックする。 + const useLike = terms.some((t) => t.length < 3); + const scope = cur.space_id + ? 'lt.space_id = @spaceId' + : 'lt.space_id IS NULL AND lt.owner_id = @ownerId'; + const filters = [ + opts.kind ? 'i.kind = @kind' : '', + opts.author ? 'i.author = @author' : '', + opts.taskId ? 'i.task_id = @taskFilter' : '', + ].filter(Boolean).join(' AND '); + const filterSql = filters ? ` AND ${filters}` : ''; + + const params: Record = { + cur: currentTaskId, spaceId: cur.space_id, ownerId, + kind: opts.kind, author: opts.author, taskFilter: opts.taskId, limit, + }; + + let sql: string; + if (useLike) { + // 各語を LIKE で AND 結合(位置・順序を問わず全語を含む)。値はパラメータ束縛、 + // 列名・プレースホルダ名は固定文字列なのでインジェクションしない。 + const likeConds = terms.map((_, i) => `i.text LIKE @like${i}`).join(' AND '); + terms.forEach((t, i) => { params[`like${i}`] = `%${t}%`; }); + sql = ` + SELECT i.task_id AS taskId, lt.title AS taskTitle, i.comment_id AS commentId, + i.author, i.kind, i.created_at AS createdAt, i.text + FROM task_comment_index i + JOIN local_tasks lt ON lt.id = i.task_id + WHERE (${likeConds}) AND i.task_id != @cur AND (${scope})${filterSql} + ORDER BY i.created_at DESC LIMIT @limit`; + } else { + params.match = buildFtsMatch(terms); + sql = ` + SELECT i.task_id AS taskId, lt.title AS taskTitle, i.comment_id AS commentId, + i.author, i.kind, i.created_at AS createdAt, i.text + FROM task_comment_fts f + JOIN task_comment_index i ON i.comment_id = f.rowid + JOIN local_tasks lt ON lt.id = i.task_id + WHERE f.text MATCH @match AND i.task_id != @cur AND (${scope})${filterSql} + ORDER BY bm25(task_comment_fts) LIMIT @limit`; + } + return db.prepare(sql).all(params) as any; + } + + + export async function readWorkspaceTaskAround(db: Database.Database, currentTaskId: number, ownerId: string, commentId: number, before: number, after: number) { + const cur = db.prepare('SELECT space_id, owner_id FROM local_tasks WHERE id = ?') + .get(currentTaskId) as { space_id: string | null; owner_id: string | null } | undefined; + if (!cur) return null; + const target = db.prepare('SELECT task_id FROM local_task_comments WHERE id = ?') + .get(commentId) as { task_id: number } | undefined; + if (!target) return null; + const t = db.prepare('SELECT id, title, space_id, owner_id FROM local_tasks WHERE id = ?') + .get(target.task_id) as { id: number; title: string; space_id: string | null; owner_id: string | null } | undefined; + if (!t) return null; + // スコープ確認: 同一スペース(or 同 owner の個人スペース) + const inScope = cur.space_id ? t.space_id === cur.space_id : (t.space_id === null && t.owner_id === ownerId); + if (!inScope) return null; + + const all = await localTasksRepo.listLocalTaskComments(db, target.task_id); + const pos = all.findIndex((x) => x.id === commentId); + if (pos < 0) return null; + const slice = all.slice(Math.max(0, pos - before), pos + after + 1); + return { + taskId: t.id, taskTitle: t.title, + comments: slice.map((x) => ({ commentId: x.id, author: x.author, kind: x.kind, createdAt: x.createdAt, body: x.body, isCenter: x.id === commentId })), + }; + } diff --git a/src/db/repositories/tool-requests.ts b/src/db/repositories/tool-requests.ts new file mode 100644 index 0000000..5f2cf8c --- /dev/null +++ b/src/db/repositories/tool-requests.ts @@ -0,0 +1,395 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// tool-requests repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { randomUUID } from 'crypto'; + +export type ToolRequestCategory = 'requested' | 'blocked' | 'unknown'; + +export type ToolRequestStatus = 'pending' | 'approved' | 'denied' | 'auto_denied'; + + +/** 現在の movement で使えないツールの要求記録(能動 RequestTool / 受動 block 捕捉)。 */ +export interface ToolRequest { + id: string; + taskId: string | null; + jobId: string | null; + spaceId: string | null; + pieceName: string; + movementName: string; + toolName: string; + reason: string | null; + category: ToolRequestCategory; + status: ToolRequestStatus; + grantScope: 'task' | 'piece' | null; + decidedBy: string | null; + createdAt: string; + decidedAt: string | null; +} + + +export interface ToolRequestRow { + id: string; + task_id: string | null; + job_id: string | null; + space_id: string | null; + piece_name: string; + movement_name: string; + tool_name: string; + reason: string | null; + category: string; + status: string; + grant_scope: string | null; + decided_by: string | null; + created_at: string; + decided_at: string | null; +} + + +export function rowToToolRequest(row: ToolRequestRow): ToolRequest { + return { + id: row.id, + taskId: row.task_id ?? null, + jobId: row.job_id ?? null, + spaceId: row.space_id ?? null, + pieceName: row.piece_name, + movementName: row.movement_name, + toolName: row.tool_name, + reason: row.reason ?? null, + category: (row.category as ToolRequestCategory) ?? 'requested', + status: (row.status as ToolRequestStatus) ?? 'pending', + grantScope: (row.grant_scope as 'task' | 'piece' | null) ?? null, + decidedBy: row.decided_by ?? null, + createdAt: row.created_at, + decidedAt: row.decided_at ?? null, + }; +} + + +/** ピース集計の1行(tool_name×category ごとの件数)。 */ +export interface ToolRequestAggregate { + toolName: string; + category: ToolRequestCategory; + count: number; + lastSeen: string; + sampleReason: string | null; +} + + + /** + * Record a tool request — either an agent's RequestTool declaration or a + * passively-captured blocked tool call. De-dups within the same job: an + * identical still-pending (job_id, movement_name, tool_name) row is reused + * so a retry loop does not spam the table. Returns the row id. + */ + export function recordToolRequest(db: Database.Database, params: { + id?: string; + taskId?: string | null; + jobId?: string | null; + spaceId?: string | null; + pieceName: string; + movementName: string; + toolName: string; + reason?: string | null; + category?: ToolRequestCategory; + status?: ToolRequestStatus; + }): string { + const category: ToolRequestCategory = params.category ?? 'requested'; + const status: ToolRequestStatus = params.status ?? 'pending'; + // De-dup pending rows so a retry loop never grows the table without bound + // (the table has no GC). Scope by the most specific identity available — + // job → task → piece — so de-dup still works for runs without a job + // binding. scopeCol is a fixed identifier (not user input) → no injection. + const scopeCol: 'job_id' | 'task_id' | 'piece_name' = params.jobId + ? 'job_id' + : params.taskId + ? 'task_id' + : 'piece_name'; + const scopeVal = params.jobId ?? params.taskId ?? params.pieceName; + const existing = db + .prepare( + `SELECT id FROM tool_requests + WHERE ${scopeCol} = ? AND movement_name = ? AND tool_name = ? AND status = 'pending' + LIMIT 1`, + ) + .get(scopeVal, params.movementName, params.toolName) as { id: string } | undefined; + if (existing) return existing.id; + const id = params.id ?? randomUUID(); + db + .prepare( + `INSERT INTO tool_requests + (id, task_id, job_id, space_id, piece_name, movement_name, tool_name, reason, category, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + ) + .run( + id, + params.taskId ?? null, + params.jobId ?? null, + params.spaceId ?? null, + params.pieceName, + params.movementName, + params.toolName, + params.reason ?? null, + category, + status, + ); + return id; + } + + + /** All tool requests for a task, newest first. */ + export function listToolRequestsByTask(db: Database.Database, taskId: string): ToolRequest[] { + const rows = db + .prepare(`SELECT * FROM tool_requests WHERE task_id = ? ORDER BY created_at DESC, id DESC`) + .all(taskId) as ToolRequestRow[]; + return rows.map(rowToToolRequest); + } + + + /** Per-piece aggregation: count + last-seen + a sample reason per + * (tool_name, category). `sampleReason` is one representative reason (not + * necessarily the most recent — it is a MAX over the group). */ + export function aggregateToolRequestsByPiece(db: Database.Database, pieceName: string): ToolRequestAggregate[] { + const rows = db + .prepare( + `SELECT tool_name, category, COUNT(*) AS count, + MAX(created_at) AS last_seen, MAX(reason) AS sample_reason + FROM tool_requests + WHERE piece_name = ? + GROUP BY tool_name, category + ORDER BY count DESC, tool_name ASC`, + ) + .all(pieceName) as Array<{ + tool_name: string; category: string; count: number; last_seen: string; sample_reason: string | null; + }>; + return rows.map((r) => ({ + toolName: r.tool_name, + category: (r.category as ToolRequestCategory) ?? 'requested', + count: r.count, + lastSeen: r.last_seen, + sampleReason: r.sample_reason ?? null, + })); + } + + + /** Fetch a single tool request by id. */ + export function getToolRequest(db: Database.Database, id: string): ToolRequest | null { + const row = db.prepare(`SELECT * FROM tool_requests WHERE id = ?`).get(id) as ToolRequestRow | undefined; + return row ? rowToToolRequest(row) : null; + } + + + /** + * Record an approve/deny/auto-deny decision on a still-pending tool request. + * Returns false if the row is missing or already decided (idempotent / race + * safe — a concurrent decide won't double-apply). + */ + export function decideToolRequest(db: Database.Database, id: string, decision: { status: Exclude; grantScope?: 'task' | 'piece' | null; decidedBy?: string | null }): boolean { + const res = db + .prepare( + `UPDATE tool_requests + SET status = ?, grant_scope = ?, decided_by = ?, decided_at = datetime('now') + WHERE id = ? AND status = 'pending'`, + ) + .run(decision.status, decision.grantScope ?? null, decision.decidedBy ?? null, id); + return res.changes > 0; + } + + + /** Per-task grant overlay (tool names approved inline for this task). */ + export function getGrantedTools(db: Database.Database, taskId: string): string[] { + const row = db.prepare(`SELECT granted_tools FROM local_tasks WHERE id = ?`).get(taskId) as + | { granted_tools: string | null } + | undefined; + if (!row?.granted_tools) return []; + try { + const parsed = JSON.parse(row.granted_tools); + return Array.isArray(parsed) ? parsed.filter((v): v is string => typeof v === 'string') : []; + } catch { + return []; + } + } + + + /** Add a tool to a task's grant overlay (idempotent — de-duped). Returns the new set. */ + export function addGrantedTool(db: Database.Database, taskId: string, toolName: string): string[] { + const current = getGrantedTools(db, taskId); + if (current.includes(toolName)) return current; + const next = [...current, toolName]; + db.prepare(`UPDATE local_tasks SET granted_tools = ? WHERE id = ?`).run(JSON.stringify(next), taskId); + return next; + } + +// ─── Package requests (RequestPackage → 承認 → install) ────────────────────── + +export type PackageRequestStatus = 'pending' | 'approved' | 'denied' | 'auto_denied'; + +/** エージェント発 Python パッケージ要求(RequestPackage)の記録。 */ +export interface PackageRequest { + id: string; + taskId: string | null; + jobId: string | null; + spaceId: string | null; + pieceName: string | null; + movementName: string | null; + spec: string; + normalizedName: string; + reason: string | null; + status: PackageRequestStatus; + decidedBy: string | null; + createdAt: string; + decidedAt: string | null; +} + +export interface PackageRequestRow { + id: string; + task_id: string | null; + job_id: string | null; + space_id: string | null; + piece_name: string | null; + movement_name: string | null; + spec: string; + normalized_name: string; + reason: string | null; + status: string; + decided_by: string | null; + created_at: string; + decided_at: string | null; +} + +export function rowToPackageRequest(row: PackageRequestRow): PackageRequest { + return { + id: row.id, + taskId: row.task_id ?? null, + jobId: row.job_id ?? null, + spaceId: row.space_id ?? null, + pieceName: row.piece_name ?? null, + movementName: row.movement_name ?? null, + spec: row.spec, + normalizedName: row.normalized_name, + reason: row.reason ?? null, + status: (row.status as PackageRequestStatus) ?? 'pending', + decidedBy: row.decided_by ?? null, + createdAt: row.created_at, + decidedAt: row.decided_at ?? null, + }; +} + +/** + * Record an agent-declared Python package request (RequestPackage). De-dups + * within the same job: an identical still-pending (job_id, normalized_name) + * row is reused so a retry loop does not spam the table. Returns the row id. + */ +export function recordPackageRequest(db: Database.Database, params: { + id?: string; + taskId?: string | null; + jobId?: string | null; + spaceId?: string | null; + pieceName?: string | null; + movementName?: string | null; + spec: string; + normalizedName: string; + reason?: string | null; + status?: PackageRequestStatus; +}): string { + const status: PackageRequestStatus = params.status ?? 'pending'; + const scopeCol: 'job_id' | 'task_id' = params.jobId ? 'job_id' : 'task_id'; + const scopeVal = params.jobId ?? params.taskId ?? null; + if (scopeVal !== null) { + const existing = db + .prepare( + `SELECT id FROM package_requests + WHERE ${scopeCol} = ? AND normalized_name = ? AND status = 'pending' + LIMIT 1`, + ) + .get(scopeVal, params.normalizedName) as { id: string } | undefined; + if (existing) return existing.id; + } + const id = params.id ?? randomUUID(); + db + .prepare( + `INSERT INTO package_requests + (id, task_id, job_id, space_id, piece_name, movement_name, spec, normalized_name, reason, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + ) + .run( + id, + params.taskId ?? null, + params.jobId ?? null, + params.spaceId ?? null, + params.pieceName ?? null, + params.movementName ?? null, + params.spec, + params.normalizedName, + params.reason ?? null, + status, + ); + return id; +} + +/** All package requests for a task, newest first. */ +export function listPackageRequestsByTask(db: Database.Database, taskId: string): PackageRequest[] { + const rows = db + .prepare(`SELECT * FROM package_requests WHERE task_id = ? ORDER BY created_at DESC, id DESC`) + .all(taskId) as PackageRequestRow[]; + return rows.map(rowToPackageRequest); +} + +/** + * Normalized names declined (denied OR auto_denied) for a job. RequestPackage + * uses this on resume to short-circuit a re-request instead of re-parking — + * otherwise a deny would loop (deny → movement re-runs → re-request → park). + */ +export function listDeclinedPackageNamesByJob(db: Database.Database, jobId: string): string[] { + const rows = db + .prepare( + `SELECT DISTINCT normalized_name FROM package_requests + WHERE job_id = ? AND status IN ('denied', 'auto_denied')`, + ) + .all(jobId) as Array<{ normalized_name: string }>; + return rows.map((r) => r.normalized_name); +} + +/** Fetch a single package request by id. */ +export function getPackageRequest(db: Database.Database, id: string): PackageRequest | null { + const row = db.prepare(`SELECT * FROM package_requests WHERE id = ?`).get(id) as PackageRequestRow | undefined; + return row ? rowToPackageRequest(row) : null; +} + +/** + * Record an approve/deny/auto-deny decision on a still-pending package request. + * Returns false if the row is missing or already decided (idempotent / race + * safe — a concurrent decide won't double-apply, so the install path can gate + * on this to avoid a double install). + */ +export function decidePackageRequest( + db: Database.Database, + id: string, + decision: { status: Exclude; decidedBy?: string | null }, +): boolean { + const res = db + .prepare( + `UPDATE package_requests + SET status = ?, decided_by = ?, decided_at = datetime('now') + WHERE id = ? AND status = 'pending'`, + ) + .run(decision.status, decision.decidedBy ?? null, id); + return res.changes > 0; +} + +/** + * Revert an approved request whose install then FAILED back to pending, so it + * can be retried. CAS on the current 'approved' state (no-op otherwise). The + * approve path claims (pending→approved) BEFORE installing precisely so a + * concurrent deny can never leave a denied request with an installed package; + * on install failure it reverts here. + */ +export function revertPackageRequestToPending(db: Database.Database, id: string): boolean { + const res = db + .prepare( + `UPDATE package_requests + SET status = 'pending', decided_by = NULL, decided_at = NULL + WHERE id = ? AND status = 'approved'`, + ) + .run(id); + return res.changes > 0; +} diff --git a/src/db/repositories/users.ts b/src/db/repositories/users.ts new file mode 100644 index 0000000..ed33f94 --- /dev/null +++ b/src/db/repositories/users.ts @@ -0,0 +1,563 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// users repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { scryptSync, randomBytes, timingSafeEqual } from 'crypto'; +import { v4 as uuidv4 } from 'uuid'; +import { buildVisibilityWhere } from '../../bridge/visibility.js'; +import { utc } from './shared.js'; + +export interface User { + id: string; + email: string; + name: string | null; + avatarUrl: string | null; + role: 'admin' | 'user'; + status: 'active' | 'pending' | 'disabled'; + defaultVisibility: 'private' | 'org' | 'public'; + defaultVisibilityOrgId: string | null; + createdAt: string; + updatedAt: string; +} + + +export interface CreateUserParams { + email: string; + name: string; + role: 'admin' | 'user'; + status: 'active' | 'pending' | 'disabled'; + avatarUrl?: string; +} + + +export interface FindOrCreateByOAuthParams { + provider: string; + providerId: string; + email: string; + name: string; + avatarUrl?: string; +} + + +export interface CreateLocalUserParams { + email: string; + password: string; + role: 'admin' | 'user'; + status: 'active' | 'pending' | 'disabled'; + name?: string; +} + + +export interface LocalOrg { + id: string; + name: string; + createdBy: string | null; + createdAt: string; +} + + +export interface LocalOrgMember { + userId: string; + role: string; +} + + +export interface GiteaOrgInput { + orgId: string; + orgName: string; +} + + +export interface GiteaOrg extends GiteaOrgInput { + fetchedAt: string; +} + + +export interface UserRow { + id: string; + email: string; + name: string | null; + avatar_url: string | null; + role: string; + status: string; + default_visibility: string | null; + default_visibility_org_id: string | null; + created_at: string; + updated_at: string; +} + + +export function rowToUser(row: UserRow): User { + return { + id: row.id, + email: row.email, + name: row.name, + avatarUrl: row.avatar_url, + role: row.role as 'admin' | 'user', + status: row.status as 'active' | 'pending' | 'disabled', + defaultVisibility: (row.default_visibility ?? 'private') as User['defaultVisibility'], + defaultVisibilityOrgId: row.default_visibility_org_id, + createdAt: utc(row.created_at), + updatedAt: utc(row.updated_at), + }; +} + + + export function createUser(db: Database.Database, params: CreateUserParams): User { + const id = uuidv4(); + const now = new Date().toISOString(); + db + .prepare( + `INSERT INTO users (id, email, name, avatar_url, role, status, created_at, updated_at) + VALUES (@id, @email, @name, @avatarUrl, @role, @status, @now, @now)` + ) + .run({ + id, + email: params.email, + name: params.name, + avatarUrl: params.avatarUrl ?? null, + role: params.role, + status: params.status, + now, + }); + const user = getUserById(db, id); + if (!user) throw new Error(`createUser: failed to retrieve created user ${id}`); + return user; + } + + + /** + * Ensure the synthetic 'local' user row exists. No-auth single-user + * deployments own per-user rows under the id 'local' (tasks, jobs, SSH + * connections, DEKs, …). Many of those tables FK to users(id) with + * foreign_keys ON, so the row must exist or the inserts fail — e.g. + * ssh_user_deks → SSH connection creation returned create_failed. + * Idempotent (INSERT OR IGNORE), so it is safe to call on every startup. + * role='admin' mirrors the synthetic 'local' user the HTTP layer injects + * for task-visibility routes in no-auth mode. + */ + export function ensureLocalUser(db: Database.Database): void { + const now = new Date().toISOString(); + db + .prepare( + `INSERT OR IGNORE INTO users (id, email, name, avatar_url, role, status, created_at, updated_at) + VALUES ('local', 'local@localhost', 'local', NULL, 'admin', 'active', @now, @now)` + ) + .run({ now }); + } + + + // ── Local auth (email + password) ───────────────────────────────────── + + /** scrypt hash with a fresh per-user salt. Overwrites any existing credential. */ + export function setLocalPassword(db: Database.Database, userId: string, plainPassword: string): void { + const salt = randomBytes(16).toString('hex'); + const hash = scryptSync(plainPassword, salt, 64).toString('hex'); + const now = new Date().toISOString(); + db + .prepare( + `INSERT INTO local_credentials (user_id, password_hash, salt, updated_at) + VALUES (@userId, @hash, @salt, @now) + ON CONFLICT(user_id) DO UPDATE SET password_hash=@hash, salt=@salt, updated_at=@now`, + ) + .run({ userId, hash, salt, now }); + } + + + /** Constant-time verify. False when the user has no local credential. */ + export function verifyLocalPassword(db: Database.Database, userId: string, plainPassword: string): boolean { + const row = db + .prepare('SELECT password_hash, salt FROM local_credentials WHERE user_id = ?') + .get(userId) as { password_hash: string; salt: string } | undefined; + if (!row) return false; + const expected = Buffer.from(row.password_hash, 'hex'); + const actual = scryptSync(plainPassword, row.salt, expected.length); + return expected.length === actual.length && timingSafeEqual(expected, actual); + } + + + export function hasLocalCredential(db: Database.Database, userId: string): boolean { + return !!db.prepare('SELECT 1 FROM local_credentials WHERE user_id = ?').get(userId); + } + + + /** + * Create a brand-new local account (self-signup or admin-created). The email + * MUST be unused: attaching a password to an existing account would be an + * account-takeover vector, so we reject instead of linking. Linking a local + * credential to an existing OAuth account is a separate, authenticated action + * (not v1 signup). + */ + export function createLocalUser(db: Database.Database, params: CreateLocalUserParams): User { + if (getUserByEmail(db, params.email)) { + throw new Error(`createLocalUser: a user with email ${params.email} already exists`); + } + const user = createUser(db, { + email: params.email, + name: params.name ?? params.email, + role: params.role, + status: params.status, + }); + db + .prepare( + `INSERT OR IGNORE INTO oauth_accounts (id, user_id, provider, provider_id, created_at) + VALUES (@id, @userId, 'local', @providerId, @now)`, + ) + .run({ id: uuidv4(), userId: user.id, providerId: params.email, now: new Date().toISOString() }); + setLocalPassword(db, user.id, params.password); + return user; + } + + + /** + * Idempotently seed the shared system admin under the fixed id `local` — the + * same owner the no-auth path synthesizes. This makes all pre-existing + * `local`-owned data belong to the logged-in admin once local auth is turned + * on, and lets an existing no-auth deployment gain a login mid-stream. + * Re-running updates the password and keeps role=admin/status=active. + */ + export function upsertLocalSystemAdmin(db: Database.Database, params: { email: string; password: string; name?: string }): User { + const LOCAL_ID = 'local'; + const now = new Date().toISOString(); + const existing = getUserById(db, LOCAL_ID); + if (!existing) { + db + .prepare( + `INSERT INTO users (id, email, name, avatar_url, role, status, created_at, updated_at) + VALUES (@id, @email, @name, NULL, 'admin', 'active', @now, @now)`, + ) + .run({ id: LOCAL_ID, email: params.email, name: params.name ?? 'Local Admin', now }); + } else { + db + .prepare(`UPDATE users SET email=@email, role='admin', status='active', updated_at=@now WHERE id=@id`) + .run({ id: LOCAL_ID, email: params.email, now }); + } + db + .prepare( + `INSERT OR IGNORE INTO oauth_accounts (id, user_id, provider, provider_id, created_at) + VALUES (@id, @userId, 'local', @providerId, @now)`, + ) + .run({ id: uuidv4(), userId: LOCAL_ID, providerId: params.email, now }); + setLocalPassword(db, LOCAL_ID, params.password); + const user = getUserById(db, LOCAL_ID); + if (!user) throw new Error('upsertLocalSystemAdmin: failed to retrieve local admin'); + return user; + } + + + // ── Local organizations ─────────────────────────────────────────────── + + export function rowToLocalOrg(r: { id: string; name: string; created_by: string | null; created_at: string }): LocalOrg { + return { id: r.id, name: r.name, createdBy: r.created_by, createdAt: r.created_at }; + } + + + /** Create a local org. id is prefixed `lorg:` so it never collides with a + * Gitea numeric org id (both live in visibility_scope_org_id). */ + export function createLocalOrg(db: Database.Database, name: string, createdBy: string | null): LocalOrg { + const id = `lorg:${uuidv4()}`; + const now = new Date().toISOString(); + db + .prepare(`INSERT INTO local_orgs (id, name, created_by, created_at) VALUES (@id, @name, @createdBy, @now)`) + .run({ id, name, createdBy, now }); + return { id, name, createdBy, createdAt: now }; + } + + + export function getLocalOrg(db: Database.Database, id: string): LocalOrg | null { + const r = db.prepare('SELECT id, name, created_by, created_at FROM local_orgs WHERE id = ?').get(id) as + | { id: string; name: string; created_by: string | null; created_at: string } + | undefined; + return r ? rowToLocalOrg(r) : null; + } + + + export function listLocalOrgs(db: Database.Database): LocalOrg[] { + const rows = db.prepare('SELECT id, name, created_by, created_at FROM local_orgs ORDER BY name COLLATE NOCASE').all() as Array<{ id: string; name: string; created_by: string | null; created_at: string }>; + return rows.map(r => rowToLocalOrg(r)); + } + + + export function renameLocalOrg(db: Database.Database, id: string, name: string): void { + db.prepare('UPDATE local_orgs SET name = ? WHERE id = ?').run(name, id); + } + + /** Tables carrying `visibility_scope_org_id` (org-scoped resources). */ + const ORG_SCOPED_TABLES = ['local_tasks', 'scheduled_tasks', 'jobs']; + + + /** + * Delete a local org. Members cascade via FK. Resources scoped to this org + * (visibility='org', visibility_scope_org_id=id) would become invisible to + * everyone once the org is gone — so first downgrade them to 'private' + * (owner + admin can still see them; no data loss). Atomic. + */ + export function deleteLocalOrg(db: Database.Database, id: string): void { + const tx = db.transaction((orgId: string) => { + for (const table of ORG_SCOPED_TABLES) { + db + .prepare(`UPDATE ${table} SET visibility = 'private', visibility_scope_org_id = NULL WHERE visibility_scope_org_id = ?`) + .run(orgId); + } + db.prepare('DELETE FROM local_orgs WHERE id = ?').run(orgId); + }); + tx(id); + } + + + /** Add or update a member (idempotent — re-add updates the role). */ + export function addOrgMember(db: Database.Database, orgId: string, userId: string, role: string = 'member'): void { + const now = new Date().toISOString(); + db + .prepare( + `INSERT INTO local_org_members (org_id, user_id, role, added_at) + VALUES (@orgId, @userId, @role, @now) + ON CONFLICT(org_id, user_id) DO UPDATE SET role=@role`, + ) + .run({ orgId, userId, role, now }); + } + + + export function removeOrgMember(db: Database.Database, orgId: string, userId: string): void { + db.prepare('DELETE FROM local_org_members WHERE org_id = ? AND user_id = ?').run(orgId, userId); + } + + + export function listOrgMembers(db: Database.Database, orgId: string): LocalOrgMember[] { + const rows = db + .prepare('SELECT user_id, role FROM local_org_members WHERE org_id = ? ORDER BY added_at') + .all(orgId) as Array<{ user_id: string; role: string }>; + return rows.map(r => ({ userId: r.user_id, role: r.role })); + } + + + /** Orgs a user belongs to — merged into session.orgIds so the existing + * provider-agnostic 'org' visibility (buildVisibilityWhere) covers them. */ + export function listUserLocalOrgs(db: Database.Database, userId: string): Array<{ orgId: string; name: string }> { + const rows = db + .prepare( + `SELECT o.id AS org_id, o.name AS name + FROM local_org_members m JOIN local_orgs o ON o.id = m.org_id + WHERE m.user_id = ? ORDER BY o.name COLLATE NOCASE`, + ) + .all(userId) as Array<{ org_id: string; name: string }>; + return rows.map(r => ({ orgId: r.org_id, name: r.name })); + } + + + export function getUserById(db: Database.Database, id: string): User | null { + const row = db + .prepare('SELECT * FROM users WHERE id = ?') + .get(id) as UserRow | undefined; + return row ? rowToUser(row) : null; + } + + + export function getUserByEmail(db: Database.Database, email: string): User | null { + const row = db + .prepare('SELECT * FROM users WHERE email = ?') + .get(email) as UserRow | undefined; + return row ? rowToUser(row) : null; + } + + + export function findOrCreateUserByOAuth(db: Database.Database, params: FindOrCreateByOAuthParams): User { + // 1. Check if oauth_account already exists + const existing = db + .prepare('SELECT user_id FROM oauth_accounts WHERE provider = ? AND provider_id = ?') + .get(params.provider, params.providerId) as { user_id: string } | undefined; + + if (existing) { + const user = getUserById(db, existing.user_id); + if (!user) throw new Error(`findOrCreateUserByOAuth: user ${existing.user_id} not found`); + // Sync mutable profile fields from the provider on every re-login so + // existing users whose name was missing on first login pick it up once + // their Gitea profile is populated. Email upgrade only applies when the + // dummy @gitea.local placeholder is being replaced. + const patch: { email?: string; name?: string; avatarUrl?: string | null } = {}; + if (user.email.endsWith('@gitea.local') && !params.email.endsWith('@gitea.local')) { + patch.email = params.email; + } + if (params.name && params.name !== user.name) patch.name = params.name; + if (params.avatarUrl !== undefined && params.avatarUrl !== user.avatarUrl) { + patch.avatarUrl = params.avatarUrl; + } + if (Object.keys(patch).length > 0) { + updateUser(db, user.id, patch); + const refreshed = getUserById(db, user.id); + if (refreshed) return refreshed; + } + return user; + } + + // 2. Check if user exists by email + let user = getUserByEmail(db, params.email); + + if (!user) { + // 3. Create new user with status=pending + user = createUser(db, { + email: params.email, + name: params.name, + role: 'user', + status: 'pending', + avatarUrl: params.avatarUrl, + }); + } + + // 4. Link oauth_account to user + const oauthId = uuidv4(); + const now = new Date().toISOString(); + db + .prepare( + `INSERT OR IGNORE INTO oauth_accounts (id, user_id, provider, provider_id, created_at) + VALUES (@id, @userId, @provider, @providerId, @now)` + ) + .run({ + id: oauthId, + userId: user.id, + provider: params.provider, + providerId: params.providerId, + now, + }); + + return user; + } + + + export function listUsers(db: Database.Database): User[] { + const rows = db + .prepare('SELECT * FROM users ORDER BY created_at ASC') + .all() as UserRow[]; + return rows.map(row => rowToUser(row)); + } + + + /** + * Active users who share at least one organization with the given org id + * set (Gitea org cache `user_gitea_orgs` OR local org membership + * `local_org_members`). Used by the member-invite picker so a requester only + * sees collaborators inside their own org(s) — exposing the full user list is + * a privacy leak. Empty `orgIds` → empty result. Deduplicated by user id. + */ + export function listActiveUsersInOrgs(db: Database.Database, orgIds: string[]): User[] { + if (orgIds.length === 0) return []; + const placeholders = orgIds.map(() => '?').join(', '); + const rows = db + .prepare( + `SELECT u.* FROM users u + WHERE u.status = 'active' AND u.id IN ( + SELECT user_id FROM user_gitea_orgs WHERE org_id IN (${placeholders}) + UNION + SELECT user_id FROM local_org_members WHERE org_id IN (${placeholders}) + ) + ORDER BY u.created_at ASC`, + ) + .all(...orgIds, ...orgIds) as UserRow[]; + return rows.map(row => rowToUser(row)); + } + + + export function updateUser(db: Database.Database, id: string, updates: { + status?: 'active' | 'pending' | 'disabled'; + role?: 'admin' | 'user'; + email?: string; + name?: string; + avatarUrl?: string | null; + defaultVisibility?: 'private' | 'org' | 'public'; + defaultVisibilityOrgId?: string | null; + }): void { + const setClauses: string[] = ["updated_at = datetime('now')"]; + const params: Record = { id }; + + if (updates.status !== undefined) { + setClauses.push('status = @status'); + params['status'] = updates.status; + } + if (updates.role !== undefined) { + setClauses.push('role = @role'); + params['role'] = updates.role; + } + if (updates.email !== undefined) { + setClauses.push('email = @email'); + params['email'] = updates.email; + } + if (updates.name !== undefined) { + setClauses.push('name = @name'); + params['name'] = updates.name; + } + if (updates.avatarUrl !== undefined) { + setClauses.push('avatar_url = @avatar_url'); + params['avatar_url'] = updates.avatarUrl; + } + if (updates.defaultVisibility !== undefined) { + setClauses.push('default_visibility = @default_visibility'); + params['default_visibility'] = updates.defaultVisibility; + } + if (updates.defaultVisibilityOrgId !== undefined) { + setClauses.push('default_visibility_org_id = @default_visibility_org_id'); + params['default_visibility_org_id'] = updates.defaultVisibilityOrgId; + } + + if (setClauses.length === 1) return; + + db + .prepare(`UPDATE users SET ${setClauses.join(', ')} WHERE id = @id`) + .run(params); + } + + + export function deleteUser(db: Database.Database, id: string): void { + // Never delete the shared `local` system/admin user: it is the no-auth + // fallback owner and owns all single-user-mode data. Deleting it would + // break no-auth mode and orphan every `local`-owned task/job/folder. + if (id === 'local') { + throw new Error('cannot delete the local/system user'); + } + db.prepare('DELETE FROM users WHERE id = ?').run(id); + } + + + export function deleteSessionsByUserId(db: Database.Database, userId: string): void { + // Sessions store passport user info as JSON in sess column + // Delete sessions where sess contains the user id + const rows = db + .prepare('SELECT sid, sess FROM sessions') + .all() as Array<{ sid: string; sess: string }>; + + const toDelete: string[] = []; + for (const row of rows) { + try { + const sess = JSON.parse(row.sess) as Record; + const passport = sess['passport'] as Record | undefined; + if (passport && passport['user'] === userId) { + toDelete.push(row.sid); + } + } catch { + // ignore parse errors + } + } + + if (toDelete.length > 0) { + const placeholders = toDelete.map(() => '?').join(', '); + db.prepare(`DELETE FROM sessions WHERE sid IN (${placeholders})`).run(...toDelete); + } + } + + + export function replaceUserGiteaOrgs(db: Database.Database, userId: string, orgs: GiteaOrgInput[]): void { + const tx = db.transaction((uid: string, items: GiteaOrgInput[]) => { + db.prepare('DELETE FROM user_gitea_orgs WHERE user_id = ?').run(uid); + const insert = db.prepare( + 'INSERT INTO user_gitea_orgs (user_id, org_id, org_name) VALUES (?, ?, ?)' + ); + for (const o of items) insert.run(uid, o.orgId, o.orgName); + }); + tx(userId, orgs); + } + + + export function listUserGiteaOrgs(db: Database.Database, userId: string): GiteaOrg[] { + const rows = db + .prepare('SELECT org_id, org_name, fetched_at FROM user_gitea_orgs WHERE user_id = ? ORDER BY org_name ASC') + .all(userId) as Array<{ org_id: string; org_name: string; fetched_at: string }>; + return rows.map(r => ({ orgId: r.org_id, orgName: r.org_name, fetchedAt: r.fetched_at })); + } diff --git a/src/db/repositories/worker-nodes.ts b/src/db/repositories/worker-nodes.ts new file mode 100644 index 0000000..c1acac0 --- /dev/null +++ b/src/db/repositories/worker-nodes.ts @@ -0,0 +1,140 @@ +// Extracted from src/db/repository.ts (mechanical split — bodies unchanged). +// worker-nodes repository domain. Facade: src/db/repository.ts (Repository class). +import Database from 'better-sqlite3'; +import { encodeTags, decodeTags, decodeAvailableModels, utc } from './shared.js'; + +export interface WorkerNode { + workerId: string; + endpoint: string; + enabled: boolean; + healthy: boolean; + roles: string[]; + availableModels: string[]; + inflightJobs: number; + maxConcurrency: number; + lastError: string | null; + lastSeenAt: string; + updatedAt: string; +} + + +export interface UpsertWorkerNodeParams { + workerId: string; + endpoint: string; + enabled: boolean; + healthy: boolean; + roles: string[]; + availableModels?: string[]; + inflightJobs?: number; + maxConcurrency?: number; + lastError?: string | null; +} + + +export interface WorkerNodeRow { + worker_id: string; + endpoint: string; + enabled: number; + healthy: number; + profile_tags: string; + task_class_tags: string; + available_models: string | null; + inflight_jobs: number; + max_concurrency: number; + last_error: string | null; + last_seen_at: string; + updated_at: string; +} + + +export function rowToWorkerNode(row: WorkerNodeRow): WorkerNode { + return { + workerId: row.worker_id, + endpoint: row.endpoint, + enabled: row.enabled === 1, + healthy: row.healthy === 1, + roles: decodeTags(row.profile_tags), + availableModels: decodeAvailableModels(row.available_models), + inflightJobs: row.inflight_jobs, + maxConcurrency: row.max_concurrency, + lastError: row.last_error, + lastSeenAt: utc(row.last_seen_at), + updatedAt: utc(row.updated_at), + }; +} + + + export async function upsertWorkerNode(db: Database.Database, params: UpsertWorkerNodeParams): Promise { + const now = new Date().toISOString(); + db.prepare(` + INSERT INTO worker_nodes ( + worker_id, endpoint, enabled, healthy, profile_tags, task_class_tags, available_models, + inflight_jobs, max_concurrency, last_error, last_seen_at, updated_at + ) VALUES ( + @workerId, @endpoint, @enabled, @healthy, @roleTags, @roleTags, @availableModels, + @inflightJobs, @maxConcurrency, @lastError, @now, @now + ) + ON CONFLICT(worker_id) DO UPDATE SET + endpoint = excluded.endpoint, + enabled = excluded.enabled, + healthy = excluded.healthy, + profile_tags = excluded.profile_tags, + task_class_tags = excluded.task_class_tags, + available_models = excluded.available_models, + inflight_jobs = excluded.inflight_jobs, + max_concurrency = excluded.max_concurrency, + last_error = excluded.last_error, + last_seen_at = excluded.last_seen_at, + updated_at = excluded.updated_at + `).run({ + workerId: params.workerId, + endpoint: params.endpoint, + enabled: params.enabled ? 1 : 0, + healthy: params.healthy ? 1 : 0, + roleTags: encodeTags(params.roles), + availableModels: JSON.stringify(params.availableModels ?? []), + inflightJobs: params.inflightJobs ?? 0, + maxConcurrency: params.maxConcurrency ?? 1, + lastError: params.lastError ?? null, + now, + }); + } + + + export async function updateWorkerNodeHealth(db: Database.Database, workerId: string, updates: { healthy: boolean; lastError?: string | null; inflightJobs?: number; availableModels?: string[]; enabled?: boolean }): Promise { + const setClauses = [ + 'healthy = @healthy', + 'last_error = @lastError', + "last_seen_at = @now", + "updated_at = @now", + ]; + const params: Record = { + workerId, + healthy: updates.healthy ? 1 : 0, + lastError: updates.lastError ?? null, + now: new Date().toISOString(), + }; + + if (updates.inflightJobs !== undefined) { + setClauses.push('inflight_jobs = @inflightJobs'); + params['inflightJobs'] = updates.inflightJobs; + } + if (updates.availableModels !== undefined) { + setClauses.push('available_models = @availableModels'); + params['availableModels'] = JSON.stringify(updates.availableModels); + } + if (updates.enabled !== undefined) { + setClauses.push('enabled = @enabled'); + params['enabled'] = updates.enabled ? 1 : 0; + } + + db.prepare(`UPDATE worker_nodes SET ${setClauses.join(', ')} WHERE worker_id = @workerId`).run(params); + } + + + export async function getWorkerNode(db: Database.Database, workerId: string): Promise { + const row = db + .prepare('SELECT * FROM worker_nodes WHERE worker_id = ?') + .get(workerId) as WorkerNodeRow | undefined; + return row ? rowToWorkerNode(row) : null; + } diff --git a/src/db/repository.a2a-revocation.test.ts b/src/db/repository.a2a-revocation.test.ts index a3fbac5..e89dd4e 100644 --- a/src/db/repository.a2a-revocation.test.ts +++ b/src/db/repository.a2a-revocation.test.ts @@ -70,6 +70,55 @@ describe('A2A revocation repo primitives', () => { expect(repo.listLiveA2aDelegationsForClient('c1', now).map(d => d.id)).toEqual(['live']); }); + it('countNonTerminalA2aTasksByGrant counts only non-terminal tasks for the grant', async () => { + // Seed: working + completed + rejected + input-required for g1, plus working for g2 + repo.saveA2aTask({ id: 'cnt-working', contextId: null, jobId: null, localTaskId: null, + payload: { status: { state: 'working' } }, grantId: 'g1' }); + repo.saveA2aTask({ id: 'cnt-completed', contextId: null, jobId: null, localTaskId: null, + payload: { status: { state: 'completed' } }, grantId: 'g1' }); + repo.saveA2aTask({ id: 'cnt-rejected', contextId: null, jobId: null, localTaskId: null, + payload: { status: { state: 'rejected' } }, grantId: 'g1' }); + repo.saveA2aTask({ id: 'cnt-input-required', contextId: null, jobId: null, localTaskId: null, + payload: { status: { state: 'input-required' } }, grantId: 'g1' }); + repo.saveA2aTask({ id: 'cnt-g2-working', contextId: null, jobId: null, localTaskId: null, + payload: { status: { state: 'working' } }, grantId: 'g2' }); + // g1: only the 'working' task counts — completed, rejected, and input-required are excluded. + // input-required is parked (no live worker/LLM) so must not block new task creation. + expect(repo.countNonTerminalA2aTasksByGrant('g1')).toBe(1); + // g2: one non-terminal task + expect(repo.countNonTerminalA2aTasksByGrant('g2')).toBe(1); + // unknown grant: zero + expect(repo.countNonTerminalA2aTasksByGrant('nope')).toBe(0); + + // listNonTerminalA2aTasksByGrant must still include input-required for revocation coverage. + const listed = repo.listNonTerminalA2aTasksByGrant('g1').map(r => r.id); + expect(listed).toContain('cnt-working'); + expect(listed).toContain('cnt-input-required'); + expect(listed).not.toContain('cnt-completed'); + expect(listed).not.toContain('cnt-rejected'); + }); + + it('countNonTerminalA2aTasksByGrant counts an input-required task once its job resumes (no ASK-park bypass)', async () => { + // A parked input-required task (job still waiting_human) must NOT count — holds no capacity. + const parked = await repo.createJob({ repo: 'local/task-1', issueNumber: 1, instruction: 'p' }); + (repo as unknown as { db: { prepare: (s: string) => { run: (...a: unknown[]) => void } } }) + .db.prepare("UPDATE jobs SET status = 'waiting_human' WHERE id = ?").run(parked.id); + repo.saveA2aTask({ id: 'ir-parked', contextId: null, jobId: parked.id, localTaskId: null, + payload: { status: { state: 'input-required' } }, grantId: 'gx' }); + expect(repo.countNonTerminalA2aTasksByGrant('gx')).toBe(0); + + // The human answers → job resumes to 'running' while the a2a_tasks payload still reads + // 'input-required'. That resumed task consumes capacity again and MUST count. + (repo as unknown as { db: { prepare: (s: string) => { run: (...a: unknown[]) => void } } }) + .db.prepare("UPDATE jobs SET status = 'running' WHERE id = ?").run(parked.id); + expect(repo.countNonTerminalA2aTasksByGrant('gx')).toBe(1); + + // An input-required task whose job is gone (jobId null) stays excluded — nothing running. + repo.saveA2aTask({ id: 'ir-nojob', contextId: null, jobId: null, localTaskId: null, + payload: { status: { state: 'input-required' } }, grantId: 'gx' }); + expect(repo.countNonTerminalA2aTasksByGrant('gx')).toBe(1); + }); + // sonnet review Finding 2: 二重失効で元の revoked_at を上書きしない it('revokeA2aDelegation does not overwrite an existing revoked_at', () => { const first = '2026-07-02T00:00:00.000Z'; diff --git a/src/db/repository.package-requests.test.ts b/src/db/repository.package-requests.test.ts new file mode 100644 index 0000000..e70b25e --- /dev/null +++ b/src/db/repository.package-requests.test.ts @@ -0,0 +1,98 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdtempSync, rmSync } from 'fs'; +import { join } from 'path'; +import { tmpdir } from 'os'; +import { Repository } from './repository.js'; + +let tempDir = ''; +let repo: Repository; + +beforeEach(() => { + tempDir = mkdtempSync(join(tmpdir(), 'maestro-pkgreq-')); + repo = new Repository(join(tempDir, 'db.sqlite')); +}); +afterEach(() => { + repo.close(); + rmSync(tempDir, { recursive: true, force: true }); +}); + +describe('package_requests repository', () => { + it('records, fetches and lists a request', () => { + const id = repo.recordPackageRequest({ + taskId: '100', jobId: 'job-1', spaceId: 'space-1', + pieceName: 'chat', movementName: 'work', + spec: 'requests==2.32.3', normalizedName: 'requests', reason: 'http', + }); + const got = repo.getPackageRequest(id); + expect(got?.spec).toBe('requests==2.32.3'); + expect(got?.normalizedName).toBe('requests'); + expect(got?.status).toBe('pending'); + expect(got?.spaceId).toBe('space-1'); + expect(got?.movementName).toBe('work'); + + const list = repo.listPackageRequestsByTask('100'); + expect(list).toHaveLength(1); + expect(list[0].id).toBe(id); + }); + + it('de-dups an identical still-pending request within the same job', () => { + const a = repo.recordPackageRequest({ taskId: '1', jobId: 'j', spec: 'requests', normalizedName: 'requests' }); + const b = repo.recordPackageRequest({ taskId: '1', jobId: 'j', spec: 'requests', normalizedName: 'requests' }); + expect(b).toBe(a); + expect(repo.listPackageRequestsByTask('1')).toHaveLength(1); + }); + + it('does NOT de-dup once the first is decided (a fresh request can be raised)', () => { + const a = repo.recordPackageRequest({ taskId: '1', jobId: 'j', spec: 'numpy', normalizedName: 'numpy' }); + expect(repo.decidePackageRequest(a, { status: 'denied', decidedBy: 'u1' })).toBe(true); + const b = repo.recordPackageRequest({ taskId: '1', jobId: 'j', spec: 'numpy', normalizedName: 'numpy' }); + expect(b).not.toBe(a); + expect(repo.listPackageRequestsByTask('1')).toHaveLength(2); + }); + + it('decide is a CAS: a second decide on the same row is a no-op', () => { + const id = repo.recordPackageRequest({ taskId: '1', jobId: 'j', spec: 'pandas', normalizedName: 'pandas' }); + expect(repo.decidePackageRequest(id, { status: 'approved', decidedBy: 'u1' })).toBe(true); + expect(repo.decidePackageRequest(id, { status: 'denied', decidedBy: 'u2' })).toBe(false); + const got = repo.getPackageRequest(id); + expect(got?.status).toBe('approved'); + expect(got?.decidedBy).toBe('u1'); + expect(got?.decidedAt).not.toBeNull(); + }); + + it('revertPackageRequestToPending un-claims an approved request (for install-failure retry)', () => { + const id = repo.recordPackageRequest({ taskId: '1', jobId: 'j', spec: 'httpx', normalizedName: 'httpx' }); + // claim + expect(repo.decidePackageRequest(id, { status: 'approved', decidedBy: 'u1' })).toBe(true); + // revert (install failed) + expect(repo.revertPackageRequestToPending(id)).toBe(true); + const got = repo.getPackageRequest(id); + expect(got?.status).toBe('pending'); + expect(got?.decidedBy).toBeNull(); + expect(got?.decidedAt).toBeNull(); + // can be claimed again + expect(repo.decidePackageRequest(id, { status: 'approved', decidedBy: 'u2' })).toBe(true); + // revert only touches 'approved' rows: a denied row is untouched + const denied = repo.recordPackageRequest({ taskId: '1', jobId: 'k', spec: 'numpy', normalizedName: 'numpy' }); + repo.decidePackageRequest(denied, { status: 'denied', decidedBy: 'u1' }); + expect(repo.revertPackageRequestToPending(denied)).toBe(false); + expect(repo.getPackageRequest(denied)?.status).toBe('denied'); + }); + + it('listDeclinedPackageNamesByJob returns denied + auto_denied names for the job only', () => { + const denied = repo.recordPackageRequest({ taskId: '1', jobId: 'jA', spec: 'httpx', normalizedName: 'httpx' }); + repo.decidePackageRequest(denied, { status: 'denied', decidedBy: 'u1' }); + const auto = repo.recordPackageRequest({ taskId: '1', jobId: 'jA', spec: 'boto3', normalizedName: 'boto3', status: 'auto_denied' }); + expect(auto).toBeTruthy(); + // pending + other-job requests are excluded + repo.recordPackageRequest({ taskId: '1', jobId: 'jA', spec: 'numpy', normalizedName: 'numpy' }); + repo.recordPackageRequest({ taskId: '1', jobId: 'jB', spec: 'flask', normalizedName: 'flask', status: 'denied' }); + const names = repo.listDeclinedPackageNamesByJob('jA').sort(); + expect(names).toEqual(['boto3', 'httpx']); + }); + + it('resumePackageRequestJob only re-queues a job parked for package_request', () => { + // No such job → 0 changes (safe no-op). + expect(repo.resumePackageRequestJob('missing-job')).toBe(0); + }); +}); diff --git a/src/db/repository.transcript-only-io.test.ts b/src/db/repository.transcript-only-io.test.ts new file mode 100644 index 0000000..e44d924 --- /dev/null +++ b/src/db/repository.transcript-only-io.test.ts @@ -0,0 +1,29 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdtempSync, rmSync } from 'fs'; +import { join } from 'path'; +import { tmpdir } from 'os'; +import { Repository } from './repository.js'; + +let tempDir = ''; +function makeRepo(): Repository { + return new Repository(join(tempDir, 'orchestrator.db')); +} + +beforeEach(() => { tempDir = mkdtempSync(join(tmpdir(), 'maestro-tconly-')); }); +afterEach(() => { rmSync(tempDir, { recursive: true, force: true }); }); + +describe('makeTranscriptOnlyConversationIO', () => { + it('yields empty comments and passes the transcript path through', async () => { + const repo = makeRepo(); + const io = repo.makeTranscriptOnlyConversationIO('/tmp/does-not-matter/transcript.jsonl'); + expect(await io.listComments()).toEqual([]); + expect(io.transcriptPath).toBe('/tmp/does-not-matter/transcript.jsonl'); + }); + + it('accepts an undefined transcript path (comments still empty)', async () => { + const repo = makeRepo(); + const io = repo.makeTranscriptOnlyConversationIO(undefined); + expect(await io.listComments()).toEqual([]); + expect(io.transcriptPath).toBeUndefined(); + }); +}); diff --git a/src/db/repository.ts b/src/db/repository.ts index 1fd5495..e2b6ae7 100644 --- a/src/db/repository.ts +++ b/src/db/repository.ts @@ -1,1337 +1,51 @@ import Database from 'better-sqlite3'; -import { readFileSync, rmSync, existsSync, realpathSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join, normalize, sep } from 'path'; -import { randomUUID, scryptSync, randomBytes, timingSafeEqual } from 'crypto'; -import { v4 as uuidv4 } from 'uuid'; import { logger } from '../logger.js'; -import { buildVisibilityWhere, buildSpaceVisibilityWhere } from '../bridge/visibility.js'; -import { spaceColor } from '../spaces/space-color.js'; -import { buildTitleFromGoal } from '../title-generation.js'; -import { ensureTaskCommentFts } from './migrate.js'; -import { normalizeCommentToIndexText } from '../engine/task-index/normalize.js'; +import * as schemaRepo from './repositories/schema.js'; +import * as jobsRepo from './repositories/jobs.js'; +import { JobStatus, Job, CreateJobParams, SubtaskInfo } from './repositories/jobs.js'; +import * as spacesRepo from './repositories/spaces.js'; +import { Space, SpaceMemberRoleValue, SpaceMember, SpaceInviteRole, SpaceInvite, CreateSpaceParams } from './repositories/spaces.js'; +import * as usersRepo from './repositories/users.js'; +import { User, CreateUserParams, FindOrCreateByOAuthParams, CreateLocalUserParams, LocalOrg, LocalOrgMember, GiteaOrgInput, GiteaOrg } from './repositories/users.js'; +import * as workerNodesRepo from './repositories/worker-nodes.js'; +import { WorkerNode, UpsertWorkerNodeParams } from './repositories/worker-nodes.js'; +import * as scheduledTasksRepo from './repositories/scheduled-tasks.js'; +import { ScheduledTask, CreateScheduledTaskParams, UpdateScheduledTaskParams } from './repositories/scheduled-tasks.js'; +import * as a2aRepo from './repositories/a2a.js'; +import { A2aClientRow, A2aDelegationRow } from './repositories/a2a.js'; +import * as reflectionRepo from './repositories/reflection.js'; +import * as toolRequestsRepo from './repositories/tool-requests.js'; +import { ToolRequestCategory, ToolRequestStatus, ToolRequest, ToolRequestAggregate, PackageRequest, PackageRequestStatus } from './repositories/tool-requests.js'; +import * as gatewayRepo from './repositories/gateway.js'; +import { GatewayVirtualKeySource, GatewayVirtualKey, GatewayKeyUsage } from './repositories/gateway.js'; +import * as llmUsageRepo from './repositories/llm-usage.js'; +import { LlmUsageIncrement, LlmUsageDailyAgg, LlmUsageHourlyIncrement, LlmUsageHourlyRow } from './repositories/llm-usage.js'; +import * as notificationsRepo from './repositories/notifications.js'; +import { PushSubscriptionRecord, UpsertPushSubscriptionInput, NotificationPrefs, NotificationPrefsUpdate } from './repositories/notifications.js'; +import * as auditRepo from './repositories/audit.js'; +import * as appSharesRepo from './repositories/app-shares.js'; +import * as provenanceRepo from './repositories/provenance.js'; +import * as localTasksRepo from './repositories/local-tasks.js'; +import { LocalTask, MissionBrief, LocalTaskComment, CreateLocalTaskParams } from './repositories/local-tasks.js'; +export { MISSION_BRIEF_FIELDS } from './repositories/local-tasks.js'; +export type { TitleSource, LocalTask, MissionBriefField, MissionBrief, LocalTaskComment, CreateLocalTaskParams } from './repositories/local-tasks.js'; +import * as taskSearchRepo from './repositories/task-search.js'; +import * as calendarRepo from './repositories/calendar.js'; +import { CalendarEvent, CalendarDayCounts, CalendarDayTask, CrossSpaceCalendarMonth } from './repositories/calendar.js'; +export { enumerateLocalDays } from './repositories/calendar.js'; +export type { CalendarEvent, CalendarDayCounts, CalendarDayTask, CrossCalendarSpace, CrossSpaceCalendarMonth } from './repositories/calendar.js'; +export type { NotifyEventType, PushSubscriptionRecord, UpsertPushSubscriptionInput, NotificationPrefs, NotificationPrefsUpdate } from './repositories/notifications.js'; +export type { LlmUsageIncrement, LlmUsageDailyAgg, LlmUsageHourlyIncrement, LlmUsageHourlyRow } from './repositories/llm-usage.js'; +export type { GatewayVirtualKeySource, GatewayVirtualKey, GatewayKeyUsage } from './repositories/gateway.js'; +export type { ToolRequestCategory, ToolRequestStatus, ToolRequest, ToolRequestAggregate, PackageRequest, PackageRequestStatus } from './repositories/tool-requests.js'; +export type { A2aClientRow, A2aDelegationRow } from './repositories/a2a.js'; +export type { ScheduledTaskKind, ScheduledTask, CreateScheduledTaskParams, UpdateScheduledTaskParams } from './repositories/scheduled-tasks.js'; +export type { WorkerNode, UpsertWorkerNodeParams } from './repositories/worker-nodes.js'; +export type { User, CreateUserParams, FindOrCreateByOAuthParams, CreateLocalUserParams, LocalOrg, LocalOrgMember, GiteaOrgInput, GiteaOrg } from './repositories/users.js'; +export type { Space, SpaceMemberRoleValue, SpaceMember, SpaceInviteRole, SpaceInvite, CreateSpaceParams } from './repositories/spaces.js'; +export type { JobStatus, JobRole, JobProfile, TaskClass, Job, CreateJobParams, SubtaskInfo } from './repositories/jobs.js'; +export { localTaskRepoName } from './repositories/shared.js'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -/** - * Shared SQL fragments for LocalTask read queries. - * - * getLocalTask / listLocalTasks / getLocalTaskByShareToken all need to expose - * the owner's display name and the org display name of `visibility_scope_org_id`. - * These constants keep the three queries in sync. - * - * Usage: splice into the SELECT list and the FROM-clause joins, e.g. - * - * SELECT lt.*, ${LOCAL_TASK_DISPLAY_SELECT} - * FROM local_tasks lt - * ${LOCAL_TASK_DISPLAY_JOIN} - * WHERE ... - * - * A correlated subquery (`MIN(org_name)`) is used instead of a JOIN because - * user_gitea_orgs is keyed per-user, and we only need any one display name - * for the org id — this avoids row-multiplication across the join. - */ -const LOCAL_TASK_DISPLAY_SELECT = ` - u.name AS owner_name, - COALESCE( - (SELECT MIN(org_name) FROM user_gitea_orgs WHERE org_id = lt.visibility_scope_org_id), - (SELECT name FROM local_orgs WHERE id = lt.visibility_scope_org_id) - ) AS visibility_scope_org_name -`.trim(); -const LOCAL_TASK_DISPLAY_JOIN = `LEFT JOIN users u ON u.id = lt.owner_id`; - -const SCHEDULED_TASK_DISPLAY_SELECT = ` - u.name AS owner_name, - COALESCE( - (SELECT MIN(org_name) FROM user_gitea_orgs WHERE org_id = st.visibility_scope_org_id), - (SELECT name FROM local_orgs WHERE id = st.visibility_scope_org_id) - ) AS visibility_scope_org_name -`.trim(); -const SCHEDULED_TASK_DISPLAY_JOIN = `LEFT JOIN users u ON u.id = st.owner_id`; - -export type JobStatus = - | 'queued' - | 'dispatching' - | 'running' - | 'succeeded' - | 'failed' - | 'retry' - | 'cancelled' - | 'waiting_human' - | 'waiting_subtasks'; - -export type JobRole = 'auto' | 'fast' | 'quality' | 'reflection'; -/** @deprecated Use JobRole instead */ -export type JobProfile = JobRole; -/** @deprecated Removed — taskClass is no longer a separate concept */ -export type TaskClass = 'auto' | 'low_level' | 'high_level'; - -export interface Job { - id: string; - repo: string; - issueNumber: number; - prNumber: number | null; - status: JobStatus; - pieceName: string; - currentMovement: string | null; - currentActivity: string | null; - instruction: string; - branchName: string | null; - worktreePath: string | null; - attempt: number; - maxAttempts: number; - nextRetryAt: string | null; - errorSummary: string | null; - abortReason: string | null; - resumeMovement: string | null; - waitReason: string | null; - askCount: number; - workerId: string | null; - /** - * Physical backend id (e.g. LiteLLM deployment name) that handled - * this job's LLM calls when running through a proxy worker. Set on - * the first LLM call and never overwritten — sticky-backend policy - * per design Open Question #3. NULL for direct workers and for jobs - * that haven't issued any proxied LLM call yet. - */ - lastBackendId: string | null; - parentJobId: string | null; - continuedFromJobId: string | null; - subtaskDepth: number; - requiredRole: JobRole; - /** @deprecated Use requiredRole */ - requiredProfile: JobRole; - ownerId: string | null; - visibility: 'private' | 'org' | 'public'; - visibilityScopeOrgId: string | null; - contextPromptTokens: number | null; - contextLimitTokens: number | null; - contextUpdatedAt: string | null; - browserSessionProfileId?: number | null; - taskKind: 'agent' | 'reflection'; - payload: string | null; - /** 所属スペース。親タスク/親ジョブから継承される(Task 5 で設定)。null は個人スペース。 */ - spaceId: string | null; - /** 実行ログ root(計画5)。NULL = 後方互換で workspacePath/logs に解決。サブタスクが親から継承する。 */ - runtimeDir: string | null; - createdAt: string; - updatedAt: string; -} - -export interface CreateJobParams { - repo: string; - issueNumber: number; - instruction: string; - pieceName?: string; - maxAttempts?: number; - resumeMovement?: string | null; - askCount?: number; - role?: JobRole; - /** @deprecated Use role instead */ - profile?: JobRole; - parentJobId?: string | null; - continuedFromJobId?: string | null; - subtaskDepth?: number; - ownerId?: string | null; - visibility?: 'private' | 'org' | 'public'; - visibilityScopeOrgId?: string | null; - browserSessionProfileId?: number | null; - taskKind?: 'agent' | 'reflection'; - payload?: string; - /** 所属スペース(spec §5.7 親→子継承)。NULL は owner 個人スペースに解決。 */ - spaceId?: string | null; - /** 実行ログ root(計画5)。NULL = 後方互換で workspacePath/logs に解決。 */ - runtimeDir?: string | null; -} - -export interface SubtaskInfo { - id: string; - issueNumber: number; - status: JobStatus; - instruction: string; - worktreePath: string | null; - createdAt: string; - updatedAt: string; - children?: SubtaskInfo[]; - childCount?: number; - childCompleted?: number; -} - -export type TitleSource = 'auto' | 'agent' | 'user'; - -export interface LocalTask { - id: number; - title: string; - /** Provenance of `title`. 'user' is never auto-overwritten by the agent. */ - titleSource: TitleSource; - body: string; - pieceName: string; - profile: 'auto' | 'fast' | 'quality' | string; - outputFormat: 'text' | 'markdown' | 'json' | string; - askPolicy: 'low' | 'high' | string; - priority: 'low' | 'medium' | 'high' | string; - state: 'open' | 'closed' | string; - workspacePath: string | null; - /** 実行先ワークスペースの種類。persistent=スペース共有ツリー / ephemeral=使い捨て。 */ - workspaceMode: 'persistent' | 'ephemeral'; - ownerId: string | null; - ownerName?: string | null; - visibility: 'private' | 'org' | 'public'; - visibilityScopeOrgId: string | null; - visibilityScopeOrgName?: string | null; - /** 所属スペース。null は owner の個人スペース(=現行の data/users/{owner})に解決される。 */ - spaceId: string | null; - /** 実行ログ root(計画5)。null = 後方互換で workspacePath/logs に解決。 */ - runtimeDir: string | null; - createdAt: string; - updatedAt: string; - feedbackRating: 'good' | 'bad' | null; - feedbackTags: string[] | null; - feedbackComment: string | null; - feedbackAt: string | null; - shareToken: string | null; - sharedAt: string | null; - browserSessionProfileId?: number | null; - /** - * Mission Brief: per-task pinned memo. Carries goal / done / open / - * clarifications. Always rendered at the top of every movement's - * system prompt. The LLM updates it via mission_update; the user - * edits it from the Overview tab. - */ - missionBrief: MissionBrief | null; - /** Per-task runtime options (e.g. { mcpDisabled, skillsDisabled }). */ - options: Record; - latestJob?: Job | null; - subtasks?: SubtaskInfo[]; - subtaskCount?: number; - subtaskCompleted?: number; -} - -/** - * Allowed widget kinds. 'markdown' is the original Side Info Panel widget - * (PR #308). 'node-status' was added in Phase B (2026-05) and ignores - * markdown_content — it renders BackendStatusRegistry data live. The union - * intentionally lives here so the API, tools and UI all share a single - * source of truth. - */ -/** - * スペース・カレンダーの予定エントリ(passive。実行はしない)。 - * spec: docs/superpowers/specs/2026-06-19-space-calendar-design.md §データモデル - */ -export interface CalendarEvent { - id: number; - spaceId: string; - ownerId: string | null; - date: string; // 開始日 YYYY-MM-DD(ローカル日付) - endDate: string | null; // 終了日 YYYY-MM-DD。null = date と同じ(単日) - time: string | null; // 開始 HH:MM。null = 終日 - endTime: string | null; // 終了 HH:MM。null = 終了時刻なし(time が null なら常に null) - title: string; - description: string | null; - createdBy: 'user' | 'agent'; - sourceTaskId: number | null; - createdAt: string; - updatedAt: string; -} - -interface CalendarEventRow { - id: number; - space_id: string; - owner_id: string | null; - date: string; - end_date: string | null; - time: string | null; - end_time: string | null; - title: string; - description: string | null; - created_by: string; - source_task_id: number | null; - created_at: string; - updated_at: string; -} - -/** - * [start, end](両端含む)のローカル暦日を 'YYYY-MM-DD' 配列で返す。 - * end < start は空配列。複数日イベントを各日に展開する集計で使う。 - */ -export function enumerateLocalDays(start: string, end: string): string[] { - if (end < start) return []; - const out: string[] = []; - const [ys, ms, ds] = start.split('-').map(Number); - const [ye, me, de] = end.split('-').map(Number); - let cur = Date.UTC(ys, ms - 1, ds); - const last = Date.UTC(ye, me - 1, de); - // 安全弁: 異常な範囲で無限ループしないよう上限を設ける(最大 ~2 年)。 - for (let i = 0; cur <= last && i < 800; i++) { - const d = new Date(cur); - out.push(`${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}-${String(d.getUTCDate()).padStart(2, '0')}`); - cur += 86_400_000; - } - return out; -} - -function rowToCalendarEvent(row: CalendarEventRow): CalendarEvent { - return { - id: row.id, - spaceId: row.space_id, - ownerId: row.owner_id ?? null, - date: row.date, - endDate: row.end_date ?? null, - time: row.time ?? null, - endTime: row.end_time ?? null, - title: row.title, - description: row.description ?? null, - createdBy: row.created_by === 'agent' ? 'agent' : 'user', - sourceTaskId: row.source_task_id ?? null, - createdAt: row.created_at, - updatedAt: row.updated_at, - }; -} - -/** カレンダー月ビューの1日分の集計カウント。 */ -export interface CalendarDayCounts { - taskCount: number; - eventCount: number; -} - -export type ToolRequestCategory = 'requested' | 'blocked' | 'unknown'; -export type ToolRequestStatus = 'pending' | 'approved' | 'denied' | 'auto_denied'; - -/** 現在の movement で使えないツールの要求記録(能動 RequestTool / 受動 block 捕捉)。 */ -export interface ToolRequest { - id: string; - taskId: string | null; - jobId: string | null; - spaceId: string | null; - pieceName: string; - movementName: string; - toolName: string; - reason: string | null; - category: ToolRequestCategory; - status: ToolRequestStatus; - grantScope: 'task' | 'piece' | null; - decidedBy: string | null; - createdAt: string; - decidedAt: string | null; -} - -interface ToolRequestRow { - id: string; - task_id: string | null; - job_id: string | null; - space_id: string | null; - piece_name: string; - movement_name: string; - tool_name: string; - reason: string | null; - category: string; - status: string; - grant_scope: string | null; - decided_by: string | null; - created_at: string; - decided_at: string | null; -} - -export interface A2aClientRow { - clientId: string; - name: string; - redirectUris: string[]; - secretHash: string | null; - tokenEndpointAuthMethod: string; - agentCardUrl: string | null; - issuer: string | null; - keyFingerprint: string | null; - status: string; - createdBy: string | null; - createdAt?: string; -} - -export interface A2aDelegationRow { - id: string; - userId: string; - clientId: string; - grantId: string | null; - grantedSpaceIds: string[]; - grantedSkills: string[]; - audience: string | null; - expiresAt: string | null; - revokedAt: string | null; - createdAt?: string; -} - -function rowToToolRequest(row: ToolRequestRow): ToolRequest { - return { - id: row.id, - taskId: row.task_id ?? null, - jobId: row.job_id ?? null, - spaceId: row.space_id ?? null, - pieceName: row.piece_name, - movementName: row.movement_name, - toolName: row.tool_name, - reason: row.reason ?? null, - category: (row.category as ToolRequestCategory) ?? 'requested', - status: (row.status as ToolRequestStatus) ?? 'pending', - grantScope: (row.grant_scope as 'task' | 'piece' | null) ?? null, - decidedBy: row.decided_by ?? null, - createdAt: row.created_at, - decidedAt: row.decided_at ?? null, - }; -} - -/** DB row shape for workspace_file_provenance. */ -interface WorkspaceFileProvenanceRow { - space_id: string | null; - workspace_path: string; - rel_path: string; - created_by_task_id: number | null; - created_by_job_id: string | null; - created_by_piece: string | null; - created_by_movement: string | null; - source_kind: string; - first_seen_at: string | null; - last_modified_by_task_id: number | null; - last_modified_by_job_id: string | null; - last_modified_at: string | null; - checksum: string | null; - note: string | null; -} - -function rowToFileProvenance( - row: WorkspaceFileProvenanceRow, -): import('../engine/tools/core.js').FileProvenanceRecord { - return { - relPath: row.rel_path, - sourceKind: (row.source_kind as import('../engine/tools/core.js').FileProvenanceSourceKind) ?? 'unknown', - createdByTaskId: row.created_by_task_id ?? null, - createdByJobId: row.created_by_job_id ?? null, - createdByPiece: row.created_by_piece ?? null, - createdByMovement: row.created_by_movement ?? null, - firstSeenAt: row.first_seen_at ?? null, - lastModifiedByTaskId: row.last_modified_by_task_id ?? null, - lastModifiedByJobId: row.last_modified_by_job_id ?? null, - lastModifiedAt: row.last_modified_at ?? null, - checksum: row.checksum ?? null, - note: row.note ?? null, - }; -} - -/** Escape LIKE wildcards so a caller-supplied prefix matches literally. */ -function escapeLike(s: string): string { - return s.replace(/[\\%_]/g, (c) => '\\' + c); -} - -/** ピース集計の1行(tool_name×category ごとの件数)。 */ -export interface ToolRequestAggregate { - toolName: string; - category: ToolRequestCategory; - count: number; - lastSeen: string; - sampleReason: string | null; -} - -/** カレンダー日詳細のタスク行(軽量・最新 job 状態付き)。 */ -export interface CalendarDayTask { - id: number; - title: string; - state: string; - status: string | null; // 最新 job の status(無ければ null) - createdAt: string; -} - -/** 横断カレンダーの凡例 1 行(スペースとその表示色)。 */ -export interface CrossCalendarSpace { - id: string; - name: string; - color: string; // brand_color、無ければ spaceColor(id) で決定論的に導出 -} - -/** 横断カレンダー月ビュー。`days[date][spaceId]` = そのスペースのその日の集計。 */ -export interface CrossSpaceCalendarMonth { - days: Record>; - events: CalendarEvent[]; // 各イベントは spaceId を持つ(CalendarEvent.spaceId) - spaces: CrossCalendarSpace[]; -} - -/** - * Ordered Mission Brief field list. Single source of truth for parse / update - * / API whitelist / MissionUpdate tool so a future field is a one-line - * addition here (plus its typed interface member). Legacy fields come first so - * JSON key order and title-derivation (goal only) stay unchanged. - */ -export const MISSION_BRIEF_FIELDS = [ - 'goal', - 'done', - 'open', - 'clarifications', - 'user_constraints', - 'decisions', - 'current_focus', -] as const; - -export type MissionBriefField = (typeof MISSION_BRIEF_FIELDS)[number]; - -export interface MissionBrief { - goal: string; - done: string; - open: string; - clarifications: string; - /** Durable user-stated constraints ("do not change X"). */ - user_constraints?: string; - /** Decisions made after clarification, with rationale. */ - decisions?: string; - /** What this movement is actively working on right now. */ - current_focus?: string; -} - -// ── AAO Gateway Phase 2a: virtual keys ───────────────────────────────── -// Stored shape returned by all gateway-key repository methods. The raw -// bearer is NEVER persisted or returned — only `keyHash` (sha256) lives -// in the DB, and `keyPrefix` is the human-readable head used in admin UI -// lists (`sk-aao-XXXXXX`). The raw key surfaces exactly once, from the -// admin API on issue / rotate; see src/bridge/admin-gateway-api.ts. - -export type GatewayVirtualKeySource = 'admin' | 'config-import'; - -export interface GatewayVirtualKey { - id: string; - keyHash: string; - keyPrefix: string; - team: string; - /** Null = no per-key allowlist (any backend.model is accepted). */ - allowedModels: string[] | null; - source: GatewayVirtualKeySource; - createdAt: string; - createdBy: string | null; - /** ISO timestamp if revoked; null while active. */ - revokedAt: string | null; - revokedBy: string | null; - lastUsedAt: string | null; - /** - * Phase 2b: monthly tokens budget. NULL = unlimited. When set, the - * gateway rejects requests with 402 once the current UTC month - * `tokens_in + tokens_out` reaches this number (post-hoc enforcement — - * the offending request that pushes the counter over the limit still - * completes; the next one is rejected). - */ - tokensBudget: number | null; - /** - * Phase 2b: per-key requests-per-minute cap. NULL = unlimited. Enforced - * as a sliding 60-second window in-process; multi-instance setups are - * intentionally NOT synchronized (Phase 3 if needed). - */ - rateLimitRpm: number | null; -} - -interface GatewayVirtualKeyRow { - id: string; - key_hash: string; - key_prefix: string; - team: string; - allowed_models: string | null; - source: string; - created_at: string; - created_by: string | null; - revoked_at: string | null; - revoked_by: string | null; - last_used_at: string | null; - tokens_budget: number | null; - rate_limit_rpm: number | null; -} - -// Phase 2b: monthly usage counter per virtual key. -export interface GatewayKeyUsage { - keyId: string; - /** UTC month bucket as 'YYYY-MM' — see src/gateway/period.ts. */ - periodStart: string; - tokensIn: number; - tokensOut: number; - requests: number; - lastUpdatedAt: string; -} - -interface GatewayKeyUsageRow { - key_id: string; - period_start: string; - tokens_in: number; - tokens_out: number; - requests: number; - last_updated_at: string; -} - -function rowToGatewayKeyUsage(row: GatewayKeyUsageRow): GatewayKeyUsage { - return { - keyId: row.key_id, - periodStart: row.period_start, - tokensIn: row.tokens_in, - tokensOut: row.tokens_out, - requests: row.requests, - lastUpdatedAt: row.last_updated_at, - }; -} - -/** Per-call delta for the per-user daily LLM usage ledger. */ -export interface LlmUsageIncrement { - /** UTC day bucket 'YYYY-MM-DD'. Defaults to today (UTC) when omitted. */ - day?: string; - /** Owner id, or 'local' (no-auth) / 'system' (ownerless) sentinel. */ - userId: string; - source: 'gateway' | 'direct'; - /** Real model name (chunk.model), routing-key fallback, or 'unknown'. */ - model: string; - /** Backend server name (gateway backendId / direct host), or 'unknown'. */ - route: string; - tokensIn?: number; - tokensOut?: number; - requests?: number; - at?: string; -} - -/** Daily-grouped aggregate row (model/route collapsed) for the usage API. */ -export interface LlmUsageDailyAgg { - day: string; - userId: string; - source: string; - tokensIn: number; - tokensOut: number; - requests: number; -} - -/** Hour-grain UPSERT input for the v2 usage ledger. */ -export interface LlmUsageHourlyIncrement { - /** UTC hour bucket 'YYYY-MM-DDTHH'. Defaults to the current hour (UTC). */ - hour?: string; - /** Owner id, 'local' / 'system' sentinel, or 'gw:' for downstream gateway consumers. */ - userId: string; - /** - * 'direct' / 'gateway' are this orchestrator's own calls. 'gateway_downstream' - * is traffic the AAO Gateway proxied for an EXTERNAL consumer (recorded from - * the gateway proxy path, keyed by team) — kept distinct so it never merges - * with our own usage. See docs/superpowers/specs/2026-06-19-usage-hourly-profile-and-gateway-downstream-design.md - */ - source: 'gateway' | 'direct' | 'gateway_downstream'; - /** Real model name (chunk.model), routing-key fallback, or 'unknown'. */ - model: string; - /** Backend server name (gateway backendId / direct host), or 'unknown'. */ - route: string; - tokensIn?: number; - tokensOut?: number; - requests?: number; - /** ISO timestamp for last_updated_at + hour default. Defaults to now. */ - at?: string; -} - -/** - * Raw hour-grain ledger row (no axis collapsed) for the usage API. The API - * re-buckets `hour` into the viewer's local calendar period and groups by - * whichever of source/model/route/user/org the request asked for. - */ -export interface LlmUsageHourlyRow { - hour: string; - userId: string; - source: string; - model: string; - route: string; - tokensIn: number; - tokensOut: number; - requests: number; -} - -/** - * Coerce an optional limit (tokens_budget / rate_limit_rpm) to either - * a positive integer or null. Anything else (undefined, null, 0, - * negative, NaN, non-number) collapses to null = "no limit" so callers - * can't accidentally persist a value that would silently block all - * traffic. - */ -function normalizeOptionalPositiveInt(v: unknown): number | null { - if (v === undefined || v === null) return null; - if (typeof v !== 'number' || !Number.isFinite(v) || v <= 0) return null; - return Math.floor(v); -} - -function rowToGatewayVirtualKey(row: GatewayVirtualKeyRow): GatewayVirtualKey { - let allowedModels: string[] | null = null; - if (row.allowed_models !== null && row.allowed_models !== '') { - try { - const parsed: unknown = JSON.parse(row.allowed_models); - if (Array.isArray(parsed) && parsed.every(x => typeof x === 'string')) { - allowedModels = parsed; - } - } catch { - // Corrupt JSON: treat as "no allowlist" (safer than fail-open - // because routing has its own backend.model gate; this is just - // the per-key filter on top). - allowedModels = null; - } - } - const source: GatewayVirtualKeySource = - row.source === 'config-import' ? 'config-import' : 'admin'; - // tokens_budget / rate_limit_rpm may legitimately arrive as null (no - // limit). Coerce non-positive integers to null defensively because the - // gateway middleware treats null as "unlimited" — a corrupt `0` would - // otherwise silently block every request. - const tokensBudget = - typeof row.tokens_budget === 'number' && Number.isFinite(row.tokens_budget) && row.tokens_budget > 0 - ? Math.floor(row.tokens_budget) - : null; - const rateLimitRpm = - typeof row.rate_limit_rpm === 'number' && Number.isFinite(row.rate_limit_rpm) && row.rate_limit_rpm > 0 - ? Math.floor(row.rate_limit_rpm) - : null; - return { - id: row.id, - keyHash: row.key_hash, - keyPrefix: row.key_prefix, - team: row.team, - allowedModels, - source, - createdAt: row.created_at, - createdBy: row.created_by, - revokedAt: row.revoked_at, - revokedBy: row.revoked_by, - lastUsedAt: row.last_used_at, - tokensBudget, - rateLimitRpm, - }; -} - -export interface LocalTaskComment { - id: number; - taskId: number; - author: string; - kind: string; - body: string; - /** Filenames saved to the task's input/ dir for this comment (UI download links). */ - attachments: string[]; - createdAt: string; - injectedAt: string | null; -} - -export interface WorkerNode { - workerId: string; - endpoint: string; - enabled: boolean; - healthy: boolean; - roles: string[]; - availableModels: string[]; - inflightJobs: number; - maxConcurrency: number; - lastError: string | null; - lastSeenAt: string; - updatedAt: string; -} - -export interface UpsertWorkerNodeParams { - workerId: string; - endpoint: string; - enabled: boolean; - healthy: boolean; - roles: string[]; - availableModels?: string[]; - inflightJobs?: number; - maxConcurrency?: number; - lastError?: string | null; -} - -export interface CreateLocalTaskParams { - title: string; - /** Defaults to 'auto'. Pass 'user' when the caller supplied an explicit title. */ - titleSource?: TitleSource; - body: string; - pieceName?: string; - profile?: 'auto' | 'fast' | 'quality'; - outputFormat?: 'text' | 'markdown' | 'json'; - askPolicy?: 'low' | 'high'; - priority?: 'low' | 'medium' | 'high'; - workspacePath?: string | null; - /** ワークスペースの種類。未指定は 'persistent'(スペース共有)。 */ - workspaceMode?: 'persistent' | 'ephemeral'; - ownerId?: string | null; - visibility?: 'private' | 'org' | 'public'; - visibilityScopeOrgId?: string | null; - browserSessionProfileId?: number | null; - /** 所属スペース。未指定(null)は owner の個人スペースに解決される。 */ - spaceId?: string | null; - /** Per-task runtime options (e.g. { mcpDisabled, skillsDisabled }). Stored as JSON. */ - options?: Record; -} - -// ── Browser Notifications V2 (Web Push) ────────────────────────────── -// Spec: docs/superpowers/specs/2026-05-28-browser-notifications-v2-webpush.md - -export type NotifyEventType = 'running' | 'succeeded' | 'failed' | 'waiting_human'; - -export interface PushSubscriptionRecord { - id: string; - userId: string; - endpoint: string; - p256dh: string; - auth: string; - userAgent: string | null; - vapidKeyId: string; - createdAt: string; - lastSuccessAt: string | null; - lastFailureAt: string | null; - failureCount: number; -} - -export interface UpsertPushSubscriptionInput { - userId: string; - endpoint: string; - p256dh: string; - auth: string; - userAgent?: string | null; - vapidKeyId: string; -} - -export interface NotificationPrefs { - userId: string; - enabled: boolean; - events: Record; - includeDetails: boolean; - v1Migrated: boolean; - updatedAt: string; -} - -export type NotificationPrefsUpdate = Partial>; - -export type ScheduledTaskKind = 'agent' | 'script'; - -export interface ScheduledTask { - id: number; - title: string | null; - body: string; - pieceName: string; - profile: string; - outputFormat: string; - cronExpression: string; - nextRunAt: string; - lastRunAt: string | null; - lastJobId: string | null; - isActive: boolean; - ownerId: string | null; - ownerName?: string | null; - visibility: 'private' | 'org' | 'public'; - visibilityScopeOrgId: string | null; - visibilityScopeOrgName?: string | null; - browserSessionProfileId?: number | null; - taskKind: ScheduledTaskKind; - scriptName: string | null; - scriptParams: string | null; // JSON-encoded object or null - /** 所属スペース。生成される local_task に継承される。null = owner 個人スペース。 */ - spaceId: string | null; - createdAt: string; - updatedAt: string; -} - -export interface CreateScheduledTaskParams { - title?: string | null; - body: string; - pieceName?: string; - profile?: string; - outputFormat?: string; - cronExpression: string; - nextRunAt: string; - ownerId?: string | null; - visibility?: 'private' | 'org' | 'public'; - visibilityScopeOrgId?: string | null; - browserSessionProfileId?: number | null; - taskKind?: ScheduledTaskKind; - scriptName?: string | null; - scriptParams?: string | null; - spaceId?: string | null; -} - -export interface UpdateScheduledTaskParams { - title?: string; - body?: string; - pieceName?: string; - profile?: string; - outputFormat?: string; - cronExpression?: string; - nextRunAt?: string; - lastRunAt?: string; - lastJobId?: string; - isActive?: boolean; - visibility?: 'private' | 'org' | 'public'; - visibilityScopeOrgId?: string | null; - browserSessionProfileId?: number | null; - taskKind?: ScheduledTaskKind; - scriptName?: string | null; - scriptParams?: string | null; -} - -export interface User { - id: string; - email: string; - name: string | null; - avatarUrl: string | null; - role: 'admin' | 'user'; - status: 'active' | 'pending' | 'disabled'; - defaultVisibility: 'private' | 'org' | 'public'; - defaultVisibilityOrgId: string | null; - createdAt: string; - updatedAt: string; -} - -export interface CreateUserParams { - email: string; - name: string; - role: 'admin' | 'user'; - status: 'active' | 'pending' | 'disabled'; - avatarUrl?: string; -} - -export interface FindOrCreateByOAuthParams { - provider: string; - providerId: string; - email: string; - name: string; - avatarUrl?: string; -} - -export interface CreateLocalUserParams { - email: string; - password: string; - role: 'admin' | 'user'; - status: 'active' | 'pending' | 'disabled'; - name?: string; -} - -export interface LocalOrg { - id: string; - name: string; - createdBy: string | null; - createdAt: string; -} - -export interface LocalOrgMember { - userId: string; - role: string; -} - -export interface GiteaOrgInput { - orgId: string; - orgName: string; -} - -export interface GiteaOrg extends GiteaOrgInput { - fetchedAt: string; -} - -interface JobRow { - id: string; - repo: string; - issue_number: number; - pr_number: number | null; - status: string; - piece_name: string; - current_movement: string | null; - current_activity: string | null; - instruction: string; - branch_name: string | null; - worktree_path: string | null; - attempt: number; - max_attempts: number; - next_retry_at: string | null; - error_summary: string | null; - abort_reason: string | null; - resume_movement: string | null; - wait_reason: string | null; - ask_count: number; - worker_id: string | null; - last_backend_id: string | null; - parent_job_id: string | null; - continued_from_job_id: string | null; - subtask_depth: number; - required_profile: string; - task_class: string; - owner_id: string | null; - visibility: string | null; - visibility_scope_org_id: string | null; - context_prompt_tokens: number | null; - context_limit_tokens: number | null; - context_updated_at: string | null; - browser_session_profile_id: number | null; - task_kind: string; - payload: string | null; - space_id: string | null; - runtime_dir: string | null; - created_at: string; - updated_at: string; -} - -interface LocalTaskRow { - id: number; - title: string; - title_source: string | null; - body: string; - piece_name: string; - profile: string; - output_format: string; - ask_policy: string; - priority: string; - state: string; - workspace_path: string | null; - workspace_mode: string | null; - runtime_dir: string | null; - owner_id: string | null; - owner_name?: string | null; - visibility: string | null; - visibility_scope_org_id: string | null; - visibility_scope_org_name?: string | null; - space_id: string | null; - created_at: string; - updated_at: string; - feedback_rating: string | null; - feedback_comment: string | null; - feedback_tags: string | null; - feedback_at: string | null; - share_token: string | null; - shared_at: string | null; - mission_brief: string | null; - browser_session_profile_id: number | null; - options: string | null; -} - -interface SpaceRow { - id: string; - kind: string; - title: string; - description: string; - owner_id: string | null; - visibility: string; - visibility_scope_org_id: string | null; - status: string; - brand_color: string | null; - workspace_dir: string | null; - created_at: string; - updated_at: string; -} - -export interface Space { - id: string; - kind: 'personal' | 'case'; - title: string; - description: string; - ownerId: string | null; - visibility: 'private' | 'org' | 'public'; - visibilityScopeOrgId: string | null; - status: 'open' | 'archived'; - brandColor: string | null; - workspaceDir: string | null; - createdAt: string; - updatedAt: string; -} - -export type SpaceMemberRoleValue = 'owner' | 'editor' | 'viewer'; - -/** space_members の 1 行 + users から合成した表示情報。owner_id は別途 owner として扱う。 */ -export interface SpaceMember { - spaceId: string; - userId: string; - role: SpaceMemberRoleValue; - invitedBy: string | null; - createdAt: string; - /** users JOIN 由来の表示用フィールド(行が無い場合 null)。 */ - name: string | null; - email: string | null; - avatarUrl: string | null; -} - -/** 招待リンクで付与できるロール。owner は付与不可(権限昇格防止)。 */ -export type SpaceInviteRole = 'editor' | 'viewer'; - -/** space_invites の 1 行。 */ -export interface SpaceInvite { - token: string; - spaceId: string; - role: SpaceInviteRole; - createdBy: string | null; - createdAt: string; - /** ISO 文字列。null = 無期限。 */ - expiresAt: string | null; - /** ISO 文字列。null = 有効。 */ - revokedAt: string | null; -} - -export interface CreateSpaceParams { - id?: string; - kind: 'personal' | 'case'; - title: string; - description?: string; - ownerId: string | null; - visibility?: 'private' | 'org' | 'public'; - visibilityScopeOrgId?: string | null; - brandColor?: string | null; - workspaceDir?: string | null; -} - -interface LocalTaskCommentRow { - id: number; - task_id: number; - author: string; - kind: string; - body: string; - attachments: string | null; - created_at: string; - injected_at: string | null; -} - -interface UserRow { - id: string; - email: string; - name: string | null; - avatar_url: string | null; - role: string; - status: string; - default_visibility: string | null; - default_visibility_org_id: string | null; - created_at: string; - updated_at: string; -} - -interface WorkerNodeRow { - worker_id: string; - endpoint: string; - enabled: number; - healthy: number; - profile_tags: string; - task_class_tags: string; - available_models: string | null; - inflight_jobs: number; - max_concurrency: number; - last_error: string | null; - last_seen_at: string; - updated_at: string; -} - -function isJobRole(value: string): value is JobRole { - return value === 'auto' || value === 'fast' || value === 'quality' || value === 'reflection'; -} - -function normalizeJobRole(value: string | undefined): JobRole { - return value && isJobRole(value) ? value : 'auto'; -} - -function encodeTags(values: string[]): string { - const unique = Array.from(new Set(values.filter(Boolean))); - return `,${unique.join(',')},`; -} - -function decodeTags(raw: string | null): string[] { - if (!raw) return []; - return raw.split(',').map((value) => value.trim()).filter(Boolean); -} - -function decodeAvailableModels(raw: string | null): string[] { - if (!raw) return []; - try { - const parsed = JSON.parse(raw) as unknown; - return Array.isArray(parsed) ? parsed.filter((value): value is string => typeof value === 'string') : []; - } catch { - return []; - } -} - -function deriveJobRole(instruction: string, explicitRole?: JobRole): JobRole { - if (explicitRole) return explicitRole; - const match = /ui_profile:\s*(auto|fast|quality)/i.exec(instruction); - return normalizeJobRole(match?.[1]?.toLowerCase()); -} - -/** SQLite datetime('now') は UTC だがタイムゾーン情報なし。'Z' を付加して ISO 8601 UTC として明示する */ -function utc(dt: string | null): string { - if (!dt) return ''; - // 既に Z や +/- オフセットが付いていれば何もしない - if (/[Zz]$/.test(dt) || /[+-]\d{2}:\d{2}$/.test(dt)) return dt; - return dt.replace(' ', 'T') + 'Z'; -} - -function rowToJob(row: JobRow): Job { - return { - id: row.id, - repo: row.repo, - issueNumber: row.issue_number, - prNumber: row.pr_number, - status: row.status as JobStatus, - pieceName: row.piece_name, - currentMovement: row.current_movement, - currentActivity: row.current_activity, - instruction: row.instruction, - branchName: row.branch_name, - worktreePath: row.worktree_path, - attempt: row.attempt, - maxAttempts: row.max_attempts, - nextRetryAt: utc(row.next_retry_at), - errorSummary: row.error_summary, - abortReason: row.abort_reason ?? null, - resumeMovement: row.resume_movement, - waitReason: row.wait_reason ?? null, - askCount: row.ask_count, - workerId: row.worker_id, - lastBackendId: row.last_backend_id ?? null, - parentJobId: row.parent_job_id, - continuedFromJobId: row.continued_from_job_id ?? null, - subtaskDepth: row.subtask_depth ?? 0, - requiredRole: normalizeJobRole(row.required_profile), - requiredProfile: normalizeJobRole(row.required_profile), - ownerId: row.owner_id ?? null, - visibility: (row.visibility === 'org' || row.visibility === 'public' ? row.visibility : 'private'), - visibilityScopeOrgId: row.visibility_scope_org_id ?? null, - contextPromptTokens: row.context_prompt_tokens, - contextLimitTokens: row.context_limit_tokens, - contextUpdatedAt: row.context_updated_at ? utc(row.context_updated_at) : null, - browserSessionProfileId: row.browser_session_profile_id ?? null, - taskKind: row.task_kind === 'reflection' ? 'reflection' : 'agent', - payload: row.payload, - spaceId: row.space_id ?? null, - runtimeDir: row.runtime_dir ?? null, - createdAt: utc(row.created_at), - updatedAt: utc(row.updated_at), - }; -} - -function rowToLocalTask(row: LocalTaskRow): LocalTask { - return { - id: row.id, - title: row.title, - titleSource: (row.title_source as TitleSource | null) ?? 'auto', - body: row.body, - pieceName: row.piece_name, - profile: row.profile, - outputFormat: row.output_format, - askPolicy: row.ask_policy, - priority: row.priority, - state: row.state, - workspacePath: row.workspace_path, - workspaceMode: row.workspace_mode === 'ephemeral' ? 'ephemeral' : 'persistent', - ownerId: row.owner_id ?? null, - ownerName: row.owner_name ?? null, - visibility: (row.visibility ?? 'private') as LocalTask['visibility'], - visibilityScopeOrgId: row.visibility_scope_org_id ?? null, - visibilityScopeOrgName: row.visibility_scope_org_name ?? null, - spaceId: row.space_id ?? null, - runtimeDir: row.runtime_dir ?? null, - createdAt: utc(row.created_at), - updatedAt: utc(row.updated_at), - feedbackRating: (row.feedback_rating as 'good' | 'bad' | null) ?? null, - feedbackTags: row.feedback_tags ? JSON.parse(row.feedback_tags) : null, - feedbackComment: row.feedback_comment ?? null, - feedbackAt: row.feedback_at ? utc(row.feedback_at) : null, - shareToken: row.share_token ?? null, - sharedAt: row.shared_at ? utc(row.shared_at) : null, - browserSessionProfileId: row.browser_session_profile_id ?? null, - missionBrief: parseMissionBrief(row.mission_brief), - options: parseTaskOptions(row.options), - }; -} - -function parseMissionBrief(raw: string | null | undefined): MissionBrief | null { - if (!raw) return null; - try { - const parsed = JSON.parse(raw); - if (!parsed || typeof parsed !== 'object') return null; - const brief = {} as MissionBrief; - for (const field of MISSION_BRIEF_FIELDS) { - brief[field] = typeof parsed[field] === 'string' ? parsed[field] : ''; - } - if (MISSION_BRIEF_FIELDS.every((field) => !brief[field])) return null; - return brief; - } catch { - return null; - } -} - -function parseTaskOptions(raw: string | null | undefined): Record { - if (!raw) return {}; - try { - const parsed = JSON.parse(raw); - if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { - return parsed as Record; - } - return {}; - } catch { - return {}; - } -} - -function rowToSpace(row: SpaceRow): Space { - return { - id: row.id, - kind: row.kind as Space['kind'], - title: row.title, - description: row.description, - ownerId: row.owner_id, - visibility: row.visibility as Space['visibility'], - visibilityScopeOrgId: row.visibility_scope_org_id, - status: row.status as Space['status'], - brandColor: row.brand_color, - workspaceDir: row.workspace_dir, - createdAt: utc(row.created_at), - updatedAt: utc(row.updated_at), - }; -} - -function rowToLocalTaskComment(row: LocalTaskCommentRow): LocalTaskComment { - return { - id: row.id, - taskId: row.task_id, - author: row.author, - kind: row.kind, - body: row.body, - attachments: parseCommentAttachments(row.attachments), - createdAt: utc(row.created_at), - injectedAt: row.injected_at ? utc(row.injected_at) : null, - }; -} - -/** Parse the JSON attachments column to a string[]; tolerate null/legacy/garbage. */ -function parseCommentAttachments(raw: string | null): string[] { - if (!raw) return []; - try { - const parsed = JSON.parse(raw); - return Array.isArray(parsed) ? parsed.filter((n): n is string => typeof n === 'string') : []; - } catch { - return []; - } -} - -function rowToWorkerNode(row: WorkerNodeRow): WorkerNode { - return { - workerId: row.worker_id, - endpoint: row.endpoint, - enabled: row.enabled === 1, - healthy: row.healthy === 1, - roles: decodeTags(row.profile_tags), - availableModels: decodeAvailableModels(row.available_models), - inflightJobs: row.inflight_jobs, - maxConcurrency: row.max_concurrency, - lastError: row.last_error, - lastSeenAt: utc(row.last_seen_at), - updatedAt: utc(row.updated_at), - }; -} - -function rowToUser(row: UserRow): User { - return { - id: row.id, - email: row.email, - name: row.name, - avatarUrl: row.avatar_url, - role: row.role as 'admin' | 'user', - status: row.status as 'active' | 'pending' | 'disabled', - defaultVisibility: (row.default_visibility ?? 'private') as User['defaultVisibility'], - defaultVisibilityOrgId: row.default_visibility_org_id, - createdAt: utc(row.created_at), - updatedAt: utc(row.updated_at), - }; -} - -export function localTaskRepoName(taskId: number): string { - return `local/task-${taskId}`; -} export class Repository { private readonly db: Database.Database; @@ -1346,876 +60,122 @@ export class Repository { } private initSchema(): void { - this.runPreSchemaCompatibilityMigrations(); - const schemaPath = join(__dirname, 'schema.sql'); - const schema = readFileSync(schemaPath, 'utf-8'); - this.db.exec(schema); - this.ensureColumn('jobs', 'required_profile', "TEXT NOT NULL DEFAULT 'auto'"); - this.ensureColumn('jobs', 'task_class', "TEXT NOT NULL DEFAULT 'auto'"); - this.ensureColumn('worker_nodes', 'profile_tags', "TEXT NOT NULL DEFAULT ',auto,'"); - this.ensureColumn('worker_nodes', 'task_class_tags', "TEXT NOT NULL DEFAULT ',auto,'"); - this.ensureColumn('worker_nodes', 'available_models', 'TEXT'); - this.ensureColumn('worker_nodes', 'max_concurrency', 'INTEGER NOT NULL DEFAULT 1'); - this.ensureColumn('worker_nodes', 'last_error', 'TEXT'); - this.db.exec("CREATE INDEX IF NOT EXISTS idx_jobs_profile_task_class ON jobs (status, required_profile, task_class)"); - this.db.exec("CREATE INDEX IF NOT EXISTS idx_worker_nodes_health ON worker_nodes (enabled, healthy, last_seen_at)"); - this.ensureColumn('jobs', 'parent_job_id', 'TEXT'); - this.ensureColumn('jobs', 'subtask_depth', 'INTEGER NOT NULL DEFAULT 0'); - this.ensureColumn('jobs', 'wait_reason', 'TEXT'); - this.ensureColumn('jobs', 'continued_from_job_id', 'TEXT'); - // Phase A (multi-team GPU pool): physical backend id assigned when - // the worker is a proxy. Sticky once set; never overwritten mid-job. - this.ensureColumn('jobs', 'last_backend_id', 'TEXT'); - this.db.exec("CREATE INDEX IF NOT EXISTS idx_jobs_parent_job_id ON jobs (parent_job_id)"); - this.ensureColumn('local_tasks', 'feedback_rating', 'TEXT'); - this.ensureColumn('local_tasks', 'feedback_comment', 'TEXT'); - this.ensureColumn('local_tasks', 'feedback_tags', 'TEXT'); - this.ensureColumn('local_tasks', 'feedback_at', 'TEXT'); - this.migrateWaitingSubtasksStatus(); - // Auth migrations: owner_id columns - this.ensureColumn('jobs', 'owner_id', 'TEXT'); - this.ensureColumn('local_tasks', 'owner_id', 'TEXT'); - // #142: 実行中アクティビティ表示 - this.ensureColumn('jobs', 'current_activity', 'TEXT'); - // abortReason 細分化: agent-loop / piece-runner が出す構造化 abort code を保持 - this.ensureColumn('jobs', 'abort_reason', 'TEXT'); - // #134: 共有機能 - this.ensureColumn('local_tasks', 'share_token', 'TEXT'); - this.ensureColumn('local_tasks', 'shared_at', 'TEXT'); - this.db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_local_tasks_share_token ON local_tasks (share_token)"); - - // Ownership and visibility columns (3 tables) - for (const table of ['local_tasks', 'scheduled_tasks', 'jobs']) { - this.ensureColumn(table, 'owner_id', 'TEXT'); - this.ensureColumn(table, 'visibility', "TEXT NOT NULL DEFAULT 'private'"); - this.ensureColumn(table, 'visibility_scope_org_id', 'TEXT'); - } - - // User preferences - this.ensureColumn('users', 'default_visibility', "TEXT NOT NULL DEFAULT 'private'"); - this.ensureColumn('users', 'default_visibility_org_id', 'TEXT'); - - // Indexes and user_gitea_orgs table - this.db.exec(` - CREATE INDEX IF NOT EXISTS idx_local_tasks_owner_id ON local_tasks(owner_id); - CREATE INDEX IF NOT EXISTS idx_local_tasks_visibility ON local_tasks(visibility, visibility_scope_org_id); - CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_owner_id ON scheduled_tasks(owner_id); - CREATE INDEX IF NOT EXISTS idx_jobs_owner_id ON jobs(owner_id); - CREATE INDEX IF NOT EXISTS idx_jobs_visibility ON jobs(visibility, visibility_scope_org_id); - - CREATE TABLE IF NOT EXISTS user_gitea_orgs ( - user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - org_id TEXT NOT NULL, - org_name TEXT NOT NULL, - fetched_at TEXT NOT NULL DEFAULT (datetime('now')), - PRIMARY KEY (user_id, org_id) - ); - CREATE INDEX IF NOT EXISTS idx_user_gitea_orgs_org_id ON user_gitea_orgs(org_id); - `); - - // スペース基盤(計画1)。schema.sql / migrate.ts と三重ミラー。 - this.db.exec(` - CREATE TABLE IF NOT EXISTS spaces ( - id TEXT PRIMARY KEY, - kind TEXT NOT NULL DEFAULT 'case', - title TEXT NOT NULL, - description TEXT NOT NULL DEFAULT '', - owner_id TEXT, - visibility TEXT NOT NULL DEFAULT 'private', - visibility_scope_org_id TEXT, - status TEXT NOT NULL DEFAULT 'open', - brand_color TEXT, - workspace_dir TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - updated_at TEXT NOT NULL DEFAULT (datetime('now')) - ); - CREATE INDEX IF NOT EXISTS idx_spaces_owner ON spaces(owner_id); - CREATE INDEX IF NOT EXISTS idx_spaces_visibility ON spaces(visibility, visibility_scope_org_id); - CREATE UNIQUE INDEX IF NOT EXISTS idx_spaces_personal_owner ON spaces(owner_id) WHERE kind = 'personal'; - `); - this.ensureColumn('local_tasks', 'space_id', 'TEXT'); - this.ensureColumn('local_tasks', 'workspace_mode', "TEXT NOT NULL DEFAULT 'persistent'"); - this.ensureColumn('jobs', 'space_id', 'TEXT'); - this.ensureColumn('scheduled_tasks', 'space_id', 'TEXT'); - - // タスク横断検索用の会話コメント索引(通常テーブルのみ)。schema.sql / migrate.ts と三重ミラー。 - // FTS5 仮想テーブル+同期トリガは src/db/migrate.ts の ensureTaskCommentFts が単一定義箇所として作成する - // (runMigrations() を経由しない bare `new Repository()` 構築でも FTS が確実に存在するように、ここからも呼ぶ)。 - this.db.exec(` - CREATE TABLE IF NOT EXISTS task_comment_index ( - comment_id INTEGER PRIMARY KEY, - task_id INTEGER NOT NULL, - author TEXT, - kind TEXT, - created_at TEXT, - text TEXT NOT NULL - ); - CREATE INDEX IF NOT EXISTS idx_tci_task ON task_comment_index(task_id); - `); - ensureTaskCommentFts(this.db); - - // スペース・メンバー(協働者)。schema.sql / migrate.ts と三重ミラー。 - // owner は members 行を持たず owner_id で判定する。 - this.db.exec(` - CREATE TABLE IF NOT EXISTS space_members ( - space_id TEXT NOT NULL, - user_id TEXT NOT NULL, - role TEXT NOT NULL DEFAULT 'editor' CHECK (role IN ('owner','editor','viewer')), - invited_by TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - PRIMARY KEY (space_id, user_id), - FOREIGN KEY (space_id) REFERENCES spaces(id) ON DELETE CASCADE, - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE - ); - CREATE INDEX IF NOT EXISTS idx_space_members_user ON space_members (user_id); - `); - - // スペース・カレンダー(予定)。schema.sql / migrate.ts と三重ミラー。 - this.db.exec(` - CREATE TABLE IF NOT EXISTS calendar_events ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - space_id TEXT NOT NULL, - owner_id TEXT, - date TEXT NOT NULL, - end_date TEXT, - time TEXT, - end_time TEXT, - title TEXT NOT NULL, - description TEXT, - created_by TEXT NOT NULL DEFAULT 'user', - source_task_id INTEGER, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - updated_at TEXT NOT NULL DEFAULT (datetime('now')) - ); - CREATE INDEX IF NOT EXISTS idx_calendar_events_space_date ON calendar_events (space_id, date); - `); - - // スペース招待リンク(再利用トークン)。schema.sql / migrate.ts と三重ミラー。 - this.db.exec(` - CREATE TABLE IF NOT EXISTS space_invites ( - token TEXT PRIMARY KEY, - space_id TEXT NOT NULL, - role TEXT NOT NULL DEFAULT 'viewer' CHECK (role IN ('editor','viewer')), - created_by TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - expires_at TEXT, - revoked_at TEXT, - FOREIGN KEY (space_id) REFERENCES spaces(id) ON DELETE CASCADE - ); - CREATE INDEX IF NOT EXISTS idx_space_invites_space ON space_invites (space_id); - `); - // 公開アプリ共有リンク(read-only)。schema.sql / migrate.ts と三重ミラー。 - this.db.exec(` - CREATE TABLE IF NOT EXISTS app_share_links ( - token TEXT PRIMARY KEY, - space_id TEXT NOT NULL, - app_name TEXT NOT NULL, - created_by TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - revoked_at TEXT, - FOREIGN KEY (space_id) REFERENCES spaces(id) ON DELETE CASCADE - ); - CREATE INDEX IF NOT EXISTS idx_app_share_links_space_app - ON app_share_links(space_id, app_name); - `); - // ツール要求の記録(RequestTool 能動申告 / 未許可ツール呼び出しの受動捕捉)。 - // schema.sql / migrate.ts と三重ミラー。 - this.db.exec(` - CREATE TABLE IF NOT EXISTS tool_requests ( - id TEXT PRIMARY KEY, - task_id TEXT, - job_id TEXT, - space_id TEXT, - piece_name TEXT NOT NULL, - movement_name TEXT NOT NULL, - tool_name TEXT NOT NULL, - reason TEXT, - category TEXT NOT NULL DEFAULT 'requested', - status TEXT NOT NULL DEFAULT 'pending', - grant_scope TEXT, - decided_by TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - decided_at TEXT - ); - CREATE INDEX IF NOT EXISTS idx_tool_requests_task ON tool_requests (task_id); - CREATE INDEX IF NOT EXISTS idx_tool_requests_piece ON tool_requests (piece_name); - CREATE INDEX IF NOT EXISTS idx_tool_requests_dedup ON tool_requests (job_id, movement_name, tool_name, status); - `); - // ワークスペースファイル来歴台帳。schema.sql / migrate.ts と三重ミラー。 - this.db.exec(` - CREATE TABLE IF NOT EXISTS workspace_file_provenance ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - space_id TEXT, - workspace_path TEXT NOT NULL, - rel_path TEXT NOT NULL, - created_by_task_id INTEGER, - created_by_job_id TEXT, - created_by_piece TEXT, - created_by_movement TEXT, - source_kind TEXT NOT NULL, - first_seen_at TEXT NOT NULL, - last_modified_by_task_id INTEGER, - last_modified_by_job_id TEXT, - last_modified_at TEXT, - checksum TEXT, - note TEXT, - UNIQUE(workspace_path, rel_path) - ); - CREATE INDEX IF NOT EXISTS idx_wsfile_prov_ws ON workspace_file_provenance (workspace_path); - CREATE INDEX IF NOT EXISTS idx_wsfile_prov_created ON workspace_file_provenance (workspace_path, created_by_task_id); - `); - // Per-space isolation for MCP servers / SSH connections (spaces foundation). - // NULL = global/legacy (resolved globally; Phase 1 is a no-op, no consumer - // reads this yet). Triple-path mirror: schema.sql + migrate.ts. - this.ensureColumn('mcp_servers', 'space_id', 'TEXT'); - this.ensureColumn('ssh_connections', 'space_id', 'TEXT'); - // workstream 2: ブラウザセッションプロファイルの per-space 化。owner_id は復号 - // DEK のため保持。Triple-path mirror: schema.sql + migrate.ts。 - this.ensureColumn('browser_session_profiles', 'space_id', 'TEXT'); - // 計画5: 実行ログ root。NULL = 後方互換で workspace_path/logs に解決。 - this.ensureColumn('local_tasks', 'runtime_dir', 'TEXT'); - this.ensureColumn('jobs', 'runtime_dir', 'TEXT'); - // Tool-request mechanism: per-task grant overlay (JSON string[]). - this.ensureColumn('local_tasks', 'granted_tools', 'TEXT'); - - // Browser session persistence (2026-05) — keep in sync with schema.sql - this.db.exec(` - CREATE TABLE IF NOT EXISTS user_deks ( - user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, - encrypted_dek BLOB NOT NULL, - created_at TEXT NOT NULL DEFAULT (datetime('now')) - ); - CREATE TABLE IF NOT EXISTS browser_session_profiles ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - owner_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - space_id TEXT, - label TEXT NOT NULL, - start_url TEXT NOT NULL, - match_patterns TEXT NOT NULL DEFAULT '[]', - storage_origins TEXT NOT NULL DEFAULT '[]', - logged_in_selector TEXT, - login_url_patterns TEXT NOT NULL DEFAULT '[]', - encrypted_state_blob BLOB, - state_version INTEGER NOT NULL DEFAULT 0, - playwright_version TEXT, - status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','active','expired','revoked','error')), - last_saved_at TEXT, - last_used_at TEXT, - last_validated_at TEXT, - last_error TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - updated_at TEXT NOT NULL DEFAULT (datetime('now')) - ); - CREATE INDEX IF NOT EXISTS idx_bsp_owner ON browser_session_profiles(owner_id); - CREATE INDEX IF NOT EXISTS idx_bsp_space ON browser_session_profiles(space_id); - -- audit log: intentionally no FK — must survive deletion of referenced rows - CREATE TABLE IF NOT EXISTS browser_session_audit ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ts TEXT NOT NULL DEFAULT (datetime('now')), - actor_user_id TEXT, - profile_id INTEGER, - owner_id TEXT, - action TEXT NOT NULL CHECK (action IN ('create','save','decrypt','use','delete','expire','revoke','test','login_start','login_cancel')), - task_id INTEGER, - job_id TEXT, - result TEXT NOT NULL CHECK (result IN ('success','error')), - reason TEXT - ); - CREATE INDEX IF NOT EXISTS idx_bsa_profile ON browser_session_audit(profile_id); - CREATE INDEX IF NOT EXISTS idx_bsa_actor ON browser_session_audit(actor_user_id); - `); - - this.ensureColumn('local_tasks', 'browser_session_profile_id', 'INTEGER REFERENCES browser_session_profiles(id) ON DELETE SET NULL'); - this.ensureColumn('scheduled_tasks', 'browser_session_profile_id', 'INTEGER REFERENCES browser_session_profiles(id) ON DELETE SET NULL'); - this.ensureColumn('jobs', 'browser_session_profile_id', 'INTEGER REFERENCES browser_session_profiles(id) ON DELETE SET NULL'); - - // E: scheduled_tasks can now run a user script directly (without going - // through the agent / LLM loop). task_kind='agent' (default) keeps the - // pre-existing behavior; task_kind='script' uses script_name + script_params. - this.ensureColumn('scheduled_tasks', 'task_kind', "TEXT NOT NULL DEFAULT 'agent' CHECK (task_kind IN ('agent','script'))"); - this.ensureColumn('scheduled_tasks', 'script_name', 'TEXT'); - this.ensureColumn('scheduled_tasks', 'script_params', 'TEXT'); // JSON-encoded object or NULL - - // F: reflection jobs — task_kind distinguishes agent jobs from reflection - // jobs that run the self-improving-memory pipeline (no LLM task loop). - // payload carries JSON inputs (scope, trigger metadata, etc.). - this.ensureColumn('jobs', 'task_kind', "TEXT NOT NULL DEFAULT 'agent'"); - this.ensureColumn('jobs', 'payload', 'TEXT'); - - // G: reflection piece-edit cooldown tracking. - // reflection_piece_edits records each time the reflection pipeline writes - // a user's custom piece. The cooldown gate in piece-writer.ts queries - // countRecentPieceEdits to rate-limit piece rewrites. - this.db.exec(` - CREATE TABLE IF NOT EXISTS reflection_piece_edits ( - user_id TEXT NOT NULL, - piece_name TEXT NOT NULL, - snapshot_id TEXT NOT NULL, - created_at INTEGER NOT NULL, - PRIMARY KEY (user_id, piece_name, created_at) - ); - CREATE INDEX IF NOT EXISTS idx_rpe_user_piece_time - ON reflection_piece_edits (user_id, piece_name, created_at DESC); - `); - - // The Side Info Panel dashboard widget feature was removed (2026-06); - // drop the legacy per-user widget table if an older DB still carries it. - this.db.exec(`DROP TABLE IF EXISTS user_dashboard_widgets;`); - - // H: reflection_metrics — one row per reflection job, records outcome, - // token usage, memory changes, and whether a piece edit was applied. - // Used by the /api/reflection/metrics endpoint (Phase 7.2) and future - // per-user budget enforcement (Phase 8.2). - this.db.exec(` - CREATE TABLE IF NOT EXISTS reflection_metrics ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - reflection_job_id TEXT NOT NULL, - original_job_id TEXT, - user_id TEXT NOT NULL, - piece_name TEXT, - outcome TEXT NOT NULL, - memory_changes INTEGER NOT NULL DEFAULT 0, - piece_edited INTEGER NOT NULL DEFAULT 0, - tokens_in INTEGER NOT NULL DEFAULT 0, - tokens_out INTEGER NOT NULL DEFAULT 0, - duration_ms INTEGER NOT NULL DEFAULT 0, - created_at INTEGER NOT NULL - ); - CREATE INDEX IF NOT EXISTS idx_rm_user_time - ON reflection_metrics (user_id, created_at DESC); - `); - - // I: AAO Gateway Phase 2a — DB-backed virtual keys. - // Mirrors src/db/schema.sql and migrateGatewayVirtualKeys in migrate.ts; - // all three paths must stay in sync (project_db_migration_dual_path). - this.db.exec(` - CREATE TABLE IF NOT EXISTS gateway_virtual_keys ( - id TEXT PRIMARY KEY, - key_hash TEXT NOT NULL UNIQUE, - key_prefix TEXT NOT NULL, - team TEXT NOT NULL, - allowed_models TEXT, - source TEXT NOT NULL DEFAULT 'admin' CHECK (source IN ('admin','config-import')), - created_at TEXT NOT NULL, - created_by TEXT, - revoked_at TEXT, - revoked_by TEXT, - last_used_at TEXT - ); - CREATE INDEX IF NOT EXISTS idx_gateway_keys_hash_active - ON gateway_virtual_keys (key_hash) - WHERE revoked_at IS NULL; - CREATE INDEX IF NOT EXISTS idx_gateway_keys_team - ON gateway_virtual_keys (team); - `); - - // I.b: AAO Gateway Phase 2b — budget / rate limit columns + - // gateway_key_usage table. Mirrors schema.sql + migrate.ts and uses - // the same PRAGMA-based idempotency pattern. - this.ensureColumn('gateway_virtual_keys', 'tokens_budget', 'INTEGER'); - this.ensureColumn('gateway_virtual_keys', 'rate_limit_rpm', 'INTEGER'); - this.db.exec(` - CREATE TABLE IF NOT EXISTS gateway_key_usage ( - key_id TEXT NOT NULL REFERENCES gateway_virtual_keys(id) ON DELETE CASCADE, - period_start TEXT NOT NULL, - tokens_in INTEGER NOT NULL DEFAULT 0, - tokens_out INTEGER NOT NULL DEFAULT 0, - requests INTEGER NOT NULL DEFAULT 0, - last_updated_at TEXT NOT NULL, - PRIMARY KEY (key_id, period_start) - ); - CREATE INDEX IF NOT EXISTS idx_gateway_usage_key - ON gateway_key_usage (key_id); - `); - - // Per-user daily LLM usage (gateway + direct). Mirrors schema.sql + - // migrate.ts (dual-path rule). Separate lens from gateway_key_usage. - // Spec: docs/superpowers/specs/2026-06-11-llm-usage-aggregation-design.md - this.db.exec(` - CREATE TABLE IF NOT EXISTS llm_usage_daily ( - day TEXT NOT NULL, - user_id TEXT NOT NULL, - source TEXT NOT NULL, - model TEXT NOT NULL, - route TEXT NOT NULL, - tokens_in INTEGER NOT NULL DEFAULT 0, - tokens_out INTEGER NOT NULL DEFAULT 0, - requests INTEGER NOT NULL DEFAULT 0, - last_updated_at TEXT NOT NULL, - PRIMARY KEY (day, user_id, source, model, route) - ); - CREATE INDEX IF NOT EXISTS idx_llm_usage_daily_user_day - ON llm_usage_daily (user_id, day); - `); - - // Usage dashboard v2: hour-grain ledger (supersedes llm_usage_daily as the - // write target; daily kept as frozen archive). Mirrors schema.sql + - // migrate.ts (dual-path). Backfill of the daily archive lives in migrate.ts - // so it runs once on upgrade, not on every fresh initSchema. - // Spec: docs/superpowers/specs/2026-06-11-usage-dashboard-v2-design.md - this.db.exec(` - CREATE TABLE IF NOT EXISTS llm_usage_hourly ( - hour TEXT NOT NULL, - user_id TEXT NOT NULL, - source TEXT NOT NULL, - model TEXT NOT NULL, - route TEXT NOT NULL, - tokens_in INTEGER NOT NULL DEFAULT 0, - tokens_out INTEGER NOT NULL DEFAULT 0, - requests INTEGER NOT NULL DEFAULT 0, - last_updated_at TEXT NOT NULL, - PRIMARY KEY (hour, user_id, source, model, route) - ); - CREATE INDEX IF NOT EXISTS idx_llm_usage_hourly_user_hour - ON llm_usage_hourly (user_id, hour); - `); + return schemaRepo.initSchema(this.db); } private runPreSchemaCompatibilityMigrations(): void { - const hasBrowserSessionProfiles = this.db - .prepare("SELECT 1 FROM sqlite_master WHERE type = ? AND name = ?") - .get("table", "browser_session_profiles"); - if (!hasBrowserSessionProfiles) { - return; - } - - const columns = this.db - .prepare("PRAGMA table_info(browser_session_profiles)") - .all() as Array<{ name: string }>; - if (columns.some((column) => column.name === "space_id")) { - return; - } - - this.db.exec("ALTER TABLE browser_session_profiles ADD COLUMN space_id TEXT"); + return schemaRepo.runPreSchemaCompatibilityMigrations(this.db); } private ensureColumn(tableName: string, columnName: string, definition: string): void { - const columns = this.db.prepare(`PRAGMA table_info(${tableName})`).all() as Array<{ name: string }>; - if (columns.some((column) => column.name === columnName)) { - return; - } - this.db.prepare(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${definition}`).run(); + return schemaRepo.ensureColumn(this.db, tableName, columnName, definition); } private migrateWaitingSubtasksStatus(): void { - // Check if jobs table already has waiting_subtasks in its CHECK constraint - const tableInfo = this.db.prepare( - "SELECT sql FROM sqlite_master WHERE type='table' AND name='jobs'" - ).get() as { sql: string } | undefined; - if (!tableInfo || tableInfo.sql.includes('waiting_subtasks')) return; - - // Recreate jobs table with updated CHECK constraint - logger.info('Repository: migrating jobs table to support waiting_subtasks status...'); - this.db.transaction(() => { - this.db.exec(` - CREATE TABLE IF NOT EXISTS jobs_v2 ( - id TEXT PRIMARY KEY, - repo TEXT NOT NULL, - issue_number INTEGER NOT NULL, - pr_number INTEGER, - status TEXT NOT NULL DEFAULT 'queued' - CHECK (status IN ('queued','dispatching','running','succeeded','failed','retry','cancelled','waiting_human','waiting_subtasks')), - piece_name TEXT NOT NULL DEFAULT 'general', - required_profile TEXT NOT NULL DEFAULT 'auto', - task_class TEXT NOT NULL DEFAULT 'auto', - current_movement TEXT, - instruction TEXT NOT NULL DEFAULT '', - branch_name TEXT, - worktree_path TEXT, - attempt INTEGER NOT NULL DEFAULT 1, - max_attempts INTEGER NOT NULL DEFAULT 3, - next_retry_at TEXT, - error_summary TEXT, - resume_movement TEXT, - ask_count INTEGER NOT NULL DEFAULT 0, - worker_id TEXT, - parent_job_id TEXT, - subtask_depth INTEGER NOT NULL DEFAULT 0, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - updated_at TEXT NOT NULL DEFAULT (datetime('now')) - ); - INSERT INTO jobs_v2 - SELECT id, repo, issue_number, pr_number, status, piece_name, required_profile, task_class, - current_movement, instruction, branch_name, worktree_path, attempt, max_attempts, - next_retry_at, error_summary, resume_movement, ask_count, worker_id, - NULL AS parent_job_id, 0 AS subtask_depth, created_at, updated_at - FROM jobs; - DROP TABLE jobs; - ALTER TABLE jobs_v2 RENAME TO jobs; - `); - })(); - logger.info('Repository: jobs table migration complete'); + return schemaRepo.migrateWaitingSubtasksStatus(this.db); } - // Non-terminal job states. A task with a job in any of these already has work - // pending/running, so a new user comment should be appended to it rather than - // spawning a second job. waiting_human is excluded on purpose: a comment there - // is the answer that resumes via a fresh job. - private static readonly PENDING_JOB_STATES = ['queued', 'dispatching', 'running', 'waiting_subtasks', 'retry'] as const; - private insertJobSync(params: CreateJobParams): Job { - const id = randomUUID(); - const now = new Date().toISOString(); - const pieceName = params.pieceName ?? 'chat'; - const maxAttempts = params.maxAttempts ?? 3; - const resumeMovement = params.resumeMovement ?? null; - const askCount = params.askCount ?? 0; - const requiredRole = deriveJobRole(params.instruction, params.role ?? params.profile); + return jobsRepo.insertJobSync(this.db, params); + } - this.db - .prepare( - `INSERT INTO jobs (id, repo, issue_number, status, piece_name, required_profile, task_class, instruction, attempt, max_attempts, resume_movement, ask_count, worker_id, parent_job_id, continued_from_job_id, subtask_depth, owner_id, visibility, visibility_scope_org_id, browser_session_profile_id, task_kind, payload, space_id, runtime_dir, created_at, updated_at) - VALUES (@id, @repo, @issueNumber, 'queued', @pieceName, @requiredRole, 'auto', @instruction, 1, @maxAttempts, @resumeMovement, @askCount, NULL, @parentJobId, @continuedFromJobId, @subtaskDepth, @ownerId, @visibility, @visibilityScopeOrgId, @browserSessionProfileId, @taskKind, @payload, @spaceId, @runtimeDir, @now, @now)` - ) - .run({ - id, - repo: params.repo, - issueNumber: params.issueNumber, - pieceName, - instruction: params.instruction, - maxAttempts, - resumeMovement, - askCount, - requiredRole, - parentJobId: params.parentJobId ?? null, - continuedFromJobId: params.continuedFromJobId ?? null, - subtaskDepth: params.subtaskDepth ?? 0, - ownerId: params.ownerId ?? null, - visibility: params.visibility ?? 'private', - visibilityScopeOrgId: params.visibilityScopeOrgId ?? null, - browserSessionProfileId: params.browserSessionProfileId ?? null, - taskKind: params.taskKind ?? 'agent', - payload: params.payload ?? null, - spaceId: params.spaceId ?? null, - runtimeDir: params.runtimeDir ?? null, - now, - }); + private getJobSync(id: string): Job | null { + return jobsRepo.getJobSync(this.db, id); + } - const job = this.getJobSync(id); - if (!job) throw new Error(`createJob: failed to retrieve created job ${id}`); - return job; + private async buildSubtaskInfos(subJobs: Job[], maxDepth: number = 3): Promise { + return jobsRepo.buildSubtaskInfos(this.db, subJobs, maxDepth); } async createJob(params: CreateJobParams): Promise { - return this.insertJobSync(params); + return jobsRepo.createJob(this.db, params); } - /** - * Atomically reuse-or-create. If a non-terminal job already exists for the - * issue, return it (created=false) without inserting; otherwise insert a new - * queued job (created=true). better-sqlite3 transactions run synchronously, so - * the check+insert can't interleave with a concurrent request — this closes - * the TOCTOU race where two near-simultaneous comments each spawned a job, - * leaving the newest (queued) duplicate as the task's latestJob while an older - * job actually ran (status stuck on "Inbox"). - */ - createJobIfNoPending( - params: CreateJobParams, - opts?: { blockOnToolRequestPause?: boolean }, - ): { job: Job; created: boolean; blockedByToolRequest?: boolean } { - const states = Repository.PENDING_JOB_STATES; - const placeholders = states.map(() => '?').join(','); - const tx = this.db.transaction((): { job: Job; created: boolean; blockedByToolRequest?: boolean } => { - // Tool-request mechanism: atomically refuse to create a normal resume job - // while a job is parked for tool approval (resolved via approve/deny, which - // re-queues the original). Done inside the same transaction as the insert - // so there is no check-then-act race with the engine parking the job. - if (opts?.blockOnToolRequestPause) { - const parked = this.db - .prepare( - `SELECT * FROM jobs WHERE repo = ? AND issue_number = ? - AND status = 'waiting_human' AND wait_reason = 'tool_request' - ORDER BY created_at DESC, rowid DESC LIMIT 1`, - ) - .get(params.repo, params.issueNumber) as JobRow | undefined; - if (parked) return { job: rowToJob(parked), created: false, blockedByToolRequest: true }; - } - const existing = this.db - .prepare( - `SELECT * FROM jobs WHERE repo = ? AND issue_number = ? AND status IN (${placeholders}) - ORDER BY created_at DESC, rowid DESC LIMIT 1`, - ) - .get(params.repo, params.issueNumber, ...states) as JobRow | undefined; - if (existing) return { job: rowToJob(existing), created: false }; - return { job: this.insertJobSync(params), created: true }; - }); - return tx(); + createJobIfNoPending(params: CreateJobParams, opts?: { blockOnToolRequestPause?: boolean }): { job: Job; created: boolean; blockedByToolRequest?: boolean } { + return jobsRepo.createJobIfNoPending(this.db, params, opts); } async getJob(id: string, opts?: { viewer?: Express.User }): Promise { - const viewerClause = opts?.viewer - ? buildVisibilityWhere(opts.viewer, 'j', { spaceColumn: 'space_id' }) - : { clause: '1=1', params: [] as unknown[] }; - const row = this.db - .prepare(`SELECT j.* FROM jobs j WHERE j.id = ? AND ${viewerClause.clause}`) - .get(id, ...viewerClause.params) as JobRow | undefined; - return row ? rowToJob(row) : null; + return jobsRepo.getJob(this.db, id, opts); } async createSpace(params: CreateSpaceParams): Promise { - const id = params.id ?? randomUUID(); - this.db - .prepare( - `INSERT INTO spaces (id, kind, title, description, owner_id, visibility, visibility_scope_org_id, status, brand_color, workspace_dir) - VALUES (@id, @kind, @title, @description, @ownerId, @visibility, @visibilityScopeOrgId, 'open', @brandColor, @workspaceDir)` - ) - .run({ - id, - kind: params.kind, - title: params.title, - description: params.description ?? '', - ownerId: params.ownerId, - visibility: params.visibility ?? 'private', - visibilityScopeOrgId: params.visibilityScopeOrgId ?? null, - brandColor: params.brandColor ?? null, - workspaceDir: params.workspaceDir ?? null, - }); - const space = await this.getSpace(id); - if (!space) throw new Error('createSpace: failed to load inserted space'); - return space; + return spacesRepo.createSpace(this.db, params); } async getSpace(id: string, opts?: { viewer?: Express.User }): Promise { - // 個人スペースは所有者のみ可視(admin 含む)。buildSpaceVisibilityWhere が - // kind='personal' を owner_id 一致に限定し、kind='case' は従来通り扱う。 - const viewerClause = opts?.viewer - ? buildSpaceVisibilityWhere(opts.viewer, 's') - : { clause: '1=1', params: [] as unknown[] }; - const row = this.db - .prepare(`SELECT s.* FROM spaces s WHERE s.id = ? AND ${viewerClause.clause}`) - .get(id, ...viewerClause.params) as SpaceRow | undefined; - return row ? rowToSpace(row) : null; + return spacesRepo.getSpace(this.db, id, opts); } async listSpaces(opts?: { viewer?: Express.User; includeArchived?: boolean }): Promise { - // 個人スペースは所有者のみ可視(admin 含む)。これにより admin の一覧/レールに - // 他ユーザーの個人スペースが混ざらず、UI の誤ワークスペース解決を防ぐ。 - const viewerClause = opts?.viewer - ? buildSpaceVisibilityWhere(opts.viewer, 's') - : { clause: '1=1', params: [] as unknown[] }; - const statusClause = opts?.includeArchived ? '1=1' : "s.status = 'open'"; - const rows = this.db - .prepare(` - SELECT s.* FROM spaces s - WHERE ${statusClause} AND ${viewerClause.clause} - ORDER BY s.kind = 'personal' DESC, s.updated_at DESC - `) - .all(...viewerClause.params) as SpaceRow[]; - return rows.map(rowToSpace); + return spacesRepo.listSpaces(this.db, opts); } - async updateSpace( - id: string, - patch: { title?: string; description?: string; brandColor?: string | null; visibility?: 'private' | 'org' | 'public'; visibilityScopeOrgId?: string | null }, - ): Promise { - const sets: string[] = []; - const vals: unknown[] = []; - if (patch.title !== undefined) { sets.push('title = ?'); vals.push(patch.title); } - if (patch.description !== undefined) { sets.push('description = ?'); vals.push(patch.description); } - if (patch.brandColor !== undefined) { sets.push('brand_color = ?'); vals.push(patch.brandColor); } - if (patch.visibility !== undefined) { sets.push('visibility = ?'); vals.push(patch.visibility); } - if (patch.visibilityScopeOrgId !== undefined) { sets.push('visibility_scope_org_id = ?'); vals.push(patch.visibilityScopeOrgId); } - if (sets.length === 0) return this.getSpace(id); - sets.push("updated_at = datetime('now')"); - this.db.prepare(`UPDATE spaces SET ${sets.join(', ')} WHERE id = ?`).run(...vals, id); - return this.getSpace(id); + async updateSpace(id: string, patch: { title?: string; description?: string; brandColor?: string | null; visibility?: 'private' | 'org' | 'public'; visibilityScopeOrgId?: string | null }): Promise { + return spacesRepo.updateSpace(this.db, id, patch); } async archiveSpace(id: string): Promise { - this.db - .prepare(`UPDATE spaces SET status = 'archived', updated_at = datetime('now') WHERE id = ? AND kind = 'case'`) - .run(id); + return spacesRepo.archiveSpace(this.db, id); } async setSpaceWorkspaceDir(id: string, workspaceDir: string): Promise { - this.db - .prepare(`UPDATE spaces SET workspace_dir = ?, updated_at = datetime('now') WHERE id = ?`) - .run(workspaceDir, id); + return spacesRepo.setSpaceWorkspaceDir(this.db, id, workspaceDir); } - /** - * ロールバック専用のハード削除。通常は archiveSpace を使う。 - * 個人スペースは「削除不可」(spec §5.7) なので kind='case' のみ消す。 - * この backstop により、誤って personal id を渡しても個人スペースは保護される。 - */ async hardDeleteSpace(id: string): Promise { - const tx = this.db.transaction((sid: string) => { - // Crypto-shred space-owned credential DEKs EXPLICITLY (Space-as-Principal P2). - // We don't rely on FK ON DELETE CASCADE because migrate.ts intentionally - // omits FK clauses, so a migrated DB's space_ssh_deks would otherwise - // outlive the space. Gate on kind='case' (subquery) so a mistaken personal - // id never shreds a personal space's DEK. Runs before the spaces delete so - // the guard subquery still sees the row. - const isCase = `AND space_id IN (SELECT id FROM spaces WHERE kind = 'case')`; - this.db.prepare(`DELETE FROM space_ssh_deks WHERE space_id = ? ${isCase}`).run(sid); - this.db.prepare(`DELETE FROM space_browser_deks WHERE space_id = ? ${isCase}`).run(sid); - this.db.prepare(`DELETE FROM spaces WHERE id = ? AND kind = 'case'`).run(sid); - }); - tx(id); + return spacesRepo.hardDeleteSpace(this.db, id); } - /** - * ユーザーの個人スペースを返す。無ければ生成する(冪等)。 - * UNIQUE INDEX idx_spaces_personal_owner により1ユーザー1個に保たれる。 - * 既存ユーザーの初回アクセス移行を兼ねる(spec §7)。 - * 個人スペースの workspace_dir はこの計画では NULL(フォルダ物理ワイヤリングは計画2)。 - */ async ensurePersonalSpace(ownerId: string, displayName?: string): Promise { - const existing = this.db - .prepare(`SELECT s.* FROM spaces s WHERE s.owner_id = ? AND s.kind = 'personal'`) - .get(ownerId) as SpaceRow | undefined; - if (existing) return rowToSpace(existing); - return this.createSpace({ - kind: 'personal', - title: displayName ? `${displayName} の個人ワークスペース` : '個人ワークスペース', - ownerId, - visibility: 'private', - }); + return spacesRepo.ensurePersonalSpace(this.db, ownerId, displayName); } - // ─── スペース・メンバー(協働者)CRUD ───────────────────────────── - // owner_id は members 行を持たない根オーナー。これらは追加協働者だけを扱う。 - - /** - * メンバーを追加または上書きする(冪等)。既存行があれば role/invited_by を更新。 - * owner_id 本人を渡してはならない(既に owner)。呼び出し側でガードする。 - */ async addSpaceMember(params: { spaceId: string; userId: string; role: SpaceMemberRoleValue; invitedBy?: string | null; }): Promise { - this.db - .prepare( - `INSERT INTO space_members (space_id, user_id, role, invited_by) - VALUES (@spaceId, @userId, @role, @invitedBy) - ON CONFLICT(space_id, user_id) DO UPDATE SET - role = excluded.role, - invited_by = excluded.invited_by` - ) - .run({ - spaceId: params.spaceId, - userId: params.userId, - role: params.role, - invitedBy: params.invitedBy ?? null, - }); + return spacesRepo.addSpaceMember(this.db, params); } - /** スペースのメンバー一覧(users JOIN で表示情報付き)。owner_id は含まない。 */ async listSpaceMembers(spaceId: string): Promise { - const rows = this.db - .prepare(` - SELECT sm.space_id, sm.user_id, sm.role, sm.invited_by, sm.created_at, - u.name AS name, u.email AS email, u.avatar_url AS avatar_url - FROM space_members sm - LEFT JOIN users u ON u.id = sm.user_id - WHERE sm.space_id = ? - ORDER BY sm.created_at ASC, sm.user_id ASC - `) - .all(spaceId) as Array<{ - space_id: string; - user_id: string; - role: string; - invited_by: string | null; - created_at: string; - name: string | null; - email: string | null; - avatar_url: string | null; - }>; - return rows.map((r) => ({ - spaceId: r.space_id, - userId: r.user_id, - role: r.role as SpaceMemberRoleValue, - invitedBy: r.invited_by, - createdAt: r.created_at, - name: r.name, - email: r.email, - avatarUrl: r.avatar_url, - })); + return spacesRepo.listSpaceMembers(this.db, spaceId); } - /** - * 指定ユーザーのメンバーロールを返す。メンバー行が無ければ null。 - * 根オーナー(owner_id)は members 行を持たないため null を返す。 - * 呼び出し側は owner_id を別途 'owner' として扱うこと(canManageSpace 参照)。 - */ getSpaceMemberRole(spaceId: string, userId: string): SpaceMemberRoleValue | null { - const row = this.db - .prepare(`SELECT role FROM space_members WHERE space_id = ? AND user_id = ?`) - .get(spaceId, userId) as { role: string } | undefined; - return row ? (row.role as SpaceMemberRoleValue) : null; + return spacesRepo.getSpaceMemberRole(this.db, spaceId, userId); } - /** スペースのツール制限ポリシー(JSON 文字列)を返す。未設定なら null。 */ getSpaceToolPolicy(spaceId: string): string | null { - const row = this.db - .prepare(`SELECT tool_policy FROM spaces WHERE id = ?`) - .get(spaceId) as { tool_policy: string | null } | undefined; - return row?.tool_policy ?? null; + return spacesRepo.getSpaceToolPolicy(this.db, spaceId); } - /** スペースのツール制限ポリシーを更新する。null を渡すとクリア。 */ setSpaceToolPolicy(spaceId: string, policyJson: string | null): void { - this.db - .prepare(`UPDATE spaces SET tool_policy = ?, updated_at = datetime('now') WHERE id = ?`) - .run(policyJson, spaceId); + return spacesRepo.setSpaceToolPolicy(this.db, spaceId, policyJson); } - /** スペースの Python パッケージ desired-state(JSON 文字列)を返す。未設定なら null。 */ getSpacePythonPackages(spaceId: string): string | null { - const row = this.db - .prepare(`SELECT python_packages FROM spaces WHERE id = ?`) - .get(spaceId) as { python_packages: string | null } | undefined; - return row?.python_packages ?? null; + return spacesRepo.getSpacePythonPackages(this.db, spaceId); } - /** スペースの Python パッケージ desired-state を更新する。null を渡すとクリア。 */ setSpacePythonPackages(spaceId: string, packagesJson: string | null): void { - this.db - .prepare(`UPDATE spaces SET python_packages = ?, updated_at = datetime('now') WHERE id = ?`) - .run(packagesJson, spaceId); + return spacesRepo.setSpacePythonPackages(this.db, spaceId, packagesJson); } - /** - * 指定 user がスペースの行(タスク/ファイル/ログ等)を VIEW できるか。 - * buildVisibilityWhere の membership OR ブランチと同じルールをコード上で再現する: - * admin → true / space.owner_id === user.id(根オーナー)→ true / - * space_members に (spaceId, user.id) 行あり → true / それ以外 → false。 - * 二次ゲート(canViewTask / canUserSeeTask)が呼び出す。書き込み判定ではない。 - */ userCanViewSpace(spaceId: string, user: { id: string; role: string }): boolean { - if (user.role === 'admin') return true; - const owns = this.db - .prepare(`SELECT 1 FROM spaces WHERE id = ? AND owner_id = ?`) - .get(spaceId, user.id); - if (owns) return true; - const member = this.db - .prepare(`SELECT 1 FROM space_members WHERE space_id = ? AND user_id = ?`) - .get(spaceId, user.id); - return !!member; + return spacesRepo.userCanViewSpace(this.db, spaceId, user); } - /** 既存メンバーのロールを変更する。行が無ければ no-op。 */ - async updateSpaceMemberRole( - spaceId: string, - userId: string, - role: SpaceMemberRoleValue, - ): Promise { - this.db - .prepare(`UPDATE space_members SET role = ? WHERE space_id = ? AND user_id = ?`) - .run(role, spaceId, userId); + async updateSpaceMemberRole(spaceId: string, userId: string, role: SpaceMemberRoleValue): Promise { + return spacesRepo.updateSpaceMemberRole(this.db, spaceId, userId, role); } - /** メンバーを除去する。行が無ければ no-op。 */ async removeSpaceMember(spaceId: string, userId: string): Promise { - this.db - .prepare(`DELETE FROM space_members WHERE space_id = ? AND user_id = ?`) - .run(spaceId, userId); + return spacesRepo.removeSpaceMember(this.db, spaceId, userId); } - // ─── スペース招待リンク(再利用トークン)───────────────────────────── - private rowToSpaceInvite(row: { token: string; space_id: string; @@ -2225,412 +185,98 @@ export class Repository { expires_at: string | null; revoked_at: string | null; }): SpaceInvite { - return { - token: row.token, - spaceId: row.space_id, - role: row.role as SpaceInviteRole, - createdBy: row.created_by, - createdAt: row.created_at, - expiresAt: row.expires_at, - revokedAt: row.revoked_at, - }; + return spacesRepo.rowToSpaceInvite(row); } - /** - * 招待が今この瞬間に有効か(無効化されておらず、期限切れでない)。 - * 期限比較は SQLite と同じ datetime('now')(UTC)基準の ISO 文字列辞書順。 - */ isSpaceInviteValid(invite: SpaceInvite | null | undefined): invite is SpaceInvite { - if (!invite) return false; - if (invite.revokedAt) return false; - if (invite.expiresAt) { - const now = this.db.prepare(`SELECT datetime('now') AS n`).get() as { n: string }; - if (invite.expiresAt <= now.n) return false; - } - return true; + return spacesRepo.isSpaceInviteValid(this.db, invite); } - /** - * スペースの招待リンクを (再)生成する。再利用方針=スペースごとに有効リンク1本。 - * 既存 invite 行は削除してから新トークンを 1 行 insert する。 - * @param expiresInDays 有効日数。未指定/null は無期限。期限は SQLite の - * datetime('now','+N days') で保存し、isSpaceInviteValid と同じ形式に揃える。 - */ createSpaceInvite(params: { spaceId: string; role: SpaceInviteRole; createdBy?: string | null; expiresInDays?: number | null; }): SpaceInvite { - const token = randomUUID(); - // 整数日数のみ受理(負/0/非整数は無期限扱い)。文字列補間するため厳格に検証する。 - const days = - typeof params.expiresInDays === 'number' && - Number.isInteger(params.expiresInDays) && - params.expiresInDays > 0 - ? params.expiresInDays - : null; - const tx = this.db.transaction(() => { - this.db.prepare(`DELETE FROM space_invites WHERE space_id = ?`).run(params.spaceId); - this.db - .prepare( - `INSERT INTO space_invites (token, space_id, role, created_by, expires_at) - VALUES (@token, @spaceId, @role, @createdBy, - ${days === null ? 'NULL' : `datetime('now', '+${days} days')`})`, - ) - .run({ - token, - spaceId: params.spaceId, - role: params.role, - createdBy: params.createdBy ?? null, - }); - }); - tx(); - return this.getSpaceInviteByToken(token)!; + return spacesRepo.createSpaceInvite(this.db, params); } - /** スペースの現行 invite(有効・無効問わず最新の1本)。無ければ null。 */ getSpaceInvite(spaceId: string): SpaceInvite | null { - const row = this.db - .prepare(`SELECT * FROM space_invites WHERE space_id = ? ORDER BY created_at DESC LIMIT 1`) - .get(spaceId) as Parameters[0] | undefined; - return row ? this.rowToSpaceInvite(row) : null; + return spacesRepo.getSpaceInvite(this.db, spaceId); } - /** トークンで invite を引く(有効性は呼び出し側で判定)。無ければ null。 */ getSpaceInviteByToken(token: string): SpaceInvite | null { - const row = this.db - .prepare(`SELECT * FROM space_invites WHERE token = ?`) - .get(token) as Parameters[0] | undefined; - return row ? this.rowToSpaceInvite(row) : null; + return spacesRepo.getSpaceInviteByToken(this.db, token); } - /** スペースの現行 invite を無効化する。行が無ければ no-op。 */ revokeSpaceInvite(spaceId: string): void { - this.db - .prepare( - `UPDATE space_invites SET revoked_at = datetime('now') - WHERE space_id = ? AND revoked_at IS NULL`, - ) - .run(spaceId); + return spacesRepo.revokeSpaceInvite(this.db, spaceId); } - // ─── 公開アプリ共有リンク(read-only) ─────────────────────────────── - // - // スペース内の 1 ワークスペースアプリ(apps/{appName}/)を、ログイン不要の - // 公開 URL で read-only 共有するためのトークン。失効後の再発行は新トークンを - // 作る(失効済みトークンは再利用しない)ため、未失効リンクが既にある場合のみ - // 再利用する。 - - /** (spaceId, appName) の未失効リンクがあれば再利用、無ければ新規発行して token を返す。 */ createAppShareLink(spaceId: string, appName: string, createdBy: string | null): { token: string } { - const existing = this.db - .prepare( - `SELECT token FROM app_share_links - WHERE space_id = ? AND app_name = ? AND revoked_at IS NULL - ORDER BY created_at DESC LIMIT 1`, - ) - .get(spaceId, appName) as { token: string } | undefined; - if (existing) return { token: existing.token }; - const token = randomUUID(); - this.db - .prepare( - `INSERT INTO app_share_links (token, space_id, app_name, created_by) - VALUES (?, ?, ?, ?)`, - ) - .run(token, spaceId, appName, createdBy ?? null); - return { token }; + return appSharesRepo.createAppShareLink(this.db, spaceId, appName, createdBy); } - /** (spaceId, appName) の最新リンク(有効・無効問わず)を返す。無ければ null。 */ getAppShareLink(spaceId: string, appName: string): { token: string; revokedAt: string | null } | null { - const row = this.db - .prepare( - `SELECT token, revoked_at FROM app_share_links - WHERE space_id = ? AND app_name = ? - ORDER BY created_at DESC LIMIT 1`, - ) - .get(spaceId, appName) as { token: string; revoked_at: string | null } | undefined; - if (!row) return null; - return { token: row.token, revokedAt: row.revoked_at ?? null }; + return appSharesRepo.getAppShareLink(this.db, spaceId, appName); } - /** (spaceId, appName) の未失効リンクを全て失効させる。行が無ければ no-op。 */ revokeAppShareLink(spaceId: string, appName: string): void { - this.db - .prepare( - `UPDATE app_share_links SET revoked_at = datetime('now') - WHERE space_id = ? AND app_name = ? AND revoked_at IS NULL`, - ) - .run(spaceId, appName); + return appSharesRepo.revokeAppShareLink(this.db, spaceId, appName); } - /** トークンを (spaceId, appName) に解決する。失効済み・不正トークンは null。 */ resolveAppShareToken(token: string): { spaceId: string; appName: string } | null { - const row = this.db - .prepare( - `SELECT space_id, app_name FROM app_share_links - WHERE token = ? AND revoked_at IS NULL`, - ) - .get(token) as { space_id: string; app_name: string } | undefined; - if (!row) return null; - return { spaceId: row.space_id, appName: row.app_name }; + return appSharesRepo.resolveAppShareToken(this.db, token); } - /** - * タスクの実効スペースを解決し FolderContext を返す。 - * - space_id があればそのスペース、無ければ owner の個人スペース(無ければ lazy 生成)。 - * - owner も無い(no-auth)の場合は 'local' の個人スペース。 - * 返り値は userPiecesDir/readUserAgentsMd 等にそのまま渡せる { rootDir, leafId }。 - */ - async resolveTaskFolderContext( - taskId: number, - userFolderRoot: string, - ): Promise { - const { resolveSpaceFolder } = await import('../spaces/folder-resolver.js'); - const row = this.db - .prepare(`SELECT space_id, owner_id FROM local_tasks WHERE id = ?`) - .get(taskId) as { space_id: string | null; owner_id: string | null } | undefined; - const ownerId = row?.owner_id ?? 'local'; - if (row?.space_id) { - const space = await this.getSpace(row.space_id); - if (space) return resolveSpaceFolder(space, userFolderRoot); - // 参照先スペースが消えている場合は個人スペースにフォールバック - } - const personal = await this.ensurePersonalSpace(ownerId); - return resolveSpaceFolder(personal, userFolderRoot); + async resolveTaskFolderContext(taskId: number, userFolderRoot: string): Promise { + return localTasksRepo.resolveTaskFolderContext(this.db, taskId, userFolderRoot); } async createLocalTask(params: CreateLocalTaskParams): Promise { - const result = this.db - .prepare( - `INSERT INTO local_tasks (title, title_source, body, piece_name, profile, output_format, ask_policy, priority, workspace_path, workspace_mode, owner_id, visibility, visibility_scope_org_id, browser_session_profile_id, space_id, options) - VALUES (@title, @titleSource, @body, @pieceName, @profile, @outputFormat, @askPolicy, @priority, @workspacePath, @workspaceMode, @ownerId, @visibility, @visibilityScopeOrgId, @browserSessionProfileId, @spaceId, @options)` - ) - .run({ - title: params.title, - titleSource: params.titleSource ?? 'auto', - body: params.body, - pieceName: params.pieceName ?? 'chat', - profile: params.profile ?? 'auto', - outputFormat: params.outputFormat ?? 'markdown', - askPolicy: params.askPolicy ?? 'low', - priority: params.priority ?? 'medium', - workspacePath: params.workspacePath ?? null, - workspaceMode: params.workspaceMode ?? 'persistent', - ownerId: params.ownerId ?? null, - visibility: params.visibility ?? 'private', - visibilityScopeOrgId: params.visibilityScopeOrgId ?? null, - browserSessionProfileId: params.browserSessionProfileId ?? null, - spaceId: params.spaceId ?? null, - options: JSON.stringify(params.options ?? {}), - }); - - const task = await this.getLocalTask(Number(result.lastInsertRowid)); - if (!task) throw new Error('createLocalTask: failed to load inserted task'); - return task; - } - - /** サブジョブ一覧を SubtaskInfo[] に変換。waiting_subtasks の子は再帰的に children を取得する */ - private async buildSubtaskInfos(subJobs: Job[], maxDepth: number = 3): Promise { - return Promise.all(subJobs.map(async (j): Promise => { - const info: SubtaskInfo = { - id: j.id, - issueNumber: j.issueNumber, - status: j.status, - instruction: j.instruction, - worktreePath: j.worktreePath, - createdAt: j.createdAt, - updatedAt: j.updatedAt, - }; - // 再帰: waiting_subtasks のサブタスクは孫タスク情報も取得 - if (j.status === 'waiting_subtasks' && maxDepth > 0) { - const grandChildren = await this.getSubJobs(j.id); - if (grandChildren.length > 0) { - info.children = await this.buildSubtaskInfos(grandChildren, maxDepth - 1); - info.childCount = grandChildren.length; - info.childCompleted = grandChildren.filter(g => - ['succeeded', 'failed', 'cancelled'].includes(g.status) - ).length; - } - } - return info; - })); + return localTasksRepo.createLocalTask(this.db, params); } async getLocalTask(taskId: number, opts?: { viewer?: Express.User }): Promise { - const viewerClause = opts?.viewer - ? buildVisibilityWhere(opts.viewer, 'lt', { spaceColumn: 'space_id' }) - : { clause: '1=1', params: [] as unknown[] }; - const row = this.db - .prepare(` - SELECT lt.*, - ${LOCAL_TASK_DISPLAY_SELECT} - FROM local_tasks lt - ${LOCAL_TASK_DISPLAY_JOIN} - WHERE lt.id = ? AND ${viewerClause.clause} - `) - .get(taskId, ...viewerClause.params) as LocalTaskRow | undefined; - if (!row) return null; - const task = rowToLocalTask(row); - task.latestJob = await this.getLatestJobForIssue(localTaskRepoName(taskId), taskId); - // サブタスク情報を付与 - if (task.latestJob) { - const subJobs = await this.getSubJobs(task.latestJob.id); - if (subJobs.length > 0) { - task.subtasks = await this.buildSubtaskInfos(subJobs); - task.subtaskCount = subJobs.length; - task.subtaskCompleted = subJobs.filter(j => - ['succeeded', 'failed', 'cancelled'].includes(j.status) - ).length; - } - } - return task; + return localTasksRepo.getLocalTask(this.db, taskId, opts); } async shareLocalTask(taskId: number): Promise { - const existing = await this.getLocalTask(taskId); - if (!existing) throw new Error(`Task ${taskId} not found`); - if (existing.shareToken) return existing.shareToken; - - const token = randomUUID(); - this.db.prepare( - `UPDATE local_tasks SET share_token = ?, shared_at = datetime('now'), updated_at = datetime('now') WHERE id = ?` - ).run(token, taskId); - return token; + return localTasksRepo.shareLocalTask(this.db, taskId); } async unshareLocalTask(taskId: number): Promise { - this.db.prepare( - `UPDATE local_tasks SET share_token = NULL, shared_at = NULL, updated_at = datetime('now') WHERE id = ?` - ).run(taskId); + return localTasksRepo.unshareLocalTask(this.db, taskId); } - /** - * Mission Brief: partial-replace update. Only fields explicitly provided - * are written; undefined leaves the field untouched. Passing all-empty - * strings is treated as "clear the brief" (NULL in storage). - * - * Returns the merged brief so callers can echo it back to the client - * without an extra read. - */ - async updateMissionBrief( - taskId: number, - patch: Partial, - ): Promise { - return this.updateMissionBriefSync(taskId, patch); + async updateMissionBrief(taskId: number, patch: Partial): Promise { + return localTasksRepo.updateMissionBrief(this.db, taskId, patch); } - /** Sync variant used by the engine's MissionBriefIO so it can be called - * from sync paths (e.g. buildSystemPrompt). better-sqlite3 is sync - * underneath anyway. */ updateMissionBriefSync(taskId: number, patch: Partial): MissionBrief | null { - const row = this.db - .prepare(`SELECT mission_brief, title_source FROM local_tasks WHERE id = ?`) - .get(taskId) as { mission_brief: string | null; title_source: string | null } | undefined; - const existing = parseMissionBrief(row?.mission_brief ?? null); - const next = {} as MissionBrief; - for (const field of MISSION_BRIEF_FIELDS) { - next[field] = patch[field] !== undefined ? patch[field] : existing?.[field] ?? ''; - } - const allEmpty = MISSION_BRIEF_FIELDS.every((field) => !next[field]); - const stored = allEmpty ? null : JSON.stringify(next); - - // Derive the task title from the agent's goal (no LLM call). Only when the - // goal value actually changed (agents re-send an unchanged brief across - // iterations — re-deriving every time would churn updated_at and flicker - // the title) and the user hasn't taken manual control (a user edit pins - // title_source='user' and is never overwritten). - const goalChanged = patch.goal !== undefined && patch.goal !== (existing?.goal ?? ''); - const derivedTitle = (goalChanged && (row?.title_source ?? 'auto') !== 'user') - ? buildTitleFromGoal(next.goal) - : ''; - - // Atomic: persist the brief and the derived title as one unit so a crash - // between them can't leave the title out of sync with the goal. - this.db.transaction(() => { - this.db.prepare( - `UPDATE local_tasks SET mission_brief = ?, updated_at = datetime('now') WHERE id = ?` - ).run(stored, taskId); - if (derivedTitle) { - this.db.prepare( - `UPDATE local_tasks SET title = ?, title_source = 'agent' WHERE id = ?` - ).run(derivedTitle, taskId); - } - })(); - return allEmpty ? null : next; + return localTasksRepo.updateMissionBriefSync(this.db, taskId, patch); } - /** Sync read of the mission brief column. Used by MissionBriefIO.read() */ getMissionBriefSync(taskId: number): MissionBrief | null { - const row = this.db.prepare(`SELECT mission_brief FROM local_tasks WHERE id = ?`).get(taskId) as { mission_brief: string | null } | undefined; - return parseMissionBrief(row?.mission_brief ?? null); + return localTasksRepo.getMissionBriefSync(this.db, taskId); } - /** - * Construct a MissionBriefIO bound to a specific local task. The engine - * uses this to thread mission brief read/write into the ToolContext - * without leaking the repository instance into tool code. - */ makeMissionBriefIO(taskId: number): import('../engine/tools/core.js').MissionBriefIO { - return { - read: () => this.getMissionBriefSync(taskId), - update: (patch) => this.updateMissionBriefSync(taskId, patch), - }; + return localTasksRepo.makeMissionBriefIO(this.db, taskId); } - /** - * Construct a TaskConversationIO bound to a single local task (comments) + - * this run's transcript path. Used by SearchTaskConversation / - * ReadTaskConversation; the binding is what scopes those tools to the current - * task so they can't read another task's logs. - */ - makeTaskConversationIO( - taskId: number, - transcriptPath?: string, - ): import('../engine/tools/core.js').TaskConversationIO { - return { - listComments: () => this.listLocalTaskComments(taskId), - transcriptPath, - }; + makeTaskConversationIO(taskId: number, transcriptPath?: string): import('../engine/tools/core.js').TaskConversationIO { + return localTasksRepo.makeTaskConversationIO(this.db, taskId, transcriptPath); } - /** - * Construct a WorkspaceTaskSearchIO scoped to the current task + owner. - * Used by SearchWorkspaceTasks / ReadWorkspaceTaskAround; searchWorkspaceTasks / - * readWorkspaceTaskAround themselves enforce the space/owner scoping, this is - * just the binding closure. fts5Available lets the tool degrade gracefully - * when the FTS5 index table is missing (e.g. pre-migration DB). - */ - makeWorkspaceTaskSearchIO( - currentTaskId: number, - ownerId: string, - ): import('../engine/tools/core.js').WorkspaceTaskSearchIO { - const fts5Available = !!this.db - .prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='task_comment_fts'") - .get(); - return { - fts5Available, - search: (query, opts) => this.searchWorkspaceTasks(currentTaskId, ownerId, query, opts), - around: (commentId, before, after) => - this.readWorkspaceTaskAround(currentTaskId, ownerId, commentId, before, after), - }; + makeTranscriptOnlyConversationIO(transcriptPath: string | undefined): import('../engine/tools/core.js').TaskConversationIO { + return localTasksRepo.makeTranscriptOnlyConversationIO(transcriptPath); } - // ── Workspace file provenance ──────────────────────────────────────────── - // Every read/write is scoped by workspace_path (the shared files-tree root), - // which is the isolation boundary: a query for workspace A can never surface - // workspace B's rows (and thus never another user's task lineage in a shared - // space). schema.sql / migrate.ts / initSchema keep the table in triple sync. + makeWorkspaceTaskSearchIO(currentTaskId: number, ownerId: string): import('../engine/tools/core.js').WorkspaceTaskSearchIO { + return taskSearchRepo.makeWorkspaceTaskSearchIO(this.db, currentTaskId, ownerId); + } - /** - * Upsert a provenance row for one workspace file. - * - event 'create' with no existing row → inserts the creator identity. - * - existing row (any event) → updates only the last-modified fields, so the - * original creator + source_kind are preserved (Edit / overwrite semantics). - * - event 'modify' with no existing row → backfills a row for a file that - * pre-dates the ledger (source_kind=imported_existing, creator NULL). - */ recordProvenance(params: { workspacePath: string; relPath: string; @@ -2644,384 +290,31 @@ export class Repository { checksum?: string | null; note?: string | null; }): void { - const now = new Date().toISOString(); - const taskId = params.taskId ?? null; - const jobId = params.jobId ?? null; - const checksum = params.checksum ?? null; - const existing = this.db - .prepare('SELECT id FROM workspace_file_provenance WHERE workspace_path = ? AND rel_path = ?') - .get(params.workspacePath, params.relPath) as { id: number } | undefined; - - if (existing) { - // Preserve creator + source_kind; only advance the last-modified pointer. - this.db - .prepare( - `UPDATE workspace_file_provenance - SET last_modified_by_task_id = ?, last_modified_by_job_id = ?, - last_modified_at = ?, checksum = COALESCE(?, checksum) - WHERE id = ?`, - ) - .run(taskId, jobId, now, checksum, existing.id); - return; - } - - // No row yet. A create event knows the creator; a modify event means the - // file pre-dates the ledger → backfill with an unknown creator. - const sourceKind = - params.event === 'create' ? params.sourceKind ?? 'unknown' : 'imported_existing'; - const createdTask = params.event === 'create' ? taskId : null; - const createdJob = params.event === 'create' ? jobId : null; - const createdPiece = params.event === 'create' ? params.piece ?? null : null; - const createdMovement = params.event === 'create' ? params.movement ?? null : null; - this.db - .prepare( - `INSERT INTO workspace_file_provenance - (space_id, workspace_path, rel_path, created_by_task_id, created_by_job_id, - created_by_piece, created_by_movement, source_kind, first_seen_at, - last_modified_by_task_id, last_modified_by_job_id, last_modified_at, checksum, note) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - ) - .run( - params.spaceId ?? null, - params.workspacePath, - params.relPath, - createdTask, - createdJob, - createdPiece, - createdMovement, - sourceKind, - now, - taskId, - jobId, - now, - checksum, - params.note ?? null, - ); + return provenanceRepo.recordProvenance(this.db, params); } - getProvenance( - workspacePath: string, - relPath: string, - ): import('../engine/tools/core.js').FileProvenanceRecord | null { - const row = this.db - .prepare('SELECT * FROM workspace_file_provenance WHERE workspace_path = ? AND rel_path = ?') - .get(workspacePath, relPath) as WorkspaceFileProvenanceRow | undefined; - return row ? rowToFileProvenance(row) : null; + getProvenance(workspacePath: string, relPath: string): import('../engine/tools/core.js').FileProvenanceRecord | null { + return provenanceRepo.getProvenance(this.db, workspacePath, relPath); } - listProvenance( - workspacePath: string, - filters?: import('../engine/tools/core.js').FileProvenanceListFilters, - ): import('../engine/tools/core.js').FileProvenanceRecord[] { - const clauses = ['workspace_path = ?']; - const args: unknown[] = [workspacePath]; - if (filters?.pathPrefix) { - clauses.push('rel_path LIKE ? ESCAPE \'\\\''); - args.push(escapeLike(filters.pathPrefix) + '%'); - } - if (filters?.sourceKind) { - clauses.push('source_kind = ?'); - args.push(filters.sourceKind); - } - if (typeof filters?.createdByTaskId === 'number') { - clauses.push('created_by_task_id = ?'); - args.push(filters.createdByTaskId); - } - if (typeof filters?.lastModifiedByTaskId === 'number') { - clauses.push('last_modified_by_task_id = ?'); - args.push(filters.lastModifiedByTaskId); - } - if (filters?.includeUnknown === false) { - clauses.push("source_kind NOT IN ('unknown', 'imported_existing')"); - } - // Cap hard at 500 rows regardless of caller input (bounded output). - const limit = Math.min(Math.max(filters?.limit ?? 200, 1), 500); - const rows = this.db - .prepare( - `SELECT * FROM workspace_file_provenance - WHERE ${clauses.join(' AND ')} - ORDER BY rel_path ASC - LIMIT ?`, - ) - .all(...args, limit) as WorkspaceFileProvenanceRow[]; - return rows.map(rowToFileProvenance); + listProvenance(workspacePath: string, filters?: import('../engine/tools/core.js').FileProvenanceListFilters): import('../engine/tools/core.js').FileProvenanceRecord[] { + return provenanceRepo.listProvenance(this.db, workspacePath, filters); } - /** Construct a workspace-bound read IO for GetFileProvenance / ListWorkspaceFiles. */ makeFileProvenanceIO(workspacePath: string): import('../engine/tools/core.js').FileProvenanceIO { - return { - get: (relPath) => this.getProvenance(workspacePath, relPath), - list: (filters) => this.listProvenance(workspacePath, filters), - }; + return provenanceRepo.makeFileProvenanceIO(this.db, workspacePath); } async getLocalTaskByShareToken(token: string): Promise { - const row = this.db - .prepare(` - SELECT lt.*, - ${LOCAL_TASK_DISPLAY_SELECT} - FROM local_tasks lt - ${LOCAL_TASK_DISPLAY_JOIN} - WHERE lt.share_token = ? - `) - .get(token) as LocalTaskRow | undefined; - if (!row) return null; - const task = rowToLocalTask(row); - task.latestJob = await this.getLatestJobForIssue(localTaskRepoName(task.id), task.id); - if (task.latestJob) { - const subJobs = await this.getSubJobs(task.latestJob.id); - if (subJobs.length > 0) { - task.subtasks = await this.buildSubtaskInfos(subJobs); - task.subtaskCount = subJobs.length; - task.subtaskCompleted = subJobs.filter(j => - ['succeeded', 'failed', 'cancelled'].includes(j.status) - ).length; - } - } - return task; + return localTasksRepo.getLocalTaskByShareToken(this.db, token); } async listLocalTasks(filter?: { ownerId?: string; viewer?: Express.User }): Promise { - // 1. Single JOIN query: local_tasks LEFT JOIN jobs (latest per task via correlated subquery) - const conditions: string[] = []; - const queryParams: unknown[] = []; - if (filter?.ownerId) { - conditions.push('lt.owner_id = ?'); - queryParams.push(filter.ownerId); - } - if (filter?.viewer) { - const w = buildVisibilityWhere(filter.viewer, 'lt', { spaceColumn: 'space_id' }); - conditions.push(w.clause); - queryParams.push(...w.params); - } - const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; - - const joinedRows = this.db - .prepare(` - SELECT - lt.*, - ${LOCAL_TASK_DISPLAY_SELECT}, - j.id AS job_id, - j.repo AS job_repo, - j.issue_number AS job_issue_number, - j.pr_number AS job_pr_number, - j.status AS job_status, - j.piece_name AS job_piece_name, - j.required_profile AS job_required_profile, - j.task_class AS job_task_class, - j.current_movement AS job_current_movement, - j.current_activity AS job_current_activity, - j.instruction AS job_instruction, - j.branch_name AS job_branch_name, - j.worktree_path AS job_worktree_path, - j.attempt AS job_attempt, - j.max_attempts AS job_max_attempts, - j.next_retry_at AS job_next_retry_at, - j.error_summary AS job_error_summary, - j.abort_reason AS job_abort_reason, - j.resume_movement AS job_resume_movement, - j.wait_reason AS job_wait_reason, - j.ask_count AS job_ask_count, - j.worker_id AS job_worker_id, - j.last_backend_id AS job_last_backend_id, - j.parent_job_id AS job_parent_job_id, - j.continued_from_job_id AS job_continued_from_job_id, - j.subtask_depth AS job_subtask_depth, - j.owner_id AS job_owner_id, - j.visibility AS job_visibility, - j.visibility_scope_org_id AS job_visibility_scope_org_id, - j.created_at AS job_created_at, - j.updated_at AS job_updated_at, - j.context_prompt_tokens AS job_context_prompt_tokens, - j.context_limit_tokens AS job_context_limit_tokens, - j.context_updated_at AS job_context_updated_at, - j.browser_session_profile_id AS job_browser_session_profile_id, - j.task_kind AS job_task_kind, - j.payload AS job_payload - FROM local_tasks lt - ${LOCAL_TASK_DISPLAY_JOIN} - LEFT JOIN jobs j ON j.id = ( - SELECT j2.id FROM jobs j2 - WHERE j2.repo = 'local/task-' || lt.id - AND j2.issue_number = lt.id - ORDER BY j2.created_at DESC, j2.rowid DESC - LIMIT 1 - ) - ${whereClause} - ORDER BY lt.updated_at DESC, lt.id DESC - `) - .all(...queryParams) as Array; - - // Build tasks with latestJob from joined data - const tasks: LocalTask[] = []; - const jobIds: string[] = []; - - for (const row of joinedRows) { - const task = rowToLocalTask(row); - if (row.job_id) { - task.latestJob = rowToJob({ - id: row.job_id, - repo: row.job_repo!, - issue_number: row.job_issue_number!, - pr_number: row.job_pr_number ?? null, - status: row.job_status!, - piece_name: row.job_piece_name!, - current_movement: row.job_current_movement ?? null, - current_activity: row.job_current_activity ?? null, - instruction: row.job_instruction!, - branch_name: row.job_branch_name ?? null, - worktree_path: row.job_worktree_path ?? null, - attempt: row.job_attempt!, - max_attempts: row.job_max_attempts!, - next_retry_at: row.job_next_retry_at ?? null, - error_summary: row.job_error_summary ?? null, - abort_reason: row.job_abort_reason ?? null, - resume_movement: row.job_resume_movement ?? null, - wait_reason: row.job_wait_reason ?? null, - ask_count: row.job_ask_count!, - worker_id: row.job_worker_id ?? null, - last_backend_id: row.job_last_backend_id ?? null, - parent_job_id: row.job_parent_job_id ?? null, - continued_from_job_id: row.job_continued_from_job_id ?? null, - subtask_depth: row.job_subtask_depth ?? 0, - required_profile: row.job_required_profile!, - task_class: row.job_task_class!, - owner_id: row.job_owner_id ?? null, - visibility: row.job_visibility ?? null, - visibility_scope_org_id: row.job_visibility_scope_org_id ?? null, - context_prompt_tokens: row.job_context_prompt_tokens ?? null, - context_limit_tokens: row.job_context_limit_tokens ?? null, - context_updated_at: row.job_context_updated_at ?? null, - browser_session_profile_id: row.job_browser_session_profile_id ?? null, - task_kind: row.job_task_kind ?? 'agent', - payload: row.job_payload ?? null, - // この join は latestJob 表示用。スペース解決は local_tasks.space_id を使うため - // ここでは job の space_id / runtime_dir を選択しておらず null で足りる。 - space_id: null, - runtime_dir: null, - created_at: row.job_created_at!, - updated_at: row.job_updated_at!, - }); - jobIds.push(row.job_id); - } else { - task.latestJob = null; - } - tasks.push(task); - } - - // 2. Single query for all sub-jobs - if (jobIds.length > 0) { - const placeholders = jobIds.map(() => '?').join(', '); - const subJobRows = this.db - .prepare(` - SELECT * FROM ( - SELECT j.*, ROW_NUMBER() OVER ( - PARTITION BY j.parent_job_id, j.issue_number - ORDER BY j.created_at DESC, j.rowid DESC - ) AS rn - FROM jobs j - WHERE j.parent_job_id IN (${placeholders}) - ) WHERE rn = 1 - ORDER BY parent_job_id, issue_number ASC - `) - .all(...jobIds) as JobRow[]; - - // Group sub-jobs by parent_job_id - const subJobsByParent = new Map(); - for (const row of subJobRows) { - const parentId = row.parent_job_id!; - if (!subJobsByParent.has(parentId)) { - subJobsByParent.set(parentId, []); - } - subJobsByParent.get(parentId)!.push(rowToJob(row)); - } - - // 3. Attach subtask info to tasks - for (const task of tasks) { - if (task.latestJob && subJobsByParent.has(task.latestJob.id)) { - const subJobs = subJobsByParent.get(task.latestJob.id)!; - task.subtasks = await this.buildSubtaskInfos(subJobs); - task.subtaskCount = subJobs.length; - task.subtaskCompleted = subJobs.filter(j => - ['succeeded', 'failed', 'cancelled'].includes(j.status) - ).length; - } - } - } - - return tasks; + return localTasksRepo.listLocalTasks(this.db, filter); } async updateLocalTask(taskId: number, updates: Partial>): Promise { - const setClauses: string[] = ["updated_at = datetime('now')"]; - const params: Record = { taskId }; - const fieldMap: Record = { - title: 'title', - titleSource: 'title_source', - body: 'body', - pieceName: 'piece_name', - profile: 'profile', - outputFormat: 'output_format', - askPolicy: 'ask_policy', - priority: 'priority', - state: 'state', - workspacePath: 'workspace_path', - runtimeDir: 'runtime_dir', - visibility: 'visibility', - visibilityScopeOrgId: 'visibility_scope_org_id', - }; - - for (const [jsKey, dbCol] of Object.entries(fieldMap)) { - const val = (updates as Record)[jsKey]; - if (val !== undefined) { - setClauses.push(`${dbCol} = @${jsKey}`); - params[jsKey] = val; - } - } - - if (setClauses.length === 1) return; - - this.db - .prepare(`UPDATE local_tasks SET ${setClauses.join(', ')} WHERE id = @taskId`) - .run(params); + return localTasksRepo.updateLocalTask(this.db, taskId, updates); } async updateFeedback(taskId: number, feedback: { @@ -3029,612 +322,193 @@ export class Repository { tags: string[]; comment: string | null; }): Promise { - this.db - .prepare(` - UPDATE local_tasks - SET feedback_rating = @rating, - feedback_tags = @tags, - feedback_comment = @comment, - feedback_at = datetime('now'), - updated_at = datetime('now') - WHERE id = @taskId - `) - .run({ - taskId, - rating: feedback.rating, - tags: JSON.stringify(feedback.tags), - comment: feedback.comment, - }); + return localTasksRepo.updateFeedback(this.db, taskId, feedback); } async addLocalTaskComment(taskId: number, author: string, body: string, kind: string = 'comment', attachments: string[] = []): Promise { - const attachmentsJson = attachments.length > 0 ? JSON.stringify(attachments) : null; - const result = this.db - .prepare('INSERT INTO local_task_comments (task_id, author, kind, body, attachments) VALUES (?, ?, ?, ?, ?)') - .run(taskId, author, kind, body, attachmentsJson); - this.db - .prepare("UPDATE local_tasks SET updated_at = datetime('now') WHERE id = ?") - .run(taskId); - const row = this.db - .prepare('SELECT * FROM local_task_comments WHERE id = ?') - .get(Number(result.lastInsertRowid)) as LocalTaskCommentRow | undefined; - if (!row) throw new Error('addLocalTaskComment: failed to load inserted comment'); - this.indexTaskComment(row.id, taskId, author, kind, row.created_at, body, attachmentsJson); - return rowToLocalTaskComment(row); + return localTasksRepo.addLocalTaskComment(this.db, taskId, author, body, kind, attachments); } - /** best-effort に1コメントを索引する。失敗してもコメント書き込みを壊さない。 */ - indexTaskComment( - commentId: number, taskId: number, author: string, kind: string, - createdAt: string, body: string, attachments: string | null, - ): void { - try { - const text = normalizeCommentToIndexText(kind, body, attachments); - if (text === null) return; - this.db.prepare( - `INSERT OR IGNORE INTO task_comment_index (comment_id, task_id, author, kind, created_at, text) - VALUES (?, ?, ?, ?, ?, ?)`, - ).run(commentId, taskId, author, kind, createdAt, text); - } catch (err) { - logger.warn(`[task-index] indexTaskComment failed comment=${commentId}: ${(err as Error).message}`); - } + indexTaskComment(commentId: number, taskId: number, author: string, kind: string, createdAt: string, body: string, attachments: string | null): void { + return taskSearchRepo.indexTaskComment(this.db, commentId, taskId, author, kind, createdAt, body, attachments); } - /** - * 正規化済みの各語を個別に引用して FTS5 MATCH 文字列を組む(Google 的な暗黙 AND)。 - * 各語をダブルクォートで囲むので、`AND`/`OR`/`NEAR`/`*`/`"` などが語に混じっても - * FTS5 演算子として解釈されず、インジェクションを防ぐ。語同士はこちらが制御する - * ` AND ` でつなぐため「全語を含む」検索になる。呼び出し側で引用符除去済みを前提とする。 - */ private buildFtsMatch(terms: string[]): string { - return terms.map((t) => `"${t}"`).join(' AND '); + return taskSearchRepo.buildFtsMatch(terms); } - async searchWorkspaceTasks( - currentTaskId: number, ownerId: string, query: string, - opts: { limit?: number; kind?: string; author?: string; taskId?: number } = {}, - ): Promise> { - const q = (query ?? '').trim(); - if (!q) return []; - const cur = this.db.prepare('SELECT space_id, owner_id FROM local_tasks WHERE id = ?') - .get(currentTaskId) as { space_id: string | null; owner_id: string | null } | undefined; - if (!cur) return []; - - // スペース帰属確認(owner または space_members メンバー)。非帰属なら空。 - if (cur.space_id) { - const member = this.db.prepare( - `SELECT 1 FROM spaces WHERE id = @s AND owner_id = @u - UNION SELECT 1 FROM space_members WHERE space_id = @s AND user_id = @u`, - ).get({ s: cur.space_id, u: ownerId }); - if (!member) return []; - } - - const limit = Math.min(Math.max(opts.limit ?? 10, 1), 30); - // Google 的な暗黙 AND: 空白区切りの各語を「すべて含む」ものにヒットさせる。 - // 各語から埋め込み二重引用符を除去(空白化)してトリムし、空になった語は捨てる。 - // これで純粋な記号だけの「語」が AND を汚染して常に0件になるのを防ぐ。 - const terms = q.split(/\s+/).map((t) => t.replace(/"/g, ' ').trim()).filter(Boolean); - if (terms.length === 0) return []; // 引用符・記号のみで実質的な検索語が無い - // trigram は3文字未満の語を MATCH できないため、短い語が1つでもあれば LIKE 経路 - // (語ごとの AND-of-LIKE。LIKE は語長に依存せず動く)にフォールバックする。 - const useLike = terms.some((t) => t.length < 3); - const scope = cur.space_id - ? 'lt.space_id = @spaceId' - : 'lt.space_id IS NULL AND lt.owner_id = @ownerId'; - const filters = [ - opts.kind ? 'i.kind = @kind' : '', - opts.author ? 'i.author = @author' : '', - opts.taskId ? 'i.task_id = @taskFilter' : '', - ].filter(Boolean).join(' AND '); - const filterSql = filters ? ` AND ${filters}` : ''; - - const params: Record = { - cur: currentTaskId, spaceId: cur.space_id, ownerId, - kind: opts.kind, author: opts.author, taskFilter: opts.taskId, limit, - }; - - let sql: string; - if (useLike) { - // 各語を LIKE で AND 結合(位置・順序を問わず全語を含む)。値はパラメータ束縛、 - // 列名・プレースホルダ名は固定文字列なのでインジェクションしない。 - const likeConds = terms.map((_, i) => `i.text LIKE @like${i}`).join(' AND '); - terms.forEach((t, i) => { params[`like${i}`] = `%${t}%`; }); - sql = ` - SELECT i.task_id AS taskId, lt.title AS taskTitle, i.comment_id AS commentId, - i.author, i.kind, i.created_at AS createdAt, i.text - FROM task_comment_index i - JOIN local_tasks lt ON lt.id = i.task_id - WHERE (${likeConds}) AND i.task_id != @cur AND (${scope})${filterSql} - ORDER BY i.created_at DESC LIMIT @limit`; - } else { - params.match = this.buildFtsMatch(terms); - sql = ` - SELECT i.task_id AS taskId, lt.title AS taskTitle, i.comment_id AS commentId, - i.author, i.kind, i.created_at AS createdAt, i.text - FROM task_comment_fts f - JOIN task_comment_index i ON i.comment_id = f.rowid - JOIN local_tasks lt ON lt.id = i.task_id - WHERE f.text MATCH @match AND i.task_id != @cur AND (${scope})${filterSql} - ORDER BY bm25(task_comment_fts) LIMIT @limit`; - } - return this.db.prepare(sql).all(params) as any; + async searchWorkspaceTasks(currentTaskId: number, ownerId: string, query: string, opts: { limit?: number; kind?: string; author?: string; taskId?: number } = {}): Promise> { + return taskSearchRepo.searchWorkspaceTasks(this.db, currentTaskId, ownerId, query, opts); } - async readWorkspaceTaskAround( - currentTaskId: number, ownerId: string, commentId: number, before: number, after: number, - ) { - const cur = this.db.prepare('SELECT space_id, owner_id FROM local_tasks WHERE id = ?') - .get(currentTaskId) as { space_id: string | null; owner_id: string | null } | undefined; - if (!cur) return null; - const target = this.db.prepare('SELECT task_id FROM local_task_comments WHERE id = ?') - .get(commentId) as { task_id: number } | undefined; - if (!target) return null; - const t = this.db.prepare('SELECT id, title, space_id, owner_id FROM local_tasks WHERE id = ?') - .get(target.task_id) as { id: number; title: string; space_id: string | null; owner_id: string | null } | undefined; - if (!t) return null; - // スコープ確認: 同一スペース(or 同 owner の個人スペース) - const inScope = cur.space_id ? t.space_id === cur.space_id : (t.space_id === null && t.owner_id === ownerId); - if (!inScope) return null; - - const all = await this.listLocalTaskComments(target.task_id); - const pos = all.findIndex((x) => x.id === commentId); - if (pos < 0) return null; - const slice = all.slice(Math.max(0, pos - before), pos + after + 1); - return { - taskId: t.id, taskTitle: t.title, - comments: slice.map((x) => ({ commentId: x.id, author: x.author, kind: x.kind, createdAt: x.createdAt, body: x.body, isCenter: x.id === commentId })), - }; + async readWorkspaceTaskAround(currentTaskId: number, ownerId: string, commentId: number, before: number, after: number) { + return taskSearchRepo.readWorkspaceTaskAround(this.db, currentTaskId, ownerId, commentId, before, after); } async listLocalTaskComments(taskId: number): Promise { - const rows = this.db - .prepare('SELECT * FROM local_task_comments WHERE task_id = ? ORDER BY created_at ASC, id ASC') - .all(taskId) as LocalTaskCommentRow[]; - return rows.map(rowToLocalTaskComment); + return localTasksRepo.listLocalTaskComments(this.db, taskId); } async getUninjectedComments(taskId: number, sinceId: number = 0): Promise { - const rows = this.db - .prepare( - `SELECT * FROM local_task_comments - WHERE task_id = ? AND id > ? AND author = 'user' AND injected_at IS NULL - ORDER BY id ASC` - ) - .all(taskId, sinceId) as LocalTaskCommentRow[]; - return rows.map(rowToLocalTaskComment); + return localTasksRepo.getUninjectedComments(this.db, taskId, sinceId); } markCommentsInjected(commentIds: number[]): void { - if (commentIds.length === 0) return; - const placeholders = commentIds.map(() => '?').join(','); - this.db - .prepare(`UPDATE local_task_comments SET injected_at = datetime('now') WHERE id IN (${placeholders})`) - .run(...commentIds); + return localTasksRepo.markCommentsInjected(this.db, commentIds); } - /** - * Latest agent-authored "result" or "ask" comment for a task. Used by the - * piece handoff feature to surface the previous job's terminal output as - * context to a continuation job's LLM. Returns null when none exist - * (e.g., task has not yet completed any job). - */ async getLatestResultComment(taskId: number): Promise<{ body: string; kind: string; createdAt: string } | null> { - const row = this.db - .prepare( - `SELECT body, kind, created_at - FROM local_task_comments - WHERE task_id = ? AND author = 'agent' AND kind IN ('result', 'ask') - ORDER BY created_at DESC - LIMIT 1` - ) - .get(taskId) as { body: string; kind: string; created_at: string } | undefined; - return row ? { body: row.body, kind: row.kind, createdAt: row.created_at } : null; + return localTasksRepo.getLatestResultComment(this.db, taskId); } - private getJobSync(id: string): Job | null { - const row = this.db - .prepare('SELECT * FROM jobs WHERE id = ?') - .get(id) as JobRow | undefined; - return row ? rowToJob(row) : null; - } - - /** - * ジョブの現在のステータスを同期的に取得する。 - * キャンセルチェックなど、非同期が使えない箇所で利用する。 - */ getJobStatusSync(id: string): JobStatus | null { - const row = this.db - .prepare('SELECT status FROM jobs WHERE id = ?') - .get(id) as { status: string } | undefined; - return row ? (row.status as JobStatus) : null; + return jobsRepo.getJobStatusSync(this.db, id); } - /** - * Returns true if `workerId` currently has at least one job with status='running'. - * Used by the Side Info Panel's worker status endpoint. Boolean-only on purpose: - * never expose the job id/title/owner to other users in the shared panel. - */ isWorkerBusy(workerId: string): boolean { - const row = this.db - .prepare(`SELECT 1 AS hit FROM jobs WHERE worker_id = ? AND status = 'running' LIMIT 1`) - .get(workerId) as { hit: number } | undefined; - return !!row; + return jobsRepo.isWorkerBusy(this.db, workerId); } - /** - * いま実行中 (status='running') のジョブを worker_id ごとにまとめ、占有ユーザーの - * 表示名と種別 (`task_kind`: 'agent' 通常 / 'reflection' 学習 等) を返す。 - * **admin 専用の監視用**(ワーカー / GPU パネルで「誰が・何で GPU を占有しているか」を - * 出す)。一般ユーザーには呼び出し側で渡さない(dashboard-workers の privacy 既定を維持)。 - * - * `user` は `users.name`。null(owner 不明 / no-auth 'local')は owner_id →'unknown' - * の順でフォールバック。`(user, kind)` の組で重複排除し、1 ワーカーに複数ユーザー / - * 複数種別が同居するケースに対応する。 - */ listRunningJobOwnersByWorker(): Map> { - const rows = this.db - .prepare( - `SELECT j.worker_id AS workerId, j.owner_id AS ownerId, j.task_kind AS taskKind, u.name AS ownerName - FROM jobs j - LEFT JOIN users u ON u.id = j.owner_id - WHERE j.status = 'running' AND j.worker_id IS NOT NULL`, - ) - .all() as Array<{ workerId: string; ownerId: string | null; taskKind: string | null; ownerName: string | null }>; - const byWorker = new Map>(); - for (const r of rows) { - const user = (r.ownerName && r.ownerName.trim()) || r.ownerId || 'unknown'; - const kind = r.taskKind || 'agent'; - const list = byWorker.get(r.workerId); - if (list) { - if (!list.some((o) => o.user === user && o.kind === kind)) list.push({ user, kind }); - } else { - byWorker.set(r.workerId, [{ user, kind }]); - } - } - return byWorker; + return jobsRepo.listRunningJobOwnersByWorker(this.db); } async updateJob(id: string, updates: Partial>): Promise { - const setClauses: string[] = ["updated_at = datetime('now')"]; - const params: Record = { id }; - - const fieldMap: Record = { - status: 'status', - pieceName: 'piece_name', - currentMovement: 'current_movement', - currentActivity: 'current_activity', - instruction: 'instruction', - branchName: 'branch_name', - worktreePath: 'worktree_path', - prNumber: 'pr_number', - attempt: 'attempt', - maxAttempts: 'max_attempts', - nextRetryAt: 'next_retry_at', - errorSummary: 'error_summary', - abortReason: 'abort_reason', - resumeMovement: 'resume_movement', - waitReason: 'wait_reason', - askCount: 'ask_count', - workerId: 'worker_id', - lastBackendId: 'last_backend_id', - parentJobId: 'parent_job_id', - subtaskDepth: 'subtask_depth', - requiredRole: 'required_profile', - requiredProfile: 'required_profile', - }; - - for (const [jsKey, dbCol] of Object.entries(fieldMap)) { - const val = (updates as Record)[jsKey]; - if (val !== undefined) { - setClauses.push(`${dbCol} = @${jsKey}`); - params[jsKey] = val; - } - } - - if (setClauses.length === 1) return; // updated_at のみ = 実質変更なし - - this.db - .prepare(`UPDATE jobs SET ${setClauses.join(', ')} WHERE id = @id`) - .run(params); + return jobsRepo.updateJob(this.db, id, updates); } - /** ジョブの updated_at のみを更新(ハートビート用)。updateJob は変更フィールドなしだと早期リターンするため別メソッド */ touchJobUpdatedAt(id: string): void { - this.db.prepare("UPDATE jobs SET updated_at = datetime('now') WHERE id = ?").run(id); + return jobsRepo.touchJobUpdatedAt(this.db, id); } - /** - * Re-queue jobs parked with wait_reason='mcp_auth_required' for the given owner. - * Worker re-evaluates required_mcp on next pickup and will pause again if other servers - * are still unauthorized. _serverId is accepted for API symmetry but not used at SQL time - * (filtering by piece's required_mcp happens at the worker side). - * - * Returns the number of jobs actually re-queued. - */ resumeMcpWaitingJobs(ownerId: string, _serverId: string): number { - const result = this.db - .prepare( - `UPDATE jobs - SET status='queued', wait_reason=NULL, updated_at=datetime('now') - WHERE status='waiting_human' AND wait_reason='mcp_auth_required' - AND owner_id = ?`, - ) - .run(ownerId); - return result.changes; + return jobsRepo.resumeMcpWaitingJobs(this.db, ownerId, _serverId); } - /** - * Re-queue a job parked with wait_reason='tool_request' after a user - * decided (approve/deny) the pending tool request. The worker resumes from - * `resume_movement` and re-reads the task's granted_tools. Returns the number - * of jobs re-queued (0 if the job is gone or no longer parked for approval). - */ resumeToolRequestJob(jobId: string): number { - const result = this.db - .prepare( - `UPDATE jobs - SET status='queued', wait_reason=NULL, updated_at=datetime('now') - WHERE id = ? AND status='waiting_human' AND wait_reason='tool_request'`, - ) - .run(jobId); - return result.changes; + return jobsRepo.resumeToolRequestJob(this.db, jobId); + } + + resumePackageRequestJob(jobId: string): number { + return jobsRepo.resumePackageRequestJob(this.db, jobId); } async lockIssue(repo: string, issueNumber: number, jobId: string): Promise { - try { - this.db - .prepare('INSERT INTO issue_locks (repo, issue_number, job_id) VALUES (?, ?, ?)') - .run(repo, issueNumber, jobId); - return true; - } catch { - return false; - } + return jobsRepo.lockIssue(this.db, repo, issueNumber, jobId); } async unlockIssue(repo: string, issueNumber: number): Promise { - this.db - .prepare('DELETE FROM issue_locks WHERE repo = ? AND issue_number = ?') - .run(repo, issueNumber); + return jobsRepo.unlockIssue(this.db, repo, issueNumber); } async deleteJobsForIssue(repo: string, issueNumber: number): Promise { - const result = this.db - .prepare('DELETE FROM jobs WHERE repo = ? AND issue_number = ?') - .run(repo, issueNumber); - this.db - .prepare('DELETE FROM issue_locks WHERE repo = ? AND issue_number = ?') - .run(repo, issueNumber); - return result.changes; + return jobsRepo.deleteJobsForIssue(this.db, repo, issueNumber); } async addAuditLog(jobId: string | null, action: string, actor: string, detail: object): Promise { - this.db - .prepare('INSERT INTO audit_log (job_id, action, actor, detail) VALUES (?, ?, ?, ?)') - .run(jobId, action, actor, JSON.stringify(detail)); + return auditRepo.addAuditLog(this.db, jobId, action, actor, detail); } - oidcUpsert( - model: string, - id: string, - payload: object, - opts: { expiresAt?: number | null; grantId?: string | null; userCode?: string | null; uid?: string | null } = {}, - ): void { - this.db.prepare(` - INSERT INTO oidc_models (model, id, payload, grant_id, user_code, uid, expires_at, consumed_at) - VALUES (@model, @id, @payload, @grantId, @userCode, @uid, @expiresAt, NULL) - ON CONFLICT(model, id) DO UPDATE SET - payload = excluded.payload, - grant_id = excluded.grant_id, - user_code = excluded.user_code, - uid = excluded.uid, - expires_at = excluded.expires_at - `).run({ - model, id, - payload: JSON.stringify(payload), - grantId: opts.grantId ?? null, - userCode: opts.userCode ?? null, - uid: opts.uid ?? null, - expiresAt: opts.expiresAt ?? null, - }); + oidcUpsert(model: string, id: string, payload: object, opts: { expiresAt?: number | null; grantId?: string | null; userCode?: string | null; uid?: string | null } = {}): void { + return a2aRepo.oidcUpsert(this.db, model, id, payload, opts); } oidcFind(model: string, id: string): { payload: Record; consumedAt: number | null } | undefined { - const row = this.db.prepare( - 'SELECT payload, consumed_at FROM oidc_models WHERE model = ? AND id = ?', - ).get(model, id) as { payload: string; consumed_at: number | null } | undefined; - if (!row) return undefined; - return { payload: JSON.parse(row.payload), consumedAt: row.consumed_at }; + return a2aRepo.oidcFind(this.db, model, id); } oidcFindByUid(uid: string): { payload: Record } | undefined { - const row = this.db.prepare( - "SELECT payload FROM oidc_models WHERE model = 'Session' AND uid = ?", - ).get(uid) as { payload: string } | undefined; - return row ? { payload: JSON.parse(row.payload) } : undefined; + return a2aRepo.oidcFindByUid(this.db, uid); } oidcFindByUserCode(userCode: string): { payload: Record } | undefined { - const row = this.db.prepare( - 'SELECT payload FROM oidc_models WHERE user_code = ?', - ).get(userCode) as { payload: string } | undefined; - return row ? { payload: JSON.parse(row.payload) } : undefined; + return a2aRepo.oidcFindByUserCode(this.db, userCode); } oidcConsume(model: string, id: string, consumedAt: number): void { - this.db.prepare('UPDATE oidc_models SET consumed_at = ? WHERE model = ? AND id = ?') - .run(consumedAt, model, id); + return a2aRepo.oidcConsume(this.db, model, id, consumedAt); } oidcDestroy(model: string, id: string): void { - this.db.prepare('DELETE FROM oidc_models WHERE model = ? AND id = ?').run(model, id); + return a2aRepo.oidcDestroy(this.db, model, id); } oidcRevokeByGrantId(grantId: string): void { - this.db.prepare('DELETE FROM oidc_models WHERE grant_id = ?').run(grantId); + return a2aRepo.oidcRevokeByGrantId(this.db, grantId); } createA2aClient(row: A2aClientRow): void { - this.db.prepare(` - INSERT INTO a2a_clients - (client_id, name, redirect_uris, secret_hash, token_endpoint_auth_method, - agent_card_url, issuer, key_fingerprint, status, created_by) - VALUES (@clientId, @name, @redirectUris, @secretHash, @tokenEndpointAuthMethod, - @agentCardUrl, @issuer, @keyFingerprint, @status, @createdBy) - `).run({ - ...row, - redirectUris: JSON.stringify(row.redirectUris), - }); + return a2aRepo.createA2aClient(this.db, row); } getA2aClient(clientId: string): A2aClientRow | undefined { - const r = this.db.prepare('SELECT * FROM a2a_clients WHERE client_id = ?').get(clientId) as any; - if (!r) return undefined; - return { - clientId: r.client_id, name: r.name, redirectUris: JSON.parse(r.redirect_uris), - secretHash: r.secret_hash, tokenEndpointAuthMethod: r.token_endpoint_auth_method, - agentCardUrl: r.agent_card_url, issuer: r.issuer, keyFingerprint: r.key_fingerprint, - status: r.status, createdBy: r.created_by, createdAt: r.created_at, - }; + return a2aRepo.getA2aClient(this.db, clientId); } listA2aClients(): A2aClientRow[] { - const rows = this.db.prepare('SELECT * FROM a2a_clients ORDER BY created_at DESC').all() as any[]; - return rows.map(r => ({ - clientId: r.client_id, name: r.name, redirectUris: JSON.parse(r.redirect_uris), - secretHash: r.secret_hash, tokenEndpointAuthMethod: r.token_endpoint_auth_method, - agentCardUrl: r.agent_card_url, issuer: r.issuer, keyFingerprint: r.key_fingerprint, - status: r.status, createdBy: r.created_by, createdAt: r.created_at, - })); + return a2aRepo.listA2aClients(this.db); } setA2aClientStatus(clientId: string, status: 'active' | 'disabled'): void { - this.db.prepare('UPDATE a2a_clients SET status = ? WHERE client_id = ?').run(status, clientId); + return a2aRepo.setA2aClientStatus(this.db, clientId, status); } createA2aDelegation(row: A2aDelegationRow): void { - this.db.prepare(` - INSERT INTO a2a_delegations - (id, user_id, client_id, grant_id, granted_space_ids, granted_skills, audience, expires_at, revoked_at) - VALUES (@id, @userId, @clientId, @grantId, @grantedSpaceIds, @grantedSkills, @audience, @expiresAt, @revokedAt) - `).run({ - id: row.id, userId: row.userId, clientId: row.clientId, grantId: row.grantId, - grantedSpaceIds: JSON.stringify(row.grantedSpaceIds), - grantedSkills: JSON.stringify(row.grantedSkills), - audience: row.audience, expiresAt: row.expiresAt, revokedAt: row.revokedAt, - }); + return a2aRepo.createA2aDelegation(this.db, row); } private mapDelegation(r: any): A2aDelegationRow { - return { - id: r.id, userId: r.user_id, clientId: r.client_id, grantId: r.grant_id, - grantedSpaceIds: JSON.parse(r.granted_space_ids || '[]'), - grantedSkills: JSON.parse(r.granted_skills || '[]'), - audience: r.audience, expiresAt: r.expires_at, revokedAt: r.revoked_at, createdAt: r.created_at, - }; + return a2aRepo.mapDelegation(r); } getA2aDelegationByGrantId(grantId: string): A2aDelegationRow | undefined { - const r = this.db.prepare('SELECT * FROM a2a_delegations WHERE grant_id = ?').get(grantId); - return r ? this.mapDelegation(r) : undefined; + return a2aRepo.getA2aDelegationByGrantId(this.db, grantId); } listA2aDelegationsForUser(userId: string): A2aDelegationRow[] { - return (this.db.prepare('SELECT * FROM a2a_delegations WHERE user_id = ? ORDER BY created_at DESC').all(userId) as any[]) - .map(r => this.mapDelegation(r)); + return a2aRepo.listA2aDelegationsForUser(this.db, userId); } revokeA2aDelegation(id: string, revokedAtIso: string): void { - // revoked_at IS NULL ガード: 二重失効で元の失効時刻を上書きしない(監査の forensic 記録を守る) - this.db - .prepare('UPDATE a2a_delegations SET revoked_at = ? WHERE id = ? AND revoked_at IS NULL') - .run(revokedAtIso, id); + return a2aRepo.revokeA2aDelegation(this.db, id, revokedAtIso); } getA2aDelegationById(id: string): A2aDelegationRow | undefined { - const r = this.db.prepare('SELECT * FROM a2a_delegations WHERE id = ?').get(id); - return r ? this.mapDelegation(r) : undefined; + return a2aRepo.getA2aDelegationById(this.db, id); } listLiveA2aDelegationsForClient(clientId: string, nowIso: string): A2aDelegationRow[] { - const rows = this.db.prepare( - `SELECT * FROM a2a_delegations - WHERE client_id = ? AND revoked_at IS NULL - AND (expires_at IS NULL OR expires_at > ?) - ORDER BY created_at DESC`, - ).all(clientId, nowIso); - return (rows as any[]).map((r) => this.mapDelegation(r)); + return a2aRepo.listLiveA2aDelegationsForClient(this.db, clientId, nowIso); } - listNonTerminalA2aTasksByGrant( - grantId: string, - limit = 500, - ): Array<{ id: string; jobId: string | null; payload: Record }> { - const rows = this.db.prepare( - `SELECT id, job_id, payload FROM a2a_tasks - WHERE grant_id = ? - AND (json_extract(payload, '$.status.state') NOT IN - ('completed','failed','canceled','rejected') - OR json_extract(payload, '$.status.state') IS NULL) - ORDER BY updated_at - LIMIT ?`, - ).all(grantId, limit); - return (rows as any[]).map((r) => ({ - id: r.id, - jobId: r.job_id ?? null, - payload: JSON.parse(r.payload ?? '{}'), - })); + listNonTerminalA2aTasksByGrant(grantId: string, limit = 500): Array<{ id: string; jobId: string | null; payload: Record }> { + return a2aRepo.listNonTerminalA2aTasksByGrant(this.db, grantId, limit); + } + + countNonTerminalA2aTasksByGrant(grantId: string): number { + return a2aRepo.countNonTerminalA2aTasksByGrant(this.db, grantId); } cancelA2aLinkedJob(jobId: string): boolean { - const res = this.db.prepare( - `UPDATE jobs SET status = 'cancelled', updated_at = datetime('now') - WHERE id = ? AND status IN - ('queued','dispatching','running','waiting_human','waiting_subtasks','retry')`, - ).run(jobId); - return res.changes > 0; + return a2aRepo.cancelA2aLinkedJob(this.db, jobId); } - listA2aDelegationsForUserWithClient( - userId: string, - ): Array { - const rows = this.db.prepare( - `SELECT d.*, c.name AS client_name, c.status AS client_status - FROM a2a_delegations d - LEFT JOIN a2a_clients c ON c.client_id = d.client_id - WHERE d.user_id = ? - ORDER BY d.created_at DESC`, - ).all(userId); - return (rows as any[]).map((r) => ({ - ...this.mapDelegation(r), - clientName: r.client_name ?? null, - clientStatus: r.client_status ?? null, - })); + listA2aDelegationsForUserWithClient(userId: string): Array { + return a2aRepo.listA2aDelegationsForUserWithClient(this.db, userId); } listA2aDelegationsAllWithClient(): Array< A2aDelegationRow & { userId: string; clientName: string | null; clientStatus: string | null } > { - const rows = this.db.prepare( - `SELECT d.*, c.name AS client_name, c.status AS client_status - FROM a2a_delegations d - LEFT JOIN a2a_clients c ON c.client_id = d.client_id - ORDER BY d.created_at DESC`, - ).all(); - return (rows as any[]).map((r) => ({ - ...this.mapDelegation(r), - clientName: r.client_name ?? null, - clientStatus: r.client_status ?? null, - })); + return a2aRepo.listA2aDelegationsAllWithClient(this.db); } getSpaceA2aSkills(spaceId: string): string[] { - const r = this.db.prepare('SELECT a2a_skills FROM spaces WHERE id = ?').get(spaceId) as { a2a_skills: string | null } | undefined; - if (!r || !r.a2a_skills) return []; - try { const v = JSON.parse(r.a2a_skills); return Array.isArray(v) ? v : []; } catch { return []; } + return spacesRepo.getSpaceA2aSkills(this.db, spaceId); } setSpaceA2aSkills(spaceId: string, skills: string[]): void { - this.db.prepare('UPDATE spaces SET a2a_skills = ? WHERE id = ?').run(JSON.stringify(skills), spaceId); + return spacesRepo.setSpaceA2aSkills(this.db, spaceId, skills); } saveA2aTask(row: { @@ -3647,1072 +521,219 @@ export class Repository { grantId?: string; actingUserId?: string; }): void { - this.db.prepare(` - INSERT INTO a2a_tasks (id, context_id, job_id, local_task_id, payload, updated_at, delegation_id, grant_id, acting_user_id) - VALUES (@id, @contextId, @jobId, @localTaskId, @payload, datetime('now'), @delegationId, @grantId, @actingUserId) - ON CONFLICT(id) DO UPDATE SET - context_id = excluded.context_id, job_id = excluded.job_id, - local_task_id = excluded.local_task_id, payload = excluded.payload, updated_at = datetime('now'), - delegation_id = excluded.delegation_id, grant_id = excluded.grant_id, acting_user_id = excluded.acting_user_id - `).run({ - id: row.id, - contextId: row.contextId, - jobId: row.jobId, - localTaskId: row.localTaskId, - payload: JSON.stringify(row.payload), - delegationId: row.delegationId ?? null, - grantId: row.grantId ?? null, - actingUserId: row.actingUserId ?? null, - }); + return a2aRepo.saveA2aTask(this.db, row); } loadA2aTask(id: string): { payload: Record } | undefined { - const r = this.db.prepare('SELECT payload FROM a2a_tasks WHERE id = ?').get(id) as { payload: string } | undefined; - return r ? { payload: JSON.parse(r.payload) } : undefined; + return a2aRepo.loadA2aTask(this.db, id); } getA2aTaskByJobId(jobId: string): { id: string; payload: Record } | undefined { - const r = this.db.prepare('SELECT id, payload FROM a2a_tasks WHERE job_id = ?').get(jobId) as { id: string; payload: string } | undefined; - return r ? { id: r.id, payload: JSON.parse(r.payload) } : undefined; + return a2aRepo.getA2aTaskByJobId(this.db, jobId); } listNonTerminalA2aTasks(limit = 500): Array<{ id: string; jobId: string | null; payload: Record }> { - const rows = this.db.prepare(` - SELECT id, job_id, payload FROM a2a_tasks - WHERE json_extract(payload, '$.status.state') NOT IN ('completed', 'failed', 'canceled', 'rejected') - OR json_extract(payload, '$.status.state') IS NULL - ORDER BY updated_at - LIMIT ? - `).all(limit) as Array<{ id: string; job_id: string | null; payload: string }>; - return rows.map(r => ({ id: r.id, jobId: r.job_id, payload: JSON.parse(r.payload) })); + return a2aRepo.listNonTerminalA2aTasks(this.db, limit); } async upsertWorkerNode(params: UpsertWorkerNodeParams): Promise { - const now = new Date().toISOString(); - this.db.prepare(` - INSERT INTO worker_nodes ( - worker_id, endpoint, enabled, healthy, profile_tags, task_class_tags, available_models, - inflight_jobs, max_concurrency, last_error, last_seen_at, updated_at - ) VALUES ( - @workerId, @endpoint, @enabled, @healthy, @roleTags, @roleTags, @availableModels, - @inflightJobs, @maxConcurrency, @lastError, @now, @now - ) - ON CONFLICT(worker_id) DO UPDATE SET - endpoint = excluded.endpoint, - enabled = excluded.enabled, - healthy = excluded.healthy, - profile_tags = excluded.profile_tags, - task_class_tags = excluded.task_class_tags, - available_models = excluded.available_models, - inflight_jobs = excluded.inflight_jobs, - max_concurrency = excluded.max_concurrency, - last_error = excluded.last_error, - last_seen_at = excluded.last_seen_at, - updated_at = excluded.updated_at - `).run({ - workerId: params.workerId, - endpoint: params.endpoint, - enabled: params.enabled ? 1 : 0, - healthy: params.healthy ? 1 : 0, - roleTags: encodeTags(params.roles), - availableModels: JSON.stringify(params.availableModels ?? []), - inflightJobs: params.inflightJobs ?? 0, - maxConcurrency: params.maxConcurrency ?? 1, - lastError: params.lastError ?? null, - now, - }); + return workerNodesRepo.upsertWorkerNode(this.db, params); } - async updateWorkerNodeHealth( - workerId: string, - updates: { healthy: boolean; lastError?: string | null; inflightJobs?: number; availableModels?: string[]; enabled?: boolean }, - ): Promise { - const setClauses = [ - 'healthy = @healthy', - 'last_error = @lastError', - "last_seen_at = @now", - "updated_at = @now", - ]; - const params: Record = { - workerId, - healthy: updates.healthy ? 1 : 0, - lastError: updates.lastError ?? null, - now: new Date().toISOString(), - }; - - if (updates.inflightJobs !== undefined) { - setClauses.push('inflight_jobs = @inflightJobs'); - params['inflightJobs'] = updates.inflightJobs; - } - if (updates.availableModels !== undefined) { - setClauses.push('available_models = @availableModels'); - params['availableModels'] = JSON.stringify(updates.availableModels); - } - if (updates.enabled !== undefined) { - setClauses.push('enabled = @enabled'); - params['enabled'] = updates.enabled ? 1 : 0; - } - - this.db.prepare(`UPDATE worker_nodes SET ${setClauses.join(', ')} WHERE worker_id = @workerId`).run(params); + async updateWorkerNodeHealth(workerId: string, updates: { healthy: boolean; lastError?: string | null; inflightJobs?: number; availableModels?: string[]; enabled?: boolean }): Promise { + return workerNodesRepo.updateWorkerNodeHealth(this.db, workerId, updates); } async getWorkerNode(workerId: string): Promise { - const row = this.db - .prepare('SELECT * FROM worker_nodes WHERE worker_id = ?') - .get(workerId) as WorkerNodeRow | undefined; - return row ? rowToWorkerNode(row) : null; + return workerNodesRepo.getWorkerNode(this.db, workerId); } async claimNextJob(workerId: string): Promise { - const row = this.db.prepare(` - UPDATE jobs - SET status = 'running', worker_id = ?, updated_at = datetime('now') - WHERE id = ( - SELECT j.id - FROM jobs j - JOIN worker_nodes w ON w.worker_id = ? - WHERE j.status = 'queued' - AND w.enabled = 1 - AND w.healthy = 1 - AND instr(w.profile_tags, ',' || j.required_profile || ',') > 0 - AND NOT EXISTS ( - SELECT 1 FROM issue_locks il - WHERE il.repo = j.repo AND il.issue_number = j.issue_number - ) - ORDER BY j.created_at ASC - LIMIT 1 - ) - RETURNING * - `).get(workerId, workerId) as JobRow | undefined; - return row ? rowToJob(row) : null; + return jobsRepo.claimNextJob(this.db, workerId); } - /** - * リトライ待ちジョブの中から next_retry_at を過ぎたものを1件取得して running に遷移 - */ async claimNextRetryJob(workerId: string): Promise { - const row = this.db.prepare(` - UPDATE jobs - SET status = 'running', worker_id = ?, updated_at = datetime('now') - WHERE id = ( - SELECT j.id - FROM jobs j - JOIN worker_nodes w ON w.worker_id = ? - WHERE j.status = 'retry' - AND replace(j.next_retry_at, 'T', ' ') <= datetime('now') - AND w.enabled = 1 - AND w.healthy = 1 - AND instr(w.profile_tags, ',' || j.required_profile || ',') > 0 - AND NOT EXISTS ( - SELECT 1 FROM issue_locks il - WHERE il.repo = j.repo AND il.issue_number = j.issue_number - ) - ORDER BY j.next_retry_at ASC - LIMIT 1 - ) - RETURNING * - `).get(workerId, workerId) as JobRow | undefined; - return row ? rowToJob(row) : null; + return jobsRepo.claimNextRetryJob(this.db, workerId); } - /** - * Read-only peek at the next job this worker WOULD claim (retry-priority, - * then oldest queued), without claiming it. Used by the idle-preferring - * claim gate to learn the next job's role before deciding whether to defer - * to an idler sibling. Mirrors the claimNext*Job WHERE clauses exactly. - */ async peekNextClaimable(workerId: string): Promise { - const retry = this.db.prepare(` - SELECT j.* - FROM jobs j - JOIN worker_nodes w ON w.worker_id = ? - WHERE j.status = 'retry' - AND replace(j.next_retry_at, 'T', ' ') <= datetime('now') - AND w.enabled = 1 - AND w.healthy = 1 - AND instr(w.profile_tags, ',' || j.required_profile || ',') > 0 - AND NOT EXISTS ( - SELECT 1 FROM issue_locks il - WHERE il.repo = j.repo AND il.issue_number = j.issue_number - ) - ORDER BY j.next_retry_at ASC - LIMIT 1 - `).get(workerId) as JobRow | undefined; - if (retry) return rowToJob(retry); - - const queued = this.db.prepare(` - SELECT j.* - FROM jobs j - JOIN worker_nodes w ON w.worker_id = ? - WHERE j.status = 'queued' - AND w.enabled = 1 - AND w.healthy = 1 - AND instr(w.profile_tags, ',' || j.required_profile || ',') > 0 - AND NOT EXISTS ( - SELECT 1 FROM issue_locks il - WHERE il.repo = j.repo AND il.issue_number = j.issue_number - ) - ORDER BY j.created_at ASC - LIMIT 1 - `).get(workerId) as JobRow | undefined; - return queued ? rowToJob(queued) : null; + return jobsRepo.peekNextClaimable(this.db, workerId); } async getJobsByStatus(status: JobStatus): Promise { - const rows = this.db - .prepare('SELECT * FROM jobs WHERE status = ? ORDER BY created_at ASC') - .all(status) as JobRow[]; - return rows.map(rowToJob); + return jobsRepo.getJobsByStatus(this.db, status); } async getLatestJobForIssue(repo: string, issueNumber: number): Promise { - const row = this.db - .prepare('SELECT * FROM jobs WHERE repo = ? AND issue_number = ? ORDER BY created_at DESC LIMIT 1') - .get(repo, issueNumber) as JobRow | undefined; - return row ? rowToJob(row) : null; + return jobsRepo.getLatestJobForIssue(this.db, repo, issueNumber); } - async updateJobContext( - jobId: string, - payload: { promptTokens: number; limitTokens: number } - ): Promise { - const updatedAt = new Date().toISOString(); - this.db - .prepare( - `UPDATE jobs - SET context_prompt_tokens = ?, - context_limit_tokens = ?, - context_updated_at = ? - WHERE id = ?` - ) - .run(payload.promptTokens, payload.limitTokens, updatedAt, jobId); + async updateJobContext(jobId: string, payload: { promptTokens: number; limitTokens: number }): Promise { + return jobsRepo.updateJobContext(this.db, jobId, payload); } - /** 起動時に孤立ジョブを回復 */ async recoverOrphanedJobs(): Promise { - const result = this.db - .prepare("UPDATE jobs SET status = 'queued', worker_id = NULL, updated_at = datetime('now') WHERE status IN ('running', 'dispatching')") - .run(); - if (result.changes > 0) { - this.db.prepare('DELETE FROM issue_locks').run(); - logger.warn(`Repository: recovered ${result.changes} orphaned jobs, cleared issue locks`); - } - // waiting_subtasks のジョブで全サブジョブが完了済みのものを再キュー - // 同一 issue_number に複数ジョブがある場合、最新のみで判定する - const subtaskRecovery = this.db.prepare(` - UPDATE jobs - SET status = 'queued', updated_at = datetime('now') - WHERE status = 'waiting_subtasks' - AND ( - SELECT COUNT(*) FROM ( - SELECT s.status, ROW_NUMBER() OVER ( - PARTITION BY s.issue_number - ORDER BY s.created_at DESC, s.rowid DESC - ) AS rn - FROM jobs s - WHERE s.parent_job_id = jobs.id - ) WHERE rn = 1 - AND status NOT IN ('succeeded','failed','cancelled') - ) = 0 - `).run(); - if (subtaskRecovery.changes > 0) { - logger.warn(`Repository: recovered ${subtaskRecovery.changes} waiting_subtasks jobs`); - } - return result.changes; + return jobsRepo.recoverOrphanedJobs(this.db); } - /** - * running/dispatching 状態のまま staleMinutes 以上 updated_at が更新されていないジョブを - * queued に戻す(ランタイム watchdog) - */ recoverStuckRunningJobs(staleMinutes: number): number { - const rows = this.db.prepare(` - UPDATE jobs - SET status = 'queued', - worker_id = NULL, - error_summary = 'Recovered: stuck in running for over ' || ? || ' minutes', - updated_at = datetime('now') - WHERE status IN ('running', 'dispatching') - AND updated_at < datetime('now', '-' || ? || ' minutes') - RETURNING id, repo - `).all(staleMinutes, staleMinutes) as Array<{ id: string; repo: string }>; - if (rows.length > 0) { - // issue ロックも解除 - for (const row of rows) { - this.db.prepare('DELETE FROM issue_locks WHERE job_id = ?').run(row.id); - } - logger.warn(`Repository: recovered ${rows.length} stuck jobs (stale > ${staleMinutes}min): ${rows.map(r => r.repo).join(', ')}`); - } - return rows.length; + return jobsRepo.recoverStuckRunningJobs(this.db, staleMinutes); } - /** running/dispatching 状態のジョブを全て queued に戻す(graceful shutdown 用) */ requeueRunningJobs(): number { - const result = this.db - .prepare("UPDATE jobs SET status = 'queued', worker_id = NULL, updated_at = datetime('now') WHERE status IN ('running', 'dispatching')") - .run(); - if (result.changes > 0) { - this.db.prepare('DELETE FROM issue_locks').run(); - logger.warn(`Repository: requeued ${result.changes} running jobs, cleared issue locks`); - } - return result.changes; + return jobsRepo.requeueRunningJobs(this.db); } getDistinctRepos(): string[] { - const rows = this.db.prepare('SELECT DISTINCT repo FROM jobs ORDER BY repo').all() as { repo: string }[]; - return rows.map(r => r.repo); + return jobsRepo.getDistinctRepos(this.db); } getJobsByRepo(repoName: string): Job[] { - const rows = this.db.prepare('SELECT * FROM jobs WHERE repo = ? ORDER BY created_at DESC').all(repoName) as JobRow[]; - return rows.map(rowToJob); + return jobsRepo.getJobsByRepo(this.db, repoName); } - /** Issue ごとに最新の Job だけを返す(カンバンUI用) */ getLatestJobsPerIssue(repoName: string): Job[] { - const rows = this.db.prepare(` - SELECT j.* - FROM jobs j - WHERE j.repo = ? - AND j.id = ( - SELECT j2.id - FROM jobs j2 - WHERE j2.repo = j.repo - AND j2.issue_number = j.issue_number - ORDER BY j2.updated_at DESC, j2.created_at DESC, j2.rowid DESC - LIMIT 1 - ) - ORDER BY j.updated_at DESC - `).all(repoName) as JobRow[]; - return rows.map(rowToJob); + return jobsRepo.getLatestJobsPerIssue(this.db, repoName); } - // Cascade a local_task visibility change to all jobs spawned for that task - // and their recursive subtask descendants (repo='subtask/'). - // Returns the number of job rows updated. - async updateJobsVisibilityForTask( - taskId: number, - updates: { visibility: 'private' | 'org' | 'public'; visibilityScopeOrgId: string | null }, - ): Promise { - const repoName = localTaskRepoName(taskId); - const now = new Date().toISOString(); - const result = this.db - .prepare(` - WITH RECURSIVE job_tree(id) AS ( - SELECT id FROM jobs WHERE repo = ? - UNION ALL - SELECT j.id FROM jobs j JOIN job_tree jt ON j.parent_job_id = jt.id - ) - UPDATE jobs - SET visibility = ?, - visibility_scope_org_id = ?, - updated_at = ? - WHERE id IN (SELECT id FROM job_tree) - `) - .run(repoName, updates.visibility, updates.visibilityScopeOrgId, now); - return result.changes; + async updateJobsVisibilityForTask(taskId: number, updates: { visibility: 'private' | 'org' | 'public'; visibilityScopeOrgId: string | null }): Promise { + return jobsRepo.updateJobsVisibilityForTask(this.db, taskId, updates); } async getSubJobs(parentJobId: string): Promise { - // 同一 issue_number に複数ジョブがある場合(ASK再投入等)、最新のみ返す - // ROW_NUMBER() + rowid で同一 created_at でも一意に決定する - const rows = this.db - .prepare(` - SELECT * FROM ( - SELECT j.*, ROW_NUMBER() OVER ( - PARTITION BY j.issue_number - ORDER BY j.created_at DESC, j.rowid DESC - ) AS rn - FROM jobs j - WHERE j.parent_job_id = ? - ) WHERE rn = 1 - ORDER BY issue_number ASC - `) - .all(parentJobId) as JobRow[]; - return rows.map(rowToJob); + return jobsRepo.getSubJobs(this.db, parentJobId); } - /** - * 全サブジョブが終端状態なら親ジョブを再キューに戻す。 - * 再キューできた場合 true を返す。 - */ async requeueParentJobIfAllSubtasksDone(parentJobId: string): Promise { - // 同一 issue_number に複数ジョブがある場合、最新のもの(ROW_NUMBER=1)のみで判定する - const result = this.db.prepare(` - UPDATE jobs - SET status = 'queued', - updated_at = datetime('now') - WHERE id = ? - AND status = 'waiting_subtasks' - AND ( - SELECT COUNT(*) FROM ( - SELECT status, ROW_NUMBER() OVER ( - PARTITION BY issue_number - ORDER BY created_at DESC, rowid DESC - ) AS rn - FROM jobs - WHERE parent_job_id = ? - ) WHERE rn = 1 - AND status NOT IN ('succeeded', 'failed', 'cancelled') - ) = 0 - `).run(parentJobId, parentJobId); - return result.changes > 0; + return jobsRepo.requeueParentJobIfAllSubtasksDone(this.db, parentJobId); } async deleteLocalTask(taskId: number, worktreeDir?: string): Promise { - const repoName = localTaskRepoName(taskId); - - // タスク存在確認 & workspace_path / space_id / mode / runtime_dir を取得(削除前に必要) - const taskRow = this.db - .prepare('SELECT workspace_path, space_id, workspace_mode, runtime_dir FROM local_tasks WHERE id = ?') - .get(taskId) as { - workspace_path: string | null; - space_id: string | null; - workspace_mode: string | null; - runtime_dir: string | null; - } | undefined; - if (!taskRow) { - throw new Error(`deleteLocalTask: task ${taskId} not found`); - } - - // running/dispatching なジョブがある場合は削除を拒否 - const activeJob = this.db - .prepare("SELECT id FROM jobs WHERE repo = ? AND status IN ('running', 'dispatching') LIMIT 1") - .get(repoName) as { id: string } | undefined; - if (activeJob) { - throw new Error(`deleteLocalTask: task ${taskId} has an active job (${activeJob.id})`); - } - - // DB 操作をトランザクションで実行 - const deleteTransaction = this.db.transaction(() => { - this.db.prepare('DELETE FROM issue_locks WHERE repo = ?').run(repoName); - this.db.prepare('DELETE FROM jobs WHERE repo = ?').run(repoName); - this.db.prepare('DELETE FROM task_comment_index WHERE task_id = ?').run(taskId); - this.db.prepare('DELETE FROM local_tasks WHERE id = ?').run(taskId); - }); - deleteTransaction(); - - // ワークスペース削除(DB トランザクション外 — ロールバック不可のため)。 - // persistent スペースタスクの workspace_path は **スペース共有ツリー** - // (`{worktreeDir}/space/{spaceId}/files`) で、同じスペースの全会話が共有する。 - // 1 会話の削除でこれを rm すると他会話の output/input まで巻き添えで消える(データ消失) - // ため、共有ツリーは削除しない(DB 行のみ削除)。ephemeral/local の per-task - // ワークスペースは従来どおり削除する。会話固有の runtime_dir - // (`space/{spaceId}/runs/{taskId}` = logs/checklists/raw) は per-会話なので掃除する。 - - // パスヘルパー: normalize(末尾区切り除去)/ 実体 realpath / 末尾セグメント一致。 - const norm = (p: string): string => normalize(p).replace(/[/\\]+$/, ''); - const realOf = (p: string): string => { - const n = norm(p); - try { return existsSync(n) ? realpathSync(n) : n; } catch { return n; } - }; - const hasTail = (path: string, tail: string[]): boolean => { - const segs = path.split(/[/\\]+/); - return segs.length >= tail.length && tail.every((t, i) => segs[segs.length - tail.length + i] === t); - }; - - // 削除は **ホワイトリスト + worktreeDir アンカー** 方式。「共有ツリー形を blacklist して - // 例外を追う」と symlink/normalization のたびに抜け道が増えるため、逆に **設定された - // worktreeDir 配下に root され、かつシステムが作る per-task 形** のものだけを消す。 - // 判定は logical と realpath の **両方** で行い、どちらかが worktree 外/別実体を指す - // symlink なら消さない(共有ツリーや worktree 外データを巻き込まない)。 - // worktreeDir が無い経路(legacy/一部テスト)は安全側に倒して fs 削除を一切しない。 - if (worktreeDir) { - const root = realOf(worktreeDir); - const rooted = (real: string): boolean => real === root || real.startsWith(root + sep); - - // workspace_path: per-task は ephemeral/{taskId} または旧 local/{taskId} のみ。共有ツリー - // (space/{id}/files)・worktree 外・symlink リダイレクトは保持。 - if (taskRow.workspace_path) { - const wsReal = realOf(taskRow.workspace_path); - const perTaskShape = (s: string): boolean => - hasTail(s, ['ephemeral', String(taskId)]) || hasTail(s, ['local', String(taskId)]); - if (rooted(wsReal) && perTaskShape(wsReal) && perTaskShape(norm(taskRow.workspace_path)) && existsSync(wsReal)) { - rmSync(wsReal, { recursive: true, force: true }); - } else { - logger.info(`Repository: deleteLocalTask kept non-per-task workspace ${taskRow.workspace_path} (task ${taskId})`); - } - } - - // runtime_dir: 会話固有 (space/<任意>/runs/{taskId})。worktree 配下に root され、logical/real - // 両方が runs/{taskId} 形のときだけ掃除(共有 files を指す symlink は real 形が一致せず除外)。 - if (taskRow.runtime_dir) { - const rtReal = realOf(taskRow.runtime_dir); - const runsShape = (s: string): boolean => - hasTail(s, ['runs', String(taskId)]) && s.split(/[/\\]+/).slice(-4)[0] === 'space'; - if (rooted(rtReal) && runsShape(rtReal) && runsShape(norm(taskRow.runtime_dir)) && existsSync(rtReal)) { - rmSync(rtReal, { recursive: true, force: true }); - } else { - logger.info(`Repository: deleteLocalTask kept runtime_dir ${taskRow.runtime_dir} (task ${taskId})`); - } - } - } - - logger.info(`Repository: deleted local task ${taskId}`); + return localTasksRepo.deleteLocalTask(this.db, taskId, worktreeDir); } - /** - * 実行中のジョブをキャンセル状態に変更する。 - * running または dispatching 状態のジョブのみ対象。 - * 戻り値: キャンセル対象ジョブが見つかったら true、見つからなかったら false。 - */ requestJobCancel(jobId: string): boolean { - const result = this.db.prepare(` - UPDATE jobs - SET status = 'cancelled', updated_at = datetime('now') - WHERE id = ? AND status IN ('running', 'dispatching') - `).run(jobId); - return result.changes > 0; + return jobsRepo.requestJobCancel(this.db, jobId); } - // ── Scheduled Tasks ────────────────────────────────────────── - private mapScheduledTask(row: any): ScheduledTask { - const rawVisibility = row.visibility; - const visibility: ScheduledTask['visibility'] = - rawVisibility === 'org' || rawVisibility === 'public' ? rawVisibility : 'private'; - return { - id: row.id, - title: row.title, - body: row.body, - pieceName: row.piece_name, - profile: row.profile, - outputFormat: row.output_format, - cronExpression: row.cron_expression, - nextRunAt: utc(row.next_run_at), - lastRunAt: utc(row.last_run_at), - lastJobId: row.last_job_id, - isActive: row.is_active === 1, - ownerId: row.owner_id ?? null, - ownerName: row.owner_name ?? null, - visibility, - visibilityScopeOrgId: row.visibility_scope_org_id ?? null, - visibilityScopeOrgName: row.visibility_scope_org_name ?? null, - browserSessionProfileId: row.browser_session_profile_id ?? null, - taskKind: row.task_kind === 'script' ? 'script' : 'agent', - scriptName: row.script_name ?? null, - scriptParams: row.script_params ?? null, - spaceId: row.space_id ?? null, - createdAt: utc(row.created_at), - updatedAt: utc(row.updated_at), - }; + return scheduledTasksRepo.mapScheduledTask(row); } async createScheduledTask(params: CreateScheduledTaskParams): Promise { - const result = this.db - .prepare( - `INSERT INTO scheduled_tasks (title, body, piece_name, profile, output_format, cron_expression, next_run_at, owner_id, visibility, visibility_scope_org_id, browser_session_profile_id, task_kind, script_name, script_params, space_id) - VALUES (@title, @body, @pieceName, @profile, @outputFormat, @cronExpression, @nextRunAt, @ownerId, @visibility, @visibilityScopeOrgId, @browserSessionProfileId, @taskKind, @scriptName, @scriptParams, @spaceId)` - ) - .run({ - title: params.title ?? null, - body: params.body, - pieceName: params.pieceName ?? 'auto', - profile: params.profile ?? 'auto', - outputFormat: params.outputFormat ?? 'markdown', - cronExpression: params.cronExpression, - nextRunAt: params.nextRunAt, - ownerId: params.ownerId ?? null, - visibility: params.visibility ?? 'private', - visibilityScopeOrgId: params.visibilityScopeOrgId ?? null, - browserSessionProfileId: params.browserSessionProfileId ?? null, - taskKind: params.taskKind ?? 'agent', - scriptName: params.scriptName ?? null, - scriptParams: params.scriptParams ?? null, - spaceId: params.spaceId ?? null, - }); - const task = this.getScheduledTaskSync(Number(result.lastInsertRowid)); - if (!task) throw new Error('createScheduledTask: failed to load inserted task'); - return task; + return scheduledTasksRepo.createScheduledTask(this.db, params); } private getScheduledTaskSync(id: number): ScheduledTask | null { - const row = this.db - .prepare(` - SELECT st.*, - ${SCHEDULED_TASK_DISPLAY_SELECT} - FROM scheduled_tasks st - ${SCHEDULED_TASK_DISPLAY_JOIN} - WHERE st.id = ? - `) - .get(id) as any; - return row ? this.mapScheduledTask(row) : null; + return scheduledTasksRepo.getScheduledTaskSync(this.db, id); } async getScheduledTask(id: number, opts?: { viewer?: Express.User }): Promise { - const viewerClause = opts?.viewer - ? buildVisibilityWhere(opts.viewer, 'st', { spaceColumn: 'space_id' }) - : { clause: '1=1', params: [] as unknown[] }; - const row = this.db - .prepare(` - SELECT st.*, - ${SCHEDULED_TASK_DISPLAY_SELECT} - FROM scheduled_tasks st - ${SCHEDULED_TASK_DISPLAY_JOIN} - WHERE st.id = ? AND ${viewerClause.clause} - `) - .get(id, ...viewerClause.params) as any; - return row ? this.mapScheduledTask(row) : null; + return scheduledTasksRepo.getScheduledTask(this.db, id, opts); } async listScheduledTasks(filter?: { viewer?: Express.User; spaceId?: string | null }): Promise { - const conditions: string[] = []; - const queryParams: unknown[] = []; - if (filter?.viewer) { - const w = buildVisibilityWhere(filter.viewer, 'st', { spaceColumn: 'space_id' }); - conditions.push(w.clause); - queryParams.push(...w.params); - } - // Optional per-space scoping (the space's "Schedules" tab). null = only the - // space-less (personal) schedules; a string = exactly that space. - if (filter?.spaceId !== undefined) { - if (filter.spaceId === null) { - conditions.push('st.space_id IS NULL'); - } else { - conditions.push('st.space_id = ?'); - queryParams.push(filter.spaceId); - } - } - const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; - const rows = this.db - .prepare(` - SELECT st.*, - ${SCHEDULED_TASK_DISPLAY_SELECT} - FROM scheduled_tasks st - ${SCHEDULED_TASK_DISPLAY_JOIN} - ${whereClause} - ORDER BY st.created_at DESC - `) - .all(...queryParams) as any[]; - return rows.map(r => this.mapScheduledTask(r)); + return scheduledTasksRepo.listScheduledTasks(this.db, filter); } - /** - * due なスケジュールタスクをアトミックに claim して返す。 - * BEGIN IMMEDIATE で書き込みロックを即座に取得し、 - * 他のスケジューラーインスタンスとの重複実行を防止する。 - * claim されたタスクは next_run_at が遠い未来に設定されるため、 - * 他インスタンスに再取得されない。 - * 呼び出し側が実行後に正しい next_run_at を再設定する。 - */ async getScheduledTasksDue(): Promise { - // 十分遠い未来(claim マーカー) - const claimMarker = '9999-12-31 23:59:59'; - - // BEGIN IMMEDIATE: トランザクション開始時に RESERVED ロックを取得し、 - // 他の書き込みトランザクションとの競合を防ぐ - const txn = this.db.transaction(() => { - const rows = this.db - .prepare( - `SELECT * FROM scheduled_tasks - WHERE is_active = 1 AND next_run_at <= datetime('now') - ORDER BY next_run_at ASC` - ) - .all() as any[]; - - if (rows.length === 0) return []; - - // claim: next_run_at を遠い未来に設定して他インスタンスからの重複取得を防止 - const ids = rows.map((r: any) => r.id); - this.db - .prepare( - `UPDATE scheduled_tasks - SET next_run_at = ?, updated_at = datetime('now') - WHERE id IN (${ids.map(() => '?').join(',')})` - ) - .run(claimMarker, ...ids); - - return rows; - }); - - const rows = txn.immediate(); - return rows.map((r: any) => this.mapScheduledTask(r)); + return scheduledTasksRepo.getScheduledTasksDue(this.db); } async updateScheduledTask(id: number, params: UpdateScheduledTaskParams): Promise { - const sets: string[] = []; - const values: Record = { id }; - - if (params.title !== undefined) { sets.push('title = @title'); values.title = params.title; } - if (params.body !== undefined) { sets.push('body = @body'); values.body = params.body; } - if (params.pieceName !== undefined) { sets.push('piece_name = @pieceName'); values.pieceName = params.pieceName; } - if (params.profile !== undefined) { sets.push('profile = @profile'); values.profile = params.profile; } - if (params.outputFormat !== undefined) { sets.push('output_format = @outputFormat'); values.outputFormat = params.outputFormat; } - if (params.cronExpression !== undefined) { sets.push('cron_expression = @cronExpression'); values.cronExpression = params.cronExpression; } - if (params.nextRunAt !== undefined) { sets.push('next_run_at = @nextRunAt'); values.nextRunAt = params.nextRunAt; } - if (params.lastRunAt !== undefined) { sets.push('last_run_at = @lastRunAt'); values.lastRunAt = params.lastRunAt; } - if (params.lastJobId !== undefined) { sets.push('last_job_id = @lastJobId'); values.lastJobId = params.lastJobId; } - if (params.isActive !== undefined) { sets.push('is_active = @isActive'); values.isActive = params.isActive ? 1 : 0; } - if (params.visibility !== undefined) { sets.push('visibility = @visibility'); values.visibility = params.visibility; } - if (params.visibilityScopeOrgId !== undefined) { - sets.push('visibility_scope_org_id = @visibilityScopeOrgId'); - values.visibilityScopeOrgId = params.visibilityScopeOrgId; - } - if (params.browserSessionProfileId !== undefined) { - sets.push('browser_session_profile_id = @browserSessionProfileId'); - values.browserSessionProfileId = params.browserSessionProfileId; - } - if (params.taskKind !== undefined) { - sets.push('task_kind = @taskKind'); - values.taskKind = params.taskKind; - } - if (params.scriptName !== undefined) { - sets.push('script_name = @scriptName'); - values.scriptName = params.scriptName; - } - if (params.scriptParams !== undefined) { - sets.push('script_params = @scriptParams'); - values.scriptParams = params.scriptParams; - } - - if (sets.length === 0) return this.getScheduledTaskSync(id); - - sets.push("updated_at = datetime('now')"); - this.db.prepare(`UPDATE scheduled_tasks SET ${sets.join(', ')} WHERE id = @id`).run(values); - return this.getScheduledTaskSync(id); + return scheduledTasksRepo.updateScheduledTask(this.db, id, params); } async deleteScheduledTask(id: number): Promise { - const result = this.db.prepare('DELETE FROM scheduled_tasks WHERE id = ?').run(id); - return result.changes > 0; - } - - // ── User CRUD ──────────────────────────────────────────────────── - - private rowToUser(row: UserRow): User { - return rowToUser(row); + return scheduledTasksRepo.deleteScheduledTask(this.db, id); } createUser(params: CreateUserParams): User { - const id = uuidv4(); - const now = new Date().toISOString(); - this.db - .prepare( - `INSERT INTO users (id, email, name, avatar_url, role, status, created_at, updated_at) - VALUES (@id, @email, @name, @avatarUrl, @role, @status, @now, @now)` - ) - .run({ - id, - email: params.email, - name: params.name, - avatarUrl: params.avatarUrl ?? null, - role: params.role, - status: params.status, - now, - }); - const user = this.getUserById(id); - if (!user) throw new Error(`createUser: failed to retrieve created user ${id}`); - return user; + return usersRepo.createUser(this.db, params); } - /** - * Ensure the synthetic 'local' user row exists. No-auth single-user - * deployments own per-user rows under the id 'local' (tasks, jobs, SSH - * connections, DEKs, …). Many of those tables FK to users(id) with - * foreign_keys ON, so the row must exist or the inserts fail — e.g. - * ssh_user_deks → SSH connection creation returned create_failed. - * Idempotent (INSERT OR IGNORE), so it is safe to call on every startup. - * role='admin' mirrors the synthetic 'local' user the HTTP layer injects - * for task-visibility routes in no-auth mode. - */ ensureLocalUser(): void { - const now = new Date().toISOString(); - this.db - .prepare( - `INSERT OR IGNORE INTO users (id, email, name, avatar_url, role, status, created_at, updated_at) - VALUES ('local', 'local@localhost', 'local', NULL, 'admin', 'active', @now, @now)` - ) - .run({ now }); + return usersRepo.ensureLocalUser(this.db); } - // ── Local auth (email + password) ───────────────────────────────────── - - /** scrypt hash with a fresh per-user salt. Overwrites any existing credential. */ setLocalPassword(userId: string, plainPassword: string): void { - const salt = randomBytes(16).toString('hex'); - const hash = scryptSync(plainPassword, salt, 64).toString('hex'); - const now = new Date().toISOString(); - this.db - .prepare( - `INSERT INTO local_credentials (user_id, password_hash, salt, updated_at) - VALUES (@userId, @hash, @salt, @now) - ON CONFLICT(user_id) DO UPDATE SET password_hash=@hash, salt=@salt, updated_at=@now`, - ) - .run({ userId, hash, salt, now }); + return usersRepo.setLocalPassword(this.db, userId, plainPassword); } - /** Constant-time verify. False when the user has no local credential. */ verifyLocalPassword(userId: string, plainPassword: string): boolean { - const row = this.db - .prepare('SELECT password_hash, salt FROM local_credentials WHERE user_id = ?') - .get(userId) as { password_hash: string; salt: string } | undefined; - if (!row) return false; - const expected = Buffer.from(row.password_hash, 'hex'); - const actual = scryptSync(plainPassword, row.salt, expected.length); - return expected.length === actual.length && timingSafeEqual(expected, actual); + return usersRepo.verifyLocalPassword(this.db, userId, plainPassword); } hasLocalCredential(userId: string): boolean { - return !!this.db.prepare('SELECT 1 FROM local_credentials WHERE user_id = ?').get(userId); + return usersRepo.hasLocalCredential(this.db, userId); } - /** - * Create a brand-new local account (self-signup or admin-created). The email - * MUST be unused: attaching a password to an existing account would be an - * account-takeover vector, so we reject instead of linking. Linking a local - * credential to an existing OAuth account is a separate, authenticated action - * (not v1 signup). - */ createLocalUser(params: CreateLocalUserParams): User { - if (this.getUserByEmail(params.email)) { - throw new Error(`createLocalUser: a user with email ${params.email} already exists`); - } - const user = this.createUser({ - email: params.email, - name: params.name ?? params.email, - role: params.role, - status: params.status, - }); - this.db - .prepare( - `INSERT OR IGNORE INTO oauth_accounts (id, user_id, provider, provider_id, created_at) - VALUES (@id, @userId, 'local', @providerId, @now)`, - ) - .run({ id: uuidv4(), userId: user.id, providerId: params.email, now: new Date().toISOString() }); - this.setLocalPassword(user.id, params.password); - return user; + return usersRepo.createLocalUser(this.db, params); } - /** - * Idempotently seed the shared system admin under the fixed id `local` — the - * same owner the no-auth path synthesizes. This makes all pre-existing - * `local`-owned data belong to the logged-in admin once local auth is turned - * on, and lets an existing no-auth deployment gain a login mid-stream. - * Re-running updates the password and keeps role=admin/status=active. - */ upsertLocalSystemAdmin(params: { email: string; password: string; name?: string }): User { - const LOCAL_ID = 'local'; - const now = new Date().toISOString(); - const existing = this.getUserById(LOCAL_ID); - if (!existing) { - this.db - .prepare( - `INSERT INTO users (id, email, name, avatar_url, role, status, created_at, updated_at) - VALUES (@id, @email, @name, NULL, 'admin', 'active', @now, @now)`, - ) - .run({ id: LOCAL_ID, email: params.email, name: params.name ?? 'Local Admin', now }); - } else { - this.db - .prepare(`UPDATE users SET email=@email, role='admin', status='active', updated_at=@now WHERE id=@id`) - .run({ id: LOCAL_ID, email: params.email, now }); - } - this.db - .prepare( - `INSERT OR IGNORE INTO oauth_accounts (id, user_id, provider, provider_id, created_at) - VALUES (@id, @userId, 'local', @providerId, @now)`, - ) - .run({ id: uuidv4(), userId: LOCAL_ID, providerId: params.email, now }); - this.setLocalPassword(LOCAL_ID, params.password); - const user = this.getUserById(LOCAL_ID); - if (!user) throw new Error('upsertLocalSystemAdmin: failed to retrieve local admin'); - return user; + return usersRepo.upsertLocalSystemAdmin(this.db, params); } - // ── Local organizations ─────────────────────────────────────────────── - private rowToLocalOrg(r: { id: string; name: string; created_by: string | null; created_at: string }): LocalOrg { - return { id: r.id, name: r.name, createdBy: r.created_by, createdAt: r.created_at }; + return usersRepo.rowToLocalOrg(r); } - /** Create a local org. id is prefixed `lorg:` so it never collides with a - * Gitea numeric org id (both live in visibility_scope_org_id). */ createLocalOrg(name: string, createdBy: string | null): LocalOrg { - const id = `lorg:${uuidv4()}`; - const now = new Date().toISOString(); - this.db - .prepare(`INSERT INTO local_orgs (id, name, created_by, created_at) VALUES (@id, @name, @createdBy, @now)`) - .run({ id, name, createdBy, now }); - return { id, name, createdBy, createdAt: now }; + return usersRepo.createLocalOrg(this.db, name, createdBy); } getLocalOrg(id: string): LocalOrg | null { - const r = this.db.prepare('SELECT id, name, created_by, created_at FROM local_orgs WHERE id = ?').get(id) as - | { id: string; name: string; created_by: string | null; created_at: string } - | undefined; - return r ? this.rowToLocalOrg(r) : null; + return usersRepo.getLocalOrg(this.db, id); } listLocalOrgs(): LocalOrg[] { - const rows = this.db.prepare('SELECT id, name, created_by, created_at FROM local_orgs ORDER BY name COLLATE NOCASE').all() as Array<{ id: string; name: string; created_by: string | null; created_at: string }>; - return rows.map(r => this.rowToLocalOrg(r)); + return usersRepo.listLocalOrgs(this.db); } renameLocalOrg(id: string, name: string): void { - this.db.prepare('UPDATE local_orgs SET name = ? WHERE id = ?').run(name, id); + return usersRepo.renameLocalOrg(this.db, id, name); } - /** Tables carrying `visibility_scope_org_id` (org-scoped resources). */ - private static readonly ORG_SCOPED_TABLES = ['local_tasks', 'scheduled_tasks', 'jobs']; - - /** - * Delete a local org. Members cascade via FK. Resources scoped to this org - * (visibility='org', visibility_scope_org_id=id) would become invisible to - * everyone once the org is gone — so first downgrade them to 'private' - * (owner + admin can still see them; no data loss). Atomic. - */ deleteLocalOrg(id: string): void { - const tx = this.db.transaction((orgId: string) => { - for (const table of Repository.ORG_SCOPED_TABLES) { - this.db - .prepare(`UPDATE ${table} SET visibility = 'private', visibility_scope_org_id = NULL WHERE visibility_scope_org_id = ?`) - .run(orgId); - } - this.db.prepare('DELETE FROM local_orgs WHERE id = ?').run(orgId); - }); - tx(id); + return usersRepo.deleteLocalOrg(this.db, id); } - /** Add or update a member (idempotent — re-add updates the role). */ addOrgMember(orgId: string, userId: string, role: string = 'member'): void { - const now = new Date().toISOString(); - this.db - .prepare( - `INSERT INTO local_org_members (org_id, user_id, role, added_at) - VALUES (@orgId, @userId, @role, @now) - ON CONFLICT(org_id, user_id) DO UPDATE SET role=@role`, - ) - .run({ orgId, userId, role, now }); + return usersRepo.addOrgMember(this.db, orgId, userId, role); } removeOrgMember(orgId: string, userId: string): void { - this.db.prepare('DELETE FROM local_org_members WHERE org_id = ? AND user_id = ?').run(orgId, userId); + return usersRepo.removeOrgMember(this.db, orgId, userId); } listOrgMembers(orgId: string): LocalOrgMember[] { - const rows = this.db - .prepare('SELECT user_id, role FROM local_org_members WHERE org_id = ? ORDER BY added_at') - .all(orgId) as Array<{ user_id: string; role: string }>; - return rows.map(r => ({ userId: r.user_id, role: r.role })); + return usersRepo.listOrgMembers(this.db, orgId); } - /** Orgs a user belongs to — merged into session.orgIds so the existing - * provider-agnostic 'org' visibility (buildVisibilityWhere) covers them. */ listUserLocalOrgs(userId: string): Array<{ orgId: string; name: string }> { - const rows = this.db - .prepare( - `SELECT o.id AS org_id, o.name AS name - FROM local_org_members m JOIN local_orgs o ON o.id = m.org_id - WHERE m.user_id = ? ORDER BY o.name COLLATE NOCASE`, - ) - .all(userId) as Array<{ org_id: string; name: string }>; - return rows.map(r => ({ orgId: r.org_id, name: r.name })); + return usersRepo.listUserLocalOrgs(this.db, userId); } getUserById(id: string): User | null { - const row = this.db - .prepare('SELECT * FROM users WHERE id = ?') - .get(id) as UserRow | undefined; - return row ? this.rowToUser(row) : null; + return usersRepo.getUserById(this.db, id); } getUserByEmail(email: string): User | null { - const row = this.db - .prepare('SELECT * FROM users WHERE email = ?') - .get(email) as UserRow | undefined; - return row ? this.rowToUser(row) : null; + return usersRepo.getUserByEmail(this.db, email); } findOrCreateUserByOAuth(params: FindOrCreateByOAuthParams): User { - // 1. Check if oauth_account already exists - const existing = this.db - .prepare('SELECT user_id FROM oauth_accounts WHERE provider = ? AND provider_id = ?') - .get(params.provider, params.providerId) as { user_id: string } | undefined; - - if (existing) { - const user = this.getUserById(existing.user_id); - if (!user) throw new Error(`findOrCreateUserByOAuth: user ${existing.user_id} not found`); - // Sync mutable profile fields from the provider on every re-login so - // existing users whose name was missing on first login pick it up once - // their Gitea profile is populated. Email upgrade only applies when the - // dummy @gitea.local placeholder is being replaced. - const patch: { email?: string; name?: string; avatarUrl?: string | null } = {}; - if (user.email.endsWith('@gitea.local') && !params.email.endsWith('@gitea.local')) { - patch.email = params.email; - } - if (params.name && params.name !== user.name) patch.name = params.name; - if (params.avatarUrl !== undefined && params.avatarUrl !== user.avatarUrl) { - patch.avatarUrl = params.avatarUrl; - } - if (Object.keys(patch).length > 0) { - this.updateUser(user.id, patch); - const refreshed = this.getUserById(user.id); - if (refreshed) return refreshed; - } - return user; - } - - // 2. Check if user exists by email - let user = this.getUserByEmail(params.email); - - if (!user) { - // 3. Create new user with status=pending - user = this.createUser({ - email: params.email, - name: params.name, - role: 'user', - status: 'pending', - avatarUrl: params.avatarUrl, - }); - } - - // 4. Link oauth_account to user - const oauthId = uuidv4(); - const now = new Date().toISOString(); - this.db - .prepare( - `INSERT OR IGNORE INTO oauth_accounts (id, user_id, provider, provider_id, created_at) - VALUES (@id, @userId, @provider, @providerId, @now)` - ) - .run({ - id: oauthId, - userId: user.id, - provider: params.provider, - providerId: params.providerId, - now, - }); - - return user; + return usersRepo.findOrCreateUserByOAuth(this.db, params); } listUsers(): User[] { - const rows = this.db - .prepare('SELECT * FROM users ORDER BY created_at ASC') - .all() as UserRow[]; - return rows.map(row => this.rowToUser(row)); + return usersRepo.listUsers(this.db); } - /** - * Active users who share at least one organization with the given org id - * set (Gitea org cache `user_gitea_orgs` OR local org membership - * `local_org_members`). Used by the member-invite picker so a requester only - * sees collaborators inside their own org(s) — exposing the full user list is - * a privacy leak. Empty `orgIds` → empty result. Deduplicated by user id. - */ listActiveUsersInOrgs(orgIds: string[]): User[] { - if (orgIds.length === 0) return []; - const placeholders = orgIds.map(() => '?').join(', '); - const rows = this.db - .prepare( - `SELECT u.* FROM users u - WHERE u.status = 'active' AND u.id IN ( - SELECT user_id FROM user_gitea_orgs WHERE org_id IN (${placeholders}) - UNION - SELECT user_id FROM local_org_members WHERE org_id IN (${placeholders}) - ) - ORDER BY u.created_at ASC`, - ) - .all(...orgIds, ...orgIds) as UserRow[]; - return rows.map(row => this.rowToUser(row)); + return usersRepo.listActiveUsersInOrgs(this.db, orgIds); } updateUser(id: string, updates: { @@ -4724,136 +745,34 @@ export class Repository { defaultVisibility?: 'private' | 'org' | 'public'; defaultVisibilityOrgId?: string | null; }): void { - const setClauses: string[] = ["updated_at = datetime('now')"]; - const params: Record = { id }; - - if (updates.status !== undefined) { - setClauses.push('status = @status'); - params['status'] = updates.status; - } - if (updates.role !== undefined) { - setClauses.push('role = @role'); - params['role'] = updates.role; - } - if (updates.email !== undefined) { - setClauses.push('email = @email'); - params['email'] = updates.email; - } - if (updates.name !== undefined) { - setClauses.push('name = @name'); - params['name'] = updates.name; - } - if (updates.avatarUrl !== undefined) { - setClauses.push('avatar_url = @avatar_url'); - params['avatar_url'] = updates.avatarUrl; - } - if (updates.defaultVisibility !== undefined) { - setClauses.push('default_visibility = @default_visibility'); - params['default_visibility'] = updates.defaultVisibility; - } - if (updates.defaultVisibilityOrgId !== undefined) { - setClauses.push('default_visibility_org_id = @default_visibility_org_id'); - params['default_visibility_org_id'] = updates.defaultVisibilityOrgId; - } - - if (setClauses.length === 1) return; - - this.db - .prepare(`UPDATE users SET ${setClauses.join(', ')} WHERE id = @id`) - .run(params); + return usersRepo.updateUser(this.db, id, updates); } deleteUser(id: string): void { - // Never delete the shared `local` system/admin user: it is the no-auth - // fallback owner and owns all single-user-mode data. Deleting it would - // break no-auth mode and orphan every `local`-owned task/job/folder. - if (id === 'local') { - throw new Error('cannot delete the local/system user'); - } - this.db.prepare('DELETE FROM users WHERE id = ?').run(id); + return usersRepo.deleteUser(this.db, id); } deleteSessionsByUserId(userId: string): void { - // Sessions store passport user info as JSON in sess column - // Delete sessions where sess contains the user id - const rows = this.db - .prepare('SELECT sid, sess FROM sessions') - .all() as Array<{ sid: string; sess: string }>; - - const toDelete: string[] = []; - for (const row of rows) { - try { - const sess = JSON.parse(row.sess) as Record; - const passport = sess['passport'] as Record | undefined; - if (passport && passport['user'] === userId) { - toDelete.push(row.sid); - } - } catch { - // ignore parse errors - } - } - - if (toDelete.length > 0) { - const placeholders = toDelete.map(() => '?').join(', '); - this.db.prepare(`DELETE FROM sessions WHERE sid IN (${placeholders})`).run(...toDelete); - } + return usersRepo.deleteSessionsByUserId(this.db, userId); } replaceUserGiteaOrgs(userId: string, orgs: GiteaOrgInput[]): void { - const tx = this.db.transaction((uid: string, items: GiteaOrgInput[]) => { - this.db.prepare('DELETE FROM user_gitea_orgs WHERE user_id = ?').run(uid); - const insert = this.db.prepare( - 'INSERT INTO user_gitea_orgs (user_id, org_id, org_name) VALUES (?, ?, ?)' - ); - for (const o of items) insert.run(uid, o.orgId, o.orgName); - }); - tx(userId, orgs); + return usersRepo.replaceUserGiteaOrgs(this.db, userId, orgs); } listUserGiteaOrgs(userId: string): GiteaOrg[] { - const rows = this.db - .prepare('SELECT org_id, org_name, fetched_at FROM user_gitea_orgs WHERE user_id = ? ORDER BY org_name ASC') - .all(userId) as Array<{ org_id: string; org_name: string; fetched_at: string }>; - return rows.map(r => ({ orgId: r.org_id, orgName: r.org_name, fetchedAt: r.fetched_at })); + return usersRepo.listUserGiteaOrgs(this.db, userId); } - // ── Reflection piece-edit cooldown ────────────────────────────────────────── - - /** - * Records that the reflection pipeline wrote a new version of pieceName for - * userId. snapshotId ties the edit back to the snapshot that triggered it. - */ recordPieceEdit(userId: string, pieceName: string, snapshotId: string): void { - this.db.prepare( - `INSERT INTO reflection_piece_edits (user_id, piece_name, snapshot_id, created_at) - VALUES (?, ?, ?, ?)` - ).run(userId, pieceName, snapshotId, Date.now()); + return reflectionRepo.recordPieceEdit(this.db, userId, pieceName, snapshotId); } - /** - * Returns the number of piece edits for (userId, pieceName) that occurred - * within the last sinceMs milliseconds. Used by the cooldown gate in - * piece-writer.ts to prevent over-editing the same piece. - */ countRecentPieceEdits(userId: string, pieceName: string, sinceMs: number): number { - return (this.db.prepare( - `SELECT COUNT(*) AS c FROM reflection_piece_edits - WHERE user_id = ? AND piece_name = ? AND created_at > ?` - ).get(userId, pieceName, Date.now() - sinceMs) as { c: number }).c; + return reflectionRepo.countRecentPieceEdits(this.db, userId, pieceName, sinceMs); } - // ── Reflection metrics ─────────────────────────────────────────────────────── - - /** - * Insert one row into reflection_metrics, optionally bundling a - * reflection_piece_edits row in the same transaction. - * - * When pieceEdit is supplied the two inserts are wrapped in a single - * db.transaction() so the tables stay consistent even if the process - * crashes between them. - */ - recordReflectionRun( - metric: { + recordReflectionRun(metric: { reflection_job_id: string; original_job_id: string | null; user_id: string; @@ -4864,38 +783,11 @@ export class Repository { tokens_in: number; tokens_out: number; duration_ms: number; - }, - pieceEdit?: { pieceName: string; snapshotId: string }, - ): void { - const now = Date.now(); - const insertMetric = this.db.prepare(` - INSERT INTO reflection_metrics - (reflection_job_id, original_job_id, user_id, piece_name, outcome, - memory_changes, piece_edited, tokens_in, tokens_out, duration_ms, created_at) - VALUES - (@reflection_job_id, @original_job_id, @user_id, @piece_name, @outcome, - @memory_changes, @piece_edited, @tokens_in, @tokens_out, @duration_ms, @created_at) - `); - - if (pieceEdit) { - const insertEdit = this.db.prepare(` - INSERT INTO reflection_piece_edits (user_id, piece_name, snapshot_id, created_at) - VALUES (?, ?, ?, ?) - `); - this.db.transaction(() => { - insertMetric.run({ ...metric, created_at: now }); - insertEdit.run(metric.user_id, pieceEdit.pieceName, pieceEdit.snapshotId, now); - })(); - } else { - insertMetric.run({ ...metric, created_at: now }); - } + }, pieceEdit?: { pieceName: string; snapshotId: string }): void { + return reflectionRepo.recordReflectionRun(this.db, metric, pieceEdit); } - /** - * Convenience alias for callers that don't need the bundled pieceEdit path. - */ - recordReflectionMetric( - row: { + recordReflectionMetric(row: { reflection_job_id: string; original_job_id: string | null; user_id: string; @@ -4906,19 +798,11 @@ export class Repository { tokens_in: number; tokens_out: number; duration_ms: number; - }, - ): void { - this.recordReflectionRun(row); + }): void { + return reflectionRepo.recordReflectionMetric(this.db, row); } - /** - * Aggregate reflection metrics for a user since sinceMs (epoch ms). - * Returns counts per outcome and totals for tokens + piece edits. - */ - aggregateReflectionMetrics( - userId: string, - sinceMs: number, - ): { + aggregateReflectionMetrics(userId: string, sinceMs: number): { applied: number; partial: number; abstained: number; @@ -4929,50 +813,11 @@ export class Repository { pieceEdits: number; totalRuns: number; } { - const rows = this.db - .prepare( - `SELECT outcome, COUNT(*) AS cnt, - SUM(tokens_in) AS ti, SUM(tokens_out) AS to_, - SUM(piece_edited) AS pe - FROM reflection_metrics - WHERE user_id = ? AND created_at >= ? - GROUP BY outcome`, - ) - .all(userId, sinceMs) as Array<{ - outcome: string; - cnt: number; - ti: number; - to_: number; - pe: number; - }>; - - const result = { - applied: 0, partial: 0, abstained: 0, rejected: 0, failed: 0, - tokensIn: 0, tokensOut: 0, pieceEdits: 0, totalRuns: 0, - }; - for (const r of rows) { - const o = r.outcome as keyof Pick; - if (o in result) (result as Record)[o] = r.cnt; - result.tokensIn += r.ti ?? 0; - result.tokensOut += r.to_ ?? 0; - result.pieceEdits += r.pe ?? 0; - result.totalRuns += r.cnt; - } - return result; + return reflectionRepo.aggregateReflectionMetrics(this.db, userId, sinceMs); } - // ── スペース・カレンダー(予定)───────────────────────────────────── - // - // CRUD はスペース id でスコープ。可視性判定は呼び出し側(space-api が - // getSpace({viewer}) で先にゲートする)に委ねる。日付集計はローカル TZ - // オフセット(分)を SQLite の date(...,'±N minutes') 修飾子で適用する - // (Usage ダッシュボードの localDayOf と同じ「UTC instant をオフセット分 - // ずらして暦日に落とす」方式の SQL 版)。 - - /** ローカル TZ オフセット(分)を SQLite の date() 修飾子文字列に変換する。 */ private tzModifier(tzOffsetMin: number): string { - const n = Math.trunc(tzOffsetMin); - return `${n >= 0 ? '+' : '-'}${Math.abs(n)} minutes`; + return calendarRepo.tzModifier(tzOffsetMin); } async createCalendarEvent(params: { @@ -4987,40 +832,9 @@ export class Repository { createdBy?: 'user' | 'agent'; sourceTaskId?: number | null; }): Promise { - // 終了日が開始日と同じ(または無効)なら単日として NULL に正規化する。 - const endDate = params.endDate && params.endDate > params.date ? params.endDate : null; - const time = params.time ?? null; - // 終了時刻は開始時刻があるときだけ意味を持つ。開始が終日なら end_time は捨てる。 - const endTime = time ? (params.endTime ?? null) : null; - const result = this.db - .prepare( - `INSERT INTO calendar_events (space_id, owner_id, date, end_date, time, end_time, title, description, created_by, source_task_id) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - ) - .run( - params.spaceId, - params.ownerId ?? null, - params.date, - endDate, - time, - endTime, - params.title, - params.description ?? null, - params.createdBy ?? 'user', - params.sourceTaskId ?? null, - ); - const row = this.db - .prepare(`SELECT * FROM calendar_events WHERE id = ?`) - .get(Number(result.lastInsertRowid)) as CalendarEventRow; - return rowToCalendarEvent(row); + return calendarRepo.createCalendarEvent(this.db, params); } - /** - * Record a tool request — either an agent's RequestTool declaration or a - * passively-captured blocked tool call. De-dups within the same job: an - * identical still-pending (job_id, movement_name, tool_name) row is reused - * so a retry loop does not spam the table. Returns the row id. - */ recordToolRequest(params: { id?: string; taskId?: string | null; @@ -5033,409 +847,100 @@ export class Repository { category?: ToolRequestCategory; status?: ToolRequestStatus; }): string { - const category: ToolRequestCategory = params.category ?? 'requested'; - const status: ToolRequestStatus = params.status ?? 'pending'; - // De-dup pending rows so a retry loop never grows the table without bound - // (the table has no GC). Scope by the most specific identity available — - // job → task → piece — so de-dup still works for runs without a job - // binding. scopeCol is a fixed identifier (not user input) → no injection. - const scopeCol: 'job_id' | 'task_id' | 'piece_name' = params.jobId - ? 'job_id' - : params.taskId - ? 'task_id' - : 'piece_name'; - const scopeVal = params.jobId ?? params.taskId ?? params.pieceName; - const existing = this.db - .prepare( - `SELECT id FROM tool_requests - WHERE ${scopeCol} = ? AND movement_name = ? AND tool_name = ? AND status = 'pending' - LIMIT 1`, - ) - .get(scopeVal, params.movementName, params.toolName) as { id: string } | undefined; - if (existing) return existing.id; - const id = params.id ?? randomUUID(); - this.db - .prepare( - `INSERT INTO tool_requests - (id, task_id, job_id, space_id, piece_name, movement_name, tool_name, reason, category, status) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - ) - .run( - id, - params.taskId ?? null, - params.jobId ?? null, - params.spaceId ?? null, - params.pieceName, - params.movementName, - params.toolName, - params.reason ?? null, - category, - status, - ); - return id; + return toolRequestsRepo.recordToolRequest(this.db, params); } - /** All tool requests for a task, newest first. */ listToolRequestsByTask(taskId: string): ToolRequest[] { - const rows = this.db - .prepare(`SELECT * FROM tool_requests WHERE task_id = ? ORDER BY created_at DESC, id DESC`) - .all(taskId) as ToolRequestRow[]; - return rows.map(rowToToolRequest); + return toolRequestsRepo.listToolRequestsByTask(this.db, taskId); } - /** Per-piece aggregation: count + last-seen + a sample reason per - * (tool_name, category). `sampleReason` is one representative reason (not - * necessarily the most recent — it is a MAX over the group). */ aggregateToolRequestsByPiece(pieceName: string): ToolRequestAggregate[] { - const rows = this.db - .prepare( - `SELECT tool_name, category, COUNT(*) AS count, - MAX(created_at) AS last_seen, MAX(reason) AS sample_reason - FROM tool_requests - WHERE piece_name = ? - GROUP BY tool_name, category - ORDER BY count DESC, tool_name ASC`, - ) - .all(pieceName) as Array<{ - tool_name: string; category: string; count: number; last_seen: string; sample_reason: string | null; - }>; - return rows.map((r) => ({ - toolName: r.tool_name, - category: (r.category as ToolRequestCategory) ?? 'requested', - count: r.count, - lastSeen: r.last_seen, - sampleReason: r.sample_reason ?? null, - })); + return toolRequestsRepo.aggregateToolRequestsByPiece(this.db, pieceName); } - /** Fetch a single tool request by id. */ getToolRequest(id: string): ToolRequest | null { - const row = this.db.prepare(`SELECT * FROM tool_requests WHERE id = ?`).get(id) as ToolRequestRow | undefined; - return row ? rowToToolRequest(row) : null; + return toolRequestsRepo.getToolRequest(this.db, id); } - /** - * Record an approve/deny/auto-deny decision on a still-pending tool request. - * Returns false if the row is missing or already decided (idempotent / race - * safe — a concurrent decide won't double-apply). - */ - decideToolRequest( - id: string, - decision: { status: Exclude; grantScope?: 'task' | 'piece' | null; decidedBy?: string | null }, - ): boolean { - const res = this.db - .prepare( - `UPDATE tool_requests - SET status = ?, grant_scope = ?, decided_by = ?, decided_at = datetime('now') - WHERE id = ? AND status = 'pending'`, - ) - .run(decision.status, decision.grantScope ?? null, decision.decidedBy ?? null, id); - return res.changes > 0; + decideToolRequest(id: string, decision: { status: Exclude; grantScope?: 'task' | 'piece' | null; decidedBy?: string | null }): boolean { + return toolRequestsRepo.decideToolRequest(this.db, id, decision); } - /** Per-task grant overlay (tool names approved inline for this task). */ getGrantedTools(taskId: string): string[] { - const row = this.db.prepare(`SELECT granted_tools FROM local_tasks WHERE id = ?`).get(taskId) as - | { granted_tools: string | null } - | undefined; - if (!row?.granted_tools) return []; - try { - const parsed = JSON.parse(row.granted_tools); - return Array.isArray(parsed) ? parsed.filter((v): v is string => typeof v === 'string') : []; - } catch { - return []; - } + return toolRequestsRepo.getGrantedTools(this.db, taskId); } - /** Add a tool to a task's grant overlay (idempotent — de-duped). Returns the new set. */ addGrantedTool(taskId: string, toolName: string): string[] { - const current = this.getGrantedTools(taskId); - if (current.includes(toolName)) return current; - const next = [...current, toolName]; - this.db.prepare(`UPDATE local_tasks SET granted_tools = ? WHERE id = ?`).run(JSON.stringify(next), taskId); - return next; + return toolRequestsRepo.addGrantedTool(this.db, taskId, toolName); } - async listCalendarEvents( - spaceId: string, - range?: { from?: string; to?: string }, - ): Promise { - const conditions = ['space_id = ?']; - const args: unknown[] = [spaceId]; - // 複数日対応: イベント [date, COALESCE(end_date,date)] が [from, to] と - // 重なるものを返す(単日 end_date=NULL は date 自身)。 - if (range?.from) { - conditions.push('COALESCE(end_date, date) >= ?'); - args.push(range.from); - } - if (range?.to) { - conditions.push('date <= ?'); - args.push(range.to); - } - const rows = this.db - .prepare( - `SELECT * FROM calendar_events WHERE ${conditions.join(' AND ')} - ORDER BY date ASC, (time IS NULL) DESC, time ASC, id ASC` - ) - .all(...args) as CalendarEventRow[]; - return rows.map(rowToCalendarEvent); + recordPackageRequest(params: { + id?: string; + taskId?: string | null; + jobId?: string | null; + spaceId?: string | null; + pieceName?: string | null; + movementName?: string | null; + spec: string; + normalizedName: string; + reason?: string | null; + status?: PackageRequestStatus; + }): string { + return toolRequestsRepo.recordPackageRequest(this.db, params); + } + + listPackageRequestsByTask(taskId: string): PackageRequest[] { + return toolRequestsRepo.listPackageRequestsByTask(this.db, taskId); + } + + listDeclinedPackageNamesByJob(jobId: string): string[] { + return toolRequestsRepo.listDeclinedPackageNamesByJob(this.db, jobId); + } + + getPackageRequest(id: string): PackageRequest | null { + return toolRequestsRepo.getPackageRequest(this.db, id); + } + + decidePackageRequest(id: string, decision: { status: Exclude; decidedBy?: string | null }): boolean { + return toolRequestsRepo.decidePackageRequest(this.db, id, decision); + } + + revertPackageRequestToPending(id: string): boolean { + return toolRequestsRepo.revertPackageRequestToPending(this.db, id); + } + + async listCalendarEvents(spaceId: string, range?: { from?: string; to?: string }): Promise { + return calendarRepo.listCalendarEvents(this.db, spaceId, range); } async getCalendarEvent(eventId: number): Promise { - const row = this.db - .prepare(`SELECT * FROM calendar_events WHERE id = ?`) - .get(eventId) as CalendarEventRow | undefined; - return row ? rowToCalendarEvent(row) : null; + return calendarRepo.getCalendarEvent(this.db, eventId); } - async updateCalendarEvent( - eventId: number, - fields: { date?: string; endDate?: string | null; time?: string | null; endTime?: string | null; title?: string; description?: string | null }, - ): Promise { - const sets: string[] = []; - const args: unknown[] = []; - if (fields.date !== undefined) { - sets.push('date = ?'); - args.push(fields.date); - } - if (fields.endDate !== undefined) { - sets.push('end_date = ?'); - args.push(fields.endDate); - } - // time / end_time は連動。開始を終日(null)にしたら end_time も必ず null に落とす。 - if (fields.time !== undefined || fields.endTime !== undefined) { - const existing = await this.getCalendarEvent(eventId); - const newTime = fields.time !== undefined ? fields.time : (existing?.time ?? null); - let newEndTime = fields.endTime !== undefined ? fields.endTime : (existing?.endTime ?? null); - if (newTime == null) newEndTime = null; - if (fields.time !== undefined) { - sets.push('time = ?'); - args.push(newTime); - } - if (newEndTime !== (existing?.endTime ?? null)) { - sets.push('end_time = ?'); - args.push(newEndTime); - } - } - if (fields.title !== undefined) { - sets.push('title = ?'); - args.push(fields.title); - } - if (fields.description !== undefined) { - sets.push('description = ?'); - args.push(fields.description); - } - if (sets.length === 0) return this.getCalendarEvent(eventId); - sets.push(`updated_at = datetime('now')`); - args.push(eventId); - this.db.prepare(`UPDATE calendar_events SET ${sets.join(', ')} WHERE id = ?`).run(...args); - return this.getCalendarEvent(eventId); + async updateCalendarEvent(eventId: number, fields: { date?: string; endDate?: string | null; time?: string | null; endTime?: string | null; title?: string; description?: string | null }): Promise { + return calendarRepo.updateCalendarEvent(this.db, eventId, fields); } async deleteCalendarEvent(eventId: number): Promise { - this.db.prepare(`DELETE FROM calendar_events WHERE id = ?`).run(eventId); + return calendarRepo.deleteCalendarEvent(this.db, eventId); } - /** - * 月ビューの日別カウント。`days` は date → {taskCount, eventCount}。 - * タスクは local_tasks を space_id で絞り、created_at(UTC)をローカル TZ で - * 暦日にバケツ化した日付が [monthStart, monthEnd] に入るものを数える。 - * イベントは date 列がそのまま暦日(ローカル)なので素直に範囲で絞る。 - * ファイル数は重いので含めない(日詳細でのみ算出)。 - */ - /** - * SQL scope for "tasks belonging to this space's calendar". Normally just - * `space_id = ?`. A PERSONAL space also owns its owner's space-less tasks: - * personal-workspace tasks are stored with `space_id = NULL` (by design), so - * include `(space_id IS NULL AND owner_id = ?)` when a personalOwnerId is - * given. Mirrors worker.ts resolveToolSpaceId (NULL+owner → personal space). - */ - private calendarTaskScope( - spaceId: string, - personalOwnerId?: string | null, - alias = '', - ): { clause: string; params: unknown[] } { - const col = alias ? `${alias}.` : ''; - if (personalOwnerId) { - return { - clause: `(${col}space_id = ? OR (${col}space_id IS NULL AND ${col}owner_id = ?))`, - params: [spaceId, personalOwnerId], - }; - } - return { clause: `${col}space_id = ?`, params: [spaceId] }; + private calendarTaskScope(spaceId: string, personalOwnerId?: string | null, alias = ''): { clause: string; params: unknown[] } { + return calendarRepo.calendarTaskScope(spaceId, personalOwnerId, alias); } - async getSpaceCalendarMonth( - spaceId: string, - opts: { monthStart: string; monthEnd: string; tzOffsetMin: number; personalOwnerId?: string | null }, - ): Promise<{ days: Record; events: CalendarEvent[] }> { - const mod = this.tzModifier(opts.tzOffsetMin); - const days: Record = {}; - const ensure = (d: string): CalendarDayCounts => { - if (!days[d]) days[d] = { taskCount: 0, eventCount: 0 }; - return days[d]!; - }; - - const scope = this.calendarTaskScope(spaceId, opts.personalOwnerId); - const taskRows = this.db - .prepare( - `SELECT date(created_at, ?) AS local_day, COUNT(*) AS c - FROM local_tasks - WHERE ${scope.clause} AND date(created_at, ?) BETWEEN ? AND ? - GROUP BY local_day` - ) - .all(mod, ...scope.params, mod, opts.monthStart, opts.monthEnd) as Array<{ local_day: string; c: number }>; - for (const r of taskRows) ensure(r.local_day).taskCount = r.c; - - const events = await this.listCalendarEvents(spaceId, { from: opts.monthStart, to: opts.monthEnd }); - // 複数日イベントは、またがる各日(当月内にクリップ)に 1 件ずつ計上する。 - for (const ev of events) { - const start = ev.date < opts.monthStart ? opts.monthStart : ev.date; - const end = (ev.endDate ?? ev.date) > opts.monthEnd ? opts.monthEnd : (ev.endDate ?? ev.date); - for (const d of enumerateLocalDays(start, end)) ensure(d).eventCount += 1; - } - return { days, events }; + async getSpaceCalendarMonth(spaceId: string, opts: { monthStart: string; monthEnd: string; tzOffsetMin: number; personalOwnerId?: string | null }): Promise<{ days: Record; events: CalendarEvent[] }> { + return calendarRepo.getSpaceCalendarMonth(this.db, spaceId, opts); } - /** - * 日詳細。tasks = その日(ローカル暦日)に作成された local_tasks(最新 job - * 状態付き)、events = その date の calendar_events。ファイル列挙は API 層 - * (fs スキャン)で行う。 - */ - async getSpaceCalendarDay( - spaceId: string, - date: string, - tzOffsetMin: number, - personalOwnerId?: string | null, - ): Promise<{ tasks: CalendarDayTask[]; events: CalendarEvent[] }> { - const mod = this.tzModifier(tzOffsetMin); - const scope = this.calendarTaskScope(spaceId, personalOwnerId, 'lt'); - const taskRows = this.db - .prepare( - `SELECT lt.id AS id, lt.title AS title, lt.state AS state, lt.created_at AS created_at, - (SELECT j.status FROM jobs j - WHERE j.repo = 'local/task-' || lt.id - ORDER BY j.created_at DESC LIMIT 1) AS status - FROM local_tasks lt - WHERE ${scope.clause} AND date(lt.created_at, ?) = ? - ORDER BY lt.created_at ASC, lt.id ASC` - ) - .all(...scope.params, mod, date) as Array<{ - id: number; title: string; state: string; created_at: string; status: string | null; - }>; - const tasks: CalendarDayTask[] = taskRows.map((r) => ({ - id: r.id, - title: r.title, - state: r.state, - status: r.status ?? null, - createdAt: r.created_at, - })); - const events = await this.listCalendarEvents(spaceId, { from: date, to: date }); - return { tasks, events }; + async getSpaceCalendarDay(spaceId: string, date: string, tzOffsetMin: number, personalOwnerId?: string | null): Promise<{ tasks: CalendarDayTask[]; events: CalendarEvent[] }> { + return calendarRepo.getSpaceCalendarDay(this.db, spaceId, date, tzOffsetMin, personalOwnerId); } - /** - * 横断(全スペース)月ビュー。viewer が見られるスペースだけを対象に、 - * 日 × スペースの {taskCount, eventCount} を集計する。可視性は spaces - * 一覧と同じ `buildVisibilityWhere`(listSpaces 経由)で絞るので、 - * 見えないスペースのカウントもイベントも一切返さない。 - * - * クエリ本数を抑えるため、対象 space_id を IN (...) でまとめて 2 本に集約する: - * 1) local_tasks を space_id + ローカル暦日で GROUP BY - * 2) calendar_events を space_id + date で GROUP BY - * さらに当月のイベント本体を 1 本でまとめて取得する(各 CalendarEvent は - * spaceId を持つので UI 側でスペース別に振り分けられる)。 - */ - async getCrossSpaceCalendarMonth( - viewer: Express.User, - opts: { monthStart: string; monthEnd: string; tzOffsetMin: number }, - ): Promise { - // 可視スペースの列挙は spaces 一覧とまったく同じ可視性判定を流用する。 - const visibleSpaces = await this.listSpaces({ viewer }); - const spaces: CrossCalendarSpace[] = visibleSpaces.map((s) => ({ - id: s.id, - name: s.title, - color: s.brandColor ?? spaceColor(s.id), - })); - - const days: Record> = {}; - if (spaces.length === 0) { - return { days, events: [], spaces }; - } - - const ids = spaces.map((s) => s.id); - const placeholders = ids.map(() => '?').join(','); - const mod = this.tzModifier(opts.tzOffsetMin); - const ensure = (date: string, spaceId: string): CalendarDayCounts => { - const byDay = (days[date] ??= {}); - return (byDay[spaceId] ??= { taskCount: 0, eventCount: 0 }); - }; - - const taskRows = this.db - .prepare( - `SELECT space_id AS sid, date(created_at, ?) AS local_day, COUNT(*) AS c - FROM local_tasks - WHERE space_id IN (${placeholders}) AND date(created_at, ?) BETWEEN ? AND ? - GROUP BY sid, local_day` - ) - .all(mod, ...ids, mod, opts.monthStart, opts.monthEnd) as Array<{ - sid: string; local_day: string; c: number; - }>; - for (const r of taskRows) ensure(r.local_day, r.sid).taskCount = r.c; - - // 個人スペースのタスクは space_id が NULL(owner_id で紐付く)ため、上の - // `space_id IN (...)` では拾えず全体カレンダーに反映されなかった。per-space カレンダーの - // calendarTaskScope と同じ owner フォールバックで、`space_id IS NULL AND owner_id=個人スペース - // 所有者` のタスクを個人スペース列に計上する。 - for (const ps of visibleSpaces) { - if (ps.kind !== 'personal' || !ps.ownerId) continue; - const personalRows = this.db - .prepare( - `SELECT date(created_at, ?) AS local_day, COUNT(*) AS c - FROM local_tasks - WHERE space_id IS NULL AND owner_id = ? AND date(created_at, ?) BETWEEN ? AND ? - GROUP BY local_day` - ) - .all(mod, ps.ownerId, mod, opts.monthStart, opts.monthEnd) as Array<{ local_day: string; c: number }>; - for (const r of personalRows) ensure(r.local_day, ps.id).taskCount += r.c; - } - - // 複数日対応: 当月に重なるイベントを 1 本でまとめて取得し、各日(当月内に - // クリップ)に 1 件ずつ計上する。終了日が当月外の単日 end_date=NULL は date 自身。 - const eventRows = this.db - .prepare( - `SELECT * FROM calendar_events - WHERE space_id IN (${placeholders}) - AND COALESCE(end_date, date) >= ? AND date <= ? - ORDER BY date ASC, (time IS NULL) DESC, time ASC, id ASC` - ) - .all(...ids, opts.monthStart, opts.monthEnd) as CalendarEventRow[]; - const events = eventRows.map(rowToCalendarEvent); - for (const ev of events) { - const start = ev.date < opts.monthStart ? opts.monthStart : ev.date; - const end = (ev.endDate ?? ev.date) > opts.monthEnd ? opts.monthEnd : (ev.endDate ?? ev.date); - for (const d of enumerateLocalDays(start, end)) ensure(d, ev.spaceId).eventCount += 1; - } - - return { days, events, spaces }; + async getCrossSpaceCalendarMonth(viewer: Express.User, opts: { monthStart: string; monthEnd: string; tzOffsetMin: number }): Promise { + return calendarRepo.getCrossSpaceCalendarMonth(this.db, viewer, opts); } - // ── AAO Gateway Phase 2a: virtual keys ─────────────────────────────── - // - // The gateway auth middleware reads `findGatewayVirtualKeyByHash` on - // every request, so it MUST stay an indexed point lookup. The partial - // index `idx_gateway_keys_hash_active` covers that path. Admin-side - // methods (list/get/revoke/rotate/delete) are not hot. - - /** - * Insert a new virtual key row. Throws on UNIQUE(key_hash) violation — - * callers must hash the raw key first (via src/gateway/key-format.ts) - * and pass the hash here. The raw key is never accepted by the - * Repository on purpose: there is no path that could log it. - * - * `allowedModels` is JSON-encoded when present and stored as NULL when - * omitted — distinct from `[]` which means "lock to zero models". - */ createGatewayVirtualKey(params: { id?: string; keyHash: string; @@ -5450,51 +955,10 @@ export class Repository { /** Phase 2b: optional requests-per-minute cap. null/undefined = unlimited. */ rateLimitRpm?: number | null; }): GatewayVirtualKey { - const id = params.id ?? randomUUID(); - const allowedJson = - params.allowedModels === null || params.allowedModels === undefined - ? null - : JSON.stringify(params.allowedModels); - const source: GatewayVirtualKeySource = params.source ?? 'admin'; - const createdAt = params.createdAt ?? new Date().toISOString(); - const tokensBudget = normalizeOptionalPositiveInt(params.tokensBudget); - const rateLimitRpm = normalizeOptionalPositiveInt(params.rateLimitRpm); - this.db - .prepare( - `INSERT INTO gateway_virtual_keys - (id, key_hash, key_prefix, team, allowed_models, source, created_at, created_by, tokens_budget, rate_limit_rpm) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - ) - .run( - id, - params.keyHash, - params.keyPrefix, - params.team, - allowedJson, - source, - createdAt, - params.createdBy ?? null, - tokensBudget, - rateLimitRpm, - ); - const row = this.db - .prepare(`SELECT * FROM gateway_virtual_keys WHERE id = ?`) - .get(id) as GatewayVirtualKeyRow; - return rowToGatewayVirtualKey(row); + return gatewayRepo.createGatewayVirtualKey(this.db, params); } - /** - * Phase 2b: partial update of a virtual key's policy fields. The - * bearer hash, team, source, and creation metadata are immutable here - * (use rotate to change the bearer). Each field is opt-in — undefined - * means "leave alone"; explicit null clears the limit (= unlimited). - * - * Returns the refreshed row. Throws when the id doesn't exist (caller - * is expected to 404 before calling). - */ - updateGatewayVirtualKey( - id: string, - patch: { + updateGatewayVirtualKey(id: string, patch: { /** * Phase 3a follow-up: team is now patchable so the config-migration * importer can propagate a YAML-side team rename to the DB. Admin @@ -5506,198 +970,38 @@ export class Repository { tokensBudget?: number | null; rateLimitRpm?: number | null; allowedModels?: string[] | null; - }, - ): GatewayVirtualKey { - const sets: string[] = []; - const args: unknown[] = []; - if (Object.prototype.hasOwnProperty.call(patch, 'team')) { - sets.push('team = ?'); - args.push(patch.team); - } - if (Object.prototype.hasOwnProperty.call(patch, 'tokensBudget')) { - sets.push('tokens_budget = ?'); - args.push(normalizeOptionalPositiveInt(patch.tokensBudget)); - } - if (Object.prototype.hasOwnProperty.call(patch, 'rateLimitRpm')) { - sets.push('rate_limit_rpm = ?'); - args.push(normalizeOptionalPositiveInt(patch.rateLimitRpm)); - } - if (Object.prototype.hasOwnProperty.call(patch, 'allowedModels')) { - sets.push('allowed_models = ?'); - args.push( - patch.allowedModels === null || patch.allowedModels === undefined - ? null - : JSON.stringify(patch.allowedModels), - ); - } - if (sets.length > 0) { - args.push(id); - this.db - .prepare(`UPDATE gateway_virtual_keys SET ${sets.join(', ')} WHERE id = ?`) - .run(...args); - } - const refreshed = this.findGatewayVirtualKeyById(id); - if (!refreshed) { - throw new Error(`updateGatewayVirtualKey: id not found (${id})`); - } - return refreshed; + }): GatewayVirtualKey { + return gatewayRepo.updateGatewayVirtualKey(this.db, id, patch); } - /** - * Auth hot path: look up an active (non-revoked) key by SHA-256 hash. - * The partial index covers this query so the planner uses it directly. - * Returns null on miss; never throws. - */ findGatewayVirtualKeyByHash(keyHash: string): GatewayVirtualKey | null { - const row = this.db - .prepare( - `SELECT * FROM gateway_virtual_keys WHERE key_hash = ? AND revoked_at IS NULL`, - ) - .get(keyHash) as GatewayVirtualKeyRow | undefined; - return row ? rowToGatewayVirtualKey(row) : null; + return gatewayRepo.findGatewayVirtualKeyByHash(this.db, keyHash); } - /** - * Admin lookup by row id. Includes revoked keys (the admin list/detail - * view shows them so an admin can audit a recent revoke). - */ findGatewayVirtualKeyById(id: string): GatewayVirtualKey | null { - const row = this.db - .prepare(`SELECT * FROM gateway_virtual_keys WHERE id = ?`) - .get(id) as GatewayVirtualKeyRow | undefined; - return row ? rowToGatewayVirtualKey(row) : null; + return gatewayRepo.findGatewayVirtualKeyById(this.db, id); } - /** - * Admin list. `activeOnly` filters out revoked rows; `team` narrows by - * team string (exact match). Ordering is `created_at DESC, id DESC` so - * the freshest issuance is first regardless of system clock skew. - */ listGatewayVirtualKeys(opts?: { team?: string; activeOnly?: boolean }): GatewayVirtualKey[] { - const where: string[] = []; - const args: unknown[] = []; - if (opts?.team !== undefined) { - where.push('team = ?'); - args.push(opts.team); - } - if (opts?.activeOnly) { - where.push('revoked_at IS NULL'); - } - const sql = - `SELECT * FROM gateway_virtual_keys` + - (where.length > 0 ? ` WHERE ${where.join(' AND ')}` : '') + - ` ORDER BY created_at DESC, id DESC`; - const rows = this.db.prepare(sql).all(...args) as GatewayVirtualKeyRow[]; - return rows.map(rowToGatewayVirtualKey); + return gatewayRepo.listGatewayVirtualKeys(this.db, opts); } - /** - * Mark a key as revoked. Returns true if the row was active (so the - * caller can return a clean 200) and false if it was already revoked - * or doesn't exist (so the caller can return 404 / 409). Idempotent - * second calls return false. - */ revokeGatewayVirtualKey(id: string, revokedBy: string, at?: string): boolean { - const ts = at ?? new Date().toISOString(); - const info = this.db - .prepare( - `UPDATE gateway_virtual_keys - SET revoked_at = ?, revoked_by = ? - WHERE id = ? AND revoked_at IS NULL`, - ) - .run(ts, revokedBy, id); - return info.changes > 0; + return gatewayRepo.revokeGatewayVirtualKey(this.db, id, revokedBy, at); } - /** - * Hard delete. The admin API guards `source='config-import'` and - * returns 400 before calling this — but the Repository itself doesn't - * enforce that (tests need to be able to clean up). Returns true if a - * row was deleted. - */ - /** - * Hard-delete a virtual key row. - * - * Defense-in-depth: refuses to delete rows with `source='config-import'` - * by throwing. The admin REST API also rejects this case (returning a - * 400 with a human-readable message), but a future internal caller - * could easily forget — and a hard delete of a config-import row - * would simply be replayed on the next gateway boot when - * importConfigKeysToDb re-imports the entry from config.yaml. That - * recreates the row with a different id, which silently breaks any - * audit history that referenced the previous id and is generally - * confusing operator behavior. Force callers to use `revoke` (soft - * delete) or to remove the entry from config.yaml first. - * - * Returns true when a row was deleted, false when the id didn't - * exist. Throws when the row exists but is config-import. - */ deleteGatewayVirtualKey(id: string): boolean { - const row = this.findGatewayVirtualKeyById(id); - if (!row) return false; - if (row.source === 'config-import') { - throw new Error( - `cannot delete config-import virtual key (id=${id}); ` + - "remove the entry from config.yaml's gateway.virtual_keys instead, " + - 'or use revoke for a soft delete', - ); - } - const info = this.db - .prepare(`DELETE FROM gateway_virtual_keys WHERE id = ?`) - .run(id); - return info.changes > 0; + return gatewayRepo.deleteGatewayVirtualKey(this.db, id); } - /** - * Bump `last_used_at` for an active key. Called from the gateway auth - * middleware on successful match. Per-request volume can be high, so - * callers typically dedup with a 30-second in-memory bucket (see - * src/gateway/auth.ts) before touching the DB. Best-effort: failures - * are swallowed by the caller so a temporary write-lock contention - * never blocks auth. - */ touchGatewayVirtualKeyLastUsed(id: string, at?: string): void { - const ts = at ?? new Date().toISOString(); - this.db - .prepare(`UPDATE gateway_virtual_keys SET last_used_at = ? WHERE id = ?`) - .run(ts, id); + return gatewayRepo.touchGatewayVirtualKeyLastUsed(this.db, id, at); } - // ── AAO Gateway Phase 2b: usage tracking ───────────────────────────── - // - // Read path is on the budget enforcement hot loop, so it stays a - // single point lookup over the composite PRIMARY KEY. Write path is - // an UPSERT (`ON CONFLICT … DO UPDATE`) so the gateway can fire-and- - // forget after every chat completion without a pre-read. - - /** - * Point-lookup over the (key_id, period_start) PRIMARY KEY. Returns - * null when there's no row yet (= "no usage in this period"), which - * the caller treats as zero counters. - */ getGatewayKeyUsage(keyId: string, periodStart: string): GatewayKeyUsage | null { - const row = this.db - .prepare( - `SELECT * FROM gateway_key_usage WHERE key_id = ? AND period_start = ?`, - ) - .get(keyId, periodStart) as GatewayKeyUsageRow | undefined; - return row ? rowToGatewayKeyUsage(row) : null; + return gatewayRepo.getGatewayKeyUsage(this.db, keyId, periodStart); } - /** - * UPSERT: bump the per-(key, period) counters by the supplied deltas. - * All three deltas are clamped at zero so a buggy caller can never - * decrement a counter. `last_updated_at` always reflects the call - * time (or the explicit `at` override) so a downstream sweeper can - * tell when activity stopped. - * - * Called from two places on the gateway hot path: - * 1. stream-proxy's finally block (token deltas from upstream usage) - * 2. rate-limiter's 30-second batch flush (request count only) - * - * The second caller passes `tokensIn=0 tokensOut=0` so the UPSERT - * still creates a row even when no token usage was extracted. - */ incrementGatewayKeyUsage(params: { keyId: string; period: string; @@ -5706,217 +1010,35 @@ export class Repository { requests?: number; at?: string; }): void { - const tIn = Math.max(0, Math.floor(params.tokensIn ?? 0)); - const tOut = Math.max(0, Math.floor(params.tokensOut ?? 0)); - const reqs = Math.max(0, Math.floor(params.requests ?? 0)); - const ts = params.at ?? new Date().toISOString(); - this.db - .prepare( - `INSERT INTO gateway_key_usage - (key_id, period_start, tokens_in, tokens_out, requests, last_updated_at) - VALUES (?, ?, ?, ?, ?, ?) - ON CONFLICT (key_id, period_start) DO UPDATE SET - tokens_in = tokens_in + excluded.tokens_in, - tokens_out = tokens_out + excluded.tokens_out, - requests = requests + excluded.requests, - last_updated_at = excluded.last_updated_at`, - ) - .run(params.keyId, params.period, tIn, tOut, reqs, ts); + return gatewayRepo.incrementGatewayKeyUsage(this.db, params); } - /** - * Admin view: history of usage rows for a key, freshest period first. - * Default limit 12 covers a full year of monthly buckets — enough for - * the in-UI bar chart and for `GET /:id/usage` to embed history without - * a follow-up call. - */ listGatewayKeyUsagesByKey(keyId: string, opts?: { limit?: number }): GatewayKeyUsage[] { - const limit = Math.max(1, Math.min(120, Math.floor(opts?.limit ?? 12))); - const rows = this.db - .prepare( - `SELECT * FROM gateway_key_usage - WHERE key_id = ? - ORDER BY period_start DESC - LIMIT ?`, - ) - .all(keyId, limit) as GatewayKeyUsageRow[]; - return rows.map(rowToGatewayKeyUsage); + return gatewayRepo.listGatewayKeyUsagesByKey(this.db, keyId, opts); } - // ── Per-user daily LLM usage (gateway + direct) ────────────────────── - // - // Recorded at the OpenAICompatClient completion boundary for every - // successful chat completion. UPSERT on the (day, user_id, source, - // model, route) grain. Separate lens from gateway_key_usage — never - // summed across the two tables. Spec: - // docs/superpowers/specs/2026-06-11-llm-usage-aggregation-design.md - - /** - * UPSERT: bump per-(day, user, source, model, route) counters. Deltas - * are clamped at zero. `day` defaults to the UTC day of `at` (or now). - * Called once per successful stream completion; `usage`-less completions - * still bump `requests` (tokens 0) so a 0-token request is distinct from - * a failed/aborted one (which is never recorded). - */ incrementLlmUsage(params: LlmUsageIncrement): void { - const tIn = Math.max(0, Math.floor(params.tokensIn ?? 0)); - const tOut = Math.max(0, Math.floor(params.tokensOut ?? 0)); - const reqs = Math.max(0, Math.floor(params.requests ?? 1)); - const ts = params.at ?? new Date().toISOString(); - const day = params.day ?? ts.slice(0, 10); - this.db - .prepare( - `INSERT INTO llm_usage_daily - (day, user_id, source, model, route, tokens_in, tokens_out, requests, last_updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - ON CONFLICT (day, user_id, source, model, route) DO UPDATE SET - tokens_in = tokens_in + excluded.tokens_in, - tokens_out = tokens_out + excluded.tokens_out, - requests = requests + excluded.requests, - last_updated_at = excluded.last_updated_at`, - ) - .run(day, params.userId, params.source, params.model, params.route, tIn, tOut, reqs, ts); + return llmUsageRepo.incrementLlmUsage(this.db, params); } - /** - * Daily time series for the usage dashboard, grouped by (day, user_id, - * source) with model/route collapsed. `userId` filter scopes a non-admin - * to their own rows; omit it for the admin all-users view (callers can - * collapse user_id afterwards). Inclusive `from`/`to` are 'YYYY-MM-DD'. - */ queryLlmUsageDaily(opts: { from: string; to: string; userId?: string }): LlmUsageDailyAgg[] { - const where = ['day >= ?', 'day <= ?']; - const args: unknown[] = [opts.from, opts.to]; - if (opts.userId !== undefined) { - where.push('user_id = ?'); - args.push(opts.userId); - } - const rows = this.db - .prepare( - `SELECT day, user_id, source, - SUM(tokens_in) AS tokens_in, - SUM(tokens_out) AS tokens_out, - SUM(requests) AS requests - FROM llm_usage_daily - WHERE ${where.join(' AND ')} - GROUP BY day, user_id, source - ORDER BY day ASC`, - ) - .all(...args) as Array<{ - day: string; - user_id: string; - source: string; - tokens_in: number; - tokens_out: number; - requests: number; - }>; - return rows.map((r) => ({ - day: r.day, - userId: r.user_id, - source: r.source, - tokensIn: r.tokens_in, - tokensOut: r.tokens_out, - requests: r.requests, - })); + return llmUsageRepo.queryLlmUsageDaily(this.db, opts); } - /** - * v2 write path: UPSERT per-(hour, user, source, model, route) counters. - * `hour` defaults to the current UTC hour 'YYYY-MM-DDTHH'. Same contract as - * incrementLlmUsage (deltas clamped at zero; usage-less completions still - * bump `requests`). Supersedes incrementLlmUsage as the recorder target. - */ incrementLlmUsageHourly(params: LlmUsageHourlyIncrement): void { - const tIn = Math.max(0, Math.floor(params.tokensIn ?? 0)); - const tOut = Math.max(0, Math.floor(params.tokensOut ?? 0)); - const reqs = Math.max(0, Math.floor(params.requests ?? 1)); - const ts = params.at ?? new Date().toISOString(); - const hour = params.hour ?? ts.slice(0, 13); - this.db - .prepare( - `INSERT INTO llm_usage_hourly - (hour, user_id, source, model, route, tokens_in, tokens_out, requests, last_updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - ON CONFLICT (hour, user_id, source, model, route) DO UPDATE SET - tokens_in = tokens_in + excluded.tokens_in, - tokens_out = tokens_out + excluded.tokens_out, - requests = requests + excluded.requests, - last_updated_at = excluded.last_updated_at`, - ) - .run(hour, params.userId, params.source, params.model, params.route, tIn, tOut, reqs, ts); + return llmUsageRepo.incrementLlmUsageHourly(this.db, params); } - /** - * Raw hour-grain rows for the usage dashboard v2, no axis collapsed so the - * API can group by any of source/model/route/user/org and re-bucket into the - * viewer's local timezone. `userId` scopes a non-admin to their own rows. - * Inclusive `fromHour`/`toHour` are 'YYYY-MM-DDTHH' (UTC) — callers widen the - * UTC window by ±1 day before filtering precisely against local days. - */ queryLlmUsageHourly(opts: { fromHour: string; toHour: string; userId?: string; }): LlmUsageHourlyRow[] { - const where = ['hour >= ?', 'hour <= ?']; - const args: unknown[] = [opts.fromHour, opts.toHour]; - if (opts.userId !== undefined) { - where.push('user_id = ?'); - args.push(opts.userId); - } - const rows = this.db - .prepare( - `SELECT hour, user_id, source, model, route, tokens_in, tokens_out, requests - FROM llm_usage_hourly - WHERE ${where.join(' AND ')} - ORDER BY hour ASC`, - ) - .all(...args) as Array<{ - hour: string; - user_id: string; - source: string; - model: string; - route: string; - tokens_in: number; - tokens_out: number; - requests: number; - }>; - return rows.map((r) => ({ - hour: r.hour, - userId: r.user_id, - source: r.source, - model: r.model, - route: r.route, - tokensIn: r.tokens_in, - tokensOut: r.tokens_out, - requests: r.requests, - })); + return llmUsageRepo.queryLlmUsageHourly(this.db, opts); } - /** - * Map every user to a single org label for the usage dashboard's "by org" - * breakdown. Unions Gitea org cache (user_gitea_orgs) and local orgs - * (local_org_members → local_orgs.name); a multi-org user collapses to the - * alphabetically-first org name (MIN) so the breakdown is deterministic. - * Users with no org (and the 'local'/'system' sentinels) are simply absent — - * the API buckets them under 'no-org'. - */ getUsageOrgMap(): Map { - const rows = this.db - .prepare( - `SELECT user_id, MIN(org_name) AS org_name FROM ( - SELECT user_id, org_name FROM user_gitea_orgs - UNION ALL - SELECT m.user_id AS user_id, o.name AS org_name - FROM local_org_members m - JOIN local_orgs o ON o.id = m.org_id - ) - GROUP BY user_id`, - ) - .all() as Array<{ user_id: string; org_name: string }>; - const map = new Map(); - for (const r of rows) map.set(r.user_id, r.org_name); - return map; + return llmUsageRepo.getUsageOrgMap(this.db); } /** Return the underlying Database instance (needed by migrate.ts and session store) */ @@ -5924,240 +1046,40 @@ export class Repository { return this.db; } - // ── Browser Notifications V2: push_subscriptions ──────────────────── - // Spec: docs/superpowers/specs/2026-05-28-browser-notifications-v2-webpush.md - - /** - * Insert or, on endpoint collision, transfer ownership to the new user. - * endpoint is globally UNIQUE: the same browser logging in as a different - * user re-uses the same push service URL, so we move it rather than fail. - */ upsertPushSubscription(input: UpsertPushSubscriptionInput): { id: string } { - const existing = this.db - .prepare('SELECT id FROM push_subscriptions WHERE endpoint = ?') - .get(input.endpoint) as { id: string } | undefined; - if (existing) { - this.db - .prepare( - `UPDATE push_subscriptions - SET user_id = ?, p256dh = ?, auth = ?, user_agent = ?, - vapid_key_id = ?, last_success_at = NULL, - last_failure_at = NULL, failure_count = 0 - WHERE id = ?`, - ) - .run( - input.userId, - input.p256dh, - input.auth, - input.userAgent ?? null, - input.vapidKeyId, - existing.id, - ); - return { id: existing.id }; - } - const id = randomUUID(); - this.db - .prepare( - `INSERT INTO push_subscriptions - (id, user_id, endpoint, p256dh, auth, user_agent, vapid_key_id) - VALUES (?, ?, ?, ?, ?, ?, ?)`, - ) - .run( - id, - input.userId, - input.endpoint, - input.p256dh, - input.auth, - input.userAgent ?? null, - input.vapidKeyId, - ); - return { id }; + return notificationsRepo.upsertPushSubscription(this.db, input); } listPushSubscriptionsForUser(userId: string): PushSubscriptionRecord[] { - const rows = this.db - .prepare( - `SELECT id, user_id, endpoint, p256dh, auth, user_agent, vapid_key_id, - created_at, last_success_at, last_failure_at, failure_count - FROM push_subscriptions - WHERE user_id = ? - ORDER BY created_at ASC`, - ) - .all(userId) as Array<{ - id: string; - user_id: string; - endpoint: string; - p256dh: string; - auth: string; - user_agent: string | null; - vapid_key_id: string; - created_at: string; - last_success_at: string | null; - last_failure_at: string | null; - failure_count: number; - }>; - return rows.map(rowToPushSubscription); + return notificationsRepo.listPushSubscriptionsForUser(this.db, userId); } getPushSubscriptionById(id: string): PushSubscriptionRecord | null { - const row = this.db - .prepare( - `SELECT id, user_id, endpoint, p256dh, auth, user_agent, vapid_key_id, - created_at, last_success_at, last_failure_at, failure_count - FROM push_subscriptions WHERE id = ?`, - ) - .get(id) as Parameters[0] | undefined; - return row ? rowToPushSubscription(row) : null; + return notificationsRepo.getPushSubscriptionById(this.db, id); } deletePushSubscription(id: string): void { - this.db.prepare('DELETE FROM push_subscriptions WHERE id = ?').run(id); + return notificationsRepo.deletePushSubscription(this.db, id); } markPushSubscriptionSuccess(id: string): void { - this.db - .prepare( - `UPDATE push_subscriptions - SET last_success_at = datetime('now'), failure_count = 0 - WHERE id = ?`, - ) - .run(id); + return notificationsRepo.markPushSubscriptionSuccess(this.db, id); } markPushSubscriptionFailure(id: string): void { - this.db - .prepare( - `UPDATE push_subscriptions - SET last_failure_at = datetime('now'), - failure_count = failure_count + 1 - WHERE id = ?`, - ) - .run(id); + return notificationsRepo.markPushSubscriptionFailure(this.db, id); } - // ── Browser Notifications V2: user_notification_prefs ──────────────── - - /** - * Read per-user prefs; create row with defaults if none exists. - * Defaults: enabled=true, all events on, include_details=false, v1_migrated=false. - */ getUserNotificationPrefs(userId: string): NotificationPrefs { - const row = this.db - .prepare( - `SELECT user_id, enabled, event_running, event_succeeded, - event_failed, event_waiting_human, include_details, - v1_migrated, updated_at - FROM user_notification_prefs WHERE user_id = ?`, - ) - .get(userId) as - | { - user_id: string; - enabled: number; - event_running: number; - event_succeeded: number; - event_failed: number; - event_waiting_human: number; - include_details: number; - v1_migrated: number; - updated_at: string; - } - | undefined; - if (!row) { - // Lazily create the default row so subsequent reads/updates are - // a simple UPDATE rather than a conditional insert. - this.db - .prepare( - `INSERT INTO user_notification_prefs (user_id) VALUES (?) - ON CONFLICT(user_id) DO NOTHING`, - ) - .run(userId); - return { - userId, - enabled: true, - events: { running: true, succeeded: true, failed: true, waiting_human: true }, - includeDetails: false, - v1Migrated: false, - updatedAt: new Date().toISOString(), - }; - } - return { - userId: row.user_id, - enabled: row.enabled !== 0, - events: { - running: row.event_running !== 0, - succeeded: row.event_succeeded !== 0, - failed: row.event_failed !== 0, - waiting_human: row.event_waiting_human !== 0, - }, - includeDetails: row.include_details !== 0, - v1Migrated: row.v1_migrated !== 0, - updatedAt: row.updated_at, - }; + return notificationsRepo.getUserNotificationPrefs(this.db, userId); } upsertUserNotificationPrefs(userId: string, update: NotificationPrefsUpdate): void { - // Ensure a row exists first (lazy default creation matches getUserNotificationPrefs). - this.db - .prepare(`INSERT OR IGNORE INTO user_notification_prefs (user_id) VALUES (?)`) - .run(userId); - const sets: string[] = []; - const params: Array = []; - if (update.enabled !== undefined) { - sets.push('enabled = ?'); - params.push(update.enabled ? 1 : 0); - } - if (update.events) { - if (update.events.running !== undefined) { - sets.push('event_running = ?'); - params.push(update.events.running ? 1 : 0); - } - if (update.events.succeeded !== undefined) { - sets.push('event_succeeded = ?'); - params.push(update.events.succeeded ? 1 : 0); - } - if (update.events.failed !== undefined) { - sets.push('event_failed = ?'); - params.push(update.events.failed ? 1 : 0); - } - if (update.events.waiting_human !== undefined) { - sets.push('event_waiting_human = ?'); - params.push(update.events.waiting_human ? 1 : 0); - } - } - if (update.includeDetails !== undefined) { - sets.push('include_details = ?'); - params.push(update.includeDetails ? 1 : 0); - } - if (update.v1Migrated !== undefined) { - sets.push('v1_migrated = ?'); - params.push(update.v1Migrated ? 1 : 0); - } - if (sets.length === 0) return; - sets.push("updated_at = datetime('now')"); - params.push(userId); - this.db - .prepare(`UPDATE user_notification_prefs SET ${sets.join(', ')} WHERE user_id = ?`) - .run(...params); + return notificationsRepo.upsertUserNotificationPrefs(this.db, userId, update); } - /** - * One-time V1 (localStorage) → V2 (server) preferences migration marker. - * Returns true if this call performed the migration mark (caller should - * then apply the localStorage values via upsertUserNotificationPrefs). - * Returns false if already migrated (caller should treat as 409 conflict). - */ markV1MigrationComplete(userId: string): boolean { - this.db - .prepare(`INSERT OR IGNORE INTO user_notification_prefs (user_id) VALUES (?)`) - .run(userId); - const result = this.db - .prepare( - `UPDATE user_notification_prefs - SET v1_migrated = 1, updated_at = datetime('now') - WHERE user_id = ? AND v1_migrated = 0`, - ) - .run(userId); - return result.changes > 0; + return notificationsRepo.markV1MigrationComplete(this.db, userId); } close(): void { @@ -6165,33 +1087,5 @@ export class Repository { } } -function rowToPushSubscription(row: { - id: string; - user_id: string; - endpoint: string; - p256dh: string; - auth: string; - user_agent: string | null; - vapid_key_id: string; - created_at: string; - last_success_at: string | null; - last_failure_at: string | null; - failure_count: number; -}): PushSubscriptionRecord { - return { - id: row.id, - userId: row.user_id, - endpoint: row.endpoint, - p256dh: row.p256dh, - auth: row.auth, - userAgent: row.user_agent, - vapidKeyId: row.vapid_key_id, - createdAt: row.created_at, - lastSuccessAt: row.last_success_at, - lastFailureAt: row.last_failure_at, - failureCount: row.failure_count, - }; -} - export { BrowserSessionRepo } from './browser-session-repo.js'; export type { BrowserSessionProfile, CreateProfileInput, AuditInput } from './browser-session-repo.js'; diff --git a/src/db/schema.sql b/src/db/schema.sql index 9caf2cb..6d0bf09 100644 --- a/src/db/schema.sql +++ b/src/db/schema.sql @@ -215,6 +215,30 @@ CREATE INDEX IF NOT EXISTS idx_tool_requests_piece ON tool_requests (piece_name) -- de-dup lookup (job-scoped pending row) — avoids a full scan per record call CREATE INDEX IF NOT EXISTS idx_tool_requests_dedup ON tool_requests (job_id, movement_name, tool_name, status); +-- エージェント発 Python パッケージ要求(RequestPackage)。実行中に「このライブラリを +-- 使いたい」と申告 → task write 権限者が承認 → そのスペースの overlay に install → +-- ジョブ再開で import 可能に。承認時のインストールは PR2 の installSpacePackages +-- (bwrap 必須 fail-closed / wheels のみ / 固定 index / shadowing 拒否)を再利用する。 +-- 三重経路: schema.sql(fresh) / migrate.ts(upgrade) / repository.initSchema(整合) +CREATE TABLE IF NOT EXISTS package_requests ( + id TEXT PRIMARY KEY, -- randomUUID + task_id TEXT, -- local_tasks 擬似 id(nullable) + job_id TEXT, -- jobs.id(nullable、承認後 resume 対象) + space_id TEXT, -- install 先スペース(承認に必須) + piece_name TEXT, -- 要求元 piece(診断用・nullable) + movement_name TEXT, -- 要求元 movement(UI 表示用・nullable) + spec TEXT NOT NULL, -- 正規化済み pip spec(例: requests==2.32.3) + normalized_name TEXT NOT NULL, -- PEP 503 正規化名(例: requests) + reason TEXT, -- なぜ必要か(エージェント申告) + status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | denied | auto_denied + decided_by TEXT, -- 承認/拒否した user_id + created_at TEXT NOT NULL DEFAULT (datetime('now')), + decided_at TEXT +); +CREATE INDEX IF NOT EXISTS idx_package_requests_task ON package_requests (task_id); +-- de-dup lookup (job-scoped pending row) — avoids a full scan per record call +CREATE INDEX IF NOT EXISTS idx_package_requests_dedup ON package_requests (job_id, normalized_name, status); + -- ワークスペースファイルの来歴(プロヴェナンス)台帳。永続ワークスペースが -- 複数タスク間で共有されるため、各ファイルを「どのタスクが作った / アップロード -- したか」「最後に変更したのは誰か」を追跡する。ファイル本文にはタグを埋めない。 diff --git a/src/engine/agent-loop.ts b/src/engine/agent-loop.ts index 6ccef40..16d3932 100644 --- a/src/engine/agent-loop.ts +++ b/src/engine/agent-loop.ts @@ -49,6 +49,7 @@ import { runLlmIteration } from './agent-loop/llm-iteration.js'; import { buildBrowserWaitResult, buildToolApprovalWaitResult, + buildPackageApprovalWaitResult, buildSubtaskWaitResult, buildCompleteAutoParkResult, } from './agent-loop/pending-states.js'; @@ -415,6 +416,8 @@ export async function executeMovement( } const toolApprovalWait = buildToolApprovalWaitResult(toolCtx, toolsUsed, movement.name); if (toolApprovalWait) return finishMovement(toolApprovalWait); + const packageApprovalWait = buildPackageApprovalWaitResult(toolCtx, toolsUsed, movement.name); + if (packageApprovalWait) return finishMovement(packageApprovalWait); const subtaskWait = buildSubtaskWaitResult(toolCtx, toolsUsed, movement.name); if (subtaskWait) return finishMovement(subtaskWait); regularToolsUsed += dispatch.regularToolsUsedDelta; diff --git a/src/engine/agent-loop/context-control.test.ts b/src/engine/agent-loop/context-control.test.ts index 2fb0899..b019d43 100644 --- a/src/engine/agent-loop/context-control.test.ts +++ b/src/engine/agent-loop/context-control.test.ts @@ -57,3 +57,108 @@ describe('buildContextOverflowResult — same terminal-default policy (Codex #2) expect(res.next).toBe('verify'); }); }); + +// Livelock fix: when the client blocks a request pre-send but the local guard +// re-check finds nothing it can shrink, returning "recovered" resends the +// byte-identical request, which the client blocks again — an infinite spin +// that burns maxIterations at ~10ms per iteration (observed in production: +// 195 consecutive 14ms error calls). A recovery that changed nothing is not +// a recovery; the movement must end via the context-overflow path instead. +describe('handleLLMError — client preflight block (livelock fix)', () => { + const blockedMessage = + 'LLM request blocked before send: estimated prompt size 102,415 tokens exceeds safe limit 102,400 tokens (80% of context 128,000). Narrow the requested content.'; + + it('ends the movement instead of spinning when the guard cannot shrink anything', async () => { + const { handleLLMError } = await import('./context-control.js'); + const res = await handleLLMError(blockedMessage, { + movement: mv(undefined), + messages: [{ role: 'user', content: 'tiny prompt' }], + tools: [], + toolsUsed: ['Grep'], + contextManager: new ContextManager({ limitTokens: 128_000 }), + promptGuardRatio: 0.8, + runIsolatedLlm: async () => 'summary', + }); + expect(res).not.toBeNull(); + expect(res?.next).toBe('ABORT'); + expect(res?.abortCode).toBe('context_overflow'); + }); + + it('still returns null (continue) when the guard actually freed space', async () => { + const { handleLLMError } = await import('./context-control.js'); + const bigContent = 'A'.repeat(21_600); // ~6,170 tokens per read + const readPair = (callId: string): import('../../llm/openai-compat.js').Message[] => ([ + { + role: 'assistant', + tool_calls: [{ + id: callId, + type: 'function', + function: { name: 'Read', arguments: JSON.stringify({ file_path: 'output/big.txt' }) }, + }], + }, + { role: 'tool', tool_call_id: callId, content: bigContent }, + ]); + // Two reads of the same file: ~12,400 tokens total against a safe limit of + // 8,000 — over the limit, but the file-read dedup stage can free one copy. + const messages = [...readPair('c1'), ...readPair('c2')]; + const res = await handleLLMError( + 'LLM request blocked before send: estimated prompt size 12,400 tokens exceeds safe limit 8,000 tokens (80% of context 10,000).', + { + movement: mv(undefined), + messages, + tools: [], + toolsUsed: [], + contextManager: new ContextManager({ limitTokens: 10_000 }), + promptGuardRatio: 0.8, + runIsolatedLlm: async () => 'summary', + }, + ); + expect(res).toBeNull(); + }); +}); + +// Reserve-cap follow-up: on large-context models the guard's re-check derived +// its threshold as `limit - min(ratioReserve, 32k)`, which is LOOSER than the +// client's actual safe limit (e.g. 230,144 vs 209,715 on a 262k model). A +// prompt inside that ~20k band made the guard say "within limits, nothing to +// shrink" even though real recovery (compacting a huge tool result) was +// possible. The recovery re-check must target the safe limit the client +// actually enforced, not a re-derived, capped one. +describe('handleLLMError — recovery honors the client-reported safe limit on large-context models', () => { + it('compacts down to the reported limit and continues instead of giving up', async () => { + const { handleLLMError } = await import('./context-control.js'); + // 262,144-token model: client safe limit 209,715; guard re-derivation + // with the 32k reserve cap would allow up to 230,144. + // One giant compactable tool result puts the prompt at ~215k — inside + // the disagreement band. + const bigToolResult = 'A'.repeat(750_000); // ~214,300 tokens + const messages: import('../../llm/openai-compat.js').Message[] = [ + { + role: 'assistant', + tool_calls: [{ + id: 'g1', + type: 'function', + function: { name: 'Grep', arguments: JSON.stringify({ pattern: 'x' }) }, + }], + }, + { role: 'tool', tool_call_id: 'g1', content: bigToolResult }, + ]; + const res = await handleLLMError( + 'LLM request blocked before send: estimated prompt size 214,500 tokens exceeds safe limit 209,715 tokens (80% of context 262,144).', + { + movement: mv(undefined), + messages, + tools: [], + toolsUsed: [], + contextManager: new ContextManager({ limitTokens: 262_144 }), + promptGuardRatio: 0.8, + runIsolatedLlm: async () => 'summary', + }, + ); + // Recovery succeeded by compaction — continue the loop. + expect(res).toBeNull(); + // The oversized tool result was actually replaced. + const toolMsg = messages.find((m) => m.role === 'tool'); + expect(typeof toolMsg?.content === 'string' && toolMsg.content.length).toBeLessThan(750_000); + }); +}); diff --git a/src/engine/agent-loop/context-control.ts b/src/engine/agent-loop/context-control.ts index 65e7f5f..1c16903 100644 --- a/src/engine/agent-loop/context-control.ts +++ b/src/engine/agent-loop/context-control.ts @@ -172,17 +172,44 @@ export async function handleLLMError( ): Promise { if (errorMessage.startsWith('LLM request blocked before send:')) { const parsedSafeLimit = parsePromptSafeLimitTokens(errorMessage); - const impliedRatio = parsedSafeLimit && ctx.contextManager - ? parsedSafeLimit / ctx.contextManager.getContextLimit() + const limitTokens = ctx.contextManager?.getContextLimit(); + const impliedRatio = parsedSafeLimit && limitTokens + ? parsedSafeLimit / limitTokens : ctx.promptGuardRatio; + // Recovery must target the safe limit the client actually enforced. The + // guard normally re-derives its threshold as `limit - min(ratioReserve, + // reserveCap)`, which on large-context models is LOOSER than the client's + // limit (e.g. 230,144 vs 209,715 on a 262k model) — the guard would then + // see the blocked prompt as "within limits" and shrink nothing. Pinning + // reserveCapTokens to `limit - parsedSafeLimit` makes the re-derived + // threshold equal the client's limit exactly. + const historySummarization = parsedSafeLimit && limitTokens + ? { ...ctx.safetyConfig?.historySummarization, reserveCapTokens: limitTokens - parsedSafeLimit } + : ctx.safetyConfig?.historySummarization; const recoveredGuard = await guardPromptBeforeSend(ctx.messages, ctx.tools, ctx.contextManager, { promptGuardRatio: impliedRatio, - historySummarization: ctx.safetyConfig?.historySummarization, + historySummarization, runIsolatedLlm: ctx.runIsolatedLlm, }); if (recoveredGuard.ok) { - logger.warn(`[agent-loop] movement=${ctx.movement.name} recovered from client prompt preflight block (deduped=${recoveredGuard.deduped} compacted=${recoveredGuard.compacted} summarized=${recoveredGuard.summarized}) estimated=${recoveredGuard.estimatedTokens}`); - return null; + const changedAnything = recoveredGuard.deduped || recoveredGuard.compacted || recoveredGuard.summarized; + if (changedAnything) { + logger.warn(`[agent-loop] movement=${ctx.movement.name} recovered from client prompt preflight block (deduped=${recoveredGuard.deduped} compacted=${recoveredGuard.compacted} summarized=${recoveredGuard.summarized}) estimated=${recoveredGuard.estimatedTokens}`); + return null; + } + // The client blocked the request but the local guard sees the prompt as + // within limits AND changed nothing — the estimators disagree. Resending + // the byte-identical request would be blocked again on every iteration + // (observed in production: 195 consecutive ~14ms failures straight to + // maxIterations). A recovery that changed nothing is not a recovery. + logger.warn(`[agent-loop] movement=${ctx.movement.name} client preflight blocked but local guard found nothing to shrink (estimated=${recoveredGuard.estimatedTokens}) — ending movement instead of resending an identical request`); + return await buildContextOverflowResult( + ctx.movement, + `${errorMessage}\n\nThe local prompt guard found nothing further to shrink; resending the identical request would spin until max iterations.`, + ctx.messages, + ctx.toolsUsed, + ctx.runIsolatedLlm, + ); } return await buildContextOverflowResult( ctx.movement, diff --git a/src/engine/agent-loop/delegate-runner.ts b/src/engine/agent-loop/delegate-runner.ts index 76b972b..5d6a255 100644 --- a/src/engine/agent-loop/delegate-runner.ts +++ b/src/engine/agent-loop/delegate-runner.ts @@ -121,8 +121,10 @@ export async function runDelegateSubAgent( : res.next === 'ASK' ? 'needs_user_input' : 'aborted'; + const resultPreview = (res.output ?? '').slice(0, 2000); + parentCtx.eventLogger?.emit('delegate_complete', { - delegateRunId, parentRunId, description: params.description, depth, next: res.next, status, + delegateRunId, parentRunId, description: params.description, depth, next: res.next, status, resultPreview, }); parentCallbacks?.onDelegateLifecycle?.({ delegateRunId, parentRunId, depth, description: params.description, status, diff --git a/src/engine/agent-loop/pending-states.test.ts b/src/engine/agent-loop/pending-states.test.ts index 765203c..72539a7 100644 --- a/src/engine/agent-loop/pending-states.test.ts +++ b/src/engine/agent-loop/pending-states.test.ts @@ -4,6 +4,7 @@ import type { DispatchOutcome } from './tool-dispatcher.js'; import { buildBrowserWaitResult, buildToolApprovalWaitResult, + buildPackageApprovalWaitResult, buildSubtaskWaitResult, buildCompleteAutoParkResult, } from './pending-states.js'; @@ -37,6 +38,20 @@ describe('buildToolApprovalWaitResult', () => { }); }); +describe('buildPackageApprovalWaitResult', () => { + it('parks for approval and clears the flag when set', () => { + const c = ctx({ pendingPackageApproval: { spec: 'requests==2.32.3' } }); + const r = buildPackageApprovalWaitResult(c, [], 'm1'); + expect(r?.next).toBe('WAITING_HUMAN_PACKAGE_REQUEST'); + expect(r?.waitReason).toBe('package_request'); + expect(String(r?.output)).toContain('requests==2.32.3'); + expect(c.pendingPackageApproval).toBeNull(); // flag consumed + }); + it('returns null when no package approval is pending', () => { + expect(buildPackageApprovalWaitResult(ctx(), [], 'm1')).toBeNull(); + }); +}); + describe('buildSubtaskWaitResult', () => { it('parks as WAIT_SUBTASKS (resume same movement) and clears the flag', () => { const c = ctx({ pendingSubtaskWait: true }); diff --git a/src/engine/agent-loop/pending-states.ts b/src/engine/agent-loop/pending-states.ts index d0fcbf1..00dbc2c 100644 --- a/src/engine/agent-loop/pending-states.ts +++ b/src/engine/agent-loop/pending-states.ts @@ -61,6 +61,31 @@ export function buildToolApprovalWaitResult( }; } +/** + * Package-request mechanism: RequestPackage set a pause flag → end the movement + * as waiting_human so a user can approve/deny the requested Python package + * inline. On approve the wheel is installed into the space overlay and the job + * resumes; the movement re-runs from the start (resumeMovement) with the + * package importable. Clears toolCtx.pendingPackageApproval. Mirrors + * buildToolApprovalWaitResult. + */ +export function buildPackageApprovalWaitResult( + toolCtx: ToolContext, + toolsUsed: string[], + movementName: string, +): MovementResult | null { + if (!toolCtx.pendingPackageApproval) return null; + const pending = toolCtx.pendingPackageApproval; + toolCtx.pendingPackageApproval = null; + logger.info(`[agent-loop] movement=${movementName} waiting_human for package approval: ${pending.spec}`); + return { + next: 'WAITING_HUMAN_PACKAGE_REQUEST', + output: `Python パッケージ "${pending.spec}" のインストール承認を待っています。`, + toolsUsed, + waitReason: 'package_request', + }; +} + /** * WaitSubTask set a park flag → end the movement as WAIT_SUBTASKS so the worker * parks this job (releasing the worker) until the spawned children finish, then diff --git a/src/engine/context/prompt-guard.test.ts b/src/engine/context/prompt-guard.test.ts index e2a1bce..4d2f432 100644 --- a/src/engine/context/prompt-guard.test.ts +++ b/src/engine/context/prompt-guard.test.ts @@ -328,3 +328,24 @@ describe('guardPromptBeforeSend', () => { expect(result.ok).toBe(false); }); }); + +// Livelock fix: the client-side preflight adds a fixed base overhead +// (PROMPT_BASE_OVERHEAD_TOKENS) that this guard historically did not count. +// A prompt landing inside that band passed here ("nothing to do") but was +// blocked at send time forever — the loop burned all iterations at ~10ms per +// spin. The guard must charge the same overhead as the client. +describe('guardPromptBeforeSend — client-preflight estimator parity', () => { + it('treats a prompt inside the last-overhead-band as over the limit instead of passing it through unchanged', async () => { + const cm = new ContextManager({ limitTokens: 1_000 }); + // maxPromptTokens = 1000 - min(1000*0.2, cap) = 800. + // A plain user message of ~750 tokens is under 800 on its own, but over + // once the client's base overhead is charged. Nothing is shrinkable + // (no duplicate reads, no oversized tool results, no summarizer hook), + // so the guard must report ok:false rather than "recovered". + const messages: Message[] = [ + { role: 'user', content: asciiApproxTokens(740) }, + ]; + const res = await guardPromptBeforeSend(messages, NO_TOOLS, cm); + expect(res.ok).toBe(false); + }); +}); diff --git a/src/engine/context/prompt-guard.ts b/src/engine/context/prompt-guard.ts index 24b6dfa..071491a 100644 --- a/src/engine/context/prompt-guard.ts +++ b/src/engine/context/prompt-guard.ts @@ -7,6 +7,7 @@ import { estimateTokensFromText, estimateMessagesTokens, estimateToolsTokens, + PROMPT_BASE_OVERHEAD_TOKENS, } from './token-estimate.js'; import { logger } from '../../logger.js'; @@ -106,10 +107,10 @@ export function parsePromptSafeLimitTokens(errorMessage: string): number | null */ export function compactOversizedToolResults( messages: Message[], - toolTokens: number, + fixedTokens: number, // non-message tokens: tool definitions + request base overhead maxPromptTokens: number, ): { changed: boolean; estimatedTokens: number; omittedCount: number } { - let estimatedTokens = estimateMessagesTokens(messages) + toolTokens; + let estimatedTokens = estimateMessagesTokens(messages) + fixedTokens; let changed = false; let omittedCount = 0; if (estimatedTokens <= maxPromptTokens) return { changed, estimatedTokens, omittedCount }; @@ -166,11 +167,15 @@ export async function guardPromptBeforeSend( const promptGuardRatio = options.promptGuardRatio ?? PROMPT_GUARD_RATIO_DEFAULT; // tools is built once per movement and never mutated, so JSON.stringify it // exactly once instead of recomputing inside every estimate call. - const toolTokens = estimateToolsTokens(tools); + // The fixed part of every estimate is tools + the client's per-request base + // overhead — the guard must count exactly what the client preflight counts + // (see estimateRequestTokens), or prompts in the last-overhead-band pass + // here but are blocked at send time forever. + const fixedTokens = estimateToolsTokens(tools) + PROMPT_BASE_OVERHEAD_TOKENS; if (!contextManager) { return { ok: true, - estimatedTokens: estimateMessagesTokens(messages) + toolTokens, + estimatedTokens: estimateMessagesTokens(messages) + fixedTokens, compacted: false, deduped: false, summarized: false, @@ -180,21 +185,21 @@ export async function guardPromptBeforeSend( const reserveCapTokens = options.historySummarization?.reserveCapTokens ?? RESERVE_CAP_TOKENS_DEFAULT; const maxPromptTokens = computeMaxPromptTokens(limitTokens, promptGuardRatio, reserveCapTokens); - let estimated = estimateMessagesTokens(messages) + toolTokens; + let estimated = estimateMessagesTokens(messages) + fixedTokens; if (estimated <= maxPromptTokens) { return { ok: true, estimatedTokens: estimated, compacted: false, deduped: false, summarized: false }; } const dedup = dedupeFileReads(messages); if (dedup.changed) { - estimated = estimateMessagesTokens(messages) + toolTokens; + estimated = estimateMessagesTokens(messages) + fixedTokens; logger.info(`[prompt-guard] file-read dedup replaced=${dedup.replacedCount} freedChars=${dedup.freedChars} estimated=${estimated}`); } if (estimated <= maxPromptTokens) { return { ok: true, estimatedTokens: estimated, compacted: false, deduped: dedup.changed, summarized: false }; } - const compacted = compactOversizedToolResults(messages, toolTokens, maxPromptTokens); + const compacted = compactOversizedToolResults(messages, fixedTokens, maxPromptTokens); let summarized = false; if (compacted.changed) { const feedback = buildPromptLimitAgentInstruction(compacted.estimatedTokens, maxPromptTokens); @@ -237,7 +242,7 @@ export async function guardPromptBeforeSend( }); if (summary.summarized) { summarized = true; - estimated = estimateMessagesTokens(messages) + toolTokens; + estimated = estimateMessagesTokens(messages) + fixedTokens; logger.info(`[prompt-guard] history summarization complete freedChars=${summary.freedChars} estimated=${estimated}`); if (estimated <= maxPromptTokens) { return { diff --git a/src/engine/context/token-estimate.test.ts b/src/engine/context/token-estimate.test.ts index c534498..a520f7f 100644 --- a/src/engine/context/token-estimate.test.ts +++ b/src/engine/context/token-estimate.test.ts @@ -4,10 +4,12 @@ import { estimateMessagesTokens, estimateMessageTokens, estimatePromptTokens, + estimateRequestTokens, estimateTokensFromChars, estimateTokensFromText, estimateToolsTokens, IMAGE_CONTENT_TOKENS, + PROMPT_BASE_OVERHEAD_TOKENS, UNKNOWN_CHARS_TO_TOKENS_FACTOR, } from './token-estimate.js'; @@ -183,3 +185,24 @@ describe('estimatePromptTokens', () => { expect(estimatePromptTokens([], [])).toBe(1); // only "[]" tools serialization }); }); + +// Livelock fix: the LLM client's send-time preflight charges a fixed base +// overhead per request on top of message + tool content. Local guards must +// count the exact same total, or a prompt landing in the last +// PROMPT_BASE_OVERHEAD_TOKENS below the safe limit passes the guard but is +// blocked by the client forever (burning iterations at ~10ms per spin). +describe('estimateRequestTokens', () => { + it('adds the request base overhead on top of the prompt estimate', () => { + const messages: Message[] = [{ role: 'user', content: 'hello world' }]; + const tools: ToolDef[] = [{ + type: 'function', + function: { name: 'X', description: 'd', parameters: {} }, + }]; + expect(estimateRequestTokens(messages, tools)) + .toBe(estimatePromptTokens(messages, tools) + PROMPT_BASE_OVERHEAD_TOKENS); + }); + + it('pins the overhead to the value the client preflight has always charged', () => { + expect(PROMPT_BASE_OVERHEAD_TOKENS).toBe(128); + }); +}); diff --git a/src/engine/context/token-estimate.ts b/src/engine/context/token-estimate.ts index d9eeffa..14c2519 100644 --- a/src/engine/context/token-estimate.ts +++ b/src/engine/context/token-estimate.ts @@ -89,3 +89,21 @@ export function estimateToolsTokens(tools: ToolDef[]): number { export function estimatePromptTokens(messages: Message[], tools: ToolDef[]): number { return estimateMessagesTokens(messages) + estimateToolsTokens(tools); } + +/** + * Fixed per-request overhead (chat-template scaffolding, request framing) + * charged by the send-time preflight in OpenAICompatClient. Every local + * guard that compares an estimate against the same safe limit MUST include + * this, or prompts landing within this band pass the guard but are blocked + * at send time on every retry — an unrecoverable livelock. + */ +export const PROMPT_BASE_OVERHEAD_TOKENS = 128; + +/** + * What a request actually costs at the client preflight: prompt content + * plus the fixed base overhead. Single source of truth shared by the client + * preflight and the agent-loop prompt guard. + */ +export function estimateRequestTokens(messages: Message[], tools: ToolDef[] = []): number { + return estimatePromptTokens(messages, tools) + PROMPT_BASE_OVERHEAD_TOKENS; +} diff --git a/src/engine/piece-runner.ts b/src/engine/piece-runner.ts index cc6c763..ccbf432 100644 --- a/src/engine/piece-runner.ts +++ b/src/engine/piece-runner.ts @@ -398,6 +398,22 @@ export async function runPiece( category: 'requested' | 'blocked' | 'unknown'; status?: 'pending' | 'approved' | 'denied' | 'auto_denied'; }) => void; + /** Package-request mechanism: record an agent Python package request. + * Bound to task/job/space by the worker only when the feature is enabled + * AND a space is resolvable; undefined => feature unavailable this run. */ + recordPackageRequest?: (req: { + pieceName?: string | null; + movementName?: string | null; + spec: string; + normalizedName: string; + reason?: string | null; + status?: 'pending' | 'approved' | 'denied' | 'auto_denied'; + }) => void; + /** Package-request mechanism: packages already installed in the space + * overlay (name→spec, version-aware). undefined => feature off. */ + installedPackages?: Array<{ name: string; spec: string }>; + /** Package-request mechanism: names declined for this job (deny loop guard). */ + declinedPackages?: string[]; }, ): Promise { const movementHistory: Array<{ name: string; result: MovementResult }> = []; @@ -769,6 +785,22 @@ function prepareMovementContext( category: 'requested' | 'blocked' | 'unknown'; status?: 'pending' | 'approved' | 'denied' | 'auto_denied'; }) => void; + /** Package-request mechanism: record an agent Python package request. + * Bound to task/job/space by the worker only when the feature is enabled + * AND a space is resolvable; undefined => feature unavailable this run. */ + recordPackageRequest?: (req: { + pieceName?: string | null; + movementName?: string | null; + spec: string; + normalizedName: string; + reason?: string | null; + status?: 'pending' | 'approved' | 'denied' | 'auto_denied'; + }) => void; + /** Package-request mechanism: packages already installed in the space + * overlay (name→spec, version-aware). undefined => feature off. */ + installedPackages?: Array<{ name: string; spec: string }>; + /** Package-request mechanism: names declined for this job (deny loop guard). */ + declinedPackages?: string[]; /** File-provenance read IO (workspace-bound). Threaded into ToolContext. */ fileProvenance?: import('./tools/core.js').FileProvenanceIO; /** File-provenance write recorder. Bound to task/job/space by the worker; @@ -835,6 +867,14 @@ function prepareMovementContext( recordToolRequest: options?.recordToolRequest ? (req) => options.recordToolRequest!({ ...req, pieceName: piece.name, movementName: movementDef.name }) : undefined, + // Package-request mechanism: recorder wrapped to inject piece + movement + // (worker binds task/job/space); the installed-set is space-wide (no per- + // movement injection needed). + recordPackageRequest: options?.recordPackageRequest + ? (req) => options.recordPackageRequest!({ ...req, pieceName: piece.name, movementName: movementDef.name }) + : undefined, + installedPackages: options?.installedPackages, + declinedPackages: options?.declinedPackages, toolsConfig, eventLogger: options?.eventLogger, searchFilter: options?.searchFilter, @@ -1193,6 +1233,22 @@ function mapMovementResult( }; } + if (result.next === 'WAITING_HUMAN_PACKAGE_REQUEST') { + logger.info(`[piece-runner] piece=${piece.name} WAITING_HUMAN_PACKAGE_REQUEST movement=${currentMovementName}`); + return { + kind: 'done', + result: { + status: 'waiting_human', + finalOutput: result.output, + movementHistory, + resumeMovement: currentMovementName, + abortReason: null, + contextActions, + waitReason: result.waitReason ?? 'package_request', + }, + }; + } + if (result.next === null) { logger.warn(`[piece-runner] piece=${piece.name} movement=${currentMovementName} returned null next`); return { diff --git a/src/engine/skills.ts b/src/engine/skills.ts index dac2f4c..e7e0658 100644 --- a/src/engine/skills.ts +++ b/src/engine/skills.ts @@ -37,6 +37,17 @@ function parseSkillFile(filePath: string, source: 'system' | 'user', dirPath: st } } +/** + * Scan a skills directory (depth 1: `{dir}/{name}/SKILL.md` dirs + flat + * `{dir}/{name}.md` files) into catalog entries. Entry names come from the + * SKILL.md frontmatter `name`, which can differ from the folder/file name — + * callers that need to locate a skill on disk by its catalog name must use + * the returned entry's `dirPath`/`filePath` instead of reconstructing paths. + */ +export function scanSkillsDir(dir: string, source: 'system' | 'user'): SkillEntry[] { + return scanDir(dir, source); +} + function scanDir(dir: string, source: 'system' | 'user'): SkillEntry[] { if (!existsSync(dir)) return []; const entries = readdirSync(dir); diff --git a/src/engine/tools/core.test.ts b/src/engine/tools/core.test.ts index ce32f83..ca087be 100644 --- a/src/engine/tools/core.test.ts +++ b/src/engine/tools/core.test.ts @@ -1339,3 +1339,51 @@ describe('Read: document formats vs text-only range params', () => { expect(result?.output ?? '').not.toContain('offset/limit'); }); }); + +// Livelock fix follow-up: Grep was the only core output tool without a token +// budget — Read and Bash truncate, Grep returned every matching line raw. A +// broad pattern over a large workspace injects an unbounded blob into the +// conversation in one step, which is exactly what pushes the prompt into the +// preflight danger band. +describe('Grep output budget', () => { + let ws = ''; + + afterEach(() => { + if (ws) { + fs.rmSync(ws, { recursive: true, force: true }); + ws = ''; + } + }); + + it('truncates oversized match lists to the context budget with a notice', async () => { + ws = makeWorkspace(); + fs.mkdirSync(path.join(ws, 'output'), { recursive: true }); + // ~40k matching lines × ~30 chars ≈ 1.2MB — far over the 60k-token + // absolute cap that applies when no contextManager is present. + const lines: string[] = []; + for (let i = 0; i < 40_000; i++) { + lines.push(`needle match line ${i} padding-padding`); + } + fs.writeFileSync(path.join(ws, 'output', 'big.txt'), lines.join('\n'), 'utf-8'); + + const result = await executeCoreTools('Grep', { pattern: 'needle', path: 'output' }, makeContext(ws)); + + expect(result?.isError).toBe(false); + expect(result?.output).toContain('[自動切り詰め]'); + expect(result?.output.length ?? 0).toBeLessThan(1_000_000); + // The head of the match list is still returned. + expect(result?.output).toContain('big.txt:1:'); + }); + + it('returns small match lists untouched', async () => { + ws = makeWorkspace(); + fs.mkdirSync(path.join(ws, 'output'), { recursive: true }); + fs.writeFileSync(path.join(ws, 'output', 'small.txt'), 'one needle here\n', 'utf-8'); + + const result = await executeCoreTools('Grep', { pattern: 'needle', path: 'output' }, makeContext(ws)); + + expect(result?.isError).toBe(false); + expect(result?.output).not.toContain('[自動切り詰め]'); + expect(result?.output).toContain('small.txt:1:'); + }); +}); diff --git a/src/engine/tools/core.ts b/src/engine/tools/core.ts index c217635..822bb7e 100644 --- a/src/engine/tools/core.ts +++ b/src/engine/tools/core.ts @@ -261,6 +261,41 @@ export interface ToolContext { * then resumes the SAME movement. Mirrors pendingToolApproval. */ pendingSubtaskWait?: boolean; + /** + * Package-request mechanism: recorder for agent-declared Python package + * requests (RequestPackage). Bound by the worker (task/job/space) ONLY when + * the feature is enabled AND the run has a resolvable space. Undefined => + * feature unavailable for this run (RequestPackage records nothing and tells + * the agent to proceed). piece-runner wraps it to inject piece + movement. + */ + recordPackageRequest?: (req: { + spec: string; + normalizedName: string; + reason?: string | null; + status?: 'pending' | 'approved' | 'denied' | 'auto_denied'; + }) => void; + /** + * Package-request mechanism: packages already installed in this workspace's + * overlay (name→canonical spec, from the space desired-state at job start). + * Version-aware: a pinned request (`name==version`) is only "already + * installed" on an exact spec match; a bare-name request matches any version. + * Lets RequestPackage short-circuit (and after a resume, recognise the + * just-approved one). Undefined when the feature is off. + */ + installedPackages?: Array<{ name: string; spec: string }>; + /** + * Package-request mechanism: PEP 503 normalized names already declined + * (denied / auto_denied) for THIS job. RequestPackage short-circuits a + * re-request for one of these instead of re-parking — otherwise a deny would + * loop (deny → movement re-runs → re-request → park). Undefined => none. + */ + declinedPackages?: string[]; + /** + * Set by RequestPackage to signal the movement loop to pause (waiting_human) + * for inline approval of the named package. Read + reset by agent-loop. + * Mirrors pendingToolApproval. + */ + pendingPackageApproval?: { spec: string } | null; /** Per-task option: when true, MCP tools are not loaded/dispatched. */ mcpDisabled?: boolean; /** Per-task option: when true, skill index is not injected into the system prompt. */ @@ -920,8 +955,8 @@ export function checkBlockedInstallPatterns(command: string): void { throw new Error( `Package installation is not available in the sandbox. ` + `These are preinstalled and importable: ${PREINSTALLED_HINT}. ` + - `If you need another package, ask an admin to add it to ` + - `runtime/python-requirements.txt and rebuild — do not retry the install.` + `If you need another package, request it with RequestPackage({ name: "", reason: "..." }) ` + + `— an approver installs it into this workspace and the run resumes. Do not retry the install here.` ); } } @@ -1876,7 +1911,21 @@ function executeGrep(input: Record, ctx: ToolContext): ToolResu if (results.length === 0) { return { output: '(no matches)', isError: false }; } - return { output: results.join('\n'), isError: false }; + // Read と Bash は予算切り詰めがあるのに Grep だけ無制限だった。緩いパターン + // 1回で数十万文字が会話に流れ込み、プロンプトを preflight 上限際まで押し込む + // 起爆剤になっていたので、同じトークン予算で切り詰める。 + const budgetTokens = getToolOutputBudgetTokens(ctx); + const raw = results.join('\n'); + const { text, truncated } = truncateToBudget(raw, budgetTokens, { + sourceLabel: `Grep 結果 (pattern: ${pattern})`, + totalLines: results.length, + continuationHint: + 'マッチが多すぎます。pattern をより具体的にする、glob でファイルを絞る、path でディレクトリを絞る、のいずれかで検索範囲を狭めてください', + }); + if (truncated) { + logger.info(`[grep] truncated matches=${results.length} original_chars=${raw.length} budget_tokens=${budgetTokens}`); + } + return { output: text, isError: false }; } // コアツールを実行 (Read, Write, Edit, Bash, Glob, Grep) diff --git a/src/engine/tools/image.ts b/src/engine/tools/image.ts index de441c7..b640cf3 100644 --- a/src/engine/tools/image.ts +++ b/src/engine/tools/image.ts @@ -275,6 +275,8 @@ export async function callVisionModel( const toolsConfig = ctx.toolsConfig ?? {}; const visionModel = toolsConfig.visionModel ?? 'qwen2-vl:8b-instruct'; + // Neutral placeholder — set tools.vision_base_url in config.yaml for a real endpoint. + // (Workers usually override this with their own endpoint; see worker.ts) const visionBaseUrl = toolsConfig.visionBaseUrl ?? 'http://10.0.0.10:11434/v1'; const visionTimeout = (toolsConfig.visionTimeout ?? 60) * 1000; const visionMaxTokens = toolsConfig.visionMaxTokens ?? 1024; diff --git a/src/engine/tools/index.ts b/src/engine/tools/index.ts index 5864b6d..7319159 100644 --- a/src/engine/tools/index.ts +++ b/src/engine/tools/index.ts @@ -245,6 +245,15 @@ async function getToolRequestModule(): Promise { return _toolRequestModule; } +let _packageRequestModule: ToolModule | null | undefined; +async function getPackageRequestModule(): Promise { + if (_packageRequestModule === undefined) { + _packageRequestModule = await tryLoadModule('./package-request.js'); + if (_packageRequestModule) logger.debug('[tools/index] package-request module loaded'); + } + return _packageRequestModule; +} + /** * Snapshot of all known static tool names, refreshed every getToolDefs() call * (which runs at movement start, before any tool executes). Injected into @@ -418,6 +427,9 @@ export async function getToolDefs( const toolRequestMod = await getToolRequestModule(); if (toolRequestMod) Object.assign(allDefs, toolRequestMod.TOOL_DEFS); + const packageRequestMod = await getPackageRequestModule(); + if (packageRequestMod) Object.assign(allDefs, packageRequestMod.TOOL_DEFS); + // Refresh the known-tool snapshot for RequestTool classification. // // CONCURRENCY INVARIANT (must hold): this is taken HERE — after every static @@ -444,7 +456,8 @@ export async function getToolDefs( // - ReadAppDoc / ListAppDocs / GetMyOrchestratorState: Help アシスタント用 (#help piece) // ただし他の piece からも参照できるようにメタ扱い // - RequestTool: 足りないツールの要求を記録(タスク詳細/ピース集計で可視化) - const META_TOOLS = ['ReadToolDoc', 'CreateChecklist', 'CheckItem', 'GetChecklist', 'MissionUpdate', 'SearchTaskConversation', 'ReadTaskConversation', 'SearchWorkspaceTasks', 'GetFileProvenance', 'ListWorkspaceFiles', 'ListUserAssets', 'RunUserScript', 'UpdateUserMemory', 'ReadUserMemory', 'ReadUserAgents', 'UpdateUserAgents', 'WriteUserScript', 'Brainstorm', 'ReadAppDoc', 'ListAppDocs', 'GetMyOrchestratorState', 'ReadSkill', 'ListSkills', 'InstallSkill', 'RequestTool']; + // - RequestPackage: 足りない Python パッケージ(wheel)を申請(承認でスペース overlay へ install) + const META_TOOLS = ['ReadToolDoc', 'CreateChecklist', 'CheckItem', 'GetChecklist', 'MissionUpdate', 'SearchTaskConversation', 'ReadTaskConversation', 'SearchWorkspaceTasks', 'GetFileProvenance', 'ListWorkspaceFiles', 'ListUserAssets', 'RunUserScript', 'UpdateUserMemory', 'ReadUserMemory', 'ReadUserAgents', 'UpdateUserAgents', 'WriteUserScript', 'Brainstorm', 'ReadAppDoc', 'ListAppDocs', 'GetMyOrchestratorState', 'ReadSkill', 'ListSkills', 'InstallSkill', 'RequestTool', 'RequestPackage']; const effectiveAllowed = [...allowedTools]; for (const meta of META_TOOLS) { if (!effectiveAllowed.includes(meta) && meta in allDefs) { @@ -674,6 +687,14 @@ async function executeToolInner( if (toolRequestResult !== null) return toolRequestResult; } + // RequestPackage: 足りない Python パッケージ(wheel)を申請。承認でスペース + // overlay へ install され、ジョブ再開で import 可能になる。 + const packageRequestMod = await getPackageRequestModule(); + if (packageRequestMod) { + const packageRequestResult = await packageRequestMod.executeTool(name, input, ctx); + if (packageRequestResult !== null) return packageRequestResult; + } + // user-folder ツール (ListUserAssets / RunUserScript) const userFolderMod = await getUserFolderModule(); if (userFolderMod) { diff --git a/src/engine/tools/package-request.test.ts b/src/engine/tools/package-request.test.ts new file mode 100644 index 0000000..a0aed29 --- /dev/null +++ b/src/engine/tools/package-request.test.ts @@ -0,0 +1,93 @@ +import { describe, it, expect, vi } from 'vitest'; +import type { ToolContext } from './core.js'; +import { executeTool } from './package-request.js'; + +const ctx = (over: Partial = {}): ToolContext => + ({ workspacePath: '/ws', ...over } as ToolContext); + +describe('RequestPackage tool', () => { + it('ignores non-RequestPackage names (returns null)', async () => { + expect(await executeTool('Bash', { name: 'x', reason: 'y' }, ctx())).toBeNull(); + }); + + it('requires name and reason', async () => { + expect((await executeTool('RequestPackage', { reason: 'r' }, ctx()))?.isError).toBe(true); + expect((await executeTool('RequestPackage', { name: 'requests' }, ctx()))?.isError).toBe(true); + }); + + it('rejects an invalid / shadowing spec with a hard error (records nothing)', async () => { + const rec = vi.fn(); + // "os" shadows the stdlib → assertNotShadowing throws. + const r = await executeTool('RequestPackage', { name: 'os', reason: 'need it' }, ctx({ recordPackageRequest: rec, toolApprovalInteractive: true })); + expect(r?.isError).toBe(true); + expect(rec).not.toHaveBeenCalled(); + // a range operator is also rejected. + const r2 = await executeTool('RequestPackage', { name: 'requests>=2', reason: 'x' }, ctx({ recordPackageRequest: rec, toolApprovalInteractive: true })); + expect(r2?.isError).toBe(true); + }); + + it('reports feature unavailable when no recorder is bound (records nothing)', async () => { + const r = await executeTool('RequestPackage', { name: 'requests', reason: 'http' }, ctx()); + expect(r?.isError).toBe(false); + expect(String(r?.output)).toContain('利用できません'); + }); + + it('short-circuits an already-installed package (exact spec match)', async () => { + const rec = vi.fn(); + const c = ctx({ recordPackageRequest: rec, installedPackages: [{ name: 'requests', spec: 'requests==2.32.3' }], toolApprovalInteractive: true }); + const r = await executeTool('RequestPackage', { name: 'requests==2.32.3', reason: 'http' }, c); + expect(r?.isError).toBe(false); + expect(String(r?.output)).toContain('インストール済み'); + expect(rec).not.toHaveBeenCalled(); + expect(c.pendingPackageApproval).toBeUndefined(); // no park + }); + + it('short-circuits a bare-name request when any version is installed', async () => { + const rec = vi.fn(); + const c = ctx({ recordPackageRequest: rec, installedPackages: [{ name: 'requests', spec: 'requests==2.32.3' }], toolApprovalInteractive: true }); + const r = await executeTool('RequestPackage', { name: 'requests', reason: 'http' }, c); + expect(String(r?.output)).toContain('インストール済み'); + expect(c.pendingPackageApproval).toBeUndefined(); + }); + + it('does NOT park a pinned request for a different version than the one installed', async () => { + const rec = vi.fn(); + const c = ctx({ recordPackageRequest: rec, installedPackages: [{ name: 'httpx', spec: 'httpx==0.27.0' }], toolApprovalInteractive: true }); + const r = await executeTool('RequestPackage', { name: 'httpx==0.28.0', reason: 'need newer' }, c); + expect(r?.isError).toBe(false); + expect(String(r?.output)).toContain('固定'); + expect(rec).not.toHaveBeenCalled(); + expect(c.pendingPackageApproval).toBeUndefined(); // no dead-end park + }); + + it('does NOT re-park a package already declined for this job (deny loop guard)', async () => { + const rec = vi.fn(); + const c = ctx({ recordPackageRequest: rec, declinedPackages: ['httpx'], toolApprovalInteractive: true }); + const r = await executeTool('RequestPackage', { name: 'httpx==0.28.0', reason: 'retry' }, c); + expect(r?.isError).toBe(false); + expect(String(r?.output)).toContain('却下'); + expect(rec).not.toHaveBeenCalled(); + expect(c.pendingPackageApproval).toBeUndefined(); + }); + + it('parks for approval on an interactive run (records pending)', async () => { + const rec = vi.fn(); + const c = ctx({ recordPackageRequest: rec, toolApprovalInteractive: true }); + const r = await executeTool('RequestPackage', { name: 'Requests==2.32.3', reason: 'http calls' }, c); + expect(r?.isError).toBe(false); + expect(c.pendingPackageApproval).toEqual({ spec: 'requests==2.32.3' }); + expect(rec).toHaveBeenCalledWith( + expect.objectContaining({ spec: 'requests==2.32.3', normalizedName: 'requests', status: 'pending', reason: 'http calls' }), + ); + }); + + it('records auto_denied and does NOT park on a non-interactive run', async () => { + const rec = vi.fn(); + const c = ctx({ recordPackageRequest: rec, toolApprovalInteractive: false }); + // httpx is neither stdlib nor preinstalled, so it passes the shadowing guard. + const r = await executeTool('RequestPackage', { name: 'httpx', reason: 'async http' }, c); + expect(r?.isError).toBe(false); + expect(c.pendingPackageApproval).toBeUndefined(); + expect(rec).toHaveBeenCalledWith(expect.objectContaining({ normalizedName: 'httpx', status: 'auto_denied' })); + }); +}); diff --git a/src/engine/tools/package-request.ts b/src/engine/tools/package-request.ts new file mode 100644 index 0000000..18666e0 --- /dev/null +++ b/src/engine/tools/package-request.ts @@ -0,0 +1,146 @@ +/** + * RequestPackage — META_TOOL for the agent-initiated Python package request. + * + * When an agent needs a Python wheel that is neither preinstalled nor already + * in the workspace overlay, it calls RequestPackage({ name, reason }). The + * request is recorded so it surfaces in the task detail; when the run can reach + * a user (interactive) the movement PARKS for approval. A task-write approver + * (owner / admin / space editor) then approves — which installs the wheel into + * THIS workspace's per-space overlay (reusing PR2's installSpacePackages: + * bwrap-isolated, wheels-only, fixed index, stdlib/preinstalled shadowing + * guards) and resumes the parked job so the movement re-runs with the package + * importable. The agent never installs anything itself. + * + * Gating (all runtime, so the tool def stays unconditional — see the + * concurrency invariant in tools/index.ts): + * - `ctx.recordPackageRequest` unset → feature unavailable for this run + * (disabled globally, or no resolvable space) → record nothing, proceed. + * - name already in `ctx.installedPackages` → already installed, just import. + * - `ctx.toolApprovalInteractive` → park (pendingPackageApproval). + * - otherwise (subtask / headless) → record auto_denied, proceed without. + * + * The spec is validated (parseAndValidateSpec + assertNotShadowing) HERE and + * re-validated at approval time — the DB stores the canonical spec string. + */ + +import { ToolDef } from '../../llm/openai-compat.js'; +import type { ToolContext, ToolResult } from './core.js'; +import { parseAndValidateSpec, assertNotShadowing } from '../python-packages.js'; + +const REQUEST_PACKAGE_DEF: ToolDef = { + type: 'function', + function: { + name: 'RequestPackage', + description: + 'preinstalled に無い Python ライブラリ(wheel)が必要なとき name(例: requests または requests==2.32.3)と理由(reason)を渡して申請する。ユーザーが応答できる実行では承認を求め、承認されればこのワークスペースに入り import できる。詳細は ReadToolDoc({ name: "RequestPackage" })。', + parameters: { + type: 'object', + properties: { + name: { type: 'string', description: 'パッケージ名。厳密固定は "name==version"(例: pandas==2.2.2)。バージョン省略可。' }, + reason: { type: 'string', description: 'なぜそのパッケージが必要か(具体的に)。' }, + }, + required: ['name', 'reason'], + }, + }, +}; + +export const TOOL_DEFS: Record = { + RequestPackage: REQUEST_PACKAGE_DEF, +}; + +export async function executeTool( + name: string, + input: Record, + ctx: ToolContext, +): Promise { + if (name !== 'RequestPackage') return null; + + const pkgName = typeof input['name'] === 'string' ? input['name'].trim() : ''; + const reason = typeof input['reason'] === 'string' ? input['reason'].trim() : ''; + if (!pkgName) return { output: 'RequestPackage: name は必須です。', isError: true }; + if (!reason) return { output: 'RequestPackage: reason(なぜ必要か)は必須です。', isError: true }; + + // Validate the spec (grammar + shadowing) up front. A HARD ERROR so the agent + // does not mistake a rejected spec for a pending approval. + let parsed; + try { + parsed = parseAndValidateSpec(pkgName); + assertNotShadowing(parsed.normalizedName); + } catch (e) { + return { + output: + `"${pkgName}" は申請できません: ${(e as Error).message}。` + + ` 厳密固定 "name==version" のみ・標準ライブラリや同梱パッケージと同名は不可です。`, + isError: true, + }; + } + + // Feature unavailable for this run (disabled globally, or no resolvable space). + // recordPackageRequest is bound by the worker only when both hold. `?.` would + // be a silent no-op, so gate explicitly and DON'T claim it was recorded. + if (!ctx.recordPackageRequest) { + return { + output: + `Python パッケージの追加はこの実行では利用できません。` + + ` 標準ライブラリや同梱パッケージで代替するか、達成できない場合は complete({status:"needs_user_input", missing_info:"..."}) でユーザーに依頼してください。`, + isError: false, + }; + } + + // Already declined for THIS job → don't re-park (deny loop guard). The user + // already said no; proceed without it. + if (ctx.declinedPackages?.includes(parsed.normalizedName)) { + return { + output: + `"${parsed.normalizedName}" の追加は既にこのタスクで却下されています。再申請せず、このパッケージ無しで進めてください(達成できない場合は complete({status:"needs_user_input"}))。`, + isError: false, + }; + } + + // Already installed in this workspace overlay → nothing to approve. + // Version-aware: a pinned request needs an exact spec match; a bare-name + // request is satisfied by any installed version. A pinned request for a + // DIFFERENT version than the one already pinned cannot be satisfied (only one + // version per package), so short-circuit WITHOUT parking (the approve path + // couldn't install it anyway) and tell the agent to use the existing one. + const sameName = (ctx.installedPackages ?? []).filter((p) => p.name === parsed.normalizedName); + if (sameName.length > 0) { + const requestIsPinned = parsed.spec.includes('=='); + const exact = sameName.some((p) => p.spec === parsed.spec); + if (exact || !requestIsPinned) { + return { + output: `"${parsed.normalizedName}" は既にこのワークスペースにインストール済みです(${sameName[0].spec})。そのまま import して使ってください。`, + isError: false, + }; + } + return { + output: + `"${parsed.normalizedName}" は既に ${sameName[0].spec} でこのワークスペースに固定されています。` + + ` 1 パッケージにつき 1 バージョンのみです。別バージョンが必要なら管理者が 設定 → Python で入れ替える必要があります。今は既存版で進めてください。`, + isError: false, + }; + } + + // Interactive run: pause for inline approval. On approve the wheel is + // installed into the space overlay and the job resumes. + if (ctx.toolApprovalInteractive) { + ctx.recordPackageRequest({ spec: parsed.spec, normalizedName: parsed.normalizedName, reason, status: 'pending' }); + ctx.pendingPackageApproval = { spec: parsed.spec }; + return { + output: + `"${parsed.spec}" のインストール承認を待っています。` + + ` 承認されればこのワークスペースに追加され import して続行でき、拒否されればそのパッケージ無しで進めることになります。`, + isError: false, + }; + } + + // Non-interactive (subtask / headless): record so the user finds it later, + // and proceed without the package. + ctx.recordPackageRequest({ spec: parsed.spec, normalizedName: parsed.normalizedName, reason, status: 'auto_denied' }); + return { + output: + `"${parsed.spec}" のインストール要求を記録しました(この実行では自動で承認できません)。` + + ` このパッケージ無しで進めるか、達成できない場合は complete({status:"needs_user_input", missing_info:"..."}) でユーザーに依頼してください。`, + isError: false, + }; +} diff --git a/src/engine/tools/skills.materialize-swap.test.ts b/src/engine/tools/skills.materialize-swap.test.ts new file mode 100644 index 0000000..9a371f6 --- /dev/null +++ b/src/engine/tools/skills.materialize-swap.test.ts @@ -0,0 +1,79 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { tmpdir } from 'os'; +import { describe, expect, it, afterEach, vi } from 'vitest'; +import { SkillCatalog } from '../skills.js'; +import { executeSkillTool } from './skills.js'; +import type { ToolContext } from './core.js'; + +// Failure injection for the swap step: renameSync throws once when the +// destination matches, everything else passes through to the real fs. +const swapState = vi.hoisted(() => ({ failRenameToSuffix: null as string | null })); + +vi.mock('fs', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + renameSync: (src: fs.PathLike, dst: fs.PathLike) => { + if (swapState.failRenameToSuffix && String(dst).endsWith(swapState.failRenameToSuffix)) { + swapState.failRenameToSuffix = null; + throw new Error('simulated EIO on rename'); + } + return actual.renameSync(src, dst); + }, + }; +}); + +function makeTempDir(): string { + return fs.mkdtempSync(path.join(tmpdir(), 'skill-swap-test-')); +} + +function writeSkill(dir: string, filename: string, content: string): void { + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(path.join(dir, filename), content, 'utf-8'); +} + +const DIR_SKILL = `--- +name: tdd +description: TDD workflow +--- + +## Steps +`; + +describe('materializeSkill swap failure', () => { + const dirs: string[] = []; + afterEach(() => { + swapState.failRenameToSuffix = null; + for (const d of dirs) fs.rmSync(d, { recursive: true, force: true }); + dirs.length = 0; + }); + + it('restores the previous copy when the swap rename fails', () => { + const systemDir = makeTempDir(); + const userRoot = makeTempDir(); + const workspace = makeTempDir(); + dirs.push(systemDir, userRoot, workspace); + + const skillDir = path.join(systemDir, 'tdd'); + writeSkill(skillDir, 'SKILL.md', DIR_SKILL); + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho v1\n'); + + const catalog = new SkillCatalog(systemDir, userRoot); + const ctx = { workspacePath: workspace, editAllowed: false, skillCatalog: catalog, userId: 'user1' } as ToolContext; + + const first = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(first!.isError).toBe(false); + + // Source updated; the tmp→dest swap will fail once (e.g. transient FS error). + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho v2\n'); + swapState.failRenameToSuffix = `${path.sep}skills${path.sep}tdd`; + + const second = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(second!.isError).toBe(false); + expect(second!.output).toContain('コピーできませんでした'); + // The previous copy must be back in place, not lost. + const copied = fs.readFileSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'run.sh'), 'utf-8'); + expect(copied).toContain('v1'); + }); +}); diff --git a/src/engine/tools/skills.test.ts b/src/engine/tools/skills.test.ts index 31c7112..6dc1096 100644 --- a/src/engine/tools/skills.test.ts +++ b/src/engine/tools/skills.test.ts @@ -90,6 +90,210 @@ describe('ReadSkill tool', () => { expect(fs.existsSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'run.sh'))).toBe(true); }); + it('re-materializes when the source skill changed since the copy', () => { + const systemDir = makeTempDir(); + const userRoot = makeTempDir(); + const workspace = makeTempDir(); + dirs.push(systemDir, userRoot, workspace); + + const skillDir = path.join(systemDir, 'tdd'); + writeSkill(skillDir, 'SKILL.md', DIR_SKILL); + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho v1\n'); + writeSkill(path.join(skillDir, 'scripts'), 'removed-later.sh', '#!/bin/sh\necho old\n'); + + const catalog = new SkillCatalog(systemDir, userRoot); + const ctx = { workspacePath: workspace, editAllowed: false, skillCatalog: catalog, userId: 'user1' } as ToolContext; + + const first = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(first!.isError).toBe(false); + + // Another task updates the skill in the store: one file rewritten, one removed. + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho v2 updated\n'); + fs.rmSync(path.join(skillDir, 'scripts', 'removed-later.sh')); + + const second = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(second!.isError).toBe(false); + const copied = fs.readFileSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'run.sh'), 'utf-8'); + expect(copied).toContain('v2 updated'); + // Files deleted at the source must not linger in the refreshed copy. + expect(fs.existsSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'removed-later.sh'))).toBe(false); + }); + + it('keeps the existing copy (including local edits) when the source is unchanged', () => { + const systemDir = makeTempDir(); + const userRoot = makeTempDir(); + const workspace = makeTempDir(); + dirs.push(systemDir, userRoot, workspace); + + const skillDir = path.join(systemDir, 'tdd'); + writeSkill(skillDir, 'SKILL.md', DIR_SKILL); + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho hi\n'); + + const catalog = new SkillCatalog(systemDir, userRoot); + const ctx = { workspacePath: workspace, editAllowed: false, skillCatalog: catalog, userId: 'user1' } as ToolContext; + + executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + // The agent tweaks its workspace copy; an unchanged source must not clobber it. + fs.writeFileSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'run.sh'), '#!/bin/sh\necho local edit\n'); + + const second = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(second!.isError).toBe(false); + const copied = fs.readFileSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'run.sh'), 'utf-8'); + expect(copied).toContain('local edit'); + }); + + it('keeps the copy when only source mtimes changed (content identical)', () => { + const systemDir = makeTempDir(); + const userRoot = makeTempDir(); + const workspace = makeTempDir(); + dirs.push(systemDir, userRoot, workspace); + + const skillDir = path.join(systemDir, 'tdd'); + writeSkill(skillDir, 'SKILL.md', DIR_SKILL); + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho hi\n'); + + const catalog = new SkillCatalog(systemDir, userRoot); + const ctx = { workspacePath: workspace, editAllowed: false, skillCatalog: catalog, userId: 'user1' } as ToolContext; + + executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + fs.writeFileSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'run.sh'), '#!/bin/sh\necho local edit\n'); + + // e.g. the skill store was re-synced: identical bytes, new timestamps. + const later = new Date(Date.now() + 60_000); + fs.utimesSync(path.join(skillDir, 'scripts', 'run.sh'), later, later); + fs.utimesSync(path.join(skillDir, 'SKILL.md'), later, later); + + const second = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(second!.isError).toBe(false); + const copied = fs.readFileSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'run.sh'), 'utf-8'); + expect(copied).toContain('local edit'); + }); + + it('rejects an over-cap skill with the copy-limit note before touching the workspace', () => { + const systemDir = makeTempDir(); + const userRoot = makeTempDir(); + const workspace = makeTempDir(); + dirs.push(systemDir, userRoot, workspace); + + const skillDir = path.join(systemDir, 'tdd'); + writeSkill(skillDir, 'SKILL.md', DIR_SKILL); + // Sparse 51MB file: over the 50MB cap without hitting the disk for real. + writeSkill(path.join(skillDir, 'scripts'), 'big.bin', ''); + fs.truncateSync(path.join(skillDir, 'scripts', 'big.bin'), 51 * 1024 * 1024); + + const catalog = new SkillCatalog(systemDir, userRoot); + const ctx = { workspacePath: workspace, editAllowed: false, skillCatalog: catalog, userId: 'user1' } as ToolContext; + + const result = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(result!.isError).toBe(false); + expect(result!.output).toContain('50MB'); + expect(fs.existsSync(path.join(workspace, 'skills', 'tdd'))).toBe(false); + }); + + it('keeps the previous copy intact when refreshing fails mid-copy', () => { + const systemDir = makeTempDir(); + const userRoot = makeTempDir(); + const workspace = makeTempDir(); + dirs.push(systemDir, userRoot, workspace); + + const skillDir = path.join(systemDir, 'tdd'); + writeSkill(skillDir, 'SKILL.md', DIR_SKILL); + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho v1\n'); + + const catalog = new SkillCatalog(systemDir, userRoot); + const ctx = { workspacePath: workspace, editAllowed: false, skillCatalog: catalog, userId: 'user1' } as ToolContext; + + executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + + // Source changes, but one file becomes unreadable → the copy itself fails. + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho v2\n'); + fs.chmodSync(path.join(skillDir, 'scripts', 'run.sh'), 0o000); + try { + const second = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(second!.isError).toBe(false); + expect(second!.output).toContain('コピーできませんでした'); + // The previously materialized copy must survive a failed refresh. + const copied = fs.readFileSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'run.sh'), 'utf-8'); + expect(copied).toContain('v1'); + } finally { + fs.chmodSync(path.join(skillDir, 'scripts', 'run.sh'), 0o644); + } + }); + + it('does not clobber a skill-owned .skill-src-signature file', () => { + const systemDir = makeTempDir(); + const userRoot = makeTempDir(); + const workspace = makeTempDir(); + dirs.push(systemDir, userRoot, workspace); + + const skillDir = path.join(systemDir, 'tdd'); + writeSkill(skillDir, 'SKILL.md', DIR_SKILL); + writeSkill(skillDir, '.skill-src-signature', 'skill-owned data\n'); + + const catalog = new SkillCatalog(systemDir, userRoot); + const ctx = { workspacePath: workspace, editAllowed: false, skillCatalog: catalog, userId: 'user1' } as ToolContext; + + const result = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(result!.isError).toBe(false); + const copied = fs.readFileSync(path.join(workspace, 'skills', 'tdd', '.skill-src-signature'), 'utf-8'); + expect(copied).toBe('skill-owned data\n'); + // And the up-to-date check still holds on the second read (no refresh loop). + fs.writeFileSync(path.join(workspace, 'skills', 'tdd', 'marker.txt'), 'local\n'); + executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(fs.existsSync(path.join(workspace, 'skills', 'tdd', 'marker.txt'))).toBe(true); + }); + + it('sweeps abandoned tmp dirs but leaves fresh ones (concurrent copy in flight)', () => { + const systemDir = makeTempDir(); + const userRoot = makeTempDir(); + const workspace = makeTempDir(); + dirs.push(systemDir, userRoot, workspace); + + const skillDir = path.join(systemDir, 'tdd'); + writeSkill(skillDir, 'SKILL.md', DIR_SKILL); + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho hi\n'); + + // Leftover from a crashed run (old) and one from a copy in flight (fresh). + const staleTmp = path.join(workspace, 'skills', '.tdd.tmp-dead'); + const freshTmp = path.join(workspace, 'skills', '.tdd.tmp-live'); + writeSkill(staleTmp, 'SKILL.md', 'stale'); + writeSkill(freshTmp, 'SKILL.md', 'fresh'); + const old = new Date(Date.now() - 60 * 60_000); + fs.utimesSync(staleTmp, old, old); + + const catalog = new SkillCatalog(systemDir, userRoot); + const ctx = { workspacePath: workspace, editAllowed: false, skillCatalog: catalog, userId: 'user1' } as ToolContext; + + const result = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(result!.isError).toBe(false); + expect(fs.existsSync(path.join(workspace, 'skills', 'tdd', 'SKILL.md'))).toBe(true); + expect(fs.existsSync(staleTmp)).toBe(false); + expect(fs.existsSync(freshTmp)).toBe(true); + }); + + it('refreshes a legacy copy that predates signature tracking', () => { + const systemDir = makeTempDir(); + const userRoot = makeTempDir(); + const workspace = makeTempDir(); + dirs.push(systemDir, userRoot, workspace); + + const skillDir = path.join(systemDir, 'tdd'); + writeSkill(skillDir, 'SKILL.md', DIR_SKILL); + writeSkill(path.join(skillDir, 'scripts'), 'run.sh', '#!/bin/sh\necho fresh\n'); + + // Simulate a copy left behind by an older build (no signature file). + writeSkill(path.join(workspace, 'skills', 'tdd'), 'SKILL.md', DIR_SKILL); + writeSkill(path.join(workspace, 'skills', 'tdd', 'scripts'), 'run.sh', '#!/bin/sh\necho stale\n'); + + const catalog = new SkillCatalog(systemDir, userRoot); + const ctx = { workspacePath: workspace, editAllowed: false, skillCatalog: catalog, userId: 'user1' } as ToolContext; + + const result = executeSkillTool('ReadSkill', { name: 'tdd' }, ctx); + expect(result!.isError).toBe(false); + const copied = fs.readFileSync(path.join(workspace, 'skills', 'tdd', 'scripts', 'run.sh'), 'utf-8'); + expect(copied).toContain('fresh'); + }); + it('does NOT prepend path for single-file skills', () => { const systemDir = makeTempDir(); const userRoot = makeTempDir(); @@ -417,6 +621,70 @@ describe('InstallSkill tool', () => { expect(fs.existsSync(path.join(targetDir, 'scripts', 'run.sh'))).toBe(true); }); + // Folder/frontmatter name mismatch guard: installing under a name that + // differs from the SKILL.md frontmatter `name` produces a skill that is + // listed under the frontmatter name but stored under the install name — + // undeletable/uneditable from the UI. InstallSkill must reject the mismatch. + it('rejects a sourcePath install when the frontmatter name differs from the install name', () => { + const sourceDir = makeSourceSkill('fm-name'); + const ctx = makeDirCtx(); + + const result = executeSkillTool('InstallSkill', { + sourcePath: sourceDir, + name: 'other-name', + scope: 'user', + }, ctx); + + expect(result!.isError).toBe(true); + expect(result!.output).toContain('fm-name'); + expect(result!.output).toContain('other-name'); + expect(fs.existsSync(path.join(userRoot, 'user1', 'skills', 'other-name'))).toBe(false); + }); + + it('rejects a sourcePath install when SKILL.md has no valid frontmatter name', () => { + const skillDir = path.join(workspace, 'no-fm'); + fs.mkdirSync(skillDir, { recursive: true }); + fs.writeFileSync(path.join(skillDir, 'SKILL.md'), '# just a heading\nno frontmatter here', 'utf-8'); + const ctx = makeDirCtx(); + + const result = executeSkillTool('InstallSkill', { + sourcePath: skillDir, + name: 'no-fm', + scope: 'user', + }, ctx); + + expect(result!.isError).toBe(true); + expect(result!.output).toContain('frontmatter'); + expect(fs.existsSync(path.join(userRoot, 'user1', 'skills', 'no-fm'))).toBe(false); + }); + + it('rejects a content install when the frontmatter name differs from the install name', () => { + const ctx = makeDirCtx(); + const result = executeSkillTool('InstallSkill', { + name: 'param-name', + content: `---\nname: content-name\ndescription: mismatch\n---\n\nbody`, + scope: 'user', + }, ctx); + + expect(result!.isError).toBe(true); + expect(result!.output).toContain('content-name'); + expect(result!.output).toContain('param-name'); + expect(fs.existsSync(path.join(userRoot, 'user1', 'skills', 'param-name'))).toBe(false); + }); + + it('rejects a content install when the content has no valid frontmatter name', () => { + const ctx = makeDirCtx(); + const result = executeSkillTool('InstallSkill', { + name: 'no-fm-content', + content: '# heading only\nno frontmatter', + scope: 'user', + }, ctx); + + expect(result!.isError).toBe(true); + expect(result!.output).toContain('frontmatter'); + expect(fs.existsSync(path.join(userRoot, 'user1', 'skills', 'no-fm-content'))).toBe(false); + }); + it('resolves a workspace-relative sourcePath against the workspace root', () => { makeSourceSkill('rel-skill', { scripts: true }); const ctx = makeDirCtx(); @@ -521,6 +789,110 @@ describe('InstallSkill tool', () => { expect(result!.output).toContain('admin'); }); + // --- sourcePath 再アンカー --- + // エージェントは workspace のホスト上の絶対パスを知らないため、スペースID等から + // 「data/space/{id}/files/output/xxx」のようなフルパス風の相対パスを組み立てがち。 + // workspace 末尾セグメント(2つ以上)がパス中に現れたら、その続きを workspace + // 相対として再解決する。 + + /** workspace をスペース実配置と同じ `.../space/{id}/files` 形にした ctx を作る */ + function makeSpaceLikeWorkspace(): { spaceWs: string; spaceId: string } { + const base = makeTempDir(); + dirs.push(base); + const spaceId = 'c3fef194-44d7-4cd8-8dc0-b41a5f92a3f3'; + const spaceWs = path.join(base, 'data', 'workspaces', 'space', spaceId, 'files'); + fs.mkdirSync(spaceWs, { recursive: true }); + return { spaceWs, spaceId }; + } + + function makeCtxForWorkspace(ws: string, opts?: { role?: 'admin' | 'user' }): ToolContext { + const catalog = new SkillCatalog(systemDir, userRoot); + return { + workspacePath: ws, + editAllowed: true, + skillCatalog: catalog, + userId: 'user1', + userRole: opts?.role ?? 'user', + } as ToolContext; + } + + it('re-anchors a sourcePath that embeds the workspace tail (space layout)', () => { + // Given: スペース workspace 直下 output/skill-template にスキル一式 + const { spaceWs, spaceId } = makeSpaceLikeWorkspace(); + const skillDir = path.join(spaceWs, 'output', 'skill-template'); + fs.mkdirSync(path.join(skillDir, 'templates'), { recursive: true }); + fs.writeFileSync(path.join(skillDir, 'SKILL.md'), '---\nname: reanchor-skill\ndescription: A test skill\n---\nbody', 'utf-8'); + fs.writeFileSync(path.join(skillDir, 'templates', 'a.html'), '', 'utf-8'); + + // When: 実障害と同形の「フルパス風」相対パスを渡す + const result = executeSkillTool('InstallSkill', { + sourcePath: `data/space/${spaceId}/files/output/skill-template`, + name: 'reanchor-skill', + scope: 'user', + }, makeCtxForWorkspace(spaceWs)); + + // Then: 再アンカーされてフォルダごとインストールされる + expect(result!.isError).toBe(false); + const targetDir = path.join(userRoot, 'user1', 'skills', 'reanchor-skill'); + expect(fs.existsSync(path.join(targetDir, 'SKILL.md'))).toBe(true); + expect(fs.existsSync(path.join(targetDir, 'templates', 'a.html'))).toBe(true); + }); + + it('re-anchors an absolute-looking sourcePath with a wrong prefix', () => { + const { spaceWs, spaceId } = makeSpaceLikeWorkspace(); + const skillDir = path.join(spaceWs, 'output', 'my-skill'); + fs.mkdirSync(skillDir, { recursive: true }); + fs.writeFileSync(path.join(skillDir, 'SKILL.md'), '---\nname: reanchor-abs\ndescription: A test skill\n---\nbody', 'utf-8'); + + const result = executeSkillTool('InstallSkill', { + sourcePath: `/srv/app/data/space/${spaceId}/files/output/my-skill`, + name: 'reanchor-abs', + scope: 'user', + }, makeCtxForWorkspace(spaceWs)); + + expect(result!.isError).toBe(false); + expect(fs.existsSync(path.join(userRoot, 'user1', 'skills', 'reanchor-abs', 'SKILL.md'))).toBe(true); + }); + + it('does not re-anchor on a single ambiguous segment match', () => { + // workspace 末尾の "files" 1 セグメントだけ一致しても再アンカーしない + const { spaceWs } = makeSpaceLikeWorkspace(); + const decoy = path.join(spaceWs, 'x'); + fs.mkdirSync(decoy, { recursive: true }); + fs.writeFileSync(path.join(decoy, 'SKILL.md'), '---\nname: t\ndescription: A test skill\n---\nbody', 'utf-8'); + + const result = executeSkillTool('InstallSkill', { + sourcePath: 'nope/files/x', + name: 'ambiguous', + scope: 'user', + }, makeCtxForWorkspace(spaceWs)); + + expect(result!.isError).toBe(true); + }); + + it('hints workspace-relative paths when sourcePath cannot be resolved', () => { + const ctx = makeDirCtx(); + const result = executeSkillTool('InstallSkill', { + sourcePath: 'no/such/dir', + name: 'missing', + scope: 'user', + }, ctx); + + expect(result!.isError).toBe(true); + expect(result!.output).toContain('workspace-relative'); + }); + + it('does not re-anchor a path containing ".."', () => { + const { spaceWs, spaceId } = makeSpaceLikeWorkspace(); + const result = executeSkillTool('InstallSkill', { + sourcePath: `data/space/${spaceId}/files/../../../../evil`, + name: 'evil-dots', + scope: 'user', + }, makeCtxForWorkspace(spaceWs)); + + expect(result!.isError).toBe(true); + }); + it('overwrites existing skill on reinstall', () => { const sourceDir = makeSourceSkill('overwrite-me', { scripts: true }); const ctx = makeDirCtx(); diff --git a/src/engine/tools/skills.ts b/src/engine/tools/skills.ts index 96518d4..e83503b 100644 --- a/src/engine/tools/skills.ts +++ b/src/engine/tools/skills.ts @@ -1,10 +1,26 @@ -import { mkdirSync, writeFileSync, renameSync, unlinkSync, realpathSync, cpSync, rmSync, existsSync, readdirSync, lstatSync } from 'fs'; -import { join, isAbsolute, resolve } from 'path'; +import { mkdirSync, writeFileSync, renameSync, unlinkSync, realpathSync, cpSync, rmSync, existsSync, readdirSync, lstatSync, readFileSync } from 'fs'; +import { createHash, randomUUID } from 'crypto'; +import { join, isAbsolute, resolve, relative } from 'path'; import type { ToolDef } from '../../llm/openai-compat.js'; import type { ToolContext, ToolResult } from './core.js'; import { VALID_SKILL_NAME } from '../skills.js'; import { scanSkillContent, scanSkillDirectory, maxSeverity } from '../skills-scanner.js'; import { logger } from '../../logger.js'; +import matter from 'gray-matter'; + +/** + * Extract the frontmatter `name` from SKILL.md text ('' when missing or + * unparseable). The skill catalog lists skills by this name, so installs must + * keep it in sync with the folder name (see the mismatch guard in InstallSkill). + */ +function frontmatterNameOf(raw: string): string { + try { + const { data } = matter(raw); + return typeof data?.name === 'string' ? data.name : ''; + } catch { + return ''; + } +} // ── Injected deps (server.ts / worker.ts call setSkillToolDeps) ───────────── @@ -38,13 +54,40 @@ function skillFolderTarget(ctx: ToolContext): { root: string; leaf: string } { return { root: personalRoot, leaf: ctx.userId ?? 'local' }; } +/** + * `sourcePath` の中に workspace 実パスの末尾セグメント列(2 つ以上)が現れたら、 + * その直後からを workspace 相対パスとして返す。見つからなければ null。 + * 例: workspace=…/space/{id}/files, sourcePath="data/space/{id}/files/output/x" + * → "output/x"。1 セグメントだけの一致(例: "files")は誤アンカーしやすいので + * 対象外。".." を含むパスは再解釈しない(通常の解決に任せて拒否させる)。 + */ +function reanchorToWorkspace(workspacePath: string, sourcePath: string): string | null { + const wsSegs = resolve(workspacePath).split('/').filter(Boolean); + const srcSegs = sourcePath.split(/[\\/]+/).filter(s => s && s !== '.'); + if (srcSegs.includes('..')) return null; + for (let len = Math.min(wsSegs.length, srcSegs.length); len >= 2; len--) { + const tail = wsSegs.slice(wsSegs.length - len); + for (let i = 0; i + len <= srcSegs.length; i++) { + if (tail.every((seg, j) => seg === srcSegs[i + j])) { + const rest = srcSegs.slice(i + len); + return rest.length > 0 ? rest.join('/') : null; + } + } + } + return null; +} + // ── Skill materialization ─────────────────────────────────────────────────── // When the agent ReadSkill's a directory-based skill, copy its files into the // task workspace (`{workspace}/skills/{name}/`) so its scripts are usable in // every Bash sandbox mode (the skill store lives outside the workspace and is -// only bind-mounted in the bwrap path). The copy is rw and idempotent per task. +// only bind-mounted in the bwrap path). The copy is rw. A signature of the +// source dir is stored alongside the copy; when the source changes (e.g. the +// skill was updated by another task) the next ReadSkill replaces the copy — +// including any local edits. An unchanged source leaves the copy untouched. const SKILL_MATERIALIZE_MAX_BYTES = 50 * 1024 * 1024; // 50MB +const SKILL_SIGNATURE_SUFFIX = '.skill-src-signature'; /** Total size of `dir` (skipping symlinks), or null once it exceeds `cap`. */ function dirSizeCapped(dir: string, cap: number): number | null { @@ -69,28 +112,127 @@ function dirSizeCapped(dir: string, cap: number): number | null { interface MaterializeResult { ok: boolean; relPath: string; note?: string } -/** Copy a skill's source dir into `{workspace}/skills/{name}/` (idempotent). */ +/** + * Signature of a skill source dir: sha1 over each file's relative path and + * content bytes, symlinks skipped — the same file set `materializeSkill` + * copies. Content-based (not size/mtime) so a re-synced store with identical + * bytes does not count as a change. Source dirs are capped at 50MB and + * ReadSkill is infrequent, so hashing the bytes is cheap enough. + */ +function skillSourceSignature(srcDir: string): string { + const files: string[] = []; + const stack = [srcDir]; + while (stack.length > 0) { + const d = stack.pop()!; + let entries: string[]; + try { entries = readdirSync(d); } catch { continue; } + for (const e of entries) { + const p = join(d, e); + let st; + try { st = lstatSync(p); } catch { continue; } + if (st.isSymbolicLink()) continue; + if (st.isDirectory()) { stack.push(p); continue; } + files.push(relative(srcDir, p)); + } + } + files.sort(); + const hash = createHash('sha1'); + for (const rel of files) { + // Length-prefix each field so path/content boundaries stay unambiguous. + hash.update(`${rel.length}:${rel}:`); + let content: Buffer; + try { content = readFileSync(join(srcDir, rel)); } catch { content = Buffer.from(''); } + hash.update(`${content.length}:`); + hash.update(content); + } + return hash.digest('hex'); +} + +/** + * Copy a skill's source dir into `{workspace}/skills/{name}/`. A stale copy + * (source changed since it was made, or made before signature tracking) is + * replaced wholesale; an up-to-date copy is left untouched. The copy is staged + * in a temp dir first so a failed refresh keeps the previous copy usable, and + * the signature lives in a sidecar (`skills/.{name}.skill-src-signature`) so + * it can never clobber a file the skill itself owns. Skill names are + * `[a-z0-9_-]` only, so the dot-prefixed sidecar/tmp names cannot collide + * with a real skill dir. + */ export function materializeSkill(srcDir: string, workspacePath: string, skillName: string): MaterializeResult { const relPath = `skills/${skillName}`; - const dest = join(workspacePath, 'skills', skillName); - if (existsSync(dest)) return { ok: true, relPath }; // already materialized this task + const skillsRoot = join(workspacePath, 'skills'); + const dest = join(skillsRoot, skillName); + const sigPath = join(skillsRoot, `.${skillName}${SKILL_SIGNATURE_SUFFIX}`); + // Cap check first: the signature reads every source byte, so an oversized + // dir must be rejected before it gets loaded into memory. if (dirSizeCapped(srcDir, SKILL_MATERIALIZE_MAX_BYTES) === null) { return { ok: false, relPath, note: 'skill exceeds 50MB copy limit' }; } + const signature = skillSourceSignature(srcDir); + if (existsSync(dest)) { + let existing: string | null = null; + try { existing = readFileSync(sigPath, 'utf-8').trim(); } catch { /* legacy copy without signature → refresh */ } + if (existing === signature) return { ok: true, relPath }; // copy is up to date + } + // Per-call unique tmp: within one process ReadSkill runs synchronously, but + // another process sharing the workspace (dev + prod, multi-instance) may be + // materializing the same skill concurrently — never touch its tmp. + const tmp = join(skillsRoot, `.${skillName}.tmp-${randomUUID()}`); try { - mkdirSync(join(workspacePath, 'skills'), { recursive: true }); - cpSync(srcDir, dest, { + mkdirSync(skillsRoot, { recursive: true }); + sweepStaleTmpDirs(skillsRoot, skillName); + cpSync(srcDir, tmp, { recursive: true, dereference: false, // Skip symlinks so a link inside the skill cannot point outside the workspace. filter: (src) => { try { return !lstatSync(src).isSymbolicLink(); } catch { return false; } }, }); + // Swap in only after the whole copy succeeded. The old copy is moved + // aside (not deleted) so a failed swap can put it back. + const backup = join(skillsRoot, `.${skillName}.tmp-bak-${randomUUID()}`); + let haveBackup = false; + if (existsSync(dest)) { renameSync(dest, backup); haveBackup = true; } + try { + renameSync(tmp, dest); + } catch (e) { + if (haveBackup && !existsSync(dest)) { + try { renameSync(backup, dest); } catch { /* leave the backup for the sweep */ } + } + throw e; + } + if (haveBackup) { try { rmSync(backup, { recursive: true, force: true }); } catch { /* sweep will get it */ } } + writeFileSync(sigPath, `${signature}\n`, 'utf-8'); return { ok: true, relPath }; } catch (e) { + try { rmSync(tmp, { recursive: true, force: true }); } catch { /* best effort */ } + // A concurrent materialize may have won the swap — then the copy is fine. + try { + if (existsSync(dest) && readFileSync(sigPath, 'utf-8').trim() === signature) { + return { ok: true, relPath }; + } + } catch { /* fall through to the failure result */ } return { ok: false, relPath, note: `copy failed: ${(e as Error).message}` }; } } +const SKILL_TMP_STALE_MS = 15 * 60_000; + +/** Remove `.{name}.tmp-*` leftovers from crashed runs; keep recent ones (a copy may be in flight). */ +function sweepStaleTmpDirs(skillsRoot: string, skillName: string): void { + let entries: string[]; + try { entries = readdirSync(skillsRoot); } catch { return; } + const prefix = `.${skillName}.tmp-`; + for (const e of entries) { + if (!e.startsWith(prefix)) continue; + const p = join(skillsRoot, e); + try { + if (Date.now() - lstatSync(p).mtimeMs > SKILL_TMP_STALE_MS) { + rmSync(p, { recursive: true, force: true }); + } + } catch { /* best effort */ } + } +} + // --------------------------------------------------------------------------- // Tool definitions // --------------------------------------------------------------------------- @@ -103,9 +245,9 @@ export const TOOL_DEFS: Record = { parameters: { type: 'object', properties: { - name: { type: 'string', description: 'スキル名 ([a-z0-9_-] のみ)' }, + name: { type: 'string', description: 'スキル名 ([a-z0-9_-] のみ)。SKILL.md frontmatter の name: と完全一致必須' }, content: { type: 'string', description: 'SKILL.md の全文 (YAML frontmatter + 本文)。通常はこちらを使用' }, - sourcePath: { type: 'string', description: 'workspace 内のスキルディレクトリの絶対パス (SKILL.md + scripts/ 等を含む場合のみ)。workspace 外のパスは拒否される' }, + sourcePath: { type: 'string', description: 'workspace 相対のスキルディレクトリパス(例: "output/my-skill"。SKILL.md + scripts/ 等を含むフォルダごと登録する場合のみ)。workspace 外のパスは拒否される' }, scope: { type: 'string', enum: ['system', 'user'], description: 'system=全ユーザー共有 (admin only), user=個人' }, }, required: ['name', 'scope'], @@ -195,8 +337,13 @@ function executeInstallSkill( if (typeof sourcePath !== 'string') { return { output: 'InstallSkill: "sourcePath" must be a string', isError: true }; } - let realSource: string; let realWorkspace: string; + try { + realWorkspace = realpathSync(ctx.workspacePath); + } catch (e) { + return { output: `InstallSkill: sourcePath does not exist or is not accessible. Use "content" parameter instead to pass SKILL.md text directly.`, isError: true }; + } + let realSource: string; try { // Resolve relative paths against the workspace root, mirroring // resolveAndGuard() used by Read/Write/Edit. Without this, a natural @@ -206,9 +353,18 @@ function executeInstallSkill( // friendly result instead of crashing the task. const absSource = isAbsolute(sourcePath) ? sourcePath : resolve(ctx.workspacePath, sourcePath); realSource = realpathSync(absSource); - realWorkspace = realpathSync(ctx.workspacePath); } catch (e) { - return { output: `InstallSkill: sourcePath does not exist or is not accessible. Use "content" parameter instead to pass SKILL.md text directly.`, isError: true }; + // Agents don't know the workspace's host-side absolute path, so they + // often reconstruct a plausible-looking one (e.g. "data/space/{id}/files/ + // output/x"). If the given path embeds the workspace's trailing segments, + // re-anchor: treat everything after that run as workspace-relative. + const reanchored = reanchorToWorkspace(ctx.workspacePath, sourcePath); + try { + if (!reanchored) throw new Error('no re-anchor candidate'); + realSource = realpathSync(resolve(ctx.workspacePath, reanchored)); + } catch { + return { output: `InstallSkill: sourcePath does not exist or is not accessible. Specify a workspace-relative path (e.g. "output/my-skill"), or use "content" to pass SKILL.md text directly.`, isError: true }; + } } if (!realSource.startsWith(realWorkspace + '/')) { return { output: 'InstallSkill: sourcePath must be inside the task workspace. Use "content" parameter to pass SKILL.md text directly.', isError: true }; @@ -217,6 +373,22 @@ function executeInstallSkill( return { output: `InstallSkill: SKILL.md not found in ${sourcePath}`, isError: true }; } + // The catalog lists skills by their SKILL.md frontmatter `name`. If it + // differs from the install name the skill gets stored under one name but + // listed under another — the UI can then never edit or delete it. + let srcFmName = ''; + try { + srcFmName = frontmatterNameOf(readFileSync(join(realSource, 'SKILL.md'), 'utf-8')); + } catch { + srcFmName = ''; + } + if (!srcFmName || !VALID_SKILL_NAME.test(srcFmName)) { + return { output: 'InstallSkill: SKILL.md must start with YAML frontmatter containing a valid "name:" ([a-z0-9_-]). Without it the skill will not appear in the skill list.', isError: true }; + } + if (srcFmName !== skillName) { + return { output: `InstallSkill: SKILL.md frontmatter name "${srcFmName}" does not match the install name "${skillName}". Pass name: "${srcFmName}" (or fix the frontmatter) so they match.`, isError: true }; + } + const stats = getDirStats(realSource); if (stats.fileCount > MAX_FILE_COUNT) { return { output: `InstallSkill: directory contains ${stats.fileCount} files (max ${MAX_FILE_COUNT})`, isError: true }; @@ -269,6 +441,16 @@ function executeInstallSkill( return { output: `InstallSkill: content exceeds 64 KB limit`, isError: true }; } + // Same mismatch guard as sourcePath mode: the frontmatter name must exist + // and match the install name, or the skill becomes unmanageable from the UI. + const contentFmName = frontmatterNameOf(content!); + if (!contentFmName || !VALID_SKILL_NAME.test(contentFmName)) { + return { output: 'InstallSkill: content must start with YAML frontmatter containing a valid "name:" ([a-z0-9_-]). Without it the skill will not appear in the skill list.', isError: true }; + } + if (contentFmName !== skillName) { + return { output: `InstallSkill: content frontmatter name "${contentFmName}" does not match the install name "${skillName}". Pass name: "${contentFmName}" (or fix the frontmatter) so they match.`, isError: true }; + } + const findings = scanSkillContent(content!); const severity = maxSeverity(findings); if (severity === 'high') { diff --git a/src/engine/tools/ssh-console-space.test.ts b/src/engine/tools/ssh-console-space.test.ts index 5a4192b..9bd8cd9 100644 --- a/src/engine/tools/ssh-console-space.test.ts +++ b/src/engine/tools/ssh-console-space.test.ts @@ -26,6 +26,7 @@ import { createGrantsRepo } from '../../ssh/grants-repo.js'; import { createAuditRepo } from '../../ssh/audit-repo.js'; import { createAbuseRepo } from '../../ssh/abuse-repo.js'; import { createAccessResolver } from '../../ssh/access.js'; +import { SessionRegistry } from '../../ssh/console-registry.js'; import { SSH_DEFAULTS } from '../../ssh/config.js'; import { preflight, setSshSubsystem, type SshSubsystem } from './ssh.js'; import { openConsoleSession } from './ssh-console.js'; @@ -69,18 +70,17 @@ function makeSubsystem(db: Database.Database): { sub: SshSubsystem; conn: SshCon maintenance: { isActive: () => false, snapshot: () => ({ active: false }), enter: () => undefined, exit: () => undefined }, // Enable the console (disabled by default) so the agent-tool wrapper reaches preflight. config: { ...SSH_DEFAULTS, console: { ...SSH_DEFAULTS.console, enabled: true } }, - sessionRegistry: { - register: () => undefined, - get: () => null, - listAll: () => [], - listForConnection: () => [], - closeForTask: async () => undefined, - enforceCap: () => [], - sweep: async () => undefined, - startSweepTimer: () => undefined, - stopSweepTimer: () => undefined, - shutdown: async () => undefined, - }, + // REAL registry, not a stub: every test here fails preflight or dies at + // the openShellChannel sentinel, so no session is ever registered and the + // registry stays empty (no sweep timer is started without an explicit + // startSweepTimer call). A previous hand-rolled stub silently broke when + // multi-session (#732) added listForTask/closeSession to the registry API; + // the real class cannot drift like that. + sessionRegistry: new SessionRegistry({ + idleTimeoutMs: 60_000, + maxSessionDurationMs: 600_000, + maxSessionsPerConnection: 2, + }), // A cleared-preflight session reaches this and fails here (open_shell_failed). openShellChannel: async () => { throw new Error(SHELL_SENTINEL); diff --git a/src/engine/tools/task-conversation.test.ts b/src/engine/tools/task-conversation.test.ts index 60bc038..6d71119 100644 --- a/src/engine/tools/task-conversation.test.ts +++ b/src/engine/tools/task-conversation.test.ts @@ -143,6 +143,51 @@ describe('SearchTaskConversation', () => { }); }); +describe('SearchTaskConversation — AND search (space-separated tokens)', () => { + it('matches when all tokens appear even if non-contiguous', async () => { + // comment:10 = 'Please keep the existing auth flow unchanged.' + // 'auth unchanged' is not a contiguous substring, but both tokens appear. + const ctx = makeCtx(makeIO(commentsA)); + const res = await executeTool('SearchTaskConversation', { query: 'auth unchanged', source: 'comments' }, ctx); + expect(res!.isError).toBe(false); + expect(res!.output).toContain('comment:10'); + }); + + it('does not match when tokens are split across different entries (AND, not OR)', async () => { + // 'login' appears only in comment:11, 'components' only in comment:12. + const ctx = makeCtx(makeIO(commentsA)); + const res = await executeTool('SearchTaskConversation', { query: 'login components', source: 'comments' }, ctx); + expect(res!.output).not.toContain('comment:11'); + expect(res!.output).not.toContain('comment:12'); + expect(res!.output).toContain('見つかりませんでした'); + }); + + it('applies AND matching to the transcript source too', async () => { + const tp = writeTranscript('transcript.jsonl', [ + { role: 'user', content: 'Constraint: never touch the payment code.' }, + { role: 'assistant', content: 'I refactored the payment module docs.' }, + ]); + const ctx = makeCtx(makeIO([], tp)); + const res = await executeTool('SearchTaskConversation', { query: 'payment constraint', source: 'transcript' }, ctx); + expect(res!.output).toContain('transcript:0'); + expect(res!.output).not.toContain('transcript:1'); + }); + + it('treats a full-width (ideographic) space as a token separator', async () => { + const ctx = makeCtx(makeIO(commentsA)); + const res = await executeTool('SearchTaskConversation', { query: 'auth unchanged', source: 'comments' }, ctx); + expect(res!.output).toContain('comment:10'); + }); + + it('centers the excerpt on the first token hit when the full query never appears contiguously', async () => { + const body = 'x'.repeat(300) + ' alpha middle beta ' + 'y'.repeat(300); + const ctx = makeCtx(makeIO([{ id: 1, author: 'user', kind: 'comment', body, createdAt: '2026-06-30T10:00:00Z' }])); + const res = await executeTool('SearchTaskConversation', { query: 'alpha beta' }, ctx); + expect(res!.output).toContain('comment:1'); + expect(res!.output).toContain('alpha'); // excerpt must show the hit, not the head of the body + }); +}); + // P0/P1 (review): the REAL scoping guarantee comes from // repo.makeTaskConversationIO(taskId) → listLocalTaskComments(taskId) WHERE // task_id = ?. Exercise it end-to-end with a real temp-DB Repository and two @@ -202,3 +247,25 @@ describe('ReadTaskConversation', () => { expect(res!.output.length).toBeGreaterThan(0); }); }); + +describe('sub-task transcript-only recall', () => { + it('searches the transcript while comments stay empty', async () => { + const tp = writeTranscript('transcript.jsonl', [ + { role: 'user', content: 'Sub-task brief: summarize the quarterly churn drivers.' }, + { role: 'assistant', content: 'Noted churn driver: onboarding friction.' }, + ]); + const ctx = makeCtx(makeIO([], tp)); // transcript-only shape (no comments) + const res = await executeTool('SearchTaskConversation', { query: 'churn', source: 'both' }, ctx); + expect(res!.isError).toBe(false); + expect(res!.output).toContain('transcript:0'); + expect(res!.output).toContain('transcript:1'); + }); + + it('degrades gracefully when a comment ref is read but no comments exist', async () => { + const tp = writeTranscript('transcript.jsonl', [{ role: 'user', content: 'hello' }]); + const ctx = makeCtx(makeIO([], tp)); + const res = await executeTool('ReadTaskConversation', { ref: 'comment:1' }, ctx); + expect(res!.isError).toBe(true); + expect(res!.output).toContain('見つかりません'); + }); +}); diff --git a/src/engine/tools/task-conversation.ts b/src/engine/tools/task-conversation.ts index 2e51e22..1863cbe 100644 --- a/src/engine/tools/task-conversation.ts +++ b/src/engine/tools/task-conversation.ts @@ -12,16 +12,20 @@ * * Scoping is enforced by the bound `ctx.taskConversation` IO: comments come from * `listLocalTaskComments(taskId)` (WHERE task_id = ?), and the transcript path is - * bound by the worker ONLY to a per-task `runtimeDir` — never the space-shared - * `workspacePath/logs` fallback — so a tool call can never read another task's - * logs. When the transcript path is unset the transcript source yields nothing - * (comments still work); when the whole IO is unset (subtasks / gitea-issue runs) - * the tools degrade to a friendly non-error message. + * bound by the worker to a location a tool call can never use to read another + * task's logs. For a top-level local task that is the per-task `runtimeDir` + * (never the space-shared `workspacePath/logs` fallback). A sub-task run instead + * gets a transcript-only IO (no comments — see `makeTranscriptOnlyConversationIO`) + * bound to the sub-task's OWN isolated workspace transcript. When the transcript + * path is unset the transcript source yields nothing (comments still work); when + * the whole IO is unset (gitea-issue runs) the tools degrade to a friendly + * non-error message. */ import { existsSync, readFileSync } from 'node:fs'; import { ToolDef } from '../../llm/openai-compat.js'; import type { ToolContext, ToolResult, TaskConversationComment } from './core.js'; +import { excerpt, tokenize } from './text-excerpt.js'; const SEARCH_DEF: ToolDef = { type: 'function', @@ -32,7 +36,7 @@ const SEARCH_DEF: ToolDef = { parameters: { type: 'object', properties: { - query: { type: 'string', description: '検索キーワード (部分一致・大文字小文字無視)。' }, + query: { type: 'string', description: '検索キーワード。スペース区切りで複数指定すると AND 検索 (すべての語を含む発言にヒット)。部分一致・大文字小文字無視。' }, source: { type: 'string', enum: ['comments', 'transcript', 'both'], description: '検索対象。既定 both。' }, author: { type: 'string', enum: ['user', 'agent', 'system'], description: '発言者で絞り込む。' }, kind: { type: 'string', description: 'コメント種別で絞り込む (request/comment/interjection/result/ask/progress/handoff)。transcript には適用されない。' }, @@ -105,23 +109,6 @@ function loadTranscript(path: string | undefined): TranscriptEntry[] { return out; } -function collapse(s: string): string { - return s.replace(/\s+/g, ' ').trim(); -} - -/** Compact excerpt centered on the first query hit, bounded to `max` chars. */ -function excerpt(body: string, query: string, max: number): string { - const flat = collapse(body); - if (flat.length <= max && !query) return flat; - const idx = query ? flat.toLowerCase().indexOf(query.toLowerCase()) : 0; - if (idx < 0 || flat.length <= max) { - return flat.length <= max ? flat : flat.slice(0, max - 1) + '…'; - } - const start = Math.max(0, idx - 40); - const end = Math.min(flat.length, start + max); - return (start > 0 ? '…' : '') + flat.slice(start, end) + (end < flat.length ? '…' : ''); -} - function clampLimit(v: unknown, def: number, max: number): number { const n = typeof v === 'number' && Number.isFinite(v) ? Math.floor(v) : def; return Math.min(Math.max(n, 1), max); @@ -144,7 +131,11 @@ async function search(input: Record, ctx: ToolContext): Promise const author = input['author'] as Author | undefined; const kind = typeof input['kind'] === 'string' ? (input['kind'] as string) : undefined; const limit = clampLimit(input['limit'], 10, 50); - const q = query.toLowerCase(); + const tokens = tokenize(query); + const matches = (body: string): boolean => { + const lower = body.toLowerCase(); + return tokens.every((t) => lower.includes(t)); + }; type Hit = { ref: string; header: string; body: string }; const hits: Hit[] = []; @@ -154,7 +145,7 @@ async function search(input: Record, ctx: ToolContext): Promise for (const c of comments) { if (author && c.author !== author) continue; if (kind && c.kind !== kind) continue; - if (!c.body.toLowerCase().includes(q)) continue; + if (!matches(c.body)) continue; hits.push({ ref: `comment:${c.id}`, header: `${c.author}/${c.kind} ${c.createdAt}`, body: c.body }); } } @@ -165,7 +156,7 @@ async function search(input: Record, ctx: ToolContext): Promise if (!kind) { for (const e of loadTranscript(io.transcriptPath)) { if (author && e.author !== author) continue; - if (!e.content.toLowerCase().includes(q)) continue; + if (!matches(e.content)) continue; hits.push({ ref: `transcript:${e.index}`, header: e.author, body: e.content }); } } @@ -179,7 +170,7 @@ async function search(input: Record, ctx: ToolContext): Promise const lines = [`## Conversation Search Results (query: "${query}", ${hits.length} hit(s)${hits.length > limit ? `, showing ${limit}` : ''})`, '']; for (const h of shown) { lines.push(`- ${h.ref} ${h.header}`); - lines.push(` ${excerpt(h.body, query, EXCERPT_MAX)}`); + lines.push(` ${excerpt(h.body, tokens, EXCERPT_MAX)}`); lines.push(''); } lines.push('前後の文脈は ReadTaskConversation({ ref }) で確認できます。'); @@ -209,7 +200,7 @@ async function read(input: Record, ctx: ToolContext): Promise, ctx: ToolContext): Promise= 0 && (idx < 0 || i < idx)) idx = i; + } + if (idx < 0) return flat.slice(0, max - 1) + '…'; + const start = Math.max(0, idx - 40); + const end = Math.min(flat.length, start + max); + return (start > 0 ? '…' : '') + flat.slice(start, end) + (end < flat.length ? '…' : ''); +} diff --git a/src/engine/tools/tool-categories.ts b/src/engine/tools/tool-categories.ts index 92ee72a..ec3603b 100644 --- a/src/engine/tools/tool-categories.ts +++ b/src/engine/tools/tool-categories.ts @@ -45,6 +45,7 @@ export const META_TOOLS = new Set([ 'ListSkills', 'InstallSkill', 'RequestTool', + 'RequestPackage', ]); // ───────────────────────────────────────────────────────────────────────────── diff --git a/src/engine/tools/workspace-task-search.test.ts b/src/engine/tools/workspace-task-search.test.ts index dcf0b9d..b79fcdf 100644 --- a/src/engine/tools/workspace-task-search.test.ts +++ b/src/engine/tools/workspace-task-search.test.ts @@ -87,6 +87,23 @@ describe('SearchWorkspaceTasks tool', () => { expect(res!.output).toMatch(/見つからない|スコープ外/); }); + it('centers the excerpt on the first token hit for multi-word (AND) queries', async () => { + // FTS5 matches tokens non-contiguously, so the excerpt must center on the + // first token occurrence — not require the whole query as one substring. + const text = 'x'.repeat(300) + ' 認証エラー が発生、その後 再ログイン で解消 ' + 'y'.repeat(300); + const io = { + fts5Available: true, + search: async () => [{ taskId: 1, taskTitle: 'T', commentId: 2, author: 'agent', kind: 'result', createdAt: 'd', text }], + around: async () => null, + }; + const res = await executeTool('SearchWorkspaceTasks', { query: '認証エラー 再ログイン' }, ctxWith(io)); + expect(res!.isError).toBe(false); + // The excerpt line (2-space indent) must show the hit, not the head of the body. + const excerptLine = res!.output.split('\n').find((l) => l.startsWith(' ') && !l.startsWith(' ')); + expect(excerptLine).toBeDefined(); + expect(excerptLine!).toContain('認証エラー'); + }); + it('context>0 attaches neighbor comments under each hit', async () => { let aroundCalledWith: { id: number; before: number; after: number } | null = null; const io = { diff --git a/src/engine/tools/workspace-task-search.ts b/src/engine/tools/workspace-task-search.ts index cbabd11..f4fa3e7 100644 --- a/src/engine/tools/workspace-task-search.ts +++ b/src/engine/tools/workspace-task-search.ts @@ -18,6 +18,7 @@ import { ToolDef } from '../../llm/openai-compat.js'; import type { ToolContext, ToolResult } from './core.js'; +import { excerpt, tokenize } from './text-excerpt.js'; const SEARCH_DEF: ToolDef = { type: 'function', @@ -52,23 +53,6 @@ const FTS5_UNAVAILABLE_MSG = const EXCERPT_MAX = 200; -function collapse(s: string): string { - return s.replace(/\s+/g, ' ').trim(); -} - -/** Compact excerpt centered on the first query hit, bounded to `max` chars. */ -function excerpt(body: string, query: string, max: number): string { - const flat = collapse(body); - if (flat.length <= max && !query) return flat; - const idx = query ? flat.toLowerCase().indexOf(query.toLowerCase()) : 0; - if (idx < 0 || flat.length <= max) { - return flat.length <= max ? flat : flat.slice(0, max - 1) + '…'; - } - const start = Math.max(0, idx - 40); - const end = Math.min(flat.length, start + max); - return (start > 0 ? '…' : '') + flat.slice(start, end) + (end < flat.length ? '…' : ''); -} - function clampLimit(v: unknown, def: number, max: number): number { const n = typeof v === 'number' && Number.isFinite(v) ? Math.floor(v) : def; return Math.min(Math.max(n, 1), max); @@ -125,14 +109,14 @@ async function search(input: Record, ctx: ToolContext): Promise const lines = [`## Workspace Task Search Results (query: "${query}", ${hits.length} hit(s))`, '']; for (const h of hits) { lines.push(`- task:${h.taskId} "${h.taskTitle}" / comment:${h.commentId} ${h.author}/${h.kind} ${h.createdAt}`); - lines.push(` ${excerpt(h.text, query!, EXCERPT_MAX)}`); + lines.push(` ${excerpt(h.text, tokenize(query!), EXCERPT_MAX)}`); if (context > 0) { const around = await io.around(h.commentId, context, context); if (around) { for (const c of around.comments) { const marker = c.isCenter ? ' ←' : ''; lines.push(` - comment:${c.commentId} ${c.author}/${c.kind} ${c.createdAt}${marker}`); - lines.push(` ${excerpt(c.body, '', EXCERPT_MAX)}`); + lines.push(` ${excerpt(c.body, [], EXCERPT_MAX)}`); } } } @@ -159,7 +143,7 @@ async function searchAroundRef(ref: string, context: number, io: NonNullable void; const DEFAULT_CONTEXT_LIMIT_TOKENS = 32_000; const DEFAULT_PROMPT_GUARD_RATIO = 0.8; -function estimateRequestTokens(messages: Message[], tools?: ToolDef[]): number { - const messageTokens = messages.reduce((total, message) => total + estimateMessageTokens(message), 0); - const toolTokens = tools && tools.length > 0 ? estimateToolsTokens(tools) : 0; - return messageTokens + toolTokens + 128; -} +// The estimate MUST stay byte-identical to what the agent-loop prompt guard +// computes (estimateRequestTokens in token-estimate.ts). A drift between the +// two creates a band of prompt sizes the guard passes but this preflight +// blocks — the error-recovery path then finds nothing to shrink and the loop +// resends the identical request until maxIterations. function contentChars(message: Message): number { if (typeof message.content === 'string') return message.content.length; diff --git a/src/progress/delegate-runs.test.ts b/src/progress/delegate-runs.test.ts index 04b454c..d0c0fc6 100644 --- a/src/progress/delegate-runs.test.ts +++ b/src/progress/delegate-runs.test.ts @@ -61,4 +61,127 @@ describe('reconstructDelegateRuns', () => { it('delegate イベントが無ければ空配列', () => { expect(reconstructDelegateRuns([ev({ seq: 1, kind: 'tool_call', payload: {} })])).toEqual([]); }); + + it('resultPreview がある場合に復元される', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R3', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'delegate_complete', payload: { delegateRunId: 'R3', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success', resultPreview: 'Success result preview' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].resultPreview).toBe('Success result preview'); + }); + + it('resultPreview がない場合に null になる', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R4', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'delegate_complete', payload: { delegateRunId: 'R4', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].resultPreview).toBeNull(); + }); + + it('llm_call_end イベントをトークン集計:prompt 100 + completion 50 = 150', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R5', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'llm_call_end', correlationId: 'R5', payload: { promptTokens: 100, completionTokens: 50 } }), + ev({ seq: 3, kind: 'delegate_complete', payload: { delegateRunId: 'R5', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].totalTokens).toBe(150); + }); + + it('複数 llm_call_end を累積:100+50 + 200+100 = 450', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R6', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'llm_call_end', correlationId: 'R6', payload: { promptTokens: 100, completionTokens: 50 } }), + ev({ seq: 3, kind: 'llm_call_end', correlationId: 'R6', payload: { promptTokens: 200, completionTokens: 100 } }), + ev({ seq: 4, kind: 'delegate_complete', payload: { delegateRunId: 'R6', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].totalTokens).toBe(450); + }); + + it('llm_call_end がトークン欠落のときは 0 として扱う', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R7', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'llm_call_end', correlationId: 'R7', payload: { /* no tokens */ } }), + ev({ seq: 3, kind: 'delegate_complete', payload: { delegateRunId: 'R7', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].totalTokens).toBe(0); + }); + + it('llm_call_end がなければ totalTokens = 0', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R8', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'tool_call', correlationId: 'R8', payload: { tool: 'WebFetch' } }), + ev({ seq: 3, kind: 'delegate_complete', payload: { delegateRunId: 'R8', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].totalTokens).toBe(0); + }); + + it('toolSummary: Write ×2(同一 file_path)+ WebFetch ×1 → [Write:2, WebFetch:1](初出順)', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R9', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'tool_call', correlationId: 'R9', payload: { tool: 'Write', args: { file_path: 'output.txt' } } }), + ev({ seq: 3, kind: 'tool_call', correlationId: 'R9', payload: { tool: 'WebFetch' } }), + ev({ seq: 4, kind: 'tool_call', correlationId: 'R9', payload: { tool: 'Write', args: { file_path: 'output.txt' } } }), + ev({ seq: 5, kind: 'delegate_complete', payload: { delegateRunId: 'R9', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].toolSummary).toEqual([ + { tool: 'Write', count: 2 }, + { tool: 'WebFetch', count: 1 }, + ]); + }); + + it('filesChanged: Write と Edit の file_path を重複除去して記録', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R10', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'tool_call', correlationId: 'R10', payload: { tool: 'Write', args: { file_path: 'file1.txt' } } }), + ev({ seq: 3, kind: 'tool_call', correlationId: 'R10', payload: { tool: 'Edit', args: { file_path: 'file2.txt' } } }), + ev({ seq: 4, kind: 'tool_call', correlationId: 'R10', payload: { tool: 'Write', args: { file_path: 'file1.txt' } } }), + ev({ seq: 5, kind: 'delegate_complete', payload: { delegateRunId: 'R10', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].filesChanged).toEqual(['file1.txt', 'file2.txt']); + }); + + it('filesChanged: Read など他ツールの file_path は入らない', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R11', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'tool_call', correlationId: 'R11', payload: { tool: 'Read', args: { file_path: 'input.txt' } } }), + ev({ seq: 3, kind: 'tool_call', correlationId: 'R11', payload: { tool: 'Write', args: { file_path: 'output.txt' } } }), + ev({ seq: 4, kind: 'delegate_complete', payload: { delegateRunId: 'R11', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].filesChanged).toEqual(['output.txt']); + }); + + it('tool_call がない run では toolSummary と filesChanged が空配列', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R12', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'llm_call_end', correlationId: 'R12', payload: { promptTokens: 100, completionTokens: 50 } }), + ev({ seq: 3, kind: 'delegate_complete', payload: { delegateRunId: 'R12', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].toolSummary).toEqual([]); + expect(runs[0].filesChanged).toEqual([]); + }); + + it('tool_call に args がなくてもクラッシュしない', () => { + const events = [ + ev({ seq: 1, kind: 'delegate_start', payload: { delegateRunId: 'R13', parentRunId: null, description: 'test', depth: 1 } }), + ev({ seq: 2, kind: 'tool_call', correlationId: 'R13', payload: { tool: 'WebFetch' } }), + ev({ seq: 3, kind: 'tool_call', correlationId: 'R13', payload: { tool: 'Write' } }), + ev({ seq: 4, kind: 'delegate_complete', payload: { delegateRunId: 'R13', parentRunId: null, description: 'test', depth: 1, next: 'COMPLETE', status: 'success' } }), + ]; + const runs = reconstructDelegateRuns(events); + expect(runs[0].toolSummary).toEqual([ + { tool: 'WebFetch', count: 1 }, + { tool: 'Write', count: 1 }, + ]); + expect(runs[0].filesChanged).toEqual([]); + }); }); diff --git a/src/progress/delegate-runs.ts b/src/progress/delegate-runs.ts index 348c181..0fa89a9 100644 --- a/src/progress/delegate-runs.ts +++ b/src/progress/delegate-runs.ts @@ -15,6 +15,10 @@ export interface DelegateRun { endTs: string | null; eventCount: number; toolCalls: number; + resultPreview: string | null; + totalTokens: number; + toolSummary: Array<{ tool: string; count: number }>; + filesChanged: string[]; } function payloadOf(e: EventBase): Record { @@ -42,6 +46,10 @@ export function reconstructDelegateRuns(events: EventBase[]): DelegateRun[] { endTs: null, eventCount: 0, toolCalls: 0, + resultPreview: null, + totalTokens: 0, + toolSummary: [], + filesChanged: [], }); order.push(id); } @@ -55,6 +63,7 @@ export function reconstructDelegateRuns(events: EventBase[]): DelegateRun[] { run.endTs = e.ts; const s = String(p.status ?? ''); run.status = (s === 'success' || s === 'aborted' || s === 'needs_user_input') ? s : 'running'; + run.resultPreview = typeof p.resultPreview === 'string' ? p.resultPreview : null; } continue; } @@ -63,7 +72,34 @@ export function reconstructDelegateRuns(events: EventBase[]): DelegateRun[] { if (e.correlationId && byId.has(e.correlationId)) { const run = byId.get(e.correlationId)!; run.eventCount++; - if (e.kind === 'tool_call') run.toolCalls++; + if (e.kind === 'tool_call') { + run.toolCalls++; + const p = payloadOf(e); + const tool = typeof p.tool === 'string' ? p.tool : null; + if (tool) { + // toolSummary に追加:既存エントリがあれば count を +1、なければ末尾に追加 + const existing = run.toolSummary.find((t) => t.tool === tool); + if (existing) { + existing.count++; + } else { + run.toolSummary.push({ tool, count: 1 }); + } + // filesChanged に追加:Write/Edit なら args.file_path を抽出 + if ((tool === 'Write' || tool === 'Edit') && typeof p.args === 'object' && p.args !== null) { + const args = p.args as Record; + const filePath = typeof args.file_path === 'string' ? args.file_path : null; + if (filePath && !run.filesChanged.includes(filePath)) { + run.filesChanged.push(filePath); + } + } + } + } + if (e.kind === 'llm_call_end') { + const p = payloadOf(e); + const promptTokens = typeof p.promptTokens === 'number' ? p.promptTokens : 0; + const completionTokens = typeof p.completionTokens === 'number' ? p.completionTokens : 0; + run.totalTokens += promptTokens + completionTokens; + } } } diff --git a/src/worker.execute-job-gates.test.ts b/src/worker.execute-job-gates.test.ts new file mode 100644 index 0000000..cdec282 --- /dev/null +++ b/src/worker.execute-job-gates.test.ts @@ -0,0 +1,196 @@ +import { describe, expect, it, vi } from 'vitest'; +import { mkdirSync, mkdtempSync, writeFileSync } from 'fs'; +import { join } from 'path'; +import { tmpdir } from 'os'; +import { Worker } from './worker.js'; +import { userPiecesDir } from './user-folder/paths.js'; +import type { AppConfig } from './config.js'; +import type { Job } from './db/repository.js'; + +/** + * executeJob の pre-flight ゲート(切り出しリファクタ前のピン留めテスト)。 + * + * 対象: + * - MCP 認証ゲート: piece.required_mcp のトークンが無い場合、 + * status='waiting_human' + wait_reason='mcp_auth_required' で park する。 + * wait_reason の永続化が漏れるとジョブが沈黙して永久停止する既知の + * 失敗モードがあるため、パッチ内容を厳密に固定する。 + * - Model-mismatch requeue ゲート: piece が pin したモデルをこの worker が + * サーブできない場合、status='queued' + workerId=null で再キューする。 + * + * どちらも executeJob を実際に走らせ、リポジトリモックへの書き込みを検証する + * (Given-When-Then)。 + */ + +function makeConfig(overrides: Partial): AppConfig { + return { + provider: { + model: 'test-model', + workers: [{ id: 'worker-1', endpoint: 'http://localhost:11434/v1' }], + }, + worktreeDir: '/tmp/worker-gate-test', + concurrency: 1, + maxMovements: 30, + retry: { maxAttempts: 3, backoffSeconds: [60, 300, 900] }, + ask: { maxPerJob: 2 }, + subtasks: { maxDepth: 2 }, + tools: { + searxngUrl: 'http://localhost:8080', + visionModel: 'vision', + visionTimeout: 60, + visionMaxTokens: 1024, + webfetchTimeout: 30, + websearchTimeout: 15, + webfetchAllowedHosts: [], + }, + ...overrides, + } as AppConfig; +} + +function makeLocalTaskJob(overrides: Partial): Job { + return { + id: 'job-gate-1', + repo: 'local/task-77', + issueNumber: 77, + prNumber: null, + status: 'running', + pieceName: 'pin-gate-piece', + requiredRole: 'auto', + requiredProfile: 'auto', + currentMovement: null, + instruction: 'do something', + branchName: null, + worktreePath: null, + attempt: 1, + maxAttempts: 3, + nextRetryAt: null, + errorSummary: null, + resumeMovement: null, + askCount: 0, + workerId: 'worker-1', + parentJobId: null, + subtaskDepth: 0, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + ...overrides, + } as unknown as Job; +} + +function makeRepoMock() { + const updateJobCalls: Array<[string, Record]> = []; + const repo = { + lockIssue: vi.fn().mockResolvedValue(true), + unlockIssue: vi.fn().mockResolvedValue(undefined), + addAuditLog: vi.fn().mockResolvedValue(undefined), + updateWorkerNodeHealth: vi.fn().mockResolvedValue(undefined), + updateJob: vi.fn().mockImplementation((id: string, patch: Record) => { + updateJobCalls.push([id, patch]); + return Promise.resolve(undefined); + }), + getLocalTask: vi.fn().mockResolvedValue(null), + updateLocalTask: vi.fn().mockResolvedValue(undefined), + addLocalTaskComment: vi.fn().mockResolvedValue(undefined), + }; + return { repo, updateJobCalls }; +} + +/** Given: userFolderRoot 配下の local オーナー custom piece ディレクトリに piece を配置 */ +function writeCustomPiece(userFolderRoot: string, name: string, extraTopLevel: string): void { + const dir = userPiecesDir(userFolderRoot, 'local'); + mkdirSync(dir, { recursive: true }); + const yaml = [ + `name: ${name}`, + 'description: gate pinning test piece', + 'initial_movement: respond', + extraTopLevel, + 'movements:', + ' - name: respond', + ' persona: assistant', + ' instruction: answer the user', + ' default_next: COMPLETE', + ' rules: []', + '', + ].join('\n'); + writeFileSync(join(dir, `${name}.yaml`), yaml, 'utf-8'); +} + +describe('executeJob pre-flight gates (behaviour pinning)', () => { + it('parks the job as waiting_human with wait_reason=mcp_auth_required when a required MCP token is missing', async () => { + // Given: required_mcp を持つ custom piece と、MCP トークンを一切持たない worker + const root = mkdtempSync(join(tmpdir(), 'worker-gate-mcp-')); + const userFolderRoot = join(root, 'users'); + writeCustomPiece(userFolderRoot, 'pin-gate-piece', 'required_mcp:\n - github-mcp'); + const { repo, updateJobCalls } = makeRepoMock(); + const worker = new Worker( + 'worker-1', + 'http://localhost:11434/v1', + 'test-model', + repo as never, + makeConfig({ worktreeDir: join(root, 'worktree'), userFolderRoot }), + ); + + // When: executeJob を最後まで走らせる + await (worker as unknown as { executeJob: (job: Job) => Promise }).executeJob( + makeLocalTaskJob({}), + ); + + // Then: wait_reason='mcp_auth_required' を含む park パッチが永続化される + //(resume_movement は piece の initial_movement) + const parkCall = updateJobCalls.find(([, patch]) => patch.status === 'waiting_human'); + expect(parkCall).toBeDefined(); + expect(parkCall![1]).toEqual({ + status: 'waiting_human', + waitReason: 'mcp_auth_required', + resumeMovement: 'respond', + }); + // Then: ユーザーに system イベントコメントで連携を促す + expect(repo.addLocalTaskComment).toHaveBeenCalledWith( + 77, + 'system', + expect.stringContaining('github-mcp'), + 'event', + ); + // Then: 失敗扱いにはならず、issue ロックは finally で必ず解放される + expect(updateJobCalls.some(([, patch]) => patch.status === 'failed')).toBe(false); + expect(repo.unlockIssue).toHaveBeenCalledWith('local/task-77', 77); + }); + + it('requeues the job (status=queued, workerId=null) when the piece pins a model this worker cannot serve', async () => { + // Given: model を pin した custom piece と、そのモデルを持たない direct worker + const root = mkdtempSync(join(tmpdir(), 'worker-gate-model-')); + const userFolderRoot = join(root, 'users'); + writeCustomPiece(userFolderRoot, 'pin-gate-piece', 'model: exotic-model'); + const { repo, updateJobCalls } = makeRepoMock(); + const worker = new Worker( + 'worker-1', + 'http://localhost:11434/v1', + 'test-model', + repo as never, + makeConfig({ worktreeDir: join(root, 'worktree'), userFolderRoot }), + ); + // initialize() 相当: probed available models(exotic-model を含まない) + (worker as unknown as { availableModels: Set }).availableModels = new Set(['test-model']); + + // When + await (worker as unknown as { executeJob: (job: Job) => Promise }).executeJob( + makeLocalTaskJob({}), + ); + + // Then: queued に戻し worker を外す(他 worker が拾えるように) + const requeueCall = updateJobCalls.find(([, patch]) => patch.status === 'queued'); + expect(requeueCall).toBeDefined(); + expect(requeueCall![1]).toEqual({ + status: 'queued', + workerId: null, + errorSummary: 'Required model exotic-model is not available on worker-1', + }); + expect(repo.addAuditLog).toHaveBeenCalledWith('job-gate-1', 'job_requeued_model_mismatch', 'worker', { + workerId: 'worker-1', + requiredModel: 'exotic-model', + availableModels: ['test-model'], + }); + // Then: park はしない + expect(updateJobCalls.some(([, patch]) => patch.status === 'waiting_human')).toBe(false); + expect(repo.unlockIssue).toHaveBeenCalledWith('local/task-77', 77); + }); +}); diff --git a/src/worker.ts b/src/worker.ts index 5e713b8..e3300e0 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -21,6 +21,7 @@ import { ensureKeepaGraphs } from './engine/tools/amazon.js'; import type { McpTokenManager } from './mcp/token-manager.js'; import { mergeMcpConfig } from './mcp/config.js'; import { createStickyBackendResolver } from './worker/sticky-backend.js'; +import { mapPieceStatusToMetric, type JobMetricStatus } from './worker/piece-metric-status.js'; import { pickIdlerIndex } from './worker/idle-routing.js'; import { jobEventBus } from './bridge/job-events.js'; import { normalizeToolNameForMetric } from './metrics/tool-name-allowlist.js'; @@ -1187,6 +1188,979 @@ export class Worker { return true; } + /** + * task_kind='reflection' の実行パス(executeJob から委譲)。workspace 準備と + * agent / LLM ループをバイパスして固定ハンドラ (handleReflectionJob) を走らせる。 + * finally の health 更新 + issue unlock は agent パスの finally と同じ後始末。 + */ + private async runReflectionJobPath(job: Job): Promise { + const { repo: repoName, issueNumber } = job; + try { + await this.handleReflectionJob(job); + } finally { + await this.repo.updateWorkerNodeHealth(this.workerId, { + healthy: this.healthy, + lastError: this.lastHealthError, + inflightJobs: this.inflight, + availableModels: [...this.availableModels], + }); + await this.repo.unlockIssue(repoName, issueNumber); + } + } + + /** + * 進捗レポーターの runtime_dir 解決(executeJob から切り出し)。 + * 計画5 (Opus P1): reporter は logPath を構築時に固定するため、runtime_dir を + * 構築前に解決しておく必要がある。persistent スペースタスクは local_task の + * runtime_dir(space/{id}/runs/{taskId})を使い、reader(file API の logs + * セクション = logRoot(task))と同じ dir を指すことで writer/reader の + * split-brain を防ぐ。localTaskId / runtime_dir が無い ephemeral / legacy / + * gitea-issue では undefined となり、reporter は workspacePath/logs に + * フォールバックして従来と byte 一致する。 + */ + private async resolveReporterRuntime( + job: Job, + localTaskId: number | null, + ): Promise<{ + localTaskForRuntime: { runtimeDir?: string | null; workspaceMode?: string | null; spaceId?: string | null } | undefined; + reporterRuntimeDir: ReturnType; + }> { + const jobId = job.id; + let localTaskForRuntime: { runtimeDir?: string | null; workspaceMode?: string | null; spaceId?: string | null } | undefined; + if (localTaskId != null) { + try { + const lt = await this.repo.getLocalTask(localTaskId); + localTaskForRuntime = lt ?? undefined; + } catch (err) { + logger.warn(`[worker:${this.workerId}] job ${jobId} getLocalTask for reporter runtime_dir failed, falling back to workspace/logs: ${err}`); + } + } + const reporterRuntimeDir = resolveReporterRuntimeDir({ + jobRuntimeDir: job.runtimeDir, + localTaskRuntimeDir: localTaskForRuntime?.runtimeDir, + }); + return { localTaskForRuntime, reporterRuntimeDir }; + } + + /** + * Agent 実行パスの space / folder / runtime コンテキスト解決(executeJob から + * 切り出し)。piece のロード元 custom ディレクトリ、conflict detection の要否、 + * 実行ログ root、per-space MCP/SSH の実効 spaceId を確定する。ロジック・ + * 条件・順序は移動のみで不変(元コメントも保持)。 + */ + private async resolveRunSpaceContext( + job: Job, + localTaskForRuntime: { runtimeDir?: string | null; workspaceMode?: string | null; spaceId?: string | null } | undefined, + ): Promise<{ + folderContext: { rootDir: string; leafId: string } | undefined; + conflictDetection: boolean | undefined; + runtimeDir: string | undefined; + conflictDir: string | undefined; + spaceId: string | undefined; + customPieceDirs: string[]; + }> { + const jobId = job.id; + // Piece 読み込み: per-user カスタムディレクトリ → global カスタムディレクトリ → builtin の順に探索 + // No-auth jobs (ownerId null) resolve pieces from data/users/local/pieces, matching + // where no-auth POST now writes (LOCAL_OWNER='local' in pieces-api.ts). + const userFolderRoot = this.config.userFolderRoot ?? './data/users'; + const ownerForPieces = job.ownerId ?? 'local'; + // Spaces foundation: for LOCAL tasks, resolve the effective space folder + // (data/users/{owner} for personal / null space_id, or data/spaces/{id} + // for case spaces). Gitea-issue jobs (no local task id) stay undefined => + // legacy personal-folder resolution (backward compatible). + let folderContext: { rootDir: string; leafId: string } | undefined; + const localTaskIdForSpace = getLocalTaskId(job.repo); + // Spaces foundation (plan 3): enable conflict detection only for persistent + // (shared) local-task workspaces. Ephemeral / gitea-issue jobs leave it OFF + // (undefined) → legacy byte-identical Read/Write/Edit behaviour. + let conflictDetection: boolean | undefined; + // 計画5: タスクの実行ログ root。persistent スペースタスクは local_task の + // runtime_dir(space/{id}/runs/{taskId})、subtask は親から継承した + // jobs.runtime_dir を使う。どちらも無ければ undefined= runPiece / reporter が + // workspacePath/logs にフォールバック(ephemeral / legacy)。 + let runtimeDir: string | undefined = job.runtimeDir ?? undefined; + // 計画5 (spec §10.3): 競合台帳 dir。スペースタスクは space/{id}/.conflict + // (files の外)、それ以外は ToolContext 側で workspacePath/.conflict に + // フォールバック(後方互換)。undefined のまま渡してフォールバックさせる。 + let conflictDir: string | undefined; + // Spaces foundation: the space this run belongs to. Threaded into + // ToolContext.spaceId and CONSUMED for per-space MCP/SSH resolution + // (Phases 2-3). Resolved from the local task's space_id (preferred), but + // INITIALIZED from the trusted jobs.space_id (set server-side at enqueue) + // so a getLocalTask exception below degrades to the job's own space — + // NEVER fails open to the global NULL-space MCP/SSH set (Opus security P2). + // Undefined only for gitea-issue jobs / genuinely space-less tasks. + let spaceId: string | undefined = job.spaceId ?? undefined; + if (localTaskIdForSpace != null) { + try { + folderContext = await this.repo.resolveTaskFolderContext(localTaskIdForSpace, userFolderRoot); + } catch (err) { + logger.warn(`[worker:${this.workerId}] job ${jobId} resolveTaskFolderContext failed, using legacy personal folder: ${err}`); + } + // 計画5 (Opus P1): reporter 構築時に取得済みの local_task 行を再利用する + // (localTaskIdForSpace === localTaskId で同一行)。フェッチ失敗で未取得の + // 場合のみ防御的に再取得する。 + try { + const localTaskForMode = localTaskForRuntime ?? await this.repo.getLocalTask(localTaskIdForSpace); + conflictDetection = localTaskForMode?.workspaceMode === 'persistent'; + runtimeDir = localTaskForMode?.runtimeDir ?? undefined; + // Prefer the local task's space; fall back to the job's space_id. + spaceId = localTaskForMode?.spaceId ?? job.spaceId ?? undefined; + if (conflictDetection && localTaskForMode?.spaceId) { + conflictDir = spaceConflictDir(this.config.worktreeDir, localTaskForMode.spaceId); + } + } catch (err) { + logger.warn(`[worker:${this.workerId}] job ${jobId} getLocalTask for workspaceMode failed, conflict detection OFF: ${err}`); + } + } + // Personal-workspace tasks carry space_id=NULL, but the user registered + // their MCP/SSH servers against their real personal-space id. Mirror what + // resolveTaskFolderContext does for folders: resolve NULL+owner to the + // owner's personal space so per-space MCP/SSH enumeration AND the + // execute-time guard (aggregator.executeTool) both see those servers. + // Applied to EVERY job (not just the local-task branch) so spawned + // subtasks — which inherit the parent's NULL space and don't re-enter the + // block above — also resolve to the owner's personal space. Case-space + // jobs keep their id; no-auth jobs (no owner) stay NULL-space (legacy); + // a resolver failure fails closed (sentinel), never the global NULL set. + // See docs/superpowers/specs/2026-06-19-personal-space-mcp-resolution-design.md + spaceId = await resolveToolSpaceId(spaceId, job.ownerId, (o) => + this.repo.ensurePersonalSpace(o).then((s) => s.id), + ); + // Custom-piece load dir: prefer the resolved space folder when present. + const pieceRootDir = folderContext?.rootDir ?? userFolderRoot; + const pieceLeafId = folderContext?.leafId ?? ownerForPieces; + const customPieceDirs = [ + userPiecesDir(pieceRootDir, pieceLeafId), + this.config.customPiecesDir, + ].filter((d): d is string => !!d); + return { folderContext, conflictDetection, runtimeDir, conflictDir, spaceId, customPieceDirs }; + } + + /** + * Model-mismatch requeue gate (direct mode only — gateway routes by + * role, see shouldRequeueForModelMismatch). executeJob から切り出し。 + * true を返したら呼び出し側は即 return(ジョブは queued に戻し済み)。 + */ + private async requeueOnModelMismatch(job: Job, piece: PieceDef): Promise { + const jobId = job.id; + if ( + shouldRequeueForModelMismatch({ + isGateway: this.getWorkerDef().proxy === true, + pieceModel: piece.model, + availableModels: this.availableModels, + workerModel: this.model, + }) + ) { + await this.repo.updateJob(jobId, { + status: 'queued', + workerId: null, + errorSummary: `Required model ${piece.model} is not available on ${this.workerId}`, + }); + await this.repo.addAuditLog(jobId, 'job_requeued_model_mismatch', 'worker', { + workerId: this.workerId, + requiredModel: piece.model, + availableModels: [...this.availableModels], + }); + logger.info(`[worker:${this.workerId}] requeued job ${jobId} due to model mismatch (${piece.model})`); + return true; + } + return false; + } + + /** + * MCP 認証ゲート: piece.required_mcp に記載されたサーバーのトークンがなければ park。 + * executeJob から切り出し。wait_reason='mcp_auth_required' の永続化は + * job-recovery(トークン取得時の自動再キュー)の前提条件なので変更禁止。 + * true を返したら呼び出し側は即 return(ジョブは waiting_human で停車済み)。 + */ + private async parkOnMissingMcp(job: Job, piece: PieceDef, localTaskId: number | null): Promise { + const jobId = job.id; + const missingMcp = (piece.required_mcp ?? []).filter( + (serverId) => !this.mcpTokenManager || !this.mcpTokenManager.hasToken(job.ownerId ?? '', serverId), + ); + if (missingMcp.length > 0) { + await this.repo.updateJob(jobId, { + status: 'waiting_human', + waitReason: 'mcp_auth_required', + resumeMovement: piece.initial_movement ?? null, + }); + if (localTaskId !== null) { + await this.repo.addLocalTaskComment( + localTaskId, + 'system', + `この piece は MCP サーバー「${missingMcp.join(', ')}」との連携が必要です。Settings → MCP 接続から連携してください。`, + 'event', + ); + } + logger.info(`[worker:${this.workerId}] mcp gate parked job=${jobId} missing=${missingMcp.join(',')}`); + return true; + } + return false; + } + + /** + * このジョブ用の LLM クライアント構築(executeJob から切り出し)。 + * Gateway routes by role; direct resolves the worker's model. The + * resolver thunk runs only in direct mode (no auto-select via gateway). + */ + private createJobLlmClient( + job: Job, + piece: PieceDef, + reporter: LocalProgressReporter, + ): { llmClient: OpenAICompatClient; isProxyWorker: boolean } { + const workerDefForLlm = this.getWorkerDef(); + const isProxyWorker = workerDefForLlm.proxy === true; + const resolvedModel = llmRoutingKey({ + isGateway: isProxyWorker, + role: job.requiredRole, + resolveDirectModel: () => this.resolveModel(piece), + }); + const timeoutMs = (this.config.provider.timeoutMinutes ?? 10) * 60 * 1000; + const llmClient = new OpenAICompatClient( + this.endpoint, + resolvedModel, + workerDefForLlm.apiKey, + this.config.provider.retry, + timeoutMs, + this.contextLimitTokens, + this.config.safety?.promptGuardRatio, + (line) => reporter.reportPromptPreflight(line), + { proxy: isProxyWorker, maxStreamMs: this.resolveMaxStreamMs() }, + ); + return { llmClient, isProxyWorker }; + } + + /** + * runPiece に渡す piece 実行オプション(ASK 再開・割り込み注入・SpawnSubTask・ + * 未消化サブタスク検知)の構築。executeJob から切り出し(copy-and-delete)。 + * spawnSubTask 閉包の zombie guard / TOCTOU 再チェック / inheritJobContext に + * よる親→子継承(所有権・可視性・スペース・browser_session)は行単位で不変。 + */ + private buildPieceRunOptions(args: { + job: Job; + workspacePath: string; + isLocalTask: boolean; + localTaskId: number | null; + isSubTask: boolean; + reporter: LocalProgressReporter; + jobAbortController: AbortController; + cancelCheck: () => boolean; + }) { + const { job, workspacePath, isLocalTask, localTaskId, isSubTask, reporter, jobAbortController, cancelCheck } = args; + const jobId = job.id; + return { + resumeMovement: job.resumeMovement ?? undefined, + askCount: job.askCount, + maxAskPerJob: this.config.ask.maxPerJob, + checkInterjections: isLocalTask && localTaskId !== null && !isSubTask + ? async (movementName: string) => { + const comments = await this.repo.getUninjectedComments(localTaskId); + if (comments.length === 0) return []; + const injected = comments.map(c => ({ id: c.id, body: c.body })); + this.repo.markCommentsInjected(injected.map(c => c.id)); + reporter.reportInterjectionAck(injected, movementName); + return injected; + } + : undefined, + spawnSubTask: job.subtaskDepth < this.config.subtasks.maxDepth + ? async (params: { title: string; instruction: string; piece?: string }) => { + // Zombie guard: after a deadline/user abort — especially a ②B + // hard-kill that already ran cancelPendingChildren and marked the + // job terminal — a detached runPiece can still reach SpawnSubTask. + // Refuse to enqueue a NEW orphan child: nothing live will consume + // it, and it would burn a worker slot under an already-cancelled + // parent. The abort signal catches the in-grace window (terminal + // status not written yet); the status check catches post-hardkill. + if (!canSpawnSubtask(jobAbortController.signal.aborted, this.repo.getJobStatusSync(jobId) ?? '')) { + throw new Error('ジョブが終了またはキャンセルされたため、新しいサブタスクは作成できません。'); + } + const subJobs = await this.repo.getSubJobs(jobId); + const subtaskIndex = subJobs.length + 1; + if (subJobs.length >= this.config.subtasks.maxPerParent) { + throw new Error(`サブタスク上限 (${this.config.subtasks.maxPerParent}) に達しました。これ以上のサブタスクは作成できません。`); + } + const subtaskWorkspace = join(workspacePath, 'subtasks', String(subtaskIndex)); + mkdirSync(subtaskWorkspace, { recursive: true }); + mkdirSync(join(subtaskWorkspace, 'output'), { recursive: true }); + mkdirSync(join(subtaskWorkspace, 'logs'), { recursive: true }); + // 計画5 (Opus P1): サブタスクは親と分離した自前の isolated worktree + // (subtaskWorkspace)で実行されるため、その activity.log は共有されず + // resume poisoning の対象にならない。よって runtime_dir はネストせず + // 未設定のままにし、reporter を subtaskWorkspace/logs にフォールバック + // させる。これは subtask の reader(subtask-activity-api / + // subtask-files-api が worktreePath/logs を読む)と byte 一致し、 + // writer/reader の split-brain を防ぐ。 + + // 親ジョブの role を継承 + const subJobInstruction = [ + `ui_profile: ${job.requiredRole}`, + '', + `# ${params.title}`, + '', + params.instruction, + ].join('\n'); + + const subJob = await this.repo.createJob({ + repo: `subtask/${jobId}`, + issueNumber: subtaskIndex, + instruction: subJobInstruction, + pieceName: params.piece ?? 'general', + parentJobId: jobId, + subtaskDepth: job.subtaskDepth + 1, + maxAttempts: 2, + role: job.requiredRole, + // 親ジョブから所有権・可視性・スペース・ブラウザセッションのバインドを + // 継承する(spec §5.7 のスペース継承 + 保存セッションの引き継ぎ)。 + // browserSessionProfileId が無いとサブタスク内の BrowseWeb が + // 保存セッション (ログイン Cookie) を使えず未ログインでアクセスする。 + ...inheritJobContext(job), + // 計画5 (Opus P1): サブタスクは isolated worktree で実行され共有 + // されないため runtime_dir はネストしない(reporter は + // subtaskWorkspace/logs にフォールバックし reader と一致する)。 + runtimeDir: null, + }); + await this.repo.updateJob(subJob.id, { worktreePath: subtaskWorkspace }); + // TOCTOU close: the abort/hard-kill can land between the entry + // guard and this insert — finalizeHardkill's cancelPendingChildren + // reads getSubJobs *before* this child row exists and so misses it. + // Re-check now: if the parent is no longer live, cancel the child we + // just created (it would otherwise run as an orphan under a + // cancelled parent) and refuse the spawn. finalizeHardkill writes + // the terminal status before cancelPendingChildren, so a status read + // here reliably reflects an in-flight hard-kill. + if (!canSpawnSubtask(jobAbortController.signal.aborted, this.repo.getJobStatusSync(jobId) ?? '')) { + await this.repo.updateJob(subJob.id, { status: 'cancelled' }); + logger.info(`[worker:${this.workerId}] cancelled just-spawned sub-task #${subtaskIndex} job=${subJob.id} (parent no longer live)`); + throw new Error('ジョブが終了またはキャンセルされたため、新しいサブタスクは作成できません。'); + } + logger.info(`[worker:${this.workerId}] spawned sub-task #${subtaskIndex} depth=${job.subtaskDepth + 1} job=${subJob.id}`); + // ④ spawn stagger: brief delay so a burst of N SpawnSubTask enqueues + // doesn't all land in the same instant and jump GPU-slot priority over + // other queued work. Throttle only — NOT a completion-wait. Honors + // cancel so a cancelled parent doesn't sit sleeping. + if (this.config.subtasks.spawnStaggerMs > 0) { + await sleepWithCancel(this.config.subtasks.spawnStaggerMs, cancelCheck); + } + return { jobId: subJob.id, subtaskIndex, workspacePath: subtaskWorkspace }; + } + : undefined, + // WaitSubTask / auto-park support: report whether this job has unwaited + // pending children, and cancel them when the parent ends without consuming + // them (orphan prevention). Both use getSubJobs (latest revision per index). + hasPendingSubtasks: async (): Promise => { + const subs = await this.repo.getSubJobs(jobId); + return subs.some((s) => !SUBTASK_DB_TERMINAL.includes(s.status)); + }, + }; + } + + /** + * Browser session profile binding(executeJob から切り出し)。 + * If this job is bound to a browser_session_profile, decrypt the + * captured Playwright storageState and pass it into runPiece so + * BrowseWeb can inject it into BrowserContext. Owner-mismatch and + * expired-profile checks fail-fast before the agent loop starts + * (throw はそのまま executeJob の catch → scheduleRetryOrFail に伝播)。 + */ + private resolveBrowserSessionBinding( + job: Job, + localTaskId: number | null, + ): { + browserSessionState: object | undefined; + browserSessionProfileId: number | undefined; + browserSessionProfile: { loggedInSelector: string | null; loginUrlPatterns: string[] } | undefined; + onAuthExpired: ((profileId: number, reason: string) => void) | undefined; + } { + let browserSessionState: object | undefined; + let browserSessionProfileId: number | undefined; + let browserSessionProfile: + | { loggedInSelector: string | null; loginUrlPatterns: string[] } + | undefined; + let onAuthExpired: + | ((profileId: number, reason: string) => void) + | undefined; + + if (job.browserSessionProfileId) { + const sessRepo = new BrowserSessionRepo(this.repo.getDb()); + const profile = sessRepo.getProfileByIdUnsafe(job.browserSessionProfileId); + if (!profile) { + sessRepo.audit({ + actorUserId: job.ownerId ?? null, + ownerId: null, + profileId: job.browserSessionProfileId, + action: 'use', + result: 'error', + reason: 'profile not found', + jobId: job.id, + }); + throw new Error(`Browser session profile ${job.browserSessionProfileId} not found`); + } + // Fail-closed owner check: a job with null/missing ownerId must not + // be allowed to decrypt any profile, even if the profile id would + // otherwise resolve. Helper audits + throws on rejection. Extracted + // to src/engine/browser-session-auth.ts so the contract is unit + // tested in isolation from the Worker class. + assertProfileOwner(profile, job, sessRepo); + if (profile.status !== 'active' || !profile.encryptedStateBlob) { + sessRepo.audit({ + actorUserId: job.ownerId, ownerId: profile.ownerId, profileId: profile.id, + action: 'use', result: 'error', reason: `status=${profile.status}`, jobId: job.id, + }); + throw new Error(`AUTH_SESSION_EXPIRED: profile ${profile.id} status=${profile.status}`); + } + const masterKeyPath = this.config.secrets?.masterKeyPath ?? './data/secrets/master.key'; + // Space-as-Principal P2: space-owned profiles decrypt under the space DEK + // (with owner-DEK fallback for pre-migration blobs); personal under owner DEK. + let stateJson: string; + try { + stateJson = decryptProfileState({ masterKeyPath, sessRepo }, { + ownerId: profile.ownerId, + spaceId: profile.spaceId, + encryptedStateBlob: profile.encryptedStateBlob, + }); + } catch (e) { + sessRepo.audit({ + actorUserId: job.ownerId, + ownerId: profile.ownerId, + profileId: profile.id, + action: 'decrypt', + result: 'error', + reason: `state decrypt failed: ${(e as Error).message}`, + jobId: job.id, + }); + throw e; + } + browserSessionState = JSON.parse(stateJson) as object; + browserSessionProfileId = profile.id; + browserSessionProfile = { + loggedInSelector: profile.loggedInSelector, + loginUrlPatterns: profile.loginUrlPatterns, + }; + onAuthExpired = (pid, reason) => { + sessRepo.markProfileStatus(pid, 'expired', reason); + sessRepo.audit({ + actorUserId: job.ownerId, ownerId: profile.ownerId, profileId: pid, + action: 'expire', result: 'success', reason, jobId: job.id, + }); + // Best-effort task-level notification. Subtask jobs and + // gitea-issue jobs may not have a numeric local_task id. + if (localTaskId !== null) { + this.repo.addLocalTaskComment( + localTaskId, + 'agent', + `⚠️ Browser session "${profile.label}" expired: ${reason}. Re-login from Settings → Browser Sessions.`, + 'progress', + ).catch(() => { /* ignore — comment posting is best-effort */ }); + } + }; + sessRepo.audit({ + actorUserId: job.ownerId, ownerId: profile.ownerId, profileId: profile.id, + action: 'use', result: 'success', jobId: job.id, + }); + sessRepo.touchUsed(profile.id); + } + return { browserSessionState, browserSessionProfileId, browserSessionProfile, onAuthExpired }; + } + + /** + * LLM に渡す指示コンテキストの組み立て(executeJob から切り出し)。 + * + * Piece handoff: when this job continues an earlier one in the same + * local_task, agent-loop injects a "this is a continuation of piece X" + * block into the system prompt. We resolve the prev piece name + the + * most recent agent result/ask comment as the LLM-visible carry-over. + * + * 会話コンテキスト P3: この継続ジョブが transcript を再生する場合 + * (handoffContext あり && 再生可能ターンあり = piece-runner の再生条件と完全一致)、 + * 再生した実ターンと重複するコメント要約ブロックを抑制する。transcriptPath は + * runPiece に渡す runtimeDir から算出するため piece-runner の logsRoot と一致し、 + * 抑制判定と再生判定の split-brain が構造的に起きない。 + */ + private async buildInstructionContext( + job: Job, + workspacePath: string, + localTaskId: number | null, + runtimeDir: string | undefined, + ): Promise<{ + handoffContext: import('./engine/agent-loop.js').HandoffContext | undefined; + transcriptPath: string; + enrichedInstruction: string; + }> { + const jobId = job.id; + const isLocalTask = localTaskId !== null; + let handoffContext: import('./engine/agent-loop.js').HandoffContext | undefined; + if (job.continuedFromJobId && isLocalTask && localTaskId !== null) { + const prevJob = await this.repo.getJob(job.continuedFromJobId); + if (prevJob) { + const prevResultComment = await this.repo.getLatestResultComment(localTaskId); + handoffContext = { + prevPiece: prevJob.pieceName, + prevResult: prevResultComment?.body ?? null, + }; + } else { + logger.warn(`[worker:${this.workerId}] continued_from_job_id=${job.continuedFromJobId} not found for job ${jobId}; skipping handoff context`); + } + } + + const transcriptPath = join(runtimeDir ?? join(workspacePath, 'logs'), 'transcript.jsonl'); + const willReplayTranscript = + handoffContext !== undefined && Conversation.hasReplayableTranscript(transcriptPath); + + let enrichedInstruction = `${buildTimeContextBlock()}${job.instruction}`; + if (isLocalTask) { + try { + const comments = await this.repo.listLocalTaskComments(localTaskId); + const outputFiles = this.listDir(join(workspacePath, 'output')); + const inputFiles = this.listDir(join(workspacePath, 'input')); + const contextBody = buildLocalConversationContext({ + comments, + jobInstruction: job.instruction, + inputFiles, + outputFiles, + omitRecentConversation: willReplayTranscript, + }); + enrichedInstruction = `${buildTimeContextBlock()}${contextBody}`; + } catch (err) { + logger.warn(`[worker:${this.workerId}] failed to build local context: ${err}`); + } + } + const retryHandoffContext = buildRetryHandoffContext(workspacePath, job); + if (retryHandoffContext) { + enrichedInstruction = `${enrichedInstruction}\n\n${retryHandoffContext}`; + } + return { handoffContext, transcriptPath, enrichedInstruction }; + } + + /** + * Parse per-task options from job payload (e.g. { options: { mcpDisabled, skillsDisabled } }). + * executeJob から切り出し。パース失敗は warn のみ(従来どおりジョブは続行)。 + */ + private resolveJobPayloadOptions(job: Job): { mcpDisabled: boolean; skillsDisabled: boolean } { + const jobId = job.id; + let jobPayloadOptions: Record = {}; + if (job.payload) { + try { + const parsed = JSON.parse(job.payload) as Record; + if (parsed?.options && typeof parsed.options === 'object' && !Array.isArray(parsed.options)) { + jobPayloadOptions = parsed.options as Record; + } + } catch { + logger.warn(`[worker:${this.workerId}] job ${jobId} failed to parse payload JSON`); + } + } + const mcpDisabled = jobPayloadOptions.mcpDisabled === true; + const skillsDisabled = jobPayloadOptions.skillsDisabled === true; + return { mcpDisabled, skillsDisabled }; + } + + /** + * Resolve the job owner's role to gate admin-only tool actions + * (e.g. system-scope InstallSkill). Defaults to 'user' when the owner + * is unknown (no-auth mode) or the lookup fails. executeJob から切り出し。 + */ + private resolveJobUserRole(job: Job): 'admin' | 'user' { + const jobId = job.id; + let userRole: 'admin' | 'user' = 'user'; + if (job.ownerId) { + try { + const ownerRow = this.repo.getUserById(job.ownerId); + if (ownerRow?.role === 'admin') userRole = 'admin'; + } catch (err) { + logger.warn(`[worker:${this.workerId}] job ${jobId} owner role lookup failed: ${(err as Error).message}`); + } + } + return userRole; + } + + /** + * Workspace tool policy + SSH 接続スコープの解決(executeJob から切り出し)。 + * + * Workspace tool policy: resolve once per job, applied to every movement. + * Uses the already-resolved spaceId (personal-space NULL→real-id handled + * by resolveToolSpaceId in resolveRunSpaceContext). Fail-closed: any error → + * safe defaults (sensitive tools off). Never throws — a policy error must + * not crash a job. + * + * SSH workspace scoping: when the workspace policy enables the 'ssh' category, + * resolve the connection IDs available to this job's space. This space-scoped + * list is the SOLE SSH connection gate (piece-level allowed_ssh_connections was + * removed in PR B), mirroring how MCP uses listEnabledForSpace. spaceId is + * already personal-space-resolved (NULL→real-id), so personal-WS jobs find + * their connections via listBySpace(personalSpaceId). NEVER '*' wildcard — + * always an explicit id list so cross-space isolation is guaranteed. + */ + private async resolveWorkspaceToolContext( + job: Job, + spaceId: string | undefined, + ): Promise<{ + workspaceTools: { allowedTools: string[]; editAllowed: boolean }; + workspaceSshConnections: string[] | undefined; + }> { + const jobId = job.id; + let workspaceTools: { allowedTools: string[]; editAllowed: boolean }; + let parsedPolicy: import('./engine/workspace-tool-policy.js').WorkspaceToolPolicy; + try { + const policyJson = spaceId != null ? this.repo.getSpaceToolPolicy(spaceId) : null; + parsedPolicy = parseToolPolicy(policyJson); + workspaceTools = await resolveWorkspaceTools(parsedPolicy); + } catch (err) { + logger.warn(`[worker:${this.workerId}] job ${jobId} workspace tool policy resolution failed, using safe defaults: ${(err as Error).message}`); + parsedPolicy = {}; + workspaceTools = await resolveWorkspaceTools({}); + } + + let workspaceSshConnections: string[] | undefined; + if (parsedPolicy.enabledSensitive?.includes('ssh') && spaceId != null) { + try { + const sshSub = getSshSubsystem(); + if (sshSub !== null) { + workspaceSshConnections = sshSub.connectionRepo.listBySpace(spaceId).map((c) => c.id); + logger.debug(`[worker:${this.workerId}] job ${jobId} ssh workspace connections resolved count=${workspaceSshConnections.length} spaceId=${spaceId}`); + } else { + // SSH subsystem not initialised (e.g. test environment without bridge setup). + // Leave undefined → piece-runner treats it as [] (no connections → SSH rejects). + logger.debug(`[worker:${this.workerId}] job ${jobId} ssh enabled in policy but subsystem not initialised — no connections available`); + } + } catch (err) { + logger.warn(`[worker:${this.workerId}] job ${jobId} ssh connection resolution failed: ${(err as Error).message}`); + // Leave undefined → piece-runner treats it as [] (fail-closed, no connections). + } + } + return { workspaceTools, workspaceSshConnections }; + } + + /** + * runPiece の起動(executeJob から切り出し)。executeJob 側で解決済みの + * 実行コンテキストを束ねて runPiece の options に配線する。await はせず + * Promise を返す — 呼び出し側が ②B hard-kill レース (raceWithGrace) に + * かけるため。options の各項目・ゲート条件・コメントは行単位で不変。 + */ + private startPieceRun(args: { + job: Job; + piece: PieceDef; + enrichedInstruction: string; + llmClient: OpenAICompatClient; + workspacePath: string; + callbacks: PieceRunCallbacks; + toolsConfig: AppConfig['tools']; + pieceOptions: ReturnType; + cancelCheck: () => boolean; + jobAbortController: AbortController; + customPieceDirs: string[]; + folderContext: { rootDir: string; leafId: string } | undefined; + spaceId: string | undefined; + sessionIdentity: Awaited>; + conflictDetection: boolean | undefined; + runtimeDir: string | undefined; + conflictDir: string | undefined; + contextManager: ContextManager; + workerDef: WorkerDef; + handoffContext: import('./engine/agent-loop.js').HandoffContext | undefined; + transcriptPath: string; + isLocalTask: boolean; + localTaskId: number | null; + isSubTask: boolean; + parentJobId: string | null; + browserSessionState: object | undefined; + browserSessionProfileId: number | undefined; + browserSessionProfile: { loggedInSelector: string | null; loginUrlPatterns: string[] } | undefined; + onAuthExpired: ((profileId: number, reason: string) => void) | undefined; + mcpDisabled: boolean; + skillsDisabled: boolean; + userRole: 'admin' | 'user'; + workspaceTools: { allowedTools: string[]; editAllowed: boolean }; + workspaceSshConnections: string[] | undefined; + }): Promise { + const { + job, + piece, + enrichedInstruction, + llmClient, + workspacePath, + callbacks, + toolsConfig, + pieceOptions, + cancelCheck, + jobAbortController, + customPieceDirs, + folderContext, + spaceId, + sessionIdentity, + conflictDetection, + runtimeDir, + conflictDir, + contextManager, + workerDef, + handoffContext, + transcriptPath, + isLocalTask, + localTaskId, + isSubTask, + parentJobId, + browserSessionState, + browserSessionProfileId, + browserSessionProfile, + onAuthExpired, + mcpDisabled, + skillsDisabled, + userRole, + workspaceTools, + workspaceSshConnections, + } = args; + const { id: jobId, issueNumber } = job; + return runPiece(piece, enrichedInstruction, llmClient, workspacePath, callbacks, toolsConfig, { + ...pieceOptions, + cancelCheck, + abortController: jobAbortController, + safetyConfig: this.config.safety, + searchFilter: this.config.searchFilter, + customPiecesDir: customPieceDirs, + // Spaces foundation: resolved space folder for AGENTS.md / memory / + // custom-piece resolution. Undefined for gitea-issue jobs => legacy. + folderContext, + // Spaces foundation: space id for future per-space MCP/SSH resolution. + // Phase 1 no-op (no consumer reads ctx.spaceId yet). + spaceId, + // Per-space Python package overlay (admin-installed wheels). Resolve the + // stable `current` symlink path so Bash can bind it read-only + expose it + // on PYTHONPATH. undefined when the feature is disabled; the Bash tool + // also no-ops if the path does not exist yet (nothing installed). + pythonPackagesDir: (() => { + const pk = resolvePythonPackagesConfig(this.config.pythonPackages); + if (!pk.enabled) return undefined; + return spaceCurrentDir(pk.dir, spaceKeyFor(spaceId, sessionIdentity.userId ?? job.ownerId ?? 'local')); + })(), + // Spaces foundation (plan 3): conflict detection for persistent local-task + // workspaces. Undefined (OFF) for ephemeral / gitea-issue jobs => legacy. + conflictDetection, + // 計画5: 実行ログ root + 競合台帳 dir。スペースタスクのみ非 undefined。 + // ephemeral / legacy は undefined = workspacePath/logs・workspacePath/.conflict(後方互換)。 + runtimeDir, + conflictDir, + contextManager, + vlmEnabled: workerDef.vlm === true, + jobId, + handoffContext, + // When this run IS a subtask, pass parent identity + child workspace + // path so the runner records the lineage in its run-start event. + // Subtask workspaces follow `/subtasks/` where N is the + // subtask job's issueNumber. + parentJobId: isSubTask && parentJobId ? parentJobId : undefined, + childWorkspaceRelative: isSubTask ? `subtasks/${issueNumber}` : undefined, + // Mission Brief: only wire IO when this run is bound to a local + // task (the brief is per-LocalTask, not per-job). Subtask runs + // and gitea-issue runs leave it unset → MissionUpdate degrades + // to a no-op and the system prompt MISSION block is skipped. + missionBrief: isLocalTask && localTaskId !== null && !isSubTask + ? this.repo.makeMissionBriefIO(localTaskId) + : undefined, + // Conversation recall: bind SearchTaskConversation / ReadTaskConversation + // to this local task's comments + this run's transcript for top-level + // local tasks; sub-tasks get a transcript-only variant (see the isSubTask + // branch below). gitea-issue runs leave it unset so the tools degrade + // gracefully. + // + // SECURITY (adversarial review): bind the transcript ONLY to a per-task + // `runtimeDir` (space/{spaceId}/runs/{taskId}). The old + // `runtimeDir ?? workspacePath/logs` fallback is UNSAFE for persistent + // space tasks — workspacePath is the space-SHARED files tree, so a + // NULL-runtime_dir task would expose another (possibly different-user) + // task's transcript in the same space. When runtimeDir is unset we pass + // no transcript path: comment search (always task_id-scoped) still works, + // transcript search simply returns nothing rather than risk a leak. + taskConversation: isLocalTask && localTaskId !== null && !isSubTask + ? this.repo.makeTaskConversationIO( + localTaskId, + runtimeDir ? join(runtimeDir, 'transcript.jsonl') : undefined, + ) + // サブタスクは backing local_task を持たない(comments 無し)が、自分の + // transcript は subtaskWorkspace/logs に書かれる。上で算出済みの + // transcriptPath(piece-runner の logsRoot と同一式)をそのまま再利用し、 + // writer/reader の drift を防ぐ。上の SECURITY 注記が禁じる workspacePath/logs + // フォールバックは「永続スペースの共有 workspacePath」が対象で、サブタスクの + // workspacePath は subtasks/ の隔離ディレクトリなので安全(他タスクの + // transcript には届かない)。workspaceTaskSearch は配線しない(他タスク横断は + // サブタスクに開かない — 設計 §2 非ゴール)。 + : isSubTask + ? this.repo.makeTranscriptOnlyConversationIO(transcriptPath) + : undefined, + // Workspace-wide task search: same gate as taskConversation, plus an + // owner id (needed to scope the search to the owner's space). In + // no-auth mode job.ownerId is null, so this is undefined and the tool + // degrades to a no-context message rather than searching unscoped. + workspaceTaskSearch: isLocalTask && localTaskId !== null && !isSubTask && job.ownerId + ? this.repo.makeWorkspaceTaskSearchIO(localTaskId, job.ownerId) + : undefined, + // File provenance: bind reads to THIS run's workspace_path (the scoping + // boundary — a shared space workspace is queried only for its own rows, + // never another workspace's / user's lineage) and a recorder bound to + // this task/job/space. Gated on a resolvable local task id (need a + // numeric created_by_task_id). Subtask / gitea-issue runs leave both + // unset → the agent tools degrade to a no-op and Write/Edit/Bash skip + // recording. + fileProvenance: + isLocalTask && localTaskId !== null + ? this.repo.makeFileProvenanceIO(workspacePath) + : undefined, + recordFileProvenance: + isLocalTask && localTaskId !== null + ? (ev) => + this.repo.recordProvenance({ + workspacePath, + relPath: ev.relPath, + event: ev.event, + sourceKind: ev.sourceKind, + checksum: ev.checksum ?? null, + taskId: localTaskId, + jobId, + spaceId: spaceId ?? null, + piece: ev.pieceName, + movement: ev.movementName, + }) + : undefined, + taskId: sessionIdentity.taskId, + userId: sessionIdentity.userId, + // Tool-request mechanism: per-task grant overlay, read fresh each run so + // a resume after inline approval picks up the newly-granted tool. + grantedTools: + isLocalTask && localTaskId !== null ? this.repo.getGrantedTools(String(localTaskId)) : undefined, + // Workspace tool policy: pre-resolved once per job above (fail-closed). + // Drives every movement's effective allowed tools and edit flag. + workspaceTools, + // SSH workspace scoping: connection IDs available to this job's space. + // Undefined when ssh not enabled in policy (movement declaration is fallback). + workspaceSshConnections, + // Pause for inline approval only when a user is reachable: a local task + // that is not itself a subtask (subtasks/headless runs auto-proceed). + toolApprovalInteractive: isLocalTask && !isSubTask, + // Tool-request mechanism: bind the recorder to this run's task/job/space. + // piece + movement are injected upstream by piece-runner. + recordToolRequest: (req) => + this.repo.recordToolRequest({ + ...req, + // Use the local task id (same key as granted_tools + the decide API) + // so the task-detail query and grant overlay line up. + taskId: localTaskId !== null ? String(localTaskId) : null, + jobId, + spaceId: spaceId ?? null, + }), + // Package-request mechanism (RequestPackage): bind the recorder + + // installed/declined state ONLY when the feature is enabled AND this run + // has a resolvable space. Approval installs into spaces.python_packages + // (keyed by space id) and the resumed job's overlay key is + // spaceKeyFor(spaceId) = spaceId, so the two must agree — hence the hard + // space requirement. No space => the tool reports "unavailable". + ...(() => { + const pk = resolvePythonPackagesConfig(this.config.pythonPackages); + if (!pk.enabled || spaceId == null) return {}; + // Installed set is name→spec (version-aware): a pinned request is only + // "already installed" on an exact spec match; a bare-name request + // matches any installed version. + let installedPackages: Array<{ name: string; spec: string }> = []; + try { + const raw = this.repo.getSpacePythonPackages(spaceId); + if (raw) { + const parsed = JSON.parse(raw) as { packages?: Array<{ name?: unknown; spec?: unknown }> }; + if (Array.isArray(parsed.packages)) { + installedPackages = parsed.packages + .map((p) => (typeof p?.name === 'string' && typeof p?.spec === 'string' ? { name: p.name, spec: p.spec } : null)) + .filter((p): p is { name: string; spec: string } => p !== null); + } + } + } catch { + // best-effort: a malformed desired-state must not fail the job. + } + // Names already declined for THIS job (deny/auto_deny) → RequestPackage + // short-circuits a re-request instead of re-parking (deny loop guard). + const declinedPackages = this.repo.listDeclinedPackageNamesByJob(jobId); + return { + installedPackages, + declinedPackages, + recordPackageRequest: (req: { + spec: string; + normalizedName: string; + reason?: string | null; + status?: 'pending' | 'approved' | 'denied' | 'auto_denied'; + pieceName?: string | null; + movementName?: string | null; + }) => + this.repo.recordPackageRequest({ + ...req, + taskId: localTaskId !== null ? String(localTaskId) : null, + jobId, + spaceId, + }), + }; + })(), + browserSessionState, + browserSessionProfileId, + browserSessionProfile, + onAuthExpired, + ownerId: job.ownerId, + mcpConfig: mergeMcpConfig(this.config.mcp), + userRole, + skillCatalog: this.skillCatalog ?? undefined, + mcpDisabled, + skillsDisabled, + }); + } + + /** + * piece 実行の完了処理(executeJob から切り出し)。②B hard-kill レース → + * 実行診断の書き出し → handlePieceResult(ジョブライフサイクル遷移・park + * 永続化・reflection 起票を含む)→ メトリクス status 確定、の順序は不変。 + * + * metricFinalStatus は setter 経由で書き込む: hard-kill 時は finalizeHardkill + * の【前】に 'cancelled' をセット(finalizeHardkill が throw しても finally の + * メトリクスが 'cancelled' で記録される従来挙動を保持)、通常時は + * handlePieceResult の【後】にセット(handlePieceResult が throw したら + * 'error' のまま catch に渡る従来挙動を保持)。hard-kill 時はここで処理を + * 打ち切って戻る(呼び出し側 try ブロックの末尾なので従来の early return と + * 同一の後続フロー = finally のみ実行)。 + */ + private async settlePieceRun(args: { + piecePromise: Promise; + jobAbortController: AbortController; + job: Job; + reporter: LocalProgressReporter; + workspacePath: string; + isLocalTask: boolean; + isSubTask: boolean; + parentJobId: string | null; + setMetricFinalStatus: (s: JobMetricStatus) => void; + }): Promise { + const { piecePromise, jobAbortController, job, reporter, workspacePath, isLocalTask, isSubTask, parentJobId, setMetricFinalStatus } = args; + const jobId = job.id; + // ②B deadline hard-kill safety net. Race the piece run against a grace + // timer that arms when the job's abort signal fires (deadline OR cancel). + // ②A wires the abort into the realistic blocking tools so a run normally + // unwinds cooperatively; if it does not within the grace window, hard-kill + // so the inflight slot is freed and the job reaches a terminal state. The + // detached run keeps executing in-process (accepted zombie residual). + // deadlineGraceSeconds <= 0 disables the ②B hard-kill fallback: await the + // run directly and rely on ②A's cooperative abort (matches the documented + // "0 disables" semantics, same convention as maxJobMinutes=0). + const graceSec = this.config.safety?.deadlineGraceSeconds ?? DEFAULT_DEADLINE_GRACE_SECONDS; + const raced = graceSec > 0 + ? await raceWithGrace(piecePromise, jobAbortController.signal, graceSec * 1000) + : { ok: true as const, value: await piecePromise }; + if (!raced.ok) { + logger.warn(`[worker:${this.workerId}] job ${jobId} did not unwind within grace=${graceSec}s after abort; hard-killing to release slot (deadline=${raced.deadline})`); + setMetricFinalStatus('cancelled'); + await this.finalizeHardkill(job, reporter, isSubTask, parentJobId, raced.deadline); + return; + } + const result = raced.value; + logger.info(`[worker:${this.workerId}] job ${jobId} runPiece done: status=${result.status}`); + this.writeRunDiagnostics(workspacePath, result); + + await this.handlePieceResult(result, job, reporter, workspacePath, isLocalTask, isSubTask, parentJobId); + + setMetricFinalStatus(mapPieceStatusToMetric(result.status)); + } + private async executeJob(job: Job): Promise { const { repo: repoName, issueNumber, id: jobId } = job; const localTaskId = getLocalTaskId(repoName); @@ -1231,17 +2205,7 @@ export class Worker { // Reflection jobs bypass workspace preparation and the agent / LLM loop. // task_kind='agent' (default) keeps the pre-existing piece-runner path. if (job.taskKind === 'reflection') { - try { - await this.handleReflectionJob(job); - } finally { - await this.repo.updateWorkerNodeHealth(this.workerId, { - healthy: this.healthy, - lastError: this.lastHealthError, - inflightJobs: this.inflight, - availableModels: [...this.availableModels], - }); - await this.repo.unlockIssue(repoName, issueNumber); - } + await this.runReflectionJobPath(job); return; } @@ -1250,26 +2214,7 @@ export class Worker { // 進捗レポーター // ローカルタスク・サブタスクともに activity.log を書き出す // サブタスクでは isSubTask=true を渡し、DB コメント書き込みをスキップする - // 計画5 (Opus P1): reporter は logPath を構築時に固定するため、runtime_dir を - // 構築前に解決しておく必要がある。persistent スペースタスクは local_task の - // runtime_dir(space/{id}/runs/{taskId})を使い、reader(file API の logs - // セクション = logRoot(task))と同じ dir を指すことで writer/reader の - // split-brain を防ぐ。localTaskId / runtime_dir が無い ephemeral / legacy / - // gitea-issue では undefined となり、reporter は workspacePath/logs に - // フォールバックして従来と byte 一致する。 - let localTaskForRuntime: { runtimeDir?: string | null; workspaceMode?: string | null; spaceId?: string | null } | undefined; - if (localTaskId != null) { - try { - const lt = await this.repo.getLocalTask(localTaskId); - localTaskForRuntime = lt ?? undefined; - } catch (err) { - logger.warn(`[worker:${this.workerId}] job ${jobId} getLocalTask for reporter runtime_dir failed, falling back to workspace/logs: ${err}`); - } - } - const reporterRuntimeDir = resolveReporterRuntimeDir({ - jobRuntimeDir: job.runtimeDir, - localTaskRuntimeDir: localTaskForRuntime?.runtimeDir, - }); + const { localTaskForRuntime, reporterRuntimeDir } = await this.resolveReporterRuntime(job, localTaskId); const reporter = new LocalProgressReporter(this.repo, localTaskId ?? issueNumber, workspacePath, logMetadata, isSubTask, reporterRuntimeDir); // 会話コンテキスト (enrichedInstruction) は handoffContext / runtimeDir 解決後に @@ -1281,256 +2226,38 @@ export class Worker { // 中断の即応 + ジョブ全体のハードデッドライン(fix: runaway stream cancel/reaper) let jobGuardTimer: ReturnType | undefined; try { - // Piece 読み込み: per-user カスタムディレクトリ → global カスタムディレクトリ → builtin の順に探索 - // No-auth jobs (ownerId null) resolve pieces from data/users/local/pieces, matching - // where no-auth POST now writes (LOCAL_OWNER='local' in pieces-api.ts). - const userFolderRoot = this.config.userFolderRoot ?? './data/users'; - const ownerForPieces = job.ownerId ?? 'local'; - // Spaces foundation: for LOCAL tasks, resolve the effective space folder - // (data/users/{owner} for personal / null space_id, or data/spaces/{id} - // for case spaces). Gitea-issue jobs (no local task id) stay undefined => - // legacy personal-folder resolution (backward compatible). - let folderContext: { rootDir: string; leafId: string } | undefined; - const localTaskIdForSpace = getLocalTaskId(job.repo); - // Spaces foundation (plan 3): enable conflict detection only for persistent - // (shared) local-task workspaces. Ephemeral / gitea-issue jobs leave it OFF - // (undefined) → legacy byte-identical Read/Write/Edit behaviour. - let conflictDetection: boolean | undefined; - // 計画5: タスクの実行ログ root。persistent スペースタスクは local_task の - // runtime_dir(space/{id}/runs/{taskId})、subtask は親から継承した - // jobs.runtime_dir を使う。どちらも無ければ undefined= runPiece / reporter が - // workspacePath/logs にフォールバック(ephemeral / legacy)。 - let runtimeDir: string | undefined = job.runtimeDir ?? undefined; - // 計画5 (spec §10.3): 競合台帳 dir。スペースタスクは space/{id}/.conflict - // (files の外)、それ以外は ToolContext 側で workspacePath/.conflict に - // フォールバック(後方互換)。undefined のまま渡してフォールバックさせる。 - let conflictDir: string | undefined; - // Spaces foundation: the space this run belongs to. Threaded into - // ToolContext.spaceId and CONSUMED for per-space MCP/SSH resolution - // (Phases 2-3). Resolved from the local task's space_id (preferred), but - // INITIALIZED from the trusted jobs.space_id (set server-side at enqueue) - // so a getLocalTask exception below degrades to the job's own space — - // NEVER fails open to the global NULL-space MCP/SSH set (Opus security P2). - // Undefined only for gitea-issue jobs / genuinely space-less tasks. - let spaceId: string | undefined = job.spaceId ?? undefined; - if (localTaskIdForSpace != null) { - try { - folderContext = await this.repo.resolveTaskFolderContext(localTaskIdForSpace, userFolderRoot); - } catch (err) { - logger.warn(`[worker:${this.workerId}] job ${jobId} resolveTaskFolderContext failed, using legacy personal folder: ${err}`); - } - // 計画5 (Opus P1): reporter 構築時に取得済みの local_task 行を再利用する - // (localTaskIdForSpace === localTaskId で同一行)。フェッチ失敗で未取得の - // 場合のみ防御的に再取得する。 - try { - const localTaskForMode = localTaskForRuntime ?? await this.repo.getLocalTask(localTaskIdForSpace); - conflictDetection = localTaskForMode?.workspaceMode === 'persistent'; - runtimeDir = localTaskForMode?.runtimeDir ?? undefined; - // Prefer the local task's space; fall back to the job's space_id. - spaceId = localTaskForMode?.spaceId ?? job.spaceId ?? undefined; - if (conflictDetection && localTaskForMode?.spaceId) { - conflictDir = spaceConflictDir(this.config.worktreeDir, localTaskForMode.spaceId); - } - } catch (err) { - logger.warn(`[worker:${this.workerId}] job ${jobId} getLocalTask for workspaceMode failed, conflict detection OFF: ${err}`); - } - } - // Personal-workspace tasks carry space_id=NULL, but the user registered - // their MCP/SSH servers against their real personal-space id. Mirror what - // resolveTaskFolderContext does for folders: resolve NULL+owner to the - // owner's personal space so per-space MCP/SSH enumeration AND the - // execute-time guard (aggregator.executeTool) both see those servers. - // Applied to EVERY job (not just the local-task branch) so spawned - // subtasks — which inherit the parent's NULL space and don't re-enter the - // block above — also resolve to the owner's personal space. Case-space - // jobs keep their id; no-auth jobs (no owner) stay NULL-space (legacy); - // a resolver failure fails closed (sentinel), never the global NULL set. - // See docs/superpowers/specs/2026-06-19-personal-space-mcp-resolution-design.md - spaceId = await resolveToolSpaceId(spaceId, job.ownerId, (o) => - this.repo.ensurePersonalSpace(o).then((s) => s.id), - ); - // Custom-piece load dir: prefer the resolved space folder when present. - const pieceRootDir = folderContext?.rootDir ?? userFolderRoot; - const pieceLeafId = folderContext?.leafId ?? ownerForPieces; - const customPieceDirs = [ - userPiecesDir(pieceRootDir, pieceLeafId), - this.config.customPiecesDir, - ].filter((d): d is string => !!d); + const { folderContext, conflictDetection, runtimeDir, conflictDir, spaceId, customPieceDirs } = + await this.resolveRunSpaceContext(job, localTaskForRuntime); logger.info(`[worker:${this.workerId}] job ${jobId} loadPiece piece=${job.pieceName} customDirs=[${customPieceDirs.join(', ') || 'none'}] piecesDir=pieces`); const piece = loadPiece(job.pieceName, 'pieces', customPieceDirs); - // Model-mismatch requeue gate (direct mode only — gateway routes by - // role, see shouldRequeueForModelMismatch). - if ( - shouldRequeueForModelMismatch({ - isGateway: this.getWorkerDef().proxy === true, - pieceModel: piece.model, - availableModels: this.availableModels, - workerModel: this.model, - }) - ) { - await this.repo.updateJob(jobId, { - status: 'queued', - workerId: null, - errorSummary: `Required model ${piece.model} is not available on ${this.workerId}`, - }); - await this.repo.addAuditLog(jobId, 'job_requeued_model_mismatch', 'worker', { - workerId: this.workerId, - requiredModel: piece.model, - availableModels: [...this.availableModels], - }); - logger.info(`[worker:${this.workerId}] requeued job ${jobId} due to model mismatch (${piece.model})`); - return; - } + if (await this.requeueOnModelMismatch(job, piece)) return; + if (await this.parkOnMissingMcp(job, piece, localTaskId)) return; - // MCP 認証ゲート: piece.required_mcp に記載されたサーバーのトークンがなければ park - const missingMcp = (piece.required_mcp ?? []).filter( - (serverId) => !this.mcpTokenManager || !this.mcpTokenManager.hasToken(job.ownerId ?? '', serverId), - ); - if (missingMcp.length > 0) { - await this.repo.updateJob(jobId, { - status: 'waiting_human', - waitReason: 'mcp_auth_required', - resumeMovement: piece.initial_movement ?? null, - }); - if (localTaskId !== null) { - await this.repo.addLocalTaskComment( - localTaskId, - 'system', - `この piece は MCP サーバー「${missingMcp.join(', ')}」との連携が必要です。Settings → MCP 接続から連携してください。`, - 'event', - ); + const { llmClient, isProxyWorker } = this.createJobLlmClient(job, piece, reporter); + + // キャンセル用 AbortController + const jobAbortController = new AbortController(); + + // キャンセルチェック: DB のジョブ状態が 'cancelled' になっていたら中断する + const cancelCheck = (): boolean => { + const isCancelled = this.repo.getJobStatusSync(jobId) === 'cancelled'; + if (isCancelled) { + jobAbortController.abort(); } - logger.info(`[worker:${this.workerId}] mcp gate parked job=${jobId} missing=${missingMcp.join(',')}`); - return; - } - - const workerDefForLlm = this.getWorkerDef(); - const isProxyWorker = workerDefForLlm.proxy === true; - // Gateway routes by role; direct resolves the worker's model. The - // resolver thunk runs only in direct mode (no auto-select via gateway). - const resolvedModel = llmRoutingKey({ - isGateway: isProxyWorker, - role: job.requiredRole, - resolveDirectModel: () => this.resolveModel(piece), - }); - const timeoutMs = (this.config.provider.timeoutMinutes ?? 10) * 60 * 1000; - const llmClient = new OpenAICompatClient( - this.endpoint, - resolvedModel, - workerDefForLlm.apiKey, - this.config.provider.retry, - timeoutMs, - this.contextLimitTokens, - this.config.safety?.promptGuardRatio, - (line) => reporter.reportPromptPreflight(line), - { proxy: isProxyWorker, maxStreamMs: this.resolveMaxStreamMs() }, - ); + return isCancelled; + }; // ASK 再開の場合、resume_movement を使用 - const pieceOptions = { - resumeMovement: job.resumeMovement ?? undefined, - askCount: job.askCount, - maxAskPerJob: this.config.ask.maxPerJob, - checkInterjections: isLocalTask && localTaskId !== null && !isSubTask - ? async (movementName: string) => { - const comments = await this.repo.getUninjectedComments(localTaskId); - if (comments.length === 0) return []; - const injected = comments.map(c => ({ id: c.id, body: c.body })); - this.repo.markCommentsInjected(injected.map(c => c.id)); - reporter.reportInterjectionAck(injected, movementName); - return injected; - } - : undefined, - spawnSubTask: job.subtaskDepth < this.config.subtasks.maxDepth - ? async (params: { title: string; instruction: string; piece?: string }) => { - // Zombie guard: after a deadline/user abort — especially a ②B - // hard-kill that already ran cancelPendingChildren and marked the - // job terminal — a detached runPiece can still reach SpawnSubTask. - // Refuse to enqueue a NEW orphan child: nothing live will consume - // it, and it would burn a worker slot under an already-cancelled - // parent. The abort signal catches the in-grace window (terminal - // status not written yet); the status check catches post-hardkill. - if (!canSpawnSubtask(jobAbortController.signal.aborted, this.repo.getJobStatusSync(jobId) ?? '')) { - throw new Error('ジョブが終了またはキャンセルされたため、新しいサブタスクは作成できません。'); - } - const subJobs = await this.repo.getSubJobs(jobId); - const subtaskIndex = subJobs.length + 1; - if (subJobs.length >= this.config.subtasks.maxPerParent) { - throw new Error(`サブタスク上限 (${this.config.subtasks.maxPerParent}) に達しました。これ以上のサブタスクは作成できません。`); - } - const subtaskWorkspace = join(workspacePath, 'subtasks', String(subtaskIndex)); - mkdirSync(subtaskWorkspace, { recursive: true }); - mkdirSync(join(subtaskWorkspace, 'output'), { recursive: true }); - mkdirSync(join(subtaskWorkspace, 'logs'), { recursive: true }); - // 計画5 (Opus P1): サブタスクは親と分離した自前の isolated worktree - // (subtaskWorkspace)で実行されるため、その activity.log は共有されず - // resume poisoning の対象にならない。よって runtime_dir はネストせず - // 未設定のままにし、reporter を subtaskWorkspace/logs にフォールバック - // させる。これは subtask の reader(subtask-activity-api / - // subtask-files-api が worktreePath/logs を読む)と byte 一致し、 - // writer/reader の split-brain を防ぐ。 - - // 親ジョブの role を継承 - const subJobInstruction = [ - `ui_profile: ${job.requiredRole}`, - '', - `# ${params.title}`, - '', - params.instruction, - ].join('\n'); - - const subJob = await this.repo.createJob({ - repo: `subtask/${jobId}`, - issueNumber: subtaskIndex, - instruction: subJobInstruction, - pieceName: params.piece ?? 'general', - parentJobId: jobId, - subtaskDepth: job.subtaskDepth + 1, - maxAttempts: 2, - role: job.requiredRole, - // 親ジョブから所有権・可視性・スペース・ブラウザセッションのバインドを - // 継承する(spec §5.7 のスペース継承 + 保存セッションの引き継ぎ)。 - // browserSessionProfileId が無いとサブタスク内の BrowseWeb が - // 保存セッション (ログイン Cookie) を使えず未ログインでアクセスする。 - ...inheritJobContext(job), - // 計画5 (Opus P1): サブタスクは isolated worktree で実行され共有 - // されないため runtime_dir はネストしない(reporter は - // subtaskWorkspace/logs にフォールバックし reader と一致する)。 - runtimeDir: null, - }); - await this.repo.updateJob(subJob.id, { worktreePath: subtaskWorkspace }); - // TOCTOU close: the abort/hard-kill can land between the entry - // guard and this insert — finalizeHardkill's cancelPendingChildren - // reads getSubJobs *before* this child row exists and so misses it. - // Re-check now: if the parent is no longer live, cancel the child we - // just created (it would otherwise run as an orphan under a - // cancelled parent) and refuse the spawn. finalizeHardkill writes - // the terminal status before cancelPendingChildren, so a status read - // here reliably reflects an in-flight hard-kill. - if (!canSpawnSubtask(jobAbortController.signal.aborted, this.repo.getJobStatusSync(jobId) ?? '')) { - await this.repo.updateJob(subJob.id, { status: 'cancelled' }); - logger.info(`[worker:${this.workerId}] cancelled just-spawned sub-task #${subtaskIndex} job=${subJob.id} (parent no longer live)`); - throw new Error('ジョブが終了またはキャンセルされたため、新しいサブタスクは作成できません。'); - } - logger.info(`[worker:${this.workerId}] spawned sub-task #${subtaskIndex} depth=${job.subtaskDepth + 1} job=${subJob.id}`); - // ④ spawn stagger: brief delay so a burst of N SpawnSubTask enqueues - // doesn't all land in the same instant and jump GPU-slot priority over - // other queued work. Throttle only — NOT a completion-wait. Honors - // cancel so a cancelled parent doesn't sit sleeping. - if (this.config.subtasks.spawnStaggerMs > 0) { - await sleepWithCancel(this.config.subtasks.spawnStaggerMs, cancelCheck); - } - return { jobId: subJob.id, subtaskIndex, workspacePath: subtaskWorkspace }; - } - : undefined, - // WaitSubTask / auto-park support: report whether this job has unwaited - // pending children, and cancel them when the parent ends without consuming - // them (orphan prevention). Both use getSubJobs (latest revision per index). - hasPendingSubtasks: async (): Promise => { - const subs = await this.repo.getSubJobs(jobId); - return subs.some((s) => !SUBTASK_DB_TERMINAL.includes(s.status)); - }, - }; + const pieceOptions = this.buildPieceRunOptions({ + job, + workspacePath, + isLocalTask, + localTaskId, + isSubTask, + reporter, + jobAbortController, + cancelCheck, + }); // サブジョブの delegate イベントを root 親ジョブへ中継するための job id を解決する。 // サブタスクでない通常ジョブは undefined(中継なし)。 @@ -1555,18 +2282,6 @@ export class Worker { // 開始コメント await reporter.reportMovementStart(`${piece.name} タスク開始`); - // キャンセル用 AbortController - const jobAbortController = new AbortController(); - - // キャンセルチェック: DB のジョブ状態が 'cancelled' になっていたら中断する - const cancelCheck = (): boolean => { - const isCancelled = this.repo.getJobStatusSync(jobId) === 'cancelled'; - if (isCancelled) { - jobAbortController.abort(); - } - return isCancelled; - }; - // ── ジョブガード ─────────────────────────────────────────────── // cancelCheck はエージェントループのイテレーション先頭でしか呼ばれない。 // LLM が停止トークンを出さずにトークンを吐き続ける暴走に入ると、処理は @@ -1603,395 +2318,64 @@ export class Worker { const sessionIdentity = await resolveSessionTaskId(this.repo, job); // ── Browser session profile binding ───────────────────────────── - // If this job is bound to a browser_session_profile, decrypt the - // captured Playwright storageState and pass it into runPiece so - // BrowseWeb can inject it into BrowserContext. Owner-mismatch and - // expired-profile checks fail-fast before the agent loop starts. - let browserSessionState: object | undefined; - let browserSessionProfileId: number | undefined; - let browserSessionProfile: - | { loggedInSelector: string | null; loginUrlPatterns: string[] } - | undefined; - let onAuthExpired: - | ((profileId: number, reason: string) => void) - | undefined; + const { browserSessionState, browserSessionProfileId, browserSessionProfile, onAuthExpired } = + this.resolveBrowserSessionBinding(job, localTaskId); - if (job.browserSessionProfileId) { - const sessRepo = new BrowserSessionRepo(this.repo.getDb()); - const profile = sessRepo.getProfileByIdUnsafe(job.browserSessionProfileId); - if (!profile) { - sessRepo.audit({ - actorUserId: job.ownerId ?? null, - ownerId: null, - profileId: job.browserSessionProfileId, - action: 'use', - result: 'error', - reason: 'profile not found', - jobId: job.id, - }); - throw new Error(`Browser session profile ${job.browserSessionProfileId} not found`); - } - // Fail-closed owner check: a job with null/missing ownerId must not - // be allowed to decrypt any profile, even if the profile id would - // otherwise resolve. Helper audits + throws on rejection. Extracted - // to src/engine/browser-session-auth.ts so the contract is unit - // tested in isolation from the Worker class. - assertProfileOwner(profile, job, sessRepo); - if (profile.status !== 'active' || !profile.encryptedStateBlob) { - sessRepo.audit({ - actorUserId: job.ownerId, ownerId: profile.ownerId, profileId: profile.id, - action: 'use', result: 'error', reason: `status=${profile.status}`, jobId: job.id, - }); - throw new Error(`AUTH_SESSION_EXPIRED: profile ${profile.id} status=${profile.status}`); - } - const masterKeyPath = this.config.secrets?.masterKeyPath ?? './data/secrets/master.key'; - // Space-as-Principal P2: space-owned profiles decrypt under the space DEK - // (with owner-DEK fallback for pre-migration blobs); personal under owner DEK. - let stateJson: string; - try { - stateJson = decryptProfileState({ masterKeyPath, sessRepo }, { - ownerId: profile.ownerId, - spaceId: profile.spaceId, - encryptedStateBlob: profile.encryptedStateBlob, - }); - } catch (e) { - sessRepo.audit({ - actorUserId: job.ownerId, - ownerId: profile.ownerId, - profileId: profile.id, - action: 'decrypt', - result: 'error', - reason: `state decrypt failed: ${(e as Error).message}`, - jobId: job.id, - }); - throw e; - } - browserSessionState = JSON.parse(stateJson) as object; - browserSessionProfileId = profile.id; - browserSessionProfile = { - loggedInSelector: profile.loggedInSelector, - loginUrlPatterns: profile.loginUrlPatterns, - }; - onAuthExpired = (pid, reason) => { - sessRepo.markProfileStatus(pid, 'expired', reason); - sessRepo.audit({ - actorUserId: job.ownerId, ownerId: profile.ownerId, profileId: pid, - action: 'expire', result: 'success', reason, jobId: job.id, - }); - // Best-effort task-level notification. Subtask jobs and - // gitea-issue jobs may not have a numeric local_task id. - if (localTaskId !== null) { - this.repo.addLocalTaskComment( - localTaskId, - 'agent', - `⚠️ Browser session "${profile.label}" expired: ${reason}. Re-login from Settings → Browser Sessions.`, - 'progress', - ).catch(() => { /* ignore — comment posting is best-effort */ }); - } - }; - sessRepo.audit({ - actorUserId: job.ownerId, ownerId: profile.ownerId, profileId: profile.id, - action: 'use', result: 'success', jobId: job.id, - }); - sessRepo.touchUsed(profile.id); - } + const { handoffContext, transcriptPath, enrichedInstruction } = + await this.buildInstructionContext(job, workspacePath, localTaskId, runtimeDir); - // Piece handoff: when this job continues an earlier one in the same - // local_task, agent-loop injects a "this is a continuation of piece X" - // block into the system prompt. We resolve the prev piece name + the - // most recent agent result/ask comment as the LLM-visible carry-over. - let handoffContext: import('./engine/agent-loop.js').HandoffContext | undefined; - if (job.continuedFromJobId && isLocalTask && localTaskId !== null) { - const prevJob = await this.repo.getJob(job.continuedFromJobId); - if (prevJob) { - const prevResultComment = await this.repo.getLatestResultComment(localTaskId); - handoffContext = { - prevPiece: prevJob.pieceName, - prevResult: prevResultComment?.body ?? null, - }; - } else { - logger.warn(`[worker:${this.workerId}] continued_from_job_id=${job.continuedFromJobId} not found for job ${jobId}; skipping handoff context`); - } - } + const { mcpDisabled, skillsDisabled } = this.resolveJobPayloadOptions(job); + const userRole = this.resolveJobUserRole(job); + const { workspaceTools, workspaceSshConnections } = await this.resolveWorkspaceToolContext(job, spaceId); - // 会話コンテキストの組み立て。P3: この継続ジョブが transcript を再生する場合 - // (handoffContext あり && 再生可能ターンあり = piece-runner の再生条件と完全一致)、 - // 再生した実ターンと重複するコメント要約ブロックを抑制する。transcriptPath は - // runPiece に渡す runtimeDir から算出するため piece-runner の logsRoot と一致し、 - // 抑制判定と再生判定の split-brain が構造的に起きない。 - const transcriptPath = join(runtimeDir ?? join(workspacePath, 'logs'), 'transcript.jsonl'); - const willReplayTranscript = - handoffContext !== undefined && Conversation.hasReplayableTranscript(transcriptPath); - - let enrichedInstruction = `${buildTimeContextBlock()}${job.instruction}`; - if (isLocalTask) { - try { - const comments = await this.repo.listLocalTaskComments(localTaskId); - const outputFiles = this.listDir(join(workspacePath, 'output')); - const inputFiles = this.listDir(join(workspacePath, 'input')); - const contextBody = buildLocalConversationContext({ - comments, - jobInstruction: job.instruction, - inputFiles, - outputFiles, - omitRecentConversation: willReplayTranscript, - }); - enrichedInstruction = `${buildTimeContextBlock()}${contextBody}`; - } catch (err) { - logger.warn(`[worker:${this.workerId}] failed to build local context: ${err}`); - } - } - const retryHandoffContext = buildRetryHandoffContext(workspacePath, job); - if (retryHandoffContext) { - enrichedInstruction = `${enrichedInstruction}\n\n${retryHandoffContext}`; - } - - // Parse per-task options from job payload (e.g. { options: { mcpDisabled, skillsDisabled } }). - let jobPayloadOptions: Record = {}; - if (job.payload) { - try { - const parsed = JSON.parse(job.payload) as Record; - if (parsed?.options && typeof parsed.options === 'object' && !Array.isArray(parsed.options)) { - jobPayloadOptions = parsed.options as Record; - } - } catch { - logger.warn(`[worker:${this.workerId}] job ${jobId} failed to parse payload JSON`); - } - } - const mcpDisabled = jobPayloadOptions.mcpDisabled === true; - const skillsDisabled = jobPayloadOptions.skillsDisabled === true; - - // Resolve the job owner's role to gate admin-only tool actions - // (e.g. system-scope InstallSkill). Defaults to 'user' when the owner - // is unknown (no-auth mode) or the lookup fails. - let userRole: 'admin' | 'user' = 'user'; - if (job.ownerId) { - try { - const ownerRow = this.repo.getUserById(job.ownerId); - if (ownerRow?.role === 'admin') userRole = 'admin'; - } catch (err) { - logger.warn(`[worker:${this.workerId}] job ${jobId} owner role lookup failed: ${(err as Error).message}`); - } - } - - // Workspace tool policy: resolve once per job, applied to every movement. - // Uses the already-resolved spaceId (personal-space NULL→real-id handled - // above by resolveToolSpaceId). Fail-closed: any error → safe defaults - // (sensitive tools off). Never throws — a policy error must not crash a job. - let workspaceTools: { allowedTools: string[]; editAllowed: boolean }; - let parsedPolicy: import('./engine/workspace-tool-policy.js').WorkspaceToolPolicy; - try { - const policyJson = spaceId != null ? this.repo.getSpaceToolPolicy(spaceId) : null; - parsedPolicy = parseToolPolicy(policyJson); - workspaceTools = await resolveWorkspaceTools(parsedPolicy); - } catch (err) { - logger.warn(`[worker:${this.workerId}] job ${jobId} workspace tool policy resolution failed, using safe defaults: ${(err as Error).message}`); - parsedPolicy = {}; - workspaceTools = await resolveWorkspaceTools({}); - } - - // SSH workspace scoping: when the workspace policy enables the 'ssh' category, - // resolve the connection IDs available to this job's space. This space-scoped - // list is the SOLE SSH connection gate (piece-level allowed_ssh_connections was - // removed in PR B), mirroring how MCP uses listEnabledForSpace. spaceId is - // already personal-space-resolved - // (NULL→real-id) by resolveToolSpaceId above, so personal-WS jobs find their - // connections via listBySpace(personalSpaceId). NEVER '*' wildcard — always an - // explicit id list so cross-space isolation is guaranteed. - let workspaceSshConnections: string[] | undefined; - if (parsedPolicy.enabledSensitive?.includes('ssh') && spaceId != null) { - try { - const sshSub = getSshSubsystem(); - if (sshSub !== null) { - workspaceSshConnections = sshSub.connectionRepo.listBySpace(spaceId).map((c) => c.id); - logger.debug(`[worker:${this.workerId}] job ${jobId} ssh workspace connections resolved count=${workspaceSshConnections.length} spaceId=${spaceId}`); - } else { - // SSH subsystem not initialised (e.g. test environment without bridge setup). - // Leave undefined → piece-runner treats it as [] (no connections → SSH rejects). - logger.debug(`[worker:${this.workerId}] job ${jobId} ssh enabled in policy but subsystem not initialised — no connections available`); - } - } catch (err) { - logger.warn(`[worker:${this.workerId}] job ${jobId} ssh connection resolution failed: ${(err as Error).message}`); - // Leave undefined → piece-runner treats it as [] (fail-closed, no connections). - } - } - - const piecePromise = runPiece(piece, enrichedInstruction, llmClient, workspacePath, callbacks, toolsConfig, { - ...pieceOptions, + const piecePromise = this.startPieceRun({ + job, + piece, + enrichedInstruction, + llmClient, + workspacePath, + callbacks, + toolsConfig, + pieceOptions, cancelCheck, - abortController: jobAbortController, - safetyConfig: this.config.safety, - searchFilter: this.config.searchFilter, - customPiecesDir: customPieceDirs, - // Spaces foundation: resolved space folder for AGENTS.md / memory / - // custom-piece resolution. Undefined for gitea-issue jobs => legacy. + jobAbortController, + customPieceDirs, folderContext, - // Spaces foundation: space id for future per-space MCP/SSH resolution. - // Phase 1 no-op (no consumer reads ctx.spaceId yet). spaceId, - // Per-space Python package overlay (admin-installed wheels). Resolve the - // stable `current` symlink path so Bash can bind it read-only + expose it - // on PYTHONPATH. undefined when the feature is disabled; the Bash tool - // also no-ops if the path does not exist yet (nothing installed). - pythonPackagesDir: (() => { - const pk = resolvePythonPackagesConfig(this.config.pythonPackages); - if (!pk.enabled) return undefined; - return spaceCurrentDir(pk.dir, spaceKeyFor(spaceId, sessionIdentity.userId ?? job.ownerId ?? 'local')); - })(), - // Spaces foundation (plan 3): conflict detection for persistent local-task - // workspaces. Undefined (OFF) for ephemeral / gitea-issue jobs => legacy. + sessionIdentity, conflictDetection, - // 計画5: 実行ログ root + 競合台帳 dir。スペースタスクのみ非 undefined。 - // ephemeral / legacy は undefined = workspacePath/logs・workspacePath/.conflict(後方互換)。 runtimeDir, conflictDir, contextManager, - vlmEnabled: workerDef.vlm === true, - jobId, + workerDef, handoffContext, - // When this run IS a subtask, pass parent identity + child workspace - // path so the runner records the lineage in its run-start event. - // Subtask workspaces follow `/subtasks/` where N is the - // subtask job's issueNumber. - parentJobId: isSubTask && parentJobId ? parentJobId : undefined, - childWorkspaceRelative: isSubTask ? `subtasks/${issueNumber}` : undefined, - // Mission Brief: only wire IO when this run is bound to a local - // task (the brief is per-LocalTask, not per-job). Subtask runs - // and gitea-issue runs leave it unset → MissionUpdate degrades - // to a no-op and the system prompt MISSION block is skipped. - missionBrief: isLocalTask && localTaskId !== null && !isSubTask - ? this.repo.makeMissionBriefIO(localTaskId) - : undefined, - // Conversation recall: bind SearchTaskConversation / ReadTaskConversation - // to this local task's comments + this run's transcript. Same gate as - // missionBrief (per-LocalTask, not subtasks/gitea-issue runs) so the - // tools stay scoped and degrade gracefully otherwise. - // - // SECURITY (adversarial review): bind the transcript ONLY to a per-task - // `runtimeDir` (space/{spaceId}/runs/{taskId}). The old - // `runtimeDir ?? workspacePath/logs` fallback is UNSAFE for persistent - // space tasks — workspacePath is the space-SHARED files tree, so a - // NULL-runtime_dir task would expose another (possibly different-user) - // task's transcript in the same space. When runtimeDir is unset we pass - // no transcript path: comment search (always task_id-scoped) still works, - // transcript search simply returns nothing rather than risk a leak. - taskConversation: isLocalTask && localTaskId !== null && !isSubTask - ? this.repo.makeTaskConversationIO( - localTaskId, - runtimeDir ? join(runtimeDir, 'transcript.jsonl') : undefined, - ) - : undefined, - // Workspace-wide task search: same gate as taskConversation, plus an - // owner id (needed to scope the search to the owner's space). In - // no-auth mode job.ownerId is null, so this is undefined and the tool - // degrades to a no-context message rather than searching unscoped. - workspaceTaskSearch: isLocalTask && localTaskId !== null && !isSubTask && job.ownerId - ? this.repo.makeWorkspaceTaskSearchIO(localTaskId, job.ownerId) - : undefined, - // File provenance: bind reads to THIS run's workspace_path (the scoping - // boundary — a shared space workspace is queried only for its own rows, - // never another workspace's / user's lineage) and a recorder bound to - // this task/job/space. Gated on a resolvable local task id (need a - // numeric created_by_task_id). Subtask / gitea-issue runs leave both - // unset → the agent tools degrade to a no-op and Write/Edit/Bash skip - // recording. - fileProvenance: - isLocalTask && localTaskId !== null - ? this.repo.makeFileProvenanceIO(workspacePath) - : undefined, - recordFileProvenance: - isLocalTask && localTaskId !== null - ? (ev) => - this.repo.recordProvenance({ - workspacePath, - relPath: ev.relPath, - event: ev.event, - sourceKind: ev.sourceKind, - checksum: ev.checksum ?? null, - taskId: localTaskId, - jobId, - spaceId: spaceId ?? null, - piece: ev.pieceName, - movement: ev.movementName, - }) - : undefined, - taskId: sessionIdentity.taskId, - userId: sessionIdentity.userId, - // Tool-request mechanism: per-task grant overlay, read fresh each run so - // a resume after inline approval picks up the newly-granted tool. - grantedTools: - isLocalTask && localTaskId !== null ? this.repo.getGrantedTools(String(localTaskId)) : undefined, - // Workspace tool policy: pre-resolved once per job above (fail-closed). - // Drives every movement's effective allowed tools and edit flag. - workspaceTools, - // SSH workspace scoping: connection IDs available to this job's space. - // Undefined when ssh not enabled in policy (movement declaration is fallback). - workspaceSshConnections, - // Pause for inline approval only when a user is reachable: a local task - // that is not itself a subtask (subtasks/headless runs auto-proceed). - toolApprovalInteractive: isLocalTask && !isSubTask, - // Tool-request mechanism: bind the recorder to this run's task/job/space. - // piece + movement are injected upstream by piece-runner. - recordToolRequest: (req) => - this.repo.recordToolRequest({ - ...req, - // Use the local task id (same key as granted_tools + the decide API) - // so the task-detail query and grant overlay line up. - taskId: localTaskId !== null ? String(localTaskId) : null, - jobId, - spaceId: spaceId ?? null, - }), + transcriptPath, + isLocalTask, + localTaskId, + isSubTask, + parentJobId, browserSessionState, browserSessionProfileId, browserSessionProfile, onAuthExpired, - ownerId: job.ownerId, - mcpConfig: mergeMcpConfig(this.config.mcp), - userRole, - skillCatalog: this.skillCatalog ?? undefined, mcpDisabled, skillsDisabled, + userRole, + workspaceTools, + workspaceSshConnections, }); - // ②B deadline hard-kill safety net. Race the piece run against a grace - // timer that arms when the job's abort signal fires (deadline OR cancel). - // ②A wires the abort into the realistic blocking tools so a run normally - // unwinds cooperatively; if it does not within the grace window, hard-kill - // so the inflight slot is freed and the job reaches a terminal state. The - // detached run keeps executing in-process (accepted zombie residual). - // deadlineGraceSeconds <= 0 disables the ②B hard-kill fallback: await the - // run directly and rely on ②A's cooperative abort (matches the documented - // "0 disables" semantics, same convention as maxJobMinutes=0). - const graceSec = this.config.safety?.deadlineGraceSeconds ?? DEFAULT_DEADLINE_GRACE_SECONDS; - const raced = graceSec > 0 - ? await raceWithGrace(piecePromise, jobAbortController.signal, graceSec * 1000) - : { ok: true as const, value: await piecePromise }; - if (!raced.ok) { - logger.warn(`[worker:${this.workerId}] job ${jobId} did not unwind within grace=${graceSec}s after abort; hard-killing to release slot (deadline=${raced.deadline})`); - metricFinalStatus = 'cancelled'; - await this.finalizeHardkill(job, reporter, isSubTask, parentJobId, raced.deadline); - return; - } - const result = raced.value; - logger.info(`[worker:${this.workerId}] job ${jobId} runPiece done: status=${result.status}`); - this.writeRunDiagnostics(workspacePath, result); - - await this.handlePieceResult(result, job, reporter, workspacePath, isLocalTask, isSubTask, parentJobId); - - // Phase 3b: capture the terminal status for the jobs_total label. - // result.status uses piece-runner's own enum - // ('completed'|'aborted'|'error'|'waiting_human'|'waiting_subtasks'|'cancelled'); map to the - // metric enum (waiting_subtasks stays "succeeded" for the metric - // because the job pauses cleanly — not a failure). - switch (result.status) { - case 'completed': metricFinalStatus = 'succeeded'; break; - case 'aborted': metricFinalStatus = 'aborted'; break; - case 'cancelled': metricFinalStatus = 'cancelled'; break; - case 'waiting_human': metricFinalStatus = 'waiting_human'; break; - case 'waiting_subtasks': metricFinalStatus = 'succeeded'; break; - case 'error': - default: metricFinalStatus = 'failed'; break; - } + await this.settlePieceRun({ + piecePromise, + jobAbortController, + job, + reporter, + workspacePath, + isLocalTask, + isSubTask, + parentJobId, + setMetricFinalStatus: (s) => { metricFinalStatus = s; }, + }); } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err); const errorStack = err instanceof Error && err.stack ? err.stack : '(no stack)'; @@ -2447,6 +2831,21 @@ export class Worker { await this.repo.addAuditLog(jobId, 'job_tool_request', 'worker', { resumeMovement: result.resumeMovement, }); + } else if (result.waitReason === 'package_request') { + // Package-approval pause (RequestPackage). MUST persist + // wait_reason='package_request' so the decide API's + // resumePackageRequestJob can re-queue it — otherwise the job is stuck + // forever. Like tool_request, this is NOT an ASK: it does not consume + // the per-job ASK budget and is not reported as a question. + await this.repo.updateJob(jobId, { + status: 'waiting_human', + resumeMovement: result.resumeMovement ?? null, + waitReason: 'package_request', + }); + this.enqueuePush(job, 'waiting_human'); + await this.repo.addAuditLog(jobId, 'job_package_request', 'worker', { + resumeMovement: result.resumeMovement, + }); } else { await this.repo.updateJob(jobId, { status: 'waiting_human', diff --git a/src/worker/piece-metric-status.test.ts b/src/worker/piece-metric-status.test.ts new file mode 100644 index 0000000..305ba31 --- /dev/null +++ b/src/worker/piece-metric-status.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest'; +import { mapPieceStatusToMetric } from './piece-metric-status.js'; + +/** + * executeJob(旧: インライン switch)から切り出したメトリクス status 変換の + * ピン留め。src/worker.metrics.test.ts が executeJob の switch をミラーして + * いたのと同じ対応表を、切り出し後の純関数に対して固定する。 + */ +describe('mapPieceStatusToMetric', () => { + it.each([ + ['completed', 'succeeded'], + ['aborted', 'aborted'], + ['cancelled', 'cancelled'], + ['waiting_human', 'waiting_human'], + // waiting_subtasks はクリーンな停車であり失敗ではないため succeeded に寄せる + ['waiting_subtasks', 'succeeded'], + ['error', 'failed'], + ] as const)('maps %s -> %s', (input, expected) => { + expect(mapPieceStatusToMetric(input)).toBe(expected); + }); +}); diff --git a/src/worker/piece-metric-status.ts b/src/worker/piece-metric-status.ts new file mode 100644 index 0000000..1c5d82d --- /dev/null +++ b/src/worker/piece-metric-status.ts @@ -0,0 +1,25 @@ +import type { PieceRunResult } from '../engine/piece-runner.js'; + +/** jobs_total / job_duration_seconds メトリクスの status ラベル値。 */ +export type JobMetricStatus = 'succeeded' | 'failed' | 'aborted' | 'cancelled' | 'waiting_human' | 'error'; + +/** + * Phase 3b: capture the terminal status for the jobs_total label. + * result.status uses piece-runner's own enum + * ('completed'|'aborted'|'error'|'waiting_human'|'waiting_subtasks'|'cancelled'); map to the + * metric enum (waiting_subtasks stays "succeeded" for the metric + * because the job pauses cleanly — not a failure). + * + * worker.ts executeJob 内の switch を純関数として切り出したもの(挙動不変)。 + */ +export function mapPieceStatusToMetric(status: PieceRunResult['status']): JobMetricStatus { + switch (status) { + case 'completed': return 'succeeded'; + case 'aborted': return 'aborted'; + case 'cancelled': return 'cancelled'; + case 'waiting_human': return 'waiting_human'; + case 'waiting_subtasks': return 'succeeded'; + case 'error': + default: return 'failed'; + } +} diff --git a/ui/e2e-auth/sharing-scope.auth.spec.ts b/ui/e2e-auth/sharing-scope.auth.spec.ts index ac8340a..066e4cf 100644 --- a/ui/e2e-auth/sharing-scope.auth.spec.ts +++ b/ui/e2e-auth/sharing-scope.auth.spec.ts @@ -353,19 +353,19 @@ test('invite picker lists same-org member, excludes the stranger', async ({ page expect(fatalErrors, `fatal errors:\n${fatalErrors.join('\n')}`).toEqual([]); }); -// 4. NO visibility selector. The space chat shows the static visibility NOTE and -// never the removed selector control. -test('space chat shows the static visibility note, not a selector', async ({ page }) => { +// 4. NO visibility control at all. Space-chat visibility is a fixed member-only +// scope, so neither the old selector nor the static note chip should render — +// there is nothing for the user to read or choose. +test('space chat exposes neither a visibility selector nor a visibility note (fixed member-only scope)', async ({ page }) => { const fatalErrors = trackFatalErrors(page); await login(page, MANAGER.email, MANAGER.password); await openSharedSpace(page); await openSeededChat(page); - // The static note is present; the removed selector is absent. - await expect(page.getByTestId('space-chat-visibility-note')).toBeVisible({ timeout: 15_000 }); - await expect(page.getByTestId('space-chat-visibility-note')).toContainText('メンバーに公開'); + // Both the removed selector and the removed static-note chip are absent. await expect(page.getByTestId('space-chat-visibility')).toHaveCount(0); + await expect(page.getByTestId('space-chat-visibility-note')).toHaveCount(0); expect(fatalErrors, `fatal errors:\n${fatalErrors.join('\n')}`).toEqual([]); }); diff --git a/ui/e2e/spaces.spec.ts b/ui/e2e/spaces.spec.ts index a378525..2957f8d 100644 --- a/ui/e2e/spaces.spec.ts +++ b/ui/e2e/spaces.spec.ts @@ -1103,8 +1103,8 @@ test('space MCP isolation: server registered in A is visible in A, absent in B', // Space task/settings feature parity (feat/space-feature-parity-tests). // // Phase 1 added a per-chat action toolbar (data-testid="space-chat-actions") to -// the inline SpaceConversation, with delete / share / continue / visibility -// controls reusing the SAME implementation as the Tasks-page DetailHeader. These +// the inline SpaceConversation, with delete / share / continue controls reusing +// the SAME implementation as the Tasks-page DetailHeader. These // tests prove each action works INSIDE a space without ever bouncing to the // Tasks page, plus the 概要 (overview) feedback flow and every 設定 sub-tab. // diff --git a/ui/src/api.ts b/ui/src/api.ts index 7ee5538..96ff8ae 100644 --- a/ui/src/api.ts +++ b/ui/src/api.ts @@ -1,2276 +1,26 @@ -const BASE = '/api'; - -/** - * 4-state representation of a secret field on the wire. See design doc - * 2026-05-21-settings-ui-and-config-restructure-design.md (Form Behavior → - * Secret Inputs) for the canonical contract. - * - * Phase 1 (this release) keeps the on-wire representation backwards - * compatible with the existing `apiKey: string` shape so that mask- - * preservation in `ConfigManager.updateConfig` keeps working unchanged: - * - * - `unchanged` → serialized as the masked sentinel `'********'` - * - `literal` → serialized as the raw string value - * - `env_ref` → serialized as `${ENV_NAME}` - * - `cleared` → serialized as `''` (empty string) - * - * Phase 2 will switch to a tagged object on the wire and drop the magic - * `'********'` sentinel; the UI form keeps the 4-state union today so the - * Phase 2 migration is a server-side change only. - */ -export type SecretFieldValue = - | { type: 'unchanged' } - | { type: 'literal'; value: string } - | { type: 'env_ref'; env_name: string } - | { type: 'cleared' }; - -/** Server-side masked sentinel. Kept in sync with `MASKED` in src/config-manager.ts. */ -export const SECRET_MASKED_SENTINEL = '********'; - -/** - * Parse a stored string secret (as received from `GET /api/config`) into - * the 4-state form used by the UI. The server masks literal secrets to - * `'********'`, so any literal-looking string is treated as `unchanged` - * unless it's an `${ENV_REF}` pattern. Empty / missing values map to - * `cleared`. - */ -export function parseSecretValue(raw: string | null | undefined): SecretFieldValue { - if (raw == null || raw === '') return { type: 'cleared' }; - if (raw === SECRET_MASKED_SENTINEL) return { type: 'unchanged' }; - const envMatch = /^\$\{([A-Z0-9_]+)\}$/.exec(raw.trim()); - if (envMatch) return { type: 'env_ref', env_name: envMatch[1] }; - // Anything else came back as plaintext (e.g. fresh UI form not yet - // round-tripped through the server mask) — treat as literal. - return { type: 'literal', value: raw }; -} - -/** - * Serialize a 4-state secret into the string the server currently - * expects. `unchanged` becomes the masked sentinel so server-side - * mask-preservation in `ConfigManager.updateConfig` keeps the existing - * literal in place. - */ -export function serializeSecretValue(v: SecretFieldValue): string { - if (v.type === 'unchanged') return SECRET_MASKED_SENTINEL; - if (v.type === 'literal') return v.value; - if (v.type === 'env_ref') return `\${${v.env_name}}`; - return ''; -} - -export type PieceName = string; // Dynamically loaded from API -export type ProfileName = 'auto' | 'fast' | 'quality'; -export type OutputFormat = 'text' | 'markdown' | 'json'; -export type AskPolicy = 'low' | 'high'; -export type Priority = 'low' | 'medium' | 'high'; -export type Visibility = 'private' | 'org' | 'public'; - -export interface UserOrg { - orgId: string; - orgName: string; - fetchedAt: string; -} - -export async function fetchMyOrgs(): Promise { - const res = await fetch('/api/users/me/orgs'); - if (!res.ok) return []; - const { orgs } = (await res.json()) as { orgs: Array<{ orgId: string; orgName: string; fetchedAt: string }> }; - return orgs; -} - -export interface SubtaskInfo { - id: string; - issueNumber: number; - status: string; - instruction: string; - worktreePath: string | null; - createdAt: string; - updatedAt: string; - children?: SubtaskInfo[]; - childCount?: number; - childCompleted?: number; -} - -export interface SubtaskActivity { - jobId: string; - issueNumber: number; - status: string; - currentMovement: string | null; - currentActivity: string | null; - activityLog: string; -} - -export type TitleSource = 'auto' | 'agent' | 'user'; - -export interface LocalTask { - id: number; - title: string; - /** Provenance of the title: 'auto' (creation fallback), 'agent' (derived from goal), 'user' (manual edit). */ - titleSource?: TitleSource; - body: string; - pieceName: string; - profile: string; - outputFormat: string; - askPolicy: string; - priority: string; - state: string; - workspacePath: string | null; - ownerId?: string | null; - ownerName?: string | null; - visibility?: Visibility; - visibilityScopeOrgId?: string | null; - visibilityScopeOrgName?: string | null; - /** Spaces foundation: which space this task belongs to (null = legacy/個人). */ - spaceId?: string | null; - createdAt: string; - updatedAt: string; - latestJob?: { - id: string; - status: string; - waitReason?: string | null; - currentMovement?: string | null; - currentActivity?: string | null; - workerId?: string | null; - /** - * Physical backend id (e.g. LiteLLM deployment) for jobs run through - * a proxy worker. NULL until the proxy has resolved a backend or for - * direct workers entirely. - * Phase A: docs/superpowers/specs/2026-05-18-multi-team-gpu-pool-and-node-status-design.md. - */ - lastBackendId?: string | null; - contextPromptTokens?: number | null; - contextLimitTokens?: number | null; - contextUpdatedAt?: string | null; - } | null; - subtasks?: SubtaskInfo[]; - subtaskCount?: number; - subtaskCompleted?: number; - feedbackRating?: 'good' | 'bad' | null; - feedbackTags?: string[] | null; - feedbackComment?: string | null; - feedbackAt?: string | null; - shareToken?: string | null; - sharedAt?: string | null; - missionBrief?: MissionBrief | null; -} - -export interface MissionBrief { - goal: string; - done: string; - open: string; - clarifications: string; - user_constraints?: string; - decisions?: string; - current_focus?: string; -} - -export async function updateMissionBrief( - taskId: number, - patch: Partial, -): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/mission`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(patch), - }); - if (!res.ok) { - const err = await res.json().catch(() => ({ error: res.statusText })); - throw new Error(err.error || res.statusText); - } - const data = await res.json(); - return data.missionBrief ?? null; -} - -// ─── Tool-request mechanism ──────────────────────────────────────────────── -export interface ToolRequest { - id: string; - taskId: string | null; - jobId: string | null; - spaceId: string | null; - pieceName: string; - movementName: string; - toolName: string; - reason: string | null; - category: 'requested' | 'blocked' | 'unknown'; - status: 'pending' | 'approved' | 'denied' | 'auto_denied'; - grantScope: 'task' | 'piece' | null; - decidedBy: string | null; - createdAt: string; - decidedAt: string | null; -} - -export async function fetchToolRequests(taskId: number): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/tool-requests`); - if (!res.ok) throw new Error('failed to fetch tool requests'); - const data = await res.json(); - return (data.toolRequests ?? []) as ToolRequest[]; -} - -export async function decideToolRequest( - taskId: number, - reqId: string, - decision: 'approve' | 'deny', -): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/tool-requests/${reqId}/decide`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ decision }), - }); - if (!res.ok) { - const err = await res.json().catch(() => ({ error: res.statusText })); - throw new Error(err.error || res.statusText); - } -} - -// Mirrors the API's safe projection (local-files-api.ts): task IDs + source -// kind + piece/movement + timestamps only. Job UUIDs, checksum, and the -// free-text note are intentionally NOT sent to the client (adversarial-review D4). -export interface FileProvenance { - relPath: string; - sourceKind: string; - createdByTaskId: number | null; - createdByPiece: string | null; - createdByMovement: string | null; - firstSeenAt: string | null; - lastModifiedByTaskId: number | null; - lastModifiedAt: string | null; -} - -/** Fetch the provenance record for one workspace file (null when untracked). */ -export async function fetchFileProvenance( - taskId: number, - section: string, - path: string, -): Promise { - const qs = new URLSearchParams({ section, path }); - const res = await fetch(`${BASE}/local/tasks/${taskId}/files/provenance?${qs.toString()}`); - if (!res.ok) return null; - const data = await res.json(); - return (data.provenance ?? null) as FileProvenance | null; -} - -export type CommentKind = 'request' | 'comment' | 'result' | 'ask' | 'progress' | 'handoff' | 'interjection'; - -export interface LocalTaskComment { - id: number; - taskId: number; - author: string; - kind: CommentKind; - body: string; - /** Filenames attached to this comment, saved under the task's input/ dir. */ - attachments?: string[]; - createdAt: string; - injectedAt: string | null; -} - -export interface LocalFileEntry { - name: string; - path: string; - kind: 'directory' | 'file'; - size: number; - modifiedAt: string; -} - -export interface CreateLocalTaskInput { - title?: string; - body: string; - piece: PieceName; - profile: ProfileName; - outputFormat: OutputFormat; - askPolicy: AskPolicy; - priority: Priority; - attachments?: Array<{ name: string; contentBase64: string }>; - visibility?: Visibility; - visibilityScopeOrgId?: string | null; - browserSessionProfileId?: number | null; - /** - * Spaces foundation: 'persistent'(既定)はスペースのワークスペースに蓄積、 - * 'ephemeral' は使い捨て。未指定時はバックエンドが 'persistent' に解決する。 - */ - workspaceMode?: 'persistent' | 'ephemeral'; - /** 紐付けるスペース。未指定なら owner の個人スペースに解決される。 */ - spaceId?: string; - options?: { - mcpDisabled?: boolean; - skillsDisabled?: boolean; - }; -} - -export async function fetchLocalTasks(): Promise { - const res = await fetch(`${BASE}/local/tasks`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch local tasks'); - return data.tasks ?? []; -} - -export async function createLocalTask(input: CreateLocalTaskInput): Promise<{ task: LocalTask; jobId: string }> { - const res = await fetch(`${BASE}/local/tasks`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(input), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to create local task'); - return data; -} - -// --- Spaces (個人/案件) --------------------------------------------------- - -export interface Space { - id: string; - kind: 'personal' | 'case'; - title: string; - description: string; - ownerId: string | null; - visibility: 'private' | 'org' | 'public'; - visibilityScopeOrgId: string | null; - status: 'open' | 'archived'; - brandColor: string | null; - workspaceDir: string | null; - createdAt: string; - updatedAt: string; - /** 閲覧者自身のメンバーロール。一覧(GET /spaces)・詳細(GET /spaces/:id)の両方で付与。 - * 非メンバー(根オーナー含む。owner_id はメンバー行ではない)は null。 */ - myRole?: SpaceMemberRole | null; -} - -export async function fetchSpaces(): Promise { - const res = await fetch(`${BASE}/local/spaces`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch spaces'); - return data as Space[]; -} - -export type SpaceMemberRole = 'owner' | 'editor' | 'viewer'; - -export interface SpaceMember { - userId: string; - name: string | null; - email: string | null; - avatarUrl: string | null; - role: SpaceMemberRole; - isOwner: boolean; -} - -export async function fetchSpaceMembers(spaceId: string): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/members`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch members'); - return data as SpaceMember[]; -} - -export async function addSpaceMember( - spaceId: string, - input: { userId: string; role: SpaceMemberRole }, -): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/members`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(input), - }); - if (!res.ok) { - const d = await res.json().catch(() => ({})); - throw new Error(d?.error ?? 'Failed to add member'); - } -} - -export async function updateSpaceMemberRole( - spaceId: string, - userId: string, - role: SpaceMemberRole, -): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/members/${encodeURIComponent(userId)}`, { - method: 'PATCH', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ role }), - }); - if (!res.ok) { - const d = await res.json().catch(() => ({})); - throw new Error(d?.error ?? 'Failed to update member role'); - } -} - -export async function removeSpaceMember(spaceId: string, userId: string): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/members/${encodeURIComponent(userId)}`, { - method: 'DELETE', - }); - if (!res.ok) { - const d = await res.json().catch(() => ({})); - throw new Error(d?.error ?? 'Failed to remove member'); - } -} - -// ─── ワークスペース招待リンク(再利用トークン)────────────────────── -export type SpaceInviteRole = 'editor' | 'viewer'; - -export interface SpaceInviteInfo { - token: string; - /** 相対パス。origin を前置して共有する。 */ - url: string; - role: SpaceInviteRole; - createdAt: string; - expiresAt: string | null; - revokedAt: string | null; - valid: boolean; -} - -/** 現行の招待リンク(canManageSpace)。無ければ null。 */ -export async function fetchSpaceInvite(spaceId: string): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/invite`); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch invite'); - return (data?.invite ?? null) as SpaceInviteInfo | null; -} - -/** 招待リンクを (再)生成する(canManageSpace)。 */ -export async function createSpaceInvite( - spaceId: string, - input: { role: SpaceInviteRole; expiresInDays?: number | null }, -): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/invite`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(input), - }); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to create invite'); - return data.invite as SpaceInviteInfo; -} - -/** 招待リンクを無効化する(canManageSpace)。 */ -export async function revokeSpaceInvite(spaceId: string): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/invite`, { method: 'DELETE' }); - if (!res.ok && res.status !== 204) { - const d = await res.json().catch(() => ({})); - throw new Error(d?.error ?? 'Failed to revoke invite'); - } -} - -export interface InvitePreview { - spaceId: string; - spaceTitle: string; - role: SpaceInviteRole; -} - -/** - * 招待トークンのプレビュー。`status` で分岐を呼び出し側に渡す: - * 'ok' → preview / 'unauthorized'(要ログイン)/ 'invalid'(無効・期限切れ・不明)。 - */ -export async function fetchInvitePreview( - token: string, -): Promise<{ status: 'ok'; preview: InvitePreview } | { status: 'unauthorized' | 'invalid' }> { - const res = await fetch(`${BASE}/local/spaces/invite/${encodeURIComponent(token)}`); - if (res.status === 401) return { status: 'unauthorized' }; - if (!res.ok) return { status: 'invalid' }; - const data = await res.json(); - return { status: 'ok', preview: data as InvitePreview }; -} - -/** 招待を受諾して参加する。参加先 spaceId を返す。 */ -export async function acceptSpaceInvite(token: string): Promise<{ spaceId: string; alreadyMember: boolean }> { - const res = await fetch(`${BASE}/local/spaces/invite/${encodeURIComponent(token)}/accept`, { - method: 'POST', - }); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to accept invite'); - return data as { spaceId: string; alreadyMember: boolean }; -} - -export interface PickableUser { - id: string; - name: string | null; - email: string | null; - avatarUrl: string | null; -} - -export async function fetchPickableUsers(): Promise { - const res = await fetch(`${BASE}/users/pickable`); - if (!res.ok) return []; - return (await res.json()) as PickableUser[]; -} - -// ─── ワークスペース ツールポリシー ──────────────────────────────────────── - -export interface ToolCategory { - name: string; - sensitive: boolean; - enabled: boolean; -} - -export interface SpaceToolPolicy { - disabledSafe: string[]; - enabledSensitive: string[]; -} - -export interface SpaceToolPolicyResponse { - policy: SpaceToolPolicy; - categories: ToolCategory[]; - sensitiveTools: { name: string; enabled: boolean }[]; -} - -export async function fetchSpaceToolPolicy(spaceId: string): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/tool-policy`); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch tool policy'); - return data as SpaceToolPolicyResponse; -} - -export async function updateSpaceToolPolicy( - spaceId: string, - patch: { disabledSafe?: string[]; enabledSensitive?: string[] }, -): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/tool-policy`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(patch), - }); - if (!res.ok) { - const d = await res.json().catch(() => ({})); - throw new Error(d?.error ?? 'Failed to update tool policy'); - } -} - -// ─── Per-space Python packages ──────────────────────────────────────────── -export interface SpacePythonPackage { - name: string; - spec: string; - addedAt: string; -} -export interface SpacePythonPackagesResponse { - enabled: boolean; - maxPackagesPerSpace: number; - preflight: { ok: boolean; reason?: string }; - packages: SpacePythonPackage[]; -} - -export async function fetchSpacePythonPackages(spaceId: string): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/python-packages`); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch python packages'); - return data as SpacePythonPackagesResponse; -} - -export async function addSpacePythonPackage( - spaceId: string, - spec: string, -): Promise<{ packages: SpacePythonPackage[] }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/python-packages`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ spec }), - }); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to add package'); - return data as { packages: SpacePythonPackage[] }; -} - -export async function removeSpacePythonPackage( - spaceId: string, - name: string, -): Promise<{ packages: SpacePythonPackage[] }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/python-packages/${encodeURIComponent(name)}`, { - method: 'DELETE', - }); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to remove package'); - return data as { packages: SpacePythonPackage[] }; -} - -export async function createSpace(input: { - title: string; - description?: string; - brandColor?: string | null; - visibility?: Space['visibility']; -}): Promise { - const res = await fetch(`${BASE}/local/spaces`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(input), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to create space'); - return data as Space; -} - -export async function updateSpace( - id: string, - patch: Partial>, -): Promise { - const res = await fetch(`${BASE}/local/spaces/${id}`, { - method: 'PATCH', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(patch), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to update space'); - return data as Space; -} - -export async function archiveSpace(id: string): Promise { - const res = await fetch(`${BASE}/local/spaces/${id}/archive`, { method: 'POST' }); - if (!res.ok) { - const d = await res.json().catch(() => ({})); - throw new Error(d?.error ?? 'Failed to archive space'); - } -} - -export interface PromptCoachAxis { - name: string; - score: number; - comment: string; -} -export interface PromptCoachResult { - overall: number; - axes: PromptCoachAxis[]; - rewrite: string; - predicted_piece: { name: string; reason: string } | null; - maestro_tips: Array<{ feature: string; suggestion: string }>; - personalized: string[]; -} - -/** - * On-demand prompt coach: evaluates a draft task prompt before it is submitted. - * Stateless — nothing is persisted. Returns 503 when the coach is unconfigured. - */ -export async function evaluatePrompt(input: { instruction: string; piece?: string }): Promise { - const res = await fetch(`${BASE}/local/tasks/evaluate-prompt`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(input), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to evaluate prompt'); - return data as PromptCoachResult; -} - -export async function fetchLocalTask(taskId: number): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch local task'); - return data.task; -} - -export async function fetchLocalTaskComments(taskId: number): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/comments`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch local task comments'); - return data.comments ?? []; -} - -export async function postLocalTaskComment(taskId: number, body: string, author: string = 'user', attachments?: Array<{ name: string; contentBase64: string }>): Promise { - const payload: Record = { body, author }; - if (attachments && attachments.length > 0) payload.attachments = attachments; - const res = await fetch(`${BASE}/local/tasks/${taskId}/comments`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(payload), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to post local task comment'); -} - -export async function updateLocalTask( - taskId: number, - updates: { title?: string; visibility?: Visibility; visibilityScopeOrgId?: string | null }, -): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}`, { - method: 'PATCH', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(updates), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to update local task'); - return data.task; -} - -/** Trigger on-demand AI title regeneration. Owner/admin only. Returns the new title. */ -export async function regenerateTaskTitle(taskId: number): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/regenerate-title`, { method: 'POST' }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to regenerate title'); - return data.title as string; -} - -export async function continueTaskWithPiece( - taskId: number, - body: { piece: string; instruction: string }, -): Promise<{ jobId: string }> { - const res = await fetch(`${BASE}/local/tasks/${taskId}/continue`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to continue task'); - return data; -} - -export async function deleteLocalTask(taskId: number): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}`, { method: 'DELETE' }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to delete local task'); -} - -export async function cancelLocalTask(taskId: number): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/cancel`, { - method: 'POST', - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to cancel task'); -} - -export async function fetchLocalFiles(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string = ''): Promise<{ basePath: string; path: string; entries: LocalFileEntry[] }> { - const params = new URLSearchParams({ section }); - if (path) params.set('path', path); - const res = await fetch(`${BASE}/local/tasks/${taskId}/files?${params.toString()}`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to list files'); - return data; -} - -export async function fetchLocalFileContent(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string): Promise { - const params = new URLSearchParams({ section, path }); - const res = await fetch(`${BASE}/local/tasks/${taskId}/files/content?${params.toString()}`); - if (!res.ok) { - const data = await res.json().catch(() => ({})); - throw new Error(data?.error ?? 'Failed to read file'); - } - return await res.text(); -} - -export function getLocalFileRawUrl(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string): string { - const params = new URLSearchParams({ section, path }); - return `${BASE}/local/tasks/${taskId}/files/raw?${params.toString()}`; -} - -export function getTrustedLocalHtmlUrl(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string): string { - const params = new URLSearchParams({ section, path, trusted: '1' }); - return `${BASE}/local/tasks/${taskId}/files/raw?${params.toString()}`; -} - -// ── Office プレビュー (Excel / PowerPoint) ─────────────────────────────── -// サーバが Excel→シートのセル配列、PPTX→スライド画像・DOCX→ページ画像(PNG data URL)に変換して返す。 - -export interface OfficeSpreadsheetSheet { - name: string; - rows: string[][]; - rowCount: number; - colCount: number; - truncated: boolean; -} -export interface OfficeSpreadsheetPreview { - kind: 'spreadsheet'; - sheets: OfficeSpreadsheetSheet[]; - truncated: boolean; -} -export interface OfficePresentationPreview { - kind: 'presentation'; - slides: { index: number; dataUrl: string }[]; - slideCount: number; - truncated: boolean; -} -export interface OfficeDocumentPreview { - kind: 'document'; - pages: { index: number; dataUrl: string }[]; - pageCount: number; - truncated: boolean; -} -export type OfficePreview = OfficeSpreadsheetPreview | OfficePresentationPreview | OfficeDocumentPreview; - -/** office-preview エンドポイントの失敗を、変換エンジン未導入(503)とそれ以外で区別できる型。 */ -export class OfficePreviewError extends Error { - /** サーバが返した error コード ('converter_unavailable' 等)。 */ - code?: string; - constructor(message: string, code?: string) { - super(message); - this.name = 'OfficePreviewError'; - this.code = code; - } -} - -export async function fetchOfficePreview(url: string): Promise { - const res = await fetch(url); - if (!res.ok) { - const data = await res.json().catch(() => ({} as { error?: string; message?: string })); - throw new OfficePreviewError(data?.message ?? data?.error ?? 'Failed to load preview', data?.error); - } - return (await res.json()) as OfficePreview; -} - -export function getLocalFileOfficePreviewUrl(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string): string { - const params = new URLSearchParams({ section, path }); - return `${BASE}/local/tasks/${taskId}/files/office-preview?${params.toString()}`; -} - -// アップロード・削除が許される区分。サーバ側 WRITABLE_SECTIONS と一致させること。 -export type WritableTaskSection = 'input' | 'output'; - -// タスクワークスペースの input/output へファイルをアップロード(複数可)。サーバが -// O_EXCL で衝突回避リネーム + ensurePathWithin で section root に封じ込め。実行中は 409。 -export async function uploadLocalFiles( - taskId: number, - section: WritableTaskSection, - path: string, - files: { name: string; contentBase64: string }[], -): Promise<{ uploaded: { name: string; path: string }[] }> { - const res = await fetch(`${BASE}/local/tasks/${taskId}/files/upload`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ section, path, files }), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to upload files'); - return data as { uploaded: { name: string; path: string }[] }; -} - -// タスクワークスペースの input/output ファイルを削除(複数選択は paths 配列)。サーバ側で -// section root に封じ込め、ファイルのみ対象、存在しないものは冪等スキップ。 -export async function deleteLocalFiles( - taskId: number, - section: WritableTaskSection, - paths: string[], -): Promise<{ deleted: string[]; skipped: string[] }> { - const res = await fetch(`${BASE}/local/tasks/${taskId}/files/delete`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ section, paths }), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to delete files'); - return data as { deleted: string[]; skipped: string[] }; -} - -// Blob を受け取りブラウザのダウンロードを発火する(zip 一括ダウンロード用)。 -function triggerBlobDownload(blob: Blob, filename: string): void { - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = filename; - document.body.appendChild(a); - a.click(); - a.remove(); - URL.revokeObjectURL(url); -} - -// タスクワークスペースの複数ファイルを zip でまとめてダウンロード(全 section 可、read)。 -// サーバが paths を section root に封じ込め、ファイルのみ zip 化。1 件でも zip にする。 -export async function downloadLocalFilesZip( - taskId: number, - section: 'workspace' | 'input' | 'output' | 'logs', - paths: string[], - filename = 'files.zip', -): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/files/download-zip`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ section, paths }), - }); - if (!res.ok) { - const data = await res.json().catch(() => ({})); - throw new Error(data?.error ?? 'Failed to download files'); - } - triggerBlobDownload(await res.blob(), filename); -} - -// --- Space files (永続ワークスペース {worktreeDir}/space/{id}/files) --- -// タスク版の files API(getLocalFileRawUrl 等)と同形だが、スペース id でキーする。 - -export async function fetchSpaceFiles(spaceId: string, path: string = ''): Promise<{ basePath: string; path: string; entries: LocalFileEntry[] }> { - const params = new URLSearchParams(); - if (path) params.set('path', path); - const qs = params.toString(); - const res = await fetch(`${BASE}/local/spaces/${spaceId}/files${qs ? `?${qs}` : ''}`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to list files'); - return data; -} - -export async function fetchSpaceFileContent(spaceId: string, path: string): Promise { - const params = new URLSearchParams({ path }); - const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/content?${params.toString()}`); - if (!res.ok) { - const data = await res.json().catch(() => ({})); - throw new Error(data?.error ?? 'Failed to read file'); - } - return await res.text(); -} - -export function getSpaceFileRawUrl(spaceId: string, path: string): string { - const params = new URLSearchParams({ path }); - return `${BASE}/local/spaces/${spaceId}/files/raw?${params.toString()}`; -} - -export function getSpaceTrustedHtmlUrl(spaceId: string, path: string): string { - const params = new URLSearchParams({ path, trusted: '1' }); - return `${BASE}/local/spaces/${spaceId}/files/raw?${params.toString()}`; -} - -export function getSpaceFileOfficePreviewUrl(spaceId: string, path: string): string { - const params = new URLSearchParams({ path }); - return `${BASE}/local/spaces/${spaceId}/files/office-preview?${params.toString()}`; -} - -export async function uploadSpaceFiles( - spaceId: string, - path: string, - files: { name: string; contentBase64: string }[], -): Promise<{ uploaded: { name: string; path: string }[] }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/upload`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ path, files }), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to upload files'); - return data as { uploaded: { name: string; path: string }[] }; -} - -// スペースのワークスペースファイルを削除する。複数選択は paths(相対パス配列)で渡す。 -// 各パスはサーバ側で spaceFilesDir に封じ込め(traversal は 400)、ファイルのみ対象。 -// 存在しないパスは冪等にスキップされる。 -export async function deleteSpaceFiles( - spaceId: string, - paths: string[], -): Promise<{ deleted: string[]; skipped: string[] }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/delete`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ paths }), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to delete files'); - return data as { deleted: string[]; skipped: string[] }; -} - -// スペースのワークスペースに空フォルダを作る(owner/editor のみ)。path は親からの -// 相対パス。サーバ側で spaceFilesDir に封じ込め(traversal は 400)。 -export async function createSpaceFolder( - spaceId: string, - path: string, -): Promise<{ created: string }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/mkdir`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ path }), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to create folder'); - return data as { created: string }; -} - -// スペースのファイル/フォルダをリネーム・移動する(owner/editor のみ)。from→to は -// いずれも相対パス完全形。構造ディレクトリ(input/output/logs/apps/readonly)と隠しは -// 不可。衝突時はサーバが `{stem} (N){ext}` に自動リネームし、確定先 path を返す。 -export async function moveSpaceFile( - spaceId: string, - from: string, - to: string, -): Promise<{ from: string; to: string }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/move`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ from, to }), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to move'); - return data as { from: string; to: string }; -} - -// スペースの複数ファイルを zip でまとめてダウンロード(read)。サーバが paths を -// spaceFilesDir に封じ込め、ファイルのみ zip 化。1 件でも zip にする。 -export async function downloadSpaceFilesZip( - spaceId: string, - paths: string[], - filename = 'files.zip', -): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/download-zip`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ paths }), - }); - if (!res.ok) { - const data = await res.json().catch(() => ({})); - throw new Error(data?.error ?? 'Failed to download files'); - } - triggerBlobDownload(await res.blob(), filename); -} - -// スペースのワークスペースファイルを path 指定で 1 件書き込む(上書き許可)。 -// content(UTF-8 テキスト)または contentBase64(任意バイナリ)のどちらかを渡す。 -// サーバ側で canEditInSpace + ensurePathWithin によりゲートされる。ワークスペース・ -// アプリの postMessage ブリッジ writeFile の代理に使う。 -export async function writeSpaceFile( - spaceId: string, - path: string, - body: { content: string } | { contentBase64: string }, -): Promise<{ path: string; bytes: number }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/write`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ path, ...body }), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to write file'); - return data as { path: string; bytes: number }; -} - -// --- Space calendar (予定 + 日次集計) --- -// spec: docs/superpowers/specs/2026-06-19-space-calendar-design.md - -export interface CalendarEvent { - id: number; - spaceId: string; - ownerId: string | null; - date: string; // 開始日 YYYY-MM-DD(ローカル日付) - endDate: string | null; // 終了日 YYYY-MM-DD。null = 単日 - time: string | null; // 開始 HH:MM。null = 終日 - endTime: string | null; // 終了 HH:MM。null = 終了時刻なし(time が null なら常に null) - title: string; - description: string | null; - createdBy: 'user' | 'agent'; - sourceTaskId: number | null; - createdAt: string; - updatedAt?: string; -} - -export interface CalendarDayCounts { - taskCount: number; - eventCount: number; -} - -export interface CalendarMonth { - days: Record; - events: CalendarEvent[]; -} - -export interface CalendarDayTask { - id: number; - title: string; - state: string; - status: string | null; - createdAt: string; -} - -export interface CalendarDayFile { - name: string; - path: string; - size: number; - mtime: string; -} - -export interface CalendarDay { - tasks: CalendarDayTask[]; - files: CalendarDayFile[]; - events: CalendarEvent[]; -} - -// ── V2: cross-space calendar ────────────────────────────────────────────── -export interface CrossCalendarSpace { - id: string; - name: string; - color: string; // brand_color、無ければ spaceColor(id) で導出済み -} - -export interface CrossCalendarMonth { - // days[date][spaceId] = そのスペースのその日の集計 - days: Record>; - events: CalendarEvent[]; // 各イベントは spaceId を持つ - spaces: CrossCalendarSpace[]; -} - -export async function fetchCrossSpaceCalendarMonth( - month: string, - tzOffset: number, -): Promise { - const params = new URLSearchParams({ month, tz_offset: String(tzOffset) }); - const res = await fetch(`${BASE}/calendar/cross?${params.toString()}`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch calendar'); - return data as CrossCalendarMonth; -} - -export async function fetchSpaceCalendarMonth( - spaceId: string, - month: string, - tzOffset: number, -): Promise { - const params = new URLSearchParams({ month, tz_offset: String(tzOffset) }); - const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar?${params.toString()}`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch calendar'); - return data as CalendarMonth; -} - -export async function fetchSpaceCalendarDay( - spaceId: string, - date: string, - tzOffset: number, -): Promise { - const params = new URLSearchParams({ date, tz_offset: String(tzOffset) }); - const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar/day?${params.toString()}`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch calendar day'); - return data as CalendarDay; -} - -export async function createCalendarEvent( - spaceId: string, - input: { date: string; endDate?: string | null; time?: string | null; endTime?: string | null; title: string; description?: string | null }, -): Promise { - const { endDate, endTime, ...rest } = input; - const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar/events`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ ...rest, end_date: endDate ?? null, end_time: endTime ?? null }), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to create event'); - return data as CalendarEvent; -} - -export async function updateCalendarEvent( - spaceId: string, - eventId: number, - patch: { date?: string; endDate?: string | null; time?: string | null; endTime?: string | null; title?: string; description?: string | null }, -): Promise { - const { endDate, endTime, ...rest } = patch; - const body: Record = { ...rest }; - if (endDate !== undefined) body.end_date = endDate; - if (endTime !== undefined) body.end_time = endTime; - const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar/events/${eventId}`, { - method: 'PATCH', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to update event'); - return data as CalendarEvent; -} - -export async function deleteCalendarEvent(spaceId: string, eventId: number): Promise { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar/events/${eventId}`, { - method: 'DELETE', - }); - if (!res.ok && res.status !== 204) { - const err = await res.json().catch(() => ({ error: res.statusText })); - throw new Error(err.error || res.statusText); - } -} - -export async function updateLocalFileContent(taskId: number, section: string, path: string, content: string): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/files/content`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ section, path, content }), - }); - if (!res.ok) { - const err = await res.json().catch(() => ({ error: res.statusText })); - throw new Error(err.error || res.statusText); - } -} - -// --- Config --- -export async function fetchConfig(): Promise<{ config: any; etag: string; overriddenByEnv: Record }> { - const res = await fetch(`${BASE}/config`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch config'); - return { config: data.config, etag: res.headers.get('etag') ?? '', overriddenByEnv: data.overriddenByEnv ?? {} }; -} - -export async function updateConfig(config: any, etag: string): Promise<{ ok: boolean; conflict?: boolean }> { - const res = await fetch(`${BASE}/config`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json', 'If-Match': etag }, - body: JSON.stringify(config), - }); - const data = await res.json(); - if (res.status === 409) return { ok: false, conflict: true }; - if (!res.ok) throw new Error(data?.error ?? 'Failed to update config'); - return data; -} - -export async function reloadConfig(): Promise { - const res = await fetch(`${BASE}/config/reload`, { method: 'POST' }); - if (!res.ok) throw new Error('Failed to reload config'); -} - -// --- Pieces --- -export interface DriftStatus { drifted: boolean; forkedFromCommit: string | null; latestCommit: string | null } -export interface PieceSummary { name: string; description: string; triggers?: { keywords: string[] }; custom?: boolean; source?: 'builtin' | 'user-custom' | 'global-custom'; ownerId?: string; drift?: DriftStatus; requiredMcp?: string[] } -export interface PieceDef { name: string; description: string; max_movements: number; initial_movement: string; triggers?: { keywords: string[] }; movements: any[]; requiredMcp?: string[] } -/** Full response from GET /api/pieces/:name — includes the server-resolved source. */ -export interface PieceFetchResult { piece: PieceDef; source: 'builtin' | 'user-custom' | 'global-custom'; ownerId?: string } - -/** - * Build a `?key=value&...` query string from defined entries (undefined dropped). - * Used by the space-scoped variants below so passing `spaceId` adds `?spaceId=…` - * and omitting it leaves the per-user URL unchanged (backward compatible). - */ -function buildQuery(params: Record): string { - const parts = Object.entries(params) - .filter(([, v]) => v !== undefined && v !== '') - .map(([k, v]) => `${k}=${encodeURIComponent(v as string)}`); - return parts.length ? `?${parts.join('&')}` : ''; -} - -export async function fetchPieces(spaceId?: string): Promise { - const res = await fetch(`${BASE}/pieces${buildQuery({ spaceId })}`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch pieces'); - return data.pieces; -} - -export async function fetchPiece(name: string, source?: 'builtin' | 'user-custom' | 'global-custom', spaceId?: string): Promise { - const url = `${BASE}/pieces/${name}${buildQuery({ source, spaceId })}`; - const res = await fetch(url); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch piece'); - return { piece: data.piece, source: data.source, ownerId: data.ownerId }; -} - -export async function updatePiece(name: string, piece: PieceDef, source?: 'builtin' | 'user-custom' | 'global-custom', spaceId?: string): Promise { - const url = `${BASE}/pieces/${name}${buildQuery({ source, spaceId })}`; - const res = await fetch(url, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(spaceId ? { ...piece, spaceId } : piece), - }); - if (!res.ok) { const d = await res.json(); throw new Error(d?.error ?? 'Failed to update piece'); } -} - -export interface PieceCreateResult { source: 'builtin' | 'user-custom' | 'global-custom' } - -export async function createPiece(piece: PieceDef, spaceId?: string): Promise { - const res = await fetch(`${BASE}/pieces${buildQuery({ spaceId })}`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(spaceId ? { ...piece, spaceId } : piece), - }); - if (!res.ok) { const d = await res.json(); throw new Error(d?.error ?? 'Failed to create piece'); } - const d = await res.json(); - return { source: d.source ?? 'user-custom' }; -} - -export async function deletePiece(name: string, source?: 'builtin' | 'user-custom' | 'global-custom', spaceId?: string): Promise { - const url = `${BASE}/pieces/${name}${buildQuery({ source, spaceId })}`; - const res = await fetch(url, { method: 'DELETE' }); - if (!res.ok) { const d = await res.json(); throw new Error(d?.error ?? 'Failed to delete piece'); } -} - -// --- Tools --- -/** - * Runtime tool catalog entry. Mirrors `ToolCatalogEntry` exported by - * `src/bridge/tools-api.ts` (server side). See design doc step 4: - * docs/superpowers/specs/2026-05-21-settings-ui-and-config-restructure-design.md - */ -export interface ToolCatalogEntry { - name: string; - source: 'builtin' | 'meta' | 'mcp'; - /** - * Coarse grouping for UI. For builtin/meta tools this is a module name - * (e.g. 'core', 'web'). For MCP tools the server uses `mcp:`. - */ - category: string; - /** MCP server id (only set when source === 'mcp'). */ - serverId?: string; - /** Whether the tool can be invoked right now. */ - available: boolean; - /** Human-readable explanation when `available` is false. */ - reason?: string; - /** - * - 'global' → meta tools auto-injected by the agent loop (always available) - * - 'piece' → builtin tool gated by the workspace tool policy (Settings → Tools) - * - 'user' → per-user resource (MCP / SSH) - */ - scope: 'global' | 'piece' | 'user'; -} - -export async function fetchTools(): Promise { - const res = await fetch(`${BASE}/tools`); - if (!res.ok) throw new Error('Failed to fetch tools'); - const data = (await res.json()) as { tools?: unknown }; - if (!Array.isArray(data.tools)) return []; - // Server may still occasionally serve the legacy flat-string shape (e.g. - // during a transient mismatch / proxy / cache). Filter to only well-formed - // catalog entries so the UI never crashes; legacy strings are dropped. - return data.tools.filter( - (t): t is ToolCatalogEntry => - typeof t === 'object' && t !== null && typeof (t as { name?: unknown }).name === 'string', - ); -} - -// --- Subtasks --- -export interface SubtaskFiles { - files: string[]; - categories: Record; -} - -export async function fetchSubtaskFiles(taskId: number, jobId: string): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/subtasks/${jobId}/files`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask files'); - return { files: data.files ?? [], categories: data.categories ?? {} }; -} - -export function subtaskFileRawUrl(taskId: number, jobId: string, filePath: string): string { - return `${BASE}/local/tasks/${taskId}/subtasks/${jobId}/files/${filePath}`; -} - -export async function fetchSubtaskFileContent(taskId: number, jobId: string, filePath: string): Promise { - const res = await fetch(subtaskFileRawUrl(taskId, jobId, filePath)); - if (!res.ok) throw new Error('Failed to fetch subtask file content'); - return res.text(); -} - -export async function fetchSubtaskActivities(taskId: number): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/subtasks/activities`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask activities'); - return data.subtasks ?? []; -} - -export async function fetchSubtaskActivity(taskId: number, jobId: string): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/subtasks/${jobId}/activity`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask activity'); - return data.activityLog ?? ''; -} - -export async function putFeedback( - taskId: number, - feedback: { rating: 'good' | 'bad'; tags: string[]; comment?: string }, -): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/feedback`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(feedback), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to update feedback'); - return data.task; -} - -// --- Share --- -export async function shareTask(taskId: number): Promise<{ shareToken: string; shareUrl: string }> { - const res = await fetch(`${BASE}/local/tasks/${taskId}/share`, { method: 'POST' }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to share task'); - return data; -} - -export async function unshareTask(taskId: number): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/share`, { method: 'DELETE' }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to unshare task'); -} - -export async function fetchSharedTask(token: string): Promise { - const res = await fetch(`${BASE}/shared/${token}`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Not found'); - return data.task; -} - -export async function fetchSharedTaskComments(token: string): Promise { - const res = await fetch(`${BASE}/shared/${token}/comments`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch comments'); - return data.comments ?? []; -} - -export async function fetchSharedFiles(token: string, path: string = ''): Promise<{ basePath: string; path: string; entries: LocalFileEntry[] }> { - const params = new URLSearchParams(); - if (path) params.set('path', path); - const res = await fetch(`${BASE}/shared/${token}/files?${params.toString()}`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to list files'); - return data; -} - -export async function fetchSharedFileContent(token: string, path: string): Promise { - const params = new URLSearchParams({ path }); - const res = await fetch(`${BASE}/shared/${token}/files/content?${params.toString()}`); - if (!res.ok) throw new Error('Failed to read file'); - return res.text(); -} - -export function getSharedFileRawUrl(token: string, path: string): string { - const params = new URLSearchParams({ path }); - return `${BASE}/shared/${token}/files/raw?${params.toString()}`; -} - -export async function fetchSharedSubtaskActivities(token: string): Promise { - const res = await fetch(`${BASE}/shared/${token}/subtasks/activities`); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask activities'); - return data.subtasks ?? []; -} - -// 個別サブタスク(共有・read-only)。本体の fetchSubtaskActivity / fetchSubtaskFiles の -// 共有版。TaskDataSource(shared) から使う。封じ込めはサーバ側(share-api.ts)。 -export async function fetchSharedSubtaskActivity(token: string, jobId: string): Promise { - const res = await fetch(`${BASE}/shared/${encodeURIComponent(token)}/subtasks/${jobId}/activity`); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask activity'); - return data.activityLog ?? ''; -} - -export async function fetchSharedSubtaskFiles(token: string, jobId: string): Promise { - const res = await fetch(`${BASE}/shared/${encodeURIComponent(token)}/subtasks/${jobId}/files`); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask files'); - return { files: data.files ?? [], categories: data.categories ?? {} }; -} - -export function sharedSubtaskFileRawUrl(token: string, jobId: string, filePath: string): string { - return `${BASE}/shared/${encodeURIComponent(token)}/subtasks/${jobId}/files/${filePath}`; -} - -// --- 公開アプリ共有リンク(read-only・認証なし) --- -// スペース内の 1 ワークスペースアプリ(apps/{app}/)をログイン不要の公開トークンで -// read-only 配信する。公開取得(/api/app-share/:token/*)は認証なし、発行/失効 -// (/api/local/spaces/:id/apps/:app/share)は canManageSpace(owner/admin)が必要。 -// サーバ側で apps/{app}/+output/ にパス封じ込め。詳細は src/bridge/app-share-api.ts。 - -// 公開アプリのメタ。entryPath は entry HTML(apps/{app}/index.html 等)。空アプリ等で -// 見つからなければ null になりうる(呼び出し側で 404 表示)。失効/不正トークンは throw。 -export async function fetchAppShareMeta(token: string): Promise<{ appName: string; entryPath: string | null }> { - const res = await fetch(`${BASE}/app-share/${token}`); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Not found'); - return data.app; -} - -export async function fetchAppShareFileContent(token: string, path: string): Promise { - const params = new URLSearchParams({ path }); - const res = await fetch(`${BASE}/app-share/${token}/files/content?${params.toString()}`); - if (!res.ok) { - const data = await res.json().catch(() => ({})); - throw new Error(data?.error ?? 'Failed to read file'); - } - return res.text(); -} - -export async function fetchAppShareFiles( - token: string, - dir: string = '', -): Promise<{ basePath: string; path: string; entries: LocalFileEntry[] }> { - const params = new URLSearchParams(); - if (dir) params.set('dir', dir); - const qs = params.toString(); - const res = await fetch(`${BASE}/app-share/${token}/files/list${qs ? `?${qs}` : ''}`); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to list files'); - return data; -} - -export function getAppShareRawUrl(token: string, path: string): string { - const params = new URLSearchParams({ path }); - return `${BASE}/app-share/${token}/files/raw?${params.toString()}`; -} - -// 発行/失効/取得(canManageSpace)。shareUrl は相対パス(/ui/app/:token)。表示時は -// window.location.origin を前置する。 -export async function createAppShareLink( - spaceId: string, - app: string, -): Promise<{ token: string; shareUrl: string }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/apps/${encodeURIComponent(app)}/share`, { - method: 'POST', - }); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to create share link'); - return data as { token: string; shareUrl: string }; -} - -export async function getAppShareLink( - spaceId: string, - app: string, -): Promise<{ token: string | null; shareUrl?: string; revokedAt?: string | null }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/apps/${encodeURIComponent(app)}/share`); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch share link'); - return data as { token: string | null; shareUrl?: string; revokedAt?: string | null }; -} - -export async function revokeAppShareLink(spaceId: string, app: string): Promise<{ ok: true }> { - const res = await fetch(`${BASE}/local/spaces/${spaceId}/apps/${encodeURIComponent(app)}/share`, { - method: 'DELETE', - }); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to revoke share link'); - return data as { ok: true }; -} - -// --- Browser Session Profiles --- -export interface BrowserSessionProfile { - id: number; - label: string; - startUrl: string; - matchPatterns: string[]; - storageOrigins: string[]; - loggedInSelector: string | null; - loginUrlPatterns: string[]; - status: 'pending' | 'active' | 'expired' | 'revoked' | 'error'; - stateVersion: number; - lastSavedAt: string | null; - lastUsedAt: string | null; - lastValidatedAt: string | null; - lastError: string | null; - createdAt: string; - updatedAt: string; - /** Owning space (null = legacy/personal). Surfaced for space-scoped listings. */ - spaceId?: string | null; - /** The user who created the profile (its DEK owner). */ - ownerId?: string; - /** False when the requesting viewer is NOT the owner: visible but unusable - * (the per-user DEK can only be decrypted by its owner). */ - decryptableByViewer?: boolean; -} - -const SESS_BASE = `${BASE}/browser-sessions`; - -export async function listBrowserSessionProfiles(spaceId?: string): Promise { - const r = await fetch(`${SESS_BASE}/profiles${buildQuery({ spaceId })}`, { credentials: 'same-origin' }); - if (!r.ok) throw new Error(`listBrowserSessionProfiles: ${r.status}`); - return (await r.json() as { profiles: BrowserSessionProfile[] }).profiles; -} - -export async function createBrowserSessionProfile( - input: Partial & { label: string; startUrl: string }, - spaceId?: string, -): Promise { - const r = await fetch(`${SESS_BASE}/profiles${buildQuery({ spaceId })}`, { - method: 'POST', credentials: 'same-origin', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(spaceId ? { ...input, spaceId } : input), - }); - if (!r.ok) throw new Error(`createBrowserSessionProfile: ${r.status}`); - return (await r.json() as { profile: BrowserSessionProfile }).profile; -} - -export async function startBrowserSessionLogin(id: number): Promise<{ sessionId: string; novncPath: string }> { - const r = await fetch(`${SESS_BASE}/profiles/${id}/login`, { method: 'POST', credentials: 'same-origin' }); - if (!r.ok) throw new Error((await r.text().catch(() => '')) || `startLogin: ${r.status}`); - return r.json(); -} - -export async function saveBrowserSession(id: number, sessionId: string): Promise { - const r = await fetch(`${SESS_BASE}/profiles/${id}/save`, { - method: 'POST', credentials: 'same-origin', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ sessionId }), - }); - if (!r.ok) throw new Error(`saveBrowserSession: ${r.status}`); - return (await r.json() as { profile: BrowserSessionProfile }).profile; -} - -export async function cancelBrowserSession(id: number, sessionId: string): Promise { - await fetch(`${SESS_BASE}/profiles/${id}/cancel`, { - method: 'POST', credentials: 'same-origin', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ sessionId }), - }); -} - -export async function testBrowserSessionProfile(id: number): Promise<{ - verdict: { expired: boolean; reason?: string }; - finalUrl: string; - statusCode: number; -}> { - const r = await fetch(`${SESS_BASE}/profiles/${id}/test`, { method: 'POST', credentials: 'same-origin' }); - if (!r.ok) throw new Error(`testBrowserSession: ${r.status}`); - return r.json(); -} - -export async function deleteBrowserSessionProfile(id: number, spaceId?: string): Promise { - const r = await fetch(`${SESS_BASE}/profiles/${id}${buildQuery({ spaceId })}`, { method: 'DELETE', credentials: 'same-origin' }); - if (!r.ok) throw new Error(`deleteBrowserSessionProfile: ${r.status}`); -} - -// --- User/space folder files (browser-macros / recordings) --- -export interface FolderFileEntry { name: string; size: number; mtime: string } - -/** - * List files in a writable user-folder subdir. Passing `spaceId` scopes the - * listing to that space's folder (`data/spaces/{id}/{subdir}`); omitting it - * targets the per-user folder. Matches the convention used by the piece/agents - * space-scoped fetchers above. - */ -export async function listFolderFiles(subdir: 'browser-macros' | 'recordings', spaceId?: string): Promise { - const r = await fetch(`${BASE}/users/me/folder/list${buildQuery({ subdir, spaceId })}`, { credentials: 'same-origin' }); - if (!r.ok) throw new Error(`listFolderFiles: ${r.status}`); - return (await r.json() as { files: FolderFileEntry[] }).files; -} - -/** Read one folder file's text content (space-scoped when `spaceId` is given). */ -export async function getFolderFile(subdir: 'browser-macros' | 'recordings', path: string, spaceId?: string): Promise { - const r = await fetch(`${BASE}/users/me/folder/file${buildQuery({ subdir, path, spaceId })}`, { credentials: 'same-origin' }); - if (!r.ok) throw new Error(`getFolderFile: ${r.status}`); - return r.text(); -} - -/** Delete one folder file (space-scoped when `spaceId` is given). */ -export async function deleteFolderFile(subdir: 'browser-macros' | 'recordings', path: string, spaceId?: string): Promise { - const r = await fetch(`${BASE}/users/me/folder/file${buildQuery({ subdir, path, spaceId })}`, { method: 'DELETE', credentials: 'same-origin' }); - if (!r.ok) throw new Error(`deleteFolderFile: ${r.status}`); -} - -// --- Reflection --- -export interface LatestReflectionForTask { - snapshotId: string; - outcome: string; - memoryChanges: number | null; - pieceEdited: boolean; -} - -export async function getLatestReflectionForTask( - taskId: number, -): Promise { - const res = await fetch(`${BASE}/local/reflection/latest-for-task/${taskId}`); - if (res.status === 404) return null; - if (!res.ok) throw new Error(`getLatestReflectionForTask: ${res.status}`); - const data = await res.json(); - // API returns null body when no reflection exists - if (!data || !data.snapshotId) return null; - return data as LatestReflectionForTask; -} - -// --- User Folder Pets --- -export interface PetSettings { - enabled: boolean; - activePetId: string | null; - size: 32 | 48 | 64 | 80; - position: 'bottom-right'; - sound: boolean; - reducedMotion: boolean; - toolSparkEnabled: boolean; - workerPets: Record; -} - -export interface WorkerInfo { - id: string; - endpoint: string | null; - model: string | null; - roles: string[]; - enabled: boolean; - /** True if this worker fronts an LLM gateway / proxy (Phase A). */ - proxy?: boolean; - /** Proxy implementation; only 'litellm' is currently shipped. */ - proxyType?: 'litellm'; -} - -export async function fetchWorkers(): Promise { - const res = await fetch('/api/workers', { credentials: 'include' }); - if (!res.ok) return []; - const data = await res.json() as { workers?: WorkerInfo[] }; - return data.workers ?? []; -} - -export interface BackendInfo { - id: string; - model: string | null; - online: boolean; -} - -export interface WorkerBackendsResponse { - source: 'direct' | 'proxy'; - proxyType?: 'litellm'; - backends: BackendInfo[]; - /** Set when the proxy probe failed (network error, 5xx). UI renders degraded. */ - error?: string; -} - -export async function fetchWorkerBackends(workerId: string): Promise { - const res = await fetch(`/api/workers/${encodeURIComponent(workerId)}/backends`, { - credentials: 'include', - }); - if (!res.ok && res.status !== 502) { - // 502 still carries a typed payload from the server. - return { source: 'direct', backends: [], error: `HTTP ${res.status}` }; - } - return await res.json() as WorkerBackendsResponse; -} - -export interface PetSummary { - id: string; - name: string; - description: string | null; - spriteFile: string | null; - previewFile: string | null; - frameWidth: number | null; - frameHeight: number | null; - gridCols: number | null; - gridRows: number | null; - updatedAt: string; -} - -export interface PetDetail extends PetSummary { - manifest: Record; -} - -export interface PetsResponse { - pets: PetSummary[]; - settings: PetSettings; -} - -const PETS_BASE = '/api/users/me/pets'; - -export function petAssetUrl(petId: string, file: string): string { - return `${PETS_BASE}/${encodeURIComponent(petId)}/assets/${encodeURIComponent(file)}`; -} - -export async function fetchPets(): Promise { - const res = await fetch(PETS_BASE, { credentials: 'include' }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch pets'); - return data; -} - -export async function fetchPet(petId: string): Promise { - const res = await fetch(`${PETS_BASE}/${encodeURIComponent(petId)}`, { credentials: 'include' }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch pet'); - return data.pet; -} - -export async function importPet(file: File, options: { petId?: string; overwrite?: boolean } = {}): Promise { - const params = new URLSearchParams(); - params.set('filename', file.name); - if (options.petId) params.set('petId', options.petId); - if (options.overwrite) params.set('overwrite', 'true'); - const res = await fetch(`${PETS_BASE}/import?${params.toString()}`, { - method: 'POST', - credentials: 'include', - headers: { 'Content-Type': 'application/zip' }, - body: await file.arrayBuffer(), - }); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to import pet'); - return data.pet; -} - -export async function deletePet(petId: string): Promise { - const res = await fetch(`${PETS_BASE}/${encodeURIComponent(petId)}`, { - method: 'DELETE', - credentials: 'include', - }); - const data = await res.json().catch(() => ({})); - if (!res.ok) throw new Error(data?.error ?? 'Failed to delete pet'); -} - -export async function updatePetSettings(patch: Partial): Promise { - const res = await fetch(`${PETS_BASE}/settings`, { - method: 'PUT', - credentials: 'include', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(patch), - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error ?? 'Failed to update pet settings'); - return data.settings; -} - -// ── Side Info Panel ──────────────────────────────────────────────────────── - -export interface NodeStatus { - nodeId: string; - workerId: string; - source: 'direct' | 'proxy'; - online: boolean; - busy: boolean; - busySlots: number; - totalSlots: number; - loadedModel: string | null; - throughputTps: number | null; - lastSeen: string; - lastProbeError?: string; -} - -export interface WorkerStatusBackendRow { - id: string; - state: 'idle' | 'running'; - busySlots: number; - totalSlots: number; - online: boolean | null; -} - -export interface WorkerStatusRow { - id: string; - name: string; - roles: string[]; - state: 'idle' | 'running'; - /** True when this row represents a `proxy: true` worker. */ - proxy: boolean; - /** Slot pressure from BackendStatusRegistry. Populated for direct workers with a registry probe row. */ - busySlots?: number; - totalSlots?: number; - online?: boolean; - /** Per-backend rows for proxy workers (Phase 3c + dashboard tree). */ - backends?: WorkerStatusBackendRow[]; - /** - * Occupants of this worker — users whose running jobs use it, each with - * the job kind (`task_kind`: 'agent' = normal, 'reflection' = learning). - * Admin-only — the server only populates this for admins, so non-admin - * clients always receive undefined. - */ - occupants?: Array<{ user: string; kind: string }>; -} - -export async function fetchWorkerStatuses(): Promise { - const res = await fetch('/api/local/dashboard/workers'); - if (!res.ok) throw new Error(`Failed to list worker statuses: ${res.status}`); - return (await res.json()).workers; -} - -/** Thrown by fetchNodeStatus when the registry is not configured (HTTP 503). */ -export class NodeStatusUnavailableError extends Error { - constructor() { - super('node-status registry not configured'); - this.name = 'NodeStatusUnavailableError'; - } -} - -export async function fetchNodeStatus(): Promise { - const res = await fetch('/api/local/dashboard/node-status'); - if (!res.ok) { - // 503 = registry not configured (e.g. legacy install). Surface as an - // error so the React Query hook can back off polling instead of - // hammering the server every 5s indefinitely. The hook turns this - // into an empty list for rendering. - if (res.status === 503) throw new NodeStatusUnavailableError(); - throw new Error(`Failed to list node status: ${res.status}`); - } - return (await res.json()).nodes; -} - -// ── AAO Gateway: virtual key admin (Phase 2a + 2b) ────────────────────── -// -// Talks to /api/admin/gateway/keys/* — requires admin role. The raw -// bearer key is returned ONCE from POST / and POST /:id/rotate; UI must -// surface it to the user immediately and not expose it again. - -export interface GatewayKey { - id: string; - object: 'gateway.key'; - keyPrefix: string; - team: string; - allowedModels: string[] | null; - source: 'admin' | 'config-import'; - createdAt: string; - createdBy: string | null; - revokedAt: string | null; - revokedBy: string | null; - lastUsedAt: string | null; - tokensBudget: number | null; - rateLimitRpm: number | null; - /** Only present on POST / rotate responses. */ - key?: string; -} - -export interface GatewayKeyUsageResponse { - keyId: string; - currentPeriod: string; - tokensIn: number; - tokensOut: number; - tokensTotal: number; - tokensBudget: number | null; - remaining: number | null; - requestsThisMonth: number; - rateLimitRpm: number | null; - // Phase 3a F9: `rateRecentRequests` removed. The field was always - // null because the admin process doesn't own the gateway's live - // RateLimiter. Phase 3b/3c may re-add it via gateway IPC. - history: Array<{ period: string; tokensIn: number; tokensOut: number; requests: number }>; -} - -export async function listGatewayKeys(params?: { team?: string; activeOnly?: boolean }): Promise { - const q = new URLSearchParams(); - if (params?.team) q.set('team', params.team); - if (params?.activeOnly) q.set('activeOnly', 'true'); - const qs = q.toString(); - const res = await fetch(`/api/admin/gateway/keys${qs ? `?${qs}` : ''}`); - if (!res.ok) throw new Error(`Failed to list gateway keys: ${res.status}`); - return (await res.json()).keys; -} - -export async function createGatewayKey(input: { - team: string; - allowedModels?: string[]; - tokensBudget?: number | null; - rateLimitRpm?: number | null; -}): Promise { - const res = await fetch(`/api/admin/gateway/keys`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(input), - }); - if (!res.ok) { - const text = await res.text(); - throw new Error(`Failed to create gateway key (${res.status}): ${text}`); - } - return res.json(); -} - -export async function getGatewayKey(id: string): Promise { - const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}`); - if (!res.ok) throw new Error(`Failed to get gateway key: ${res.status}`); - return res.json(); -} - -export async function patchGatewayKey( - id: string, - patch: { - tokensBudget?: number | null; - rateLimitRpm?: number | null; - allowedModels?: string[] | null; - }, -): Promise { - const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}`, { - method: 'PATCH', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(patch), - }); - if (!res.ok) { - const text = await res.text(); - throw new Error(`Failed to update gateway key (${res.status}): ${text}`); - } - return res.json(); -} - -export async function revokeGatewayKey(id: string): Promise { - const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}/revoke`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({}), - }); - if (!res.ok) { - const text = await res.text(); - throw new Error(`Failed to revoke gateway key (${res.status}): ${text}`); - } -} - -export async function rotateGatewayKey(id: string): Promise { - const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}/rotate`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({}), - }); - if (!res.ok) { - const text = await res.text(); - throw new Error(`Failed to rotate gateway key (${res.status}): ${text}`); - } - return res.json(); -} - -export async function deleteGatewayKey(id: string): Promise { - const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}`, { method: 'DELETE' }); - if (!res.ok) { - const text = await res.text(); - throw new Error(`Failed to delete gateway key (${res.status}): ${text}`); - } -} - -export async function getGatewayKeyUsage(id: string): Promise { - const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}/usage`); - if (!res.ok) throw new Error(`Failed to get gateway key usage: ${res.status}`); - return res.json(); -} - -// ============================================================ -// Gateway Server status (Phase 3c) — read-only admin endpoint. -// Drives the Settings → Gateway Server badge and error list. -// ============================================================ -export type GatewayServerState = - | 'unavailable' - | 'disabled' - | 'starting' - | 'running' - | 'stopping' - | 'misconfigured'; - -export interface GatewayServerStatus { - state: GatewayServerState; - /** Desired-enabled flag read from current config. Null when no ConfigManager. */ - enabled: boolean | null; - /** Validation errors that prevented the gateway from starting. */ - errors: string[]; - mounted: boolean; - sharedPort: number; - /** Only present when state==='unavailable'. */ - message?: string; -} - -export async function getGatewayServerStatus(): Promise { - const res = await fetch('/api/admin/gateway/status'); - if (!res.ok) throw new Error(`Failed to get gateway status: ${res.status}`); - return res.json(); -} - -// ── Skills API ────────────────────────────────────────────────────────── - -export interface SkillSummary { - name: string; - description: string; - triggers: string[]; - source: 'system' | 'user'; - hasDir: boolean; -} - -export interface SkillDetail extends SkillSummary { - /** Body only (frontmatter stripped) — for read-only preview. */ - content: string; - /** Full SKILL.md incl. frontmatter — what the editor must edit/save. */ - raw: string; - files: string[]; - findings: Array<{ severity: 'medium' | 'high'; pattern: string; match: string; line: number; file?: string }>; - maxSeverity: 'high' | 'medium' | 'none'; -} - -export async function fetchSkills(scope?: string, spaceId?: string): Promise { - const res = await fetch(`/api/skills${buildQuery({ scope, spaceId })}`); - if (!res.ok) throw new Error('Failed to fetch skills'); - const data = await res.json(); - return data.skills; -} - -export async function fetchSkillDetail(name: string, scope?: string, spaceId?: string): Promise { - const res = await fetch(`/api/skills/${encodeURIComponent(name)}${buildQuery({ scope, spaceId })}`); - if (!res.ok) throw new Error('Skill not found'); - return res.json(); -} - -export async function createSkill(name: string, content: string, scope: string, spaceId?: string): Promise<{ name: string; severity: string; findings: any[] }> { - const res = await fetch(`/api/skills${buildQuery({ spaceId })}`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(spaceId ? { name, content, scope, spaceId } : { name, content, scope }), - }); - if (!res.ok) { - const err = await res.json().catch(() => ({ error: 'Unknown error' })); - throw new Error(err.error); - } - return res.json(); -} - -export async function updateSkill(name: string, content: string, scope: string, spaceId?: string): Promise { - const res = await fetch(`/api/skills/${encodeURIComponent(name)}${buildQuery({ scope, spaceId })}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(spaceId ? { content, spaceId } : { content }), - }); - if (!res.ok) { - const err = await res.json().catch(() => ({ error: 'Unknown error' })); - throw new Error(err.error); - } - return res.json(); -} - -export async function deleteSkill(name: string, scope: string, spaceId?: string): Promise { - const res = await fetch(`/api/skills/${encodeURIComponent(name)}${buildQuery({ scope, spaceId })}`, { method: 'DELETE' }); - if (!res.ok) { - const err = await res.json().catch(() => ({ error: 'Unknown error' })); - throw new Error(err.error); - } -} - -export async function installSkillFromUrl(url: string, scope: string, selectedSkills?: string[], spaceId?: string): Promise { - const res = await fetch(`/api/skills/install-from-url${buildQuery({ spaceId })}`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(spaceId ? { url, scope, selectedSkills, spaceId } : { url, scope, selectedSkills }), - }); - if (!res.ok) { - const err = await res.json().catch(() => ({ error: 'Unknown error' })); - throw new Error(err.error); - } - return res.json(); -} - -// ── Notifications V2 (Web Push) ─────────────────────────────────────── - -export type NotifyEventType = 'running' | 'succeeded' | 'failed' | 'waiting_human'; - -export interface NotificationPrefsDTO { - userId: string; - enabled: boolean; - events: Record; - includeDetails: boolean; - v1Migrated: boolean; - updatedAt: string; -} - -export interface NotificationPrefsInput { - enabled?: boolean; - events?: Partial>; - includeDetails?: boolean; -} - -export interface PushSubscriptionPublic { - id: string; - endpointHost: string; - userAgent: string | null; - createdAt: string; - lastSuccessAt: string | null; - lastFailureAt: string | null; - failureCount: number; -} - -export interface VapidPublicKeyDTO { - publicKey: string; - keyId: string; -} - -async function notificationsJsonOrThrow(res: Response, fallback: string): Promise { - const data = await res.json().catch(() => ({} as Record)); - if (!res.ok) throw new Error((data as { error?: string }).error ?? fallback); - return data as T; -} - -export async function fetchVapidPublicKey(): Promise { - const res = await fetch(`${BASE}/notifications/vapid-public-key`); - return notificationsJsonOrThrow(res, 'failed to fetch VAPID key'); -} - -export async function listPushSubscriptions(): Promise { - const res = await fetch(`${BASE}/notifications/subscriptions`); - const data = await notificationsJsonOrThrow<{ subscriptions: PushSubscriptionPublic[] }>( - res, 'failed to list subscriptions', - ); - return data.subscriptions; -} - -export async function postPushSubscription(input: { - endpoint: string; - p256dh: string; - auth: string; - userAgent?: string; -}): Promise<{ id: string }> { - const res = await fetch(`${BASE}/notifications/subscriptions`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(input), - }); - return notificationsJsonOrThrow(res, 'failed to register subscription'); -} - -export async function deletePushSubscription(id: string): Promise { - const res = await fetch(`${BASE}/notifications/subscriptions/${id}`, { method: 'DELETE' }); - await notificationsJsonOrThrow(res, 'failed to delete subscription'); -} - -export async function fetchNotificationPrefs(): Promise { - const res = await fetch(`${BASE}/notifications/preferences`); - return notificationsJsonOrThrow(res, 'failed to fetch preferences'); -} - -export async function updateNotificationPrefs( - input: NotificationPrefsInput, -): Promise { - const res = await fetch(`${BASE}/notifications/preferences`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(input), - }); - return notificationsJsonOrThrow(res, 'failed to update preferences'); -} - -export async function migrateLocalStoragePrefs( - input: NotificationPrefsInput, -): Promise<{ ok: boolean; prefs: NotificationPrefsDTO } | { alreadyMigrated: true }> { - const res = await fetch(`${BASE}/notifications/preferences/migrate-from-localstorage`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(input), - }); - if (res.status === 409) return { alreadyMigrated: true }; - return notificationsJsonOrThrow(res, 'failed to migrate preferences'); -} - -export async function postTestNotification(): Promise<{ ok: boolean }> { - const res = await fetch(`${BASE}/notifications/test`, { method: 'POST' }); - return notificationsJsonOrThrow(res, 'failed to send test notification'); -} - -// ============================================================ -// LLM usage dashboard v2 (per-user, multi-axis group-by + local timezone). -// Spec: docs/superpowers/specs/2026-06-11-usage-dashboard-v2-design.md -export interface UsageCounters { - tokensIn: number; - tokensOut: number; - requests: number; -} -export type UsageGroupBy = 'source' | 'model' | 'route' | 'user' | 'org'; - -/** One time bucket: a per-series-key map of counters. Keys come from `keys`. */ -export interface UsageBucket { - bucket: string; // 'YYYY-MM-DD' | 'YYYY-Www' | 'YYYY-MM' - segments: Record; -} -export interface UsageByUser extends UsageCounters { - userId: string; - /** Resolved display name (real users); 'local' / 'system' for sentinels. */ - displayName: string; -} -export interface UsageDailyResponse { - from: string; - to: string; - granularity: 'hour' | 'day' | 'week' | 'month'; - groupBy: UsageGroupBy; - tzOffset: number; - scope: 'all' | 'self'; - /** Ordered series keys (legend/palette order). */ - keys: string[]; - /** Human labels for keys (only populated for groupBy=user; else key=label). */ - labels: Record; - series: UsageBucket[]; - totals: Record; - byUser?: UsageByUser[]; // admin / local mode only -} - -export async function getUsageDaily(params: { - from?: string; - to?: string; - granularity?: 'hour' | 'day' | 'week' | 'month'; - groupBy?: UsageGroupBy; - tzOffset?: number; -}): Promise { - const qs = new URLSearchParams(); - if (params.from) qs.set('from', params.from); - if (params.to) qs.set('to', params.to); - if (params.granularity) qs.set('granularity', params.granularity); - if (params.groupBy) qs.set('groupBy', params.groupBy); - if (params.tzOffset !== undefined) qs.set('tzOffset', String(params.tzOffset)); - const q = qs.toString(); - const res = await fetch(`${BASE}/usage/daily${q ? `?${q}` : ''}`); - if (!res.ok) throw new Error(`Failed to load usage (${res.status})`); - return res.json(); -} - -// ── Delegate Observability (D0) ────────────────────────────────────────── -// Task 3 API: GET /api/local/tasks/:id/delegate-runs -// GET /api/local/tasks/:id/delegate-runs/:runId/timeline - -/** Lightweight event record returned by the delegate-run timeline endpoint. */ -export interface TraceEventLite { - eventId: string; - ts: string; - seq: number; - kind: string; - movement?: string; - iteration?: number; - payload: unknown; -} - -export async function fetchDelegateRuns(taskId: number): Promise { - const res = await fetch(`${BASE}/local/tasks/${taskId}/delegate-runs`); - if (!res.ok) throw new Error(`fetchDelegateRuns failed: ${res.status}`); - const body = await res.json(); - return { runs: body.runs ?? [], subtasks: body.subtasks ?? [] }; -} - -export async function fetchDelegateRunTimeline(taskId: number, delegateRunId: string, jobId?: string): Promise { - const q = jobId ? `?jobId=${encodeURIComponent(jobId)}` : ''; - const res = await fetch(`${BASE}/local/tasks/${taskId}/delegate-runs/${encodeURIComponent(delegateRunId)}/timeline${q}`); - if (!res.ok) throw new Error(`fetchDelegateRunTimeline failed: ${res.status}`); - return (await res.json()).events ?? []; -} +// ドメイン別モジュール(ui/src/api/*.ts)への分割に伴うバレル。 +// 既存の `import { ... } from '../api'` を全て無変更で通すため、 +// 分割前に export されていた全シンボルをここから再エクスポートする。 +// 注: './api/client'(BASE / buildQuery / triggerBlobDownload)は分割前は +// モジュール内部の非公開ヘルパーだったため、公開面を変えないよう再エクスポートしない。 +export * from './api/config'; +export * from './api/users'; +export * from './api/tasks'; +export * from './api/task-files'; +export * from './api/spaces'; +export * from './api/space-files'; +export * from './api/calendar'; +export * from './api/pieces'; +export * from './api/tools'; +export * from './api/subtasks'; +export * from './api/share'; +export * from './api/browser-sessions'; +export * from './api/folder-files'; +export * from './api/reflection'; +export * from './api/workers'; +export * from './api/pets'; +export * from './api/gateway'; +export * from './api/skills'; +export * from './api/notifications'; +export * from './api/usage'; +export * from './api/delegate'; diff --git a/ui/src/api/browser-sessions.ts b/ui/src/api/browser-sessions.ts new file mode 100644 index 0000000..62f97b6 --- /dev/null +++ b/ui/src/api/browser-sessions.ts @@ -0,0 +1,88 @@ +// api.ts から分割(挙動不変): ブラウザセッションプロファイル。 +import { BASE, buildQuery } from './client'; + +// --- Browser Session Profiles --- +export interface BrowserSessionProfile { + id: number; + label: string; + startUrl: string; + matchPatterns: string[]; + storageOrigins: string[]; + loggedInSelector: string | null; + loginUrlPatterns: string[]; + status: 'pending' | 'active' | 'expired' | 'revoked' | 'error'; + stateVersion: number; + lastSavedAt: string | null; + lastUsedAt: string | null; + lastValidatedAt: string | null; + lastError: string | null; + createdAt: string; + updatedAt: string; + /** Owning space (null = legacy/personal). Surfaced for space-scoped listings. */ + spaceId?: string | null; + /** The user who created the profile (its DEK owner). */ + ownerId?: string; + /** False when the requesting viewer is NOT the owner: visible but unusable + * (the per-user DEK can only be decrypted by its owner). */ + decryptableByViewer?: boolean; +} + +const SESS_BASE = `${BASE}/browser-sessions`; + +export async function listBrowserSessionProfiles(spaceId?: string): Promise { + const r = await fetch(`${SESS_BASE}/profiles${buildQuery({ spaceId })}`, { credentials: 'same-origin' }); + if (!r.ok) throw new Error(`listBrowserSessionProfiles: ${r.status}`); + return (await r.json() as { profiles: BrowserSessionProfile[] }).profiles; +} + +export async function createBrowserSessionProfile( + input: Partial & { label: string; startUrl: string }, + spaceId?: string, +): Promise { + const r = await fetch(`${SESS_BASE}/profiles${buildQuery({ spaceId })}`, { + method: 'POST', credentials: 'same-origin', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(spaceId ? { ...input, spaceId } : input), + }); + if (!r.ok) throw new Error(`createBrowserSessionProfile: ${r.status}`); + return (await r.json() as { profile: BrowserSessionProfile }).profile; +} + +export async function startBrowserSessionLogin(id: number): Promise<{ sessionId: string; novncPath: string }> { + const r = await fetch(`${SESS_BASE}/profiles/${id}/login`, { method: 'POST', credentials: 'same-origin' }); + if (!r.ok) throw new Error((await r.text().catch(() => '')) || `startLogin: ${r.status}`); + return r.json(); +} + +export async function saveBrowserSession(id: number, sessionId: string): Promise { + const r = await fetch(`${SESS_BASE}/profiles/${id}/save`, { + method: 'POST', credentials: 'same-origin', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ sessionId }), + }); + if (!r.ok) throw new Error(`saveBrowserSession: ${r.status}`); + return (await r.json() as { profile: BrowserSessionProfile }).profile; +} + +export async function cancelBrowserSession(id: number, sessionId: string): Promise { + await fetch(`${SESS_BASE}/profiles/${id}/cancel`, { + method: 'POST', credentials: 'same-origin', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ sessionId }), + }); +} + +export async function testBrowserSessionProfile(id: number): Promise<{ + verdict: { expired: boolean; reason?: string }; + finalUrl: string; + statusCode: number; +}> { + const r = await fetch(`${SESS_BASE}/profiles/${id}/test`, { method: 'POST', credentials: 'same-origin' }); + if (!r.ok) throw new Error(`testBrowserSession: ${r.status}`); + return r.json(); +} + +export async function deleteBrowserSessionProfile(id: number, spaceId?: string): Promise { + const r = await fetch(`${SESS_BASE}/profiles/${id}${buildQuery({ spaceId })}`, { method: 'DELETE', credentials: 'same-origin' }); + if (!r.ok) throw new Error(`deleteBrowserSessionProfile: ${r.status}`); +} diff --git a/ui/src/api/calendar.ts b/ui/src/api/calendar.ts new file mode 100644 index 0000000..fe0135d --- /dev/null +++ b/ui/src/api/calendar.ts @@ -0,0 +1,145 @@ +// api.ts から分割(挙動不変): スペースカレンダー(予定 + 日次集計 + クロススペース)。 +import { BASE } from './client'; + +// --- Space calendar (予定 + 日次集計) --- +// spec: docs/superpowers/specs/2026-06-19-space-calendar-design.md + +export interface CalendarEvent { + id: number; + spaceId: string; + ownerId: string | null; + date: string; // 開始日 YYYY-MM-DD(ローカル日付) + endDate: string | null; // 終了日 YYYY-MM-DD。null = 単日 + time: string | null; // 開始 HH:MM。null = 終日 + endTime: string | null; // 終了 HH:MM。null = 終了時刻なし(time が null なら常に null) + title: string; + description: string | null; + createdBy: 'user' | 'agent'; + sourceTaskId: number | null; + createdAt: string; + updatedAt?: string; +} + +export interface CalendarDayCounts { + taskCount: number; + eventCount: number; +} + +export interface CalendarMonth { + days: Record; + events: CalendarEvent[]; +} + +export interface CalendarDayTask { + id: number; + title: string; + state: string; + status: string | null; + createdAt: string; +} + +export interface CalendarDayFile { + name: string; + path: string; + size: number; + mtime: string; +} + +export interface CalendarDay { + tasks: CalendarDayTask[]; + files: CalendarDayFile[]; + events: CalendarEvent[]; +} + +// ── V2: cross-space calendar ────────────────────────────────────────────── +export interface CrossCalendarSpace { + id: string; + name: string; + color: string; // brand_color、無ければ spaceColor(id) で導出済み +} + +export interface CrossCalendarMonth { + // days[date][spaceId] = そのスペースのその日の集計 + days: Record>; + events: CalendarEvent[]; // 各イベントは spaceId を持つ + spaces: CrossCalendarSpace[]; +} + +export async function fetchCrossSpaceCalendarMonth( + month: string, + tzOffset: number, +): Promise { + const params = new URLSearchParams({ month, tz_offset: String(tzOffset) }); + const res = await fetch(`${BASE}/calendar/cross?${params.toString()}`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch calendar'); + return data as CrossCalendarMonth; +} + +export async function fetchSpaceCalendarMonth( + spaceId: string, + month: string, + tzOffset: number, +): Promise { + const params = new URLSearchParams({ month, tz_offset: String(tzOffset) }); + const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar?${params.toString()}`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch calendar'); + return data as CalendarMonth; +} + +export async function fetchSpaceCalendarDay( + spaceId: string, + date: string, + tzOffset: number, +): Promise { + const params = new URLSearchParams({ date, tz_offset: String(tzOffset) }); + const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar/day?${params.toString()}`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch calendar day'); + return data as CalendarDay; +} + +export async function createCalendarEvent( + spaceId: string, + input: { date: string; endDate?: string | null; time?: string | null; endTime?: string | null; title: string; description?: string | null }, +): Promise { + const { endDate, endTime, ...rest } = input; + const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar/events`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ...rest, end_date: endDate ?? null, end_time: endTime ?? null }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to create event'); + return data as CalendarEvent; +} + +export async function updateCalendarEvent( + spaceId: string, + eventId: number, + patch: { date?: string; endDate?: string | null; time?: string | null; endTime?: string | null; title?: string; description?: string | null }, +): Promise { + const { endDate, endTime, ...rest } = patch; + const body: Record = { ...rest }; + if (endDate !== undefined) body.end_date = endDate; + if (endTime !== undefined) body.end_time = endTime; + const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar/events/${eventId}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to update event'); + return data as CalendarEvent; +} + +export async function deleteCalendarEvent(spaceId: string, eventId: number): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/calendar/events/${eventId}`, { + method: 'DELETE', + }); + if (!res.ok && res.status !== 204) { + const err = await res.json().catch(() => ({ error: res.statusText })); + throw new Error(err.error || res.statusText); + } +} diff --git a/ui/src/api/client.ts b/ui/src/api/client.ts new file mode 100644 index 0000000..6908e0a --- /dev/null +++ b/ui/src/api/client.ts @@ -0,0 +1,27 @@ +// api.ts から分割(挙動不変): 全 API モジュール共通の基盤ヘルパー。 + +export const BASE = '/api'; + +// Blob を受け取りブラウザのダウンロードを発火する(zip 一括ダウンロード用)。 +export function triggerBlobDownload(blob: Blob, filename: string): void { + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + a.remove(); + URL.revokeObjectURL(url); +} + +/** + * Build a `?key=value&...` query string from defined entries (undefined dropped). + * Used by the space-scoped variants below so passing `spaceId` adds `?spaceId=…` + * and omitting it leaves the per-user URL unchanged (backward compatible). + */ +export function buildQuery(params: Record): string { + const parts = Object.entries(params) + .filter(([, v]) => v !== undefined && v !== '') + .map(([k, v]) => `${k}=${encodeURIComponent(v as string)}`); + return parts.length ? `?${parts.join('&')}` : ''; +} diff --git a/ui/src/api/config.ts b/ui/src/api/config.ts new file mode 100644 index 0000000..07ee637 --- /dev/null +++ b/ui/src/api/config.ts @@ -0,0 +1,84 @@ +// api.ts から分割(挙動不変): Config(設定 API + シークレット4状態表現)。 +import { BASE } from './client'; + +/** + * 4-state representation of a secret field on the wire. See design doc + * 2026-05-21-settings-ui-and-config-restructure-design.md (Form Behavior → + * Secret Inputs) for the canonical contract. + * + * Phase 1 (this release) keeps the on-wire representation backwards + * compatible with the existing `apiKey: string` shape so that mask- + * preservation in `ConfigManager.updateConfig` keeps working unchanged: + * + * - `unchanged` → serialized as the masked sentinel `'********'` + * - `literal` → serialized as the raw string value + * - `env_ref` → serialized as `${ENV_NAME}` + * - `cleared` → serialized as `''` (empty string) + * + * Phase 2 will switch to a tagged object on the wire and drop the magic + * `'********'` sentinel; the UI form keeps the 4-state union today so the + * Phase 2 migration is a server-side change only. + */ +export type SecretFieldValue = + | { type: 'unchanged' } + | { type: 'literal'; value: string } + | { type: 'env_ref'; env_name: string } + | { type: 'cleared' }; + +/** Server-side masked sentinel. Kept in sync with `MASKED` in src/config-manager.ts. */ +export const SECRET_MASKED_SENTINEL = '********'; + +/** + * Parse a stored string secret (as received from `GET /api/config`) into + * the 4-state form used by the UI. The server masks literal secrets to + * `'********'`, so any literal-looking string is treated as `unchanged` + * unless it's an `${ENV_REF}` pattern. Empty / missing values map to + * `cleared`. + */ +export function parseSecretValue(raw: string | null | undefined): SecretFieldValue { + if (raw == null || raw === '') return { type: 'cleared' }; + if (raw === SECRET_MASKED_SENTINEL) return { type: 'unchanged' }; + const envMatch = /^\$\{([A-Z0-9_]+)\}$/.exec(raw.trim()); + if (envMatch) return { type: 'env_ref', env_name: envMatch[1] }; + // Anything else came back as plaintext (e.g. fresh UI form not yet + // round-tripped through the server mask) — treat as literal. + return { type: 'literal', value: raw }; +} + +/** + * Serialize a 4-state secret into the string the server currently + * expects. `unchanged` becomes the masked sentinel so server-side + * mask-preservation in `ConfigManager.updateConfig` keeps the existing + * literal in place. + */ +export function serializeSecretValue(v: SecretFieldValue): string { + if (v.type === 'unchanged') return SECRET_MASKED_SENTINEL; + if (v.type === 'literal') return v.value; + if (v.type === 'env_ref') return `\${${v.env_name}}`; + return ''; +} + +// --- Config --- +export async function fetchConfig(): Promise<{ config: any; etag: string; overriddenByEnv: Record }> { + const res = await fetch(`${BASE}/config`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch config'); + return { config: data.config, etag: res.headers.get('etag') ?? '', overriddenByEnv: data.overriddenByEnv ?? {} }; +} + +export async function updateConfig(config: any, etag: string): Promise<{ ok: boolean; conflict?: boolean }> { + const res = await fetch(`${BASE}/config`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json', 'If-Match': etag }, + body: JSON.stringify(config), + }); + const data = await res.json(); + if (res.status === 409) return { ok: false, conflict: true }; + if (!res.ok) throw new Error(data?.error ?? 'Failed to update config'); + return data; +} + +export async function reloadConfig(): Promise { + const res = await fetch(`${BASE}/config/reload`, { method: 'POST' }); + if (!res.ok) throw new Error('Failed to reload config'); +} diff --git a/ui/src/api/delegate.ts b/ui/src/api/delegate.ts new file mode 100644 index 0000000..4eedde2 --- /dev/null +++ b/ui/src/api/delegate.ts @@ -0,0 +1,32 @@ +// api.ts から分割(挙動不変): Delegate Observability(D0)。 +// ※ 分割に伴い dynamic type import のパスのみ ./lib → ../lib に補正(同一モジュールを指す)。 +import { BASE } from './client'; + +// ── Delegate Observability (D0) ────────────────────────────────────────── +// Task 3 API: GET /api/local/tasks/:id/delegate-runs +// GET /api/local/tasks/:id/delegate-runs/:runId/timeline + +/** Lightweight event record returned by the delegate-run timeline endpoint. */ +export interface TraceEventLite { + eventId: string; + ts: string; + seq: number; + kind: string; + movement?: string; + iteration?: number; + payload: unknown; +} + +export async function fetchDelegateRuns(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/delegate-runs`); + if (!res.ok) throw new Error(`fetchDelegateRuns failed: ${res.status}`); + const body = await res.json(); + return { runs: body.runs ?? [], subtasks: body.subtasks ?? [] }; +} + +export async function fetchDelegateRunTimeline(taskId: number, delegateRunId: string, jobId?: string): Promise { + const q = jobId ? `?jobId=${encodeURIComponent(jobId)}` : ''; + const res = await fetch(`${BASE}/local/tasks/${taskId}/delegate-runs/${encodeURIComponent(delegateRunId)}/timeline${q}`); + if (!res.ok) throw new Error(`fetchDelegateRunTimeline failed: ${res.status}`); + return (await res.json()).events ?? []; +} diff --git a/ui/src/api/folder-files.ts b/ui/src/api/folder-files.ts new file mode 100644 index 0000000..95df0c9 --- /dev/null +++ b/ui/src/api/folder-files.ts @@ -0,0 +1,30 @@ +// api.ts から分割(挙動不変): ユーザー/スペースフォルダのファイル(browser-macros / recordings)。 +import { BASE, buildQuery } from './client'; + +// --- User/space folder files (browser-macros / recordings) --- +export interface FolderFileEntry { name: string; size: number; mtime: string } + +/** + * List files in a writable user-folder subdir. Passing `spaceId` scopes the + * listing to that space's folder (`data/spaces/{id}/{subdir}`); omitting it + * targets the per-user folder. Matches the convention used by the piece/agents + * space-scoped fetchers above. + */ +export async function listFolderFiles(subdir: 'browser-macros' | 'recordings', spaceId?: string): Promise { + const r = await fetch(`${BASE}/users/me/folder/list${buildQuery({ subdir, spaceId })}`, { credentials: 'same-origin' }); + if (!r.ok) throw new Error(`listFolderFiles: ${r.status}`); + return (await r.json() as { files: FolderFileEntry[] }).files; +} + +/** Read one folder file's text content (space-scoped when `spaceId` is given). */ +export async function getFolderFile(subdir: 'browser-macros' | 'recordings', path: string, spaceId?: string): Promise { + const r = await fetch(`${BASE}/users/me/folder/file${buildQuery({ subdir, path, spaceId })}`, { credentials: 'same-origin' }); + if (!r.ok) throw new Error(`getFolderFile: ${r.status}`); + return r.text(); +} + +/** Delete one folder file (space-scoped when `spaceId` is given). */ +export async function deleteFolderFile(subdir: 'browser-macros' | 'recordings', path: string, spaceId?: string): Promise { + const r = await fetch(`${BASE}/users/me/folder/file${buildQuery({ subdir, path, spaceId })}`, { method: 'DELETE', credentials: 'same-origin' }); + if (!r.ok) throw new Error(`deleteFolderFile: ${r.status}`); +} diff --git a/ui/src/api/gateway.ts b/ui/src/api/gateway.ts new file mode 100644 index 0000000..d17384f --- /dev/null +++ b/ui/src/api/gateway.ts @@ -0,0 +1,164 @@ +// api.ts から分割(挙動不変): AAO Gateway(virtual key 管理 + サーバ状態)。 + +// ── AAO Gateway: virtual key admin (Phase 2a + 2b) ────────────────────── +// +// Talks to /api/admin/gateway/keys/* — requires admin role. The raw +// bearer key is returned ONCE from POST / and POST /:id/rotate; UI must +// surface it to the user immediately and not expose it again. + +export interface GatewayKey { + id: string; + object: 'gateway.key'; + keyPrefix: string; + team: string; + allowedModels: string[] | null; + source: 'admin' | 'config-import'; + createdAt: string; + createdBy: string | null; + revokedAt: string | null; + revokedBy: string | null; + lastUsedAt: string | null; + tokensBudget: number | null; + rateLimitRpm: number | null; + /** Only present on POST / rotate responses. */ + key?: string; +} + +export interface GatewayKeyUsageResponse { + keyId: string; + currentPeriod: string; + tokensIn: number; + tokensOut: number; + tokensTotal: number; + tokensBudget: number | null; + remaining: number | null; + requestsThisMonth: number; + rateLimitRpm: number | null; + // Phase 3a F9: `rateRecentRequests` removed. The field was always + // null because the admin process doesn't own the gateway's live + // RateLimiter. Phase 3b/3c may re-add it via gateway IPC. + history: Array<{ period: string; tokensIn: number; tokensOut: number; requests: number }>; +} + +export async function listGatewayKeys(params?: { team?: string; activeOnly?: boolean }): Promise { + const q = new URLSearchParams(); + if (params?.team) q.set('team', params.team); + if (params?.activeOnly) q.set('activeOnly', 'true'); + const qs = q.toString(); + const res = await fetch(`/api/admin/gateway/keys${qs ? `?${qs}` : ''}`); + if (!res.ok) throw new Error(`Failed to list gateway keys: ${res.status}`); + return (await res.json()).keys; +} + +export async function createGatewayKey(input: { + team: string; + allowedModels?: string[]; + tokensBudget?: number | null; + rateLimitRpm?: number | null; +}): Promise { + const res = await fetch(`/api/admin/gateway/keys`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(input), + }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to create gateway key (${res.status}): ${text}`); + } + return res.json(); +} + +export async function getGatewayKey(id: string): Promise { + const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}`); + if (!res.ok) throw new Error(`Failed to get gateway key: ${res.status}`); + return res.json(); +} + +export async function patchGatewayKey( + id: string, + patch: { + tokensBudget?: number | null; + rateLimitRpm?: number | null; + allowedModels?: string[] | null; + }, +): Promise { + const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(patch), + }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to update gateway key (${res.status}): ${text}`); + } + return res.json(); +} + +export async function revokeGatewayKey(id: string): Promise { + const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}/revoke`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}), + }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to revoke gateway key (${res.status}): ${text}`); + } +} + +export async function rotateGatewayKey(id: string): Promise { + const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}/rotate`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}), + }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to rotate gateway key (${res.status}): ${text}`); + } + return res.json(); +} + +export async function deleteGatewayKey(id: string): Promise { + const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}`, { method: 'DELETE' }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to delete gateway key (${res.status}): ${text}`); + } +} + +export async function getGatewayKeyUsage(id: string): Promise { + const res = await fetch(`/api/admin/gateway/keys/${encodeURIComponent(id)}/usage`); + if (!res.ok) throw new Error(`Failed to get gateway key usage: ${res.status}`); + return res.json(); +} + +// ============================================================ +// Gateway Server status (Phase 3c) — read-only admin endpoint. +// Drives the Settings → Gateway Server badge and error list. +// ============================================================ +export type GatewayServerState = + | 'unavailable' + | 'disabled' + | 'starting' + | 'running' + | 'stopping' + | 'misconfigured'; + +export interface GatewayServerStatus { + state: GatewayServerState; + /** Desired-enabled flag read from current config. Null when no ConfigManager. */ + enabled: boolean | null; + /** Validation errors that prevented the gateway from starting. */ + errors: string[]; + mounted: boolean; + sharedPort: number; + /** Only present when state==='unavailable'. */ + message?: string; +} + +export async function getGatewayServerStatus(): Promise { + const res = await fetch('/api/admin/gateway/status'); + if (!res.ok) throw new Error(`Failed to get gateway status: ${res.status}`); + return res.json(); +} diff --git a/ui/src/api/notifications.ts b/ui/src/api/notifications.ts new file mode 100644 index 0000000..20bf2ba --- /dev/null +++ b/ui/src/api/notifications.ts @@ -0,0 +1,107 @@ +// api.ts から分割(挙動不変): 通知 V2(Web Push)。 +import { BASE } from './client'; + +// ── Notifications V2 (Web Push) ─────────────────────────────────────── + +export type NotifyEventType = 'running' | 'succeeded' | 'failed' | 'waiting_human'; + +export interface NotificationPrefsDTO { + userId: string; + enabled: boolean; + events: Record; + includeDetails: boolean; + v1Migrated: boolean; + updatedAt: string; +} + +export interface NotificationPrefsInput { + enabled?: boolean; + events?: Partial>; + includeDetails?: boolean; +} + +export interface PushSubscriptionPublic { + id: string; + endpointHost: string; + userAgent: string | null; + createdAt: string; + lastSuccessAt: string | null; + lastFailureAt: string | null; + failureCount: number; +} + +export interface VapidPublicKeyDTO { + publicKey: string; + keyId: string; +} + +async function notificationsJsonOrThrow(res: Response, fallback: string): Promise { + const data = await res.json().catch(() => ({} as Record)); + if (!res.ok) throw new Error((data as { error?: string }).error ?? fallback); + return data as T; +} + +export async function fetchVapidPublicKey(): Promise { + const res = await fetch(`${BASE}/notifications/vapid-public-key`); + return notificationsJsonOrThrow(res, 'failed to fetch VAPID key'); +} + +export async function listPushSubscriptions(): Promise { + const res = await fetch(`${BASE}/notifications/subscriptions`); + const data = await notificationsJsonOrThrow<{ subscriptions: PushSubscriptionPublic[] }>( + res, 'failed to list subscriptions', + ); + return data.subscriptions; +} + +export async function postPushSubscription(input: { + endpoint: string; + p256dh: string; + auth: string; + userAgent?: string; +}): Promise<{ id: string }> { + const res = await fetch(`${BASE}/notifications/subscriptions`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(input), + }); + return notificationsJsonOrThrow(res, 'failed to register subscription'); +} + +export async function deletePushSubscription(id: string): Promise { + const res = await fetch(`${BASE}/notifications/subscriptions/${id}`, { method: 'DELETE' }); + await notificationsJsonOrThrow(res, 'failed to delete subscription'); +} + +export async function fetchNotificationPrefs(): Promise { + const res = await fetch(`${BASE}/notifications/preferences`); + return notificationsJsonOrThrow(res, 'failed to fetch preferences'); +} + +export async function updateNotificationPrefs( + input: NotificationPrefsInput, +): Promise { + const res = await fetch(`${BASE}/notifications/preferences`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(input), + }); + return notificationsJsonOrThrow(res, 'failed to update preferences'); +} + +export async function migrateLocalStoragePrefs( + input: NotificationPrefsInput, +): Promise<{ ok: boolean; prefs: NotificationPrefsDTO } | { alreadyMigrated: true }> { + const res = await fetch(`${BASE}/notifications/preferences/migrate-from-localstorage`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(input), + }); + if (res.status === 409) return { alreadyMigrated: true }; + return notificationsJsonOrThrow(res, 'failed to migrate preferences'); +} + +export async function postTestNotification(): Promise<{ ok: boolean }> { + const res = await fetch(`${BASE}/notifications/test`, { method: 'POST' }); + return notificationsJsonOrThrow(res, 'failed to send test notification'); +} diff --git a/ui/src/api/pets.ts b/ui/src/api/pets.ts new file mode 100644 index 0000000..5375d03 --- /dev/null +++ b/ui/src/api/pets.ts @@ -0,0 +1,92 @@ +// api.ts から分割(挙動不変): User Folder Pets。 + +// --- User Folder Pets --- +export interface PetSettings { + enabled: boolean; + activePetId: string | null; + size: 32 | 48 | 64 | 80; + position: 'bottom-right'; + sound: boolean; + reducedMotion: boolean; + toolSparkEnabled: boolean; + workerPets: Record; +} + +export interface PetSummary { + id: string; + name: string; + description: string | null; + spriteFile: string | null; + previewFile: string | null; + frameWidth: number | null; + frameHeight: number | null; + gridCols: number | null; + gridRows: number | null; + updatedAt: string; +} + +export interface PetDetail extends PetSummary { + manifest: Record; +} + +export interface PetsResponse { + pets: PetSummary[]; + settings: PetSettings; +} + +const PETS_BASE = '/api/users/me/pets'; + +export function petAssetUrl(petId: string, file: string): string { + return `${PETS_BASE}/${encodeURIComponent(petId)}/assets/${encodeURIComponent(file)}`; +} + +export async function fetchPets(): Promise { + const res = await fetch(PETS_BASE, { credentials: 'include' }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch pets'); + return data; +} + +export async function fetchPet(petId: string): Promise { + const res = await fetch(`${PETS_BASE}/${encodeURIComponent(petId)}`, { credentials: 'include' }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch pet'); + return data.pet; +} + +export async function importPet(file: File, options: { petId?: string; overwrite?: boolean } = {}): Promise { + const params = new URLSearchParams(); + params.set('filename', file.name); + if (options.petId) params.set('petId', options.petId); + if (options.overwrite) params.set('overwrite', 'true'); + const res = await fetch(`${PETS_BASE}/import?${params.toString()}`, { + method: 'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/zip' }, + body: await file.arrayBuffer(), + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to import pet'); + return data.pet; +} + +export async function deletePet(petId: string): Promise { + const res = await fetch(`${PETS_BASE}/${encodeURIComponent(petId)}`, { + method: 'DELETE', + credentials: 'include', + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to delete pet'); +} + +export async function updatePetSettings(patch: Partial): Promise { + const res = await fetch(`${PETS_BASE}/settings`, { + method: 'PUT', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(patch), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to update pet settings'); + return data.settings; +} diff --git a/ui/src/api/pieces.ts b/ui/src/api/pieces.ts new file mode 100644 index 0000000..5bb3686 --- /dev/null +++ b/ui/src/api/pieces.ts @@ -0,0 +1,53 @@ +// api.ts から分割(挙動不変): Piece CRUD。 +import { BASE, buildQuery } from './client'; + +// --- Pieces --- +export interface DriftStatus { drifted: boolean; forkedFromCommit: string | null; latestCommit: string | null } +export interface PieceSummary { name: string; description: string; triggers?: { keywords: string[] }; custom?: boolean; source?: 'builtin' | 'user-custom' | 'global-custom'; ownerId?: string; drift?: DriftStatus; requiredMcp?: string[] } +export interface PieceDef { name: string; description: string; max_movements: number; initial_movement: string; triggers?: { keywords: string[] }; movements: any[]; requiredMcp?: string[] } +/** Full response from GET /api/pieces/:name — includes the server-resolved source. */ +export interface PieceFetchResult { piece: PieceDef; source: 'builtin' | 'user-custom' | 'global-custom'; ownerId?: string } + +export async function fetchPieces(spaceId?: string): Promise { + const res = await fetch(`${BASE}/pieces${buildQuery({ spaceId })}`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch pieces'); + return data.pieces; +} + +export async function fetchPiece(name: string, source?: 'builtin' | 'user-custom' | 'global-custom', spaceId?: string): Promise { + const url = `${BASE}/pieces/${name}${buildQuery({ source, spaceId })}`; + const res = await fetch(url); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch piece'); + return { piece: data.piece, source: data.source, ownerId: data.ownerId }; +} + +export async function updatePiece(name: string, piece: PieceDef, source?: 'builtin' | 'user-custom' | 'global-custom', spaceId?: string): Promise { + const url = `${BASE}/pieces/${name}${buildQuery({ source, spaceId })}`; + const res = await fetch(url, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(spaceId ? { ...piece, spaceId } : piece), + }); + if (!res.ok) { const d = await res.json(); throw new Error(d?.error ?? 'Failed to update piece'); } +} + +export interface PieceCreateResult { source: 'builtin' | 'user-custom' | 'global-custom' } + +export async function createPiece(piece: PieceDef, spaceId?: string): Promise { + const res = await fetch(`${BASE}/pieces${buildQuery({ spaceId })}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(spaceId ? { ...piece, spaceId } : piece), + }); + if (!res.ok) { const d = await res.json(); throw new Error(d?.error ?? 'Failed to create piece'); } + const d = await res.json(); + return { source: d.source ?? 'user-custom' }; +} + +export async function deletePiece(name: string, source?: 'builtin' | 'user-custom' | 'global-custom', spaceId?: string): Promise { + const url = `${BASE}/pieces/${name}${buildQuery({ source, spaceId })}`; + const res = await fetch(url, { method: 'DELETE' }); + if (!res.ok) { const d = await res.json(); throw new Error(d?.error ?? 'Failed to delete piece'); } +} diff --git a/ui/src/api/reflection.ts b/ui/src/api/reflection.ts new file mode 100644 index 0000000..e9261f6 --- /dev/null +++ b/ui/src/api/reflection.ts @@ -0,0 +1,22 @@ +// api.ts から分割(挙動不変): Reflection(タスク別最新スナップショット)。 +import { BASE } from './client'; + +// --- Reflection --- +export interface LatestReflectionForTask { + snapshotId: string; + outcome: string; + memoryChanges: number | null; + pieceEdited: boolean; +} + +export async function getLatestReflectionForTask( + taskId: number, +): Promise { + const res = await fetch(`${BASE}/local/reflection/latest-for-task/${taskId}`); + if (res.status === 404) return null; + if (!res.ok) throw new Error(`getLatestReflectionForTask: ${res.status}`); + const data = await res.json(); + // API returns null body when no reflection exists + if (!data || !data.snapshotId) return null; + return data as LatestReflectionForTask; +} diff --git a/ui/src/api/share.ts b/ui/src/api/share.ts new file mode 100644 index 0000000..cced8fb --- /dev/null +++ b/ui/src/api/share.ts @@ -0,0 +1,157 @@ +// api.ts から分割(挙動不変): タスク共有リンク + 公開アプリ共有リンク。 +import { BASE } from './client'; +import type { LocalTask, LocalTaskComment, SubtaskActivity } from './tasks'; +import type { LocalFileEntry } from './task-files'; +import type { SubtaskFiles } from './subtasks'; + +// --- Share --- +export async function shareTask(taskId: number): Promise<{ shareToken: string; shareUrl: string }> { + const res = await fetch(`${BASE}/local/tasks/${taskId}/share`, { method: 'POST' }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to share task'); + return data; +} + +export async function unshareTask(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/share`, { method: 'DELETE' }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to unshare task'); +} + +export async function fetchSharedTask(token: string): Promise { + const res = await fetch(`${BASE}/shared/${token}`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Not found'); + return data.task; +} + +export async function fetchSharedTaskComments(token: string): Promise { + const res = await fetch(`${BASE}/shared/${token}/comments`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch comments'); + return data.comments ?? []; +} + +export async function fetchSharedFiles(token: string, path: string = ''): Promise<{ basePath: string; path: string; entries: LocalFileEntry[] }> { + const params = new URLSearchParams(); + if (path) params.set('path', path); + const res = await fetch(`${BASE}/shared/${token}/files?${params.toString()}`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to list files'); + return data; +} + +export async function fetchSharedFileContent(token: string, path: string): Promise { + const params = new URLSearchParams({ path }); + const res = await fetch(`${BASE}/shared/${token}/files/content?${params.toString()}`); + if (!res.ok) throw new Error('Failed to read file'); + return res.text(); +} + +export function getSharedFileRawUrl(token: string, path: string): string { + const params = new URLSearchParams({ path }); + return `${BASE}/shared/${token}/files/raw?${params.toString()}`; +} + +export async function fetchSharedSubtaskActivities(token: string): Promise { + const res = await fetch(`${BASE}/shared/${token}/subtasks/activities`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask activities'); + return data.subtasks ?? []; +} + +// 個別サブタスク(共有・read-only)。本体の fetchSubtaskActivity / fetchSubtaskFiles の +// 共有版。TaskDataSource(shared) から使う。封じ込めはサーバ側(share-api.ts)。 +export async function fetchSharedSubtaskActivity(token: string, jobId: string): Promise { + const res = await fetch(`${BASE}/shared/${encodeURIComponent(token)}/subtasks/${jobId}/activity`); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask activity'); + return data.activityLog ?? ''; +} + +export async function fetchSharedSubtaskFiles(token: string, jobId: string): Promise { + const res = await fetch(`${BASE}/shared/${encodeURIComponent(token)}/subtasks/${jobId}/files`); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask files'); + return { files: data.files ?? [], categories: data.categories ?? {} }; +} + +export function sharedSubtaskFileRawUrl(token: string, jobId: string, filePath: string): string { + return `${BASE}/shared/${encodeURIComponent(token)}/subtasks/${jobId}/files/${filePath}`; +} + +// --- 公開アプリ共有リンク(read-only・認証なし) --- +// スペース内の 1 ワークスペースアプリ(apps/{app}/)をログイン不要の公開トークンで +// read-only 配信する。公開取得(/api/app-share/:token/*)は認証なし、発行/失効 +// (/api/local/spaces/:id/apps/:app/share)は canManageSpace(owner/admin)が必要。 +// サーバ側で apps/{app}/+output/ にパス封じ込め。詳細は src/bridge/app-share-api.ts。 + +// 公開アプリのメタ。entryPath は entry HTML(apps/{app}/index.html 等)。空アプリ等で +// 見つからなければ null になりうる(呼び出し側で 404 表示)。失効/不正トークンは throw。 +export async function fetchAppShareMeta(token: string): Promise<{ appName: string; entryPath: string | null }> { + const res = await fetch(`${BASE}/app-share/${token}`); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Not found'); + return data.app; +} + +export async function fetchAppShareFileContent(token: string, path: string): Promise { + const params = new URLSearchParams({ path }); + const res = await fetch(`${BASE}/app-share/${token}/files/content?${params.toString()}`); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data?.error ?? 'Failed to read file'); + } + return res.text(); +} + +export async function fetchAppShareFiles( + token: string, + dir: string = '', +): Promise<{ basePath: string; path: string; entries: LocalFileEntry[] }> { + const params = new URLSearchParams(); + if (dir) params.set('dir', dir); + const qs = params.toString(); + const res = await fetch(`${BASE}/app-share/${token}/files/list${qs ? `?${qs}` : ''}`); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to list files'); + return data; +} + +export function getAppShareRawUrl(token: string, path: string): string { + const params = new URLSearchParams({ path }); + return `${BASE}/app-share/${token}/files/raw?${params.toString()}`; +} + +// 発行/失効/取得(canManageSpace)。shareUrl は相対パス(/ui/app/:token)。表示時は +// window.location.origin を前置する。 +export async function createAppShareLink( + spaceId: string, + app: string, +): Promise<{ token: string; shareUrl: string }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/apps/${encodeURIComponent(app)}/share`, { + method: 'POST', + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to create share link'); + return data as { token: string; shareUrl: string }; +} + +export async function getAppShareLink( + spaceId: string, + app: string, +): Promise<{ token: string | null; shareUrl?: string; revokedAt?: string | null }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/apps/${encodeURIComponent(app)}/share`); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch share link'); + return data as { token: string | null; shareUrl?: string; revokedAt?: string | null }; +} + +export async function revokeAppShareLink(spaceId: string, app: string): Promise<{ ok: true }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/apps/${encodeURIComponent(app)}/share`, { + method: 'DELETE', + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to revoke share link'); + return data as { ok: true }; +} diff --git a/ui/src/api/skills.ts b/ui/src/api/skills.ts new file mode 100644 index 0000000..ad6298c --- /dev/null +++ b/ui/src/api/skills.ts @@ -0,0 +1,82 @@ +// api.ts から分割(挙動不変): Skills API。 +import { buildQuery } from './client'; + +// ── Skills API ────────────────────────────────────────────────────────── + +export interface SkillSummary { + name: string; + description: string; + triggers: string[]; + source: 'system' | 'user'; + hasDir: boolean; +} + +export interface SkillDetail extends SkillSummary { + /** Body only (frontmatter stripped) — for read-only preview. */ + content: string; + /** Full SKILL.md incl. frontmatter — what the editor must edit/save. */ + raw: string; + files: string[]; + findings: Array<{ severity: 'medium' | 'high'; pattern: string; match: string; line: number; file?: string }>; + maxSeverity: 'high' | 'medium' | 'none'; +} + +export async function fetchSkills(scope?: string, spaceId?: string): Promise { + const res = await fetch(`/api/skills${buildQuery({ scope, spaceId })}`); + if (!res.ok) throw new Error('Failed to fetch skills'); + const data = await res.json(); + return data.skills; +} + +export async function fetchSkillDetail(name: string, scope?: string, spaceId?: string): Promise { + const res = await fetch(`/api/skills/${encodeURIComponent(name)}${buildQuery({ scope, spaceId })}`); + if (!res.ok) throw new Error('Skill not found'); + return res.json(); +} + +export async function createSkill(name: string, content: string, scope: string, spaceId?: string): Promise<{ name: string; severity: string; findings: any[] }> { + const res = await fetch(`/api/skills${buildQuery({ spaceId })}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(spaceId ? { name, content, scope, spaceId } : { name, content, scope }), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: 'Unknown error' })); + throw new Error(err.error); + } + return res.json(); +} + +export async function updateSkill(name: string, content: string, scope: string, spaceId?: string): Promise { + const res = await fetch(`/api/skills/${encodeURIComponent(name)}${buildQuery({ scope, spaceId })}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(spaceId ? { content, spaceId } : { content }), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: 'Unknown error' })); + throw new Error(err.error); + } + return res.json(); +} + +export async function deleteSkill(name: string, scope: string, spaceId?: string): Promise { + const res = await fetch(`/api/skills/${encodeURIComponent(name)}${buildQuery({ scope, spaceId })}`, { method: 'DELETE' }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: 'Unknown error' })); + throw new Error(err.error); + } +} + +export async function installSkillFromUrl(url: string, scope: string, selectedSkills?: string[], spaceId?: string): Promise { + const res = await fetch(`/api/skills/install-from-url${buildQuery({ spaceId })}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(spaceId ? { url, scope, selectedSkills, spaceId } : { url, scope, selectedSkills }), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: 'Unknown error' })); + throw new Error(err.error); + } + return res.json(); +} diff --git a/ui/src/api/space-files.ts b/ui/src/api/space-files.ts new file mode 100644 index 0000000..667eb9b --- /dev/null +++ b/ui/src/api/space-files.ts @@ -0,0 +1,145 @@ +// api.ts から分割(挙動不変): スペースの永続ワークスペースファイル。 +import { BASE, triggerBlobDownload } from './client'; +import type { LocalFileEntry } from './task-files'; + +// --- Space files (永続ワークスペース {worktreeDir}/space/{id}/files) --- +// タスク版の files API(getLocalFileRawUrl 等)と同形だが、スペース id でキーする。 + +export async function fetchSpaceFiles(spaceId: string, path: string = ''): Promise<{ basePath: string; path: string; entries: LocalFileEntry[] }> { + const params = new URLSearchParams(); + if (path) params.set('path', path); + const qs = params.toString(); + const res = await fetch(`${BASE}/local/spaces/${spaceId}/files${qs ? `?${qs}` : ''}`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to list files'); + return data; +} + +export async function fetchSpaceFileContent(spaceId: string, path: string): Promise { + const params = new URLSearchParams({ path }); + const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/content?${params.toString()}`); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data?.error ?? 'Failed to read file'); + } + return await res.text(); +} + +export function getSpaceFileRawUrl(spaceId: string, path: string): string { + const params = new URLSearchParams({ path }); + return `${BASE}/local/spaces/${spaceId}/files/raw?${params.toString()}`; +} + +export function getSpaceTrustedHtmlUrl(spaceId: string, path: string): string { + const params = new URLSearchParams({ path, trusted: '1' }); + return `${BASE}/local/spaces/${spaceId}/files/raw?${params.toString()}`; +} + +export function getSpaceFileOfficePreviewUrl(spaceId: string, path: string): string { + const params = new URLSearchParams({ path }); + return `${BASE}/local/spaces/${spaceId}/files/office-preview?${params.toString()}`; +} + +export async function uploadSpaceFiles( + spaceId: string, + path: string, + files: { name: string; contentBase64: string }[], +): Promise<{ uploaded: { name: string; path: string }[] }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/upload`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ path, files }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to upload files'); + return data as { uploaded: { name: string; path: string }[] }; +} + +// スペースのワークスペースファイルを削除する。複数選択は paths(相対パス配列)で渡す。 +// 各パスはサーバ側で spaceFilesDir に封じ込め(traversal は 400)、ファイルのみ対象。 +// 存在しないパスは冪等にスキップされる。 +export async function deleteSpaceFiles( + spaceId: string, + paths: string[], +): Promise<{ deleted: string[]; skipped: string[] }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/delete`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ paths }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to delete files'); + return data as { deleted: string[]; skipped: string[] }; +} + +// スペースのワークスペースに空フォルダを作る(owner/editor のみ)。path は親からの +// 相対パス。サーバ側で spaceFilesDir に封じ込め(traversal は 400)。 +export async function createSpaceFolder( + spaceId: string, + path: string, +): Promise<{ created: string }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/mkdir`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ path }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to create folder'); + return data as { created: string }; +} + +// スペースのファイル/フォルダをリネーム・移動する(owner/editor のみ)。from→to は +// いずれも相対パス完全形。構造ディレクトリ(input/output/logs/apps/readonly)と隠しは +// 不可。衝突時はサーバが `{stem} (N){ext}` に自動リネームし、確定先 path を返す。 +export async function moveSpaceFile( + spaceId: string, + from: string, + to: string, +): Promise<{ from: string; to: string }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/move`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ from, to }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to move'); + return data as { from: string; to: string }; +} + +// スペースの複数ファイルを zip でまとめてダウンロード(read)。サーバが paths を +// spaceFilesDir に封じ込め、ファイルのみ zip 化。1 件でも zip にする。 +export async function downloadSpaceFilesZip( + spaceId: string, + paths: string[], + filename = 'files.zip', +): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/download-zip`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ paths }), + }); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data?.error ?? 'Failed to download files'); + } + triggerBlobDownload(await res.blob(), filename); +} + +// スペースのワークスペースファイルを path 指定で 1 件書き込む(上書き許可)。 +// content(UTF-8 テキスト)または contentBase64(任意バイナリ)のどちらかを渡す。 +// サーバ側で canEditInSpace + ensurePathWithin によりゲートされる。ワークスペース・ +// アプリの postMessage ブリッジ writeFile の代理に使う。 +export async function writeSpaceFile( + spaceId: string, + path: string, + body: { content: string } | { contentBase64: string }, +): Promise<{ path: string; bytes: number }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/files/write`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ path, ...body }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to write file'); + return data as { path: string; bytes: number }; +} diff --git a/ui/src/api/spaces.ts b/ui/src/api/spaces.ts new file mode 100644 index 0000000..948b7d0 --- /dev/null +++ b/ui/src/api/spaces.ts @@ -0,0 +1,290 @@ +// api.ts から分割(挙動不変): スペース(CRUD・メンバー・招待・ツールポリシー・Python パッケージ)。 +import { BASE } from './client'; + +// --- Spaces (個人/案件) --------------------------------------------------- + +export interface Space { + id: string; + kind: 'personal' | 'case'; + title: string; + description: string; + ownerId: string | null; + visibility: 'private' | 'org' | 'public'; + visibilityScopeOrgId: string | null; + status: 'open' | 'archived'; + brandColor: string | null; + workspaceDir: string | null; + createdAt: string; + updatedAt: string; + /** 閲覧者自身のメンバーロール。一覧(GET /spaces)・詳細(GET /spaces/:id)の両方で付与。 + * 非メンバー(根オーナー含む。owner_id はメンバー行ではない)は null。 */ + myRole?: SpaceMemberRole | null; +} + +export async function fetchSpaces(): Promise { + const res = await fetch(`${BASE}/local/spaces`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch spaces'); + return data as Space[]; +} + +export type SpaceMemberRole = 'owner' | 'editor' | 'viewer'; + +export interface SpaceMember { + userId: string; + name: string | null; + email: string | null; + avatarUrl: string | null; + role: SpaceMemberRole; + isOwner: boolean; +} + +export async function fetchSpaceMembers(spaceId: string): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/members`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch members'); + return data as SpaceMember[]; +} + +export async function addSpaceMember( + spaceId: string, + input: { userId: string; role: SpaceMemberRole }, +): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/members`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(input), + }); + if (!res.ok) { + const d = await res.json().catch(() => ({})); + throw new Error(d?.error ?? 'Failed to add member'); + } +} + +export async function updateSpaceMemberRole( + spaceId: string, + userId: string, + role: SpaceMemberRole, +): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/members/${encodeURIComponent(userId)}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ role }), + }); + if (!res.ok) { + const d = await res.json().catch(() => ({})); + throw new Error(d?.error ?? 'Failed to update member role'); + } +} + +export async function removeSpaceMember(spaceId: string, userId: string): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/members/${encodeURIComponent(userId)}`, { + method: 'DELETE', + }); + if (!res.ok) { + const d = await res.json().catch(() => ({})); + throw new Error(d?.error ?? 'Failed to remove member'); + } +} + +// ─── ワークスペース招待リンク(再利用トークン)────────────────────── +export type SpaceInviteRole = 'editor' | 'viewer'; + +export interface SpaceInviteInfo { + token: string; + /** 相対パス。origin を前置して共有する。 */ + url: string; + role: SpaceInviteRole; + createdAt: string; + expiresAt: string | null; + revokedAt: string | null; + valid: boolean; +} + +/** 現行の招待リンク(canManageSpace)。無ければ null。 */ +export async function fetchSpaceInvite(spaceId: string): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/invite`); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch invite'); + return (data?.invite ?? null) as SpaceInviteInfo | null; +} + +/** 招待リンクを (再)生成する(canManageSpace)。 */ +export async function createSpaceInvite( + spaceId: string, + input: { role: SpaceInviteRole; expiresInDays?: number | null }, +): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/invite`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(input), + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to create invite'); + return data.invite as SpaceInviteInfo; +} + +/** 招待リンクを無効化する(canManageSpace)。 */ +export async function revokeSpaceInvite(spaceId: string): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/invite`, { method: 'DELETE' }); + if (!res.ok && res.status !== 204) { + const d = await res.json().catch(() => ({})); + throw new Error(d?.error ?? 'Failed to revoke invite'); + } +} + +export interface InvitePreview { + spaceId: string; + spaceTitle: string; + role: SpaceInviteRole; +} + +/** + * 招待トークンのプレビュー。`status` で分岐を呼び出し側に渡す: + * 'ok' → preview / 'unauthorized'(要ログイン)/ 'invalid'(無効・期限切れ・不明)。 + */ +export async function fetchInvitePreview( + token: string, +): Promise<{ status: 'ok'; preview: InvitePreview } | { status: 'unauthorized' | 'invalid' }> { + const res = await fetch(`${BASE}/local/spaces/invite/${encodeURIComponent(token)}`); + if (res.status === 401) return { status: 'unauthorized' }; + if (!res.ok) return { status: 'invalid' }; + const data = await res.json(); + return { status: 'ok', preview: data as InvitePreview }; +} + +/** 招待を受諾して参加する。参加先 spaceId を返す。 */ +export async function acceptSpaceInvite(token: string): Promise<{ spaceId: string; alreadyMember: boolean }> { + const res = await fetch(`${BASE}/local/spaces/invite/${encodeURIComponent(token)}/accept`, { + method: 'POST', + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to accept invite'); + return data as { spaceId: string; alreadyMember: boolean }; +} + + +// ─── ワークスペース ツールポリシー ──────────────────────────────────────── + +export interface ToolCategory { + name: string; + sensitive: boolean; + enabled: boolean; +} + +export interface SpaceToolPolicy { + disabledSafe: string[]; + enabledSensitive: string[]; +} + +export interface SpaceToolPolicyResponse { + policy: SpaceToolPolicy; + categories: ToolCategory[]; + sensitiveTools: { name: string; enabled: boolean }[]; +} + +export async function fetchSpaceToolPolicy(spaceId: string): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/tool-policy`); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch tool policy'); + return data as SpaceToolPolicyResponse; +} + +export async function updateSpaceToolPolicy( + spaceId: string, + patch: { disabledSafe?: string[]; enabledSensitive?: string[] }, +): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/tool-policy`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(patch), + }); + if (!res.ok) { + const d = await res.json().catch(() => ({})); + throw new Error(d?.error ?? 'Failed to update tool policy'); + } +} + +// ─── Per-space Python packages ──────────────────────────────────────────── +export interface SpacePythonPackage { + name: string; + spec: string; + addedAt: string; +} +export interface SpacePythonPackagesResponse { + enabled: boolean; + maxPackagesPerSpace: number; + preflight: { ok: boolean; reason?: string }; + packages: SpacePythonPackage[]; +} + +export async function fetchSpacePythonPackages(spaceId: string): Promise { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/python-packages`); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch python packages'); + return data as SpacePythonPackagesResponse; +} + +export async function addSpacePythonPackage( + spaceId: string, + spec: string, +): Promise<{ packages: SpacePythonPackage[] }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/python-packages`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ spec }), + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to add package'); + return data as { packages: SpacePythonPackage[] }; +} + +export async function removeSpacePythonPackage( + spaceId: string, + name: string, +): Promise<{ packages: SpacePythonPackage[] }> { + const res = await fetch(`${BASE}/local/spaces/${spaceId}/python-packages/${encodeURIComponent(name)}`, { + method: 'DELETE', + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error ?? 'Failed to remove package'); + return data as { packages: SpacePythonPackage[] }; +} + +export async function createSpace(input: { + title: string; + description?: string; + brandColor?: string | null; + visibility?: Space['visibility']; +}): Promise { + const res = await fetch(`${BASE}/local/spaces`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(input), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to create space'); + return data as Space; +} + +export async function updateSpace( + id: string, + patch: Partial>, +): Promise { + const res = await fetch(`${BASE}/local/spaces/${id}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(patch), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to update space'); + return data as Space; +} + +export async function archiveSpace(id: string): Promise { + const res = await fetch(`${BASE}/local/spaces/${id}/archive`, { method: 'POST' }); + if (!res.ok) { + const d = await res.json().catch(() => ({})); + throw new Error(d?.error ?? 'Failed to archive space'); + } +} diff --git a/ui/src/api/subtasks.ts b/ui/src/api/subtasks.ts new file mode 100644 index 0000000..08f294d --- /dev/null +++ b/ui/src/api/subtasks.ts @@ -0,0 +1,40 @@ +// api.ts から分割(挙動不変): サブタスク(ファイル・進捗)。 +import { BASE } from './client'; +import type { SubtaskActivity } from './tasks'; + +// --- Subtasks --- +export interface SubtaskFiles { + files: string[]; + categories: Record; +} + +export async function fetchSubtaskFiles(taskId: number, jobId: string): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/subtasks/${jobId}/files`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask files'); + return { files: data.files ?? [], categories: data.categories ?? {} }; +} + +export function subtaskFileRawUrl(taskId: number, jobId: string, filePath: string): string { + return `${BASE}/local/tasks/${taskId}/subtasks/${jobId}/files/${filePath}`; +} + +export async function fetchSubtaskFileContent(taskId: number, jobId: string, filePath: string): Promise { + const res = await fetch(subtaskFileRawUrl(taskId, jobId, filePath)); + if (!res.ok) throw new Error('Failed to fetch subtask file content'); + return res.text(); +} + +export async function fetchSubtaskActivities(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/subtasks/activities`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask activities'); + return data.subtasks ?? []; +} + +export async function fetchSubtaskActivity(taskId: number, jobId: string): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/subtasks/${jobId}/activity`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch subtask activity'); + return data.activityLog ?? ''; +} diff --git a/ui/src/api/task-files.ts b/ui/src/api/task-files.ts new file mode 100644 index 0000000..0e83045 --- /dev/null +++ b/ui/src/api/task-files.ts @@ -0,0 +1,191 @@ +// api.ts から分割(挙動不変): タスクワークスペースのファイル(一覧・内容・Office プレビュー・アップロード/削除/zip)。 +import { BASE, triggerBlobDownload } from './client'; + +// Mirrors the API's safe projection (local-files-api.ts): task IDs + source +// kind + piece/movement + timestamps only. Job UUIDs, checksum, and the +// free-text note are intentionally NOT sent to the client (adversarial-review D4). +export interface FileProvenance { + relPath: string; + sourceKind: string; + createdByTaskId: number | null; + createdByPiece: string | null; + createdByMovement: string | null; + firstSeenAt: string | null; + lastModifiedByTaskId: number | null; + lastModifiedAt: string | null; +} + +/** Fetch the provenance record for one workspace file (null when untracked). */ +export async function fetchFileProvenance( + taskId: number, + section: string, + path: string, +): Promise { + const qs = new URLSearchParams({ section, path }); + const res = await fetch(`${BASE}/local/tasks/${taskId}/files/provenance?${qs.toString()}`); + if (!res.ok) return null; + const data = await res.json(); + return (data.provenance ?? null) as FileProvenance | null; +} + +export interface LocalFileEntry { + name: string; + path: string; + kind: 'directory' | 'file'; + size: number; + modifiedAt: string; +} + +export async function fetchLocalFiles(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string = ''): Promise<{ basePath: string; path: string; entries: LocalFileEntry[] }> { + const params = new URLSearchParams({ section }); + if (path) params.set('path', path); + const res = await fetch(`${BASE}/local/tasks/${taskId}/files?${params.toString()}`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to list files'); + return data; +} + +export async function fetchLocalFileContent(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string): Promise { + const params = new URLSearchParams({ section, path }); + const res = await fetch(`${BASE}/local/tasks/${taskId}/files/content?${params.toString()}`); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data?.error ?? 'Failed to read file'); + } + return await res.text(); +} + +export function getLocalFileRawUrl(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string): string { + const params = new URLSearchParams({ section, path }); + return `${BASE}/local/tasks/${taskId}/files/raw?${params.toString()}`; +} + +export function getTrustedLocalHtmlUrl(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string): string { + const params = new URLSearchParams({ section, path, trusted: '1' }); + return `${BASE}/local/tasks/${taskId}/files/raw?${params.toString()}`; +} + +// ── Office プレビュー (Excel / PowerPoint) ─────────────────────────────── +// サーバが Excel→シートのセル配列、PPTX→スライド画像・DOCX→ページ画像(PNG data URL)に変換して返す。 + +export interface OfficeSpreadsheetSheet { + name: string; + rows: string[][]; + rowCount: number; + colCount: number; + truncated: boolean; +} +export interface OfficeSpreadsheetPreview { + kind: 'spreadsheet'; + sheets: OfficeSpreadsheetSheet[]; + truncated: boolean; +} +export interface OfficePresentationPreview { + kind: 'presentation'; + slides: { index: number; dataUrl: string }[]; + slideCount: number; + truncated: boolean; +} +export interface OfficeDocumentPreview { + kind: 'document'; + pages: { index: number; dataUrl: string }[]; + pageCount: number; + truncated: boolean; +} +export type OfficePreview = OfficeSpreadsheetPreview | OfficePresentationPreview | OfficeDocumentPreview; + +/** office-preview エンドポイントの失敗を、変換エンジン未導入(503)とそれ以外で区別できる型。 */ +export class OfficePreviewError extends Error { + /** サーバが返した error コード ('converter_unavailable' 等)。 */ + code?: string; + constructor(message: string, code?: string) { + super(message); + this.name = 'OfficePreviewError'; + this.code = code; + } +} + +export async function fetchOfficePreview(url: string): Promise { + const res = await fetch(url); + if (!res.ok) { + const data = await res.json().catch(() => ({} as { error?: string; message?: string })); + throw new OfficePreviewError(data?.message ?? data?.error ?? 'Failed to load preview', data?.error); + } + return (await res.json()) as OfficePreview; +} + +export function getLocalFileOfficePreviewUrl(taskId: number, section: 'workspace' | 'input' | 'output' | 'logs', path: string): string { + const params = new URLSearchParams({ section, path }); + return `${BASE}/local/tasks/${taskId}/files/office-preview?${params.toString()}`; +} + +// アップロード・削除が許される区分。サーバ側 WRITABLE_SECTIONS と一致させること。 +export type WritableTaskSection = 'input' | 'output'; + +// タスクワークスペースの input/output へファイルをアップロード(複数可)。サーバが +// O_EXCL で衝突回避リネーム + ensurePathWithin で section root に封じ込め。実行中は 409。 +export async function uploadLocalFiles( + taskId: number, + section: WritableTaskSection, + path: string, + files: { name: string; contentBase64: string }[], +): Promise<{ uploaded: { name: string; path: string }[] }> { + const res = await fetch(`${BASE}/local/tasks/${taskId}/files/upload`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ section, path, files }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to upload files'); + return data as { uploaded: { name: string; path: string }[] }; +} + +// タスクワークスペースの input/output ファイルを削除(複数選択は paths 配列)。サーバ側で +// section root に封じ込め、ファイルのみ対象、存在しないものは冪等スキップ。 +export async function deleteLocalFiles( + taskId: number, + section: WritableTaskSection, + paths: string[], +): Promise<{ deleted: string[]; skipped: string[] }> { + const res = await fetch(`${BASE}/local/tasks/${taskId}/files/delete`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ section, paths }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to delete files'); + return data as { deleted: string[]; skipped: string[] }; +} + + +// タスクワークスペースの複数ファイルを zip でまとめてダウンロード(全 section 可、read)。 +// サーバが paths を section root に封じ込め、ファイルのみ zip 化。1 件でも zip にする。 +export async function downloadLocalFilesZip( + taskId: number, + section: 'workspace' | 'input' | 'output' | 'logs', + paths: string[], + filename = 'files.zip', +): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/files/download-zip`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ section, paths }), + }); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data?.error ?? 'Failed to download files'); + } + triggerBlobDownload(await res.blob(), filename); +} + +export async function updateLocalFileContent(taskId: number, section: string, path: string, content: string): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/files/content`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ section, path, content }), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: res.statusText })); + throw new Error(err.error || res.statusText); + } +} diff --git a/ui/src/api/tasks.ts b/ui/src/api/tasks.ts new file mode 100644 index 0000000..bce80e5 --- /dev/null +++ b/ui/src/api/tasks.ts @@ -0,0 +1,369 @@ +// api.ts から分割(挙動不変): ローカルタスク(CRUD・コメント・ツール要求・プロンプトコーチ・フィードバック)。 +import { BASE } from './client'; + +export type PieceName = string; // Dynamically loaded from API +export type ProfileName = 'auto' | 'fast' | 'quality'; +export type OutputFormat = 'text' | 'markdown' | 'json'; +export type AskPolicy = 'low' | 'high'; +export type Priority = 'low' | 'medium' | 'high'; +export type Visibility = 'private' | 'org' | 'public'; + +export interface SubtaskInfo { + id: string; + issueNumber: number; + status: string; + instruction: string; + worktreePath: string | null; + createdAt: string; + updatedAt: string; + children?: SubtaskInfo[]; + childCount?: number; + childCompleted?: number; +} + +export interface SubtaskActivity { + jobId: string; + issueNumber: number; + status: string; + currentMovement: string | null; + currentActivity: string | null; + activityLog: string; +} + +export type TitleSource = 'auto' | 'agent' | 'user'; + +export interface LocalTask { + id: number; + title: string; + /** Provenance of the title: 'auto' (creation fallback), 'agent' (derived from goal), 'user' (manual edit). */ + titleSource?: TitleSource; + body: string; + pieceName: string; + profile: string; + outputFormat: string; + askPolicy: string; + priority: string; + state: string; + workspacePath: string | null; + ownerId?: string | null; + ownerName?: string | null; + visibility?: Visibility; + visibilityScopeOrgId?: string | null; + visibilityScopeOrgName?: string | null; + /** Spaces foundation: which space this task belongs to (null = legacy/個人). */ + spaceId?: string | null; + createdAt: string; + updatedAt: string; + latestJob?: { + id: string; + status: string; + waitReason?: string | null; + currentMovement?: string | null; + currentActivity?: string | null; + workerId?: string | null; + /** + * Physical backend id (e.g. LiteLLM deployment) for jobs run through + * a proxy worker. NULL until the proxy has resolved a backend or for + * direct workers entirely. + * Phase A: docs/superpowers/specs/2026-05-18-multi-team-gpu-pool-and-node-status-design.md. + */ + lastBackendId?: string | null; + contextPromptTokens?: number | null; + contextLimitTokens?: number | null; + contextUpdatedAt?: string | null; + } | null; + subtasks?: SubtaskInfo[]; + subtaskCount?: number; + subtaskCompleted?: number; + feedbackRating?: 'good' | 'bad' | null; + feedbackTags?: string[] | null; + feedbackComment?: string | null; + feedbackAt?: string | null; + shareToken?: string | null; + sharedAt?: string | null; + missionBrief?: MissionBrief | null; +} + +export interface MissionBrief { + goal: string; + done: string; + open: string; + clarifications: string; + user_constraints?: string; + decisions?: string; + current_focus?: string; +} + +export async function updateMissionBrief( + taskId: number, + patch: Partial, +): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/mission`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(patch), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: res.statusText })); + throw new Error(err.error || res.statusText); + } + const data = await res.json(); + return data.missionBrief ?? null; +} + +// ─── Tool-request mechanism ──────────────────────────────────────────────── +export interface ToolRequest { + id: string; + taskId: string | null; + jobId: string | null; + spaceId: string | null; + pieceName: string; + movementName: string; + toolName: string; + reason: string | null; + category: 'requested' | 'blocked' | 'unknown'; + status: 'pending' | 'approved' | 'denied' | 'auto_denied'; + grantScope: 'task' | 'piece' | null; + decidedBy: string | null; + createdAt: string; + decidedAt: string | null; +} + +export async function fetchToolRequests(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/tool-requests`); + if (!res.ok) throw new Error('failed to fetch tool requests'); + const data = await res.json(); + return (data.toolRequests ?? []) as ToolRequest[]; +} + +export async function decideToolRequest( + taskId: number, + reqId: string, + decision: 'approve' | 'deny', +): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/tool-requests/${reqId}/decide`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ decision }), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: res.statusText })); + throw new Error(err.error || res.statusText); + } +} + +// Package-request mechanism (PR3): agent-declared Python package requests. +export interface PackageRequest { + id: string; + taskId: string | null; + jobId: string | null; + spaceId: string | null; + pieceName: string | null; + movementName: string | null; + spec: string; + normalizedName: string; + reason: string | null; + status: 'pending' | 'approved' | 'denied' | 'auto_denied'; + decidedBy: string | null; + createdAt: string; + decidedAt: string | null; +} + +export async function fetchPackageRequests(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/package-requests`); + if (!res.ok) throw new Error('failed to fetch package requests'); + const data = await res.json(); + return (data.packageRequests ?? []) as PackageRequest[]; +} + +export async function decidePackageRequest( + taskId: number, + reqId: string, + decision: 'approve' | 'deny', +): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/package-requests/${reqId}/decide`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ decision }), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: res.statusText })); + throw new Error(err.error || res.statusText); + } +} + +export type CommentKind = 'request' | 'comment' | 'result' | 'ask' | 'progress' | 'handoff' | 'interjection'; + +export interface LocalTaskComment { + id: number; + taskId: number; + author: string; + kind: CommentKind; + body: string; + /** Filenames attached to this comment, saved under the task's input/ dir. */ + attachments?: string[]; + createdAt: string; + injectedAt: string | null; +} + +export interface CreateLocalTaskInput { + title?: string; + body: string; + piece: PieceName; + profile: ProfileName; + outputFormat: OutputFormat; + askPolicy: AskPolicy; + priority: Priority; + attachments?: Array<{ name: string; contentBase64: string }>; + visibility?: Visibility; + visibilityScopeOrgId?: string | null; + browserSessionProfileId?: number | null; + /** + * Spaces foundation: 'persistent'(既定)はスペースのワークスペースに蓄積、 + * 'ephemeral' は使い捨て。未指定時はバックエンドが 'persistent' に解決する。 + */ + workspaceMode?: 'persistent' | 'ephemeral'; + /** 紐付けるスペース。未指定なら owner の個人スペースに解決される。 */ + spaceId?: string; + options?: { + mcpDisabled?: boolean; + skillsDisabled?: boolean; + }; +} + +export async function fetchLocalTasks(): Promise { + const res = await fetch(`${BASE}/local/tasks`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch local tasks'); + return data.tasks ?? []; +} + +export async function createLocalTask(input: CreateLocalTaskInput): Promise<{ task: LocalTask; jobId: string }> { + const res = await fetch(`${BASE}/local/tasks`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(input), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to create local task'); + return data; +} + +export interface PromptCoachAxis { + name: string; + score: number; + comment: string; +} +export interface PromptCoachResult { + overall: number; + axes: PromptCoachAxis[]; + rewrite: string; + predicted_piece: { name: string; reason: string } | null; + maestro_tips: Array<{ feature: string; suggestion: string }>; + personalized: string[]; +} + +/** + * On-demand prompt coach: evaluates a draft task prompt before it is submitted. + * Stateless — nothing is persisted. Returns 503 when the coach is unconfigured. + */ +export async function evaluatePrompt(input: { instruction: string; piece?: string }): Promise { + const res = await fetch(`${BASE}/local/tasks/evaluate-prompt`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(input), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to evaluate prompt'); + return data as PromptCoachResult; +} + +export async function fetchLocalTask(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch local task'); + return data.task; +} + +export async function fetchLocalTaskComments(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/comments`); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch local task comments'); + return data.comments ?? []; +} + +export async function postLocalTaskComment(taskId: number, body: string, author: string = 'user', attachments?: Array<{ name: string; contentBase64: string }>): Promise { + const payload: Record = { body, author }; + if (attachments && attachments.length > 0) payload.attachments = attachments; + const res = await fetch(`${BASE}/local/tasks/${taskId}/comments`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to post local task comment'); +} + +export async function updateLocalTask( + taskId: number, + updates: { title?: string; visibility?: Visibility; visibilityScopeOrgId?: string | null }, +): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(updates), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to update local task'); + return data.task; +} + +/** Trigger on-demand AI title regeneration. Owner/admin only. Returns the new title. */ +export async function regenerateTaskTitle(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/regenerate-title`, { method: 'POST' }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to regenerate title'); + return data.title as string; +} + +export async function continueTaskWithPiece( + taskId: number, + body: { piece: string; instruction: string }, +): Promise<{ jobId: string }> { + const res = await fetch(`${BASE}/local/tasks/${taskId}/continue`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to continue task'); + return data; +} + +export async function deleteLocalTask(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}`, { method: 'DELETE' }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to delete local task'); +} + +export async function cancelLocalTask(taskId: number): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/cancel`, { + method: 'POST', + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to cancel task'); +} + +export async function putFeedback( + taskId: number, + feedback: { rating: 'good' | 'bad'; tags: string[]; comment?: string }, +): Promise { + const res = await fetch(`${BASE}/local/tasks/${taskId}/feedback`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(feedback), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data?.error ?? 'Failed to update feedback'); + return data.task; +} diff --git a/ui/src/api/tools.ts b/ui/src/api/tools.ts new file mode 100644 index 0000000..7b9cb66 --- /dev/null +++ b/ui/src/api/tools.ts @@ -0,0 +1,44 @@ +// api.ts から分割(挙動不変): ツールカタログ。 +import { BASE } from './client'; + +// --- Tools --- +/** + * Runtime tool catalog entry. Mirrors `ToolCatalogEntry` exported by + * `src/bridge/tools-api.ts` (server side). See design doc step 4: + * docs/superpowers/specs/2026-05-21-settings-ui-and-config-restructure-design.md + */ +export interface ToolCatalogEntry { + name: string; + source: 'builtin' | 'meta' | 'mcp'; + /** + * Coarse grouping for UI. For builtin/meta tools this is a module name + * (e.g. 'core', 'web'). For MCP tools the server uses `mcp:`. + */ + category: string; + /** MCP server id (only set when source === 'mcp'). */ + serverId?: string; + /** Whether the tool can be invoked right now. */ + available: boolean; + /** Human-readable explanation when `available` is false. */ + reason?: string; + /** + * - 'global' → meta tools auto-injected by the agent loop (always available) + * - 'piece' → builtin tool gated by the workspace tool policy (Settings → Tools) + * - 'user' → per-user resource (MCP / SSH) + */ + scope: 'global' | 'piece' | 'user'; +} + +export async function fetchTools(): Promise { + const res = await fetch(`${BASE}/tools`); + if (!res.ok) throw new Error('Failed to fetch tools'); + const data = (await res.json()) as { tools?: unknown }; + if (!Array.isArray(data.tools)) return []; + // Server may still occasionally serve the legacy flat-string shape (e.g. + // during a transient mismatch / proxy / cache). Filter to only well-formed + // catalog entries so the UI never crashes; legacy strings are dropped. + return data.tools.filter( + (t): t is ToolCatalogEntry => + typeof t === 'object' && t !== null && typeof (t as { name?: unknown }).name === 'string', + ); +} diff --git a/ui/src/api/usage.ts b/ui/src/api/usage.ts new file mode 100644 index 0000000..a5bfbbd --- /dev/null +++ b/ui/src/api/usage.ts @@ -0,0 +1,57 @@ +// api.ts から分割(挙動不変): LLM usage ダッシュボード v2。 +import { BASE } from './client'; + +// ============================================================ +// LLM usage dashboard v2 (per-user, multi-axis group-by + local timezone). +// Spec: docs/superpowers/specs/2026-06-11-usage-dashboard-v2-design.md +export interface UsageCounters { + tokensIn: number; + tokensOut: number; + requests: number; +} +export type UsageGroupBy = 'source' | 'model' | 'route' | 'user' | 'org'; + +/** One time bucket: a per-series-key map of counters. Keys come from `keys`. */ +export interface UsageBucket { + bucket: string; // 'YYYY-MM-DD' | 'YYYY-Www' | 'YYYY-MM' + segments: Record; +} +export interface UsageByUser extends UsageCounters { + userId: string; + /** Resolved display name (real users); 'local' / 'system' for sentinels. */ + displayName: string; +} +export interface UsageDailyResponse { + from: string; + to: string; + granularity: 'hour' | 'day' | 'week' | 'month'; + groupBy: UsageGroupBy; + tzOffset: number; + scope: 'all' | 'self'; + /** Ordered series keys (legend/palette order). */ + keys: string[]; + /** Human labels for keys (only populated for groupBy=user; else key=label). */ + labels: Record; + series: UsageBucket[]; + totals: Record; + byUser?: UsageByUser[]; // admin / local mode only +} + +export async function getUsageDaily(params: { + from?: string; + to?: string; + granularity?: 'hour' | 'day' | 'week' | 'month'; + groupBy?: UsageGroupBy; + tzOffset?: number; +}): Promise { + const qs = new URLSearchParams(); + if (params.from) qs.set('from', params.from); + if (params.to) qs.set('to', params.to); + if (params.granularity) qs.set('granularity', params.granularity); + if (params.groupBy) qs.set('groupBy', params.groupBy); + if (params.tzOffset !== undefined) qs.set('tzOffset', String(params.tzOffset)); + const q = qs.toString(); + const res = await fetch(`${BASE}/usage/daily${q ? `?${q}` : ''}`); + if (!res.ok) throw new Error(`Failed to load usage (${res.status})`); + return res.json(); +} diff --git a/ui/src/api/users.ts b/ui/src/api/users.ts new file mode 100644 index 0000000..69d89e7 --- /dev/null +++ b/ui/src/api/users.ts @@ -0,0 +1,28 @@ +// api.ts から分割(挙動不変): ユーザー・組織。 +import { BASE } from './client'; + +export interface UserOrg { + orgId: string; + orgName: string; + fetchedAt: string; +} + +export async function fetchMyOrgs(): Promise { + const res = await fetch('/api/users/me/orgs'); + if (!res.ok) return []; + const { orgs } = (await res.json()) as { orgs: Array<{ orgId: string; orgName: string; fetchedAt: string }> }; + return orgs; +} + +export interface PickableUser { + id: string; + name: string | null; + email: string | null; + avatarUrl: string | null; +} + +export async function fetchPickableUsers(): Promise { + const res = await fetch(`${BASE}/users/pickable`); + if (!res.ok) return []; + return (await res.json()) as PickableUser[]; +} diff --git a/ui/src/api/workers.ts b/ui/src/api/workers.ts new file mode 100644 index 0000000..5920059 --- /dev/null +++ b/ui/src/api/workers.ts @@ -0,0 +1,118 @@ +// api.ts から分割(挙動不変): ワーカー・バックエンド・ノード状態(Side Info Panel)。 + +export interface WorkerInfo { + id: string; + endpoint: string | null; + model: string | null; + roles: string[]; + enabled: boolean; + /** True if this worker fronts an LLM gateway / proxy (Phase A). */ + proxy?: boolean; + /** Proxy implementation; only 'litellm' is currently shipped. */ + proxyType?: 'litellm'; +} + +export async function fetchWorkers(): Promise { + const res = await fetch('/api/workers', { credentials: 'include' }); + if (!res.ok) return []; + const data = await res.json() as { workers?: WorkerInfo[] }; + return data.workers ?? []; +} + +export interface BackendInfo { + id: string; + model: string | null; + online: boolean; +} + +export interface WorkerBackendsResponse { + source: 'direct' | 'proxy'; + proxyType?: 'litellm'; + backends: BackendInfo[]; + /** Set when the proxy probe failed (network error, 5xx). UI renders degraded. */ + error?: string; +} + +export async function fetchWorkerBackends(workerId: string): Promise { + const res = await fetch(`/api/workers/${encodeURIComponent(workerId)}/backends`, { + credentials: 'include', + }); + if (!res.ok && res.status !== 502) { + // 502 still carries a typed payload from the server. + return { source: 'direct', backends: [], error: `HTTP ${res.status}` }; + } + return await res.json() as WorkerBackendsResponse; +} + +// ── Side Info Panel ──────────────────────────────────────────────────────── + +export interface NodeStatus { + nodeId: string; + workerId: string; + source: 'direct' | 'proxy'; + online: boolean; + busy: boolean; + busySlots: number; + totalSlots: number; + loadedModel: string | null; + throughputTps: number | null; + lastSeen: string; + lastProbeError?: string; +} + +export interface WorkerStatusBackendRow { + id: string; + state: 'idle' | 'running'; + busySlots: number; + totalSlots: number; + online: boolean | null; +} + +export interface WorkerStatusRow { + id: string; + name: string; + roles: string[]; + state: 'idle' | 'running'; + /** True when this row represents a `proxy: true` worker. */ + proxy: boolean; + /** Slot pressure from BackendStatusRegistry. Populated for direct workers with a registry probe row. */ + busySlots?: number; + totalSlots?: number; + online?: boolean; + /** Per-backend rows for proxy workers (Phase 3c + dashboard tree). */ + backends?: WorkerStatusBackendRow[]; + /** + * Occupants of this worker — users whose running jobs use it, each with + * the job kind (`task_kind`: 'agent' = normal, 'reflection' = learning). + * Admin-only — the server only populates this for admins, so non-admin + * clients always receive undefined. + */ + occupants?: Array<{ user: string; kind: string }>; +} + +export async function fetchWorkerStatuses(): Promise { + const res = await fetch('/api/local/dashboard/workers'); + if (!res.ok) throw new Error(`Failed to list worker statuses: ${res.status}`); + return (await res.json()).workers; +} + +/** Thrown by fetchNodeStatus when the registry is not configured (HTTP 503). */ +export class NodeStatusUnavailableError extends Error { + constructor() { + super('node-status registry not configured'); + this.name = 'NodeStatusUnavailableError'; + } +} + +export async function fetchNodeStatus(): Promise { + const res = await fetch('/api/local/dashboard/node-status'); + if (!res.ok) { + // 503 = registry not configured (e.g. legacy install). Surface as an + // error so the React Query hook can back off polling instead of + // hammering the server every 5s indefinitely. The hook turns this + // into an empty list for rendering. + if (res.status === 503) throw new NodeStatusUnavailableError(); + throw new Error(`Failed to list node status: ${res.status}`); + } + return (await res.json()).nodes; +} diff --git a/ui/src/components/chat/ChatMessage.tsx b/ui/src/components/chat/ChatMessage.tsx index f673d4a..5042513 100644 --- a/ui/src/components/chat/ChatMessage.tsx +++ b/ui/src/components/chat/ChatMessage.tsx @@ -301,7 +301,7 @@ function ProgressCard({ comment, isStaleThinking }: { comment: LocalTaskComment; // movement-complete arrives (live tool calls during running movement). const toolCall = parseToolCallComment(comment.body); if (toolCall) { - return ; + return ; } // Checklist progress → dedicated card (center, retained as per decision) @@ -435,7 +435,7 @@ export function ChatMessage({ comment, taskId, imageBaseUrl, isStaleThinking, hi {author} · {new Date(createdAt).toLocaleString()}

$OL6T=(%8|;uj*WlBFYb583E~$O@V}!Lb(=RZl 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 DtJH-` diff --git a/docs/screenshots/workspace.png b/docs/screenshots/workspace.png index 79be7ad46475a5bf7d44127bab4139180d46b7bf..7c92b9581f7481fa98e6a5fb89033c35d9c18722 100644 GIT binary patch literal 333320 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