File size: 7,448 Bytes
116524e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# Data Model: OpenClaw Integration

**Feature**: 001-openclaw-integration | **Date**: 2026-02-27

## Pipeline Steps

### LoadTracesStep (`ace/steps/load_traces.py`)



Generic step that reads a trace file from disk and puts raw content on `ctx.trace`.



| Attribute | Value |

|-----------|-------|

| `requires` | `frozenset({"sample"})` β€” `sample` is the file path (`str \| Path`) |

| `provides` | `frozenset({"trace"})` β€” raw file content (type depends on file format) |



**Behaviour**: Reads the file at `ctx.sample`, parses JSONL lines into `list[dict]`, places on `ctx.trace`. Skips unparseable lines gracefully.



### OpenClawToTraceStep (`ace/integrations/openclaw/to_trace.py`)

OpenClaw-specific step that converts raw JSONL events into a structured trace dict, preserving chronological order of queries, thinking, and tool uses.

| Attribute | Value |
|-----------|-------|
| `requires` | `frozenset({"trace"})` β€” raw `list[dict]` from LoadTracesStep |
| `provides` | `frozenset({"trace"})` β€” structured trace dict for ReflectStep |

**Behaviour**: Walks events in order, extracts message content items (text, thinking, toolCall, toolResult) preserving full content without truncation. Produces a trace dict with `{question, reasoning, answer, skill_ids, feedback, ground_truth}`. Transformation logic TBD (user will define separately).

## Entities

### SessionEvent

A single line from an OpenClaw JSONL transcript file.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `str` | yes | Event type: `"session"`, `"message"`, `"thinking_level_change"`, `"custom"` |
| `id` | `str` | yes | Unique event identifier |
| `parentId` | `str \| None` | no | Parent event ID for threading |
| `timestamp` | `str` | yes | ISO 8601 timestamp |
| `message` | `MessagePayload \| None` | no | Present when `type == "message"` |
| `version` | `int \| None` | no | Present when `type == "session"` |
| `cwd` | `str \| None` | no | Working directory (session events only) |
| `thinkingLevel` | `str \| None` | no | Present when `type == "thinking_level_change"` |
| `data` | `dict \| None` | no | Present when `type == "custom"` |

### MessagePayload

The `message` field within a `SessionEvent` of type `"message"`.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `role` | `str` | yes | `"user"`, `"assistant"`, or `"toolResult"` |
| `content` | `list[ContentItem]` | yes | Array of content items |
| `api` | `str \| None` | no | API used (e.g., `"openai-completions"`) |
| `provider` | `str \| None` | no | Provider name (e.g., `"litellm"`) |
| `model` | `str \| None` | no | Model identifier |
| `usage` | `UsageInfo \| None` | no | Token/cost tracking |
| `stopReason` | `str \| None` | no | Why generation stopped |
| `timestamp` | `int \| None` | no | Unix timestamp (ms) |

### ContentItem

An individual content block within a message's content array.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `str` | yes | `"text"`, `"thinking"`, `"toolCall"`, `"toolResult"` |
| `text` | `str \| None` | no | Present when `type == "text"` |
| `thinking` | `str \| None` | no | Present when `type == "thinking"` |
| `id` | `str \| None` | no | Tool call ID (toolCall) |
| `name` | `str \| None` | no | Tool name (toolCall) |
| `arguments` | `dict \| None` | no | Tool arguments (toolCall) |
| `toolCallId` | `str \| None` | no | Matching call ID (toolResult) |
| `content` | `list[dict] \| None` | no | Result content (toolResult) |

### Trace (dict)

The structured representation placed on `ctx.trace` by `OpenClawToTraceStep`. This is a plain dict matching the TraceAnalyser's raw trace interface.

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `question` | `str` | yes | First user message in the session |
| `reasoning` | `str` | yes | Full chronological conversation: user messages, assistant responses, thinking (full), tool calls (full), tool results (full) |
| `answer` | `str` | yes | Last assistant text response |
| `skill_ids` | `list[str]` | yes | Always `[]` (no prior skills applied) |
| `feedback` | `str` | yes | Summary string (e.g., "Session completed with N tool calls") |
| `ground_truth` | `None` | yes | Always `None` (no ground truth for open-ended sessions) |

### Skillbook (existing)

Reused from `ace.core.skillbook.Skillbook`. No changes needed.

| Field | Type | Description |
|-------|------|-------------|
| `skills` | `dict[str, Skill]` | ID β†’ Skill mapping |
| `sections` | `dict[str, list[str]]` | Section β†’ skill IDs |
| `next_id` | `int` | Counter for new skill IDs |
| `similarity_decisions` | `dict` | Deduplication cache |

### Skill (existing)

Reused from `ace.core.skillbook.Skill`. No changes needed.

| Field | Type | Description |
|-------|------|-------------|
| `id` | `str` | Format: `{section}-{5-digit-counter}` |
| `section` | `str` | Category/domain |
| `content` | `str` | Strategy description |
| `justification` | `str` | Why this strategy is valuable |
| `evidence` | `str` | Source session evidence |
| `helpful` | `int` | Positive vote count |
| `harmful` | `int` | Negative vote count |
| `neutral` | `int` | Neutral vote count |
| `created_at` | `str` | ISO 8601 timestamp |
| `updated_at` | `str` | ISO 8601 timestamp |
| `status` | `str` | `"active"` or `"invalid"` |

### ProcessedLog

Plain text file tracking which sessions have been processed.

| Aspect | Detail |
|--------|--------|
| **Path** | `~/.openclaw/ace_processed.txt` |
| **Format** | Newline-delimited session filenames (sorted) |
| **Example** | `b3db607f-7ae8-4089-b806-44800e961672.jsonl\nc4ef912a-...jsonl\n` |

## Relationships

```text

LoadTracesStep:

  ctx.sample (file path) β†’ read JSONL β†’ ctx.trace (list[dict] raw events)



OpenClawToTraceStep:

  ctx.trace (list[dict] raw events) β†’ convert β†’ ctx.trace (structured trace dict)



Pipeline composition:

  LoadTracesStep β†’ OpenClawToTraceStep β†’ ReflectStep β†’ TagStep β†’ UpdateStep β†’ ApplyStep



SessionEvent (JSONL line)

  └── contains β†’ MessagePayload

       └── contains β†’ ContentItem[]

            β”œβ”€β”€ text β†’ user messages / assistant responses (full)

            β”œβ”€β”€ thinking β†’ reasoning content (full, no truncation)

            β”œβ”€β”€ toolCall β†’ tool invocation data (full)

            └── toolResult β†’ tool output data (full)



Skillbook β†’ save_to_file() β†’ ace_skillbook.json

Skillbook β†’ wrap_skillbook_context() β†’ sync_to_agents_md() β†’ AGENTS.md

Session filenames β†’ ProcessedLog (ace_processed.txt)

```

## Validation Rules

1. **LoadTracesStep**: Skips unparseable JSONL lines. Returns empty list for empty/missing files.
2. **SessionEvent**: Events with `type != "message"` are skipped by OpenClawToTraceStep.
3. **MessagePayload**: Messages must have `role` in `{"user", "assistant"}` and non-empty `content` array.
4. **ContentItem**: Unknown `type` values are silently skipped.
5. **Trace**: Must have non-empty `question` (at least one user message). Sessions with no user messages produce `None` from OpenClawToTraceStep.
6. **No truncation**: Thinking content, tool call arguments, and tool results are all preserved in full per clarifications.