clisbot 0.1.13 → 0.1.16
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 +15 -9
- package/config/clisbot.json.template +88 -21
- package/dist/main.js +1413 -763
- package/package.json +1 -1
- package/templates/slack/default/app-manifest-guide.md +111 -0
- package/templates/slack/default/app-manifest.json +13 -36
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
|
}
|
|
@@ -59747,6 +59703,39 @@ function convertLocalDateTimeToUtcMs(params) {
|
|
|
59747
59703
|
return null;
|
|
59748
59704
|
}
|
|
59749
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
|
+
|
|
59750
59739
|
// src/config/schema.ts
|
|
59751
59740
|
var defaultRunnerSessionIdConfig = {
|
|
59752
59741
|
create: {
|
|
@@ -59863,10 +59852,46 @@ var runnerOverrideSchema = exports_external.object({
|
|
|
59863
59852
|
var agentBootstrapSchema = exports_external.object({
|
|
59864
59853
|
mode: exports_external.enum(SUPPORTED_BOOTSTRAP_MODES).default("personal-assistant")
|
|
59865
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
|
+
});
|
|
59866
59890
|
var agentOverrideSchema = exports_external.object({
|
|
59867
59891
|
workspace: exports_external.string().optional(),
|
|
59868
59892
|
responseMode: exports_external.enum(["capture-pane", "message-tool"]).optional(),
|
|
59869
59893
|
additionalMessageMode: exports_external.enum(["queue", "steer"]).optional(),
|
|
59894
|
+
auth: agentAuthSchema.optional(),
|
|
59870
59895
|
runner: runnerOverrideSchema.optional(),
|
|
59871
59896
|
stream: streamSchema.partial().optional(),
|
|
59872
59897
|
session: sessionSchema.partial().optional()
|
|
@@ -59881,6 +59906,19 @@ var agentEntrySchema = agentOverrideSchema.extend({
|
|
|
59881
59906
|
});
|
|
59882
59907
|
var agentDefaultsSchema = exports_external.object({
|
|
59883
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
|
+
}),
|
|
59884
59922
|
runner: runnerSchema.default({
|
|
59885
59923
|
command: "codex",
|
|
59886
59924
|
args: [
|
|
@@ -59947,6 +59985,7 @@ var channelAgentPromptSchema = exports_external.object({
|
|
|
59947
59985
|
});
|
|
59948
59986
|
var channelResponseModeSchema = exports_external.enum(["capture-pane", "message-tool"]);
|
|
59949
59987
|
var channelAdditionalMessageModeSchema = exports_external.enum(["queue", "steer"]);
|
|
59988
|
+
var channelVerboseSchema = exports_external.enum(["off", "minimal"]);
|
|
59950
59989
|
var timezoneSchema = exports_external.string().refine(isValidLoopTimezone, {
|
|
59951
59990
|
message: "Expected a valid IANA timezone such as Asia/Ho_Chi_Minh"
|
|
59952
59991
|
});
|
|
@@ -59960,6 +59999,7 @@ var slackRouteSchema = exports_external.object({
|
|
|
59960
59999
|
response: slackResponseSchema.optional(),
|
|
59961
60000
|
responseMode: channelResponseModeSchema.optional(),
|
|
59962
60001
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60002
|
+
verbose: channelVerboseSchema.optional(),
|
|
59963
60003
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
59964
60004
|
timezone: timezoneSchema.optional()
|
|
59965
60005
|
});
|
|
@@ -59973,6 +60013,7 @@ var telegramTopicRouteSchema = exports_external.object({
|
|
|
59973
60013
|
response: slackResponseSchema.optional(),
|
|
59974
60014
|
responseMode: channelResponseModeSchema.optional(),
|
|
59975
60015
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60016
|
+
verbose: channelVerboseSchema.optional(),
|
|
59976
60017
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
59977
60018
|
timezone: timezoneSchema.optional()
|
|
59978
60019
|
});
|
|
@@ -59986,6 +60027,7 @@ var telegramGroupRouteSchema = exports_external.object({
|
|
|
59986
60027
|
response: slackResponseSchema.optional(),
|
|
59987
60028
|
responseMode: channelResponseModeSchema.optional(),
|
|
59988
60029
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60030
|
+
verbose: channelVerboseSchema.optional(),
|
|
59989
60031
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
59990
60032
|
timezone: timezoneSchema.optional(),
|
|
59991
60033
|
topics: exports_external.record(exports_external.string(), telegramTopicRouteSchema).default({})
|
|
@@ -60003,6 +60045,7 @@ var telegramDirectMessagesSchema = exports_external.object({
|
|
|
60003
60045
|
response: slackResponseSchema.optional(),
|
|
60004
60046
|
responseMode: channelResponseModeSchema.optional(),
|
|
60005
60047
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60048
|
+
verbose: channelVerboseSchema.optional(),
|
|
60006
60049
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
60007
60050
|
timezone: timezoneSchema.optional()
|
|
60008
60051
|
});
|
|
@@ -60030,10 +60073,7 @@ var telegramSchema = exports_external.object({
|
|
|
60030
60073
|
allowBots: exports_external.boolean().default(false),
|
|
60031
60074
|
groupPolicy: slackConversationPolicySchema.default("allowlist"),
|
|
60032
60075
|
defaultAgentId: exports_external.string().default("default"),
|
|
60033
|
-
privilegeCommands: privilegeCommandsSchema.
|
|
60034
|
-
enabled: false,
|
|
60035
|
-
allowUsers: []
|
|
60036
|
-
}),
|
|
60076
|
+
privilegeCommands: privilegeCommandsSchema.optional(),
|
|
60037
60077
|
commandPrefixes: commandPrefixesSchema.default({
|
|
60038
60078
|
slash: ["::", "\\"],
|
|
60039
60079
|
bash: ["!"]
|
|
@@ -60042,6 +60082,7 @@ var telegramSchema = exports_external.object({
|
|
|
60042
60082
|
response: slackResponseSchema.default("final"),
|
|
60043
60083
|
responseMode: channelResponseModeSchema.default("message-tool"),
|
|
60044
60084
|
additionalMessageMode: channelAdditionalMessageModeSchema.default("steer"),
|
|
60085
|
+
verbose: channelVerboseSchema.default("minimal"),
|
|
60045
60086
|
followUp: slackFollowUpSchema.default({
|
|
60046
60087
|
mode: "auto",
|
|
60047
60088
|
participationTtlMin: 5
|
|
@@ -60072,6 +60113,7 @@ var directMessagesSchema = exports_external.object({
|
|
|
60072
60113
|
response: slackResponseSchema.optional(),
|
|
60073
60114
|
responseMode: channelResponseModeSchema.optional(),
|
|
60074
60115
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60116
|
+
verbose: channelVerboseSchema.optional(),
|
|
60075
60117
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
60076
60118
|
timezone: timezoneSchema.optional()
|
|
60077
60119
|
});
|
|
@@ -60106,10 +60148,7 @@ var slackSchema = exports_external.object({
|
|
|
60106
60148
|
channelPolicy: slackConversationPolicySchema.default("allowlist"),
|
|
60107
60149
|
groupPolicy: slackConversationPolicySchema.default("allowlist"),
|
|
60108
60150
|
defaultAgentId: exports_external.string().default("default"),
|
|
60109
|
-
privilegeCommands: privilegeCommandsSchema.
|
|
60110
|
-
enabled: false,
|
|
60111
|
-
allowUsers: []
|
|
60112
|
-
}),
|
|
60151
|
+
privilegeCommands: privilegeCommandsSchema.optional(),
|
|
60113
60152
|
commandPrefixes: commandPrefixesSchema.default({
|
|
60114
60153
|
slash: ["::", "\\"],
|
|
60115
60154
|
bash: ["!"]
|
|
@@ -60118,6 +60157,7 @@ var slackSchema = exports_external.object({
|
|
|
60118
60157
|
response: slackResponseSchema.default("final"),
|
|
60119
60158
|
responseMode: channelResponseModeSchema.default("message-tool"),
|
|
60120
60159
|
additionalMessageMode: channelAdditionalMessageModeSchema.default("steer"),
|
|
60160
|
+
verbose: channelVerboseSchema.default("minimal"),
|
|
60121
60161
|
followUp: slackFollowUpSchema.default({
|
|
60122
60162
|
mode: "auto",
|
|
60123
60163
|
participationTtlMin: 5
|
|
@@ -60177,6 +60217,45 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
60177
60217
|
identityLinks: {},
|
|
60178
60218
|
storePath: "~/.clisbot/state/sessions.json"
|
|
60179
60219
|
}),
|
|
60220
|
+
app: exports_external.object({
|
|
60221
|
+
auth: appAuthSchema.default({
|
|
60222
|
+
ownerClaimWindowMinutes: 30,
|
|
60223
|
+
defaultRole: "member",
|
|
60224
|
+
roles: {
|
|
60225
|
+
owner: {
|
|
60226
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60227
|
+
users: []
|
|
60228
|
+
},
|
|
60229
|
+
admin: {
|
|
60230
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60231
|
+
users: []
|
|
60232
|
+
},
|
|
60233
|
+
member: {
|
|
60234
|
+
allow: [],
|
|
60235
|
+
users: []
|
|
60236
|
+
}
|
|
60237
|
+
}
|
|
60238
|
+
})
|
|
60239
|
+
}).default({
|
|
60240
|
+
auth: {
|
|
60241
|
+
ownerClaimWindowMinutes: 30,
|
|
60242
|
+
defaultRole: "member",
|
|
60243
|
+
roles: {
|
|
60244
|
+
owner: {
|
|
60245
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60246
|
+
users: []
|
|
60247
|
+
},
|
|
60248
|
+
admin: {
|
|
60249
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60250
|
+
users: []
|
|
60251
|
+
},
|
|
60252
|
+
member: {
|
|
60253
|
+
allow: [],
|
|
60254
|
+
users: []
|
|
60255
|
+
}
|
|
60256
|
+
}
|
|
60257
|
+
}
|
|
60258
|
+
}),
|
|
60180
60259
|
agents: exports_external.object({
|
|
60181
60260
|
defaults: agentDefaultsSchema,
|
|
60182
60261
|
list: exports_external.array(agentEntrySchema).default([
|
|
@@ -60229,6 +60308,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
60229
60308
|
response: "final",
|
|
60230
60309
|
responseMode: "message-tool",
|
|
60231
60310
|
additionalMessageMode: "steer",
|
|
60311
|
+
verbose: "minimal",
|
|
60232
60312
|
followUp: {
|
|
60233
60313
|
mode: "auto",
|
|
60234
60314
|
participationTtlMin: 5
|
|
@@ -60268,17 +60348,19 @@ function resolveMaxRuntimeMs(stream) {
|
|
|
60268
60348
|
defaultMinutes: 15
|
|
60269
60349
|
});
|
|
60270
60350
|
}
|
|
60271
|
-
async function loadConfig(configPath = getDefaultConfigPath()) {
|
|
60351
|
+
async function loadConfig(configPath = getDefaultConfigPath(), options = {}) {
|
|
60272
60352
|
const expandedConfigPath = expandHomePath(configPath);
|
|
60273
60353
|
const text = await readTextFile(expandedConfigPath);
|
|
60274
60354
|
const parsed = JSON.parse(text);
|
|
60355
|
+
assertNoLegacyPrivilegeCommands(parsed);
|
|
60275
60356
|
const withDynamicDefaults = clisbotConfigSchema.parse(applyDynamicPathDefaults(parsed));
|
|
60276
60357
|
const substituted = resolveConfigEnvVars(withDynamicDefaults, process.env, {
|
|
60277
60358
|
skipPaths: getCredentialSkipPaths(withDynamicDefaults)
|
|
60278
60359
|
});
|
|
60279
60360
|
const validated = clisbotConfigSchema.parse(substituted);
|
|
60280
60361
|
const materialized = materializeRuntimeChannelCredentials(validated, {
|
|
60281
|
-
env: process.env
|
|
60362
|
+
env: process.env,
|
|
60363
|
+
materializeChannels: options.materializeChannels
|
|
60282
60364
|
});
|
|
60283
60365
|
return materializeLoadedConfig(expandedConfigPath, materialized);
|
|
60284
60366
|
}
|
|
@@ -60286,6 +60368,7 @@ async function loadConfigWithoutEnvResolution(configPath = getDefaultConfigPath(
|
|
|
60286
60368
|
const expandedConfigPath = expandHomePath(configPath);
|
|
60287
60369
|
const text = await readTextFile(expandedConfigPath);
|
|
60288
60370
|
const parsed = JSON.parse(text);
|
|
60371
|
+
assertNoLegacyPrivilegeCommands(parsed);
|
|
60289
60372
|
const validated = clisbotConfigSchema.parse(applyDynamicPathDefaults(parsed));
|
|
60290
60373
|
return materializeLoadedConfig(expandedConfigPath, validated);
|
|
60291
60374
|
}
|
|
@@ -60348,6 +60431,21 @@ function applyDynamicPathDefaults(parsed, env = process.env) {
|
|
|
60348
60431
|
function isRecord2(value) {
|
|
60349
60432
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
60350
60433
|
}
|
|
60434
|
+
function assertNoLegacyPrivilegeCommands(value, path2 = "root") {
|
|
60435
|
+
if (Array.isArray(value)) {
|
|
60436
|
+
value.forEach((entry, index) => assertNoLegacyPrivilegeCommands(entry, `${path2}[${index}]`));
|
|
60437
|
+
return;
|
|
60438
|
+
}
|
|
60439
|
+
if (!isRecord2(value)) {
|
|
60440
|
+
return;
|
|
60441
|
+
}
|
|
60442
|
+
if (Object.prototype.hasOwnProperty.call(value, "privilegeCommands")) {
|
|
60443
|
+
throw new Error(`Unsupported config key at ${path2}.privilegeCommands. Move routed permissions to app.auth and agents.<id>.auth.`);
|
|
60444
|
+
}
|
|
60445
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
60446
|
+
assertNoLegacyPrivilegeCommands(entry, `${path2}.${key}`);
|
|
60447
|
+
}
|
|
60448
|
+
}
|
|
60351
60449
|
function getAgentEntry(config, agentId) {
|
|
60352
60450
|
return config.raw.agents.list.find((entry) => entry.id === agentId);
|
|
60353
60451
|
}
|
|
@@ -60384,9 +60482,42 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60384
60482
|
identityLinks: {},
|
|
60385
60483
|
storePath: sessionStorePath
|
|
60386
60484
|
},
|
|
60485
|
+
app: {
|
|
60486
|
+
auth: {
|
|
60487
|
+
ownerClaimWindowMinutes: 30,
|
|
60488
|
+
defaultRole: "member",
|
|
60489
|
+
roles: {
|
|
60490
|
+
owner: {
|
|
60491
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60492
|
+
users: []
|
|
60493
|
+
},
|
|
60494
|
+
admin: {
|
|
60495
|
+
allow: [...APP_ADMIN_PERMISSIONS],
|
|
60496
|
+
users: []
|
|
60497
|
+
},
|
|
60498
|
+
member: {
|
|
60499
|
+
allow: [],
|
|
60500
|
+
users: []
|
|
60501
|
+
}
|
|
60502
|
+
}
|
|
60503
|
+
}
|
|
60504
|
+
},
|
|
60387
60505
|
agents: {
|
|
60388
60506
|
defaults: {
|
|
60389
60507
|
workspace: workspaceTemplate,
|
|
60508
|
+
auth: {
|
|
60509
|
+
defaultRole: "member",
|
|
60510
|
+
roles: {
|
|
60511
|
+
admin: {
|
|
60512
|
+
allow: [...DEFAULT_AGENT_ADMIN_PERMISSIONS],
|
|
60513
|
+
users: []
|
|
60514
|
+
},
|
|
60515
|
+
member: {
|
|
60516
|
+
allow: [...DEFAULT_AGENT_MEMBER_PERMISSIONS],
|
|
60517
|
+
users: []
|
|
60518
|
+
}
|
|
60519
|
+
}
|
|
60520
|
+
},
|
|
60390
60521
|
runner: {
|
|
60391
60522
|
command: "codex",
|
|
60392
60523
|
args: [
|
|
@@ -60486,10 +60617,6 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60486
60617
|
channelPolicy: "allowlist",
|
|
60487
60618
|
groupPolicy: "allowlist",
|
|
60488
60619
|
defaultAgentId: "default",
|
|
60489
|
-
privilegeCommands: {
|
|
60490
|
-
enabled: false,
|
|
60491
|
-
allowUsers: []
|
|
60492
|
-
},
|
|
60493
60620
|
commandPrefixes: {
|
|
60494
60621
|
slash: ["::", "\\"],
|
|
60495
60622
|
bash: ["!"]
|
|
@@ -60498,6 +60625,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60498
60625
|
response: "final",
|
|
60499
60626
|
responseMode: "message-tool",
|
|
60500
60627
|
additionalMessageMode: "steer",
|
|
60628
|
+
verbose: "minimal",
|
|
60501
60629
|
followUp: {
|
|
60502
60630
|
mode: "auto",
|
|
60503
60631
|
participationTtlMin: 5
|
|
@@ -60509,11 +60637,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60509
60637
|
policy: "pairing",
|
|
60510
60638
|
allowFrom: [],
|
|
60511
60639
|
requireMention: false,
|
|
60512
|
-
agentId: "default"
|
|
60513
|
-
privilegeCommands: {
|
|
60514
|
-
enabled: false,
|
|
60515
|
-
allowUsers: []
|
|
60516
|
-
}
|
|
60640
|
+
agentId: "default"
|
|
60517
60641
|
}
|
|
60518
60642
|
},
|
|
60519
60643
|
telegram: {
|
|
@@ -60534,10 +60658,6 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60534
60658
|
allowBots: false,
|
|
60535
60659
|
groupPolicy: "allowlist",
|
|
60536
60660
|
defaultAgentId: "default",
|
|
60537
|
-
privilegeCommands: {
|
|
60538
|
-
enabled: false,
|
|
60539
|
-
allowUsers: []
|
|
60540
|
-
},
|
|
60541
60661
|
commandPrefixes: {
|
|
60542
60662
|
slash: ["::", "\\"],
|
|
60543
60663
|
bash: ["!"]
|
|
@@ -60546,6 +60666,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60546
60666
|
response: "final",
|
|
60547
60667
|
responseMode: "message-tool",
|
|
60548
60668
|
additionalMessageMode: "steer",
|
|
60669
|
+
verbose: "minimal",
|
|
60549
60670
|
followUp: {
|
|
60550
60671
|
mode: "auto",
|
|
60551
60672
|
participationTtlMin: 5
|
|
@@ -60561,11 +60682,7 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60561
60682
|
allowFrom: [],
|
|
60562
60683
|
requireMention: false,
|
|
60563
60684
|
allowBots: false,
|
|
60564
|
-
agentId: "default"
|
|
60565
|
-
privilegeCommands: {
|
|
60566
|
-
enabled: false,
|
|
60567
|
-
allowUsers: []
|
|
60568
|
-
}
|
|
60685
|
+
agentId: "default"
|
|
60569
60686
|
}
|
|
60570
60687
|
}
|
|
60571
60688
|
}
|
|
@@ -61494,22 +61611,103 @@ class RuntimeHealthStore {
|
|
|
61494
61611
|
// src/control/runtime-process.ts
|
|
61495
61612
|
import { execFileSync, spawn as spawn2 } from "node:child_process";
|
|
61496
61613
|
import { closeSync, existsSync as existsSync6, openSync, readFileSync as readFileSync3, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
61497
|
-
import { dirname as
|
|
61614
|
+
import { dirname as dirname10 } from "node:path";
|
|
61498
61615
|
import { kill } from "node:process";
|
|
61499
61616
|
|
|
61500
61617
|
// src/control/clisbot-wrapper.ts
|
|
61501
61618
|
import { chmod } from "node:fs/promises";
|
|
61502
61619
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
61503
|
-
import { dirname as
|
|
61620
|
+
import { dirname as dirname8, join as join6, sep } from "node:path";
|
|
61621
|
+
|
|
61622
|
+
// src/control/runner-exit-diagnostics.ts
|
|
61623
|
+
import { unlink } from "node:fs/promises";
|
|
61624
|
+
import { dirname as dirname7, join as join5 } from "node:path";
|
|
61625
|
+
function shellQuote(value) {
|
|
61626
|
+
if (/^[a-zA-Z0-9_./:@=-]+$/.test(value)) {
|
|
61627
|
+
return value;
|
|
61628
|
+
}
|
|
61629
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
61630
|
+
}
|
|
61631
|
+
function buildCommandString(command, args) {
|
|
61632
|
+
return [command, ...args].map(shellQuote).join(" ");
|
|
61633
|
+
}
|
|
61634
|
+
function sanitizeSessionName2(sessionName) {
|
|
61635
|
+
return sessionName.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
61636
|
+
}
|
|
61637
|
+
function getRunnerExitRecordPath(stateDir, sessionName) {
|
|
61638
|
+
return join5(stateDir, "runner-exits", `${sanitizeSessionName2(sessionName)}.json`);
|
|
61639
|
+
}
|
|
61640
|
+
function buildRunnerLaunchCommand(params) {
|
|
61641
|
+
const runnerCommand = buildCommandString(params.command, params.args);
|
|
61642
|
+
const exitRecordPath = getRunnerExitRecordPath(params.stateDir, params.sessionName);
|
|
61643
|
+
const exitWriterScript = [
|
|
61644
|
+
"const fs = require('fs');",
|
|
61645
|
+
"const path = require('path');",
|
|
61646
|
+
"const filePath = process.argv[1];",
|
|
61647
|
+
"const sessionName = process.argv[2];",
|
|
61648
|
+
"const exitCode = Number(process.argv[3]);",
|
|
61649
|
+
"const command = process.argv[4];",
|
|
61650
|
+
"fs.mkdirSync(path.dirname(filePath), { recursive: true });",
|
|
61651
|
+
"fs.writeFileSync(filePath, JSON.stringify({ sessionName, exitCode, command, exitedAt: new Date().toISOString() }) + '\\n');"
|
|
61652
|
+
].join(" ");
|
|
61653
|
+
const exports = [
|
|
61654
|
+
`export PATH=${shellQuote(params.wrapperDir)}:"$PATH"`,
|
|
61655
|
+
`export CLISBOT_BIN=${shellQuote(params.wrapperPath)}`
|
|
61656
|
+
];
|
|
61657
|
+
return [
|
|
61658
|
+
...exports,
|
|
61659
|
+
`rm -f ${shellQuote(exitRecordPath)}`,
|
|
61660
|
+
runnerCommand,
|
|
61661
|
+
"status=$?",
|
|
61662
|
+
`node -e ${shellQuote(exitWriterScript)} ${shellQuote(exitRecordPath)} ${shellQuote(params.sessionName)} "$status" ${shellQuote(runnerCommand)} || true`,
|
|
61663
|
+
'exit "$status"'
|
|
61664
|
+
].join("; ");
|
|
61665
|
+
}
|
|
61666
|
+
async function clearRunnerExitRecord(stateDir, sessionName) {
|
|
61667
|
+
const exitRecordPath = getRunnerExitRecordPath(stateDir, sessionName);
|
|
61668
|
+
if (!await fileExists(exitRecordPath)) {
|
|
61669
|
+
return;
|
|
61670
|
+
}
|
|
61671
|
+
try {
|
|
61672
|
+
await unlink(exitRecordPath);
|
|
61673
|
+
} catch {}
|
|
61674
|
+
}
|
|
61675
|
+
async function readRunnerExitRecord(stateDir, sessionName) {
|
|
61676
|
+
const exitRecordPath = getRunnerExitRecordPath(stateDir, sessionName);
|
|
61677
|
+
if (!await fileExists(exitRecordPath)) {
|
|
61678
|
+
return null;
|
|
61679
|
+
}
|
|
61680
|
+
try {
|
|
61681
|
+
const text = await readTextFile(exitRecordPath);
|
|
61682
|
+
const parsed = JSON.parse(text);
|
|
61683
|
+
const exitCode = parsed.exitCode;
|
|
61684
|
+
if (typeof parsed.sessionName !== "string" || typeof exitCode !== "number" || !Number.isFinite(exitCode) || typeof parsed.command !== "string" || typeof parsed.exitedAt !== "string") {
|
|
61685
|
+
return null;
|
|
61686
|
+
}
|
|
61687
|
+
return {
|
|
61688
|
+
sessionName: parsed.sessionName,
|
|
61689
|
+
exitCode,
|
|
61690
|
+
command: parsed.command,
|
|
61691
|
+
exitedAt: parsed.exitedAt
|
|
61692
|
+
};
|
|
61693
|
+
} catch {
|
|
61694
|
+
return null;
|
|
61695
|
+
}
|
|
61696
|
+
}
|
|
61697
|
+
async function ensureRunnerExitRecordDir(stateDir, sessionName) {
|
|
61698
|
+
await ensureDir(dirname7(getRunnerExitRecordPath(stateDir, sessionName)));
|
|
61699
|
+
}
|
|
61700
|
+
|
|
61701
|
+
// src/control/clisbot-wrapper.ts
|
|
61504
61702
|
function getDefaultClisbotBinDir(env = process.env) {
|
|
61505
|
-
return
|
|
61703
|
+
return join6(resolveAppHomeDir(env), "bin");
|
|
61506
61704
|
}
|
|
61507
61705
|
function getDefaultClisbotWrapperPath(env = process.env) {
|
|
61508
|
-
return
|
|
61706
|
+
return join6(getDefaultClisbotBinDir(env), "clisbot");
|
|
61509
61707
|
}
|
|
61510
61708
|
var DEFAULT_CLISBOT_BIN_DIR = getDefaultClisbotBinDir();
|
|
61511
61709
|
var DEFAULT_CLISBOT_WRAPPER_PATH = getDefaultClisbotWrapperPath();
|
|
61512
|
-
function
|
|
61710
|
+
function shellQuote2(value) {
|
|
61513
61711
|
if (/^[a-zA-Z0-9_./:@=-]+$/.test(value)) {
|
|
61514
61712
|
return value;
|
|
61515
61713
|
}
|
|
@@ -61532,7 +61730,7 @@ function getClisbotPromptCommand() {
|
|
|
61532
61730
|
return isPackagedRuntime() ? "clis" : getClisbotWrapperPath();
|
|
61533
61731
|
}
|
|
61534
61732
|
function getClisbotWrapperDir() {
|
|
61535
|
-
return
|
|
61733
|
+
return dirname8(getClisbotWrapperPath());
|
|
61536
61734
|
}
|
|
61537
61735
|
function renderClisbotWrapperScript() {
|
|
61538
61736
|
const execPath = process.execPath;
|
|
@@ -61540,14 +61738,14 @@ function renderClisbotWrapperScript() {
|
|
|
61540
61738
|
return [
|
|
61541
61739
|
"#!/usr/bin/env bash",
|
|
61542
61740
|
"set -euo pipefail",
|
|
61543
|
-
`exec ${
|
|
61741
|
+
`exec ${shellQuote2(execPath)} ${shellQuote2(mainScriptPath)} "$@"`,
|
|
61544
61742
|
""
|
|
61545
61743
|
].join(`
|
|
61546
61744
|
`);
|
|
61547
61745
|
}
|
|
61548
61746
|
async function ensureClisbotWrapper() {
|
|
61549
61747
|
const wrapperPath = getClisbotWrapperPath();
|
|
61550
|
-
const wrapperDir =
|
|
61748
|
+
const wrapperDir = dirname8(wrapperPath);
|
|
61551
61749
|
await ensureDir2(wrapperDir);
|
|
61552
61750
|
const nextScript = renderClisbotWrapperScript();
|
|
61553
61751
|
const existing = await fileExists(wrapperPath) ? await readTextFile(wrapperPath) : null;
|
|
@@ -61564,7 +61762,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
|
|
|
61564
61762
|
// src/shared/process.ts
|
|
61565
61763
|
import { spawn } from "node:child_process";
|
|
61566
61764
|
import { existsSync as existsSync5 } from "node:fs";
|
|
61567
|
-
import { delimiter, join as
|
|
61765
|
+
import { delimiter, join as join7 } from "node:path";
|
|
61568
61766
|
function sleep(ms) {
|
|
61569
61767
|
return new Promise((resolve) => {
|
|
61570
61768
|
setTimeout(resolve, ms);
|
|
@@ -61582,7 +61780,7 @@ function commandExists(command) {
|
|
|
61582
61780
|
const executableNames = getExecutableNames(command);
|
|
61583
61781
|
for (const directory of pathEntries) {
|
|
61584
61782
|
for (const executableName of executableNames) {
|
|
61585
|
-
if (existsSync5(
|
|
61783
|
+
if (existsSync5(join7(directory, executableName))) {
|
|
61586
61784
|
return true;
|
|
61587
61785
|
}
|
|
61588
61786
|
}
|
|
@@ -61906,7 +62104,7 @@ function getProcessLiveness(pid, dependencies = {}) {
|
|
|
61906
62104
|
async function ensureConfigFile(configPath, options = {}) {
|
|
61907
62105
|
await ensureClisbotWrapper();
|
|
61908
62106
|
const expandedConfigPath = resolveConfigPath(configPath);
|
|
61909
|
-
await ensureDir2(
|
|
62107
|
+
await ensureDir2(dirname10(expandedConfigPath));
|
|
61910
62108
|
if (existsSync6(expandedConfigPath)) {
|
|
61911
62109
|
return {
|
|
61912
62110
|
configPath: expandedConfigPath,
|
|
@@ -61943,8 +62141,8 @@ async function startDetachedRuntime(params) {
|
|
|
61943
62141
|
rmSync2(pidPath, { force: true });
|
|
61944
62142
|
}
|
|
61945
62143
|
const configResult = await ensureConfigFile(params.configPath);
|
|
61946
|
-
await ensureDir2(
|
|
61947
|
-
await ensureDir2(
|
|
62144
|
+
await ensureDir2(dirname10(pidPath));
|
|
62145
|
+
await ensureDir2(dirname10(logPath));
|
|
61948
62146
|
const logStartOffset = getLogSize(logPath);
|
|
61949
62147
|
const logFd = openSync(logPath, "a");
|
|
61950
62148
|
const child = spawn2(process.execPath, [params.scriptPath, "serve-foreground"], {
|
|
@@ -62034,7 +62232,7 @@ async function disableExpiredMemAccountsInConfig(configPath) {
|
|
|
62034
62232
|
}
|
|
62035
62233
|
async function writeRuntimePid(pidPath, pid = process.pid) {
|
|
62036
62234
|
const expandedPidPath = resolvePidPath(pidPath);
|
|
62037
|
-
await ensureDir2(
|
|
62235
|
+
await ensureDir2(dirname10(expandedPidPath));
|
|
62038
62236
|
await writeTextFile(expandedPidPath, `${pid}
|
|
62039
62237
|
`);
|
|
62040
62238
|
}
|
|
@@ -62655,7 +62853,13 @@ function buildConfiguredTargetFromIdentity(identity) {
|
|
|
62655
62853
|
};
|
|
62656
62854
|
}
|
|
62657
62855
|
function renderFieldLabel(field) {
|
|
62658
|
-
|
|
62856
|
+
if (field === "responseMode") {
|
|
62857
|
+
return "response-mode";
|
|
62858
|
+
}
|
|
62859
|
+
if (field === "additionalMessageMode") {
|
|
62860
|
+
return "additional-message-mode";
|
|
62861
|
+
}
|
|
62862
|
+
return "streaming";
|
|
62659
62863
|
}
|
|
62660
62864
|
|
|
62661
62865
|
// src/channels/additional-message-mode-config.ts
|
|
@@ -62747,217 +62951,21 @@ async function setConfiguredResponseMode(params) {
|
|
|
62747
62951
|
}
|
|
62748
62952
|
|
|
62749
62953
|
// src/control/channel-privilege-cli.ts
|
|
62750
|
-
function
|
|
62751
|
-
return process.env.CLISBOT_CONFIG_PATH;
|
|
62752
|
-
}
|
|
62753
|
-
function parseTarget(raw) {
|
|
62754
|
-
if (raw === "slack-dm" || raw === "slack-channel" || raw === "slack-group" || raw === "telegram-dm" || raw === "telegram-group") {
|
|
62755
|
-
return raw;
|
|
62756
|
-
}
|
|
62757
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62758
|
-
}
|
|
62759
|
-
function parseOptionValue2(args, name) {
|
|
62760
|
-
const index = args.findIndex((arg) => arg === name);
|
|
62761
|
-
if (index === -1) {
|
|
62762
|
-
return;
|
|
62763
|
-
}
|
|
62764
|
-
const value = args[index + 1]?.trim();
|
|
62765
|
-
if (!value) {
|
|
62766
|
-
throw new Error(`Missing value for ${name}`);
|
|
62767
|
-
}
|
|
62768
|
-
return value;
|
|
62769
|
-
}
|
|
62770
|
-
function ensureAllowUsersList(value) {
|
|
62771
|
-
return {
|
|
62772
|
-
enabled: value?.enabled ?? false,
|
|
62773
|
-
allowUsers: value?.allowUsers ?? []
|
|
62774
|
-
};
|
|
62775
|
-
}
|
|
62776
|
-
function renderPrivilegeCliHelp() {
|
|
62954
|
+
function renderChannelPrivilegeCliRemovedMessage() {
|
|
62777
62955
|
return [
|
|
62778
|
-
"
|
|
62779
|
-
"
|
|
62780
|
-
"
|
|
62781
|
-
" clisbot channels privilege allow-user slack-dm <userId>",
|
|
62782
|
-
" clisbot channels privilege remove-user slack-dm <userId>",
|
|
62783
|
-
" clisbot channels privilege enable slack-channel <channelId>",
|
|
62784
|
-
" clisbot channels privilege allow-user slack-channel <channelId> <userId>",
|
|
62785
|
-
" clisbot channels privilege enable slack-group <groupId>",
|
|
62786
|
-
" clisbot channels privilege allow-user slack-group <groupId> <userId>",
|
|
62787
|
-
" clisbot channels privilege enable telegram-dm",
|
|
62788
|
-
" clisbot channels privilege allow-user telegram-dm <userId>",
|
|
62789
|
-
" clisbot channels privilege enable telegram-group <chatId> [--topic <topicId>]",
|
|
62790
|
-
" clisbot channels privilege allow-user telegram-group <chatId> <userId> [--topic <topicId>]"
|
|
62956
|
+
"`clisbot channels privilege` has been removed.",
|
|
62957
|
+
"Manage routed permissions through `app.auth` and `agents.<id>.auth` instead.",
|
|
62958
|
+
"Grant `shellExecute` on the target agent role when `/bash` should be allowed."
|
|
62791
62959
|
].join(`
|
|
62792
62960
|
`);
|
|
62793
62961
|
}
|
|
62794
|
-
function
|
|
62795
|
-
|
|
62796
|
-
return normalized && !users.includes(normalized) ? [...users, normalized] : users;
|
|
62797
|
-
}
|
|
62798
|
-
function removeUser(users, userId) {
|
|
62799
|
-
const normalized = userId.trim();
|
|
62800
|
-
return users.filter((value) => value !== normalized);
|
|
62801
|
-
}
|
|
62802
|
-
async function runChannelPrivilegeCli(args) {
|
|
62803
|
-
const action = args[0];
|
|
62804
|
-
const target = parseTarget(args[1]);
|
|
62805
|
-
const rest = args.slice(2);
|
|
62806
|
-
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
62807
|
-
if (target === "slack-dm") {
|
|
62808
|
-
const current2 = ensureAllowUsersList(config.channels.slack.directMessages.privilegeCommands);
|
|
62809
|
-
await applyPrivilegeAction({
|
|
62810
|
-
action,
|
|
62811
|
-
current: current2,
|
|
62812
|
-
args: rest,
|
|
62813
|
-
set: (next) => {
|
|
62814
|
-
config.channels.slack.directMessages.privilegeCommands = next;
|
|
62815
|
-
},
|
|
62816
|
-
configPath,
|
|
62817
|
-
label: "slack direct messages",
|
|
62818
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62819
|
-
});
|
|
62820
|
-
return;
|
|
62821
|
-
}
|
|
62822
|
-
if (target === "telegram-dm") {
|
|
62823
|
-
const current2 = ensureAllowUsersList(config.channels.telegram.directMessages.privilegeCommands);
|
|
62824
|
-
await applyPrivilegeAction({
|
|
62825
|
-
action,
|
|
62826
|
-
current: current2,
|
|
62827
|
-
args: rest,
|
|
62828
|
-
set: (next) => {
|
|
62829
|
-
config.channels.telegram.directMessages.privilegeCommands = next;
|
|
62830
|
-
},
|
|
62831
|
-
configPath,
|
|
62832
|
-
label: "telegram direct messages",
|
|
62833
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62834
|
-
});
|
|
62835
|
-
return;
|
|
62836
|
-
}
|
|
62837
|
-
if (target === "slack-channel" || target === "slack-group") {
|
|
62838
|
-
const routeId = rest[0]?.trim();
|
|
62839
|
-
if (!routeId) {
|
|
62840
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62841
|
-
}
|
|
62842
|
-
const routes = target === "slack-channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
62843
|
-
const route = routes[routeId];
|
|
62844
|
-
if (!route) {
|
|
62845
|
-
throw new Error(`Route not configured yet: ${target} ${routeId}. Add the route first with \`clisbot channels add ...\`.`);
|
|
62846
|
-
}
|
|
62847
|
-
const current2 = ensureAllowUsersList(route.privilegeCommands);
|
|
62848
|
-
await applyPrivilegeAction({
|
|
62849
|
-
action,
|
|
62850
|
-
current: current2,
|
|
62851
|
-
args: rest.slice(1),
|
|
62852
|
-
set: (next) => {
|
|
62853
|
-
route.privilegeCommands = next;
|
|
62854
|
-
},
|
|
62855
|
-
configPath,
|
|
62856
|
-
label: `${target} ${routeId}`,
|
|
62857
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62858
|
-
});
|
|
62859
|
-
return;
|
|
62860
|
-
}
|
|
62861
|
-
const chatId = rest[0]?.trim();
|
|
62862
|
-
if (!chatId) {
|
|
62863
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62864
|
-
}
|
|
62865
|
-
const topicId = parseOptionValue2(rest, "--topic");
|
|
62866
|
-
const group = config.channels.telegram.groups[chatId];
|
|
62867
|
-
if (!group) {
|
|
62868
|
-
throw new Error(renderTelegramRouteChoiceMessage({ chatId }));
|
|
62869
|
-
}
|
|
62870
|
-
if (topicId) {
|
|
62871
|
-
const topic = group.topics?.[topicId];
|
|
62872
|
-
if (!topic) {
|
|
62873
|
-
throw new Error(renderTelegramRouteChoiceMessage({ chatId, topicId }));
|
|
62874
|
-
}
|
|
62875
|
-
const current2 = ensureAllowUsersList(topic.privilegeCommands);
|
|
62876
|
-
await applyPrivilegeAction({
|
|
62877
|
-
action,
|
|
62878
|
-
current: current2,
|
|
62879
|
-
args: rest.filter((value, index) => {
|
|
62880
|
-
if (index === 0) {
|
|
62881
|
-
return false;
|
|
62882
|
-
}
|
|
62883
|
-
return value !== "--topic" && value !== topicId;
|
|
62884
|
-
}),
|
|
62885
|
-
set: (next) => {
|
|
62886
|
-
topic.privilegeCommands = next;
|
|
62887
|
-
},
|
|
62888
|
-
configPath,
|
|
62889
|
-
label: `telegram topic ${chatId}/${topicId}`,
|
|
62890
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62891
|
-
});
|
|
62892
|
-
return;
|
|
62893
|
-
}
|
|
62894
|
-
const current = ensureAllowUsersList(group.privilegeCommands);
|
|
62895
|
-
await applyPrivilegeAction({
|
|
62896
|
-
action,
|
|
62897
|
-
current,
|
|
62898
|
-
args: rest.slice(1),
|
|
62899
|
-
set: (next) => {
|
|
62900
|
-
group.privilegeCommands = next;
|
|
62901
|
-
},
|
|
62902
|
-
configPath,
|
|
62903
|
-
label: `telegram group ${chatId}`,
|
|
62904
|
-
save: async () => writeEditableConfig(configPath, config)
|
|
62905
|
-
});
|
|
62906
|
-
}
|
|
62907
|
-
async function applyPrivilegeAction(params) {
|
|
62908
|
-
if (params.action === "enable") {
|
|
62909
|
-
params.set({
|
|
62910
|
-
enabled: true,
|
|
62911
|
-
allowUsers: params.current.allowUsers
|
|
62912
|
-
});
|
|
62913
|
-
await params.save();
|
|
62914
|
-
console.log(`enabled privilege commands for ${params.label}`);
|
|
62915
|
-
console.log(`config: ${params.configPath}`);
|
|
62916
|
-
return;
|
|
62917
|
-
}
|
|
62918
|
-
if (params.action === "disable") {
|
|
62919
|
-
params.set({
|
|
62920
|
-
enabled: false,
|
|
62921
|
-
allowUsers: params.current.allowUsers
|
|
62922
|
-
});
|
|
62923
|
-
await params.save();
|
|
62924
|
-
console.log(`disabled privilege commands for ${params.label}`);
|
|
62925
|
-
console.log(`config: ${params.configPath}`);
|
|
62926
|
-
return;
|
|
62927
|
-
}
|
|
62928
|
-
if (params.action === "allow-user") {
|
|
62929
|
-
const userId = params.args[0]?.trim();
|
|
62930
|
-
if (!userId) {
|
|
62931
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62932
|
-
}
|
|
62933
|
-
params.set({
|
|
62934
|
-
enabled: params.current.enabled,
|
|
62935
|
-
allowUsers: addUniqueUser(params.current.allowUsers, userId)
|
|
62936
|
-
});
|
|
62937
|
-
await params.save();
|
|
62938
|
-
console.log(`allowed ${userId} to use privilege commands for ${params.label}`);
|
|
62939
|
-
console.log(`config: ${params.configPath}`);
|
|
62940
|
-
return;
|
|
62941
|
-
}
|
|
62942
|
-
if (params.action === "remove-user") {
|
|
62943
|
-
const userId = params.args[0]?.trim();
|
|
62944
|
-
if (!userId) {
|
|
62945
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62946
|
-
}
|
|
62947
|
-
params.set({
|
|
62948
|
-
enabled: params.current.enabled,
|
|
62949
|
-
allowUsers: removeUser(params.current.allowUsers, userId)
|
|
62950
|
-
});
|
|
62951
|
-
await params.save();
|
|
62952
|
-
console.log(`removed ${userId} from privilege commands for ${params.label}`);
|
|
62953
|
-
console.log(`config: ${params.configPath}`);
|
|
62954
|
-
return;
|
|
62955
|
-
}
|
|
62956
|
-
throw new Error(renderPrivilegeCliHelp());
|
|
62962
|
+
async function runChannelPrivilegeCli(_args) {
|
|
62963
|
+
throw new Error(renderChannelPrivilegeCliRemovedMessage());
|
|
62957
62964
|
}
|
|
62958
62965
|
|
|
62959
62966
|
// src/control/channels-cli.ts
|
|
62960
|
-
|
|
62967
|
+
var AUTH_USER_GUIDE_DOC_PATH = "docs/user-guide/auth-and-roles.md";
|
|
62968
|
+
function getEditableConfigPath5() {
|
|
62961
62969
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
62962
62970
|
}
|
|
62963
62971
|
function renderChannelsHelp() {
|
|
@@ -62975,7 +62983,6 @@ function renderChannelsHelp() {
|
|
|
62975
62983
|
" clisbot channels remove slack-channel <channelId>",
|
|
62976
62984
|
" clisbot channels add slack-group <groupId> [--agent <id>] [--require-mention true|false]",
|
|
62977
62985
|
" clisbot channels remove slack-group <groupId>",
|
|
62978
|
-
" clisbot channels privilege <enable|disable|allow-user|remove-user> <target> ...",
|
|
62979
62986
|
" clisbot channels response-mode status --channel <slack|telegram> [--target <target>] [--topic <topicId>]",
|
|
62980
62987
|
" clisbot channels response-mode set <capture-pane|message-tool> --channel <slack|telegram> [--target <target>] [--topic <topicId>]",
|
|
62981
62988
|
" clisbot channels additional-message-mode status --channel <slack|telegram> [--target <target>] [--topic <topicId>]",
|
|
@@ -62991,7 +62998,8 @@ function renderChannelsHelp() {
|
|
|
62991
62998
|
" - Telegram groups need channels.telegram.groups.<chatId>",
|
|
62992
62999
|
" - Telegram forum topics need channels.telegram.groups.<chatId>.topics.<topicId>",
|
|
62993
63000
|
" - Adding a route puts that surface on the allowlist; other channels, groups, or topics still need to be added explicitly",
|
|
62994
|
-
" - Tune route settings such as requireMention
|
|
63001
|
+
" - Tune route settings such as requireMention and followUp in clisbot.json when a surface should behave differently",
|
|
63002
|
+
` - Manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`,
|
|
62995
63003
|
" - Response delivery can be tuned with responseMode: `capture-pane` or `message-tool`",
|
|
62996
63004
|
" - Busy-session follow-up can be tuned with additionalMessageMode: `steer` or `queue`",
|
|
62997
63005
|
" - Slack response-mode targets use `channel:<id>`, `group:<id>`, or `dm:<id>`",
|
|
@@ -63024,7 +63032,6 @@ function renderChannelsHelp() {
|
|
|
63024
63032
|
"Next steps:",
|
|
63025
63033
|
" - Run `clisbot status` to inspect routes and current channel state",
|
|
63026
63034
|
" - Run `clisbot logs` if the bot is still not responding",
|
|
63027
|
-
...renderGenericPrivilegeCommandHelpLines(),
|
|
63028
63035
|
...renderChannelSetupHelpLines("", { includePrivilegeHelp: false })
|
|
63029
63036
|
].join(`
|
|
63030
63037
|
`);
|
|
@@ -63079,7 +63086,7 @@ function parseResponseModeTarget(channel, raw) {
|
|
|
63079
63086
|
}
|
|
63080
63087
|
return target;
|
|
63081
63088
|
}
|
|
63082
|
-
function
|
|
63089
|
+
function parseOptionValue2(args, name) {
|
|
63083
63090
|
const index = args.findIndex((arg) => arg === name);
|
|
63084
63091
|
if (index === -1) {
|
|
63085
63092
|
return;
|
|
@@ -63091,7 +63098,7 @@ function parseOptionValue3(args, name) {
|
|
|
63091
63098
|
return value;
|
|
63092
63099
|
}
|
|
63093
63100
|
function parseBooleanOption(args, name, fallback) {
|
|
63094
|
-
const raw =
|
|
63101
|
+
const raw = parseOptionValue2(args, name);
|
|
63095
63102
|
if (!raw) {
|
|
63096
63103
|
return fallback;
|
|
63097
63104
|
}
|
|
@@ -63104,10 +63111,10 @@ function parseBooleanOption(args, name, fallback) {
|
|
|
63104
63111
|
throw new Error(`${name} requires true or false`);
|
|
63105
63112
|
}
|
|
63106
63113
|
function getAgentId(args) {
|
|
63107
|
-
return
|
|
63114
|
+
return parseOptionValue2(args, "--agent") ?? "default";
|
|
63108
63115
|
}
|
|
63109
63116
|
async function setChannelEnabled(action, channel) {
|
|
63110
|
-
const { config, configPath } = await readEditableConfig(
|
|
63117
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63111
63118
|
const enabled = action === "enable";
|
|
63112
63119
|
const current = config.channels[channel].enabled;
|
|
63113
63120
|
if (current === enabled) {
|
|
@@ -63127,8 +63134,8 @@ async function addTelegramGroup(args) {
|
|
|
63127
63134
|
if (!chatId) {
|
|
63128
63135
|
throw new Error("Usage: clisbot channels add telegram-group <chatId> [--topic <topicId>] [--agent <id>] [--require-mention true|false]");
|
|
63129
63136
|
}
|
|
63130
|
-
const { config, configPath } = await readEditableConfig(
|
|
63131
|
-
const topicId =
|
|
63137
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63138
|
+
const topicId = parseOptionValue2(args, "--topic");
|
|
63132
63139
|
const agentId = getAgentId(args);
|
|
63133
63140
|
const requireMention = parseBooleanOption(args, "--require-mention", true);
|
|
63134
63141
|
const groupRoute = config.channels.telegram.groups[chatId] ?? {
|
|
@@ -63180,8 +63187,8 @@ async function removeTelegramGroup(args) {
|
|
|
63180
63187
|
if (!chatId) {
|
|
63181
63188
|
throw new Error("Usage: clisbot channels remove telegram-group <chatId> [--topic <topicId>]");
|
|
63182
63189
|
}
|
|
63183
|
-
const { config, configPath } = await readEditableConfig(
|
|
63184
|
-
const topicId =
|
|
63190
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63191
|
+
const topicId = parseOptionValue2(args, "--topic");
|
|
63185
63192
|
const groupRoute = config.channels.telegram.groups[chatId];
|
|
63186
63193
|
if (!groupRoute) {
|
|
63187
63194
|
console.log(`telegram group route ${chatId} is not configured`);
|
|
@@ -63210,7 +63217,7 @@ async function addSlackRoute(kind, args) {
|
|
|
63210
63217
|
if (!routeId) {
|
|
63211
63218
|
throw new Error(`Usage: clisbot channels add slack-${kind} <${kind}Id> [--agent <id>] [--require-mention true|false]`);
|
|
63212
63219
|
}
|
|
63213
|
-
const { config, configPath } = await readEditableConfig(
|
|
63220
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63214
63221
|
const agentId = getAgentId(args);
|
|
63215
63222
|
const requireMention = parseBooleanOption(args, "--require-mention", false);
|
|
63216
63223
|
const target = kind === "channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
@@ -63234,7 +63241,7 @@ async function removeSlackRoute(kind, args) {
|
|
|
63234
63241
|
if (!routeId) {
|
|
63235
63242
|
throw new Error(`Usage: clisbot channels remove slack-${kind} <${kind}Id>`);
|
|
63236
63243
|
}
|
|
63237
|
-
const { config, configPath } = await readEditableConfig(
|
|
63244
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63238
63245
|
const target = kind === "channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
63239
63246
|
if (!target[routeId]) {
|
|
63240
63247
|
console.log(`slack ${kind} route ${routeId} is not configured`);
|
|
@@ -63247,7 +63254,7 @@ async function removeSlackRoute(kind, args) {
|
|
|
63247
63254
|
console.log(`config: ${configPath}`);
|
|
63248
63255
|
}
|
|
63249
63256
|
async function setToken(target, value) {
|
|
63250
|
-
const { config, configPath } = await readEditableConfig(
|
|
63257
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63251
63258
|
if (target === "slack-app") {
|
|
63252
63259
|
config.channels.slack.appToken = value;
|
|
63253
63260
|
const defaultAccountId = config.channels.slack.defaultAccount || "default";
|
|
@@ -63279,7 +63286,7 @@ async function setToken(target, value) {
|
|
|
63279
63286
|
console.log(`config: ${configPath}`);
|
|
63280
63287
|
}
|
|
63281
63288
|
async function clearToken(target) {
|
|
63282
|
-
const { config, configPath } = await readEditableConfig(
|
|
63289
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
63283
63290
|
if (target === "slack-app") {
|
|
63284
63291
|
config.channels.slack.appToken = "";
|
|
63285
63292
|
const defaultAccountId = config.channels.slack.defaultAccount || "default";
|
|
@@ -63323,9 +63330,9 @@ async function runResponseModeCli(args) {
|
|
|
63323
63330
|
}
|
|
63324
63331
|
const responseMode = action === "set" ? parseResponseMode2(args[1]) : undefined;
|
|
63325
63332
|
const optionArgs = action === "set" ? args.slice(2) : args.slice(1);
|
|
63326
|
-
const channel = parseResponseModeChannel(
|
|
63327
|
-
const target = parseResponseModeTarget(channel,
|
|
63328
|
-
const topic =
|
|
63333
|
+
const channel = parseResponseModeChannel(parseOptionValue2(optionArgs, "--channel"));
|
|
63334
|
+
const target = parseResponseModeTarget(channel, parseOptionValue2(optionArgs, "--target"));
|
|
63335
|
+
const topic = parseOptionValue2(optionArgs, "--topic");
|
|
63329
63336
|
if (channel === "slack" && topic) {
|
|
63330
63337
|
throw new Error("Slack response-mode commands do not support --topic");
|
|
63331
63338
|
}
|
|
@@ -63357,9 +63364,9 @@ async function runAdditionalMessageModeCli(args) {
|
|
|
63357
63364
|
}
|
|
63358
63365
|
const additionalMessageMode = action === "set" ? parseAdditionalMessageMode2(args[1]) : undefined;
|
|
63359
63366
|
const optionArgs = action === "set" ? args.slice(2) : args.slice(1);
|
|
63360
|
-
const channel = parseResponseModeChannel(
|
|
63361
|
-
const target = parseResponseModeTarget(channel,
|
|
63362
|
-
const topic =
|
|
63367
|
+
const channel = parseResponseModeChannel(parseOptionValue2(optionArgs, "--channel"));
|
|
63368
|
+
const target = parseResponseModeTarget(channel, parseOptionValue2(optionArgs, "--target"));
|
|
63369
|
+
const topic = parseOptionValue2(optionArgs, "--topic");
|
|
63363
63370
|
if (channel === "slack" && topic) {
|
|
63364
63371
|
throw new Error("Slack additional-message-mode commands do not support --topic");
|
|
63365
63372
|
}
|
|
@@ -63422,16 +63429,10 @@ function renderRouteAddGuidance(params) {
|
|
|
63422
63429
|
console.log("Slack route next steps:");
|
|
63423
63430
|
console.log(` - route added: ${routePath}`);
|
|
63424
63431
|
console.log(" - direct messages still follow channels.slack.directMessages.policy (`open`, `pairing`, `allowlist`, or `disabled`)");
|
|
63425
|
-
console.log(` - this ${routeLabel} still follows channels.slack.groupPolicy and route settings such as requireMention
|
|
63432
|
+
console.log(` - this ${routeLabel} still follows channels.slack.groupPolicy and route settings such as requireMention and followUp`);
|
|
63426
63433
|
console.log(" - if you want pairing-style access control for DMs, set channels.slack.directMessages.policy to `pairing`");
|
|
63427
63434
|
console.log(" - if you want stricter route access, keep Slack groups on allowlist and only add the channels/groups you trust");
|
|
63428
|
-
|
|
63429
|
-
platform: "slack",
|
|
63430
|
-
conversationKind: params.kind === "group" ? "group" : "channel",
|
|
63431
|
-
channelId: params.routeId
|
|
63432
|
-
}, " ")) {
|
|
63433
|
-
console.log(line);
|
|
63434
|
-
}
|
|
63435
|
+
console.log(` - manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`);
|
|
63435
63436
|
} else {
|
|
63436
63437
|
const [chatId, topicId] = params.routeId.split("/");
|
|
63437
63438
|
const routePath = params.kind === "topic" ? `channels.telegram.groups."${chatId}".topics."${topicId}"` : `channels.telegram.groups."${params.routeId}"`;
|
|
@@ -63440,15 +63441,8 @@ function renderRouteAddGuidance(params) {
|
|
|
63440
63441
|
console.log(" - direct messages still follow channels.telegram.directMessages.policy (`open`, `pairing`, `allowlist`, or `disabled`)");
|
|
63441
63442
|
console.log(` - this ${params.kind} is now on the Telegram allowlist; other groups or topics still need to be added explicitly`);
|
|
63442
63443
|
console.log(" - if you want pairing-style access control for DMs, set channels.telegram.directMessages.policy to `pairing`");
|
|
63443
|
-
console.log(" - tune route settings such as requireMention
|
|
63444
|
-
|
|
63445
|
-
platform: "telegram",
|
|
63446
|
-
conversationKind: params.kind === "topic" ? "topic" : "group",
|
|
63447
|
-
chatId: chatId ?? params.routeId,
|
|
63448
|
-
topicId
|
|
63449
|
-
}, " ")) {
|
|
63450
|
-
console.log(line);
|
|
63451
|
-
}
|
|
63444
|
+
console.log(" - tune route settings such as requireMention and followUp in clisbot.json if this surface should behave differently");
|
|
63445
|
+
console.log(` - manage routed auth and /bash access in ${AUTH_USER_GUIDE_DOC_PATH}`);
|
|
63452
63446
|
}
|
|
63453
63447
|
console.log("Run `clisbot status` to inspect routes and current channel state.");
|
|
63454
63448
|
console.log("Run `clisbot logs` if the bot is still not responding.");
|
|
@@ -63542,7 +63536,7 @@ function parseBotType(rawValue) {
|
|
|
63542
63536
|
}
|
|
63543
63537
|
throw new Error(`Invalid bot type: ${rawValue}`);
|
|
63544
63538
|
}
|
|
63545
|
-
function
|
|
63539
|
+
function parseOptionValue3(args, name, index) {
|
|
63546
63540
|
const value = args[index + 1]?.trim();
|
|
63547
63541
|
if (!value) {
|
|
63548
63542
|
throw new Error(`Missing value for ${name}`);
|
|
@@ -63597,17 +63591,17 @@ function parseBootstrapFlags(args) {
|
|
|
63597
63591
|
for (let index = 0;index < args.length; index += 1) {
|
|
63598
63592
|
const arg = args[index];
|
|
63599
63593
|
if (arg === "--cli") {
|
|
63600
|
-
cliTool =
|
|
63594
|
+
cliTool = parseOptionValue3(args, arg, index);
|
|
63601
63595
|
index += 1;
|
|
63602
63596
|
continue;
|
|
63603
63597
|
}
|
|
63604
63598
|
if (arg === "--bootstrap") {
|
|
63605
|
-
bootstrap = parseBotType(
|
|
63599
|
+
bootstrap = parseBotType(parseOptionValue3(args, arg, index));
|
|
63606
63600
|
index += 1;
|
|
63607
63601
|
continue;
|
|
63608
63602
|
}
|
|
63609
63603
|
if (arg === "--bot-type") {
|
|
63610
|
-
bootstrap = parseBotType(
|
|
63604
|
+
bootstrap = parseBotType(parseOptionValue3(args, arg, index));
|
|
63611
63605
|
index += 1;
|
|
63612
63606
|
continue;
|
|
63613
63607
|
}
|
|
@@ -63616,7 +63610,7 @@ function parseBootstrapFlags(args) {
|
|
|
63616
63610
|
continue;
|
|
63617
63611
|
}
|
|
63618
63612
|
if (arg === "--slack-account") {
|
|
63619
|
-
const accountId =
|
|
63613
|
+
const accountId = parseOptionValue3(args, arg, index);
|
|
63620
63614
|
ensureUniqueAccount(slackAccounts, accountId, "--slack-account");
|
|
63621
63615
|
currentSlackAccountId = accountId;
|
|
63622
63616
|
getOrCreateSlackAccount(slackAccounts, accountId);
|
|
@@ -63625,7 +63619,7 @@ function parseBootstrapFlags(args) {
|
|
|
63625
63619
|
continue;
|
|
63626
63620
|
}
|
|
63627
63621
|
if (arg === "--telegram-account") {
|
|
63628
|
-
const accountId =
|
|
63622
|
+
const accountId = parseOptionValue3(args, arg, index);
|
|
63629
63623
|
ensureUniqueAccount(telegramAccounts, accountId, "--telegram-account");
|
|
63630
63624
|
currentTelegramAccountId = accountId;
|
|
63631
63625
|
getOrCreateTelegramAccount(telegramAccounts, accountId);
|
|
@@ -63634,7 +63628,7 @@ function parseBootstrapFlags(args) {
|
|
|
63634
63628
|
continue;
|
|
63635
63629
|
}
|
|
63636
63630
|
if (arg === "--slack-app-token") {
|
|
63637
|
-
const token = parseTokenInput(
|
|
63631
|
+
const token = parseTokenInput(parseOptionValue3(args, arg, index));
|
|
63638
63632
|
const account = getOrCreateSlackAccount(slackAccounts, currentSlackAccountId ?? "default");
|
|
63639
63633
|
account.appToken = token;
|
|
63640
63634
|
sawCredentialFlags = true;
|
|
@@ -63643,7 +63637,7 @@ function parseBootstrapFlags(args) {
|
|
|
63643
63637
|
continue;
|
|
63644
63638
|
}
|
|
63645
63639
|
if (arg === "--slack-bot-token") {
|
|
63646
|
-
const token = parseTokenInput(
|
|
63640
|
+
const token = parseTokenInput(parseOptionValue3(args, arg, index));
|
|
63647
63641
|
const account = getOrCreateSlackAccount(slackAccounts, currentSlackAccountId ?? "default");
|
|
63648
63642
|
account.botToken = token;
|
|
63649
63643
|
sawCredentialFlags = true;
|
|
@@ -63652,7 +63646,7 @@ function parseBootstrapFlags(args) {
|
|
|
63652
63646
|
continue;
|
|
63653
63647
|
}
|
|
63654
63648
|
if (arg === "--telegram-bot-token") {
|
|
63655
|
-
const token = parseTokenInput(
|
|
63649
|
+
const token = parseTokenInput(parseOptionValue3(args, arg, index));
|
|
63656
63650
|
const account = getOrCreateTelegramAccount(telegramAccounts, currentTelegramAccountId ?? "default");
|
|
63657
63651
|
account.botToken = token;
|
|
63658
63652
|
sawCredentialFlags = true;
|
|
@@ -63736,6 +63730,8 @@ class AgentSessionState {
|
|
|
63736
63730
|
startedAt: entry?.runtime?.startedAt,
|
|
63737
63731
|
detachedAt: entry?.runtime?.detachedAt,
|
|
63738
63732
|
finalReplyAt: entry?.runtime?.finalReplyAt,
|
|
63733
|
+
lastMessageToolReplyAt: entry?.runtime?.lastMessageToolReplyAt,
|
|
63734
|
+
messageToolFinalReplyAt: entry?.runtime?.messageToolFinalReplyAt,
|
|
63739
63735
|
sessionKey: target.sessionKey,
|
|
63740
63736
|
agentId: target.agentId
|
|
63741
63737
|
};
|
|
@@ -63747,6 +63743,8 @@ class AgentSessionState {
|
|
|
63747
63743
|
startedAt: entry.runtime.startedAt,
|
|
63748
63744
|
detachedAt: entry.runtime.detachedAt,
|
|
63749
63745
|
finalReplyAt: entry.runtime.finalReplyAt,
|
|
63746
|
+
lastMessageToolReplyAt: entry.runtime.lastMessageToolReplyAt,
|
|
63747
|
+
messageToolFinalReplyAt: entry.runtime.messageToolFinalReplyAt,
|
|
63750
63748
|
sessionKey: entry.sessionKey,
|
|
63751
63749
|
agentId: entry.agentId
|
|
63752
63750
|
}));
|
|
@@ -63880,7 +63878,7 @@ class AgentSessionState {
|
|
|
63880
63878
|
}
|
|
63881
63879
|
return this.resetConversationFollowUpMode(resolved);
|
|
63882
63880
|
}
|
|
63883
|
-
async recordConversationReply(resolved, kind = "reply") {
|
|
63881
|
+
async recordConversationReply(resolved, kind = "reply", source = "channel") {
|
|
63884
63882
|
const repliedAt = Date.now();
|
|
63885
63883
|
return this.upsertSessionEntry(resolved, (existing) => ({
|
|
63886
63884
|
sessionId: existing?.sessionId,
|
|
@@ -63889,9 +63887,17 @@ class AgentSessionState {
|
|
|
63889
63887
|
lastBotReplyAt: repliedAt
|
|
63890
63888
|
},
|
|
63891
63889
|
runnerCommand: existing?.runnerCommand ?? resolved.runner.command,
|
|
63892
|
-
runtime:
|
|
63890
|
+
runtime: existing?.runtime && existing.runtime.state !== "idle" ? {
|
|
63893
63891
|
...existing.runtime,
|
|
63894
|
-
|
|
63892
|
+
...kind === "final" ? {
|
|
63893
|
+
finalReplyAt: repliedAt
|
|
63894
|
+
} : {},
|
|
63895
|
+
...source === "message-tool" ? {
|
|
63896
|
+
lastMessageToolReplyAt: repliedAt,
|
|
63897
|
+
...kind === "final" ? {
|
|
63898
|
+
messageToolFinalReplyAt: repliedAt
|
|
63899
|
+
} : {}
|
|
63900
|
+
} : {}
|
|
63895
63901
|
} : existing?.runtime,
|
|
63896
63902
|
intervalLoops: existing?.intervalLoops
|
|
63897
63903
|
}));
|
|
@@ -63918,7 +63924,7 @@ function hasActiveRuntime(entry) {
|
|
|
63918
63924
|
}
|
|
63919
63925
|
|
|
63920
63926
|
// src/agents/session-store.ts
|
|
63921
|
-
import { dirname as
|
|
63927
|
+
import { dirname as dirname11 } from "node:path";
|
|
63922
63928
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
63923
63929
|
import { rename } from "node:fs/promises";
|
|
63924
63930
|
class SessionStore {
|
|
@@ -64011,7 +64017,7 @@ class SessionStore {
|
|
|
64011
64017
|
return parsed;
|
|
64012
64018
|
}
|
|
64013
64019
|
async writeStore(store) {
|
|
64014
|
-
await ensureDir2(
|
|
64020
|
+
await ensureDir2(dirname11(this.storePath));
|
|
64015
64021
|
const tempPath = `${this.storePath}.${process.pid}.${randomUUID3()}.tmp`;
|
|
64016
64022
|
await writeTextFile(tempPath, JSON.stringify(store, null, 2));
|
|
64017
64023
|
await rename(tempPath, this.storePath);
|
|
@@ -64019,7 +64025,7 @@ class SessionStore {
|
|
|
64019
64025
|
}
|
|
64020
64026
|
|
|
64021
64027
|
// src/control/loops-cli.ts
|
|
64022
|
-
function
|
|
64028
|
+
function getEditableConfigPath6() {
|
|
64023
64029
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
64024
64030
|
}
|
|
64025
64031
|
function renderLoopsHelp() {
|
|
@@ -64077,7 +64083,7 @@ function getSessionState(sessionStorePath) {
|
|
|
64077
64083
|
return new AgentSessionState(new SessionStore(sessionStorePath));
|
|
64078
64084
|
}
|
|
64079
64085
|
async function loadLoopControlState() {
|
|
64080
|
-
const configPath = await ensureEditableConfigFile(
|
|
64086
|
+
const configPath = await ensureEditableConfigFile(getEditableConfigPath6());
|
|
64081
64087
|
const loadedConfig = await loadConfigWithoutEnvResolution(configPath);
|
|
64082
64088
|
const sessionStorePath = resolveSessionStorePath(loadedConfig);
|
|
64083
64089
|
return {
|
|
@@ -64146,23 +64152,158 @@ async function runLoopsCli(args) {
|
|
|
64146
64152
|
// src/agents/agent-service.ts
|
|
64147
64153
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
64148
64154
|
|
|
64149
|
-
// src/
|
|
64150
|
-
|
|
64151
|
-
|
|
64152
|
-
|
|
64153
|
-
|
|
64154
|
-
|
|
64155
|
-
|
|
64156
|
-
|
|
64155
|
+
// src/channels/agent-prompt.ts
|
|
64156
|
+
function buildAgentPromptText(params) {
|
|
64157
|
+
if (!params.config.enabled) {
|
|
64158
|
+
return params.text;
|
|
64159
|
+
}
|
|
64160
|
+
const systemBlock = renderAgentPromptInstruction(params);
|
|
64161
|
+
return `<system>
|
|
64162
|
+
${systemBlock}
|
|
64163
|
+
</system>
|
|
64164
|
+
|
|
64165
|
+
<user>
|
|
64166
|
+
${params.text}
|
|
64167
|
+
</user>`;
|
|
64157
64168
|
}
|
|
64158
|
-
function
|
|
64159
|
-
const
|
|
64160
|
-
|
|
64161
|
-
|
|
64169
|
+
function renderAgentPromptInstruction(params) {
|
|
64170
|
+
const messageToolMode = (params.responseMode ?? "message-tool") === "message-tool";
|
|
64171
|
+
const progressAllowed = messageToolMode && (params.streaming ?? "all") !== "off";
|
|
64172
|
+
const lines = [
|
|
64173
|
+
`[${renderPromptTimestamp()}] ${renderIdentitySummary(params.identity)}`,
|
|
64174
|
+
"",
|
|
64175
|
+
"You are operating inside clisbot.",
|
|
64176
|
+
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"
|
|
64177
|
+
];
|
|
64178
|
+
if (messageToolMode) {
|
|
64179
|
+
const replyCommand = buildReplyCommand({
|
|
64180
|
+
command: getClisbotPromptCommand(),
|
|
64181
|
+
identity: params.identity
|
|
64182
|
+
});
|
|
64183
|
+
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 ? [
|
|
64184
|
+
"- keep progress updates short and meaningful",
|
|
64185
|
+
"- do not send progress updates for trivial internal steps"
|
|
64186
|
+
] : []);
|
|
64162
64187
|
}
|
|
64163
|
-
|
|
64188
|
+
if (params.protectedControlMutationRule) {
|
|
64189
|
+
lines.push("", params.protectedControlMutationRule);
|
|
64190
|
+
}
|
|
64191
|
+
return lines.join(`
|
|
64192
|
+
`);
|
|
64164
64193
|
}
|
|
64165
|
-
function
|
|
64194
|
+
function renderPromptTimestamp() {
|
|
64195
|
+
const date = new Date;
|
|
64196
|
+
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
64197
|
+
year: "numeric",
|
|
64198
|
+
month: "2-digit",
|
|
64199
|
+
day: "2-digit",
|
|
64200
|
+
hour: "2-digit",
|
|
64201
|
+
minute: "2-digit",
|
|
64202
|
+
second: "2-digit",
|
|
64203
|
+
hour12: false,
|
|
64204
|
+
timeZoneName: "shortOffset"
|
|
64205
|
+
});
|
|
64206
|
+
return formatter.format(date).replace(",", "");
|
|
64207
|
+
}
|
|
64208
|
+
function renderIdentitySummary(identity) {
|
|
64209
|
+
const segments = [renderConversationSummary(identity)];
|
|
64210
|
+
const sender = renderSenderSummary(identity);
|
|
64211
|
+
if (sender) {
|
|
64212
|
+
segments.push(sender);
|
|
64213
|
+
}
|
|
64214
|
+
return segments.join(" | ");
|
|
64215
|
+
}
|
|
64216
|
+
function renderConversationSummary(identity) {
|
|
64217
|
+
if (identity.platform === "slack") {
|
|
64218
|
+
const scopeLabel = identity.conversationKind === "dm" ? "Slack direct message" : identity.conversationKind === "group" ? "Slack group" : "Slack channel";
|
|
64219
|
+
const segments = [scopeLabel];
|
|
64220
|
+
const channel = renderLabeledTarget(identity.channelName, identity.channelId, "#");
|
|
64221
|
+
if (channel) {
|
|
64222
|
+
segments.push(channel);
|
|
64223
|
+
}
|
|
64224
|
+
if (identity.threadTs) {
|
|
64225
|
+
segments.push(`thread ${identity.threadTs}`);
|
|
64226
|
+
}
|
|
64227
|
+
return segments.join(" ");
|
|
64228
|
+
}
|
|
64229
|
+
if (identity.conversationKind === "dm") {
|
|
64230
|
+
return ["Telegram direct message", renderLabeledTarget(identity.chatName, identity.chatId)].filter(Boolean).join(" ");
|
|
64231
|
+
}
|
|
64232
|
+
if (identity.conversationKind === "topic") {
|
|
64233
|
+
const topic = renderNamedValue("topic", identity.topicName, identity.topicId);
|
|
64234
|
+
const group = renderNamedValue("in group", identity.chatName, identity.chatId);
|
|
64235
|
+
return [topic, group].filter(Boolean).join(" ");
|
|
64236
|
+
}
|
|
64237
|
+
return ["Telegram group", renderLabeledTarget(identity.chatName, identity.chatId)].filter(Boolean).join(" ");
|
|
64238
|
+
}
|
|
64239
|
+
function renderSenderSummary(identity) {
|
|
64240
|
+
const sender = renderLabeledTarget(identity.senderName, identity.senderId);
|
|
64241
|
+
return sender ? `sender ${sender}` : "";
|
|
64242
|
+
}
|
|
64243
|
+
function renderLabeledTarget(name, id, namePrefix = "") {
|
|
64244
|
+
const normalizedName = name?.trim();
|
|
64245
|
+
const normalizedId = id?.trim();
|
|
64246
|
+
if (normalizedName && normalizedId) {
|
|
64247
|
+
return `${namePrefix}${normalizedName} (${normalizedId})`;
|
|
64248
|
+
}
|
|
64249
|
+
if (normalizedName) {
|
|
64250
|
+
return `${namePrefix}${normalizedName}`;
|
|
64251
|
+
}
|
|
64252
|
+
return normalizedId ?? "";
|
|
64253
|
+
}
|
|
64254
|
+
function renderNamedValue(label, name, id) {
|
|
64255
|
+
const value = renderLabeledTarget(name, id);
|
|
64256
|
+
return value ? `${label} ${value}` : "";
|
|
64257
|
+
}
|
|
64258
|
+
function buildReplyCommand(params) {
|
|
64259
|
+
const lines = [`${params.command} message send \\`];
|
|
64260
|
+
if (params.identity.platform === "slack") {
|
|
64261
|
+
lines.push(" --channel slack \\");
|
|
64262
|
+
lines.push(` --target channel:${params.identity.channelId ?? ""} \\`);
|
|
64263
|
+
if (params.identity.threadTs) {
|
|
64264
|
+
lines.push(` --thread-id ${params.identity.threadTs} \\`);
|
|
64265
|
+
}
|
|
64266
|
+
lines.push(" --final \\");
|
|
64267
|
+
lines.push(' --message "$(cat <<\\__CLISBOT_MESSAGE__');
|
|
64268
|
+
lines.push("<short progress update>");
|
|
64269
|
+
lines.push("__CLISBOT_MESSAGE__");
|
|
64270
|
+
lines.push(')" \\');
|
|
64271
|
+
lines.push(" [--media /absolute/path/to/file]");
|
|
64272
|
+
return lines.join(`
|
|
64273
|
+
`);
|
|
64274
|
+
}
|
|
64275
|
+
lines.push(" --channel telegram \\");
|
|
64276
|
+
lines.push(` --target ${params.identity.chatId ?? ""} \\`);
|
|
64277
|
+
if (params.identity.topicId) {
|
|
64278
|
+
lines.push(` --thread-id ${params.identity.topicId} \\`);
|
|
64279
|
+
}
|
|
64280
|
+
lines.push(" --final \\");
|
|
64281
|
+
lines.push(' --message "$(cat <<\\__CLISBOT_MESSAGE__');
|
|
64282
|
+
lines.push("<short progress update>");
|
|
64283
|
+
lines.push("__CLISBOT_MESSAGE__");
|
|
64284
|
+
lines.push(')" \\');
|
|
64285
|
+
lines.push(" [--media /absolute/path/to/file]");
|
|
64286
|
+
return lines.join(`
|
|
64287
|
+
`);
|
|
64288
|
+
}
|
|
64289
|
+
|
|
64290
|
+
// src/agents/session-key.ts
|
|
64291
|
+
var DEFAULT_MAIN_KEY = "main";
|
|
64292
|
+
var DEFAULT_ACCOUNT_ID = "default";
|
|
64293
|
+
function normalizeToken(value) {
|
|
64294
|
+
return (value ?? "").trim().toLowerCase();
|
|
64295
|
+
}
|
|
64296
|
+
function normalizeMainKey(value) {
|
|
64297
|
+
return normalizeToken(value) || DEFAULT_MAIN_KEY;
|
|
64298
|
+
}
|
|
64299
|
+
function normalizeAgentId(value) {
|
|
64300
|
+
const trimmed = (value ?? "").trim();
|
|
64301
|
+
if (!trimmed) {
|
|
64302
|
+
return "default";
|
|
64303
|
+
}
|
|
64304
|
+
return trimmed.toLowerCase().replaceAll(/[^a-z0-9_-]+/g, "-").replaceAll(/-+/g, "-").replaceAll(/^-|-$/g, "") || "default";
|
|
64305
|
+
}
|
|
64306
|
+
function normalizeAccountId2(value) {
|
|
64166
64307
|
return normalizeToken(value) || DEFAULT_ACCOUNT_ID;
|
|
64167
64308
|
}
|
|
64168
64309
|
function buildAgentMainSessionKey(params) {
|
|
@@ -64417,7 +64558,7 @@ class AgentJobQueue {
|
|
|
64417
64558
|
}
|
|
64418
64559
|
|
|
64419
64560
|
// src/agents/runner-session.ts
|
|
64420
|
-
import { dirname as
|
|
64561
|
+
import { dirname as dirname12 } from "node:path";
|
|
64421
64562
|
|
|
64422
64563
|
// src/shared/transcript-normalization.ts
|
|
64423
64564
|
import { stripVTControlCharacters } from "node:util";
|
|
@@ -64981,7 +65122,11 @@ ${body}` : queueNote;
|
|
|
64981
65122
|
_Timed out waiting for more output._` : "_Timed out waiting for visible output._";
|
|
64982
65123
|
}
|
|
64983
65124
|
if (params.status === "detached") {
|
|
64984
|
-
const note =
|
|
65125
|
+
const note = resolveDetachedInteractionNote({
|
|
65126
|
+
baseNote: params.note,
|
|
65127
|
+
allowTranscriptInspection: params.allowTranscriptInspection,
|
|
65128
|
+
transcriptCommand: "`/transcript`"
|
|
65129
|
+
});
|
|
64985
65130
|
return body ? `${body}
|
|
64986
65131
|
|
|
64987
65132
|
_${note}_` : `_${note}_`;
|
|
@@ -65010,7 +65155,11 @@ ${body}` : queueNote;
|
|
|
65010
65155
|
Timed out waiting for more output.` : "Timed out waiting for visible output.";
|
|
65011
65156
|
}
|
|
65012
65157
|
if (params.status === "detached") {
|
|
65013
|
-
const note =
|
|
65158
|
+
const note = resolveDetachedInteractionNote({
|
|
65159
|
+
baseNote: params.note,
|
|
65160
|
+
allowTranscriptInspection: params.allowTranscriptInspection,
|
|
65161
|
+
transcriptCommand: "/transcript"
|
|
65162
|
+
});
|
|
65014
65163
|
return body ? `${body}
|
|
65015
65164
|
|
|
65016
65165
|
${note}` : note;
|
|
@@ -65046,6 +65195,16 @@ ${body}
|
|
|
65046
65195
|
}
|
|
65047
65196
|
var renderSlackSnapshot = renderSlackTranscript;
|
|
65048
65197
|
var renderChannelSnapshot = renderSlackSnapshot;
|
|
65198
|
+
function resolveDetachedInteractionNote(params) {
|
|
65199
|
+
const note = params.baseNote ?? "This session is still running. Use `/attach`, `/watch every 30s`, or `/stop` to manage it.";
|
|
65200
|
+
if (!params.allowTranscriptInspection) {
|
|
65201
|
+
return note;
|
|
65202
|
+
}
|
|
65203
|
+
if (note.includes("/transcript")) {
|
|
65204
|
+
return note;
|
|
65205
|
+
}
|
|
65206
|
+
return `${note} You can also use ${params.transcriptCommand} to inspect the current session snapshot.`;
|
|
65207
|
+
}
|
|
65049
65208
|
// src/control/latency-debug.ts
|
|
65050
65209
|
function isLatencyDebugEnabled() {
|
|
65051
65210
|
return process.env.CLISBOT_DEBUG_LATENCY === "1";
|
|
@@ -65396,24 +65555,6 @@ function escapeRegExp(raw) {
|
|
|
65396
65555
|
// src/agents/runner-session.ts
|
|
65397
65556
|
var TMUX_MISSING_SESSION_PATTERN = /(?:can't find session:|no server running on )/i;
|
|
65398
65557
|
var TMUX_DUPLICATE_SESSION_PATTERN = /duplicate session:/i;
|
|
65399
|
-
function shellQuote2(value) {
|
|
65400
|
-
if (/^[a-zA-Z0-9_./:@=-]+$/.test(value)) {
|
|
65401
|
-
return value;
|
|
65402
|
-
}
|
|
65403
|
-
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
65404
|
-
}
|
|
65405
|
-
function buildCommandString(command, args) {
|
|
65406
|
-
return [command, ...args].map(shellQuote2).join(" ");
|
|
65407
|
-
}
|
|
65408
|
-
function buildRunnerLaunchCommand(command, args) {
|
|
65409
|
-
const wrapperDir = getClisbotWrapperDir();
|
|
65410
|
-
const wrapperPath = getClisbotWrapperPath();
|
|
65411
|
-
const exports = [
|
|
65412
|
-
`export PATH=${shellQuote2(wrapperDir)}:"$PATH"`,
|
|
65413
|
-
`export CLISBOT_BIN=${shellQuote2(wrapperPath)}`
|
|
65414
|
-
];
|
|
65415
|
-
return `${exports.join("; ")}; exec ${buildCommandString(command, args)}`;
|
|
65416
|
-
}
|
|
65417
65558
|
function summarizeSnapshot(snapshot) {
|
|
65418
65559
|
const compact = snapshot.split(`
|
|
65419
65560
|
`).map((line) => line.trim()).filter(Boolean).join(" ").slice(0, 220);
|
|
@@ -65439,8 +65580,17 @@ class RunnerSessionService {
|
|
|
65439
65580
|
this.sessionState = sessionState;
|
|
65440
65581
|
this.resolveTarget = resolveTarget;
|
|
65441
65582
|
}
|
|
65442
|
-
mapSessionError(error, sessionName, action) {
|
|
65583
|
+
async mapSessionError(error, sessionName, action, lastSnapshot = "") {
|
|
65443
65584
|
if (isMissingTmuxSessionError(error)) {
|
|
65585
|
+
const exitRecord = await readRunnerExitRecord(this.loadedConfig.stateDir, sessionName);
|
|
65586
|
+
console.error("runner session disappeared", {
|
|
65587
|
+
sessionName,
|
|
65588
|
+
action,
|
|
65589
|
+
exitCode: exitRecord?.exitCode,
|
|
65590
|
+
exitedAt: exitRecord?.exitedAt,
|
|
65591
|
+
runnerCommand: exitRecord?.command,
|
|
65592
|
+
lastVisiblePane: lastSnapshot ? summarizeSnapshot(lastSnapshot).trim() : undefined
|
|
65593
|
+
});
|
|
65444
65594
|
return new Error(`Runner session "${sessionName}" disappeared ${action}.`);
|
|
65445
65595
|
}
|
|
65446
65596
|
return error instanceof Error ? error : new Error(String(error));
|
|
@@ -65559,7 +65709,8 @@ class RunnerSessionService {
|
|
|
65559
65709
|
};
|
|
65560
65710
|
logLatencyDebug("ensure-session-ready-start", timingContext);
|
|
65561
65711
|
await ensureDir2(resolved.workspacePath);
|
|
65562
|
-
await ensureDir2(
|
|
65712
|
+
await ensureDir2(dirname12(this.loadedConfig.raw.tmux.socketPath));
|
|
65713
|
+
await ensureRunnerExitRecordDir(this.loadedConfig.stateDir, resolved.sessionName);
|
|
65563
65714
|
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
65564
65715
|
const serverRunning = await this.tmux.isServerRunning();
|
|
65565
65716
|
if (serverRunning && await this.tmux.hasSession(resolved.sessionName)) {
|
|
@@ -65567,9 +65718,10 @@ class RunnerSessionService {
|
|
|
65567
65718
|
hasStoredSessionId: Boolean(existing?.sessionId)
|
|
65568
65719
|
});
|
|
65569
65720
|
try {
|
|
65721
|
+
await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
65570
65722
|
await this.syncSessionIdentity(resolved);
|
|
65571
65723
|
} catch (error) {
|
|
65572
|
-
throw this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
65724
|
+
throw await this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
65573
65725
|
}
|
|
65574
65726
|
logLatencyDebug("ensure-session-ready-complete", timingContext, {
|
|
65575
65727
|
startupDelayMs: 0,
|
|
@@ -65586,7 +65738,15 @@ class RunnerSessionService {
|
|
|
65586
65738
|
sessionId: startupSessionId || undefined,
|
|
65587
65739
|
resume: resumingExistingSession
|
|
65588
65740
|
});
|
|
65589
|
-
|
|
65741
|
+
await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
65742
|
+
const command = buildRunnerLaunchCommand({
|
|
65743
|
+
command: runnerLaunch.command,
|
|
65744
|
+
args: runnerLaunch.args,
|
|
65745
|
+
wrapperDir: getClisbotWrapperDir(),
|
|
65746
|
+
wrapperPath: getClisbotWrapperPath(),
|
|
65747
|
+
sessionName: resolved.sessionName,
|
|
65748
|
+
stateDir: this.loadedConfig.stateDir
|
|
65749
|
+
});
|
|
65590
65750
|
try {
|
|
65591
65751
|
await this.tmux.newSession({
|
|
65592
65752
|
sessionName: resolved.sessionName,
|
|
@@ -65640,7 +65800,7 @@ class RunnerSessionService {
|
|
|
65640
65800
|
allowFreshRetry: options.allowFreshRetry
|
|
65641
65801
|
});
|
|
65642
65802
|
} catch (error) {
|
|
65643
|
-
throw this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
65803
|
+
throw await this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
65644
65804
|
}
|
|
65645
65805
|
logLatencyDebug("ensure-session-ready-complete", timingContext, {
|
|
65646
65806
|
startupDelayMs: resolved.runner.startupDelayMs,
|
|
@@ -65709,14 +65869,14 @@ class RunnerSessionService {
|
|
|
65709
65869
|
} catch (error) {
|
|
65710
65870
|
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
65711
65871
|
if (options.allowFreshRetryBeforePrompt === false || !existing?.sessionId || !isMissingTmuxSessionError(error)) {
|
|
65712
|
-
throw this.mapSessionError(error, resolved.sessionName, "before prompt submission");
|
|
65872
|
+
throw await this.mapSessionError(error, resolved.sessionName, "before prompt submission", resolved.sessionName ? await this.captureSessionSnapshot(resolved).catch(() => "") : "");
|
|
65713
65873
|
}
|
|
65714
65874
|
const retried = await this.retryFreshStartWithClearedSessionId(target, resolved, {
|
|
65715
65875
|
allowRetry: true,
|
|
65716
65876
|
nextAllowFreshRetry: false
|
|
65717
65877
|
});
|
|
65718
65878
|
if (!retried) {
|
|
65719
|
-
throw this.mapSessionError(error, resolved.sessionName, "before prompt submission");
|
|
65879
|
+
throw await this.mapSessionError(error, resolved.sessionName, "before prompt submission", resolved.sessionName ? await this.captureSessionSnapshot(resolved).catch(() => "") : "");
|
|
65720
65880
|
}
|
|
65721
65881
|
resolved = retried;
|
|
65722
65882
|
return {
|
|
@@ -65835,8 +65995,8 @@ class RunnerSessionService {
|
|
|
65835
65995
|
workspacePath: resolved.workspacePath
|
|
65836
65996
|
};
|
|
65837
65997
|
}
|
|
65838
|
-
mapRunError(error, sessionName) {
|
|
65839
|
-
return this.mapSessionError(error, sessionName, "while the prompt was running");
|
|
65998
|
+
async mapRunError(error, sessionName, lastSnapshot = "") {
|
|
65999
|
+
return await this.mapSessionError(error, sessionName, "while the prompt was running", lastSnapshot);
|
|
65840
66000
|
}
|
|
65841
66001
|
}
|
|
65842
66002
|
|
|
@@ -66348,7 +66508,7 @@ class ActiveRunManager {
|
|
|
66348
66508
|
}
|
|
66349
66509
|
});
|
|
66350
66510
|
} catch (error) {
|
|
66351
|
-
await this.failActiveRun(sessionKey, this.runnerSessions.mapRunError(error, run.resolved.sessionName));
|
|
66511
|
+
await this.failActiveRun(sessionKey, await this.runnerSessions.mapRunError(error, run.resolved.sessionName, run.latestUpdate.fullSnapshot));
|
|
66352
66512
|
}
|
|
66353
66513
|
})();
|
|
66354
66514
|
}
|
|
@@ -66454,8 +66614,8 @@ class AgentService {
|
|
|
66454
66614
|
sessionKey: this.loadedConfig.raw.session.mainKey
|
|
66455
66615
|
});
|
|
66456
66616
|
}
|
|
66457
|
-
async recordConversationReply(target, kind = "reply") {
|
|
66458
|
-
return this.sessionState.recordConversationReply(this.resolveTarget(target), kind);
|
|
66617
|
+
async recordConversationReply(target, kind = "reply", source = "channel") {
|
|
66618
|
+
return this.sessionState.recordConversationReply(this.resolveTarget(target), kind, source);
|
|
66459
66619
|
}
|
|
66460
66620
|
async runShellCommand(target, command) {
|
|
66461
66621
|
return this.queue.enqueue(`${target.sessionKey}:bash`, async () => this.runnerSessions.runShellCommand(target, command)).result;
|
|
@@ -66512,10 +66672,13 @@ class AgentService {
|
|
|
66512
66672
|
updatedAt: Date.now(),
|
|
66513
66673
|
nextRunAt: Date.now(),
|
|
66514
66674
|
promptText: params.promptText,
|
|
66675
|
+
canonicalPromptText: params.canonicalPromptText,
|
|
66676
|
+
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
66515
66677
|
promptSummary: params.promptSummary,
|
|
66516
66678
|
promptSource: params.promptSource,
|
|
66517
66679
|
createdBy: params.createdBy,
|
|
66518
|
-
force: params.force
|
|
66680
|
+
force: params.force,
|
|
66681
|
+
surfaceBinding: params.surfaceBinding
|
|
66519
66682
|
};
|
|
66520
66683
|
const resolved = this.resolveTarget(params.target);
|
|
66521
66684
|
await this.sessionState.setIntervalLoop(resolved, loop);
|
|
@@ -66553,6 +66716,8 @@ class AgentService {
|
|
|
66553
66716
|
updatedAt: Date.now(),
|
|
66554
66717
|
nextRunAt,
|
|
66555
66718
|
promptText: params.promptText,
|
|
66719
|
+
canonicalPromptText: params.canonicalPromptText,
|
|
66720
|
+
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
66556
66721
|
promptSummary: params.promptSummary,
|
|
66557
66722
|
promptSource: params.promptSource,
|
|
66558
66723
|
createdBy: params.createdBy,
|
|
@@ -66562,7 +66727,8 @@ class AgentService {
|
|
|
66562
66727
|
hour: params.hour,
|
|
66563
66728
|
minute: params.minute,
|
|
66564
66729
|
timezone: params.timezone,
|
|
66565
|
-
force: false
|
|
66730
|
+
force: false,
|
|
66731
|
+
surfaceBinding: params.surfaceBinding
|
|
66566
66732
|
};
|
|
66567
66733
|
const resolved = this.resolveTarget(params.target);
|
|
66568
66734
|
await this.sessionState.setIntervalLoop(resolved, loop);
|
|
@@ -66718,7 +66884,8 @@ class AgentService {
|
|
|
66718
66884
|
this.dropManagedIntervalLoop(loopId);
|
|
66719
66885
|
return;
|
|
66720
66886
|
}
|
|
66721
|
-
const
|
|
66887
|
+
const promptText = this.buildManagedLoopPrompt(managed.target.agentId, nextLoopState);
|
|
66888
|
+
const { result } = this.enqueuePrompt(managed.target, promptText, {
|
|
66722
66889
|
observerId: `loop:${loopId}:${attemptedRuns}`,
|
|
66723
66890
|
onUpdate: async () => {
|
|
66724
66891
|
return;
|
|
@@ -66790,6 +66957,52 @@ class AgentService {
|
|
|
66790
66957
|
}
|
|
66791
66958
|
return nowMs + loop.intervalMs;
|
|
66792
66959
|
}
|
|
66960
|
+
buildManagedLoopPrompt(agentId, loop) {
|
|
66961
|
+
if (!loop.canonicalPromptText || !loop.surfaceBinding) {
|
|
66962
|
+
return loop.promptText;
|
|
66963
|
+
}
|
|
66964
|
+
const identity = this.buildLoopChannelIdentity(loop.surfaceBinding);
|
|
66965
|
+
const channelConfig = identity.platform === "slack" ? this.loadedConfig.raw.channels.slack : this.loadedConfig.raw.channels.telegram;
|
|
66966
|
+
const { responseMode, streaming } = this.resolveLoopSurfaceModes(identity);
|
|
66967
|
+
return buildAgentPromptText({
|
|
66968
|
+
text: loop.canonicalPromptText,
|
|
66969
|
+
identity,
|
|
66970
|
+
config: channelConfig.agentPrompt,
|
|
66971
|
+
cliTool: getAgentEntry(this.loadedConfig, agentId)?.cliTool,
|
|
66972
|
+
responseMode,
|
|
66973
|
+
streaming,
|
|
66974
|
+
protectedControlMutationRule: loop.protectedControlMutationRule
|
|
66975
|
+
});
|
|
66976
|
+
}
|
|
66977
|
+
buildLoopChannelIdentity(binding) {
|
|
66978
|
+
return {
|
|
66979
|
+
platform: binding.platform,
|
|
66980
|
+
conversationKind: binding.conversationKind,
|
|
66981
|
+
channelId: binding.channelId,
|
|
66982
|
+
chatId: binding.chatId,
|
|
66983
|
+
threadTs: binding.threadTs,
|
|
66984
|
+
topicId: binding.topicId
|
|
66985
|
+
};
|
|
66986
|
+
}
|
|
66987
|
+
resolveLoopSurfaceModes(identity) {
|
|
66988
|
+
const channelConfig = identity.platform === "slack" ? this.loadedConfig.raw.channels.slack : this.loadedConfig.raw.channels.telegram;
|
|
66989
|
+
let responseMode = channelConfig.responseMode;
|
|
66990
|
+
let streaming = channelConfig.streaming;
|
|
66991
|
+
if (identity.conversationKind === "dm") {
|
|
66992
|
+
responseMode = channelConfig.directMessages.responseMode ?? responseMode;
|
|
66993
|
+
streaming = channelConfig.directMessages.streaming ?? streaming;
|
|
66994
|
+
}
|
|
66995
|
+
try {
|
|
66996
|
+
responseMode = resolveConfiguredSurfaceModeTarget(this.loadedConfig.raw, "responseMode", buildConfiguredTargetFromIdentity(identity)).get() ?? responseMode;
|
|
66997
|
+
} catch {}
|
|
66998
|
+
try {
|
|
66999
|
+
streaming = resolveConfiguredSurfaceModeTarget(this.loadedConfig.raw, "streaming", buildConfiguredTargetFromIdentity(identity)).get() ?? streaming;
|
|
67000
|
+
} catch {}
|
|
67001
|
+
return {
|
|
67002
|
+
responseMode,
|
|
67003
|
+
streaming
|
|
67004
|
+
};
|
|
67005
|
+
}
|
|
66793
67006
|
}
|
|
66794
67007
|
|
|
66795
67008
|
// src/config/channel-accounts.ts
|
|
@@ -67112,6 +67325,37 @@ function parseAgentCommand(text, options = {}) {
|
|
|
67112
67325
|
action: "status"
|
|
67113
67326
|
};
|
|
67114
67327
|
}
|
|
67328
|
+
if (lowered === "streaming") {
|
|
67329
|
+
const action = withoutSlash.slice(command.length).trim().toLowerCase();
|
|
67330
|
+
if (!action || action === "status") {
|
|
67331
|
+
return {
|
|
67332
|
+
type: "control",
|
|
67333
|
+
name: "streaming",
|
|
67334
|
+
action: "status"
|
|
67335
|
+
};
|
|
67336
|
+
}
|
|
67337
|
+
if (action === "on") {
|
|
67338
|
+
return {
|
|
67339
|
+
type: "control",
|
|
67340
|
+
name: "streaming",
|
|
67341
|
+
action: "on",
|
|
67342
|
+
streaming: "all"
|
|
67343
|
+
};
|
|
67344
|
+
}
|
|
67345
|
+
if (action === "off" || action === "latest" || action === "all") {
|
|
67346
|
+
return {
|
|
67347
|
+
type: "control",
|
|
67348
|
+
name: "streaming",
|
|
67349
|
+
action,
|
|
67350
|
+
streaming: action
|
|
67351
|
+
};
|
|
67352
|
+
}
|
|
67353
|
+
return {
|
|
67354
|
+
type: "control",
|
|
67355
|
+
name: "streaming",
|
|
67356
|
+
action: "status"
|
|
67357
|
+
};
|
|
67358
|
+
}
|
|
67115
67359
|
if (lowered === "additionalmessagemode") {
|
|
67116
67360
|
const action = withoutSlash.slice(command.length).trim().toLowerCase();
|
|
67117
67361
|
if (!action || action === "status") {
|
|
@@ -67240,7 +67484,7 @@ function renderAgentControlSlashHelp() {
|
|
|
67240
67484
|
"- `/status`: show the current route status and operator setup commands",
|
|
67241
67485
|
"- `/help`: show available control slash commands",
|
|
67242
67486
|
"- `/whoami`: show the current platform, route, and sender identity details",
|
|
67243
|
-
"- `/transcript`: show the current conversation session transcript when the route
|
|
67487
|
+
"- `/transcript`: show the current conversation session transcript when the route verbose policy allows it",
|
|
67244
67488
|
"- `/attach`: attach this thread to the active run and resume live updates when it is still processing",
|
|
67245
67489
|
"- `/detach`: stop live updates for this thread while still allowing final settlement here",
|
|
67246
67490
|
"- `/watch every 30s [for 10m]`: post the latest state on an interval until the run settles or the watch window ends",
|
|
@@ -67251,6 +67495,11 @@ function renderAgentControlSlashHelp() {
|
|
|
67251
67495
|
"- `/followup mention-only`: require explicit mention for each later turn",
|
|
67252
67496
|
"- `/followup pause`: stop passive follow-up until the next explicit mention",
|
|
67253
67497
|
"- `/followup resume`: clear the runtime override and restore config defaults",
|
|
67498
|
+
"- `/streaming status`: show the configured streaming mode for this surface",
|
|
67499
|
+
"- `/streaming on`: enable streaming for this surface using the current `all` preview behavior",
|
|
67500
|
+
"- `/streaming off`: disable surface streaming previews for this surface",
|
|
67501
|
+
"- `/streaming latest`: prefer the latest preview shape for this surface",
|
|
67502
|
+
"- `/streaming all`: retain the full current preview shape for this surface",
|
|
67254
67503
|
"- `/responsemode status`: show the configured response mode for this surface",
|
|
67255
67504
|
"- `/responsemode capture-pane`: settle replies from captured pane output for this surface",
|
|
67256
67505
|
"- `/responsemode message-tool`: expect the agent to reply through `clisbot message send` for this surface",
|
|
@@ -67262,8 +67511,8 @@ function renderAgentControlSlashHelp() {
|
|
|
67262
67511
|
"- `/queue-list`: show queued messages that have not started yet",
|
|
67263
67512
|
"- `/queue-clear`: clear queued messages that have not started yet",
|
|
67264
67513
|
...renderLoopHelpLines(),
|
|
67265
|
-
"- `/bash` followed by a shell command: requires `
|
|
67266
|
-
"- shortcut prefixes such as `!` run bash when the
|
|
67514
|
+
"- `/bash` followed by a shell command: requires `shellExecute` on the resolved agent role",
|
|
67515
|
+
"- shortcut prefixes such as `!` run bash only when the resolved agent role allows `shellExecute`",
|
|
67267
67516
|
"",
|
|
67268
67517
|
"Other slash commands are forwarded to the agent unchanged."
|
|
67269
67518
|
].join(`
|
|
@@ -67300,6 +67549,7 @@ function buildRenderedMessageState(params) {
|
|
|
67300
67549
|
queuePosition: params.queuePosition,
|
|
67301
67550
|
maxChars: params.maxChars,
|
|
67302
67551
|
note: params.note,
|
|
67552
|
+
allowTranscriptInspection: params.allowTranscriptInspection,
|
|
67303
67553
|
responsePolicy: params.responsePolicy
|
|
67304
67554
|
}),
|
|
67305
67555
|
body
|
|
@@ -67329,35 +67579,45 @@ function formatChannelFollowUpStatus(params) {
|
|
|
67329
67579
|
`);
|
|
67330
67580
|
}
|
|
67331
67581
|
|
|
67332
|
-
// src/channels/
|
|
67333
|
-
function
|
|
67582
|
+
// src/channels/streaming-config.ts
|
|
67583
|
+
function getEditableConfigPath7() {
|
|
67584
|
+
return process.env.CLISBOT_CONFIG_PATH;
|
|
67585
|
+
}
|
|
67586
|
+
async function getConversationStreaming(params) {
|
|
67587
|
+
const { config } = await readEditableConfig(getEditableConfigPath7());
|
|
67588
|
+
const target = resolveConfiguredSurfaceModeTarget(config, "streaming", buildConfiguredTargetFromIdentity(params.identity));
|
|
67334
67589
|
return {
|
|
67335
|
-
|
|
67336
|
-
|
|
67590
|
+
label: target.label,
|
|
67591
|
+
streaming: target.get()
|
|
67337
67592
|
};
|
|
67338
67593
|
}
|
|
67339
|
-
function
|
|
67340
|
-
|
|
67341
|
-
|
|
67342
|
-
|
|
67343
|
-
|
|
67344
|
-
|
|
67345
|
-
|
|
67346
|
-
|
|
67347
|
-
|
|
67348
|
-
|
|
67349
|
-
}
|
|
67350
|
-
return params.config.allowUsers.includes(normalizedUserId);
|
|
67594
|
+
async function setConversationStreaming(params) {
|
|
67595
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath7());
|
|
67596
|
+
const target = resolveConfiguredSurfaceModeTarget(config, "streaming", buildConfiguredTargetFromIdentity(params.identity));
|
|
67597
|
+
target.set(params.streaming);
|
|
67598
|
+
await writeEditableConfig(configPath, config);
|
|
67599
|
+
return {
|
|
67600
|
+
configPath,
|
|
67601
|
+
label: target.label,
|
|
67602
|
+
streaming: params.streaming
|
|
67603
|
+
};
|
|
67351
67604
|
}
|
|
67352
67605
|
|
|
67353
67606
|
// src/channels/interaction-processing.ts
|
|
67354
|
-
import { join as
|
|
67355
|
-
|
|
67607
|
+
import { join as join8 } from "node:path";
|
|
67608
|
+
var MESSAGE_TOOL_FINAL_GRACE_WINDOW_MS = 3000;
|
|
67609
|
+
var MESSAGE_TOOL_FINAL_GRACE_POLL_MS = 100;
|
|
67610
|
+
function renderSensitiveCommandDisabledMessage() {
|
|
67356
67611
|
return [
|
|
67357
|
-
"
|
|
67358
|
-
"
|
|
67359
|
-
|
|
67360
|
-
|
|
67612
|
+
"Shell execution is not allowed for your current role on this agent.",
|
|
67613
|
+
"Ask an app or agent admin to grant `shellExecute` if this surface should allow `/bash`."
|
|
67614
|
+
].join(`
|
|
67615
|
+
`);
|
|
67616
|
+
}
|
|
67617
|
+
function renderTranscriptDisabledMessage() {
|
|
67618
|
+
return [
|
|
67619
|
+
"Transcript inspection is disabled for this route.",
|
|
67620
|
+
'Set `verbose: "minimal"` on the route or channel to allow `/transcript`.'
|
|
67361
67621
|
].join(`
|
|
67362
67622
|
`);
|
|
67363
67623
|
}
|
|
@@ -67385,7 +67645,7 @@ function renderWhoAmIMessage(params) {
|
|
|
67385
67645
|
if (params.identity.topicId) {
|
|
67386
67646
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
67387
67647
|
}
|
|
67388
|
-
lines.push(`
|
|
67648
|
+
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}\``);
|
|
67389
67649
|
return lines.join(`
|
|
67390
67650
|
`);
|
|
67391
67651
|
}
|
|
@@ -67413,7 +67673,7 @@ function renderRouteStatusMessage(params) {
|
|
|
67413
67673
|
if (params.identity.topicId) {
|
|
67414
67674
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
67415
67675
|
}
|
|
67416
|
-
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}
|
|
67676
|
+
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}\``);
|
|
67417
67677
|
if (params.runtimeState.startedAt) {
|
|
67418
67678
|
lines.push(`run.startedAt: \`${new Date(params.runtimeState.startedAt).toISOString()}\``);
|
|
67419
67679
|
}
|
|
@@ -67427,11 +67687,13 @@ function renderRouteStatusMessage(params) {
|
|
|
67427
67687
|
lines.push(`- \`${loop.id}\` ${renderLoopStatusSchedule(loop)} remaining \`${loop.remainingRuns}\` nextRunAt \`${new Date(loop.nextRunAt).toISOString()}\``);
|
|
67428
67688
|
}
|
|
67429
67689
|
}
|
|
67430
|
-
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`
|
|
67431
|
-
lines.push("", ...renderPrivilegeCommandHelpLines(params.identity));
|
|
67690
|
+
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`");
|
|
67432
67691
|
return lines.join(`
|
|
67433
67692
|
`);
|
|
67434
67693
|
}
|
|
67694
|
+
function allowTranscriptInspectionForRoute(route) {
|
|
67695
|
+
return route.verbose === "minimal";
|
|
67696
|
+
}
|
|
67435
67697
|
function renderResponseModeStatusMessage(params) {
|
|
67436
67698
|
const lines = [
|
|
67437
67699
|
"clisbot response mode",
|
|
@@ -67446,6 +67708,18 @@ function renderResponseModeStatusMessage(params) {
|
|
|
67446
67708
|
return lines.join(`
|
|
67447
67709
|
`);
|
|
67448
67710
|
}
|
|
67711
|
+
function renderStreamingStatusMessage(params) {
|
|
67712
|
+
const lines = [
|
|
67713
|
+
`clisbot streaming mode: \`${params.route.streaming}\``
|
|
67714
|
+
];
|
|
67715
|
+
if (params.persisted) {
|
|
67716
|
+
lines.push("");
|
|
67717
|
+
lines.push(`config.target: \`${params.persisted.label}\``);
|
|
67718
|
+
}
|
|
67719
|
+
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");
|
|
67720
|
+
return lines.join(`
|
|
67721
|
+
`);
|
|
67722
|
+
}
|
|
67449
67723
|
function renderAdditionalMessageModeStatusMessage(params) {
|
|
67450
67724
|
const lines = [
|
|
67451
67725
|
"clisbot additional message mode",
|
|
@@ -67471,11 +67745,17 @@ function buildChannelObserverId(identity) {
|
|
|
67471
67745
|
identity.topicId ?? ""
|
|
67472
67746
|
].join(":");
|
|
67473
67747
|
}
|
|
67474
|
-
function buildSteeringMessage(text) {
|
|
67748
|
+
function buildSteeringMessage(text, protectedControlMutationRule) {
|
|
67749
|
+
const systemLines = [
|
|
67750
|
+
"A new user message arrived while you were still working.",
|
|
67751
|
+
"Adjust your current work if needed and continue."
|
|
67752
|
+
];
|
|
67753
|
+
if (protectedControlMutationRule) {
|
|
67754
|
+
systemLines.push("", protectedControlMutationRule);
|
|
67755
|
+
}
|
|
67475
67756
|
return [
|
|
67476
67757
|
"<system>",
|
|
67477
|
-
|
|
67478
|
-
"Adjust your current work if needed and continue.",
|
|
67758
|
+
...systemLines,
|
|
67479
67759
|
"</system>",
|
|
67480
67760
|
"",
|
|
67481
67761
|
"<user>",
|
|
@@ -67614,7 +67894,7 @@ async function resolveLoopPromptText(params) {
|
|
|
67614
67894
|
};
|
|
67615
67895
|
}
|
|
67616
67896
|
const workspacePath = params.agentService.getWorkspacePath(params.sessionTarget);
|
|
67617
|
-
const loopPromptPath =
|
|
67897
|
+
const loopPromptPath = join8(workspacePath, "LOOP.md");
|
|
67618
67898
|
if (!await fileExists(loopPromptPath)) {
|
|
67619
67899
|
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.`);
|
|
67620
67900
|
}
|
|
@@ -67627,29 +67907,93 @@ async function resolveLoopPromptText(params) {
|
|
|
67627
67907
|
maintenancePrompt: true
|
|
67628
67908
|
};
|
|
67629
67909
|
}
|
|
67910
|
+
function buildLoopSurfaceBinding(identity) {
|
|
67911
|
+
return {
|
|
67912
|
+
platform: identity.platform,
|
|
67913
|
+
conversationKind: identity.conversationKind,
|
|
67914
|
+
channelId: identity.channelId,
|
|
67915
|
+
chatId: identity.chatId,
|
|
67916
|
+
threadTs: identity.threadTs,
|
|
67917
|
+
topicId: identity.topicId
|
|
67918
|
+
};
|
|
67919
|
+
}
|
|
67630
67920
|
async function executePromptDelivery(params) {
|
|
67631
67921
|
let responseChunks = [];
|
|
67632
67922
|
let renderedState;
|
|
67633
67923
|
let renderChain = Promise.resolve();
|
|
67634
67924
|
let replyRecorded = false;
|
|
67925
|
+
let finalReplyRecorded = false;
|
|
67635
67926
|
let loggedFirstRunningUpdate = false;
|
|
67636
|
-
|
|
67637
|
-
|
|
67927
|
+
let activePreviewStartedAt;
|
|
67928
|
+
let lastFrozenPreviewText;
|
|
67929
|
+
const paneManagedDelivery = params.route.responseMode === "capture-pane" || params.forceQueuedDelivery === true;
|
|
67930
|
+
const messageToolPreview = params.route.responseMode === "message-tool" && params.forceQueuedDelivery !== true && params.route.streaming !== "off";
|
|
67931
|
+
const previewEnabled = params.route.streaming !== "off" && (paneManagedDelivery || messageToolPreview);
|
|
67932
|
+
async function recordVisibleReply(kind = "reply", source = "channel") {
|
|
67933
|
+
if (kind === "final") {
|
|
67934
|
+
if (finalReplyRecorded) {
|
|
67935
|
+
return;
|
|
67936
|
+
}
|
|
67937
|
+
await params.agentService.recordConversationReply(params.sessionTarget, "final", source);
|
|
67938
|
+
finalReplyRecorded = true;
|
|
67939
|
+
replyRecorded = true;
|
|
67940
|
+
return;
|
|
67941
|
+
}
|
|
67638
67942
|
if (replyRecorded) {
|
|
67639
67943
|
return;
|
|
67640
67944
|
}
|
|
67641
|
-
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67945
|
+
await params.agentService.recordConversationReply(params.sessionTarget, "reply", source);
|
|
67642
67946
|
replyRecorded = true;
|
|
67643
67947
|
}
|
|
67644
67948
|
async function renderResponseText(nextText) {
|
|
67645
67949
|
if (!responseChunks.length) {
|
|
67646
67950
|
responseChunks = await params.postText(nextText);
|
|
67647
|
-
|
|
67648
|
-
await recordReplyIfNeeded();
|
|
67649
|
-
}
|
|
67650
|
-
return;
|
|
67951
|
+
return responseChunks.length > 0;
|
|
67651
67952
|
}
|
|
67652
67953
|
responseChunks = await params.reconcileText(responseChunks, nextText);
|
|
67954
|
+
return false;
|
|
67955
|
+
}
|
|
67956
|
+
async function clearResponseText() {
|
|
67957
|
+
if (!responseChunks.length) {
|
|
67958
|
+
return;
|
|
67959
|
+
}
|
|
67960
|
+
responseChunks = await params.reconcileText(responseChunks, "");
|
|
67961
|
+
renderedState = undefined;
|
|
67962
|
+
activePreviewStartedAt = undefined;
|
|
67963
|
+
}
|
|
67964
|
+
function resetPreviewBoundary() {
|
|
67965
|
+
lastFrozenPreviewText = renderedState?.text ?? lastFrozenPreviewText;
|
|
67966
|
+
responseChunks = [];
|
|
67967
|
+
renderedState = undefined;
|
|
67968
|
+
activePreviewStartedAt = undefined;
|
|
67969
|
+
}
|
|
67970
|
+
async function getMessageToolRuntimeSignals() {
|
|
67971
|
+
if (params.route.responseMode !== "message-tool" || params.forceQueuedDelivery === true) {
|
|
67972
|
+
return {
|
|
67973
|
+
lastMessageToolReplyAt: undefined,
|
|
67974
|
+
messageToolFinalReplyAt: undefined
|
|
67975
|
+
};
|
|
67976
|
+
}
|
|
67977
|
+
const runtime = await params.agentService.getSessionRuntime?.(params.sessionTarget);
|
|
67978
|
+
return {
|
|
67979
|
+
lastMessageToolReplyAt: runtime?.lastMessageToolReplyAt,
|
|
67980
|
+
messageToolFinalReplyAt: runtime?.messageToolFinalReplyAt
|
|
67981
|
+
};
|
|
67982
|
+
}
|
|
67983
|
+
async function waitForMessageToolFinalReply() {
|
|
67984
|
+
const deadline = Date.now() + MESSAGE_TOOL_FINAL_GRACE_WINDOW_MS;
|
|
67985
|
+
while (true) {
|
|
67986
|
+
const signals = await getMessageToolRuntimeSignals();
|
|
67987
|
+
const toolFinalSeen = typeof signals.messageToolFinalReplyAt === "number" && Number.isFinite(signals.messageToolFinalReplyAt);
|
|
67988
|
+
if (toolFinalSeen) {
|
|
67989
|
+
return true;
|
|
67990
|
+
}
|
|
67991
|
+
const remainingMs = deadline - Date.now();
|
|
67992
|
+
if (remainingMs <= 0) {
|
|
67993
|
+
return false;
|
|
67994
|
+
}
|
|
67995
|
+
await sleep(Math.min(MESSAGE_TOOL_FINAL_GRACE_POLL_MS, remainingMs));
|
|
67996
|
+
}
|
|
67653
67997
|
}
|
|
67654
67998
|
logLatencyDebug("channel-enqueue-start", params.timingContext, {
|
|
67655
67999
|
agentId: params.route.agentId,
|
|
@@ -67660,7 +68004,7 @@ async function executePromptDelivery(params) {
|
|
|
67660
68004
|
observerId: params.observerId,
|
|
67661
68005
|
timingContext: params.timingContext,
|
|
67662
68006
|
onUpdate: async (update) => {
|
|
67663
|
-
if (!
|
|
68007
|
+
if (!paneManagedDelivery && !messageToolPreview) {
|
|
67664
68008
|
return;
|
|
67665
68009
|
}
|
|
67666
68010
|
if (params.route.streaming === "off" && update.status === "running") {
|
|
@@ -67674,6 +68018,15 @@ async function executePromptDelivery(params) {
|
|
|
67674
68018
|
});
|
|
67675
68019
|
}
|
|
67676
68020
|
await (renderChain = renderChain.then(async () => {
|
|
68021
|
+
const signals = await getMessageToolRuntimeSignals();
|
|
68022
|
+
if (messageToolPreview && typeof activePreviewStartedAt === "number" && typeof signals.messageToolFinalReplyAt === "number" && signals.messageToolFinalReplyAt >= activePreviewStartedAt) {
|
|
68023
|
+
lastFrozenPreviewText = renderedState?.text ?? lastFrozenPreviewText;
|
|
68024
|
+
activePreviewStartedAt = undefined;
|
|
68025
|
+
return;
|
|
68026
|
+
}
|
|
68027
|
+
if (messageToolPreview && typeof activePreviewStartedAt === "number" && typeof signals.lastMessageToolReplyAt === "number" && signals.lastMessageToolReplyAt >= activePreviewStartedAt) {
|
|
68028
|
+
resetPreviewBoundary();
|
|
68029
|
+
}
|
|
67677
68030
|
const nextState2 = buildRenderedMessageState({
|
|
67678
68031
|
platform: params.identity.platform,
|
|
67679
68032
|
status: update.status,
|
|
@@ -67681,21 +68034,26 @@ async function executePromptDelivery(params) {
|
|
|
67681
68034
|
queuePosition: positionAhead,
|
|
67682
68035
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67683
68036
|
note: update.note,
|
|
68037
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
67684
68038
|
previousState: renderedState,
|
|
67685
68039
|
responsePolicy: params.route.response
|
|
67686
68040
|
});
|
|
67687
68041
|
if (renderedState?.text === nextState2.text) {
|
|
67688
68042
|
return;
|
|
67689
68043
|
}
|
|
67690
|
-
if (!
|
|
68044
|
+
if (!renderedState && lastFrozenPreviewText === nextState2.text) {
|
|
67691
68045
|
return;
|
|
67692
68046
|
}
|
|
67693
|
-
await renderResponseText(nextState2.text);
|
|
68047
|
+
const postedNew2 = await renderResponseText(nextState2.text);
|
|
68048
|
+
if (postedNew2) {
|
|
68049
|
+
await recordVisibleReply("reply", "channel");
|
|
68050
|
+
activePreviewStartedAt = Date.now();
|
|
68051
|
+
}
|
|
67694
68052
|
renderedState = nextState2;
|
|
67695
68053
|
}));
|
|
67696
68054
|
}
|
|
67697
68055
|
});
|
|
67698
|
-
if (
|
|
68056
|
+
if (previewEnabled) {
|
|
67699
68057
|
const placeholderText = renderPlatformInteraction({
|
|
67700
68058
|
platform: params.identity.platform,
|
|
67701
68059
|
status: positionAhead > 0 ? "queued" : "running",
|
|
@@ -67704,13 +68062,16 @@ async function executePromptDelivery(params) {
|
|
|
67704
68062
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67705
68063
|
note: positionAhead > 0 ? "Waiting for the agent queue to clear." : "Working..."
|
|
67706
68064
|
});
|
|
67707
|
-
|
|
67708
|
-
|
|
68065
|
+
const postedNew2 = await renderResponseText(placeholderText);
|
|
68066
|
+
if (postedNew2) {
|
|
68067
|
+
await recordVisibleReply("reply", "channel");
|
|
68068
|
+
activePreviewStartedAt = Date.now();
|
|
68069
|
+
}
|
|
67709
68070
|
renderedState = {
|
|
67710
68071
|
text: placeholderText,
|
|
67711
68072
|
body: ""
|
|
67712
68073
|
};
|
|
67713
|
-
} else if (
|
|
68074
|
+
} else if (paneManagedDelivery && positionAhead > 0 && params.route.streaming !== "off") {
|
|
67714
68075
|
const queuedText = renderPlatformInteraction({
|
|
67715
68076
|
platform: params.identity.platform,
|
|
67716
68077
|
status: "queued",
|
|
@@ -67719,8 +68080,10 @@ async function executePromptDelivery(params) {
|
|
|
67719
68080
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67720
68081
|
note: "Waiting for the agent queue to clear."
|
|
67721
68082
|
});
|
|
67722
|
-
|
|
67723
|
-
|
|
68083
|
+
const postedNew2 = await renderResponseText(queuedText);
|
|
68084
|
+
if (postedNew2) {
|
|
68085
|
+
await recordVisibleReply("reply", "channel");
|
|
68086
|
+
}
|
|
67724
68087
|
renderedState = {
|
|
67725
68088
|
text: queuedText,
|
|
67726
68089
|
body: ""
|
|
@@ -67728,55 +68091,86 @@ async function executePromptDelivery(params) {
|
|
|
67728
68091
|
}
|
|
67729
68092
|
const finalResult = await result;
|
|
67730
68093
|
await renderChain;
|
|
67731
|
-
if (!channelManagedDelivery) {
|
|
67732
|
-
if (finalResult.status !== "error") {
|
|
67733
|
-
return;
|
|
67734
|
-
}
|
|
67735
|
-
await params.postText(renderPlatformInteraction({
|
|
67736
|
-
platform: params.identity.platform,
|
|
67737
|
-
status: finalResult.status,
|
|
67738
|
-
content: finalResult.note ?? finalResult.snapshot,
|
|
67739
|
-
maxChars: Number.POSITIVE_INFINITY,
|
|
67740
|
-
note: finalResult.note,
|
|
67741
|
-
responsePolicy: "final"
|
|
67742
|
-
}));
|
|
67743
|
-
await recordReplyIfNeeded();
|
|
67744
|
-
return;
|
|
67745
|
-
}
|
|
67746
68094
|
const nextState = buildRenderedMessageState({
|
|
67747
68095
|
platform: params.identity.platform,
|
|
67748
68096
|
status: finalResult.status,
|
|
67749
68097
|
snapshot: finalResult.snapshot,
|
|
67750
68098
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67751
68099
|
note: finalResult.note,
|
|
68100
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
67752
68101
|
previousState: renderedState,
|
|
67753
68102
|
responsePolicy: params.route.response
|
|
67754
68103
|
});
|
|
67755
|
-
if (
|
|
67756
|
-
|
|
68104
|
+
if (paneManagedDelivery) {
|
|
68105
|
+
if (params.route.streaming === "off") {
|
|
68106
|
+
const postedNew3 = await renderResponseText(renderPlatformInteraction({
|
|
68107
|
+
platform: params.identity.platform,
|
|
68108
|
+
status: finalResult.status,
|
|
68109
|
+
content: nextState.body,
|
|
68110
|
+
maxChars: Number.POSITIVE_INFINITY,
|
|
68111
|
+
note: finalResult.note,
|
|
68112
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
68113
|
+
responsePolicy: params.route.response
|
|
68114
|
+
}));
|
|
68115
|
+
if (postedNew3 || finalResult.status === "completed") {
|
|
68116
|
+
await recordVisibleReply(finalResult.status === "completed" ? "final" : "reply", "channel");
|
|
68117
|
+
}
|
|
68118
|
+
return;
|
|
68119
|
+
}
|
|
68120
|
+
const postedNew2 = await renderResponseText(nextState.text);
|
|
68121
|
+
if (postedNew2) {
|
|
68122
|
+
await recordVisibleReply("reply", "channel");
|
|
68123
|
+
}
|
|
68124
|
+
if (finalResult.status === "completed") {
|
|
68125
|
+
await recordVisibleReply("final", "channel");
|
|
68126
|
+
}
|
|
68127
|
+
return;
|
|
68128
|
+
}
|
|
68129
|
+
const toolFinalSeen = finalResult.status === "completed" ? await waitForMessageToolFinalReply() : false;
|
|
68130
|
+
if (finalResult.status === "completed" && toolFinalSeen) {
|
|
68131
|
+
if (params.route.response === "final") {
|
|
68132
|
+
await clearResponseText();
|
|
68133
|
+
}
|
|
68134
|
+
return;
|
|
68135
|
+
}
|
|
68136
|
+
if (finalResult.status === "completed") {
|
|
68137
|
+
return;
|
|
68138
|
+
}
|
|
68139
|
+
if (params.route.streaming === "off" || responseChunks.length === 0) {
|
|
68140
|
+
const postedNew2 = await renderResponseText(renderPlatformInteraction({
|
|
67757
68141
|
platform: params.identity.platform,
|
|
67758
68142
|
status: finalResult.status,
|
|
67759
68143
|
content: nextState.body,
|
|
67760
68144
|
maxChars: Number.POSITIVE_INFINITY,
|
|
67761
68145
|
note: finalResult.note,
|
|
67762
|
-
|
|
68146
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
68147
|
+
responsePolicy: params.route.response
|
|
67763
68148
|
}));
|
|
67764
|
-
|
|
68149
|
+
if (postedNew2) {
|
|
68150
|
+
await recordVisibleReply("reply", "channel");
|
|
68151
|
+
}
|
|
67765
68152
|
return;
|
|
67766
68153
|
}
|
|
67767
|
-
await renderResponseText(nextState.text);
|
|
68154
|
+
const postedNew = await renderResponseText(nextState.text);
|
|
68155
|
+
if (postedNew) {
|
|
68156
|
+
await recordVisibleReply("reply", "channel");
|
|
68157
|
+
}
|
|
67768
68158
|
} catch (error) {
|
|
67769
68159
|
if (error instanceof ClearedQueuedTaskError) {
|
|
67770
68160
|
return;
|
|
67771
68161
|
}
|
|
67772
68162
|
if (error instanceof ActiveRunInProgressError) {
|
|
67773
|
-
const activeText = error.update.
|
|
68163
|
+
const activeText = error.update.status === "detached" ? resolveDetachedInteractionNote({
|
|
68164
|
+
baseNote: error.update.note ?? String(error),
|
|
68165
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
68166
|
+
transcriptCommand: params.identity.platform === "telegram" ? "/transcript" : "`/transcript`"
|
|
68167
|
+
}) : error.update.note ?? String(error);
|
|
67774
68168
|
if (params.route.streaming !== "off" && responseChunks.length > 0) {
|
|
67775
68169
|
await params.reconcileText(responseChunks, activeText);
|
|
67776
68170
|
} else {
|
|
67777
68171
|
await params.postText(activeText);
|
|
67778
68172
|
}
|
|
67779
|
-
await
|
|
68173
|
+
await recordVisibleReply("reply", "channel");
|
|
67780
68174
|
return;
|
|
67781
68175
|
}
|
|
67782
68176
|
const errorText = renderPlatformInteraction({
|
|
@@ -67790,12 +68184,24 @@ async function executePromptDelivery(params) {
|
|
|
67790
68184
|
} else {
|
|
67791
68185
|
await params.postText(errorText);
|
|
67792
68186
|
}
|
|
68187
|
+
await recordVisibleReply("reply", "channel");
|
|
67793
68188
|
}
|
|
67794
68189
|
}
|
|
67795
68190
|
async function processChannelInteraction(params) {
|
|
68191
|
+
const interactionResult = {
|
|
68192
|
+
processingIndicatorLifecycle: "handler"
|
|
68193
|
+
};
|
|
67796
68194
|
let responseChunks = [];
|
|
67797
68195
|
let renderedState;
|
|
67798
68196
|
const observerId = buildChannelObserverId(params.identity);
|
|
68197
|
+
const auth = params.auth ?? {
|
|
68198
|
+
principal: params.senderId ? `${params.identity.platform}:${params.senderId}` : undefined,
|
|
68199
|
+
appRole: "member",
|
|
68200
|
+
agentRole: "member",
|
|
68201
|
+
mayBypassPairing: false,
|
|
68202
|
+
mayManageProtectedResources: false,
|
|
68203
|
+
canUseShell: false
|
|
68204
|
+
};
|
|
67799
68205
|
let replyRecorded = false;
|
|
67800
68206
|
let renderChain = Promise.resolve();
|
|
67801
68207
|
async function recordReplyIfNeeded() {
|
|
@@ -67852,14 +68258,12 @@ async function processChannelInteraction(params) {
|
|
|
67852
68258
|
const sessionBusy = await (params.agentService.isAwaitingFollowUpRouting?.(params.sessionTarget) ?? params.agentService.isSessionBusy?.(params.sessionTarget) ?? false);
|
|
67853
68259
|
const queueByMode = !explicitQueueMessage && params.route.additionalMessageMode === "queue" && sessionBusy;
|
|
67854
68260
|
const forceQueuedDelivery = typeof explicitQueueMessage === "string" || queueByMode;
|
|
67855
|
-
const
|
|
67856
|
-
|
|
67857
|
-
|
|
67858
|
-
|
|
67859
|
-
})) {
|
|
67860
|
-
await params.postText(renderSensitiveCommandDisabledMessage(params.identity));
|
|
68261
|
+
const delayedPromptText = explicitQueueMessage ? params.agentPromptBuilder ? params.agentPromptBuilder(explicitQueueMessage) : explicitQueueMessage : params.agentPromptText ?? params.text;
|
|
68262
|
+
const isSensitiveCommand = slashCommand?.type === "bash";
|
|
68263
|
+
if (isSensitiveCommand && !auth.canUseShell) {
|
|
68264
|
+
await params.postText(renderSensitiveCommandDisabledMessage());
|
|
67861
68265
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67862
|
-
return;
|
|
68266
|
+
return interactionResult;
|
|
67863
68267
|
}
|
|
67864
68268
|
if (slashCommand?.type === "control") {
|
|
67865
68269
|
if (slashCommand.name === "start" || slashCommand.name === "status") {
|
|
@@ -67871,6 +68275,7 @@ async function processChannelInteraction(params) {
|
|
|
67871
68275
|
await params.postText(renderRouteStatusMessage({
|
|
67872
68276
|
identity: params.identity,
|
|
67873
68277
|
route: params.route,
|
|
68278
|
+
auth,
|
|
67874
68279
|
sessionTarget: params.sessionTarget,
|
|
67875
68280
|
followUpState,
|
|
67876
68281
|
runtimeState,
|
|
@@ -67880,23 +68285,29 @@ async function processChannelInteraction(params) {
|
|
|
67880
68285
|
}
|
|
67881
68286
|
}));
|
|
67882
68287
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67883
|
-
return;
|
|
68288
|
+
return interactionResult;
|
|
67884
68289
|
}
|
|
67885
68290
|
if (slashCommand.name === "help") {
|
|
67886
68291
|
await params.postText(renderAgentControlSlashHelp());
|
|
67887
68292
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67888
|
-
return;
|
|
68293
|
+
return interactionResult;
|
|
67889
68294
|
}
|
|
67890
68295
|
if (slashCommand.name === "whoami") {
|
|
67891
68296
|
await params.postText(renderWhoAmIMessage({
|
|
67892
68297
|
identity: params.identity,
|
|
67893
68298
|
route: params.route,
|
|
68299
|
+
auth,
|
|
67894
68300
|
sessionTarget: params.sessionTarget
|
|
67895
68301
|
}));
|
|
67896
68302
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67897
|
-
return;
|
|
68303
|
+
return interactionResult;
|
|
67898
68304
|
}
|
|
67899
68305
|
if (slashCommand.name === "transcript") {
|
|
68306
|
+
if (params.route.verbose === "off") {
|
|
68307
|
+
await params.postText(renderTranscriptDisabledMessage());
|
|
68308
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68309
|
+
return interactionResult;
|
|
68310
|
+
}
|
|
67900
68311
|
const transcript = await params.agentService.captureTranscript(params.sessionTarget);
|
|
67901
68312
|
await params.postText(renderChannelSnapshot({
|
|
67902
68313
|
agentId: transcript.agentId,
|
|
@@ -67907,20 +68318,20 @@ async function processChannelInteraction(params) {
|
|
|
67907
68318
|
maxChars: params.maxChars,
|
|
67908
68319
|
note: "transcript command"
|
|
67909
68320
|
}));
|
|
67910
|
-
return;
|
|
68321
|
+
return interactionResult;
|
|
67911
68322
|
}
|
|
67912
68323
|
if (slashCommand.name === "attach") {
|
|
67913
68324
|
const observation = await params.agentService.observeRun(params.sessionTarget, buildRunObserver({
|
|
67914
68325
|
mode: "live"
|
|
67915
68326
|
}));
|
|
67916
68327
|
await applyRunUpdate(observation.update);
|
|
67917
|
-
return;
|
|
68328
|
+
return interactionResult;
|
|
67918
68329
|
}
|
|
67919
68330
|
if (slashCommand.name === "detach") {
|
|
67920
68331
|
const detached = await params.agentService.detachRunObserver(params.sessionTarget, observerId);
|
|
67921
68332
|
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.");
|
|
67922
68333
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67923
|
-
return;
|
|
68334
|
+
return interactionResult;
|
|
67924
68335
|
}
|
|
67925
68336
|
if (slashCommand.name === "watch") {
|
|
67926
68337
|
const observation = await params.agentService.observeRun(params.sessionTarget, buildRunObserver({
|
|
@@ -67929,19 +68340,19 @@ async function processChannelInteraction(params) {
|
|
|
67929
68340
|
durationMs: slashCommand.durationMs
|
|
67930
68341
|
}));
|
|
67931
68342
|
await applyRunUpdate(observation.update);
|
|
67932
|
-
return;
|
|
68343
|
+
return interactionResult;
|
|
67933
68344
|
}
|
|
67934
68345
|
if (slashCommand.name === "stop") {
|
|
67935
68346
|
const stopped = await params.agentService.interruptSession(params.sessionTarget);
|
|
67936
68347
|
await params.postText(stopped.interrupted ? `Interrupted agent \`${stopped.agentId}\` session \`${stopped.sessionName}\`.` : `Agent \`${stopped.agentId}\` session \`${stopped.sessionName}\` was not running.`);
|
|
67937
68348
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67938
|
-
return;
|
|
68349
|
+
return interactionResult;
|
|
67939
68350
|
}
|
|
67940
68351
|
if (slashCommand.name === "nudge") {
|
|
67941
68352
|
const nudged = await params.agentService.nudgeSession(params.sessionTarget);
|
|
67942
68353
|
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}\`.`);
|
|
67943
68354
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67944
|
-
return;
|
|
68355
|
+
return interactionResult;
|
|
67945
68356
|
}
|
|
67946
68357
|
if (slashCommand.name === "followup") {
|
|
67947
68358
|
if (slashCommand.action === "status") {
|
|
@@ -67960,7 +68371,32 @@ async function processChannelInteraction(params) {
|
|
|
67960
68371
|
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.`);
|
|
67961
68372
|
}
|
|
67962
68373
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67963
|
-
return;
|
|
68374
|
+
return interactionResult;
|
|
68375
|
+
}
|
|
68376
|
+
if (slashCommand.name === "streaming") {
|
|
68377
|
+
if (slashCommand.action === "status") {
|
|
68378
|
+
const persisted = await getConversationStreaming({
|
|
68379
|
+
identity: params.identity
|
|
68380
|
+
});
|
|
68381
|
+
await params.postText(renderStreamingStatusMessage({
|
|
68382
|
+
route: params.route,
|
|
68383
|
+
persisted
|
|
68384
|
+
}));
|
|
68385
|
+
} else if (slashCommand.streaming) {
|
|
68386
|
+
const persisted = await setConversationStreaming({
|
|
68387
|
+
identity: params.identity,
|
|
68388
|
+
streaming: slashCommand.streaming
|
|
68389
|
+
});
|
|
68390
|
+
await params.postText([
|
|
68391
|
+
`Updated streaming mode for \`${persisted.label}\`.`,
|
|
68392
|
+
`config.streaming: \`${persisted.streaming}\``,
|
|
68393
|
+
`config: \`${persisted.configPath}\``,
|
|
68394
|
+
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."
|
|
68395
|
+
].join(`
|
|
68396
|
+
`));
|
|
68397
|
+
}
|
|
68398
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68399
|
+
return interactionResult;
|
|
67964
68400
|
}
|
|
67965
68401
|
if (slashCommand.name === "responsemode") {
|
|
67966
68402
|
if (slashCommand.action === "status") {
|
|
@@ -67985,7 +68421,7 @@ async function processChannelInteraction(params) {
|
|
|
67985
68421
|
`));
|
|
67986
68422
|
}
|
|
67987
68423
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
67988
|
-
return;
|
|
68424
|
+
return interactionResult;
|
|
67989
68425
|
}
|
|
67990
68426
|
if (slashCommand.name === "additionalmessagemode") {
|
|
67991
68427
|
if (slashCommand.action === "status") {
|
|
@@ -68010,19 +68446,19 @@ async function processChannelInteraction(params) {
|
|
|
68010
68446
|
`));
|
|
68011
68447
|
}
|
|
68012
68448
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68013
|
-
return;
|
|
68449
|
+
return interactionResult;
|
|
68014
68450
|
}
|
|
68015
68451
|
if (slashCommand.name === "queue-list") {
|
|
68016
68452
|
const queuedItems = params.agentService.listQueuedPrompts?.(params.sessionTarget) ?? [];
|
|
68017
68453
|
await params.postText(renderQueuedMessagesList(queuedItems));
|
|
68018
68454
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68019
|
-
return;
|
|
68455
|
+
return interactionResult;
|
|
68020
68456
|
}
|
|
68021
68457
|
if (slashCommand.name === "queue-clear") {
|
|
68022
68458
|
const clearedCount = params.agentService.clearQueuedPrompts?.(params.sessionTarget) ?? 0;
|
|
68023
68459
|
await params.postText(clearedCount > 0 ? `Cleared ${clearedCount} queued message${clearedCount === 1 ? "" : "s"}.` : "Queue was already empty.");
|
|
68024
68460
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68025
|
-
return;
|
|
68461
|
+
return interactionResult;
|
|
68026
68462
|
}
|
|
68027
68463
|
}
|
|
68028
68464
|
if (slashCommand?.type === "loop-control") {
|
|
@@ -68034,7 +68470,7 @@ async function processChannelInteraction(params) {
|
|
|
68034
68470
|
globalLoopCount: params.agentService.getActiveIntervalLoopCount?.() ?? 0
|
|
68035
68471
|
}));
|
|
68036
68472
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68037
|
-
return;
|
|
68473
|
+
return interactionResult;
|
|
68038
68474
|
}
|
|
68039
68475
|
const sessionLoops = params.agentService.listIntervalLoops?.({
|
|
68040
68476
|
sessionKey: params.sessionTarget.sessionKey
|
|
@@ -68043,31 +68479,31 @@ async function processChannelInteraction(params) {
|
|
|
68043
68479
|
const cancelled2 = await params.agentService.cancelAllIntervalLoops();
|
|
68044
68480
|
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.");
|
|
68045
68481
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68046
|
-
return;
|
|
68482
|
+
return interactionResult;
|
|
68047
68483
|
}
|
|
68048
68484
|
if (slashCommand.all) {
|
|
68049
68485
|
const cancelled2 = await params.agentService.cancelIntervalLoopsForSession(params.sessionTarget);
|
|
68050
68486
|
await params.postText(cancelled2 > 0 ? `Cancelled ${cancelled2} active loop${cancelled2 === 1 ? "" : "s"} for this session.` : "No active loops to cancel for this session.");
|
|
68051
68487
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68052
|
-
return;
|
|
68488
|
+
return interactionResult;
|
|
68053
68489
|
}
|
|
68054
68490
|
const targetLoopId = slashCommand.loopId || (sessionLoops.length === 1 ? sessionLoops[0]?.id : undefined);
|
|
68055
68491
|
if (!targetLoopId) {
|
|
68056
68492
|
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`.");
|
|
68057
68493
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68058
|
-
return;
|
|
68494
|
+
return interactionResult;
|
|
68059
68495
|
}
|
|
68060
68496
|
const cancelled = await params.agentService.cancelIntervalLoop(targetLoopId);
|
|
68061
68497
|
await params.postText(cancelled ? `Cancelled loop \`${targetLoopId}\`.` : `No active loop found with id \`${targetLoopId}\`.`);
|
|
68062
68498
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68063
|
-
return;
|
|
68499
|
+
return interactionResult;
|
|
68064
68500
|
}
|
|
68065
68501
|
if (slashCommand?.type === "loop-error") {
|
|
68066
68502
|
await params.postText(`${slashCommand.message}
|
|
68067
68503
|
|
|
68068
68504
|
${renderLoopUsage()}`);
|
|
68069
68505
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68070
|
-
return;
|
|
68506
|
+
return interactionResult;
|
|
68071
68507
|
}
|
|
68072
68508
|
if (slashCommand?.type === "loop") {
|
|
68073
68509
|
const loopConfig = params.agentService.getLoopConfig();
|
|
@@ -68078,7 +68514,7 @@ ${renderLoopUsage()}`);
|
|
|
68078
68514
|
|
|
68079
68515
|
${renderLoopUsage()}`);
|
|
68080
68516
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68081
|
-
return;
|
|
68517
|
+
return interactionResult;
|
|
68082
68518
|
}
|
|
68083
68519
|
if (slashCommand.params.mode === "interval") {
|
|
68084
68520
|
const intervalValidation = validateLoopInterval({
|
|
@@ -68090,7 +68526,7 @@ ${renderLoopUsage()}`);
|
|
|
68090
68526
|
|
|
68091
68527
|
${renderLoopUsage()}`);
|
|
68092
68528
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68093
|
-
return;
|
|
68529
|
+
return interactionResult;
|
|
68094
68530
|
}
|
|
68095
68531
|
}
|
|
68096
68532
|
let resolvedLoopPrompt;
|
|
@@ -68103,7 +68539,7 @@ ${renderLoopUsage()}`);
|
|
|
68103
68539
|
} catch (error) {
|
|
68104
68540
|
await params.postText(String(error));
|
|
68105
68541
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68106
|
-
return;
|
|
68542
|
+
return interactionResult;
|
|
68107
68543
|
}
|
|
68108
68544
|
const buildLoopPromptText = (text) => params.agentPromptBuilder ? params.agentPromptBuilder(text) : text;
|
|
68109
68545
|
if (slashCommand.params.mode === "times") {
|
|
@@ -68126,7 +68562,7 @@ ${renderLoopUsage()}`);
|
|
|
68126
68562
|
observerId: `${observerId}:loop:${index + 1}`
|
|
68127
68563
|
});
|
|
68128
68564
|
}
|
|
68129
|
-
return;
|
|
68565
|
+
return interactionResult;
|
|
68130
68566
|
}
|
|
68131
68567
|
if (slashCommand.params.mode === "calendar") {
|
|
68132
68568
|
const effectiveTimezone = resolveEffectiveLoopTimezone({
|
|
@@ -68136,8 +68572,10 @@ ${renderLoopUsage()}`);
|
|
|
68136
68572
|
const createdLoop2 = await params.agentService.createCalendarLoop({
|
|
68137
68573
|
target: params.sessionTarget,
|
|
68138
68574
|
promptText: buildLoopPromptText(resolvedLoopPrompt.text),
|
|
68575
|
+
canonicalPromptText: resolvedLoopPrompt.text,
|
|
68139
68576
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
68140
68577
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
68578
|
+
surfaceBinding: buildLoopSurfaceBinding(params.identity),
|
|
68141
68579
|
cadence: slashCommand.params.cadence,
|
|
68142
68580
|
dayOfWeek: slashCommand.params.dayOfWeek,
|
|
68143
68581
|
localTime: slashCommand.params.localTime,
|
|
@@ -68145,7 +68583,8 @@ ${renderLoopUsage()}`);
|
|
|
68145
68583
|
minute: slashCommand.params.minute,
|
|
68146
68584
|
timezone: effectiveTimezone,
|
|
68147
68585
|
maxRuns: maxRunsPerLoop,
|
|
68148
|
-
createdBy: params.senderId
|
|
68586
|
+
createdBy: params.senderId,
|
|
68587
|
+
protectedControlMutationRule: params.protectedControlMutationRule
|
|
68149
68588
|
});
|
|
68150
68589
|
await params.postText(renderLoopStartedMessage({
|
|
68151
68590
|
mode: "calendar",
|
|
@@ -68165,17 +68604,20 @@ ${renderLoopUsage()}`);
|
|
|
68165
68604
|
globalLoopCount: params.agentService.getActiveIntervalLoopCount()
|
|
68166
68605
|
}));
|
|
68167
68606
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68168
|
-
return;
|
|
68607
|
+
return interactionResult;
|
|
68169
68608
|
}
|
|
68170
68609
|
const createdLoop = await params.agentService.createIntervalLoop({
|
|
68171
68610
|
target: params.sessionTarget,
|
|
68172
68611
|
promptText: buildLoopPromptText(resolvedLoopPrompt.text),
|
|
68612
|
+
canonicalPromptText: resolvedLoopPrompt.text,
|
|
68173
68613
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
68174
68614
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
68615
|
+
surfaceBinding: buildLoopSurfaceBinding(params.identity),
|
|
68175
68616
|
intervalMs: effectiveIntervalMs,
|
|
68176
68617
|
maxRuns: maxRunsPerLoop,
|
|
68177
68618
|
createdBy: params.senderId,
|
|
68178
|
-
force: slashCommand.params.force
|
|
68619
|
+
force: slashCommand.params.force,
|
|
68620
|
+
protectedControlMutationRule: params.protectedControlMutationRule
|
|
68179
68621
|
});
|
|
68180
68622
|
await params.postText(renderLoopStartedMessage({
|
|
68181
68623
|
mode: "interval",
|
|
@@ -68193,55 +68635,59 @@ ${renderLoopUsage()}`);
|
|
|
68193
68635
|
}).warning
|
|
68194
68636
|
}));
|
|
68195
68637
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68196
|
-
return;
|
|
68638
|
+
return interactionResult;
|
|
68197
68639
|
}
|
|
68198
68640
|
if (slashCommand?.type === "bash") {
|
|
68199
68641
|
if (!slashCommand.command.trim()) {
|
|
68200
68642
|
await params.postText("Usage: `/bash <command>` or a configured bash shortcut such as `!<command>`");
|
|
68201
|
-
return;
|
|
68643
|
+
return interactionResult;
|
|
68202
68644
|
}
|
|
68203
|
-
const
|
|
68645
|
+
const shellResult = await params.agentService.runShellCommand(params.sessionTarget, slashCommand.command);
|
|
68204
68646
|
const header = [
|
|
68205
|
-
`Bash in \`${
|
|
68206
|
-
`command: \`${
|
|
68207
|
-
|
|
68647
|
+
`Bash in \`${shellResult.workspacePath}\``,
|
|
68648
|
+
`command: \`${shellResult.command}\``,
|
|
68649
|
+
shellResult.timedOut ? "exit: `124` timed out" : `exit: \`${shellResult.exitCode}\``
|
|
68208
68650
|
].join(`
|
|
68209
68651
|
`);
|
|
68210
|
-
const body =
|
|
68652
|
+
const body = shellResult.output ? `
|
|
68211
68653
|
|
|
68212
68654
|
\`\`\`text
|
|
68213
|
-
${escapeCodeFence(
|
|
68655
|
+
${escapeCodeFence(shellResult.output)}
|
|
68214
68656
|
\`\`\`` : "\n\n`(no output)`";
|
|
68215
68657
|
await params.postText(`${header}${body}`);
|
|
68216
68658
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68217
|
-
return;
|
|
68659
|
+
return interactionResult;
|
|
68218
68660
|
}
|
|
68219
68661
|
if (slashCommand?.type === "queue" && !explicitQueueMessage) {
|
|
68220
68662
|
await params.postText("Usage: `/queue <message>` or `\\q <message>`");
|
|
68221
68663
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68222
|
-
return;
|
|
68664
|
+
return interactionResult;
|
|
68223
68665
|
}
|
|
68224
68666
|
if (slashCommand?.type === "steer" && !explicitSteerMessage) {
|
|
68225
68667
|
await params.postText("Usage: `/steer <message>` or `\\s <message>`");
|
|
68226
68668
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68227
|
-
return;
|
|
68669
|
+
return interactionResult;
|
|
68228
68670
|
}
|
|
68229
68671
|
if (explicitSteerMessage) {
|
|
68230
68672
|
const hasActiveRun = params.agentService.hasActiveRun?.(params.sessionTarget) ?? false;
|
|
68231
68673
|
if (!hasActiveRun) {
|
|
68232
68674
|
await params.postText("No active run to steer.");
|
|
68233
68675
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68234
|
-
return;
|
|
68676
|
+
return interactionResult;
|
|
68235
68677
|
}
|
|
68236
|
-
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(explicitSteerMessage));
|
|
68678
|
+
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(explicitSteerMessage, params.protectedControlMutationRule));
|
|
68237
68679
|
await params.postText("Steered.");
|
|
68238
68680
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
68239
|
-
return
|
|
68681
|
+
return {
|
|
68682
|
+
processingIndicatorLifecycle: "active-run"
|
|
68683
|
+
};
|
|
68240
68684
|
}
|
|
68241
68685
|
if (!forceQueuedDelivery && params.route.additionalMessageMode === "steer") {
|
|
68242
68686
|
if (sessionBusy) {
|
|
68243
|
-
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(params.text));
|
|
68244
|
-
return
|
|
68687
|
+
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(params.text, params.protectedControlMutationRule));
|
|
68688
|
+
return {
|
|
68689
|
+
processingIndicatorLifecycle: "active-run"
|
|
68690
|
+
};
|
|
68245
68691
|
}
|
|
68246
68692
|
}
|
|
68247
68693
|
await executePromptDelivery({
|
|
@@ -68250,13 +68696,14 @@ ${escapeCodeFence(result.output)}
|
|
|
68250
68696
|
identity: params.identity,
|
|
68251
68697
|
route: params.route,
|
|
68252
68698
|
maxChars: params.maxChars,
|
|
68253
|
-
promptText:
|
|
68699
|
+
promptText: delayedPromptText,
|
|
68254
68700
|
postText: params.postText,
|
|
68255
68701
|
reconcileText: params.reconcileText,
|
|
68256
68702
|
observerId,
|
|
68257
68703
|
timingContext: params.timingContext,
|
|
68258
68704
|
forceQueuedDelivery
|
|
68259
68705
|
});
|
|
68706
|
+
return interactionResult;
|
|
68260
68707
|
}
|
|
68261
68708
|
|
|
68262
68709
|
// src/channels/pairing/access.ts
|
|
@@ -68309,132 +68756,88 @@ function isTelegramSenderAllowed(params) {
|
|
|
68309
68756
|
return false;
|
|
68310
68757
|
}
|
|
68311
68758
|
|
|
68312
|
-
// src/
|
|
68313
|
-
function
|
|
68314
|
-
|
|
68315
|
-
|
|
68759
|
+
// src/auth/resolve.ts
|
|
68760
|
+
function normalizePrincipal(principal) {
|
|
68761
|
+
const trimmed = principal.trim();
|
|
68762
|
+
if (!trimmed) {
|
|
68763
|
+
return "";
|
|
68316
68764
|
}
|
|
68317
|
-
const
|
|
68318
|
-
|
|
68319
|
-
|
|
68320
|
-
</system>
|
|
68321
|
-
|
|
68322
|
-
<user>
|
|
68323
|
-
${params.text}
|
|
68324
|
-
</user>`;
|
|
68325
|
-
}
|
|
68326
|
-
function renderAgentPromptInstruction(params) {
|
|
68327
|
-
const messageToolMode = (params.responseMode ?? "message-tool") === "message-tool";
|
|
68328
|
-
const lines = [
|
|
68329
|
-
`[${renderPromptTimestamp()}] ${renderIdentitySummary(params.identity)}`,
|
|
68330
|
-
"",
|
|
68331
|
-
"You are operating inside clisbot.",
|
|
68332
|
-
messageToolMode ? "To send a user-visible progress update or final 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"
|
|
68333
|
-
];
|
|
68334
|
-
if (messageToolMode) {
|
|
68335
|
-
const replyCommand = buildReplyCommand({
|
|
68336
|
-
command: getClisbotPromptCommand(),
|
|
68337
|
-
identity: params.identity
|
|
68338
|
-
});
|
|
68339
|
-
lines.push(replyCommand, "When replying to the user:", "- put the user-facing message inside the --message body of that command", "- use that command to send progress updates and the final reply back to the conversation", `- send at most ${params.config.maxProgressMessages} progress updates`, params.config.requireFinalResponse ? "- send exactly 1 final user-facing response" : "- final response is optional", "- keep progress updates short and meaningful", "- do not send progress updates for trivial internal steps");
|
|
68765
|
+
const [platform, userId] = trimmed.split(":", 2);
|
|
68766
|
+
if (!platform || !userId) {
|
|
68767
|
+
return trimmed;
|
|
68340
68768
|
}
|
|
68341
|
-
|
|
68342
|
-
`)
|
|
68769
|
+
if (platform === "slack") {
|
|
68770
|
+
return `slack:${userId.trim().toUpperCase()}`;
|
|
68771
|
+
}
|
|
68772
|
+
if (platform === "telegram") {
|
|
68773
|
+
return `telegram:${userId.trim()}`;
|
|
68774
|
+
}
|
|
68775
|
+
return `${platform}:${userId.trim()}`;
|
|
68343
68776
|
}
|
|
68344
|
-
function
|
|
68345
|
-
|
|
68346
|
-
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
68347
|
-
year: "numeric",
|
|
68348
|
-
month: "2-digit",
|
|
68349
|
-
day: "2-digit",
|
|
68350
|
-
hour: "2-digit",
|
|
68351
|
-
minute: "2-digit",
|
|
68352
|
-
second: "2-digit",
|
|
68353
|
-
hour12: false,
|
|
68354
|
-
timeZoneName: "shortOffset"
|
|
68355
|
-
});
|
|
68356
|
-
return formatter.format(date).replace(",", "");
|
|
68777
|
+
function normalizeRoleUsers(users) {
|
|
68778
|
+
return (users ?? []).map(normalizePrincipal).filter(Boolean);
|
|
68357
68779
|
}
|
|
68358
|
-
function
|
|
68359
|
-
const
|
|
68360
|
-
|
|
68361
|
-
|
|
68362
|
-
segments.push(sender);
|
|
68780
|
+
function resolvePrincipal(identity) {
|
|
68781
|
+
const senderId = identity.senderId?.trim();
|
|
68782
|
+
if (!senderId) {
|
|
68783
|
+
return;
|
|
68363
68784
|
}
|
|
68364
|
-
return segments.join(" | ");
|
|
68365
|
-
}
|
|
68366
|
-
function renderConversationSummary(identity) {
|
|
68367
68785
|
if (identity.platform === "slack") {
|
|
68368
|
-
|
|
68369
|
-
const segments = [scopeLabel];
|
|
68370
|
-
const channel = renderLabeledTarget(identity.channelName, identity.channelId, "#");
|
|
68371
|
-
if (channel) {
|
|
68372
|
-
segments.push(channel);
|
|
68373
|
-
}
|
|
68374
|
-
if (identity.threadTs) {
|
|
68375
|
-
segments.push(`thread ${identity.threadTs}`);
|
|
68376
|
-
}
|
|
68377
|
-
return segments.join(" ");
|
|
68378
|
-
}
|
|
68379
|
-
if (identity.conversationKind === "dm") {
|
|
68380
|
-
return ["Telegram direct message", renderLabeledTarget(identity.chatName, identity.chatId)].filter(Boolean).join(" ");
|
|
68381
|
-
}
|
|
68382
|
-
if (identity.conversationKind === "topic") {
|
|
68383
|
-
const topic = renderNamedValue("topic", identity.topicName, identity.topicId);
|
|
68384
|
-
const group = renderNamedValue("in group", identity.chatName, identity.chatId);
|
|
68385
|
-
return [topic, group].filter(Boolean).join(" ");
|
|
68786
|
+
return normalizePrincipal(`slack:${senderId}`);
|
|
68386
68787
|
}
|
|
68387
|
-
return
|
|
68388
|
-
}
|
|
68389
|
-
function renderSenderSummary(identity) {
|
|
68390
|
-
const sender = renderLabeledTarget(identity.senderName, identity.senderId);
|
|
68391
|
-
return sender ? `sender ${sender}` : "";
|
|
68788
|
+
return normalizePrincipal(`telegram:${senderId}`);
|
|
68392
68789
|
}
|
|
68393
|
-
function
|
|
68394
|
-
|
|
68395
|
-
|
|
68396
|
-
if (normalizedName && normalizedId) {
|
|
68397
|
-
return `${namePrefix}${normalizedName} (${normalizedId})`;
|
|
68790
|
+
function findExplicitRole(roles, principal) {
|
|
68791
|
+
if (!principal || !roles) {
|
|
68792
|
+
return;
|
|
68398
68793
|
}
|
|
68399
|
-
|
|
68400
|
-
|
|
68794
|
+
for (const [roleName, roleDefinition] of Object.entries(roles)) {
|
|
68795
|
+
if (normalizeRoleUsers(roleDefinition.users).includes(principal)) {
|
|
68796
|
+
return roleName;
|
|
68797
|
+
}
|
|
68401
68798
|
}
|
|
68402
|
-
return
|
|
68403
|
-
}
|
|
68404
|
-
function renderNamedValue(label, name, id) {
|
|
68405
|
-
const value = renderLabeledTarget(name, id);
|
|
68406
|
-
return value ? `${label} ${value}` : "";
|
|
68799
|
+
return;
|
|
68407
68800
|
}
|
|
68408
|
-
function
|
|
68409
|
-
const
|
|
68410
|
-
|
|
68411
|
-
|
|
68412
|
-
|
|
68413
|
-
|
|
68414
|
-
|
|
68801
|
+
function getAgentAuth(config, agentId) {
|
|
68802
|
+
const defaults = config.agents.defaults.auth;
|
|
68803
|
+
const entry = config.agents.list.find((item) => item.id === agentId);
|
|
68804
|
+
const override = entry?.auth;
|
|
68805
|
+
return {
|
|
68806
|
+
defaultRole: override?.defaultRole ?? defaults.defaultRole,
|
|
68807
|
+
roles: {
|
|
68808
|
+
...defaults.roles,
|
|
68809
|
+
...override?.roles ?? {}
|
|
68415
68810
|
}
|
|
68416
|
-
|
|
68417
|
-
|
|
68418
|
-
|
|
68419
|
-
|
|
68420
|
-
|
|
68421
|
-
|
|
68422
|
-
|
|
68423
|
-
|
|
68424
|
-
}
|
|
68425
|
-
lines.push(" --channel telegram \\");
|
|
68426
|
-
lines.push(` --target ${params.identity.chatId ?? ""} \\`);
|
|
68427
|
-
if (params.identity.topicId) {
|
|
68428
|
-
lines.push(` --thread-id ${params.identity.topicId} \\`);
|
|
68811
|
+
};
|
|
68812
|
+
}
|
|
68813
|
+
function getAllowedPermissions(roles, role) {
|
|
68814
|
+
return new Set(roles?.[role]?.allow ?? []);
|
|
68815
|
+
}
|
|
68816
|
+
function hasAppPermission(config, appRole, permission) {
|
|
68817
|
+
if (appRole === "owner") {
|
|
68818
|
+
return true;
|
|
68429
68819
|
}
|
|
68430
|
-
|
|
68431
|
-
|
|
68432
|
-
|
|
68433
|
-
|
|
68434
|
-
|
|
68435
|
-
|
|
68436
|
-
|
|
68437
|
-
|
|
68820
|
+
return getAllowedPermissions(config.app.auth.roles, appRole).has(permission);
|
|
68821
|
+
}
|
|
68822
|
+
function resolveChannelAuth(params) {
|
|
68823
|
+
const principal = resolvePrincipal(params.identity);
|
|
68824
|
+
const appAuth = params.config.app.auth;
|
|
68825
|
+
const explicitAppRole = findExplicitRole(appAuth.roles, principal);
|
|
68826
|
+
const appRole = explicitAppRole ?? appAuth.defaultRole;
|
|
68827
|
+
const appAdminLike = appRole === "owner" || appRole === "admin";
|
|
68828
|
+
const agentAuth = getAgentAuth(params.config, params.agentId);
|
|
68829
|
+
const explicitAgentRole = findExplicitRole(agentAuth.roles, principal);
|
|
68830
|
+
const agentRole = explicitAgentRole ?? agentAuth.defaultRole;
|
|
68831
|
+
const agentPermissions = getAllowedPermissions(agentAuth.roles, agentRole);
|
|
68832
|
+
const mayManageProtectedResources = appAdminLike || hasAppPermission(params.config, appRole, "configManage") || hasAppPermission(params.config, appRole, "appAuthManage") || hasAppPermission(params.config, appRole, "agentAuthManage") || hasAppPermission(params.config, appRole, "promptGovernanceManage");
|
|
68833
|
+
return {
|
|
68834
|
+
principal,
|
|
68835
|
+
appRole,
|
|
68836
|
+
agentRole,
|
|
68837
|
+
mayBypassPairing: appAdminLike,
|
|
68838
|
+
mayManageProtectedResources,
|
|
68839
|
+
canUseShell: appAdminLike || agentPermissions.has("shellExecute")
|
|
68840
|
+
};
|
|
68438
68841
|
}
|
|
68439
68842
|
|
|
68440
68843
|
// src/channels/slack/session-routing.ts
|
|
@@ -68492,9 +68895,20 @@ function resolveSlackConversationTarget(params) {
|
|
|
68492
68895
|
};
|
|
68493
68896
|
}
|
|
68494
68897
|
|
|
68898
|
+
// src/channels/privilege-commands.ts
|
|
68899
|
+
function resolvePrivilegeCommands(rootConfig, override) {
|
|
68900
|
+
return {
|
|
68901
|
+
enabled: override?.enabled ?? rootConfig.enabled,
|
|
68902
|
+
allowUsers: override?.allowUsers ?? rootConfig.allowUsers
|
|
68903
|
+
};
|
|
68904
|
+
}
|
|
68905
|
+
|
|
68495
68906
|
// src/channels/route-policy.ts
|
|
68496
68907
|
function buildSharedChannelRoute(params) {
|
|
68497
|
-
const privilegeCommands = resolvePrivilegeCommands(params.channelConfig.privilegeCommands
|
|
68908
|
+
const privilegeCommands = resolvePrivilegeCommands(params.channelConfig.privilegeCommands ?? {
|
|
68909
|
+
enabled: false,
|
|
68910
|
+
allowUsers: []
|
|
68911
|
+
}, params.route?.privilegeCommands);
|
|
68498
68912
|
const agentId = params.route?.agentId ?? resolveTopLevelBoundAgentId(params.loadedConfig, {
|
|
68499
68913
|
channel: params.channel,
|
|
68500
68914
|
accountId: params.accountId
|
|
@@ -68516,6 +68930,7 @@ function buildSharedChannelRoute(params) {
|
|
|
68516
68930
|
response: params.route?.response ?? params.channelConfig.response,
|
|
68517
68931
|
responseMode: params.route?.responseMode ?? agentEntry?.responseMode ?? params.channelConfig.responseMode,
|
|
68518
68932
|
additionalMessageMode: params.route?.additionalMessageMode ?? agentEntry?.additionalMessageMode ?? params.channelConfig.additionalMessageMode,
|
|
68933
|
+
verbose: params.route?.verbose ?? params.channelConfig.verbose,
|
|
68519
68934
|
followUp: {
|
|
68520
68935
|
mode: params.route?.followUp?.mode ?? params.channelConfig.followUp.mode,
|
|
68521
68936
|
participationTtlMs: resolveConfigDurationMs({
|
|
@@ -68696,6 +69111,178 @@ async function clearSlackAssistantThreadStatus(client, target) {
|
|
|
68696
69111
|
}
|
|
68697
69112
|
}
|
|
68698
69113
|
|
|
69114
|
+
// src/channels/processing-indicator.ts
|
|
69115
|
+
function shouldResolveIndicatorWait(update) {
|
|
69116
|
+
return isTerminalRunStatus(update.status) || update.status === "detached";
|
|
69117
|
+
}
|
|
69118
|
+
async function waitForProcessingIndicatorLifecycle(params) {
|
|
69119
|
+
if (params.lifecycle !== "active-run") {
|
|
69120
|
+
return;
|
|
69121
|
+
}
|
|
69122
|
+
if (!params.agentService.hasActiveRun(params.sessionTarget)) {
|
|
69123
|
+
return;
|
|
69124
|
+
}
|
|
69125
|
+
let settled = false;
|
|
69126
|
+
const settle = () => {
|
|
69127
|
+
if (settled) {
|
|
69128
|
+
return;
|
|
69129
|
+
}
|
|
69130
|
+
settled = true;
|
|
69131
|
+
};
|
|
69132
|
+
try {
|
|
69133
|
+
await new Promise(async (resolve, reject) => {
|
|
69134
|
+
const resolveOnce = () => {
|
|
69135
|
+
if (settled) {
|
|
69136
|
+
return;
|
|
69137
|
+
}
|
|
69138
|
+
settled = true;
|
|
69139
|
+
resolve();
|
|
69140
|
+
};
|
|
69141
|
+
try {
|
|
69142
|
+
const observation = await params.agentService.observeRun(params.sessionTarget, {
|
|
69143
|
+
id: params.observerId,
|
|
69144
|
+
mode: "live",
|
|
69145
|
+
onUpdate: async (update) => {
|
|
69146
|
+
if (shouldResolveIndicatorWait(update)) {
|
|
69147
|
+
resolveOnce();
|
|
69148
|
+
}
|
|
69149
|
+
}
|
|
69150
|
+
});
|
|
69151
|
+
if (!observation.active || shouldResolveIndicatorWait(observation.update)) {
|
|
69152
|
+
resolveOnce();
|
|
69153
|
+
}
|
|
69154
|
+
} catch (error) {
|
|
69155
|
+
reject(error);
|
|
69156
|
+
}
|
|
69157
|
+
});
|
|
69158
|
+
} finally {
|
|
69159
|
+
settle();
|
|
69160
|
+
await params.agentService.detachRunObserver(params.sessionTarget, params.observerId).catch(() => {
|
|
69161
|
+
return;
|
|
69162
|
+
});
|
|
69163
|
+
}
|
|
69164
|
+
}
|
|
69165
|
+
|
|
69166
|
+
class ConversationProcessingIndicatorCoordinator {
|
|
69167
|
+
entries = new Map;
|
|
69168
|
+
async acquire(params) {
|
|
69169
|
+
let entry = this.entries.get(params.key);
|
|
69170
|
+
if (!entry) {
|
|
69171
|
+
entry = {
|
|
69172
|
+
activeRunHold: false,
|
|
69173
|
+
indicatorActive: false,
|
|
69174
|
+
key: params.key,
|
|
69175
|
+
operationChain: Promise.resolve(),
|
|
69176
|
+
refCount: 0
|
|
69177
|
+
};
|
|
69178
|
+
this.entries.set(params.key, entry);
|
|
69179
|
+
}
|
|
69180
|
+
entry.refCount += 1;
|
|
69181
|
+
await this.ensureIndicatorActive(entry, params.activate, params.onError);
|
|
69182
|
+
let released = false;
|
|
69183
|
+
return {
|
|
69184
|
+
setLifecycle: async (lifecycleParams) => {
|
|
69185
|
+
if (released || lifecycleParams.lifecycle !== "active-run" || entry.activeRunHold) {
|
|
69186
|
+
return;
|
|
69187
|
+
}
|
|
69188
|
+
entry.activeRunHold = true;
|
|
69189
|
+
entry.activeRunWait = waitForProcessingIndicatorLifecycle(lifecycleParams).catch((error) => {
|
|
69190
|
+
params.onError?.("active-run", error);
|
|
69191
|
+
}).finally(() => {
|
|
69192
|
+
entry.activeRunHold = false;
|
|
69193
|
+
entry.activeRunWait = undefined;
|
|
69194
|
+
this.maybeDeactivate(entry, params.onError);
|
|
69195
|
+
});
|
|
69196
|
+
},
|
|
69197
|
+
release: async () => {
|
|
69198
|
+
if (released) {
|
|
69199
|
+
return;
|
|
69200
|
+
}
|
|
69201
|
+
released = true;
|
|
69202
|
+
entry.refCount = Math.max(0, entry.refCount - 1);
|
|
69203
|
+
await this.maybeDeactivate(entry, params.onError);
|
|
69204
|
+
}
|
|
69205
|
+
};
|
|
69206
|
+
}
|
|
69207
|
+
async ensureIndicatorActive(entry, activate, onError) {
|
|
69208
|
+
entry.operationChain = entry.operationChain.then(async () => {
|
|
69209
|
+
if (entry.indicatorActive) {
|
|
69210
|
+
return;
|
|
69211
|
+
}
|
|
69212
|
+
try {
|
|
69213
|
+
const cleanup = await activate();
|
|
69214
|
+
entry.cleanup = typeof cleanup === "function" ? cleanup : undefined;
|
|
69215
|
+
entry.indicatorActive = true;
|
|
69216
|
+
} catch (error) {
|
|
69217
|
+
onError?.("activate", error);
|
|
69218
|
+
}
|
|
69219
|
+
});
|
|
69220
|
+
await entry.operationChain;
|
|
69221
|
+
}
|
|
69222
|
+
async maybeDeactivate(entry, onError) {
|
|
69223
|
+
if (entry.refCount > 0 || entry.activeRunHold) {
|
|
69224
|
+
return;
|
|
69225
|
+
}
|
|
69226
|
+
entry.operationChain = entry.operationChain.then(async () => {
|
|
69227
|
+
if (entry.refCount > 0 || entry.activeRunHold || !entry.indicatorActive) {
|
|
69228
|
+
return;
|
|
69229
|
+
}
|
|
69230
|
+
try {
|
|
69231
|
+
await entry.cleanup?.();
|
|
69232
|
+
} catch (error) {
|
|
69233
|
+
onError?.("deactivate", error);
|
|
69234
|
+
} finally {
|
|
69235
|
+
entry.cleanup = undefined;
|
|
69236
|
+
entry.indicatorActive = false;
|
|
69237
|
+
if (entry.refCount === 0 && !entry.activeRunHold) {
|
|
69238
|
+
this.entries.delete(entry.key);
|
|
69239
|
+
}
|
|
69240
|
+
}
|
|
69241
|
+
});
|
|
69242
|
+
await entry.operationChain;
|
|
69243
|
+
}
|
|
69244
|
+
}
|
|
69245
|
+
|
|
69246
|
+
// src/channels/slack/processing-decoration.ts
|
|
69247
|
+
async function activateSlackProcessingDecoration(params) {
|
|
69248
|
+
const [reactionResult, statusResult] = await Promise.allSettled([
|
|
69249
|
+
params.addReaction(),
|
|
69250
|
+
params.setStatus()
|
|
69251
|
+
]);
|
|
69252
|
+
const reactionApplied = reactionResult.status === "fulfilled" ? reactionResult.value === true : false;
|
|
69253
|
+
const statusApplied = statusResult.status === "fulfilled" ? statusResult.value === true : false;
|
|
69254
|
+
if (reactionResult.status === "rejected") {
|
|
69255
|
+
params.onUnexpectedError?.("add-reaction", reactionResult.reason);
|
|
69256
|
+
}
|
|
69257
|
+
if (statusResult.status === "rejected") {
|
|
69258
|
+
params.onUnexpectedError?.("set-status", statusResult.reason);
|
|
69259
|
+
}
|
|
69260
|
+
if (!reactionApplied && !statusApplied) {
|
|
69261
|
+
if (reactionResult.status === "rejected") {
|
|
69262
|
+
throw reactionResult.reason;
|
|
69263
|
+
}
|
|
69264
|
+
if (statusResult.status === "rejected") {
|
|
69265
|
+
throw statusResult.reason;
|
|
69266
|
+
}
|
|
69267
|
+
}
|
|
69268
|
+
return async () => {
|
|
69269
|
+
if (reactionApplied) {
|
|
69270
|
+
try {
|
|
69271
|
+
await params.removeReaction();
|
|
69272
|
+
} catch (error) {
|
|
69273
|
+
params.onUnexpectedError?.("remove-reaction", error);
|
|
69274
|
+
}
|
|
69275
|
+
}
|
|
69276
|
+
if (statusApplied) {
|
|
69277
|
+
try {
|
|
69278
|
+
await params.clearStatus();
|
|
69279
|
+
} catch (error) {
|
|
69280
|
+
params.onUnexpectedError?.("clear-status", error);
|
|
69281
|
+
}
|
|
69282
|
+
}
|
|
69283
|
+
};
|
|
69284
|
+
}
|
|
69285
|
+
|
|
68699
69286
|
// src/channels/slack/bolt-compat.ts
|
|
68700
69287
|
var SlackBolt = __toESM(require_dist7(), 1);
|
|
68701
69288
|
var { App } = SlackBolt;
|
|
@@ -68863,7 +69450,7 @@ async function downloadRemoteBuffer(params) {
|
|
|
68863
69450
|
|
|
68864
69451
|
// src/agents/attachments/storage.ts
|
|
68865
69452
|
import { access as access2 } from "node:fs/promises";
|
|
68866
|
-
import { extname, join as
|
|
69453
|
+
import { extname, join as join9 } from "node:path";
|
|
68867
69454
|
var CONTENT_TYPE_EXTENSION_MAP = {
|
|
68868
69455
|
"application/json": ".json",
|
|
68869
69456
|
"application/pdf": ".pdf",
|
|
@@ -68896,12 +69483,12 @@ function buildAttachmentFilename(params) {
|
|
|
68896
69483
|
async function resolveUniquePath(directoryPath, fileName) {
|
|
68897
69484
|
const extension = extname(fileName);
|
|
68898
69485
|
const baseName = extension ? fileName.slice(0, -extension.length) : fileName;
|
|
68899
|
-
let candidatePath =
|
|
69486
|
+
let candidatePath = join9(directoryPath, fileName);
|
|
68900
69487
|
let index = 2;
|
|
68901
69488
|
while (true) {
|
|
68902
69489
|
try {
|
|
68903
69490
|
await access2(candidatePath);
|
|
68904
|
-
candidatePath =
|
|
69491
|
+
candidatePath = join9(directoryPath, `${baseName}-${index}${extension}`);
|
|
68905
69492
|
index += 1;
|
|
68906
69493
|
} catch {
|
|
68907
69494
|
return candidatePath;
|
|
@@ -68909,7 +69496,7 @@ async function resolveUniquePath(directoryPath, fileName) {
|
|
|
68909
69496
|
}
|
|
68910
69497
|
}
|
|
68911
69498
|
async function saveWorkspaceAttachment(params) {
|
|
68912
|
-
const attachmentDir =
|
|
69499
|
+
const attachmentDir = join9(params.workspacePath, ".attachments", sanitizeSessionName(params.sessionKey), sanitizeSessionName(params.messageId));
|
|
68913
69500
|
await ensureDir2(attachmentDir);
|
|
68914
69501
|
const fileName = buildAttachmentFilename({
|
|
68915
69502
|
originalFilename: params.originalFilename,
|
|
@@ -69299,6 +69886,7 @@ class SlackSocketService {
|
|
|
69299
69886
|
accountId;
|
|
69300
69887
|
accountConfig;
|
|
69301
69888
|
app;
|
|
69889
|
+
processingIndicators = new ConversationProcessingIndicatorCoordinator;
|
|
69302
69890
|
botUserId = "";
|
|
69303
69891
|
botLabel = "";
|
|
69304
69892
|
teamId = "";
|
|
@@ -69442,12 +70030,22 @@ class SlackSocketService {
|
|
|
69442
70030
|
if (params.conversationKind === "dm") {
|
|
69443
70031
|
const directUserId = typeof event.user === "string" ? event.user.trim() : "";
|
|
69444
70032
|
const dmConfig = this.loadedConfig.raw.channels.slack.directMessages;
|
|
70033
|
+
const auth2 = resolveChannelAuth({
|
|
70034
|
+
config: this.loadedConfig.raw,
|
|
70035
|
+
agentId: params.route.agentId,
|
|
70036
|
+
identity: {
|
|
70037
|
+
platform: "slack",
|
|
70038
|
+
conversationKind: params.conversationKind,
|
|
70039
|
+
senderId: directUserId || undefined,
|
|
70040
|
+
channelId
|
|
70041
|
+
}
|
|
70042
|
+
});
|
|
69445
70043
|
if (!directUserId || dmConfig.policy === "disabled") {
|
|
69446
70044
|
debugSlackEvent("drop-dm-disabled", { eventId, directUserId });
|
|
69447
70045
|
await this.processedEventsStore.markCompleted(eventId);
|
|
69448
70046
|
return;
|
|
69449
70047
|
}
|
|
69450
|
-
if (dmConfig.policy !== "open") {
|
|
70048
|
+
if (dmConfig.policy !== "open" && !auth2.mayBypassPairing) {
|
|
69451
70049
|
const storedAllowFrom = await readChannelAllowFromStore("slack");
|
|
69452
70050
|
const allowed = isSlackSenderAllowed({
|
|
69453
70051
|
allowFrom: [...dmConfig.allowFrom, ...storedAllowFrom],
|
|
@@ -69559,18 +70157,27 @@ class SlackSocketService {
|
|
|
69559
70157
|
};
|
|
69560
70158
|
let responseChunks = [];
|
|
69561
70159
|
const cliTool = getAgentEntry(this.loadedConfig, params.route.agentId)?.cliTool;
|
|
70160
|
+
const identity = {
|
|
70161
|
+
platform: "slack",
|
|
70162
|
+
conversationKind: params.conversationKind,
|
|
70163
|
+
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
70164
|
+
channelId,
|
|
70165
|
+
threadTs
|
|
70166
|
+
};
|
|
70167
|
+
const auth = resolveChannelAuth({
|
|
70168
|
+
config: this.loadedConfig.raw,
|
|
70169
|
+
agentId: params.route.agentId,
|
|
70170
|
+
identity
|
|
70171
|
+
});
|
|
70172
|
+
const protectedControlMutationRule = auth.mayManageProtectedResources ? undefined : DEFAULT_PROTECTED_CONTROL_RULE;
|
|
69562
70173
|
const agentPromptText = buildAgentPromptText({
|
|
69563
70174
|
text,
|
|
69564
|
-
identity
|
|
69565
|
-
platform: "slack",
|
|
69566
|
-
conversationKind: params.conversationKind,
|
|
69567
|
-
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
69568
|
-
channelId,
|
|
69569
|
-
threadTs
|
|
69570
|
-
},
|
|
70175
|
+
identity,
|
|
69571
70176
|
config: this.loadedConfig.raw.channels.slack.agentPrompt,
|
|
69572
70177
|
cliTool,
|
|
69573
|
-
responseMode: params.route.responseMode
|
|
70178
|
+
responseMode: params.route.responseMode,
|
|
70179
|
+
streaming: params.route.streaming,
|
|
70180
|
+
protectedControlMutationRule
|
|
69574
70181
|
});
|
|
69575
70182
|
const timingContext = {
|
|
69576
70183
|
platform: "slack",
|
|
@@ -69586,40 +70193,46 @@ class SlackSocketService {
|
|
|
69586
70193
|
accountId: this.accountId
|
|
69587
70194
|
});
|
|
69588
70195
|
const ackReactionTask = waitForBackgroundSlackTask(addConfiguredReaction(this.app.client, this.loadedConfig.raw.channels.slack.ackReaction, reactionTarget));
|
|
69589
|
-
const
|
|
69590
|
-
|
|
69591
|
-
|
|
69592
|
-
|
|
69593
|
-
|
|
69594
|
-
|
|
69595
|
-
|
|
70196
|
+
const processingLease = await this.processingIndicators.acquire({
|
|
70197
|
+
key: `slack:${this.accountId}:${channelId}:${threadTs}`,
|
|
70198
|
+
activate: async () => activateSlackProcessingDecoration({
|
|
70199
|
+
addReaction: () => addConfiguredReaction(this.app.client, this.loadedConfig.raw.channels.slack.typingReaction, reactionTarget),
|
|
70200
|
+
removeReaction: () => removeConfiguredReaction(this.app.client, this.loadedConfig.raw.channels.slack.typingReaction, reactionTarget),
|
|
70201
|
+
setStatus: () => setSlackAssistantThreadStatus(this.app.client, this.loadedConfig.raw.channels.slack.processingStatus, {
|
|
70202
|
+
channel: channelId,
|
|
70203
|
+
threadTs
|
|
70204
|
+
}),
|
|
70205
|
+
clearStatus: () => clearSlackAssistantThreadStatus(this.app.client, {
|
|
70206
|
+
channel: channelId,
|
|
70207
|
+
threadTs
|
|
70208
|
+
}),
|
|
70209
|
+
onUnexpectedError: (phase, error) => {
|
|
70210
|
+
console.error(`slack processing indicator ${phase} failed`, error);
|
|
70211
|
+
}
|
|
70212
|
+
}),
|
|
70213
|
+
onError: (phase, error) => {
|
|
70214
|
+
console.error(`slack processing indicator ${phase} failed`, error);
|
|
70215
|
+
}
|
|
70216
|
+
});
|
|
69596
70217
|
try {
|
|
69597
|
-
await processChannelInteraction({
|
|
70218
|
+
const interaction = await processChannelInteraction({
|
|
69598
70219
|
agentService: this.agentService,
|
|
69599
70220
|
sessionTarget,
|
|
69600
|
-
identity
|
|
69601
|
-
|
|
69602
|
-
conversationKind: params.conversationKind,
|
|
69603
|
-
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
69604
|
-
channelId,
|
|
69605
|
-
threadTs
|
|
69606
|
-
},
|
|
70221
|
+
identity,
|
|
70222
|
+
auth,
|
|
69607
70223
|
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
69608
70224
|
text,
|
|
69609
70225
|
agentPromptText,
|
|
69610
70226
|
agentPromptBuilder: (nextText) => buildAgentPromptText({
|
|
69611
70227
|
text: nextText,
|
|
69612
|
-
identity
|
|
69613
|
-
platform: "slack",
|
|
69614
|
-
conversationKind: params.conversationKind,
|
|
69615
|
-
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
69616
|
-
channelId,
|
|
69617
|
-
threadTs
|
|
69618
|
-
},
|
|
70228
|
+
identity,
|
|
69619
70229
|
config: this.loadedConfig.raw.channels.slack.agentPrompt,
|
|
69620
70230
|
cliTool,
|
|
69621
|
-
responseMode: params.route.responseMode
|
|
70231
|
+
responseMode: params.route.responseMode,
|
|
70232
|
+
streaming: params.route.streaming,
|
|
70233
|
+
protectedControlMutationRule
|
|
69622
70234
|
}),
|
|
70235
|
+
protectedControlMutationRule,
|
|
69623
70236
|
route: params.route,
|
|
69624
70237
|
maxChars: this.getSlackMaxChars(params.route.agentId),
|
|
69625
70238
|
timingContext,
|
|
@@ -69641,6 +70254,12 @@ class SlackSocketService {
|
|
|
69641
70254
|
return responseChunks;
|
|
69642
70255
|
}
|
|
69643
70256
|
});
|
|
70257
|
+
await processingLease.setLifecycle({
|
|
70258
|
+
agentService: this.agentService,
|
|
70259
|
+
sessionTarget,
|
|
70260
|
+
observerId: `slack-processing:${channelId}:${threadTs}`,
|
|
70261
|
+
lifecycle: interaction.processingIndicatorLifecycle
|
|
70262
|
+
});
|
|
69644
70263
|
await this.processedEventsStore.markCompleted(eventId);
|
|
69645
70264
|
} catch (error) {
|
|
69646
70265
|
console.error("slack handler error", error);
|
|
@@ -69648,12 +70267,7 @@ class SlackSocketService {
|
|
|
69648
70267
|
return;
|
|
69649
70268
|
} finally {
|
|
69650
70269
|
await ackReactionTask;
|
|
69651
|
-
await
|
|
69652
|
-
await removeConfiguredReaction(this.app.client, this.loadedConfig.raw.channels.slack.typingReaction, reactionTarget);
|
|
69653
|
-
await clearSlackAssistantThreadStatus(this.app.client, {
|
|
69654
|
-
channel: channelId,
|
|
69655
|
-
threadTs
|
|
69656
|
-
});
|
|
70270
|
+
await processingLease.release();
|
|
69657
70271
|
}
|
|
69658
70272
|
}
|
|
69659
70273
|
registerEvents() {
|
|
@@ -70613,22 +71227,17 @@ function startTelegramTypingHeartbeat(params) {
|
|
|
70613
71227
|
}
|
|
70614
71228
|
};
|
|
70615
71229
|
}
|
|
70616
|
-
async function
|
|
71230
|
+
async function beginTelegramTypingHeartbeat(params) {
|
|
70617
71231
|
try {
|
|
70618
71232
|
await params.sendTyping();
|
|
70619
71233
|
} catch (error) {
|
|
70620
71234
|
logTelegramTypingError(params.onError, error);
|
|
70621
71235
|
}
|
|
70622
|
-
|
|
71236
|
+
return startTelegramTypingHeartbeat({
|
|
70623
71237
|
sendTyping: params.sendTyping,
|
|
70624
71238
|
intervalMs: params.intervalMs ?? TELEGRAM_TYPING_HEARTBEAT_MS,
|
|
70625
71239
|
onError: params.onError
|
|
70626
71240
|
});
|
|
70627
|
-
try {
|
|
70628
|
-
return await params.run();
|
|
70629
|
-
} finally {
|
|
70630
|
-
stopHeartbeat();
|
|
70631
|
-
}
|
|
70632
71241
|
}
|
|
70633
71242
|
|
|
70634
71243
|
// src/channels/telegram/service.ts
|
|
@@ -70647,6 +71256,7 @@ var TELEGRAM_FULL_COMMANDS = [
|
|
|
70647
71256
|
{ command: "stop", description: "Interrupt current run" },
|
|
70648
71257
|
{ command: "nudge", description: "Send one extra Enter to the session" },
|
|
70649
71258
|
{ command: "followup", description: "Show or change follow-up mode" },
|
|
71259
|
+
{ command: "streaming", description: "Show or change streaming mode" },
|
|
70650
71260
|
{ command: "responsemode", description: "Show or change response mode" },
|
|
70651
71261
|
{ command: "queue", description: "Queue a later message behind the active run" },
|
|
70652
71262
|
{ command: "steer", description: "Steer the active run immediately" },
|
|
@@ -70734,6 +71344,7 @@ class TelegramPollingService {
|
|
|
70734
71344
|
loopPromise;
|
|
70735
71345
|
activePollController;
|
|
70736
71346
|
inFlightUpdates = new Set;
|
|
71347
|
+
processingIndicators = new ConversationProcessingIndicatorCoordinator;
|
|
70737
71348
|
constructor(loadedConfig, agentService, processedEventsStore, activityStore, accountId = "default", accountConfig) {
|
|
70738
71349
|
this.loadedConfig = loadedConfig;
|
|
70739
71350
|
this.agentService = agentService;
|
|
@@ -70874,11 +71485,21 @@ class TelegramPollingService {
|
|
|
70874
71485
|
const directMessages = this.loadedConfig.raw.channels.telegram.directMessages;
|
|
70875
71486
|
const senderId = message.from?.id != null ? String(message.from.id) : "";
|
|
70876
71487
|
const senderUsername = message.from?.username;
|
|
71488
|
+
const auth = resolveChannelAuth({
|
|
71489
|
+
config: this.loadedConfig.raw,
|
|
71490
|
+
agentId: routeInfo.route.agentId,
|
|
71491
|
+
identity: {
|
|
71492
|
+
platform: "telegram",
|
|
71493
|
+
conversationKind: routeInfo.conversationKind,
|
|
71494
|
+
senderId: senderId || undefined,
|
|
71495
|
+
chatId: String(message.chat.id)
|
|
71496
|
+
}
|
|
71497
|
+
});
|
|
70877
71498
|
if (!senderId || directMessages.policy === "disabled") {
|
|
70878
71499
|
await this.processedEventsStore.markCompleted(eventId);
|
|
70879
71500
|
return;
|
|
70880
71501
|
}
|
|
70881
|
-
if (directMessages.policy !== "open") {
|
|
71502
|
+
if (directMessages.policy !== "open" && !auth.mayBypassPairing) {
|
|
70882
71503
|
const storedAllowFrom = await readChannelAllowFromStore("telegram");
|
|
70883
71504
|
const allowed = isTelegramSenderAllowed({
|
|
70884
71505
|
allowFrom: [...directMessages.allowFrom, ...storedAllowFrom],
|
|
@@ -70968,12 +71589,20 @@ class TelegramPollingService {
|
|
|
70968
71589
|
topicId: routeInfo.topicId != null ? String(routeInfo.topicId) : undefined
|
|
70969
71590
|
};
|
|
70970
71591
|
const cliTool = getAgentEntry(this.loadedConfig, routeInfo.route.agentId)?.cliTool;
|
|
71592
|
+
const auth = resolveChannelAuth({
|
|
71593
|
+
config: this.loadedConfig.raw,
|
|
71594
|
+
agentId: routeInfo.route.agentId,
|
|
71595
|
+
identity
|
|
71596
|
+
});
|
|
71597
|
+
const protectedControlMutationRule = auth.mayManageProtectedResources ? undefined : DEFAULT_PROTECTED_CONTROL_RULE;
|
|
70971
71598
|
const agentPromptText = buildAgentPromptText({
|
|
70972
71599
|
text,
|
|
70973
71600
|
identity,
|
|
70974
71601
|
config: this.loadedConfig.raw.channels.telegram.agentPrompt,
|
|
70975
71602
|
cliTool,
|
|
70976
|
-
responseMode: routeInfo.route.responseMode
|
|
71603
|
+
responseMode: routeInfo.route.responseMode,
|
|
71604
|
+
streaming: routeInfo.route.streaming,
|
|
71605
|
+
protectedControlMutationRule
|
|
70977
71606
|
});
|
|
70978
71607
|
const timingContext = {
|
|
70979
71608
|
platform: "telegram",
|
|
@@ -70988,53 +71617,71 @@ class TelegramPollingService {
|
|
|
70988
71617
|
responseMode: routeInfo.route.responseMode,
|
|
70989
71618
|
accountId: this.accountId
|
|
70990
71619
|
});
|
|
70991
|
-
await
|
|
70992
|
-
|
|
70993
|
-
|
|
70994
|
-
|
|
70995
|
-
|
|
70996
|
-
|
|
70997
|
-
|
|
70998
|
-
|
|
70999
|
-
|
|
71000
|
-
|
|
71001
|
-
senderId: message.from?.id != null ? String(message.from.id).trim() : undefined,
|
|
71002
|
-
text,
|
|
71003
|
-
agentPromptText,
|
|
71004
|
-
agentPromptBuilder: (nextText) => buildAgentPromptText({
|
|
71005
|
-
text: nextText,
|
|
71006
|
-
identity,
|
|
71007
|
-
config: this.loadedConfig.raw.channels.telegram.agentPrompt,
|
|
71008
|
-
cliTool,
|
|
71009
|
-
responseMode: routeInfo.route.responseMode
|
|
71010
|
-
}),
|
|
71011
|
-
route: routeInfo.route,
|
|
71012
|
-
maxChars: this.getTelegramMaxChars(routeInfo.route.agentId),
|
|
71013
|
-
timingContext,
|
|
71014
|
-
postText: async (nextText) => {
|
|
71015
|
-
responseChunks = await postTelegramText({
|
|
71016
|
-
token: this.accountConfig.botToken,
|
|
71017
|
-
chatId: message.chat.id,
|
|
71018
|
-
text: nextText,
|
|
71019
|
-
topicId: routeInfo.topicId,
|
|
71020
|
-
omitThreadId: shouldOmitTelegramThreadId(routeInfo.topicId)
|
|
71021
|
-
});
|
|
71022
|
-
return responseChunks;
|
|
71023
|
-
},
|
|
71024
|
-
reconcileText: async (chunks, nextText) => {
|
|
71025
|
-
responseChunks = await reconcileTelegramText({
|
|
71026
|
-
token: this.accountConfig.botToken,
|
|
71027
|
-
chatId: message.chat.id,
|
|
71028
|
-
chunks,
|
|
71029
|
-
text: nextText,
|
|
71030
|
-
topicId: routeInfo.topicId,
|
|
71031
|
-
omitThreadId: shouldOmitTelegramThreadId(routeInfo.topicId)
|
|
71032
|
-
});
|
|
71033
|
-
return responseChunks;
|
|
71034
|
-
}
|
|
71035
|
-
});
|
|
71620
|
+
const processingLease = await this.processingIndicators.acquire({
|
|
71621
|
+
key: `telegram:${this.accountId}:${message.chat.id}:${routeInfo.topicId ?? "root"}`,
|
|
71622
|
+
activate: async () => beginTelegramTypingHeartbeat({
|
|
71623
|
+
sendTyping: () => this.sendTyping(message.chat.id, routeInfo.topicId),
|
|
71624
|
+
onError: (error) => {
|
|
71625
|
+
console.error("telegram typing failed", error);
|
|
71626
|
+
}
|
|
71627
|
+
}),
|
|
71628
|
+
onError: (_phase, error) => {
|
|
71629
|
+
console.error("telegram processing indicator failed", error);
|
|
71036
71630
|
}
|
|
71037
71631
|
});
|
|
71632
|
+
try {
|
|
71633
|
+
const interaction = await processChannelInteraction({
|
|
71634
|
+
agentService: this.agentService,
|
|
71635
|
+
sessionTarget: routeInfo.sessionTarget,
|
|
71636
|
+
identity,
|
|
71637
|
+
auth,
|
|
71638
|
+
senderId: message.from?.id != null ? String(message.from.id).trim() : undefined,
|
|
71639
|
+
text,
|
|
71640
|
+
agentPromptText,
|
|
71641
|
+
agentPromptBuilder: (nextText) => buildAgentPromptText({
|
|
71642
|
+
text: nextText,
|
|
71643
|
+
identity,
|
|
71644
|
+
config: this.loadedConfig.raw.channels.telegram.agentPrompt,
|
|
71645
|
+
cliTool,
|
|
71646
|
+
responseMode: routeInfo.route.responseMode,
|
|
71647
|
+
streaming: routeInfo.route.streaming,
|
|
71648
|
+
protectedControlMutationRule
|
|
71649
|
+
}),
|
|
71650
|
+
protectedControlMutationRule,
|
|
71651
|
+
route: routeInfo.route,
|
|
71652
|
+
maxChars: this.getTelegramMaxChars(routeInfo.route.agentId),
|
|
71653
|
+
timingContext,
|
|
71654
|
+
postText: async (nextText) => {
|
|
71655
|
+
responseChunks = await postTelegramText({
|
|
71656
|
+
token: this.accountConfig.botToken,
|
|
71657
|
+
chatId: message.chat.id,
|
|
71658
|
+
text: nextText,
|
|
71659
|
+
topicId: routeInfo.topicId,
|
|
71660
|
+
omitThreadId: shouldOmitTelegramThreadId(routeInfo.topicId)
|
|
71661
|
+
});
|
|
71662
|
+
return responseChunks;
|
|
71663
|
+
},
|
|
71664
|
+
reconcileText: async (chunks, nextText) => {
|
|
71665
|
+
responseChunks = await reconcileTelegramText({
|
|
71666
|
+
token: this.accountConfig.botToken,
|
|
71667
|
+
chatId: message.chat.id,
|
|
71668
|
+
chunks,
|
|
71669
|
+
text: nextText,
|
|
71670
|
+
topicId: routeInfo.topicId,
|
|
71671
|
+
omitThreadId: shouldOmitTelegramThreadId(routeInfo.topicId)
|
|
71672
|
+
});
|
|
71673
|
+
return responseChunks;
|
|
71674
|
+
}
|
|
71675
|
+
});
|
|
71676
|
+
await processingLease.setLifecycle({
|
|
71677
|
+
agentService: this.agentService,
|
|
71678
|
+
sessionTarget: routeInfo.sessionTarget,
|
|
71679
|
+
observerId: `telegram-processing:${message.chat.id}:${routeInfo.topicId ?? "root"}`,
|
|
71680
|
+
lifecycle: interaction.processingIndicatorLifecycle
|
|
71681
|
+
});
|
|
71682
|
+
} finally {
|
|
71683
|
+
await processingLease.release();
|
|
71684
|
+
}
|
|
71038
71685
|
await this.processedEventsStore.markCompleted(eventId);
|
|
71039
71686
|
} catch (error) {
|
|
71040
71687
|
console.error("telegram handler error", error);
|
|
@@ -71435,9 +72082,9 @@ var defaultMessageCliDependencies = {
|
|
|
71435
72082
|
loadConfig,
|
|
71436
72083
|
plugins: listChannelPlugins(),
|
|
71437
72084
|
print: (text) => console.log(text),
|
|
71438
|
-
recordConversationReply: async ({ loadedConfig, target, kind }) => {
|
|
72085
|
+
recordConversationReply: async ({ loadedConfig, target, kind, source }) => {
|
|
71439
72086
|
const agentService = new AgentService(loadedConfig);
|
|
71440
|
-
await agentService.recordConversationReply(target, kind);
|
|
72087
|
+
await agentService.recordConversationReply(target, kind, source);
|
|
71441
72088
|
}
|
|
71442
72089
|
};
|
|
71443
72090
|
function parseRepeatedOption2(args, name) {
|
|
@@ -71454,12 +72101,12 @@ function parseRepeatedOption2(args, name) {
|
|
|
71454
72101
|
}
|
|
71455
72102
|
return values;
|
|
71456
72103
|
}
|
|
71457
|
-
function
|
|
72104
|
+
function parseOptionValue4(args, name) {
|
|
71458
72105
|
const values = parseRepeatedOption2(args, name);
|
|
71459
72106
|
return values.length > 0 ? values.at(-1) : undefined;
|
|
71460
72107
|
}
|
|
71461
72108
|
function parseIntegerOption(args, name) {
|
|
71462
|
-
const raw =
|
|
72109
|
+
const raw = parseOptionValue4(args, name);
|
|
71463
72110
|
if (!raw) {
|
|
71464
72111
|
return;
|
|
71465
72112
|
}
|
|
@@ -71488,25 +72135,25 @@ function parseMessageCommand(args) {
|
|
|
71488
72135
|
}
|
|
71489
72136
|
const action = rawAction;
|
|
71490
72137
|
const rest = args.slice(1);
|
|
71491
|
-
const channel =
|
|
72138
|
+
const channel = parseOptionValue4(rest, "--channel");
|
|
71492
72139
|
if (channel !== "slack" && channel !== "telegram") {
|
|
71493
72140
|
throw new Error("--channel <slack|telegram> is required");
|
|
71494
72141
|
}
|
|
71495
72142
|
return {
|
|
71496
72143
|
action,
|
|
71497
72144
|
channel,
|
|
71498
|
-
account:
|
|
71499
|
-
target:
|
|
71500
|
-
message:
|
|
71501
|
-
media:
|
|
71502
|
-
messageId:
|
|
71503
|
-
emoji:
|
|
72145
|
+
account: parseOptionValue4(rest, "--account"),
|
|
72146
|
+
target: parseOptionValue4(rest, "--target"),
|
|
72147
|
+
message: parseOptionValue4(rest, "--message") ?? parseOptionValue4(rest, "-m"),
|
|
72148
|
+
media: parseOptionValue4(rest, "--media"),
|
|
72149
|
+
messageId: parseOptionValue4(rest, "--message-id"),
|
|
72150
|
+
emoji: parseOptionValue4(rest, "--emoji"),
|
|
71504
72151
|
remove: hasFlag3(rest, "--remove"),
|
|
71505
|
-
threadId:
|
|
71506
|
-
replyTo:
|
|
72152
|
+
threadId: parseOptionValue4(rest, "--thread-id"),
|
|
72153
|
+
replyTo: parseOptionValue4(rest, "--reply-to"),
|
|
71507
72154
|
limit: parseIntegerOption(rest, "--limit"),
|
|
71508
|
-
query:
|
|
71509
|
-
pollQuestion:
|
|
72155
|
+
query: parseOptionValue4(rest, "--query"),
|
|
72156
|
+
pollQuestion: parseOptionValue4(rest, "--poll-question"),
|
|
71510
72157
|
pollOptions: parseRepeatedOption2(rest, "--poll-option"),
|
|
71511
72158
|
forceDocument: hasFlag3(rest, "--force-document"),
|
|
71512
72159
|
silent: hasFlag3(rest, "--silent"),
|
|
@@ -71549,7 +72196,9 @@ async function runMessageCli(args, dependencies = defaultMessageCliDependencies)
|
|
|
71549
72196
|
throw new Error("--progress and --final cannot be used together");
|
|
71550
72197
|
}
|
|
71551
72198
|
assertTarget(command);
|
|
71552
|
-
const loadedConfig = await dependencies.loadConfig(getConfigPath()
|
|
72199
|
+
const loadedConfig = await dependencies.loadConfig(getConfigPath(), {
|
|
72200
|
+
materializeChannels: [command.channel]
|
|
72201
|
+
});
|
|
71553
72202
|
const plugin = dependencies.plugins.find((entry) => entry.id === command.channel);
|
|
71554
72203
|
if (!plugin) {
|
|
71555
72204
|
throw new Error(`Unsupported message channel: ${command.channel}`);
|
|
@@ -71564,7 +72213,8 @@ async function runMessageCli(args, dependencies = defaultMessageCliDependencies)
|
|
|
71564
72213
|
await dependencies.recordConversationReply({
|
|
71565
72214
|
loadedConfig,
|
|
71566
72215
|
target: replyTarget,
|
|
71567
|
-
kind: resolveReplyKind(command)
|
|
72216
|
+
kind: resolveReplyKind(command),
|
|
72217
|
+
source: "message-tool"
|
|
71568
72218
|
});
|
|
71569
72219
|
}
|
|
71570
72220
|
if (command.json) {
|
|
@@ -71576,7 +72226,7 @@ async function runMessageCli(args, dependencies = defaultMessageCliDependencies)
|
|
|
71576
72226
|
|
|
71577
72227
|
// src/control/runtime-supervisor.ts
|
|
71578
72228
|
import { statSync as statSync4, watch } from "node:fs";
|
|
71579
|
-
import { basename as basename4, dirname as
|
|
72229
|
+
import { basename as basename4, dirname as dirname14 } from "node:path";
|
|
71580
72230
|
|
|
71581
72231
|
// src/channels/processed-events-store.ts
|
|
71582
72232
|
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
|
|
@@ -71654,7 +72304,7 @@ class ProcessedEventsStore {
|
|
|
71654
72304
|
}
|
|
71655
72305
|
|
|
71656
72306
|
// src/control/activity-store.ts
|
|
71657
|
-
import { dirname as
|
|
72307
|
+
import { dirname as dirname13 } from "node:path";
|
|
71658
72308
|
class ActivityStore {
|
|
71659
72309
|
filePath;
|
|
71660
72310
|
constructor(filePath = getDefaultActivityStorePath()) {
|
|
@@ -71693,7 +72343,7 @@ class ActivityStore {
|
|
|
71693
72343
|
};
|
|
71694
72344
|
}
|
|
71695
72345
|
async write(document2) {
|
|
71696
|
-
await ensureDir2(
|
|
72346
|
+
await ensureDir2(dirname13(this.filePath));
|
|
71697
72347
|
await writeTextFile(this.filePath, `${JSON.stringify(document2, null, 2)}
|
|
71698
72348
|
`);
|
|
71699
72349
|
}
|
|
@@ -71935,7 +72585,7 @@ class RuntimeSupervisor {
|
|
|
71935
72585
|
if (this.configWatcher) {
|
|
71936
72586
|
return;
|
|
71937
72587
|
}
|
|
71938
|
-
const watchedDir =
|
|
72588
|
+
const watchedDir = dirname14(loadedConfig.configPath);
|
|
71939
72589
|
const watchedFile = basename4(loadedConfig.configPath);
|
|
71940
72590
|
this.configWatcher = watch(watchedDir, (_eventType, filename) => {
|
|
71941
72591
|
if (filename && filename.toString() !== watchedFile) {
|