perf: cache deps layer + drop arm64 QEMU build - #2
Merged
Conversation
pahud
approved these changes
Apr 3, 2026
henrieopenclaw
added a commit
to henrieopenclaw/agent-broker
that referenced
this pull request
Apr 6, 2026
* perf: cache dependency build layer in Dockerfile * perf: native multi-arch build with matrix runners --------- Co-authored-by: thepagent <thepagent@users.noreply.github.com>
Reese-max
pushed a commit
to Reese-max/openab
that referenced
this pull request
Apr 12, 2026
* perf: cache dependency build layer in Dockerfile * perf: native multi-arch build with matrix runners --------- Co-authored-by: thepagent <thepagent@users.noreply.github.com>
11 tasks
6 tasks
3 tasks
brettchien
added a commit
to brettchien/openab
that referenced
this pull request
Apr 19, 2026
Before this change, `openab-claude:0.7.8-beta.7` ships: - claude-agent-acp@0.25.0 — hardcoded model list, no Opus 4.7 - claude-code@2.1.104 — knows up to Opus 4.6 only - ENV CLAUDE_CODE_EXECUTABLE=/usr/local/bin/claude (openabdev#447) With openabdev#447 making the pinned claude-code binary load-bearing, neither the adapter's availableModels nor the CLI's model resolver knows about Opus 4.7 — users get Sonnet 4.6 regardless of ANTHROPIC_MODEL=opus. This PR: - introduces `CLAUDE_AGENT_ACP_VERSION` ARG (pattern parity with `CLAUDE_CODE_VERSION` from openabdev#326/openabdev#412) - bumps adapter 0.25.0 → 0.29.2 (brings claude-agent-sdk 0.2.111+ whose availableModels includes Opus 4.7) - bumps claude-code 2.1.104 → 2.1.114 anthropics/claude-code#49512 (parallel-mkdir ENOENT race) was filed against 2.1.112 and is still OPEN but has zero comments. Inspection of the 2.1.114 install shows the CLI moved to a per-platform native binary (openabdev#2.1.113) and session state relocated from `/tmp/claude-<uid>/…/tasks/` to `$HOME/.claude/{projects,tasks}/`, so the vulnerable filesystem layout no longer exists — binary-grep for `/home-//tasks` returns 0 matches. 50 parallel `claude -p` calls as the same user (40ms stagger) produced 0/50 errors, 0 bytes stderr, and no `/tmp/claude-*` directories. Refs openabdev#326, openabdev#412, openabdev#418, openabdev#447 Closes nothing explicitly (no issue filed; repro + rationale in body). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 25, 2026
This was referenced May 1, 2026
masami-agent
added a commit
to masami-agent/openab
that referenced
this pull request
May 1, 2026
- CONTRIBUTING.md now has all 8 sections (0-7) matching the ADR table - Added missing sections: At a Glance (openabdev#2), split Proposed Solution (openabdev#4) and Why This Approach (openabdev#5) into separate sections - ADR status updated from Proposed to Accepted
wangyuyan-agent
added a commit
to wangyuyan-agent/openab
that referenced
this pull request
May 3, 2026
…nd nits - Remove dead variables in markdown_to_post() (blocker openabdev#2) - Fix allowlist bypass: check allowlist before bot classification (suggested openabdev#3) - Fix thread key mismatch: use thread_id.unwrap_or(channel_id) (suggested openabdev#4) - Add /models and /agents slash commands - NITs: let mut→let, cancel format, DM comment, bot_turns TODO, timeout 5s, dead_code allow, warn on parse error
thepagent
pushed a commit
that referenced
this pull request
May 3, 2026
…o-bot readiness (#706) * feat(gateway): feishu slash commands, rich text, and bot-to-bot readiness - /reset and /cancel interception in OAB core gateway adapter (src/gateway.rs) - Rich text (post) messaging: markdown_to_post() converter + send_post_message() - Bot-to-bot: AllowBots enum, trusted_bot_ids, max_bot_turns, bot detection heuristic - Updated docs/feishu.md with new sections and env vars * feat(gateway): streaming (typewriter) support for Feishu adapter - OAB core: send_message request-response for Feishu (real message_id) - OAB core: edit_message sends command via WebSocket - OAB core: use_streaming returns true for Feishu only - GatewayResponse: add message_id field (backward compatible) - Gateway service: edit_feishu_message via PUT API - Gateway service: handle_reply returns message_id in response * docs: add streaming section to feishu.md * fix: address PR #706 review — blockers, suggested changes, and nits - Remove dead variables in markdown_to_post() (blocker #2) - Fix allowlist bypass: check allowlist before bot classification (suggested #3) - Fix thread key mismatch: use thread_id.unwrap_or(channel_id) (suggested #4) - Add /models and /agents slash commands - NITs: let mut→let, cancel format, DM comment, bot_turns TODO, timeout 5s, dead_code allow, warn on parse error * docs: fix bot-to-bot description after allowlist bypass fix * docs: add bot-to-bot env vars to gateway README * fix: use split_once per clippy * refactor: remove /models /agents (follow-up PR), replace platform hardcode with supports_streaming() - Remove /models and /agents slash command interception (will be a separate PR with exact matching, ambiguity handling, get_or_create, category aliasing) - Replace channel.platform == "feishu" with supports_streaming() method - supports_streaming() uses matches!(platform_name, "feishu" | "lark") * refactor: remove /models /agents (follow-up PR), config-driven streaming - Remove /models and /agents slash commands (separate PR with design review) - Replace platform-specific checks with config-driven streaming flag - send_message: all platforms use request-response + 5s timeout fallback - use_streaming: reads self.streaming from GatewayConfig - Add gateway.streaming config field (default false) - Update docs/feishu.md: remove /models /agents, streaming via config * fix: streaming-only request-response, proper inline code parsing - send_message: request-response only when self.streaming is true, fire-and-forget otherwise (no Telegram regression) - parse_inline: paired backtick parsing preserves literal content inside code spans, no longer strips markers inside inline code * fix(gateway): slash command responses use fire-and-forget, not request-response - Extract SharedWsTx type alias (Arc<Mutex<SplitSink>>) so ws_tx can be shared between GatewayAdapter and the event loop - Add send_fire_and_forget() helper for slash command responses that don't need message_id back (no 5s timeout penalty) - send_message() still uses request-response when streaming=true (needed for placeholder message_id in streaming edit flow) Addresses review feedback: slash command responses (/reset, /cancel) no longer go through the request-response path, avoiding unnecessary latency when gateway.streaming is enabled. * docs(gateway): add TODO for bot-to-bot core guard gap (擺渡法師 finding) Gateway core unconditionally drops is_bot events, but feishu adapter's AllowBots filtering happens before events reach core. When Feishu lifts the bot-to-bot delivery restriction, this guard needs to become adapter-aware. Telegram adapter does not filter bots, so we cannot simply remove the guard without introducing regression. * docs(feishu): narrow bot-to-bot claim to gateway-side scaffolding only Per 擺渡法師 review: PR claimed 'bot-to-bot readiness' but OAB core unconditionally drops is_bot events (src/gateway.rs:430). The gateway adapter's AllowBots filtering works, but events never reach the router. Updated docs/feishu.md to explicitly state both blockers: 1. Feishu platform doesn't deliver bot messages to other bots 2. OAB core drops is_bot events before router Claim narrowed from 'readiness' to 'gateway-side scaffolding only'. * fix(feishu): paired-only marker stripping + send failure response (普渡法師 findings) 1. parse_inline(): only strip *paired* markdown markers (**, *, ~~). Unpaired markers kept as literal text. Fixes: ~/.ssh → /.ssh, *.rs → .rs, 3 * 4 → 3 4 2. handle_reply(): send GatewayResponse { success: false } when send_post_message() fails. Prevents core from waiting 5s timeout then sending edit_message to a non-existent message_id. --------- Co-authored-by: wangyuyan-agent <265828726+wangyuyan-agent@users.noreply.github.com> Co-authored-by: chaodu-agent <chaodu-agent@openab.dev>
thepagent
pushed a commit
that referenced
this pull request
May 4, 2026
* RFC 002: PR contribution guidelines with mandatory prior art research Add RFC, PR template, and CONTRIBUTING.md requiring contributors to research OpenClaw and Hermes Agent before proposing solutions. - docs/rfcs/002-pr-guidelines.md — full RFC (follows RFC 001 format) - .github/pull_request_template.md — auto-populated PR form - CONTRIBUTING.md — contributor guide linking to RFC * feat: add Discord Discussion URL field to PR template, CONTRIBUTING.md, and RFC * docs: reformat RFC 002 as ADR under docs/adr/ Move PR contribution guidelines from docs/rfcs/ to docs/adr/ format, matching the existing ADR structure (line-adapter, custom-gateway). * docs: implement tiered PR contribution guidelines Rework PR #302 to address review feedback from @shaun-agent and @masami-agent: - Prior art research: required for architectural/runtime changes, N/A for docs/chore/CI - Validation section: multi-surface (Rust, Helm, CI, docs) instead of Rust-only - Discord Discussion URL: strongly recommended instead of auto-close policy - CONTRIBUTING.md: add link to ADR as specified in implementation table - ADR: update to reflect tiered policy (Option 3) as adopted approach - Fix trailing whitespace in PR template * docs: align CONTRIBUTING.md sections with ADR, update status to Accepted - CONTRIBUTING.md now has all 8 sections (0-7) matching the ADR table - Added missing sections: At a Glance (#2), split Proposed Solution (#4) and Why This Approach (#5) into separate sections - ADR status updated from Proposed to Accepted --------- Co-authored-by: OpenAB Bot <openab-bot@users.noreply.github.com> Co-authored-by: chaodufashi <chaodu-agent@openab.dev> Co-authored-by: chaodu-agent <chaodu-agent@users.noreply.github.com> Co-authored-by: Masami <masami-agent@users.noreply.github.com>
5 tasks
brettchien
added a commit
to brettchien/openab
that referenced
this pull request
May 5, 2026
…ts, fix ADR path - main.rs: collapse 3x repeated (cap, grouping, idle) match blocks into dispatch::dispatch_params(mode, max_buffered). - dispatch.rs: replace magic 4 / 512 in estimate_tokens with named CHARS_PER_TOKEN_ESTIMATE / TOKENS_PER_IMAGE_ESTIMATE constants. - dispatch.rs: fix top-level ADR reference to point at the actual docs/adr/turn-boundary-batching.md path landing in openabdev#598. Addresses chaodu-agent NITs openabdev#1, openabdev#2, openabdev#5 from PR openabdev#686. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
3 tasks
4 tasks
chaodu-agent
pushed a commit
that referenced
this pull request
Jun 10, 2026
🔴 #1: Thread key is available in <sender_context> JSON block that OAB already injects into every prompt. Adapter parses thread_id from it. No OAB changes needed. Added detailed explanation + fallback. 🟡 #2: session/load now documents expired session handling — if 404, transparently re-invoke (AgentCore auto-mounts persisted filesystem). 🟡 #3: Cancel strategy is now configurable via --cancel-strategy flag (noop = ignore cancel, stop = StopRuntimeSession). Default: stop.
chaodu-agent
pushed a commit
that referenced
this pull request
Jun 10, 2026
- Fix #1: synthesis uses absolute path /opt/agentcore-acp/agentcore_acp.py - Fix #2: Dockerfile.agentcore adds curl, installs uv as agent user, installs boto3 system-wide, sets PATH for agent - Fix #3: replace value-based heuristic with command_explicit bool flag (immune to OPENAB_AGENT_COMMAND env pollution) - Fix #4: cancel_strategy is now a validated enum (stop|noop), rejects typos - Fix #6: docs priority rules clarify env var interaction - Fix #7: docs use absolute path consistently
chaodu-agent
pushed a commit
that referenced
this pull request
Jun 12, 2026
…on destroy - #1 (🔴) Fix None dereference: check session is not None before accessing - #2 (🟡) Log shell reader errors to stderr instead of silently swallowing - #3 (🟡) Clear stale response queue on reconnect - #4 (🟡) Remove unused ACP_READY_MARKER constant - #5 (🟡) Add session/destroy handler to clean up resources
thepagent
pushed a commit
that referenced
this pull request
Jun 13, 2026
…ions (#1089) * feat(agentcore): use InvokeAgentRuntimeCommandShell for persistent sessions Replace InvokeAgentRuntime (stateless HTTP) with InvokeAgentRuntimeCommandShell (persistent WebSocket PTY). This opens a long-lived shell in the microVM, runs kiro-cli in native ACP mode, and bridges JSON-RPC bidirectionally. Fixes: each Discord thread message was treated as a new conversation because InvokeAgentRuntime hit healthcheck.py which spawned a fresh kiro-cli per call. Now: same shell_id = same kiro-cli process = continuous session memory. Key changes: - Switch from boto3 invoke_agent_runtime to bedrock-agentcore SDK open_shell() - Deterministic shell_id derived from thread_id for reconnection support - Async architecture (asyncio) for WebSocket + stdin multiplexing - Auto-reconnect via ReconnectConfig on transient drops - Forwards kiro-cli ACP notifications (streaming) directly to OAB stdout * fix: address review — None deref, error logging, queue cleanup, session destroy - #1 (🔴) Fix None dereference: check session is not None before accessing - #2 (🟡) Log shell reader errors to stderr instead of silently swallowing - #3 (🟡) Clear stale response queue on reconnect - #4 (🟡) Remove unused ACP_READY_MARKER constant - #5 (🟡) Add session/destroy handler to clean up resources * nit: remove redundant import sys in _read_loop * fix: address 擺渡 review — ANSI stripping, JSON-RPC validation, dispatch guard - F1: Dispatch loop now skips messages without 'method' field (no false error responses) - F2: Strip ANSI escape codes before JSON parse; require line starts with '{' and contains 'jsonrpc' field to be treated as valid JSON-RPC - Disable PTY echo via 'stty -echo' before launching kiro-cli to reduce noise * fix: use _extract_json_object for PTY-robust JSON parsing Handles shell prompt prefix (e.g. 'agent@vm:~$ {"jsonrpc"...}') by reusing the existing brace-counting parser instead of startswith. * nit: move import time to top-level * feat(agentcore): replace Python adapter with native Rust WebSocket bridge Drop agentcore/acp/ Python adapter entirely. The bridge now lives in src/acp/agentcore.rs and runs as an 'openab agentcore-bridge' subprocess spawned by config.rs when [agentcore] is set. Architecture: OAB pool → spawn('openab agentcore-bridge --runtime-arn ... --region ...') → opens WebSocket to InvokeAgentRuntimeCommandShell → launches 'kiro-cli acp --trust-all-tools' inside PTY → bidirectional JSON-RPC over binary frames (0x00=stdin, 0x01=stdout) Key benefits over Python version: - Single binary, no Python/uv/pip dependency - Native tokio async, zero overhead - SigV4 signing via aws-sigv4 crate (same as agctl) - Deterministic shell_id for reconnection on transient drops - PTY noise filtering (ANSI strip, JSON boundary detection) New dependencies (behind 'agentcore' feature flag, included in default): aws-sigv4, aws-credential-types, urlencoding, hex, http Note: Cannot verify full compilation in this environment (missing cc linker), but rustfmt --edition 2021 confirms all files parse correctly. * fix: port F1 guard to Rust — skip messages without method field * fix: address 覺渡 review — channel constants + brace-counting JSON extraction - Replace magic 0x00/0x01/0x02 with named CHANNEL_STDIN/STDOUT/STDERR constants - Replace naive find('{') with brace-counting extract_json_object() that handles PTY noise prefixes containing '{' characters (same approach as Python version) * fix: add session/destroy handler + address remaining review findings - Add session/destroy and session/stop handlers (parity with Python version) - Sessions are removed from HashMap on destroy, dropping ShellHandle and aborting the pump task (Rust Drop semantics handle cleanup) * fix: exit pump task when receiver is dropped (prevent leak) * docs: update Dockerfile.agentcore and docs for native Rust bridge - Dockerfile.agentcore: remove Python/pip/uv/boto3, single binary only (~20MB) - docs/agentcore.md: update architecture diagram, prerequisites, IAM policy, config examples to reflect WebSocket shell bridge * fix: resolve borrow checker errors + deprecated aws_config::from_env - Take line_rx out of session via std::mem::replace to avoid holding &mut self across await points while also writing to stdout - Replace deprecated aws_config::from_env() with aws_config::defaults() * fix: resolve clippy lints — unused imports, dead code, iterator, useless into - Remove unused tracing imports (debug, error, warn) - Allow dead_code on runtime_session_id and cancel_strategy (future use) - Use iter().position() instead of manual index loop - Remove useless .into() on Vec<u8> → Vec<u8> * fix: remove unnecessary to_string in handle_cancel * fix: update agentcore test to expect self-spawn instead of uv * fix(agentcore): send initialize + session/new to kiro-cli in PTY The bridge was intercepting initialize/session/new from OAB but never forwarding them to kiro-cli. Without initialization, kiro-cli silently drops all subsequent messages (session/prompt returns empty). Fix: after opening the WebSocket PTY and launching kiro-cli acp, send initialize → wait for response → send session/new → store the kiro sessionId → use it when forwarding prompts. * feat(agentcore): make bridge agent-agnostic with --command flag The bridge is now a generic ACP proxy that works with any ACP agent, not just kiro-cli. The command to run in the PTY is configurable: [agentcore] runtime_arn = "arn:..." shell_command = "kiro-cli acp --trust-all-tools" # default Other examples: shell_command = "claude-agent-acp" shell_command = "codex-acp" shell_command = "opencode acp" CLI flag: openab agentcore-bridge --command "..." * fix(agentcore): retry initialize send instead of fixed 2s sleep * fix(agentcore): eager shell init during session/new Move open_shell + initialize + session/new handshake from handle_prompt into the session/new handler. By the time session/prompt arrives, the shell is already connected and kiro-cli is initialized. This eliminates the race where the first prompt times out because shell opening + agent boot takes longer than OAB's dispatch timeout. * fix(agentcore): remove runtimeSessionId from query string Per the Python SDK, session_id belongs in the signed header (X-Amzn-Bedrock-AgentCore-Runtime-Session-Id), not as a query param. The extra query param was causing 400 Bad Request. * fix(agentcore): remove qualifier=DEFAULT from shell URL * fix(agentcore): switch to presigned URL auth for WebSocket shell Use SigV4 query string auth (presigned URL) instead of header auth. Matches the Python SDK's connect_shell_presigned approach. Session ID passed as query param covered by signature. * fix: add qualifier=DEFAULT back * fix(agentcore): retry initialize with 10s wait per attempt kiro-cli needs several seconds to boot in the PTY before it starts listening on stdin. Send initialize, wait 10s for response, retry up to 5 times. * fix(agentcore): skip notifications in init handshake, add cwd to session/new kiro-cli sends notifications before responding to session/new. The init loop now checks for matching 'id' field and skips notifications. session/new includes cwd and mcpServers params as required by kiro-cli. * fix(agentcore): copy OAuth DB to /tmp before starting agent AgentCore's /mnt/agent filesystem doesn't support SQLite file locking. Copy the DB to /tmp (local filesystem) and set XDG_DATA_HOME=/tmp so kiro-cli finds auth on a lockable filesystem. * docs(agentcore): add architecture diagram and shell_command config --------- Co-authored-by: 超渡法師 <chaodu@openab.dev> Co-authored-by: chaodu-agent <chaodu-agent@users.noreply.github.com>
chaodu-agent
pushed a commit
that referenced
this pull request
Jun 15, 2026
…ft path - edit_message with reply_to='draft': route to sendRichMessageDraft (rich) or silently drop (non-rich). Never attempt editMessageText on dummy ref. - edit_message with real message_id: perform actual editMessageText for legacy streaming placeholder updates. - Stream finalization: when placeholder is dummy 'draft' ref, send final content as new message (send_message) so it gets persisted. This ensures rich path uses sendRichMessage for the final reply, not just a draft. Addresses 擺渡法師's 🔴 #2 — streaming finalization must persist.
thepagent
pushed a commit
that referenced
this pull request
Jun 16, 2026
* docs: add ADR for Telegram Rich Messages (Bot API 10.1) Propose integrating sendRichMessage and sendRichMessageDraft into the TG gateway adapter for structured formatting and AI streaming UX. Refs: https://core.telegram.org/bots/api#rich-message-formatting-options * feat(gateway): implement Telegram Rich Messages (Bot API 10.1) - Add sendRichMessage() for structured content (tables, code, headings) - Add sendRichMessageDraft() for future AI streaming support - Add is_complex_markdown() classifier to route complex replies - Feature-gated via TELEGRAM_RICH_MESSAGES=true env var (default: off) - Falls back to sendMessage on sendRichMessage failure When enabled, replies containing tables, fenced code blocks, headings, or content >4096 chars will use sendRichMessage with InputRichMessage markdown format. This passes agent markdown directly (GFM-compatible) without needing any conversion layer. * fix: improve ATX heading detection in is_complex_markdown Address review from 擺渡法師: - Detect h1-h6 headings (not just h1-h3) - Handle headings at line start (not just after \n) - Handle indented headings (leading whitespace) - Reject #hashtag (no space after #) * fix: add 32768 char truncation before sendRichMessage Address review from 口渡法師: clamp content to Bot API limit (32768 UTF-8 chars) before sending, avoiding wasted round-trip on oversized payloads. * feat: default TELEGRAM_RICH_MESSAGES to true (opt-out) Users get rich messages automatically. Set TELEGRAM_RICH_MESSAGES=false to opt out. * fix: robust table separator detection for aligned GFM tables Address 擺渡法師 review: LLM outputs commonly use alignment markers like |:---|, |---:|, | :---: | which the naive |---| check missed. Now parses the separator row properly: starts/ends with |, each cell between pipes contains only dashes (optionally wrapped in colons). * fix: detect tilde fences (~~~) in is_complex_markdown Address 口渡法師 review: some agents/tools use ~~~ instead of backtick fences. Both are valid GFM code fences. * docs: add inline design decision comments Explain rationale for: - Classify at adapter layer (not agent) — zero prompt changes needed - Conservative heuristic — only route when legacy visibly breaks - 4096 threshold — sendMessage hard limit, prefer rich over chunking - GFM table parsing — avoid false positives on plain pipe text - Fallback strategy — one extra round-trip worst case, never lost delivery - sendRichMessageDraft — wired for Phase 2 streaming * feat(gateway/telegram): wire edit_message to sendRichMessageDraft for streaming * feat(gateway/telegram): send RichBlockThinking draft on first reaction (👀) * feat(gateway/telegram): state-aware thinking animations (👀🤔👨💻🔥⚡) * fix(gateway/telegram): use html field for tg-thinking drafts * fix(gateway/telegram): drop custom emoji from thinking (RICH_MESSAGE_EMOJI_INVALID) * fix(gateway/telegram): skip short streaming drafts, let thinking show * fix(gateway/telegram): suppress streaming placeholder, use thinking draft instead * Revert "fix(gateway/telegram): suppress streaming placeholder, use thinking draft instead" This reverts commit bad8749. * feat: add streaming_placeholder config to suppress '…' placeholder When streaming_placeholder = false, the core skips sending the initial '…' message. The gateway's edit loop still works via a dummy MessageRef since the gateway adapter uses sendRichMessageDraft (no real msg_id needed). Config: [gateway] streaming_placeholder = false * fix: MessageRef field is message_id not id * fix(gateway/telegram): address review findings from musingfox 🔴 1: Rich truncation now counts chars, not bytes (CJK safe) 🔴 2: Legacy sendMessage chunks at 4096 chars (no more lost replies) 🟡 3: Code blocks stay on legacy path (preserves syntax highlighting); only tables + headings route to sendRichMessage * fix(gateway): update test — code blocks no longer trigger is_complex_markdown * fix: address review findings — draft guard, draft_id collision, docs/ADR sync - Guard edit_message when rich_messages=false (drop silently instead of attempting editMessageText with dummy 'draft' message_id) - Incorporate thread_id in draft_id derivation to prevent forum topic collision - ADR: remove code blocks from classification, fix MarkdownV2→Markdown, update config reference from TOML to env var - docs/telegram.md: update markdown rendering section, add TELEGRAM_RICH_MESSAGES to env var table - docs/config-reference.md: add streaming + streaming_placeholder fields - config.toml.example: add streaming/streaming_placeholder examples * fix: proper edit_message handling — finalize via send_message for draft path - edit_message with reply_to='draft': route to sendRichMessageDraft (rich) or silently drop (non-rich). Never attempt editMessageText on dummy ref. - edit_message with real message_id: perform actual editMessageText for legacy streaming placeholder updates. - Stream finalization: when placeholder is dummy 'draft' ref, send final content as new message (send_message) so it gets persisted. This ensures rich path uses sendRichMessage for the final reply, not just a draft. Addresses 擺渡法師's 🔴 #2 — streaming finalization must persist. * docs(adr): fix remaining MarkdownV2 reference, update status to Accepted - Context section: MarkdownV2 → Markdown (matches actual code) - Status: Proposed → Accepted (feature is implemented) --------- Co-authored-by: chaodu-agent <bot@openab.dev> Co-authored-by: chaodu-agent <chaodu-agent@users.noreply.github.com> Co-authored-by: chaodu-agent <chaodu-agent@openab.dev>
4 tasks
chaodu-agent
pushed a commit
that referenced
this pull request
Jun 18, 2026
Addresses review feedback from 口渡法師: 🔴 #1: register_thread() was never called - Discord: call register_thread(registry, channel_id, "discord") before submit - Slack: call register_thread(registry, thread_id, "slack") before submit - Pass ctl_registry through run_slack_adapter → handle_message - Add ctl_registry field to Discord Handler struct 🟡 #2: SlackAdapter missing rename_thread - Implement via conversations.rename API - Works for channels; message-thread rename not supported by Slack API
thepagent
pushed a commit
that referenced
this pull request
Jun 19, 2026
* feat: add `openab set/get` subcommands with Unix socket IPC Phase 1 implementation of runtime control via IPC: - Add `openab set <key> <value>` and `openab get <key>` subcommands - IPC over Unix domain socket (/tmp/openab.sock, configurable via OPENAB_SOCK) - `openab run` spawns a control socket server on startup - Client (set/get) connects to the running daemon, sends JSON, gets response Supported keys (Phase 1): - thread.name — rename the current Discord/Slack thread Architecture: - Same binary, different modes (like consul/vault) - `openab run` = daemon, `openab set/get` = client - Handler reads OPENAB_THREAD_ID env var for target channel - Extensible: new keys just add match arms in RuntimeHandler Closes #1131 * feat: add thread.archived and agent.status to openab set New keys: - thread.archived (true/false) — archive/unarchive Discord thread - agent.status — set bot custom presence (Discord gateway) Implementation: - Add archive_thread() to ChatAdapter trait (default no-op) - Implement via EditThread in DiscordAdapter - agent.status uses ShardMessenger::set_presence with ActivityData::custom - ShardMessenger shared via Arc<OnceLock<>> set in ready handler * feat: add --thread flag and adapter routing via ThreadRegistry - openab set/get now accept --thread <ID> (falls back to $OPENAB_THREAD_ID) - RuntimeHandler maintains a HashMap<platform, adapter> for multi-platform - ThreadRegistry (thread_id → platform) resolves which adapter handles a thread - Exposes ctl::new_registry() and ctl::register_thread() for adapters to call on message dispatch Usage: openab set --thread 1517277368 thread.name "new name" OPENAB_THREAD_ID=1517277368 openab set thread.name "new name" * feat: default rename_thread/archive_thread returns unsupported error Agent gets explicit feedback when the platform doesn't support the operation, rather than silent no-op success. * fix: wire ThreadRegistry into dispatch + implement Slack rename_thread Addresses review feedback from 口渡法師: 🔴 #1: register_thread() was never called - Discord: call register_thread(registry, channel_id, "discord") before submit - Slack: call register_thread(registry, thread_id, "slack") before submit - Pass ctl_registry through run_slack_adapter → handle_message - Add ctl_registry field to Discord Handler struct 🟡 #2: SlackAdapter missing rename_thread - Implement via conversations.rename API - Works for channels; message-thread rename not supported by Slack API * fix: chmod 0600 on control socket after bind (defense-in-depth) Per 覺渡法師 review — restrict socket to owner-only even in single-tenant pods, as a low-cost hardening measure. * fix: remove broken Slack rename_thread — fall back to unsupported error Per 擺渡法師 review: - conversations.rename only works on channels, not threads - Calling it on a thread returns invalid_channel - Calling it on a channel renames the entire public channel (dangerous) Correct behavior: Slack threads don't support rename, so use the trait default which returns 'not supported on this platform'. * fix: address 🟡 review items — log spam + agent.status messaging - dispatch.rs: downgrade rename_thread failure from warn → debug (expected on platforms that don't support it, not log-worthy) - ctl.rs: clarify agent.status error — 'only supported on Discord' instead of misleading 'shard not ready' * fix: clone ctl_registry before async move to avoid use-after-move E0382: ctl_registry was moved into the Slack tokio::spawn closure, then referenced again when constructing the Discord Handler. Clone before the closure so the original remains available. * fix: remove unsafe set_var from tests, use explicit socket path - Add spawn_server_at(path) and send_request_to(path) variants - Test uses explicit PathBuf instead of env var mutation - Avoids Rust 1.80+ UB with set_var in multi-threaded tests --------- Co-authored-by: 超渡法師 <超渡法師@openab.dev>
chaodu-agent
pushed a commit
that referenced
this pull request
Jun 24, 2026
- Add is_denied_user access control gate (🔴 #1) - Kill orphaned child process on empty-output early return (🔴 #2) - Add single-flight AtomicBool guard to prevent concurrent /auth (🟡 #4) - Truncate output to fit Discord 2000-char limit (🟡 #5) - Scope readers in a block to drop cleanly before wait (🟡 #6)
chaodu-agent
pushed a commit
that referenced
this pull request
Jun 24, 2026
…r) wait - Extract the UTF-16-code-unit truncation into a pure, module-level truncate_to_utf16_budget() helper (matching the repo's testable decision-helper convention) and cover it with unit tests: short body, prefix/suffix budgeting, surrogate-pair counting, no-scalar-splitting, zero-budget saturation, and an assembled-total-within-limit regression guard for the original scalar-count miscount. (addresses review #2 / tests) - Add the missing tracing::error on the final child.wait() Ok(Err(e)) arm, for symmetry with the spawn-failure path.
chaodu-agent
pushed a commit
that referenced
this pull request
Jun 26, 2026
Resolved: - [口渡 #1] channels empty = disabled (fail-safe), not all channels - [口渡 #2] Buffer lifecycle: swap-and-drain model, flush clears buffer - [口渡 #3] @mention triggers immediate flush + separate normal dispatch - [口渡 #4] Dedicated ambient session pool, isolated from main pool - [口渡 #5] Bot echo prevention: own messages never enter buffer - [口渡 #6] max_concurrent_flushes cap for global rate limiting - [口渡 #9] context_window = Discord API fetch, clarified semantics - [口渡 #10] Error handling table added (timeout, tool calls, etc.) - [核渡 #1] OpenClaw described as cross-platform group chat feature - [核渡 #2] Hermes backfill scope clarified (mention-only, skips free-response) - [核渡 #3] Added Hermes inline reply + per-user session detail - [覺渡 #1] Race condition resolved: swap-and-drain buffer model - [覺渡 #2] Session strategy fully specified (separate pool, rolling window) - [覺渡 #3] Mention detection fires before buffer, reuses existing logic - [覺渡 #4] flush_hard_cap = 50 as safety cap - [覺渡 #5] ±20% jitter on flush interval to prevent thundering herd - [覺渡 #6] context_window semantics clarified (API fetch, not buffer)
thepagent
added a commit
that referenced
this pull request
Jun 27, 2026
* docs: add ADR for Ambient Mode * docs: update ADR with batch flush strategy and prior art * docs: address all review findings (口渡, 核渡, 覺渡) Resolved: - [口渡 #1] channels empty = disabled (fail-safe), not all channels - [口渡 #2] Buffer lifecycle: swap-and-drain model, flush clears buffer - [口渡 #3] @mention triggers immediate flush + separate normal dispatch - [口渡 #4] Dedicated ambient session pool, isolated from main pool - [口渡 #5] Bot echo prevention: own messages never enter buffer - [口渡 #6] max_concurrent_flushes cap for global rate limiting - [口渡 #9] context_window = Discord API fetch, clarified semantics - [口渡 #10] Error handling table added (timeout, tool calls, etc.) - [核渡 #1] OpenClaw described as cross-platform group chat feature - [核渡 #2] Hermes backfill scope clarified (mention-only, skips free-response) - [核渡 #3] Added Hermes inline reply + per-user session detail - [覺渡 #1] Race condition resolved: swap-and-drain buffer model - [覺渡 #2] Session strategy fully specified (separate pool, rolling window) - [覺渡 #3] Mention detection fires before buffer, reuses existing logic - [覺渡 #4] flush_hard_cap = 50 as safety cap - [覺渡 #5] ±20% jitter on flush interval to prevent thundering herd - [覺渡 #6] context_window semantics clarified (API fetch, not buffer) * docs: integrate with existing Dispatcher/turn-boundary batching infra Ambient Mode reuses the Dispatcher, BufferedMessage, consumer_loop, and pack_arrival_event infrastructure from PR #686 (message_processing_mode). Key difference: ambient consumer uses timer-based flush instead of turn-boundary drain. * docs: add semaphore + error handling to ambient consumer pseudocode [覺渡 R2#1] flush_semaphore.acquire() before dispatch [覺渡 R2#2] match on dispatch result, warn + discard on error * docs: address 擺渡 R2 findings — race condition, thinking msg, bot loop [擺渡 #1] Add concurrent reply prevention via per-channel flushing flag [擺渡 #2] Ambient dispatches post directly, no thinking placeholder [擺渡 #3] Handle None on first rx.recv() (channel closed → exit) [擺渡 #4] Bot loop prevention: MAX_CONSECUTIVE_BOT_TURNS + prompt instruction * docs: strengthen F1 (discard buffer on mention) + F4 (bot msgs off by default) [擺渡 F1] @mention discards ambient buffer instead of flushing it; cancel in-flight ambient on mention arrival [擺渡 F4] allow_bot_messages defaults to 'off' for ambient channels * docs: fix wording inconsistency — mention discards buffer, not flushes [口渡 R3#1] Unify Message Filtering wording with Immediate flush section * docs(adr): add detailed ASCII architecture diagrams for ambient mode - Architecture Overview: full message routing decision tree - Dual-Path Concurrency: timeline showing mention vs ambient interaction - Shows buffer lifecycle, flush phases, and response routing * docs(adr): unify ambient config under top-level [ambient] section - Group all ambient settings under [ambient] instead of scattered [discord.ambient], [pool.ambient], [ambient.limits] - Platform-specific config in [ambient.discord] (extensible to slack/telegram) - Simplify pool key names: remove redundant ambient_ prefix (session_ttl_minutes, context_flushes) * docs(adr): move ambient mode ADR to docs/adr/ambient.md Addresses external reviewer feedback from @thepagent and team consensus. docs/steering/ is for process guides; docs/adr/ is for ADRs. * docs(adr): address review findings from 法師團隊 Fixes: - Unify @mention handling path (discard buffer, not flush) — resolves 3 contradictions across flush triggers table, concurrent reply section, and implementation details - Replace non-existent MAX_CONSECUTIVE_BOT_TURNS with actual max_bot_turns - Explain flush_hard_cap vs flush_max_messages relationship - Add post_guard for atomic check-and-post (TOCTOU race fix) - Add flush_timeout_seconds for AtomicBool safety recovery - Reconcile Buffer Lifecycle (conceptual) with mpsc channel (implementation) - Remove contradictory 'extend enum' wording from message_processing_mode - Add allow_bot_messages to [ambient.discord] config - Simplify pool key names (drop ambient_ prefix) * docs(adr): fix author attribution to chaodu-agent --------- Co-authored-by: 超渡法師 <超渡法師@openab.dev> Co-authored-by: chaodu-agent <chaodu-agent@openab.dev> Co-authored-by: thepagent <hehsieh1010@gmail.com>
thepagent
pushed a commit
that referenced
this pull request
Jun 28, 2026
* feat: add Devin CLI agent backend support Add Devin CLI (formerly Windsurf/Codeium) as a new agent backend. Devin CLI natively supports ACP via `devin acp` (JSON-RPC over stdio). Changes: - Dockerfile.unified: new `devin` target - docker-bake.hcl: add devin to default group and targets - config.toml.example: add commented Devin config block - docs/devin.md: full integration guide * fix: address review feedback on Devin CLI PR - Fix binary path: install script puts binary at ~/.local/bin/devin, not ~/.devin/bin/devin. Copy resolved binary to /usr/local/bin/. - Add standalone Dockerfile.devin for consistency with other agents. - Update docker-bake.hcl to reference Dockerfile.devin. * fix: address all review feedback - Pin Devin CLI version (2026.8.18) with SHA256 verification (same pattern as grok target) — fixes curl|bash supply chain concern - Remove incorrect WINDSURF_API_KEY claim (Devin is Cognition AI, not Codeium/Windsurf) - Remove redundant mkdir .config/devin (agent homedir suffices) - Add config mount note to Known Limitations in docs - Standalone Dockerfile.devin also uses pinned download * docs: add Model Selection and MCP Usage sections to devin.md Addresses 覺渡 review findings #2 and #3 — adds model selection instructions and MCP configuration guide to match cursor.md depth. * docs: fix install method description to match pinned tarball approach * fix: correct binary path to /tmp/bin/devin (tarball extracts to bin/ subdir) * docs: add Devin CLI to README agent list * ci: add devin to all workflow matrices and build lists --------- Co-authored-by: chaodu-agent <chaodu-agent@openab.dev>
thepagent
pushed a commit
that referenced
this pull request
Jul 13, 2026
* docs(adr): revise identity-trust-none to three-layer architecture Receiver → Trust Gate → Handler replaces the previous 'gate at handle_message()' design. Addresses all findings from the PR #1263 mob review (howie + 3 LLM reviewers). Key changes: - §4.2: Trust Gate is a dedicated ingress layer upstream of Handler - §5: New architecture diagram showing three-layer separation - §7: Implementation plan starts with Receiver/Handler split - Address #1: gate at actual convergence point (not handle_message) - Address #2: trust lookup keys off per-event platform (not adapter) - Address #3: slash commands gated (Handler is downstream of gate) - Address #4: exhaustive scattered-checks inventory - Address #5: explicit empty-vs-missing semantics - Address #6: phased rollout (Phase 0-3) - Address #7: echo rate-limit + bot exclusion + DM-preferred - Address #8: gateway vs first-class section precedence - Address #9: no static HashSet (runtime construction) - Address #10: structured logging on allow + deny - Address #11-#15: minor fixes (Teams ID, bot semantics, etc.) * docs(adr): address team review findings - Add type-level guarantee (GatedEvent vs InboundEvent) — compile-time enforcement, not just convention (#4) - Clarify Gateway Receiver is one receiver that demuxes by platform (#11) - Fix layer numbering inconsistency — use names, not numbers (#21) - Add sender ID format table with per-platform gotchas (#22, #23, #24) - Clarify is_bot bypass is caller-side, not inside decide() (擺渡-1) - Change echo group fallback to silent drop (avoid UID leakage) (#6) * docs(adr): add event loop binding design + fix is_bot L2 bypass - Add §5 'Event loop binding' section: run_platform generic pipeline, EventReceiver/EventHandler traits, main.rs startup wiring - Gateway platforms: one shared WS, demux by event.platform, fan-out to per-platform Handlers - Fix is_bot bypass: bots skip L3 but STILL enforce L2 scope (擺渡-1 🔴) - Add cross-crate boundary note for Gateway Receiver (擺渡-2 🟡) - Include binding topology summary diagram * docs(adr): address round 3 findings — tighten pseudocode precision - GatedEvent: private field in narrow module (not pub(crate)), with read-only accessors and module layout diagram (諸葛村夫-1) - gate_event: use configs.get().surface_allowed() to match real API (擺渡-3) - Phase table: add Phase 0.5 for current partially-wired state on main, clarify Phase 2 means 'refuse to start' (諸葛村夫-2) * docs(adr): fix seal() visibility — private fn, not pub(super) seal() lives in the same module as gate_event(), so it should be a plain private fn. pub(super) would unnecessarily expose it to the parent module. * docs(adr): fix stale module layout comment — constructor is private, not pub(super) * docs(adr): address LINE/Slack/Feishu review feedback - Replace line-number refs with symbol+semantic descriptions (drift-proof) - Rewrite echo section: platform-specific echo trait (LINE=Reply only, Slack=chat.postEphemeral, Discord=DM); leak-safe content by scope - Add is_bot per-platform derivation table (pinned canonical rules) - Document trusted_bot_ids as shared config (resolves Feishu circular dep) - Clarify slash commands scope (Slack doesn't consume them) - Update Slack sender ID: Enterprise Grid composite key (team_id, sender_id) - Add non-message events section (assistant_thread_started must gate) - Add Slack scope notes (Socket Mode only, MPIM=channel) - Add LINE group policy: open/members dual-mode in decide() - Add LINE @mention pre-filter as documented Receiver exception - Feishu: gateway=L1 only, eliminate double-gating, empty list=deny-all Addresses feedback from: - @luffy-aiagent (LINE platform review) - @antigenius0910 (Slack platform review) - @wangyuyan-agent (Feishu platform review) * docs(adr): address 9 review findings — API contract gaps + hardening Fixes identified during group review: F1+F6: Add workspace_id to InboundEvent; define Slack Enterprise Grid canonical sender_id format and config examples for Grid deployments F2: Replace HashMap<String, TrustConfig> with enum PlatformTrustConfig (Base/Line/Slack) — LINE group policy and Slack workspace-scoped trust now have proper type representations F3: Add cron bypass in gate_event() — system-initiated events skip L2/L3 (platform='cron' or sender_id='openab-cron') F4: Add #[cfg(test)] assume_trusted_for_test() constructor for GatedEvent — enables Handler unit testing without full pipeline F5+F9: Change into_inner() to pub(crate); adjust safety claim wording from 'bypass impossible' to 'accidental bypass compile error' F7: Change gate_event() signature to take InboundEvent by value — zero-copy hot path (no .clone() on RawPlatformEvent) F8: Specify bounded LRU cache (max_capacity + TTL) for rate-limit state — prevents OOM from random sender_id flooding * fix(adr): address review findings F1-F3 on identity-trust-none F1 (critical): Remove sender_id spoofing hole in cron bypass — only check platform == "cron" since WeCom allows freeform UserIDs that could match any synthetic value. Update rationale accordingly. F2: Add WeCom, Google Chat, MS Teams to pinned is_bot derivation and echo-delivery tables (all 8 platforms now covered). F3: Fix pseudocode precision — unwrap_or no longer borrows a temporary; add PlatformTrustConfigs::get() and PlatformTrustConfig::surface_allowed() delegating method used by gate_event. * fix(adr): address group review round 2 — 9 findings from B1/B4/B5/B8/B11/B12/B15 Fixes: - WeCom is_bot: remove enter_agent (user-initiated, not bot); keep only trusted_bot_ids (B5 F1, B12 F1) - SlackTrustConfig::decide(): workspace_users is now strict override (ignore allow_all_users); base fallback supports team_id:user_id composite key for Enterprise Grid (B4 F1, B5 F2, B12 F3) - LINE group policy: fix prose vs code contradiction — unconfigured groups use default_group_policy, not DenyScope (B12 F2) - Reserved platform validation: MUST-level requirement for all external Receivers to reject reserved platform names; cron bypass invariant documented; Phase 1 SHOULD for typed InboundSource enum (B1) - decide() simplified to use self.get() — remove duplicate default logic (B5 F3) - Echo rate-limit key updated to (platform, workspace_id, sender_id) (B12 F4, B15 F2) - Module layout: InboundEvent in mod.rs (public), gate.rs narrow (B12 F5) - into_inner() trust boundary doc: module-level vs crate-level explained, Phase 1 SHOULD for lint/annotation (B11, B15 F1) - Slack Enterprise Grid gotcha: split dense table cell into footnote (B8 F2) * fix(adr): add platform lowercase invariant to InboundEvent (B14 F2) InboundEvent.platform MUST be lowercase — Receivers normalize before constructing. This ensures consistency between gate_event's == "cron" check and PlatformTrustConfigs::get()'s to_lowercase() lookup. * docs(adr): clarify gw_event.platform source in run_gateway_platforms Add comment noting platform field is assigned by gateway routing config, not from webhook payload body — satisfies reserved platform invariant. * docs(adr): WeCom corrections from external review (canyugs#18) Three verified adjustments to the v2 trust architecture text: - WeCom callback mode is DM-only: channel_type always "direct" with a per-user channel id, so "group routing" does not exist for WeCom and its L2 scope is effectively allow_dm only (verified wecom.rs:1059-71) - Sender-id table: plain UserIDs are self-built-app only; external contact / ISV callbacks carry wm/wo-prefixed external_userid or encrypted OpenUserID (verified against official WeCom API docs) - Bot-bypass wording: is_bot is hardcoded false in the WeCom Receiver today, so the L3 bot-bypass is a no-op for WeCom — the bypass is available uniformly but effective only where the Receiver can derive is_bot Credit: canyugs. * docs(adr): refresh Phase 0.5 snapshot to shipped state Since the last revision, main gained: Slack L3 gate (#1363), per-platform [section] trust for all 8 platforms (#1365/#1366/#1385), full config-first parity with conformance guard (#1375/#1387), and the L1 unenforceable-auth startup warning (#1373). The Phase 0.5 row now lists the actual inventory and names the one outstanding Phase 1 prerequisite (standalone WS path still on should_skip_event, tracked on #1356). --------- Co-authored-by: chaodu-agent <chaodu-agent@openab.dev> Co-authored-by: chaodu-agent <chaodu-agent@users.noreply.github.com>
5 tasks
12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Build took 45 min because arm64 was compiled under QEMU emulation on an amd64 runner.
Fix
main.rsfirst (Docker layer cache)Before (sequential, QEMU)
After (parallel, native runners)