clisbot 0.1.11 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -155
- package/config/clisbot.json.template +88 -21
- package/dist/main.js +1832 -820
- package/package.json +1 -1
- package/templates/customized/personal-assistant/GEMINI.md +136 -0
- package/templates/customized/team-assistant/GEMINI.md +137 -0
- package/templates/slack/default/app-manifest-guide.md +111 -0
- package/templates/slack/default/app-manifest.json +57 -0
package/dist/main.js
CHANGED
|
@@ -47740,10 +47740,10 @@ var require_view2 = __commonJS((exports, module) => {
|
|
|
47740
47740
|
var debug = require_src()("express:view");
|
|
47741
47741
|
var path2 = __require("node:path");
|
|
47742
47742
|
var fs2 = __require("node:fs");
|
|
47743
|
-
var
|
|
47743
|
+
var dirname13 = path2.dirname;
|
|
47744
47744
|
var basename = path2.basename;
|
|
47745
47745
|
var extname = path2.extname;
|
|
47746
|
-
var
|
|
47746
|
+
var join9 = path2.join;
|
|
47747
47747
|
var resolve = path2.resolve;
|
|
47748
47748
|
module.exports = View;
|
|
47749
47749
|
function View(name, options) {
|
|
@@ -47779,7 +47779,7 @@ var require_view2 = __commonJS((exports, module) => {
|
|
|
47779
47779
|
for (var i = 0;i < roots.length && !path3; i++) {
|
|
47780
47780
|
var root = roots[i];
|
|
47781
47781
|
var loc = resolve(root, name);
|
|
47782
|
-
var dir =
|
|
47782
|
+
var dir = dirname13(loc);
|
|
47783
47783
|
var file = basename(loc);
|
|
47784
47784
|
path3 = this.resolve(dir, file);
|
|
47785
47785
|
}
|
|
@@ -47805,12 +47805,12 @@ var require_view2 = __commonJS((exports, module) => {
|
|
|
47805
47805
|
};
|
|
47806
47806
|
View.prototype.resolve = function resolve2(dir, file) {
|
|
47807
47807
|
var ext = this.ext;
|
|
47808
|
-
var path3 =
|
|
47808
|
+
var path3 = join9(dir, file);
|
|
47809
47809
|
var stat = tryStat(path3);
|
|
47810
47810
|
if (stat && stat.isFile()) {
|
|
47811
47811
|
return path3;
|
|
47812
47812
|
}
|
|
47813
|
-
path3 =
|
|
47813
|
+
path3 = join9(dir, basename(file, ext), "index" + ext);
|
|
47814
47814
|
stat = tryStat(path3);
|
|
47815
47815
|
if (stat && stat.isFile()) {
|
|
47816
47816
|
return path3;
|
|
@@ -51153,7 +51153,7 @@ var require_send = __commonJS((exports, module) => {
|
|
|
51153
51153
|
var Stream = __require("stream");
|
|
51154
51154
|
var util3 = __require("util");
|
|
51155
51155
|
var extname = path2.extname;
|
|
51156
|
-
var
|
|
51156
|
+
var join9 = path2.join;
|
|
51157
51157
|
var normalize = path2.normalize;
|
|
51158
51158
|
var resolve = path2.resolve;
|
|
51159
51159
|
var sep2 = path2.sep;
|
|
@@ -51325,7 +51325,7 @@ var require_send = __commonJS((exports, module) => {
|
|
|
51325
51325
|
return res;
|
|
51326
51326
|
}
|
|
51327
51327
|
parts = path3.split(sep2);
|
|
51328
|
-
path3 = normalize(
|
|
51328
|
+
path3 = normalize(join9(root, path3));
|
|
51329
51329
|
} else {
|
|
51330
51330
|
if (UP_PATH_REGEXP.test(path3)) {
|
|
51331
51331
|
debug('malicious path "%s"', path3);
|
|
@@ -51465,7 +51465,7 @@ var require_send = __commonJS((exports, module) => {
|
|
|
51465
51465
|
return self2.onStatError(err);
|
|
51466
51466
|
return self2.error(404);
|
|
51467
51467
|
}
|
|
51468
|
-
var p =
|
|
51468
|
+
var p = join9(path3, self2._index[i]);
|
|
51469
51469
|
debug('stat "%s"', p);
|
|
51470
51470
|
fs2.stat(p, function(err2, stat) {
|
|
51471
51471
|
if (err2)
|
|
@@ -54225,7 +54225,11 @@ function resolveSlackCredential(params) {
|
|
|
54225
54225
|
function materializeRuntimeChannelCredentials(config, options = {}) {
|
|
54226
54226
|
const env = options.env ?? process.env;
|
|
54227
54227
|
const nextConfig = structuredClone(config);
|
|
54228
|
-
|
|
54228
|
+
const materializeChannels = options.materializeChannels ?? [];
|
|
54229
|
+
const materializeAll = materializeChannels.length === 0;
|
|
54230
|
+
const shouldMaterializeTelegram = materializeAll || materializeChannels.includes("telegram");
|
|
54231
|
+
const shouldMaterializeSlack = materializeAll || materializeChannels.includes("slack");
|
|
54232
|
+
if (shouldMaterializeTelegram && nextConfig.channels.telegram.enabled) {
|
|
54229
54233
|
const accountIds = Object.keys(getAccountsRecord(nextConfig.channels.telegram.accounts));
|
|
54230
54234
|
const ids = accountIds.length > 0 ? accountIds : [getConfiguredDefaultAccountId({
|
|
54231
54235
|
defaultAccount: nextConfig.channels.telegram.defaultAccount,
|
|
@@ -54263,7 +54267,7 @@ function materializeRuntimeChannelCredentials(config, options = {}) {
|
|
|
54263
54267
|
const fallbackTelegramAccountId = preferredDefaultTelegramAccountId && resolvedAccounts[preferredDefaultTelegramAccountId] ? preferredDefaultTelegramAccountId : Object.keys(resolvedAccounts)[0];
|
|
54264
54268
|
nextConfig.channels.telegram.botToken = fallbackTelegramAccountId ? resolvedAccounts[fallbackTelegramAccountId]?.botToken ?? "" : "";
|
|
54265
54269
|
}
|
|
54266
|
-
if (nextConfig.channels.slack.enabled) {
|
|
54270
|
+
if (shouldMaterializeSlack && nextConfig.channels.slack.enabled) {
|
|
54267
54271
|
const accountIds = Object.keys(getAccountsRecord(nextConfig.channels.slack.accounts));
|
|
54268
54272
|
const ids = accountIds.length > 0 ? accountIds : [getConfiguredDefaultAccountId({
|
|
54269
54273
|
defaultAccount: nextConfig.channels.slack.defaultAccount,
|
|
@@ -54361,53 +54365,6 @@ function getConfigReloadMtimeMs(configPath) {
|
|
|
54361
54365
|
return statSync(expandHomePath(configPath)).mtimeMs;
|
|
54362
54366
|
}
|
|
54363
54367
|
|
|
54364
|
-
// src/channels/privilege-help.ts
|
|
54365
|
-
function renderGenericPrivilegeCommandHelpLines(prefix = "") {
|
|
54366
|
-
return [
|
|
54367
|
-
`${prefix}Privilege command setup:`,
|
|
54368
|
-
`${prefix} - enable for a Slack route: \`clisbot channels privilege enable slack-channel <channelId>\` or \`clisbot channels privilege enable slack-group <groupId>\``,
|
|
54369
|
-
`${prefix} - allow a Slack user: \`clisbot channels privilege allow-user slack-channel <channelId> <userId>\``,
|
|
54370
|
-
`${prefix} - enable Slack DM privilege commands: \`clisbot channels privilege enable slack-dm\``,
|
|
54371
|
-
`${prefix} - allow a Slack DM user: \`clisbot channels privilege allow-user slack-dm <userId>\``,
|
|
54372
|
-
`${prefix} - enable for a Telegram route: \`clisbot channels privilege enable telegram-group <chatId> [--topic <topicId>]\``,
|
|
54373
|
-
`${prefix} - allow a Telegram user: \`clisbot channels privilege allow-user telegram-group <chatId> <userId> [--topic <topicId>]\``,
|
|
54374
|
-
`${prefix} - enable Telegram DM privilege commands: \`clisbot channels privilege enable telegram-dm\``,
|
|
54375
|
-
`${prefix} - allow a Telegram DM user: \`clisbot channels privilege allow-user telegram-dm <userId>\``
|
|
54376
|
-
];
|
|
54377
|
-
}
|
|
54378
|
-
function renderPrivilegeCommandHelpLines(identity, prefix = "") {
|
|
54379
|
-
const target = buildPrivilegeCommandTarget(identity);
|
|
54380
|
-
if (!target) {
|
|
54381
|
-
return [];
|
|
54382
|
-
}
|
|
54383
|
-
const allowUserSuffix = identity.senderId ? ` ${identity.senderId}` : " <userId>";
|
|
54384
|
-
return [
|
|
54385
|
-
`${prefix}Operator commands:`,
|
|
54386
|
-
`${prefix} - enable privilege commands: \`clisbot channels privilege enable ${target}\``,
|
|
54387
|
-
`${prefix} - allow this user: \`clisbot channels privilege allow-user ${target}${allowUserSuffix}\``,
|
|
54388
|
-
`${prefix} - disable privilege commands: \`clisbot channels privilege disable ${target}\``,
|
|
54389
|
-
`${prefix} - remove this user: \`clisbot channels privilege remove-user ${target}${allowUserSuffix}\``
|
|
54390
|
-
];
|
|
54391
|
-
}
|
|
54392
|
-
function buildPrivilegeCommandTarget(identity) {
|
|
54393
|
-
if (identity.platform === "slack") {
|
|
54394
|
-
if (identity.conversationKind === "dm") {
|
|
54395
|
-
return "slack-dm";
|
|
54396
|
-
}
|
|
54397
|
-
if (identity.conversationKind === "group") {
|
|
54398
|
-
return identity.channelId ? `slack-group ${identity.channelId}` : null;
|
|
54399
|
-
}
|
|
54400
|
-
return identity.channelId ? `slack-channel ${identity.channelId}` : null;
|
|
54401
|
-
}
|
|
54402
|
-
if (identity.conversationKind === "dm") {
|
|
54403
|
-
return "telegram-dm";
|
|
54404
|
-
}
|
|
54405
|
-
if (!identity.chatId) {
|
|
54406
|
-
return null;
|
|
54407
|
-
}
|
|
54408
|
-
return identity.conversationKind === "topic" && identity.topicId ? `telegram-group ${identity.chatId} --topic ${identity.topicId}` : `telegram-group ${identity.chatId}`;
|
|
54409
|
-
}
|
|
54410
|
-
|
|
54411
54368
|
// src/control/startup-bootstrap.ts
|
|
54412
54369
|
var CHANNEL_ACCOUNT_DOC_PATH = "docs/user-guide/channel-accounts.md";
|
|
54413
54370
|
var USER_GUIDE_DOC_PATH = "docs/user-guide/README.md";
|
|
@@ -54532,7 +54489,7 @@ function renderTmuxDebugHelpLines(prefix = "") {
|
|
|
54532
54489
|
`${prefix} - attach to a session: \`tmux -S ${socketPath} attach -t <session-name>\``
|
|
54533
54490
|
];
|
|
54534
54491
|
}
|
|
54535
|
-
function renderChannelSetupHelpLines(prefix = "",
|
|
54492
|
+
function renderChannelSetupHelpLines(prefix = "", _options = {}) {
|
|
54536
54493
|
return [
|
|
54537
54494
|
`${prefix}Channel setup docs: ${CHANNEL_ACCOUNT_DOC_PATH}`,
|
|
54538
54495
|
`${prefix}Operator guide: ${USER_GUIDE_DOC_PATH}`,
|
|
@@ -54542,7 +54499,6 @@ function renderChannelSetupHelpLines(prefix = "", options = {}) {
|
|
|
54542
54499
|
telegramDirectMessagesPolicy: "pairing"
|
|
54543
54500
|
}),
|
|
54544
54501
|
...renderTmuxDebugHelpLines(prefix),
|
|
54545
|
-
...options.includePrivilegeHelp === false ? [] : renderGenericPrivilegeCommandHelpLines(prefix),
|
|
54546
54502
|
...renderRepoHelpLines(prefix)
|
|
54547
54503
|
];
|
|
54548
54504
|
}
|
|
@@ -54670,7 +54626,7 @@ function renderCliHelp() {
|
|
|
54670
54626
|
" Fresh bootstrap only enables channels named by flags; ambient env vars alone do not auto-enable extra channels.",
|
|
54671
54627
|
"",
|
|
54672
54628
|
"Usage:",
|
|
54673
|
-
" clisbot start [--cli <codex|claude>] [--bot-type <personal|team>] [--persist]",
|
|
54629
|
+
" clisbot start [--cli <codex|claude|gemini>] [--bot-type <personal|team>] [--persist]",
|
|
54674
54630
|
" [--slack-account <id> --slack-app-token <ENV_NAME|${ENV_NAME}|literal> --slack-bot-token <ENV_NAME|${ENV_NAME}|literal>]...",
|
|
54675
54631
|
" [--telegram-account <id> --telegram-bot-token <ENV_NAME|${ENV_NAME}|literal>]...",
|
|
54676
54632
|
" clisbot restart",
|
|
@@ -54684,7 +54640,7 @@ function renderCliHelp() {
|
|
|
54684
54640
|
" clisbot message <subcommand>",
|
|
54685
54641
|
" clisbot agents <subcommand>",
|
|
54686
54642
|
" clisbot pairing <subcommand>",
|
|
54687
|
-
" clisbot init [--cli <codex|claude>] [--bot-type <personal|team>] [--persist]",
|
|
54643
|
+
" clisbot init [--cli <codex|claude|gemini>] [--bot-type <personal|team>] [--persist]",
|
|
54688
54644
|
" [--slack-account <id> --slack-app-token <ENV_NAME|${ENV_NAME}|literal> --slack-bot-token <ENV_NAME|${ENV_NAME}|literal>]...",
|
|
54689
54645
|
" [--telegram-account <id> --telegram-bot-token <ENV_NAME|${ENV_NAME}|literal>]...",
|
|
54690
54646
|
" clis <same-command>",
|
|
@@ -55104,7 +55060,7 @@ async function runPairingCli(args, writer = console) {
|
|
|
55104
55060
|
}
|
|
55105
55061
|
|
|
55106
55062
|
// src/config/agent-tool-presets.ts
|
|
55107
|
-
var SUPPORTED_AGENT_CLI_TOOLS = ["codex", "claude"];
|
|
55063
|
+
var SUPPORTED_AGENT_CLI_TOOLS = ["codex", "claude", "gemini"];
|
|
55108
55064
|
var SUPPORTED_BOOTSTRAP_MODES = ["personal-assistant", "team-assistant"];
|
|
55109
55065
|
var SESSION_ID_PATTERN = "\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\b";
|
|
55110
55066
|
var DEFAULT_AGENT_TOOL_TEMPLATES = {
|
|
@@ -55169,6 +55125,41 @@ var DEFAULT_AGENT_TOOL_TEMPLATES = {
|
|
|
55169
55125
|
]
|
|
55170
55126
|
}
|
|
55171
55127
|
}
|
|
55128
|
+
},
|
|
55129
|
+
gemini: {
|
|
55130
|
+
command: "gemini",
|
|
55131
|
+
startupOptions: ["--approval-mode=yolo", "--sandbox=false"],
|
|
55132
|
+
trustWorkspace: true,
|
|
55133
|
+
startupDelayMs: 15000,
|
|
55134
|
+
startupReadyPattern: "Type your message or @path/to/file",
|
|
55135
|
+
startupBlockers: [
|
|
55136
|
+
{
|
|
55137
|
+
pattern: "Please visit the following URL to authorize the application|Enter the authorization code:",
|
|
55138
|
+
message: "Gemini CLI is waiting for manual OAuth authorization. Authenticate Gemini once in a direct interactive terminal, or configure headless auth such as GEMINI_API_KEY or Vertex AI before routing Gemini through clisbot."
|
|
55139
|
+
},
|
|
55140
|
+
{
|
|
55141
|
+
pattern: "How would you like to authenticate for this project\\?|Failed to sign in\\.|Manual authorization is required but the current session is non-interactive",
|
|
55142
|
+
message: "Gemini CLI is blocked in its authentication setup flow or sign-in recovery. Complete Gemini authentication directly first, or switch clisbot to a headless auth path such as GEMINI_API_KEY or Vertex AI before routing prompts."
|
|
55143
|
+
}
|
|
55144
|
+
],
|
|
55145
|
+
promptSubmitDelayMs: 200,
|
|
55146
|
+
sessionId: {
|
|
55147
|
+
create: {
|
|
55148
|
+
mode: "runner",
|
|
55149
|
+
args: []
|
|
55150
|
+
},
|
|
55151
|
+
capture: {
|
|
55152
|
+
mode: "status-command",
|
|
55153
|
+
statusCommand: "/stats session",
|
|
55154
|
+
pattern: SESSION_ID_PATTERN,
|
|
55155
|
+
timeoutMs: 8000,
|
|
55156
|
+
pollIntervalMs: 250
|
|
55157
|
+
},
|
|
55158
|
+
resume: {
|
|
55159
|
+
mode: "command",
|
|
55160
|
+
args: ["--resume", "{sessionId}", "--approval-mode=yolo", "--sandbox=false"]
|
|
55161
|
+
}
|
|
55162
|
+
}
|
|
55172
55163
|
}
|
|
55173
55164
|
};
|
|
55174
55165
|
function buildRunnerFromToolTemplate(toolId, template, startupOptions) {
|
|
@@ -55179,6 +55170,8 @@ function buildRunnerFromToolTemplate(toolId, template, startupOptions) {
|
|
|
55179
55170
|
args: [...options, "-C", "{workspace}"],
|
|
55180
55171
|
trustWorkspace: template.trustWorkspace,
|
|
55181
55172
|
startupDelayMs: template.startupDelayMs,
|
|
55173
|
+
startupReadyPattern: template.startupReadyPattern,
|
|
55174
|
+
startupBlockers: template.startupBlockers?.map((entry) => ({ ...entry })),
|
|
55182
55175
|
promptSubmitDelayMs: template.promptSubmitDelayMs,
|
|
55183
55176
|
sessionId: {
|
|
55184
55177
|
...template.sessionId,
|
|
@@ -55201,6 +55194,8 @@ function buildRunnerFromToolTemplate(toolId, template, startupOptions) {
|
|
|
55201
55194
|
args: [...options],
|
|
55202
55195
|
trustWorkspace: template.trustWorkspace,
|
|
55203
55196
|
startupDelayMs: template.startupDelayMs,
|
|
55197
|
+
startupReadyPattern: template.startupReadyPattern,
|
|
55198
|
+
startupBlockers: template.startupBlockers?.map((entry) => ({ ...entry })),
|
|
55204
55199
|
promptSubmitDelayMs: template.promptSubmitDelayMs,
|
|
55205
55200
|
sessionId: {
|
|
55206
55201
|
...template.sessionId,
|
|
@@ -55229,6 +55224,9 @@ function inferAgentCliToolId(command) {
|
|
|
55229
55224
|
if (trimmed === "claude") {
|
|
55230
55225
|
return "claude";
|
|
55231
55226
|
}
|
|
55227
|
+
if (trimmed === "gemini") {
|
|
55228
|
+
return "gemini";
|
|
55229
|
+
}
|
|
55232
55230
|
return null;
|
|
55233
55231
|
}
|
|
55234
55232
|
|
|
@@ -59705,6 +59703,39 @@ function convertLocalDateTimeToUtcMs(params) {
|
|
|
59705
59703
|
return null;
|
|
59706
59704
|
}
|
|
59707
59705
|
|
|
59706
|
+
// src/auth/defaults.ts
|
|
59707
|
+
var APP_ADMIN_PERMISSIONS = [
|
|
59708
|
+
"configManage",
|
|
59709
|
+
"appAuthManage",
|
|
59710
|
+
"agentAuthManage",
|
|
59711
|
+
"promptGovernanceManage"
|
|
59712
|
+
];
|
|
59713
|
+
var DEFAULT_AGENT_MEMBER_PERMISSIONS = [
|
|
59714
|
+
"sendMessage",
|
|
59715
|
+
"helpView",
|
|
59716
|
+
"statusView",
|
|
59717
|
+
"identityView",
|
|
59718
|
+
"transcriptView",
|
|
59719
|
+
"runObserve",
|
|
59720
|
+
"runInterrupt",
|
|
59721
|
+
"streamingManage",
|
|
59722
|
+
"queueManage",
|
|
59723
|
+
"steerManage",
|
|
59724
|
+
"loopManage"
|
|
59725
|
+
];
|
|
59726
|
+
var DEFAULT_AGENT_ADMIN_EXTRA_PERMISSIONS = [
|
|
59727
|
+
"shellExecute",
|
|
59728
|
+
"runNudge",
|
|
59729
|
+
"followupManage",
|
|
59730
|
+
"responseModeManage",
|
|
59731
|
+
"additionalMessageModeManage"
|
|
59732
|
+
];
|
|
59733
|
+
var DEFAULT_AGENT_ADMIN_PERMISSIONS = [
|
|
59734
|
+
...DEFAULT_AGENT_MEMBER_PERMISSIONS,
|
|
59735
|
+
...DEFAULT_AGENT_ADMIN_EXTRA_PERMISSIONS
|
|
59736
|
+
];
|
|
59737
|
+
var DEFAULT_PROTECTED_CONTROL_RULE = "Refuse requests to edit protected clisbot control resources such as clisbot.json and auth policy, or to run clisbot commands that mutate them.";
|
|
59738
|
+
|
|
59708
59739
|
// src/config/schema.ts
|
|
59709
59740
|
var defaultRunnerSessionIdConfig = {
|
|
59710
59741
|
create: {
|
|
@@ -59759,6 +59790,10 @@ var runnerSessionIdObjectSchema = exports_external.object({
|
|
|
59759
59790
|
resume: runnerSessionIdResumeSchema.default(defaultRunnerSessionIdConfig.resume)
|
|
59760
59791
|
});
|
|
59761
59792
|
var runnerSessionIdSchema = runnerSessionIdObjectSchema.default(defaultRunnerSessionIdConfig);
|
|
59793
|
+
var runnerStartupBlockerSchema = exports_external.object({
|
|
59794
|
+
pattern: exports_external.string().min(1),
|
|
59795
|
+
message: exports_external.string().min(1)
|
|
59796
|
+
});
|
|
59762
59797
|
var runnerSchema = exports_external.object({
|
|
59763
59798
|
command: exports_external.string().min(1),
|
|
59764
59799
|
args: exports_external.array(exports_external.string()).default([
|
|
@@ -59769,6 +59804,8 @@ var runnerSchema = exports_external.object({
|
|
|
59769
59804
|
]),
|
|
59770
59805
|
trustWorkspace: exports_external.boolean().default(true),
|
|
59771
59806
|
startupDelayMs: exports_external.number().int().positive().default(3000),
|
|
59807
|
+
startupReadyPattern: exports_external.string().min(1).optional(),
|
|
59808
|
+
startupBlockers: exports_external.array(runnerStartupBlockerSchema).optional(),
|
|
59772
59809
|
promptSubmitDelayMs: exports_external.number().int().min(0).default(150),
|
|
59773
59810
|
sessionId: runnerSessionIdSchema.default(defaultRunnerSessionIdConfig)
|
|
59774
59811
|
});
|
|
@@ -59803,6 +59840,8 @@ var runnerOverrideSchema = exports_external.object({
|
|
|
59803
59840
|
args: exports_external.array(exports_external.string()).optional(),
|
|
59804
59841
|
trustWorkspace: exports_external.boolean().optional(),
|
|
59805
59842
|
startupDelayMs: exports_external.number().int().positive().optional(),
|
|
59843
|
+
startupReadyPattern: exports_external.string().min(1).optional(),
|
|
59844
|
+
startupBlockers: exports_external.array(runnerStartupBlockerSchema).optional(),
|
|
59806
59845
|
promptSubmitDelayMs: exports_external.number().int().min(0).optional(),
|
|
59807
59846
|
sessionId: runnerSessionIdObjectSchema.partial().extend({
|
|
59808
59847
|
create: runnerSessionIdCreateSchema.partial().optional(),
|
|
@@ -59813,10 +59852,46 @@ var runnerOverrideSchema = exports_external.object({
|
|
|
59813
59852
|
var agentBootstrapSchema = exports_external.object({
|
|
59814
59853
|
mode: exports_external.enum(SUPPORTED_BOOTSTRAP_MODES).default("personal-assistant")
|
|
59815
59854
|
});
|
|
59855
|
+
var authRoleSchema = exports_external.object({
|
|
59856
|
+
allow: exports_external.array(exports_external.string().min(1)).default([]),
|
|
59857
|
+
users: exports_external.array(exports_external.string().min(1)).default([])
|
|
59858
|
+
});
|
|
59859
|
+
var appAuthSchema = exports_external.object({
|
|
59860
|
+
ownerClaimWindowMinutes: exports_external.number().int().positive().default(30),
|
|
59861
|
+
defaultRole: exports_external.string().min(1).default("member"),
|
|
59862
|
+
roles: exports_external.record(exports_external.string(), authRoleSchema).default({
|
|
59863
|
+
owner: {
|
|
59864
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
59865
|
+
users: []
|
|
59866
|
+
},
|
|
59867
|
+
admin: {
|
|
59868
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
59869
|
+
users: []
|
|
59870
|
+
},
|
|
59871
|
+
member: {
|
|
59872
|
+
allow: [],
|
|
59873
|
+
users: []
|
|
59874
|
+
}
|
|
59875
|
+
})
|
|
59876
|
+
});
|
|
59877
|
+
var agentAuthSchema = exports_external.object({
|
|
59878
|
+
defaultRole: exports_external.string().min(1).default("member"),
|
|
59879
|
+
roles: exports_external.record(exports_external.string(), authRoleSchema).default({
|
|
59880
|
+
admin: {
|
|
59881
|
+
allow: [...DEFAULT_AGENT_ADMIN_PERMISSIONS],
|
|
59882
|
+
users: []
|
|
59883
|
+
},
|
|
59884
|
+
member: {
|
|
59885
|
+
allow: [...DEFAULT_AGENT_MEMBER_PERMISSIONS],
|
|
59886
|
+
users: []
|
|
59887
|
+
}
|
|
59888
|
+
})
|
|
59889
|
+
});
|
|
59816
59890
|
var agentOverrideSchema = exports_external.object({
|
|
59817
59891
|
workspace: exports_external.string().optional(),
|
|
59818
59892
|
responseMode: exports_external.enum(["capture-pane", "message-tool"]).optional(),
|
|
59819
59893
|
additionalMessageMode: exports_external.enum(["queue", "steer"]).optional(),
|
|
59894
|
+
auth: agentAuthSchema.optional(),
|
|
59820
59895
|
runner: runnerOverrideSchema.optional(),
|
|
59821
59896
|
stream: streamSchema.partial().optional(),
|
|
59822
59897
|
session: sessionSchema.partial().optional()
|
|
@@ -59831,6 +59906,19 @@ var agentEntrySchema = agentOverrideSchema.extend({
|
|
|
59831
59906
|
});
|
|
59832
59907
|
var agentDefaultsSchema = exports_external.object({
|
|
59833
59908
|
workspace: exports_external.string().default("~/.clisbot/workspaces/{agentId}"),
|
|
59909
|
+
auth: agentAuthSchema.default({
|
|
59910
|
+
defaultRole: "member",
|
|
59911
|
+
roles: {
|
|
59912
|
+
admin: {
|
|
59913
|
+
allow: [...DEFAULT_AGENT_ADMIN_PERMISSIONS],
|
|
59914
|
+
users: []
|
|
59915
|
+
},
|
|
59916
|
+
member: {
|
|
59917
|
+
allow: [...DEFAULT_AGENT_MEMBER_PERMISSIONS],
|
|
59918
|
+
users: []
|
|
59919
|
+
}
|
|
59920
|
+
}
|
|
59921
|
+
}),
|
|
59834
59922
|
runner: runnerSchema.default({
|
|
59835
59923
|
command: "codex",
|
|
59836
59924
|
args: [
|
|
@@ -59897,6 +59985,7 @@ var channelAgentPromptSchema = exports_external.object({
|
|
|
59897
59985
|
});
|
|
59898
59986
|
var channelResponseModeSchema = exports_external.enum(["capture-pane", "message-tool"]);
|
|
59899
59987
|
var channelAdditionalMessageModeSchema = exports_external.enum(["queue", "steer"]);
|
|
59988
|
+
var channelVerboseSchema = exports_external.enum(["off", "minimal"]);
|
|
59900
59989
|
var timezoneSchema = exports_external.string().refine(isValidLoopTimezone, {
|
|
59901
59990
|
message: "Expected a valid IANA timezone such as Asia/Ho_Chi_Minh"
|
|
59902
59991
|
});
|
|
@@ -59910,6 +59999,7 @@ var slackRouteSchema = exports_external.object({
|
|
|
59910
59999
|
response: slackResponseSchema.optional(),
|
|
59911
60000
|
responseMode: channelResponseModeSchema.optional(),
|
|
59912
60001
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60002
|
+
verbose: channelVerboseSchema.optional(),
|
|
59913
60003
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
59914
60004
|
timezone: timezoneSchema.optional()
|
|
59915
60005
|
});
|
|
@@ -59923,6 +60013,7 @@ var telegramTopicRouteSchema = exports_external.object({
|
|
|
59923
60013
|
response: slackResponseSchema.optional(),
|
|
59924
60014
|
responseMode: channelResponseModeSchema.optional(),
|
|
59925
60015
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60016
|
+
verbose: channelVerboseSchema.optional(),
|
|
59926
60017
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
59927
60018
|
timezone: timezoneSchema.optional()
|
|
59928
60019
|
});
|
|
@@ -59936,6 +60027,7 @@ var telegramGroupRouteSchema = exports_external.object({
|
|
|
59936
60027
|
response: slackResponseSchema.optional(),
|
|
59937
60028
|
responseMode: channelResponseModeSchema.optional(),
|
|
59938
60029
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60030
|
+
verbose: channelVerboseSchema.optional(),
|
|
59939
60031
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
59940
60032
|
timezone: timezoneSchema.optional(),
|
|
59941
60033
|
topics: exports_external.record(exports_external.string(), telegramTopicRouteSchema).default({})
|
|
@@ -59953,6 +60045,7 @@ var telegramDirectMessagesSchema = exports_external.object({
|
|
|
59953
60045
|
response: slackResponseSchema.optional(),
|
|
59954
60046
|
responseMode: channelResponseModeSchema.optional(),
|
|
59955
60047
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60048
|
+
verbose: channelVerboseSchema.optional(),
|
|
59956
60049
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
59957
60050
|
timezone: timezoneSchema.optional()
|
|
59958
60051
|
});
|
|
@@ -59992,6 +60085,7 @@ var telegramSchema = exports_external.object({
|
|
|
59992
60085
|
response: slackResponseSchema.default("final"),
|
|
59993
60086
|
responseMode: channelResponseModeSchema.default("message-tool"),
|
|
59994
60087
|
additionalMessageMode: channelAdditionalMessageModeSchema.default("steer"),
|
|
60088
|
+
verbose: channelVerboseSchema.default("minimal"),
|
|
59995
60089
|
followUp: slackFollowUpSchema.default({
|
|
59996
60090
|
mode: "auto",
|
|
59997
60091
|
participationTtlMin: 5
|
|
@@ -60022,6 +60116,7 @@ var directMessagesSchema = exports_external.object({
|
|
|
60022
60116
|
response: slackResponseSchema.optional(),
|
|
60023
60117
|
responseMode: channelResponseModeSchema.optional(),
|
|
60024
60118
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60119
|
+
verbose: channelVerboseSchema.optional(),
|
|
60025
60120
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
60026
60121
|
timezone: timezoneSchema.optional()
|
|
60027
60122
|
});
|
|
@@ -60068,6 +60163,7 @@ var slackSchema = exports_external.object({
|
|
|
60068
60163
|
response: slackResponseSchema.default("final"),
|
|
60069
60164
|
responseMode: channelResponseModeSchema.default("message-tool"),
|
|
60070
60165
|
additionalMessageMode: channelAdditionalMessageModeSchema.default("steer"),
|
|
60166
|
+
verbose: channelVerboseSchema.default("minimal"),
|
|
60071
60167
|
followUp: slackFollowUpSchema.default({
|
|
60072
60168
|
mode: "auto",
|
|
60073
60169
|
participationTtlMin: 5
|
|
@@ -60127,6 +60223,45 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
60127
60223
|
identityLinks: {},
|
|
60128
60224
|
storePath: "~/.clisbot/state/sessions.json"
|
|
60129
60225
|
}),
|
|
60226
|
+
app: exports_external.object({
|
|
60227
|
+
auth: appAuthSchema.default({
|
|
60228
|
+
ownerClaimWindowMinutes: 30,
|
|
60229
|
+
defaultRole: "member",
|
|
60230
|
+
roles: {
|
|
60231
|
+
owner: {
|
|
60232
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60233
|
+
users: []
|
|
60234
|
+
},
|
|
60235
|
+
admin: {
|
|
60236
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60237
|
+
users: []
|
|
60238
|
+
},
|
|
60239
|
+
member: {
|
|
60240
|
+
allow: [],
|
|
60241
|
+
users: []
|
|
60242
|
+
}
|
|
60243
|
+
}
|
|
60244
|
+
})
|
|
60245
|
+
}).default({
|
|
60246
|
+
auth: {
|
|
60247
|
+
ownerClaimWindowMinutes: 30,
|
|
60248
|
+
defaultRole: "member",
|
|
60249
|
+
roles: {
|
|
60250
|
+
owner: {
|
|
60251
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60252
|
+
users: []
|
|
60253
|
+
},
|
|
60254
|
+
admin: {
|
|
60255
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60256
|
+
users: []
|
|
60257
|
+
},
|
|
60258
|
+
member: {
|
|
60259
|
+
allow: [],
|
|
60260
|
+
users: []
|
|
60261
|
+
}
|
|
60262
|
+
}
|
|
60263
|
+
}
|
|
60264
|
+
}),
|
|
60130
60265
|
agents: exports_external.object({
|
|
60131
60266
|
defaults: agentDefaultsSchema,
|
|
60132
60267
|
list: exports_external.array(agentEntrySchema).default([
|
|
@@ -60179,6 +60314,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
60179
60314
|
response: "final",
|
|
60180
60315
|
responseMode: "message-tool",
|
|
60181
60316
|
additionalMessageMode: "steer",
|
|
60317
|
+
verbose: "minimal",
|
|
60182
60318
|
followUp: {
|
|
60183
60319
|
mode: "auto",
|
|
60184
60320
|
participationTtlMin: 5
|
|
@@ -60218,17 +60354,19 @@ function resolveMaxRuntimeMs(stream) {
|
|
|
60218
60354
|
defaultMinutes: 15
|
|
60219
60355
|
});
|
|
60220
60356
|
}
|
|
60221
|
-
async function loadConfig(configPath = getDefaultConfigPath()) {
|
|
60357
|
+
async function loadConfig(configPath = getDefaultConfigPath(), options = {}) {
|
|
60222
60358
|
const expandedConfigPath = expandHomePath(configPath);
|
|
60223
60359
|
const text = await readTextFile(expandedConfigPath);
|
|
60224
60360
|
const parsed = JSON.parse(text);
|
|
60361
|
+
assertNoLegacyPrivilegeCommands(parsed);
|
|
60225
60362
|
const withDynamicDefaults = clisbotConfigSchema.parse(applyDynamicPathDefaults(parsed));
|
|
60226
60363
|
const substituted = resolveConfigEnvVars(withDynamicDefaults, process.env, {
|
|
60227
60364
|
skipPaths: getCredentialSkipPaths(withDynamicDefaults)
|
|
60228
60365
|
});
|
|
60229
60366
|
const validated = clisbotConfigSchema.parse(substituted);
|
|
60230
60367
|
const materialized = materializeRuntimeChannelCredentials(validated, {
|
|
60231
|
-
env: process.env
|
|
60368
|
+
env: process.env,
|
|
60369
|
+
materializeChannels: options.materializeChannels
|
|
60232
60370
|
});
|
|
60233
60371
|
return materializeLoadedConfig(expandedConfigPath, materialized);
|
|
60234
60372
|
}
|
|
@@ -60236,6 +60374,7 @@ async function loadConfigWithoutEnvResolution(configPath = getDefaultConfigPath(
|
|
|
60236
60374
|
const expandedConfigPath = expandHomePath(configPath);
|
|
60237
60375
|
const text = await readTextFile(expandedConfigPath);
|
|
60238
60376
|
const parsed = JSON.parse(text);
|
|
60377
|
+
assertNoLegacyPrivilegeCommands(parsed);
|
|
60239
60378
|
const validated = clisbotConfigSchema.parse(applyDynamicPathDefaults(parsed));
|
|
60240
60379
|
return materializeLoadedConfig(expandedConfigPath, validated);
|
|
60241
60380
|
}
|
|
@@ -60298,6 +60437,21 @@ function applyDynamicPathDefaults(parsed, env = process.env) {
|
|
|
60298
60437
|
function isRecord2(value) {
|
|
60299
60438
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
60300
60439
|
}
|
|
60440
|
+
function assertNoLegacyPrivilegeCommands(value, path2 = "root") {
|
|
60441
|
+
if (Array.isArray(value)) {
|
|
60442
|
+
value.forEach((entry, index) => assertNoLegacyPrivilegeCommands(entry, `${path2}[${index}]`));
|
|
60443
|
+
return;
|
|
60444
|
+
}
|
|
60445
|
+
if (!isRecord2(value)) {
|
|
60446
|
+
return;
|
|
60447
|
+
}
|
|
60448
|
+
if (Object.prototype.hasOwnProperty.call(value, "privilegeCommands")) {
|
|
60449
|
+
throw new Error(`Unsupported config key at ${path2}.privilegeCommands. Move routed permissions to app.auth and agents.<id>.auth.`);
|
|
60450
|
+
}
|
|
60451
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
60452
|
+
assertNoLegacyPrivilegeCommands(entry, `${path2}.${key}`);
|
|
60453
|
+
}
|
|
60454
|
+
}
|
|
60301
60455
|
function getAgentEntry(config, agentId) {
|
|
60302
60456
|
return config.raw.agents.list.find((entry) => entry.id === agentId);
|
|
60303
60457
|
}
|
|
@@ -60334,9 +60488,42 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60334
60488
|
identityLinks: {},
|
|
60335
60489
|
storePath: sessionStorePath
|
|
60336
60490
|
},
|
|
60491
|
+
app: {
|
|
60492
|
+
auth: {
|
|
60493
|
+
ownerClaimWindowMinutes: 30,
|
|
60494
|
+
defaultRole: "member",
|
|
60495
|
+
roles: {
|
|
60496
|
+
owner: {
|
|
60497
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60498
|
+
users: []
|
|
60499
|
+
},
|
|
60500
|
+
admin: {
|
|
60501
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60502
|
+
users: []
|
|
60503
|
+
},
|
|
60504
|
+
member: {
|
|
60505
|
+
allow: [],
|
|
60506
|
+
users: []
|
|
60507
|
+
}
|
|
60508
|
+
}
|
|
60509
|
+
}
|
|
60510
|
+
},
|
|
60337
60511
|
agents: {
|
|
60338
60512
|
defaults: {
|
|
60339
60513
|
workspace: workspaceTemplate,
|
|
60514
|
+
auth: {
|
|
60515
|
+
defaultRole: "member",
|
|
60516
|
+
roles: {
|
|
60517
|
+
admin: {
|
|
60518
|
+
allow: [...DEFAULT_AGENT_ADMIN_PERMISSIONS],
|
|
60519
|
+
users: []
|
|
60520
|
+
},
|
|
60521
|
+
member: {
|
|
60522
|
+
allow: [...DEFAULT_AGENT_MEMBER_PERMISSIONS],
|
|
60523
|
+
users: []
|
|
60524
|
+
}
|
|
60525
|
+
}
|
|
60526
|
+
},
|
|
60340
60527
|
runner: {
|
|
60341
60528
|
command: "codex",
|
|
60342
60529
|
args: [
|
|
@@ -60347,6 +60534,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60347
60534
|
],
|
|
60348
60535
|
trustWorkspace: true,
|
|
60349
60536
|
startupDelayMs: 3000,
|
|
60537
|
+
startupReadyPattern: undefined,
|
|
60350
60538
|
promptSubmitDelayMs: 150,
|
|
60351
60539
|
sessionId: {
|
|
60352
60540
|
create: {
|
|
@@ -60435,10 +60623,6 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60435
60623
|
channelPolicy: "allowlist",
|
|
60436
60624
|
groupPolicy: "allowlist",
|
|
60437
60625
|
defaultAgentId: "default",
|
|
60438
|
-
privilegeCommands: {
|
|
60439
|
-
enabled: false,
|
|
60440
|
-
allowUsers: []
|
|
60441
|
-
},
|
|
60442
60626
|
commandPrefixes: {
|
|
60443
60627
|
slash: ["::", "\\"],
|
|
60444
60628
|
bash: ["!"]
|
|
@@ -60447,6 +60631,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60447
60631
|
response: "final",
|
|
60448
60632
|
responseMode: "message-tool",
|
|
60449
60633
|
additionalMessageMode: "steer",
|
|
60634
|
+
verbose: "minimal",
|
|
60450
60635
|
followUp: {
|
|
60451
60636
|
mode: "auto",
|
|
60452
60637
|
participationTtlMin: 5
|
|
@@ -60458,11 +60643,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60458
60643
|
policy: "pairing",
|
|
60459
60644
|
allowFrom: [],
|
|
60460
60645
|
requireMention: false,
|
|
60461
|
-
agentId: "default"
|
|
60462
|
-
privilegeCommands: {
|
|
60463
|
-
enabled: false,
|
|
60464
|
-
allowUsers: []
|
|
60465
|
-
}
|
|
60646
|
+
agentId: "default"
|
|
60466
60647
|
}
|
|
60467
60648
|
},
|
|
60468
60649
|
telegram: {
|
|
@@ -60483,10 +60664,6 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60483
60664
|
allowBots: false,
|
|
60484
60665
|
groupPolicy: "allowlist",
|
|
60485
60666
|
defaultAgentId: "default",
|
|
60486
|
-
privilegeCommands: {
|
|
60487
|
-
enabled: false,
|
|
60488
|
-
allowUsers: []
|
|
60489
|
-
},
|
|
60490
60667
|
commandPrefixes: {
|
|
60491
60668
|
slash: ["::", "\\"],
|
|
60492
60669
|
bash: ["!"]
|
|
@@ -60495,6 +60672,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60495
60672
|
response: "final",
|
|
60496
60673
|
responseMode: "message-tool",
|
|
60497
60674
|
additionalMessageMode: "steer",
|
|
60675
|
+
verbose: "minimal",
|
|
60498
60676
|
followUp: {
|
|
60499
60677
|
mode: "auto",
|
|
60500
60678
|
participationTtlMin: 5
|
|
@@ -60510,11 +60688,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60510
60688
|
allowFrom: [],
|
|
60511
60689
|
requireMention: false,
|
|
60512
60690
|
allowBots: false,
|
|
60513
|
-
agentId: "default"
|
|
60514
|
-
privilegeCommands: {
|
|
60515
|
-
enabled: false,
|
|
60516
|
-
allowUsers: []
|
|
60517
|
-
}
|
|
60691
|
+
agentId: "default"
|
|
60518
60692
|
}
|
|
60519
60693
|
}
|
|
60520
60694
|
}
|
|
@@ -60542,16 +60716,26 @@ async function readEditableConfig(configPath = getDefaultConfigPath()) {
|
|
|
60542
60716
|
async function writeEditableConfig(configPath, config) {
|
|
60543
60717
|
const expandedConfigPath = expandHomePath(configPath);
|
|
60544
60718
|
await ensureDir2(dirname4(expandedConfigPath));
|
|
60545
|
-
const nextConfig = {
|
|
60719
|
+
const nextConfig = stripLegacyPrivilegeCommands({
|
|
60546
60720
|
...config,
|
|
60547
60721
|
meta: {
|
|
60548
60722
|
...config.meta,
|
|
60549
60723
|
lastTouchedAt: new Date().toISOString()
|
|
60550
60724
|
}
|
|
60551
|
-
};
|
|
60725
|
+
});
|
|
60552
60726
|
await writeTextFile(expandedConfigPath, `${JSON.stringify(nextConfig, null, 2)}
|
|
60553
60727
|
`);
|
|
60554
60728
|
}
|
|
60729
|
+
function stripLegacyPrivilegeCommands(value) {
|
|
60730
|
+
if (Array.isArray(value)) {
|
|
60731
|
+
return value.map((entry) => stripLegacyPrivilegeCommands(entry));
|
|
60732
|
+
}
|
|
60733
|
+
if (!value || typeof value !== "object") {
|
|
60734
|
+
return value;
|
|
60735
|
+
}
|
|
60736
|
+
const nextEntries = Object.entries(value).filter(([key]) => key !== "privilegeCommands").map(([key, entry]) => [key, stripLegacyPrivilegeCommands(entry)]);
|
|
60737
|
+
return Object.fromEntries(nextEntries);
|
|
60738
|
+
}
|
|
60555
60739
|
|
|
60556
60740
|
// src/agents/bootstrap.ts
|
|
60557
60741
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
@@ -60575,7 +60759,8 @@ var CUSTOMIZED_TEMPLATE_DIR = join4(TEMPLATE_ROOT, "customized");
|
|
|
60575
60759
|
var CUSTOMIZED_DEFAULT_TEMPLATE_DIR = join4(CUSTOMIZED_TEMPLATE_DIR, "default");
|
|
60576
60760
|
var TOOL_BOOTSTRAP_FILE = {
|
|
60577
60761
|
codex: "AGENTS.md",
|
|
60578
|
-
claude: "CLAUDE.md"
|
|
60762
|
+
claude: "CLAUDE.md",
|
|
60763
|
+
gemini: "GEMINI.md"
|
|
60579
60764
|
};
|
|
60580
60765
|
function shouldIncludeTemplateFile(toolId, relativePath) {
|
|
60581
60766
|
const normalized = relativePath.replaceAll("\\", "/");
|
|
@@ -60585,6 +60770,9 @@ function shouldIncludeTemplateFile(toolId, relativePath) {
|
|
|
60585
60770
|
if (normalized.endsWith("CLAUDE.md")) {
|
|
60586
60771
|
return toolId === "claude";
|
|
60587
60772
|
}
|
|
60773
|
+
if (normalized.endsWith("GEMINI.md")) {
|
|
60774
|
+
return toolId === "gemini";
|
|
60775
|
+
}
|
|
60588
60776
|
return true;
|
|
60589
60777
|
}
|
|
60590
60778
|
function collectTemplateFiles(rootDir, toolId, prefix = "") {
|
|
@@ -60882,11 +61070,11 @@ async function addAgentToEditableConfig(params) {
|
|
|
60882
61070
|
async function addAgent(args) {
|
|
60883
61071
|
const agentId = args[0]?.trim();
|
|
60884
61072
|
if (!agentId) {
|
|
60885
|
-
throw new Error("Usage: agents add <id> --cli <codex|claude> [--workspace <path>] [--startup-option <arg>]... [--bootstrap <personal-assistant|team-assistant>] [--bind <channel[:accountId]>]...");
|
|
61073
|
+
throw new Error("Usage: agents add <id> --cli <codex|claude|gemini> [--workspace <path>] [--startup-option <arg>]... [--bootstrap <personal-assistant|team-assistant>] [--bind <channel[:accountId]>]...");
|
|
60886
61074
|
}
|
|
60887
61075
|
const cliTool = parseSingleOption(args, "--cli");
|
|
60888
61076
|
if (!cliTool || !(cliTool in DEFAULT_AGENT_TOOL_TEMPLATES)) {
|
|
60889
|
-
throw new Error("agents add requires --cli codex or --cli
|
|
61077
|
+
throw new Error("agents add requires --cli codex, --cli claude, or --cli gemini");
|
|
60890
61078
|
}
|
|
60891
61079
|
const workspace = parseSingleOption(args, "--workspace");
|
|
60892
61080
|
const startupOptions = parseRepeatedOption(args, "--startup-option");
|
|
@@ -61088,7 +61276,7 @@ async function runAgentsCli(args) {
|
|
|
61088
61276
|
|
|
61089
61277
|
// src/control/accounts-cli.ts
|
|
61090
61278
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
61091
|
-
import { existsSync as existsSync7, readFileSync as
|
|
61279
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4 } from "node:fs";
|
|
61092
61280
|
|
|
61093
61281
|
// src/config/channel-account-management.ts
|
|
61094
61282
|
function getFirstAccountId(accounts) {
|
|
@@ -61437,24 +61625,105 @@ class RuntimeHealthStore {
|
|
|
61437
61625
|
}
|
|
61438
61626
|
|
|
61439
61627
|
// src/control/runtime-process.ts
|
|
61440
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
61441
|
-
import { closeSync, existsSync as existsSync6, openSync, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
61442
|
-
import { dirname as
|
|
61628
|
+
import { execFileSync, spawn as spawn2 } from "node:child_process";
|
|
61629
|
+
import { closeSync, existsSync as existsSync6, openSync, readFileSync as readFileSync3, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
61630
|
+
import { dirname as dirname10 } from "node:path";
|
|
61443
61631
|
import { kill } from "node:process";
|
|
61444
61632
|
|
|
61445
61633
|
// src/control/clisbot-wrapper.ts
|
|
61446
61634
|
import { chmod } from "node:fs/promises";
|
|
61447
61635
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
61448
|
-
import { dirname as
|
|
61636
|
+
import { dirname as dirname8, join as join6, sep } from "node:path";
|
|
61637
|
+
|
|
61638
|
+
// src/control/runner-exit-diagnostics.ts
|
|
61639
|
+
import { unlink } from "node:fs/promises";
|
|
61640
|
+
import { dirname as dirname7, join as join5 } from "node:path";
|
|
61641
|
+
function shellQuote(value) {
|
|
61642
|
+
if (/^[a-zA-Z0-9_./:@=-]+$/.test(value)) {
|
|
61643
|
+
return value;
|
|
61644
|
+
}
|
|
61645
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
61646
|
+
}
|
|
61647
|
+
function buildCommandString(command, args) {
|
|
61648
|
+
return [command, ...args].map(shellQuote).join(" ");
|
|
61649
|
+
}
|
|
61650
|
+
function sanitizeSessionName2(sessionName) {
|
|
61651
|
+
return sessionName.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
61652
|
+
}
|
|
61653
|
+
function getRunnerExitRecordPath(stateDir, sessionName) {
|
|
61654
|
+
return join5(stateDir, "runner-exits", `${sanitizeSessionName2(sessionName)}.json`);
|
|
61655
|
+
}
|
|
61656
|
+
function buildRunnerLaunchCommand(params) {
|
|
61657
|
+
const runnerCommand = buildCommandString(params.command, params.args);
|
|
61658
|
+
const exitRecordPath = getRunnerExitRecordPath(params.stateDir, params.sessionName);
|
|
61659
|
+
const exitWriterScript = [
|
|
61660
|
+
"const fs = require('fs');",
|
|
61661
|
+
"const path = require('path');",
|
|
61662
|
+
"const filePath = process.argv[1];",
|
|
61663
|
+
"const sessionName = process.argv[2];",
|
|
61664
|
+
"const exitCode = Number(process.argv[3]);",
|
|
61665
|
+
"const command = process.argv[4];",
|
|
61666
|
+
"fs.mkdirSync(path.dirname(filePath), { recursive: true });",
|
|
61667
|
+
"fs.writeFileSync(filePath, JSON.stringify({ sessionName, exitCode, command, exitedAt: new Date().toISOString() }) + '\\n');"
|
|
61668
|
+
].join(" ");
|
|
61669
|
+
const exports = [
|
|
61670
|
+
`export PATH=${shellQuote(params.wrapperDir)}:"$PATH"`,
|
|
61671
|
+
`export CLISBOT_BIN=${shellQuote(params.wrapperPath)}`
|
|
61672
|
+
];
|
|
61673
|
+
return [
|
|
61674
|
+
...exports,
|
|
61675
|
+
`rm -f ${shellQuote(exitRecordPath)}`,
|
|
61676
|
+
runnerCommand,
|
|
61677
|
+
"status=$?",
|
|
61678
|
+
`node -e ${shellQuote(exitWriterScript)} ${shellQuote(exitRecordPath)} ${shellQuote(params.sessionName)} "$status" ${shellQuote(runnerCommand)} || true`,
|
|
61679
|
+
'exit "$status"'
|
|
61680
|
+
].join("; ");
|
|
61681
|
+
}
|
|
61682
|
+
async function clearRunnerExitRecord(stateDir, sessionName) {
|
|
61683
|
+
const exitRecordPath = getRunnerExitRecordPath(stateDir, sessionName);
|
|
61684
|
+
if (!await fileExists(exitRecordPath)) {
|
|
61685
|
+
return;
|
|
61686
|
+
}
|
|
61687
|
+
try {
|
|
61688
|
+
await unlink(exitRecordPath);
|
|
61689
|
+
} catch {}
|
|
61690
|
+
}
|
|
61691
|
+
async function readRunnerExitRecord(stateDir, sessionName) {
|
|
61692
|
+
const exitRecordPath = getRunnerExitRecordPath(stateDir, sessionName);
|
|
61693
|
+
if (!await fileExists(exitRecordPath)) {
|
|
61694
|
+
return null;
|
|
61695
|
+
}
|
|
61696
|
+
try {
|
|
61697
|
+
const text = await readTextFile(exitRecordPath);
|
|
61698
|
+
const parsed = JSON.parse(text);
|
|
61699
|
+
const exitCode = parsed.exitCode;
|
|
61700
|
+
if (typeof parsed.sessionName !== "string" || typeof exitCode !== "number" || !Number.isFinite(exitCode) || typeof parsed.command !== "string" || typeof parsed.exitedAt !== "string") {
|
|
61701
|
+
return null;
|
|
61702
|
+
}
|
|
61703
|
+
return {
|
|
61704
|
+
sessionName: parsed.sessionName,
|
|
61705
|
+
exitCode,
|
|
61706
|
+
command: parsed.command,
|
|
61707
|
+
exitedAt: parsed.exitedAt
|
|
61708
|
+
};
|
|
61709
|
+
} catch {
|
|
61710
|
+
return null;
|
|
61711
|
+
}
|
|
61712
|
+
}
|
|
61713
|
+
async function ensureRunnerExitRecordDir(stateDir, sessionName) {
|
|
61714
|
+
await ensureDir(dirname7(getRunnerExitRecordPath(stateDir, sessionName)));
|
|
61715
|
+
}
|
|
61716
|
+
|
|
61717
|
+
// src/control/clisbot-wrapper.ts
|
|
61449
61718
|
function getDefaultClisbotBinDir(env = process.env) {
|
|
61450
|
-
return
|
|
61719
|
+
return join6(resolveAppHomeDir(env), "bin");
|
|
61451
61720
|
}
|
|
61452
61721
|
function getDefaultClisbotWrapperPath(env = process.env) {
|
|
61453
|
-
return
|
|
61722
|
+
return join6(getDefaultClisbotBinDir(env), "clisbot");
|
|
61454
61723
|
}
|
|
61455
61724
|
var DEFAULT_CLISBOT_BIN_DIR = getDefaultClisbotBinDir();
|
|
61456
61725
|
var DEFAULT_CLISBOT_WRAPPER_PATH = getDefaultClisbotWrapperPath();
|
|
61457
|
-
function
|
|
61726
|
+
function shellQuote2(value) {
|
|
61458
61727
|
if (/^[a-zA-Z0-9_./:@=-]+$/.test(value)) {
|
|
61459
61728
|
return value;
|
|
61460
61729
|
}
|
|
@@ -61477,7 +61746,7 @@ function getClisbotPromptCommand() {
|
|
|
61477
61746
|
return isPackagedRuntime() ? "clis" : getClisbotWrapperPath();
|
|
61478
61747
|
}
|
|
61479
61748
|
function getClisbotWrapperDir() {
|
|
61480
|
-
return
|
|
61749
|
+
return dirname8(getClisbotWrapperPath());
|
|
61481
61750
|
}
|
|
61482
61751
|
function renderClisbotWrapperScript() {
|
|
61483
61752
|
const execPath = process.execPath;
|
|
@@ -61485,14 +61754,14 @@ function renderClisbotWrapperScript() {
|
|
|
61485
61754
|
return [
|
|
61486
61755
|
"#!/usr/bin/env bash",
|
|
61487
61756
|
"set -euo pipefail",
|
|
61488
|
-
`exec ${
|
|
61757
|
+
`exec ${shellQuote2(execPath)} ${shellQuote2(mainScriptPath)} "$@"`,
|
|
61489
61758
|
""
|
|
61490
61759
|
].join(`
|
|
61491
61760
|
`);
|
|
61492
61761
|
}
|
|
61493
61762
|
async function ensureClisbotWrapper() {
|
|
61494
61763
|
const wrapperPath = getClisbotWrapperPath();
|
|
61495
|
-
const wrapperDir =
|
|
61764
|
+
const wrapperDir = dirname8(wrapperPath);
|
|
61496
61765
|
await ensureDir2(wrapperDir);
|
|
61497
61766
|
const nextScript = renderClisbotWrapperScript();
|
|
61498
61767
|
const existing = await fileExists(wrapperPath) ? await readTextFile(wrapperPath) : null;
|
|
@@ -61509,7 +61778,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
|
|
|
61509
61778
|
// src/shared/process.ts
|
|
61510
61779
|
import { spawn } from "node:child_process";
|
|
61511
61780
|
import { existsSync as existsSync5 } from "node:fs";
|
|
61512
|
-
import { delimiter, join as
|
|
61781
|
+
import { delimiter, join as join7 } from "node:path";
|
|
61513
61782
|
function sleep(ms) {
|
|
61514
61783
|
return new Promise((resolve) => {
|
|
61515
61784
|
setTimeout(resolve, ms);
|
|
@@ -61527,7 +61796,7 @@ function commandExists(command) {
|
|
|
61527
61796
|
const executableNames = getExecutableNames(command);
|
|
61528
61797
|
for (const directory of pathEntries) {
|
|
61529
61798
|
for (const executableName of executableNames) {
|
|
61530
|
-
if (existsSync5(
|
|
61799
|
+
if (existsSync5(join7(directory, executableName))) {
|
|
61531
61800
|
return true;
|
|
61532
61801
|
}
|
|
61533
61802
|
}
|
|
@@ -61807,6 +62076,12 @@ class StartDetachedRuntimeError extends Error {
|
|
|
61807
62076
|
this.name = "StartDetachedRuntimeError";
|
|
61808
62077
|
}
|
|
61809
62078
|
}
|
|
62079
|
+
var DEFAULT_PROCESS_LIVENESS_DEPENDENCIES = {
|
|
62080
|
+
platform: process.platform,
|
|
62081
|
+
signalCheck: signalCheckProcess,
|
|
62082
|
+
readLinuxProcStat: readLinuxProcStatLiveness,
|
|
62083
|
+
readPsStat: readPsStatLiveness
|
|
62084
|
+
};
|
|
61810
62085
|
function readRuntimePid(pidPath) {
|
|
61811
62086
|
const expandedPidPath = resolvePidPath(pidPath);
|
|
61812
62087
|
if (!existsSync6(expandedPidPath)) {
|
|
@@ -61819,17 +62094,33 @@ function readRuntimePid(pidPath) {
|
|
|
61819
62094
|
});
|
|
61820
62095
|
}
|
|
61821
62096
|
function isProcessRunning(pid) {
|
|
61822
|
-
|
|
61823
|
-
|
|
61824
|
-
|
|
61825
|
-
|
|
61826
|
-
|
|
62097
|
+
return getProcessLiveness(pid) === "running";
|
|
62098
|
+
}
|
|
62099
|
+
function getProcessLiveness(pid, dependencies = {}) {
|
|
62100
|
+
const resolvedDependencies = {
|
|
62101
|
+
...DEFAULT_PROCESS_LIVENESS_DEPENDENCIES,
|
|
62102
|
+
...dependencies
|
|
62103
|
+
};
|
|
62104
|
+
if (!resolvedDependencies.signalCheck(pid)) {
|
|
62105
|
+
return "missing";
|
|
61827
62106
|
}
|
|
62107
|
+
if (resolvedDependencies.platform === "win32") {
|
|
62108
|
+
return "running";
|
|
62109
|
+
}
|
|
62110
|
+
const linuxState = resolvedDependencies.readLinuxProcStat(pid);
|
|
62111
|
+
if (linuxState !== "unknown") {
|
|
62112
|
+
return linuxState;
|
|
62113
|
+
}
|
|
62114
|
+
const psState = resolvedDependencies.readPsStat(pid);
|
|
62115
|
+
if (psState !== "unknown") {
|
|
62116
|
+
return psState;
|
|
62117
|
+
}
|
|
62118
|
+
return "running";
|
|
61828
62119
|
}
|
|
61829
62120
|
async function ensureConfigFile(configPath, options = {}) {
|
|
61830
62121
|
await ensureClisbotWrapper();
|
|
61831
62122
|
const expandedConfigPath = resolveConfigPath(configPath);
|
|
61832
|
-
await ensureDir2(
|
|
62123
|
+
await ensureDir2(dirname10(expandedConfigPath));
|
|
61833
62124
|
if (existsSync6(expandedConfigPath)) {
|
|
61834
62125
|
return {
|
|
61835
62126
|
configPath: expandedConfigPath,
|
|
@@ -61866,8 +62157,8 @@ async function startDetachedRuntime(params) {
|
|
|
61866
62157
|
rmSync2(pidPath, { force: true });
|
|
61867
62158
|
}
|
|
61868
62159
|
const configResult = await ensureConfigFile(params.configPath);
|
|
61869
|
-
await ensureDir2(
|
|
61870
|
-
await ensureDir2(
|
|
62160
|
+
await ensureDir2(dirname10(pidPath));
|
|
62161
|
+
await ensureDir2(dirname10(logPath));
|
|
61871
62162
|
const logStartOffset = getLogSize(logPath);
|
|
61872
62163
|
const logFd = openSync(logPath, "a");
|
|
61873
62164
|
const child = spawn2(process.execPath, [params.scriptPath, "serve-foreground"], {
|
|
@@ -61907,18 +62198,27 @@ async function startDetachedRuntime(params) {
|
|
|
61907
62198
|
logPath
|
|
61908
62199
|
};
|
|
61909
62200
|
}
|
|
61910
|
-
async function stopDetachedRuntime(params) {
|
|
62201
|
+
async function stopDetachedRuntime(params, dependencies = {}) {
|
|
61911
62202
|
const pidPath = resolvePidPath(params.pidPath);
|
|
61912
62203
|
const runtimeCredentialsPath = resolveRuntimeCredentialsPath(params.runtimeCredentialsPath);
|
|
61913
62204
|
const existingPid = await readRuntimePid(pidPath);
|
|
61914
62205
|
let stopped = false;
|
|
61915
|
-
|
|
61916
|
-
|
|
61917
|
-
|
|
62206
|
+
const processLiveness = dependencies.processLiveness ?? getProcessLiveness;
|
|
62207
|
+
const sendSignal = dependencies.sendSignal ?? kill;
|
|
62208
|
+
const sleepFn = dependencies.sleep ?? sleep;
|
|
62209
|
+
const existingLiveness = existingPid ? processLiveness(existingPid) : "missing";
|
|
62210
|
+
if (existingPid && existingLiveness === "running") {
|
|
62211
|
+
sendSignal(existingPid, "SIGTERM");
|
|
62212
|
+
const exited = await waitForProcessExit(existingPid, STOP_WAIT_TIMEOUT_MS, {
|
|
62213
|
+
processLiveness,
|
|
62214
|
+
sleep: sleepFn
|
|
62215
|
+
});
|
|
61918
62216
|
if (!exited) {
|
|
61919
62217
|
throw new Error(`clisbot did not stop within ${STOP_WAIT_TIMEOUT_MS}ms`);
|
|
61920
62218
|
}
|
|
61921
62219
|
stopped = true;
|
|
62220
|
+
} else if (existingPid && existingLiveness === "zombie") {
|
|
62221
|
+
stopped = true;
|
|
61922
62222
|
}
|
|
61923
62223
|
rmSync2(pidPath, { force: true });
|
|
61924
62224
|
removeRuntimeCredentials(runtimeCredentialsPath);
|
|
@@ -61948,7 +62248,7 @@ async function disableExpiredMemAccountsInConfig(configPath) {
|
|
|
61948
62248
|
}
|
|
61949
62249
|
async function writeRuntimePid(pidPath, pid = process.pid) {
|
|
61950
62250
|
const expandedPidPath = resolvePidPath(pidPath);
|
|
61951
|
-
await ensureDir2(
|
|
62251
|
+
await ensureDir2(dirname10(expandedPidPath));
|
|
61952
62252
|
await writeTextFile(expandedPidPath, `${pid}
|
|
61953
62253
|
`);
|
|
61954
62254
|
}
|
|
@@ -61960,9 +62260,10 @@ async function getRuntimeStatus(params = {}) {
|
|
|
61960
62260
|
const pidPath = resolvePidPath(params.pidPath);
|
|
61961
62261
|
const logPath = resolveLogPath(params.logPath);
|
|
61962
62262
|
const pid = await readRuntimePid(pidPath);
|
|
62263
|
+
const liveness = pid ? getProcessLiveness(pid) : "missing";
|
|
61963
62264
|
return {
|
|
61964
|
-
running:
|
|
61965
|
-
pid:
|
|
62265
|
+
running: liveness === "running",
|
|
62266
|
+
pid: liveness === "running" && pid ? pid : undefined,
|
|
61966
62267
|
configPath,
|
|
61967
62268
|
pidPath,
|
|
61968
62269
|
logPath,
|
|
@@ -62032,15 +62333,17 @@ async function waitForStart(params) {
|
|
|
62032
62333
|
childPid: params.childPid
|
|
62033
62334
|
};
|
|
62034
62335
|
}
|
|
62035
|
-
async function waitForProcessExit(pid, timeoutMs) {
|
|
62336
|
+
async function waitForProcessExit(pid, timeoutMs, dependencies = {}) {
|
|
62337
|
+
const processLiveness = dependencies.processLiveness ?? getProcessLiveness;
|
|
62338
|
+
const sleepFn = dependencies.sleep ?? sleep;
|
|
62036
62339
|
const deadline = Date.now() + timeoutMs;
|
|
62037
62340
|
while (Date.now() < deadline) {
|
|
62038
|
-
if (
|
|
62341
|
+
if (processLiveness(pid) !== "running") {
|
|
62039
62342
|
return true;
|
|
62040
62343
|
}
|
|
62041
|
-
await
|
|
62344
|
+
await sleepFn(PROCESS_POLL_INTERVAL_MS);
|
|
62042
62345
|
}
|
|
62043
|
-
return
|
|
62346
|
+
return processLiveness(pid) !== "running";
|
|
62044
62347
|
}
|
|
62045
62348
|
async function cleanupFailedStartChild(result) {
|
|
62046
62349
|
if (result.reason === "child-exited-before-pid") {
|
|
@@ -62087,6 +62390,66 @@ async function resolveTmuxSocketPath(configPath) {
|
|
|
62087
62390
|
}
|
|
62088
62391
|
return getDefaultTmuxSocketPath();
|
|
62089
62392
|
}
|
|
62393
|
+
function signalCheckProcess(pid) {
|
|
62394
|
+
try {
|
|
62395
|
+
kill(pid, 0);
|
|
62396
|
+
return true;
|
|
62397
|
+
} catch {
|
|
62398
|
+
return false;
|
|
62399
|
+
}
|
|
62400
|
+
}
|
|
62401
|
+
function readLinuxProcStatLiveness(pid) {
|
|
62402
|
+
if (process.platform !== "linux") {
|
|
62403
|
+
return "unknown";
|
|
62404
|
+
}
|
|
62405
|
+
try {
|
|
62406
|
+
const raw = readFileSync3(`/proc/${pid}/stat`, "utf8");
|
|
62407
|
+
const state = extractLinuxProcState(raw);
|
|
62408
|
+
if (!state) {
|
|
62409
|
+
return "unknown";
|
|
62410
|
+
}
|
|
62411
|
+
return state.includes("Z") ? "zombie" : "running";
|
|
62412
|
+
} catch (error) {
|
|
62413
|
+
const code = error.code;
|
|
62414
|
+
if (code === "ENOENT") {
|
|
62415
|
+
return "missing";
|
|
62416
|
+
}
|
|
62417
|
+
return "unknown";
|
|
62418
|
+
}
|
|
62419
|
+
}
|
|
62420
|
+
function readPsStatLiveness(pid) {
|
|
62421
|
+
try {
|
|
62422
|
+
const raw = execFileSync("ps", ["-o", "stat=", "-p", String(pid)], {
|
|
62423
|
+
encoding: "utf8",
|
|
62424
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
62425
|
+
}).trim();
|
|
62426
|
+
if (!raw) {
|
|
62427
|
+
return "missing";
|
|
62428
|
+
}
|
|
62429
|
+
return raw.includes("Z") ? "zombie" : "running";
|
|
62430
|
+
} catch (error) {
|
|
62431
|
+
const commandError = error;
|
|
62432
|
+
if (commandError.code === "ENOENT") {
|
|
62433
|
+
return "unknown";
|
|
62434
|
+
}
|
|
62435
|
+
if (commandError.status === 1) {
|
|
62436
|
+
return "missing";
|
|
62437
|
+
}
|
|
62438
|
+
return "unknown";
|
|
62439
|
+
}
|
|
62440
|
+
}
|
|
62441
|
+
function extractLinuxProcState(raw) {
|
|
62442
|
+
const closingParenIndex = raw.lastIndexOf(")");
|
|
62443
|
+
if (closingParenIndex < 0) {
|
|
62444
|
+
return null;
|
|
62445
|
+
}
|
|
62446
|
+
const remainder = raw.slice(closingParenIndex + 1).trim();
|
|
62447
|
+
if (!remainder) {
|
|
62448
|
+
return null;
|
|
62449
|
+
}
|
|
62450
|
+
const [state] = remainder.split(/\s+/, 1);
|
|
62451
|
+
return state?.trim() || null;
|
|
62452
|
+
}
|
|
62090
62453
|
|
|
62091
62454
|
// src/control/accounts-cli.ts
|
|
62092
62455
|
function getEditableConfigPath2() {
|
|
@@ -62111,7 +62474,7 @@ function readRuntimeCredentialDocument() {
|
|
|
62111
62474
|
if (!existsSync7(path2)) {
|
|
62112
62475
|
return {};
|
|
62113
62476
|
}
|
|
62114
|
-
const text =
|
|
62477
|
+
const text = readFileSync4(path2, "utf8").trim();
|
|
62115
62478
|
return text ? JSON.parse(text) : {};
|
|
62116
62479
|
}
|
|
62117
62480
|
async function waitForReloadResult(configPath, deps, timeoutMs = 12000) {
|
|
@@ -62506,7 +62869,13 @@ function buildConfiguredTargetFromIdentity(identity) {
|
|
|
62506
62869
|
};
|
|
62507
62870
|
}
|
|
62508
62871
|
function renderFieldLabel(field) {
|
|
62509
|
-
|
|
62872
|
+
if (field === "responseMode") {
|
|
62873
|
+
return "response-mode";
|
|
62874
|
+
}
|
|
62875
|
+
if (field === "additionalMessageMode") {
|
|
62876
|
+
return "additional-message-mode";
|
|
62877
|
+
}
|
|
62878
|
+
return "streaming";
|
|
62510
62879
|
}
|
|
62511
62880
|
|
|
62512
62881
|
// src/channels/additional-message-mode-config.ts
|
|
@@ -62598,218 +62967,22 @@ async function setConfiguredResponseMode(params) {
|
|
|
62598
62967
|
}
|
|
62599
62968
|
|
|
62600
62969
|
// src/control/channel-privilege-cli.ts
|
|
62601
|
-
function
|
|
62602
|
-
return process.env.CLISBOT_CONFIG_PATH;
|
|
62603
|
-
}
|
|
62604
|
-
function parseTarget(raw) {
|
|
62605
|
-
if (raw === "slack-dm" || raw === "slack-channel" || raw === "slack-group" || raw === "telegram-dm" || raw === "telegram-group") {
|
|
62606
|
-
return raw;
|
|
62607
|
-
}
|
|
62608
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62609
|
-
}
|
|
62610
|
-
function parseOptionValue2(args, name) {
|
|
62611
|
-
const index = args.findIndex((arg) => arg === name);
|
|
62612
|
-
if (index === -1) {
|
|
62613
|
-
return;
|
|
62614
|
-
}
|
|
62615
|
-
const value = args[index + 1]?.trim();
|
|
62616
|
-
if (!value) {
|
|
62617
|
-
throw new Error(`Missing value for ${name}`);
|
|
62618
|
-
}
|
|
62619
|
-
return value;
|
|
62620
|
-
}
|
|
62621
|
-
function ensureAllowUsersList(value) {
|
|
62622
|
-
return {
|
|
62623
|
-
enabled: value?.enabled ?? false,
|
|
62624
|
-
allowUsers: value?.allowUsers ?? []
|
|
62625
|
-
};
|
|
62626
|
-
}
|
|
62627
|
-
function renderPrivilegeCliHelp() {
|
|
62970
|
+
function renderChannelPrivilegeCliRemovedMessage() {
|
|
62628
62971
|
return [
|
|
62629
|
-
"
|
|
62630
|
-
"
|
|
62631
|
-
"
|
|
62632
|
-
" clisbot channels privilege allow-user slack-dm <userId>",
|
|
62633
|
-
" clisbot channels privilege remove-user slack-dm <userId>",
|
|
62634
|
-
" clisbot channels privilege enable slack-channel <channelId>",
|
|
62635
|
-
" clisbot channels privilege allow-user slack-channel <channelId> <userId>",
|
|
62636
|
-
" clisbot channels privilege enable slack-group <groupId>",
|
|
62637
|
-
" clisbot channels privilege allow-user slack-group <groupId> <userId>",
|
|
62638
|
-
" clisbot channels privilege enable telegram-dm",
|
|
62639
|
-
" clisbot channels privilege allow-user telegram-dm <userId>",
|
|
62640
|
-
" clisbot channels privilege enable telegram-group <chatId> [--topic <topicId>]",
|
|
62641
|
-
" clisbot channels privilege allow-user telegram-group <chatId> <userId> [--topic <topicId>]"
|
|
62972
|
+
"`clisbot channels privilege` has been removed.",
|
|
62973
|
+
"Manage routed permissions through `app.auth` and `agents.<id>.auth` instead.",
|
|
62974
|
+
"Grant `shellExecute` on the target agent role when `/bash` should be allowed."
|
|
62642
62975
|
].join(`
|
|
62643
62976
|
`);
|
|
62644
62977
|
}
|
|
62645
|
-
function
|
|
62646
|
-
|
|
62647
|
-
return normalized && !users.includes(normalized) ? [...users, normalized] : users;
|
|
62978
|
+
async function runChannelPrivilegeCli(_args) {
|
|
62979
|
+
throw new Error(renderChannelPrivilegeCliRemovedMessage());
|
|
62648
62980
|
}
|
|
62649
|
-
|
|
62650
|
-
|
|
62651
|
-
|
|
62652
|
-
|
|
62653
|
-
|
|
62654
|
-
const action = args[0];
|
|
62655
|
-
const target = parseTarget(args[1]);
|
|
62656
|
-
const rest = args.slice(2);
|
|
62657
|
-
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
62658
|
-
if (target === "slack-dm") {
|
|
62659
|
-
const current2 = ensureAllowUsersList(config.channels.slack.directMessages.privilegeCommands);
|
|
62660
|
-
await applyPrivilegeAction({
|
|
62661
|
-
action,
|
|
62662
|
-
current: current2,
|
|
62663
|
-
args: rest,
|
|
62664
|
-
set: (next) => {
|
|
62665
|
-
config.channels.slack.directMessages.privilegeCommands = next;
|
|
62666
|
-
},
|
|
62667
|
-
configPath,
|
|
62668
|
-
label: "slack direct messages",
|
|
62669
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62670
|
-
});
|
|
62671
|
-
return;
|
|
62672
|
-
}
|
|
62673
|
-
if (target === "telegram-dm") {
|
|
62674
|
-
const current2 = ensureAllowUsersList(config.channels.telegram.directMessages.privilegeCommands);
|
|
62675
|
-
await applyPrivilegeAction({
|
|
62676
|
-
action,
|
|
62677
|
-
current: current2,
|
|
62678
|
-
args: rest,
|
|
62679
|
-
set: (next) => {
|
|
62680
|
-
config.channels.telegram.directMessages.privilegeCommands = next;
|
|
62681
|
-
},
|
|
62682
|
-
configPath,
|
|
62683
|
-
label: "telegram direct messages",
|
|
62684
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62685
|
-
});
|
|
62686
|
-
return;
|
|
62687
|
-
}
|
|
62688
|
-
if (target === "slack-channel" || target === "slack-group") {
|
|
62689
|
-
const routeId = rest[0]?.trim();
|
|
62690
|
-
if (!routeId) {
|
|
62691
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62692
|
-
}
|
|
62693
|
-
const routes = target === "slack-channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
62694
|
-
const route = routes[routeId];
|
|
62695
|
-
if (!route) {
|
|
62696
|
-
throw new Error(`Route not configured yet: ${target} ${routeId}. Add the route first with \`clisbot channels add ...\`.`);
|
|
62697
|
-
}
|
|
62698
|
-
const current2 = ensureAllowUsersList(route.privilegeCommands);
|
|
62699
|
-
await applyPrivilegeAction({
|
|
62700
|
-
action,
|
|
62701
|
-
current: current2,
|
|
62702
|
-
args: rest.slice(1),
|
|
62703
|
-
set: (next) => {
|
|
62704
|
-
route.privilegeCommands = next;
|
|
62705
|
-
},
|
|
62706
|
-
configPath,
|
|
62707
|
-
label: `${target} ${routeId}`,
|
|
62708
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62709
|
-
});
|
|
62710
|
-
return;
|
|
62711
|
-
}
|
|
62712
|
-
const chatId = rest[0]?.trim();
|
|
62713
|
-
if (!chatId) {
|
|
62714
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62715
|
-
}
|
|
62716
|
-
const topicId = parseOptionValue2(rest, "--topic");
|
|
62717
|
-
const group = config.channels.telegram.groups[chatId];
|
|
62718
|
-
if (!group) {
|
|
62719
|
-
throw new Error(renderTelegramRouteChoiceMessage({ chatId }));
|
|
62720
|
-
}
|
|
62721
|
-
if (topicId) {
|
|
62722
|
-
const topic = group.topics?.[topicId];
|
|
62723
|
-
if (!topic) {
|
|
62724
|
-
throw new Error(renderTelegramRouteChoiceMessage({ chatId, topicId }));
|
|
62725
|
-
}
|
|
62726
|
-
const current2 = ensureAllowUsersList(topic.privilegeCommands);
|
|
62727
|
-
await applyPrivilegeAction({
|
|
62728
|
-
action,
|
|
62729
|
-
current: current2,
|
|
62730
|
-
args: rest.filter((value, index) => {
|
|
62731
|
-
if (index === 0) {
|
|
62732
|
-
return false;
|
|
62733
|
-
}
|
|
62734
|
-
return value !== "--topic" && value !== topicId;
|
|
62735
|
-
}),
|
|
62736
|
-
set: (next) => {
|
|
62737
|
-
topic.privilegeCommands = next;
|
|
62738
|
-
},
|
|
62739
|
-
configPath,
|
|
62740
|
-
label: `telegram topic ${chatId}/${topicId}`,
|
|
62741
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62742
|
-
});
|
|
62743
|
-
return;
|
|
62744
|
-
}
|
|
62745
|
-
const current = ensureAllowUsersList(group.privilegeCommands);
|
|
62746
|
-
await applyPrivilegeAction({
|
|
62747
|
-
action,
|
|
62748
|
-
current,
|
|
62749
|
-
args: rest.slice(1),
|
|
62750
|
-
set: (next) => {
|
|
62751
|
-
group.privilegeCommands = next;
|
|
62752
|
-
},
|
|
62753
|
-
configPath,
|
|
62754
|
-
label: `telegram group ${chatId}`,
|
|
62755
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62756
|
-
});
|
|
62757
|
-
}
|
|
62758
|
-
async function applyPrivilegeAction(params) {
|
|
62759
|
-
if (params.action === "enable") {
|
|
62760
|
-
params.set({
|
|
62761
|
-
enabled: true,
|
|
62762
|
-
allowUsers: params.current.allowUsers
|
|
62763
|
-
});
|
|
62764
|
-
await params.save();
|
|
62765
|
-
console.log(`enabled privilege commands for ${params.label}`);
|
|
62766
|
-
console.log(`config: ${params.configPath}`);
|
|
62767
|
-
return;
|
|
62768
|
-
}
|
|
62769
|
-
if (params.action === "disable") {
|
|
62770
|
-
params.set({
|
|
62771
|
-
enabled: false,
|
|
62772
|
-
allowUsers: params.current.allowUsers
|
|
62773
|
-
});
|
|
62774
|
-
await params.save();
|
|
62775
|
-
console.log(`disabled privilege commands for ${params.label}`);
|
|
62776
|
-
console.log(`config: ${params.configPath}`);
|
|
62777
|
-
return;
|
|
62778
|
-
}
|
|
62779
|
-
if (params.action === "allow-user") {
|
|
62780
|
-
const userId = params.args[0]?.trim();
|
|
62781
|
-
if (!userId) {
|
|
62782
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62783
|
-
}
|
|
62784
|
-
params.set({
|
|
62785
|
-
enabled: params.current.enabled,
|
|
62786
|
-
allowUsers: addUniqueUser(params.current.allowUsers, userId)
|
|
62787
|
-
});
|
|
62788
|
-
await params.save();
|
|
62789
|
-
console.log(`allowed ${userId} to use privilege commands for ${params.label}`);
|
|
62790
|
-
console.log(`config: ${params.configPath}`);
|
|
62791
|
-
return;
|
|
62792
|
-
}
|
|
62793
|
-
if (params.action === "remove-user") {
|
|
62794
|
-
const userId = params.args[0]?.trim();
|
|
62795
|
-
if (!userId) {
|
|
62796
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62797
|
-
}
|
|
62798
|
-
params.set({
|
|
62799
|
-
enabled: params.current.enabled,
|
|
62800
|
-
allowUsers: removeUser(params.current.allowUsers, userId)
|
|
62801
|
-
});
|
|
62802
|
-
await params.save();
|
|
62803
|
-
console.log(`removed ${userId} from privilege commands for ${params.label}`);
|
|
62804
|
-
console.log(`config: ${params.configPath}`);
|
|
62805
|
-
return;
|
|
62806
|
-
}
|
|
62807
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62808
|
-
}
|
|
62809
|
-
|
|
62810
|
-
// src/control/channels-cli.ts
|
|
62811
|
-
function getEditableConfigPath6() {
|
|
62812
|
-
return process.env.CLISBOT_CONFIG_PATH;
|
|
62981
|
+
|
|
62982
|
+
// src/control/channels-cli.ts
|
|
62983
|
+
var AUTH_USER_GUIDE_DOC_PATH = "docs/user-guide/auth-and-roles.md";
|
|
62984
|
+
function getEditableConfigPath5() {
|
|
62985
|
+
return process.env.CLISBOT_CONFIG_PATH;
|
|
62813
62986
|
}
|
|
62814
62987
|
function renderChannelsHelp() {
|
|
62815
62988
|
return [
|
|
@@ -62826,7 +62999,6 @@ function renderChannelsHelp() {
|
|
|
62826
62999
|
" clisbot channels remove slack-channel <channelId>",
|
|
62827
63000
|
" clisbot channels add slack-group <groupId> [--agent <id>] [--require-mention true|false]",
|
|
62828
63001
|
" clisbot channels remove slack-group <groupId>",
|
|
62829
|
-
" clisbot channels privilege <enable|disable|allow-user|remove-user> <target> ...",
|
|
62830
63002
|
" clisbot channels response-mode status --channel <slack|telegram> [--target <target>] [--topic <topicId>]",
|
|
62831
63003
|
" clisbot channels response-mode set <capture-pane|message-tool> --channel <slack|telegram> [--target <target>] [--topic <topicId>]",
|
|
62832
63004
|
" clisbot channels additional-message-mode status --channel <slack|telegram> [--target <target>] [--topic <topicId>]",
|
|
@@ -62842,7 +63014,8 @@ function renderChannelsHelp() {
|
|
|
62842
63014
|
" - Telegram groups need channels.telegram.groups.<chatId>",
|
|
62843
63015
|
" - Telegram forum topics need channels.telegram.groups.<chatId>.topics.<topicId>",
|
|
62844
63016
|
" - Adding a route puts that surface on the allowlist; other channels, groups, or topics still need to be added explicitly",
|
|
62845
|
-
" - Tune route settings such as requireMention
|
|
63017
|
+
" - Tune route settings such as requireMention and followUp in clisbot.json when a surface should behave differently",
|
|
63018
|
+
` - Manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`,
|
|
62846
63019
|
" - Response delivery can be tuned with responseMode: `capture-pane` or `message-tool`",
|
|
62847
63020
|
" - Busy-session follow-up can be tuned with additionalMessageMode: `steer` or `queue`",
|
|
62848
63021
|
" - Slack response-mode targets use `channel:<id>`, `group:<id>`, or `dm:<id>`",
|
|
@@ -62875,7 +63048,6 @@ function renderChannelsHelp() {
|
|
|
62875
63048
|
"Next steps:",
|
|
62876
63049
|
" - Run `clisbot status` to inspect routes and current channel state",
|
|
62877
63050
|
" - Run `clisbot logs` if the bot is still not responding",
|
|
62878
|
-
...renderGenericPrivilegeCommandHelpLines(),
|
|
62879
63051
|
...renderChannelSetupHelpLines("", { includePrivilegeHelp: false })
|
|
62880
63052
|
].join(`
|
|
62881
63053
|
`);
|
|
@@ -62930,7 +63102,7 @@ function parseResponseModeTarget(channel, raw) {
|
|
|
62930
63102
|
}
|
|
62931
63103
|
return target;
|
|
62932
63104
|
}
|
|
62933
|
-
function
|
|
63105
|
+
function parseOptionValue2(args, name) {
|
|
62934
63106
|
const index = args.findIndex((arg) => arg === name);
|
|
62935
63107
|
if (index === -1) {
|
|
62936
63108
|
return;
|
|
@@ -62942,7 +63114,7 @@ function parseOptionValue3(args, name) {
|
|
|
62942
63114
|
return value;
|
|
62943
63115
|
}
|
|
62944
63116
|
function parseBooleanOption(args, name, fallback) {
|
|
62945
|
-
const raw =
|
|
63117
|
+
const raw = parseOptionValue2(args, name);
|
|
62946
63118
|
if (!raw) {
|
|
62947
63119
|
return fallback;
|
|
62948
63120
|
}
|
|
@@ -62955,10 +63127,10 @@ function parseBooleanOption(args, name, fallback) {
|
|
|
62955
63127
|
throw new Error(`${name} requires true or false`);
|
|
62956
63128
|
}
|
|
62957
63129
|
function getAgentId(args) {
|
|
62958
|
-
return
|
|
63130
|
+
return parseOptionValue2(args, "--agent") ?? "default";
|
|
62959
63131
|
}
|
|
62960
63132
|
async function setChannelEnabled(action, channel) {
|
|
62961
|
-
const { config, configPath } = await readEditableConfig(
|
|
63133
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
62962
63134
|
const enabled = action === "enable";
|
|
62963
63135
|
const current = config.channels[channel].enabled;
|
|
62964
63136
|
if (current === enabled) {
|
|
@@ -62978,8 +63150,8 @@ async function addTelegramGroup(args) {
|
|
|
62978
63150
|
if (!chatId) {
|
|
62979
63151
|
throw new Error("Usage: clisbot channels add telegram-group <chatId> [--topic <topicId>] [--agent <id>] [--require-mention true|false]");
|
|
62980
63152
|
}
|
|
62981
|
-
const { config, configPath } = await readEditableConfig(
|
|
62982
|
-
const topicId =
|
|
63153
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63154
|
+
const topicId = parseOptionValue2(args, "--topic");
|
|
62983
63155
|
const agentId = getAgentId(args);
|
|
62984
63156
|
const requireMention = parseBooleanOption(args, "--require-mention", true);
|
|
62985
63157
|
const groupRoute = config.channels.telegram.groups[chatId] ?? {
|
|
@@ -63031,8 +63203,8 @@ async function removeTelegramGroup(args) {
|
|
|
63031
63203
|
if (!chatId) {
|
|
63032
63204
|
throw new Error("Usage: clisbot channels remove telegram-group <chatId> [--topic <topicId>]");
|
|
63033
63205
|
}
|
|
63034
|
-
const { config, configPath } = await readEditableConfig(
|
|
63035
|
-
const topicId =
|
|
63206
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63207
|
+
const topicId = parseOptionValue2(args, "--topic");
|
|
63036
63208
|
const groupRoute = config.channels.telegram.groups[chatId];
|
|
63037
63209
|
if (!groupRoute) {
|
|
63038
63210
|
console.log(`telegram group route ${chatId} is not configured`);
|
|
@@ -63061,7 +63233,7 @@ async function addSlackRoute(kind, args) {
|
|
|
63061
63233
|
if (!routeId) {
|
|
63062
63234
|
throw new Error(`Usage: clisbot channels add slack-${kind} <${kind}Id> [--agent <id>] [--require-mention true|false]`);
|
|
63063
63235
|
}
|
|
63064
|
-
const { config, configPath } = await readEditableConfig(
|
|
63236
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63065
63237
|
const agentId = getAgentId(args);
|
|
63066
63238
|
const requireMention = parseBooleanOption(args, "--require-mention", false);
|
|
63067
63239
|
const target = kind === "channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
@@ -63085,7 +63257,7 @@ async function removeSlackRoute(kind, args) {
|
|
|
63085
63257
|
if (!routeId) {
|
|
63086
63258
|
throw new Error(`Usage: clisbot channels remove slack-${kind} <${kind}Id>`);
|
|
63087
63259
|
}
|
|
63088
|
-
const { config, configPath } = await readEditableConfig(
|
|
63260
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63089
63261
|
const target = kind === "channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
63090
63262
|
if (!target[routeId]) {
|
|
63091
63263
|
console.log(`slack ${kind} route ${routeId} is not configured`);
|
|
@@ -63098,7 +63270,7 @@ async function removeSlackRoute(kind, args) {
|
|
|
63098
63270
|
console.log(`config: ${configPath}`);
|
|
63099
63271
|
}
|
|
63100
63272
|
async function setToken(target, value) {
|
|
63101
|
-
const { config, configPath } = await readEditableConfig(
|
|
63273
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63102
63274
|
if (target === "slack-app") {
|
|
63103
63275
|
config.channels.slack.appToken = value;
|
|
63104
63276
|
const defaultAccountId = config.channels.slack.defaultAccount || "default";
|
|
@@ -63130,7 +63302,7 @@ async function setToken(target, value) {
|
|
|
63130
63302
|
console.log(`config: ${configPath}`);
|
|
63131
63303
|
}
|
|
63132
63304
|
async function clearToken(target) {
|
|
63133
|
-
const { config, configPath } = await readEditableConfig(
|
|
63305
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63134
63306
|
if (target === "slack-app") {
|
|
63135
63307
|
config.channels.slack.appToken = "";
|
|
63136
63308
|
const defaultAccountId = config.channels.slack.defaultAccount || "default";
|
|
@@ -63174,9 +63346,9 @@ async function runResponseModeCli(args) {
|
|
|
63174
63346
|
}
|
|
63175
63347
|
const responseMode = action === "set" ? parseResponseMode2(args[1]) : undefined;
|
|
63176
63348
|
const optionArgs = action === "set" ? args.slice(2) : args.slice(1);
|
|
63177
|
-
const channel = parseResponseModeChannel(
|
|
63178
|
-
const target = parseResponseModeTarget(channel,
|
|
63179
|
-
const topic =
|
|
63349
|
+
const channel = parseResponseModeChannel(parseOptionValue2(optionArgs, "--channel"));
|
|
63350
|
+
const target = parseResponseModeTarget(channel, parseOptionValue2(optionArgs, "--target"));
|
|
63351
|
+
const topic = parseOptionValue2(optionArgs, "--topic");
|
|
63180
63352
|
if (channel === "slack" && topic) {
|
|
63181
63353
|
throw new Error("Slack response-mode commands do not support --topic");
|
|
63182
63354
|
}
|
|
@@ -63208,9 +63380,9 @@ async function runAdditionalMessageModeCli(args) {
|
|
|
63208
63380
|
}
|
|
63209
63381
|
const additionalMessageMode = action === "set" ? parseAdditionalMessageMode2(args[1]) : undefined;
|
|
63210
63382
|
const optionArgs = action === "set" ? args.slice(2) : args.slice(1);
|
|
63211
|
-
const channel = parseResponseModeChannel(
|
|
63212
|
-
const target = parseResponseModeTarget(channel,
|
|
63213
|
-
const topic =
|
|
63383
|
+
const channel = parseResponseModeChannel(parseOptionValue2(optionArgs, "--channel"));
|
|
63384
|
+
const target = parseResponseModeTarget(channel, parseOptionValue2(optionArgs, "--target"));
|
|
63385
|
+
const topic = parseOptionValue2(optionArgs, "--topic");
|
|
63214
63386
|
if (channel === "slack" && topic) {
|
|
63215
63387
|
throw new Error("Slack additional-message-mode commands do not support --topic");
|
|
63216
63388
|
}
|
|
@@ -63273,16 +63445,10 @@ function renderRouteAddGuidance(params) {
|
|
|
63273
63445
|
console.log("Slack route next steps:");
|
|
63274
63446
|
console.log(` - route added: ${routePath}`);
|
|
63275
63447
|
console.log(" - direct messages still follow channels.slack.directMessages.policy (`open`, `pairing`, `allowlist`, or `disabled`)");
|
|
63276
|
-
console.log(` - this ${routeLabel} still follows channels.slack.groupPolicy and route settings such as requireMention
|
|
63448
|
+
console.log(` - this ${routeLabel} still follows channels.slack.groupPolicy and route settings such as requireMention and followUp`);
|
|
63277
63449
|
console.log(" - if you want pairing-style access control for DMs, set channels.slack.directMessages.policy to `pairing`");
|
|
63278
63450
|
console.log(" - if you want stricter route access, keep Slack groups on allowlist and only add the channels/groups you trust");
|
|
63279
|
-
|
|
63280
|
-
platform: "slack",
|
|
63281
|
-
conversationKind: params.kind === "group" ? "group" : "channel",
|
|
63282
|
-
channelId: params.routeId
|
|
63283
|
-
}, " ")) {
|
|
63284
|
-
console.log(line);
|
|
63285
|
-
}
|
|
63451
|
+
console.log(` - manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`);
|
|
63286
63452
|
} else {
|
|
63287
63453
|
const [chatId, topicId] = params.routeId.split("/");
|
|
63288
63454
|
const routePath = params.kind === "topic" ? `channels.telegram.groups."${chatId}".topics."${topicId}"` : `channels.telegram.groups."${params.routeId}"`;
|
|
@@ -63291,15 +63457,8 @@ function renderRouteAddGuidance(params) {
|
|
|
63291
63457
|
console.log(" - direct messages still follow channels.telegram.directMessages.policy (`open`, `pairing`, `allowlist`, or `disabled`)");
|
|
63292
63458
|
console.log(` - this ${params.kind} is now on the Telegram allowlist; other groups or topics still need to be added explicitly`);
|
|
63293
63459
|
console.log(" - if you want pairing-style access control for DMs, set channels.telegram.directMessages.policy to `pairing`");
|
|
63294
|
-
console.log(" - tune route settings such as requireMention
|
|
63295
|
-
|
|
63296
|
-
platform: "telegram",
|
|
63297
|
-
conversationKind: params.kind === "topic" ? "topic" : "group",
|
|
63298
|
-
chatId: chatId ?? params.routeId,
|
|
63299
|
-
topicId
|
|
63300
|
-
}, " ")) {
|
|
63301
|
-
console.log(line);
|
|
63302
|
-
}
|
|
63460
|
+
console.log(" - tune route settings such as requireMention and followUp in clisbot.json if this surface should behave differently");
|
|
63461
|
+
console.log(` - manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`);
|
|
63303
63462
|
}
|
|
63304
63463
|
console.log("Run `clisbot status` to inspect routes and current channel state.");
|
|
63305
63464
|
console.log("Run `clisbot logs` if the bot is still not responding.");
|
|
@@ -63380,6 +63539,9 @@ async function runChannelsCli(args) {
|
|
|
63380
63539
|
}
|
|
63381
63540
|
|
|
63382
63541
|
// src/control/channel-bootstrap-flags.ts
|
|
63542
|
+
function isLiteralToken(token) {
|
|
63543
|
+
return token?.kind === "mem";
|
|
63544
|
+
}
|
|
63383
63545
|
function parseBotType(rawValue) {
|
|
63384
63546
|
const value = rawValue.trim().toLowerCase();
|
|
63385
63547
|
if (value === "personal" || value === "personal-assistant") {
|
|
@@ -63390,7 +63552,7 @@ function parseBotType(rawValue) {
|
|
|
63390
63552
|
}
|
|
63391
63553
|
throw new Error(`Invalid bot type: ${rawValue}`);
|
|
63392
63554
|
}
|
|
63393
|
-
function
|
|
63555
|
+
function parseOptionValue3(args, name, index) {
|
|
63394
63556
|
const value = args[index + 1]?.trim();
|
|
63395
63557
|
if (!value) {
|
|
63396
63558
|
throw new Error(`Missing value for ${name}`);
|
|
@@ -63434,7 +63596,6 @@ function validateTelegramAccount(account) {
|
|
|
63434
63596
|
function parseBootstrapFlags(args) {
|
|
63435
63597
|
const slackAccounts = [];
|
|
63436
63598
|
const telegramAccounts = [];
|
|
63437
|
-
const literalWarnings = [];
|
|
63438
63599
|
let currentSlackAccountId;
|
|
63439
63600
|
let currentTelegramAccountId;
|
|
63440
63601
|
let cliTool;
|
|
@@ -63446,17 +63607,17 @@ function parseBootstrapFlags(args) {
|
|
|
63446
63607
|
for (let index = 0;index < args.length; index += 1) {
|
|
63447
63608
|
const arg = args[index];
|
|
63448
63609
|
if (arg === "--cli") {
|
|
63449
|
-
cliTool =
|
|
63610
|
+
cliTool = parseOptionValue3(args, arg, index);
|
|
63450
63611
|
index += 1;
|
|
63451
63612
|
continue;
|
|
63452
63613
|
}
|
|
63453
63614
|
if (arg === "--bootstrap") {
|
|
63454
|
-
bootstrap = parseBotType(
|
|
63615
|
+
bootstrap = parseBotType(parseOptionValue3(args, arg, index));
|
|
63455
63616
|
index += 1;
|
|
63456
63617
|
continue;
|
|
63457
63618
|
}
|
|
63458
63619
|
if (arg === "--bot-type") {
|
|
63459
|
-
bootstrap = parseBotType(
|
|
63620
|
+
bootstrap = parseBotType(parseOptionValue3(args, arg, index));
|
|
63460
63621
|
index += 1;
|
|
63461
63622
|
continue;
|
|
63462
63623
|
}
|
|
@@ -63465,7 +63626,7 @@ function parseBootstrapFlags(args) {
|
|
|
63465
63626
|
continue;
|
|
63466
63627
|
}
|
|
63467
63628
|
if (arg === "--slack-account") {
|
|
63468
|
-
const accountId =
|
|
63629
|
+
const accountId = parseOptionValue3(args, arg, index);
|
|
63469
63630
|
ensureUniqueAccount(slackAccounts, accountId, "--slack-account");
|
|
63470
63631
|
currentSlackAccountId = accountId;
|
|
63471
63632
|
getOrCreateSlackAccount(slackAccounts, accountId);
|
|
@@ -63474,7 +63635,7 @@ function parseBootstrapFlags(args) {
|
|
|
63474
63635
|
continue;
|
|
63475
63636
|
}
|
|
63476
63637
|
if (arg === "--telegram-account") {
|
|
63477
|
-
const accountId =
|
|
63638
|
+
const accountId = parseOptionValue3(args, arg, index);
|
|
63478
63639
|
ensureUniqueAccount(telegramAccounts, accountId, "--telegram-account");
|
|
63479
63640
|
currentTelegramAccountId = accountId;
|
|
63480
63641
|
getOrCreateTelegramAccount(telegramAccounts, accountId);
|
|
@@ -63483,36 +63644,27 @@ function parseBootstrapFlags(args) {
|
|
|
63483
63644
|
continue;
|
|
63484
63645
|
}
|
|
63485
63646
|
if (arg === "--slack-app-token") {
|
|
63486
|
-
const token = parseTokenInput(
|
|
63647
|
+
const token = parseTokenInput(parseOptionValue3(args, arg, index));
|
|
63487
63648
|
const account = getOrCreateSlackAccount(slackAccounts, currentSlackAccountId ?? "default");
|
|
63488
63649
|
account.appToken = token;
|
|
63489
|
-
if (token.kind === "mem") {
|
|
63490
|
-
literalWarnings.push(`Slack account ${account.accountId} uses a literal CLI token; shell history or process inspection may expose it.`);
|
|
63491
|
-
}
|
|
63492
63650
|
sawCredentialFlags = true;
|
|
63493
63651
|
sawSlackFlags = true;
|
|
63494
63652
|
index += 1;
|
|
63495
63653
|
continue;
|
|
63496
63654
|
}
|
|
63497
63655
|
if (arg === "--slack-bot-token") {
|
|
63498
|
-
const token = parseTokenInput(
|
|
63656
|
+
const token = parseTokenInput(parseOptionValue3(args, arg, index));
|
|
63499
63657
|
const account = getOrCreateSlackAccount(slackAccounts, currentSlackAccountId ?? "default");
|
|
63500
63658
|
account.botToken = token;
|
|
63501
|
-
if (token.kind === "mem") {
|
|
63502
|
-
literalWarnings.push(`Slack account ${account.accountId} uses a literal CLI token; shell history or process inspection may expose it.`);
|
|
63503
|
-
}
|
|
63504
63659
|
sawCredentialFlags = true;
|
|
63505
63660
|
sawSlackFlags = true;
|
|
63506
63661
|
index += 1;
|
|
63507
63662
|
continue;
|
|
63508
63663
|
}
|
|
63509
63664
|
if (arg === "--telegram-bot-token") {
|
|
63510
|
-
const token = parseTokenInput(
|
|
63665
|
+
const token = parseTokenInput(parseOptionValue3(args, arg, index));
|
|
63511
63666
|
const account = getOrCreateTelegramAccount(telegramAccounts, currentTelegramAccountId ?? "default");
|
|
63512
63667
|
account.botToken = token;
|
|
63513
|
-
if (token.kind === "mem") {
|
|
63514
|
-
literalWarnings.push(`Telegram account ${account.accountId} uses a literal CLI token; shell history or process inspection may expose it.`);
|
|
63515
|
-
}
|
|
63516
63668
|
sawCredentialFlags = true;
|
|
63517
63669
|
sawTelegramFlags = true;
|
|
63518
63670
|
index += 1;
|
|
@@ -63535,9 +63687,12 @@ function parseBootstrapFlags(args) {
|
|
|
63535
63687
|
sawCredentialFlags,
|
|
63536
63688
|
sawSlackFlags,
|
|
63537
63689
|
sawTelegramFlags,
|
|
63538
|
-
literalWarnings
|
|
63690
|
+
literalWarnings: []
|
|
63539
63691
|
};
|
|
63540
63692
|
}
|
|
63693
|
+
function hasLiteralBootstrapCredentials(flags) {
|
|
63694
|
+
return flags.slackAccounts.some((account) => isLiteralToken(account.appToken) || isLiteralToken(account.botToken)) || flags.telegramAccounts.some((account) => isLiteralToken(account.botToken));
|
|
63695
|
+
}
|
|
63541
63696
|
|
|
63542
63697
|
// src/agents/session-state.ts
|
|
63543
63698
|
class AgentSessionState {
|
|
@@ -63591,6 +63746,8 @@ class AgentSessionState {
|
|
|
63591
63746
|
startedAt: entry?.runtime?.startedAt,
|
|
63592
63747
|
detachedAt: entry?.runtime?.detachedAt,
|
|
63593
63748
|
finalReplyAt: entry?.runtime?.finalReplyAt,
|
|
63749
|
+
lastMessageToolReplyAt: entry?.runtime?.lastMessageToolReplyAt,
|
|
63750
|
+
messageToolFinalReplyAt: entry?.runtime?.messageToolFinalReplyAt,
|
|
63594
63751
|
sessionKey: target.sessionKey,
|
|
63595
63752
|
agentId: target.agentId
|
|
63596
63753
|
};
|
|
@@ -63602,6 +63759,8 @@ class AgentSessionState {
|
|
|
63602
63759
|
startedAt: entry.runtime.startedAt,
|
|
63603
63760
|
detachedAt: entry.runtime.detachedAt,
|
|
63604
63761
|
finalReplyAt: entry.runtime.finalReplyAt,
|
|
63762
|
+
lastMessageToolReplyAt: entry.runtime.lastMessageToolReplyAt,
|
|
63763
|
+
messageToolFinalReplyAt: entry.runtime.messageToolFinalReplyAt,
|
|
63605
63764
|
sessionKey: entry.sessionKey,
|
|
63606
63765
|
agentId: entry.agentId
|
|
63607
63766
|
}));
|
|
@@ -63735,7 +63894,7 @@ class AgentSessionState {
|
|
|
63735
63894
|
}
|
|
63736
63895
|
return this.resetConversationFollowUpMode(resolved);
|
|
63737
63896
|
}
|
|
63738
|
-
async recordConversationReply(resolved, kind = "reply") {
|
|
63897
|
+
async recordConversationReply(resolved, kind = "reply", source = "channel") {
|
|
63739
63898
|
const repliedAt = Date.now();
|
|
63740
63899
|
return this.upsertSessionEntry(resolved, (existing) => ({
|
|
63741
63900
|
sessionId: existing?.sessionId,
|
|
@@ -63744,9 +63903,17 @@ class AgentSessionState {
|
|
|
63744
63903
|
lastBotReplyAt: repliedAt
|
|
63745
63904
|
},
|
|
63746
63905
|
runnerCommand: existing?.runnerCommand ?? resolved.runner.command,
|
|
63747
|
-
runtime:
|
|
63906
|
+
runtime: existing?.runtime && existing.runtime.state !== "idle" ? {
|
|
63748
63907
|
...existing.runtime,
|
|
63749
|
-
|
|
63908
|
+
...kind === "final" ? {
|
|
63909
|
+
finalReplyAt: repliedAt
|
|
63910
|
+
} : {},
|
|
63911
|
+
...source === "message-tool" ? {
|
|
63912
|
+
lastMessageToolReplyAt: repliedAt,
|
|
63913
|
+
...kind === "final" ? {
|
|
63914
|
+
messageToolFinalReplyAt: repliedAt
|
|
63915
|
+
} : {}
|
|
63916
|
+
} : {}
|
|
63750
63917
|
} : existing?.runtime,
|
|
63751
63918
|
intervalLoops: existing?.intervalLoops
|
|
63752
63919
|
}));
|
|
@@ -63773,7 +63940,7 @@ function hasActiveRuntime(entry) {
|
|
|
63773
63940
|
}
|
|
63774
63941
|
|
|
63775
63942
|
// src/agents/session-store.ts
|
|
63776
|
-
import { dirname as
|
|
63943
|
+
import { dirname as dirname11 } from "node:path";
|
|
63777
63944
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
63778
63945
|
import { rename } from "node:fs/promises";
|
|
63779
63946
|
class SessionStore {
|
|
@@ -63866,7 +64033,7 @@ class SessionStore {
|
|
|
63866
64033
|
return parsed;
|
|
63867
64034
|
}
|
|
63868
64035
|
async writeStore(store) {
|
|
63869
|
-
await ensureDir2(
|
|
64036
|
+
await ensureDir2(dirname11(this.storePath));
|
|
63870
64037
|
const tempPath = `${this.storePath}.${process.pid}.${randomUUID3()}.tmp`;
|
|
63871
64038
|
await writeTextFile(tempPath, JSON.stringify(store, null, 2));
|
|
63872
64039
|
await rename(tempPath, this.storePath);
|
|
@@ -63874,7 +64041,7 @@ class SessionStore {
|
|
|
63874
64041
|
}
|
|
63875
64042
|
|
|
63876
64043
|
// src/control/loops-cli.ts
|
|
63877
|
-
function
|
|
64044
|
+
function getEditableConfigPath6() {
|
|
63878
64045
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
63879
64046
|
}
|
|
63880
64047
|
function renderLoopsHelp() {
|
|
@@ -63932,7 +64099,7 @@ function getSessionState(sessionStorePath) {
|
|
|
63932
64099
|
return new AgentSessionState(new SessionStore(sessionStorePath));
|
|
63933
64100
|
}
|
|
63934
64101
|
async function loadLoopControlState() {
|
|
63935
|
-
const configPath = await ensureEditableConfigFile(
|
|
64102
|
+
const configPath = await ensureEditableConfigFile(getEditableConfigPath6());
|
|
63936
64103
|
const loadedConfig = await loadConfigWithoutEnvResolution(configPath);
|
|
63937
64104
|
const sessionStorePath = resolveSessionStorePath(loadedConfig);
|
|
63938
64105
|
return {
|
|
@@ -64001,6 +64168,141 @@ async function runLoopsCli(args) {
|
|
|
64001
64168
|
// src/agents/agent-service.ts
|
|
64002
64169
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
64003
64170
|
|
|
64171
|
+
// src/channels/agent-prompt.ts
|
|
64172
|
+
function buildAgentPromptText(params) {
|
|
64173
|
+
if (!params.config.enabled) {
|
|
64174
|
+
return params.text;
|
|
64175
|
+
}
|
|
64176
|
+
const systemBlock = renderAgentPromptInstruction(params);
|
|
64177
|
+
return `<system>
|
|
64178
|
+
${systemBlock}
|
|
64179
|
+
</system>
|
|
64180
|
+
|
|
64181
|
+
<user>
|
|
64182
|
+
${params.text}
|
|
64183
|
+
</user>`;
|
|
64184
|
+
}
|
|
64185
|
+
function renderAgentPromptInstruction(params) {
|
|
64186
|
+
const messageToolMode = (params.responseMode ?? "message-tool") === "message-tool";
|
|
64187
|
+
const progressAllowed = messageToolMode && (params.streaming ?? "all") !== "off";
|
|
64188
|
+
const lines = [
|
|
64189
|
+
`[${renderPromptTimestamp()}] ${renderIdentitySummary(params.identity)}`,
|
|
64190
|
+
"",
|
|
64191
|
+
"You are operating inside clisbot.",
|
|
64192
|
+
messageToolMode ? progressAllowed ? "To send a user-visible progress update or final reply, use the following CLI command:" : "To send the final user-visible reply, use the following CLI command:" : "channel auto-delivery remains enabled for this conversation; do not send user-facing progress updates or the final response with clisbot message send"
|
|
64193
|
+
];
|
|
64194
|
+
if (messageToolMode) {
|
|
64195
|
+
const replyCommand = buildReplyCommand({
|
|
64196
|
+
command: getClisbotPromptCommand(),
|
|
64197
|
+
identity: params.identity
|
|
64198
|
+
});
|
|
64199
|
+
lines.push(replyCommand, "When replying to the user:", "- put the user-facing message inside the --message body of that command", progressAllowed ? "- use that command to send progress updates and the final reply back to the conversation" : "- use that command only for the final user-facing reply", ...progressAllowed ? [`- send at most ${params.config.maxProgressMessages} progress updates`] : ["- do not send user-facing progress updates for this conversation"], params.config.requireFinalResponse ? "- send exactly 1 final user-facing response" : "- final response is optional", ...progressAllowed ? [
|
|
64200
|
+
"- keep progress updates short and meaningful",
|
|
64201
|
+
"- do not send progress updates for trivial internal steps"
|
|
64202
|
+
] : []);
|
|
64203
|
+
}
|
|
64204
|
+
if (params.protectedControlMutationRule) {
|
|
64205
|
+
lines.push("", params.protectedControlMutationRule);
|
|
64206
|
+
}
|
|
64207
|
+
return lines.join(`
|
|
64208
|
+
`);
|
|
64209
|
+
}
|
|
64210
|
+
function renderPromptTimestamp() {
|
|
64211
|
+
const date = new Date;
|
|
64212
|
+
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
64213
|
+
year: "numeric",
|
|
64214
|
+
month: "2-digit",
|
|
64215
|
+
day: "2-digit",
|
|
64216
|
+
hour: "2-digit",
|
|
64217
|
+
minute: "2-digit",
|
|
64218
|
+
second: "2-digit",
|
|
64219
|
+
hour12: false,
|
|
64220
|
+
timeZoneName: "shortOffset"
|
|
64221
|
+
});
|
|
64222
|
+
return formatter.format(date).replace(",", "");
|
|
64223
|
+
}
|
|
64224
|
+
function renderIdentitySummary(identity) {
|
|
64225
|
+
const segments = [renderConversationSummary(identity)];
|
|
64226
|
+
const sender = renderSenderSummary(identity);
|
|
64227
|
+
if (sender) {
|
|
64228
|
+
segments.push(sender);
|
|
64229
|
+
}
|
|
64230
|
+
return segments.join(" | ");
|
|
64231
|
+
}
|
|
64232
|
+
function renderConversationSummary(identity) {
|
|
64233
|
+
if (identity.platform === "slack") {
|
|
64234
|
+
const scopeLabel = identity.conversationKind === "dm" ? "Slack direct message" : identity.conversationKind === "group" ? "Slack group" : "Slack channel";
|
|
64235
|
+
const segments = [scopeLabel];
|
|
64236
|
+
const channel = renderLabeledTarget(identity.channelName, identity.channelId, "#");
|
|
64237
|
+
if (channel) {
|
|
64238
|
+
segments.push(channel);
|
|
64239
|
+
}
|
|
64240
|
+
if (identity.threadTs) {
|
|
64241
|
+
segments.push(`thread ${identity.threadTs}`);
|
|
64242
|
+
}
|
|
64243
|
+
return segments.join(" ");
|
|
64244
|
+
}
|
|
64245
|
+
if (identity.conversationKind === "dm") {
|
|
64246
|
+
return ["Telegram direct message", renderLabeledTarget(identity.chatName, identity.chatId)].filter(Boolean).join(" ");
|
|
64247
|
+
}
|
|
64248
|
+
if (identity.conversationKind === "topic") {
|
|
64249
|
+
const topic = renderNamedValue("topic", identity.topicName, identity.topicId);
|
|
64250
|
+
const group = renderNamedValue("in group", identity.chatName, identity.chatId);
|
|
64251
|
+
return [topic, group].filter(Boolean).join(" ");
|
|
64252
|
+
}
|
|
64253
|
+
return ["Telegram group", renderLabeledTarget(identity.chatName, identity.chatId)].filter(Boolean).join(" ");
|
|
64254
|
+
}
|
|
64255
|
+
function renderSenderSummary(identity) {
|
|
64256
|
+
const sender = renderLabeledTarget(identity.senderName, identity.senderId);
|
|
64257
|
+
return sender ? `sender ${sender}` : "";
|
|
64258
|
+
}
|
|
64259
|
+
function renderLabeledTarget(name, id, namePrefix = "") {
|
|
64260
|
+
const normalizedName = name?.trim();
|
|
64261
|
+
const normalizedId = id?.trim();
|
|
64262
|
+
if (normalizedName && normalizedId) {
|
|
64263
|
+
return `${namePrefix}${normalizedName} (${normalizedId})`;
|
|
64264
|
+
}
|
|
64265
|
+
if (normalizedName) {
|
|
64266
|
+
return `${namePrefix}${normalizedName}`;
|
|
64267
|
+
}
|
|
64268
|
+
return normalizedId ?? "";
|
|
64269
|
+
}
|
|
64270
|
+
function renderNamedValue(label, name, id) {
|
|
64271
|
+
const value = renderLabeledTarget(name, id);
|
|
64272
|
+
return value ? `${label} ${value}` : "";
|
|
64273
|
+
}
|
|
64274
|
+
function buildReplyCommand(params) {
|
|
64275
|
+
const lines = [`${params.command} message send \\`];
|
|
64276
|
+
if (params.identity.platform === "slack") {
|
|
64277
|
+
lines.push(" --channel slack \\");
|
|
64278
|
+
lines.push(` --target channel:${params.identity.channelId ?? ""} \\`);
|
|
64279
|
+
if (params.identity.threadTs) {
|
|
64280
|
+
lines.push(` --thread-id ${params.identity.threadTs} \\`);
|
|
64281
|
+
}
|
|
64282
|
+
lines.push(" --final \\");
|
|
64283
|
+
lines.push(' --message "$(cat <<\\__CLISBOT_MESSAGE__');
|
|
64284
|
+
lines.push("<short progress update>");
|
|
64285
|
+
lines.push("__CLISBOT_MESSAGE__");
|
|
64286
|
+
lines.push(')" \\');
|
|
64287
|
+
lines.push(" [--media /absolute/path/to/file]");
|
|
64288
|
+
return lines.join(`
|
|
64289
|
+
`);
|
|
64290
|
+
}
|
|
64291
|
+
lines.push(" --channel telegram \\");
|
|
64292
|
+
lines.push(` --target ${params.identity.chatId ?? ""} \\`);
|
|
64293
|
+
if (params.identity.topicId) {
|
|
64294
|
+
lines.push(` --thread-id ${params.identity.topicId} \\`);
|
|
64295
|
+
}
|
|
64296
|
+
lines.push(" --final \\");
|
|
64297
|
+
lines.push(' --message "$(cat <<\\__CLISBOT_MESSAGE__');
|
|
64298
|
+
lines.push("<short progress update>");
|
|
64299
|
+
lines.push("__CLISBOT_MESSAGE__");
|
|
64300
|
+
lines.push(')" \\');
|
|
64301
|
+
lines.push(" [--media /absolute/path/to/file]");
|
|
64302
|
+
return lines.join(`
|
|
64303
|
+
`);
|
|
64304
|
+
}
|
|
64305
|
+
|
|
64004
64306
|
// src/agents/session-key.ts
|
|
64005
64307
|
var DEFAULT_MAIN_KEY = "main";
|
|
64006
64308
|
var DEFAULT_ACCOUNT_ID = "default";
|
|
@@ -64272,7 +64574,7 @@ class AgentJobQueue {
|
|
|
64272
64574
|
}
|
|
64273
64575
|
|
|
64274
64576
|
// src/agents/runner-session.ts
|
|
64275
|
-
import { dirname as
|
|
64577
|
+
import { dirname as dirname12 } from "node:path";
|
|
64276
64578
|
|
|
64277
64579
|
// src/shared/transcript-normalization.ts
|
|
64278
64580
|
import { stripVTControlCharacters } from "node:util";
|
|
@@ -64393,6 +64695,12 @@ function looksLikeClaudeSnapshot(lines) {
|
|
|
64393
64695
|
return trimmed.includes("Claude Code v") || trimmed.includes("Welcome back!") || trimmed.includes("Tips for getting started") || trimmed.startsWith("❯") || trimmed.startsWith("⏺");
|
|
64394
64696
|
});
|
|
64395
64697
|
}
|
|
64698
|
+
function looksLikeGeminiSnapshot(lines) {
|
|
64699
|
+
return lines.some((line) => {
|
|
64700
|
+
const trimmed = line.trim();
|
|
64701
|
+
return trimmed.includes("Gemini CLI v") || trimmed.includes("Signed in with Google") || trimmed.includes("YOLO Ctrl+Y") || trimmed.includes("Type your message or @path/to/file") || trimmed.includes("workspace (/directory)");
|
|
64702
|
+
});
|
|
64703
|
+
}
|
|
64396
64704
|
function isProgressLine(line) {
|
|
64397
64705
|
const trimmed = line.trim();
|
|
64398
64706
|
const normalized = trimmed.replace(/^(?::eight_spoked_asterisk:|[✽✶])\s+/, "");
|
|
@@ -64491,6 +64799,9 @@ function dropClaudePromptBlocks(lines) {
|
|
|
64491
64799
|
}
|
|
64492
64800
|
return filtered;
|
|
64493
64801
|
}
|
|
64802
|
+
function dropGeminiPromptBlocks(lines) {
|
|
64803
|
+
return dropPromptBlocks(lines, /^\s*>\s/);
|
|
64804
|
+
}
|
|
64494
64805
|
function isInterruptStatusLine(line) {
|
|
64495
64806
|
const trimmed = line.trim();
|
|
64496
64807
|
if (!trimmed) {
|
|
@@ -64512,6 +64823,13 @@ function shouldDropClaudeChromeLine(line) {
|
|
|
64512
64823
|
}
|
|
64513
64824
|
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 |") || /^(?:[✻*]\s*)?(?:Worked|Cooked) for \d+s$/i.test(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);
|
|
64514
64825
|
}
|
|
64826
|
+
function shouldDropGeminiChromeLine(line) {
|
|
64827
|
+
const trimmed = line.trim();
|
|
64828
|
+
if (!trimmed) {
|
|
64829
|
+
return false;
|
|
64830
|
+
}
|
|
64831
|
+
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) || /^Thinking\.\.\. \(esc to cancel,\s*\d+s\)$/i.test(trimmed) || /^[╭╰│]/.test(trimmed) || /^[-▀▄]{10,}$/.test(trimmed) || /^─+$/.test(trimmed);
|
|
64832
|
+
}
|
|
64515
64833
|
function normalizeBoundaryLine(line) {
|
|
64516
64834
|
return line.trim().replace(/^(?::eight_spoked_asterisk:|[-*•◦·✽✶])\s+/, "");
|
|
64517
64835
|
}
|
|
@@ -64548,7 +64866,8 @@ function cleanInteractionSnapshot(raw) {
|
|
|
64548
64866
|
const lines = splitNormalizedLines(raw);
|
|
64549
64867
|
const isCodex = looksLikeCodexSnapshot(lines);
|
|
64550
64868
|
const isClaude = looksLikeClaudeSnapshot(lines);
|
|
64551
|
-
const
|
|
64869
|
+
const isGemini = looksLikeGeminiSnapshot(lines);
|
|
64870
|
+
const promptStripped = isCodex ? dropCodexPromptBlocks(lines) : isClaude ? dropClaudePromptBlocks(lines) : isGemini ? dropGeminiPromptBlocks(lines) : lines;
|
|
64552
64871
|
const filtered = promptStripped.filter((line) => {
|
|
64553
64872
|
if (shouldDropDeliveryReportLine(line)) {
|
|
64554
64873
|
return false;
|
|
@@ -64559,9 +64878,12 @@ function cleanInteractionSnapshot(raw) {
|
|
|
64559
64878
|
if (isClaude && shouldDropClaudeChromeLine(line)) {
|
|
64560
64879
|
return false;
|
|
64561
64880
|
}
|
|
64881
|
+
if (isGemini && shouldDropGeminiChromeLine(line)) {
|
|
64882
|
+
return false;
|
|
64883
|
+
}
|
|
64562
64884
|
return true;
|
|
64563
64885
|
});
|
|
64564
|
-
const normalized = isCodex ? unwrapCodexMessageBlocks(filtered) : isClaude ? unwrapClaudeMessageBlocks(filtered) : filtered;
|
|
64886
|
+
const normalized = isCodex ? unwrapCodexMessageBlocks(filtered) : isClaude ? unwrapClaudeMessageBlocks(filtered) : isGemini ? filtered.map((line) => line.replace(/^\s*>\s*/, "")) : filtered;
|
|
64565
64887
|
const unwrapped = unwrapSoftWrappedLines(normalized);
|
|
64566
64888
|
return collapseAdjacentDuplicateLines(collapseBlankLines(trimBlankLines(unwrapped)).join(`
|
|
64567
64889
|
`));
|
|
@@ -64747,6 +65069,7 @@ function extractFinalAnswer(raw) {
|
|
|
64747
65069
|
const rawLines = splitNormalizedLines(raw);
|
|
64748
65070
|
const isCodex = looksLikeCodexSnapshot(rawLines);
|
|
64749
65071
|
const isClaude = looksLikeClaudeSnapshot(rawLines);
|
|
65072
|
+
const isGemini = looksLikeGeminiSnapshot(rawLines);
|
|
64750
65073
|
const cleaned = cleanInteractionSnapshot(raw);
|
|
64751
65074
|
if (!cleaned) {
|
|
64752
65075
|
return "";
|
|
@@ -64773,14 +65096,14 @@ function extractFinalAnswer(raw) {
|
|
|
64773
65096
|
if (answerBlocks.length > 1 && answerBlocks.every(isShortAtomicAnswerBlock)) {
|
|
64774
65097
|
const lastAnswer = answerBlocks.at(-1)?.trim() ?? "";
|
|
64775
65098
|
if (lastAnswer) {
|
|
64776
|
-
return isCodex || isClaude ? stripSingleLineAssistantEnvelope(lastAnswer) : lastAnswer;
|
|
65099
|
+
return isCodex || isClaude || isGemini ? stripSingleLineAssistantEnvelope(lastAnswer) : lastAnswer;
|
|
64777
65100
|
}
|
|
64778
65101
|
}
|
|
64779
65102
|
const answer = answerBlocks.join(`
|
|
64780
65103
|
|
|
64781
65104
|
`).trim();
|
|
64782
65105
|
const extracted = answer || cleaned;
|
|
64783
|
-
if (isCodex || isClaude) {
|
|
65106
|
+
if (isCodex || isClaude || isGemini) {
|
|
64784
65107
|
return stripSingleLineAssistantEnvelope(extracted);
|
|
64785
65108
|
}
|
|
64786
65109
|
return extracted;
|
|
@@ -64815,7 +65138,11 @@ ${body}` : queueNote;
|
|
|
64815
65138
|
_Timed out waiting for more output._` : "_Timed out waiting for visible output._";
|
|
64816
65139
|
}
|
|
64817
65140
|
if (params.status === "detached") {
|
|
64818
|
-
const note =
|
|
65141
|
+
const note = resolveDetachedInteractionNote({
|
|
65142
|
+
baseNote: params.note,
|
|
65143
|
+
allowTranscriptInspection: params.allowTranscriptInspection,
|
|
65144
|
+
transcriptCommand: "`/transcript`"
|
|
65145
|
+
});
|
|
64819
65146
|
return body ? `${body}
|
|
64820
65147
|
|
|
64821
65148
|
_${note}_` : `_${note}_`;
|
|
@@ -64844,7 +65171,11 @@ ${body}` : queueNote;
|
|
|
64844
65171
|
Timed out waiting for more output.` : "Timed out waiting for visible output.";
|
|
64845
65172
|
}
|
|
64846
65173
|
if (params.status === "detached") {
|
|
64847
|
-
const note =
|
|
65174
|
+
const note = resolveDetachedInteractionNote({
|
|
65175
|
+
baseNote: params.note,
|
|
65176
|
+
allowTranscriptInspection: params.allowTranscriptInspection,
|
|
65177
|
+
transcriptCommand: "/transcript"
|
|
65178
|
+
});
|
|
64848
65179
|
return body ? `${body}
|
|
64849
65180
|
|
|
64850
65181
|
${note}` : note;
|
|
@@ -64880,6 +65211,16 @@ ${body}
|
|
|
64880
65211
|
}
|
|
64881
65212
|
var renderSlackSnapshot = renderSlackTranscript;
|
|
64882
65213
|
var renderChannelSnapshot = renderSlackSnapshot;
|
|
65214
|
+
function resolveDetachedInteractionNote(params) {
|
|
65215
|
+
const note = params.baseNote ?? "This session is still running. Use `/attach`, `/watch every 30s`, or `/stop` to manage it.";
|
|
65216
|
+
if (!params.allowTranscriptInspection) {
|
|
65217
|
+
return note;
|
|
65218
|
+
}
|
|
65219
|
+
if (note.includes("/transcript")) {
|
|
65220
|
+
return note;
|
|
65221
|
+
}
|
|
65222
|
+
return `${note} You can also use ${params.transcriptCommand} to inspect the current session snapshot.`;
|
|
65223
|
+
}
|
|
64883
65224
|
// src/control/latency-debug.ts
|
|
64884
65225
|
function isLatencyDebugEnabled() {
|
|
64885
65226
|
return process.env.CLISBOT_DEBUG_LATENCY === "1";
|
|
@@ -64899,14 +65240,23 @@ function logLatencyDebug(stage, context = {}, details = {}) {
|
|
|
64899
65240
|
// src/runners/tmux/session-handshake.ts
|
|
64900
65241
|
var TRUST_PROMPT_POLL_INTERVAL_MS = 250;
|
|
64901
65242
|
var TRUST_PROMPT_MAX_WAIT_MS = 1e4;
|
|
64902
|
-
var TRUST_PROMPT_SETTLE_DELAY_MS = 1500;
|
|
64903
65243
|
var SESSION_BOOTSTRAP_POLL_INTERVAL_MS = 100;
|
|
65244
|
+
var PASTE_SETTLE_POLL_INTERVAL_MS = 40;
|
|
65245
|
+
var PASTE_SETTLE_QUIET_WINDOW_MS = 60;
|
|
65246
|
+
var PASTE_SETTLE_MULTILINE_MAX_WAIT_MS = 800;
|
|
65247
|
+
var PASTE_SETTLE_SINGLE_LINE_MAX_WAIT_MS = 80;
|
|
64904
65248
|
var SUBMIT_CONFIRM_POLL_INTERVAL_MS = 40;
|
|
64905
65249
|
var SUBMIT_CONFIRM_MAX_WAIT_MS = 160;
|
|
64906
65250
|
async function submitTmuxSessionInput(params) {
|
|
65251
|
+
const prePasteState = await params.tmux.getPaneState(params.sessionName);
|
|
64907
65252
|
await params.tmux.sendLiteral(params.sessionName, params.text);
|
|
64908
|
-
await
|
|
64909
|
-
|
|
65253
|
+
const preSubmitState = await waitForPanePasteSettlement({
|
|
65254
|
+
tmux: params.tmux,
|
|
65255
|
+
sessionName: params.sessionName,
|
|
65256
|
+
baseline: prePasteState,
|
|
65257
|
+
text: params.text,
|
|
65258
|
+
minDelayMs: params.promptSubmitDelayMs
|
|
65259
|
+
});
|
|
64910
65260
|
await params.tmux.sendKey(params.sessionName, "Enter");
|
|
64911
65261
|
if (await waitForPaneSubmitConfirmation({
|
|
64912
65262
|
tmux: params.tmux,
|
|
@@ -64932,7 +65282,6 @@ async function submitTmuxSessionInput(params) {
|
|
|
64932
65282
|
throw new Error("tmux submit was not confirmed after Enter. The pane state did not change, so clisbot did not treat the prompt as truthfully submitted.");
|
|
64933
65283
|
}
|
|
64934
65284
|
async function captureTmuxSessionIdentity(params) {
|
|
64935
|
-
let deadline = Date.now() + params.timeoutMs;
|
|
64936
65285
|
await submitTmuxSessionInput({
|
|
64937
65286
|
tmux: params.tmux,
|
|
64938
65287
|
sessionName: params.sessionName,
|
|
@@ -64940,11 +65289,16 @@ async function captureTmuxSessionIdentity(params) {
|
|
|
64940
65289
|
promptSubmitDelayMs: params.promptSubmitDelayMs,
|
|
64941
65290
|
timingContext: undefined
|
|
64942
65291
|
});
|
|
65292
|
+
let deadline = Date.now() + params.timeoutMs;
|
|
64943
65293
|
while (Date.now() < deadline) {
|
|
64944
65294
|
await sleep(params.pollIntervalMs);
|
|
64945
65295
|
const snapshot = normalizePaneText(await params.tmux.capturePane(params.sessionName, params.captureLines));
|
|
64946
65296
|
if (hasTrustPrompt(snapshot)) {
|
|
64947
|
-
await dismissTrustPrompt(
|
|
65297
|
+
await dismissTrustPrompt({
|
|
65298
|
+
tmux: params.tmux,
|
|
65299
|
+
sessionName: params.sessionName,
|
|
65300
|
+
captureLines: params.captureLines
|
|
65301
|
+
});
|
|
64948
65302
|
deadline = Date.now() + params.timeoutMs;
|
|
64949
65303
|
await submitTmuxSessionInput({
|
|
64950
65304
|
tmux: params.tmux,
|
|
@@ -64973,11 +65327,21 @@ async function dismissTmuxTrustPromptIfPresent(params) {
|
|
|
64973
65327
|
if (!hasTrustPrompt(snapshot)) {
|
|
64974
65328
|
return;
|
|
64975
65329
|
}
|
|
64976
|
-
await dismissTrustPrompt(
|
|
65330
|
+
await dismissTrustPrompt({
|
|
65331
|
+
tmux: params.tmux,
|
|
65332
|
+
sessionName: params.sessionName,
|
|
65333
|
+
captureLines: params.captureLines
|
|
65334
|
+
});
|
|
64977
65335
|
}
|
|
64978
65336
|
}
|
|
64979
65337
|
async function waitForTmuxSessionBootstrap(params) {
|
|
64980
65338
|
const deadline = Date.now() + Math.max(params.startupDelayMs, SESSION_BOOTSTRAP_POLL_INTERVAL_MS);
|
|
65339
|
+
const readyRegex = params.readyPattern ? new RegExp(params.readyPattern, "i") : null;
|
|
65340
|
+
const blockerPatterns = (params.blockers ?? []).map((entry) => ({
|
|
65341
|
+
regex: new RegExp(entry.pattern, "i"),
|
|
65342
|
+
message: entry.message
|
|
65343
|
+
}));
|
|
65344
|
+
let lastSnapshot = "";
|
|
64981
65345
|
while (Date.now() <= deadline) {
|
|
64982
65346
|
let snapshot = "";
|
|
64983
65347
|
try {
|
|
@@ -64985,23 +65349,60 @@ async function waitForTmuxSessionBootstrap(params) {
|
|
|
64985
65349
|
} catch (error) {
|
|
64986
65350
|
const message = error instanceof Error ? error.message : String(error);
|
|
64987
65351
|
if (message.includes("can't find session:") || message.includes("no server running on ")) {
|
|
64988
|
-
return
|
|
65352
|
+
return {
|
|
65353
|
+
status: "timeout",
|
|
65354
|
+
snapshot: lastSnapshot
|
|
65355
|
+
};
|
|
64989
65356
|
}
|
|
64990
65357
|
throw error;
|
|
64991
65358
|
}
|
|
64992
65359
|
if (snapshot) {
|
|
64993
|
-
|
|
65360
|
+
lastSnapshot = snapshot;
|
|
65361
|
+
if (params.trustWorkspace && hasTrustPrompt(snapshot)) {
|
|
65362
|
+
await dismissTrustPrompt({
|
|
65363
|
+
tmux: params.tmux,
|
|
65364
|
+
sessionName: params.sessionName,
|
|
65365
|
+
captureLines: params.captureLines
|
|
65366
|
+
});
|
|
65367
|
+
await sleep(SESSION_BOOTSTRAP_POLL_INTERVAL_MS);
|
|
65368
|
+
continue;
|
|
65369
|
+
}
|
|
65370
|
+
for (const blocker of blockerPatterns) {
|
|
65371
|
+
if (blocker.regex.test(snapshot)) {
|
|
65372
|
+
return {
|
|
65373
|
+
status: "blocked",
|
|
65374
|
+
snapshot,
|
|
65375
|
+
message: blocker.message
|
|
65376
|
+
};
|
|
65377
|
+
}
|
|
65378
|
+
}
|
|
65379
|
+
if (readyRegex && !readyRegex.test(snapshot)) {
|
|
65380
|
+
await sleep(SESSION_BOOTSTRAP_POLL_INTERVAL_MS);
|
|
65381
|
+
continue;
|
|
65382
|
+
}
|
|
65383
|
+
return {
|
|
65384
|
+
status: "ready",
|
|
65385
|
+
snapshot
|
|
65386
|
+
};
|
|
64994
65387
|
}
|
|
64995
65388
|
await sleep(SESSION_BOOTSTRAP_POLL_INTERVAL_MS);
|
|
64996
65389
|
}
|
|
64997
|
-
return
|
|
64998
|
-
|
|
64999
|
-
|
|
65000
|
-
|
|
65390
|
+
return {
|
|
65391
|
+
status: "timeout",
|
|
65392
|
+
snapshot: lastSnapshot
|
|
65393
|
+
};
|
|
65001
65394
|
}
|
|
65002
|
-
async function dismissTrustPrompt(
|
|
65003
|
-
await tmux.sendKey(sessionName, "Enter");
|
|
65004
|
-
|
|
65395
|
+
async function dismissTrustPrompt(params) {
|
|
65396
|
+
await params.tmux.sendKey(params.sessionName, "Enter");
|
|
65397
|
+
const deadline = Date.now() + TRUST_PROMPT_MAX_WAIT_MS;
|
|
65398
|
+
while (Date.now() <= deadline) {
|
|
65399
|
+
await sleep(TRUST_PROMPT_POLL_INTERVAL_MS);
|
|
65400
|
+
const snapshot = normalizePaneText(await params.tmux.capturePane(params.sessionName, params.captureLines));
|
|
65401
|
+
if (!snapshot || hasTrustPrompt(snapshot)) {
|
|
65402
|
+
continue;
|
|
65403
|
+
}
|
|
65404
|
+
return;
|
|
65405
|
+
}
|
|
65005
65406
|
}
|
|
65006
65407
|
async function waitForPaneSubmitConfirmation(params) {
|
|
65007
65408
|
const deadline = Date.now() + SUBMIT_CONFIRM_MAX_WAIT_MS;
|
|
@@ -65017,9 +65418,50 @@ async function waitForPaneSubmitConfirmation(params) {
|
|
|
65017
65418
|
await sleep(Math.min(SUBMIT_CONFIRM_POLL_INTERVAL_MS, remainingMs));
|
|
65018
65419
|
}
|
|
65019
65420
|
}
|
|
65421
|
+
async function waitForPanePasteSettlement(params) {
|
|
65422
|
+
await sleep(params.minDelayMs);
|
|
65423
|
+
let currentState = await params.tmux.getPaneState(params.sessionName);
|
|
65424
|
+
let sawChange = hasPaneStateChanged(params.baseline, currentState);
|
|
65425
|
+
let lastChangeAt = Date.now();
|
|
65426
|
+
const deadline = Date.now() + (shouldWaitForVisiblePaste(params.text) ? PASTE_SETTLE_MULTILINE_MAX_WAIT_MS : PASTE_SETTLE_SINGLE_LINE_MAX_WAIT_MS);
|
|
65427
|
+
while (true) {
|
|
65428
|
+
if (sawChange && Date.now() - lastChangeAt >= PASTE_SETTLE_QUIET_WINDOW_MS) {
|
|
65429
|
+
return currentState;
|
|
65430
|
+
}
|
|
65431
|
+
const remainingMs = deadline - Date.now();
|
|
65432
|
+
if (remainingMs <= 0) {
|
|
65433
|
+
return currentState;
|
|
65434
|
+
}
|
|
65435
|
+
await sleep(Math.min(PASTE_SETTLE_POLL_INTERVAL_MS, remainingMs));
|
|
65436
|
+
const nextState = await params.tmux.getPaneState(params.sessionName);
|
|
65437
|
+
if (!arePaneStatesEqual(currentState, nextState)) {
|
|
65438
|
+
currentState = nextState;
|
|
65439
|
+
if (hasPaneStateChanged(params.baseline, currentState)) {
|
|
65440
|
+
sawChange = true;
|
|
65441
|
+
}
|
|
65442
|
+
lastChangeAt = Date.now();
|
|
65443
|
+
}
|
|
65444
|
+
}
|
|
65445
|
+
}
|
|
65020
65446
|
function hasPaneStateChanged(left, right) {
|
|
65021
65447
|
return left.cursorX !== right.cursorX || left.cursorY !== right.cursorY || left.historySize !== right.historySize;
|
|
65022
65448
|
}
|
|
65449
|
+
function arePaneStatesEqual(left, right) {
|
|
65450
|
+
return left.cursorX === right.cursorX && left.cursorY === right.cursorY && left.historySize === right.historySize;
|
|
65451
|
+
}
|
|
65452
|
+
function looksLikeClaudeTrustPrompt(snapshot) {
|
|
65453
|
+
return snapshot.includes("Quick safety check:") && snapshot.includes("Yes, I trust this folder") || snapshot.includes("Enter to confirm · Esc to cancel");
|
|
65454
|
+
}
|
|
65455
|
+
function looksLikeGeminiTrustPrompt(snapshot) {
|
|
65456
|
+
return snapshot.includes("Skipping project agents due to untrusted folder.") && snapshot.includes("Do you trust the files in this folder?") || snapshot.includes("Trusting a folder allows Gemini CLI to load its local configurations") && snapshot.includes("Trust folder (default)");
|
|
65457
|
+
}
|
|
65458
|
+
function hasTrustPrompt(snapshot) {
|
|
65459
|
+
return snapshot.includes("Do you trust the contents of this directory?") || snapshot.includes("Press enter to continue") || looksLikeClaudeTrustPrompt(snapshot) || looksLikeGeminiTrustPrompt(snapshot);
|
|
65460
|
+
}
|
|
65461
|
+
function shouldWaitForVisiblePaste(text) {
|
|
65462
|
+
return text.includes(`
|
|
65463
|
+
`);
|
|
65464
|
+
}
|
|
65023
65465
|
|
|
65024
65466
|
// src/runners/tmux/shell-command.ts
|
|
65025
65467
|
var BASH_WINDOW_NAME = "bash";
|
|
@@ -65129,23 +65571,10 @@ function escapeRegExp(raw) {
|
|
|
65129
65571
|
// src/agents/runner-session.ts
|
|
65130
65572
|
var TMUX_MISSING_SESSION_PATTERN = /(?:can't find session:|no server running on )/i;
|
|
65131
65573
|
var TMUX_DUPLICATE_SESSION_PATTERN = /duplicate session:/i;
|
|
65132
|
-
function
|
|
65133
|
-
|
|
65134
|
-
|
|
65135
|
-
}
|
|
65136
|
-
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
65137
|
-
}
|
|
65138
|
-
function buildCommandString(command, args) {
|
|
65139
|
-
return [command, ...args].map(shellQuote2).join(" ");
|
|
65140
|
-
}
|
|
65141
|
-
function buildRunnerLaunchCommand(command, args) {
|
|
65142
|
-
const wrapperDir = getClisbotWrapperDir();
|
|
65143
|
-
const wrapperPath = getClisbotWrapperPath();
|
|
65144
|
-
const exports = [
|
|
65145
|
-
`export PATH=${shellQuote2(wrapperDir)}:"$PATH"`,
|
|
65146
|
-
`export CLISBOT_BIN=${shellQuote2(wrapperPath)}`
|
|
65147
|
-
];
|
|
65148
|
-
return `${exports.join("; ")}; exec ${buildCommandString(command, args)}`;
|
|
65574
|
+
function summarizeSnapshot(snapshot) {
|
|
65575
|
+
const compact = snapshot.split(`
|
|
65576
|
+
`).map((line) => line.trim()).filter(Boolean).join(" ").slice(0, 220);
|
|
65577
|
+
return compact ? ` Last visible pane: ${compact}` : "";
|
|
65149
65578
|
}
|
|
65150
65579
|
function isTmuxDuplicateSessionError(error) {
|
|
65151
65580
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -65167,8 +65596,17 @@ class RunnerSessionService {
|
|
|
65167
65596
|
this.sessionState = sessionState;
|
|
65168
65597
|
this.resolveTarget = resolveTarget;
|
|
65169
65598
|
}
|
|
65170
|
-
mapSessionError(error, sessionName, action) {
|
|
65599
|
+
async mapSessionError(error, sessionName, action, lastSnapshot = "") {
|
|
65171
65600
|
if (isMissingTmuxSessionError(error)) {
|
|
65601
|
+
const exitRecord = await readRunnerExitRecord(this.loadedConfig.stateDir, sessionName);
|
|
65602
|
+
console.error("runner session disappeared", {
|
|
65603
|
+
sessionName,
|
|
65604
|
+
action,
|
|
65605
|
+
exitCode: exitRecord?.exitCode,
|
|
65606
|
+
exitedAt: exitRecord?.exitedAt,
|
|
65607
|
+
runnerCommand: exitRecord?.command,
|
|
65608
|
+
lastVisiblePane: lastSnapshot ? summarizeSnapshot(lastSnapshot).trim() : undefined
|
|
65609
|
+
});
|
|
65172
65610
|
return new Error(`Runner session "${sessionName}" disappeared ${action}.`);
|
|
65173
65611
|
}
|
|
65174
65612
|
return error instanceof Error ? error : new Error(String(error));
|
|
@@ -65239,7 +65677,11 @@ class RunnerSessionService {
|
|
|
65239
65677
|
allowFreshRetry: options.nextAllowFreshRetry
|
|
65240
65678
|
});
|
|
65241
65679
|
}
|
|
65242
|
-
async
|
|
65680
|
+
async abortUnreadySession(resolved, reason, snapshot) {
|
|
65681
|
+
await this.tmux.killSession(resolved.sessionName);
|
|
65682
|
+
throw new Error(`${reason}${summarizeSnapshot(snapshot)}`);
|
|
65683
|
+
}
|
|
65684
|
+
async runSessionCleanup() {
|
|
65243
65685
|
if (this.cleanupInFlight) {
|
|
65244
65686
|
return;
|
|
65245
65687
|
}
|
|
@@ -65283,7 +65725,8 @@ class RunnerSessionService {
|
|
|
65283
65725
|
};
|
|
65284
65726
|
logLatencyDebug("ensure-session-ready-start", timingContext);
|
|
65285
65727
|
await ensureDir2(resolved.workspacePath);
|
|
65286
|
-
await ensureDir2(
|
|
65728
|
+
await ensureDir2(dirname12(this.loadedConfig.raw.tmux.socketPath));
|
|
65729
|
+
await ensureRunnerExitRecordDir(this.loadedConfig.stateDir, resolved.sessionName);
|
|
65287
65730
|
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
65288
65731
|
const serverRunning = await this.tmux.isServerRunning();
|
|
65289
65732
|
if (serverRunning && await this.tmux.hasSession(resolved.sessionName)) {
|
|
@@ -65291,9 +65734,10 @@ class RunnerSessionService {
|
|
|
65291
65734
|
hasStoredSessionId: Boolean(existing?.sessionId)
|
|
65292
65735
|
});
|
|
65293
65736
|
try {
|
|
65737
|
+
await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
65294
65738
|
await this.syncSessionIdentity(resolved);
|
|
65295
65739
|
} catch (error) {
|
|
65296
|
-
throw this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
65740
|
+
throw await this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
65297
65741
|
}
|
|
65298
65742
|
logLatencyDebug("ensure-session-ready-complete", timingContext, {
|
|
65299
65743
|
startupDelayMs: 0,
|
|
@@ -65310,7 +65754,15 @@ class RunnerSessionService {
|
|
|
65310
65754
|
sessionId: startupSessionId || undefined,
|
|
65311
65755
|
resume: resumingExistingSession
|
|
65312
65756
|
});
|
|
65313
|
-
|
|
65757
|
+
await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
65758
|
+
const command = buildRunnerLaunchCommand({
|
|
65759
|
+
command: runnerLaunch.command,
|
|
65760
|
+
args: runnerLaunch.args,
|
|
65761
|
+
wrapperDir: getClisbotWrapperDir(),
|
|
65762
|
+
wrapperPath: getClisbotWrapperPath(),
|
|
65763
|
+
sessionName: resolved.sessionName,
|
|
65764
|
+
stateDir: this.loadedConfig.stateDir
|
|
65765
|
+
});
|
|
65314
65766
|
try {
|
|
65315
65767
|
await this.tmux.newSession({
|
|
65316
65768
|
sessionName: resolved.sessionName,
|
|
@@ -65328,11 +65780,14 @@ class RunnerSessionService {
|
|
|
65328
65780
|
resumingExistingSession,
|
|
65329
65781
|
hasStoredSessionId: Boolean(existing?.sessionId)
|
|
65330
65782
|
});
|
|
65331
|
-
await waitForTmuxSessionBootstrap({
|
|
65783
|
+
const bootstrapResult = await waitForTmuxSessionBootstrap({
|
|
65332
65784
|
tmux: this.tmux,
|
|
65333
65785
|
sessionName: resolved.sessionName,
|
|
65334
65786
|
captureLines: resolved.stream.captureLines,
|
|
65335
|
-
startupDelayMs: resolved.runner.startupDelayMs
|
|
65787
|
+
startupDelayMs: resolved.runner.startupDelayMs,
|
|
65788
|
+
trustWorkspace: resolved.runner.trustWorkspace,
|
|
65789
|
+
readyPattern: resolved.runner.startupReadyPattern,
|
|
65790
|
+
blockers: resolved.runner.startupBlockers
|
|
65336
65791
|
});
|
|
65337
65792
|
const sessionStillExists = await this.tmux.hasSession(resolved.sessionName);
|
|
65338
65793
|
if (!sessionStillExists) {
|
|
@@ -65347,6 +65802,12 @@ class RunnerSessionService {
|
|
|
65347
65802
|
}
|
|
65348
65803
|
throw new Error(`Runner session "${resolved.sessionName}" disappeared during startup.`);
|
|
65349
65804
|
}
|
|
65805
|
+
if (bootstrapResult.status === "blocked") {
|
|
65806
|
+
await this.abortUnreadySession(resolved, bootstrapResult.message, bootstrapResult.snapshot);
|
|
65807
|
+
}
|
|
65808
|
+
if (bootstrapResult.status === "timeout" && resolved.runner.startupReadyPattern) {
|
|
65809
|
+
await this.abortUnreadySession(resolved, `Runner session "${resolved.sessionName}" did not reach the configured ready state within ${resolved.runner.startupDelayMs}ms.`, bootstrapResult.snapshot);
|
|
65810
|
+
}
|
|
65350
65811
|
try {
|
|
65351
65812
|
await this.finalizeSessionStartup(target, resolved, {
|
|
65352
65813
|
startupSessionId,
|
|
@@ -65355,7 +65816,7 @@ class RunnerSessionService {
|
|
|
65355
65816
|
allowFreshRetry: options.allowFreshRetry
|
|
65356
65817
|
});
|
|
65357
65818
|
} catch (error) {
|
|
65358
|
-
throw this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
65819
|
+
throw await this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
65359
65820
|
}
|
|
65360
65821
|
logLatencyDebug("ensure-session-ready-complete", timingContext, {
|
|
65361
65822
|
startupDelayMs: resolved.runner.startupDelayMs,
|
|
@@ -65424,14 +65885,14 @@ class RunnerSessionService {
|
|
|
65424
65885
|
} catch (error) {
|
|
65425
65886
|
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
65426
65887
|
if (options.allowFreshRetryBeforePrompt === false || !existing?.sessionId || !isMissingTmuxSessionError(error)) {
|
|
65427
|
-
throw this.mapSessionError(error, resolved.sessionName, "before prompt submission");
|
|
65888
|
+
throw await this.mapSessionError(error, resolved.sessionName, "before prompt submission", resolved.sessionName ? await this.captureSessionSnapshot(resolved).catch(() => "") : "");
|
|
65428
65889
|
}
|
|
65429
65890
|
const retried = await this.retryFreshStartWithClearedSessionId(target, resolved, {
|
|
65430
65891
|
allowRetry: true,
|
|
65431
65892
|
nextAllowFreshRetry: false
|
|
65432
65893
|
});
|
|
65433
65894
|
if (!retried) {
|
|
65434
|
-
throw this.mapSessionError(error, resolved.sessionName, "before prompt submission");
|
|
65895
|
+
throw await this.mapSessionError(error, resolved.sessionName, "before prompt submission", resolved.sessionName ? await this.captureSessionSnapshot(resolved).catch(() => "") : "");
|
|
65435
65896
|
}
|
|
65436
65897
|
resolved = retried;
|
|
65437
65898
|
return {
|
|
@@ -65550,8 +66011,8 @@ class RunnerSessionService {
|
|
|
65550
66011
|
workspacePath: resolved.workspacePath
|
|
65551
66012
|
};
|
|
65552
66013
|
}
|
|
65553
|
-
mapRunError(error, sessionName) {
|
|
65554
|
-
return this.mapSessionError(error, sessionName, "while the prompt was running");
|
|
66014
|
+
async mapRunError(error, sessionName, lastSnapshot = "") {
|
|
66015
|
+
return await this.mapSessionError(error, sessionName, "while the prompt was running", lastSnapshot);
|
|
65555
66016
|
}
|
|
65556
66017
|
}
|
|
65557
66018
|
|
|
@@ -65710,6 +66171,7 @@ class ActiveRunManager {
|
|
|
65710
66171
|
runnerSessions;
|
|
65711
66172
|
resolveTarget;
|
|
65712
66173
|
activeRuns = new Map;
|
|
66174
|
+
stopping = false;
|
|
65713
66175
|
constructor(tmux, sessionState, runnerSessions, resolveTarget) {
|
|
65714
66176
|
this.tmux = tmux;
|
|
65715
66177
|
this.sessionState = sessionState;
|
|
@@ -65759,6 +66221,9 @@ class ActiveRunManager {
|
|
|
65759
66221
|
}
|
|
65760
66222
|
}
|
|
65761
66223
|
async executePrompt(target, prompt, observer, options = {}) {
|
|
66224
|
+
if (this.stopping) {
|
|
66225
|
+
throw new Error("Runtime is stopping and cannot accept a new prompt.");
|
|
66226
|
+
}
|
|
65762
66227
|
const existingActiveRun = this.activeRuns.get(target.sessionKey);
|
|
65763
66228
|
if (existingActiveRun) {
|
|
65764
66229
|
throw new ActiveRunInProgressError(existingActiveRun.latestUpdate);
|
|
@@ -65804,6 +66269,9 @@ class ActiveRunManager {
|
|
|
65804
66269
|
const startedAt = Date.now();
|
|
65805
66270
|
const run = this.activeRuns.get(provisionalResolved.sessionKey);
|
|
65806
66271
|
if (!run) {
|
|
66272
|
+
if (this.stopping) {
|
|
66273
|
+
throw new Error("Runtime stopped before the active run finished startup.");
|
|
66274
|
+
}
|
|
65807
66275
|
throw new Error(`Active run disappeared during startup for ${provisionalResolved.sessionKey}.`);
|
|
65808
66276
|
}
|
|
65809
66277
|
run.resolved = resolved;
|
|
@@ -65880,6 +66348,16 @@ class ActiveRunManager {
|
|
|
65880
66348
|
hasActiveRun(target) {
|
|
65881
66349
|
return this.activeRuns.has(target.sessionKey);
|
|
65882
66350
|
}
|
|
66351
|
+
async stop() {
|
|
66352
|
+
this.stopping = true;
|
|
66353
|
+
const activeRuns = [...this.activeRuns.values()];
|
|
66354
|
+
for (const run of activeRuns) {
|
|
66355
|
+
await this.sessionState.setSessionRuntime(run.resolved, {
|
|
66356
|
+
state: "idle"
|
|
66357
|
+
});
|
|
66358
|
+
}
|
|
66359
|
+
this.activeRuns.clear();
|
|
66360
|
+
}
|
|
65883
66361
|
buildDetachedNote(resolved) {
|
|
65884
66362
|
return `This session has been running for over ${resolved.stream.maxRuntimeLabel}. clisbot will keep monitoring it and will 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.`;
|
|
65885
66363
|
}
|
|
@@ -66046,7 +66524,7 @@ class ActiveRunManager {
|
|
|
66046
66524
|
}
|
|
66047
66525
|
});
|
|
66048
66526
|
} catch (error) {
|
|
66049
|
-
await this.failActiveRun(sessionKey, this.runnerSessions.mapRunError(error, run.resolved.sessionName));
|
|
66527
|
+
await this.failActiveRun(sessionKey, await this.runnerSessions.mapRunError(error, run.resolved.sessionName, run.latestUpdate.fullSnapshot));
|
|
66050
66528
|
}
|
|
66051
66529
|
})();
|
|
66052
66530
|
}
|
|
@@ -66060,6 +66538,7 @@ class AgentService {
|
|
|
66060
66538
|
sessionState;
|
|
66061
66539
|
runnerSessions;
|
|
66062
66540
|
activeRuns;
|
|
66541
|
+
stopping = false;
|
|
66063
66542
|
cleanupTimer;
|
|
66064
66543
|
loopTimers = new Set;
|
|
66065
66544
|
intervalLoops = new Map;
|
|
@@ -66095,6 +66574,7 @@ class AgentService {
|
|
|
66095
66574
|
}, cleanup.intervalMinutes * 60000);
|
|
66096
66575
|
}
|
|
66097
66576
|
async stop() {
|
|
66577
|
+
this.stopping = true;
|
|
66098
66578
|
if (this.cleanupTimer) {
|
|
66099
66579
|
clearInterval(this.cleanupTimer);
|
|
66100
66580
|
this.cleanupTimer = undefined;
|
|
@@ -66109,6 +66589,7 @@ class AgentService {
|
|
|
66109
66589
|
clearTimeout(timer);
|
|
66110
66590
|
}
|
|
66111
66591
|
this.loopTimers.clear();
|
|
66592
|
+
await this.activeRuns.stop();
|
|
66112
66593
|
}
|
|
66113
66594
|
async cleanupStaleSessions() {
|
|
66114
66595
|
await this.runnerSessions.runSessionCleanup();
|
|
@@ -66149,8 +66630,8 @@ class AgentService {
|
|
|
66149
66630
|
sessionKey: this.loadedConfig.raw.session.mainKey
|
|
66150
66631
|
});
|
|
66151
66632
|
}
|
|
66152
|
-
async recordConversationReply(target, kind = "reply") {
|
|
66153
|
-
return this.sessionState.recordConversationReply(this.resolveTarget(target), kind);
|
|
66633
|
+
async recordConversationReply(target, kind = "reply", source = "channel") {
|
|
66634
|
+
return this.sessionState.recordConversationReply(this.resolveTarget(target), kind, source);
|
|
66154
66635
|
}
|
|
66155
66636
|
async runShellCommand(target, command) {
|
|
66156
66637
|
return this.queue.enqueue(`${target.sessionKey}:bash`, async () => this.runnerSessions.runShellCommand(target, command)).result;
|
|
@@ -66207,10 +66688,13 @@ class AgentService {
|
|
|
66207
66688
|
updatedAt: Date.now(),
|
|
66208
66689
|
nextRunAt: Date.now(),
|
|
66209
66690
|
promptText: params.promptText,
|
|
66691
|
+
canonicalPromptText: params.canonicalPromptText,
|
|
66692
|
+
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
66210
66693
|
promptSummary: params.promptSummary,
|
|
66211
66694
|
promptSource: params.promptSource,
|
|
66212
66695
|
createdBy: params.createdBy,
|
|
66213
|
-
force: params.force
|
|
66696
|
+
force: params.force,
|
|
66697
|
+
surfaceBinding: params.surfaceBinding
|
|
66214
66698
|
};
|
|
66215
66699
|
const resolved = this.resolveTarget(params.target);
|
|
66216
66700
|
await this.sessionState.setIntervalLoop(resolved, loop);
|
|
@@ -66248,6 +66732,8 @@ class AgentService {
|
|
|
66248
66732
|
updatedAt: Date.now(),
|
|
66249
66733
|
nextRunAt,
|
|
66250
66734
|
promptText: params.promptText,
|
|
66735
|
+
canonicalPromptText: params.canonicalPromptText,
|
|
66736
|
+
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
66251
66737
|
promptSummary: params.promptSummary,
|
|
66252
66738
|
promptSource: params.promptSource,
|
|
66253
66739
|
createdBy: params.createdBy,
|
|
@@ -66257,7 +66743,8 @@ class AgentService {
|
|
|
66257
66743
|
hour: params.hour,
|
|
66258
66744
|
minute: params.minute,
|
|
66259
66745
|
timezone: params.timezone,
|
|
66260
|
-
force: false
|
|
66746
|
+
force: false,
|
|
66747
|
+
surfaceBinding: params.surfaceBinding
|
|
66261
66748
|
};
|
|
66262
66749
|
const resolved = this.resolveTarget(params.target);
|
|
66263
66750
|
await this.sessionState.setIntervalLoop(resolved, loop);
|
|
@@ -66413,13 +66900,17 @@ class AgentService {
|
|
|
66413
66900
|
this.dropManagedIntervalLoop(loopId);
|
|
66414
66901
|
return;
|
|
66415
66902
|
}
|
|
66416
|
-
const
|
|
66903
|
+
const promptText = this.buildManagedLoopPrompt(managed.target.agentId, nextLoopState);
|
|
66904
|
+
const { result } = this.enqueuePrompt(managed.target, promptText, {
|
|
66417
66905
|
observerId: `loop:${loopId}:${attemptedRuns}`,
|
|
66418
66906
|
onUpdate: async () => {
|
|
66419
66907
|
return;
|
|
66420
66908
|
}
|
|
66421
66909
|
});
|
|
66422
66910
|
result.catch((error) => {
|
|
66911
|
+
if (this.shouldSuppressLoopShutdownError(error)) {
|
|
66912
|
+
return;
|
|
66913
|
+
}
|
|
66423
66914
|
console.error("loop execution failed", error);
|
|
66424
66915
|
});
|
|
66425
66916
|
if (attemptedRuns >= managed.loop.maxRuns) {
|
|
@@ -66445,6 +66936,9 @@ class AgentService {
|
|
|
66445
66936
|
}
|
|
66446
66937
|
current.timer = undefined;
|
|
66447
66938
|
this.runIntervalLoopIteration(loopId).catch((error) => {
|
|
66939
|
+
if (this.shouldSuppressLoopShutdownError(error)) {
|
|
66940
|
+
return;
|
|
66941
|
+
}
|
|
66448
66942
|
console.error("loop execution failed", error);
|
|
66449
66943
|
});
|
|
66450
66944
|
}, delayMs);
|
|
@@ -66459,6 +66953,13 @@ class AgentService {
|
|
|
66459
66953
|
managed.loop = nextLoopState;
|
|
66460
66954
|
return true;
|
|
66461
66955
|
}
|
|
66956
|
+
shouldSuppressLoopShutdownError(error) {
|
|
66957
|
+
if (!this.stopping) {
|
|
66958
|
+
return false;
|
|
66959
|
+
}
|
|
66960
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66961
|
+
return /Runtime stopped before the active run finished startup|Runtime is stopping and cannot accept a new prompt/i.test(message);
|
|
66962
|
+
}
|
|
66462
66963
|
computeNextManagedLoopRunAtMs(loop, nowMs) {
|
|
66463
66964
|
if (loop.kind === "calendar") {
|
|
66464
66965
|
return computeNextCalendarLoopRunAtMs({
|
|
@@ -66472,6 +66973,52 @@ class AgentService {
|
|
|
66472
66973
|
}
|
|
66473
66974
|
return nowMs + loop.intervalMs;
|
|
66474
66975
|
}
|
|
66976
|
+
buildManagedLoopPrompt(agentId, loop) {
|
|
66977
|
+
if (!loop.canonicalPromptText || !loop.surfaceBinding) {
|
|
66978
|
+
return loop.promptText;
|
|
66979
|
+
}
|
|
66980
|
+
const identity = this.buildLoopChannelIdentity(loop.surfaceBinding);
|
|
66981
|
+
const channelConfig = identity.platform === "slack" ? this.loadedConfig.raw.channels.slack : this.loadedConfig.raw.channels.telegram;
|
|
66982
|
+
const { responseMode, streaming } = this.resolveLoopSurfaceModes(identity);
|
|
66983
|
+
return buildAgentPromptText({
|
|
66984
|
+
text: loop.canonicalPromptText,
|
|
66985
|
+
identity,
|
|
66986
|
+
config: channelConfig.agentPrompt,
|
|
66987
|
+
cliTool: getAgentEntry(this.loadedConfig, agentId)?.cliTool,
|
|
66988
|
+
responseMode,
|
|
66989
|
+
streaming,
|
|
66990
|
+
protectedControlMutationRule: loop.protectedControlMutationRule
|
|
66991
|
+
});
|
|
66992
|
+
}
|
|
66993
|
+
buildLoopChannelIdentity(binding) {
|
|
66994
|
+
return {
|
|
66995
|
+
platform: binding.platform,
|
|
66996
|
+
conversationKind: binding.conversationKind,
|
|
66997
|
+
channelId: binding.channelId,
|
|
66998
|
+
chatId: binding.chatId,
|
|
66999
|
+
threadTs: binding.threadTs,
|
|
67000
|
+
topicId: binding.topicId
|
|
67001
|
+
};
|
|
67002
|
+
}
|
|
67003
|
+
resolveLoopSurfaceModes(identity) {
|
|
67004
|
+
const channelConfig = identity.platform === "slack" ? this.loadedConfig.raw.channels.slack : this.loadedConfig.raw.channels.telegram;
|
|
67005
|
+
let responseMode = channelConfig.responseMode;
|
|
67006
|
+
let streaming = channelConfig.streaming;
|
|
67007
|
+
if (identity.conversationKind === "dm") {
|
|
67008
|
+
responseMode = channelConfig.directMessages.responseMode ?? responseMode;
|
|
67009
|
+
streaming = channelConfig.directMessages.streaming ?? streaming;
|
|
67010
|
+
}
|
|
67011
|
+
try {
|
|
67012
|
+
responseMode = resolveConfiguredSurfaceModeTarget(this.loadedConfig.raw, "responseMode", buildConfiguredTargetFromIdentity(identity)).get() ?? responseMode;
|
|
67013
|
+
} catch {}
|
|
67014
|
+
try {
|
|
67015
|
+
streaming = resolveConfiguredSurfaceModeTarget(this.loadedConfig.raw, "streaming", buildConfiguredTargetFromIdentity(identity)).get() ?? streaming;
|
|
67016
|
+
} catch {}
|
|
67017
|
+
return {
|
|
67018
|
+
responseMode,
|
|
67019
|
+
streaming
|
|
67020
|
+
};
|
|
67021
|
+
}
|
|
66475
67022
|
}
|
|
66476
67023
|
|
|
66477
67024
|
// src/config/channel-accounts.ts
|
|
@@ -66794,6 +67341,37 @@ function parseAgentCommand(text, options = {}) {
|
|
|
66794
67341
|
action: "status"
|
|
66795
67342
|
};
|
|
66796
67343
|
}
|
|
67344
|
+
if (lowered === "streaming") {
|
|
67345
|
+
const action = withoutSlash.slice(command.length).trim().toLowerCase();
|
|
67346
|
+
if (!action || action === "status") {
|
|
67347
|
+
return {
|
|
67348
|
+
type: "control",
|
|
67349
|
+
name: "streaming",
|
|
67350
|
+
action: "status"
|
|
67351
|
+
};
|
|
67352
|
+
}
|
|
67353
|
+
if (action === "on") {
|
|
67354
|
+
return {
|
|
67355
|
+
type: "control",
|
|
67356
|
+
name: "streaming",
|
|
67357
|
+
action: "on",
|
|
67358
|
+
streaming: "all"
|
|
67359
|
+
};
|
|
67360
|
+
}
|
|
67361
|
+
if (action === "off" || action === "latest" || action === "all") {
|
|
67362
|
+
return {
|
|
67363
|
+
type: "control",
|
|
67364
|
+
name: "streaming",
|
|
67365
|
+
action,
|
|
67366
|
+
streaming: action
|
|
67367
|
+
};
|
|
67368
|
+
}
|
|
67369
|
+
return {
|
|
67370
|
+
type: "control",
|
|
67371
|
+
name: "streaming",
|
|
67372
|
+
action: "status"
|
|
67373
|
+
};
|
|
67374
|
+
}
|
|
66797
67375
|
if (lowered === "additionalmessagemode") {
|
|
66798
67376
|
const action = withoutSlash.slice(command.length).trim().toLowerCase();
|
|
66799
67377
|
if (!action || action === "status") {
|
|
@@ -66922,7 +67500,7 @@ function renderAgentControlSlashHelp() {
|
|
|
66922
67500
|
"- `/status`: show the current route status and operator setup commands",
|
|
66923
67501
|
"- `/help`: show available control slash commands",
|
|
66924
67502
|
"- `/whoami`: show the current platform, route, and sender identity details",
|
|
66925
|
-
"- `/transcript`: show the current conversation session transcript when the route
|
|
67503
|
+
"- `/transcript`: show the current conversation session transcript when the route verbose policy allows it",
|
|
66926
67504
|
"- `/attach`: attach this thread to the active run and resume live updates when it is still processing",
|
|
66927
67505
|
"- `/detach`: stop live updates for this thread while still allowing final settlement here",
|
|
66928
67506
|
"- `/watch every 30s [for 10m]`: post the latest state on an interval until the run settles or the watch window ends",
|
|
@@ -66933,6 +67511,11 @@ function renderAgentControlSlashHelp() {
|
|
|
66933
67511
|
"- `/followup mention-only`: require explicit mention for each later turn",
|
|
66934
67512
|
"- `/followup pause`: stop passive follow-up until the next explicit mention",
|
|
66935
67513
|
"- `/followup resume`: clear the runtime override and restore config defaults",
|
|
67514
|
+
"- `/streaming status`: show the configured streaming mode for this surface",
|
|
67515
|
+
"- `/streaming on`: enable streaming for this surface using the current `all` preview behavior",
|
|
67516
|
+
"- `/streaming off`: disable surface streaming previews for this surface",
|
|
67517
|
+
"- `/streaming latest`: prefer the latest preview shape for this surface",
|
|
67518
|
+
"- `/streaming all`: retain the full current preview shape for this surface",
|
|
66936
67519
|
"- `/responsemode status`: show the configured response mode for this surface",
|
|
66937
67520
|
"- `/responsemode capture-pane`: settle replies from captured pane output for this surface",
|
|
66938
67521
|
"- `/responsemode message-tool`: expect the agent to reply through `clisbot message send` for this surface",
|
|
@@ -66944,8 +67527,8 @@ function renderAgentControlSlashHelp() {
|
|
|
66944
67527
|
"- `/queue-list`: show queued messages that have not started yet",
|
|
66945
67528
|
"- `/queue-clear`: clear queued messages that have not started yet",
|
|
66946
67529
|
...renderLoopHelpLines(),
|
|
66947
|
-
"- `/bash` followed by a shell command: requires `
|
|
66948
|
-
"- shortcut prefixes such as `!` run bash when the
|
|
67530
|
+
"- `/bash` followed by a shell command: requires `shellExecute` on the resolved agent role",
|
|
67531
|
+
"- shortcut prefixes such as `!` run bash only when the resolved agent role allows `shellExecute`",
|
|
66949
67532
|
"",
|
|
66950
67533
|
"Other slash commands are forwarded to the agent unchanged."
|
|
66951
67534
|
].join(`
|
|
@@ -66982,6 +67565,7 @@ function buildRenderedMessageState(params) {
|
|
|
66982
67565
|
queuePosition: params.queuePosition,
|
|
66983
67566
|
maxChars: params.maxChars,
|
|
66984
67567
|
note: params.note,
|
|
67568
|
+
allowTranscriptInspection: params.allowTranscriptInspection,
|
|
66985
67569
|
responsePolicy: params.responsePolicy
|
|
66986
67570
|
}),
|
|
66987
67571
|
body
|
|
@@ -67011,35 +67595,45 @@ function formatChannelFollowUpStatus(params) {
|
|
|
67011
67595
|
`);
|
|
67012
67596
|
}
|
|
67013
67597
|
|
|
67014
|
-
// src/channels/
|
|
67015
|
-
function
|
|
67598
|
+
// src/channels/streaming-config.ts
|
|
67599
|
+
function getEditableConfigPath7() {
|
|
67600
|
+
return process.env.CLISBOT_CONFIG_PATH;
|
|
67601
|
+
}
|
|
67602
|
+
async function getConversationStreaming(params) {
|
|
67603
|
+
const { config } = await readEditableConfig(getEditableConfigPath7());
|
|
67604
|
+
const target = resolveConfiguredSurfaceModeTarget(config, "streaming", buildConfiguredTargetFromIdentity(params.identity));
|
|
67016
67605
|
return {
|
|
67017
|
-
|
|
67018
|
-
|
|
67606
|
+
label: target.label,
|
|
67607
|
+
streaming: target.get()
|
|
67019
67608
|
};
|
|
67020
67609
|
}
|
|
67021
|
-
function
|
|
67022
|
-
|
|
67023
|
-
|
|
67024
|
-
|
|
67025
|
-
|
|
67026
|
-
|
|
67027
|
-
|
|
67028
|
-
|
|
67029
|
-
|
|
67030
|
-
|
|
67031
|
-
}
|
|
67032
|
-
return params.config.allowUsers.includes(normalizedUserId);
|
|
67610
|
+
async function setConversationStreaming(params) {
|
|
67611
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath7());
|
|
67612
|
+
const target = resolveConfiguredSurfaceModeTarget(config, "streaming", buildConfiguredTargetFromIdentity(params.identity));
|
|
67613
|
+
target.set(params.streaming);
|
|
67614
|
+
await writeEditableConfig(configPath, config);
|
|
67615
|
+
return {
|
|
67616
|
+
configPath,
|
|
67617
|
+
label: target.label,
|
|
67618
|
+
streaming: params.streaming
|
|
67619
|
+
};
|
|
67033
67620
|
}
|
|
67034
67621
|
|
|
67035
67622
|
// src/channels/interaction-processing.ts
|
|
67036
|
-
import { join as
|
|
67037
|
-
|
|
67623
|
+
import { join as join8 } from "node:path";
|
|
67624
|
+
var MESSAGE_TOOL_FINAL_GRACE_WINDOW_MS = 3000;
|
|
67625
|
+
var MESSAGE_TOOL_FINAL_GRACE_POLL_MS = 100;
|
|
67626
|
+
function renderSensitiveCommandDisabledMessage() {
|
|
67038
67627
|
return [
|
|
67039
|
-
"
|
|
67040
|
-
"
|
|
67041
|
-
|
|
67042
|
-
|
|
67628
|
+
"Shell execution is not allowed for your current role on this agent.",
|
|
67629
|
+
"Ask an app or agent admin to grant `shellExecute` if this surface should allow `/bash`."
|
|
67630
|
+
].join(`
|
|
67631
|
+
`);
|
|
67632
|
+
}
|
|
67633
|
+
function renderTranscriptDisabledMessage() {
|
|
67634
|
+
return [
|
|
67635
|
+
"Transcript inspection is disabled for this route.",
|
|
67636
|
+
'Set `verbose: "minimal"` on the route or channel to allow `/transcript`.'
|
|
67043
67637
|
].join(`
|
|
67044
67638
|
`);
|
|
67045
67639
|
}
|
|
@@ -67067,7 +67661,7 @@ function renderWhoAmIMessage(params) {
|
|
|
67067
67661
|
if (params.identity.topicId) {
|
|
67068
67662
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
67069
67663
|
}
|
|
67070
|
-
lines.push(`
|
|
67664
|
+
lines.push(`principal: \`${params.auth.principal ?? "(none)"}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayBypassPairing: \`${params.auth.mayBypassPairing}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `verbose: \`${params.route.verbose}\``);
|
|
67071
67665
|
return lines.join(`
|
|
67072
67666
|
`);
|
|
67073
67667
|
}
|
|
@@ -67095,7 +67689,7 @@ function renderRouteStatusMessage(params) {
|
|
|
67095
67689
|
if (params.identity.topicId) {
|
|
67096
67690
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
67097
67691
|
}
|
|
67098
|
-
lines.push(`streaming: \`${params.route.streaming}\``, `response: \`${params.route.response}\``, `responseMode: \`${params.route.responseMode}\``, `additionalMessageMode: \`${params.route.additionalMessageMode}\``, `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}
|
|
67692
|
+
lines.push(`streaming: \`${params.route.streaming}\``, `response: \`${params.route.response}\``, `responseMode: \`${params.route.responseMode}\``, `additionalMessageMode: \`${params.route.additionalMessageMode}\``, `verbose: \`${params.route.verbose}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `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}\``);
|
|
67099
67693
|
if (params.runtimeState.startedAt) {
|
|
67100
67694
|
lines.push(`run.startedAt: \`${new Date(params.runtimeState.startedAt).toISOString()}\``);
|
|
67101
67695
|
}
|
|
@@ -67109,11 +67703,13 @@ function renderRouteStatusMessage(params) {
|
|
|
67109
67703
|
lines.push(`- \`${loop.id}\` ${renderLoopStatusSchedule(loop)} remaining \`${loop.remainingRuns}\` nextRunAt \`${new Date(loop.nextRunAt).toISOString()}\``);
|
|
67110
67704
|
}
|
|
67111
67705
|
}
|
|
67112
|
-
lines.push("", "Useful commands:", "- `/help`", "- `/whoami`", "- `/status`", "- `/attach`, `/detach`, `/watch every 30s`", "- `/followup status`", "- `/responsemode status`", "- `/additionalmessagemode status`", "- `/loop status`, `/loop cancel`, `/loop cancel <id>`", "- `/queue <message>`, `/steer <message>`", "- `/queue-list`, `/queue-clear`", "- `/transcript`
|
|
67113
|
-
lines.push("", ...renderPrivilegeCommandHelpLines(params.identity));
|
|
67706
|
+
lines.push("", "Useful commands:", "- `/help`", "- `/whoami`", "- `/status`", "- `/attach`, `/detach`, `/watch every 30s`", "- `/followup status`", "- `/streaming status`", "- `/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`");
|
|
67114
67707
|
return lines.join(`
|
|
67115
67708
|
`);
|
|
67116
67709
|
}
|
|
67710
|
+
function allowTranscriptInspectionForRoute(route) {
|
|
67711
|
+
return route.verbose === "minimal";
|
|
67712
|
+
}
|
|
67117
67713
|
function renderResponseModeStatusMessage(params) {
|
|
67118
67714
|
const lines = [
|
|
67119
67715
|
"clisbot response mode",
|
|
@@ -67128,6 +67724,18 @@ function renderResponseModeStatusMessage(params) {
|
|
|
67128
67724
|
return lines.join(`
|
|
67129
67725
|
`);
|
|
67130
67726
|
}
|
|
67727
|
+
function renderStreamingStatusMessage(params) {
|
|
67728
|
+
const lines = [
|
|
67729
|
+
`clisbot streaming mode: \`${params.route.streaming}\``
|
|
67730
|
+
];
|
|
67731
|
+
if (params.persisted) {
|
|
67732
|
+
lines.push("");
|
|
67733
|
+
lines.push(`config.target: \`${params.persisted.label}\``);
|
|
67734
|
+
}
|
|
67735
|
+
lines.push("", "Available values:", "- `off`: do not show live surface preview updates", "- `on`: slash-command shorthand that persists as `all`", "- `all`: keep streaming enabled with the current full preview behavior", "- `latest`: keep streaming enabled; current runtime behavior still matches `all` until preview shaping is refined");
|
|
67736
|
+
return lines.join(`
|
|
67737
|
+
`);
|
|
67738
|
+
}
|
|
67131
67739
|
function renderAdditionalMessageModeStatusMessage(params) {
|
|
67132
67740
|
const lines = [
|
|
67133
67741
|
"clisbot additional message mode",
|
|
@@ -67153,14 +67761,22 @@ function buildChannelObserverId(identity) {
|
|
|
67153
67761
|
identity.topicId ?? ""
|
|
67154
67762
|
].join(":");
|
|
67155
67763
|
}
|
|
67156
|
-
function buildSteeringMessage(text) {
|
|
67764
|
+
function buildSteeringMessage(text, protectedControlMutationRule) {
|
|
67765
|
+
const systemLines = [
|
|
67766
|
+
"A new user message arrived while you were still working.",
|
|
67767
|
+
"Adjust your current work if needed and continue."
|
|
67768
|
+
];
|
|
67769
|
+
if (protectedControlMutationRule) {
|
|
67770
|
+
systemLines.push("", protectedControlMutationRule);
|
|
67771
|
+
}
|
|
67157
67772
|
return [
|
|
67773
|
+
"<system>",
|
|
67774
|
+
...systemLines,
|
|
67775
|
+
"</system>",
|
|
67158
67776
|
"",
|
|
67159
|
-
"
|
|
67160
|
-
|
|
67161
|
-
"
|
|
67162
|
-
"",
|
|
67163
|
-
text
|
|
67777
|
+
"<user>",
|
|
67778
|
+
text,
|
|
67779
|
+
"</user>"
|
|
67164
67780
|
].join(`
|
|
67165
67781
|
`);
|
|
67166
67782
|
}
|
|
@@ -67294,7 +67910,7 @@ async function resolveLoopPromptText(params) {
|
|
|
67294
67910
|
};
|
|
67295
67911
|
}
|
|
67296
67912
|
const workspacePath = params.agentService.getWorkspacePath(params.sessionTarget);
|
|
67297
|
-
const loopPromptPath =
|
|
67913
|
+
const loopPromptPath = join8(workspacePath, "LOOP.md");
|
|
67298
67914
|
if (!await fileExists(loopPromptPath)) {
|
|
67299
67915
|
throw new Error(`No loop prompt was provided and LOOP.md was not found in \`${workspacePath}\`. Create LOOP.md there if you want maintenance loops.`);
|
|
67300
67916
|
}
|
|
@@ -67307,29 +67923,93 @@ async function resolveLoopPromptText(params) {
|
|
|
67307
67923
|
maintenancePrompt: true
|
|
67308
67924
|
};
|
|
67309
67925
|
}
|
|
67926
|
+
function buildLoopSurfaceBinding(identity) {
|
|
67927
|
+
return {
|
|
67928
|
+
platform: identity.platform,
|
|
67929
|
+
conversationKind: identity.conversationKind,
|
|
67930
|
+
channelId: identity.channelId,
|
|
67931
|
+
chatId: identity.chatId,
|
|
67932
|
+
threadTs: identity.threadTs,
|
|
67933
|
+
topicId: identity.topicId
|
|
67934
|
+
};
|
|
67935
|
+
}
|
|
67310
67936
|
async function executePromptDelivery(params) {
|
|
67311
67937
|
let responseChunks = [];
|
|
67312
67938
|
let renderedState;
|
|
67313
67939
|
let renderChain = Promise.resolve();
|
|
67314
67940
|
let replyRecorded = false;
|
|
67941
|
+
let finalReplyRecorded = false;
|
|
67315
67942
|
let loggedFirstRunningUpdate = false;
|
|
67316
|
-
|
|
67317
|
-
|
|
67943
|
+
let activePreviewStartedAt;
|
|
67944
|
+
let lastFrozenPreviewText;
|
|
67945
|
+
const paneManagedDelivery = params.route.responseMode === "capture-pane" || params.forceQueuedDelivery === true;
|
|
67946
|
+
const messageToolPreview = params.route.responseMode === "message-tool" && params.forceQueuedDelivery !== true && params.route.streaming !== "off";
|
|
67947
|
+
const previewEnabled = params.route.streaming !== "off" && (paneManagedDelivery || messageToolPreview);
|
|
67948
|
+
async function recordVisibleReply(kind = "reply", source = "channel") {
|
|
67949
|
+
if (kind === "final") {
|
|
67950
|
+
if (finalReplyRecorded) {
|
|
67951
|
+
return;
|
|
67952
|
+
}
|
|
67953
|
+
await params.agentService.recordConversationReply(params.sessionTarget, "final", source);
|
|
67954
|
+
finalReplyRecorded = true;
|
|
67955
|
+
replyRecorded = true;
|
|
67956
|
+
return;
|
|
67957
|
+
}
|
|
67318
67958
|
if (replyRecorded) {
|
|
67319
67959
|
return;
|
|
67320
67960
|
}
|
|
67321
|
-
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67961
|
+
await params.agentService.recordConversationReply(params.sessionTarget, "reply", source);
|
|
67322
67962
|
replyRecorded = true;
|
|
67323
67963
|
}
|
|
67324
67964
|
async function renderResponseText(nextText) {
|
|
67325
67965
|
if (!responseChunks.length) {
|
|
67326
67966
|
responseChunks = await params.postText(nextText);
|
|
67327
|
-
|
|
67328
|
-
await recordReplyIfNeeded();
|
|
67329
|
-
}
|
|
67330
|
-
return;
|
|
67967
|
+
return responseChunks.length > 0;
|
|
67331
67968
|
}
|
|
67332
67969
|
responseChunks = await params.reconcileText(responseChunks, nextText);
|
|
67970
|
+
return false;
|
|
67971
|
+
}
|
|
67972
|
+
async function clearResponseText() {
|
|
67973
|
+
if (!responseChunks.length) {
|
|
67974
|
+
return;
|
|
67975
|
+
}
|
|
67976
|
+
responseChunks = await params.reconcileText(responseChunks, "");
|
|
67977
|
+
renderedState = undefined;
|
|
67978
|
+
activePreviewStartedAt = undefined;
|
|
67979
|
+
}
|
|
67980
|
+
function resetPreviewBoundary() {
|
|
67981
|
+
lastFrozenPreviewText = renderedState?.text ?? lastFrozenPreviewText;
|
|
67982
|
+
responseChunks = [];
|
|
67983
|
+
renderedState = undefined;
|
|
67984
|
+
activePreviewStartedAt = undefined;
|
|
67985
|
+
}
|
|
67986
|
+
async function getMessageToolRuntimeSignals() {
|
|
67987
|
+
if (params.route.responseMode !== "message-tool" || params.forceQueuedDelivery === true) {
|
|
67988
|
+
return {
|
|
67989
|
+
lastMessageToolReplyAt: undefined,
|
|
67990
|
+
messageToolFinalReplyAt: undefined
|
|
67991
|
+
};
|
|
67992
|
+
}
|
|
67993
|
+
const runtime = await params.agentService.getSessionRuntime?.(params.sessionTarget);
|
|
67994
|
+
return {
|
|
67995
|
+
lastMessageToolReplyAt: runtime?.lastMessageToolReplyAt,
|
|
67996
|
+
messageToolFinalReplyAt: runtime?.messageToolFinalReplyAt
|
|
67997
|
+
};
|
|
67998
|
+
}
|
|
67999
|
+
async function waitForMessageToolFinalReply() {
|
|
68000
|
+
const deadline = Date.now() + MESSAGE_TOOL_FINAL_GRACE_WINDOW_MS;
|
|
68001
|
+
while (true) {
|
|
68002
|
+
const signals = await getMessageToolRuntimeSignals();
|
|
68003
|
+
const toolFinalSeen = typeof signals.messageToolFinalReplyAt === "number" && Number.isFinite(signals.messageToolFinalReplyAt);
|
|
68004
|
+
if (toolFinalSeen) {
|
|
68005
|
+
return true;
|
|
68006
|
+
}
|
|
68007
|
+
const remainingMs = deadline - Date.now();
|
|
68008
|
+
if (remainingMs <= 0) {
|
|
68009
|
+
return false;
|
|
68010
|
+
}
|
|
68011
|
+
await sleep(Math.min(MESSAGE_TOOL_FINAL_GRACE_POLL_MS, remainingMs));
|
|
68012
|
+
}
|
|
67333
68013
|
}
|
|
67334
68014
|
logLatencyDebug("channel-enqueue-start", params.timingContext, {
|
|
67335
68015
|
agentId: params.route.agentId,
|
|
@@ -67340,7 +68020,7 @@ async function executePromptDelivery(params) {
|
|
|
67340
68020
|
observerId: params.observerId,
|
|
67341
68021
|
timingContext: params.timingContext,
|
|
67342
68022
|
onUpdate: async (update) => {
|
|
67343
|
-
if (!
|
|
68023
|
+
if (!paneManagedDelivery && !messageToolPreview) {
|
|
67344
68024
|
return;
|
|
67345
68025
|
}
|
|
67346
68026
|
if (params.route.streaming === "off" && update.status === "running") {
|
|
@@ -67354,6 +68034,15 @@ async function executePromptDelivery(params) {
|
|
|
67354
68034
|
});
|
|
67355
68035
|
}
|
|
67356
68036
|
await (renderChain = renderChain.then(async () => {
|
|
68037
|
+
const signals = await getMessageToolRuntimeSignals();
|
|
68038
|
+
if (messageToolPreview && typeof activePreviewStartedAt === "number" && typeof signals.messageToolFinalReplyAt === "number" && signals.messageToolFinalReplyAt >= activePreviewStartedAt) {
|
|
68039
|
+
lastFrozenPreviewText = renderedState?.text ?? lastFrozenPreviewText;
|
|
68040
|
+
activePreviewStartedAt = undefined;
|
|
68041
|
+
return;
|
|
68042
|
+
}
|
|
68043
|
+
if (messageToolPreview && typeof activePreviewStartedAt === "number" && typeof signals.lastMessageToolReplyAt === "number" && signals.lastMessageToolReplyAt >= activePreviewStartedAt) {
|
|
68044
|
+
resetPreviewBoundary();
|
|
68045
|
+
}
|
|
67357
68046
|
const nextState2 = buildRenderedMessageState({
|
|
67358
68047
|
platform: params.identity.platform,
|
|
67359
68048
|
status: update.status,
|
|
@@ -67361,21 +68050,26 @@ async function executePromptDelivery(params) {
|
|
|
67361
68050
|
queuePosition: positionAhead,
|
|
67362
68051
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67363
68052
|
note: update.note,
|
|
68053
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
67364
68054
|
previousState: renderedState,
|
|
67365
68055
|
responsePolicy: params.route.response
|
|
67366
68056
|
});
|
|
67367
68057
|
if (renderedState?.text === nextState2.text) {
|
|
67368
68058
|
return;
|
|
67369
68059
|
}
|
|
67370
|
-
if (!
|
|
68060
|
+
if (!renderedState && lastFrozenPreviewText === nextState2.text) {
|
|
67371
68061
|
return;
|
|
67372
68062
|
}
|
|
67373
|
-
await renderResponseText(nextState2.text);
|
|
68063
|
+
const postedNew2 = await renderResponseText(nextState2.text);
|
|
68064
|
+
if (postedNew2) {
|
|
68065
|
+
await recordVisibleReply("reply", "channel");
|
|
68066
|
+
activePreviewStartedAt = Date.now();
|
|
68067
|
+
}
|
|
67374
68068
|
renderedState = nextState2;
|
|
67375
68069
|
}));
|
|
67376
68070
|
}
|
|
67377
68071
|
});
|
|
67378
|
-
if (
|
|
68072
|
+
if (previewEnabled) {
|
|
67379
68073
|
const placeholderText = renderPlatformInteraction({
|
|
67380
68074
|
platform: params.identity.platform,
|
|
67381
68075
|
status: positionAhead > 0 ? "queued" : "running",
|
|
@@ -67384,13 +68078,16 @@ async function executePromptDelivery(params) {
|
|
|
67384
68078
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67385
68079
|
note: positionAhead > 0 ? "Waiting for the agent queue to clear." : "Working..."
|
|
67386
68080
|
});
|
|
67387
|
-
|
|
67388
|
-
|
|
68081
|
+
const postedNew2 = await renderResponseText(placeholderText);
|
|
68082
|
+
if (postedNew2) {
|
|
68083
|
+
await recordVisibleReply("reply", "channel");
|
|
68084
|
+
activePreviewStartedAt = Date.now();
|
|
68085
|
+
}
|
|
67389
68086
|
renderedState = {
|
|
67390
68087
|
text: placeholderText,
|
|
67391
68088
|
body: ""
|
|
67392
68089
|
};
|
|
67393
|
-
} else if (
|
|
68090
|
+
} else if (paneManagedDelivery && positionAhead > 0 && params.route.streaming !== "off") {
|
|
67394
68091
|
const queuedText = renderPlatformInteraction({
|
|
67395
68092
|
platform: params.identity.platform,
|
|
67396
68093
|
status: "queued",
|
|
@@ -67399,8 +68096,10 @@ async function executePromptDelivery(params) {
|
|
|
67399
68096
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67400
68097
|
note: "Waiting for the agent queue to clear."
|
|
67401
68098
|
});
|
|
67402
|
-
|
|
67403
|
-
|
|
68099
|
+
const postedNew2 = await renderResponseText(queuedText);
|
|
68100
|
+
if (postedNew2) {
|
|
68101
|
+
await recordVisibleReply("reply", "channel");
|
|
68102
|
+
}
|
|
67404
68103
|
renderedState = {
|
|
67405
68104
|
text: queuedText,
|
|
67406
68105
|
body: ""
|
|
@@ -67408,55 +68107,86 @@ async function executePromptDelivery(params) {
|
|
|
67408
68107
|
}
|
|
67409
68108
|
const finalResult = await result;
|
|
67410
68109
|
await renderChain;
|
|
67411
|
-
if (!channelManagedDelivery) {
|
|
67412
|
-
if (finalResult.status !== "error") {
|
|
67413
|
-
return;
|
|
67414
|
-
}
|
|
67415
|
-
await params.postText(renderPlatformInteraction({
|
|
67416
|
-
platform: params.identity.platform,
|
|
67417
|
-
status: finalResult.status,
|
|
67418
|
-
content: finalResult.note ?? finalResult.snapshot,
|
|
67419
|
-
maxChars: Number.POSITIVE_INFINITY,
|
|
67420
|
-
note: finalResult.note,
|
|
67421
|
-
responsePolicy: "final"
|
|
67422
|
-
}));
|
|
67423
|
-
await recordReplyIfNeeded();
|
|
67424
|
-
return;
|
|
67425
|
-
}
|
|
67426
68110
|
const nextState = buildRenderedMessageState({
|
|
67427
68111
|
platform: params.identity.platform,
|
|
67428
68112
|
status: finalResult.status,
|
|
67429
68113
|
snapshot: finalResult.snapshot,
|
|
67430
68114
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67431
68115
|
note: finalResult.note,
|
|
68116
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
67432
68117
|
previousState: renderedState,
|
|
67433
68118
|
responsePolicy: params.route.response
|
|
67434
68119
|
});
|
|
67435
|
-
if (
|
|
67436
|
-
|
|
68120
|
+
if (paneManagedDelivery) {
|
|
68121
|
+
if (params.route.streaming === "off") {
|
|
68122
|
+
const postedNew3 = await renderResponseText(renderPlatformInteraction({
|
|
68123
|
+
platform: params.identity.platform,
|
|
68124
|
+
status: finalResult.status,
|
|
68125
|
+
content: nextState.body,
|
|
68126
|
+
maxChars: Number.POSITIVE_INFINITY,
|
|
68127
|
+
note: finalResult.note,
|
|
68128
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
68129
|
+
responsePolicy: params.route.response
|
|
68130
|
+
}));
|
|
68131
|
+
if (postedNew3 || finalResult.status === "completed") {
|
|
68132
|
+
await recordVisibleReply(finalResult.status === "completed" ? "final" : "reply", "channel");
|
|
68133
|
+
}
|
|
68134
|
+
return;
|
|
68135
|
+
}
|
|
68136
|
+
const postedNew2 = await renderResponseText(nextState.text);
|
|
68137
|
+
if (postedNew2) {
|
|
68138
|
+
await recordVisibleReply("reply", "channel");
|
|
68139
|
+
}
|
|
68140
|
+
if (finalResult.status === "completed") {
|
|
68141
|
+
await recordVisibleReply("final", "channel");
|
|
68142
|
+
}
|
|
68143
|
+
return;
|
|
68144
|
+
}
|
|
68145
|
+
const toolFinalSeen = finalResult.status === "completed" ? await waitForMessageToolFinalReply() : false;
|
|
68146
|
+
if (finalResult.status === "completed" && toolFinalSeen) {
|
|
68147
|
+
if (params.route.response === "final") {
|
|
68148
|
+
await clearResponseText();
|
|
68149
|
+
}
|
|
68150
|
+
return;
|
|
68151
|
+
}
|
|
68152
|
+
if (finalResult.status === "completed") {
|
|
68153
|
+
return;
|
|
68154
|
+
}
|
|
68155
|
+
if (params.route.streaming === "off" || responseChunks.length === 0) {
|
|
68156
|
+
const postedNew2 = await renderResponseText(renderPlatformInteraction({
|
|
67437
68157
|
platform: params.identity.platform,
|
|
67438
68158
|
status: finalResult.status,
|
|
67439
68159
|
content: nextState.body,
|
|
67440
68160
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67441
68161
|
note: finalResult.note,
|
|
67442
|
-
|
|
68162
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
68163
|
+
responsePolicy: params.route.response
|
|
67443
68164
|
}));
|
|
67444
|
-
|
|
68165
|
+
if (postedNew2) {
|
|
68166
|
+
await recordVisibleReply("reply", "channel");
|
|
68167
|
+
}
|
|
67445
68168
|
return;
|
|
67446
68169
|
}
|
|
67447
|
-
await renderResponseText(nextState.text);
|
|
68170
|
+
const postedNew = await renderResponseText(nextState.text);
|
|
68171
|
+
if (postedNew) {
|
|
68172
|
+
await recordVisibleReply("reply", "channel");
|
|
68173
|
+
}
|
|
67448
68174
|
} catch (error) {
|
|
67449
68175
|
if (error instanceof ClearedQueuedTaskError) {
|
|
67450
68176
|
return;
|
|
67451
68177
|
}
|
|
67452
68178
|
if (error instanceof ActiveRunInProgressError) {
|
|
67453
|
-
const activeText = error.update.
|
|
68179
|
+
const activeText = error.update.status === "detached" ? resolveDetachedInteractionNote({
|
|
68180
|
+
baseNote: error.update.note ?? String(error),
|
|
68181
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
68182
|
+
transcriptCommand: params.identity.platform === "telegram" ? "/transcript" : "`/transcript`"
|
|
68183
|
+
}) : error.update.note ?? String(error);
|
|
67454
68184
|
if (params.route.streaming !== "off" && responseChunks.length > 0) {
|
|
67455
68185
|
await params.reconcileText(responseChunks, activeText);
|
|
67456
68186
|
} else {
|
|
67457
68187
|
await params.postText(activeText);
|
|
67458
68188
|
}
|
|
67459
|
-
await
|
|
68189
|
+
await recordVisibleReply("reply", "channel");
|
|
67460
68190
|
return;
|
|
67461
68191
|
}
|
|
67462
68192
|
const errorText = renderPlatformInteraction({
|
|
@@ -67470,12 +68200,24 @@ async function executePromptDelivery(params) {
|
|
|
67470
68200
|
} else {
|
|
67471
68201
|
await params.postText(errorText);
|
|
67472
68202
|
}
|
|
68203
|
+
await recordVisibleReply("reply", "channel");
|
|
67473
68204
|
}
|
|
67474
68205
|
}
|
|
67475
68206
|
async function processChannelInteraction(params) {
|
|
68207
|
+
const interactionResult = {
|
|
68208
|
+
processingIndicatorLifecycle: "handler"
|
|
68209
|
+
};
|
|
67476
68210
|
let responseChunks = [];
|
|
67477
68211
|
let renderedState;
|
|
67478
68212
|
const observerId = buildChannelObserverId(params.identity);
|
|
68213
|
+
const auth = params.auth ?? {
|
|
68214
|
+
principal: params.senderId ? `${params.identity.platform}:${params.senderId}` : undefined,
|
|
68215
|
+
appRole: "member",
|
|
68216
|
+
agentRole: "member",
|
|
68217
|
+
mayBypassPairing: false,
|
|
68218
|
+
mayManageProtectedResources: false,
|
|
68219
|
+
canUseShell: false
|
|
68220
|
+
};
|
|
67479
68221
|
let replyRecorded = false;
|
|
67480
68222
|
let renderChain = Promise.resolve();
|
|
67481
68223
|
async function recordReplyIfNeeded() {
|
|
@@ -67532,14 +68274,12 @@ async function processChannelInteraction(params) {
|
|
|
67532
68274
|
const sessionBusy = await (params.agentService.isAwaitingFollowUpRouting?.(params.sessionTarget) ?? params.agentService.isSessionBusy?.(params.sessionTarget) ?? false);
|
|
67533
68275
|
const queueByMode = !explicitQueueMessage && params.route.additionalMessageMode === "queue" && sessionBusy;
|
|
67534
68276
|
const forceQueuedDelivery = typeof explicitQueueMessage === "string" || queueByMode;
|
|
67535
|
-
const
|
|
67536
|
-
|
|
67537
|
-
|
|
67538
|
-
|
|
67539
|
-
})) {
|
|
67540
|
-
await params.postText(renderSensitiveCommandDisabledMessage(params.identity));
|
|
68277
|
+
const delayedPromptText = explicitQueueMessage ? params.agentPromptBuilder ? params.agentPromptBuilder(explicitQueueMessage) : explicitQueueMessage : params.agentPromptText ?? params.text;
|
|
68278
|
+
const isSensitiveCommand = slashCommand?.type === "bash";
|
|
68279
|
+
if (isSensitiveCommand && !auth.canUseShell) {
|
|
68280
|
+
await params.postText(renderSensitiveCommandDisabledMessage());
|
|
67541
68281
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67542
|
-
return;
|
|
68282
|
+
return interactionResult;
|
|
67543
68283
|
}
|
|
67544
68284
|
if (slashCommand?.type === "control") {
|
|
67545
68285
|
if (slashCommand.name === "start" || slashCommand.name === "status") {
|
|
@@ -67551,6 +68291,7 @@ async function processChannelInteraction(params) {
|
|
|
67551
68291
|
await params.postText(renderRouteStatusMessage({
|
|
67552
68292
|
identity: params.identity,
|
|
67553
68293
|
route: params.route,
|
|
68294
|
+
auth,
|
|
67554
68295
|
sessionTarget: params.sessionTarget,
|
|
67555
68296
|
followUpState,
|
|
67556
68297
|
runtimeState,
|
|
@@ -67560,23 +68301,29 @@ async function processChannelInteraction(params) {
|
|
|
67560
68301
|
}
|
|
67561
68302
|
}));
|
|
67562
68303
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67563
|
-
return;
|
|
68304
|
+
return interactionResult;
|
|
67564
68305
|
}
|
|
67565
68306
|
if (slashCommand.name === "help") {
|
|
67566
68307
|
await params.postText(renderAgentControlSlashHelp());
|
|
67567
68308
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67568
|
-
return;
|
|
68309
|
+
return interactionResult;
|
|
67569
68310
|
}
|
|
67570
68311
|
if (slashCommand.name === "whoami") {
|
|
67571
68312
|
await params.postText(renderWhoAmIMessage({
|
|
67572
68313
|
identity: params.identity,
|
|
67573
68314
|
route: params.route,
|
|
68315
|
+
auth,
|
|
67574
68316
|
sessionTarget: params.sessionTarget
|
|
67575
68317
|
}));
|
|
67576
68318
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67577
|
-
return;
|
|
68319
|
+
return interactionResult;
|
|
67578
68320
|
}
|
|
67579
68321
|
if (slashCommand.name === "transcript") {
|
|
68322
|
+
if (params.route.verbose === "off") {
|
|
68323
|
+
await params.postText(renderTranscriptDisabledMessage());
|
|
68324
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68325
|
+
return interactionResult;
|
|
68326
|
+
}
|
|
67580
68327
|
const transcript = await params.agentService.captureTranscript(params.sessionTarget);
|
|
67581
68328
|
await params.postText(renderChannelSnapshot({
|
|
67582
68329
|
agentId: transcript.agentId,
|
|
@@ -67587,20 +68334,20 @@ async function processChannelInteraction(params) {
|
|
|
67587
68334
|
maxChars: params.maxChars,
|
|
67588
68335
|
note: "transcript command"
|
|
67589
68336
|
}));
|
|
67590
|
-
return;
|
|
68337
|
+
return interactionResult;
|
|
67591
68338
|
}
|
|
67592
68339
|
if (slashCommand.name === "attach") {
|
|
67593
68340
|
const observation = await params.agentService.observeRun(params.sessionTarget, buildRunObserver({
|
|
67594
68341
|
mode: "live"
|
|
67595
68342
|
}));
|
|
67596
68343
|
await applyRunUpdate(observation.update);
|
|
67597
|
-
return;
|
|
68344
|
+
return interactionResult;
|
|
67598
68345
|
}
|
|
67599
68346
|
if (slashCommand.name === "detach") {
|
|
67600
68347
|
const detached = await params.agentService.detachRunObserver(params.sessionTarget, observerId);
|
|
67601
68348
|
await params.postText(detached.detached ? "Detached this thread from live updates. clisbot will still post the final settlement here when the run completes." : "This thread was not attached to an active run.");
|
|
67602
68349
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67603
|
-
return;
|
|
68350
|
+
return interactionResult;
|
|
67604
68351
|
}
|
|
67605
68352
|
if (slashCommand.name === "watch") {
|
|
67606
68353
|
const observation = await params.agentService.observeRun(params.sessionTarget, buildRunObserver({
|
|
@@ -67609,19 +68356,19 @@ async function processChannelInteraction(params) {
|
|
|
67609
68356
|
durationMs: slashCommand.durationMs
|
|
67610
68357
|
}));
|
|
67611
68358
|
await applyRunUpdate(observation.update);
|
|
67612
|
-
return;
|
|
68359
|
+
return interactionResult;
|
|
67613
68360
|
}
|
|
67614
68361
|
if (slashCommand.name === "stop") {
|
|
67615
68362
|
const stopped = await params.agentService.interruptSession(params.sessionTarget);
|
|
67616
68363
|
await params.postText(stopped.interrupted ? `Interrupted agent \`${stopped.agentId}\` session \`${stopped.sessionName}\`.` : `Agent \`${stopped.agentId}\` session \`${stopped.sessionName}\` was not running.`);
|
|
67617
68364
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67618
|
-
return;
|
|
68365
|
+
return interactionResult;
|
|
67619
68366
|
}
|
|
67620
68367
|
if (slashCommand.name === "nudge") {
|
|
67621
68368
|
const nudged = await params.agentService.nudgeSession(params.sessionTarget);
|
|
67622
68369
|
await params.postText(nudged.nudged ? `Sent one extra Enter to agent \`${nudged.agentId}\` session \`${nudged.sessionName}\`.` : `No active or resumable session to nudge for agent \`${nudged.agentId}\`.`);
|
|
67623
68370
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67624
|
-
return;
|
|
68371
|
+
return interactionResult;
|
|
67625
68372
|
}
|
|
67626
68373
|
if (slashCommand.name === "followup") {
|
|
67627
68374
|
if (slashCommand.action === "status") {
|
|
@@ -67640,7 +68387,32 @@ async function processChannelInteraction(params) {
|
|
|
67640
68387
|
await params.postText(slashCommand.mode === "paused" ? "Follow-up paused for this conversation until the next explicit mention." : `Follow-up mode set to \`${slashCommand.mode}\` for this conversation.`);
|
|
67641
68388
|
}
|
|
67642
68389
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67643
|
-
return;
|
|
68390
|
+
return interactionResult;
|
|
68391
|
+
}
|
|
68392
|
+
if (slashCommand.name === "streaming") {
|
|
68393
|
+
if (slashCommand.action === "status") {
|
|
68394
|
+
const persisted = await getConversationStreaming({
|
|
68395
|
+
identity: params.identity
|
|
68396
|
+
});
|
|
68397
|
+
await params.postText(renderStreamingStatusMessage({
|
|
68398
|
+
route: params.route,
|
|
68399
|
+
persisted
|
|
68400
|
+
}));
|
|
68401
|
+
} else if (slashCommand.streaming) {
|
|
68402
|
+
const persisted = await setConversationStreaming({
|
|
68403
|
+
identity: params.identity,
|
|
68404
|
+
streaming: slashCommand.streaming
|
|
68405
|
+
});
|
|
68406
|
+
await params.postText([
|
|
68407
|
+
`Updated streaming mode for \`${persisted.label}\`.`,
|
|
68408
|
+
`config.streaming: \`${persisted.streaming}\``,
|
|
68409
|
+
`config: \`${persisted.configPath}\``,
|
|
68410
|
+
slashCommand.action === "on" ? "`/streaming on` persists as `all` until `latest` and `all` diverge in runtime behavior." : "If config reload is enabled, the new mode should apply automatically shortly."
|
|
68411
|
+
].join(`
|
|
68412
|
+
`));
|
|
68413
|
+
}
|
|
68414
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68415
|
+
return interactionResult;
|
|
67644
68416
|
}
|
|
67645
68417
|
if (slashCommand.name === "responsemode") {
|
|
67646
68418
|
if (slashCommand.action === "status") {
|
|
@@ -67665,7 +68437,7 @@ async function processChannelInteraction(params) {
|
|
|
67665
68437
|
`));
|
|
67666
68438
|
}
|
|
67667
68439
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67668
|
-
return;
|
|
68440
|
+
return interactionResult;
|
|
67669
68441
|
}
|
|
67670
68442
|
if (slashCommand.name === "additionalmessagemode") {
|
|
67671
68443
|
if (slashCommand.action === "status") {
|
|
@@ -67690,19 +68462,19 @@ async function processChannelInteraction(params) {
|
|
|
67690
68462
|
`));
|
|
67691
68463
|
}
|
|
67692
68464
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67693
|
-
return;
|
|
68465
|
+
return interactionResult;
|
|
67694
68466
|
}
|
|
67695
68467
|
if (slashCommand.name === "queue-list") {
|
|
67696
68468
|
const queuedItems = params.agentService.listQueuedPrompts?.(params.sessionTarget) ?? [];
|
|
67697
68469
|
await params.postText(renderQueuedMessagesList(queuedItems));
|
|
67698
68470
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67699
|
-
return;
|
|
68471
|
+
return interactionResult;
|
|
67700
68472
|
}
|
|
67701
68473
|
if (slashCommand.name === "queue-clear") {
|
|
67702
68474
|
const clearedCount = params.agentService.clearQueuedPrompts?.(params.sessionTarget) ?? 0;
|
|
67703
68475
|
await params.postText(clearedCount > 0 ? `Cleared ${clearedCount} queued message${clearedCount === 1 ? "" : "s"}.` : "Queue was already empty.");
|
|
67704
68476
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67705
|
-
return;
|
|
68477
|
+
return interactionResult;
|
|
67706
68478
|
}
|
|
67707
68479
|
}
|
|
67708
68480
|
if (slashCommand?.type === "loop-control") {
|
|
@@ -67714,7 +68486,7 @@ async function processChannelInteraction(params) {
|
|
|
67714
68486
|
globalLoopCount: params.agentService.getActiveIntervalLoopCount?.() ?? 0
|
|
67715
68487
|
}));
|
|
67716
68488
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67717
|
-
return;
|
|
68489
|
+
return interactionResult;
|
|
67718
68490
|
}
|
|
67719
68491
|
const sessionLoops = params.agentService.listIntervalLoops?.({
|
|
67720
68492
|
sessionKey: params.sessionTarget.sessionKey
|
|
@@ -67723,31 +68495,31 @@ async function processChannelInteraction(params) {
|
|
|
67723
68495
|
const cancelled2 = await params.agentService.cancelAllIntervalLoops();
|
|
67724
68496
|
await params.postText(cancelled2 > 0 ? `Cancelled ${cancelled2} active loop${cancelled2 === 1 ? "" : "s"} across the whole app.` : "No active loops to cancel across the whole app.");
|
|
67725
68497
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67726
|
-
return;
|
|
68498
|
+
return interactionResult;
|
|
67727
68499
|
}
|
|
67728
68500
|
if (slashCommand.all) {
|
|
67729
68501
|
const cancelled2 = await params.agentService.cancelIntervalLoopsForSession(params.sessionTarget);
|
|
67730
68502
|
await params.postText(cancelled2 > 0 ? `Cancelled ${cancelled2} active loop${cancelled2 === 1 ? "" : "s"} for this session.` : "No active loops to cancel for this session.");
|
|
67731
68503
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67732
|
-
return;
|
|
68504
|
+
return interactionResult;
|
|
67733
68505
|
}
|
|
67734
68506
|
const targetLoopId = slashCommand.loopId || (sessionLoops.length === 1 ? sessionLoops[0]?.id : undefined);
|
|
67735
68507
|
if (!targetLoopId) {
|
|
67736
68508
|
await params.postText(sessionLoops.length === 0 ? "No active loops to cancel for this session." : "Multiple active loops exist for this session. Use `/loop cancel <id>` or `/loop cancel --all`.");
|
|
67737
68509
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67738
|
-
return;
|
|
68510
|
+
return interactionResult;
|
|
67739
68511
|
}
|
|
67740
68512
|
const cancelled = await params.agentService.cancelIntervalLoop(targetLoopId);
|
|
67741
68513
|
await params.postText(cancelled ? `Cancelled loop \`${targetLoopId}\`.` : `No active loop found with id \`${targetLoopId}\`.`);
|
|
67742
68514
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67743
|
-
return;
|
|
68515
|
+
return interactionResult;
|
|
67744
68516
|
}
|
|
67745
68517
|
if (slashCommand?.type === "loop-error") {
|
|
67746
68518
|
await params.postText(`${slashCommand.message}
|
|
67747
68519
|
|
|
67748
68520
|
${renderLoopUsage()}`);
|
|
67749
68521
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67750
|
-
return;
|
|
68522
|
+
return interactionResult;
|
|
67751
68523
|
}
|
|
67752
68524
|
if (slashCommand?.type === "loop") {
|
|
67753
68525
|
const loopConfig = params.agentService.getLoopConfig();
|
|
@@ -67758,7 +68530,7 @@ ${renderLoopUsage()}`);
|
|
|
67758
68530
|
|
|
67759
68531
|
${renderLoopUsage()}`);
|
|
67760
68532
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67761
|
-
return;
|
|
68533
|
+
return interactionResult;
|
|
67762
68534
|
}
|
|
67763
68535
|
if (slashCommand.params.mode === "interval") {
|
|
67764
68536
|
const intervalValidation = validateLoopInterval({
|
|
@@ -67770,7 +68542,7 @@ ${renderLoopUsage()}`);
|
|
|
67770
68542
|
|
|
67771
68543
|
${renderLoopUsage()}`);
|
|
67772
68544
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67773
|
-
return;
|
|
68545
|
+
return interactionResult;
|
|
67774
68546
|
}
|
|
67775
68547
|
}
|
|
67776
68548
|
let resolvedLoopPrompt;
|
|
@@ -67783,7 +68555,7 @@ ${renderLoopUsage()}`);
|
|
|
67783
68555
|
} catch (error) {
|
|
67784
68556
|
await params.postText(String(error));
|
|
67785
68557
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67786
|
-
return;
|
|
68558
|
+
return interactionResult;
|
|
67787
68559
|
}
|
|
67788
68560
|
const buildLoopPromptText = (text) => params.agentPromptBuilder ? params.agentPromptBuilder(text) : text;
|
|
67789
68561
|
if (slashCommand.params.mode === "times") {
|
|
@@ -67806,7 +68578,7 @@ ${renderLoopUsage()}`);
|
|
|
67806
68578
|
observerId: `${observerId}:loop:${index + 1}`
|
|
67807
68579
|
});
|
|
67808
68580
|
}
|
|
67809
|
-
return;
|
|
68581
|
+
return interactionResult;
|
|
67810
68582
|
}
|
|
67811
68583
|
if (slashCommand.params.mode === "calendar") {
|
|
67812
68584
|
const effectiveTimezone = resolveEffectiveLoopTimezone({
|
|
@@ -67816,8 +68588,10 @@ ${renderLoopUsage()}`);
|
|
|
67816
68588
|
const createdLoop2 = await params.agentService.createCalendarLoop({
|
|
67817
68589
|
target: params.sessionTarget,
|
|
67818
68590
|
promptText: buildLoopPromptText(resolvedLoopPrompt.text),
|
|
68591
|
+
canonicalPromptText: resolvedLoopPrompt.text,
|
|
67819
68592
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
67820
68593
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
68594
|
+
surfaceBinding: buildLoopSurfaceBinding(params.identity),
|
|
67821
68595
|
cadence: slashCommand.params.cadence,
|
|
67822
68596
|
dayOfWeek: slashCommand.params.dayOfWeek,
|
|
67823
68597
|
localTime: slashCommand.params.localTime,
|
|
@@ -67825,7 +68599,8 @@ ${renderLoopUsage()}`);
|
|
|
67825
68599
|
minute: slashCommand.params.minute,
|
|
67826
68600
|
timezone: effectiveTimezone,
|
|
67827
68601
|
maxRuns: maxRunsPerLoop,
|
|
67828
|
-
createdBy: params.senderId
|
|
68602
|
+
createdBy: params.senderId,
|
|
68603
|
+
protectedControlMutationRule: params.protectedControlMutationRule
|
|
67829
68604
|
});
|
|
67830
68605
|
await params.postText(renderLoopStartedMessage({
|
|
67831
68606
|
mode: "calendar",
|
|
@@ -67845,17 +68620,20 @@ ${renderLoopUsage()}`);
|
|
|
67845
68620
|
globalLoopCount: params.agentService.getActiveIntervalLoopCount()
|
|
67846
68621
|
}));
|
|
67847
68622
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67848
|
-
return;
|
|
68623
|
+
return interactionResult;
|
|
67849
68624
|
}
|
|
67850
68625
|
const createdLoop = await params.agentService.createIntervalLoop({
|
|
67851
68626
|
target: params.sessionTarget,
|
|
67852
68627
|
promptText: buildLoopPromptText(resolvedLoopPrompt.text),
|
|
68628
|
+
canonicalPromptText: resolvedLoopPrompt.text,
|
|
67853
68629
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
67854
68630
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
68631
|
+
surfaceBinding: buildLoopSurfaceBinding(params.identity),
|
|
67855
68632
|
intervalMs: effectiveIntervalMs,
|
|
67856
68633
|
maxRuns: maxRunsPerLoop,
|
|
67857
68634
|
createdBy: params.senderId,
|
|
67858
|
-
force: slashCommand.params.force
|
|
68635
|
+
force: slashCommand.params.force,
|
|
68636
|
+
protectedControlMutationRule: params.protectedControlMutationRule
|
|
67859
68637
|
});
|
|
67860
68638
|
await params.postText(renderLoopStartedMessage({
|
|
67861
68639
|
mode: "interval",
|
|
@@ -67873,55 +68651,59 @@ ${renderLoopUsage()}`);
|
|
|
67873
68651
|
}).warning
|
|
67874
68652
|
}));
|
|
67875
68653
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67876
|
-
return;
|
|
68654
|
+
return interactionResult;
|
|
67877
68655
|
}
|
|
67878
68656
|
if (slashCommand?.type === "bash") {
|
|
67879
68657
|
if (!slashCommand.command.trim()) {
|
|
67880
68658
|
await params.postText("Usage: `/bash <command>` or a configured bash shortcut such as `!<command>`");
|
|
67881
|
-
return;
|
|
68659
|
+
return interactionResult;
|
|
67882
68660
|
}
|
|
67883
|
-
const
|
|
68661
|
+
const shellResult = await params.agentService.runShellCommand(params.sessionTarget, slashCommand.command);
|
|
67884
68662
|
const header = [
|
|
67885
|
-
`Bash in \`${
|
|
67886
|
-
`command: \`${
|
|
67887
|
-
|
|
68663
|
+
`Bash in \`${shellResult.workspacePath}\``,
|
|
68664
|
+
`command: \`${shellResult.command}\``,
|
|
68665
|
+
shellResult.timedOut ? "exit: `124` timed out" : `exit: \`${shellResult.exitCode}\``
|
|
67888
68666
|
].join(`
|
|
67889
68667
|
`);
|
|
67890
|
-
const body =
|
|
68668
|
+
const body = shellResult.output ? `
|
|
67891
68669
|
|
|
67892
68670
|
\`\`\`text
|
|
67893
|
-
${escapeCodeFence(
|
|
68671
|
+
${escapeCodeFence(shellResult.output)}
|
|
67894
68672
|
\`\`\`` : "\n\n`(no output)`";
|
|
67895
68673
|
await params.postText(`${header}${body}`);
|
|
67896
68674
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67897
|
-
return;
|
|
68675
|
+
return interactionResult;
|
|
67898
68676
|
}
|
|
67899
68677
|
if (slashCommand?.type === "queue" && !explicitQueueMessage) {
|
|
67900
68678
|
await params.postText("Usage: `/queue <message>` or `\\q <message>`");
|
|
67901
68679
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67902
|
-
return;
|
|
68680
|
+
return interactionResult;
|
|
67903
68681
|
}
|
|
67904
68682
|
if (slashCommand?.type === "steer" && !explicitSteerMessage) {
|
|
67905
68683
|
await params.postText("Usage: `/steer <message>` or `\\s <message>`");
|
|
67906
68684
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67907
|
-
return;
|
|
68685
|
+
return interactionResult;
|
|
67908
68686
|
}
|
|
67909
68687
|
if (explicitSteerMessage) {
|
|
67910
68688
|
const hasActiveRun = params.agentService.hasActiveRun?.(params.sessionTarget) ?? false;
|
|
67911
68689
|
if (!hasActiveRun) {
|
|
67912
68690
|
await params.postText("No active run to steer.");
|
|
67913
68691
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67914
|
-
return;
|
|
68692
|
+
return interactionResult;
|
|
67915
68693
|
}
|
|
67916
|
-
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(explicitSteerMessage));
|
|
68694
|
+
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(explicitSteerMessage, params.protectedControlMutationRule));
|
|
67917
68695
|
await params.postText("Steered.");
|
|
67918
68696
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67919
|
-
return
|
|
68697
|
+
return {
|
|
68698
|
+
processingIndicatorLifecycle: "active-run"
|
|
68699
|
+
};
|
|
67920
68700
|
}
|
|
67921
68701
|
if (!forceQueuedDelivery && params.route.additionalMessageMode === "steer") {
|
|
67922
68702
|
if (sessionBusy) {
|
|
67923
|
-
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(params.text));
|
|
67924
|
-
return
|
|
68703
|
+
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(params.text, params.protectedControlMutationRule));
|
|
68704
|
+
return {
|
|
68705
|
+
processingIndicatorLifecycle: "active-run"
|
|
68706
|
+
};
|
|
67925
68707
|
}
|
|
67926
68708
|
}
|
|
67927
68709
|
await executePromptDelivery({
|
|
@@ -67930,13 +68712,14 @@ ${escapeCodeFence(result.output)}
|
|
|
67930
68712
|
identity: params.identity,
|
|
67931
68713
|
route: params.route,
|
|
67932
68714
|
maxChars: params.maxChars,
|
|
67933
|
-
promptText:
|
|
68715
|
+
promptText: delayedPromptText,
|
|
67934
68716
|
postText: params.postText,
|
|
67935
68717
|
reconcileText: params.reconcileText,
|
|
67936
68718
|
observerId,
|
|
67937
68719
|
timingContext: params.timingContext,
|
|
67938
68720
|
forceQueuedDelivery
|
|
67939
68721
|
});
|
|
68722
|
+
return interactionResult;
|
|
67940
68723
|
}
|
|
67941
68724
|
|
|
67942
68725
|
// src/channels/pairing/access.ts
|
|
@@ -67980,141 +68763,97 @@ function isTelegramSenderAllowed(params) {
|
|
|
67980
68763
|
const userId = params.userId?.trim() ?? "";
|
|
67981
68764
|
const username = params.username?.trim() ? `@${params.username.trim().replace(/^@+/, "").toLowerCase()}` : "";
|
|
67982
68765
|
const normalizedAllowFrom = params.allowFrom.map((entry) => normalizeAllowEntry("telegram", entry)).filter(Boolean);
|
|
67983
|
-
if (userId && normalizedAllowFrom.includes(userId)) {
|
|
67984
|
-
return true;
|
|
67985
|
-
}
|
|
67986
|
-
if (username && normalizedAllowFrom.includes(username)) {
|
|
67987
|
-
return true;
|
|
67988
|
-
}
|
|
67989
|
-
return false;
|
|
67990
|
-
}
|
|
67991
|
-
|
|
67992
|
-
// src/channels/agent-prompt.ts
|
|
67993
|
-
function buildAgentPromptText(params) {
|
|
67994
|
-
if (!params.config.enabled) {
|
|
67995
|
-
return params.text;
|
|
67996
|
-
}
|
|
67997
|
-
const systemBlock = renderAgentPromptInstruction(params);
|
|
67998
|
-
return `<system>
|
|
67999
|
-
${systemBlock}
|
|
68000
|
-
</system>
|
|
68001
|
-
|
|
68002
|
-
<user>
|
|
68003
|
-
${params.text}
|
|
68004
|
-
</user>`;
|
|
68005
|
-
}
|
|
68006
|
-
function renderAgentPromptInstruction(params) {
|
|
68007
|
-
const messageToolMode = (params.responseMode ?? "message-tool") === "message-tool";
|
|
68008
|
-
const lines = [
|
|
68009
|
-
`[${renderPromptTimestamp()}] ${renderIdentitySummary(params.identity)}`,
|
|
68010
|
-
"",
|
|
68011
|
-
"You are operating inside clisbot.",
|
|
68012
|
-
messageToolMode ? "channel auto-delivery is disabled for this conversation; send user-facing progress updates and the final response yourself with the reply command" : "channel auto-delivery remains enabled for this conversation; do not send user-facing progress updates or the final response with clisbot message send"
|
|
68013
|
-
];
|
|
68014
|
-
if (messageToolMode) {
|
|
68015
|
-
const replyCommand = buildReplyCommand({
|
|
68016
|
-
command: getClisbotPromptCommand(),
|
|
68017
|
-
identity: params.identity
|
|
68018
|
-
});
|
|
68019
|
-
lines.push("Use the exact command below when you need to send progress updates, media attachments, or the final response back to the user.", "reply command:", replyCommand, `progress updates: at most ${params.config.maxProgressMessages}`, params.config.requireFinalResponse ? "final response: send exactly 1 final user-facing response" : "final response: optional", "keep progress updates short and meaningful", "do not send progress updates for trivial internal steps");
|
|
68020
|
-
}
|
|
68021
|
-
return lines.join(`
|
|
68022
|
-
`);
|
|
68023
|
-
}
|
|
68024
|
-
function renderPromptTimestamp() {
|
|
68025
|
-
const date = new Date;
|
|
68026
|
-
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
68027
|
-
year: "numeric",
|
|
68028
|
-
month: "2-digit",
|
|
68029
|
-
day: "2-digit",
|
|
68030
|
-
hour: "2-digit",
|
|
68031
|
-
minute: "2-digit",
|
|
68032
|
-
second: "2-digit",
|
|
68033
|
-
hour12: false,
|
|
68034
|
-
timeZoneName: "shortOffset"
|
|
68035
|
-
});
|
|
68036
|
-
return formatter.format(date).replace(",", "");
|
|
68037
|
-
}
|
|
68038
|
-
function renderIdentitySummary(identity) {
|
|
68039
|
-
const segments = [renderConversationSummary(identity)];
|
|
68040
|
-
const sender = renderSenderSummary(identity);
|
|
68041
|
-
if (sender) {
|
|
68042
|
-
segments.push(sender);
|
|
68766
|
+
if (userId && normalizedAllowFrom.includes(userId)) {
|
|
68767
|
+
return true;
|
|
68043
68768
|
}
|
|
68044
|
-
|
|
68769
|
+
if (username && normalizedAllowFrom.includes(username)) {
|
|
68770
|
+
return true;
|
|
68771
|
+
}
|
|
68772
|
+
return false;
|
|
68045
68773
|
}
|
|
68046
|
-
|
|
68047
|
-
|
|
68048
|
-
|
|
68049
|
-
|
|
68050
|
-
|
|
68051
|
-
|
|
68052
|
-
segments.push(channel);
|
|
68053
|
-
}
|
|
68054
|
-
if (identity.threadTs) {
|
|
68055
|
-
segments.push(`thread ${identity.threadTs}`);
|
|
68056
|
-
}
|
|
68057
|
-
return segments.join(" ");
|
|
68774
|
+
|
|
68775
|
+
// src/auth/resolve.ts
|
|
68776
|
+
function normalizePrincipal(principal) {
|
|
68777
|
+
const trimmed = principal.trim();
|
|
68778
|
+
if (!trimmed) {
|
|
68779
|
+
return "";
|
|
68058
68780
|
}
|
|
68059
|
-
|
|
68060
|
-
|
|
68781
|
+
const [platform, userId] = trimmed.split(":", 2);
|
|
68782
|
+
if (!platform || !userId) {
|
|
68783
|
+
return trimmed;
|
|
68061
68784
|
}
|
|
68062
|
-
if (
|
|
68063
|
-
|
|
68064
|
-
const group = renderNamedValue("in group", identity.chatName, identity.chatId);
|
|
68065
|
-
return [topic, group].filter(Boolean).join(" ");
|
|
68785
|
+
if (platform === "slack") {
|
|
68786
|
+
return `slack:${userId.trim().toUpperCase()}`;
|
|
68066
68787
|
}
|
|
68067
|
-
|
|
68788
|
+
if (platform === "telegram") {
|
|
68789
|
+
return `telegram:${userId.trim()}`;
|
|
68790
|
+
}
|
|
68791
|
+
return `${platform}:${userId.trim()}`;
|
|
68068
68792
|
}
|
|
68069
|
-
function
|
|
68070
|
-
|
|
68071
|
-
return sender ? `sender ${sender}` : "";
|
|
68793
|
+
function normalizeRoleUsers(users) {
|
|
68794
|
+
return (users ?? []).map(normalizePrincipal).filter(Boolean);
|
|
68072
68795
|
}
|
|
68073
|
-
function
|
|
68074
|
-
const
|
|
68075
|
-
|
|
68076
|
-
|
|
68077
|
-
return `${namePrefix}${normalizedName} (${normalizedId})`;
|
|
68796
|
+
function resolvePrincipal(identity) {
|
|
68797
|
+
const senderId = identity.senderId?.trim();
|
|
68798
|
+
if (!senderId) {
|
|
68799
|
+
return;
|
|
68078
68800
|
}
|
|
68079
|
-
if (
|
|
68080
|
-
return
|
|
68801
|
+
if (identity.platform === "slack") {
|
|
68802
|
+
return normalizePrincipal(`slack:${senderId}`);
|
|
68081
68803
|
}
|
|
68082
|
-
return
|
|
68083
|
-
}
|
|
68084
|
-
function renderNamedValue(label, name, id) {
|
|
68085
|
-
const value = renderLabeledTarget(name, id);
|
|
68086
|
-
return value ? `${label} ${value}` : "";
|
|
68804
|
+
return normalizePrincipal(`telegram:${senderId}`);
|
|
68087
68805
|
}
|
|
68088
|
-
function
|
|
68089
|
-
|
|
68090
|
-
|
|
68091
|
-
|
|
68092
|
-
|
|
68093
|
-
if (
|
|
68094
|
-
|
|
68806
|
+
function findExplicitRole(roles, principal) {
|
|
68807
|
+
if (!principal || !roles) {
|
|
68808
|
+
return;
|
|
68809
|
+
}
|
|
68810
|
+
for (const [roleName, roleDefinition] of Object.entries(roles)) {
|
|
68811
|
+
if (normalizeRoleUsers(roleDefinition.users).includes(principal)) {
|
|
68812
|
+
return roleName;
|
|
68095
68813
|
}
|
|
68096
|
-
lines.push(" --final \\");
|
|
68097
|
-
lines.push(' --message "$(cat <<\\__CLISBOT_MESSAGE__');
|
|
68098
|
-
lines.push("<short progress update>");
|
|
68099
|
-
lines.push("__CLISBOT_MESSAGE__");
|
|
68100
|
-
lines.push(')" \\');
|
|
68101
|
-
lines.push(" [--media /absolute/path/to/file]");
|
|
68102
|
-
return lines.join(`
|
|
68103
|
-
`);
|
|
68104
68814
|
}
|
|
68105
|
-
|
|
68106
|
-
|
|
68107
|
-
|
|
68108
|
-
|
|
68815
|
+
return;
|
|
68816
|
+
}
|
|
68817
|
+
function getAgentAuth(config, agentId) {
|
|
68818
|
+
const defaults = config.agents.defaults.auth;
|
|
68819
|
+
const entry = config.agents.list.find((item) => item.id === agentId);
|
|
68820
|
+
const override = entry?.auth;
|
|
68821
|
+
return {
|
|
68822
|
+
defaultRole: override?.defaultRole ?? defaults.defaultRole,
|
|
68823
|
+
roles: {
|
|
68824
|
+
...defaults.roles,
|
|
68825
|
+
...override?.roles ?? {}
|
|
68826
|
+
}
|
|
68827
|
+
};
|
|
68828
|
+
}
|
|
68829
|
+
function getAllowedPermissions(roles, role) {
|
|
68830
|
+
return new Set(roles?.[role]?.allow ?? []);
|
|
68831
|
+
}
|
|
68832
|
+
function hasAppPermission(config, appRole, permission) {
|
|
68833
|
+
if (appRole === "owner") {
|
|
68834
|
+
return true;
|
|
68109
68835
|
}
|
|
68110
|
-
|
|
68111
|
-
|
|
68112
|
-
|
|
68113
|
-
|
|
68114
|
-
|
|
68115
|
-
|
|
68116
|
-
|
|
68117
|
-
|
|
68836
|
+
return getAllowedPermissions(config.app.auth.roles, appRole).has(permission);
|
|
68837
|
+
}
|
|
68838
|
+
function resolveChannelAuth(params) {
|
|
68839
|
+
const principal = resolvePrincipal(params.identity);
|
|
68840
|
+
const appAuth = params.config.app.auth;
|
|
68841
|
+
const explicitAppRole = findExplicitRole(appAuth.roles, principal);
|
|
68842
|
+
const appRole = explicitAppRole ?? appAuth.defaultRole;
|
|
68843
|
+
const appAdminLike = appRole === "owner" || appRole === "admin";
|
|
68844
|
+
const agentAuth = getAgentAuth(params.config, params.agentId);
|
|
68845
|
+
const explicitAgentRole = findExplicitRole(agentAuth.roles, principal);
|
|
68846
|
+
const agentRole = explicitAgentRole ?? agentAuth.defaultRole;
|
|
68847
|
+
const agentPermissions = getAllowedPermissions(agentAuth.roles, agentRole);
|
|
68848
|
+
const mayManageProtectedResources = appAdminLike || hasAppPermission(params.config, appRole, "configManage") || hasAppPermission(params.config, appRole, "appAuthManage") || hasAppPermission(params.config, appRole, "agentAuthManage") || hasAppPermission(params.config, appRole, "promptGovernanceManage");
|
|
68849
|
+
return {
|
|
68850
|
+
principal,
|
|
68851
|
+
appRole,
|
|
68852
|
+
agentRole,
|
|
68853
|
+
mayBypassPairing: appAdminLike,
|
|
68854
|
+
mayManageProtectedResources,
|
|
68855
|
+
canUseShell: appAdminLike || agentPermissions.has("shellExecute")
|
|
68856
|
+
};
|
|
68118
68857
|
}
|
|
68119
68858
|
|
|
68120
68859
|
// src/channels/slack/session-routing.ts
|
|
@@ -68172,6 +68911,14 @@ function resolveSlackConversationTarget(params) {
|
|
|
68172
68911
|
};
|
|
68173
68912
|
}
|
|
68174
68913
|
|
|
68914
|
+
// src/channels/privilege-commands.ts
|
|
68915
|
+
function resolvePrivilegeCommands(rootConfig, override) {
|
|
68916
|
+
return {
|
|
68917
|
+
enabled: override?.enabled ?? rootConfig.enabled,
|
|
68918
|
+
allowUsers: override?.allowUsers ?? rootConfig.allowUsers
|
|
68919
|
+
};
|
|
68920
|
+
}
|
|
68921
|
+
|
|
68175
68922
|
// src/channels/route-policy.ts
|
|
68176
68923
|
function buildSharedChannelRoute(params) {
|
|
68177
68924
|
const privilegeCommands = resolvePrivilegeCommands(params.channelConfig.privilegeCommands, params.route?.privilegeCommands);
|
|
@@ -68196,6 +68943,7 @@ function buildSharedChannelRoute(params) {
|
|
|
68196
68943
|
response: params.route?.response ?? params.channelConfig.response,
|
|
68197
68944
|
responseMode: params.route?.responseMode ?? agentEntry?.responseMode ?? params.channelConfig.responseMode,
|
|
68198
68945
|
additionalMessageMode: params.route?.additionalMessageMode ?? agentEntry?.additionalMessageMode ?? params.channelConfig.additionalMessageMode,
|
|
68946
|
+
verbose: params.route?.verbose ?? params.channelConfig.verbose,
|
|
68199
68947
|
followUp: {
|
|
68200
68948
|
mode: params.route?.followUp?.mode ?? params.channelConfig.followUp.mode,
|
|
68201
68949
|
participationTtlMs: resolveConfigDurationMs({
|
|
@@ -68376,6 +69124,178 @@ async function clearSlackAssistantThreadStatus(client, target) {
|
|
|
68376
69124
|
}
|
|
68377
69125
|
}
|
|
68378
69126
|
|
|
69127
|
+
// src/channels/processing-indicator.ts
|
|
69128
|
+
function shouldResolveIndicatorWait(update) {
|
|
69129
|
+
return isTerminalRunStatus(update.status) || update.status === "detached";
|
|
69130
|
+
}
|
|
69131
|
+
async function waitForProcessingIndicatorLifecycle(params) {
|
|
69132
|
+
if (params.lifecycle !== "active-run") {
|
|
69133
|
+
return;
|
|
69134
|
+
}
|
|
69135
|
+
if (!params.agentService.hasActiveRun(params.sessionTarget)) {
|
|
69136
|
+
return;
|
|
69137
|
+
}
|
|
69138
|
+
let settled = false;
|
|
69139
|
+
const settle = () => {
|
|
69140
|
+
if (settled) {
|
|
69141
|
+
return;
|
|
69142
|
+
}
|
|
69143
|
+
settled = true;
|
|
69144
|
+
};
|
|
69145
|
+
try {
|
|
69146
|
+
await new Promise(async (resolve, reject) => {
|
|
69147
|
+
const resolveOnce = () => {
|
|
69148
|
+
if (settled) {
|
|
69149
|
+
return;
|
|
69150
|
+
}
|
|
69151
|
+
settled = true;
|
|
69152
|
+
resolve();
|
|
69153
|
+
};
|
|
69154
|
+
try {
|
|
69155
|
+
const observation = await params.agentService.observeRun(params.sessionTarget, {
|
|
69156
|
+
id: params.observerId,
|
|
69157
|
+
mode: "live",
|
|
69158
|
+
onUpdate: async (update) => {
|
|
69159
|
+
if (shouldResolveIndicatorWait(update)) {
|
|
69160
|
+
resolveOnce();
|
|
69161
|
+
}
|
|
69162
|
+
}
|
|
69163
|
+
});
|
|
69164
|
+
if (!observation.active || shouldResolveIndicatorWait(observation.update)) {
|
|
69165
|
+
resolveOnce();
|
|
69166
|
+
}
|
|
69167
|
+
} catch (error) {
|
|
69168
|
+
reject(error);
|
|
69169
|
+
}
|
|
69170
|
+
});
|
|
69171
|
+
} finally {
|
|
69172
|
+
settle();
|
|
69173
|
+
await params.agentService.detachRunObserver(params.sessionTarget, params.observerId).catch(() => {
|
|
69174
|
+
return;
|
|
69175
|
+
});
|
|
69176
|
+
}
|
|
69177
|
+
}
|
|
69178
|
+
|
|
69179
|
+
class ConversationProcessingIndicatorCoordinator {
|
|
69180
|
+
entries = new Map;
|
|
69181
|
+
async acquire(params) {
|
|
69182
|
+
let entry = this.entries.get(params.key);
|
|
69183
|
+
if (!entry) {
|
|
69184
|
+
entry = {
|
|
69185
|
+
activeRunHold: false,
|
|
69186
|
+
indicatorActive: false,
|
|
69187
|
+
key: params.key,
|
|
69188
|
+
operationChain: Promise.resolve(),
|
|
69189
|
+
refCount: 0
|
|
69190
|
+
};
|
|
69191
|
+
this.entries.set(params.key, entry);
|
|
69192
|
+
}
|
|
69193
|
+
entry.refCount += 1;
|
|
69194
|
+
await this.ensureIndicatorActive(entry, params.activate, params.onError);
|
|
69195
|
+
let released = false;
|
|
69196
|
+
return {
|
|
69197
|
+
setLifecycle: async (lifecycleParams) => {
|
|
69198
|
+
if (released || lifecycleParams.lifecycle !== "active-run" || entry.activeRunHold) {
|
|
69199
|
+
return;
|
|
69200
|
+
}
|
|
69201
|
+
entry.activeRunHold = true;
|
|
69202
|
+
entry.activeRunWait = waitForProcessingIndicatorLifecycle(lifecycleParams).catch((error) => {
|
|
69203
|
+
params.onError?.("active-run", error);
|
|
69204
|
+
}).finally(() => {
|
|
69205
|
+
entry.activeRunHold = false;
|
|
69206
|
+
entry.activeRunWait = undefined;
|
|
69207
|
+
this.maybeDeactivate(entry, params.onError);
|
|
69208
|
+
});
|
|
69209
|
+
},
|
|
69210
|
+
release: async () => {
|
|
69211
|
+
if (released) {
|
|
69212
|
+
return;
|
|
69213
|
+
}
|
|
69214
|
+
released = true;
|
|
69215
|
+
entry.refCount = Math.max(0, entry.refCount - 1);
|
|
69216
|
+
await this.maybeDeactivate(entry, params.onError);
|
|
69217
|
+
}
|
|
69218
|
+
};
|
|
69219
|
+
}
|
|
69220
|
+
async ensureIndicatorActive(entry, activate, onError) {
|
|
69221
|
+
entry.operationChain = entry.operationChain.then(async () => {
|
|
69222
|
+
if (entry.indicatorActive) {
|
|
69223
|
+
return;
|
|
69224
|
+
}
|
|
69225
|
+
try {
|
|
69226
|
+
const cleanup = await activate();
|
|
69227
|
+
entry.cleanup = typeof cleanup === "function" ? cleanup : undefined;
|
|
69228
|
+
entry.indicatorActive = true;
|
|
69229
|
+
} catch (error) {
|
|
69230
|
+
onError?.("activate", error);
|
|
69231
|
+
}
|
|
69232
|
+
});
|
|
69233
|
+
await entry.operationChain;
|
|
69234
|
+
}
|
|
69235
|
+
async maybeDeactivate(entry, onError) {
|
|
69236
|
+
if (entry.refCount > 0 || entry.activeRunHold) {
|
|
69237
|
+
return;
|
|
69238
|
+
}
|
|
69239
|
+
entry.operationChain = entry.operationChain.then(async () => {
|
|
69240
|
+
if (entry.refCount > 0 || entry.activeRunHold || !entry.indicatorActive) {
|
|
69241
|
+
return;
|
|
69242
|
+
}
|
|
69243
|
+
try {
|
|
69244
|
+
await entry.cleanup?.();
|
|
69245
|
+
} catch (error) {
|
|
69246
|
+
onError?.("deactivate", error);
|
|
69247
|
+
} finally {
|
|
69248
|
+
entry.cleanup = undefined;
|
|
69249
|
+
entry.indicatorActive = false;
|
|
69250
|
+
if (entry.refCount === 0 && !entry.activeRunHold) {
|
|
69251
|
+
this.entries.delete(entry.key);
|
|
69252
|
+
}
|
|
69253
|
+
}
|
|
69254
|
+
});
|
|
69255
|
+
await entry.operationChain;
|
|
69256
|
+
}
|
|
69257
|
+
}
|
|
69258
|
+
|
|
69259
|
+
// src/channels/slack/processing-decoration.ts
|
|
69260
|
+
async function activateSlackProcessingDecoration(params) {
|
|
69261
|
+
const [reactionResult, statusResult] = await Promise.allSettled([
|
|
69262
|
+
params.addReaction(),
|
|
69263
|
+
params.setStatus()
|
|
69264
|
+
]);
|
|
69265
|
+
const reactionApplied = reactionResult.status === "fulfilled" ? reactionResult.value === true : false;
|
|
69266
|
+
const statusApplied = statusResult.status === "fulfilled" ? statusResult.value === true : false;
|
|
69267
|
+
if (reactionResult.status === "rejected") {
|
|
69268
|
+
params.onUnexpectedError?.("add-reaction", reactionResult.reason);
|
|
69269
|
+
}
|
|
69270
|
+
if (statusResult.status === "rejected") {
|
|
69271
|
+
params.onUnexpectedError?.("set-status", statusResult.reason);
|
|
69272
|
+
}
|
|
69273
|
+
if (!reactionApplied && !statusApplied) {
|
|
69274
|
+
if (reactionResult.status === "rejected") {
|
|
69275
|
+
throw reactionResult.reason;
|
|
69276
|
+
}
|
|
69277
|
+
if (statusResult.status === "rejected") {
|
|
69278
|
+
throw statusResult.reason;
|
|
69279
|
+
}
|
|
69280
|
+
}
|
|
69281
|
+
return async () => {
|
|
69282
|
+
if (reactionApplied) {
|
|
69283
|
+
try {
|
|
69284
|
+
await params.removeReaction();
|
|
69285
|
+
} catch (error) {
|
|
69286
|
+
params.onUnexpectedError?.("remove-reaction", error);
|
|
69287
|
+
}
|
|
69288
|
+
}
|
|
69289
|
+
if (statusApplied) {
|
|
69290
|
+
try {
|
|
69291
|
+
await params.clearStatus();
|
|
69292
|
+
} catch (error) {
|
|
69293
|
+
params.onUnexpectedError?.("clear-status", error);
|
|
69294
|
+
}
|
|
69295
|
+
}
|
|
69296
|
+
};
|
|
69297
|
+
}
|
|
69298
|
+
|
|
68379
69299
|
// src/channels/slack/bolt-compat.ts
|
|
68380
69300
|
var SlackBolt = __toESM(require_dist7(), 1);
|
|
68381
69301
|
var { App } = SlackBolt;
|
|
@@ -68543,7 +69463,7 @@ async function downloadRemoteBuffer(params) {
|
|
|
68543
69463
|
|
|
68544
69464
|
// src/agents/attachments/storage.ts
|
|
68545
69465
|
import { access as access2 } from "node:fs/promises";
|
|
68546
|
-
import { extname, join as
|
|
69466
|
+
import { extname, join as join9 } from "node:path";
|
|
68547
69467
|
var CONTENT_TYPE_EXTENSION_MAP = {
|
|
68548
69468
|
"application/json": ".json",
|
|
68549
69469
|
"application/pdf": ".pdf",
|
|
@@ -68576,12 +69496,12 @@ function buildAttachmentFilename(params) {
|
|
|
68576
69496
|
async function resolveUniquePath(directoryPath, fileName) {
|
|
68577
69497
|
const extension = extname(fileName);
|
|
68578
69498
|
const baseName = extension ? fileName.slice(0, -extension.length) : fileName;
|
|
68579
|
-
let candidatePath =
|
|
69499
|
+
let candidatePath = join9(directoryPath, fileName);
|
|
68580
69500
|
let index = 2;
|
|
68581
69501
|
while (true) {
|
|
68582
69502
|
try {
|
|
68583
69503
|
await access2(candidatePath);
|
|
68584
|
-
candidatePath =
|
|
69504
|
+
candidatePath = join9(directoryPath, `${baseName}-${index}${extension}`);
|
|
68585
69505
|
index += 1;
|
|
68586
69506
|
} catch {
|
|
68587
69507
|
return candidatePath;
|
|
@@ -68589,7 +69509,7 @@ async function resolveUniquePath(directoryPath, fileName) {
|
|
|
68589
69509
|
}
|
|
68590
69510
|
}
|
|
68591
69511
|
async function saveWorkspaceAttachment(params) {
|
|
68592
|
-
const attachmentDir =
|
|
69512
|
+
const attachmentDir = join9(params.workspacePath, ".attachments", sanitizeSessionName(params.sessionKey), sanitizeSessionName(params.messageId));
|
|
68593
69513
|
await ensureDir2(attachmentDir);
|
|
68594
69514
|
const fileName = buildAttachmentFilename({
|
|
68595
69515
|
originalFilename: params.originalFilename,
|
|
@@ -68979,6 +69899,7 @@ class SlackSocketService {
|
|
|
68979
69899
|
accountId;
|
|
68980
69900
|
accountConfig;
|
|
68981
69901
|
app;
|
|
69902
|
+
processingIndicators = new ConversationProcessingIndicatorCoordinator;
|
|
68982
69903
|
botUserId = "";
|
|
68983
69904
|
botLabel = "";
|
|
68984
69905
|
teamId = "";
|
|
@@ -69122,12 +70043,22 @@ class SlackSocketService {
|
|
|
69122
70043
|
if (params.conversationKind === "dm") {
|
|
69123
70044
|
const directUserId = typeof event.user === "string" ? event.user.trim() : "";
|
|
69124
70045
|
const dmConfig = this.loadedConfig.raw.channels.slack.directMessages;
|
|
70046
|
+
const auth2 = resolveChannelAuth({
|
|
70047
|
+
config: this.loadedConfig.raw,
|
|
70048
|
+
agentId: params.route.agentId,
|
|
70049
|
+
identity: {
|
|
70050
|
+
platform: "slack",
|
|
70051
|
+
conversationKind: params.conversationKind,
|
|
70052
|
+
senderId: directUserId || undefined,
|
|
70053
|
+
channelId
|
|
70054
|
+
}
|
|
70055
|
+
});
|
|
69125
70056
|
if (!directUserId || dmConfig.policy === "disabled") {
|
|
69126
70057
|
debugSlackEvent("drop-dm-disabled", { eventId, directUserId });
|
|
69127
70058
|
await this.processedEventsStore.markCompleted(eventId);
|
|
69128
70059
|
return;
|
|
69129
70060
|
}
|
|
69130
|
-
if (dmConfig.policy !== "open") {
|
|
70061
|
+
if (dmConfig.policy !== "open" && !auth2.mayBypassPairing) {
|
|
69131
70062
|
const storedAllowFrom = await readChannelAllowFromStore("slack");
|
|
69132
70063
|
const allowed = isSlackSenderAllowed({
|
|
69133
70064
|
allowFrom: [...dmConfig.allowFrom, ...storedAllowFrom],
|
|
@@ -69238,17 +70169,28 @@ class SlackSocketService {
|
|
|
69238
70169
|
timestamp: messageTs
|
|
69239
70170
|
};
|
|
69240
70171
|
let responseChunks = [];
|
|
70172
|
+
const cliTool = getAgentEntry(this.loadedConfig, params.route.agentId)?.cliTool;
|
|
70173
|
+
const identity = {
|
|
70174
|
+
platform: "slack",
|
|
70175
|
+
conversationKind: params.conversationKind,
|
|
70176
|
+
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
70177
|
+
channelId,
|
|
70178
|
+
threadTs
|
|
70179
|
+
};
|
|
70180
|
+
const auth = resolveChannelAuth({
|
|
70181
|
+
config: this.loadedConfig.raw,
|
|
70182
|
+
agentId: params.route.agentId,
|
|
70183
|
+
identity
|
|
70184
|
+
});
|
|
70185
|
+
const protectedControlMutationRule = auth.mayManageProtectedResources ? undefined : DEFAULT_PROTECTED_CONTROL_RULE;
|
|
69241
70186
|
const agentPromptText = buildAgentPromptText({
|
|
69242
70187
|
text,
|
|
69243
|
-
identity
|
|
69244
|
-
platform: "slack",
|
|
69245
|
-
conversationKind: params.conversationKind,
|
|
69246
|
-
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
69247
|
-
channelId,
|
|
69248
|
-
threadTs
|
|
69249
|
-
},
|
|
70188
|
+
identity,
|
|
69250
70189
|
config: this.loadedConfig.raw.channels.slack.agentPrompt,
|
|
69251
|
-
|
|
70190
|
+
cliTool,
|
|
70191
|
+
responseMode: params.route.responseMode,
|
|
70192
|
+
streaming: params.route.streaming,
|
|
70193
|
+
protectedControlMutationRule
|
|
69252
70194
|
});
|
|
69253
70195
|
const timingContext = {
|
|
69254
70196
|
platform: "slack",
|
|
@@ -69264,39 +70206,46 @@ class SlackSocketService {
|
|
|
69264
70206
|
accountId: this.accountId
|
|
69265
70207
|
});
|
|
69266
70208
|
const ackReactionTask = waitForBackgroundSlackTask(addConfiguredReaction(this.app.client, this.loadedConfig.raw.channels.slack.ackReaction, reactionTarget));
|
|
69267
|
-
const
|
|
69268
|
-
|
|
69269
|
-
|
|
69270
|
-
|
|
69271
|
-
|
|
69272
|
-
|
|
69273
|
-
|
|
70209
|
+
const processingLease = await this.processingIndicators.acquire({
|
|
70210
|
+
key: `slack:${this.accountId}:${channelId}:${threadTs}`,
|
|
70211
|
+
activate: async () => activateSlackProcessingDecoration({
|
|
70212
|
+
addReaction: () => addConfiguredReaction(this.app.client, this.loadedConfig.raw.channels.slack.typingReaction, reactionTarget),
|
|
70213
|
+
removeReaction: () => removeConfiguredReaction(this.app.client, this.loadedConfig.raw.channels.slack.typingReaction, reactionTarget),
|
|
70214
|
+
setStatus: () => setSlackAssistantThreadStatus(this.app.client, this.loadedConfig.raw.channels.slack.processingStatus, {
|
|
70215
|
+
channel: channelId,
|
|
70216
|
+
threadTs
|
|
70217
|
+
}),
|
|
70218
|
+
clearStatus: () => clearSlackAssistantThreadStatus(this.app.client, {
|
|
70219
|
+
channel: channelId,
|
|
70220
|
+
threadTs
|
|
70221
|
+
}),
|
|
70222
|
+
onUnexpectedError: (phase, error) => {
|
|
70223
|
+
console.error(`slack processing indicator ${phase} failed`, error);
|
|
70224
|
+
}
|
|
70225
|
+
}),
|
|
70226
|
+
onError: (phase, error) => {
|
|
70227
|
+
console.error(`slack processing indicator ${phase} failed`, error);
|
|
70228
|
+
}
|
|
70229
|
+
});
|
|
69274
70230
|
try {
|
|
69275
|
-
await processChannelInteraction({
|
|
70231
|
+
const interaction = await processChannelInteraction({
|
|
69276
70232
|
agentService: this.agentService,
|
|
69277
70233
|
sessionTarget,
|
|
69278
|
-
identity
|
|
69279
|
-
|
|
69280
|
-
conversationKind: params.conversationKind,
|
|
69281
|
-
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
69282
|
-
channelId,
|
|
69283
|
-
threadTs
|
|
69284
|
-
},
|
|
70234
|
+
identity,
|
|
70235
|
+
auth,
|
|
69285
70236
|
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
69286
70237
|
text,
|
|
69287
70238
|
agentPromptText,
|
|
69288
70239
|
agentPromptBuilder: (nextText) => buildAgentPromptText({
|
|
69289
70240
|
text: nextText,
|
|
69290
|
-
identity
|
|
69291
|
-
platform: "slack",
|
|
69292
|
-
conversationKind: params.conversationKind,
|
|
69293
|
-
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
69294
|
-
channelId,
|
|
69295
|
-
threadTs
|
|
69296
|
-
},
|
|
70241
|
+
identity,
|
|
69297
70242
|
config: this.loadedConfig.raw.channels.slack.agentPrompt,
|
|
69298
|
-
|
|
70243
|
+
cliTool,
|
|
70244
|
+
responseMode: params.route.responseMode,
|
|
70245
|
+
streaming: params.route.streaming,
|
|
70246
|
+
protectedControlMutationRule
|
|
69299
70247
|
}),
|
|
70248
|
+
protectedControlMutationRule,
|
|
69300
70249
|
route: params.route,
|
|
69301
70250
|
maxChars: this.getSlackMaxChars(params.route.agentId),
|
|
69302
70251
|
timingContext,
|
|
@@ -69318,6 +70267,12 @@ class SlackSocketService {
|
|
|
69318
70267
|
return responseChunks;
|
|
69319
70268
|
}
|
|
69320
70269
|
});
|
|
70270
|
+
await processingLease.setLifecycle({
|
|
70271
|
+
agentService: this.agentService,
|
|
70272
|
+
sessionTarget,
|
|
70273
|
+
observerId: `slack-processing:${channelId}:${threadTs}`,
|
|
70274
|
+
lifecycle: interaction.processingIndicatorLifecycle
|
|
70275
|
+
});
|
|
69321
70276
|
await this.processedEventsStore.markCompleted(eventId);
|
|
69322
70277
|
} catch (error) {
|
|
69323
70278
|
console.error("slack handler error", error);
|
|
@@ -69325,12 +70280,7 @@ class SlackSocketService {
|
|
|
69325
70280
|
return;
|
|
69326
70281
|
} finally {
|
|
69327
70282
|
await ackReactionTask;
|
|
69328
|
-
await
|
|
69329
|
-
await removeConfiguredReaction(this.app.client, this.loadedConfig.raw.channels.slack.typingReaction, reactionTarget);
|
|
69330
|
-
await clearSlackAssistantThreadStatus(this.app.client, {
|
|
69331
|
-
channel: channelId,
|
|
69332
|
-
threadTs
|
|
69333
|
-
});
|
|
70283
|
+
await processingLease.release();
|
|
69334
70284
|
}
|
|
69335
70285
|
}
|
|
69336
70286
|
registerEvents() {
|
|
@@ -70290,22 +71240,17 @@ function startTelegramTypingHeartbeat(params) {
|
|
|
70290
71240
|
}
|
|
70291
71241
|
};
|
|
70292
71242
|
}
|
|
70293
|
-
async function
|
|
71243
|
+
async function beginTelegramTypingHeartbeat(params) {
|
|
70294
71244
|
try {
|
|
70295
71245
|
await params.sendTyping();
|
|
70296
71246
|
} catch (error) {
|
|
70297
71247
|
logTelegramTypingError(params.onError, error);
|
|
70298
71248
|
}
|
|
70299
|
-
|
|
71249
|
+
return startTelegramTypingHeartbeat({
|
|
70300
71250
|
sendTyping: params.sendTyping,
|
|
70301
71251
|
intervalMs: params.intervalMs ?? TELEGRAM_TYPING_HEARTBEAT_MS,
|
|
70302
71252
|
onError: params.onError
|
|
70303
71253
|
});
|
|
70304
|
-
try {
|
|
70305
|
-
return await params.run();
|
|
70306
|
-
} finally {
|
|
70307
|
-
stopHeartbeat();
|
|
70308
|
-
}
|
|
70309
71254
|
}
|
|
70310
71255
|
|
|
70311
71256
|
// src/channels/telegram/service.ts
|
|
@@ -70324,6 +71269,7 @@ var TELEGRAM_FULL_COMMANDS = [
|
|
|
70324
71269
|
{ command: "stop", description: "Interrupt current run" },
|
|
70325
71270
|
{ command: "nudge", description: "Send one extra Enter to the session" },
|
|
70326
71271
|
{ command: "followup", description: "Show or change follow-up mode" },
|
|
71272
|
+
{ command: "streaming", description: "Show or change streaming mode" },
|
|
70327
71273
|
{ command: "responsemode", description: "Show or change response mode" },
|
|
70328
71274
|
{ command: "queue", description: "Queue a later message behind the active run" },
|
|
70329
71275
|
{ command: "steer", description: "Steer the active run immediately" },
|
|
@@ -70411,6 +71357,7 @@ class TelegramPollingService {
|
|
|
70411
71357
|
loopPromise;
|
|
70412
71358
|
activePollController;
|
|
70413
71359
|
inFlightUpdates = new Set;
|
|
71360
|
+
processingIndicators = new ConversationProcessingIndicatorCoordinator;
|
|
70414
71361
|
constructor(loadedConfig, agentService, processedEventsStore, activityStore, accountId = "default", accountConfig) {
|
|
70415
71362
|
this.loadedConfig = loadedConfig;
|
|
70416
71363
|
this.agentService = agentService;
|
|
@@ -70551,11 +71498,21 @@ class TelegramPollingService {
|
|
|
70551
71498
|
const directMessages = this.loadedConfig.raw.channels.telegram.directMessages;
|
|
70552
71499
|
const senderId = message.from?.id != null ? String(message.from.id) : "";
|
|
70553
71500
|
const senderUsername = message.from?.username;
|
|
71501
|
+
const auth = resolveChannelAuth({
|
|
71502
|
+
config: this.loadedConfig.raw,
|
|
71503
|
+
agentId: routeInfo.route.agentId,
|
|
71504
|
+
identity: {
|
|
71505
|
+
platform: "telegram",
|
|
71506
|
+
conversationKind: routeInfo.conversationKind,
|
|
71507
|
+
senderId: senderId || undefined,
|
|
71508
|
+
chatId: String(message.chat.id)
|
|
71509
|
+
}
|
|
71510
|
+
});
|
|
70554
71511
|
if (!senderId || directMessages.policy === "disabled") {
|
|
70555
71512
|
await this.processedEventsStore.markCompleted(eventId);
|
|
70556
71513
|
return;
|
|
70557
71514
|
}
|
|
70558
|
-
if (directMessages.policy !== "open") {
|
|
71515
|
+
if (directMessages.policy !== "open" && !auth.mayBypassPairing) {
|
|
70559
71516
|
const storedAllowFrom = await readChannelAllowFromStore("telegram");
|
|
70560
71517
|
const allowed = isTelegramSenderAllowed({
|
|
70561
71518
|
allowFrom: [...directMessages.allowFrom, ...storedAllowFrom],
|
|
@@ -70644,11 +71601,21 @@ class TelegramPollingService {
|
|
|
70644
71601
|
chatName: message.chat.title?.trim() || undefined,
|
|
70645
71602
|
topicId: routeInfo.topicId != null ? String(routeInfo.topicId) : undefined
|
|
70646
71603
|
};
|
|
71604
|
+
const cliTool = getAgentEntry(this.loadedConfig, routeInfo.route.agentId)?.cliTool;
|
|
71605
|
+
const auth = resolveChannelAuth({
|
|
71606
|
+
config: this.loadedConfig.raw,
|
|
71607
|
+
agentId: routeInfo.route.agentId,
|
|
71608
|
+
identity
|
|
71609
|
+
});
|
|
71610
|
+
const protectedControlMutationRule = auth.mayManageProtectedResources ? undefined : DEFAULT_PROTECTED_CONTROL_RULE;
|
|
70647
71611
|
const agentPromptText = buildAgentPromptText({
|
|
70648
71612
|
text,
|
|
70649
71613
|
identity,
|
|
70650
71614
|
config: this.loadedConfig.raw.channels.telegram.agentPrompt,
|
|
70651
|
-
|
|
71615
|
+
cliTool,
|
|
71616
|
+
responseMode: routeInfo.route.responseMode,
|
|
71617
|
+
streaming: routeInfo.route.streaming,
|
|
71618
|
+
protectedControlMutationRule
|
|
70652
71619
|
});
|
|
70653
71620
|
const timingContext = {
|
|
70654
71621
|
platform: "telegram",
|
|
@@ -70663,52 +71630,71 @@ class TelegramPollingService {
|
|
|
70663
71630
|
responseMode: routeInfo.route.responseMode,
|
|
70664
71631
|
accountId: this.accountId
|
|
70665
71632
|
});
|
|
70666
|
-
await
|
|
70667
|
-
|
|
70668
|
-
|
|
70669
|
-
|
|
70670
|
-
|
|
70671
|
-
|
|
70672
|
-
|
|
70673
|
-
|
|
70674
|
-
|
|
70675
|
-
|
|
70676
|
-
senderId: message.from?.id != null ? String(message.from.id).trim() : undefined,
|
|
70677
|
-
text,
|
|
70678
|
-
agentPromptText,
|
|
70679
|
-
agentPromptBuilder: (nextText) => buildAgentPromptText({
|
|
70680
|
-
text: nextText,
|
|
70681
|
-
identity,
|
|
70682
|
-
config: this.loadedConfig.raw.channels.telegram.agentPrompt,
|
|
70683
|
-
responseMode: routeInfo.route.responseMode
|
|
70684
|
-
}),
|
|
70685
|
-
route: routeInfo.route,
|
|
70686
|
-
maxChars: this.getTelegramMaxChars(routeInfo.route.agentId),
|
|
70687
|
-
timingContext,
|
|
70688
|
-
postText: async (nextText) => {
|
|
70689
|
-
responseChunks = await postTelegramText({
|
|
70690
|
-
token: this.accountConfig.botToken,
|
|
70691
|
-
chatId: message.chat.id,
|
|
70692
|
-
text: nextText,
|
|
70693
|
-
topicId: routeInfo.topicId,
|
|
70694
|
-
omitThreadId: shouldOmitTelegramThreadId(routeInfo.topicId)
|
|
70695
|
-
});
|
|
70696
|
-
return responseChunks;
|
|
70697
|
-
},
|
|
70698
|
-
reconcileText: async (chunks, nextText) => {
|
|
70699
|
-
responseChunks = await reconcileTelegramText({
|
|
70700
|
-
token: this.accountConfig.botToken,
|
|
70701
|
-
chatId: message.chat.id,
|
|
70702
|
-
chunks,
|
|
70703
|
-
text: nextText,
|
|
70704
|
-
topicId: routeInfo.topicId,
|
|
70705
|
-
omitThreadId: shouldOmitTelegramThreadId(routeInfo.topicId)
|
|
70706
|
-
});
|
|
70707
|
-
return responseChunks;
|
|
70708
|
-
}
|
|
70709
|
-
});
|
|
71633
|
+
const processingLease = await this.processingIndicators.acquire({
|
|
71634
|
+
key: `telegram:${this.accountId}:${message.chat.id}:${routeInfo.topicId ?? "root"}`,
|
|
71635
|
+
activate: async () => beginTelegramTypingHeartbeat({
|
|
71636
|
+
sendTyping: () => this.sendTyping(message.chat.id, routeInfo.topicId),
|
|
71637
|
+
onError: (error) => {
|
|
71638
|
+
console.error("telegram typing failed", error);
|
|
71639
|
+
}
|
|
71640
|
+
}),
|
|
71641
|
+
onError: (_phase, error) => {
|
|
71642
|
+
console.error("telegram processing indicator failed", error);
|
|
70710
71643
|
}
|
|
70711
71644
|
});
|
|
71645
|
+
try {
|
|
71646
|
+
const interaction = await processChannelInteraction({
|
|
71647
|
+
agentService: this.agentService,
|
|
71648
|
+
sessionTarget: routeInfo.sessionTarget,
|
|
71649
|
+
identity,
|
|
71650
|
+
auth,
|
|
71651
|
+
senderId: message.from?.id != null ? String(message.from.id).trim() : undefined,
|
|
71652
|
+
text,
|
|
71653
|
+
agentPromptText,
|
|
71654
|
+
agentPromptBuilder: (nextText) => buildAgentPromptText({
|
|
71655
|
+
text: nextText,
|
|
71656
|
+
identity,
|
|
71657
|
+
config: this.loadedConfig.raw.channels.telegram.agentPrompt,
|
|
71658
|
+
cliTool,
|
|
71659
|
+
responseMode: routeInfo.route.responseMode,
|
|
71660
|
+
streaming: routeInfo.route.streaming,
|
|
71661
|
+
protectedControlMutationRule
|
|
71662
|
+
}),
|
|
71663
|
+
protectedControlMutationRule,
|
|
71664
|
+
route: routeInfo.route,
|
|
71665
|
+
maxChars: this.getTelegramMaxChars(routeInfo.route.agentId),
|
|
71666
|
+
timingContext,
|
|
71667
|
+
postText: async (nextText) => {
|
|
71668
|
+
responseChunks = await postTelegramText({
|
|
71669
|
+
token: this.accountConfig.botToken,
|
|
71670
|
+
chatId: message.chat.id,
|
|
71671
|
+
text: nextText,
|
|
71672
|
+
topicId: routeInfo.topicId,
|
|
71673
|
+
omitThreadId: shouldOmitTelegramThreadId(routeInfo.topicId)
|
|
71674
|
+
});
|
|
71675
|
+
return responseChunks;
|
|
71676
|
+
},
|
|
71677
|
+
reconcileText: async (chunks, nextText) => {
|
|
71678
|
+
responseChunks = await reconcileTelegramText({
|
|
71679
|
+
token: this.accountConfig.botToken,
|
|
71680
|
+
chatId: message.chat.id,
|
|
71681
|
+
chunks,
|
|
71682
|
+
text: nextText,
|
|
71683
|
+
topicId: routeInfo.topicId,
|
|
71684
|
+
omitThreadId: shouldOmitTelegramThreadId(routeInfo.topicId)
|
|
71685
|
+
});
|
|
71686
|
+
return responseChunks;
|
|
71687
|
+
}
|
|
71688
|
+
});
|
|
71689
|
+
await processingLease.setLifecycle({
|
|
71690
|
+
agentService: this.agentService,
|
|
71691
|
+
sessionTarget: routeInfo.sessionTarget,
|
|
71692
|
+
observerId: `telegram-processing:${message.chat.id}:${routeInfo.topicId ?? "root"}`,
|
|
71693
|
+
lifecycle: interaction.processingIndicatorLifecycle
|
|
71694
|
+
});
|
|
71695
|
+
} finally {
|
|
71696
|
+
await processingLease.release();
|
|
71697
|
+
}
|
|
70712
71698
|
await this.processedEventsStore.markCompleted(eventId);
|
|
70713
71699
|
} catch (error) {
|
|
70714
71700
|
console.error("telegram handler error", error);
|
|
@@ -71109,9 +72095,9 @@ var defaultMessageCliDependencies = {
|
|
|
71109
72095
|
loadConfig,
|
|
71110
72096
|
plugins: listChannelPlugins(),
|
|
71111
72097
|
print: (text) => console.log(text),
|
|
71112
|
-
recordConversationReply: async ({ loadedConfig, target, kind }) => {
|
|
72098
|
+
recordConversationReply: async ({ loadedConfig, target, kind, source }) => {
|
|
71113
72099
|
const agentService = new AgentService(loadedConfig);
|
|
71114
|
-
await agentService.recordConversationReply(target, kind);
|
|
72100
|
+
await agentService.recordConversationReply(target, kind, source);
|
|
71115
72101
|
}
|
|
71116
72102
|
};
|
|
71117
72103
|
function parseRepeatedOption2(args, name) {
|
|
@@ -71128,12 +72114,12 @@ function parseRepeatedOption2(args, name) {
|
|
|
71128
72114
|
}
|
|
71129
72115
|
return values;
|
|
71130
72116
|
}
|
|
71131
|
-
function
|
|
72117
|
+
function parseOptionValue4(args, name) {
|
|
71132
72118
|
const values = parseRepeatedOption2(args, name);
|
|
71133
72119
|
return values.length > 0 ? values.at(-1) : undefined;
|
|
71134
72120
|
}
|
|
71135
72121
|
function parseIntegerOption(args, name) {
|
|
71136
|
-
const raw =
|
|
72122
|
+
const raw = parseOptionValue4(args, name);
|
|
71137
72123
|
if (!raw) {
|
|
71138
72124
|
return;
|
|
71139
72125
|
}
|
|
@@ -71162,25 +72148,25 @@ function parseMessageCommand(args) {
|
|
|
71162
72148
|
}
|
|
71163
72149
|
const action = rawAction;
|
|
71164
72150
|
const rest = args.slice(1);
|
|
71165
|
-
const channel =
|
|
72151
|
+
const channel = parseOptionValue4(rest, "--channel");
|
|
71166
72152
|
if (channel !== "slack" && channel !== "telegram") {
|
|
71167
72153
|
throw new Error("--channel <slack|telegram> is required");
|
|
71168
72154
|
}
|
|
71169
72155
|
return {
|
|
71170
72156
|
action,
|
|
71171
72157
|
channel,
|
|
71172
|
-
account:
|
|
71173
|
-
target:
|
|
71174
|
-
message:
|
|
71175
|
-
media:
|
|
71176
|
-
messageId:
|
|
71177
|
-
emoji:
|
|
72158
|
+
account: parseOptionValue4(rest, "--account"),
|
|
72159
|
+
target: parseOptionValue4(rest, "--target"),
|
|
72160
|
+
message: parseOptionValue4(rest, "--message") ?? parseOptionValue4(rest, "-m"),
|
|
72161
|
+
media: parseOptionValue4(rest, "--media"),
|
|
72162
|
+
messageId: parseOptionValue4(rest, "--message-id"),
|
|
72163
|
+
emoji: parseOptionValue4(rest, "--emoji"),
|
|
71178
72164
|
remove: hasFlag3(rest, "--remove"),
|
|
71179
|
-
threadId:
|
|
71180
|
-
replyTo:
|
|
72165
|
+
threadId: parseOptionValue4(rest, "--thread-id"),
|
|
72166
|
+
replyTo: parseOptionValue4(rest, "--reply-to"),
|
|
71181
72167
|
limit: parseIntegerOption(rest, "--limit"),
|
|
71182
|
-
query:
|
|
71183
|
-
pollQuestion:
|
|
72168
|
+
query: parseOptionValue4(rest, "--query"),
|
|
72169
|
+
pollQuestion: parseOptionValue4(rest, "--poll-question"),
|
|
71184
72170
|
pollOptions: parseRepeatedOption2(rest, "--poll-option"),
|
|
71185
72171
|
forceDocument: hasFlag3(rest, "--force-document"),
|
|
71186
72172
|
silent: hasFlag3(rest, "--silent"),
|
|
@@ -71223,7 +72209,9 @@ async function runMessageCli(args, dependencies = defaultMessageCliDependencies)
|
|
|
71223
72209
|
throw new Error("--progress and --final cannot be used together");
|
|
71224
72210
|
}
|
|
71225
72211
|
assertTarget(command);
|
|
71226
|
-
const loadedConfig = await dependencies.loadConfig(getConfigPath()
|
|
72212
|
+
const loadedConfig = await dependencies.loadConfig(getConfigPath(), {
|
|
72213
|
+
materializeChannels: [command.channel]
|
|
72214
|
+
});
|
|
71227
72215
|
const plugin = dependencies.plugins.find((entry) => entry.id === command.channel);
|
|
71228
72216
|
if (!plugin) {
|
|
71229
72217
|
throw new Error(`Unsupported message channel: ${command.channel}`);
|
|
@@ -71238,7 +72226,8 @@ async function runMessageCli(args, dependencies = defaultMessageCliDependencies)
|
|
|
71238
72226
|
await dependencies.recordConversationReply({
|
|
71239
72227
|
loadedConfig,
|
|
71240
72228
|
target: replyTarget,
|
|
71241
|
-
kind: resolveReplyKind(command)
|
|
72229
|
+
kind: resolveReplyKind(command),
|
|
72230
|
+
source: "message-tool"
|
|
71242
72231
|
});
|
|
71243
72232
|
}
|
|
71244
72233
|
if (command.json) {
|
|
@@ -71250,7 +72239,7 @@ async function runMessageCli(args, dependencies = defaultMessageCliDependencies)
|
|
|
71250
72239
|
|
|
71251
72240
|
// src/control/runtime-supervisor.ts
|
|
71252
72241
|
import { statSync as statSync4, watch } from "node:fs";
|
|
71253
|
-
import { basename as basename4, dirname as
|
|
72242
|
+
import { basename as basename4, dirname as dirname14 } from "node:path";
|
|
71254
72243
|
|
|
71255
72244
|
// src/channels/processed-events-store.ts
|
|
71256
72245
|
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
|
|
@@ -71328,7 +72317,7 @@ class ProcessedEventsStore {
|
|
|
71328
72317
|
}
|
|
71329
72318
|
|
|
71330
72319
|
// src/control/activity-store.ts
|
|
71331
|
-
import { dirname as
|
|
72320
|
+
import { dirname as dirname13 } from "node:path";
|
|
71332
72321
|
class ActivityStore {
|
|
71333
72322
|
filePath;
|
|
71334
72323
|
constructor(filePath = getDefaultActivityStorePath()) {
|
|
@@ -71367,7 +72356,7 @@ class ActivityStore {
|
|
|
71367
72356
|
};
|
|
71368
72357
|
}
|
|
71369
72358
|
async write(document2) {
|
|
71370
|
-
await ensureDir2(
|
|
72359
|
+
await ensureDir2(dirname13(this.filePath));
|
|
71371
72360
|
await writeTextFile(this.filePath, `${JSON.stringify(document2, null, 2)}
|
|
71372
72361
|
`);
|
|
71373
72362
|
}
|
|
@@ -71609,7 +72598,7 @@ class RuntimeSupervisor {
|
|
|
71609
72598
|
if (this.configWatcher) {
|
|
71610
72599
|
return;
|
|
71611
72600
|
}
|
|
71612
|
-
const watchedDir =
|
|
72601
|
+
const watchedDir = dirname14(loadedConfig.configPath);
|
|
71613
72602
|
const watchedFile = basename4(loadedConfig.configPath);
|
|
71614
72603
|
this.configWatcher = watch(watchedDir, (_eventType, filename) => {
|
|
71615
72604
|
if (filename && filename.toString() !== watchedFile) {
|
|
@@ -71711,7 +72700,7 @@ async function getRuntimeOperatorSummary(params) {
|
|
|
71711
72700
|
const agentSummaries = loadedConfig.raw.agents.list.map((entry) => {
|
|
71712
72701
|
const resolved = new AgentService(loadedConfig).getResolvedAgentConfig(entry.id);
|
|
71713
72702
|
const tool = deriveAgentTool(loadedConfig, entry.id);
|
|
71714
|
-
const bootstrapState = getBootstrapWorkspaceState(resolved.workspacePath, entry.bootstrap?.mode, tool.cliTool === "codex" || tool.cliTool === "claude" ? tool.cliTool : undefined);
|
|
72703
|
+
const bootstrapState = getBootstrapWorkspaceState(resolved.workspacePath, entry.bootstrap?.mode, tool.cliTool === "codex" || tool.cliTool === "claude" || tool.cliTool === "gemini" ? tool.cliTool : undefined);
|
|
71715
72704
|
return {
|
|
71716
72705
|
id: entry.id,
|
|
71717
72706
|
cliTool: tool.cliTool,
|
|
@@ -72048,6 +73037,9 @@ function printCommandOutcomeBanner(outcome) {
|
|
|
72048
73037
|
console.log("+---------+");
|
|
72049
73038
|
console.log("");
|
|
72050
73039
|
}
|
|
73040
|
+
function printCommandOutcomeFooter(outcome) {
|
|
73041
|
+
printCommandOutcomeBanner(outcome);
|
|
73042
|
+
}
|
|
72051
73043
|
function getPrimaryWorkspacePath(summary) {
|
|
72052
73044
|
const preferredAgentId = summary.channelSummaries.find((channel) => channel.enabled)?.defaultAgentId ?? "default";
|
|
72053
73045
|
return summary.agentSummaries.find((agent) => agent.id === preferredAgentId)?.workspacePath ?? summary.agentSummaries[0]?.workspacePath;
|
|
@@ -72068,13 +73060,18 @@ function printMissingBootstrapOptions(commandName) {
|
|
|
72068
73060
|
console.log(` clisbot ${commandName} --cli codex --bot-type team`);
|
|
72069
73061
|
console.log(` clisbot ${commandName} --cli claude --bot-type personal`);
|
|
72070
73062
|
console.log(` clisbot ${commandName} --cli claude --bot-type team`);
|
|
73063
|
+
console.log(` clisbot ${commandName} --cli gemini --bot-type personal`);
|
|
73064
|
+
console.log(` clisbot ${commandName} --cli gemini --bot-type team`);
|
|
72071
73065
|
console.log("Manual setup is still available with `clisbot agents add ...`.");
|
|
72072
73066
|
for (const line of renderOperatorHelpLines()) {
|
|
72073
73067
|
console.log(line);
|
|
72074
73068
|
}
|
|
73069
|
+
if (commandName === "start") {
|
|
73070
|
+
printCommandOutcomeFooter("failure");
|
|
73071
|
+
}
|
|
72075
73072
|
}
|
|
72076
73073
|
function hasLiteralMemCredentials(flags) {
|
|
72077
|
-
return flags
|
|
73074
|
+
return hasLiteralBootstrapCredentials(flags);
|
|
72078
73075
|
}
|
|
72079
73076
|
async function prepareBootstrapState(rawArgs, commandName, options = {
|
|
72080
73077
|
runtimeRunning: false
|
|
@@ -72090,6 +73087,9 @@ async function prepareBootstrapState(rawArgs, commandName, options = {
|
|
|
72090
73087
|
for (const line of renderMissingTokenWarningLines()) {
|
|
72091
73088
|
console.log(line);
|
|
72092
73089
|
}
|
|
73090
|
+
if (commandName === "start") {
|
|
73091
|
+
printCommandOutcomeFooter("failure");
|
|
73092
|
+
}
|
|
72093
73093
|
return null;
|
|
72094
73094
|
}
|
|
72095
73095
|
if (commandName === "init" && hasLiteralMemCredentials(bootstrapFlags) && !bootstrapFlags.persist) {
|
|
@@ -72151,6 +73151,7 @@ async function ensureDefaultAgentBootstrap(state, options, commandName) {
|
|
|
72151
73151
|
for (const line of renderOperatorHelpLines()) {
|
|
72152
73152
|
console.log(line);
|
|
72153
73153
|
}
|
|
73154
|
+
printCommandOutcomeFooter("failure");
|
|
72154
73155
|
return false;
|
|
72155
73156
|
}
|
|
72156
73157
|
}
|
|
@@ -72224,11 +73225,14 @@ async function serveForeground() {
|
|
|
72224
73225
|
async function start(args = []) {
|
|
72225
73226
|
const runtimeStatus = await getRuntimeStatus();
|
|
72226
73227
|
const bootstrapFlags = parseBootstrapFlags(args);
|
|
72227
|
-
|
|
72228
|
-
|
|
73228
|
+
const restartForLiteralBootstrap = runtimeStatus.running && hasLiteralMemCredentials(bootstrapFlags);
|
|
73229
|
+
if (restartForLiteralBootstrap) {
|
|
73230
|
+
await stopDetachedRuntime({
|
|
73231
|
+
configPath: runtimeStatus.configPath
|
|
73232
|
+
});
|
|
72229
73233
|
}
|
|
72230
73234
|
const state = await prepareBootstrapState(args, "start", {
|
|
72231
|
-
runtimeRunning: runtimeStatus.running,
|
|
73235
|
+
runtimeRunning: restartForLiteralBootstrap ? false : runtimeStatus.running,
|
|
72232
73236
|
bootstrapFlags
|
|
72233
73237
|
});
|
|
72234
73238
|
if (!state) {
|
|
@@ -72244,13 +73248,14 @@ async function start(args = []) {
|
|
|
72244
73248
|
for (const line of renderConfiguredChannelTokenStatusLines(state.config, runtimeMemEnv)) {
|
|
72245
73249
|
console.log(line);
|
|
72246
73250
|
}
|
|
72247
|
-
if (!runtimeStatus.running) {
|
|
73251
|
+
if (restartForLiteralBootstrap || !runtimeStatus.running) {
|
|
72248
73252
|
const tokenIssueLines = renderConfiguredChannelTokenIssueLines(state.config, runtimeMemEnv);
|
|
72249
73253
|
for (const line of tokenIssueLines) {
|
|
72250
73254
|
console.log(line);
|
|
72251
73255
|
}
|
|
72252
73256
|
if (tokenIssueLines.length > 0) {
|
|
72253
73257
|
printCommandOutcomeBanner("failure");
|
|
73258
|
+
printCommandOutcomeFooter("failure");
|
|
72254
73259
|
return;
|
|
72255
73260
|
}
|
|
72256
73261
|
}
|
|
@@ -72266,7 +73271,7 @@ async function start(args = []) {
|
|
|
72266
73271
|
const result = await startDetachedRuntime({
|
|
72267
73272
|
scriptPath: fileURLToPath4(import.meta.url),
|
|
72268
73273
|
configPath: state.configResult.configPath,
|
|
72269
|
-
extraEnv: runtimeStatus.running ?
|
|
73274
|
+
extraEnv: restartForLiteralBootstrap || !runtimeStatus.running ? runtimeMemEnv : undefined,
|
|
72270
73275
|
runtimeCredentialsPath: getDefaultRuntimeCredentialsPath()
|
|
72271
73276
|
});
|
|
72272
73277
|
if (result.alreadyRunning) {
|
|
@@ -72284,6 +73289,7 @@ async function start(args = []) {
|
|
|
72284
73289
|
console.log(`config: ${result.configPath}`);
|
|
72285
73290
|
console.log(`log: ${result.logPath}`);
|
|
72286
73291
|
console.log(renderStartSummary(summary));
|
|
73292
|
+
printCommandOutcomeFooter("success");
|
|
72287
73293
|
} catch (error) {
|
|
72288
73294
|
printCommandOutcomeBanner("success");
|
|
72289
73295
|
console.log(`clisbot is already running with pid: ${result.pid}`);
|
|
@@ -72294,6 +73300,7 @@ async function start(args = []) {
|
|
|
72294
73300
|
for (const line of renderRuntimeErrorLines("failed to render already-running summary", error)) {
|
|
72295
73301
|
console.error(line);
|
|
72296
73302
|
}
|
|
73303
|
+
printCommandOutcomeFooter("success");
|
|
72297
73304
|
}
|
|
72298
73305
|
return;
|
|
72299
73306
|
}
|
|
@@ -72314,6 +73321,7 @@ async function start(args = []) {
|
|
|
72314
73321
|
console.log(`config: ${result.configPath}`);
|
|
72315
73322
|
console.log(`log: ${result.logPath}`);
|
|
72316
73323
|
console.log(renderStartSummary(summary));
|
|
73324
|
+
printCommandOutcomeFooter("success");
|
|
72317
73325
|
} catch (error) {
|
|
72318
73326
|
printCommandOutcomeBanner("success");
|
|
72319
73327
|
console.log(`clisbot started with pid: ${result.pid}`);
|
|
@@ -72322,6 +73330,7 @@ async function start(args = []) {
|
|
|
72322
73330
|
for (const line of renderRuntimeErrorLines("failed to render start summary", error)) {
|
|
72323
73331
|
console.error(line);
|
|
72324
73332
|
}
|
|
73333
|
+
printCommandOutcomeFooter("success");
|
|
72325
73334
|
}
|
|
72326
73335
|
}
|
|
72327
73336
|
async function printCliError(error) {
|
|
@@ -72363,15 +73372,18 @@ async function stop(hard = false) {
|
|
|
72363
73372
|
if (!result.stopped && !hard) {
|
|
72364
73373
|
printCommandOutcomeBanner("failure");
|
|
72365
73374
|
console.log("clisbot is not running");
|
|
73375
|
+
printCommandOutcomeFooter("failure");
|
|
72366
73376
|
return;
|
|
72367
73377
|
}
|
|
72368
73378
|
if (hard) {
|
|
72369
73379
|
printCommandOutcomeBanner("success");
|
|
72370
73380
|
console.log(result.stopped ? "clisbot stopped and tmux sessions cleaned up" : "clisbot was not running, but tmux sessions were cleaned up");
|
|
73381
|
+
printCommandOutcomeFooter("success");
|
|
72371
73382
|
return;
|
|
72372
73383
|
}
|
|
72373
73384
|
printCommandOutcomeBanner("success");
|
|
72374
73385
|
console.log("clisbot stopped");
|
|
73386
|
+
printCommandOutcomeFooter("success");
|
|
72375
73387
|
}
|
|
72376
73388
|
async function restart() {
|
|
72377
73389
|
await stopDetachedRuntime({
|