Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a1ee598
docs(migration): continuation plan for tinyagents 1.4/1.5 (goals, tod…
senamakel Jul 3, 2026
1ec4c3a
docs(migration): coverage sweep for previously unreferenced harness f…
senamakel Jul 3, 2026
c7f2873
chore(deps): update tinyagents to version 1.5.0 and add .diff to .git…
senamakel Jul 3, 2026
e2f010c
build(deps): vendor tinyagents as submodule, patch both Cargo worlds …
senamakel Jul 3, 2026
4c28958
refactor(tinyagents): drive RepeatedToolFailureMiddleware over crate …
senamakel Jul 3, 2026
fbafad2
ci(docker): carry vendored tinyagents into core image builds
senamakel Jul 3, 2026
dd63b3a
feat(tinyagents): wire crate-native per-node RetryPolicy onto delegat…
senamakel Jul 3, 2026
5568ca2
feat(tinyagents): restart-stable journal event ids + thread status (0…
senamakel Jul 3, 2026
1c4ce63
refactor(tinyagents): delete warn-only CacheAlignMiddleware shadow (C3)
senamakel Jul 3, 2026
ce22061
docs(tinyagents): record why unknown-tool stays ReturnToolError, not …
senamakel Jul 3, 2026
ba20de2
feat(todos): shadow-mirror task board onto crate graph::todos (C2b fi…
senamakel Jul 3, 2026
1ac896f
docs(migration): C2b todos parity note (crate TodoTool vs tools/todo.rs)
senamakel Jul 3, 2026
634231a
feat(thread_goals): C2a dual-write adapter onto tinyagents graph::goals
senamakel Jul 3, 2026
2a97eb5
feat(config): add session_dual_write agent flag (default ON)
senamakel Jul 3, 2026
044c721
feat(sessions): dual-write live turns into tinyagents store, gated by…
senamakel Jul 3, 2026
02be67b
docs(migration): C3 execution corrections — Microcompact not crate-su…
senamakel Jul 3, 2026
5a1227d
docs(migration): wave-1 handoff — branch map, corrections, gotchas, n…
senamakel Jul 3, 2026
efa23cd
Merge branch 'feat/tinyagents-c1-dual-writes' into feat/tinyagents-c0…
senamakel Jul 3, 2026
1a2f124
Merge branch 'feat/tinyagents-c2-goals' into feat/tinyagents-c0-15-ba…
senamakel Jul 3, 2026
7d71262
Merge branch 'feat/tinyagents-c2-todos' into feat/tinyagents-c0-15-ba…
senamakel Jul 3, 2026
7b8f52a
Merge branch 'feat/tinyagents-c4-journals' into feat/tinyagents-c0-15…
senamakel Jul 3, 2026
800d620
Merge branch 'feat/tinyagents-c3-middleware-dedupe' into feat/tinyage…
senamakel Jul 3, 2026
840969f
style: cargo fmt over merged wave-1 slices
senamakel Jul 3, 2026
e7bdbb7
docs(migration): handoff update — wave-1 merged into PR #4473, wave-2…
senamakel Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/release-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,11 @@ jobs:
with:
ref: ${{ needs.prepare-build.outputs.build_ref }}
fetch-depth: 1
# Targeted init (not `submodules: true`) so we skip the large tauri-cef
# fork the core image doesn't need. The Dockerfile COPYs vendor/ because
# [patch.crates-io] resolves tinyagents from vendor/tinyagents.
- name: Init tinyagents submodule
run: git submodule update --init vendor/tinyagents
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/release-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ jobs:
with:
ref: ${{ needs.prepare-build.outputs.build_ref }}
fetch-depth: 1
# Targeted init (not `submodules: true`) so we skip the large tauri-cef
# fork the core image doesn't need. The Dockerfile COPYs vendor/ because
# [patch.crates-io] resolves tinyagents from vendor/tinyagents.
- name: Init tinyagents submodule
run: git submodule update --init vendor/tinyagents
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build image (no push)
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,4 @@ distribution.cer
# Release note previews
CHANGELOG.preview.md
*.profraw
*.diff
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
[submodule "app/src-tauri/vendor/tauri-plugin-notification"]
path = app/src-tauri/vendor/tauri-plugin-notification
url = https://github.com/tinyhumansai/tauri-plugin-notification.git
[submodule "vendor/tinyagents"]
path = vendor/tinyagents
url = https://github.com/tinyhumansai/tinyagents
4 changes: 1 addition & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ crate-type = ["rlib"]
tinyplace = "1.0.1"
# tinyflows — host-agnostic workflow engine (typed node graph → validate → compile →
# run on tinyagents). Powers the "Workflows" feature via the seam in
# `src/openhuman/tinyflows/` + the `flows::` domain. Pulls tinyagents 1.3 transitively
# `src/openhuman/tinyflows/` + the `flows::` domain. Pulls tinyagents 1.5 transitively
# (same version openhuman already uses — no conflict). Published on crates.io.
tinyflows = "0.3"
# TinyAgents — Rust LLM orchestration framework (LangGraph/LangChain-style):
Expand All @@ -56,7 +56,7 @@ tinyflows = "0.3"
# aligned to 0.40, avoiding duplicate `links = "sqlite3"` native bindings.
# Durable graph checkpoints still use `SqlRunLedgerCheckpointer` until the
# migration re-points those rows to the crate checkpointer.
tinyagents = { version = "1.3", features = ["sqlite"] }
tinyagents = { version = "1.5.0", features = ["sqlite"] }
# TokenJuice code compressor — AST-aware signature extraction. Optional (C build)
# behind the default `tokenjuice-treesitter` feature; disabling it falls back to
# the language-agnostic brace-depth heuristic. See src/openhuman/tokenjuice/compressors/code.rs.
Expand Down Expand Up @@ -230,7 +230,7 @@ fantoccini = { version = "0.22.0", optional = true, default-features = false, fe
serde-big-array = { version = "0.5", optional = true }
pdf-extract = "0.10"
# WhatsApp Web — upstream `whatsapp-rust` 0.5. Its Diesel-backed sqlite-storage
# feature links sqlite3 separately from rusqlite 0.40, so the TinyAgents 1.3
# feature links sqlite3 separately from rusqlite 0.40, so the TinyAgents 1.5
# baseline compiles this provider against wacore's in-memory Backend until a
# rusqlite-backed durable store lands.
whatsapp-rust = { version = "0.5", optional = true, default-features = false, features = ["tokio-runtime"] }
Expand Down Expand Up @@ -331,6 +331,12 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] }
# See: https://github.com/tinyhumansai/openhuman/issues/273
[patch.crates-io]
whisper-rs-sys = { git = "https://github.com/tinyhumansai/whisper-rs-sys.git", branch = "main" }
# TinyAgents is vendored as a git submodule (pinned at the released tag) so
# migration work can change the SDK source in-tree, test it against OpenHuman
# immediately, and PR the diff upstream from the submodule. Keep the submodule
# version in lockstep with the `tinyagents` requirement above. After cloning:
# `git submodule update --init vendor/tinyagents` (worktrees included).
tinyagents = { path = "vendor/tinyagents" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run Rust CI when vendored TinyAgents changes

Because this patch makes both Cargo worlds resolve tinyagents from vendor/tinyagents, SDK changes can now change the compiled product without touching Cargo.toml, Cargo.lock, or src/**. I checked the PR CI path filters in .github/workflows/pr-ci.yml and they do not include vendor/tinyagents or .gitmodules, so a PR that only bumps the submodule pointer would skip the Rust quality/coverage lanes and the Playwright artifact build/cache despite changing a compiled dependency. Add the submodule path (and usually .gitmodules) to the Rust/Tauri/playwright filters and related artifact cache keys so vendored SDK updates are actually built and tested.

Useful? React with 👍 / 👎.


# Emit just enough DWARF in release builds for Sentry to symbolicate Rust
# panics + render surrounding source lines. `line-tables-only` keeps the
Expand Down
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ WORKDIR /build

# Cache dependencies — copy only manifests first
COPY Cargo.toml Cargo.lock rust-toolchain.toml ./
# Vendored TinyAgents SDK (git submodule; [patch.crates-io] points here, so
# the dep-cache build below already resolves it). CI must init the submodule
# before docker build — see the "Init tinyagents submodule" steps in
# release-production.yml / release-staging.yml.
COPY vendor/ vendor/
# Create a dummy src to build deps
RUN mkdir -p src && \
echo 'fn main() {}' > src/main.rs && \
Expand Down
4 changes: 1 addition & 3 deletions app/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ e2e-test-support = ["openhuman_core/e2e-test-support"]
# whisper.cpp to use MSVC's static runtime (/MT), matching CEF and avoiding
# LNK2038/LNK1169 CRT conflicts on Windows.
whisper-rs-sys = { git = "https://github.com/tinyhumansai/whisper-rs-sys.git", branch = "main" }
# TinyAgents vendored submodule (repo-root vendor/tinyagents, pinned at the
# released tag) — same patch as the root Cargo world so both resolve the
# in-tree SDK source. `git submodule update --init vendor/tinyagents` first.
tinyagents = { path = "../../vendor/tinyagents" }

# CEF support lives on the `feat/cef` branch of tauri-apps/tauri. We carry our
# own fork at tinyhumansai/tauri-cef on `feat/cef-notification-intercept` which
Expand Down
8 changes: 4 additions & 4 deletions docs/tinyagents-full-migration-plan/00-baseline.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# 00 — Baseline: crate, features, native links

Current status (2026-07-02): baseline dependency alignment is complete in both
Cargo worlds. `tinyagents 1.3.0` is resolved with the `sqlite` feature,
Current status (2026-07-03): baseline dependency alignment is complete in both
Cargo worlds. `tinyagents 1.5.0` is resolved with the `sqlite` feature,
OpenHuman pins `rusqlite = "=0.40.0"`, both worlds patch through
`vendor/rusqlite-0.40.0` and `vendor/libsqlite3-sys-0.38.0`, and the SDK-gaps
inventory has been refreshed against the published 1.3.0 crate source.

## Steps

1. **Bump `tinyagents` to `"1.3"`** (done in both Cargo worlds — root and
1. **Bump `tinyagents` to `"1.5.0"`** (done in both Cargo worlds — root and
`app/src-tauri/`). Known 1.1→1.2 break already handled
(`MessageDelta::text` ctor). Note: the `openai` crate feature was removed
after 1.2.0 (1.2.1+ features are only `sqlite`/`repl`) — we never enabled
it, so no impact. See "1.3.0 delta" below for new API this plan uses.
2. **Align rusqlite to 0.40** in both worlds (`Cargo.toml` root and
`app/src-tauri/Cargo.toml`). OpenHuman pins `rusqlite = "=0.40.0"` and
enables `tinyagents = { version = "1.3", features = ["sqlite"] }`.
enables `tinyagents = { version = "1.5.0", features = ["sqlite"] }`.
Comment on lines +3 to +18

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'NoProgressTracker|with_node_retry|1\.4\.0|1\.5\.0' docs/tinyagents-full-migration-plan/CONTINUATION-2026-07.md docs/tinyagents-full-migration-plan/HANDOFF-2026-07-03.md 2>/dev/null

Repository: tinyhumansai/openhuman

Length of output: 4144


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== baseline excerpts ==\n'
sed -n '1,120p' docs/tinyagents-full-migration-plan/00-baseline.md | cat -n

printf '\n== continuation excerpts ==\n'
sed -n '1,120p' docs/tinyagents-full-migration-plan/CONTINUATION-2026-07.md | cat -n

printf '\n== handoff excerpts ==\n'
sed -n '1,120p' docs/tinyagents-full-migration-plan/HANDOFF-2026-07-03.md | cat -n

Repository: tinyhumansai/openhuman

Length of output: 31494


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== baseline references to delta/gaps ==\n'
rg -n '1\.3\.0 delta|SDK-gaps|NoProgressTracker|with_node_retry|1\.4\.0|1\.5\.0' docs/tinyagents-full-migration-plan/00-baseline.md docs/tinyagents-full-migration-plan/CONTINUATION-2026-07.md docs/tinyagents-full-migration-plan/HANDOFF-2026-07-03.md

Repository: tinyhumansai/openhuman

Length of output: 5066


Update 00-baseline.md to the 1.4/1.5 delta.
The baseline still points readers to a 1.3.0 delta and says the SDK-gaps inventory was refreshed against 1.3.0, but this plan is already on tinyagents 1.5.0. Rename that section and refresh the gap notes so the newer APIs (CompiledGraph::with_node_retry, NoProgressTracker) are covered.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tinyagents-full-migration-plan/00-baseline.md` around lines 3 - 18,
Update the baseline doc to match the current tinyagents 1.5.0 state: the section
still references a 1.3.0 delta and an inventory refreshed against 1.3.0, so
rename that delta section in 00-baseline.md and revise the gap notes for the
newer API surface. Make sure the updated plan explicitly covers the 1.4/1.5
additions referenced by the review, including CompiledGraph::with_node_retry and
NoProgressTracker.

Compatibility notes:
- `rusqlite 0.40` and `libsqlite3-sys 0.38` are consumed directly from
crates.io. Their build scripts use the `cfg_select!` macro (stable from
Expand Down
2 changes: 1 addition & 1 deletion docs/tinyagents-full-migration-plan/01-tooling/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exposure, and output budgeting onto SDK primitives; delete the OpenHuman
side-lookup pattern and legacy tool plumbing.

Target SDK surface (available across tinyagents 1.2.x–1.3.0; current repo lock
is 1.3.0):
is 1.5.0):

- `Tool::policy() -> ToolPolicy { side_effects, runtime, access }`
serializable safety metadata (read_only/writes_files/network/destructive/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 04.3 — SqliteCheckpointer swap

Baseline is complete: OpenHuman is on tinyagents 1.3.0 with the `sqlite`
Baseline is complete: OpenHuman is on tinyagents 1.5.0 with the `sqlite`
feature and the compatible `rusqlite` pin. The remaining work is the
OpenHuman-row migration/expiry decision.

Expand Down
2 changes: 1 addition & 1 deletion docs/tinyagents-full-migration-plan/10-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ aliases }`, `RegistrySnapshot` (`to_dot()`),
native/MCP/Composio/generated tools, unsafe aliases → registry
diagnostic errors (today: duplicate handling is scattered across generated
tools, MCP, and native registration instead of one SDK diagnostic stream).
TinyAgents 1.3.0 is pinned and exposes `AliasBinding`, alias diagnostics,
TinyAgents 1.5.0 is pinned and exposes `AliasBinding`, alias diagnostics,
cross-kind name-reuse detection, and `ComponentKind::{Middleware,
Checkpointer, TaskStore, Listener}`; OpenHuman still needs to project those
SDK diagnostics into its runtime.
Expand Down
103 changes: 103 additions & 0 deletions docs/tinyagents-full-migration-plan/C2b-todos-parity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# C2b — Task board / todos onto `graph::todos` (parity note)

Status: **first slice landed** (branch `feat/tinyagents-c2-todos`). Adapter-first,
shadow-only. Legacy stays authoritative; nothing here changes product behavior.

This note pairs with the CONTINUATION plan §C2 (step 3) and records what the
crate `tinyagents::graph::todos` surface maps onto in OpenHuman, and what it does
**not** — the residue that must stay in the product host after a future cutover.

## What landed in this slice

- `src/openhuman/todos/graph_shadow.rs` — the adapter:
- Total, lossless status mapping OpenHuman ↔ crate (`map_status_to_crate` /
`map_status_from_crate`) — the two `TaskCardStatus` enums share the same
seven variants (`Todo`, `AwaitingApproval`, `Ready`, `InProgress`,
`Blocked`, `Done`, `Rejected`).
- `TaskBoardCard` field-by-field conversion (`to_crate_card`) — all metadata
(objective, plan, assignedAgent, allowedTools, approvalMode,
acceptanceCriteria, evidence, notes, blocker, sessionThreadId,
sourceMetadata, order, updatedAt) preserved.
- `spawn_mirror` — after every authoritative `todos::ops::save_cards` write of
a `Thread` board, mirrors the persisted cards into a crate `FileStore`
(`<workspace>/tinyagents_graph_store`) under namespace `graph.todos`. Fire-
and-forget, log-only; a crate rejection (e.g. the single-`InProgress`
invariant) is warn-logged as a **DIVERGENCE**.
- `spawn_shadow_claim` — wired into `todos::ops::claim_card` (which is the
single claim entry-point the dispatcher's two claim sites in
`task_dispatcher/dispatch.rs` funnel through, plus RPC/reclaim callers). It
seeds the crate board with the pre-claim snapshot and replays the crate
`claim_card` CAS, warn-logging when the crate ok/err verdict disagrees with
the authoritative legacy claim.

The legacy claim/save path is byte-for-byte preserved: `claim_card` was
refactored to compute one ok/err verdict via an extracted `apply_claim` helper
(so the shadow sees the same not-found / wrong-status / invariant outcomes), but
the persisted result and all existing tests are unchanged.

## Crate `TodoTool` vs `agent/tools/todo.rs` — what maps

The crate ships a single multiplexer `TodoTool` (`op`-dispatched) that is a near
drop-in for the OpenHuman `todo` tool. Shared surface:

| Concern | Crate `TodoTool` | OpenHuman `tools/todo.rs` | Match? |
| --- | --- | --- | --- |
| Dispatch style | single tool, `op` field | single tool, `op` field | yes |
| Thread binding | `ToolExecutionContext::thread_id` (never an arg) | `thread_context::current_thread_id()` + `fork_context` parent | yes (both bind to current thread, never an arg) |
| `add`/`edit`/`update_status`/`remove`/`replace`/`clear`/`list` | present | present | yes |
| Optional card fields | objective/plan/assignedAgent/allowedTools/approvalMode/acceptanceCriteria/evidence/notes/blocker | same | yes |
| Return shape | `{ threadId, cards, markdown }` | `{ threadId, cards, markdown }` | yes |
| Status aliases | `parse_status` (pending→todo, approved→ready, …) | `ops::parse_status` (identical alias table) | yes |
| Single-`InProgress` invariant | `enforce_single_in_progress` (hard error) | `enforce_single_in_progress` (identical) | yes |
| `claim_card` CAS | `store::claim_card(expected, target)` | `ops::claim_card(expected, target)` | yes (identical semantics; proven by shadow tests) |

## What does NOT map (product residue — must stay in the host)

1. **Approval-gate coupling.** OpenHuman's `todo` tool stamps a default
`approvalMode` by reading `config.autonomy.require_task_plan_approval`
(`default_task_approval_mode`), and the dispatcher's
`requires_plan_approval` + `TaskPlanAwaitingApproval` `DomainEvent` drive the
interactive plan-review gate. The crate `TodoTool` has **no** config read and
**no** approval-gate wiring — it exposes `decide_plan`/`revise_plan` state
transitions only. The gate policy stays product.
2. **`DomainEvent` emissions.** `ops::claim_card`/mutations emit
`AgentProgress::TaskBoardUpdated` (via `fork_context` `on_progress`) and the
dispatcher publishes `TaskPlanAwaitingApproval`. The crate store emits
nothing. All event vocabulary stays product (ledger: keep).
3. **RPC projection shapes.** `threads.task_board_*` and `openhuman.todos_*`
(see `todos/schemas.rs`) are the wire contracts the kanban UI binds to
(`app/src/services/api/todosApi.ts`, `USER_TASKS_THREAD_ID = "user-tasks"`).
These are **unchanged** by this slice and, per §C2, become read-side
projections over the crate store only at cutover — not now.
4. **Scratch board.** OpenHuman has a thread-less in-memory `BoardLocation::Scratch`
fallback (tool calls outside a chat thread). The crate board is always
`(Store, thread_id)`, so scratch mutations have **no** crate mirror target —
the shadow skips them (trace-logged).
5. **Persistence substrate + timestamps.** Product persists RFC3339
`updated_at` to `<workspace>/agent_task_boards/<hex(thread_id)>.json`; the
crate uses epoch-millis strings in a `Store` namespace. The mirror does not
reconcile timestamps (cosmetic). Card-id minting also differs
(`task-<uuid>` product vs `task-<seq>` crate) but ids are passed through, so a
persisted board round-trips.
6. **Run lifecycle.** `todos/runs.rs` (run records, heartbeats, stale-reclaim)
and `task_dispatcher/` executor mechanics (executor resolution, autonomous
run, board write-back) are **not** part of the crate todos surface — they
stay product and are the §C2 step-3 "runner node" work, tracked separately.

## Single-writer constraint

The crate `Store` has no compare-and-set, so ns `graph.todos` assumes a single
writer. The core process is that single writer (both the mirror and the
shadow-claim run in-core). Documented in the module header; honoured because all
mutations funnel through `todos::ops`.

## Next (not in this slice)

- Flip the mirror from shadow to authoritative (crate store becomes the source
of truth; legacy JSON becomes a projection or is retired).
- Reimplement `threads.task_board_*` / `openhuman.todos_*` as projections over
the crate store.
- Replace the dispatcher claim/poll loop with the crate `claim_card` CAS + a
graph runner node, keeping `DomainEvent` emission + channel bindings product.
- Delete `task_board.rs` + todo CRUD mechanics + dispatcher executor mechanics
(~3.2k + tests) once parity logs are clean.
Loading
Loading