clisbot 0.1.45-beta.4 → 0.1.45-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -12
- package/config/clisbot.json.template +0 -1
- package/dist/main.js +348 -48
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,7 +118,7 @@ If you want to try first without persisting the token yet, just remove `--persis
|
|
|
118
118
|
Next steps:
|
|
119
119
|
|
|
120
120
|
- For security, DMs default to pairing.
|
|
121
|
-
- Existing `0.1.
|
|
121
|
+
- 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
122
|
- 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
123
|
- 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`.
|
|
124
124
|
- If the effective shared policy is `disabled`, the bot stays silent there for everyone, including owner/admin.
|
|
@@ -139,7 +139,7 @@ Need the step-by-step setup docs instead of the shortest path?
|
|
|
139
139
|
|
|
140
140
|
- Telegram: [Telegram Bot Setup](docs/user-guide/telegram-setup.md)
|
|
141
141
|
- Slack: [Slack App Setup](docs/user-guide/slack-setup.md)
|
|
142
|
-
- Release
|
|
142
|
+
- Release history: [CHANGELOG.md](CHANGELOG.md), [release notes](docs/releases/README.md), [update guide](docs/update/README.md), [operator updates](docs/updates/README.md), and [migration index](docs/migrations/index.md)
|
|
143
143
|
- Slack app manifest template: [app-manifest.json](templates/slack/default/app-manifest.json)
|
|
144
144
|
- Slack app manifest guide: [app-manifest-guide.md](templates/slack/default/app-manifest-guide.md)
|
|
145
145
|
|
|
@@ -152,18 +152,20 @@ What happens next:
|
|
|
152
152
|
- fresh bootstrap only enables the channels you name explicitly
|
|
153
153
|
- after the persisted first run, later restarts can use plain `clisbot start`
|
|
154
154
|
|
|
155
|
-
##
|
|
155
|
+
## Recent Release Highlights
|
|
156
156
|
|
|
157
|
-
-
|
|
158
|
-
-
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
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.
|
|
158
|
+
- `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
|
+
|
|
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.
|
|
162
162
|
|
|
163
163
|
Read the full notes here:
|
|
164
164
|
|
|
165
165
|
- [CHANGELOG.md](CHANGELOG.md)
|
|
166
166
|
- [Release Notes Index](docs/releases/README.md)
|
|
167
|
+
- [v0.1.45 Release Notes](docs/releases/v0.1.45.md)
|
|
168
|
+
- [v0.1.43 Release Notes](docs/releases/v0.1.43.md)
|
|
167
169
|
- [v0.1.39 Release Notes](docs/releases/v0.1.39.md)
|
|
168
170
|
|
|
169
171
|
If you prefer Slack first:
|
|
@@ -191,11 +193,11 @@ bun run start --cli codex --bot-type personal --telegram-bot-token <your-telegra
|
|
|
191
193
|
|
|
192
194
|
Repo-local `bun run start|stop|restart|status|logs|init|pairing` is pinned by `.env` to `CLISBOT_HOME=~/.clisbot-dev`, so local testing does not accidentally reuse your main `~/.clisbot` runtime.
|
|
193
195
|
|
|
194
|
-
|
|
196
|
+
Update note for existing installs:
|
|
195
197
|
|
|
196
198
|
- `v0.1.39` includes breaking changes in config shape and in the main CLI command surface.
|
|
197
|
-
- If you already run an older install, ask Codex or Claude in this repo to update your current config before
|
|
198
|
-
- The
|
|
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:
|
|
199
201
|
|
|
200
202
|
```bash
|
|
201
203
|
clisbot stop
|
|
@@ -337,7 +339,7 @@ Most users only need a small set of commands at first:
|
|
|
337
339
|
|
|
338
340
|
- `clisbot start`: start the bot runtime and create the default first-run setup when needed.
|
|
339
341
|
- `clisbot restart`: restart the runtime cleanly; use this first when the bot stops responding.
|
|
340
|
-
- `clisbot stop`: stop the runtime cleanly before
|
|
342
|
+
- `clisbot stop`: stop the runtime cleanly before updates, config changes, or maintenance.
|
|
341
343
|
- `clisbot stop --hard`: stop the runtime and kill all tmux runner sessions on the configured clisbot socket; use this when stale runner panes, old environment variables, or stuck sessions survive a normal restart.
|
|
342
344
|
- `clisbot status`: check whether the runtime, channels, and active sessions look healthy.
|
|
343
345
|
- `clisbot logs`: inspect recent runtime logs when startup, routing, or replies look wrong.
|
package/dist/main.js
CHANGED
|
@@ -54634,6 +54634,12 @@ function parseCliArgs(argv) {
|
|
|
54634
54634
|
lines: parseLineCount(args.slice(1))
|
|
54635
54635
|
};
|
|
54636
54636
|
}
|
|
54637
|
+
if (command === "update") {
|
|
54638
|
+
return {
|
|
54639
|
+
name: "update",
|
|
54640
|
+
args: args.slice(1)
|
|
54641
|
+
};
|
|
54642
|
+
}
|
|
54637
54643
|
if (command === "timezone") {
|
|
54638
54644
|
return {
|
|
54639
54645
|
name: "timezone",
|
|
@@ -54757,6 +54763,7 @@ function renderCliHelp() {
|
|
|
54757
54763
|
` ${renderCliCommand("status")}`,
|
|
54758
54764
|
` ${renderCliCommand("version")}`,
|
|
54759
54765
|
` ${renderCliCommand("logs [--lines N]")}`,
|
|
54766
|
+
` ${renderCliCommand("update --help")}`,
|
|
54760
54767
|
` ${renderCliCommand("timezone <get|set|clear|doctor>")}`,
|
|
54761
54768
|
` ${renderCliCommand("bots <subcommand>")}`,
|
|
54762
54769
|
` ${renderCliCommand("routes <subcommand>")}`,
|
|
@@ -54781,6 +54788,8 @@ function renderCliHelp() {
|
|
|
54781
54788
|
" status Show runtime process, config, log, tmux socket status, and recent runner sessions.",
|
|
54782
54789
|
" version Show the installed clisbot version.",
|
|
54783
54790
|
" logs Print the most recent clisbot log lines.",
|
|
54791
|
+
" update Print the AI-readable package update guide and release/migration doc links.",
|
|
54792
|
+
` See ${renderCliCommand("update --help", { inline: true })} before asking an agent to update clisbot.`,
|
|
54784
54793
|
" timezone Manage the app-wide wall-clock timezone used by schedules and loops.",
|
|
54785
54794
|
` See ${renderCliCommand("timezone --help", { inline: true })} for override guidance.`,
|
|
54786
54795
|
" bots Manage provider bot identities, credentials, and bot-level fallback settings.",
|
|
@@ -59304,6 +59313,7 @@ function convertLocalDateTimeToUtcMs(params) {
|
|
|
59304
59313
|
var SUPPORTED_AGENT_CLI_TOOLS = ["codex", "claude", "gemini"];
|
|
59305
59314
|
var SUPPORTED_BOOTSTRAP_MODES = ["personal-assistant", "team-assistant"];
|
|
59306
59315
|
var SESSION_ID_PATTERN = "\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\b";
|
|
59316
|
+
var INTERACTIVE_CLI_STARTUP_DELAY_MS = 15000;
|
|
59307
59317
|
var DEFAULT_AGENT_TOOL_TEMPLATES = {
|
|
59308
59318
|
codex: {
|
|
59309
59319
|
command: "codex",
|
|
@@ -59312,7 +59322,7 @@ var DEFAULT_AGENT_TOOL_TEMPLATES = {
|
|
|
59312
59322
|
"--no-alt-screen"
|
|
59313
59323
|
],
|
|
59314
59324
|
trustWorkspace: true,
|
|
59315
|
-
startupDelayMs:
|
|
59325
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
59316
59326
|
startupRetryCount: 2,
|
|
59317
59327
|
startupRetryDelayMs: 1000,
|
|
59318
59328
|
startupReadyPattern: "(?:^|\\s)›\\s",
|
|
@@ -59346,7 +59356,7 @@ var DEFAULT_AGENT_TOOL_TEMPLATES = {
|
|
|
59346
59356
|
command: "claude",
|
|
59347
59357
|
startupOptions: ["--dangerously-skip-permissions"],
|
|
59348
59358
|
trustWorkspace: true,
|
|
59349
|
-
startupDelayMs:
|
|
59359
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
59350
59360
|
startupRetryCount: 2,
|
|
59351
59361
|
startupRetryDelayMs: 1000,
|
|
59352
59362
|
promptSubmitDelayMs: 150,
|
|
@@ -59376,7 +59386,7 @@ var DEFAULT_AGENT_TOOL_TEMPLATES = {
|
|
|
59376
59386
|
command: "gemini",
|
|
59377
59387
|
startupOptions: ["--approval-mode=yolo", "--sandbox=false"],
|
|
59378
59388
|
trustWorkspace: true,
|
|
59379
|
-
startupDelayMs:
|
|
59389
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
59380
59390
|
startupRetryCount: 2,
|
|
59381
59391
|
startupRetryDelayMs: 1000,
|
|
59382
59392
|
startupReadyPattern: "Type your message or @path/to/file",
|
|
@@ -61287,6 +61297,7 @@ var agentsDefaultsSchema = exports_external.object({
|
|
|
61287
61297
|
"-C",
|
|
61288
61298
|
"{workspace}"
|
|
61289
61299
|
],
|
|
61300
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
61290
61301
|
startupReadyPattern: codexStartupReadyPattern,
|
|
61291
61302
|
sessionId: {
|
|
61292
61303
|
create: {
|
|
@@ -61316,6 +61327,7 @@ var agentsDefaultsSchema = exports_external.object({
|
|
|
61316
61327
|
claude: runnerFamilySchema.default({
|
|
61317
61328
|
command: "claude",
|
|
61318
61329
|
args: ["--dangerously-skip-permissions"],
|
|
61330
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
61319
61331
|
sessionId: {
|
|
61320
61332
|
create: {
|
|
61321
61333
|
mode: "explicit",
|
|
@@ -61337,7 +61349,7 @@ var agentsDefaultsSchema = exports_external.object({
|
|
|
61337
61349
|
gemini: runnerFamilySchema.default({
|
|
61338
61350
|
command: "gemini",
|
|
61339
61351
|
args: ["--approval-mode=yolo", "--sandbox=false"],
|
|
61340
|
-
startupDelayMs:
|
|
61352
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
61341
61353
|
startupRetryCount: 2,
|
|
61342
61354
|
startupRetryDelayMs: 1000,
|
|
61343
61355
|
startupReadyPattern: "Type your message or @path/to/file",
|
|
@@ -61737,6 +61749,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
61737
61749
|
"-C",
|
|
61738
61750
|
"{workspace}"
|
|
61739
61751
|
],
|
|
61752
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
61740
61753
|
startupReadyPattern: codexStartupReadyPattern,
|
|
61741
61754
|
sessionId: {
|
|
61742
61755
|
create: {
|
|
@@ -61766,6 +61779,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
61766
61779
|
claude: {
|
|
61767
61780
|
command: "claude",
|
|
61768
61781
|
args: ["--dangerously-skip-permissions"],
|
|
61782
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
61769
61783
|
sessionId: {
|
|
61770
61784
|
create: {
|
|
61771
61785
|
mode: "explicit",
|
|
@@ -61787,7 +61801,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
61787
61801
|
gemini: {
|
|
61788
61802
|
command: "gemini",
|
|
61789
61803
|
args: ["--approval-mode=yolo", "--sandbox=false"],
|
|
61790
|
-
startupDelayMs:
|
|
61804
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
61791
61805
|
startupRetryCount: 2,
|
|
61792
61806
|
startupRetryDelayMs: 1000,
|
|
61793
61807
|
startupReadyPattern: "Type your message or @path/to/file",
|
|
@@ -61864,6 +61878,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
61864
61878
|
"-C",
|
|
61865
61879
|
"{workspace}"
|
|
61866
61880
|
],
|
|
61881
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
61867
61882
|
startupReadyPattern: codexStartupReadyPattern,
|
|
61868
61883
|
sessionId: {
|
|
61869
61884
|
create: {
|
|
@@ -61893,6 +61908,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
61893
61908
|
claude: {
|
|
61894
61909
|
command: "claude",
|
|
61895
61910
|
args: ["--dangerously-skip-permissions"],
|
|
61911
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
61896
61912
|
sessionId: {
|
|
61897
61913
|
create: {
|
|
61898
61914
|
mode: "explicit",
|
|
@@ -61914,7 +61930,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
61914
61930
|
gemini: {
|
|
61915
61931
|
command: "gemini",
|
|
61916
61932
|
args: ["--approval-mode=yolo", "--sandbox=false"],
|
|
61917
|
-
startupDelayMs:
|
|
61933
|
+
startupDelayMs: INTERACTIVE_CLI_STARTUP_DELAY_MS,
|
|
61918
61934
|
startupRetryCount: 2,
|
|
61919
61935
|
startupRetryDelayMs: 1000,
|
|
61920
61936
|
startupReadyPattern: "Type your message or @path/to/file",
|
|
@@ -62028,6 +62044,9 @@ var defaultOwnedRunnerFields = {
|
|
|
62028
62044
|
"promptSubmitDelayMs"
|
|
62029
62045
|
]
|
|
62030
62046
|
};
|
|
62047
|
+
var defaultOwnedRunnerDefaultFields = {
|
|
62048
|
+
startupDelayMs: 3000
|
|
62049
|
+
};
|
|
62031
62050
|
function isRecord5(value) {
|
|
62032
62051
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
62033
62052
|
}
|
|
@@ -62092,6 +62111,14 @@ function pruneRunnerDefaults(config, forceRunnerStartupDefaults) {
|
|
|
62092
62111
|
if (!runner) {
|
|
62093
62112
|
return;
|
|
62094
62113
|
}
|
|
62114
|
+
const runnerDefaults = runner.defaults;
|
|
62115
|
+
if (isRecord5(runnerDefaults)) {
|
|
62116
|
+
for (const [field, defaultValue] of Object.entries(defaultOwnedRunnerDefaultFields)) {
|
|
62117
|
+
if (forceRunnerStartupDefaults || Object.hasOwn(runnerDefaults, field) && areJsonEqual(runnerDefaults[field], defaultValue)) {
|
|
62118
|
+
delete runnerDefaults[field];
|
|
62119
|
+
}
|
|
62120
|
+
}
|
|
62121
|
+
}
|
|
62095
62122
|
for (const toolId of ["codex", "gemini"]) {
|
|
62096
62123
|
const target = runner[toolId];
|
|
62097
62124
|
if (isRecord5(target)) {
|
|
@@ -62136,20 +62163,49 @@ function pruneConfigForPersistence(config, options = {}) {
|
|
|
62136
62163
|
}
|
|
62137
62164
|
|
|
62138
62165
|
// src/config/config-upgrade.ts
|
|
62139
|
-
function
|
|
62166
|
+
function isRecord6(value) {
|
|
62140
62167
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
62141
|
-
return;
|
|
62168
|
+
return false;
|
|
62142
62169
|
}
|
|
62143
|
-
|
|
62144
|
-
|
|
62170
|
+
return true;
|
|
62171
|
+
}
|
|
62172
|
+
function readSchemaVersion(value) {
|
|
62173
|
+
if (!isRecord6(value) || !isRecord6(value.meta)) {
|
|
62145
62174
|
return;
|
|
62146
62175
|
}
|
|
62147
|
-
const schemaVersion = meta.schemaVersion;
|
|
62176
|
+
const schemaVersion = value.meta.schemaVersion;
|
|
62148
62177
|
return typeof schemaVersion === "string" ? schemaVersion.trim() : undefined;
|
|
62149
62178
|
}
|
|
62179
|
+
function readMetaRecord(value) {
|
|
62180
|
+
return isRecord6(value) && isRecord6(value.meta) ? value.meta : {};
|
|
62181
|
+
}
|
|
62150
62182
|
function logUpgradeStage(message) {
|
|
62151
62183
|
console.warn(`clisbot config upgrade: ${message}`);
|
|
62152
62184
|
}
|
|
62185
|
+
function stableConfigText(config) {
|
|
62186
|
+
return `${JSON.stringify(config, null, 2)}
|
|
62187
|
+
`;
|
|
62188
|
+
}
|
|
62189
|
+
function pruneCurrentSchemaStartupDefaults(config) {
|
|
62190
|
+
const nextConfig = structuredClone(config);
|
|
62191
|
+
const meta = readMetaRecord(nextConfig);
|
|
62192
|
+
if (readSchemaVersion(nextConfig) !== CURRENT_SCHEMA_VERSION) {
|
|
62193
|
+
return nextConfig;
|
|
62194
|
+
}
|
|
62195
|
+
const agents = isRecord6(nextConfig.agents) ? nextConfig.agents : undefined;
|
|
62196
|
+
const defaults = isRecord6(agents?.defaults) ? agents.defaults : undefined;
|
|
62197
|
+
const runner = isRecord6(defaults?.runner) ? defaults.runner : undefined;
|
|
62198
|
+
const runnerDefaults = isRecord6(runner?.defaults) ? runner.defaults : undefined;
|
|
62199
|
+
if (runnerDefaults?.startupDelayMs === 3000) {
|
|
62200
|
+
delete runnerDefaults.startupDelayMs;
|
|
62201
|
+
nextConfig.meta = {
|
|
62202
|
+
...meta,
|
|
62203
|
+
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
62204
|
+
lastTouchedAt: new Date().toISOString()
|
|
62205
|
+
};
|
|
62206
|
+
}
|
|
62207
|
+
return nextConfig;
|
|
62208
|
+
}
|
|
62153
62209
|
function renderBackupTimestamp(date = new Date) {
|
|
62154
62210
|
return date.toISOString().replaceAll(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
62155
62211
|
}
|
|
@@ -62172,6 +62228,23 @@ async function upgradeEditableConfigFileIfNeeded(configPath) {
|
|
|
62172
62228
|
const rawConfig = JSON.parse(originalText);
|
|
62173
62229
|
const fromVersion = readSchemaVersion(rawConfig);
|
|
62174
62230
|
if (!shouldUpgradeConfigSchema(fromVersion)) {
|
|
62231
|
+
const persistedConfig2 = pruneCurrentSchemaStartupDefaults(rawConfig);
|
|
62232
|
+
if (stableConfigText(persistedConfig2) !== stableConfigText(rawConfig)) {
|
|
62233
|
+
const backupPath2 = await reserveBackupPath(expandedConfigPath, fromVersion);
|
|
62234
|
+
await writeTextFile(backupPath2, originalText.endsWith(`
|
|
62235
|
+
`) ? originalText : `${originalText}
|
|
62236
|
+
`);
|
|
62237
|
+
const normalizedDocument2 = normalizeConfigDocumentShape(persistedConfig2);
|
|
62238
|
+
assertNoLegacyPrivilegeCommands(normalizedDocument2);
|
|
62239
|
+
clisbotConfigSchema.parse(applyDynamicPathDefaults(normalizedDocument2));
|
|
62240
|
+
await writeTextFile(expandedConfigPath, stableConfigText(persistedConfig2));
|
|
62241
|
+
return {
|
|
62242
|
+
upgraded: false,
|
|
62243
|
+
pruned: true,
|
|
62244
|
+
backupPath: backupPath2,
|
|
62245
|
+
schemaVersion: fromVersion || CURRENT_SCHEMA_VERSION
|
|
62246
|
+
};
|
|
62247
|
+
}
|
|
62175
62248
|
return { upgraded: false };
|
|
62176
62249
|
}
|
|
62177
62250
|
const versionLabel = fromVersion || "legacy";
|
|
@@ -62191,14 +62264,13 @@ async function upgradeEditableConfigFileIfNeeded(configPath) {
|
|
|
62191
62264
|
const persistedConfig = pruneConfigForPersistence(normalizedConfig, {
|
|
62192
62265
|
forceRunnerStartupDefaults: true
|
|
62193
62266
|
});
|
|
62194
|
-
await writeTextFile(expandedConfigPath,
|
|
62267
|
+
await writeTextFile(expandedConfigPath, stableConfigText({
|
|
62195
62268
|
...persistedConfig,
|
|
62196
62269
|
meta: {
|
|
62197
62270
|
...normalizedConfig.meta,
|
|
62198
62271
|
lastTouchedAt: new Date().toISOString()
|
|
62199
62272
|
}
|
|
62200
|
-
}
|
|
62201
|
-
`);
|
|
62273
|
+
}));
|
|
62202
62274
|
logUpgradeStage(`applied ${versionLabel} -> ${CURRENT_SCHEMA_VERSION}; backup: ${collapseHomePath(backupPath)}`);
|
|
62203
62275
|
return {
|
|
62204
62276
|
upgraded: true,
|
|
@@ -62459,7 +62531,6 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
62459
62531
|
socketPath: tmuxSocketPath
|
|
62460
62532
|
},
|
|
62461
62533
|
trustWorkspace: true,
|
|
62462
|
-
startupDelayMs: 3000,
|
|
62463
62534
|
startupRetryCount: 2,
|
|
62464
62535
|
startupRetryDelayMs: 1000,
|
|
62465
62536
|
promptSubmitDelayMs: 150,
|
|
@@ -65667,6 +65738,12 @@ function parseAgentCommand(text, options = {}) {
|
|
|
65667
65738
|
name: "stop"
|
|
65668
65739
|
};
|
|
65669
65740
|
}
|
|
65741
|
+
if (lowered === "new") {
|
|
65742
|
+
return {
|
|
65743
|
+
type: "control",
|
|
65744
|
+
name: "new"
|
|
65745
|
+
};
|
|
65746
|
+
}
|
|
65670
65747
|
if (lowered === "nudge") {
|
|
65671
65748
|
return {
|
|
65672
65749
|
type: "control",
|
|
@@ -65902,6 +65979,7 @@ function renderAgentControlSlashHelp() {
|
|
|
65902
65979
|
"- `/detach`: stop live updates for this thread while still posting the final result here",
|
|
65903
65980
|
"- `/watch every 30s [for 10m]`: post the latest state on an interval until the run settles or the watch window ends",
|
|
65904
65981
|
"- `/stop`: send Escape to interrupt the current conversation session",
|
|
65982
|
+
"- `/new`: trigger a new runner conversation for this routed session and store the new session id",
|
|
65905
65983
|
"- `/nudge`: send one extra Enter to the current tmux session without resending the prompt text",
|
|
65906
65984
|
"- `/followup status`: show the current conversation follow-up policy",
|
|
65907
65985
|
"- `/followup auto`: allow natural follow-up after the bot has replied in-thread",
|
|
@@ -67075,7 +67153,7 @@ function buildReplyStyleHint(identity) {
|
|
|
67075
67153
|
function renderConfigurationGuidance() {
|
|
67076
67154
|
const cliName = getRenderedCliName();
|
|
67077
67155
|
return [
|
|
67078
|
-
`When the user asks to change ${cliName} configuration, use ${cliName} CLI commands; see ${renderCliCommand("--help", { inline: true })}, ${renderCliCommand("bots --help", { inline: true })}, ${renderCliCommand("routes --help", { inline: true })}, or ${renderCliCommand("
|
|
67156
|
+
`When the user asks to change ${cliName} configuration, use ${cliName} CLI commands; see ${renderCliCommand("--help", { inline: true })}, ${renderCliCommand("bots --help", { inline: true })}, ${renderCliCommand("routes --help", { inline: true })}, ${renderCliCommand("auth --help", { inline: true })}, or ${renderCliCommand("update --help", { inline: true })} for details.`,
|
|
67079
67157
|
`For schedule/loop/reminder requests, inspect ${renderCliCommand("loops --help", { inline: true })} and use the loops CLI.`
|
|
67080
67158
|
].join(`
|
|
67081
67159
|
`);
|
|
@@ -68092,6 +68170,16 @@ function isTimerDrivenStatusLine(line) {
|
|
|
68092
68170
|
function hasActiveTimerStatus(snapshot) {
|
|
68093
68171
|
return splitNormalizedLines(snapshot).some((line) => isActiveTimerStatusLine(line));
|
|
68094
68172
|
}
|
|
68173
|
+
function extractLatestActiveTimerStatusLine(snapshot) {
|
|
68174
|
+
const lines = splitNormalizedLines(snapshot);
|
|
68175
|
+
for (let index = lines.length - 1;index >= 0; index -= 1) {
|
|
68176
|
+
const line = lines[index]?.trim() ?? "";
|
|
68177
|
+
if (isActiveTimerStatusLine(line)) {
|
|
68178
|
+
return line;
|
|
68179
|
+
}
|
|
68180
|
+
}
|
|
68181
|
+
return "";
|
|
68182
|
+
}
|
|
68095
68183
|
function shouldDropCodexChromeLine(line) {
|
|
68096
68184
|
const trimmed = line.trim();
|
|
68097
68185
|
if (!trimmed) {
|
|
@@ -68274,7 +68362,11 @@ function getPromptMarker(lines) {
|
|
|
68274
68362
|
}
|
|
68275
68363
|
return null;
|
|
68276
68364
|
}
|
|
68277
|
-
function
|
|
68365
|
+
function slicePromptBlockFrom(lines, index) {
|
|
68366
|
+
return lines.slice(index).join(`
|
|
68367
|
+
`);
|
|
68368
|
+
}
|
|
68369
|
+
function sliceFromLastPromptBlock(raw, cleanSnapshot) {
|
|
68278
68370
|
const lines = splitNormalizedLines(raw);
|
|
68279
68371
|
const marker = getPromptMarker(lines);
|
|
68280
68372
|
if (!marker) {
|
|
@@ -68284,17 +68376,19 @@ function sliceFromLastPromptBlock(raw) {
|
|
|
68284
68376
|
if (!marker.test((lines[index] ?? "").trimStart())) {
|
|
68285
68377
|
continue;
|
|
68286
68378
|
}
|
|
68287
|
-
|
|
68288
|
-
|
|
68379
|
+
const promptTail = slicePromptBlockFrom(lines, index);
|
|
68380
|
+
if (cleanSnapshot(promptTail)) {
|
|
68381
|
+
return promptTail;
|
|
68382
|
+
}
|
|
68289
68383
|
}
|
|
68290
68384
|
return "";
|
|
68291
68385
|
}
|
|
68292
68386
|
function deriveLatestPromptInteractionSnapshot(currentSnapshot) {
|
|
68293
|
-
const promptTail = sliceFromLastPromptBlock(currentSnapshot);
|
|
68387
|
+
const promptTail = sliceFromLastPromptBlock(currentSnapshot, cleanInteractionSnapshot);
|
|
68294
68388
|
return promptTail ? cleanInteractionSnapshot(promptTail) : "";
|
|
68295
68389
|
}
|
|
68296
68390
|
function deriveLatestPromptRunningInteractionSnapshot(currentSnapshot) {
|
|
68297
|
-
const promptTail = sliceFromLastPromptBlock(currentSnapshot);
|
|
68391
|
+
const promptTail = sliceFromLastPromptBlock(currentSnapshot, cleanRunningInteractionSnapshot);
|
|
68298
68392
|
return promptTail ? cleanRunningInteractionSnapshot(promptTail) : "";
|
|
68299
68393
|
}
|
|
68300
68394
|
function deriveInteractionText(initialSnapshot, currentSnapshot) {
|
|
@@ -68849,7 +68943,7 @@ async function captureTmuxSessionIdentity(params) {
|
|
|
68849
68943
|
});
|
|
68850
68944
|
continue;
|
|
68851
68945
|
}
|
|
68852
|
-
const sessionId = extractSessionId(
|
|
68946
|
+
const sessionId = extractSessionId(deriveSessionIdentityText(statusSubmission.submittedSnapshot, snapshot), params.pattern);
|
|
68853
68947
|
if (sessionId) {
|
|
68854
68948
|
await waitForTmuxPaneSettle({
|
|
68855
68949
|
tmux: params.tmux,
|
|
@@ -68864,6 +68958,11 @@ async function captureTmuxSessionIdentity(params) {
|
|
|
68864
68958
|
}
|
|
68865
68959
|
return null;
|
|
68866
68960
|
}
|
|
68961
|
+
function deriveSessionIdentityText(submittedSnapshot, snapshot) {
|
|
68962
|
+
const rawSubmitted = normalizePaneText(submittedSnapshot);
|
|
68963
|
+
const rawSnapshot = normalizePaneText(snapshot);
|
|
68964
|
+
return extractScrolledAppend(rawSubmitted, rawSnapshot) || deriveInteractionText(submittedSnapshot, snapshot);
|
|
68965
|
+
}
|
|
68867
68966
|
async function dismissTmuxTrustPromptIfPresent(params) {
|
|
68868
68967
|
const deadline = Date.now() + Math.max(TRUST_PROMPT_MAX_WAIT_MS, params.startupDelayMs);
|
|
68869
68968
|
while (Date.now() <= deadline) {
|
|
@@ -69344,6 +69443,7 @@ var TMUX_DUPLICATE_SESSION_PATTERN = /duplicate session:/i;
|
|
|
69344
69443
|
var TMUX_TRANSIENT_TARGET_PATTERN = /(?:no current target|can't find pane|can't find window|no such pane|no such window|tmux pane state unavailable)/i;
|
|
69345
69444
|
var SESSION_READY_CAPTURE_RETRY_COUNT = 5;
|
|
69346
69445
|
var SESSION_READY_CAPTURE_RETRY_DELAY_MS = 100;
|
|
69446
|
+
var TRIGGER_NEW_SESSION_CAPTURE_RETRY_COUNT = 3;
|
|
69347
69447
|
var SESSION_ID_CAPTURE_FAILURE_COOLDOWN_MS = 15000;
|
|
69348
69448
|
function summarizeSnapshot(snapshot) {
|
|
69349
69449
|
const compact = snapshot.split(`
|
|
@@ -69465,9 +69565,9 @@ class RunnerService {
|
|
|
69465
69565
|
runnerCommand: resolved.runner.command
|
|
69466
69566
|
});
|
|
69467
69567
|
}
|
|
69468
|
-
async captureSessionIdentity(resolved) {
|
|
69568
|
+
async captureSessionIdentity(resolved, options = {}) {
|
|
69469
69569
|
const capture = resolved.runner.sessionId.capture;
|
|
69470
|
-
if (capture.mode !== "status-command") {
|
|
69570
|
+
if (capture.mode !== "status-command" && !options.forceStatusCommand) {
|
|
69471
69571
|
return null;
|
|
69472
69572
|
}
|
|
69473
69573
|
return captureTmuxSessionIdentity({
|
|
@@ -69481,14 +69581,11 @@ class RunnerService {
|
|
|
69481
69581
|
pollIntervalMs: capture.pollIntervalMs
|
|
69482
69582
|
});
|
|
69483
69583
|
}
|
|
69484
|
-
async
|
|
69584
|
+
async retryRunnerRestartPreservingSessionId(target, resolved, remainingFreshRetries) {
|
|
69485
69585
|
if (remainingFreshRetries <= 0) {
|
|
69486
69586
|
return null;
|
|
69487
69587
|
}
|
|
69488
|
-
await this.
|
|
69489
|
-
await this.sessionState.clearSessionIdEntry(resolved, {
|
|
69490
|
-
runnerCommand: resolved.runner.command
|
|
69491
|
-
});
|
|
69588
|
+
await this.killRunnerAndPreserveSessionId(resolved);
|
|
69492
69589
|
if (resolved.runner.startupRetryDelayMs > 0) {
|
|
69493
69590
|
await sleep(resolved.runner.startupRetryDelayMs);
|
|
69494
69591
|
}
|
|
@@ -69500,10 +69597,10 @@ class RunnerService {
|
|
|
69500
69597
|
if (!isRetryableFreshStartFault(error)) {
|
|
69501
69598
|
return null;
|
|
69502
69599
|
}
|
|
69503
|
-
return this.
|
|
69600
|
+
return this.retryRunnerRestartPreservingSessionId(target, resolved, remainingFreshRetries);
|
|
69504
69601
|
}
|
|
69505
69602
|
async retryAfterStartupTimeout(target, resolved, remainingFreshRetries) {
|
|
69506
|
-
return this.
|
|
69603
|
+
return this.retryRunnerRestartPreservingSessionId(target, resolved, remainingFreshRetries);
|
|
69507
69604
|
}
|
|
69508
69605
|
resolveRemainingFreshRetries(resolved, options) {
|
|
69509
69606
|
if (typeof options.remainingFreshRetries === "number") {
|
|
@@ -69728,7 +69825,7 @@ class RunnerService {
|
|
|
69728
69825
|
if (options.allowFreshRetryBeforePrompt === false || !isRecoverableStartupSessionLoss(error)) {
|
|
69729
69826
|
throw await this.mapSessionError(error, resolved.sessionName, "before prompt submission", resolved.sessionName ? await this.captureSessionSnapshot(resolved).catch(() => "") : "");
|
|
69730
69827
|
}
|
|
69731
|
-
const retried = await this.
|
|
69828
|
+
const retried = await this.retryRunnerRestartPreservingSessionId(target, resolved, resolved.runner.startupRetryCount);
|
|
69732
69829
|
if (!retried) {
|
|
69733
69830
|
throw await this.mapSessionError(error, resolved.sessionName, "before prompt submission", resolved.sessionName ? await this.captureSessionSnapshot(resolved).catch(() => "") : "");
|
|
69734
69831
|
}
|
|
@@ -69753,17 +69850,116 @@ class RunnerService {
|
|
|
69753
69850
|
}
|
|
69754
69851
|
return this.ensureRunnerReady(target, { allowFreshRetryBeforePrompt: false, timingContext });
|
|
69755
69852
|
}
|
|
69756
|
-
async
|
|
69853
|
+
async restartRunnerWithFreshSessionId(target, timingContext) {
|
|
69757
69854
|
const resolved = this.resolveTarget(target);
|
|
69758
69855
|
await this.tmux.killSession(resolved.sessionName).catch(() => {
|
|
69759
69856
|
return;
|
|
69760
69857
|
});
|
|
69858
|
+
console.log(`clisbot clearing stored sessionId for explicit fresh session ${resolved.sessionName}`);
|
|
69761
69859
|
await this.sessionState.clearSessionIdEntry(resolved, { runnerCommand: resolved.runner.command });
|
|
69762
69860
|
return this.ensureRunnerReady(target, {
|
|
69763
69861
|
allowFreshRetryBeforePrompt: false,
|
|
69764
69862
|
timingContext
|
|
69765
69863
|
});
|
|
69766
69864
|
}
|
|
69865
|
+
async triggerNewSession(target) {
|
|
69866
|
+
const resolved = this.resolveTarget(target);
|
|
69867
|
+
if (!await this.tmux.hasSession(resolved.sessionName)) {
|
|
69868
|
+
return this.restartRunnerWithFreshSessionIdForNewCommand(target);
|
|
69869
|
+
}
|
|
69870
|
+
return this.triggerNewSessionInLiveRunner(resolved);
|
|
69871
|
+
}
|
|
69872
|
+
async restartRunnerPreservingSessionId(target, timingContext) {
|
|
69873
|
+
const resolved = this.resolveTarget(target);
|
|
69874
|
+
await this.killRunnerAndPreserveSessionId(resolved);
|
|
69875
|
+
return this.ensureRunnerReady(target, {
|
|
69876
|
+
allowFreshRetryBeforePrompt: false,
|
|
69877
|
+
timingContext
|
|
69878
|
+
});
|
|
69879
|
+
}
|
|
69880
|
+
async triggerNewSessionInLiveRunner(resolved) {
|
|
69881
|
+
const oldSessionId = (await this.sessionState.getEntry(resolved.sessionKey))?.sessionId;
|
|
69882
|
+
const command = this.resolveNewSessionCommand(resolved);
|
|
69883
|
+
let sessionId = null;
|
|
69884
|
+
for (let attempt = 0;attempt < 2; attempt += 1) {
|
|
69885
|
+
await this.submitNewSessionCommand(resolved, command);
|
|
69886
|
+
sessionId = await this.captureNewSessionIdentityAfterTrigger(resolved, oldSessionId);
|
|
69887
|
+
if (sessionId) {
|
|
69888
|
+
break;
|
|
69889
|
+
}
|
|
69890
|
+
}
|
|
69891
|
+
if (!sessionId) {
|
|
69892
|
+
await this.clearSessionIdAfterNewSessionFailure(resolved, command);
|
|
69893
|
+
}
|
|
69894
|
+
await this.sessionState.touchSessionEntry(resolved, {
|
|
69895
|
+
sessionId,
|
|
69896
|
+
runnerCommand: resolved.runner.command,
|
|
69897
|
+
runtime: {
|
|
69898
|
+
state: "idle"
|
|
69899
|
+
}
|
|
69900
|
+
});
|
|
69901
|
+
return {
|
|
69902
|
+
agentId: resolved.agentId,
|
|
69903
|
+
sessionKey: resolved.sessionKey,
|
|
69904
|
+
sessionName: resolved.sessionName,
|
|
69905
|
+
workspacePath: resolved.workspacePath,
|
|
69906
|
+
command,
|
|
69907
|
+
sessionId,
|
|
69908
|
+
restartedRunner: false
|
|
69909
|
+
};
|
|
69910
|
+
}
|
|
69911
|
+
async restartRunnerWithFreshSessionIdForNewCommand(target) {
|
|
69912
|
+
const { resolved } = await this.restartRunnerWithFreshSessionId(target);
|
|
69913
|
+
const entry = await this.sessionState.getEntry(resolved.sessionKey);
|
|
69914
|
+
return {
|
|
69915
|
+
agentId: resolved.agentId,
|
|
69916
|
+
sessionKey: resolved.sessionKey,
|
|
69917
|
+
sessionName: resolved.sessionName,
|
|
69918
|
+
workspacePath: resolved.workspacePath,
|
|
69919
|
+
command: "(fresh runner)",
|
|
69920
|
+
sessionId: entry?.sessionId,
|
|
69921
|
+
restartedRunner: true
|
|
69922
|
+
};
|
|
69923
|
+
}
|
|
69924
|
+
async submitNewSessionCommand(resolved, command) {
|
|
69925
|
+
await submitTmuxSessionInput({
|
|
69926
|
+
tmux: this.tmux,
|
|
69927
|
+
sessionName: resolved.sessionName,
|
|
69928
|
+
text: command,
|
|
69929
|
+
promptSubmitDelayMs: resolved.runner.promptSubmitDelayMs,
|
|
69930
|
+
timingContext: undefined
|
|
69931
|
+
});
|
|
69932
|
+
}
|
|
69933
|
+
async captureNewSessionIdentityAfterTrigger(resolved, oldSessionId) {
|
|
69934
|
+
for (let attempt = 0;attempt < TRIGGER_NEW_SESSION_CAPTURE_RETRY_COUNT; attempt += 1) {
|
|
69935
|
+
const sessionId = await this.captureSessionIdentity(resolved, {
|
|
69936
|
+
forceStatusCommand: true
|
|
69937
|
+
});
|
|
69938
|
+
if (sessionId && sessionId !== oldSessionId) {
|
|
69939
|
+
return sessionId;
|
|
69940
|
+
}
|
|
69941
|
+
if (attempt < TRIGGER_NEW_SESSION_CAPTURE_RETRY_COUNT - 1) {
|
|
69942
|
+
await sleep(SESSION_READY_CAPTURE_RETRY_DELAY_MS);
|
|
69943
|
+
}
|
|
69944
|
+
}
|
|
69945
|
+
return null;
|
|
69946
|
+
}
|
|
69947
|
+
async clearSessionIdAfterNewSessionFailure(resolved, command) {
|
|
69948
|
+
console.log(`clisbot clearing stored sessionId after ${command} because status capture returned no id for ${resolved.sessionName}`);
|
|
69949
|
+
await this.sessionState.clearSessionIdEntry(resolved, {
|
|
69950
|
+
runnerCommand: resolved.runner.command
|
|
69951
|
+
});
|
|
69952
|
+
throw new Error(`${command} completed, but clisbot could not capture a new session id from the runner status command.`);
|
|
69953
|
+
}
|
|
69954
|
+
async killRunnerAndPreserveSessionId(resolved) {
|
|
69955
|
+
await this.tmux.killSession(resolved.sessionName);
|
|
69956
|
+
await this.sessionState.touchSessionEntry(resolved, {
|
|
69957
|
+
runnerCommand: resolved.runner.command
|
|
69958
|
+
});
|
|
69959
|
+
}
|
|
69960
|
+
resolveNewSessionCommand(resolved) {
|
|
69961
|
+
return resolved.runner.command.toLowerCase().includes("gemini") ? "/clear" : "/new";
|
|
69962
|
+
}
|
|
69767
69963
|
async captureTranscript(target) {
|
|
69768
69964
|
const resolved = this.resolveTarget(target);
|
|
69769
69965
|
if (!await this.tmux.hasSession(resolved.sessionName)) {
|
|
@@ -69897,6 +70093,12 @@ function buildRunRecoveryNote(kind, params) {
|
|
|
69897
70093
|
if (kind === "fresh-attempt") {
|
|
69898
70094
|
return "The previous runner session could not be resumed. Opening a fresh runner session 2/2 without replaying your prompt.";
|
|
69899
70095
|
}
|
|
70096
|
+
if (kind === "resume-failed") {
|
|
70097
|
+
return "The previous runner session could not be resumed. The stored session id was preserved; use `/new` to intentionally trigger a new runner conversation.";
|
|
70098
|
+
}
|
|
70099
|
+
if (kind === "manual-new-required") {
|
|
70100
|
+
return "The previous runner session could not be resumed. clisbot preserved the stored session id instead of opening a new conversation automatically. Use `/new` if you want to trigger a new runner conversation, then resend the prompt.";
|
|
70101
|
+
}
|
|
69900
70102
|
return "The previous runner session could not be resumed. clisbot opened a new fresh session, but did not replay your prompt because the prior conversation context is no longer guaranteed. Please resend the full prompt/context to continue.";
|
|
69901
70103
|
}
|
|
69902
70104
|
|
|
@@ -69907,6 +70109,17 @@ function shouldUsePostSubmitBaseline(snapshot, prompt) {
|
|
|
69907
70109
|
const trimmedPrompt = prompt.trim();
|
|
69908
70110
|
return Boolean(trimmedPrompt) && snapshot.includes(trimmedPrompt);
|
|
69909
70111
|
}
|
|
70112
|
+
function appendLatestActiveTimer(snapshot, fullSnapshot) {
|
|
70113
|
+
const timerStatus = extractLatestActiveTimerStatusLine(fullSnapshot);
|
|
70114
|
+
if (!timerStatus) {
|
|
70115
|
+
return snapshot;
|
|
70116
|
+
}
|
|
70117
|
+
const body = splitNormalizedLines(snapshot).filter((line) => !isActiveTimerStatusLine(line)).join(`
|
|
70118
|
+
`).trim();
|
|
70119
|
+
return [body, timerStatus].filter(Boolean).join(`
|
|
70120
|
+
|
|
70121
|
+
`);
|
|
70122
|
+
}
|
|
69910
70123
|
async function monitorTmuxRun(params) {
|
|
69911
70124
|
let baselineSnapshot = params.initialSnapshot;
|
|
69912
70125
|
let previousSnapshot = params.initialSnapshot;
|
|
@@ -69966,10 +70179,11 @@ async function monitorTmuxRun(params) {
|
|
|
69966
70179
|
snapshot: nextRunningTruth,
|
|
69967
70180
|
maxLines: RUNNING_REWRITE_PREVIEW_MAX_LINES
|
|
69968
70181
|
}) : "";
|
|
70182
|
+
const renderedRunningSnapshot = appendLatestActiveTimer(runningSnapshot, snapshot);
|
|
69969
70183
|
previousSnapshot = snapshot;
|
|
69970
70184
|
previousRunningTruth = nextRunningTruth;
|
|
69971
|
-
if (
|
|
69972
|
-
previousRenderedRunningSnapshot =
|
|
70185
|
+
if (renderedRunningSnapshot && renderedRunningSnapshot !== previousRenderedRunningSnapshot) {
|
|
70186
|
+
previousRenderedRunningSnapshot = renderedRunningSnapshot;
|
|
69973
70187
|
sawActivity = true;
|
|
69974
70188
|
if (!firstMeaningfulDeltaLogged) {
|
|
69975
70189
|
firstMeaningfulDeltaLogged = true;
|
|
@@ -69979,7 +70193,7 @@ async function monitorTmuxRun(params) {
|
|
|
69979
70193
|
});
|
|
69980
70194
|
}
|
|
69981
70195
|
await params.onRunning({
|
|
69982
|
-
snapshot:
|
|
70196
|
+
snapshot: renderedRunningSnapshot,
|
|
69983
70197
|
fullSnapshot: snapshot,
|
|
69984
70198
|
initialSnapshot: baselineSnapshot
|
|
69985
70199
|
});
|
|
@@ -70069,11 +70283,15 @@ class ActiveRunInProgressError extends Error {
|
|
|
70069
70283
|
function createDeferred() {
|
|
70070
70284
|
let resolve;
|
|
70071
70285
|
let reject;
|
|
70286
|
+
const promise = new Promise((nextResolve, nextReject) => {
|
|
70287
|
+
resolve = nextResolve;
|
|
70288
|
+
reject = nextReject;
|
|
70289
|
+
});
|
|
70290
|
+
promise.catch(() => {
|
|
70291
|
+
return;
|
|
70292
|
+
});
|
|
70072
70293
|
const deferred = {
|
|
70073
|
-
promise
|
|
70074
|
-
resolve = nextResolve;
|
|
70075
|
-
reject = nextReject;
|
|
70076
|
-
}),
|
|
70294
|
+
promise,
|
|
70077
70295
|
resolve: (value) => {
|
|
70078
70296
|
if (deferred.settled) {
|
|
70079
70297
|
return;
|
|
@@ -70443,21 +70661,21 @@ class SessionService {
|
|
|
70443
70661
|
sessionKey: run.resolved.sessionKey
|
|
70444
70662
|
};
|
|
70445
70663
|
try {
|
|
70446
|
-
const
|
|
70664
|
+
const restarted = await this.runnerSessions.restartRunnerPreservingSessionId(target, params.timingContext);
|
|
70447
70665
|
const currentRun = this.getRun(sessionKey, params.runId);
|
|
70448
70666
|
if (!currentRun) {
|
|
70449
70667
|
return true;
|
|
70450
70668
|
}
|
|
70451
70669
|
const restartedAt = Date.now();
|
|
70452
|
-
currentRun.resolved =
|
|
70670
|
+
currentRun.resolved = restarted.resolved;
|
|
70453
70671
|
currentRun.steeringReady = false;
|
|
70454
70672
|
currentRun.startedAt = restartedAt;
|
|
70455
70673
|
currentRun.latestUpdate = this.createRunUpdate({
|
|
70456
70674
|
resolved: currentRun.resolved,
|
|
70457
70675
|
status: currentRun.latestUpdate.status === "detached" ? "detached" : "running",
|
|
70458
70676
|
snapshot: "",
|
|
70459
|
-
fullSnapshot:
|
|
70460
|
-
initialSnapshot:
|
|
70677
|
+
fullSnapshot: restarted.initialSnapshot,
|
|
70678
|
+
initialSnapshot: restarted.initialSnapshot
|
|
70461
70679
|
});
|
|
70462
70680
|
await this.sessionState.setSessionRuntime(currentRun.resolved, {
|
|
70463
70681
|
state: "running",
|
|
@@ -70467,12 +70685,12 @@ class SessionService {
|
|
|
70467
70685
|
this.startRunMonitor(sessionKey, {
|
|
70468
70686
|
...params,
|
|
70469
70687
|
promptRetryAttempt: 1,
|
|
70470
|
-
initialSnapshot:
|
|
70688
|
+
initialSnapshot: restarted.initialSnapshot,
|
|
70471
70689
|
startedAt: restartedAt
|
|
70472
70690
|
});
|
|
70473
70691
|
return true;
|
|
70474
|
-
} catch (
|
|
70475
|
-
await this.failActiveRun(sessionKey, run.runId, await this.runnerSessions.mapRunError(
|
|
70692
|
+
} catch (restartError) {
|
|
70693
|
+
await this.failActiveRun(sessionKey, run.runId, await this.runnerSessions.mapRunError(restartError, run.resolved.sessionName, run.latestUpdate.fullSnapshot));
|
|
70476
70694
|
return true;
|
|
70477
70695
|
}
|
|
70478
70696
|
}
|
|
@@ -70535,9 +70753,14 @@ class SessionService {
|
|
|
70535
70753
|
if (!currentRun) {
|
|
70536
70754
|
return true;
|
|
70537
70755
|
}
|
|
70756
|
+
if (await this.hasStoredResumableSessionId(currentRun.resolved)) {
|
|
70757
|
+
await this.notifyRecoveryStep(currentRun, buildRunRecoveryNote("resume-failed"));
|
|
70758
|
+
await this.failActiveRun(sessionKey, currentRun.runId, new Error(buildRunRecoveryNote("manual-new-required")));
|
|
70759
|
+
return true;
|
|
70760
|
+
}
|
|
70538
70761
|
await this.notifyRecoveryStep(currentRun, buildRunRecoveryNote("fresh-attempt"));
|
|
70539
70762
|
try {
|
|
70540
|
-
await this.runnerSessions.
|
|
70763
|
+
await this.runnerSessions.restartRunnerWithFreshSessionId(target, params.timingContext);
|
|
70541
70764
|
} catch (freshError) {
|
|
70542
70765
|
await this.failActiveRun(sessionKey, currentRun.runId, await this.runnerSessions.mapRunError(freshError, currentRun.resolved.sessionName, currentRun.latestUpdate.fullSnapshot));
|
|
70543
70766
|
return true;
|
|
@@ -70546,6 +70769,13 @@ class SessionService {
|
|
|
70546
70769
|
return true;
|
|
70547
70770
|
}
|
|
70548
70771
|
}
|
|
70772
|
+
async hasStoredResumableSessionId(resolved) {
|
|
70773
|
+
if (resolved.runner.sessionId.resume.mode !== "command") {
|
|
70774
|
+
return false;
|
|
70775
|
+
}
|
|
70776
|
+
const entry = await this.sessionState.getEntry(resolved.sessionKey);
|
|
70777
|
+
return Boolean(entry?.sessionId);
|
|
70778
|
+
}
|
|
70549
70779
|
startRunMonitor(sessionKey, params) {
|
|
70550
70780
|
const run = this.getRun(sessionKey, params.runId);
|
|
70551
70781
|
if (!run) {
|
|
@@ -70851,6 +71081,9 @@ class AgentService {
|
|
|
70851
71081
|
async nudgeSession(target) {
|
|
70852
71082
|
return this.runnerSessions.nudgeSession(target);
|
|
70853
71083
|
}
|
|
71084
|
+
async triggerNewSession(target) {
|
|
71085
|
+
return this.runnerSessions.triggerNewSession(target);
|
|
71086
|
+
}
|
|
70854
71087
|
async getConversationFollowUpState(target) {
|
|
70855
71088
|
return this.sessionState.getConversationFollowUpState(target);
|
|
70856
71089
|
}
|
|
@@ -72668,6 +72901,23 @@ async function processChannelInteraction(params) {
|
|
|
72668
72901
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
72669
72902
|
return interactionResult;
|
|
72670
72903
|
}
|
|
72904
|
+
if (slashCommand.name === "new") {
|
|
72905
|
+
if (sessionBusy) {
|
|
72906
|
+
await params.postText("This session is busy. Use `/stop` first if you want to interrupt it before triggering a new runner conversation.");
|
|
72907
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
72908
|
+
return interactionResult;
|
|
72909
|
+
}
|
|
72910
|
+
const rotated = await params.agentService.triggerNewSession(params.sessionTarget);
|
|
72911
|
+
await params.postText([
|
|
72912
|
+
`Triggered a new runner conversation for agent \`${rotated.agentId}\`.`,
|
|
72913
|
+
`sessionName: \`${rotated.sessionName}\``,
|
|
72914
|
+
`storedSessionId: \`${rotated.sessionId ?? "none"}\``,
|
|
72915
|
+
rotated.restartedRunner ? "No live runner existed, so clisbot opened a fresh runner session." : `triggerCommand: \`${rotated.command}\``
|
|
72916
|
+
].join(`
|
|
72917
|
+
`));
|
|
72918
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
72919
|
+
return interactionResult;
|
|
72920
|
+
}
|
|
72671
72921
|
if (slashCommand.name === "nudge") {
|
|
72672
72922
|
const nudged = await params.agentService.nudgeSession(params.sessionTarget);
|
|
72673
72923
|
await params.postText(nudged.nudged ? `Sent one extra Enter to agent \`${nudged.agentId}\` session \`${nudged.sessionName}\`.` : `No active or resumable session to nudge for agent \`${nudged.agentId}\`.`);
|
|
@@ -82635,6 +82885,52 @@ async function runTimezoneCli(args) {
|
|
|
82635
82885
|
throw new Error(renderTimezoneHelp());
|
|
82636
82886
|
}
|
|
82637
82887
|
|
|
82888
|
+
// src/control/update-cli.ts
|
|
82889
|
+
var GITHUB_RAW_BASE = "https://raw.githubusercontent.com/longbkit/clisbot/main";
|
|
82890
|
+
function renderUpdateHelp() {
|
|
82891
|
+
return [
|
|
82892
|
+
`${renderCliCommand("update")} / ${renderCliCommand("update --help")}`,
|
|
82893
|
+
"Prints this guide only. Direct update is not supported yet.",
|
|
82894
|
+
"A bot can use this guide to update itself.",
|
|
82895
|
+
"",
|
|
82896
|
+
"Targets:",
|
|
82897
|
+
" stable/latest/default -> clisbot@latest",
|
|
82898
|
+
" beta -> clisbot@beta",
|
|
82899
|
+
" exact version -> clisbot@<version>",
|
|
82900
|
+
"",
|
|
82901
|
+
"Flow:",
|
|
82902
|
+
` 1. ${renderCliCommand("status")}`,
|
|
82903
|
+
" 2. Read docs in priority order.",
|
|
82904
|
+
" 3. npm install -g clisbot@<target> && clisbot restart",
|
|
82905
|
+
` 4. ${renderCliCommand("status")}`,
|
|
82906
|
+
" 5. Report version, health, manual action, and useful release-note highlights.",
|
|
82907
|
+
"",
|
|
82908
|
+
"Docs, read in order:",
|
|
82909
|
+
` 1. Migration index: ${GITHUB_RAW_BASE}/docs/migrations/index.md`,
|
|
82910
|
+
" If Manual action: required, follow its runbook. If none, continue.",
|
|
82911
|
+
` 2. Release quick info: ${GITHUB_RAW_BASE}/docs/releases/README.md`,
|
|
82912
|
+
" Use for release-note map and current release highlights.",
|
|
82913
|
+
` 3. Update guide: ${GITHUB_RAW_BASE}/docs/update/README.md`,
|
|
82914
|
+
" Use for release notes, useful new features, things to try, and user-guide links.",
|
|
82915
|
+
" 4. Full docs: https://github.com/longbkit/clisbot/tree/main/docs",
|
|
82916
|
+
" Use for deep questions. If needed, fetch or clone docs and inspect relevant files.",
|
|
82917
|
+
"",
|
|
82918
|
+
"Rules:",
|
|
82919
|
+
" - Use npm dist-tags, not highest semver.",
|
|
82920
|
+
" - Stable/latest is default; beta only when the user asks.",
|
|
82921
|
+
" - If install mode is not normal global npm, stop and ask."
|
|
82922
|
+
].join(`
|
|
82923
|
+
`);
|
|
82924
|
+
}
|
|
82925
|
+
async function runUpdateCli(args) {
|
|
82926
|
+
const action = args[0];
|
|
82927
|
+
if (!action || action === "--help" || action === "-h" || action === "help") {
|
|
82928
|
+
console.log(renderUpdateHelp());
|
|
82929
|
+
return;
|
|
82930
|
+
}
|
|
82931
|
+
throw new Error(renderUpdateHelp());
|
|
82932
|
+
}
|
|
82933
|
+
|
|
82638
82934
|
// src/control/activity-store.ts
|
|
82639
82935
|
import { dirname as dirname16 } from "node:path";
|
|
82640
82936
|
class ActivityStore {
|
|
@@ -84516,6 +84812,10 @@ async function runBuiltinCommand(command) {
|
|
|
84516
84812
|
await logs(command.lines);
|
|
84517
84813
|
return true;
|
|
84518
84814
|
}
|
|
84815
|
+
if (command.name === "update") {
|
|
84816
|
+
await runUpdateCli(command.args);
|
|
84817
|
+
return true;
|
|
84818
|
+
}
|
|
84519
84819
|
return false;
|
|
84520
84820
|
}
|
|
84521
84821
|
async function runControlCommand(command) {
|