Skip to content

feat: cross-conversation context for a seat, and collapse private asides - #2247

Open
sanil-23 wants to merge 273 commits into
tinyhumansai:mainfrom
sanil-23:feat/agent-cross-conversation-context
Open

feat: cross-conversation context for a seat, and collapse private asides#2247
sanil-23 wants to merge 273 commits into
tinyhumansai:mainfrom
sanil-23:feat/agent-cross-conversation-context

Conversation

@sanil-23

@sanil-23 sanil-23 commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Draft. Six commits, each readable on its own.

The vision this follows

An agent's context is a pure function of (turn, transcript) — rebuilt from
the log every turn, never accumulated in the agent. That is what lets an episode
replay byte-for-byte, and it is the property every change here is careful not to
spend.

Around it sits one invariant, which tinyhivemind states three times in three
mechanisms and which this PR states a fourth time: information crosses a
boundary, support never does.
An aside "carries information and never
support". A referral's answer "is not a vote, and it supports nothing here until
one of us spends a line on it". A digest "deposits information into every reader
and support into nobody's standings". The cross-conversation context added here
is the fourth instance — a seat may read and quote what it said elsewhere, and
nothing it reads there can move an option towards a decision here.

That invariant is why a desk stays a correlation boundary. Members of one
desk read the same transcript and are wrong about the same things; pooling two
desks that share an error imports it rather than cancelling it. The benchmark
measures the cliff — a bounded ask holds 95% at twenty-five desks and collapses
to 0.0% at a hundred once the peer pool shares the asking desk's blind spot.
Facts may cross. Conclusions may not.

What is done

commit
39fc6d78 hivemind — a seat reads the conversations it is part of elsewhere
67e3ec49 chat — fold a private aside onto the move it rode under
d59dc305 retail-co — enable private asides, seat triage on returns
a2a0387b retail-co — address desks as @#id, route on order status
a089fa64 console — name the responding teammate across a reload
3c854e2e chore — bump the tinyhivemind pin, drive a round of one

Cross-conversation context. Every other desk a seat sits on, plus its own
direct line, projected as the speaker and kept out of the fold. The desk it is
deliberating on is deliberately excluded — that comes from project_for,
because loading it in full would re-insert the peer rows a blind round withheld
while quorum kept counting as though independence held.

Aside folding. A private line reached the operator's transcript verbatim,
!aside @refunds and all, addressed to a room it was never addressed to. Now
lifted out and collapsed onto its author's move, rendered as
ReferralConversation's sibling. The collapse means something different from a
referral's, and the doc comment says so: a referral's rows are dropped
host-side, so expanding is the only way to read them; an aside is withheld from
agents and never from a person, so expanding reveals nothing the reader was
not already entitled to.

Desk addressing. Three silent failures — a bare @returns resolving to
nobody, two seats naming their own desk (a self-referral, refused without an
error), and the block promising a referral was a deliberation when it is one
turn.

The pin bump crosses ADR 0011: HiveStep::Speak carries a round rather
than a turn, and HiveTurn trades next_state for watermark + round_start.
The widths are pinned, not inheritedEpisodePolicy::DEFAULT carries
round_width = 4 upstream and this host spreads that default, so the bump would
otherwise have adopted four concurrent speakers in the opening blind round
without a line of code saying so, with spent advancing by the round's width. At
1 the round_start clause is inert and upstream states a round of one is
bit-identical, which makes the bump verifiable against a baseline rather than a
guess. A wider round is refused with an error naming round_width, because
next_state may only be adopted once every turn in the round has landed and
what a half-failed round does to the budget is not answered by the existing
failure cap.

No Cargo.lock churn — both locks unchanged, --locked passes, crates stay at
0.2.1.

Verified

  • 7670 lib tests green before the bump; the post-bump run must reproduce that number exactly, and is the bump's whole acceptance criterion
  • clippy clean, tsc --noEmit clean
  • retail task 0 DB PASS on the rebuilt binary — with asides off, with asides on, and with three seats
  • cross-conversation context observed live: @exchanges deliberating on Company Line, shown #returns (Returns and Exchanges) carrying the real task rows, its own line marked (you), sequence numbers preserved
  • no raw !aside in any live transcript after the fold

What is NOT verified

The aside chip has never been seen in a browser. The path is proven by
an_aside_reaches_the_operator_as_a_collapsed_conversation, which drives a real
episode and asserts the folded result — but no live run has produced an aside
since the renderer was written. Across eight attempts, including a reseeded
database and a task neither seat could finish alone, exactly one seat wrote an
aside, unbidden. Rooms coordinate in the open, which is what must_surface and
the prompt both push them towards. Treat the rendering as test-verified, not
eyeball-verified.

The frontend vitest suite has not run locally, including the two new tests
here. CI is the first thing to execute them.

For whoever picks this up

  1. Reseed before quoting any pass rate. tau2-up.sh wipes the rig home but
    not opencompany-tau2/.state/retail.json, so every run after the first scores
    against a database earlier runs mutated. This bit us mid-session: a room spent
    its whole budget on an order already stuck in exchange requested.
  2. tau2-up.sh can silently select the wrong binary. Two runs used a
    different target directory with OC_BIN and CARGO_TARGET_DIR both unset, and
    the resolution was not reproducible. Pass OC_BIN explicitly until it is.
  3. Your direct line (@id) is probably the wrong label. It reads as the
    agent's private line; it is specifically what people said to that agent.
    Agent-to-agent traffic never gets its own conversation.
  4. Decide whether triage stays on returns. Added to make asides likelier;
    it did not. It changes the bundle's shape — three seats against quorum = 2 on
    the same twelve-turn budget.
  5. The confirm episode still coins a topic from the word "yes" (#yes-i) and
    burns turns restating a settled decision. Pre-existing, visible in every run.
  6. Width 4 is the next measured change, and it needs the round-failure rule
    the bump refuses to guess at.

Summary by CodeRabbit

  • New Features

    • Added continuous agent sessions with a new Session tab, cross-channel history, raw-turn views, channel attribution, and private aside conversations.
    • Added optional speech tools for posting to desks, messaging teammates, closing conversations, and reading history.
    • Added Chargebee onboarding and migration offers to Invoicing.
    • Added durable per-company onboarding dismissal.
    • Improved inference provider and proxy status display.
  • Improvements

    • Desk handoffs now route to one designated teammate using explicit desk-address syntax.
    • Agent responses and working indicators more accurately identify the responding teammate.

senamakel and others added 16 commits September 11, 2026 00:37
Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…rc/onboarding/OnboardingGate.ts

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…t.ts

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…t.ts

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…rc/onboarding/OnboardingGate.ts

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update onboarding gate logic and state handling to keep the flow consistent.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Persist onboarding dismissal so skipped setup does not reappear in later tabs, while clearing stale dismissal after activation. Add Chargebee-specific pitch and credits messaging, hiding inactive invoice controls until a company is configured.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
A seat took its turn holding one conversation: the desk in front of it.
An agent seated on two desks, or holding its own direct line, answered on
one of them knowing nothing of the others — and re-seeded rather than
remembered, because `SharingState` is a single conversation and a single
watermark.

This gives every speaker its *other* conversations in its prompt: each
desk it also sits on, and its own direct line, each already projected for
it. Four constraints shape it, and each is load-bearing:

**Projected as the speaker, never as the operator.** `Viewer::Agent`, so
an aside it is not in arrives elided here exactly as it would at home.
Passing `Viewer::Operator` would hand a seat every private row in the
company.

**Out of the fold.** `step` still folds only the deliberating
conversation, so a row from elsewhere can be quoted and can never move an
option towards a decision. Information crosses a boundary; support does
not — the same bargain an aside strikes and a referral's answer strikes.

**The deliberating desk is excluded from the load.** It comes from
`project_for`, not from this. Loading it in full would re-insert the peer
rows a blind round withheld while quorum kept counting as though
independence held: the appearance of it with none of the property, which
is worse than not having it.

**Per conversation, never merged.** Two desks number their rows
independently, so a merged `[7]` would name two different messages.

`company_desks` is split out of `desk_federation` because that gates on
the home desk's referral opt-in, and whether a seat may *ask* another
desk a question is not the same permission as whether it can already
*read* a desk it sits on. Unset renders nothing, including no read of the
direct line, so every caller that existed before is byte-identical.
Add coverage for the finance company switching behavior to verify it works as expected.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
A seat writes its move and may add one `!aside @peer` line beneath it.
The host journals that line as its own row carrying an `audience`, and
the projection had no way to tell it from an ordinary reply — so it
reached the operator's transcript verbatim, `!aside @refunds` and all,
addressed to a room it was never addressed to.

Two defects in one row, and they need different fixes.

**The grammar leaked.** `MOVE_KINDS` deliberately omits `aside` — a
marker the fold discards is not a move anybody made — so `line_kind`
returns `None` and `readable_moves` leaves the head in the body. The
strip lives here instead, and takes every leading `@name` rather than the
first, since a desk may raise `max_members`.

**The row read as the room's own voice.** Aside rows are now lifted out
of the transcript and hung on the nearest preceding desk-visible row *by
the same author* — the move they rode under. By author rather than
adjacency: two seats asiding in one round would otherwise cross. An
orphan — no move above it, a page that begins mid-exchange — is kept as
an ordinary row, matching the rule the referral fold already states one
function below: a line in the wrong shape beats a line nobody can read.

The console renders it as `ReferralConversation`'s sibling, collapsed,
`aside @Refunds · 1 message`, dashed where a referral's border is solid.

**The collapse is tidiness, not access control**, and that is the one
thing not to copy from the referral. A referral's rows are dropped
host-side, so expanding is the only way to read them at all. An aside is
withheld from *agents* outside it and never from a person —
`Audience::admits` admits every operator unconditionally, because privacy
here is a deliberation device and not a security boundary. Expanding
reveals nothing the reader was not already entitled to.

`an_aside_reaches_the_operator_as_a_collapsed_conversation` covers the
seam: a real episode writes an aside, and the operator gets it folded.
Both halves were already tested — that asides are journaled with an
audience, and that the fold groups such rows — and neither noticed that
nothing joined them, which is exactly where the leak lived.
Two bundle changes, both made to exercise the aside path end to end.

**Asides on all three desks.** `AsidePolicy::DEFAULT` permits nothing —
deliberately, because the mechanism does not get the benefit of the
doubt — so a desk that wants one says so. Bounds are left at their
defaults: a pair (`max_members = 1`, excluding the author), two rows (a
question and an answer), and `must_surface`, which makes a pair deposit
one desk-visible line before opening another. Widening them would turn an
aside into what the spec calls a second desk with no quorum and no
record.

**`triage` joins `returns`.** Nine read tools and no mutating one, so it
is a checker rather than a third competing remedy. On two seats an aside
was the whole room minus one, and the elision stub had no reader to be a
stub for; with three, an outsider exists and the collapse means
something. `tau2-sim.py` moves in step because `--check` asserts desk
membership exactly.

Measured, and worth recording honestly: this did not make asides
frequent. Across seven runs of task 0 exactly one seat wrote an aside,
unbidden, and it was the narrow case the mechanism is for — a seat that
could swap items but held no refund tool asking the seat that did. Every
deliberate attempt failed, including a seat writing a dictated line that
was then discarded because addressing one agent is a responder turn and
not an episode. Rooms coordinate tool boundaries in the open, which is
what `must_surface` and the prompt both push them towards.
Add unit coverage for escaping an onboarding gate that becomes stuck, ensuring users can continue navigating when the gate cannot complete.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Every seat's hand-off block carried two defects that failed silently.

**The mention form was wrong.** The block said `@returns`, and a desk is
addressed as `@#returns` — the `@#` form resolves to a desk and cannot be
shadowed by a teammate who happens to share the name. A bare mention
resolves to nothing and reaches nobody, so the hand-off was dropped with
no error and the work never happened.

**Two seats named their own desk.** `exchanges` and `refunds` sit on
`returns` and were told to write `@returns`, copied from the `order_ops`
seats without changing the target. That is a self-desk referral, and
`delegates_to` authorises only `order_ops` in any case, so both were
refused — silently — and no case ever crossed from returns to order
operations.

**The block also misdescribed what a referral is.** It promised the far
desk's seats would deliberate the question between them and return what
the room settled on. A referral is at most one turn: one teammate on that
desk answers, and what comes back is one colleague's reading, on the
record, for the asker to weigh. It is information and not a vote — it
supports nothing here until a seat spends a line acting on it.

`triage` now routes on order status and nothing else: `@#order_ops` for
pending, `@#returns` for delivered.

The README's credential example gains the `models` map. The paragraph
directly beneath it already warned that an omitted `models` becomes an
empty map that shadows the bundle's own table, leaving the next turn
asking the provider for a model nobody chose — and the example above it
omitted exactly that.
The working indicator could name whoever the first live frame named, and
nothing else. A receipt is client state: a reload throws it away, and the
re-armed indicator had nothing left to say but a bare "Working…".

The host now records who it expects to answer on the open-turn row, and
serves it, so the reload leg has a name to show. It is optimistic exactly
as the host's own row is — the brain may still pick a different seat, and
the first frame of the resumed turn overwrites it. Absent from a host
that does not record it, which reads as it did before.

`mergeOpenTurns` folds a re-arm onto an existing row by spreading it, so
the key is omitted rather than set to `undefined` when the host named
nobody: a present-but-undefined key would erase a name the POST leg had
already put there, where an absent one leaves it standing.

Two tests cover it — that the indicator names the teammate, and that the
reload leg keeps the name the live leg found.
Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 8d95c4d7-8e4c-4937-842b-c0ceeb5a7947

📥 Commits

Reviewing files that changed from the base of the PR and between b8ffe78 and 671df0f.

📒 Files selected for processing (8)
  • companies/retail_co/company.toml
  • frontend/src/views/room/StepTimeline.tsx
  • frontend/src/views/team/AgentSession.tsx
  • frontend/test/e2e/agent-session.spec.ts
  • src/harness/speech_tools.rs
  • src/runtime/delegation.rs
  • tests/auth_matrix.rs
  • tests/snapshots/auth-matrix.txt
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/test/e2e/agent-session.spec.ts
  • companies/retail_co/company.toml

Included review availability: Your plan provides up to 2 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change adds continuous cross-channel agent sessions, speech tools, session inspection, private-aside rendering, inference-provider state reporting, durable onboarding dismissal, Chargebee invoicing surfaces, and updated retail desk handoff prompts.

Changes

Runtime sessions and speech

Layer / File(s) Summary
Session identity and configuration
src/session_key.rs, src/harness/built_in/*, src/company/types.rs
Agents receive company-scoped session keys. The manifest adds company-level speech configuration.
Cross-channel session processing
src/harness/built_in/agent_session.rs, src/hivemind/*, src/server/chat_history.rs
Journal rows are filtered, watermark-tracked, rendered as cues, and added to agent context. Private asides are folded into history views.
Speech tool execution
src/harness/speech_tools.rs, src/runtime/delegation.rs, src/harness/built_in/build.rs
Agents can post, DM, close conversations, and read bounded history through speech tools. Per-turn tracking prevents duplicate fallback replies.

Session API and console

Layer / File(s) Summary
Session and aside contracts
src/server/operator.rs, frontend/src/api/*
The host exposes agent-session rows, responder identities, cue text, session keys, and folded aside DTOs.
Session and raw-turn views
frontend/src/views/team/*, frontend/src/views/room/*
The console adds Session tabs, raw-turn views, DM raw-turn toggles, channel attribution, aside rendering, and named working indicators.
Session and UI validation
frontend/test/e2e/agent-session.spec.ts, frontend/test/unit/*
Tests cover session routing, raw modes, aside rendering, responder names, loading states, and stale responses.

Console configuration and finance

Layer / File(s) Summary
Inference provider state
src/company/inference.rs, src/server/ops/inference.rs, frontend/src/api/inference.ts, frontend/src/views/connections/*, frontend/test/*inference*
Selected provider identity is separated from normalized routing. Proxy state is reported explicitly.
Durable onboarding dismissal
frontend/src/onboarding/*, frontend/src/components/app-shell.tsx, frontend/test/unit/onboarding-*
The onboarding gate stores a company-scoped durable dismissal and clears it after activation.
Chargebee invoicing surfaces
frontend/src/views/finance/*, frontend/test/unit/finance-company-switch.test.ts
Unconfigured companies see a Chargebee pitch. Other health states can show a credits banner.

Retail configuration and documentation

Layer / File(s) Summary
Retail desk routing
companies/retail_co/agents/*, companies/retail_co/company.toml, companies/retail_co/README.md
Desk mentions use the @# form. Each handoff routes one teammate for one turn. Private asides are enabled on the three desks.
Runtime documentation and vendor alignment
docs/spec/*, vendor/openhuman
The specifications describe speech tools, continuous sessions, raw-turn inspection, and named sessions. The OpenHuman reference is updated.

Priority: ➖ Normal

Estimated code review effort: 5 (Critical) | ~90 minutes

Change: Feature · Severity of issue fixed: Medium

Suggested reviewers: senamakel, oxoxdev

Merge Risk: 🟡 Moderate · up to 671df

Several unresolved defects affect newly added session, speech, and inference flows, including duplicated DMs on retry, incomplete conversation history, and stale configuration or agent state. These should be addressed before merge unless explicitly accepted.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.11% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 201 functions across 53 files. (2 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the pull request's primary objectives: cross-conversation context for seats and collapsed private asides.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 77.11% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 201 functions across 53 files. (2 skipped: 2 unsupported.)


A rabbit hops through sessions bright
With speech tools tucked in moonlit light
Desks now answer one by one
Raw turns sparkle in the sun
Asides fold softly, neat and clear
Named sessions hop from ear to ear

Comment @coderabbitai help to get the list of available commands.

@sanil-23

Copy link
Copy Markdown
Collaborator Author

Final local run on the pushed tree:

test result: ok. 7670 passed; 0 failed; 13 ignored; 0 measured; 0 filtered out

Built with --features openhuman,hivemind,mcp. clippy clean, tsc --noEmit clean.

Not covered locally: the frontend vitest suite, including the two new tests in this PR — CI is the first thing to actually run those.

One late result worth recording, since it changes what the aside commit claims. I reseeded the tau2 database (an earlier run had left order #W2378156 stuck in exchange requested, which was silently invalidating attempts) and re-ran with a query neither seat can complete alone — an exchange plus an outright return, needing both exchange_delivered_order_items and return_delivered_order_items. That is the exact shape that produced the one organic aside earlier.

Still no aside. The room coordinated the tool boundary in the open:

exchanges: the keyboard part is mine — exchange 1151293680 → 7706410293 — but the thermostat is refunds'

So that is eight attempts, one aside, unbidden. I read this as the mechanism behaving as designed rather than as a gap: must_surface means anything private has to come back to the desk anyway, and the prompt tells every seat that "an aside you never surface bought the room nothing". A seat whose question its teammate can answer has every reason to just ask on the desk.

The consequence for review: the collapsed-aside rendering is test-verified and not eyeball-verified, and the odds of it being exercised by an ordinary run are low. Reviewers should lean on an_aside_reaches_the_operator_as_a_collapsed_conversation rather than expecting to see it in a preview.

senamakel and others added 12 commits September 11, 2026 02:11
An artifact of a local screenshot run that the auto-commit hook picked up; it
was never part of any change here.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
Brings the inference managed-provider read-back fix, the Chargebee migration
pitch on Finance -> Invoicing, and the durable onboarding-gate dismissal onto
PR tinyhumansai#2247's cross-conversation-context branch, so both can be worked on together.

No file is touched by both sides.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a built-in agent session for the harness so agent runs can be
driven through the standard built-in interface.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Switched the agent session to the shared GENERAL_DESK constant and the EventSeq::new constructor instead of local equivalents, and widened operator_label to pub(super) so it can be reused. No behaviour change.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add the agent_session module to the built-in harness so its watermark and
channel identity cue block are available to callers.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…aring

The agent now keeps one continuous session across every chat it serves,
receiving only the rows it has not yet seen, each cued with its origin
channel, rather than clearing its history and re-seeding whenever the
chat changes. Audience narrowing still applies, so private exchanges the
agent is not party to remain out of reach, but channel isolation is
deliberately dropped so the agent can carry context between

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a session field to CompanyAgent holding the agent session state, initialized at each construction site. The watermark is kept beside bound_chat rather than inside it so it survives a channel switch instead of being cleared and reseeded per chat.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
ChatSeedRequest now exposes session_delta, which walks the journal for rows
the agent has not yet been handed across every channel it can read, returning
None when the company record is unreadable so the caller leaves the session
untouched. render_cues no longer takes an unused agent_id.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Inbound turns now carry their session cue block ahead of the message text,
matching the reference implementation where inbound is a cued turn rather
than a synthesised tool result. The text is borrowed when no cues are
present, so ordinary same-channel replies pay nothing for the change.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Expose desk_post, desk_dm, desk_close and desk_read as tool calls whose names, argument shapes and descriptions come from tinyhivemind::speech. The module stays off unless the manifest enables [speech].

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper tinysweeper Bot added the priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. label Sep 11, 2026

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

             $0.0577 · 339,315 in / 8,166 out · 113,932 cached (34%) · openrouter/openai/text-embedding-3-small, deepseek/deepseek-v4-flash, z-ai/glm-5.2 · 769 embedded
critique:    $0.0012 · 13,797 in  / 84 out    · 0 cached (0%)        · deepseek/deepseek-v4-flash
security:    $0.0012 · 13,776 in  / 112 out   · 0 cached (0%)        · deepseek/deepseek-v4-flash
tests:       $0.0136 · 156,801 in / 134 out   · 0 cached (0%)        · deepseek/deepseek-v4-flash
description: $0.0417 · 154,941 in / 7,836 out · 113,932 cached (74%) · z-ai/glm-5.2

@tinysweeper tinysweeper Bot removed the priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. label Sep 11, 2026
Updated the end-to-end inference test to match the updated API response format. The previous test expected a field that was removed during a backend refactor, causing the test to fail against the current service implementation.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

             $0.0748 · 344,138 in / 11,795 out · 273,982 cached (80%) · openrouter/openai/text-embedding-3-small, deepseek/deepseek-v4-flash, z-ai/glm-5.2 · 769 embedded
critique:    $0.0003 · 13,797 in  / 88 out     · 13,568 cached (98%)  · deepseek/deepseek-v4-flash
security:    $0.0038 · 13,412 in  / 953 out    · 10,449 cached (78%)  · z-ai/glm-5.2
tests:       $0.0394 · 161,988 in / 5,296 out  · 118,715 cached (73%) · z-ai/glm-5.2
description: $0.0313 · 154,941 in / 5,458 out  · 131,250 cached (85%) · z-ai/glm-5.2

Comment thread src/harness/speech_tools.rs
senamakel and others added 4 commits September 11, 2026 21:12
The file previously lacked a trailing newline, which can cause issues with some text processing tools and version control systems. Adding the newline ensures the file conforms to POSIX standards and avoids potential merge conflicts or display artifacts.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The speech tools module now returns an empty result instead of panicking when given an empty input string, ensuring robust handling of edge cases in test harness speech processing.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When an episode record lacks metadata, the system now returns a default empty metadata object instead of failing with a null pointer error. This ensures that downstream consumers can always access metadata fields without additional null checks.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5956051be9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness/speech_tools.rs Outdated
// watermark exactly so an unseen row on some OTHER channel
// does not silently become "already delivered" underneath it;
// see its doc comment.
*session = session.reseeded(chat.message_seq);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record every row loaded by a session re-seed

When a chat-only or oversized-gap re-seed loads recent history from the incoming channel, this records only the current message. For example, with watermark 5 and incoming-channel rows 6–10, the seed shows rows 6–9, but the next substantive turn still treats them as unseen and cues them into the already-seeded history a second time. Carry the seeded row sequences out of ChatSeedRequest::build and accept exactly those rows as well.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed as a real gap, pre-existing and not touched by me in this session — deferring alongside the sibling watermark findings already tracked as follow-ups (PRRT_kwDOTU5giM6hge3Z, PRRT_kwDOTU5giM6hgxH2). ChatSeedRequest::build only records the current message's sequence into the reseeded watermark, not the full range of rows the seed actually loaded, so a re-seed's own visible window (e.g. rows 6-9 with the current message at 10) is re-cued as unseen on the very next delta. A correct fix means carrying the seeded row sequences out of build and accepting all of them, which touches the same core watermarking path as the other deferred findings — grouping these for one dedicated follow-up pass rather than patching each individually under this PR.

Comment on lines +113 to +115
let mut next = Self {
watermark: self.watermark,
present_above_watermark: BTreeSet::new(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve previously accepted sparse rows during re-seeds

If an unseen row pins the watermark while a later chat-only turn has already put its own sequence in present_above_watermark, the next re-seed discards that accepted sequence here. A later delta then delivers the previously seen turn again; for example, watermark 5, unseen row 6, accepted greeting 7, followed by another re-seed at 8 causes row 7 to become unseen. Initialize this set from the prior state rather than emptying it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed as a real gap, pre-existing and not touched by me in this session — deferring alongside the sibling watermark findings (see PRRT_kwDOTU5giM6hibPL). AgentSessionState::reseeded starts present_above_watermark from an empty BTreeSet rather than the prior state's, so a sparse accepted row above the watermark (an already-seen greeting that arrived before an older unseen row completed) is dropped on the next reseed and can be redelivered. Grouping with the other watermark-correctness findings for one dedicated follow-up pass.

Comment thread src/hivemind/episode.rs

drop_dead_cards(runtime, &mut messages).await?;
attach_referral_origins(runtime, desk_id, &mut messages).await?;
fold_asides(&mut messages);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bypass aside folding for the raw session projection

Fresh evidence after the cueText fix: history_for_desk still runs fold_asides before the session DTO is built, which removes each aside's journal row and strips its !aside @… prefix into an AsideConversation attached to another row. Consequently Raw Turns cannot display that row's own sequence, timestamp, channel, or literal text despite documenting one unrendered block per journal row. The raw endpoint needs a projection that bypasses this chat-only fold.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed as a genuine sharper angle on the same underlying gap already tracked as a follow-up (PRRT_kwDOTU5giM6heIqv) — history_for_desk's fold_asides step removes an aside's own row entirely before the session DTO is built, so the raw-turn surface can never show that row's own sequence/timestamp/channel/literal text even with the cueText fix in place, contradicting the 'one unrendered block per journal row' documentation. Same reasoning as the original finding: fixing history_for_desk's fold/pagination interaction correctly is a cursor-contract change that deserves its own boundary tests, not a patch alongside the other fixes on this head. Tracking as part of that same follow-up rather than resolving separately.

senamakel and others added 5 commits September 11, 2026 21:20
The closure passed to the `.chain()` method was formatted on a single line, exceeding the project's line length limit. It has been broken across multiple lines to improve readability and comply with formatting conventions.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The speech tool output was previously being discarded due to an incorrect
condition that filtered out valid results. This change corrects the logic
so that generated speech is properly returned to the caller, ensuring the
harness delivers the expected audio output.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Previously, the speech synthesis module incorrectly aligned phoneme timestamps for utterances containing certain edge case patterns. This fix ensures that the alignment algorithm properly handles these patterns, producing accurate and consistent timing in the generated speech output.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…nction

Replace three identical inline pattern matches that extract text from tool results with calls to the existing `tool_result_text` helper. This removes duplicated error handling and makes the test code more maintainable.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The speech tool harness was silently dropping output from the speech synthesis pipeline, causing tests to pass without verifying actual audio generation. This change re-enables output capture so the harness can properly assert on synthesized speech content.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper tinysweeper Bot added priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. and removed priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. labels Sep 11, 2026
senamakel and others added 2 commits September 11, 2026 21:37
Prevents a panic when the speech synthesis function receives an empty string by returning an early error instead of attempting to process the input. This ensures robust behavior for edge cases where no text is provided.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The episode metadata fields were inadvertently dropped during a previous refactor, causing downstream consumers to lose access to episode-level information. This change restores the metadata struct and its associated accessor methods, ensuring the episode API remains backward compatible.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 42cfacbe6e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness/built_in/agent_session.rs Outdated
Comment thread src/harness/speech_tools.rs Outdated
senamakel and others added 2 commits September 11, 2026 21:41
The speech tool output was previously being discarded, which prevented downstream components from receiving the generated speech data. This change re-enables the output propagation so that speech results flow correctly through the harness pipeline.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat the `collides_with_desk` boolean expression to break the chained iterator calls across multiple lines, improving code readability without changing any behaviour.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e8cb17e226

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +117 to +118
if let Some(seq) = current_message {
next.accept(seq);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not initialize cold sessions at the current sequence

Fresh evidence beyond the earlier chat-only reseed case is a true cold start: after a process restart or session reset, self.watermark is None, so accepting the current message makes its sequence the watermark even though ChatSeedRequest::build seeded only the incoming channel. Every older row on other readable channels—and older rows outside that channel's seed window—is then permanently classified as seen, so cross-conversation history cannot survive a restart. Seed all readable channels before advancing this boundary, or initialize it below the oldest unseeded row.

Useful? React with 👍 / 👎.

Comment on lines +4719 to +4722
if let Ok(turn) = outcome.as_mut() {
let said = speech.utterances();
if !said.is_empty() {
turn.reply = said.join("\n\n");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist collected speech when the turn later fails

When an agent successfully calls ambient desk_post or desk_close and a later provider iteration returns an error, the tool's text exists only in TurnSpeech: this branch reads it exclusively for an Ok outcome, while post_to_channel has deliberately skipped the journal append. The tool already reported that the message was said, but a timeout or provider error after the call silently discards it; flush collected utterances to the channel on the error path as well.

Useful? React with 👍 / 👎.

Comment on lines +459 to +460
if collides_with_desk {
format!("{}{peer}", crate::runtime::assignee::DM_PREFIX)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Ensure the prefixed DM key cannot match a desk alias

Fresh evidence beyond the earlier display-name collision fix is that only the bare peer is checked: the replacement key dm:<peer> is never checked against desk display names, which are unrestricted and need not be unique. With agent support, one desk named support, and another desk named dm:support, this branch selects dm:support, and chat_history::owns matches that second desk's name, exposing the private row to all of its members. The final journal key must be guaranteed outside every desk id/name alias, not merely different from aliases of the bare id.

Useful? React with 👍 / 👎.

senamakel and others added 3 commits September 11, 2026 21:55
Introduce a built-in agent session for the harness so sessions can be
driven directly from the runtime. This provides the foundation for
running agent interactions without an external driver.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a built-in agent session for the harness so sessions can be
driven directly from the runtime. This provides the foundation for
running agent interactions without an external driver.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The speech tool output capture was previously removed, which broke the ability to verify spoken responses in tests. This change restores the capture mechanism so that speech output can be properly asserted against expected values.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper tinysweeper Bot added priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. and removed priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. labels Sep 11, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f7cd1438e9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/hivemind/episode.rs
Comment on lines +1002 to +1006
.chain(std::iter::once((
agent_id.to_string(),
agent_id.to_string(),
direct_line_label.clone(),
)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude desk-colliding keys from direct-line reads

When an agent ID also identifies a desk the agent does not sit on, this unconditional bare-ID target makes EventLogSessionLog::addresses_desk accept that desk's rows as the agent's direct line. The membership filter above applies only to context_desks, and querying the prefixed spelling afterward does not remove the leaked rows, so a hive prompt can expose another desk's conversation to a non-member. Omit the bare target when it collides with a real desk, using the same collision decision as the DM writer.

Useful? React with 👍 / 👎.

Comment thread src/hivemind/episode.rs
Comment on lines +998 to +1000
.chain(
(self.desk.id != general)
.then(|| (general.clone(), general.clone(), general_label)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat the configured General claimant as the home desk

When [company].general_desk routes General to a hive desk such as company_line in retail_co, self.desk.id is the claimant ID rather than the literal General, so this condition always adds the synthetic General conversation as “elsewhere.” The triggering operator row remains journaled under a General spelling such as main, causing the current request and prior traffic from the same logical company line to be duplicated and explicitly labeled as not belonging to the floor. Suppress this target when the current desk is the configured General claimant.

Useful? React with 👍 / 👎.

The build script was previously removed but is needed again to generate the test harness for built-in tests. This restores the functionality so the build process can properly compile and link the test binaries.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants