Get started
Porting del motore di contesto di Codex Harness
Stato
Specificazione di implementazione in bozza.
Obiettivo
Fare in modo che l'harness app-server Codex incluso rispetti lo stesso contratto di ciclo di vita del motore di contesto OpenClaw che i turni PI integrati rispettano già.
Una sessione che usa agents.defaults.embeddedHarness.runtime: "codex" o un
modello codex/* dovrebbe comunque consentire al plugin del motore di contesto
selezionato, come lossless-claw, di controllare assemblaggio del contesto,
ingest post-turno, manutenzione e policy di Compaction a livello OpenClaw nei
limiti consentiti dal confine app-server Codex.
Non obiettivi
- Non reimplementare gli interni dell'app-server Codex.
- Non fare in modo che la Compaction nativa dei thread Codex produca un riepilogo lossless-claw.
- Non richiedere ai modelli non Codex di usare l'harness Codex.
- Non modificare il comportamento delle sessioni ACP/acpx. Questa specifica è solo per il percorso dell'harness agente integrato non ACP.
- Non fare in modo che Plugin di terze parti registrino factory di estensione dell'app-server Codex; il confine di fiducia esistente dei plugin inclusi resta invariato.
Architettura attuale
Il loop di esecuzione integrato risolve il motore di contesto configurato una volta per esecuzione prima di selezionare un harness concreto di basso livello:
src/agents/pi-embedded-runner/run.ts- inizializza i plugin del motore di contesto
- chiama
resolveContextEngine(params.config) - passa
contextEngineecontextTokenBudgetarunEmbeddedAttemptWithBackend(...)
runEmbeddedAttemptWithBackend(...) delega all'harness agente selezionato:
src/agents/pi-embedded-runner/run/backend.tssrc/agents/harness/selection.ts
L'harness app-server Codex è registrato dal Plugin Codex incluso:
extensions/codex/index.tsextensions/codex/harness.ts
L'implementazione dell'harness Codex riceve gli stessi
EmbeddedRunAttemptParams dei tentativi basati su PI:
extensions/codex/src/app-server/run-attempt.ts
Questo significa che il punto di hook richiesto è in codice controllato da
OpenClaw. Il confine esterno è il protocollo app-server Codex stesso: OpenClaw
può controllare cosa invia a thread/start, thread/resume e turn/start, e
può osservare le notifiche, ma non può modificare lo store interno dei thread di
Codex né il compattatore nativo.
Lacuna attuale
I tentativi PI integrati chiamano direttamente il ciclo di vita del motore di contesto:
- bootstrap/manutenzione prima del tentativo
- assemblaggio prima della chiamata al modello
- afterTurn o ingest dopo il tentativo
- manutenzione dopo un turno riuscito
- Compaction del motore di contesto per i motori che possiedono la Compaction
Codice PI rilevante:
src/agents/pi-embedded-runner/run/attempt.tssrc/agents/pi-embedded-runner/run/attempt.context-engine-helpers.tssrc/agents/pi-embedded-runner/context-engine-maintenance.ts
I tentativi app-server Codex attualmente eseguono hook generici dell'harness
agente e rispecchiano la trascrizione, ma non chiamano
params.contextEngine.bootstrap, params.contextEngine.assemble,
params.contextEngine.afterTurn, params.contextEngine.ingestBatch,
params.contextEngine.ingest o params.contextEngine.maintain.
Codice Codex rilevante:
extensions/codex/src/app-server/run-attempt.tsextensions/codex/src/app-server/thread-lifecycle.tsextensions/codex/src/app-server/event-projector.tsextensions/codex/src/app-server/compact.ts
Comportamento desiderato
Per i turni dell'harness Codex, OpenClaw dovrebbe preservare questo ciclo di vita:
- Leggere la trascrizione rispecchiata della sessione OpenClaw.
- Eseguire il bootstrap del motore di contesto attivo quando esiste un file di sessione precedente.
- Eseguire la manutenzione di bootstrap quando disponibile.
- Assemblare il contesto usando il motore di contesto attivo.
- Convertire il contesto assemblato in input compatibili con Codex.
- Avviare o riprendere il thread Codex con istruzioni per sviluppatori che
includono eventuali
systemPromptAdditiondel motore di contesto. - Avviare il turno Codex con il prompt rivolto all'utente assemblato.
- Rispecchiare il risultato Codex nella trascrizione OpenClaw.
- Chiamare
afterTurnse implementato, altrimentiingestBatch/ingest, usando lo snapshot della trascrizione rispecchiata. - Eseguire la manutenzione del turno dopo turni riusciti e non interrotti.
- Preservare i segnali di Compaction nativa Codex e gli hook di Compaction OpenClaw.
Vincoli di progettazione
L'app-server Codex resta canonico per lo stato nativo dei thread
Codex possiede il proprio thread nativo e qualsiasi cronologia estesa interna. OpenClaw non dovrebbe tentare di mutare la cronologia interna dell'app-server se non tramite chiamate di protocollo supportate.
Il mirror della trascrizione OpenClaw resta la fonte per le funzionalità OpenClaw:
- cronologia chat
- ricerca
- contabilità di
/newe/reset - futuro cambio di modello o harness
- stato del plugin del motore di contesto
L'assemblaggio del motore di contesto deve essere proiettato negli input Codex
L'interfaccia del motore di contesto restituisce AgentMessage[] OpenClaw, non
una patch del thread Codex. turn/start dell'app-server Codex accetta un input
utente corrente, mentre thread/start e thread/resume accettano istruzioni per
sviluppatori.
Pertanto l'implementazione necessita di un livello di proiezione. La prima versione sicura dovrebbe evitare di fingere di poter sostituire la cronologia interna di Codex. Dovrebbe iniettare il contesto assemblato come materiale deterministico di prompt/istruzioni per sviluppatori attorno al turno corrente.
La stabilità della cache dei prompt è importante
Per motori come lossless-claw, il contesto assemblato dovrebbe essere deterministico per input invariati. Non aggiungere timestamp, id casuali o ordinamenti non deterministici al testo di contesto generato.
La semantica di selezione del runtime non cambia
La selezione dell'harness resta invariata:
runtime: "pi"forza PIruntime: "codex"seleziona l'harness Codex registratoruntime: "auto"consente agli harness dei plugin di rivendicare provider supportati- le esecuzioni
autosenza corrispondenza usano PI
Questo lavoro cambia ciò che accade dopo la selezione dell'harness Codex.
Piano di implementazione
1. Esportare o ricollocare helper riutilizzabili per i tentativi del motore di contesto
Oggi gli helper riutilizzabili del ciclo di vita vivono sotto il runner PI:
src/agents/pi-embedded-runner/run/attempt.context-engine-helpers.tssrc/agents/pi-embedded-runner/run/attempt.prompt-helpers.tssrc/agents/pi-embedded-runner/context-engine-maintenance.ts
Codex non dovrebbe importare da un percorso di implementazione il cui nome implica PI, se possiamo evitarlo.
Creare un modulo neutro rispetto all'harness, per esempio:
src/agents/harness/context-engine-lifecycle.ts
Spostare o riesportare:
runAttemptContextEngineBootstrapassembleAttemptContextEnginefinalizeAttemptContextEngineTurnbuildAfterTurnRuntimeContextbuildAfterTurnRuntimeContextFromUsage- un piccolo wrapper attorno a
runContextEngineMaintenance
Mantenere funzionanti gli import PI riesportando dai vecchi file oppure aggiornando i call site PI nella stessa PR.
I nomi degli helper neutri non dovrebbero menzionare PI.
Nomi suggeriti:
bootstrapHarnessContextEngineassembleHarnessContextEnginefinalizeHarnessContextEngineTurnbuildHarnessContextEngineRuntimeContextrunHarnessContextEngineMaintenance
2. Aggiungere un helper di proiezione del contesto Codex
Aggiungere un nuovo modulo:
extensions/codex/src/app-server/context-engine-projection.ts
Responsabilità:
- Accettare
AgentMessage[]assemblati, la cronologia originale rispecchiata e il prompt corrente. - Determinare quale contesto appartiene alle istruzioni per sviluppatori rispetto all'input utente corrente.
- Preservare il prompt utente corrente come richiesta finale azionabile.
- Renderizzare i messaggi precedenti in un formato stabile ed esplicito.
- Evitare metadati volatili.
API proposta:
export type CodexContextProjection = {
developerInstructionAddition?: string;
promptText: string;
assembledMessages: AgentMessage[];
prePromptMessageCount: number;
};
export function projectContextEngineAssemblyForCodex(params: {
assembledMessages: AgentMessage[];
originalHistoryMessages: AgentMessage[];
prompt: string;
systemPromptAddition?: string;
}): CodexContextProjection;
Prima proiezione consigliata:
- Inserire
systemPromptAdditionnelle istruzioni per sviluppatori. - Inserire il contesto della trascrizione assemblata prima del prompt corrente in
promptText. - Etichettarlo chiaramente come contesto assemblato OpenClaw.
- Mantenere il prompt corrente per ultimo.
- Escludere il prompt utente corrente duplicato se appare già in coda.
Forma di prompt di esempio:
OpenClaw assembled context for this turn:
<conversation_context>
[user]
...
[assistant]
...
</conversation_context>
Current user request:
...
Questo è meno elegante della modifica nativa della cronologia Codex, ma è implementabile dentro OpenClaw e preserva la semantica del motore di contesto.
Miglioramento futuro: se l'app-server Codex espone un protocollo per sostituire o integrare la cronologia del thread, modificare questo livello di proiezione per usare quell'API.
3. Collegare il bootstrap prima dell'avvio del thread Codex
In extensions/codex/src/app-server/run-attempt.ts:
- Leggere la cronologia della sessione rispecchiata come oggi.
- Determinare se il file di sessione esisteva prima di questa esecuzione.
Preferire un helper che controlli
fs.stat(params.sessionFile)prima delle scritture di mirroring. - Aprire un
SessionManagero usare un adapter stretto per il session manager se l'helper lo richiede. - Chiamare l'helper di bootstrap neutro quando
params.contextEngineesiste.
Pseudo-flusso:
const hadSessionFile = await fileExists(params.sessionFile);
const sessionManager = SessionManager.open(params.sessionFile);
const historyMessages = sessionManager.buildSessionContext().messages;
await bootstrapHarnessContextEngine({
hadSessionFile,
contextEngine: params.contextEngine,
sessionId: params.sessionId,
sessionKey: sandboxSessionKey,
sessionFile: params.sessionFile,
sessionManager,
runtimeContext: buildHarnessContextEngineRuntimeContext(...),
runMaintenance: runHarnessContextEngineMaintenance,
warn,
});
Usare la stessa convenzione sessionKey del bridge strumenti Codex e del mirror
della trascrizione. Oggi Codex calcola sandboxSessionKey da
params.sessionKey o params.sessionId; usarlo in modo coerente, salvo che ci
sia una ragione per preservare params.sessionKey grezzo.
4. Collegare l'assemblaggio prima di thread/start / thread/resume e turn/start
In runCodexAppServerAttempt:
- Costruire prima gli strumenti dinamici, così il motore di contesto vede i nomi effettivi degli strumenti disponibili.
- Leggere la cronologia della sessione rispecchiata.
- Eseguire
assemble(...)del motore di contesto quandoparams.contextEngineesiste. - Proiettare il risultato assemblato in:
- aggiunta alle istruzioni per sviluppatori
- testo del prompt per
turn/start
La chiamata hook esistente:
resolveAgentHarnessBeforePromptBuildResult({
prompt: params.prompt,
developerInstructions: buildDeveloperInstructions(params),
messages: historyMessages,
ctx: hookContext,
});
dovrebbe diventare consapevole del contesto:
- calcolare le istruzioni base per sviluppatori con
buildDeveloperInstructions(params) - applicare assemblaggio/proiezione del motore di contesto
- eseguire
before_prompt_buildcon prompt/istruzioni per sviluppatori proiettati
Questo ordine consente agli hook generici dei prompt di vedere lo stesso prompt
che Codex riceverà. Se serve parità stretta con PI, eseguire l'assemblaggio del
motore di contesto prima della composizione degli hook, perché PI applica
systemPromptAddition del motore di contesto al prompt di sistema finale dopo la
sua pipeline di prompt. L'invariante importante è che sia il motore di contesto
sia gli hook abbiano un ordine deterministico e documentato.
Ordine consigliato per la prima implementazione:
buildDeveloperInstructions(params)assemble()del motore di contesto- aggiungere/preporre
systemPromptAdditionalle istruzioni per sviluppatori - proiettare i messaggi assemblati nel testo del prompt
resolveAgentHarnessBeforePromptBuildResult(...)- passare le istruzioni finali per sviluppatori a
startOrResumeThread(...) - passare il testo finale del prompt a
buildTurnStartParams(...)
La specifica dovrebbe essere codificata nei test così che modifiche future non ne riordinino i passi per errore.
5. Preservare la formattazione stabile della cache dei prompt
L'helper di proiezione deve produrre output stabile a livello di byte per input identici:
- ordine stabile dei messaggi
- etichette dei ruoli stabili
- nessun timestamp generato
- nessuna fuga dell'ordine delle chiavi degli oggetti
- nessun delimitatore casuale
- nessun id per esecuzione
Usare delimitatori fissi e sezioni esplicite.
6. Collegare il post-turno dopo il mirroring della trascrizione
Codex CodexAppServerEventProjector crea un messagesSnapshot locale per il
turno corrente. mirrorTranscriptBestEffort(...) scrive quello snapshot nel
mirror della trascrizione di OpenClaw.
Dopo che il mirroring riesce o fallisce, chiama il finalizzatore del motore di contesto con il miglior snapshot dei messaggi disponibile:
- Preferisci il contesto completo della sessione con mirroring dopo la scrittura, perché
afterTurnsi aspetta lo snapshot della sessione, non solo il turno corrente. - Ripiega su
historyMessages + result.messagesSnapshotse il file della sessione non può essere riaperto.
Flusso pseudo:
const prePromptMessageCount = historyMessages.length;
await mirrorTranscriptBestEffort(...);
const finalMessages = readMirroredSessionHistoryMessages(params.sessionFile)
?? [...historyMessages, ...result.messagesSnapshot];
await finalizeHarnessContextEngineTurn({
contextEngine: params.contextEngine,
promptError: Boolean(finalPromptError),
aborted: finalAborted,
yieldAborted,
sessionIdUsed: params.sessionId,
sessionKey: sandboxSessionKey,
sessionFile: params.sessionFile,
messagesSnapshot: finalMessages,
prePromptMessageCount,
tokenBudget: params.contextTokenBudget,
runtimeContext: buildHarnessContextEngineRuntimeContextFromUsage({
attempt: params,
workspaceDir: effectiveWorkspace,
agentDir,
tokenBudget: params.contextTokenBudget,
lastCallUsage: result.attemptUsage,
promptCache: result.promptCache,
}),
runMaintenance: runHarnessContextEngineMaintenance,
sessionManager,
warn,
});
Se il mirroring fallisce, chiama comunque afterTurn con lo snapshot di fallback, ma registra
che il motore di contesto sta importando dai dati di turno di fallback.
7. Normalizzare il contesto di runtime di utilizzo e prompt-cache
I risultati di Codex includono l'utilizzo normalizzato dalle notifiche dei token app-server quando disponibili. Passa quell'utilizzo nel contesto di runtime del motore di contesto.
Se Codex app-server espone in futuro dettagli di lettura/scrittura della cache, mappali in
ContextEnginePromptCacheInfo. Fino ad allora, ometti promptCache invece di
inventare zeri.
8. Criterio di Compaction
Ci sono due sistemi di Compaction:
compact()del motore di contesto OpenClawthread/compact/startnativo di Codex app-server
Non confonderli silenziosamente.
/compact e Compaction OpenClaw esplicita
Quando il motore di contesto selezionato ha info.ownsCompaction === true, la Compaction
OpenClaw esplicita dovrebbe preferire il risultato compact() del motore di contesto per
il mirror della trascrizione di OpenClaw e lo stato del Plugin.
Quando l'harness Codex selezionato ha un'associazione nativa al thread, possiamo inoltre richiedere la Compaction nativa Codex per mantenere sano il thread app-server, ma questo deve essere riportato come azione backend separata nei dettagli.
Comportamento consigliato:
- Se
contextEngine.info.ownsCompaction === true:- chiama prima
compact()del motore di contesto - poi chiama in modo best-effort la Compaction nativa Codex quando esiste un'associazione al thread
- restituisci il risultato del motore di contesto come risultato primario
- includi lo stato della Compaction nativa Codex in
details.codexNativeCompaction
- chiama prima
- Se il motore di contesto attivo non possiede la Compaction:
- preserva il comportamento attuale della Compaction nativa Codex
Questo probabilmente richiede di modificare extensions/codex/src/app-server/compact.ts o
di incapsularlo dal percorso di Compaction generico, a seconda di dove viene invocato
maybeCompactAgentHarnessSession(...).
Eventi contextCompaction nativi Codex durante il turno
Codex può emettere eventi di elemento contextCompaction durante un turno. Mantieni l'emissione
attuale degli hook di Compaction before/after in event-projector.ts, ma non trattarla
come una Compaction del motore di contesto completata.
Per i motori che possiedono la Compaction, emetti una diagnostica esplicita quando Codex esegue comunque la Compaction nativa:
- nome stream/evento: lo stream
compactionesistente è accettabile - dettagli:
{ backend: "codex-app-server", ownsCompaction: true }
Questo rende verificabile la separazione.
9. Reset della sessione e comportamento di binding
Il reset(...) esistente dell'harness Codex cancella l'associazione Codex app-server dal
file di sessione OpenClaw. Preserva questo comportamento.
Assicurati inoltre che la pulizia dello stato del motore di contesto continui ad avvenire tramite i percorsi esistenti del ciclo di vita della sessione OpenClaw. Non aggiungere pulizia specifica di Codex a meno che il ciclo di vita del motore di contesto non perda attualmente eventi di reset/eliminazione per tutti gli harness.
10. Gestione degli errori
Segui la semantica PI:
- gli errori di bootstrap avvisano e continuano
- gli errori di assemble avvisano e ripiegano sui messaggi/prompt della pipeline non assemblata
- gli errori afterTurn/ingest avvisano e marcano la finalizzazione post-turno come non riuscita
- la manutenzione viene eseguita solo dopo turni riusciti, non interrotti e non yield
- gli errori di Compaction non dovrebbero essere ritentati come prompt nuovi
Aggiunte specifiche di Codex:
- Se la proiezione del contesto fallisce, avvisa e ripiega sul prompt originale.
- Se il mirror della trascrizione fallisce, tenta comunque la finalizzazione del motore di contesto con messaggi di fallback.
- Se la Compaction nativa Codex fallisce dopo che la Compaction del motore di contesto è riuscita, non far fallire l'intera Compaction OpenClaw quando il motore di contesto è primario.
Piano di test
Test unitari
Aggiungi test sotto extensions/codex/src/app-server:
-
run-attempt.context-engine.test.ts- Codex chiama
bootstrapquando esiste un file di sessione. - Codex chiama
assemblecon messaggi con mirroring, budget di token, nomi degli strumenti, modalità delle citazioni, id del modello e prompt. systemPromptAdditionè incluso nelle istruzioni developer.- I messaggi assemblati sono proiettati nel prompt prima della richiesta corrente.
- Codex chiama
afterTurndopo il mirroring della trascrizione. - Senza
afterTurn, Codex chiamaingestBatchoingestper messaggio. - La manutenzione del turno viene eseguita dopo i turni riusciti.
- La manutenzione del turno non viene eseguita su errore del prompt, abort o yield abort.
- Codex chiama
-
context-engine-projection.test.ts- output stabile per input identici
- nessun prompt corrente duplicato quando la cronologia assemblata lo include
- gestisce la cronologia vuota
- preserva l'ordine dei ruoli
- include l'aggiunta al prompt di sistema solo nelle istruzioni developer
-
compact.context-engine.test.ts- il risultato primario del motore di contesto proprietario vince
- lo stato della Compaction nativa Codex appare nei dettagli quando viene tentata anche quella
- il fallimento nativo Codex non fa fallire la Compaction del motore di contesto proprietario
- il motore di contesto non proprietario preserva il comportamento attuale della Compaction nativa
Test esistenti da aggiornare
extensions/codex/src/app-server/run-attempt.test.tsse presente, altrimenti i test di esecuzione Codex app-server più vicini.extensions/codex/src/app-server/event-projector.test.tssolo se cambiano i dettagli degli eventi di Compaction.src/agents/harness/selection.test.tsnon dovrebbe richiedere modifiche a meno che non cambi il comportamento della configurazione; dovrebbe rimanere stabile.- I test del motore di contesto PI dovrebbero continuare a passare invariati.
Test di integrazione / live
Aggiungi o estendi smoke test live dell'harness Codex:
- configura
plugins.slots.contextEnginesu un motore di test - configura
agents.defaults.modelsu un modellocodex/* - configura
agents.defaults.embeddedHarness.runtime = "codex" - verifica che il motore di test abbia osservato:
- bootstrap
- assemble
- afterTurn o ingest
- manutenzione
Evita di richiedere lossless-claw nei test core di OpenClaw. Usa un piccolo Plugin finto del motore di contesto interno al repo.
Osservabilità
Aggiungi log di debug attorno alle chiamate del ciclo di vita del motore di contesto Codex:
codex context engine bootstrap started/completed/failedcodex context engine assemble appliedcodex context engine finalize completed/failedcodex context engine maintenance skippedcon motivocodex native compaction completed alongside context-engine compaction
Evita di registrare prompt completi o contenuti della trascrizione.
Aggiungi campi strutturati dove utili:
sessionIdsessionKeyredatto o omesso secondo la prassi di logging esistenteengineIdthreadIdturnIdassembledMessageCountestimatedTokenshasSystemPromptAddition
Migrazione / compatibilità
Questo dovrebbe essere retrocompatibile:
- Se non è configurato alcun motore di contesto, il comportamento legacy del motore di contesto dovrebbe essere equivalente al comportamento attuale dell'harness Codex.
- Se
assembledel motore di contesto fallisce, Codex dovrebbe continuare con il percorso del prompt originale. - Le associazioni thread Codex esistenti dovrebbero rimanere valide.
- Il fingerprinting dinamico degli strumenti non dovrebbe includere l'output del motore di contesto; altrimenti ogni modifica del contesto potrebbe forzare un nuovo thread Codex. Solo il catalogo degli strumenti dovrebbe influire sul fingerprint dinamico degli strumenti.
Domande aperte
-
Il contesto assemblato dovrebbe essere iniettato interamente nel prompt utente, interamente nelle istruzioni developer, o diviso?
Raccomandazione: dividerlo. Metti
systemPromptAdditionnelle istruzioni developer; metti il contesto della trascrizione assemblata nel wrapper del prompt utente. Questo corrisponde meglio al protocollo Codex attuale senza mutare la cronologia nativa del thread. -
La Compaction nativa Codex dovrebbe essere disabilitata quando un motore di contesto possiede la Compaction?
Raccomandazione: no, non inizialmente. La Compaction nativa Codex può essere ancora necessaria per mantenere vivo il thread app-server. Ma deve essere riportata come Compaction Codex nativa, non come Compaction del motore di contesto.
-
before_prompt_builddovrebbe essere eseguito prima o dopo l'assemblaggio del motore di contesto?Raccomandazione: dopo la proiezione del motore di contesto per Codex, così gli hook generici dell'harness vedono il prompt/le istruzioni developer effettivi che Codex riceverà. Se la parità PI richiede l'opposto, codifica l'ordine scelto nei test e documentalo qui.
-
Codex app-server può accettare in futuro un override strutturato di contesto/cronologia?
Sconosciuto. Se può, sostituisci il livello di proiezione testuale con quel protocollo e mantieni invariate le chiamate del ciclo di vita.
Criteri di accettazione
- Un turno dell'harness embedded
codex/*invoca il ciclo di vita assemble del motore di contesto selezionato. - Un
systemPromptAdditiondel motore di contesto influisce sulle istruzioni developer di Codex. - Il contesto assemblato influisce sull'input del turno Codex in modo deterministico.
- I turni Codex riusciti chiamano
afterTurno il fallback ingest. - I turni Codex riusciti eseguono la manutenzione del turno del motore di contesto.
- I turni falliti/interrotti/yield-aborted non eseguono la manutenzione del turno.
- La Compaction posseduta dal motore di contesto rimane primaria per lo stato OpenClaw/Plugin.
- La Compaction nativa Codex rimane verificabile come comportamento nativo Codex.
- Il comportamento esistente del motore di contesto PI è invariato.
- Il comportamento esistente dell'harness Codex è invariato quando non è selezionato alcun motore di contesto non legacy o quando l'assemblaggio fallisce.