Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
624
docs/plan/codex-context-engine-harness.md
Normal file
624
docs/plan/codex-context-engine-harness.md
Normal file
@@ -0,0 +1,624 @@
|
||||
---
|
||||
title: "Codex Harness Context Engine Port"
|
||||
summary: "Specification for making the bundled Codex app-server harness honor OpenClaw context-engine plugins"
|
||||
read_when:
|
||||
- You are wiring context-engine lifecycle behavior into the Codex harness
|
||||
- You need lossless-claw or another context-engine plugin to work with codex/* embedded harness sessions
|
||||
- You are comparing embedded OpenClaw and Codex app-server context behavior
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
Draft implementation specification.
|
||||
|
||||
## Goal
|
||||
|
||||
Make the bundled Codex app-server harness honor the same OpenClaw context-engine
|
||||
lifecycle contract that embedded OpenClaw turns already honor.
|
||||
|
||||
A session using provider/model `agentRuntime.id: "codex"` or a `codex/*` model
|
||||
should still let the selected context-engine plugin, such as
|
||||
`lossless-claw`, control context assembly, post-turn ingest, maintenance, and
|
||||
OpenClaw-level compaction policy as far as the Codex app-server boundary allows.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Do not reimplement Codex app-server internals.
|
||||
- Do not make Codex native thread compaction produce a lossless-claw summary.
|
||||
- Do not require non-Codex models to use the Codex harness.
|
||||
- Do not change ACP/acpx session behavior. This specification is for the
|
||||
non-ACP embedded agent harness path only.
|
||||
- Do not make third-party plugins register Codex app-server extension factories;
|
||||
the existing bundled-plugin trust boundary remains unchanged.
|
||||
|
||||
## Current architecture
|
||||
|
||||
The embedded run loop resolves the configured context engine once per run before
|
||||
selecting a concrete low-level harness:
|
||||
|
||||
- `src/agents/embedded-agent-runner/run.ts`
|
||||
- initializes context-engine plugins
|
||||
- calls `resolveContextEngine(params.config)`
|
||||
- passes `contextEngine` and `contextTokenBudget` into
|
||||
`runEmbeddedAttemptWithBackend(...)`
|
||||
|
||||
`runEmbeddedAttemptWithBackend(...)` delegates to the selected agent harness:
|
||||
|
||||
- `src/agents/embedded-agent-runner/run/backend.ts`
|
||||
- `src/agents/harness/selection.ts`
|
||||
|
||||
The Codex app-server harness is registered by the bundled Codex plugin:
|
||||
|
||||
- `extensions/codex/index.ts`
|
||||
- `extensions/codex/harness.ts`
|
||||
|
||||
The Codex harness implementation receives the same `EmbeddedRunAttemptParams`
|
||||
as built-in OpenClaw attempts:
|
||||
|
||||
- `extensions/codex/src/app-server/run-attempt.ts`
|
||||
|
||||
That means the required hook point is in OpenClaw-controlled code. The external
|
||||
boundary is the Codex app-server protocol itself: OpenClaw can control what it
|
||||
sends to `thread/start`, `thread/resume`, and `turn/start`, and can observe
|
||||
notifications, but it cannot change Codex's internal thread store or native
|
||||
compactor.
|
||||
|
||||
## Current gap
|
||||
|
||||
Built-in OpenClaw attempts call the context-engine lifecycle directly:
|
||||
|
||||
- bootstrap/maintenance before the attempt
|
||||
- assemble before the model call
|
||||
- afterTurn or ingest after the attempt
|
||||
- maintenance after a successful turn
|
||||
- context-engine compaction for engines that own compaction
|
||||
|
||||
Relevant OpenClaw code:
|
||||
|
||||
- `src/agents/embedded-agent-runner/run/attempt.ts`
|
||||
- `src/agents/embedded-agent-runner/run/attempt.context-engine-helpers.ts`
|
||||
- `src/agents/embedded-agent-runner/context-engine-maintenance.ts`
|
||||
|
||||
Codex app-server attempts currently run generic agent-harness hooks and mirror
|
||||
the transcript, but do not call `params.contextEngine.bootstrap`,
|
||||
`params.contextEngine.assemble`, `params.contextEngine.afterTurn`,
|
||||
`params.contextEngine.ingestBatch`, `params.contextEngine.ingest`, or
|
||||
`params.contextEngine.maintain`.
|
||||
|
||||
Relevant Codex code:
|
||||
|
||||
- `extensions/codex/src/app-server/run-attempt.ts`
|
||||
- `extensions/codex/src/app-server/thread-lifecycle.ts`
|
||||
- `extensions/codex/src/app-server/event-projector.ts`
|
||||
- `extensions/codex/src/app-server/compact.ts`
|
||||
|
||||
## Desired behavior
|
||||
|
||||
For Codex harness turns, OpenClaw should preserve this lifecycle:
|
||||
|
||||
1. Read the mirrored OpenClaw session transcript.
|
||||
2. Bootstrap the active context engine when a previous session file exists.
|
||||
3. Run bootstrap maintenance when available.
|
||||
4. Assemble context using the active context engine.
|
||||
5. Convert the assembled context into Codex-compatible inputs.
|
||||
6. Start or resume the Codex thread with developer instructions that include any
|
||||
context-engine `systemPromptAddition`.
|
||||
7. Start the Codex turn with the assembled user-facing prompt.
|
||||
8. Mirror the Codex result back into the OpenClaw transcript.
|
||||
9. Call `afterTurn` if implemented, otherwise `ingestBatch`/`ingest`, using the
|
||||
mirrored transcript snapshot.
|
||||
10. Run turn maintenance after successful non-aborted turns.
|
||||
11. Preserve Codex native compaction signals and OpenClaw compaction hooks.
|
||||
|
||||
## Design constraints
|
||||
|
||||
### Codex app-server remains canonical for native thread state
|
||||
|
||||
Codex owns its native thread and any internal extended history. OpenClaw should
|
||||
not try to mutate the app-server's internal history except through supported
|
||||
protocol calls.
|
||||
|
||||
OpenClaw's transcript mirror remains the source for OpenClaw features:
|
||||
|
||||
- chat history
|
||||
- search
|
||||
- `/new` and `/reset` bookkeeping
|
||||
- future model or harness switching
|
||||
- context-engine plugin state
|
||||
|
||||
### Context engine assembly must be projected into Codex inputs
|
||||
|
||||
The context-engine interface returns OpenClaw `AgentMessage[]`, not a Codex
|
||||
thread patch. Codex app-server `turn/start` accepts a current user input, while
|
||||
`thread/start` and `thread/resume` accept developer instructions.
|
||||
|
||||
Therefore the implementation needs a projection layer. The safe first version
|
||||
should avoid pretending it can replace Codex internal history. It should inject
|
||||
assembled context as deterministic prompt/developer-instruction material around
|
||||
the current turn.
|
||||
|
||||
### Prompt-cache stability matters
|
||||
|
||||
For engines like lossless-claw, the assembled context should be deterministic
|
||||
for unchanged inputs. Do not add timestamps, random ids, or nondeterministic
|
||||
ordering to generated context text.
|
||||
|
||||
### Runtime selection semantics do not change
|
||||
|
||||
Harness selection remains as-is:
|
||||
|
||||
- `runtime: "openclaw"` selects the built-in OpenClaw harness
|
||||
- `runtime: "codex"` selects the registered Codex harness
|
||||
- `runtime: "auto"` lets plugin harnesses claim supported providers
|
||||
- unmatched `auto` runs use the built-in OpenClaw harness
|
||||
|
||||
This work changes what happens after the Codex harness is selected.
|
||||
|
||||
## Implementation plan
|
||||
|
||||
### 1. Export or relocate reusable context-engine attempt helpers
|
||||
|
||||
Today the reusable lifecycle helpers live under the embedded agent runner:
|
||||
|
||||
- `src/agents/embedded-agent-runner/run/attempt.context-engine-helpers.ts`
|
||||
- `src/agents/embedded-agent-runner/run/attempt.prompt-helpers.ts`
|
||||
- `src/agents/embedded-agent-runner/context-engine-maintenance.ts`
|
||||
|
||||
Codex should import harness-neutral helpers rather than reaching into runner
|
||||
implementation details.
|
||||
|
||||
Create a harness-neutral module, for example:
|
||||
|
||||
- `src/agents/harness/context-engine-lifecycle.ts`
|
||||
|
||||
Move or re-export:
|
||||
|
||||
- `runAttemptContextEngineBootstrap`
|
||||
- `assembleAttemptContextEngine`
|
||||
- `finalizeAttemptContextEngineTurn`
|
||||
- `buildAfterTurnRuntimeContext`
|
||||
- `buildAfterTurnRuntimeContextFromUsage`
|
||||
- a small wrapper around `runContextEngineMaintenance`
|
||||
|
||||
Update built-in harness call sites in the same PR.
|
||||
|
||||
The neutral helper names should not mention the built-in harness.
|
||||
|
||||
Suggested names:
|
||||
|
||||
- `bootstrapHarnessContextEngine`
|
||||
- `assembleHarnessContextEngine`
|
||||
- `finalizeHarnessContextEngineTurn`
|
||||
- `buildHarnessContextEngineRuntimeContext`
|
||||
- `runHarnessContextEngineMaintenance`
|
||||
|
||||
### 2. Add a Codex context projection helper
|
||||
|
||||
Add a new module:
|
||||
|
||||
- `extensions/codex/src/app-server/context-engine-projection.ts`
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Accept the assembled `AgentMessage[]`, original mirrored history, and current
|
||||
prompt.
|
||||
- Determine which context belongs in developer instructions vs current user
|
||||
input.
|
||||
- Preserve the current user prompt as the final actionable request.
|
||||
- Render prior messages in a stable, explicit format.
|
||||
- Avoid volatile metadata.
|
||||
|
||||
Proposed API:
|
||||
|
||||
```ts
|
||||
export type CodexContextProjection = {
|
||||
developerInstructionAddition?: string;
|
||||
promptText: string;
|
||||
assembledMessages: AgentMessage[];
|
||||
prePromptMessageCount: number;
|
||||
};
|
||||
|
||||
export function projectContextEngineAssemblyForCodex(params: {
|
||||
assembledMessages: AgentMessage[];
|
||||
originalHistoryMessages: AgentMessage[];
|
||||
prompt: string;
|
||||
systemPromptAddition?: string;
|
||||
}): CodexContextProjection;
|
||||
```
|
||||
|
||||
Recommended first projection:
|
||||
|
||||
- Put `systemPromptAddition` into developer instructions.
|
||||
- Put the assembled transcript context before the current prompt in `promptText`.
|
||||
- Label it clearly as OpenClaw assembled context.
|
||||
- Keep current prompt last.
|
||||
- Exclude duplicate current user prompt if it already appears at the tail.
|
||||
|
||||
Example prompt shape:
|
||||
|
||||
```text
|
||||
OpenClaw assembled context for this turn:
|
||||
|
||||
<conversation_context>
|
||||
[user]
|
||||
...
|
||||
|
||||
[assistant]
|
||||
...
|
||||
</conversation_context>
|
||||
|
||||
Current user request:
|
||||
...
|
||||
```
|
||||
|
||||
This is less elegant than native Codex history surgery, but it is implementable
|
||||
inside OpenClaw and preserves context-engine semantics.
|
||||
|
||||
Future improvement: if Codex app-server exposes a protocol for replacing or
|
||||
supplementing thread history, swap this projection layer to use that API.
|
||||
|
||||
### 3. Wire bootstrap before Codex thread startup
|
||||
|
||||
In `extensions/codex/src/app-server/run-attempt.ts`:
|
||||
|
||||
- Read mirrored session history as today.
|
||||
- Determine whether the session file existed before this run. Prefer a helper
|
||||
that checks `fs.stat(params.sessionFile)` before mirroring writes.
|
||||
- Open a `SessionManager` or use a narrow session manager adapter if the helper
|
||||
requires it.
|
||||
- Call the neutral bootstrap helper when `params.contextEngine` exists.
|
||||
|
||||
Pseudo-flow:
|
||||
|
||||
```ts
|
||||
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,
|
||||
});
|
||||
```
|
||||
|
||||
Use the same `sessionKey` convention as the Codex tool bridge and transcript
|
||||
mirror. Today Codex computes `sandboxSessionKey` from `params.sessionKey` or
|
||||
`params.sessionId`; use that consistently unless there is a reason to preserve
|
||||
raw `params.sessionKey`.
|
||||
|
||||
### 4. Wire assemble before `thread/start` / `thread/resume` and `turn/start`
|
||||
|
||||
In `runCodexAppServerAttempt`:
|
||||
|
||||
1. Build dynamic tools first, so the context engine sees the actual available
|
||||
tool names.
|
||||
2. Read mirrored session history.
|
||||
3. Run context-engine `assemble(...)` when `params.contextEngine` exists.
|
||||
4. Project the assembled result into:
|
||||
- developer instruction addition
|
||||
- prompt text for `turn/start`
|
||||
|
||||
The existing hook call:
|
||||
|
||||
```ts
|
||||
resolveAgentHarnessBeforePromptBuildResult({
|
||||
prompt: params.prompt,
|
||||
developerInstructions: buildDeveloperInstructions(params),
|
||||
messages: historyMessages,
|
||||
ctx: hookContext,
|
||||
});
|
||||
```
|
||||
|
||||
should become context-aware:
|
||||
|
||||
1. compute base developer instructions with `buildDeveloperInstructions(params)`
|
||||
2. apply context-engine assembly/projection
|
||||
3. run `before_prompt_build` with the projected prompt/developer instructions
|
||||
|
||||
This order lets generic prompt hooks see the same prompt Codex will receive. If
|
||||
we need strict OpenClaw parity, run context-engine assembly before hook
|
||||
composition, because the built-in harness applies context-engine
|
||||
`systemPromptAddition` to the final system prompt after its prompt pipeline. The
|
||||
important invariant is that both context engine and hooks get a deterministic,
|
||||
documented order.
|
||||
|
||||
Recommended order for first implementation:
|
||||
|
||||
1. `buildDeveloperInstructions(params)`
|
||||
2. context-engine `assemble()`
|
||||
3. append/prepend `systemPromptAddition` to developer instructions
|
||||
4. project assembled messages into prompt text
|
||||
5. `resolveAgentHarnessBeforePromptBuildResult(...)`
|
||||
6. pass final developer instructions to `startOrResumeThread(...)`
|
||||
7. pass final prompt text to `buildTurnStartParams(...)`
|
||||
|
||||
The spec should be encoded in tests so future changes do not reorder it by
|
||||
accident.
|
||||
|
||||
### 5. Preserve prompt-cache stable formatting
|
||||
|
||||
The projection helper must produce byte-stable output for identical inputs:
|
||||
|
||||
- stable message order
|
||||
- stable role labels
|
||||
- no generated timestamps
|
||||
- no object key order leakage
|
||||
- no random delimiters
|
||||
- no per-run ids
|
||||
|
||||
Use fixed delimiters and explicit sections.
|
||||
|
||||
### 6. Wire post-turn after transcript mirroring
|
||||
|
||||
Codex's `CodexAppServerEventProjector` builds a local `messagesSnapshot` for the
|
||||
current turn. `mirrorTranscriptBestEffort(...)` writes that snapshot into the
|
||||
OpenClaw transcript mirror.
|
||||
|
||||
After mirroring succeeds or fails, call the context-engine finalizer with the
|
||||
best available message snapshot:
|
||||
|
||||
- Prefer full mirrored session context after the write, because `afterTurn`
|
||||
expects the session snapshot, not only the current turn.
|
||||
- Fall back to `historyMessages + result.messagesSnapshot` if the session file
|
||||
cannot be reopened.
|
||||
|
||||
Pseudo-flow:
|
||||
|
||||
```ts
|
||||
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,
|
||||
});
|
||||
```
|
||||
|
||||
If mirroring fails, still call `afterTurn` with the fallback snapshot, but log
|
||||
that the context engine is ingesting from fallback turn data.
|
||||
|
||||
### 7. Normalize usage and prompt-cache runtime context
|
||||
|
||||
Codex results include normalized usage from app-server token notifications when
|
||||
available. Pass that usage into the context-engine runtime context.
|
||||
|
||||
If Codex app-server eventually exposes cache read/write details, map them into
|
||||
`ContextEnginePromptCacheInfo`. Until then, omit `promptCache` rather than
|
||||
inventing zeros.
|
||||
|
||||
### 8. Compaction policy
|
||||
|
||||
There are two compaction systems:
|
||||
|
||||
1. OpenClaw context-engine `compact()`
|
||||
2. Codex app-server native `thread/compact/start`
|
||||
|
||||
Do not silently conflate them.
|
||||
|
||||
#### `/compact` and explicit OpenClaw compaction
|
||||
|
||||
When the selected context engine has `info.ownsCompaction === true`, explicit
|
||||
OpenClaw compaction should prefer the context engine's `compact()` result for
|
||||
the OpenClaw transcript mirror and plugin state.
|
||||
|
||||
When the selected Codex harness has a native thread binding, we may additionally
|
||||
request Codex native compaction to keep the app-server thread healthy, but this
|
||||
must be reported as a separate backend action in details.
|
||||
|
||||
Recommended behavior:
|
||||
|
||||
- If `contextEngine.info.ownsCompaction === true`:
|
||||
- call context-engine `compact()` first
|
||||
- then best-effort call Codex native compaction when a thread binding exists
|
||||
- return the context-engine result as the primary result
|
||||
- include Codex native compaction status in `details.codexNativeCompaction`
|
||||
- If the active context engine does not own compaction:
|
||||
- preserve current Codex native compaction behavior
|
||||
|
||||
This likely requires changing `extensions/codex/src/app-server/compact.ts` or
|
||||
wrapping it from the generic compaction path, depending on where
|
||||
`maybeCompactAgentHarnessSession(...)` is invoked.
|
||||
|
||||
#### In-turn Codex native contextCompaction events
|
||||
|
||||
Codex may emit `contextCompaction` item events during a turn. Keep the current
|
||||
before/after compaction hook emission in `event-projector.ts`, but do not treat
|
||||
that as a completed context-engine compaction.
|
||||
|
||||
For engines that own compaction, emit an explicit diagnostic when Codex performs
|
||||
native compaction anyway:
|
||||
|
||||
- stream/event name: existing `compaction` stream is acceptable
|
||||
- details: `{ backend: "codex-app-server", ownsCompaction: true }`
|
||||
|
||||
This makes the split auditable.
|
||||
|
||||
### 9. Session reset and binding behavior
|
||||
|
||||
The existing Codex harness `reset(...)` clears the Codex app-server binding from
|
||||
the OpenClaw session file. Preserve that behavior.
|
||||
|
||||
Also ensure context-engine state cleanup continues to happen through existing
|
||||
OpenClaw session lifecycle paths. Do not add Codex-specific cleanup unless the
|
||||
context-engine lifecycle currently misses reset/delete events for all harnesses.
|
||||
|
||||
### 10. Error handling
|
||||
|
||||
Follow built-in OpenClaw semantics:
|
||||
|
||||
- bootstrap failures warn and continue
|
||||
- assemble failures warn and fall back to unassembled pipeline messages/prompt
|
||||
- afterTurn/ingest failures warn and mark post-turn finalization unsuccessful
|
||||
- maintenance runs only after successful, non-aborted, non-yield turns
|
||||
- compaction errors should not be retried as fresh prompts
|
||||
|
||||
Codex-specific additions:
|
||||
|
||||
- If context projection fails, warn and fall back to the original prompt.
|
||||
- If transcript mirror fails, still attempt context-engine finalization with
|
||||
fallback messages.
|
||||
- If Codex native compaction fails after context-engine compaction succeeds,
|
||||
do not fail the whole OpenClaw compaction when the context engine is primary.
|
||||
|
||||
## Test plan
|
||||
|
||||
### Unit tests
|
||||
|
||||
Add tests under `extensions/codex/src/app-server`:
|
||||
|
||||
1. `run-attempt.context-engine.test.ts`
|
||||
- Codex calls `bootstrap` when a session file exists.
|
||||
- Codex calls `assemble` with mirrored messages, token budget, tool names,
|
||||
citations mode, model id, and prompt.
|
||||
- `systemPromptAddition` is included in developer instructions.
|
||||
- Assembled messages are projected into the prompt before current request.
|
||||
- Codex calls `afterTurn` after transcript mirroring.
|
||||
- Without `afterTurn`, Codex calls `ingestBatch` or per-message `ingest`.
|
||||
- Turn maintenance runs after successful turns.
|
||||
- Turn maintenance does not run on prompt error, abort, or yield abort.
|
||||
|
||||
2. `context-engine-projection.test.ts`
|
||||
- stable output for identical inputs
|
||||
- no duplicate current prompt when assembled history includes it
|
||||
- handles empty history
|
||||
- preserves role order
|
||||
- includes system prompt addition only in developer instructions
|
||||
|
||||
3. `compact.context-engine.test.ts`
|
||||
- owning context engine primary result wins
|
||||
- Codex native compaction status appears in details when also attempted
|
||||
- Codex native failure does not fail owning context-engine compaction
|
||||
- non-owning context engine preserves current native compaction behavior
|
||||
|
||||
### Existing tests to update
|
||||
|
||||
- `extensions/codex/src/app-server/run-attempt.test.ts` if present, otherwise
|
||||
nearest Codex app-server run tests.
|
||||
- `extensions/codex/src/app-server/event-projector.test.ts` only if compaction
|
||||
event details change.
|
||||
- `src/agents/harness/selection.test.ts` should not need changes unless config
|
||||
behavior changes; it should remain stable.
|
||||
- Built-in harness context-engine tests should continue to pass unchanged.
|
||||
|
||||
### Integration / live tests
|
||||
|
||||
Add or extend live Codex harness smoke tests:
|
||||
|
||||
- configure `plugins.slots.contextEngine` to a test engine
|
||||
- configure `agents.defaults.model` to a `codex/*` model
|
||||
- configure provider/model `agentRuntime.id = "codex"`
|
||||
- assert test engine observed:
|
||||
- bootstrap
|
||||
- assemble
|
||||
- afterTurn or ingest
|
||||
- maintenance
|
||||
|
||||
Avoid requiring lossless-claw in OpenClaw core tests. Use a small in-repo fake
|
||||
context engine plugin.
|
||||
|
||||
## Observability
|
||||
|
||||
Add debug logs around Codex context-engine lifecycle calls:
|
||||
|
||||
- `codex context engine bootstrap started/completed/failed`
|
||||
- `codex context engine assemble applied`
|
||||
- `codex context engine finalize completed/failed`
|
||||
- `codex context engine maintenance skipped` with reason
|
||||
- `codex native compaction completed alongside context-engine compaction`
|
||||
|
||||
Avoid logging full prompts or transcript contents.
|
||||
|
||||
Add structured fields where useful:
|
||||
|
||||
- `sessionId`
|
||||
- `sessionKey` redacted or omitted according to existing logging practice
|
||||
- `engineId`
|
||||
- `threadId`
|
||||
- `turnId`
|
||||
- `assembledMessageCount`
|
||||
- `estimatedTokens`
|
||||
- `hasSystemPromptAddition`
|
||||
|
||||
## Migration / compatibility
|
||||
|
||||
This should be backward-compatible:
|
||||
|
||||
- If no context engine is configured, legacy context engine behavior should be
|
||||
equivalent to today's Codex harness behavior.
|
||||
- If context-engine `assemble` fails, Codex should continue with the original
|
||||
prompt path.
|
||||
- Existing Codex thread bindings should remain valid.
|
||||
- Dynamic tool fingerprinting should not include context-engine output; otherwise
|
||||
every context change could force a new Codex thread. Only the tool catalog
|
||||
should affect the dynamic tool fingerprint.
|
||||
|
||||
## Open questions
|
||||
|
||||
1. Should assembled context be injected entirely into the user prompt, entirely
|
||||
into developer instructions, or split?
|
||||
|
||||
Recommendation: split. Put `systemPromptAddition` in developer instructions;
|
||||
put assembled transcript context in the user prompt wrapper. This best matches
|
||||
the current Codex protocol without mutating native thread history.
|
||||
|
||||
2. Should Codex native compaction be disabled when a context engine owns
|
||||
compaction?
|
||||
|
||||
Recommendation: no, not initially. Codex native compaction may still be
|
||||
necessary to keep the app-server thread alive. But it must be reported as
|
||||
native Codex compaction, not as context-engine compaction.
|
||||
|
||||
3. Should `before_prompt_build` run before or after context-engine assembly?
|
||||
|
||||
Recommendation: after context-engine projection for Codex, so generic harness
|
||||
hooks see the actual prompt/developer instructions Codex will receive. If
|
||||
built-in harness parity requires the opposite, encode the chosen order in
|
||||
tests and document it here.
|
||||
|
||||
4. Can Codex app-server accept a future structured context/history override?
|
||||
|
||||
Unknown. If it can, replace the text projection layer with that protocol and
|
||||
keep the lifecycle calls unchanged.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- A `codex/*` embedded harness turn invokes the selected context engine's
|
||||
assemble lifecycle.
|
||||
- A context-engine `systemPromptAddition` affects Codex developer instructions.
|
||||
- Assembled context affects the Codex turn input deterministically.
|
||||
- Successful Codex turns call `afterTurn` or ingest fallback.
|
||||
- Successful Codex turns run context-engine turn maintenance.
|
||||
- Failed/aborted/yield-aborted turns do not run turn maintenance.
|
||||
- Context-engine-owned compaction remains primary for OpenClaw/plugin state.
|
||||
- Codex native compaction remains auditable as native Codex behavior.
|
||||
- Existing built-in harness context-engine behavior is unchanged.
|
||||
- Existing Codex harness behavior is unchanged when no non-legacy context engine
|
||||
is selected or when assembly fails.
|
||||
284
docs/plan/ui-channels.md
Normal file
284
docs/plan/ui-channels.md
Normal file
@@ -0,0 +1,284 @@
|
||||
---
|
||||
summary: Decouple semantic message presentation from channel native UI renderers.
|
||||
title: Channel presentation refactor plan
|
||||
read_when:
|
||||
- Refactoring channel message UI, interactive payloads, or native channel renderers
|
||||
- Changing message tool capabilities, delivery hints, or cross-context markers
|
||||
- Debugging Discord Carbon import fanout or channel plugin runtime laziness
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
Implemented for the shared agent, CLI, plugin capability, and outbound delivery surfaces:
|
||||
|
||||
- `ReplyPayload.presentation` carries semantic message UI.
|
||||
- `ReplyPayload.delivery.pin` carries sent-message pin requests.
|
||||
- Shared message actions expose `presentation`, `delivery`, and `pin` instead of provider-native `components`, `blocks`, `buttons`, or `card`.
|
||||
- Core renders or auto-degrades presentation through plugin-declared outbound capabilities.
|
||||
- Discord, Slack, Telegram, Mattermost, MS Teams, and Feishu renderers consume the generic contract.
|
||||
- Discord channel control-plane code no longer imports Carbon-backed UI containers.
|
||||
|
||||
Canonical docs now live in [Message Presentation](/plugins/message-presentation).
|
||||
Keep this plan as historical implementation context; update the canonical guide
|
||||
for contract, renderer, or fallback behavior changes.
|
||||
|
||||
## Problem
|
||||
|
||||
Channel UI is currently split across several incompatible surfaces:
|
||||
|
||||
- Core owns a Discord-shaped cross-context renderer hook through `buildCrossContextComponents`.
|
||||
- Discord `channel.ts` can import native Carbon UI through `DiscordUiContainer`, which pulls runtime UI dependencies into the channel plugin control plane.
|
||||
- The agent and CLI expose native payload escape hatches such as Discord `components`, Slack `blocks`, Telegram or Mattermost `buttons`, and Teams or Feishu `card`.
|
||||
- `ReplyPayload.channelData` carries both transport hints and native UI envelopes.
|
||||
- The generic `interactive` model exists, but it is narrower than the richer layouts already used by Discord, Slack, Teams, Feishu, LINE, Telegram, and Mattermost.
|
||||
|
||||
This makes core aware of native UI shapes, weakens plugin runtime laziness, and gives agents too many provider-specific ways to express the same message intent.
|
||||
|
||||
## Goals
|
||||
|
||||
- Core decides the best semantic presentation for a message from declared capabilities.
|
||||
- Extensions declare capabilities and render semantic presentation into native transport payloads.
|
||||
- Web Control UI remains separate from chat native UI.
|
||||
- Native channel payloads are not exposed through the shared agent or CLI message surface.
|
||||
- Unsupported presentation features auto-degrade to the best text representation.
|
||||
- Delivery behavior such as pinning a sent message is generic delivery metadata, not presentation.
|
||||
|
||||
## Non goals
|
||||
|
||||
- No backwards compatibility shim for `buildCrossContextComponents`.
|
||||
- No public native escape hatches for `components`, `blocks`, `buttons`, or `card`.
|
||||
- No core imports of channel-native UI libraries.
|
||||
- No provider-specific SDK seams for bundled channels.
|
||||
|
||||
## Target model
|
||||
|
||||
Add a core-owned `presentation` field to `ReplyPayload`.
|
||||
|
||||
```ts
|
||||
type MessagePresentationTone = "neutral" | "info" | "success" | "warning" | "danger";
|
||||
|
||||
type MessagePresentation = {
|
||||
tone?: MessagePresentationTone;
|
||||
title?: string;
|
||||
blocks: MessagePresentationBlock[];
|
||||
};
|
||||
|
||||
type MessagePresentationBlock =
|
||||
| { type: "text"; text: string }
|
||||
| { type: "context"; text: string }
|
||||
| { type: "divider" }
|
||||
| { type: "buttons"; buttons: MessagePresentationButton[] }
|
||||
| { type: "select"; placeholder?: string; options: MessagePresentationOption[] };
|
||||
|
||||
type MessagePresentationButton = {
|
||||
label: string;
|
||||
value?: string;
|
||||
url?: string;
|
||||
style?: "primary" | "secondary" | "success" | "danger";
|
||||
};
|
||||
|
||||
type MessagePresentationOption = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
```
|
||||
|
||||
`interactive` becomes a subset of `presentation` during migration:
|
||||
|
||||
- `interactive` text block maps to `presentation.blocks[].type = "text"`.
|
||||
- `interactive` buttons block maps to `presentation.blocks[].type = "buttons"`.
|
||||
- `interactive` select block maps to `presentation.blocks[].type = "select"`.
|
||||
|
||||
The external agent and CLI schemas now use `presentation`; `interactive` remains an internal legacy parser/rendering helper for existing reply producers.
|
||||
The public producer-facing API treats `interactive` as deprecated. Runtime
|
||||
support remains so existing approval helpers and older plugins continue to
|
||||
work while new code emits `presentation`.
|
||||
|
||||
## Delivery metadata
|
||||
|
||||
Add a core-owned `delivery` field for send behavior that is not UI.
|
||||
|
||||
```ts
|
||||
type ReplyPayloadDelivery = {
|
||||
pin?:
|
||||
| boolean
|
||||
| {
|
||||
enabled: boolean;
|
||||
notify?: boolean;
|
||||
required?: boolean;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Semantics:
|
||||
|
||||
- `delivery.pin = true` means pin the first successfully delivered message.
|
||||
- `notify` defaults to `false`.
|
||||
- `required` defaults to `false`; unsupported channels or failed pinning auto-degrade by continuing delivery.
|
||||
- Manual `pin`, `unpin`, and `list-pins` message actions remain for existing messages.
|
||||
|
||||
Current Telegram ACP topic binding should move from `channelData.telegram.pin = true` to `delivery.pin = true`.
|
||||
|
||||
## Runtime capability contract
|
||||
|
||||
Add presentation and delivery render hooks to the runtime outbound adapter, not the control-plane channel plugin.
|
||||
|
||||
```ts
|
||||
type ChannelPresentationCapabilities = {
|
||||
supported: boolean;
|
||||
buttons?: boolean;
|
||||
selects?: boolean;
|
||||
context?: boolean;
|
||||
divider?: boolean;
|
||||
tones?: MessagePresentationTone[];
|
||||
limits?: {
|
||||
actions?: {
|
||||
maxActions?: number;
|
||||
maxActionsPerRow?: number;
|
||||
maxRows?: number;
|
||||
maxLabelLength?: number;
|
||||
maxValueBytes?: number;
|
||||
supportsStyles?: boolean;
|
||||
supportsDisabled?: boolean;
|
||||
supportsLayoutHints?: boolean;
|
||||
};
|
||||
selects?: {
|
||||
maxOptions?: number;
|
||||
maxLabelLength?: number;
|
||||
maxValueBytes?: number;
|
||||
};
|
||||
text?: {
|
||||
maxLength?: number;
|
||||
encoding?: "characters" | "utf8-bytes" | "utf16-units";
|
||||
markdownDialect?: "plain" | "markdown" | "html" | "slack-mrkdwn" | "discord-markdown";
|
||||
supportsEdit?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
type ChannelDeliveryCapabilities = {
|
||||
pinSentMessage?: boolean;
|
||||
};
|
||||
|
||||
type ChannelOutboundAdapter = {
|
||||
presentationCapabilities?: ChannelPresentationCapabilities;
|
||||
|
||||
renderPresentation?: (params: {
|
||||
payload: ReplyPayload;
|
||||
presentation: MessagePresentation;
|
||||
ctx: ChannelOutboundSendContext;
|
||||
}) => ReplyPayload | null;
|
||||
|
||||
deliveryCapabilities?: ChannelDeliveryCapabilities;
|
||||
|
||||
pinDeliveredMessage?: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
to: string;
|
||||
threadId?: string | number | null;
|
||||
messageId: string;
|
||||
notify: boolean;
|
||||
}) => Promise<void>;
|
||||
};
|
||||
```
|
||||
|
||||
Core behavior:
|
||||
|
||||
- Resolve target channel and runtime adapter.
|
||||
- Ask for presentation capabilities.
|
||||
- Degrade unsupported blocks and apply generic capability limits before
|
||||
rendering.
|
||||
- Call `renderPresentation`.
|
||||
- If no renderer exists, convert presentation to text fallback.
|
||||
- After successful send, call `pinDeliveredMessage` when `delivery.pin` is requested and supported.
|
||||
|
||||
## Channel mapping
|
||||
|
||||
Discord:
|
||||
|
||||
- Render `presentation` to components v2 and Carbon containers in runtime-only modules.
|
||||
- Keep accent color helpers in light modules.
|
||||
- Remove `DiscordUiContainer` imports from channel plugin control-plane code.
|
||||
|
||||
Slack:
|
||||
|
||||
- Render `presentation` to Block Kit.
|
||||
- Remove agent and CLI `blocks` input.
|
||||
|
||||
Telegram:
|
||||
|
||||
- Render text, context, and dividers as text.
|
||||
- Render actions and select as inline keyboards when configured and allowed for the target surface.
|
||||
- Use text fallback when inline buttons are disabled.
|
||||
- Move ACP topic pinning to `delivery.pin`.
|
||||
|
||||
Mattermost:
|
||||
|
||||
- Render actions as interactive buttons where configured.
|
||||
- Render other blocks as text fallback.
|
||||
|
||||
MS Teams:
|
||||
|
||||
- Render `presentation` to Adaptive Cards.
|
||||
- Keep manual pin/unpin/list-pins actions.
|
||||
- Optionally implement `pinDeliveredMessage` if Graph support is reliable for the target conversation.
|
||||
|
||||
Feishu:
|
||||
|
||||
- Render `presentation` to interactive cards.
|
||||
- Keep manual pin/unpin/list-pins actions.
|
||||
- Optionally implement `pinDeliveredMessage` for sent-message pinning if API behavior is reliable.
|
||||
|
||||
LINE:
|
||||
|
||||
- Render `presentation` to Flex or template messages where possible.
|
||||
- Fall back to text for unsupported blocks.
|
||||
- Remove LINE UI payloads from `channelData`.
|
||||
|
||||
Plain or limited channels:
|
||||
|
||||
- Convert presentation to text with conservative formatting.
|
||||
|
||||
## Refactor steps
|
||||
|
||||
1. Reapply the Discord release fix that splits `ui-colors.ts` from Carbon-backed UI and removes `DiscordUiContainer` from `extensions/discord/src/channel.ts`.
|
||||
2. Add `presentation` and `delivery` to `ReplyPayload`, outbound payload normalization, delivery summaries, and hook payloads.
|
||||
3. Add `MessagePresentation` schema and parser helpers in a narrow SDK/runtime subpath.
|
||||
4. Replace message capabilities `buttons`, `cards`, `components`, and `blocks` with semantic presentation capabilities.
|
||||
5. Add runtime outbound adapter hooks for presentation render and delivery pinning.
|
||||
6. Replace cross-context component construction with `buildCrossContextPresentation`.
|
||||
7. Delete `src/infra/outbound/channel-adapters.ts` and remove `buildCrossContextComponents` from channel plugin types.
|
||||
8. Change `maybeApplyCrossContextMarker` to attach `presentation` instead of native params.
|
||||
9. Update plugin-dispatch send paths to consume only semantic presentation and delivery metadata.
|
||||
10. Remove agent and CLI native payload params: `components`, `blocks`, `buttons`, and `card`.
|
||||
11. Remove SDK helpers that create native message-tool schemas, replacing them with presentation schema helpers.
|
||||
12. Remove UI/native envelopes from `channelData`; keep only transport metadata until each remaining field is reviewed.
|
||||
13. Migrate Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, and LINE renderers.
|
||||
14. Update docs for message CLI, channel pages, plugin SDK, and capability cookbook.
|
||||
15. Run import fanout profiling for Discord and affected channel entrypoints.
|
||||
|
||||
Steps 1-11 and 13-14 are implemented in this refactor for the shared agent, CLI, plugin capability, and outbound adapter contracts. Step 12 remains a deeper internal cleanup pass for provider-private `channelData` transport envelopes. Step 15 remains follow-up validation if we want quantified import-fanout numbers beyond the type/test gate.
|
||||
|
||||
## Tests
|
||||
|
||||
Add or update:
|
||||
|
||||
- Presentation normalization tests.
|
||||
- Presentation auto-degrade tests for unsupported blocks.
|
||||
- Cross-context marker tests for plugin dispatch and core delivery paths.
|
||||
- Channel render matrix tests for Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE, and text fallback.
|
||||
- Message tool schema tests proving native fields are gone.
|
||||
- CLI tests proving native flags are gone.
|
||||
- Discord entrypoint import-laziness regression covering Carbon.
|
||||
- Delivery pin tests covering Telegram and generic fallback.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Should `delivery.pin` be implemented for Discord, Slack, MS Teams, and Feishu in the first pass, or only Telegram first?
|
||||
- Should `delivery` eventually absorb existing fields such as `replyToId`, `replyToCurrent`, `silent`, and `audioAsVoice`, or stay focused on post-send behaviors?
|
||||
- Should presentation support images or file references directly, or should media remain separate from UI layout for now?
|
||||
|
||||
## Related
|
||||
|
||||
- [Channels overview](/channels)
|
||||
- [Message presentation](/plugins/message-presentation)
|
||||
Reference in New Issue
Block a user