- Restrict fallback_models to only reference preset names in model_presets. - Add schema validation to reject unknown preset names in fallback_models. - Remove build_provider_for_model() since bare model fallback is no longer supported. - Simplify make_provider_factory() to only look up presets by name. - Update onboard UI to remove "Add custom model" option from fallback chain. - Update tests to use preset names instead of bare model strings in fallback chains. - Fix test imports referencing deleted _make_provider function.
7.8 KiB
My Tool
Let the agent sense and adjust its own runtime state — like asking a coworker "are you busy? can you switch to a bigger monitor?"
Why You Need It
Normal tools let the agent operate on the outside world (read/write files, search code). But the agent knows nothing about itself — it doesn't know which model it's running on, how many iterations are left, or how many tokens it has consumed.
My tool fills this gap. With it, the agent can:
- Know who it is: What model am I using? Where is my workspace? How many iterations remain?
- Adapt on the fly: Complex task? Expand the context window. Simple chat? Switch to a faster model.
- Remember across turns: Store notes in your scratchpad that persist into the next conversation turn.
Note
This tool uses snake_case keys (
model_preset,context_window_tokens). The matching config fields inconfig.jsonare camelCase (modelPreset,contextWindowTokens). Seeconfiguration.mdfor how to define presets in your config.
Configuration
Enabled by default (read-only mode). The agent can check its state but not set it.
tools:
my:
enable: true # default: true
allow_set: false # default: false (read-only)
To allow the agent to set its configuration (e.g. switch models, adjust parameters), set tools.my.allow_set: true.
Legacy tools.myEnabled / tools.mySet keys are auto-migrated on load, and
rewritten in-place the next time nanobot onboard refreshes the config.
All modifications are held in memory only — restart restores defaults.
check — Check "my" current state
Without parameters, returns a key config overview:
my(action="check")
# → max_iterations: 40
# model_preset: 'fast'
# workspace: PosixPath('/tmp/workspace')
# provider_retry_mode: 'standard'
# max_tool_result_chars: 16000
# _current_iteration: 3
# _last_usage: {'prompt_tokens': 45000, 'completion_tokens': 8000}
# Note: prompt_tokens is cumulative across all turns, not current context window occupancy.
With a key parameter, drill into a specific config:
my(action="check", key="_last_usage.prompt_tokens")
# → How many prompt tokens I've used so far
my(action="check", key="model_preset")
# → Current active preset name (e.g. 'fast')
my(action="check", key="model_presets")
# → Lists all preset names and their models, e.g.:
# fast → gpt-4.1-mini (openai)
# deep → claude-opus-4-7 (anthropic)
my(action="check", key="web_config.enable")
# → Whether web search is enabled
What you can do with it
| Scenario | How |
|---|---|
| "What model are you using?" | check("model_preset") |
| "How many more tool calls can you make?" | check("max_iterations") minus check("_current_iteration") |
| "How many tokens has this conversation used?" | check("_last_usage") — cumulative across all turns |
| "Where is your working directory?" | check("workspace") |
| "Show me your full config" | check() |
| "Are there any subagents running?" | check("subagents") — shows phase, iteration, elapsed time, tool events |
set — Runtime tuning
Changes take effect immediately, no restart required.
my(action="set", key="max_iterations", value=80)
# → Bump iteration limit from 40 to 80
my(action="set", key="model_preset", value="fast")
# → Switch to the 'fast' preset (model, provider, temperature, etc. all at once)
#
# If the preset name does not exist:
# → Error: model_preset 'unknown' not found. Available: fast, deep
my(action="set", key="context_window_tokens", value=131072)
# → Expand context window for long documents
You can also store custom state in your scratchpad:
my(action="set", key="current_project", value="nanobot")
my(action="set", key="user_style_preference", value="concise")
my(action="set", key="task_complexity", value="high")
# → These values persist into the next conversation turn
Protected parameters
These parameters have validation — invalid values are rejected:
| Parameter | Type | Range / Constraint | Purpose |
|---|---|---|---|
max_iterations |
int | 1–100 | Max tool calls per conversation turn |
model_preset |
str | must exist in model_presets |
Switch to a named preset bundle |
Other parameters (e.g. model, context_window_tokens, workspace, provider_retry_mode, max_tool_result_chars) can be set freely, as long as the value is JSON-safe.
Note
Setting
modelorcontext_window_tokensdirectly automatically clears the activemodel_preset, because the live state no longer matches the preset bundle. Usemodel_presetfor atomic switches instead.
Practical Scenarios
"This task is complex, I need more room"
Agent: This codebase is large, let me expand my context window to handle it.
→ my(action="set", key="context_window_tokens", value=131072)
"Simple question, don't waste compute"
Agent: This is a straightforward question, let me switch to the fast preset.
→ my(action="set", key="model_preset", value="fast")
"Remember user preferences across turns"
Turn 1: my(action="set", key="user_prefers_concise", value=True)
Turn 2: my(action="check", key="user_prefers_concise")
# → True (still remembers the user likes concise replies)
"Self-diagnosis"
User: "Why aren't you searching the web?"
Agent: Let me check my web config.
→ my(action="check", key="web_config.enable")
# → False
Agent: Web search is disabled — please set web.enable: true in your config.
"Token budget management"
Agent: Let me check how much budget I have left.
→ my(action="check", key="_last_usage")
# → {"prompt_tokens": 45000, "completion_tokens": 8000}
Agent: I've used ~53k tokens total so far. I'll keep my remaining replies concise.
"Subagent monitoring"
Agent: Let me check on the background tasks.
→ my(action="check", key="subagents")
# → 2 subagent(s):
# [task-1] 'Code review'
# phase: running, iteration: 5, elapsed: 12.3s
# tools: read(✓), grep(✓)
# usage: {'prompt_tokens': 8000, 'completion_tokens': 1200}
# [task-2] 'Write tests'
# phase: pending, iteration: 0, elapsed: 0.2s
# tools: none
Agent: The code review is progressing well. The test task hasn't started yet.
Safety Mechanisms
Core design principle: All modifications live in memory only. Restart restores defaults. The agent cannot cause persistent damage.
Off-limits (BLOCKED)
Cannot be checked or modified — fully hidden:
| Category | Attributes | Reason |
|---|---|---|
| Core infrastructure | bus, provider, _running |
Changes would crash the system |
| Tool registry | tools |
Must not remove its own tools |
| Subsystems | runner, sessions, consolidator, etc. |
Affects other users/sessions |
| Sensitive data | _mcp_servers, _pending_queues, etc. |
Contains credentials and message routing |
| Security boundaries | restrict_to_workspace, channels_config |
Bypassing would violate isolation |
| Python internals | __class__, __dict__, etc. |
Prevents sandbox escape |
Read-only (check only)
Can be checked but not set:
| Category | Attributes | Reason |
|---|---|---|
| Subagent manager | subagents |
Observable, but replacing breaks the system |
| Execution config | exec_config |
Can check sandbox/enable status, cannot change it |
| Web config | web_config |
Can check enable status, cannot change it |
| Iteration counter | _current_iteration |
Updated by runner only |
Sensitive field protection
Sub-fields matching sensitive names (api_key, password, secret, token, etc.) are blocked from both check and set, regardless of parent path. This prevents credential leaks via dot-path traversal (e.g. web_config.search.api_key).