clisbot 0.1.45-beta.2 → 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 +322 -50
- 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 {
|
|
@@ -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);
|
|
@@ -69437,7 +69600,10 @@ class RunnerService {
|
|
|
69437
69600
|
});
|
|
69438
69601
|
try {
|
|
69439
69602
|
await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
69440
|
-
await this.
|
|
69603
|
+
await this.sessionState.touchSessionEntry(resolved, {
|
|
69604
|
+
sessionId: existing?.sessionId,
|
|
69605
|
+
runnerCommand: resolved.runner.command
|
|
69606
|
+
});
|
|
69441
69607
|
} catch (error) {
|
|
69442
69608
|
throw await this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
69443
69609
|
}
|
|
@@ -69790,9 +69956,10 @@ async function monitorTmuxRun(params) {
|
|
|
69790
69956
|
}
|
|
69791
69957
|
const hasActiveTimer = hasActiveTimerStatus(snapshot);
|
|
69792
69958
|
const currentRunningSnapshot = deriveRunningInteractionSnapshot(snapshot);
|
|
69793
|
-
const
|
|
69794
|
-
const
|
|
69795
|
-
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;
|
|
69796
69963
|
const nextRunningTruth = runningDelta ? previousRunningTruth ? appendInteractionText(previousRunningTruth, runningDelta) : runningDelta : shouldReplaceRunningSnapshot ? baselineRunningSnapshot : previousRunningTruth;
|
|
69797
69964
|
const runningSnapshot = runningDelta ? nextRunningTruth : shouldReplaceRunningSnapshot ? deriveBoundedRunningRewritePreview({
|
|
69798
69965
|
previousSnapshot: previousRunningTruth,
|
|
@@ -69826,8 +69993,9 @@ async function monitorTmuxRun(params) {
|
|
|
69826
69993
|
});
|
|
69827
69994
|
}
|
|
69828
69995
|
if (!hasActiveTimer && (sawActivity || sawPaneChange || sawPromptSubmission) && now - lastPaneChangeAt >= params.idleTimeoutMs) {
|
|
69996
|
+
const completedSnapshot = deriveLatestPromptInteractionSnapshot(previousSnapshot) || deriveInteractionText(baselineSnapshot, previousSnapshot);
|
|
69829
69997
|
await params.onCompleted({
|
|
69830
|
-
snapshot:
|
|
69998
|
+
snapshot: completedSnapshot,
|
|
69831
69999
|
fullSnapshot: previousSnapshot,
|
|
69832
70000
|
initialSnapshot: baselineSnapshot
|
|
69833
70001
|
});
|
|
@@ -71816,16 +71984,12 @@ function buildLoopSurfaceBinding(identity) {
|
|
|
71816
71984
|
};
|
|
71817
71985
|
}
|
|
71818
71986
|
function buildLoopSender(identity) {
|
|
71819
|
-
|
|
71820
|
-
|
|
71821
|
-
|
|
71822
|
-
}
|
|
71823
|
-
return {
|
|
71824
|
-
senderId: identity.platform === "slack" ? `slack:${providerId.toUpperCase()}` : `telegram:${providerId}`,
|
|
71825
|
-
providerId,
|
|
71987
|
+
return buildStoredLoopSender({
|
|
71988
|
+
platform: identity.platform,
|
|
71989
|
+
providerId: identity.senderId ?? "",
|
|
71826
71990
|
displayName: identity.senderName,
|
|
71827
71991
|
handle: identity.senderHandle
|
|
71828
|
-
};
|
|
71992
|
+
});
|
|
71829
71993
|
}
|
|
71830
71994
|
async function executePromptDelivery(params) {
|
|
71831
71995
|
let responseChunks = [];
|
|
@@ -79981,16 +80145,17 @@ function renderLoopsHelp() {
|
|
|
79981
80145
|
"Usage:",
|
|
79982
80146
|
` ${renderCliCommand("loops")}`,
|
|
79983
80147
|
` ${renderCliCommand("loops --help")}`,
|
|
80148
|
+
` ${renderCliCommand("loops create --help")}`,
|
|
79984
80149
|
` ${renderCliCommand("loops list")}`,
|
|
79985
80150
|
` ${renderCliCommand("loops status")}`,
|
|
79986
80151
|
` ${renderCliCommand("loops status --channel slack --target group:C1234567890 --thread-id 1712345678.123456")}`,
|
|
79987
|
-
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 every day at 07:00 check CI")}`,
|
|
79988
|
-
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --thread-id 1712345678.123456 every day at 07:00 check CI --confirm")}`,
|
|
79989
|
-
` ${renderCliCommand("loops create --channel telegram --target -1001234567890 --timezone America/Los_Angeles every day at 07:00 check tickets")}`,
|
|
79990
|
-
` ${renderCliCommand("loops create --channel slack --target group:C1234567890 --new-thread every day at 07:00 check CI")}`,
|
|
79991
|
-
` ${renderCliCommand("loops --channel slack --target group:C1234567890 --thread-id 1712345678.123456 5m check CI")}`,
|
|
79992
|
-
` ${renderCliCommand("loops create --channel telegram --target -1001234567890 --topic-id 42 every weekday at 07:00 standup")}`,
|
|
79993
|
-
` ${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")}`,
|
|
79994
80159
|
` ${renderCliCommand("loops cancel <id>")}`,
|
|
79995
80160
|
` ${renderCliCommand("loops cancel --all")}`,
|
|
79996
80161
|
` ${renderCliCommand("loops cancel --channel slack --target group:C1234567890 --thread-id 1712345678.123456 --all")}`,
|
|
@@ -80004,6 +80169,8 @@ function renderLoopsHelp() {
|
|
|
80004
80169
|
" - use `--topic-id` for a Telegram topic id",
|
|
80005
80170
|
" - omitting the sub-surface flag targets the parent Slack channel/group/DM or the parent Telegram chat",
|
|
80006
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>`",
|
|
80007
80174
|
" - `--timezone <iana>` is a one-off wall-clock loop override and is frozen on the created loop record",
|
|
80008
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",
|
|
80009
80176
|
"",
|
|
@@ -80016,13 +80183,14 @@ function renderLoopsHelp() {
|
|
|
80016
80183
|
"",
|
|
80017
80184
|
"Examples:",
|
|
80018
80185
|
` ${renderCliCommand("loops status --channel slack --target group:C1234567890 --thread-id 1712345678.123456")}`,
|
|
80019
|
-
` ${renderCliCommand("loops --channel telegram --target -1001234567890 --topic-id 42 5m")}`,
|
|
80020
|
-
` ${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")}`,
|
|
80021
80188
|
` ${renderCliCommand("loops cancel --channel slack --target group:C1234567890 --thread-id 1712345678.123456 abc123")}`,
|
|
80022
80189
|
"Behavior:",
|
|
80023
80190
|
" - `list` always renders the global persisted loop inventory",
|
|
80024
80191
|
" - bare `status` is global; scoped `status --channel ... --target ...` matches `/loop status` for one routed session",
|
|
80025
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",
|
|
80026
80194
|
" - the first wall-clock loop returns `confirmation_required` and does not persist until rerun with `--confirm`",
|
|
80027
80195
|
" - recurring interval loops and confirmed wall-clock loops are persisted immediately and picked up by the runtime when it is running",
|
|
80028
80196
|
" - if runtime is stopped, recurring loops activate on the next `clisbot start`",
|
|
@@ -80034,6 +80202,39 @@ function renderLoopsHelp() {
|
|
|
80034
80202
|
].join(`
|
|
80035
80203
|
`);
|
|
80036
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
|
+
}
|
|
80037
80238
|
function renderLoopInventory(params) {
|
|
80038
80239
|
const lines = [
|
|
80039
80240
|
renderCliCommand(`loops ${params.commandLabel}`),
|
|
@@ -80241,6 +80442,9 @@ async function getScopedLoopCounts(params) {
|
|
|
80241
80442
|
// src/control/loops-cli.ts
|
|
80242
80443
|
var LOOP_BUSY_RETRY_MS = 250;
|
|
80243
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";
|
|
80244
80448
|
function getEditableConfigPath8() {
|
|
80245
80449
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
80246
80450
|
}
|
|
@@ -80426,8 +80630,29 @@ async function executeCountLoop(params) {
|
|
|
80426
80630
|
function stripConfirmFlag(args) {
|
|
80427
80631
|
return args.filter((arg) => arg !== LOOP_CONFIRM_FLAG);
|
|
80428
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
|
+
}
|
|
80429
80654
|
function parseCreateExpression(rawArgs, explicitCreateSubcommand) {
|
|
80430
|
-
const expressionArgs = stripLoopContextArgs(stripConfirmFlag(explicitCreateSubcommand ? rawArgs.slice(1) : rawArgs));
|
|
80655
|
+
const expressionArgs = stripLoopContextArgs(stripLoopCreatorArgs(stripConfirmFlag(explicitCreateSubcommand ? rawArgs.slice(1) : rawArgs)));
|
|
80431
80656
|
const expression = expressionArgs.join(" ").trim();
|
|
80432
80657
|
if (!expression) {
|
|
80433
80658
|
throw new Error("Loop creation requires an interval, count, or schedule expression.");
|
|
@@ -80448,6 +80673,43 @@ function parseLoopTimezone(args) {
|
|
|
80448
80673
|
}
|
|
80449
80674
|
return parseTimezone(timezone, "--timezone");
|
|
80450
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
|
+
}
|
|
80451
80713
|
async function enforceLoopCreateLimits(state, parsed, maxRunsPerLoop, maxActiveLoops) {
|
|
80452
80714
|
const globalLoops = await state.sessionState.listIntervalLoops();
|
|
80453
80715
|
if (parsed.mode !== "times" && globalLoops.length >= maxActiveLoops) {
|
|
@@ -80473,6 +80735,7 @@ async function resolveLoopCreateRequest(state, rawArgs, explicitCreateSubcommand
|
|
|
80473
80735
|
const expression = parseCreateExpression(rawArgs, explicitCreateSubcommand);
|
|
80474
80736
|
const parsed = parseCreateCommand(expression);
|
|
80475
80737
|
let addressing = parseAddressing(rawArgs);
|
|
80738
|
+
const creator = parseLoopCreator(rawArgs, addressing);
|
|
80476
80739
|
if (addressing.channel === "telegram" && addressing.threadId) {
|
|
80477
80740
|
throw new Error("Telegram loop commands use `--topic-id`, not `--thread-id`.");
|
|
80478
80741
|
}
|
|
@@ -80490,6 +80753,7 @@ async function resolveLoopCreateRequest(state, rawArgs, explicitCreateSubcommand
|
|
|
80490
80753
|
return {
|
|
80491
80754
|
addressing,
|
|
80492
80755
|
context: provisionalContext,
|
|
80756
|
+
creator,
|
|
80493
80757
|
parsed,
|
|
80494
80758
|
resolvedPrompt,
|
|
80495
80759
|
resolvedTarget: provisionalResolvedTarget,
|
|
@@ -80520,6 +80784,7 @@ async function resolveLoopCreateRequest(state, rawArgs, explicitCreateSubcommand
|
|
|
80520
80784
|
addressing,
|
|
80521
80785
|
context,
|
|
80522
80786
|
deliveryContext,
|
|
80787
|
+
creator,
|
|
80523
80788
|
parsed,
|
|
80524
80789
|
resolvedPrompt,
|
|
80525
80790
|
resolvedTarget,
|
|
@@ -80560,6 +80825,8 @@ function buildRecurringLoopPromptMetadata(request) {
|
|
|
80560
80825
|
promptSummary: summarizeLoopPrompt(request.resolvedPrompt.text, request.resolvedPrompt.maintenancePrompt),
|
|
80561
80826
|
promptSource: request.resolvedPrompt.maintenancePrompt ? "LOOP.md" : "custom",
|
|
80562
80827
|
maintenancePrompt: request.resolvedPrompt.maintenancePrompt,
|
|
80828
|
+
createdBy: request.creator.providerId,
|
|
80829
|
+
sender: request.creator,
|
|
80563
80830
|
surfaceBinding: buildLoopSurfaceBinding2(request)
|
|
80564
80831
|
};
|
|
80565
80832
|
}
|
|
@@ -80633,7 +80900,8 @@ function renderCalendarConfirmation(params) {
|
|
|
80633
80900
|
nowMs: Date.now()
|
|
80634
80901
|
});
|
|
80635
80902
|
const timezoneClause = params.request.loopTimezone ? ` --timezone ${params.request.loopTimezone}` : "";
|
|
80636
|
-
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}`;
|
|
80637
80905
|
return [
|
|
80638
80906
|
"confirmation_required: first wall-clock loop",
|
|
80639
80907
|
`proposed schedule: ${formatCalendarLoopSchedule(parsed)}`,
|
|
@@ -80746,6 +81014,10 @@ async function runLoopsCli(args) {
|
|
|
80746
81014
|
console.log(renderLoopsHelp());
|
|
80747
81015
|
return;
|
|
80748
81016
|
}
|
|
81017
|
+
if (subcommand === "create" && (hasFlag4(args, "--help") || hasFlag4(args, "-h"))) {
|
|
81018
|
+
console.log(renderLoopsCreateHelp());
|
|
81019
|
+
return;
|
|
81020
|
+
}
|
|
80749
81021
|
const state = await loadLoopControlState();
|
|
80750
81022
|
const addressing = parseAddressing(args);
|
|
80751
81023
|
if (subcommand === "list") {
|