Repository navigation
OmaQ 1.2.0: queued direct messages, and replies and identity actions in omaq-connect - #64
Merged
Merged
Conversation
Since 1.1.0 a pane header shows the friend's presence dot, "❯ bob ● online", but cli-two-homes.py and cli-split.py still waited for "❯ bob online". They matched only during the short connecting state, so on main both failed once the header showed the dot. The patterns now accept the header with and without the dot, as cli-invite.py already does.
A direct message sent before the Ratchet session existed failed at once with ratchet_pending, and every such send asked for a new session. Each OQB2 request rotates the bootstrap prekey, so a burst of sends right after pairing could also delay the handshake. The helper now keeps such messages in a bounded queue (send_queue.c: 64 messages, 16 per contact), bound to the contact's stable key, the own identity and the IPC client that sent them. It asks for one session per waiting phase and sends the queue in order once the session exists. A message that still waits after 5 minutes fails with the new code ratchet_timeout, "No secure session yet; message not sent" in the bar and in omaq-connect. A contact that goes offline, is removed or changes key, an identity change and a helper shutdown fail queued messages at once. When the sending client disconnects, the helper drops its messages, because that client already shows them as delivery_unknown. Sent messages are never sent again, and a full queue still answers ratchet_pending. tests/phase8.sh sends two messages before the session exists and requires both in order without a failure; the 1.1.2 helper fails both with ratchet_pending. The PlainText gate pins the new ChatPage source hashes.
omaq-connect always sent an empty reply and ignored reply ids, edits and deletes. ctrl+r (or :reply) now marks the newest message that can be answered with an accent ▌; up/down, pgup/pgdn and home/end move the mark, the chat keeps it in view, and enter or r picks it. "↩ Reply: <preview>" above the input line shows the choice, and the next message carries its id. esc ends the choice, then the reply, then clears the draft. Pasted text and other keys end the choice and go into the draft, so a paste never picks a message. Only messages with a helper id can be answered: system lines, unsent, failed and local items, deleted messages, files and images are skipped, because file ids differ between the two sides. A reply starts with "↩ <preview>" in the accent color when the quoted message is loaded, as in the bar. compact_preview() ports the bar's compactReplyPreview and matched it on all 3005 texts of a one-off comparison. message.updated now changes a loaded message at once: an edit shows "(edited)", a delete "Message deleted", and history items carry the same flags. A reply to a message that gets deleted ends with a notice. cli-two-homes.py answers bob's message with ctrl+r and checks the reply id on bob's helper and the quote in the CLI.
omaq-connect could only unlock a protected identity. :identity now shows whether the identity has a passphrase, and :identity protect, unprotect, export [path], validate <path> and import <path> run the helper's identity ops with the bar's wording, one at a time, each with its own request id and the bar's 20 second timeout. Passphrases go into a masked line, never into the command line, drafts, recall or history; a new one is entered twice. Every change asks y/n right before it is sent, and the passphrase waits only inside that question. Export writes omaq-identity.save to $XDG_DOWNLOAD_DIR or ~/Downloads by default and asks before it replaces a file. A protected bundle asks for its passphrase, and import asks again before it sends it. tab after export, validate and import opens the :sendfile file list. The helper now refuses identity.protect while a passphrase is set, so no client can replace it without the current one; encryptsave.sh checks this. Passphrase lines, the unlock prompt included, accept a tab and refuse a pasted line break. In a terminal without bracketed paste, an enter, esc, ctrl+c or ctrl+d with more input in the same read refuses the line, and after a passphrase line closes the client discards input until the terminal is quiet for 0.3 s; an incomplete escape sequence or a running paste is then dropped whole. The key parser no longer flushes during a paste, so a split end marker still ends it, and a sequence broken by a byte that cannot continue it restarts parsing at that byte. IdentityActions in cli_unit_test.py has 28 tests, and cli-pty.py exports, validates, unprotects, protects and imports with the real helper.
While a passphrase line is open, an incomplete ESC [ sequence that stays
alone for 30 ms is kept, and what follows decides. A known key, a focus
report or the paste start counts as such, and a split cursor position,
mode or device report is dropped. Anything else counts as typed text
with the digits typed after the piece, so alt-[ no longer swallows the
next key. A lock from another client drops a started paste together with
the passphrase line it belonged to. A running paste no longer waits
30 ms at the drain.
The startup query now also asks whether bracketed paste is on. A
terminal that does not confirm it holds input after a passphrase line
until esc ("press esc to type again").
identity.export takes replace:false and then creates the file
exclusively. omaq-connect sends it unless the user agreed to replace the
file. If a file appeared in the meantime, it reports "<path> already
exists. Export again to replace it." An export to a new path needs a
helper whose snapshot announces identityExport 2.
Long questions and notices wrap onto up to three lines, and :help
indents subcommands under their command. When the helper refuses the
bar's Protect because a passphrase is already set, the bar now says so.
After an identity import or a guarded restore, the helper writes its new
instance to omaq.protocol. The client that started it and the
uninstaller therefore still recognize it, and :quit stops the helper
again. If that write fails, the helper keeps running. Once the change is
saved, it reports runtime_marker_failed, which the bar and omaq-connect
show, and it retries the file with every status.
cli_unit_test.py now has 161 tests. tests/identity-feedback.sh and
tests/runtime-marker.sh are new. phase5.sh, reinvite-recovery.sh,
cli-pty.py, ipc-regression.py and omaq_test.c cover the export flag, the
capability and the marker, and qml_plaintext_test.py carries the
reviewed Panel.qml hashes.
…connect The helper accepts message ids of up to 96 characters from a peer but reads the reply id of msg.send into an 80-byte field. A reply to a longer id made it reject the whole request with an error that names no request, and omaq-connect kept the message pending. Helpers create 32-character ids, so only a modified peer sends such ids. Only messages whose id fits in 79 bytes can now be answered, and a test ties REPLY_ID_MAX to the reply field in helper/json_io.h. A message deleted while omaq-connect had no link to the helper first shows as deleted in the history reload after the reconnect. That reload now ends a reply to it with "Reply canceled: the message was deleted", as a live delete does, and moves a choice resting on it to the newest message that can still be answered. phase8.sh now also fails when a queued message that arrived is reported as failed. cli_unit_test.py now has 163 tests.
The manifest moves to 1.2.0, still Protocol 16. CURRENT.md, the stage index and the terminal-client stage now name 1.2.0 for the terminal client's replies and identity actions, and the snapshot date moves to 2026-10-14.
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.
OmaQ 1.2.0 holds direct messages until the secure session exists, and adds replies, edits and deletes, and identity actions to omaq-connect. The protocol stays at 16; the helper's snapshot now also announces
identityExport: 2.Changes
ratchet_pending, and every such send asked for a new session. The helper now keeps these messages in a bounded queue (64 messages, 16 per contact), asks for one session per waiting phase and sends them in order once the session exists. A message still waiting after 5 minutes fails with the new coderatchet_timeout("No secure session yet; message not sent" in the bar and in omaq-connect). A contact that goes offline, is removed or changes key, an identity change and a helper shutdown fail queued messages at once; the messages of a client that disconnects are dropped.ctrl+ror:replymarks the newest message that can be answered, and the next message carries its id. Quotes look as in the bar.message.updatedshows edits and deletes at once, and a reply to a message that gets deleted ends with a notice, also when the delete first shows in a history reload. Only ids that fit the helper's 79-bytereplyfield can be answered.:identity protect,unprotect,export [path],validate <path>andimport <path>run the helper's identity ops with the bar's wording. Passphrases go into a masked line, never into the command line, drafts, recall or history, and every change asks y/n first. The helper now refusesidentity.protectwhile a passphrase is set.esc.identity.exporttakesreplace:falseand creates the file exclusively. After an identity import or a guarded restore, the helper writes its new instance toomaq.protocol, so:quitand the uninstaller still recognize it; if that write fails, it reportsruntime_marker_failedand retries.cli-two-homes.pyandcli-split.pyaccept the presence dot in the pane header; they failed on main since 1.1.0.docs/CURRENT.md, the stage index and the terminal-client stage.Verification
make test, including the terminal client's 163 unit tests, andtests/cli_arch_test.py../tests/phase8.sh: two messages sent before the session exists arrive in order without a failure. The 1.1.2 helper fails both withratchet_pending.tests/cli-pty.pywith the real helper: export (mode0600), validate, unprotect, protect and import; no passphrase appears in the output or in any file of the test's home.tests/cli-two-homes.pyover the Tox network, including actrl+rreply that bob's helper receives with its id.omarchy plugin validate ., shellcheck on all*.shandgit diff --check.ctrl+rreply arrived with its quote, and an edit and a delete showed on the other side;