Get started
Портування рушія контексту обв’язки Codex
Стан
Чернетка специфікації реалізації.
Мета
Зробити так, щоб вбудована обгортка Codex app-server дотримувалася того самого контракту життєвого циклу контекстного рушія OpenClaw, якого вже дотримуються вбудовані PI-ходи.
Сесія, що використовує agents.defaults.embeddedHarness.runtime: "codex" або
модель codex/*, все одно має дозволяти вибраному Plugin контекстного рушія,
наприклад lossless-claw, керувати збиранням контексту, прийманням даних після
ходу, обслуговуванням і політикою Compaction рівня OpenClaw настільки, наскільки
це дозволяє межа Codex app-server.
Нецілі
- Не реалізовувати заново внутрішню логіку Codex app-server.
- Не змушувати нативну Compaction потоків Codex створювати зведення lossless-claw.
- Не вимагати від моделей, що не є Codex, використовувати обгортку Codex.
- Не змінювати поведінку сесій ACP/acpx. Ця специфікація стосується лише шляху не-ACP вбудованої обгортки агента.
- Не змушувати сторонні Plugins реєструвати фабрики розширень Codex app-server; наявна межа довіри для вбудованих Plugins залишається незмінною.
Поточна архітектура
Вбудований цикл виконання визначає налаштований контекстний рушій один раз за запуск перед вибором конкретної низькорівневої обгортки:
src/agents/pi-embedded-runner/run.ts- ініціалізує Plugins контекстного рушія
- викликає
resolveContextEngine(params.config) - передає
contextEngineіcontextTokenBudgetдоrunEmbeddedAttemptWithBackend(...)
runEmbeddedAttemptWithBackend(...) делегує вибраній обгортці агента:
src/agents/pi-embedded-runner/run/backend.tssrc/agents/harness/selection.ts
Обгортку Codex app-server реєструє вбудований Codex Plugin:
extensions/codex/index.tsextensions/codex/harness.ts
Реалізація обгортки Codex отримує ті самі EmbeddedRunAttemptParams, що й
спроби на основі PI:
extensions/codex/src/app-server/run-attempt.ts
Це означає, що потрібна точка підключення розташована в коді, контрольованому
OpenClaw. Зовнішня межа - це сам протокол Codex app-server: OpenClaw може
керувати тим, що надсилає до thread/start, thread/resume і turn/start, і
може спостерігати сповіщення, але не може змінювати внутрішнє сховище потоків
Codex або нативний ущільнювач.
Поточна прогалина
Вбудовані спроби PI безпосередньо викликають життєвий цикл контекстного рушія:
- bootstrap/обслуговування перед спробою
- assemble перед викликом моделі
- afterTurn або ingest після спроби
- обслуговування після успішного ходу
- Compaction контекстного рушія для рушіїв, які володіють Compaction
Відповідний код PI:
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
Спроби Codex app-server зараз виконують загальні хуки обгортки агента та
дзеркалять транскрипт, але не викликають params.contextEngine.bootstrap,
params.contextEngine.assemble, params.contextEngine.afterTurn,
params.contextEngine.ingestBatch, params.contextEngine.ingest або
params.contextEngine.maintain.
Відповідний код Codex:
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
Бажана поведінка
Для ходів обгортки Codex OpenClaw має зберігати такий життєвий цикл:
- Прочитати дзеркальний транскрипт сесії OpenClaw.
- Завантажити активний контекстний рушій, коли існує попередній файл сесії.
- Запустити bootstrap-обслуговування, якщо воно доступне.
- Зібрати контекст за допомогою активного контекстного рушія.
- Перетворити зібраний контекст на сумісні з Codex вхідні дані.
- Запустити або відновити потік Codex з інструкціями розробника, які містять
будь-яке
systemPromptAdditionконтекстного рушія. - Запустити хід Codex із зібраним видимим для користувача prompt.
- Віддзеркалити результат Codex назад у транскрипт OpenClaw.
- Викликати
afterTurn, якщо реалізовано, інакшеingestBatch/ingest, використовуючи дзеркальний знімок транскрипта. - Запустити обслуговування ходу після успішних нескасованих ходів.
- Зберегти нативні сигнали Compaction Codex і хуки Compaction OpenClaw.
Проєктні обмеження
Codex app-server залишається канонічним для нативного стану потоку
Codex володіє своїм нативним потоком і будь-якою внутрішньою розширеною історією. OpenClaw не має намагатися змінювати внутрішню історію app-server, окрім як через підтримувані виклики протоколу.
Дзеркальний транскрипт OpenClaw залишається джерелом для функцій OpenClaw:
- історія чату
- пошук
- облік
/newі/reset - майбутнє перемикання моделі або обгортки
- стан Plugin контекстного рушія
Збирання контекстного рушія має проєктуватися у вхідні дані Codex
Інтерфейс контекстного рушія повертає OpenClaw AgentMessage[], а не patch
потоку Codex. Codex app-server turn/start приймає поточний користувацький
вхід, тоді як thread/start і thread/resume приймають інструкції розробника.
Тому реалізації потрібен шар проєкції. Безпечна перша версія має уникати вдавання, ніби вона може замінити внутрішню історію Codex. Вона має вставляти зібраний контекст як детермінований матеріал prompt/інструкцій розробника навколо поточного ходу.
Стабільність prompt-cache важлива
Для рушіїв на кшталт lossless-claw зібраний контекст має бути детермінованим для незмінних вхідних даних. Не додавайте timestamps, випадкові ідентифікатори або недетермінований порядок до згенерованого тексту контексту.
Семантика вибору runtime не змінюється
Вибір обгортки залишається таким, як є:
runtime: "pi"примусово вибирає PIruntime: "codex"вибирає зареєстровану обгортку Codexruntime: "auto"дозволяє Plugin-обгорткам заявляти підтримувані провайдери- невідповідні запуски
autoвикористовують PI
Ця робота змінює те, що відбувається після вибору обгортки Codex.
План реалізації
1. Експортувати або перемістити повторно використовувані helper-и спроб контекстного рушія
Сьогодні повторно використовувані helper-и життєвого циклу живуть у PI runner:
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 не має імпортувати з шляху реалізації, назва якого натякає на PI, якщо цього можна уникнути.
Створіть нейтральний до обгортки модуль, наприклад:
src/agents/harness/context-engine-lifecycle.ts
Перемістіть або повторно експортуйте:
runAttemptContextEngineBootstrapassembleAttemptContextEnginefinalizeAttemptContextEngineTurnbuildAfterTurnRuntimeContextbuildAfterTurnRuntimeContextFromUsage- невелику обгортку навколо
runContextEngineMaintenance
Збережіть працездатність імпортів PI або повторним експортом зі старих файлів, або оновленням місць виклику PI у тому самому PR.
Нейтральні назви helper-ів не мають згадувати PI.
Запропоновані назви:
bootstrapHarnessContextEngineassembleHarnessContextEnginefinalizeHarnessContextEngineTurnbuildHarnessContextEngineRuntimeContextrunHarnessContextEngineMaintenance
2. Додати helper проєкції контексту Codex
Додайте новий модуль:
extensions/codex/src/app-server/context-engine-projection.ts
Обов'язки:
- Приймати зібраний
AgentMessage[], початкову дзеркальну історію та поточний prompt. - Визначати, який контекст належить до інструкцій розробника, а який - до поточного користувацького вводу.
- Зберігати поточний користувацький prompt як фінальний придатний до виконання запит.
- Рендерити попередні повідомлення у стабільному, явному форматі.
- Уникати мінливої метаінформації.
Запропонований API:
export type CodexContextProjection = {
developerInstructionAddition?: string;
promptText: string;
assembledMessages: AgentMessage[];
prePromptMessageCount: number;
};
export function projectContextEngineAssemblyForCodex(params: {
assembledMessages: AgentMessage[];
originalHistoryMessages: AgentMessage[];
prompt: string;
systemPromptAddition?: string;
}): CodexContextProjection;
Рекомендована перша проєкція:
- Додати
systemPromptAdditionдо інструкцій розробника. - Додати зібраний контекст транскрипта перед поточним prompt у
promptText. - Чітко позначити його як зібраний контекст OpenClaw.
- Залишити поточний prompt останнім.
- Виключити дубльований поточний користувацький prompt, якщо він уже є в кінці.
Приклад форми prompt:
OpenClaw assembled context for this turn:
<conversation_context>
[user]
...
[assistant]
...
</conversation_context>
Current user request:
...
Це менш елегантно, ніж нативна зміна історії Codex, але це можна реалізувати всередині OpenClaw, і це зберігає семантику контекстного рушія.
Майбутнє покращення: якщо Codex app-server надасть протокол для заміни або доповнення історії потоку, перемкніть цей шар проєкції на використання цього API.
3. Підключити bootstrap перед запуском потоку Codex
У extensions/codex/src/app-server/run-attempt.ts:
- Прочитати дзеркальну історію сесії, як сьогодні.
- Визначити, чи файл сесії існував до цього запуску. Надавайте перевагу helper-у,
який перевіряє
fs.stat(params.sessionFile)перед записами дзеркалення. - Відкрити
SessionManagerабо використати вузький адаптер менеджера сесій, якщо helper цього потребує. - Викликати нейтральний bootstrap-helper, коли існує
params.contextEngine.
Псевдопотік:
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,
});
Використовуйте ту саму конвенцію sessionKey, що й міст інструментів Codex і
дзеркало транскрипта. Сьогодні Codex обчислює sandboxSessionKey з
params.sessionKey або params.sessionId; використовуйте це узгоджено, якщо
немає причини зберігати сирий params.sessionKey.
4. Підключити assemble перед thread/start / thread/resume і turn/start
У runCodexAppServerAttempt:
- Спершу побудувати динамічні інструменти, щоб контекстний рушій бачив фактичні доступні назви інструментів.
- Прочитати дзеркальну історію сесії.
- Запустити
assemble(...)контекстного рушія, коли існуєparams.contextEngine. - Спроєктувати зібраний результат у:
- додаток до інструкцій розробника
- текст prompt для
turn/start
Наявний виклик hook:
resolveAgentHarnessBeforePromptBuildResult({
prompt: params.prompt,
developerInstructions: buildDeveloperInstructions(params),
messages: historyMessages,
ctx: hookContext,
});
має стати контекстно обізнаним:
- обчислити базові інструкції розробника через
buildDeveloperInstructions(params) - застосувати збирання/проєкцію контекстного рушія
- запустити
before_prompt_buildзі спроєктованими prompt/інструкціями розробника
Такий порядок дозволяє загальним prompt-hooks бачити той самий prompt, який
отримає Codex. Якщо потрібна сувора паритетність із PI, запускайте збирання
контекстного рушія перед композицією hooks, оскільки PI застосовує
systemPromptAddition контекстного рушія до фінального системного prompt після
свого pipeline prompt. Важливий інваріант полягає в тому, що і контекстний
рушій, і hooks отримують детермінований, задокументований порядок.
Рекомендований порядок для першої реалізації:
buildDeveloperInstructions(params)assemble()контекстного рушія- додати
systemPromptAdditionдо інструкцій розробника на початку або в кінці - спроєктувати зібрані повідомлення в текст prompt
resolveAgentHarnessBeforePromptBuildResult(...)- передати фінальні інструкції розробника до
startOrResumeThread(...) - передати фінальний текст prompt до
buildTurnStartParams(...)
Специфікацію слід закодувати в тестах, щоб майбутні зміни випадково не змінили порядок.
5. Зберегти стабільне для prompt-cache форматування
Helper проєкції має створювати байтово стабільний вихід для ідентичних вхідних даних:
- стабільний порядок повідомлень
- стабільні позначки ролей
- без згенерованих timestamps
- без просочування порядку ключів об'єктів
- без випадкових розділювачів
- без ідентифікаторів на кожен запуск
Використовуйте фіксовані розділювачі та явні секції.
6. Підключити post-turn після дзеркалення транскрипта
Codex CodexAppServerEventProjector створює локальний messagesSnapshot для
поточного ходу. mirrorTranscriptBestEffort(...) записує цей знімок у дзеркало
транскрипту OpenClaw.
Після успішного або невдалого дзеркалювання викличте фіналізатор контекстного рушія з найкращим доступним знімком повідомлень:
- Віддавайте перевагу повному дзеркальному контексту сесії після запису, бо
afterTurnочікує знімок сесії, а не лише поточний хід. - Поверніться до
historyMessages + result.messagesSnapshot, якщо файл сесії не вдається повторно відкрити.
Псевдопотік:
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,
});
Якщо дзеркалювання не вдається, все одно викличте afterTurn із резервним
знімком, але залогуйте, що контекстний рушій приймає дані з резервних даних ходу.
7. Нормалізуйте використання та контекст виконання кешу промптів
Результати Codex містять нормалізоване використання з токенових сповіщень app-server, коли воно доступне. Передайте це використання в контекст виконання контекстного рушія.
Якщо Codex app-server з часом надасть деталі читання/запису кешу, відобразьте
їх у ContextEnginePromptCacheInfo. До того часу пропускайте promptCache,
а не вигадуйте нулі.
8. Політика Compaction
Існують дві системи Compaction:
compact()контекстного рушія OpenClaw- Нативна
thread/compact/startCodex app-server
Не змішуйте їх непомітно.
/compact і явна Compaction OpenClaw
Коли вибраний контекстний рушій має info.ownsCompaction === true, явна
Compaction OpenClaw має віддавати перевагу результату compact() контекстного
рушія для дзеркала транскрипту OpenClaw і стану Plugin.
Коли вибраний harness Codex має нативне прив'язування потоку, ми можемо додатково запросити нативну Compaction Codex, щоб підтримувати справність потоку app-server, але це потрібно повідомляти в деталях як окрему дію бекенда.
Рекомендована поведінка:
- Якщо
contextEngine.info.ownsCompaction === true:- спочатку викликати
compact()контекстного рушія - потім за принципом best-effort викликати нативну Compaction Codex, коли існує прив'язування потоку
- повернути результат контекстного рушія як основний результат
- включити статус нативної Compaction Codex у
details.codexNativeCompaction
- спочатку викликати
- Якщо активний контекстний рушій не володіє Compaction:
- зберегти поточну поведінку нативної Compaction Codex
Ймовірно, це потребує зміни extensions/codex/src/app-server/compact.ts або
обгортання його з загального шляху Compaction, залежно від того, де викликається
maybeCompactAgentHarnessSession(...).
Внутрішньохідні події нативного Codex contextCompaction
Codex може емітувати події елементів contextCompaction під час ходу.
Збережіть поточну емісію хуків до/після Compaction в event-projector.ts,
але не розглядайте це як завершену Compaction контекстного рушія.
Для рушіїв, які володіють Compaction, емітуйте явну діагностику, коли Codex усе одно виконує нативну Compaction:
- назва потоку/події: наявний потік
compactionприйнятний - деталі:
{ backend: "codex-app-server", ownsCompaction: true }
Це робить розділення придатним для аудиту.
9. Поведінка скидання сесії та прив'язування
Наявний reset(...) harness Codex очищає прив'язування Codex app-server з
файлу сесії OpenClaw. Збережіть цю поведінку.
Також переконайтеся, що очищення стану контекстного рушія й надалі відбувається через наявні шляхи життєвого циклу сесії OpenClaw. Не додавайте очищення, специфічне для Codex, якщо життєвий цикл контекстного рушія наразі не пропускає події reset/delete для всіх harness.
10. Обробка помилок
Дотримуйтеся семантики PI:
- помилки bootstrap попереджають і продовжують виконання
- помилки assemble попереджають і повертаються до незібраних повідомлень/промпту конвеєра
- помилки afterTurn/ingest попереджають і позначають післяходову фіналізацію як неуспішну
- обслуговування виконується лише після успішних ходів без abort і yield
- помилки Compaction не слід повторювати як нові промпти
Доповнення, специфічні для Codex:
- Якщо проєкція контексту не вдається, попередьте й поверніться до початкового промпту.
- Якщо дзеркалювання транскрипту не вдається, все одно спробуйте фіналізацію контекстного рушія з резервними повідомленнями.
- Якщо нативна Compaction Codex не вдається після успішної Compaction контекстного рушія, не провалюйте всю Compaction OpenClaw, коли контекстний рушій є основним.
План тестування
Модульні тести
Додайте тести в extensions/codex/src/app-server:
-
run-attempt.context-engine.test.ts- Codex викликає
bootstrap, коли файл сесії існує. - Codex викликає
assembleіз дзеркальними повідомленнями, токеновим бюджетом, назвами інструментів, режимом citations, id моделі та промптом. systemPromptAdditionвключено в інструкції розробника.- Зібрані повідомлення проєктуються в промпт перед поточним запитом.
- Codex викликає
afterTurnпісля дзеркалювання транскрипту. - Без
afterTurnCodex викликаєingestBatchабоingestдля кожного повідомлення. - Обслуговування ходу виконується після успішних ходів.
- Обслуговування ходу не виконується при помилці промпту, abort або yield abort.
- Codex викликає
-
context-engine-projection.test.ts- стабільний вихід для однакових вхідних даних
- немає дубліката поточного промпту, коли зібрана історія містить його
- обробляє порожню історію
- зберігає порядок ролей
- включає доповнення системного промпту лише в інструкції розробника
-
compact.context-engine.test.ts- основний результат контекстного рушія-власника має пріоритет
- статус нативної Compaction Codex з'являється в деталях, коли її також спробували
- помилка нативної Codex не провалює Compaction контекстного рушія-власника
- контекстний рушій, який не володіє Compaction, зберігає поточну поведінку нативної Compaction
Наявні тести для оновлення
extensions/codex/src/app-server/run-attempt.test.ts, якщо є, інакше найближчі тести запуску Codex app-server.extensions/codex/src/app-server/event-projector.test.tsлише якщо змінюються деталі події Compaction.src/agents/harness/selection.test.tsне має потребувати змін, якщо не змінюється поведінка конфігурації; він має залишатися стабільним.- Тести контекстного рушія PI мають і надалі проходити без змін.
Інтеграційні / live-тести
Додайте або розширте smoke-тести live harness Codex:
- налаштувати
plugins.slots.contextEngineна тестовий рушій - налаштувати
agents.defaults.modelна модельcodex/* - налаштувати
agents.defaults.embeddedHarness.runtime = "codex" - перевірити, що тестовий рушій спостерігав:
- bootstrap
- assemble
- afterTurn або ingest
- обслуговування
Не вимагайте lossless-claw у тестах ядра OpenClaw. Використайте невеликий фейковий Plugin контекстного рушія в репозиторії.
Спостережуваність
Додайте debug-логи навколо викликів життєвого циклу контекстного рушія Codex:
codex context engine bootstrap started/completed/failedcodex context engine assemble appliedcodex context engine finalize completed/failedcodex context engine maintenance skippedз причиноюcodex native compaction completed alongside context-engine compaction
Уникайте логування повних промптів або вмісту транскриптів.
Додавайте структуровані поля там, де це корисно:
sessionIdsessionKey, відредагований або пропущений відповідно до наявної практики логуванняengineIdthreadIdturnIdassembledMessageCountestimatedTokenshasSystemPromptAddition
Міграція / сумісність
Це має бути зворотно сумісним:
- Якщо контекстний рушій не налаштовано, застаріла поведінка контекстного рушія має бути еквівалентною сьогоднішній поведінці harness Codex.
- Якщо
assembleконтекстного рушія не вдається, Codex має продовжити роботу з початковим шляхом промпту. - Наявні прив'язування потоків Codex мають залишатися чинними.
- Динамічне fingerprinting інструментів не має включати вихід контекстного рушія; інакше кожна зміна контексту могла б примусово створювати новий потік Codex. На динамічний fingerprint інструментів має впливати лише каталог інструментів.
Відкриті питання
-
Чи слід вставляти зібраний контекст повністю в промпт користувача, повністю в інструкції розробника, чи розділяти?
Рекомендація: розділяти. Помістіть
systemPromptAdditionв інструкції розробника; помістіть зібраний контекст транскрипту в обгортку промпту користувача. Це найкраще відповідає поточному протоколу Codex без мутації нативної історії потоку. -
Чи слід вимикати нативну Compaction Codex, коли контекстний рушій володіє Compaction?
Рекомендація: ні, не спочатку. Нативна Compaction Codex усе ще може бути потрібною, щоб підтримувати потік app-server живим. Але її потрібно повідомляти як нативну Compaction Codex, а не як Compaction контекстного рушія.
-
Чи має
before_prompt_buildвиконуватися до чи після збирання контекстного рушія?Рекомендація: після проєкції контекстного рушія для Codex, щоб загальні хуки harness бачили фактичний промпт/інструкції розробника, які отримає Codex. Якщо паритет із PI вимагає протилежного, закодуйте вибраний порядок у тестах і задокументуйте його тут.
-
Чи може Codex app-server прийняти майбутнє структуроване перевизначення контексту/історії?
Невідомо. Якщо може, замініть шар текстової проєкції цим протоколом і залиште виклики життєвого циклу без змін.
Критерії приймання
- Хід embedded harness
codex/*викликає життєвий цикл assemble вибраного контекстного рушія. systemPromptAdditionконтекстного рушія впливає на інструкції розробника Codex.- Зібраний контекст детерміновано впливає на вхідні дані ходу Codex.
- Успішні ходи Codex викликають
afterTurnабо резервний ingest. - Успішні ходи Codex запускають обслуговування ходу контекстного рушія.
- Невдалі/aborted/yield-aborted ходи не запускають обслуговування ходу.
- Compaction, якою володіє контекстний рушій, залишається основною для стану OpenClaw/Plugin.
- Нативна Compaction Codex залишається придатною для аудиту як нативна поведінка Codex.
- Наявна поведінка контекстного рушія PI не змінюється.
- Наявна поведінка harness Codex не змінюється, коли не вибрано незастарілий контекстний рушій або коли збирання не вдається.