Hoppa till innehåll

Livscykel för agentdispatch

En dispatch är en körning av en agent, i ett samtal, på en adapter. Allt som rör den sker på cc_server: klienten skickar ett meddelande och tittar sedan på när händelser strömmar tillbaka. Det finns ingen dispatchväg i någon klient.

Agenter väcks av sex anledningar, som körningen bär med sig som sin väckningskontext så att agenten vet varför den är vaken:

Väckningsorsak Utlösare
userMessage Någon skickade ett meddelande i en yta, eller nämnde agenten
assignment Ett ärende tilldelades agenten
pipelineStep Ett pipelinesteg dispatchade den
childCompleted Ett underärende slutfördes
followUp En schemalagd uppföljningspuls
recovery Återställningsvägen dispatchade om efter ett fel

Väckningskontexten bär de id:n som hör till orsaken — ärende, yta, meddelande, pipelinekörning — och de injiceras i agentens miljö och prompt.

Prompten byggs upp i lager, där varje lager är en märkt sektion:

  • Identitet — agentens eget id, namn och arbetsyte-id, med en uttrycklig instruktion att skicka exakt dessa UUID:n till verktyg som är begränsade till arbetsytan. Arbetsytan är inneboende i agenten, så den finns alltid med.
  • Verktygskatalog och resursprotokoll, därefter arbetsytans layout.
  • Åsidosättning av systemprompt, om agenten har en, därefter persona (parametriserad efter roll) och teambriefen.
  • Skills — namnen på agenten.
  • Exekveringskontrakt och procedur för det aktuella läget.
  • Väckningskontext och omnämnandekontext: vem som kallade på agenten och närvarolistan runt den.
  • Lägesblock — genererat från lägets kapacitetsprofil och dess materialiserade verktygslista, så att en prompt aldrig kan namnge ett verktyg som körningen faktiskt inte har.
  • Minneskontext — fakta som hämtats för uppgiften.
  • Samtalskontext — den senaste ythistoriken, begränsad till 50 000 tecken ordagrant fönster oavsett agentens konfigurerade kontextstorlek. Äldre historik trunkeras inte bort: agenten hämtar den vid behov med get_messages, och äldre meddelanden med semantisk relevans dyker fortfarande upp.

Kommandon hanteras på två olika ställen. /todo och /compact fångas upp i meddelandefältet och sparas medvetet aldrig som meddelanden och skickas aldrig till agenten — att spara /compact skulle flytta komprimeringssnittet en tur bakåt och förorena transkriptet. /plan hanteras på båda ställena: meddelandefältet växlar ytans läge, vilket är det som gör att verktygsytan, räckesförinställningen och prompten stämmer överens för den här turen och varje tur därefter. /goal, /loop och /skill:<name> hanteras inuti körningen själv och bara i den inbyggda harnessen — /goal och /loop startar autonomt arbete med en kostnadsbudget, och ett namn med prefixet skill: slås upp som en skill vars SKILL.md-innehåll blir turens direktiv. Namnrymden är det som håller de två vokabulärerna åtskilda: att matcha ett bart namn mot skills innebar att en skill som hette plan eller compact permanent skuggades av det inbyggda kommandot. Ett bart namn som inte är inbyggt matchas fortfarande, för meddelanden som skrevs innan namnrymden fanns. Ett okänt namn faller helt enkelt igenom som vanlig text.

Innan något körs får samtalet sin egen arbetsrot, med en isolerad copy-on-write-worktree per repo under den. Som standard är det varje repo som är länkat till arbetsytan; en PR-yta — eller en yta som skapats med ett uttryckligt urval — etablerar bara de tillåtna, och ett PR-granskningssamtal låser sitt enda repo till pull requestens head-ref medan de övriga ligger kvar på sin standardgren. Källkodsutcheckningen ändras aldrig, och hela samtalskatalogen återtas när ytan tas bort.

Copy-on-write är den enda implementationen överallt där rift levereras, med den medföljande nativa binären som grund. Ett filsystem som inte kan reflinka — eller en datakatalog på en annan volym än repot — gör att etableringen misslyckas i stället för att falla tillbaka. git worktree är backend på Windows, där det inte finns någon copy-on-write-backend att bygga, och det är inte en tyst degradering på andra värdar. En saknad rift på en plattform som levererar den är en trasig installation — servern vägrar starta i stället för att tyst falla tillbaka, eftersom en tyst återgång gömmer felet bakom en långsammare väg för alltid.

Två identiteter fastställs per körning, innan någon process startas.

Git-författarskap. Commits som en agent gör är författade som agenten — <Name> (agent) <agentId@agents.control-center.local> i både GIT_AUTHOR_* och GIT_COMMITTER_* — aldrig genom att utge sig för att vara en människa. Den lokala delen är agentens UUID, inte dess namnslug. Den människa som begärde körningen krediteras med en Co-Authored-By:-trailer, som finns med i miljön och instrueras om i systemprompten. En uttrycklig identitet satt av anroparen vinner alltid över båda.

Forge-identitet. Körningens GitHub-uppgifter är alltid bara brokerns — en appinstallationstoken begränsad till repot, eller dess PAT-reservväg via miljön — aldrig den begärande medlemmens egen sparade token. actingUserId begränsar PAT-reservvägen så att en medlem som inte äger servern inte ärver serverns räckvidd; den lägger inte en personlig token ovanpå agentmiljön.

Uppgifter för modellleverantörer fastställs i en fast ordning, och ordningen spelar roll:

  1. ett miljöåsidosättande per adapter,
  2. anroparens miljö,
  3. serverns uppgiftslager (där en OAuth-token förnyas om den närmar sig utgång),
  4. processens miljö.

Eftersom åsidosättandet per adapter kommer först slår det tyst ett leverantörskonto som är anslutet i appen. Om en körning debiterar fel konto är det åsidosättandet det första du bör titta på. Leverantörsuppgifter är globala för värden — delade mellan alla arbetsytor i installationen.

Körningens GitHub-token kommer från uppgiftsbrokern: en repo-avgränsad token för worktreens repo som bara kan läsa. En push går genom agentkörningsgatewayen, som frågar regeln Pusha till en remote innan pushen skickas vidare, och ingen ärendenyckel injiceras. Se Sandlåda och säkerhet.

Körningen startas med sin worktree som arbetskatalog och — för Claude Code — en genererad MCP-konfiguration så att agenten kan nå Control Centers verktygsyta. ACP och harnessens bash får en rensad miljö; Claude Code via NoSandboxAdapter ärver värdens miljö.

Körningen strömmar tillbaka en typad händelsesekvens, som strömprocessorn sparar som meddelandedeltan, fogar samman till transkriptssegment och omvandlar till meddelandehändelser:

Händelse Vad den innehåller
thinking Resonemangsutdata, renderad direkt i texten
text Synlig utdata
toolCall Ett verktygsanrop, med sitt id och strukturerade argument
toolResult Det matchande resultatet, flaggat när det innehåller ett fel eller är en partiell ström
usage Tokenantal och kostnad för ett mätfönster
error Ett fel, med adapterns strukturerade kod när den rapporterade en
sandboxViolation Operativsystemet nekade en systemkallning. Skickas bara där en sandlåda faktiskt omsluter processen — se noten ovan
debug Körningsdiagnostik (startmarkörer, räckesbeslut, kontraktspåminnelser)
done Körningen är klar

Det finns medvetet inget tak för antalet turer någonstans. Ett hårt iterationsantal dödar legitimt långt arbete (migreringar, städning efter fan-out) och fångar inget som skydden nedan fångar tidigare. En körning tar slut när:

  • modellen stannar av sig själv;
  • en människa stoppar den, eller tar över samtalet (vilket pausar den inbyggda harnessen vid nästa rena turbrytpunkt — en Claude Code-körning, som inte har någon säker brytpunkt, stoppas i stället);
  • tystnadsvakten utlöses. Ingen utdata under tröskelvärdet och sessionen avslutas med ett silentRun-fel. Standardvärdet är 15 minuter, som sjunker till 10 i de läsdominerade lägena (review och plan); en agent kan åsidosätta det mellan 1 och 240 minuter. Vakten aktiveras när en barnprocess rapporterar sitt PID, så den täcker Claude Code-CLI:t — inte den inbyggda harnessen, som inte startar några processer;
  • ett kostnadstak slår till — men bara för autonoma kommandon.

Doom-loop-skyddet är ingen terminator: tre identiska verktygsanrop i rad och loopen styr agenten att byta angreppssätt.

Sammantaget har en vanlig interaktiv tur i den inbyggda harnessen inget tak för antalet turer, inget kostnadstak och ingen tystnadsvakt. Den tar slut när modellen stannar eller när du stoppar den. Det är en medveten avvägning — alternativen dödar allt legitimt långt arbete — men det betyder att en obevakad interaktiv körning inte begränsas av något annat än modellens egen bedömning.

Under en körning köas ett meddelande som skickas till en körande agent som styrning och töms vid nästa turbrytpunkt, så du kan knuffa en körning utan att döda den. Styrning, pausning, subagenter och slutförandekontraktet är funktioner i den inbyggda harnessen; Claude Code har inga av dem.

Varje dispatch skriver en AgentRunLog: start- och sluttid, sammanfattning, tokenantal och uppskattad kostnad (inklusive kostnad som rullats upp från subagenter), adapter och modell, process-id och loggsökväg, samtalet och eventuellt ärende eller pipelinekörning som den tillhör, plus två klassificeringsfält som bär det mesta av sanningen.

  • Status är grov: pending, running, completed, error.
  • Livsstatus är den finare läsningen: alive, productive, completed, blocked, empty, looping, failed, stalled, dead.
  • Felfamilj grupperar fel på ett användbart sätt: transientUpstream, sandboxInfrastructure, budgetExceeded, processLost, silentRun, unknown.

Att stänga körningen publicerar AgentRunCompleted, och mycket hänger på den: upprullning av pipelinekostnad, återupptagning av ett pipelinesteg som väntade på körningen, slutförande av det bakomliggande ärendet, mappning av en orkestrering till ett terminaltillstånd, målövervakarens beslut att fortsätta eller stoppa, kontrollantrollen och budgetutvärderingen efter körningen. Närvarolistan följer separat, genom att titta på de senaste körningsloggarna i stället för på händelsen. Aviseringen ”Agenten är klar” finns inte med i den listan: händelsen utlöses när processens ström tar slut, innan svaret har skrivits, så aviseringen är turens MessageReceived, som publiceras när meddelandet har sparats.

Budgetupprätthållande är en reaktion, inte en grind. När en körning slutförs jämför utvärderaren agentens hittillsvarande månadsspendering mot dess effektiva tak (en länkad budgetpolicy om det finns en, annars agentens egen månadssiffra; noll betyder obegränsat). Att passera den mjuka tröskeln registrerar en varningsincident; att förbruka budgeten registrerar en hård incident och flippar agentens livscykelstatus till paused i organisationsschemat. Dispatch tar fortfarande emot arbete från den agenten. En pipelinetrigger för BudgetThresholdCrossed kan starta uppföljningsarbete från samma händelse.

Varje körningslogg kan bära en omförsökskedja — ett överordnat körnings-id, en försöksräknare och en fortsättningssammanfattning. Samtalets körningsträd visar försöksnumret när det inte är noll. Ett “försök igen” från chatten är en ny dispatch med en ny körningslogg.