@ouro.bot/cli 0.1.0-alpha.824 → 0.1.0-alpha.825

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/changelog.json CHANGED
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.825",
6
+ "changes": [
7
+ "Sanctuary: a single nested emphasis no longer makes a whole Butler reply render as literal asterisks."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.824",
6
12
  "changes": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.824",
2
+ "runtimeVersion": "0.1.0-alpha.825",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-09-03T00:00:00.000Z"
5
5
  }
@@ -1,7 +1,7 @@
1
1
  <?xml version="1.0"?>
2
2
  <Container version="2">
3
3
  <Name>ouro-butler</Name>
4
- <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.824</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.825</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -76,25 +76,54 @@ function preparedTexts(effect) {
76
76
  return [requireText(effect.text, "Telegram text")];
77
77
  return [effect.text?.trim() || null];
78
78
  }
79
+ // Bold spans may carry one level of emphasis inside them, because `**bold with
80
+ // *italic* inside**` is ordinary model prose. The inner span is rendered by the
81
+ // same conservative scan, so a bold run that does not resolve cleanly still
82
+ // degrades the whole message to literal text rather than emitting partial
83
+ // markup. Everything else stays as strict as before: a span must open on a
84
+ // letter or digit and close on a non-space, which is what keeps `**/src/**`,
85
+ // `2 ** 3` and `a_b_c` literal.
86
+ const TELEGRAM_BOLD_WITH_NESTING = /`([^`\n]+)`|\*\*([\p{L}\p{N}](?:[^*_`\n]|\*(?!\*)|_)*\S|[\p{L}\p{N}])\*\*|\*([\p{L}\p{N}](?:[^*_`\n]*\S)?)\*|_([\p{L}\p{N}](?:[^*_`\n]*\S)?)_/gu;
87
+ const TELEGRAM_EMPHASIS_ONLY = /`([^`\n]+)`|\*([\p{L}\p{N}](?:[^*_`\n]*\S)?)\*|_([\p{L}\p{N}](?:[^*_`\n]*\S)?)_/gu;
79
88
  function renderTelegramButlerHtml(text) {
89
+ return renderTelegramStyledSpan(text, true) ?? (0, telegram_client_1.escapeTelegramHtml)(text);
90
+ }
91
+ // Returns null when the span cannot be rendered cleanly, so every caller falls
92
+ // back to escaping rather than emitting half-applied markup.
93
+ function renderTelegramStyledSpan(text, allowBold) {
80
94
  let html = "";
81
95
  let cursor = 0;
82
- const styledText = /`([^`\n]+)`|\*\*([\p{L}\p{N}](?:[^*_`\n]*\S)?)\*\*|\*([\p{L}\p{N}](?:[^*_`\n]*\S)?)\*|_([\p{L}\p{N}](?:[^*_`\n]*\S)?)_/gu;
83
- for (const match of text.matchAll(styledText)) {
96
+ const pattern = allowBold ? TELEGRAM_BOLD_WITH_NESTING : TELEGRAM_EMPHASIS_ONLY;
97
+ pattern.lastIndex = 0;
98
+ for (const match of text.matchAll(pattern)) {
84
99
  const gap = text.slice(cursor, match.index);
85
100
  if (/[*_`]/u.test(gap))
86
- return (0, telegram_client_1.escapeTelegramHtml)(text);
101
+ return null;
87
102
  html += (0, telegram_client_1.escapeTelegramHtml)(gap);
88
- if (match[1] !== undefined)
89
- html += `<code>${(0, telegram_client_1.escapeTelegramHtml)(match[1])}</code>`;
90
- else if (match[2] !== undefined || match[3] !== undefined)
91
- html += `<b>${(0, telegram_client_1.escapeTelegramHtml)((match[2] ?? match[3]))}</b>`;
103
+ const code = match[1];
104
+ const bold = allowBold ? match[2] : undefined;
105
+ const italic = allowBold ? match[3] : match[2];
106
+ const underscored = allowBold ? match[4] : match[3];
107
+ if (code !== undefined)
108
+ html += `<code>${(0, telegram_client_1.escapeTelegramHtml)(code)}</code>`;
109
+ else if (bold !== undefined) {
110
+ const inner = renderTelegramStyledSpan(bold, false);
111
+ if (inner === null)
112
+ return null;
113
+ html += `<b>${inner}</b>`;
114
+ // A single-marker span is bold at the top level, which is the long-standing
115
+ // convention here, but inside a bold run it is the nested emphasis the
116
+ // author meant - and nesting <b> in <b> would be markup Telegram has no
117
+ // reason to accept.
118
+ }
119
+ else if (italic !== undefined)
120
+ html += allowBold ? `<b>${(0, telegram_client_1.escapeTelegramHtml)(italic)}</b>` : `<i>${(0, telegram_client_1.escapeTelegramHtml)(italic)}</i>`;
92
121
  else
93
- html += `<i>${(0, telegram_client_1.escapeTelegramHtml)(match[4])}</i>`;
122
+ html += `<i>${(0, telegram_client_1.escapeTelegramHtml)(underscored)}</i>`;
94
123
  cursor = match.index + match[0].length;
95
124
  }
96
125
  const tail = text.slice(cursor);
97
- return /[*_`]/u.test(tail) ? (0, telegram_client_1.escapeTelegramHtml)(text) : html + (0, telegram_client_1.escapeTelegramHtml)(tail);
126
+ return /[*_`]/u.test(tail) ? null : html + (0, telegram_client_1.escapeTelegramHtml)(tail);
98
127
  }
99
128
  function assertEffectTarget(target, effect, idempotencyKey) {
100
129
  if (target.kind === "admission_gate") {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.824",
3
+ "version": "0.1.0-alpha.825",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@ouro.bot/cli",
9
- "version": "0.1.0-alpha.824",
9
+ "version": "0.1.0-alpha.825",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.78.0",
12
12
  "@azure/identity": "^4.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.824",
3
+ "version": "0.1.0-alpha.825",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },