@songsid/agend 2.1.4-beta.30 → 2.1.4-beta.32
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/backend/claude-code.d.ts +10 -0
- package/dist/backend/claude-code.js +56 -3
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.js +1 -1
- 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 +1 -0
- package/dist/backend/kiro.js +10 -3
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/opencode.js +2 -2
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/types.d.ts +14 -0
- package/dist/backend/types.js +5 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +2 -0
- package/dist/channel/adapters/discord.js +6 -0
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +6 -0
- package/dist/channel/adapters/telegram.js +10 -0
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/types.d.ts +2 -0
- package/dist/daemon.d.ts +11 -0
- package/dist/daemon.js +130 -28
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.d.ts +4 -0
- package/dist/fleet-manager.js +100 -7
- package/dist/fleet-manager.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +17 -0
- package/dist/instance-lifecycle.js +71 -0
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +6 -0
- package/dist/locale.js.map +1 -1
- package/dist/topic-commands.js +5 -1
- package/dist/topic-commands.js.map +1 -1
- package/dist/usage/providers.d.ts +7 -0
- package/dist/usage/providers.js +18 -1
- package/dist/usage/providers.js.map +1 -1
- package/package.json +1 -1
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -1021,6 +1021,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
1021
1021
|
* Returns null on success (menu shown), or a fallback string to send.
|
|
1022
1022
|
*/
|
|
1023
1023
|
promptModelMenu(instanceName: string, userId: string, channelId: string, adapter: ChannelAdapter, chatId: string, threadId?: string): Promise<string | null>;
|
|
1024
|
+
/** Cached-or-live account catalog behind "/model → more models" (claude). */
|
|
1025
|
+
private claudeApiModelOptions;
|
|
1026
|
+
/** Replace a consumed "/model" menu with the full account catalog tier. */
|
|
1027
|
+
private expandClaudeModelMenu;
|
|
1024
1028
|
/** Consume a `/model` selection callback. Returns true for all model-select ids (incl. stale). */
|
|
1025
1029
|
private handleModelSelection;
|
|
1026
1030
|
/** Apply a model to an instance: runtime paste (claude-code) or persist + restart (others). */
|
package/dist/fleet-manager.js
CHANGED
|
@@ -5109,7 +5109,7 @@ export class FleetManager {
|
|
|
5109
5109
|
* longer parses) collapses the clicked message so the dead button stops
|
|
5110
5110
|
* inviting clicks — the same courtesy the cancel button extends.
|
|
5111
5111
|
*/
|
|
5112
|
-
consumeNonceCallback(prefix, actionRe, data, callbackAdapterId, receivingAdapter) {
|
|
5112
|
+
consumeNonceCallback(prefix, actionRe, data, callbackAdapterId, receivingAdapter, staleHandling) {
|
|
5113
5113
|
if (!data.callbackData.startsWith(prefix))
|
|
5114
5114
|
return null;
|
|
5115
5115
|
const match = data.callbackData.match(actionRe);
|
|
@@ -5121,7 +5121,17 @@ export class FleetManager {
|
|
|
5121
5121
|
pending = undefined;
|
|
5122
5122
|
if (!match || !pending) {
|
|
5123
5123
|
const adapter = receivingAdapter ?? this.adapter;
|
|
5124
|
-
|
|
5124
|
+
if (staleHandling?.keepText && adapter?.removeMessageButtons) {
|
|
5125
|
+
adapter.removeMessageButtons(data.chatId, data.messageId, data.threadId)
|
|
5126
|
+
.catch(() => { });
|
|
5127
|
+
}
|
|
5128
|
+
else {
|
|
5129
|
+
adapter?.editMessageRemoveButtons?.(data.chatId, data.messageId, t("buttons.stale"), data.threadId).catch(() => { });
|
|
5130
|
+
}
|
|
5131
|
+
if (staleHandling?.notice) {
|
|
5132
|
+
adapter?.sendText(data.chatId, staleHandling.notice, { threadId: data.threadId })
|
|
5133
|
+
.catch(() => { });
|
|
5134
|
+
}
|
|
5125
5135
|
return "consumed";
|
|
5126
5136
|
}
|
|
5127
5137
|
// Bind the capability to the exact message/world that created it. Telegram
|
|
@@ -5294,7 +5304,12 @@ export class FleetManager {
|
|
|
5294
5304
|
}
|
|
5295
5305
|
}
|
|
5296
5306
|
async handleTipDismiss(data, callbackAdapterId, receivingAdapter) {
|
|
5297
|
-
const claimed = this.consumeNonceCallback(TIP_DISMISS_CALLBACK_PREFIX, /^tip-dismiss:([0-9a-f]+):(dismiss|confused)$/, data, callbackAdapterId, receivingAdapter
|
|
5307
|
+
const claimed = this.consumeNonceCallback(TIP_DISMISS_CALLBACK_PREFIX, /^tip-dismiss:([0-9a-f]+):(dismiss|confused)$/, data, callbackAdapterId, receivingAdapter,
|
|
5308
|
+
// A restart orphans the expiry timer, so tip buttons can outlive their
|
|
5309
|
+
// nonce by days. Keep the tip text readable and always tell the clicker
|
|
5310
|
+
// what to do next (the keyboard edit itself can be refused: Telegram
|
|
5311
|
+
// rejects edits on messages older than 48h).
|
|
5312
|
+
{ keepText: true, notice: t("tips.expired_use_tips") });
|
|
5298
5313
|
if (claimed === null)
|
|
5299
5314
|
return false;
|
|
5300
5315
|
if (claimed === "consumed")
|
|
@@ -5363,7 +5378,7 @@ export class FleetManager {
|
|
|
5363
5378
|
return true;
|
|
5364
5379
|
}
|
|
5365
5380
|
async handleTipUnlock(data, callbackAdapterId, receivingAdapter) {
|
|
5366
|
-
const claimed = this.consumeNonceCallback(TIP_UNLOCK_CALLBACK_PREFIX, /^tip-unlock:([0-9a-f]+):(unlock)$/, data, callbackAdapterId, receivingAdapter);
|
|
5381
|
+
const claimed = this.consumeNonceCallback(TIP_UNLOCK_CALLBACK_PREFIX, /^tip-unlock:([0-9a-f]+):(unlock)$/, data, callbackAdapterId, receivingAdapter, { keepText: true, notice: t("tips.expired_use_tips") });
|
|
5367
5382
|
if (claimed === null)
|
|
5368
5383
|
return false;
|
|
5369
5384
|
if (claimed === "consumed")
|
|
@@ -7504,6 +7519,13 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
7504
7519
|
if (previous?.models?.length)
|
|
7505
7520
|
env.models = previous.models;
|
|
7506
7521
|
}
|
|
7522
|
+
// Same protection for the extended catalog: one offline moment must not
|
|
7523
|
+
// blank a good account list for the whole cache TTL.
|
|
7524
|
+
if (!env.apiModels?.length) {
|
|
7525
|
+
const previous = this.readCliEnv(backend);
|
|
7526
|
+
if (previous?.apiModels?.length)
|
|
7527
|
+
env.apiModels = previous.apiModels;
|
|
7528
|
+
}
|
|
7507
7529
|
const path = this.cliEnvPath(backend);
|
|
7508
7530
|
mkdirSync(dirname(path), { recursive: true });
|
|
7509
7531
|
writeFileSync(path, JSON.stringify(env, null, 2));
|
|
@@ -7839,13 +7861,17 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
7839
7861
|
// Raw id for ✓-matching options; display resolves an inherited CLI default.
|
|
7840
7862
|
const { model: currentModel, display: currentDisplay } = this.resolveInstanceModel(name);
|
|
7841
7863
|
const nonce = randomBytes(6).toString("hex");
|
|
7842
|
-
const
|
|
7864
|
+
const isClaude = this.backendNameForInstance(name) === "claude-code";
|
|
7865
|
+
const choices = options.slice(0, isClaude ? 24 : 25).map(o => ({
|
|
7843
7866
|
id: `${MODEL_SELECT_CALLBACK_PREFIX}${nonce}:${o.id}`,
|
|
7844
7867
|
label: this.modelChoiceLabel(o, currentModel),
|
|
7845
7868
|
}));
|
|
7869
|
+
if (isClaude) {
|
|
7870
|
+
choices.push({ id: `${MODEL_SELECT_CALLBACK_PREFIX}${nonce}:__more__`, label: t("model.more") });
|
|
7871
|
+
}
|
|
7846
7872
|
const timer = setTimeout(() => this.pendingModelSelects.delete(nonce), CLASSIC_BACKEND_SELECTION_TIMEOUT_MS);
|
|
7847
7873
|
timer.unref?.();
|
|
7848
|
-
this.pendingModelSelects.set(nonce, { instanceName: name, model: "", userId: data.userId, channelId: data.channelId, timer, respond: data.respond });
|
|
7874
|
+
this.pendingModelSelects.set(nonce, { instanceName: name, model: "", userId: data.userId, channelId: data.channelId, timer, respond: data.respond, respondChoices: data.respondChoices });
|
|
7849
7875
|
try {
|
|
7850
7876
|
await data.respondChoices(t("model.menu", `**${currentDisplay}**`), choices);
|
|
7851
7877
|
}
|
|
@@ -7868,10 +7894,15 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
7868
7894
|
}
|
|
7869
7895
|
const { model: currentModel, display: currentDisplay } = this.resolveInstanceModel(instanceName);
|
|
7870
7896
|
const nonce = randomBytes(6).toString("hex");
|
|
7871
|
-
const
|
|
7897
|
+
const isClaude = this.backendNameForInstance(instanceName) === "claude-code";
|
|
7898
|
+
// Keep the more-models entry inside Discord's 25-option select cap.
|
|
7899
|
+
const choices = options.slice(0, isClaude ? 24 : 25).map(o => ({
|
|
7872
7900
|
id: `${MODEL_SELECT_CALLBACK_PREFIX}${nonce}:${o.id}`,
|
|
7873
7901
|
label: this.modelChoiceLabel(o, currentModel),
|
|
7874
7902
|
}));
|
|
7903
|
+
if (isClaude) {
|
|
7904
|
+
choices.push({ id: `${MODEL_SELECT_CALLBACK_PREFIX}${nonce}:__more__`, label: t("model.more") });
|
|
7905
|
+
}
|
|
7875
7906
|
const respond = async (text) => {
|
|
7876
7907
|
await adapter.sendText(chatId, text, { threadId });
|
|
7877
7908
|
return undefined;
|
|
@@ -7899,6 +7930,62 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
7899
7930
|
return t("model.usage");
|
|
7900
7931
|
}
|
|
7901
7932
|
}
|
|
7933
|
+
/** Cached-or-live account catalog behind "/model → more models" (claude). */
|
|
7934
|
+
async claudeApiModelOptions() {
|
|
7935
|
+
const cached = this.readCliEnv("claude-code");
|
|
7936
|
+
if (cached?.apiModels?.length)
|
|
7937
|
+
return cached.apiModels;
|
|
7938
|
+
const env = await this.probeBackend("claude-code");
|
|
7939
|
+
return env?.apiModels ?? [];
|
|
7940
|
+
}
|
|
7941
|
+
/** Replace a consumed "/model" menu with the full account catalog tier. */
|
|
7942
|
+
async expandClaudeModelMenu(pending) {
|
|
7943
|
+
const expanded = await this.claudeApiModelOptions();
|
|
7944
|
+
if (!expanded.length) {
|
|
7945
|
+
await pending.respond(t("model.more_unavailable")).catch(() => { });
|
|
7946
|
+
return;
|
|
7947
|
+
}
|
|
7948
|
+
const { model: currentModel, display: currentDisplay } = this.resolveInstanceModel(pending.instanceName);
|
|
7949
|
+
const nonce = randomBytes(6).toString("hex");
|
|
7950
|
+
const choices = expanded.slice(0, 25).map(o => ({
|
|
7951
|
+
id: `${MODEL_SELECT_CALLBACK_PREFIX}${nonce}:${o.id}`,
|
|
7952
|
+
label: this.modelChoiceLabel(o, currentModel),
|
|
7953
|
+
}));
|
|
7954
|
+
const timer = setTimeout(() => {
|
|
7955
|
+
const p = this.pendingModelSelects.get(nonce);
|
|
7956
|
+
if (p) {
|
|
7957
|
+
this.pendingModelSelects.delete(nonce);
|
|
7958
|
+
p.respond(t("model.selection_expired")).catch(() => { });
|
|
7959
|
+
}
|
|
7960
|
+
}, CLASSIC_BACKEND_SELECTION_TIMEOUT_MS);
|
|
7961
|
+
timer.unref?.();
|
|
7962
|
+
this.pendingModelSelects.set(nonce, { ...pending, model: "", timer });
|
|
7963
|
+
try {
|
|
7964
|
+
if (pending.respondChoices) {
|
|
7965
|
+
// Discord select menu: edit the same interaction reply in place.
|
|
7966
|
+
await pending.respondChoices(t("model.menu", `**${currentDisplay}**`), choices);
|
|
7967
|
+
return;
|
|
7968
|
+
}
|
|
7969
|
+
if (pending.adapter && pending.adapterChatId) {
|
|
7970
|
+
// Telegram: retire the tier-1 keyboard, then post the expanded menu.
|
|
7971
|
+
if (pending.menuMessageId && pending.adapter.editMessageRemoveButtons) {
|
|
7972
|
+
await pending.adapter.editMessageRemoveButtons(pending.adapterChatId, pending.menuMessageId, t("model.more"), pending.adapterThreadId).catch(() => { });
|
|
7973
|
+
}
|
|
7974
|
+
const menuMessageId = await pending.adapter.promptUser(pending.adapterChatId, t("model.menu", currentDisplay), choices, { threadId: pending.adapterThreadId });
|
|
7975
|
+
const fresh = this.pendingModelSelects.get(nonce);
|
|
7976
|
+
if (fresh)
|
|
7977
|
+
fresh.menuMessageId = menuMessageId;
|
|
7978
|
+
return;
|
|
7979
|
+
}
|
|
7980
|
+
await pending.respond(t("model.more_unavailable")).catch(() => { });
|
|
7981
|
+
}
|
|
7982
|
+
catch (err) {
|
|
7983
|
+
this.pendingModelSelects.delete(nonce);
|
|
7984
|
+
clearTimeout(timer);
|
|
7985
|
+
this.logger.warn({ err, instanceName: pending.instanceName }, "Expanded model menu failed");
|
|
7986
|
+
await pending.respond(t("model.more_unavailable")).catch(() => { });
|
|
7987
|
+
}
|
|
7988
|
+
}
|
|
7902
7989
|
/** Consume a `/model` selection callback. Returns true for all model-select ids (incl. stale). */
|
|
7903
7990
|
async handleModelSelection(data) {
|
|
7904
7991
|
if (!data.callbackData.startsWith(MODEL_SELECT_CALLBACK_PREFIX))
|
|
@@ -7918,6 +8005,12 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
7918
8005
|
this.pendingModelSelects.delete(match[1]);
|
|
7919
8006
|
clearTimeout(pending.timer);
|
|
7920
8007
|
const model = match[2];
|
|
8008
|
+
// "More models…" is a navigation choice, not a model: swap the menu for the
|
|
8009
|
+
// full account catalog (live /v1/models with [1m] variants).
|
|
8010
|
+
if (model === "__more__") {
|
|
8011
|
+
await this.expandClaudeModelMenu(pending);
|
|
8012
|
+
return true;
|
|
8013
|
+
}
|
|
7921
8014
|
// Send immediate "⏳ Switching..." feedback, then apply in background.
|
|
7922
8015
|
const progressText = t("model.switching", pending.instanceName, model);
|
|
7923
8016
|
let progressMsgId;
|