@songsid/agend 2.1.4-beta.4 → 2.1.4-beta.40
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 +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/agent-endpoint.js +1 -1
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/antigravity.d.ts +9 -5
- package/dist/backend/antigravity.js +156 -31
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +62 -23
- package/dist/backend/claude-code.js +296 -43
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.d.ts +18 -0
- package/dist/backend/codex.js +148 -8
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/gemini-cli.js +2 -2
- package/dist/backend/gemini-cli.js.map +1 -1
- package/dist/backend/grok.js +2 -2
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +2 -0
- package/dist/backend/kiro.js +24 -3
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/opencode.d.ts +27 -11
- package/dist/backend/opencode.js +65 -41
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/types.d.ts +45 -3
- package/dist/backend/types.js +12 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +44 -2
- package/dist/channel/adapters/discord.js +526 -78
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +30 -0
- package/dist/channel/adapters/telegram.js +212 -18
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/agy-mcp-launcher.d.ts +2 -0
- package/dist/channel/agy-mcp-launcher.js +28 -0
- package/dist/channel/agy-mcp-launcher.js.map +1 -0
- package/dist/channel/ipc-bridge.d.ts +2 -1
- package/dist/channel/ipc-bridge.js +33 -4
- package/dist/channel/ipc-bridge.js.map +1 -1
- package/dist/channel/mcp-server.js +28 -25
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.js +4 -2
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-queue.js +62 -1
- package/dist/channel/message-queue.js.map +1 -1
- package/dist/channel/types.d.ts +31 -1
- package/dist/classic-channel-manager.d.ts +40 -3
- package/dist/classic-channel-manager.js +218 -31
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +109 -37
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +48 -7
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +12 -1
- package/dist/config.js.map +1 -1
- package/dist/cross-instance-envelope.d.ts +6 -0
- package/dist/cross-instance-envelope.js +30 -0
- package/dist/cross-instance-envelope.js.map +1 -0
- package/dist/daemon.d.ts +319 -9
- package/dist/daemon.js +1402 -255
- package/dist/daemon.js.map +1 -1
- package/dist/doctor.d.ts +41 -0
- package/dist/doctor.js +267 -0
- package/dist/doctor.js.map +1 -0
- package/dist/fleet-context.d.ts +47 -0
- package/dist/fleet-manager.d.ts +265 -11
- package/dist/fleet-manager.js +2273 -455
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-yaml-slim.d.ts +10 -0
- package/dist/fleet-yaml-slim.js +59 -0
- package/dist/fleet-yaml-slim.js.map +1 -0
- package/dist/general-knowledge/skills/backend-providers/SKILL.md +114 -0
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +3 -1
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +52 -0
- package/dist/general-knowledge/skills/development-workflow/SKILL.md +28 -0
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -0
- package/dist/general-knowledge/skills/fleet-health/SKILL.md +1 -0
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -0
- package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +1 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +64 -16
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -0
- package/dist/general-knowledge/skills/scheduling/SKILL.md +1 -0
- package/dist/general-knowledge/skills/session-management/SKILL.md +1 -0
- package/dist/general-knowledge/skills/tui-effort/SKILL.md +1 -0
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +30 -0
- package/dist/instance-lifecycle.d.ts +67 -0
- package/dist/instance-lifecycle.js +273 -8
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +12 -0
- package/dist/instructions.js +35 -1
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +958 -288
- package/dist/locale.js.map +1 -1
- package/dist/login-flows.d.ts +91 -0
- package/dist/login-flows.js +170 -0
- package/dist/login-flows.js.map +1 -0
- package/dist/login-manager.d.ts +63 -0
- package/dist/login-manager.js +134 -0
- package/dist/login-manager.js.map +1 -0
- package/dist/network-family.d.ts +18 -0
- package/dist/network-family.js +20 -0
- package/dist/network-family.js.map +1 -0
- package/dist/outbound-handlers.d.ts +8 -0
- package/dist/outbound-handlers.js +101 -18
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +7 -1
- package/dist/outbound-schemas.js +9 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/reply-dedup.d.ts +7 -8
- package/dist/reply-dedup.js +0 -0
- package/dist/reply-dedup.js.map +1 -1
- package/dist/restart-progress.d.ts +2 -0
- package/dist/restart-progress.js +3 -0
- package/dist/restart-progress.js.map +1 -1
- package/dist/scheduler/db.d.ts +12 -0
- package/dist/scheduler/db.js +59 -0
- package/dist/scheduler/db.js.map +1 -1
- package/dist/service-installer.d.ts +11 -0
- package/dist/service-installer.js +84 -18
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.js +1 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/setup-wizard.js +2 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/spawn-gate.d.ts +30 -0
- package/dist/spawn-gate.js +79 -0
- package/dist/spawn-gate.js.map +1 -0
- package/dist/storm-window.d.ts +83 -0
- package/dist/storm-window.js +251 -0
- package/dist/storm-window.js.map +1 -0
- package/dist/tips.d.ts +44 -0
- package/dist/tips.js +355 -0
- package/dist/tips.js.map +1 -0
- package/dist/tmux-manager.d.ts +16 -0
- package/dist/tmux-manager.js +110 -25
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-progress.d.ts +40 -0
- package/dist/tool-progress.js +289 -0
- package/dist/tool-progress.js.map +1 -0
- package/dist/topic-commands.d.ts +42 -4
- package/dist/topic-commands.js +354 -66
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-monitor.d.ts +15 -2
- package/dist/transcript-monitor.js +63 -17
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +131 -0
- package/dist/transcript-sources.js +580 -0
- package/dist/transcript-sources.js.map +1 -0
- package/dist/types.d.ts +13 -0
- package/dist/ui/dashboard.html +55 -32
- package/dist/ui/settings.html +200 -60
- package/dist/ui/view.html +147 -30
- package/dist/usage/format-rich.js +24 -23
- package/dist/usage/format-rich.js.map +1 -1
- package/dist/usage/i18n-keys.d.ts +7 -0
- package/dist/usage/i18n-keys.js +34 -0
- package/dist/usage/i18n-keys.js.map +1 -0
- package/dist/usage/i18n.d.ts +5 -0
- package/dist/usage/i18n.js +27 -0
- package/dist/usage/i18n.js.map +1 -0
- package/dist/usage/providers.d.ts +19 -0
- package/dist/usage/providers.js +150 -74
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/usage-api.d.ts +8 -2
- package/dist/usage/usage-api.js +45 -23
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/workflow-templates/default.md +2 -1
- package/package.json +2 -2
package/dist/topic-commands.js
CHANGED
|
@@ -10,7 +10,7 @@ import { formatCents } from "./cost-guard.js";
|
|
|
10
10
|
import { getTmuxSocketName, getTmuxSessionName } from "./paths.js";
|
|
11
11
|
import { t } from "./locale.js";
|
|
12
12
|
import { clampContextPercent, parseContextPercent, parseTokenContextRatio, } from "./context-percent.js";
|
|
13
|
-
import {
|
|
13
|
+
import { isGeneralInstance } from "./general-instance.js";
|
|
14
14
|
export { parseContextPercent, parseTokenContextRatio } from "./context-percent.js";
|
|
15
15
|
/** Sanitize a directory name into a valid instance name. Keeps Unicode letters (incl. CJK). */
|
|
16
16
|
export function sanitizeInstanceName(name) {
|
|
@@ -19,6 +19,8 @@ export function sanitizeInstanceName(name) {
|
|
|
19
19
|
}
|
|
20
20
|
/** Allowed filename for /save and /load (no path separators, no shell/inject chars). */
|
|
21
21
|
export const SAVE_FILENAME_RE = /^[\w.-]+$/;
|
|
22
|
+
/** Backends with a native side-question command that does not steer the active turn. */
|
|
23
|
+
export const BTW_SUPPORTED_BACKENDS = new Set(["claude-code"]);
|
|
22
24
|
/**
|
|
23
25
|
* Build the backend-specific session-save command, or null if the backend has no
|
|
24
26
|
* /save equivalent. kiro-cli → `/chat save <name>`; claude-code → `/export <name>.md`.
|
|
@@ -36,7 +38,8 @@ export function parseSaveFilename(text) {
|
|
|
36
38
|
return (m?.[1] ?? "").trim();
|
|
37
39
|
}
|
|
38
40
|
/** Shared message when a backend doesn't support /save. */
|
|
39
|
-
|
|
41
|
+
/** @deprecated Prefer t("save.unsupported") at the user-facing call site. */
|
|
42
|
+
export const SAVE_UNSUPPORTED_MSG = "⚠️ /save is not supported for this backend (only Kiro CLI and Claude Code).";
|
|
40
43
|
export function parsePauseWakeCommand(text) {
|
|
41
44
|
const match = text.match(/^\/(pause|wake)(?:@\S+)?(?:\s+(\S+))?$/);
|
|
42
45
|
if (!match)
|
|
@@ -103,7 +106,8 @@ export function clearCommandForBackend(backend) {
|
|
|
103
106
|
return null;
|
|
104
107
|
}
|
|
105
108
|
}
|
|
106
|
-
|
|
109
|
+
/** @deprecated Prefer t("clear.unsupported") at the user-facing call site. */
|
|
110
|
+
export const CLEAR_UNSUPPORTED_MSG = "⚠️ Clear is not supported for this backend.";
|
|
107
111
|
/**
|
|
108
112
|
* Extract context-usage % from a captured CLI pane. Scans bottom-up so the
|
|
109
113
|
* MOST RECENT prompt wins (a captured scrollback may hold several). Covers the
|
|
@@ -137,6 +141,29 @@ export function readStatuslineContextPct(dataDir, instanceName) {
|
|
|
137
141
|
return null;
|
|
138
142
|
}
|
|
139
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Claude Code's statusline payload reports the model that actually served the
|
|
146
|
+
* current session. This can differ from fleet.yaml after the CLI rejects a
|
|
147
|
+
* plan-gated model and keeps using its previous/default model.
|
|
148
|
+
*/
|
|
149
|
+
export function readStatuslineModel(dataDir, instanceName) {
|
|
150
|
+
try {
|
|
151
|
+
const statusFile = join(dataDir, "instances", instanceName, "statusline.json");
|
|
152
|
+
if (!existsSync(statusFile))
|
|
153
|
+
return null;
|
|
154
|
+
const data = JSON.parse(readFileSync(statusFile, "utf-8"));
|
|
155
|
+
const id = typeof data.model?.id === "string" ? data.model.id.trim() : "";
|
|
156
|
+
const displayName = typeof data.model?.display_name === "string"
|
|
157
|
+
? data.model.display_name.trim()
|
|
158
|
+
: "";
|
|
159
|
+
if (displayName && id && displayName !== id)
|
|
160
|
+
return `${displayName} (${id})`;
|
|
161
|
+
return id || displayName || null;
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
140
167
|
/** Live-pane scrape used by /ctx, /status Ctx, and /view sidebar. */
|
|
141
168
|
export function scrapePaneContext(instanceName, backend) {
|
|
142
169
|
try {
|
|
@@ -270,6 +297,20 @@ export class TopicCommands {
|
|
|
270
297
|
await this.handleUsageCommand(msg);
|
|
271
298
|
return true;
|
|
272
299
|
}
|
|
300
|
+
if (text === "/tips" || text.startsWith("/tips ") || text.startsWith("/tips@")) {
|
|
301
|
+
await this.handleTipsCommand(msg);
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
if (text === "/login" || text.startsWith("/login ") || text.startsWith("/login@")) {
|
|
305
|
+
await this.handleLoginCommand(msg);
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
// Telegram command names cannot contain "-", so the menu registers
|
|
309
|
+
// /install_cli; accept both spellings as typed text.
|
|
310
|
+
if (/^\/install[-_]cli(?:@\S+)?(?:\s|$)/.test(text)) {
|
|
311
|
+
await this.handleInstallCliCommand(msg);
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
273
314
|
if (text === "/update" || text.startsWith("/update@")) {
|
|
274
315
|
await this.handleUpdateCommand(msg);
|
|
275
316
|
return true;
|
|
@@ -332,6 +373,13 @@ export class TopicCommands {
|
|
|
332
373
|
const text = msg.text?.trim();
|
|
333
374
|
if (!text)
|
|
334
375
|
return false;
|
|
376
|
+
// Tips are informational and should appear where requested, including a
|
|
377
|
+
// worker topic. This also keeps Telegram text commands aligned with
|
|
378
|
+
// Discord's channel-local slash-command behavior.
|
|
379
|
+
if (text === "/tips" || text.startsWith("/tips ") || text.startsWith("/tips@")) {
|
|
380
|
+
await this.handleTipsCommand(msg);
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
335
383
|
const pauseWake = parsePauseWakeCommand(text);
|
|
336
384
|
if (pauseWake) {
|
|
337
385
|
const adapter = this.getReplyAdapter(msg);
|
|
@@ -352,7 +400,7 @@ export class TopicCommands {
|
|
|
352
400
|
return true;
|
|
353
401
|
}
|
|
354
402
|
if (pauseWake.action === "pause" && isGeneralInstance(this.ctx.fleetConfig, target)) {
|
|
355
|
-
await adapter.sendText(msg.chatId,
|
|
403
|
+
await adapter.sendText(msg.chatId, t("general.pause_forbidden"), { threadId: msg.threadId });
|
|
356
404
|
return true;
|
|
357
405
|
}
|
|
358
406
|
await adapter.sendText(msg.chatId, await this.runPauseWake(target, pauseWake.action), { threadId: msg.threadId });
|
|
@@ -379,7 +427,7 @@ export class TopicCommands {
|
|
|
379
427
|
const level = text.replace(/^\/effort(@\S+)?/, "").trim();
|
|
380
428
|
if (level) {
|
|
381
429
|
const reply = await this.ctx.applyEffort?.(instanceName, level)
|
|
382
|
-
?? "
|
|
430
|
+
?? t("effort.unsupported", this.effectiveBackend(instanceName));
|
|
383
431
|
await adapter.sendText(msg.chatId, reply, { threadId: msg.threadId });
|
|
384
432
|
}
|
|
385
433
|
else if (this.ctx.promptEffortMenu) {
|
|
@@ -410,7 +458,7 @@ export class TopicCommands {
|
|
|
410
458
|
await adapter.sendText(msg.chatId, fallback, { threadId: msg.threadId });
|
|
411
459
|
}
|
|
412
460
|
else {
|
|
413
|
-
await adapter.sendText(msg.chatId, "
|
|
461
|
+
await adapter.sendText(msg.chatId, t("model.usage"), { threadId: msg.threadId });
|
|
414
462
|
}
|
|
415
463
|
return true;
|
|
416
464
|
}
|
|
@@ -422,6 +470,39 @@ export class TopicCommands {
|
|
|
422
470
|
await adapter.sendText(msg.chatId, result, { threadId: msg.threadId });
|
|
423
471
|
return true;
|
|
424
472
|
}
|
|
473
|
+
if (text === "/steer" || text.startsWith("/steer ") || text.startsWith("/steer@")) {
|
|
474
|
+
const adapter = this.getReplyAdapter(msg);
|
|
475
|
+
if (!adapter)
|
|
476
|
+
return false;
|
|
477
|
+
// Deliberately NOT admin-gated: anyone who can speak in this topic can
|
|
478
|
+
// already send the instance a message; /steer only changes WHEN it lands
|
|
479
|
+
// (mid-turn instead of after), and it goes through the full [user:]
|
|
480
|
+
// formatting — unlike /raw, which bypasses it and is gated.
|
|
481
|
+
const content = text.replace(/^\/steer(@\S+)?/, "").trim();
|
|
482
|
+
if (!content) {
|
|
483
|
+
await adapter.sendText(msg.chatId, t("steer.usage"), { threadId: msg.threadId });
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
const result = this.sendSteer(instanceName, content, msg);
|
|
487
|
+
await adapter.sendText(msg.chatId, result, { threadId: msg.threadId });
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
if (text === "/btw" || text.startsWith("/btw ") || text.startsWith("/btw@")) {
|
|
491
|
+
const adapter = this.getReplyAdapter(msg);
|
|
492
|
+
if (!adapter)
|
|
493
|
+
return false;
|
|
494
|
+
// Like /steer, /btw is not admin-gated: anyone who can send the agent a
|
|
495
|
+
// normal message may ask a side question. Claude owns the isolation from
|
|
496
|
+
// the active turn; AgEnD only submits its native command immediately.
|
|
497
|
+
const content = text.replace(/^\/btw(@\S+)?/, "").trim();
|
|
498
|
+
if (!content) {
|
|
499
|
+
await adapter.sendText(msg.chatId, t("btw.usage"), { threadId: msg.threadId });
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
const result = this.sendBtw(instanceName, content, msg);
|
|
503
|
+
await adapter.sendText(msg.chatId, result, { threadId: msg.threadId });
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
425
506
|
if (text === "/clear" || text.startsWith("/clear@")) {
|
|
426
507
|
const adapter = this.getReplyAdapter(msg);
|
|
427
508
|
if (!adapter)
|
|
@@ -430,8 +511,13 @@ export class TopicCommands {
|
|
|
430
511
|
await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
|
|
431
512
|
return true;
|
|
432
513
|
}
|
|
433
|
-
|
|
434
|
-
|
|
514
|
+
if (!this.ctx.promptClearConfirmation) {
|
|
515
|
+
await adapter.sendText(msg.chatId, t("clear.prompt_unavailable"), { threadId: msg.threadId });
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
const fallback = await this.ctx.promptClearConfirmation(instanceName, msg.threadId ?? msg.chatId, adapter, msg.chatId, msg.threadId);
|
|
519
|
+
if (fallback)
|
|
520
|
+
await adapter.sendText(msg.chatId, fallback, { threadId: msg.threadId });
|
|
435
521
|
return true;
|
|
436
522
|
}
|
|
437
523
|
if (text === "/cancel" || text.startsWith("/cancel@")) {
|
|
@@ -470,7 +556,7 @@ export class TopicCommands {
|
|
|
470
556
|
}
|
|
471
557
|
async runPauseWake(instanceName, action) {
|
|
472
558
|
if (action === "pause" && isGeneralInstance(this.ctx.fleetConfig, instanceName)) {
|
|
473
|
-
return
|
|
559
|
+
return t("general.pause_forbidden");
|
|
474
560
|
}
|
|
475
561
|
try {
|
|
476
562
|
const result = await this.ctx.changeInstancePauseState(instanceName, action);
|
|
@@ -501,7 +587,10 @@ export class TopicCommands {
|
|
|
501
587
|
const contextLine = context == null ? null : formatContextUsageLine(context, tokenRatio);
|
|
502
588
|
// Effective model (resolves per-instance → fleet default → classic channel →
|
|
503
589
|
// the CLI's own default) via the shared resolver, so /ctx and /model agree.
|
|
504
|
-
const modelDisplay =
|
|
590
|
+
const modelDisplay = backend === "claude-code"
|
|
591
|
+
? readStatuslineModel(this.ctx.dataDir, instanceName)
|
|
592
|
+
?? this.ctx.modelDisplayForInstance?.(instanceName)
|
|
593
|
+
: this.ctx.modelDisplayForInstance?.(instanceName);
|
|
505
594
|
const modelLine = modelDisplay ? `\n${t("ctx.model", modelDisplay)}` : "";
|
|
506
595
|
return context != null
|
|
507
596
|
? `${contextLine}\n${t("ctx.backend", backend)}${modelLine}\n${t("ctx.instance", instanceName)}`
|
|
@@ -519,22 +608,92 @@ export class TopicCommands {
|
|
|
519
608
|
?? this.ctx.fleetConfig?.defaults?.backend ?? "claude-code";
|
|
520
609
|
const cmd = compactCommandForBackend(backend);
|
|
521
610
|
ipc.send({ type: "raw_paste", content: cmd });
|
|
522
|
-
return
|
|
611
|
+
return t("compact.sent", cmd);
|
|
523
612
|
}
|
|
524
|
-
return "
|
|
613
|
+
return t("compact.not_connected");
|
|
614
|
+
}
|
|
615
|
+
/** Whether the instance backend exposes a verified full-reset command. */
|
|
616
|
+
supportsClear(instanceName) {
|
|
617
|
+
return clearCommandForBackend(this.effectiveBackend(instanceName)) !== null;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Steer: interject a message into the instance's CURRENT turn. The daemon
|
|
621
|
+
* pastes it into the busy CLI instead of queueing for idle (falling back to
|
|
622
|
+
* the queue if the TUI swallows busy input — see Daemon.steerMessage).
|
|
623
|
+
*/
|
|
624
|
+
/**
|
|
625
|
+
* Backends whose TUI accepts a busy-pane paste as steering input,
|
|
626
|
+
* live-verified: claude-code and codex buffer-then-submit at the turn
|
|
627
|
+
* boundary, grok accepts it in its input line. kiro's legacy TUI swallows
|
|
628
|
+
* the paste outright, and opencode/antigravity are unverified — for those
|
|
629
|
+
* the user gets an honest "not supported" instead of a silent queue
|
|
630
|
+
* fallback that looks like a steer but behaves like a normal message.
|
|
631
|
+
*/
|
|
632
|
+
static STEER_SUPPORTED_BACKENDS = new Set(["claude-code", "codex", "grok", "mock"]);
|
|
633
|
+
sendSteer(instanceName, content, msg) {
|
|
634
|
+
const backend = this.effectiveBackend(instanceName);
|
|
635
|
+
if (!TopicCommands.STEER_SUPPORTED_BACKENDS.has(backend)) {
|
|
636
|
+
return t("steer.unsupported", backend);
|
|
637
|
+
}
|
|
638
|
+
const ipc = this.ctx.instanceIpcClients.get(instanceName);
|
|
639
|
+
if (!ipc?.connected)
|
|
640
|
+
return t("steer.not_connected");
|
|
641
|
+
ipc.send({
|
|
642
|
+
type: "steer",
|
|
643
|
+
content,
|
|
644
|
+
delivery_epoch: this.ctx.getDeliveryEpoch?.(instanceName) ?? undefined,
|
|
645
|
+
meta: {
|
|
646
|
+
chat_id: msg.chatId,
|
|
647
|
+
message_id: msg.messageId ?? "",
|
|
648
|
+
user: msg.username,
|
|
649
|
+
user_id: msg.userId,
|
|
650
|
+
thread_id: msg.threadId ?? "",
|
|
651
|
+
adapter_id: msg.adapterId ?? "",
|
|
652
|
+
source: msg.source,
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
return t("steer.sent", instanceName);
|
|
656
|
+
}
|
|
657
|
+
/** Ask a Claude Code side question without folding it into the active turn. */
|
|
658
|
+
sendBtw(instanceName, content, msg) {
|
|
659
|
+
const backend = this.effectiveBackend(instanceName);
|
|
660
|
+
if (!BTW_SUPPORTED_BACKENDS.has(backend))
|
|
661
|
+
return t("btw.unsupported", backend);
|
|
662
|
+
const ipc = this.ctx.instanceIpcClients.get(instanceName);
|
|
663
|
+
if (!ipc?.connected)
|
|
664
|
+
return t("btw.not_connected");
|
|
665
|
+
ipc.send({
|
|
666
|
+
type: "btw",
|
|
667
|
+
content,
|
|
668
|
+
delivery_epoch: this.ctx.getDeliveryEpoch?.(instanceName) ?? undefined,
|
|
669
|
+
meta: {
|
|
670
|
+
chat_id: msg.chatId,
|
|
671
|
+
message_id: msg.messageId ?? "",
|
|
672
|
+
user: msg.username,
|
|
673
|
+
user_id: msg.userId,
|
|
674
|
+
thread_id: msg.threadId ?? "",
|
|
675
|
+
adapter_id: msg.adapterId ?? "",
|
|
676
|
+
source: msg.source,
|
|
677
|
+
},
|
|
678
|
+
});
|
|
679
|
+
return t("btw.sent", instanceName);
|
|
525
680
|
}
|
|
526
681
|
/** Send the backend-appropriate full conversation reset through raw_paste. */
|
|
527
682
|
async sendClear(instanceName) {
|
|
528
683
|
const backend = this.effectiveBackend(instanceName);
|
|
529
684
|
const cmd = clearCommandForBackend(backend);
|
|
530
685
|
if (!cmd)
|
|
531
|
-
return
|
|
686
|
+
return t("clear.unsupported");
|
|
532
687
|
const ipc = this.ctx.instanceIpcClients.get(instanceName);
|
|
533
688
|
if (ipc?.connected) {
|
|
534
|
-
ipc.send({
|
|
535
|
-
|
|
689
|
+
ipc.send({
|
|
690
|
+
type: "raw_paste",
|
|
691
|
+
content: cmd,
|
|
692
|
+
...(backend === "kiro-cli" ? { confirm_clear: true } : {}),
|
|
693
|
+
});
|
|
694
|
+
return t("clear.sent", cmd);
|
|
536
695
|
}
|
|
537
|
-
return "
|
|
696
|
+
return t("clear.not_connected");
|
|
538
697
|
}
|
|
539
698
|
/** Send a backend-appropriate session-save command to a fleet-topic instance. */
|
|
540
699
|
async sendSave(instanceName, filename) {
|
|
@@ -546,13 +705,13 @@ export class TopicCommands {
|
|
|
546
705
|
?? this.ctx.fleetConfig?.defaults?.backend ?? "claude-code";
|
|
547
706
|
const cmd = saveCommandForBackend(backend, filename);
|
|
548
707
|
if (!cmd)
|
|
549
|
-
return
|
|
708
|
+
return t("save.unsupported");
|
|
550
709
|
const ipc = this.ctx.instanceIpcClients.get(instanceName);
|
|
551
710
|
if (ipc?.connected) {
|
|
552
711
|
ipc.send({ type: "raw_paste", content: cmd });
|
|
553
|
-
return
|
|
712
|
+
return t("save.sent", cmd, instanceName);
|
|
554
713
|
}
|
|
555
|
-
return "
|
|
714
|
+
return t("save.not_connected");
|
|
556
715
|
}
|
|
557
716
|
async handleRestartCommand(msg) {
|
|
558
717
|
const adapter = this.getReplyAdapter(msg);
|
|
@@ -569,8 +728,8 @@ export class TopicCommands {
|
|
|
569
728
|
process.kill(process.pid, "SIGUSR2");
|
|
570
729
|
}
|
|
571
730
|
/**
|
|
572
|
-
* `/usage` — AI subscription usage for
|
|
573
|
-
*
|
|
731
|
+
* `/usage` — AI subscription usage for CLI backends used by running or paused
|
|
732
|
+
* instances, one compact message. Same permission level as /ctx (none): whoever
|
|
574
733
|
* can talk to the fleet may see how much headroom it has left. Vendor rate
|
|
575
734
|
* limits are protected by the shared 5-minute cache, not by gating callers.
|
|
576
735
|
*/
|
|
@@ -581,7 +740,7 @@ export class TopicCommands {
|
|
|
581
740
|
try {
|
|
582
741
|
const { getUsageSnapshot } = await import("./usage/usage-api.js");
|
|
583
742
|
const { renderUsageHtml, renderUsageMarkdown } = await import("./usage/format-rich.js");
|
|
584
|
-
const payload = await getUsageSnapshot();
|
|
743
|
+
const payload = await getUsageSnapshot(false, this.ctx.getActiveUsageProviderIds?.());
|
|
585
744
|
// Each platform gets its native rich format: Telegram needs parse_mode
|
|
586
745
|
// HTML; Discord renders Markdown in plain content. Anything else falls back
|
|
587
746
|
// to Markdown, which degrades to readable text.
|
|
@@ -593,7 +752,129 @@ export class TopicCommands {
|
|
|
593
752
|
}
|
|
594
753
|
}
|
|
595
754
|
catch (err) {
|
|
596
|
-
await adapter.sendText(msg.chatId,
|
|
755
|
+
await adapter.sendText(msg.chatId, t("usage.failed", err.message), { threadId: msg.threadId });
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* `/login [backend] | code <text> | cancel` — remote CLI re-login.
|
|
760
|
+
* Fleet-admin only: the flow emits live authorization URLs/codes, and anyone
|
|
761
|
+
* completing one binds their own account to this fleet's CLI.
|
|
762
|
+
*/
|
|
763
|
+
async handleLoginCommand(msg) {
|
|
764
|
+
const adapter = this.getReplyAdapter(msg);
|
|
765
|
+
if (!adapter)
|
|
766
|
+
return;
|
|
767
|
+
if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
|
|
768
|
+
await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
if (!this.ctx.startLoginSession || !this.ctx.promptLoginBackends
|
|
772
|
+
|| !this.ctx.loginSubmitInput || !this.ctx.cancelLoginSession) {
|
|
773
|
+
await adapter.sendText(msg.chatId, t("login.no_session"), { threadId: msg.threadId });
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
const arg = msg.text.trim().replace(/^\/login(?:@\S+)?/, "").trim();
|
|
777
|
+
const chat = {
|
|
778
|
+
adapter,
|
|
779
|
+
adapterId: msg.adapterId ?? adapter.id,
|
|
780
|
+
chatId: msg.chatId,
|
|
781
|
+
threadId: msg.threadId,
|
|
782
|
+
};
|
|
783
|
+
if (!arg) {
|
|
784
|
+
await this.ctx.promptLoginBackends(chat);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (arg === "cancel") {
|
|
788
|
+
await adapter.sendText(msg.chatId, await this.ctx.cancelLoginSession(), { threadId: msg.threadId });
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
if (arg.startsWith("code ")) {
|
|
792
|
+
const input = arg.slice(5).trim();
|
|
793
|
+
const reply = input ? await this.ctx.loginSubmitInput(input) : t("login.usage");
|
|
794
|
+
await adapter.sendText(msg.chatId, reply, { threadId: msg.threadId });
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
if (/\s/.test(arg)) {
|
|
798
|
+
await adapter.sendText(msg.chatId, t("login.usage"), { threadId: msg.threadId });
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
const started = await this.ctx.startLoginSession(arg, chat);
|
|
802
|
+
// null → the still-valid-auth confirmation prompt was posted instead.
|
|
803
|
+
if (started)
|
|
804
|
+
await adapter.sendText(msg.chatId, started, { threadId: msg.threadId });
|
|
805
|
+
}
|
|
806
|
+
/** `/install-cli <backend> | cancel` — remote CLI install. Fleet-admin only. */
|
|
807
|
+
async handleInstallCliCommand(msg) {
|
|
808
|
+
const adapter = this.getReplyAdapter(msg);
|
|
809
|
+
if (!adapter)
|
|
810
|
+
return;
|
|
811
|
+
if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
|
|
812
|
+
await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (!this.ctx.startInstallSession || !this.ctx.cancelInstallSession) {
|
|
816
|
+
await adapter.sendText(msg.chatId, t("install.no_session"), { threadId: msg.threadId });
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
const arg = msg.text.trim().replace(/^\/install[-_]cli(?:@\S+)?/, "").trim();
|
|
820
|
+
if (!arg || /\s/.test(arg)) {
|
|
821
|
+
await adapter.sendText(msg.chatId, t("install.usage"), { threadId: msg.threadId });
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
if (arg === "cancel") {
|
|
825
|
+
await adapter.sendText(msg.chatId, await this.ctx.cancelInstallSession(), { threadId: msg.threadId });
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
const chat = {
|
|
829
|
+
adapter,
|
|
830
|
+
adapterId: msg.adapterId ?? adapter.id,
|
|
831
|
+
chatId: msg.chatId,
|
|
832
|
+
threadId: msg.threadId,
|
|
833
|
+
};
|
|
834
|
+
await adapter.sendText(msg.chatId, await this.ctx.startInstallSession(arg, chat), { threadId: msg.threadId });
|
|
835
|
+
}
|
|
836
|
+
async handleTipsCommand(msg) {
|
|
837
|
+
const adapter = this.getReplyAdapter(msg);
|
|
838
|
+
if (!adapter || !this.ctx.fleetConfig)
|
|
839
|
+
return;
|
|
840
|
+
const mode = msg.text.trim().replace(/^\/tips(?:@\S+)?/, "").trim().toLowerCase();
|
|
841
|
+
if (mode === "advanced on") {
|
|
842
|
+
if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
|
|
843
|
+
await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
await adapter.sendText(msg.chatId, this.ctx.unlockAdvancedTips?.(msg.userId)
|
|
847
|
+
? t("tips.advanced.unlocked")
|
|
848
|
+
: t("tips.unavailable"), { threadId: msg.threadId });
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (mode === "on" || mode === "off") {
|
|
852
|
+
if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
|
|
853
|
+
await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
this.ctx.fleetConfig.defaults.tips = mode === "on";
|
|
857
|
+
this.ctx.saveFleetConfig();
|
|
858
|
+
await adapter.sendText(msg.chatId, t(mode === "on" ? "tips.enabled" : "tips.disabled"), {
|
|
859
|
+
threadId: msg.threadId,
|
|
860
|
+
});
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (mode) {
|
|
864
|
+
await adapter.sendText(msg.chatId, t("tips.usage"), { threadId: msg.threadId });
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
const result = await this.ctx.promptTip?.(
|
|
868
|
+
// The instance name scopes prompt cleanup; the Tip is posted at the
|
|
869
|
+
// caller's current chat/thread below.
|
|
870
|
+
Object.entries(this.ctx.fleetConfig.instances)
|
|
871
|
+
.find(([, config]) => config.general_topic === true
|
|
872
|
+
&& (!config.channel_id || config.channel_id === msg.adapterId))?.[0] ?? "general", adapter, msg.chatId, msg.threadId) ?? "unavailable";
|
|
873
|
+
if (result === "empty") {
|
|
874
|
+
await adapter.sendText(msg.chatId, t("tips.empty"), { threadId: msg.threadId });
|
|
875
|
+
}
|
|
876
|
+
else if (result === "unavailable") {
|
|
877
|
+
await adapter.sendText(msg.chatId, t("tips.unavailable"), { threadId: msg.threadId });
|
|
597
878
|
}
|
|
598
879
|
}
|
|
599
880
|
async handleStatusCommand(msg) {
|
|
@@ -622,7 +903,7 @@ export class TopicCommands {
|
|
|
622
903
|
/** Get fleet status as markdown text (shared by TG + DC) */
|
|
623
904
|
async getStatusText() {
|
|
624
905
|
if (!this.ctx.fleetConfig)
|
|
625
|
-
return "
|
|
906
|
+
return t("status.no_config");
|
|
626
907
|
const rows = [];
|
|
627
908
|
let pausedCount = 0;
|
|
628
909
|
const fleetNames = new Set(Object.keys(this.ctx.fleetConfig.instances));
|
|
@@ -659,10 +940,10 @@ export class TopicCommands {
|
|
|
659
940
|
icon = "🔴";
|
|
660
941
|
else
|
|
661
942
|
icon = "⚪";
|
|
662
|
-
const stateLabel = executionState === "idle" ? "
|
|
663
|
-
: executionState === "working" ? "
|
|
664
|
-
: executionState === "stuck" ? "
|
|
665
|
-
: executionState === "paused" ? "
|
|
943
|
+
const stateLabel = executionState === "idle" ? `🟢 ${t("state.idle")}`
|
|
944
|
+
: executionState === "working" ? `🔵 ${t("state.working")}`
|
|
945
|
+
: executionState === "stuck" ? `🔴 ${t("state.stuck")}`
|
|
946
|
+
: executionState === "paused" ? `⏸ ${t("state.paused")}`
|
|
666
947
|
: "—";
|
|
667
948
|
const displayName = this.shortInstanceName(name);
|
|
668
949
|
// IPC reachability moved here from /sysinfo: it is per-instance health, and
|
|
@@ -674,27 +955,27 @@ export class TopicCommands {
|
|
|
674
955
|
rows.push(`| ${displayName} | ${backend} | ${contextStr} | ${effort} | ${formatCents(costCents)} | ${icon} | ${stateLabel} | ${ipc} |`);
|
|
675
956
|
}
|
|
676
957
|
if (rows.length === 0)
|
|
677
|
-
return "
|
|
958
|
+
return t("status.no_instances");
|
|
678
959
|
const lines = [
|
|
679
|
-
|
|
960
|
+
`## ${t("status.title")}`,
|
|
680
961
|
"",
|
|
681
|
-
"
|
|
962
|
+
t("status.table_header"),
|
|
682
963
|
"|----------|---------|-----|--------|------|--------|-------|-----|",
|
|
683
964
|
...rows,
|
|
684
965
|
"",
|
|
685
|
-
|
|
966
|
+
t("status.paused_count", pausedCount),
|
|
686
967
|
];
|
|
687
968
|
const limitCents = this.ctx.costGuard?.getLimitCents() ?? 0;
|
|
688
969
|
const totalCents = this.ctx.costGuard?.getFleetTotalCents() ?? 0;
|
|
689
970
|
if (limitCents > 0) {
|
|
690
|
-
lines.push("",
|
|
971
|
+
lines.push("", t("status.daily_cost", formatCents(totalCents), formatCents(limitCents)));
|
|
691
972
|
}
|
|
692
973
|
// Adapter states (only show if any are not connected)
|
|
693
974
|
const adapterStates = this.ctx.getAdapterStates?.();
|
|
694
975
|
if (adapterStates && adapterStates.size > 0) {
|
|
695
976
|
const issues = [...adapterStates.entries()].filter(([, s]) => s.status !== "connected");
|
|
696
977
|
if (issues.length > 0) {
|
|
697
|
-
lines.push("", "
|
|
978
|
+
lines.push("", `**${t("status.adapters")}**`);
|
|
698
979
|
for (const [id, s] of adapterStates) {
|
|
699
980
|
const icon = s.status === "connected" ? "✅" : s.status === "retrying" ? "🔄" : "❌";
|
|
700
981
|
lines.push(` ${icon} ${id}: ${s.status}${s.lastError ? ` (${s.lastError.slice(0, 60)})` : ""}`);
|
|
@@ -725,17 +1006,17 @@ export class TopicCommands {
|
|
|
725
1006
|
const require = createRequire(import.meta.url);
|
|
726
1007
|
const agendVersion = require("../package.json").version ?? "unknown";
|
|
727
1008
|
return [
|
|
728
|
-
|
|
1009
|
+
`## ${t("sysinfo.title")}`,
|
|
729
1010
|
"",
|
|
730
|
-
"
|
|
1011
|
+
`| ${t("sysinfo.metric")} | ${t("sysinfo.value")} |`,
|
|
731
1012
|
"|--------|-------|",
|
|
732
1013
|
`| AgEnD | v${agendVersion} |`,
|
|
733
1014
|
`| OS | ${process.platform} ${osRelease()} (${process.arch}) |`,
|
|
734
1015
|
`| Node | ${process.version} |`,
|
|
735
1016
|
`| tmux | ${tmuxVersion()} |`,
|
|
736
|
-
`|
|
|
737
|
-
`|
|
|
738
|
-
`|
|
|
1017
|
+
`| ${t("sysinfo.uptime")} | ${upHours}h ${upMins}m |`,
|
|
1018
|
+
`| ${t("sysinfo.memory")} | ${info.memory_mb.rss} MB RSS |`,
|
|
1019
|
+
`| ${t("sysinfo.heap")} | ${info.memory_mb.heapUsed} / ${info.memory_mb.heapTotal} MB |`,
|
|
739
1020
|
].join("\n");
|
|
740
1021
|
}
|
|
741
1022
|
async handleUpdateCommand(msg) {
|
|
@@ -788,11 +1069,11 @@ export class TopicCommands {
|
|
|
788
1069
|
timeout: 30_000,
|
|
789
1070
|
encoding: "utf-8",
|
|
790
1071
|
});
|
|
791
|
-
output = stripAnsi(stdout) || "
|
|
1072
|
+
output = stripAnsi(stdout) || t("doctor.no_output");
|
|
792
1073
|
}
|
|
793
1074
|
catch (err) {
|
|
794
1075
|
const e = err;
|
|
795
|
-
output = stripAnsi(e.stdout ?? e.message ?? "
|
|
1076
|
+
output = stripAnsi(e.stdout ?? e.message ?? t("doctor.failed"));
|
|
796
1077
|
}
|
|
797
1078
|
await adapter.sendText(chatId, output, { threadId });
|
|
798
1079
|
}
|
|
@@ -801,7 +1082,7 @@ export class TopicCommands {
|
|
|
801
1082
|
const adapter = this.getReplyAdapter(msg);
|
|
802
1083
|
if (!adapter)
|
|
803
1084
|
return;
|
|
804
|
-
await adapter.sendText(msg.chatId, "
|
|
1085
|
+
await adapter.sendText(msg.chatId, t("topic.unbound"), { threadId: msg.threadId });
|
|
805
1086
|
}
|
|
806
1087
|
/** Handle topic deletion — stop daemon and remove from config */
|
|
807
1088
|
async handleTopicDeleted(threadId) {
|
|
@@ -914,21 +1195,26 @@ export class TopicCommands {
|
|
|
914
1195
|
}
|
|
915
1196
|
try {
|
|
916
1197
|
const fleetCommands = [
|
|
917
|
-
{ command: "status", description: "🔒
|
|
918
|
-
{ command: "sysinfo", description: "
|
|
919
|
-
{ command: "dashboard", description: "🔒
|
|
920
|
-
{ command: "ctx", description: "
|
|
921
|
-
{ command: "compact", description: "
|
|
922
|
-
{ command: "
|
|
923
|
-
{ command: "
|
|
924
|
-
{ command: "
|
|
925
|
-
{ command: "
|
|
926
|
-
{ command: "
|
|
927
|
-
{ command: "
|
|
928
|
-
{ command: "
|
|
929
|
-
{ command: "
|
|
930
|
-
{ command: "
|
|
931
|
-
{ command: "
|
|
1198
|
+
{ command: "status", description: "🔒 " + t("slash.status") },
|
|
1199
|
+
{ command: "sysinfo", description: t("slash.sysinfo") },
|
|
1200
|
+
{ command: "dashboard", description: "🔒 " + t("slash.dashboard") },
|
|
1201
|
+
{ command: "ctx", description: t("slash.ctx") },
|
|
1202
|
+
{ command: "compact", description: t("slash.compact") },
|
|
1203
|
+
{ command: "steer", description: t("slash.steer") },
|
|
1204
|
+
{ command: "btw", description: t("slash.btw") },
|
|
1205
|
+
{ command: "clear", description: "🔒 " + t("slash.clear") },
|
|
1206
|
+
{ command: "model", description: "🔒 " + t("slash.model") },
|
|
1207
|
+
{ command: "effort", description: "🔒 " + t("slash.effort") },
|
|
1208
|
+
{ command: "pause", description: "🔒 " + t("slash.pause") },
|
|
1209
|
+
{ command: "wake", description: "🔒 " + t("slash.wake") },
|
|
1210
|
+
{ command: "restart", description: "🔒 " + t("slash.restart") },
|
|
1211
|
+
{ command: "collab", description: "🔒 " + t("slash.collab") },
|
|
1212
|
+
{ command: "update", description: "🔒 " + t("slash.update") },
|
|
1213
|
+
{ command: "doctor", description: "🔒 " + t("slash.doctor") },
|
|
1214
|
+
{ command: "login", description: "🔒 " + t("slash.login") },
|
|
1215
|
+
{ command: "install_cli", description: "🔒 " + t("slash.install_cli") },
|
|
1216
|
+
{ command: "usage", description: t("slash.usage") },
|
|
1217
|
+
{ command: "tips", description: t("slash.tips") },
|
|
932
1218
|
];
|
|
933
1219
|
const setCommands = async (commands, scope) => {
|
|
934
1220
|
const response = await fetch(`https://api.telegram.org/bot${botToken}/setMyCommands`, {
|
|
@@ -949,15 +1235,17 @@ export class TopicCommands {
|
|
|
949
1235
|
}
|
|
950
1236
|
};
|
|
951
1237
|
const classicCommands = [
|
|
952
|
-
{ command: "start", description: "🔒
|
|
953
|
-
{ command: "stop", description: "🔒
|
|
954
|
-
{ command: "compact", description: "🔒
|
|
955
|
-
{ command: "
|
|
956
|
-
{ command: "
|
|
957
|
-
{ command: "
|
|
958
|
-
{ command: "
|
|
959
|
-
{ command: "
|
|
960
|
-
{ command: "
|
|
1238
|
+
{ command: "start", description: "🔒 " + t("slash.start") },
|
|
1239
|
+
{ command: "stop", description: "🔒 " + t("slash.stop") },
|
|
1240
|
+
{ command: "compact", description: "🔒 " + t("slash.compact") },
|
|
1241
|
+
{ command: "steer", description: t("slash.steer") },
|
|
1242
|
+
{ command: "btw", description: t("slash.btw") },
|
|
1243
|
+
{ command: "clear", description: "🔒 " + t("slash.clear") },
|
|
1244
|
+
{ command: "model", description: "🔒 " + t("slash.model") },
|
|
1245
|
+
{ command: "effort", description: "🔒 " + t("slash.effort") },
|
|
1246
|
+
{ command: "pause", description: "🔒 " + t("slash.pause") },
|
|
1247
|
+
{ command: "wake", description: "🔒 " + t("slash.wake") },
|
|
1248
|
+
{ command: "ctx", description: t("slash.ctx") },
|
|
961
1249
|
];
|
|
962
1250
|
// A chat_administrators scope has higher precedence than the chat scope.
|
|
963
1251
|
// Keep both synchronized so a stale admin-only list from BotFather or an
|