@craftsamo/telegram-tools 0.1.0 → 0.3.0

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/opencode.js CHANGED
@@ -25,6 +25,21 @@ var PROJECT = {
25
25
  toolsets: ["account", "chats", "sync", "files", "sends", "actions"],
26
26
  /** The toolsets on when a setting names none. */
27
27
  defaultToolsets: ["account", "chats", "sync", "sends"],
28
+ /** The package's own settings, which the tools themselves read: each snake_case name and its type, with bounds when it needs them. */
29
+ options: {
30
+ /** Where every account's state lives. */
31
+ state_dir: "path",
32
+ /** Where received files are saved. */
33
+ download_dir: "path",
34
+ /** The largest received file a call may download, in megabytes. */
35
+ download_max_mb: { type: "number", integer: true, min: 1, max: 2e3 },
36
+ /** The directories files may be sent from, besides the working directory. */
37
+ send_dirs: "paths",
38
+ /** Whether messages that disappear are kept; read by the sync service's commands. */
39
+ keep_disappearing: "boolean",
40
+ /** Whether channels are listed and their messages received; read by the sync service's commands. */
41
+ channels: "boolean"
42
+ },
28
43
  /** The environment variables the tools read besides the settings, which Codex forwards to the server after them. */
29
44
  toolVariables: []
30
45
  };
@@ -5099,7 +5114,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5099
5114
  var NONE = 0;
5100
5115
  var ASSUMED = 1;
5101
5116
  var PROVEN = 2;
5102
- function isRecursive(inst, stack, resolve8) {
5117
+ function isRecursive(inst, stack, resolve6) {
5103
5118
  const cached2 = recursive.get(inst);
5104
5119
  if (cached2 !== void 0)
5105
5120
  return cached2 ? PROVEN : NONE;
@@ -5109,7 +5124,7 @@ function isRecursive(inst, stack, resolve8) {
5109
5124
  let result = NONE;
5110
5125
  const check2 = (child) => {
5111
5126
  if (result !== PROVEN && child?._zod) {
5112
- const answer = isRecursive(child, stack, resolve8);
5127
+ const answer = isRecursive(child, stack, resolve6);
5113
5128
  if (answer > result)
5114
5129
  result = answer;
5115
5130
  }
@@ -5120,7 +5135,7 @@ function isRecursive(inst, stack, resolve8) {
5120
5135
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5121
5136
  if (spread && !desc.enumerable)
5122
5137
  continue;
5123
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve8) : NONE;
5138
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve6) : NONE;
5124
5139
  if (child > answer)
5125
5140
  answer = child;
5126
5141
  }
@@ -5184,7 +5199,7 @@ function isRecursive(inst, stack, resolve8) {
5184
5199
  break;
5185
5200
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5186
5201
  case "lazy": {
5187
- const inner = def._cachedInner ?? (resolve8 ? inst._zod.innerType : void 0);
5202
+ const inner = def._cachedInner ?? (resolve6 ? inst._zod.innerType : void 0);
5188
5203
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
5189
5204
  break;
5190
5205
  }
@@ -9382,8 +9397,8 @@ function ko_default() {
9382
9397
  }
9383
9398
 
9384
9399
  // node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/locales/lt.js
9385
- var capitalizeFirstCharacter = (text) => {
9386
- return text.charAt(0).toUpperCase() + text.slice(1);
9400
+ var capitalizeFirstCharacter = (text2) => {
9401
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
9387
9402
  };
9388
9403
  function getUnitTypeFromNumber(number4) {
9389
9404
  const abs = Math.abs(number4);
@@ -19788,8 +19803,8 @@ function quoteBadLabel(value) {
19788
19803
  const truncated = value.length > MAX_BAD_LABEL_PREVIEW ? `${value.slice(0, MAX_BAD_LABEL_PREVIEW)}\u2026` : value;
19789
19804
  return JSON.stringify(truncated);
19790
19805
  }
19791
- function isLabel(text) {
19792
- return typeof text === "string" && LABEL_PATTERN.test(text);
19806
+ function isLabel(text2) {
19807
+ return typeof text2 === "string" && LABEL_PATTERN.test(text2);
19793
19808
  }
19794
19809
  function checkLabel(label) {
19795
19810
  if (!isLabel(label)) {
@@ -20008,6 +20023,7 @@ function lockHolder(path) {
20008
20023
  }
20009
20024
 
20010
20025
  // src/core/time.ts
20026
+ var MS_PER_S = 1e3;
20011
20027
  var MS_PER_MINUTE = 6e4;
20012
20028
  var MINUTES_PER_HOUR = 60;
20013
20029
  var localOffsetMinutes = (ms) => -new Date(ms).getTimezoneOffset();
@@ -20027,10 +20043,10 @@ var MILLISECOND_DIGITS = 3;
20027
20043
  var MIN_YEAR = 1e3;
20028
20044
  var MAX_HOUR = 23;
20029
20045
  var MAX_MINUTE = 59;
20030
- function badTime(text, name) {
20046
+ function badTime(text2, name) {
20031
20047
  return new ToolError(
20032
20048
  "invalid_argument",
20033
- `${name} is not a time: ${JSON.stringify(text)}. Use RFC 3339 with an offset (2026-11-07T14:03:12+09:00), or a date or time without one (2026-11-07, 2026-11-07T14:03), which reads as local time.`
20049
+ `${name} is not a time: ${JSON.stringify(text2)}. Use RFC 3339 with an offset (2026-11-07T14:03:12+09:00), or a date or time without one (2026-11-07, 2026-11-07T14:03), which reads as local time.`
20034
20050
  );
20035
20051
  }
20036
20052
  function isCalendarDay(year, month, day) {
@@ -20038,21 +20054,21 @@ function isCalendarDay(year, month, day) {
20038
20054
  date5.setUTCFullYear(year, month - 1, day);
20039
20055
  return date5.getUTCFullYear() === year && date5.getUTCMonth() === month - 1 && date5.getUTCDate() === day;
20040
20056
  }
20041
- function parseTimeArgument(text, name) {
20042
- const match = TIME_PATTERN.exec(text.trim());
20043
- if (!match) throw badTime(text, name);
20057
+ function parseTimeArgument(text2, name) {
20058
+ const match = TIME_PATTERN.exec(text2.trim());
20059
+ if (!match) throw badTime(text2, name);
20044
20060
  const [, year, month, day, hour = "0", minute = "0", second = "0", fraction = "", zulu, sign, offsetHour, offsetMin] = match;
20045
20061
  const [y, mon, d, h, mi, s] = [year, month, day, hour, minute, second].map(Number);
20046
20062
  const ms = Number(fraction.padEnd(MILLISECOND_DIGITS, "0").slice(0, MILLISECOND_DIGITS));
20047
20063
  if (y < MIN_YEAR || !isCalendarDay(y, mon, d) || h > MAX_HOUR || mi > MAX_MINUTE || s > MAX_MINUTE) {
20048
- throw badTime(text, name);
20064
+ throw badTime(text2, name);
20049
20065
  }
20050
20066
  if (sign === void 0 && zulu === void 0) return new Date(y, mon - 1, d, h, mi, s, ms).getTime();
20051
20067
  const wall = Date.UTC(y, mon - 1, d, h, mi, s, ms);
20052
20068
  if (zulu) return wall;
20053
20069
  const offsetH = Number(offsetHour);
20054
20070
  const offsetM = Number(offsetMin);
20055
- if (offsetH > MAX_HOUR || offsetM > MAX_MINUTE) throw badTime(text, name);
20071
+ if (offsetH > MAX_HOUR || offsetM > MAX_MINUTE) throw badTime(text2, name);
20056
20072
  const offset = (offsetH * MINUTES_PER_HOUR + offsetM) * MS_PER_MINUTE;
20057
20073
  return sign === "-" ? wall + offset : wall - offset;
20058
20074
  }
@@ -20387,23 +20403,23 @@ function encodeCursor(value) {
20387
20403
  function notACursor(name) {
20388
20404
  return new ToolError("invalid_argument", `${name} is not a cursor this tool returned.`);
20389
20405
  }
20390
- function decodeCursor(text, name) {
20391
- const bytes = Buffer3.from(text, "base64url");
20392
- if (bytes.toString("base64url") !== text) throw notACursor(name);
20406
+ function decodeCursor(text2, name) {
20407
+ const bytes = Buffer3.from(text2, "base64url");
20408
+ if (bytes.toString("base64url") !== text2) throw notACursor(name);
20393
20409
  try {
20394
20410
  return JSON.parse(bytes.toString("utf8"));
20395
20411
  } catch {
20396
20412
  throw notACursor(name);
20397
20413
  }
20398
20414
  }
20399
- function readCursor(text, name, schema) {
20400
- const parsed = schema.safeParse(decodeCursor(text, name));
20415
+ function readCursor(text2, name, schema) {
20416
+ const parsed = schema.safeParse(decodeCursor(text2, name));
20401
20417
  if (!parsed.success) throw notACursor(name);
20402
20418
  return parsed.data;
20403
20419
  }
20404
20420
  var HIGH_SURROGATE = /[\uD800-\uDBFF]$/;
20405
- function cutText(text, keep) {
20406
- return text.length <= keep ? text : text.slice(0, keep).replace(HIGH_SURROGATE, "");
20421
+ function cutText(text2, keep) {
20422
+ return text2.length <= keep ? text2 : text2.slice(0, keep).replace(HIGH_SURROGATE, "");
20407
20423
  }
20408
20424
  function jsonLength(value) {
20409
20425
  return JSON.stringify(value).length;
@@ -20493,15 +20509,15 @@ var ERROR_CODES = {
20493
20509
  function isErrorCode(value) {
20494
20510
  return typeof value === "string" && Object.hasOwn(ERROR_CODES, value);
20495
20511
  }
20496
- function maskPhoneNumbers(text) {
20497
- return text.replace(PHONE_NUMBER, "[number]");
20512
+ function maskPhoneNumbers(text2) {
20513
+ return text2.replace(PHONE_NUMBER, "[number]");
20498
20514
  }
20499
20515
  function cut(message) {
20500
20516
  return maskPhoneNumbers(message).trim().slice(0, MAX_DETAIL_LENGTH);
20501
20517
  }
20502
20518
  function detail(message) {
20503
- const text = cut(message);
20504
- return text ? `: ${text}` : "";
20519
+ const text2 = cut(message);
20520
+ return text2 ? `: ${text2}` : "";
20505
20521
  }
20506
20522
  function afterWriteError(method, effect, reason) {
20507
20523
  return effect === "write" ? new ToolError("uncertain", `${method} may have taken effect (${reason}). ${UNCERTAIN_ADVICE}`) : new ToolError("upstream_error", `${method} failed (${reason}).`);
@@ -20622,7 +20638,7 @@ function abortError(signal) {
20622
20638
  }
20623
20639
  function exec(argv, options) {
20624
20640
  const { signal } = options;
20625
- return new Promise((resolve8, reject) => {
20641
+ return new Promise((resolve6, reject) => {
20626
20642
  if (signal?.aborted) {
20627
20643
  reject(abortError(signal));
20628
20644
  return;
@@ -20681,7 +20697,7 @@ function exec(argv, options) {
20681
20697
  }
20682
20698
  const finish = () => settle2(() => {
20683
20699
  const { code, killedBy } = exitedWith ?? { code: null, killedBy: null };
20684
- resolve8({
20700
+ resolve6({
20685
20701
  code: code ?? -1,
20686
20702
  stdout,
20687
20703
  stderr,
@@ -20775,19 +20791,19 @@ var ServiceConnection = class _ServiceConnection {
20775
20791
  socket.on("error", (error62) => {
20776
20792
  this.endReason ??= `the connection to the service broke${error62.code ? ` (${error62.code})` : ""}`;
20777
20793
  });
20778
- this.closed = new Promise((resolve8) => {
20794
+ this.closed = new Promise((resolve6) => {
20779
20795
  socket.once("close", () => {
20780
20796
  this.endReason ??= "the service closed the connection";
20781
20797
  const lost = { lost: this.endReason };
20782
20798
  for (const settle2 of [...this.pending.values()]) settle2(lost);
20783
- resolve8();
20799
+ resolve6();
20784
20800
  });
20785
20801
  });
20786
20802
  }
20787
20803
  /** Connects to the service's socket; rejects as `upstream_error` when nothing is listening there. */
20788
20804
  static connect(socketPath, options = {}) {
20789
20805
  const { timeoutMs = DEFAULT_CONNECT_TIMEOUT_MS, signal, maxLineLength = DEFAULT_MAX_LINE_LENGTH } = options;
20790
- return new Promise((resolve8, reject) => {
20806
+ return new Promise((resolve6, reject) => {
20791
20807
  if (signal?.aborted) {
20792
20808
  reject(abortError(signal));
20793
20809
  return;
@@ -20809,7 +20825,7 @@ var ServiceConnection = class _ServiceConnection {
20809
20825
  };
20810
20826
  const onConnect = () => {
20811
20827
  detach();
20812
- resolve8(new _ServiceConnection(socket, maxLineLength));
20828
+ resolve6(new _ServiceConnection(socket, maxLineLength));
20813
20829
  };
20814
20830
  const onFailure = () => stop(new ToolError("upstream_error", NOT_RUNNING_MESSAGE));
20815
20831
  const onAbort = () => stop(abortError(signal));
@@ -20832,7 +20848,7 @@ var ServiceConnection = class _ServiceConnection {
20832
20848
  */
20833
20849
  call(method, params, options) {
20834
20850
  const { effect, timeoutMs = DEFAULT_CALL_TIMEOUT_MS, signal } = options;
20835
- return new Promise((resolve8, reject) => {
20851
+ return new Promise((resolve6, reject) => {
20836
20852
  if (signal?.aborted) {
20837
20853
  reject(abortError(signal));
20838
20854
  return;
@@ -20861,7 +20877,7 @@ var ServiceConnection = class _ServiceConnection {
20861
20877
  clearTimeout(timer);
20862
20878
  signal?.removeEventListener("abort", onAbort);
20863
20879
  try {
20864
- resolve8(produce());
20880
+ resolve6(produce());
20865
20881
  } catch (error62) {
20866
20882
  reject(error62);
20867
20883
  }
@@ -20934,7 +20950,6 @@ async function callService(socketPath, method, params, options) {
20934
20950
  // src/core/chat/live.ts
20935
20951
  var REFRESH_TIMEOUT_MS = 12e4;
20936
20952
  var FETCH_TIMEOUT_MS = 18e4;
20937
- var MS_PER_S = 1e3;
20938
20953
  function requestHistory(socket, params, signal) {
20939
20954
  return callService(socket, SERVICE_REQUESTS.history, params, { effect: "read", signal });
20940
20955
  }
@@ -21062,113 +21077,6 @@ var messageEntry = external_exports.object({
21062
21077
  private_note: external_exports.string().optional()
21063
21078
  });
21064
21079
 
21065
- // src/core/chat/schema.ts
21066
- var METHODS = ["list", "view", "topics", "search", "around", "fetch"];
21067
- var GENERAL_TOPIC2 = 1;
21068
- var LIMITS = {
21069
- list: { default: 30, max: 200 },
21070
- view: { default: 50, max: 300 },
21071
- topics: { default: 30, max: TOPICS_LIMIT },
21072
- search: { default: 30, max: 200 }
21073
- };
21074
- var AROUND_COUNT = { default: 5, max: AROUND_LIMIT };
21075
- var FETCH_PAGES = { default: 1, max: FETCH_PAGE_LIMIT };
21076
- var accountArgument = external_exports.string().optional().describe("The account label; may be left out when only one account is logged in.");
21077
- var input3 = external_exports.object({
21078
- method: external_exports.enum(METHODS).describe(
21079
- "`list` finds chats; `view` reads the newest messages of one chat between two times, oldest first; `topics` lists the topics of a forum chat; `search` finds messages in the mirror by words; `around` reads the messages around one; `fetch` adds older history of a mirrored chat to the mirror."
21080
- ),
21081
- account: accountArgument,
21082
- query: external_exports.string().optional().describe(
21083
- "list: only chats whose name or username holds this text; a leading @ is ignored. search (required): the words to look for, all of which a message must hold in its text, caption or file name, in any order. Both ignore case and letter width."
21084
- ),
21085
- unread: external_exports.boolean().optional().describe("list: only chats with unread messages."),
21086
- last: external_exports.boolean().optional().describe("list: add each chat's newest mirrored message as `last_message`."),
21087
- refresh: external_exports.boolean().optional().describe(
21088
- "list: first take the chat list from Telegram (needs the sync service), then read it, and report when in `refreshed_at`."
21089
- ),
21090
- chat: external_exports.string().optional().describe(
21091
- "view, topics, around, fetch (required) and search (optional, to search one chat): the chat id, from `list`, such as 123456789 or -1001234567890. A name, username or phone number is not a chat."
21092
- ),
21093
- topic: external_exports.number().int().positive().optional().describe(
21094
- `view, around: only messages of this topic of a forum chat (ids from \`topics\`); the General topic is ${GENERAL_TOPIC2}. Refused for a chat without topics.`
21095
- ),
21096
- after: external_exports.string().optional().describe(
21097
- "view, search: only messages after this time (exclusive): RFC 3339 with an offset, or a date or time without one, read as local time, such as 2026-11-07 or 2026-11-07T14:03."
21098
- ),
21099
- before: external_exports.string().optional().describe("view, search: only messages before this time (exclusive); the same forms as `after`."),
21100
- limit: external_exports.number().int().optional().describe(
21101
- `How many to return. list: default ${LIMITS.list.default}, at most ${LIMITS.list.max}. view: default ${LIMITS.view.default}, at most ${LIMITS.view.max} (${HISTORY_LIMIT} for a chat read live). topics: default ${LIMITS.topics.default}, at most ${LIMITS.topics.max}. search: default ${LIMITS.search.default}, at most ${LIMITS.search.max}. A larger value is lowered to the most; a result may hold fewer to stay a size a model can read.`
21102
- ),
21103
- cursor: external_exports.string().optional().describe("The `next` of the previous result, to continue where it stopped."),
21104
- id: external_exports.number().int().positive().optional().describe("around (required): the id of the message to read around."),
21105
- before_count: external_exports.number().int().optional().describe(
21106
- `around: how many older messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
21107
- ),
21108
- after_count: external_exports.number().int().optional().describe(
21109
- `around: how many newer messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
21110
- ),
21111
- pages: external_exports.number().int().optional().describe(
21112
- `fetch: how many pages of ${FETCH_PAGE_SIZE} older messages to add, default ${FETCH_PAGES.default}, at most ${FETCH_PAGES.max}.`
21113
- )
21114
- });
21115
- var nameCut = external_exports.literal(true).optional().describe("Set when the name was longer than the most a result carries and was cut.");
21116
- var chatEntry2 = external_exports.object({
21117
- id: external_exports.string().describe("The chat id: Telegram's signed id as text, such as `123456789` or `-1001234567890`."),
21118
- kind: external_exports.enum(CHAT_KINDS).describe("`self` is Saved Messages."),
21119
- name: external_exports.string().nullable().describe("The person's or chat's name; null when none is known."),
21120
- name_cut: nameCut,
21121
- username: external_exports.string().optional(),
21122
- mirrored: external_exports.boolean().describe("Whether the mirror keeps this chat's messages."),
21123
- unread: external_exports.number().int().describe("Incoming messages not read yet."),
21124
- last_activity_at: localTimeSchema.nullable().describe("The latest message's time, in local time with offset; null when none is known."),
21125
- forum: external_exports.literal(true).optional().describe("Set for a supergroup with topics."),
21126
- archived: external_exports.literal(true).optional(),
21127
- left: external_exports.literal(true).optional().describe("Set for a group or channel the user has left or been removed from."),
21128
- can_send: external_exports.literal(false).optional().describe("Set when the user is known not to be able to write here."),
21129
- auto_delete_seconds: external_exports.number().int().optional().describe("The chat's auto-delete period, when on."),
21130
- last_message: messageEntry.nullable().optional().describe("With `last`: the newest mirrored message of the chat, or null when none is mirrored.")
21131
- });
21132
- var topicEntry = external_exports.object({
21133
- id: external_exports.number().int().describe(`The topic id, to pass as \`topic\` to \`view\`; ${GENERAL_TOPIC2} is the General topic.`),
21134
- title: external_exports.string().nullable().describe("The topic's title; null when none is known."),
21135
- closed: external_exports.literal(true).optional().describe("Set for a topic that is closed."),
21136
- pinned: external_exports.literal(true).optional().describe("Set for a topic that is pinned."),
21137
- unread: external_exports.number().int().describe("Messages in it not read yet."),
21138
- last_activity_at: localTimeSchema.nullable().describe("The newest message's time, in local time with offset; null when none is known.")
21139
- });
21140
- var output3 = external_exports.object({
21141
- chats: external_exports.array(chatEntry2).optional().describe("list: the chats, newest activity first."),
21142
- refreshed_at: localTimeSchema.optional().describe("list with `refresh`: when the chat list was taken from Telegram, in local time with offset."),
21143
- chat: chatEntry2.optional().describe("view, topics, around, fetch: the chat read."),
21144
- messages: external_exports.array(messageEntry).optional().describe("view, around: the messages, oldest first. search: the hits, newest first."),
21145
- live: external_exports.literal(true).optional().describe(
21146
- "view, around: set when the chat is not mirrored, so the messages were read from Telegram and stored nowhere."
21147
- ),
21148
- topics: external_exports.array(topicEntry).optional().describe("topics: the topics, most recently active first."),
21149
- anchor: external_exports.number().int().optional().describe("around: the id of the message read around, among `messages`."),
21150
- trimmed_before: external_exports.number().int().optional().describe(
21151
- "around: how many older messages were left out, farthest first, to keep the result a size that can be read."
21152
- ),
21153
- trimmed_after: external_exports.number().int().optional().describe(
21154
- "around: how many newer messages were left out, farthest first, to keep the result a size that can be read."
21155
- ),
21156
- scanned_to: localTimeSchema.optional().describe(
21157
- "search: set when the search stopped before the end of the history it covers: the time of the oldest message looked at, in local time with offset. `next` continues from there."
21158
- ),
21159
- scope: external_exports.string().optional().describe("search: what the search covers and what it does not."),
21160
- fetched: external_exports.object({
21161
- added: external_exports.number().int().describe("How many messages the mirror gained."),
21162
- pages: external_exports.number().int().describe("How many pages were read."),
21163
- complete: external_exports.boolean().describe("Whether Telegram has nothing older for this chat."),
21164
- oldest_at: localTimeSchema.nullable().describe(
21165
- "The time of the oldest message the mirror now holds of this chat, in local time with offset; null when none."
21166
- )
21167
- }).optional().describe("fetch: what the history fetch stored."),
21168
- next: external_exports.string().optional().describe("Pass back as `cursor` to continue; absent when there is no more."),
21169
- note: external_exports.string().describe("Says that what the result carries is data written by other people.")
21170
- });
21171
-
21172
21080
  // src/core/chat/message-rows.ts
21173
21081
  var MESSAGE_COLUMNS = "chat, id, topic, ts, from_me, sender, sender_name, kind, body, entities, media, action, reply, forward, grouped, reactions, edited, expires, view_once, self_destruct";
21174
21082
  function liveMessages(now) {
@@ -21176,7 +21084,7 @@ function liveMessages(now) {
21176
21084
  }
21177
21085
  function inTopic(topic) {
21178
21086
  return {
21179
- condition: topic === GENERAL_TOPIC2 ? "(topic IS NULL OR topic = ?)" : "topic = ?",
21087
+ condition: topic === GENERAL_TOPIC ? "(topic IS NULL OR topic = ?)" : "topic = ?",
21180
21088
  params: [topic]
21181
21089
  };
21182
21090
  }
@@ -21289,10 +21197,10 @@ function replyTo(parsed, related) {
21289
21197
  if (reply.chat !== void 0) entry2.chat = reply.chat;
21290
21198
  if (reply.topic !== void 0) entry2.topic = reply.topic;
21291
21199
  if (reply.quote !== void 0) entry2.quote = reply.quote;
21292
- const text = repliedText(parsed, related)?.text;
21293
- if (text !== void 0) {
21294
- entry2.text = cutText(text, REPLY_TEXT_LENGTH);
21295
- if (entry2.text.length < text.length) entry2.text_cut = true;
21200
+ const text2 = repliedText(parsed, related)?.text;
21201
+ if (text2 !== void 0) {
21202
+ entry2.text = cutText(text2, REPLY_TEXT_LENGTH);
21203
+ if (entry2.text.length < text2.length) entry2.text_cut = true;
21296
21204
  }
21297
21205
  return entry2;
21298
21206
  }
@@ -21361,25 +21269,25 @@ function texts(entry2) {
21361
21269
  ];
21362
21270
  }
21363
21271
  function messageTextLength(entry2) {
21364
- return texts(entry2).reduce((total, text) => total + text.length, 0);
21272
+ return texts(entry2).reduce((total, text2) => total + text2.length, 0);
21365
21273
  }
21366
21274
  function cutMessage(entry2, keep) {
21367
21275
  let room = keep;
21368
21276
  let cut3 = false;
21369
- const take = (text) => {
21370
- const kept = cutText(text, room);
21371
- room = Math.max(0, room - text.length);
21372
- if (kept.length < text.length) cut3 = true;
21277
+ const take = (text2) => {
21278
+ const kept = cutText(text2, room);
21279
+ room = Math.max(0, room - text2.length);
21280
+ if (kept.length < text2.length) cut3 = true;
21373
21281
  return kept;
21374
21282
  };
21375
21283
  const cutEntry = { ...entry2 };
21376
21284
  if (entry2.text !== void 0) cutEntry.text = take(entry2.text);
21377
21285
  if (entry2.reply_to) {
21378
- const { quote, text } = entry2.reply_to;
21286
+ const { quote, text: text2 } = entry2.reply_to;
21379
21287
  cutEntry.reply_to = {
21380
21288
  ...entry2.reply_to,
21381
21289
  ...quote === void 0 ? {} : { quote: take(quote) },
21382
- ...text === void 0 ? {} : { text: take(text) }
21290
+ ...text2 === void 0 ? {} : { text: take(text2) }
21383
21291
  };
21384
21292
  }
21385
21293
  if (entry2.earlier_versions)
@@ -21401,6 +21309,112 @@ var cuttableMessage = {
21401
21309
  drop: dropMetadata
21402
21310
  };
21403
21311
 
21312
+ // src/core/chat/schema.ts
21313
+ var METHODS = ["list", "view", "topics", "search", "around", "fetch"];
21314
+ var LIMITS = {
21315
+ list: { default: 30, max: 200 },
21316
+ view: { default: 50, max: 300 },
21317
+ topics: { default: 30, max: TOPICS_LIMIT },
21318
+ search: { default: 30, max: 200 }
21319
+ };
21320
+ var AROUND_COUNT = { default: 5, max: AROUND_LIMIT };
21321
+ var FETCH_PAGES = { default: 1, max: FETCH_PAGE_LIMIT };
21322
+ var accountArgument = external_exports.string().optional().describe("The account label; may be left out when only one account is logged in.");
21323
+ var input3 = external_exports.object({
21324
+ method: external_exports.enum(METHODS).describe(
21325
+ "`list` finds chats; `view` reads the newest messages of one chat between two times, oldest first; `topics` lists the topics of a forum chat; `search` finds messages in the mirror by words; `around` reads the messages around one; `fetch` adds older history of a mirrored chat to the mirror."
21326
+ ),
21327
+ account: accountArgument,
21328
+ query: external_exports.string().optional().describe(
21329
+ "list: only chats whose name or username holds this text; a leading @ is ignored. search (required): the words to look for, all of which a message must hold in its text, caption or file name, in any order. Both ignore case and letter width."
21330
+ ),
21331
+ unread: external_exports.boolean().optional().describe("list: only chats with unread messages."),
21332
+ last: external_exports.boolean().optional().describe("list: add each chat's newest mirrored message as `last_message`."),
21333
+ refresh: external_exports.boolean().optional().describe(
21334
+ "list: first take the chat list from Telegram (needs the sync service), then read it, and report when in `refreshed_at`."
21335
+ ),
21336
+ chat: external_exports.string().optional().describe(
21337
+ "view, topics, around, fetch (required) and search (optional, to search one chat): the chat id, from `list`, such as 123456789 or -1001234567890. A name, username or phone number is not a chat."
21338
+ ),
21339
+ topic: external_exports.number().int().positive().optional().describe(
21340
+ `view, around: only messages of this topic of a forum chat (ids from \`topics\`); the General topic is ${GENERAL_TOPIC}. Refused for a chat without topics.`
21341
+ ),
21342
+ after: external_exports.string().optional().describe(
21343
+ "view, search: only messages after this time (exclusive): RFC 3339 with an offset, or a date or time without one, read as local time, such as 2026-11-07 or 2026-11-07T14:03."
21344
+ ),
21345
+ before: external_exports.string().optional().describe("view, search: only messages before this time (exclusive); the same forms as `after`."),
21346
+ limit: external_exports.number().int().optional().describe(
21347
+ `How many to return. list: default ${LIMITS.list.default}, at most ${LIMITS.list.max}. view: default ${LIMITS.view.default}, at most ${LIMITS.view.max} (${HISTORY_LIMIT} for a chat read live). topics: default ${LIMITS.topics.default}, at most ${LIMITS.topics.max}. search: default ${LIMITS.search.default}, at most ${LIMITS.search.max}. A larger value is lowered to the most; a result may hold fewer to stay a size a model can read.`
21348
+ ),
21349
+ cursor: external_exports.string().optional().describe("The `next` of the previous result, to continue where it stopped."),
21350
+ id: external_exports.number().int().positive().optional().describe("around (required): the id of the message to read around."),
21351
+ before_count: external_exports.number().int().optional().describe(
21352
+ `around: how many older messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
21353
+ ),
21354
+ after_count: external_exports.number().int().optional().describe(
21355
+ `around: how many newer messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
21356
+ ),
21357
+ pages: external_exports.number().int().optional().describe(
21358
+ `fetch: how many pages of ${FETCH_PAGE_SIZE} older messages to add, default ${FETCH_PAGES.default}, at most ${FETCH_PAGES.max}.`
21359
+ )
21360
+ });
21361
+ var nameCut = external_exports.literal(true).optional().describe("Set when the name was longer than the most a result carries and was cut.");
21362
+ var chatEntry2 = external_exports.object({
21363
+ id: external_exports.string().describe("The chat id: Telegram's signed id as text, such as `123456789` or `-1001234567890`."),
21364
+ kind: external_exports.enum(CHAT_KINDS).describe("`self` is Saved Messages."),
21365
+ name: external_exports.string().nullable().describe("The person's or chat's name; null when none is known."),
21366
+ name_cut: nameCut,
21367
+ username: external_exports.string().optional(),
21368
+ mirrored: external_exports.boolean().describe("Whether the mirror keeps this chat's messages."),
21369
+ unread: external_exports.number().int().describe("Incoming messages not read yet."),
21370
+ last_activity_at: localTimeSchema.nullable().describe("The latest message's time, in local time with offset; null when none is known."),
21371
+ forum: external_exports.literal(true).optional().describe("Set for a supergroup with topics."),
21372
+ archived: external_exports.literal(true).optional(),
21373
+ left: external_exports.literal(true).optional().describe("Set for a group or channel the user has left or been removed from."),
21374
+ can_send: external_exports.literal(false).optional().describe("Set when the user is known not to be able to write here."),
21375
+ auto_delete_seconds: external_exports.number().int().optional().describe("The chat's auto-delete period, when on."),
21376
+ last_message: messageEntry.nullable().optional().describe("With `last`: the newest mirrored message of the chat, or null when none is mirrored.")
21377
+ });
21378
+ var topicEntry = external_exports.object({
21379
+ id: external_exports.number().int().describe(`The topic id, to pass as \`topic\` to \`view\`; ${GENERAL_TOPIC} is the General topic.`),
21380
+ title: external_exports.string().nullable().describe("The topic's title; null when none is known."),
21381
+ closed: external_exports.literal(true).optional().describe("Set for a topic that is closed."),
21382
+ pinned: external_exports.literal(true).optional().describe("Set for a topic that is pinned."),
21383
+ unread: external_exports.number().int().describe("Messages in it not read yet."),
21384
+ last_activity_at: localTimeSchema.nullable().describe("The newest message's time, in local time with offset; null when none is known.")
21385
+ });
21386
+ var output3 = external_exports.object({
21387
+ chats: external_exports.array(chatEntry2).optional().describe("list: the chats, newest activity first."),
21388
+ refreshed_at: localTimeSchema.optional().describe("list with `refresh`: when the chat list was taken from Telegram, in local time with offset."),
21389
+ chat: chatEntry2.optional().describe("view, topics, around, fetch: the chat read."),
21390
+ messages: external_exports.array(messageEntry).optional().describe("view, around: the messages, oldest first. search: the hits, newest first."),
21391
+ live: external_exports.literal(true).optional().describe(
21392
+ "view, around: set when the chat is not mirrored, so the messages were read from Telegram and stored nowhere."
21393
+ ),
21394
+ topics: external_exports.array(topicEntry).optional().describe("topics: the topics, most recently active first."),
21395
+ anchor: external_exports.number().int().optional().describe("around: the id of the message read around, among `messages`."),
21396
+ trimmed_before: external_exports.number().int().optional().describe(
21397
+ "around: how many older messages were left out, farthest first, to keep the result a size that can be read."
21398
+ ),
21399
+ trimmed_after: external_exports.number().int().optional().describe(
21400
+ "around: how many newer messages were left out, farthest first, to keep the result a size that can be read."
21401
+ ),
21402
+ scanned_to: localTimeSchema.optional().describe(
21403
+ "search: set when the search stopped before the end of the history it covers: the time of the oldest message looked at, in local time with offset. `next` continues from there."
21404
+ ),
21405
+ scope: external_exports.string().optional().describe("search: what the search covers and what it does not."),
21406
+ fetched: external_exports.object({
21407
+ added: external_exports.number().int().describe("How many messages the mirror gained."),
21408
+ pages: external_exports.number().int().describe("How many pages were read."),
21409
+ complete: external_exports.boolean().describe("Whether Telegram has nothing older for this chat."),
21410
+ oldest_at: localTimeSchema.nullable().describe(
21411
+ "The time of the oldest message the mirror now holds of this chat, in local time with offset; null when none."
21412
+ )
21413
+ }).optional().describe("fetch: what the history fetch stored."),
21414
+ next: external_exports.string().optional().describe("Pass back as `cursor` to continue; absent when there is no more."),
21415
+ note: external_exports.string().describe("Says that what the result carries is data written by other people.")
21416
+ });
21417
+
21404
21418
  // src/core/chat/around.ts
21405
21419
  var TRIMMED_RESERVE = 100;
21406
21420
  var NO_MESSAGE = "No message with this id in this chat.";
@@ -21522,8 +21536,8 @@ function fetchResult(read, stored) {
21522
21536
  }
21523
21537
 
21524
21538
  // src/core/fold.ts
21525
- function foldCase(text) {
21526
- return text.normalize("NFKC").toUpperCase().toLowerCase().replace(/ς/g, "\u03C3");
21539
+ function foldCase(text2) {
21540
+ return text2.normalize("NFKC").toUpperCase().toLowerCase().replace(/ς/g, "\u03C3");
21527
21541
  }
21528
21542
 
21529
21543
  // src/core/chat/filter.ts
@@ -21603,7 +21617,7 @@ function listChats(mirror, args, now) {
21603
21617
  return result;
21604
21618
  }
21605
21619
 
21606
- // src/core/chat/open.ts
21620
+ // src/core/open.ts
21607
21621
  async function pickPaths(ctx, account2) {
21608
21622
  if (!ctx.stateDir) throw new ToolError("internal_error", "The entry point did not supply a state directory.");
21609
21623
  return pickLabel(ctx.stateDir, account2);
@@ -21662,8 +21676,8 @@ function foldedFileName(media) {
21662
21676
  return isRecord(info) && typeof info.name === "string" ? foldCase(info.name) : void 0;
21663
21677
  }
21664
21678
  function matchesAllWords(row, words2) {
21665
- const text = foldCase(row.body ?? "");
21666
- const unmatched = words2.filter((word) => !text.includes(word));
21679
+ const text2 = foldCase(row.body ?? "");
21680
+ const unmatched = words2.filter((word) => !text2.includes(word));
21667
21681
  if (unmatched.length === 0) return true;
21668
21682
  const name = row.media === null ? void 0 : foldedFileName(row.media);
21669
21683
  return name !== void 0 && unmatched.every((word) => name.includes(word));
@@ -22023,6 +22037,56 @@ var CHAT_FACT_TRANSLATIONS = {
22023
22037
  }
22024
22038
  };
22025
22039
 
22040
+ // src/core/chat/media-facts.ts
22041
+ var MEDIA_KIND_WORDS = {
22042
+ photo: "Photo",
22043
+ document: "Document",
22044
+ audio: "Audio",
22045
+ voice: "Voice message",
22046
+ video: "Video",
22047
+ sticker: "Sticker",
22048
+ dice: "Dice",
22049
+ contact: "Contact",
22050
+ venue: "Venue",
22051
+ geo: "Location",
22052
+ geo_live: "Live location",
22053
+ game: "Game",
22054
+ invoice: "Invoice",
22055
+ webpage: "Link preview",
22056
+ poll: "Poll",
22057
+ todo: "Checklist",
22058
+ story: "Story",
22059
+ paid: "Paid media",
22060
+ unsupported: "Unsupported media"
22061
+ };
22062
+ function mediaKindValue(type) {
22063
+ return Object.hasOwn(MEDIA_KIND_WORDS, type) ? { text: MEDIA_KIND_WORDS[type] } : type;
22064
+ }
22065
+ var MEDIA_FACT_TRANSLATIONS = {
22066
+ ja: {
22067
+ Media: "\u30E1\u30C7\u30A3\u30A2",
22068
+ Photo: "\u5199\u771F",
22069
+ Document: "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
22070
+ Audio: "\u97F3\u58F0",
22071
+ "Voice message": "\u30DC\u30A4\u30B9\u30E1\u30C3\u30BB\u30FC\u30B8",
22072
+ Video: "\u52D5\u753B",
22073
+ Sticker: "\u30B9\u30BF\u30F3\u30D7",
22074
+ Dice: "\u30B5\u30A4\u30B3\u30ED",
22075
+ Contact: "\u9023\u7D61\u5148",
22076
+ Venue: "\u5834\u6240",
22077
+ Location: "\u4F4D\u7F6E\u60C5\u5831",
22078
+ "Live location": "\u30E9\u30A4\u30D6\u4F4D\u7F6E\u60C5\u5831",
22079
+ Game: "\u30B2\u30FC\u30E0",
22080
+ Invoice: "\u8ACB\u6C42\u66F8",
22081
+ "Link preview": "\u30EA\u30F3\u30AF\u306E\u30D7\u30EC\u30D3\u30E5\u30FC",
22082
+ Poll: "\u6295\u7968",
22083
+ Checklist: "\u30C1\u30A7\u30C3\u30AF\u30EA\u30B9\u30C8",
22084
+ Story: "\u30B9\u30C8\u30FC\u30EA\u30FC",
22085
+ "Paid media": "\u6709\u6599\u30E1\u30C7\u30A3\u30A2",
22086
+ "Unsupported media": "\u672A\u5BFE\u5FDC\u306E\u30E1\u30C7\u30A3\u30A2"
22087
+ }
22088
+ };
22089
+
22026
22090
  // src/core/facts.ts
22027
22091
  function sizeFact(bytes) {
22028
22092
  return { label: "Size", value: { text: bytes === 1 ? "{bytes} byte" : "{bytes} bytes", values: { bytes } } };
@@ -22257,11 +22321,11 @@ var ANIMATED_STICKER = "an animated sticker (.tgs), which cannot be inspected";
22257
22321
  function unreadableArchive(label) {
22258
22322
  return `an archive that cannot be inspected (${label})`;
22259
22323
  }
22260
- function words(text) {
22261
- return text.split(/\s+/).filter((word) => word !== "");
22324
+ function words(text2) {
22325
+ return text2.split(/\s+/).filter((word) => word !== "");
22262
22326
  }
22263
- function ascii(text) {
22264
- return Array.from(text, (character) => character.charCodeAt(0));
22327
+ function ascii(text2) {
22328
+ return Array.from(text2, (character) => character.charCodeAt(0));
22265
22329
  }
22266
22330
  function at(offset, bytes) {
22267
22331
  return { offset, bytes };
@@ -22519,9 +22583,9 @@ function parseHeader(block) {
22519
22583
  }
22520
22584
  return { path, size: parseNumber(block.subarray(SIZE_START, SIZE_END)), type: block[TYPE_OFFSET] };
22521
22585
  }
22522
- function parseDecimal(text) {
22523
- if (!/^[0-9]{1,15}$/.test(text)) return void 0;
22524
- return Number(text);
22586
+ function parseDecimal(text2) {
22587
+ if (!/^[0-9]{1,15}$/.test(text2)) return void 0;
22588
+ return Number(text2);
22525
22589
  }
22526
22590
  function parsePaxRecords(data, maxRecords = Infinity) {
22527
22591
  const records = [];
@@ -22711,8 +22775,8 @@ var EXTENSIONS = {
22711
22775
  };
22712
22776
  var KIND_PATTERN = /^[a-z]{1,16}$/;
22713
22777
  var UNKNOWN_KIND = "file";
22714
- function byteLength(text) {
22715
- return Buffer5.byteLength(text);
22778
+ function byteLength(text2) {
22779
+ return Buffer5.byteLength(text2);
22716
22780
  }
22717
22781
  function extensionOf2(type) {
22718
22782
  return type == null ? "" : EXTENSIONS[type.split(";")[0].trim().toLowerCase()] ?? "";
@@ -22981,8 +23045,8 @@ function fitListings(result) {
22981
23045
  var MAX_NAME_CHARACTERS = 255;
22982
23046
  var CUT_MARK = "\u2026";
22983
23047
  var HIDDEN_CHARACTERS = /[\u0000-\u001F\u007F-\u009F\u00AD\u034F\u061C\u115F\u1160\u17B4\u17B5\u180B-\u180F\u200B-\u200F\u2028-\u202E\u2060-\u206F\u3164\uFE00-\uFE0D\uFEFF\uFFA0\uFFF9-\uFFFC\u{1D173}-\u{1D17A}\u{E0000}-\u{E007F}]/gu;
22984
- function cleanName(text) {
22985
- const cleaned = text.normalize("NFC").replace(HIDDEN_CHARACTERS, "?");
23048
+ function cleanName(text2) {
23049
+ const cleaned = text2.normalize("NFC").replace(HIDDEN_CHARACTERS, "?");
22986
23050
  const characters = Array.from(cleaned);
22987
23051
  if (characters.length <= MAX_NAME_CHARACTERS) return cleaned;
22988
23052
  return characters.slice(0, MAX_NAME_CHARACTERS - CUT_MARK.length).join("") + CUT_MARK;
@@ -23155,9 +23219,9 @@ function decodeUtf8Lossy(bytes) {
23155
23219
  return LOSSY_UTF8.decode(bytes);
23156
23220
  }
23157
23221
  function decodeCp437(bytes) {
23158
- let text = "";
23159
- for (const byte of bytes) text += byte < ASCII_END ? String.fromCharCode(byte) : CP437_HIGH[byte - ASCII_END];
23160
- return text;
23222
+ let text2 = "";
23223
+ for (const byte of bytes) text2 += byte < ASCII_END ? String.fromCharCode(byte) : CP437_HIGH[byte - ASCII_END];
23224
+ return text2;
23161
23225
  }
23162
23226
 
23163
23227
  // src/core/archive/tar.ts
@@ -23977,14 +24041,6 @@ function namesOf(file2, id) {
23977
24041
  const name = safeFileName(file2.name, { kind: file2.kind, id, type: file2.type });
23978
24042
  return { name, shown: file2.name === null ? file2.kind : name };
23979
24043
  }
23980
- var FILE_KIND_WORDS = {
23981
- photo: "Photo",
23982
- document: "Document",
23983
- audio: "Audio",
23984
- voice: "Voice message",
23985
- video: "Video",
23986
- sticker: "Sticker"
23987
- };
23988
24044
  function saveHeadline(file2, chat) {
23989
24045
  if (file2 !== void 0 && chat !== null) return { text: "About to save {file} from {chat}", values: { file: file2, chat } };
23990
24046
  if (chat !== null) return { text: "About to save a file from {chat}", values: { chat } };
@@ -23992,9 +24048,8 @@ function saveHeadline(file2, chat) {
23992
24048
  return void 0;
23993
24049
  }
23994
24050
  function fileFacts(media, unpack) {
23995
- const kind = media && Object.hasOwn(FILE_KIND_WORDS, media.type) ? FILE_KIND_WORDS[media.type] : void 0;
23996
24051
  return [
23997
- ...media ? [{ label: "File", value: kind ? { text: kind } : media.type }] : [],
24052
+ ...media ? [{ label: "File", value: mediaKindValue(media.type) }] : [],
23998
24053
  ...unpack ? [{ label: "Unpack", value: { text: "Yes" } }] : [],
23999
24054
  ...media?.size !== void 0 && Number.isSafeInteger(media.size) && media.size > 0 ? [sizeFact(media.size)] : []
24000
24055
  ];
@@ -24225,14 +24280,14 @@ function storedReactions(json2) {
24225
24280
  function ownReaction(reactions) {
24226
24281
  return reactions.find((reaction2) => reaction2.mine)?.emoji;
24227
24282
  }
24228
- function checkEditable({ kind, body, captioned }, text) {
24283
+ function checkEditable({ kind, body, captioned }, text2) {
24229
24284
  if (kind === "service" || body === null && !captioned) {
24230
24285
  throw new ToolError("invalid_argument", "This message has no text or caption to edit; nothing was edited.");
24231
24286
  }
24232
- if (body === text) {
24287
+ if (body === text2) {
24233
24288
  throw new ToolError("invalid_argument", "The text is the same as the message's; nothing to edit.");
24234
24289
  }
24235
- if (captioned && text.length > CAPTION_MAX_UNITS) {
24290
+ if (captioned && text2.length > CAPTION_MAX_UNITS) {
24236
24291
  throw new ToolError(
24237
24292
  "invalid_argument",
24238
24293
  `A caption must be at most ${CAPTION_MAX_UNITS} characters; nothing was edited.`
@@ -24241,8 +24296,8 @@ function checkEditable({ kind, body, captioned }, text) {
24241
24296
  }
24242
24297
 
24243
24298
  // src/core/message/arguments.ts
24244
- function checkedText(text) {
24245
- const trimmed = text.trim();
24299
+ function checkedText(text2) {
24300
+ const trimmed = text2.trim();
24246
24301
  if (trimmed === "") throw new ToolError("invalid_argument", "text is blank; give the text to send.");
24247
24302
  if (trimmed.length > TEXT_MAX_UNITS) {
24248
24303
  throw new ToolError(
@@ -24300,8 +24355,8 @@ var Window = class {
24300
24355
  get tail() {
24301
24356
  return this.#tail;
24302
24357
  }
24303
- sees(text) {
24304
- const all = this.#tail + text;
24358
+ sees(text2) {
24359
+ const all = this.#tail + text2;
24305
24360
  this.#tail = all.slice(-TAIL_LENGTH);
24306
24361
  return MARKER.test(all);
24307
24362
  }
@@ -24311,13 +24366,13 @@ var PrivateKeyScan = class {
24311
24366
  #withoutNuls;
24312
24367
  /** Feeds the next chunk; whether a marker starts anywhere in the bytes so far. */
24313
24368
  sees(chunk) {
24314
- const text = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString("latin1");
24369
+ const text2 = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString("latin1");
24315
24370
  const before = this.#plain.tail;
24316
- const plain2 = this.#plain.sees(text);
24317
- if (this.#withoutNuls === void 0 && text.includes(NUL)) {
24371
+ const plain2 = this.#plain.sees(text2);
24372
+ if (this.#withoutNuls === void 0 && text2.includes(NUL)) {
24318
24373
  this.#withoutNuls = new Window(before);
24319
24374
  }
24320
- const stripped = this.#withoutNuls?.sees(text.replaceAll(NUL, "")) ?? false;
24375
+ const stripped = this.#withoutNuls?.sees(text2.replaceAll(NUL, "")) ?? false;
24321
24376
  return plain2 || stripped;
24322
24377
  }
24323
24378
  };
@@ -24509,9 +24564,8 @@ async function checkArchive(path, format, limits, signal) {
24509
24564
  }
24510
24565
 
24511
24566
  // src/core/outgoing/limits.ts
24512
- var BYTES_PER_MB2 = 1024 * 1024;
24513
24567
  var MAX_FILES = 10;
24514
- var MAX_TOTAL_BYTES = 100 * BYTES_PER_MB2;
24568
+ var MAX_TOTAL_BYTES = 100 * BYTES_PER_MB;
24515
24569
 
24516
24570
  // src/core/file/send/constants.ts
24517
24571
  var SEND_FILES_TIMEOUT_MS = 30 * 60 * 1e3;
@@ -24944,12 +24998,12 @@ var DUPLICATE_NOTE = "An earlier delivery of this same call had already gone out
24944
24998
  var DUPLICATE_UNKNOWN_ID_NOTE = `${DUPLICATE_NOTE} Its id is not known yet; \`telegram_chat_read\` shows it once the service receives it.`;
24945
24999
 
24946
25000
  // src/core/message/uncertain.ts
24947
- function sentLike(paths, chat, text, startedAt) {
25001
+ function sentLike(paths, chat, text2, startedAt) {
24948
25002
  const rows = readMirror(
24949
25003
  paths,
24950
25004
  (mirror) => mirror.prepare(
24951
25005
  "SELECT id, ts FROM messages WHERE chat = ? AND from_me = 1 AND body = ? AND ts >= ? ORDER BY ts DESC, id DESC LIMIT ?"
24952
- ).all(chat, text, startedAt - SENT_LOOKBACK_MS, MAX_SENT_LIKE)
25006
+ ).all(chat, text2, startedAt - SENT_LOOKBACK_MS, MAX_SENT_LIKE)
24953
25007
  );
24954
25008
  return rows.map(({ id, ts }) => ({ id, time: formatLocalTime(ts) }));
24955
25009
  }
@@ -24969,8 +25023,8 @@ function withFound(error62, find, describe3) {
24969
25023
  details: { ...error62.details, sent_like_this: found }
24970
25024
  });
24971
25025
  }
24972
- function withSentLike(error62, paths, chat, text, startedAt) {
24973
- return withFound(error62, () => sentLike(paths, chat, text, startedAt), sentence);
25026
+ function withSentLike(error62, paths, chat, text2, startedAt) {
25027
+ return withFound(error62, () => sentLike(paths, chat, text2, startedAt), sentence);
24974
25028
  }
24975
25029
 
24976
25030
  // src/core/file/send/uncertain.ts
@@ -25107,17 +25161,17 @@ var MAX_HEADLINE_VALUES = 8;
25107
25161
  var MAX_HEADLINE_VALUE_LENGTH = 80;
25108
25162
  var MAX_PINS = 16;
25109
25163
  var HEADLINE_PLACEHOLDER = /\{([A-Za-z_][A-Za-z0-9_]*)\}/;
25110
- function cut2(text, limit) {
25111
- const points = Array.from(text);
25112
- return points.length > limit ? `${points.slice(0, limit - 1).join("")}\u2026` : text;
25164
+ function cut2(text2, limit) {
25165
+ const points = Array.from(text2);
25166
+ return points.length > limit ? `${points.slice(0, limit - 1).join("")}\u2026` : text2;
25113
25167
  }
25114
- function omittedBy(text, limit) {
25115
- const length = Array.from(text).length;
25168
+ function omittedBy(text2, limit) {
25169
+ const length = Array.from(text2).length;
25116
25170
  return length > limit ? length - (limit - 1) : 0;
25117
25171
  }
25118
- function templatePlaceholders(text) {
25172
+ function templatePlaceholders(text2) {
25119
25173
  const names = /* @__PURE__ */ new Set();
25120
- const parts = text.split(HEADLINE_PLACEHOLDER);
25174
+ const parts = text2.split(HEADLINE_PLACEHOLDER);
25121
25175
  for (const [index, part] of parts.entries()) {
25122
25176
  if (index % 2 === 1) names.add(part);
25123
25177
  else if (/[{}]/.test(part)) return void 0;
@@ -25135,10 +25189,10 @@ function valueEntries(values) {
25135
25189
  const entries = Object.entries(values);
25136
25190
  return entries.length > MAX_HEADLINE_VALUES ? void 0 : entries;
25137
25191
  }
25138
- function isTemplate(text, limit, entries) {
25139
- if (text.trim() === "" || Array.from(text).length > limit) return false;
25192
+ function isTemplate(text2, limit, entries) {
25193
+ if (text2.trim() === "" || Array.from(text2).length > limit) return false;
25140
25194
  const given = new Set(entries.map(([name]) => name));
25141
- const placeholders2 = templatePlaceholders(text);
25195
+ const placeholders2 = templatePlaceholders(text2);
25142
25196
  return placeholders2 !== void 0 && [...placeholders2].every((name) => given.has(name));
25143
25197
  }
25144
25198
  function isTemplateValue(value) {
@@ -25154,15 +25208,15 @@ function isFact(fact) {
25154
25208
  }
25155
25209
  function cutValue(value) {
25156
25210
  if (typeof value === "string") return cut2(value, MAX_VALUE_LENGTH);
25157
- const { text, values } = value;
25158
- if (values === void 0) return { text };
25211
+ const { text: text2, values } = value;
25212
+ if (values === void 0) return { text: text2 };
25159
25213
  const cutValues = Object.entries(values).map(
25160
25214
  ([name, entry2]) => [name, typeof entry2 === "string" ? cut2(entry2, MAX_VALUE_LENGTH) : entry2]
25161
25215
  );
25162
- return { text, values: Object.fromEntries(cutValues) };
25216
+ return { text: text2, values: Object.fromEntries(cutValues) };
25163
25217
  }
25164
- function isText2(text) {
25165
- return isRecord2(text) && typeof text.label === "string" && text.label.trim() !== "" && typeof text.body === "string";
25218
+ function isText2(text2) {
25219
+ return isRecord2(text2) && typeof text2.label === "string" && text2.label.trim() !== "" && typeof text2.body === "string";
25166
25220
  }
25167
25221
  function pinsOf(pins) {
25168
25222
  if (pins === void 0) return {};
@@ -25176,32 +25230,32 @@ function pinsOf(pins) {
25176
25230
  }
25177
25231
  function copyValue(value) {
25178
25232
  if (typeof value === "string") return value;
25179
- const { text, values } = value;
25180
- return values === void 0 ? { text } : { text, values: { ...values } };
25233
+ const { text: text2, values } = value;
25234
+ return values === void 0 ? { text: text2 } : { text: text2, values: { ...values } };
25181
25235
  }
25182
25236
  function headlineOf(headline) {
25183
25237
  if (!isRecord2(headline) || typeof headline.text !== "string") return void 0;
25184
- const { text, values } = headline;
25238
+ const { text: text2, values } = headline;
25185
25239
  const entries = valueEntries(values);
25186
- if (entries === void 0 || !isTemplate(text, MAX_HEADLINE_LENGTH, entries)) return void 0;
25240
+ if (entries === void 0 || !isTemplate(text2, MAX_HEADLINE_LENGTH, entries)) return void 0;
25187
25241
  if (!entries.every((entry2) => typeof entry2[1] === "string")) return void 0;
25188
- return values === void 0 ? { text } : { text, values: Object.fromEntries(entries) };
25242
+ return values === void 0 ? { text: text2 } : { text: text2, values: Object.fromEntries(entries) };
25189
25243
  }
25190
- function cutHeadline({ text, values }) {
25191
- if (values === void 0) return { text };
25244
+ function cutHeadline({ text: text2, values }) {
25245
+ if (values === void 0) return { text: text2 };
25192
25246
  const cutValues = Object.entries(values).map(
25193
25247
  ([name, value]) => [name, cut2(value, MAX_HEADLINE_VALUE_LENGTH)]
25194
25248
  );
25195
- return { text, values: Object.fromEntries(cutValues) };
25249
+ return { text: text2, values: Object.fromEntries(cutValues) };
25196
25250
  }
25197
- function cutDescription({ headline, facts, text, pins }) {
25251
+ function cutDescription({ headline, facts, text: text2, pins }) {
25198
25252
  return {
25199
25253
  ...headline && { headline: cutHeadline(headline) },
25200
25254
  facts: facts.slice(0, MAX_FACTS).map(({ label, value }) => ({
25201
25255
  label: cut2(label, MAX_LABEL_LENGTH),
25202
25256
  value: cutValue(value)
25203
25257
  })),
25204
- ...text && { text: { label: cut2(text.label, MAX_TEXT_LABEL_LENGTH), body: cut2(text.body, MAX_TEXT_LENGTH) } },
25258
+ ...text2 && { text: { label: cut2(text2.label, MAX_TEXT_LABEL_LENGTH), body: cut2(text2.body, MAX_TEXT_LENGTH) } },
25205
25259
  ...pins && { pins }
25206
25260
  };
25207
25261
  }
@@ -25225,47 +25279,54 @@ async function wholeCallDescription(spec, args, ctx) {
25225
25279
  if (kept === void 0) return void 0;
25226
25280
  const facts = entries.map(({ label, value }) => ({ label, value: copyValue(value) }));
25227
25281
  const headline = answer.headline === void 0 ? void 0 : headlineOf(answer.headline);
25228
- const text = answer.text === void 0 || answer.text.body === "" ? void 0 : { label: answer.text.label, body: answer.text.body };
25229
- if (headline === void 0 && facts.length === 0 && text === void 0) return void 0;
25230
- return { ...headline && { headline }, facts, ...text && { text }, ...kept };
25282
+ const text2 = answer.text === void 0 || answer.text.body === "" ? void 0 : { label: answer.text.label, body: answer.text.body };
25283
+ if (headline === void 0 && facts.length === 0 && text2 === void 0) return void 0;
25284
+ return { ...headline && { headline }, facts, ...text2 && { text: text2 }, ...kept };
25231
25285
  }
25232
25286
 
25233
25287
  // src/core/message/action-describe.ts
25234
25288
  var EDIT_HEADLINES = {
25235
25289
  named: "About to edit message {id} in {chat}",
25236
- saved: "About to edit message {id} in Saved Messages"
25290
+ saved: "About to edit message {id} in Saved Messages",
25291
+ bare: "About to edit message {id}"
25237
25292
  };
25238
25293
  var DELETE_HEADLINES = {
25239
- named: "About to delete message {id} from {chat}",
25240
- saved: "About to delete message {id} from Saved Messages"
25294
+ named: "About to delete message {id} from {chat} for everyone",
25295
+ saved: "About to delete message {id} from Saved Messages",
25296
+ bare: "About to delete message {id} for everyone"
25241
25297
  };
25242
25298
  var REACT_HEADLINES = {
25243
25299
  named: "About to react {emoji} to message {id} in {chat}",
25244
- saved: "About to react {emoji} to message {id} in Saved Messages"
25300
+ saved: "About to react {emoji} to message {id} in Saved Messages",
25301
+ bare: "About to react {emoji} to message {id}"
25245
25302
  };
25246
25303
  var UNREACT_HEADLINES = {
25247
25304
  named: "About to remove your reaction from message {id} in {chat}",
25248
- saved: "About to remove your reaction from message {id} in Saved Messages"
25305
+ saved: "About to remove your reaction from message {id} in Saved Messages",
25306
+ bare: "About to remove your reaction from message {id}"
25249
25307
  };
25250
25308
  var PIN_HEADLINES = {
25251
25309
  named: "About to pin message {id} in {chat}",
25252
- saved: "About to pin message {id} in Saved Messages"
25310
+ saved: "About to pin message {id} in Saved Messages",
25311
+ bare: "About to pin message {id}"
25253
25312
  };
25254
25313
  var UNPIN_HEADLINES = {
25255
25314
  named: "About to unpin message {id} in {chat}",
25256
- saved: "About to unpin message {id} in Saved Messages"
25315
+ saved: "About to unpin message {id} in Saved Messages",
25316
+ bare: "About to unpin message {id}"
25257
25317
  };
25258
25318
  var MARK_READ_HEADLINES = {
25259
25319
  named: "About to mark {chat} read up to message {id}",
25260
- saved: "About to mark Saved Messages read up to message {id}"
25320
+ saved: "About to mark Saved Messages read up to message {id}",
25321
+ bare: "About to mark messages read up to message {id}"
25261
25322
  };
25262
25323
  function actionHeadline(headlines, { kind, name }, id, values) {
25263
25324
  if (kind === "self") return { text: headlines.saved, values: { id: String(id), ...values } };
25264
- return name === null ? void 0 : { text: headlines.named, values: { id: String(id), chat: name, ...values } };
25325
+ return name === null ? { text: headlines.bare, values: { id: String(id), ...values } } : { text: headlines.named, values: { id: String(id), chat: name, ...values } };
25265
25326
  }
25266
25327
  function currentFacts({ body, media }) {
25267
- const shown = body || media?.type;
25268
- return shown ? [{ label: "Current", value: cut2(shown, MAX_VALUE_LENGTH) }] : [];
25328
+ if (body) return [{ label: "Current", value: cut2(body, MAX_VALUE_LENGTH) }];
25329
+ return media ? [{ label: "Media", value: mediaKindValue(media.type) }] : [];
25269
25330
  }
25270
25331
  function describeAction(headlines, chat, id, message, extras = {}) {
25271
25332
  const headline = actionHeadline(headlines, chat, id, extras.values ?? {});
@@ -25501,7 +25562,7 @@ var telegramMessageDelete = defineTool({
25501
25562
  "A result means Telegram deleted the message for everyone. `already` means it was gone before this call.",
25502
25563
  "A failure `uncertain` means the delete may have gone through: check the chat in Telegram and never ask for it again without the user checking."
25503
25564
  ].join(" "),
25504
- intent: "About to delete a message",
25565
+ intent: "About to delete a message for everyone",
25505
25566
  toolset: "actions",
25506
25567
  class: "administrative",
25507
25568
  input: deleteInput,
@@ -25533,10 +25594,10 @@ var telegramMessageDelete = defineTool({
25533
25594
  });
25534
25595
 
25535
25596
  // src/core/message/edit-prepare.ts
25536
- function prepareEdit(mirror, chatId, id, text) {
25597
+ function prepareEdit(mirror, chatId, id, text2) {
25537
25598
  const { chat, message } = findOwnMessage(mirror, chatId, id, "edit");
25538
25599
  if (!message) throw deletedMessage();
25539
- checkEditable({ ...message, captioned: message.media !== null && takesCaption(message.media.type) }, text);
25600
+ checkEditable({ ...message, captioned: message.media !== null && takesCaption(message.media.type) }, text2);
25540
25601
  return { chat, message };
25541
25602
  }
25542
25603
 
@@ -25559,22 +25620,22 @@ var telegramMessageEdit = defineTool({
25559
25620
  input: editInput,
25560
25621
  output: editOutput,
25561
25622
  async describe(args, ctx) {
25562
- const text = checkedText(args.text);
25623
+ const text2 = checkedText(args.text);
25563
25624
  const chat = requireChatId(args.chat, MESSAGE_EDIT_TOOL_ID);
25564
25625
  const paths = await pickPaths(ctx, args.account);
25565
- const target = readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text));
25626
+ const target = readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text2));
25566
25627
  return {
25567
25628
  ...describeAction(EDIT_HEADLINES, target.chat, args.id, target.message),
25568
- text: { label: "New text", body: text }
25629
+ text: { label: "New text", body: text2 }
25569
25630
  };
25570
25631
  },
25571
25632
  targets: (args, ctx) => actionTargets(MESSAGE_EDIT_TOOL_ID, args, ctx),
25572
25633
  async execute(args, ctx) {
25573
25634
  const chat = requireChatId(args.chat, MESSAGE_EDIT_TOOL_ID);
25574
- const text = checkedText(args.text);
25635
+ const text2 = checkedText(args.text);
25575
25636
  const paths = await pickPaths(ctx, args.account);
25576
- readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text));
25577
- const params = { chat, id: args.id, text };
25637
+ readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text2));
25638
+ const params = { chat, id: args.id, text: text2 };
25578
25639
  const edited = await callService(paths.socket, SERVICE_REQUESTS.edit, params, {
25579
25640
  effect: "write",
25580
25641
  signal: ctx.signal,
@@ -25770,7 +25831,7 @@ var telegramMessageSend = defineTool({
25770
25831
  input: input5,
25771
25832
  output: output5,
25772
25833
  async describe(args, ctx) {
25773
- const text = checkedText(args.text);
25834
+ const text2 = checkedText(args.text);
25774
25835
  const chat = requireChatId(args.chat, MESSAGE_SEND_TOOL_ID);
25775
25836
  const paths = await pickPaths(ctx, args.account);
25776
25837
  const { row, title } = readMirror(paths, (mirror) => {
@@ -25782,17 +25843,17 @@ var telegramMessageSend = defineTool({
25782
25843
  headline: { text: "About to send a message to {chat}", values: { chat: row.name } }
25783
25844
  },
25784
25845
  facts: [chatFact(row.kind), ...topicFacts(args.topic, title), ...replyFacts(args.reply_to)],
25785
- text: { label: "Message text", body: text }
25846
+ text: { label: "Message text", body: text2 }
25786
25847
  };
25787
25848
  },
25788
25849
  targets: (args, ctx) => chatTargets(MESSAGE_SEND_TOOL_ID, args, ctx),
25789
25850
  async execute(args, ctx) {
25790
25851
  const chat = requireChatId(args.chat, MESSAGE_SEND_TOOL_ID);
25791
- const text = checkedText(args.text);
25852
+ const text2 = checkedText(args.text);
25792
25853
  const paths = await pickPaths(ctx, args.account);
25793
25854
  const params = {
25794
25855
  chat,
25795
- text,
25856
+ text: text2,
25796
25857
  ...args.topic !== void 0 && { topic: args.topic },
25797
25858
  ...args.reply_to !== void 0 && { reply_to: args.reply_to }
25798
25859
  };
@@ -25805,7 +25866,7 @@ var telegramMessageSend = defineTool({
25805
25866
  });
25806
25867
  return sendResult(chat, sent);
25807
25868
  } catch (error62) {
25808
- throw isToolError(error62) && error62.code === "uncertain" ? withSentLike(error62, paths, chat, text, startedAt) : error62;
25869
+ throw isToolError(error62) && error62.code === "uncertain" ? withSentLike(error62, paths, chat, text2, startedAt) : error62;
25809
25870
  }
25810
25871
  }
25811
25872
  });
@@ -26106,12 +26167,6 @@ var FILE_TRANSLATIONS = {
26106
26167
  "About to save a file from {chat}": "{chat} \u306E\u30D5\u30A1\u30A4\u30EB\u3092\u4FDD\u5B58\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26107
26168
  "About to save {file}": "{file} \u3092\u4FDD\u5B58\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26108
26169
  File: "\u30D5\u30A1\u30A4\u30EB",
26109
- Photo: "\u5199\u771F",
26110
- Document: "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
26111
- Audio: "\u97F3\u58F0",
26112
- "Voice message": "\u30DC\u30A4\u30B9\u30E1\u30C3\u30BB\u30FC\u30B8",
26113
- Video: "\u52D5\u753B",
26114
- Sticker: "\u30B9\u30BF\u30F3\u30D7",
26115
26170
  Unpack: "\u5C55\u958B",
26116
26171
  Yes: "\u3059\u308B"
26117
26172
  }
@@ -26126,24 +26181,31 @@ var ACTION_TRANSLATIONS = {
26126
26181
  "Pin or unpin a message": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u30D4\u30F3\u7559\u3081\u3059\u308B\u30FB\u89E3\u9664\u3059\u308B",
26127
26182
  "Mark a chat read": "\u30C1\u30E3\u30C3\u30C8\u3092\u65E2\u8AAD\u306B\u3059\u308B",
26128
26183
  "About to edit a message": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26129
- "About to delete a message": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26184
+ "About to delete a message for everyone": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u5168\u54E1\u5206\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26130
26185
  "About to change a reaction": "\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5909\u66F4\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26131
26186
  "About to pin or unpin a message": "\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30D4\u30F3\u7559\u3081\u3092\u5909\u66F4\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26132
26187
  "About to mark messages read": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u65E2\u8AAD\u306B\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26133
26188
  "About to edit message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26134
- "About to edit message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26135
- "About to delete message {id} from {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26136
- "About to delete message {id} from Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26189
+ "About to edit message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26190
+ "About to edit message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26191
+ "About to delete message {id} from {chat} for everyone": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u5168\u54E1\u5206\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26192
+ "About to delete message {id} from Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26193
+ "About to delete message {id} for everyone": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u5168\u54E1\u5206\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26137
26194
  "About to react {emoji} to message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306B {emoji} \u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u4ED8\u3051\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26138
- "About to react {emoji} to message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306B {emoji} \u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u4ED8\u3051\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26195
+ "About to react {emoji} to message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306B {emoji} \u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u4ED8\u3051\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26196
+ "About to react {emoji} to message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306B {emoji} \u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u4ED8\u3051\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26139
26197
  "About to remove your reaction from message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u304B\u3089\u81EA\u5206\u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5916\u305D\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26140
- "About to remove your reaction from message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u304B\u3089\u81EA\u5206\u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5916\u305D\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26198
+ "About to remove your reaction from message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u304B\u3089\u81EA\u5206\u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5916\u305D\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26199
+ "About to remove your reaction from message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u304B\u3089\u81EA\u5206\u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5916\u305D\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26141
26200
  "About to pin message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u30D4\u30F3\u7559\u3081\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26142
- "About to pin message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u30D4\u30F3\u7559\u3081\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26201
+ "About to pin message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u30D4\u30F3\u7559\u3081\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26202
+ "About to pin message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u30D4\u30F3\u7559\u3081\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26143
26203
  "About to unpin message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306E\u30D4\u30F3\u7559\u3081\u3092\u89E3\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26144
- "About to unpin message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306E\u30D4\u30F3\u7559\u3081\u3092\u89E3\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26204
+ "About to unpin message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306E\u30D4\u30F3\u7559\u3081\u3092\u89E3\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26205
+ "About to unpin message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306E\u30D4\u30F3\u7559\u3081\u3092\u89E3\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26145
26206
  "About to mark {chat} read up to message {id}": "{chat} \u3092\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u307E\u3067\u65E2\u8AAD\u306B\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26146
26207
  "About to mark Saved Messages read up to message {id}": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u307E\u3067\u65E2\u8AAD\u306B\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26208
+ "About to mark messages read up to message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u307E\u3067\u65E2\u8AAD\u306B\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26147
26209
  Message: "\u30E1\u30C3\u30BB\u30FC\u30B8",
26148
26210
  Current: "\u73FE\u5728\u306E\u5185\u5BB9",
26149
26211
  Replaces: "\u7F6E\u304D\u63DB\u3048\u308B\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3",
@@ -26187,6 +26249,7 @@ var SYNC_TRANSLATIONS = {
26187
26249
  var ALL_TRANSLATIONS = mergeTranslations(
26188
26250
  FACT_TRANSLATIONS,
26189
26251
  CHAT_FACT_TRANSLATIONS,
26252
+ MEDIA_FACT_TRANSLATIONS,
26190
26253
  MESSAGE_TRANSLATIONS,
26191
26254
  ACTION_TRANSLATIONS,
26192
26255
  FILE_TRANSLATIONS,
@@ -26209,9 +26272,9 @@ function mark(character) {
26209
26272
  const hex3 = character.codePointAt(0).toString(16).toUpperCase();
26210
26273
  return `<U+${hex3.padStart(MARK_DIGITS, "0")}>`;
26211
26274
  }
26212
- function visible(text, options = {}) {
26213
- if (options.keepNewlines) return text.replace(/\r\n/g, "\n").replace(HIDDEN_EXCEPT_NEWLINE, mark);
26214
- return text.replace(HIDDEN_ALL, mark);
26275
+ function visible(text2, options = {}) {
26276
+ if (options.keepNewlines) return text2.replace(/\r\n/g, "\n").replace(HIDDEN_EXCEPT_NEWLINE, mark);
26277
+ return text2.replace(HIDDEN_ALL, mark);
26215
26278
  }
26216
26279
 
26217
26280
  // src/entry/words.ts
@@ -26257,12 +26320,12 @@ var omittedOf = (count) => count > 0 ? { omitted: count } : {};
26257
26320
  function quoted(value) {
26258
26321
  return `"${visible(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
26259
26322
  }
26260
- function filled(text, literal2, slot) {
26261
- return text.split(HEADLINE_PLACEHOLDER).map((part, index) => index % 2 === 0 ? literal2(part) : slot(part)).join("");
26323
+ function filled(text2, literal2, slot) {
26324
+ return text2.split(HEADLINE_PLACEHOLDER).map((part, index) => index % 2 === 0 ? literal2(part) : slot(part)).join("");
26262
26325
  }
26263
- function renderedHeadline({ text, values = {} }) {
26326
+ function renderedHeadline({ text: text2, values = {} }) {
26264
26327
  return filled(
26265
- text,
26328
+ text2,
26266
26329
  (part) => visible(part),
26267
26330
  (name) => quoted(values[name] ?? "")
26268
26331
  );
@@ -26276,7 +26339,7 @@ function renderedValue(value, t) {
26276
26339
  return { value: visible(cut2(whole, MAX_VALUE_LENGTH)), ...omittedOf(omittedBy(whole, MAX_VALUE_LENGTH)) };
26277
26340
  }
26278
26341
  function shownDescription(description, t) {
26279
- const { headline, facts, text } = cutDescription(description);
26342
+ const { headline, facts, text: text2 } = cutDescription(description);
26280
26343
  const wholeBody = description.text?.body ?? "";
26281
26344
  const dropped = description.facts.length - facts.length;
26282
26345
  return {
@@ -26286,9 +26349,9 @@ function shownDescription(description, t) {
26286
26349
  ...renderedValue(description.facts[index].value, t)
26287
26350
  })),
26288
26351
  ...dropped > 0 && { dropped },
26289
- ...text && {
26352
+ ...text2 && {
26290
26353
  text: {
26291
- label: visible(t.word(text.label, MAX_TEXT_LABEL_LENGTH)),
26354
+ label: visible(t.word(text2.label, MAX_TEXT_LABEL_LENGTH)),
26292
26355
  body: visible(cut2(wholeBody, MAX_TEXT_LENGTH), { keepNewlines: true }),
26293
26356
  ...omittedOf(omittedBy(wholeBody, MAX_TEXT_LENGTH))
26294
26357
  }
@@ -26302,9 +26365,9 @@ async function timedCallDescription(spec, args, ctx, timeoutMs = DESCRIBE_TIMEOU
26302
26365
  let timer;
26303
26366
  let onAbort;
26304
26367
  try {
26305
- const timedOut = new Promise((resolve8) => {
26368
+ const timedOut = new Promise((resolve6) => {
26306
26369
  timer = setTimeout(() => {
26307
- resolve8(void 0);
26370
+ resolve6(void 0);
26308
26371
  controller.abort();
26309
26372
  }, timeoutMs);
26310
26373
  });
@@ -26380,76 +26443,70 @@ ${JSON.stringify(error62.details)}` : ""}`;
26380
26443
  var PROJECT_PROFILE = {
26381
26444
  name: PROJECT.name,
26382
26445
  envPrefix: PROJECT.envPrefix,
26383
- toolsets: { names: PROJECT.toolsets, defaults: PROJECT.defaultToolsets }
26446
+ toolsets: { names: PROJECT.toolsets, defaults: PROJECT.defaultToolsets },
26447
+ options: PROJECT.options
26384
26448
  };
26385
26449
 
26386
- // src/entry/download-dir.ts
26387
- import { resolve as resolve5 } from "node:path";
26450
+ // src/entry/settings.ts
26451
+ import { homedir as homedir3 } from "node:os";
26452
+ import { isAbsolute as isAbsolute6, join as join10, resolve as resolvePath, sep as sep4 } from "node:path";
26388
26453
 
26389
26454
  // src/entry/directory.ts
26390
26455
  import { homedir as homedir2 } from "node:os";
26391
- import { isAbsolute as isAbsolute5, join as join10, resolve as resolve4, sep as sep4 } from "node:path";
26456
+ import { isAbsolute as isAbsolute5, resolve as resolve4 } from "node:path";
26457
+ var DOWNLOADS_DIR = "Downloads";
26458
+ var DEFAULT_DOWNLOAD_MAX_MB = 100;
26392
26459
  function homeDir(env) {
26393
26460
  const envHome = env.HOME;
26394
26461
  if (envHome && isAbsolute5(envHome)) return envHome;
26395
26462
  return homedir2();
26396
26463
  }
26397
- function resolveGivenDirectory(given, home) {
26398
- const text = given.text.trim();
26399
- const expanded = text === "~" ? home : text.startsWith("~/") || text.startsWith(`~${sep4}`) ? join10(home, text.slice(2)) : text;
26400
- if (!text || !isAbsolute5(expanded)) {
26401
- throw new ToolError("invalid_argument", `${given.source} must be an absolute path.`);
26402
- }
26403
- return resolve4(expanded);
26404
- }
26405
-
26406
- // src/entry/download-dir.ts
26407
- var DOWNLOADS_DIR = "Downloads";
26408
- function resolveDownloadDir(given, env, profile) {
26409
- const home = homeDir(env);
26410
- return given !== void 0 ? resolveGivenDirectory(given, home) : resolve5(home, DOWNLOADS_DIR, profile.name);
26464
+ function defaultStateDir(name, env, home) {
26465
+ const xdg = env.XDG_STATE_HOME;
26466
+ return xdg && isAbsolute5(xdg) ? resolve4(xdg, name) : resolve4(home, ".local", "state", name);
26411
26467
  }
26412
-
26413
- // src/entry/send-dirs.ts
26414
- function resolveSendDirs(given, env) {
26415
- const home = homeDir(env);
26416
- return given.map((directory) => resolveGivenDirectory(directory, home));
26468
+ function text(value) {
26469
+ return typeof value === "string" ? value : void 0;
26417
26470
  }
26418
-
26419
- // src/entry/state-dir.ts
26420
- import { isAbsolute as isAbsolute6, resolve as resolve6 } from "node:path";
26421
- function resolveStateDir(given, env, profile) {
26471
+ function resolveDirectorySettings(options, env, profile) {
26422
26472
  const home = homeDir(env);
26423
- if (given !== void 0) return resolveGivenDirectory(given, home);
26424
- const xdg = env.XDG_STATE_HOME;
26425
- if (xdg && isAbsolute6(xdg)) {
26426
- return resolve6(xdg, profile.name);
26427
- }
26428
- return resolve6(home, ".local", "state", profile.name);
26473
+ const maxMb = options.download_max_mb;
26474
+ const sendDirs = options.send_dirs;
26475
+ return {
26476
+ state_dir: text(options.state_dir) ?? defaultStateDir(profile.name, env, home),
26477
+ download_dir: text(options.download_dir) ?? resolve4(home, DOWNLOADS_DIR, profile.name),
26478
+ download_max_mb: typeof maxMb === "number" ? maxMb : DEFAULT_DOWNLOAD_MAX_MB,
26479
+ send_dirs: Array.isArray(sendDirs) ? [...sendDirs] : []
26480
+ };
26429
26481
  }
26430
26482
 
26431
26483
  // src/entry/settings.ts
26432
26484
  var ALL_TOOLSETS = "all";
26433
26485
  var TRUE_VALUES = ["1", "true"];
26434
26486
  var FALSE_VALUES = ["0", "false"];
26435
- var DEFAULT_DOWNLOAD_MAX_MB = 100;
26436
- var MAX_DOWNLOAD_MB = 2e3;
26437
26487
  var OPTIONS = {
26438
26488
  toolsets: { flag: "--toolsets", envSuffix: "TOOLSETS" },
26439
26489
  read_only: { flag: "--read-only", envSuffix: "READ_ONLY", boolean: true },
26440
26490
  tools: { flag: "--tools", envSuffix: "TOOLS" },
26441
26491
  exclude_tools: { flag: "--exclude-tools", envSuffix: "EXCLUDE_TOOLS" },
26442
- confirm: { flag: "--confirm", envSuffix: "CONFIRM" },
26443
- state_dir: { flag: "--state-dir", envSuffix: "STATE_DIR" },
26444
- download_dir: { flag: "--download-dir", envSuffix: "DOWNLOAD_DIR" },
26445
- download_max_mb: { flag: "--download-max-mb", envSuffix: "DOWNLOAD_MAX_MB" },
26446
- send_dirs: { flag: "--send-dirs", envSuffix: "SEND_DIRS" },
26447
- keep_disappearing: { flag: "--keep-disappearing", envSuffix: "KEEP_DISAPPEARING", boolean: true },
26448
- channels: { flag: "--channels", envSuffix: "CHANNELS", boolean: true }
26492
+ confirm: { flag: "--confirm", envSuffix: "CONFIRM" }
26449
26493
  };
26450
- var SERVICE_OPTION_NAMES = ["keep_disappearing", "channels"];
26494
+ function profileOptions(profile) {
26495
+ return profile.options ?? {};
26496
+ }
26497
+ function settingOptions(profile) {
26498
+ const toolOptions = Object.entries(profileOptions(profile)).map(([name, spec]) => [
26499
+ name,
26500
+ {
26501
+ flag: `--${name.replace(/_/g, "-")}`,
26502
+ envSuffix: name.toUpperCase(),
26503
+ ...optionType(spec) === "boolean" && { boolean: true }
26504
+ }
26505
+ ]);
26506
+ return { ...OPTIONS, ...Object.fromEntries(toolOptions) };
26507
+ }
26451
26508
  function envName(name, profile) {
26452
- return `${profile.envPrefix}_${OPTIONS[name].envSuffix}`;
26509
+ return `${profile.envPrefix}_${settingOptions(profile)[name].envSuffix}`;
26453
26510
  }
26454
26511
  function splitList(value) {
26455
26512
  return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
@@ -26464,43 +26521,87 @@ function checkToolsets(names, source, toolsets) {
26464
26521
  return names.length ? names : void 0;
26465
26522
  }
26466
26523
  function parseBoolean(value, source) {
26467
- const text = value.trim().toLowerCase();
26468
- if (TRUE_VALUES.includes(text)) return true;
26469
- if (FALSE_VALUES.includes(text)) return false;
26524
+ const text2 = value.trim().toLowerCase();
26525
+ if (TRUE_VALUES.includes(text2)) return true;
26526
+ if (FALSE_VALUES.includes(text2)) return false;
26470
26527
  throw new ToolError(
26471
26528
  "invalid_argument",
26472
26529
  `Unknown value "${value}" in ${source}; use one of ${[...TRUE_VALUES, ...FALSE_VALUES].join(", ")}.`
26473
26530
  );
26474
26531
  }
26475
- function listedDirectories(given) {
26476
- return given === void 0 ? [] : splitList(given.text).map((text) => ({ text, source: given.source }));
26477
- }
26478
- function downloadMaxMb(value, source) {
26479
- if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > MAX_DOWNLOAD_MB) {
26480
- throw new ToolError("invalid_argument", `${source} must be an integer from 1 to ${MAX_DOWNLOAD_MB}.`);
26532
+ function parseNumber2(value, source) {
26533
+ const trimmed = value.trim();
26534
+ const parsed = trimmed === "" ? Number.NaN : Number(trimmed);
26535
+ if (!Number.isFinite(parsed)) {
26536
+ throw new ToolError("invalid_argument", `Unknown number "${value}" in ${source}; use a finite number.`);
26481
26537
  }
26482
- return value;
26538
+ return parsed;
26483
26539
  }
26484
- function downloadMaxMbText({ text, source }) {
26485
- const trimmed = text.trim();
26486
- return downloadMaxMb(/^\d+$/.test(trimmed) ? Number(trimmed) : Number.NaN, source);
26540
+ function absolutePath(text2, source, env) {
26541
+ const home = env.HOME !== void 0 && isAbsolute6(env.HOME) ? env.HOME : homedir3();
26542
+ const expanded = text2 === "~" || text2.startsWith("~/") || text2.startsWith(`~${sep4}`) ? join10(home, text2.slice(1)) : text2;
26543
+ if (!isAbsolute6(expanded)) throw new ToolError("invalid_argument", `${source} must be an absolute path.`);
26544
+ return resolvePath(expanded);
26545
+ }
26546
+ function absolutePaths(items, source, env) {
26547
+ return items.length > 0 ? items.map((item) => absolutePath(item, source, env)) : void 0;
26487
26548
  }
26488
26549
  function envText(name, env, profile) {
26489
26550
  const variable = envName(name, profile);
26490
- const text = env[variable];
26491
- return text === void 0 || text.trim() === "" ? void 0 : { text, source: variable };
26492
- }
26493
- var TOOL_OPTION_NAMES = [
26494
- "toolsets",
26495
- "read_only",
26496
- "tools",
26497
- "exclude_tools",
26498
- "state_dir",
26499
- "download_dir",
26500
- "download_max_mb",
26501
- "send_dirs",
26502
- ...SERVICE_OPTION_NAMES
26503
- ];
26551
+ const text2 = env[variable];
26552
+ return text2 === void 0 || text2.trim() === "" ? void 0 : { text: text2, source: variable };
26553
+ }
26554
+ function optionType(spec) {
26555
+ return typeof spec === "string" ? spec : spec.type;
26556
+ }
26557
+ function itemCount(count) {
26558
+ return count === 1 ? "1 item" : `${count} items`;
26559
+ }
26560
+ function numberBoundsError({ integer: integer2, min, max }, source) {
26561
+ const range = min !== void 0 && max !== void 0 ? ` from ${min} to ${max}` : min !== void 0 ? ` of at least ${min}` : max !== void 0 ? ` of at most ${max}` : "";
26562
+ return new ToolError("invalid_argument", `${source} must be ${integer2 ? "a whole number" : "a number"}${range}.`);
26563
+ }
26564
+ function countBoundsError({ min, max }, source) {
26565
+ const range = min !== void 0 && max !== void 0 ? `from ${min} to ${max} items` : min !== void 0 ? `at least ${itemCount(min)}` : `at most ${itemCount(max)}`;
26566
+ return new ToolError("invalid_argument", `${source} must list ${range}.`);
26567
+ }
26568
+ function withinBounds(spec, value, source) {
26569
+ if (value === void 0 || typeof spec === "string") return value;
26570
+ const bounds = spec;
26571
+ const { integer: integer2, min, max } = bounds;
26572
+ if (typeof value === "number") {
26573
+ if (integer2 && !Number.isInteger(value) || min !== void 0 && value < min || max !== void 0 && value > max) {
26574
+ throw numberBoundsError(bounds, source);
26575
+ }
26576
+ } else if (typeof value === "object" && (min !== void 0 && value.length < min || max !== void 0 && value.length > max)) {
26577
+ throw countBoundsError(bounds, source);
26578
+ }
26579
+ return value;
26580
+ }
26581
+ function parseOptionText(spec, given, env) {
26582
+ return withinBounds(spec, parseTypedText(optionType(spec), given, env), given.source);
26583
+ }
26584
+ function parseTypedText(kind, given, env) {
26585
+ switch (kind) {
26586
+ case "text":
26587
+ return nonBlank(given.text);
26588
+ case "boolean":
26589
+ return parseBoolean(given.text, given.source);
26590
+ case "number":
26591
+ return parseNumber2(given.text, given.source);
26592
+ case "list": {
26593
+ const items = splitList(given.text);
26594
+ return items.length > 0 ? items : void 0;
26595
+ }
26596
+ case "path": {
26597
+ const text2 = nonBlank(given.text);
26598
+ return text2 === void 0 ? void 0 : absolutePath(text2, given.source, env);
26599
+ }
26600
+ case "paths":
26601
+ return absolutePaths(splitList(given.text), given.source, env);
26602
+ }
26603
+ }
26604
+ var TOOL_OPTION_NAMES = ["toolsets", "read_only", "tools", "exclude_tools"];
26504
26605
  function stringList(value) {
26505
26606
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
26506
26607
  }
@@ -26508,15 +26609,6 @@ var optionSource = (name) => `Option "${name}"`;
26508
26609
  function optionError(name, expected) {
26509
26610
  return new ToolError("invalid_argument", `${optionSource(name)} must be ${expected}.`);
26510
26611
  }
26511
- function directoryOption(name, value) {
26512
- if (typeof value !== "string") throw optionError(name, "an absolute path");
26513
- return value;
26514
- }
26515
- function sendDirsOption(value) {
26516
- const list2 = stringList(value);
26517
- if (!list2) throw optionError("send_dirs", "a list of absolute paths");
26518
- return list2.map((text) => ({ text, source: optionSource("send_dirs") }));
26519
- }
26520
26612
  function toolsetsOption(value, toolsets) {
26521
26613
  if (value === ALL_TOOLSETS) return ALL_TOOLSETS;
26522
26614
  const names = stringList(value);
@@ -26537,12 +26629,14 @@ function listOption(name) {
26537
26629
  };
26538
26630
  }
26539
26631
  var LANGUAGE_OPTION = "language";
26540
- var PLUGIN_OPTION_NAMES = [...TOOL_OPTION_NAMES, LANGUAGE_OPTION];
26632
+ function pluginOptionNames(profile) {
26633
+ return [...TOOL_OPTION_NAMES, LANGUAGE_OPTION, ...Object.keys(profileOptions(profile))];
26634
+ }
26541
26635
  function languageVariable(profile = PROJECT_PROFILE) {
26542
26636
  return `${profile.envPrefix}_LANGUAGE`;
26543
26637
  }
26544
- function nonBlank(text) {
26545
- const trimmed = text?.trim();
26638
+ function nonBlank(text2) {
26639
+ const trimmed = text2?.trim();
26546
26640
  return trimmed === "" ? void 0 : trimmed;
26547
26641
  }
26548
26642
  function promptLanguage(flag, env, harness, profile = PROJECT_PROFILE) {
@@ -26552,42 +26646,80 @@ function languageOption(value) {
26552
26646
  if (typeof value !== "string") throw optionError(LANGUAGE_OPTION, 'a language tag such as "ja"');
26553
26647
  return nonBlank(value);
26554
26648
  }
26649
+ function toolOption(name, spec, value, env) {
26650
+ return withinBounds(spec, toolOptionOfType(name, optionType(spec), value, env), `Option "${name}"`);
26651
+ }
26652
+ function toolOptionOfType(name, kind, value, env) {
26653
+ switch (kind) {
26654
+ case "text":
26655
+ if (typeof value !== "string") throw optionError(name, "text");
26656
+ return nonBlank(value);
26657
+ case "boolean":
26658
+ if (typeof value !== "boolean") throw optionError(name, "true or false");
26659
+ return value;
26660
+ case "number":
26661
+ if (typeof value !== "number" || !Number.isFinite(value)) throw optionError(name, "a finite number");
26662
+ return value;
26663
+ case "list": {
26664
+ const list2 = stringList(value);
26665
+ if (!list2) throw optionError(name, "a list of strings");
26666
+ const items = list2.map((item) => item.trim()).filter((item) => item !== "");
26667
+ return items.length > 0 ? items : void 0;
26668
+ }
26669
+ case "path": {
26670
+ if (typeof value !== "string") throw optionError(name, "an absolute path");
26671
+ const text2 = nonBlank(value);
26672
+ return text2 === void 0 ? void 0 : absolutePath(text2, `Option "${name}"`, env);
26673
+ }
26674
+ case "paths": {
26675
+ const list2 = stringList(value);
26676
+ if (!list2) throw optionError(name, "a list of absolute paths");
26677
+ return absolutePaths(
26678
+ list2.flatMap((item) => nonBlank(item) ?? []),
26679
+ `Option "${name}"`,
26680
+ env
26681
+ );
26682
+ }
26683
+ }
26684
+ }
26555
26685
  function settingsFromOptions(options, env, profile = PROJECT_PROFILE) {
26556
26686
  const given = options ?? {};
26557
- const unknown2 = Object.keys(given).find((name) => !PLUGIN_OPTION_NAMES.includes(name));
26687
+ const names = pluginOptionNames(profile);
26688
+ const unknown2 = Object.keys(given).find((name) => !names.includes(name));
26558
26689
  if (unknown2 !== void 0) {
26559
- throw new ToolError(
26560
- "invalid_argument",
26561
- `Unknown option "${unknown2}"; use one of ${PLUGIN_OPTION_NAMES.join(", ")}.`
26562
- );
26690
+ throw new ToolError("invalid_argument", `Unknown option "${unknown2}"; use one of ${names.join(", ")}.`);
26563
26691
  }
26564
- const resolve8 = (name, fromOption, fromText) => {
26692
+ const resolve6 = (name, fromOption, fromText) => {
26565
26693
  if (given[name] !== void 0) return fromOption(given[name]);
26566
- const text = envText(name, env, profile);
26567
- return text !== void 0 ? fromText(text) : void 0;
26568
- };
26569
- const directoryText = (name) => given[name] !== void 0 ? { text: directoryOption(name, given[name]), source: optionSource(name) } : envText(name, env, profile);
26694
+ const text2 = envText(name, env, profile);
26695
+ return text2 !== void 0 ? fromText(text2) : void 0;
26696
+ };
26697
+ const parsedOptions = {};
26698
+ for (const [name, spec] of Object.entries(profileOptions(profile))) {
26699
+ let parsed;
26700
+ if (given[name] !== void 0) {
26701
+ parsed = toolOption(name, spec, given[name], env);
26702
+ } else {
26703
+ const fromEnv = envText(name, env, profile);
26704
+ if (fromEnv !== void 0) parsed = parseOptionText(spec, fromEnv, env);
26705
+ }
26706
+ if (parsed !== void 0) parsedOptions[name] = parsed;
26707
+ }
26570
26708
  const settings = {
26571
- state_dir: resolveStateDir(directoryText("state_dir"), env, profile),
26572
- send_dirs: resolveSendDirs(resolve8("send_dirs", sendDirsOption, listedDirectories) ?? [], env),
26573
- download_dir: resolveDownloadDir(directoryText("download_dir"), env, profile),
26574
- download_max_mb: resolve8("download_max_mb", (value) => downloadMaxMb(value, optionSource("download_max_mb")), downloadMaxMbText) ?? DEFAULT_DOWNLOAD_MAX_MB
26709
+ options: parsedOptions,
26710
+ ...resolveDirectorySettings(parsedOptions, env, profile)
26575
26711
  };
26576
- const toolsets = resolve8(
26712
+ const toolsets = resolve6(
26577
26713
  "toolsets",
26578
26714
  (value) => toolsetsOption(value, profile.toolsets),
26579
- ({ text, source }) => checkToolsets(splitList(text), source, profile.toolsets)
26715
+ ({ text: text2, source }) => checkToolsets(splitList(text2), source, profile.toolsets)
26580
26716
  );
26581
26717
  if (toolsets !== void 0) settings.toolsets = toolsets;
26582
- const readOnly = resolve8("read_only", booleanOption("read_only"), ({ text, source }) => parseBoolean(text, source));
26718
+ const readOnly = resolve6("read_only", booleanOption("read_only"), ({ text: text2, source }) => parseBoolean(text2, source));
26583
26719
  if (readOnly !== void 0) settings.read_only = readOnly;
26584
- for (const name of SERVICE_OPTION_NAMES) {
26585
- const given2 = resolve8(name, booleanOption(name), ({ text, source }) => parseBoolean(text, source));
26586
- if (given2 !== void 0) settings[name] = given2;
26587
- }
26588
- const tools = resolve8("tools", listOption("tools"), ({ text }) => splitList(text));
26720
+ const tools = resolve6("tools", listOption("tools"), ({ text: text2 }) => splitList(text2));
26589
26721
  if (tools !== void 0) settings.tools = tools;
26590
- const excluded = resolve8("exclude_tools", listOption("exclude_tools"), ({ text }) => splitList(text));
26722
+ const excluded = resolve6("exclude_tools", listOption("exclude_tools"), ({ text: text2 }) => splitList(text2));
26591
26723
  if (excluded !== void 0) settings.exclude_tools = excluded;
26592
26724
  const language = given[LANGUAGE_OPTION] !== void 0 ? languageOption(given[LANGUAGE_OPTION]) : promptLanguage(void 0, env, void 0, profile);
26593
26725
  if (language !== void 0) settings.language = language;
@@ -26618,7 +26750,8 @@ function selectedTools(specs, options, deps) {
26618
26750
  downloadMaxMb: settings.download_max_mb,
26619
26751
  sendDirs: settings.send_dirs,
26620
26752
  programName: PROJECT.name,
26621
- translator: promptTranslator(deps.translations ?? {}, settings.language)
26753
+ translator: promptTranslator(deps.translations ?? {}, settings.language),
26754
+ options: settings.options
26622
26755
  };
26623
26756
  }
26624
26757
  async function runTool(spec, args, ctx, pin) {
@@ -26641,8 +26774,8 @@ function describeArguments(spec) {
26641
26774
  const { properties = {}, required: required2 = [] } = inputJsonSchema(spec);
26642
26775
  const lines = Object.entries(properties).map(([name, property]) => {
26643
26776
  const kind = required2.includes(name) ? typeName(property) : `${typeName(property)}, optional`;
26644
- const text = property.description?.replace(/\s+/g, " ").trim();
26645
- return `- ${name} (${kind})${text ? `: ${text}` : ""}`;
26777
+ const text2 = property.description?.replace(/\s+/g, " ").trim();
26778
+ return `- ${name} (${kind})${text2 ? `: ${text2}` : ""}`;
26646
26779
  });
26647
26780
  return lines.length > 0 ? `
26648
26781
 
@@ -26686,7 +26819,7 @@ function createV1Server(specs, deps = {}) {
26686
26819
 
26687
26820
  // src/opencode/skills.ts
26688
26821
  import { readFile, readdir as readdir3 } from "node:fs/promises";
26689
- import { join as join11, resolve as resolve7 } from "node:path";
26822
+ import { join as join11, resolve as resolve5 } from "node:path";
26690
26823
  import { fileURLToPath as fileURLToPath2 } from "node:url";
26691
26824
  var SKILL_FILE = "SKILL.md";
26692
26825
  var FRONTMATTER = /^---[ \t]*\r?\n([\s\S]*?\r?\n)?---[ \t]*(?:\r?\n|$)/;
@@ -26713,8 +26846,8 @@ function unquote(value) {
26713
26846
  function scalar(raw) {
26714
26847
  if (raw === "" || UNSUPPORTED_VALUE_START.test(raw)) return void 0;
26715
26848
  if (raw.startsWith('"') || raw.startsWith("'")) return unquote(raw);
26716
- const text = raw.replace(TRAILING_COMMENT, "").trim();
26717
- return text === "" ? void 0 : text;
26849
+ const text2 = raw.replace(TRAILING_COMMENT, "").trim();
26850
+ return text2 === "" ? void 0 : text2;
26718
26851
  }
26719
26852
  function readToolset(line, indent) {
26720
26853
  if (!line.startsWith(indent)) return void 0;
@@ -26749,8 +26882,8 @@ function readFrontmatter(block) {
26749
26882
  }
26750
26883
  return found;
26751
26884
  }
26752
- function parseSkill(directory, text, path) {
26753
- const match = FRONTMATTER.exec(text);
26885
+ function parseSkill(directory, text2, path) {
26886
+ const match = FRONTMATTER.exec(text2);
26754
26887
  const { name, description, toolset } = match ? readFrontmatter(match[1] ?? "") : {};
26755
26888
  const id = name ?? directory;
26756
26889
  return {
@@ -26759,7 +26892,7 @@ function parseSkill(directory, text, path) {
26759
26892
  name: id,
26760
26893
  ...description !== void 0 && { description },
26761
26894
  path,
26762
- content: match ? text.slice(match[0].length) : text
26895
+ content: match ? text2.slice(match[0].length) : text2
26763
26896
  },
26764
26897
  ...toolset !== void 0 && { toolset }
26765
26898
  };
@@ -26777,7 +26910,7 @@ async function readSkillFile(path) {
26777
26910
  }
26778
26911
  }
26779
26912
  async function readSkills(dir) {
26780
- const root = resolve7(typeof dir === "string" ? dir : fileURLToPath2(dir));
26913
+ const root = resolve5(typeof dir === "string" ? dir : fileURLToPath2(dir));
26781
26914
  let entries;
26782
26915
  try {
26783
26916
  entries = (await readdir3(root, { withFileTypes: true })).filter((entry2) => !entry2.isFile()).map((entry2) => entry2.name);
@@ -26788,9 +26921,9 @@ async function readSkills(dir) {
26788
26921
  const skills = /* @__PURE__ */ new Map();
26789
26922
  for (const directory of entries.sort()) {
26790
26923
  const path = join11(root, directory, SKILL_FILE);
26791
- const text = await readSkillFile(path);
26792
- if (text === void 0) continue;
26793
- const found = parseSkill(directory, text, path);
26924
+ const text2 = await readSkillFile(path);
26925
+ if (text2 === void 0) continue;
26926
+ const found = parseSkill(directory, text2, path);
26794
26927
  if (!skills.has(found.skill.id)) skills.set(found.skill.id, found);
26795
26928
  }
26796
26929
  return [...skills.values()];