@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/mcp.js CHANGED
@@ -2984,7 +2984,7 @@ var require_compile = __commonJS({
2984
2984
  const schOrFunc = root.refs[ref];
2985
2985
  if (schOrFunc)
2986
2986
  return schOrFunc;
2987
- let _sch = resolve7.call(this, root, ref);
2987
+ let _sch = resolve5.call(this, root, ref);
2988
2988
  if (_sch === void 0) {
2989
2989
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
2990
2990
  const { schemaId } = this.opts;
@@ -3011,7 +3011,7 @@ var require_compile = __commonJS({
3011
3011
  function sameSchemaEnv(s1, s2) {
3012
3012
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3013
3013
  }
3014
- function resolve7(root, ref) {
3014
+ function resolve5(root, ref) {
3015
3015
  let sch;
3016
3016
  while (typeof (sch = this.refs[ref]) == "string")
3017
3017
  ref = sch;
@@ -3875,7 +3875,7 @@ var require_fast_uri = __commonJS({
3875
3875
  }
3876
3876
  return uri;
3877
3877
  }
3878
- function resolve7(baseURI, relativeURI, options) {
3878
+ function resolve5(baseURI, relativeURI, options) {
3879
3879
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3880
3880
  const {
3881
3881
  parsed: baseParsed,
@@ -4266,7 +4266,7 @@ var require_fast_uri = __commonJS({
4266
4266
  var fastUri = {
4267
4267
  SCHEMES,
4268
4268
  normalize,
4269
- resolve: resolve7,
4269
+ resolve: resolve5,
4270
4270
  resolveComponent,
4271
4271
  equal,
4272
4272
  serialize,
@@ -4681,7 +4681,7 @@ var require_core = __commonJS({
4681
4681
  errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
4682
4682
  if (!errors || errors.length === 0)
4683
4683
  return "No errors";
4684
- return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text, msg) => text + separator + msg);
4684
+ return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text2, msg) => text2 + separator + msg);
4685
4685
  }
4686
4686
  $dataMetaSchema(metaSchema, keywordsJsonPointers) {
4687
4687
  const rules = this.RULES.all;
@@ -12325,7 +12325,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
12325
12325
  var NONE = 0;
12326
12326
  var ASSUMED = 1;
12327
12327
  var PROVEN = 2;
12328
- function isRecursive(inst, stack, resolve7) {
12328
+ function isRecursive(inst, stack, resolve5) {
12329
12329
  const cached2 = recursive.get(inst);
12330
12330
  if (cached2 !== void 0)
12331
12331
  return cached2 ? PROVEN : NONE;
@@ -12335,7 +12335,7 @@ function isRecursive(inst, stack, resolve7) {
12335
12335
  let result = NONE;
12336
12336
  const check2 = (child) => {
12337
12337
  if (result !== PROVEN && child?._zod) {
12338
- const answer = isRecursive(child, stack, resolve7);
12338
+ const answer = isRecursive(child, stack, resolve5);
12339
12339
  if (answer > result)
12340
12340
  result = answer;
12341
12341
  }
@@ -12346,7 +12346,7 @@ function isRecursive(inst, stack, resolve7) {
12346
12346
  const desc = Object.getOwnPropertyDescriptor(sh, key);
12347
12347
  if (spread && !desc.enumerable)
12348
12348
  continue;
12349
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve7) : NONE;
12349
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve5) : NONE;
12350
12350
  if (child > answer)
12351
12351
  answer = child;
12352
12352
  }
@@ -12410,7 +12410,7 @@ function isRecursive(inst, stack, resolve7) {
12410
12410
  break;
12411
12411
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
12412
12412
  case "lazy": {
12413
- const inner = def._cachedInner ?? (resolve7 ? inst._zod.innerType : void 0);
12413
+ const inner = def._cachedInner ?? (resolve5 ? inst._zod.innerType : void 0);
12414
12414
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
12415
12415
  break;
12416
12416
  }
@@ -16608,8 +16608,8 @@ function ko_default() {
16608
16608
  }
16609
16609
 
16610
16610
  // node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/locales/lt.js
16611
- var capitalizeFirstCharacter = (text) => {
16612
- return text.charAt(0).toUpperCase() + text.slice(1);
16611
+ var capitalizeFirstCharacter = (text2) => {
16612
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
16613
16613
  };
16614
16614
  function getUnitTypeFromNumber(number4) {
16615
16615
  const abs = Math.abs(number4);
@@ -28587,12 +28587,12 @@ var StdioServerTransport = class {
28587
28587
  this.onclose?.();
28588
28588
  }
28589
28589
  send(message) {
28590
- return new Promise((resolve7) => {
28590
+ return new Promise((resolve5) => {
28591
28591
  const json2 = serializeMessage(message);
28592
28592
  if (this._stdout.write(json2)) {
28593
- resolve7();
28593
+ resolve5();
28594
28594
  } else {
28595
- this._stdout.once("drain", resolve7);
28595
+ this._stdout.once("drain", resolve5);
28596
28596
  }
28597
28597
  });
28598
28598
  }
@@ -28639,7 +28639,7 @@ function abortError(signal) {
28639
28639
  }
28640
28640
  function exec(argv, options) {
28641
28641
  const { signal } = options;
28642
- return new Promise((resolve7, reject) => {
28642
+ return new Promise((resolve5, reject) => {
28643
28643
  if (signal?.aborted) {
28644
28644
  reject(abortError(signal));
28645
28645
  return;
@@ -28698,7 +28698,7 @@ function exec(argv, options) {
28698
28698
  }
28699
28699
  const finish = () => settle2(() => {
28700
28700
  const { code, killedBy } = exitedWith ?? { code: null, killedBy: null };
28701
- resolve7({
28701
+ resolve5({
28702
28702
  code: code ?? -1,
28703
28703
  stdout,
28704
28704
  stderr,
@@ -28872,9 +28872,9 @@ var StepErrors = class {
28872
28872
 
28873
28873
  // src/core/describe.ts
28874
28874
  var MAX_VALUE_LENGTH = 200;
28875
- function cut(text, limit) {
28876
- const points = Array.from(text);
28877
- return points.length > limit ? `${points.slice(0, limit - 1).join("")}\u2026` : text;
28875
+ function cut(text2, limit) {
28876
+ const points = Array.from(text2);
28877
+ return points.length > limit ? `${points.slice(0, limit - 1).join("")}\u2026` : text2;
28878
28878
  }
28879
28879
 
28880
28880
  // src/core/facts.ts
@@ -28889,6 +28889,10 @@ function packageRoot(bundleUrl) {
28889
28889
  return dirname(dirname(fileURLToPath(bundleUrl)));
28890
28890
  }
28891
28891
 
28892
+ // src/entry/settings.ts
28893
+ import { homedir as homedir2 } from "node:os";
28894
+ import { isAbsolute as isAbsolute3, join, resolve as resolvePath, sep as sep2 } from "node:path";
28895
+
28892
28896
  // src/project.ts
28893
28897
  var PROJECT = {
28894
28898
  /** The plugin, MCP server and program name. */
@@ -28903,89 +28907,94 @@ var PROJECT = {
28903
28907
  toolsets: ["account", "chats", "sync", "files", "sends", "actions"],
28904
28908
  /** The toolsets on when a setting names none. */
28905
28909
  defaultToolsets: ["account", "chats", "sync", "sends"],
28910
+ /** The package's own settings, which the tools themselves read: each snake_case name and its type, with bounds when it needs them. */
28911
+ options: {
28912
+ /** Where every account's state lives. */
28913
+ state_dir: "path",
28914
+ /** Where received files are saved. */
28915
+ download_dir: "path",
28916
+ /** The largest received file a call may download, in megabytes. */
28917
+ download_max_mb: { type: "number", integer: true, min: 1, max: 2e3 },
28918
+ /** The directories files may be sent from, besides the working directory. */
28919
+ send_dirs: "paths",
28920
+ /** Whether messages that disappear are kept; read by the sync service's commands. */
28921
+ keep_disappearing: "boolean",
28922
+ /** Whether channels are listed and their messages received; read by the sync service's commands. */
28923
+ channels: "boolean"
28924
+ },
28906
28925
  /** The environment variables the tools read besides the settings, which Codex forwards to the server after them. */
28907
28926
  toolVariables: []
28908
28927
  };
28909
28928
 
28910
- // src/entry/download-dir.ts
28911
- import { resolve as resolve2 } from "node:path";
28912
-
28913
28929
  // src/entry/directory.ts
28914
28930
  import { homedir } from "node:os";
28915
- import { isAbsolute as isAbsolute2, join, resolve, sep as sep2 } from "node:path";
28931
+ import { isAbsolute as isAbsolute2, resolve } from "node:path";
28932
+ var DOWNLOADS_DIR = "Downloads";
28933
+ var DEFAULT_DOWNLOAD_MAX_MB = 100;
28916
28934
  function homeDir(env) {
28917
28935
  const envHome = env.HOME;
28918
28936
  if (envHome && isAbsolute2(envHome)) return envHome;
28919
28937
  return homedir();
28920
28938
  }
28921
- function resolveGivenDirectory(given, home) {
28922
- const text = given.text.trim();
28923
- const expanded = text === "~" ? home : text.startsWith("~/") || text.startsWith(`~${sep2}`) ? join(home, text.slice(2)) : text;
28924
- if (!text || !isAbsolute2(expanded)) {
28925
- throw new ToolError("invalid_argument", `${given.source} must be an absolute path.`);
28926
- }
28927
- return resolve(expanded);
28939
+ function defaultStateDir(name, env, home) {
28940
+ const xdg = env.XDG_STATE_HOME;
28941
+ return xdg && isAbsolute2(xdg) ? resolve(xdg, name) : resolve(home, ".local", "state", name);
28928
28942
  }
28929
-
28930
- // src/entry/download-dir.ts
28931
- var DOWNLOADS_DIR = "Downloads";
28932
- function resolveDownloadDir(given, env, profile) {
28943
+ function text(value) {
28944
+ return typeof value === "string" ? value : void 0;
28945
+ }
28946
+ function resolveDirectorySettings(options, env, profile) {
28933
28947
  const home = homeDir(env);
28934
- return given !== void 0 ? resolveGivenDirectory(given, home) : resolve2(home, DOWNLOADS_DIR, profile.name);
28948
+ const maxMb = options.download_max_mb;
28949
+ const sendDirs = options.send_dirs;
28950
+ return {
28951
+ state_dir: text(options.state_dir) ?? defaultStateDir(profile.name, env, home),
28952
+ download_dir: text(options.download_dir) ?? resolve(home, DOWNLOADS_DIR, profile.name),
28953
+ download_max_mb: typeof maxMb === "number" ? maxMb : DEFAULT_DOWNLOAD_MAX_MB,
28954
+ send_dirs: Array.isArray(sendDirs) ? [...sendDirs] : []
28955
+ };
28935
28956
  }
28936
28957
 
28937
28958
  // src/entry/profile.ts
28938
28959
  var PROJECT_PROFILE = {
28939
28960
  name: PROJECT.name,
28940
28961
  envPrefix: PROJECT.envPrefix,
28941
- toolsets: { names: PROJECT.toolsets, defaults: PROJECT.defaultToolsets }
28962
+ toolsets: { names: PROJECT.toolsets, defaults: PROJECT.defaultToolsets },
28963
+ options: PROJECT.options
28942
28964
  };
28943
28965
 
28944
- // src/entry/send-dirs.ts
28945
- function resolveSendDirs(given, env) {
28946
- const home = homeDir(env);
28947
- return given.map((directory) => resolveGivenDirectory(directory, home));
28948
- }
28949
-
28950
- // src/entry/state-dir.ts
28951
- import { isAbsolute as isAbsolute3, resolve as resolve3 } from "node:path";
28952
- function resolveStateDir(given, env, profile) {
28953
- const home = homeDir(env);
28954
- if (given !== void 0) return resolveGivenDirectory(given, home);
28955
- const xdg = env.XDG_STATE_HOME;
28956
- if (xdg && isAbsolute3(xdg)) {
28957
- return resolve3(xdg, profile.name);
28958
- }
28959
- return resolve3(home, ".local", "state", profile.name);
28960
- }
28961
-
28962
28966
  // src/entry/settings.ts
28963
28967
  var CONFIRM_MODES = ["harness", "elicitation"];
28964
28968
  var ALL_TOOLSETS = "all";
28965
28969
  var TRUE_VALUES = ["1", "true"];
28966
28970
  var FALSE_VALUES = ["0", "false"];
28967
- var DEFAULT_DOWNLOAD_MAX_MB = 100;
28968
- var MAX_DOWNLOAD_MB = 2e3;
28969
28971
  var OPTIONS = {
28970
28972
  toolsets: { flag: "--toolsets", envSuffix: "TOOLSETS" },
28971
28973
  read_only: { flag: "--read-only", envSuffix: "READ_ONLY", boolean: true },
28972
28974
  tools: { flag: "--tools", envSuffix: "TOOLS" },
28973
28975
  exclude_tools: { flag: "--exclude-tools", envSuffix: "EXCLUDE_TOOLS" },
28974
- confirm: { flag: "--confirm", envSuffix: "CONFIRM" },
28975
- state_dir: { flag: "--state-dir", envSuffix: "STATE_DIR" },
28976
- download_dir: { flag: "--download-dir", envSuffix: "DOWNLOAD_DIR" },
28977
- download_max_mb: { flag: "--download-max-mb", envSuffix: "DOWNLOAD_MAX_MB" },
28978
- send_dirs: { flag: "--send-dirs", envSuffix: "SEND_DIRS" },
28979
- keep_disappearing: { flag: "--keep-disappearing", envSuffix: "KEEP_DISAPPEARING", boolean: true },
28980
- channels: { flag: "--channels", envSuffix: "CHANNELS", boolean: true }
28976
+ confirm: { flag: "--confirm", envSuffix: "CONFIRM" }
28981
28977
  };
28982
- var SERVICE_OPTION_NAMES = ["keep_disappearing", "channels"];
28978
+ function profileOptions(profile) {
28979
+ return profile.options ?? {};
28980
+ }
28981
+ function settingOptions(profile) {
28982
+ const toolOptions = Object.entries(profileOptions(profile)).map(([name, spec]) => [
28983
+ name,
28984
+ {
28985
+ flag: `--${name.replace(/_/g, "-")}`,
28986
+ envSuffix: name.toUpperCase(),
28987
+ ...optionType(spec) === "boolean" && { boolean: true }
28988
+ }
28989
+ ]);
28990
+ return { ...OPTIONS, ...Object.fromEntries(toolOptions) };
28991
+ }
28983
28992
  function envName(name, profile) {
28984
- return `${profile.envPrefix}_${OPTIONS[name].envSuffix}`;
28993
+ return `${profile.envPrefix}_${settingOptions(profile)[name].envSuffix}`;
28985
28994
  }
28986
- function readFlags(argv) {
28995
+ function readFlags(argv, profile) {
28987
28996
  const byFlag = new Map(
28988
- Object.keys(OPTIONS).map((name) => [OPTIONS[name].flag, { name, ...OPTIONS[name] }])
28997
+ Object.entries(settingOptions(profile)).map(([name, option]) => [option.flag, { name, ...option }])
28989
28998
  );
28990
28999
  const given = {};
28991
29000
  for (let i = 0; i < argv.length; i++) {
@@ -29020,9 +29029,9 @@ function checkToolsets(names, source, toolsets) {
29020
29029
  return names.length ? names : void 0;
29021
29030
  }
29022
29031
  function parseBoolean(value, source) {
29023
- const text = value.trim().toLowerCase();
29024
- if (TRUE_VALUES.includes(text)) return true;
29025
- if (FALSE_VALUES.includes(text)) return false;
29032
+ const text2 = value.trim().toLowerCase();
29033
+ if (TRUE_VALUES.includes(text2)) return true;
29034
+ if (FALSE_VALUES.includes(text2)) return false;
29026
29035
  throw new ToolError(
29027
29036
  "invalid_argument",
29028
29037
  `Unknown value "${value}" in ${source}; use one of ${[...TRUE_VALUES, ...FALSE_VALUES].join(", ")}.`
@@ -29038,44 +29047,106 @@ function parseConfirm(value, source) {
29038
29047
  }
29039
29048
  return mode;
29040
29049
  }
29041
- function listedDirectories(given) {
29042
- return given === void 0 ? [] : splitList(given.text).map((text) => ({ text, source: given.source }));
29043
- }
29044
- function downloadMaxMb(value, source) {
29045
- if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > MAX_DOWNLOAD_MB) {
29046
- throw new ToolError("invalid_argument", `${source} must be an integer from 1 to ${MAX_DOWNLOAD_MB}.`);
29050
+ function parseNumber(value, source) {
29051
+ const trimmed = value.trim();
29052
+ const parsed = trimmed === "" ? Number.NaN : Number(trimmed);
29053
+ if (!Number.isFinite(parsed)) {
29054
+ throw new ToolError("invalid_argument", `Unknown number "${value}" in ${source}; use a finite number.`);
29047
29055
  }
29048
- return value;
29056
+ return parsed;
29049
29057
  }
29050
- function downloadMaxMbText({ text, source }) {
29051
- const trimmed = text.trim();
29052
- return downloadMaxMb(/^\d+$/.test(trimmed) ? Number(trimmed) : Number.NaN, source);
29058
+ function absolutePath(text2, source, env) {
29059
+ const home = env.HOME !== void 0 && isAbsolute3(env.HOME) ? env.HOME : homedir2();
29060
+ const expanded = text2 === "~" || text2.startsWith("~/") || text2.startsWith(`~${sep2}`) ? join(home, text2.slice(1)) : text2;
29061
+ if (!isAbsolute3(expanded)) throw new ToolError("invalid_argument", `${source} must be an absolute path.`);
29062
+ return resolvePath(expanded);
29053
29063
  }
29054
- function downloadSettings(given, env, profile) {
29055
- const value = given ?? ((name) => envText(name, env, profile));
29056
- const max = value("download_max_mb");
29057
- return {
29058
- download_dir: resolveDownloadDir(value("download_dir"), env, profile),
29059
- download_max_mb: max === void 0 ? DEFAULT_DOWNLOAD_MAX_MB : downloadMaxMbText(max)
29060
- };
29064
+ function absolutePaths(items, source, env) {
29065
+ return items.length > 0 ? items.map((item) => absolutePath(item, source, env)) : void 0;
29061
29066
  }
29062
29067
  function envText(name, env, profile) {
29063
29068
  const variable = envName(name, profile);
29064
- const text = env[variable];
29065
- return text === void 0 || text.trim() === "" ? void 0 : { text, source: variable };
29069
+ const text2 = env[variable];
29070
+ return text2 === void 0 || text2.trim() === "" ? void 0 : { text: text2, source: variable };
29066
29071
  }
29067
29072
  function flagOrEnvText(flags, name, env, profile) {
29068
29073
  const flagged = flags[name];
29069
29074
  return flagged !== void 0 ? { text: flagged, source: OPTIONS[name].flag } : envText(name, env, profile);
29070
29075
  }
29076
+ function optionType(spec) {
29077
+ return typeof spec === "string" ? spec : spec.type;
29078
+ }
29079
+ function itemCount(count) {
29080
+ return count === 1 ? "1 item" : `${count} items`;
29081
+ }
29082
+ function numberBoundsError({ integer: integer2, min, max }, source) {
29083
+ 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}` : "";
29084
+ return new ToolError("invalid_argument", `${source} must be ${integer2 ? "a whole number" : "a number"}${range}.`);
29085
+ }
29086
+ function countBoundsError({ min, max }, source) {
29087
+ const range = min !== void 0 && max !== void 0 ? `from ${min} to ${max} items` : min !== void 0 ? `at least ${itemCount(min)}` : `at most ${itemCount(max)}`;
29088
+ return new ToolError("invalid_argument", `${source} must list ${range}.`);
29089
+ }
29090
+ function withinBounds(spec, value, source) {
29091
+ if (value === void 0 || typeof spec === "string") return value;
29092
+ const bounds = spec;
29093
+ const { integer: integer2, min, max } = bounds;
29094
+ if (typeof value === "number") {
29095
+ if (integer2 && !Number.isInteger(value) || min !== void 0 && value < min || max !== void 0 && value > max) {
29096
+ throw numberBoundsError(bounds, source);
29097
+ }
29098
+ } else if (typeof value === "object" && (min !== void 0 && value.length < min || max !== void 0 && value.length > max)) {
29099
+ throw countBoundsError(bounds, source);
29100
+ }
29101
+ return value;
29102
+ }
29103
+ function parseOptionText(spec, given, env) {
29104
+ return withinBounds(spec, parseTypedText(optionType(spec), given, env), given.source);
29105
+ }
29106
+ function parseTypedText(kind, given, env) {
29107
+ switch (kind) {
29108
+ case "text":
29109
+ return nonBlank(given.text);
29110
+ case "boolean":
29111
+ return parseBoolean(given.text, given.source);
29112
+ case "number":
29113
+ return parseNumber(given.text, given.source);
29114
+ case "list": {
29115
+ const items = splitList(given.text);
29116
+ return items.length > 0 ? items : void 0;
29117
+ }
29118
+ case "path": {
29119
+ const text2 = nonBlank(given.text);
29120
+ return text2 === void 0 ? void 0 : absolutePath(text2, given.source, env);
29121
+ }
29122
+ case "paths":
29123
+ return absolutePaths(splitList(given.text), given.source, env);
29124
+ }
29125
+ }
29126
+ function optionsFrom(flags, env, profile) {
29127
+ const options = {};
29128
+ for (const [name, spec] of Object.entries(profileOptions(profile))) {
29129
+ if (flags[name] !== void 0) {
29130
+ const parsed = parseOptionText(spec, { text: flags[name], source: settingOptions(profile)[name].flag }, env);
29131
+ if (parsed !== void 0) options[name] = parsed;
29132
+ continue;
29133
+ }
29134
+ const fromEnv = envText(name, env, profile);
29135
+ if (fromEnv !== void 0) {
29136
+ const parsed = parseOptionText(spec, fromEnv, env);
29137
+ if (parsed !== void 0) options[name] = parsed;
29138
+ }
29139
+ }
29140
+ return options;
29141
+ }
29071
29142
  function parseSettings(argv, env, profile = PROJECT_PROFILE) {
29072
- const flags = readFlags(argv);
29143
+ const flags = readFlags(argv, profile);
29073
29144
  const value = (name) => flagOrEnvText(flags, name, env, profile);
29145
+ const options = optionsFrom(flags, env, profile);
29074
29146
  const settings = {
29075
29147
  confirm: "harness",
29076
- state_dir: resolveStateDir(value("state_dir"), env, profile),
29077
- ...downloadSettings(value, env, profile),
29078
- send_dirs: resolveSendDirs(listedDirectories(value("send_dirs")), env)
29148
+ options,
29149
+ ...resolveDirectorySettings(options, env, profile)
29079
29150
  };
29080
29151
  const toolsets = value("toolsets");
29081
29152
  if (toolsets) settings.toolsets = checkToolsets(splitList(toolsets.text), toolsets.source, profile.toolsets);
@@ -29087,25 +29158,12 @@ function parseSettings(argv, env, profile = PROJECT_PROFILE) {
29087
29158
  if (excluded) settings.exclude_tools = splitList(excluded.text);
29088
29159
  const confirm = value("confirm");
29089
29160
  if (confirm) settings.confirm = parseConfirm(confirm.text.trim(), confirm.source);
29090
- for (const name of SERVICE_OPTION_NAMES) {
29091
- const given = value(name);
29092
- if (given) settings[name] = parseBoolean(given.text, given.source);
29093
- }
29094
29161
  return settings;
29095
29162
  }
29096
- var TOOL_OPTION_NAMES = [
29097
- "toolsets",
29098
- "read_only",
29099
- "tools",
29100
- "exclude_tools",
29101
- "state_dir",
29102
- "download_dir",
29103
- "download_max_mb",
29104
- "send_dirs",
29105
- ...SERVICE_OPTION_NAMES
29106
- ];
29107
- var LANGUAGE_OPTION = "language";
29108
- var PLUGIN_OPTION_NAMES = [...TOOL_OPTION_NAMES, LANGUAGE_OPTION];
29163
+ function nonBlank(text2) {
29164
+ const trimmed = text2?.trim();
29165
+ return trimmed === "" ? void 0 : trimmed;
29166
+ }
29109
29167
 
29110
29168
  // src/core/account/read.ts
29111
29169
  import { existsSync as existsSync2, statSync } from "node:fs";
@@ -29121,8 +29179,8 @@ function quoteBadLabel(value) {
29121
29179
  const truncated = value.length > MAX_BAD_LABEL_PREVIEW ? `${value.slice(0, MAX_BAD_LABEL_PREVIEW)}\u2026` : value;
29122
29180
  return JSON.stringify(truncated);
29123
29181
  }
29124
- function isLabel(text) {
29125
- return typeof text === "string" && LABEL_PATTERN.test(text);
29182
+ function isLabel(text2) {
29183
+ return typeof text2 === "string" && LABEL_PATTERN.test(text2);
29126
29184
  }
29127
29185
  function checkLabel(label) {
29128
29186
  if (!isLabel(label)) {
@@ -29341,6 +29399,7 @@ function lockHolder(path) {
29341
29399
  }
29342
29400
 
29343
29401
  // src/core/time.ts
29402
+ var MS_PER_S = 1e3;
29344
29403
  var MS_PER_MINUTE = 6e4;
29345
29404
  var MINUTES_PER_HOUR = 60;
29346
29405
  var localOffsetMinutes = (ms) => -new Date(ms).getTimezoneOffset();
@@ -29360,10 +29419,10 @@ var MILLISECOND_DIGITS = 3;
29360
29419
  var MIN_YEAR = 1e3;
29361
29420
  var MAX_HOUR = 23;
29362
29421
  var MAX_MINUTE = 59;
29363
- function badTime(text, name) {
29422
+ function badTime(text2, name) {
29364
29423
  return new ToolError(
29365
29424
  "invalid_argument",
29366
- `${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.`
29425
+ `${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.`
29367
29426
  );
29368
29427
  }
29369
29428
  function isCalendarDay(year, month, day) {
@@ -29371,21 +29430,21 @@ function isCalendarDay(year, month, day) {
29371
29430
  date5.setUTCFullYear(year, month - 1, day);
29372
29431
  return date5.getUTCFullYear() === year && date5.getUTCMonth() === month - 1 && date5.getUTCDate() === day;
29373
29432
  }
29374
- function parseTimeArgument(text, name) {
29375
- const match = TIME_PATTERN.exec(text.trim());
29376
- if (!match) throw badTime(text, name);
29433
+ function parseTimeArgument(text2, name) {
29434
+ const match = TIME_PATTERN.exec(text2.trim());
29435
+ if (!match) throw badTime(text2, name);
29377
29436
  const [, year, month, day, hour = "0", minute = "0", second = "0", fraction = "", zulu, sign, offsetHour, offsetMin] = match;
29378
29437
  const [y, mon, d, h, mi, s] = [year, month, day, hour, minute, second].map(Number);
29379
29438
  const ms = Number(fraction.padEnd(MILLISECOND_DIGITS, "0").slice(0, MILLISECOND_DIGITS));
29380
29439
  if (y < MIN_YEAR || !isCalendarDay(y, mon, d) || h > MAX_HOUR || mi > MAX_MINUTE || s > MAX_MINUTE) {
29381
- throw badTime(text, name);
29440
+ throw badTime(text2, name);
29382
29441
  }
29383
29442
  if (sign === void 0 && zulu === void 0) return new Date(y, mon - 1, d, h, mi, s, ms).getTime();
29384
29443
  const wall = Date.UTC(y, mon - 1, d, h, mi, s, ms);
29385
29444
  if (zulu) return wall;
29386
29445
  const offsetH = Number(offsetHour);
29387
29446
  const offsetM = Number(offsetMin);
29388
- if (offsetH > MAX_HOUR || offsetM > MAX_MINUTE) throw badTime(text, name);
29447
+ if (offsetH > MAX_HOUR || offsetM > MAX_MINUTE) throw badTime(text2, name);
29389
29448
  const offset = (offsetH * MINUTES_PER_HOUR + offsetM) * MS_PER_MINUTE;
29390
29449
  return sign === "-" ? wall + offset : wall - offset;
29391
29450
  }
@@ -29604,23 +29663,23 @@ function encodeCursor(value) {
29604
29663
  function notACursor(name) {
29605
29664
  return new ToolError("invalid_argument", `${name} is not a cursor this tool returned.`);
29606
29665
  }
29607
- function decodeCursor(text, name) {
29608
- const bytes = Buffer3.from(text, "base64url");
29609
- if (bytes.toString("base64url") !== text) throw notACursor(name);
29666
+ function decodeCursor(text2, name) {
29667
+ const bytes = Buffer3.from(text2, "base64url");
29668
+ if (bytes.toString("base64url") !== text2) throw notACursor(name);
29610
29669
  try {
29611
29670
  return JSON.parse(bytes.toString("utf8"));
29612
29671
  } catch {
29613
29672
  throw notACursor(name);
29614
29673
  }
29615
29674
  }
29616
- function readCursor(text, name, schema) {
29617
- const parsed = schema.safeParse(decodeCursor(text, name));
29675
+ function readCursor(text2, name, schema) {
29676
+ const parsed = schema.safeParse(decodeCursor(text2, name));
29618
29677
  if (!parsed.success) throw notACursor(name);
29619
29678
  return parsed.data;
29620
29679
  }
29621
29680
  var HIGH_SURROGATE = /[\uD800-\uDBFF]$/;
29622
- function cutText(text, keep) {
29623
- return text.length <= keep ? text : text.slice(0, keep).replace(HIGH_SURROGATE, "");
29681
+ function cutText(text2, keep) {
29682
+ return text2.length <= keep ? text2 : text2.slice(0, keep).replace(HIGH_SURROGATE, "");
29624
29683
  }
29625
29684
  function jsonLength(value) {
29626
29685
  return JSON.stringify(value).length;
@@ -29710,15 +29769,15 @@ var ERROR_CODES = {
29710
29769
  function isErrorCode(value) {
29711
29770
  return typeof value === "string" && Object.hasOwn(ERROR_CODES, value);
29712
29771
  }
29713
- function maskPhoneNumbers(text) {
29714
- return text.replace(PHONE_NUMBER, "[number]");
29772
+ function maskPhoneNumbers(text2) {
29773
+ return text2.replace(PHONE_NUMBER, "[number]");
29715
29774
  }
29716
29775
  function cut2(message) {
29717
29776
  return maskPhoneNumbers(message).trim().slice(0, MAX_DETAIL_LENGTH);
29718
29777
  }
29719
29778
  function detail(message) {
29720
- const text = cut2(message);
29721
- return text ? `: ${text}` : "";
29779
+ const text2 = cut2(message);
29780
+ return text2 ? `: ${text2}` : "";
29722
29781
  }
29723
29782
  function afterWriteError(method, effect, reason) {
29724
29783
  return effect === "write" ? new ToolError("uncertain", `${method} may have taken effect (${reason}). ${UNCERTAIN_ADVICE}`) : new ToolError("upstream_error", `${method} failed (${reason}).`);
@@ -29896,19 +29955,19 @@ var ServiceConnection = class _ServiceConnection {
29896
29955
  socket.on("error", (error62) => {
29897
29956
  this.endReason ??= `the connection to the service broke${error62.code ? ` (${error62.code})` : ""}`;
29898
29957
  });
29899
- this.closed = new Promise((resolve7) => {
29958
+ this.closed = new Promise((resolve5) => {
29900
29959
  socket.once("close", () => {
29901
29960
  this.endReason ??= "the service closed the connection";
29902
29961
  const lost = { lost: this.endReason };
29903
29962
  for (const settle2 of [...this.pending.values()]) settle2(lost);
29904
- resolve7();
29963
+ resolve5();
29905
29964
  });
29906
29965
  });
29907
29966
  }
29908
29967
  /** Connects to the service's socket; rejects as `upstream_error` when nothing is listening there. */
29909
29968
  static connect(socketPath, options = {}) {
29910
29969
  const { timeoutMs = DEFAULT_CONNECT_TIMEOUT_MS, signal, maxLineLength = DEFAULT_MAX_LINE_LENGTH } = options;
29911
- return new Promise((resolve7, reject) => {
29970
+ return new Promise((resolve5, reject) => {
29912
29971
  if (signal?.aborted) {
29913
29972
  reject(abortError(signal));
29914
29973
  return;
@@ -29930,7 +29989,7 @@ var ServiceConnection = class _ServiceConnection {
29930
29989
  };
29931
29990
  const onConnect = () => {
29932
29991
  detach();
29933
- resolve7(new _ServiceConnection(socket, maxLineLength));
29992
+ resolve5(new _ServiceConnection(socket, maxLineLength));
29934
29993
  };
29935
29994
  const onFailure = () => stop(new ToolError("upstream_error", NOT_RUNNING_MESSAGE));
29936
29995
  const onAbort = () => stop(abortError(signal));
@@ -29953,7 +30012,7 @@ var ServiceConnection = class _ServiceConnection {
29953
30012
  */
29954
30013
  call(method, params, options) {
29955
30014
  const { effect, timeoutMs = DEFAULT_CALL_TIMEOUT_MS, signal } = options;
29956
- return new Promise((resolve7, reject) => {
30015
+ return new Promise((resolve5, reject) => {
29957
30016
  if (signal?.aborted) {
29958
30017
  reject(abortError(signal));
29959
30018
  return;
@@ -29982,7 +30041,7 @@ var ServiceConnection = class _ServiceConnection {
29982
30041
  clearTimeout(timer);
29983
30042
  signal?.removeEventListener("abort", onAbort);
29984
30043
  try {
29985
- resolve7(produce());
30044
+ resolve5(produce());
29986
30045
  } catch (error62) {
29987
30046
  reject(error62);
29988
30047
  }
@@ -30055,7 +30114,6 @@ async function callService(socketPath, method, params, options) {
30055
30114
  // src/core/chat/live.ts
30056
30115
  var REFRESH_TIMEOUT_MS = 12e4;
30057
30116
  var FETCH_TIMEOUT_MS = 18e4;
30058
- var MS_PER_S = 1e3;
30059
30117
  function requestHistory(socket, params, signal) {
30060
30118
  return callService(socket, SERVICE_REQUESTS.history, params, { effect: "read", signal });
30061
30119
  }
@@ -30183,113 +30241,6 @@ var messageEntry = external_exports.object({
30183
30241
  private_note: external_exports.string().optional()
30184
30242
  });
30185
30243
 
30186
- // src/core/chat/schema.ts
30187
- var METHODS = ["list", "view", "topics", "search", "around", "fetch"];
30188
- var GENERAL_TOPIC2 = 1;
30189
- var LIMITS = {
30190
- list: { default: 30, max: 200 },
30191
- view: { default: 50, max: 300 },
30192
- topics: { default: 30, max: TOPICS_LIMIT },
30193
- search: { default: 30, max: 200 }
30194
- };
30195
- var AROUND_COUNT = { default: 5, max: AROUND_LIMIT };
30196
- var FETCH_PAGES = { default: 1, max: FETCH_PAGE_LIMIT };
30197
- var accountArgument = external_exports.string().optional().describe("The account label; may be left out when only one account is logged in.");
30198
- var input3 = external_exports.object({
30199
- method: external_exports.enum(METHODS).describe(
30200
- "`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."
30201
- ),
30202
- account: accountArgument,
30203
- query: external_exports.string().optional().describe(
30204
- "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."
30205
- ),
30206
- unread: external_exports.boolean().optional().describe("list: only chats with unread messages."),
30207
- last: external_exports.boolean().optional().describe("list: add each chat's newest mirrored message as `last_message`."),
30208
- refresh: external_exports.boolean().optional().describe(
30209
- "list: first take the chat list from Telegram (needs the sync service), then read it, and report when in `refreshed_at`."
30210
- ),
30211
- chat: external_exports.string().optional().describe(
30212
- "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."
30213
- ),
30214
- topic: external_exports.number().int().positive().optional().describe(
30215
- `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.`
30216
- ),
30217
- after: external_exports.string().optional().describe(
30218
- "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."
30219
- ),
30220
- before: external_exports.string().optional().describe("view, search: only messages before this time (exclusive); the same forms as `after`."),
30221
- limit: external_exports.number().int().optional().describe(
30222
- `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.`
30223
- ),
30224
- cursor: external_exports.string().optional().describe("The `next` of the previous result, to continue where it stopped."),
30225
- id: external_exports.number().int().positive().optional().describe("around (required): the id of the message to read around."),
30226
- before_count: external_exports.number().int().optional().describe(
30227
- `around: how many older messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
30228
- ),
30229
- after_count: external_exports.number().int().optional().describe(
30230
- `around: how many newer messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
30231
- ),
30232
- pages: external_exports.number().int().optional().describe(
30233
- `fetch: how many pages of ${FETCH_PAGE_SIZE} older messages to add, default ${FETCH_PAGES.default}, at most ${FETCH_PAGES.max}.`
30234
- )
30235
- });
30236
- var nameCut = external_exports.literal(true).optional().describe("Set when the name was longer than the most a result carries and was cut.");
30237
- var chatEntry2 = external_exports.object({
30238
- id: external_exports.string().describe("The chat id: Telegram's signed id as text, such as `123456789` or `-1001234567890`."),
30239
- kind: external_exports.enum(CHAT_KINDS).describe("`self` is Saved Messages."),
30240
- name: external_exports.string().nullable().describe("The person's or chat's name; null when none is known."),
30241
- name_cut: nameCut,
30242
- username: external_exports.string().optional(),
30243
- mirrored: external_exports.boolean().describe("Whether the mirror keeps this chat's messages."),
30244
- unread: external_exports.number().int().describe("Incoming messages not read yet."),
30245
- last_activity_at: localTimeSchema.nullable().describe("The latest message's time, in local time with offset; null when none is known."),
30246
- forum: external_exports.literal(true).optional().describe("Set for a supergroup with topics."),
30247
- archived: external_exports.literal(true).optional(),
30248
- left: external_exports.literal(true).optional().describe("Set for a group or channel the user has left or been removed from."),
30249
- can_send: external_exports.literal(false).optional().describe("Set when the user is known not to be able to write here."),
30250
- auto_delete_seconds: external_exports.number().int().optional().describe("The chat's auto-delete period, when on."),
30251
- last_message: messageEntry.nullable().optional().describe("With `last`: the newest mirrored message of the chat, or null when none is mirrored.")
30252
- });
30253
- var topicEntry = external_exports.object({
30254
- id: external_exports.number().int().describe(`The topic id, to pass as \`topic\` to \`view\`; ${GENERAL_TOPIC2} is the General topic.`),
30255
- title: external_exports.string().nullable().describe("The topic's title; null when none is known."),
30256
- closed: external_exports.literal(true).optional().describe("Set for a topic that is closed."),
30257
- pinned: external_exports.literal(true).optional().describe("Set for a topic that is pinned."),
30258
- unread: external_exports.number().int().describe("Messages in it not read yet."),
30259
- last_activity_at: localTimeSchema.nullable().describe("The newest message's time, in local time with offset; null when none is known.")
30260
- });
30261
- var output3 = external_exports.object({
30262
- chats: external_exports.array(chatEntry2).optional().describe("list: the chats, newest activity first."),
30263
- refreshed_at: localTimeSchema.optional().describe("list with `refresh`: when the chat list was taken from Telegram, in local time with offset."),
30264
- chat: chatEntry2.optional().describe("view, topics, around, fetch: the chat read."),
30265
- messages: external_exports.array(messageEntry).optional().describe("view, around: the messages, oldest first. search: the hits, newest first."),
30266
- live: external_exports.literal(true).optional().describe(
30267
- "view, around: set when the chat is not mirrored, so the messages were read from Telegram and stored nowhere."
30268
- ),
30269
- topics: external_exports.array(topicEntry).optional().describe("topics: the topics, most recently active first."),
30270
- anchor: external_exports.number().int().optional().describe("around: the id of the message read around, among `messages`."),
30271
- trimmed_before: external_exports.number().int().optional().describe(
30272
- "around: how many older messages were left out, farthest first, to keep the result a size that can be read."
30273
- ),
30274
- trimmed_after: external_exports.number().int().optional().describe(
30275
- "around: how many newer messages were left out, farthest first, to keep the result a size that can be read."
30276
- ),
30277
- scanned_to: localTimeSchema.optional().describe(
30278
- "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."
30279
- ),
30280
- scope: external_exports.string().optional().describe("search: what the search covers and what it does not."),
30281
- fetched: external_exports.object({
30282
- added: external_exports.number().int().describe("How many messages the mirror gained."),
30283
- pages: external_exports.number().int().describe("How many pages were read."),
30284
- complete: external_exports.boolean().describe("Whether Telegram has nothing older for this chat."),
30285
- oldest_at: localTimeSchema.nullable().describe(
30286
- "The time of the oldest message the mirror now holds of this chat, in local time with offset; null when none."
30287
- )
30288
- }).optional().describe("fetch: what the history fetch stored."),
30289
- next: external_exports.string().optional().describe("Pass back as `cursor` to continue; absent when there is no more."),
30290
- note: external_exports.string().describe("Says that what the result carries is data written by other people.")
30291
- });
30292
-
30293
30244
  // src/core/chat/message-rows.ts
30294
30245
  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";
30295
30246
  function liveMessages(now) {
@@ -30297,7 +30248,7 @@ function liveMessages(now) {
30297
30248
  }
30298
30249
  function inTopic(topic) {
30299
30250
  return {
30300
- condition: topic === GENERAL_TOPIC2 ? "(topic IS NULL OR topic = ?)" : "topic = ?",
30251
+ condition: topic === GENERAL_TOPIC ? "(topic IS NULL OR topic = ?)" : "topic = ?",
30301
30252
  params: [topic]
30302
30253
  };
30303
30254
  }
@@ -30410,10 +30361,10 @@ function replyTo(parsed, related) {
30410
30361
  if (reply.chat !== void 0) entry2.chat = reply.chat;
30411
30362
  if (reply.topic !== void 0) entry2.topic = reply.topic;
30412
30363
  if (reply.quote !== void 0) entry2.quote = reply.quote;
30413
- const text = repliedText(parsed, related)?.text;
30414
- if (text !== void 0) {
30415
- entry2.text = cutText(text, REPLY_TEXT_LENGTH);
30416
- if (entry2.text.length < text.length) entry2.text_cut = true;
30364
+ const text2 = repliedText(parsed, related)?.text;
30365
+ if (text2 !== void 0) {
30366
+ entry2.text = cutText(text2, REPLY_TEXT_LENGTH);
30367
+ if (entry2.text.length < text2.length) entry2.text_cut = true;
30417
30368
  }
30418
30369
  return entry2;
30419
30370
  }
@@ -30482,25 +30433,25 @@ function texts(entry2) {
30482
30433
  ];
30483
30434
  }
30484
30435
  function messageTextLength(entry2) {
30485
- return texts(entry2).reduce((total, text) => total + text.length, 0);
30436
+ return texts(entry2).reduce((total, text2) => total + text2.length, 0);
30486
30437
  }
30487
30438
  function cutMessage(entry2, keep) {
30488
30439
  let room = keep;
30489
30440
  let cut3 = false;
30490
- const take = (text) => {
30491
- const kept = cutText(text, room);
30492
- room = Math.max(0, room - text.length);
30493
- if (kept.length < text.length) cut3 = true;
30441
+ const take = (text2) => {
30442
+ const kept = cutText(text2, room);
30443
+ room = Math.max(0, room - text2.length);
30444
+ if (kept.length < text2.length) cut3 = true;
30494
30445
  return kept;
30495
30446
  };
30496
30447
  const cutEntry = { ...entry2 };
30497
30448
  if (entry2.text !== void 0) cutEntry.text = take(entry2.text);
30498
30449
  if (entry2.reply_to) {
30499
- const { quote, text } = entry2.reply_to;
30450
+ const { quote, text: text2 } = entry2.reply_to;
30500
30451
  cutEntry.reply_to = {
30501
30452
  ...entry2.reply_to,
30502
30453
  ...quote === void 0 ? {} : { quote: take(quote) },
30503
- ...text === void 0 ? {} : { text: take(text) }
30454
+ ...text2 === void 0 ? {} : { text: take(text2) }
30504
30455
  };
30505
30456
  }
30506
30457
  if (entry2.earlier_versions)
@@ -30522,6 +30473,112 @@ var cuttableMessage = {
30522
30473
  drop: dropMetadata
30523
30474
  };
30524
30475
 
30476
+ // src/core/chat/schema.ts
30477
+ var METHODS = ["list", "view", "topics", "search", "around", "fetch"];
30478
+ var LIMITS = {
30479
+ list: { default: 30, max: 200 },
30480
+ view: { default: 50, max: 300 },
30481
+ topics: { default: 30, max: TOPICS_LIMIT },
30482
+ search: { default: 30, max: 200 }
30483
+ };
30484
+ var AROUND_COUNT = { default: 5, max: AROUND_LIMIT };
30485
+ var FETCH_PAGES = { default: 1, max: FETCH_PAGE_LIMIT };
30486
+ var accountArgument = external_exports.string().optional().describe("The account label; may be left out when only one account is logged in.");
30487
+ var input3 = external_exports.object({
30488
+ method: external_exports.enum(METHODS).describe(
30489
+ "`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."
30490
+ ),
30491
+ account: accountArgument,
30492
+ query: external_exports.string().optional().describe(
30493
+ "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."
30494
+ ),
30495
+ unread: external_exports.boolean().optional().describe("list: only chats with unread messages."),
30496
+ last: external_exports.boolean().optional().describe("list: add each chat's newest mirrored message as `last_message`."),
30497
+ refresh: external_exports.boolean().optional().describe(
30498
+ "list: first take the chat list from Telegram (needs the sync service), then read it, and report when in `refreshed_at`."
30499
+ ),
30500
+ chat: external_exports.string().optional().describe(
30501
+ "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."
30502
+ ),
30503
+ topic: external_exports.number().int().positive().optional().describe(
30504
+ `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.`
30505
+ ),
30506
+ after: external_exports.string().optional().describe(
30507
+ "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."
30508
+ ),
30509
+ before: external_exports.string().optional().describe("view, search: only messages before this time (exclusive); the same forms as `after`."),
30510
+ limit: external_exports.number().int().optional().describe(
30511
+ `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.`
30512
+ ),
30513
+ cursor: external_exports.string().optional().describe("The `next` of the previous result, to continue where it stopped."),
30514
+ id: external_exports.number().int().positive().optional().describe("around (required): the id of the message to read around."),
30515
+ before_count: external_exports.number().int().optional().describe(
30516
+ `around: how many older messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
30517
+ ),
30518
+ after_count: external_exports.number().int().optional().describe(
30519
+ `around: how many newer messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
30520
+ ),
30521
+ pages: external_exports.number().int().optional().describe(
30522
+ `fetch: how many pages of ${FETCH_PAGE_SIZE} older messages to add, default ${FETCH_PAGES.default}, at most ${FETCH_PAGES.max}.`
30523
+ )
30524
+ });
30525
+ var nameCut = external_exports.literal(true).optional().describe("Set when the name was longer than the most a result carries and was cut.");
30526
+ var chatEntry2 = external_exports.object({
30527
+ id: external_exports.string().describe("The chat id: Telegram's signed id as text, such as `123456789` or `-1001234567890`."),
30528
+ kind: external_exports.enum(CHAT_KINDS).describe("`self` is Saved Messages."),
30529
+ name: external_exports.string().nullable().describe("The person's or chat's name; null when none is known."),
30530
+ name_cut: nameCut,
30531
+ username: external_exports.string().optional(),
30532
+ mirrored: external_exports.boolean().describe("Whether the mirror keeps this chat's messages."),
30533
+ unread: external_exports.number().int().describe("Incoming messages not read yet."),
30534
+ last_activity_at: localTimeSchema.nullable().describe("The latest message's time, in local time with offset; null when none is known."),
30535
+ forum: external_exports.literal(true).optional().describe("Set for a supergroup with topics."),
30536
+ archived: external_exports.literal(true).optional(),
30537
+ left: external_exports.literal(true).optional().describe("Set for a group or channel the user has left or been removed from."),
30538
+ can_send: external_exports.literal(false).optional().describe("Set when the user is known not to be able to write here."),
30539
+ auto_delete_seconds: external_exports.number().int().optional().describe("The chat's auto-delete period, when on."),
30540
+ last_message: messageEntry.nullable().optional().describe("With `last`: the newest mirrored message of the chat, or null when none is mirrored.")
30541
+ });
30542
+ var topicEntry = external_exports.object({
30543
+ id: external_exports.number().int().describe(`The topic id, to pass as \`topic\` to \`view\`; ${GENERAL_TOPIC} is the General topic.`),
30544
+ title: external_exports.string().nullable().describe("The topic's title; null when none is known."),
30545
+ closed: external_exports.literal(true).optional().describe("Set for a topic that is closed."),
30546
+ pinned: external_exports.literal(true).optional().describe("Set for a topic that is pinned."),
30547
+ unread: external_exports.number().int().describe("Messages in it not read yet."),
30548
+ last_activity_at: localTimeSchema.nullable().describe("The newest message's time, in local time with offset; null when none is known.")
30549
+ });
30550
+ var output3 = external_exports.object({
30551
+ chats: external_exports.array(chatEntry2).optional().describe("list: the chats, newest activity first."),
30552
+ refreshed_at: localTimeSchema.optional().describe("list with `refresh`: when the chat list was taken from Telegram, in local time with offset."),
30553
+ chat: chatEntry2.optional().describe("view, topics, around, fetch: the chat read."),
30554
+ messages: external_exports.array(messageEntry).optional().describe("view, around: the messages, oldest first. search: the hits, newest first."),
30555
+ live: external_exports.literal(true).optional().describe(
30556
+ "view, around: set when the chat is not mirrored, so the messages were read from Telegram and stored nowhere."
30557
+ ),
30558
+ topics: external_exports.array(topicEntry).optional().describe("topics: the topics, most recently active first."),
30559
+ anchor: external_exports.number().int().optional().describe("around: the id of the message read around, among `messages`."),
30560
+ trimmed_before: external_exports.number().int().optional().describe(
30561
+ "around: how many older messages were left out, farthest first, to keep the result a size that can be read."
30562
+ ),
30563
+ trimmed_after: external_exports.number().int().optional().describe(
30564
+ "around: how many newer messages were left out, farthest first, to keep the result a size that can be read."
30565
+ ),
30566
+ scanned_to: localTimeSchema.optional().describe(
30567
+ "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."
30568
+ ),
30569
+ scope: external_exports.string().optional().describe("search: what the search covers and what it does not."),
30570
+ fetched: external_exports.object({
30571
+ added: external_exports.number().int().describe("How many messages the mirror gained."),
30572
+ pages: external_exports.number().int().describe("How many pages were read."),
30573
+ complete: external_exports.boolean().describe("Whether Telegram has nothing older for this chat."),
30574
+ oldest_at: localTimeSchema.nullable().describe(
30575
+ "The time of the oldest message the mirror now holds of this chat, in local time with offset; null when none."
30576
+ )
30577
+ }).optional().describe("fetch: what the history fetch stored."),
30578
+ next: external_exports.string().optional().describe("Pass back as `cursor` to continue; absent when there is no more."),
30579
+ note: external_exports.string().describe("Says that what the result carries is data written by other people.")
30580
+ });
30581
+
30525
30582
  // src/core/chat/around.ts
30526
30583
  var TRIMMED_RESERVE = 100;
30527
30584
  var NO_MESSAGE = "No message with this id in this chat.";
@@ -30643,8 +30700,8 @@ function fetchResult(read, stored) {
30643
30700
  }
30644
30701
 
30645
30702
  // src/core/fold.ts
30646
- function foldCase(text) {
30647
- return text.normalize("NFKC").toUpperCase().toLowerCase().replace(/ς/g, "\u03C3");
30703
+ function foldCase(text2) {
30704
+ return text2.normalize("NFKC").toUpperCase().toLowerCase().replace(/ς/g, "\u03C3");
30648
30705
  }
30649
30706
 
30650
30707
  // src/core/chat/filter.ts
@@ -30724,7 +30781,7 @@ function listChats(mirror, args, now) {
30724
30781
  return result;
30725
30782
  }
30726
30783
 
30727
- // src/core/chat/open.ts
30784
+ // src/core/open.ts
30728
30785
  async function pickPaths(ctx, account2) {
30729
30786
  if (!ctx.stateDir) throw new ToolError("internal_error", "The entry point did not supply a state directory.");
30730
30787
  return pickLabel(ctx.stateDir, account2);
@@ -30783,8 +30840,8 @@ function foldedFileName(media) {
30783
30840
  return isRecord(info) && typeof info.name === "string" ? foldCase(info.name) : void 0;
30784
30841
  }
30785
30842
  function matchesAllWords(row, words2) {
30786
- const text = foldCase(row.body ?? "");
30787
- const unmatched = words2.filter((word) => !text.includes(word));
30843
+ const text2 = foldCase(row.body ?? "");
30844
+ const unmatched = words2.filter((word) => !text2.includes(word));
30788
30845
  if (unmatched.length === 0) return true;
30789
30846
  const name = row.media === null ? void 0 : foldedFileName(row.media);
30790
30847
  return name !== void 0 && unmatched.every((word) => name.includes(word));
@@ -31133,6 +31190,32 @@ function chatFact(kind) {
31133
31190
  return { label: "Chat", value: { text: CHAT_KIND_WORDS[kind] } };
31134
31191
  }
31135
31192
 
31193
+ // src/core/chat/media-facts.ts
31194
+ var MEDIA_KIND_WORDS = {
31195
+ photo: "Photo",
31196
+ document: "Document",
31197
+ audio: "Audio",
31198
+ voice: "Voice message",
31199
+ video: "Video",
31200
+ sticker: "Sticker",
31201
+ dice: "Dice",
31202
+ contact: "Contact",
31203
+ venue: "Venue",
31204
+ geo: "Location",
31205
+ geo_live: "Live location",
31206
+ game: "Game",
31207
+ invoice: "Invoice",
31208
+ webpage: "Link preview",
31209
+ poll: "Poll",
31210
+ todo: "Checklist",
31211
+ story: "Story",
31212
+ paid: "Paid media",
31213
+ unsupported: "Unsupported media"
31214
+ };
31215
+ function mediaKindValue(type) {
31216
+ return Object.hasOwn(MEDIA_KIND_WORDS, type) ? { text: MEDIA_KIND_WORDS[type] } : type;
31217
+ }
31218
+
31136
31219
  // src/core/file/constants.ts
31137
31220
  var FILE_SAVE_TOOL_ID = "telegram_file_save";
31138
31221
  var FILE_SEND_TOOL_ID = "telegram_file_send";
@@ -31317,7 +31400,7 @@ async function openMessageFolder(downloadDir, folder) {
31317
31400
 
31318
31401
  // src/core/file/incoming.ts
31319
31402
  import { lstat as lstat4, realpath as realpath3 } from "node:fs/promises";
31320
- import { dirname as dirname3, isAbsolute as isAbsolute6, resolve as resolve4 } from "node:path";
31403
+ import { dirname as dirname3, isAbsolute as isAbsolute6, resolve as resolve2 } from "node:path";
31321
31404
 
31322
31405
  // src/core/file/place.ts
31323
31406
  import { createHash as createHash3, randomBytes } from "node:crypto";
@@ -31335,11 +31418,11 @@ var ANIMATED_STICKER = "an animated sticker (.tgs), which cannot be inspected";
31335
31418
  function unreadableArchive(label) {
31336
31419
  return `an archive that cannot be inspected (${label})`;
31337
31420
  }
31338
- function words(text) {
31339
- return text.split(/\s+/).filter((word) => word !== "");
31421
+ function words(text2) {
31422
+ return text2.split(/\s+/).filter((word) => word !== "");
31340
31423
  }
31341
- function ascii(text) {
31342
- return Array.from(text, (character) => character.charCodeAt(0));
31424
+ function ascii(text2) {
31425
+ return Array.from(text2, (character) => character.charCodeAt(0));
31343
31426
  }
31344
31427
  function at(offset, bytes) {
31345
31428
  return { offset, bytes };
@@ -31556,7 +31639,7 @@ function parseOctal(field) {
31556
31639
  }
31557
31640
  return digits > 0 ? value : void 0;
31558
31641
  }
31559
- function parseNumber(field) {
31642
+ function parseNumber2(field) {
31560
31643
  const first = field[0];
31561
31644
  if (first === void 0 || (first & BASE256_FLAG) === 0) return parseOctal(field);
31562
31645
  if (first === BASE256_NEGATIVE) return void 0;
@@ -31595,11 +31678,11 @@ function parseHeader(block) {
31595
31678
  path[prefix.length] = "/".charCodeAt(0);
31596
31679
  path.set(name, prefix.length + 1);
31597
31680
  }
31598
- return { path, size: parseNumber(block.subarray(SIZE_START, SIZE_END)), type: block[TYPE_OFFSET] };
31681
+ return { path, size: parseNumber2(block.subarray(SIZE_START, SIZE_END)), type: block[TYPE_OFFSET] };
31599
31682
  }
31600
- function parseDecimal(text) {
31601
- if (!/^[0-9]{1,15}$/.test(text)) return void 0;
31602
- return Number(text);
31683
+ function parseDecimal(text2) {
31684
+ if (!/^[0-9]{1,15}$/.test(text2)) return void 0;
31685
+ return Number(text2);
31603
31686
  }
31604
31687
  function parsePaxRecords(data, maxRecords = Infinity) {
31605
31688
  const records = [];
@@ -31789,8 +31872,8 @@ var EXTENSIONS = {
31789
31872
  };
31790
31873
  var KIND_PATTERN = /^[a-z]{1,16}$/;
31791
31874
  var UNKNOWN_KIND = "file";
31792
- function byteLength(text) {
31793
- return Buffer5.byteLength(text);
31875
+ function byteLength(text2) {
31876
+ return Buffer5.byteLength(text2);
31794
31877
  }
31795
31878
  function extensionOf2(type) {
31796
31879
  return type == null ? "" : EXTENSIONS[type.split(";")[0].trim().toLowerCase()] ?? "";
@@ -31958,7 +32041,7 @@ async function openDownloaded(incoming, path) {
31958
32041
  }
31959
32042
  async function discardDownload(incoming, path) {
31960
32043
  if (!isAbsolute6(path)) return;
31961
- const folder = dirname3(resolve4(path));
32044
+ const folder = dirname3(resolve2(path));
31962
32045
  if (dirname3(folder) !== incoming) return;
31963
32046
  await discard(folder).catch(() => {
31964
32047
  });
@@ -32059,8 +32142,8 @@ function fitListings(result) {
32059
32142
  var MAX_NAME_CHARACTERS = 255;
32060
32143
  var CUT_MARK = "\u2026";
32061
32144
  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;
32062
- function cleanName(text) {
32063
- const cleaned = text.normalize("NFC").replace(HIDDEN_CHARACTERS, "?");
32145
+ function cleanName(text2) {
32146
+ const cleaned = text2.normalize("NFC").replace(HIDDEN_CHARACTERS, "?");
32064
32147
  const characters = Array.from(cleaned);
32065
32148
  if (characters.length <= MAX_NAME_CHARACTERS) return cleaned;
32066
32149
  return characters.slice(0, MAX_NAME_CHARACTERS - CUT_MARK.length).join("") + CUT_MARK;
@@ -32233,9 +32316,9 @@ function decodeUtf8Lossy(bytes) {
32233
32316
  return LOSSY_UTF8.decode(bytes);
32234
32317
  }
32235
32318
  function decodeCp437(bytes) {
32236
- let text = "";
32237
- for (const byte of bytes) text += byte < ASCII_END ? String.fromCharCode(byte) : CP437_HIGH[byte - ASCII_END];
32238
- return text;
32319
+ let text2 = "";
32320
+ for (const byte of bytes) text2 += byte < ASCII_END ? String.fromCharCode(byte) : CP437_HIGH[byte - ASCII_END];
32321
+ return text2;
32239
32322
  }
32240
32323
 
32241
32324
  // src/core/archive/tar.ts
@@ -33055,14 +33138,6 @@ function namesOf(file2, id) {
33055
33138
  const name = safeFileName(file2.name, { kind: file2.kind, id, type: file2.type });
33056
33139
  return { name, shown: file2.name === null ? file2.kind : name };
33057
33140
  }
33058
- var FILE_KIND_WORDS = {
33059
- photo: "Photo",
33060
- document: "Document",
33061
- audio: "Audio",
33062
- voice: "Voice message",
33063
- video: "Video",
33064
- sticker: "Sticker"
33065
- };
33066
33141
  function saveHeadline(file2, chat) {
33067
33142
  if (file2 !== void 0 && chat !== null) return { text: "About to save {file} from {chat}", values: { file: file2, chat } };
33068
33143
  if (chat !== null) return { text: "About to save a file from {chat}", values: { chat } };
@@ -33070,9 +33145,8 @@ function saveHeadline(file2, chat) {
33070
33145
  return void 0;
33071
33146
  }
33072
33147
  function fileFacts(media, unpack) {
33073
- const kind = media && Object.hasOwn(FILE_KIND_WORDS, media.type) ? FILE_KIND_WORDS[media.type] : void 0;
33074
33148
  return [
33075
- ...media ? [{ label: "File", value: kind ? { text: kind } : media.type }] : [],
33149
+ ...media ? [{ label: "File", value: mediaKindValue(media.type) }] : [],
33076
33150
  ...unpack ? [{ label: "Unpack", value: { text: "Yes" } }] : [],
33077
33151
  ...media?.size !== void 0 && Number.isSafeInteger(media.size) && media.size > 0 ? [sizeFact(media.size)] : []
33078
33152
  ];
@@ -33221,7 +33295,7 @@ var telegramFileSave = createFileSave();
33221
33295
 
33222
33296
  // src/core/file/send/tool.ts
33223
33297
  import { realpath as realpath6 } from "node:fs/promises";
33224
- import { join as join10, resolve as resolve6 } from "node:path";
33298
+ import { join as join10, resolve as resolve4 } from "node:path";
33225
33299
 
33226
33300
  // src/core/telegram/action-checks.ts
33227
33301
  var LINK_PREVIEW = "webpage";
@@ -33303,14 +33377,14 @@ function storedReactions(json2) {
33303
33377
  function ownReaction(reactions) {
33304
33378
  return reactions.find((reaction2) => reaction2.mine)?.emoji;
33305
33379
  }
33306
- function checkEditable({ kind, body, captioned }, text) {
33380
+ function checkEditable({ kind, body, captioned }, text2) {
33307
33381
  if (kind === "service" || body === null && !captioned) {
33308
33382
  throw new ToolError("invalid_argument", "This message has no text or caption to edit; nothing was edited.");
33309
33383
  }
33310
- if (body === text) {
33384
+ if (body === text2) {
33311
33385
  throw new ToolError("invalid_argument", "The text is the same as the message's; nothing to edit.");
33312
33386
  }
33313
- if (captioned && text.length > CAPTION_MAX_UNITS) {
33387
+ if (captioned && text2.length > CAPTION_MAX_UNITS) {
33314
33388
  throw new ToolError(
33315
33389
  "invalid_argument",
33316
33390
  `A caption must be at most ${CAPTION_MAX_UNITS} characters; nothing was edited.`
@@ -33319,8 +33393,8 @@ function checkEditable({ kind, body, captioned }, text) {
33319
33393
  }
33320
33394
 
33321
33395
  // src/core/message/arguments.ts
33322
- function checkedText(text) {
33323
- const trimmed = text.trim();
33396
+ function checkedText(text2) {
33397
+ const trimmed = text2.trim();
33324
33398
  if (trimmed === "") throw new ToolError("invalid_argument", "text is blank; give the text to send.");
33325
33399
  if (trimmed.length > TEXT_MAX_UNITS) {
33326
33400
  throw new ToolError(
@@ -33378,8 +33452,8 @@ var Window = class {
33378
33452
  get tail() {
33379
33453
  return this.#tail;
33380
33454
  }
33381
- sees(text) {
33382
- const all = this.#tail + text;
33455
+ sees(text2) {
33456
+ const all = this.#tail + text2;
33383
33457
  this.#tail = all.slice(-TAIL_LENGTH);
33384
33458
  return MARKER.test(all);
33385
33459
  }
@@ -33389,13 +33463,13 @@ var PrivateKeyScan = class {
33389
33463
  #withoutNuls;
33390
33464
  /** Feeds the next chunk; whether a marker starts anywhere in the bytes so far. */
33391
33465
  sees(chunk) {
33392
- const text = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString("latin1");
33466
+ const text2 = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString("latin1");
33393
33467
  const before = this.#plain.tail;
33394
- const plain2 = this.#plain.sees(text);
33395
- if (this.#withoutNuls === void 0 && text.includes(NUL)) {
33468
+ const plain2 = this.#plain.sees(text2);
33469
+ if (this.#withoutNuls === void 0 && text2.includes(NUL)) {
33396
33470
  this.#withoutNuls = new Window(before);
33397
33471
  }
33398
- const stripped = this.#withoutNuls?.sees(text.replaceAll(NUL, "")) ?? false;
33472
+ const stripped = this.#withoutNuls?.sees(text2.replaceAll(NUL, "")) ?? false;
33399
33473
  return plain2 || stripped;
33400
33474
  }
33401
33475
  };
@@ -33587,9 +33661,8 @@ async function checkArchive(path, format, limits, signal) {
33587
33661
  }
33588
33662
 
33589
33663
  // src/core/outgoing/limits.ts
33590
- var BYTES_PER_MB2 = 1024 * 1024;
33591
33664
  var MAX_FILES = 10;
33592
- var MAX_TOTAL_BYTES = 100 * BYTES_PER_MB2;
33665
+ var MAX_TOTAL_BYTES = 100 * BYTES_PER_MB;
33593
33666
 
33594
33667
  // src/core/file/send/constants.ts
33595
33668
  var SEND_FILES_TIMEOUT_MS = 30 * 60 * 1e3;
@@ -33712,7 +33785,7 @@ async function hashResolved({ shown, resolved }, budget, signal) {
33712
33785
  }
33713
33786
 
33714
33787
  // src/core/file/send/resolve-files.ts
33715
- import { homedir as homedir2 } from "node:os";
33788
+ import { homedir as homedir3 } from "node:os";
33716
33789
 
33717
33790
  // src/core/outgoing/roots.ts
33718
33791
  import { realpath as realpath4 } from "node:fs/promises";
@@ -33750,7 +33823,7 @@ async function allowedRoots({ cwd, sendDirs, home, forbidden }) {
33750
33823
 
33751
33824
  // src/core/outgoing/resolve.ts
33752
33825
  import { lstat as lstat6, realpath as realpath5 } from "node:fs/promises";
33753
- import { basename as basename2, resolve as resolve5, sep as sep4 } from "node:path";
33826
+ import { basename as basename2, resolve as resolve3, sep as sep4 } from "node:path";
33754
33827
  var UNRESOLVABLE = /* @__PURE__ */ new Set(["ENOENT", "ENOTDIR", "ELOOP"]);
33755
33828
  var UNREADABLE = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
33756
33829
  async function realPathOf(path, shown) {
@@ -33765,7 +33838,7 @@ async function realPathOf(path, shown) {
33765
33838
  }
33766
33839
  async function resolveOutgoing(path, { cwd, roots, forbidden, home }) {
33767
33840
  const shown = JSON.stringify(path);
33768
- const real = await realPathOf(resolve5(cwd, path), shown);
33841
+ const real = await realPathOf(resolve3(cwd, path), shown);
33769
33842
  const stats = await lstat6(real, { bigint: true });
33770
33843
  if (!stats.isFile()) throw new ToolError("invalid_argument", `${shown} is not a regular file.`);
33771
33844
  if (stats.nlink > 1n) throw hardLinked(shown);
@@ -33797,7 +33870,7 @@ async function resolveOutgoing(path, { cwd, roots, forbidden, home }) {
33797
33870
  var FALLBACK_KIND = "file";
33798
33871
  async function resolveFiles(files, ctx) {
33799
33872
  const forbidden = [ctx.stateDir, ctx.ownDir].filter((directory) => directory !== void 0);
33800
- const home = homedir2();
33873
+ const home = homedir3();
33801
33874
  const roots = await allowedRoots({ cwd: ctx.cwd, sendDirs: ctx.sendDirs ?? [], home, forbidden });
33802
33875
  const seen = /* @__PURE__ */ new Set();
33803
33876
  const resolvedFiles = [];
@@ -34010,12 +34083,12 @@ var DUPLICATE_NOTE = "An earlier delivery of this same call had already gone out
34010
34083
  var DUPLICATE_UNKNOWN_ID_NOTE = `${DUPLICATE_NOTE} Its id is not known yet; \`telegram_chat_read\` shows it once the service receives it.`;
34011
34084
 
34012
34085
  // src/core/message/uncertain.ts
34013
- function sentLike(paths, chat, text, startedAt) {
34086
+ function sentLike(paths, chat, text2, startedAt) {
34014
34087
  const rows = readMirror(
34015
34088
  paths,
34016
34089
  (mirror) => mirror.prepare(
34017
34090
  "SELECT id, ts FROM messages WHERE chat = ? AND from_me = 1 AND body = ? AND ts >= ? ORDER BY ts DESC, id DESC LIMIT ?"
34018
- ).all(chat, text, startedAt - SENT_LOOKBACK_MS, MAX_SENT_LIKE)
34091
+ ).all(chat, text2, startedAt - SENT_LOOKBACK_MS, MAX_SENT_LIKE)
34019
34092
  );
34020
34093
  return rows.map(({ id, ts }) => ({ id, time: formatLocalTime(ts) }));
34021
34094
  }
@@ -34035,8 +34108,8 @@ function withFound(error62, find, describe3) {
34035
34108
  details: { ...error62.details, sent_like_this: found }
34036
34109
  });
34037
34110
  }
34038
- function withSentLike(error62, paths, chat, text, startedAt) {
34039
- return withFound(error62, () => sentLike(paths, chat, text, startedAt), sentence);
34111
+ function withSentLike(error62, paths, chat, text2, startedAt) {
34112
+ return withFound(error62, () => sentLike(paths, chat, text2, startedAt), sentence);
34040
34113
  }
34041
34114
 
34042
34115
  // src/core/file/send/uncertain.ts
@@ -34067,7 +34140,7 @@ function cannotBeSent(shown, refusal) {
34067
34140
  return new ToolError("forbidden", `${shown} cannot be sent: ${why}.`, { cause: refusal });
34068
34141
  }
34069
34142
  async function fileTarget(path, cwd) {
34070
- const absolute = resolve6(cwd, path);
34143
+ const absolute = resolve4(cwd, path);
34071
34144
  return realpath6(absolute).catch(() => absolute);
34072
34145
  }
34073
34146
  async function stageAll(files, folder, budget, signal) {
@@ -34164,39 +34237,46 @@ var telegramFileSend = createFileSend();
34164
34237
  // src/core/message/action-describe.ts
34165
34238
  var EDIT_HEADLINES = {
34166
34239
  named: "About to edit message {id} in {chat}",
34167
- saved: "About to edit message {id} in Saved Messages"
34240
+ saved: "About to edit message {id} in Saved Messages",
34241
+ bare: "About to edit message {id}"
34168
34242
  };
34169
34243
  var DELETE_HEADLINES = {
34170
- named: "About to delete message {id} from {chat}",
34171
- saved: "About to delete message {id} from Saved Messages"
34244
+ named: "About to delete message {id} from {chat} for everyone",
34245
+ saved: "About to delete message {id} from Saved Messages",
34246
+ bare: "About to delete message {id} for everyone"
34172
34247
  };
34173
34248
  var REACT_HEADLINES = {
34174
34249
  named: "About to react {emoji} to message {id} in {chat}",
34175
- saved: "About to react {emoji} to message {id} in Saved Messages"
34250
+ saved: "About to react {emoji} to message {id} in Saved Messages",
34251
+ bare: "About to react {emoji} to message {id}"
34176
34252
  };
34177
34253
  var UNREACT_HEADLINES = {
34178
34254
  named: "About to remove your reaction from message {id} in {chat}",
34179
- saved: "About to remove your reaction from message {id} in Saved Messages"
34255
+ saved: "About to remove your reaction from message {id} in Saved Messages",
34256
+ bare: "About to remove your reaction from message {id}"
34180
34257
  };
34181
34258
  var PIN_HEADLINES = {
34182
34259
  named: "About to pin message {id} in {chat}",
34183
- saved: "About to pin message {id} in Saved Messages"
34260
+ saved: "About to pin message {id} in Saved Messages",
34261
+ bare: "About to pin message {id}"
34184
34262
  };
34185
34263
  var UNPIN_HEADLINES = {
34186
34264
  named: "About to unpin message {id} in {chat}",
34187
- saved: "About to unpin message {id} in Saved Messages"
34265
+ saved: "About to unpin message {id} in Saved Messages",
34266
+ bare: "About to unpin message {id}"
34188
34267
  };
34189
34268
  var MARK_READ_HEADLINES = {
34190
34269
  named: "About to mark {chat} read up to message {id}",
34191
- saved: "About to mark Saved Messages read up to message {id}"
34270
+ saved: "About to mark Saved Messages read up to message {id}",
34271
+ bare: "About to mark messages read up to message {id}"
34192
34272
  };
34193
34273
  function actionHeadline(headlines, { kind, name }, id, values) {
34194
34274
  if (kind === "self") return { text: headlines.saved, values: { id: String(id), ...values } };
34195
- return name === null ? void 0 : { text: headlines.named, values: { id: String(id), chat: name, ...values } };
34275
+ return name === null ? { text: headlines.bare, values: { id: String(id), ...values } } : { text: headlines.named, values: { id: String(id), chat: name, ...values } };
34196
34276
  }
34197
34277
  function currentFacts({ body, media }) {
34198
- const shown = body || media?.type;
34199
- return shown ? [{ label: "Current", value: cut(shown, MAX_VALUE_LENGTH) }] : [];
34278
+ if (body) return [{ label: "Current", value: cut(body, MAX_VALUE_LENGTH) }];
34279
+ return media ? [{ label: "Media", value: mediaKindValue(media.type) }] : [];
34200
34280
  }
34201
34281
  function describeAction(headlines, chat, id, message, extras = {}) {
34202
34282
  const headline = actionHeadline(headlines, chat, id, extras.values ?? {});
@@ -34432,7 +34512,7 @@ var telegramMessageDelete = defineTool({
34432
34512
  "A result means Telegram deleted the message for everyone. `already` means it was gone before this call.",
34433
34513
  "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."
34434
34514
  ].join(" "),
34435
- intent: "About to delete a message",
34515
+ intent: "About to delete a message for everyone",
34436
34516
  toolset: "actions",
34437
34517
  class: "administrative",
34438
34518
  input: deleteInput,
@@ -34464,10 +34544,10 @@ var telegramMessageDelete = defineTool({
34464
34544
  });
34465
34545
 
34466
34546
  // src/core/message/edit-prepare.ts
34467
- function prepareEdit(mirror, chatId, id, text) {
34547
+ function prepareEdit(mirror, chatId, id, text2) {
34468
34548
  const { chat, message } = findOwnMessage(mirror, chatId, id, "edit");
34469
34549
  if (!message) throw deletedMessage();
34470
- checkEditable({ ...message, captioned: message.media !== null && takesCaption(message.media.type) }, text);
34550
+ checkEditable({ ...message, captioned: message.media !== null && takesCaption(message.media.type) }, text2);
34471
34551
  return { chat, message };
34472
34552
  }
34473
34553
 
@@ -34490,22 +34570,22 @@ var telegramMessageEdit = defineTool({
34490
34570
  input: editInput,
34491
34571
  output: editOutput,
34492
34572
  async describe(args, ctx) {
34493
- const text = checkedText(args.text);
34573
+ const text2 = checkedText(args.text);
34494
34574
  const chat = requireChatId(args.chat, MESSAGE_EDIT_TOOL_ID);
34495
34575
  const paths = await pickPaths(ctx, args.account);
34496
- const target = readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text));
34576
+ const target = readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text2));
34497
34577
  return {
34498
34578
  ...describeAction(EDIT_HEADLINES, target.chat, args.id, target.message),
34499
- text: { label: "New text", body: text }
34579
+ text: { label: "New text", body: text2 }
34500
34580
  };
34501
34581
  },
34502
34582
  targets: (args, ctx) => actionTargets(MESSAGE_EDIT_TOOL_ID, args, ctx),
34503
34583
  async execute(args, ctx) {
34504
34584
  const chat = requireChatId(args.chat, MESSAGE_EDIT_TOOL_ID);
34505
- const text = checkedText(args.text);
34585
+ const text2 = checkedText(args.text);
34506
34586
  const paths = await pickPaths(ctx, args.account);
34507
- readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text));
34508
- const params = { chat, id: args.id, text };
34587
+ readMirror(paths, (mirror) => prepareEdit(mirror, chat, args.id, text2));
34588
+ const params = { chat, id: args.id, text: text2 };
34509
34589
  const edited = await callService(paths.socket, SERVICE_REQUESTS.edit, params, {
34510
34590
  effect: "write",
34511
34591
  signal: ctx.signal,
@@ -34701,7 +34781,7 @@ var telegramMessageSend = defineTool({
34701
34781
  input: input5,
34702
34782
  output: output5,
34703
34783
  async describe(args, ctx) {
34704
- const text = checkedText(args.text);
34784
+ const text2 = checkedText(args.text);
34705
34785
  const chat = requireChatId(args.chat, MESSAGE_SEND_TOOL_ID);
34706
34786
  const paths = await pickPaths(ctx, args.account);
34707
34787
  const { row, title } = readMirror(paths, (mirror) => {
@@ -34713,17 +34793,17 @@ var telegramMessageSend = defineTool({
34713
34793
  headline: { text: "About to send a message to {chat}", values: { chat: row.name } }
34714
34794
  },
34715
34795
  facts: [chatFact(row.kind), ...topicFacts(args.topic, title), ...replyFacts(args.reply_to)],
34716
- text: { label: "Message text", body: text }
34796
+ text: { label: "Message text", body: text2 }
34717
34797
  };
34718
34798
  },
34719
34799
  targets: (args, ctx) => chatTargets(MESSAGE_SEND_TOOL_ID, args, ctx),
34720
34800
  async execute(args, ctx) {
34721
34801
  const chat = requireChatId(args.chat, MESSAGE_SEND_TOOL_ID);
34722
- const text = checkedText(args.text);
34802
+ const text2 = checkedText(args.text);
34723
34803
  const paths = await pickPaths(ctx, args.account);
34724
34804
  const params = {
34725
34805
  chat,
34726
- text,
34806
+ text: text2,
34727
34807
  ...args.topic !== void 0 && { topic: args.topic },
34728
34808
  ...args.reply_to !== void 0 && { reply_to: args.reply_to }
34729
34809
  };
@@ -34736,7 +34816,7 @@ var telegramMessageSend = defineTool({
34736
34816
  });
34737
34817
  return sendResult(chat, sent);
34738
34818
  } catch (error62) {
34739
- throw isToolError(error62) && error62.code === "uncertain" ? withSentLike(error62, paths, chat, text, startedAt) : error62;
34819
+ throw isToolError(error62) && error62.code === "uncertain" ? withSentLike(error62, paths, chat, text2, startedAt) : error62;
34740
34820
  }
34741
34821
  }
34742
34822
  });
@@ -35562,7 +35642,7 @@ var Protocol = class {
35562
35642
  return;
35563
35643
  }
35564
35644
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
35565
- await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
35645
+ await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
35566
35646
  options?.signal?.throwIfAborted();
35567
35647
  }
35568
35648
  } catch (error62) {
@@ -35579,7 +35659,7 @@ var Protocol = class {
35579
35659
  */
35580
35660
  request(request, resultSchema, options) {
35581
35661
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
35582
- return new Promise((resolve7, reject) => {
35662
+ return new Promise((resolve5, reject) => {
35583
35663
  const earlyReject = (error62) => {
35584
35664
  reject(error62);
35585
35665
  };
@@ -35657,7 +35737,7 @@ var Protocol = class {
35657
35737
  if (!parseResult.success) {
35658
35738
  reject(parseResult.error);
35659
35739
  } else {
35660
- resolve7(parseResult.data);
35740
+ resolve5(parseResult.data);
35661
35741
  }
35662
35742
  } catch (error62) {
35663
35743
  reject(error62);
@@ -35919,12 +35999,12 @@ var Protocol = class {
35919
35999
  }
35920
36000
  } catch {
35921
36001
  }
35922
- return new Promise((resolve7, reject) => {
36002
+ return new Promise((resolve5, reject) => {
35923
36003
  if (signal.aborted) {
35924
36004
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
35925
36005
  return;
35926
36006
  }
35927
- const timeoutId = setTimeout(resolve7, interval);
36007
+ const timeoutId = setTimeout(resolve5, interval);
35928
36008
  signal.addEventListener("abort", () => {
35929
36009
  clearTimeout(timeoutId);
35930
36010
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -36740,23 +36820,52 @@ function elicitationConfirm(server, signal) {
36740
36820
 
36741
36821
  // src/mcp/roots.ts
36742
36822
  import { statSync as statSync2 } from "node:fs";
36823
+ import { isAbsolute as isAbsolute7 } from "node:path";
36743
36824
  import { fileURLToPath as fileURLToPath2 } from "node:url";
36744
36825
  var FILE_URI_PROTOCOL = "file:";
36826
+ var SANDBOX_STATE_META = "codex/sandbox-state-meta";
36827
+ var TURN_METADATA = "x-codex-turn-metadata";
36828
+ function existingDirectory(dir) {
36829
+ try {
36830
+ return statSync2(dir, { throwIfNoEntry: false })?.isDirectory() ? dir : void 0;
36831
+ } catch {
36832
+ return void 0;
36833
+ }
36834
+ }
36745
36835
  function localDirectory(uri) {
36746
36836
  try {
36747
36837
  const url2 = new URL(uri);
36748
- if (url2.protocol !== FILE_URI_PROTOCOL) return void 0;
36749
- const dir = fileURLToPath2(url2);
36750
- return statSync2(dir, { throwIfNoEntry: false })?.isDirectory() ? dir : void 0;
36838
+ return url2.protocol === FILE_URI_PROTOCOL ? existingDirectory(fileURLToPath2(url2)) : void 0;
36751
36839
  } catch {
36752
36840
  return void 0;
36753
36841
  }
36754
36842
  }
36843
+ function isRecord2(value) {
36844
+ return typeof value === "object" && value !== null && !Array.isArray(value);
36845
+ }
36846
+ async function usableDirectory(candidate, ownDir, allowPath = false) {
36847
+ if (typeof candidate !== "string") return void 0;
36848
+ const dir = allowPath && isAbsolute7(candidate) ? existingDirectory(candidate) : localDirectory(candidate);
36849
+ if (dir === void 0 || ownDir !== void 0 && await isWithin(dir, ownDir)) return void 0;
36850
+ return dir;
36851
+ }
36852
+ async function callDirectory(meta3, ownDir) {
36853
+ if (!isRecord2(meta3)) return void 0;
36854
+ const sandbox = meta3[SANDBOX_STATE_META];
36855
+ if (isRecord2(sandbox)) {
36856
+ const dir = await usableDirectory(sandbox.sandboxCwd, ownDir, true);
36857
+ if (dir !== void 0) return dir;
36858
+ }
36859
+ const turn = meta3[TURN_METADATA];
36860
+ if (!isRecord2(turn) || !isRecord2(turn.workspaces)) return void 0;
36861
+ const workspaces = Object.keys(turn.workspaces);
36862
+ return workspaces.length === 1 ? usableDirectory(workspaces[0], ownDir, true) : void 0;
36863
+ }
36755
36864
  async function firstUsableRoot(server, ownDir, signal) {
36756
36865
  const { roots } = await server.listRoots(void 0, { signal });
36757
36866
  for (const root of roots) {
36758
- const dir = localDirectory(root.uri);
36759
- if (dir !== void 0 && !(ownDir !== void 0 && await isWithin(dir, ownDir))) return dir;
36867
+ const dir = await usableDirectory(root.uri, ownDir);
36868
+ if (dir !== void 0) return dir;
36760
36869
  }
36761
36870
  return void 0;
36762
36871
  }
@@ -36767,7 +36876,9 @@ function createWorkingDirResolver(server, fallback, ownDir) {
36767
36876
  cached2 = void 0;
36768
36877
  generation++;
36769
36878
  });
36770
- return async (signal) => {
36879
+ return async (signal, meta3) => {
36880
+ const callDir = await callDirectory(meta3, ownDir);
36881
+ if (callDir !== void 0) return callDir;
36771
36882
  if (!server.getClientCapabilities()?.roots) return fallback();
36772
36883
  if (cached2 !== void 0) return cached2;
36773
36884
  const started = generation;
@@ -36808,7 +36919,7 @@ function createServer(specs, settings, options = {}) {
36808
36919
  `));
36809
36920
  const server = new Server(
36810
36921
  { name: PROJECT.name, version: options.version ?? DEFAULT_VERSION },
36811
- { capabilities: { tools: {} } }
36922
+ { capabilities: { tools: {}, experimental: { [SANDBOX_STATE_META]: {} } } }
36812
36923
  );
36813
36924
  const workingDir = createWorkingDirResolver(server, options.cwd ?? (() => process.cwd()), options.ownDir);
36814
36925
  const toolsets = options.toolsets ?? PROJECT_PROFILE.toolsets;
@@ -36821,13 +36932,14 @@ function createServer(specs, settings, options = {}) {
36821
36932
  try {
36822
36933
  const confirm = settings.confirm === "elicitation" ? elicitationConfirm(server, extra.signal) : void 0;
36823
36934
  const ctx = {
36824
- cwd: await workingDir(extra.signal),
36935
+ cwd: await workingDir(extra.signal, request.params._meta),
36825
36936
  ownDir: options.ownDir,
36826
36937
  stateDir: settings.state_dir,
36827
36938
  downloadDir: settings.download_dir,
36828
36939
  downloadMaxMb: settings.download_max_mb,
36829
36940
  sendDirs: settings.send_dirs,
36830
36941
  programName: PROJECT.name,
36942
+ options: settings.options,
36831
36943
  signal: extra.signal,
36832
36944
  ...confirm && { confirm }
36833
36945
  };
@@ -36853,7 +36965,7 @@ async function main() {
36853
36965
  const settings = parseSettings(process.argv.slice(2), process.env);
36854
36966
  server = createServer(ALL_TOOLS, settings, {
36855
36967
  ownDir: packageRoot(import.meta.url),
36856
- version: true ? "0.1.0" : void 0
36968
+ version: true ? "0.3.0" : void 0
36857
36969
  });
36858
36970
  } catch (error62) {
36859
36971
  if (!isToolError(error62)) throw error62;