전체 문서
이 페이지 목차
소비하는 외부 operation 계약
Norma가 제공하는 inbound HTTP/RPC route는 0개다. 아래는 이 snapshot이 소비하는 outbound HTTP 또는 stdio JSON-RPC operation 전부다. URL의 /v1 표기는 실제 suffix를 뜻하며, BaseURL에 이미 잘못된 suffix가 있으면 중복될 수 있다.
Model HTTP
Model HTTP · 관계
이 대상을 사용하는 기능·모듈·계약 · 5개
- 계약 사용 · Provider, extension과 coordination · built-in provider가 외부 model HTTP operation을 소비 · llm-anthropic · llm-openai-chat · llm-openai-responses
- 계약 사용 · llm · llm adapters가 세 wire protocol operation을 소비 · llm-anthropic · llm-openai-chat · llm-openai-responses
- 계약 사용 · tool, permission, hook · network tools와 Grep bootstrap이 외부 HTTP/npm operation을 소비 · webfetch-runner · websearch-operations · ripgrep-bootstrap
- 계약 사용 · mcp, skill, plan · MCP client가 stdio JSON-RPC handshake, list와 call을 소비 · mcp-handshake · mcp-tools · mcp-call · mcp-stdio
- 계약 사용 · 통합과 운영 경계 · 운영 통합에서 outbound transport, proxy와 shutdown 경계를 확인 · llm-provider · webfetch-runner · mcp-stdio · ripgrep-bootstrap
| operation | auth | 주요 input | result·fidelity | retry | timeout·failure |
|---|---|---|---|---|---|
Anthropic POST {BaseURL}/v1/messages |
x-api-key, anthropic-version; JSON content type |
model, segmented system/cache control, normalized messages, tools, max tokens(0→8192), temperature/stop/thinking/effort, stream | SSE 또는 JSON을 text/thinking/signature/tool-use/stop/usage로 정규화; adjacent same-role merge와 thinking filter가 wire shape를 바꿈 | 공통 establishment: 기본 3회, network/408/429/500/502/503/504, 0.5/1/2s…; mid-stream retry 없음 | SDK 자체 request deadline 없음; caller context/custom HTTPClient가 상한. non-200 body를 error에 포함 |
OpenAI Chat POST {BaseURL}/chat/completions |
Authorization: Bearer |
model, role messages/tool_calls, tools, 정확히 하나의 max token key, temperature/stop/thinking/reasoning, stream | SSE/JSON을 neutral blocks로 변환; tool pair 정리, 빈 assistant placeholder, provider-specific reasoning replay 때문에 byte fidelity 없음 | 공통 establishment + 정상 stop인데 content가 전혀 없으면 기본 2회 전체 prompt 재요청 | SDK 자체 deadline 없음; mid-stream drop은 error. 200 error object도 parser에서 error |
OpenAI Responses POST {BaseURL}/responses |
Authorization: Bearer |
model, instructions, flat message/function items, tools, max_output_tokens, reasoning, store:false, stream |
named SSE/JSON output을 neutral blocks로 변환; full history 매번 전송, thinking replay는 버림; previous_response_id 사용 안 함 |
공통 establishment만; Chat의 empty-response retry 없음 | SDK 자체 deadline 없음; unknown SSE events는 건너뜀 |
공통 rate limiter는 한 provider 객체의 logical request 시작 전에 한 번 적용되고 retry를 추가 count하지 않는다. built-in LLM client는 Config.Proxy가 비면 Go http.ProxyFromEnvironment를 명시해 HTTP_PROXY·HTTPS_PROXY·NO_PROXY와 각각의 lowercase alias를 읽는다. REQUEST_METHOD가 설정된 CGI 환경에서는 HTTP URL에 환경 proxy를 쓰지 않고 오류를 반환하는 표준 라이브러리 예외가 적용된다. custom HTTPClient를 host가 주면 Config.Proxy와 이 built-in transport 설정은 모두 무시되고 timeout도 그 client가 소유한다. transport 근거 Anthropic 근거 Chat 근거 Responses 근거 retry 근거
WebFetch HTTP
| operation | auth/input | result·fidelity | retry/redirect | timeout·failure |
|---|---|---|---|---|
GET <model-supplied http(s) URL> |
SDK auth header 없음; User-Agent: norma/0.4; input url, optional extract |
status, selected headers와 body 최대 2 MiB를 읽고 전체 결과 50k chars로 cap. HTML은 Markdown으로 재렌더; extract는 comment/script/form/link/meta 신호를 덧붙임. non-HTML은 bytes→string |
http.Client 기본 redirect 정책으로 최대 10 hop; hop별 scope/auth 정책 없음. configured proxy에서 proxy-like transport error면 direct-intended client로 1회 fallback; HTTP status 자체는 retry 조건 아님 |
runner context 30s. static asset suffix와 non-http(s)는 call 전 error result; non-2xx도 body와 함께 성공 tool result 형태 |
redirect 대상과 fallback traffic은 permission callback이 처음 본 URL과 다를 수 있다. proxy가 capture/scope 강제점이면 fallback은 그 경계를 이탈할 수 있다. 다만 fallback이 InsecureTLS=false이면 nil Transport의 http.Client{}를 만들어 다시 http.DefaultTransport와 proxy environment를 상속하므로 실제 direct 연결도 보장하지 않는다. WebFetch 근거
WebFetch와 네 search backend의 newFetchClient는 built-in LLM transport와 다른 조건 분기를 쓴다. Proxy, CACert, InsecureTLS가 모두 zero면 nil Transport의 http.Client{}이므로 http.DefaultTransport를 통해 HTTP_PROXY·HTTPS_PROXY·NO_PROXY와 lowercase alias를 상속한다. 세 값 중 하나라도 non-zero면 새 http.Transport{}를 만들며, 이때 explicit Proxy가 없으면 ProxyFromEnvironment를 설정하지 않아 direct가 된다. built-in LLM client는 반대로 custom HTTPClient가 없는 한 빈 Config.Proxy에서 항상 cloned default transport에 ProxyFromEnvironment를 명시한다. 공유 client 근거 WebSearch caller 근거
WebSearch provider HTTP
| backend/operation | auth·input | result·fidelity | retry | timeout·failure |
|---|---|---|---|---|
DDG POST https://html.duckduckgo.com/html/ |
key 없음; form q, kl=wt-wt; browser UA/Referer |
HTML title/link/snippet를 parse, DDG redirect URL의 uddg 해제; markup 의존 |
없음 | runner 30s; 202/429는 rate-limit error, non-200 error; body 2 MiB cap |
Brave GET https://api.search.brave.com/res/v1/web/search?q=&count= |
X-Subscription-Token; count≤20 |
JSON title/url/description을 순서대로 변환 | 없음 | runner 30s; HTTP 200만 성공; body 2 MiB cap |
Tavily POST https://api.tavily.com/search |
JSON body에 api_key, query, max_results≤20, search_depth:basic |
JSON title/url/content를 description으로 변환 | 없음 | runner 30s; HTTP 200만 성공; body 2 MiB cap |
DeepSeek POST <normalized-base>/v1/messages |
x-api-key, Anthropic version; model, search instruction, server tool web_search_20250305, max uses 1..3 |
SSE의 web_search_tool_result URL/title만 보존; encrypted content/snippet은 버림, URL 중복 제거 |
없음 | runner 120s; streaming만 사용, body stream 2 MiB cap; tool error가 모든 hit을 없앨 때 error |
web_search call은 기본 5, 최대 20 results를 요청한다. WebSearchProbe는 같은 backend를 직접 부르지만 agent schema/permission/hook pipeline을 거치지 않는다. search 근거
MCP stdio JSON-RPC 2.0
newline-delimited stdio transport이며 HTTP operation이 아니다. protocol-level auth는 없고 host가 child command와 optional environment를 정한다.
| 순서/method | id | input | result·fidelity | retry/timeout/failure |
|---|---|---|---|---|
initialize |
증가 integer | protocol 2024-11-05, empty capabilities, clientInfo norma/0.2 |
result body는 성공 여부 외에 사용하지 않음; server-negotiated version/capabilities를 저장하지 않음 | retry 없음; caller context 또는 connection close까지 |
notifications/initialized |
없음 | empty object | response를 기다리지 않는 notification | write error만 반환 |
tools/list |
증가 integer | empty object | name/description/inputSchema를 mcp__server__name CoreTool로 snapshot |
retry/refresh 없음; invalid JSON result는 error |
tools/call |
증가 integer | original remote name와 arguments | content[].text를 단순 연결하고 isError 보존; non-text/resource/image/annotations/structured result는 보존하지 않음 |
retry 없음; RPC error는 error tool result; caller context/connection close까지 |
reader는 최대 8 MiB line을 받고 JSON parse 불가 frame, notification, id 0 response를 건너뛴다. context timeout은 호출자가 제공해야 하며 subprocess lifecycle은 Client.Close가 소유한다. MCP handshake 근거 MCP tool 근거 stdio 근거
Grep의 숨은 ripgrep bootstrap
Grep은 검색 전 RipgrepPath를 부른다. 이 resolution은 검색 metadata 밖에서 다음 순서로 process-wide 한 번 실행된다. host가 미리 EnsureRipgrep을 호출해도 같은 경로다. 구현 근거
| 단계 | trigger·operation | 결과와 실패 경계 |
|---|---|---|
| 1 | NORMA_DISABLE_RIPGREP가 non-empty |
즉시 pure-Go backend; sync.Once를 소비하지 않음 |
| 2 | rg가 PATH에 존재 |
그 executable을 사용; install 없음 |
| 3 | rg 없음 + NORMA_RIPGREP_NO_INSTALL가 non-empty |
install 없이 pure-Go fallback |
| 4 | 위 조건 모두 아님 + npm 존재 |
exact npm install -g @vscode/ripgrep; process 사용자 권한, npm global prefix·registry·proxy 설정으로 network와 전역 filesystem을 변경할 수 있음 |
| 5 | install 뒤 lookup | PATH의 rg, 이어 npm root -g 아래 package binary를 찾고, 어느 단계든 실패하면 오류를 노출하지 않고 pure-Go fallback |
exec.Command를 context 없이 사용하므로 bootstrap 자체 timeout/cancel 상한이 없다. npm 부재, network·권한·install·lookup 실패는 모두 정상 fallback처럼 보인다. 따라서 Grep의 RO=Y, C=Y, self permission allow와 실제 최초 호출 효과가 일치하지 않으며 중앙 permission callback도 install을 보지 못한다. 승인된 운영에서는 image에 rg를 미리 넣거나 NORMA_DISABLE_RIPGREP=1/NORMA_RIPGREP_NO_INSTALL=1로 이 side effect를 닫아야 한다.