omnius 1.0.156 → 1.0.158

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11733,6 +11733,8 @@ function _envRole() {
11733
11733
  return role === 'light' || role === 'storage' || role === 'full' ? role : 'full';
11734
11734
  }
11735
11735
  var _nexusSignalingServer = process.env.OMNIUS_NEXUS_SIGNALING_SERVER || 'https://openagents.nexus';
11736
+ var _nexusDirectoryOrigin = (process.env.OMNIUS_NEXUS_DIRECTORY_ORIGIN || _nexusSignalingServer).replace(/\\/+$/, '');
11737
+ var _nexusSponsorsUrl = _nexusDirectoryOrigin + '/api/v1/sponsors';
11736
11738
  var _nexusNatsServers = _envList('OMNIUS_NEXUS_NATS_SERVERS');
11737
11739
  var _nexusManifestUrls = _envList('OMNIUS_NEXUS_MANIFEST_URLS');
11738
11740
  var nexusOpts = {
@@ -12348,10 +12350,11 @@ function _coherePersistEndpointCatalog() {
12348
12350
  source: m.source || _cohereEndpointCatalog.source,
12349
12351
  passthrough: !!m.passthrough,
12350
12352
  };
12351
- });
12352
- pub.pricingMenu = Array.isArray(_cohereEndpointCatalog.pricingMenu) ? _cohereEndpointCatalog.pricingMenu : [];
12353
- _writeJson(cohereEndpointCatalogFile, pub);
12354
- }
12353
+ });
12354
+ pub.pricingMenu = Array.isArray(_cohereEndpointCatalog.pricingMenu) ? _cohereEndpointCatalog.pricingMenu : [];
12355
+ pub.endpointAuth = String(_cohereEndpointCatalog.endpointAuth || '');
12356
+ _writeJson(cohereEndpointCatalogFile, pub);
12357
+ }
12355
12358
  function _cohereRememberEndpointCatalog(opts) {
12356
12359
  opts = opts || {};
12357
12360
  var passthrough = opts.passthrough === true;
@@ -12378,7 +12381,7 @@ function _cohereLoadPersistedEndpointCatalog() {
12378
12381
  source: String(stored.source || 'cached'),
12379
12382
  passthrough: stored.passthrough === true,
12380
12383
  endpointUrl: String(stored.endpointUrl || ''),
12381
- endpointAuth: '',
12384
+ endpointAuth: String(stored.endpointAuth || ''),
12382
12385
  models: _cohereAnnotateModels(stored.models, String(stored.source || 'cached'), stored.passthrough === true),
12383
12386
  pricingMenu: Array.isArray(stored.pricingMenu) ? stored.pricingMenu : [],
12384
12387
  updatedAt: Number(stored.updatedAt || 0) || 0,
@@ -12976,7 +12979,9 @@ async function handleCmd(cmd) {
12976
12979
  return;
12977
12980
  }
12978
12981
  // NX-07: Fetch per-model metadata from Ollama for capacity announcements
12979
- var _saModels = args.models || [];
12982
+ var _saModels = Array.isArray(args.models)
12983
+ ? args.models
12984
+ : String(args.models || '').split(',').map(function(m) { return m.trim(); }).filter(Boolean);
12980
12985
  var _saModelDetails = [];
12981
12986
  try {
12982
12987
  var _saOllamaUrl = process.env.OLLAMA_HOST || 'http://localhost:11434';
@@ -12997,11 +13002,13 @@ async function handleCmd(cmd) {
12997
13002
  } catch {}
12998
13003
  var sponsorLimitsArg = {};
12999
13004
  try { sponsorLimitsArg = args.limits ? JSON.parse(args.limits) : {}; } catch {}
13000
- var sponsorServicesArg = [];
13001
- try {
13002
- if (args.services) sponsorServicesArg = typeof args.services === 'string' ? JSON.parse(args.services) : args.services;
13003
- } catch {}
13004
- var sponsorData = {
13005
+ var sponsorServicesArg = [];
13006
+ try {
13007
+ if (args.services) sponsorServicesArg = typeof args.services === 'string' ? JSON.parse(args.services) : args.services;
13008
+ } catch {}
13009
+ var sponsorHeaderArg = {};
13010
+ try { sponsorHeaderArg = args.header ? (typeof args.header === 'string' ? JSON.parse(args.header) : args.header) : {}; } catch {}
13011
+ var sponsorData = {
13005
13012
  type: 'sponsor.announce',
13006
13013
  peerId: (connected ? nexus.peerId : 'unknown') || 'unknown',
13007
13014
  libp2pPeerId: (connected ? nexus.peerId : '') || '',
@@ -13012,15 +13019,17 @@ async function handleCmd(cmd) {
13012
13019
  mediaCapabilities: Array.isArray(sponsorServicesArg) ? sponsorServicesArg.filter(function(s) { return s && s.kind && s.kind !== 'llm'; }) : [],
13013
13020
  tunnelUrl: args.tunnel_url || null,
13014
13021
  authKey: args.auth_key || '',
13015
- limits: {
13016
- maxRequestsPerMinute: parseInt(args.rpm || sponsorLimitsArg.maxRequestsPerMinute || '60', 10),
13017
- maxTokensPerDay: parseInt(args.tpd || sponsorLimitsArg.maxTokensPerDay || '100000', 10),
13018
- maxConcurrent: parseInt(args.max_concurrent || sponsorLimitsArg.maxConcurrent || '1', 10),
13019
- },
13020
- banner: args.banner || null,
13021
- message: args.message || '',
13022
- linkUrl: args.link_url || '',
13023
- linkText: args.link_text || '',
13022
+ limits: {
13023
+ maxRequestsPerMinute: parseInt(args.rpm || sponsorLimitsArg.maxRequestsPerMinute || '60', 10),
13024
+ maxTokensPerDay: parseInt(args.tpd || sponsorLimitsArg.maxTokensPerDay || '100000', 10),
13025
+ maxConcurrent: parseInt(args.max_concurrent || sponsorLimitsArg.maxConcurrent || '1', 10),
13026
+ media: sponsorLimitsArg.media,
13027
+ },
13028
+ banner: args.banner || null,
13029
+ message: args.message || '',
13030
+ linkUrl: args.link_url || '',
13031
+ linkText: args.link_text || '',
13032
+ header: sponsorHeaderArg && typeof sponsorHeaderArg === 'object' ? sponsorHeaderArg : {},
13024
13033
  status: 'active',
13025
13034
  timestamp: Date.now(),
13026
13035
  };
@@ -13029,9 +13038,9 @@ async function handleCmd(cmd) {
13029
13038
  globalThis._activeSponsorData = sponsorData;
13030
13039
  dlog('sponsor_announce: published to nexus.sponsors.announce');
13031
13040
 
13032
- // Persist to KV-backed sponsor directory (omnius.nexus worker)
13041
+ // Persist to KV-backed sponsor directory (OpenAgents Nexus worker)
13033
13042
  try {
13034
- var kvResp = await fetch('https://omnius.nexus/api/v1/sponsors', {
13043
+ var kvResp = await fetch(_nexusSponsorsUrl, {
13035
13044
  method: 'POST',
13036
13045
  headers: { 'Content-Type': 'application/json' },
13037
13046
  body: JSON.stringify(sponsorData),
@@ -13174,9 +13183,9 @@ async function handleCmd(cmd) {
13174
13183
  var discoverTimeout = parseInt(args.timeout_ms || '5000', 10);
13175
13184
 
13176
13185
  // ── Source 1: KV-backed persistent directory (MOST RELIABLE) ──
13177
- // Query the omnius.nexus worker for persisted sponsor listings
13178
- try {
13179
- var kvResp = await fetch('https://omnius.nexus/api/v1/sponsors', { signal: AbortSignal.timeout(5000) });
13186
+ // Query the OpenAgents Nexus worker for persisted sponsor listings
13187
+ try {
13188
+ var kvResp = await fetch(_nexusSponsorsUrl, { signal: AbortSignal.timeout(5000) });
13180
13189
  if (kvResp.ok) {
13181
13190
  var kvText = await kvResp.text();
13182
13191
  var kvData = kvText.trim() ? JSON.parse(kvText) : { sponsors: [] };
@@ -572817,6 +572826,7 @@ function newDocument(scope) {
572817
572826
  updatedAt: now,
572818
572827
  messageCount: 0,
572819
572828
  participants: {},
572829
+ preferences: {},
572820
572830
  dominantTone: [],
572821
572831
  responseStyle: [
572822
572832
  "Prefer concise, direct answers unless the user asks for depth.",
@@ -572850,6 +572860,7 @@ function normalizeDocument(scope, parsed) {
572850
572860
  label: scope.label || parsed.scope?.label || fresh.scope.label
572851
572861
  },
572852
572862
  participants: parsed.participants && typeof parsed.participants === "object" ? parsed.participants : {},
572863
+ preferences: parsed.preferences && typeof parsed.preferences === "object" ? parsed.preferences : {},
572853
572864
  dominantTone: Array.isArray(parsed.dominantTone) ? parsed.dominantTone : [],
572854
572865
  responseStyle: Array.isArray(parsed.responseStyle) && parsed.responseStyle.length > 0 ? parsed.responseStyle : fresh.responseStyle,
572855
572866
  relationshipModel: Array.isArray(parsed.relationshipModel) && parsed.relationshipModel.length > 0 ? parsed.relationshipModel : fresh.relationshipModel,
@@ -572869,6 +572880,41 @@ function normalizeDocument(scope, parsed) {
572869
572880
  }
572870
572881
  return doc;
572871
572882
  }
572883
+ function updateScopedPersonalityReplyPreference(scope, preference) {
572884
+ const doc = loadScopedPersonality(scope);
572885
+ const now = new Date(preference.ts ?? Date.now()).toISOString();
572886
+ const existing = doc.preferences?.replyMode;
572887
+ const evidence = preference.evidenceMessageId === void 0 ? existing?.evidenceMessageIds ?? [] : [.../* @__PURE__ */ new Set([...existing?.evidenceMessageIds ?? [], preference.evidenceMessageId])].slice(-12);
572888
+ doc.preferences = {
572889
+ ...doc.preferences ?? {},
572890
+ replyMode: {
572891
+ mode: preference.mode,
572892
+ scope: preference.preferenceScope,
572893
+ updatedAt: now,
572894
+ confidence: Math.max(existing?.confidence ?? 0, Math.max(0, Math.min(1, preference.confidence ?? 0.84))),
572895
+ evidenceMessageIds: evidence,
572896
+ note: preference.note ? compactLine(preference.note, 220) : existing?.note,
572897
+ source: preference.source ? compactLine(preference.source, 80) : existing?.source
572898
+ }
572899
+ };
572900
+ doc.updatedAt = now;
572901
+ const line = `${now} preference/reply_mode: ${preference.mode} (${preference.preferenceScope})${preference.note ? ` - ${compactLine(preference.note, 160)}` : ""}`;
572902
+ if (!doc.durableObservations.includes(line)) doc.durableObservations.push(line);
572903
+ doc.durableObservations = doc.durableObservations.slice(-MAX_PROFILE_DURABLE_OBSERVATIONS);
572904
+ appendScopedPersonalityEvent(scope, {
572905
+ ts: preference.ts ?? Date.now(),
572906
+ iso: now,
572907
+ role: "system",
572908
+ mode: "reply-preference",
572909
+ replyMode: preference.mode,
572910
+ preferenceScope: preference.preferenceScope,
572911
+ evidenceMessageId: preference.evidenceMessageId,
572912
+ note: preference.note,
572913
+ source: preference.source
572914
+ });
572915
+ saveScopedPersonality(scope, doc);
572916
+ return doc;
572917
+ }
572872
572918
  function loadScopedPersonality(scope) {
572873
572919
  const paths = scopedPersonalityPaths(scope);
572874
572920
  if (!existsSync88(paths.json)) return newDocument(scope);
@@ -572992,6 +573038,7 @@ ${samples}` : ""}`;
572992
573038
  const topTopics = Object.entries(doc.topicCounts ?? {}).sort((a2, b) => b[1] - a2[1]).slice(0, 18).map(([topic, count]) => `- ${topic}: ${count}`);
572993
573039
  const durable = doc.durableObservations.slice(-18).map((line) => `- ${line}`);
572994
573040
  const relationships = doc.relationshipEvents.slice(-10).map((line) => `- ${line}`);
573041
+ const replyMode = doc.preferences?.replyMode;
572995
573042
  return [
572996
573043
  `# Scoped Personality Profile`,
572997
573044
  ``,
@@ -573005,6 +573052,9 @@ ${samples}` : ""}`;
573005
573052
  `## Response Style`,
573006
573053
  doc.responseStyle.map((line) => `- ${line}`).join("\n"),
573007
573054
  ``,
573055
+ `## Stored Preferences`,
573056
+ replyMode ? `- reply_mode: ${replyMode.mode}; scope=${replyMode.scope}; confidence=${replyMode.confidence.toFixed(2)}; updated=${replyMode.updatedAt}${replyMode.note ? `; note=${replyMode.note}` : ""}` : "- none",
573057
+ ``,
573008
573058
  `## Relationship Model`,
573009
573059
  doc.relationshipModel.map((line) => `- ${line}`).join("\n"),
573010
573060
  ``,
@@ -593445,7 +593495,7 @@ async function stepCohere(config, rl, availableRows) {
593445
593495
  { key: "desc1", label: "" },
593446
593496
  { key: "desc2", label: " COHERE lets your node collaborate with other sponsors" },
593447
593497
  { key: "desc3", label: " on the Omnius meshnet. When someone asks a question" },
593448
- { key: "desc4", label: " on omnius.nexus, your node can:" },
593498
+ { key: "desc4", label: " on openagents.nexus, your node can:" },
593449
593499
  { key: "desc5", label: "" },
593450
593500
  { key: "desc6", label: " • Answer queries using your local models" },
593451
593501
  { key: "desc7", label: " • Bid on questions based on model fit + GPU load" },
@@ -597897,13 +597947,19 @@ import { basename as basename23, dirname as dirname35, relative as relative11, j
597897
597947
  async function parseJsonResponse(resp, source) {
597898
597948
  const body = await resp.text();
597899
597949
  const trimmed = body.trim();
597950
+ const status = resp.status ? ` (HTTP ${resp.status})` : "";
597951
+ const ok3 = typeof resp.ok === "boolean" ? resp.ok : !resp.status || resp.status >= 200 && resp.status < 300;
597952
+ if (!ok3) {
597953
+ const preview = trimmed ? `: ${trimmed.slice(0, 160)}` : "";
597954
+ throw new Error(`${source} request failed${status}${preview}`);
597955
+ }
597900
597956
  if (!trimmed) {
597901
- throw new Error(`${source} returned an empty response${resp.status ? ` (HTTP ${resp.status})` : ""}`);
597957
+ throw new Error(`${source} returned an empty response${status}`);
597902
597958
  }
597903
597959
  try {
597904
597960
  return JSON.parse(trimmed);
597905
597961
  } catch {
597906
- throw new Error(`${source} returned malformed JSON${resp.status ? ` (HTTP ${resp.status})` : ""}`);
597962
+ throw new Error(`${source} returned malformed JSON${status}`);
597907
597963
  }
597908
597964
  }
597909
597965
  async function _immediateReregister(newUrl) {
@@ -597911,7 +597967,7 @@ async function _immediateReregister(newUrl) {
597911
597967
  _lastRegisteredSponsorPayload.tunnelUrl = newUrl;
597912
597968
  _lastRegisteredSponsorPayload.status = "active";
597913
597969
  try {
597914
- await fetch("https://omnius.nexus/api/v1/sponsors", {
597970
+ await fetch(NEXUS_SPONSORS_URL, {
597915
597971
  method: "POST",
597916
597972
  headers: { "Content-Type": "application/json" },
597917
597973
  body: JSON.stringify(_lastRegisteredSponsorPayload),
@@ -597976,7 +598032,7 @@ function startSponsorHeartbeat(payload, getExposeGateway) {
597976
598032
  }
597977
598033
  }
597978
598034
  try {
597979
- await fetch("https://omnius.nexus/api/v1/sponsors", {
598035
+ await fetch(NEXUS_SPONSORS_URL, {
597980
598036
  method: "POST",
597981
598037
  headers: { "Content-Type": "application/json" },
597982
598038
  body: JSON.stringify(_lastRegisteredSponsorPayload),
@@ -602280,10 +602336,11 @@ sleep 1
602280
602336
  });
602281
602337
  }
602282
602338
  try {
602283
- await fetch("https://omnius.nexus/api/v1/sponsors", {
602339
+ await fetch(NEXUS_SPONSORS_URL, {
602284
602340
  method: "POST",
602285
602341
  headers: { "Content-Type": "application/json" },
602286
602342
  body: JSON.stringify({
602343
+ peerId: pauseGw?.peerId || existingConfig.header?.message || "unknown",
602287
602344
  name: existingConfig.header?.message || "unknown",
602288
602345
  status: "inactive",
602289
602346
  tunnelUrl: pauseGw?.tunnelUrl || ""
@@ -602323,11 +602380,12 @@ sleep 1
602323
602380
  );
602324
602381
  try {
602325
602382
  const retirePayload = {
602383
+ peerId: existingConfig.header?.message || "unknown",
602326
602384
  name: existingConfig.header?.message || "unknown",
602327
602385
  status: "inactive",
602328
602386
  tunnelUrl: ""
602329
602387
  };
602330
- await fetch("https://omnius.nexus/api/v1/sponsors", {
602388
+ await fetch(NEXUS_SPONSORS_URL, {
602331
602389
  method: "POST",
602332
602390
  headers: { "Content-Type": "application/json" },
602333
602391
  body: JSON.stringify(retirePayload),
@@ -602366,10 +602424,11 @@ sleep 1
602366
602424
  saveSponsorConfig2(projectDir2, existingConfig);
602367
602425
  stopSponsorHeartbeat();
602368
602426
  try {
602369
- await fetch("https://omnius.nexus/api/v1/sponsors", {
602427
+ await fetch(NEXUS_SPONSORS_URL, {
602370
602428
  method: "POST",
602371
602429
  headers: { "Content-Type": "application/json" },
602372
602430
  body: JSON.stringify({
602431
+ peerId: existingConfig.header?.message || "unknown",
602373
602432
  name: existingConfig.header?.message || "unknown",
602374
602433
  status: "inactive"
602375
602434
  }),
@@ -602386,17 +602445,19 @@ sleep 1
602386
602445
  if (resumeGw && "setSponsorLimits" in resumeGw) {
602387
602446
  resumeGw.setSponsorLimits(existingConfig.rateLimits);
602388
602447
  }
602389
- if (resumeGw?.tunnelUrl) {
602448
+ if (resumeGw?.tunnelUrl || resumeGw?.peerId) {
602390
602449
  const resumePayload = {
602450
+ peerId: resumeGw.peerId || existingConfig.header?.message || "unknown",
602451
+ libp2pPeerId: resumeGw.peerId || "",
602391
602452
  name: existingConfig.header?.message || "Omnius Sponsor",
602392
- tunnelUrl: resumeGw.tunnelUrl,
602453
+ tunnelUrl: resumeGw.tunnelUrl || "",
602393
602454
  authKey: resumeGw.authKey || "",
602394
602455
  models: [],
602395
602456
  status: "active"
602396
602457
  };
602397
602458
  startSponsorHeartbeat(resumePayload, ctx3.getExposeGateway);
602398
602459
  try {
602399
- await fetch("https://omnius.nexus/api/v1/sponsors", {
602460
+ await fetch(NEXUS_SPONSORS_URL, {
602400
602461
  method: "POST",
602401
602462
  headers: { "Content-Type": "application/json" },
602402
602463
  body: JSON.stringify(resumePayload),
@@ -602791,7 +602852,7 @@ sleep 1
602791
602852
  };
602792
602853
  try {
602793
602854
  const kvResp = await fetch(
602794
- "https://omnius.nexus/api/v1/sponsors",
602855
+ NEXUS_SPONSORS_URL,
602795
602856
  {
602796
602857
  method: "POST",
602797
602858
  headers: { "Content-Type": "application/json" },
@@ -606354,6 +606415,32 @@ function mapNumberRecord(value2) {
606354
606415
  }
606355
606416
  return out;
606356
606417
  }
606418
+ function cleanCohereModelName(value2) {
606419
+ const raw = String(value2 ?? "").replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "").trim();
606420
+ if (!raw) return null;
606421
+ const withoutBadge = raw.replace(/^\[(?:EXPOSED|HIDDEN)\]\s*/i, "").trim();
606422
+ const lower = withoutBadge.toLowerCase();
606423
+ if (lower.startsWith("allowlist:") || lower.startsWith("models exposed:") || lower.startsWith("models available:") || lower.startsWith("endpoint:") || lower.startsWith("source:") || lower.startsWith("url:") || lower.includes("downloaded models") || lower.includes("no filter active") || lower.includes("embed") || lower.includes("nomic-bert") || lower.includes("rerank") || lower.includes("whisper") || lower.includes("tts")) {
606424
+ return null;
606425
+ }
606426
+ const beforeMeta = withoutBadge.split(" (")[0]?.trim() || withoutBadge;
606427
+ if (!beforeMeta || beforeMeta.startsWith("──")) return null;
606428
+ return beforeMeta;
606429
+ }
606430
+ function uniqueCohereModelNames(values) {
606431
+ const out = [];
606432
+ const seen = /* @__PURE__ */ new Set();
606433
+ for (const value2 of values) {
606434
+ const pieces = typeof value2 === "string" && value2.includes(",") ? value2.split(",") : [value2];
606435
+ for (const piece of pieces) {
606436
+ const name10 = cleanCohereModelName(piece);
606437
+ if (!name10 || seen.has(name10)) continue;
606438
+ seen.add(name10);
606439
+ out.push(name10);
606440
+ }
606441
+ }
606442
+ return out;
606443
+ }
606357
606444
  function parseCohereStatsOutput(output, isActive = false) {
606358
606445
  try {
606359
606446
  const parsed = JSON.parse(output);
@@ -606374,7 +606461,7 @@ function parseCohereStatsOutput(output, isActive = false) {
606374
606461
  bytesOut: numberField(parsed.bytesOut),
606375
606462
  modelsUsed: mapNumberRecord(parsed.modelsUsed),
606376
606463
  peersServed: mapNumberRecord(parsed.peersServed),
606377
- allowedModels: Array.isArray(parsed.allowedModels) ? parsed.allowedModels.map(String) : null,
606464
+ allowedModels: Array.isArray(parsed.allowedModels) ? uniqueCohereModelNames(parsed.allowedModels) : null,
606378
606465
  endpoint: {
606379
606466
  source: String(endpointRaw.source ?? "unknown"),
606380
606467
  passthrough: endpointRaw.passthrough === true,
@@ -606411,9 +606498,18 @@ async function fetchCohereDashboardState(ctx3) {
606411
606498
  if (r2.success) {
606412
606499
  try {
606413
606500
  const parsed = JSON.parse(r2.output);
606414
- state.modelList = Array.isArray(parsed.models) ? parsed.models.map(String) : [];
606501
+ if (Array.isArray(parsed.modelDetails)) {
606502
+ const exposedDetails = parsed.modelDetails.filter((model) => model?.exposed !== false).map((model) => model?.name);
606503
+ state.modelList = uniqueCohereModelNames(exposedDetails);
606504
+ }
606505
+ if (state.modelList.length === 0 && Array.isArray(parsed.exposedModels)) {
606506
+ state.modelList = uniqueCohereModelNames(parsed.exposedModels);
606507
+ }
606508
+ if (state.modelList.length === 0 && Array.isArray(parsed.models)) {
606509
+ state.modelList = uniqueCohereModelNames(parsed.models);
606510
+ }
606415
606511
  } catch {
606416
- state.modelList = r2.output.split("\n").map((l2) => l2.trim()).filter(Boolean);
606512
+ state.modelList = uniqueCohereModelNames(r2.output.split("\n"));
606417
606513
  }
606418
606514
  }
606419
606515
  } catch {
@@ -606438,8 +606534,8 @@ function cohereStatusLines(stats, modelList) {
606438
606534
  `Data: in ${formatFileSize(stats.bytesIn)} · out ${formatFileSize(stats.bytesOut)}`,
606439
606535
  "",
606440
606536
  `Endpoint: ${stats.endpoint.source}${stats.endpoint.passthrough ? " passthrough" : ""}${stats.endpoint.endpointUrl ? ` · ${stats.endpoint.endpointUrl}` : ""}`,
606441
- `Models available: ${modelList.length || stats.endpoint.modelCount}${stats.endpoint.cachedOnly ? " (cached)" : ""}`,
606442
- `Allowlist: ${stats.allowedModels ? stats.allowedModels.join(", ") || "(empty)" : "all endpoint models"}`,
606537
+ `Models exposed: ${modelList.length || stats.endpoint.modelCount}${stats.endpoint.cachedOnly ? " (cached)" : ""}`,
606538
+ `Allowlist: ${stats.allowedModels ? stats.allowedModels.join(", ") || "(empty)" : "ALL (no filter active)"}`,
606443
606539
  `Top models: ${modelEntries.length ? modelEntries.slice(0, 5).map(([m2, n2]) => `${m2} (${n2})`).join(", ") : "none yet"}`,
606444
606540
  `Peers served: ${peerEntries.length ? peerEntries.slice(0, 5).map(([p2, n2]) => `${p2.slice(0, 20)} (${n2})`).join(", ") : "none yet"}`
606445
606541
  ];
@@ -608053,7 +608149,7 @@ async function discoverSponsorMediaCandidates(ctx3, modality) {
608053
608149
  } catch {
608054
608150
  }
608055
608151
  try {
608056
- const resp = await fetch("https://omnius.nexus/api/v1/sponsors", { signal: AbortSignal.timeout(5e3) });
608152
+ const resp = await fetch(NEXUS_SPONSORS_URL, { signal: AbortSignal.timeout(5e3) });
608057
608153
  if (resp.ok) {
608058
608154
  const data = await parseJsonResponse(resp, "Sponsor directory");
608059
608155
  rawSponsors.push(...(data.sponsors ?? []).filter((s2) => s2.status === "active"));
@@ -608441,7 +608537,7 @@ async function handleSponsoredEndpoint(ctx3, local) {
608441
608537
  }
608442
608538
  }
608443
608539
  try {
608444
- const kvResp = await fetch("https://omnius.nexus/api/v1/sponsors", {
608540
+ const kvResp = await fetch(NEXUS_SPONSORS_URL, {
608445
608541
  signal: AbortSignal.timeout(5e3)
608446
608542
  });
608447
608543
  if (kvResp.ok) {
@@ -608645,7 +608741,8 @@ async function handleSponsoredEndpoint(ctx3, local) {
608645
608741
  selected.peerId,
608646
608742
  selected.authKey || void 0,
608647
608743
  ctx3,
608648
- local
608744
+ local,
608745
+ selected.models
608649
608746
  );
608650
608747
  } else {
608651
608748
  renderError(
@@ -608676,7 +608773,7 @@ async function handleSponsoredEndpoint(ctx3, local) {
608676
608773
  }
608677
608774
  renderInfo(`Connected to sponsored endpoint: ${selected.name}`);
608678
608775
  }
608679
- async function handlePeerEndpoint(peerId, authKey, ctx3, local) {
608776
+ async function handlePeerEndpoint(peerId, authKey, ctx3, local, advertisedModels = []) {
608680
608777
  process.stdout.write(`
608681
608778
  ${c3.dim("Detected:")} ${c3.bold("libp2p peer")}
608682
608779
  `);
@@ -608751,8 +608848,17 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local) {
608751
608848
 
608752
608849
  `
608753
608850
  );
608851
+ let models = [];
608754
608852
  try {
608755
- const models = await fetchModels(peerUrl, authKey);
608853
+ models = await fetchModels(peerUrl, authKey);
608854
+ if (models.length === 0 && advertisedModels.length > 0) {
608855
+ models = advertisedModels.map((name10) => ({
608856
+ name: name10,
608857
+ size: "unknown",
608858
+ modified: "",
608859
+ sizeBytes: 0
608860
+ }));
608861
+ }
608756
608862
  if (models.length > 0) {
608757
608863
  try {
608758
608864
  const { writeFileSync: writeFileSync76, mkdirSync: mkdirSync84 } = await import("node:fs");
@@ -608827,9 +608933,48 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local) {
608827
608933
  );
608828
608934
  }
608829
608935
  } catch {
608830
- renderWarning(
608831
- "Could not discover models on peer. Use /models to try again later."
608832
- );
608936
+ if (advertisedModels.length > 0) {
608937
+ models = advertisedModels.map((name10) => ({
608938
+ name: name10,
608939
+ size: "unknown",
608940
+ modified: "",
608941
+ sizeBytes: 0
608942
+ }));
608943
+ renderWarning("Live model probe failed; using sponsor directory model advertisement.");
608944
+ try {
608945
+ const { writeFileSync: writeFileSync76, mkdirSync: mkdirSync84 } = await import("node:fs");
608946
+ const { join: join154, dirname: dirname44 } = await import("node:path");
608947
+ const cachePath = join154(
608948
+ ctx3.repoRoot || process.cwd(),
608949
+ ".omnius",
608950
+ "nexus",
608951
+ "peer-models-cache.json"
608952
+ );
608953
+ mkdirSync84(dirname44(cachePath), { recursive: true });
608954
+ writeFileSync76(
608955
+ cachePath,
608956
+ JSON.stringify({
608957
+ peerId,
608958
+ cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
608959
+ models: models.map((m2) => ({ name: m2.name, size: m2.size, parameterSize: m2.parameterSize }))
608960
+ }, null, 2)
608961
+ );
608962
+ } catch {
608963
+ }
608964
+ const currentModel = ctx3.config.model;
608965
+ const found = findModel(models, currentModel);
608966
+ if (!found && models.length > 0) {
608967
+ const autoModel = models[0].name;
608968
+ ctx3.setModel(autoModel);
608969
+ if (local) ctx3.saveLocalSettings({ model: autoModel });
608970
+ else ctx3.saveSettings({ model: autoModel });
608971
+ renderModelSwitch(currentModel, autoModel);
608972
+ }
608973
+ } else {
608974
+ renderWarning(
608975
+ "Could not discover models on peer. Use /models to try again later."
608976
+ );
608977
+ }
608833
608978
  }
608834
608979
  ctx3.refreshModelCache?.();
608835
608980
  if (ctx3.hasActiveTask?.()) {
@@ -611312,7 +611457,7 @@ async function showExposeDashboard(gateway, rl, ctx3) {
611312
611457
  renderInfo("Expose gateway stopped.");
611313
611458
  }
611314
611459
  }
611315
- var _sponsorHeartbeatTimer, _lastRegisteredSponsorPayload, __COMMAND_REGISTRY, DASH_INTERNAL;
611460
+ var NEXUS_DIRECTORY_ORIGIN, NEXUS_SPONSORS_URL, _sponsorHeartbeatTimer, _lastRegisteredSponsorPayload, __COMMAND_REGISTRY, DASH_INTERNAL;
611316
611461
  var init_commands = __esm({
611317
611462
  "packages/cli/src/tui/commands.ts"() {
611318
611463
  "use strict";
@@ -611343,6 +611488,8 @@ var init_commands = __esm({
611343
611488
  init_audio_waveform();
611344
611489
  init_neovim_mode();
611345
611490
  init_daemon_registry();
611491
+ NEXUS_DIRECTORY_ORIGIN = (process.env["OMNIUS_NEXUS_DIRECTORY_ORIGIN"] || process.env["OMNIUS_NEXUS_SIGNALING_SERVER"] || "https://openagents.nexus").replace(/\/+$/, "");
611492
+ NEXUS_SPONSORS_URL = `${NEXUS_DIRECTORY_ORIGIN}/api/v1/sponsors`;
611346
611493
  _sponsorHeartbeatTimer = null;
611347
611494
  _lastRegisteredSponsorPayload = null;
611348
611495
  __COMMAND_REGISTRY = /* @__PURE__ */ new Map();
@@ -620323,10 +620470,10 @@ function formatSystemObservations(sessionKey) {
620323
620470
  if (recent.length > 0) {
620324
620471
  const sends = recent.filter((e2) => e2.kind.startsWith("telegram.send."));
620325
620472
  const reactions = recent.filter((e2) => e2.kind.startsWith("emoji."));
620326
- const forbidden = sends.filter((e2) => e2.kind === "telegram.send.forbidden").length;
620327
- const rateLimited = sends.filter((e2) => e2.kind === "telegram.send.rate_limited").length;
620328
- if (forbidden > 0) lines.push(`This chat has refused ${forbidden} recent send attempt(s) (e.g. no rights to post). Treat as a strong silence signal.`);
620329
- if (rateLimited > 0) lines.push(`This chat rate-limited ${rateLimited} recent send(s). Slow cadence.`);
620473
+ const failures = sends.filter((e2) => e2.kind === "telegram.send.failed");
620474
+ if (failures.length > 0) {
620475
+ lines.push(`This chat has ${failures.length} recent raw Telegram send failure(s). Latest raw failure: ${failures[failures.length - 1]?.reason ?? "unknown"}.`);
620476
+ }
620330
620477
  if (reactions.length > 0) {
620331
620478
  const reactSummary = reactions.filter((e2) => e2.kind === "emoji.reaction.received").map((e2) => e2.emoji).join("");
620332
620479
  if (reactSummary) lines.push(`Recent inbound reactions in this chat: ${reactSummary}`);
@@ -621773,6 +621920,15 @@ function telegramSocialActorKind(actor) {
621773
621920
  function telegramSocialThreadKey(input) {
621774
621921
  return input.messageThreadId !== void 0 ? `chat:${String(input.chatId)}:thread:${input.messageThreadId}` : `chat:${String(input.chatId)}`;
621775
621922
  }
621923
+ function telegramSocialChatPreferenceKey(chatId) {
621924
+ return `chat:${String(chatId)}`;
621925
+ }
621926
+ function telegramSocialUserInChatPreferenceKey(chatId, actorKey) {
621927
+ return `${telegramSocialChatPreferenceKey(chatId)}:${actorKey}`;
621928
+ }
621929
+ function telegramTextDeliveryCapabilityKey(input) {
621930
+ return input.messageThreadId !== void 0 ? `${telegramSocialChatPreferenceKey(input.chatId)}:thread:${input.messageThreadId}:text` : `${telegramSocialChatPreferenceKey(input.chatId)}:text`;
621931
+ }
621776
621932
  function appendUnique(items, value2, max) {
621777
621933
  if (value2 === void 0 || value2 === null) return items.slice(-max);
621778
621934
  const next = items.filter((item) => item !== value2);
@@ -621845,14 +622001,29 @@ function formatTelegramSocialStateContext(state, input) {
621845
622001
  const salience = state.salience.filter((item) => item.senderKey === senderKey3 || item.chatId === String(input.chatId)).sort((a2, b) => b.ts - a2.ts).slice(0, limit);
621846
622002
  const daydreams = Object.values(state.daydreamOpportunities).filter((item) => item.lifecycle !== "retired").sort((a2, b) => b.updatedAt - a2.updatedAt).slice(0, Math.min(5, limit));
621847
622003
  const preferences = preferenceLines(state.preferences[senderKey3]);
622004
+ const chatPreferenceKey = telegramSocialChatPreferenceKey(input.chatId);
622005
+ const userChatPreferenceKey = telegramSocialUserInChatPreferenceKey(input.chatId, senderKey3);
622006
+ const scopedPreferences = [
622007
+ ...preferenceLines(state.preferences[userChatPreferenceKey], "user-in-chat"),
622008
+ ...preferenceLines(state.preferences[chatPreferenceKey], "chat")
622009
+ ];
622010
+ const resolvedReplyMode = resolveReplyModePreferenceForContext(state, input.chatId, senderKey3);
622011
+ const delivery = state.deliveryCapabilities[telegramTextDeliveryCapabilityKey({
622012
+ chatId: input.chatId,
622013
+ messageThreadId: input.messageThreadId
622014
+ })] ?? state.deliveryCapabilities[telegramTextDeliveryCapabilityKey({ chatId: input.chatId })];
621848
622015
  return [
621849
622016
  "### Telegram Structured Social State",
621850
622017
  selfKey ? `Agent self node: ${selfKey}` : "Agent self node: unknown",
621851
622018
  `Identity boundary: the agent is the self node only. Current actor ${senderKey3} is ${senderIdentity}; reply target ${replyKey ?? "none"} is ${replyIdentity}. Participant first-person claims belong to their actor node, not the agent, unless that actor is the self node.`,
621852
622019
  `Current actor node: ${senderKey3} [${participant?.actorKind || telegramSocialActorKind(input)}] messages=${participant?.messageCount ?? 0}${participant?.lastText ? ` last=${jsonLine(participant.lastText, 140)}` : ""}`,
621853
622020
  thread ? `Active channel/thread: ${thread.key}; messages=${thread.messageCount}; participants=${thread.participantKeys.slice(-8).join(", ") || "none"}; last_outcomes=${thread.lastOutcomeIds.slice(-5).join(", ") || "none"}` : "",
622021
+ delivery ? `Telegram text delivery raw warning stream: ${delivery.status}; key=${telegramTextDeliveryCapabilityKey({ chatId: input.chatId, messageThreadId: input.messageThreadId })}${delivery.reason ? `; raw=${jsonLine(delivery.reason, 180)}` : ""}` : "Telegram text delivery raw warning stream: none recorded",
622022
+ resolvedReplyMode ? `Resolved reply mode: ${resolvedReplyMode.item.mode} from ${resolvedReplyMode.item.scope} (${resolvedReplyMode.key}); confidence=${resolvedReplyMode.item.confidence.toFixed(2)}${resolvedReplyMode.item.note ? ` note=${jsonLine(resolvedReplyMode.item.note, 120)}` : ""}` : "Resolved reply mode: notes_then_reply (system default)",
621854
622023
  preferences.length ? `Relevant preference vector for ${senderKey3}:
621855
622024
  ${preferences.join("\n")}` : `Relevant preference vector for ${senderKey3}: no durable preferences yet`,
622025
+ scopedPreferences.length ? `Scoped reply preferences:
622026
+ ${scopedPreferences.join("\n")}` : "",
621856
622027
  salience.length ? `Recent salience observations:
621857
622028
  ${salience.map((item) => `- ${iso(item.ts)} ${item.senderKey}: ${item.signals.join(", ") || "none"}; text=${jsonLine(item.textPreview, 120)}`).join("\n")}` : "",
621858
622029
  edges.length ? `Relevant relationship edges:
@@ -621863,11 +622034,26 @@ ${outcomes.map(formatOutcome).join("\n")}` : "Prior response outcomes: none yet"
621863
622034
  ${daydreams.map((item) => `- ${item.id} [${item.lifecycle}] confidence=${item.confidence.toFixed(2)} trigger=${jsonLine(item.trigger, 140)}`).join("\n")}` : ""
621864
622035
  ].filter(Boolean).join("\n");
621865
622036
  }
621866
- function preferenceLines(vector) {
622037
+ function resolveReplyModePreferenceForContext(state, chatId, senderKey3) {
622038
+ for (const key of [
622039
+ telegramSocialUserInChatPreferenceKey(chatId, senderKey3),
622040
+ senderKey3,
622041
+ telegramSocialChatPreferenceKey(chatId)
622042
+ ]) {
622043
+ const item = state.preferences[key]?.replyMode;
622044
+ if (item) return { key, item };
622045
+ }
622046
+ return void 0;
622047
+ }
622048
+ function preferenceLines(vector, prefix) {
621867
622049
  if (!vector) return [];
621868
622050
  return Object.entries(vector).map(([key, evidence]) => {
622051
+ if (key === "replyMode") {
622052
+ const item2 = evidence;
622053
+ return `- ${prefix ? `${prefix}.` : ""}${key}: mode=${item2.mode} scope=${item2.scope} confidence=${item2.confidence.toFixed(2)} evidence=${item2.evidenceMessageIds.join(",") || "none"}${item2.note ? ` note=${jsonLine(item2.note, 120)}` : ""}`;
622054
+ }
621869
622055
  const item = evidence;
621870
- return `- ${key}: value=${item.value.toFixed(2)} confidence=${item.confidence.toFixed(2)} evidence=${item.evidenceMessageIds.join(",") || "none"}${item.note ? ` note=${jsonLine(item.note, 120)}` : ""}`;
622056
+ return `- ${prefix ? `${prefix}.` : ""}${key}: value=${item.value.toFixed(2)} confidence=${item.confidence.toFixed(2)} evidence=${item.evidenceMessageIds.join(",") || "none"}${item.note ? ` note=${jsonLine(item.note, 120)}` : ""}`;
621871
622057
  });
621872
622058
  }
621873
622059
  function formatEdge(edge) {
@@ -621897,6 +622083,7 @@ function createTelegramSocialState(now = Date.now()) {
621897
622083
  participants: {},
621898
622084
  relationships: [],
621899
622085
  preferences: {},
622086
+ deliveryCapabilities: {},
621900
622087
  threads: {},
621901
622088
  salience: [],
621902
622089
  outcomes: [],
@@ -621913,6 +622100,7 @@ function normalizeTelegramSocialState(raw) {
621913
622100
  state.participants = normalizeRecord(value2.participants, normalizeParticipant);
621914
622101
  state.relationships = Array.isArray(value2.relationships) ? value2.relationships.map(normalizeRelationship).filter(Boolean).slice(-TELEGRAM_SOCIAL_LIMITS.relationships) : [];
621915
622102
  state.preferences = normalizePreferences(value2.preferences);
622103
+ state.deliveryCapabilities = normalizeRecord(value2.deliveryCapabilities, normalizeDeliveryCapability);
621916
622104
  state.threads = normalizeRecord(value2.threads, normalizeThread);
621917
622105
  state.salience = Array.isArray(value2.salience) ? value2.salience.map(normalizeSalience).filter(Boolean).slice(-TELEGRAM_SOCIAL_LIMITS.salience) : [];
621918
622106
  state.outcomes = Array.isArray(value2.outcomes) ? value2.outcomes.map(normalizeOutcome).filter(Boolean).slice(-TELEGRAM_SOCIAL_LIMITS.outcomes) : [];
@@ -621982,9 +622170,55 @@ function normalizePreferences(raw) {
621982
622170
  note: compactOptional(evidence.note, 220)
621983
622171
  };
621984
622172
  }
622173
+ const replyMode = vector["replyMode"];
622174
+ if (replyMode && typeof replyMode === "object" && !Array.isArray(replyMode)) {
622175
+ const record = replyMode;
622176
+ const mode = normalizeReplyMode(record["mode"]);
622177
+ if (mode) {
622178
+ out[actorKey].replyMode = {
622179
+ mode,
622180
+ scope: normalizeReplyPreferenceScope(record["scope"]),
622181
+ confidence: clamp0110(numberOr(record["confidence"], 0.8)),
622182
+ updatedAt: numberOr(record["updatedAt"], Date.now()),
622183
+ evidenceMessageIds: Array.isArray(record["evidenceMessageIds"]) ? record["evidenceMessageIds"].filter(isNumber).slice(-12) : [],
622184
+ note: compactOptional(record["note"], 220),
622185
+ source: compactOptional(record["source"], 80)
622186
+ };
622187
+ }
622188
+ }
621985
622189
  }
621986
622190
  return out;
621987
622191
  }
622192
+ function normalizeReplyMode(raw) {
622193
+ return raw === "reply_then_notes" || raw === "notes_then_reply" || raw === "reply_only" ? raw : void 0;
622194
+ }
622195
+ function normalizeReplyPreferenceScope(raw) {
622196
+ return raw === "chat" || raw === "user_in_chat" || raw === "user" ? raw : "user_in_chat";
622197
+ }
622198
+ function normalizeDeliveryCapability(raw) {
622199
+ if (!raw || typeof raw !== "object") return null;
622200
+ const value2 = raw;
622201
+ const key = compact2(value2.key || "", 180);
622202
+ const chatId = compact2(value2.chatId || "", 120);
622203
+ if (!key || !chatId) return null;
622204
+ const status = normalizeDeliveryStatus(value2.status);
622205
+ return {
622206
+ key,
622207
+ chatId,
622208
+ messageThreadId: typeof value2.messageThreadId === "number" ? value2.messageThreadId : void 0,
622209
+ status,
622210
+ canSendText: typeof value2.canSendText === "boolean" ? value2.canSendText : status === "allowed" ? true : void 0,
622211
+ checkedAt: numberOr(value2.checkedAt, Date.now()),
622212
+ lastSuccessAt: typeof value2.lastSuccessAt === "number" ? value2.lastSuccessAt : void 0,
622213
+ lastFailureAt: typeof value2.lastFailureAt === "number" ? value2.lastFailureAt : void 0,
622214
+ reason: compactOptional(value2.reason, 320),
622215
+ retryAfterSec: typeof value2.retryAfterSec === "number" ? Math.max(0, Math.floor(value2.retryAfterSec)) : void 0,
622216
+ source: compactOptional(value2.source, 80)
622217
+ };
622218
+ }
622219
+ function normalizeDeliveryStatus(raw) {
622220
+ return raw === "allowed" || raw === "failed" ? raw : "unknown";
622221
+ }
621988
622222
  function normalizeThread(raw) {
621989
622223
  if (!raw || typeof raw !== "object") return null;
621990
622224
  const value2 = raw;
@@ -622185,6 +622419,73 @@ function markTelegramDaydreamOpportunitiesConsidered(state, opportunities, messa
622185
622419
  }
622186
622420
  return registered;
622187
622421
  }
622422
+ function setTelegramReplyModePreference(state, input) {
622423
+ const now = input.ts ?? Date.now();
622424
+ const actorKey = input.actor ? telegramSocialActorKey(input.actor) : void 0;
622425
+ const key = input.scope === "chat" ? telegramSocialChatPreferenceKey(input.chatId) : input.scope === "user_in_chat" && actorKey ? telegramSocialUserInChatPreferenceKey(input.chatId, actorKey) : actorKey ?? telegramSocialChatPreferenceKey(input.chatId);
622426
+ const vector = state.preferences[key] ?? {};
622427
+ const existing = vector.replyMode;
622428
+ const next = {
622429
+ mode: input.mode,
622430
+ scope: input.scope,
622431
+ confidence: Math.max(existing?.confidence ?? 0, clamp0110(input.confidence ?? 0.84)),
622432
+ updatedAt: now,
622433
+ evidenceMessageIds: appendUnique(existing?.evidenceMessageIds ?? [], input.messageId, 12),
622434
+ note: compactOptional(input.note, 220),
622435
+ source: compactOptional(input.source, 80)
622436
+ };
622437
+ vector.replyMode = next;
622438
+ state.preferences[key] = vector;
622439
+ state.updatedAt = now;
622440
+ return next;
622441
+ }
622442
+ function resolveTelegramReplyModePreference(state, input) {
622443
+ const actorKey = input.actor ? telegramSocialActorKey(input.actor) : void 0;
622444
+ const keys = [
622445
+ actorKey ? telegramSocialUserInChatPreferenceKey(input.chatId, actorKey) : "",
622446
+ actorKey ?? "",
622447
+ telegramSocialChatPreferenceKey(input.chatId)
622448
+ ].filter(Boolean);
622449
+ for (const key of keys) {
622450
+ const pref = state.preferences[key]?.replyMode;
622451
+ if (!pref) continue;
622452
+ return {
622453
+ key,
622454
+ mode: pref.mode,
622455
+ scope: pref.scope,
622456
+ confidence: pref.confidence,
622457
+ updatedAt: pref.updatedAt,
622458
+ note: pref.note,
622459
+ source: pref.source
622460
+ };
622461
+ }
622462
+ return void 0;
622463
+ }
622464
+ function setTelegramTextDeliveryCapability(state, input) {
622465
+ const now = input.ts ?? Date.now();
622466
+ const key = telegramTextDeliveryCapabilityKey(input);
622467
+ const existing = state.deliveryCapabilities[key];
622468
+ const canSendText = input.status === "allowed" ? true : input.status === "failed" ? void 0 : input.canSendText ?? existing?.canSendText;
622469
+ const next = {
622470
+ key,
622471
+ chatId: String(input.chatId),
622472
+ messageThreadId: input.messageThreadId,
622473
+ status: input.status,
622474
+ canSendText,
622475
+ checkedAt: now,
622476
+ lastSuccessAt: input.status === "allowed" ? now : existing?.lastSuccessAt,
622477
+ lastFailureAt: input.status === "failed" ? now : existing?.lastFailureAt,
622478
+ reason: compactOptional(input.reason, 320) || existing?.reason,
622479
+ retryAfterSec: input.retryAfterSec,
622480
+ source: compactOptional(input.source, 80)
622481
+ };
622482
+ state.deliveryCapabilities[key] = next;
622483
+ state.updatedAt = now;
622484
+ return next;
622485
+ }
622486
+ function telegramTextDeliveryCapabilityFor(state, input) {
622487
+ return state.deliveryCapabilities[telegramTextDeliveryCapabilityKey(input)] ?? state.deliveryCapabilities[telegramTextDeliveryCapabilityKey({ chatId: input.chatId })];
622488
+ }
622188
622489
  function commitDecisionEdges(state, input, senderKey3, selfKey, now) {
622189
622490
  if (!selfKey) return;
622190
622491
  if (input.shouldReply) {
@@ -622600,6 +622901,28 @@ function telegramDecisionNumber(parsed, keys, nestedKeys = ["internal_notes", "i
622600
622901
  }
622601
622902
  return void 0;
622602
622903
  }
622904
+ function parseTelegramReplyMode(raw) {
622905
+ return raw === "reply_then_notes" || raw === "notes_then_reply" || raw === "reply_only" ? raw : void 0;
622906
+ }
622907
+ function parseTelegramReplyPreferenceToolScope(raw) {
622908
+ return raw === "current_user_in_chat" || raw === "current_user_global" || raw === "current_group" ? raw : void 0;
622909
+ }
622910
+ function parseTelegramReplyPreferenceUpdate(parsed) {
622911
+ const raw = parsed["reply_mode_preference"] ?? parsed["replyModePreference"] ?? parsed["preference_update"] ?? parsed["preferenceUpdate"];
622912
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
622913
+ const record = raw;
622914
+ const replyMode = parseTelegramReplyMode(record["reply_mode"] ?? record["replyMode"] ?? record["mode"]);
622915
+ const scope = parseTelegramReplyPreferenceToolScope(record["scope"]);
622916
+ if (!replyMode || !scope) return void 0;
622917
+ const confidence2 = Number(record["confidence"]);
622918
+ return {
622919
+ scope,
622920
+ replyMode,
622921
+ confidence: Number.isFinite(confidence2) ? Math.max(0, Math.min(1, confidence2)) : void 0,
622922
+ note: cleanTelegramDecisionNote(record["note"] ?? record["reason"] ?? record["rationale"]),
622923
+ source: cleanTelegramDecisionNote(record["source"])
622924
+ };
622925
+ }
622603
622926
  function uniqueTelegramJsonCandidates(candidates) {
622604
622927
  const seen = /* @__PURE__ */ new Set();
622605
622928
  const unique = [];
@@ -622942,7 +623265,8 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
622942
623265
  scenarioLabel: telegramDecisionNote(parsed, ["scenario_label", "scenarioLabel", "label"], ["scenario", "classification"]),
622943
623266
  scenarioConfidence: telegramDecisionNumber(parsed, ["scenario_confidence", "scenarioConfidence", "confidence"], ["scenario", "classification"]),
622944
623267
  scenarioObjective: telegramDecisionNote(parsed, ["scenario_objective", "scenarioObjective", "objective"], ["scenario", "classification"]),
622945
- scenarioStateLoop: telegramDecisionNote(parsed, ["scenario_state_loop", "scenarioStateLoop", "state_loop", "stateLoop"], ["scenario", "classification"])
623268
+ scenarioStateLoop: telegramDecisionNote(parsed, ["scenario_state_loop", "scenarioStateLoop", "state_loop", "stateLoop"], ["scenario", "classification"]),
623269
+ replyPreferenceUpdate: parseTelegramReplyPreferenceUpdate(parsed)
622946
623270
  };
622947
623271
  } catch {
622948
623272
  continue;
@@ -625167,6 +625491,7 @@ External acquisition contract:
625167
625491
  decision2.voiceNote ? `voice note: ${decision2.voiceNote}` : "",
625168
625492
  decision2.scenarioNote ? `scenario note: ${decision2.scenarioNote}` : "",
625169
625493
  decision2.scenarioId ? `scenario classification: ${decision2.scenarioId}${decision2.scenarioLabel ? ` (${decision2.scenarioLabel})` : ""}` : "",
625494
+ decision2.replyPreferenceUpdate ? `reply preference update: ${decision2.replyPreferenceUpdate.replyMode} (${this.telegramReplyPreferenceScopeLabel(decision2.replyPreferenceUpdate.scope)})` : "",
625170
625495
  cadence ? `next attention sample: ${cadence}` : ""
625171
625496
  ].filter(Boolean);
625172
625497
  if (viewId && this.subAgentViewCallbacks) {
@@ -625187,7 +625512,8 @@ External acquisition contract:
625187
625512
  decision2.procedureNote ? `procedure note: ${decision2.procedureNote}` : "",
625188
625513
  decision2.voiceNote ? `voice note: ${decision2.voiceNote}` : "",
625189
625514
  decision2.scenarioNote ? `scenario note: ${decision2.scenarioNote}` : "",
625190
- decision2.scenarioId ? `scenario classification: ${decision2.scenarioId}${decision2.scenarioLabel ? ` (${decision2.scenarioLabel})` : ""}` : ""
625515
+ decision2.scenarioId ? `scenario classification: ${decision2.scenarioId}${decision2.scenarioLabel ? ` (${decision2.scenarioLabel})` : ""}` : "",
625516
+ decision2.replyPreferenceUpdate ? `reply preference update: ${decision2.replyPreferenceUpdate.replyMode} (${this.telegramReplyPreferenceScopeLabel(decision2.replyPreferenceUpdate.scope)})` : ""
625191
625517
  ].filter(Boolean);
625192
625518
  this.tuiWrite(() => {
625193
625519
  renderTelegramSubAgentEvent(msg.username, primary);
@@ -625604,6 +625930,7 @@ ${message2}`)
625604
625930
  decision2.voiceNote ? `voice_note: ${decision2.voiceNote}` : "",
625605
625931
  decision2.memoryNote ? `memory_note: ${decision2.memoryNote}` : "",
625606
625932
  decision2.relationshipNote ? `relationship_note: ${decision2.relationshipNote}` : "",
625933
+ decision2.replyPreferenceUpdate ? `reply_preference_update: ${decision2.replyPreferenceUpdate.replyMode} (${this.telegramReplyPreferenceScopeLabel(decision2.replyPreferenceUpdate.scope)})` : "",
625607
625934
  "Use this as classifier output from the prior attention stage, not as a new instruction to spawn or route again."
625608
625935
  ].filter(Boolean).join("\n");
625609
625936
  }
@@ -626409,6 +626736,158 @@ ${mediaContext}` : ""
626409
626736
  repoRoot: this.repoRoot || "."
626410
626737
  };
626411
626738
  }
626739
+ telegramUserPersonalityScope(msg) {
626740
+ const id = msg.fromUserId ? `telegram-user:${msg.fromUserId}` : `telegram-user:${msg.username || msg.firstName || "unknown"}`;
626741
+ const label = `user-${msg.username || msg.firstName || msg.fromUserId || "unknown"}`;
626742
+ return {
626743
+ kind: "telegram-user",
626744
+ id,
626745
+ label,
626746
+ repoRoot: this.repoRoot || "."
626747
+ };
626748
+ }
626749
+ telegramUserInChatPersonalityScope(sessionKey, msg) {
626750
+ const user = msg.fromUserId ? `user:${msg.fromUserId}` : `user:${msg.username || msg.firstName || "unknown"}`;
626751
+ const chat = msg.chatTitle || msg.chatType || String(msg.chatId);
626752
+ return {
626753
+ kind: "telegram-user-in-chat",
626754
+ id: `${sessionKey}:${user}`,
626755
+ label: `${chat}-${msg.username || msg.firstName || msg.fromUserId || "unknown"}`,
626756
+ repoRoot: this.repoRoot || "."
626757
+ };
626758
+ }
626759
+ telegramSocialPreferenceScope(scope) {
626760
+ if (scope === "current_group") return "chat";
626761
+ if (scope === "current_user_global") return "user";
626762
+ return "user_in_chat";
626763
+ }
626764
+ telegramReplyPreferenceScopeLabel(scope) {
626765
+ if (scope === "current_group") return "current group";
626766
+ if (scope === "current_user_global") return "current user globally";
626767
+ return "current user in this group/chat";
626768
+ }
626769
+ scopedPersonalityForReplyPreference(sessionKey, msg, scope) {
626770
+ if (scope === "current_group") return this.telegramPersonalityScope(sessionKey, msg);
626771
+ if (scope === "current_user_global") return this.telegramUserPersonalityScope(msg);
626772
+ return this.telegramUserInChatPersonalityScope(sessionKey, msg);
626773
+ }
626774
+ applyTelegramReplyPreferenceUpdate(sessionKey, msg, update2, source) {
626775
+ if (!update2) return void 0;
626776
+ const preferenceScope = this.telegramSocialPreferenceScope(update2.scope);
626777
+ const evidence = setTelegramReplyModePreference(this.telegramSocialStateForSession(sessionKey), {
626778
+ chatId: msg.chatId,
626779
+ actor: this.telegramMessageSocialActorInput(msg),
626780
+ scope: preferenceScope,
626781
+ mode: update2.replyMode,
626782
+ confidence: update2.confidence,
626783
+ messageId: msg.messageId,
626784
+ note: update2.note,
626785
+ source: update2.source || source
626786
+ });
626787
+ try {
626788
+ updateScopedPersonalityReplyPreference(
626789
+ this.scopedPersonalityForReplyPreference(sessionKey, msg, update2.scope),
626790
+ {
626791
+ mode: update2.replyMode,
626792
+ preferenceScope,
626793
+ confidence: evidence.confidence,
626794
+ evidenceMessageId: msg.messageId,
626795
+ note: update2.note,
626796
+ source: update2.source || source
626797
+ }
626798
+ );
626799
+ } catch {
626800
+ }
626801
+ this.saveTelegramConversationState(sessionKey);
626802
+ return `stored reply_mode=${update2.replyMode} for ${this.telegramReplyPreferenceScopeLabel(update2.scope)}`;
626803
+ }
626804
+ hydrateTelegramReplyPreferencesFromPersona(sessionKey, msg) {
626805
+ for (const [scope, personalityScope] of [
626806
+ ["current_group", this.telegramPersonalityScope(sessionKey, msg)],
626807
+ ["current_user_global", this.telegramUserPersonalityScope(msg)],
626808
+ ["current_user_in_chat", this.telegramUserInChatPersonalityScope(sessionKey, msg)]
626809
+ ]) {
626810
+ try {
626811
+ const pref = loadScopedPersonality(personalityScope).preferences?.replyMode;
626812
+ if (!pref) continue;
626813
+ setTelegramReplyModePreference(this.telegramSocialStateForSession(sessionKey), {
626814
+ chatId: msg.chatId,
626815
+ actor: this.telegramMessageSocialActorInput(msg),
626816
+ scope: pref.scope,
626817
+ mode: pref.mode,
626818
+ confidence: pref.confidence,
626819
+ note: pref.note,
626820
+ source: pref.source || `persona:${scope}`,
626821
+ ts: Number.isFinite(Date.parse(pref.updatedAt)) ? Date.parse(pref.updatedAt) : Date.now()
626822
+ });
626823
+ } catch {
626824
+ }
626825
+ }
626826
+ }
626827
+ resolvedTelegramReplyMode(sessionKey, msg) {
626828
+ try {
626829
+ this.hydrateTelegramReplyPreferencesFromPersona(sessionKey, msg);
626830
+ return resolveTelegramReplyModePreference(this.telegramSocialStateForSession(sessionKey), {
626831
+ chatId: msg.chatId,
626832
+ actor: this.telegramMessageSocialActorInput(msg)
626833
+ })?.mode ?? "notes_then_reply";
626834
+ } catch {
626835
+ return "notes_then_reply";
626836
+ }
626837
+ }
626838
+ stripTelegramDecisionNotes(decision2) {
626839
+ return {
626840
+ ...decision2,
626841
+ silentDisposition: void 0,
626842
+ mentalNote: void 0,
626843
+ memoryNote: void 0,
626844
+ relationshipNote: void 0,
626845
+ procedureNote: void 0,
626846
+ voiceNote: void 0,
626847
+ scenarioNote: void 0,
626848
+ scenarioId: void 0,
626849
+ scenarioLabel: void 0,
626850
+ scenarioConfidence: void 0,
626851
+ scenarioObjective: void 0,
626852
+ scenarioStateLoop: void 0
626853
+ };
626854
+ }
626855
+ telegramTextDeliveryCapability(sessionKey, chatId, messageThreadId) {
626856
+ try {
626857
+ return telegramTextDeliveryCapabilityFor(this.telegramSocialStateForSession(sessionKey), {
626858
+ chatId,
626859
+ messageThreadId
626860
+ });
626861
+ } catch {
626862
+ return void 0;
626863
+ }
626864
+ }
626865
+ updateTelegramTextDeliveryCapability(chatId, input) {
626866
+ const sessionKey = `chat:${String(chatId)}`;
626867
+ try {
626868
+ const capability = setTelegramTextDeliveryCapability(this.telegramSocialStateForSession(sessionKey), {
626869
+ chatId,
626870
+ messageThreadId: input.messageThreadId,
626871
+ status: input.status,
626872
+ canSendText: input.canSendText,
626873
+ reason: input.reason,
626874
+ retryAfterSec: input.retryAfterSec,
626875
+ source: input.source
626876
+ });
626877
+ this.saveTelegramConversationState(sessionKey);
626878
+ return capability;
626879
+ } catch {
626880
+ return void 0;
626881
+ }
626882
+ }
626883
+ formatTelegramDeliveryCapabilityContext(sessionKey, msg) {
626884
+ const capability = this.telegramTextDeliveryCapability(sessionKey, msg.chatId, msg.messageThreadId);
626885
+ if (!capability) return "Telegram text delivery raw warning stream for this chat: none recorded.";
626886
+ return [
626887
+ `Telegram text delivery raw warning stream for this chat: ${capability.status}.`,
626888
+ capability.reason ? `Latest raw Telegram warning/error text: ${capability.reason}` : ""
626889
+ ].filter(Boolean).join(" ");
626890
+ }
626412
626891
  ensureTelegramConversationLoaded(sessionKey) {
626413
626892
  if (this.loadedConversationState.has(sessionKey)) return;
626414
626893
  this.loadedConversationState.add(sessionKey);
@@ -626918,7 +627397,7 @@ ${mediaContext}` : ""
626918
627397
  convertMarkdownToTelegramHTML(decision2.text),
626919
627398
  replyTo
626920
627399
  );
626921
- this.recordTelegramAssistantMessage({
627400
+ if (sentMessageId !== null) this.recordTelegramAssistantMessage({
626922
627401
  chatId,
626923
627402
  chatType: artifact.chatType,
626924
627403
  chatTitle: artifact.chatTitle,
@@ -626931,6 +627410,7 @@ ${mediaContext}` : ""
626931
627410
  messageId: sentMessageId,
626932
627411
  replyToMessageId: replyTo
626933
627412
  });
627413
+ if (sentMessageId === null) return { sent: false, reason: "Telegram returned no delivered message id" };
626934
627414
  state.lastFollowupAt = now;
626935
627415
  this.saveTelegramConversationState(sessionKey);
626936
627416
  this.tuiWrite(() => renderTelegramSubAgentEvent("reflection", `sent scoped Telegram follow-up (${decision2.confidence.toFixed(2)}): ${decision2.reason}`));
@@ -627021,6 +627501,7 @@ ${mediaContext}` : ""
627021
627501
  buildTelegramSocialStateContext(sessionKey, msg, salienceSignals = []) {
627022
627502
  try {
627023
627503
  this.registerLatestTelegramDaydreamOpportunities(sessionKey);
627504
+ this.hydrateTelegramReplyPreferencesFromPersona(sessionKey, msg);
627024
627505
  return formatTelegramSocialStateContext(this.telegramSocialStateForSession(sessionKey), {
627025
627506
  sessionKey,
627026
627507
  chatId: msg.chatId,
@@ -627149,6 +627630,28 @@ ${mediaContext}` : ""
627149
627630
  msg.chatType !== "private" ? `Group participant ${telegramSpeakerLabel(msg)} in ${msg.chatTitle || "unknown group"}` : `Private Telegram chat with ${telegramSpeakerLabel(msg)}`
627150
627631
  ]
627151
627632
  });
627633
+ updateScopedPersonality(this.telegramUserPersonalityScope(msg), {
627634
+ speaker: telegramSpeakerLabel(msg),
627635
+ text,
627636
+ role: "user",
627637
+ mode,
627638
+ ts: entry.ts,
627639
+ toneTags: inferTelegramToneTags(text),
627640
+ relationshipHints: [
627641
+ `Telegram user identity ${telegramSpeakerLabel(msg)} observed in ${msg.chatType}`
627642
+ ]
627643
+ });
627644
+ updateScopedPersonality(this.telegramUserInChatPersonalityScope(sessionKey, msg), {
627645
+ speaker: telegramSpeakerLabel(msg),
627646
+ text,
627647
+ role: "user",
627648
+ mode,
627649
+ ts: entry.ts,
627650
+ toneTags: inferTelegramToneTags(text),
627651
+ relationshipHints: [
627652
+ msg.chatType !== "private" ? `Participant-specific preference/memory scope for ${telegramSpeakerLabel(msg)} in ${msg.chatTitle || "unknown group"}` : `Participant-specific preference/memory scope for private chat with ${telegramSpeakerLabel(msg)}`
627653
+ ]
627654
+ });
627152
627655
  } catch {
627153
627656
  }
627154
627657
  this.saveTelegramConversationState(sessionKey);
@@ -628880,7 +629383,7 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`,
628880
629383
  routeInstruction,
628881
629384
  ``,
628882
629385
  `Return JSON only with this schema:`,
628883
- `{"recoverable":true|false,"route":"chat"|"action","should_reply":true|false,"confidence":0.0-1.0,"reason":"short reason","attention_state":"idle"|"observing"|"engaged"|"cooldown","attention_delta":-1.0..1.0,"next_check_after_messages":1..12,"silent_disposition":"short outcome-level disposition","mental_note":"short outcome-level observation","memory_note":"short memory/summary update","relationship_note":"short relationship/thread note","procedure_note":"short tree/procedure note","voice_note":"short final-voice note","scenario_note":"short scenario/transition note","scenario_id":"dynamic inferred scenario id","scenario_label":"human readable dynamic scenario label","scenario_confidence":0.0-1.0,"scenario_objective":"current scenario objective","scenario_state_loop":"state loop to maintain until transition"}`,
629386
+ `{"recoverable":true|false,"route":"chat"|"action","should_reply":true|false,"confidence":0.0-1.0,"reason":"short reason","attention_state":"idle"|"observing"|"engaged"|"cooldown","attention_delta":-1.0..1.0,"next_check_after_messages":1..12,"silent_disposition":"short outcome-level disposition","mental_note":"short outcome-level observation","memory_note":"short memory/summary update","relationship_note":"short relationship/thread note","procedure_note":"short tree/procedure note","voice_note":"short final-voice note","scenario_note":"short scenario/transition note","scenario_id":"dynamic inferred scenario id","scenario_label":"human readable dynamic scenario label","scenario_confidence":0.0-1.0,"scenario_objective":"current scenario objective","scenario_state_loop":"state loop to maintain until transition","reply_mode_preference":null|{"scope":"current_user_in_chat"|"current_user_global"|"current_group","reply_mode":"reply_then_notes"|"notes_then_reply"|"reply_only","confidence":0.0-1.0,"note":"why this durable preference was explicitly expressed"}}`,
628884
629387
  ``,
628885
629388
  `Original router output:`,
628886
629389
  rawPreview,
@@ -628944,7 +629447,7 @@ ${userPrompt.slice(-4e3)}` : userPrompt;
628944
629447
  `Return exactly one JSON object and no prose. No <think> tags. No commentary.`,
628945
629448
  routeInstruction,
628946
629449
  ``,
628947
- `Required schema: {"route":"chat"|"action","should_reply":true|false,"confidence":0.0-1.0,"reason":"short reason","attention_state":"idle"|"observing"|"engaged"|"cooldown","attention_delta":-1.0..1.0,"next_check_after_messages":1..12,"silent_disposition":"short outcome-level disposition","mental_note":"short outcome-level observation","memory_note":"short memory/summary update","relationship_note":"short relationship/thread note","procedure_note":"short tree/procedure note","voice_note":"short final-voice note","scenario_note":"short scenario/transition note","scenario_id":"dynamic inferred scenario id","scenario_label":"human readable dynamic scenario label","scenario_confidence":0.0-1.0,"scenario_objective":"current scenario objective","scenario_state_loop":"state loop to maintain until transition"}`,
629450
+ `Required schema: {"route":"chat"|"action","should_reply":true|false,"confidence":0.0-1.0,"reason":"short reason","attention_state":"idle"|"observing"|"engaged"|"cooldown","attention_delta":-1.0..1.0,"next_check_after_messages":1..12,"silent_disposition":"short outcome-level disposition","mental_note":"short outcome-level observation","memory_note":"short memory/summary update","relationship_note":"short relationship/thread note","procedure_note":"short tree/procedure note","voice_note":"short final-voice note","scenario_note":"short scenario/transition note","scenario_id":"dynamic inferred scenario id","scenario_label":"human readable dynamic scenario label","scenario_confidence":0.0-1.0,"scenario_objective":"current scenario objective","scenario_state_loop":"state loop to maintain until transition","reply_mode_preference":null|{"scope":"current_user_in_chat"|"current_user_global"|"current_group","reply_mode":"reply_then_notes"|"notes_then_reply"|"reply_only","confidence":0.0-1.0,"note":"why this durable preference was explicitly expressed"}}`,
628948
629451
  ``,
628949
629452
  `Invalid previous output, for diagnostics only:`,
628950
629453
  invalidPreview,
@@ -629244,6 +629747,7 @@ ${retryText}`,
629244
629747
  `Platform salience signals (context only, not triggers): ${identitySalienceSignals.length ? identitySalienceSignals.join(", ") : "none"}`,
629245
629748
  `Current chat type: ${msg.chatType}`,
629246
629749
  `Current sender: ${telegramSpeakerLabel(msg)} [${telegramActorKindLabel(msg)}]`,
629750
+ this.formatTelegramDeliveryCapabilityContext(sessionKey, msg),
629247
629751
  msg.replyToMessageId ? `Current message replies to message_id ${msg.replyToMessageId}` : "",
629248
629752
  msg.replyToUsername ? `Current message replies to @${msg.replyToUsername}${msg.replyToBot ? " [bot]" : " [human/unknown]"}` : "",
629249
629753
  currentReplyContext,
@@ -629308,7 +629812,7 @@ ${stimulationProbe.context}`,
629308
629812
  `Use the persona docs below as binding behavioral guidance for whether speaking is appropriate and how to avoid over-eager or adversarially bad interventions.`,
629309
629813
  `Return JSON only, with no markdown and no explanation outside JSON.`,
629310
629814
  ``,
629311
- `Schema: {"route":"chat"|"action","should_reply":true|false,"confidence":0.0-1.0,"reason":"short reason","attention_state":"idle"|"observing"|"engaged"|"cooldown","attention_delta":-1.0..1.0,"next_check_after_messages":1..12,"silent_disposition":"short outcome-level disposition","mental_note":"short outcome-level observation","memory_note":"short memory/summary update","relationship_note":"short relationship/thread note","procedure_note":"short tree/procedure note","voice_note":"short final-voice note","scenario_note":"short scenario/transition note","scenario_id":"dynamic inferred scenario id","scenario_label":"human readable dynamic scenario label","scenario_confidence":0.0-1.0,"scenario_objective":"current scenario objective","scenario_state_loop":"state loop to maintain until transition"}`,
629815
+ `Schema: {"route":"chat"|"action","should_reply":true|false,"confidence":0.0-1.0,"reason":"short reason","attention_state":"idle"|"observing"|"engaged"|"cooldown","attention_delta":-1.0..1.0,"next_check_after_messages":1..12,"silent_disposition":"short outcome-level disposition","mental_note":"short outcome-level observation","memory_note":"short memory/summary update","relationship_note":"short relationship/thread note","procedure_note":"short tree/procedure note","voice_note":"short final-voice note","scenario_note":"short scenario/transition note","scenario_id":"dynamic inferred scenario id","scenario_label":"human readable dynamic scenario label","scenario_confidence":0.0-1.0,"scenario_objective":"current scenario objective","scenario_state_loop":"state loop to maintain until transition","reply_mode_preference":null|{"scope":"current_user_in_chat"|"current_user_global"|"current_group","reply_mode":"reply_then_notes"|"notes_then_reply"|"reply_only","confidence":0.0-1.0,"note":"why this durable preference was explicitly expressed"}}`,
629312
629816
  ``,
629313
629817
  `Route meanings:`,
629314
629818
  `- chat: a short conversational answer can be produced without tools.`,
@@ -629320,6 +629824,8 @@ ${stimulationProbe.context}`,
629320
629824
  `High-salience evidence: private DMs, exact @username matches, display-name self references, and replies to this bot are notification-like signals. They should usually move attention toward deeper processing and a likely response unless the surrounding context makes silence more natural.`,
629321
629825
  `No keyword routing: do not use static keyword rules. Infer whether identity references are actually aimed at this bot from syntax, tone, recent turns, and relationships.`,
629322
629826
  `Observation notes: always fill silent_disposition, mental_note, memory_note, relationship_note, procedure_note, voice_note, scenario_note, scenario_id, scenario_label, scenario_confidence, scenario_objective, and scenario_state_loop with concise outcome-level notes for the TUI. Do not expose hidden chain-of-thought; summarize what the bot did with the heard message.`,
629827
+ `Reply-mode preference capture: if and only if the current sender explicitly expresses a durable preference for reply cadence/order, populate reply_mode_preference as a typed update. Do not infer this from style, keywords, tone, or one-off task shape. Use null otherwise.`,
629828
+ `Reply-mode meanings: reply_then_notes means visible reply first and internal notes afterward; notes_then_reply means internal notes before visible reply; reply_only means one user-facing reply with tools/context and no extra internal notes stage.`,
629323
629829
  `Bot-to-bot discipline: sender labels include [bot] or [human]. Treat peer bots as autonomous actors, not human users. Avoid bot loops by replying only when the exchange context makes a response socially useful.`,
629324
629830
  `Ingress discipline: this Telegram message has already been retained as chat context. should_reply controls only whether to emit a visible reply.`,
629325
629831
  `Memory discipline: use durable associative user memory, relationships, prior actions, and recent context to infer whether this speaker is continuing a bot-related thread. A mention is not required when the semantic target is clearly the bot or an ongoing bot-mediated discussion.`,
@@ -630127,6 +630633,11 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
630127
630633
  if (replyParameters) body["reply_parameters"] = replyParameters;
630128
630634
  const result = await this.apiCall("sendMessage", body);
630129
630635
  this.state.messagesSent++;
630636
+ this.updateTelegramTextDeliveryCapability(chatId, {
630637
+ status: "allowed",
630638
+ canSendText: true,
630639
+ source: "sendLiveMessage"
630640
+ });
630130
630641
  return result.result?.message_id ?? null;
630131
630642
  } catch {
630132
630643
  try {
@@ -630138,8 +630649,26 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
630138
630649
  if (replyParameters) body["reply_parameters"] = replyParameters;
630139
630650
  const result = await this.apiCall("sendMessage", body);
630140
630651
  this.state.messagesSent++;
630652
+ this.updateTelegramTextDeliveryCapability(chatId, {
630653
+ status: "allowed",
630654
+ canSendText: true,
630655
+ source: "sendLiveMessage.plain_fallback"
630656
+ });
630141
630657
  return result.result?.message_id ?? null;
630142
- } catch {
630658
+ } catch (err) {
630659
+ const errStr = err instanceof Error ? err.message : String(err);
630660
+ this.tuiWrite(() => renderWarning(`Failed to send Telegram live message: ${errStr}`));
630661
+ this.updateTelegramTextDeliveryCapability(chatId, {
630662
+ status: "failed",
630663
+ reason: errStr,
630664
+ source: "sendLiveMessage"
630665
+ });
630666
+ getSoulObservationStream().emit({
630667
+ kind: "telegram.send.failed",
630668
+ sessionKey: String(chatId),
630669
+ reason: errStr,
630670
+ ts: Date.now()
630671
+ });
630143
630672
  return null;
630144
630673
  }
630145
630674
  }
@@ -630374,14 +630903,29 @@ Join: ${newUrl}`);
630374
630903
  } catch (err) {
630375
630904
  decision2 = this.fallbackTelegramRouterDecision(msg, toolContext, err);
630376
630905
  }
630377
- this.deliverTelegramAttentionDecision(
630906
+ const storedPreference = this.applyTelegramReplyPreferenceUpdate(
630378
630907
  sessionKey,
630379
630908
  msg,
630380
- attentionViewId,
630381
- decision2,
630382
- this.telegramMessageIdentitySalienceSignals(msg),
630383
- this.markLatestTelegramDaydreamOpportunitiesConsidered(sessionKey, msg)
630909
+ decision2.replyPreferenceUpdate,
630910
+ "telegram-router"
630384
630911
  );
630912
+ if (storedPreference) this.subAgentViewCallbacks?.onWrite(attentionViewId || this.viewIdForMessage(msg), `preference: ${storedPreference}`);
630913
+ const replyMode = this.resolvedTelegramReplyMode(sessionKey, msg);
630914
+ const deliveredDecision = replyMode === "reply_only" ? this.stripTelegramDecisionNotes(decision2) : decision2;
630915
+ const daydreamOpportunities = this.markLatestTelegramDaydreamOpportunitiesConsidered(sessionKey, msg);
630916
+ const shouldDeferNotes = replyMode === "reply_then_notes" && decision2.shouldReply;
630917
+ if (!shouldDeferNotes) {
630918
+ this.deliverTelegramAttentionDecision(
630919
+ sessionKey,
630920
+ msg,
630921
+ attentionViewId,
630922
+ deliveredDecision,
630923
+ this.telegramMessageIdentitySalienceSignals(msg),
630924
+ daydreamOpportunities
630925
+ );
630926
+ } else if (attentionViewId && this.subAgentViewCallbacks) {
630927
+ this.subAgentViewCallbacks.onWrite(attentionViewId, `reply mode: reply_then_notes; internal notes will be committed after visible reply attempt`);
630928
+ }
630385
630929
  this.markLastTelegramUserMessageMode(msg, decision2.shouldReply ? decision2.route : "ambient");
630386
630930
  this.subAgentViewCallbacks?.onWrite(
630387
630931
  this.viewIdForMessage(msg),
@@ -630391,7 +630935,7 @@ Join: ${newUrl}`);
630391
630935
  this.maybeLogTelegramGroupSkip(msg, `live inference: no reply — ${decision2.reason}`);
630392
630936
  return;
630393
630937
  }
630394
- const decisionContext = this.formatTelegramAttentionDecisionContext(decision2);
630938
+ const decisionContext = this.formatTelegramAttentionDecisionContext(deliveredDecision);
630395
630939
  const existingAfterDecision = this.subAgents.get(sessionKey);
630396
630940
  if (existingAfterDecision && !existingAfterDecision.aborted) {
630397
630941
  this.subAgentViewCallbacks?.onWrite(
@@ -630400,11 +630944,31 @@ Join: ${newUrl}`);
630400
630944
  );
630401
630945
  this.enqueueTelegramSubAgentContext(sessionKey, existingAfterDecision, decisionContext, msg.username);
630402
630946
  await this.enqueueTelegramQueuedSessionWorkForExistingSubAgent(work, existingAfterDecision);
630947
+ if (shouldDeferNotes) {
630948
+ this.deliverTelegramAttentionDecision(
630949
+ sessionKey,
630950
+ msg,
630951
+ attentionViewId,
630952
+ deliveredDecision,
630953
+ this.telegramMessageIdentitySalienceSignals(msg),
630954
+ daydreamOpportunities
630955
+ );
630956
+ }
630403
630957
  return;
630404
630958
  }
630405
630959
  const subAgentProfile = decision2.route === "chat" ? "chat" : "action";
630406
630960
  if (decision2.route === "chat" && msg.chatType === "private") {
630407
630961
  await this.handleTelegramChatCompletion(msg, toolContext, [decisionContext, rapidContext].filter(Boolean).join("\n\n"));
630962
+ if (shouldDeferNotes) {
630963
+ this.deliverTelegramAttentionDecision(
630964
+ sessionKey,
630965
+ msg,
630966
+ attentionViewId,
630967
+ deliveredDecision,
630968
+ this.telegramMessageIdentitySalienceSignals(msg),
630969
+ daydreamOpportunities
630970
+ );
630971
+ }
630408
630972
  return;
630409
630973
  }
630410
630974
  const subAgent = {
@@ -630489,6 +631053,16 @@ Join: ${newUrl}`);
630489
631053
  await this.deleteLiveMessage(msg.chatId, subAgent.liveMessageId).catch(() => {
630490
631054
  });
630491
631055
  }
631056
+ if (shouldDeferNotes) {
631057
+ this.deliverTelegramAttentionDecision(
631058
+ sessionKey,
631059
+ msg,
631060
+ attentionViewId,
631061
+ deliveredDecision,
631062
+ this.telegramMessageIdentitySalienceSignals(msg),
631063
+ daydreamOpportunities
631064
+ );
631065
+ }
630492
631066
  return;
630493
631067
  }
630494
631068
  if (subAgent.liveMessagePromise) {
@@ -630497,16 +631071,30 @@ Join: ${newUrl}`);
630497
631071
  }
630498
631072
  const finalHtml = convertMarkdownToTelegramHTML(finalText);
630499
631073
  const sentMessageId = await this.sendOrEditFinalTelegramHTML(msg, finalHtml, subAgent.liveMessageId);
630500
- this.recordTelegramAssistantMessage(msg, finalText, subAgentProfile, {
630501
- messageId: sentMessageId,
630502
- replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
630503
- });
631074
+ if (sentMessageId !== null || msg.guestQueryId) {
631075
+ this.recordTelegramAssistantMessage(msg, finalText, subAgentProfile, {
631076
+ messageId: sentMessageId,
631077
+ replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
631078
+ });
631079
+ } else {
631080
+ this.subAgentViewCallbacks?.onWrite(subAgent.viewId, "Telegram text send returned no delivered message id");
631081
+ }
630504
631082
  await this.sendGeneratedArtifactsFromSubAgent(
630505
631083
  msg,
630506
631084
  subAgent,
630507
631085
  finalText,
630508
631086
  Boolean(subAgent.liveMessageId && !msg.guestQueryId)
630509
631087
  );
631088
+ if (shouldDeferNotes) {
631089
+ this.deliverTelegramAttentionDecision(
631090
+ sessionKey,
631091
+ msg,
631092
+ attentionViewId,
631093
+ deliveredDecision,
631094
+ this.telegramMessageIdentitySalienceSignals(msg),
631095
+ daydreamOpportunities
631096
+ );
631097
+ }
630510
631098
  this.tuiWrite(() => renderTelegramSubAgentComplete(msg.username, finalText));
630511
631099
  this.subAgentViewCallbacks?.onWrite(subAgent.viewId, `completed: ${finalText}`);
630512
631100
  this.subAgentViewCallbacks?.onStatus(subAgent.viewId, "completed");
@@ -630527,6 +631115,16 @@ Join: ${newUrl}`);
630527
631115
  await this.deleteLiveMessage(msg.chatId, subAgent.liveMessageId).catch(() => {
630528
631116
  });
630529
631117
  }
631118
+ if (shouldDeferNotes) {
631119
+ this.deliverTelegramAttentionDecision(
631120
+ sessionKey,
631121
+ msg,
631122
+ attentionViewId,
631123
+ deliveredDecision,
631124
+ this.telegramMessageIdentitySalienceSignals(msg),
631125
+ daydreamOpportunities
631126
+ );
631127
+ }
630530
631128
  } finally {
630531
631129
  this.clearTelegramSubAgentContextBuffer(sessionKey);
630532
631130
  this.subAgents.delete(sessionKey);
@@ -630610,10 +631208,14 @@ Join: ${newUrl}`);
630610
631208
  }
630611
631209
  const finalHtml = convertMarkdownToTelegramHTML(finalText);
630612
631210
  const sentMessageId = await this.sendOrEditFinalTelegramHTML(msg, finalHtml, subAgent.liveMessageId);
630613
- this.recordTelegramAssistantMessage(msg, finalText, "chat", {
630614
- messageId: sentMessageId,
630615
- replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
630616
- });
631211
+ if (sentMessageId !== null || msg.guestQueryId) {
631212
+ this.recordTelegramAssistantMessage(msg, finalText, "chat", {
631213
+ messageId: sentMessageId,
631214
+ replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
631215
+ });
631216
+ } else {
631217
+ this.subAgentViewCallbacks?.onWrite(subAgent.viewId, "Telegram text send returned no delivered message id");
631218
+ }
630617
631219
  await this.sendGeneratedArtifactsFromSubAgent(
630618
631220
  msg,
630619
631221
  subAgent,
@@ -630739,10 +631341,14 @@ Join: ${newUrl}`);
630739
631341
  }
630740
631342
  const finalHtml = convertMarkdownToTelegramHTML(cleaned);
630741
631343
  const sentMessageId = await this.sendOrEditFinalTelegramHTML(msg, finalHtml, liveMessageId);
630742
- this.recordTelegramAssistantMessage(msg, cleaned, "chat", {
630743
- messageId: sentMessageId,
630744
- replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
630745
- });
631344
+ if (sentMessageId !== null || msg.guestQueryId) {
631345
+ this.recordTelegramAssistantMessage(msg, cleaned, "chat", {
631346
+ messageId: sentMessageId,
631347
+ replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
631348
+ });
631349
+ } else {
631350
+ this.subAgentViewCallbacks?.onWrite(viewId, "Telegram text send returned no delivered message id");
631351
+ }
630746
631352
  this.subAgentViewCallbacks?.onWrite(viewId, `completed: ${cleaned}`);
630747
631353
  this.subAgentViewCallbacks?.onStatus(viewId, "completed");
630748
631354
  } catch (err) {
@@ -631138,6 +631744,7 @@ ${currentTelegramPrompt}`;
631138
631744
  "You have access to isolated per-chat memory (memory_write, memory_read, memory_search) scoped to this conversation.",
631139
631745
  "memory_search may use scope=group/current_chat for this group or scope=user with user_id/username for a participant in this same group. Other groups, admin chats, and private DMs are not accessible here.",
631140
631746
  "You can remember facts about users and retrieve them later. Durable associative memory in the prompt includes participant profiles, relationships, scoped facts, and prior actions retained across days, sessions, and Omnius updates. You also have web_search and web_fetch to look up information.",
631747
+ "If a user explicitly states a durable preference for reply cadence/order, call telegram_preference_set. Do not infer or classify reply-mode preferences from keywords, style, tone, or task type.",
631141
631748
  "You have the full scoped Telegram media-analysis stack by default: telegram_media_recent, image_read, ocr, ocr_image_advanced, vision, pdf_to_text, ocr_pdf, transcribe_file, video_understand, audio_analyze, and identity_memory. For complex textual imagery, screenshots, forms, scans, or dense labels, prefer ocr_image_advanced after resolving media with path='reply' or path='latest'.",
631142
631749
  formatIdentityMemoryContext(chatLabel || "Telegram private chat"),
631143
631750
  reminderToolContract,
@@ -631890,6 +632497,7 @@ Scoped workspace: ${scopedRoot}`,
631890
632497
  new SkillListTool(repoRoot),
631891
632498
  sharedSkillExtractTool,
631892
632499
  this.buildTelegramMediaRecentTool(chatId, msg),
632500
+ this.buildTelegramReplyPreferenceTool(context2, chatId, msg),
631893
632501
  this.buildTelegramTool(context2, repoRoot, chatId, msg),
631894
632502
  ...this.buildTelegramReminderTools(context2, repoRoot, chatId, msg)
631895
632503
  ];
@@ -631955,6 +632563,7 @@ Scoped workspace: ${scopedRoot}`,
631955
632563
  new SkillListTool(repoRoot),
631956
632564
  new SkillExecuteTool(repoRoot),
631957
632565
  adminSkillExtractTool,
632566
+ this.buildTelegramReplyPreferenceTool(context2, chatId, msg),
631958
632567
  this.buildTelegramTool(context2, repoRoot, chatId, msg),
631959
632568
  ...this.buildTelegramReminderTools(context2, repoRoot, chatId, msg),
631960
632569
  fullSubAgentTool,
@@ -632212,8 +632821,9 @@ Scoped workspace: ${scopedRoot}`,
632212
632821
  const text = String(args["text"] || "").trim();
632213
632822
  if (!text) throw new Error("text is required for send_message.");
632214
632823
  if (dryRun) return this.telegramDryRun(action, targetChatId, { text });
632215
- const sent = await this.sendMessage(targetChatId, text);
632216
- return { action, telegram_method: "sendMessage", ok: true, chat_id: targetChatId, message_id: sent };
632824
+ const sent = await this.sendMessageDetailed(targetChatId, text);
632825
+ if (!sent.ok) throw new Error(sent.reason || `Telegram send_message ${sent.status}`);
632826
+ return { action, telegram_method: "sendMessage", ok: true, chat_id: targetChatId, message_id: sent.messageId ?? null };
632217
632827
  }
632218
632828
  case "edit_message_text": {
632219
632829
  if (targetChatId === void 0 || messageId === void 0) throw new Error("target/chat_id and message_id are required for edit_message_text.");
@@ -633127,6 +633737,64 @@ Scoped workspace: ${scopedRoot}`,
633127
633737
  resolveMedia: (ref, args) => this.resolveTelegramIdentityMedia(chatId, currentMsg, ref, args)
633128
633738
  });
633129
633739
  }
633740
+ buildTelegramReplyPreferenceTool(_context, _chatId, currentMsg) {
633741
+ const bridge = this;
633742
+ return {
633743
+ name: "telegram_preference_set",
633744
+ description: [
633745
+ "Store an explicitly stated Telegram reply-mode preference for the current user or group.",
633746
+ "Use only when the user directly expresses a durable preference; do not infer from style, tone, keywords, or task type.",
633747
+ "This stores internal behavior preferences only and does not send a Telegram message."
633748
+ ].join(" "),
633749
+ parameters: {
633750
+ type: "object",
633751
+ properties: {
633752
+ scope: {
633753
+ type: "string",
633754
+ enum: ["current_user_in_chat", "current_user_global", "current_group"],
633755
+ description: "Where to store the preference. Default current_user_in_chat for public/group contexts."
633756
+ },
633757
+ reply_mode: {
633758
+ type: "string",
633759
+ enum: ["reply_then_notes", "notes_then_reply", "reply_only"],
633760
+ description: "reply_then_notes = visible reply first, then internal notes; notes_then_reply = internal notes first; reply_only = one reply with tools/context and no extra internal notes stage."
633761
+ },
633762
+ confidence: { type: "number", description: "Confidence that the user explicitly expressed this durable preference, 0.0-1.0." },
633763
+ note: { type: "string", description: "Short evidence/rationale from the user's message." }
633764
+ },
633765
+ required: ["reply_mode"]
633766
+ },
633767
+ async execute(args) {
633768
+ const start2 = performance.now();
633769
+ if (!currentMsg) {
633770
+ return { success: false, output: "", error: "No current Telegram message is available for preference scope.", durationMs: performance.now() - start2 };
633771
+ }
633772
+ const replyMode = parseTelegramReplyMode(args["reply_mode"] ?? args["replyMode"] ?? args["mode"]);
633773
+ if (!replyMode) {
633774
+ return { success: false, output: "", error: "reply_mode must be reply_then_notes, notes_then_reply, or reply_only.", durationMs: performance.now() - start2 };
633775
+ }
633776
+ const scope = parseTelegramReplyPreferenceToolScope(args["scope"]) ?? "current_user_in_chat";
633777
+ const confidenceRaw = Number(args["confidence"]);
633778
+ const update2 = {
633779
+ scope,
633780
+ replyMode,
633781
+ confidence: Number.isFinite(confidenceRaw) ? Math.max(0, Math.min(1, confidenceRaw)) : void 0,
633782
+ note: typeof args["note"] === "string" ? args["note"].trim().slice(0, 220) : void 0,
633783
+ source: "telegram_preference_set"
633784
+ };
633785
+ const sessionKey = bridge.sessionKeyForMessage(currentMsg);
633786
+ const stored = bridge.applyTelegramReplyPreferenceUpdate(sessionKey, currentMsg, update2, "telegram_preference_set");
633787
+ return {
633788
+ success: true,
633789
+ output: stored || `stored reply_mode=${replyMode}`,
633790
+ llmContent: stored || `Stored Telegram reply preference ${replyMode}.`,
633791
+ durationMs: performance.now() - start2,
633792
+ mutated: true,
633793
+ mutatedFiles: []
633794
+ };
633795
+ }
633796
+ };
633797
+ }
633130
633798
  buildTelegramMediaRecentTool(chatId, currentMsg) {
633131
633799
  const bridge = this;
633132
633800
  return {
@@ -633637,17 +634305,25 @@ ${text}`.trim());
633637
634305
  // ── Message sending ───────────────────────────────────────────────────
633638
634306
  /** Send a response back to a Telegram chat (Markdown → HTML conversion) */
633639
634307
  async sendMessage(chatId, text, replyToMessageId, options2 = {}) {
634308
+ const result = await this.sendMessageDetailed(chatId, text, replyToMessageId, options2);
634309
+ return result.messageId ?? null;
634310
+ }
634311
+ async sendMessageDetailed(chatId, text, replyToMessageId, options2 = {}) {
633640
634312
  const extracted = extractMediaReferences(text);
633641
634313
  const mediaRefs = this.filterTelegramMediaReferences(extracted.media, options2);
633642
634314
  const html = convertMarkdownToTelegramHTML(extracted.text);
633643
- const msgId = extracted.text.trim() ? await this.sendMessageHTML(chatId, html, replyToMessageId, options2) : null;
634315
+ const textResult = extracted.text.trim() ? await this.sendMessageHTMLDetailed(chatId, html, replyToMessageId, options2) : { ok: true, status: "sent", chatId, messageId: null };
633644
634316
  for (const media of mediaRefs) {
633645
634317
  await this.sendMediaReference(chatId, media, { replyToMessageId }).catch(() => null);
633646
634318
  }
633647
- return msgId;
634319
+ return textResult;
633648
634320
  }
633649
634321
  /** Send an HTML-formatted message to a Telegram chat */
633650
634322
  async sendMessageHTML(chatId, html, replyToMessageId, options2 = {}) {
634323
+ const result = await this.sendMessageHTMLDetailed(chatId, html, replyToMessageId, options2);
634324
+ return result.messageId ?? null;
634325
+ }
634326
+ async sendMessageHTMLDetailed(chatId, html, replyToMessageId, options2 = {}) {
633651
634327
  const extracted = extractMediaReferences(html);
633652
634328
  const mediaRefs = this.filterTelegramMediaReferences(extracted.media, options2);
633653
634329
  const sendHtml = extracted.text || (extracted.media.length > 0 ? "" : html);
@@ -633662,7 +634338,7 @@ ${text}`.trim());
633662
634338
  ).catch(() => null);
633663
634339
  if (sentId === null) sentId = mediaId;
633664
634340
  }
633665
- return sentId;
634341
+ return { ok: sentId !== null || mediaRefs.length === 0, status: "sent", chatId, messageId: sentId };
633666
634342
  }
633667
634343
  const chunks = splitTelegramMessageText(sendHtml, 3900);
633668
634344
  for (let idx = 0; idx < chunks.length; idx++) {
@@ -633680,6 +634356,11 @@ ${text}`.trim());
633680
634356
  if (result.ok === false) throw new Error(String(result.description || "Telegram sendMessage failed"));
633681
634357
  this.state.messagesSent++;
633682
634358
  if (sentId === null) sentId = result.result?.message_id ?? null;
634359
+ this.updateTelegramTextDeliveryCapability(chatId, {
634360
+ status: "allowed",
634361
+ canSendText: true,
634362
+ source: "sendMessage"
634363
+ });
633683
634364
  getSoulObservationStream().emit({
633684
634365
  kind: "telegram.send.success",
633685
634366
  sessionKey: sessionKeyForObs,
@@ -633695,6 +634376,11 @@ ${text}`.trim());
633695
634376
  if (result.ok === false) throw new Error(String(result.description || "Telegram sendMessage failed"));
633696
634377
  this.state.messagesSent++;
633697
634378
  if (sentId === null) sentId = result.result?.message_id ?? null;
634379
+ this.updateTelegramTextDeliveryCapability(chatId, {
634380
+ status: "allowed",
634381
+ canSendText: true,
634382
+ source: "sendMessage.plain_fallback"
634383
+ });
633698
634384
  getSoulObservationStream().emit({
633699
634385
  kind: "telegram.send.success",
633700
634386
  sessionKey: sessionKeyForObs,
@@ -633704,30 +634390,25 @@ ${text}`.trim());
633704
634390
  } catch (err) {
633705
634391
  this.tuiWrite(() => renderWarning(`Failed to send Telegram message: ${err instanceof Error ? err.message : String(err)}`));
633706
634392
  const errStr = err instanceof Error ? err.message : String(err);
633707
- const lc = errStr.toLowerCase();
633708
- if (/(not enough rights|forbidden|chat_write_forbidden|user_banned|kicked|chat_admin_required)/.test(lc)) {
633709
- getSoulObservationStream().emit({
633710
- kind: "telegram.send.forbidden",
633711
- sessionKey: sessionKeyForObs,
633712
- reason: errStr,
633713
- ts: Date.now()
633714
- });
633715
- } else if (/too many requests|retry after/.test(lc)) {
633716
- const m2 = lc.match(/retry after (\d+)/);
633717
- getSoulObservationStream().emit({
633718
- kind: "telegram.send.rate_limited",
633719
- sessionKey: sessionKeyForObs,
633720
- retryAfterSec: m2 ? parseInt(m2[1], 10) : void 0,
633721
- ts: Date.now()
633722
- });
633723
- }
634393
+ this.updateTelegramTextDeliveryCapability(chatId, {
634394
+ status: "failed",
634395
+ reason: errStr,
634396
+ source: "sendMessage"
634397
+ });
634398
+ getSoulObservationStream().emit({
634399
+ kind: "telegram.send.failed",
634400
+ sessionKey: sessionKeyForObs,
634401
+ reason: errStr,
634402
+ ts: Date.now()
634403
+ });
634404
+ return { ok: false, status: "failed", chatId, messageId: sentId, reason: errStr };
633724
634405
  }
633725
634406
  }
633726
634407
  }
633727
634408
  for (const media of mediaRefs) {
633728
634409
  await this.sendMediaReference(chatId, media).catch(() => null);
633729
634410
  }
633730
- return sentId;
634411
+ return { ok: sentId !== null, status: "sent", chatId, messageId: sentId };
633731
634412
  }
633732
634413
  filterTelegramMediaReferences(media, options2) {
633733
634414
  const suppress = options2.suppressMedia;
@@ -652024,7 +652705,7 @@ async function refreshEndpointRegistry() {
652024
652705
  });
652025
652706
  if (process.env["OMNIUS_SKIP_SPONSOR_DISCOVERY"] === "1") return;
652026
652707
  try {
652027
- const resp = await fetch("https://omnius.nexus/api/v1/sponsors", {
652708
+ const resp = await fetch(NEXUS_SPONSORS_URL2, {
652028
652709
  signal: AbortSignal.timeout(5e3)
652029
652710
  });
652030
652711
  if (resp.ok) {
@@ -659591,7 +660272,7 @@ function setTimerEnabled(name10, enabled2) {
659591
660272
  return false;
659592
660273
  }
659593
660274
  }
659594
- var require4, endpointRegistry, modelRouteMap, endpointUsage, _lastEndpointDiagnostics, BACKEND_TIMEOUT_DEFAULT_MS, BACKEND_TIMEOUT_MAX_MS, MODEL_LIST_TIMEOUT_DEFAULT_MS, metrics, startedAt, runningProcesses, perKeyUsage, CRON_MARKER2;
660275
+ var require4, NEXUS_DIRECTORY_ORIGIN2, NEXUS_SPONSORS_URL2, endpointRegistry, modelRouteMap, endpointUsage, _lastEndpointDiagnostics, BACKEND_TIMEOUT_DEFAULT_MS, BACKEND_TIMEOUT_MAX_MS, MODEL_LIST_TIMEOUT_DEFAULT_MS, metrics, startedAt, runningProcesses, perKeyUsage, CRON_MARKER2;
659595
660276
  var init_serve = __esm({
659596
660277
  "packages/cli/src/api/serve.ts"() {
659597
660278
  "use strict";
@@ -659622,6 +660303,8 @@ var init_serve = __esm({
659622
660303
  init_docker();
659623
660304
  init_typed_node_events();
659624
660305
  require4 = createRequire7(import.meta.url);
660306
+ NEXUS_DIRECTORY_ORIGIN2 = (process.env["OMNIUS_NEXUS_DIRECTORY_ORIGIN"] || process.env["OMNIUS_NEXUS_SIGNALING_SERVER"] || "https://openagents.nexus").replace(/\/+$/, "");
660307
+ NEXUS_SPONSORS_URL2 = `${NEXUS_DIRECTORY_ORIGIN2}/api/v1/sponsors`;
659625
660308
  endpointRegistry = [];
659626
660309
  modelRouteMap = /* @__PURE__ */ new Map();
659627
660310
  endpointUsage = /* @__PURE__ */ new Map();
@@ -664739,7 +665422,7 @@ Log: ${nexusLogPath}`)
664739
665422
  const { getNodeMnemonic: getNodeMnemonic2 } = await Promise.resolve().then(() => (init_banner(), banner_exports));
664740
665423
  agName = getNodeMnemonic2();
664741
665424
  }
664742
- fetch("https://omnius.nexus/api/v1/directory", {
665425
+ fetch(NEXUS_AGENT_DIRECTORY_URL, {
664743
665426
  method: "POST",
664744
665427
  headers: { "Content-Type": "application/json" },
664745
665428
  body: JSON.stringify({
@@ -664783,7 +665466,7 @@ Log: ${nexusLogPath}`)
664783
665466
  } catch {
664784
665467
  }
664785
665468
  const spResp = await fetch(
664786
- "https://omnius.nexus/api/v1/sponsors",
665469
+ NEXUS_SPONSORS_URL3,
664787
665470
  { signal: AbortSignal.timeout(8e3) }
664788
665471
  );
664789
665472
  if (spResp.ok) {
@@ -668647,7 +669330,7 @@ Rules:
668647
669330
  process.exit(1);
668648
669331
  }
668649
669332
  }
668650
- var _interactiveSessionActive, _interactiveSessionReason, _voiceChatSession2, taskManager, _apiCallbacks, _shellToolRef, _replToolRef, _fullSubAgentToolRef, _agentToolRef, _sendMessageToolRef, _agentLifecycleMgr, _activeRunnerRef, _parentRunnerForArchive, _wireSubAgentCallbacks, _wireAgentToolCallbacks, _wireSubAgentToolCallbacks, _autoUpdatedThisSession, _mcpManager, _pluginManager, _mcpTools, SELF_IMPROVE_INTERVAL, _tasksSinceImprove;
669333
+ var NEXUS_DIRECTORY_ORIGIN3, NEXUS_AGENT_DIRECTORY_URL, NEXUS_SPONSORS_URL3, _interactiveSessionActive, _interactiveSessionReason, _voiceChatSession2, taskManager, _apiCallbacks, _shellToolRef, _replToolRef, _fullSubAgentToolRef, _agentToolRef, _sendMessageToolRef, _agentLifecycleMgr, _activeRunnerRef, _parentRunnerForArchive, _wireSubAgentCallbacks, _wireAgentToolCallbacks, _wireSubAgentToolCallbacks, _autoUpdatedThisSession, _mcpManager, _pluginManager, _mcpTools, SELF_IMPROVE_INTERVAL, _tasksSinceImprove;
668651
669334
  var init_interactive = __esm({
668652
669335
  "packages/cli/src/tui/interactive.ts"() {
668653
669336
  "use strict";
@@ -668702,6 +669385,9 @@ var init_interactive = __esm({
668702
669385
  init_neovim_mode();
668703
669386
  init_task_manager_singleton();
668704
669387
  init_tui_tasks_renderer();
669388
+ NEXUS_DIRECTORY_ORIGIN3 = (process.env["OMNIUS_NEXUS_DIRECTORY_ORIGIN"] || process.env["OMNIUS_NEXUS_SIGNALING_SERVER"] || "https://openagents.nexus").replace(/\/+$/, "");
669389
+ NEXUS_AGENT_DIRECTORY_URL = `${NEXUS_DIRECTORY_ORIGIN3}/api/v1/directory`;
669390
+ NEXUS_SPONSORS_URL3 = `${NEXUS_DIRECTORY_ORIGIN3}/api/v1/sponsors`;
668705
669391
  _interactiveSessionActive = false;
668706
669392
  _interactiveSessionReason = "";
668707
669393
  _voiceChatSession2 = null;