claude-slack-channel-bots 0.18.3 → 0.18.5
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/README.md +1 -1
- package/docs/bots.md +2 -0
- package/docs/configuration.md +17 -17
- package/docs/migration.md +2 -1
- package/docs/reload.md +4 -2
- package/docs/troubleshooting.md +7 -1
- package/package.json +1 -1
- package/skills/debug-slack-channel-bots/SKILL.md +43 -14
- package/skills/setup-slack-channel-bots/SKILL.md +14 -3
- package/src/agent-director-template.ts +65 -8
- package/src/config.ts +34 -2
- package/src/delivery-intake.ts +5 -2
- package/src/live-row-sequence.ts +35 -4
- package/src/persona-identity.ts +49 -2
- package/src/reload-plan.ts +124 -27
- package/src/reply-reminder.ts +1 -1
- package/src/server.ts +2 -0
- package/src/session-manager.ts +131 -30
- package/src/tag-wrapper.ts +15 -10
package/README.md
CHANGED
|
@@ -102,7 +102,7 @@ Replace the channel ID and paths with your own. [Configuration](docs/configurati
|
|
|
102
102
|
| `claude-slack-channel-bots start` | Starts the server in the background. It brings up every persona's bot. |
|
|
103
103
|
| `claude-slack-channel-bots stop` | Stops the server. The bots keep running and are picked up by the next start. |
|
|
104
104
|
| `claude-slack-channel-bots stop --stop-bots` | Stops the server, then each bot, keeping its conversation for the next start. |
|
|
105
|
-
| `claude-slack-channel-bots clean_restart --server` | Stops the server and every bot, then starts them again. Conversations resume. Run it from a terminal, not from a bot. |
|
|
105
|
+
| `claude-slack-channel-bots clean_restart --server` | Stops the server and every bot, then starts them again. Conversations resume unless a [confirmed next-launch change](docs/reload.md#when-next-launch-and-server-wide-changes-take-effect) starts a persona fresh. Run it from a terminal, not from a bot. |
|
|
106
106
|
| `claude-slack-channel-bots clean_restart <bot> [<bot>…]` | Restarts only the named bots; the running server relaunches them. Naming the bot you run it from takes that bot down too, and the command with it; the server relaunches it. |
|
|
107
107
|
| `claude-slack-channel-bots credentials <persona>` | Writes one persona's credentials file from your terminal. |
|
|
108
108
|
| `claude-slack-channel-bots clear-latch <persona>` | Ends one persona's hold once a human has fixed its cause. |
|
package/docs/bots.md
CHANGED
|
@@ -141,6 +141,8 @@ A message from Slack carries these tag attributes:
|
|
|
141
141
|
| `thread_ts` | The thread's timestamp, for a thread reply. |
|
|
142
142
|
| `attachment_count`, `attachments` | The number of attached files and each file's name, type and size, when files are attached. |
|
|
143
143
|
|
|
144
|
+
A long `attachments` or `user` value can be cut short, ending in `…`; on a macOS host this happens sooner, and a long `channel_name` can be cut too. `chat_id` and `attachment_count` are always whole.
|
|
145
|
+
|
|
144
146
|
Each way a message reaches a persona:
|
|
145
147
|
|
|
146
148
|
| Kind | How it reaches the persona | Tag attributes | `via` | Reminder |
|
package/docs/configuration.md
CHANGED
|
@@ -10,16 +10,14 @@ Each bot is a **persona**: one Slack app, one Claude instance with its own worki
|
|
|
10
10
|
|
|
11
11
|
The top level holds a required `personas` array and the [server-wide settings](server-settings.md#server-wide-settings). Unknown keys are rejected at every level: the top level, each persona, its `dm` object, its `invited` object in fungible mode, and each channel entry in declarative mode (see [Load-time rules](#load-time-rules)). Only this persona format is accepted; a configuration written for an earlier major version must be rewritten by hand (see [Upgrading to personas](migration.md#upgrading-to-personas)).
|
|
12
12
|
|
|
13
|
-
The first start of an install applies the file as it stands. After that, the server runs a recorded copy, and an edit changes nothing until you confirm it; a server restart alone doesn't apply it. See [Reload](reload.md#reload), and [What a confirmation applies](reload.md#what-a-confirmation-applies) for how each kind of change is applied.
|
|
14
|
-
|
|
15
|
-
Postinstall creates a skeleton with an empty persona list, `{"personas": []}`. An empty list is valid: the server starts with no persona connected.
|
|
13
|
+
The first start of an install applies the file as it stands. After that, the server runs a recorded copy, and an edit changes nothing until you confirm it; a server restart alone doesn't apply it. See [Reload](reload.md#reload), and [What a confirmation applies](reload.md#what-a-confirmation-applies) for how each kind of change is applied. Postinstall creates a skeleton with an empty persona list, `{"personas": []}`, which is valid: the server starts with no persona connected.
|
|
16
14
|
|
|
17
15
|
### Example
|
|
18
16
|
|
|
19
17
|
A complete `config.json` in declarative mode (`allow_invited_channels` absent), with three personas:
|
|
20
18
|
|
|
21
19
|
- `planner` receives every message in its home channel `C0123456789` and in the shared channel `C0555555555`. Its permission prompts and notices go to its home channel. It takes no DMs.
|
|
22
|
-
- `reviewer` receives only its @mentions and `@here` / `@channel` broadcasts in the shared channel. It takes DMs, and its prompts and notices go by DM to its contact `U0123456789`. It has its own Claude config directory and turns [Slack reply reminders](bots.md#slack-reply-reminders) off for itself.
|
|
20
|
+
- `reviewer` receives only its @mentions and `@here` / `@channel` broadcasts in the shared channel. It takes DMs, and its prompts and notices go by DM to its contact `U0123456789`. It has its own Claude config directory, model and effort level, and turns [Slack reply reminders](bots.md#slack-reply-reminders) off for itself.
|
|
23
21
|
- `helpdesk` is DM-only: it is in no channel and is reached only by DM. Its prompts and notices go by DM to its contact `U0987654321`.
|
|
24
22
|
|
|
25
23
|
`planner` and `helpdesk` inherit the top-level `claude_config_dir`. The server-wide settings select queued/delivered reactions and reply splitting. **Custom emoji must exist in the Slack workspace before configuration**; `spinthinking` is custom. Ensure both names below are available, or choose available names; see [Acknowledgement reactions](server-settings.md#acknowledgement-reactions).
|
|
@@ -42,6 +40,7 @@ A complete `config.json` in declarative mode (`allow_invited_channels` absent),
|
|
|
42
40
|
"dm": { "enabled": true, "contact": "U0123456789" },
|
|
43
41
|
"permission_prompts": "dm",
|
|
44
42
|
"claude_config_dir": "~/.claude-reviewer",
|
|
43
|
+
"model": "opus", "effort": "high",
|
|
45
44
|
"stop_hook_bootstrap": false
|
|
46
45
|
},
|
|
47
46
|
{
|
|
@@ -77,6 +76,8 @@ Replace the channel IDs, user IDs and paths with your own. Each persona's creden
|
|
|
77
76
|
| `invited.permission_prompts` | string | no | `"dm"` | Fungible mode only: the persona's **destination**, where its permission prompts and server notices are posted. `"dm"` or a Slack channel ID such as `C0123456789`; the channel needs no listing. Not read in declarative mode. See [Permission prompts in fungible mode](channel-modes.md#permission-prompts-in-fungible-mode). |
|
|
78
77
|
| `claude_config_dir` | path | no | top-level `claude_config_dir` | Claude config directory for this persona. See [Next-launch settings](#next-launch-settings). |
|
|
79
78
|
| `stop_hook_bootstrap` | boolean | no | top-level `stop_hook_bootstrap` | Slack reply reminder switch for this persona. Confirmed changes apply immediately; see [Slack reply reminders](bots.md#slack-reply-reminders). |
|
|
79
|
+
| `model` | string | no | none (Claude Code chooses) | The Claude model the persona's instance starts with, such as `opus`, `opus[1m]` or a full model ID, used exactly as written. Per persona only. See [Next-launch settings](#next-launch-settings). |
|
|
80
|
+
| `effort` | string | no | none (Claude Code chooses) | The effort level the persona's instance starts with: `low`, `medium`, `high`, `xhigh` or `max`. Per persona only. See [Next-launch settings](#next-launch-settings). |
|
|
80
81
|
|
|
81
82
|
`dm` is an object holding `enabled` and `contact`: `"dm": { "enabled": true, "contact": "U0123456789" }`. It applies in both modes. `invited` is an object holding `permission_prompts`: `"invited": { "permission_prompts": "C0123456789" }`. A path is absolute, `~` or starts with `~/`. Paths are never redacted: a log line, preview or error that names a path shows it in full, with `~` expanded.
|
|
82
83
|
|
|
@@ -108,9 +109,7 @@ The key identifies the persona's agent-director instance (`cscb_<key>`), its tmu
|
|
|
108
109
|
|
|
109
110
|
**No persona's key may start with another persona's key.** The server rejects such a pair when it checks the configuration (see [Load-time rules](#load-time-rules)), with an error that names both personas and suggests a new name. tmux matches a session target by prefix unless it is written with `=`, so a human's tmux command without `=` for one persona (for example `tmux attach -t slack_bot_dev`) could reach another persona's session. With personas `dev` and `dev_2`, that command could reach `slack_bot_dev_2`.
|
|
110
111
|
|
|
111
|
-
Give the shorter name a suffix, such as `dev_main` beside `dev_2`, or `team_main` beside `team_admin`, or rename either so that neither key starts with the other. Keys that only share a start, such as `dev_a` and `dev_b`, are fine. A name that isn't its own key counts by its key: `"Dev Bot"` (key `dev_bot_…`) can't sit beside `dev` either.
|
|
112
|
-
|
|
113
|
-
Renaming a persona changes its key, so the old persona is removed and a new one added (see [What a confirmation applies](reload.md#what-a-confirmation-applies)).
|
|
112
|
+
Give the shorter name a suffix, such as `dev_main` beside `dev_2`, or `team_main` beside `team_admin`, or rename either so that neither key starts with the other. Keys that only share a start, such as `dev_a` and `dev_b`, are fine. A name that isn't its own key counts by its key: `"Dev Bot"` (key `dev_bot_…`) can't sit beside `dev` either. Renaming a persona changes its key, so the old persona is removed and a new one added (see [What a confirmation applies](reload.md#what-a-confirmation-applies)).
|
|
114
113
|
|
|
115
114
|
### Credentials files
|
|
116
115
|
|
|
@@ -161,36 +160,37 @@ Any number of personas may list the same channel, each with its own `delivery`.
|
|
|
161
160
|
|
|
162
161
|
`dm.contact` is the person who receives the persona's prompts and notices when its destination is `"dm"`: in declarative mode when `permission_prompts` is `"dm"`, and in fungible mode when `invited.permission_prompts` is `"dm"` or unset. In either mode a `"dm"` destination needs `dm.enabled: true` and a `dm.contact`. `dm.contact` gates nothing else: it doesn't limit who can DM the persona.
|
|
163
162
|
|
|
164
|
-
Starting a DM with a user (a `reply` to a user ID, or a persona whose destination is `"dm"`) needs the `im:write` bot scope, which the shipped `slack-app-manifest.yml` grants. A `"dm"` destination always opens its DM through Slack before it first posts there, even when the DM already exists. For an app created from an earlier manifest, a `"dm"` persona's prompts and notices are not posted, and a `reply` to a user ID fails with `missing_scope`, until the scope is added and the app is re-installed.
|
|
165
|
-
|
|
166
|
-
Receiving DMs and replying in an existing DM (a `D…` ID) work without the scope. The `debug-slack-channel-bots` skill (see [Troubleshooting](troubleshooting.md#troubleshooting)) has the steps under "A persona can't open a DM".
|
|
163
|
+
Starting a DM with a user (a `reply` to a user ID, or a persona whose destination is `"dm"`) needs the `im:write` bot scope, which the shipped `slack-app-manifest.yml` grants. A `"dm"` destination always opens its DM through Slack before it first posts there, even when the DM already exists. For an app created from an earlier manifest, a `"dm"` persona's prompts and notices are not posted, and a `reply` to a user ID fails with `missing_scope`, until the scope is added and the app is re-installed. Receiving DMs and replying in an existing DM (a `D…` ID) work without the scope; the `debug-slack-channel-bots` skill (see [Troubleshooting](troubleshooting.md#troubleshooting)) has the steps under "A persona can't open a DM".
|
|
167
164
|
|
|
168
165
|
### Permission prompts
|
|
169
166
|
|
|
170
|
-
This section applies in declarative mode, where `permission_prompts` is required; for fungible mode, see [Permission prompts in fungible mode](channel-modes.md#permission-prompts-in-fungible-mode). It is the persona's destination: its permission prompts and its server notices, such as *Message not saved* notices and restart-limit warnings, are posted there as the persona.
|
|
167
|
+
This section applies in declarative mode, where `permission_prompts` is required; for fungible mode, see [Permission prompts in fungible mode](channel-modes.md#permission-prompts-in-fungible-mode). It is the persona's destination: its permission prompts and its server notices, such as *Message not saved* notices and restart-limit warnings, are posted there as the persona. [Permission Relay](bots.md#permission-relay) covers how prompts are delivered and answered, and [Troubleshooting](troubleshooting.md#troubleshooting) what happens when Slack refuses a post to the destination.
|
|
171
168
|
|
|
172
169
|
- **A channel ID:** one of the persona's own `channels`. Everyone in that channel sees the prompts and notices.
|
|
173
170
|
- **`"dm"`:** a DM from the persona's app to its `dm.contact`. It needs `dm.enabled: true` and a `dm.contact`.
|
|
174
171
|
|
|
175
172
|
Choose a destination with [channel delivery](#channel-delivery) in mind. If two personas each receive every message in the other’s destination and storing fails, the notices stop after one *Message not saved* each per episode. Further failures are counted in `server.log` (see [Troubleshooting](troubleshooting.md#troubleshooting)).
|
|
176
173
|
|
|
177
|
-
[Permission Relay](bots.md#permission-relay) covers how prompts are delivered and answered, and [Troubleshooting](troubleshooting.md#troubleshooting) what happens when Slack refuses a post to the destination.
|
|
178
|
-
|
|
179
174
|
### Next-launch settings
|
|
180
175
|
|
|
181
|
-
`claude_config_dir` can be set on a persona or at the top level
|
|
176
|
+
`claude_config_dir` can be set on a persona or at the top level: a persona's own value wins, and the top-level value is the default for every persona without one. `model` and `effort` are set on each persona only, with no top-level value, so a persona without them inherits nothing. Once a change is confirmed, it takes effect at the persona's next launch (see [When next-launch and server-wide changes take effect](reload.md#when-next-launch-and-server-wide-changes-take-effect)).
|
|
182
177
|
|
|
183
178
|
**`claude_config_dir`** is the Claude config directory the persona's instance launches with (`CLAUDE_CONFIG_DIR`), so each persona can authenticate as a different Claude account. Populate each directory before the persona first launches:
|
|
184
179
|
|
|
185
180
|
```sh
|
|
186
181
|
CLAUDE_CONFIG_DIR=~/.claude-reviewer claude auth login --claudeai
|
|
187
182
|
```
|
|
188
|
-
Use `--console` instead of `--claudeai` for a Console account. When neither the persona nor the top level sets one, Claude's own default applies.
|
|
183
|
+
Use `--console` instead of `--claudeai` for a Console account. When neither the persona nor the top level sets one, Claude's own default applies. A directory that doesn't exist yet is fine when its parent exists.
|
|
189
184
|
|
|
190
|
-
- A
|
|
191
|
-
- A persona whose `claude_config_dir` can't be resolved to a real path (for example, a symlink on its path that points to nothing) is held: it closes or never opens its Slack connection and receives nothing, and `server.log` shows `persona-config-dir-unresolvable`. It reconnects once the directory resolves, with no restart.
|
|
185
|
+
- A persona whose `claude_config_dir` can't be resolved to a real path (for example, a symlink on its path that points to nothing) is held, receiving nothing, until the directory resolves, with no restart; `server.log` shows `persona-config-dir-unresolvable`.
|
|
192
186
|
- A confirmed change to a persona's effective `claude_config_dir` makes it start a new conversation on the same instance at its next launch. Nothing is deleted: the old conversation stays in the old directory, with prior sessions retained in the same AD life.
|
|
193
187
|
|
|
188
|
+
**`model`** and **`effort`** are the model and effort level the persona's instance starts with, overriding the defaults Claude Code would otherwise choose, an organisation seat's included. A key the persona doesn't set is not passed, so Claude Code chooses it as it does today.
|
|
189
|
+
|
|
190
|
+
- `model` is any non-empty name with no control characters, compared exactly as written, so an alias and its full ID are different values. A name Claude Code doesn't know is accepted and the persona launches, but its requests fail until the name is corrected.
|
|
191
|
+
- `effort` is one of the five levels, lower-case. An invalid value of either key is rejected naming the persona and the key (see [Load-time rules](#load-time-rules)).
|
|
192
|
+
- After a confirmed change, the persona's next launch uses the new values. It starts a new conversation on the same instance unless the values then match the ones its conversation started with; nothing is deleted. Each resume keeps the values its conversation started with.
|
|
193
|
+
|
|
194
194
|
### Slack reply reminders
|
|
195
195
|
|
|
196
196
|
**`stop_hook_bootstrap`** turns [Slack reply reminders](bots.md#slack-reply-reminders) on (`true`, the default) or off. This compatibility setting can be set on a persona or at the top level; a persona's own boolean overrides the default. A confirmed change applies immediately to that persona, including when it shares its `claude_config_dir` with others. See [Personas that answer without `reply`](bots.md#personas-that-answer-without-reply--opt-them-out) for when to turn it off.
|
package/docs/migration.md
CHANGED
|
@@ -295,7 +295,8 @@ Earlier releases ignore this release’s queue and can resume a bot without thei
|
|
|
295
295
|
1. **Stop sending and empty the queues.** Wait until `server.log` shows every received item delivered. Otherwise explicitly accept that undelivered messages are lost when using the earlier release.
|
|
296
296
|
2. **Disable resume on the current release.** Set `resume_enabled` to `false` in `config.json`, [confirm the pending change](reload.md#confirming-a-change), and verify `config.json.last-applied` holds `resume_enabled: false`. Keep the current server running until this is applied.
|
|
297
297
|
If the target release does not support these settings, first give every persona a valid declarative section (`permission_prompts`, and `channels` unless `dm.enabled` is `true`; see [Load-time rules](configuration.md#load-time-rules)), remove `allow_invited_channels` and every persona’s `invited`, confirm, and ensure the last-applied record holds neither.
|
|
298
|
-
If the target release does not support `queued_ack_reaction`, remove it from `config.json`, confirm while the current server is running, and verify it is absent from `config.json.last-applied`.
|
|
298
|
+
If the target release does not support `queued_ack_reaction`, remove it from `config.json`, confirm while the current server is running, and verify it is absent from `config.json.last-applied`.
|
|
299
|
+
If the target release does not support the persona keys `model` and `effort`, remove them from every persona that sets them, confirm while the current server is running, and verify `config.json.last-applied` holds neither. These precautions do not replace any phase.
|
|
299
300
|
3. **Stop with the bots, then install and pin the earlier build.** Run `claude-slack-channel-bots stop --stop-bots` and verify it succeeds, install the earlier build (for example `bun install -g claude-slack-channel-bots@<version>`), then restore its required Claude Code version pin before starting it.
|
|
300
301
|
4. **Start the earlier release fresh with channels, then restore resume.** Run `claude-slack-channel-bots start`; verify every bot starts a new conversation with that release’s channels launch and no configuration load error. Only then set `resume_enabled` back to its previous value and confirm the change. The earlier build ignores `channel-delivery.json`; leave it in place.
|
|
301
302
|
|
package/docs/reload.md
CHANGED
|
@@ -91,6 +91,7 @@ Paths in the preview are absolute: a `~` in `config.json` is shown expanded. In
|
|
|
91
91
|
| `credentials file "<path>" changed, but it cannot be used (<cause>): …` | The new content is missing, unreadable or invalid, for example `credentials file does not exist`. A persona that is up keeps its current connection, and a persona still retrying keeps retrying with its current content; confirming logs `persona-credentials-change-failed` in `server.log`, and the change stays pending. A persona down because of its credentials stays down, and confirming logs its usual credentials line and leaves nothing pending. Every start reads the file as it stands, so after a restart the persona is down until the file is fixed and the change confirmed. |
|
|
92
92
|
| `claude_config_dir changed: …, which starts fresh (the conversation is not resumed)` | The persona's next launch uses the new config directory and starts a new conversation. The running instance is kept until then. |
|
|
93
93
|
| `…; but at that launch it cannot come up: claude_config_dir cannot be resolved to a real path (<errno>)` | A warning after a changed `claude_config_dir`: the new directory can't be resolved, for example a symlink on its path points to nothing. On a changed top-level `claude_config_dir`, the warning names the inheriting personas it stops; on a `DESTRUCTIVE:` line that also changes the directory, it reads `but it cannot come up`. Confirming still applies the change: at the persona's next launch (at once, for a destructive change) it is held, its Slack connection closed, and it comes up once the directory resolves (`persona-config-dir-unresolvable` in `server.log`). Fix the directory, or the setting, before confirming. A directory that doesn't exist yet but whose parent does is fine. |
|
|
94
|
+
| `model changed: …, which starts fresh (the conversation is not resumed) unless its model and effort then match the ones its conversation started with, …` (or `effort changed` or `model, effort changed`) | The persona's own `model` or `effort` changed. Its next launch uses the new values and starts a new conversation unless they then match the ones its conversation started with; the running instance is kept until then. The line names the changed keys, never a value. A persona whose `claude_config_dir` changes too gets both effects on its one line, each with its own clause, joined by `; `. |
|
|
94
95
|
| `… could not be checked.` | The server couldn't check whether an added persona can come up, whether a changed `claude_config_dir` can be resolved, or whether a persona is down because of its credentials. `server.log` has a `reload: cannot check …` line; report it as a bug. |
|
|
95
96
|
| `… changed in the declarative section: recorded, with no effect until allow_invited_channels selects declarative mode.` or `… changed in the fungible section: recorded, with no effect until allow_invited_channels selects fungible mode.` | A recorded change: the persona's section not in force changed (`channels` or `permission_prompts` in fungible mode, `invited` in declarative mode). The line names the persona and the changed fields. The change is recorded and has no effect until the switch selects that section. These lines come after every persona line and before the server-wide setting lines. A change made only of recorded changes previews as `no effective change`, followed by its recorded lines. |
|
|
96
97
|
| `server-wide setting allow_invited_channels changed: turns <mode> mode on, applied in place at once, from the next event, tool call, prompt and notice, for …` | The switch. The line names the mode it turns on (`fungible` or `declarative`) and every persona present in both configurations, or ends `; no persona is affected.` when there is none. It applies at once, with no restart, and every session is kept (see [Channel modes](channel-modes.md#channel-modes)). The inherited `stop_hook_bootstrap` default also applies immediately to reminder checks. |
|
|
@@ -136,6 +137,7 @@ Each row is one kind of change: what happens once you confirm it, and what happe
|
|
|
136
137
|
| `channels`, `delivery` or `permission_prompts` changes (in declarative mode), `invited.permission_prompts` changes (in fungible mode), or `dm.enabled` or `dm.contact` changes | Applied in place, immediately: from the next event or post. A changed DM contact is used for the next prompt. | Kept |
|
|
137
138
|
| A credentials file's content changes (same path), such as a rotated token | Only that persona reconnects: the new connection opens, then the old one closes. If the new file can't be used or Slack refuses it, the old connection keeps running, `server.log` shows `persona-credentials-change-failed`, and the change stays pending. If Slack can't be reached, the old connection stays in use while the new one retries. A persona that is retrying (Slack unreachable, its working directory unusable, or held for its `claude_config_dir` by `persona-config-dir-unresolvable`) has no connection and retries with the new content. A persona down because of its credentials comes up on the confirmed change, with no restart; if its new file can't be used, it stays down with its usual line, such as `persona-credentials-invalid`, and nothing stays pending. | Kept |
|
|
138
139
|
| `claude_config_dir` changes (the persona's own or inherited) | Recorded. The persona's next launch uses it and starts a new conversation on the same instance when the directory changed: the conversation is not resumed, nothing is deleted, and the old transcript stays in the old directory. | Kept until the next launch |
|
|
140
|
+
| `model` or `effort` changes (the persona's own: added, removed or written differently, such as an alias for a full model ID) | Recorded. The persona's next launch uses the new values and starts a new conversation on the same instance unless the values then match the ones its conversation started with: nothing is deleted, and the old conversation is kept. | Kept until the next launch |
|
|
139
141
|
| `stop_hook_bootstrap` changes (the persona's own or inherited) | Applied in place, immediately: subsequent reminder checks use the confirmed value. | Kept |
|
|
140
142
|
| A persona is removed | Torn down: its session is ended, with the result checked and retried, and its agent-director row is kept as the old life until agent-director's `expire` removes it, never resumed. Its posted permission prompts stay in Slack, and clicking one has no effect. Until its old instance has ended, no persona is brought up in its working directory (see "A persona waits on an old instance in its working directory" in [Troubleshooting](troubleshooting.md#troubleshooting)). Previewed `DESTRUCTIVE:`. | Retired: never resumed |
|
|
141
143
|
| A persona's `credentials_file` path or `working_directory` changes | Torn down as a removal is (its row kept, never resumed), then brought up fresh from its new entry on the same instance, with no restart: a new conversation, and the new credentials file is read. The new conversation starts only once the old instance has ended, and any other persona in the old working directory waits until then too (see "A persona waits on an old instance in its working directory" in [Troubleshooting](troubleshooting.md#troubleshooting)). Previewed `DESTRUCTIVE:`. See [Destructive changes](#destructive-changes). | Retired: never resumed |
|
|
@@ -152,9 +154,9 @@ Each row is one kind of change: what happens once you confirm it, and what happe
|
|
|
152
154
|
|
|
153
155
|
## When next-launch and server-wide changes take effect
|
|
154
156
|
|
|
155
|
-
A persona's next launch happens when the bot dies (a crash or a failed health check), at `clean_restart --server`, or `clean_restart <bot>…` naming it, at `stop --stop-bots` then `start`, or after a host reboot. A plain `stop` and `start` reconnects to the running instance, which is not a launch. Server-wide settings take effect at the next server start, except `allow_invited_channels` and the inherited `stop_hook_bootstrap` reminder setting: both take effect when the change is confirmed, and need no `clean_restart`. The CLI takes `stop_timeout`, `exit_timeout` and `agent_director_call_timeout_ms` from the record at once; see the table.
|
|
157
|
+
The next-launch settings, `claude_config_dir`, `model` and `effort`, take effect at the persona's next launch. A persona's next launch happens when the bot dies (a crash or a failed health check), at `clean_restart --server`, or `clean_restart <bot>…` naming it, at `stop --stop-bots` then `start`, or after a host reboot. A plain `stop` and `start` reconnects to the running instance, which is not a launch. Server-wide settings take effect at the next server start, except `allow_invited_channels` and the inherited `stop_hook_bootstrap` reminder setting: both take effect when the change is confirmed, and need no `clean_restart`. The CLI takes `stop_timeout`, `exit_timeout` and `agent_director_call_timeout_ms` from the record at once; see the table.
|
|
156
158
|
|
|
157
|
-
To make them take effect sooner, wait for the `reload-applied` line, then run `claude-slack-channel-bots clean_restart --server` from a terminal. It reads the bot list from the record before it stops the server, so if you run it earlier it can work from the old record. The server comes back on the record, and every bot is relaunched. Conversations resume, except that a changed `claude_config_dir` starts that persona fresh. For a next-launch change to some personas only, `claude-slack-channel-bots clean_restart <bot> [<bot>…]` relaunches just those bots, and the server keeps running.
|
|
159
|
+
To make them take effect sooner, wait for the `reload-applied` line, then run `claude-slack-channel-bots clean_restart --server` from a terminal. It reads the bot list from the record before it stops the server, so if you run it earlier it can work from the old record. The server comes back on the record, and every bot is relaunched. Conversations resume, except that a changed `claude_config_dir`, or a `model` or `effort` that differs from what its conversation started with, starts that persona fresh. For a next-launch change to some personas only, `claude-slack-channel-bots clean_restart <bot> [<bot>…]` relaunches just those bots, and the server keeps running.
|
|
158
160
|
|
|
159
161
|
## Destructive changes
|
|
160
162
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -23,6 +23,9 @@ The first start recorded the bad setting, such as `port` or `bind`, before it fa
|
|
|
23
23
|
**A persona doesn't come up or doesn't answer**
|
|
24
24
|
The persona is silent. Accepted messages remain queued while its instance is unavailable. Find its key on its `persona-start` line in `server.log` and read its lines: each names a class (such as `persona-credentials-invalid` or `persona-directory-missing`) and the file or directory to fix. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-doesnt-come-up-or-doesnt-answer).
|
|
25
25
|
|
|
26
|
+
**A persona seems silent after its `model` was set or changed**
|
|
27
|
+
Correct the name (or remove `model`) in `config.json`, confirm the change, then run `claude-slack-channel-bots clean_restart <bot>` to start the persona fresh. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-seems-silent-after-its-model-was-set-or-changed).
|
|
28
|
+
|
|
26
29
|
**Acknowledgement reaction missing or stuck**
|
|
27
30
|
Check that both configured emoji exist in the workspace and the confirmed settings took effect at server start; use `message-delivered` to confirm delivery when Slack reaction calls fail. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-acknowledgement-reaction-missing-or-stuck).
|
|
28
31
|
|
|
@@ -74,6 +77,9 @@ Each start logs one `[slack] startupSessionManager: complete — …` line count
|
|
|
74
77
|
**Delivery notices or delivery diagnostic lines**
|
|
75
78
|
For *Message not saved*, check the server disk; for *Delivery stuck* or *Message not taken in*, follow the notice's remedy; for *Messages waiting*, resolve the persona's state; for *Queue record unreadable*, preserve its file and ask the senders to resend. Accepted messages stay queued in order, including when [`message-partial-receipt`](../skills/debug-slack-channel-bots/SKILL.md#message-partial-receipt) reports cut-off text. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-delivery-notices-or-delivery-diagnostic-lines).
|
|
76
79
|
|
|
80
|
+
**A persona on a Mac keeps retyping a long message**
|
|
81
|
+
On a macOS host CSCB types a long message in parts of at most about 880 bytes each. A message queued before an upgrade can keep larger parts, and a restart does not split it again: CSCB keeps offering it until the bot takes it in, while later messages wait. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-on-a-mac-keeps-retyping-a-long-message).
|
|
82
|
+
|
|
77
83
|
**A persona posts a *Waiting on a prompt* or *Not connected* notice**
|
|
78
84
|
The session runs but is disconnected; accepted messages are kept until it reconnects. A human attaches with `tmux attach -t =slack_bot_<key>`, deals with anything on screen and, if it stays disconnected, types `/mcp reconnect slack-channel-router`, or restarts the server. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-a-waiting-on-a-prompt-or-not-connected-notice).
|
|
79
85
|
|
|
@@ -132,7 +138,7 @@ The installed agent-director is below the client's own minimum, so only the serv
|
|
|
132
138
|
Each `could not stop persona` line names a persona, its session and the class of the failure. Fix the cause and run the command again; rows are never deleted. If `clean_restart --server` says the server was not started, start it with `claude-slack-channel-bots start` once `agent-director version` answers. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-clean_restart-or-stop---stop-bots-exits-non-zero-with-could-not-stop).
|
|
133
139
|
|
|
134
140
|
**Bots come back with no memory of the prior conversation after a reboot**
|
|
135
|
-
A bot starts a new conversation when its session no longer matches the applied configuration, when it was retired by a confirmed change, once after an upgrade, or after 2 resumes in a row that failed. Search `server.log` for `sweeping row`, `pre-persona row`, `replacing the row`, `not resuming; replacing its row`, `key is retired` or `not resuming its transcript again` to see which. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-bots-come-back-with-no-memory-of-the-prior-conversation-after-a-reboot).
|
|
141
|
+
A bot starts a new conversation when its session no longer matches the applied configuration, when it was retired by a confirmed change, once after an upgrade, or after 2 resumes in a row that failed. A persona whose `model` or `effort` differs from what its conversation started with starts a new conversation on the same instance at its next launch; this includes setting either key over a conversation from an earlier release. Nothing is deleted. Search `server.log` for `sweeping row`, `pre-persona row`, `replacing the row`, `not resuming; replacing its row`, `key is retired`, `from what its conversation started with` or `not resuming its transcript again` to see which. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-bots-come-back-with-no-memory-of-the-prior-conversation-after-a-reboot).
|
|
136
142
|
|
|
137
143
|
**Session crashes on resume with "sandbox required but unavailable"**
|
|
138
144
|
A regression in some Claude Code releases (for example v2.1.120). Set `resume_enabled: false` in `config.json`, confirm the change and restart the server; bots then always start a new conversation. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-session-crashes-on-resume-with-sandbox-required-but-unavailable).
|
package/package.json
CHANGED
|
@@ -498,7 +498,7 @@ no recovery outcome. See [Message saved after retry](#message-saved-after-retry)
|
|
|
498
498
|
- **Line:** `[slack] message-retyped: persona="<name>" (key=<key>) source=<source> chat_id="<chat>" ts="<ts>" sender_id="<sender>" retype_count=<n>`
|
|
499
499
|
- **Meaning:** CSCB offered the queued message again; it may reach the bot more than once.
|
|
500
500
|
- **Cause:** The transcript has not confirmed the earlier copy.
|
|
501
|
-
- **Fix:** Check **Message not taken in** and **Delivery stuck** below if it persists. Retries continue indefinitely; later messages wait behind it.
|
|
501
|
+
- **Fix:** Check **Message not taken in** and **Delivery stuck** below if it persists. Retries continue indefinitely; later messages wait behind it. For a long message on a Mac host, see [A persona on a Mac keeps retyping a long message](#symptom-a-persona-on-a-mac-keeps-retyping-a-long-message).
|
|
502
502
|
|
|
503
503
|
### `message-not-taken-in`
|
|
504
504
|
|
|
@@ -3233,7 +3233,8 @@ destination:
|
|
|
3233
3233
|
for a human only.
|
|
3234
3234
|
It also comes when the server replaces a persona's old instance that is
|
|
3235
3235
|
still running (a launch that can't keep its row: `resume_enabled` is
|
|
3236
|
-
`false`,
|
|
3236
|
+
`false`, the working directory or config directory changed, or its
|
|
3237
|
+
`model` or `effort` differs from what its conversation started with; or a
|
|
3237
3238
|
launch that found the persona's session gone while agent-director still
|
|
3238
3239
|
read its row as running): its
|
|
3239
3240
|
kills returned, but agent-director still listed the old instance's row as
|
|
@@ -4750,8 +4751,9 @@ agent-director get --instance-id <id>
|
|
|
4750
4751
|
| `[slack] spawnForPersona: "<name>" (key=<key>) row cwd=<dir> differs from working_directory=<dir> (state=<state>) — replacing the row by a reuse spawn of the same id; nothing is deleted` | At a start or relaunch the persona's agent-director row was in another directory than its `working_directory`, so it can't be kept: the persona starts a new conversation on the same instance, and the Claude row retains its AD life and prior sessions. The `replacing the row of` line below follows. Nothing. |
|
|
4751
4752
|
| `[slack] spawnForPersona: resume_enabled=false for "<name>" (key=<key>) — not resuming; replacing its row by a reuse spawn of the same id` | `resume_enabled` is `false`, so the persona starts a new conversation on the same instance instead of resuming; the Claude row retains its AD life and prior sessions. The `replacing the row of` line below follows. Nothing. |
|
|
4752
4753
|
| `[slack] spawnForPersona: "<name>" (key=<key>) config_dir label <missing\|changed> (<label absent\|was=<old>>, now=<new> for claude_config_dir=<dir>) — not resuming; replacing its row by a reuse spawn of the same id` | The row was launched with another `claude_config_dir` (or records none), and a resume would keep that old directory, so the persona starts a new conversation on the same instance. The old conversation stays in the old directory, with prior sessions retained in the same AD life; nothing is deleted. `<dir>` is `<default>` when none is set. The `replacing the row of` line below follows. Nothing, if the config directory change was meant. |
|
|
4754
|
+
| `[slack] spawnForPersona: <ref> <keys> <differs\|differ> from what its conversation started with — not resuming; a fresh conversation starts and the old one is kept` | The persona starts a new conversation on the same instance because its `model` or `effort` differs from what its conversation started with; the old one is kept and nothing is deleted. `<ref>` is `"<name>" (key=<key>)`; `<keys>` is `model`, `effort` or `model and effort`, with `differs` for one key and `differ` for two. Keys only, never the value. Cause: a confirmed addition, removal or change (another spelling included), or a conversation started by an earlier release for a persona that sets either key; then the line names the keys it sets. Logged once each time a launch decides to start fresh for this reason, before the replacement's own lines (`replacing the row of` at the ladder); a retry or collision re-run logs it again. A successful fresh launch counts as `fresh-spawned` in the start summary, with no `startup-errors.log` entry, notice or Slack post. No such line for an empty session or any earlier reason to start fresh (a working-directory or config-directory mismatch, `resume_enabled` false, a retired key, the one fresh start after an upgrade, or 2 resumes in a row that ended before the session started). Fix: nothing if meant; otherwise set the values back, confirm and relaunch. Once the fresh conversation has started, reverting also starts a new one; it does not restore the old conversation. |
|
|
4753
4755
|
| `[slack] pending-row: "<name>" (key=<key>)'s pending row is not covered (<reason>: <why>) — it goes through the live-row sequence, the conversation not kept (alert context recovery); no approver, nothing typed` | A row that is still starting (`pending`) is not the persona's current launch, so the server replaces it as an old instance, never typing into it: it ends it with a checked kill, then, while it still reads `pending`, waits until agent-director's grace period (`pending_grace_seconds`) has passed since that launch's start, confirms it has ended (one more checked kill if needed) and brings the persona up on a new conversation on the same instance (the `live-row-sequence:` lines below). `<reason>`: `cwd-mismatch` (`its cwd differs from the persona's working directory`: it was launched in another directory), `config-dir-mismatch` (`its config_dir label is missing or differs`: it was launched with another `claude_config_dir`, or records none) or `retired-key-old-life` (`its key is retired and no new life has begun yet, so the row is the old life`: see [Added again or renamed back](#added-again-or-renamed-back)). During a launch the `replacing the row of … a live row goes through the live-row sequence first …` line follows; after a restart's or a retry's check, the `pending-row: … live-row sequence start answered <answer>` line. Nothing. |
|
|
4754
|
-
| `[slack] spawnForPersona: replacing the row of "<name>" (key=<key>) (<why>; last read <state>): a reuse spawn of the same id; nothing is deleted` | The row was finished (`<state>` `ended`, `missing` or `no-row`), so the persona is brought up on the same instance id at once (the `reuseSpawnForPersona:` lines follow). `<why>` is `resume_enabled is false`, `its cwd differs from the persona's working directory
|
|
4756
|
+
| `[slack] spawnForPersona: replacing the row of "<name>" (key=<key>) (<why>; last read <state>): a reuse spawn of the same id; nothing is deleted` | The row was finished (`<state>` `ended`, `missing` or `no-row`), so the persona is brought up on the same instance id at once (the `reuseSpawnForPersona:` lines follow). `<why>` is `resume_enabled is false`, `its cwd differs from the persona's working directory`, `its config_dir label is missing or differs` or `its model or effort differs from what its conversation started with`. Nothing. |
|
|
4755
4757
|
| `[slack] spawnForPersona: replacing the row of "<name>" (key=<key>) (<why>; last read <state>): a live row goes through the live-row sequence first, which ends in a reuse spawn of the same id; start answered <answer> — answering <sequence-waiting\|held>; no other call, nothing counted` | The row was still running (or starting, `pending`, or could not be read), so it is never launched over: the server began replacing the old instance step by step (the `live-row-sequence:` lines below, alert context `recovery`), ending in a new conversation on the same instance. For a persona whose key is retired (see [Added again or renamed back](#added-again-or-renamed-back)), the line reads `… the live-row sequence first (its key is retired), which ends …`, `<why>` starts `its key is retired and`, and it ends `)`. This launch makes no other call and counts nothing. `<answer>` is `started`, or `already-running`, `closed` (the server is stopping) or `not-installed`, each with its own line. `start answered held — answering held`: the persona is held because agent-director rejected its launch, so no replacement started (see [A persona posts a Cannot launch notice](#a-persona-posts-a-cannot-launch-notice)). Nothing. A *Kill failed* notice can follow if the old instance can't be stopped (see [A persona posts a Kill failed or Process outlived kill notice](#a-persona-posts-a-kill-failed-or-process-outlived-kill-notice)). |
|
|
4756
4758
|
| `[slack] spawnForPersona: the reuse spawn of "<name>" (key=<key>) collided with a live row — nothing launched; re-running get-then-act once` | Right after the reuse spawn's collision line (below): the instance id's row was live again, so nothing was launched; the launch reads the row once more and decides again. After a resume that found no conversation the line (and the one below) reads `the reuse spawn after its resume's no-transcript answer of "<name>" (key=<key>)`. Nothing. |
|
|
4757
4759
|
| `[slack] spawnForPersona: the reuse spawn of "<name>" (key=<key>) collided with a live row again, in the re-run of get-then-act — nothing launched; answering retrying, no spawn-failure notice, nothing counted; the retry timer is armed (cause=collision)` | It collided a second time. No notice and nothing counted; the persona's retries start (`[slack] unavailable-retry: persona=<key> armed (collision) — first retry in 30 s`, unless they were already waiting), and the next try reads the row again. `could not be armed` in place of `is armed` is seen only in tests and development runs. Nothing. |
|
|
@@ -4761,8 +4763,8 @@ agent-director get --instance-id <id>
|
|
|
4761
4763
|
| `[slack] spawnForPersona: "<name>" (key=<key>)'s dead evidence (<evidence>) is void for the rest of this attempt — <what voided it>; a later live reading of the row is a launch that evidence never saw, handled as on a path with no dead evidence` | The launch held proof that the persona's old session was gone (no session or screen found), then read the row finished or gone, or was told something that means the row's life has changed. That proof covered the session it was about, which has finished, so for the rest of this launch it counts for nothing: if the row then reads running, that is someone else's launch, and the server kills nothing of it (a lost race; the persona is tried again later). `<what voided it>` names it: `the collision get read the row <state>` or `… found no row (ErrSpawnNotFound)`, `the prompt-row ladder action's re-read after its find-missing run read the row <state>`, `the get of its own row after the path's find-missing run read the row <state>`, `the read behind its dead-session cause (cause=<cause>) …`, `the path's last read …`, `resume answered ErrSpawnNotFound: the row is gone`, `the reconnect's send-keys answered ErrSpawnNotInteractive (row-not-interactive): the row is finished, or pending under a launch the verdict never saw`, `the reuse spawn collided, and the re-run of get-then-act it gives carries no dead evidence`, or `the retired key's first launch is a reuse spawn that starts a new life, which the evidence never saw, so the get-then-act its collision enters carries no dead evidence`. One line per voided piece. Nothing to do. If a lost race follows and repeats, check for a `resume` or spawn of `cscb_<key>` made by a human or another orchestrator; the persona is judged afresh at each try. |
|
|
4762
4764
|
| `[slack] spawnForPersona: the joining call for "<name>" (key=<key>) carried an escalate-dead verdict (<evidence>) — dropped; it gets the launch in flight's result` | A restart relaunch found a launch of the persona already running (after `launch already in flight … joining it`): it takes that launch's result, and what its health check escalated with is not used. Nothing. |
|
|
4763
4765
|
| `[slack] spawnForPersona: attempting resume for "<name>" (key=<key>)`, then `[slack] spawnForPersona: resumed "<name>" (key=<key>)` | A launch resumes the persona's conversation, and the resume call succeeded; a `pre_trust` line follows. It counts as a success only once the persona's session reports in: a resume whose row ends first gets an `ended before reporting in` line, and 2 in a row are followed by a fresh launch (see [A persona's resumed launch ends before it reports in](#a-personas-resumed-launch-ends-before-it-reports-in)). When agent-director refuses the resume over a tmux session conflict, the persona is held instead: see [A persona posts a Held: tmux session conflict notice](#a-persona-posts-a-held-tmux-session-conflict-notice). Nothing. |
|
|
4764
|
-
| `[slack] spawnForPersona: <error> on resume for "<name>" (key=<key>) — re-read: <re-read>; <evidence> — <outcome>` | agent-director said the instance can't be resumed (`ErrSpawnNotResumable`): its row was not finished when the resume ran. That alone never stops or replaces anything: the server read the row once more (`<re-read>`: `state=<state>`, `state=unreadable` when the state couldn't be read, `state=no-row`, `the read failed` or `the read latched the persona`). `<evidence>` is what the launch held about the old session, worded as in the `dead session for` line above. `<outcome>` says what followed: `the persona is latched: …` (follow the *Held:* entry the persona's notice names); `a launch in progress, not a failure: …` (the row is still starting, so nothing is counted or posted; the next line, `no action — state=pending` or a `not covered` line, says whether it is left alone or replaced); `the path holds dead evidence and the row is live: the live-row sequence, with the conversation kept (alert context recovery), ending in resume; start answered <answer> — …` (the session was proven gone but its row still reads one of the running states this server knows, other than `pending`, so the server ends the old instance step by step
|
|
4765
|
-
| `[slack] spawnForPersona: replacing the row of "<name>" (key=<key>) (<why>; last read <state>; <evidence>) — the path holds no dead evidence, so the row is read again first: re-read: <re-read> — <outcome>` | The persona's row had to be replaced (`<why>`: `resume_enabled is false
|
|
4766
|
+
| `[slack] spawnForPersona: <error> on resume for "<name>" (key=<key>) — re-read: <re-read>; <evidence> — <outcome>` | agent-director said the instance can't be resumed (`ErrSpawnNotResumable`): its row was not finished when the resume ran. That alone never stops or replaces anything: the server read the row once more (`<re-read>`: `state=<state>`, `state=unreadable` when the state couldn't be read, `state=no-row`, `the read failed` or `the read latched the persona`). `<evidence>` is what the launch held about the old session, worded as in the `dead session for` line above. `<outcome>` says what followed: `the persona is latched: …` (follow the *Held:* entry the persona's notice names); `a launch in progress, not a failure: …` (the row is still starting, so nothing is counted or posted; the next line, `no action — state=pending` or a `not covered` line, says whether it is left alone or replaced); `the path holds dead evidence and the row is live: the live-row sequence, with the conversation kept (alert context recovery), ending in resume; start answered <answer> — …` (the session was proven gone but its row still reads one of the running states this server knows, other than `pending`, so the server ends the old instance step by step, then decides whether to resume under the current [next-launch settings](#next-launch-and-server-wide-settings); the `live-row-sequence:` lines follow); or `a lost race: nothing killed, deleted or launched; … the retry timer is armed (cause=spawn-not-resumable-lost-race)` (no proof the old session is gone, or proof a read of the row as finished or gone has voided (see the `dead evidence (…) is void` line), the row reads a state this server version doesn't know or couldn't read (whatever the proof), or the row finished or vanished meanwhile: nothing is stopped or launched, no notice is posted and nothing is counted; the persona's retries start, `armed (spawn-not-resumable-lost-race)`, and the next try reads the row again). Nothing; if the lost race repeats for a long time, see the persona's row (see [Listing instances](#listing-instances)). |
|
|
4767
|
+
| `[slack] spawnForPersona: replacing the row of "<name>" (key=<key>) (<why>; last read <state>; <evidence>) — the path holds no dead evidence, so the row is read again first: re-read: <re-read> — <outcome>` | The persona's row had to be replaced (`<why>`: `resume_enabled is false`, `its config_dir label is missing or differs` or `its model or effort differs from what its conversation started with`) and was last read running, but nothing proved its old session gone (for example, agent-director refused the reconnect), so the server read the row again instead of acting on that read. `<outcome>`: `the row is finished: …` (a new conversation on the same instance follows, with the `replacing the row of` line above); `a launch in progress: …` (the row is still starting; the next line says whether it is left alone or replaced); `a lost race: …` (the row still runs, or reads a state this server version doesn't know or couldn't read: nothing is stopped or launched, and the persona's retries start, `armed (spawn-not-resumable-lost-race)`); `the read was refused: …` (agent-director didn't answer; the persona's retries start); `the persona is latched: …` (follow the *Held:* entry). Nothing. |
|
|
4766
4768
|
| `[slack] spawnForPersona: ErrSpawnNotFound on resume for "<name>" (key=<key>) — fresh-spawn: one plain spawn of the same id, which makes agent-director's pre-spawn scan; no spawn-failure notice for the ErrSpawnNotFound`, then `[slack] spawnForPersona: fresh-spawned (after ErrSpawnNotFound on resume) for "<name>" (key=<key>)` | agent-director had no row for the persona when it was resumed (it was removed or expired meanwhile), so the persona is launched fresh on the same instance id. No notice is posted for the missing row. If that launch meets a tmux session conflict, the persona is held: see [A persona posts a Held: tmux session conflict notice](#a-persona-posts-a-held-tmux-session-conflict-notice). Nothing. The live-row sequence's resume logs the same line with `[slack] live-row-sequence:` in place of `[slack] spawnForPersona:`. |
|
|
4767
4769
|
| `[slack] pending-row: "<name>" (key=<key>)'s live-row sequence start answered <answer>; no other call, nothing counted` | After a restart's or a retry's check found an old instance still starting (the `is not covered` line above), the server began replacing it (`started`), or it was already being replaced (`already-running`); `held`, `closed` (the server is stopping) and `not-installed` start nothing. Nothing counts toward the restart limit. Nothing. |
|
|
4768
4770
|
| `[slack] pending-row: "<name>" (key=<key>)'s pending row is undecided (<reason>: <why>) — no approver, no sequence; its retry timer is armed in pending-only mode, and the next read decides again` | The instance is still starting, but the server can't tell yet whether it is the persona's own launch: `cwd-unresolved` (`its working_directory does not resolve to a real path now`) or `config-dir-unresolved` (`its claude_config_dir cannot be resolved now`). It is left alone and read again at the next retry. `its retry timer could not be armed` in its place: the persona is held. Check that the working directory, or the `claude_config_dir` drive or symlink, is there (see `persona-directory-*` and `persona-config-dir-unresolvable` in [Persona diagnostic classes](#persona-diagnostic-classes)). |
|
|
@@ -4779,7 +4781,7 @@ agent-director get --instance-id <id>
|
|
|
4779
4781
|
| `[slack] spawnForPersona: <error> on the resume of "<name>" (key=<key>) — its row is live, so nothing was launched; no spawn-failure notice, nothing counted; answering retrying, the retry timer is armed (cause=collision), and that retry's run of the restart path's decision is the get-then-act` | Not seen in practice: agent-director answered a resume of the persona with an instance-id collision (`ErrInstanceIdCollision`), which it does not do today. Nothing was launched, posted or counted; the persona's retry reads the row and acts on it. `could not be armed` in place of `is armed`: the line came from outside a launch attempt, and the persona's next retry or restart decides. Nothing. |
|
|
4780
4782
|
| `[slack] reuseSpawnForPersona: reuse spawn failed for "<name>" (key=<key>): <classification> (after one immediate agent-director version re-check: stop); no other launch` | That spawn answered `ErrInvalidFlags` and the version re-check found a binary the server can't run with: the server stops. Nothing is posted or counted. See [Found while the server was running](#found-while-the-server-was-running). |
|
|
4781
4783
|
| `[slack] Launch failed for persona=<key> — counted` | The live-row sequence's own launch (its `resume` or reuse spawn) failed in a way that counts (`ErrTmuxSessionCreate`, or a working directory that can't be found or isn't a directory), so one failure was counted toward the restart limit. At the limit the `Cap reached` line and notice follow. Follow the launch's own failure line just before it. A `resume` whose session ended before it started is counted with its own line, `Resumed launch failed for persona=<key>` (see [A persona's resumed launch ends before it reports in](#a-personas-resumed-launch-ends-before-it-reports-in)). |
|
|
4782
|
-
| `[slack] live-row-sequence: <ref>: started at step <n> for <id> (last read <state>; <form>; alert context <context>)` | The server began replacing the persona's old instance `<id>`, whose row it last read as `<state>`, step by step (the `live-row-sequence` rows below; `<ref>` names the persona, as `persona=<key>` or `"<name>" (key=<key>)`). `<n>` is `1` (it ends the old instance first) or `2` (that was done already). `<form>` is `ends in a launch` or `no launch (old-life form)`. `<context>` is what a *Kill failed* notice from it would name: `recovery` when a launch started it: because it found the persona's row still running but could not keep it (`resume_enabled` is `false`, the row's working directory or config directory differs, or the persona's key is retired and the row is its old life; the `replacing the row of … a live row goes through the live-row sequence first` line comes just before), or because the persona's session was proven gone while agent-director still read its row as running and the resume was refused (the `spawnForPersona: … on resume for … the live-row sequence, with the conversation kept` line comes just before; this replacement
|
|
4784
|
+
| `[slack] live-row-sequence: <ref>: started at step <n> for <id> (last read <state>; <form>; alert context <context>)` | The server began replacing the persona's old instance `<id>`, whose row it last read as `<state>`, step by step (the `live-row-sequence` rows below; `<ref>` names the persona, as `persona=<key>` or `"<name>" (key=<key>)`). `<n>` is `1` (it ends the old instance first) or `2` (that was done already). `<form>` is `ends in a launch` or `no launch (old-life form)`. `<context>` is what a *Kill failed* notice from it would name: `recovery` when a launch started it: because it found the persona's row still running but could not keep it (`resume_enabled` is `false`, the row's working directory or config directory differs, its `model` or `effort` differs from what its conversation started with, or the persona's key is retired and the row is its old life; the `replacing the row of … a live row goes through the live-row sequence first` line comes just before), or because the persona's session was proven gone while agent-director still read its row as running and the resume was refused (the `spawnForPersona: … on resume for … the live-row sequence, with the conversation kept` line comes just before; this replacement keeps the persona's conversation unless its current [next-launch settings](#next-launch-and-server-wide-settings) require a fresh one); or `stuck-launch abort` at step `2`, when the server ended its own stuck launch and relaunches it (the `stuck-launch abort: the live-row sequence started at its second step` line comes just after; see [A persona posts a Launch stuck or Session not starting notice](#a-persona-posts-a-launch-stuck-or-session-not-starting-notice)). Nothing to do: the lines after it show each step, and one end line closes it. |
|
|
4783
4785
|
| `[slack] live-row-sequence: <ref>: step <1\|4> kill: <outcome> after <t> kill(s) and <r> read(s) (end=<end>; alert=<none\|survivor\|ordinary>)` | The replacement's kill of the old instance, after its tries (see [A kill that is tried again](#a-kill-that-is-tried-again)). A success goes on to the next step; any other `<outcome>` ends the replacement (the end line says how). `alert=` says which notice the tries call for. Nothing for a success. Otherwise follow the end line and any notice. |
|
|
4784
4786
|
| `[slack] live-row-sequence: <ref>: step <1\|4>: no kill — the row was last read pending and agent-director refuses its config file (ad-config-malformed)` | The old instance was still starting and agent-director refuses its config file, so the server does not end it: the replacement ends with no kill and the persona is retried later. See **agent-director refuses its config file** under [agent-director refuses a persona](#agent-director-refuses-a-persona-it-is-retried-on-its-own); the retries go on once agent-director reads its config again. |
|
|
4785
4787
|
| `[slack] live-row-sequence: <ref>: step <2\|3\|4> get: <answer>` | One read of the old instance's row. `<answer>`: `state=<state>`; `no row (ErrSpawnNotFound)`; `failed: <failure>` (the read failed: the replacement ends and the persona is retried later; `<failure>` is `class=<class> name=<reportedName> message="…"`, as on the kill line, when agent-director's answer carries a name or a message (classes `UNCLASSIFIED`, `UNUSABLE_NAME` and `CONFIG`; `name=` or `message=` is left out when absent), else agent-director's error name and its message); or `the read latched the persona` (the read held the persona: its *Held:* notice follows and the replacement stops). After step 3 or 4, `state=ended`, `state=missing` or no row sends the sequence on to its launch (a row read, after the sequence's own kill, not by itself proof the old instance is gone). Nothing, unless it failed repeatedly (check that agent-director answers) or held the persona (follow its *Held:* entry). |
|
|
@@ -4792,7 +4794,7 @@ agent-director get --instance-id <id>
|
|
|
4792
4794
|
| `[slack] live-row-sequence: <ref>: step <3\|4> run <k> of 4: run refused — the sequence ends without its launch` | agent-director refused the check (it can't act right now, tmux isn't available, it refuses its config file, or an error the server can't classify). The replacement ends with nothing launched and the persona is retried later; the refusal's own line and any notice come with it. Follow that refusal under [agent-director refuses a persona](#agent-director-refuses-a-persona-it-is-retried-on-its-own). |
|
|
4793
4795
|
| `[slack] live-row-sequence: <ref>: step <3\|4> run <k> of 4: the persona is latched after the run` | The check's read held the persona (its *Held:* notice follows); the replacement stops. Follow the *Held:* entry the persona's notice names. |
|
|
4794
4796
|
| `[slack] live-row-sequence: <ref>: step <3\|4> run <k> of 4: run failed — it judged nothing; the step goes on` | The check failed in another way (its `findMissing sweep failed` line has the reason). It counts as no judgement; the `get` and the next check go on. Nothing, unless every check fails (then the end line reads `no run judged it`). |
|
|
4795
|
-
| `[slack] live-row-sequence: <ref>: step 6: <resume\|reuse> — <reason>` | The old instance is gone, and the server launches the persona again: `resume` keeps its conversation (`the row has a session id and the persona keeps its conversation`); `reuse` starts it fresh under the same instance id, with `<reason>` one of `the key is retired`, `the persona does not keep its conversation`, `there is no row`, `the row has no session id`, `the persona is not in the applied configuration`, `resume_enabled is false`, `the row's cwd differs from the persona's working directory
|
|
4797
|
+
| `[slack] live-row-sequence: <ref>: step 6: <resume\|reuse> — <reason>` | The old instance is gone, and the server launches the persona again: `resume` keeps its conversation (`the row has a session id and the persona keeps its conversation`); `reuse` starts it fresh under the same instance id, with `<reason>` one of `the key is retired`, `the persona does not keep its conversation`, `there is no row`, `the row has no session id`, `the persona is not in the applied configuration`, `resume_enabled is false`, `the row's cwd differs from the persona's working directory`, `the row's config_dir label is missing or differs` or `the row's model or effort differs from the persona's`. Nothing. |
|
|
4796
4798
|
| `[slack] live-row-sequence: <ref>: launched (<resume\|reuse>; result=<result>) — runs=<n> kills=<n> judged=<n>; no cause armed by the sequence; the launch's success arms the pending-only watch (pending-row)` | The end line of a replacement whose launch succeeded: the new instance is watched while it starts, as after any launch (`unavailable-retry: persona=<key> armed in pending-only mode (pending-row) …`, unless the persona's retries were already waiting). `no retry timer armed` in its place: the launch reconnected or did nothing. Nothing. |
|
|
4797
4799
|
| `[slack] live-row-sequence: <ref>: the launch failed (<resume\|reuse>; result=<result>[, refused][, stopping]) — runs=<n> kills=<n> judged=<n>; <armed>` | The replacement's launch did not succeed (`<result>` `failed`, `deferred`, `latched` or `held`; `held`: see [A persona posts a Cannot launch notice](#a-persona-posts-a-cannot-launch-notice)). Nothing more is called. `<armed>` is `the retry timer armed (sequence-ended-without-launch)`: the persona is retried later; `the retry timer armed by the launch (pending-only)`: the launch could not create its tmux session (`ErrTmuxSessionCreate`) and started the persona's retries itself, in pending-only mode; or `no retry timer armed` when the launch held the persona, the server is stopping, or the persona's teardown or the server's shutdown stopped the replacement. Follow the launch's own lines just before it. |
|
|
4798
4800
|
| `[slack] live-row-sequence: <ref>: not launched (<resume\|reuse>; <why>) — runs=<n> kills=<n> judged=<n>; the retry timer armed (<cause>)` | No launch was made. `<why>`: `not-resumable` (agent-director said the instance can't be resumed and the row was then not found starting; the line ends `the retry timer armed (spawn-not-resumable-lost-race)`), `not-resumable-pending` (the same, but the row was starting again), `not-applied` (the persona left the configuration), `reuse-collision` (the `reuse` launch found the instance id's row live again, so nothing was launched and nothing is counted; the line ends `the retry timer armed (collision)`), `stopped` (the persona was held, or its teardown or the server's shutdown began, while the launch waited for another launch of it; the line then ends `no retry timer armed`: follow the *Held:* entry if one was posted) or `capped` (the persona reached the restart limit while the replacement ran, after its `not launching … — the persona reached the restart cap while its sequence ran` line; the line ends `no retry timer armed`: see [A persona's resumed launch ends before it reports in](#a-personas-resumed-launch-ends-before-it-reports-in)). `<cause>` is `collision` for `reuse-collision`, `spawn-not-resumable-lost-race` for `not-resumable` and `sequence-ended-without-launch` otherwise; the line ends `no retry timer armed` instead when the persona is held. The persona's retries start, except for `stopped` and `capped`; for a persona no longer in the configuration they stop at their first retry, with no call. Nothing; for `capped`, restart the server to retry the persona once the cause of its failed launches is fixed. |
|
|
@@ -5598,7 +5600,7 @@ Messages about one persona start `personas[<i>] "<name>" (key=<key>): `, or
|
|
|
5598
5600
|
just `personas[<i>]: ` when the name itself is the problem. A malformed value
|
|
5599
5601
|
(a bad channel ID, `dm.contact`, `permission_prompts` or
|
|
5600
5602
|
`invited.permission_prompts`) is never echoed, and neither is a rejected
|
|
5601
|
-
`allow_invited_channels`, and
|
|
5603
|
+
`allow_invited_channels`, `model` or `effort`, and
|
|
5602
5604
|
no message shows a `bot_token` or `app_token` value. Messages do quote
|
|
5603
5605
|
persona names (as written) and keys, channel IDs, the configuration file's path and, in the duplicate-path rules, the
|
|
5604
5606
|
persona paths. An unknown key's name is quoted only when it is a plain setting
|
|
@@ -5633,6 +5635,7 @@ In both modes:
|
|
|
5633
5635
|
- Two personas share a `working_directory` or a `credentials_file`, compared by real path.
|
|
5634
5636
|
- A `dm.contact` is malformed. User IDs start with `U` or `W`, followed by capital letters and digits only; underscores are not allowed.
|
|
5635
5637
|
- A path isn't absolute, `~` or `~/…`.
|
|
5638
|
+
- A `model` is empty, only whitespace or holds a control character, or an `effort` isn't one of `low`, `medium`, `high`, `xhigh` or `max` (case-sensitive). Both are set per persona only: at the top level they are unknown keys.
|
|
5636
5639
|
- A required field is missing, or a key of the top level, a persona entry or `dm` is unknown.
|
|
5637
5640
|
- A value has the wrong type, isn't an allowed value, or is out of range.
|
|
5638
5641
|
|
|
@@ -5669,6 +5672,7 @@ For a persona error, the error names the persona (`personas[<i>]`) and the field
|
|
|
5669
5672
|
| `the configuration must be a JSON object, got <type>.` | Top level isn't an object. | Wrap it in `{ … }`. |
|
|
5670
5673
|
| A key belonging to the pre-persona shape | See [Pre-persona configuration](#pre-persona-configuration). | |
|
|
5671
5674
|
| `unknown top-level field(s) in config.json: "chanels".`, or `…: 1 field whose name is not shown (it is not a plain setting name, so it could be a pasted secret).`, or `…: "chanels", plus 2 fields whose names are not shown (they are not plain setting names, so they could be pasted secrets).` | A top-level key the server doesn't know (often a typo). Only names made of letters and underscores (up to 48 characters) are shown; any other name (one with a digit such as `channels2`, a dash, a dot, or a pasted token) is only counted. | Remove or correct it. For an unshown field, compare the file's top-level keys with the settings in `docs/configuration.md`. |
|
|
5675
|
+
| `unknown top-level field(s) in config.json: "model".` or `… "effort".` | `model` or `effort` written at the top level, where neither exists: both are set per persona only. | Set it on each persona that should use it, and remove the top-level key. |
|
|
5672
5676
|
| `claude_director_poll_interval_ms has been renamed to agent_director_poll_interval_ms …` | Old key name. | Rename it. |
|
|
5673
5677
|
| `personas is required: an array of persona entries, which may be empty.` / `personas must be an array, got <type>.` | Missing or wrong type. | Add `"personas": [ … ]`. |
|
|
5674
5678
|
| Server-wide type and range errors: `<key> must be a non-negative number.`, `<key> is invalid. Allowed values are: ….`, `<key> must be a boolean.`, `<key> must be a non-empty string when set.`, `<key> must be a string when set.`, `<key> must be a positive integer (>= 1) when set.`, `agent_director_poll_interval_ms must be a positive integer in [200, 3600000].`, `agent_director_call_timeout_ms must be a positive integer in [1000, 3600000].`, `cron_table_path must be a non-empty string.`, `cron_log_path must be a non-empty string.` | A server-wide setting has the wrong type or is out of range. | Correct the named key. |
|
|
@@ -5691,6 +5695,8 @@ of one mode is never checked in the other.
|
|
|
5691
5695
|
| `credentials_file is required.` / `working_directory is required.` | both | Missing path. | Add it. |
|
|
5692
5696
|
| `<setting> must be an absolute path, "~" or a path starting with "~/".` | both | `credentials_file`, `working_directory` or `claude_config_dir` is relative (or not a string). | Use an absolute path or `~/…`. |
|
|
5693
5697
|
| `stop_hook_bootstrap must be a boolean.` / `dm.enabled must be a boolean.` | both | Wrong type. | Use `true` or `false`. |
|
|
5698
|
+
| `model must be a non-empty string with no control characters when set.` | both | `model` is empty or only whitespace, holds a control character, or isn't a string (`null`, a number, a boolean, an array or an object). The value is never shown. `model` then `effort` are checked after `stop_hook_bootstrap` and before the channel, `dm` and destination values, so this error comes ahead of an invalid channel ID or delivery, a non-boolean `dm.enabled` or a bad destination. Shape errors (an unknown key, `dm` or `invited` not a JSON object, `channels` not an array, a `channels` entry not a JSON object) are reported first. | Use a non-empty model name such as `opus`, `opus[1m]` or a full model ID, or remove the key. |
|
|
5699
|
+
| `effort is invalid. Allowed values are: low, medium, high, xhigh, max.` | both | `effort` is another value, such as `ultracode` or `High` (case-sensitive), or isn't a string. The value is never shown. Checked right after `model`, in the same order as the `model` row says, with shape errors reported first. | Use one of `low`, `medium`, `high`, `xhigh`, `max`, lower-case, or remove the key. |
|
|
5694
5700
|
| `dm must be a JSON object, got <type>.` | both | Wrong shape. | Fix the shape. |
|
|
5695
5701
|
| `channels must be an array, got <type>.` / `channels[<j>] must be a JSON object, got <type>.` | declarative | Wrong shape. | Fix the shape. |
|
|
5696
5702
|
| `channels[<j>].id is required.` / `channels[<j>].delivery is required.` | declarative | A channel entry lacks a key. | Every channel needs `id` and `delivery`. |
|
|
@@ -5882,6 +5888,7 @@ line among them.
|
|
|
5882
5888
|
| `…: stop_hook_bootstrap changed: applied in place immediately, instance kept.` | The persona's own reminder setting changed. Subsequent checks use the confirmed value, keeping its running session. |
|
|
5883
5889
|
| `…: claude_config_dir changed: takes effect at its next launch, which starts fresh (the conversation is not resumed), instance kept until then.` | The persona's own `claude_config_dir` changed (by real path). The running instance is kept; its next launch uses the new directory and starts a new conversation. A reminder-setting change alongside it has its own immediate in-place effect. |
|
|
5884
5890
|
| `…: claude_config_dir changed: …, instance kept until then; but at that launch it cannot come up: claude_config_dir cannot be resolved to a real path (<errno>).` | A warning: the new directory can't be resolved now, with the same check and wording as an added persona's (`(<errno>: a symlink on its path points to nothing)` for a dangling symlink). It is not a refusal: confirming applies the change, and at the persona's next launch it is held as [`persona-config-dir-unresolvable`](#persona-config-dir-unresolvable) (its Slack connection closed, its launch waiting) until the directory resolves. The same warning ends a changed top-level `claude_config_dir` line, naming the inheriting personas it stops (`; but at that launch "<name>" (key=<key>), … cannot come up: …`), and a `DESTRUCTIVE:` line whose persona's `claude_config_dir` changed too (`; but it cannot come up: …`; that persona is held when it is brought up at the confirmation). Fix the directory, or the setting, before confirming. If the check itself failed, the line ends `; whether it can come up at that launch could not be checked` (see the `cannot check` line below). |
|
|
5891
|
+
| `…: model changed: takes effect at its next launch, which starts fresh (the conversation is not resumed) unless its model and effort then match the ones its conversation started with, instance kept until then.` (or `effort changed` or `model, effort changed`) | The persona's own `model` or `effort` changed: added, removed or written differently (compared as written, so an alias and a full model ID differ). The running instance is kept; its next launch uses the new values and starts a new conversation unless they then match the ones its conversation started with. The line names the keys, never a value, and carries no warning. A `DESTRUCTIVE:` or `is added` line carries no model or effort wording: that persona starts fresh with the new values. |
|
|
5885
5892
|
| `…: credentials file "<path>" changed: a new connection opens, then the old one closes, instance kept.` | The credentials file's content changed at the same path, and the persona is up. The line names the persona and the path, never a token. |
|
|
5886
5893
|
| `…: credentials file "<path>" changed: it has no connection yet, so it retries with the new content, instance kept.` | The same, for a persona still retrying its bring-up (Slack unreachable, its working directory unusable, or held for its `claude_config_dir`, see [`persona-config-dir-unresolvable`](#persona-config-dir-unresolvable)). |
|
|
5887
5894
|
| `…: credentials file "<path>" changed: it is broken by its credentials now, so it will be brought up.` | The same, for a persona that is broken by its credentials: its credentials file is missing, unreadable or invalid, or Slack refused its tokens ([`persona-credentials-refused`](#persona-credentials-refused)). |
|
|
@@ -5901,6 +5908,9 @@ line among them.
|
|
|
5901
5908
|
|
|
5902
5909
|
One persona's effects share one line, joined by `; `, for example
|
|
5903
5910
|
`persona "bravo" (key=bravo): channels changed: applied in place immediately, instance kept; credentials file "<path>" changed: a new connection opens, then the old one closes, instance kept.`
|
|
5911
|
+
A persona whose `claude_config_dir` and `model` or `effort` both change gets
|
|
5912
|
+
both effects on its one line, each with its own clause (the
|
|
5913
|
+
`claude_config_dir` warning only on its own), joined by `; `.
|
|
5904
5914
|
|
|
5905
5915
|
**Invalid.** When the edit can't be applied, the preview is one line and
|
|
5906
5916
|
nothing else, and [`reload-invalid`](#reload-invalid) is logged:
|
|
@@ -6074,6 +6084,7 @@ for a server start.
|
|
|
6074
6084
|
| Preview line | After the confirmation | To carry it out sooner |
|
|
6075
6085
|
|---|---|---|
|
|
6076
6086
|
| `claude_config_dir changed` (`takes effect at its next launch`, or a top-level setting `inherited by …`) | Recorded. It reaches each persona at its next launch: when the bot dies (a crash or a failed health check), at `clean_restart --server`, or `clean_restart <bot>…` naming it, at `stop --stop-bots` then `start`, or after a host reboot. A changed `claude_config_dir` makes that launch start a new conversation on the same instance; nothing is deleted, and the old conversation stays in the old directory. | Relaunch the bots: `claude-slack-channel-bots clean_restart <bot> [<bot>…]` for the personas it names, `claude-slack-channel-bots clean_restart --server` from a terminal, or `stop --stop-bots` then `start`. Each cuts off the relaunched bots' current turns. A plain restart reconnects running bots, which is not a launch. |
|
|
6087
|
+
| `model changed`, `effort changed` or `model, effort changed` (`takes effect at its next launch, which starts fresh … unless its model and effort then match …`) | Recorded. It reaches the persona at its next launch, the same launches as the `claude_config_dir` row. That launch starts a new conversation on the same instance unless the values then match the ones its conversation started with; nothing is deleted, and the old conversation is kept. | The same relaunches as the `claude_config_dir` row. |
|
|
6077
6088
|
| `server-wide setting <name> changed: once applied, it is recorded and takes effect at the next server start after that.` | Recorded. The running server keeps the value it started with. | Restart. |
|
|
6078
6089
|
| `server-wide setting <name> changed: once applied, it is recorded, and the CLI takes it from the record from then on (the running server does not use it).` | `stop_timeout` or `exit_timeout`. The next `stop` or `clean_restart` uses it. | Nothing. |
|
|
6079
6090
|
| `server-wide setting <name> changed: once applied, it is recorded, the CLI takes it from the record from then on, and the running server uses it from its next start.` | `agent_director_call_timeout_ms`. The next `stop --stop-bots` or `clean_restart` uses it; the running server keeps the value it started with. | Restart, or `clean_restart --server` from a terminal. |
|
|
@@ -6083,7 +6094,8 @@ Wait for `reload-applied`, then, with the operator's say-so, run
|
|
|
6083
6094
|
from a bot's own session (it would end that session and leave the server
|
|
6084
6095
|
down). It covers every row above: the
|
|
6085
6096
|
server comes back on the record and every bot is relaunched, resuming its
|
|
6086
|
-
conversation except where a changed `claude_config_dir
|
|
6097
|
+
conversation except where a changed `claude_config_dir`, or a `model` or
|
|
6098
|
+
`effort` that differs from what its conversation started with, starts it fresh.
|
|
6087
6099
|
When only next-launch settings of some personas changed,
|
|
6088
6100
|
`claude-slack-channel-bots clean_restart <bot> [<bot>…]` relaunches just
|
|
6089
6101
|
those, and the server keeps running; a bot running it names only other
|
|
@@ -6161,7 +6173,7 @@ applied. Compared with a confirmation:
|
|
|
6161
6173
|
up fresh in the new directory.
|
|
6162
6174
|
- A changed `credentials_file` only reconnects the persona to Slack with the
|
|
6163
6175
|
new app; its instance keeps running.
|
|
6164
|
-
- A setting that takes effect at a bot's launch (a changed `claude_config_dir`) doesn't reach a bot that kept running. If the
|
|
6176
|
+
- A setting that takes effect at a bot's launch (a changed `claude_config_dir`, `model` or `effort`) doesn't reach a bot that kept running. If the
|
|
6165
6177
|
server is still running, stop it with `stop --stop-bots` instead of `stop`.
|
|
6166
6178
|
|
|
6167
6179
|
With the operator's say-so, stop the server if it is still running:
|
|
@@ -6479,10 +6491,12 @@ or a filesystem that doesn't support syncing a directory).
|
|
|
6479
6491
|
- a change to the section the mode doesn't read is only written into the
|
|
6480
6492
|
record, with no effect until the switch selects that section;
|
|
6481
6493
|
- a kept persona's `stop_hook_bootstrap` change applies immediately;
|
|
6482
|
-
- a kept persona's `claude_config_dir` change takes effect at its next launch (a relaunch after the bot dies, a
|
|
6494
|
+
- a kept persona's `claude_config_dir`, `model` or `effort` change takes effect at its next launch (a relaunch after the bot dies, a
|
|
6483
6495
|
`clean_restart --server`, or `clean_restart <bot>…` naming it, `stop --stop-bots` then `start`, or a host reboot; a
|
|
6484
6496
|
plain `stop` and `start` only reconnects), which launches the new
|
|
6485
|
-
declaration; until then its instance runs as launched
|
|
6497
|
+
declaration; until then its instance runs as launched. A changed
|
|
6498
|
+
`model` or `effort` makes that launch start a new conversation only when
|
|
6499
|
+
the values then differ from the ones its conversation started with;
|
|
6486
6500
|
- a kept persona whose `credentials_file` path or `working_directory`
|
|
6487
6501
|
changed is torn down, like a removed persona, then brought up fresh from
|
|
6488
6502
|
its new entry, like an added one: its old instance is ended and its row
|
|
@@ -7151,6 +7165,15 @@ See the `Session connected with CWD "<path>" — no matching persona` and `Sessi
|
|
|
7151
7165
|
### Symptom: A persona doesn't come up or doesn't answer
|
|
7152
7166
|
|
|
7153
7167
|
See [Triage](#triage), [Reading a persona line](#reading-a-persona-line), the [persona diagnostic classes](#persona-diagnostic-classes) and [A persona is down but its instance is still running](#a-persona-is-down-but-its-instance-is-still-running).
|
|
7168
|
+
If it went silent after a `model` change, see [the model-error entry](#symptom-a-persona-seems-silent-after-its-model-was-set-or-changed).
|
|
7169
|
+
|
|
7170
|
+
### Symptom: A persona seems silent after its `model` was set or changed
|
|
7171
|
+
|
|
7172
|
+
CSCB accepts any `model` name it can pass. If Claude Code does not know the name, the persona launches but its requests fail with Claude Code's model error. With the operator's say-so, attach to its session (`tmux attach -t =slack_bot_<key>`) to read the error.
|
|
7173
|
+
|
|
7174
|
+
1. Correct the name, or remove `model`, in `config.json`.
|
|
7175
|
+
2. [Confirm the change](../../docs/reload.md#reload) and wait for `reload-applied`.
|
|
7176
|
+
3. With the operator's say-so, run `claude-slack-channel-bots clean_restart <bot>` to relaunch the persona on a fresh conversation; run it from a terminal or another bot's session.
|
|
7154
7177
|
|
|
7155
7178
|
### Symptom: Acknowledgement reaction missing or stuck
|
|
7156
7179
|
|
|
@@ -7250,6 +7273,12 @@ and [Persona diagnostic classes](#persona-diagnostic-classes). Storage failures
|
|
|
7250
7273
|
a disk check; stuck delivery needs the notice's remedy; waiting messages need the
|
|
7251
7274
|
persona's hold or down state resolved. Accepted items remain queued in order.
|
|
7252
7275
|
|
|
7276
|
+
### Symptom: A persona on a Mac keeps retyping a long message
|
|
7277
|
+
|
|
7278
|
+
Claude Code on macOS loses typed input sent as one large burst, so on a macOS host CSCB types a long message as several parts of at most about 880 bytes each, tag included; on other hosts a part can reach about 1,900 bytes. On a Mac, long attachment lists, sender names and channel names in the tag are shortened sooner, each ending in `…`; the channel ID and the attachment count are kept whole.
|
|
7279
|
+
|
|
7280
|
+
CSCB sets a message's parts once, shortly after it is queued, and never changes them; restarting the bot or the server does not split it again. A message queued before an upgrade can therefore keep its larger parts, go on logging `message-retyped` and raise a *Message not taken in* or *Delivery stuck* notice while later messages wait behind it. CSCB keeps offering it until the bot takes it in. [Delivery notices](#delivery-notices) explains each notice and how to attach to the bot's terminal to see what it received.
|
|
7281
|
+
|
|
7253
7282
|
### Symptom: A persona posts a *Waiting on a prompt* or *Not connected* notice
|
|
7254
7283
|
|
|
7255
7284
|
The persona's session runs but is disconnected. Messages sent to it are kept and delivered once it reconnects. What each notice means and what to do: the notice table under [A persona's instance runs but isn't connected](#a-personas-instance-runs-but-isnt-connected).
|
|
@@ -7312,7 +7341,7 @@ The precheck failed, so the server and every bot keep running. Fix the cause eac
|
|
|
7312
7341
|
|
|
7313
7342
|
### Symptom: The server and every bot are down after `clean_restart --server` run from a bot
|
|
7314
7343
|
|
|
7315
|
-
`clean_restart --server` stops the server first, then ends every bot's session, the one it runs in included, and so the command itself, before it can start the server again. `clean_restart.log` ends with `[slack] clean_restart: stopping server` and `teardownBots` lines, with no `[slack] clean_restart: starting server`, and `server.pid` is absent or stale. Fix: with the operator's say-so, start the server from a terminal, `claude-slack-channel-bots start`; it brings every bot back,
|
|
7344
|
+
`clean_restart --server` stops the server first, then ends every bot's session, the one it runs in included, and so the command itself, before it can start the server again. `clean_restart.log` ends with `[slack] clean_restart: stopping server` and `teardownBots` lines, with no `[slack] clean_restart: starting server`, and `server.pid` is absent or stale. Fix: with the operator's say-so, start the server from a terminal, `claude-slack-channel-bots start`; it brings every bot back, subject to the fresh-start rules in [Next-launch and server-wide settings](#next-launch-and-server-wide-settings). To restart from a bot, run `clean_restart <bot>…` naming only other bots; a bot that names itself is relaunched by the server, but the command can't report on it.
|
|
7316
7345
|
|
|
7317
7346
|
### Symptom: `clean_restart <bot>…` says a bot was `not seen relaunched`
|
|
7318
7347
|
|
|
@@ -7330,7 +7359,7 @@ At least one persona could not be stopped after a passed precheck; the command f
|
|
|
7330
7359
|
|
|
7331
7360
|
With `resume_enabled: true`, a bot whose host rebooted (or pod resumed) should return with its conversation history. The agent-director version is not the cause: the server starts only on agent-director Phase 1, since the startup gate refuses any binary below it (`ad-system-install-too-old` or `ad-below-phase1-floor`, see [Startup errors](#startup-errors-startup-errorslog)). `bun run install-check` passes a binary below Phase 1 only with its note that the server will not start on it (see [Checking your agent-director install](../../docs/agent-director.md#checking-your-agent-director-install)). A bot that comes back amnesiac on a running server started a new conversation for one of the reasons below, which the log names.
|
|
7332
7361
|
|
|
7333
|
-
A bot also starts a new conversation, by design, when its session no longer matches the applied configuration. A config edit takes effect only once it is applied (see [Reload](../../docs/reload.md#reload)); a restart or reboot alone runs the last-applied record. When a change to a persona's `working_directory` is applied, the persona is torn down and brought up fresh, once its old instance has ended (see "A persona waits on an old instance in its working directory" above). When a change to a persona's effective `claude_config_dir` (its own or the top-level default) is applied, the bot starts a new conversation the next time it would be resumed (see [When next-launch and server-wide changes take effect](../../docs/reload.md#when-next-launch-and-server-wide-changes-take-effect)); a bot that keeps running keeps its old config directory until then. A bot whose instance no longer matches its working directory or config directory, or whose `resume_enabled` is false, starts a new conversation on the same instance: the old conversation stays in its directory and prior sessions are retained in the same AD life, and nothing is deleted. A bot that is still running is first ended, with each kill checked, before the new conversation starts; one that is still starting is never typed into: it is ended with a checked kill, and the new conversation starts only once agent-director's grace period has passed since its launch start (see "A persona's instance is still starting" above). A persona retired by a confirmed change (removed and added again with the same key, renamed back, or brought up from its new entry after a `credentials_file` path or `working_directory` change) starts a new conversation on the same instance, restarts included, and its old conversation is never resumed. So does a persona whose instance a server start found while the persona was not in the applied configuration (for example, after `config.json.last-applied` was deleted) and that is added back later: that start recorded its key as retired. After upgrading from an earlier release, each bot also starts one new conversation once, with memory files kept: a bot instance from before the upgrade is never resumed (see [Step 10: Start the new CSCB](../../docs/migration.md#step-10-start-the-new-cscb)). A bot whose last 2 resumes ended before its session started also starts fresh: its transcript holds no conversation Claude Code can load (see "Session not restarting after crash" above). The log names the reason: search `server.log` for `sweeping row`, `pre-persona row`, `replacing the row`, `not resuming; replacing its row`, `key is retired` or `not resuming its transcript again`.
|
|
7362
|
+
A bot also starts a new conversation, by design, when its session no longer matches the applied configuration. A config edit takes effect only once it is applied (see [Reload](../../docs/reload.md#reload)); a restart or reboot alone runs the last-applied record. When a change to a persona's `working_directory` is applied, the persona is torn down and brought up fresh, once its old instance has ended (see "A persona waits on an old instance in its working directory" above). When a change to a persona's effective `claude_config_dir` (its own or the top-level default) is applied, the bot starts a new conversation the next time it would be resumed (see [When next-launch and server-wide changes take effect](../../docs/reload.md#when-next-launch-and-server-wide-changes-take-effect)); a bot that keeps running keeps its old config directory until then. A persona whose `model` or `effort` differs from what its conversation started with starts a new conversation on the same instance at its next launch; this includes setting either key over a conversation from an earlier release. Nothing is deleted. A bot whose instance no longer matches its working directory or config directory, or whose `resume_enabled` is false, starts a new conversation on the same instance: the old conversation stays in its directory and prior sessions are retained in the same AD life, and nothing is deleted. A bot that is still running is first ended, with each kill checked, before the new conversation starts; one that is still starting is never typed into: it is ended with a checked kill, and the new conversation starts only once agent-director's grace period has passed since its launch start (see "A persona's instance is still starting" above). A persona retired by a confirmed change (removed and added again with the same key, renamed back, or brought up from its new entry after a `credentials_file` path or `working_directory` change) starts a new conversation on the same instance, restarts included, and its old conversation is never resumed. So does a persona whose instance a server start found while the persona was not in the applied configuration (for example, after `config.json.last-applied` was deleted) and that is added back later: that start recorded its key as retired. After upgrading from an earlier release, each bot also starts one new conversation once, with memory files kept: a bot instance from before the upgrade is never resumed (see [Step 10: Start the new CSCB](../../docs/migration.md#step-10-start-the-new-cscb)). A bot whose last 2 resumes ended before its session started also starts fresh: its transcript holds no conversation Claude Code can load (see "Session not restarting after crash" above). The log names the reason: search `server.log` for `sweeping row`, `pre-persona row`, `replacing the row`, `not resuming; replacing its row`, `key is retired`, `from what its conversation started with` or `not resuming its transcript again`.
|
|
7334
7363
|
|
|
7335
7364
|
### Symptom: Session crashes on resume with "sandbox required but unavailable"
|
|
7336
7365
|
|
|
@@ -381,8 +381,10 @@ Everyone in a destination channel sees the prompts and notices. See `docs/channe
|
|
|
381
381
|
|
|
382
382
|
#### 5.8 Optional next-launch settings
|
|
383
383
|
|
|
384
|
-
Offer these only if the operator wants
|
|
385
|
-
|
|
384
|
+
Offer these only if the operator wants them (`docs/configuration.md`
|
|
385
|
+
`### Next-launch settings`). A setting left out keeps its default: the
|
|
386
|
+
top-level value for `claude_config_dir` and `stop_hook_bootstrap`, Claude
|
|
387
|
+
Code's own choice for `model` and `effort`.
|
|
386
388
|
|
|
387
389
|
- **`claude_config_dir`**: the persona's own Claude config directory, so it can
|
|
388
390
|
use a different Claude account. Never `~/.claude`. The operator populates it
|
|
@@ -398,6 +400,13 @@ Offer these only if the operator wants to override the top-level defaults
|
|
|
398
400
|
- **`stop_hook_bootstrap`**: boolean compatibility setting; `false` turns Slack
|
|
399
401
|
reply reminders off for this persona. Confirmed changes apply immediately
|
|
400
402
|
and keep its running session. See [Slack reply reminders](../../docs/bots.md#slack-reply-reminders).
|
|
403
|
+
- **`model`** and **`effort`**: the model and effort level the persona's
|
|
404
|
+
instance starts with. Set per persona only; at the top level they are
|
|
405
|
+
rejected. `model` is a name such as `opus`, `opus[1m]` or a full model ID,
|
|
406
|
+
written exactly; `effort` is one of `low`, `medium`, `high`, `xhigh` or
|
|
407
|
+
`max`. Set both, one or neither. A changed value takes effect at the
|
|
408
|
+
persona's next launch, which starts a new conversation unless the values
|
|
409
|
+
then match the ones its conversation started with.
|
|
401
410
|
|
|
402
411
|
#### 5.9 Declare the persona
|
|
403
412
|
|
|
@@ -615,7 +624,9 @@ runs:
|
|
|
615
624
|
claude-slack-channel-bots clean_restart --server
|
|
616
625
|
```
|
|
617
626
|
|
|
618
|
-
It restarts the server and relaunches every persona; conversations resume
|
|
627
|
+
It restarts the server and relaunches every persona; conversations resume
|
|
628
|
+
unless a confirmed `claude_config_dir` change, or a `model` or `effort` that
|
|
629
|
+
differs from what the conversation started with, starts that persona fresh.
|
|
619
630
|
The operator runs it from a terminal; you never do. Run from a bot's own
|
|
620
631
|
session, it ends that session and leaves the server down.
|
|
621
632
|
|