# OpenRouter tool calling vs Furoshiki callable skills (research notes)

*Saved for later planning — not a committed roadmap. Last updated with the 2026-04-01 architecture review.*

## Executive summary

Furoshiki’s **callable plugin skills** (`~/.furoshiki/plugins/`, `type: callable`) already use **OpenRouter-native tool calling** on the main Telegram path when a manifest exists: `tools` on every request round, `tool_calls` from the model, local execution via `run_callable_skill`, then `role: "tool"` results. See OpenRouter’s guide: [Tool & function calling](https://openrouter.ai/docs/guides/features/tool-calling).

Further robustness is mostly **tighter JSON Schemas**, optional **server-side arg validation**, **`tool_choice` policy** (`none` vs `auto`), **model capability checks** (`tools` support), and **unifying** the one path that still uses **JSON `skill_calls` in prose** (`run_micro_contemplation`) if desired.

## Evidence in-repo

| Piece | Role |
|-------|------|
| `plugin_loader.callable_manifest_to_openrouter_tools` | Manifest → OpenAI-style `tools` |
| `llm.call_llm_chat_with_tool_loop` | Agentic loop; executes `run_callable_skill` per `tool_call` |
| `telegram_listener.py` | If manifest non-empty, uses tool loop for main reply (`parallel_tool_calls=False`) |
| `skill_orchestrator.plan_and_invoke` | Separate planning chat with same tool loop (`source=skill_orchestrator_plan`) |
| `run_micro_contemplation.py` | Optional `skill_calls` inside model JSON — **not** native tools for that step |

## OpenRouter doc highlights (for later design)

- **Tools must be sent on each round** of a multi-turn tool conversation (easy to forget).
- **`tool_choice`**: `auto`, `none`, or force a specific function.
- **`parallel_tool_calls`**: sequential vs parallel; Furoshiki forces **false** on Telegram for ordering safety.
- **Interleaved thinking**: optional; trades latency/tokens for clearer multi-step tool reasoning on supported models.
- **Streaming + tools**: needs careful handling of deltas (`tool_calls` fragments).

## Robustness ideas (not implemented)

1. Require real **`args_schema`** on callable hooks; avoid default `additionalProperties: true` where possible.
2. Validate arguments with **jsonschema** (or similar) before `run_callable_skill`.
3. Use **`tool_choice: "none"`** on turns where callable skills must not run (token + safety).
4. Confirm **listener model** supports `tools` on OpenRouter ([model filter](https://openrouter.ai/models?supported_parameters=tools)).
5. Optionally align **micro_contemplation** with native tools instead of JSON `skill_calls`.

## What tool calling does not replace

- Plugin **permissions**, subprocess **sandboxing**, and **timeouts** remain client-side.
- Good **descriptions** / **`when_to_use`** text still drive correct tool selection.

---

## Related: “Do you have any questions for me?” (not a callable skill)

The user-initiated **ask-user roundup** is implemented as a **Telegram listener dedicated path**: **`micro_brain`** sets **`route_pending_ask_user`** (plus **`/myquestions`** commands and regex fallback when inbound LLM is off), then **`self_questions_roundup.py`**, **`record_pending_ask_user`**, and **`classify_user_answer_multi` → `user_answered` → soul_engine**. It is **not** a `callable` plugin in `~/.furoshiki/plugins/`.

Operator/agents: see **`.cursor/skills/furoshiki-user-questions-roundup/SKILL.md`**.

**Related:** temporary large context blocks (design, memory, etc.) with TTL and renewal — **`docs/DYNAMIC-CONTEXT-LAYERS.md`**.
