Cloud multi-agent review of the three follow-up commits surfaced 15
verified defects; this addresses them.
Security / correctness:
- output_guard was blind to the new sender-label trust marker: add
fence.SENDER_LABEL_TAG to the forgery/leak detector and thread a
second trusted nonce (trusted_sender_label_nonce) through
evaluate_output/_check_marker_forgery so a forged or leaked
sender-label block in tool output is flagged like an operator marker.
- Attachment-derived text (PDF extraction, audio transcript, perception
output) bypassed sender-label neutralization because it materializes
after _inject_sender_labels runs; neutralize it at each fallback site.
- _recompute_shared_state now runs on every compose (moved out of the
non-creative branch) so a creative-mode resume can't leave shared
state stale.
- /new and rewind/retry now reset shared state (were leaking the prior
conversation's participant set / keeping a workstream latched 'shared'
after its only second-participant evidence was deleted).
- Non-fork resume and /new remint both trust nonces; carrying a nonce
across a workstream switch would let a token leaked in one forge a
marker in another.
- _senders_dirty is only cleared once the persisted-sender read has
actually landed, so a transient storage error retries within the turn.
- ws_id snapshot guard in _recompute_shared_state discards a result if
resume() swapped workstreams mid-scan (MCP-callback race).
- recall/history search is scoped to the acting sender's visibility;
the shared-workstream declaration now names that exception so the
model doesn't read a filtered 'no results' as 'no record exists'.
Cleanup:
- fork skips the redundant persisted-sender read (its rows were just
bulk-written); _maybe_note_new_participant goes through the single
recompute entrypoint; senders_from_user_meta reuses _source_meta_from_json.
- fence.wrap docstring names the sender-label caller as a third
untrusted-host boundary.
Tests: end-to-end compaction-narrowed resume recovery, hostile
display-name fence break-out, sender-label output-guard leak/forgery,
and the two-nonce independence.
- q-2: document the deliberate username-first display-name precedence in
_resolve_display_name (diverges from auth.py's display_name-first
because sender labels must match the owner-banner identity kind).
- q-3: drop change-lineage comments referencing the separate acting-user
credential fix (tombstone noise once merged).
- q-4: tighten the plain-text attachment assertion from a tolerant
subset check to exact shape + _sender value now that the stamp is
deterministic.
- q-5: drop the contributor-local bare 'etc/' from .gitignore.
sec-1: the [message from <sender>] label was plain text, so a participant
could type a look-alike in their own message and impersonate another
sender to the model. Labels are now wrapped in a nonce-delimited
[start sender-label_<nonce>] ... [end sender-label_<nonce>] fence (new
fence.SENDER_LABEL_TAG, distinct value from the operator nonce) whose
token lives in the cached system prefix; participant content is
neutralized so typed look-alikes are defanged. A new
build_shared_workstream_declaration pins the token as the sole authentic
label.
sec-2 + q-1: the CONTEXT banner no longer embeds behavioral prose. It
carries a terse owner line + shared flag; the attribution rules, the
authenticity declaration, and the (now narrowed) tool-credential claim
move into the shared-workstream declaration. The credential claim is
corrected: per-participant credentials apply to MCP (OAuth) tools only;
built-in tools and skills run under the server/owner identity.
perf-3: _inject_sender_labels resolves each distinct sender's display
name once per call instead of once per turn, capping blocking storage
lookups at one per sender on the uncached error path.
- _known_senders/_shared_workstream are now monotonic: union-only growth,
latched shared flag, seeded once per workstream from a full-history
distinct-sender read (new StorageBackend.list_message_senders) so
compaction narrowing the resumable slice can no longer forget
participants (duplicate join notes) or flip the banner back to
single-user framing (prompt-prefix cache churn).
- Recompute is memoized per turn (invalidated on stamped user-turn
append); system-prompt composition no longer pays an O(n) trajectory
scan on every recompose.
- resume() resets the state: the monotonic guarantees are per
workstream, not per session object.
- resume(fork=True) bulk-persist now carries the user-turn sender stamp
into the fork's meta column (was: _source_meta only, which dropped
attribution for every forked user turn on reopen).
* multiuser chat fixes for identity clarity and obo oauth token selection during tool calls
* added some missing context to the session so that the llm would know what session/project to reference in tool calls
* updated to address copilots issues and excluded a local config folder
* I think this resolves the cicd failures
---------
Co-authored-by: pow3rtool <root@pow3rtools>