@integrity-labs/agt-cli 0.27.15 → 0.27.17
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/bin/agt.js +3 -3
- package/dist/{chunk-LJEV2QHN.js → chunk-QQVNHJ76.js} +30 -40
- package/dist/chunk-QQVNHJ76.js.map +1 -0
- package/dist/lib/manager-worker.js +9 -5
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +1 -462
- package/dist/mcp/slack-channel.js +30 -518
- package/dist/mcp/telegram-channel.js +14 -474
- package/package.json +1 -1
- package/dist/chunk-LJEV2QHN.js.map +0 -1
|
@@ -14293,7 +14293,7 @@ var StdioServerTransport = class {
|
|
|
14293
14293
|
|
|
14294
14294
|
// src/telegram-channel.ts
|
|
14295
14295
|
import https from "https";
|
|
14296
|
-
import { createHash, randomUUID
|
|
14296
|
+
import { createHash, randomUUID } from "crypto";
|
|
14297
14297
|
import {
|
|
14298
14298
|
createWriteStream,
|
|
14299
14299
|
existsSync as existsSync2,
|
|
@@ -15386,430 +15386,6 @@ function createInboundContextClient(args) {
|
|
|
15386
15386
|
};
|
|
15387
15387
|
}
|
|
15388
15388
|
|
|
15389
|
-
// src/progress-tools.ts
|
|
15390
|
-
import { randomUUID } from "crypto";
|
|
15391
|
-
|
|
15392
|
-
// src/progress-handle.ts
|
|
15393
|
-
async function startProgressHandle(opts) {
|
|
15394
|
-
const now = opts.now ?? (() => Date.now());
|
|
15395
|
-
const setTimer = opts.setTimer ?? ((cb, ms) => setTimeout(cb, ms));
|
|
15396
|
-
const clearTimer = opts.clearTimer ?? ((id) => clearTimeout(id));
|
|
15397
|
-
const debounceMs = opts.debounceMs ?? 1e3;
|
|
15398
|
-
const onPostTerminalUpdate = opts.onPostTerminalUpdate ?? defaultPostTerminalWarn;
|
|
15399
|
-
const state = {
|
|
15400
|
-
initialLabel: opts.initialLabel,
|
|
15401
|
-
mode: opts.initialMode ?? "working",
|
|
15402
|
-
lastChangeAt: now()
|
|
15403
|
-
};
|
|
15404
|
-
let lastFlushAt = 0;
|
|
15405
|
-
let pendingTimer = null;
|
|
15406
|
-
let inFlight = null;
|
|
15407
|
-
let terminal = false;
|
|
15408
|
-
async function doFlush() {
|
|
15409
|
-
while (inFlight) await inFlight;
|
|
15410
|
-
if (pendingTimer != null) {
|
|
15411
|
-
clearTimer(pendingTimer);
|
|
15412
|
-
pendingTimer = null;
|
|
15413
|
-
}
|
|
15414
|
-
lastFlushAt = now();
|
|
15415
|
-
const promise = opts.flush(snapshotState());
|
|
15416
|
-
inFlight = promise.then(
|
|
15417
|
-
() => {
|
|
15418
|
-
inFlight = null;
|
|
15419
|
-
},
|
|
15420
|
-
(err) => {
|
|
15421
|
-
inFlight = null;
|
|
15422
|
-
throw err;
|
|
15423
|
-
}
|
|
15424
|
-
);
|
|
15425
|
-
await inFlight;
|
|
15426
|
-
}
|
|
15427
|
-
function snapshotState() {
|
|
15428
|
-
return {
|
|
15429
|
-
initialLabel: state.initialLabel,
|
|
15430
|
-
mode: state.mode,
|
|
15431
|
-
stepLabel: state.stepLabel,
|
|
15432
|
-
stepNumber: state.stepNumber ? { ...state.stepNumber } : void 0,
|
|
15433
|
-
detail: state.detail,
|
|
15434
|
-
lastChangeAt: state.lastChangeAt,
|
|
15435
|
-
terminal: state.terminal ? { ...state.terminal } : void 0
|
|
15436
|
-
};
|
|
15437
|
-
}
|
|
15438
|
-
function applyUpdate(next) {
|
|
15439
|
-
if (next.mode !== void 0) state.mode = next.mode;
|
|
15440
|
-
if (next.stepLabel !== void 0) state.stepLabel = next.stepLabel;
|
|
15441
|
-
if (next.stepNumber !== void 0) state.stepNumber = { ...next.stepNumber };
|
|
15442
|
-
if (next.detail !== void 0) state.detail = next.detail;
|
|
15443
|
-
state.lastChangeAt = now();
|
|
15444
|
-
}
|
|
15445
|
-
async function scheduleOrFlush() {
|
|
15446
|
-
const elapsed = now() - lastFlushAt;
|
|
15447
|
-
if (elapsed >= debounceMs) {
|
|
15448
|
-
await doFlush();
|
|
15449
|
-
return;
|
|
15450
|
-
}
|
|
15451
|
-
if (pendingTimer != null) return;
|
|
15452
|
-
const remaining = debounceMs - elapsed;
|
|
15453
|
-
pendingTimer = setTimer(() => {
|
|
15454
|
-
pendingTimer = null;
|
|
15455
|
-
void doFlush().catch(() => {
|
|
15456
|
-
});
|
|
15457
|
-
}, remaining);
|
|
15458
|
-
}
|
|
15459
|
-
lastFlushAt = now();
|
|
15460
|
-
await opts.flush(snapshotState());
|
|
15461
|
-
return {
|
|
15462
|
-
snapshot: snapshotState,
|
|
15463
|
-
async update(next) {
|
|
15464
|
-
if (terminal) {
|
|
15465
|
-
const peeked = snapshotState();
|
|
15466
|
-
Object.assign(peeked, {
|
|
15467
|
-
mode: next.mode ?? peeked.mode,
|
|
15468
|
-
stepLabel: next.stepLabel ?? peeked.stepLabel,
|
|
15469
|
-
stepNumber: next.stepNumber ?? peeked.stepNumber,
|
|
15470
|
-
detail: next.detail ?? peeked.detail
|
|
15471
|
-
});
|
|
15472
|
-
try {
|
|
15473
|
-
onPostTerminalUpdate(peeked);
|
|
15474
|
-
} catch (err) {
|
|
15475
|
-
console.warn(
|
|
15476
|
-
"[progress-handle] onPostTerminalUpdate threw \u2014 swallowed to keep update() non-throwing:",
|
|
15477
|
-
err
|
|
15478
|
-
);
|
|
15479
|
-
}
|
|
15480
|
-
return;
|
|
15481
|
-
}
|
|
15482
|
-
applyUpdate(next);
|
|
15483
|
-
await scheduleOrFlush();
|
|
15484
|
-
},
|
|
15485
|
-
async complete(summary) {
|
|
15486
|
-
if (terminal) return;
|
|
15487
|
-
terminal = true;
|
|
15488
|
-
state.terminal = { kind: "completed", message: summary };
|
|
15489
|
-
state.lastChangeAt = now();
|
|
15490
|
-
await doFlush();
|
|
15491
|
-
},
|
|
15492
|
-
async fail(reason) {
|
|
15493
|
-
if (terminal) return;
|
|
15494
|
-
terminal = true;
|
|
15495
|
-
state.terminal = { kind: "failed", message: reason };
|
|
15496
|
-
state.lastChangeAt = now();
|
|
15497
|
-
await doFlush();
|
|
15498
|
-
},
|
|
15499
|
-
isTerminal: () => terminal
|
|
15500
|
-
};
|
|
15501
|
-
}
|
|
15502
|
-
function defaultPostTerminalWarn(state) {
|
|
15503
|
-
console.warn(
|
|
15504
|
-
`[progress-handle] update() called after terminal transition (${state.terminal?.kind}) \u2014 ignored.`
|
|
15505
|
-
);
|
|
15506
|
-
}
|
|
15507
|
-
|
|
15508
|
-
// src/progress-tools.ts
|
|
15509
|
-
var ProgressToolRegistry = class {
|
|
15510
|
-
constructor(opts) {
|
|
15511
|
-
this.opts = opts;
|
|
15512
|
-
this.toolNames = {
|
|
15513
|
-
start: `${opts.prefix}_progress_start`,
|
|
15514
|
-
update: `${opts.prefix}_progress_update`,
|
|
15515
|
-
complete: `${opts.prefix}_progress_complete`,
|
|
15516
|
-
fail: `${opts.prefix}_progress_fail`
|
|
15517
|
-
};
|
|
15518
|
-
}
|
|
15519
|
-
handles = /* @__PURE__ */ new Map();
|
|
15520
|
-
toolNames;
|
|
15521
|
-
/** Tool definitions to splice into the MCP ListToolsRequest response. */
|
|
15522
|
-
getDefinitions() {
|
|
15523
|
-
const { surfaceDescription, startArgsSchema } = this.opts;
|
|
15524
|
-
const debounceMs = this.opts.debounceMs ?? 1e3;
|
|
15525
|
-
const debounceText = debounceMs === 1e3 ? "per second" : `every ${debounceMs}ms`;
|
|
15526
|
-
return [
|
|
15527
|
-
{
|
|
15528
|
-
name: this.toolNames.start,
|
|
15529
|
-
description: `Post a "still working" anchor to ${surfaceDescription} and return a progress_id. The anchor message updates in place as you call ${this.toolNames.update} \u2014 operators see one message that ticks forward instead of a flood of new ones. Always end with ${this.toolNames.complete} or ${this.toolNames.fail}; otherwise the anchor lingers as "working" until the stale-state sweep flips it. Opt-in \u2014 only call this for tasks that will take more than a few seconds.`,
|
|
15530
|
-
inputSchema: {
|
|
15531
|
-
type: "object",
|
|
15532
|
-
properties: {
|
|
15533
|
-
label: {
|
|
15534
|
-
type: "string",
|
|
15535
|
-
description: 'Short top-line label for the task (e.g. "diagnose vigil"). Stays constant for the lifetime of this progress anchor.'
|
|
15536
|
-
},
|
|
15537
|
-
initial_mode: {
|
|
15538
|
-
type: "string",
|
|
15539
|
-
enum: ["thinking", "working", "waiting"],
|
|
15540
|
-
description: 'Initial mode emoji \u2014 defaults to "working".'
|
|
15541
|
-
},
|
|
15542
|
-
...startArgsSchema.properties
|
|
15543
|
-
},
|
|
15544
|
-
required: ["label", ...startArgsSchema.required]
|
|
15545
|
-
}
|
|
15546
|
-
},
|
|
15547
|
-
{
|
|
15548
|
-
name: this.toolNames.update,
|
|
15549
|
-
description: `Update the progress anchor in place. The state machine debounces calls to one ${surfaceDescription} API call ${debounceText}, so spamming this every step is safe \u2014 only the latest pending state will paint. Any subset of fields can be passed; omitted ones retain their previous value.`,
|
|
15550
|
-
inputSchema: {
|
|
15551
|
-
type: "object",
|
|
15552
|
-
properties: {
|
|
15553
|
-
progress_id: { type: "string", description: "The progress_id returned from start." },
|
|
15554
|
-
step_label: { type: "string", description: 'Short label for the current step (e.g. "Querying CloudWatch logs").' },
|
|
15555
|
-
step_current: { type: "number", description: "1-based step counter \u2014 current." },
|
|
15556
|
-
step_total: { type: "number", description: "Total steps if known. Omit for open-ended progress." },
|
|
15557
|
-
mode: { type: "string", enum: ["thinking", "working", "waiting"] },
|
|
15558
|
-
detail: { type: "string", description: "Free-text detail line under the step label." }
|
|
15559
|
-
},
|
|
15560
|
-
required: ["progress_id"]
|
|
15561
|
-
}
|
|
15562
|
-
},
|
|
15563
|
-
{
|
|
15564
|
-
name: this.toolNames.complete,
|
|
15565
|
-
description: `Mark the progress anchor as \u2705 completed. Flushes any pending debounced state before painting the terminal banner \u2014 the operator never sees a stale "Step N" beside the \u2705. After this, the handle is dead; further updates are no-ops.`,
|
|
15566
|
-
inputSchema: {
|
|
15567
|
-
type: "object",
|
|
15568
|
-
properties: {
|
|
15569
|
-
progress_id: { type: "string" },
|
|
15570
|
-
summary: { type: "string", description: "Optional one-line summary of what was achieved." }
|
|
15571
|
-
},
|
|
15572
|
-
required: ["progress_id"]
|
|
15573
|
-
}
|
|
15574
|
-
},
|
|
15575
|
-
{
|
|
15576
|
-
name: this.toolNames.fail,
|
|
15577
|
-
description: `Mark the progress anchor as \u274C failed. Always include a one-sentence reason \u2014 the operator can't tell *why* from emoji alone. Same flush-then-paint behaviour as complete.`,
|
|
15578
|
-
inputSchema: {
|
|
15579
|
-
type: "object",
|
|
15580
|
-
properties: {
|
|
15581
|
-
progress_id: { type: "string" },
|
|
15582
|
-
reason: { type: "string", description: "One-sentence failure reason." }
|
|
15583
|
-
},
|
|
15584
|
-
required: ["progress_id", "reason"]
|
|
15585
|
-
}
|
|
15586
|
-
}
|
|
15587
|
-
];
|
|
15588
|
-
}
|
|
15589
|
-
/**
|
|
15590
|
-
* Dispatch a CallToolRequest. Returns `undefined` if the tool name
|
|
15591
|
-
* isn't one of ours (so the caller can keep walking its dispatch
|
|
15592
|
-
* chain). Returns a tool result otherwise.
|
|
15593
|
-
*/
|
|
15594
|
-
async handle(name, args) {
|
|
15595
|
-
if (name === this.toolNames.start) return this.handleStart(args);
|
|
15596
|
-
if (name === this.toolNames.update) return this.handleUpdate(args);
|
|
15597
|
-
if (name === this.toolNames.complete) return this.handleComplete(args);
|
|
15598
|
-
if (name === this.toolNames.fail) return this.handleFail(args);
|
|
15599
|
-
return void 0;
|
|
15600
|
-
}
|
|
15601
|
-
/** Live count of in-flight handles. Exposed for tests + diagnostics. */
|
|
15602
|
-
size() {
|
|
15603
|
-
return this.handles.size;
|
|
15604
|
-
}
|
|
15605
|
-
async handleStart(args) {
|
|
15606
|
-
const label = typeof args.label === "string" ? args.label : null;
|
|
15607
|
-
if (!label) return errResult(`${this.toolNames.start}: 'label' must be a non-empty string`);
|
|
15608
|
-
if ("initial_mode" in args && !isMode(args.initial_mode)) {
|
|
15609
|
-
return errResult(
|
|
15610
|
-
`${this.toolNames.start}: 'initial_mode' must be one of 'thinking', 'working', or 'waiting'`
|
|
15611
|
-
);
|
|
15612
|
-
}
|
|
15613
|
-
for (const k of this.opts.startArgsSchema.required) {
|
|
15614
|
-
if (typeof args[k] !== "string" || args[k].length === 0) {
|
|
15615
|
-
return errResult(`${this.toolNames.start}: '${k}' must be a non-empty string`);
|
|
15616
|
-
}
|
|
15617
|
-
}
|
|
15618
|
-
const initialMode = isMode(args.initial_mode) ? args.initial_mode : void 0;
|
|
15619
|
-
const progressId = randomUUID();
|
|
15620
|
-
try {
|
|
15621
|
-
const flush = this.opts.createFlush(args);
|
|
15622
|
-
const handle = await startProgressHandle({
|
|
15623
|
-
initialLabel: label,
|
|
15624
|
-
initialMode,
|
|
15625
|
-
flush,
|
|
15626
|
-
debounceMs: this.opts.debounceMs ?? 1e3
|
|
15627
|
-
});
|
|
15628
|
-
this.handles.set(progressId, handle);
|
|
15629
|
-
return okResult(JSON.stringify({ progress_id: progressId }));
|
|
15630
|
-
} catch (err) {
|
|
15631
|
-
return errResult(`${this.toolNames.start} failed: ${err.message ?? String(err)}`);
|
|
15632
|
-
}
|
|
15633
|
-
}
|
|
15634
|
-
async handleUpdate(args) {
|
|
15635
|
-
const id = typeof args.progress_id === "string" ? args.progress_id : null;
|
|
15636
|
-
if (!id) return errResult(`${this.toolNames.update}: 'progress_id' must be a string`);
|
|
15637
|
-
const handle = this.handles.get(id);
|
|
15638
|
-
if (!handle) return errResult(`${this.toolNames.update}: unknown progress_id (already terminated, or never started)`);
|
|
15639
|
-
if ("mode" in args && !isMode(args.mode)) {
|
|
15640
|
-
return errResult(
|
|
15641
|
-
`${this.toolNames.update}: 'mode' must be one of 'thinking', 'working', or 'waiting'`
|
|
15642
|
-
);
|
|
15643
|
-
}
|
|
15644
|
-
if (typeof args.step_total === "number" && typeof args.step_current !== "number") {
|
|
15645
|
-
return errResult(
|
|
15646
|
-
`${this.toolNames.update}: 'step_total' requires 'step_current'`
|
|
15647
|
-
);
|
|
15648
|
-
}
|
|
15649
|
-
const next = {};
|
|
15650
|
-
if (typeof args.step_label === "string") next.stepLabel = args.step_label;
|
|
15651
|
-
if (typeof args.step_current === "number") {
|
|
15652
|
-
next.stepNumber = {
|
|
15653
|
-
current: args.step_current,
|
|
15654
|
-
...typeof args.step_total === "number" ? { total: args.step_total } : {}
|
|
15655
|
-
};
|
|
15656
|
-
}
|
|
15657
|
-
if (isMode(args.mode)) next.mode = args.mode;
|
|
15658
|
-
if (typeof args.detail === "string") next.detail = args.detail;
|
|
15659
|
-
try {
|
|
15660
|
-
await handle.update(next);
|
|
15661
|
-
return okResult("ok");
|
|
15662
|
-
} catch (err) {
|
|
15663
|
-
return errResult(`${this.toolNames.update} failed: ${err.message ?? String(err)}`);
|
|
15664
|
-
}
|
|
15665
|
-
}
|
|
15666
|
-
async handleComplete(args) {
|
|
15667
|
-
const id = typeof args.progress_id === "string" ? args.progress_id : null;
|
|
15668
|
-
if (!id) return errResult(`${this.toolNames.complete}: 'progress_id' must be a string`);
|
|
15669
|
-
const handle = this.handles.get(id);
|
|
15670
|
-
if (!handle) return errResult(`${this.toolNames.complete}: unknown progress_id`);
|
|
15671
|
-
const summary = typeof args.summary === "string" ? args.summary : void 0;
|
|
15672
|
-
try {
|
|
15673
|
-
await handle.complete(summary);
|
|
15674
|
-
this.handles.delete(id);
|
|
15675
|
-
return okResult("ok");
|
|
15676
|
-
} catch (err) {
|
|
15677
|
-
return errResult(`${this.toolNames.complete} failed: ${err.message ?? String(err)}`);
|
|
15678
|
-
}
|
|
15679
|
-
}
|
|
15680
|
-
async handleFail(args) {
|
|
15681
|
-
const id = typeof args.progress_id === "string" ? args.progress_id : null;
|
|
15682
|
-
if (!id) return errResult(`${this.toolNames.fail}: 'progress_id' must be a string`);
|
|
15683
|
-
const reason = typeof args.reason === "string" ? args.reason : null;
|
|
15684
|
-
if (!reason) return errResult(`${this.toolNames.fail}: 'reason' must be a non-empty string`);
|
|
15685
|
-
const handle = this.handles.get(id);
|
|
15686
|
-
if (!handle) return errResult(`${this.toolNames.fail}: unknown progress_id`);
|
|
15687
|
-
try {
|
|
15688
|
-
await handle.fail(reason);
|
|
15689
|
-
this.handles.delete(id);
|
|
15690
|
-
return okResult("ok");
|
|
15691
|
-
} catch (err) {
|
|
15692
|
-
return errResult(`${this.toolNames.fail} failed: ${err.message ?? String(err)}`);
|
|
15693
|
-
}
|
|
15694
|
-
}
|
|
15695
|
-
};
|
|
15696
|
-
function okResult(text) {
|
|
15697
|
-
return { content: [{ type: "text", text }] };
|
|
15698
|
-
}
|
|
15699
|
-
function errResult(text) {
|
|
15700
|
-
return { content: [{ type: "text", text }], isError: true };
|
|
15701
|
-
}
|
|
15702
|
-
function isMode(value) {
|
|
15703
|
-
return value === "thinking" || value === "working" || value === "waiting";
|
|
15704
|
-
}
|
|
15705
|
-
|
|
15706
|
-
// src/telegram-progress.ts
|
|
15707
|
-
var MODE_EMOJI = {
|
|
15708
|
-
thinking: "\u{1F4AD}",
|
|
15709
|
-
working: "\u{1F6E0}\uFE0F",
|
|
15710
|
-
waiting: "\u23F3"
|
|
15711
|
-
};
|
|
15712
|
-
var TERMINAL_EMOJI = {
|
|
15713
|
-
completed: "\u2705",
|
|
15714
|
-
failed: "\u274C"
|
|
15715
|
-
};
|
|
15716
|
-
function formatWallClock(ms, timeZone) {
|
|
15717
|
-
const fmt = new Intl.DateTimeFormat("en-GB", {
|
|
15718
|
-
hour: "2-digit",
|
|
15719
|
-
minute: "2-digit",
|
|
15720
|
-
second: "2-digit",
|
|
15721
|
-
hour12: false,
|
|
15722
|
-
timeZone,
|
|
15723
|
-
timeZoneName: "short"
|
|
15724
|
-
});
|
|
15725
|
-
const parts = fmt.formatToParts(new Date(ms));
|
|
15726
|
-
const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
|
|
15727
|
-
return `${get("hour")}:${get("minute")}:${get("second")} ${get("timeZoneName")}`;
|
|
15728
|
-
}
|
|
15729
|
-
function escapeHtml(s) {
|
|
15730
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
15731
|
-
}
|
|
15732
|
-
function renderProgressHtml(state) {
|
|
15733
|
-
const lines = [];
|
|
15734
|
-
if (state.terminal) {
|
|
15735
|
-
const banner = state.terminal.kind === "completed" ? "Done" : "Failed";
|
|
15736
|
-
lines.push(`${TERMINAL_EMOJI[state.terminal.kind]} <b>${banner}</b> \u2014 ${escapeHtml(state.initialLabel)}`);
|
|
15737
|
-
if (state.terminal.message) lines.push(escapeHtml(state.terminal.message));
|
|
15738
|
-
const verb = state.terminal.kind === "completed" ? "completed" : "failed";
|
|
15739
|
-
lines.push(`<i>${verb}: ${formatWallClock(state.lastChangeAt)}</i>`);
|
|
15740
|
-
return lines.join("\n");
|
|
15741
|
-
}
|
|
15742
|
-
lines.push(`${MODE_EMOJI[state.mode]} <b>Working on:</b> ${escapeHtml(state.initialLabel)}`);
|
|
15743
|
-
if (state.stepNumber || state.stepLabel) {
|
|
15744
|
-
const parts = [];
|
|
15745
|
-
if (state.stepNumber) {
|
|
15746
|
-
if (state.stepNumber.total != null) {
|
|
15747
|
-
parts.push(`<b>Step ${state.stepNumber.current} of ${state.stepNumber.total}</b>`);
|
|
15748
|
-
} else {
|
|
15749
|
-
parts.push(`<b>Step ${state.stepNumber.current}</b>`);
|
|
15750
|
-
}
|
|
15751
|
-
}
|
|
15752
|
-
if (state.stepLabel) parts.push(escapeHtml(state.stepLabel));
|
|
15753
|
-
lines.push(parts.join(": "));
|
|
15754
|
-
}
|
|
15755
|
-
if (state.detail) lines.push(`<i>${escapeHtml(state.detail)}</i>`);
|
|
15756
|
-
lines.push(`<i>last update: ${formatWallClock(state.lastChangeAt)}</i>`);
|
|
15757
|
-
return lines.join("\n");
|
|
15758
|
-
}
|
|
15759
|
-
var TelegramApiError = class extends Error {
|
|
15760
|
-
constructor(endpoint, apiError) {
|
|
15761
|
-
super(`Telegram ${endpoint} failed: ${apiError}`);
|
|
15762
|
-
this.endpoint = endpoint;
|
|
15763
|
-
this.apiError = apiError;
|
|
15764
|
-
this.name = "TelegramApiError";
|
|
15765
|
-
}
|
|
15766
|
-
};
|
|
15767
|
-
function isMessageNotModified(description) {
|
|
15768
|
-
if (!description) return false;
|
|
15769
|
-
return /message is not modified/i.test(description);
|
|
15770
|
-
}
|
|
15771
|
-
function createTelegramProgressFlush(opts) {
|
|
15772
|
-
let anchorMessageId = null;
|
|
15773
|
-
return async function flush(state) {
|
|
15774
|
-
const text = renderProgressHtml(state);
|
|
15775
|
-
if (anchorMessageId == null) {
|
|
15776
|
-
const body = {
|
|
15777
|
-
chat_id: opts.chatId,
|
|
15778
|
-
text,
|
|
15779
|
-
parse_mode: "HTML",
|
|
15780
|
-
disable_notification: true
|
|
15781
|
-
};
|
|
15782
|
-
if (opts.messageThreadId != null) body.message_thread_id = opts.messageThreadId;
|
|
15783
|
-
if (opts.replyToMessageId != null) body.reply_to_message_id = opts.replyToMessageId;
|
|
15784
|
-
const res = await opts.callImpl("sendMessage", body);
|
|
15785
|
-
if (!res.ok || !res.result?.message_id) {
|
|
15786
|
-
throw new TelegramApiError("sendMessage", res.description ?? "missing message_id");
|
|
15787
|
-
}
|
|
15788
|
-
anchorMessageId = res.result.message_id;
|
|
15789
|
-
opts.onAnchorPosted?.(res.result.message_id);
|
|
15790
|
-
return;
|
|
15791
|
-
}
|
|
15792
|
-
try {
|
|
15793
|
-
const res = await opts.callImpl("editMessageText", {
|
|
15794
|
-
chat_id: opts.chatId,
|
|
15795
|
-
message_id: anchorMessageId,
|
|
15796
|
-
text,
|
|
15797
|
-
parse_mode: "HTML"
|
|
15798
|
-
});
|
|
15799
|
-
if (!res.ok) {
|
|
15800
|
-
if (isMessageNotModified(res.description)) return;
|
|
15801
|
-
throw new TelegramApiError("editMessageText", res.description ?? "unknown");
|
|
15802
|
-
}
|
|
15803
|
-
} catch (err) {
|
|
15804
|
-
if (opts.onApiError && err instanceof TelegramApiError) {
|
|
15805
|
-
await opts.onApiError(err);
|
|
15806
|
-
return;
|
|
15807
|
-
}
|
|
15808
|
-
throw err;
|
|
15809
|
-
}
|
|
15810
|
-
};
|
|
15811
|
-
}
|
|
15812
|
-
|
|
15813
15389
|
// src/impersonation.ts
|
|
15814
15390
|
var ENV_VAR = "AGT_ACT_AS_AGENT_ID";
|
|
15815
15391
|
var OVERRIDE_ENV_VAR = "ENABLE_IMPERSONATION_CHANNELS";
|
|
@@ -16276,7 +15852,7 @@ async function handleRestartCommand(opts) {
|
|
|
16276
15852
|
ts: Date.now(),
|
|
16277
15853
|
reply: { chat_id: opts.chatId, message_id: opts.messageId }
|
|
16278
15854
|
};
|
|
16279
|
-
const tmpPath = `${flagPath}.${process.pid}.${
|
|
15855
|
+
const tmpPath = `${flagPath}.${process.pid}.${randomUUID()}.tmp`;
|
|
16280
15856
|
writeFileSync3(tmpPath, JSON.stringify(flag) + "\n", "utf8");
|
|
16281
15857
|
renameSync3(tmpPath, flagPath);
|
|
16282
15858
|
process.stderr.write(
|
|
@@ -16702,42 +16278,8 @@ var mcp = new Server(
|
|
|
16702
16278
|
].join(" ")
|
|
16703
16279
|
}
|
|
16704
16280
|
);
|
|
16705
|
-
var TELEGRAM_PROGRESS_TIMEOUT_MS = 5e3;
|
|
16706
|
-
var progressRegistry = new ProgressToolRegistry({
|
|
16707
|
-
prefix: "telegram",
|
|
16708
|
-
surfaceDescription: "a Telegram chat",
|
|
16709
|
-
startArgsSchema: {
|
|
16710
|
-
properties: {
|
|
16711
|
-
chat_id: {
|
|
16712
|
-
type: "string",
|
|
16713
|
-
description: "Telegram chat id (from the `chat_id` attribute on the inbound <channel> tag). Numeric ids are passed as strings; the API accepts both."
|
|
16714
|
-
},
|
|
16715
|
-
message_thread_id: {
|
|
16716
|
-
type: "string",
|
|
16717
|
-
description: "Forum topic id (`message_thread_id`) \u2014 pass this if the inbound arrived in a forum topic so the anchor lands in the same topic. Omit for normal chats."
|
|
16718
|
-
},
|
|
16719
|
-
reply_to_message_id: {
|
|
16720
|
-
type: "string",
|
|
16721
|
-
description: "Optional message id to thread the anchor under (typically the inbound message_id from the <channel> tag)."
|
|
16722
|
-
}
|
|
16723
|
-
},
|
|
16724
|
-
required: ["chat_id"]
|
|
16725
|
-
},
|
|
16726
|
-
createFlush: (args) => {
|
|
16727
|
-
const chatId = args.chat_id;
|
|
16728
|
-
const threadId = typeof args.message_thread_id === "string" && args.message_thread_id.length > 0 ? Number(args.message_thread_id) : void 0;
|
|
16729
|
-
const replyTo = typeof args.reply_to_message_id === "string" && args.reply_to_message_id.length > 0 ? Number(args.reply_to_message_id) : void 0;
|
|
16730
|
-
return createTelegramProgressFlush({
|
|
16731
|
-
chatId,
|
|
16732
|
-
messageThreadId: Number.isFinite(threadId) ? threadId : void 0,
|
|
16733
|
-
replyToMessageId: Number.isFinite(replyTo) ? replyTo : void 0,
|
|
16734
|
-
callImpl: (method, body) => telegramApiCall(method, body, TELEGRAM_PROGRESS_TIMEOUT_MS)
|
|
16735
|
-
});
|
|
16736
|
-
}
|
|
16737
|
-
});
|
|
16738
16281
|
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
16739
16282
|
tools: [
|
|
16740
|
-
...progressRegistry.getDefinitions(),
|
|
16741
16283
|
{
|
|
16742
16284
|
name: "channel_request_input",
|
|
16743
16285
|
description: "Generic Yes/No or multi-choice picker. Renders as a Telegram inline keyboard; the tool blocks until the user taps an option (or the timeout elapses) and returns the resolved value. Use this when you need a structured pick rather than freeform text \u2014 e.g. confirming a draft action or disambiguating an ambiguous fork.",
|
|
@@ -16856,8 +16398,6 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
16856
16398
|
if (isImpersonating() && !channelsEnabledOverride() && TELEGRAM_EGRESS_TOOLS.has(name)) {
|
|
16857
16399
|
return buildImpersonationRefusal(name);
|
|
16858
16400
|
}
|
|
16859
|
-
const progressResult = await progressRegistry.handle(name, args ?? {});
|
|
16860
|
-
if (progressResult !== void 0) return progressResult;
|
|
16861
16401
|
if (name === "channel_request_input") {
|
|
16862
16402
|
return handleChannelRequestInput(args ?? {});
|
|
16863
16403
|
}
|
|
@@ -17057,12 +16597,12 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
17057
16597
|
function channelRequestInputAvailable() {
|
|
17058
16598
|
return Boolean(AGT_HOST && AGT_API_KEY && AGT_AGENT_ID);
|
|
17059
16599
|
}
|
|
17060
|
-
function
|
|
16600
|
+
function errResult(text) {
|
|
17061
16601
|
return { content: [{ type: "text", text }], isError: true };
|
|
17062
16602
|
}
|
|
17063
16603
|
async function handleChannelRequestInput(args) {
|
|
17064
16604
|
if (!channelRequestInputAvailable()) {
|
|
17065
|
-
return
|
|
16605
|
+
return errResult(
|
|
17066
16606
|
"channel_request_input is disabled \u2014 missing AGT_HOST / AGT_API_KEY / AGT_AGENT_ID env wiring."
|
|
17067
16607
|
);
|
|
17068
16608
|
}
|
|
@@ -17070,12 +16610,12 @@ async function handleChannelRequestInput(args) {
|
|
|
17070
16610
|
const { apiCall: apiCall2, waitForResolution: waitForResolution2, validateAskUserOptions: validateAskUserOptions2 } = runtime;
|
|
17071
16611
|
const { thread_id, prompt_text, options, schema, request_id, timeout_seconds } = args;
|
|
17072
16612
|
if (typeof thread_id !== "string" || !thread_id) {
|
|
17073
|
-
return
|
|
16613
|
+
return errResult(
|
|
17074
16614
|
"thread_id is required (Telegram shape: `<chat_id>` or `<chat_id>:<reply_to_message_id>`)"
|
|
17075
16615
|
);
|
|
17076
16616
|
}
|
|
17077
16617
|
if (typeof prompt_text !== "string" || !prompt_text) {
|
|
17078
|
-
return
|
|
16618
|
+
return errResult("prompt_text is required");
|
|
17079
16619
|
}
|
|
17080
16620
|
const optsValidation = validateAskUserOptions2(options, {
|
|
17081
16621
|
maxOptions: 4,
|
|
@@ -17083,27 +16623,27 @@ async function handleChannelRequestInput(args) {
|
|
|
17083
16623
|
maxValueChars: 2e3
|
|
17084
16624
|
});
|
|
17085
16625
|
if (!optsValidation.ok) {
|
|
17086
|
-
return
|
|
16626
|
+
return errResult(
|
|
17087
16627
|
`Invalid options: ${optsValidation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`
|
|
17088
16628
|
);
|
|
17089
16629
|
}
|
|
17090
16630
|
const optionsArr = options;
|
|
17091
16631
|
if (optionsArr.length < 2) {
|
|
17092
|
-
return
|
|
16632
|
+
return errResult(
|
|
17093
16633
|
"options must contain at least 2 entries (this is a picker, not a notification)"
|
|
17094
16634
|
);
|
|
17095
16635
|
}
|
|
17096
16636
|
if (!schema || typeof schema.type !== "string") {
|
|
17097
|
-
return
|
|
16637
|
+
return errResult("schema is required with shape { type: 'yes_no' | 'choice' }");
|
|
17098
16638
|
}
|
|
17099
16639
|
if (schema.type !== "yes_no" && schema.type !== "choice") {
|
|
17100
|
-
return
|
|
16640
|
+
return errResult("schema.type must be 'yes_no' or 'choice'");
|
|
17101
16641
|
}
|
|
17102
16642
|
if (schema.type === "yes_no" && optionsArr.length !== 2) {
|
|
17103
|
-
return
|
|
16643
|
+
return errResult("schema.type='yes_no' requires exactly 2 options");
|
|
17104
16644
|
}
|
|
17105
16645
|
if (request_id !== void 0 && (typeof request_id !== "string" || !request_id)) {
|
|
17106
|
-
return
|
|
16646
|
+
return errResult("request_id must be a non-empty string when provided");
|
|
17107
16647
|
}
|
|
17108
16648
|
const requestedTimeout = typeof timeout_seconds === "number" ? timeout_seconds : 300;
|
|
17109
16649
|
const timeoutSec = Math.max(10, Math.min(3600, requestedTimeout));
|
|
@@ -17125,10 +16665,10 @@ async function handleChannelRequestInput(args) {
|
|
|
17125
16665
|
dispatchPayload = await res.json().catch(() => ({}));
|
|
17126
16666
|
if (!res.ok || !dispatchPayload.ok || !dispatchPayload.callback_id) {
|
|
17127
16667
|
const reason = dispatchPayload.reason ?? `http_${res.status}`;
|
|
17128
|
-
return
|
|
16668
|
+
return errResult(`channel_request_input dispatch failed: ${reason}`);
|
|
17129
16669
|
}
|
|
17130
16670
|
} catch (err) {
|
|
17131
|
-
return
|
|
16671
|
+
return errResult(
|
|
17132
16672
|
`channel_request_input dispatch failed: ${err.message}`
|
|
17133
16673
|
);
|
|
17134
16674
|
}
|