Skip to content

Read DeepSeek Harness Trajectory Logs

Trajectory · Session log · tool/call · Troubleshoot

Start at cwd and agentPreset, then find the tool/call whose callId never received a tool/result.

How to read a DeepSeek Harness Trajectory log from the session header to an unmatched tool call

Read the session header before the chat bubble

To read a DeepSeek Harness Trajectory log after a failed run, skip the last chat bubble and open the durable session log. The JSONL persistence notes describe one append-only logical log per session, stored as .jsonl.zstd by default, or raw .jsonl when compression is off. The first logical line is the session header: type: session, plus version, id, optional cwd, createdAt, optional parentSession, and a required delegationDepth. agentPreset is stored because it decides which tools and prompt a resume is allowed to use.

delegationDepth is 0 on a top-level session. A missing or invalid value rejects the log. If the number is not zero, you are inside a delegated session: a tool name that does not belong to the preset you clicked may belong to the child. parentSession points up. cwd is the workspace root that was bound — the first question on the session log intro. This page is the order you walk once that question is about a run that already failed.

The persistence catalog on the default branch pins SESSION_FORMAT_VERSION at 0 and says a pre-release format implies no compatibility. The 0.1.5 RC notes describe a session-data upgrade that cannot be read back as the old format. Those are not the same integer. Read the version field on the header you decoded. If a custom reader was written for another generation, fix the reader.

Every later line is one storage record. After decode, sequence numbers stay contiguous: the event at index i has seq === i. A gap means your reader dropped a row — often a packed chunk line, or a plugin event type it did not know — not that the model deleted history. Downstream plugins can merge event types the persistence catalog does not list.

Pair each tool/call with a tool/result

The catalog splits events into surface and log-only. Surface events become LLM messages. For this read, the ones that matter are user/message, assistant/message, and tool/result. tool/call is log-only. A chat export can show the result text and still hide the raw arguments the model emitted.

A tool/call carries turn, step, callId, name, and arguments. arguments is the raw JSON string, unparsed, exactly as the model produced it. callId pairs it with tool/result. The result carries turn, step, a tool-result message, an optional error with name and code, and optional meta. meta must be JSON. The file tool dsh-tool-fs may put a result-time diff there; absence of meta is normal for other tools.

Walk calls from the end. The failure is usually the last callId with no result, or a result whose error is set. Do not reopen three plugins at once. If the name is a tool you never meant to load, stop and use the plugin security checklist before you re-prompt.

Session timeline split between TOOL_NOT_STARTED and TOOL_OUTCOME_UNKNOWN

TOOL_NOT_STARTED or TOOL_OUTCOME_UNKNOWN

The session core notes give two recovery sentences. They are not synonyms.

  • TOOL_NOT_STARTED — an assistant tool request exists, but no durable tool/call was recorded. Upstream’s wording: the call was interrupted before the Harness recorded it as started, and it may be retried if still needed. You do not have evidence the tool ran.
  • TOOL_OUTCOME_UNKNOWN — a durable tool/call exists and no result was recorded. The outcome is unknown. Retry only if the operation is read-only or idempotent. If it may have side effects, verify external state or ask the user. Do not retry blindly.

A shell follows the same shape with different types: command/run pairs with command/done by commandId, and command/run is log-only. A run without done is the shell version of a call without a result. Check the workspace before you send “try again.”

llm/retry is also log-only: one provider retry after a failed request. A failed model request has no assistant/message. If the chat looks like the model went silent, look for llm/retry or a missing assistant message before you blame a tool. Token rows, when present, are assistant/chunk records with type usage; assistant/message.usage is only the fallback when no usage chunk exists.

Log-only rows the chat will hide

Approvals are an audit, not a bubble. approval/asked and approval/decided share an id and are both log-only. If someone says the agent ran a shell without asking, find those two rows before you argue about the UI copy. The button label in a preview build moves; the event types are what the catalog names today.

Three or more consecutive assistant/chunk deltas of the same block may be stored as one packed row: text-chunks, reasoning-chunks, or tool-call-chunks. Those tags have no slash, so they are not event types. Loading decodes them. A file with fewer lines than the highest seq is not truncated history when those packed rows are present.

A log event is not a Cordis event. The catalog says the live bus is a separate session/event emit. Grepping plugin source for a Cordis hook will not show you the durable line.

A ten-minute pass

1. Decode the log (respect .jsonl.zstd). Read header cwd, agentPreset, delegationDepth, and version.
2. Confirm seq is contiguous after decode. If it is not, fix the reader before you interpret a gap.
3. Find the last tool/call with no tool/result, or a result error.
4. Classify the gap as TOOL_NOT_STARTED or TOOL_OUTCOME_UNKNOWN before you retry.
5. If the name is unexpected, unload the last plugin. Do not add another.
6. Quote callId, name, and the header version in the bug. A screenshot of the final apology is not a template.

What this order does not decide

This is not the essay on why a flight recorder exists. That remains the session log intro. The slow path is the observability tutorial. Names on agentPreset still mean what the modes page says; this article does not pick Standard versus PTC. “The UI is up and every tool is blind” is still troubleshoot, not a missing tool/result.

If the header version or a type name on your tag disagrees with the catalog linked above, the tag wins for that build. Preview compatibility breaks on purpose. Pin the DSH version next to the plugin SHAs, and do not file a bug from a log your reader only half-decoded.

Continue on the portal

Install DSH, walk the tutorial track, or browse curated plugins after this article.