fix(agent-loop): mark finalized prefixes durable-interrupted; never finalize failed attempts
Review round findings: - Clear the streaming attempt before the request-error waterfall: a cancel landing during recovery (typically the llm/retry backoff, after clients reset the streamed rendering) must not resurrect the failed stream's prefix. Provider failures commit nothing, now including that window. - Record interrupted: true on the cancellation-finalized assistant/message. The chat projection keeps the settled prefix classified as interrupted (Stopped chip, restored web goldens), and request inspection leaves the request uncompleted so the step boundary classifies it as before. - Pin the recovery-window and retry-discard semantics with content-bearing failed streams in cancel.spec; update the ACP late-end expectation to the finalized-prefix transcript. - Mention interruptedBlocks() in the assembler stream-lifecycle JSDoc.
This commit is contained in:
1 parent
3e02c06d7f
commit
87f24bb991
31 files changed
+217
-97
No files matched your search
+2
-2
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.md
|
||||
2026-08-10-cancelled-stream-prefix-finalize.md: a2201cd17fd89744590bd4024bff80f3e3835e39
|
||||
2026-08-10-cancelled-stream-prefix-finalize.zh.md: 08abb5c097bdf3c6c21e0a96873ab4a995e8f3e9
|
||||
2026-08-10-cancelled-stream-prefix-finalize.md: 34b68ff30e399d75b2ad4417bd3e2b8c25cd5757
|
||||
2026-08-10-cancelled-stream-prefix-finalize.zh.md: febd96f8179433fb7411eed6c533e95bcfad2648
|
||||
+2
-2
@@ -12,7 +12,7 @@ The governing principle this violated: what the user can see, the next model req
|
||||
|
||||
## Decision
|
||||
|
||||
`Agent.step()` keeps the current streaming attempt (assembler, logged chunk seqs, provider route) alive across the request loop. When an abort escapes the step while an attempt is uncommitted, `appendInterruptedAssistant` finalizes the attempt's user-visible prefix as the step's ordinary `assistant/message` — `surfaceOp: 'append'`, `sourceEventSeqs` citing exactly the logged chunks — before the abort continues to the `step/end`/`turn/end` teardown. A retry decision clears the attempt first: `llm/retry` resets what clients render, so an abort after it finalizes nothing from the abandoned attempt.
|
||||
`Agent.step()` keeps the current streaming attempt (assembler, logged chunk seqs, provider route) alive across the request loop. When an abort escapes the step while an attempt is uncommitted, `appendInterruptedAssistant` finalizes the attempt's user-visible prefix as the step's `assistant/message` with `interrupted: true` — `surfaceOp: 'append'`, `sourceEventSeqs` citing exactly the logged chunks — before the abort continues to the `step/end`/`turn/end` teardown. The durable marker is the classification consumers read: the chat projection keeps rendering the settled prefix as interrupted (the Stopped chip), and request inspection leaves the request uncompleted so the step boundary classifies it as before. An attempt that ends in an `error`/`aborted` finish is cleared before the recovery waterfall runs: provider failures commit nothing, and a cancel landing during recovery (typically the `llm/retry` backoff, after clients reset the streamed rendering) must not resurrect the failed stream's prefix.
|
||||
|
||||
`BlockAssembler.interruptedBlocks()` owns what is safe to finalize, next to the existing max-tokens truncation rule: closed and open `text`/`reasoning` blocks with non-whitespace content, in stream order. Tool calls are dropped whole — interruption precedes dispatch, so a kept call would demand a fabricated result — as are empty blocks and open blocks of unknown type. When nothing survives, no event is appended and the turn keeps its previous shape: chunks, `step/end`, `turn/end` aborted.
|
||||
|
||||
@@ -30,7 +30,7 @@ Cancellation during tool execution is untouched: the tool-call message was alrea
|
||||
|
||||
## Consequences
|
||||
|
||||
The surface now contains what the user saw at the moment of cancellation, so post-cancel follow-ups and forks connect. The cancel and goal snapshot fixtures record the finalized prefix event, and the ACP bridge forwards it as a final `agent_message_chunk` update after the cancelled stop reason. An interrupted step's `assistant/message` can now carry a mid-sentence prefix; consumers reading the aborted `turn/end` can classify it. Terminal provider errors keep the old behavior — their streamed prefix still vanishes from the surface — an asymmetry deliberately left for a follow-up decision because error turns end without the user choosing to stop.
|
||||
The surface now contains what the user saw at the moment of cancellation, so post-cancel follow-ups and forks connect. The cancel and goal snapshot fixtures record the finalized prefix event, and the ACP bridge forwards it as a final `agent_message_chunk` update after the cancelled stop reason — prompt settlement does not wait on loop teardown, so automation clients may receive the update after the cancelled stop reason. An interrupted step's `assistant/message` carries a mid-sentence prefix and the `interrupted: true` marker that classifies it. Terminal provider errors keep the old behavior — their streamed prefix still vanishes from the surface — an asymmetry deliberately left for a follow-up decision because error turns end without the user choosing to stop.
|
||||
|
||||
## Testing
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ Status: implemented
|
||||
|
||||
## Decision
|
||||
|
||||
`Agent.step()` 让当前流式尝试(assembler、已记录的分片 seq、提供方路由)在请求循环之间保持存活。当 abort 在尝试未提交时逃出 step,`appendInterruptedAssistant` 会在 abort 继续走向 `step/end`/`turn/end` 收尾之前,把该尝试的用户可见前缀定稿为该 step 的普通 `assistant/message`,`surfaceOp: 'append'`,`sourceEventSeqs` 恰好引用已记录的分片。重试决定会先清空尝试:`llm/retry` 会重置客户端渲染的内容,因此其后的 abort 不会从被放弃的尝试中定稿任何东西。
|
||||
`Agent.step()` 让当前流式尝试(assembler、已记录的分片 seq、提供方路由)在请求循环之间保持存活。当 abort 在尝试未提交时逃出 step,`appendInterruptedAssistant` 会在 abort 继续走向 `step/end`/`turn/end` 收尾之前,把该尝试的用户可见前缀定稿为该 step 的带 `interrupted: true` 的 `assistant/message`,`surfaceOp: 'append'`,`sourceEventSeqs` 恰好引用已记录的分片。这个持久标记就是消费者读取的分类:chat 投影继续把定稿前缀渲染为被打断(Stopped 徽章),请求检查让该请求保持未完成,由 step 边界照旧归类。以 `error`/`aborted` finish 结束的尝试会在恢复 waterfall 运行前被清空:提供方故障不提交任何内容,落在恢复期间的取消(典型是 `llm/retry` 退避期,此时客户端已重置流式渲染)不得复活失败流的前缀。
|
||||
|
||||
`BlockAssembler.interruptedBlocks()` 拥有「什么可以安全定稿」的规则,与既有的 max-tokens 截断规则放在一起:按流顺序保留内容非空白的已闭合与未闭合 `text`/`reasoning` 块。工具调用整块丢弃,因为打断先于分派,保留的调用会要求捏造一个结果;空块和未知类型的未闭合块同样丢弃。没有内容存活时不追加任何事件,轮次保持原有形状:分片、`step/end`、`turn/end` aborted。
|
||||
|
||||
@@ -30,7 +30,7 @@ Status: implemented
|
||||
|
||||
## Consequences
|
||||
|
||||
surface 现在包含取消瞬间用户看到的内容,取消后的追问和 fork 都能接上。cancel 与 goal 两组快照 fixture 记录了定稿前缀事件,ACP 桥在 cancelled stop reason 之后把它作为最后一条 `agent_message_chunk` 更新转发。被打断 step 的 `assistant/message` 现在可能带着一个中途截断的前缀;消费者读到 aborted 的 `turn/end` 即可归类。终局提供方错误保持旧行为,其已流出前缀仍会从 surface 消失,这个不对称是有意留给后续决定的,因为 error 轮次的结束不是用户主动选择的停止。
|
||||
surface 现在包含取消瞬间用户看到的内容,取消后的追问和 fork 都能接上。cancel 与 goal 两组快照 fixture 记录了定稿前缀事件,ACP 桥在 cancelled stop reason 之后把它作为最后一条 `agent_message_chunk` 更新转发,prompt 的结算不等待循环收尾,因此自动化客户端可能在 cancelled stop reason 之后才收到该更新。被打断 step 的 `assistant/message` 带着中途截断的前缀和用于归类的 `interrupted: true` 标记。终局提供方错误保持旧行为,其已流出前缀仍会从 surface 消失,这个不对称是有意留给后续决定的,因为 error 轮次的结束不是用户主动选择的停止。
|
||||
|
||||
## Testing
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
- img
|
||||
- button "Branch into a new conversation":
|
||||
- img
|
||||
- text: {{clock}} Ran for {{duration}}
|
||||
- text: {{clock}} Ran for {{duration}} TTFT {{duration}}
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
@@ -28,4 +28,4 @@
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
- button "Send message" [disabled]
|
||||
- text: 1 turns · 1 steps Input 0 tok · Output 0 tok
|
||||
- text: 1 turns · 1 steps LLM {{duration}} TTFT avg {{duration}} Input 0 tok · Output 0 tok
|
||||
@@ -19,7 +19,7 @@
|
||||
- img
|
||||
- button "Branch into a new conversation":
|
||||
- img
|
||||
- text: {{clock}} Ran for {{duration}}
|
||||
- text: {{clock}} Ran for {{duration}} TTFT {{duration}}
|
||||
- button "2 queued messages" [expanded]
|
||||
- list:
|
||||
- listitem:
|
||||
@@ -47,4 +47,4 @@
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
- button "Send message" [disabled]
|
||||
- text: 1 turns · 1 steps Input 0 tok · Output 0 tok
|
||||
- text: 1 turns · 1 steps LLM {{duration}} TTFT avg {{duration}} Input 0 tok · Output 0 tok
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/persistence-catalog.md
|
||||
persistence-catalog.md: 96374f4488dbfd4e60da99532ae2e559a3855c0f
|
||||
persistence-catalog.zh.md: c39e5db3deb01305ae687b79e2ee252f29bca55d
|
||||
persistence-catalog.md: 09f440514335fdc24b9abd036c448818aa8a4465
|
||||
persistence-catalog.zh.md: 6fe6b46375d30505fcea742c31d5cf6701491c95
|
||||
+12
-11
@@ -79,7 +79,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
}[T]
|
||||
```
|
||||
|
||||
Sources: [`packages/core/session/src/types.ts:319`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:326`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:355`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:387`](../packages/core/session/src/types.ts)
|
||||
Sources: [`packages/core/session/src/types.ts:320`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:327`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:356`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:388`](../packages/core/session/src/types.ts)
|
||||
|
||||
## Events
|
||||
|
||||
@@ -203,15 +203,16 @@ Source: [`packages/core/session/src/types.ts:246`](../packages/core/session/src/
|
||||
* the model output and its accounting travel together (there is no separate
|
||||
* usage record). `usage` is absent when the adapter reported none. A turn
|
||||
* cancelled mid-stream finalizes its delivered text/reasoning prefix as this
|
||||
* event (undispatched tool calls are absent); an aborted turn with no such
|
||||
* event streamed no visible content.
|
||||
* event with `interrupted: true` (undispatched tool calls are absent), so
|
||||
* consumers classify the truncation without re-deriving it from turn
|
||||
* boundaries; an aborted turn with no such event streamed no visible content.
|
||||
*/
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage }
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage; interrupted?: true }
|
||||
```
|
||||
|
||||
Types: [TokenUsage](subsystems/llm-streaming.md)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:256`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:257`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `command/*`
|
||||
|
||||
@@ -491,7 +492,7 @@ Source: [`packages/plan/plan-mode/src/index.ts:52`](../packages/plan/plan-mode/s
|
||||
'request/context': RequestContext
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:292`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:293`](../packages/core/session/src/types.ts)
|
||||
|
||||
#### `request/header` — log-only
|
||||
|
||||
@@ -503,7 +504,7 @@ Source: [`packages/core/session/src/types.ts:292`](../packages/core/session/src/
|
||||
'request/header': { header: EpochHeader; reason: RequestHeaderReason }
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:287`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:288`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `sandbox/*`
|
||||
|
||||
@@ -556,7 +557,7 @@ Source: [`packages/sandbox/sandbox-policy/src/session-mode.ts:33`](../packages/s
|
||||
'session/end-seed': Record<string, never>
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:315`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:316`](../packages/core/session/src/types.ts)
|
||||
|
||||
#### `session/title` — log-only
|
||||
|
||||
@@ -631,7 +632,7 @@ Source: [`packages/subagent/subagent/src/descriptor.ts:37`](../packages/subagent
|
||||
|
||||
Types: [TodoItem](subsystems/session.md)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:282`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:283`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `tool/*`
|
||||
|
||||
@@ -648,7 +649,7 @@ Source: [`packages/core/session/src/types.ts:282`](../packages/core/session/src/
|
||||
|
||||
Types: [CallId](subsystems/core.md)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:262`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:263`](../packages/core/session/src/types.ts)
|
||||
|
||||
#### `tool/code-dispatch` — log-only
|
||||
|
||||
@@ -717,7 +718,7 @@ Source: [`packages/core/tools/src/types.ts:40`](../packages/core/tools/src/types
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:274`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:275`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `turn/*`
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
}[T]
|
||||
```
|
||||
|
||||
来源:[`packages/core/session/src/types.ts:319`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:326`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:355`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:387`](../packages/core/session/src/types.ts)
|
||||
来源:[`packages/core/session/src/types.ts:320`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:327`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:356`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:388`](../packages/core/session/src/types.ts)
|
||||
|
||||
## 事件
|
||||
|
||||
@@ -205,15 +205,16 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
* the model output and its accounting travel together (there is no separate
|
||||
* usage record). `usage` is absent when the adapter reported none. A turn
|
||||
* cancelled mid-stream finalizes its delivered text/reasoning prefix as this
|
||||
* event (undispatched tool calls are absent); an aborted turn with no such
|
||||
* event streamed no visible content.
|
||||
* event with `interrupted: true` (undispatched tool calls are absent), so
|
||||
* consumers classify the truncation without re-deriving it from turn
|
||||
* boundaries; an aborted turn with no such event streamed no visible content.
|
||||
*/
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage }
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage; interrupted?: true }
|
||||
```
|
||||
|
||||
类型:[TokenUsage](subsystems/llm-streaming.md)
|
||||
|
||||
来源:[`packages/core/session/src/types.ts:256`](../packages/core/session/src/types.ts)
|
||||
来源:[`packages/core/session/src/types.ts:257`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `command/*`
|
||||
|
||||
@@ -493,7 +494,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
'request/context': RequestContext
|
||||
```
|
||||
|
||||
来源:[`packages/core/session/src/types.ts:292`](../packages/core/session/src/types.ts)
|
||||
来源:[`packages/core/session/src/types.ts:293`](../packages/core/session/src/types.ts)
|
||||
|
||||
#### `request/header` — log-only
|
||||
|
||||
@@ -505,7 +506,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
'request/header': { header: EpochHeader; reason: RequestHeaderReason }
|
||||
```
|
||||
|
||||
来源:[`packages/core/session/src/types.ts:287`](../packages/core/session/src/types.ts)
|
||||
来源:[`packages/core/session/src/types.ts:288`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `sandbox/*`
|
||||
|
||||
@@ -558,7 +559,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
'session/end-seed': Record<string, never>
|
||||
```
|
||||
|
||||
来源:[`packages/core/session/src/types.ts:315`](../packages/core/session/src/types.ts)
|
||||
来源:[`packages/core/session/src/types.ts:316`](../packages/core/session/src/types.ts)
|
||||
|
||||
#### `session/title` — log-only
|
||||
|
||||
@@ -633,7 +634,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
|
||||
类型:[TodoItem](subsystems/session.md)
|
||||
|
||||
来源:[`packages/core/session/src/types.ts:282`](../packages/core/session/src/types.ts)
|
||||
来源:[`packages/core/session/src/types.ts:283`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `tool/*`
|
||||
|
||||
@@ -650,7 +651,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
|
||||
类型:[CallId](subsystems/core.md)
|
||||
|
||||
来源:[`packages/core/session/src/types.ts:262`](../packages/core/session/src/types.ts)
|
||||
来源:[`packages/core/session/src/types.ts:263`](../packages/core/session/src/types.ts)
|
||||
|
||||
#### `tool/code-dispatch` — log-only
|
||||
|
||||
@@ -719,7 +720,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
}
|
||||
```
|
||||
|
||||
来源:[`packages/core/session/src/types.ts:274`](../packages/core/session/src/types.ts)
|
||||
来源:[`packages/core/session/src/types.ts:275`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `turn/*`
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/llm-streaming.md
|
||||
llm-streaming.md: 93669b0781a16121765ffafb094ea14c9021e1ec
|
||||
llm-streaming.zh.md: bab98094f6a9ab3ca0aa9dc6178b46a612b205e8
|
||||
llm-streaming.md: 7ab12d8e26b5bedbde8a20e661b049336ed2a459
|
||||
llm-streaming.zh.md: f0fe8c1e724f7e58251a772da5c5fc9a1ff3d9e9
|
||||
@@ -270,7 +270,8 @@ interface TokenUsage {
|
||||
* {@link ContentBlock}s and a final assistant {@link Message}.
|
||||
*
|
||||
* The agent loop feeds it while logging raw chunks for replay fidelity, then
|
||||
* reads `blocks()` / `message()` / `usage` / `finish` once the stream ends.
|
||||
* reads `blocks()` / `message()` / `usage` / `finish` once the stream ends,
|
||||
* or `interruptedBlocks()` when cancellation cut the stream short.
|
||||
*
|
||||
* Tolerant of delta-only protocols (no block-start/end); deltas arriving for
|
||||
* an index already closed by `block-end` are ignored (malformed stream) so a
|
||||
|
||||
@@ -278,7 +278,8 @@ interface TokenUsage {
|
||||
* {@link ContentBlock}s and a final assistant {@link Message}.
|
||||
*
|
||||
* The agent loop feeds it while logging raw chunks for replay fidelity, then
|
||||
* reads `blocks()` / `message()` / `usage` / `finish` once the stream ends.
|
||||
* reads `blocks()` / `message()` / `usage` / `finish` once the stream ends,
|
||||
* or `interruptedBlocks()` when cancellation cut the stream short.
|
||||
*
|
||||
* Tolerant of delta-only protocols (no block-start/end); deltas arriving for
|
||||
* an index already closed by `block-end` are ignored (malformed stream) so a
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/session.md
|
||||
session.md: 3ffd7997afaf93633a034ffb8ebd308bd18e773d
|
||||
session.zh.md: 9a89f42c5a7f8bf0ac82ab0d9ca96be506d18876
|
||||
session.md: 7c26bbc1cfd197adf139c353c610ce2d951f0170
|
||||
session.zh.md: b05bbd52eec45fbd5520f1b6cc5e343b8e80950b
|
||||
@@ -61,10 +61,11 @@ interface SessionEventMap {
|
||||
* the model output and its accounting travel together (there is no separate
|
||||
* usage record). `usage` is absent when the adapter reported none. A turn
|
||||
* cancelled mid-stream finalizes its delivered text/reasoning prefix as this
|
||||
* event (undispatched tool calls are absent); an aborted turn with no such
|
||||
* event streamed no visible content.
|
||||
* event with `interrupted: true` (undispatched tool calls are absent), so
|
||||
* consumers classify the truncation without re-deriving it from turn
|
||||
* boundaries; an aborted turn with no such event streamed no visible content.
|
||||
*/
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage }
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage; interrupted?: true }
|
||||
/**
|
||||
* The model requested one tool invocation: `name` with the raw `arguments`
|
||||
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
||||
|
||||
@@ -61,10 +61,11 @@ interface SessionEventMap {
|
||||
* the model output and its accounting travel together (there is no separate
|
||||
* usage record). `usage` is absent when the adapter reported none. A turn
|
||||
* cancelled mid-stream finalizes its delivered text/reasoning prefix as this
|
||||
* event (undispatched tool calls are absent); an aborted turn with no such
|
||||
* event streamed no visible content.
|
||||
* event with `interrupted: true` (undispatched tool calls are absent), so
|
||||
* consumers classify the truncation without re-deriving it from turn
|
||||
* boundaries; an aborted turn with no such event streamed no visible content.
|
||||
*/
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage }
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage; interrupted?: true }
|
||||
/**
|
||||
* The model requested one tool invocation: `name` with the raw `arguments`
|
||||
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
{"type":"user/message","seq":55,"time":0,"data":{"content":[{"type":"text","text":"<goal_round>\nObjective: \"Finish the ACP goal-session snapshot proof\"\nRound: 2/2\n\nContinue working toward the objective in this same session. Treat the current workspace, tool results, and durable session state as authoritative; inspect them instead of assuming earlier narration is still current. Make concrete progress and verify the result. Before claiming completion, gather evidence that the whole objective is achieved, read the current goal, and mark it complete. If work remains, leave the goal active for the next round. Follow the configured goal-tool policy before reporting a blocker.\n</goal_round>"}],"source":{"kind":"goal","goalId":"goal-{{sessionId}}","revision":1,"round":2},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
|
||||
{"type":"assistant/chunk","seq":56,"time":0,"data":{"turn":3,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":0,"data":{"turn":3,"step":1,"chunk":{"type":"text-delta","index":0,"text":"partial"}}}
|
||||
{"type":"assistant/message","seq":58,"time":0,"data":{"turn":3,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"partial"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"}},"sourceEventSeqs":[56,57],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":58,"time":0,"data":{"turn":3,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"partial"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"interrupted":true},"sourceEventSeqs":[56,57],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":59,"time":0,"data":{"turn":3,"step":1}}
|
||||
{"type":"turn/end","seq":60,"time":0,"data":{"turn":3,"reason":{"kind":"aborted","reason":{"kind":"user"}}}}
|
||||
{"type":"goal/change","seq":61,"time":0,"data":{"kind":"goal/change","version":1,"operation":"pause","goal":{"id":"goal-{{sessionId}}","revision":2,"objective":"Finish the ACP goal-session snapshot proof","phase":"paused","maxGoalRounds":2},"roundsStarted":2,"createdAt":0,"updatedAt":0}}
|
||||
@@ -10,6 +10,6 @@
|
||||
{"type":"request/context","seq":8,"time":1785730444532,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1785498791456,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1785730444541,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"partial"}}}
|
||||
{"type":"assistant/message","seq":11,"time":1786334791338,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"partial"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"104e9294-f9b8-4248-b7df-0b7e2a069c0a"}},"sourceEventSeqs":[9,10],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":11,"time":1786334791338,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"partial"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"104e9294-f9b8-4248-b7df-0b7e2a069c0a"},"interrupted":true},"sourceEventSeqs":[9,10],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":12,"time":1786334791338,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":13,"time":1786334791338,"data":{"turn":1,"reason":{"kind":"aborted","reason":{"kind":"user"}}}}
|
||||
@@ -15,7 +15,7 @@
|
||||
{"type":"assistant/chunk","seq":13,"time":1785730459883,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":14,"time":1785730459883,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"inspect-tools-api","name":"cordis_inspect","arguments":"{\"what\":\"api\",\"name\":\"tools\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"6b62bed7-113a-4d2e-a6aa-b935a1063ee2"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":15,"time":1785730459883,"data":{"turn":1,"step":1,"callId":"inspect-tools-api","name":"cordis_inspect","arguments":"{\"what\":\"api\",\"name\":\"tools\"}"}}
|
||||
{"type":"tool/result","seq":16,"time":1785730459904,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"inspect-tools-api"},"content":[{"type":"tool-result","toolCallId":"inspect-tools-api","content":[{"type":"text","text":"## api\n- tools — Tool registry and execution pipeline.\n /**\n * Present this agent's tools in `mode` instead of the deployment default.\n *\n * Scoped only, and one declaration per agent: this is how an agent preset\n * composes a Code Mode agent beside native ones in the same process, and a\n * process-global override would be the `mode` config field instead.\n * @param mode - the presentation this agent's model sees.\n * @returns the exact disposer that restores the deployment default.\n */\n presentAs(mode: ToolPresentationMode): () => void\n /**\n * Register globally or in the calling agent scope. Scoped tools shadow\n * globals; duplicates within one layer and the reserved `run_code` name fail.\n * @param definition - tool schema, execution, and optional finalization/presentation callbacks.\n * @returns the exact disposer that unregisters the tool.\n */\n register(definition: ToolDefinition): () => void\n /**\n * Restrict global tools for the calling agent scope. Empty filters, unknown\n * names, scope-local names, and reserved transport names fail. Restrictions\n * intersect; scoped registrations remain visible.\n * @param filter - global-surface mask: `allow` (keep only) and/or `deny` (remove).\n * @returns the exact disposer that lifts this restriction.\n */\n restrict(filter: ToolRestriction): () => void\n /**\n * Register a monotonic guard after the extensible `tools/pre-execute`\n * waterfall. A plain-context guard applies globally; one registered through\n * `agent.ctx` applies only to that agent. Any matching guard may deny by\n * returning a reason, while no guard can force-allow a call another guard\n * denied. The exact effect disposer is returned for ordered ownership and\n * HMR cleanup.\n * @param guard - synchronous check; a returned string denies the execution.\n * @returns the exact disposer that unregisters the guard.\n */\n guard(guard: ToolGuard): () => void\n /**\n * Look up a tool as one scope sees it (scoped\n * shadows global; a restricted-away global reads as absent). Presenters pass\n * the calling agent so the rendered card matches the definition that\n * actually executed.\n * @param name - the tool name as registered.\n * @param scope - the viewing scope (the agent); omitted = the global view.\n * @returns the definition the scope resolves, or undefined when none is visible.\n */\n get(name: string, scope?: ScopeKey): ToolDefinition | undefined\n /**\n * Project visible definitions onto the allowlisted model-facing schema fields,\n * excluding execution and presentation callbacks.\n * @param scope - the viewing scope (the agent); omitted = the global view.\n * @returns one deep-cloned schema per visible tool.\n */\n schemas(scope?: ScopeKey): ToolSchema[]\n /**\n * Classify a pending call through the caller's visible tool definition. Only\n * an exact `true` is parallel; unknown, hidden, undeclared, invalid, or\n * throwing classifiers are exclusive.\n * @param exec - call name, parsed arguments, and optional agent scope.\n * @returns the fail-closed scheduling mode.\n */\n executionMode(exec: ToolExecutionInput): ToolExecutionMode\n /**\n * Execute through pre-policy, guards, around-dispatch, post-policy,\n * definition-owned content finalization, and final notification. Tool and\n * listener failures resolve as materialized error results; an invisible tool\n * reports `UNKNOWN_TOOL`. The returned outcome is the same lossless, frozen\n * snapshot final observers receive. Cancellation\n * arriving after entry and before final result materialization skips a\n * not-yet-started body with `ABORTED_BEFORE_DISPATCH` or replaces a\n * successful started outcome with `ABORTED`; already-started work is still\n * drained and may retain a tool-owned structured error.\n * @param exec - the typed same-process call input. The registry assigns its\n * correlation token before policy begins.\n * @returns the materialized final result.\n */\n async execute(exec: ToolExecutionInput): Promise<ToolExecutionResult>\ntype shapes (referenced by the signatures above — read these before assuming a field is a string):\n export interface Agent {\n readonly id: SessionId;\n readonly options: AgentOptions;\n readonly session: Session;\n readonly inbox: Inbox;\n readonly status: AgentStatus;\n readonly ctx: Context;\n cancel(cause: AgentCancelCause, options?: CancelOptions): void;\n whenIdle(): Promise<void>;\n Line truncated
|
||||
{"type":"tool/result","seq":16,"time":1785730459904,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"inspect-tools-api"},"content":[{"type":"tool-result","toolCallId":"inspect-tools-api","content":[{"type":"text","text":"## api\n- tools — Tool registry and execution pipeline.\n /**\n * Present this agent's tools in `mode` instead of the deployment default.\n *\n * Scoped only, and one declaration per agent: this is how an agent preset\n * composes a Code Mode agent beside native ones in the same process, and a\n * process-global override would be the `mode` config field instead.\n * @param mode - the presentation this agent's model sees.\n * @returns the exact disposer that restores the deployment default.\n */\n presentAs(mode: ToolPresentationMode): () => void\n /**\n * Register globally or in the calling agent scope. Scoped tools shadow\n * globals; duplicates within one layer and the reserved `run_code` name fail.\n * @param definition - tool schema, execution, and optional finalization/presentation callbacks.\n * @returns the exact disposer that unregisters the tool.\n */\n register(definition: ToolDefinition): () => void\n /**\n * Restrict global tools for the calling agent scope. Empty filters, unknown\n * names, scope-local names, and reserved transport names fail. Restrictions\n * intersect; scoped registrations remain visible.\n * @param filter - global-surface mask: `allow` (keep only) and/or `deny` (remove).\n * @returns the exact disposer that lifts this restriction.\n */\n restrict(filter: ToolRestriction): () => void\n /**\n * Register a monotonic guard after the extensible `tools/pre-execute`\n * waterfall. A plain-context guard applies globally; one registered through\n * `agent.ctx` applies only to that agent. Any matching guard may deny by\n * returning a reason, while no guard can force-allow a call another guard\n * denied. The exact effect disposer is returned for ordered ownership and\n * HMR cleanup.\n * @param guard - synchronous check; a returned string denies the execution.\n * @returns the exact disposer that unregisters the guard.\n */\n guard(guard: ToolGuard): () => void\n /**\n * Look up a tool as one scope sees it (scoped\n * shadows global; a restricted-away global reads as absent). Presenters pass\n * the calling agent so the rendered card matches the definition that\n * actually executed.\n * @param name - the tool name as registered.\n * @param scope - the viewing scope (the agent); omitted = the global view.\n * @returns the definition the scope resolves, or undefined when none is visible.\n */\n get(name: string, scope?: ScopeKey): ToolDefinition | undefined\n /**\n * Project visible definitions onto the allowlisted model-facing schema fields,\n * excluding execution and presentation callbacks.\n * @param scope - the viewing scope (the agent); omitted = the global view.\n * @returns one deep-cloned schema per visible tool.\n */\n schemas(scope?: ScopeKey): ToolSchema[]\n /**\n * Classify a pending call through the caller's visible tool definition. Only\n * an exact `true` is parallel; unknown, hidden, undeclared, invalid, or\n * throwing classifiers are exclusive.\n * @param exec - call name, parsed arguments, and optional agent scope.\n * @returns the fail-closed scheduling mode.\n */\n executionMode(exec: ToolExecutionInput): ToolExecutionMode\n /**\n * Execute through pre-policy, guards, around-dispatch, post-policy,\n * definition-owned content finalization, and final notification. Tool and\n * listener failures resolve as materialized error results; an invisible tool\n * reports `UNKNOWN_TOOL`. The returned outcome is the same lossless, frozen\n * snapshot final observers receive. Cancellation\n * arriving after entry and before final result materialization skips a\n * not-yet-started body with `ABORTED_BEFORE_DISPATCH` or replaces a\n * successful started outcome with `ABORTED`; already-started work is still\n * drained and may retain a tool-owned structured error.\n * @param exec - the typed same-process call input. The registry assigns its\n * correlation token before policy begins.\n * @returns the materialized final result.\n */\n async execute(exec: ToolExecutionInput): Promise<ToolExecutionResult>\ntype shapes (referenced by the signatures above — read these before assuming a field is a string):\n export interface Agent {\n readonly id: SessionId;\n readonly options: AgentOptions;\n readonly session: Session;\n readonly inbox: Inbox;\n readonly status: AgentStatus;\n readonly ctx: Context;\n cancel(cause: AgentCancelCause, options?: CancelOptions): void;\n whenIdle(): Promise<void>;\n Line truncated
|
||||
{"type":"step/end","seq":17,"time":1785730459904,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":18,"time":1785730459916,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1784449176734,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{"type":"session","version":0,"id":"22222222-2222-4222-8222-222222222222","createdAt":1783950001000,"cwd":"{{cwd}}","parentSession":"11111111-1111-4111-8111-111111111111","origin":"subagent","delegationDepth":1}
|
||||
{"type":"agent/inbox/spliced","seq":0,"time":1785498583877,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"fc62f9e7-b8f6-441f-9ee8-17f1f9e4feca"}]}}
|
||||
{"type":"agent/inbox/spliced","seq":0,"time":1785498583877,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"adc87967-3604-494d-9a0c-d3ba992321b5"}]}}
|
||||
{"type":"turn/start","seq":1,"time":1785821454445,"data":{"turn":1}}
|
||||
{"type":"agent/inbox/spliced","seq":2,"time":1785821454445,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"subagent/descriptor","seq":3,"time":1785821454466,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Check direct child"}}
|
||||
{"type":"step/start","seq":4,"time":1785730501506,"data":{"turn":1,"step":1}}
|
||||
{"type":"user/message","seq":5,"time":1785730501506,"data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"fc62f9e7-b8f6-441f-9ee8-17f1f9e4feca"},"surfaceOp":"append"}
|
||||
{"type":"user/message","seq":5,"time":1785730501506,"data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"adc87967-3604-494d-9a0c-d3ba992321b5"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":6,"time":1785730501506,"data":{"title":"Reply with exactly DIRECT_CHILD_OK and","messageSeqs":[5],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","seq":7,"time":1785498583897,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"You are an AI agent powered by the DeepSeek Harness SDK.\n\nYou are headless-agent, a coding assistant powered by the deepseek-v4-flash model. Your working directory is {{cwd}}.\n\nVerify your work by running the code or tests. Keep answers brief and factual.\n\n\nUse the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.\n\nUse the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.\n\nUse the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.\n\nCheck the [exit code: N] marker on every bash result; investigate failures before moving on.\n\nTrack every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.\n\nUse the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.\n\nUse the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.\n\n## Writing code for run_code\n\nPass `run_code` the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped). Inside the program:\n\n- Call tools as `await tools.name(args)` — quoted access for exotic names: `tools[\"my-tool\"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON.\n- A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue.\n- Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`.\n- Emit results with `return` and/or `console.log(...)`. ONLY what you print or return comes back to you — intermediate tool results never enter the conversation, so extract just what you need.\n\nThe available tools:\n\n```ts\ntype JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }\n\ninterface ToolArgsMap {\n /** Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. */\n bash: {\n /** The bash command to execute. */\n command: string;\n /** Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\". */\n description: string;\n /** Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry. */\n timeoutMs?: number;\n /** Working directory for this command. Defaults to the sessioLine truncated
|
||||
{"type":"request/header","seq":7,"time":1785498583897,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"You are an AI agent powered by the DeepSeek Harness SDK.\n\nYou are headless-agent, a coding assistant powered by the deepseek-v4-flash model. Your working directory is {{cwd}}.\n\nVerify your work by running the code or tests. Keep answers brief and factual.\n\n\nUse the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.\n\nUse the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.\n\nUse the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.\n\nCheck the [exit code: N] marker on every bash result; investigate failures before moving on.\n\nTrack every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.\n\nUse the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.\n\nUse the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.\n\n## Writing code for run_code\n\nPass `run_code` the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped). Inside the program:\n\n- Call tools as `await tools.name(args)` — quoted access for exotic names: `tools[\"my-tool\"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON.\n- A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue.\n- Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`.\n- Emit results with `return` and/or `console.log(...)`. ONLY what you print or return comes back to you — intermediate tool results never enter the conversation, so extract just what you need.\n\nThe available tools:\n\n```ts\ntype JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }\n\ninterface ToolArgsMap {\n /** Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. */\n bash: {\n /** The bash command to execute. */\n command: string;\n /** Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\". */\n description: string;\n /** Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry. */\n timeoutMs?: number;\n /** Working directory for this command. Defaults to the sessioLine truncated
|
||||
{"type":"request/context","seq":8,"time":1785730501507,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"DIRECT_CHILD_OK"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1785498583897,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1785730501507,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":14,"time":1785730501507,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"cac680cf-1d70-4fb2-91a3-da1e3a317d2e"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":14,"time":1785730501507,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"5a0d8fae-1484-40f9-8fbc-a21bd6e34f4b"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":15,"time":1785730501507,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":16,"time":1785730501507,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -1,18 +1,18 @@
|
||||
{"type":"session","version":0,"id":"33333333-3333-4333-8333-333333333333","createdAt":1783950002000,"cwd":"{{cwd}}","parentSession":"11111111-1111-4111-8111-111111111111","origin":"subagent","delegationDepth":1}
|
||||
{"type":"agent/inbox/spliced","seq":0,"time":1785498584048,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"093bfc20-c6fc-4573-b172-2c6ca40c188b"}]}}
|
||||
{"type":"agent/inbox/spliced","seq":0,"time":1785498584048,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"80153740-f1c2-4c68-a021-f683b7299f9a"}]}}
|
||||
{"type":"turn/start","seq":1,"time":1785821454599,"data":{"turn":1}}
|
||||
{"type":"agent/inbox/spliced","seq":2,"time":1785821454599,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"subagent/descriptor","seq":3,"time":1785821454618,"data":{"version":2,"mode":"one-shot","provider":"spawn"}}
|
||||
{"type":"step/start","seq":4,"time":1785730501645,"data":{"turn":1,"step":1}}
|
||||
{"type":"user/message","seq":5,"time":1785730501645,"data":{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"093bfc20-c6fc-4573-b172-2c6ca40c188b"},"surfaceOp":"append"}
|
||||
{"type":"user/message","seq":5,"time":1785730501645,"data":{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"80153740-f1c2-4c68-a021-f683b7299f9a"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":6,"time":1785730501645,"data":{"title":"Reply with exactly WORKFLOW_CHILD_OK and","messageSeqs":[5],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","seq":7,"time":1785498584067,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"You are an AI agent powered by the DeepSeek Harness SDK.\n\nYou are headless-agent, a coding assistant powered by the deepseek-v4-flash model. Your working directory is {{cwd}}.\n\nVerify your work by running the code or tests. Keep answers brief and factual.\n\n\nUse the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.\n\nUse the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.\n\nUse the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.\n\nCheck the [exit code: N] marker on every bash result; investigate failures before moving on.\n\nTrack every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.\n\nUse the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.\n\nUse the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.\n\n## Writing code for run_code\n\nPass `run_code` the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped). Inside the program:\n\n- Call tools as `await tools.name(args)` — quoted access for exotic names: `tools[\"my-tool\"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON.\n- A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue.\n- Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`.\n- Emit results with `return` and/or `console.log(...)`. ONLY what you print or return comes back to you — intermediate tool results never enter the conversation, so extract just what you need.\n\nThe available tools:\n\n```ts\ntype JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }\n\ninterface ToolArgsMap {\n /** Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. */\n bash: {\n /** The bash command to execute. */\n command: string;\n /** Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\". */\n description: string;\n /** Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry. */\n timeoutMs?: number;\n /** Working directory for this command. Defaults to the sessioLine truncated
|
||||
{"type":"request/header","seq":7,"time":1785498584067,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"You are an AI agent powered by the DeepSeek Harness SDK.\n\nYou are headless-agent, a coding assistant powered by the deepseek-v4-flash model. Your working directory is {{cwd}}.\n\nVerify your work by running the code or tests. Keep answers brief and factual.\n\n\nUse the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.\n\nUse the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.\n\nUse the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.\n\nCheck the [exit code: N] marker on every bash result; investigate failures before moving on.\n\nTrack every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.\n\nUse the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.\n\nUse the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.\n\n## Writing code for run_code\n\nPass `run_code` the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped). Inside the program:\n\n- Call tools as `await tools.name(args)` — quoted access for exotic names: `tools[\"my-tool\"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON.\n- A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue.\n- Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`.\n- Emit results with `return` and/or `console.log(...)`. ONLY what you print or return comes back to you — intermediate tool results never enter the conversation, so extract just what you need.\n\nThe available tools:\n\n```ts\ntype JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }\n\ninterface ToolArgsMap {\n /** Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. */\n bash: {\n /** The bash command to execute. */\n command: string;\n /** Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\". */\n description: string;\n /** Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry. */\n timeoutMs?: number;\n /** Working directory for this command. Defaults to the sessioLine truncated
|
||||
{"type":"request/context","seq":8,"time":1785730501646,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"WORKFLOW_CHILD_OK"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1785498584067,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1785730501646,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":14,"time":1785730501646,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"2b31dae5-8939-44e1-bbcd-9f64aa637d76"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":14,"time":1785730501646,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"c0e12068-d11c-470b-b579-d3e1f965d3dc"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":15,"time":1785730501646,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":16,"time":1785730501646,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -1,20 +1,20 @@
|
||||
{"type":"session","version":0,"id":"11111111-1111-4111-8111-111111111111","createdAt":1783950000000,"cwd":"{{cwd}}","delegationDepth":0}
|
||||
{"type":"agent/inbox/spliced","seq":0,"time":1785498583746,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Run this advanced flow exactly once: try a no-op temporary Cordis Plugin named snapshot-marker; use run_code to inspect the live temporary Plugins through tools.cordis_inspect; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; stop dyn-1; then reply with exactly ADVANCED_HEADLESS_OK."}],"source":{"kind":"user"},"role":"user","id":"d2f4f71c-78bc-4a22-908d-c08fbb3ab9ef"}]}}
|
||||
{"type":"agent/inbox/spliced","seq":0,"time":1785498583746,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Run this advanced flow exactly once: try a no-op temporary Cordis Plugin named snapshot-marker; use run_code to inspect the live temporary Plugins through tools.cordis_inspect; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; stop dyn-1; then reply with exactly ADVANCED_HEADLESS_OK."}],"source":{"kind":"user"},"role":"user","id":"660f770b-8f37-4cb9-8bfb-7e588e297ad7"}]}}
|
||||
{"type":"turn/start","seq":1,"time":1785821454304,"data":{"turn":1}}
|
||||
{"type":"agent/inbox/spliced","seq":2,"time":1785821454304,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"step/start","seq":3,"time":1783957884486,"data":{"turn":1,"step":1}}
|
||||
{"type":"user/message","seq":4,"time":1785498583779,"data":{"content":[{"type":"text","text":"Run this advanced flow exactly once: try a no-op temporary Cordis Plugin named snapshot-marker; use run_code to inspect the live temporary Plugins through tools.cordis_inspect; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; stop dyn-1; then reply with exactly ADVANCED_HEADLESS_OK."}],"source":{"kind":"user"},"role":"user","id":"d2f4f71c-78bc-4a22-908d-c08fbb3ab9ef"},"surfaceOp":"append"}
|
||||
{"type":"user/message","seq":4,"time":1785498583779,"data":{"content":[{"type":"text","text":"Run this advanced flow exactly once: try a no-op temporary Cordis Plugin named snapshot-marker; use run_code to inspect the live temporary Plugins through tools.cordis_inspect; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; stop dyn-1; then reply with exactly ADVANCED_HEADLESS_OK."}],"source":{"kind":"user"},"role":"user","id":"660f770b-8f37-4cb9-8bfb-7e588e297ad7"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":5,"time":1785498583779,"data":{"title":"Run this advanced flow exactly","messageSeqs":[4],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","seq":6,"time":1785498583782,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"You are an AI agent powered by the DeepSeek Harness SDK.\n\nYou are headless-agent, a coding assistant powered by the deepseek-v4-flash model. Your working directory is {{cwd}}.\n\nVerify your work by running the code or tests. Keep answers brief and factual.\n\n\nUse the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.\n\nUse the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.\n\nUse the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.\n\nCheck the [exit code: N] marker on every bash result; investigate failures before moving on.\n\nTrack every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.\n\nUse the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.\n\nUse the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.\n\n## Writing code for run_code\n\nPass `run_code` the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped). Inside the program:\n\n- Call tools as `await tools.name(args)` — quoted access for exotic names: `tools[\"my-tool\"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON.\n- A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue.\n- Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`.\n- Emit results with `return` and/or `console.log(...)`. ONLY what you print or return comes back to you — intermediate tool results never enter the conversation, so extract just what you need.\n\nThe available tools:\n\n```ts\ntype JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }\n\ninterface ToolArgsMap {\n /** Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. */\n bash: {\n /** The bash command to execute. */\n command: string;\n /** Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\". */\n description: string;\n /** Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry. */\n timeoutMs?: number;\n /** Working directory for this command. Defaults to the sessioLine truncated
|
||||
{"type":"request/header","seq":6,"time":1785498583782,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"You are an AI agent powered by the DeepSeek Harness SDK.\n\nYou are headless-agent, a coding assistant powered by the deepseek-v4-flash model. Your working directory is {{cwd}}.\n\nVerify your work by running the code or tests. Keep answers brief and factual.\n\n\nUse the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.\n\nUse the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.\n\nUse the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.\n\nCheck the [exit code: N] marker on every bash result; investigate failures before moving on.\n\nTrack every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.\n\nUse the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.\n\nUse the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.\n\n## Writing code for run_code\n\nPass `run_code` the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped). Inside the program:\n\n- Call tools as `await tools.name(args)` — quoted access for exotic names: `tools[\"my-tool\"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON.\n- A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue.\n- Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`.\n- Emit results with `return` and/or `console.log(...)`. ONLY what you print or return comes back to you — intermediate tool results never enter the conversation, so extract just what you need.\n\nThe available tools:\n\n```ts\ntype JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }\n\ninterface ToolArgsMap {\n /** Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. */\n bash: {\n /** The bash command to execute. */\n command: string;\n /** Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\". */\n description: string;\n /** Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry. */\n timeoutMs?: number;\n /** Working directory for this command. Defaults to the sessioLine truncated
|
||||
{"type":"request/context","seq":7,"time":1785730501403,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1783950000007,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783950000008,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"advanced-mount","name":"cordis_mount","argumentsDelta":"{\"code\":\"return { name: 'snapshot-marker', apply() {} }\"}"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783950000009,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-mount","name":"cordis_mount","arguments":"{\"code\":\"return { name: 'snapshot-marker', apply() {} }\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1785498583784,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1785730501404,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":13,"time":1785730501404,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-mount","name":"cordis_mount","arguments":"{\"code\":\"return { name: 'snapshot-marker', apply() {} }\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"e65c0ebe-8e3d-44c0-833f-68efcbc0acb5"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[8,9,10,11,12],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":13,"time":1785730501404,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-mount","name":"cordis_mount","arguments":"{\"code\":\"return { name: 'snapshot-marker', apply() {} }\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"34d67879-3a88-4396-a1b0-127bae0e1ccf"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[8,9,10,11,12],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":14,"time":1785730501404,"data":{"turn":1,"step":1,"callId":"advanced-mount","name":"cordis_mount","arguments":"{\"code\":\"return { name: 'snapshot-marker', apply() {} }\"}"}}
|
||||
{"type":"tool/result","seq":15,"time":1785730501413,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"advanced-mount"},"content":[{"type":"tool-result","toolCallId":"advanced-mount","content":[{"type":"text","text":"Temporary Plugin dyn-1 is running (plugin \"snapshot-marker\"; available until unmounted or DSH restarts)."}],"isError":false}],"role":"user","id":"abb8ecee-cb03-4a66-9477-38a52458ab05"}},"sourceEventSeqs":[14],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":15,"time":1785730501413,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"advanced-mount"},"content":[{"type":"tool-result","toolCallId":"advanced-mount","content":[{"type":"text","text":"Temporary Plugin dyn-1 is running (plugin \"snapshot-marker\"; available until unmounted or DSH restarts)."}],"isError":false}],"role":"user","id":"157dc2aa-743a-4564-b1d3-472c25bf5831"}},"sourceEventSeqs":[14],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":16,"time":1785730501413,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":17,"time":1785730501423,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1783950000017,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
@@ -22,11 +22,11 @@
|
||||
{"type":"assistant/chunk","seq":20,"time":1783950000019,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.cordis_inspect({ what: 'temporary' })\", \"description\": \"Run the scripted inspection program\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1785498583804,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1785730501424,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":23,"time":1785730501424,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.cordis_inspect({ what: 'temporary' })\", \"description\": \"Run the scripted inspection program\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"cdc95327-3ce1-49ea-8a92-b17e450cc455"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[18,19,20,21,22],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":23,"time":1785730501424,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.cordis_inspect({ what: 'temporary' })\", \"description\": \"Run the scripted inspection program\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"9d5b359b-f2e6-4648-aa0c-89fac1938f30"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[18,19,20,21,22],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":24,"time":1785730501424,"data":{"turn":1,"step":2,"callId":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.cordis_inspect({ what: 'temporary' })\", \"description\": \"Run the scripted inspection program\"}"}}
|
||||
{"type":"tool/code-dispatch-start","seq":25,"time":1785730501473,"data":{"rootCallId":"advanced-code","parentCallId":"advanced-code","subCallId":"advanced-code:code:1","name":"cordis_inspect","arguments":{"what":"temporary"}}}
|
||||
{"type":"tool/code-dispatch","seq":26,"time":1785730501474,"data":{"rootCallId":"advanced-code","parentCallId":"advanced-code","subCallId":"advanced-code:code:1","name":"cordis_inspect","arguments":{"what":"temporary"},"isError":false,"content":[{"type":"text","text":"## Temporary Plugins\n- Temporary Plugin dyn-1: snapshot-marker [running] — provides: none; waiting for: none; lifetime: until unmounted or DSH restarts"}]}}
|
||||
{"type":"tool/result","seq":27,"time":1785730501475,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"advanced-code"},"content":[{"type":"tool-result","toolCallId":"advanced-code","content":[{"type":"text","text":"## Temporary Plugins\n- Temporary Plugin dyn-1: snapshot-marker [running] — provides: none; waiting for: none; lifetime: until unmounted or DSH restarts"}],"isError":false}],"role":"user","id":"d75c7d03-cbbc-4260-ba40-8c210a3b5bbe"}},"sourceEventSeqs":[24],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":27,"time":1785730501475,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"advanced-code"},"content":[{"type":"tool-result","toolCallId":"advanced-code","content":[{"type":"text","text":"## Temporary Plugins\n- Temporary Plugin dyn-1: snapshot-marker [running] — provides: none; waiting for: none; lifetime: until unmounted or DSH restarts"}],"isError":false}],"role":"user","id":"f8b90fd0-fabb-4e8d-8645-b10f7674c998"}},"sourceEventSeqs":[24],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":28,"time":1785730501475,"data":{"turn":1,"step":2}}
|
||||
{"type":"step/start","seq":29,"time":1785730501483,"data":{"turn":1,"step":3}}
|
||||
{"type":"assistant/chunk","seq":30,"time":1783950000029,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
@@ -34,9 +34,9 @@
|
||||
{"type":"assistant/chunk","seq":32,"time":1785037378923,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1785498583869,"data":{"turn":1,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1785730501484,"data":{"turn":1,"step":3,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":35,"time":1785730501484,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"ba4958e9-231c-437f-a2fc-7a13f392d3ba"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[30,31,32,33,34],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":35,"time":1785730501484,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"eb0416ad-752f-4c9a-a6c0-066e955c36d6"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[30,31,32,33,34],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":36,"time":1785730501484,"data":{"turn":1,"step":3,"callId":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}}
|
||||
{"type":"tool/result","seq":37,"time":1785730501508,"data":{"turn":1,"step":3,"message":{"source":{"kind":"tool","callId":"advanced-direct-child"},"content":[{"type":"tool-result","toolCallId":"advanced-direct-child","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"isError":false}],"role":"user","id":"b9ebb37d-e565-4882-95b0-5343da1d68d8"}},"sourceEventSeqs":[36],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":37,"time":1785730501508,"data":{"turn":1,"step":3,"message":{"source":{"kind":"tool","callId":"advanced-direct-child"},"content":[{"type":"tool-result","toolCallId":"advanced-direct-child","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"isError":false}],"role":"user","id":"d14db6df-b6fa-4cd4-aa8e-701ebe0bcab7"}},"sourceEventSeqs":[36],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":38,"time":1785730501508,"data":{"turn":1,"step":3}}
|
||||
{"type":"step/start","seq":39,"time":1785730501521,"data":{"turn":1,"step":4}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1783957884594,"data":{"turn":1,"step":4,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
@@ -44,9 +44,9 @@
|
||||
{"type":"assistant/chunk","seq":42,"time":1785037378946,"data":{"turn":1,"step":4,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1785498583919,"data":{"turn":1,"step":4,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1785730501522,"data":{"turn":1,"step":4,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":45,"time":1785730501522,"data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"4757f4b9-9bde-488b-a54a-1bdea55dd15f"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[40,41,42,43,44],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":45,"time":1785730501522,"data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"69a4bcfc-a8af-41e5-827a-704a9ac4a84f"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[40,41,42,43,44],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":46,"time":1785730501522,"data":{"turn":1,"step":4,"callId":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}}
|
||||
{"type":"tool/result","seq":47,"time":1785730501647,"data":{"turn":1,"step":4,"message":{"source":{"kind":"tool","callId":"advanced-workflow"},"content":[{"type":"tool-result","toolCallId":"advanced-workflow","content":[{"type":"text","text":"workflow \"advanced-headless-snapshot\" completed (1 agent).\nReturn value:\n{\n \"reply\": \"WORKFLOW_CHILD_OK\"\n}"}],"isError":false}],"role":"user","id":"35baa460-54ff-4fa1-ba9d-66b6661f84e9"}},"sourceEventSeqs":[46],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":47,"time":1785730501647,"data":{"turn":1,"step":4,"message":{"source":{"kind":"tool","callId":"advanced-workflow"},"content":[{"type":"tool-result","toolCallId":"advanced-workflow","content":[{"type":"text","text":"workflow \"advanced-headless-snapshot\" completed (1 agent).\nReturn value:\n{\n \"reply\": \"WORKFLOW_CHILD_OK\"\n}"}],"isError":false}],"role":"user","id":"5a027b96-5860-408c-86d3-d7c1306b4640"}},"sourceEventSeqs":[46],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":48,"time":1785730501648,"data":{"turn":1,"step":4}}
|
||||
{"type":"step/start","seq":49,"time":1785730501660,"data":{"turn":1,"step":5}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1783957884719,"data":{"turn":1,"step":5,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
@@ -54,9 +54,9 @@
|
||||
{"type":"assistant/chunk","seq":52,"time":1785037379534,"data":{"turn":1,"step":5,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\":\"dyn-1\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1785498584085,"data":{"turn":1,"step":5,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1785730501661,"data":{"turn":1,"step":5,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":55,"time":1785730501661,"data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\":\"dyn-1\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"739166e2-ed48-4df2-a9a5-207f34058030"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[50,51,52,53,54],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":55,"time":1785730501661,"data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\":\"dyn-1\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"f552f360-52c4-4d68-96c2-e86b9cab792e"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[50,51,52,53,54],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":56,"time":1785730501661,"data":{"turn":1,"step":5,"callId":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\":\"dyn-1\"}"}}
|
||||
{"type":"tool/result","seq":57,"time":1785730501668,"data":{"turn":1,"step":5,"message":{"source":{"kind":"tool","callId":"advanced-unmount"},"content":[{"type":"tool-result","toolCallId":"advanced-unmount","content":[{"type":"text","text":"Temporary Plugin dyn-1 was unmounted and removed."}],"isError":false}],"role":"user","id":"98b05c06-cb77-41a9-8310-324bc72fc7a0"}},"sourceEventSeqs":[56],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":57,"time":1785730501668,"data":{"turn":1,"step":5,"message":{"source":{"kind":"tool","callId":"advanced-unmount"},"content":[{"type":"tool-result","toolCallId":"advanced-unmount","content":[{"type":"text","text":"Temporary Plugin dyn-1 was unmounted and removed."}],"isError":false}],"role":"user","id":"cc001237-dcec-42bd-a728-c580d3028e17"}},"sourceEventSeqs":[56],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":58,"time":1785730501668,"data":{"turn":1,"step":5}}
|
||||
{"type":"step/start","seq":59,"time":1785730501678,"data":{"turn":1,"step":6}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1783957884720,"data":{"turn":1,"step":6,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
@@ -64,6 +64,6 @@
|
||||
{"type":"assistant/chunk","seq":62,"time":1785037379541,"data":{"turn":1,"step":6,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_HEADLESS_OK"}}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":1785498584102,"data":{"turn":1,"step":6,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":64,"time":1785730501679,"data":{"turn":1,"step":6,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":65,"time":1785730501679,"data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"text","text":"ADVANCED_HEADLESS_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"0a4ca8f2-92c1-4dbc-beb8-923b8791c298"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[60,61,62,63,64],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":65,"time":1785730501679,"data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"text","text":"ADVANCED_HEADLESS_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"acc0c8e0-97ea-4225-a735-9a54193c20d8"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[60,61,62,63,64],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":66,"time":1785730501679,"data":{"turn":1,"step":6}}
|
||||
{"type":"turn/end","seq":67,"time":1785730501679,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -1,21 +1,21 @@
|
||||
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":0,"cwd":"{{cwd}}","delegationDepth":0}
|
||||
{"type":"agent/inbox/spliced","seq":0,"time":1785498587408,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Exercise the six PTY tools in order, including one missing-session signal error, then reply DONE."}],"source":{"kind":"user"},"role":"user","id":"39f39ecc-5772-4814-8feb-46433c71becd"}]}}
|
||||
{"type":"agent/inbox/spliced","seq":0,"time":1785498587408,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Exercise the six PTY tools in order, including one missing-session signal error, then reply DONE."}],"source":{"kind":"user"},"role":"user","id":"d05853ba-1ee0-477e-b673-92a955b98724"}]}}
|
||||
{"type":"turn/start","seq":1,"time":1785821457966,"data":{"turn":1}}
|
||||
{"type":"agent/inbox/spliced","seq":2,"time":1785821457966,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"step/start","seq":3,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"user/message","seq":4,"time":1785498587436,"data":{"content":[{"type":"text","text":"Exercise the six PTY tools in order, including one missing-session signal error, then reply DONE."}],"source":{"kind":"user"},"role":"user","id":"39f39ecc-5772-4814-8feb-46433c71becd"},"surfaceOp":"append"}
|
||||
{"type":"user/message","seq":5,"time":1785730504659,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."}]},"role":"user","id":"a5ae9c04-0652-436f-9b5a-437a3a6ed235"},"surfaceOp":"append"}
|
||||
{"type":"user/message","seq":4,"time":1785498587436,"data":{"content":[{"type":"text","text":"Exercise the six PTY tools in order, including one missing-session signal error, then reply DONE."}],"source":{"kind":"user"},"role":"user","id":"d05853ba-1ee0-477e-b673-92a955b98724"},"surfaceOp":"append"}
|
||||
{"type":"user/message","seq":5,"time":1785730504659,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."}]},"role":"user","id":"d4ac87a7-eed3-41fd-bf5d-e6d49f331f66"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":6,"time":1785730504659,"data":{"title":"Exercise the six PTY tools","messageSeqs":[4],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","seq":7,"time":1785498587438,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"You are an AI agent powered by the DeepSeek Harness SDK.\n\nYou are headless-agent, a coding assistant powered by the deepseek-v4-flash model.\n\nVerify your work by running the code or tests. Keep answers brief and\nfactual.\n\n\nUse the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.\n\nUse the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.\n\nUse the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.\n\nCheck the [exit code: N] marker on every bash result; investigate failures before moving on.\n\nUse a terminal session only when work needs persistent terminal state or interactive stdin; prefer bash/read/write/edit for bounded one-shot operations. Track every terminal session id and close sessions that no longer matter. An inferred_idle or timeout result does not prove the foreground command exited.\n\nTrack every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.\n\nUse the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.\n\nUse the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.","tools":[{"name":"bash","description":"Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`.","parameters":{"type":"object","properties":{"command":{"type":"string","description":"The bash command to execute."},"description":{"type":"string","description":"Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"."},"timeoutMs":{"type":"number","description":"Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry."},"workdir":{"type":"string","description":"Working directory for this command. Defaults to the session workspace; a relative path is resolved against it."},"run_in_background":{"type":"boolean","description":"Run in the background and return a task id immediately (collect with task_output, stop with task_kill). No timeout applies."}},"required":["command","description"]}},{"name":"edit","description":"Edit an existing UTF-8 text file by replacing literal text.","parameters":{"type":"object","properties":{"file_path":{"type":"string","description":"Path to edit, resolved by the filesystem backend."},"old_string":{"type":"string","description":"Literal text to replace. Must match exactly."},"new_string":{"type":"string","description":"Literal replacement text. Use an empty string to delete the match."},"replace_all":{"type":"booLine truncated
|
||||
{"type":"request/header","seq":7,"time":1785498587438,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"You are an AI agent powered by the DeepSeek Harness SDK.\n\nYou are headless-agent, a coding assistant powered by the deepseek-v4-flash model.\n\nVerify your work by running the code or tests. Keep answers brief and\nfactual.\n\n\nUse the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.\n\nUse the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.\n\nUse the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.\n\nCheck the [exit code: N] marker on every bash result; investigate failures before moving on.\n\nUse a terminal session only when work needs persistent terminal state or interactive stdin; prefer bash/read/write/edit for bounded one-shot operations. Track every terminal session id and close sessions that no longer matter. An inferred_idle or timeout result does not prove the foreground command exited.\n\nTrack every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.\n\nUse the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.\n\nUse the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.","tools":[{"name":"bash","description":"Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`.","parameters":{"type":"object","properties":{"command":{"type":"string","description":"The bash command to execute."},"description":{"type":"string","description":"Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"."},"timeoutMs":{"type":"number","description":"Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry."},"workdir":{"type":"string","description":"Working directory for this command. Defaults to the session workspace; a relative path is resolved against it."},"run_in_background":{"type":"boolean","description":"Run in the background and return a task id immediately (collect with task_output, stop with task_kill). No timeout applies."}},"required":["command","description"]}},{"name":"edit","description":"Edit an existing UTF-8 text file by replacing literal text.","parameters":{"type":"object","properties":{"file_path":{"type":"string","description":"Path to edit, resolved by the filesystem backend."},"old_string":{"type":"string","description":"Literal text to replace. Must match exactly."},"new_string":{"type":"string","description":"Literal replacement text. Use an empty string to delete the match."},"replace_all":{"type":"booLine truncated
|
||||
{"type":"request/context","seq":8,"time":1785730504660,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
|
||||
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"pty-spawn","name":"terminal_open","argumentsDelta":"{\"type\":\"shell\",\"name\":\"main\"}"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"pty-spawn","name":"terminal_open","arguments":"{\"type\":\"shell\",\"name\":\"main\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1785498587439,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1785730504661,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":14,"time":1785730504661,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-spawn","name":"terminal_open","arguments":"{\"type\":\"shell\",\"name\":\"main\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"8cad9650-de5a-4075-8aa3-1b35e67efc2d"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":14,"time":1785730504661,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-spawn","name":"terminal_open","arguments":"{\"type\":\"shell\",\"name\":\"main\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"898077a7-5a40-4517-9eeb-bd840750776b"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":15,"time":1785730504662,"data":{"turn":1,"step":1,"callId":"pty-spawn","name":"terminal_open","arguments":"{\"type\":\"shell\",\"name\":\"main\"}"}}
|
||||
{"type":"tool/result","seq":16,"time":1785730504671,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"pty-spawn"},"content":[{"type":"tool-result","toolCallId":"pty-spawn","content":[{"type":"text","text":"started terminal session pty-1 (main) [type: shell]\ndsh> "}],"isError":false}],"role":"user","id":"1ba1f641-0cf2-496c-895e-3982aa40b0ed"}},"sourceEventSeqs":[15],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":16,"time":1785730504671,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"pty-spawn"},"content":[{"type":"tool-result","toolCallId":"pty-spawn","content":[{"type":"text","text":"started terminal session pty-1 (main) [type: shell]\ndsh> "}],"isError":false}],"role":"user","id":"ea4323d9-aa22-4a9c-a7cc-165c147ce624"}},"sourceEventSeqs":[15],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":17,"time":1785730504671,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":18,"time":1785730504679,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
@@ -23,9 +23,9 @@
|
||||
{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"pty-send","name":"terminal_send","arguments":"{\"sessionId\":\"pty-1\",\"text\":\"printf 'PTY_OK\\\\n'\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1785498587457,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1785730504680,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":24,"time":1785730504680,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-send","name":"terminal_send","arguments":"{\"sessionId\":\"pty-1\",\"text\":\"printf 'PTY_OK\\\\n'\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"f5850535-906d-4a78-8518-a733ec91bbd8"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[19,20,21,22,23],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":24,"time":1785730504680,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-send","name":"terminal_send","arguments":"{\"sessionId\":\"pty-1\",\"text\":\"printf 'PTY_OK\\\\n'\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"dc612d66-912d-4248-b95c-48675b127574"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[19,20,21,22,23],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":25,"time":1785730504680,"data":{"turn":1,"step":2,"callId":"pty-send","name":"terminal_send","arguments":"{\"sessionId\":\"pty-1\",\"text\":\"printf 'PTY_OK\\\\n'\"}"}}
|
||||
{"type":"tool/result","seq":26,"time":1785730504688,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"pty-send"},"content":[{"type":"tool-result","toolCallId":"pty-send","content":[{"type":"text","text":"K\ndsh> \n[wait: stdin_read]\n[session: running]\n[output truncated]"}],"isError":false}],"role":"user","id":"4f19b0d0-b31d-40b7-84e5-2783131cb363"},"meta":{"viewport":"printf 'PTY_OK\\n'\nPTY_OK\ndsh> ","waitReason":"stdin_read","sessionStatus":{"kind":"running"},"truncated":false}},"sourceEventSeqs":[25],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":26,"time":1785730504688,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"pty-send"},"content":[{"type":"tool-result","toolCallId":"pty-send","content":[{"type":"text","text":"K\ndsh> \n[wait: stdin_read]\n[session: running]\n[output truncated]"}],"isError":false}],"role":"user","id":"fbcfc85b-a9fb-4421-81ec-f16f8ba772c4"},"meta":{"viewport":"printf 'PTY_OK\\n'\nPTY_OK\ndsh> ","waitReason":"stdin_read","sessionStatus":{"kind":"running"},"truncated":false}},"sourceEventSeqs":[25],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":27,"time":1785730504688,"data":{"turn":1,"step":2}}
|
||||
{"type":"step/start","seq":28,"time":1785730504696,"data":{"turn":1,"step":3}}
|
||||
{"type":"assistant/chunk","seq":29,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
@@ -33,9 +33,9 @@
|
||||
{"type":"assistant/chunk","seq":31,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"pty-read","name":"terminal_read","arguments":"{\"sessionId\":\"pty-1\",\"offset\":0,\"count\":20}"}}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":1785498587473,"data":{"turn":1,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1785730504697,"data":{"turn":1,"step":3,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":34,"time":1785730504697,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-read","name":"terminal_read","arguments":"{\"sessionId\":\"pty-1\",\"offset\":0,\"count\":20}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"4e0d8eed-877d-4a5b-bd92-e7c4c8f2cf23"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[29,30,31,32,33],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":34,"time":1785730504697,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-read","name":"terminal_read","arguments":"{\"sessionId\":\"pty-1\",\"offset\":0,\"count\":20}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"c023922c-a5a6-4dd4-bace-578f30e453f5"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[29,30,31,32,33],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":35,"time":1785730504697,"data":{"turn":1,"step":3,"callId":"pty-read","name":"terminal_read","arguments":"{\"sessionId\":\"pty-1\",\"offset\":0,\"count\":20}"}}
|
||||
{"type":"tool/result","seq":36,"time":1785730504704,"data":{"turn":1,"step":3,"message":{"source":{"kind":"tool","callId":"pty-read"},"content":[{"type":"tool-result","toolCallId":"pty-read","content":[{"type":"text","text":"dsh> printf 'PTY_OK\\n'\nPTY_OK\ndsh> \n[lines: 0-3 of 3]"}],"isError":false}],"role":"user","id":"72be20be-6022-4363-b9bb-1d9f2cce0e20"}},"sourceEventSeqs":[35],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":36,"time":1785730504704,"data":{"turn":1,"step":3,"message":{"source":{"kind":"tool","callId":"pty-read"},"content":[{"type":"tool-result","toolCallId":"pty-read","content":[{"type":"text","text":"dsh> printf 'PTY_OK\\n'\nPTY_OK\ndsh> \n[lines: 0-3 of 3]"}],"isError":false}],"role":"user","id":"b68e7f58-8151-4a5c-974b-2ec5dc11186f"}},"sourceEventSeqs":[35],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":37,"time":1785730504704,"data":{"turn":1,"step":3}}
|
||||
{"type":"step/start","seq":38,"time":1785730504712,"data":{"turn":1,"step":4}}
|
||||
{"type":"assistant/chunk","seq":39,"time":0,"data":{"turn":1,"step":4,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
@@ -43,9 +43,9 @@
|
||||
{"type":"assistant/chunk","seq":41,"time":0,"data":{"turn":1,"step":4,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"pty-signal","name":"terminal_signal","arguments":"{\"sessionId\":\"pty-missing\",\"signal\":\"SIGINT\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1785498587489,"data":{"turn":1,"step":4,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1785730504713,"data":{"turn":1,"step":4,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":44,"time":1785730504713,"data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-signal","name":"terminal_signal","arguments":"{\"sessionId\":\"pty-missing\",\"signal\":\"SIGINT\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"87c2857f-5da3-4050-b081-e044e207be88"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[39,40,41,42,43],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":44,"time":1785730504713,"data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-signal","name":"terminal_signal","arguments":"{\"sessionId\":\"pty-missing\",\"signal\":\"SIGINT\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"3ce15df8-96d0-48a6-a1bd-908f779980bb"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[39,40,41,42,43],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":45,"time":1785730504713,"data":{"turn":1,"step":4,"callId":"pty-signal","name":"terminal_signal","arguments":"{\"sessionId\":\"pty-missing\",\"signal\":\"SIGINT\"}"}}
|
||||
{"type":"tool/result","seq":46,"time":1785730504721,"data":{"turn":1,"step":4,"message":{"source":{"kind":"tool","callId":"pty-signal"},"content":[{"type":"tool-result","toolCallId":"pty-signal","content":[{"type":"text","text":"Error: unknown PTY session pty-missing"}],"isError":true}],"role":"user","id":"c6e3ff43-809c-4bd8-ba88-da7294c3385a"}},"sourceEventSeqs":[45],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":46,"time":1785730504721,"data":{"turn":1,"step":4,"message":{"source":{"kind":"tool","callId":"pty-signal"},"content":[{"type":"tool-result","toolCallId":"pty-signal","content":[{"type":"text","text":"Error: unknown PTY session pty-missing"}],"isError":true}],"role":"user","id":"2809ea63-1fa4-4878-b610-8e05ac36b294"}},"sourceEventSeqs":[45],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":47,"time":1785730504721,"data":{"turn":1,"step":4}}
|
||||
{"type":"step/start","seq":48,"time":1785730504730,"data":{"turn":1,"step":5}}
|
||||
{"type":"assistant/chunk","seq":49,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
@@ -53,9 +53,9 @@
|
||||
{"type":"assistant/chunk","seq":51,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"pty-kill","name":"terminal_close","arguments":"{\"sessionId\":\"pty-1\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1785498587503,"data":{"turn":1,"step":5,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1785730504731,"data":{"turn":1,"step":5,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":54,"time":1785730504731,"data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-kill","name":"terminal_close","arguments":"{\"sessionId\":\"pty-1\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"5b53e0d5-1c68-4988-9fab-d885a9122fe8"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[49,50,51,52,53],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":54,"time":1785730504731,"data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-kill","name":"terminal_close","arguments":"{\"sessionId\":\"pty-1\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"5c7f9390-88ed-48cf-9ada-099320bac560"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[49,50,51,52,53],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":55,"time":1785730504731,"data":{"turn":1,"step":5,"callId":"pty-kill","name":"terminal_close","arguments":"{\"sessionId\":\"pty-1\"}"}}
|
||||
{"type":"tool/result","seq":56,"time":1785730504738,"data":{"turn":1,"step":5,"message":{"source":{"kind":"tool","callId":"pty-kill"},"content":[{"type":"tool-result","toolCallId":"pty-kill","content":[{"type":"text","text":"closed terminal session pty-1"}],"isError":false}],"role":"user","id":"20e3fe4d-3d9d-4771-b75d-1f287fc20048"}},"sourceEventSeqs":[55],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":56,"time":1785730504738,"data":{"turn":1,"step":5,"message":{"source":{"kind":"tool","callId":"pty-kill"},"content":[{"type":"tool-result","toolCallId":"pty-kill","content":[{"type":"text","text":"closed terminal session pty-1"}],"isError":false}],"role":"user","id":"d16c935f-465d-4dd8-882c-fe2dda09357c"}},"sourceEventSeqs":[55],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":57,"time":1785730504738,"data":{"turn":1,"step":5}}
|
||||
{"type":"step/start","seq":58,"time":1785730504746,"data":{"turn":1,"step":6}}
|
||||
{"type":"assistant/chunk","seq":59,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
@@ -63,9 +63,9 @@
|
||||
{"type":"assistant/chunk","seq":61,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"pty-list","name":"terminal_list","arguments":"{}"}}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":1785498587517,"data":{"turn":1,"step":6,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":1785730504747,"data":{"turn":1,"step":6,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":64,"time":1785730504747,"data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-list","name":"terminal_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"178fb9bc-859c-484d-8376-096a705de30a"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[59,60,61,62,63],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":64,"time":1785730504747,"data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"tool-call","id":"pty-list","name":"terminal_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"b8603c4c-d8b7-4675-a8cd-7e5ea8b949a8"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[59,60,61,62,63],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":65,"time":1785730504747,"data":{"turn":1,"step":6,"callId":"pty-list","name":"terminal_list","arguments":"{}"}}
|
||||
{"type":"tool/result","seq":66,"time":1785730504755,"data":{"turn":1,"step":6,"message":{"source":{"kind":"tool","callId":"pty-list"},"content":[{"type":"tool-result","toolCallId":"pty-list","content":[{"type":"text","text":"(no terminal sessions)"}],"isError":false}],"role":"user","id":"584673b3-9221-4a42-b9e4-69ce1b9f4d60"}},"sourceEventSeqs":[65],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":66,"time":1785730504755,"data":{"turn":1,"step":6,"message":{"source":{"kind":"tool","callId":"pty-list"},"content":[{"type":"tool-result","toolCallId":"pty-list","content":[{"type":"text","text":"(no terminal sessions)"}],"isError":false}],"role":"user","id":"47a7e409-fff1-4102-a4bb-be6bd6f7db53"}},"sourceEventSeqs":[65],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":67,"time":1785730504755,"data":{"turn":1,"step":6}}
|
||||
{"type":"step/start","seq":68,"time":1785730504763,"data":{"turn":1,"step":7}}
|
||||
{"type":"assistant/chunk","seq":69,"time":0,"data":{"turn":1,"step":7,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
@@ -73,6 +73,6 @@
|
||||
{"type":"assistant/chunk","seq":71,"time":0,"data":{"turn":1,"step":7,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":1785498587531,"data":{"turn":1,"step":7,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":1785730504764,"data":{"turn":1,"step":7,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":74,"time":1785730504764,"data":{"turn":1,"step":7,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"23d457ba-b690-4bb9-b434-86f43c9f4da5"},"usage":{"inputTokens":10,"outputTokens":3}},"sourceEventSeqs":[69,70,71,72,73],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":74,"time":1785730504764,"data":{"turn":1,"step":7,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"b6ccfcf3-5079-4c17-a361-610e1e588065"},"usage":{"inputTokens":10,"outputTokens":3}},"sourceEventSeqs":[69,70,71,72,73],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":75,"time":1785730504764,"data":{"turn":1,"step":7}}
|
||||
{"type":"turn/end","seq":76,"time":1785730504764,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -228,7 +228,9 @@ describe('ACP prompt lifecycle', () => {
|
||||
|
||||
await expect(harness.client.prompt({ sessionId, prompt: [{ type: 'text', text: 'two' }] }))
|
||||
.resolves.toEqual({ stopReason: 'end_turn' })
|
||||
await vi.waitFor(() => { expect(messageText(harness!)).toBe('next') })
|
||||
// 'partial' is the cancelled turn's finalized prefix update; 'next' proves
|
||||
// the second prompt settled independently of the aborted turn's late end.
|
||||
await vi.waitFor(() => { expect(messageText(harness!)).toBe('partialnext') })
|
||||
})
|
||||
|
||||
it('a retry turn adopts the prompt instead of rejecting at the failed turn end', async () => {
|
||||
|
||||
@@ -283,7 +283,10 @@ function deriveRequests(events: readonly SessionEvent[]): readonly RequestView[]
|
||||
const request = index === undefined ? undefined : requests[index]
|
||||
updateAssistant(index, {
|
||||
completedAt: sourceEvent.time,
|
||||
status: 'complete',
|
||||
// A cancellation-finalized prefix is not a completed request: leave it
|
||||
// running so the following step/end classifies it as before the prefix
|
||||
// event existed.
|
||||
status: sourceEvent.data.interrupted === true ? 'running' : 'complete',
|
||||
resultSeq: sourceEvent.seq,
|
||||
provenance: {
|
||||
provider: sourceEvent.data.message.source.provider,
|
||||
|
||||
@@ -85,6 +85,31 @@ describe('inspectRequests', () => {
|
||||
expect(snapshot.callSchemas.get('call-1')?.name).toBe('read')
|
||||
})
|
||||
|
||||
it('leaves a cancellation-finalized prefix uncompleted so the step boundary classifies it', () => {
|
||||
const events = [
|
||||
at(0, 'step/start', { turn: 1, step: 1 }),
|
||||
at(1, 'request/header', {
|
||||
reason: 'initial',
|
||||
header: { config: { provider: 'fake', model: 'model' }, system: 'system' },
|
||||
}),
|
||||
at(2, 'assistant/message', {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
message: createAssistantMessage({
|
||||
content: [{ type: 'text', text: 'cut short' }],
|
||||
source: { provider: 'fake', model: 'model' },
|
||||
}),
|
||||
interrupted: true,
|
||||
}),
|
||||
at(3, 'step/end', { turn: 1, step: 1 }),
|
||||
at(4, 'turn/end', { turn: 1, reason: { kind: 'aborted', reason: { kind: 'user' } } }),
|
||||
]
|
||||
const snapshot = inspectRequests(entriesOf(events))
|
||||
expect(snapshot.requests).toMatchObject([
|
||||
{ purpose: 'assistant', resultSeq: 2, status: 'error' },
|
||||
])
|
||||
})
|
||||
|
||||
it('does not promote a truncated resume or change header to the initial prompt', () => {
|
||||
for (const reason of ['resume', 'change'] as const) {
|
||||
const snapshot = inspectRequests(entriesOf([
|
||||
|
||||
@@ -162,6 +162,10 @@ function finalNode(
|
||||
firstTokenTime: state.firstTokenTime ?? null,
|
||||
completedTime: event.time,
|
||||
},
|
||||
// A cancellation-finalized prefix keeps its truncation marker: the
|
||||
// durable event carries the classification, so the settled node still
|
||||
// renders as interrupted (Stopped chip) rather than a completed answer.
|
||||
...event.data.interrupted === true ? { interrupted: true } : {},
|
||||
}
|
||||
}
|
||||
const location = context.start?.location ?? context.matches.at(-1)?.location
|
||||
|
||||
@@ -158,6 +158,22 @@ describe('built-in conversation node Definitions', () => {
|
||||
expect(interrupted?.data).toMatchObject({ status: 'interrupted' })
|
||||
expect((interrupted?.data as AssistantChatData).finalNode?.interrupted).toBe(true)
|
||||
|
||||
// A cancellation-finalized prefix event keeps its durable truncation
|
||||
// marker through the settled projection.
|
||||
const markedValue = assembler([
|
||||
at(20, 'turn/start', { turn: 3 }),
|
||||
at(21, 'step/start', { turn: 3, step: 1 }),
|
||||
at(22, 'assistant/message', {
|
||||
turn: 3,
|
||||
step: 1,
|
||||
message: assistantMessage('assistant-3', 'cut short'),
|
||||
interrupted: true,
|
||||
}, { surfaceOp: 'append' }),
|
||||
])
|
||||
const marked = node(snapshot(markedValue), 'assistant-step')
|
||||
expect(marked?.data).toMatchObject({ status: 'interrupted', blocks: [{ kind: 'text', text: 'cut short' }] })
|
||||
expect((marked?.data as AssistantChatData).finalNode?.interrupted).toBe(true)
|
||||
|
||||
const hiddenValue = assembler([
|
||||
at(20, 'turn/start', { turn: 3 }),
|
||||
at(21, 'step/start', { turn: 3, step: 1 }),
|
||||
|
||||
@@ -367,6 +367,11 @@ export class ReactLoopAgent implements Agent {
|
||||
signal.throwIfAborted()
|
||||
const finish = assembler.finish
|
||||
if (finish.kind === 'error' || finish.kind === 'aborted') {
|
||||
// A failed attempt is never finalizable: provider failures commit
|
||||
// nothing, and a cancel landing during recovery (typically the
|
||||
// llm/retry backoff, after clients reset the streamed rendering)
|
||||
// must not resurrect the failed stream's prefix.
|
||||
attempt = undefined
|
||||
const action = await this.dispatch.waterfall(
|
||||
'agent/request-error', {
|
||||
turn,
|
||||
@@ -382,7 +387,6 @@ export class ReactLoopAgent implements Agent {
|
||||
if (action?.kind !== 'retry') {
|
||||
throw new LlmError(finish.failure.message, finish.failure.code, finish.failure)
|
||||
}
|
||||
attempt = undefined
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -444,6 +448,7 @@ export class ReactLoopAgent implements Agent {
|
||||
turn,
|
||||
step,
|
||||
message,
|
||||
interrupted: true,
|
||||
...attempt.assembler.usage === undefined ? {} : { usage: attempt.assembler.usage },
|
||||
},
|
||||
{ surfaceOp: 'append', sourceEventSeqs: attempt.chunkSeqs },
|
||||
|
||||
@@ -490,10 +490,12 @@ describe('Agent.cancel()', () => {
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
// The prefix the user watched stream is committed as the step's message,
|
||||
// citing exactly the chunk events that delivered it.
|
||||
// carrying the truncation marker and citing exactly the chunk events that
|
||||
// delivered it.
|
||||
const message = agent.session.events.find(e => e.type === 'assistant/message')
|
||||
expect(message?.type === 'assistant/message' ? message.data.message.content : undefined)
|
||||
.toEqual([{ type: 'text', text: 'partial' }])
|
||||
expect(message?.type === 'assistant/message' ? message.data.interrupted : undefined).toBe(true)
|
||||
const chunkSeqs = agent.session.events.filter(e => e.type === 'assistant/chunk').map(e => e.seq)
|
||||
expect(message?.sourceEventSeqs).toEqual(chunkSeqs)
|
||||
const types = agent.session.events.map(e => e.type)
|
||||
@@ -559,6 +561,61 @@ describe('Agent.cancel()', () => {
|
||||
expect(agent.session.events.some(e => e.type === 'tool/call')).toBe(false)
|
||||
})
|
||||
|
||||
it('cancel during error recovery does not finalize the failed stream', async () => {
|
||||
const adapter = new MockAdapter([[
|
||||
{ type: 'block-start', index: 0, blockType: 'text' },
|
||||
{ type: 'text-delta', index: 0, text: 'doomed partial' },
|
||||
{ type: 'finish', reason: { kind: 'error', failure: { message: 'boom', code: 'SERVER_ERROR' } } },
|
||||
]])
|
||||
const ctx = await harness(adapter)
|
||||
const agent = ctx.agentLoop.create(SessionId('recovery-cancel'), { provider: 'mock', model: 'mock' })
|
||||
// Cancellation lands while agent/request-error is in flight — the window
|
||||
// dsh-llm-retry opens when its backoff waits after appending llm/retry.
|
||||
ctx.on('agent/request-error', async ({ agent: subject }) => {
|
||||
if (subject === agent) subject.cancel({ kind: 'user' })
|
||||
})
|
||||
|
||||
send(agent, 'go')
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
// The failed stream's prefix stays off the surface: clients reset it on
|
||||
// retry, and provider failures commit nothing.
|
||||
expect(agent.session.events.some(e => e.type === 'assistant/message')).toBe(false)
|
||||
const end = agent.session.events.find(e => e.type === 'turn/end')
|
||||
expect(end?.type === 'turn/end' ? end.data.reason.kind : undefined).toBe('aborted')
|
||||
})
|
||||
|
||||
it('retry discards the failed attempt; the final message cites only its own chunks', async () => {
|
||||
const adapter = new MockAdapter([
|
||||
[
|
||||
{ type: 'block-start', index: 0, blockType: 'text' },
|
||||
{ type: 'text-delta', index: 0, text: 'doomed partial' },
|
||||
{ type: 'finish', reason: { kind: 'error', failure: { message: 'boom', code: 'SERVER_ERROR' } } },
|
||||
],
|
||||
textResponse('recovered'),
|
||||
])
|
||||
const ctx = await harness(adapter)
|
||||
const agent = ctx.agentLoop.create(SessionId('retry-discards-content'), { provider: 'mock', model: 'mock' })
|
||||
ctx.on('agent/request-error', async () => ({ kind: 'retry' as const }))
|
||||
|
||||
send(agent, 'go')
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
const messages = agent.session.events.filter(e => e.type === 'assistant/message')
|
||||
expect(messages).toHaveLength(1)
|
||||
const message = messages[0]!
|
||||
expect(message.type === 'assistant/message' ? message.data.message.content : undefined)
|
||||
.toEqual([{ type: 'text', text: 'recovered' }])
|
||||
expect(message.type === 'assistant/message' ? message.data.interrupted : undefined).toBeUndefined()
|
||||
// The abandoned attempt's chunks stay out of the completion's source set.
|
||||
const doomedSeqs = agent.session.events
|
||||
.filter(e => e.type === 'assistant/chunk'
|
||||
&& e.data.chunk.type === 'text-delta' && e.data.chunk.text === 'doomed partial')
|
||||
.map(e => e.seq)
|
||||
expect(doomedSeqs).toHaveLength(1)
|
||||
expect(message.sourceEventSeqs).not.toContain(doomedSeqs[0])
|
||||
})
|
||||
|
||||
it('cancel before any visible content finalizes nothing', async () => {
|
||||
const adapter = new MockAdapter([{
|
||||
hangAfter: [
|
||||
|
||||
@@ -250,10 +250,11 @@ export interface SessionEventMap {
|
||||
* the model output and its accounting travel together (there is no separate
|
||||
* usage record). `usage` is absent when the adapter reported none. A turn
|
||||
* cancelled mid-stream finalizes its delivered text/reasoning prefix as this
|
||||
* event (undispatched tool calls are absent); an aborted turn with no such
|
||||
* event streamed no visible content.
|
||||
* event with `interrupted: true` (undispatched tool calls are absent), so
|
||||
* consumers classify the truncation without re-deriving it from turn
|
||||
* boundaries; an aborted turn with no such event streamed no visible content.
|
||||
*/
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage }
|
||||
'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage; interrupted?: true }
|
||||
/**
|
||||
* The model requested one tool invocation: `name` with the raw `arguments`
|
||||
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
||||
|
||||
@@ -27,7 +27,8 @@ interface PartialBlock {
|
||||
* {@link ContentBlock}s and a final assistant {@link Message}.
|
||||
*
|
||||
* The agent loop feeds it while logging raw chunks for replay fidelity, then
|
||||
* reads `blocks()` / `message()` / `usage` / `finish` once the stream ends.
|
||||
* reads `blocks()` / `message()` / `usage` / `finish` once the stream ends,
|
||||
* or `interruptedBlocks()` when cancellation cut the stream short.
|
||||
*
|
||||
* Tolerant of delta-only protocols (no block-start/end); deltas arriving for
|
||||
* an index already closed by `block-end` are ignored (malformed stream) so a
|
||||
|
||||
@@ -2583,7 +2583,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'SessionEventMap',
|
||||
declaration: 'export interface SessionEventMap {\n \'turn/start\': {\n turn: number;\n };\n \'turn/end\': {\n turn: number;\n reason: TurnEndReason;\n };\n \'step/start\': {\n turn: number;\n step: number;\n };\n \'step/end\': {\n turn: number;\n step: number;\n };\n \'user/message\': UserMessage;\n \'assistant/chunk\': {\n turn: number;\n step: number;\n chunk: StreamChunk;\n };\n \'assistant/message\': {\n turn: number;\n step: number;\n message: AssistantMessage;\n usage?: TokenUsage;\n };\n \'tool/call\': {\n turn: number;\n step: number;\n callId: CallId;\n name: string;\n arguments: string;\n };\n \'tool/result\': {\n turn: number;\n step: number;\n message: ToolResultMessage;\n error?: {\n name: string;\n code: string;\n };\n meta?: JsonValue;\n };\n \'todo/write\': {\n todos: TodoItem[];\n };\n \'request/header\': {\n header: EpochHeader;\n reason: RequestHeaderReason;\n };\n \'request/context\': RequestContext;\n \'session/end-seed\': Record<string, never>;\n}',
|
||||
declaration: 'export interface SessionEventMap {\n \'turn/start\': {\n turn: number;\n };\n \'turn/end\': {\n turn: number;\n reason: TurnEndReason;\n };\n \'step/start\': {\n turn: number;\n step: number;\n };\n \'step/end\': {\n turn: number;\n step: number;\n };\n \'user/message\': UserMessage;\n \'assistant/chunk\': {\n turn: number;\n step: number;\n chunk: StreamChunk;\n };\n \'assistant/message\': {\n turn: number;\n step: number;\n message: AssistantMessage;\n usage?: TokenUsage;\n interrupted?: true;\n };\n \'tool/call\': {\n turn: number;\n step: number;\n callId: CallId;\n name: string;\n arguments: string;\n };\n \'tool/result\': {\n turn: number;\n step: number;\n message: ToolResultMessage;\n error?: {\n name: string;\n code: string;\n };\n meta?: JsonValue;\n };\n \'todo/write\': {\n todos: TodoItem[];\n };\n \'request/header\': {\n header: EpochHeader;\n reason: RequestHeaderReason;\n };\n \'request/context\': RequestContext;\n \'session/end-seed\': Record<string, never>;\n}',
|
||||
},
|
||||
{
|
||||
name: 'SessionEventMetadataFilter',
|
||||
|
||||
Reference in new issue
Block a user