Norma Agent Harness 현재 시스템 문서
전체 문서
이 페이지 목차

소비하는 외부 operation 계약

Norma가 제공하는 inbound HTTP/RPC route는 0개다. 아래는 이 snapshot이 소비하는 outbound HTTP 또는 stdio JSON-RPC operation 전부다. URL의 /v1 표기는 실제 suffix를 뜻하며, BaseURL에 이미 잘못된 suffix가 있으면 중복될 수 있다.

Model HTTP

Model HTTP · 관계

이 대상을 사용하는 기능·모듈·계약 · 5개

전체 관계 5개 · 종류 선택·관계도
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를 닫아야 한다.

상위 영역: 라이브러리·이벤트·파일·설정 계약

전체로 돌아가기 · Markdown 원본

검색을 열면 색인을 읽습니다.

등록한 문서 본문에서 검색합니다.