mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
docs: clarify tool result details persistence
This commit is contained in:
@@ -77,6 +77,19 @@ gateway-backed session transcript, so they are the source of truth.
|
||||
|
||||
Details: [Session management](/concepts/session).
|
||||
|
||||
## Tool result metadata
|
||||
|
||||
Tool result `content` is the model-visible result. Tool result `details` is
|
||||
runtime metadata for UI rendering, diagnostics, media delivery, and plugins.
|
||||
|
||||
OpenClaw keeps that boundary explicit:
|
||||
|
||||
- `toolResult.details` is stripped before provider replay and compaction input.
|
||||
- Persisted session transcripts keep only bounded `details`; oversized metadata
|
||||
is replaced with a compact summary marked `persistedDetailsTruncated: true`.
|
||||
- Plugins and tools should put text the model must read in `content`, not only
|
||||
in `details`.
|
||||
|
||||
## Inbound bodies and history context
|
||||
|
||||
OpenClaw separates the **prompt body** from the **command body**:
|
||||
|
||||
@@ -147,6 +147,21 @@ Rules:
|
||||
- `onResolution` receives the resolved approval decision — `allow-once`,
|
||||
`allow-always`, `deny`, `timeout`, or `cancelled`.
|
||||
|
||||
### Tool result persistence
|
||||
|
||||
Tool results can include structured `details` for UI rendering, diagnostics,
|
||||
media routing, or plugin-owned metadata. Treat `details` as runtime metadata,
|
||||
not prompt content:
|
||||
|
||||
- OpenClaw strips `toolResult.details` before provider replay and compaction
|
||||
input so metadata does not become model context.
|
||||
- Persisted session entries keep only bounded `details`. Oversized details are
|
||||
replaced with a compact summary and `persistedDetailsTruncated: true`.
|
||||
- `tool_result_persist` and `before_message_write` run before the final
|
||||
persistence cap. Hooks should still keep returned `details` small and avoid
|
||||
placing prompt-relevant text only in `details`; put model-visible tool output
|
||||
in `content`.
|
||||
|
||||
## Prompt and model hooks
|
||||
|
||||
Use the phase-specific hooks for new plugins:
|
||||
|
||||
@@ -44,6 +44,10 @@ function resolveMaxToolResultChars(opts?: { maxToolResultChars?: number }): numb
|
||||
return Math.max(1, opts?.maxToolResultChars ?? DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS);
|
||||
}
|
||||
|
||||
// `details` is runtime/UI metadata, not model-visible tool output. Keep the
|
||||
// session JSONL useful for debugging without letting metadata blobs dominate
|
||||
// disk, replay repair, transcript broadcasts, or future tooling that reads raw
|
||||
// sessions. Model-visible text belongs in tool result `content`.
|
||||
const MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES = 8_192;
|
||||
const MAX_PERSISTED_DETAIL_STRING_CHARS = 2_000;
|
||||
const MAX_PERSISTED_DETAIL_SESSION_COUNT = 10;
|
||||
@@ -102,6 +106,9 @@ function buildPersistedDetailsFallback(
|
||||
originalSize: BoundedJsonUtf8Bytes,
|
||||
sanitizedBytes?: number,
|
||||
): Record<string, unknown> {
|
||||
// If even the structured summary is too large, keep only shape and stable
|
||||
// status fields. This preserves "what happened?" without persisting the raw
|
||||
// diagnostics payload that caused the cap to trip.
|
||||
const fallback: Record<string, unknown> = {
|
||||
persistedDetailsTruncated: true,
|
||||
finalDetailsTruncated: true,
|
||||
@@ -150,6 +157,8 @@ function sanitizeToolResultDetailsForPersistence(details: unknown): unknown {
|
||||
if (details === undefined || details === null) {
|
||||
return details;
|
||||
}
|
||||
// Measure with an early-exit walker so hostile or enormous details do not
|
||||
// need to be fully stringified just to learn they exceed the persistence cap.
|
||||
const originalSize = boundedJsonUtf8Bytes(details, MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES);
|
||||
if (originalSize.complete && originalSize.bytes <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {
|
||||
return details;
|
||||
|
||||
Reference in New Issue
Block a user