charness 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/DEVELOPMENT.md +12 -1
- package/README.md +89 -6
- package/SETUP.md +199 -1
- package/dist/adapters/adapterEventRouting.d.ts +2 -2
- package/dist/adapters/adapterEventRouting.d.ts.map +1 -1
- package/dist/adapters/adapterEventRouting.js +4 -3
- package/dist/adapters/adapterEventRouting.js.map +1 -1
- package/dist/adapters/claudeCliAdapter.js +2 -2
- package/dist/adapters/claudeCliAdapter.js.map +1 -1
- package/dist/adapters/claudeConfigDir.d.ts +18 -0
- package/dist/adapters/claudeConfigDir.d.ts.map +1 -1
- package/dist/adapters/claudeJsonStreamAdapter.d.ts +82 -6
- package/dist/adapters/claudeJsonStreamAdapter.d.ts.map +1 -1
- package/dist/adapters/claudeJsonStreamAdapter.js +211 -14
- package/dist/adapters/claudeJsonStreamAdapter.js.map +1 -1
- package/dist/adapters/createAdapter.d.ts +14 -1
- package/dist/adapters/createAdapter.d.ts.map +1 -1
- package/dist/adapters/createAdapter.js +54 -2
- package/dist/adapters/createAdapter.js.map +1 -1
- package/dist/adapters/openCodeAdapter.d.ts +5 -0
- package/dist/adapters/openCodeAdapter.d.ts.map +1 -1
- package/dist/adapters/openCodeAdapter.js +7 -0
- package/dist/adapters/openCodeAdapter.js.map +1 -1
- package/dist/agentLogin/agentLogin.d.ts +18 -1
- package/dist/agentLogin/agentLogin.d.ts.map +1 -1
- package/dist/agentLogin/agentLogin.js +59 -18
- package/dist/agentLogin/agentLogin.js.map +1 -1
- package/dist/apiErrorRetry.d.ts +3 -1
- package/dist/apiErrorRetry.d.ts.map +1 -1
- package/dist/apiErrorRetry.js +11 -1
- package/dist/apiErrorRetry.js.map +1 -1
- package/dist/bot.d.ts.map +1 -1
- package/dist/bot.js +475 -105
- package/dist/bot.js.map +1 -1
- package/dist/cli/claudeProfiles.d.ts +1 -1
- package/dist/cli/claudeProfiles.d.ts.map +1 -1
- package/dist/cli/claudeProfiles.js +11 -6
- package/dist/cli/claudeProfiles.js.map +1 -1
- package/dist/connectors/jira/connector.d.ts +2 -0
- package/dist/connectors/jira/connector.d.ts.map +1 -1
- package/dist/connectors/jira/connector.js +5 -4
- package/dist/connectors/jira/connector.js.map +1 -1
- package/dist/connectors/telegram/commands/claudeAccounts.d.ts +6 -2
- package/dist/connectors/telegram/commands/claudeAccounts.d.ts.map +1 -1
- package/dist/connectors/telegram/commands/claudeAccounts.js +127 -28
- package/dist/connectors/telegram/commands/claudeAccounts.js.map +1 -1
- package/dist/connectors/telegram/commands/compaction.d.ts.map +1 -1
- package/dist/connectors/telegram/commands/compaction.js +39 -8
- package/dist/connectors/telegram/commands/compaction.js.map +1 -1
- package/dist/connectors/telegram/commands/fallbackChain.d.ts.map +1 -1
- package/dist/connectors/telegram/commands/fallbackChain.js +6 -2
- package/dist/connectors/telegram/commands/fallbackChain.js.map +1 -1
- package/dist/connectors/telegram/forumTopicCreated.d.ts +20 -0
- package/dist/connectors/telegram/forumTopicCreated.d.ts.map +1 -0
- package/dist/connectors/telegram/forumTopicCreated.js +16 -0
- package/dist/connectors/telegram/forumTopicCreated.js.map +1 -0
- package/dist/i18n/de.d.ts.map +1 -1
- package/dist/i18n/de.js +42 -1
- package/dist/i18n/de.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +42 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +42 -1
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/fr.d.ts.map +1 -1
- package/dist/i18n/fr.js +42 -1
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/hi.d.ts.map +1 -1
- package/dist/i18n/hi.js +42 -1
- package/dist/i18n/hi.js.map +1 -1
- package/dist/i18n/ja.d.ts.map +1 -1
- package/dist/i18n/ja.js +42 -1
- package/dist/i18n/ja.js.map +1 -1
- package/dist/i18n/ka.d.ts.map +1 -1
- package/dist/i18n/ka.js +42 -1
- package/dist/i18n/ka.js.map +1 -1
- package/dist/i18n/pt.d.ts.map +1 -1
- package/dist/i18n/pt.js +42 -1
- package/dist/i18n/pt.js.map +1 -1
- package/dist/i18n/ru.d.ts.map +1 -1
- package/dist/i18n/ru.js +42 -1
- package/dist/i18n/ru.js.map +1 -1
- package/dist/i18n/uk.d.ts.map +1 -1
- package/dist/i18n/uk.js +42 -1
- package/dist/i18n/uk.js.map +1 -1
- package/dist/i18n/uz.d.ts.map +1 -1
- package/dist/i18n/uz.js +42 -1
- package/dist/i18n/uz.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +42 -1
- package/dist/i18n/zh.js.map +1 -1
- package/dist/i18n.d.ts +2 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js.map +1 -1
- package/dist/rateLimiter.d.ts +6 -0
- package/dist/rateLimiter.d.ts.map +1 -1
- package/dist/rateLimiter.js +9 -0
- package/dist/rateLimiter.js.map +1 -1
- package/dist/requests/wakeUpEngine.d.ts +4 -0
- package/dist/requests/wakeUpEngine.d.ts.map +1 -1
- package/dist/requests/wakeUpEngine.js +3 -3
- package/dist/requests/wakeUpEngine.js.map +1 -1
- package/dist/requests/wakeUpRules.d.ts +2 -1
- package/dist/requests/wakeUpRules.d.ts.map +1 -1
- package/dist/requests/wakeUpRules.js +3 -2
- package/dist/requests/wakeUpRules.js.map +1 -1
- package/dist/scheduler/mcpSurface.d.ts.map +1 -1
- package/dist/scheduler/mcpSurface.js +5 -3
- package/dist/scheduler/mcpSurface.js.map +1 -1
- package/dist/selfUpdate/updateMessages.d.ts +1 -1
- package/dist/selfUpdate/updateMessages.d.ts.map +1 -1
- package/dist/selfUpdate/updateMessages.js.map +1 -1
- package/dist/state.d.ts +67 -5
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +76 -7
- package/dist/state.js.map +1 -1
- package/dist/testTimings.d.ts +45 -0
- package/dist/testTimings.d.ts.map +1 -0
- package/dist/testTimings.js +89 -0
- package/dist/testTimings.js.map +1 -0
- package/dist/types.d.ts +49 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/adapterSwitchSessionIds.d.ts +5 -1
- package/dist/utils/adapterSwitchSessionIds.d.ts.map +1 -1
- package/dist/utils/adapterSwitchSessionIds.js +1 -1
- package/dist/utils/adapterSwitchSessionIds.js.map +1 -1
- package/dist/utils/adoptedToolList.d.ts +16 -21
- package/dist/utils/adoptedToolList.d.ts.map +1 -1
- package/dist/utils/adoptedToolList.js +18 -17
- package/dist/utils/adoptedToolList.js.map +1 -1
- package/dist/utils/anthropicApiKeyCheck.d.ts +19 -0
- package/dist/utils/anthropicApiKeyCheck.d.ts.map +1 -0
- package/dist/utils/anthropicApiKeyCheck.js +41 -0
- package/dist/utils/anthropicApiKeyCheck.js.map +1 -0
- package/dist/utils/claudeAccountPickerPlan.d.ts +6 -1
- package/dist/utils/claudeAccountPickerPlan.d.ts.map +1 -1
- package/dist/utils/claudeAccountPickerPlan.js +5 -1
- package/dist/utils/claudeAccountPickerPlan.js.map +1 -1
- package/dist/utils/claudeApiKeyHelper.d.ts +19 -0
- package/dist/utils/claudeApiKeyHelper.d.ts.map +1 -0
- package/dist/utils/claudeApiKeyHelper.js +32 -0
- package/dist/utils/claudeApiKeyHelper.js.map +1 -0
- package/dist/utils/claudeApiSpend.d.ts +73 -0
- package/dist/utils/claudeApiSpend.d.ts.map +1 -0
- package/dist/utils/claudeApiSpend.js +82 -0
- package/dist/utils/claudeApiSpend.js.map +1 -0
- package/dist/utils/claudeCompactHook.d.ts +11 -6
- package/dist/utils/claudeCompactHook.d.ts.map +1 -1
- package/dist/utils/claudeCompactHook.js +28 -11
- package/dist/utils/claudeCompactHook.js.map +1 -1
- package/dist/utils/claudeLimitSwitch.d.ts +2 -1
- package/dist/utils/claudeLimitSwitch.d.ts.map +1 -1
- package/dist/utils/claudeLimitSwitch.js +3 -2
- package/dist/utils/claudeLimitSwitch.js.map +1 -1
- package/dist/utils/claudeProfileUsageLines.d.ts +18 -3
- package/dist/utils/claudeProfileUsageLines.d.ts.map +1 -1
- package/dist/utils/claudeProfileUsageLines.js +23 -2
- package/dist/utils/claudeProfileUsageLines.js.map +1 -1
- package/dist/utils/claudeProfiles.d.ts +57 -2
- package/dist/utils/claudeProfiles.d.ts.map +1 -1
- package/dist/utils/claudeProfiles.js +96 -3
- package/dist/utils/claudeProfiles.js.map +1 -1
- package/dist/utils/claudeScheduledJobs.d.ts +49 -0
- package/dist/utils/claudeScheduledJobs.d.ts.map +1 -0
- package/dist/utils/claudeScheduledJobs.js +62 -0
- package/dist/utils/claudeScheduledJobs.js.map +1 -0
- package/dist/utils/claudeStreamJson.d.ts +35 -2
- package/dist/utils/claudeStreamJson.d.ts.map +1 -1
- package/dist/utils/claudeStreamJson.js +31 -8
- package/dist/utils/claudeStreamJson.js.map +1 -1
- package/dist/utils/claudeTranscriptContent.d.ts +8 -0
- package/dist/utils/claudeTranscriptContent.d.ts.map +1 -1
- package/dist/utils/claudeTranscriptContent.js +44 -0
- package/dist/utils/claudeTranscriptContent.js.map +1 -1
- package/dist/utils/claudeUsage.d.ts +22 -6
- package/dist/utils/claudeUsage.d.ts.map +1 -1
- package/dist/utils/claudeUsage.js +12 -5
- package/dist/utils/claudeUsage.js.map +1 -1
- package/dist/utils/claudeUsageRefresher.d.ts +6 -0
- package/dist/utils/claudeUsageRefresher.d.ts.map +1 -1
- package/dist/utils/claudeUsageRefresher.js +2 -2
- package/dist/utils/claudeUsageRefresher.js.map +1 -1
- package/dist/utils/compactOnIdle.d.ts +12 -0
- package/dist/utils/compactOnIdle.d.ts.map +1 -1
- package/dist/utils/compactOnIdle.js +15 -1
- package/dist/utils/compactOnIdle.js.map +1 -1
- package/dist/utils/doctorLine.d.ts +13 -0
- package/dist/utils/doctorLine.d.ts.map +1 -0
- package/dist/utils/doctorLine.js +18 -0
- package/dist/utils/doctorLine.js.map +1 -0
- package/dist/utils/effortPickerPlan.d.ts +1 -2
- package/dist/utils/effortPickerPlan.d.ts.map +1 -1
- package/dist/utils/effortPickerPlan.js.map +1 -1
- package/dist/utils/fallbackChain.d.ts +15 -8
- package/dist/utils/fallbackChain.d.ts.map +1 -1
- package/dist/utils/fallbackChain.js +30 -13
- package/dist/utils/fallbackChain.js.map +1 -1
- package/dist/utils/jsonStreamHost.d.ts +19 -7
- package/dist/utils/jsonStreamHost.d.ts.map +1 -1
- package/dist/utils/jsonStreamHost.js +25 -11
- package/dist/utils/jsonStreamHost.js.map +1 -1
- package/dist/utils/jsonStreamPendingWork.d.ts +44 -0
- package/dist/utils/jsonStreamPendingWork.d.ts.map +1 -0
- package/dist/utils/jsonStreamPendingWork.js +48 -0
- package/dist/utils/jsonStreamPendingWork.js.map +1 -0
- package/dist/utils/limitsBoardPlan.d.ts +4 -2
- package/dist/utils/limitsBoardPlan.d.ts.map +1 -1
- package/dist/utils/limitsBoardPlan.js +12 -5
- package/dist/utils/limitsBoardPlan.js.map +1 -1
- package/dist/utils/lostWork.d.ts +117 -0
- package/dist/utils/lostWork.d.ts.map +1 -0
- package/dist/utils/lostWork.js +187 -0
- package/dist/utils/lostWork.js.map +1 -0
- package/dist/utils/nativeWindows.d.ts +29 -0
- package/dist/utils/nativeWindows.d.ts.map +1 -0
- package/dist/utils/nativeWindows.js +50 -0
- package/dist/utils/nativeWindows.js.map +1 -0
- package/dist/utils/recvPreviewRedaction.d.ts +7 -2
- package/dist/utils/recvPreviewRedaction.d.ts.map +1 -1
- package/dist/utils/recvPreviewRedaction.js +10 -4
- package/dist/utils/recvPreviewRedaction.js.map +1 -1
- package/dist/utils/threadStatusReport.d.ts +12 -1
- package/dist/utils/threadStatusReport.d.ts.map +1 -1
- package/dist/utils/threadStatusReport.js +13 -4
- package/dist/utils/threadStatusReport.js.map +1 -1
- package/dist/utils/updateScreenPlan.d.ts +1 -1
- package/dist/utils/updateScreenPlan.d.ts.map +1 -1
- package/dist/utils/updateScreenPlan.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 3.6.0 — 2026-10-24
|
|
6
|
+
|
|
7
|
+
**If the agent's background work is lost anyway, the agent is told and the topic gets one line.** When a Claude process
|
|
8
|
+
ends with its own work pending — it crashed, it was gone after a restart, `/model`, `/effort` or an account change
|
|
9
|
+
restarted it (cutting off its turn too), or a task nobody heard from for 6 h was let go — the topic gets one plain line
|
|
10
|
+
naming the lost work, and the agent is resumed at once with a short note to check that work and start again what is still
|
|
11
|
+
needed (after a forgotten task, or a second loss within 10 minutes, the note waits for your next message instead). A
|
|
12
|
+
usage-limit switch to another account or backend, or a `/claude_mode` switch, says so in its own reply and the agent's next prompt carries the note; a topic
|
|
13
|
+
that comes back to a backend it left tells that agent too. `/loop` wakeups (`ScheduleWakeup`) now count as pending work
|
|
14
|
+
like `CronCreate` jobs, and the agent's instructions steer anything that must happen later to the bot's own scheduler.
|
|
15
|
+
|
|
16
|
+
**The agent's background work is no longer cut off by an automatic stop.** On the default Claude backend the process
|
|
17
|
+
used to be stopped in the seconds between a background task's end and the agent's turn that reads its result — the
|
|
18
|
+
result was lost and the topic went quiet until you wrote. Now a background command, Monitor or sub-agent the agent
|
|
19
|
+
started, the result it is about to read and a `CronCreate` job it scheduled all keep the process (for up to 6 h after the
|
|
20
|
+
agent's last turn, so a forgotten task does not keep it forever). After a bot update that changed its tools, a running
|
|
21
|
+
agent is no longer stopped early either: it is compacted and stopped at its normal idle mark, and resumes with the new
|
|
22
|
+
tools.
|
|
23
|
+
|
|
24
|
+
**On native Windows the bot now says which features do not work there and points to WSL2.** Claude and `/terminal` need
|
|
25
|
+
tmux, a self-update cannot restart the bot in place, hot mode refuses to start and the local speech engine cannot be
|
|
26
|
+
installed; at a cold start the bot prints one console line, posts one short notice in General (in the chat's language) and
|
|
27
|
+
shows the same line in `/doctor`, with a link to the new SETUP.md section «Windows (WSL2)»: install WSL, the config and
|
|
28
|
+
projects inside Linux, Windows programs from WSL, and a recipe for a PC with no monitor that starts the bot at Windows boot
|
|
29
|
+
without anyone logged in. A `/doctor` warning that has no hint no longer ends in a dangling dash.
|
|
30
|
+
|
|
31
|
+
**An answer tapped on a question the bot re-asked after an idle compaction is no longer lost when the tap lands while
|
|
32
|
+
the agent is being stopped.** The idle stop that follows the re-asked question could finish while the tap was being
|
|
33
|
+
answered; the chosen option was then kept for a session start that never came, and the agent never got it. The answer
|
|
34
|
+
now wakes the conversation as any message does.
|
|
35
|
+
|
|
36
|
+
## 3.5.0 — 2026-10-23
|
|
37
|
+
|
|
38
|
+
**You can see what an account billed to API credits has spent.** On the default Claude backend each turn's cost is
|
|
39
|
+
counted for such an account — only the part that turn added to the conversation, never a subscription turn's — and shown
|
|
40
|
+
under the account in `/accounts` and `/limits` (`💳 $12.34 spent since 23.10 · last turn $0.21`), on `/status`, when a
|
|
41
|
+
limit switches a topic onto it («billed per request … spent through the bot so far») and in its «credits used up»
|
|
42
|
+
notice. It is what the bot saw, not the Console's balance. A request on the account that goes through again also ends
|
|
43
|
+
its «credits used up» mark.
|
|
44
|
+
|
|
45
|
+
**An account billed to API credits never spends money unasked, and «Credit balance is too low» no longer waits for a
|
|
46
|
+
reset that never comes.** The `claude_profiles` recommendation and `charness claude-profiles --best` never name such an
|
|
47
|
+
account, the switch to another Claude account never offers it, and «🔄 Fill from connected» puts it last in the
|
|
48
|
+
fallback list and left out of automatic switching — tap it on the `/limits` board (`Claude · console 💳`), or include it.
|
|
49
|
+
When its credits run out, the topic gets one `💸` notice instead of hourly retries, the account is marked as out of
|
|
50
|
+
credits (`💸` in `/limits_autoswitch`, `/accounts`, `/limits`, `/status`) until you pick it or sign it in again, and the
|
|
51
|
+
fallback list takes over as for a limit: an available entry, a «Switch to …» button, or a wait for an entry that comes
|
|
52
|
+
back at a known time. An API rate limit (429) is still a short retry.
|
|
53
|
+
|
|
54
|
+
**A Claude account can bill the API credits of a Claude Console organization.** `/accounts` → «➕ Claude API account
|
|
55
|
+
(Console credits)» (or `/accounts add <name> api`) signs it in with `claude auth login --console`, which creates an API
|
|
56
|
+
key in that organization and keeps it in the account's own directory; or paste an existing API key while the sign-in
|
|
57
|
+
waits (on its own or as an `ANTHROPIC_API_KEY=…` line) — the message is deleted, the key is checked with the API and kept
|
|
58
|
+
for that account only, never in `state.json` or in an environment variable. A key pasted where the name is asked, or typed
|
|
59
|
+
on the `/accounts` command line, is deleted too. `/accounts` shows such an account as `💳 API · pay per use`, `/status` marks it
|
|
60
|
+
`💳 API`, and picking it says every request is billed. Your subscription accounts are unchanged: they still never
|
|
61
|
+
see an `ANTHROPIC_API_KEY`. Any `sk-ant-…` text is redacted from the output trace.
|
|
62
|
+
|
|
5
63
|
## 3.4.0 — 2026-10-23
|
|
6
64
|
|
|
7
65
|
**Charness now updates itself.** Every hour the bot checks for a new version; when nobody is using it — no agent working,
|
package/DEVELOPMENT.md
CHANGED
|
@@ -125,7 +125,8 @@ yarn build # tsc → dist/ (tsconfig.json; src/__tests__ is not emitted)
|
|
|
125
125
|
yarn test # test:unit, then test:flows (node test runner + tsx); build
|
|
126
126
|
# first — some tests exercise the built dist/cli.js
|
|
127
127
|
yarn test:unit # every file but the flows; each file must finish in 2 min
|
|
128
|
-
yarn test:flows # the `*E2e.test.ts` flows
|
|
128
|
+
yarn test:flows # the `*E2e.test.ts` flows, 16 files at a time (they mostly wait
|
|
129
|
+
# on their fakes, not the CPU); each file must finish in 20 min
|
|
129
130
|
yarn hot # hot-reload mode: tsc -w + nodemon on dist/ (also
|
|
130
131
|
# `charness hot` from anywhere) — a broken edit can't
|
|
131
132
|
# take the bot down; OpenCode generations stay outside the
|
|
@@ -145,6 +146,16 @@ A flow's own time budget is that same limit less a teardown reserve (`getFlowDea
|
|
|
145
146
|
`--test-timeout`), so a flow that runs long fails first, at the wait that is late and with charness's output tail,
|
|
146
147
|
not with the runner's bare timeout. Run one flow directly with `--test-timeout=<ms>`.
|
|
147
148
|
|
|
149
|
+
A flow does not wait out the production windows in real time: its env file sets `CHARNESS_TEST_TIMINGS`, a JSON object
|
|
150
|
+
of named overrides (`src/testTimings.ts`: `telegramSendIntervalMs` — the global send pacer's 2 s, `jiraPollIntervalMs` —
|
|
151
|
+
below the 10 s floor of `jira.json`, `requestSweepIntervalMs` and `requestFirstPostRetryDelayMs` — the wake-up engine's
|
|
152
|
+
minutes, `limitProbeDelayMs` — the two-minute probe of an unconfirmed usage limit, `stateSaveDebounceMs` — `state.json`'s
|
|
153
|
+
500 ms save debounce, `openCodeServerUpBudgetMs` — the OpenCode server's 60 s of retries, `backgroundHoldMaxMs` — the 6 h the
|
|
154
|
+
agent's own pending work holds a json-stream process). It is the ONE test-only timing seam,
|
|
155
|
+
never a setting: unset, every window is the production one; a value may only shorten its window (each key is capped at its
|
|
156
|
+
production value); set, the boot prints `[test-timings] … overrides active`, and
|
|
157
|
+
an unknown key or a bad value stops the start naming it.
|
|
158
|
+
|
|
148
159
|
The Docker dev loop (never `docker compose restart` — it ignores
|
|
149
160
|
`depends_on`):
|
|
150
161
|
|
package/README.md
CHANGED
|
@@ -80,6 +80,11 @@ The bot serves two Telegram surfaces:
|
|
|
80
80
|
|
|
81
81
|
Full setup — Telegram, Jira, environment, updating: [SETUP.md](SETUP.md)
|
|
82
82
|
|
|
83
|
+
**Platforms:** Linux, macOS, and Windows via WSL2 — on Windows WSL2 is the recommended and fully supported way
|
|
84
|
+
([Windows (WSL2)](SETUP.md#windows-wsl2), with a recipe for an always-on PC with no monitor). Native Windows works with
|
|
85
|
+
limits: OpenCode runs, but Claude and `/terminal` need tmux, a self-update cannot restart the bot in place, hot mode refuses
|
|
86
|
+
to start and the local speech engine cannot be installed. A native Windows bot says so at start (console, General, `/doctor`).
|
|
87
|
+
|
|
83
88
|
## Reference
|
|
84
89
|
|
|
85
90
|
### Commands (Telegram)
|
|
@@ -95,7 +100,7 @@ actually start an agent or terminal in the folder.
|
|
|
95
100
|
| `/terminal` | Open a raw `$SHELL` in the bound folder — see [Raw terminal](#raw-terminal-terminal-telegram) |
|
|
96
101
|
| `/claude_mode` | Switch this topic's Claude backend (json-stream ⇄ per-turn ⇄ tmux-scrape); bare shows a picker — see [Claude Code backends](#claude-code-backends-claude_mode) |
|
|
97
102
|
| `/model` | Switch model through a two-level picker: providers, then that provider's models (10 per page). On OpenCode the first row is 🕘 Recently used — a flat list of the models picked before, across providers (bot-wide, most recent first, up to 10), one tap to switch; it appears once something has been picked. Each provider row has a 🙈 to hide it from the picker (bot-wide, persisted; 👁 brings it back) — `/model <provider/model>` still reaches a hidden provider. If OpenCode is waiting on an old provider's retry, the next prompt interrupts that wait and starts with the selected model instead of sitting queued behind it |
|
|
98
|
-
| `/accounts` | Every account the bot works with, on one screen. **Claude accounts** (when you keep several subscriptions signed in): each profile with its email, plan (personal Pro/Max or corporate Team/Enterprise), how many topics use it and its usage windows, ✓ on the active one; tap a profile to switch this topic (a running session is restarted under it, same conversation — at the end of its turn if it is working; on a topic that runs OpenCode the tap switches the topic to Claude on that account, after a confirmation when there is a conversation to hand over); in **General** the pick becomes the default for all topics. The title says where the topic really runs (`Claude «work»`, `OpenCode openai/gpt-5`) and the ✓ sits on that row — on no account while OpenCode runs. Per profile: 🔑 signs it in again, 🗑 removes it (after a confirmation; for `default`, which IS `~/.claude`, «remove» signs it out — see below). The usage lines give each window's percentage USED and when it resets as a time from now (`5h 5% 🔄 in 4h · week 20% 🔄 in 5d 8h · Fable 4% 🔄 in 5d 8h`; «🔄 in 2h», «🔄 in 2d 1h», «🔄 in 30m»; Fable only on a plan that has its own weekly window); when the 5h window's reset is unknown the weekly one is shown in its place, and with no reset at all only the percentage. The hint paragraph under the account list says the percentages are used. «➕ Claude account» asks for a name and runs the sign-in. **OpenCode providers**: the connected ones are listed with how each is signed in (`ChatGPT subscription` with its ~5 h / ~7 d usage windows like an account's, `API key`). In a topic a provider row is `[provider] [🔑] [🔌]`: a tap switches the topic to the provider through its model page (✓ on the model it runs, ⛔ on a limited one; a provider with one model is picked at once), 🔑 connects it again, 🔌 disconnects it (it asks, then removes the stored credentials; a provider OpenCode enables from an environment variable, e.g. `OPENROUTER_API_KEY`, stays active — the reply says so and points at hiding it in `/model`); in **General** a provider row is «🔌 Disconnect <provider>» and 🔑 only; «➕ OpenCode provider» asks for the provider id and continues with the connect flow (OAuth methods when available, otherwise an API key whose message is deleted from Telegram). What happens when an account hits a usage limit is set in `/limits_autoswitch`. Text forms: `/accounts add <name>`, `/accounts use <name>`, `/accounts use <provider>/<model>` (an OpenCode model, in a topic), `/accounts login <name>`, `/accounts remove <name>`, `/accounts autoswitch on\|off`. `/account`, `/connect [provider [key]]` and `/disconnect [provider]` still work as hidden aliases (the menu lists only `/accounts`). See [Several accounts](#several-accounts-accounts) |
|
|
103
|
+
| `/accounts` | Every account the bot works with, on one screen. **Claude accounts** (when you keep several subscriptions signed in): each profile with its email, plan (personal Pro/Max or corporate Team/Enterprise), how many topics use it and its usage windows, ✓ on the active one; tap a profile to switch this topic (a running session is restarted under it, same conversation — at the end of its turn if it is working; on a topic that runs OpenCode the tap switches the topic to Claude on that account, after a confirmation when there is a conversation to hand over); in **General** the pick becomes the default for all topics. The title says where the topic really runs (`Claude «work»`, `OpenCode openai/gpt-5`) and the ✓ sits on that row — on no account while OpenCode runs. Per profile: 🔑 signs it in again, 🗑 removes it (after a confirmation; for `default`, which IS `~/.claude`, «remove» signs it out — see below). The usage lines give each window's percentage USED and when it resets as a time from now (`5h 5% 🔄 in 4h · week 20% 🔄 in 5d 8h · Fable 4% 🔄 in 5d 8h`; «🔄 in 2h», «🔄 in 2d 1h», «🔄 in 30m»; Fable only on a plan that has its own weekly window); when the 5h window's reset is unknown the weekly one is shown in its place, and with no reset at all only the percentage. The hint paragraph under the account list says the percentages are used. «➕ Claude account» asks for a name and runs the sign-in; «➕ Claude API account (Console credits)» adds an account billed to a Claude Console organization's API credits (`💳`, see [An account billed to API credits](#an-account-billed-to-api-credits-claude-console)). **OpenCode providers**: the connected ones are listed with how each is signed in (`ChatGPT subscription` with its ~5 h / ~7 d usage windows like an account's, `API key`). In a topic a provider row is `[provider] [🔑] [🔌]`: a tap switches the topic to the provider through its model page (✓ on the model it runs, ⛔ on a limited one; a provider with one model is picked at once), 🔑 connects it again, 🔌 disconnects it (it asks, then removes the stored credentials; a provider OpenCode enables from an environment variable, e.g. `OPENROUTER_API_KEY`, stays active — the reply says so and points at hiding it in `/model`); in **General** a provider row is «🔌 Disconnect <provider>» and 🔑 only; «➕ OpenCode provider» asks for the provider id and continues with the connect flow (OAuth methods when available, otherwise an API key whose message is deleted from Telegram). What happens when an account hits a usage limit is set in `/limits_autoswitch`. Text forms: `/accounts add <name>`, `/accounts add <name> api`, `/accounts use <name>`, `/accounts use <provider>/<model>` (an OpenCode model, in a topic), `/accounts login <name>`, `/accounts remove <name>`, `/accounts autoswitch on\|off`. `/account`, `/connect [provider [key]]` and `/disconnect [provider]` still work as hidden aliases (the menu lists only `/accounts`). See [Several accounts](#several-accounts-accounts) |
|
|
99
104
|
| `/effort` | Reasoning effort. **In a topic:** shows the level its session runs and where the next start's level comes from («medium — as in General» / «high — set for this topic»; a running session keeps the level it started with when General moves on: «xhigh (medium from the next start) — as in General») with a button per level — Claude: the native `/effort` levels (`low…ultracode`); OpenCode: the current model's variants, applied per-prompt; General's `ultracode` is the model's highest variant and `auto` sends no variant — and «As in General (medium)», named with the level the topic will really get (OpenCode: rounded to the model's variants), which drops the topic's own pick (a live session is brought to the default at once). **In General:** the default effort for every topic that has no effort of its own (✓ on the current one; `medium` until you pick), saved to `settings.json` and applied from each topic's next session start or wake-up — running sessions are not restarted. No env configuration — see [Instance settings](SETUP.md#instance-settings-settingsjson) |
|
|
100
105
|
| `/verbosity` | Output-verbosity macro (`minimal\|short\|full`): sets the thinking, tool-results and sub-agent display prefs at once; `/thinking`, `/tool_results`, `/subagent` point-override afterwards. Mixed prefs show as "custom" in the picker. The picker's second row (or `/verbosity stream\|stream_answers\|answers`) sets what the topic SHOWS — "Stream only" (default), "Stream + answers" (the stream plus the agent's pinned answers) or "Answers only" (the pinned answers, no stream; an agent that keeps writing after its final answer is reminded once to send what matters); the answers are written like for a manager or QA — brief and to the point; a switch applies from the next message, and `/status` names the view |
|
|
101
106
|
| `/thinking` | Chain-of-thought display: `full` keeps the reasoning, `short` collapses to "💭 thought for Ns", `minimal` keeps only the live working cue |
|
|
@@ -105,7 +110,7 @@ actually start an agent or terminal in the folder.
|
|
|
105
110
|
| `/rename_session` | Rename the current live session (OpenCode; Claude transcripts have no title) |
|
|
106
111
|
| `/quit`, `/q` | End the session — Claude: graceful double Ctrl+C; OpenCode/terminal: `stopSession`. Releases the persisted session id, so a bot restart won't auto-reattach it (resume later via `/sessions`). A topic whose agent process is gone but whose session is kept (it "sleeps": the next message, file, voice note, `/compact`, `/model` or `/effort` resumes it by itself) is released the same way |
|
|
107
112
|
| `/new`, `/clear_session` | End the current session and start a fresh one (same adapter) |
|
|
108
|
-
| `/status` | This thread's status: agent, bound folder, the Claude account it runs on and the resolved workdir; a live session also shows the model it is actually running, the effort it runs (when the default moved on under it: «xhigh (medium from the next start)»), start time, runtime version and context use (anything the backend cannot report renders as unknown). On a Claude topic the account is its profile (name · email as in `/accounts`, marked ⛔ while it is at its limit, with a second row for a switch still pending); an OpenCode topic's model already names its provider |
|
|
113
|
+
| `/status` | This thread's status: agent, bound folder, the Claude account it runs on and the resolved workdir; a live session also shows the model it is actually running, the effort it runs (when the default moved on under it: «xhigh (medium from the next start)»), start time, runtime version and context use (anything the backend cannot report renders as unknown). On a Claude topic the account is its profile (name · email as in `/accounts`, `· 💳 API` for an account billed to API credits — with a line of what it spent through the bot — marked ⛔ while it is at its limit, with a second row for a switch still pending); an OpenCode topic's model already names its provider |
|
|
109
114
|
| `/output` | Last 500 lines of agent output (sent as at most 5 chunks; the overflow is reported as omitted) |
|
|
110
115
|
| `/c`, `/y`, `/n` | Ctrl+C / "y" / "n" |
|
|
111
116
|
| `/enter`, `/up`, `/down`, `/tab` | tmux key passthrough |
|
|
@@ -118,8 +123,8 @@ actually start an agent or terminal in the folder.
|
|
|
118
123
|
| `/compact` | Compact the agent's context — a real, confirmed compaction on OpenCode and on the default (stream) Claude backend; the tmux Claude backend has the literal command forwarded (its TUI compacts); terminal: not supported |
|
|
119
124
|
| `/compact_on_idle` | Toggle auto-compaction after ~55 min idle (Enable/Disable picker; `AGENT_IDLE_MINUTES` sets the window). Per topic; run it in **General** to set the default for all topics. On by default. Each idle compaction posts one short notice; the picker's «Show summary» / «Hide summary» row (the `/compact_summary` switch) adds the agent's full summary after it. Fires at most once per active period (until you write again); if a question was pending it is re-asked with tappable buttons after the compaction. On the json-stream backends the idle mark also STOPS the agent process, compaction or not — the topic sleeps and your next message resumes it (`/status` says so) |
|
|
120
125
|
| `/compact_summary` | Toggle posting the agent's full summary after a compaction — `/compact`, the idle compaction and the agent's own `compact_conversation` (Enable/Disable picker). Per topic; run it in **General** to set the default for all topics. Off by default: a compaction ends in a short line. On OpenCode it also hides the summary OpenCode streams itself |
|
|
121
|
-
| `/limits` | The **switch board**: every signed-in Claude account and every connected OpenCode provider on one screen. The **message** carries the figures — `Usage (% used)` and a line per account in the same wording `/accounts` uses (`default — ⏱ 5h 14% 🔄 in 3h · week 3% 🔄 in 6d`; `⛔ at its limit until …` while the account is limited; `(stale)` when the last poll could not refresh), a provider signed in through a ChatGPT subscription after them (`openai (ChatGPT) — ⏱ 5h 12% 🔄 in 3h · week 40% 🔄 in 4d`), the two automatic-switching settings' state with a link to `/limits_autoswitch`, and the caption «Choose the active model:». The **buttons** are one full-width name each — `✓ Claude · work`, `Claude · default`, `OpenCode · openai` — with no figure on them (the figures do not fit a phone's button); ✓ marks where the topic runs; a signed-out account is not listed. **A tap switches this topic**: another Claude account (the running session restarts under it, same conversation — at the end of its turn when it is working), an OpenCode provider (opens its model page, 10 models per page with ⛔ on a limited one; a provider with one model switches at once), the ✓ row (the model page of where the topic runs: change the model). Across backends with a conversation to hand over the bot asks first (✅ Continue / ✖️ Cancel); while a usage-limit wait is armed the tap is the limit move — the wait ends and the work continues, no question — and «⏭ Skip once» is offered. An unbound topic is told to bind a folder first. In **General** a Claude row sets the default account for all topics (the caption reads «Choose the default Claude account:»), and an OpenCode row only says that a model is picked inside a topic. The limit notice names this command. The old text forms `/limits rebuild`, `/limits clear`, `/limits switch …`, `/limits wait …` still work (they are `/limits_autoswitch`'s verbs). See [Usage limits and the fallback models](#usage-limits-and-the-fallback-models-limits-and-limits_autoswitch) |
|
|
122
|
-
| `/limits_autoswitch` | The setup of automatic switching on a usage limit, with a short text: a title, what the topic runs on and where its two settings come from. **Fallback models** — an ordered list of Claude accounts (with an optional model) and OpenCode providers with a model, top = preferred, where a topic continues when its agent hits a limit. One full-width button per entry (`✓ 1. Claude · work · opus`, `2. Claude · default ⛔ 14:00`, `3. OpenCode · openai · gpt-5 🚫` — ✓ where the topic runs, ⛔ limited until, 🔒 not signed in, ❓ no longer offered, 🚫 left out of automatic switching); a tap opens that entry in the same message with ⬆️ Up / ⬇️ Down, 🚫 Don't use / ✅ Use (a left-out entry stays usable by hand through `/model` and `/accounts`), 🗑 Remove and ⬅️ Back. Below the list: «➕ Add» (account or provider, then the model; an entry of a backend the list does not use yet — an OpenCode model next to Claude accounts, or the reverse — asks first, because a limit then hands a conversation over in compacted form; including a 🚫 entry that does that asks the same way), «🔄 Fill from connected» (a draft from what is connected; entries you left out stay left out; asks first when a list exists), «🗑 Clear». The list is ONE instance-wide list and is editable from every topic. **Two settings** decide what a limit does: «🔁 Auto switch» — auto switch to a fallback model (OFF by default: the limit notice carries «🔁 Switch to …» buttons instead) and «⏳ Wait for reset» — wait for the reset and continue (ON by default; while a wait is armed the topic screen also offers «⏭ Skip once»). In General they are the defaults for all topics and each is one button that flips the value. In a regular topic each is one short full-width button showing the effective value (`🔁 Auto switch: ON`; one line of text above the list says where each value comes from — `🔁 as in General · ⏳ set for this topic`); a tap opens the setting in the same message with ON / OFF / «As in General (<the General value>)» — the topic's own choice marked ✓ — and ⬅️ Back, and a choice applies and returns to the screen. Switch ON: the topic moves to the first available entry at once. OFF: buttons on the notice, and if none is pressed the bot waits for the reset (waiting ON); both OFF: it only reports the limit. When every enabled entry, the current one included, is limited, the wait ends at the EARLIEST reset and the topic continues on that entry (the notice names it and the time). Text forms: `/limits_autoswitch rebuild`, `/limits_autoswitch clear`, `/limits_autoswitch switch on\|off\|same`, `/limits_autoswitch wait on\|off\|same` (the same verbs work after `/limits`). `/fallback` and `/auto_continue_limits [on\|off]` still work as hidden aliases. See [Usage limits and the fallback models](#usage-limits-and-the-fallback-models-limits-and-limits_autoswitch) |
|
|
126
|
+
| `/limits` | The **switch board**: every signed-in Claude account and every connected OpenCode provider on one screen. The **message** carries the figures — `Usage (% used)` and a line per account in the same wording `/accounts` uses (`default — ⏱ 5h 14% 🔄 in 3h · week 3% 🔄 in 6d`; `⛔ at its limit until …` while the account is limited; `(stale)` when the last poll could not refresh), a provider signed in through a ChatGPT subscription after them (`openai (ChatGPT) — ⏱ 5h 12% 🔄 in 3h · week 40% 🔄 in 4d`), the two automatic-switching settings' state with a link to `/limits_autoswitch`, and the caption «Choose the active model:». The **buttons** are one full-width name each — `✓ Claude · work`, `Claude · default`, `OpenCode · openai`, `Claude · console 💳` for an account billed to API credits — with no figure on them (the figures do not fit a phone's button); ✓ marks where the topic runs; a signed-out account is not listed. **A tap switches this topic**: another Claude account (the running session restarts under it, same conversation — at the end of its turn when it is working), an OpenCode provider (opens its model page, 10 models per page with ⛔ on a limited one; a provider with one model switches at once), the ✓ row (the model page of where the topic runs: change the model). Across backends with a conversation to hand over the bot asks first (✅ Continue / ✖️ Cancel); while a usage-limit wait is armed the tap is the limit move — the wait ends and the work continues, no question — and «⏭ Skip once» is offered. An unbound topic is told to bind a folder first. In **General** a Claude row sets the default account for all topics (the caption reads «Choose the default Claude account:»), and an OpenCode row only says that a model is picked inside a topic. The limit notice names this command. The old text forms `/limits rebuild`, `/limits clear`, `/limits switch …`, `/limits wait …` still work (they are `/limits_autoswitch`'s verbs). See [Usage limits and the fallback models](#usage-limits-and-the-fallback-models-limits-and-limits_autoswitch) |
|
|
127
|
+
| `/limits_autoswitch` | The setup of automatic switching on a usage limit, with a short text: a title, what the topic runs on and where its two settings come from. **Fallback models** — an ordered list of Claude accounts (with an optional model) and OpenCode providers with a model, top = preferred, where a topic continues when its agent hits a limit. One full-width button per entry (`✓ 1. Claude · work · opus`, `2. Claude · default ⛔ 14:00`, `3. OpenCode · openai · gpt-5 🚫` — ✓ where the topic runs, ⛔ limited until, 🔒 not signed in, ❓ no longer offered, 💸 API credits used up, 🚫 left out of automatic switching); a tap opens that entry in the same message with ⬆️ Up / ⬇️ Down, 🚫 Don't use / ✅ Use (a left-out entry stays usable by hand through `/model` and `/accounts`), 🗑 Remove and ⬅️ Back. Below the list: «➕ Add» (account or provider, then the model; an entry of a backend the list does not use yet — an OpenCode model next to Claude accounts, or the reverse — asks first, because a limit then hands a conversation over in compacted form; including a 🚫 entry that does that asks the same way), «🔄 Fill from connected» (a draft from what is connected; entries you left out stay left out; an account billed to API credits comes last and left out unless you had included it; asks first when a list exists), «🗑 Clear». The list is ONE instance-wide list and is editable from every topic. **Two settings** decide what a limit does: «🔁 Auto switch» — auto switch to a fallback model (OFF by default: the limit notice carries «🔁 Switch to …» buttons instead) and «⏳ Wait for reset» — wait for the reset and continue (ON by default; while a wait is armed the topic screen also offers «⏭ Skip once»). In General they are the defaults for all topics and each is one button that flips the value. In a regular topic each is one short full-width button showing the effective value (`🔁 Auto switch: ON`; one line of text above the list says where each value comes from — `🔁 as in General · ⏳ set for this topic`); a tap opens the setting in the same message with ON / OFF / «As in General (<the General value>)» — the topic's own choice marked ✓ — and ⬅️ Back, and a choice applies and returns to the screen. Switch ON: the topic moves to the first available entry at once. OFF: buttons on the notice, and if none is pressed the bot waits for the reset (waiting ON); both OFF: it only reports the limit. When every enabled entry, the current one included, is limited, the wait ends at the EARLIEST reset and the topic continues on that entry (the notice names it and the time). Text forms: `/limits_autoswitch rebuild`, `/limits_autoswitch clear`, `/limits_autoswitch switch on\|off\|same`, `/limits_autoswitch wait on\|off\|same` (the same verbs work after `/limits`). `/fallback` and `/auto_continue_limits [on\|off]` still work as hidden aliases. See [Usage limits and the fallback models](#usage-limits-and-the-fallback-models-limits-and-limits_autoswitch) |
|
|
123
128
|
| `/bind` | Bare: current binding + folder picker, with «leave current dir» (the old `/unbind`) and «create new folder» buttons |
|
|
124
129
|
| `/topic [name]` | Open another forum topic on a folder, without leaving this one. Asks the new topic's name (`/topic <name>` takes it from the command), then the folder: «📁 <folder> (as here)» — the folder this topic is bound to — or «📂 Other folder», which is the `/bind` picker with «create new folder»; in General and in an unbound topic the picker comes at once. Then the bot creates the topic (it needs the `can_manage_topics` right — `/doctor` checks it), binds it, shows the usual binding welcome there and turns the question into the answer with an «Open topic →» button. «✖️ Cancel» on the question and in the picker, and `/esc` at any step, end it. No agent starts by itself: start one in the new topic as in any bound topic. Works in the group only — in the bot's private chat it says so |
|
|
125
130
|
| `/mcp` | List MCP servers active for this thread |
|
|
@@ -174,7 +179,17 @@ backends:
|
|
|
174
179
|
output, and it survives bot restarts (the bot re-adopts the process and
|
|
175
180
|
replays what was produced during the downtime). After ~55 min idle
|
|
176
181
|
(`AGENT_IDLE_MINUTES`) the process is stopped — right after the idle
|
|
177
|
-
compaction when `/compact_on_idle` allows one
|
|
182
|
+
compaction when `/compact_on_idle` allows one — but never while the agent's
|
|
183
|
+
own work is pending: a background command, Monitor or sub-agent it started, a
|
|
184
|
+
finished task's result it is about to read, a `CronCreate` job or a `/loop`
|
|
185
|
+
wakeup it scheduled.
|
|
186
|
+
That work keeps the process for up to 6 h after the agent's last turn, so a
|
|
187
|
+
task nobody hears from does not keep it forever. When that work is lost
|
|
188
|
+
anyway (a crash, a process gone after a restart, a `/model` / `/effort` /
|
|
189
|
+
account restart, a task let go after 6 h), the topic gets one plain line and
|
|
190
|
+
the agent is resumed with a note to check that work again — or, after a
|
|
191
|
+
forgotten task or a second loss within 10 minutes, your next message carries
|
|
192
|
+
the note. The **default**.
|
|
178
193
|
- **per-turn** (`/claude_mode per-turn`) — the same host, stopped right
|
|
179
194
|
after every answer. Nothing is lost: the conversation sleeps and your next
|
|
180
195
|
message resumes it. Needs Claude Code 2.1.287 or newer; the switch is
|
|
@@ -295,7 +310,8 @@ so one account's usage limit does not stop every topic.
|
|
|
295
310
|
`claude_profiles` — optionally with the model it will run — and gets every
|
|
296
311
|
profile with its windows plus `recommended`: the signed-in, unlimited profile
|
|
297
312
|
with the most headroom (the session window and the shared weekly window bound
|
|
298
|
-
every model; Fable additionally drains its own weekly window)
|
|
313
|
+
every model; Fable additionally drains its own weekly window) — never an
|
|
314
|
+
account billed to API credits. It then
|
|
299
315
|
starts the child with `CLAUDE_CONFIG_DIR=<configDir>` (`null` = the default
|
|
300
316
|
profile: set nothing). The same answer from a shell, for a worker without
|
|
301
317
|
MCP: `charness claude-profiles [--best] [--model <name>] [--json]` —
|
|
@@ -303,6 +319,73 @@ so one account's usage limit does not stop every topic.
|
|
|
303
319
|
`CLAUDE_CONFIG_DIR="$(charness claude-profiles --best --model fable)"`
|
|
304
320
|
works as is; it reads the bot's cache off `DATA_DIR/state.json`.
|
|
305
321
|
|
|
322
|
+
#### An account billed to API credits (Claude Console)
|
|
323
|
+
|
|
324
|
+
Besides subscriptions, an account can bill the **API credits of a Claude Console
|
|
325
|
+
organization** — useful as a fallback when every subscription is at its limit.
|
|
326
|
+
Such an account pays per request (no 5-hour or weekly windows: its limit is the
|
|
327
|
+
balance), so the bot never uses it without you choosing it.
|
|
328
|
+
|
|
329
|
+
- **Adding one:** `/accounts` → «➕ Claude API account (Console credits)» (or
|
|
330
|
+
`/accounts add console api`) — give it a name. The bot runs
|
|
331
|
+
`claude auth login --console` for it: open the link, sign in to the Console
|
|
332
|
+
organization whose credits it should spend and paste the code back. Claude
|
|
333
|
+
Code then creates an API key in that organization's workspace and keeps it in
|
|
334
|
+
the account's own directory (`DATA_DIR/claude-profiles/<name>/`), where only
|
|
335
|
+
that account's sessions read it. Your Console role must allow creating a key
|
|
336
|
+
(«Claude Code» or «Developer»).
|
|
337
|
+
- **Or paste a key:** while that sign-in waits, paste an existing API key
|
|
338
|
+
(`sk-ant-api…`, on its own or as an `ANTHROPIC_API_KEY=…` line) instead of the
|
|
339
|
+
code. The message is deleted at once (so is a key pasted where the name is
|
|
340
|
+
asked, or typed on the `/accounts` command line), the key
|
|
341
|
+
is checked with the API (a refused key is not stored), then kept in the
|
|
342
|
+
account's directory (`0600`) and handed to its sessions only, through an
|
|
343
|
+
`apiKeyHelper` in that account's launch settings — never in `state.json`, never
|
|
344
|
+
in an environment variable an agent or a child process could read, never in
|
|
345
|
+
the trace (any `sk-ant-…` text is redacted from it). `/accounts login <name>`
|
|
346
|
+
of an API account runs the same flow again; a later Console sign-in replaces
|
|
347
|
+
a pasted key.
|
|
348
|
+
- The Console's keyless sign-in («Sign in with your Console account» in
|
|
349
|
+
Claude Code's own `/login`) is **not** used: it is stored outside the account's
|
|
350
|
+
directory, for the whole machine, and signs the subscriptions out.
|
|
351
|
+
- Every other account stays a subscription exactly as before: the bot still
|
|
352
|
+
removes any `ANTHROPIC_API_KEY` from the environment of every session and of
|
|
353
|
+
every sign-in.
|
|
354
|
+
- **Recognising one:** `/accounts` shows it as `console — you@example.com, 💳 API
|
|
355
|
+
· pay per use` (`💳 API key · pay per use` for a pasted key), `/status` marks
|
|
356
|
+
the account `· 💳 API`, and picking it — in a topic, or as the default in
|
|
357
|
+
General — says that every request is billed. The bot reads the billing from
|
|
358
|
+
`claude auth status` each time it checks the account, so a mistaken
|
|
359
|
+
subscription login shows as a subscription.
|
|
360
|
+
- **Never used by itself.** It has no windows, so to every headroom rule it
|
|
361
|
+
would look inexhaustible: the `claude_profiles` recommendation (and
|
|
362
|
+
`charness claude-profiles --best`) never names it, the within-Claude switch
|
|
363
|
+
never offers it, and «🔄 Fill from connected» in `/limits_autoswitch` puts it
|
|
364
|
+
LAST in the fallback list and left out of automatic switching (🚫). To use it,
|
|
365
|
+
tap it on the `/limits` board (`Claude · console 💳`) — or include its entry
|
|
366
|
+
(«✅ Use») so a limit notice offers it, or switches to it by itself with
|
|
367
|
+
switching on. A rebuilt list keeps it included once you did.
|
|
368
|
+
- **When the credits run out** («Credit balance is too low») the bot does not
|
|
369
|
+
wait for a reset — there is none — and never retries hourly. The topic gets
|
|
370
|
+
one `💸` notice, the account is marked as out of credits (`💸` in
|
|
371
|
+
`/limits_autoswitch`, `/accounts`, `/limits` and `/status`) and the fallback
|
|
372
|
+
list decides as for a limit: switching on moves the topic at once, otherwise
|
|
373
|
+
the notice carries «🔁 Switch to …» buttons. If nothing can take over now but
|
|
374
|
+
another entry comes back at a known time (a subscription's weekly reset), the
|
|
375
|
+
topic waits for that and continues there; otherwise the notice is all. The
|
|
376
|
+
mark lasts until you act: pick the account again (after topping the balance
|
|
377
|
+
up), or sign it in again — or until a request on it goes through again. A
|
|
378
|
+
rate limit of the API (429) is still a short retry.
|
|
379
|
+
- **What it spent.** On the default (stream) backend every turn reports the
|
|
380
|
+
conversation's running cost; the bot counts the part each turn on the API
|
|
381
|
+
account added (a subscription turn of the same conversation is never
|
|
382
|
+
counted) and shows the total under the account in `/accounts` and `/limits`
|
|
383
|
+
(`💳 $12.34 spent since 23.10 · last turn $0.21`), on `/status`, in the line a
|
|
384
|
+
limit switch onto the account adds («billed per request … $X spent through
|
|
385
|
+
the bot so far») and in its «credits used up» notice. It is what the bot
|
|
386
|
+
saw, not the organization's balance — that one is on the Console's Billing
|
|
387
|
+
page. The tmux backend reports no cost.
|
|
388
|
+
|
|
306
389
|
### Usage limits and the fallback models (`/limits` and `/limits_autoswitch`)
|
|
307
390
|
|
|
308
391
|
When a topic's agent stops on a usage limit — a Claude account window, an
|
package/SETUP.md
CHANGED
|
@@ -14,7 +14,8 @@ The bot runs anywhere the agent CLIs run:
|
|
|
14
14
|
- **Your own working machine** — the simplest start: launch `charness`
|
|
15
15
|
from your projects parent folder and your laptop/desktop becomes the
|
|
16
16
|
backend. The catch: agents are only reachable while the machine is on and
|
|
17
|
-
awake.
|
|
17
|
+
awake. On Windows that machine runs Charness inside
|
|
18
|
+
WSL2 — see [Windows (WSL2)](#windows-wsl2).
|
|
18
19
|
- **A remote server (cloud VPS/VDS or bare metal)** — the more convenient
|
|
19
20
|
setup: organize your working environment there once (git + your repos,
|
|
20
21
|
Claude Code / OpenCode, `charness`) and you get an always-on dev box you
|
|
@@ -44,6 +45,203 @@ at the old path) and says so; every old name still in use prints one deprecation
|
|
|
44
45
|
line. What it does, its limits and the steps by hand:
|
|
45
46
|
[MIGRATION_RENAME_TO_CHARNESS.md](MIGRATION_RENAME_TO_CHARNESS.md).
|
|
46
47
|
|
|
48
|
+
### Windows (WSL2)
|
|
49
|
+
|
|
50
|
+
**On Windows run Charness inside WSL2** — a real Linux, where everything works. **Native Windows** (`npm install -g charness`
|
|
51
|
+
in PowerShell) stays allowed but is limited: Claude and `/terminal` need tmux, a self-update cannot restart the bot in place
|
|
52
|
+
(no `process.execve`; the bot only says that a new version is out), hot mode refuses to start and the local speech engine's
|
|
53
|
+
installer is POSIX-only. A native Windows bot says so itself: on the console at start, once in General per cold start, and in
|
|
54
|
+
`/doctor`.
|
|
55
|
+
|
|
56
|
+
#### Install WSL and Charness
|
|
57
|
+
|
|
58
|
+
1. In an **administrator** PowerShell: `wsl --install`, then restart Windows. It needs Windows 10 2004+ or Windows 11 and
|
|
59
|
+
installs Ubuntu; its first launch asks for a Linux user name and password. `wsl --list --verbose` must show `VERSION 2`
|
|
60
|
+
and `wsl --update` brings WSL itself up to date ([Microsoft: install WSL](https://learn.microsoft.com/en-us/windows/wsl/install)).
|
|
61
|
+
2. Inside Ubuntu (Start menu → Ubuntu, or `wsl` in PowerShell):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
sudo apt update && sudo apt install -y tmux git curl
|
|
65
|
+
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash # then open a new Ubuntu window
|
|
66
|
+
nvm install 22 # 22.15+ is what a self-update needs to restart the bot in place (check: node -v)
|
|
67
|
+
npm install -g charness
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The bot installs the agents on first use (`/claude`, `/opencode`); to have them up front: `npm install -g @anthropic-ai/claude-code opencode-ai`.
|
|
71
|
+
|
|
72
|
+
#### Config, projects, topics
|
|
73
|
+
|
|
74
|
+
- **Config** is `~/.config/charness/.env` **inside Ubuntu** (not the Windows one) — [Telegram setup](#telegram-setup). Coming from a
|
|
75
|
+
native Windows install: stop that bot first (a token has one long-poller), copy the file and fix every Windows path in it
|
|
76
|
+
(`WORK_ROOT`, `DATA_DIR`, `CLAUDE_BIN`, …):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
mkdir -p ~/.config/charness && cp /mnt/c/Users/<name>/.config/charness/.env ~/.config/charness/.env
|
|
80
|
+
```
|
|
81
|
+
- **Projects live in the Linux filesystem** — `~/projects/<repo>`, not under `/mnt/c`. Microsoft: «For the fastest performance
|
|
82
|
+
speed, store your files in the WSL file system» ([Working across file systems](https://learn.microsoft.com/en-us/windows/wsl/filesystems));
|
|
83
|
+
`/mnt/c` is the Windows disk reached through WSL's DrvFs bridge, slower for git and builds. Windows reaches them at
|
|
84
|
+
`\\wsl.localhost\Ubuntu\home\<user>\projects` (type it into Explorer's address bar; `\\wsl$\Ubuntu\…` is the older form),
|
|
85
|
+
and VS Code opens them through its [WSL extension](https://code.visualstudio.com/docs/remote/wsl): `code .` in the project
|
|
86
|
+
folder in Ubuntu. Run the bot from the projects parent: `cd ~/projects && charness`.
|
|
87
|
+
- **Topics start over.** The WSL bot has its own `DATA_DIR`, so it knows none of the old topic bindings: run `/bind <folder>`
|
|
88
|
+
in each topic again (the folder must exist under the WSL `WORK_ROOT`: `git clone` the repository there, or
|
|
89
|
+
`cp -r /mnt/c/Users/<name>/projects/<repo> ~/projects/`). Claude conversations of the native install stay
|
|
90
|
+
in Windows' `%USERPROFILE%\.claude` and cannot be resumed from WSL.
|
|
91
|
+
|
|
92
|
+
#### Windows programs from WSL (interop)
|
|
93
|
+
|
|
94
|
+
Interop is on by default (`[interop] enabled=true` in `/etc/wsl.conf`). A Windows program starts from Ubuntu by name **with its
|
|
95
|
+
extension**, as the active Windows user ([Microsoft: run Windows tools from Linux](https://learn.microsoft.com/en-us/windows/wsl/filesystems#run-windows-tools-from-linux)),
|
|
96
|
+
and `wslpath` — a tool that comes with WSL (`wslpath --help` lists its options) — converts paths between the two worlds:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
powershell.exe -NoProfile -Command "Get-Date"
|
|
100
|
+
cmd.exe /C dir
|
|
101
|
+
wslpath -w ~/projects/app # WSL path → Windows path, e.g. \\wsl.localhost\Ubuntu\home\…
|
|
102
|
+
wslpath -u 'C:\Users\me' # Windows path → WSL path (/mnt/c/Users/me)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Such a program has the rights of the WSL session, not administrator's. To run one elevated, start it through PowerShell:
|
|
106
|
+
`powershell.exe -Command "Start-Process <program> -Verb RunAs"` — Windows then asks for confirmation (UAC) on the desktop and
|
|
107
|
+
somebody has to click it. That works only when you opened WSL yourself at a logged-in desktop. **On the headless PC below it does not:**
|
|
108
|
+
the distro there is started by a scheduled task, nobody is logged in, there is no desktop to show the prompt on and nobody
|
|
109
|
+
to click it, so an elevated call from the bot fails or hangs. Do not build on elevated Windows programs there; do such a step by
|
|
110
|
+
hand at the PC.
|
|
111
|
+
|
|
112
|
+
#### A PC with no monitor as an always-on server
|
|
113
|
+
|
|
114
|
+
The goal: Windows boots, nobody logs in, the Linux that WSL runs (the *distro*, here Ubuntu) starts, systemd — Linux's service
|
|
115
|
+
manager — starts the bot, and nothing shuts the distro down. Four parts; the Windows half follows Microsoft's documentation (linked), so check a step against the page when your Windows build
|
|
116
|
+
behaves differently.
|
|
117
|
+
|
|
118
|
+
**1. systemd and the bot as a user service (inside Ubuntu).** The current Ubuntu from `wsl --install` has it on by default
|
|
119
|
+
(check with `systemctl status`); any other distro needs `/etc/wsl.conf` — `sudo nano /etc/wsl.conf`:
|
|
120
|
+
|
|
121
|
+
```ini
|
|
122
|
+
[boot]
|
|
123
|
+
systemd=true
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
then `wsl --shutdown` in PowerShell and open Ubuntu again. It needs WSL 0.67.6+ (`wsl --version`; `wsl --update`), and Microsoft
|
|
127
|
+
lists the `[boot]` section for Windows 11 and Windows Server 2022 ([systemd in WSL](https://learn.microsoft.com/en-us/windows/wsl/systemd),
|
|
128
|
+
[wsl.conf](https://learn.microsoft.com/en-us/windows/wsl/wsl-config#boot-settings)). Then write `~/.config/systemd/user/charness.service`:
|
|
129
|
+
|
|
130
|
+
```ini
|
|
131
|
+
[Unit]
|
|
132
|
+
Description=Charness
|
|
133
|
+
|
|
134
|
+
[Service]
|
|
135
|
+
WorkingDirectory=%h/projects
|
|
136
|
+
ExecStart=/bin/bash -c 'source %h/.nvm/nvm.sh && exec charness'
|
|
137
|
+
KillMode=process
|
|
138
|
+
Restart=on-failure
|
|
139
|
+
RestartSec=10
|
|
140
|
+
|
|
141
|
+
[Install]
|
|
142
|
+
WantedBy=default.target
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`nvm.sh` puts the nvm Node — and so `charness`, `claude` and `opencode` — on the service's `PATH`, which a service does not
|
|
146
|
+
inherit from your shell. `KillMode=process` is the one [Restart behaviour](README.md#restart-behaviour) asks for: systemd's
|
|
147
|
+
default would kill the agents' tmux and `opencode serve` with the bot, and while systemd marks `process` «not recommended»
|
|
148
|
+
([systemd.kill](https://www.freedesktop.org/software/systemd/man/latest/systemd.kill.html)), leaving them running is exactly
|
|
149
|
+
what lets a bot restart keep its agents. Enable it and let the user manager start without a login:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
systemctl --user daemon-reload && systemctl --user enable --now charness
|
|
153
|
+
sudo loginctl enable-linger "$USER" # "a user manager is spawned for the user at boot and kept around after logouts"
|
|
154
|
+
journalctl --user -u charness -f # the bot's log
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
([loginctl](https://www.freedesktop.org/software/systemd/man/latest/loginctl.html).)
|
|
158
|
+
|
|
159
|
+
**2. Keep the distro running.** systemd services do **not** keep a WSL instance alive: «systemd services will NOT keep your WSL
|
|
160
|
+
instance alive» ([systemd in WSL](https://learn.microsoft.com/en-us/windows/wsl/systemd#how-does-enabling-systemd-affect-wsl-architecture)),
|
|
161
|
+
and an idle distro is shut down (by default 15 s after its last process exits). The documented switches are two idle
|
|
162
|
+
timeouts in `%UserProfile%\.wslconfig` of the Windows account that runs the task below (e.g. `C:\Users\<name>\.wslconfig`) — the
|
|
163
|
+
file does not exist by default, create it:
|
|
164
|
+
|
|
165
|
+
```ini
|
|
166
|
+
# How long a distro may sit idle before it is shut down, in ms; -1 = never (default 15000)
|
|
167
|
+
[general]
|
|
168
|
+
instanceIdleTimeout=-1
|
|
169
|
+
|
|
170
|
+
# The same for the WSL 2 virtual machine (default 60000); Windows 11 only, leave it out on Windows 10
|
|
171
|
+
[wsl2]
|
|
172
|
+
vmIdleTimeout=-1
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Both are needed — the maintainers' answer in [microsoft/WSL#13291](https://github.com/microsoft/WSL/issues/13291) —
|
|
176
|
+
and `instanceIdleTimeout` exists since WSL 2.5.4 ([release notes](https://github.com/microsoft/WSL/releases/tag/2.5.4);
|
|
177
|
+
[wsl-config](https://learn.microsoft.com/en-us/windows/wsl/wsl-config#general-wsl-settings)). Apply with `wsl --shutdown`.
|
|
178
|
+
On an older WSL, `wsl --update` first; failing that, the community workaround is a Windows process that never ends, e.g. the
|
|
179
|
+
task below running `--exec sleep infinity` instead of `/bin/true`, with its time limit removed
|
|
180
|
+
(`New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero)`; the default is three days).
|
|
181
|
+
|
|
182
|
+
**3. Start the distro at Windows boot, nobody logged in.** A scheduled task with the *At startup* trigger, registered with
|
|
183
|
+
the account's password (Task Scheduler's «Run whether user is logged on or not»), starts without anyone logged in. Register it
|
|
184
|
+
once from an **administrator** PowerShell, as the Windows account that installed the distro (a WSL distro is registered per
|
|
185
|
+
Windows account). `Get-Credential` asks for that account's **password**: if you sign in to Windows with a Microsoft account
|
|
186
|
+
(an e-mail address), it is that account's password — not the PIN or the fingerprint. Some setups refuse a stored password
|
|
187
|
+
while Settings → Accounts → Sign-in options has «only allow Windows Hello sign-in for Microsoft accounts» switched on; if
|
|
188
|
+
registering or running the task fails with a password error, switch it off and register again.
|
|
189
|
+
|
|
190
|
+
```powershell
|
|
191
|
+
$cred = Get-Credential "$env:USERDOMAIN\$env:USERNAME"
|
|
192
|
+
$action = New-ScheduledTaskAction -Execute 'C:\Program Files\WSL\wsl.exe' -Argument '--distribution Ubuntu --exec /bin/true'
|
|
193
|
+
$trigger = New-ScheduledTaskTrigger -AtStartup
|
|
194
|
+
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
|
|
195
|
+
Register-ScheduledTask -TaskName 'Start WSL' -Action $action -Trigger $trigger -Settings $settings `
|
|
196
|
+
-User $cred.UserName -Password $cred.GetNetworkCredential().Password
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Use `C:\Program Files\WSL\wsl.exe`, **not** the `wsl.exe` on `PATH` (`C:\Windows\System32`): a task that runs without a
|
|
200
|
+
logged-in user has no desktop session («Session 0»), and Microsoft says only the Program Files copy works there since WSL 2.0.0
|
|
201
|
+
([microsoft/WSL#9231](https://github.com/microsoft/WSL/issues/9231), still open: the System32 copy waits for a Windows image change). The
|
|
202
|
+
password is stored in the task: when the Windows password changes, register the task again. A task registered with a password
|
|
203
|
+
launches only if the account has the «Log on as a batch job» right, which Administrators have by default
|
|
204
|
+
([Task Scheduler security contexts](https://learn.microsoft.com/en-us/windows/win32/taskschd/security-contexts-for-running-tasks)).
|
|
205
|
+
Once the distro is up, systemd, the lingering user manager and
|
|
206
|
+
`charness.service` follow by themselves.
|
|
207
|
+
|
|
208
|
+
**4. Never sleep, and make switching on a real boot.** Two things, both required, from an administrator PowerShell
|
|
209
|
+
([powercfg](https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/powercfg-command-line-options)):
|
|
210
|
+
|
|
211
|
+
```powershell
|
|
212
|
+
powercfg /change standby-timeout-ac 0 # never go to sleep on mains power (minutes; 0 = never)
|
|
213
|
+
powercfg /hibernate off # no hibernation — and with it no Fast Startup
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
(The screen may still turn off.) Why the second line: Windows' *Fast Startup*, on by default when the PC can hibernate,
|
|
217
|
+
makes «Shut down» a hibernation instead of a full shutdown ([Microsoft](https://learn.microsoft.com/en-us/troubleshoot/windows-client/setup-upgrade-and-drivers/updates-not-install-with-fast-startup)),
|
|
218
|
+
so the next power-on is a resume, not a boot — and reports say the *At startup* task of part 3 then does not run
|
|
219
|
+
([Microsoft Q&A](https://learn.microsoft.com/en-us/answers/questions/3254425/scheduled-at-startup-task-that-worked-in-windows-7)).
|
|
220
|
+
A «Restart» is a real boot either way. With hibernation off, switching the PC on starts the task.
|
|
221
|
+
|
|
222
|
+
**Check:** shut Windows down (not only restart), switch it on again, do **not** log in, and message the bot from your phone —
|
|
223
|
+
it answers. That proves the whole chain. Then log in once: `wsl --list --running` shows Ubuntu and
|
|
224
|
+
`systemctl --user status charness` inside it shows the bot. Reports from WSL 2.0.0 ([microsoft/WSL#9231](https://github.com/microsoft/WSL/issues/9231))
|
|
225
|
+
say that once the same user logged in and ran WSL commands, a distro started this way appeared as *Stopped* or actually
|
|
226
|
+
stopped; this is not confirmed on current WSL. If the bot stops after you log in, run the task again (Task Scheduler → *Run*), and please
|
|
227
|
+
[open an issue](https://github.com/olosegres/charness/issues) with your `wsl --version`.
|
|
228
|
+
|
|
229
|
+
**Known risks.**
|
|
230
|
+
|
|
231
|
+
- A Windows restart, a Windows Update reboot or `wsl --shutdown` ends the agents' processes, like a container restart:
|
|
232
|
+
each conversation resumes with its next message ([Restart behaviour](README.md#restart-behaviour)).
|
|
233
|
+
- **If a Windows program that the bot calls fails, run `wsl --update` in PowerShell first** and try again: the known
|
|
234
|
+
failures below were fixed in newer WSL. The details, for whoever helps you: Windows `.exe` interop from a systemd service
|
|
235
|
+
has failed in some WSL versions. WSL 2.0.5–2.0.13 could not run a Windows program stored in the Linux filesystem from a
|
|
236
|
+
process without a live interop session, a systemd service among them
|
|
237
|
+
([microsoft/WSL#10812](https://github.com/microsoft/WSL/issues/10812), fixed in 2.0.14); and with `mono` installed its
|
|
238
|
+
`binfmt` handler can take over `.exe` files («File does not contain a valid CIL image»,
|
|
239
|
+
[microsoft/WSL#13000](https://github.com/microsoft/WSL/issues/13000); WSL 2.5.8 added a systemd override that protects WSL's
|
|
240
|
+
interop entry). Workarounds: keep Windows tools on the Windows disk and call them by their `/mnt/c/…` path, and with
|
|
241
|
+
`mono` installed run `sudo update-binfmts --disable cli`.
|
|
242
|
+
- Starting WSL from a scheduled task is the part Microsoft documents least: if the check above fails, run the task by hand
|
|
243
|
+
(Task Scheduler → *Run*) and read its *Last Run Result*.
|
|
244
|
+
|
|
47
245
|
### Environment variables
|
|
48
246
|
|
|
49
247
|
These settings apply to every surface. The Telegram ones — the bot token, the group and the DM, voice — are listed
|
|
@@ -32,11 +32,11 @@ export declare const foreignKeyEventRoutes: {
|
|
|
32
32
|
readonly questionGone: "none";
|
|
33
33
|
readonly closed: "requestSide";
|
|
34
34
|
readonly stopped: "requestSide";
|
|
35
|
-
readonly suspended: "
|
|
35
|
+
readonly suspended: "requestSide";
|
|
36
36
|
readonly turnEnded: "all";
|
|
37
37
|
readonly contextCompacted: "all";
|
|
38
38
|
readonly conversationLost: "requestSide";
|
|
39
|
-
readonly started: "
|
|
39
|
+
readonly started: "requestSide";
|
|
40
40
|
readonly error: "requestSide";
|
|
41
41
|
};
|
|
42
42
|
/** The events whose foreign-key route is a dedicated request-side handler. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapterEventRouting.d.ts","sourceRoot":"","sources":["../../src/adapters/adapterEventRouting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD;;;;;;;;GAQG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,4OAkBpB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,aAAa,GAAG,KAAK,CAAC;AAElE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"adapterEventRouting.d.ts","sourceRoot":"","sources":["../../src/adapters/adapterEventRouting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD;;;;;;;;GAQG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,4OAkBpB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,aAAa,GAAG,KAAK,CAAC;AAElE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;CA4B2C,CAAC;AAE9E,8EAA8E;AAC9E,KAAK,oBAAoB,GAAG;KACzB,CAAC,IAAI,gBAAgB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC,SAAS,aAAa,GAAG,CAAC,GAAG,KAAK;CAC7F,CAAC,gBAAgB,CAAC,CAAC;AAEpB;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,EAC7D,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,CAAC,EACZ,KAAK,EAAE,MAAM,IAAI,EAGjB,GAAG,aAAa,EAAE,CAAC,SAAS,oBAAoB,GAAG,CAAC,aAAa,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,GAClF,IAAI,CAIN"}
|
|
@@ -48,15 +48,16 @@ exports.foreignKeyEventRoutes = {
|
|
|
48
48
|
// Drop the retry, compaction and preamble state a gone session leaves behind.
|
|
49
49
|
closed: 'requestSide',
|
|
50
50
|
stopped: 'requestSide',
|
|
51
|
-
// An idle stop keeps the session, its retry and its compaction state
|
|
52
|
-
suspended: '
|
|
51
|
+
// An idle stop keeps the session, its retry and its compaction state; the work a spent hold let go is noted for the agent.
|
|
52
|
+
suspended: 'requestSide',
|
|
53
53
|
// The per-turn stop (L5) applies to an issue's conversation as to a topic's; the handler posts nothing.
|
|
54
54
|
turnEnded: 'all',
|
|
55
55
|
// A completed compaction resets what the conversation was told: its connector sends the whole issue next (C6).
|
|
56
56
|
contextCompacted: 'all',
|
|
57
57
|
// A live re-spawn began a new conversation where one was lost: a connector that tracks what it told starts over (C6); only a topic gets the notice.
|
|
58
58
|
conversationLost: 'requestSide',
|
|
59
|
-
started
|
|
59
|
+
// A started or adopted process arms its idle countdown (compaction, then the stop) whatever the platform; the banner is a topic's.
|
|
60
|
+
started: 'requestSide',
|
|
60
61
|
// Logged; there is no topic to post it to.
|
|
61
62
|
error: 'requestSide',
|
|
62
63
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapterEventRouting.js","sourceRoot":"","sources":["../../src/adapters/adapterEventRouting.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"adapterEventRouting.js","sourceRoot":"","sources":["../../src/adapters/adapterEventRouting.ts"],"names":[],"mappings":";;;AAqFA,oDAWC;AA/FD,4EAA4E;AAE5E;;;;;;;;GAQG;AAEH,0EAA0E;AAC7D,QAAA,iBAAiB,GAAG;IAC/B,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,UAAU;IACV,YAAY;IACZ,gBAAgB;IAChB,UAAU;IACV,YAAY;IACZ,cAAc;IACd,QAAQ;IACR,SAAS;IACT,SAAS;IACT,WAAW;IACX,WAAW;IACX,kBAAkB;IAClB,kBAAkB;IAClB,OAAO;CACC,CAAC;AAcE,QAAA,qBAAqB,GAAG;IACnC,0FAA0F;IAC1F,MAAM,EAAE,aAAa;IACrB,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;IAClB,cAAc,EAAE,MAAM;IACtB,oEAAoE;IACpE,QAAQ,EAAE,KAAK;IACf,yDAAyD;IACzD,UAAU,EAAE,KAAK;IACjB,YAAY,EAAE,MAAM;IACpB,8EAA8E;IAC9E,MAAM,EAAE,aAAa;IACrB,OAAO,EAAE,aAAa;IACtB,2HAA2H;IAC3H,SAAS,EAAE,aAAa;IACxB,wGAAwG;IACxG,SAAS,EAAE,KAAK;IAChB,+GAA+G;IAC/G,gBAAgB,EAAE,KAAK;IACvB,oJAAoJ;IACpJ,gBAAgB,EAAE,aAAa;IAC/B,mIAAmI;IACnI,OAAO,EAAE,aAAa;IACtB,2CAA2C;IAC3C,KAAK,EAAE,aAAa;CACuD,CAAC;AAO9E;;;GAGG;AACH,SAAgB,oBAAoB,CAClC,GAAe,EACf,SAAY,EACZ,KAAiB;AACjB,8EAA8E;AAC9E,4EAA4E;AAC5E,GAAG,aAAgF;IAEnF,MAAM,KAAK,GAAyB,IAAA,oCAAkB,EAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,6BAAqB,CAAC,SAAS,CAAC,CAAC;IACvG,IAAI,KAAK,KAAK,KAAK;QAAE,KAAK,EAAE,CAAC;SACxB,IAAI,KAAK,KAAK,aAAa;QAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACzD,CAAC"}
|
|
@@ -2899,7 +2899,7 @@ class ClaudeCliAdapter extends events_1.EventEmitter {
|
|
|
2899
2899
|
...claudePermissionArgs,
|
|
2900
2900
|
'--session-id', claudeSessionId,
|
|
2901
2901
|
...mcpFlagsArr,
|
|
2902
|
-
...(0, claudeCompactHook_1.prepareClaudeCompactHookFlags)((0, state_1.resolveDataDir)()),
|
|
2902
|
+
...(0, claudeCompactHook_1.prepareClaudeCompactHookFlags)((0, state_1.resolveDataDir)(), configDir),
|
|
2903
2903
|
...(args ? [args] : []),
|
|
2904
2904
|
];
|
|
2905
2905
|
const claudeShellCmd = claudeArgv.map(tmuxExec_1.shellSingleQuote).join(' ');
|
|
@@ -3530,7 +3530,7 @@ class ClaudeCliAdapter extends events_1.EventEmitter {
|
|
|
3530
3530
|
...claudePermissionArgs,
|
|
3531
3531
|
'--resume', sessionId,
|
|
3532
3532
|
...mcpFlagsArr,
|
|
3533
|
-
...(0, claudeCompactHook_1.prepareClaudeCompactHookFlags)((0, state_1.resolveDataDir)()),
|
|
3533
|
+
...(0, claudeCompactHook_1.prepareClaudeCompactHookFlags)((0, state_1.resolveDataDir)(), configDir),
|
|
3534
3534
|
];
|
|
3535
3535
|
const claudeShellCmd = claudeArgv.map(tmuxExec_1.shellSingleQuote).join(' ');
|
|
3536
3536
|
try {
|