Commit 56f112e
authored
fix: make research chat and PDF use one canonical report (#178)
## Summary
- Publishes a successful deep-research tool's validated Markdown
directly as assistant text in the same durable Workflow step that
publishes its artifact.
- Treats successful `research_deep` and `research_fanout` calls as
terminal response producers, so no second model turn can summarize or
rewrite the report.
- Suppresses model pre-tool narration for terminal research calls,
making the visible report body and PDF input identical by construction.
## Why
Production QA for PR #177 proved that the PDF was complete and valid,
but the outer agent ignored its prompt and posted a 1,386-character
summary instead of the canonical report. Prompt-only enforcement cannot
guarantee content identity. This moves ownership to the durable runtime
boundary.
## Architecture
The research tool continues to return `{ artifact, report }`, with
`report` already validated against collected citation provenance. The
Workflow projects that strict output into both destinations:
1. `artifact` becomes the durable deliverable part.
2. `report` becomes the assistant text part.
3. The run completes immediately after the successful tool turn.
Both parts share the deterministic `tool:<model-step>:<tool-index>`
publication receipt, so Workflow replay cannot duplicate either one.
## Decisions Made
| Decision | Choice | Alternatives considered | Reasoning |
|---|---|---|---|
| Response ownership | Durable Workflow publishes canonical report | Ask
the outer model to copy tool output | The production model summarized
despite explicit instructions. |
| Completion | Successful research tool is terminal | Run another model
turn | A second turn can rewrite, truncate, or append content. |
| Progress prose | Suppress research-call model text | Keep preamble
before the report | Ensures visible report content and PDF source are
the same Markdown. |
| Failure path | Continue agent loop on tool error | Treat every
research attempt as terminal | The model still needs to explain provider
or validation failures. |
| Replay behavior | Report and artifact share tool publication |
Separate non-durable append | Existing atomic receipts already guarantee
exactly-once transcript publication. |
## Edge Cases Handled
| Scenario | Handling |
|---|---|
| Research tool fails | No canonical response is projected; the next
model turn explains the error. |
| Non-terminal search tool runs | Existing model loop is unchanged. |
| Research output shape is invalid | Strict schema parsing fails the run
rather than publishing divergent content. |
| Worker/DO replays publication | Deterministic event receipt prevents
duplicate report or artifact parts. |
| Model emits a preamble with the tool call | It stays in model context
but is not published to the user. |
## How to Review
1. Review `agent-run-research-response-support.ts` for terminal-tool
classification and strict output projection.
2. Review `agent-run-workflow.ts` for suppression, atomic report
publication, and terminal completion.
3. Confirm the agent-worker README and system prompt document the same
ownership boundary.
## Verification
- `pnpm lint`
- `pnpm typecheck`
- `pnpm turbo build --force`
- `pnpm deadcode`
- `pnpm architecture:check`
- `pnpm turbo skills:build`
- Direct projection contract: successful report preserved byte-for-byte;
failed, non-terminal, and invalid outputs handled as designed.
- Production evidence before this fix: reload recovery completed one run
with one tool and one artifact, but persisted assistant text was a short
model summary rather than the PDF's report.1 parent be85331 commit 56f112e
4 files changed
Lines changed: 57 additions & 4 deletions
File tree
- apps/agent-worker
- src/durable-objects
- packages/agent-core/src/mastra
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
86 | 90 | | |
87 | 91 | | |
88 | 92 | | |
| |||
Lines changed: 29 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
Lines changed: 23 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
13 | 17 | | |
14 | 18 | | |
15 | 19 | | |
| |||
164 | 168 | | |
165 | 169 | | |
166 | 170 | | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
167 | 174 | | |
168 | 175 | | |
169 | 176 | | |
| |||
302 | 309 | | |
303 | 310 | | |
304 | 311 | | |
305 | | - | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
306 | 316 | | |
307 | 317 | | |
308 | 318 | | |
309 | 319 | | |
310 | | - | |
| 320 | + | |
311 | 321 | | |
312 | 322 | | |
313 | 323 | | |
| |||
347 | 357 | | |
348 | 358 | | |
349 | 359 | | |
350 | | - | |
| 360 | + | |
| 361 | + | |
351 | 362 | | |
352 | 363 | | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
353 | 372 | | |
354 | 373 | | |
355 | 374 | | |
| |||
362 | 381 | | |
363 | 382 | | |
364 | 383 | | |
| 384 | + | |
365 | 385 | | |
366 | 386 | | |
367 | 387 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
236 | | - | |
| 236 | + | |
237 | 237 | | |
238 | 238 | | |
239 | 239 | | |
| |||
0 commit comments