Repository navigation
Integrate Bitbucket master-v1: Telegram continuity, token counter, reasoning render (+3 fixes) - #98
Merged
Merged
Conversation
A turn-1 reasoning-only AI message (content is a lone reasoning block, no text, no tool_calls) becomes empty after stripReasoningBlocks() and crashed Bedrock on turn 2 — and on history-session resume — with "The content field in the Message object ... is empty". Both paths replay the LangGraph checkpoint through sanitizeMessagesForBedrock(), so both hit it. Fix: in sanitizeMessagesForBedrock(), when an AI message is empty after stripping and has no tool calls, rewrite its content to "(reasoning omitted)" instead of leaving it empty. Rewriting (not dropping) preserves message count and Human/AI role alternation, which getRecentMessages enforces upstream and Bedrock requires. Adds a module-private isEmptyAiMessageContent() predicate that leaves tool-use turns and normal answers untouched. Includes design spec + implementation plan under docs/superpowers/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reopened history sessions rendered extended-thinking as a raw
[{"type":"reasoning",...}] JSON blob. The write path JSON.stringify's AI
messages whose content is a content-block array, and the read path only kept
text blocks, so a pure-reasoning array fell through as a plain text answer.
Add a read-path helper (lib/agent-chat/ai-content-parts.ts) that reconstructs
such arrays block-by-block into reasoning/text parts — dropping empty-text
reasoning to match the live reducer — and wire it into the AI branch of
convertPlainMessage and convertMessage in the history route, with the existing
string/marker path as the untouched fallback. Read-path only: storage, the
write path, and the live stream are unchanged, so it fixes existing and future
sessions with no migration. Gated to the AI role; human/tool paths untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…kens to 8192 - Chat composer input limit 2000 -> 6000 chars (counter warning 1800 -> 5400). - DEFAULT_MAX_OUTPUT_TOKENS 4096 -> 8192 (fallback when a ProviderModel record sets no maxTokens); Bedrock/Anthropic Claude models used by AI Ops support it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inbound Telegram webhooks were being redirected to /login by the NextAuth middleware (gateway routes were never exempted from the auth matcher, unlike api/v1/trigger), and even once reachable, the adapter resolved tenantId from Telegram's chat.id instead of an actual tenant — a value that never matches, so every request was rejected as unauthorized. Adds TelegramBotLink (secretToken -> tenantId), mirroring the existing SlackWorkspaceLink pattern, since Telegram's payload carries no tenant- identifying field of its own. Also stops the gateway from returning a hard 400 for text-less messages (e.g. Telegram's automatic /start) on async "streaming" channels, since a non-2xx there makes Telegram retry the same update forever and blocks its whole queue behind it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A Telegram chat now behaves as an ongoing conversation. When a run is awaiting the user's input (agent asked a clarifying question), the next message is fed into that same run/thread instead of spawning a new run — so answers keep context. Once a run has completed and returned a result, the next message starts a fresh run. - findResumableTelegramRun(chatId, idleCutoff): resolves the chat's run currently awaiting input, within a 30-min idle window (stale questions are abandoned). Keyed on trigger.chatId, source=telegram. - /new (and /n): cancels a pending awaiting-input run and confirms, so the next message starts fresh rather than being read as the answer. - gateway: 'reset' action wired; adapter gains sendSessionReset. Fixes the prior behavior where every Telegram message — including a reply to the agent's own clarifying question — created a separate run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Show a cumulative incoming/outgoing token total in the AI Ops chat header, next to the elapsed timer. The counter sums usage_metadata (already produced by every Bedrock/Claude call but previously discarded) across all model calls. - Server: accumulate a request-scoped run total across every non-guard on_chat_model_end, stream a data-usage part per call (live), and persist the run total onto the turn's final AI message (metadata.usage_metadata). - History: reconstruct one data-usage part per turn from the stored total so the count survives reload and shows when reopening a session. - Client: deriveRunState sums data-usage parts into RunState.tokenUsage; the header renders "↓ in · ↑ out" (hidden at zero, exact counts on hover). Read-path/stream/persist only; no schema change. Pure helpers (parseUsageMetadata/formatTokens) and the reducer accumulation are unit-tested; live==reload parity verified by review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Integration/aiops telegram
…ing render Integrates Omar Hussain's work from the Bitbucket remote (rohitahuja1/smc-stx-nucleus-cloud-ops), merged there as PR #1: 47ba4ad fix(aiops): rewrite empty-after-strip reasoning messages to placeholder 3df80a9 fix(aiops): render array-content reasoning as thinking blocks in history 14fdc74 feat(aiops): raise chat input cap to 6000 chars, default output tokens 8192 6716c28 fix(agent-ops): Telegram tenant resolution + gateway webhook auth bypass 2586975 feat(agent-ops): Telegram conversational continuity + /new reset 825d81f feat(aiops): live + persisted token counter in chat header Both repos share history; they diverged at 4aa59f3, so this is a true merge. The only file touched by both sides — workspace/composer.tsx — auto-merged cleanly (our MicButton import/JSX vs Omar's MAX_CHARS bump); the dictation button now inherits the new 6000-char cap. Three fixes applied on top of the merged result: 1. middleware.ts — Omar exempted all of `api/v1/gateway` from the NextAuth gate so Telegram's webhook could reach the adapter. That also exempted `api/v1/gateway/api` and `api/v1/gateway/stream`. ApiAdapter.validateRequest accepts ANY Authorization/x-api-key header without validating it, and this middleware is what overwrites a client-supplied x-tenant-id with the session's real tenantId — so the wider matcher let an unauthenticated caller start an agent run under an arbitrary tenant. Narrowed the exemption to the five externally-signed webhook channels, which verify their own signature/secret. Verified against the compiled matcher regex. 2. libs/prisma/migrations/20260725120000_restore_raw_sql_indexes — Omar's telegram migration was generated by `prisma migrate dev`, which read three hand-written raw-SQL indexes as drift and emitted DROP INDEX for them (agent_memories HNSW, inventory searchVector GIN, kb_document_chunks IVFFlat). Dropping them is silent: queries stay correct but fall back to sequential scans. Confirmed on a scratch database — 0 of 3 survive the chain without this migration, 3 of 3 with it. The upstream migration is left byte-identical so an already-applied deploy does not hit a checksum mismatch; this forward migration is idempotent. 3. composer.test.tsx — the 2000 -> 6000 cap bump left the char-counter test asserting '1900/2000'. Updated to the new thresholds. Verification: web-ui tsc clean of new errors (0 TS2307; the 6 errors in touched files are byte-identical pre-merge). web-ui vitest 19 failed / 1330 passed vs a pre-merge baseline of 19 failed / 1295 passed — identical failure set (all DATABASE_URL-dependent), +35 new passing tests. workers typecheck clean, 5 failures vs 7 pre-merge. `bun run build:web` succeeds. Full migration chain applies clean on a fresh pgvector database. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Integrates Omar Hussain's work from the Bitbucket remote (
rohitahuja1/smc-stx-nucleus-cloud-ops), merged there as PR #1.Both repos share commit history and the Nx layout, so this is a true
git mergerather than a path remap — they diverged at4aa59f36.Commits integrated
47ba4add3df80a9014fdc7476716c282telegram_bot_linkstable + gateway webhook auth bypass25869751/newreset825d81ffapps/web-ui/components/agent/workspace/composer.tsxwas the only file touched by both sides (ourMicButtonimport/JSX vs theMAX_CHARSbump). It auto-merged cleanly and the dictation button now inherits the 6000-char cap.Fixes applied on top of the merged result
1. Narrow the gateway auth exemption (critical)
6716c282exempted all ofapi/v1/gatewayfrom the NextAuth matcher so Telegram's webhook could reach the adapter. That also exemptedapi/v1/gateway/apiandapi/v1/gateway/stream.Two things compound there:
ApiAdapter.validateRequestreturnstruefor anyAuthorizationorx-api-keyheader without validating its value.middleware.tsis what overwrites a client-suppliedx-tenant-idwith the session's realtenantId— skipping the matcher means the client's header reaches the route verbatim.Net effect: an unauthenticated caller could start an agent run under an arbitrary tenant, with that tenant's AWS credentials and LLM provider.
Narrowed the exemption to the five externally-signed webhook channels (Telegram secret token, Slack HMAC, Discord Ed25519, Jira/webhook shared secret), each of which verifies its own signature in
validateRequest. Verified all 12 path cases against the compiled matcher regex from.next/server/middleware-manifest.json.2. Restore three dropped production indexes
6716c282's migration was generated byprisma migrate dev, which cannot see three hand-written raw-SQL indexes (pgvectorUnsupported("vector")/ tsvector columns have no Prisma index syntax). It read them as drift and emittedDROP INDEXalongside the intended table:agent_memories_embedding_hnsw— memory recallidx_inventory_search_vector— inventory full-text searchidx_kb_document_chunks_embedding— KB semantic searchDropping them is silent: queries stay correct but fall back to sequential scans. Confirmed on a scratch pgvector database — 0 of 3 survive the chain without a fix, 3 of 3 with it.
Added
20260725120000_restore_raw_sql_indexes, which recreates all three withIF NOT EXISTS. The upstream migration is left byte-identical so an already-applied deploy does not hit aprisma migrate deploychecksum mismatch.3. Fix the char-counter test
The 2000→6000 cap bump left
composer.test.tsxasserting'1900/2000'. Updated to the new thresholds.Verification
TS2307. The 6 errors in touched files are byte-identical pre-merge (line numbers shift exactly by the insertions above).DATABASE_URL-dependent), +35 new passing tests.bun run build:web: succeeds.pgvector/pgvector:pg16container; end state asserted viapg_indexes. The local dev database was never touched.Follow-ups (not in this PR)
api/v1/triggeris exempted wholesale andapi/v1/trigger/api/route.tsis a live back-compat alias calling the samehandleInbound('api', …), so the same unauthenticated-run path exists there today. The real fix is validating the API key inApiAdapter.validateRequest(or deleting the alias), not another matcher tweak.deployment/to-sandbox-envbranch (6 infra-only commits parameterizing both Pulumi stacks byappName/natStrategyfor an isolatedsbxenvironment) is unmerged on Bitbucket and deliberately left out.🤖 Generated with Claude Code