clisbot 0.1.45-beta.6 → 0.1.45-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +497 -101
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -58902,10 +58902,12 @@ function parseCommandDurationMs(raw) {
|
|
|
58902
58902
|
// src/agents/loop-command.ts
|
|
58903
58903
|
var DEFAULT_LOOP_MAX_TIMES = 50;
|
|
58904
58904
|
var LOOP_FORCE_FLAG = "--force";
|
|
58905
|
+
var LOOP_START_FLAG = "--loop-start";
|
|
58905
58906
|
var LOOP_ALL_FLAG = "--all";
|
|
58906
58907
|
var LOOP_APP_FLAG = "--app";
|
|
58907
58908
|
var MIN_LOOP_INTERVAL_MS = 60000;
|
|
58908
58909
|
var FORCE_LOOP_INTERVAL_MS = 5 * 60000;
|
|
58910
|
+
var LOOP_START_MODES = ["none", "brief", "full"];
|
|
58909
58911
|
var LOOP_WEEKDAY_LABELS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
|
|
58910
58912
|
var LOOP_WEEKDAY_ALIASES = {
|
|
58911
58913
|
sun: 0,
|
|
@@ -58934,7 +58936,12 @@ function parseLoopSlashCommand(raw) {
|
|
|
58934
58936
|
error: "Loop requires an interval, count, or schedule. Try `/loop 5m check CI`, `/loop 3 check CI`, `/loop every day at 07:00 check CI`, or `/loop 3` for maintenance mode."
|
|
58935
58937
|
};
|
|
58936
58938
|
}
|
|
58937
|
-
const
|
|
58939
|
+
const modifier = extractLoopStartModifier(trimmed);
|
|
58940
|
+
if ("error" in modifier) {
|
|
58941
|
+
return modifier;
|
|
58942
|
+
}
|
|
58943
|
+
const normalizedText = modifier.normalizedText;
|
|
58944
|
+
const tokens = modifier.normalizedText.split(/\s+/).filter(Boolean);
|
|
58938
58945
|
const forceTokenIndexes = tokens.map((token, index) => token.toLowerCase() === LOOP_FORCE_FLAG ? index : -1).filter((index) => index >= 0);
|
|
58939
58946
|
if (forceTokenIndexes.length > 1) {
|
|
58940
58947
|
return {
|
|
@@ -58943,7 +58950,7 @@ function parseLoopSlashCommand(raw) {
|
|
|
58943
58950
|
}
|
|
58944
58951
|
const forceTokenIndex = forceTokenIndexes[0];
|
|
58945
58952
|
const leadingToken = tokens[0] ?? "";
|
|
58946
|
-
const everyDayMatch =
|
|
58953
|
+
const everyDayMatch = normalizedText.match(/^every\s+day\s+at\s+(\S+)(?:\s+(.*))?$/i);
|
|
58947
58954
|
if (everyDayMatch) {
|
|
58948
58955
|
if (forceTokenIndex !== undefined) {
|
|
58949
58956
|
return {
|
|
@@ -58956,7 +58963,7 @@ function parseLoopSlashCommand(raw) {
|
|
|
58956
58963
|
error: "Loop wall-clock time must use `HH:MM` in 24-hour format."
|
|
58957
58964
|
};
|
|
58958
58965
|
}
|
|
58959
|
-
|
|
58966
|
+
const parsed = {
|
|
58960
58967
|
mode: "calendar",
|
|
58961
58968
|
cadence: "daily",
|
|
58962
58969
|
localTime: parsedTime.localTime,
|
|
@@ -58966,8 +58973,18 @@ function parseLoopSlashCommand(raw) {
|
|
|
58966
58973
|
force: false,
|
|
58967
58974
|
syntax: "calendar-at"
|
|
58968
58975
|
};
|
|
58976
|
+
const validationError = validateLoopStartModifierPlacement(parsed, modifier, modifier.tokens.length);
|
|
58977
|
+
if (validationError) {
|
|
58978
|
+
return {
|
|
58979
|
+
error: validationError
|
|
58980
|
+
};
|
|
58981
|
+
}
|
|
58982
|
+
return {
|
|
58983
|
+
...parsed,
|
|
58984
|
+
...modifier.loopStart ? { loopStart: modifier.loopStart } : {}
|
|
58985
|
+
};
|
|
58969
58986
|
}
|
|
58970
|
-
const everyWeekdayMatch =
|
|
58987
|
+
const everyWeekdayMatch = normalizedText.match(/^every\s+weekday\s+at\s+(\S+)(?:\s+(.*))?$/i);
|
|
58971
58988
|
if (everyWeekdayMatch) {
|
|
58972
58989
|
if (forceTokenIndex !== undefined) {
|
|
58973
58990
|
return {
|
|
@@ -58980,7 +58997,7 @@ function parseLoopSlashCommand(raw) {
|
|
|
58980
58997
|
error: "Loop wall-clock time must use `HH:MM` in 24-hour format."
|
|
58981
58998
|
};
|
|
58982
58999
|
}
|
|
58983
|
-
|
|
59000
|
+
const parsed = {
|
|
58984
59001
|
mode: "calendar",
|
|
58985
59002
|
cadence: "weekday",
|
|
58986
59003
|
localTime: parsedTime.localTime,
|
|
@@ -58990,8 +59007,18 @@ function parseLoopSlashCommand(raw) {
|
|
|
58990
59007
|
force: false,
|
|
58991
59008
|
syntax: "calendar-at"
|
|
58992
59009
|
};
|
|
59010
|
+
const validationError = validateLoopStartModifierPlacement(parsed, modifier, modifier.tokens.length);
|
|
59011
|
+
if (validationError) {
|
|
59012
|
+
return {
|
|
59013
|
+
error: validationError
|
|
59014
|
+
};
|
|
59015
|
+
}
|
|
59016
|
+
return {
|
|
59017
|
+
...parsed,
|
|
59018
|
+
...modifier.loopStart ? { loopStart: modifier.loopStart } : {}
|
|
59019
|
+
};
|
|
58993
59020
|
}
|
|
58994
|
-
const everyDayOfWeekMatch =
|
|
59021
|
+
const everyDayOfWeekMatch = normalizedText.match(/^every\s+([a-z]+)\s+at\s+(\S+)(?:\s+(.*))?$/i);
|
|
58995
59022
|
if (everyDayOfWeekMatch) {
|
|
58996
59023
|
const dayOfWeek = resolveLoopDayOfWeek(everyDayOfWeekMatch[1] ?? "");
|
|
58997
59024
|
if (dayOfWeek != null) {
|
|
@@ -59006,7 +59033,7 @@ function parseLoopSlashCommand(raw) {
|
|
|
59006
59033
|
error: "Loop wall-clock time must use `HH:MM` in 24-hour format."
|
|
59007
59034
|
};
|
|
59008
59035
|
}
|
|
59009
|
-
|
|
59036
|
+
const parsed = {
|
|
59010
59037
|
mode: "calendar",
|
|
59011
59038
|
cadence: "day-of-week",
|
|
59012
59039
|
dayOfWeek,
|
|
@@ -59017,6 +59044,16 @@ function parseLoopSlashCommand(raw) {
|
|
|
59017
59044
|
force: false,
|
|
59018
59045
|
syntax: "calendar-at"
|
|
59019
59046
|
};
|
|
59047
|
+
const validationError = validateLoopStartModifierPlacement(parsed, modifier, modifier.tokens.length);
|
|
59048
|
+
if (validationError) {
|
|
59049
|
+
return {
|
|
59050
|
+
error: validationError
|
|
59051
|
+
};
|
|
59052
|
+
}
|
|
59053
|
+
return {
|
|
59054
|
+
...parsed,
|
|
59055
|
+
...modifier.loopStart ? { loopStart: modifier.loopStart } : {}
|
|
59056
|
+
};
|
|
59020
59057
|
}
|
|
59021
59058
|
}
|
|
59022
59059
|
const leadingIntervalMs = parseCommandDurationMs(leadingToken);
|
|
@@ -59028,15 +59065,30 @@ function parseLoopSlashCommand(raw) {
|
|
|
59028
59065
|
}
|
|
59029
59066
|
const promptTokens = tokens.slice(forceTokenIndex === 1 ? 2 : 1);
|
|
59030
59067
|
const promptText = promptTokens.join(" ").trim() || undefined;
|
|
59031
|
-
|
|
59068
|
+
const parsed = {
|
|
59032
59069
|
mode: "interval",
|
|
59033
59070
|
intervalMs: leadingIntervalMs,
|
|
59034
59071
|
promptText,
|
|
59035
59072
|
force: forceTokenIndex === 1,
|
|
59036
59073
|
syntax: "leading-interval"
|
|
59037
59074
|
};
|
|
59075
|
+
const validationError = validateLoopStartModifierPlacement(parsed, modifier, modifier.tokens.length);
|
|
59076
|
+
if (validationError) {
|
|
59077
|
+
return {
|
|
59078
|
+
error: validationError
|
|
59079
|
+
};
|
|
59080
|
+
}
|
|
59081
|
+
return {
|
|
59082
|
+
...parsed,
|
|
59083
|
+
...modifier.loopStart ? { loopStart: modifier.loopStart } : {}
|
|
59084
|
+
};
|
|
59038
59085
|
}
|
|
59039
59086
|
if (/^-?\d+$/.test(leadingToken)) {
|
|
59087
|
+
if (modifier.loopStart) {
|
|
59088
|
+
return {
|
|
59089
|
+
error: `\`${LOOP_START_FLAG}\` is only supported for recurring interval and wall-clock loops.`
|
|
59090
|
+
};
|
|
59091
|
+
}
|
|
59040
59092
|
if (forceTokenIndex !== undefined) {
|
|
59041
59093
|
return {
|
|
59042
59094
|
error: `\`${LOOP_FORCE_FLAG}\` is only supported for interval loops.`
|
|
@@ -59057,8 +59109,13 @@ function parseLoopSlashCommand(raw) {
|
|
|
59057
59109
|
syntax: "leading-count"
|
|
59058
59110
|
};
|
|
59059
59111
|
}
|
|
59060
|
-
const trailingTimes =
|
|
59112
|
+
const trailingTimes = normalizedText.match(/^(.*?)(?:\s+)?(-?\d+)\s+times$/i);
|
|
59061
59113
|
if (trailingTimes) {
|
|
59114
|
+
if (modifier.loopStart) {
|
|
59115
|
+
return {
|
|
59116
|
+
error: `\`${LOOP_START_FLAG}\` is only supported for recurring interval and wall-clock loops.`
|
|
59117
|
+
};
|
|
59118
|
+
}
|
|
59062
59119
|
if (forceTokenIndex !== undefined) {
|
|
59063
59120
|
return {
|
|
59064
59121
|
error: `\`${LOOP_FORCE_FLAG}\` is only supported for interval loops.`
|
|
@@ -59079,7 +59136,7 @@ function parseLoopSlashCommand(raw) {
|
|
|
59079
59136
|
syntax: "trailing-times"
|
|
59080
59137
|
};
|
|
59081
59138
|
}
|
|
59082
|
-
const withoutTrailingForce = forceTokenIndex === tokens.length - 1 ? tokens.slice(0, -1).join(" ") :
|
|
59139
|
+
const withoutTrailingForce = forceTokenIndex === tokens.length - 1 ? tokens.slice(0, -1).join(" ") : normalizedText;
|
|
59083
59140
|
const normalizedEveryInput = withoutTrailingForce.trim();
|
|
59084
59141
|
const hasTrailingForce = forceTokenIndex === tokens.length - 1;
|
|
59085
59142
|
if (forceTokenIndex !== undefined && !hasTrailingForce) {
|
|
@@ -59096,13 +59153,23 @@ function parseLoopSlashCommand(raw) {
|
|
|
59096
59153
|
};
|
|
59097
59154
|
}
|
|
59098
59155
|
const promptText = everyCompactClause[1]?.trim() || undefined;
|
|
59099
|
-
|
|
59156
|
+
const parsed = {
|
|
59100
59157
|
mode: "interval",
|
|
59101
59158
|
intervalMs,
|
|
59102
59159
|
promptText,
|
|
59103
59160
|
force: hasTrailingForce,
|
|
59104
59161
|
syntax: "every-clause"
|
|
59105
59162
|
};
|
|
59163
|
+
const validationError = validateLoopStartModifierPlacement(parsed, modifier, modifier.tokens.length);
|
|
59164
|
+
if (validationError) {
|
|
59165
|
+
return {
|
|
59166
|
+
error: validationError
|
|
59167
|
+
};
|
|
59168
|
+
}
|
|
59169
|
+
return {
|
|
59170
|
+
...parsed,
|
|
59171
|
+
...modifier.loopStart ? { loopStart: modifier.loopStart } : {}
|
|
59172
|
+
};
|
|
59106
59173
|
}
|
|
59107
59174
|
const everyClause = normalizedEveryInput.match(/^(.*?)(?:\s+)?every\s+(-?\d+)\s+([a-z]+)$/i);
|
|
59108
59175
|
if (everyClause) {
|
|
@@ -59120,18 +59187,84 @@ function parseLoopSlashCommand(raw) {
|
|
|
59120
59187
|
};
|
|
59121
59188
|
}
|
|
59122
59189
|
const promptText = everyClause[1]?.trim() || undefined;
|
|
59123
|
-
|
|
59190
|
+
const parsed = {
|
|
59124
59191
|
mode: "interval",
|
|
59125
59192
|
intervalMs,
|
|
59126
59193
|
promptText,
|
|
59127
59194
|
force: hasTrailingForce,
|
|
59128
59195
|
syntax: "every-clause"
|
|
59129
59196
|
};
|
|
59197
|
+
const validationError = validateLoopStartModifierPlacement(parsed, modifier, modifier.tokens.length);
|
|
59198
|
+
if (validationError) {
|
|
59199
|
+
return {
|
|
59200
|
+
error: validationError
|
|
59201
|
+
};
|
|
59202
|
+
}
|
|
59203
|
+
return {
|
|
59204
|
+
...parsed,
|
|
59205
|
+
...modifier.loopStart ? { loopStart: modifier.loopStart } : {}
|
|
59206
|
+
};
|
|
59130
59207
|
}
|
|
59131
59208
|
return {
|
|
59132
59209
|
error: "Loop requires an interval, count, or schedule. Try `/loop 5m check CI`, `/loop 3 check CI`, `/loop every day at 07:00 check CI`, or `/loop 3` for maintenance mode."
|
|
59133
59210
|
};
|
|
59134
59211
|
}
|
|
59212
|
+
function extractLoopStartModifier(raw) {
|
|
59213
|
+
const tokens = raw.split(/\s+/).filter(Boolean);
|
|
59214
|
+
const indexes = tokens.map((token, index) => token.trim().toLowerCase() === LOOP_START_FLAG ? index : -1).filter((index) => index >= 0);
|
|
59215
|
+
if (indexes.length > 1) {
|
|
59216
|
+
return {
|
|
59217
|
+
error: `Loop accepts at most one \`${LOOP_START_FLAG}\` flag.`
|
|
59218
|
+
};
|
|
59219
|
+
}
|
|
59220
|
+
const flagIndex = indexes[0];
|
|
59221
|
+
if (flagIndex == null) {
|
|
59222
|
+
return {
|
|
59223
|
+
normalizedText: raw,
|
|
59224
|
+
tokens
|
|
59225
|
+
};
|
|
59226
|
+
}
|
|
59227
|
+
const rawMode = tokens[flagIndex + 1]?.trim().toLowerCase();
|
|
59228
|
+
if (!rawMode) {
|
|
59229
|
+
return {
|
|
59230
|
+
error: `Loop requires \`${LOOP_START_FLAG} <none|brief|full>\`.`
|
|
59231
|
+
};
|
|
59232
|
+
}
|
|
59233
|
+
if (!LOOP_START_MODES.includes(rawMode)) {
|
|
59234
|
+
return {
|
|
59235
|
+
error: `\`${LOOP_START_FLAG}\` must be one of \`none\`, \`brief\`, or \`full\`.`
|
|
59236
|
+
};
|
|
59237
|
+
}
|
|
59238
|
+
const strippedTokens = tokens.filter((_, index) => index !== flagIndex && index !== flagIndex + 1);
|
|
59239
|
+
return {
|
|
59240
|
+
normalizedText: strippedTokens.join(" "),
|
|
59241
|
+
tokens,
|
|
59242
|
+
flagIndex,
|
|
59243
|
+
loopStart: rawMode
|
|
59244
|
+
};
|
|
59245
|
+
}
|
|
59246
|
+
function validateLoopStartModifierPlacement(parsed, modifier, tokenCount) {
|
|
59247
|
+
if (!modifier.loopStart || modifier.flagIndex == null) {
|
|
59248
|
+
return;
|
|
59249
|
+
}
|
|
59250
|
+
if (parsed.mode === "calendar") {
|
|
59251
|
+
if (modifier.flagIndex === 4) {
|
|
59252
|
+
return;
|
|
59253
|
+
}
|
|
59254
|
+
return `For wall-clock loops, \`${LOOP_START_FLAG}\` must appear immediately after the \`at HH:MM\` clause, for example \`/loop every day at 07:00 --loop-start none morning brief\`.`;
|
|
59255
|
+
}
|
|
59256
|
+
if (parsed.syntax === "leading-interval") {
|
|
59257
|
+
const expectedIndex = parsed.force ? 2 : 1;
|
|
59258
|
+
if (modifier.flagIndex === expectedIndex) {
|
|
59259
|
+
return;
|
|
59260
|
+
}
|
|
59261
|
+
return `For leading interval loops, \`${LOOP_START_FLAG}\` must appear after the interval and optional \`${LOOP_FORCE_FLAG}\`, before the prompt, for example \`/loop 5m --loop-start none check CI\`.`;
|
|
59262
|
+
}
|
|
59263
|
+
if (modifier.flagIndex === tokenCount - 2) {
|
|
59264
|
+
return;
|
|
59265
|
+
}
|
|
59266
|
+
return `For \`every ...\` interval loops, \`${LOOP_START_FLAG}\` must appear at the end of the loop schedule, for example \`/loop check deploy every 2h --loop-start none\`.`;
|
|
59267
|
+
}
|
|
59135
59268
|
function formatLoopIntervalShort(intervalMs) {
|
|
59136
59269
|
if (intervalMs % (60 * 60000) === 0) {
|
|
59137
59270
|
return `${intervalMs / (60 * 60000)}h`;
|
|
@@ -59161,7 +59294,8 @@ function renderLoopHelpLines() {
|
|
|
59161
59294
|
"- `/loop /codereview 3 times`: run the slash command 3 times",
|
|
59162
59295
|
"- `/loop status`: show active loops for this session",
|
|
59163
59296
|
`- \`/loop cancel\`, \`/loop cancel <id>\`, \`/loop cancel --all\`, \`/loop cancel --all ${LOOP_APP_FLAG}\`: cancel active loops`,
|
|
59164
|
-
`- intervals must be at least \`1m\`; intervals below \`5m\` require \`${LOOP_FORCE_FLAG}\` right after the interval clause; wall-clock schedules use \`every ... at HH:MM\`; the first wall-clock loop created with \`clisbot loops create\` requires \`--confirm\`; timezone resolves from route/topic, agent, bot, app timezone, then legacy defaults and host; bare numbers mean times, compact durations such as \`5m\` mean intervals, and the historical default loop cap was \`${DEFAULT_LOOP_MAX_TIMES}
|
|
59297
|
+
`- intervals must be at least \`1m\`; intervals below \`5m\` require \`${LOOP_FORCE_FLAG}\` right after the interval clause; wall-clock schedules use \`every ... at HH:MM\`; the first wall-clock loop created with \`clisbot loops create\` requires \`--confirm\`; timezone resolves from route/topic, agent, bot, app timezone, then legacy defaults and host; bare numbers mean times, compact durations such as \`5m\` mean intervals, and the historical default loop cap was \`${DEFAULT_LOOP_MAX_TIMES}\``,
|
|
59298
|
+
`- Advanced: loop creation also accepts \`${LOOP_START_FLAG} <none|brief|full>\` to override the default start notification behavior for that loop. Example: \`/loop every day at 07:00 --loop-start none morning brief\``
|
|
59165
59299
|
];
|
|
59166
59300
|
}
|
|
59167
59301
|
function hasLoopFlag(raw, flag) {
|
|
@@ -65882,12 +66016,19 @@ function parseAgentCommand(text, options = {}) {
|
|
|
65882
66016
|
if (lowered === "loop") {
|
|
65883
66017
|
const loopText = withoutSlash.slice(command.length).trim();
|
|
65884
66018
|
const loweredLoopText = loopText.toLowerCase();
|
|
66019
|
+
const hasLoopStartOverride = hasLoopFlag(loopText, LOOP_START_FLAG);
|
|
65885
66020
|
if (!loweredLoopText || loweredLoopText === "help") {
|
|
65886
66021
|
return {
|
|
65887
66022
|
type: "control",
|
|
65888
66023
|
name: "loop-help"
|
|
65889
66024
|
};
|
|
65890
66025
|
}
|
|
66026
|
+
if (hasLoopStartOverride && (loweredLoopText.startsWith("help ") || loweredLoopText === "status" || loweredLoopText.startsWith("status "))) {
|
|
66027
|
+
return {
|
|
66028
|
+
type: "loop-error",
|
|
66029
|
+
message: `\`${LOOP_START_FLAG}\` is only supported when creating recurring interval and wall-clock loops.`
|
|
66030
|
+
};
|
|
66031
|
+
}
|
|
65891
66032
|
if (loweredLoopText === "status") {
|
|
65892
66033
|
return {
|
|
65893
66034
|
type: "loop-control",
|
|
@@ -65896,6 +66037,12 @@ function parseAgentCommand(text, options = {}) {
|
|
|
65896
66037
|
}
|
|
65897
66038
|
if (loweredLoopText === "cancel" || loweredLoopText.startsWith("cancel ")) {
|
|
65898
66039
|
const cancelArgs = loopText.slice("cancel".length).trim();
|
|
66040
|
+
if (hasLoopStartOverride) {
|
|
66041
|
+
return {
|
|
66042
|
+
type: "loop-error",
|
|
66043
|
+
message: `\`${LOOP_START_FLAG}\` is only supported when creating recurring interval and wall-clock loops.`
|
|
66044
|
+
};
|
|
66045
|
+
}
|
|
65899
66046
|
if (hasLoopFlag(cancelArgs, LOOP_FORCE_FLAG)) {
|
|
65900
66047
|
return {
|
|
65901
66048
|
type: "loop-error",
|
|
@@ -66011,7 +66158,7 @@ function renderAgentControlSlashHelp() {
|
|
|
66011
66158
|
"- `/detach`: stop live updates for this thread while still posting the final result here",
|
|
66012
66159
|
"- `/watch every 30s [for 10m]`: post the latest state on an interval until the run settles or the watch window ends",
|
|
66013
66160
|
"- `/stop`: send Escape to interrupt the current conversation session",
|
|
66014
|
-
"- `/new`:
|
|
66161
|
+
"- `/new`: start a new session for this routed conversation and store the new session id",
|
|
66015
66162
|
"- `/nudge`: send one extra Enter to the current tmux session without resending the prompt text",
|
|
66016
66163
|
"- `/followup status`: show the current conversation follow-up policy",
|
|
66017
66164
|
"- `/followup auto`: allow natural follow-up after the bot has replied in-thread",
|
|
@@ -67176,6 +67323,7 @@ function createStoredLoopBase(params) {
|
|
|
67176
67323
|
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
67177
67324
|
promptSummary: params.promptSummary,
|
|
67178
67325
|
promptSource: params.promptSource,
|
|
67326
|
+
loopStart: params.loopStart,
|
|
67179
67327
|
createdBy: params.createdBy,
|
|
67180
67328
|
sender: params.sender ?? deriveLegacyLoopSender({
|
|
67181
67329
|
createdBy: params.createdBy,
|
|
@@ -67206,6 +67354,7 @@ function createStoredIntervalLoop(params) {
|
|
|
67206
67354
|
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
67207
67355
|
promptSummary: params.promptSummary,
|
|
67208
67356
|
promptSource: params.promptSource,
|
|
67357
|
+
loopStart: params.loopStart,
|
|
67209
67358
|
createdBy: params.createdBy,
|
|
67210
67359
|
sender: params.sender,
|
|
67211
67360
|
surfaceBinding: params.surfaceBinding,
|
|
@@ -67236,6 +67385,7 @@ function createStoredCalendarLoop(params) {
|
|
|
67236
67385
|
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
67237
67386
|
promptSummary: params.promptSummary,
|
|
67238
67387
|
promptSource: params.promptSource,
|
|
67388
|
+
loopStart: params.loopStart,
|
|
67239
67389
|
createdBy: params.createdBy,
|
|
67240
67390
|
sender: params.sender,
|
|
67241
67391
|
surfaceBinding: params.surfaceBinding,
|
|
@@ -67624,6 +67774,8 @@ class ManagedLoopController {
|
|
|
67624
67774
|
}
|
|
67625
67775
|
|
|
67626
67776
|
// src/agents/managed-queue-controller.ts
|
|
67777
|
+
var QUEUE_MESSAGE_TOOL_FINAL_POLL_MS = 250;
|
|
67778
|
+
|
|
67627
67779
|
class ManagedQueueController {
|
|
67628
67780
|
deps;
|
|
67629
67781
|
queuedItems = new Map;
|
|
@@ -67709,12 +67861,29 @@ class ManagedQueueController {
|
|
|
67709
67861
|
}
|
|
67710
67862
|
}
|
|
67711
67863
|
for (const persisted of persistedItems) {
|
|
67712
|
-
if (persisted.status
|
|
67864
|
+
if (persisted.status === "running") {
|
|
67865
|
+
await this.clearStaleRunningQueueItem(persisted);
|
|
67866
|
+
continue;
|
|
67867
|
+
}
|
|
67868
|
+
if (this.queuedItems.has(persisted.id)) {
|
|
67713
67869
|
continue;
|
|
67714
67870
|
}
|
|
67715
67871
|
this.enqueuePersistedQueueItem(persisted);
|
|
67716
67872
|
}
|
|
67717
67873
|
}
|
|
67874
|
+
async clearStaleRunningQueueItem(item) {
|
|
67875
|
+
if (this.queuedItems.has(item.id)) {
|
|
67876
|
+
return;
|
|
67877
|
+
}
|
|
67878
|
+
const target = {
|
|
67879
|
+
agentId: item.agentId,
|
|
67880
|
+
sessionKey: item.sessionKey
|
|
67881
|
+
};
|
|
67882
|
+
if (await this.deps.hasBlockingActiveRun(target)) {
|
|
67883
|
+
return;
|
|
67884
|
+
}
|
|
67885
|
+
await this.removeManagedQueueItem(target, item);
|
|
67886
|
+
}
|
|
67718
67887
|
persistQueueItem(resolved, item) {
|
|
67719
67888
|
return this.deps.sessionState.countPendingQueuedItemsForSessionKey(resolved.sessionKey, {
|
|
67720
67889
|
excludeId: item.id
|
|
@@ -67759,6 +67928,7 @@ class ManagedQueueController {
|
|
|
67759
67928
|
item: next
|
|
67760
67929
|
});
|
|
67761
67930
|
await this.deps.sessionState.replaceQueuedItemIfPresent(this.deps.resolveTarget(target), next);
|
|
67931
|
+
return next;
|
|
67762
67932
|
}
|
|
67763
67933
|
async removeManagedQueueItem(target, item) {
|
|
67764
67934
|
if (!item) {
|
|
@@ -67777,23 +67947,38 @@ class ManagedQueueController {
|
|
|
67777
67947
|
item
|
|
67778
67948
|
});
|
|
67779
67949
|
const queued = this.deps.queue.enqueue(item.sessionKey, async () => {
|
|
67780
|
-
await this.markQueueItemRunning(target, item);
|
|
67950
|
+
const runningItem = await this.markQueueItemRunning(target, item);
|
|
67781
67951
|
await this.deps.surfaceRuntime.notifyManagedQueueStart(target, item);
|
|
67782
67952
|
const promptText = await this.deps.surfaceRuntime.buildManagedQueuePrompt(item.agentId, item);
|
|
67783
|
-
|
|
67953
|
+
const result = this.deps.activeRuns.executePrompt(target, promptText, {
|
|
67784
67954
|
id: `queue:${item.id}`,
|
|
67785
67955
|
mode: "live",
|
|
67786
67956
|
onUpdate: async () => {
|
|
67787
67957
|
return;
|
|
67788
67958
|
}
|
|
67789
67959
|
});
|
|
67960
|
+
let stopWaitingForToolFinal = false;
|
|
67961
|
+
try {
|
|
67962
|
+
const outcome = await Promise.race([
|
|
67963
|
+
result.then((update) => ({ kind: "result", update })),
|
|
67964
|
+
this.waitForMessageToolFinal(target, runningItem ?? item, () => stopWaitingForToolFinal).then((seen) => ({ kind: seen ? "message-tool-final" : "result" }))
|
|
67965
|
+
]);
|
|
67966
|
+
if (outcome.kind === "message-tool-final") {
|
|
67967
|
+
return this.createMessageToolFinalQueueUpdate(target, item);
|
|
67968
|
+
}
|
|
67969
|
+
return await result;
|
|
67970
|
+
} finally {
|
|
67971
|
+
stopWaitingForToolFinal = true;
|
|
67972
|
+
}
|
|
67790
67973
|
}, {
|
|
67791
67974
|
id: item.id,
|
|
67792
67975
|
createdAt: item.createdAt,
|
|
67793
67976
|
text: item.promptSummary,
|
|
67794
67977
|
canStart: async () => !await this.deps.hasBlockingActiveRun(target),
|
|
67795
67978
|
onComplete: async (value) => {
|
|
67796
|
-
await this.
|
|
67979
|
+
if (!value.messageToolFinalAlreadySent && !await this.hasMessageToolFinalForQueueItem(target, item)) {
|
|
67980
|
+
await this.deps.surfaceRuntime.notifyManagedQueueSettlement(target, item, value);
|
|
67981
|
+
}
|
|
67797
67982
|
await this.removeManagedQueueItem(target, item);
|
|
67798
67983
|
},
|
|
67799
67984
|
onFailure: async (error) => {
|
|
@@ -67809,6 +67994,37 @@ class ManagedQueueController {
|
|
|
67809
67994
|
console.error("queued prompt execution failed", error);
|
|
67810
67995
|
});
|
|
67811
67996
|
}
|
|
67997
|
+
async waitForMessageToolFinal(target, item, shouldStop) {
|
|
67998
|
+
while (!shouldStop()) {
|
|
67999
|
+
if (await this.hasMessageToolFinalForQueueItem(target, item)) {
|
|
68000
|
+
return true;
|
|
68001
|
+
}
|
|
68002
|
+
await sleep(QUEUE_MESSAGE_TOOL_FINAL_POLL_MS);
|
|
68003
|
+
}
|
|
68004
|
+
return false;
|
|
68005
|
+
}
|
|
68006
|
+
async hasMessageToolFinalForQueueItem(target, item) {
|
|
68007
|
+
const startedAt = this.queuedItems.get(item.id)?.item.startedAt ?? item.startedAt;
|
|
68008
|
+
if (typeof startedAt !== "number" || !Number.isFinite(startedAt)) {
|
|
68009
|
+
return false;
|
|
68010
|
+
}
|
|
68011
|
+
const runtime = await this.deps.sessionState.getSessionRuntime(target);
|
|
68012
|
+
return typeof runtime.messageToolFinalReplyAt === "number" && Number.isFinite(runtime.messageToolFinalReplyAt) && runtime.messageToolFinalReplyAt >= startedAt;
|
|
68013
|
+
}
|
|
68014
|
+
createMessageToolFinalQueueUpdate(target, item) {
|
|
68015
|
+
const resolved = this.deps.resolveTarget(target);
|
|
68016
|
+
return {
|
|
68017
|
+
status: "completed",
|
|
68018
|
+
agentId: target.agentId,
|
|
68019
|
+
sessionKey: target.sessionKey,
|
|
68020
|
+
sessionName: resolved.sessionName,
|
|
68021
|
+
workspacePath: resolved.workspacePath,
|
|
68022
|
+
snapshot: "",
|
|
68023
|
+
fullSnapshot: "",
|
|
68024
|
+
initialSnapshot: "",
|
|
68025
|
+
messageToolFinalAlreadySent: true
|
|
68026
|
+
};
|
|
68027
|
+
}
|
|
67812
68028
|
}
|
|
67813
68029
|
|
|
67814
68030
|
// src/agents/runner-service.ts
|
|
@@ -68089,18 +68305,37 @@ function isTimerDrivenStatusLine(line) {
|
|
|
68089
68305
|
return isActiveTimerStatusLine(trimmed) || CLAUDE_WORKED_STATUS_PATTERN.test(trimmed);
|
|
68090
68306
|
}
|
|
68091
68307
|
function hasActiveTimerStatus(snapshot) {
|
|
68092
|
-
return
|
|
68308
|
+
return Boolean(extractLatestActiveTimerStatusLine(snapshot));
|
|
68093
68309
|
}
|
|
68094
68310
|
function extractLatestActiveTimerStatusLine(snapshot) {
|
|
68095
68311
|
const lines = splitNormalizedLines(snapshot);
|
|
68096
68312
|
for (let index = lines.length - 1;index >= 0; index -= 1) {
|
|
68097
68313
|
const line = lines[index]?.trim() ?? "";
|
|
68098
|
-
if (isActiveTimerStatusLine(line)) {
|
|
68314
|
+
if (isActiveTimerStatusLine(line) && isLiveTimerStatusLine(lines, index)) {
|
|
68099
68315
|
return line;
|
|
68100
68316
|
}
|
|
68101
68317
|
}
|
|
68102
68318
|
return "";
|
|
68103
68319
|
}
|
|
68320
|
+
function isLiveTimerStatusLine(lines, timerIndex) {
|
|
68321
|
+
for (let index = timerIndex + 1;index < lines.length; index += 1) {
|
|
68322
|
+
const trimmed = lines[index]?.trim() ?? "";
|
|
68323
|
+
if (!trimmed) {
|
|
68324
|
+
continue;
|
|
68325
|
+
}
|
|
68326
|
+
if (isRunnerIdlePromptLine(trimmed) || isRunnerPromptMetadataLine(trimmed)) {
|
|
68327
|
+
continue;
|
|
68328
|
+
}
|
|
68329
|
+
return false;
|
|
68330
|
+
}
|
|
68331
|
+
return true;
|
|
68332
|
+
}
|
|
68333
|
+
function isRunnerIdlePromptLine(trimmed) {
|
|
68334
|
+
return trimmed.startsWith("› ") || trimmed === "›" || trimmed.startsWith("> ");
|
|
68335
|
+
}
|
|
68336
|
+
function isRunnerPromptMetadataLine(trimmed) {
|
|
68337
|
+
return /^gpt-[\w.-]+ .*·/.test(trimmed) || trimmed.startsWith("model:") || trimmed.startsWith("directory:") || trimmed.startsWith("ctx:") || trimmed.startsWith("tokens:") || trimmed.startsWith("? for shortcuts");
|
|
68338
|
+
}
|
|
68104
68339
|
function shouldDropCodexChromeLine(line) {
|
|
68105
68340
|
const trimmed = line.trim();
|
|
68106
68341
|
if (!trimmed) {
|
|
@@ -68317,6 +68552,11 @@ function deriveInteractionText(initialSnapshot, currentSnapshot) {
|
|
|
68317
68552
|
const current = cleanInteractionSnapshot(currentSnapshot);
|
|
68318
68553
|
return extractScrolledAppend(previous, current) || diffText(previous, current);
|
|
68319
68554
|
}
|
|
68555
|
+
function deriveInteractionDiffText(initialSnapshot, currentSnapshot) {
|
|
68556
|
+
const previous = cleanInteractionSnapshot(initialSnapshot);
|
|
68557
|
+
const current = cleanInteractionSnapshot(currentSnapshot);
|
|
68558
|
+
return diffText(previous, current);
|
|
68559
|
+
}
|
|
68320
68560
|
function appendInteractionText(currentBody, nextDelta) {
|
|
68321
68561
|
const trimmedCurrent = currentBody.trim();
|
|
68322
68562
|
const trimmedDelta = nextDelta.trim();
|
|
@@ -68864,7 +69104,7 @@ async function captureTmuxSessionIdentity(params) {
|
|
|
68864
69104
|
});
|
|
68865
69105
|
continue;
|
|
68866
69106
|
}
|
|
68867
|
-
const sessionId =
|
|
69107
|
+
const sessionId = extractSessionIdFromCandidates(deriveSessionIdentityTexts(statusSubmission.submittedSnapshot, snapshot), params.pattern);
|
|
68868
69108
|
if (sessionId) {
|
|
68869
69109
|
await waitForTmuxPaneSettle({
|
|
68870
69110
|
tmux: params.tmux,
|
|
@@ -68879,10 +69119,23 @@ async function captureTmuxSessionIdentity(params) {
|
|
|
68879
69119
|
}
|
|
68880
69120
|
return null;
|
|
68881
69121
|
}
|
|
68882
|
-
function
|
|
69122
|
+
function deriveSessionIdentityTexts(submittedSnapshot, snapshot) {
|
|
68883
69123
|
const rawSubmitted = normalizePaneText(submittedSnapshot);
|
|
68884
69124
|
const rawSnapshot = normalizePaneText(snapshot);
|
|
68885
|
-
return
|
|
69125
|
+
return [
|
|
69126
|
+
extractScrolledAppend(rawSubmitted, rawSnapshot),
|
|
69127
|
+
deriveInteractionText(submittedSnapshot, snapshot),
|
|
69128
|
+
deriveInteractionDiffText(submittedSnapshot, snapshot)
|
|
69129
|
+
].filter((candidate, index, candidates) => candidate && candidates.indexOf(candidate) === index);
|
|
69130
|
+
}
|
|
69131
|
+
function extractSessionIdFromCandidates(candidates, pattern) {
|
|
69132
|
+
for (const candidate of candidates) {
|
|
69133
|
+
const sessionId = extractSessionId(candidate, pattern);
|
|
69134
|
+
if (sessionId) {
|
|
69135
|
+
return sessionId;
|
|
69136
|
+
}
|
|
69137
|
+
}
|
|
69138
|
+
return null;
|
|
68886
69139
|
}
|
|
68887
69140
|
async function dismissTmuxTrustPromptIfPresent(params) {
|
|
68888
69141
|
const deadline = Date.now() + Math.max(TRUST_PROMPT_MAX_WAIT_MS, params.startupDelayMs);
|
|
@@ -68956,7 +69209,7 @@ async function waitForTmuxSessionBootstrap(params) {
|
|
|
68956
69209
|
};
|
|
68957
69210
|
}
|
|
68958
69211
|
}
|
|
68959
|
-
if (readyRegex && !
|
|
69212
|
+
if (readyRegex && !snapshotHasActiveReadyPattern(snapshot, readyRegex)) {
|
|
68960
69213
|
await sleep(SESSION_BOOTSTRAP_POLL_INTERVAL_MS);
|
|
68961
69214
|
continue;
|
|
68962
69215
|
}
|
|
@@ -69190,6 +69443,29 @@ function buildBootstrapSessionLostError(sessionName, error) {
|
|
|
69190
69443
|
function arePaneStatesEqual(left, right) {
|
|
69191
69444
|
return left.cursorX === right.cursorX && left.cursorY === right.cursorY && left.historySize === right.historySize;
|
|
69192
69445
|
}
|
|
69446
|
+
function snapshotHasActiveReadyPattern(snapshot, readyRegex) {
|
|
69447
|
+
const lines = splitNormalizedLines(snapshot);
|
|
69448
|
+
let readyLineIndex = -1;
|
|
69449
|
+
for (let index = 0;index < lines.length; index += 1) {
|
|
69450
|
+
if (readyRegex.test(lines[index] ?? "")) {
|
|
69451
|
+
readyLineIndex = index;
|
|
69452
|
+
}
|
|
69453
|
+
}
|
|
69454
|
+
if (readyLineIndex < 0) {
|
|
69455
|
+
return readyRegex.test(snapshot);
|
|
69456
|
+
}
|
|
69457
|
+
for (const rawLine of lines.slice(readyLineIndex + 1)) {
|
|
69458
|
+
const line = rawLine.trim();
|
|
69459
|
+
if (!line || isPromptMetadataLine(line)) {
|
|
69460
|
+
continue;
|
|
69461
|
+
}
|
|
69462
|
+
return false;
|
|
69463
|
+
}
|
|
69464
|
+
return true;
|
|
69465
|
+
}
|
|
69466
|
+
function isPromptMetadataLine(line) {
|
|
69467
|
+
return /^gpt-[\w.-]+\b/i.test(line) || /^model:\s*/i.test(line) || /^session:\s*/i.test(line);
|
|
69468
|
+
}
|
|
69193
69469
|
function looksLikeClaudeTrustPrompt(snapshot) {
|
|
69194
69470
|
return snapshot.includes("Quick safety check:") && snapshot.includes("Yes, I trust this folder") || snapshot.includes("Enter to confirm · Esc to cancel");
|
|
69195
69471
|
}
|
|
@@ -69396,6 +69672,9 @@ function isFreshStartRetryablePromptDeliveryError(error) {
|
|
|
69396
69672
|
function isRetryableFreshStartFault(error) {
|
|
69397
69673
|
return isRecoverableStartupSessionLoss(error) || isTransientTmuxTargetError(error) || isFreshStartRetryablePromptDeliveryError(error);
|
|
69398
69674
|
}
|
|
69675
|
+
function canRestartWithStoredSessionId(resolved) {
|
|
69676
|
+
return resolved.runner.sessionId.resume.mode === "command" || resolved.runner.sessionId.create.mode === "explicit";
|
|
69677
|
+
}
|
|
69399
69678
|
|
|
69400
69679
|
class RunnerService {
|
|
69401
69680
|
loadedConfig;
|
|
@@ -69618,10 +69897,7 @@ class RunnerService {
|
|
|
69618
69897
|
});
|
|
69619
69898
|
try {
|
|
69620
69899
|
await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
69621
|
-
await this.
|
|
69622
|
-
sessionId: existing?.sessionId,
|
|
69623
|
-
runnerCommand: resolved.runner.command
|
|
69624
|
-
});
|
|
69900
|
+
await this.syncSessionIdentity(resolved);
|
|
69625
69901
|
} catch (error) {
|
|
69626
69902
|
throw await this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
69627
69903
|
}
|
|
@@ -69766,7 +70042,7 @@ class RunnerService {
|
|
|
69766
70042
|
async reopenRunContext(target, timingContext) {
|
|
69767
70043
|
const resolved = this.resolveTarget(target);
|
|
69768
70044
|
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
69769
|
-
if (!existing?.sessionId || resolved
|
|
70045
|
+
if (!existing?.sessionId || !canRestartWithStoredSessionId(resolved)) {
|
|
69770
70046
|
throw new Error(`Runner session "${resolved.sessionName}" cannot reopen the same conversation context.`);
|
|
69771
70047
|
}
|
|
69772
70048
|
return this.ensureRunnerReady(target, { allowFreshRetryBeforePrompt: false, timingContext });
|
|
@@ -69925,7 +70201,6 @@ class RunnerService {
|
|
|
69925
70201
|
});
|
|
69926
70202
|
try {
|
|
69927
70203
|
await this.tmux.sendKey(resolved.sessionName, "Escape");
|
|
69928
|
-
await sleep(150);
|
|
69929
70204
|
} catch {}
|
|
69930
70205
|
}
|
|
69931
70206
|
return {
|
|
@@ -70048,7 +70323,7 @@ async function monitorTmuxRun(params) {
|
|
|
70048
70323
|
let previousRenderedRunningSnapshot = "";
|
|
70049
70324
|
let lastPaneChangeAt = params.startedAt;
|
|
70050
70325
|
let sawActivity = false;
|
|
70051
|
-
let sawPaneChange =
|
|
70326
|
+
let sawPaneChange = !params.prompt;
|
|
70052
70327
|
let sawPromptSubmission = Boolean(params.prompt);
|
|
70053
70328
|
let detachedNotified = params.detachedAlready;
|
|
70054
70329
|
let firstMeaningfulDeltaLogged = false;
|
|
@@ -70258,6 +70533,26 @@ class SessionService {
|
|
|
70258
70533
|
});
|
|
70259
70534
|
}
|
|
70260
70535
|
}
|
|
70536
|
+
async clearLostPersistedActiveRuns() {
|
|
70537
|
+
const entries = await this.sessionState.listEntries();
|
|
70538
|
+
for (const entry of entries) {
|
|
70539
|
+
if (!entry.runtime || entry.runtime.state === "idle") {
|
|
70540
|
+
continue;
|
|
70541
|
+
}
|
|
70542
|
+
if (this.activeRuns.has(entry.sessionKey)) {
|
|
70543
|
+
continue;
|
|
70544
|
+
}
|
|
70545
|
+
const resolved = this.resolveTarget({
|
|
70546
|
+
agentId: entry.agentId,
|
|
70547
|
+
sessionKey: entry.sessionKey
|
|
70548
|
+
});
|
|
70549
|
+
if (!await this.tmux.hasSession(resolved.sessionName)) {
|
|
70550
|
+
await this.sessionState.setSessionRuntime(resolved, {
|
|
70551
|
+
state: "idle"
|
|
70552
|
+
});
|
|
70553
|
+
}
|
|
70554
|
+
}
|
|
70555
|
+
}
|
|
70261
70556
|
async executePrompt(target, prompt, observer, options = {}) {
|
|
70262
70557
|
if (this.stopping) {
|
|
70263
70558
|
throw new Error("Runtime is stopping and cannot accept a new prompt.");
|
|
@@ -70392,6 +70687,34 @@ class SessionService {
|
|
|
70392
70687
|
detached: true
|
|
70393
70688
|
};
|
|
70394
70689
|
}
|
|
70690
|
+
async interruptActiveRun(target) {
|
|
70691
|
+
const run = this.activeRuns.get(target.sessionKey) ?? await this.reconcilePersistedActiveRun(target);
|
|
70692
|
+
if (!run) {
|
|
70693
|
+
return {
|
|
70694
|
+
interrupted: false
|
|
70695
|
+
};
|
|
70696
|
+
}
|
|
70697
|
+
const error = new Error("Run interrupted by /stop.");
|
|
70698
|
+
const update = this.createRunUpdate({
|
|
70699
|
+
resolved: run.resolved,
|
|
70700
|
+
status: "error",
|
|
70701
|
+
snapshot: error.message,
|
|
70702
|
+
fullSnapshot: run.latestUpdate.fullSnapshot,
|
|
70703
|
+
initialSnapshot: run.latestUpdate.initialSnapshot,
|
|
70704
|
+
note: "Run interrupted."
|
|
70705
|
+
});
|
|
70706
|
+
await this.sessionState.setSessionRuntime(run.resolved, {
|
|
70707
|
+
state: "idle"
|
|
70708
|
+
});
|
|
70709
|
+
await this.notifyRunObservers(run, update);
|
|
70710
|
+
if (!run.initialResult.settled) {
|
|
70711
|
+
run.initialResult.reject(error);
|
|
70712
|
+
}
|
|
70713
|
+
this.activeRuns.delete(run.resolved.sessionKey);
|
|
70714
|
+
return {
|
|
70715
|
+
interrupted: true
|
|
70716
|
+
};
|
|
70717
|
+
}
|
|
70395
70718
|
hasActiveRun(target) {
|
|
70396
70719
|
return this.activeRuns.has(target.sessionKey);
|
|
70397
70720
|
}
|
|
@@ -70691,7 +71014,7 @@ class SessionService {
|
|
|
70691
71014
|
}
|
|
70692
71015
|
}
|
|
70693
71016
|
async hasStoredResumableSessionId(resolved) {
|
|
70694
|
-
if (resolved.runner.sessionId.resume.mode !== "command") {
|
|
71017
|
+
if (resolved.runner.sessionId.resume.mode !== "command" && resolved.runner.sessionId.create.mode !== "explicit") {
|
|
70695
71018
|
return false;
|
|
70696
71019
|
}
|
|
70697
71020
|
const entry = await this.sessionState.getEntry(resolved.sessionKey);
|
|
@@ -71067,18 +71390,11 @@ var REPLY_RULES = `When replying to the user:
|
|
|
71067
71390
|
- put the user-facing message inside the --message body of that command
|
|
71068
71391
|
{{progress_rules_block}}- {{final_rule_line}}`;
|
|
71069
71392
|
var PROGRESS_PHRASE = "progress update or ";
|
|
71070
|
-
var EMPTY_PROGRESS_PHRASE = "";
|
|
71071
71393
|
var PROGRESS_FLAG_SUFFIX = "|progress";
|
|
71072
|
-
var EMPTY_PROGRESS_FLAG_SUFFIX = "";
|
|
71073
71394
|
var PROGRESS_RULES_BLOCK = `- use that command to send progress updates and the final reply back to the conversation
|
|
71074
|
-
- send at most {{max_progress_messages}} progress updates
|
|
71075
|
-
- keep progress updates short and meaningful
|
|
71076
|
-
- do not send progress updates for trivial internal steps
|
|
71077
|
-
`;
|
|
71078
|
-
var FINAL_ONLY_RULES_BLOCK = `- use that command only for the final user-facing reply
|
|
71079
|
-
- do not send user-facing progress updates for this conversation
|
|
71395
|
+
- send at most {{max_progress_messages}} short, meaningful progress updates; skip trivial internal steps
|
|
71080
71396
|
`;
|
|
71081
|
-
var FINAL_RULE_REQUIRED = "send
|
|
71397
|
+
var FINAL_RULE_REQUIRED = "send a single final user-facing message by default; split only when channel limits require it or clarity would otherwise suffer";
|
|
71082
71398
|
var FINAL_RULE_OPTIONAL = "final response is optional";
|
|
71083
71399
|
var EMPTY_REPLY_COMMAND = "";
|
|
71084
71400
|
var EMPTY_REPLY_RULES = "";
|
|
@@ -71183,10 +71499,9 @@ function renderMessagePromptParts(params) {
|
|
|
71183
71499
|
replyStyleHint: EMPTY_REPLY_STYLE_HINT
|
|
71184
71500
|
};
|
|
71185
71501
|
}
|
|
71186
|
-
const
|
|
71187
|
-
const
|
|
71188
|
-
const
|
|
71189
|
-
const progressRulesBlock = allowProgress ? PROGRESS_RULES_BLOCK : FINAL_ONLY_RULES_BLOCK;
|
|
71502
|
+
const progressPhrase = PROGRESS_PHRASE;
|
|
71503
|
+
const progressFlagSuffix = PROGRESS_FLAG_SUFFIX;
|
|
71504
|
+
const progressRulesBlock = PROGRESS_RULES_BLOCK;
|
|
71190
71505
|
const finalRuleLine = params.config.requireFinalResponse ? FINAL_RULE_REQUIRED : FINAL_RULE_OPTIONAL;
|
|
71191
71506
|
return {
|
|
71192
71507
|
deliveryIntro: renderTemplate(DELIVERY_INTRO, {
|
|
@@ -71215,8 +71530,9 @@ function renderConfigurationGuidance() {
|
|
|
71215
71530
|
const cliName = getRenderedCliName();
|
|
71216
71531
|
return [
|
|
71217
71532
|
`When the user asks to change ${cliName} configuration, use ${cliName} CLI commands; see ${renderCliCommand("--help", { inline: true })}, ${renderCliCommand("bots --help", { inline: true })}, ${renderCliCommand("routes --help", { inline: true })}, ${renderCliCommand("auth --help", { inline: true })}, or ${renderCliCommand("update --help", { inline: true })} for details.`,
|
|
71533
|
+
`For ${cliName} install or update requests, check ${renderCliCommand("update --help", { inline: true })} first and follow it.`,
|
|
71218
71534
|
`For schedule/loop/reminder requests, inspect ${renderCliCommand("loops --help", { inline: true })} and use the loops CLI.`,
|
|
71219
|
-
`For durable queue
|
|
71535
|
+
`For durable queue requests, inspect ${renderCliCommand("queues --help", { inline: true })} and use the queues CLI.`
|
|
71220
71536
|
].join(`
|
|
71221
71537
|
`);
|
|
71222
71538
|
}
|
|
@@ -71722,8 +72038,9 @@ class SurfaceRuntime {
|
|
|
71722
72038
|
}
|
|
71723
72039
|
const identity = this.buildLoopChannelIdentity(loop);
|
|
71724
72040
|
const notifications = this.resolveSurfaceNotifications(identity);
|
|
72041
|
+
const mode = loop.loopStart ?? notifications.loopStart;
|
|
71725
72042
|
const text = loop.kind === "calendar" ? renderLoopStartNotification({
|
|
71726
|
-
mode
|
|
72043
|
+
mode,
|
|
71727
72044
|
agentId: target.agentId,
|
|
71728
72045
|
loopId: loop.id,
|
|
71729
72046
|
promptSummary: loop.promptSummary,
|
|
@@ -71736,7 +72053,7 @@ class SurfaceRuntime {
|
|
|
71736
72053
|
maxRuns: loop.maxRuns,
|
|
71737
72054
|
kind: "calendar"
|
|
71738
72055
|
}) : renderLoopStartNotification({
|
|
71739
|
-
mode
|
|
72056
|
+
mode,
|
|
71740
72057
|
agentId: target.agentId,
|
|
71741
72058
|
loopId: loop.id,
|
|
71742
72059
|
promptSummary: loop.promptSummary,
|
|
@@ -72150,7 +72467,12 @@ class AgentService {
|
|
|
72150
72467
|
return this.runnerSessions.captureTranscript(target);
|
|
72151
72468
|
}
|
|
72152
72469
|
async interruptSession(target) {
|
|
72153
|
-
|
|
72470
|
+
const runner = await this.runnerSessions.interruptSession(target);
|
|
72471
|
+
const activeRun = await this.activeRuns.interruptActiveRun(target);
|
|
72472
|
+
return {
|
|
72473
|
+
...runner,
|
|
72474
|
+
interrupted: runner.interrupted || activeRun.interrupted
|
|
72475
|
+
};
|
|
72154
72476
|
}
|
|
72155
72477
|
async nudgeSession(target) {
|
|
72156
72478
|
return this.runnerSessions.nudgeSession(target);
|
|
@@ -72167,13 +72489,16 @@ class AgentService {
|
|
|
72167
72489
|
async getSessionDiagnostics(target) {
|
|
72168
72490
|
const resolved = this.resolveTarget(target);
|
|
72169
72491
|
const entry = await this.sessionState.getEntry(target.sessionKey);
|
|
72170
|
-
const
|
|
72492
|
+
const storedSessionId = entry?.sessionId?.trim() || undefined;
|
|
72171
72493
|
return {
|
|
72172
|
-
|
|
72173
|
-
|
|
72494
|
+
sessionName: resolved.sessionName,
|
|
72495
|
+
sessionId: storedSessionId,
|
|
72496
|
+
storedSessionId,
|
|
72497
|
+
resumeCommand: buildResumeCommandPreview(resolved, storedSessionId)
|
|
72174
72498
|
};
|
|
72175
72499
|
}
|
|
72176
72500
|
async listActiveSessionRuntimes() {
|
|
72501
|
+
await this.activeRuns.clearLostPersistedActiveRuns();
|
|
72177
72502
|
return this.sessionState.listActiveSessionRuntimes();
|
|
72178
72503
|
}
|
|
72179
72504
|
async setConversationFollowUpMode(target, mode) {
|
|
@@ -72593,30 +72918,16 @@ function renderStartupSteeringUnavailableMessage() {
|
|
|
72593
72918
|
].join(`
|
|
72594
72919
|
`);
|
|
72595
72920
|
}
|
|
72596
|
-
function renderPrincipalFormat(identity) {
|
|
72597
|
-
if (identity.platform === "slack") {
|
|
72598
|
-
return "slack:<nativeUserId>";
|
|
72599
|
-
}
|
|
72600
|
-
return "telegram:<nativeUserId>";
|
|
72601
|
-
}
|
|
72602
|
-
function renderPrincipalExample(identity) {
|
|
72603
|
-
if (identity.senderId) {
|
|
72604
|
-
return `${identity.platform}:${identity.senderId}`;
|
|
72605
|
-
}
|
|
72606
|
-
if (identity.platform === "slack") {
|
|
72607
|
-
return "slack:U123ABC456";
|
|
72608
|
-
}
|
|
72609
|
-
return "telegram:1276408333";
|
|
72610
|
-
}
|
|
72611
72921
|
function renderWhoAmIMessage(params) {
|
|
72922
|
+
const storedSessionId = params.sessionDiagnostics.storedSessionId ?? params.sessionDiagnostics.sessionId;
|
|
72612
72923
|
const lines = [
|
|
72613
72924
|
"Who am I",
|
|
72614
72925
|
"",
|
|
72615
72926
|
`platform: \`${params.identity.platform}\``,
|
|
72616
72927
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
72617
72928
|
`agentId: \`${params.route.agentId}\``,
|
|
72618
|
-
`
|
|
72619
|
-
`storedSessionId: \`${
|
|
72929
|
+
`sessionName: \`${params.sessionDiagnostics.sessionName ?? "(not available)"}\``,
|
|
72930
|
+
`storedSessionId: \`${storedSessionId ?? "(not captured yet)"}\``
|
|
72620
72931
|
];
|
|
72621
72932
|
if (params.identity.senderId) {
|
|
72622
72933
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -72633,19 +72944,20 @@ function renderWhoAmIMessage(params) {
|
|
|
72633
72944
|
if (params.identity.topicId) {
|
|
72634
72945
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
72635
72946
|
}
|
|
72636
|
-
lines.push(`resumeCommand: \`${params.sessionDiagnostics.resumeCommand ?? "(not available yet)"}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `
|
|
72947
|
+
lines.push(`resumeCommand: \`${params.sessionDiagnostics.resumeCommand ?? "(not available yet)"}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayBypassPairing: \`${params.auth.mayBypassPairing}\``, `mayBypassSharedSenderPolicy: \`${params.auth.mayBypassSharedSenderPolicy}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `verbose: \`${params.route.verbose}\``);
|
|
72637
72948
|
return lines.join(`
|
|
72638
72949
|
`);
|
|
72639
72950
|
}
|
|
72640
72951
|
function renderRouteStatusMessage(params) {
|
|
72952
|
+
const storedSessionId = params.sessionDiagnostics.storedSessionId ?? params.sessionDiagnostics.sessionId;
|
|
72641
72953
|
const lines = [
|
|
72642
72954
|
"Status",
|
|
72643
72955
|
"",
|
|
72644
72956
|
`platform: \`${params.identity.platform}\``,
|
|
72645
72957
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
72646
72958
|
`agentId: \`${params.route.agentId}\``,
|
|
72647
|
-
`
|
|
72648
|
-
`storedSessionId: \`${
|
|
72959
|
+
`sessionName: \`${params.sessionDiagnostics.sessionName ?? "(not available)"}\``,
|
|
72960
|
+
`storedSessionId: \`${storedSessionId ?? "(not captured yet)"}\``
|
|
72649
72961
|
];
|
|
72650
72962
|
if (params.identity.senderId) {
|
|
72651
72963
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -72662,7 +72974,7 @@ function renderRouteStatusMessage(params) {
|
|
|
72662
72974
|
if (params.identity.topicId) {
|
|
72663
72975
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
72664
72976
|
}
|
|
72665
|
-
lines.push(`resumeCommand: \`${params.sessionDiagnostics.resumeCommand ?? "(not available yet)"}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `
|
|
72977
|
+
lines.push(`resumeCommand: \`${params.sessionDiagnostics.resumeCommand ?? "(not available yet)"}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `streaming: \`${params.route.streaming}\``, `response: \`${params.route.response}\``, `additionalMessageMode: \`${params.route.additionalMessageMode}\``, `surfaceNotifications.queueStart: \`${params.route.surfaceNotifications.queueStart}\``, `surfaceNotifications.loopStart: \`${params.route.surfaceNotifications.loopStart}\``, `verbose: \`${params.route.verbose}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayBypassSharedSenderPolicy: \`${params.auth.mayBypassSharedSenderPolicy}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `timezone.effective: \`${params.timezone.effective}\``, `timezone.route: \`${params.timezone.route ?? "(inherit)"}\``, `timezone.bot: \`${params.timezone.bot ?? "(inherit)"}\``, `followUp.mode: \`${params.followUpState.overrideMode ?? params.route.followUp.mode}\``, `followUp.windowMinutes: \`${formatFollowUpTtlMinutes(params.route.followUp.participationTtlMs)}\``, `run.state: \`${params.runtimeState.state}\``);
|
|
72666
72978
|
if (params.runtimeState.startedAt) {
|
|
72667
72979
|
lines.push(`run.startedAt: \`${new Date(params.runtimeState.startedAt).toISOString()}\``);
|
|
72668
72980
|
}
|
|
@@ -73823,6 +74135,7 @@ ${renderLoopUsage()}`);
|
|
|
73823
74135
|
canonicalPromptText: resolvedLoopPrompt.text,
|
|
73824
74136
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
73825
74137
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
74138
|
+
loopStart: slashCommand.params.loopStart,
|
|
73826
74139
|
surfaceBinding: buildLoopSurfaceBinding(params.identity),
|
|
73827
74140
|
cadence: slashCommand.params.cadence,
|
|
73828
74141
|
dayOfWeek: slashCommand.params.dayOfWeek,
|
|
@@ -73862,6 +74175,7 @@ ${renderLoopUsage()}`);
|
|
|
73862
74175
|
canonicalPromptText: resolvedLoopPrompt.text,
|
|
73863
74176
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
73864
74177
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
74178
|
+
loopStart: slashCommand.params.loopStart,
|
|
73865
74179
|
surfaceBinding: buildLoopSurfaceBinding(params.identity),
|
|
73866
74180
|
intervalMs: effectiveIntervalMs,
|
|
73867
74181
|
maxRuns: maxRunsPerLoop,
|
|
@@ -77955,6 +78269,7 @@ var TELEGRAM_FULL_COMMANDS = [
|
|
|
77955
78269
|
{ command: "detach", description: "Stop live updates for this thread" },
|
|
77956
78270
|
{ command: "watch", description: "Watch the active run on an interval" },
|
|
77957
78271
|
{ command: "stop", description: "Interrupt current run" },
|
|
78272
|
+
{ command: "new", description: "Start new session" },
|
|
77958
78273
|
{ command: "nudge", description: "Send one extra Enter to the session" },
|
|
77959
78274
|
{ command: "followup", description: "Show or change follow-up mode" },
|
|
77960
78275
|
{ command: "mention", description: "Require explicit mention for later turns" },
|
|
@@ -81143,6 +81458,7 @@ function renderLoopsCreateHelp() {
|
|
|
81143
81458
|
" - `--new-thread` creates a Slack thread anchor before persisting the loop",
|
|
81144
81459
|
" - `--timezone <iana>` freezes a one-off wall-clock timezone on the loop record",
|
|
81145
81460
|
" - `--confirm` persists the first wall-clock loop after reviewing the confirmation output",
|
|
81461
|
+
` - advanced: \`${LOOP_START_FLAG} <none|brief|full>\` overrides the default scheduled loop-start notification behavior for that recurring loop`,
|
|
81146
81462
|
"",
|
|
81147
81463
|
"Examples:",
|
|
81148
81464
|
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --sender slack:U1234567890 every day at 07:00 check CI")}`,
|
|
@@ -81749,6 +82065,7 @@ function buildRecurringLoopPromptMetadata(request) {
|
|
|
81749
82065
|
canonicalPromptText: request.resolvedPrompt.text,
|
|
81750
82066
|
promptSummary: summarizeLoopPrompt(request.resolvedPrompt.text, request.resolvedPrompt.maintenancePrompt),
|
|
81751
82067
|
promptSource: request.resolvedPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
82068
|
+
loopStart: request.parsed.mode === "times" ? undefined : request.parsed.loopStart,
|
|
81752
82069
|
maintenancePrompt: request.resolvedPrompt.maintenancePrompt,
|
|
81753
82070
|
createdBy: request.creator.providerId,
|
|
81754
82071
|
sender: request.creator,
|
|
@@ -82254,6 +82571,11 @@ async function runMessageCli(args, dependencies = defaultMessageCliDependencies)
|
|
|
82254
82571
|
var QUEUE_SENDER_FLAG = "--sender";
|
|
82255
82572
|
var QUEUE_SENDER_NAME_FLAG = "--sender-name";
|
|
82256
82573
|
var QUEUE_SENDER_HANDLE_FLAG = "--sender-handle";
|
|
82574
|
+
var defaultQueueCliDependencies = {
|
|
82575
|
+
print: (text) => console.log(text),
|
|
82576
|
+
warn: (text) => console.warn(text),
|
|
82577
|
+
sendQueueCreatedNotification: sendQueueCreatedNotificationToSurface
|
|
82578
|
+
};
|
|
82257
82579
|
function getEditableConfigPath9() {
|
|
82258
82580
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
82259
82581
|
}
|
|
@@ -82419,6 +82741,57 @@ function createQueueItemForContext(params) {
|
|
|
82419
82741
|
surfaceBinding: buildQueueSurfaceBinding(params.context)
|
|
82420
82742
|
});
|
|
82421
82743
|
}
|
|
82744
|
+
function renderQueueCreatedNotification(params) {
|
|
82745
|
+
const queueLine = params.positionAhead > 0 ? `Queued: ${params.positionAhead} ahead.` : "Queued.";
|
|
82746
|
+
return `${queueLine}
|
|
82747
|
+
|
|
82748
|
+
Prompt:
|
|
82749
|
+
${params.promptText.trim()}`;
|
|
82750
|
+
}
|
|
82751
|
+
async function getQueuePositionAhead(state, sessionKey, itemId) {
|
|
82752
|
+
const queues = await state.sessionState.listQueuedItems({
|
|
82753
|
+
sessionKey,
|
|
82754
|
+
statuses: ["pending", "running"]
|
|
82755
|
+
});
|
|
82756
|
+
const index = queues.findIndex((item) => item.id === itemId);
|
|
82757
|
+
return index >= 0 ? index : 0;
|
|
82758
|
+
}
|
|
82759
|
+
function buildQueueCreatedMessageCommand(params) {
|
|
82760
|
+
return {
|
|
82761
|
+
action: "send",
|
|
82762
|
+
channel: params.context.channel,
|
|
82763
|
+
account: params.context.botId,
|
|
82764
|
+
target: params.context.target,
|
|
82765
|
+
message: params.text,
|
|
82766
|
+
threadId: params.context.threadId,
|
|
82767
|
+
remove: false,
|
|
82768
|
+
pollOptions: [],
|
|
82769
|
+
forceDocument: false,
|
|
82770
|
+
silent: false,
|
|
82771
|
+
progress: false,
|
|
82772
|
+
final: false,
|
|
82773
|
+
json: false,
|
|
82774
|
+
inputFormat: "plain",
|
|
82775
|
+
renderMode: "none"
|
|
82776
|
+
};
|
|
82777
|
+
}
|
|
82778
|
+
async function sendQueueCreatedNotificationToSurface(params) {
|
|
82779
|
+
if (!params.item.surfaceBinding) {
|
|
82780
|
+
return;
|
|
82781
|
+
}
|
|
82782
|
+
const loadedConfig = await loadConfig(params.state.configPath, {
|
|
82783
|
+
materializeChannels: [params.context.channel]
|
|
82784
|
+
});
|
|
82785
|
+
const plugin = listChannelPlugins().find((entry) => entry.id === params.context.channel);
|
|
82786
|
+
if (!plugin) {
|
|
82787
|
+
throw new Error(`Unsupported queue notification channel: ${params.context.channel}`);
|
|
82788
|
+
}
|
|
82789
|
+
await plugin.runMessageCommand(loadedConfig, buildQueueCreatedMessageCommand({
|
|
82790
|
+
context: params.context,
|
|
82791
|
+
text: params.text
|
|
82792
|
+
}));
|
|
82793
|
+
await params.state.sessionState.recordConversationReply(params.resolved);
|
|
82794
|
+
}
|
|
82422
82795
|
function renderQueueInventory(params) {
|
|
82423
82796
|
const lines = [
|
|
82424
82797
|
`Queue ${params.commandLabel}`,
|
|
@@ -82433,20 +82806,20 @@ function renderQueueInventory(params) {
|
|
|
82433
82806
|
return lines.join(`
|
|
82434
82807
|
`);
|
|
82435
82808
|
}
|
|
82436
|
-
async function listQueues(state, addressing, commandLabel) {
|
|
82809
|
+
async function listQueues(state, addressing, commandLabel, deps) {
|
|
82437
82810
|
const context = addressing.channel || addressing.target ? resolveScopedContext(state, addressing) : undefined;
|
|
82438
82811
|
const sessionKey = context?.sessionTarget.sessionKey;
|
|
82439
82812
|
const queues = await state.sessionState.listQueuedItems({
|
|
82440
82813
|
sessionKey,
|
|
82441
82814
|
statuses: commandLabel === "list" ? ["pending"] : ["pending", "running"]
|
|
82442
82815
|
});
|
|
82443
|
-
|
|
82816
|
+
deps.print(renderQueueInventory({
|
|
82444
82817
|
commandLabel,
|
|
82445
82818
|
sessionStorePath: state.sessionStorePath,
|
|
82446
82819
|
queues
|
|
82447
82820
|
}));
|
|
82448
82821
|
}
|
|
82449
|
-
async function createQueue(state, args) {
|
|
82822
|
+
async function createQueue(state, args, deps) {
|
|
82450
82823
|
const addressing = parseQueueCliAddressing(args);
|
|
82451
82824
|
const promptText = stripQueueArgs(args.slice(1)).join(" ").trim();
|
|
82452
82825
|
if (!promptText) {
|
|
@@ -82467,18 +82840,30 @@ async function createQueue(state, args) {
|
|
|
82467
82840
|
sender
|
|
82468
82841
|
});
|
|
82469
82842
|
await state.sessionState.setQueuedItem(resolved, item);
|
|
82470
|
-
|
|
82843
|
+
const positionAhead = await getQueuePositionAhead(state, context.sessionTarget.sessionKey, item.id);
|
|
82844
|
+
const text = renderQueueCreatedNotification({ positionAhead, promptText });
|
|
82845
|
+
await deps.sendQueueCreatedNotification({
|
|
82846
|
+
state,
|
|
82847
|
+
context,
|
|
82848
|
+
resolved,
|
|
82849
|
+
item,
|
|
82850
|
+
positionAhead,
|
|
82851
|
+
text
|
|
82852
|
+
}).catch((error) => {
|
|
82853
|
+
deps.warn(`Queued prompt ${item.id}, but surface acknowledgement failed: ${String(error)}`);
|
|
82854
|
+
});
|
|
82855
|
+
deps.print(`Queued prompt \`${item.id}\` for \`${context.sessionTarget.sessionKey}\`.`);
|
|
82471
82856
|
}
|
|
82472
|
-
async function clearQueues(state, addressing) {
|
|
82857
|
+
async function clearQueues(state, addressing, deps) {
|
|
82473
82858
|
if (addressing.all) {
|
|
82474
82859
|
const cleared2 = await state.sessionState.clearAllPendingQueuedItems();
|
|
82475
|
-
|
|
82860
|
+
deps.print(`Cleared ${cleared2.length} pending queued prompt${cleared2.length === 1 ? "" : "s"} across the whole app.`);
|
|
82476
82861
|
return;
|
|
82477
82862
|
}
|
|
82478
82863
|
const context = resolveScopedContext(state, addressing);
|
|
82479
82864
|
const sessionKey = context.sessionTarget.sessionKey;
|
|
82480
82865
|
const cleared = await state.sessionState.clearPendingQueuedItemsForSessionKey(sessionKey);
|
|
82481
|
-
|
|
82866
|
+
deps.print(`Cleared ${cleared.length} pending queued prompt${cleared.length === 1 ? "" : "s"} for \`${sessionKey}\`.`);
|
|
82482
82867
|
}
|
|
82483
82868
|
function renderQueuesHelp() {
|
|
82484
82869
|
return [
|
|
@@ -82499,23 +82884,24 @@ function renderQueuesHelp() {
|
|
|
82499
82884
|
].join(`
|
|
82500
82885
|
`);
|
|
82501
82886
|
}
|
|
82502
|
-
async function runQueuesCli(args) {
|
|
82887
|
+
async function runQueuesCli(args, dependencies = {}) {
|
|
82888
|
+
const deps = { ...defaultQueueCliDependencies, ...dependencies };
|
|
82503
82889
|
if (args[0] === "--help" || args[0] === "help" || args.length === 0) {
|
|
82504
|
-
|
|
82890
|
+
deps.print(renderQueuesHelp());
|
|
82505
82891
|
return;
|
|
82506
82892
|
}
|
|
82507
82893
|
const command = args[0];
|
|
82508
82894
|
const state = await loadQueueControlState();
|
|
82509
82895
|
if (command === "list" || command === "status") {
|
|
82510
|
-
await listQueues(state, parseQueueCliAddressing(args.slice(1)), command);
|
|
82896
|
+
await listQueues(state, parseQueueCliAddressing(args.slice(1)), command, deps);
|
|
82511
82897
|
return;
|
|
82512
82898
|
}
|
|
82513
82899
|
if (command === "create") {
|
|
82514
|
-
await createQueue(state, args);
|
|
82900
|
+
await createQueue(state, args, deps);
|
|
82515
82901
|
return;
|
|
82516
82902
|
}
|
|
82517
82903
|
if (command === "clear") {
|
|
82518
|
-
await clearQueues(state, parseQueueCliAddressing(args.slice(1)));
|
|
82904
|
+
await clearQueues(state, parseQueueCliAddressing(args.slice(1)), deps);
|
|
82519
82905
|
return;
|
|
82520
82906
|
}
|
|
82521
82907
|
throw new Error(`Unknown queues subcommand: ${command}`);
|
|
@@ -83604,10 +83990,9 @@ function renderWatchFrame(params) {
|
|
|
83604
83990
|
"",
|
|
83605
83991
|
`session: ${params.sessionName}`,
|
|
83606
83992
|
params.agentId ? `agent: ${params.agentId}` : null,
|
|
83607
|
-
|
|
83993
|
+
`sessionId: ${params.sessionId?.trim() || "none"}`,
|
|
83608
83994
|
`lines: ${params.lines}`,
|
|
83609
|
-
`
|
|
83610
|
-
`status: ${params.status}`,
|
|
83995
|
+
`state: ${params.state}`,
|
|
83611
83996
|
"",
|
|
83612
83997
|
params.snapshot.trimEnd() || "(empty pane)"
|
|
83613
83998
|
].filter((line) => Boolean(line)).join(`
|
|
@@ -83645,18 +84030,15 @@ function renderRunnerListSession(session) {
|
|
|
83645
84030
|
return [
|
|
83646
84031
|
prefix,
|
|
83647
84032
|
" sessionId: none",
|
|
83648
|
-
" state: unmanaged"
|
|
83649
|
-
" live: yes"
|
|
84033
|
+
" state: unmanaged"
|
|
83650
84034
|
].join(`
|
|
83651
84035
|
`);
|
|
83652
84036
|
}
|
|
83653
84037
|
return [
|
|
83654
84038
|
prefix,
|
|
83655
84039
|
` agent: ${session.entry.agentId}`,
|
|
83656
|
-
` sessionKey: ${session.entry.sessionKey}`,
|
|
83657
84040
|
` sessionId: ${session.entry.sessionId?.trim() || "none"}`,
|
|
83658
84041
|
` state: ${session.entry.runtime?.state ?? "no-runtime"}`,
|
|
83659
|
-
` live: ${session.live ? "yes" : "no"}`,
|
|
83660
84042
|
` lastAdmittedPromptAt: ${formatTimestamp(session.entry.lastAdmittedPromptAt)}`
|
|
83661
84043
|
].join(`
|
|
83662
84044
|
`);
|
|
@@ -83767,11 +84149,10 @@ async function runWatchCli(args) {
|
|
|
83767
84149
|
}
|
|
83768
84150
|
const frame = renderWatchFrame({
|
|
83769
84151
|
sessionName: selection.sessionName,
|
|
83770
|
-
|
|
84152
|
+
sessionId: selection.metadata?.entry.sessionId,
|
|
83771
84153
|
agentId: selection.metadata?.entry.agentId,
|
|
83772
84154
|
lines: options.lines,
|
|
83773
|
-
|
|
83774
|
-
status,
|
|
84155
|
+
state: status,
|
|
83775
84156
|
snapshot
|
|
83776
84157
|
});
|
|
83777
84158
|
if (process.stdout.isTTY) {
|
|
@@ -83887,6 +84268,7 @@ var GITHUB_RAW_BASE = "https://raw.githubusercontent.com/longbkit/clisbot/main";
|
|
|
83887
84268
|
function renderUpdateHelp() {
|
|
83888
84269
|
return [
|
|
83889
84270
|
`${renderCliCommand("update")} / ${renderCliCommand("update --help")}`,
|
|
84271
|
+
"Start here for any clisbot install or update request.",
|
|
83890
84272
|
"Prints this guide only. Direct update is not supported yet.",
|
|
83891
84273
|
"A bot can use this guide to update itself.",
|
|
83892
84274
|
"",
|
|
@@ -83897,7 +84279,7 @@ function renderUpdateHelp() {
|
|
|
83897
84279
|
"",
|
|
83898
84280
|
"Flow:",
|
|
83899
84281
|
` 1. ${renderCliCommand("status")}`,
|
|
83900
|
-
" 2. Read docs in priority order.",
|
|
84282
|
+
" 2. Read docs in priority order and follow them before installing.",
|
|
83901
84283
|
" 3. npm install -g clisbot@<target> && clisbot restart",
|
|
83902
84284
|
` 4. ${renderCliCommand("status")}`,
|
|
83903
84285
|
" 5. Report version, health, manual action, and useful release-note highlights.",
|
|
@@ -85727,11 +86109,25 @@ async function stop(hard = false) {
|
|
|
85727
86109
|
console.log("clisbot stopped");
|
|
85728
86110
|
printCommandOutcomeFooter("success");
|
|
85729
86111
|
}
|
|
85730
|
-
async function restart(
|
|
85731
|
-
|
|
85732
|
-
|
|
85733
|
-
|
|
85734
|
-
|
|
86112
|
+
async function restart(dependencies = {
|
|
86113
|
+
stopDetachedRuntime,
|
|
86114
|
+
getRuntimeStatus,
|
|
86115
|
+
warn: (message) => console.error(message)
|
|
86116
|
+
}) {
|
|
86117
|
+
const configPath = getOperatorConfigPath();
|
|
86118
|
+
try {
|
|
86119
|
+
await dependencies.stopDetachedRuntime({
|
|
86120
|
+
configPath,
|
|
86121
|
+
hard: false
|
|
86122
|
+
});
|
|
86123
|
+
} catch (error) {
|
|
86124
|
+
const status = await dependencies.getRuntimeStatus({ configPath });
|
|
86125
|
+
if (status.running) {
|
|
86126
|
+
throw error;
|
|
86127
|
+
}
|
|
86128
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86129
|
+
dependencies.warn(`warning: clisbot stop reported an error, but status now shows the service is stopped; continuing with start. Stop error: ${message}`);
|
|
86130
|
+
}
|
|
85735
86131
|
}
|
|
85736
86132
|
async function status() {
|
|
85737
86133
|
await printStatusSummary();
|