Cowork B: Mealie, Obsidian and search tools return artifacts #61

Merged
ryan merged 2 commits from feat/cowork-tool-artifacts into main 2026-10-12 18:31:35 -04:00
Owner

Task B (part 1) from docs/cowork-ui-plan.md.

What

These tools now use @tool(response_format="content_and_artifact") and return (text, artifact):

tool kind
mealie_get_recipe mealie.recipe
mealie_meal_plans, mealie_todays_meals mealie.meal_plan
mealie_shopping_lists mealie.shopping_lists
obsidian_read_note, journal_get_today obsidian.note
simba_search, obsidian_search_notes search.documents
  • The text is byte-for-byte unchanged, so the LLM's view doesn't move. Error, empty and not-configured paths return no artifact.
  • Helpers live in blueprints/conversation/artifacts_util.py: titles, date ranges, snippet and note caps, JSON-safe frontmatter, and the Mealie/Obsidian source_ref links.
  • New optional env vars (in .env.example + docker-compose.yml):
    • MEALIE_GROUP_SLUG, default home, for /g/<group>/r/<slug> recipe links
    • OBSIDIAN_VAULT_NAME; obsidian://open links only appear when it's set
    • Add these to the real .env if needed.
  • YNAB kinds are a follow-up once #55 merges.
  • Safe to merge before or after #60. Without #60 the artifacts ride in ToolMessage.artifact and are ignored.

Test plan

  • pytest tests/ -q: 147 passed. New test_tool_artifacts.py (22 tests) invokes the tools with ToolCall dicts and checks the exact text, the artifact shapes, the error paths and JSON serialisability.
  • Coordinator review, 2 passes (no findings)

🤖 Generated with Claude Code

Task B (part 1) from `docs/cowork-ui-plan.md`. ## What These tools now use `@tool(response_format="content_and_artifact")` and return `(text, artifact)`: | tool | kind | |---|---| | `mealie_get_recipe` | `mealie.recipe` | | `mealie_meal_plans`, `mealie_todays_meals` | `mealie.meal_plan` | | `mealie_shopping_lists` | `mealie.shopping_lists` | | `obsidian_read_note`, `journal_get_today` | `obsidian.note` | | `simba_search`, `obsidian_search_notes` | `search.documents` | - The **text is byte-for-byte unchanged**, so the LLM's view doesn't move. Error, empty and not-configured paths return no artifact. - Helpers live in `blueprints/conversation/artifacts_util.py`: titles, date ranges, snippet and note caps, JSON-safe frontmatter, and the Mealie/Obsidian `source_ref` links. - New optional env vars (in `.env.example` + `docker-compose.yml`): - `MEALIE_GROUP_SLUG`, default `home`, for `/g/<group>/r/<slug>` recipe links - `OBSIDIAN_VAULT_NAME`; `obsidian://open` links only appear when it's set - **Add these to the real `.env` if needed.** - YNAB kinds are a follow-up once #55 merges. - Safe to merge before or after #60. Without #60 the artifacts ride in `ToolMessage.artifact` and are ignored. ## Test plan - [x] `pytest tests/ -q`: 147 passed. New `test_tool_artifacts.py` (22 tests) invokes the tools with ToolCall dicts and checks the exact text, the artifact shapes, the error paths and JSON serialisability. - [x] Coordinator review, 2 passes (no findings) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Updated: rebased onto main after #55 and #60 merged, resolving the agents.py conflict in #55's favour. Added a second commit with the YNAB artifacts from blueprints/ynab/tools.py: ynab.summary, ynab.transactions (≤ 500 rows, ordered by id as a tie-breaker), ynab.spending_report and ynab.category_budget. age_of_money and goal_target are optional, and start_date can be null for open-ended transaction searches. 240 tests pass. Coordinator review: 2 passes, no blocking findings.

Updated: rebased onto main after #55 and #60 merged, resolving the agents.py conflict in #55's favour. Added a second commit with the YNAB artifacts from blueprints/ynab/tools.py: ynab.summary, ynab.transactions (≤ 500 rows, ordered by id as a tie-breaker), ynab.spending_report and ynab.category_budget. age_of_money and goal_target are optional, and start_date can be null for open-ended transaction searches. 240 tests pass. Coordinator review: 2 passes, no blocking findings.
ryan added 2 commits 2026-10-12 18:31:21 -04:00
Convert mealie_get_recipe, mealie_meal_plans, mealie_todays_meals,
mealie_shopping_lists, obsidian_read_note, journal_get_today,
obsidian_search_notes and simba_search to content_and_artifact. The text
the LLM sees is unchanged; the artifact is a JSON-safe ToolArtifact dict
per the Cowork UI contract (mealie.recipe, mealie.meal_plan,
mealie.shopping_lists, obsidian.note, search.documents). Error, empty and
not-configured paths return no artifact.

simba_search's artifact changes from a list of Documents (unused) to a
search.documents dict.

New optional env vars: MEALIE_GROUP_SLUG (default "home") for Mealie v2
recipe links, OBSIDIAN_VAULT_NAME for obsidian://open links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Convert ynab_budget_summary, ynab_search_transactions, ynab_spending_report
and ynab_category_budget to content_and_artifact (ynab.summary,
ynab.transactions, ynab.spending_report, ynab.category_budget). The text
the LLM sees is unchanged. Not-synced, bad-input, empty and error paths
return no artifact; Decimals become floats and dates ISO strings.

ynab_search_transactions now fetches up to 500 rows in one query (with an
id tie-break so ordering is stable) and lists the first `limit` in the
text; the artifact carries all of them. ynab_account_balances,
ynab_recent_changes and ynab_sql stay text-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ryan force-pushed feat/cowork-tool-artifacts from 84dd972ed0 to 20ec7ca8b6 2026-10-12 18:31:21 -04:00 Compare
ryan merged commit ce50979ff7 into main 2026-10-12 18:31:35 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ryan/simbarag#61