clisbot 0.1.45-beta.10 → 0.1.45-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +159 -36
  2. package/dist/main.js +67 -13
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,3 +1,24 @@
1
+ <p align="center">
2
+ <img src="docs/brand/x-profile-banner-2026-04-29/images/clisbot-x-banner-v5-frontier-tagline-1500x500.png" alt="clisbot banner" width="100%" />
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="./README.md">English</a> |
7
+ <a href="./docs/langs/root/README.vi.md">Tiếng Việt</a> |
8
+ <a href="./docs/langs/root/README.zh-CN.md">简体中文</a> |
9
+ <a href="./docs/langs/root/README.ko.md">한국어</a>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://www.npmjs.com/package/clisbot"><img src="https://img.shields.io/npm/v/clisbot?label=npm&color=cb3837" alt="npm version" /></a>
14
+ <a href="https://www.npmjs.com/package/clisbot"><img src="https://img.shields.io/npm/dm/clisbot?label=downloads&color=22c55e" alt="npm downloads per month" /></a>
15
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/License-MIT-d4a017" alt="MIT license" /></a>
16
+ <img src="https://img.shields.io/badge/CLI-Codex%20%7C%20Claude%20%7C%20Gemini-111827" alt="supported cli tools" />
17
+ <img src="https://img.shields.io/badge/Channels-Slack%20%7C%20Telegram-0a66c2" alt="supported channels" />
18
+ <img src="https://img.shields.io/badge/Runtime-tmux%20backed-16a34a" alt="tmux backed runtime" />
19
+ <img src="https://img.shields.io/badge/Workflow-AI--native-f59e0b" alt="AI-native workflow" />
20
+ </p>
21
+
1
22
  # clisbot - Turn your favorite coding CLI into an agentic personal assistant, workplace assistant, coding partner - on the go
2
23
  Want to use OpenClaw but are struggling because:
3
24
 
@@ -13,6 +34,40 @@ It is not just a tmux bridge with chat glued on top. `clisbot` treats Slack and
13
34
 
14
35
  `clisbot` is also meant to grow into a reusable agent runtime layer that can support many CLI tools, many channels, and many workflow shapes on top of the same durable agent session.
15
36
 
37
+ ## Start Here By Goal
38
+
39
+ ### I Want A Personal Coding Bot In Telegram Or Slack
40
+
41
+ - start with the [Quick Start](#quick-start)
42
+ - best fit when you want Codex, Claude, or Gemini available from chat without
43
+ giving up a real workspace
44
+ - current release value: a much more AI-native control path, where the bot can
45
+ increasingly set up `/queue`, loops, schedules, and other recurring work for
46
+ you from normal chat instead of forcing you to memorize command syntax first
47
+
48
+ ### I Want A Shared Team Bot
49
+
50
+ - start with [Quick Start](#quick-start), then read [Surface Access Model](#surface-access-model)
51
+ - best fit when you need one bot in a real Slack channel, Telegram group, or
52
+ Telegram topic with explicit route and sender control
53
+ - current release value: safer shared-surface policy, tighter topic or thread
54
+ isolation, per-group sender control, and permission boundaries that let one
55
+ bot live in a team group without opening it to everyone there
56
+
57
+ ### I Need Operator Control And Debugging
58
+
59
+ - start with [Common CLI commands](#common-cli-commands)
60
+ - most useful surfaces: `clisbot status`, `clisbot logs`,
61
+ `clisbot watch --latest`, `clisbot inspect --latest`, and `clisbot queues`
62
+ - current release value: more truthful `sessionId`, lighter runner inventory,
63
+ and less confusing restart behavior during updates
64
+
65
+ ### I Just Want To Know What Changed Recently
66
+
67
+ - start with [Recent Release Highlights](#recent-release-highlights)
68
+ - then read [v0.1.45 Release Notes](docs/releases/v0.1.45.md) or the
69
+ [v0.1.45 Release Guide](docs/updates/releases/v0.1.45-release-guide.md)
70
+
16
71
  ## Why I Built This
17
72
 
18
73
  I’m Long Luong (Long), Co-founder & CTO of Vexere, Vietnam’s #1 transportation booking platform, where we also build SaaS and inventory distribution infrastructure for transportation operators. As we scale a 300-person company with a 100-member Engineering, Product, and Design team, I’ve been searching for the most practical way to roll out AI-native workflows across the organization.
@@ -28,17 +83,20 @@ The challenge is not whether AI is useful. It is how to make it work at enterpri
28
83
  - Learns from and integrates the two biggest strengths that made OpenClaw popular: memory and native channel integration with deep, channel-specific conversation and presentation capabilities.
29
84
  - Not just a tmux bridge. Slack and Telegram are treated as real channel surfaces with routing, thread or topic continuity, pairing, follow-up control, and attachment-aware interaction instead of plain text passthrough so you can work from your laptop or on the go without giving up a real coding workspace.
30
85
  - Team-first by design, with `AGENTS`, `USER`, and `MEMORY` context bootstrapping shaped for shared team reality instead of only personal solo-assistant flows.
31
- - Useful for coding, operations, teamwork, and general assistant work, with fast chat controls such as `!<command>` and `/bash <command>` for terminal-like control, `/loop` to bring loop-style automation beyond Claude, `/queue` to add follow-up prompts in the same session without interrupting the current run, `/streaming on` to view real-time processing progress for coding tasks, and `/mention`, `/mention channel`, or `/mention all` to tighten follow-up policy at conversation, route, or bot scope.
86
+ - Shared-surface permission control is a first-class feature: a bot can be in a team group but still answer only the specific people you allow there, while sensitive control actions stay behind explicit auth roles and permissions.
87
+ - Useful for coding, operations, teamwork, and general assistant work, with fast chat controls such as `!<command>`, `/bash <command>`, `/queue`, `/loop`, `/streaming`, and `/mention`.
88
+ - New in `v0.1.45`: the AI-native control experience is much better. You can increasingly ask the bot in normal chat to update itself and explain what changed, help with onboarding, add or configure bots and agents, or create recurring schedules and loops for you instead of relying only on slash commands.
32
89
 
33
- ## What to expect
90
+ ## Who This Fits Best
34
91
 
35
- - You can get the first Telegram bot or Slack bot running in one command.
36
- - The first-run path creates one default agent and only enables the channels you explicitly name.
37
- - DMs start with pairing so access stays explicit.
38
- - `--persist` lets later restarts use plain `clisbot start`.
39
- - Streaming is disabled by default. If you want real-time coding progress in chat, turn it on from the chat surface with `/streaming on`, and turn it off any time with `/streaming off`.
40
- - Slack and Telegram are not treated as plain-text sinks: routed conversations can carry thread or topic identity, pairing, and file-aware workflows.
41
- - Advanced multi-agent setup is available later, but it is not required for day one.
92
+ - Anyone who wants a high-agency personal assistant with OpenClaw-style memory,
93
+ workspace context, and a skill-oriented operating model that can do far more
94
+ than a thin chat wrapper.
95
+ - Solo builders who want a real coding assistant in Telegram or Slack, backed
96
+ by Codex, Claude, or Gemini, without rebuilding their workflow around a new
97
+ web product.
98
+ - Team leads who want one shared bot with explicit group or topic safety,
99
+ durable context, and attachment-aware chat workflows.
42
100
 
43
101
  ## Surface Access Model
44
102
 
@@ -114,23 +172,44 @@ clisbot start \
114
172
  ```
115
173
 
116
174
  If you want to try first without persisting the token yet, just remove `--persist`.
175
+ Day-to-day rescue commands are `clisbot stop`, `clisbot restart`,
176
+ `clisbot status`, and `clisbot logs`.
117
177
 
118
178
  Next steps:
119
179
 
120
180
  - For security, DMs default to pairing.
181
+ - `clisbot` also has a smart autopairing path to reduce first-run friction. If
182
+ you send the bot a DM within the first 30 minutes, you can usually claim the
183
+ owner role immediately and start using it without a separate pairing round.
184
+ - New from `v0.1.45`: the AI-native operator experience is much stronger. You
185
+ can increasingly ask the bot through chat to explain how to use it, update
186
+ itself and summarize what's new, help onboard you, create or add a new bot or
187
+ agent, or set up loops and schedules for recurring work instead of relying
188
+ only on slash commands.
121
189
  - Existing configs from any version before `0.1.45` update directly to `0.1.45` automatically on first run. clisbot writes a backup first under `~/.clisbot/backups/`, then rewrites the config to the current shape.
122
190
  - Shared Slack channels, Slack groups, Telegram groups, and Telegram topics are a separate gate: normal users need an explicit route such as `group:<id>` or `topic:<chatId>:<topicId>` before the bot will talk there. Legacy Slack `channel:<id>` input still works for compatibility.
123
191
  - After a shared surface is admitted, per-surface sender control comes from the bot's default shared rule `groups["*"]` plus any route-local `allowUsers` or `blockUsers`.
192
+ - With that permission model, a bot can be added to a team group but still be
193
+ allowed to answer only some people in that group.
124
194
  - If the effective shared policy is `disabled`, the bot stays silent there for everyone, including owner/admin.
125
195
  - If the effective shared policy is `allowlist` and a sender is not allowed, the bot denies before the runner:
126
196
  - `You are not allowed to use this bot in this group. Ask a bot owner or admin to add you to \`allowUsers\` for this surface.`
127
- - However, `clisbot` has smart autopairing feature to help you get started frictionless. Just send direct message to your bot (through telegram or slack) within 30 minutes so you can claim owner role automatically, and use the bot right away without pairing. After this 30 minutes window you need to approve pairing following instructions by the bot in direct message.
128
197
  - To chat with the bot in a group:
129
- - telegram: Add bot to group, then use slash command in that group /start, you will be guided with command to add a group. Run that command directly or copy that command and chat directly with the bot in DM to ask it do for you (since you are the owner, you are authorized to run that command). After completed, come back to the group and start talk with the bot.
130
- - Notice that group has require mention (or tag the bot) enabled by default to avoid abuse. But it also has smart follow up within 5 minutes by default so you dont need to tag it again. You could change the mode by asking the bot to do for you.
198
+ - telegram: Add the bot to the group, then use `/start` there. It will guide
199
+ you toward the route you need to add. You can run that command directly or
200
+ copy it into a DM with the bot and ask it to do the setup for you if you
201
+ are already authorized.
202
+ - slack: similar flow, but Slack-native slash command handling is awkward.
203
+ Use a leading space such as ` /start`, or use the alias `\start`. The same
204
+ workaround applies to other slash commands such as ` /streaming on` or
205
+ `\mention`.
206
+ - group conversations require a mention by default to avoid abuse, but smart
207
+ follow-up stays open for a short window so you do not need to tag the bot
208
+ again on every reply. You can also ask the bot to change that mode for you.
131
209
  - If you want stricter mention behavior, use `/mention` for this conversation only, `/mention channel` for the current channel or group default, or `/mention all` for the current bot default.
132
- - For long running task such as coding, you might want to toggle streaming mode on with slash command inside the chat "/streaming on", check streaming status anytime with "/streaming status". In slack, native slash command is unconventional so you can get around to use slash command with a space prefix such as " /streaming on", or use alias "\streaming on". This is also true for any other slash command supported by `clisbot`.
133
- - slack:
210
+ - For long running tasks such as coding, turn streaming on with `/streaming on`
211
+ and check it with `/streaming status`. In Slack, use a leading space such
212
+ as ` /streaming on` or the alias `\streaming on`.
134
213
  - If you want to add more owner or app admin, grant that principal explicitly with the platform prefix plus the channel-native user id, for example `clisbot auth add-user app --role owner --user telegram:1276408333` or `clisbot auth add-user app --role admin --user slack:U123ABC456`.
135
214
  - `clisbot auth --help` now covers role scopes, permission sets, and add/remove flows for users and permissions.
136
215
  - App-level auth and owner-claim semantics in [Authorization And Roles](docs/user-guide/auth-and-roles.md) describe both the current runtime reality and the remaining target-model gaps.
@@ -154,11 +233,32 @@ What happens next:
154
233
 
155
234
  ## Recent Release Highlights
156
235
 
157
- - `v0.1.45`: safer personal and team bots in real Slack and Telegram groups, automatic direct updates from older installs, more reliable scheduled loops, clearer sender and surface context, Telegram audio support, and stricter streaming/session isolation.
236
+ - `v0.1.45`: a much more AI-native operator experience, where you can
237
+ increasingly talk to the bot to manage itself; plus safer personal and team
238
+ bots in real Slack and Telegram groups, automatic direct updates from older
239
+ installs, durable queue control, clearer session continuity truth, more
240
+ reliable scheduled loops, stronger trust/restart behavior, and stricter
241
+ streaming/session isolation.
158
242
  - `v0.1.43`: more durable runtime recovery, clearer routed follow-up controls, more truthful tmux prompt submission checks, better queued-start notifications, and safer Slack thread attachment behavior.
159
- - `v0.1.39`: the first large release of the current bot-first shape, with native Slack and Telegram rendering, cleaner first-run setup, stronger pairing/auth defaults, better long-running run visibility, and recurring `/loop` automation.
160
243
 
161
- There are many more useful fixes and operator improvements in the full release notes, including config update safety, CLI help, setup docs, runner debugging, route policy behavior, and channel-specific polish.
244
+ What `v0.1.45` most likely means for you:
245
+
246
+ - The headline is AI-native control: ask the bot in chat to queue work,
247
+ schedule recurring briefs, help update itself, explain release changes, or
248
+ guide setup and routing instead of dropping to the shell for every action.
249
+ - personal user: fewer fragile long-run failures, better `/queue`, better media
250
+ handling on Telegram
251
+ - shared bot owner: clearer route safety, easier direct upgrade from older
252
+ installs, and more interesting team use cases where one bot lives in the
253
+ group but only responds to selected people there
254
+ - operator: better queue visibility, better session continuity truth, and
255
+ restart behavior that is less misleading during updates, plus faster
256
+ `watch` and `inspect` shortcuts when something goes wrong
257
+
258
+ There are many more useful fixes and operator improvements in the full release
259
+ notes, including config update safety, CLI help, setup docs, runner debugging,
260
+ route policy behavior, channel-specific polish, and the broader AI-native
261
+ workflow direction behind this release.
162
262
 
163
263
  Read the full notes here:
164
264
 
@@ -195,14 +295,19 @@ Repo-local `bun run start|stop|restart|status|logs|init|pairing` is pinned by `.
195
295
 
196
296
  Update note for existing installs:
197
297
 
198
- - `v0.1.39` includes breaking changes in config shape and in the main CLI command surface.
199
- - If you already run an older install, ask Codex or Claude in this repo to update your current config before updating the package.
200
- - The package update itself is still simple:
298
+ - Older installs before `v0.1.45` now update directly on first run with a
299
+ backup written first, so most people can update and restart without a manual
300
+ migration pass.
301
+ - After you are on `v0.1.45`, future upgrades should feel much more AI-native:
302
+ in many cases you can simply ask the bot to update `clisbot` to the latest
303
+ version, and it can follow the update guide, perform the upgrade flow, then
304
+ brief you on what changed.
305
+ - If you still want an agent to inspect your current config before updating,
306
+ ask Codex or Claude in this repo to review it first.
307
+ - The manual package upgrade path is now simpler:
201
308
 
202
309
  ```bash
203
- clisbot stop
204
- npm install -g clisbot
205
- clisbot start
310
+ npm install -g clisbot && clisbot restart
206
311
  clisbot --version
207
312
  ```
208
313
 
@@ -264,6 +369,12 @@ If you want the repo-guided setup path:
264
369
  3. Ask it to help you set up `clisbot`.
265
370
 
266
371
  The docs in this repo are kept current, including the [User Guide](docs/user-guide/README.md), so the agent should have enough context to walk you through setup, configuration, and troubleshooting directly inside the repo.
372
+ If anything goes wrong, the fastest rescue loop is usually `clisbot logs`,
373
+ `clisbot status`, `clisbot restart`, or if needed `clisbot stop --hard`
374
+ followed by `clisbot start`.
375
+ Also open the coding CLI directly inside the bot workspace, usually
376
+ `~/.clisbot/workspaces/default`, and make sure that CLI already works there.
377
+ That is one of the strongest end-to-end checks for bot health.
267
378
 
268
379
  If you prefer to configure everything yourself:
269
380
 
@@ -307,26 +418,28 @@ clisbot start \
307
418
  If the quick start does not work, check these in order:
308
419
 
309
420
  - If setup feels unclear, open Claude Code, Codex, or Gemini CLI in this repo and ask it to help using the local docs.
421
+ - If anything looks wrong, start with `clisbot logs`, `clisbot status`,
422
+ `clisbot restart`, or if needed `clisbot stop --hard` followed by
423
+ `clisbot start`.
310
424
  - If config behavior is confusing, inspect [config/clisbot.json.template](config/clisbot.json.template) first, then compare it with [docs/user-guide/README.md](docs/user-guide/README.md).
311
425
  - If `clisbot start` says no agents are configured, prefer `clisbot start --cli codex --bot-type personal --telegram-bot-token <your-telegram-bot-token>`.
312
- - If you want later runs to work with plain `clisbot start`, rerun your successful first-run command with `--persist`.
313
426
  - If `clisbot start` prints token refs as `missing`, either pass the token explicitly on the command line or switch to env-backed setup described in [docs/user-guide/bots-and-credentials.md](docs/user-guide/bots-and-credentials.md).
314
- - If you use custom env names, pass them explicitly with `--slack-app-token`, `--slack-bot-token`, or `--telegram-bot-token`.
315
427
  - If `clisbot status` shows `bootstrap=...:missing` or `bootstrap=...:not-bootstrapped`, follow the advanced agent bootstrap steps in [docs/user-guide/README.md](docs/user-guide/README.md).
316
- - If Codex shows `Do you trust the contents of this directory?`, keep `trustWorkspace: true` in clisbot config and also mark the workspace as trusted in `~/.codex/config.toml`, for example:
317
-
318
- ```toml
319
- [projects."/home/node/.clisbot/workspaces/default"]
320
- trust_level = "trusted"
321
- ```
322
-
323
- - If that trust screen is still blocking, inspect the live session name with `clisbot runner list`, then attach directly with `tmux -S ~/.clisbot/state/clisbot.sock attach -t <session-name>`.
428
+ - Trust is usually handled automatically by the bot, but if trust or startup
429
+ behavior still looks wrong, go to the workspace and launch the underlying CLI
430
+ there directly, for example `cd ~/.clisbot/workspaces/default` and start
431
+ `codex`, `claude`, or `gemini` yourself. If the CLI cannot start cleanly in
432
+ that workspace, the bot will not be healthy either.
324
433
  - If Gemini startup says it is waiting for manual authorization, authenticate Gemini directly first or provide a headless auth path such as `GEMINI_API_KEY` or Vertex AI credentials; `clisbot` now treats that screen as a startup blocker instead of a healthy ready session.
325
434
  - If Codex warns that `bubblewrap` is missing on Linux, install `bubblewrap` in the runtime environment.
326
435
  - If the bot does not answer, check `clisbot status` first. Healthy channels should show `connection=active`; if a channel stays `starting`, inspect `clisbot logs`.
327
- - If a routed message was accepted but no reply arrives, send one test message and immediately run `clisbot runner watch --latest --lines 100` in a terminal. This shows the live tmux runner pane and usually reveals missing CLI auth, trust prompts, stuck startup, or model/provider errors.
436
+ - If a routed message was accepted but no reply arrives, send one test message
437
+ and immediately run `clisbot watch --latest --lines 100` in a terminal. This
438
+ shows the live tmux runner pane and usually reveals missing CLI auth, trust
439
+ prompts, stuck startup, or model/provider errors.
328
440
  - If Codex works in your normal terminal but the routed runner shows `Missing environment variable: CODEX_CLIPROXYAPI_KEY`, remember that `clisbot` runs Codex from a detached background process and tmux session. Start or restart `clisbot` from a shell where `echo $CODEX_CLIPROXYAPI_KEY` prints a value, or export the key in the environment used by your service manager. Existing tmux runner sessions keep their old environment, so recycle them after fixing env.
329
441
  - If runtime startup still fails, run `clisbot logs` and inspect the recent log tail that `clisbot` now prints automatically on startup failure.
442
+ - If `clisbot restart` warns that stop timed out during an update, run `clisbot status` once. Current releases should continue cleanly when status already shows the worker exited; only treat it as a real bug if restart leaves the runtime down.
330
443
  - If a normal restart is not enough, use `clisbot stop --hard` to stop the runtime and kill all tmux runner sessions on the configured clisbot socket, then start again from a shell with the correct environment.
331
444
  - If you need the full command list, run `clisbot --help`.
332
445
  - If you need step-by-step operator docs, start with [docs/user-guide/README.md](docs/user-guide/README.md).
@@ -344,8 +457,12 @@ Most users only need a small set of commands at first:
344
457
  - `clisbot status`: check whether the runtime, channels, and active sessions look healthy.
345
458
  - `clisbot logs`: inspect recent runtime logs when startup, routing, or replies look wrong.
346
459
  - `clisbot runner list`: list the live tmux-backed runner sessions and see what is active.
347
- - `clisbot runner watch <session-name>`: live-watch one specific session when debugging a real run.
348
- - `clisbot runner watch --latest --lines 100`: jump straight into the most recently active session with enough context to debug a just-submitted message.
460
+ - `clisbot inspect --latest`: capture the current pane state of the newest
461
+ admitted session once.
462
+ - `clisbot watch --latest --lines 100`: jump straight into the newest admitted
463
+ live session with enough context to debug a just-submitted message.
464
+ - `clisbot watch --index 2`: follow the second most recent admitted session
465
+ without needing to copy a tmux session name first.
349
466
  - `clisbot queues list`: inspect pending durable queued prompts across the app.
350
467
  - `clisbot queues create --channel telegram --target group:-1001234567890 --topic-id 4335 --sender telegram:1276408333 <prompt>`: create one durable same-session queued prompt, capped by `control.queue.maxPendingItemsPerSession` (default `20`).
351
468
 
@@ -406,6 +523,10 @@ Detailed slash-command guide:
406
523
 
407
524
  ## Docs
408
525
 
526
+ - [Localized Docs Hub](docs/langs/README.md)
527
+ - [Vietnamese Repo README](docs/langs/root/README.vi.md)
528
+ - [Simplified Chinese Repo README](docs/langs/root/README.zh-CN.md)
529
+ - [Korean Repo README](docs/langs/root/README.ko.md)
409
530
  - [Overview](docs/overview/README.md)
410
531
  - [Architecture](docs/architecture/README.md)
411
532
  - [Development Guide](docs/development/README.md)
@@ -429,6 +550,8 @@ Detailed slash-command guide:
429
550
  - more communication channels beyond Slack and Telegram
430
551
  - simple workflow building blocks such as cron jobs, heartbeat jobs, and loops
431
552
  - durable agent sessions, workspaces, follow-up policy, commands, attachments, and operator controls that stay reusable across all those surfaces
553
+ - stability and security stay at the top of the project focus; if you find an
554
+ issue in either area, please report it
432
555
 
433
556
  tmux is still the current stability boundary. One agent maps to one durable runner session in one workspace, and every CLI, channel, or workflow layer should route onto that durable runtime instead of recreating the agent from scratch.
434
557
 
package/dist/main.js CHANGED
@@ -69100,6 +69100,14 @@ class TmuxSubmitUnconfirmedError extends Error {
69100
69100
  }
69101
69101
  }
69102
69102
  async function submitTmuxSessionInput(params) {
69103
+ if (params.trustPrompt) {
69104
+ await acceptTmuxTrustPromptIfPresent({
69105
+ tmux: params.tmux,
69106
+ sessionName: params.sessionName,
69107
+ captureLines: params.trustPrompt.captureLines,
69108
+ startupDelayMs: params.trustPrompt.startupDelayMs
69109
+ });
69110
+ }
69103
69111
  const prePasteState = await params.tmux.getPaneState(params.sessionName);
69104
69112
  const captureLines = estimatePasteCaptureLines(params.text);
69105
69113
  const prePasteSnapshot = normalizePaneText(await params.tmux.capturePane(params.sessionName, captureLines));
@@ -69153,6 +69161,12 @@ async function submitTmuxSessionInput(params) {
69153
69161
  throw new TmuxSubmitUnconfirmedError;
69154
69162
  }
69155
69163
  async function captureTmuxSessionIdentity(params) {
69164
+ await acceptTmuxTrustPromptIfPresent({
69165
+ tmux: params.tmux,
69166
+ sessionName: params.sessionName,
69167
+ captureLines: params.captureLines,
69168
+ startupDelayMs: params.timeoutMs
69169
+ });
69156
69170
  let statusSubmission = await submitTmuxSessionInput({
69157
69171
  tmux: params.tmux,
69158
69172
  sessionName: params.sessionName,
@@ -69176,7 +69190,7 @@ async function captureTmuxSessionIdentity(params) {
69176
69190
  throw error;
69177
69191
  }
69178
69192
  if (tmuxPaneHasTrustPrompt(snapshot)) {
69179
- await dismissTrustPrompt({
69193
+ await acceptTrustPrompt({
69180
69194
  tmux: params.tmux,
69181
69195
  sessionName: params.sessionName,
69182
69196
  captureLines: params.captureLines
@@ -69233,7 +69247,7 @@ function extractSessionIdFromCaptureCandidates(candidates, pattern) {
69233
69247
  }
69234
69248
  return null;
69235
69249
  }
69236
- async function dismissTmuxTrustPromptIfPresent(params) {
69250
+ async function acceptTmuxTrustPromptIfPresent(params) {
69237
69251
  const deadline = Date.now() + Math.max(TRUST_PROMPT_MAX_WAIT_MS, params.startupDelayMs);
69238
69252
  while (Date.now() <= deadline) {
69239
69253
  let snapshot = "";
@@ -69256,7 +69270,7 @@ async function dismissTmuxTrustPromptIfPresent(params) {
69256
69270
  if (!tmuxPaneHasTrustPrompt(snapshot)) {
69257
69271
  return;
69258
69272
  }
69259
- await dismissTrustPrompt({
69273
+ await acceptTrustPrompt({
69260
69274
  tmux: params.tmux,
69261
69275
  sessionName: params.sessionName,
69262
69276
  captureLines: params.captureLines
@@ -69288,7 +69302,7 @@ async function waitForTmuxSessionBootstrap(params) {
69288
69302
  if (snapshot) {
69289
69303
  lastSnapshot = snapshot;
69290
69304
  if (params.trustWorkspace && tmuxPaneHasTrustPrompt(snapshot)) {
69291
- await dismissTrustPrompt({
69305
+ await acceptTrustPrompt({
69292
69306
  tmux: params.tmux,
69293
69307
  sessionName: params.sessionName,
69294
69308
  captureLines: params.captureLines
@@ -69321,7 +69335,7 @@ async function waitForTmuxSessionBootstrap(params) {
69321
69335
  snapshot: lastSnapshot
69322
69336
  };
69323
69337
  }
69324
- async function dismissTrustPrompt(params) {
69338
+ async function acceptTrustPrompt(params) {
69325
69339
  await params.tmux.sendKey(params.sessionName, "Enter");
69326
69340
  const deadline = Date.now() + TRUST_PROMPT_MAX_WAIT_MS;
69327
69341
  while (Date.now() <= deadline) {
@@ -69970,7 +69984,7 @@ class RunnerService {
69970
69984
  try {
69971
69985
  const snapshot = await this.captureSessionSnapshot(resolved);
69972
69986
  if (tmuxPaneHasTrustPrompt(snapshot)) {
69973
- await this.dismissVisibleTrustPrompt(resolved);
69987
+ await this.acceptVisibleWorkspaceTrustPrompt(resolved);
69974
69988
  continue;
69975
69989
  }
69976
69990
  return;
@@ -70041,6 +70055,7 @@ class RunnerService {
70041
70055
  });
70042
70056
  try {
70043
70057
  await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
70058
+ await this.acceptWorkspaceTrustPromptIfPresent(resolved);
70044
70059
  await this.syncStoredSessionIdForResolvedTarget(resolved);
70045
70060
  } catch (error) {
70046
70061
  throw await this.mapSessionError(error, resolved.sessionName, "during startup");
@@ -70124,7 +70139,7 @@ class RunnerService {
70124
70139
  return resolved;
70125
70140
  }
70126
70141
  async finalizeSessionStartup(resolved, params) {
70127
- await this.dismissTrustPrompt(resolved);
70142
+ await this.acceptWorkspaceTrustPromptIfPresent(resolved);
70128
70143
  await this.verifySessionReady(resolved);
70129
70144
  if (params.storedOrExplicitSessionId) {
70130
70145
  await this.persistStoredSessionIdBestEffort(resolved, params.storedOrExplicitSessionId, params.runnerCommand);
@@ -70158,14 +70173,14 @@ class RunnerService {
70158
70173
  return;
70159
70174
  }
70160
70175
  }
70161
- async dismissTrustPrompt(resolved) {
70176
+ async acceptWorkspaceTrustPromptIfPresent(resolved) {
70162
70177
  if (!resolved.runner.trustWorkspace) {
70163
70178
  return;
70164
70179
  }
70165
- await this.dismissVisibleTrustPrompt(resolved);
70180
+ await this.acceptVisibleWorkspaceTrustPrompt(resolved);
70166
70181
  }
70167
- async dismissVisibleTrustPrompt(resolved) {
70168
- await dismissTmuxTrustPromptIfPresent({
70182
+ async acceptVisibleWorkspaceTrustPrompt(resolved) {
70183
+ await acceptTmuxTrustPromptIfPresent({
70169
70184
  tmux: this.tmux,
70170
70185
  sessionName: resolved.sessionName,
70171
70186
  captureLines: resolved.stream.captureLines,
@@ -70246,9 +70261,22 @@ class RunnerService {
70246
70261
  async triggerNewSessionInLiveRunner(resolved) {
70247
70262
  const oldSessionId = (await this.sessionMapping.get(resolved.sessionKey))?.sessionId;
70248
70263
  const command = this.resolveNewSessionCommand(resolved);
70249
- await this.submitNewSessionCommand(resolved, command);
70264
+ await this.acceptWorkspaceTrustPromptIfPresent(resolved);
70265
+ let submitUnconfirmedError = null;
70266
+ try {
70267
+ await this.submitNewSessionCommand(resolved, command);
70268
+ } catch (error) {
70269
+ if (error instanceof TmuxSubmitUnconfirmedError) {
70270
+ submitUnconfirmedError = error;
70271
+ } else {
70272
+ throw error;
70273
+ }
70274
+ }
70250
70275
  const sessionId = await this.captureNewSessionIdentityAfterTrigger(resolved, oldSessionId);
70251
70276
  if (!sessionId) {
70277
+ if (submitUnconfirmedError) {
70278
+ throw submitUnconfirmedError;
70279
+ }
70252
70280
  this.throwNewSessionCaptureFailure(command, oldSessionId);
70253
70281
  }
70254
70282
  try {
@@ -70422,6 +70450,7 @@ class RunnerService {
70422
70450
  if (!await this.tmux.hasSession(resolved.sessionName)) {
70423
70451
  throw new Error(`tmux session "${resolved.sessionName}" does not exist`);
70424
70452
  }
70453
+ await this.acceptWorkspaceTrustPromptIfPresent(resolved);
70425
70454
  await submitTmuxSessionInput({
70426
70455
  tmux: this.tmux,
70427
70456
  sessionName: resolved.sessionName,
@@ -70500,6 +70529,14 @@ async function monitorTmuxRun(params) {
70500
70529
  let firstMeaningfulDeltaLogged = false;
70501
70530
  let noOutputThresholdLogged = false;
70502
70531
  if (params.prompt) {
70532
+ if (params.trustWorkspace) {
70533
+ await acceptTmuxTrustPromptIfPresent({
70534
+ tmux: params.tmux,
70535
+ sessionName: params.sessionName,
70536
+ captureLines: params.captureLines,
70537
+ startupDelayMs: params.startupDelayMs ?? 0
70538
+ });
70539
+ }
70503
70540
  logLatencyDebug("tmux-submit-start", params.timingContext, {
70504
70541
  sessionName: params.sessionName,
70505
70542
  promptSubmitDelayMs: params.promptSubmitDelayMs
@@ -71242,6 +71279,8 @@ class SessionService {
71242
71279
  sessionName: run.resolved.sessionName,
71243
71280
  prompt: params.prompt,
71244
71281
  promptSubmitDelayMs: run.resolved.runner.promptSubmitDelayMs,
71282
+ trustWorkspace: run.resolved.runner.trustWorkspace,
71283
+ startupDelayMs: run.resolved.runner.startupDelayMs,
71245
71284
  captureLines: run.resolved.stream.captureLines,
71246
71285
  updateIntervalMs: run.resolved.stream.updateIntervalMs,
71247
71286
  idleTimeoutMs: run.resolved.stream.idleTimeoutMs,
@@ -71620,8 +71659,10 @@ var TELEGRAM_REPLY_COMMAND_BASE = `{{command}} message send \\
71620
71659
  --render native \\
71621
71660
  `;
71622
71661
  var SLACK_REPLY_STYLE_HINT = `Put readable hierarchical Markdown in the --message body.
71662
+ For clickable links, use canonical URLs and do not wrap them in backticks.
71623
71663
  Keep each paragraph, list, or code block under 2500 chars.`;
71624
71664
  var TELEGRAM_REPLY_STYLE_HINT = `Put readable hierarchical Markdown in the --message body.
71665
+ For clickable links, use canonical URLs and do not wrap them in backticks.
71625
71666
  Keep the Markdown body under 3000 chars.`;
71626
71667
  var ACCOUNT_CLAUSE = " --account {{account_id}} \\\n";
71627
71668
  var EMPTY_ACCOUNT_CLAUSE = "";
@@ -86200,6 +86241,8 @@ async function withStartupTimeout(name, start2) {
86200
86241
  }
86201
86242
 
86202
86243
  // src/control/runtime-management-cli.ts
86244
+ var RESTART_STOP_STATUS_RECHECK_TIMEOUT_MS = 2000;
86245
+ var RESTART_STOP_STATUS_RECHECK_INTERVAL_MS = 100;
86203
86246
  function getOperatorConfigPath() {
86204
86247
  return expandHomePath(process.env.CLISBOT_CONFIG_PATH || DEFAULT_CONFIG_PATH);
86205
86248
  }
@@ -86439,6 +86482,7 @@ async function stop(hard = false) {
86439
86482
  async function restart(dependencies = {
86440
86483
  stopDetachedRuntime,
86441
86484
  getRuntimeStatus,
86485
+ sleep,
86442
86486
  warn: (message) => console.error(message)
86443
86487
  }) {
86444
86488
  const configPath = getOperatorConfigPath();
@@ -86448,7 +86492,7 @@ async function restart(dependencies = {
86448
86492
  hard: false
86449
86493
  });
86450
86494
  } catch (error) {
86451
- const status = await dependencies.getRuntimeStatus({ configPath });
86495
+ const status = await waitForStoppedRuntimeAfterStopError(configPath, dependencies);
86452
86496
  if (status.running) {
86453
86497
  throw error;
86454
86498
  }
@@ -86456,6 +86500,16 @@ async function restart(dependencies = {
86456
86500
  dependencies.warn(`warning: clisbot stop reported an error, but status now shows the service is stopped; continuing with start. Stop error: ${message}`);
86457
86501
  }
86458
86502
  }
86503
+ async function waitForStoppedRuntimeAfterStopError(configPath, dependencies) {
86504
+ const deadline = Date.now() + RESTART_STOP_STATUS_RECHECK_TIMEOUT_MS;
86505
+ while (true) {
86506
+ const status = await dependencies.getRuntimeStatus({ configPath });
86507
+ if (!status.running || Date.now() >= deadline) {
86508
+ return status;
86509
+ }
86510
+ await dependencies.sleep(RESTART_STOP_STATUS_RECHECK_INTERVAL_MS);
86511
+ }
86512
+ }
86459
86513
  async function status() {
86460
86514
  await printStatusSummary();
86461
86515
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clisbot",
3
- "version": "0.1.45-beta.10",
3
+ "version": "0.1.45-beta.12",
4
4
  "private": false,
5
5
  "description": "Chat surfaces for durable AI coding agents running in tmux",
6
6
  "license": "MIT",