clisbot 0.1.45-beta.7 → 0.1.45-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/main.js +241 -181
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -139,7 +139,7 @@ Need the step-by-step setup docs instead of the shortest path?
|
|
|
139
139
|
|
|
140
140
|
- Telegram: [Telegram Bot Setup](docs/user-guide/telegram-setup.md)
|
|
141
141
|
- Slack: [Slack App Setup](docs/user-guide/slack-setup.md)
|
|
142
|
-
- Release history: [CHANGELOG.md](CHANGELOG.md), [release notes](docs/releases/README.md), [update guide](docs/update
|
|
142
|
+
- Release history: [CHANGELOG.md](CHANGELOG.md), [release notes](docs/releases/README.md), [update guide](docs/updates/update-guide.md), [release guides](docs/updates/README.md), and [migration index](docs/migrations/index.md)
|
|
143
143
|
- Slack app manifest template: [app-manifest.json](templates/slack/default/app-manifest.json)
|
|
144
144
|
- Slack app manifest guide: [app-manifest-guide.md](templates/slack/default/app-manifest-guide.md)
|
|
145
145
|
|
|
@@ -320,7 +320,7 @@ If the quick start does not work, check these in order:
|
|
|
320
320
|
trust_level = "trusted"
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
-
- If that trust screen is still blocking,
|
|
323
|
+
- If that trust screen is still blocking, inspect the live session name with `clisbot runner list`, then attach directly with `tmux -S ~/.clisbot/state/clisbot.sock attach -t <session-name>`.
|
|
324
324
|
- If Gemini startup says it is waiting for manual authorization, authenticate Gemini directly first or provide a headless auth path such as `GEMINI_API_KEY` or Vertex AI credentials; `clisbot` now treats that screen as a startup blocker instead of a healthy ready session.
|
|
325
325
|
- If Codex warns that `bubblewrap` is missing on Linux, install `bubblewrap` in the runtime environment.
|
|
326
326
|
- If the bot does not answer, check `clisbot status` first. Healthy channels should show `connection=active`; if a channel stays `starting`, inspect `clisbot logs`.
|
package/dist/main.js
CHANGED
|
@@ -32451,7 +32451,7 @@ var require_websocket = __commonJS((exports, module) => {
|
|
|
32451
32451
|
var http = __require("http");
|
|
32452
32452
|
var net = __require("net");
|
|
32453
32453
|
var tls = __require("tls");
|
|
32454
|
-
var { randomBytes, createHash } = __require("crypto");
|
|
32454
|
+
var { randomBytes, createHash: createHash2 } = __require("crypto");
|
|
32455
32455
|
var { Duplex, Readable } = __require("stream");
|
|
32456
32456
|
var { URL: URL2 } = __require("url");
|
|
32457
32457
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -32990,7 +32990,7 @@ var require_websocket = __commonJS((exports, module) => {
|
|
|
32990
32990
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
32991
32991
|
return;
|
|
32992
32992
|
}
|
|
32993
|
-
const digest =
|
|
32993
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
32994
32994
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
32995
32995
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
32996
32996
|
return;
|
|
@@ -33363,7 +33363,7 @@ var require_websocket_server = __commonJS((exports, module) => {
|
|
|
33363
33363
|
var EventEmitter = __require("events");
|
|
33364
33364
|
var http = __require("http");
|
|
33365
33365
|
var { Duplex } = __require("stream");
|
|
33366
|
-
var { createHash } = __require("crypto");
|
|
33366
|
+
var { createHash: createHash2 } = __require("crypto");
|
|
33367
33367
|
var extension = require_extension();
|
|
33368
33368
|
var PerMessageDeflate = require_permessage_deflate();
|
|
33369
33369
|
var subprotocol = require_subprotocol();
|
|
@@ -33576,7 +33576,7 @@ var require_websocket_server = __commonJS((exports, module) => {
|
|
|
33576
33576
|
}
|
|
33577
33577
|
if (this._state > RUNNING)
|
|
33578
33578
|
return abortHandshake(socket, 503);
|
|
33579
|
-
const digest =
|
|
33579
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
33580
33580
|
const headers = [
|
|
33581
33581
|
"HTTP/1.1 101 Switching Protocols",
|
|
33582
33582
|
"Upgrade: websocket",
|
|
@@ -54803,7 +54803,7 @@ function renderCliHelp() {
|
|
|
54803
54803
|
" status Show runtime process, config, log, tmux socket status, and recent runner sessions.",
|
|
54804
54804
|
" version Show the installed clisbot version.",
|
|
54805
54805
|
" logs Print the most recent clisbot log lines.",
|
|
54806
|
-
" update Print the
|
|
54806
|
+
" update Print the update guide and release/migration doc links.",
|
|
54807
54807
|
` See ${renderCliCommand("update --help", { inline: true })} before asking an agent to update clisbot.`,
|
|
54808
54808
|
" timezone Manage the app-wide wall-clock timezone used by schedules and loops.",
|
|
54809
54809
|
` See ${renderCliCommand("timezone --help", { inline: true })} for override guidance.`,
|
|
@@ -65267,14 +65267,20 @@ class TmuxClient {
|
|
|
65267
65267
|
}
|
|
65268
65268
|
return result.stdout;
|
|
65269
65269
|
}
|
|
65270
|
+
sessionTarget(sessionName) {
|
|
65271
|
+
return `=${sessionName}`;
|
|
65272
|
+
}
|
|
65270
65273
|
target(sessionName) {
|
|
65271
|
-
return `${sessionName}:${MAIN_WINDOW_NAME}`;
|
|
65274
|
+
return `${this.sessionTarget(sessionName)}:${MAIN_WINDOW_NAME}`;
|
|
65275
|
+
}
|
|
65276
|
+
windowTarget(sessionName, windowName) {
|
|
65277
|
+
return `${this.sessionTarget(sessionName)}:${windowName}`;
|
|
65272
65278
|
}
|
|
65273
65279
|
rawTarget(target) {
|
|
65274
65280
|
return target;
|
|
65275
65281
|
}
|
|
65276
65282
|
async hasSession(sessionName) {
|
|
65277
|
-
const result = await this.exec(["has-session", "-t", sessionName]);
|
|
65283
|
+
const result = await this.exec(["has-session", "-t", this.sessionTarget(sessionName)]);
|
|
65278
65284
|
return result.exitCode === 0;
|
|
65279
65285
|
}
|
|
65280
65286
|
async listSessions() {
|
|
@@ -65370,14 +65376,14 @@ ${result.stdout}`.trim();
|
|
|
65370
65376
|
"-F",
|
|
65371
65377
|
"#{pane_id}",
|
|
65372
65378
|
"-t",
|
|
65373
|
-
params.sessionName,
|
|
65379
|
+
this.sessionTarget(params.sessionName),
|
|
65374
65380
|
"-n",
|
|
65375
65381
|
params.name,
|
|
65376
65382
|
"-c",
|
|
65377
65383
|
params.cwd,
|
|
65378
65384
|
params.command
|
|
65379
65385
|
]);
|
|
65380
|
-
await this.freezeWindowName(
|
|
65386
|
+
await this.freezeWindowName(this.windowTarget(params.sessionName, params.name));
|
|
65381
65387
|
return paneId.trim();
|
|
65382
65388
|
}
|
|
65383
65389
|
async freezeWindowName(target) {
|
|
@@ -65388,7 +65394,7 @@ ${result.stdout}`.trim();
|
|
|
65388
65394
|
const output = await this.execOrThrow([
|
|
65389
65395
|
"list-windows",
|
|
65390
65396
|
"-t",
|
|
65391
|
-
sessionName,
|
|
65397
|
+
this.sessionTarget(sessionName),
|
|
65392
65398
|
"-F",
|
|
65393
65399
|
"#{window_name}\t#{pane_id}"
|
|
65394
65400
|
]);
|
|
@@ -65464,7 +65470,7 @@ ${result.stdout}`.trim();
|
|
|
65464
65470
|
};
|
|
65465
65471
|
}
|
|
65466
65472
|
async killSession(sessionName) {
|
|
65467
|
-
await this.exec(["kill-session", "-t", sessionName]);
|
|
65473
|
+
await this.exec(["kill-session", "-t", this.sessionTarget(sessionName)]);
|
|
65468
65474
|
}
|
|
65469
65475
|
async killPane(target) {
|
|
65470
65476
|
await this.exec(["kill-pane", "-t", this.rawTarget(target)]);
|
|
@@ -66541,19 +66547,6 @@ class AgentSessionState {
|
|
|
66541
66547
|
agentId: target.agentId
|
|
66542
66548
|
};
|
|
66543
66549
|
}
|
|
66544
|
-
async listActiveSessionRuntimes() {
|
|
66545
|
-
const entries = await this.sessionStore.list();
|
|
66546
|
-
return entries.filter(hasActiveRuntime).map((entry) => ({
|
|
66547
|
-
state: entry.runtime.state,
|
|
66548
|
-
startedAt: entry.runtime.startedAt,
|
|
66549
|
-
detachedAt: entry.runtime.detachedAt,
|
|
66550
|
-
finalReplyAt: entry.runtime.finalReplyAt,
|
|
66551
|
-
lastMessageToolReplyAt: entry.runtime.lastMessageToolReplyAt,
|
|
66552
|
-
messageToolFinalReplyAt: entry.runtime.messageToolFinalReplyAt,
|
|
66553
|
-
sessionKey: entry.sessionKey,
|
|
66554
|
-
agentId: entry.agentId
|
|
66555
|
-
}));
|
|
66556
|
-
}
|
|
66557
66550
|
async listIntervalLoops(params) {
|
|
66558
66551
|
const entries = await this.sessionStore.list();
|
|
66559
66552
|
return entries.flatMap((entry) => getStoredLoops(entry).filter((loop) => !params?.sessionKey || entry.sessionKey === params.sessionKey).map((loop) => ({
|
|
@@ -66811,6 +66804,10 @@ class AgentSessionState {
|
|
|
66811
66804
|
const entry = await this.sessionStore.get(sessionKey);
|
|
66812
66805
|
return getStoredQueues(entry).some((item) => item.status === "pending" || item.status === "running");
|
|
66813
66806
|
}
|
|
66807
|
+
async hasQueuedItem(sessionKey, queueId) {
|
|
66808
|
+
const entry = await this.sessionStore.get(sessionKey);
|
|
66809
|
+
return getStoredQueues(entry).some((item) => item.id === queueId);
|
|
66810
|
+
}
|
|
66814
66811
|
async resetStaleRunningQueuedItems(activeSessionKeys) {
|
|
66815
66812
|
const entries = await this.sessionStore.list();
|
|
66816
66813
|
let reset = 0;
|
|
@@ -66891,11 +66888,9 @@ function getStoredLoops(entry) {
|
|
|
66891
66888
|
function getStoredQueues(entry) {
|
|
66892
66889
|
return entry?.queues ?? [];
|
|
66893
66890
|
}
|
|
66894
|
-
function hasActiveRuntime(entry) {
|
|
66895
|
-
return entry.runtime?.state === "running" || entry.runtime?.state === "detached";
|
|
66896
|
-
}
|
|
66897
66891
|
|
|
66898
66892
|
// src/agents/session-key.ts
|
|
66893
|
+
import { createHash } from "node:crypto";
|
|
66899
66894
|
var DEFAULT_MAIN_KEY = "main";
|
|
66900
66895
|
var DEFAULT_BOT_ID = "default";
|
|
66901
66896
|
var DEFAULT_ACCOUNT_ID = DEFAULT_BOT_ID;
|
|
@@ -66986,7 +66981,8 @@ function buildTmuxSessionName(params) {
|
|
|
66986
66981
|
mainKey: normalizeMainKey(params.mainKey)
|
|
66987
66982
|
});
|
|
66988
66983
|
const baseName = sanitizeSessionName(rendered);
|
|
66989
|
-
|
|
66984
|
+
const sessionHash = createHash("sha1").update(params.sessionKey).digest("hex").slice(0, 8);
|
|
66985
|
+
return `${baseName}-${sessionHash}`;
|
|
66990
66986
|
}
|
|
66991
66987
|
|
|
66992
66988
|
// src/agents/resolved-target.ts
|
|
@@ -67146,39 +67142,15 @@ class AgentJobQueue {
|
|
|
67146
67142
|
if (!state) {
|
|
67147
67143
|
return 0;
|
|
67148
67144
|
}
|
|
67149
|
-
|
|
67150
|
-
const removedEntries = state.entries.filter((entry) => entry.status === "pending");
|
|
67151
|
-
state.entries = keptEntries;
|
|
67152
|
-
for (const entry of removedEntries) {
|
|
67153
|
-
Promise.resolve(entry.lifecycle?.onClear?.()).catch(() => {
|
|
67154
|
-
return;
|
|
67155
|
-
});
|
|
67156
|
-
entry.reject(new ClearedQueuedTaskError);
|
|
67157
|
-
}
|
|
67158
|
-
if (state.entries.length === 0 && !state.running) {
|
|
67159
|
-
this.states.delete(key);
|
|
67160
|
-
}
|
|
67161
|
-
return removedEntries.length;
|
|
67145
|
+
return this.clearPendingEntriesForState(key, state, () => true);
|
|
67162
67146
|
}
|
|
67163
|
-
|
|
67164
|
-
const
|
|
67165
|
-
|
|
67166
|
-
|
|
67167
|
-
const keptEntries = state.entries.filter((entry) => entry.status === "running" || !idSet.has(entry.id));
|
|
67168
|
-
const removedEntries = state.entries.filter((entry) => entry.status === "pending" && idSet.has(entry.id));
|
|
67169
|
-
state.entries = keptEntries;
|
|
67170
|
-
cleared += removedEntries.length;
|
|
67171
|
-
for (const entry of removedEntries) {
|
|
67172
|
-
Promise.resolve(entry.lifecycle?.onClear?.()).catch(() => {
|
|
67173
|
-
return;
|
|
67174
|
-
});
|
|
67175
|
-
entry.reject(new ClearedQueuedTaskError);
|
|
67176
|
-
}
|
|
67177
|
-
if (state.entries.length === 0 && !state.running) {
|
|
67178
|
-
this.states.delete(key);
|
|
67179
|
-
}
|
|
67147
|
+
clearPendingByIdsForKey(key, ids) {
|
|
67148
|
+
const state = this.states.get(key);
|
|
67149
|
+
if (!state) {
|
|
67150
|
+
return 0;
|
|
67180
67151
|
}
|
|
67181
|
-
|
|
67152
|
+
const idSet = new Set(ids);
|
|
67153
|
+
return this.clearPendingEntriesForState(key, state, (entry) => idSet.has(entry.id));
|
|
67182
67154
|
}
|
|
67183
67155
|
getOrCreateState(key) {
|
|
67184
67156
|
const existing = this.states.get(key);
|
|
@@ -67203,6 +67175,21 @@ class AgentJobQueue {
|
|
|
67203
67175
|
return left.sequence - right.sequence;
|
|
67204
67176
|
});
|
|
67205
67177
|
}
|
|
67178
|
+
clearPendingEntriesForState(key, state, shouldClear) {
|
|
67179
|
+
const keptEntries = state.entries.filter((entry) => entry.status === "running" || !shouldClear(entry));
|
|
67180
|
+
const removedEntries = state.entries.filter((entry) => entry.status === "pending" && shouldClear(entry));
|
|
67181
|
+
state.entries = keptEntries;
|
|
67182
|
+
for (const entry of removedEntries) {
|
|
67183
|
+
Promise.resolve(entry.lifecycle?.onClear?.()).catch(() => {
|
|
67184
|
+
return;
|
|
67185
|
+
});
|
|
67186
|
+
entry.reject(new ClearedQueuedTaskError);
|
|
67187
|
+
}
|
|
67188
|
+
if (state.entries.length === 0 && !state.running) {
|
|
67189
|
+
this.states.delete(key);
|
|
67190
|
+
}
|
|
67191
|
+
return removedEntries.length;
|
|
67192
|
+
}
|
|
67206
67193
|
async drain(key, state) {
|
|
67207
67194
|
if (state.running) {
|
|
67208
67195
|
return;
|
|
@@ -67319,7 +67306,6 @@ function createStoredLoopBase(params) {
|
|
|
67319
67306
|
updatedAt: now,
|
|
67320
67307
|
nextRunAt: params.nextRunAt,
|
|
67321
67308
|
promptText: params.promptText,
|
|
67322
|
-
canonicalPromptText: params.canonicalPromptText,
|
|
67323
67309
|
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
67324
67310
|
promptSummary: params.promptSummary,
|
|
67325
67311
|
promptSource: params.promptSource,
|
|
@@ -67350,7 +67336,6 @@ function createStoredIntervalLoop(params) {
|
|
|
67350
67336
|
...createStoredLoopBase({
|
|
67351
67337
|
nextRunAt: Date.now(),
|
|
67352
67338
|
promptText: params.promptText,
|
|
67353
|
-
canonicalPromptText: params.canonicalPromptText,
|
|
67354
67339
|
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
67355
67340
|
promptSummary: params.promptSummary,
|
|
67356
67341
|
promptSource: params.promptSource,
|
|
@@ -67381,7 +67366,6 @@ function createStoredCalendarLoop(params) {
|
|
|
67381
67366
|
...createStoredLoopBase({
|
|
67382
67367
|
nextRunAt,
|
|
67383
67368
|
promptText: params.promptText,
|
|
67384
|
-
canonicalPromptText: params.canonicalPromptText,
|
|
67385
67369
|
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
67386
67370
|
promptSummary: params.promptSummary,
|
|
67387
67371
|
promptSource: params.promptSource,
|
|
@@ -67542,87 +67526,86 @@ class ManagedLoopController {
|
|
|
67542
67526
|
}
|
|
67543
67527
|
async reconcilePersistedIntervalLoops() {
|
|
67544
67528
|
const persistedLoops = await this.deps.sessionState.listIntervalLoops();
|
|
67545
|
-
const
|
|
67529
|
+
const persistedKeys = new Set;
|
|
67546
67530
|
for (const persisted of persistedLoops) {
|
|
67547
|
-
|
|
67531
|
+
const managedKey = this.buildManagedLoopKey(persisted.sessionKey, persisted.id);
|
|
67532
|
+
persistedKeys.add(managedKey);
|
|
67548
67533
|
if (persisted.attemptedRuns >= persisted.maxRuns) {
|
|
67549
|
-
this.dropManagedIntervalLoop(
|
|
67534
|
+
this.dropManagedIntervalLoop(managedKey);
|
|
67550
67535
|
continue;
|
|
67551
67536
|
}
|
|
67552
|
-
if (this.intervalLoops.has(
|
|
67537
|
+
if (this.intervalLoops.has(managedKey)) {
|
|
67553
67538
|
continue;
|
|
67554
67539
|
}
|
|
67555
|
-
this.intervalLoops.set(
|
|
67540
|
+
this.intervalLoops.set(managedKey, {
|
|
67556
67541
|
target: {
|
|
67557
67542
|
agentId: persisted.agentId,
|
|
67558
67543
|
sessionKey: persisted.sessionKey
|
|
67559
67544
|
},
|
|
67560
67545
|
loop: persisted
|
|
67561
67546
|
});
|
|
67562
|
-
this.scheduleIntervalLoopTimer(
|
|
67547
|
+
this.scheduleIntervalLoopTimer(managedKey, Math.max(0, persisted.nextRunAt - Date.now()));
|
|
67563
67548
|
}
|
|
67564
|
-
for (const
|
|
67565
|
-
if (!
|
|
67566
|
-
this.dropManagedIntervalLoop(
|
|
67549
|
+
for (const managedKey of this.intervalLoops.keys()) {
|
|
67550
|
+
if (!persistedKeys.has(managedKey)) {
|
|
67551
|
+
this.dropManagedIntervalLoop(managedKey);
|
|
67567
67552
|
}
|
|
67568
67553
|
}
|
|
67569
67554
|
}
|
|
67570
67555
|
async createIntervalLoop(params) {
|
|
67571
67556
|
this.assertActiveLoopCapacity();
|
|
67572
67557
|
const loop = createStoredIntervalLoop(params);
|
|
67558
|
+
const managedKey = this.buildManagedLoopKey(params.target.sessionKey, loop.id);
|
|
67573
67559
|
await this.deps.sessionState.setIntervalLoop(this.deps.resolveTarget(params.target), loop);
|
|
67574
|
-
this.intervalLoops.set(
|
|
67560
|
+
this.intervalLoops.set(managedKey, {
|
|
67575
67561
|
target: params.target,
|
|
67576
67562
|
loop
|
|
67577
67563
|
});
|
|
67578
|
-
await this.runIntervalLoopIteration(
|
|
67564
|
+
await this.runIntervalLoopIteration(managedKey, {
|
|
67579
67565
|
notifyStart: false
|
|
67580
67566
|
});
|
|
67581
|
-
return this.getIntervalLoop(loop.id);
|
|
67567
|
+
return this.getIntervalLoop(params.target.sessionKey, loop.id);
|
|
67582
67568
|
}
|
|
67583
67569
|
async createCalendarLoop(params) {
|
|
67584
67570
|
this.assertActiveLoopCapacity();
|
|
67585
67571
|
const loop = createStoredCalendarLoop(params);
|
|
67572
|
+
const managedKey = this.buildManagedLoopKey(params.target.sessionKey, loop.id);
|
|
67586
67573
|
await this.deps.sessionState.setIntervalLoop(this.deps.resolveTarget(params.target), loop);
|
|
67587
|
-
this.intervalLoops.set(
|
|
67574
|
+
this.intervalLoops.set(managedKey, {
|
|
67588
67575
|
target: params.target,
|
|
67589
67576
|
loop
|
|
67590
67577
|
});
|
|
67591
|
-
this.scheduleIntervalLoopTimer(
|
|
67592
|
-
return this.getIntervalLoop(loop.id);
|
|
67578
|
+
this.scheduleIntervalLoopTimer(managedKey, Math.max(0, loop.nextRunAt - Date.now()));
|
|
67579
|
+
return this.getIntervalLoop(params.target.sessionKey, loop.id);
|
|
67593
67580
|
}
|
|
67594
|
-
async cancelIntervalLoop(loopId) {
|
|
67595
|
-
const
|
|
67581
|
+
async cancelIntervalLoop(target, loopId) {
|
|
67582
|
+
const managedKey = this.buildManagedLoopKey(target.sessionKey, loopId);
|
|
67583
|
+
const managed = this.intervalLoops.get(managedKey);
|
|
67596
67584
|
if (!managed) {
|
|
67597
67585
|
return false;
|
|
67598
67586
|
}
|
|
67599
|
-
|
|
67600
|
-
clearTimeout(managed.timer);
|
|
67601
|
-
this.loopTimers.delete(managed.timer);
|
|
67602
|
-
}
|
|
67603
|
-
this.intervalLoops.delete(loopId);
|
|
67604
|
-
await this.deps.sessionState.removeIntervalLoop(this.deps.resolveTarget(managed.target), loopId);
|
|
67587
|
+
await this.cancelManagedIntervalLoop(managedKey, managed);
|
|
67605
67588
|
return true;
|
|
67606
67589
|
}
|
|
67607
67590
|
async cancelIntervalLoopsForSession(target) {
|
|
67608
|
-
const matching = [...this.intervalLoops.
|
|
67609
|
-
for (const
|
|
67610
|
-
await this.
|
|
67591
|
+
const matching = [...this.intervalLoops.entries()].filter(([, managed]) => managed.target.sessionKey === target.sessionKey);
|
|
67592
|
+
for (const [managedKey, managed] of matching) {
|
|
67593
|
+
await this.cancelManagedIntervalLoop(managedKey, managed);
|
|
67611
67594
|
}
|
|
67612
67595
|
return matching.length;
|
|
67613
67596
|
}
|
|
67614
67597
|
async cancelAllIntervalLoops() {
|
|
67615
|
-
const
|
|
67616
|
-
for (const
|
|
67617
|
-
await this.
|
|
67598
|
+
const matching = [...this.intervalLoops.entries()];
|
|
67599
|
+
for (const [managedKey, managed] of matching) {
|
|
67600
|
+
await this.cancelManagedIntervalLoop(managedKey, managed);
|
|
67618
67601
|
}
|
|
67619
|
-
return
|
|
67602
|
+
return matching.length;
|
|
67620
67603
|
}
|
|
67621
67604
|
listIntervalLoops(params) {
|
|
67622
67605
|
return [...this.intervalLoops.values()].filter((managed) => !params?.sessionKey || managed.target.sessionKey === params.sessionKey).map((managed) => this.toLoopStatus(managed)).sort((left, right) => left.nextRunAt - right.nextRunAt);
|
|
67623
67606
|
}
|
|
67624
|
-
getIntervalLoop(loopId) {
|
|
67625
|
-
const managed = this.intervalLoops.get(loopId);
|
|
67607
|
+
getIntervalLoop(sessionKey, loopId) {
|
|
67608
|
+
const managed = this.intervalLoops.get(this.buildManagedLoopKey(sessionKey, loopId));
|
|
67626
67609
|
return managed ? this.toLoopStatus(managed) : null;
|
|
67627
67610
|
}
|
|
67628
67611
|
getActiveIntervalLoopCount() {
|
|
@@ -67646,8 +67629,8 @@ class ManagedLoopController {
|
|
|
67646
67629
|
const entry = await this.deps.sessionState.getEntry(managed.target.sessionKey);
|
|
67647
67630
|
return (entry?.loops ?? entry?.intervalLoops ?? []).some((loop) => loop.id === managed.loop.id);
|
|
67648
67631
|
}
|
|
67649
|
-
dropManagedIntervalLoop(
|
|
67650
|
-
const managed = this.intervalLoops.get(
|
|
67632
|
+
dropManagedIntervalLoop(managedKey) {
|
|
67633
|
+
const managed = this.intervalLoops.get(managedKey);
|
|
67651
67634
|
if (!managed) {
|
|
67652
67635
|
return;
|
|
67653
67636
|
}
|
|
@@ -67655,30 +67638,34 @@ class ManagedLoopController {
|
|
|
67655
67638
|
clearTimeout(managed.timer);
|
|
67656
67639
|
this.loopTimers.delete(managed.timer);
|
|
67657
67640
|
}
|
|
67658
|
-
this.intervalLoops.delete(
|
|
67641
|
+
this.intervalLoops.delete(managedKey);
|
|
67659
67642
|
}
|
|
67660
|
-
async
|
|
67661
|
-
|
|
67643
|
+
async cancelManagedIntervalLoop(managedKey, managed) {
|
|
67644
|
+
this.dropManagedIntervalLoop(managedKey);
|
|
67645
|
+
await this.deps.sessionState.removeIntervalLoop(this.deps.resolveTarget(managed.target), managed.loop.id);
|
|
67646
|
+
}
|
|
67647
|
+
async runIntervalLoopIteration(managedKey, options = {}) {
|
|
67648
|
+
const managed = this.intervalLoops.get(managedKey);
|
|
67662
67649
|
if (!managed) {
|
|
67663
67650
|
return;
|
|
67664
67651
|
}
|
|
67665
67652
|
if (!await this.isManagedLoopPersisted(managed)) {
|
|
67666
|
-
this.dropManagedIntervalLoop(
|
|
67653
|
+
this.dropManagedIntervalLoop(managedKey);
|
|
67667
67654
|
return;
|
|
67668
67655
|
}
|
|
67669
67656
|
const attemptedRuns = managed.loop.attemptedRuns + 1;
|
|
67670
67657
|
const now = Date.now();
|
|
67671
67658
|
const nextLoopState = this.buildNextLoopState(managed.loop, attemptedRuns, now);
|
|
67672
67659
|
if (await this.deps.isSessionBusy(managed.target)) {
|
|
67673
|
-
await this.skipLoopIteration(
|
|
67660
|
+
await this.skipLoopIteration(managedKey, managed, nextLoopState, attemptedRuns, now);
|
|
67674
67661
|
return;
|
|
67675
67662
|
}
|
|
67676
67663
|
nextLoopState.executedRuns += 1;
|
|
67677
67664
|
if (!await this.updateManagedIntervalLoop(managed, nextLoopState)) {
|
|
67678
|
-
this.dropManagedIntervalLoop(
|
|
67665
|
+
this.dropManagedIntervalLoop(managedKey);
|
|
67679
67666
|
return;
|
|
67680
67667
|
}
|
|
67681
|
-
await this.executeLoopIteration(
|
|
67668
|
+
await this.executeLoopIteration(managedKey, managed.target, nextLoopState, attemptedRuns, now, options.notifyStart !== false);
|
|
67682
67669
|
}
|
|
67683
67670
|
buildNextLoopState(loop, attemptedRuns, now) {
|
|
67684
67671
|
return {
|
|
@@ -67688,33 +67675,35 @@ class ManagedLoopController {
|
|
|
67688
67675
|
nextRunAt: this.computeNextManagedLoopRunAtMs(loop, now)
|
|
67689
67676
|
};
|
|
67690
67677
|
}
|
|
67691
|
-
async skipLoopIteration(
|
|
67678
|
+
async skipLoopIteration(managedKey, managed, nextLoopState, attemptedRuns, now) {
|
|
67692
67679
|
nextLoopState.skippedRuns += 1;
|
|
67693
67680
|
if (!await this.updateManagedIntervalLoop(managed, nextLoopState)) {
|
|
67694
|
-
this.dropManagedIntervalLoop(
|
|
67681
|
+
this.dropManagedIntervalLoop(managedKey);
|
|
67695
67682
|
return;
|
|
67696
67683
|
}
|
|
67697
67684
|
if (attemptedRuns >= managed.loop.maxRuns) {
|
|
67698
|
-
|
|
67685
|
+
this.dropManagedIntervalLoop(managedKey);
|
|
67686
|
+
await this.deps.sessionState.removeIntervalLoop(this.deps.resolveTarget(managed.target), managed.loop.id);
|
|
67699
67687
|
return;
|
|
67700
67688
|
}
|
|
67701
|
-
this.scheduleIntervalLoopTimer(
|
|
67689
|
+
this.scheduleIntervalLoopTimer(managedKey, Math.max(0, nextLoopState.nextRunAt - now));
|
|
67702
67690
|
}
|
|
67703
|
-
async executeLoopIteration(
|
|
67704
|
-
await this.notifyAndEnqueueLoop(
|
|
67691
|
+
async executeLoopIteration(managedKey, target, nextLoopState, attemptedRuns, now, notifyStart) {
|
|
67692
|
+
await this.notifyAndEnqueueLoop(managedKey, target, nextLoopState, attemptedRuns, notifyStart);
|
|
67705
67693
|
if (attemptedRuns >= nextLoopState.maxRuns) {
|
|
67706
|
-
|
|
67694
|
+
this.dropManagedIntervalLoop(managedKey);
|
|
67695
|
+
await this.deps.sessionState.removeIntervalLoop(this.deps.resolveTarget(target), nextLoopState.id);
|
|
67707
67696
|
return;
|
|
67708
67697
|
}
|
|
67709
|
-
this.scheduleIntervalLoopTimer(
|
|
67698
|
+
this.scheduleIntervalLoopTimer(managedKey, Math.max(0, nextLoopState.nextRunAt - now));
|
|
67710
67699
|
}
|
|
67711
|
-
async notifyAndEnqueueLoop(
|
|
67700
|
+
async notifyAndEnqueueLoop(managedKey, target, nextLoopState, attemptedRuns, notifyStart) {
|
|
67712
67701
|
if (notifyStart) {
|
|
67713
67702
|
await this.deps.surfaceRuntime.notifyManagedLoopStart(target, nextLoopState);
|
|
67714
67703
|
}
|
|
67715
67704
|
const promptText = await this.deps.surfaceRuntime.buildManagedLoopPrompt(target.agentId, nextLoopState);
|
|
67716
67705
|
const { result } = this.deps.enqueuePrompt(target, promptText, {
|
|
67717
|
-
observerId: `loop:${
|
|
67706
|
+
observerId: `loop:${managedKey}:${attemptedRuns}`,
|
|
67718
67707
|
onUpdate: async () => {
|
|
67719
67708
|
return;
|
|
67720
67709
|
}
|
|
@@ -67725,8 +67714,8 @@ class ManagedLoopController {
|
|
|
67725
67714
|
}
|
|
67726
67715
|
});
|
|
67727
67716
|
}
|
|
67728
|
-
scheduleIntervalLoopTimer(
|
|
67729
|
-
const managed = this.intervalLoops.get(
|
|
67717
|
+
scheduleIntervalLoopTimer(managedKey, delayMs) {
|
|
67718
|
+
const managed = this.intervalLoops.get(managedKey);
|
|
67730
67719
|
if (!managed) {
|
|
67731
67720
|
return;
|
|
67732
67721
|
}
|
|
@@ -67736,12 +67725,12 @@ class ManagedLoopController {
|
|
|
67736
67725
|
}
|
|
67737
67726
|
const timer = setTimeout(() => {
|
|
67738
67727
|
this.loopTimers.delete(timer);
|
|
67739
|
-
const current = this.intervalLoops.get(
|
|
67728
|
+
const current = this.intervalLoops.get(managedKey);
|
|
67740
67729
|
if (!current) {
|
|
67741
67730
|
return;
|
|
67742
67731
|
}
|
|
67743
67732
|
current.timer = undefined;
|
|
67744
|
-
this.runIntervalLoopIteration(
|
|
67733
|
+
this.runIntervalLoopIteration(managedKey, { notifyStart: true }).catch((error) => {
|
|
67745
67734
|
if (!this.deps.shouldSuppressShutdownError(error)) {
|
|
67746
67735
|
console.error("loop execution failed", error);
|
|
67747
67736
|
}
|
|
@@ -67758,6 +67747,9 @@ class ManagedLoopController {
|
|
|
67758
67747
|
managed.loop = nextLoopState;
|
|
67759
67748
|
return true;
|
|
67760
67749
|
}
|
|
67750
|
+
buildManagedLoopKey(sessionKey, loopId) {
|
|
67751
|
+
return `${sessionKey}::${loopId}`;
|
|
67752
|
+
}
|
|
67761
67753
|
computeNextManagedLoopRunAtMs(loop, nowMs) {
|
|
67762
67754
|
if (loop.kind === "calendar") {
|
|
67763
67755
|
return computeNextCalendarLoopRunAtMs({
|
|
@@ -67798,7 +67790,7 @@ class ManagedQueueController {
|
|
|
67798
67790
|
}
|
|
67799
67791
|
async clearQueuedPrompts(target) {
|
|
67800
67792
|
const clearedStored = await this.deps.sessionState.clearPendingQueuedItemsForSessionKey(target.sessionKey);
|
|
67801
|
-
this.deps.queue.
|
|
67793
|
+
this.deps.queue.clearPendingByIdsForKey(target.sessionKey, clearedStored.map((item) => item.id));
|
|
67802
67794
|
return clearedStored.length;
|
|
67803
67795
|
}
|
|
67804
67796
|
enqueuePrompt(target, prompt, callbacks) {
|
|
@@ -67836,8 +67828,8 @@ class ManagedQueueController {
|
|
|
67836
67828
|
return;
|
|
67837
67829
|
}
|
|
67838
67830
|
this.setManagedQueueItem(target, item, true);
|
|
67839
|
-
return this.persistQueueItem(this.deps.resolveTarget(target), item).then(() => this.markPersisted(item)).catch((error) => {
|
|
67840
|
-
this.clearPendingById(item);
|
|
67831
|
+
return this.persistQueueItem(this.deps.resolveTarget(target), item).then(() => this.markPersisted(target, item)).catch((error) => {
|
|
67832
|
+
this.clearPendingById(target, item);
|
|
67841
67833
|
throw error;
|
|
67842
67834
|
});
|
|
67843
67835
|
}
|
|
@@ -67848,16 +67840,32 @@ class ManagedQueueController {
|
|
|
67848
67840
|
await reconciledBeforeStart;
|
|
67849
67841
|
return !await this.deps.hasBlockingActiveRun(target);
|
|
67850
67842
|
}
|
|
67843
|
+
async canStartPersistedQueueItem(target, item) {
|
|
67844
|
+
if (await this.deps.hasBlockingActiveRun(target)) {
|
|
67845
|
+
return false;
|
|
67846
|
+
}
|
|
67847
|
+
if (!await this.deps.sessionState.hasQueuedItem(target.sessionKey, item.id)) {
|
|
67848
|
+
this.clearPendingById(target, item);
|
|
67849
|
+
return false;
|
|
67850
|
+
}
|
|
67851
|
+
return true;
|
|
67852
|
+
}
|
|
67851
67853
|
async reconcilePersistedQueueItems() {
|
|
67852
67854
|
const persistedItems = await this.deps.sessionState.listQueuedItems({
|
|
67853
67855
|
statuses: ["pending", "running"]
|
|
67854
67856
|
});
|
|
67855
|
-
const
|
|
67856
|
-
const
|
|
67857
|
-
if (
|
|
67858
|
-
|
|
67859
|
-
for (const
|
|
67860
|
-
|
|
67857
|
+
const persistedKeys = new Set(persistedItems.map((item) => this.buildManagedQueueKey(item.sessionKey, item.id)));
|
|
67858
|
+
const removedManagedItems = [...this.queuedItems.entries()].filter(([key, managed]) => !managed.persisting && !persistedKeys.has(key));
|
|
67859
|
+
if (removedManagedItems.length > 0) {
|
|
67860
|
+
const removedBySession = new Map;
|
|
67861
|
+
for (const [key, managed] of removedManagedItems) {
|
|
67862
|
+
const ids = removedBySession.get(managed.target.sessionKey) ?? [];
|
|
67863
|
+
ids.push(managed.item.id);
|
|
67864
|
+
removedBySession.set(managed.target.sessionKey, ids);
|
|
67865
|
+
this.queuedItems.delete(key);
|
|
67866
|
+
}
|
|
67867
|
+
for (const [sessionKey, itemIds] of removedBySession.entries()) {
|
|
67868
|
+
this.deps.queue.clearPendingByIdsForKey(sessionKey, itemIds);
|
|
67861
67869
|
}
|
|
67862
67870
|
}
|
|
67863
67871
|
for (const persisted of persistedItems) {
|
|
@@ -67865,14 +67873,14 @@ class ManagedQueueController {
|
|
|
67865
67873
|
await this.clearStaleRunningQueueItem(persisted);
|
|
67866
67874
|
continue;
|
|
67867
67875
|
}
|
|
67868
|
-
if (this.queuedItems.has(persisted.id)) {
|
|
67876
|
+
if (this.queuedItems.has(this.buildManagedQueueKey(persisted.sessionKey, persisted.id))) {
|
|
67869
67877
|
continue;
|
|
67870
67878
|
}
|
|
67871
67879
|
this.enqueuePersistedQueueItem(persisted);
|
|
67872
67880
|
}
|
|
67873
67881
|
}
|
|
67874
67882
|
async clearStaleRunningQueueItem(item) {
|
|
67875
|
-
if (this.queuedItems.has(item.id)) {
|
|
67883
|
+
if (this.queuedItems.has(this.buildManagedQueueKey(item.sessionKey, item.id))) {
|
|
67876
67884
|
return;
|
|
67877
67885
|
}
|
|
67878
67886
|
const target = {
|
|
@@ -67896,21 +67904,22 @@ class ManagedQueueController {
|
|
|
67896
67904
|
});
|
|
67897
67905
|
}
|
|
67898
67906
|
setManagedQueueItem(target, item, persisting) {
|
|
67899
|
-
this.queuedItems.set(item.id, {
|
|
67907
|
+
this.queuedItems.set(this.buildManagedQueueKey(target.sessionKey, item.id), {
|
|
67900
67908
|
target,
|
|
67901
67909
|
item,
|
|
67902
67910
|
persisting
|
|
67903
67911
|
});
|
|
67904
67912
|
}
|
|
67905
|
-
markPersisted(item) {
|
|
67906
|
-
const
|
|
67913
|
+
markPersisted(target, item) {
|
|
67914
|
+
const key = this.buildManagedQueueKey(target.sessionKey, item.id);
|
|
67915
|
+
const managed = this.queuedItems.get(key);
|
|
67907
67916
|
if (managed) {
|
|
67908
|
-
this.queuedItems.set(
|
|
67917
|
+
this.queuedItems.set(key, { ...managed, persisting: false });
|
|
67909
67918
|
}
|
|
67910
67919
|
}
|
|
67911
|
-
clearPendingById(item) {
|
|
67912
|
-
this.deps.queue.
|
|
67913
|
-
this.queuedItems.delete(item.id);
|
|
67920
|
+
clearPendingById(target, item) {
|
|
67921
|
+
this.deps.queue.clearPendingByIdsForKey(target.sessionKey, [item.id]);
|
|
67922
|
+
this.queuedItems.delete(this.buildManagedQueueKey(target.sessionKey, item.id));
|
|
67914
67923
|
}
|
|
67915
67924
|
async markQueueItemRunning(target, item) {
|
|
67916
67925
|
if (!item) {
|
|
@@ -67923,7 +67932,7 @@ class ManagedQueueController {
|
|
|
67923
67932
|
startedAt: now,
|
|
67924
67933
|
updatedAt: now
|
|
67925
67934
|
};
|
|
67926
|
-
this.queuedItems.set(item.id, {
|
|
67935
|
+
this.queuedItems.set(this.buildManagedQueueKey(target.sessionKey, item.id), {
|
|
67927
67936
|
target,
|
|
67928
67937
|
item: next
|
|
67929
67938
|
});
|
|
@@ -67934,7 +67943,7 @@ class ManagedQueueController {
|
|
|
67934
67943
|
if (!item) {
|
|
67935
67944
|
return;
|
|
67936
67945
|
}
|
|
67937
|
-
this.queuedItems.delete(item.id);
|
|
67946
|
+
this.queuedItems.delete(this.buildManagedQueueKey(target.sessionKey, item.id));
|
|
67938
67947
|
await this.deps.sessionState.removeQueuedItem(this.deps.resolveTarget(target), item.id);
|
|
67939
67948
|
}
|
|
67940
67949
|
enqueuePersistedQueueItem(item) {
|
|
@@ -67942,7 +67951,7 @@ class ManagedQueueController {
|
|
|
67942
67951
|
agentId: item.agentId,
|
|
67943
67952
|
sessionKey: item.sessionKey
|
|
67944
67953
|
};
|
|
67945
|
-
this.queuedItems.set(item.id, {
|
|
67954
|
+
this.queuedItems.set(this.buildManagedQueueKey(target.sessionKey, item.id), {
|
|
67946
67955
|
target,
|
|
67947
67956
|
item
|
|
67948
67957
|
});
|
|
@@ -67974,7 +67983,7 @@ class ManagedQueueController {
|
|
|
67974
67983
|
id: item.id,
|
|
67975
67984
|
createdAt: item.createdAt,
|
|
67976
67985
|
text: item.promptSummary,
|
|
67977
|
-
canStart: async () =>
|
|
67986
|
+
canStart: async () => this.canStartPersistedQueueItem(target, item),
|
|
67978
67987
|
onComplete: async (value) => {
|
|
67979
67988
|
if (!value.messageToolFinalAlreadySent && !await this.hasMessageToolFinalForQueueItem(target, item)) {
|
|
67980
67989
|
await this.deps.surfaceRuntime.notifyManagedQueueSettlement(target, item, value);
|
|
@@ -67988,6 +67997,9 @@ class ManagedQueueController {
|
|
|
67988
67997
|
onClear: () => this.removeManagedQueueItem(target, item)
|
|
67989
67998
|
});
|
|
67990
67999
|
queued.result.catch((error) => {
|
|
68000
|
+
if (error instanceof ClearedQueuedTaskError) {
|
|
68001
|
+
return;
|
|
68002
|
+
}
|
|
67991
68003
|
if (this.deps.shouldSuppressShutdownError(error)) {
|
|
67992
68004
|
return;
|
|
67993
68005
|
}
|
|
@@ -68004,7 +68016,7 @@ class ManagedQueueController {
|
|
|
68004
68016
|
return false;
|
|
68005
68017
|
}
|
|
68006
68018
|
async hasMessageToolFinalForQueueItem(target, item) {
|
|
68007
|
-
const startedAt = this.queuedItems.get(item.id)?.item.startedAt ?? item.startedAt;
|
|
68019
|
+
const startedAt = this.queuedItems.get(this.buildManagedQueueKey(target.sessionKey, item.id))?.item.startedAt ?? item.startedAt;
|
|
68008
68020
|
if (typeof startedAt !== "number" || !Number.isFinite(startedAt)) {
|
|
68009
68021
|
return false;
|
|
68010
68022
|
}
|
|
@@ -68025,6 +68037,9 @@ class ManagedQueueController {
|
|
|
68025
68037
|
messageToolFinalAlreadySent: true
|
|
68026
68038
|
};
|
|
68027
68039
|
}
|
|
68040
|
+
buildManagedQueueKey(sessionKey, itemId) {
|
|
68041
|
+
return `${sessionKey}::${itemId}`;
|
|
68042
|
+
}
|
|
68028
68043
|
}
|
|
68029
68044
|
|
|
68030
68045
|
// src/agents/runner-service.ts
|
|
@@ -69731,7 +69746,11 @@ class RunnerService {
|
|
|
69731
69746
|
args: args.map((value) => applyTemplate(value, values))
|
|
69732
69747
|
};
|
|
69733
69748
|
}
|
|
69734
|
-
async
|
|
69749
|
+
async syncStoredSessionId(target) {
|
|
69750
|
+
const resolved = this.resolveTarget(target);
|
|
69751
|
+
return this.syncStoredSessionIdForResolvedTarget(resolved);
|
|
69752
|
+
}
|
|
69753
|
+
async syncStoredSessionIdForResolvedTarget(resolved) {
|
|
69735
69754
|
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
69736
69755
|
if (existing?.sessionId) {
|
|
69737
69756
|
this.sessionIdentityCaptureRetryAt.delete(resolved.sessionKey);
|
|
@@ -69793,12 +69812,41 @@ class RunnerService {
|
|
|
69793
69812
|
remainingFreshRetries: remainingFreshRetries - 1
|
|
69794
69813
|
});
|
|
69795
69814
|
}
|
|
69796
|
-
async retryAfterStartupFault(target, resolved, error, remainingFreshRetries) {
|
|
69815
|
+
async retryAfterStartupFault(target, resolved, error, remainingFreshRetries, allowFreshResumeFallback) {
|
|
69816
|
+
if (allowFreshResumeFallback) {
|
|
69817
|
+
const resumedFresh = await this.retryFreshStartAfterStoredResumeFailure(target, resolved, error, remainingFreshRetries);
|
|
69818
|
+
if (resumedFresh) {
|
|
69819
|
+
return resumedFresh;
|
|
69820
|
+
}
|
|
69821
|
+
}
|
|
69797
69822
|
if (!isRetryableFreshStartFault(error)) {
|
|
69798
69823
|
return null;
|
|
69799
69824
|
}
|
|
69800
69825
|
return this.retryRunnerRestartPreservingSessionId(target, resolved, remainingFreshRetries);
|
|
69801
69826
|
}
|
|
69827
|
+
async retryFreshStartAfterStoredResumeFailure(target, resolved, error, remainingFreshRetries) {
|
|
69828
|
+
if (!isRecoverableStartupSessionLoss(error)) {
|
|
69829
|
+
return null;
|
|
69830
|
+
}
|
|
69831
|
+
if (resolved.runner.sessionId.resume.mode !== "command" || resolved.runner.sessionId.create.mode !== "runner") {
|
|
69832
|
+
return null;
|
|
69833
|
+
}
|
|
69834
|
+
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
69835
|
+
if (!existing?.sessionId) {
|
|
69836
|
+
return null;
|
|
69837
|
+
}
|
|
69838
|
+
const exitRecord = await readRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
69839
|
+
if (!exitRecord || exitRecord.exitCode === 0) {
|
|
69840
|
+
return null;
|
|
69841
|
+
}
|
|
69842
|
+
console.log(`clisbot clearing stored sessionId after failed runner resume startup ${resolved.sessionName}`);
|
|
69843
|
+
await this.sessionState.clearSessionIdEntry(resolved, {
|
|
69844
|
+
runnerCommand: resolved.runner.command
|
|
69845
|
+
});
|
|
69846
|
+
return this.ensureSessionReady(target, {
|
|
69847
|
+
remainingFreshRetries
|
|
69848
|
+
});
|
|
69849
|
+
}
|
|
69802
69850
|
async retryAfterStartupTimeout(target, resolved, remainingFreshRetries) {
|
|
69803
69851
|
return this.retryRunnerRestartPreservingSessionId(target, resolved, remainingFreshRetries);
|
|
69804
69852
|
}
|
|
@@ -69897,7 +69945,7 @@ class RunnerService {
|
|
|
69897
69945
|
});
|
|
69898
69946
|
try {
|
|
69899
69947
|
await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
69900
|
-
await this.
|
|
69948
|
+
await this.syncStoredSessionIdForResolvedTarget(resolved);
|
|
69901
69949
|
} catch (error) {
|
|
69902
69950
|
throw await this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
69903
69951
|
}
|
|
@@ -69967,7 +70015,7 @@ class RunnerService {
|
|
|
69967
70015
|
runnerCommand: runnerLaunch.command
|
|
69968
70016
|
});
|
|
69969
70017
|
} catch (error) {
|
|
69970
|
-
const retried = await this.retryAfterStartupFault(target, resolved, error, remainingFreshRetries);
|
|
70018
|
+
const retried = await this.retryAfterStartupFault(target, resolved, error, remainingFreshRetries, options.allowFreshRetry !== false);
|
|
69971
70019
|
if (retried) {
|
|
69972
70020
|
return retried;
|
|
69973
70021
|
}
|
|
@@ -69989,7 +70037,7 @@ class RunnerService {
|
|
|
69989
70037
|
});
|
|
69990
70038
|
return;
|
|
69991
70039
|
}
|
|
69992
|
-
await this.
|
|
70040
|
+
await this.syncStoredSessionIdForResolvedTarget(resolved);
|
|
69993
70041
|
}
|
|
69994
70042
|
async dismissTrustPrompt(resolved) {
|
|
69995
70043
|
if (!resolved.runner.trustWorkspace) {
|
|
@@ -70522,16 +70570,21 @@ class SessionService {
|
|
|
70522
70570
|
this.resolveTarget = resolveTarget;
|
|
70523
70571
|
}
|
|
70524
70572
|
async recoverPersistedRuns() {
|
|
70573
|
+
const activeSessionKeys = new Set;
|
|
70525
70574
|
const entries = await this.sessionState.listEntries();
|
|
70526
70575
|
for (const entry of entries) {
|
|
70527
70576
|
if (!entry.runtime || entry.runtime.state === "idle") {
|
|
70528
70577
|
continue;
|
|
70529
70578
|
}
|
|
70530
|
-
await this.reconcilePersistedActiveRun({
|
|
70579
|
+
const run = await this.reconcilePersistedActiveRun({
|
|
70531
70580
|
agentId: entry.agentId,
|
|
70532
70581
|
sessionKey: entry.sessionKey
|
|
70533
70582
|
});
|
|
70583
|
+
if (run) {
|
|
70584
|
+
activeSessionKeys.add(run.resolved.sessionKey);
|
|
70585
|
+
}
|
|
70534
70586
|
}
|
|
70587
|
+
return activeSessionKeys;
|
|
70535
70588
|
}
|
|
70536
70589
|
async clearLostPersistedActiveRuns() {
|
|
70537
70590
|
const entries = await this.sessionState.listEntries();
|
|
@@ -70762,6 +70815,14 @@ class SessionService {
|
|
|
70762
70815
|
}
|
|
70763
70816
|
this.activeRuns.clear();
|
|
70764
70817
|
}
|
|
70818
|
+
listLiveSessionRuntimes() {
|
|
70819
|
+
return [...this.activeRuns.values()].map((run) => ({
|
|
70820
|
+
state: run.latestUpdate.status === "detached" ? "detached" : "running",
|
|
70821
|
+
startedAt: run.startedAt,
|
|
70822
|
+
sessionKey: run.resolved.sessionKey,
|
|
70823
|
+
agentId: run.resolved.agentId
|
|
70824
|
+
}));
|
|
70825
|
+
}
|
|
70765
70826
|
buildDetachedNote(resolved) {
|
|
70766
70827
|
return `This session has been running for over ${resolved.stream.maxRuntimeLabel}. clisbot left it running and will post the final result here when it completes. Use \`/attach\` for live updates, \`/watch every <duration>\` for periodic updates, or \`/stop\` to interrupt it.`;
|
|
70767
70828
|
}
|
|
@@ -72141,7 +72202,7 @@ class SurfaceRuntime {
|
|
|
72141
72202
|
}
|
|
72142
72203
|
}
|
|
72143
72204
|
async buildManagedLoopPrompt(agentId, loop) {
|
|
72144
|
-
if (!loop.
|
|
72205
|
+
if (!loop.surfaceBinding) {
|
|
72145
72206
|
return loop.promptText;
|
|
72146
72207
|
}
|
|
72147
72208
|
const identity = this.buildLoopChannelIdentity(loop);
|
|
@@ -72156,7 +72217,7 @@ class SurfaceRuntime {
|
|
|
72156
72217
|
scheduledLoopId: loop.id
|
|
72157
72218
|
});
|
|
72158
72219
|
return buildAgentPromptText({
|
|
72159
|
-
text: loop.
|
|
72220
|
+
text: loop.promptText,
|
|
72160
72221
|
identity,
|
|
72161
72222
|
config: channelConfig.agentPrompt,
|
|
72162
72223
|
cliTool: getAgentEntry2(this.loadedConfig, agentId)?.cli,
|
|
@@ -72170,7 +72231,7 @@ class SurfaceRuntime {
|
|
|
72170
72231
|
});
|
|
72171
72232
|
}
|
|
72172
72233
|
async buildManagedQueuePrompt(agentId, item) {
|
|
72173
|
-
if (!item.
|
|
72234
|
+
if (!item.surfaceBinding) {
|
|
72174
72235
|
return item.promptText;
|
|
72175
72236
|
}
|
|
72176
72237
|
const identity = this.buildQueueChannelIdentity(item);
|
|
@@ -72184,7 +72245,7 @@ class SurfaceRuntime {
|
|
|
72184
72245
|
time: promptTime
|
|
72185
72246
|
});
|
|
72186
72247
|
return buildAgentPromptText({
|
|
72187
|
-
text: item.
|
|
72248
|
+
text: item.promptText,
|
|
72188
72249
|
identity,
|
|
72189
72250
|
config: channelConfig.agentPrompt,
|
|
72190
72251
|
cliTool: getAgentEntry2(this.loadedConfig, agentId)?.cli,
|
|
@@ -72407,9 +72468,8 @@ class AgentService {
|
|
|
72407
72468
|
});
|
|
72408
72469
|
}
|
|
72409
72470
|
async start() {
|
|
72410
|
-
await this.activeRuns.recoverPersistedRuns();
|
|
72411
|
-
|
|
72412
|
-
await this.sessionState.resetStaleRunningQueuedItems(activeSessions);
|
|
72471
|
+
const activeSessionKeys = await this.activeRuns.recoverPersistedRuns();
|
|
72472
|
+
await this.sessionState.resetStaleRunningQueuedItems(activeSessionKeys);
|
|
72413
72473
|
await this.managedQueues.reconcilePersistedQueueItems();
|
|
72414
72474
|
this.queueReconcileTimer = setInterval(() => {
|
|
72415
72475
|
this.managedQueues.reconcilePersistedQueueItems().catch((error) => {
|
|
@@ -72492,14 +72552,13 @@ class AgentService {
|
|
|
72492
72552
|
const storedSessionId = entry?.sessionId?.trim() || undefined;
|
|
72493
72553
|
return {
|
|
72494
72554
|
sessionName: resolved.sessionName,
|
|
72495
|
-
sessionId: storedSessionId,
|
|
72496
72555
|
storedSessionId,
|
|
72497
72556
|
resumeCommand: buildResumeCommandPreview(resolved, storedSessionId)
|
|
72498
72557
|
};
|
|
72499
72558
|
}
|
|
72500
|
-
async
|
|
72559
|
+
async listLiveSessionRuntimes() {
|
|
72501
72560
|
await this.activeRuns.clearLostPersistedActiveRuns();
|
|
72502
|
-
return this.
|
|
72561
|
+
return this.activeRuns.listLiveSessionRuntimes();
|
|
72503
72562
|
}
|
|
72504
72563
|
async setConversationFollowUpMode(target, mode) {
|
|
72505
72564
|
return this.sessionState.setConversationFollowUpMode(this.resolveTarget(target), mode);
|
|
@@ -72600,8 +72659,8 @@ class AgentService {
|
|
|
72600
72659
|
async createCalendarLoop(params) {
|
|
72601
72660
|
return this.managedLoops.createCalendarLoop(params);
|
|
72602
72661
|
}
|
|
72603
|
-
async cancelIntervalLoop(loopId) {
|
|
72604
|
-
return this.managedLoops.cancelIntervalLoop(loopId);
|
|
72662
|
+
async cancelIntervalLoop(target, loopId) {
|
|
72663
|
+
return this.managedLoops.cancelIntervalLoop(target, loopId);
|
|
72605
72664
|
}
|
|
72606
72665
|
async cancelIntervalLoopsForSession(target) {
|
|
72607
72666
|
return this.managedLoops.cancelIntervalLoopsForSession(target);
|
|
@@ -72612,8 +72671,8 @@ class AgentService {
|
|
|
72612
72671
|
listIntervalLoops(params) {
|
|
72613
72672
|
return this.managedLoops.listIntervalLoops(params);
|
|
72614
72673
|
}
|
|
72615
|
-
getIntervalLoop(loopId) {
|
|
72616
|
-
return this.managedLoops.getIntervalLoop(loopId);
|
|
72674
|
+
getIntervalLoop(target, loopId) {
|
|
72675
|
+
return this.managedLoops.getIntervalLoop(target.sessionKey, loopId);
|
|
72617
72676
|
}
|
|
72618
72677
|
getActiveIntervalLoopCount() {
|
|
72619
72678
|
return this.managedLoops.getActiveIntervalLoopCount();
|
|
@@ -72665,9 +72724,8 @@ function createStoredQueueItem(params) {
|
|
|
72665
72724
|
createdAt: now,
|
|
72666
72725
|
updatedAt: now,
|
|
72667
72726
|
promptText: params.promptText,
|
|
72668
|
-
canonicalPromptText: params.canonicalPromptText,
|
|
72669
72727
|
protectedControlMutationRule: params.protectedControlMutationRule,
|
|
72670
|
-
promptSummary: params.promptSummary ?? summarizeQueuePrompt(params.
|
|
72728
|
+
promptSummary: params.promptSummary ?? summarizeQueuePrompt(params.promptText),
|
|
72671
72729
|
promptSource: "custom",
|
|
72672
72730
|
createdBy: params.createdBy,
|
|
72673
72731
|
sender: params.sender,
|
|
@@ -72919,7 +72977,6 @@ function renderStartupSteeringUnavailableMessage() {
|
|
|
72919
72977
|
`);
|
|
72920
72978
|
}
|
|
72921
72979
|
function renderWhoAmIMessage(params) {
|
|
72922
|
-
const storedSessionId = params.sessionDiagnostics.storedSessionId ?? params.sessionDiagnostics.sessionId;
|
|
72923
72980
|
const lines = [
|
|
72924
72981
|
"Who am I",
|
|
72925
72982
|
"",
|
|
@@ -72927,7 +72984,7 @@ function renderWhoAmIMessage(params) {
|
|
|
72927
72984
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
72928
72985
|
`agentId: \`${params.route.agentId}\``,
|
|
72929
72986
|
`sessionName: \`${params.sessionDiagnostics.sessionName ?? "(not available)"}\``,
|
|
72930
|
-
`storedSessionId: \`${storedSessionId ?? "(not captured yet)"}\``
|
|
72987
|
+
`storedSessionId: \`${params.sessionDiagnostics.storedSessionId ?? "(not captured yet)"}\``
|
|
72931
72988
|
];
|
|
72932
72989
|
if (params.identity.senderId) {
|
|
72933
72990
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -72949,7 +73006,6 @@ function renderWhoAmIMessage(params) {
|
|
|
72949
73006
|
`);
|
|
72950
73007
|
}
|
|
72951
73008
|
function renderRouteStatusMessage(params) {
|
|
72952
|
-
const storedSessionId = params.sessionDiagnostics.storedSessionId ?? params.sessionDiagnostics.sessionId;
|
|
72953
73009
|
const lines = [
|
|
72954
73010
|
"Status",
|
|
72955
73011
|
"",
|
|
@@ -72957,7 +73013,7 @@ function renderRouteStatusMessage(params) {
|
|
|
72957
73013
|
`conversationKind: \`${params.identity.conversationKind}\``,
|
|
72958
73014
|
`agentId: \`${params.route.agentId}\``,
|
|
72959
73015
|
`sessionName: \`${params.sessionDiagnostics.sessionName ?? "(not available)"}\``,
|
|
72960
|
-
`storedSessionId: \`${storedSessionId ?? "(not captured yet)"}\``
|
|
73016
|
+
`storedSessionId: \`${params.sessionDiagnostics.storedSessionId ?? "(not captured yet)"}\``
|
|
72961
73017
|
];
|
|
72962
73018
|
if (params.identity.senderId) {
|
|
72963
73019
|
lines.push(`senderId: \`${params.identity.senderId}\``);
|
|
@@ -73734,7 +73790,6 @@ async function processChannelInteraction(params) {
|
|
|
73734
73790
|
};
|
|
73735
73791
|
const queueItem = forceQueuedDelivery ? createStoredQueueItem({
|
|
73736
73792
|
promptText: delayedPromptQueueText,
|
|
73737
|
-
canonicalPromptText: delayedPromptQueueText,
|
|
73738
73793
|
promptSummary: explicitQueueMessage ?? summarizeQueuePrompt(delayedPromptQueueText),
|
|
73739
73794
|
createdBy: params.senderId,
|
|
73740
73795
|
sender: buildLoopSender(params.identity),
|
|
@@ -74047,7 +74102,7 @@ async function processChannelInteraction(params) {
|
|
|
74047
74102
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
74048
74103
|
return interactionResult;
|
|
74049
74104
|
}
|
|
74050
|
-
const cancelled = await params.agentService.cancelIntervalLoop(targetLoopId);
|
|
74105
|
+
const cancelled = await params.agentService.cancelIntervalLoop(params.sessionTarget, targetLoopId);
|
|
74051
74106
|
await params.postText(cancelled ? `Cancelled loop \`${targetLoopId}\`.` : `No active loop found with id \`${targetLoopId}\`.`);
|
|
74052
74107
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
74053
74108
|
return interactionResult;
|
|
@@ -74132,7 +74187,6 @@ ${renderLoopUsage()}`);
|
|
|
74132
74187
|
const createdLoop2 = await params.agentService.createCalendarLoop({
|
|
74133
74188
|
target: params.sessionTarget,
|
|
74134
74189
|
promptText: resolvedLoopPrompt.text,
|
|
74135
|
-
canonicalPromptText: resolvedLoopPrompt.text,
|
|
74136
74190
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
74137
74191
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
74138
74192
|
loopStart: slashCommand.params.loopStart,
|
|
@@ -74172,7 +74226,6 @@ ${renderLoopUsage()}`);
|
|
|
74172
74226
|
const createdLoop = await params.agentService.createIntervalLoop({
|
|
74173
74227
|
target: params.sessionTarget,
|
|
74174
74228
|
promptText: resolvedLoopPrompt.text,
|
|
74175
|
-
canonicalPromptText: resolvedLoopPrompt.text,
|
|
74176
74229
|
promptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
74177
74230
|
promptSource: resolvedLoopPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
74178
74231
|
loopStart: slashCommand.params.loopStart,
|
|
@@ -75661,9 +75714,9 @@ function getSlackMaxChars(maxMessageChars) {
|
|
|
75661
75714
|
}
|
|
75662
75715
|
|
|
75663
75716
|
// src/channels/runtime-identity.ts
|
|
75664
|
-
import { createHash } from "node:crypto";
|
|
75717
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
75665
75718
|
function buildTokenHint(token) {
|
|
75666
|
-
return
|
|
75719
|
+
return createHash2("sha256").update(token.trim()).digest("hex").slice(0, 8);
|
|
75667
75720
|
}
|
|
75668
75721
|
|
|
75669
75722
|
// src/channels/slack/service.ts
|
|
@@ -82062,7 +82115,6 @@ function buildRecurringLoopCreateBase(state, request) {
|
|
|
82062
82115
|
function buildRecurringLoopPromptMetadata(request) {
|
|
82063
82116
|
return {
|
|
82064
82117
|
promptText: request.resolvedPrompt.text,
|
|
82065
|
-
canonicalPromptText: request.resolvedPrompt.text,
|
|
82066
82118
|
promptSummary: summarizeLoopPrompt(request.resolvedPrompt.text, request.resolvedPrompt.maintenancePrompt),
|
|
82067
82119
|
promptSource: request.resolvedPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
82068
82120
|
loopStart: request.parsed.mode === "times" ? undefined : request.parsed.loopStart,
|
|
@@ -82733,7 +82785,6 @@ function resolveProtectedControlMutationRule(state, agentId, sender) {
|
|
|
82733
82785
|
function createQueueItemForContext(params) {
|
|
82734
82786
|
return createStoredQueueItem({
|
|
82735
82787
|
promptText: params.promptText,
|
|
82736
|
-
canonicalPromptText: params.promptText,
|
|
82737
82788
|
protectedControlMutationRule: resolveProtectedControlMutationRule(params.state, params.context.sessionTarget.agentId, params.sender),
|
|
82738
82789
|
promptSummary: params.promptText,
|
|
82739
82790
|
createdBy: params.sender.providerId,
|
|
@@ -84282,18 +84333,27 @@ function renderUpdateHelp() {
|
|
|
84282
84333
|
" 2. Read docs in priority order and follow them before installing.",
|
|
84283
84334
|
" 3. npm install -g clisbot@<target> && clisbot restart",
|
|
84284
84335
|
` 4. ${renderCliCommand("status")}`,
|
|
84285
|
-
" 5. Report version, health, manual action, and useful release
|
|
84336
|
+
" 5. Report version, health, manual action, and useful release highlights.",
|
|
84286
84337
|
"",
|
|
84287
84338
|
"Docs, read in order:",
|
|
84288
84339
|
` 1. Migration index: ${GITHUB_RAW_BASE}/docs/migrations/index.md`,
|
|
84289
84340
|
" If Manual action: required, follow its runbook. If none, continue.",
|
|
84290
|
-
` 2.
|
|
84291
|
-
" Use for
|
|
84292
|
-
` 3.
|
|
84293
|
-
" Use for
|
|
84294
|
-
|
|
84341
|
+
` 2. Update guide: ${GITHUB_RAW_BASE}/docs/updates/update-guide.md`,
|
|
84342
|
+
" Use for target choice, install flow, verification, and wrong-publish recovery.",
|
|
84343
|
+
` 3. Release notes: ${GITHUB_RAW_BASE}/docs/releases/README.md`,
|
|
84344
|
+
" Use for the canonical version map and full version notes.",
|
|
84345
|
+
` 4. Release guides: ${GITHUB_RAW_BASE}/docs/updates/README.md`,
|
|
84346
|
+
" Use for shorter catch-up notes: what changed, what to try, and what to watch.",
|
|
84347
|
+
" 5. Full docs: https://github.com/longbkit/clisbot/tree/main/docs",
|
|
84295
84348
|
" Use for deep questions. If needed, fetch or clone docs and inspect relevant files.",
|
|
84296
84349
|
"",
|
|
84350
|
+
"Recovery:",
|
|
84351
|
+
" - If a version was published by mistake, publish the corrected target or tag first.",
|
|
84352
|
+
" - Then deprecate the wrong version.",
|
|
84353
|
+
" - Start with `npm login` in an attached session.",
|
|
84354
|
+
" - If npm returns a browser approval URL, keep that same session open and continue it after approval.",
|
|
84355
|
+
" - If the write command still returns EOTP, ask the operator for a current OTP and rerun the exact command with --otp=<code>.",
|
|
84356
|
+
"",
|
|
84297
84357
|
"Rules:",
|
|
84298
84358
|
" - Use npm dist-tags, not highest semver.",
|
|
84299
84359
|
" - Stable/latest is default; beta only when the user asks.",
|
|
@@ -84847,7 +84907,7 @@ async function getRuntimeOperatorSummary(params) {
|
|
|
84847
84907
|
},
|
|
84848
84908
|
agentSummaries,
|
|
84849
84909
|
channelSummaries,
|
|
84850
|
-
activeRuns: await agentService.
|
|
84910
|
+
activeRuns: await agentService.listLiveSessionRuntimes(),
|
|
84851
84911
|
configuredAgents: agentSummaries.length,
|
|
84852
84912
|
bootstrapPendingAgents: agentSummaries.filter((item) => item.bootstrapState === "missing" || item.bootstrapState === "not-bootstrapped").length,
|
|
84853
84913
|
bootstrappedAgents: agentSummaries.filter((item) => item.bootstrapState === "bootstrapped").length,
|