Repository navigation
Agent-session tracking: single source of truth (no title/mtime heuristics) #6631
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6d67c0a
000cbec
edc04ec
775ce2f
1392e7e
324f085
6f2e0d8
ef03f5c
e6f4804
5d0d79e
4bcca3e
fbfc7da
4a1a8c8
bef654d
bd85a66
d8e980f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -38,6 +38,19 @@ public struct ChatSessionListReducer: Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var updated = sessions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let index = updated.firstIndex(where: { $0.id == descriptor.id }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Version-gated upsert: best-effort pushes can arrive out of | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // order, be duplicated, or race an authoritative pull. The host | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // stamps a strictly increasing `version` on every change, so a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // descriptor whose version is LOWER than the one already | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // applied is stale (or out of order) and must not clobber newer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // state the client got from a later push or a snapshot pull. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Equal version is allowed through (a no-op in practice: the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // monotonic counter guarantees equal version == identical | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // content), which also keeps unversioned (version 0) payloads | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // upserting as before. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| guard descriptor.version >= updated[index].version else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return sessions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+41
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Equal-version upserts can re-apply stale descriptor payloads after a newer state transition. Line 51 accepts 💡 Suggested fix if let index = updated.firstIndex(where: { $0.id == descriptor.id }) {
- guard descriptor.version >= updated[index].version else {
+ let currentVersion = updated[index].version
+ let incomingVersion = descriptor.version
+ let allowLegacyUnversionedEqual = (currentVersion == 0 && incomingVersion == 0)
+ guard incomingVersion > currentVersion || allowLegacyUnversionedEqual else {
return sessions
}
updated[index] = descriptor
} else {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updated[index] = descriptor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updated.append(descriptor) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| public import CmuxAgentChat | ||
|
|
||
| /// Result payload of `mobile.chat.session` (single-session snapshot pull). | ||
| public struct MobileChatSessionResponse: Sendable, Equatable, Codable { | ||
| /// The authoritative current descriptor for the requested session, | ||
| /// carrying the host's monotonic `version` for reconciliation. | ||
| public let session: ChatSessionDescriptor | ||
|
|
||
| /// Creates a response. | ||
| /// | ||
| /// - Parameter session: The session descriptor. | ||
| public init(session: ChatSessionDescriptor) { | ||
| self.session = session | ||
| } | ||
|
|
||
| private enum CodingKeys: String, CodingKey { | ||
| case session | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
❓ Verification inconclusive
🌐 Web query:
For the OpenAI Codex CLI version range cmux supports, when was --dangerously-bypass-hook-trust introduced, and does it bypass review for all enabled hook sources in an invocation or only hooks injected via command-line -c arguments?💡 Result:
The flag was introduced in OpenAI Codex via PR
#21768(May 8, 2026) and first stable in Codex 0.131.0 (after being merged May 13, 2026). [1][2] Effect: it is an invocation-scoped runtime-only override that bypasses the “persisted hook trust” requirement for enabled hooks for that specific Codex invocation, not just hooks injected via command-line -c arguments. Evidence: - PR#21768states the flag “sets a runtime-only bypass_hook_trust override” and is “one invocation-scoped escape hatch,” plus explicitly notes it affects “enabled hooks” being allowed to run without requiring persisted hook trust for that invocation. [1] - The OpenAI Developers “Hooks” docs describe that for “one-off automation… pass --dangerously-bypass-hook-trust to run enabled hooks without requiring persisted hook trust for that invocation.” This implies the behavior is based on which hooks are enabled, not whether they were provided via -c arguments. [3] - Additional Codex issue discussion confirms there are separate CLI/non-interactive vs TUI behaviors, but in the described working behavior the bypass suppresses the startup trust prompt and “Enabled hooks run without requiring per-session trust (still respecting explicit disabled hooks).” [2] So, for a given Codex invocation where the flag is actually honored (e.g., not lost across a TUI/app-server path), it bypasses review/trust gating for all hooks that are enabled in that invocation (subject to explicit disabled hooks), regardless of whether they were supplied through -c arguments or otherwise discovered/loaded. [1][3][2] Note: there were reported cases where TUI mode startup initially didn’t respect the flag (blocking on the interactive “Hooks need review” prompt), but that was later fixed to skip the prompt appropriately; also, persistent/resumed threads may keep review prompt when the bypass override can’t be safely applied. [2][4]Citations:
--dangerously-bypass-hook-trustis ignored in TUI mode (codex 0.131.0–0.133.0): "Hooks need review" prompt still blocks startup openai/codex#24093🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 198
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 857
🏁 Script executed:
# Read the CodexFireAndForgetHooks file to understand context cat -n ./CLI/CMUXCLI+CodexFireAndForgetHooks.swiftRepository: manaflow-ai/cmux
Length of output: 5363
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 500
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 226
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 5085
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 3107
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 4311
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 1492
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 962
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 6068
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 11116
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 5294
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
🏁 Script executed:
# Check if there are any guards or conditions around enabling the bypass ast-grep outline ./CLI/CMUXCLI+CodexFireAndForgetHooks.swiftRepository: manaflow-ai/cmux
Length of output: 355
🏁 Script executed:
Repository: manaflow-ai/cmux
Length of output: 154
Gate Codex hook-trust bypass by version and scope.
Line 32 unconditionally emits
--dangerously-bypass-hook-truston every Codex invocation. The flag (introduced in Codex v0.131.0, May 2026) bypasses hook review for all enabled hooks in that invocation—not just the cmux-injected ones via-carguments. Since Codex persists hooks in~/.codex/hooks.jsonand config in~/.codex/config.toml, any user or project hooks already installed there would bypass review too, even though cmux did not inject them. The design correctly states cmux should not clobber user Codex config, so a user with existing project/personal Codex hooks would have those hooks automatically authorized without their knowledge. Confirm that cmux-owned Codex wrapper injection scopes the bypass only to cmux-injected hooks, or gate the flag by minimum Codex version and fail closed if user hook sources are detected. Document the security boundary in the code.🤖 Prompt for AI Agents