스쿼드¶
스쿼드는 하나의 워크스페이스 디렉터리와 메모리 뱅크, 태스크 목록을 공유하면서 플래너가 만든 계획을 함께 수행하는 LLM 에이전트 그룹입니다. 관리 API는 이 표면 전체를 노출합니다. /squads, /squad-discussions, /squad-templates, /squad-registry 아래 95개 엔드포인트가 있고, 여기에는 스쿼드 전체 및 스쿼드별 Server-Sent Events 스트림과 실행별 이벤트 원장이 포함됩니다.
이 문서의 모든 엔드포인트는 관리 API 기본 주소(기본값 http://127.0.0.1:8001/api/v1)에서 접근할 수 있고, /api/docs의 Swagger UI에서 Squads, Squad Sessions, Squad Templates, Squad Discussions 태그로 확인할 수 있습니다. aigo squad 명령 그룹이 같은 엔드포인트를 감싸며, 플래그는 CLI 레퍼런스에 있습니다.
헤드리스에서의 동작¶
이 표면 위에 무언가를 만들기 전에 알아 두어야 할 것이 두 가지 있습니다.
계획 실행은 헤드리스에서도 동일하게 동작합니다. 헤드리스 aigo-server에서 POST /squads/{id}/execute를 호출하면 요청을 검증하고, 플래너 에이전트를 해석하고, 플래너 분해를 수행하고, 만들어진 계획을 저장하며, autoApprove가 켜져 있으면 실행기까지 시작합니다. 승인을 기다리던 계획은 POST /squads/{id}/executions/{eid}/approve가 실행기를 시작합니다. 데스크톱 Tauri 명령과 이 엔드포인트들은 같은 두 공유 서비스를 감싼 얇은 래퍼이므로, 런은 두 런타임에서 같은 상태를 같은 순서로 거치고, 아래의 개입 엔드포인트도 실제로 진행 중인 런을 대상으로 동작합니다. pause와 resume은 태스크 디스패치 사이에서 런을 멈추고 다시 진행시키며, steer는 다음 디스패치 전에 실행기가 읽고, skip과 retry는 실재하는 태스크를 대상으로 합니다. 이슈 #4954 이전에는 이 엔드포인트가 빈 계획과 함께 실행 레코드만 만들고 아무것도 시작하지 않았기 때문에, 헤드리스 서버에서는 이 제어들이 진행되지 않는 런을 대상으로 동작했습니다. 차이가 하나 남아 있는데, 이는 수행 능력이 아니라 정책의 차이입니다. 태스크의 도구 호출은 POST /tools/execute와 같은 게이트를 거치므로, 데스크톱 앱이 필요한 도구는 실행되지 않고 도구 카탈로그가 알리는 사유와 함께 거부됩니다.
스쿼드는 MCP 도구로 제공됩니다. 이슈 #4581이 스쿼드 도구의 상태 접근을 squad::runtime_handles 뒤로 옮겼고, 데스크톱 앱과 aigo-server가 모두 이를 설치하므로 MCP 엔드포인트가 도구를 광고하고 POST /tools/execute도 두 모드에서 모두 받습니다. 노출되는 도구는 열세 개입니다: list_squads, list_squad_templates, create_squad, get_squad, list_squad_tasks, get_squad_execution, list_squad_executions, submit_squad_request, approve_squad_plan, reject_squad_plan, cancel_squad_execution, steer_squad_execution, send_squad_agent_message. submit_squad_request와 approve_squad_plan은 위 엔드포인트와 같은 공유 서비스에 도달하므로, 도구로 제출한 요청도 계획이 세워지고 승인된 계획은 실행됩니다. 두 도구 모두 무엇이 일어났는지를 응답으로 알려 주므로 호출자가 짐작할 필요가 없습니다. submit_squad_request는 plannerStarted를, approve_squad_plan은 executorStarted를 반환합니다. plannerStarted는 런타임이 제출 경로를 호출했는지가 아니라 플래너가 실제로 요청을 분해했는지를 답합니다. 라우터가 꺼져 있거나 플래너 에이전트에 쓸 수 있는 모델이 없거나 플래너 응답이 태스크를 하나도 만들지 않으면 run_planner_decomposition은 에이전트마다 태스크 하나씩인 폴백으로 내려앉고, 그 폴백에 안착한 제출은 plannerStarted: false와 함께 원인을 담은 plannerDegradedReason을 보고합니다(이슈 #4966). 이 사유는 실행에 남아 있으므로 이후의 모든 get_squad_execution 폴링에서도 함께 읽힙니다.
인증과 스코프¶
인증 방식은 관리 API의 나머지와 같습니다. X-API-Key 헤더, Authorization: Bearer 토큰, 또는 POST /api/v1/auth/login으로 받은 aigo_session 쿠키를 사용합니다.
액세스 키는 스코프를 가지며, 스쿼드 표면은 두 가지를 씁니다.
- 모든
GET은agent_read. - 모든
POST,PUT,PATCH,DELETE는agent_write.
변경 메서드이면서 의도적으로 agent_read인 경로가 둘 있습니다. 어느 쪽도 저장 상태를 바꾸지 않기 때문입니다. POST /squads/workspace/validate는 후보 디렉터리 경로를 검사만 하고, POST /squad-discussions/{id}/export는 전사본을 렌더링만 합니다. 권위 있는 표는 src-tauri/crates/aigo-rest/src/route_scope.rs의 ROUTE_MANIFEST이며, 아래 표의 스코프 열은 거기에서 그대로 옮긴 값입니다.
관리형 설치에서는 관리자가 features.hiddenPages에 /squad 페이지 ID를 넣어 스쿼드 페이지를 숨길 수 있습니다. 이 게이트는 /squads, /squad-discussions, /squad-templates, /squad-registry 경로 접두사와 대응하는 Tauri 명령까지 함께 막으므로, 페이지를 숨기면 UI뿐 아니라 API도 거부됩니다. POST /squad-registry/install이 이 목록에 있는 이유는 스쿼드 템플릿 저장소에 쓰기 때문이고, 빼 두면 같은 작업이 IPC에서는 거부되고 HTTP에서는 허용되기 때문입니다.
리소스 모델¶
스쿼드¶
스쿼드는 최상위 레코드입니다. 이름, 설명, 에이전트 라인업, 선택적 플래너 에이전트, 워크스페이스 경로, 상태를 가집니다. ID는 UUID v4입니다. 스쿼드는 <app_data_dir>/squads/<squad-uuid>.json에 저장되고 빠른 목록 조회를 위한 index.json이 옆에 놓이며, 모든 쓰기는 임시 파일에 쓴 뒤 이름을 바꾸는 원자적 쓰기입니다.
에이전트¶
에이전트는 라인업의 구성원 하나입니다. 이름, 역할(planner, developer, reviewer, writer, 또는 사용자 정의 레이블), 모델, 시스템 프롬프트, 지시문, 도구 구성, 실행 모드(인프로세스 또는 컨테이너)를 가집니다. 에이전트 ID는 요청이 지정하지 않으면 서버가 생성하는 UUID v4이므로, 템플릿으로 만든 에이전트도 생성 시점에 ID를 받습니다. 스쿼드의 plannerAgentId가 계획과 취합을 담당하는 에이전트를 가리키며, 그 역할을 맡고 있는 동안에는 해당 에이전트 삭제가 거부됩니다.
워크스페이스¶
워크스페이스는 스쿼드의 에이전트들이 공유하는 디스크상의 디렉터리입니다. POST /squads/{id}/workspace/init이 정해진 구조로 생성합니다.
{workspace_path}/
|-- .squad.json # 스쿼드 메타데이터와 전체 설정. 디렉터리만으로 이식 가능
|-- plans/ # 플래너가 만든 태스크 계획
|-- tasks/ # 태스크별 추적 파일
|-- memory/ # 에이전트별 메모리 뱅크 파일
|-- artifacts/ # 산출물과 결과 파일
|-- logs/ # 실행 로그와 이벤트 원장
|-- sessions/ # 저장된 에이전트 대화 이력
경로 탈출(..)은 거부되고, 아카이브나 디렉터리 순회 시 심볼릭 링크를 따라가지 않으며, 정리 작업은 삭제 전에 .squad.json이 있는지 확인합니다.
태스크¶
관리 태스크는 제목, 설명, 우선순위, 상태, 선택적 담당자, 다른 태스크에 대한 의존성, 재시도 예산을 가집니다. 상태는 pending, ready, assigned, in_progress, review, done, failed, cancelled입니다. 태스크는 의존성 그래프를 이루며, GET /squads/{id}/tasks/graph가 이를 반환하고 플래너가 실행 웨이브를 구성할 때 사용합니다.
계획과 실행¶
실행은 요청 하나에 대한 한 번의 수행입니다. 요청을 제출하면 실행 ID가 생기고, 플래너가 요청을 웨이브로 묶인 태스크 계획으로 바꾸며, 자동 승인을 요청하지 않았다면 계획은 승인을 기다리고, 이후 실행기가 웨이브를 하나씩 수행합니다. 실행은 상태, 현재 웨이브, 태스크별 시도 횟수, 운영자가 건너뛴 태스크, 그리고 실행이 받은 운영자 지시를 담습니다.
에이전트 세션¶
계획 실행과 별개로, 각 에이전트는 1:1 채팅 세션을 가질 수 있습니다. 세션은 sessions/ 아래에 저장되는 자체 대화 이력을 가지며, 한 에이전트가 여러 세션을 저장해 두고 그중 하나를 현재 세션으로 쓸 수 있습니다.
메모리 뱅크¶
각 에이전트는 memory/ 아래에 이름 있는 섹션으로 나뉜 마크다운 메모리 파일을 가집니다. 쓰기는 기본적으로 섹션에 덧붙이고, 요청하면 교체합니다. GET /squads/{id}/memory/search는 스쿼드의 에이전트 전체를 대상으로 검색하며, 에이전트와 섹션 필터를 선택적으로 받습니다.
토론 룸¶
토론 룸은 스쿼드에 붙는 진행형 다중 에이전트 대화입니다. 에이전트들이 전략과 턴 예산에 따라 차례로 발언하고, 사용자는 턴 사이에 메시지를 큐에 넣을 수 있으며, 룸은 구조화된 요약으로 마무리해 스쿼드 실행으로 넘길 수 있습니다.
템플릿¶
스쿼드 템플릿은 재사용 가능한 라인업입니다. 워크스페이스 없이 역할·모델·프롬프트를 담은 에이전트 정의만 가집니다. 템플릿으로 스쿼드를 만들면 라인업이 복사되고 템플릿의 planner 역할 에이전트가 플래너로 선택됩니다. 템플릿은 파일에서 가져오거나, 내보내거나, 기존 스쿼드에서 저장하거나, 공유 레지스트리 카탈로그에서 설치할 수 있습니다.
예산¶
예산은 스쿼드의 토큰과 비용 소비 상한을 정하고, 하드 리밋 아래에 경고 임계값을 둡니다. 설정은 저장되고 조회할 수 있으며, GET /squads/{id}/budget/usage가 진행 중인 실행의 소비량을 예산과 함께 보고합니다. 아래의 예산 이벤트 두 개는 선언되어 있지만 현재 아무것도 발생시키지 않으므로, 알림을 그 위에 만들지 말고 사용량 엔드포인트를 폴링하세요.
엔드포인트¶
아래 경로는 /api/v1 접두사를 생략했습니다. {id}는 스쿼드 ID이며, /squad-discussions 아래에서만 토론 룸 ID입니다.
스쿼드 라이프사이클¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
GET | /squads | agent_read | - | SquadIndexEntry[] |
POST | /squads | agent_write | CreateSquadRequest | Squad (201) |
GET | /squads/{id} | agent_read | - | Squad |
PUT | /squads/{id} | agent_write | UpdateSquadRequest | Squad |
DELETE | /squads/{id} | agent_write | ?keepWorkspace=(기본 true) | 204 |
POST | /squads/restore | agent_write | RestoreSquadRequest | RestoreSquadPreview |
DELETE /squads/{id}는 기본적으로 워크스페이스 디렉터리를 남겨 둡니다. 함께 지우려면 ?keepWorkspace=false를 전달하세요. POST /squads/restore는 워크스페이스의 .squad.json을 읽어 복원될 스쿼드의 미리보기를 반환하므로, 호출자가 확정 전에 확인할 수 있습니다.
워크스페이스¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
POST | /squads/{id}/workspace/init | agent_write | InitWorkspaceRequest | WorkspaceInfo |
GET | /squads/{id}/workspace/status | agent_read | - | WorkspaceStatus |
GET | /squads/{id}/readiness | agent_read | - | SquadReadiness |
DELETE | /squads/{id}/workspace | agent_write | ?archive=(기본 false) | CleanupResult |
POST | /squads/workspace/validate | agent_read | ValidatePathRequest | ValidationResult |
GET /squads/{id}/readiness는 워크스페이스 점검이 아니라 모델 사전 점검입니다. available은 에이전트가 하나 이상 있고 모든 에이전트가 모델을 해석할 수 있을 때 참이며, agents가 스쿼드 순서대로 에이전트별 판정을, cause와 message가 무엇이 막고 있는지를 담습니다. routerHealthy는 추론 라우터가 헬스 프로브에 응답했는지를 알려 주는 참고값으로, 거짓이어도 available을 낮추지 않습니다. 워크스페이스의 존재와 구조는 GET /squads/{id}/workspace/status에서 확인하세요.
태스크¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
GET | /squads/tasks/summary | agent_read | - | SquadTaskSummary[] |
POST | /squads/{id}/tasks | agent_write | CreateTaskRequest | ManagedTask (201) |
GET | /squads/{id}/tasks | agent_read | ?status= | ManagedTask[] |
GET | /squads/{id}/tasks/graph | agent_read | - | TaskGraph |
GET | /squads/{id}/tasks/{task_id} | agent_read | - | ManagedTask |
PATCH | /squads/{id}/tasks/{task_id} | agent_write | UpdateTaskRequest | ManagedTask |
DELETE | /squads/{id}/tasks/{task_id} | agent_write | ?force=(기본 false) | 204 |
POST | /squads/{id}/tasks/{task_id}/retry | agent_write | RetryTaskRequest(선택) | ManagedTask |
POST | /squads/{id}/tasks/{task_id}/reassign | agent_write | ReassignTaskRequest(선택) | ManagedTask |
PATCH | /squads/{id}/tasks/{task_id}/status | agent_write | UpdateTaskStatusRequest | ManagedTask |
태스크가 이미 진행된 뒤의 편집은 거부됩니다. 실행 중인 태스크는 description, priority, maxRetries만 받고, 끝난 태스크는 아무것도 받지 않습니다. 두 거부는 각각 409와 SQUAD_TASK_ACTIVE, SQUAD_TASK_TERMINAL로 응답합니다. 실행 중인 태스크의 DELETE에는 ?force=true가 필요하며, 이 경우 먼저 취소한 뒤 삭제합니다. 삭제된 태스크에 의존하던 태스크는 충족 불가능해지는 대신 그 의존성을 잃습니다. retry는 실패하거나 취소된 태스크를 다시 큐에 넣고, 재시도 예산을 다 썼더라도 force가 설정되어 있으면 한 번을 더 허용합니다.
메모리 뱅크¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
POST | /squads/{id}/memory/init | agent_write | - | 204 |
GET | /squads/{id}/memory/search | agent_read | ?q=, ?agentFilter=, ?sectionFilter=, ?caseSensitive=, ?limit= | MemorySearchResult[] |
GET | /squads/{id}/memory/{agent_id} | agent_read | - | MemoryContent |
POST | /squads/{id}/memory/{agent_id} | agent_write | WriteMemoryRequest | 204 |
GET | /squads/{id}/memory/{agent_id}/sections | agent_read | - | string[] |
에이전트¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
GET | /squads/{id}/agents | agent_read | - | AgentConfig[] |
POST | /squads/{id}/agents | agent_write | AddSquadAgentRequest | AgentConfig (201) |
GET | /squads/{id}/agents/{agent_id} | agent_read | - | AgentConfig |
PATCH | /squads/{id}/agents/{agent_id} | agent_write | UpdateSquadAgentRequest | UpdateSquadAgentResponse |
DELETE | /squads/{id}/agents/{agent_id} | agent_write | - | 204 |
POST | /squads/{id}/agents/bulk-update-model | agent_write | BulkUpdateAgentModelRequest | BulkUpdateAgentModelResult |
에이전트 삭제는 두 경우에 409로 거부됩니다. 해당 에이전트가 스쿼드의 플래너일 때는 SQUAD_AGENT_IS_PLANNER(먼저 plannerAgentId를 비우세요), 채팅 세션이나 실행 중인 실행에서 작업 중일 때는 SQUAD_AGENT_BUSY입니다. 추가, 수정, 삭제는 모두 squad:agents-changed를 발생시키므로, 열려 있는 스쿼드 페이지가 CLI나 API에서 이루어진 변경을 새로고침 없이 반영합니다.
PATCH는 객체를 병합하지 않고 통째로 교체합니다. modelPreferences를 보내면 선호 설정 레코드 전체가, toolConfig를 보내면 도구 구성 전체가 바뀝니다. aigo squad agent set은 이 두 필드에 한해 에이전트를 먼저 읽어 병합하며, API를 직접 호출하는 쪽도 같은 처리가 필요합니다. settingsOverrides와 containerConfig는 명시적 null로 비울 수 있고, 생략하면 그대로 둡니다.
에이전트 세션과 1:1 채팅¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
POST | /squads/{id}/agents/{agent_id}/session | agent_write | - | SessionInfo (201) |
DELETE | /squads/{id}/agents/{agent_id}/session | agent_write | - | 204 |
GET | /squads/{id}/agents/{agent_id}/status | agent_read | - | AgentSessionStatus |
POST | /squads/{id}/agents/{agent_id}/message | agent_write | SendMessageRequest | SendAgentMessageResponse |
PUT | /squads/{id}/agents/{agent_id}/response | agent_write | RecordResponseRequest | 200 |
GET | /squads/{id}/agents/{agent_id}/conversation | agent_read | - | PersistedSession \| null |
GET | /squads/{id}/agents/{agent_id}/sessions | agent_read | - | SessionIndexEntry[] |
POST | /squads/{id}/agents/{agent_id}/sessions | agent_write | - | 새 세션 ID (201) |
GET | /squads/{id}/agents/{agent_id}/sessions/{session_id} | agent_read | - | PersistedSession \| null |
DELETE | /squads/{id}/agents/{agent_id}/sessions/{session_id} | agent_write | - | 204 |
GET | /squads/{id}/agents/{agent_id}/chat-system-prompt | agent_read | ?maxTokens= | AgentChatSystemPrompt |
실행과 실행 제어¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
POST | /squads/{id}/execute | agent_write | SubmitExecutionRequest | { executionId } (201) |
GET | /squads/{id}/executions/{eid} | agent_read | - | SquadExecution |
POST | /squads/{id}/executions/{eid}/approve | agent_write | ApprovePlanRequest(선택) | 200 |
POST | /squads/{id}/executions/{eid}/reject | agent_write | RejectPlanRequest | 200 |
DELETE | /squads/{id}/executions/{eid} | agent_write | - | 200 |
POST | /squads/{id}/executions/{eid}/pause | agent_write | - | SquadExecution |
POST | /squads/{id}/executions/{eid}/resume | agent_write | - | SquadExecution |
POST | /squads/{id}/executions/{eid}/steer | agent_write | SteerExecutionRequest | SteerMessage |
POST | /squads/{id}/executions/{eid}/tasks/{task_id}/skip | agent_write | - | SquadExecution |
POST | /squads/{id}/executions/{eid}/tasks/{task_id}/retry | agent_write | - | SquadExecution |
모든 제어는 태스크 사이에서 작용합니다. 이미 실행 중인 태스크는 중단되지 않습니다. 일시 정지는 그 태스크를 끝까지 두고 다음 디스패치 직전에 멈추며, 건너뛰기는 아직 시작하지 않은 태스크에만 적용됩니다. 거부는 409와 SQUAD_EXECUTION_NOT_PAUSABLE, SQUAD_EXECUTION_NOT_RUNNING, SQUAD_EXECUTION_NOT_STEERABLE, SQUAD_EXECUTION_TASK_NOT_SKIPPABLE, SQUAD_EXECUTION_TASK_NOT_RETRIABLE입니다.
approve는 planOverride로 계획 교체본을 함께 보낼 수 있습니다. 교체본은 패치가 아니라 대체입니다. 교체본이 언급하지 않은 저장된 태스크는 제거되고, 저장된 계획에 있는 id를 가진 태스크는 그 정체성과 기록된 상태를 유지하며, 저장된 계획에 없는 id를 가진 태스크는 그 id 그대로 추가되고, id가 없는 태스크는 생성된 id로 추가됩니다.
id를 직접 정할 수 있다는 점이 한 교체본으로 여러 태스크를 새로 넣고 그 순서까지 지정할 수 있게 해줍니다. dependsOn은 저장된 계획이 아니라 결과 계획의 id를 기준으로 해석되기 때문입니다. 직접 정한 id는 ASCII 영문자, 숫자, 하이픈만 쓸 수 있고 128바이트를 넘을 수 없습니다. 태스크 건너뛰기와 재시도 엔드포인트에서 URL 경로 조각으로 되돌아오고, 실행이 시작되면 스쿼드 보드가 그 태스크를 워크스페이스의 tasks 디렉터리에 <id>.json과 <id>.md로 쓰기 때문입니다. 그래서 몇몇 이름은 아예 거부됩니다. 보드가 같은 디렉터리에 자기 index.json을 두므로 index가 그렇고, 윈도우 장치 이름(CON, PRN, AUX, NUL, COM1~COM9, LPT1~LPT9)도 그렇습니다. 결과 계획의 두 태스크는 같은 id를 가질 수 없고 대소문자만 다를 수도 없습니다. macOS와 윈도우에서는 그 둘이 한 파일이 되기 때문입니다. 저장된 id를 지정하는 것은 충돌이 아니라 그 태스크를 유지하라는 지시입니다. 담당자는 스쿼드의 에이전트여야 하고, 모든 dependsOn 항목은 그 교체본이 계획에 남긴 태스크를 가리켜야 하며, 자기 자신을 가리키는 경우를 포함한 순환은 실행 시작 전에 거부됩니다.
직접 정한 id는 스쿼드 보드에서도 비어 있어야 합니다. 보드는 실행 단위가 아니라 스쿼드 단위입니다. 워크스페이스에 놓여 모든 실행보다 오래 남으므로, 같은 스쿼드의 이전 실행이 이미 올려둔 id는 그 id와 그것을 쥐고 있는 플랜을, 서버가 그 플랜의 실행을 아직 메모리에 들고 있다면 실행까지 함께 알려주며 거부됩니다. 따라서 draft 같이 기억하기 쉬운 id를 한 스쿼드의 두 실행에서 재사용하면, 이전 실행이 기록한 상태와 결과와 오류가 조용히 덮어써지는 대신 승인 시점에 오류로 드러납니다. 승인하려는 실행의 계획이 이미 보드에 올려둔 id는 충돌이 아닙니다. 그것은 같은 계획이 다시 동기화하는 것이고, 재시작 후 복구된 실행이 하는 일이 바로 그것입니다. POST /squads/{id}/tasks로 planId 없이 보드에 직접 만든 행은 어떤 실행에도 속하지 않으므로 이 역시 가져올 수 없습니다. 그 id를 쓰려면 먼저 그 태스크를 지우세요.
steer 지시는 기본적으로 상시 적용되어 이후 모든 태스크 턴에 덧붙습니다. 본문은 { "message": "...", "scope": {...} }이고 scope는 내부 태그 방식입니다. 실행 전체에 적용하려면 생략하고, 범위를 좁히려면 {"type": "task", "taskId": "..."} 또는 {"type": "agent", "agentId": "..."}를 보냅니다. 이 요청은 모르는 필드를 거부하므로 최상위에 taskId만 넣으면 400으로 응답합니다.
운영 흐름을 이 위에 만들기 전에 헤드리스에서의 동작을 읽으세요.
워크스페이스 파일¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
GET | /squads/{id}/workspace/files | agent_read | ?path= | FileEntry[] |
GET | /squads/{id}/workspace/files/content | agent_read | ?path= | FileContent |
GET | /squads/{id}/workspace/search | agent_read | ?q= | WorkspaceSearchResult[] |
?path=는 워크스페이스 기준 상대 경로입니다. .. 구성 요소는 거부되며, 해석된 경로는 정규화된 뒤 워크스페이스 루트 안에 있어야 하므로 바깥을 가리키는 심볼릭 링크는 거부됩니다.
활동 로그¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
GET | /squads/{id}/activity-log | agent_read | ?limit=, ?offset= | ActivityLogResponse |
GET | /squads/{id}/activity-log/load | agent_read | ?limit=, ?offset= | ActivityLogResponse |
GET /squads/{id}/activity-log는 메모리 링 버퍼를 읽으므로 서버 재시작 후에는 비어 있습니다. GET /squads/{id}/activity-log/load는 워크스페이스의 logs/events.jsonl을 먼저 다시 읽으므로, 서버가 계속 떠 있지 않았던 스쿼드에는 이쪽을 쓰세요.
이력과 이벤트 원장¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
GET | /squads/{id}/history | agent_read | ?limit=, ?offset= | ExecutionRecord[] |
GET | /squads/{id}/history/{eid} | agent_read | - | ExecutionRecord |
GET | /squads/{id}/history/{eid}/logs | agent_read | ?agentId=, ?minLevel=, ?limit= | LogEntry[] |
GET | /squads/{id}/history/{eid}/events | agent_read | ?eventTypes=, ?afterSeq=, ?limit= | ExecutionEventPage |
POST | /squads/{id}/history/{eid}/report | agent_write | - | 리포트 파일 경로 |
GET /squads/{id}/history/{eid}/events는 실행별 타입 원장인 logs/{execution-id}.events.jsonl을 읽습니다. 해당 실행을 지목한 squad:* 이벤트마다 한 줄씩 발생 순서대로 기록되고, 각 줄이 1부터 시작하는 seq를 가지므로 ?afterSeq=로 이어 읽을 수 있습니다. ?eventTypes=는 쉼표로 구분한 이벤트 이름 목록을 받습니다. 한 실행은 기록 이벤트 수 상한과 바이트 크기 상한 중 먼저 닿는 쪽에서 잘리며, 어느 쪽에 닿았는지 밝히는 절단 레코드가 마지막에 붙으므로 잘린 원장은 도중에 끊기는 대신 잘렸다고 말합니다.
활동 로그가 읽는 스쿼드 단위 logs/events.jsonl은 다른 파일입니다. 모든 실행을 통틀어 최근 이벤트만 담고 실행 단위로 나뉘어 있지 않습니다.
분석, 예산, 긴급 정지, 이벤트 스트림¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
GET | /squads/{id}/analytics | agent_read | ?period= | SquadAnalytics |
GET | /squads/{id}/budget | agent_read | - | BudgetConfig |
PUT | /squads/{id}/budget | agent_write | BudgetConfig | 200 |
GET | /squads/{id}/budget/usage | agent_read | - | BudgetUsage |
POST | /squads/{id}/emergency-stop | agent_write | - | 200 |
GET | /squads/{id}/events | agent_read | ?types= | text/event-stream |
GET /squads/{id}/events는 스쿼드의 실시간 이벤트를 Server-Sent Events로 흘려보냅니다. ?types=로 이벤트 이름을 쉼표로 구분해 거를 수 있습니다(예: ?types=squad:task-completed,squad:execution-failed). squad: 계열이 아닌 이름을 넘기면 아무것도 오지 않는 연결이 열리는 대신 그 이름을 적은 400으로 거절합니다. POST /squads/{id}/emergency-stop은 해당 스쿼드의 종료되지 않은 실행을 모두 취소하고 squad:emergency-stopped를 발생시킵니다.
템플릿¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
GET | /squad-templates | agent_read | - | SquadTemplate[] |
GET | /squad-templates/{id} | agent_read | - | SquadTemplate |
POST | /squad-templates/import | agent_write | ImportTemplateRequest | SquadTemplate (201) |
GET | /squad-templates/{id}/export | agent_read | - | ExportTemplateResponse |
DELETE | /squad-templates/{id} | agent_write | - | 204 |
POST | /squads/{id}/save-as-template | agent_write | SaveAsTemplateRequest | SquadTemplate (201) |
POST | /squad-registry/install | agent_write | InstallSquadTemplateRequest | InstallSquadTemplateResult |
토론 룸¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
POST | /squad-discussions | agent_write | CreateDiscussionRequest | DiscussionRoom (201) |
GET | /squad-discussions/{id} | agent_read | - | DiscussionRoom |
DELETE | /squad-discussions/{id} | agent_write | - | 204 |
GET | /squads/{id}/discussions | agent_read | ?limit=, ?offset= | DiscussionSummary[] |
GET | /squads/{id}/discussions/completed | agent_read | ?limit=, ?offset= | DiscussionSummary[] |
POST | /squad-discussions/{id}/start | agent_write | - | 200 |
POST | /squad-discussions/{id}/pause | agent_write | - | 200 |
POST | /squad-discussions/{id}/resume | agent_write | - | 200 |
POST | /squad-discussions/{id}/stop | agent_write | - | 200 |
POST | /squad-discussions/{id}/messages | agent_write | PostDiscussionMessageRequest | PostDiscussionMessageResponse |
DELETE | /squad-discussions/{id}/messages/{message_id} | agent_write | - | { queueLength } |
PUT | /squad-discussions/{id}/mode | agent_write | SetDiscussionModeRequest | 204 |
PUT | /squad-discussions/{id}/turn-budget | agent_write | SetTurnBudgetRequest | 204 |
PUT | /squad-discussions/{id}/strategy | agent_write | SetDiscussionStrategyRequest | 204 |
POST | /squad-discussions/{id}/conclusion | agent_write | SynthesizeDiscussionConclusionRequest(선택) | DiscussionConclusion |
POST | /squad-discussions/{id}/handoff | agent_write | - | DiscussionHandoffRequest |
POST | /squad-discussions/{id}/export | agent_read | ExportDiscussionTranscriptRequest(선택) | ExportDiscussionTranscriptResponse |
GET | /squad-discussions/{id}/analytics | agent_read | - | DiscussionAnalytics |
GET /squads/{id}/discussions는 전사본이 없는 경량 요약을 반환합니다. 사용자가 룸을 열 때 GET /squad-discussions/{id}로 전체를 가져오세요. 두 목록 모두 ?limit=(기본 50, 최대 100)과 ?offset=으로 페이지를 나눕니다.
이벤트 스트림¶
| 메서드 | 경로 | 스코프 | 본문 또는 쿼리 | 반환 |
|---|---|---|---|---|
GET | /squads/events | agent_read | ?types= | text/event-stream |
GET | /squads/ws | agent_read | ?types=, ?since= | WebSocket (101) |
GET | /squads/{id}/ws | agent_read | ?types=, ?since= | WebSocket (101) |
/squads/events와 /squads/ws는 /squads/{id}보다 먼저 등록되므로 마지막 세그먼트가 스쿼드 id가 아니라 리터럴로 읽힙니다. /squads/ws는 스쿼드 전체 SSE 스트림의 WebSocket 짝입니다. /squads/{id}/ws는 스쿼드별 SSE 스트림의 WebSocket 짝이며, SSE 쪽과 달리 이어받기를 지원합니다. 이벤트와 이벤트 스트림을 참고하세요.
이벤트¶
스쿼드 이벤트는 스쿼드 전체 SSE 또는 WebSocket 스트림, 스쿼드별 SSE 또는 WebSocket 스트림, 그리고 실행을 지목한 이벤트에 한해 영속 원장 GET /squads/{id}/history/{eid}/events를 통해 클라이언트에 도달합니다. 데스크톱 앱에서는 같은 이름이 Tauri 이벤트로 발생하므로 페이로드는 두 전송 방식에서 동일합니다. 페이로드 필드는 camelCase입니다.
GET /squads/events와 GET /squads/ws는 모든 스쿼드의 squad:* 이름을 전부 실어 나르며, squad:discussion_* 계열도 포함합니다. GET /squads와 같은 agent_read를 요구합니다. 클라이언트가 아직 스쿼드 id를 모르거나 여러 스쿼드를 한꺼번에 지켜볼 때 쓰고, id를 아는 경우에는 스쿼드별 스트림을 쓰세요. 어떤 경로도 squad:* 밖의 것은 실어 나르지 않으며, GET /events는 여전히 도메인을 가리지 않는 스트림이고 admin을 요구합니다. ?types=로 쉼표로 구분한 부분집합만 받을 수 있고, squad: 계열이 아닌 이름은 아무것도 오지 않는 연결 대신 400으로 거절합니다. 두 WebSocket 경로는 소켓이 생기기 전에 업그레이드를 거절합니다. 스쿼드 전체 전송 쌍과 스쿼드별 WebSocket은 재개 커서를 지원하고, 잘린 이어받기는 stream:gap으로, 뒤처진 소비자는 stream:lagged로 알립니다. 이어받은 이벤트도 실시간 스트림과 같은 도메인 필터를 통과합니다. 이벤트 스트림을 참고하세요.
이 스트림은 데스크톱 앱에 내장된 관리 API에서도 동작합니다. 내장 서버가 시작될 때 스쿼드 이미터가 그 서버 쪽으로 다시 연결되므로(이슈 #4889), 데스크톱 UI에서 시작한 턴도 SSE로 보입니다. /schedules/events, /memory/events, /data/events도 이슈 #5040 이후로 같습니다. 각 하위 시스템이 따로 만들던 웹뷰 전용 이미터를, 내장 서버가 붙는 공유 싱크 하나로 바꾼 변경입니다.
실행 범위 페이로드는 executionId를, 태스크 범위 페이로드는 taskId를 함께 실어 보내므로, 클라이언트가 자체 상태를 추적하지 않고도 스트림 이벤트를 실행과 연결할 수 있습니다.
실행 라이프사이클¶
| 이벤트 | 페이로드 | 발생 시점 |
|---|---|---|
squad:planning-started | PlanningStartedPayload | 플래너가 요청 분석을 시작할 때. |
squad:plan-ready | PlanReadyPayload | 계획이 승인 대기 상태가 될 때. |
squad:execution-started | ExecutionStartedPayload | 승인 또는 자동 승인 후 실행이 시작될 때. |
squad:task-wave-started | TaskWaveStartedPayload | 새로운 병렬 태스크 웨이브가 시작될 때. |
squad:task-completed | TaskCompletedPayload | 태스크가 성공이든 실패든 끝났을 때. |
squad:aggregation-started | AggregationStartedPayload | 플래너가 결과 취합을 시작할 때. |
squad:execution-completed | ExecutionCompletedPayload | 실행이 성공적으로 끝났을 때. |
squad:execution-failed | ExecutionFailedPayload | 실행이 실패했을 때. |
실행 제어¶
| 이벤트 | 페이로드 | 발생 시점 |
|---|---|---|
squad:execution-paused | ExecutionPausedPayload | 운영자가 실행 중인 실행을 일시 정지할 때. |
squad:execution-resumed | ExecutionResumedPayload | 운영자가 일시 정지된 실행을 재개할 때. |
squad:execution-steered | ExecutionSteeredPayload | 운영자 지시가 수락될 때. |
squad:task-skipped | TaskSkippedPayload | 운영자가 아직 시작하지 않은 태스크를 건너뛸 때. |
에이전트 세션¶
| 이벤트 | 페이로드 | 발생 시점 |
|---|---|---|
squad:agent-session-started | AgentSessionStartedPayload | 에이전트 세션이 시작될 때. |
squad:agent-state-changed | AgentStateChangedPayload | 에이전트 상태가 바뀔 때. |
squad:agent-stream-chunk | AgentStreamChunkPayload | 에이전트에서 스트리밍 토큰 청크가 도착할 때. |
squad:agent-stream-completed | AgentStreamCompletedPayload | 에이전트 스트리밍이 완료될 때. |
squad:agent-error | AgentErrorPayload | 에이전트가 오류를 만났을 때. |
squad:agent-tool-call | AgentToolCallPayload | 에이전트가 도구를 실행하기 직전에. 1:1 채팅 턴과 플랜 실행 태스크 모두에서 발생한다. 실행 경로의 페이로드는 executionId와 taskId를 함께 담고, 채팅 턴 페이로드는 둘 다 생략한다. 실행 경로에서는 toolCall.arguments 안의 각 문자열이 4,000자로 잘리며 절단 표시가 붙고, 직렬화된 전체는 8,000자로 제한된다. 도구 자체는 원본 인자를 그대로 받는다. |
squad:agent-tool-result | AgentToolResultPayload | 에이전트가 호출한 도구가 결과를 돌려줄 때. 실행 경로에서는 toolResult.output이 4,000자로 잘린 사본이며 절단 표시가 붙는다. 에이전트는 전체 출력을 그대로 받는다. |
스쿼드 구성¶
| 이벤트 | 페이로드 | 발생 시점 |
|---|---|---|
squad:agents-changed | SquadAgentsChangedPayload | 에이전트가 추가·수정·삭제될 때. change는 added, updated, removed 중 하나입니다. |
태스크 라이프사이클¶
| 이벤트 | 페이로드 | 발생 시점 |
|---|---|---|
squad:task-created | TaskCreatedPayload | 태스크가 생성될 때. |
squad:task-status-changed | TaskStatusChangedPayload | 태스크 상태가 바뀔 때. |
squad:task-assigned | TaskAssignedPayload | 태스크가 에이전트에 배정될 때. |
squad:task-failed | TaskFailedPayload | 태스크가 실패할 때. |
squad:task-updated | TaskUpdatedPayload | 태스크의 편집 가능한 필드가 바뀔 때. 한 번의 편집이 여러 필드를 움직일 수 있어 태스크 전체를 싣습니다. |
squad:task-deleted | TaskDeletedPayload | 태스크가 삭제될 때. |
자원과 상태¶
| 이벤트 | 페이로드 | 발생 시점 |
|---|---|---|
squad:token-usage-update | TokenUsageUpdatePayload | 토큰 사용량이 바뀔 때(디바운스됨). |
squad:execution-token-usage | ExecutionTokenUsagePayload | 계획 실행 중 LLM 왕복마다, 해당 실행의 누적 사용량과 함께. |
squad:memory-updated | MemoryUpdatedPayload | 에이전트 메모리가 기록될 때. |
squad:workspace-file-changed | WorkspaceFileChangedPayload | 워크스페이스 파일이 바뀔 때(디바운스됨). |
예산과 안전¶
| 이벤트 | 페이로드 | 발생 시점 |
|---|---|---|
squad:budget-warning | BudgetWarningPayload | 경고 임계값용으로 선언되어 있으나 현재 어디에서도 발생하지 않습니다. |
squad:budget-exceeded | BudgetExceededPayload | 예산 상한 초과용으로 선언되어 있으나 현재 어디에서도 발생하지 않습니다. |
squad:emergency-stopped | EmergencyStoppedPayload | 긴급 정지가 발동될 때. |
컨테이너 출력¶
| 이벤트 | 페이로드 | 발생 시점 |
|---|---|---|
squad:container-output | ContainerOutputPayload | 컨테이너 stdout에서 구조화된 출력 블록이 파싱될 때. |
squad:container-log | ContainerLogPayload | 컨테이너 stdout에 마커가 아닌 디버그 줄이 나올 때. |
squad:container-status | ContainerStatusPayload | 컨테이너의 실행 상태가 바뀔 때. |
토론 룸¶
토론 이벤트는 위의 하이픈 이름과 달리 접두사 뒤에 밑줄을 씁니다.
| 이벤트 | 페이로드 | 발생 시점 |
|---|---|---|
squad:discussion_message | DiscussionMessagePayload | 공유 로그에 메시지가 추가될 때. |
squad:discussion_turn_started | DiscussionTurnStartedPayload | 에이전트 턴이 시작될 때. |
squad:discussion_turn_ended | DiscussionTurnEndedPayload | 에이전트 턴이 성공 또는 오류로 끝날 때. |
squad:discussion_status_changed | DiscussionStatusChangedPayload | 오케스트레이터 상태가 전이될 때. |
squad:discussion_queue_changed | DiscussionQueueChangedPayload | 대기 중인 사용자 메시지 큐가 바뀔 때(추가, 소비, 취소). |
squad:discussion_conclusion_synthesized | DiscussionConclusionSynthesizedPayload | 결론이 합성될 때. |
squad:discussion_turn_delta | DiscussionTurnDeltaPayload | 턴 진행 중 스트리밍 콘텐츠 청크가 도착할 때. 실시간 미리보기일 뿐이며, 전사본의 기준은 확정된 squad:discussion_message입니다. |
squad:discussion_turn_tool_call | DiscussionTurnToolCallPayload | 현재 발언자가 도구를 실행하기 직전에. |
squad:discussion_turn_tool_result | DiscussionTurnToolResultPayload | 현재 발언자가 호출한 도구가 결과를 돌려줄 때. |
사용 예¶
예제는 액세스 키를 X-API-Key로 보냅니다. 서버가 루프백에서 API 키 요구를 끈 채로 실행 중이면 이 헤더는 생략하세요.
템플릿으로 스쿼드 만들기¶
BASE=http://127.0.0.1:8001/api/v1
KEY=<your-access-key>
# 템플릿 고르기
curl -s -H "X-API-Key: $KEY" "$BASE/squad-templates" | jq -r '.[] | "\(.id)\t\(.name)"'
# 템플릿으로 스쿼드 생성
SQUAD=$(curl -s -X POST "$BASE/squads" \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"name":"docs-team","workspacePath":"/tmp/docs-team","templateId":"builtin-fullstack-dev-team"}' \
| jq -r .id)
# 워크스페이스 디렉터리 생성
curl -s -X POST "$BASE/squads/$SQUAD/workspace/init" \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"path":"/tmp/docs-team"}' | jq .
에이전트 추가하기¶
AGENT=$(curl -s -X POST "$BASE/squads/$SQUAD/agents" \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"name":"editor","role":{"type":"reviewer"},"modelPreferences":{"preferredModelId":"qwen3-8b"}}' \
| jq -r .id)
curl -s -H "X-API-Key: $KEY" "$BASE/squads/$SQUAD/agents" \
| jq -r '.[] | "\(.id)\t\(.name)\t\(.role.type)"'
요청 제출과 계획 승인¶
EXEC=$(curl -s -X POST "$BASE/squads/$SQUAD/execute" \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"request":"Draft the release notes for 1.13"}' \
| jq -r .executionId)
# 플래너가 계획을 만든 뒤 읽기
curl -s -H "X-API-Key: $KEY" "$BASE/squads/$SQUAD/executions/$EXEC" | jq '.plan.tasks'
# 계획 그대로 승인
curl -s -X POST "$BASE/squads/$SQUAD/executions/$EXEC/approve" -H "X-API-Key: $KEY"
# 태스크 목록을 교체하며 승인
curl -s -X POST "$BASE/squads/$SQUAD/executions/$EXEC/approve" \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d @plan-override.json
헤드리스 aigo-server에서도 같은 호출이 같은 일을 합니다. 플래너가 실행되고 계획이 저장되며 autoApprove를 주면 실행기가 시작됩니다. 헤드리스에서의 동작을 참고하세요.
이벤트 지켜보기¶
# 이 스쿼드의 모든 이벤트
curl -N -H "X-API-Key: $KEY" "$BASE/squads/$SQUAD/events"
# 진행률 표시에 필요한 두 가지만
curl -N -H "X-API-Key: $KEY" \
"$BASE/squads/$SQUAD/events?types=squad:task-completed,squad:execution-completed"
지시를 넣고 리포트 읽기¶
curl -s -X POST "$BASE/squads/$SQUAD/executions/$EXEC/pause" -H "X-API-Key: $KEY" | jq .status
curl -s -X POST "$BASE/squads/$SQUAD/executions/$EXEC/steer" \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"message":"Keep the public API unchanged"}' | jq .
curl -s -X POST "$BASE/squads/$SQUAD/executions/$EXEC/resume" -H "X-API-Key: $KEY" | jq .status
# 실행이 실제로 무엇을 했는지 다시 읽기
curl -s -H "X-API-Key: $KEY" \
"$BASE/squads/$SQUAD/history/$EXEC/events?limit=200" | jq -r '.events[] | "\(.seq)\t\(.event)"'
# 마크다운 리포트 생성. 응답은 리포트 본문이 아니라 서버 파일 시스템에 있는
# 리포트 경로이므로, 원격 호출자는 열 수 없는 경로를 받습니다. 실행 내용은 위의
# 이벤트 엔드포인트로 읽으세요.
curl -s -X POST "$BASE/squads/$SQUAD/history/$EXEC/report" -H "X-API-Key: $KEY" | jq -r .