clisbot 0.1.45-beta.1 → 0.1.45-beta.3
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 +333 -57
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -59315,6 +59315,7 @@ var DEFAULT_AGENT_TOOL_TEMPLATES = {
|
|
|
59315
59315
|
startupDelayMs: 3000,
|
|
59316
59316
|
startupRetryCount: 2,
|
|
59317
59317
|
startupRetryDelayMs: 1000,
|
|
59318
|
+
startupReadyPattern: "(?:^|\\s)›\\s",
|
|
59318
59319
|
promptSubmitDelayMs: 150,
|
|
59319
59320
|
sessionId: {
|
|
59320
59321
|
create: {
|
|
@@ -60711,6 +60712,7 @@ function getRuntimeMonitorRestartPlan(restartBackoff, restartNumber) {
|
|
|
60711
60712
|
|
|
60712
60713
|
// src/config/schema.ts
|
|
60713
60714
|
var defaultSessionIdPattern = "\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\b";
|
|
60715
|
+
var codexStartupReadyPattern = "(?:^|\\s)›\\s";
|
|
60714
60716
|
var runnerSessionIdCreateSchema = exports_external.object({
|
|
60715
60717
|
mode: exports_external.enum(["runner", "explicit"]).default("runner"),
|
|
60716
60718
|
args: exports_external.array(exports_external.string()).default([])
|
|
@@ -61285,6 +61287,7 @@ var agentsDefaultsSchema = exports_external.object({
|
|
|
61285
61287
|
"-C",
|
|
61286
61288
|
"{workspace}"
|
|
61287
61289
|
],
|
|
61290
|
+
startupReadyPattern: codexStartupReadyPattern,
|
|
61288
61291
|
sessionId: {
|
|
61289
61292
|
create: {
|
|
61290
61293
|
mode: "runner",
|
|
@@ -61734,6 +61737,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
61734
61737
|
"-C",
|
|
61735
61738
|
"{workspace}"
|
|
61736
61739
|
],
|
|
61740
|
+
startupReadyPattern: codexStartupReadyPattern,
|
|
61737
61741
|
sessionId: {
|
|
61738
61742
|
create: {
|
|
61739
61743
|
mode: "runner",
|
|
@@ -61860,6 +61864,7 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
61860
61864
|
"-C",
|
|
61861
61865
|
"{workspace}"
|
|
61862
61866
|
],
|
|
61867
|
+
startupReadyPattern: codexStartupReadyPattern,
|
|
61863
61868
|
sessionId: {
|
|
61864
61869
|
create: {
|
|
61865
61870
|
mode: "runner",
|
|
@@ -62010,6 +62015,127 @@ function assertNoLegacyPrivilegeCommands(value, path = "root") {
|
|
|
62010
62015
|
|
|
62011
62016
|
// src/config/config-upgrade.ts
|
|
62012
62017
|
import { basename, dirname as dirname4, join as join5 } from "node:path";
|
|
62018
|
+
|
|
62019
|
+
// src/config/persisted-config.ts
|
|
62020
|
+
var defaultOwnedRunnerFields = {
|
|
62021
|
+
codex: ["startupReadyPattern"],
|
|
62022
|
+
gemini: [
|
|
62023
|
+
"startupDelayMs",
|
|
62024
|
+
"startupRetryCount",
|
|
62025
|
+
"startupRetryDelayMs",
|
|
62026
|
+
"startupReadyPattern",
|
|
62027
|
+
"startupBlockers",
|
|
62028
|
+
"promptSubmitDelayMs"
|
|
62029
|
+
]
|
|
62030
|
+
};
|
|
62031
|
+
function isRecord5(value) {
|
|
62032
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
62033
|
+
}
|
|
62034
|
+
function cloneConfig(config) {
|
|
62035
|
+
return structuredClone(config);
|
|
62036
|
+
}
|
|
62037
|
+
function areJsonEqual(left, right) {
|
|
62038
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
62039
|
+
}
|
|
62040
|
+
function nestedRecord(root, path) {
|
|
62041
|
+
let current = root;
|
|
62042
|
+
for (const segment of path) {
|
|
62043
|
+
if (!isRecord5(current)) {
|
|
62044
|
+
return;
|
|
62045
|
+
}
|
|
62046
|
+
current = current[segment];
|
|
62047
|
+
}
|
|
62048
|
+
return isRecord5(current) ? current : undefined;
|
|
62049
|
+
}
|
|
62050
|
+
function deleteIfEmpty(owner, key) {
|
|
62051
|
+
const value = owner[key];
|
|
62052
|
+
if (isRecord5(value) && Object.keys(value).length === 0) {
|
|
62053
|
+
delete owner[key];
|
|
62054
|
+
}
|
|
62055
|
+
}
|
|
62056
|
+
function defaultRunner(toolId) {
|
|
62057
|
+
return buildRunnerFromToolTemplate(toolId, DEFAULT_AGENT_TOOL_TEMPLATES[toolId], undefined);
|
|
62058
|
+
}
|
|
62059
|
+
function pruneDefaultOwnedFields(params) {
|
|
62060
|
+
const defaults = defaultRunner(params.toolId);
|
|
62061
|
+
for (const field of defaultOwnedRunnerFields[params.toolId] ?? []) {
|
|
62062
|
+
if (params.force || Object.hasOwn(params.target, field) && areJsonEqual(params.target[field], defaults[field])) {
|
|
62063
|
+
delete params.target[field];
|
|
62064
|
+
}
|
|
62065
|
+
}
|
|
62066
|
+
}
|
|
62067
|
+
function pruneAgentRunnerOverride(runner, cli) {
|
|
62068
|
+
const toolId = inferAgentCliToolId(typeof runner.command === "string" ? runner.command : cli) ?? cli;
|
|
62069
|
+
if (!toolId) {
|
|
62070
|
+
return;
|
|
62071
|
+
}
|
|
62072
|
+
const defaults = defaultRunner(toolId);
|
|
62073
|
+
for (const field of [
|
|
62074
|
+
"command",
|
|
62075
|
+
"args",
|
|
62076
|
+
"trustWorkspace",
|
|
62077
|
+
"startupDelayMs",
|
|
62078
|
+
"startupRetryCount",
|
|
62079
|
+
"startupRetryDelayMs",
|
|
62080
|
+
"startupReadyPattern",
|
|
62081
|
+
"startupBlockers",
|
|
62082
|
+
"promptSubmitDelayMs",
|
|
62083
|
+
"sessionId"
|
|
62084
|
+
]) {
|
|
62085
|
+
if (Object.hasOwn(runner, field) && areJsonEqual(runner[field], defaults[field])) {
|
|
62086
|
+
delete runner[field];
|
|
62087
|
+
}
|
|
62088
|
+
}
|
|
62089
|
+
}
|
|
62090
|
+
function pruneRunnerDefaults(config, forceRunnerStartupDefaults) {
|
|
62091
|
+
const runner = nestedRecord(config, ["agents", "defaults", "runner"]);
|
|
62092
|
+
if (!runner) {
|
|
62093
|
+
return;
|
|
62094
|
+
}
|
|
62095
|
+
for (const toolId of ["codex", "gemini"]) {
|
|
62096
|
+
const target = runner[toolId];
|
|
62097
|
+
if (isRecord5(target)) {
|
|
62098
|
+
pruneDefaultOwnedFields({
|
|
62099
|
+
target,
|
|
62100
|
+
toolId,
|
|
62101
|
+
force: forceRunnerStartupDefaults
|
|
62102
|
+
});
|
|
62103
|
+
}
|
|
62104
|
+
}
|
|
62105
|
+
}
|
|
62106
|
+
function pruneAgentOverrides(config) {
|
|
62107
|
+
const agents = nestedRecord(config, ["agents"]);
|
|
62108
|
+
if (!Array.isArray(agents?.list)) {
|
|
62109
|
+
return;
|
|
62110
|
+
}
|
|
62111
|
+
for (const agent of agents.list) {
|
|
62112
|
+
if (!isRecord5(agent) || !isRecord5(agent.runner)) {
|
|
62113
|
+
continue;
|
|
62114
|
+
}
|
|
62115
|
+
const cli = typeof agent.cli === "string" ? inferAgentCliToolId(agent.cli) ?? undefined : undefined;
|
|
62116
|
+
pruneAgentRunnerOverride(agent.runner, cli);
|
|
62117
|
+
deleteIfEmpty(agent, "runner");
|
|
62118
|
+
}
|
|
62119
|
+
}
|
|
62120
|
+
function pruneRuntimeMonitorBackoff(config) {
|
|
62121
|
+
const runtimeMonitor = nestedRecord(config, ["app", "control", "runtimeMonitor"]);
|
|
62122
|
+
if (!runtimeMonitor) {
|
|
62123
|
+
return;
|
|
62124
|
+
}
|
|
62125
|
+
const restartBackoff = runtimeMonitor?.restartBackoff;
|
|
62126
|
+
if (isRecord5(restartBackoff) && areJsonEqual(normalizeRuntimeMonitorRestartBackoff(restartBackoff), getDefaultRuntimeMonitorRestartBackoff())) {
|
|
62127
|
+
delete runtimeMonitor.restartBackoff;
|
|
62128
|
+
}
|
|
62129
|
+
}
|
|
62130
|
+
function pruneConfigForPersistence(config, options = {}) {
|
|
62131
|
+
const nextConfig = cloneConfig(config);
|
|
62132
|
+
pruneRuntimeMonitorBackoff(nextConfig);
|
|
62133
|
+
pruneRunnerDefaults(nextConfig, options.forceRunnerStartupDefaults === true);
|
|
62134
|
+
pruneAgentOverrides(nextConfig);
|
|
62135
|
+
return nextConfig;
|
|
62136
|
+
}
|
|
62137
|
+
|
|
62138
|
+
// src/config/config-upgrade.ts
|
|
62013
62139
|
function readSchemaVersion(value) {
|
|
62014
62140
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
62015
62141
|
return;
|
|
@@ -62062,8 +62188,11 @@ async function upgradeEditableConfigFileIfNeeded(configPath) {
|
|
|
62062
62188
|
exactAdmissionMode: "explicit"
|
|
62063
62189
|
}));
|
|
62064
62190
|
logUpgradeStage(`applying ${CURRENT_SCHEMA_VERSION} config to ${collapseHomePath(expandedConfigPath)}`);
|
|
62191
|
+
const persistedConfig = pruneConfigForPersistence(normalizedConfig, {
|
|
62192
|
+
forceRunnerStartupDefaults: true
|
|
62193
|
+
});
|
|
62065
62194
|
await writeTextFile(expandedConfigPath, `${JSON.stringify({
|
|
62066
|
-
...
|
|
62195
|
+
...persistedConfig,
|
|
62067
62196
|
meta: {
|
|
62068
62197
|
...normalizedConfig.meta,
|
|
62069
62198
|
lastTouchedAt: new Date().toISOString()
|
|
@@ -62090,7 +62219,6 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
62090
62219
|
const sessionStorePath = collapseHomePath(getDefaultSessionStorePath());
|
|
62091
62220
|
const workspaceTemplate = collapseHomePath(getDefaultWorkspaceTemplate());
|
|
62092
62221
|
const defaultTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
62093
|
-
const defaultRuntimeMonitorRestartBackoff2 = getDefaultRuntimeMonitorRestartBackoff();
|
|
62094
62222
|
return JSON.stringify({
|
|
62095
62223
|
meta: {
|
|
62096
62224
|
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
@@ -62135,7 +62263,6 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
62135
62263
|
maxActiveLoops: 10
|
|
62136
62264
|
},
|
|
62137
62265
|
runtimeMonitor: {
|
|
62138
|
-
restartBackoff: defaultRuntimeMonitorRestartBackoff2,
|
|
62139
62266
|
ownerAlerts: {
|
|
62140
62267
|
enabled: true,
|
|
62141
62268
|
minIntervalMinutes: 30
|
|
@@ -62407,21 +62534,6 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
62407
62534
|
gemini: {
|
|
62408
62535
|
command: "gemini",
|
|
62409
62536
|
args: ["--approval-mode=yolo", "--sandbox=false"],
|
|
62410
|
-
startupDelayMs: 15000,
|
|
62411
|
-
startupRetryCount: 2,
|
|
62412
|
-
startupRetryDelayMs: 1000,
|
|
62413
|
-
startupReadyPattern: "Type your message or @path/to/file",
|
|
62414
|
-
startupBlockers: [
|
|
62415
|
-
{
|
|
62416
|
-
pattern: "Please visit the following URL to authorize the application|Enter the authorization code:",
|
|
62417
|
-
message: "Gemini CLI is waiting for manual OAuth authorization. Authenticate Gemini once in a direct interactive terminal, or configure headless auth such as GEMINI_API_KEY or Vertex AI before routing Gemini through clisbot."
|
|
62418
|
-
},
|
|
62419
|
-
{
|
|
62420
|
-
pattern: "How would you like to authenticate for this project\\?|Failed to sign in\\.|Manual authorization is required but the current session is non-interactive",
|
|
62421
|
-
message: "Gemini CLI is blocked in its authentication setup flow or sign-in recovery. Complete Gemini authentication directly first, or switch clisbot to a headless auth path such as GEMINI_API_KEY or Vertex AI before routing prompts."
|
|
62422
|
-
}
|
|
62423
|
-
],
|
|
62424
|
-
promptSubmitDelayMs: 200,
|
|
62425
62537
|
sessionId: {
|
|
62426
62538
|
create: {
|
|
62427
62539
|
mode: "runner",
|
|
@@ -62482,7 +62594,7 @@ async function writeEditableConfig(configPath, config) {
|
|
|
62482
62594
|
exactAdmissionMode: "explicit"
|
|
62483
62595
|
}));
|
|
62484
62596
|
const nextConfig = {
|
|
62485
|
-
...normalizedConfig,
|
|
62597
|
+
...pruneConfigForPersistence(normalizedConfig),
|
|
62486
62598
|
meta: {
|
|
62487
62599
|
...normalizedConfig.meta,
|
|
62488
62600
|
lastTouchedAt: new Date().toISOString()
|
|
@@ -63381,7 +63493,7 @@ function resolveWorkspacePath(config, agentId, customWorkspace) {
|
|
|
63381
63493
|
agentId
|
|
63382
63494
|
}));
|
|
63383
63495
|
}
|
|
63384
|
-
function
|
|
63496
|
+
function areJsonEqual2(left, right) {
|
|
63385
63497
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
63386
63498
|
}
|
|
63387
63499
|
function ensureAgentMissing(config, agentId) {
|
|
@@ -63451,8 +63563,8 @@ async function addAgentToEditableConfig(params) {
|
|
|
63451
63563
|
const resolvedStartupOptions = params.startupOptions && params.startupOptions.length > 0 ? params.startupOptions : toolTemplate.startupOptions;
|
|
63452
63564
|
const runner = buildRunnerFromToolTemplate(cliTool, toolTemplate, resolvedStartupOptions);
|
|
63453
63565
|
const workspacePath = resolveWorkspacePath(config, params.agentId, params.workspace);
|
|
63454
|
-
const
|
|
63455
|
-
const runnerOverride =
|
|
63566
|
+
const defaultRunner2 = config.agents.defaults.runner[cliTool];
|
|
63567
|
+
const runnerOverride = areJsonEqual2(runner, defaultRunner2) ? undefined : runner;
|
|
63456
63568
|
if (params.bootstrap) {
|
|
63457
63569
|
await applyBootstrapTemplate(workspacePath, params.bootstrap, cliTool);
|
|
63458
63570
|
}
|
|
@@ -65912,6 +66024,19 @@ import { join as join10 } from "node:path";
|
|
|
65912
66024
|
function createLoopId() {
|
|
65913
66025
|
return randomUUID2().split("-")[0] ?? randomUUID2();
|
|
65914
66026
|
}
|
|
66027
|
+
function buildStoredLoopSender(params) {
|
|
66028
|
+
const providerId = params.providerId.trim();
|
|
66029
|
+
if (!providerId) {
|
|
66030
|
+
return;
|
|
66031
|
+
}
|
|
66032
|
+
const normalizedProviderId = params.platform === "slack" ? providerId.toUpperCase() : providerId;
|
|
66033
|
+
return {
|
|
66034
|
+
senderId: `${params.platform}:${normalizedProviderId}`,
|
|
66035
|
+
providerId: normalizedProviderId,
|
|
66036
|
+
displayName: params.displayName,
|
|
66037
|
+
handle: params.handle
|
|
66038
|
+
};
|
|
66039
|
+
}
|
|
65915
66040
|
function createStoredLoopBase(params) {
|
|
65916
66041
|
const now = Date.now();
|
|
65917
66042
|
return {
|
|
@@ -65941,10 +66066,13 @@ function deriveLegacyLoopSender(params) {
|
|
|
65941
66066
|
if (!providerId) {
|
|
65942
66067
|
return;
|
|
65943
66068
|
}
|
|
65944
|
-
|
|
65945
|
-
providerId
|
|
65946
|
-
|
|
65947
|
-
|
|
66069
|
+
if (!params.surfaceBinding?.platform) {
|
|
66070
|
+
return { providerId };
|
|
66071
|
+
}
|
|
66072
|
+
return buildStoredLoopSender({
|
|
66073
|
+
platform: params.surfaceBinding.platform,
|
|
66074
|
+
providerId
|
|
66075
|
+
});
|
|
65948
66076
|
}
|
|
65949
66077
|
function createStoredIntervalLoop(params) {
|
|
65950
66078
|
return {
|
|
@@ -67743,7 +67871,7 @@ function collapseBlankLines(lines) {
|
|
|
67743
67871
|
return collapsed;
|
|
67744
67872
|
}
|
|
67745
67873
|
var DURATION_STATUS_PATTERN = String.raw`(?:\d+(?:h|m|s))(?:\s+\d+(?:h|m|s)){0,2}`;
|
|
67746
|
-
var CODEX_WORKING_STATUS_PATTERN = new RegExp(String.raw`^(
|
|
67874
|
+
var CODEX_WORKING_STATUS_PATTERN = new RegExp(String.raw`^(?=.*\b${DURATION_STATUS_PATTERN}\b)(?=.*(?:esc\s+to\s+(?:interrupt|cancel)|interrupt|cancel|ctrl\+c))(?:[•◦·✻✽*]\s*)?Working(?:\.{3}|…)?\s*.*\)?$`, "i");
|
|
67747
67875
|
var CODEX_INTERRUPT_FOOTER_PATTERN = new RegExp(String.raw`^(?:[•◦·]\s*)?${DURATION_STATUS_PATTERN}\s*[•◦·]?\s*esc to interrupt\)?$`, "i");
|
|
67748
67876
|
var GEMINI_THINKING_STATUS_PATTERN = new RegExp(String.raw`^Thinking\.\.\. \(esc to cancel,\s*${DURATION_STATUS_PATTERN}\)$`, "i");
|
|
67749
67877
|
var CLAUDE_WORKED_STATUS_PATTERN = new RegExp(String.raw`^(?:[✻✽*]\s*)?(?:Worked|Cooked) for ${DURATION_STATUS_PATTERN}$`, "i");
|
|
@@ -68134,6 +68262,41 @@ function deriveRunningInteractionText(previousSnapshot, currentSnapshot) {
|
|
|
68134
68262
|
function deriveRunningInteractionSnapshot(currentSnapshot) {
|
|
68135
68263
|
return cleanRunningInteractionSnapshot(currentSnapshot);
|
|
68136
68264
|
}
|
|
68265
|
+
function getPromptMarker(lines) {
|
|
68266
|
+
if (looksLikeCodexSnapshot(lines)) {
|
|
68267
|
+
return /^\s*›\s/;
|
|
68268
|
+
}
|
|
68269
|
+
if (looksLikeClaudeSnapshot(lines)) {
|
|
68270
|
+
return /^\s*❯/;
|
|
68271
|
+
}
|
|
68272
|
+
if (looksLikeGeminiSnapshot(lines)) {
|
|
68273
|
+
return /^\s*>\s/;
|
|
68274
|
+
}
|
|
68275
|
+
return null;
|
|
68276
|
+
}
|
|
68277
|
+
function sliceFromLastPromptBlock(raw) {
|
|
68278
|
+
const lines = splitNormalizedLines(raw);
|
|
68279
|
+
const marker = getPromptMarker(lines);
|
|
68280
|
+
if (!marker) {
|
|
68281
|
+
return "";
|
|
68282
|
+
}
|
|
68283
|
+
for (let index = lines.length - 1;index >= 0; index -= 1) {
|
|
68284
|
+
if (!marker.test((lines[index] ?? "").trimStart())) {
|
|
68285
|
+
continue;
|
|
68286
|
+
}
|
|
68287
|
+
return lines.slice(index).join(`
|
|
68288
|
+
`);
|
|
68289
|
+
}
|
|
68290
|
+
return "";
|
|
68291
|
+
}
|
|
68292
|
+
function deriveLatestPromptInteractionSnapshot(currentSnapshot) {
|
|
68293
|
+
const promptTail = sliceFromLastPromptBlock(currentSnapshot);
|
|
68294
|
+
return promptTail ? cleanInteractionSnapshot(promptTail) : "";
|
|
68295
|
+
}
|
|
68296
|
+
function deriveLatestPromptRunningInteractionSnapshot(currentSnapshot) {
|
|
68297
|
+
const promptTail = sliceFromLastPromptBlock(currentSnapshot);
|
|
68298
|
+
return promptTail ? cleanRunningInteractionSnapshot(promptTail) : "";
|
|
68299
|
+
}
|
|
68137
68300
|
function deriveInteractionText(initialSnapshot, currentSnapshot) {
|
|
68138
68301
|
const previous = cleanInteractionSnapshot(initialSnapshot);
|
|
68139
68302
|
const current = cleanInteractionSnapshot(currentSnapshot);
|
|
@@ -68407,16 +68570,20 @@ function renderErrorInteractionBody(body, footer) {
|
|
|
68407
68570
|
${footer}`;
|
|
68408
68571
|
}
|
|
68409
68572
|
function renderSlackRunningInteraction(body, note) {
|
|
68410
|
-
|
|
68411
|
-
|
|
68573
|
+
if (note) {
|
|
68574
|
+
return body ? `${body}
|
|
68412
68575
|
|
|
68413
|
-
${
|
|
68576
|
+
_${note}_` : `_${note}_`;
|
|
68577
|
+
}
|
|
68578
|
+
return body || "_Working..._";
|
|
68414
68579
|
}
|
|
68415
68580
|
function renderTelegramRunningInteraction(body, note) {
|
|
68416
|
-
|
|
68417
|
-
|
|
68581
|
+
if (note) {
|
|
68582
|
+
return body ? `${body}
|
|
68418
68583
|
|
|
68419
|
-
${
|
|
68584
|
+
${note}` : note;
|
|
68585
|
+
}
|
|
68586
|
+
return body || "Working...";
|
|
68420
68587
|
}
|
|
68421
68588
|
function renderSlackInteraction(params) {
|
|
68422
68589
|
const body = renderInteractionBody(params);
|
|
@@ -69433,7 +69600,10 @@ class RunnerService {
|
|
|
69433
69600
|
});
|
|
69434
69601
|
try {
|
|
69435
69602
|
await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
69436
|
-
await this.
|
|
69603
|
+
await this.sessionState.touchSessionEntry(resolved, {
|
|
69604
|
+
sessionId: existing?.sessionId,
|
|
69605
|
+
runnerCommand: resolved.runner.command
|
|
69606
|
+
});
|
|
69437
69607
|
} catch (error) {
|
|
69438
69608
|
throw await this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
69439
69609
|
}
|
|
@@ -69786,9 +69956,10 @@ async function monitorTmuxRun(params) {
|
|
|
69786
69956
|
}
|
|
69787
69957
|
const hasActiveTimer = hasActiveTimerStatus(snapshot);
|
|
69788
69958
|
const currentRunningSnapshot = deriveRunningInteractionSnapshot(snapshot);
|
|
69789
|
-
const
|
|
69790
|
-
const
|
|
69791
|
-
const
|
|
69959
|
+
const promptRunningSnapshot = deriveLatestPromptRunningInteractionSnapshot(snapshot);
|
|
69960
|
+
const baselineRunningSnapshot = promptRunningSnapshot || deriveInteractionText(baselineSnapshot, snapshot) || currentRunningSnapshot;
|
|
69961
|
+
const runningDelta = promptRunningSnapshot ? "" : priorSnapshot ? deriveRunningInteractionText(priorSnapshot, snapshot) : currentRunningSnapshot;
|
|
69962
|
+
const shouldReplaceRunningSnapshot = (paneChanged || Boolean(promptRunningSnapshot)) && !runningDelta && Boolean(baselineRunningSnapshot) && baselineRunningSnapshot !== previousRunningTruth;
|
|
69792
69963
|
const nextRunningTruth = runningDelta ? previousRunningTruth ? appendInteractionText(previousRunningTruth, runningDelta) : runningDelta : shouldReplaceRunningSnapshot ? baselineRunningSnapshot : previousRunningTruth;
|
|
69793
69964
|
const runningSnapshot = runningDelta ? nextRunningTruth : shouldReplaceRunningSnapshot ? deriveBoundedRunningRewritePreview({
|
|
69794
69965
|
previousSnapshot: previousRunningTruth,
|
|
@@ -69822,8 +69993,9 @@ async function monitorTmuxRun(params) {
|
|
|
69822
69993
|
});
|
|
69823
69994
|
}
|
|
69824
69995
|
if (!hasActiveTimer && (sawActivity || sawPaneChange || sawPromptSubmission) && now - lastPaneChangeAt >= params.idleTimeoutMs) {
|
|
69996
|
+
const completedSnapshot = deriveLatestPromptInteractionSnapshot(previousSnapshot) || deriveInteractionText(baselineSnapshot, previousSnapshot);
|
|
69825
69997
|
await params.onCompleted({
|
|
69826
|
-
snapshot:
|
|
69998
|
+
snapshot: completedSnapshot,
|
|
69827
69999
|
fullSnapshot: previousSnapshot,
|
|
69828
70000
|
initialSnapshot: baselineSnapshot
|
|
69829
70001
|
});
|
|
@@ -71812,16 +71984,12 @@ function buildLoopSurfaceBinding(identity) {
|
|
|
71812
71984
|
};
|
|
71813
71985
|
}
|
|
71814
71986
|
function buildLoopSender(identity) {
|
|
71815
|
-
|
|
71816
|
-
|
|
71817
|
-
|
|
71818
|
-
}
|
|
71819
|
-
return {
|
|
71820
|
-
senderId: identity.platform === "slack" ? `slack:${providerId.toUpperCase()}` : `telegram:${providerId}`,
|
|
71821
|
-
providerId,
|
|
71987
|
+
return buildStoredLoopSender({
|
|
71988
|
+
platform: identity.platform,
|
|
71989
|
+
providerId: identity.senderId ?? "",
|
|
71822
71990
|
displayName: identity.senderName,
|
|
71823
71991
|
handle: identity.senderHandle
|
|
71824
|
-
};
|
|
71992
|
+
});
|
|
71825
71993
|
}
|
|
71826
71994
|
async function executePromptDelivery(params) {
|
|
71827
71995
|
let responseChunks = [];
|
|
@@ -79977,16 +80145,17 @@ function renderLoopsHelp() {
|
|
|
79977
80145
|
"Usage:",
|
|
79978
80146
|
` ${renderCliCommand("loops")}`,
|
|
79979
80147
|
` ${renderCliCommand("loops --help")}`,
|
|
80148
|
+
` ${renderCliCommand("loops create --help")}`,
|
|
79980
80149
|
` ${renderCliCommand("loops list")}`,
|
|
79981
80150
|
` ${renderCliCommand("loops status")}`,
|
|
79982
80151
|
` ${renderCliCommand("loops status --channel slack --target group:C1234567890 --thread-id 1712345678.123456")}`,
|
|
79983
|
-
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 every day at 07:00 check CI")}`,
|
|
79984
|
-
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 every day at 07:00 check CI --confirm")}`,
|
|
79985
|
-
` ${renderCliCommand("loops create --channel telegram --target -1001234567890 --timezone America/Los_Angeles every day at 07:00 check tickets")}`,
|
|
79986
|
-
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --new-thread every day at 07:00 check CI")}`,
|
|
79987
|
-
` ${renderCliCommand("loops --channel slack --target group:C1234567890 --thread-id 1712345678.123456 5m check CI")}`,
|
|
79988
|
-
` ${renderCliCommand("loops create --channel telegram --target -1001234567890 --topic-id 42 every weekday at 07:00 standup")}`,
|
|
79989
|
-
` ${renderCliCommand("loops --channel slack --target group:C1234567890 --thread-id 1712345678.123456 3 review backlog")}`,
|
|
80152
|
+
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --sender slack:U1234567890 every day at 07:00 check CI")}`,
|
|
80153
|
+
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --sender slack:U1234567890 every day at 07:00 check CI --confirm")}`,
|
|
80154
|
+
` ${renderCliCommand("loops create --channel telegram --target -1001234567890 --sender telegram:1276408333 --timezone America/Los_Angeles every day at 07:00 check tickets")}`,
|
|
80155
|
+
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --new-thread --sender slack:U1234567890 every day at 07:00 check CI")}`,
|
|
80156
|
+
` ${renderCliCommand("loops --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --sender slack:U1234567890 5m check CI")}`,
|
|
80157
|
+
` ${renderCliCommand("loops create --channel telegram --target -1001234567890 --topic-id 42 --sender telegram:1276408333 every weekday at 07:00 standup")}`,
|
|
80158
|
+
` ${renderCliCommand("loops --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --sender slack:U1234567890 3 review backlog")}`,
|
|
79990
80159
|
` ${renderCliCommand("loops cancel <id>")}`,
|
|
79991
80160
|
` ${renderCliCommand("loops cancel --all")}`,
|
|
79992
80161
|
` ${renderCliCommand("loops cancel --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --all")}`,
|
|
@@ -80000,6 +80169,8 @@ function renderLoopsHelp() {
|
|
|
80000
80169
|
" - use `--topic-id` for a Telegram topic id",
|
|
80001
80170
|
" - omitting the sub-surface flag targets the parent Slack channel/group/DM or the parent Telegram chat",
|
|
80002
80171
|
" - `--new-thread` is Slack-only and creates a fresh thread anchor before the loop starts",
|
|
80172
|
+
" - `--sender <principal>` is required when creating loops, using `slack:<user-id>` or `telegram:<user-id>`",
|
|
80173
|
+
" - optional creator display fields: `--sender-name <name>` and `--sender-handle <handle>`",
|
|
80003
80174
|
" - `--timezone <iana>` is a one-off wall-clock loop override and is frozen on the created loop record",
|
|
80004
80175
|
" - in Telegram forum groups, omitting `--topic-id` targets the parent chat surface; sends then follow Telegram's normal no-`message_thread_id` behavior, which is the General topic when that forum has one",
|
|
80005
80176
|
"",
|
|
@@ -80012,13 +80183,14 @@ function renderLoopsHelp() {
|
|
|
80012
80183
|
"",
|
|
80013
80184
|
"Examples:",
|
|
80014
80185
|
` ${renderCliCommand("loops status --channel slack --target group:C1234567890 --thread-id 1712345678.123456")}`,
|
|
80015
|
-
` ${renderCliCommand("loops --channel telegram --target -1001234567890 --topic-id 42 5m")}`,
|
|
80016
|
-
` ${renderCliCommand("loops --channel slack --target dm:U1234567890 --new-thread every day at 09:00 check inbox")}`,
|
|
80186
|
+
` ${renderCliCommand("loops --channel telegram --target -1001234567890 --topic-id 42 --sender telegram:1276408333 5m")}`,
|
|
80187
|
+
` ${renderCliCommand("loops --channel slack --target dm:U1234567890 --new-thread --sender slack:U1234567890 every day at 09:00 check inbox")}`,
|
|
80017
80188
|
` ${renderCliCommand("loops cancel --channel slack --target group:C1234567890 --thread-id 1712345678.123456 abc123")}`,
|
|
80018
80189
|
"Behavior:",
|
|
80019
80190
|
" - `list` always renders the global persisted loop inventory",
|
|
80020
80191
|
" - bare `status` is global; scoped `status --channel ... --target ...` matches `/loop status` for one routed session",
|
|
80021
80192
|
" - `create` and bare scoped syntax reuse the same loop parser as channel `/loop`",
|
|
80193
|
+
" - CLI loop creation fails without `--sender` so scheduled prompts can preserve creator identity",
|
|
80022
80194
|
" - the first wall-clock loop returns `confirmation_required` and does not persist until rerun with `--confirm`",
|
|
80023
80195
|
" - recurring interval loops and confirmed wall-clock loops are persisted immediately and picked up by the runtime when it is running",
|
|
80024
80196
|
" - if runtime is stopped, recurring loops activate on the next `clisbot start`",
|
|
@@ -80030,6 +80202,39 @@ function renderLoopsHelp() {
|
|
|
80030
80202
|
].join(`
|
|
80031
80203
|
`);
|
|
80032
80204
|
}
|
|
80205
|
+
function renderLoopsCreateHelp() {
|
|
80206
|
+
return [
|
|
80207
|
+
renderCliCommand("loops create"),
|
|
80208
|
+
"",
|
|
80209
|
+
"Usage:",
|
|
80210
|
+
` ${renderCliCommand("loops create --channel <slack|telegram> --target <surface> --sender <principal> <expression>")}`,
|
|
80211
|
+
` ${renderCliCommand("loops --channel <slack|telegram> --target <surface> --sender <principal> <expression>")}`,
|
|
80212
|
+
"",
|
|
80213
|
+
"Required:",
|
|
80214
|
+
" - `--channel <slack|telegram>` and `--target <surface>` select the routed session",
|
|
80215
|
+
" - `--sender <principal>` records the human creator, for example `slack:U1234567890` or `telegram:1276408333`",
|
|
80216
|
+
"",
|
|
80217
|
+
"Optional:",
|
|
80218
|
+
" - `--sender-name <name>` stores a readable creator name for scheduled prompt context",
|
|
80219
|
+
" - `--sender-handle <handle>` stores a creator handle without `@`",
|
|
80220
|
+
" - `--thread-id <ts>` targets an existing Slack thread",
|
|
80221
|
+
" - `--topic-id <id>` targets a Telegram topic",
|
|
80222
|
+
" - `--new-thread` creates a Slack thread anchor before persisting the loop",
|
|
80223
|
+
" - `--timezone <iana>` freezes a one-off wall-clock timezone on the loop record",
|
|
80224
|
+
" - `--confirm` persists the first wall-clock loop after reviewing the confirmation output",
|
|
80225
|
+
"",
|
|
80226
|
+
"Examples:",
|
|
80227
|
+
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --sender slack:U1234567890 every day at 07:00 check CI")}`,
|
|
80228
|
+
` ${renderCliCommand("loops create --channel telegram --target -1001234567890 --topic-id 42 --sender telegram:1276408333 5m check CI")}`,
|
|
80229
|
+
` ${renderCliCommand("loops create --channel slack --target dm:U1234567890 --new-thread --sender slack:U1234567890 every day at 09:00 check inbox")}`,
|
|
80230
|
+
"",
|
|
80231
|
+
"Behavior:",
|
|
80232
|
+
" - create without `--sender` fails by design",
|
|
80233
|
+
" - the `--sender` platform must match `--channel`",
|
|
80234
|
+
" - recurring CLI-created loops persist creator metadata into the session store"
|
|
80235
|
+
].join(`
|
|
80236
|
+
`);
|
|
80237
|
+
}
|
|
80033
80238
|
function renderLoopInventory(params) {
|
|
80034
80239
|
const lines = [
|
|
80035
80240
|
renderCliCommand(`loops ${params.commandLabel}`),
|
|
@@ -80237,6 +80442,9 @@ async function getScopedLoopCounts(params) {
|
|
|
80237
80442
|
// src/control/loops-cli.ts
|
|
80238
80443
|
var LOOP_BUSY_RETRY_MS = 250;
|
|
80239
80444
|
var LOOP_CONFIRM_FLAG = "--confirm";
|
|
80445
|
+
var LOOP_SENDER_FLAG = "--sender";
|
|
80446
|
+
var LOOP_SENDER_NAME_FLAG = "--sender-name";
|
|
80447
|
+
var LOOP_SENDER_HANDLE_FLAG = "--sender-handle";
|
|
80240
80448
|
function getEditableConfigPath8() {
|
|
80241
80449
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
80242
80450
|
}
|
|
@@ -80422,8 +80630,29 @@ async function executeCountLoop(params) {
|
|
|
80422
80630
|
function stripConfirmFlag(args) {
|
|
80423
80631
|
return args.filter((arg) => arg !== LOOP_CONFIRM_FLAG);
|
|
80424
80632
|
}
|
|
80633
|
+
function stripLoopCreatorArgs(args) {
|
|
80634
|
+
const remaining = [];
|
|
80635
|
+
const creatorFlags = new Set([
|
|
80636
|
+
LOOP_SENDER_FLAG,
|
|
80637
|
+
LOOP_SENDER_NAME_FLAG,
|
|
80638
|
+
LOOP_SENDER_HANDLE_FLAG
|
|
80639
|
+
]);
|
|
80640
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
80641
|
+
const current = args[index];
|
|
80642
|
+
if (current === "--") {
|
|
80643
|
+
remaining.push(...args.slice(index));
|
|
80644
|
+
break;
|
|
80645
|
+
}
|
|
80646
|
+
if (creatorFlags.has(current)) {
|
|
80647
|
+
index += 1;
|
|
80648
|
+
continue;
|
|
80649
|
+
}
|
|
80650
|
+
remaining.push(current);
|
|
80651
|
+
}
|
|
80652
|
+
return remaining;
|
|
80653
|
+
}
|
|
80425
80654
|
function parseCreateExpression(rawArgs, explicitCreateSubcommand) {
|
|
80426
|
-
const expressionArgs = stripLoopContextArgs(stripConfirmFlag(explicitCreateSubcommand ? rawArgs.slice(1) : rawArgs));
|
|
80655
|
+
const expressionArgs = stripLoopContextArgs(stripLoopCreatorArgs(stripConfirmFlag(explicitCreateSubcommand ? rawArgs.slice(1) : rawArgs)));
|
|
80427
80656
|
const expression = expressionArgs.join(" ").trim();
|
|
80428
80657
|
if (!expression) {
|
|
80429
80658
|
throw new Error("Loop creation requires an interval, count, or schedule expression.");
|
|
@@ -80444,6 +80673,43 @@ function parseLoopTimezone(args) {
|
|
|
80444
80673
|
}
|
|
80445
80674
|
return parseTimezone(timezone, "--timezone");
|
|
80446
80675
|
}
|
|
80676
|
+
function parseLoopCreator(args, addressing) {
|
|
80677
|
+
const sender = parseOptionValue3(args, LOOP_SENDER_FLAG)?.trim();
|
|
80678
|
+
if (!sender) {
|
|
80679
|
+
throw new Error(`Loop creation requires ${LOOP_SENDER_FLAG} <principal>, for example ${LOOP_SENDER_FLAG} telegram:1276408333 or ${LOOP_SENDER_FLAG} slack:U1234567890.`);
|
|
80680
|
+
}
|
|
80681
|
+
const [platform, ...providerParts] = sender.split(":");
|
|
80682
|
+
const providerId = providerParts.join(":").trim();
|
|
80683
|
+
if (platform !== "slack" && platform !== "telegram" || !providerId) {
|
|
80684
|
+
throw new Error(`${LOOP_SENDER_FLAG} must be a principal like telegram:<id> or slack:<user-id>.`);
|
|
80685
|
+
}
|
|
80686
|
+
if (addressing.channel && platform !== addressing.channel) {
|
|
80687
|
+
throw new Error(`${LOOP_SENDER_FLAG} platform must match --channel ${addressing.channel}.`);
|
|
80688
|
+
}
|
|
80689
|
+
const creator = buildStoredLoopSender({
|
|
80690
|
+
platform,
|
|
80691
|
+
providerId,
|
|
80692
|
+
displayName: parseOptionValue3(args, LOOP_SENDER_NAME_FLAG),
|
|
80693
|
+
handle: parseOptionValue3(args, LOOP_SENDER_HANDLE_FLAG)
|
|
80694
|
+
});
|
|
80695
|
+
if (!creator) {
|
|
80696
|
+
throw new Error(`${LOOP_SENDER_FLAG} must include a non-empty provider id.`);
|
|
80697
|
+
}
|
|
80698
|
+
return creator;
|
|
80699
|
+
}
|
|
80700
|
+
function quoteLoopCliValue(value) {
|
|
80701
|
+
if (/^[A-Za-z0-9_@.:/-]+$/.test(value)) {
|
|
80702
|
+
return value;
|
|
80703
|
+
}
|
|
80704
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
80705
|
+
}
|
|
80706
|
+
function renderLoopCreatorArgs(creator) {
|
|
80707
|
+
return [
|
|
80708
|
+
`${LOOP_SENDER_FLAG} ${quoteLoopCliValue(creator.senderId ?? creator.providerId ?? "")}`,
|
|
80709
|
+
creator.displayName ? `${LOOP_SENDER_NAME_FLAG} ${quoteLoopCliValue(creator.displayName)}` : undefined,
|
|
80710
|
+
creator.handle ? `${LOOP_SENDER_HANDLE_FLAG} ${quoteLoopCliValue(creator.handle)}` : undefined
|
|
80711
|
+
].filter(Boolean).join(" ");
|
|
80712
|
+
}
|
|
80447
80713
|
async function enforceLoopCreateLimits(state, parsed, maxRunsPerLoop, maxActiveLoops) {
|
|
80448
80714
|
const globalLoops = await state.sessionState.listIntervalLoops();
|
|
80449
80715
|
if (parsed.mode !== "times" && globalLoops.length >= maxActiveLoops) {
|
|
@@ -80469,6 +80735,7 @@ async function resolveLoopCreateRequest(state, rawArgs, explicitCreateSubcommand
|
|
|
80469
80735
|
const expression = parseCreateExpression(rawArgs, explicitCreateSubcommand);
|
|
80470
80736
|
const parsed = parseCreateCommand(expression);
|
|
80471
80737
|
let addressing = parseAddressing(rawArgs);
|
|
80738
|
+
const creator = parseLoopCreator(rawArgs, addressing);
|
|
80472
80739
|
if (addressing.channel === "telegram" && addressing.threadId) {
|
|
80473
80740
|
throw new Error("Telegram loop commands use `--topic-id`, not `--thread-id`.");
|
|
80474
80741
|
}
|
|
@@ -80486,6 +80753,7 @@ async function resolveLoopCreateRequest(state, rawArgs, explicitCreateSubcommand
|
|
|
80486
80753
|
return {
|
|
80487
80754
|
addressing,
|
|
80488
80755
|
context: provisionalContext,
|
|
80756
|
+
creator,
|
|
80489
80757
|
parsed,
|
|
80490
80758
|
resolvedPrompt,
|
|
80491
80759
|
resolvedTarget: provisionalResolvedTarget,
|
|
@@ -80516,6 +80784,7 @@ async function resolveLoopCreateRequest(state, rawArgs, explicitCreateSubcommand
|
|
|
80516
80784
|
addressing,
|
|
80517
80785
|
context,
|
|
80518
80786
|
deliveryContext,
|
|
80787
|
+
creator,
|
|
80519
80788
|
parsed,
|
|
80520
80789
|
resolvedPrompt,
|
|
80521
80790
|
resolvedTarget,
|
|
@@ -80556,6 +80825,8 @@ function buildRecurringLoopPromptMetadata(request) {
|
|
|
80556
80825
|
promptSummary: summarizeLoopPrompt(request.resolvedPrompt.text, request.resolvedPrompt.maintenancePrompt),
|
|
80557
80826
|
promptSource: request.resolvedPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
80558
80827
|
maintenancePrompt: request.resolvedPrompt.maintenancePrompt,
|
|
80828
|
+
createdBy: request.creator.providerId,
|
|
80829
|
+
sender: request.creator,
|
|
80559
80830
|
surfaceBinding: buildLoopSurfaceBinding2(request)
|
|
80560
80831
|
};
|
|
80561
80832
|
}
|
|
@@ -80629,7 +80900,8 @@ function renderCalendarConfirmation(params) {
|
|
|
80629
80900
|
nowMs: Date.now()
|
|
80630
80901
|
});
|
|
80631
80902
|
const timezoneClause = params.request.loopTimezone ? ` --timezone ${params.request.loopTimezone}` : "";
|
|
80632
|
-
const
|
|
80903
|
+
const senderClause = ` ${renderLoopCreatorArgs(params.request.creator)}`;
|
|
80904
|
+
const retryCommand = `${renderScopedCommand("loops create", params.request.addressing)}${senderClause}${timezoneClause} ${params.request.expression} ${LOOP_CONFIRM_FLAG}`;
|
|
80633
80905
|
return [
|
|
80634
80906
|
"confirmation_required: first wall-clock loop",
|
|
80635
80907
|
`proposed schedule: ${formatCalendarLoopSchedule(parsed)}`,
|
|
@@ -80742,6 +81014,10 @@ async function runLoopsCli(args) {
|
|
|
80742
81014
|
console.log(renderLoopsHelp());
|
|
80743
81015
|
return;
|
|
80744
81016
|
}
|
|
81017
|
+
if (subcommand === "create" && (hasFlag4(args, "--help") || hasFlag4(args, "-h"))) {
|
|
81018
|
+
console.log(renderLoopsCreateHelp());
|
|
81019
|
+
return;
|
|
81020
|
+
}
|
|
80745
81021
|
const state = await loadLoopControlState();
|
|
80746
81022
|
const addressing = parseAddressing(args);
|
|
80747
81023
|
if (subcommand === "list") {
|