Get started

Refactor van de ACP-levenscyclus

De ACP-levenscyclus werkt momenteel, maar te veel ervan wordt achteraf afgeleid. Procesopschoning reconstrueert eigenaarschap op basis van PID's, commandoreeksen, wrapper- paden en de live procestabel. Sessiezichtbaarheid reconstrueert eigenaarschap op basis van sessiesleutelreeksen plus secundaire sessions.list({ spawnedBy })-lookups. Dat maakt gerichte fixes mogelijk, maar zorgt er ook voor dat randgevallen gemakkelijk worden gemist: PID-hergebruik, commando's met aanhalingstekens, kleinkinderen van adapters, statusroots met meerdere Gateways, cancel versus close, en tree versus all-zichtbaarheid worden allemaal afzonderlijke plekken om dezelfde eigendomsregels opnieuw te ontdekken.

Deze refactor maakt eigenaarschap eersteklas. Het doel is geen nieuw ACP-productoppervlak; het is een veiliger intern contract voor het bestaande ACP- en ACPX-gedrag.

Doelen

  • Opschoning stuurt nooit een signaal naar een proces tenzij huidig live bewijs overeenkomt met een lease die eigendom is van OpenClaw.
  • cancel, close en opruimen bij opstarten hebben afzonderlijke levenscyclusintenties.
  • sessions_list, sessions_history, sessions_send en statuscontroles gebruiken hetzelfde sessiemodel dat eigendom is van de aanvrager.
  • Installaties met meerdere Gateways kunnen elkaars ACPX-wrappers niet opruimen.
  • Oude ACPX-sessierecords blijven werken tijdens migratie.
  • De runtime blijft eigendom van de Plugin; de kern leert geen ACPX-pakketdetails.

Niet-doelen

  • ACPX vervangen of het openbare /acp-commando-oppervlak wijzigen.
  • Leveranciersspecifiek ACP-adaptergedrag naar de kern verplaatsen.
  • Vereisen dat gebruikers handmatig status opschonen voordat ze upgraden.
  • Ervoor zorgen dat cancel herbruikbare ACP-sessies sluit.

Doelmodel

Gateway-instantie-identiteit

Elk Gateway-proces zou een stabiele runtime-instantie-id moeten hebben:

type GatewayInstanceId = string;

Die kan worden gegenereerd bij het opstarten van de Gateway en worden bewaard in de status voor de levensduur van die installatie. Het is geen beveiligingsgeheim; het is een eigendomsdiscriminator die wordt gebruikt om te voorkomen dat ACP-processen van de ene Gateway worden verward met processen van een andere Gateway.

ACP-sessie-eigenaarschap

Elke gespawnde ACP-sessie zou genormaliseerde eigendomsmetadata moeten hebben:

type AcpSessionOwner = {
  sessionKey: string;
  spawnedBy?: string;
  parentSessionKey?: string;
  ownerSessionKey: string;
  agentId: string;
  backend: "acpx";
  gatewayInstanceId: GatewayInstanceId;
  createdAt: number;
};

De Gateway zou deze velden moeten retourneren op sessierijen waar ze bekend zijn. Zichtbaarheidsfiltering zou een pure controle over rijmetadata moeten zijn:

canSeeSessionRow({
  row,
  requesterSessionKey,
  visibility,
  a2aPolicy,
});

Dat verwijdert verborgen secundaire sessions.list({ spawnedBy })-aanroepen uit zichtbaarheidscontroles. Een gespawnd cross-agent ACP-kind is eigendom van de aanvrager omdat de rij dat zegt, niet omdat een tweede query het toevallig vindt.

ACPX-procesleases

Elke gegenereerde wrapperstart zou een leaserecord moeten maken:

type AcpxProcessLease = {
  leaseId: string;
  gatewayInstanceId: GatewayInstanceId;
  sessionKey: string;
  wrapperRoot: string;
  wrapperPath: string;
  rootPid: number;
  processGroupId?: number;
  commandHash: string;
  startedAt: number;
  state: "open" | "closing" | "closed" | "lost";
};

Het wrapperproces zou de lease-id en Gateway-instantie-id in zijn omgeving moeten ontvangen:

OPENCLAW_ACPX_LEASE_ID=...
OPENCLAW_GATEWAY_INSTANCE_ID=...

Wanneer het platform dit toestaat, zou verificatie de voorkeur moeten geven aan live procesmetadata die niet door commando-aanhalingstekens kunnen worden verward:

  • root-PID bestaat nog
  • live wrapperpad valt onder wrapperRoot
  • procesgroep komt overeen met de lease wanneer beschikbaar
  • omgeving bevat de verwachte lease-id wanneer leesbaar
  • commandohash of uitvoerbaar pad komt overeen met de lease

Als het live proces niet kan worden geverifieerd, faalt opschoning gesloten.

Levenscycluscontroller

Introduceer één ACPX-levenscycluscontroller die procesleases en opschoningsbeleid beheert:

interface AcpxLifecycleController {
  ensureSession(input: AcpRuntimeEnsureInput): Promise<AcpRuntimeHandle>;
  cancelTurn(handle: AcpRuntimeHandle): Promise<void>;
  closeSession(input: {
    handle: AcpRuntimeHandle;
    discardPersistentState?: boolean;
    reason?: string;
  }): Promise<void>;
  reapStartupOrphans(): Promise<void>;
  verifyOwnedTree(lease: AcpxProcessLease): Promise&lt;OwnedProcessTree | null&gt;;
}

cancelTurn vraagt alleen annulering van de beurt aan. Het mag herbruikbare wrapper- of adapterprocessen niet opruimen.

closeSession mag opruimen, maar alleen na het laden van het sessierecord, het laden van de lease en het verifiëren dat de live procesboom nog bij die lease hoort.

reapStartupOrphans begint bij open leases in de status. Het mag de procestabel gebruiken om descendants te vinden, maar het zou niet eerst willekeurige commando's moeten scannen die op ACP lijken en daarna beslissen dat ze waarschijnlijk van ons zijn.

Wrappercontract

Gegenereerde wrappers moeten klein blijven. Ze moeten:

  • de adapter starten in een procesgroep waar ondersteund
  • normale beëindigingssignalen doorsturen naar de procesgroep
  • overlijden van de ouder detecteren
  • bij overlijden van de ouder SIGTERM sturen en vervolgens de wrapper in leven houden totdat de SIGKILL- fallback wordt uitgevoerd
  • root-PID en procesgroep-id terugrapporteren aan de levenscycluscontroller wanneer dat beschikbaar is

Wrappers moeten geen sessiebeleid bepalen. Ze handhaven alleen lokale opschoning van procesbomen voor hun eigen adaptergroep.

Sessiezichtbaarheidscontract

Zichtbaarheid moet genormaliseerd rijeigenaarschap gebruiken:

type SessionVisibilityInput = {
  requesterSessionKey: string;
  row: {
    key: string;
    agentId: string;
    ownerSessionKey?: string;
    spawnedBy?: string;
    parentSessionKey?: string;
  };
  visibility: "self" | "tree" | "agent" | "all";
  a2aPolicy: AgentToAgentPolicy;
};

Regels:

  • self: alleen de aanvragersessie.
  • tree: aanvragersessie plus rijen die eigendom zijn van of gespawnd zijn vanuit de aanvrager.
  • all: alle rijen van dezelfde agent, a2a-toegestane cross-agent-rijen en door de aanvrager beheerde gespawnde cross-agent-rijen, zelfs wanneer algemene a2a is uitgeschakeld.
  • agent: alleen dezelfde agent, tenzij een expliciete eigendomsrelatie zegt dat de rij bij de aanvrager hoort.

Dit maakt tree en all monotoon: all mag geen eigendomskind verbergen dat tree zou tonen.

Migratieplan

Fase 1: Identiteit En Leases Toevoegen

  • Voeg gatewayInstanceId toe aan Gateway-status.
  • Voeg een ACPX-leasestore toe onder de ACPX-statusmap.
  • Schrijf een lease voordat een gegenereerde wrapper wordt gespawnd.
  • Sla leaseId op in nieuwe ACPX-sessierecords.
  • Behoud bestaande PID- en commandovelden voor oude records.

Fase 2: Lease-Eerste Opschoning

  • Wijzig sluitopschoning om eerst leaseId te laden.
  • Verifieer live proceseigenaarschap tegen de lease voordat signalen worden gestuurd.
  • Behoud de huidige root-PID- en wrapper-root-fallback alleen voor legacy-records.
  • Markeer leases als closed na geverifieerde opschoning.
  • Markeer leases als lost wanneer het proces vóór opschoning verdwenen is.

Fase 3: Lease-Eerst Opruimen Bij Opstarten

  • Opruimen bij opstarten scant open leases.
  • Verifieer voor elke lease het rootproces en verzamel descendants.
  • Ruim geverifieerde bomen kinderen-eerst op.
  • Laat oude closed- en lost-leases verlopen met een begrensde retentieperiode.
  • Behoud scannen met commandomarkers alleen als tijdelijke legacy-fallback, bewaakt door wrapper-root en Gateway-instantie waar mogelijk.

Fase 4: Sessierijen Voor Eigenaarschap

  • Voeg eigendomsmetadata toe aan Gateway-sessierijen.
  • Leer ACPX-, subagent-, achtergrondtaak- en sessiestore-schrijvers om ownerSessionKey of spawnedBy te vullen.
  • Zet sessiezichtbaarheidscontroles om naar gebruik van rijmetadata.
  • Verwijder secundaire sessions.list({ spawnedBy })-lookups tijdens zichtbaarheidscontrole.

Fase 5: Legacy-Heuristieken Verwijderen

Na één releaseperiode:

  • stop met vertrouwen op opgeslagen root-commandoreeksen voor niet-legacy ACPX-opschoning
  • verwijder commandomarker-scans bij opstarten
  • verwijder fallback-listlookups voor zichtbaarheid
  • behoud defensief fail-closed-gedrag voor ontbrekende of niet-verifieerbare leases

Tests

Voeg twee tabelgestuurde suites toe.

Simulator voor proceslevenscyclus:

  • PID hergebruikt door niet-gerelateerd proces
  • PID hergebruikt door wrapper-root van een andere Gateway
  • opgeslagen wrappercommando is shell-gequote, live ps-commando is dat niet
  • adapterkind sluit af, kleinkind blijft in de procesgroep
  • SIGTERM-fallback bij overlijden van ouder bereikt SIGKILL
  • proceslijst niet beschikbaar
  • verouderde lease met ontbrekend proces
  • opstartwees met wrapper, adapterkind en kleinkind

Matrix voor sessiezichtbaarheid:

  • self, tree, agent, all
  • a2a ingeschakeld en uitgeschakeld
  • rij van dezelfde agent
  • cross-agent-rij
  • door aanvrager beheerde gespawnde cross-agent ACP-rij
  • gesandboxte aanvrager beperkt tot tree
  • lijst-, geschiedenis-, verzend- en statusacties

De belangrijke invariant: een door de aanvrager beheerd gespawnd kind is zichtbaar overal waar de geconfigureerde zichtbaarheid de sessieboom van de aanvrager omvat, en all is niet minder capabel dan tree.

Compatibiliteitsnotities

Oude sessierecords hebben mogelijk geen leaseId. Ze moeten het legacy fail-closed-opschoningspad gebruiken:

  • vereis een live rootproces
  • vereis wrapper-root-eigenaarschap wanneer een gegenereerde wrapper wordt verwacht
  • vereis commando-overeenkomst voor niet-wrapper-roots
  • stuur nooit signalen alleen op basis van verouderde opgeslagen PID-metadata

Als een legacy-record niet kan worden geverifieerd, laat het dan met rust. Startup-leaseopschoning en de volgende releaseperiode zouden de fallback uiteindelijk moeten uitfaseren.

Succescriteria

  • Het sluiten van een oude of verouderde ACPX-sessie kan geen proces van een andere Gateway doden.
  • Overlijden van de ouder laat geen hardnekkige adapterkleinkinderen actief.
  • cancel breekt de actieve beurt af zonder herbruikbare sessies te sluiten.
  • sessions_list kan door de aanvrager beheerde cross-agent ACP-kinderen tonen onder zowel tree als all.
  • Opschoning bij opstarten wordt aangestuurd door leases, niet door brede scans van commandoreeksen.
  • De gerichte proces- en zichtbaarheidsmatrixtests dekken elk randgeval dat eerder eenmalige reviewfixes vereiste.