How a user message flows through string assembly, the pi-agent-core harness, stream wrapping, tool execution, and compaction — with full lineage tracking.
| Component / Function | Source File | Lines | Description |
|---|---|---|---|
| buildAgentSystemPrompt() | src/agents/system-prompt.ts | 439–1048 | Master builder — orchestrates all sections in order |
| buildFullBootstrapPromptLines() | src/agents/bootstrap-prompt.ts | 1–13 | Full bootstrap: read BOOTSTRAP.md, complete workflow |
| buildLimitedBootstrapPromptLines() | src/agents/bootstrap-prompt.ts | 15–25 | Limited: explain blocker, suggest next step |
| BOOTSTRAP.md content | .pi/prompts/cl.md | 58 lines | Changelog audit skill — audit entries before release |
| BOOTSTRAP.md content | .pi/prompts/is.md | 22 lines | Issue analysis skill — audit GitHub issues |
| buildBootstrapPromptWarning() | src/agents/bootstrap-budget.ts | — | Prepends "⚠️ Bootstrap budget..." warning if near limit |
| buildSkillsSection() | src/agents/system-prompt.ts | 156–172 | Skills instructions: "scan available_skills..." |
| buildMemorySection() | src/agents/system-prompt.ts | 174–187 | Memory citations guidance via plugins/memory-state.ts |
| buildDocsSection() | src/agents/system-prompt.ts | 395–425 | Docs links: docs.openclaw.ai, GitHub, Discord, ClawhHub |
| buildMessagingSection() | src/agents/system-prompt.ts | 335–382 | sessions_spawn, sessions_send, subagents guidance |
| buildVoiceSection() | src/agents/system-prompt.ts | 384–393 | TTS hint via buildTtsSystemPromptHint() |
| buildHeartbeatSection() | src/agents/system-prompt.ts | 127–138 | "If heartbeat poll: reply HEARTBEAT_OK" |
| buildExecutionBiasSection() | src/agents/system-prompt.ts | 299–313 | "Actionable request: act in this turn..." |
| Safety Constitution | src/agents/system-prompt.ts | 672–678 | No independent goals, prioritize safety, no manipulation |
| CLI Quick Reference | src/agents/system-prompt.ts | 777–788 | openclaw gateway status/restart, config.schema.lookup |
| Self-Update Rules | src/agents/system-prompt.ts | 793–803 | Updates only on explicit user request |
| Silent Reply Rules | src/agents/system-prompt.ts | 955–970 | SILENT_REPLY_TOKEN when nothing to say |
| buildProjectContextSection() | src/agents/system-prompt.ts | 95–125 | Loads workspace files: agents.md, soul.md, identity.md... |
| CONTEXT_FILE_ORDER map | src/agents/system-prompt.ts | 44–52 | File priority: agents.md(10) → soul.md(20) → ... → memory.md(70) |
| buildWebchatCanvasSection() | src/agents/system-prompt.ts | 275–297 | [embed...] directives for webchat only |
| buildOwnerIdentityLine() | src/agents/system-prompt.ts | 232–246 | "Authorized senders: [hash or raw numbers]" |
| buildTimeSection() | src/agents/system-prompt.ts | 248–253 | "## Current Date & Time — Time zone: {tz}" |
| SYSTEM_PROMPT_CACHE_BOUNDARY | src/agents/system-prompt-cache-boundary.ts | — | "--- SYSTEM PROMPT CACHE BOUNDARY ---" separator |
| buildRuntimeLine() | src/agents/system-prompt.ts | 1006–1048 | "Runtime: agent=X | os=Y | node=Z | model=A | channel=B" |
| Provider overrides | src/plugins/provider-runtime.js | — | transformProviderSystemPrompt() wraps final output |
CONTEXT_FILE_ORDER map then alphabetically. heartbeat.md is marked dynamic (below cache boundary).
--- description: Audit changelog entries --- Audit changelog entries for all commits since the last release. ## Process 1. Find last release tag 2. List commits since that tag 3. Read each package's [Unreleased] section 4. Check: skip changelog updates, doc-only, release housekeeping 5. Cross-package duplication rule: → duplicate to coding-agent 6. Add New Features section 7. Report missing entries
--- description: Analyze GitHub issues --- Analyze GitHub issue(s): $ARGUMENTS 1. Read the issue in full 2. For bugs: - Ignore root cause (likely wrong) - Read all related code files - Trace code path - Propose a fix 3. For features: - Read related files - Propose concise implementation - List affected files Do NOT implement unless asked.
| Phase | Lines | Key Functions | Purpose |
|---|---|---|---|
| 1. Setup | 579–610 | resolveUserPath(), AbortController, mkdir workspace | Resolve paths, create abort controller, ensure workspace exists |
| 2. Model Resolution | — | resolveModelAsync(), normalizeResolvedModel() | Resolve provider, model ID, API format (openai/anthropic/etc.) |
| 3. Sandbox | — | resolveSandboxContext() | Determine if running in Docker sandbox, mount paths |
| 4. Session Manager | 1284–1290 | SessionManager.open(), guardSessionManager() | Open/update session file, install tool result guards |
| 5. System Prompt | 1200–1201 | buildSystemPromptOverride() → buildEmbeddedSystemPrompt() | Assemble all 25+ prompt components from Section 01 |
| 6. Tool Setup | 1330–1413 | splitSdkTools(), toClientToolDefinitions(), createAgentSession() | Build tool allowlist, create session with tools |
| 7. Stream Wrapping | 1556–1866 | 10 x wrapStreamFn*() wrappers | Wrap streamFn with transforms (see Section 03) |
| 8. History Prep | 1869–1967 | sanitizeSessionHistory(), limitHistoryTurns(), filterHeartbeatPairs() | Prune, truncate, sanitize transcript before LLM call |
| 9. Hook Runner | 2258–2267 | resolvePromptBuildHookResult() | Fire llm_input hooks (async, fire-and-forget) |
| 10. Prompt Submit | 2649–2702 | activeSession.prompt() — THE CORE CALL | Send to LLM; pi-agent-core handles turn loop internally |
| 11. Compaction Wait | 2750–2784 | waitForCompactionRetryWithAggregateTimeout() | Wait for background compaction (up to 60s aggregate) |
| 12. Result Return | — | EmbeddedRunAttemptResult | Return transcript, usage metrics, compaction count |
activeSession.prompt() delegates the turn loop to @mariozechner/pi-agent-core. That library's prompt() method:
tool_use blocks → executes tools sequentiallytool_result back to the transcriptstop_reason ≠ "tool_use"| # | Wrapper Function | Source | What It Does |
|---|---|---|---|
| 1 | resolveEmbeddedAgentStreamFn() | stream-resolution.ts:64 | Selects transport: OpenAI WS, Anthropic Vertex, or pi streamSimple |
| 2 | Yield Abort Gate | attempt.ts:1778 | Intercepts "sessions_yield" abort; returns createYieldAbortedResponse() |
| 3 | wrapStreamFnSanitizeMalformedToolCalls() | attempt.ts:1791 | Fixes malformed tool call JSON in streaming response |
| 4 | wrapStreamFnTrimToolCallNames() | attempt.ts:1796 | Trims whitespace from tool names; guards against unknown-tool loop (3 strikes) |
| 5 | wrapStreamFnRepairMalformedToolCallArguments() | attempt.ts:1810 | Repairs malformed JSON args in tool call input blocks |
| 6 | wrapStreamFnDecodeXaiToolCallArguments() | attempt.ts:1816 | Decodes HTML entities (e.g. <) in xAI tool call arguments |
| 7 | createAnthropicPayloadLogger().wrapStreamFn() | anthropic-payload-log.ts | Logs full request/response payloads for debugging |
| 8 | wrapStreamFnHandleSensitiveStopReason() | attempt.ts:1829 | Catches provider-specific "sensitive" stop reasons before error bubble |
| 9 | streamWithIdleTimeout() | llm-idle-timeout.ts | Aborts if model is silent for > idleTimeoutMs (configurable per trigger) |
| 10 | wrapStreamFnWithDiagnosticModelCallEvents() | attempt.ts:1853 | Emits diagnostic events per model API call (runId, sessionId, provider...) |
| Conditional wrappers (applied additionally when conditions are met): | |||
| C1 | wrapStreamFnTextTransforms() | plugin-text-transforms.ts | Provider-specific input/output text transforms |
| C2 | cacheTrace.wrapStreamFn() | cache-trace.ts | Records prompt cache stages for observability |
| C3 | Google Prompt Cache | google-prompt-cache.ts | Uses Google's cached prompt feature if available |
compactWithSafetyTimeout() with grace period. If it overruns, run proceeds with pre-compaction state.llm_input hooks fire asynchronously before each LLM call (fire-and-forget, attempt.ts:2539–2566).createAnthropicPayloadLogger records without blocking.assembleAttemptContextEngine() preprocesses messages in parallel (attempt.ts:1923–1958).tool_result appended before the next LLM call.limitHistoryTurns() must complete before activeSession.prompt().scheduleAbortTimer() sets up timeout before prompt starts.| Selection Reason | Condition | Harness |
|---|---|---|
| pinned | agentHarnessId pinned via config | pi or plugin (forced) |
| forced_pi | runtime="pi" in policy | piHarness (embedded) |
| forced_plugin | runtime="plugin-id" (specific) | Named plugin harness |
| forced_plugin_fallback_to_pi | plugin not registered + fallback≠none | piHarness |
| auto_plugin | runtime="auto" + plugin supports provider | Highest-priority supporting plugin |
| auto_pi_fallback | runtime="auto" + no plugin supports | piHarness (built-in) |
@mariozechner:
createAgentSession(), SessionManager, DefaultResourceLoader, and the StreamFn type.| Field | Type | Description |
|---|---|---|
| sessionId | string | Unique run session ID |
| sessionKey | string | Routing key (user/session) |
| sessionFile | string | Disk path to session JSON file |
| provider | string | "anthropic" | "openai" | "openai-codex"... |
| modelId | string | "claude-sonnet-4-7-20250514" |
| model | ProviderRuntimeModel | Resolved model with api, baseUrl, contextWindow |
| prompt | string | Current user message |
| thinkLevel | ThinkLevel | "off" | "fast" | "medium" | "high" |
| reasoningLevel | ReasoningLevel | "off" | "summary" | "block" |
| timeoutMs | number | Run timeout (default: from config) |
| config | OpenClawConfig | Full runtime config |
| workspaceDir | string | Agent working directory |
| authStorage | AuthStorage | API key storage |
| toolsAllow | string[] | Explicit tool allowlist |
| abortSignal | AbortSignal | External abort from gateway |
ToolUse: { id, name, input: Record<string, unknown> }
ToolResult: { toolUseId, content: string }
StopReason: "tool_use" | "end_turn" | "max_tokens" | "stop_sequence" | ...
// Per-turn message flow:
UserMessage
→ activeSession.prompt()
→ LLM emits: tool_use{id, "read", {path:"/foo.txt"}}
→ ToolExecutor.execute("read", {path:"/foo.txt"})
→ ToolResult{toolUseId: id, content: "file contents..."}
→ transcript.push(tool_result)
→ LLM (next iteration)
→ until stop_reason ≠ "tool_use"
| File | Role | Lines |
|---|---|---|
| src/agents/system-prompt.ts | Master system prompt builder — all 25+ sections | 1,048 |
| src/agents/bootstrap-prompt.ts | Bootstrap prompt lines builder (full/limited) | 25 |
| src/agents/context.ts | Memory section and context file handling | — |
| src/agents/pi-embedded-runner/run/attempt.ts | runEmbeddedAttempt — THE core harness | 3,290 |
| src/agents/pi-embedded-runner/compact.ts | Background compaction orchestration | 1,287 |
| src/agents/pi-embedded-runner/model.ts | Model resolution + context window cache | 1,121 |
| src/agents/pi-embedded-runner/stream-resolution.ts | Stream transport selection (WS/HTTP/Vertex) | 132 |
| src/agents/harness/selection.ts | Harness selection decision tree | 399 |
| src/agents/pi-embedded-runner/run/payloads.ts | Tool call/request/result type definitions | — |
| src/plugins/provider-runtime.js | Provider plugin hooks + system prompt transforms | — |
| src/plugins/memory-state.js | Memory citations prompt section builder | — |
| src/agents/bootstrap-budget.ts | Bootstrap warning prepend logic | — |
| src/agents/session-write-lock.js | Session write lock for concurrent safety | — |
| src/agents/session-compaction-checkpoints.js | Compaction checkpoint snapshot system | — |
| .pi/prompts/cl.md | Bootstrap skill: Changelog Audit | 58 |
| .pi/prompts/is.md | Bootstrap skill: Issue Analysis | 22 |