clisbot 0.1.26 → 0.1.29
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
|
@@ -4,43 +4,25 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
function __accessProp(key) {
|
|
8
|
-
return this[key];
|
|
9
|
-
}
|
|
10
|
-
var __toESMCache_node;
|
|
11
|
-
var __toESMCache_esm;
|
|
12
7
|
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
-
var canCache = mod != null && typeof mod === "object";
|
|
14
|
-
if (canCache) {
|
|
15
|
-
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
-
var cached = cache.get(mod);
|
|
17
|
-
if (cached)
|
|
18
|
-
return cached;
|
|
19
|
-
}
|
|
20
8
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
9
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
10
|
for (let key of __getOwnPropNames(mod))
|
|
23
11
|
if (!__hasOwnProp.call(to, key))
|
|
24
12
|
__defProp(to, key, {
|
|
25
|
-
get:
|
|
13
|
+
get: () => mod[key],
|
|
26
14
|
enumerable: true
|
|
27
15
|
});
|
|
28
|
-
if (canCache)
|
|
29
|
-
cache.set(mod, to);
|
|
30
16
|
return to;
|
|
31
17
|
};
|
|
32
18
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
-
var __returnValue = (v) => v;
|
|
34
|
-
function __exportSetter(name, newValue) {
|
|
35
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
36
|
-
}
|
|
37
19
|
var __export = (target, all) => {
|
|
38
20
|
for (var name in all)
|
|
39
21
|
__defProp(target, name, {
|
|
40
22
|
get: all[name],
|
|
41
23
|
enumerable: true,
|
|
42
24
|
configurable: true,
|
|
43
|
-
set:
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
44
26
|
});
|
|
45
27
|
};
|
|
46
28
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
@@ -54694,6 +54676,7 @@ function renderCliHelp() {
|
|
|
54694
54676
|
"",
|
|
54695
54677
|
"Commands:",
|
|
54696
54678
|
` start Seed ${configPath} if missing, apply explicit channel-account bootstrap intent, and start clisbot in the background.`,
|
|
54679
|
+
" See `clisbot start --help` for bootstrap-focused flags and examples.",
|
|
54697
54680
|
" restart Stop the running clisbot process, then start it again.",
|
|
54698
54681
|
" stop Stop the running clisbot process.",
|
|
54699
54682
|
" stop --hard Stop clisbot and kill all tmux sessions on the configured clisbot socket.",
|
|
@@ -54710,20 +54693,26 @@ function renderCliHelp() {
|
|
|
54710
54693
|
" remove slack-group <groupId>",
|
|
54711
54694
|
" set-token <slack-app|slack-bot|telegram-bot> <value>",
|
|
54712
54695
|
" clear-token <slack-app|slack-bot|telegram-bot>",
|
|
54696
|
+
" See `clisbot channels --help` for route policy notes and defaults such as `requireMention`.",
|
|
54713
54697
|
" accounts Manage Slack and Telegram provider accounts plus persistence state.",
|
|
54714
54698
|
" add telegram --account <id> --token <ENV_NAME|${ENV_NAME}|literal> [--persist]",
|
|
54715
54699
|
" add slack --account <id> --app-token <ENV_NAME|${ENV_NAME}|literal> --bot-token <ENV_NAME|${ENV_NAME}|literal> [--persist]",
|
|
54716
54700
|
" persist --channel <slack|telegram> --account <id>",
|
|
54717
54701
|
" persist --all",
|
|
54702
|
+
" See `clisbot accounts --help` for env-vs-mem-vs-persist behavior.",
|
|
54718
54703
|
" loops Inspect or cancel managed recurring loops persisted by `/loop`.",
|
|
54719
54704
|
" list|status",
|
|
54720
54705
|
" cancel <id>",
|
|
54721
54706
|
" cancel --all",
|
|
54707
|
+
" See `clisbot loops --help` for behavior notes.",
|
|
54722
54708
|
" message Run provider message actions such as send, react, read, edit, delete, and pins.",
|
|
54709
|
+
" See `clisbot message --help` for channel-specific syntax.",
|
|
54723
54710
|
" agents Manage configured agents and top-level bindings.",
|
|
54711
|
+
" See `clisbot agents --help` for focused add/bootstrap/binding help.",
|
|
54724
54712
|
" auth Manage app and agent auth roles, principals, and permissions in config. See `clisbot auth --help`.",
|
|
54725
|
-
" pairing Run the pairing control CLI.",
|
|
54713
|
+
" pairing Run the pairing control CLI. See `clisbot pairing --help`.",
|
|
54726
54714
|
` init Seed ${configPath} and optionally create the first agent without starting clisbot.`,
|
|
54715
|
+
" See `clisbot init --help` for bootstrap-focused flags and examples.",
|
|
54727
54716
|
" --version, -v Show the installed clisbot version.",
|
|
54728
54717
|
" --help Show this help text.",
|
|
54729
54718
|
"",
|
|
@@ -54766,6 +54755,35 @@ function buildPairingReply(params) {
|
|
|
54766
54755
|
].join(`
|
|
54767
54756
|
`);
|
|
54768
54757
|
}
|
|
54758
|
+
function buildPairingQueueFullReply(params) {
|
|
54759
|
+
return [
|
|
54760
|
+
"clisbot: access not configured.",
|
|
54761
|
+
"",
|
|
54762
|
+
params.idLine,
|
|
54763
|
+
"",
|
|
54764
|
+
"Pairing queue is full right now.",
|
|
54765
|
+
"",
|
|
54766
|
+
"Ask the bot owner to inspect or clear pending requests with:",
|
|
54767
|
+
`clisbot pairing list ${params.channel}`,
|
|
54768
|
+
`clisbot pairing reject ${params.channel} <code>`,
|
|
54769
|
+
`clisbot pairing clear ${params.channel}`
|
|
54770
|
+
].join(`
|
|
54771
|
+
`);
|
|
54772
|
+
}
|
|
54773
|
+
function buildPairingReplyFromRequest(params) {
|
|
54774
|
+
const code = params.pairingRequest.code.trim();
|
|
54775
|
+
if (!code) {
|
|
54776
|
+
return buildPairingQueueFullReply({
|
|
54777
|
+
channel: params.channel,
|
|
54778
|
+
idLine: params.idLine
|
|
54779
|
+
});
|
|
54780
|
+
}
|
|
54781
|
+
return buildPairingReply({
|
|
54782
|
+
channel: params.channel,
|
|
54783
|
+
idLine: params.idLine,
|
|
54784
|
+
code
|
|
54785
|
+
});
|
|
54786
|
+
}
|
|
54769
54787
|
function renderPairingRequests(params) {
|
|
54770
54788
|
if (!params.requests.length) {
|
|
54771
54789
|
return `No pending ${params.channel} pairing requests.`;
|
|
@@ -54788,7 +54806,7 @@ import path from "node:path";
|
|
|
54788
54806
|
var PAIRING_CODE_LENGTH = 8;
|
|
54789
54807
|
var PAIRING_CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
54790
54808
|
var PAIRING_PENDING_TTL_MS = 60 * 60 * 1000;
|
|
54791
|
-
var PAIRING_PENDING_MAX =
|
|
54809
|
+
var PAIRING_PENDING_MAX = 20;
|
|
54792
54810
|
var PAIRING_STORE_LOCK_OPTIONS = {
|
|
54793
54811
|
retries: {
|
|
54794
54812
|
retries: 10,
|
|
@@ -55067,8 +55085,63 @@ async function approveChannelPairingCode(params) {
|
|
|
55067
55085
|
return { id: entry.id, entry };
|
|
55068
55086
|
});
|
|
55069
55087
|
}
|
|
55088
|
+
async function rejectChannelPairingCode(params) {
|
|
55089
|
+
const code = params.code.trim().toUpperCase();
|
|
55090
|
+
if (!code) {
|
|
55091
|
+
return null;
|
|
55092
|
+
}
|
|
55093
|
+
const filePath = resolvePairingPath(params.channel, params.baseDir);
|
|
55094
|
+
return withFileLock(filePath, { version: 1, requests: [] }, async () => {
|
|
55095
|
+
const { value } = await readJsonFile(filePath, {
|
|
55096
|
+
version: 1,
|
|
55097
|
+
requests: []
|
|
55098
|
+
});
|
|
55099
|
+
const requests = Array.isArray(value.requests) ? value.requests : [];
|
|
55100
|
+
const { requests: pruned, removed } = pruneExpiredRequests(requests, Date.now());
|
|
55101
|
+
const matchIndex = pruned.findIndex((request) => String(request.code ?? "").trim().toUpperCase() === code);
|
|
55102
|
+
if (matchIndex < 0) {
|
|
55103
|
+
if (removed) {
|
|
55104
|
+
await writeJsonFile(filePath, {
|
|
55105
|
+
version: 1,
|
|
55106
|
+
requests: pruned
|
|
55107
|
+
});
|
|
55108
|
+
}
|
|
55109
|
+
return null;
|
|
55110
|
+
}
|
|
55111
|
+
const [rejected] = pruned.splice(matchIndex, 1);
|
|
55112
|
+
await writeJsonFile(filePath, {
|
|
55113
|
+
version: 1,
|
|
55114
|
+
requests: pruned
|
|
55115
|
+
});
|
|
55116
|
+
return rejected ?? null;
|
|
55117
|
+
});
|
|
55118
|
+
}
|
|
55119
|
+
async function clearChannelPairingRequests(params) {
|
|
55120
|
+
const filePath = resolvePairingPath(params.channel, params.baseDir);
|
|
55121
|
+
return withFileLock(filePath, { version: 1, requests: [] }, async () => {
|
|
55122
|
+
const { value } = await readJsonFile(filePath, {
|
|
55123
|
+
version: 1,
|
|
55124
|
+
requests: []
|
|
55125
|
+
});
|
|
55126
|
+
const requests = Array.isArray(value.requests) ? value.requests : [];
|
|
55127
|
+
const { requests: pruned } = pruneExpiredRequests(requests, Date.now());
|
|
55128
|
+
await writeJsonFile(filePath, {
|
|
55129
|
+
version: 1,
|
|
55130
|
+
requests: []
|
|
55131
|
+
});
|
|
55132
|
+
return { cleared: pruned.length };
|
|
55133
|
+
});
|
|
55134
|
+
}
|
|
55070
55135
|
|
|
55071
55136
|
// src/channels/pairing/cli.ts
|
|
55137
|
+
function resolvePairingBaseDir(env = process.env) {
|
|
55138
|
+
const configured = env.CLISBOT_PAIRING_DIR?.trim();
|
|
55139
|
+
if (configured) {
|
|
55140
|
+
return configured;
|
|
55141
|
+
}
|
|
55142
|
+
const legacy = env.TMUX_TALK_PAIRING_DIR?.trim();
|
|
55143
|
+
return legacy || undefined;
|
|
55144
|
+
}
|
|
55072
55145
|
function parseChannel(raw) {
|
|
55073
55146
|
const value = raw?.trim().toLowerCase();
|
|
55074
55147
|
if (value === "slack" || value === "telegram") {
|
|
@@ -55076,9 +55149,33 @@ function parseChannel(raw) {
|
|
|
55076
55149
|
}
|
|
55077
55150
|
throw new Error("Channel required: slack | telegram");
|
|
55078
55151
|
}
|
|
55152
|
+
function renderPairingCliHelp() {
|
|
55153
|
+
return [
|
|
55154
|
+
"clisbot pairing",
|
|
55155
|
+
"",
|
|
55156
|
+
"Usage:",
|
|
55157
|
+
" clisbot pairing --help",
|
|
55158
|
+
" clisbot pairing help",
|
|
55159
|
+
" clisbot pairing list <slack|telegram> [--json]",
|
|
55160
|
+
" clisbot pairing approve <slack|telegram> <code>",
|
|
55161
|
+
" clisbot pairing reject <slack|telegram> <code>",
|
|
55162
|
+
" clisbot pairing clear <slack|telegram>",
|
|
55163
|
+
"",
|
|
55164
|
+
"Notes:",
|
|
55165
|
+
" - `list` shows pending pairing requests for one channel only",
|
|
55166
|
+
" - `approve` moves that sender into the channel allowlist",
|
|
55167
|
+
" - `reject` removes one pending request without allowlisting the sender",
|
|
55168
|
+
" - `clear` drops every pending request for that channel when the queue needs a reset"
|
|
55169
|
+
].join(`
|
|
55170
|
+
`);
|
|
55171
|
+
}
|
|
55079
55172
|
async function runPairingCli(args, writer = console) {
|
|
55080
55173
|
const [command, ...rest] = args;
|
|
55081
|
-
const baseDir =
|
|
55174
|
+
const baseDir = resolvePairingBaseDir();
|
|
55175
|
+
if (!command || command === "--help" || command === "-h" || command === "help") {
|
|
55176
|
+
writer.log(renderPairingCliHelp());
|
|
55177
|
+
return;
|
|
55178
|
+
}
|
|
55082
55179
|
if (command === "list") {
|
|
55083
55180
|
const wantsJson = rest.includes("--json");
|
|
55084
55181
|
const channel = parseChannel(rest.find((value) => !value.startsWith("--")));
|
|
@@ -55103,7 +55200,34 @@ async function runPairingCli(args, writer = console) {
|
|
|
55103
55200
|
writer.log(`Approved ${channel} sender ${approved.id}.`);
|
|
55104
55201
|
return;
|
|
55105
55202
|
}
|
|
55106
|
-
|
|
55203
|
+
if (command === "reject") {
|
|
55204
|
+
const [channelArg, code] = rest;
|
|
55205
|
+
const channel = parseChannel(channelArg);
|
|
55206
|
+
if (!code?.trim()) {
|
|
55207
|
+
throw new Error("Usage: pairing reject <channel> <code>");
|
|
55208
|
+
}
|
|
55209
|
+
const rejected = await rejectChannelPairingCode({
|
|
55210
|
+
channel,
|
|
55211
|
+
code,
|
|
55212
|
+
baseDir
|
|
55213
|
+
});
|
|
55214
|
+
if (!rejected) {
|
|
55215
|
+
throw new Error(`No pending pairing request found for code: ${code}`);
|
|
55216
|
+
}
|
|
55217
|
+
writer.log(`Rejected ${channel} sender ${rejected.id}.`);
|
|
55218
|
+
return;
|
|
55219
|
+
}
|
|
55220
|
+
if (command === "clear") {
|
|
55221
|
+
const [channelArg] = rest;
|
|
55222
|
+
const channel = parseChannel(channelArg);
|
|
55223
|
+
const result = await clearChannelPairingRequests({
|
|
55224
|
+
channel,
|
|
55225
|
+
baseDir
|
|
55226
|
+
});
|
|
55227
|
+
writer.log(`Cleared ${result.cleared} pending ${channel} pairing request(s).`);
|
|
55228
|
+
return;
|
|
55229
|
+
}
|
|
55230
|
+
throw new Error(renderPairingCliHelp());
|
|
55107
55231
|
}
|
|
55108
55232
|
|
|
55109
55233
|
// src/config/agent-tool-presets.ts
|
|
@@ -59283,7 +59407,7 @@ function formatConfiguredRuntimeLimit(params) {
|
|
|
59283
59407
|
if (typeof params.maxRuntimeMin === "number" && Number.isFinite(params.maxRuntimeMin)) {
|
|
59284
59408
|
return `${params.maxRuntimeMin} minute${params.maxRuntimeMin === 1 ? "" : "s"}`;
|
|
59285
59409
|
}
|
|
59286
|
-
return "
|
|
59410
|
+
return "30 minutes";
|
|
59287
59411
|
}
|
|
59288
59412
|
function parseCommandDurationMs(raw) {
|
|
59289
59413
|
const match = raw.trim().match(/^(\d+)(ms|s|m|h)$/i);
|
|
@@ -59991,7 +60115,7 @@ var agentDefaultsSchema = exports_external.object({
|
|
|
59991
60115
|
updateIntervalMs: 2000,
|
|
59992
60116
|
idleTimeoutMs: 6000,
|
|
59993
60117
|
noOutputTimeoutMs: 20000,
|
|
59994
|
-
maxRuntimeMin:
|
|
60118
|
+
maxRuntimeMin: 30,
|
|
59995
60119
|
maxMessageChars: 3500
|
|
59996
60120
|
}),
|
|
59997
60121
|
session: sessionSchema.default({
|
|
@@ -60679,7 +60803,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60679
60803
|
updateIntervalMs: 2000,
|
|
60680
60804
|
idleTimeoutMs: 6000,
|
|
60681
60805
|
noOutputTimeoutMs: 20000,
|
|
60682
|
-
maxRuntimeMin:
|
|
60806
|
+
maxRuntimeMin: 30,
|
|
60683
60807
|
maxMessageChars: 3500
|
|
60684
60808
|
},
|
|
60685
60809
|
session: {
|
|
@@ -61044,6 +61168,32 @@ function parseSingleOption(args, name) {
|
|
|
61044
61168
|
function hasFlag(args, name) {
|
|
61045
61169
|
return args.includes(name);
|
|
61046
61170
|
}
|
|
61171
|
+
function renderAgentsHelp() {
|
|
61172
|
+
return [
|
|
61173
|
+
"clisbot agents",
|
|
61174
|
+
"",
|
|
61175
|
+
"Usage:",
|
|
61176
|
+
" clisbot agents --help",
|
|
61177
|
+
" clisbot agents help",
|
|
61178
|
+
" clisbot agents list [--bindings] [--json]",
|
|
61179
|
+
" clisbot agents add <id> --cli <codex|claude|gemini> [--workspace <path>] [--startup-option <arg>]... [--bootstrap <personal-assistant|team-assistant>] [--bind <channel[:accountId]>]...",
|
|
61180
|
+
" clisbot agents bootstrap <id> --mode <personal-assistant|team-assistant> [--force]",
|
|
61181
|
+
" clisbot agents bindings [--agent <id>] [--json]",
|
|
61182
|
+
" clisbot agents bind --agent <id> --bind <channel[:accountId]>",
|
|
61183
|
+
" clisbot agents unbind --agent <id> [--bind <channel[:accountId]> | --all]",
|
|
61184
|
+
" clisbot agents response-mode <status|set|clear> --agent <id> [capture-pane|message-tool]",
|
|
61185
|
+
" clisbot agents additional-message-mode <status|set|clear> --agent <id> [queue|steer]",
|
|
61186
|
+
"",
|
|
61187
|
+
"Notes:",
|
|
61188
|
+
" - `agents add` is the lower-level manual surface; first-run `clisbot start` and `clisbot init` can bootstrap the first `default` agent for you",
|
|
61189
|
+
" - `--cli` is required on `agents add`; supported tools are `codex`, `claude`, and `gemini`",
|
|
61190
|
+
" - omit `--startup-option` to inherit the built-in startup args for the selected CLI tool",
|
|
61191
|
+
" - `--bind slack`, `--bind telegram`, or `--bind <channel>:<accountId>` creates top-level fallback bindings",
|
|
61192
|
+
" - explicit route `agentId` on Slack or Telegram still wins before these fallback bindings",
|
|
61193
|
+
" - `response-mode` and `additional-message-mode` mutate per-agent overrides under `agents.list[]`"
|
|
61194
|
+
].join(`
|
|
61195
|
+
`);
|
|
61196
|
+
}
|
|
61047
61197
|
function parseResponseMode(raw) {
|
|
61048
61198
|
if (raw === "capture-pane" || raw === "message-tool") {
|
|
61049
61199
|
return raw;
|
|
@@ -61373,7 +61523,11 @@ async function runAgentAdditionalMessageModeCli(args) {
|
|
|
61373
61523
|
async function runAgentsCli(args) {
|
|
61374
61524
|
const subcommand = args[0];
|
|
61375
61525
|
const rest = args.slice(1);
|
|
61376
|
-
if (!subcommand || subcommand === "
|
|
61526
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
|
|
61527
|
+
console.log(renderAgentsHelp());
|
|
61528
|
+
return;
|
|
61529
|
+
}
|
|
61530
|
+
if (subcommand === "list") {
|
|
61377
61531
|
await listAgents(rest);
|
|
61378
61532
|
return;
|
|
61379
61533
|
}
|
|
@@ -61405,7 +61559,7 @@ async function runAgentsCli(args) {
|
|
|
61405
61559
|
await unbindAgent(rest);
|
|
61406
61560
|
return;
|
|
61407
61561
|
}
|
|
61408
|
-
throw new Error(
|
|
61562
|
+
throw new Error(renderAgentsHelp());
|
|
61409
61563
|
}
|
|
61410
61564
|
|
|
61411
61565
|
// src/control/accounts-cli.ts
|
|
@@ -63505,6 +63659,12 @@ function collapseBlankLines(lines) {
|
|
|
63505
63659
|
}
|
|
63506
63660
|
return collapsed;
|
|
63507
63661
|
}
|
|
63662
|
+
var DURATION_STATUS_PATTERN = String.raw`(?:\d+(?:h|m|s))(?:\s+\d+(?:h|m|s)){0,2}`;
|
|
63663
|
+
var CODEX_WORKING_STATUS_PATTERN = new RegExp(String.raw`^(?:[•◦·]\s*)?Working(?:\s*\()?${DURATION_STATUS_PATTERN}\b.*(?:esc to interrupt|interrupt)\)?$`, "i");
|
|
63664
|
+
var CODEX_INTERRUPT_FOOTER_PATTERN = new RegExp(String.raw`^(?:[•◦·]\s*)?${DURATION_STATUS_PATTERN}\s*[•◦·]?\s*esc to interrupt\)?$`, "i");
|
|
63665
|
+
var GEMINI_THINKING_STATUS_PATTERN = new RegExp(String.raw`^Thinking\.\.\. \(esc to cancel,\s*${DURATION_STATUS_PATTERN}\)$`, "i");
|
|
63666
|
+
var CLAUDE_WORKED_STATUS_PATTERN = new RegExp(String.raw`^(?:[✻✽*]\s*)?(?:Worked|Cooked) for ${DURATION_STATUS_PATTERN}$`, "i");
|
|
63667
|
+
var CLAUDE_TIMER_FOOTER_PATTERN = new RegExp(String.raw`\|\s*claude\s*\|.*\|\s*${DURATION_STATUS_PATTERN}\s*$`, "i");
|
|
63508
63668
|
function looksLikeUrlContinuation(line) {
|
|
63509
63669
|
const trimmed = line.trim();
|
|
63510
63670
|
return trimmed.startsWith("(") || trimmed.startsWith("http://") || trimmed.startsWith("https://");
|
|
@@ -63702,7 +63862,14 @@ function isInterruptStatusLine(line) {
|
|
|
63702
63862
|
if (!trimmed) {
|
|
63703
63863
|
return false;
|
|
63704
63864
|
}
|
|
63705
|
-
return
|
|
63865
|
+
return CODEX_WORKING_STATUS_PATTERN.test(trimmed) || CODEX_INTERRUPT_FOOTER_PATTERN.test(trimmed);
|
|
63866
|
+
}
|
|
63867
|
+
function isTimerDrivenStatusLine(line) {
|
|
63868
|
+
const trimmed = line.trim();
|
|
63869
|
+
if (!trimmed) {
|
|
63870
|
+
return false;
|
|
63871
|
+
}
|
|
63872
|
+
return isInterruptStatusLine(trimmed) || GEMINI_THINKING_STATUS_PATTERN.test(trimmed) || CLAUDE_WORKED_STATUS_PATTERN.test(trimmed) || CLAUDE_TIMER_FOOTER_PATTERN.test(trimmed);
|
|
63706
63873
|
}
|
|
63707
63874
|
function shouldDropCodexChromeLine(line) {
|
|
63708
63875
|
const trimmed = line.trim();
|
|
@@ -63716,14 +63883,14 @@ function shouldDropClaudeChromeLine(line) {
|
|
|
63716
63883
|
if (!trimmed) {
|
|
63717
63884
|
return false;
|
|
63718
63885
|
}
|
|
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 |") ||
|
|
63886
|
+
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
63887
|
}
|
|
63721
63888
|
function shouldDropGeminiChromeLine(line) {
|
|
63722
63889
|
const trimmed = line.trim();
|
|
63723
63890
|
if (!trimmed) {
|
|
63724
63891
|
return false;
|
|
63725
63892
|
}
|
|
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) ||
|
|
63893
|
+
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
63894
|
}
|
|
63728
63895
|
function normalizeBoundaryLine(line) {
|
|
63729
63896
|
return line.trim().replace(/^(?::eight_spoked_asterisk:|[-*•◦·✽✶])\s+/, "");
|
|
@@ -63757,16 +63924,21 @@ function collapseAdjacentDuplicateLines(raw) {
|
|
|
63757
63924
|
return collapseBlankLines(collapsed).join(`
|
|
63758
63925
|
`).trim();
|
|
63759
63926
|
}
|
|
63760
|
-
function
|
|
63927
|
+
function cleanInteractionSnapshotInternal(raw, options) {
|
|
63761
63928
|
const lines = splitNormalizedLines(raw);
|
|
63762
63929
|
const isCodex = looksLikeCodexSnapshot(lines);
|
|
63763
63930
|
const isClaude = looksLikeClaudeSnapshot(lines);
|
|
63764
63931
|
const isGemini = looksLikeGeminiSnapshot(lines);
|
|
63765
63932
|
const promptStripped = isCodex ? dropCodexPromptBlocks(lines) : isClaude ? dropClaudePromptBlocks(lines) : isGemini ? dropGeminiPromptBlocks(lines) : lines;
|
|
63933
|
+
const timerStatusLines = [];
|
|
63766
63934
|
const filtered = promptStripped.filter((line) => {
|
|
63767
63935
|
if (shouldDropDeliveryReportLine(line)) {
|
|
63768
63936
|
return false;
|
|
63769
63937
|
}
|
|
63938
|
+
if (options?.preserveTimerStatusLines && isTimerDrivenStatusLine(line)) {
|
|
63939
|
+
timerStatusLines.push(line.trim());
|
|
63940
|
+
return false;
|
|
63941
|
+
}
|
|
63770
63942
|
if (isCodex && shouldDropCodexChromeLine(line)) {
|
|
63771
63943
|
return false;
|
|
63772
63944
|
}
|
|
@@ -63780,8 +63952,21 @@ function cleanInteractionSnapshot(raw) {
|
|
|
63780
63952
|
});
|
|
63781
63953
|
const normalized = isCodex ? unwrapCodexMessageBlocks(filtered) : isClaude ? unwrapClaudeMessageBlocks(filtered) : isGemini ? filtered.map((line) => line.replace(/^\s*>\s*/, "")) : filtered;
|
|
63782
63954
|
const unwrapped = unwrapSoftWrappedLines(normalized);
|
|
63783
|
-
|
|
63955
|
+
const cleanedBody = collapseAdjacentDuplicateLines(collapseBlankLines(trimBlankLines(unwrapped)).join(`
|
|
63784
63956
|
`));
|
|
63957
|
+
const cleanedTimerStatus = options?.preserveTimerStatusLines ? collapseAdjacentDuplicateLines(collapseBlankLines(trimBlankLines(timerStatusLines)).join(`
|
|
63958
|
+
`)) : "";
|
|
63959
|
+
return [cleanedBody, cleanedTimerStatus].filter(Boolean).join(`
|
|
63960
|
+
|
|
63961
|
+
`).trim();
|
|
63962
|
+
}
|
|
63963
|
+
function cleanInteractionSnapshot(raw) {
|
|
63964
|
+
return cleanInteractionSnapshotInternal(raw);
|
|
63965
|
+
}
|
|
63966
|
+
function cleanRunningInteractionSnapshot(raw) {
|
|
63967
|
+
return cleanInteractionSnapshotInternal(raw, {
|
|
63968
|
+
preserveTimerStatusLines: true
|
|
63969
|
+
});
|
|
63785
63970
|
}
|
|
63786
63971
|
// src/shared/transcript-delta.ts
|
|
63787
63972
|
function diffText(previous, current) {
|
|
@@ -63842,16 +64027,8 @@ function extractScrolledAppend(previous, current) {
|
|
|
63842
64027
|
}
|
|
63843
64028
|
return "";
|
|
63844
64029
|
}
|
|
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);
|
|
64030
|
+
function deriveRunningInteractionSnapshot(currentSnapshot) {
|
|
64031
|
+
return cleanRunningInteractionSnapshot(currentSnapshot);
|
|
63855
64032
|
}
|
|
63856
64033
|
function deriveInteractionText(initialSnapshot, currentSnapshot) {
|
|
63857
64034
|
const previous = cleanInteractionSnapshot(initialSnapshot);
|
|
@@ -64593,6 +64770,7 @@ var TMUX_DUPLICATE_SESSION_PATTERN = /duplicate session:/i;
|
|
|
64593
64770
|
var TMUX_TRANSIENT_TARGET_PATTERN = /(?:no current target|can't find pane|can't find window)/i;
|
|
64594
64771
|
var SESSION_READY_CAPTURE_RETRY_COUNT = 5;
|
|
64595
64772
|
var SESSION_READY_CAPTURE_RETRY_DELAY_MS = 100;
|
|
64773
|
+
var SESSION_ID_CAPTURE_FAILURE_COOLDOWN_MS = 15000;
|
|
64596
64774
|
function summarizeSnapshot(snapshot) {
|
|
64597
64775
|
const compact = snapshot.split(`
|
|
64598
64776
|
`).map((line) => line.trim()).filter(Boolean).join(" ").slice(0, 220);
|
|
@@ -64624,6 +64802,7 @@ class RunnerService {
|
|
|
64624
64802
|
sessionState;
|
|
64625
64803
|
resolveTarget;
|
|
64626
64804
|
cleanupInFlight = false;
|
|
64805
|
+
sessionIdentityCaptureRetryAt = new Map;
|
|
64627
64806
|
constructor(loadedConfig, tmux, sessionState, resolveTarget) {
|
|
64628
64807
|
this.loadedConfig = loadedConfig;
|
|
64629
64808
|
this.tmux = tmux;
|
|
@@ -64675,12 +64854,24 @@ class RunnerService {
|
|
|
64675
64854
|
async syncSessionIdentity(resolved) {
|
|
64676
64855
|
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
64677
64856
|
if (existing?.sessionId) {
|
|
64857
|
+
this.sessionIdentityCaptureRetryAt.delete(resolved.sessionKey);
|
|
64678
64858
|
return this.sessionState.touchSessionEntry(resolved, {
|
|
64679
64859
|
sessionId: existing.sessionId,
|
|
64680
64860
|
runnerCommand: resolved.runner.command
|
|
64681
64861
|
});
|
|
64682
64862
|
}
|
|
64863
|
+
const retryAt = this.sessionIdentityCaptureRetryAt.get(resolved.sessionKey) ?? 0;
|
|
64864
|
+
if (retryAt > Date.now()) {
|
|
64865
|
+
return this.sessionState.touchSessionEntry(resolved, {
|
|
64866
|
+
runnerCommand: resolved.runner.command
|
|
64867
|
+
});
|
|
64868
|
+
}
|
|
64683
64869
|
const sessionId = await this.captureSessionIdentity(resolved);
|
|
64870
|
+
if (sessionId) {
|
|
64871
|
+
this.sessionIdentityCaptureRetryAt.delete(resolved.sessionKey);
|
|
64872
|
+
} else {
|
|
64873
|
+
this.sessionIdentityCaptureRetryAt.set(resolved.sessionKey, Date.now() + SESSION_ID_CAPTURE_FAILURE_COOLDOWN_MS);
|
|
64874
|
+
}
|
|
64684
64875
|
return this.sessionState.touchSessionEntry(resolved, {
|
|
64685
64876
|
sessionId,
|
|
64686
64877
|
runnerCommand: resolved.runner.command
|
|
@@ -64835,6 +65026,7 @@ class RunnerService {
|
|
|
64835
65026
|
sessionName: resolved.sessionName,
|
|
64836
65027
|
stateDir: this.loadedConfig.stateDir
|
|
64837
65028
|
});
|
|
65029
|
+
this.sessionIdentityCaptureRetryAt.delete(resolved.sessionKey);
|
|
64838
65030
|
try {
|
|
64839
65031
|
await this.tmux.newSession({
|
|
64840
65032
|
sessionName: resolved.sessionName,
|
|
@@ -65101,11 +65293,12 @@ function buildRunRecoveryNote(kind, params) {
|
|
|
65101
65293
|
var FIRST_OUTPUT_POLL_INTERVAL_MS = 250;
|
|
65102
65294
|
async function monitorTmuxRun(params) {
|
|
65103
65295
|
let previousSnapshot = params.initialSnapshot;
|
|
65104
|
-
let
|
|
65105
|
-
let
|
|
65106
|
-
let
|
|
65296
|
+
let previousRunningSnapshot = "";
|
|
65297
|
+
let lastActivityAt = params.startedAt;
|
|
65298
|
+
let sawActivity = false;
|
|
65107
65299
|
let detachedNotified = params.detachedAlready;
|
|
65108
65300
|
let firstMeaningfulDeltaLogged = false;
|
|
65301
|
+
let noOutputThresholdLogged = false;
|
|
65109
65302
|
if (params.prompt) {
|
|
65110
65303
|
logLatencyDebug("tmux-submit-start", params.timingContext, {
|
|
65111
65304
|
sessionName: params.sessionName,
|
|
@@ -65126,61 +65319,57 @@ async function monitorTmuxRun(params) {
|
|
|
65126
65319
|
});
|
|
65127
65320
|
}
|
|
65128
65321
|
while (true) {
|
|
65129
|
-
await sleep(
|
|
65322
|
+
await sleep(sawActivity ? params.updateIntervalMs : Math.min(params.updateIntervalMs, FIRST_OUTPUT_POLL_INTERVAL_MS));
|
|
65130
65323
|
const snapshot = normalizePaneText(await params.tmux.capturePane(params.sessionName, params.captureLines));
|
|
65131
65324
|
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
|
|
65325
|
+
const runningSnapshot = deriveRunningInteractionSnapshot(snapshot);
|
|
65326
|
+
previousSnapshot = snapshot;
|
|
65327
|
+
if (runningSnapshot && runningSnapshot !== previousRunningSnapshot) {
|
|
65328
|
+
previousRunningSnapshot = runningSnapshot;
|
|
65329
|
+
lastActivityAt = now;
|
|
65330
|
+
sawActivity = true;
|
|
65331
|
+
if (!firstMeaningfulDeltaLogged) {
|
|
65332
|
+
firstMeaningfulDeltaLogged = true;
|
|
65333
|
+
logLatencyDebug("tmux-first-meaningful-delta", params.timingContext, {
|
|
65334
|
+
sessionName: params.sessionName,
|
|
65335
|
+
elapsedMs: now - params.startedAt
|
|
65152
65336
|
});
|
|
65153
65337
|
}
|
|
65338
|
+
await params.onRunning({
|
|
65339
|
+
snapshot: runningSnapshot,
|
|
65340
|
+
fullSnapshot: snapshot,
|
|
65341
|
+
initialSnapshot: params.initialSnapshot
|
|
65342
|
+
});
|
|
65154
65343
|
}
|
|
65155
65344
|
if (!detachedNotified && now - params.startedAt >= params.maxRuntimeMs) {
|
|
65156
65345
|
detachedNotified = true;
|
|
65157
65346
|
await params.onDetached({
|
|
65158
|
-
snapshot:
|
|
65347
|
+
snapshot: previousRunningSnapshot || deriveInteractionText(params.initialSnapshot, snapshot),
|
|
65159
65348
|
fullSnapshot: previousSnapshot,
|
|
65160
65349
|
initialSnapshot: params.initialSnapshot
|
|
65161
65350
|
});
|
|
65162
65351
|
}
|
|
65163
|
-
if (
|
|
65352
|
+
if (sawActivity && now - lastActivityAt >= params.idleTimeoutMs) {
|
|
65164
65353
|
await params.onCompleted({
|
|
65165
|
-
snapshot:
|
|
65354
|
+
snapshot: deriveInteractionText(params.initialSnapshot, previousSnapshot),
|
|
65166
65355
|
fullSnapshot: previousSnapshot,
|
|
65167
65356
|
initialSnapshot: params.initialSnapshot
|
|
65168
65357
|
});
|
|
65169
65358
|
return;
|
|
65170
65359
|
}
|
|
65171
|
-
if (!
|
|
65172
|
-
|
|
65173
|
-
|
|
65174
|
-
|
|
65175
|
-
|
|
65360
|
+
if (!noOutputThresholdLogged && !sawActivity && now - params.startedAt >= params.noOutputTimeoutMs) {
|
|
65361
|
+
noOutputThresholdLogged = true;
|
|
65362
|
+
logLatencyDebug("tmux-no-output-threshold-crossed", params.timingContext, {
|
|
65363
|
+
sessionName: params.sessionName,
|
|
65364
|
+
elapsedMs: now - params.startedAt
|
|
65176
65365
|
});
|
|
65177
|
-
return;
|
|
65178
65366
|
}
|
|
65179
65367
|
}
|
|
65180
65368
|
}
|
|
65181
65369
|
|
|
65182
65370
|
// src/agents/session-service.ts
|
|
65183
65371
|
var OBSERVER_RETRYABLE_FAILURE_LIMIT = 3;
|
|
65372
|
+
var DETACHED_OBSERVER_INTERVAL_MS = 5 * 60000;
|
|
65184
65373
|
function formatObserverError(error) {
|
|
65185
65374
|
return error instanceof Error ? error.stack ?? error.message : String(error);
|
|
65186
65375
|
}
|
|
@@ -65428,7 +65617,10 @@ class SessionService {
|
|
|
65428
65617
|
detached: false
|
|
65429
65618
|
};
|
|
65430
65619
|
}
|
|
65431
|
-
observer.mode = "
|
|
65620
|
+
observer.mode = "poll";
|
|
65621
|
+
observer.intervalMs = DETACHED_OBSERVER_INTERVAL_MS;
|
|
65622
|
+
observer.expiresAt = undefined;
|
|
65623
|
+
observer.lastSentAt = Date.now();
|
|
65432
65624
|
run.observerFailures.delete(observerId);
|
|
65433
65625
|
return {
|
|
65434
65626
|
detached: true
|
|
@@ -65451,7 +65643,19 @@ class SessionService {
|
|
|
65451
65643
|
this.activeRuns.clear();
|
|
65452
65644
|
}
|
|
65453
65645
|
buildDetachedNote(resolved) {
|
|
65454
|
-
return `This session has been running for over ${resolved.stream.maxRuntimeLabel}. clisbot will keep monitoring it and
|
|
65646
|
+
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.`;
|
|
65647
|
+
}
|
|
65648
|
+
applyDetachedObserverPolicy(run) {
|
|
65649
|
+
const now = Date.now();
|
|
65650
|
+
for (const observer of run.observers.values()) {
|
|
65651
|
+
if (observer.mode !== "live") {
|
|
65652
|
+
continue;
|
|
65653
|
+
}
|
|
65654
|
+
observer.mode = "poll";
|
|
65655
|
+
observer.intervalMs = DETACHED_OBSERVER_INTERVAL_MS;
|
|
65656
|
+
observer.expiresAt = undefined;
|
|
65657
|
+
observer.lastSentAt = now;
|
|
65658
|
+
}
|
|
65455
65659
|
}
|
|
65456
65660
|
createRunUpdate(params) {
|
|
65457
65661
|
return {
|
|
@@ -65682,7 +65886,8 @@ class SessionService {
|
|
|
65682
65886
|
startedAt: params.startedAt,
|
|
65683
65887
|
detachedAt: Date.now()
|
|
65684
65888
|
});
|
|
65685
|
-
currentRun
|
|
65889
|
+
await this.notifyRunObservers(currentRun, detachedUpdate);
|
|
65890
|
+
this.applyDetachedObserverPolicy(currentRun);
|
|
65686
65891
|
currentRun.initialResult.resolve(detachedUpdate);
|
|
65687
65892
|
},
|
|
65688
65893
|
onCompleted: async (update) => {
|
|
@@ -65694,16 +65899,6 @@ class SessionService {
|
|
|
65694
65899
|
initialSnapshot: update.initialSnapshot
|
|
65695
65900
|
});
|
|
65696
65901
|
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
65902
|
}
|
|
65708
65903
|
});
|
|
65709
65904
|
} catch (error) {
|
|
@@ -65720,6 +65915,28 @@ class SessionService {
|
|
|
65720
65915
|
}
|
|
65721
65916
|
|
|
65722
65917
|
// src/agents/agent-service.ts
|
|
65918
|
+
function shellQuote3(value) {
|
|
65919
|
+
if (/^[a-zA-Z0-9_./:@=-]+$/.test(value)) {
|
|
65920
|
+
return value;
|
|
65921
|
+
}
|
|
65922
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
65923
|
+
}
|
|
65924
|
+
function buildCommandString2(command, args) {
|
|
65925
|
+
return [command, ...args].map(shellQuote3).join(" ");
|
|
65926
|
+
}
|
|
65927
|
+
function stripWorkspaceArgs(args) {
|
|
65928
|
+
const filtered = [];
|
|
65929
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
65930
|
+
const current = args[index];
|
|
65931
|
+
if (current === "-C") {
|
|
65932
|
+
index += 1;
|
|
65933
|
+
continue;
|
|
65934
|
+
}
|
|
65935
|
+
filtered.push(current);
|
|
65936
|
+
}
|
|
65937
|
+
return filtered;
|
|
65938
|
+
}
|
|
65939
|
+
|
|
65723
65940
|
class AgentService {
|
|
65724
65941
|
loadedConfig;
|
|
65725
65942
|
tmuxClient;
|
|
@@ -65810,6 +66027,15 @@ class AgentService {
|
|
|
65810
66027
|
async getSessionRuntime(target) {
|
|
65811
66028
|
return this.sessionState.getSessionRuntime(target);
|
|
65812
66029
|
}
|
|
66030
|
+
async getSessionDiagnostics(target) {
|
|
66031
|
+
const resolved = this.resolveTarget(target);
|
|
66032
|
+
const entry = await this.sessionState.getEntry(target.sessionKey);
|
|
66033
|
+
const sessionId = entry?.sessionId?.trim() || undefined;
|
|
66034
|
+
return {
|
|
66035
|
+
sessionId,
|
|
66036
|
+
resumeCommand: this.buildResumeCommandPreview(resolved, sessionId)
|
|
66037
|
+
};
|
|
66038
|
+
}
|
|
65813
66039
|
async listActiveSessionRuntimes() {
|
|
65814
66040
|
return this.sessionState.listActiveSessionRuntimes();
|
|
65815
66041
|
}
|
|
@@ -66052,6 +66278,21 @@ class AgentService {
|
|
|
66052
66278
|
this.scheduleIntervalLoopTimer(persisted.id, Math.max(0, persisted.nextRunAt - Date.now()));
|
|
66053
66279
|
}
|
|
66054
66280
|
}
|
|
66281
|
+
buildResumeCommandPreview(resolved, sessionId) {
|
|
66282
|
+
if (!sessionId || resolved.runner.sessionId.resume.mode !== "command") {
|
|
66283
|
+
return;
|
|
66284
|
+
}
|
|
66285
|
+
const values = {
|
|
66286
|
+
agentId: resolved.agentId,
|
|
66287
|
+
workspace: resolved.workspacePath,
|
|
66288
|
+
sessionName: resolved.sessionName,
|
|
66289
|
+
sessionKey: resolved.sessionKey,
|
|
66290
|
+
sessionId
|
|
66291
|
+
};
|
|
66292
|
+
const command = resolved.runner.sessionId.resume.command ?? resolved.runner.command;
|
|
66293
|
+
const args = stripWorkspaceArgs(resolved.runner.sessionId.resume.args.map((value) => applyTemplate(value, values)));
|
|
66294
|
+
return buildCommandString2(command, args);
|
|
66295
|
+
}
|
|
66055
66296
|
async isManagedLoopPersisted(managed) {
|
|
66056
66297
|
const entry = await this.sessionState.getEntry(managed.target.sessionKey);
|
|
66057
66298
|
return (entry?.intervalLoops ?? []).some((loop) => loop.id === managed.loop.id);
|
|
@@ -66556,6 +66797,12 @@ function parseAgentCommand(text, options = {}) {
|
|
|
66556
66797
|
if (lowered === "loop") {
|
|
66557
66798
|
const loopText = withoutSlash.slice(command.length).trim();
|
|
66558
66799
|
const loweredLoopText = loopText.toLowerCase();
|
|
66800
|
+
if (!loweredLoopText || loweredLoopText === "help") {
|
|
66801
|
+
return {
|
|
66802
|
+
type: "control",
|
|
66803
|
+
name: "loop-help"
|
|
66804
|
+
};
|
|
66805
|
+
}
|
|
66559
66806
|
if (loweredLoopText === "status") {
|
|
66560
66807
|
return {
|
|
66561
66808
|
type: "loop-control",
|
|
@@ -66603,6 +66850,12 @@ function parseAgentCommand(text, options = {}) {
|
|
|
66603
66850
|
const queueText = withoutSlash.slice(command.length).trim();
|
|
66604
66851
|
const normalizedQueueText = queueText.toLowerCase();
|
|
66605
66852
|
if (lowered === "queue") {
|
|
66853
|
+
if (normalizedQueueText === "help") {
|
|
66854
|
+
return {
|
|
66855
|
+
type: "control",
|
|
66856
|
+
name: "queue-help"
|
|
66857
|
+
};
|
|
66858
|
+
}
|
|
66606
66859
|
if (normalizedQueueText === "list") {
|
|
66607
66860
|
return {
|
|
66608
66861
|
type: "control",
|
|
@@ -66669,7 +66922,7 @@ function renderAgentControlSlashHelp() {
|
|
|
66669
66922
|
"- `/whoami`: show the current platform, route, and sender identity details",
|
|
66670
66923
|
"- `/transcript`: show the current conversation session transcript when the route verbose policy allows it",
|
|
66671
66924
|
"- `/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
|
|
66925
|
+
"- `/detach`: stop live updates for this thread, switch to sparse progress updates, and still allow final settlement here",
|
|
66673
66926
|
"- `/watch every 30s [for 10m]`: post the latest state on an interval until the run settles or the watch window ends",
|
|
66674
66927
|
"- `/stop`: send Escape to interrupt the current conversation session",
|
|
66675
66928
|
"- `/nudge`: send one extra Enter to the current tmux session without resending the prompt text",
|
|
@@ -66686,9 +66939,11 @@ function renderAgentControlSlashHelp() {
|
|
|
66686
66939
|
"- `/additionalmessagemode steer`: send later user messages straight into the active session",
|
|
66687
66940
|
"- `/additionalmessagemode queue`: queue later user messages behind the active run for this surface",
|
|
66688
66941
|
"- `/queue <message>` or `\\q <message>`: enqueue a later message behind the active run and let clisbot deliver it in order",
|
|
66942
|
+
"- `/queue help`: show queue-specific help and examples",
|
|
66689
66943
|
"- `/steer <message>` or `\\s <message>`: inject a steering message into the active run immediately",
|
|
66690
66944
|
"- `/queue list`: show queued messages that have not started yet",
|
|
66691
66945
|
"- `/queue clear`: clear queued messages that have not started yet",
|
|
66946
|
+
"- `/loop help`: show loop-specific help and syntax examples",
|
|
66692
66947
|
...renderLoopHelpLines(),
|
|
66693
66948
|
"- `/bash` followed by a shell command: requires `shellExecute` on the resolved agent role",
|
|
66694
66949
|
"- shortcut prefixes such as `!` run bash only when the resolved agent role allows `shellExecute`",
|
|
@@ -66697,6 +66952,15 @@ function renderAgentControlSlashHelp() {
|
|
|
66697
66952
|
].join(`
|
|
66698
66953
|
`);
|
|
66699
66954
|
}
|
|
66955
|
+
function renderQueueHelpLines() {
|
|
66956
|
+
return [
|
|
66957
|
+
"- `/queue <message>` or `\\q <message>`: enqueue one later message behind the active run",
|
|
66958
|
+
"- `/queue list`: show queued messages that have not started yet",
|
|
66959
|
+
"- `/queue clear`: clear queued messages that have not started yet",
|
|
66960
|
+
"- `/queue help`: show this queue help again",
|
|
66961
|
+
"- `/steer <message>` or `\\s <message>`: inject an immediate steering message instead of queueing"
|
|
66962
|
+
];
|
|
66963
|
+
}
|
|
66700
66964
|
function parseWatchCommand(raw) {
|
|
66701
66965
|
const match = raw.match(/^every\s+(\S+)(?:\s+for\s+(\S+))?$/i);
|
|
66702
66966
|
if (!match) {
|
|
@@ -66918,7 +67182,8 @@ function renderWhoAmIMessage(params) {
|
|
|
66918
67182
|
`platform: \`${params.identity.platform}\``,
|
|
66919
67183
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
66920
67184
|
`agentId: \`${params.route.agentId}\``,
|
|
66921
|
-
`sessionKey: \`${params.sessionTarget.sessionKey}
|
|
67185
|
+
`sessionKey: \`${params.sessionTarget.sessionKey}\``,
|
|
67186
|
+
`storedSessionId: \`${params.sessionDiagnostics.sessionId ?? "(not captured yet)"}\``
|
|
66922
67187
|
];
|
|
66923
67188
|
if (params.identity.senderId) {
|
|
66924
67189
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -66935,7 +67200,7 @@ function renderWhoAmIMessage(params) {
|
|
|
66935
67200
|
if (params.identity.topicId) {
|
|
66936
67201
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
66937
67202
|
}
|
|
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}\``);
|
|
67203
|
+
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
67204
|
return lines.join(`
|
|
66940
67205
|
`);
|
|
66941
67206
|
}
|
|
@@ -66946,7 +67211,8 @@ function renderRouteStatusMessage(params) {
|
|
|
66946
67211
|
`platform: \`${params.identity.platform}\``,
|
|
66947
67212
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
66948
67213
|
`agentId: \`${params.route.agentId}\``,
|
|
66949
|
-
`sessionKey: \`${params.sessionTarget.sessionKey}
|
|
67214
|
+
`sessionKey: \`${params.sessionTarget.sessionKey}\``,
|
|
67215
|
+
`storedSessionId: \`${params.sessionDiagnostics.sessionId ?? "(not captured yet)"}\``
|
|
66950
67216
|
];
|
|
66951
67217
|
if (params.identity.senderId) {
|
|
66952
67218
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -66963,7 +67229,7 @@ function renderRouteStatusMessage(params) {
|
|
|
66963
67229
|
if (params.identity.topicId) {
|
|
66964
67230
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
66965
67231
|
}
|
|
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}\``);
|
|
67232
|
+
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
67233
|
if (params.runtimeState.startedAt) {
|
|
66968
67234
|
lines.push(`run.startedAt: \`${new Date(params.runtimeState.startedAt).toISOString()}\``);
|
|
66969
67235
|
}
|
|
@@ -66977,7 +67243,7 @@ function renderRouteStatusMessage(params) {
|
|
|
66977
67243
|
lines.push(`- \`${loop.id}\` ${renderLoopStatusSchedule(loop)} remaining \`${loop.remainingRuns}\` nextRunAt \`${new Date(loop.nextRunAt).toISOString()}\``);
|
|
66978
67244
|
}
|
|
66979
67245
|
}
|
|
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`");
|
|
67246
|
+
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
67247
|
return lines.join(`
|
|
66982
67248
|
`);
|
|
66983
67249
|
}
|
|
@@ -67049,9 +67315,22 @@ function renderQueuedMessagesList(items) {
|
|
|
67049
67315
|
return lines.join(`
|
|
67050
67316
|
`).trimEnd();
|
|
67051
67317
|
}
|
|
67318
|
+
function renderQueueUsage() {
|
|
67319
|
+
return [
|
|
67320
|
+
"Queue commands",
|
|
67321
|
+
"",
|
|
67322
|
+
...renderQueueHelpLines(),
|
|
67323
|
+
"",
|
|
67324
|
+
"Notes:",
|
|
67325
|
+
"- use queue when the current run should finish first and the next user request can wait in order",
|
|
67326
|
+
"- use steer when the active run should be nudged or redirected immediately"
|
|
67327
|
+
].join(`
|
|
67328
|
+
`);
|
|
67329
|
+
}
|
|
67052
67330
|
function renderLoopUsage() {
|
|
67053
67331
|
return [
|
|
67054
67332
|
"Usage:",
|
|
67333
|
+
"- `/loop help`",
|
|
67055
67334
|
"- `/loop 5m check CI`",
|
|
67056
67335
|
"- `/loop 1m --force check CI`",
|
|
67057
67336
|
"- `/loop 5m`",
|
|
@@ -67602,6 +67881,7 @@ async function processChannelInteraction(params) {
|
|
|
67602
67881
|
};
|
|
67603
67882
|
let replyRecorded = false;
|
|
67604
67883
|
let renderChain = Promise.resolve();
|
|
67884
|
+
const sessionDiagnostics = await params.agentService.getSessionDiagnostics?.(params.sessionTarget) ?? {};
|
|
67605
67885
|
async function recordReplyIfNeeded() {
|
|
67606
67886
|
if (replyRecorded) {
|
|
67607
67887
|
return;
|
|
@@ -67676,6 +67956,7 @@ async function processChannelInteraction(params) {
|
|
|
67676
67956
|
route: params.route,
|
|
67677
67957
|
auth,
|
|
67678
67958
|
sessionTarget: params.sessionTarget,
|
|
67959
|
+
sessionDiagnostics,
|
|
67679
67960
|
followUpState,
|
|
67680
67961
|
runtimeState,
|
|
67681
67962
|
loopState: {
|
|
@@ -67696,7 +67977,8 @@ async function processChannelInteraction(params) {
|
|
|
67696
67977
|
identity: params.identity,
|
|
67697
67978
|
route: params.route,
|
|
67698
67979
|
auth,
|
|
67699
|
-
sessionTarget: params.sessionTarget
|
|
67980
|
+
sessionTarget: params.sessionTarget,
|
|
67981
|
+
sessionDiagnostics
|
|
67700
67982
|
}));
|
|
67701
67983
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67702
67984
|
return interactionResult;
|
|
@@ -67853,6 +68135,11 @@ async function processChannelInteraction(params) {
|
|
|
67853
68135
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67854
68136
|
return interactionResult;
|
|
67855
68137
|
}
|
|
68138
|
+
if (slashCommand.name === "queue-help") {
|
|
68139
|
+
await params.postText(renderQueueUsage());
|
|
68140
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68141
|
+
return interactionResult;
|
|
68142
|
+
}
|
|
67856
68143
|
if (slashCommand.name === "queue-clear") {
|
|
67857
68144
|
const clearedCount = params.agentService.clearQueuedPrompts?.(params.sessionTarget) ?? 0;
|
|
67858
68145
|
await params.postText(clearedCount > 0 ? `Cleared ${clearedCount} queued message${clearedCount === 1 ? "" : "s"}.` : "Queue was already empty.");
|
|
@@ -67860,6 +68147,11 @@ async function processChannelInteraction(params) {
|
|
|
67860
68147
|
return interactionResult;
|
|
67861
68148
|
}
|
|
67862
68149
|
}
|
|
68150
|
+
if (slashCommand?.type === "control" && slashCommand.name === "loop-help") {
|
|
68151
|
+
await params.postText(renderLoopUsage());
|
|
68152
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68153
|
+
return interactionResult;
|
|
68154
|
+
}
|
|
67863
68155
|
if (slashCommand?.type === "loop-control") {
|
|
67864
68156
|
if (slashCommand.action === "status") {
|
|
67865
68157
|
await params.postText(renderLoopStatusMessage({
|
|
@@ -68686,7 +68978,7 @@ async function clearSlackAssistantThreadStatus(client, target) {
|
|
|
68686
68978
|
|
|
68687
68979
|
// src/channels/processing-indicator.ts
|
|
68688
68980
|
function shouldResolveIndicatorWait(update) {
|
|
68689
|
-
return isTerminalRunStatus(update.status)
|
|
68981
|
+
return isTerminalRunStatus(update.status);
|
|
68690
68982
|
}
|
|
68691
68983
|
async function waitForProcessingIndicatorLifecycle(params) {
|
|
68692
68984
|
if (params.lifecycle !== "active-run") {
|
|
@@ -68817,6 +69109,7 @@ class ConversationProcessingIndicatorCoordinator {
|
|
|
68817
69109
|
}
|
|
68818
69110
|
|
|
68819
69111
|
// src/channels/slack/processing-decoration.ts
|
|
69112
|
+
var DEFAULT_STATUS_REFRESH_INTERVAL_MS = 2000;
|
|
68820
69113
|
async function activateSlackProcessingDecoration(params) {
|
|
68821
69114
|
const [reactionResult, statusResult] = await Promise.allSettled([
|
|
68822
69115
|
params.addReaction(),
|
|
@@ -68838,7 +69131,38 @@ async function activateSlackProcessingDecoration(params) {
|
|
|
68838
69131
|
throw statusResult.reason;
|
|
68839
69132
|
}
|
|
68840
69133
|
}
|
|
69134
|
+
let statusRefreshTimer;
|
|
69135
|
+
let closed = false;
|
|
69136
|
+
let refreshInFlight = false;
|
|
69137
|
+
let refreshPromise;
|
|
69138
|
+
if (statusApplied) {
|
|
69139
|
+
const refreshIntervalMs = Math.max(0, params.statusRefreshIntervalMs ?? DEFAULT_STATUS_REFRESH_INTERVAL_MS);
|
|
69140
|
+
if (refreshIntervalMs > 0) {
|
|
69141
|
+
statusRefreshTimer = setInterval(() => {
|
|
69142
|
+
if (closed || refreshInFlight) {
|
|
69143
|
+
return;
|
|
69144
|
+
}
|
|
69145
|
+
refreshInFlight = true;
|
|
69146
|
+
refreshPromise = params.setStatus().then(() => {
|
|
69147
|
+
return;
|
|
69148
|
+
}).catch((error) => {
|
|
69149
|
+
if (!closed) {
|
|
69150
|
+
params.onUnexpectedError?.("refresh-status", error);
|
|
69151
|
+
}
|
|
69152
|
+
}).finally(() => {
|
|
69153
|
+
refreshInFlight = false;
|
|
69154
|
+
refreshPromise = undefined;
|
|
69155
|
+
});
|
|
69156
|
+
}, refreshIntervalMs);
|
|
69157
|
+
}
|
|
69158
|
+
}
|
|
68841
69159
|
return async () => {
|
|
69160
|
+
closed = true;
|
|
69161
|
+
if (statusRefreshTimer) {
|
|
69162
|
+
clearInterval(statusRefreshTimer);
|
|
69163
|
+
statusRefreshTimer = undefined;
|
|
69164
|
+
}
|
|
69165
|
+
await refreshPromise;
|
|
68842
69166
|
if (reactionApplied) {
|
|
68843
69167
|
try {
|
|
68844
69168
|
await params.removeReaction();
|
|
@@ -69736,20 +70060,21 @@ class SlackSocketService {
|
|
|
69736
70060
|
});
|
|
69737
70061
|
if (!allowed) {
|
|
69738
70062
|
if (dmConfig.policy === "pairing") {
|
|
69739
|
-
const
|
|
70063
|
+
const pairingRequest = await upsertChannelPairingRequest({
|
|
69740
70064
|
channel: "slack",
|
|
69741
70065
|
id: directUserId
|
|
69742
70066
|
});
|
|
69743
|
-
|
|
70067
|
+
const pairingReply = buildPairingReplyFromRequest({
|
|
70068
|
+
channel: "slack",
|
|
70069
|
+
idLine: `Your Slack user id: ${directUserId}`,
|
|
70070
|
+
pairingRequest
|
|
70071
|
+
});
|
|
70072
|
+
if (pairingReply) {
|
|
69744
70073
|
try {
|
|
69745
70074
|
await postSlackText(this.app.client, {
|
|
69746
70075
|
channel: channelId,
|
|
69747
70076
|
threadTs: directReplyThreadTs,
|
|
69748
|
-
text:
|
|
69749
|
-
channel: "slack",
|
|
69750
|
-
idLine: `Your Slack user id: ${directUserId}`,
|
|
69751
|
-
code
|
|
69752
|
-
})
|
|
70077
|
+
text: pairingReply
|
|
69753
70078
|
});
|
|
69754
70079
|
} catch (error) {
|
|
69755
70080
|
console.error("slack pairing reply failed", error);
|
|
@@ -71344,7 +71669,7 @@ class TelegramPollingService {
|
|
|
71344
71669
|
});
|
|
71345
71670
|
if (!allowed) {
|
|
71346
71671
|
if (directMessages.policy === "pairing") {
|
|
71347
|
-
const
|
|
71672
|
+
const pairingRequest = await upsertChannelPairingRequest({
|
|
71348
71673
|
channel: "telegram",
|
|
71349
71674
|
id: senderId,
|
|
71350
71675
|
meta: {
|
|
@@ -71353,15 +71678,16 @@ class TelegramPollingService {
|
|
|
71353
71678
|
lastName: message.from?.last_name
|
|
71354
71679
|
}
|
|
71355
71680
|
});
|
|
71356
|
-
|
|
71681
|
+
const pairingReply = buildPairingReplyFromRequest({
|
|
71682
|
+
channel: "telegram",
|
|
71683
|
+
idLine: `Your Telegram user id: ${senderId}`,
|
|
71684
|
+
pairingRequest
|
|
71685
|
+
});
|
|
71686
|
+
if (pairingReply) {
|
|
71357
71687
|
try {
|
|
71358
71688
|
await callTelegramApi(this.accountConfig.botToken, "sendMessage", {
|
|
71359
71689
|
chat_id: message.chat.id,
|
|
71360
|
-
text:
|
|
71361
|
-
channel: "telegram",
|
|
71362
|
-
idLine: `Your Telegram user id: ${senderId}`,
|
|
71363
|
-
code
|
|
71364
|
-
})
|
|
71690
|
+
text: pairingReply
|
|
71365
71691
|
});
|
|
71366
71692
|
} catch (error) {
|
|
71367
71693
|
console.error("telegram pairing reply failed", error);
|
|
@@ -73035,10 +73361,17 @@ function renderAccountsHelp() {
|
|
|
73035
73361
|
"",
|
|
73036
73362
|
"Usage:",
|
|
73037
73363
|
" clisbot accounts --help",
|
|
73364
|
+
" clisbot accounts help",
|
|
73038
73365
|
" clisbot accounts add telegram --account <id> --token <ENV_NAME|${ENV_NAME}|literal> [--persist]",
|
|
73039
73366
|
" clisbot accounts add slack --account <id> --app-token <ENV_NAME|${ENV_NAME}|literal> --bot-token <ENV_NAME|${ENV_NAME}|literal> [--persist]",
|
|
73040
73367
|
" clisbot accounts persist --channel <slack|telegram> --account <id>",
|
|
73041
|
-
" clisbot accounts persist --all"
|
|
73368
|
+
" clisbot accounts persist --all",
|
|
73369
|
+
"",
|
|
73370
|
+
"Notes:",
|
|
73371
|
+
" - env-style input such as `TELEGRAM_BOT_TOKEN` or `${TELEGRAM_BOT_TOKEN}` keeps the account env-backed in config",
|
|
73372
|
+
" - literal token input without `--persist` stays runtime-only and requires a running clisbot runtime",
|
|
73373
|
+
" - `--persist` writes canonical token files so later plain `clisbot start` can reuse the account safely",
|
|
73374
|
+
" - `persist --all` converts every configured `credentialType=mem` account into canonical token files"
|
|
73042
73375
|
].join(`
|
|
73043
73376
|
`);
|
|
73044
73377
|
}
|
|
@@ -73215,7 +73548,7 @@ async function runAccountsCli(args, deps = {}) {
|
|
|
73215
73548
|
...deps
|
|
73216
73549
|
};
|
|
73217
73550
|
const action = args[0];
|
|
73218
|
-
if (!action || action === "--help" || action === "-h") {
|
|
73551
|
+
if (!action || action === "--help" || action === "-h" || action === "help") {
|
|
73219
73552
|
console.log(renderAccountsHelp());
|
|
73220
73553
|
return;
|
|
73221
73554
|
}
|
|
@@ -73319,6 +73652,7 @@ function renderAuthCliHelp() {
|
|
|
73319
73652
|
" add-user/remove-user mutate roles.<role>.users",
|
|
73320
73653
|
" add-permission/remove-permission mutate roles.<role>.allow",
|
|
73321
73654
|
" agent role edits clone the inherited agent-defaults role into the target agent override on first write",
|
|
73655
|
+
" app `owner` and `admin` principals bypass DM pairing automatically once they are granted",
|
|
73322
73656
|
"",
|
|
73323
73657
|
"Examples:",
|
|
73324
73658
|
" clisbot auth add-user app --role owner --user telegram:1276408333",
|
|
@@ -73581,6 +73915,7 @@ function renderChannelsHelp() {
|
|
|
73581
73915
|
" - Slack private groups need channels.slack.groups.<groupId>",
|
|
73582
73916
|
" - Telegram groups need channels.telegram.groups.<chatId>",
|
|
73583
73917
|
" - Telegram forum topics need channels.telegram.groups.<chatId>.topics.<topicId>",
|
|
73918
|
+
" - route adds for Slack channels, Slack groups, Telegram groups, and Telegram topics default to `requireMention: true` unless you pass `--require-mention false`",
|
|
73584
73919
|
" - Adding a route puts that surface on the allowlist; other channels, groups, or topics still need to be added explicitly",
|
|
73585
73920
|
" - Tune route settings such as requireMention and followUp in clisbot.json when a surface should behave differently",
|
|
73586
73921
|
` - Manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`,
|
|
@@ -73659,6 +73994,7 @@ function renderRouteAddGuidance(params) {
|
|
|
73659
73994
|
console.log(` - route added: ${routePath}`);
|
|
73660
73995
|
console.log(" - direct messages still follow channels.slack.directMessages.policy (`open`, `pairing`, `allowlist`, or `disabled`)");
|
|
73661
73996
|
console.log(` - this ${routeLabel} still follows channels.slack.groupPolicy and route settings such as requireMention and followUp`);
|
|
73997
|
+
console.log(" - new Slack channel/group routes default to `requireMention: true` unless you passed `--require-mention false`");
|
|
73662
73998
|
console.log(" - if you want pairing-style access control for DMs, set channels.slack.directMessages.policy to `pairing`");
|
|
73663
73999
|
console.log(" - if you want stricter route access, keep Slack groups on allowlist and only add the channels/groups you trust");
|
|
73664
74000
|
console.log(` - manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`);
|
|
@@ -73669,6 +74005,7 @@ function renderRouteAddGuidance(params) {
|
|
|
73669
74005
|
console.log(` - route added: ${routePath}`);
|
|
73670
74006
|
console.log(" - direct messages still follow channels.telegram.directMessages.policy (`open`, `pairing`, `allowlist`, or `disabled`)");
|
|
73671
74007
|
console.log(` - this ${params.kind} is now on the Telegram allowlist; other groups or topics still need to be added explicitly`);
|
|
74008
|
+
console.log(" - new Telegram group/topic routes default to `requireMention: true` unless you passed `--require-mention false`");
|
|
73672
74009
|
console.log(" - if you want pairing-style access control for DMs, set channels.telegram.directMessages.policy to `pairing`");
|
|
73673
74010
|
console.log(" - tune route settings such as requireMention and followUp in clisbot.json if this surface should behave differently");
|
|
73674
74011
|
console.log(` - manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`);
|
|
@@ -73867,7 +74204,7 @@ async function addSlackRoute(kind, args) {
|
|
|
73867
74204
|
}
|
|
73868
74205
|
const { config, configPath } = await readEditableConfig(getEditableConfigPath7());
|
|
73869
74206
|
const agentId = getAgentId(args);
|
|
73870
|
-
const requireMention = parseBooleanOption(args, "--require-mention",
|
|
74207
|
+
const requireMention = parseBooleanOption(args, "--require-mention", true);
|
|
73871
74208
|
const target = kind === "channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
73872
74209
|
target[routeId] = {
|
|
73873
74210
|
...target[routeId] ?? {},
|
|
@@ -75098,6 +75435,41 @@ function assertSupportedPlatform(command) {
|
|
|
75098
75435
|
}
|
|
75099
75436
|
|
|
75100
75437
|
// src/control/runtime-bootstrap-cli.ts
|
|
75438
|
+
function hasHelpFlag(args) {
|
|
75439
|
+
return args.includes("--help") || args.includes("-h") || args.includes("help");
|
|
75440
|
+
}
|
|
75441
|
+
function renderBootstrapCommandHelp(commandName) {
|
|
75442
|
+
const behavior = commandName === "start" ? "seed config if needed and start the detached runtime" : "seed config and optionally bootstrap the first agent without starting runtime";
|
|
75443
|
+
return [
|
|
75444
|
+
`clisbot ${commandName}`,
|
|
75445
|
+
"",
|
|
75446
|
+
"Usage:",
|
|
75447
|
+
` clisbot ${commandName} --help`,
|
|
75448
|
+
` clisbot ${commandName} [--cli <codex|claude|gemini>] [--bot-type <personal|team>] [--persist]`,
|
|
75449
|
+
" [--slack-account <id> --slack-app-token <ENV_NAME|${ENV_NAME}|literal> --slack-bot-token <ENV_NAME|${ENV_NAME}|literal>]...",
|
|
75450
|
+
" [--telegram-account <id> --telegram-bot-token <ENV_NAME|${ENV_NAME}|literal>]...",
|
|
75451
|
+
"",
|
|
75452
|
+
"Behavior:",
|
|
75453
|
+
` - ${behavior}`,
|
|
75454
|
+
" - first-run agent bootstrap needs both `--cli` and `--bot-type`",
|
|
75455
|
+
" - `--bot-type personal` maps to `personal-assistant`; `--bot-type team` maps to `team-assistant`",
|
|
75456
|
+
" - explicit credential flags only enable the channels and accounts you named in this command",
|
|
75457
|
+
" - env-style values such as `SLACK_APP_TOKEN` or `${SLACK_APP_TOKEN}` stay env-backed in config",
|
|
75458
|
+
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",
|
|
75459
|
+
" - `--persist` writes canonical credential files so later plain `clisbot start` can reuse them",
|
|
75460
|
+
"",
|
|
75461
|
+
"Examples:",
|
|
75462
|
+
` clisbot ${commandName} --cli codex --bot-type personal --telegram-bot-token TELEGRAM_BOT_TOKEN`,
|
|
75463
|
+
` clisbot ${commandName} --cli codex --bot-type team --slack-app-token SLACK_APP_TOKEN --slack-bot-token SLACK_BOT_TOKEN`,
|
|
75464
|
+
` clisbot ${commandName} --cli gemini --bot-type personal --telegram-bot-token "$TELEGRAM_BOT_TOKEN" --persist`,
|
|
75465
|
+
"",
|
|
75466
|
+
"Related help:",
|
|
75467
|
+
" - `clisbot agents --help` for lower-level agent bootstrap and binding control",
|
|
75468
|
+
" - `clisbot accounts --help` for account persistence after first run",
|
|
75469
|
+
" - `clisbot channels --help` for route setup after bootstrap"
|
|
75470
|
+
].join(`
|
|
75471
|
+
`);
|
|
75472
|
+
}
|
|
75101
75473
|
function getPrimaryWorkspacePath(summary) {
|
|
75102
75474
|
const preferredAgentId = summary.channelSummaries.find((channel) => channel.enabled)?.defaultAgentId ?? "default";
|
|
75103
75475
|
return summary.agentSummaries.find((agent) => agent.id === preferredAgentId)?.workspacePath ?? summary.agentSummaries[0]?.workspacePath;
|
|
@@ -75314,6 +75686,10 @@ async function printStartedRuntimeSummary(pid, configPath, logPath) {
|
|
|
75314
75686
|
}
|
|
75315
75687
|
}
|
|
75316
75688
|
async function initConfig(args = []) {
|
|
75689
|
+
if (hasHelpFlag(args)) {
|
|
75690
|
+
console.log(renderBootstrapCommandHelp("init"));
|
|
75691
|
+
return;
|
|
75692
|
+
}
|
|
75317
75693
|
const state = await prepareBootstrapState(args, "init");
|
|
75318
75694
|
if (!state) {
|
|
75319
75695
|
return;
|
|
@@ -75332,6 +75708,10 @@ async function initConfig(args = []) {
|
|
|
75332
75708
|
}
|
|
75333
75709
|
}
|
|
75334
75710
|
async function start(args = []) {
|
|
75711
|
+
if (hasHelpFlag(args)) {
|
|
75712
|
+
console.log(renderBootstrapCommandHelp("start"));
|
|
75713
|
+
return;
|
|
75714
|
+
}
|
|
75335
75715
|
const runtimeStatus = await getRuntimeStatus();
|
|
75336
75716
|
const bootstrapFlags = parseBootstrapFlags(args);
|
|
75337
75717
|
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
|
|