clisbot 0.1.26 → 0.1.28
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/dist/main.js
CHANGED
|
@@ -54694,6 +54694,7 @@ function renderCliHelp() {
|
|
|
54694
54694
|
"",
|
|
54695
54695
|
"Commands:",
|
|
54696
54696
|
` start Seed ${configPath} if missing, apply explicit channel-account bootstrap intent, and start clisbot in the background.`,
|
|
54697
|
+
" See `clisbot start --help` for bootstrap-focused flags and examples.",
|
|
54697
54698
|
" restart Stop the running clisbot process, then start it again.",
|
|
54698
54699
|
" stop Stop the running clisbot process.",
|
|
54699
54700
|
" stop --hard Stop clisbot and kill all tmux sessions on the configured clisbot socket.",
|
|
@@ -54710,20 +54711,26 @@ function renderCliHelp() {
|
|
|
54710
54711
|
" remove slack-group <groupId>",
|
|
54711
54712
|
" set-token <slack-app|slack-bot|telegram-bot> <value>",
|
|
54712
54713
|
" clear-token <slack-app|slack-bot|telegram-bot>",
|
|
54714
|
+
" See `clisbot channels --help` for route policy notes and defaults such as `requireMention`.",
|
|
54713
54715
|
" accounts Manage Slack and Telegram provider accounts plus persistence state.",
|
|
54714
54716
|
" add telegram --account <id> --token <ENV_NAME|${ENV_NAME}|literal> [--persist]",
|
|
54715
54717
|
" add slack --account <id> --app-token <ENV_NAME|${ENV_NAME}|literal> --bot-token <ENV_NAME|${ENV_NAME}|literal> [--persist]",
|
|
54716
54718
|
" persist --channel <slack|telegram> --account <id>",
|
|
54717
54719
|
" persist --all",
|
|
54720
|
+
" See `clisbot accounts --help` for env-vs-mem-vs-persist behavior.",
|
|
54718
54721
|
" loops Inspect or cancel managed recurring loops persisted by `/loop`.",
|
|
54719
54722
|
" list|status",
|
|
54720
54723
|
" cancel <id>",
|
|
54721
54724
|
" cancel --all",
|
|
54725
|
+
" See `clisbot loops --help` for behavior notes.",
|
|
54722
54726
|
" message Run provider message actions such as send, react, read, edit, delete, and pins.",
|
|
54727
|
+
" See `clisbot message --help` for channel-specific syntax.",
|
|
54723
54728
|
" agents Manage configured agents and top-level bindings.",
|
|
54729
|
+
" See `clisbot agents --help` for focused add/bootstrap/binding help.",
|
|
54724
54730
|
" auth Manage app and agent auth roles, principals, and permissions in config. See `clisbot auth --help`.",
|
|
54725
|
-
" pairing Run the pairing control CLI.",
|
|
54731
|
+
" pairing Run the pairing control CLI. See `clisbot pairing --help`.",
|
|
54726
54732
|
` init Seed ${configPath} and optionally create the first agent without starting clisbot.`,
|
|
54733
|
+
" See `clisbot init --help` for bootstrap-focused flags and examples.",
|
|
54727
54734
|
" --version, -v Show the installed clisbot version.",
|
|
54728
54735
|
" --help Show this help text.",
|
|
54729
54736
|
"",
|
|
@@ -54766,6 +54773,35 @@ function buildPairingReply(params) {
|
|
|
54766
54773
|
].join(`
|
|
54767
54774
|
`);
|
|
54768
54775
|
}
|
|
54776
|
+
function buildPairingQueueFullReply(params) {
|
|
54777
|
+
return [
|
|
54778
|
+
"clisbot: access not configured.",
|
|
54779
|
+
"",
|
|
54780
|
+
params.idLine,
|
|
54781
|
+
"",
|
|
54782
|
+
"Pairing queue is full right now.",
|
|
54783
|
+
"",
|
|
54784
|
+
"Ask the bot owner to inspect or clear pending requests with:",
|
|
54785
|
+
`clisbot pairing list ${params.channel}`,
|
|
54786
|
+
`clisbot pairing reject ${params.channel} <code>`,
|
|
54787
|
+
`clisbot pairing clear ${params.channel}`
|
|
54788
|
+
].join(`
|
|
54789
|
+
`);
|
|
54790
|
+
}
|
|
54791
|
+
function buildPairingReplyFromRequest(params) {
|
|
54792
|
+
const code = params.pairingRequest.code.trim();
|
|
54793
|
+
if (!code) {
|
|
54794
|
+
return buildPairingQueueFullReply({
|
|
54795
|
+
channel: params.channel,
|
|
54796
|
+
idLine: params.idLine
|
|
54797
|
+
});
|
|
54798
|
+
}
|
|
54799
|
+
return buildPairingReply({
|
|
54800
|
+
channel: params.channel,
|
|
54801
|
+
idLine: params.idLine,
|
|
54802
|
+
code
|
|
54803
|
+
});
|
|
54804
|
+
}
|
|
54769
54805
|
function renderPairingRequests(params) {
|
|
54770
54806
|
if (!params.requests.length) {
|
|
54771
54807
|
return `No pending ${params.channel} pairing requests.`;
|
|
@@ -54788,7 +54824,7 @@ import path from "node:path";
|
|
|
54788
54824
|
var PAIRING_CODE_LENGTH = 8;
|
|
54789
54825
|
var PAIRING_CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
54790
54826
|
var PAIRING_PENDING_TTL_MS = 60 * 60 * 1000;
|
|
54791
|
-
var PAIRING_PENDING_MAX =
|
|
54827
|
+
var PAIRING_PENDING_MAX = 20;
|
|
54792
54828
|
var PAIRING_STORE_LOCK_OPTIONS = {
|
|
54793
54829
|
retries: {
|
|
54794
54830
|
retries: 10,
|
|
@@ -55067,8 +55103,63 @@ async function approveChannelPairingCode(params) {
|
|
|
55067
55103
|
return { id: entry.id, entry };
|
|
55068
55104
|
});
|
|
55069
55105
|
}
|
|
55106
|
+
async function rejectChannelPairingCode(params) {
|
|
55107
|
+
const code = params.code.trim().toUpperCase();
|
|
55108
|
+
if (!code) {
|
|
55109
|
+
return null;
|
|
55110
|
+
}
|
|
55111
|
+
const filePath = resolvePairingPath(params.channel, params.baseDir);
|
|
55112
|
+
return withFileLock(filePath, { version: 1, requests: [] }, async () => {
|
|
55113
|
+
const { value } = await readJsonFile(filePath, {
|
|
55114
|
+
version: 1,
|
|
55115
|
+
requests: []
|
|
55116
|
+
});
|
|
55117
|
+
const requests = Array.isArray(value.requests) ? value.requests : [];
|
|
55118
|
+
const { requests: pruned, removed } = pruneExpiredRequests(requests, Date.now());
|
|
55119
|
+
const matchIndex = pruned.findIndex((request) => String(request.code ?? "").trim().toUpperCase() === code);
|
|
55120
|
+
if (matchIndex < 0) {
|
|
55121
|
+
if (removed) {
|
|
55122
|
+
await writeJsonFile(filePath, {
|
|
55123
|
+
version: 1,
|
|
55124
|
+
requests: pruned
|
|
55125
|
+
});
|
|
55126
|
+
}
|
|
55127
|
+
return null;
|
|
55128
|
+
}
|
|
55129
|
+
const [rejected] = pruned.splice(matchIndex, 1);
|
|
55130
|
+
await writeJsonFile(filePath, {
|
|
55131
|
+
version: 1,
|
|
55132
|
+
requests: pruned
|
|
55133
|
+
});
|
|
55134
|
+
return rejected ?? null;
|
|
55135
|
+
});
|
|
55136
|
+
}
|
|
55137
|
+
async function clearChannelPairingRequests(params) {
|
|
55138
|
+
const filePath = resolvePairingPath(params.channel, params.baseDir);
|
|
55139
|
+
return withFileLock(filePath, { version: 1, requests: [] }, async () => {
|
|
55140
|
+
const { value } = await readJsonFile(filePath, {
|
|
55141
|
+
version: 1,
|
|
55142
|
+
requests: []
|
|
55143
|
+
});
|
|
55144
|
+
const requests = Array.isArray(value.requests) ? value.requests : [];
|
|
55145
|
+
const { requests: pruned } = pruneExpiredRequests(requests, Date.now());
|
|
55146
|
+
await writeJsonFile(filePath, {
|
|
55147
|
+
version: 1,
|
|
55148
|
+
requests: []
|
|
55149
|
+
});
|
|
55150
|
+
return { cleared: pruned.length };
|
|
55151
|
+
});
|
|
55152
|
+
}
|
|
55070
55153
|
|
|
55071
55154
|
// src/channels/pairing/cli.ts
|
|
55155
|
+
function resolvePairingBaseDir(env = process.env) {
|
|
55156
|
+
const configured = env.CLISBOT_PAIRING_DIR?.trim();
|
|
55157
|
+
if (configured) {
|
|
55158
|
+
return configured;
|
|
55159
|
+
}
|
|
55160
|
+
const legacy = env.TMUX_TALK_PAIRING_DIR?.trim();
|
|
55161
|
+
return legacy || undefined;
|
|
55162
|
+
}
|
|
55072
55163
|
function parseChannel(raw) {
|
|
55073
55164
|
const value = raw?.trim().toLowerCase();
|
|
55074
55165
|
if (value === "slack" || value === "telegram") {
|
|
@@ -55076,9 +55167,33 @@ function parseChannel(raw) {
|
|
|
55076
55167
|
}
|
|
55077
55168
|
throw new Error("Channel required: slack | telegram");
|
|
55078
55169
|
}
|
|
55170
|
+
function renderPairingCliHelp() {
|
|
55171
|
+
return [
|
|
55172
|
+
"clisbot pairing",
|
|
55173
|
+
"",
|
|
55174
|
+
"Usage:",
|
|
55175
|
+
" clisbot pairing --help",
|
|
55176
|
+
" clisbot pairing help",
|
|
55177
|
+
" clisbot pairing list <slack|telegram> [--json]",
|
|
55178
|
+
" clisbot pairing approve <slack|telegram> <code>",
|
|
55179
|
+
" clisbot pairing reject <slack|telegram> <code>",
|
|
55180
|
+
" clisbot pairing clear <slack|telegram>",
|
|
55181
|
+
"",
|
|
55182
|
+
"Notes:",
|
|
55183
|
+
" - `list` shows pending pairing requests for one channel only",
|
|
55184
|
+
" - `approve` moves that sender into the channel allowlist",
|
|
55185
|
+
" - `reject` removes one pending request without allowlisting the sender",
|
|
55186
|
+
" - `clear` drops every pending request for that channel when the queue needs a reset"
|
|
55187
|
+
].join(`
|
|
55188
|
+
`);
|
|
55189
|
+
}
|
|
55079
55190
|
async function runPairingCli(args, writer = console) {
|
|
55080
55191
|
const [command, ...rest] = args;
|
|
55081
|
-
const baseDir =
|
|
55192
|
+
const baseDir = resolvePairingBaseDir();
|
|
55193
|
+
if (!command || command === "--help" || command === "-h" || command === "help") {
|
|
55194
|
+
writer.log(renderPairingCliHelp());
|
|
55195
|
+
return;
|
|
55196
|
+
}
|
|
55082
55197
|
if (command === "list") {
|
|
55083
55198
|
const wantsJson = rest.includes("--json");
|
|
55084
55199
|
const channel = parseChannel(rest.find((value) => !value.startsWith("--")));
|
|
@@ -55103,7 +55218,34 @@ async function runPairingCli(args, writer = console) {
|
|
|
55103
55218
|
writer.log(`Approved ${channel} sender ${approved.id}.`);
|
|
55104
55219
|
return;
|
|
55105
55220
|
}
|
|
55106
|
-
|
|
55221
|
+
if (command === "reject") {
|
|
55222
|
+
const [channelArg, code] = rest;
|
|
55223
|
+
const channel = parseChannel(channelArg);
|
|
55224
|
+
if (!code?.trim()) {
|
|
55225
|
+
throw new Error("Usage: pairing reject <channel> <code>");
|
|
55226
|
+
}
|
|
55227
|
+
const rejected = await rejectChannelPairingCode({
|
|
55228
|
+
channel,
|
|
55229
|
+
code,
|
|
55230
|
+
baseDir
|
|
55231
|
+
});
|
|
55232
|
+
if (!rejected) {
|
|
55233
|
+
throw new Error(`No pending pairing request found for code: ${code}`);
|
|
55234
|
+
}
|
|
55235
|
+
writer.log(`Rejected ${channel} sender ${rejected.id}.`);
|
|
55236
|
+
return;
|
|
55237
|
+
}
|
|
55238
|
+
if (command === "clear") {
|
|
55239
|
+
const [channelArg] = rest;
|
|
55240
|
+
const channel = parseChannel(channelArg);
|
|
55241
|
+
const result = await clearChannelPairingRequests({
|
|
55242
|
+
channel,
|
|
55243
|
+
baseDir
|
|
55244
|
+
});
|
|
55245
|
+
writer.log(`Cleared ${result.cleared} pending ${channel} pairing request(s).`);
|
|
55246
|
+
return;
|
|
55247
|
+
}
|
|
55248
|
+
throw new Error(renderPairingCliHelp());
|
|
55107
55249
|
}
|
|
55108
55250
|
|
|
55109
55251
|
// src/config/agent-tool-presets.ts
|
|
@@ -59283,7 +59425,7 @@ function formatConfiguredRuntimeLimit(params) {
|
|
|
59283
59425
|
if (typeof params.maxRuntimeMin === "number" && Number.isFinite(params.maxRuntimeMin)) {
|
|
59284
59426
|
return `${params.maxRuntimeMin} minute${params.maxRuntimeMin === 1 ? "" : "s"}`;
|
|
59285
59427
|
}
|
|
59286
|
-
return "
|
|
59428
|
+
return "30 minutes";
|
|
59287
59429
|
}
|
|
59288
59430
|
function parseCommandDurationMs(raw) {
|
|
59289
59431
|
const match = raw.trim().match(/^(\d+)(ms|s|m|h)$/i);
|
|
@@ -59991,7 +60133,7 @@ var agentDefaultsSchema = exports_external.object({
|
|
|
59991
60133
|
updateIntervalMs: 2000,
|
|
59992
60134
|
idleTimeoutMs: 6000,
|
|
59993
60135
|
noOutputTimeoutMs: 20000,
|
|
59994
|
-
maxRuntimeMin:
|
|
60136
|
+
maxRuntimeMin: 30,
|
|
59995
60137
|
maxMessageChars: 3500
|
|
59996
60138
|
}),
|
|
59997
60139
|
session: sessionSchema.default({
|
|
@@ -60679,7 +60821,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60679
60821
|
updateIntervalMs: 2000,
|
|
60680
60822
|
idleTimeoutMs: 6000,
|
|
60681
60823
|
noOutputTimeoutMs: 20000,
|
|
60682
|
-
maxRuntimeMin:
|
|
60824
|
+
maxRuntimeMin: 30,
|
|
60683
60825
|
maxMessageChars: 3500
|
|
60684
60826
|
},
|
|
60685
60827
|
session: {
|
|
@@ -61044,6 +61186,32 @@ function parseSingleOption(args, name) {
|
|
|
61044
61186
|
function hasFlag(args, name) {
|
|
61045
61187
|
return args.includes(name);
|
|
61046
61188
|
}
|
|
61189
|
+
function renderAgentsHelp() {
|
|
61190
|
+
return [
|
|
61191
|
+
"clisbot agents",
|
|
61192
|
+
"",
|
|
61193
|
+
"Usage:",
|
|
61194
|
+
" clisbot agents --help",
|
|
61195
|
+
" clisbot agents help",
|
|
61196
|
+
" clisbot agents list [--bindings] [--json]",
|
|
61197
|
+
" clisbot agents add <id> --cli <codex|claude|gemini> [--workspace <path>] [--startup-option <arg>]... [--bootstrap <personal-assistant|team-assistant>] [--bind <channel[:accountId]>]...",
|
|
61198
|
+
" clisbot agents bootstrap <id> --mode <personal-assistant|team-assistant> [--force]",
|
|
61199
|
+
" clisbot agents bindings [--agent <id>] [--json]",
|
|
61200
|
+
" clisbot agents bind --agent <id> --bind <channel[:accountId]>",
|
|
61201
|
+
" clisbot agents unbind --agent <id> [--bind <channel[:accountId]> | --all]",
|
|
61202
|
+
" clisbot agents response-mode <status|set|clear> --agent <id> [capture-pane|message-tool]",
|
|
61203
|
+
" clisbot agents additional-message-mode <status|set|clear> --agent <id> [queue|steer]",
|
|
61204
|
+
"",
|
|
61205
|
+
"Notes:",
|
|
61206
|
+
" - `agents add` is the lower-level manual surface; first-run `clisbot start` and `clisbot init` can bootstrap the first `default` agent for you",
|
|
61207
|
+
" - `--cli` is required on `agents add`; supported tools are `codex`, `claude`, and `gemini`",
|
|
61208
|
+
" - omit `--startup-option` to inherit the built-in startup args for the selected CLI tool",
|
|
61209
|
+
" - `--bind slack`, `--bind telegram`, or `--bind <channel>:<accountId>` creates top-level fallback bindings",
|
|
61210
|
+
" - explicit route `agentId` on Slack or Telegram still wins before these fallback bindings",
|
|
61211
|
+
" - `response-mode` and `additional-message-mode` mutate per-agent overrides under `agents.list[]`"
|
|
61212
|
+
].join(`
|
|
61213
|
+
`);
|
|
61214
|
+
}
|
|
61047
61215
|
function parseResponseMode(raw) {
|
|
61048
61216
|
if (raw === "capture-pane" || raw === "message-tool") {
|
|
61049
61217
|
return raw;
|
|
@@ -61373,7 +61541,11 @@ async function runAgentAdditionalMessageModeCli(args) {
|
|
|
61373
61541
|
async function runAgentsCli(args) {
|
|
61374
61542
|
const subcommand = args[0];
|
|
61375
61543
|
const rest = args.slice(1);
|
|
61376
|
-
if (!subcommand || subcommand === "
|
|
61544
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
|
|
61545
|
+
console.log(renderAgentsHelp());
|
|
61546
|
+
return;
|
|
61547
|
+
}
|
|
61548
|
+
if (subcommand === "list") {
|
|
61377
61549
|
await listAgents(rest);
|
|
61378
61550
|
return;
|
|
61379
61551
|
}
|
|
@@ -61405,7 +61577,7 @@ async function runAgentsCli(args) {
|
|
|
61405
61577
|
await unbindAgent(rest);
|
|
61406
61578
|
return;
|
|
61407
61579
|
}
|
|
61408
|
-
throw new Error(
|
|
61580
|
+
throw new Error(renderAgentsHelp());
|
|
61409
61581
|
}
|
|
61410
61582
|
|
|
61411
61583
|
// src/control/accounts-cli.ts
|
|
@@ -63505,6 +63677,12 @@ function collapseBlankLines(lines) {
|
|
|
63505
63677
|
}
|
|
63506
63678
|
return collapsed;
|
|
63507
63679
|
}
|
|
63680
|
+
var DURATION_STATUS_PATTERN = String.raw`(?:\d+(?:h|m|s))(?:\s+\d+(?:h|m|s)){0,2}`;
|
|
63681
|
+
var CODEX_WORKING_STATUS_PATTERN = new RegExp(String.raw`^(?:[•◦·]\s*)?Working(?:\s*\()?${DURATION_STATUS_PATTERN}\b.*(?:esc to interrupt|interrupt)\)?$`, "i");
|
|
63682
|
+
var CODEX_INTERRUPT_FOOTER_PATTERN = new RegExp(String.raw`^(?:[•◦·]\s*)?${DURATION_STATUS_PATTERN}\s*[•◦·]?\s*esc to interrupt\)?$`, "i");
|
|
63683
|
+
var GEMINI_THINKING_STATUS_PATTERN = new RegExp(String.raw`^Thinking\.\.\. \(esc to cancel,\s*${DURATION_STATUS_PATTERN}\)$`, "i");
|
|
63684
|
+
var CLAUDE_WORKED_STATUS_PATTERN = new RegExp(String.raw`^(?:[✻✽*]\s*)?(?:Worked|Cooked) for ${DURATION_STATUS_PATTERN}$`, "i");
|
|
63685
|
+
var CLAUDE_TIMER_FOOTER_PATTERN = new RegExp(String.raw`\|\s*claude\s*\|.*\|\s*${DURATION_STATUS_PATTERN}\s*$`, "i");
|
|
63508
63686
|
function looksLikeUrlContinuation(line) {
|
|
63509
63687
|
const trimmed = line.trim();
|
|
63510
63688
|
return trimmed.startsWith("(") || trimmed.startsWith("http://") || trimmed.startsWith("https://");
|
|
@@ -63702,7 +63880,14 @@ function isInterruptStatusLine(line) {
|
|
|
63702
63880
|
if (!trimmed) {
|
|
63703
63881
|
return false;
|
|
63704
63882
|
}
|
|
63705
|
-
return
|
|
63883
|
+
return CODEX_WORKING_STATUS_PATTERN.test(trimmed) || CODEX_INTERRUPT_FOOTER_PATTERN.test(trimmed);
|
|
63884
|
+
}
|
|
63885
|
+
function isTimerDrivenStatusLine(line) {
|
|
63886
|
+
const trimmed = line.trim();
|
|
63887
|
+
if (!trimmed) {
|
|
63888
|
+
return false;
|
|
63889
|
+
}
|
|
63890
|
+
return isInterruptStatusLine(trimmed) || GEMINI_THINKING_STATUS_PATTERN.test(trimmed) || CLAUDE_WORKED_STATUS_PATTERN.test(trimmed) || CLAUDE_TIMER_FOOTER_PATTERN.test(trimmed);
|
|
63706
63891
|
}
|
|
63707
63892
|
function shouldDropCodexChromeLine(line) {
|
|
63708
63893
|
const trimmed = line.trim();
|
|
@@ -63716,14 +63901,14 @@ function shouldDropClaudeChromeLine(line) {
|
|
|
63716
63901
|
if (!trimmed) {
|
|
63717
63902
|
return false;
|
|
63718
63903
|
}
|
|
63719
|
-
return trimmed.includes("Claude Code v") || trimmed.includes("Welcome back!") || trimmed.includes("Tips for getting started") || trimmed.includes("Ask Claude to create a new app or clone a repository") || trimmed.includes("Recent activity") || trimmed.includes("No recent activity") || trimmed.includes("API Usage Billing") || trimmed.includes("shift+tab to cycle") || trimmed.includes("ctrl+o to expand") || trimmed.includes("ctrl+b ctrl+b") || trimmed.includes("run in background") || /^~\/\.clisbot\/(?:workspace\/)?[a-z0-9._/-]+$/i.test(trimmed) || trimmed.includes("| claude |") ||
|
|
63904
|
+
return trimmed.includes("Claude Code v") || trimmed.includes("Welcome back!") || trimmed.includes("Tips for getting started") || trimmed.includes("Ask Claude to create a new app or clone a repository") || trimmed.includes("Recent activity") || trimmed.includes("No recent activity") || trimmed.includes("API Usage Billing") || trimmed.includes("shift+tab to cycle") || trimmed.includes("ctrl+o to expand") || trimmed.includes("ctrl+b ctrl+b") || trimmed.includes("run in background") || /^~\/\.clisbot\/(?:workspace\/)?[a-z0-9._/-]+$/i.test(trimmed) || trimmed.includes("| claude |") || isTimerDrivenStatusLine(trimmed) || trimmed.startsWith("⏵⏵") || trimmed.startsWith("❯") || isProgressLine(trimmed) || /^[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+$/.test(trimmed) || /^[╭╰│]/.test(trimmed) || /^─+$/.test(trimmed) || /^[▐▛▜▌▝▘ ]+$/.test(trimmed) || /^[▐▛▜▌▝▘ ]+.+$/.test(trimmed);
|
|
63720
63905
|
}
|
|
63721
63906
|
function shouldDropGeminiChromeLine(line) {
|
|
63722
63907
|
const trimmed = line.trim();
|
|
63723
63908
|
if (!trimmed) {
|
|
63724
63909
|
return false;
|
|
63725
63910
|
}
|
|
63726
|
-
return trimmed.includes("Gemini CLI v") || trimmed.includes("Signed in with Google") || trimmed.includes("Plan:") || /^[▝▜▄▗▟▀ ]+$/.test(trimmed) || trimmed.includes("We're making changes to Gemini CLI") || trimmed.includes("What's Changing:") || trimmed.includes("How it affects you:") || trimmed.includes("Read more: https://goo.gle/geminicli-updates") || trimmed.includes("Skipping project agents due to untrusted folder.") || trimmed.includes("Do you trust the files in this folder?") || trimmed.includes("Trusting a folder allows Gemini CLI to load its local configurations") || trimmed === "1. Trust folder (default)" || trimmed === "2. Trust parent folder (workspaces)" || trimmed === "3. Don't trust" || trimmed.includes("Tips for getting started") || /^Create GEMINI\.md files to customize your interactions$/i.test(trimmed) || /^\/help for more information$/i.test(trimmed) || /^Ask coding questions, edit code or run commands$/i.test(trimmed) || /^Be specific for the best results$/i.test(trimmed) || trimmed.includes("? for shortcuts") || trimmed.includes("YOLO Ctrl+Y") || trimmed.includes("Type your message or @path/to/file") || trimmed.includes("workspace (/directory)") || /^~\/.+\s+\S+\s+no sandbox\s+\S+/i.test(trimmed) ||
|
|
63911
|
+
return trimmed.includes("Gemini CLI v") || trimmed.includes("Signed in with Google") || trimmed.includes("Plan:") || /^[▝▜▄▗▟▀ ]+$/.test(trimmed) || trimmed.includes("We're making changes to Gemini CLI") || trimmed.includes("What's Changing:") || trimmed.includes("How it affects you:") || trimmed.includes("Read more: https://goo.gle/geminicli-updates") || trimmed.includes("Skipping project agents due to untrusted folder.") || trimmed.includes("Do you trust the files in this folder?") || trimmed.includes("Trusting a folder allows Gemini CLI to load its local configurations") || trimmed === "1. Trust folder (default)" || trimmed === "2. Trust parent folder (workspaces)" || trimmed === "3. Don't trust" || trimmed.includes("Tips for getting started") || /^Create GEMINI\.md files to customize your interactions$/i.test(trimmed) || /^\/help for more information$/i.test(trimmed) || /^Ask coding questions, edit code or run commands$/i.test(trimmed) || /^Be specific for the best results$/i.test(trimmed) || trimmed.includes("? for shortcuts") || trimmed.includes("YOLO Ctrl+Y") || trimmed.includes("Type your message or @path/to/file") || trimmed.includes("workspace (/directory)") || /^~\/.+\s+\S+\s+no sandbox\s+\S+/i.test(trimmed) || isTimerDrivenStatusLine(trimmed) || /^[╭╰│]/.test(trimmed) || /^[-▀▄]{10,}$/.test(trimmed) || /^─+$/.test(trimmed);
|
|
63727
63912
|
}
|
|
63728
63913
|
function normalizeBoundaryLine(line) {
|
|
63729
63914
|
return line.trim().replace(/^(?::eight_spoked_asterisk:|[-*•◦·✽✶])\s+/, "");
|
|
@@ -63757,16 +63942,21 @@ function collapseAdjacentDuplicateLines(raw) {
|
|
|
63757
63942
|
return collapseBlankLines(collapsed).join(`
|
|
63758
63943
|
`).trim();
|
|
63759
63944
|
}
|
|
63760
|
-
function
|
|
63945
|
+
function cleanInteractionSnapshotInternal(raw, options) {
|
|
63761
63946
|
const lines = splitNormalizedLines(raw);
|
|
63762
63947
|
const isCodex = looksLikeCodexSnapshot(lines);
|
|
63763
63948
|
const isClaude = looksLikeClaudeSnapshot(lines);
|
|
63764
63949
|
const isGemini = looksLikeGeminiSnapshot(lines);
|
|
63765
63950
|
const promptStripped = isCodex ? dropCodexPromptBlocks(lines) : isClaude ? dropClaudePromptBlocks(lines) : isGemini ? dropGeminiPromptBlocks(lines) : lines;
|
|
63951
|
+
const timerStatusLines = [];
|
|
63766
63952
|
const filtered = promptStripped.filter((line) => {
|
|
63767
63953
|
if (shouldDropDeliveryReportLine(line)) {
|
|
63768
63954
|
return false;
|
|
63769
63955
|
}
|
|
63956
|
+
if (options?.preserveTimerStatusLines && isTimerDrivenStatusLine(line)) {
|
|
63957
|
+
timerStatusLines.push(line.trim());
|
|
63958
|
+
return false;
|
|
63959
|
+
}
|
|
63770
63960
|
if (isCodex && shouldDropCodexChromeLine(line)) {
|
|
63771
63961
|
return false;
|
|
63772
63962
|
}
|
|
@@ -63780,8 +63970,21 @@ function cleanInteractionSnapshot(raw) {
|
|
|
63780
63970
|
});
|
|
63781
63971
|
const normalized = isCodex ? unwrapCodexMessageBlocks(filtered) : isClaude ? unwrapClaudeMessageBlocks(filtered) : isGemini ? filtered.map((line) => line.replace(/^\s*>\s*/, "")) : filtered;
|
|
63782
63972
|
const unwrapped = unwrapSoftWrappedLines(normalized);
|
|
63783
|
-
|
|
63973
|
+
const cleanedBody = collapseAdjacentDuplicateLines(collapseBlankLines(trimBlankLines(unwrapped)).join(`
|
|
63784
63974
|
`));
|
|
63975
|
+
const cleanedTimerStatus = options?.preserveTimerStatusLines ? collapseAdjacentDuplicateLines(collapseBlankLines(trimBlankLines(timerStatusLines)).join(`
|
|
63976
|
+
`)) : "";
|
|
63977
|
+
return [cleanedBody, cleanedTimerStatus].filter(Boolean).join(`
|
|
63978
|
+
|
|
63979
|
+
`).trim();
|
|
63980
|
+
}
|
|
63981
|
+
function cleanInteractionSnapshot(raw) {
|
|
63982
|
+
return cleanInteractionSnapshotInternal(raw);
|
|
63983
|
+
}
|
|
63984
|
+
function cleanRunningInteractionSnapshot(raw) {
|
|
63985
|
+
return cleanInteractionSnapshotInternal(raw, {
|
|
63986
|
+
preserveTimerStatusLines: true
|
|
63987
|
+
});
|
|
63785
63988
|
}
|
|
63786
63989
|
// src/shared/transcript-delta.ts
|
|
63787
63990
|
function diffText(previous, current) {
|
|
@@ -63842,16 +64045,8 @@ function extractScrolledAppend(previous, current) {
|
|
|
63842
64045
|
}
|
|
63843
64046
|
return "";
|
|
63844
64047
|
}
|
|
63845
|
-
function
|
|
63846
|
-
|
|
63847
|
-
const current = cleanInteractionSnapshot(currentSnapshot);
|
|
63848
|
-
if (!current || current === previous) {
|
|
63849
|
-
return "";
|
|
63850
|
-
}
|
|
63851
|
-
if (!previous) {
|
|
63852
|
-
return current;
|
|
63853
|
-
}
|
|
63854
|
-
return extractScrolledAppend(previous, current);
|
|
64048
|
+
function deriveRunningInteractionSnapshot(currentSnapshot) {
|
|
64049
|
+
return cleanRunningInteractionSnapshot(currentSnapshot);
|
|
63855
64050
|
}
|
|
63856
64051
|
function deriveInteractionText(initialSnapshot, currentSnapshot) {
|
|
63857
64052
|
const previous = cleanInteractionSnapshot(initialSnapshot);
|
|
@@ -64593,6 +64788,7 @@ var TMUX_DUPLICATE_SESSION_PATTERN = /duplicate session:/i;
|
|
|
64593
64788
|
var TMUX_TRANSIENT_TARGET_PATTERN = /(?:no current target|can't find pane|can't find window)/i;
|
|
64594
64789
|
var SESSION_READY_CAPTURE_RETRY_COUNT = 5;
|
|
64595
64790
|
var SESSION_READY_CAPTURE_RETRY_DELAY_MS = 100;
|
|
64791
|
+
var SESSION_ID_CAPTURE_FAILURE_COOLDOWN_MS = 15000;
|
|
64596
64792
|
function summarizeSnapshot(snapshot) {
|
|
64597
64793
|
const compact = snapshot.split(`
|
|
64598
64794
|
`).map((line) => line.trim()).filter(Boolean).join(" ").slice(0, 220);
|
|
@@ -64624,6 +64820,7 @@ class RunnerService {
|
|
|
64624
64820
|
sessionState;
|
|
64625
64821
|
resolveTarget;
|
|
64626
64822
|
cleanupInFlight = false;
|
|
64823
|
+
sessionIdentityCaptureRetryAt = new Map;
|
|
64627
64824
|
constructor(loadedConfig, tmux, sessionState, resolveTarget) {
|
|
64628
64825
|
this.loadedConfig = loadedConfig;
|
|
64629
64826
|
this.tmux = tmux;
|
|
@@ -64675,12 +64872,24 @@ class RunnerService {
|
|
|
64675
64872
|
async syncSessionIdentity(resolved) {
|
|
64676
64873
|
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
64677
64874
|
if (existing?.sessionId) {
|
|
64875
|
+
this.sessionIdentityCaptureRetryAt.delete(resolved.sessionKey);
|
|
64678
64876
|
return this.sessionState.touchSessionEntry(resolved, {
|
|
64679
64877
|
sessionId: existing.sessionId,
|
|
64680
64878
|
runnerCommand: resolved.runner.command
|
|
64681
64879
|
});
|
|
64682
64880
|
}
|
|
64881
|
+
const retryAt = this.sessionIdentityCaptureRetryAt.get(resolved.sessionKey) ?? 0;
|
|
64882
|
+
if (retryAt > Date.now()) {
|
|
64883
|
+
return this.sessionState.touchSessionEntry(resolved, {
|
|
64884
|
+
runnerCommand: resolved.runner.command
|
|
64885
|
+
});
|
|
64886
|
+
}
|
|
64683
64887
|
const sessionId = await this.captureSessionIdentity(resolved);
|
|
64888
|
+
if (sessionId) {
|
|
64889
|
+
this.sessionIdentityCaptureRetryAt.delete(resolved.sessionKey);
|
|
64890
|
+
} else {
|
|
64891
|
+
this.sessionIdentityCaptureRetryAt.set(resolved.sessionKey, Date.now() + SESSION_ID_CAPTURE_FAILURE_COOLDOWN_MS);
|
|
64892
|
+
}
|
|
64684
64893
|
return this.sessionState.touchSessionEntry(resolved, {
|
|
64685
64894
|
sessionId,
|
|
64686
64895
|
runnerCommand: resolved.runner.command
|
|
@@ -64835,6 +65044,7 @@ class RunnerService {
|
|
|
64835
65044
|
sessionName: resolved.sessionName,
|
|
64836
65045
|
stateDir: this.loadedConfig.stateDir
|
|
64837
65046
|
});
|
|
65047
|
+
this.sessionIdentityCaptureRetryAt.delete(resolved.sessionKey);
|
|
64838
65048
|
try {
|
|
64839
65049
|
await this.tmux.newSession({
|
|
64840
65050
|
sessionName: resolved.sessionName,
|
|
@@ -65101,11 +65311,12 @@ function buildRunRecoveryNote(kind, params) {
|
|
|
65101
65311
|
var FIRST_OUTPUT_POLL_INTERVAL_MS = 250;
|
|
65102
65312
|
async function monitorTmuxRun(params) {
|
|
65103
65313
|
let previousSnapshot = params.initialSnapshot;
|
|
65104
|
-
let
|
|
65105
|
-
let
|
|
65106
|
-
let
|
|
65314
|
+
let previousRunningSnapshot = "";
|
|
65315
|
+
let lastActivityAt = params.startedAt;
|
|
65316
|
+
let sawActivity = false;
|
|
65107
65317
|
let detachedNotified = params.detachedAlready;
|
|
65108
65318
|
let firstMeaningfulDeltaLogged = false;
|
|
65319
|
+
let noOutputThresholdLogged = false;
|
|
65109
65320
|
if (params.prompt) {
|
|
65110
65321
|
logLatencyDebug("tmux-submit-start", params.timingContext, {
|
|
65111
65322
|
sessionName: params.sessionName,
|
|
@@ -65126,61 +65337,57 @@ async function monitorTmuxRun(params) {
|
|
|
65126
65337
|
});
|
|
65127
65338
|
}
|
|
65128
65339
|
while (true) {
|
|
65129
|
-
await sleep(
|
|
65340
|
+
await sleep(sawActivity ? params.updateIntervalMs : Math.min(params.updateIntervalMs, FIRST_OUTPUT_POLL_INTERVAL_MS));
|
|
65130
65341
|
const snapshot = normalizePaneText(await params.tmux.capturePane(params.sessionName, params.captureLines));
|
|
65131
65342
|
const now = Date.now();
|
|
65132
|
-
|
|
65133
|
-
|
|
65134
|
-
|
|
65135
|
-
|
|
65136
|
-
|
|
65137
|
-
|
|
65138
|
-
if (
|
|
65139
|
-
|
|
65140
|
-
|
|
65141
|
-
|
|
65142
|
-
|
|
65143
|
-
logLatencyDebug("tmux-first-meaningful-delta", params.timingContext, {
|
|
65144
|
-
sessionName: params.sessionName,
|
|
65145
|
-
elapsedMs: now - params.startedAt
|
|
65146
|
-
});
|
|
65147
|
-
}
|
|
65148
|
-
await params.onRunning({
|
|
65149
|
-
snapshot: cumulativeInteractionSnapshot,
|
|
65150
|
-
fullSnapshot: snapshot,
|
|
65151
|
-
initialSnapshot: params.initialSnapshot
|
|
65343
|
+
const runningSnapshot = deriveRunningInteractionSnapshot(snapshot);
|
|
65344
|
+
previousSnapshot = snapshot;
|
|
65345
|
+
if (runningSnapshot && runningSnapshot !== previousRunningSnapshot) {
|
|
65346
|
+
previousRunningSnapshot = runningSnapshot;
|
|
65347
|
+
lastActivityAt = now;
|
|
65348
|
+
sawActivity = true;
|
|
65349
|
+
if (!firstMeaningfulDeltaLogged) {
|
|
65350
|
+
firstMeaningfulDeltaLogged = true;
|
|
65351
|
+
logLatencyDebug("tmux-first-meaningful-delta", params.timingContext, {
|
|
65352
|
+
sessionName: params.sessionName,
|
|
65353
|
+
elapsedMs: now - params.startedAt
|
|
65152
65354
|
});
|
|
65153
65355
|
}
|
|
65356
|
+
await params.onRunning({
|
|
65357
|
+
snapshot: runningSnapshot,
|
|
65358
|
+
fullSnapshot: snapshot,
|
|
65359
|
+
initialSnapshot: params.initialSnapshot
|
|
65360
|
+
});
|
|
65154
65361
|
}
|
|
65155
65362
|
if (!detachedNotified && now - params.startedAt >= params.maxRuntimeMs) {
|
|
65156
65363
|
detachedNotified = true;
|
|
65157
65364
|
await params.onDetached({
|
|
65158
|
-
snapshot:
|
|
65365
|
+
snapshot: previousRunningSnapshot || deriveInteractionText(params.initialSnapshot, snapshot),
|
|
65159
65366
|
fullSnapshot: previousSnapshot,
|
|
65160
65367
|
initialSnapshot: params.initialSnapshot
|
|
65161
65368
|
});
|
|
65162
65369
|
}
|
|
65163
|
-
if (
|
|
65370
|
+
if (sawActivity && now - lastActivityAt >= params.idleTimeoutMs) {
|
|
65164
65371
|
await params.onCompleted({
|
|
65165
|
-
snapshot:
|
|
65372
|
+
snapshot: deriveInteractionText(params.initialSnapshot, previousSnapshot),
|
|
65166
65373
|
fullSnapshot: previousSnapshot,
|
|
65167
65374
|
initialSnapshot: params.initialSnapshot
|
|
65168
65375
|
});
|
|
65169
65376
|
return;
|
|
65170
65377
|
}
|
|
65171
|
-
if (!
|
|
65172
|
-
|
|
65173
|
-
|
|
65174
|
-
|
|
65175
|
-
|
|
65378
|
+
if (!noOutputThresholdLogged && !sawActivity && now - params.startedAt >= params.noOutputTimeoutMs) {
|
|
65379
|
+
noOutputThresholdLogged = true;
|
|
65380
|
+
logLatencyDebug("tmux-no-output-threshold-crossed", params.timingContext, {
|
|
65381
|
+
sessionName: params.sessionName,
|
|
65382
|
+
elapsedMs: now - params.startedAt
|
|
65176
65383
|
});
|
|
65177
|
-
return;
|
|
65178
65384
|
}
|
|
65179
65385
|
}
|
|
65180
65386
|
}
|
|
65181
65387
|
|
|
65182
65388
|
// src/agents/session-service.ts
|
|
65183
65389
|
var OBSERVER_RETRYABLE_FAILURE_LIMIT = 3;
|
|
65390
|
+
var DETACHED_OBSERVER_INTERVAL_MS = 5 * 60000;
|
|
65184
65391
|
function formatObserverError(error) {
|
|
65185
65392
|
return error instanceof Error ? error.stack ?? error.message : String(error);
|
|
65186
65393
|
}
|
|
@@ -65428,7 +65635,10 @@ class SessionService {
|
|
|
65428
65635
|
detached: false
|
|
65429
65636
|
};
|
|
65430
65637
|
}
|
|
65431
|
-
observer.mode = "
|
|
65638
|
+
observer.mode = "poll";
|
|
65639
|
+
observer.intervalMs = DETACHED_OBSERVER_INTERVAL_MS;
|
|
65640
|
+
observer.expiresAt = undefined;
|
|
65641
|
+
observer.lastSentAt = Date.now();
|
|
65432
65642
|
run.observerFailures.delete(observerId);
|
|
65433
65643
|
return {
|
|
65434
65644
|
detached: true
|
|
@@ -65451,7 +65661,19 @@ class SessionService {
|
|
|
65451
65661
|
this.activeRuns.clear();
|
|
65452
65662
|
}
|
|
65453
65663
|
buildDetachedNote(resolved) {
|
|
65454
|
-
return `This session has been running for over ${resolved.stream.maxRuntimeLabel}. clisbot will keep monitoring it and
|
|
65664
|
+
return `This session has been running for over ${resolved.stream.maxRuntimeLabel}. clisbot will keep monitoring it, switch this thread to sparse progress updates, and post the final result here when it completes. Use \`/attach\` to resume live updates, \`/watch every 30s\` for interval updates, or \`/stop\` to interrupt it.`;
|
|
65665
|
+
}
|
|
65666
|
+
applyDetachedObserverPolicy(run) {
|
|
65667
|
+
const now = Date.now();
|
|
65668
|
+
for (const observer of run.observers.values()) {
|
|
65669
|
+
if (observer.mode !== "live") {
|
|
65670
|
+
continue;
|
|
65671
|
+
}
|
|
65672
|
+
observer.mode = "poll";
|
|
65673
|
+
observer.intervalMs = DETACHED_OBSERVER_INTERVAL_MS;
|
|
65674
|
+
observer.expiresAt = undefined;
|
|
65675
|
+
observer.lastSentAt = now;
|
|
65676
|
+
}
|
|
65455
65677
|
}
|
|
65456
65678
|
createRunUpdate(params) {
|
|
65457
65679
|
return {
|
|
@@ -65682,7 +65904,8 @@ class SessionService {
|
|
|
65682
65904
|
startedAt: params.startedAt,
|
|
65683
65905
|
detachedAt: Date.now()
|
|
65684
65906
|
});
|
|
65685
|
-
currentRun
|
|
65907
|
+
await this.notifyRunObservers(currentRun, detachedUpdate);
|
|
65908
|
+
this.applyDetachedObserverPolicy(currentRun);
|
|
65686
65909
|
currentRun.initialResult.resolve(detachedUpdate);
|
|
65687
65910
|
},
|
|
65688
65911
|
onCompleted: async (update) => {
|
|
@@ -65694,16 +65917,6 @@ class SessionService {
|
|
|
65694
65917
|
initialSnapshot: update.initialSnapshot
|
|
65695
65918
|
});
|
|
65696
65919
|
await this.finishActiveRun(sessionKey, runUpdate);
|
|
65697
|
-
},
|
|
65698
|
-
onTimeout: async (update) => {
|
|
65699
|
-
const runUpdate = this.createRunUpdate({
|
|
65700
|
-
resolved: run.resolved,
|
|
65701
|
-
status: "timeout",
|
|
65702
|
-
snapshot: mergeRunSnapshot(params.snapshotPrefix ?? "", update.snapshot),
|
|
65703
|
-
fullSnapshot: update.fullSnapshot,
|
|
65704
|
-
initialSnapshot: update.initialSnapshot
|
|
65705
|
-
});
|
|
65706
|
-
await this.finishActiveRun(sessionKey, runUpdate);
|
|
65707
65920
|
}
|
|
65708
65921
|
});
|
|
65709
65922
|
} catch (error) {
|
|
@@ -65720,6 +65933,28 @@ class SessionService {
|
|
|
65720
65933
|
}
|
|
65721
65934
|
|
|
65722
65935
|
// src/agents/agent-service.ts
|
|
65936
|
+
function shellQuote3(value) {
|
|
65937
|
+
if (/^[a-zA-Z0-9_./:@=-]+$/.test(value)) {
|
|
65938
|
+
return value;
|
|
65939
|
+
}
|
|
65940
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
65941
|
+
}
|
|
65942
|
+
function buildCommandString2(command, args) {
|
|
65943
|
+
return [command, ...args].map(shellQuote3).join(" ");
|
|
65944
|
+
}
|
|
65945
|
+
function stripWorkspaceArgs(args) {
|
|
65946
|
+
const filtered = [];
|
|
65947
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
65948
|
+
const current = args[index];
|
|
65949
|
+
if (current === "-C") {
|
|
65950
|
+
index += 1;
|
|
65951
|
+
continue;
|
|
65952
|
+
}
|
|
65953
|
+
filtered.push(current);
|
|
65954
|
+
}
|
|
65955
|
+
return filtered;
|
|
65956
|
+
}
|
|
65957
|
+
|
|
65723
65958
|
class AgentService {
|
|
65724
65959
|
loadedConfig;
|
|
65725
65960
|
tmuxClient;
|
|
@@ -65810,6 +66045,15 @@ class AgentService {
|
|
|
65810
66045
|
async getSessionRuntime(target) {
|
|
65811
66046
|
return this.sessionState.getSessionRuntime(target);
|
|
65812
66047
|
}
|
|
66048
|
+
async getSessionDiagnostics(target) {
|
|
66049
|
+
const resolved = this.resolveTarget(target);
|
|
66050
|
+
const entry = await this.sessionState.getEntry(target.sessionKey);
|
|
66051
|
+
const sessionId = entry?.sessionId?.trim() || undefined;
|
|
66052
|
+
return {
|
|
66053
|
+
sessionId,
|
|
66054
|
+
resumeCommand: this.buildResumeCommandPreview(resolved, sessionId)
|
|
66055
|
+
};
|
|
66056
|
+
}
|
|
65813
66057
|
async listActiveSessionRuntimes() {
|
|
65814
66058
|
return this.sessionState.listActiveSessionRuntimes();
|
|
65815
66059
|
}
|
|
@@ -66052,6 +66296,21 @@ class AgentService {
|
|
|
66052
66296
|
this.scheduleIntervalLoopTimer(persisted.id, Math.max(0, persisted.nextRunAt - Date.now()));
|
|
66053
66297
|
}
|
|
66054
66298
|
}
|
|
66299
|
+
buildResumeCommandPreview(resolved, sessionId) {
|
|
66300
|
+
if (!sessionId || resolved.runner.sessionId.resume.mode !== "command") {
|
|
66301
|
+
return;
|
|
66302
|
+
}
|
|
66303
|
+
const values = {
|
|
66304
|
+
agentId: resolved.agentId,
|
|
66305
|
+
workspace: resolved.workspacePath,
|
|
66306
|
+
sessionName: resolved.sessionName,
|
|
66307
|
+
sessionKey: resolved.sessionKey,
|
|
66308
|
+
sessionId
|
|
66309
|
+
};
|
|
66310
|
+
const command = resolved.runner.sessionId.resume.command ?? resolved.runner.command;
|
|
66311
|
+
const args = stripWorkspaceArgs(resolved.runner.sessionId.resume.args.map((value) => applyTemplate(value, values)));
|
|
66312
|
+
return buildCommandString2(command, args);
|
|
66313
|
+
}
|
|
66055
66314
|
async isManagedLoopPersisted(managed) {
|
|
66056
66315
|
const entry = await this.sessionState.getEntry(managed.target.sessionKey);
|
|
66057
66316
|
return (entry?.intervalLoops ?? []).some((loop) => loop.id === managed.loop.id);
|
|
@@ -66556,6 +66815,12 @@ function parseAgentCommand(text, options = {}) {
|
|
|
66556
66815
|
if (lowered === "loop") {
|
|
66557
66816
|
const loopText = withoutSlash.slice(command.length).trim();
|
|
66558
66817
|
const loweredLoopText = loopText.toLowerCase();
|
|
66818
|
+
if (!loweredLoopText || loweredLoopText === "help") {
|
|
66819
|
+
return {
|
|
66820
|
+
type: "control",
|
|
66821
|
+
name: "loop-help"
|
|
66822
|
+
};
|
|
66823
|
+
}
|
|
66559
66824
|
if (loweredLoopText === "status") {
|
|
66560
66825
|
return {
|
|
66561
66826
|
type: "loop-control",
|
|
@@ -66603,6 +66868,12 @@ function parseAgentCommand(text, options = {}) {
|
|
|
66603
66868
|
const queueText = withoutSlash.slice(command.length).trim();
|
|
66604
66869
|
const normalizedQueueText = queueText.toLowerCase();
|
|
66605
66870
|
if (lowered === "queue") {
|
|
66871
|
+
if (normalizedQueueText === "help") {
|
|
66872
|
+
return {
|
|
66873
|
+
type: "control",
|
|
66874
|
+
name: "queue-help"
|
|
66875
|
+
};
|
|
66876
|
+
}
|
|
66606
66877
|
if (normalizedQueueText === "list") {
|
|
66607
66878
|
return {
|
|
66608
66879
|
type: "control",
|
|
@@ -66669,7 +66940,7 @@ function renderAgentControlSlashHelp() {
|
|
|
66669
66940
|
"- `/whoami`: show the current platform, route, and sender identity details",
|
|
66670
66941
|
"- `/transcript`: show the current conversation session transcript when the route verbose policy allows it",
|
|
66671
66942
|
"- `/attach`: attach this thread to the active run and resume live updates when it is still processing",
|
|
66672
|
-
"- `/detach`: stop live updates for this thread
|
|
66943
|
+
"- `/detach`: stop live updates for this thread, switch to sparse progress updates, and still allow final settlement here",
|
|
66673
66944
|
"- `/watch every 30s [for 10m]`: post the latest state on an interval until the run settles or the watch window ends",
|
|
66674
66945
|
"- `/stop`: send Escape to interrupt the current conversation session",
|
|
66675
66946
|
"- `/nudge`: send one extra Enter to the current tmux session without resending the prompt text",
|
|
@@ -66686,9 +66957,11 @@ function renderAgentControlSlashHelp() {
|
|
|
66686
66957
|
"- `/additionalmessagemode steer`: send later user messages straight into the active session",
|
|
66687
66958
|
"- `/additionalmessagemode queue`: queue later user messages behind the active run for this surface",
|
|
66688
66959
|
"- `/queue <message>` or `\\q <message>`: enqueue a later message behind the active run and let clisbot deliver it in order",
|
|
66960
|
+
"- `/queue help`: show queue-specific help and examples",
|
|
66689
66961
|
"- `/steer <message>` or `\\s <message>`: inject a steering message into the active run immediately",
|
|
66690
66962
|
"- `/queue list`: show queued messages that have not started yet",
|
|
66691
66963
|
"- `/queue clear`: clear queued messages that have not started yet",
|
|
66964
|
+
"- `/loop help`: show loop-specific help and syntax examples",
|
|
66692
66965
|
...renderLoopHelpLines(),
|
|
66693
66966
|
"- `/bash` followed by a shell command: requires `shellExecute` on the resolved agent role",
|
|
66694
66967
|
"- shortcut prefixes such as `!` run bash only when the resolved agent role allows `shellExecute`",
|
|
@@ -66697,6 +66970,15 @@ function renderAgentControlSlashHelp() {
|
|
|
66697
66970
|
].join(`
|
|
66698
66971
|
`);
|
|
66699
66972
|
}
|
|
66973
|
+
function renderQueueHelpLines() {
|
|
66974
|
+
return [
|
|
66975
|
+
"- `/queue <message>` or `\\q <message>`: enqueue one later message behind the active run",
|
|
66976
|
+
"- `/queue list`: show queued messages that have not started yet",
|
|
66977
|
+
"- `/queue clear`: clear queued messages that have not started yet",
|
|
66978
|
+
"- `/queue help`: show this queue help again",
|
|
66979
|
+
"- `/steer <message>` or `\\s <message>`: inject an immediate steering message instead of queueing"
|
|
66980
|
+
];
|
|
66981
|
+
}
|
|
66700
66982
|
function parseWatchCommand(raw) {
|
|
66701
66983
|
const match = raw.match(/^every\s+(\S+)(?:\s+for\s+(\S+))?$/i);
|
|
66702
66984
|
if (!match) {
|
|
@@ -66918,7 +67200,8 @@ function renderWhoAmIMessage(params) {
|
|
|
66918
67200
|
`platform: \`${params.identity.platform}\``,
|
|
66919
67201
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
66920
67202
|
`agentId: \`${params.route.agentId}\``,
|
|
66921
|
-
`sessionKey: \`${params.sessionTarget.sessionKey}
|
|
67203
|
+
`sessionKey: \`${params.sessionTarget.sessionKey}\``,
|
|
67204
|
+
`storedSessionId: \`${params.sessionDiagnostics.sessionId ?? "(not captured yet)"}\``
|
|
66922
67205
|
];
|
|
66923
67206
|
if (params.identity.senderId) {
|
|
66924
67207
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -66935,7 +67218,7 @@ function renderWhoAmIMessage(params) {
|
|
|
66935
67218
|
if (params.identity.topicId) {
|
|
66936
67219
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
66937
67220
|
}
|
|
66938
|
-
lines.push(`principal: \`${params.auth.principal ?? "(none)"}\``, `principalFormat: \`${renderPrincipalFormat(params.identity)}\``, `principalExample: \`${renderPrincipalExample(params.identity)}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayBypassPairing: \`${params.auth.mayBypassPairing}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `verbose: \`${params.route.verbose}\``);
|
|
67221
|
+
lines.push(`resumeCommand: \`${params.sessionDiagnostics.resumeCommand ?? "(not available yet)"}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `principalFormat: \`${renderPrincipalFormat(params.identity)}\``, `principalExample: \`${renderPrincipalExample(params.identity)}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayBypassPairing: \`${params.auth.mayBypassPairing}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `verbose: \`${params.route.verbose}\``);
|
|
66939
67222
|
return lines.join(`
|
|
66940
67223
|
`);
|
|
66941
67224
|
}
|
|
@@ -66946,7 +67229,8 @@ function renderRouteStatusMessage(params) {
|
|
|
66946
67229
|
`platform: \`${params.identity.platform}\``,
|
|
66947
67230
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
66948
67231
|
`agentId: \`${params.route.agentId}\``,
|
|
66949
|
-
`sessionKey: \`${params.sessionTarget.sessionKey}
|
|
67232
|
+
`sessionKey: \`${params.sessionTarget.sessionKey}\``,
|
|
67233
|
+
`storedSessionId: \`${params.sessionDiagnostics.sessionId ?? "(not captured yet)"}\``
|
|
66950
67234
|
];
|
|
66951
67235
|
if (params.identity.senderId) {
|
|
66952
67236
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -66963,7 +67247,7 @@ function renderRouteStatusMessage(params) {
|
|
|
66963
67247
|
if (params.identity.topicId) {
|
|
66964
67248
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
66965
67249
|
}
|
|
66966
|
-
lines.push(`principal: \`${params.auth.principal ?? "(none)"}\``, `principalFormat: \`${renderPrincipalFormat(params.identity)}\``, `principalExample: \`${renderPrincipalExample(params.identity)}\``, `streaming: \`${params.route.streaming}\``, `response: \`${params.route.response}\``, `responseMode: \`${params.route.responseMode}\``, `additionalMessageMode: \`${params.route.additionalMessageMode}\``, `surfaceNotifications.queueStart: \`${params.route.surfaceNotifications.queueStart}\``, `surfaceNotifications.loopStart: \`${params.route.surfaceNotifications.loopStart}\``, `verbose: \`${params.route.verbose}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `timezone: \`${params.route.timezone ?? "(inherit host/app)"}\``, `followUp.mode: \`${params.followUpState.overrideMode ?? params.route.followUp.mode}\``, `followUp.windowMinutes: \`${formatFollowUpTtlMinutes(params.route.followUp.participationTtlMs)}\``, `run.state: \`${params.runtimeState.state}\``);
|
|
67250
|
+
lines.push(`resumeCommand: \`${params.sessionDiagnostics.resumeCommand ?? "(not available yet)"}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `principalFormat: \`${renderPrincipalFormat(params.identity)}\``, `principalExample: \`${renderPrincipalExample(params.identity)}\``, `streaming: \`${params.route.streaming}\``, `response: \`${params.route.response}\``, `responseMode: \`${params.route.responseMode}\``, `additionalMessageMode: \`${params.route.additionalMessageMode}\``, `surfaceNotifications.queueStart: \`${params.route.surfaceNotifications.queueStart}\``, `surfaceNotifications.loopStart: \`${params.route.surfaceNotifications.loopStart}\``, `verbose: \`${params.route.verbose}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `timezone: \`${params.route.timezone ?? "(inherit host/app)"}\``, `followUp.mode: \`${params.followUpState.overrideMode ?? params.route.followUp.mode}\``, `followUp.windowMinutes: \`${formatFollowUpTtlMinutes(params.route.followUp.participationTtlMs)}\``, `run.state: \`${params.runtimeState.state}\``);
|
|
66967
67251
|
if (params.runtimeState.startedAt) {
|
|
66968
67252
|
lines.push(`run.startedAt: \`${new Date(params.runtimeState.startedAt).toISOString()}\``);
|
|
66969
67253
|
}
|
|
@@ -66977,7 +67261,7 @@ function renderRouteStatusMessage(params) {
|
|
|
66977
67261
|
lines.push(`- \`${loop.id}\` ${renderLoopStatusSchedule(loop)} remaining \`${loop.remainingRuns}\` nextRunAt \`${new Date(loop.nextRunAt).toISOString()}\``);
|
|
66978
67262
|
}
|
|
66979
67263
|
}
|
|
66980
|
-
lines.push("", "Useful commands:", "- `/help`", "- `/whoami`", "- `/status`", "- `/attach`, `/detach`, `/watch every 30s`", "- `/followup status`", "- `/streaming status|on|off|latest|all`", "- `/responsemode status`", "- `/additionalmessagemode status`", "- `/loop status`, `/loop cancel`, `/loop cancel <id>`", "- `/queue <message>`, `/steer <message>`", "- `/queue list`, `/queue clear`", params.route.verbose === "off" ? "- `/transcript` disabled on this route (`verbose: off`)" : "- `/transcript` enabled on this route (`verbose: minimal`)", "- `/bash` requires `shellExecute`");
|
|
67264
|
+
lines.push("", "Useful commands:", "- `/help`", "- `/whoami`", "- `/status`", "- `/attach`, `/detach`, `/watch every 30s`", "- `/followup status`", "- `/streaming status|on|off|latest|all`", "- `/responsemode status`", "- `/additionalmessagemode status`", "- `/loop help`, `/loop status`, `/loop cancel`, `/loop cancel <id>`", "- `/queue help`, `/queue <message>`, `/steer <message>`", "- `/queue list`, `/queue clear`", params.route.verbose === "off" ? "- `/transcript` disabled on this route (`verbose: off`)" : "- `/transcript` enabled on this route (`verbose: minimal`)", "- `/bash` requires `shellExecute`");
|
|
66981
67265
|
return lines.join(`
|
|
66982
67266
|
`);
|
|
66983
67267
|
}
|
|
@@ -67049,9 +67333,22 @@ function renderQueuedMessagesList(items) {
|
|
|
67049
67333
|
return lines.join(`
|
|
67050
67334
|
`).trimEnd();
|
|
67051
67335
|
}
|
|
67336
|
+
function renderQueueUsage() {
|
|
67337
|
+
return [
|
|
67338
|
+
"Queue commands",
|
|
67339
|
+
"",
|
|
67340
|
+
...renderQueueHelpLines(),
|
|
67341
|
+
"",
|
|
67342
|
+
"Notes:",
|
|
67343
|
+
"- use queue when the current run should finish first and the next user request can wait in order",
|
|
67344
|
+
"- use steer when the active run should be nudged or redirected immediately"
|
|
67345
|
+
].join(`
|
|
67346
|
+
`);
|
|
67347
|
+
}
|
|
67052
67348
|
function renderLoopUsage() {
|
|
67053
67349
|
return [
|
|
67054
67350
|
"Usage:",
|
|
67351
|
+
"- `/loop help`",
|
|
67055
67352
|
"- `/loop 5m check CI`",
|
|
67056
67353
|
"- `/loop 1m --force check CI`",
|
|
67057
67354
|
"- `/loop 5m`",
|
|
@@ -67602,6 +67899,7 @@ async function processChannelInteraction(params) {
|
|
|
67602
67899
|
};
|
|
67603
67900
|
let replyRecorded = false;
|
|
67604
67901
|
let renderChain = Promise.resolve();
|
|
67902
|
+
const sessionDiagnostics = await params.agentService.getSessionDiagnostics?.(params.sessionTarget) ?? {};
|
|
67605
67903
|
async function recordReplyIfNeeded() {
|
|
67606
67904
|
if (replyRecorded) {
|
|
67607
67905
|
return;
|
|
@@ -67676,6 +67974,7 @@ async function processChannelInteraction(params) {
|
|
|
67676
67974
|
route: params.route,
|
|
67677
67975
|
auth,
|
|
67678
67976
|
sessionTarget: params.sessionTarget,
|
|
67977
|
+
sessionDiagnostics,
|
|
67679
67978
|
followUpState,
|
|
67680
67979
|
runtimeState,
|
|
67681
67980
|
loopState: {
|
|
@@ -67696,7 +67995,8 @@ async function processChannelInteraction(params) {
|
|
|
67696
67995
|
identity: params.identity,
|
|
67697
67996
|
route: params.route,
|
|
67698
67997
|
auth,
|
|
67699
|
-
sessionTarget: params.sessionTarget
|
|
67998
|
+
sessionTarget: params.sessionTarget,
|
|
67999
|
+
sessionDiagnostics
|
|
67700
68000
|
}));
|
|
67701
68001
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67702
68002
|
return interactionResult;
|
|
@@ -67853,6 +68153,11 @@ async function processChannelInteraction(params) {
|
|
|
67853
68153
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67854
68154
|
return interactionResult;
|
|
67855
68155
|
}
|
|
68156
|
+
if (slashCommand.name === "queue-help") {
|
|
68157
|
+
await params.postText(renderQueueUsage());
|
|
68158
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68159
|
+
return interactionResult;
|
|
68160
|
+
}
|
|
67856
68161
|
if (slashCommand.name === "queue-clear") {
|
|
67857
68162
|
const clearedCount = params.agentService.clearQueuedPrompts?.(params.sessionTarget) ?? 0;
|
|
67858
68163
|
await params.postText(clearedCount > 0 ? `Cleared ${clearedCount} queued message${clearedCount === 1 ? "" : "s"}.` : "Queue was already empty.");
|
|
@@ -67860,6 +68165,11 @@ async function processChannelInteraction(params) {
|
|
|
67860
68165
|
return interactionResult;
|
|
67861
68166
|
}
|
|
67862
68167
|
}
|
|
68168
|
+
if (slashCommand?.type === "control" && slashCommand.name === "loop-help") {
|
|
68169
|
+
await params.postText(renderLoopUsage());
|
|
68170
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68171
|
+
return interactionResult;
|
|
68172
|
+
}
|
|
67863
68173
|
if (slashCommand?.type === "loop-control") {
|
|
67864
68174
|
if (slashCommand.action === "status") {
|
|
67865
68175
|
await params.postText(renderLoopStatusMessage({
|
|
@@ -68686,7 +68996,7 @@ async function clearSlackAssistantThreadStatus(client, target) {
|
|
|
68686
68996
|
|
|
68687
68997
|
// src/channels/processing-indicator.ts
|
|
68688
68998
|
function shouldResolveIndicatorWait(update) {
|
|
68689
|
-
return isTerminalRunStatus(update.status)
|
|
68999
|
+
return isTerminalRunStatus(update.status);
|
|
68690
69000
|
}
|
|
68691
69001
|
async function waitForProcessingIndicatorLifecycle(params) {
|
|
68692
69002
|
if (params.lifecycle !== "active-run") {
|
|
@@ -68817,6 +69127,7 @@ class ConversationProcessingIndicatorCoordinator {
|
|
|
68817
69127
|
}
|
|
68818
69128
|
|
|
68819
69129
|
// src/channels/slack/processing-decoration.ts
|
|
69130
|
+
var DEFAULT_STATUS_REFRESH_INTERVAL_MS = 2000;
|
|
68820
69131
|
async function activateSlackProcessingDecoration(params) {
|
|
68821
69132
|
const [reactionResult, statusResult] = await Promise.allSettled([
|
|
68822
69133
|
params.addReaction(),
|
|
@@ -68838,7 +69149,38 @@ async function activateSlackProcessingDecoration(params) {
|
|
|
68838
69149
|
throw statusResult.reason;
|
|
68839
69150
|
}
|
|
68840
69151
|
}
|
|
69152
|
+
let statusRefreshTimer;
|
|
69153
|
+
let closed = false;
|
|
69154
|
+
let refreshInFlight = false;
|
|
69155
|
+
let refreshPromise;
|
|
69156
|
+
if (statusApplied) {
|
|
69157
|
+
const refreshIntervalMs = Math.max(0, params.statusRefreshIntervalMs ?? DEFAULT_STATUS_REFRESH_INTERVAL_MS);
|
|
69158
|
+
if (refreshIntervalMs > 0) {
|
|
69159
|
+
statusRefreshTimer = setInterval(() => {
|
|
69160
|
+
if (closed || refreshInFlight) {
|
|
69161
|
+
return;
|
|
69162
|
+
}
|
|
69163
|
+
refreshInFlight = true;
|
|
69164
|
+
refreshPromise = params.setStatus().then(() => {
|
|
69165
|
+
return;
|
|
69166
|
+
}).catch((error) => {
|
|
69167
|
+
if (!closed) {
|
|
69168
|
+
params.onUnexpectedError?.("refresh-status", error);
|
|
69169
|
+
}
|
|
69170
|
+
}).finally(() => {
|
|
69171
|
+
refreshInFlight = false;
|
|
69172
|
+
refreshPromise = undefined;
|
|
69173
|
+
});
|
|
69174
|
+
}, refreshIntervalMs);
|
|
69175
|
+
}
|
|
69176
|
+
}
|
|
68841
69177
|
return async () => {
|
|
69178
|
+
closed = true;
|
|
69179
|
+
if (statusRefreshTimer) {
|
|
69180
|
+
clearInterval(statusRefreshTimer);
|
|
69181
|
+
statusRefreshTimer = undefined;
|
|
69182
|
+
}
|
|
69183
|
+
await refreshPromise;
|
|
68842
69184
|
if (reactionApplied) {
|
|
68843
69185
|
try {
|
|
68844
69186
|
await params.removeReaction();
|
|
@@ -69736,20 +70078,21 @@ class SlackSocketService {
|
|
|
69736
70078
|
});
|
|
69737
70079
|
if (!allowed) {
|
|
69738
70080
|
if (dmConfig.policy === "pairing") {
|
|
69739
|
-
const
|
|
70081
|
+
const pairingRequest = await upsertChannelPairingRequest({
|
|
69740
70082
|
channel: "slack",
|
|
69741
70083
|
id: directUserId
|
|
69742
70084
|
});
|
|
69743
|
-
|
|
70085
|
+
const pairingReply = buildPairingReplyFromRequest({
|
|
70086
|
+
channel: "slack",
|
|
70087
|
+
idLine: `Your Slack user id: ${directUserId}`,
|
|
70088
|
+
pairingRequest
|
|
70089
|
+
});
|
|
70090
|
+
if (pairingReply) {
|
|
69744
70091
|
try {
|
|
69745
70092
|
await postSlackText(this.app.client, {
|
|
69746
70093
|
channel: channelId,
|
|
69747
70094
|
threadTs: directReplyThreadTs,
|
|
69748
|
-
text:
|
|
69749
|
-
channel: "slack",
|
|
69750
|
-
idLine: `Your Slack user id: ${directUserId}`,
|
|
69751
|
-
code
|
|
69752
|
-
})
|
|
70095
|
+
text: pairingReply
|
|
69753
70096
|
});
|
|
69754
70097
|
} catch (error) {
|
|
69755
70098
|
console.error("slack pairing reply failed", error);
|
|
@@ -71344,7 +71687,7 @@ class TelegramPollingService {
|
|
|
71344
71687
|
});
|
|
71345
71688
|
if (!allowed) {
|
|
71346
71689
|
if (directMessages.policy === "pairing") {
|
|
71347
|
-
const
|
|
71690
|
+
const pairingRequest = await upsertChannelPairingRequest({
|
|
71348
71691
|
channel: "telegram",
|
|
71349
71692
|
id: senderId,
|
|
71350
71693
|
meta: {
|
|
@@ -71353,15 +71696,16 @@ class TelegramPollingService {
|
|
|
71353
71696
|
lastName: message.from?.last_name
|
|
71354
71697
|
}
|
|
71355
71698
|
});
|
|
71356
|
-
|
|
71699
|
+
const pairingReply = buildPairingReplyFromRequest({
|
|
71700
|
+
channel: "telegram",
|
|
71701
|
+
idLine: `Your Telegram user id: ${senderId}`,
|
|
71702
|
+
pairingRequest
|
|
71703
|
+
});
|
|
71704
|
+
if (pairingReply) {
|
|
71357
71705
|
try {
|
|
71358
71706
|
await callTelegramApi(this.accountConfig.botToken, "sendMessage", {
|
|
71359
71707
|
chat_id: message.chat.id,
|
|
71360
|
-
text:
|
|
71361
|
-
channel: "telegram",
|
|
71362
|
-
idLine: `Your Telegram user id: ${senderId}`,
|
|
71363
|
-
code
|
|
71364
|
-
})
|
|
71708
|
+
text: pairingReply
|
|
71365
71709
|
});
|
|
71366
71710
|
} catch (error) {
|
|
71367
71711
|
console.error("telegram pairing reply failed", error);
|
|
@@ -73035,10 +73379,17 @@ function renderAccountsHelp() {
|
|
|
73035
73379
|
"",
|
|
73036
73380
|
"Usage:",
|
|
73037
73381
|
" clisbot accounts --help",
|
|
73382
|
+
" clisbot accounts help",
|
|
73038
73383
|
" clisbot accounts add telegram --account <id> --token <ENV_NAME|${ENV_NAME}|literal> [--persist]",
|
|
73039
73384
|
" clisbot accounts add slack --account <id> --app-token <ENV_NAME|${ENV_NAME}|literal> --bot-token <ENV_NAME|${ENV_NAME}|literal> [--persist]",
|
|
73040
73385
|
" clisbot accounts persist --channel <slack|telegram> --account <id>",
|
|
73041
|
-
" clisbot accounts persist --all"
|
|
73386
|
+
" clisbot accounts persist --all",
|
|
73387
|
+
"",
|
|
73388
|
+
"Notes:",
|
|
73389
|
+
" - env-style input such as `TELEGRAM_BOT_TOKEN` or `${TELEGRAM_BOT_TOKEN}` keeps the account env-backed in config",
|
|
73390
|
+
" - literal token input without `--persist` stays runtime-only and requires a running clisbot runtime",
|
|
73391
|
+
" - `--persist` writes canonical token files so later plain `clisbot start` can reuse the account safely",
|
|
73392
|
+
" - `persist --all` converts every configured `credentialType=mem` account into canonical token files"
|
|
73042
73393
|
].join(`
|
|
73043
73394
|
`);
|
|
73044
73395
|
}
|
|
@@ -73215,7 +73566,7 @@ async function runAccountsCli(args, deps = {}) {
|
|
|
73215
73566
|
...deps
|
|
73216
73567
|
};
|
|
73217
73568
|
const action = args[0];
|
|
73218
|
-
if (!action || action === "--help" || action === "-h") {
|
|
73569
|
+
if (!action || action === "--help" || action === "-h" || action === "help") {
|
|
73219
73570
|
console.log(renderAccountsHelp());
|
|
73220
73571
|
return;
|
|
73221
73572
|
}
|
|
@@ -73319,6 +73670,7 @@ function renderAuthCliHelp() {
|
|
|
73319
73670
|
" add-user/remove-user mutate roles.<role>.users",
|
|
73320
73671
|
" add-permission/remove-permission mutate roles.<role>.allow",
|
|
73321
73672
|
" agent role edits clone the inherited agent-defaults role into the target agent override on first write",
|
|
73673
|
+
" app `owner` and `admin` principals bypass DM pairing automatically once they are granted",
|
|
73322
73674
|
"",
|
|
73323
73675
|
"Examples:",
|
|
73324
73676
|
" clisbot auth add-user app --role owner --user telegram:1276408333",
|
|
@@ -73581,6 +73933,7 @@ function renderChannelsHelp() {
|
|
|
73581
73933
|
" - Slack private groups need channels.slack.groups.<groupId>",
|
|
73582
73934
|
" - Telegram groups need channels.telegram.groups.<chatId>",
|
|
73583
73935
|
" - Telegram forum topics need channels.telegram.groups.<chatId>.topics.<topicId>",
|
|
73936
|
+
" - route adds for Slack channels, Slack groups, Telegram groups, and Telegram topics default to `requireMention: true` unless you pass `--require-mention false`",
|
|
73584
73937
|
" - Adding a route puts that surface on the allowlist; other channels, groups, or topics still need to be added explicitly",
|
|
73585
73938
|
" - Tune route settings such as requireMention and followUp in clisbot.json when a surface should behave differently",
|
|
73586
73939
|
` - Manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`,
|
|
@@ -73659,6 +74012,7 @@ function renderRouteAddGuidance(params) {
|
|
|
73659
74012
|
console.log(` - route added: ${routePath}`);
|
|
73660
74013
|
console.log(" - direct messages still follow channels.slack.directMessages.policy (`open`, `pairing`, `allowlist`, or `disabled`)");
|
|
73661
74014
|
console.log(` - this ${routeLabel} still follows channels.slack.groupPolicy and route settings such as requireMention and followUp`);
|
|
74015
|
+
console.log(" - new Slack channel/group routes default to `requireMention: true` unless you passed `--require-mention false`");
|
|
73662
74016
|
console.log(" - if you want pairing-style access control for DMs, set channels.slack.directMessages.policy to `pairing`");
|
|
73663
74017
|
console.log(" - if you want stricter route access, keep Slack groups on allowlist and only add the channels/groups you trust");
|
|
73664
74018
|
console.log(` - manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`);
|
|
@@ -73669,6 +74023,7 @@ function renderRouteAddGuidance(params) {
|
|
|
73669
74023
|
console.log(` - route added: ${routePath}`);
|
|
73670
74024
|
console.log(" - direct messages still follow channels.telegram.directMessages.policy (`open`, `pairing`, `allowlist`, or `disabled`)");
|
|
73671
74025
|
console.log(` - this ${params.kind} is now on the Telegram allowlist; other groups or topics still need to be added explicitly`);
|
|
74026
|
+
console.log(" - new Telegram group/topic routes default to `requireMention: true` unless you passed `--require-mention false`");
|
|
73672
74027
|
console.log(" - if you want pairing-style access control for DMs, set channels.telegram.directMessages.policy to `pairing`");
|
|
73673
74028
|
console.log(" - tune route settings such as requireMention and followUp in clisbot.json if this surface should behave differently");
|
|
73674
74029
|
console.log(` - manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`);
|
|
@@ -73867,7 +74222,7 @@ async function addSlackRoute(kind, args) {
|
|
|
73867
74222
|
}
|
|
73868
74223
|
const { config, configPath } = await readEditableConfig(getEditableConfigPath7());
|
|
73869
74224
|
const agentId = getAgentId(args);
|
|
73870
|
-
const requireMention = parseBooleanOption(args, "--require-mention",
|
|
74225
|
+
const requireMention = parseBooleanOption(args, "--require-mention", true);
|
|
73871
74226
|
const target = kind === "channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
73872
74227
|
target[routeId] = {
|
|
73873
74228
|
...target[routeId] ?? {},
|
|
@@ -75098,6 +75453,41 @@ function assertSupportedPlatform(command) {
|
|
|
75098
75453
|
}
|
|
75099
75454
|
|
|
75100
75455
|
// src/control/runtime-bootstrap-cli.ts
|
|
75456
|
+
function hasHelpFlag(args) {
|
|
75457
|
+
return args.includes("--help") || args.includes("-h") || args.includes("help");
|
|
75458
|
+
}
|
|
75459
|
+
function renderBootstrapCommandHelp(commandName) {
|
|
75460
|
+
const behavior = commandName === "start" ? "seed config if needed and start the detached runtime" : "seed config and optionally bootstrap the first agent without starting runtime";
|
|
75461
|
+
return [
|
|
75462
|
+
`clisbot ${commandName}`,
|
|
75463
|
+
"",
|
|
75464
|
+
"Usage:",
|
|
75465
|
+
` clisbot ${commandName} --help`,
|
|
75466
|
+
` clisbot ${commandName} [--cli <codex|claude|gemini>] [--bot-type <personal|team>] [--persist]`,
|
|
75467
|
+
" [--slack-account <id> --slack-app-token <ENV_NAME|${ENV_NAME}|literal> --slack-bot-token <ENV_NAME|${ENV_NAME}|literal>]...",
|
|
75468
|
+
" [--telegram-account <id> --telegram-bot-token <ENV_NAME|${ENV_NAME}|literal>]...",
|
|
75469
|
+
"",
|
|
75470
|
+
"Behavior:",
|
|
75471
|
+
` - ${behavior}`,
|
|
75472
|
+
" - first-run agent bootstrap needs both `--cli` and `--bot-type`",
|
|
75473
|
+
" - `--bot-type personal` maps to `personal-assistant`; `--bot-type team` maps to `team-assistant`",
|
|
75474
|
+
" - explicit credential flags only enable the channels and accounts you named in this command",
|
|
75475
|
+
" - env-style values such as `SLACK_APP_TOKEN` or `${SLACK_APP_TOKEN}` stay env-backed in config",
|
|
75476
|
+
commandName === "start" ? " - literal token values without `--persist` stay runtime-only for this start invocation" : " - literal token values on `init` require `--persist` because no runtime exists yet",
|
|
75477
|
+
" - `--persist` writes canonical credential files so later plain `clisbot start` can reuse them",
|
|
75478
|
+
"",
|
|
75479
|
+
"Examples:",
|
|
75480
|
+
` clisbot ${commandName} --cli codex --bot-type personal --telegram-bot-token TELEGRAM_BOT_TOKEN`,
|
|
75481
|
+
` clisbot ${commandName} --cli codex --bot-type team --slack-app-token SLACK_APP_TOKEN --slack-bot-token SLACK_BOT_TOKEN`,
|
|
75482
|
+
` clisbot ${commandName} --cli gemini --bot-type personal --telegram-bot-token "$TELEGRAM_BOT_TOKEN" --persist`,
|
|
75483
|
+
"",
|
|
75484
|
+
"Related help:",
|
|
75485
|
+
" - `clisbot agents --help` for lower-level agent bootstrap and binding control",
|
|
75486
|
+
" - `clisbot accounts --help` for account persistence after first run",
|
|
75487
|
+
" - `clisbot channels --help` for route setup after bootstrap"
|
|
75488
|
+
].join(`
|
|
75489
|
+
`);
|
|
75490
|
+
}
|
|
75101
75491
|
function getPrimaryWorkspacePath(summary) {
|
|
75102
75492
|
const preferredAgentId = summary.channelSummaries.find((channel) => channel.enabled)?.defaultAgentId ?? "default";
|
|
75103
75493
|
return summary.agentSummaries.find((agent) => agent.id === preferredAgentId)?.workspacePath ?? summary.agentSummaries[0]?.workspacePath;
|
|
@@ -75314,6 +75704,10 @@ async function printStartedRuntimeSummary(pid, configPath, logPath) {
|
|
|
75314
75704
|
}
|
|
75315
75705
|
}
|
|
75316
75706
|
async function initConfig(args = []) {
|
|
75707
|
+
if (hasHelpFlag(args)) {
|
|
75708
|
+
console.log(renderBootstrapCommandHelp("init"));
|
|
75709
|
+
return;
|
|
75710
|
+
}
|
|
75317
75711
|
const state = await prepareBootstrapState(args, "init");
|
|
75318
75712
|
if (!state) {
|
|
75319
75713
|
return;
|
|
@@ -75332,6 +75726,10 @@ async function initConfig(args = []) {
|
|
|
75332
75726
|
}
|
|
75333
75727
|
}
|
|
75334
75728
|
async function start(args = []) {
|
|
75729
|
+
if (hasHelpFlag(args)) {
|
|
75730
|
+
console.log(renderBootstrapCommandHelp("start"));
|
|
75731
|
+
return;
|
|
75732
|
+
}
|
|
75335
75733
|
const runtimeStatus = await getRuntimeStatus();
|
|
75336
75734
|
const bootstrapFlags = parseBootstrapFlags(args);
|
|
75337
75735
|
const restartForLiteralBootstrap = runtimeStatus.running && hasLiteralMemCredentials(bootstrapFlags);
|
package/package.json
CHANGED
|
@@ -90,7 +90,7 @@ These are broad or legacy-looking for the current `clisbot` Slack design and sho
|
|
|
90
90
|
| `function_executed` event | Not consumed by current code |
|
|
91
91
|
| `app_home.messages_tab_enabled` and related App Home surface config | Not required for current chat-routing behavior |
|
|
92
92
|
| `org_deploy_enabled` | Not needed for the current local or workspace-level setup story |
|
|
93
|
-
| `
|
|
93
|
+
| `function_runtime` | Not needed for the current Slack Socket Mode bot |
|
|
94
94
|
|
|
95
95
|
## Practical Recommendation
|
|
96
96
|
|