Repository navigation
Conversation
|
@bimgeek is attempting to deploy a commit to the OpenCore Team on Vercel. A member of the Team first needs to authorize it. |
| : recent.filter( | ||
| (candidate) => | ||
| candidate.metadata.dateSent.getTime() <= message.metadata.dateSent.getTime(), | ||
| ); |
There was a problem hiding this comment.
🟡 Later replies enter mention context
When history omits a mention, mentionWithContext includes later Slack replies sent within its millisecond. dateSent.getTime() loses Slack's microsecond ordering, so their text and files reach the earlier turn.
Learn more
A mention absent from recentMessages triggers the timestamp fallback. JavaScript Date values only store milliseconds, while Slack message IDs use microsecond timestamps. A later message can therefore have the same dateSent.getTime() as the mention and pass the inclusive comparison. Its content and files are then included by contextLine in the earlier turn.
Example: A mention at Slack timestamp 1710000000.000100 is missing from history; a later reply at 1710000000.000101 remains. Both dates produce the same millisecond, so the reply and its screenshot enter the mention's context.
Recommended fix: Compare provider message IDs at their native precision when both are comparable Slack timestamps, and use a strict timestamp comparison otherwise. Exclude the mention itself by ID in the fallback.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Fixed in 9fe004d (#466). When history no longer holds the mention, the fallback now keeps only messages from an earlier millisecond. A later reply at .100400 behind a mention at .100000 is now left out. The cost is that an earlier message in the mention's own millisecond is dropped too, which is safer than pulling a later reply into the earlier turn. The new test failed before the change and passes now, and the internals doc says the same.
| const cappedName = (name: string) => | ||
| Array.from(oneLine(name)).slice(0, ATTACHMENT_NAME_MAX_CHARS).join("").trim(); |
There was a problem hiding this comment.
🟡 Emoji file names prevent channel turns
For long emoji names, cappedName retains 255 code points exceeding the attachment schema's 255-character limit. The turn then fails validation after downloading and saving the file.
Learn more
The channel adapter normalizes attachment names before they become ChatAttachment values. The schema enforces a maximum string length of 255, but the new cap counts Unicode code points instead of UTF-16 string units. Astral characters such as emoji consume two UTF-16 units each. A name that passes this cap can thus fail validation when dispatchInboundChannelMessage sends the turn, after the file has already been stored.
Example: A file named with 200 emoji remains 200 code points after normalization but occupies 400 UTF-16 units. The channel writes the file and the turn cannot accept the attachment.
Recommended fix: Cap names using the same length measure as ChatAttachment while avoiding a trailing orphaned surrogate; test a name containing many astral characters through the full dispatch boundary.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Fixed in 3bbbcf1, which belongs to #465 where this code was added. cappedName now counts UTF-16 units, the same measure as the schema's isMaxLength(255), and stops before a character that would not fit, so it never splits a surrogate pair. A new test sends a 200-emoji name through the Slack dispatch path and gets exactly 127 emoji (254 units). It failed before the fix. An older test that encoded the code-point cap was updated to the new rule.
| const onInbound: InboundCallback = (message) => | ||
| ctx.runSdkCallback( | ||
| downloadInboundAttachments(ctx.inboundDownloadPermits, message.attachments).pipe( | ||
| Effect.flatMap((attachments) => dispatchUnderProviderLock(message, attachments)), | ||
| ), |
There was a problem hiding this comment.
🟡 Disconnected channels still fetch files
When a retired transport calls onInbound, downloadInboundAttachments fetches its files before the current-binding check. The message is discarded afterward, but the unnecessary downloads still run.
Learn more
The callback can remain reachable after a failed shutdown or a replacement. Before the change, dispatchUnderProviderLock checked that the runtime still owns the connection and its binding is connected before doing inbound work. The new callback downloads files before reaching those checks, so a retired transport can consume network time and bandwidth for messages that will not be handled.
Example: A Telegram transport fails to shut down during disconnect and invokes its old callback with eight PNG files. All eight files are fetched before the binding check discards the message.
Recommended fix: Perform a cheap current-runtime and connected-binding check before downloading, then keep the existing under-lock check to cover changes while the download is in progress.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Fixed in ed1e4d2, in #465. The "this transport still owns a connected binding" check is now one connectedRuntime effect. onInbound runs it before any download, and the dispatch runs it again under the provider lock in case the state changes during a download. The retired and replaced transport test now counts fetches. It saw 2 before the fix, and now sees 0 for retired callbacks and 1 for the current one.
| "akeru-bot": patch | ||
| --- | ||
|
|
||
| Bots connected to Slack and Discord now see where links point, even when a link shows a shortened label. |
There was a problem hiding this comment.
🟡 Unrelated changes share one pull request
This PR combines link rewriting, file ingestion, atomic writes, and mention context. AGENTS.md requires one concern per PR, so these independent changes need separate reviews.
Learn more
The repository's PR rule requires one concern per PR. This diff includes independent behavior changes to link rewriting, channel file ingestion, atomic file writes, and mention context, each described by its own changeset. Bundling them prevents independent review and rollback even though the immediate mention-context fix does not require the unrelated link feature.
Example: A regression in link rewriting requires reverting the same merged change that adds mention context files, even though the two features do not depend on each other.
Recommended fix: Separate independent features into focused PRs with their respective changesets; keep only changes needed for the mention-context behavior in this PR.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
This is a stack of four single-concern PRs: links #463, atomic write #464, shared files #465, mention-context files #466. PRs from a fork can't target another PR's branch, so each PR's diff against main includes the PRs below it. The description of each PR links to its own change, and they merge bottom-up.
| if (!(yield* fileSystem.exists(path).pipe(Effect.mapError(fail)))) { | ||
| yield* writeFileAtomically(path, input.bytes).pipe(Effect.mapError(fail)); | ||
| } |
There was a problem hiding this comment.
There was a problem hiding this comment.
No independent writers can target the same id. writeAttachmentIfMissing is only called from saveDownloadedFiles, inside dispatchInboundChannelMessage, which runs under withChannelOperation, the channel:<provider> lock. The id derives from the bot, project, provider, external thread, message, content hash, and occurrence, so two writes for one id would need the same provider and are serialized by that lock. Within one message, saves run with concurrency: 1, and the occurrence index keeps identical files apart. Composer uploads use random ids. So the existence check and the rename never race. Leaving this as is.
[High impact] The PR appears safe to merge; the startup-message issue is fixed and no new blocking issue was found. SummaryThe PR brings files from earlier Slack and Discord thread messages into a bot mention’s context.
Review was based on code and existing tests; tests were not run. Diagram%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Platform message] --> B[Read earlier thread context]
B --> C[Keep text and complete file markers]
C --> D[Wait for connected runtime]
D --> E[Download files outside provider lock]
E --> F[Recheck runtime under provider lock]
F --> G[Save files atomically]
G --> H[Start bot turn]
Reviews (6) · Last reviewed commit: "fix(server): keep same-millisecond repli..." · Reviewed by Greptile |
7e720d3 to
2603e11
Compare
2603e11 to
9fe004d
Compare
9fe004d to
0e7c8a4
Compare
0e7c8a4 to
054c278
Compare
054c278 to
80bc3a0
Compare
What Changed
Maya: [shared build-error.png].Why
"Screenshot, then @bot what's wrong here?" is a common way to ask. The bot got the question without the screenshot.
Before / after
The before and after columns are captured from the real Slack adapter and channel runtime, on #465 and on this branch.
Verification
Expected: "U1: [shared ctx.zip] [shared ctx.png]". They pass here.tsgoare clean for the server.Checklist
Written with Claude Opus 5.5 in Claude Code.