clisbot 0.1.45-beta.11 → 0.1.45-beta.13

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 +90 -24
  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
@@ -67402,6 +67402,7 @@ function createStoredLoopBase(params) {
67402
67402
  protectedControlMutationRule: params.protectedControlMutationRule,
67403
67403
  promptSummary: params.promptSummary,
67404
67404
  promptSource: params.promptSource,
67405
+ progressMessages: params.progressMessages,
67405
67406
  loopStart: params.loopStart,
67406
67407
  createdBy: params.createdBy,
67407
67408
  sender: params.sender ?? deriveLegacyLoopSender({
@@ -67432,6 +67433,7 @@ function createStoredIntervalLoop(params) {
67432
67433
  protectedControlMutationRule: params.protectedControlMutationRule,
67433
67434
  promptSummary: params.promptSummary,
67434
67435
  promptSource: params.promptSource,
67436
+ progressMessages: params.progressMessages,
67435
67437
  loopStart: params.loopStart,
67436
67438
  createdBy: params.createdBy,
67437
67439
  sender: params.sender,
@@ -67462,6 +67464,7 @@ function createStoredCalendarLoop(params) {
67462
67464
  protectedControlMutationRule: params.protectedControlMutationRule,
67463
67465
  promptSummary: params.promptSummary,
67464
67466
  promptSource: params.promptSource,
67467
+ progressMessages: params.progressMessages,
67465
67468
  loopStart: params.loopStart,
67466
67469
  createdBy: params.createdBy,
67467
67470
  sender: params.sender,
@@ -70262,9 +70265,21 @@ class RunnerService {
70262
70265
  const oldSessionId = (await this.sessionMapping.get(resolved.sessionKey))?.sessionId;
70263
70266
  const command = this.resolveNewSessionCommand(resolved);
70264
70267
  await this.acceptWorkspaceTrustPromptIfPresent(resolved);
70265
- await this.submitNewSessionCommand(resolved, command);
70268
+ let submitUnconfirmedError = null;
70269
+ try {
70270
+ await this.submitNewSessionCommand(resolved, command);
70271
+ } catch (error) {
70272
+ if (error instanceof TmuxSubmitUnconfirmedError) {
70273
+ submitUnconfirmedError = error;
70274
+ } else {
70275
+ throw error;
70276
+ }
70277
+ }
70266
70278
  const sessionId = await this.captureNewSessionIdentityAfterTrigger(resolved, oldSessionId);
70267
70279
  if (!sessionId) {
70280
+ if (submitUnconfirmedError) {
70281
+ throw submitUnconfirmedError;
70282
+ }
70268
70283
  this.throwNewSessionCaptureFailure(command, oldSessionId);
70269
70284
  }
70270
70285
  try {
@@ -71625,7 +71640,9 @@ var REPLY_RULES = `When replying to the user:
71625
71640
  - put the user-facing message inside the --message body of that command
71626
71641
  {{progress_rules_block}}- {{final_rule_line}}`;
71627
71642
  var PROGRESS_PHRASE = "progress update or ";
71643
+ var EMPTY_PROGRESS_PHRASE = "";
71628
71644
  var PROGRESS_FLAG_SUFFIX = "|progress";
71645
+ var EMPTY_PROGRESS_FLAG_SUFFIX = "";
71629
71646
  var PROGRESS_RULES_BLOCK = `- use that command to send progress updates and the final reply back to the conversation
71630
71647
  - send at most {{max_progress_messages}} short, meaningful progress updates; skip trivial internal steps
71631
71648
  `;
@@ -71647,8 +71664,10 @@ var TELEGRAM_REPLY_COMMAND_BASE = `{{command}} message send \\
71647
71664
  --render native \\
71648
71665
  `;
71649
71666
  var SLACK_REPLY_STYLE_HINT = `Put readable hierarchical Markdown in the --message body.
71667
+ For clickable links, use canonical URLs and do not wrap them in backticks.
71650
71668
  Keep each paragraph, list, or code block under 2500 chars.`;
71651
71669
  var TELEGRAM_REPLY_STYLE_HINT = `Put readable hierarchical Markdown in the --message body.
71670
+ For clickable links, use canonical URLs and do not wrap them in backticks.
71652
71671
  Keep the Markdown body under 3000 chars.`;
71653
71672
  var ACCOUNT_CLAUSE = " --account {{account_id}} \\\n";
71654
71673
  var EMPTY_ACCOUNT_CLAUSE = "";
@@ -71689,7 +71708,8 @@ function buildChannelPromptText(params) {
71689
71708
  identity: params.identity,
71690
71709
  config: params.config,
71691
71710
  responseMode: params.responseMode,
71692
- streaming: params.streaming
71711
+ streaming: params.streaming,
71712
+ maxProgressMessagesOverride: params.maxProgressMessagesOverride
71693
71713
  });
71694
71714
  const context = resolvePromptContext(params);
71695
71715
  return renderTemplate(BASE_TEMPLATE, {
@@ -71734,9 +71754,11 @@ function renderMessagePromptParts(params) {
71734
71754
  replyStyleHint: EMPTY_REPLY_STYLE_HINT
71735
71755
  };
71736
71756
  }
71737
- const progressPhrase = PROGRESS_PHRASE;
71738
- const progressFlagSuffix = PROGRESS_FLAG_SUFFIX;
71739
- const progressRulesBlock = PROGRESS_RULES_BLOCK;
71757
+ const maxProgressMessages = Math.max(0, params.maxProgressMessagesOverride ?? params.config.maxProgressMessages);
71758
+ const progressEnabled = maxProgressMessages > 0;
71759
+ const progressPhrase = progressEnabled ? PROGRESS_PHRASE : EMPTY_PROGRESS_PHRASE;
71760
+ const progressFlagSuffix = progressEnabled ? PROGRESS_FLAG_SUFFIX : EMPTY_PROGRESS_FLAG_SUFFIX;
71761
+ const progressRulesBlock = progressEnabled ? PROGRESS_RULES_BLOCK : "";
71740
71762
  const finalRuleLine = params.config.requireFinalResponse ? FINAL_RULE_REQUIRED : FINAL_RULE_OPTIONAL;
71741
71763
  return {
71742
71764
  deliveryIntro: renderTemplate(DELIVERY_INTRO, {
@@ -71751,7 +71773,7 @@ function renderMessagePromptParts(params) {
71751
71773
  }),
71752
71774
  replyRules: renderTemplate(REPLY_RULES, {
71753
71775
  progress_rules_block: renderTemplate(progressRulesBlock, {
71754
- max_progress_messages: String(params.config.maxProgressMessages)
71776
+ max_progress_messages: String(maxProgressMessages)
71755
71777
  }),
71756
71778
  final_rule_line: finalRuleLine
71757
71779
  }),
@@ -72403,7 +72425,8 @@ class SurfaceRuntime {
72403
72425
  agentId,
72404
72426
  time: promptTime,
72405
72427
  promptContext,
72406
- scheduledLoopId: loop.id
72428
+ scheduledLoopId: loop.id,
72429
+ maxProgressMessagesOverride: loop.progressMessages
72407
72430
  });
72408
72431
  }
72409
72432
  async buildManagedQueuePrompt(agentId, item) {
@@ -76524,7 +76547,7 @@ class SlackSocketService {
76524
76547
  senderId: slackSenderId,
76525
76548
  text,
76526
76549
  agentPromptText,
76527
- agentPromptBuilder: (nextText) => buildAgentPromptText({
76550
+ agentPromptBuilder: (nextText, options) => buildAgentPromptText({
76528
76551
  text: enrichPromptText(nextText),
76529
76552
  identity,
76530
76553
  config: this.getBotConfig().agentPrompt,
@@ -76542,7 +76565,8 @@ class SlackSocketService {
76542
76565
  agentId: params.route.agentId,
76543
76566
  routeTimezone: params.route.timezone,
76544
76567
  botTimezone: params.route.botTimezone
76545
- }).timezone
76568
+ }).timezone,
76569
+ maxProgressMessagesOverride: options?.maxProgressMessagesOverride
76546
76570
  }),
76547
76571
  promptContext,
76548
76572
  protectedControlMutationRule,
@@ -79193,7 +79217,7 @@ class TelegramPollingService {
79193
79217
  senderId: message.from?.id != null ? String(message.from.id).trim() : undefined,
79194
79218
  text,
79195
79219
  agentPromptText,
79196
- agentPromptBuilder: (nextText) => buildAgentPromptText({
79220
+ agentPromptBuilder: (nextText, options) => buildAgentPromptText({
79197
79221
  text: enrichPromptText(nextText),
79198
79222
  identity,
79199
79223
  config: this.getBotConfig().agentPrompt,
@@ -79211,7 +79235,8 @@ class TelegramPollingService {
79211
79235
  agentId: route.agentId,
79212
79236
  routeTimezone: route.timezone,
79213
79237
  botTimezone: route.botTimezone
79214
- }).timezone
79238
+ }).timezone,
79239
+ maxProgressMessagesOverride: options?.maxProgressMessagesOverride
79215
79240
  }),
79216
79241
  promptContext,
79217
79242
  protectedControlMutationRule,
@@ -81462,7 +81487,7 @@ function resolveSlackLoopCliContext(params) {
81462
81487
  sessionTarget,
81463
81488
  identity,
81464
81489
  route,
81465
- buildLoopPromptText: (text) => buildAgentPromptText({
81490
+ buildLoopPromptText: (text, options) => buildAgentPromptText({
81466
81491
  text,
81467
81492
  identity,
81468
81493
  config: botConfig.agentPrompt,
@@ -81476,7 +81501,8 @@ function resolveSlackLoopCliContext(params) {
81476
81501
  agentId: sessionTarget.agentId,
81477
81502
  routeTimezone: route.timezone,
81478
81503
  botTimezone: route.botTimezone
81479
- }).timezone
81504
+ }).timezone,
81505
+ maxProgressMessagesOverride: options?.maxProgressMessagesOverride
81480
81506
  })
81481
81507
  };
81482
81508
  }
@@ -81532,7 +81558,7 @@ function resolveTelegramLoopCliContext(params) {
81532
81558
  sessionTarget,
81533
81559
  identity,
81534
81560
  route,
81535
- buildLoopPromptText: (text) => buildAgentPromptText({
81561
+ buildLoopPromptText: (text, options) => buildAgentPromptText({
81536
81562
  text,
81537
81563
  identity,
81538
81564
  config: botConfig.agentPrompt,
@@ -81546,7 +81572,8 @@ function resolveTelegramLoopCliContext(params) {
81546
81572
  agentId: sessionTarget.agentId,
81547
81573
  routeTimezone: route.timezone,
81548
81574
  botTimezone: route.botTimezone
81549
- }).timezone
81575
+ }).timezone,
81576
+ maxProgressMessagesOverride: options?.maxProgressMessagesOverride
81550
81577
  })
81551
81578
  };
81552
81579
  }
@@ -81706,6 +81733,8 @@ function renderLoopsHelp() {
81706
81733
  " - `--sender <principal>` is required when creating loops, using `slack:<user-id>` or `telegram:<user-id>`",
81707
81734
  " - optional creator display fields: `--sender-name <name>` and `--sender-handle <handle>`",
81708
81735
  " - `--timezone <iana>` is a one-off wall-clock loop override and is frozen on the created loop record",
81736
+ ` - \`${LOOP_START_FLAG} <none|brief|full>\` controls scheduled loop-start notifications only; it does not control injected agent progress messages`,
81737
+ " - `--progress <count>` overrides loop progress-message injection for agent replies; `0` disables progress messages, and omitting the flag inherits the normal clisbot prompt config",
81709
81738
  " - in Telegram forum groups, omitting `--topic-id` targets the parent chat surface; sends then follow Telegram's normal no-`message_thread_id` behavior, which is the General topic when that forum has one",
81710
81739
  "",
81711
81740
  "Expressions:",
@@ -81727,6 +81756,7 @@ function renderLoopsHelp() {
81727
81756
  " - CLI loop creation fails without `--sender` so scheduled prompts can preserve creator identity",
81728
81757
  " - the first wall-clock loop returns `confirmation_required` and does not persist until rerun with `--confirm`",
81729
81758
  " - recurring interval loops and confirmed wall-clock loops are persisted immediately and picked up by the runtime when it is running",
81759
+ " - loop-created agent prompts inherit the normal clisbot prompt config unless `--progress <count>` overrides that loop",
81730
81760
  " - if runtime is stopped, recurring loops activate on the next `clisbot start`",
81731
81761
  " - global `cancel --all` clears the whole app; scoped `cancel --all` clears one routed session",
81732
81762
  " - `cancel --all --app` is accepted only with a scoped session target, matching `/loop cancel --all --app`",
@@ -81757,6 +81787,7 @@ function renderLoopsCreateHelp() {
81757
81787
  " - `--timezone <iana>` freezes a one-off wall-clock timezone on the loop record",
81758
81788
  " - `--confirm` persists the first wall-clock loop after reviewing the confirmation output",
81759
81789
  ` - advanced: \`${LOOP_START_FLAG} <none|brief|full>\` overrides the default scheduled loop-start notification behavior for that recurring loop`,
81790
+ " - advanced: `--progress <count>` overrides loop agent progress-message injection; `0` disables progress messages, and omitting the flag inherits the normal clisbot prompt config",
81760
81791
  "",
81761
81792
  "Examples:",
81762
81793
  ` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --sender slack:U1234567890 every day at 07:00 check CI")}`,
@@ -81766,7 +81797,8 @@ function renderLoopsCreateHelp() {
81766
81797
  "Behavior:",
81767
81798
  " - create without `--sender` fails by design",
81768
81799
  " - the `--sender` platform must match `--channel`",
81769
- " - recurring CLI-created loops persist creator metadata into the session store"
81800
+ " - recurring CLI-created loops persist creator metadata into the session store",
81801
+ " - CLI-created loop prompts inherit the normal clisbot prompt config unless `--progress <count>` is provided"
81770
81802
  ].join(`
81771
81803
  `);
81772
81804
  }
@@ -81977,6 +82009,7 @@ async function getScopedLoopCounts(params) {
81977
82009
  // src/control/loops-cli.ts
81978
82010
  var LOOP_BUSY_RETRY_MS = 250;
81979
82011
  var LOOP_CONFIRM_FLAG = "--confirm";
82012
+ var LOOP_PROGRESS_FLAG = "--progress";
81980
82013
  var LOOP_SENDER_FLAG = "--sender";
81981
82014
  var LOOP_SENDER_NAME_FLAG = "--sender-name";
81982
82015
  var LOOP_SENDER_HANDLE_FLAG = "--sender-handle";
@@ -82136,7 +82169,9 @@ async function waitForSessionIdle(agentService, target) {
82136
82169
  }
82137
82170
  async function executeCountLoop(params) {
82138
82171
  const agentService = new AgentService(params.state.loadedConfig);
82139
- const builtPrompt = params.context.buildLoopPromptText(params.promptText);
82172
+ const builtPrompt = params.context.buildLoopPromptText(params.promptText, params.progressMessages == null ? undefined : {
82173
+ maxProgressMessagesOverride: params.progressMessages
82174
+ });
82140
82175
  console.log(renderLoopStartedMessage({
82141
82176
  mode: "times",
82142
82177
  count: params.count,
@@ -82190,8 +82225,35 @@ function stripLoopCreatorArgs(args) {
82190
82225
  }
82191
82226
  return remaining;
82192
82227
  }
82228
+ function parseLoopProgress(args) {
82229
+ const raw = parseOptionValue3(args, LOOP_PROGRESS_FLAG);
82230
+ if (raw == null) {
82231
+ return;
82232
+ }
82233
+ const parsed = Number.parseInt(raw, 10);
82234
+ if (!Number.isFinite(parsed) || String(parsed) !== raw.trim() || parsed < 0) {
82235
+ throw new Error(`${LOOP_PROGRESS_FLAG} must be a non-negative integer.`);
82236
+ }
82237
+ return parsed;
82238
+ }
82239
+ function stripLoopProgressArgs(args) {
82240
+ const remaining = [];
82241
+ for (let index = 0;index < args.length; index += 1) {
82242
+ const current = args[index];
82243
+ if (current === "--") {
82244
+ remaining.push(...args.slice(index));
82245
+ break;
82246
+ }
82247
+ if (current === LOOP_PROGRESS_FLAG) {
82248
+ index += 1;
82249
+ continue;
82250
+ }
82251
+ remaining.push(current);
82252
+ }
82253
+ return remaining;
82254
+ }
82193
82255
  function parseCreateExpression(rawArgs, explicitCreateSubcommand) {
82194
- const expressionArgs = stripLoopContextArgs(stripLoopCreatorArgs(stripConfirmFlag(explicitCreateSubcommand ? rawArgs.slice(1) : rawArgs)));
82256
+ const expressionArgs = stripLoopContextArgs(stripLoopCreatorArgs(stripLoopProgressArgs(stripConfirmFlag(explicitCreateSubcommand ? rawArgs.slice(1) : rawArgs))));
82195
82257
  const expression = expressionArgs.join(" ").trim();
82196
82258
  if (!expression) {
82197
82259
  throw new Error("Loop creation requires an interval, count, or schedule expression.");
@@ -82271,6 +82333,7 @@ function requireValidIntervalLoop(parsed) {
82271
82333
  async function resolveLoopCreateRequest(state, rawArgs, explicitCreateSubcommand) {
82272
82334
  const confirm = hasFlag4(rawArgs, LOOP_CONFIRM_FLAG);
82273
82335
  const loopTimezone = parseLoopTimezone(rawArgs);
82336
+ const progressMessages = parseLoopProgress(rawArgs);
82274
82337
  const expression = parseCreateExpression(rawArgs, explicitCreateSubcommand);
82275
82338
  const parsed = parseCreateCommand(expression);
82276
82339
  let addressing = parseAddressing(rawArgs);
@@ -82300,7 +82363,8 @@ async function resolveLoopCreateRequest(state, rawArgs, explicitCreateSubcommand
82300
82363
  maxActiveLoops,
82301
82364
  expression,
82302
82365
  confirm,
82303
- loopTimezone
82366
+ loopTimezone,
82367
+ progressMessages
82304
82368
  };
82305
82369
  }
82306
82370
  addressing = await prepareLoopCreateAddressing({
@@ -82331,7 +82395,8 @@ async function resolveLoopCreateRequest(state, rawArgs, explicitCreateSubcommand
82331
82395
  maxActiveLoops,
82332
82396
  expression,
82333
82397
  confirm,
82334
- loopTimezone
82398
+ loopTimezone,
82399
+ progressMessages
82335
82400
  };
82336
82401
  }
82337
82402
  function buildLoopSurfaceBinding2(request) {
@@ -82362,6 +82427,7 @@ function buildRecurringLoopPromptMetadata(request) {
82362
82427
  promptText: request.resolvedPrompt.text,
82363
82428
  promptSummary: summarizeLoopPrompt(request.resolvedPrompt.text, request.resolvedPrompt.maintenancePrompt),
82364
82429
  promptSource: request.resolvedPrompt.maintenancePrompt ? "LOOP.md" : "custom",
82430
+ progressMessages: request.progressMessages,
82365
82431
  loopStart: request.parsed.mode === "times" ? undefined : request.parsed.loopStart,
82366
82432
  maintenancePrompt: request.resolvedPrompt.maintenancePrompt,
82367
82433
  createdBy: request.creator.providerId,
@@ -82502,7 +82568,8 @@ async function createLoop(state, rawArgs, options = {}) {
82502
82568
  context: request.deliveryContext ?? request.context,
82503
82569
  promptText: request.resolvedPrompt.text,
82504
82570
  count: request.parsed.count,
82505
- maintenancePrompt: request.resolvedPrompt.maintenancePrompt
82571
+ maintenancePrompt: request.resolvedPrompt.maintenancePrompt,
82572
+ progressMessages: request.progressMessages
82506
82573
  });
82507
82574
  return;
82508
82575
  }
@@ -83038,10 +83105,9 @@ function createQueueItemForContext(params) {
83038
83105
  });
83039
83106
  }
83040
83107
  function renderQueueCreatedNotification(params) {
83041
- const queueLine = params.positionAhead > 0 ? `Queued: ${params.positionAhead} ahead.` : "Queued.";
83108
+ const queueLine = params.positionAhead > 0 ? `Queued \`${params.queueId}\`: ${params.positionAhead} ahead.` : `Queued \`${params.queueId}\`.`;
83042
83109
  return `${queueLine}
83043
83110
 
83044
- Prompt:
83045
83111
  ${params.promptText.trim()}`;
83046
83112
  }
83047
83113
  async function getQueuePositionAhead(state, sessionKey, itemId) {
@@ -83137,7 +83203,7 @@ async function createQueue(state, args, deps) {
83137
83203
  });
83138
83204
  await state.sessionState.setQueuedItem(resolved, item);
83139
83205
  const positionAhead = await getQueuePositionAhead(state, context.sessionTarget.sessionKey, item.id);
83140
- const text = renderQueueCreatedNotification({ positionAhead, promptText });
83206
+ const text = renderQueueCreatedNotification({ queueId: item.id, positionAhead, promptText });
83141
83207
  await deps.sendQueueCreatedNotification({
83142
83208
  state,
83143
83209
  context,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clisbot",
3
- "version": "0.1.45-beta.11",
3
+ "version": "0.1.45-beta.13",
4
4
  "private": false,
5
5
  "description": "Chat surfaces for durable AI coding agents running in tmux",
6
6
  "license": "MIT",