monkeybot-cli 0.5.1__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/PKG-INFO +3 -3
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/pyproject.toml +3 -3
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/skills/monkeybot/SKILL.md +3 -3
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/skills/monkeybot/references/config-sections.md +8 -20
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/chat_session.py +3 -3
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/chat_tui.py +5 -5
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/commands/chat.py +2 -3
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/commands/doctor.py +153 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/commands/validate.py +2 -13
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/realtime/talk_ui.py +0 -1
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold.py +12 -3
- monkeybot_cli-0.7.0/src/monkeybot_cli/scaffold_defaults/browser/SKILL.md +41 -0
- monkeybot_cli-0.7.0/src/monkeybot_cli/scaffold_defaults/goal/SKILL.md +31 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/monkeybot.example.yaml +25 -8
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_e2e.py +5 -1
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_session.py +2 -2
- monkeybot_cli-0.7.0/tests/test_doctor.py +217 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_gateway_health.py +2 -2
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_scaffold.py +8 -1
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/uv.lock +11 -3
- monkeybot_cli-0.5.1/src/monkeybot_cli/scaffold_defaults/browser/SKILL.md +0 -16
- monkeybot_cli-0.5.1/tests/test_doctor.py +0 -85
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/.gitignore +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/__init__.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/chat_file_index.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/chat_local_shell.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/chat_renderer.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/chat_status_bar.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/chat_theme.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/chat_tool_display.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/chat_tui_widgets.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/commands/__init__.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/commands/loop.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/commands/new.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/commands/refresh.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/commands/run_cmd.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/commands/talk.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/compat.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/config_resolve.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/exit_commands.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/extras_catalog.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/gateway_health.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/main.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/opensandbox_lifecycle.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/output.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/process_tree.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/providers.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/realtime/__init__.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/realtime/audio_io.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/realtime/client.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/realtime/gateway_manager.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/realtime/push_to_talk.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/realtime/session.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/realtime/session_controller.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/realtime/wire_encode.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/runtime_python.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/AGENT.md +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/Dockerfile +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/__init__.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/command_allowlist.yaml +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/dockerignore +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/env.example +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/image-generator/SKILL.md +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/image-generator/generate_image.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/loop/SKILL.md +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/mcp.json +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/opensandbox.docker.toml +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/otel-collector.example.yaml +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/scaffold_defaults/permissions.yaml +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/session_controller.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/src/monkeybot_cli/terminal_markdown.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_continue.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_errors.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_file_index.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_grounding.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_local_shell.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_renderer_parity.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_status_bar.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_theme.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_tool_display.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_chat_tui.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_cli.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_config_resolve.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_extras_catalog.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_opensandbox_lifecycle.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_realtime_session_controller.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_run_cmd.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_runtime_python.py +0 -0
- {monkeybot_cli-0.5.1 → monkeybot_cli-0.7.0}/tests/test_terminal_markdown.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: monkeybot-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: CLI to create, configure, validate, and chat with monkeybot agents.
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: httpx>=0.27.0
|
|
7
|
-
Requires-Dist: monkeybot-browser-mcp<1,>=0.
|
|
8
|
-
Requires-Dist: monkeybot[cli]<4,>=3.
|
|
7
|
+
Requires-Dist: monkeybot-browser-mcp<1,>=0.5.0
|
|
8
|
+
Requires-Dist: monkeybot[cli]<4,>=3.1.0
|
|
9
9
|
Requires-Dist: python-dotenv>=1.0.0
|
|
10
10
|
Requires-Dist: pyyaml>=6.0.2
|
|
11
11
|
Requires-Dist: textual>=8.2.8
|
|
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "monkeybot-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.7.0"
|
|
8
8
|
description = "CLI to create, configure, validate, and chat with monkeybot agents."
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
dependencies = [
|
|
11
11
|
# `cli` pulls realtime + typer so `monkeybot talk` works out of the box (text mode).
|
|
12
12
|
# Audio mic/PTT needs the agent (or this env) to also install monkeybot[cli-realtime].
|
|
13
|
-
"monkeybot[cli]>=3.
|
|
14
|
-
"monkeybot-browser-mcp>=0.
|
|
13
|
+
"monkeybot[cli]>=3.1.0,<4",
|
|
14
|
+
"monkeybot-browser-mcp>=0.5.0,<1",
|
|
15
15
|
"httpx>=0.27.0",
|
|
16
16
|
"pyyaml>=6.0.2",
|
|
17
17
|
"python-dotenv>=1.0.0",
|
|
@@ -56,7 +56,7 @@ npx skills add human-plus-machine/monkeybot --skill monkeybot
|
|
|
56
56
|
- `monkeybot_config/monkeybot.yaml` — non-secret settings (model, paths, gateway, behavior).
|
|
57
57
|
- `.env` — secrets and machine-local paths (API keys, GCP project, DB URL).
|
|
58
58
|
- sidecars: `monkeybot_config/AGENT.md` (system prompt), `mcp.json` (MCP servers), `command_allowlist.yaml`.
|
|
59
|
-
- **Precedence — important:** environment variables and `.env` win over `monkeybot.yaml
|
|
59
|
+
- **Precedence — important:** environment variables and `.env` win over `monkeybot.yaml` except `model.*` (provider, name, sampling, context window, summarization model, max turns, cache retention), which is YAML-only. If a user edits YAML but nothing changes, suspect a stale `.env` shadowing it (the YAML→env mapping lives in `runtime_env.py:ENV_MAP`).
|
|
60
60
|
- **Defaults are fine on day one.** Most first-time users only touch `model`, `.env` credentials, and `AGENT.md`.
|
|
61
61
|
|
|
62
62
|
## Running CLI commands
|
|
@@ -112,7 +112,7 @@ uv sync
|
|
|
112
112
|
| `aws_bedrock` | `AWS_ACCESS_KEY_ID` / `AWS_PROFILE` + `AWS_REGION` | `monkeybot[bedrock]` |
|
|
113
113
|
| `huggingface` | `HF_TOKEN` (or `HUGGINGFACE_API_KEY`) | `monkeybot[huggingface]` |
|
|
114
114
|
| `ollama-cloud` | `OLLAMA_API_KEY` | `monkeybot[ollama]` |
|
|
115
|
-
| `ollama-local` | None required — `OLLAMA_BASE_URL` (default `http://localhost:11434`) | `monkeybot[ollama]` |
|
|
115
|
+
| `ollama-local` | None required — `OLLAMA_BASE_URL` (default `http://localhost:11434`). Requests send `keep_alive` 24h so prefix KV cache survives idle. | `monkeybot[ollama]` |
|
|
116
116
|
| `ollama` | Legacy auto-route (key + blank URL = cloud; explicit URL wins) | `monkeybot[ollama]` |
|
|
117
117
|
|
|
118
118
|
**Agent-first dependencies.** The CLI is thin — it does **not** install provider/storage extras globally. `monkeybot new` scaffolds a `pyproject.toml` with the selected provider (and any `--with` extras). Run plain `uv sync` in the agent directory. `monkeybot run` / `chat` spawn the gateway from that project's interpreter (`.venv/bin/python`, else `uv run python`), and `doctor` checks extras in that same interpreter. For a config-only tree (just `monkeybot_config/`, no `pyproject.toml`) the gateway uses the CLI's interpreter when it already has MonkeyBot 3.x (and MemPalace if memory is on). If memory is enabled and that interpreter cannot import MemPalace, `run` / `chat` provision a cached CLI-managed venv under `~/.cache/monkeybot/runtimes/` holding `monkeybot[memory]` pinned to the running core (never rewrites a `pyproject.toml`). `doctor` reuses that cache when it is already present. Otherwise extras must be installed in the CLI env (`uv tool install --with 'monkeybot[<extra>]' monkeybot-cli`).
|
|
@@ -160,7 +160,7 @@ Decision → config map:
|
|
|
160
160
|
| "Run untrusted code" | `sandbox.enabled` + `SANDBOX_API_KEY` |
|
|
161
161
|
| "Use specialist agents" | `subagents.personas` + `monkeybot_config/agents/*.md` |
|
|
162
162
|
| "Connect external tools" | `mcp.json` (`mcpServers` object), then `validate --check-mcp` |
|
|
163
|
-
| "Control cost / context size" | `model
|
|
163
|
+
| "Control cost / context size" | `model.*` |
|
|
164
164
|
| "Restrict dangerous commands" | `tools.denied_patterns`, `command_allowlist.yaml` |
|
|
165
165
|
| "Multiple environments" | top-level `includes:` fragments |
|
|
166
166
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Deep reference for every `monkeybot.yaml` section. Load this only when a user needs to customize beyond Tier 1. The canonical, fully-commented template lives at `cli/src/monkeybot_cli/scaffold_defaults/monkeybot.example.yaml` in the monkeybot repo (copied to `monkeybot_config/monkeybot.example.yaml` when you scaffold); this file adds the **"when would I change this?"** context the comments don't.
|
|
4
4
|
|
|
5
|
-
**Precedence:** env vars and `.env` win over YAML. The YAML→env mapping is `ENV_MAP` in `src/monkeybot/core/config/runtime_env.py`. If a YAML edit has no effect, check for a shadowing env var.
|
|
5
|
+
**Precedence:** env vars and `.env` win over YAML for `ENV_MAP` keys. `model.*` (provider, name, temperature, max_tokens, thinking_budget, context_window, summarization_model, max_turns, cache_retention) is YAML-only — leftover `MODEL_*` env vars are ignored. The YAML→env mapping is `ENV_MAP` in `src/monkeybot/core/config/runtime_env.py`. If a YAML edit has no effect, check for a shadowing env var (except `model.*`).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -34,6 +34,8 @@ Validate check ids: `paths.agent_md.exists`, `paths.skills_path.exists`, `paths.
|
|
|
34
34
|
|
|
35
35
|
## `model`
|
|
36
36
|
|
|
37
|
+
YAML-only for provider, name, sampling, context window, summarization model, max turns, and cache retention. Leftover `MODEL_*` / `MAX_TURNS` / `CONTEXT_SUMMARIZATION_MODEL` env vars are ignored.
|
|
38
|
+
|
|
37
39
|
| Field | Default | When to change |
|
|
38
40
|
|---|---|---|
|
|
39
41
|
| `provider` | `gemini` | Switch LLM vendor (see provider table in SKILL.md) |
|
|
@@ -41,9 +43,11 @@ Validate check ids: `paths.agent_md.exists`, `paths.skills_path.exists`, `paths.
|
|
|
41
43
|
| `temperature` | `0.7` | Lower for deterministic output, higher for creative |
|
|
42
44
|
| `max_tokens` | `60000` | Cap per-response length |
|
|
43
45
|
| `thinking_budget` | `-1` | Gemini: `-1` model default, `0` off, `N` token budget. Ollama reasoning models: `-1` server default, `0` off (`reasoning_effort: none`) |
|
|
44
|
-
| `context_window` | `1000000` | Summarization trigger (tokens); also drives soft-spill / `read_file` char budgets |
|
|
46
|
+
| `context_window` | `1000000` | Summarization trigger (tokens); also drives soft-spill / `read_file` char budgets. **Not** Ollama `num_ctx` |
|
|
47
|
+
| `keep_alive` | `24h` (ollama-local only) | How long local Ollama keeps the model (and KV prefix cache) loaded. YAML only. Set `"0"` to omit. See `docs/ollama-local.md` |
|
|
48
|
+
| `num_ctx` | (unset) | Optional pinned Ollama `num_ctx`. YAML only. Omit = server default. Do not copy `context_window` |
|
|
45
49
|
| `max_turns` | `1000` | Hard cap on turns per run |
|
|
46
|
-
| `summarization_model` | (main model) | Cheaper model for history summarization (
|
|
50
|
+
| `summarization_model` | (main model) | Cheaper model for history summarization (YAML-only)
|
|
47
51
|
|
|
48
52
|
Validate check ids: `model.provider.supported`, `model.name.present`. Supported YAML providers: `gemini`/`vertex`, `openai`, `anthropic`, `vertex-claude`, `huggingface`, `ollama-cloud`, `ollama-local`, `ollama`, `aws_bedrock`, `fake`.
|
|
49
53
|
|
|
@@ -71,22 +75,6 @@ Required when `memory_storage_uri` is `gcs://…` or `provider: vertex-claude` (
|
|
|
71
75
|
| `graceful_shutdown_timeout_sec` | `5` | Allow longer drain on shutdown |
|
|
72
76
|
| `cors_allow_origins` | `http://localhost:5173` | **Custom web UI** — set its origin, or `"*"` for any |
|
|
73
77
|
|
|
74
|
-
## `context_curation`
|
|
75
|
-
|
|
76
|
-
Trims memory injected into context. `enabled: true` by default.
|
|
77
|
-
|
|
78
|
-
Recent window by default; LLM curator only when the index is token-heavy. On curator failure, falls back to the window.
|
|
79
|
-
|
|
80
|
-
| Field | Default | Notes |
|
|
81
|
-
|---|---|---|
|
|
82
|
-
| `memory_window_lines` | `12` | Recent index lines injected; also caps curator-selected lines |
|
|
83
|
-
| `memory_index_cap` | `200` | Organizer keeps this many INDEX.md entries; older rows move to `INDEX.archive.md` |
|
|
84
|
-
| `memory_token_threshold` | `2000` | Call curator when estimated index tokens exceed this |
|
|
85
|
-
| `curator_model` | `gemini-3-flash` | Separate small model; empty = main model |
|
|
86
|
-
| `timeout_sec` | `10` | Curator call timeout |
|
|
87
|
-
|
|
88
|
-
When the prompt shows fewer entries than exist, a structural confidence score triggers a `search_memory` nudge. Skill names are always shown in full in the prompt; use `list_skills` to get the skills root path.
|
|
89
|
-
|
|
90
78
|
## `memory`
|
|
91
79
|
|
|
92
80
|
MemPalace capture, wake-up, and search teaching are on by default. Turn them off with `memory.enabled: false` or `MONKEYBOT_MEMORY_HOOK_ENABLED=0`. The vector stack is the optional `monkeybot[memory]` extra — omit it (and set `enabled: false`) when you do not want chromadb/onnxruntime.
|
|
@@ -176,4 +164,4 @@ Validate check: `config.includes.resolve`.
|
|
|
176
164
|
|
|
177
165
|
## `fake_provider`
|
|
178
166
|
|
|
179
|
-
Test-only. `events_json` feeds `
|
|
167
|
+
Test-only. `events_json` feeds `model.provider: fake` scripted runs (env `MONKEYBOT_FAKE_PROVIDER_EVENTS`). Not for production.
|
|
@@ -229,7 +229,7 @@ class ChatSessionController:
|
|
|
229
229
|
self.usage = UsageStore()
|
|
230
230
|
self.session_id: str | None = None
|
|
231
231
|
self.stream_error = False
|
|
232
|
-
self.
|
|
232
|
+
self.transcript_dir: str | None = None
|
|
233
233
|
self._client: httpx.AsyncClient | None = None
|
|
234
234
|
self._event_queue: asyncio.Queue[str | None] = asyncio.Queue()
|
|
235
235
|
self._stream_task: asyncio.Task[None] | None = None
|
|
@@ -924,9 +924,9 @@ class ChatSessionController:
|
|
|
924
924
|
resp = await self._client.delete(f"{self.base}/sessions/{self.session_id}")
|
|
925
925
|
if resp.status_code == 200:
|
|
926
926
|
data = resp.json()
|
|
927
|
-
report = data.get("
|
|
927
|
+
report = data.get("transcript_dir") if isinstance(data, dict) else None
|
|
928
928
|
if isinstance(report, str) and report.strip():
|
|
929
|
-
self.
|
|
929
|
+
self.transcript_dir = report.strip()
|
|
930
930
|
except Exception:
|
|
931
931
|
logger.warning(
|
|
932
932
|
"session DELETE on close failed session_id=%s",
|
|
@@ -1680,8 +1680,8 @@ class ChatApp(App[int]):
|
|
|
1680
1680
|
src = session_dir / "transcript.ndjson"
|
|
1681
1681
|
if not src.is_file():
|
|
1682
1682
|
self._mount_system(
|
|
1683
|
-
"No trace file found —
|
|
1684
|
-
"
|
|
1683
|
+
"No trace file found — set runtime.transcript_enabled: true in monkeybot.yaml "
|
|
1684
|
+
"and restart the gateway",
|
|
1685
1685
|
error=True,
|
|
1686
1686
|
)
|
|
1687
1687
|
return
|
|
@@ -1829,10 +1829,10 @@ class ChatApp(App[int]):
|
|
|
1829
1829
|
async def _close_session_and_exit(self) -> None:
|
|
1830
1830
|
# Must not be named `_shutdown` — that shadows Textual.App._shutdown.
|
|
1831
1831
|
await self._controller.close()
|
|
1832
|
-
report_dir = getattr(self._controller, "
|
|
1832
|
+
report_dir = getattr(self._controller, "transcript_dir", None)
|
|
1833
1833
|
if isinstance(report_dir, str) and report_dir:
|
|
1834
|
-
self._mount_system(f"Transcript
|
|
1835
|
-
print(f"Transcript
|
|
1834
|
+
self._mount_system(f"Transcript → {report_dir}")
|
|
1835
|
+
print(f"Transcript → {report_dir}", flush=True)
|
|
1836
1836
|
self._exit_code = 1 if self._controller.stream_error else self._exit_code
|
|
1837
1837
|
self.exit(self._exit_code)
|
|
1838
1838
|
|
|
@@ -626,9 +626,9 @@ async def _plain_chat_session(
|
|
|
626
626
|
await controller.submit(user_line)
|
|
627
627
|
finally:
|
|
628
628
|
await controller.close()
|
|
629
|
-
if controller.
|
|
629
|
+
if controller.transcript_dir:
|
|
630
630
|
print(
|
|
631
|
-
f"{_DIM}Transcript
|
|
631
|
+
f"{_DIM}Transcript → {controller.transcript_dir}{_RESET}",
|
|
632
632
|
flush=True,
|
|
633
633
|
)
|
|
634
634
|
await renderer.stop_io_worker()
|
|
@@ -678,7 +678,6 @@ def _spawn_gateway(config_path: Path | None, agent_root: Path, port: int) -> _Sp
|
|
|
678
678
|
env["MONKEYBOT_CONFIG"] = str(config_path)
|
|
679
679
|
env["PORT"] = str(port)
|
|
680
680
|
env.setdefault("LOG_LEVEL", "error")
|
|
681
|
-
env.setdefault("MONKEYBOT_TRANSCRIPT_ENABLED", "1")
|
|
682
681
|
log_file = tempfile.NamedTemporaryFile(
|
|
683
682
|
mode="w+",
|
|
684
683
|
prefix="monkeybot-gateway-",
|
|
@@ -192,6 +192,146 @@ def _add_layout_checks(report: CommandReport, layout: AgentLayout) -> None:
|
|
|
192
192
|
)
|
|
193
193
|
|
|
194
194
|
|
|
195
|
+
_OLLAMA_LOCAL_DOCS = "docs/ollama-local.md"
|
|
196
|
+
_OLLAMA_NUM_CTX_LARGE = 32_768
|
|
197
|
+
_OLLAMA_REASONING_TAGS = ("qwen3", "gemma4", "gemma-4", "deepseek-r1", "qwq", "magistral")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _is_local_ollama_provider(provider: str) -> bool:
|
|
201
|
+
"""True for ollama-local and legacy ``ollama`` (not ollama-cloud)."""
|
|
202
|
+
key = provider.strip().lower().replace("_", "-")
|
|
203
|
+
return key in {"ollama-local", "ollama"}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _looks_like_reasoning_model(model_name: str) -> bool:
|
|
207
|
+
name = model_name.lower()
|
|
208
|
+
return any(tag in name for tag in _OLLAMA_REASONING_TAGS)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _parse_int(raw: object) -> int | None:
|
|
212
|
+
if raw is None or raw == "":
|
|
213
|
+
return None
|
|
214
|
+
try:
|
|
215
|
+
return int(raw)
|
|
216
|
+
except (TypeError, ValueError):
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _strict_positive_int(raw: object) -> tuple[int | None, bool]:
|
|
221
|
+
"""Return ``(value, invalid)``. Unset is ``(None, False)``; garbage or ``<1`` is invalid."""
|
|
222
|
+
if raw is None or raw == "":
|
|
223
|
+
return None, False
|
|
224
|
+
if isinstance(raw, bool) or not isinstance(raw, int):
|
|
225
|
+
return None, True
|
|
226
|
+
if raw < 1:
|
|
227
|
+
return None, True
|
|
228
|
+
return raw, False
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _add_ollama_local_checks(
|
|
232
|
+
report: CommandReport,
|
|
233
|
+
*,
|
|
234
|
+
provider: str,
|
|
235
|
+
model_name: str,
|
|
236
|
+
thinking_budget: object,
|
|
237
|
+
num_ctx: object,
|
|
238
|
+
) -> None:
|
|
239
|
+
"""Warn on local-Ollama prefix-cache traps. Always emit the four check ids."""
|
|
240
|
+
local = _is_local_ollama_provider(provider)
|
|
241
|
+
mlx = "-mlx" in model_name.lower()
|
|
242
|
+
check(
|
|
243
|
+
report,
|
|
244
|
+
id="ollama.local.mlx_runner",
|
|
245
|
+
category="provider",
|
|
246
|
+
severity="warning",
|
|
247
|
+
passed=not mlx,
|
|
248
|
+
skip=not local,
|
|
249
|
+
message=(
|
|
250
|
+
f"model.name {model_name!r} looks like an MLX pack; prefix KV cache "
|
|
251
|
+
"often does not reuse between agent steps"
|
|
252
|
+
if mlx
|
|
253
|
+
else "model.name is not an MLX pack"
|
|
254
|
+
),
|
|
255
|
+
field="model.name",
|
|
256
|
+
value=model_name or None,
|
|
257
|
+
remediation=(
|
|
258
|
+
f"Prefer a GGUF tag for tool-calling loops. See {_OLLAMA_LOCAL_DOCS}." if mlx else None
|
|
259
|
+
),
|
|
260
|
+
docs=_OLLAMA_LOCAL_DOCS,
|
|
261
|
+
)
|
|
262
|
+
budget = _parse_int(thinking_budget)
|
|
263
|
+
if budget is None:
|
|
264
|
+
budget = -1
|
|
265
|
+
reasoning = _looks_like_reasoning_model(model_name)
|
|
266
|
+
thinking_on_default = budget == -1
|
|
267
|
+
check(
|
|
268
|
+
report,
|
|
269
|
+
id="ollama.local.thinking_default",
|
|
270
|
+
category="provider",
|
|
271
|
+
severity="warning",
|
|
272
|
+
passed=not thinking_on_default,
|
|
273
|
+
skip=not local or not reasoning,
|
|
274
|
+
message=(
|
|
275
|
+
"thinking_budget is -1 (server default); reasoning models generate "
|
|
276
|
+
"thinking tokens before the first visible reply"
|
|
277
|
+
if thinking_on_default
|
|
278
|
+
else f"thinking_budget is {budget}"
|
|
279
|
+
),
|
|
280
|
+
field="model.thinking_budget",
|
|
281
|
+
value=budget,
|
|
282
|
+
remediation=(
|
|
283
|
+
f"Set thinking_budget: 0 to send reasoning_effort: none. See {_OLLAMA_LOCAL_DOCS}."
|
|
284
|
+
if thinking_on_default
|
|
285
|
+
else None
|
|
286
|
+
),
|
|
287
|
+
docs=_OLLAMA_LOCAL_DOCS,
|
|
288
|
+
)
|
|
289
|
+
ctx, ctx_invalid = _strict_positive_int(num_ctx)
|
|
290
|
+
check(
|
|
291
|
+
report,
|
|
292
|
+
id="ollama.local.num_ctx_invalid",
|
|
293
|
+
category="provider",
|
|
294
|
+
severity="error",
|
|
295
|
+
passed=not ctx_invalid,
|
|
296
|
+
skip=not local or (ctx is None and not ctx_invalid),
|
|
297
|
+
message=(
|
|
298
|
+
f"num_ctx must be a positive integer, got {num_ctx!r}"
|
|
299
|
+
if ctx_invalid
|
|
300
|
+
else (f"num_ctx is {ctx}" if ctx is not None else "num_ctx is unset")
|
|
301
|
+
),
|
|
302
|
+
field="model.num_ctx",
|
|
303
|
+
value=num_ctx,
|
|
304
|
+
remediation=(
|
|
305
|
+
f"Set a positive integer num_ctx (e.g. 8192), or omit it. See {_OLLAMA_LOCAL_DOCS}."
|
|
306
|
+
if ctx_invalid
|
|
307
|
+
else None
|
|
308
|
+
),
|
|
309
|
+
docs=_OLLAMA_LOCAL_DOCS,
|
|
310
|
+
)
|
|
311
|
+
large = ctx is not None and ctx > _OLLAMA_NUM_CTX_LARGE
|
|
312
|
+
check(
|
|
313
|
+
report,
|
|
314
|
+
id="ollama.local.num_ctx_large",
|
|
315
|
+
category="provider",
|
|
316
|
+
severity="warning",
|
|
317
|
+
passed=not large,
|
|
318
|
+
skip=not local or ctx is None or ctx_invalid,
|
|
319
|
+
message=(
|
|
320
|
+
f"num_ctx {ctx} is large for a local runner; prefill will be slow"
|
|
321
|
+
if large
|
|
322
|
+
else (f"num_ctx is {ctx}" if ctx is not None else "num_ctx is unset")
|
|
323
|
+
),
|
|
324
|
+
field="model.num_ctx",
|
|
325
|
+
value=ctx,
|
|
326
|
+
remediation=(
|
|
327
|
+
f"Pin a modest num_ctx (e.g. 8192). Do not copy context_window. See {_OLLAMA_LOCAL_DOCS}."
|
|
328
|
+
if large
|
|
329
|
+
else None
|
|
330
|
+
),
|
|
331
|
+
docs=_OLLAMA_LOCAL_DOCS,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
|
|
195
335
|
def run_doctor(args: argparse.Namespace) -> int:
|
|
196
336
|
cwd = Path(args.cwd).expanduser().resolve() if args.cwd else None
|
|
197
337
|
config_path = resolve_config(args.config, cwd=cwd)
|
|
@@ -324,6 +464,19 @@ def run_doctor(args: argparse.Namespace) -> int:
|
|
|
324
464
|
skip=True,
|
|
325
465
|
)
|
|
326
466
|
|
|
467
|
+
thinking_raw = model.get("thinking_budget") if isinstance(model, dict) else None
|
|
468
|
+
if thinking_raw is None:
|
|
469
|
+
thinking_raw = "-1"
|
|
470
|
+
num_ctx_raw = model.get("num_ctx") if isinstance(model, dict) else None
|
|
471
|
+
model_name = str(model.get("name", "")).strip() if isinstance(model, dict) else ""
|
|
472
|
+
_add_ollama_local_checks(
|
|
473
|
+
report,
|
|
474
|
+
provider=provider,
|
|
475
|
+
model_name=model_name,
|
|
476
|
+
thinking_budget=thinking_raw,
|
|
477
|
+
num_ctx=num_ctx_raw,
|
|
478
|
+
)
|
|
479
|
+
|
|
327
480
|
runtime_cfg = doc.get("runtime") if isinstance(doc.get("runtime"), dict) else {}
|
|
328
481
|
port = int(runtime_cfg.get("port", 8080)) if isinstance(runtime_cfg, dict) else 8080
|
|
329
482
|
free = _port_free(port)
|
|
@@ -17,7 +17,7 @@ from monkeybot.core.config import (
|
|
|
17
17
|
validate_monkeybot_yaml_doc,
|
|
18
18
|
validate_provider_env,
|
|
19
19
|
)
|
|
20
|
-
from monkeybot.core.config.runtime_env import
|
|
20
|
+
from monkeybot.core.config.runtime_env import _flatten_config
|
|
21
21
|
from monkeybot.core.config.settings import ConfigError, normalize_model_provider
|
|
22
22
|
from monkeybot.core.config.yaml_loader import load_monkeybot_yaml_dict
|
|
23
23
|
from monkeybot.core.layout import resolve_agent_root
|
|
@@ -35,18 +35,7 @@ def _resolve_path(base: Path, rel: str) -> Path:
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def _flatten_to_env(doc: dict[str, Any]) -> dict[str, str]:
|
|
38
|
-
out
|
|
39
|
-
for (section, key), env_name in ENV_MAP.items():
|
|
40
|
-
sec = doc.get(section)
|
|
41
|
-
if not isinstance(sec, dict) or key not in sec:
|
|
42
|
-
continue
|
|
43
|
-
raw = sec[key]
|
|
44
|
-
if raw is None:
|
|
45
|
-
continue
|
|
46
|
-
if isinstance(raw, bool):
|
|
47
|
-
out[env_name] = "true" if raw else "false"
|
|
48
|
-
else:
|
|
49
|
-
out[env_name] = str(raw)
|
|
38
|
+
out = _flatten_config(doc)
|
|
50
39
|
gcp = doc.get("gcp") if isinstance(doc.get("gcp"), dict) else {}
|
|
51
40
|
if isinstance(gcp, dict) and gcp.get("project_id"):
|
|
52
41
|
out["GCP_PROJECT_ID"] = str(gcp["project_id"])
|
|
@@ -78,7 +78,6 @@ def _spawn_combined_gateway(
|
|
|
78
78
|
env["MONKEYBOT_CONFIG"] = str(config_path)
|
|
79
79
|
env["PORT"] = str(port)
|
|
80
80
|
env.setdefault("LOG_LEVEL", "error")
|
|
81
|
-
env.setdefault("MONKEYBOT_TRANSCRIPT_ENABLED", "1")
|
|
82
81
|
log_file = tempfile.NamedTemporaryFile(
|
|
83
82
|
mode="w+",
|
|
84
83
|
prefix="monkeybot-gateway-",
|
|
@@ -176,6 +176,13 @@ def install_loop_skill(dest: Path, *, force: bool) -> str:
|
|
|
176
176
|
return _install_file(target, resources.files(_DEFAULTS_PKG) / "loop" / "SKILL.md", force=force)
|
|
177
177
|
|
|
178
178
|
|
|
179
|
+
def install_goal_skill(dest: Path, *, force: bool) -> str:
|
|
180
|
+
"""Install the bundled native-goal procedure into ``skills/goal``."""
|
|
181
|
+
target = dest / "skills" / "goal" / "SKILL.md"
|
|
182
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
183
|
+
return _install_file(target, resources.files(_DEFAULTS_PKG) / "goal" / "SKILL.md", force=force)
|
|
184
|
+
|
|
185
|
+
|
|
179
186
|
def install_env_example(dest: Path, *, force: bool) -> str:
|
|
180
187
|
env_example = dest / ".env.example"
|
|
181
188
|
if env_example.exists() and not force:
|
|
@@ -399,6 +406,7 @@ def run_refresh(*, dest: Path) -> list[str]:
|
|
|
399
406
|
report.append(refresh_permissions_if_default(cfg_dir))
|
|
400
407
|
report.append(refresh_monkeybot_yaml(cfg_dir))
|
|
401
408
|
report.extend(ensure_memory(dest, force=False))
|
|
409
|
+
report.append(f" skills/goal/SKILL.md: {install_goal_skill(dest, force=False)}")
|
|
402
410
|
return report
|
|
403
411
|
|
|
404
412
|
|
|
@@ -412,11 +420,12 @@ def run_new(
|
|
|
412
420
|
) -> list[str]:
|
|
413
421
|
"""Full scaffold: config bundle, empty skills root, writable state, and image files.
|
|
414
422
|
|
|
415
|
-
|
|
423
|
+
Optional capability skills (``browser``, ``image-generator``, ``loop``) are packaged
|
|
416
424
|
under ``scaffold_defaults`` but not installed into new agents for now —
|
|
417
425
|
the Mac Main Agent loads them from ``~/.monkeybot/.internal/skills``
|
|
418
426
|
instead. Re-enable via ``install_*_skill`` when custom agents should get
|
|
419
|
-
them again.
|
|
427
|
+
them again. The ``goal`` skill is installed because native goal tools are
|
|
428
|
+
intentionally exposed only for explicit ``/goal`` invocations.
|
|
420
429
|
"""
|
|
421
430
|
cfg_dir = dest / "monkeybot_config"
|
|
422
431
|
report = install_config_bundle(cfg_dir, force=force)
|
|
@@ -426,7 +435,7 @@ def run_new(
|
|
|
426
435
|
)
|
|
427
436
|
report.extend(ensure_memory(dest, force=force))
|
|
428
437
|
report.extend(ensure_workspace(dest, force=force))
|
|
429
|
-
report.append(" skills
|
|
438
|
+
report.append(f" skills/goal/SKILL.md: {install_goal_skill(dest, force=force)}")
|
|
430
439
|
report.append(f" .env.example: {install_env_example(dest, force=force)}")
|
|
431
440
|
report.extend(install_container_files(dest, force=force))
|
|
432
441
|
report.append(
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: browser
|
|
3
|
+
description: Control a real browser through the optional bundled browser MCP server.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Browser
|
|
7
|
+
|
|
8
|
+
When browser tools are needed, call `enable_mcp("browser")` first. Use the
|
|
9
|
+
name from the harness MCP catalog — do not read config files. Progressive
|
|
10
|
+
disclosure still requires `enable_mcp` before `browser__*` schemas are advertised.
|
|
11
|
+
|
|
12
|
+
Use indexed DOM tools before screenshots. Prefer `browser_act` with
|
|
13
|
+
`do: fill_form` for multi-field forms, `browser_click_text` when the visible
|
|
14
|
+
label is known, `browser_act` for a batch of steps, and `browser_extract` for
|
|
15
|
+
structured scraping. Browser screenshots (JPEG under
|
|
16
|
+
`browser/Screenshots/`) and agent-written playbooks are workspace data:
|
|
17
|
+
`browser/Screenshots/` and `browser/playbooks/`.
|
|
18
|
+
They are not trusted skills and may be discarded with an ephemeral workspace.
|
|
19
|
+
|
|
20
|
+
If `browser_list_playbooks` returns `flows`, call `browser_run_playbook` instead of
|
|
21
|
+
re-planning. On `failed_step`, continue by hand and append a corrected
|
|
22
|
+
`playbook` YAML fence. Secrets only via `{do: login, expected_origin}`.
|
|
23
|
+
|
|
24
|
+
`browser_input_by_index` fills in-page by default; pass `mode="keys"` for
|
|
25
|
+
comboboxes and fields that only listen to keydown. `browser_get_elements`
|
|
26
|
+
is viewport-first; indices remain valid until navigation. Actions return
|
|
27
|
+
an `observation` (diff by default) — read that instead of calling
|
|
28
|
+
`get_elements` again. `browser_goto` returns a full observation.
|
|
29
|
+
Use `browser_get_text` to read page copy.
|
|
30
|
+
|
|
31
|
+
When the user asked to sign in on the Spaces in-app browser, call
|
|
32
|
+
`browser_login` instead of typing a password. It returns `{ok, loggedIn, origin}`
|
|
33
|
+
only. Pass `expected_origin` so a login cannot land on the wrong site, and check
|
|
34
|
+
the returned `origin` before reporting success.
|
|
35
|
+
|
|
36
|
+
Tabs use aliases (`t1`, `t2`, …). Reads (`get_elements`, `page_info`, `js`,
|
|
37
|
+
`read_tabs`) never move focus; actions do. At most five agent-controlled tabs —
|
|
38
|
+
if you hit the cap, ask the user which to close, then `browser_close_tab`. Close
|
|
39
|
+
tabs you opened. `browser_login` still targets the tab the user has focused.
|
|
40
|
+
|
|
41
|
+
Finish browser sessions with `browser_stop`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: goal
|
|
3
|
+
description: Create one durable objective that continues across scheduled turns until completed.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Goal
|
|
7
|
+
|
|
8
|
+
Use this skill only when the user explicitly invokes `/goal` with an objective
|
|
9
|
+
that may require multiple turns.
|
|
10
|
+
|
|
11
|
+
## Start
|
|
12
|
+
|
|
13
|
+
1. Preserve the user's complete objective without narrowing its scope.
|
|
14
|
+
2. Call `create_goal` exactly once with that objective.
|
|
15
|
+
3. Do the first concrete increment of work in the same turn.
|
|
16
|
+
4. Keep the returned goal id; every `update_goal` call must include it.
|
|
17
|
+
|
|
18
|
+
## Continue
|
|
19
|
+
|
|
20
|
+
- Goal continuations are scheduled automatically.
|
|
21
|
+
- Treat the stored objective as authoritative.
|
|
22
|
+
- Make measurable progress and verify results before declaring completion.
|
|
23
|
+
- Temporary session-busy deferrals are normal and are not goal failures.
|
|
24
|
+
- Persistent execution failures stop the goal after the retry limit so it
|
|
25
|
+
cannot consume resources indefinitely.
|
|
26
|
+
|
|
27
|
+
## Complete
|
|
28
|
+
|
|
29
|
+
Call `update_goal` with the returned `goal_id` and `status="complete"` only
|
|
30
|
+
after evidence proves every requirement is satisfied. Otherwise leave the goal
|
|
31
|
+
active. Use the `/goals` control plane to pause, resume, or stop it manually.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
#
|
|
3
3
|
# cp monkeybot_config/monkeybot.example.yaml monkeybot_config/monkeybot.yaml
|
|
4
4
|
#
|
|
5
|
-
# Precedence: environment variables (and optional root `.env`) win over values here
|
|
5
|
+
# Precedence: environment variables (and optional root `.env`) win over values here
|
|
6
|
+
# except ``model.*`` (provider, name, temperature, max_tokens, thinking_budget,
|
|
7
|
+
# context_window, summarization_model, max_turns, cache_retention) which are YAML-only.
|
|
6
8
|
#
|
|
7
9
|
# Secrets are not committed here. Create a `.env` in the repo root when you need keys or
|
|
8
10
|
# machine-local paths, for example:
|
|
@@ -29,7 +31,7 @@ runtime:
|
|
|
29
31
|
# Internal debugging only (not agent-visible): write every AgentEvent and raw
|
|
30
32
|
# provider request/response for each session to
|
|
31
33
|
# {workspace_root}/.monkeybot/transcripts/{UTC}_{session_id}/transcript.ndjson.
|
|
32
|
-
# (
|
|
34
|
+
# YAML-only (not overridable via environment).
|
|
33
35
|
transcript_enabled: true
|
|
34
36
|
|
|
35
37
|
# Required: choose the conversational harness for this deployment.
|
|
@@ -45,7 +47,7 @@ realtime:
|
|
|
45
47
|
# Optional model override for the realtime session. Live-only models (e.g.
|
|
46
48
|
# gemini-3.1-flash-live-preview) cannot be used for turn-based work, so the
|
|
47
49
|
# realtime session can point to a different model than model.name. When omitted,
|
|
48
|
-
# the realtime session falls back to model.name and
|
|
50
|
+
# the realtime session falls back to model.name and model.provider.
|
|
49
51
|
# model:
|
|
50
52
|
# name: gemini-3.1-flash-live-preview
|
|
51
53
|
# provider: google_genai
|
|
@@ -98,10 +100,16 @@ model:
|
|
|
98
100
|
# Summarisation trigger (tokens). Also drives soft-spill / read_file char budgets.
|
|
99
101
|
context_window: 1000000
|
|
100
102
|
max_turns: 1000
|
|
101
|
-
# Prompt-cache session hints: none | short | long (
|
|
103
|
+
# Prompt-cache session hints: none | short | long (YAML-only)
|
|
102
104
|
# cache_retention: short
|
|
103
|
-
# Optional cheaper model for sync history summarization
|
|
105
|
+
# Optional cheaper model for sync history summarization; omit = main model
|
|
104
106
|
# summarization_model: gemini-3-flash
|
|
107
|
+
# Ollama-local only (YAML). Default 24h so the KV prefix cache survives idle.
|
|
108
|
+
# Set "0" to omit. Distinct from the Ollama daemon env OLLAMA_KEEP_ALIVE.
|
|
109
|
+
# keep_alive: 24h
|
|
110
|
+
# Ollama-local only (YAML). Pin llama num_ctx; omit = server default.
|
|
111
|
+
# Do NOT copy context_window here — that budget is summarization/spill, often 1M.
|
|
112
|
+
# num_ctx: 8192
|
|
105
113
|
|
|
106
114
|
# Non-secret GCP identifiers (prefer .env for secrets / ADC path).
|
|
107
115
|
# gcp:
|
|
@@ -127,8 +135,6 @@ memory:
|
|
|
127
135
|
backend: chroma
|
|
128
136
|
embedding_model: embeddinggemma-300m
|
|
129
137
|
|
|
130
|
-
# context_curation is unused after MemPalace wake-up replaced INDEX.md + curator.
|
|
131
|
-
|
|
132
138
|
emission:
|
|
133
139
|
# Terse emission-style guidance injected into the cached harness prefix.
|
|
134
140
|
# Two named modes:
|
|
@@ -247,7 +253,18 @@ scheduler:
|
|
|
247
253
|
# Env override: MONKEYBOT_SCHEDULER_ENABLED (1 | true | yes | on).
|
|
248
254
|
enabled: false
|
|
249
255
|
|
|
250
|
-
#
|
|
256
|
+
# Background verifier (YAML-only). Off by default: ledger costs one model call
|
|
257
|
+
# per human message. `enabled: true` turns on omitted ledger/tracker flags;
|
|
258
|
+
# omitted judge.enabled turns on only when the tracker is on. Explicit
|
|
259
|
+
# `enabled: false` still opts a nested section out. Omitted ledger.model /
|
|
260
|
+
# judge.model inherit model.name (same fallback as the agent). The live
|
|
261
|
+
# worker is session-bound ProviderJudge. Realtime loops ignore this block.
|
|
262
|
+
# verifier:
|
|
263
|
+
# enabled: true
|
|
264
|
+
# escalation:
|
|
265
|
+
# max_severity: nudge
|
|
266
|
+
|
|
267
|
+
# Optional JSON for model.provider: fake tests (env: MONKEYBOT_FAKE_PROVIDER_EVENTS)
|
|
251
268
|
# fake_provider:
|
|
252
269
|
# events_json: '[[]]'
|
|
253
270
|
|
|
@@ -25,6 +25,11 @@ def _free_port() -> int:
|
|
|
25
25
|
def test_chat_repl_round_trip_with_fake_gateway(tmp_path: Path) -> None:
|
|
26
26
|
port = _free_port()
|
|
27
27
|
base = f"http://127.0.0.1:{port}"
|
|
28
|
+
(tmp_path / "monkeybot_config").mkdir()
|
|
29
|
+
(tmp_path / "monkeybot_config" / "monkeybot.yaml").write_text(
|
|
30
|
+
"model:\n provider: fake\n name: fake\n",
|
|
31
|
+
encoding="utf-8",
|
|
32
|
+
)
|
|
28
33
|
agent = tmp_path / "AGENT.md"
|
|
29
34
|
agent.write_text("# Test agent\nYou are a test assistant.\n", encoding="utf-8")
|
|
30
35
|
policy = tmp_path / "command_allowlist.yaml"
|
|
@@ -40,7 +45,6 @@ def test_chat_repl_round_trip_with_fake_gateway(tmp_path: Path) -> None:
|
|
|
40
45
|
"DB_URL": f"sqlite:///{tmp_path / 'mb.db'}",
|
|
41
46
|
"MCP_CONFIG": str(tmp_path / "no_mcp.json"),
|
|
42
47
|
"MEMORY_PATH": str(tmp_path / "memory"),
|
|
43
|
-
"MODEL_PROVIDER": "fake",
|
|
44
48
|
"PORT": str(port),
|
|
45
49
|
"SKILLS_PATH": str(tmp_path / "skills"),
|
|
46
50
|
"MONKEYBOT_CHAT_PLAIN": "1",
|
|
@@ -430,7 +430,7 @@ def test_close_deletes_session_and_captures_report_dir() -> None:
|
|
|
430
430
|
200,
|
|
431
431
|
json={
|
|
432
432
|
"deleted": True,
|
|
433
|
-
"
|
|
433
|
+
"transcript_dir": "/tmp/ws/.monkeybot/transcripts/20260714T150000Z_sess-close",
|
|
434
434
|
},
|
|
435
435
|
request=request,
|
|
436
436
|
)
|
|
@@ -441,7 +441,7 @@ def test_close_deletes_session_and_captures_report_dir() -> None:
|
|
|
441
441
|
await controller.close()
|
|
442
442
|
client.delete.assert_awaited_once_with("http://localhost:8080/sessions/sess-close")
|
|
443
443
|
assert (
|
|
444
|
-
controller.
|
|
444
|
+
controller.transcript_dir
|
|
445
445
|
== "/tmp/ws/.monkeybot/transcripts/20260714T150000Z_sess-close"
|
|
446
446
|
)
|
|
447
447
|
client.aclose.assert_awaited_once()
|