omnius 1.0.657 → 1.0.659

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/index.js CHANGED
@@ -779939,6 +779939,438 @@ var init_telegram_vision_router = __esm({
779939
779939
  }
779940
779940
  });
779941
779941
 
779942
+ // packages/cli/src/tui/telegram-api-governor.ts
779943
+ function positiveInteger3(value2, fallback) {
779944
+ return Number.isFinite(value2) && value2 > 0 ? Math.max(1, Math.floor(value2)) : fallback;
779945
+ }
779946
+ function nonNegativeInteger(value2, fallback) {
779947
+ return Number.isFinite(value2) && value2 >= 0 ? Math.max(0, Math.floor(value2)) : fallback;
779948
+ }
779949
+ function parseArrayLength(value2) {
779950
+ if (Array.isArray(value2)) return value2.length;
779951
+ if (typeof value2 !== "string") return void 0;
779952
+ try {
779953
+ const parsed = JSON.parse(value2);
779954
+ return Array.isArray(parsed) ? parsed.length : void 0;
779955
+ } catch {
779956
+ return void 0;
779957
+ }
779958
+ }
779959
+ function telegramDeliveryMessageCost(method, body) {
779960
+ if (!TELEGRAM_DELIVERY_METHODS.has(method)) return 0;
779961
+ if (method === "sendMediaGroup") {
779962
+ return Math.max(1, parseArrayLength(body?.["media"]) ?? 1);
779963
+ }
779964
+ if (method === "copyMessages" || method === "forwardMessages") {
779965
+ return Math.max(1, parseArrayLength(body?.["message_ids"]) ?? 1);
779966
+ }
779967
+ return 1;
779968
+ }
779969
+ function telegramFloodWaitSeconds(value2) {
779970
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) {
779971
+ return void 0;
779972
+ }
779973
+ const response = value2;
779974
+ if (Number(response["error_code"]) !== 429) return void 0;
779975
+ const parameters = response["parameters"];
779976
+ if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) {
779977
+ return void 0;
779978
+ }
779979
+ const seconds = Number(
779980
+ parameters["retry_after"]
779981
+ );
779982
+ return Number.isFinite(seconds) && seconds > 0 ? seconds : void 0;
779983
+ }
779984
+ function inferredGroupChat(chatId, chatType) {
779985
+ if (chatType) return chatType !== "private";
779986
+ if (typeof chatId === "number") return chatId < 0;
779987
+ return typeof chatId === "string" && /^-\d+$/.test(chatId) && Number(chatId) < 0;
779988
+ }
779989
+ function abortError(reason) {
779990
+ if (reason instanceof Error) return reason;
779991
+ const error = new Error(
779992
+ typeof reason === "string" && reason ? reason : "Telegram request aborted"
779993
+ );
779994
+ error.name = "AbortError";
779995
+ return error;
779996
+ }
779997
+ var TELEGRAM_DELIVERY_METHODS, TelegramApiGovernor;
779998
+ var init_telegram_api_governor = __esm({
779999
+ "packages/cli/src/tui/telegram-api-governor.ts"() {
780000
+ TELEGRAM_DELIVERY_METHODS = /* @__PURE__ */ new Set([
780001
+ "copyMessage",
780002
+ "copyMessages",
780003
+ "forwardMessage",
780004
+ "forwardMessages",
780005
+ "sendAnimation",
780006
+ "sendAudio",
780007
+ "sendChecklist",
780008
+ "sendContact",
780009
+ "sendDice",
780010
+ "sendDocument",
780011
+ "sendGame",
780012
+ "sendInvoice",
780013
+ "sendLivePhoto",
780014
+ "sendLocation",
780015
+ "sendMediaGroup",
780016
+ "sendMessage",
780017
+ "sendMessageDraft",
780018
+ "sendPaidMedia",
780019
+ "sendPhoto",
780020
+ "sendPoll",
780021
+ "sendSticker",
780022
+ "sendVenue",
780023
+ "sendVideo",
780024
+ "sendVideoNote",
780025
+ "sendVoice"
780026
+ ]);
780027
+ TelegramApiGovernor = class {
780028
+ globalRequestsPerSecond;
780029
+ globalMessagesPerSecond;
780030
+ perChatMessageIntervalMs;
780031
+ groupMessagesPerMinute;
780032
+ maxConcurrentRequests;
780033
+ maxQueuedRequests;
780034
+ now;
780035
+ lanes = /* @__PURE__ */ new Map();
780036
+ laneOrder = [];
780037
+ nextLaneIndex = 0;
780038
+ nextRequestId = 1;
780039
+ queuedCount = 0;
780040
+ activeCount = 0;
780041
+ timer = null;
780042
+ stoppedReason = null;
780043
+ globalCooldownUntilMs = 0;
780044
+ chatCooldownUntilMs = /* @__PURE__ */ new Map();
780045
+ globalRequestStarts = [];
780046
+ globalMessageStarts = [];
780047
+ groupMessageStarts = /* @__PURE__ */ new Map();
780048
+ nextChatMessageAtMs = /* @__PURE__ */ new Map();
780049
+ constructor(options2 = {}) {
780050
+ this.globalRequestsPerSecond = positiveInteger3(
780051
+ options2.globalRequestsPerSecond,
780052
+ 28
780053
+ );
780054
+ this.globalMessagesPerSecond = positiveInteger3(
780055
+ options2.globalMessagesPerSecond,
780056
+ 28
780057
+ );
780058
+ this.perChatMessageIntervalMs = nonNegativeInteger(
780059
+ options2.perChatMessageIntervalMs,
780060
+ 1050
780061
+ );
780062
+ this.groupMessagesPerMinute = positiveInteger3(
780063
+ options2.groupMessagesPerMinute,
780064
+ 20
780065
+ );
780066
+ this.maxConcurrentRequests = positiveInteger3(
780067
+ options2.maxConcurrentRequests,
780068
+ 8
780069
+ );
780070
+ this.maxQueuedRequests = positiveInteger3(options2.maxQueuedRequests, 2048);
780071
+ this.now = options2.now ?? Date.now;
780072
+ }
780073
+ run(request) {
780074
+ if (this.stoppedReason) return Promise.reject(this.stoppedReason);
780075
+ if (request.signal?.aborted) {
780076
+ return Promise.reject(abortError(request.signal.reason));
780077
+ }
780078
+ if (request.method === "getUpdates") return request.execute();
780079
+ if (this.queuedCount >= this.maxQueuedRequests) {
780080
+ return Promise.reject(
780081
+ new Error(
780082
+ `Telegram API governor queue is full (${this.maxQueuedRequests} requests)`
780083
+ )
780084
+ );
780085
+ }
780086
+ const chatKey = request.chatId === void 0 ? void 0 : String(request.chatId);
780087
+ const laneKey = chatKey === void 0 ? "control" : `chat:${chatKey}`;
780088
+ const messageCost = telegramDeliveryMessageCost(
780089
+ request.method,
780090
+ request.body
780091
+ );
780092
+ return new Promise((resolve92, reject) => {
780093
+ const pending2 = {
780094
+ ...request,
780095
+ id: this.nextRequestId++,
780096
+ laneKey,
780097
+ messageCost,
780098
+ notBeforeMs: 0,
780099
+ resolve: resolve92,
780100
+ reject,
780101
+ queued: false,
780102
+ settled: false
780103
+ };
780104
+ if (request.signal) {
780105
+ pending2.abortListener = () => {
780106
+ if (pending2.settled) return;
780107
+ pending2.settled = true;
780108
+ if (pending2.queued) {
780109
+ pending2.queued = false;
780110
+ this.queuedCount = Math.max(0, this.queuedCount - 1);
780111
+ }
780112
+ reject(abortError(request.signal?.reason));
780113
+ this.pump();
780114
+ };
780115
+ request.signal.addEventListener("abort", pending2.abortListener, {
780116
+ once: true
780117
+ });
780118
+ }
780119
+ this.enqueue(pending2);
780120
+ this.pump();
780121
+ });
780122
+ }
780123
+ stop(reason = "Telegram API governor stopped") {
780124
+ if (this.stoppedReason) return;
780125
+ this.stoppedReason = new Error(reason);
780126
+ if (this.timer) clearTimeout(this.timer);
780127
+ this.timer = null;
780128
+ for (const queue of this.lanes.values()) {
780129
+ for (const pending2 of queue) {
780130
+ if (pending2.settled) continue;
780131
+ pending2.settled = true;
780132
+ pending2.signal?.removeEventListener(
780133
+ "abort",
780134
+ pending2.abortListener ?? (() => {
780135
+ })
780136
+ );
780137
+ pending2.reject(this.stoppedReason);
780138
+ }
780139
+ }
780140
+ this.lanes.clear();
780141
+ this.laneOrder = [];
780142
+ this.queuedCount = 0;
780143
+ }
780144
+ snapshot() {
780145
+ return {
780146
+ active: this.activeCount,
780147
+ queued: this.queuedCount,
780148
+ lanes: this.lanes.size,
780149
+ globalCooldownUntilMs: this.globalCooldownUntilMs,
780150
+ chatCooldowns: Object.fromEntries(this.chatCooldownUntilMs)
780151
+ };
780152
+ }
780153
+ enqueue(pending2, front = false) {
780154
+ let queue = this.lanes.get(pending2.laneKey);
780155
+ if (!queue) {
780156
+ queue = [];
780157
+ this.lanes.set(pending2.laneKey, queue);
780158
+ this.laneOrder.push(pending2.laneKey);
780159
+ }
780160
+ if (pending2.coalesceKey) {
780161
+ const replacedIndex = queue.findIndex(
780162
+ (item) => !item.settled && item.coalesceKey === pending2.coalesceKey
780163
+ );
780164
+ if (replacedIndex >= 0) {
780165
+ const replaced = queue.splice(replacedIndex, 1)[0];
780166
+ replaced.queued = false;
780167
+ replaced.settled = true;
780168
+ this.queuedCount = Math.max(0, this.queuedCount - 1);
780169
+ if (replaced.abortListener) {
780170
+ replaced.signal?.removeEventListener("abort", replaced.abortListener);
780171
+ }
780172
+ if (replaced.supersededValue) {
780173
+ replaced.resolve(replaced.supersededValue());
780174
+ } else {
780175
+ replaced.reject(new Error("Telegram API request was superseded"));
780176
+ }
780177
+ }
780178
+ }
780179
+ if (front) queue.unshift(pending2);
780180
+ else queue.push(pending2);
780181
+ pending2.queued = true;
780182
+ this.queuedCount += 1;
780183
+ }
780184
+ removeEmptyLane(laneKey) {
780185
+ const queue = this.lanes.get(laneKey);
780186
+ if (queue?.some((item) => !item.settled)) return;
780187
+ this.lanes.delete(laneKey);
780188
+ const index = this.laneOrder.indexOf(laneKey);
780189
+ if (index < 0) return;
780190
+ this.laneOrder.splice(index, 1);
780191
+ if (this.laneOrder.length === 0) this.nextLaneIndex = 0;
780192
+ else if (index < this.nextLaneIndex) this.nextLaneIndex -= 1;
780193
+ this.nextLaneIndex %= Math.max(1, this.laneOrder.length);
780194
+ }
780195
+ pruneHistory(history, cutoff) {
780196
+ let remove = 0;
780197
+ while (remove < history.length && history[remove] <= cutoff) remove += 1;
780198
+ if (remove > 0) history.splice(0, remove);
780199
+ }
780200
+ windowReadyAt(history, now2, windowMs, limit2, cost) {
780201
+ this.pruneHistory(history, now2 - windowMs);
780202
+ const needed = history.length + cost - limit2;
780203
+ if (needed <= 0) return now2;
780204
+ return history[Math.min(history.length - 1, needed - 1)] + windowMs + 1;
780205
+ }
780206
+ requestReadyAt(pending2, now2) {
780207
+ let readyAt = Math.max(pending2.notBeforeMs, this.globalCooldownUntilMs);
780208
+ const chatKey = pending2.chatId === void 0 ? void 0 : String(pending2.chatId);
780209
+ if (chatKey !== void 0) {
780210
+ const cooldownUntil = this.chatCooldownUntilMs.get(chatKey) ?? 0;
780211
+ if (cooldownUntil <= now2) this.chatCooldownUntilMs.delete(chatKey);
780212
+ readyAt = Math.max(readyAt, cooldownUntil);
780213
+ }
780214
+ readyAt = Math.max(
780215
+ readyAt,
780216
+ this.windowReadyAt(
780217
+ this.globalRequestStarts,
780218
+ now2,
780219
+ 1e3,
780220
+ this.globalRequestsPerSecond,
780221
+ 1
780222
+ )
780223
+ );
780224
+ if (pending2.messageCost <= 0) return readyAt;
780225
+ readyAt = Math.max(
780226
+ readyAt,
780227
+ this.windowReadyAt(
780228
+ this.globalMessageStarts,
780229
+ now2,
780230
+ 1e3,
780231
+ this.globalMessagesPerSecond,
780232
+ pending2.messageCost
780233
+ )
780234
+ );
780235
+ if (chatKey !== void 0) {
780236
+ const nextChatAt = this.nextChatMessageAtMs.get(chatKey) ?? 0;
780237
+ if (nextChatAt <= now2) this.nextChatMessageAtMs.delete(chatKey);
780238
+ readyAt = Math.max(readyAt, nextChatAt);
780239
+ if (inferredGroupChat(pending2.chatId, pending2.chatType)) {
780240
+ const groupHistory = this.groupMessageStarts.get(chatKey) ?? [];
780241
+ const groupReadyAt = this.windowReadyAt(
780242
+ groupHistory,
780243
+ now2,
780244
+ 6e4,
780245
+ this.groupMessagesPerMinute,
780246
+ pending2.messageCost
780247
+ );
780248
+ if (groupHistory.length > 0) {
780249
+ this.groupMessageStarts.set(chatKey, groupHistory);
780250
+ } else {
780251
+ this.groupMessageStarts.delete(chatKey);
780252
+ }
780253
+ readyAt = Math.max(readyAt, groupReadyAt);
780254
+ }
780255
+ }
780256
+ return readyAt;
780257
+ }
780258
+ reserve(pending2, now2) {
780259
+ this.globalRequestStarts.push(now2);
780260
+ if (pending2.messageCost <= 0) return;
780261
+ for (let index = 0; index < pending2.messageCost; index++) {
780262
+ this.globalMessageStarts.push(now2);
780263
+ }
780264
+ if (pending2.chatId === void 0) return;
780265
+ const chatKey = String(pending2.chatId);
780266
+ this.nextChatMessageAtMs.set(chatKey, now2 + this.perChatMessageIntervalMs);
780267
+ if (!inferredGroupChat(pending2.chatId, pending2.chatType)) return;
780268
+ const groupHistory = this.groupMessageStarts.get(chatKey) ?? [];
780269
+ for (let index = 0; index < pending2.messageCost; index++) {
780270
+ groupHistory.push(now2);
780271
+ }
780272
+ this.groupMessageStarts.set(chatKey, groupHistory);
780273
+ }
780274
+ nextReadyRequest(now2) {
780275
+ if (this.laneOrder.length === 0) return {};
780276
+ let nextWakeMs = Number.POSITIVE_INFINITY;
780277
+ const laneCount = this.laneOrder.length;
780278
+ for (let offset = 0; offset < laneCount; offset++) {
780279
+ const index = (this.nextLaneIndex + offset) % laneCount;
780280
+ const laneKey = this.laneOrder[index];
780281
+ const queue = this.lanes.get(laneKey);
780282
+ while (queue?.[0]?.settled) queue.shift();
780283
+ const pending2 = queue?.[0];
780284
+ if (!pending2) {
780285
+ this.removeEmptyLane(laneKey);
780286
+ return this.nextReadyRequest(now2);
780287
+ }
780288
+ const readyAt = this.requestReadyAt(pending2, now2);
780289
+ nextWakeMs = Math.min(nextWakeMs, readyAt);
780290
+ if (readyAt > now2) continue;
780291
+ queue.shift();
780292
+ pending2.queued = false;
780293
+ this.queuedCount = Math.max(0, this.queuedCount - 1);
780294
+ this.nextLaneIndex = this.laneOrder.length === 0 ? 0 : (index + 1) % this.laneOrder.length;
780295
+ this.removeEmptyLane(laneKey);
780296
+ return { pending: pending2 };
780297
+ }
780298
+ return {
780299
+ nextWakeMs: Number.isFinite(nextWakeMs) ? nextWakeMs : void 0
780300
+ };
780301
+ }
780302
+ pump() {
780303
+ if (this.stoppedReason) return;
780304
+ if (this.timer) {
780305
+ clearTimeout(this.timer);
780306
+ this.timer = null;
780307
+ }
780308
+ while (this.activeCount < this.maxConcurrentRequests) {
780309
+ const now2 = this.now();
780310
+ const { pending: pending2, nextWakeMs } = this.nextReadyRequest(now2);
780311
+ if (!pending2) {
780312
+ if (nextWakeMs !== void 0 && this.queuedCount > 0) {
780313
+ this.timer = setTimeout(
780314
+ () => {
780315
+ this.timer = null;
780316
+ this.pump();
780317
+ },
780318
+ Math.max(1, nextWakeMs - now2)
780319
+ );
780320
+ }
780321
+ return;
780322
+ }
780323
+ if (pending2.settled) continue;
780324
+ this.reserve(pending2, now2);
780325
+ this.activeCount += 1;
780326
+ void this.execute(pending2);
780327
+ }
780328
+ }
780329
+ async execute(pending2) {
780330
+ try {
780331
+ const value2 = await pending2.execute();
780332
+ const floodWait = pending2.floodWaitSeconds?.(value2) ?? telegramFloodWaitSeconds(value2);
780333
+ if (floodWait !== void 0) {
780334
+ const retryAt = this.now() + Math.ceil(floodWait * 1e3);
780335
+ if (pending2.chatId === void 0) {
780336
+ this.globalCooldownUntilMs = Math.max(
780337
+ this.globalCooldownUntilMs,
780338
+ retryAt
780339
+ );
780340
+ } else {
780341
+ const chatKey = String(pending2.chatId);
780342
+ this.chatCooldownUntilMs.set(
780343
+ chatKey,
780344
+ Math.max(this.chatCooldownUntilMs.get(chatKey) ?? 0, retryAt)
780345
+ );
780346
+ }
780347
+ if (pending2.retryOnFlood !== false && !pending2.signal?.aborted) {
780348
+ pending2.notBeforeMs = retryAt;
780349
+ this.enqueue(pending2, true);
780350
+ return;
780351
+ }
780352
+ }
780353
+ if (!pending2.settled) {
780354
+ pending2.settled = true;
780355
+ pending2.resolve(value2);
780356
+ }
780357
+ } catch (error) {
780358
+ if (!pending2.settled) {
780359
+ pending2.settled = true;
780360
+ pending2.reject(error);
780361
+ }
780362
+ } finally {
780363
+ if (pending2.settled && pending2.abortListener) {
780364
+ pending2.signal?.removeEventListener("abort", pending2.abortListener);
780365
+ }
780366
+ this.activeCount = Math.max(0, this.activeCount - 1);
780367
+ this.pump();
780368
+ }
780369
+ }
780370
+ };
780371
+ }
780372
+ });
780373
+
779942
780374
  // packages/cli/src/tui/vision-ingress.ts
779943
780375
  var vision_ingress_exports = {};
779944
780376
  __export(vision_ingress_exports, {
@@ -782719,6 +783151,7 @@ var init_telegram_bridge = __esm({
782719
783151
  init_telegram_social_event_log();
782720
783152
  init_dist();
782721
783153
  init_telegram_vision_router();
783154
+ init_telegram_api_governor();
782722
783155
  TELEGRAM_TOOL_ACTION_GROUPS = [
782723
783156
  "read",
782724
783157
  "message",
@@ -783366,11 +783799,14 @@ Telegram link integrity contract:
783366
783799
  reminder: { limit: 20, windowMs: 24 * 60 * 6e4 }
783367
783800
  };
783368
783801
  TelegramBridge = class _TelegramBridge {
783369
- constructor(botToken, onMessage, agentConfig, repoRoot, toolPolicyConfig) {
783802
+ constructor(botToken, onMessage, agentConfig, repoRoot, toolPolicyConfig, telegramApiGovernorOptions) {
783370
783803
  this.botToken = botToken;
783371
783804
  this.onMessage = onMessage;
783372
783805
  this.agentConfig = agentConfig;
783373
783806
  this.repoRoot = repoRoot;
783807
+ this.telegramApiGovernor = new TelegramApiGovernor(
783808
+ telegramApiGovernorOptions
783809
+ );
783374
783810
  this.toolPolicyConfig = toolPolicyConfig;
783375
783811
  this.telegramToolPolicy = resolveSettings(repoRoot || ".").telegramToolPolicy ?? {};
783376
783812
  this.mediaCacheDir = resolve83(
@@ -783421,6 +783857,10 @@ Telegram link integrity contract:
783421
783857
  repoRoot;
783422
783858
  polling = false;
783423
783859
  abortController = null;
783860
+ /** One fair outbound admission queue shared by every chat on this bot. */
783861
+ telegramApiGovernor;
783862
+ /** Chat types learned from inbound updates improve group quota accounting. */
783863
+ telegramKnownChatTypes = /* @__PURE__ */ new Map();
783424
783864
  pollLoopPromise = null;
783425
783865
  pollFatalNotified = false;
783426
783866
  lastUpdateId = 0;
@@ -784978,11 +785418,11 @@ ${message2}`)
784978
785418
  for (const item of subAgent.pendingIntakeRecords.splice(0)) {
784979
785419
  pendingById.set(item.intake.inputId, item);
784980
785420
  }
784981
- const retainedSteeringIntakes = subAgent.retainedSteeringIntakes ?? (subAgent.retainedSteeringIntakes = /* @__PURE__ */ new Map());
784982
- for (const item of retainedSteeringIntakes.values()) {
785421
+ subAgent.retainedSteeringIntakes ??= /* @__PURE__ */ new Map();
785422
+ for (const item of subAgent.retainedSteeringIntakes.values()) {
784983
785423
  pendingById.set(item.intake.inputId, item);
784984
785424
  }
784985
- retainedSteeringIntakes.clear();
785425
+ subAgent.retainedSteeringIntakes.clear();
784986
785426
  const pending2 = [...pendingById.values()];
784987
785427
  if (pending2.length === 0) return;
784988
785428
  for (const item of pending2) {
@@ -791918,7 +792358,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
791918
792358
  }
791919
792359
  const retryAfter = Number(result?.["parameters"]?.["retry_after"]);
791920
792360
  if (Number.isFinite(retryAfter) && retryAfter > 0) {
791921
- return Math.min(3e3, Math.floor(retryAfter * 1e3));
792361
+ return Math.ceil(retryAfter * 1e3);
791922
792362
  }
791923
792363
  return [500, 1e3, 2e3][Math.min(attempt, 2)];
791924
792364
  }
@@ -791973,6 +792413,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
791973
792413
  this.state.active = false;
791974
792414
  this.abortController?.abort();
791975
792415
  this.abortController = null;
792416
+ this.telegramApiGovernor.stop("Telegram bridge stopped");
791976
792417
  if (this.mediaCacheCleanupTimer) {
791977
792418
  clearInterval(this.mediaCacheCleanupTimer);
791978
792419
  this.mediaCacheCleanupTimer = null;
@@ -792354,6 +792795,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
792354
792795
  });
792355
792796
  if (result.error_code === 400 && result.description?.includes("not modified"))
792356
792797
  return true;
792798
+ if (result.error_code === 429) return false;
792357
792799
  if (result.ok === false)
792358
792800
  throw new Error(
792359
792801
  String(result.description || "Telegram editMessageText failed")
@@ -796260,9 +796702,7 @@ Scoped workspace: ${scopedRoot}`,
796260
796702
  enum: exposedActions,
796261
796703
  description: [
796262
796704
  "Exact Telegram operation from this enum.",
796263
- ...moderationDescriptions.map(
796264
- (description) => `${description}.`
796265
- )
796705
+ ...moderationDescriptions.map((description) => `${description}.`)
796266
796706
  ].join(" ")
796267
796707
  },
796268
796708
  target: {
@@ -797106,7 +797546,9 @@ Scoped workspace: ${scopedRoot}`,
797106
797546
  );
797107
797547
  }
797108
797548
  if (!Number.isSafeInteger(targetUserId) || targetUserId <= 0) {
797109
- throw new Error("A positive numeric Telegram target user_id is required.");
797549
+ throw new Error(
797550
+ "A positive numeric Telegram target user_id is required."
797551
+ );
797110
797552
  }
797111
797553
  const group = await this.requireTelegramModerationGroup(chatId);
797112
797554
  const rights = await this.getBotChatRights(chatId, true);
@@ -797145,7 +797587,9 @@ Scoped workspace: ${scopedRoot}`,
797145
797587
  detail: "The exact action is already staged; no duplicate prompt was sent."
797146
797588
  };
797147
797589
  }
797148
- const conflicting = [...this.telegramPendingModerationApprovals.values()].find(
797590
+ const conflicting = [
797591
+ ...this.telegramPendingModerationApprovals.values()
797592
+ ].find(
797149
797593
  (candidate) => candidate.state === "pending" && String(candidate.action.chatId) === String(chatId) && candidate.expiresAtMs > Date.now()
797150
797594
  );
797151
797595
  if (conflicting) {
@@ -797473,10 +797917,7 @@ ${this.quoteTelegramContextBlock(msg.text, 800)}`,
797473
797917
  }
797474
797918
  try {
797475
797919
  await this.requireTelegramModerationGroup(approval.action.chatId);
797476
- const rights = await this.getBotChatRights(
797477
- approval.action.chatId,
797478
- true
797479
- );
797920
+ const rights = await this.getBotChatRights(approval.action.chatId, true);
797480
797921
  if (!rights.isAdministrator || !rights.canRestrictMembers) {
797481
797922
  throw new Error(
797482
797923
  `Bot no longer has administrator can_restrict_members authority; status=${rights.status}.`
@@ -800166,26 +800607,22 @@ Content-Type: ${contentType}\r
800166
800607
  `));
800167
800608
  const body = Buffer.concat(parts);
800168
800609
  const uploadUrl = `https://api.telegram.org/bot${this.botToken}/${method}`;
800169
- const MAX_UPLOAD_ATTEMPTS = 4;
800170
- const PER_RETRY_WAIT_CAP_MS = 3e4;
800171
- let result = {};
800172
- for (let attempt = 1; attempt <= MAX_UPLOAD_ATTEMPTS; attempt++) {
800173
- const res = await fetch(uploadUrl, {
800174
- method: "POST",
800175
- headers: {
800176
- "Content-Type": `multipart/form-data; boundary=${boundary}`,
800177
- "Content-Length": String(body.length)
800178
- },
800179
- body
800180
- });
800181
- result = await res.json();
800182
- if (result.ok) break;
800183
- const retryAfterSec = result["error_code"] === 429 ? Number(result["parameters"]?.retry_after) : NaN;
800184
- const canRetry = Number.isFinite(retryAfterSec) && retryAfterSec > 0 && attempt < MAX_UPLOAD_ATTEMPTS && !this.abortController?.signal.aborted;
800185
- if (!canRetry) break;
800186
- const waitMs = Math.min(retryAfterSec * 1e3, PER_RETRY_WAIT_CAP_MS);
800187
- await new Promise((r2) => setTimeout(r2, waitMs));
800188
- }
800610
+ const result = await this.governedTelegramApiRequest(
800611
+ method,
800612
+ { chat_id: chatId },
800613
+ async () => {
800614
+ const res = await fetch(uploadUrl, {
800615
+ method: "POST",
800616
+ headers: {
800617
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
800618
+ "Content-Length": String(body.length)
800619
+ },
800620
+ body,
800621
+ signal: this.abortController?.signal
800622
+ });
800623
+ return await res.json();
800624
+ }
800625
+ );
800189
800626
  if (result.ok) {
800190
800627
  this.state.messagesSent++;
800191
800628
  const outboundMessageId = result.result?.message_id ?? null;
@@ -800494,18 +800931,21 @@ Content-Type: ${mimeForPath(pathOrFileId, field === "photo" ? "image" : "video")
800494
800931
  parts.push(Buffer.from(`--${boundary}--\r
800495
800932
  `));
800496
800933
  const body = Buffer.concat(parts);
800497
- const res = await fetch(
800498
- `https://api.telegram.org/bot${this.botToken}/${method}`,
800499
- {
800500
- method: "POST",
800501
- headers: {
800502
- "Content-Type": `multipart/form-data; boundary=${boundary}`,
800503
- "Content-Length": String(body.length)
800504
- },
800505
- body
800506
- }
800507
- );
800508
- return await res.json();
800934
+ return this.governedTelegramApiRequest(method, { ...fields }, async () => {
800935
+ const res = await fetch(
800936
+ `https://api.telegram.org/bot${this.botToken}/${method}`,
800937
+ {
800938
+ method: "POST",
800939
+ headers: {
800940
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
800941
+ "Content-Length": String(body.length)
800942
+ },
800943
+ body,
800944
+ signal: this.abortController?.signal
800945
+ }
800946
+ );
800947
+ return await res.json();
800948
+ });
800509
800949
  }
800510
800950
  /**
800511
800951
  * Bot API 10.0: send a message to another bot by username.
@@ -800816,15 +801256,22 @@ ${caption}\r
800816
801256
  `));
800817
801257
  const body = Buffer.concat(parts);
800818
801258
  const url = `https://api.telegram.org/bot${this.botToken}/${method}`;
800819
- const res = await fetch(url, {
800820
- method: "POST",
800821
- headers: {
800822
- "Content-Type": `multipart/form-data; boundary=${boundary}`,
800823
- "Content-Length": String(body.length)
800824
- },
800825
- body
800826
- });
800827
- const result = await res.json();
801259
+ const result = await this.governedTelegramApiRequest(
801260
+ method,
801261
+ { chat_id: chatId },
801262
+ async () => {
801263
+ const res = await fetch(url, {
801264
+ method: "POST",
801265
+ headers: {
801266
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
801267
+ "Content-Length": String(body.length)
801268
+ },
801269
+ body,
801270
+ signal: this.abortController?.signal
801271
+ });
801272
+ return await res.json();
801273
+ }
801274
+ );
800828
801275
  if (result.ok) {
800829
801276
  this.state.messagesSent++;
800830
801277
  return result.result?.message_id ?? null;
@@ -800898,6 +801345,7 @@ ${caption}\r
800898
801345
  }
800899
801346
  const msg = normalizeTelegramUpdate(update2);
800900
801347
  if (!msg) continue;
801348
+ this.telegramKnownChatTypes.set(String(msg.chatId), msg.chatType);
800901
801349
  this.persistTelegramRawMessage(update2, msg);
800902
801350
  this.state.messagesReceived++;
800903
801351
  const isAdmin = this.adminUserId ? String(msg.fromUserId) === this.adminUserId || msg.username === this.adminUserId : false;
@@ -800970,44 +801418,64 @@ ${caption}\r
800970
801418
  if (raw >= floor && raw <= 3e5) return raw;
800971
801419
  return null;
800972
801420
  }
800973
- /** Make a Telegram Bot API call with rate-limit retry */
800974
- async apiCall(method, body, _retryDepth = 0) {
801421
+ telegramApiChatId(body) {
801422
+ const value2 = body?.["chat_id"];
801423
+ return typeof value2 === "number" || typeof value2 === "string" && value2.length > 0 ? value2 : void 0;
801424
+ }
801425
+ telegramApiChatType(chatId) {
801426
+ return chatId === void 0 ? void 0 : this.telegramKnownChatTypes.get(String(chatId));
801427
+ }
801428
+ async governedTelegramApiRequest(method, body, execute, options2 = {}) {
801429
+ const chatId = this.telegramApiChatId(body);
801430
+ return this.telegramApiGovernor.run({
801431
+ method,
801432
+ body,
801433
+ chatId,
801434
+ chatType: this.telegramApiChatType(chatId),
801435
+ execute,
801436
+ signal: this.abortController?.signal,
801437
+ retryOnFlood: options2.retryOnFlood,
801438
+ floodWaitSeconds: telegramFloodWaitSeconds,
801439
+ coalesceKey: options2.coalesceKey,
801440
+ supersededValue: options2.supersededValue
801441
+ });
801442
+ }
801443
+ /** Make a Telegram Bot API call through the per-bot fair governor. */
801444
+ async apiCall(method, body) {
800975
801445
  const url = `https://api.telegram.org/bot${this.botToken}/${method}`;
800976
- const options2 = {
800977
- method: "POST",
800978
- headers: { "Content-Type": "application/json" }
800979
- };
800980
- if (body) {
800981
- options2.body = JSON.stringify(body);
800982
- }
800983
801446
  const isLongPoll = method === "getUpdates";
800984
- if (isLongPoll && this.abortController) {
800985
- const timeoutFn = AbortSignal.timeout;
800986
- const anyFn = AbortSignal.any;
800987
- const bodyTimeout = typeof body?.["timeout"] === "number" ? body["timeout"] : void 0;
800988
- const clientTimeoutMs = this.telegramLongPollClientTimeoutMs(bodyTimeout);
800989
- const signals = [
800990
- this.abortController.signal,
800991
- clientTimeoutMs && typeof timeoutFn === "function" ? timeoutFn(clientTimeoutMs) : void 0
800992
- ].filter(
800993
- (signal) => signal instanceof AbortSignal
800994
- );
800995
- options2.signal = typeof anyFn === "function" && signals.length > 1 ? anyFn(signals) : signals[0];
800996
- } else if (!isLongPoll) {
800997
- options2.signal = AbortSignal.timeout(3e4);
800998
- }
800999
- const res = await fetch(url, options2);
801000
- const data = await res.json();
801001
- if (data["error_code"] === 429 && data["parameters"]?.retry_after) {
801002
- const waitSec = Math.min(3, data["parameters"].retry_after);
801003
- const isLiveEdit = method === "editMessageText";
801004
- if (isLiveEdit || _retryDepth >= 1) {
801005
- return data;
801447
+ return this.governedTelegramApiRequest(
801448
+ method,
801449
+ body,
801450
+ async () => {
801451
+ const request = {
801452
+ method: "POST",
801453
+ headers: { "Content-Type": "application/json" }
801454
+ };
801455
+ if (body) request.body = JSON.stringify(body);
801456
+ const timeoutFn = AbortSignal.timeout;
801457
+ const anyFn = AbortSignal.any;
801458
+ const bodyTimeout = typeof body?.["timeout"] === "number" ? body["timeout"] : void 0;
801459
+ const clientTimeoutMs = isLongPoll ? this.telegramLongPollClientTimeoutMs(bodyTimeout) : 3e4;
801460
+ const signals = [
801461
+ this.abortController?.signal,
801462
+ clientTimeoutMs && typeof timeoutFn === "function" ? timeoutFn(clientTimeoutMs) : void 0
801463
+ ].filter(
801464
+ (signal) => signal instanceof AbortSignal
801465
+ );
801466
+ request.signal = typeof anyFn === "function" && signals.length > 1 ? anyFn(signals) : signals[0];
801467
+ const response = await fetch(url, request);
801468
+ return await response.json();
801469
+ },
801470
+ // Progressive edits are disposable. A flood response still updates the
801471
+ // chat cooldown, but replaying stale partial text would block inference
801472
+ // and could overwrite a newer final reply.
801473
+ {
801474
+ retryOnFlood: method !== "editMessageText",
801475
+ coalesceKey: method === "editMessageText" && body?.["message_id"] !== void 0 ? `editMessageText:${String(body["chat_id"])}:${String(body["message_id"])}` : void 0,
801476
+ supersededValue: method === "editMessageText" ? () => ({ ok: true, result: true, omitted_by_governor: true }) : void 0
801006
801477
  }
801007
- await new Promise((r2) => setTimeout(r2, waitSec * 1e3));
801008
- return this.apiCall(method, body, _retryDepth + 1);
801009
- }
801010
- return data;
801478
+ );
801011
801479
  }
801012
801480
  };
801013
801481
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.657",
3
+ "version": "1.0.659",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.657",
9
+ "version": "1.0.659",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.657",
3
+ "version": "1.0.659",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/library.js",