switchroom 0.15.17 → 0.15.19
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/cli/switchroom.js +215 -24
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +245 -71
- package/telegram-plugin/gateway/effort-command.ts +272 -0
- package/telegram-plugin/gateway/gateway.ts +142 -84
- package/telegram-plugin/scoped-approval.ts +14 -11
- package/telegram-plugin/tests/effort-command.test.ts +191 -0
- package/telegram-plugin/tests/scoped-approval.test.ts +2 -2
- package/telegram-plugin/welcome-text.ts +5 -0
|
@@ -43144,6 +43144,7 @@ var TELEGRAM_MENU_COMMANDS = [
|
|
|
43144
43144
|
{ command: "logs", description: "Show recent agent logs" },
|
|
43145
43145
|
{ command: "inject", description: "Inject a Claude Code slash command (e.g. /cost)" },
|
|
43146
43146
|
{ command: "model", description: "Show or switch the Claude model" },
|
|
43147
|
+
{ command: "effort", description: "Show or switch the reasoning effort" },
|
|
43147
43148
|
{ command: "doctor", description: "Health check (deps, services, MCP)" },
|
|
43148
43149
|
{ command: "usage", description: "Pro/Max plan quota (5h + 7d windows)" },
|
|
43149
43150
|
{ command: "vault", description: "Manage vault secrets + capability grants" },
|
|
@@ -43185,6 +43186,7 @@ function switchroomHelpText(agentName3) {
|
|
|
43185
43186
|
`<code>/auth rm [agent] <slot> [--force]</code> \u2014 remove a slot`,
|
|
43186
43187
|
`<code>/model</code> \u2014 show the configured Claude model`,
|
|
43187
43188
|
`<code>/model <name></code> \u2014 switch the live session's model (opus \u00b7 sonnet \u00b7 haiku or a full id; until restart)`,
|
|
43189
|
+
`<code>/effort</code> \u2014 show or switch reasoning effort (low \u00b7 medium \u00b7 high \u00b7 xhigh \u00b7 max; until restart)`,
|
|
43188
43190
|
`<code>/topics</code> \u2014 topic-to-agent mappings`,
|
|
43189
43191
|
`<code>/permissions [agent]</code> \u2014 show agent permissions`,
|
|
43190
43192
|
`<code>/grant <tool></code> \u2014 grant a tool permission`,
|
|
@@ -44527,6 +44529,7 @@ var INJECT_COMMANDS = new Map([
|
|
|
44527
44529
|
["/hooks", { description: "List configured hooks", expectsOutput: true }],
|
|
44528
44530
|
["/memory", { description: "Open memory picker", expectsOutput: true }],
|
|
44529
44531
|
["/model", { description: "Open model picker", expectsOutput: true }],
|
|
44532
|
+
["/effort", { description: "Set reasoning effort", expectsOutput: true }],
|
|
44530
44533
|
[
|
|
44531
44534
|
"/clear",
|
|
44532
44535
|
{ description: "Clear session screen", expectsOutput: false }
|
|
@@ -44784,6 +44787,7 @@ var INJECT_COMMANDS2 = new Map([
|
|
|
44784
44787
|
["/hooks", { description: "List configured hooks", expectsOutput: true }],
|
|
44785
44788
|
["/memory", { description: "Open memory picker", expectsOutput: true }],
|
|
44786
44789
|
["/model", { description: "Open model picker", expectsOutput: true }],
|
|
44790
|
+
["/effort", { description: "Set reasoning effort", expectsOutput: true }],
|
|
44787
44791
|
[
|
|
44788
44792
|
"/clear",
|
|
44789
44793
|
{ description: "Clear session screen", expectsOutput: false }
|
|
@@ -45392,6 +45396,161 @@ function extractConfirmation(pane) {
|
|
|
45392
45396
|
return null;
|
|
45393
45397
|
}
|
|
45394
45398
|
|
|
45399
|
+
// gateway/effort-command.ts
|
|
45400
|
+
var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
45401
|
+
function isValidEffortArg(arg) {
|
|
45402
|
+
return EFFORT_LEVELS.includes(arg.toLowerCase());
|
|
45403
|
+
}
|
|
45404
|
+
function parseEffortCommand(text) {
|
|
45405
|
+
const m = text.match(/^\/effort(?:@[A-Za-z0-9_]+)?(?:\s+([\s\S]*))?$/);
|
|
45406
|
+
if (!m)
|
|
45407
|
+
return null;
|
|
45408
|
+
const rest = (m[1] ?? "").trim();
|
|
45409
|
+
if (rest.length === 0)
|
|
45410
|
+
return { kind: "show" };
|
|
45411
|
+
const parts = rest.split(/\s+/);
|
|
45412
|
+
if (parts.length > 1) {
|
|
45413
|
+
return { kind: "help", reason: "effort takes a single level" };
|
|
45414
|
+
}
|
|
45415
|
+
const arg = parts[0];
|
|
45416
|
+
if (arg.toLowerCase() === "help")
|
|
45417
|
+
return { kind: "help" };
|
|
45418
|
+
if (!isValidEffortArg(arg)) {
|
|
45419
|
+
return { kind: "help", reason: `not a valid effort level: ${arg}` };
|
|
45420
|
+
}
|
|
45421
|
+
return { kind: "set", level: arg.toLowerCase() };
|
|
45422
|
+
}
|
|
45423
|
+
var PERSIST_NOTE2 = "<i>Session-only \u2014 reverts to the configured default on restart. To change the default, set <code>thinking_effort:</code> in switchroom.yaml and restart.</i>";
|
|
45424
|
+
var LEVELS_INLINE = EFFORT_LEVELS.map((l) => `<code>${l}</code>`).join(" \u00b7 ");
|
|
45425
|
+
function helpText3(deps, reason) {
|
|
45426
|
+
const lines = [];
|
|
45427
|
+
if (reason)
|
|
45428
|
+
lines.push(`\u26a0\ufe0f ${deps.escapeHtml(reason)}`);
|
|
45429
|
+
lines.push("<b>/effort</b> \u2014 show or switch the reasoning effort (faster\u2192smarter)", "<code>/effort</code> \u2014 show the configured effort + a tap menu", `<code>/effort <level></code> \u2014 switch the live session (${LEVELS_INLINE})`, PERSIST_NOTE2);
|
|
45430
|
+
return { text: lines.join(`
|
|
45431
|
+
`), html: true };
|
|
45432
|
+
}
|
|
45433
|
+
async function handleEffortCommand(parsed, deps) {
|
|
45434
|
+
if (parsed.kind === "help")
|
|
45435
|
+
return helpText3(deps, parsed.reason);
|
|
45436
|
+
if (parsed.kind === "show") {
|
|
45437
|
+
const configured = deps.getConfiguredEffort();
|
|
45438
|
+
const shown = configured && configured.length > 0 ? configured : "low";
|
|
45439
|
+
return {
|
|
45440
|
+
text: [
|
|
45441
|
+
`<b>Effort \u2014 ${deps.escapeHtml(deps.getAgentName())}</b>`,
|
|
45442
|
+
`Configured default: <code>${deps.escapeHtml(shown)}</code>`,
|
|
45443
|
+
`Switch the live session: ${EFFORT_LEVELS.map((l) => `<code>/effort ${l}</code>`).join(" \u00b7 ")}`,
|
|
45444
|
+
PERSIST_NOTE2
|
|
45445
|
+
].join(`
|
|
45446
|
+
`),
|
|
45447
|
+
html: true
|
|
45448
|
+
};
|
|
45449
|
+
}
|
|
45450
|
+
if (!isValidEffortArg(parsed.level)) {
|
|
45451
|
+
return helpText3(deps, `not a valid effort level: ${parsed.level}`);
|
|
45452
|
+
}
|
|
45453
|
+
const verbHtml = `<code>/effort ${deps.escapeHtml(parsed.level)}</code>`;
|
|
45454
|
+
let result;
|
|
45455
|
+
try {
|
|
45456
|
+
result = await deps.inject(deps.getAgentName(), `/effort ${parsed.level}`);
|
|
45457
|
+
} catch (err) {
|
|
45458
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
45459
|
+
return { text: `\u274c ${verbHtml} \u2014 inject failed: ${deps.escapeHtml(msg)}`, html: true };
|
|
45460
|
+
}
|
|
45461
|
+
if (result.outcome === "ok") {
|
|
45462
|
+
return {
|
|
45463
|
+
text: [
|
|
45464
|
+
`${verbHtml}`,
|
|
45465
|
+
deps.preBlock(result.output),
|
|
45466
|
+
...result.truncated ? ["<i>truncated</i>"] : [],
|
|
45467
|
+
PERSIST_NOTE2
|
|
45468
|
+
].join(`
|
|
45469
|
+
`),
|
|
45470
|
+
html: true
|
|
45471
|
+
};
|
|
45472
|
+
}
|
|
45473
|
+
if (result.outcome === "ok_no_output") {
|
|
45474
|
+
return {
|
|
45475
|
+
text: [
|
|
45476
|
+
`${verbHtml} \u2014 sent, but no response captured. The agent may be mid-turn; check <code>/inject /status</code> to confirm the active effort.`,
|
|
45477
|
+
PERSIST_NOTE2
|
|
45478
|
+
].join(`
|
|
45479
|
+
`),
|
|
45480
|
+
html: true
|
|
45481
|
+
};
|
|
45482
|
+
}
|
|
45483
|
+
if (result.errorCode === "session_missing") {
|
|
45484
|
+
return {
|
|
45485
|
+
text: "\u274c tmux session not found \u2014 the agent must be running under the tmux supervisor (the default). Remove <code>experimental.legacy_pty: true</code> if set.",
|
|
45486
|
+
html: true
|
|
45487
|
+
};
|
|
45488
|
+
}
|
|
45489
|
+
return {
|
|
45490
|
+
text: `\u274c ${verbHtml} \u2014 ${deps.escapeHtml(result.errorMessage ?? "inject failed")}`,
|
|
45491
|
+
html: true
|
|
45492
|
+
};
|
|
45493
|
+
}
|
|
45494
|
+
var EFFORT_CALLBACK_PREFIX = "eff:";
|
|
45495
|
+
var EFFORT_CALLBACK_SELECT = "eff:s:";
|
|
45496
|
+
function effortSelectCallbackData(level) {
|
|
45497
|
+
return `${EFFORT_CALLBACK_SELECT}${level}`;
|
|
45498
|
+
}
|
|
45499
|
+
function menuKeyboard2(highlight) {
|
|
45500
|
+
return [
|
|
45501
|
+
EFFORT_LEVELS.map((l) => ({
|
|
45502
|
+
text: l === highlight ? `\u2705 ${l}` : l,
|
|
45503
|
+
callback_data: effortSelectCallbackData(l)
|
|
45504
|
+
}))
|
|
45505
|
+
];
|
|
45506
|
+
}
|
|
45507
|
+
function buildEffortMenu(deps, highlight) {
|
|
45508
|
+
const configured = deps.getConfiguredEffort() || "low";
|
|
45509
|
+
const live = highlight ?? configured;
|
|
45510
|
+
return {
|
|
45511
|
+
text: [
|
|
45512
|
+
`<b>Effort \u2014 ${deps.escapeHtml(deps.getAgentName())}</b>`,
|
|
45513
|
+
`Default: <code>${deps.escapeHtml(configured)}</code> \u00b7 faster \u2192 smarter: ${LEVELS_INLINE}`,
|
|
45514
|
+
"Tap to switch the live session:",
|
|
45515
|
+
PERSIST_NOTE2
|
|
45516
|
+
].join(`
|
|
45517
|
+
`),
|
|
45518
|
+
html: true,
|
|
45519
|
+
keyboard: menuKeyboard2(live)
|
|
45520
|
+
};
|
|
45521
|
+
}
|
|
45522
|
+
async function handleEffortMenuCallback(data, deps) {
|
|
45523
|
+
if (!data.startsWith(EFFORT_CALLBACK_SELECT)) {
|
|
45524
|
+
return { reply: buildEffortMenu(deps) };
|
|
45525
|
+
}
|
|
45526
|
+
const level = data.slice(EFFORT_CALLBACK_SELECT.length);
|
|
45527
|
+
if (!isValidEffortArg(level)) {
|
|
45528
|
+
return { reply: buildEffortMenu(deps) };
|
|
45529
|
+
}
|
|
45530
|
+
let banner;
|
|
45531
|
+
let selected;
|
|
45532
|
+
try {
|
|
45533
|
+
const result = await deps.inject(deps.getAgentName(), `/effort ${level}`);
|
|
45534
|
+
if (result.outcome === "ok" || result.outcome === "ok_no_output") {
|
|
45535
|
+
banner = `\u2705 Effort \u2192 <code>${deps.escapeHtml(level)}</code> for this session`;
|
|
45536
|
+
selected = level;
|
|
45537
|
+
} else if (result.errorCode === "session_missing") {
|
|
45538
|
+
banner = "\u274c tmux session not found \u2014 is the agent running under the supervisor?";
|
|
45539
|
+
} else {
|
|
45540
|
+
banner = `\u274c couldn't set effort: ${deps.escapeHtml(result.errorMessage ?? "inject failed")}`;
|
|
45541
|
+
}
|
|
45542
|
+
} catch (err) {
|
|
45543
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
45544
|
+
banner = `\u274c inject failed: ${deps.escapeHtml(msg)}`;
|
|
45545
|
+
}
|
|
45546
|
+
const menu = buildEffortMenu(deps, selected);
|
|
45547
|
+
return {
|
|
45548
|
+
reply: { ...menu, text: `${banner}
|
|
45549
|
+
${menu.text}` },
|
|
45550
|
+
selectedEffort: selected
|
|
45551
|
+
};
|
|
45552
|
+
}
|
|
45553
|
+
|
|
45395
45554
|
// ../src/config/loader.ts
|
|
45396
45555
|
init_dist();
|
|
45397
45556
|
init_zod();
|
|
@@ -54158,10 +54317,10 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
54158
54317
|
}
|
|
54159
54318
|
|
|
54160
54319
|
// ../src/build-info.ts
|
|
54161
|
-
var VERSION = "0.15.
|
|
54162
|
-
var COMMIT_SHA = "
|
|
54163
|
-
var COMMIT_DATE = "2026-06-
|
|
54164
|
-
var LATEST_PR =
|
|
54320
|
+
var VERSION = "0.15.19";
|
|
54321
|
+
var COMMIT_SHA = "3f40c6f9";
|
|
54322
|
+
var COMMIT_DATE = "2026-06-14T00:06:47Z";
|
|
54323
|
+
var LATEST_PR = 2337;
|
|
54165
54324
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
54166
54325
|
|
|
54167
54326
|
// gateway/boot-version.ts
|
|
@@ -57206,10 +57365,8 @@ if (inboundSpool != null) {
|
|
|
57206
57365
|
}
|
|
57207
57366
|
}
|
|
57208
57367
|
var pendingPermissionBuffer = createPendingPermissionBuffer();
|
|
57209
|
-
function buildPermissionActionRow(requestId, showAlways
|
|
57210
|
-
const kb = new import_grammy9.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow
|
|
57211
|
-
if (showTimeBox)
|
|
57212
|
-
kb.text("\u23F1 30 min", `perm:tmb:${requestId}`);
|
|
57368
|
+
function buildPermissionActionRow(requestId, showAlways) {
|
|
57369
|
+
const kb = new import_grammy9.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow", `perm:allow:${requestId}`);
|
|
57213
57370
|
if (showAlways)
|
|
57214
57371
|
kb.text("\uD83D\uDD01 Always\u2026", `perm:always:${requestId}`);
|
|
57215
57372
|
return kb;
|
|
@@ -57465,10 +57622,8 @@ var ipcServer = createIpcServer({
|
|
|
57465
57622
|
description,
|
|
57466
57623
|
agentName: _client.agentName
|
|
57467
57624
|
});
|
|
57468
|
-
const
|
|
57469
|
-
const
|
|
57470
|
-
const showTimeBox = scopedApprovalTtlMs() > 0 && resolveTimeBox(toolName, inputPreview, scopeChoices) != null;
|
|
57471
|
-
const keyboard = buildPermissionActionRow(requestId, showAlways, showTimeBox);
|
|
57625
|
+
const showAlways = resolveScopedAllowChoices(toolName, inputPreview) != null;
|
|
57626
|
+
const keyboard = buildPermissionActionRow(requestId, showAlways);
|
|
57472
57627
|
const activeTurn = currentTurn;
|
|
57473
57628
|
const targets = resolvePermissionCardTargets();
|
|
57474
57629
|
for (const { chatId, threadId } of targets) {
|
|
@@ -62147,6 +62302,43 @@ bot.command("model", async (ctx) => {
|
|
|
62147
62302
|
const reply = await handleModelCommand(parsed, deps);
|
|
62148
62303
|
await switchroomReply(ctx, reply.text, { html: reply.html });
|
|
62149
62304
|
});
|
|
62305
|
+
function buildEffortDeps() {
|
|
62306
|
+
return {
|
|
62307
|
+
inject: injectSlashCommand,
|
|
62308
|
+
getAgentName: getMyAgentName,
|
|
62309
|
+
getConfiguredEffort: () => {
|
|
62310
|
+
const data = switchroomExecJson(["agent", "list"]);
|
|
62311
|
+
return data?.agents?.find((a) => a.name === getMyAgentName())?.thinking_effort ?? null;
|
|
62312
|
+
},
|
|
62313
|
+
escapeHtml: escapeHtmlForTg,
|
|
62314
|
+
preBlock
|
|
62315
|
+
};
|
|
62316
|
+
}
|
|
62317
|
+
function effortMenuReplyMarkup(reply) {
|
|
62318
|
+
if (!reply.keyboard)
|
|
62319
|
+
return;
|
|
62320
|
+
const kb = new import_grammy9.InlineKeyboard;
|
|
62321
|
+
for (const row of reply.keyboard) {
|
|
62322
|
+
for (const btn of row)
|
|
62323
|
+
kb.text(btn.text, btn.callback_data);
|
|
62324
|
+
kb.row();
|
|
62325
|
+
}
|
|
62326
|
+
return kb;
|
|
62327
|
+
}
|
|
62328
|
+
bot.command("effort", async (ctx) => {
|
|
62329
|
+
if (!isAuthorizedSender(ctx))
|
|
62330
|
+
return;
|
|
62331
|
+
const text = ctx.message?.text ?? ctx.channelPost?.text ?? "";
|
|
62332
|
+
const parsed = parseEffortCommand(text) ?? { kind: "show" };
|
|
62333
|
+
const deps = buildEffortDeps();
|
|
62334
|
+
if (parsed.kind === "show") {
|
|
62335
|
+
const menu = buildEffortMenu(deps);
|
|
62336
|
+
await switchroomReply(ctx, menu.text, { html: true, reply_markup: effortMenuReplyMarkup(menu) });
|
|
62337
|
+
return;
|
|
62338
|
+
}
|
|
62339
|
+
const reply = await handleEffortCommand(parsed, deps);
|
|
62340
|
+
await switchroomReply(ctx, reply.text, { html: reply.html });
|
|
62341
|
+
});
|
|
62150
62342
|
bot.command("agentstart", async (ctx) => {
|
|
62151
62343
|
if (!isAuthorizedSender(ctx))
|
|
62152
62344
|
return;
|
|
@@ -64874,6 +65066,26 @@ bot.on("callback_query:data", async (ctx) => {
|
|
|
64874
65066
|
await handleAuthDashboardCallback(ctx);
|
|
64875
65067
|
return;
|
|
64876
65068
|
}
|
|
65069
|
+
if (data.startsWith(EFFORT_CALLBACK_PREFIX)) {
|
|
65070
|
+
const access2 = loadAccess();
|
|
65071
|
+
const senderId2 = String(ctx.from?.id ?? "");
|
|
65072
|
+
if (!access2.allowFrom.includes(senderId2)) {
|
|
65073
|
+
await ctx.answerCallbackQuery({ text: "Not authorized." });
|
|
65074
|
+
return;
|
|
65075
|
+
}
|
|
65076
|
+
await ctx.answerCallbackQuery({ text: "Setting effort\u2026" }).catch(() => {});
|
|
65077
|
+
try {
|
|
65078
|
+
const outcome = await handleEffortMenuCallback(data, buildEffortDeps());
|
|
65079
|
+
await ctx.editMessageText(outcome.reply.text, {
|
|
65080
|
+
parse_mode: "HTML",
|
|
65081
|
+
reply_markup: effortMenuReplyMarkup(outcome.reply) ?? { inline_keyboard: [] }
|
|
65082
|
+
}).catch(() => {});
|
|
65083
|
+
} catch (err) {
|
|
65084
|
+
process.stderr.write(`telegram gateway: effort-menu callback failed: ${err?.message ?? String(err)}
|
|
65085
|
+
`);
|
|
65086
|
+
}
|
|
65087
|
+
return;
|
|
65088
|
+
}
|
|
64877
65089
|
if (data.startsWith(MODEL_CALLBACK_PREFIX)) {
|
|
64878
65090
|
const access2 = loadAccess();
|
|
64879
65091
|
const senderId2 = String(ctx.from?.id ?? "");
|
|
@@ -65080,8 +65292,8 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
65080
65292
|
const cbMessageId = ctx.callbackQuery?.message?.message_id;
|
|
65081
65293
|
const metaForMessage = cbMessageId != null ? agentButtonMeta.get(`${cbChatId}:${cbMessageId}`) : undefined;
|
|
65082
65294
|
const tapMeta = metaForMessage?.get(agentCb.raw);
|
|
65083
|
-
const
|
|
65084
|
-
await ctx.answerCallbackQuery({ text:
|
|
65295
|
+
const ackText2 = typeof tapMeta?.ack_text === "string" && tapMeta.ack_text.length > 0 ? tapMeta.ack_text : "\u2713 received";
|
|
65296
|
+
await ctx.answerCallbackQuery({ text: ackText2 }).catch(() => {});
|
|
65085
65297
|
const buttonText = (() => {
|
|
65086
65298
|
const msg2 = ctx.callbackQuery?.message;
|
|
65087
65299
|
const kb = msg2 && "reply_markup" in msg2 ? msg2.reply_markup : undefined;
|
|
@@ -65150,7 +65362,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
65150
65362
|
}
|
|
65151
65363
|
return;
|
|
65152
65364
|
}
|
|
65153
|
-
const m = /^perm:(allow|deny|always|asn|asb|back
|
|
65365
|
+
const m = /^perm:(allow|deny|always|asn|asb|back):([a-km-z]{5})$/.exec(data);
|
|
65154
65366
|
if (!m) {
|
|
65155
65367
|
await ctx.answerCallbackQuery().catch(() => {});
|
|
65156
65368
|
return;
|
|
@@ -65170,9 +65382,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
65170
65382
|
}
|
|
65171
65383
|
let keyboard;
|
|
65172
65384
|
if (behavior === "back") {
|
|
65173
|
-
|
|
65174
|
-
const backTimeBox = scopedApprovalTtlMs() > 0 && resolveTimeBox(details.tool_name, details.input_preview, backChoices) != null;
|
|
65175
|
-
keyboard = buildPermissionActionRow(request_id, true, backTimeBox);
|
|
65385
|
+
keyboard = buildPermissionActionRow(request_id, true);
|
|
65176
65386
|
} else {
|
|
65177
65387
|
const choices = resolveScopedAllowChoices(details.tool_name, details.input_preview);
|
|
65178
65388
|
if (choices == null) {
|
|
@@ -65305,12 +65515,12 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
65305
65515
|
}
|
|
65306
65516
|
const ok = durable;
|
|
65307
65517
|
const legacyNote = legacy && durable;
|
|
65308
|
-
const
|
|
65518
|
+
const ackText2 = ok ? legacyNote ? `\u2705 Saved. ${agentName3} can now ${grantPhrase} without asking (legacy path).` : `\u2705 Saved. ${agentName3} can now ${grantPhrase} without asking.` : editLockHint ? `\u26A0\uFE0F Allowed for now \u2014 config edits are locked. Enable hostd.config_edit_enabled.` : `\u26A0\uFE0F Allowed for now, but "always" did NOT save \u2014 it will ask again after restart. Check gateway log.`;
|
|
65309
65519
|
const sourceMsg = ctx.callbackQuery?.message;
|
|
65310
65520
|
const baseText2 = sourceMsg && "text" in sourceMsg && sourceMsg.text ? escapeHtmlForTg(sourceMsg.text) : "";
|
|
65311
65521
|
const editLabel = ok ? legacyNote ? `\u2705 <b>${escapeHtmlForTg(agentName3)} can now ${escapeHtmlForTg(grantPhrase)}</b> without asking (legacy path); restart agent for full effect` : `\u2705 <b>${escapeHtmlForTg(agentName3)} can now ${escapeHtmlForTg(grantPhrase)}</b> without asking; restart agent for full effect` : editLockHint ? `\u26A0\uFE0F <b>Allowed for now \u2014 "always" did NOT save.</b> Config edits are locked; enable <code>hostd.config_edit_enabled</code>.` : `\u26A0\uFE0F <b>Allowed for now \u2014 "always" did NOT save.</b> It will ask again after restart. Check gateway log.`;
|
|
65312
65522
|
await finalizeCallback(ctx, {
|
|
65313
|
-
ackText:
|
|
65523
|
+
ackText: ackText2.slice(0, 200),
|
|
65314
65524
|
newText: baseText2 ? `${baseText2}
|
|
65315
65525
|
|
|
65316
65526
|
${editLabel}` : editLabel,
|
|
@@ -65318,64 +65528,28 @@ ${editLabel}` : editLabel,
|
|
|
65318
65528
|
});
|
|
65319
65529
|
return;
|
|
65320
65530
|
}
|
|
65321
|
-
|
|
65322
|
-
|
|
65323
|
-
|
|
65324
|
-
|
|
65325
|
-
|
|
65326
|
-
|
|
65327
|
-
|
|
65328
|
-
|
|
65329
|
-
|
|
65330
|
-
return;
|
|
65331
|
-
}
|
|
65332
|
-
const choices = resolveScopedAllowChoices(details.tool_name, details.input_preview);
|
|
65333
|
-
const tb = resolveTimeBox(details.tool_name, details.input_preview, choices);
|
|
65334
|
-
if (!tb) {
|
|
65335
|
-
await ctx.answerCallbackQuery({ text: "This action can't be time-boxed." }).catch(() => {});
|
|
65336
|
-
return;
|
|
65337
|
-
}
|
|
65338
|
-
const agentName3 = selfAgentName();
|
|
65339
|
-
if (!agentName3) {
|
|
65340
|
-
await ctx.answerCallbackQuery({ text: "Time-box needs SWITCHROOM_AGENT_NAME \u2014 gateway is misconfigured." }).catch(() => {});
|
|
65341
|
-
return;
|
|
65342
|
-
}
|
|
65343
|
-
pendingPermissions.delete(request_id);
|
|
65344
|
-
dispatchPermissionVerdict({ type: "permission", requestId: request_id, behavior: "allow" });
|
|
65345
|
-
recordScopedGrant(scopedGrants, agentName3, tb.rule, Date.now(), ttl);
|
|
65346
|
-
resumeReactionAfterVerdict();
|
|
65347
|
-
postPermissionResumeMessage({
|
|
65348
|
-
behavior: "allow",
|
|
65349
|
-
action: naturalAction(details.tool_name, details.input_preview)
|
|
65350
|
-
});
|
|
65351
|
-
process.stderr.write(`telegram gateway: scoped-approval granted rule="${tb.rule}" agent=${agentName3} ttl_ms=${ttl} (request_id=${request_id})
|
|
65531
|
+
const pd = pendingPermissions.get(request_id);
|
|
65532
|
+
const resumeAction = pd ? naturalAction(pd.tool_name, pd.input_preview) : "";
|
|
65533
|
+
const scopedTtl = scopedApprovalTtlMs();
|
|
65534
|
+
const timeBox = behavior === "allow" && scopedTtl > 0 && pd ? resolveTimeBox(pd.tool_name, pd.input_preview, resolveScopedAllowChoices(pd.tool_name, pd.input_preview)) : null;
|
|
65535
|
+
const grantAgent = selfAgentName();
|
|
65536
|
+
pendingPermissions.delete(request_id);
|
|
65537
|
+
if (timeBox && grantAgent) {
|
|
65538
|
+
recordScopedGrant(scopedGrants, grantAgent, timeBox.rule, Date.now(), scopedTtl);
|
|
65539
|
+
process.stderr.write(`telegram gateway: scoped-approval granted via Allow rule="${timeBox.rule}" agent=${grantAgent} ttl_ms=${scopedTtl} (request_id=${request_id})
|
|
65352
65540
|
`);
|
|
65353
|
-
const mins = Math.max(1, Math.round(ttl / 60000));
|
|
65354
|
-
const sourceMsg = ctx.callbackQuery?.message;
|
|
65355
|
-
const baseText2 = sourceMsg && "text" in sourceMsg && sourceMsg.text ? escapeHtmlForTg(sourceMsg.text) : "";
|
|
65356
|
-
const editLabel = `\u23F1 <b>Allowed for ${mins} min \u2014 ${escapeHtmlForTg(tb.breadth)}</b> \xB7 re-asks after that, and now for anything else`;
|
|
65357
|
-
await finalizeCallback(ctx, {
|
|
65358
|
-
ackText: `\u23F1 Allowed for ${mins} min`.slice(0, 200),
|
|
65359
|
-
newText: baseText2 ? `${baseText2}
|
|
65360
|
-
|
|
65361
|
-
${editLabel}` : editLabel,
|
|
65362
|
-
parseMode: "HTML"
|
|
65363
|
-
});
|
|
65364
|
-
return;
|
|
65365
65541
|
}
|
|
65366
|
-
const
|
|
65367
|
-
|
|
65368
|
-
|
|
65369
|
-
|
|
65370
|
-
pendingPermissions.delete(request_id);
|
|
65371
|
-
const label = behavior === "allow" ? "\u2705 Allowed" : "\u274C Denied";
|
|
65542
|
+
const scopedMins = Math.max(1, Math.round(scopedTtl / 60000));
|
|
65543
|
+
const windowGranted = timeBox != null && grantAgent !== "";
|
|
65544
|
+
const ackText = behavior === "deny" ? "\u274C Denied" : windowGranted ? `\u2705 Allowed (${scopedMins} min)` : "\u2705 Allowed once";
|
|
65545
|
+
const htmlLabel = behavior === "deny" ? "\u274C <b>Denied</b>" : windowGranted ? `\u2705 <b>Allowed \u2014 won't ask again about ${escapeHtmlForTg(timeBox.breadth)} for ${scopedMins} min</b>` : "\u2705 <b>Allowed once</b>";
|
|
65372
65546
|
const msg = ctx.callbackQuery?.message;
|
|
65373
65547
|
const baseText = msg && "text" in msg && msg.text ? escapeHtmlForTg(msg.text) : "";
|
|
65374
65548
|
await finalizeCallback(ctx, {
|
|
65375
|
-
ackText:
|
|
65549
|
+
ackText: ackText.slice(0, 200),
|
|
65376
65550
|
newText: baseText ? `${baseText}
|
|
65377
65551
|
|
|
65378
|
-
${
|
|
65552
|
+
${htmlLabel}` : htmlLabel,
|
|
65379
65553
|
parseMode: "HTML",
|
|
65380
65554
|
synthInbound: () => {
|
|
65381
65555
|
dispatchPermissionVerdict({
|