mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-03 08:06:01 +08:00
revert: undo 2 main-only commits to unblock staging-promote merge (#2297)
Reverts: -6f7575deFix Telegram UTF-16 message splitting (#1961) -7be3b910[codex] Label migration PRs with DB MIGRATION (#1967) Keepsf0db0a3d(registry version bumps) intact. These changes were made directly on main and conflict with staging-promote. Both already exist in staging and will flow back to main via the promote merge. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
.github/labeler.yml
vendored
10
.github/labeler.yml
vendored
@@ -1,5 +1,5 @@
|
||||
# Labels for actions/labeler@v6
|
||||
# Maps file path globs to labels. Multiple labels can apply per PR.
|
||||
# Scope labels for actions/labeler@v6
|
||||
# Maps file path globs to scope labels. Multiple labels can apply per PR.
|
||||
|
||||
"scope: agent":
|
||||
- changed-files:
|
||||
@@ -164,9 +164,3 @@
|
||||
- any-glob-to-any-file:
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
|
||||
"DB MIGRATION":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- migrations/**
|
||||
- src/db/libsql_migrations.rs
|
||||
|
||||
3
.github/scripts/create-labels.sh
vendored
3
.github/scripts/create-labels.sh
vendored
@@ -62,9 +62,6 @@ create "scope: ci" "546E7A" "CI/CD workflows"
|
||||
create "scope: docs" "78909C" "Documentation"
|
||||
create "scope: dependencies" "90A4AE" "Dependency updates"
|
||||
|
||||
echo "==> Creating coordination labels..."
|
||||
create "DB MIGRATION" "C62828" "PR adds or modifies PostgreSQL or libSQL migration definitions"
|
||||
|
||||
echo "==> Creating workflow labels..."
|
||||
create "skip-regression-check" "9E9E9E" "Acknowledged: fix without regression test"
|
||||
|
||||
|
||||
9
.github/workflows/pr-label-scope.yml
vendored
9
.github/workflows/pr-label-scope.yml
vendored
@@ -6,20 +6,13 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
scope:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Ensure DB MIGRATION label exists
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: gh label create "DB MIGRATION" --repo "$REPO" --color C62828 --description "PR adds or modifies PostgreSQL or libSQL migration definitions" --force
|
||||
|
||||
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
|
||||
- uses: actions/labeler@v5
|
||||
with:
|
||||
configuration-path: .github/labeler.yml
|
||||
sync-labels: false # additive only — never remove scope labels
|
||||
|
||||
@@ -363,26 +363,6 @@ const TELEGRAM_STATUS_MAX_CHARS: usize = 600;
|
||||
/// Telegram's hard limit for message text length.
|
||||
const TELEGRAM_MAX_MESSAGE_LEN: usize = 4096;
|
||||
|
||||
fn utf16_code_unit_len(text: &str) -> usize {
|
||||
text.encode_utf16().count()
|
||||
}
|
||||
|
||||
fn prefix_within_utf16_limit(text: &str, max_units: usize) -> usize {
|
||||
let mut units = 0;
|
||||
let mut end = 0;
|
||||
|
||||
for (byte_idx, ch) in text.char_indices() {
|
||||
let ch_units = ch.len_utf16();
|
||||
if units + ch_units > max_units {
|
||||
break;
|
||||
}
|
||||
units += ch_units;
|
||||
end = byte_idx + ch.len_utf8();
|
||||
}
|
||||
|
||||
end
|
||||
}
|
||||
|
||||
fn truncate_status_message(input: &str, max_chars: usize) -> String {
|
||||
let mut iter = input.chars();
|
||||
let truncated: String = iter.by_ref().take(max_chars).collect();
|
||||
@@ -393,7 +373,7 @@ fn truncate_status_message(input: &str, max_chars: usize) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Split a long message into chunks that fit within Telegram's 4096 UTF-16-unit limit.
|
||||
/// Split a long message into chunks that fit within Telegram's 4096-char limit.
|
||||
///
|
||||
/// Tries to split at the most natural boundary available (in priority order):
|
||||
/// 1. Double newline (paragraph break)
|
||||
@@ -402,7 +382,7 @@ fn truncate_status_message(input: &str, max_chars: usize) -> String {
|
||||
/// 4. Word boundary (space)
|
||||
/// 5. Hard cut at the limit (last resort for pathological input)
|
||||
fn split_message(text: &str) -> Vec<String> {
|
||||
if utf16_code_unit_len(text) <= TELEGRAM_MAX_MESSAGE_LEN {
|
||||
if text.chars().count() <= TELEGRAM_MAX_MESSAGE_LEN {
|
||||
return vec![text.to_string()];
|
||||
}
|
||||
|
||||
@@ -410,8 +390,13 @@ fn split_message(text: &str) -> Vec<String> {
|
||||
let mut remaining = text;
|
||||
|
||||
while !remaining.is_empty() {
|
||||
// Find the longest UTF-8 prefix that fits within Telegram's UTF-16 limit.
|
||||
let window_bytes = prefix_within_utf16_limit(remaining, TELEGRAM_MAX_MESSAGE_LEN);
|
||||
// Count chars to find the byte offset for our window.
|
||||
let window_bytes = remaining
|
||||
.char_indices()
|
||||
.take(TELEGRAM_MAX_MESSAGE_LEN)
|
||||
.last()
|
||||
.map(|(byte_idx, ch)| byte_idx + ch.len_utf8())
|
||||
.unwrap_or(remaining.len());
|
||||
|
||||
if window_bytes >= remaining.len() {
|
||||
// Remainder fits entirely.
|
||||
@@ -419,24 +404,10 @@ fn split_message(text: &str) -> Vec<String> {
|
||||
break;
|
||||
}
|
||||
|
||||
if window_bytes == 0 {
|
||||
// Defensive fallback: make progress even if a future caller uses a
|
||||
// smaller limit than a single scalar value can fit within.
|
||||
let first_char_len = remaining
|
||||
.chars()
|
||||
.next()
|
||||
.map(|ch| ch.len_utf8())
|
||||
.unwrap_or(remaining.len());
|
||||
chunks.push(remaining[..first_char_len].to_string());
|
||||
remaining = &remaining[first_char_len..];
|
||||
continue;
|
||||
}
|
||||
|
||||
let window = &remaining[..window_bytes];
|
||||
|
||||
// 1. Double newline — best paragraph boundary
|
||||
let split_at = window
|
||||
.rfind("\n\n")
|
||||
let split_at = window.rfind("\n\n")
|
||||
// 2. Single newline
|
||||
.or_else(|| window.rfind('\n'))
|
||||
// 3. Sentence-ending punctuation followed by space.
|
||||
@@ -446,9 +417,9 @@ fn split_message(text: &str) -> Vec<String> {
|
||||
.or_else(|| {
|
||||
let bytes = window.as_bytes();
|
||||
// Search backwards for '. ', '! ', '? '
|
||||
(1..bytes.len())
|
||||
.rev()
|
||||
.find(|&i| matches!(bytes[i - 1], b'.' | b'!' | b'?') && bytes[i] == b' ')
|
||||
(1..bytes.len()).rev().find(|&i| {
|
||||
matches!(bytes[i - 1], b'.' | b'!' | b'?') && bytes[i] == b' '
|
||||
})
|
||||
})
|
||||
// 4. Word boundary (last space)
|
||||
.or_else(|| window.rfind(' '))
|
||||
@@ -456,11 +427,7 @@ fn split_message(text: &str) -> Vec<String> {
|
||||
.unwrap_or(window_bytes);
|
||||
|
||||
// Avoid empty chunks (e.g. text starting with \n\n).
|
||||
let split_at = if split_at == 0 {
|
||||
window_bytes
|
||||
} else {
|
||||
split_at
|
||||
};
|
||||
let split_at = if split_at == 0 { window_bytes } else { split_at };
|
||||
|
||||
// Trim whitespace at chunk boundaries for clean Telegram display.
|
||||
// Note: this drops leading/trailing spaces at split points, which is
|
||||
@@ -1391,13 +1358,7 @@ fn send_response(
|
||||
|
||||
for (i, chunk) in chunks.into_iter().enumerate() {
|
||||
// Try Markdown, fall back to plain text on parse errors
|
||||
let result = send_message(
|
||||
chat_id,
|
||||
&chunk,
|
||||
reply_to,
|
||||
Some("Markdown"),
|
||||
message_thread_id,
|
||||
);
|
||||
let result = send_message(chat_id, &chunk, reply_to, Some("Markdown"), message_thread_id);
|
||||
|
||||
let msg_id = match result {
|
||||
Ok(id) => {
|
||||
@@ -2260,10 +2221,6 @@ export!(TelegramChannel);
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn utf16_len(text: &str) -> usize {
|
||||
text.encode_utf16().count()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_message_short() {
|
||||
let text = "Hello, world!";
|
||||
@@ -2291,7 +2248,7 @@ mod tests {
|
||||
let chunks = split_message(&text);
|
||||
assert!(chunks.len() > 1, "expected multiple chunks");
|
||||
for chunk in &chunks {
|
||||
assert!(utf16_len(chunk) <= TELEGRAM_MAX_MESSAGE_LEN);
|
||||
assert!(chunk.chars().count() <= TELEGRAM_MAX_MESSAGE_LEN);
|
||||
}
|
||||
// Rejoined chunks must equal the original text exactly.
|
||||
let rejoined = chunks.join(" ");
|
||||
@@ -2307,7 +2264,7 @@ mod tests {
|
||||
assert!(text.len() > TELEGRAM_MAX_MESSAGE_LEN);
|
||||
let chunks = split_message(&text);
|
||||
for chunk in &chunks {
|
||||
assert!(utf16_len(chunk) <= TELEGRAM_MAX_MESSAGE_LEN);
|
||||
assert!(chunk.chars().count() <= TELEGRAM_MAX_MESSAGE_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2337,7 +2294,7 @@ mod tests {
|
||||
let chunks = split_message(&text);
|
||||
assert!(chunks.len() >= 2);
|
||||
for chunk in &chunks {
|
||||
assert!(utf16_len(chunk) <= TELEGRAM_MAX_MESSAGE_LEN);
|
||||
assert!(chunk.chars().count() <= TELEGRAM_MAX_MESSAGE_LEN);
|
||||
}
|
||||
// Rejoined must preserve all characters
|
||||
let rejoined: String = chunks.concat();
|
||||
@@ -2354,25 +2311,12 @@ mod tests {
|
||||
let chunks = split_message(&text);
|
||||
assert!(chunks.len() >= 2);
|
||||
for chunk in &chunks {
|
||||
assert!(utf16_len(chunk) <= TELEGRAM_MAX_MESSAGE_LEN);
|
||||
assert!(chunk.chars().count() <= TELEGRAM_MAX_MESSAGE_LEN);
|
||||
// Every char should be a complete emoji
|
||||
assert!(chunk.chars().all(|c| c == '\u{1F600}'));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_message_exact_utf16_limit_for_surrogate_pairs() {
|
||||
let emoji = "\u{1F600}"; // 😀
|
||||
let text = emoji.repeat(TELEGRAM_MAX_MESSAGE_LEN);
|
||||
|
||||
let chunks = split_message(&text);
|
||||
|
||||
assert_eq!(chunks.len(), 2);
|
||||
assert!(chunks
|
||||
.iter()
|
||||
.all(|chunk| utf16_len(chunk) <= TELEGRAM_MAX_MESSAGE_LEN));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clean_message_text() {
|
||||
// Without bot_username: strips any leading @mention
|
||||
|
||||
Reference in New Issue
Block a user