mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-08 03:10:26 +00:00
## Summary
This is the foundational step of the unified agent transcript
implementation: it establishes one canonical set of replay types in
`fabro-types` and threads them into the existing `agent.message`,
`agent.tool.started`, and `agent.tool.completed` event shapes — without
breaking any existing producers or consumers.
## What changed
**New `fabro-types::transcript` module** owns `ContentPart`,
`ImageData`, `AudioData`, `DocumentData`, `ThinkingData`, `ToolCall`,
`ToolResult`, `MessageKind`, `MessageSource`, `PairMessageRef`,
`TranscriptMessage`, and `MessageId`. These were previously defined in
`fabro-llm::types`; they now live at the canonical layer.
**`fabro-llm::types`** drops its local definitions and re-exports from
`fabro-types` so every existing `fabro_llm::types::*` import keeps
compiling without change.
**`AgentMessageProps`** gains an optional `message:
Option<TranscriptMessage>` field; `AgentToolStartedProps` gains
`tool_call`, `turn_id`, and `parent_message_id`;
`AgentToolCompletedProps` gains `tool_result` and `turn_id`. All new
fields use `#[serde(default, skip_serializing_if = "Option::is_none")]`
so existing stored events deserialize cleanly.
**All current event emitters** (`fabro-workflow/event/convert.rs`, demo
fixtures, test helpers) are updated to set the new fields to `None` —
this is a mechanical compatibility update; actual enrichment comes in
later tasks.
### Design decisions worth noting
- `MessageKind` captures LLM role semantics (system / user / reasoning /
agent); `MessageSource` captures audit provenance (steer, pair,
loop_detection, …). They are intentionally kept separate so a steering
message can be `kind=user, source=steer` without collapsing the
distinction.
- `TranscriptMessage` is named with the `Transcript` prefix specifically
to avoid import ambiguity with `fabro_agent::Message` and
`fabro_llm::types::Message`.
- `ProviderAnswer` and `ProviderReasoning` are included as
`MessageSource` variants so committed model outputs carry a first-class
audit label distinct from user-originated inputs.
- The new fields are additive-only; no narrow legacy fields were
removed. Consumer migration is a separate step.
### Fabro Details
<details>
<summary>Ran 9 stages in 47m 27s for $12.79</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 6s | – | 0 |
| preflight_lint | 2m 21s | – | 0 |
| implement | 20m 6s | $8.53 | 0 |
| simplify_opus | 13m 36s | $2.68 | 0 |
| simplify_gpt | 4m 17s | $1.58 | 0 |
| verify | 4m 7s | – | 0 |
| fmt | 3s | – | 0 |
| **Total** | **47m 27s** | **$12.79** | **0** |
</details>
<details>
<summary>Ran <code>ImplementPlan.fabro</code> (12 nodes and 15
edges)</summary>
```dot
digraph ImplementPlan {
graph [
goal="Implement and simplify",
model_stylesheet="
* { model: claude-opus-4-7; }
"
]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD."]
simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
verify [label="Verify", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1 && cargo dev docs refresh 2>&1 && cargo dev docs check 2>&1", goal_gate=true, retry_target="fixup"]
fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings, test failures, and generated docs errors.", max_visits=3]
fmt [label="Format", shape=parallelogram, script="cargo +nightly-2026-04-14 fmt --all 2>&1", max_retries=0]
start -> toolchain
toolchain -> preflight_compile [condition="outcome=succeeded"]
toolchain -> exit
preflight_compile -> preflight_lint [condition="outcome=succeeded"]
preflight_compile -> exit
preflight_lint -> implement [condition="outcome=succeeded"]
preflight_lint -> fix_lints
fix_lints -> preflight_lint
implement -> simplify_opus -> simplify_gpt -> verify
verify -> fmt [condition="outcome=succeeded"]
verify -> fixup
fixup -> verify
fmt -> exit
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
51 lines
1.3 KiB
TOML
51 lines
1.3 KiB
TOML
[package]
|
|
name = "fabro-llm"
|
|
edition.workspace = true
|
|
version.workspace = true
|
|
publish = false
|
|
license.workspace = true
|
|
description = "A unified client library for multiple LLM providers"
|
|
repository = "https://github.com/brynary/arc"
|
|
readme = "README.md"
|
|
keywords = ["llm", "ai", "openai", "anthropic"]
|
|
categories = ["api-bindings"]
|
|
|
|
[lib]
|
|
doctest = false
|
|
|
|
[lints]
|
|
workspace = true
|
|
|
|
[dependencies]
|
|
anyhow.workspace = true
|
|
thiserror.workspace = true
|
|
serde.workspace = true
|
|
serde_json.workspace = true
|
|
strum.workspace = true
|
|
tokio.workspace = true
|
|
uuid.workspace = true
|
|
rand.workspace = true
|
|
futures.workspace = true
|
|
tokio-stream.workspace = true
|
|
async-trait.workspace = true
|
|
base64.workspace = true
|
|
bytes.workspace = true
|
|
tokio-util.workspace = true
|
|
tracing.workspace = true
|
|
fabro-http.workspace = true
|
|
fabro-auth = { path = "../fabro-auth" }
|
|
fabro-model = { path = "../fabro-model" }
|
|
fabro-redact.workspace = true
|
|
fabro-static.workspace = true
|
|
fabro-types = { path = "../fabro-types" }
|
|
fabro-util = { path = "../fabro-util" }
|
|
|
|
[dev-dependencies]
|
|
http = "1"
|
|
insta = { workspace = true }
|
|
tokio = { workspace = true, features = ["test-util", "macros"] }
|
|
httpmock = "0.8"
|
|
serde_json.workspace = true
|
|
toml.workspace = true
|
|
fabro-macros = { path = "../fabro-macros" }
|
|
fabro-test = { workspace = true }
|