Skip to content

Latest commit

 

History

History
360 lines (296 loc) · 45.2 KB

File metadata and controls

360 lines (296 loc) · 45.2 KB
created 2026-03-28
tags
cli-jaw
server
api
express
aliases
CLI-JAW Server API
server.ts reference
server_api

📚 INDEX · 체크리스트 ↗ · 커맨드 ↗ · 서버 API

server.ts — Glue + Route Registration (640L)

Express/SSE bootstrap + localhost/LAN opt-in 보안 가드 + src/routes/* registrar + mounted sub-router 등록. 현재 라이브 surface는 총 253개 route handler이며, 이 중 /를 제외한 API 엔드포인트는 252개다. mutation route(POST/PUT/DELETE)는 모두 requireAuth를 거친다. 단, requireAuth()는 loopback 요청을 토큰 없이 통과시키고, lanAllowed()가 true일 때 private IP도 LAN bypass로 통과시킨다. GET /api/auth/token은 Bearer bootstrap 전용이며 Sec-Fetch-Sitesame-origin 또는 none이 아닐 때 403을 반환한다.


Route Module Architecture

Module Lines Routes 역할
server.ts 640L mount glue Helmet/CORS/Host/rate-limit/SSE bootstrap + static middleware + route/sub-router registration
src/routes/static.ts 137L 4 root HTML + /media/:filename upload media serve + guarded /api/image local media serve + /api/widgets/:chatId/:widgetId inert widget file serve
src/routes/system.ts 82L 5 health/session/runtime/auth-token/slack-manifest
src/routes/messages.ts 107L 4 message list/count/search/latest
src/routes/command.ts 191L 4 slash command execution, command palette, normal message submit, Telegram elicitation callback relay
src/routes/instance.ts 53L 3 instance lock GET/POST/DELETE
src/routes/chat-sessions.ts 62L 4 session list/create/switch/delete (전 route requireAuth)
src/routes/search.ts 95L 1 /api/search 통합 검색 (requireAuth, corpus 검증, cursor 400)
src/routes/wiki.ts 118L 3 옵트인 위키 status/enable/configure (requireAuth, root 충돌 400, scaffold 실패 시 disabled 유지, 040)
src/routes/task.ts 59L 2 agent-native task list/action API
src/routes/events.ts 82L 1 /api/events data-only SSE event channel
src/routes/settings.ts 504L 24 settings/prompt/default-runtime migration/project pick/git summary/heartbeat-md/MCP/CLI registry/quota/copilot/Pi profile registration
src/routes/memory.ts 191L 13 memory runtime + KV memory + memory files
src/routes/browser.ts 489L 43 browser primitive/tab/debug/doctor/cleanup routes + adaptive fetch + web-ai render/send/poll/watch/sessions/capabilities/code/context routes
src/routes/jaw-memory.ts 352L 12 jaw memory search/read/save/context/list/init/reflect/flush/soul/soul-activate/bootstrap
src/routes/orchestrate.ts 1085L 18 reset/state/workers/worker-progress/worker-runs/snapshot/queue cancel/hold/queue steer async accept/dispatch/batch dispatch/worker result/state PUT
src/routes/goal.ts 183L 3 durable goal state get/history/set-update-complete-cancel-pause-resume-clear-reset
src/routes/goal-run.ts 83L 3 bounded goal-run state/preflight/start-pause-resume-stop
src/routes/messaging.ts 267L 7 upload/file-open/voice/telegram/channel/discord send + 온보딩 크리덴셜 검증
src/routes/employees.ts 123L 5 employee CRUD + reset
src/routes/skills.ts 89L 5 skills list/read/enable/disable/reset
src/routes/avatar.ts 146L 4 avatar summary + agent/user image upload/delete/read
src/routes/traces.ts 80L 3 public trace summary/event read routes
src/routes/link-preview.ts 319L 2 Rich link preview metadata fetch + guarded image proxy
src/routes/heartbeat.ts 47L 2 heartbeat GET + validated PUT
src/routes/jaw-ceo.ts 321L 20 Jaw CEO coordinator: state/message/query/docs-edit/settings/events/pending/watch/audit/voice/confirmations
src/routes/runtime-context.ts 46L 4 runtime context entry CRUD (ephemeral prompt injection), mounted at /api/runtime-context
src/routes/security-audit.ts 18L 2 security audit log entries + verify, mounted at /api/security-audit
src/routes/i18n.ts 35L 2 language list + locale bundle
src/routes/quota.ts 528L settings.ts가 호출하는 quota/auth/status reader helper
src/routes/quota-kiro-reverse.ts 239L Kiro/CodeWhisperer reverse-engineered usage-limits reader (fetchKiroUsage)
src/routes/quota-agy-reverse.ts 158L Antigravity quota snapshot reader (fetchAgyUsage)
src/routes/quota-cursor-dashboard.ts 203L Cursor dashboard session/usage reader (fetchCursorUsage)
src/routes/types.ts 3L shared AuthMiddleware type

Dashboard Board/Schedule (P3, mounted in server.ts)

Module Routes 역할
src/manager/board/routes.ts 99L / 5 board tasks CRUD + from-message
src/manager/schedule/routes.ts 112L / 5 scheduled work CRUD + dispatch

등록 순서 (server.ts)

static → employees → heartbeat → skills → jaw-memory → orchestrate
→ goal → task → events(SSE) → instance → chat-sessions → messages
→ system → agent-control → command → goal-run → memory → settings
→ messaging → avatar → traces → link-preview → jaw-ceo → runtime-context
→ security-audit → dashboard board/schedule → browser → i18n

라우트 모듈은 server.ts:298-396 부근에서 등록된다.


Base Route Surface (server.ts)

Method Path 설명
GET / public/dist/index.html이 있으면 Vite build를 서빙, 없으면 static fallback
GET /api/health { ok, version, uptime }
GET /api/slack/manifest 설정 페이지 "매니페스트 복사"용 canonical Slack 앱 매니페스트. 선택 ?name=은 1~35자 앱 표시명을 받고 봇 표시명은 자동 파생한다. { ok, data: { yaml, json, botDisplayName } } (비밀값 없음, unauthenticated)
POST /api/channels/validate 온보딩 마법사 라이브 크리덴셜 검증 { channel, botToken, appToken?, guildId? }{ ok, identity?, teamId? } 또는 { ok:false, error }. 저장하지 않고 검증만 수행
GET /api/session 현재 main session row 반환
GET /api/messages `includeTrace=1
GET /api/messages/search 메시지 본문 검색 결과 반환. ?q=, ?days=N(1-365), ?recent=N(1-5000), ?context=N(0-5), ?limit=N(1-50)
GET /api/messages/latest 가장 최근 메시지 스냅샷 반환
GET /api/runtime uptime, activeAgent, queuePending
GET /api/auth/token same-origin/CLI용 Bearer token bootstrap
POST /api/command slash command 실행
GET /api/commands 인터페이스별 command palette 데이터
POST /api/message 일반 프롬프트 제출. Optional target: RemoteTarget when Dashboard hub forwards a forum topic message (origin:'telegram'). Shape validated by isValidHubTarget + validateTarget. Optional external: true — manager/preview relay 등 외부 주입 표시; new_message broadcast에 실려 web UI가 유저 말풍선을 라이브 렌더 (devlog 260705).
POST /api/elicitation/callback Telegram Hub inline-keyboard elic:* callback relay. Hub bot forwards the tap to the mapped worker; worker completes handleElicitationCallback() and re-submits the combined answer via submitMessage() when all answers are collected.
POST /api/stop 현재 실행 중 agent 모두 종료
POST /api/clear UI-only clear broadcast, DB 메시지는 유지
POST /api/session/reset 메시지 삭제 + session reset
GET /media/:filename 미디어 파일 서빙
GET /api/image?path=<absolute path> requireAuth + assertSendFilePath() realpath guard로 JAW_HOME/workingDir/projectDirs 아래 이미지·비디오를 서빙. PNG/JPEG/GIF/WebP/MP4/WebM/MOV/OGG만 허용하고 SVG는 제외한다. 잘못된 입력·확장자는 400, 허용 루트 밖은 403, 미해결 경로·디렉터리는 404; 성공 응답은 no-store + nosniff.
GET /api/instance/lock 인스턴스 잠금 상태 조회
POST /api/instance/lock 인스턴스 잠금 (stopAll 보호)
DELETE /api/instance/lock 인스턴스 잠금 해제
GET /api/chat-sessions 채팅 세션 목록
POST /api/chat-sessions 새 채팅 세션 생성
POST /api/chat-sessions/:id/switch 활성 세션 전환
DELETE /api/chat-sessions/:id 세션 삭제 — 'default' 400, 진행 중/원격 바인딩 409, 성공 시 메시지 동시 삭제 (071)
GET /api/search 통합 검색 — corpus=chat|memory|wiki|all, 세션 횡단 기본 + sessionFilter, cursor 페이지네이션 (031)
GET /api/wiki/status 위키 상태 — enabled/root/promptDigest/provider. 기본 OFF, 디스크를 만들지 않는다 (040)
POST /api/wiki/enable 위키 활성화 — scaffold 후 provider ready 확인이 끝나야 설정을 기록한다 (040)
GET /api/wiki/entities 위키 entity 인덱스 (읽기 전용) — disabled면 디스크를 만지지 않고, root가 옮겨졌으면 부분 결과 대신 error다 (041)
POST /api/wiki/configure 위키 설정 변경 — 비활성화는 vault와 Git history를 보존한다 (040)

REST API

Category Endpoints
Core/Auth GET /api/health GET /api/slack/manifest GET /api/session GET /api/messages GET /api/messages/count GET /api/messages/search GET /api/messages/latest GET /api/runtime GET /api/auth/token GET /media/:filename GET /api/image GET /api/widgets/:chatId/:widgetId POST /api/message POST /api/stop POST /api/clear POST /api/session/reset
Commands POST /api/command GET /api/commands?interface= POST /api/elicitation/callback
Events GET /api/events
Chat Sessions GET /api/chat-sessions POST /api/chat-sessions POST /api/chat-sessions/:id/switch DELETE /api/chat-sessions/:id
Instance Lock GET /api/instance/lock POST /api/instance/lock DELETE /api/instance/lock
Search GET /api/search
Settings/Prompt GET/PUT /api/settings POST /api/settings/slack/reset POST /api/settings/runtime-default-migration POST /api/settings/multi-session-default-migration POST /api/project/pick GET /api/project/git-summary GET /api/codex-context GET/PUT /api/prompt GET /api/prompt-templates PUT /api/prompt-templates/:id GET/PUT /api/heartbeat-md
MCP/CLI/Quota GET/PUT /api/mcp POST /api/mcp/sync POST /api/mcp/install POST /api/mcp/reset GET /api/mcp/registry GET /api/cli-registry GET /api/cli-status GET /api/quota POST /api/copilot/refresh POST /api/pi/profiles/register GET /api/pi/models
Runtime Context GET /api/runtime-context POST /api/runtime-context DELETE /api/runtime-context/:id DELETE /api/runtime-context
Security Audit GET /api/security-audit/entries GET /api/security-audit/verify
Heartbeat GET/PUT /api/heartbeat
Browser POST /api/browser/start POST /api/browser/stop GET /api/browser/status GET /api/browser/doctor POST /api/browser/cleanup-runtimes GET /api/browser/snapshot POST /api/browser/screenshot POST /api/browser/act POST /api/browser/vision-click POST /api/browser/navigate POST /api/browser/reload POST /api/browser/resize GET /api/browser/tabs GET /api/browser/active-tab POST /api/browser/tab-switch POST /api/browser/tab-new POST /api/browser/tab-close POST /api/browser/tab-cleanup POST /api/browser/evaluate GET /api/browser/text GET /api/browser/dom GET /api/browser/console GET /api/browser/network POST /api/browser/fetch POST /api/browser/wait-for-selector POST /api/browser/wait-for-text POST /api/browser/web-ai/render POST /api/browser/web-ai/context-dry-run POST /api/browser/web-ai/context-render GET /api/browser/web-ai/status POST /api/browser/web-ai/send GET /api/browser/web-ai/poll GET /api/browser/web-ai/watch GET /api/browser/web-ai/watchers GET /api/browser/web-ai/sessions POST /api/browser/web-ai/sessions/prune GET /api/browser/web-ai/notifications GET /api/browser/web-ai/capabilities POST /api/browser/web-ai/query POST /api/browser/web-ai/code POST /api/browser/web-ai/code-extract POST /api/browser/web-ai/stop GET /api/browser/web-ai/diagnose
Code Mode GET /api/code/git-info GET /api/code/models POST /api/code/model-default POST /api/code/workspace/pick GET /api/code/model-assignments PUT /api/code/model-assignments/:role DELETE /api/code/model-assignments/:role GET /api/code/model-presets GET /api/code/sessions GET /api/code/sessions/stored POST /api/code/sessions/load POST /api/code/sessions/:id/ext POST /api/code/sessions/:id/fork POST /api/code/sessions/:id/model POST /api/code/sessions POST /api/code/sessions/:id/prompt POST /api/code/sessions/:id/cancel POST /api/code/sessions/:id/config DELETE /api/code/sessions/:id GET /api/code/permissions POST /api/code/permissions/:id
Orchestrate POST /api/orchestrate/reset GET /api/orchestrate/state GET /api/orchestrate/workers GET /api/orchestrate/worker-progress GET /api/orchestrate/worker-progress/:agentId (also accepts a current/recent runId) GET /api/orchestrate/worker-runs GET /api/orchestrate/worker-runs/:runId GET /api/orchestrate/worker-runs/:runId/events GET /api/orchestrate/worker-runs/:runId/output GET /api/orchestrate/snapshot DELETE /api/orchestrate/queue/:id POST /api/orchestrate/queue/:id/hold DELETE /api/orchestrate/queue/:id/hold POST /api/orchestrate/queue/:id/steer POST /api/orchestrate/dispatch/pending GET /api/orchestrate/dispatch/pending/:jti POST /api/orchestrate/dispatch POST /api/orchestrate/dispatch/batch GET /api/orchestrate/worker/:agentId/result (also accepts a current runId) PUT /api/orchestrate/state
Background Tasks GET/POST /api/bgtask GET/DELETE /api/bgtask/:id
Goal GET /api/goal GET /api/goal/history POST /api/goal
Goal Run GET /api/goal-run GET /api/goal-run/preflight POST /api/goal-run
Task GET /api/task POST /api/task
Employees GET /api/employees POST /api/employees PUT /api/employees/:id DELETE /api/employees/:id POST /api/employees/reset POST /api/employees/sessions/reset
Skills GET /api/skills GET /api/skills/:id POST /api/skills/enable POST /api/skills/disable POST /api/skills/reset
Memory Runtime / KV / Files GET /api/memory/status POST /api/memory/reindex POST /api/memory/bootstrap GET /api/memory/files GET /api/memory POST /api/memory DELETE /api/memory/:key GET /api/memory-files GET /api/memory-file GET /api/memory-files/:filename DELETE /api/memory-file DELETE /api/memory-files/:filename PUT /api/memory-files/settings
Jaw Memory GET /api/jaw-memory/search GET /api/jaw-memory/read POST /api/jaw-memory/save GET /api/jaw-memory/context GET /api/jaw-memory/list POST /api/jaw-memory/init POST /api/jaw-memory/reflect POST /api/jaw-memory/flush GET /api/jaw-memory/soul POST /api/jaw-memory/soul/activate POST /api/jaw-memory/soul POST /api/soul/bootstrap
Jaw CEO GET /api/jaw-ceo/state POST /api/jaw-ceo/message POST /api/jaw-ceo/query POST /api/jaw-ceo/docs/edit GET /api/jaw-ceo/settings PUT /api/jaw-ceo/settings POST /api/jaw-ceo/events/ingest POST /api/jaw-ceo/events/refresh GET /api/jaw-ceo/pending POST /api/jaw-ceo/pending/:completionKey/continue POST /api/jaw-ceo/pending/:completionKey/summarize POST /api/jaw-ceo/pending/:completionKey/ack POST /api/jaw-ceo/pending/:completionKey/dismiss POST /api/jaw-ceo/watch GET /api/jaw-ceo/audit POST /api/jaw-ceo/voice/connect POST /api/jaw-ceo/voice/:sessionId/close POST /api/jaw-ceo/confirmations POST /api/jaw-ceo/confirmations/:confirmationId/confirm POST /api/jaw-ceo/confirmations/:confirmationId/cancel
Messaging POST /api/upload POST /api/file/open POST /api/voice POST /api/telegram/send POST /api/channels/validate POST /api/channel/send POST /api/discord/send POST /api/slack/send GET /api/slack/history GET /api/slack/members GET /api/slack/users
Wiki GET /api/wiki/status GET /api/wiki/entities POST /api/wiki/enable POST /api/wiki/configure
Avatar GET /api/avatar POST /api/avatar/:target/upload DELETE /api/avatar/:target/image GET /api/avatar/:target/image
Traces GET /api/traces/:runId GET /api/traces/:runId/events GET /api/traces/:runId/events/:seq
Debug GET /api/debug/mem
Link Preview GET /api/link-preview?url= GET /api/link-preview/image?url=
Dashboard Board GET /api/dashboard/board/tasks POST /api/dashboard/board/tasks PATCH /api/dashboard/board/tasks/:id DELETE /api/dashboard/board/tasks/:id POST /api/dashboard/board/tasks/from-message
Dashboard Schedule GET /api/dashboard/schedule/work POST /api/dashboard/schedule/work PATCH /api/dashboard/schedule/work/:id DELETE /api/dashboard/schedule/work/:id POST /api/dashboard/schedule/work/:id/dispatch
i18n GET /api/i18n/languages GET /api/i18n/:lang

실제 코드(server.ts + src/routes/*.ts + mounted runtime/security/Jaw CEO/dashboard sub-router)에서 추출한 총 253개 route handler 기준이다. 이 중 API 엔드포인트는 252개이고, 나머지 1개는 / 엔트리이다. Browser API 43개는 src/routes/browser.ts에서 등록된다. Jaw CEO 20개는 src/routes/jaw-ceo.ts에서 sub-router로 등록된다.

POST /api/channel/send에서 channeltelegram|discord|slack|active transport다. 대화 ID는 chat_id 또는 target.targetId에 넣는다. Slack thread를 명시할 때 target.threadId는 reply ts가 아닌 parent message ts다. target을 생략하면 검증된 현재 대화와 thread를 사용한다. 빈 slack.channelIds는 임의 explicit channel을 열지 않으며, 이미 저장·검증된 lastActive/latestSeen과 같은 conversation/thread만 명시적으로 재사용할 수 있다.


Security / Guards

네트워크 가드

  • 기본 서버 bind는 127.0.0.1이지만 settings.network.bindHost, JAW_LAN_MODE=1, reverse-proxy mode에 따라 0.0.0.0 bind가 가능하다.
  • ALLOWED_HOSTS/ALLOWED_ORIGINS는 loopback을 기본 허용하고, LAN mode/bypass가 켜졌을 때 private network origin/host를 허용한다.
  • Legacy/client fallback WebSocket paths and manager-side note WebSocket surfaces must apply the same host/origin guard model; the current core public event path is SSE.

인증

  • mutation route는 모두 requireAuth로 보호된다.
  • 다만 로컬 동일 머신 사용성을 위해 loopback 요청은 Bearer 없이 허용된다. LAN bypass가 켜진 private IP 요청도 토큰 없이 통과할 수 있으므로 trusted network 전용이다.
  • /api/auth/token은 cross-origin token theft 방지를 위해 Sec-Fetch-Site를 검사한다.

경로/파일 보안

Surface Guard
Jaw Memory assertMemoryRelPath() + normalizeAdvancedReadPath()
Memory files assertMemoryRelPath() / assertFilename() / safeResolveUnder()
Skills assertSkillId()
Upload / avatar decodeFilenameSafe()
Telegram / channel send assertSendFilePath()
Local inline media (GET /api/image) assertSendFilePath() canonical realpath boundary + explicit media-extension allowlist (SVG 제외)
Avatar image serve safeResolveUnder(UPLOADS_DIR, basename(...))
  • /api/imagepath가 단일 절대경로인지 먼저 검사한 뒤 guard를 실행한다. 존재하지 않아 canonicalize할 수 없는 경로는 404, JAW_HOME/workingDir/projectDirs 밖 또는 symlink 탈출은 403이며, guard 통과 뒤에도 allowlist와 regular-file 검사를 거친다.

기타

  • Rate limit: in-memory, IP 기준 120 req/min.
  • helmet() 사용, CSP/COEP는 현재 비활성.

Selected Route Notes

/api/command

  • body textWEB_COMMAND_TEXT_LIMIT(30,000자)까지 자른 뒤 parseCommand()로 해석한다.
  • locale은 body/query/Accept-Language/settings 순으로 정해지고 Content-Language가 세팅된다.
  • command가 아니면 400 { code: 'not_command' }.

/api/elicitation/callback

  • Dashboard Telegram Hub의 bot.callbackQuery(/^elic:/)가 mapped instance로 POST /api/elicitation/callback을 호출한다.
  • body { chatId, callbackData, target? }를 받아 handleElicitationCallback(chatId, callbackData)로 pending single-select 답변을 갱신한다.
  • 모든 질문이 완료되면 combined answer를 submitMessage(..., { origin:'telegram', target, chatId, replyViaTarget })로 다시 주입하고 { ok:true, kind:'complete', ack, submit }을 반환한다.

/api/goal — action-based POST

  • GET /api/goal returns { ok, goal, pauseGate }; pauseGate is derived from active + agentPauseCount >= 1 and stays present with armed:false when no goal is active.
  • POST /api/goal body { action } 분기: set, refine-objective, update, done, cancel, pause, resume, clear, reset.
  • set may receive goalMode: "plan" and planHint; plan-mode stores a pending objective and rejects normal checkpoint updates until refine-objective replaces it with a concrete objective.
  • done action은 goalHasCompletionEvidence() gate를 거치며, evidence 없으면 409를 반환한다. force:true로 override 가능.
  • agent pause 첫 번째 audited 시도는 409pauseGate:{ armed:true, reason:"pause_gate_pending" }를 반환한다. 두 번째 audited 시도는 goal을 paused로 전환하고, productive update는 pending gate를 해제한다.
  • resume action은 이미 active이면 { alreadyActive:true }를 반환하고, paused goal을 resume하면 kickGoalContinuation()을 즉시 트리거한다.

/api/orchestrate/dispatch/pending

  • POST는 일반 requireAuth 클라이언트가 action-scoped dispatch를 제출하는 전용 경로다. 응답은 202 { jti, digest, expiresAt }이며 bearer나 boss token을 반환하지 않는다.
  • 서버는 설정된 dispatchApproval.operators의 Slack/Telegram/Discord 운영자 DM에 target, project root, task digest, mutable scope, fan-out cap, server-instance audience, JTI, digest, expiry를 보낸다. allowlist가 비거나 설정된 운영자 전달 중 하나라도 실패하면 pending을 취소하고 fail-closed 한다.
  • 승인은 HTTP로 받지 않는다. 검증된 Socket Mode/polling/gateway 이벤트에서 운영자가 approve <jti> <digest>를 보내야 한다. pending은 기본 120초, 최대 300초, 부팅 세대별, 원자적 single-use다. settings API는 300초 초과를 거부하고 store 경계도 300초로 clamp한다.
  • GET /api/orchestrate/dispatch/pending/:jti는 CLI polling용 상태/결과 조회이며 승인 기능은 없다.

/api/orchestrate/dispatch

  • boss-scoped x-jaw-boss-token이 필수다. employee spawn 환경에서는 이 토큰이 제거되므로 직원이 다시 dispatch하는 흐름은 서버에서 403으로 막힌다.
  • body는 정확히 하나의 target을 받는다: { agent, task } 또는 { virtual, task, role?, cli?, model? }.
  • virtual target은 src/core/employees.tssecurity/testing 프리셋 또는 자유 role 문자열로 SyntheticEmployeeRow를 만들고, DB employee row로 저장하지 않는다.
  • virtual dispatch에서 cli/model이 생략되면 현재 CLI와 src/cli/registry.ts의 registry default model을 사용한다.
  • 현재 plan이 있으면 dispatch body 상단에 ## Approved Plan으로 자동 주입된다.
  • wait:false async dispatch 202, worker_busy 409, and result polling payloads include both stable agentId and per-dispatch runId. The agentId remains the same-employee concurrency guard; runId identifies a specific worker run in memory progress history.
  • GET /api/orchestrate/worker-runs* exposes durable worker-run safe metadata/events and bounded raw output reads. List/get/events are safe-only and include both native status plus shared statusCategory (running|succeeded|failed|cancelled|orphaned) for comparison with background tasks; /output is the only raw-text worker-run route and requires explicit runId plus offset/limit. jaw worker read <runId> is the CLI consumer of that explicit raw-output route; worker status/watch remain safe-summary surfaces.
  • GET/POST /api/bgtask and GET/DELETE /api/bgtask/:id keep the existing background-task schema and add statusCategory to public task payloads. statusCategory is additive; bgtask storage and worker-run storage remain separate and no bgtask migration is performed.
  • POST /api/orchestrate/dispatch/batch는 같은 boss token으로 여러 직원/virtual task를 병렬 dispatch한다. 각 entry는 agent 또는 virtual 중 하나를 가진다. 응답은 full worker text를 기본 포함하지 않고 { agent, ok, runId, status, preview, recoveryCommand, outputBytes, error? } 형태의 safe summary metadata를 반환한다. raw output은 runId/api/orchestrate/worker-runs/:runId/output 또는 jaw worker read <runId>에서 명시적으로 읽는다. 구버전 manager가 이 route 없이 HTML 404를 반환하면 jaw dispatch --batch는 JSON parse 예외 대신 stale/missing route 진단을 출력한다.

/api/jaw-ceo/*

  • requireAuth 보호 sub-router로 /api/jaw-ceo 아래 마운트.
  • Core: state read, message send, query (dashboard/cli_readonly/web/github_read source), docs edit.
  • Settings: OpenAI API key management for voice.
  • Events: ingest manager events, refresh with port/cursor filter.
  • Pending: list/continue/summarize/ack/dismiss completions.
  • Watch/Audit: watch completion on port, audit log with kind/port filter.
  • Voice: WebRTC connect via OpenAI Realtime API, session close.
  • Confirmations: create/confirm/cancel action confirmations.

/api/quota

  • 응답 키: pi, agy, ai-e, claude, claude-e, codex, codex-app, cursor, gemini, grok, opencode, copilot, kiro-code (CLI_KEYS 순서).
  • pi는 Settings의 Pi profile registration을 통해 endpoint/model/key를 검증하고, quota 자체는 auth/status-only로 표시한다.
  • agysrc/routes/quota-agy-reverse.tsfetchAgyUsage()를 통해 Antigravity quota snapshot을 읽는다.
  • antigravity-usage --jsonremainingPercentage를 정밀 소수점 대신 0/1로만 반환하면 AGY window는 precision: "binary"status: "available" | "exhausted"를 포함한다. backend의 percent는 호환 필드일 뿐이며, UI는 exact percent bar 대신 Available / Exhausted 상태 텍스트를 표시해야 한다. upstream이 다시 정밀 퍼센트를 주면 기존 fractional path가 그대로 사용된다.
  • cursorsrc/routes/quota-cursor-dashboard.tsfetchCursorUsage()를 통해 dashboard session/usage를 읽는다.
  • grok~/.grok/auth.json의 OIDC key를 우선 읽고 https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig gRPC-web 응답으로 SuperGrok weekly usage pool window를 만든다. 실패하면 legacy cli-chat-proxy.grok.com/v1/billing monthly credits window로 fallback한다.
  • kiro-codesrc/routes/quota-kiro-reverse.tsfetchKiroUsage()를 통해 CodeWhisperer GetUsageLimits API를 reverse-engineer 호출한다.

Wiki lifecycle ownership

  • wiki.enabled and wiki.root are owned by POST /api/wiki/enable and POST /api/wiki/configure. The enable route scaffolds the vault, verifies provider readiness, and only then persists the lifecycle configuration.
  • Generic PUT /api/settings rejects either lifecycle field with 409 wiki_configuration_requires_wiki_route; wiki.promptDigest remains writable there. External settings.json reloads ignore the two lifecycle fields and emit a warning instead of bypassing the wiki routes.
  • At server startup, a persisted enabled vault whose provider is not ready emits [jaw:wiki] enabled but unavailable at startup (...) without logging the vault path. Repair it through POST /api/wiki/enable, or disable it through POST /api/wiki/configure.

/api/project/git-summary

  • GET /api/project/git-summary는 Settings의 projectDirs[0]만 읽는 read-only header helper다.
  • POST /api/settings/runtime-default-migrationrequireAuth 뒤에서 정확히 { "action": "accept" | "keep" }만 받는다. settingsSchemaVersionruntimeDefaultMigration은 server-owned라 generic PUT /api/settings에 포함하면 400이다. 이미 terminal인 migration은 409 runtime_default_migration_terminal과 최신 settings snapshot을 반환한다. 성공 200과 conflict 409의 snapshot은 모두 GET/PUT /api/settings와 같은 redaction을 거친다.
  • POST /api/settings/multi-session-default-migration은 같은 계약을 따르며 다중 세션 기본 ON 전환(schema v3)을 소유한다. multiSessionDefaultMigration도 server-owned이므로 generic PUT에 포함하면 400이고, terminal이면 409 multi_session_default_migration_terminal이다. 런타임 migration과 별도 route인 이유는 두 전환이 서로 다른 시점에 서로 다른 이유로 롤백될 수 있고, v1 문서는 둘이 동시에 pending이기 때문이다 — 한 쪽 응답이 다른 쪽 답으로 읽히면 안 된다. acceptenabled:true와, 현재 maxConcurrent가 1일 때 2를 함께 적용한다(동의 문구가 그 둘을 말한다). 1이 아닌 유효 값은 그대로 둔다.
  • Slack 연결 환경변수(SLACK_BOT_TOKEN, SLACK_APP_TOKEN, SLACK_TEAM_ID, SLACK_CHANNEL_IDS)는 각자 대응하는 설정 필드를 런타임에서 소유한다. SLACK_BOT_TOKENenabled|botToken, SLACK_APP_TOKENappToken, SLACK_TEAM_IDteamId, SLACK_CHANNEL_IDSchannelIds를 소유한다. GET /api/settingsslackEnvironmentVariables에 변수 이름만 싣고 연결값을 redaction한다. generic PUT은 요청에 포함된 env-owned path만 409 slack_connection_managed_by_environment로 거부하며, 전체 연결을 비우는 POST /api/settings/slack/reset은 환경변수가 하나라도 있으면 계속 409다. Settings UI와 CLI setup도 혼합 변경을 보수적으로 막는다. 직렬화는 env-owned 필드만 제거하므로 환경값이 settings.json으로 복사되지 않고 metadata-only override가 파일 토큰을 지우지 않는다. 동작 설정(forwardAll, allowBots, mentionOnly, replyInThread)은 계속 저장할 수 있다.
  • runtime.codexApp.multiplex는 사용자 소유 boolean gate이며 실행 기본값은 false다. raw settings에 키가 없으면 GET /api/settings의 실행 snapshot은 false를 제공하지만 다음 저장에서도 키와 그 결과 비는 codexApp/runtime container를 만들지 않는다. explicit false/true는 보존한다. generic PUT /api/settings에서 문자열·숫자·null400 invalid_settings_field, host probe 소유인 runtime.codexApp.laneMode는 값과 무관하게 기존 호환 오류 400 server_owned_settings_field로 거부한다.
  • GET /api/cli-status는 cold에서 nullable probeState:"checking", stale에서 즉시 이전 snapshot을 반환한다. binary/PATH, auth, capability detection은 request event loop가 아니라 finite-lifetime child worker에서 수행된다.
  • probe가 실패하면 probeState:"failing" + probeError/probeFailures/nextRetryAt을 실어 보낸다. failing은 "포기"가 아니라 "계속 실패 중, 사유는 이것"이며 캐시는 재시도를 이어간다 (첫 재시도 즉시, 이후 지수 백오프 최대 5분). 실패 기록은 남아 있는 snapshot보다 우선한다 — 그래야 동작하는 것처럼 보이는 stale 응답으로 실패를 감추지 않는다 (#277).
  • GET /api/cli-status?force=1은 백오프 창을 건너뛴다. 재시도는 타이머가 아니라 요청 시점에 일어나므로, 원인을 고친 사용자가 새로고침을 눌러도 백오프가 끝날 때까지 낡은 실패를 계속 보게 되는 것을 막는다.
  • 응답은 legacy Web UI header의 compact git status 전용이다: branch/hash, tracked modified count, untracked count.
  • project root가 없거나, home 밖 경로거나, git repository가 아니거나, git 호출이 실패하면 mutation 없이 { available:false, reason } 형태로 조용히 숨길 수 있는 payload를 반환한다.
  • status count는 git status --porcelain=v1 -z --untracked-files=all 기반이며 ignored entry는 표시하지 않는다.

/api/pi/*

  • POST /api/pi/profiles/register — body의 provider/endpoint/model/key/mode를 normalizePiProfile()로 정규화하고, isolated PI_CODING_AGENT_DIR 아래 models.json + settings.json을 만든 뒤 pi --offline --list-models <profile>로 등록 모델이 실제 Pi model list에 나타나는지 검증한다. 성공 시 applySettings()를 통해 settings.piperCli.pi.provider/model을 함께 저장한다.
  • GET /api/pi/models?profile=<id> — 저장된 Pi profile 설정으로 모델 목록을 재발견하고, settings.pi.discoveredModels[profile] 및 Settings UI dropdown 갱신에 사용할 배열을 반환한다.
  • Pi 응답은 API key를 직접 반환하지 않고 apiKeySet, apiKeyLast4, apiKeySource만 노출한다.

/api/runtime-context

  • GET — 모든 entry를 반환하며 각 entry에 expired boolean을 추가한다.
  • POST — body { text, label?, expiresAt? }. text는 필수(max 2000자). 201 + 생성된 entry 반환.
  • DELETE /:id — 단일 entry 삭제. 없으면 404.
  • DELETE / — 전체 삭제. { cleared: <count> } 반환.

/api/security-audit/*

  • GET /entries — audit log entries (limit param, max 500).
  • GET /verify — integrity verification of audit chain.

WebSocket Events

이 heading은 structure/check-doc-drift.sh의 anchor로 유지한다. X-01 이후 current server는 public browser events를 WebSocket으로 broadcast하지 않는다. 아래 catalog는 broadcast()가 발행하는 event type 전체다. 기본 audience인 public 항목은 src/core/bus.tssrc/core/event-bus.tsGET /api/events로 전달되는 SSE surface이고, broadcast(..., 'internal')로 발행되는 항목은 audience gate에서 SSE로 나가지 않고 in-process listener만 받으므로 해당 행에 internal audience로 명시한다. WebSocket은 public/js/ws.tsbin/commands/tui/channel.ts/api/events를 한 번도 열 수 없는 pre-X-01 server에 붙을 때만 fallback path로 사용한다. Current Web UI는 reconnect 시 REST snapshot hydration으로 agent_status, queue_update, 비-IDLE orc_state 상태를 보강한다.

Type 설명
agent_status running/done/error/evaluating + agentId/phase
agent_tool tool/thinking/search 진행 step
agent_output 라이브 text chunk preview
agent_done 최종 응답 + toolLog + origin
agent_retry / agent_fallback retry/fallback 안내
alert_escalation repeated failure / capacity fallback escalation alert
agent_smoke smoke auto-continue 안내
queue_update 대기열 길이 갱신
clear / session_reset UI clear / session reset broadcast
new_message Telegram/Discord/Slack inbound message
orc_state PABCD 상태 변경 + taskAnchor/resolvedSelection/interview 컨텍스트
orchestrate_done / orchestrate_warning orchestration 완료/실패 + 비차단 경고
request_settled 요청 하나의 최종 결말 (completed|steered|merged|failed|cancelled|dropped|skipped). src/orchestrator/request-registry.tssettleOnce()가 멱등이라 요청당 정확히 한 번 발생한다. orchestrate_done을 대체하지 않고 보완한다 — steer 성공처럼 완료 이벤트가 없는 경로를 덮기 위한 것. 전체 payload 스키마는 structure/stream-events.md 참고 (#276)
steer_started /steer 또는 pending queue steer가 새 프롬프트를 accepted 상태로 전환
agent_added / agent_updated / agent_deleted employee CRUD 반영
agent:claude-e:runtime_started / agent:claude-e:spawned / agent:claude-e:session / agent:claude-e:prompt_injected Claude E native helper start/session/prompt lifecycle bridge
agent:claude-e:stop / agent:claude-e:stop_failure / agent:claude-e:interrupted / agent:claude-e:cleanup / agent:claude-e:error Claude E native helper stop/error lifecycle bridge
settings_change project/workspace settings 변경 신호
memory_status memory sidebar / runtime 상태 갱신 신호
system_notice compact refresh 같은 시스템 공지
heartbeat_pending pending heartbeat job 수
worker_stalled / worker_disconnected / worker_timeout distributed worker 상태 변화; 같은 상태가 /api/orchestrate/worker-progress의 safe attention metadata에도 반영됨
goal_done / goal_done_rejected / goal_cancel / goal_continuation / goal_continuation_failed / goal_continuation_limit durable goal / bounded continuation lifecycle
goal_pause_detected / goal_pause_gate_pending goal pause 2-tap gate 감지 및 pending gate continuation suppression
session_switched / session_created / session_list multi-session state update
schedule_wakeup / schedule_wakeup_failed ScheduleWakeup continuation scheduling lifecycle
bgtask_update background task lifecycle/status update for manager/runtime monitors; running and changed entries include native status plus shared statusCategory
widget_updated file-backed diagram widget changed on disk; payload {chatId, widgetId} for targeted iframe refetch
policy_verdict internal audience 전용 — runtime policy hook 판정 broadcast (src/core/policy-hooks.ts emitVerdict). broadcast(..., 'internal')이므로 src/core/bus.ts의 audience gate에서 SSE로 발행되지 않고 in-process listener만 받는다. payload는 verdict + optional channel

Manager Dashboard Server Surface

jaw dashboard serve가 띄우는 별도 manager 서버(src/manager/server.ts, 992L)는 core server.ts route count에 포함하지 않는다. Manager instance state는 src/manager/instance-registry.ts(120L)가 cached scan + diff event source로 제공한다. Manager React UI는 /api/manager/events, /api/dashboard/instances, /i/:port/api/messages/latest 계열 HTTP polling으로 상태를 읽고, manager server는 src/manager/worker-events.ts + src/manager/worker-sse-client.ts를 통해 각 worker instance의 GET /api/events를 server-side로 구독해 latest-message cache를 갱신한다. #233부터 worker의 settings:settings_change(cli/model/projectDirs 변경)는 worker_settings_change로 재발행되어 GET /api/manager/events/stream(SSE)으로 manager UI에 live 전달되고, UI(useManagerEventStream)는 해당 instance row를 즉시 재조회한다. Code mode의 goal/PABCD/background/worker monitors는 child Jaw instance가 아니라 manager-local src/manager/routes/runtime-monitor.ts를 통해 /api/manager/runtime-status, /api/bgtask, /api/orchestrate/worker-progress JSON API를 직접 읽는다. #260 이후 per-instance Jaw sidebar에는 worker progress monitor가 렌더링되지 않고, Code/CEO runtime observability lane에서만 표시된다. /api/bgtaskpreset: "web-ai" path는 native web-ai watcher가 진행하는 session id를 session-status probe로 관찰하고 session-answer extractor로 완료 결과를 전달한다. 이 bridge는 BrowserPanel tab state나 Code session transcript ownership으로 승격하지 않는다. background_tasks.notified_at 변경도 bgtask_update를 발행하므로 Manager monitor는 completion transition 뒤 별도 reload 없이 notification handoff 상태를 재조회할 수 있다.

Surface Endpoints
Manager health/scan GET /api/dashboard/health GET /api/dashboard/instances GET /api/dashboard/instances/:port POST /api/dashboard/instances/:port/message POST /api/dashboard/instances/:port/project/pick
Manager events/logs GET /api/manager/events GET /api/manager/events/stream (SSE) GET /api/manager/health-history/:port GET /api/manager/instance-logs/:port
Runtime monitors GET /api/manager/runtime-status GET/POST /api/bgtask GET/DELETE /api/bgtask/:id GET /api/orchestrate/worker-progress GET /api/orchestrate/worker-progress/:agentId
Registry GET /api/dashboard/registry PATCH /api/dashboard/registry
Lifecycle POST /api/dashboard/lifecycle/:action (start/stop/restart/perm/unperm)
Process control GET /api/dashboard/process-control POST /api/dashboard/process-control/adopt POST /api/dashboard/process-control/stop-managed POST /api/dashboard/process-control/force-release
Desktop/Electron GET /api/dashboard/desktop-status GET/POST /api/dashboard/electron-metrics
Design workspace GET /api/dashboard/design/version GET/POST /api/dashboard/design/pages POST /api/dashboard/design/pages/rescan GET/PATCH /api/dashboard/design/pages/:pageId GET/PUT /api/dashboard/design/pages/:pageId/files/{*filePath} GET /api/dashboard/design/pages/:pageId/local-paths POST /api/dashboard/design/pages/:pageId/rescan POST /api/dashboard/design/pages/:pageId/export GET/POST /api/dashboard/design/pages/:pageId/snapshots POST /api/dashboard/design/pages/:pageId/snapshots/:snapshotId/restore GET /api/dashboard/design/catalog GET /api/dashboard/design/pages/:pageId/preview
Embedded browser POST/GET /api/manager/embedded-browser/targets POST /api/dashboard/instances/:port/embedded-browser/targets POST /api/manager/embedded-browser/:targetId/screenshot POST /api/manager/embedded-browser/:targetId/snapshot POST /api/manager/embedded-browser/:targetId/act GET /api/manager/embedded-browser/commands POST /api/manager/embedded-browser/commands/:id/result
Notes GET /api/dashboard/notes/auth/status POST /api/dashboard/notes/ws-token GET /api/dashboard/notes/history/status POST /api/dashboard/notes/history/init GET /api/dashboard/notes/history GET /api/dashboard/notes/history/show GET /api/dashboard/notes/history/diff POST /api/dashboard/notes/history/flush GET /api/dashboard/notes/plugins GET /api/dashboard/notes/plugins/:id/asset/* GET /api/dashboard/notes/version POST /api/dashboard/notes/asset POST /api/dashboard/notes/asset/remote GET /api/dashboard/notes/asset GET /api/dashboard/notes/info GET /api/dashboard/notes/tree GET /api/dashboard/notes/templates GET /api/dashboard/notes/template GET /api/dashboard/notes/snippets GET /api/dashboard/notes/snippets/file PUT /api/dashboard/notes/snippets/toggle PUT /api/dashboard/notes/theme PUT /api/dashboard/notes/plugins/:id/toggle GET /api/dashboard/notes/search GET /api/dashboard/notes/index GET /api/dashboard/notes/capabilities GET/POST/PUT /api/dashboard/notes/file POST /api/dashboard/notes/folder POST /api/dashboard/notes/rename POST /api/dashboard/notes/trash
Board GET/POST/PATCH/DELETE /api/dashboard/board/tasks POST /api/dashboard/board/tasks/from-message
Schedule GET/POST/PATCH/DELETE /api/dashboard/schedule/work POST /api/dashboard/schedule/work/:id/dispatch
Reminders GET /api/dashboard/reminders POST /api/dashboard/reminders POST /api/dashboard/reminders/from-message PATCH /api/dashboard/reminders/:id
Telegram Hub GET/PUT /api/dashboard/telegram-hub POST /api/dashboard/telegram-hub/routes DELETE /api/dashboard/telegram-hub/routes/:chatId/:threadId POST /api/dashboard/telegram-hub/outbound (loopback-only)
Connector POST /api/dashboard/connector/board PATCH /api/dashboard/connector/board/:id POST /api/dashboard/connector/reminders PATCH /api/dashboard/connector/reminders/:id POST /api/dashboard/connector/notes GET /api/dashboard/connector/audit
Git diff/status/worktrees POST /api/dashboard/git/repo-candidates POST /api/dashboard/git/diff-summary POST /api/dashboard/git/file-diff POST /api/dashboard/git/status-map POST /api/dashboard/git/scm-snapshot POST /api/dashboard/git/scm-operation POST /api/dashboard/git/worktrees POST /api/dashboard/git/worktree-operation-preview POST /api/dashboard/git/worktree-operation
Memory federation GET /api/dashboard/memory/instances GET /api/dashboard/memory/search GET /api/dashboard/memory/read GET /api/dashboard/memory/chat/search (?format=envelope로 세션 provenance 포함 envelope opt-in — 033)
Memory embedding GET /api/dashboard/memory/embed-config POST /api/dashboard/memory/embed-config POST /api/dashboard/memory/reindex GET /api/dashboard/memory/embed-state GET /api/dashboard/memory/embed-estimate GET /api/dashboard/memory/reindex-stream (SSE)
Wiki (읽기 전용 프록시) GET /api/dashboard/wiki/status GET /api/dashboard/wiki/entities (?port= 필수). Notes와 같은 preflight+auth pair를 지나고, 포트 범위는 isDashboardProxyPortAllowed를 호출해 판정하며, 미등록·오프라인·upstream 실패는 전부 503 wiki_core_unavailable 하나로 답한다 (041-C). /i 프록시로 대체하지 말 것 — 그 경로는 loopback으로 접속해 인스턴스의 requireAuth가 토큰 검사 전에 통과시키므로 인증 경계가 사라진다
Jaw CEO (manager) /api/jaw-ceo/* (same sub-router as core server)