discoclaw 0.1.0 → 0.1.1
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.
- package/.context/README.md +4 -4
- package/.context/architecture.md +0 -1
- package/.context/dev.md +127 -12
- package/.context/discord.md +20 -10
- package/.context/runtime.md +51 -1
- package/.context/tasks.md +3 -2
- package/.env.example.full +39 -10
- package/dist/cron/cron-sync.js +35 -11
- package/dist/cron/executor.js +7 -5
- package/dist/cron/forum-sync.js +5 -4
- package/dist/cron/run-stats.js +5 -3
- package/dist/discord/action-utils.js +2 -0
- package/dist/discord/actions-bot-profile.js +7 -1
- package/dist/discord/actions-config.js +1 -1
- package/dist/discord/actions-crons.js +19 -5
- package/dist/discord/actions-guild.js +16 -2
- package/dist/discord/actions-messaging.js +87 -42
- package/dist/discord/actions-messaging.test.js +26 -0
- package/dist/discord/actions-plan.js +30 -11
- package/dist/discord/actions-poll.js +2 -1
- package/dist/discord/actions.js +1 -1
- package/dist/discord/deferred-runner.js +195 -0
- package/dist/discord/durable-memory.js +14 -8
- package/dist/discord/forum-count-sync.js +9 -2
- package/dist/discord/inflight-replies.js +20 -4
- package/dist/discord/message-coordinator.groupdir.test.js +33 -0
- package/dist/discord/message-coordinator.js +78 -35
- package/dist/discord/plan-manager.js +4 -1
- package/dist/discord/platform-message.js +3 -5
- package/dist/discord/reaction-handler.js +21 -8
- package/dist/discord/reaction-prompts.js +5 -1
- package/dist/discord/restart-command.js +7 -2
- package/dist/discord/shortterm-memory.js +27 -16
- package/dist/discord/shutdown-context.js +15 -6
- package/dist/discord/status-command.js +26 -17
- package/dist/discord/streaming-progress.js +7 -1
- package/dist/discord/summarizer.js +11 -8
- package/dist/discord/system-bootstrap.js +43 -33
- package/dist/discord/update-command.js +6 -2
- package/dist/discord/user-turn-to-durable.js +13 -7
- package/dist/discord.js +7 -1
- package/dist/health/startup-healing.js +15 -6
- package/dist/index.js +154 -368
- package/dist/index.paths.js +25 -0
- package/dist/index.paths.test.js +36 -0
- package/dist/index.post-connect.js +114 -0
- package/dist/index.runtime.js +50 -0
- package/dist/runtime/cli-adapter.js +21 -10
- package/dist/runtime/cli-adapter.test.js +155 -0
- package/dist/runtime/cli-output-parsers.js +17 -7
- package/dist/runtime/session-scanner.js +22 -12
- package/dist/runtime/strategies/codex-strategy.js +3 -2
- package/dist/runtime/strategies/gemini-strategy.js +3 -2
- package/dist/runtime/strategies/template-strategy.js +2 -2
- package/dist/tasks/forum-guard.js +12 -4
- package/dist/tasks/thread-lifecycle-ops.js +37 -8
- package/dist/workspace-bootstrap.test.js +3 -3
- package/package.json +7 -2
- package/templates/workspace/AGENTS.md +4 -5
- package/templates/workspace/TOOLS.md +2 -2
- package/dist/beads/auto-tag.js +0 -2
- package/dist/beads/auto-tag.test.js +0 -62
- package/dist/beads/bd-cli.js +0 -9
- package/dist/beads/bd-cli.test.js +0 -495
- package/dist/beads/bead-hooks-cli.js +0 -149
- package/dist/beads/bead-sync-cli.js +0 -5
- package/dist/beads/bead-sync-cli.test.js +0 -72
- package/dist/beads/bead-sync-coordinator.js +0 -4
- package/dist/beads/bead-sync-coordinator.test.js +0 -239
- package/dist/beads/bead-sync-watcher.js +0 -2
- package/dist/beads/bead-sync-watcher.test.js +0 -96
- package/dist/beads/bead-sync.js +0 -7
- package/dist/beads/bead-sync.test.js +0 -876
- package/dist/beads/bead-thread-cache.js +0 -8
- package/dist/beads/bead-thread-cache.test.js +0 -91
- package/dist/beads/discord-sync.js +0 -18
- package/dist/beads/discord-sync.test.js +0 -782
- package/dist/beads/find-bead-by-thread.test.js +0 -36
- package/dist/beads/forum-guard.js +0 -2
- package/dist/beads/forum-guard.test.js +0 -204
- package/dist/beads/initialize.js +0 -3
- package/dist/beads/initialize.test.js +0 -304
- package/dist/beads/types.js +0 -10
- package/dist/discoclaw-plan-format.test.js +0 -137
- package/dist/discord/action-types.js +0 -1
- package/dist/discord/actions-beads.js +0 -1
- package/dist/discord/actions-beads.test.js +0 -372
- package/dist/discord/actions-tasks.js +0 -3
- package/dist/discord/actions-tasks.test.js +0 -418
- package/dist/discord/bot.js +0 -141
- package/dist/discord/keyed-queue.js +0 -22
- package/dist/discord/plan-run-phase-start.js +0 -20
- package/dist/discord/plan-run-phase-start.test.js +0 -29
- package/dist/engine/claudeCli.js +0 -137
- package/dist/engine/types.js +0 -1
- package/dist/sessionManager.js +0 -47
- package/dist/tasks/bd-cli.js +0 -164
- package/dist/tasks/bd-cli.test.js +0 -359
- package/dist/tasks/bead-sync.js +0 -1
- package/dist/tasks/discord-sync.js +0 -3
- package/dist/tasks/discord-sync.test.js +0 -685
- package/dist/tasks/migrate.js +0 -33
- package/dist/tasks/migrate.test.js +0 -156
- package/dist/tasks/sync-watcher.js +0 -27
- package/dist/tasks/sync-watcher.test.js +0 -92
- package/dist/tasks/task-sync-apply.js +0 -319
package/.context/README.md
CHANGED
|
@@ -12,10 +12,10 @@ Core instructions live in `CLAUDE.md` at the repo root.
|
|
|
12
12
|
| **Discord behavior + routing** | `discord.md` |
|
|
13
13
|
| **Discord bot setup (invite + env)** | `bot-setup.md` |
|
|
14
14
|
| **Development / build / test** | `dev.md` |
|
|
15
|
-
| **Runtime adapters (Claude CLI,
|
|
15
|
+
| **Runtime adapters (Claude CLI, Codex CLI, Gemini CLI, OpenRouter)** | `runtime.md` |
|
|
16
16
|
| **Ops / systemd service** | `ops.md` |
|
|
17
17
|
| **Memory system** | `memory.md` |
|
|
18
|
-
| **Task tracking
|
|
18
|
+
| **Task tracking** | `tasks.md` |
|
|
19
19
|
| **Architecture / system overview** | `architecture.md` |
|
|
20
20
|
| **Tool capabilities / browser automation** | `tools.md` |
|
|
21
21
|
| **Forge/plan standing constraints** | `project.md` *(auto-loaded by forge)* |
|
|
@@ -31,10 +31,10 @@ Core instructions live in `CLAUDE.md` at the repo root.
|
|
|
31
31
|
- **pa-safety.md** — Indirect prompt injection defense, golden rules, red flags
|
|
32
32
|
- **dev.md** — Commands, env, local dev loops, build/test
|
|
33
33
|
- **discord.md** — Allowlist gating, session keys, threading rules, output constraints
|
|
34
|
-
- **runtime.md** — Runtime adapter interface,
|
|
34
|
+
- **runtime.md** — Runtime adapter interface, multi-runtime CLI flags, capability routing
|
|
35
35
|
- **ops.md** — systemd service notes, logs, restart workflow
|
|
36
36
|
- **memory.md** — Memory layers, user-facing examples, config reference, concurrency
|
|
37
|
-
- **tasks.md** — Task tracker: data model,
|
|
37
|
+
- **tasks.md** — Task tracker: data model, store events, Discord sync, auto-tagging
|
|
38
38
|
- **architecture.md** — System overview, data flow, directory layout, key concepts
|
|
39
39
|
- **bot-setup.md** — One-time bot creation and invite guide
|
|
40
40
|
- **tools.md** — Available tools: browser automation (agent-browser), escalation ladder, CDP connect, security guardrails
|
package/.context/architecture.md
CHANGED
|
@@ -25,7 +25,6 @@ Discord message
|
|
|
25
25
|
| `src/discord/` | Discord subsystems: actions, allowlist, channel context, memory, output |
|
|
26
26
|
| `src/runtime/` | Runtime adapters (Claude CLI), concurrency, process pool |
|
|
27
27
|
| `src/tasks/` | In-process task data model + store + migration helpers |
|
|
28
|
-
| `src/beads/` | Retired legacy shim namespace (runtime shims removed in hard-cut) |
|
|
29
28
|
| `src/cron/` | Cron scheduler, executor, forum sync, run stats |
|
|
30
29
|
| `src/observability/` | Metrics registry |
|
|
31
30
|
| `src/sessions.ts` | Session manager (maps session keys to runtime session IDs) |
|
package/.context/dev.md
CHANGED
|
@@ -32,16 +32,25 @@ Two setup paths:
|
|
|
32
32
|
| `DISCORD_TOKEN` | **(required)** | Bot token |
|
|
33
33
|
| `DISCORD_ALLOW_USER_IDS` | **(required)** | Comma/space-separated Discord user IDs; fail-closed if empty |
|
|
34
34
|
| `DISCORD_CHANNEL_IDS` | *(empty — all channels)* | Restrict the bot to specific guild channel IDs (DMs still allowed) |
|
|
35
|
+
| `DISCORD_GUILD_ID` | *(empty)* | Optional guild snowflake; required for some guild-scoped features |
|
|
35
36
|
| `DISCORD_REQUIRE_CHANNEL_CONTEXT` | `1` | Require a per-channel context file before responding |
|
|
36
37
|
| `DISCORD_AUTO_INDEX_CHANNEL_CONTEXT` | `1` | Auto-create stub context files for new channels |
|
|
37
|
-
| `DISCORD_AUTO_JOIN_THREADS` | `
|
|
38
|
-
| `DISCOCLAW_DISCORD_ACTIONS` | `
|
|
38
|
+
| `DISCORD_AUTO_JOIN_THREADS` | `1` | Best-effort auto-join threads so the bot can respond inside them |
|
|
39
|
+
| `DISCOCLAW_DISCORD_ACTIONS` | `1` | Master switch for Discord server actions |
|
|
39
40
|
| `DISCOCLAW_DISCORD_ACTIONS_CHANNELS` | `1` | Channel management (create/edit/delete/list/info, categoryCreate) |
|
|
40
|
-
| `DISCOCLAW_DISCORD_ACTIONS_MESSAGING` | `
|
|
41
|
-
| `DISCOCLAW_DISCORD_ACTIONS_GUILD` | `
|
|
41
|
+
| `DISCOCLAW_DISCORD_ACTIONS_MESSAGING` | `1` | Messaging (send/edit/delete/read messages, react, threads, pins) |
|
|
42
|
+
| `DISCOCLAW_DISCORD_ACTIONS_GUILD` | `1` | Guild info (memberInfo, roleInfo, roleAdd/Remove, events, search) |
|
|
42
43
|
| `DISCOCLAW_DISCORD_ACTIONS_MODERATION` | `0` | Moderation (timeout, kick, ban) |
|
|
43
|
-
| `DISCOCLAW_DISCORD_ACTIONS_POLLS` | `
|
|
44
|
+
| `DISCOCLAW_DISCORD_ACTIONS_POLLS` | `1` | Poll creation |
|
|
44
45
|
| `DISCOCLAW_DISCORD_ACTIONS_TASKS` | `1` | Task tracking (create/update/close/show/list/sync) |
|
|
46
|
+
| `DISCOCLAW_DISCORD_ACTIONS_CRONS` | `1` | Cron/automation forum actions |
|
|
47
|
+
| `DISCOCLAW_DISCORD_ACTIONS_BOT_PROFILE` | `1` | Bot profile management (display name, avatar, status) |
|
|
48
|
+
| `DISCOCLAW_DISCORD_ACTIONS_FORGE` | `1` | Forge actions (file drafting and implementation) |
|
|
49
|
+
| `DISCOCLAW_DISCORD_ACTIONS_PLAN` | `1` | Plan command actions |
|
|
50
|
+
| `DISCOCLAW_DISCORD_ACTIONS_MEMORY` | `1` | Memory management actions |
|
|
51
|
+
| `DISCOCLAW_DISCORD_ACTIONS_DEFER` | `1` | Deferred/background action execution |
|
|
52
|
+
| `DISCOCLAW_DISCORD_ACTIONS_DEFER_MAX_DELAY_SECONDS` | `1800` | Max wait in seconds before a deferred action times out |
|
|
53
|
+
| `DISCOCLAW_DISCORD_ACTIONS_DEFER_MAX_CONCURRENT` | `5` | Max concurrent deferred actions |
|
|
45
54
|
|
|
46
55
|
### Claude CLI
|
|
47
56
|
| Variable | Default | Description |
|
|
@@ -49,6 +58,7 @@ Two setup paths:
|
|
|
49
58
|
| `CLAUDE_BIN` | `claude` | Path/name of the Claude CLI binary |
|
|
50
59
|
| `CLAUDE_DANGEROUSLY_SKIP_PERMISSIONS` | `0` | Pass `--dangerously-skip-permissions` to the CLI |
|
|
51
60
|
| `CLAUDE_OUTPUT_FORMAT` | `text` | `text` or `stream-json` (preferred for smoother streaming) |
|
|
61
|
+
| `CLAUDE_VERBOSE` | `0` | Pass `--verbose` to the CLI; only effective when `CLAUDE_OUTPUT_FORMAT=stream-json` |
|
|
52
62
|
| `CLAUDE_ECHO_STDIO` | `0` | Forward raw CLI stdout/stderr lines into Discord output |
|
|
53
63
|
| `CLAUDE_DEBUG_FILE` | *(empty)* | Write Claude CLI debug logs to this file path |
|
|
54
64
|
| `CLAUDE_STRICT_MCP_CONFIG` | `1` | Pass `--strict-mcp-config` to skip slow MCP plugin init |
|
|
@@ -63,14 +73,21 @@ Two setup paths:
|
|
|
63
73
|
| `USE_GROUP_DIR_CWD` | `0` | Enable nanoclaw-style group CWD per session |
|
|
64
74
|
| `LOG_LEVEL` | `info` | Pino log level |
|
|
65
75
|
| `DISCOCLAW_DEBUG_RUNTIME` | `0` | Dump resolved runtime config at startup (debugging systemd env issues) |
|
|
76
|
+
| `DISCOCLAW_FORCE_BOOTSTRAP` | `0` | Re-create `workspace/BOOTSTRAP.md` from template on next startup (set to `1`; auto-clears after first boot) |
|
|
66
77
|
|
|
67
78
|
### Runtime Invocation
|
|
68
79
|
| Variable | Default | Description |
|
|
69
80
|
|----------|---------|-------------|
|
|
81
|
+
| `PRIMARY_RUNTIME` | `claude` | Runtime engine (`claude`, `openai`, `openrouter`, `gemini`, `codex`) |
|
|
70
82
|
| `RUNTIME_MODEL` | `capable` | Model tier (`fast`, `capable`) or concrete model name passed to the CLI |
|
|
71
83
|
| `RUNTIME_TOOLS` | `Bash,Read,Write,Edit,Glob,Grep,WebSearch,WebFetch` | Comma-separated tool list |
|
|
72
|
-
| `RUNTIME_TIMEOUT_MS` | `
|
|
84
|
+
| `RUNTIME_TIMEOUT_MS` | `1800000` | Per-invocation timeout in milliseconds |
|
|
85
|
+
| `RUNTIME_FALLBACK_MODEL` | *(unset)* | Auto-fallback model when primary is overloaded (e.g. `sonnet`) |
|
|
86
|
+
| `RUNTIME_MAX_BUDGET_USD` | *(unset)* | Max USD per CLI process; one-shot = per invocation, multi-turn = per session lifetime |
|
|
87
|
+
| `DISCOCLAW_FAST_MODEL` | `fast` | Default "fast" model tier alias used for summarization, auto-tag, and cron parsing |
|
|
73
88
|
| `DISCOCLAW_RUNTIME_SESSIONS` | `1` | Persist Claude session IDs across messages |
|
|
89
|
+
| `DISCOCLAW_SESSION_SCANNING` | `1` | Enable session ID scanning for resume detection |
|
|
90
|
+
| `DISCOCLAW_ACTION_FOLLOWUP_DEPTH` | `3` | Max depth for chained action follow-ups |
|
|
74
91
|
| `DISCOCLAW_MESSAGE_HISTORY_BUDGET` | `3000` | Char budget for recent conversation history in prompts (0 = disabled) |
|
|
75
92
|
| `DISCOCLAW_SUMMARY_ENABLED` | `1` | Enable rolling conversation summaries |
|
|
76
93
|
| `DISCOCLAW_SUMMARY_MODEL` | `fast` | Model tier or concrete name for summarization |
|
|
@@ -81,18 +98,25 @@ Two setup paths:
|
|
|
81
98
|
| `DISCOCLAW_DURABLE_MAX_ITEMS` | `200` | Max durable items per user |
|
|
82
99
|
| `DISCOCLAW_MEMORY_COMMANDS_ENABLED` | `1` | Enable `!memory` commands (show/remember/forget/reset) |
|
|
83
100
|
| `DISCOCLAW_STATUS_CHANNEL` | *(empty — disabled)* | Channel name or ID for status messages (bot online/offline, errors) |
|
|
84
|
-
| `RUNTIME_FALLBACK_MODEL` | *(unset)* | Auto-fallback model when primary is overloaded (e.g. `sonnet`) |
|
|
85
|
-
| `RUNTIME_MAX_BUDGET_USD` | *(unset)* | Max USD per CLI process; one-shot = per invocation, multi-turn = per session lifetime |
|
|
86
101
|
| `CLAUDE_APPEND_SYSTEM_PROMPT` | *(unset)* | Append to system prompt (max 4000 chars); skips workspace PA file reads when set |
|
|
87
|
-
| `DISCOCLAW_CRON_ENABLED` | `1` | Master switch for the cron subsystem (forum-based scheduled tasks) |
|
|
88
|
-
| `DISCOCLAW_CRON_FORUM` | **(required when enabled)** | Automations forum channel ID (snowflake) for cron/automation definitions |
|
|
89
|
-
| `DISCOCLAW_CRON_MODEL` | `fast` | Model tier or concrete name for parsing cron definitions |
|
|
90
102
|
|
|
91
103
|
### Browser Automation
|
|
92
104
|
| Variable | Default | Description |
|
|
93
105
|
|----------|---------|-------------|
|
|
94
106
|
| `AGENT_BROWSER_EXECUTABLE_PATH` | *(empty)* | Path to the browser binary for `agent-browser` (e.g. Chromium). If unset, agent-browser uses its bundled default. |
|
|
95
107
|
|
|
108
|
+
### Cron
|
|
109
|
+
| Variable | Default | Description |
|
|
110
|
+
|----------|---------|-------------|
|
|
111
|
+
| `DISCOCLAW_CRON_ENABLED` | `1` | Master switch for the cron subsystem (forum-based scheduled tasks) |
|
|
112
|
+
| `DISCOCLAW_CRON_FORUM` | **(required when enabled)** | Automations forum channel ID (snowflake) for cron/automation definitions |
|
|
113
|
+
| `DISCOCLAW_CRON_MODEL` | `fast` | Model tier or concrete name for parsing cron definitions |
|
|
114
|
+
| `DISCOCLAW_CRON_AUTO_TAG` | `1` | Enable AI auto-tagging of cron threads |
|
|
115
|
+
| `DISCOCLAW_CRON_AUTO_TAG_MODEL` | `fast` | Model tier or concrete name for cron auto-tagging |
|
|
116
|
+
| `DISCOCLAW_CRON_STATS_DIR` | *(empty)* | Override directory for cron run stats (defaults to data dir) |
|
|
117
|
+
| `DISCOCLAW_CRON_TAG_MAP` | *(empty)* | Override path to cron forum tag map JSON |
|
|
118
|
+
| `DEFAULT_TIMEZONE` | *(system timezone)* | Default IANA timezone for cron schedules that don't specify one (e.g. `America/New_York`); falls back to system timezone if unset or invalid |
|
|
119
|
+
|
|
96
120
|
### Tasks (Task Tracking)
|
|
97
121
|
| Variable | Default | Description |
|
|
98
122
|
|----------|---------|-------------|
|
|
@@ -101,12 +125,103 @@ Two setup paths:
|
|
|
101
125
|
| `DISCOCLAW_TASKS_CWD` | `<WORKSPACE_CWD>` | Tasks workspace override (legacy import/migration path) |
|
|
102
126
|
| `DISCOCLAW_TASKS_TAG_MAP` | `data/tasks/tag-map.json` | Path to task forum tag map |
|
|
103
127
|
| `DISCOCLAW_TASKS_MENTION_USER` | *(empty)* | User ID to @mention in new task threads |
|
|
104
|
-
| `DISCOCLAW_TASKS_SIDEBAR` | `
|
|
128
|
+
| `DISCOCLAW_TASKS_SIDEBAR` | `1` | When `1` + `MENTION_USER` set, persists @mention in open task starters for sidebar visibility |
|
|
105
129
|
| `DISCOCLAW_TASKS_AUTO_TAG` | `1` | Enable AI auto-tagging |
|
|
106
130
|
| `DISCOCLAW_TASKS_AUTO_TAG_MODEL` | `fast` | Model tier or concrete name for auto-tagging |
|
|
107
131
|
| `DISCOCLAW_TASKS_SYNC_SKIP_PHASE5` | `0` | Disable phase-5 reconciliation during sync |
|
|
132
|
+
| `DISCOCLAW_TASKS_SYNC_FAILURE_RETRY_ENABLED` | `1` | Retry a failed Discord sync on the next mutation |
|
|
133
|
+
| `DISCOCLAW_TASKS_SYNC_FAILURE_RETRY_DELAY_MS` | `30000` | Delay in ms before retrying a failed sync |
|
|
134
|
+
| `DISCOCLAW_TASKS_SYNC_DEFERRED_RETRY_DELAY_MS` | `30000` | Delay in ms for deferred sync retries when Discord is busy |
|
|
108
135
|
| `DISCOCLAW_TASKS_PREFIX` | `ws` | Prefix for generated task IDs (e.g. `ws-001`) |
|
|
109
136
|
|
|
137
|
+
### Short-Term Memory
|
|
138
|
+
| Variable | Default | Description |
|
|
139
|
+
|----------|---------|-------------|
|
|
140
|
+
| `DISCOCLAW_SHORTTERM_MEMORY_ENABLED` | `1` | Enable ephemeral short-term memory (cross-session recency context) |
|
|
141
|
+
| `DISCOCLAW_SHORTTERM_MAX_ENTRIES` | `20` | Max entries kept in the short-term memory store |
|
|
142
|
+
| `DISCOCLAW_SHORTTERM_MAX_AGE_HOURS` | `6` | Max age in hours before a short-term entry expires |
|
|
143
|
+
| `DISCOCLAW_SHORTTERM_INJECT_MAX_CHARS` | `1000` | Max chars for short-term memory injected into prompts |
|
|
144
|
+
| `DISCOCLAW_SHORTTERM_DATA_DIR` | *(empty)* | Override directory for short-term memory storage |
|
|
145
|
+
| `DISCOCLAW_SUMMARY_TO_DURABLE_ENABLED` | `1` | Promote significant summary content into durable memory |
|
|
146
|
+
| `DISCOCLAW_SUMMARY_DATA_DIR` | *(empty)* | Override directory for rolling summary storage |
|
|
147
|
+
| `DISCOCLAW_DURABLE_DATA_DIR` | *(empty)* | Override directory for durable memory storage |
|
|
148
|
+
|
|
149
|
+
### Reaction Handler
|
|
150
|
+
| Variable | Default | Description |
|
|
151
|
+
|----------|---------|-------------|
|
|
152
|
+
| `DISCOCLAW_REACTION_HANDLER` | `1` | Enable reaction-add event handler |
|
|
153
|
+
| `DISCOCLAW_REACTION_REMOVE_HANDLER` | `0` | Enable reaction-remove event handler |
|
|
154
|
+
| `DISCOCLAW_REACTION_MAX_AGE_HOURS` | `24` | Max age in hours of a message that will trigger a reaction event |
|
|
155
|
+
|
|
156
|
+
### Bot Identity
|
|
157
|
+
| Variable | Default | Description |
|
|
158
|
+
|----------|---------|-------------|
|
|
159
|
+
| `DISCOCLAW_BOT_NAME` | *(empty)* | Override the bot's display name in Discord |
|
|
160
|
+
| `DISCOCLAW_BOT_STATUS` | *(empty — Discord default)* | Presence status: `online`, `idle`, `dnd`, or `invisible` |
|
|
161
|
+
| `DISCOCLAW_BOT_ACTIVITY` | *(empty)* | Activity text shown in Discord presence (e.g. `with fire`) |
|
|
162
|
+
| `DISCOCLAW_BOT_ACTIVITY_TYPE` | `Playing` | Activity verb: `Playing`, `Listening`, `Watching`, `Competing`, or `Custom` |
|
|
163
|
+
| `DISCOCLAW_BOT_AVATAR` | *(empty)* | Absolute file path or URL for the bot's avatar image |
|
|
164
|
+
|
|
165
|
+
### Health/Status
|
|
166
|
+
| Variable | Default | Description |
|
|
167
|
+
|----------|---------|-------------|
|
|
168
|
+
| `DISCOCLAW_HEALTH_COMMANDS_ENABLED` | `1` | Enable `!health` command (uptime, memory, runtime status) |
|
|
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
|
+
|
|
171
|
+
### Plan & Forge
|
|
172
|
+
| Variable | Default | Description |
|
|
173
|
+
|----------|---------|-------------|
|
|
174
|
+
| `DISCOCLAW_PLAN_COMMANDS_ENABLED` | `1` | Enable plan commands (`!plan`, `!plan phase`, etc.) |
|
|
175
|
+
| `PLAN_PHASES_ENABLED` | `1` | Enable phase-by-phase plan execution |
|
|
176
|
+
| `PLAN_PHASE_MAX_CONTEXT_FILES` | `5` | Max `.context/` files injected per plan phase |
|
|
177
|
+
| `PLAN_PHASE_TIMEOUT_MS` | `1800000` | Per-phase timeout in milliseconds |
|
|
178
|
+
| `PLAN_PHASE_AUDIT_FIX_MAX` | `3` | Max audit-fix attempts per phase before giving up |
|
|
179
|
+
| `DISCOCLAW_FORGE_COMMANDS_ENABLED` | `1` | Enable forge commands (`!forge`) |
|
|
180
|
+
| `FORGE_MAX_AUDIT_ROUNDS` | `5` | Max audit rounds before forge accepts the draft |
|
|
181
|
+
| `FORGE_DRAFTER_MODEL` | *(empty — uses `RUNTIME_MODEL`)* | Override model for the forge drafter step |
|
|
182
|
+
| `FORGE_AUDITOR_MODEL` | *(empty — uses `RUNTIME_MODEL`)* | Override model for the forge auditor step |
|
|
183
|
+
| `FORGE_TIMEOUT_MS` | `1800000` | Per-forge-session timeout in milliseconds |
|
|
184
|
+
| `FORGE_PROGRESS_THROTTLE_MS` | `3000` | Min ms between forge progress Discord updates |
|
|
185
|
+
| `FORGE_AUTO_IMPLEMENT` | `1` | Automatically implement the approved forge plan without a separate confirm step |
|
|
186
|
+
| `FORGE_DRAFTER_RUNTIME` | *(empty — uses `PRIMARY_RUNTIME`)* | Runtime adapter for the forge drafter (e.g. `openai`, `claude`) |
|
|
187
|
+
| `FORGE_AUDITOR_RUNTIME` | *(empty — uses `PRIMARY_RUNTIME`)* | Runtime adapter for the forge auditor |
|
|
188
|
+
|
|
189
|
+
### Multi-Provider Adapters
|
|
190
|
+
| Variable | Default | Description |
|
|
191
|
+
|----------|---------|-------------|
|
|
192
|
+
| `OPENAI_API_KEY` | *(empty)* | OpenAI API key; required when `PRIMARY_RUNTIME=openai` or `FORGE_*_RUNTIME=openai` |
|
|
193
|
+
| `OPENAI_BASE_URL` | *(empty — OpenAI default)* | OpenAI-compatible API base URL override (e.g. for local models) |
|
|
194
|
+
| `OPENAI_MODEL` | `gpt-4o` | Default model for the OpenAI adapter |
|
|
195
|
+
| `OPENROUTER_API_KEY` | *(empty)* | OpenRouter API key; required when `PRIMARY_RUNTIME=openrouter` |
|
|
196
|
+
| `OPENROUTER_BASE_URL` | *(empty — OpenRouter default)* | OpenRouter API base URL override |
|
|
197
|
+
| `OPENROUTER_MODEL` | `anthropic/claude-sonnet-4` | Default model for the OpenRouter adapter |
|
|
198
|
+
| `GEMINI_BIN` | `gemini` | Path/name of the Gemini CLI binary |
|
|
199
|
+
| `GEMINI_MODEL` | `gemini-2.5-pro` | Default model for the Gemini adapter |
|
|
200
|
+
| `CODEX_BIN` | `codex` | Path/name of the Codex CLI binary |
|
|
201
|
+
| `CODEX_MODEL` | `gpt-5.3-codex` | Default model for the Codex adapter |
|
|
202
|
+
| `CODEX_DANGEROUSLY_BYPASS_APPROVALS_AND_SANDBOX` | `0` | Pass the bypass-approvals flag to Codex (use with caution) |
|
|
203
|
+
| `CODEX_DISABLE_SESSIONS` | `0` | Disable session persistence for the Codex adapter |
|
|
204
|
+
|
|
205
|
+
### Webhooks
|
|
206
|
+
| Variable | Default | Description |
|
|
207
|
+
|----------|---------|-------------|
|
|
208
|
+
| `DISCOCLAW_WEBHOOK_ENABLED` | `0` | Enable the inbound webhook HTTP server |
|
|
209
|
+
| `DISCOCLAW_WEBHOOK_PORT` | `9400` | Port for the webhook HTTP server |
|
|
210
|
+
| `DISCOCLAW_WEBHOOK_CONFIG` | *(empty)* | Path to webhook route config JSON |
|
|
211
|
+
|
|
212
|
+
### Streaming & Multi-Turn
|
|
213
|
+
| Variable | Default | Description |
|
|
214
|
+
|----------|---------|-------------|
|
|
215
|
+
| `DISCOCLAW_MULTI_TURN` | `1` | Enable multi-turn (persistent subprocess) mode |
|
|
216
|
+
| `DISCOCLAW_MULTI_TURN_HANG_TIMEOUT_MS` | `60000` | Timeout in ms to detect a hung multi-turn process |
|
|
217
|
+
| `DISCOCLAW_MULTI_TURN_IDLE_TIMEOUT_MS` | `300000` | Idle timeout in ms before a multi-turn process is recycled |
|
|
218
|
+
| `DISCOCLAW_MULTI_TURN_MAX_PROCESSES` | `5` | Max concurrent multi-turn processes |
|
|
219
|
+
| `DISCOCLAW_TOOL_AWARE_STREAMING` | `1` | Parse tool-use events during streaming for better progress reporting |
|
|
220
|
+
| `DISCOCLAW_STREAM_STALL_TIMEOUT_MS` | `300000` | Timeout in ms before a stalled stream is aborted |
|
|
221
|
+
| `DISCOCLAW_PROGRESS_STALL_TIMEOUT_MS` | `300000` | Timeout in ms before a stalled progress indicator is shown |
|
|
222
|
+
| `DISCOCLAW_STREAM_STALL_WARNING_MS` | `150000` | Warn in Discord after this many ms of stream inactivity |
|
|
223
|
+
| `DISCOCLAW_MAX_CONCURRENT_INVOCATIONS` | `0` | Max concurrent runtime invocations (0 = unlimited) |
|
|
224
|
+
|
|
110
225
|
## Debugging
|
|
111
226
|
|
|
112
227
|
### Where logs go
|
package/.context/discord.md
CHANGED
|
@@ -28,7 +28,7 @@ Strict mode:
|
|
|
28
28
|
- `DISCORD_AUTO_INDEX_CHANNEL_CONTEXT=1` (default): when a message arrives for a new channel, DiscoClaw appends it to `discord/DISCORD.md` and creates a blank stub file.
|
|
29
29
|
|
|
30
30
|
Thread auto-join:
|
|
31
|
-
- `DISCORD_AUTO_JOIN_THREADS=
|
|
31
|
+
- `DISCORD_AUTO_JOIN_THREADS=1` (default): best-effort auto-join threads so the bot can respond inside them. Private threads still require adding the bot manually.
|
|
32
32
|
|
|
33
33
|
## Conversation History & Memory
|
|
34
34
|
When `DISCOCLAW_MESSAGE_HISTORY_BUDGET` > 0 (default: `3000`), DiscoClaw fetches recent messages from the Discord channel and includes them in the prompt as conversation context. This allows Claude to maintain conversational context across messages without relying on CLI session persistence. The char budget caps the total history size; messages are selected newest-first so the most relevant context is preserved. Bot responses are truncated when necessary to fit the budget.
|
|
@@ -73,19 +73,30 @@ Each action category has its own flag (only active when the master switch is `1`
|
|
|
73
73
|
|
|
74
74
|
| Flag | Default | Actions |
|
|
75
75
|
|------|---------|---------|
|
|
76
|
-
| `DISCOCLAW_DISCORD_ACTIONS_CHANNELS` | `1` | channelCreate, channelEdit, channelDelete, channelList, channelInfo, categoryCreate, threadListArchived |
|
|
77
|
-
| `DISCOCLAW_DISCORD_ACTIONS_MESSAGING` | `
|
|
78
|
-
| `DISCOCLAW_DISCORD_ACTIONS_GUILD` | `
|
|
76
|
+
| `DISCOCLAW_DISCORD_ACTIONS_CHANNELS` | `1` | channelCreate, channelEdit, channelDelete, channelList, channelInfo, categoryCreate, channelMove, threadListArchived, forumTagCreate, forumTagDelete, forumTagList, threadEdit |
|
|
77
|
+
| `DISCOCLAW_DISCORD_ACTIONS_MESSAGING` | `1` | sendMessage, sendFile, react, unreact, readMessages, fetchMessage, editMessage, deleteMessage, bulkDelete, crosspost, threadCreate, pinMessage, unpinMessage, listPins, reactionPrompt |
|
|
78
|
+
| `DISCOCLAW_DISCORD_ACTIONS_GUILD` | `1` | memberInfo, roleInfo, roleAdd, roleRemove, searchMessages, eventList, eventCreate, eventEdit, eventDelete |
|
|
79
79
|
| `DISCOCLAW_DISCORD_ACTIONS_MODERATION` | `0` | timeout, kick, ban |
|
|
80
|
-
| `DISCOCLAW_DISCORD_ACTIONS_POLLS` | `
|
|
81
|
-
| `DISCOCLAW_DISCORD_ACTIONS_TASKS` | `1` | taskCreate, taskUpdate, taskClose, taskShow, taskList, taskSync |
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
| `DISCOCLAW_DISCORD_ACTIONS_POLLS` | `1` | poll |
|
|
81
|
+
| `DISCOCLAW_DISCORD_ACTIONS_TASKS` | `1` | taskCreate, taskUpdate, taskClose, taskShow, taskList, taskSync, tagMapReload |
|
|
82
|
+
| `DISCOCLAW_DISCORD_ACTIONS_CRONS` | `1` | cronCreate, cronUpdate, cronList, cronShow, cronPause, cronResume, cronDelete, cronTrigger, cronSync, cronTagMapReload |
|
|
83
|
+
| `DISCOCLAW_DISCORD_ACTIONS_BOT_PROFILE` | `1` | botSetStatus, botSetActivity, botSetNickname |
|
|
84
|
+
| `DISCOCLAW_DISCORD_ACTIONS_FORGE` | `1` | forgeCreate, forgeResume, forgeStatus, forgeCancel |
|
|
85
|
+
| `DISCOCLAW_DISCORD_ACTIONS_PLAN` | `1` | planList, planShow, planApprove, planClose, planCreate, planRun |
|
|
86
|
+
| `DISCOCLAW_DISCORD_ACTIONS_MEMORY` | `1` | memoryRemember, memoryForget, memoryShow |
|
|
87
|
+
| `DISCOCLAW_DISCORD_ACTIONS_DEFER` | `1` | defer |
|
|
88
|
+
| _(config — always on)_ | — | modelSet, modelShow |
|
|
89
|
+
|
|
90
|
+
Notes:
|
|
91
|
+
- `reactionPrompt` is gated by the MESSAGING flag — it is registered via `REACTION_PROMPT_ACTION_TYPES` only when `flags.messaging` is true (`src/discord/actions.ts:113`).
|
|
92
|
+
- Config actions (`modelSet`, `modelShow`) have no separate env flag. They are always enabled when the master switch is on, hardcoded in `src/index.ts`.
|
|
93
|
+
|
|
94
|
+
Auto-follow-up: When query actions (channelList, channelInfo, threadListArchived, forumTagList, readMessages, fetchMessage, listPins, memberInfo, roleInfo, searchMessages, eventList, taskList, taskShow, cronList, cronShow, planList, planShow, memoryShow, modelShow, forgeStatus) succeed, DiscoClaw automatically re-invokes Claude with the results. This allows Claude to reason about query results without requiring the user to send a follow-up message. Controlled by `DISCOCLAW_ACTION_FOLLOWUP_DEPTH` (default `3`, `0` disables). Mutation-only responses do not trigger follow-ups. Trivially short follow-up responses (<50 chars with no actions) are suppressed.
|
|
84
95
|
|
|
85
96
|
Requirements:
|
|
86
97
|
- The bot needs appropriate permissions in the server (Manage Channels, Manage Roles, Moderate Members, etc.) depending on the actions used. These are server-level role permissions, not Developer Portal settings.
|
|
87
98
|
- Only works in guild channels (not DMs).
|
|
88
|
-
- Master switch defaults to
|
|
99
|
+
- Master switch defaults to on (`1`). Only allowlisted users can trigger actions.
|
|
89
100
|
- Destructive actions (delete, kick, ban, timeout) prompt Claude to confirm with the user first.
|
|
90
101
|
- If actions fail with "Missing Permissions", the bot's role lacks the required permission.
|
|
91
102
|
|
|
@@ -126,7 +137,6 @@ DiscoClaw includes a task tracker backed by in-process `TaskStore` data and sync
|
|
|
126
137
|
- Data model/store: `src/tasks/types.ts`, `src/tasks/store.ts`
|
|
127
138
|
- Discord task action path: `src/tasks/task-action-executor.ts` (dispatch), `src/tasks/task-action-mutations.ts` (create/update/close), `src/tasks/task-action-thread-sync.ts` (thread lifecycle helpers), `src/tasks/task-action-mutation-helpers.ts` (shared mutation helpers), `src/tasks/task-action-read-ops.ts` (show/list/sync/reload), `src/tasks/task-action-contract.ts` (request types)
|
|
128
139
|
- Canonical sync modules: `src/tasks/task-sync-engine.ts`, `src/tasks/task-sync-pipeline.ts` (facade), `src/tasks/task-sync-apply-plan.ts`, `src/tasks/task-sync-reconcile-plan.ts`, `src/tasks/task-sync-apply-types.ts`, `src/tasks/task-sync-phase-apply.ts`, `src/tasks/task-sync-reconcile.ts`, `src/tasks/sync-coordinator.ts`, `src/tasks/sync-coordinator-metrics.ts`, `src/tasks/sync-coordinator-retries.ts`, `src/tasks/thread-helpers.ts`, `src/tasks/thread-forum-ops.ts`, `src/tasks/thread-lifecycle-ops.ts`, `src/tasks/thread-ops.ts` (facade), `src/tasks/tag-map.ts`
|
|
129
|
-
- Runtime shim status: `src/beads/*` compatibility modules removed in hard-cut phase
|
|
130
140
|
|
|
131
141
|
Auto-sync is event-driven from the in-process store and runs a startup reconciliation pass.
|
|
132
142
|
Auto-triggered syncs are silent; explicit `taskSync` can post status output.
|
package/.context/runtime.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
Sources to check:
|
|
8
8
|
- **Anthropic:** https://platform.claude.com/docs/en/about-claude/models/overview
|
|
9
9
|
- **OpenAI/Codex:** https://developers.openai.com/codex/models/
|
|
10
|
+
- **Google Gemini:** https://ai.google.dev/gemini-api/docs/models
|
|
10
11
|
- **Claude Code CLI shorthand:** `sonnet`, `opus`, `haiku` resolve to the latest version of each model family
|
|
11
12
|
|
|
12
13
|
Current model IDs (as of 2026-02-17):
|
|
@@ -18,6 +19,10 @@ Current model IDs (as of 2026-02-17):
|
|
|
18
19
|
| OpenAI | GPT-5.3-Codex | `gpt-5.3-codex` |
|
|
19
20
|
| OpenAI | GPT-5.3-Codex-Spark | `gpt-5.3-codex-spark` |
|
|
20
21
|
| OpenAI | GPT-5-Codex-Mini | `gpt-5-codex-mini` |
|
|
22
|
+
| Google | Gemini 2.5 Pro | `gemini-2.5-pro` |
|
|
23
|
+
| Google | Gemini 2.5 Flash | `gemini-2.5-flash` |
|
|
24
|
+
|
|
25
|
+
**OpenRouter model IDs** use provider-namespaced format: `anthropic/claude-sonnet-4`, `openai/gpt-4o`, etc. Always check the OpenRouter model list for current IDs — do not guess.
|
|
21
26
|
|
|
22
27
|
## Runtime Adapter Interface
|
|
23
28
|
- The orchestrator consumes a provider-agnostic event stream (`EngineEvent`) from any adapter.
|
|
@@ -41,6 +46,7 @@ The factory provides: subprocess tracking, process pool, stall detection, sessio
|
|
|
41
46
|
|----------|------|------------|-------|
|
|
42
47
|
| Claude Code | `strategies/claude-strategy.ts` | process-pool | Default JSONL parsing, image support |
|
|
43
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 |
|
|
49
|
+
| Gemini CLI | `strategies/gemini-strategy.ts` | none (Phase 1) | Text-only output mode; no sessions; stdin fallback for large prompts |
|
|
44
50
|
| Template | `strategies/template-strategy.ts` | — | Commented starting point for new models |
|
|
45
51
|
|
|
46
52
|
Thin wrappers (`claude-code-cli.ts`, `codex-cli.ts`) map legacy opts and re-export for backward compatibility. Shared utilities live in `cli-shared.ts` and `cli-output-parsers.ts`. Strategy types are in `cli-strategy.ts`.
|
|
@@ -70,6 +76,49 @@ Shutdown: `killAllSubprocesses()` from `cli-adapter.ts` kills all tracked subpro
|
|
|
70
76
|
- `CLAUDE_OUTPUT_FORMAT=stream-json` (preferred; DiscoClaw parses JSONL and streams text)
|
|
71
77
|
- `CLAUDE_OUTPUT_FORMAT=text` (fallback if your local CLI doesn't support stream-json)
|
|
72
78
|
|
|
79
|
+
## Gemini CLI Runtime
|
|
80
|
+
|
|
81
|
+
- Adapter: `src/runtime/gemini-cli.ts` (thin wrapper around `cli-adapter.ts` + `strategies/gemini-strategy.ts`)
|
|
82
|
+
- Invocation shape:
|
|
83
|
+
```
|
|
84
|
+
gemini --model <id> -- <prompt>
|
|
85
|
+
```
|
|
86
|
+
For large prompts that exceed arg length limits, the prompt is passed via stdin instead.
|
|
87
|
+
- Auth: the Gemini CLI binary handles its own authentication — either OAuth (interactive login) or `GEMINI_API_KEY` env var. DiscoClaw does not manage credentials directly.
|
|
88
|
+
- Env vars:
|
|
89
|
+
| Var | Default | Purpose |
|
|
90
|
+
|-----|---------|---------|
|
|
91
|
+
| `GEMINI_BIN` | `gemini` | Path to the Gemini CLI binary |
|
|
92
|
+
| `GEMINI_MODEL` | `gemini-2.5-pro` | Default model ID |
|
|
93
|
+
- Model tier mapping:
|
|
94
|
+
| Tier | Model |
|
|
95
|
+
|------|-------|
|
|
96
|
+
| `fast` | `gemini-2.5-flash` |
|
|
97
|
+
| `capable` | `gemini-2.5-pro` |
|
|
98
|
+
- Capabilities (Phase 1):
|
|
99
|
+
- `streaming_text` only
|
|
100
|
+
- No sessions / multi-turn (each invocation is independent)
|
|
101
|
+
- No JSONL streaming — output is plain text
|
|
102
|
+
- No tool execution, no fs tools
|
|
103
|
+
- No image input/output support
|
|
104
|
+
|
|
105
|
+
## OpenRouter Adapter
|
|
106
|
+
|
|
107
|
+
- Implementation: reuses `src/runtime/openai-compat.ts` with `id: 'openrouter'` — no separate adapter file needed.
|
|
108
|
+
- Conditional registration: only registered in the runtime registry when `OPENROUTER_API_KEY` is set.
|
|
109
|
+
- Env vars:
|
|
110
|
+
| Var | Default | Purpose |
|
|
111
|
+
|-----|---------|---------|
|
|
112
|
+
| `OPENROUTER_API_KEY` | *(required)* | API key; also gates registration |
|
|
113
|
+
| `OPENROUTER_BASE_URL` | `https://openrouter.ai/api/v1` | OpenRouter API base URL |
|
|
114
|
+
| `OPENROUTER_MODEL` | `anthropic/claude-sonnet-4` | Default model (provider-namespaced) |
|
|
115
|
+
- Model naming: OpenRouter uses provider-namespaced IDs — e.g. `anthropic/claude-sonnet-4`, `openai/gpt-4o`, `google/gemini-2.5-pro`. Never use bare model names.
|
|
116
|
+
- Capabilities:
|
|
117
|
+
- `streaming_text` only
|
|
118
|
+
- No tool execution
|
|
119
|
+
- No sessions / multi-turn
|
|
120
|
+
- Health system: credential presence checked via `checkOpenRouterKey` — returns `skip` if key is missing, `fail` if key is present but invalid/expired.
|
|
121
|
+
|
|
73
122
|
## Tool Surface
|
|
74
123
|
- Default tools: `Bash, Read, Write, Edit, Glob, Grep, WebSearch, WebFetch` (8 tools).
|
|
75
124
|
- `Glob` + `Grep` are purpose-built for file search — faster than `find`/`grep` via Bash.
|
|
@@ -77,7 +126,8 @@ Shutdown: `killAllSubprocesses()` from `cli-adapter.ts` kills all tracked subpro
|
|
|
77
126
|
- Non-Claude adapters use a **capability gate** (`tools_fs`) to determine tool access:
|
|
78
127
|
- Codex CLI adapter: declares `tools_fs` — receives read-only tools (Read, Glob, Grep) in auditor role.
|
|
79
128
|
- OpenAI HTTP adapter: text-only (`streaming_text` only) — no tool execution.
|
|
80
|
-
-
|
|
129
|
+
- Gemini CLI adapter: text-only (`streaming_text` only) — no tool execution, no fs tools (Phase 1).
|
|
130
|
+
- OpenRouter adapter: text-only (`streaming_text` only) — no tool execution.
|
|
81
131
|
|
|
82
132
|
## Per-Workspace Permissions
|
|
83
133
|
- `workspace/PERMISSIONS.json` controls the tool surface per workspace.
|
package/.context/tasks.md
CHANGED
|
@@ -44,8 +44,6 @@ Canonical runtime sync implementation lives in `src/tasks/*`:
|
|
|
44
44
|
- `src/tasks/thread-cache.ts`
|
|
45
45
|
- `src/tasks/forum-guard.ts`
|
|
46
46
|
|
|
47
|
-
Legacy runtime compatibility shims under `src/beads/` have been removed.
|
|
48
|
-
|
|
49
47
|
Primary action trigger is `taskSync` via `src/tasks/task-action-executor.ts`.
|
|
50
48
|
|
|
51
49
|
## Auto-Tagging
|
|
@@ -69,3 +67,6 @@ Primary names:
|
|
|
69
67
|
- `DISCOCLAW_TASKS_AUTO_TAG_MODEL`
|
|
70
68
|
- `DISCOCLAW_TASKS_SYNC_SKIP_PHASE5`
|
|
71
69
|
- `DISCOCLAW_TASKS_PREFIX`
|
|
70
|
+
- `DISCOCLAW_TASKS_SYNC_FAILURE_RETRY_ENABLED`
|
|
71
|
+
- `DISCOCLAW_TASKS_SYNC_FAILURE_RETRY_DELAY_MS`
|
|
72
|
+
- `DISCOCLAW_TASKS_SYNC_DEFERRED_RETRY_DELAY_MS`
|
package/.env.example.full
CHANGED
|
@@ -83,11 +83,14 @@ DISCOCLAW_CRON_FORUM=
|
|
|
83
83
|
#DISCOCLAW_DISCORD_ACTIONS_CRONS=1
|
|
84
84
|
# Persistent stats directory (run counts, last run time, status).
|
|
85
85
|
#DISCOCLAW_CRON_STATS_DIR=
|
|
86
|
-
# AI auto-tagging for cron purpose + model tier
|
|
87
|
-
#DISCOCLAW_CRON_AUTO_TAG=
|
|
86
|
+
# AI auto-tagging for cron purpose + model tier.
|
|
87
|
+
#DISCOCLAW_CRON_AUTO_TAG=1
|
|
88
88
|
#DISCOCLAW_CRON_AUTO_TAG_MODEL=fast
|
|
89
89
|
# Runtime tag-map file (seeded from scripts/cron/cron-tag-map.json on first boot).
|
|
90
90
|
#DISCOCLAW_CRON_TAG_MAP=
|
|
91
|
+
# Default timezone for cron schedules that don't specify one (IANA format, e.g. America/New_York).
|
|
92
|
+
# Falls back to the system timezone if unset or invalid.
|
|
93
|
+
#DEFAULT_TIMEZONE=
|
|
91
94
|
# Set to 0 to disable cron entirely.
|
|
92
95
|
#DISCOCLAW_CRON_ENABLED=1
|
|
93
96
|
|
|
@@ -127,11 +130,11 @@ DISCORD_GUILD_ID=
|
|
|
127
130
|
#DISCOCLAW_DISCORD_ACTIONS=1
|
|
128
131
|
# Per-category flags (only active when master switch is 1):
|
|
129
132
|
#DISCOCLAW_DISCORD_ACTIONS_CHANNELS=1
|
|
130
|
-
#DISCOCLAW_DISCORD_ACTIONS_MESSAGING=
|
|
131
|
-
#DISCOCLAW_DISCORD_ACTIONS_GUILD=
|
|
133
|
+
#DISCOCLAW_DISCORD_ACTIONS_MESSAGING=1
|
|
134
|
+
#DISCOCLAW_DISCORD_ACTIONS_GUILD=1
|
|
132
135
|
# Intentionally off — moderation actions require explicit opt-in.
|
|
133
136
|
#DISCOCLAW_DISCORD_ACTIONS_MODERATION=0
|
|
134
|
-
#DISCOCLAW_DISCORD_ACTIONS_POLLS=
|
|
137
|
+
#DISCOCLAW_DISCORD_ACTIONS_POLLS=1
|
|
135
138
|
#DISCOCLAW_DISCORD_ACTIONS_TASKS=1
|
|
136
139
|
# Allow the AI to self-initiate forge runs (draft + audit loops) via action blocks.
|
|
137
140
|
# Requires DISCOCLAW_FORGE_COMMANDS_ENABLED=1. Only one forge at a time. Default: 1.
|
|
@@ -143,7 +146,7 @@ DISCORD_GUILD_ID=
|
|
|
143
146
|
# Requires DISCOCLAW_DURABLE_MEMORY_ENABLED=1. Default: 1.
|
|
144
147
|
#DISCOCLAW_DISCORD_ACTIONS_MEMORY=1
|
|
145
148
|
# Allow the AI to change status, activity, and nickname at runtime.
|
|
146
|
-
#DISCOCLAW_DISCORD_ACTIONS_BOT_PROFILE=
|
|
149
|
+
#DISCOCLAW_DISCORD_ACTIONS_BOT_PROFILE=1
|
|
147
150
|
|
|
148
151
|
# Allow the AI to defer another invocation (e.g., "check on the forge run in 10 minutes").
|
|
149
152
|
# The scheduler enforces DISCOCLAW_DISCORD_ACTIONS=1, respects the requesting message's channel
|
|
@@ -171,7 +174,7 @@ DISCOCLAW_DISCORD_ACTIONS_DEFER=1
|
|
|
171
174
|
#DISCORD_AUTO_INDEX_CHANNEL_CONTEXT=1
|
|
172
175
|
# Auto-join threads so the bot can respond inside them.
|
|
173
176
|
# Note: private threads still require manually adding the bot.
|
|
174
|
-
#DISCORD_AUTO_JOIN_THREADS=
|
|
177
|
+
#DISCORD_AUTO_JOIN_THREADS=1
|
|
175
178
|
|
|
176
179
|
# ----------------------------------------------------------
|
|
177
180
|
# Reaction handler (trigger AI via emoji reactions)
|
|
@@ -195,17 +198,23 @@ DISCOCLAW_DISCORD_ACTIONS_DEFER=1
|
|
|
195
198
|
#DISCOCLAW_SUMMARY_MODEL=fast
|
|
196
199
|
#DISCOCLAW_SUMMARY_MAX_CHARS=2000
|
|
197
200
|
#DISCOCLAW_SUMMARY_EVERY_N_TURNS=5
|
|
201
|
+
# Override storage directory for rolling summaries.
|
|
202
|
+
#DISCOCLAW_SUMMARY_DATA_DIR=
|
|
198
203
|
# Durable per-user facts/preferences (manual via !memory commands).
|
|
199
204
|
#DISCOCLAW_DURABLE_MEMORY_ENABLED=1
|
|
200
205
|
#DISCOCLAW_DURABLE_INJECT_MAX_CHARS=2000
|
|
201
206
|
#DISCOCLAW_DURABLE_MAX_ITEMS=200
|
|
202
207
|
#DISCOCLAW_MEMORY_COMMANDS_ENABLED=1
|
|
208
|
+
# Override storage directory for durable memory.
|
|
209
|
+
#DISCOCLAW_DURABLE_DATA_DIR=
|
|
203
210
|
# Cross-channel short-term memory (per-guild, per-user ring buffer).
|
|
204
211
|
# Records recent exchanges across public channels for cross-channel awareness.
|
|
205
212
|
#DISCOCLAW_SHORTTERM_MEMORY_ENABLED=0
|
|
206
213
|
#DISCOCLAW_SHORTTERM_MAX_ENTRIES=20
|
|
207
214
|
#DISCOCLAW_SHORTTERM_MAX_AGE_HOURS=6
|
|
208
215
|
#DISCOCLAW_SHORTTERM_INJECT_MAX_CHARS=1000
|
|
216
|
+
# Override storage directory for short-term memory.
|
|
217
|
+
#DISCOCLAW_SHORTTERM_DATA_DIR=
|
|
209
218
|
# Auto-extract notable facts from user messages into durable memory.
|
|
210
219
|
# Runs after rolling summary generation. Default on.
|
|
211
220
|
#DISCOCLAW_SUMMARY_TO_DURABLE_ENABLED=1
|
|
@@ -244,6 +253,8 @@ DISCOCLAW_DISCORD_ACTIONS_DEFER=1
|
|
|
244
253
|
#DISCOCLAW_DATA_DIR=
|
|
245
254
|
# Content directory override.
|
|
246
255
|
#DISCOCLAW_CONTENT_DIR=
|
|
256
|
+
# Force re-create workspace/BOOTSTRAP.md from the template on next startup (set to 1; auto-clears after first boot).
|
|
257
|
+
#DISCOCLAW_FORCE_BOOTSTRAP=0
|
|
247
258
|
# Enable per-session runtime isolation.
|
|
248
259
|
#DISCOCLAW_RUNTIME_SESSIONS=1
|
|
249
260
|
# Path to claude CLI binary (if not on $PATH).
|
|
@@ -281,12 +292,27 @@ DISCOCLAW_DISCORD_ACTIONS_DEFER=1
|
|
|
281
292
|
#DC_RESTART_CMD=
|
|
282
293
|
# Global cap on parallel runtime invocations across all Discord sessions (0 = unlimited).
|
|
283
294
|
#DISCOCLAW_MAX_CONCURRENT_INVOCATIONS=3
|
|
295
|
+
# Max depth for chained action follow-ups (e.g. defer → action → response). 0 = disabled.
|
|
296
|
+
#DISCOCLAW_ACTION_FOLLOWUP_DEPTH=3
|
|
284
297
|
# Timeout for runtime invocations (ms).
|
|
285
298
|
#RUNTIME_TIMEOUT_MS=1800000
|
|
299
|
+
# Multi-turn mode: persistent subprocess per session, keeping session context across messages (default: 1).
|
|
300
|
+
#DISCOCLAW_MULTI_TURN=1
|
|
301
|
+
# Timeout (ms) before a multi-turn process is considered hung and restarted.
|
|
302
|
+
#DISCOCLAW_MULTI_TURN_HANG_TIMEOUT_MS=60000
|
|
303
|
+
# Idle timeout (ms) before an inactive multi-turn process is recycled.
|
|
304
|
+
#DISCOCLAW_MULTI_TURN_IDLE_TIMEOUT_MS=300000
|
|
305
|
+
# Maximum concurrent multi-turn processes (per bot instance).
|
|
306
|
+
#DISCOCLAW_MULTI_TURN_MAX_PROCESSES=5
|
|
307
|
+
# Enable session-ID scanning to detect and resume existing Claude sessions.
|
|
308
|
+
#DISCOCLAW_SESSION_SCANNING=1
|
|
309
|
+
# Parse tool-use events during streaming for better progress reporting and stall suppression.
|
|
310
|
+
#DISCOCLAW_TOOL_AWARE_STREAMING=1
|
|
286
311
|
# Stream stall detection: kill one-shot process if no stdout/stderr for this long (ms). 0 = disabled.
|
|
287
312
|
#DISCOCLAW_STREAM_STALL_TIMEOUT_MS=120000
|
|
313
|
+
# Progress stall timeout: alert after this many ms with no progress event (ms). 0 = disabled.
|
|
314
|
+
#DISCOCLAW_PROGRESS_STALL_TIMEOUT_MS=300000
|
|
288
315
|
# Stream stall warning: show user-visible warning in Discord after this many ms of no events. 0 = disabled.
|
|
289
|
-
# Enable DISCOCLAW_SESSION_SCANNING=1 for tool-aware stall suppression (warnings suppressed during tool execution).
|
|
290
316
|
#DISCOCLAW_STREAM_STALL_WARNING_MS=60000
|
|
291
317
|
|
|
292
318
|
# ----------------------------------------------------------
|
|
@@ -302,7 +328,7 @@ DISCOCLAW_DISCORD_ACTIONS_DEFER=1
|
|
|
302
328
|
# Per-phase execution timeout (ms). Default: 1800000 (30 min).
|
|
303
329
|
#PLAN_PHASE_TIMEOUT_MS=1800000
|
|
304
330
|
# Max audit-fix attempts per phase before marking failed.
|
|
305
|
-
#PLAN_PHASE_AUDIT_FIX_MAX=
|
|
331
|
+
#PLAN_PHASE_AUDIT_FIX_MAX=3
|
|
306
332
|
# Max draft-audit-revise loops before CAP_REACHED.
|
|
307
333
|
#FORGE_MAX_AUDIT_ROUNDS=5
|
|
308
334
|
# Model overrides for forge roles (fall back to RUNTIME_MODEL).
|
|
@@ -317,8 +343,11 @@ DISCOCLAW_DISCORD_ACTIONS_DEFER=1
|
|
|
317
343
|
#FORGE_AUTO_IMPLEMENT=1
|
|
318
344
|
|
|
319
345
|
# ----------------------------------------------------------
|
|
320
|
-
# Multi-provider
|
|
346
|
+
# Multi-provider runtime overrides
|
|
321
347
|
# ----------------------------------------------------------
|
|
348
|
+
# Route the forge drafter to a non-Claude runtime.
|
|
349
|
+
# Valid values: "codex" (Codex CLI), "openai" (OpenAI-compatible HTTP API), "openrouter" (OpenRouter).
|
|
350
|
+
#FORGE_DRAFTER_RUNTIME=
|
|
322
351
|
# Route the forge auditor to a non-Claude runtime.
|
|
323
352
|
# Valid values: "codex" (Codex CLI), "openai" (OpenAI-compatible HTTP API), "openrouter" (OpenRouter).
|
|
324
353
|
#FORGE_AUDITOR_RUNTIME=codex
|
package/dist/cron/cron-sync.js
CHANGED
|
@@ -32,6 +32,27 @@ export async function runCronSync(opts) {
|
|
|
32
32
|
let namesUpdated = 0;
|
|
33
33
|
let statusMessagesUpdated = 0;
|
|
34
34
|
let orphansDetected = 0;
|
|
35
|
+
const asEditableCronThread = (value) => {
|
|
36
|
+
if (!value || typeof value !== 'object')
|
|
37
|
+
return null;
|
|
38
|
+
const t = value;
|
|
39
|
+
if (typeof t.id !== 'string' ||
|
|
40
|
+
typeof t.parentId !== 'string' ||
|
|
41
|
+
typeof t.name !== 'string' ||
|
|
42
|
+
typeof t.edit !== 'function' ||
|
|
43
|
+
typeof t.setName !== 'function') {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const appliedTags = Array.isArray(t.appliedTags) ? t.appliedTags.filter((id) => typeof id === 'string') : undefined;
|
|
47
|
+
return {
|
|
48
|
+
id: t.id,
|
|
49
|
+
parentId: t.parentId,
|
|
50
|
+
name: t.name,
|
|
51
|
+
appliedTags,
|
|
52
|
+
edit: t.edit,
|
|
53
|
+
setName: t.setName,
|
|
54
|
+
};
|
|
55
|
+
};
|
|
35
56
|
// Get all active threads in the forum.
|
|
36
57
|
let threads = new Map();
|
|
37
58
|
try {
|
|
@@ -79,7 +100,8 @@ export async function runCronSync(opts) {
|
|
|
79
100
|
}
|
|
80
101
|
// Apply tags to Discord thread.
|
|
81
102
|
const thread = threads.get(fullJob.threadId);
|
|
82
|
-
|
|
103
|
+
const editableThread = asEditableCronThread(thread);
|
|
104
|
+
if (editableThread) {
|
|
83
105
|
const desiredPurposeTags = updates.purposeTags ?? record.purposeTags;
|
|
84
106
|
const desiredCadence = updates.cadence ?? record.cadence;
|
|
85
107
|
const allTags = [
|
|
@@ -91,15 +113,13 @@ export async function runCronSync(opts) {
|
|
|
91
113
|
.map((t) => tagMap[t])
|
|
92
114
|
.filter((id) => Boolean(id));
|
|
93
115
|
const uniqueTagIds = [...new Set(desiredTagIds)].slice(0, 5);
|
|
94
|
-
const currentTagIds =
|
|
95
|
-
? thread.appliedTags.filter((id) => typeof id === 'string')
|
|
96
|
-
: [];
|
|
116
|
+
const currentTagIds = editableThread.appliedTags ?? [];
|
|
97
117
|
const desiredSet = new Set(uniqueTagIds);
|
|
98
118
|
const tagsOutOfSync = currentTagIds.length !== uniqueTagIds.length
|
|
99
119
|
|| currentTagIds.some((id) => !desiredSet.has(id));
|
|
100
120
|
if (tagsOutOfSync) {
|
|
101
121
|
try {
|
|
102
|
-
await
|
|
122
|
+
await editableThread.edit({ appliedTags: uniqueTagIds });
|
|
103
123
|
tagsApplied++;
|
|
104
124
|
}
|
|
105
125
|
catch (err) {
|
|
@@ -122,11 +142,12 @@ export async function runCronSync(opts) {
|
|
|
122
142
|
const cadence = record?.cadence ?? null;
|
|
123
143
|
const expectedName = buildCronThreadName(fullJob.name, cadence);
|
|
124
144
|
const thread = threads.get(fullJob.threadId);
|
|
125
|
-
|
|
145
|
+
const editableThread = asEditableCronThread(thread);
|
|
146
|
+
if (editableThread && editableThread.name !== expectedName) {
|
|
126
147
|
try {
|
|
127
|
-
await
|
|
148
|
+
await editableThread.setName(expectedName);
|
|
128
149
|
namesUpdated++;
|
|
129
|
-
log?.info({ threadId: fullJob.threadId, oldName:
|
|
150
|
+
log?.info({ threadId: fullJob.threadId, oldName: editableThread.name, newName: expectedName }, 'cron-sync:phase2 name updated');
|
|
130
151
|
}
|
|
131
152
|
catch (err) {
|
|
132
153
|
log?.warn({ err, threadId: fullJob.threadId }, 'cron-sync:phase2 name update failed');
|
|
@@ -153,11 +174,14 @@ export async function runCronSync(opts) {
|
|
|
153
174
|
}
|
|
154
175
|
// Phase 4: Orphan detection (non-destructive, log only).
|
|
155
176
|
for (const thread of threads.values()) {
|
|
156
|
-
|
|
177
|
+
const editableThread = asEditableCronThread(thread);
|
|
178
|
+
if (!editableThread)
|
|
179
|
+
continue;
|
|
180
|
+
if (editableThread.parentId !== forumId)
|
|
157
181
|
continue;
|
|
158
|
-
if (!jobThreadIds.has(
|
|
182
|
+
if (!jobThreadIds.has(editableThread.id)) {
|
|
159
183
|
orphansDetected++;
|
|
160
|
-
log?.warn({ threadId:
|
|
184
|
+
log?.warn({ threadId: editableThread.id, name: editableThread.name }, 'cron-sync:phase4 orphan thread (no registered job)');
|
|
161
185
|
}
|
|
162
186
|
}
|
|
163
187
|
log?.info({ tagsApplied, namesUpdated, statusMessagesUpdated, orphansDetected }, 'cron-sync: complete');
|