Commit c730ec0
authored
fix(agent): continue truncated model turns (#185)
## Summary
- Treat provider `length` finish reasons as nonterminal model turns.
- Checkpoint the partial response and continue durably with an internal
instruction to split interrupted operations into smaller complete tool
calls.
- Validate the SDK finish-reason contract at the agent boundary and
accept only `stop` as tool-free semantic completion.
- Fail blocked, provider-error, and invalid terminal reasons explicitly
instead of publishing incomplete work as successful.
## Root cause
The production Pomodoro retry recovered its Daytona runtime correctly,
but Claude reached its per-response output limit while forming the next
operation. The Workflow used only the absence of parsed tool calls as
its completion condition, so it finalized that truncated turn and
exposed the sandbox readiness page.
## Architecture
The Mastra adapter validates the six AI SDK finish reasons. Cloudflare
Workflow owns the policy: tool calls execute durably; `length` appends a
non-visible continuation message and starts another checkpointed model
turn; only `stop` completes a tool-free run. This keeps semantic
completion in charge without adding a step, token, duration, or cost
ceiling.
## Decisions
| Decision | Choice | Reason |
|---|---|---|
| Handle truncation in Workflow | Durable continuation | It preserves
replay safety and applies to every provider and run type. |
| Preserve partial response history | Append an internal user
continuation message | The next turn sees the exact work already
attempted and avoids repeating completed actions. |
| Keep provider output settings unchanged | No fixed output-token
override | A larger fixed cap only postpones truncation and does not
establish correct completion semantics. |
| Reject non-stop terminal reasons | Explicit upstream failure | Content
filtering and provider errors are not successful outcomes. |
## Production evidence
- Run `019fe1b3-af54-7a69-bd1d-0cd940842e41` completed after model turn
5 returned `finishReason: "length"` with no tool call.
- The replacement Daytona sandbox was healthy and mounted the correct
durable volume, isolating this from the sandbox recovery issue fixed in
#183 and #184.
## Verification
- `pnpm lint`
- `pnpm typecheck`
- `pnpm turbo build --force`
- `pnpm deadcode`
- `pnpm architecture:check`
- `pnpm turbo skills:build`
- production flow will be repeated after merge and exact-SHA Cloudflare
deployment
## How to review
1. Review the finish-reason schema in
`packages/agent-core/src/mastra/durable-agent-step.ts`.
2. Review the durable loop policy in
`apps/agent-worker/src/durable-objects/agent-run-workflow.ts`.
3. Confirm the shared schema is enforced by
`agent-run-workflow-runtime.ts` and the README contract matches the
implementation.1 parent 8d17949 commit c730ec0
6 files changed
Lines changed: 58 additions & 3 deletions
File tree
- apps/agent-worker
- src/durable-objects
- packages/agent-core
- src
- mastra
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
90 | 94 | | |
91 | 95 | | |
92 | 96 | | |
| |||
Lines changed: 2 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
87 | 88 | | |
88 | 89 | | |
89 | 90 | | |
90 | | - | |
| 91 | + | |
91 | 92 | | |
92 | 93 | | |
93 | 94 | | |
| |||
Lines changed: 37 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
57 | 58 | | |
58 | 59 | | |
59 | 60 | | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
60 | 75 | | |
61 | 76 | | |
62 | 77 | | |
| |||
185 | 200 | | |
186 | 201 | | |
187 | 202 | | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
188 | 208 | | |
189 | 209 | | |
190 | 210 | | |
| |||
219 | 239 | | |
220 | 240 | | |
221 | 241 | | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
222 | 259 | | |
223 | 260 | | |
224 | 261 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
7 | 18 | | |
8 | 19 | | |
9 | 20 | | |
10 | 21 | | |
11 | 22 | | |
12 | 23 | | |
13 | 24 | | |
14 | | - | |
| 25 | + | |
15 | 26 | | |
16 | 27 | | |
17 | 28 | | |
| |||
51 | 62 | | |
52 | 63 | | |
53 | 64 | | |
54 | | - | |
| 65 | + | |
55 | 66 | | |
56 | 67 | | |
57 | 68 | | |
| |||
0 commit comments