session.jsonl stores tool records with a dot inserted into absolute paths
session.jsonl stores tool records with a . inserted into absolute paths
Product: Craft Agents desktop app
Version: 0.13.3 (CFBundleShortVersionString / CFBundleVersion)
OS: macOS 26.6.2 (25G83), Apple Silicon
Repo: craft-ai-agents/craft-agents-oss
Severity: Low functional impact, high evidence-integrity impact
Labels suggestion: bug
Summary
The app-persisted transcript sessions/<slug>/session.jsonl stores a single arbitrarily inserted . inside some absolute paths in tool records. The same values are correct everywhere else — the raw runtime log, live process argv, and the filesystem. Only the persisted transcript is affected, which makes it unusable as evidence: any agent or human reading session.jsonl to verify paths gets paths that do not exist.
Scope
- Affects tool call records only — both tool inputs and tool outputs.
- Does not affect assistant message text in the same file (an absolute path written in assistant prose is stored verbatim).
- Does not affect
sessions/<slug>/.pi-sessions/*.jsonl(the raw runtime log).
That last point is the key diagnostic: the same tool call appears clean in the runtime log and mangled in session.jsonl.
Minimal reproduction
Run this in the Bash tool:
echo "MARKER_A=/Volumes/home/Drive" echo "MARKER_B=/Users/samflorentine/.craft-agent" echo "MARKER_C=/Volumes./home" echo "MARKER_D=//[email protected]/home" echo "MARKER_E=/opt/homebrew"Compare
sessions/<slug>/.pi-sessions/*.jsonlwithsessions/<slug>/session.jsonl.
| Marker | Runtime log (correct) | session.jsonl (mangled) |
|---|---|---|
| A | /Volumes/home/Drive |
/Volumes./home/Drive |
| B | /Users/samflorentine/.craft-agent |
./Users/samflorentine/.craft-agent |
| C | /Volumes./home |
/Volumes../home |
| D | //[email protected]/home |
//[email protected]./home |
| E | /opt/homebrew |
./opt/homebrew |
Note marker C: an input that already contained a . gained a second one, i.e. exactly one . is inserted per path.
Further samples (ground truth → persisted)
| Ground truth | Persisted in session.jsonl |
|---|---|
/Users/x |
./Users/x |
/Users/samflorentine/x |
./Users/samflorentine/x |
/etc/hosts |
./etc/hosts |
/var/log/x |
./var/log/x |
/Volumes/home/Databases |
/Volumes./home/Databases |
/private/tmp/x |
/private./tmp/x |
//[email protected]/home |
//[email protected]./home |
/Volumes/Databases/Eagle |
(unchanged) |
/Volumes/Media/x, /Volumes/Sam/x |
(unchanged) |
/Applications/x, /Library/x, /System/x |
(unchanged) |
/bin/ls, /usr/bin/env |
(unchanged) |
/a/bbb, /aaa/bbb, /Volumes/x, /Volumes |
(unchanged) |
~/x, relative/path/x |
(unchanged) |
Characterisation
The transform is deterministic but opaque from the outside:
- Not length-based (
/Users/xmangles,/a/bbbdoes not). - Not depth-based (
/private/tmp/xmangles,/usr/bin/envdoes not). - Not existence-based (
/etc/hostsmangles,/Library/xdoes not;/Volumes/Databases/Eagleis untouched while/Volumes/home/Databasesis mangled). - Exactly one
.is inserted per affected path. The insertion position varies: leading (./Users/...), after the first component (/Volumes./home/...), or after a host token (//host./share).
Because no rule can be derived, consumers cannot safely reverse it with a regex — a generic "strip the dot" repair would corrupt correctly-stored paths. Repair currently requires validating each candidate against the filesystem and failing closed, which is what we implemented as a workaround.
Ruled out
- Not
rtk. rtk 0.49.0 is an output-filtering CLI proxy; its help output and binary strings expose no path-rewriting feature.dist/interceptor.cjscontains zero references tortk, and itssanitize*/redact*functions handle OpenAI cache-control blocks and proxy-credential redaction only. - Not execution-time. The mangled form never reaches the shell: the live process
argvfor a command whose transcript entry is mangled shows the correct path. - Not the runtime log.
.pi-sessions/*.jsonlis consistently correct whilesession.jsonlfor the same call is mangled.
Impact
- Evidence integrity. Any workflow that reads stored transcripts to verify what a session actually did — audits, plan reviews, post-mortems — ingests paths that do not exist and can conclude that folders are missing/empty. We observed an agent audit session misdiagnosing empty results as a path typo because of this.
- Workaround cost. Consumers must know to ignore
session.jsonland read.pi-sessions/*.jsonlinstead; that is undocumented behaviour a user cannot reasonably discover. - There is no functional/data-corruption impact on task execution.
Requested resolution
Either:
- (a) persist tool records in
session.jsonlverbatim (matching the runtime log), or - (b) if the transform is intentional (e.g. sanitisation for sharing/export), document it, scope it to an explicit export/sanitise path rather than the on-disk default, and expose the inverse so stored transcripts remain usable.
Notes
- Reproduced on 2026-09-16/17 EDT; observed in three independent sessions (different models). The transform is stable across them.
- Related: any UI feature that copies a path from the transcript would copy the mangled form.
Drafted with Craft Agent (session 260916-light-birch) from a live reproduction on the reporter's machine.
Source: craft-ai-agents/craft-agents-oss