discoclaw 0.6.0 → 0.7.0

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.
Files changed (171) hide show
  1. package/.context/README.md +3 -1
  2. package/.context/dev.md +1 -1
  3. package/.context/memory.md +2 -2
  4. package/.context/project.md +5 -1
  5. package/.context/runtime.md +3 -2
  6. package/.env.example +22 -0
  7. package/.env.example.full +30 -4
  8. package/README.md +18 -5
  9. package/dashboard/server.js +21 -0
  10. package/dist/cli/dashboard.js +444 -0
  11. package/dist/cli/dashboard.test.js +963 -0
  12. package/dist/cli/index.js +243 -56
  13. package/dist/cli/index.test.js +122 -0
  14. package/dist/cold-storage/embeddings.js +4 -0
  15. package/dist/config.js +26 -2
  16. package/dist/config.test.js +96 -5
  17. package/dist/cron/executor.js +60 -3
  18. package/dist/cron/executor.test.js +100 -3
  19. package/dist/dashboard/api/snapshot.js +7 -0
  20. package/dist/dashboard/api/snapshot.test.js +152 -0
  21. package/dist/dashboard/options.js +53 -0
  22. package/dist/dashboard/options.test.js +44 -0
  23. package/dist/dashboard/page.js +1160 -0
  24. package/dist/dashboard/page.test.js +51 -0
  25. package/dist/dashboard/server-errors.js +20 -0
  26. package/dist/dashboard/server-errors.test.js +16 -0
  27. package/dist/dashboard/server.js +477 -0
  28. package/dist/dashboard/server.test.js +790 -0
  29. package/dist/dashboard/shipped-entrypoint.test.js +187 -0
  30. package/dist/discord/action-categories.js +4 -2
  31. package/dist/discord/actions-channels.js +73 -4
  32. package/dist/discord/actions-channels.test.js +1 -1
  33. package/dist/discord/actions-config.js +21 -8
  34. package/dist/discord/actions-config.test.js +57 -5
  35. package/dist/discord/actions-crons.js +11 -4
  36. package/dist/discord/actions-crons.test.js +28 -0
  37. package/dist/discord/actions-defer.js +4 -4
  38. package/dist/discord/actions-defer.test.js +4 -4
  39. package/dist/discord/actions-forge.js +24 -3
  40. package/dist/discord/actions-forge.test.js +104 -0
  41. package/dist/discord/actions-guild.js +104 -5
  42. package/dist/discord/actions-guild.test.js +71 -2
  43. package/dist/discord/actions-imagegen.js +1 -1
  44. package/dist/discord/actions-imagegen.test.js +39 -2
  45. package/dist/discord/actions-loop.js +434 -0
  46. package/dist/discord/actions-loop.test.js +302 -0
  47. package/dist/discord/actions-messaging.js +103 -2
  48. package/dist/discord/actions-messaging.test.js +51 -1
  49. package/dist/discord/actions-moderation.js +44 -1
  50. package/dist/discord/actions-plan.js +47 -9
  51. package/dist/discord/actions-plan.test.js +128 -5
  52. package/dist/discord/actions-poll.js +32 -1
  53. package/dist/discord/actions-spawn.js +35 -0
  54. package/dist/discord/actions-spawn.test.js +68 -2
  55. package/dist/discord/actions.js +65 -157
  56. package/dist/discord/actions.test.js +224 -3
  57. package/dist/discord/capsule.js +240 -0
  58. package/dist/discord/capsule.test.js +150 -0
  59. package/dist/discord/cron-prefetch.js +301 -0
  60. package/dist/discord/cron-prefetch.test.js +162 -0
  61. package/dist/discord/defer-scheduler.js +153 -1
  62. package/dist/discord/deferred-runner.js +47 -14
  63. package/dist/discord/deferred-runner.test.js +111 -2
  64. package/dist/discord/durable-consolidation.js +17 -13
  65. package/dist/discord/forge-audit-verdict.js +42 -6
  66. package/dist/discord/forge-commands.js +81 -10
  67. package/dist/discord/forge-commands.test.js +223 -1
  68. package/dist/discord/health-command.js +72 -1
  69. package/dist/discord/health-command.test.js +92 -1
  70. package/dist/discord/help-command.js +4 -0
  71. package/dist/discord/help-command.test.js +4 -0
  72. package/dist/discord/inflight-replies.js +37 -2
  73. package/dist/discord/inflight-replies.test.js +54 -1
  74. package/dist/discord/markdown-code-ranges.js +141 -0
  75. package/dist/discord/mcp-command.js +72 -0
  76. package/dist/discord/mcp-command.test.js +93 -0
  77. package/dist/discord/memory-timing.integration.test.js +95 -0
  78. package/dist/discord/message-coordinator.js +887 -551
  79. package/dist/discord/message-coordinator.mcp-command.test.js +146 -0
  80. package/dist/discord/message-coordinator.onboarding.test.js +38 -0
  81. package/dist/discord/message-coordinator.plan-run.test.js +149 -2
  82. package/dist/discord/message-coordinator.reaction-action-ordering.test.js +1 -0
  83. package/dist/discord/message-coordinator.trace-command.test.js +208 -0
  84. package/dist/discord/message-history.js +13 -5
  85. package/dist/discord/message-history.test.js +17 -1
  86. package/dist/discord/models-command.js +3 -2
  87. package/dist/discord/models-command.test.js +2 -1
  88. package/dist/discord/output-common.js +11 -1
  89. package/dist/discord/output-common.test.js +13 -0
  90. package/dist/discord/output-utils.js +4 -2
  91. package/dist/discord/output-utils.test.js +34 -0
  92. package/dist/discord/plan-commands.js +41 -8
  93. package/dist/discord/plan-commands.test.js +3 -0
  94. package/dist/discord/plan-manager.js +321 -41
  95. package/dist/discord/plan-manager.test.js +614 -58
  96. package/dist/discord/prompt-common.js +22 -3
  97. package/dist/discord/prompt-common.test.js +4 -1
  98. package/dist/discord/reaction-handler.js +56 -7
  99. package/dist/discord/reaction-handler.test.js +250 -10
  100. package/dist/discord/reaction-prompt-store.js +143 -0
  101. package/dist/discord/reaction-prompt-store.test.js +74 -0
  102. package/dist/discord/reaction-prompts.js +24 -23
  103. package/dist/discord/reaction-prompts.test.js +66 -3
  104. package/dist/discord/restart-command.js +1 -45
  105. package/dist/discord/shutdown-context.js +8 -0
  106. package/dist/discord/shutdown-context.test.js +102 -0
  107. package/dist/discord/status-channel.js +50 -0
  108. package/dist/discord/status-channel.test.js +116 -1
  109. package/dist/discord/summarizer-recency.test.js +88 -1
  110. package/dist/discord/summarizer.js +37 -6
  111. package/dist/discord/summarizer.test.js +1 -0
  112. package/dist/discord/trace-command.js +153 -0
  113. package/dist/discord/trace-command.test.js +131 -0
  114. package/dist/discord/user-errors.js +10 -50
  115. package/dist/discord/user-errors.test.js +38 -1
  116. package/dist/discord/verification-evidence.js +150 -0
  117. package/dist/discord/verification-evidence.test.js +311 -0
  118. package/dist/discord-followup.test.js +172 -13
  119. package/dist/discord.health-command.integration.test.js +92 -0
  120. package/dist/discord.js +1 -0
  121. package/dist/health/config-doctor.js +730 -0
  122. package/dist/health/config-doctor.test.js +319 -0
  123. package/dist/index.js +165 -39
  124. package/dist/index.post-connect.js +4 -0
  125. package/dist/mcp-detect.js +76 -9
  126. package/dist/mcp-detect.test.js +216 -19
  127. package/dist/model-config.js +1 -0
  128. package/dist/model-config.test.js +2 -1
  129. package/dist/observability/trace-store.js +119 -0
  130. package/dist/observability/trace-store.test.js +236 -0
  131. package/dist/pipeline/engine.js +3 -1
  132. package/dist/pipeline/engine.test.js +12 -1
  133. package/dist/runtime/anthropic-rest.js +7 -12
  134. package/dist/runtime/anthropic-rest.test.js +11 -4
  135. package/dist/runtime/cli-adapter.js +218 -65
  136. package/dist/runtime/cli-adapter.test.js +334 -5
  137. package/dist/runtime/cli-shared.js +10 -0
  138. package/dist/runtime/cli-shared.test.js +12 -1
  139. package/dist/runtime/codex-cli.test.js +222 -21
  140. package/dist/runtime/gemini-rest.js +6 -8
  141. package/dist/runtime/gemini-rest.test.js +8 -3
  142. package/dist/runtime/global-supervisor.js +81 -44
  143. package/dist/runtime/global-supervisor.test.js +48 -2
  144. package/dist/runtime/long-running-process.js +123 -16
  145. package/dist/runtime/long-running-process.test.js +90 -6
  146. package/dist/runtime/model-tiers.js +92 -10
  147. package/dist/runtime/model-tiers.test.js +48 -1
  148. package/dist/runtime/openai-compat.js +9 -8
  149. package/dist/runtime/openai-compat.test.js +2 -0
  150. package/dist/runtime/openai-tool-exec.js +61 -62
  151. package/dist/runtime/openai-tool-exec.test.js +26 -5
  152. package/dist/runtime/runtime-failure.js +674 -0
  153. package/dist/runtime/runtime-failure.test.js +209 -0
  154. package/dist/runtime/strategies/codex-strategy.js +5 -1
  155. package/dist/runtime/types.js +2 -0
  156. package/dist/runtime-overrides.js +25 -1
  157. package/dist/runtime-overrides.test.js +9 -0
  158. package/dist/service-control.js +100 -0
  159. package/dist/service-control.test.js +104 -0
  160. package/dist/tasks/task-action-executor.test.js +31 -0
  161. package/dist/tasks/task-action-thread-sync.js +2 -1
  162. package/dist/voice/stt-factory.js +3 -0
  163. package/dist/voice/stt-openai.js +2 -0
  164. package/dist/voice/tts-factory.js +3 -0
  165. package/dist/voice/tts-openai.js +1 -0
  166. package/dist/webhook/server.js +44 -72
  167. package/dist/webhook/server.test.js +5 -0
  168. package/docs/official-docs.md +123 -0
  169. package/package.json +4 -1
  170. package/templates/mcp.json +7 -2
  171. package/templates/workspace/MEMORY.md +1 -1
@@ -21,11 +21,12 @@ Core instructions live in `CLAUDE.md` at the repo root.
21
21
  | **Voice system (STT/TTS, audio pipeline, actions)** | `voice.md` |
22
22
  | **Forge/plan standing constraints** | `project.md` *(auto-loaded by forge)* |
23
23
  | **Plan & Forge commands** | `plan-and-forge.md` *(in docs/, not .context/)* |
24
+ | **Engineering lessons / compound learnings** | `docs/compound-lessons.md` *(single checked-in durable artifact; lives in `docs/`, not `.context/`; human/developer reference, not auto-loaded into agent context)* |
24
25
 
25
26
  ## Context Hygiene (Strict)
26
27
  - Read the minimum necessary modules for the task.
27
28
  - Do not load modules "just in case."
28
- - Some reference docs live in `docs/` rather than `.context/` — these are human/developer references and are **not** auto-loaded into agent context. The `.context/project.md` file remains the only `.context` module for plan/forge constraints.
29
+ - Some reference docs live in `docs/` rather than `.context/` — these are human/developer references and are **not** auto-loaded into agent context. The `.context/project.md` file remains the only `.context` module for plan/forge constraints, and `docs/compound-lessons.md` remains the single checked-in artifact for distilled engineering lessons.
29
30
 
30
31
  ## Quick Reference
31
32
  - **pa.md** — PA behavioral rules, Discord formatting, memory, group chat etiquette, autonomy tiers
@@ -42,3 +43,4 @@ Core instructions live in `CLAUDE.md` at the repo root.
42
43
  - **voice.md** — Voice subsystem: module map, audio data flow, key patterns (barge-in, allowlist gating), wiring sequence, dependencies, config reference
43
44
  - **project.md** — Standing constraints auto-loaded by forge drafter and auditor
44
45
  - **docs/plan-and-forge.md** — Canonical reference for `!plan` and `!forge` commands (lives in `docs/`, not `.context/` — human/developer reference, not auto-loaded into agent context)
46
+ - **docs/compound-lessons.md** — Single checked-in durable artifact for distilled engineering lessons from audits, forge runs, and repeated workflow failures
package/.context/dev.md CHANGED
@@ -165,7 +165,7 @@ Two setup paths:
165
165
  ### Health/Status
166
166
  | Variable | Default | Description |
167
167
  |----------|---------|-------------|
168
- | `DISCOCLAW_HEALTH_COMMANDS_ENABLED` | `1` | Enable `!health` command (uptime, memory, runtime status) |
168
+ | `DISCOCLAW_HEALTH_COMMANDS_ENABLED` | `1` | Enable `!health` and `!doctor` commands (uptime, memory, runtime status, config doctor) |
169
169
  | `DISCOCLAW_HEALTH_VERBOSE_ALLOWLIST` | *(empty — falls back to `DISCORD_ALLOW_USER_IDS`)* | Space/comma-separated user IDs that can request verbose health output |
170
170
 
171
171
  ### Plan & Forge
@@ -161,8 +161,8 @@ Bot: Sure — is this related to the auth middleware test you were debugging
161
161
  `workspace/MEMORY.md` + `workspace/memory/YYYY-MM-DD.md`
162
162
 
163
163
  Curated long-term notes and daily scratch logs. Loaded in DMs only. These hold
164
- things too nuanced for structured durable items — decisions, lessons, project
165
- context, relationship dynamics.
164
+ things too nuanced for structured durable items — decisions, project context,
165
+ relationship dynamics. Engineering lessons belong in `docs/compound-lessons.md`.
166
166
 
167
167
  **What the user sees:**
168
168
  - In DMs, the bot has deep context about ongoing projects and past decisions.
@@ -20,6 +20,10 @@ Standing constraints for planning and auditing. These apply to all forge/plan op
20
20
  - Keep changes minimal — don't over-engineer for hypothetical multi-user scenarios.
21
21
  - Prefer wiring existing systems together over building new abstractions.
22
22
  - Tests are required for new functionality.
23
+ - `docs/compound-lessons.md` is the single checked-in durable artifact for reusable engineering lessons. Raw findings can stay in plans, audits, postmortems, or task context, but distilled standing guidance belongs there.
24
+ - Forge drafter/auditor/reviser agents should consult `docs/compound-lessons.md` for known patterns and pitfalls before drafting, search for an existing matching lesson first, and update that entry instead of proposing a near-duplicate.
25
+ - Before drafting, consult the `## Lessons` section of `docs/compound-lessons.md` to avoid rediscovering known patterns. When a plan reveals a materially distinct reusable lesson from audits, postmortems, or task/workflow context, include a `docs/compound-lessons.md` update in `## Changes`.
26
+ - If a change closes a recurring workflow, review, or quality gap, the implementing engineer must either update `docs/compound-lessons.md` in the same change or explicitly record that no durable lesson was needed. Auditors and reviewers must verify that decision and the dedup check before approval.
23
27
 
24
28
  ## Plan Scope & Size Constraints
25
29
 
@@ -40,4 +44,4 @@ Plans that grow too large fail — they blow past token limits, cause audit/revi
40
44
  ### Auditor guidance
41
45
  - Do NOT flag "underspecified implementation details" as medium/high. The plan describes intent and scope — the implementing agent fills in the details.
42
46
  - DO flag: missing scope items, incorrect assumptions about existing code, safety/correctness issues, missing error handling for external boundaries.
43
- - If a plan is too large or touches too many source modules, flag that as **high severity** with recommendation to split. Co-located test files, lockfile changes from dependency additions, and doc/inventory updates do not count toward the scope cap.
47
+ - If a plan is too large or touches too many source modules, flag that as **high severity** with recommendation to split. Co-located test files, lockfile changes from dependency additions, and doc/inventory updates do not count toward the scope cap.
@@ -3,6 +3,7 @@
3
3
  ## Model Names & IDs
4
4
 
5
5
  **Always research official documentation** when referencing model names, IDs, or parameters — never guess or rely on training data alone. Model names change frequently across providers.
6
+ For the complete maintainer docs index covering providers, SDKs, Discord, MCP, and dependency references, see `docs/official-docs.md`.
6
7
 
7
8
  Sources to check:
8
9
  - **Anthropic:** https://platform.claude.com/docs/en/about-claude/models/overview
@@ -45,7 +46,7 @@ The factory provides: subprocess tracking, process pool, stall detection, sessio
45
46
  | Strategy | File | Multi-turn | Notes |
46
47
  |----------|------|------------|-------|
47
48
  | Claude Code | `strategies/claude-strategy.ts` | process-pool | Default JSONL parsing, image support |
48
- | Codex CLI | `strategies/codex-strategy.ts` | session-resume | Custom JSONL (thread.started, item.completed), error sanitization; reasoning items surface in the Discord preview during streaming but are excluded from the final reply; image support via `--image` temp files |
49
+ | Codex CLI | `strategies/codex-strategy.ts` | session-resume | Custom JSONL (thread.started, item.completed), error sanitization; reasoning items surface in the Discord preview during streaming but are excluded from the final reply; image support via `--image` temp files; if a resumed turn includes images, the adapter resets to a fresh session because `codex exec resume` cannot accept `--image`, notifies the user, and loses prior conversation context |
49
50
  | Gemini CLI | `strategies/gemini-strategy.ts` | none (Phase 1) | Text-only output mode; no sessions; stdin fallback for large prompts |
50
51
  | Template | `strategies/template-strategy.ts` | — | Commented starting point for new models |
51
52
 
@@ -312,7 +313,7 @@ When a Discord message or reaction target has image attachments (PNG, JPEG, WebP
312
313
  3. **Download** — `downloadAttachment()` fetches the image with a 10 s timeout, post-checks actual size, and returns base64.
313
314
  4. **Delivery** — The runtime adapter writes a `stream-json` stdin message containing `[{ type: 'text', text: prompt }, { type: 'image', source: { type: 'base64', ... } }, ...]`. When images are present, `--output-format` is forced to `stream-json` regardless of the configured format.
314
315
 
315
- **Codex delivery:** Codex CLI does not accept base64 image data via stdin — it requires file paths on disk via `--image <path>` flags. The Codex strategy writes each `ImageData` (base64) to a temp file before invocation and cleans up after. The full pipeline is: Discord attachment → base64 `ImageData` → temp file → `--image <path>` → cleanup.
316
+ **Codex delivery:** Codex CLI does not accept base64 image data via stdin — it requires file paths on disk via `--image <path>` flags. The Codex strategy writes each `ImageData` (base64) to a temp file before invocation and cleans up after. If images arrive on a resumed Codex turn, the adapter automatically falls back to a fresh session because `codex exec resume` does not support `--image`; the user sees a notification, and prior conversation context is lost. The full pipeline is: Discord attachment → base64 `ImageData` → temp file → `--image <path>` → cleanup.
316
317
 
317
318
  ### Security controls
318
319
 
package/.env.example CHANGED
@@ -45,6 +45,24 @@ DISCORD_ALLOW_USER_IDS=
45
45
  # Collected as a required field by `discoclaw init`.
46
46
  DISCORD_GUILD_ID=
47
47
 
48
+ # Optional custom service name for multi-instance installs.
49
+ # Written automatically by `discoclaw install-daemon --service-name <name>`.
50
+ # `discoclaw dashboard`, `!restart`, and other service actions use this to target the correct unit.
51
+ # Leave unset to use the default service name: discoclaw
52
+ #DISCOCLAW_SERVICE_NAME=discoclaw
53
+ # Disable the local operator dashboard HTTP server inside the main discoclaw process.
54
+ # It is enabled by default and binds to 127.0.0.1. To allow access from a Tailscale
55
+ # phone/browser without SSH tunneling, set DISCOCLAW_DASHBOARD_TRUSTED_HOSTS to your
56
+ # tailnet IP or MagicDNS name.
57
+ #DISCOCLAW_DASHBOARD_ENABLED=0
58
+ # Dashboard port (default: 9401). Multi-instance setups on the same machine must use distinct ports.
59
+ #DISCOCLAW_DASHBOARD_PORT=9401
60
+ # Comma-separated Host header allowlist for non-loopback dashboard access.
61
+ # When set, the dashboard binds to 0.0.0.0 but still rejects all Host headers except
62
+ # loopback names plus the entries listed here.
63
+ # Example: phone.tailnet.ts.net,100.64.0.12
64
+ #DISCOCLAW_DASHBOARD_TRUSTED_HOSTS=
65
+
48
66
  # Where the Claude CLI runs (its working directory).
49
67
  # Default: ./workspace (or $DISCOCLAW_DATA_DIR/workspace if DATA_DIR is set)
50
68
  #WORKSPACE_CWD=
@@ -80,6 +98,10 @@ DISCORD_GUILD_ID=
80
98
  # Model tier: fast | capable | deep (provider-agnostic).
81
99
  # Concrete model names (e.g. opus, sonnet, gpt-4o) are still accepted as passthrough.
82
100
  #RUNTIME_MODEL=capable
101
+ # [DEPRECATED] Plan execution has its own startup default now. When models.json is missing
102
+ # or reset, DISCOCLAW_PLAN_RUN_MODEL seeds the dedicated `plan-run` role independently.
103
+ # New deployments should use `!models set plan-run <model>` instead.
104
+ #DISCOCLAW_PLAN_RUN_MODEL=capable
83
105
 
84
106
  # Output format for the Claude CLI. stream-json gives smoother streaming.
85
107
  #CLAUDE_OUTPUT_FORMAT=stream-json
package/.env.example.full CHANGED
@@ -55,6 +55,10 @@ DISCORD_ALLOW_USER_IDS=
55
55
  # Model tier: fast | capable | deep (provider-agnostic).
56
56
  # Concrete model names (e.g. opus, sonnet, gpt-4o) are still accepted as passthrough.
57
57
  #RUNTIME_MODEL=capable
58
+ # [DEPRECATED] Plan execution has its own startup default now. When models.json is missing
59
+ # or reset, DISCOCLAW_PLAN_RUN_MODEL seeds the dedicated `plan-run` role independently.
60
+ # New deployments should use `!models set plan-run <model>` instead.
61
+ #DISCOCLAW_PLAN_RUN_MODEL=capable
58
62
 
59
63
  # --- Fast-tier default ---
60
64
  # [DEPRECATED] Model configuration has moved to models.json (managed via !models commands).
@@ -223,6 +227,17 @@ DISCOCLAW_DISCORD_ACTIONS_DEFER=1
223
227
  # When depth reaches this limit, the defer action is disabled for that run (default: 4).
224
228
  #DISCOCLAW_DISCORD_ACTIONS_DEFER_MAX_DEPTH=4
225
229
 
230
+ # Allow the AI to schedule repeating self-invocations with loopCreate/loopList/loopCancel.
231
+ # Loops are inspectable recurring jobs and should be preferred over chaining repeated defers.
232
+ # Default: on. Set to 0 to disable.
233
+ #DISCOCLAW_DISCORD_ACTIONS_LOOP=1
234
+ # Minimum interval (seconds) accepted for loopCreate (default: 60).
235
+ #DISCOCLAW_DISCORD_ACTIONS_LOOP_MIN_INTERVAL_SECONDS=60
236
+ # Maximum interval (seconds) accepted for loopCreate (default: 86400 / 24 h).
237
+ #DISCOCLAW_DISCORD_ACTIONS_LOOP_MAX_INTERVAL_SECONDS=86400
238
+ # Maximum number of active loops allowed at once (default: 5).
239
+ #DISCOCLAW_DISCORD_ACTIONS_LOOP_MAX_CONCURRENT=5
240
+
226
241
  # Allow the AI to spawn parallel sub-agents in target channels.
227
242
  # Each spawned agent is a fire-and-forget invocation that posts output to the specified channel.
228
243
  # Spawned agents run at recursion depth 1 and cannot themselves spawn further agents.
@@ -407,6 +422,17 @@ DISCOCLAW_DISCORD_ACTIONS_DEFER=1
407
422
  # Sets the systemd unit / launchd label name used by !restart and !update apply.
408
423
  # Default: discoclaw. Ignored when DC_RESTART_CMD is set (that overrides the entire command).
409
424
  #DISCOCLAW_SERVICE_NAME=discoclaw
425
+ # Disable the local operator dashboard HTTP server inside the main discoclaw process.
426
+ # It is enabled by default and bound to 127.0.0.1; see DISCOCLAW_DASHBOARD_TRUSTED_HOSTS
427
+ # for Tailscale/LAN access. When DISCOCLAW_DASHBOARD_TRUSTED_HOSTS is set, the server
428
+ # binds to 0.0.0.0 and accepts only loopback Host headers plus the trusted list.
429
+ #DISCOCLAW_DASHBOARD_ENABLED=0
430
+ # Operator dashboard port (default: 9401).
431
+ #DISCOCLAW_DASHBOARD_PORT=9401
432
+ # Comma-separated Host header allowlist for dashboard access over Tailscale or another
433
+ # trusted network path. Supports hostnames and IPv4 addresses only; IPv6 literals are
434
+ # rejected because Host parsing is ambiguous here. Example:
435
+ #DISCOCLAW_DASHBOARD_TRUSTED_HOSTS=phone.tailnet.ts.net,100.64.0.12
410
436
  # Global cap on parallel runtime invocations across all Discord sessions (0 = unlimited).
411
437
  #DISCOCLAW_MAX_CONCURRENT_INVOCATIONS=3
412
438
  # Max depth for chained action follow-ups (e.g. defer → action → response). 0 = disabled.
@@ -443,10 +469,10 @@ DISCOCLAW_DISCORD_ACTIONS_DEFER=1
443
469
  # Render a denser "Thinking..." streaming preview tail (more lines + wider logs + richer tool signals).
444
470
  # Helps previews update more visibly during long runs; action tags are still stripped from preview text.
445
471
  #DISCOCLAW_STREAM_PREVIEW_RAW=0
446
- # Stream stall detection: kill one-shot process if no stdout/stderr for this long (ms). 0 = disabled. (default: 600000)
447
- #DISCOCLAW_STREAM_STALL_TIMEOUT_MS=120000
448
- # Progress stall timeout: alert after this many ms with no progress event (ms). 0 = disabled.
449
- #DISCOCLAW_PROGRESS_STALL_TIMEOUT_MS=300000
472
+ # Stream stall detection: kill one-shot process if no stdout/stderr for this long (ms). 0 = disabled. (default: 1800000)
473
+ #DISCOCLAW_STREAM_STALL_TIMEOUT_MS=1800000
474
+ # Progress stall timeout: alert after this many ms with no progress event (ms). 0 = disabled. (default: 1800000)
475
+ #DISCOCLAW_PROGRESS_STALL_TIMEOUT_MS=1800000
450
476
  # Stream stall warning: show user-visible warning in Discord after this many ms of no events. 0 = disabled. (default: 300000)
451
477
  #DISCOCLAW_STREAM_STALL_WARNING_MS=60000
452
478
  # Enable long-run watchdog follow-up status updates.
package/README.md CHANGED
@@ -111,11 +111,13 @@ When multiple messages arrive while the bot is thinking (i.e., an AI invocation
111
111
 
112
112
  Set `PRIMARY_RUNTIME=openrouter` to route requests through [OpenRouter](https://openrouter.ai), which provides access to models from Anthropic, OpenAI, Google, and others via a single API key — useful if you want to switch models without managing multiple provider accounts.
113
113
 
114
- Required: `OPENROUTER_API_KEY`. Optional overrides: `OPENROUTER_BASE_URL` (default: `https://openrouter.ai/api/v1`) and `OPENROUTER_MODEL` (default: `anthropic/claude-sonnet-4`). See `.env.example` for the full reference.
114
+ Required: `OPENROUTER_API_KEY`. Optional overrides: `OPENROUTER_BASE_URL` (default: `https://openrouter.ai/api/v1`) and `OPENROUTER_MODEL` (default: `anthropic/claude-sonnet-4`). OpenRouter does not have a built-in `fast`/`capable`/`deep` tier map inside DiscoClaw, so if you want tier names or fast/voice auto-switching to resolve through OpenRouter, define the specific `DISCOCLAW_TIER_OPENROUTER_<TIER>` vars you need in `.env` and restart. A single unique entry such as `DISCOCLAW_TIER_OPENROUTER_FAST=openai/gpt-5-mini` is enough for that exact-string fast/voice reverse-mapping. See `.env.example` for the full reference.
115
115
 
116
116
  ## Model Overrides
117
117
 
118
- The `!models` command lets you view and swap AI models per role at runtime — no restart needed. Changes are persisted to `models.json` under the data dir and survive restarts.
118
+ The `!models` command lets you view and swap AI models per role at runtime — no restart needed. Per-role model values persist to `models.json` under the data dir, while fast/voice runtime overlays persist separately to `runtime-overrides.json`. Live runtime swaps like `!models set chat openrouter` are still live-only until the next restart, but they affect the main runtime path broadly, not just chat.
119
+
120
+ For the full operator guide to install-mode detection, persistent adapter switches, OpenRouter tier overrides, fast/voice runtime behavior, and `!models reset` semantics, see [docs/runtime-switching.md](docs/runtime-switching.md).
119
121
 
120
122
  **Roles:** `chat`, `fast`, `forge-drafter`, `forge-auditor`, `summary`, `cron`, `cron-exec`, `voice`
121
123
 
@@ -128,13 +130,13 @@ The `!models` command lets you view and swap AI models per role at runtime — n
128
130
 
129
131
  **Examples:**
130
132
  - `!models set chat claude-sonnet-4` — use Sonnet for chat
131
- - `!models set chat openrouter` — switch chat to the OpenRouter runtime
133
+ - `!models set chat openrouter` — live-switch the main runtime to OpenRouter until restart
132
134
  - `!models set cron-exec haiku` — run crons on a cheaper model
133
135
  - `!models set cron-exec default` — clear the cron-exec override and use the startup default again
134
136
  - `!models set voice sonnet` — use a specific model for voice
135
137
  - `!models reset` — clear all overrides and revert to startup defaults
136
138
 
137
- Setting the `chat` or `voice` role to a runtime name (`openrouter`, `openai`, `gemini`, `codex`, `claude`) switches the active runtime adapter for that role.
139
+ Setting `chat` to a runtime name (`openrouter`, `openai`, `gemini`, `codex`, `claude`) live-switches the main runtime path until restart; setting `voice` to a runtime name switches only voice. Exact model-string runtime auto-switching is only implemented for `fast` and `voice`.
138
140
 
139
141
  ## Secret Management
140
142
 
@@ -240,6 +242,7 @@ Full step-by-step guide: [docs/discord-bot-setup.md](docs/discord-bot-setup.md)
240
242
  ### Operations
241
243
 
242
244
  - [Configuration reference](docs/configuration.md) — all environment variables indexed by category
245
+ - [Runtime/model switching](docs/runtime-switching.md) — operator guide for switching adapters, models, and defaults safely
243
246
  - [Webhook exposure](docs/webhook-exposure.md) — tunnel/proxy setup and webhook security
244
247
  - [Data migration](docs/data-migration.md) — migrating task data between formats
245
248
 
@@ -271,6 +274,14 @@ Full step-by-step guide: [docs/discord-bot-setup.md](docs/discord-bot-setup.md)
271
274
  discoclaw install-daemon --service-name personal
272
275
  ```
273
276
 
277
+ 4. **Open the local operator dashboard:**
278
+ ```bash
279
+ discoclaw dashboard
280
+ ```
281
+ By default this listens on `127.0.0.1`. To reach it from a phone over Tailscale,
282
+ set `DISCOCLAW_DASHBOARD_TRUSTED_HOSTS` to your tailnet IP or MagicDNS hostname.
283
+ See [docs/dashboard-tailscale.md](docs/dashboard-tailscale.md).
284
+
274
285
  #### From source (contributors)
275
286
 
276
287
  ```bash
@@ -311,7 +322,9 @@ pnpm install
311
322
  pnpm build
312
323
  ```
313
324
 
314
- Run `pnpm preflight` — it flags configuration options from `.env.example` that aren't in your `.env` yet.
325
+ Run `pnpm preflight` — it flags configuration options from `.env.example` that aren't in your `.env` yet. You can also run `discoclaw doctor` to inspect config drift and related issues, `discoclaw doctor --fix` to apply safe remediations, or use `!doctor` / `!doctor fix` from Discord (`!health doctor` / `!health doctor fix` remain supported). Restart the service afterward for fixed config to take effect.
326
+
327
+ For a local operator console, run `discoclaw dashboard` in the project directory. It shows the active service target, current model assignments, runtime overrides, config doctor status, and quick actions for status/logs/restart. It binds to `127.0.0.1` by default; configure `DISCOCLAW_DASHBOARD_TRUSTED_HOSTS` to allow Tailscale access via a tailnet IP or MagicDNS hostname while keeping Host-header checks in place for all other names. See [docs/dashboard-tailscale.md](docs/dashboard-tailscale.md).
315
328
 
316
329
  If running as a systemd service, restart it:
317
330
 
@@ -0,0 +1,21 @@
1
+ const DIST_SERVER_URL = new URL('../dist/dashboard/server.js', import.meta.url);
2
+
3
+ let dashboardServerModulePromise;
4
+
5
+ async function loadDashboardServerModule() {
6
+ if (!dashboardServerModulePromise) {
7
+ dashboardServerModulePromise = import(DIST_SERVER_URL.href).catch((error) => {
8
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ERR_MODULE_NOT_FOUND') {
9
+ throw new Error('dashboard/server.js requires a built dist tree. Run `pnpm build` first.');
10
+ }
11
+ throw error;
12
+ });
13
+ }
14
+
15
+ return dashboardServerModulePromise;
16
+ }
17
+
18
+ export async function startDashboardServer(options = {}) {
19
+ const serverModule = await loadDashboardServerModule();
20
+ return serverModule.startDashboardServer(options);
21
+ }