[{"data":1,"prerenderedAt":528},["ShallowReactive",2],{"-docs-guide-external-agent":3,"-docs-guide-external-agent-surround":515},{"id":4,"title":5,"body":6,"description":505,"extension":506,"links":507,"meta":508,"navigation":509,"path":511,"seo":512,"stem":513,"__hash__":514},"docs\u002Fdocs\u002F2.guide\u002F11.external-agent.md","How to host an external execution agent",{"type":7,"value":8,"toc":496},"minimark",[9,13,18,40,44,47,120,127,130,181,187,191,214,218,221,235,238,242,310,313,421,428,432,458,462,479,492],[10,11,12],"p",{},"Use this guide when a third-party execution host must run TheSpider slices without the first-party Rust agent. The host remains thin: it executes the rendered prompt and returns raw process output. Parsing, scrubbing, fingerprinting, deduplication, and persistence remain server-side.",[14,15,17],"h2",{"id":16},"prerequisites","Prerequisites",[19,20,21,25,28,31],"ul",{},[22,23,24],"li",{},"A project-scoped bearer token.",[22,26,27],{},"The project identifier and model codes enabled for that project.",[22,29,30],{},"A checkout containing the campaign's target commit.",[22,32,33,34,39],{},"A runner that follows the ",[35,36,38],"a",{"href":37},"\u002Fdocs\u002Freference\u002Frunner-contract","runner contract",".",[14,41,43],{"id":42},"claim-work","Claim work",[10,45,46],{},"Send a claim request:",[48,49,54],"pre",{"className":50,"code":51,"language":52,"meta":53,"style":53},"language-http shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","POST \u002Fv1\u002Fcampaigns\u002F42\u002Fclaim\nAuthorization: Bearer TOKEN\nContent-Type: application\u002Fjson\n\n{\n  \"client_id\": \"runner-east-1\",\n  \"models\": [\"sonnet\"],\n  \"max_slices\": 2,\n  \"lease_seconds\": 3600\n}\n","http","",[55,56,57,65,71,77,84,90,96,102,108,114],"code",{"__ignoreMap":53},[58,59,62],"span",{"class":60,"line":61},"line",1,[58,63,64],{},"POST \u002Fv1\u002Fcampaigns\u002F42\u002Fclaim\n",[58,66,68],{"class":60,"line":67},2,[58,69,70],{},"Authorization: Bearer TOKEN\n",[58,72,74],{"class":60,"line":73},3,[58,75,76],{},"Content-Type: application\u002Fjson\n",[58,78,80],{"class":60,"line":79},4,[58,81,83],{"emptyLinePlaceholder":82},true,"\n",[58,85,87],{"class":60,"line":86},5,[58,88,89],{},"{\n",[58,91,93],{"class":60,"line":92},6,[58,94,95],{},"  \"client_id\": \"runner-east-1\",\n",[58,97,99],{"class":60,"line":98},7,[58,100,101],{},"  \"models\": [\"sonnet\"],\n",[58,103,105],{"class":60,"line":104},8,[58,106,107],{},"  \"max_slices\": 2,\n",[58,109,111],{"class":60,"line":110},9,[58,112,113],{},"  \"lease_seconds\": 3600\n",[58,115,117],{"class":60,"line":116},10,[58,118,119],{},"}\n",[10,121,122,123,126],{},"A successful response contains ",[55,124,125],{},"leases[]",". An empty array means no matching slice is claimable.",[10,128,129],{},"Persist these fields until submission:",[19,131,132,137,142,147,156,161,166,171,176],{},[22,133,134],{},[55,135,136],{},"lease_id",[22,138,139],{},[55,140,141],{},"slice_id",[22,143,144],{},[55,145,146],{},"model_code",[22,148,149,152,153],{},[55,150,151],{},"pass_code"," and ",[55,154,155],{},"bucket_code",[22,157,158],{},[55,159,160],{},"prompt",[22,162,163],{},[55,164,165],{},"prompt_hash",[22,167,168],{},[55,169,170],{},"marker",[22,172,173],{},[55,174,175],{},"timeout_seconds",[22,177,178],{},[55,179,180],{},"lease_expires_at",[10,182,183,184,186],{},"Do not reconstruct the prompt. Write the exact UTF-8 ",[55,185,160],{}," bytes to the runner input.",[14,188,190],{"id":189},"execute-the-runner","Execute the runner",[192,193,194,199,202,208,211],"ol",{},[22,195,196,197,39],{},"Verify that SHA-256 of the prompt bytes equals ",[55,198,165],{},[22,200,201],{},"Run the model in the checkout for the campaign target commit.",[22,203,204,205,207],{},"Enforce the smaller of your host policy and the lease's ",[55,206,175],{}," unless your claim requested a longer lease for a known model timeout.",[22,209,210],{},"Capture stdout, process exit code, duration, and a bounded stderr tail.",[22,212,213],{},"Keep model-derived output opaque. Do not parse or rewrite findings on the host.",[14,215,217],{"id":216},"extend-a-live-lease","Extend a live lease",[10,219,220],{},"While the process runs, send best-effort heartbeats:",[48,222,224],{"className":50,"code":223,"language":52,"meta":53,"style":53},"POST \u002Fv1\u002Fleases\u002F9001\u002Fheartbeat\nAuthorization: Bearer TOKEN\n",[55,225,226,231],{"__ignoreMap":53},[58,227,228],{"class":60,"line":61},[58,229,230],{},"POST \u002Fv1\u002Fleases\u002F9001\u002Fheartbeat\n",[58,232,233],{"class":60,"line":67},[58,234,70],{},[10,236,237],{},"A heartbeat extends an active lease and returns its new expiry. Heartbeat failure does not require stopping the model. Lease expiry requeues the slice, but a late result can still win if no other lease completed it.",[14,239,241],{"id":240},"submit-raw-results","Submit raw results",[48,243,245],{"className":50,"code":244,"language":52,"meta":53,"style":53},"POST \u002Fv1\u002Fleases\u002F9001\u002Fresults\nAuthorization: Bearer TOKEN\nContent-Type: application\u002Fjson\n\n{\n  \"exit_code\": 0,\n  \"duration_ms\": 182345,\n  \"model_code\": \"sonnet\",\n  \"client_version\": \"external-host\u002F1.0.0\",\n  \"prompt_hash\": \"HASH_FROM_CLAIM\",\n  \"stdout\": \"RAW_PROCESS_STDOUT\",\n  \"stderr_tail\": \"LAST_4000_CHARACTERS\"\n}\n",[55,246,247,252,256,260,264,268,273,278,283,288,293,299,305],{"__ignoreMap":53},[58,248,249],{"class":60,"line":61},[58,250,251],{},"POST \u002Fv1\u002Fleases\u002F9001\u002Fresults\n",[58,253,254],{"class":60,"line":67},[58,255,70],{},[58,257,258],{"class":60,"line":73},[58,259,76],{},[58,261,262],{"class":60,"line":79},[58,263,83],{"emptyLinePlaceholder":82},[58,265,266],{"class":60,"line":86},[58,267,89],{},[58,269,270],{"class":60,"line":92},[58,271,272],{},"  \"exit_code\": 0,\n",[58,274,275],{"class":60,"line":98},[58,276,277],{},"  \"duration_ms\": 182345,\n",[58,279,280],{"class":60,"line":104},[58,281,282],{},"  \"model_code\": \"sonnet\",\n",[58,284,285],{"class":60,"line":110},[58,286,287],{},"  \"client_version\": \"external-host\u002F1.0.0\",\n",[58,289,290],{"class":60,"line":116},[58,291,292],{},"  \"prompt_hash\": \"HASH_FROM_CLAIM\",\n",[58,294,296],{"class":60,"line":295},11,[58,297,298],{},"  \"stdout\": \"RAW_PROCESS_STDOUT\",\n",[58,300,302],{"class":60,"line":301},12,[58,303,304],{},"  \"stderr_tail\": \"LAST_4000_CHARACTERS\"\n",[58,306,308],{"class":60,"line":307},13,[58,309,119],{},[10,311,312],{},"Interpret the response by status:",[314,315,316,332],"table",{},[317,318,319],"thead",{},[320,321,322,326,329],"tr",{},[323,324,325],"th",{},"Status",[323,327,328],{},"Meaning",[323,330,331],{},"Action",[333,334,335,355,368,383,396,409],"tbody",{},[320,336,337,343,346],{},[338,339,340],"td",{},[55,341,342],{},"200",[338,344,345],{},"This lease won and the result was imported",[338,347,348,349,152,352],{},"Record ",[55,350,351],{},"accepted",[55,353,354],{},"parsed_report_count",[320,356,357,362,365],{},[338,358,359],{},[55,360,361],{},"202",[338,363,364],{},"Another lease won; this output was retained as a superseded artifact",[338,366,367],{},"Stop retrying this result",[320,369,370,375,380],{},[338,371,372],{},[55,373,374],{},"409",[338,376,377,379],{},[55,378,165],{}," does not match",[338,381,382],{},"Discard the result and claim again",[320,384,385,390,393],{},[338,386,387],{},[55,388,389],{},"413",[338,391,392],{},"An artifact exceeds the server size limit",[338,394,395],{},"Reduce output, then claim and rerun",[320,397,398,403,406],{},[338,399,400],{},[55,401,402],{},"429",[338,404,405],{},"Rate limited",[338,407,408],{},"Retry with exponential backoff and jitter",[320,410,411,416,419],{},[338,412,413],{},[55,414,415],{},"5xx",[338,417,418],{},"Server failure",[338,420,408],{},[10,422,423,424,427],{},"Results are idempotent on ",[55,425,426],{},"(lease_id, prompt_hash)",". Retrying the same accepted submission does not ingest reports twice.",[14,429,431],{"id":430},"recover-after-host-failure","Recover after host failure",[19,433,434,437,440,443,446],{},[22,435,436],{},"Before execution, persist the lease payload and runner process identifier.",[22,438,439],{},"After a process finishes, persist the result body before sending it.",[22,441,442],{},"On restart, submit an unsent result even if the lease has expired.",[22,444,445],{},"If no result exists, do not fabricate one. Let the lease expire and requeue.",[22,447,448,449,451,452,454,455,457],{},"Stop retrying after ",[55,450,361],{},", ",[55,453,374],{},", or ",[55,456,389],{}," until a new claim or rerun changes the payload.",[14,459,461],{"id":460},"protect-the-boundary","Protect the boundary",[19,463,464,467,470,473,476],{},[22,465,466],{},"Store the bearer token outside command output and model prompts.",[22,468,469],{},"Scope tokens to one project when possible.",[22,471,472],{},"Use HTTPS.",[22,474,475],{},"Do not upload the source tree to TheSpider. The host may separately invoke a cloud model provider; review that provider's data policy.",[22,477,478],{},"Treat prompt and model output as sensitive tenant data.",[10,480,481,482,486,487,491],{},"See the ",[35,483,485],{"href":484},"\u002Fdocs\u002Freference\u002Fapi","API reference"," for the canonical route summary and ",[35,488,490],{"href":489},"\u002Fdocs\u002Fguide\u002Ftroubleshooting","runner troubleshooting"," for failure diagnosis.",[493,494,495],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":53,"searchDepth":67,"depth":67,"links":497},[498,499,500,501,502,503,504],{"id":16,"depth":67,"text":17},{"id":42,"depth":67,"text":43},{"id":189,"depth":67,"text":190},{"id":216,"depth":67,"text":217},{"id":240,"depth":67,"text":241},{"id":430,"depth":67,"text":431},{"id":460,"depth":67,"text":461},"Implement the claim, lease, heartbeat, and result lifecycle on a third-party runner host","md",null,{},{"icon":510},"i-lucide-plug-zap","\u002Fdocs\u002Fguide\u002Fexternal-agent",{"title":5,"description":505},"docs\u002F2.guide\u002F11.external-agent","27EeatE4c7_4WBTuvsIgpz5q1S1F51_23ry70aNQsQQ",[516,522],{"title":517,"path":518,"stem":519,"description":520,"icon":521,"children":-1},"How to run campaign workflows","\u002Fdocs\u002Fguide\u002Faudit-workflows","docs\u002F2.guide\u002F10.audit-workflows","Choose full, incremental, baseline, and retest campaigns and establish an audit cadence","i-lucide-git-compare-arrows",{"title":523,"path":524,"stem":525,"description":526,"icon":527,"children":-1},"The agent","\u002Fdocs\u002Fguide\u002Fagent","docs\u002F2.guide\u002F2.agent","Run the TheSpider Rust agent to claim slices, run models, and gate PRs","i-lucide-terminal",1784724165141]