clisbot 0.1.46-beta.0 → 0.1.46-beta.1
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 +494 -100
- 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, {
|
|
@@ -71216,7 +71531,7 @@ function renderConfigurationGuidance() {
|
|
|
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.`,
|
|
71218
71533
|
`For schedule/loop/reminder requests, inspect ${renderCliCommand("loops --help", { inline: true })} and use the loops CLI.`,
|
|
71219
|
-
`For durable queue
|
|
71534
|
+
`For durable queue requests, inspect ${renderCliCommand("queues --help", { inline: true })} and use the queues CLI.`
|
|
71220
71535
|
].join(`
|
|
71221
71536
|
`);
|
|
71222
71537
|
}
|
|
@@ -71722,8 +72037,9 @@ class SurfaceRuntime {
|
|
|
71722
72037
|
}
|
|
71723
72038
|
const identity = this.buildLoopChannelIdentity(loop);
|
|
71724
72039
|
const notifications = this.resolveSurfaceNotifications(identity);
|
|
72040
|
+
const mode = loop.loopStart ?? notifications.loopStart;
|
|
71725
72041
|
const text = loop.kind === "calendar" ? renderLoopStartNotification({
|
|
71726
|
-
mode
|
|
72042
|
+
mode,
|
|
71727
72043
|
agentId: target.agentId,
|
|
71728
72044
|
loopId: loop.id,
|
|
71729
72045
|
promptSummary: loop.promptSummary,
|
|
@@ -71736,7 +72052,7 @@ class SurfaceRuntime {
|
|
|
71736
72052
|
maxRuns: loop.maxRuns,
|
|
71737
72053
|
kind: "calendar"
|
|
71738
72054
|
}) : renderLoopStartNotification({
|
|
71739
|
-
mode
|
|
72055
|
+
mode,
|
|
71740
72056
|
agentId: target.agentId,
|
|
71741
72057
|
loopId: loop.id,
|
|
71742
72058
|
promptSummary: loop.promptSummary,
|
|
@@ -72150,7 +72466,12 @@ class AgentService {
|
|
|
72150
72466
|
return this.runnerSessions.captureTranscript(target);
|
|
72151
72467
|
}
|
|
72152
72468
|
async interruptSession(target) {
|
|
72153
|
-
|
|
72469
|
+
const runner = await this.runnerSessions.interruptSession(target);
|
|
72470
|
+
const activeRun = await this.activeRuns.interruptActiveRun(target);
|
|
72471
|
+
return {
|
|
72472
|
+
...runner,
|
|
72473
|
+
interrupted: runner.interrupted || activeRun.interrupted
|
|
72474
|
+
};
|
|
72154
72475
|
}
|
|
72155
72476
|
async nudgeSession(target) {
|
|
72156
72477
|
return this.runnerSessions.nudgeSession(target);
|
|
@@ -72167,13 +72488,16 @@ class AgentService {
|
|
|
72167
72488
|
async getSessionDiagnostics(target) {
|
|
72168
72489
|
const resolved = this.resolveTarget(target);
|
|
72169
72490
|
const entry = await this.sessionState.getEntry(target.sessionKey);
|
|
72170
|
-
const
|
|
72491
|
+
const storedSessionId = entry?.sessionId?.trim() || undefined;
|
|
72171
72492
|
return {
|
|
72172
|
-
|
|
72173
|
-
|
|
72493
|
+
sessionName: resolved.sessionName,
|
|
72494
|
+
sessionId: storedSessionId,
|
|
72495
|
+
storedSessionId,
|
|
72496
|
+
resumeCommand: buildResumeCommandPreview(resolved, storedSessionId)
|
|
72174
72497
|
};
|
|
72175
72498
|
}
|
|
72176
72499
|
async listActiveSessionRuntimes() {
|
|
72500
|
+
await this.activeRuns.clearLostPersistedActiveRuns();
|
|
72177
72501
|
return this.sessionState.listActiveSessionRuntimes();
|
|
72178
72502
|
}
|
|
72179
72503
|
async setConversationFollowUpMode(target, mode) {
|
|
@@ -72593,30 +72917,16 @@ function renderStartupSteeringUnavailableMessage() {
|
|
|
72593
72917
|
].join(`
|
|
72594
72918
|
`);
|
|
72595
72919
|
}
|
|
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
72920
|
function renderWhoAmIMessage(params) {
|
|
72921
|
+
const storedSessionId = params.sessionDiagnostics.storedSessionId ?? params.sessionDiagnostics.sessionId;
|
|
72612
72922
|
const lines = [
|
|
72613
72923
|
"Who am I",
|
|
72614
72924
|
"",
|
|
72615
72925
|
`platform: \`${params.identity.platform}\``,
|
|
72616
72926
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
72617
72927
|
`agentId: \`${params.route.agentId}\``,
|
|
72618
|
-
`
|
|
72619
|
-
`storedSessionId: \`${
|
|
72928
|
+
`sessionName: \`${params.sessionDiagnostics.sessionName ?? "(not available)"}\``,
|
|
72929
|
+
`storedSessionId: \`${storedSessionId ?? "(not captured yet)"}\``
|
|
72620
72930
|
];
|
|
72621
72931
|
if (params.identity.senderId) {
|
|
72622
72932
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -72633,19 +72943,20 @@ function renderWhoAmIMessage(params) {
|
|
|
72633
72943
|
if (params.identity.topicId) {
|
|
72634
72944
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
72635
72945
|
}
|
|
72636
|
-
lines.push(`resumeCommand: \`${params.sessionDiagnostics.resumeCommand ?? "(not available yet)"}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `
|
|
72946
|
+
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
72947
|
return lines.join(`
|
|
72638
72948
|
`);
|
|
72639
72949
|
}
|
|
72640
72950
|
function renderRouteStatusMessage(params) {
|
|
72951
|
+
const storedSessionId = params.sessionDiagnostics.storedSessionId ?? params.sessionDiagnostics.sessionId;
|
|
72641
72952
|
const lines = [
|
|
72642
72953
|
"Status",
|
|
72643
72954
|
"",
|
|
72644
72955
|
`platform: \`${params.identity.platform}\``,
|
|
72645
72956
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
72646
72957
|
`agentId: \`${params.route.agentId}\``,
|
|
72647
|
-
`
|
|
72648
|
-
`storedSessionId: \`${
|
|
72958
|
+
`sessionName: \`${params.sessionDiagnostics.sessionName ?? "(not available)"}\``,
|
|
72959
|
+
`storedSessionId: \`${storedSessionId ?? "(not captured yet)"}\``
|
|
72649
72960
|
];
|
|
72650
72961
|
if (params.identity.senderId) {
|
|
72651
72962
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -72662,7 +72973,7 @@ function renderRouteStatusMessage(params) {
|
|
|
72662
72973
|
if (params.identity.topicId) {
|
|
72663
72974
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
72664
72975
|
}
|
|
72665
|
-
lines.push(`resumeCommand: \`${params.sessionDiagnostics.resumeCommand ?? "(not available yet)"}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `
|
|
72976
|
+
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
72977
|
if (params.runtimeState.startedAt) {
|
|
72667
72978
|
lines.push(`run.startedAt: \`${new Date(params.runtimeState.startedAt).toISOString()}\``);
|
|
72668
72979
|
}
|
|
@@ -73823,6 +74134,7 @@ ${renderLoopUsage()}`);
|
|
|
73823
74134
|
canonicalPromptText: resolvedLoopPrompt.text,
|
|
73824
74135
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
73825
74136
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
74137
|
+
loopStart: slashCommand.params.loopStart,
|
|
73826
74138
|
surfaceBinding: buildLoopSurfaceBinding(params.identity),
|
|
73827
74139
|
cadence: slashCommand.params.cadence,
|
|
73828
74140
|
dayOfWeek: slashCommand.params.dayOfWeek,
|
|
@@ -73862,6 +74174,7 @@ ${renderLoopUsage()}`);
|
|
|
73862
74174
|
canonicalPromptText: resolvedLoopPrompt.text,
|
|
73863
74175
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
73864
74176
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
74177
|
+
loopStart: slashCommand.params.loopStart,
|
|
73865
74178
|
surfaceBinding: buildLoopSurfaceBinding(params.identity),
|
|
73866
74179
|
intervalMs: effectiveIntervalMs,
|
|
73867
74180
|
maxRuns: maxRunsPerLoop,
|
|
@@ -77955,6 +78268,7 @@ var TELEGRAM_FULL_COMMANDS = [
|
|
|
77955
78268
|
{ command: "detach", description: "Stop live updates for this thread" },
|
|
77956
78269
|
{ command: "watch", description: "Watch the active run on an interval" },
|
|
77957
78270
|
{ command: "stop", description: "Interrupt current run" },
|
|
78271
|
+
{ command: "new", description: "Start new session" },
|
|
77958
78272
|
{ command: "nudge", description: "Send one extra Enter to the session" },
|
|
77959
78273
|
{ command: "followup", description: "Show or change follow-up mode" },
|
|
77960
78274
|
{ command: "mention", description: "Require explicit mention for later turns" },
|
|
@@ -81143,6 +81457,7 @@ function renderLoopsCreateHelp() {
|
|
|
81143
81457
|
" - `--new-thread` creates a Slack thread anchor before persisting the loop",
|
|
81144
81458
|
" - `--timezone <iana>` freezes a one-off wall-clock timezone on the loop record",
|
|
81145
81459
|
" - `--confirm` persists the first wall-clock loop after reviewing the confirmation output",
|
|
81460
|
+
` - advanced: \`${LOOP_START_FLAG} <none|brief|full>\` overrides the default scheduled loop-start notification behavior for that recurring loop`,
|
|
81146
81461
|
"",
|
|
81147
81462
|
"Examples:",
|
|
81148
81463
|
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --sender slack:U1234567890 every day at 07:00 check CI")}`,
|
|
@@ -81749,6 +82064,7 @@ function buildRecurringLoopPromptMetadata(request) {
|
|
|
81749
82064
|
canonicalPromptText: request.resolvedPrompt.text,
|
|
81750
82065
|
promptSummary: summarizeLoopPrompt(request.resolvedPrompt.text, request.resolvedPrompt.maintenancePrompt),
|
|
81751
82066
|
promptSource: request.resolvedPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
82067
|
+
loopStart: request.parsed.mode === "times" ? undefined : request.parsed.loopStart,
|
|
81752
82068
|
maintenancePrompt: request.resolvedPrompt.maintenancePrompt,
|
|
81753
82069
|
createdBy: request.creator.providerId,
|
|
81754
82070
|
sender: request.creator,
|
|
@@ -82254,6 +82570,11 @@ async function runMessageCli(args, dependencies = defaultMessageCliDependencies)
|
|
|
82254
82570
|
var QUEUE_SENDER_FLAG = "--sender";
|
|
82255
82571
|
var QUEUE_SENDER_NAME_FLAG = "--sender-name";
|
|
82256
82572
|
var QUEUE_SENDER_HANDLE_FLAG = "--sender-handle";
|
|
82573
|
+
var defaultQueueCliDependencies = {
|
|
82574
|
+
print: (text) => console.log(text),
|
|
82575
|
+
warn: (text) => console.warn(text),
|
|
82576
|
+
sendQueueCreatedNotification: sendQueueCreatedNotificationToSurface
|
|
82577
|
+
};
|
|
82257
82578
|
function getEditableConfigPath9() {
|
|
82258
82579
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
82259
82580
|
}
|
|
@@ -82419,6 +82740,57 @@ function createQueueItemForContext(params) {
|
|
|
82419
82740
|
surfaceBinding: buildQueueSurfaceBinding(params.context)
|
|
82420
82741
|
});
|
|
82421
82742
|
}
|
|
82743
|
+
function renderQueueCreatedNotification(params) {
|
|
82744
|
+
const queueLine = params.positionAhead > 0 ? `Queued: ${params.positionAhead} ahead.` : "Queued.";
|
|
82745
|
+
return `${queueLine}
|
|
82746
|
+
|
|
82747
|
+
Prompt:
|
|
82748
|
+
${params.promptText.trim()}`;
|
|
82749
|
+
}
|
|
82750
|
+
async function getQueuePositionAhead(state, sessionKey, itemId) {
|
|
82751
|
+
const queues = await state.sessionState.listQueuedItems({
|
|
82752
|
+
sessionKey,
|
|
82753
|
+
statuses: ["pending", "running"]
|
|
82754
|
+
});
|
|
82755
|
+
const index = queues.findIndex((item) => item.id === itemId);
|
|
82756
|
+
return index >= 0 ? index : 0;
|
|
82757
|
+
}
|
|
82758
|
+
function buildQueueCreatedMessageCommand(params) {
|
|
82759
|
+
return {
|
|
82760
|
+
action: "send",
|
|
82761
|
+
channel: params.context.channel,
|
|
82762
|
+
account: params.context.botId,
|
|
82763
|
+
target: params.context.target,
|
|
82764
|
+
message: params.text,
|
|
82765
|
+
threadId: params.context.threadId,
|
|
82766
|
+
remove: false,
|
|
82767
|
+
pollOptions: [],
|
|
82768
|
+
forceDocument: false,
|
|
82769
|
+
silent: false,
|
|
82770
|
+
progress: false,
|
|
82771
|
+
final: false,
|
|
82772
|
+
json: false,
|
|
82773
|
+
inputFormat: "plain",
|
|
82774
|
+
renderMode: "none"
|
|
82775
|
+
};
|
|
82776
|
+
}
|
|
82777
|
+
async function sendQueueCreatedNotificationToSurface(params) {
|
|
82778
|
+
if (!params.item.surfaceBinding) {
|
|
82779
|
+
return;
|
|
82780
|
+
}
|
|
82781
|
+
const loadedConfig = await loadConfig(params.state.configPath, {
|
|
82782
|
+
materializeChannels: [params.context.channel]
|
|
82783
|
+
});
|
|
82784
|
+
const plugin = listChannelPlugins().find((entry) => entry.id === params.context.channel);
|
|
82785
|
+
if (!plugin) {
|
|
82786
|
+
throw new Error(`Unsupported queue notification channel: ${params.context.channel}`);
|
|
82787
|
+
}
|
|
82788
|
+
await plugin.runMessageCommand(loadedConfig, buildQueueCreatedMessageCommand({
|
|
82789
|
+
context: params.context,
|
|
82790
|
+
text: params.text
|
|
82791
|
+
}));
|
|
82792
|
+
await params.state.sessionState.recordConversationReply(params.resolved);
|
|
82793
|
+
}
|
|
82422
82794
|
function renderQueueInventory(params) {
|
|
82423
82795
|
const lines = [
|
|
82424
82796
|
`Queue ${params.commandLabel}`,
|
|
@@ -82433,20 +82805,20 @@ function renderQueueInventory(params) {
|
|
|
82433
82805
|
return lines.join(`
|
|
82434
82806
|
`);
|
|
82435
82807
|
}
|
|
82436
|
-
async function listQueues(state, addressing, commandLabel) {
|
|
82808
|
+
async function listQueues(state, addressing, commandLabel, deps) {
|
|
82437
82809
|
const context = addressing.channel || addressing.target ? resolveScopedContext(state, addressing) : undefined;
|
|
82438
82810
|
const sessionKey = context?.sessionTarget.sessionKey;
|
|
82439
82811
|
const queues = await state.sessionState.listQueuedItems({
|
|
82440
82812
|
sessionKey,
|
|
82441
82813
|
statuses: commandLabel === "list" ? ["pending"] : ["pending", "running"]
|
|
82442
82814
|
});
|
|
82443
|
-
|
|
82815
|
+
deps.print(renderQueueInventory({
|
|
82444
82816
|
commandLabel,
|
|
82445
82817
|
sessionStorePath: state.sessionStorePath,
|
|
82446
82818
|
queues
|
|
82447
82819
|
}));
|
|
82448
82820
|
}
|
|
82449
|
-
async function createQueue(state, args) {
|
|
82821
|
+
async function createQueue(state, args, deps) {
|
|
82450
82822
|
const addressing = parseQueueCliAddressing(args);
|
|
82451
82823
|
const promptText = stripQueueArgs(args.slice(1)).join(" ").trim();
|
|
82452
82824
|
if (!promptText) {
|
|
@@ -82467,18 +82839,30 @@ async function createQueue(state, args) {
|
|
|
82467
82839
|
sender
|
|
82468
82840
|
});
|
|
82469
82841
|
await state.sessionState.setQueuedItem(resolved, item);
|
|
82470
|
-
|
|
82842
|
+
const positionAhead = await getQueuePositionAhead(state, context.sessionTarget.sessionKey, item.id);
|
|
82843
|
+
const text = renderQueueCreatedNotification({ positionAhead, promptText });
|
|
82844
|
+
await deps.sendQueueCreatedNotification({
|
|
82845
|
+
state,
|
|
82846
|
+
context,
|
|
82847
|
+
resolved,
|
|
82848
|
+
item,
|
|
82849
|
+
positionAhead,
|
|
82850
|
+
text
|
|
82851
|
+
}).catch((error) => {
|
|
82852
|
+
deps.warn(`Queued prompt ${item.id}, but surface acknowledgement failed: ${String(error)}`);
|
|
82853
|
+
});
|
|
82854
|
+
deps.print(`Queued prompt \`${item.id}\` for \`${context.sessionTarget.sessionKey}\`.`);
|
|
82471
82855
|
}
|
|
82472
|
-
async function clearQueues(state, addressing) {
|
|
82856
|
+
async function clearQueues(state, addressing, deps) {
|
|
82473
82857
|
if (addressing.all) {
|
|
82474
82858
|
const cleared2 = await state.sessionState.clearAllPendingQueuedItems();
|
|
82475
|
-
|
|
82859
|
+
deps.print(`Cleared ${cleared2.length} pending queued prompt${cleared2.length === 1 ? "" : "s"} across the whole app.`);
|
|
82476
82860
|
return;
|
|
82477
82861
|
}
|
|
82478
82862
|
const context = resolveScopedContext(state, addressing);
|
|
82479
82863
|
const sessionKey = context.sessionTarget.sessionKey;
|
|
82480
82864
|
const cleared = await state.sessionState.clearPendingQueuedItemsForSessionKey(sessionKey);
|
|
82481
|
-
|
|
82865
|
+
deps.print(`Cleared ${cleared.length} pending queued prompt${cleared.length === 1 ? "" : "s"} for \`${sessionKey}\`.`);
|
|
82482
82866
|
}
|
|
82483
82867
|
function renderQueuesHelp() {
|
|
82484
82868
|
return [
|
|
@@ -82499,23 +82883,24 @@ function renderQueuesHelp() {
|
|
|
82499
82883
|
].join(`
|
|
82500
82884
|
`);
|
|
82501
82885
|
}
|
|
82502
|
-
async function runQueuesCli(args) {
|
|
82886
|
+
async function runQueuesCli(args, dependencies = {}) {
|
|
82887
|
+
const deps = { ...defaultQueueCliDependencies, ...dependencies };
|
|
82503
82888
|
if (args[0] === "--help" || args[0] === "help" || args.length === 0) {
|
|
82504
|
-
|
|
82889
|
+
deps.print(renderQueuesHelp());
|
|
82505
82890
|
return;
|
|
82506
82891
|
}
|
|
82507
82892
|
const command = args[0];
|
|
82508
82893
|
const state = await loadQueueControlState();
|
|
82509
82894
|
if (command === "list" || command === "status") {
|
|
82510
|
-
await listQueues(state, parseQueueCliAddressing(args.slice(1)), command);
|
|
82895
|
+
await listQueues(state, parseQueueCliAddressing(args.slice(1)), command, deps);
|
|
82511
82896
|
return;
|
|
82512
82897
|
}
|
|
82513
82898
|
if (command === "create") {
|
|
82514
|
-
await createQueue(state, args);
|
|
82899
|
+
await createQueue(state, args, deps);
|
|
82515
82900
|
return;
|
|
82516
82901
|
}
|
|
82517
82902
|
if (command === "clear") {
|
|
82518
|
-
await clearQueues(state, parseQueueCliAddressing(args.slice(1)));
|
|
82903
|
+
await clearQueues(state, parseQueueCliAddressing(args.slice(1)), deps);
|
|
82519
82904
|
return;
|
|
82520
82905
|
}
|
|
82521
82906
|
throw new Error(`Unknown queues subcommand: ${command}`);
|
|
@@ -83604,10 +83989,9 @@ function renderWatchFrame(params) {
|
|
|
83604
83989
|
"",
|
|
83605
83990
|
`session: ${params.sessionName}`,
|
|
83606
83991
|
params.agentId ? `agent: ${params.agentId}` : null,
|
|
83607
|
-
|
|
83992
|
+
`sessionId: ${params.sessionId?.trim() || "none"}`,
|
|
83608
83993
|
`lines: ${params.lines}`,
|
|
83609
|
-
`
|
|
83610
|
-
`status: ${params.status}`,
|
|
83994
|
+
`state: ${params.state}`,
|
|
83611
83995
|
"",
|
|
83612
83996
|
params.snapshot.trimEnd() || "(empty pane)"
|
|
83613
83997
|
].filter((line) => Boolean(line)).join(`
|
|
@@ -83645,18 +84029,15 @@ function renderRunnerListSession(session) {
|
|
|
83645
84029
|
return [
|
|
83646
84030
|
prefix,
|
|
83647
84031
|
" sessionId: none",
|
|
83648
|
-
" state: unmanaged"
|
|
83649
|
-
" live: yes"
|
|
84032
|
+
" state: unmanaged"
|
|
83650
84033
|
].join(`
|
|
83651
84034
|
`);
|
|
83652
84035
|
}
|
|
83653
84036
|
return [
|
|
83654
84037
|
prefix,
|
|
83655
84038
|
` agent: ${session.entry.agentId}`,
|
|
83656
|
-
` sessionKey: ${session.entry.sessionKey}`,
|
|
83657
84039
|
` sessionId: ${session.entry.sessionId?.trim() || "none"}`,
|
|
83658
84040
|
` state: ${session.entry.runtime?.state ?? "no-runtime"}`,
|
|
83659
|
-
` live: ${session.live ? "yes" : "no"}`,
|
|
83660
84041
|
` lastAdmittedPromptAt: ${formatTimestamp(session.entry.lastAdmittedPromptAt)}`
|
|
83661
84042
|
].join(`
|
|
83662
84043
|
`);
|
|
@@ -83767,11 +84148,10 @@ async function runWatchCli(args) {
|
|
|
83767
84148
|
}
|
|
83768
84149
|
const frame = renderWatchFrame({
|
|
83769
84150
|
sessionName: selection.sessionName,
|
|
83770
|
-
|
|
84151
|
+
sessionId: selection.metadata?.entry.sessionId,
|
|
83771
84152
|
agentId: selection.metadata?.entry.agentId,
|
|
83772
84153
|
lines: options.lines,
|
|
83773
|
-
|
|
83774
|
-
status,
|
|
84154
|
+
state: status,
|
|
83775
84155
|
snapshot
|
|
83776
84156
|
});
|
|
83777
84157
|
if (process.stdout.isTTY) {
|
|
@@ -85727,11 +86107,25 @@ async function stop(hard = false) {
|
|
|
85727
86107
|
console.log("clisbot stopped");
|
|
85728
86108
|
printCommandOutcomeFooter("success");
|
|
85729
86109
|
}
|
|
85730
|
-
async function restart(
|
|
85731
|
-
|
|
85732
|
-
|
|
85733
|
-
|
|
85734
|
-
|
|
86110
|
+
async function restart(dependencies = {
|
|
86111
|
+
stopDetachedRuntime,
|
|
86112
|
+
getRuntimeStatus,
|
|
86113
|
+
warn: (message) => console.error(message)
|
|
86114
|
+
}) {
|
|
86115
|
+
const configPath = getOperatorConfigPath();
|
|
86116
|
+
try {
|
|
86117
|
+
await dependencies.stopDetachedRuntime({
|
|
86118
|
+
configPath,
|
|
86119
|
+
hard: false
|
|
86120
|
+
});
|
|
86121
|
+
} catch (error) {
|
|
86122
|
+
const status = await dependencies.getRuntimeStatus({ configPath });
|
|
86123
|
+
if (status.running) {
|
|
86124
|
+
throw error;
|
|
86125
|
+
}
|
|
86126
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86127
|
+
dependencies.warn(`warning: clisbot stop reported an error, but status now shows the service is stopped; continuing with start. Stop error: ${message}`);
|
|
86128
|
+
}
|
|
85735
86129
|
}
|
|
85736
86130
|
async function status() {
|
|
85737
86131
|
await printStatusSummary();
|