내 에이전트 붙이기
endue OS 의 에이전트(linux · mac · windows · chrome · android)는 특별한 것이 아니에요. 같은 와이어 프로토콜을 구현하면 직접 만든 프로그램도 기기(actual Device)가 되고, 그 프로그램이 알려 준 도구를 명령 API 로 부를 수 있어요.
1. 기기를 등록하고 토큰 받기
섹션 제목: “1. 기기를 등록하고 토큰 받기”curl -s -X POST $OS/api/v1/stations/$STN/devices \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"type":"actual","kind":"linux","name":"my-agent"}' | jq .data.enrollment# { "endpoint": "wss://…/api/v1/relay/socket", "token": "osat_…" }kind 는 linux·mac·windows·chrome·android 중 기기에 가장 가까운 것을 골라요. 화면(screen)과 설치된 앱 목록(installed-apps)은 kind 로 열리므로, 그 기능을 쓰려면 해당 kind 의 플랫폼 도구 계약을 구현해요.
2. 접속과 핸드셰이크
섹션 제목: “2. 접속과 핸드셰이크”GET {endpoint} Upgrade: websocketSec-WebSocket-Protocol: endue-os.v1, endue-os.token.{토큰}에이전트가 먼저 접속해요 (기기에 공인 주소가 필요 없어요). 토큰은 Sec-WebSocket-Protocol 헤더에 실어요 (v1.2) — 릴레이는 101 에 endue-os.v1 만 돌려줘요. v1·v1.1 의 ?token={URL 인코딩한 토큰} 도 계속 받지만, 새로 만드는 에이전트는 헤더를 쓰세요. 토큰은 불투명한 문자열로 다루고 로그에 남기지 마세요.
에이전트 릴레이 │── ready {version, tools, os, arch, session_epoch, capabilities} ─▶│ │◀── relay_config {max_inline_bytes, blob_upload_url, max_concurrency} │ │◀── tool_request describe_tools {} ───────────────────────────────│ │── tool_response {output: {tools: […]}} ─────────────────────────────▶│ │◀── tool_request workspace_info {} ───────────────────────────────│ │── tool_response {output: {…}} ─────────────────────────────────────▶│ → 온라인session_epoch— 프로세스마다 새 UUID. 같은 프로세스가 다시 붙으면 릴레이가 진행 중이던 명령을 같은 프레임으로 다시 보내요.capabilities—concurrency:N(동시 실행 수),ping_interval:초,ack,progress,blob_upload.describe_tools의tools[]원소는name·description·input_schema(JSON Schema)·requires_confirmation. 비어 있으면 온라인이 아니에요.
3. 명령 받기
섹션 제목: “3. 명령 받기”{ "type": "tool_request", "id": "…", "command": "say_hello", "input": { "name": "endue" }, "timeout_ms": 30000 }받자마자 {"type":"tool_ack","id":"…"} (명령이 running 이 돼요), 끝나면:
{ "type": "tool_response", "id": "…", "status": "ok", "output": { "greeting": "안녕하세요" }, "error": null, "duration_ms": 3 }오류면 status: "error" 와 최상위 error_code 를 꼭 실어요.
지켜야 할 규칙
섹션 제목: “지켜야 할 규칙”| # | 규칙 |
|---|---|
| 1 | ping 은 바이트가 정확해야 해요 — 정확히 {"type":"ping"}. 릴레이 런타임이 서버를 깨우지 않고 자동으로 답해요. 가만히 있는 에이전트의 비용이 0 인 이유이자, 살아 있다는 유일한 근거예요 |
| 2 | 모르는 type·JSON 이 아닌 프레임·바이너리는 끊지 말고 버려요. 모르는 필드는 무시해요 |
| 3 | 응답은 id 로만 짝지어요. 순서는 상관없어요 |
| 4 | 같은 id 는 한 번만 실행해요. 같은 입력으로 다시 오면 같은 결과를, 다른 입력이면 request_id_conflict 를 돌려줘요 (timeout_ms 는 비교에서 빼요) |
| 5 | 취소(cancel_request)는 되돌리기가 아니에요. 취소된 요청에도 응답을 보내요 |
| 6 | 닫힘 4001 이거나 접속 때 401 이면 다시 붙지 마세요 — 토큰이 폐기·회전됐어요 |
| 7 | 재접속은 1초부터 두 배(권장 상한 60초) + 매번 0~50% 지터. 1012(같은 토큰의 다른 프로세스)면 1분 넘게 쉬어요 |
ping 주기를 지키지 않으면 명령을 받을 때 생존 확인에 걸려 4008 로 닫혀요. 주기는 capabilities 의 ping_interval 로 알리고, 없으면 kind 의 기본값(linux·mac 60초, chrome·android 20초)이에요.
큰 바이트
섹션 제목: “큰 바이트”출력에 넣을 바이트가 relay_config.max_inline_bytes(256 KiB)보다 크면 먼저 HTTPS 로 올리고 출력에는 참조만 실어요:
PUT {소켓과 같은 origin}/api/v1/relay/blobs/{request_id}/{name}Authorization: Bearer {연결 토큰}응답의 {name, mime_type, size, sha256} 를 {이름}_blob(예: image_blob)으로 실어 tool_response 를 보내요. 업로드가 끝난 뒤에 응답을 보내야 해요. 업로드 주소는 반드시 소켓과 같은 origin 이에요 — 토큰을 다른 곳으로 보내지 마세요.
시험하기
섹션 제목: “시험하기”플랫폼 없이 에이전트 하나만 시험하려면 endue-os agents 레포의 가짜 릴레이(scripts/fake-relay.mjs)를 써요. 실제 릴레이와 같은 핸드셰이크·기한·블롭 업로드를 하고, 에이전트의 프레임을 스키마로 검사해요. 프레임 예시(유효·무효 52개)는 spec 의 protocol/vectors.json 에 있어요.
전체 규약: 와이어 프로토콜