Skip to content

feat(t3team): kill the inter-agent chatter loop + idle-aware wake policy - #202

Merged
johnnyelwailer merged 20 commits into
mainfrom
fix/chatter-interagent-vendor
Sep 11, 2026
Merged

johnnyelwailer merged 20 commits into
mainfrom
fix/chatter-interagent-vendor

Conversation

@johnnyelwailer

Copy link
Copy Markdown
Owner

Aligns all inter-agent prompt surfaces on one contract and changes the wake policy so coordinated child threads stop turning into a send/receive frenzy.

  1. Reaction-visibility: user-return instruction split by reason — strong answer-first rule for unanswered user messages; quiet rule for unreacted replies (no re-stating, no re-acknowledging, no status recap, one short line when nothing is new).
  2. Steering context: anti-ping line fires for every parented thread (idle gets the standing variant), not only while a human is steering.
  3. Child kickoff frame: "report progress and results" -> stay silent while working, report the final result exactly once.
  4. Wake policy: default coalescing debounce 2s -> 45s when idle (env override + 0 semantics unchanged), urgent entries bypass to immediate.
  5. Delivery tiers: first delivery full bodies; every later delivery header-only with t3team_read_message pointers; user-interjection compressed framing.

Verification: 75/75 across the six affected test files; tsc clean on touched files.

Philip Jonientz and others added 14 commits September 3, 2026 16:32
Extracts the launch -> settle -> resume -> fail funnel that the t3team server currently owns (controller skeleton, resume/fail closures, run registry, lifecycle interface) into @t3team-sdk so both the t3team server and external durable hosts (nexi-distribution portal, GHE #383) consume one implementation instead of re-mirroring it. No app code; broker, lifecycle, sinks and repair are injected.
createWorkflowRunController now builds the t3team broker/sinks/repair and delegates start/settle/resume/fail to @t3team/sdk createWorkflowRunHost; launchWorkflowRecipe maps the host start to the launch result. makeControllerResume/makeControllerFail (neutral duplication) deleted; race-boundary tests moved to the host level; GHE #403 retainPendingStep behavior carried into the host-fail sink.
… runsRoot

Host lifecycle interface drops broker-owned pending/sleep records (hosts call those through their own broker); recordFailed detail matches the durable row shape; controller passes the full drive handles into the repair funnel; race test supplies launchThreadId.
…ive-guard LOC ceiling

Split t3team-sdk.workflowHost.ts into workflowHostTypes.ts (the funnel's contract) + the two factories; split the server controller into controllerEnv.ts (broker + options plumbing) + the host binding. Verified against the additive guard with the CI sync point pinned: this branch adds zero violations beyond the fork-main baseline.
…ink-248-current' into integ/shared-workflow-host-289
…defaultRunsRoot

The 'retries a transient reply-journal failure without losing the suspended
ask' test's custom appendResolved read runsRoot from the appendReply opts,
but the resume funnel only passes {runId, correlationId, reply}. So the
retried append fell back to defaultRunsRoot() -- a different journal than the
suspended run's (runsRoot). The replay then couldn't find the reply and the
ask re-suspended, so onCompleted never fired (appendAttempts was 2).

Capture the run's journal root from the test's config (as the default
appendReply does from config.runOptions), so the retried write lands where
the run lives. The host retry logic was correct; the mock was unfaithful to
the host's appendResolved contract.
Three contradicting texts made coordinated child threads into a
send/receive frenzy; align all of them on one contract:

- reaction-visibility: split the user-return instruction by reason.
  Unanswered user message keeps the strong answer-first rule; an
  unreacted agent reply now gets the quiet rule - act only if the
  situation changed, no re-stating, no re-acknowledging standing
  instructions, no status recap, one short line when nothing is new.
- steering context: the anti-ping line now fires for EVERY parented
  thread, not only while a human is steering (idle gets the standing
  variant: one final report when done, hard blockers only).
- child kickoff frame: "report progress and results" -> stay silent
  while working, report the final result exactly once.
…aming

- Debounce default 2000ms -> 45s idle window (env override + 0=immediate
  semantics unchanged); urgent pending deliveries claim the batch at
  window 0 (bypass) via a per-thread urgent-pending mirror in the reactor
  (mailbox untouched: no peek API; batch cap + requeue-on-failure kept).
- Delivery tiers in startActorReaction: user interjection while queueing
  -> compressed pointer framing (user's message comes first); first
  inter-agent delivery -> full bodies; every later delivery -> header-only
  (sender, ~1-line summary, id, urgency) with t3team_read_message pointer.
- Restart-rehydrate prefix-matching now recognizes all three bases
  (full / header-single / compressed-single).
- Tests: 45s default window, urgent immediate claim, tier selection,
  rehydrate matching; existing clock-driven tests keep the 50ms pin
  re-pinned before each test.
Adds t3team_ask_user to the t3team MCP toolkit so any agent thread can ask
the user a structured question, suspend the turn, and resume with the answer
as the tool result — mirroring the provider adapters' AskUserQuestion flow
for harnesses whose model ships no native question tool.

- askUser.ts: appends a user-input.requested activity (same shape the
  provider runtime ingestion projects), suspends on the durable
  thread.user-input-response-requested event for its requestId, appends a
  user-input.resolved activity, and returns the submitted answers.
  Cancellation (turn interrupted or subscription ended without an answer)
  closes the question with a cancelled user-input.resolved activity under
  the same requestId so the pending panel clears.
- tools.ts: T3TeamAskUserTool (question/options/multiSelect/allowFreeText),
  canonical map entry t3team.thread.ask_user, and askUserDependencies
  (McpInvocationContext + OrchestrationEngineService + ambient Scope for
  the cancellation finalizer).
- handlers.ts: routes t3team_ask_user to t3TeamAskUser (not the broker
  callTool dispatch).
- catalog: ASK_USER_INPUT_SCHEMA + t3team.thread.ask_user entry
  (defaultEnabled).
- Tests: happy path, decoy-event filtering, multiSelect/allowFreeText
  mapping, empty-question rejection, cancellation on subscription end, and
  end-to-end wiring through T3TeamToolkitRegistrationLive.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Sep 11, 2026
pj and others added 6 commits September 11, 2026 13:02
- M1: first-delivery tier was unreachable - each delivery is persisted
  into the transcript before the drain claims the batch, so
  hasPriorInterAgentMessages now excludes the current batch's own
  message ids; regression test added.
- M2: userInterjectedDuringQueueing no longer counts inter-agent
  reaction inputs or automated senders as user interjections; a sibling
  burst can no longer compress its own batch.
- m1: claimed-then-requeued urgent entries re-note their urgent mirror
  (onRequeueUrgent on both dispatch paths, only when willRetry) so a
  failed dispatch does not downgrade them to the 45s idle window.
- m2: restart-rehydrate prefix matching now applies the identity guards
  to all three tier bases, not only the full-body one.
- m3: buildChildKickoffText gets first assertions.
- quiet rule gains an explicit blocker/failure carve-out.
- t3team_send_message gains an optional urgent flag: urgent deliveries
  bypass the recipients idle coalescing window and wake immediately.
  The tool description restricts it to hard blockers and unblocking
  questions; everything else coalesces into the idle digest.
- Child abnormal-stop notifications are marked urgent server-side: a
  child stopping unexpectedly is exactly what a parent must hear
  about without the 45s delay.
…ctions

The restart-hold summary path reads the steering context off the thread
(humanSteeringInstructionForThread -> findHandoffParentThreadId), so the
idleThread fixture needs messages/activities; without them the summary
turn defects out and the mailbox reacting flag sticks.
…hatter fix

Main now carries the evolved delivery tiers (buildActorReactionTurnInput,
modular reactor limits/events). Resolution: keep main's architecture and
re-apply the chatter fixes on top - 45s idle debounce + urgent wake-bypass
mirror (limits + reactor + events), M1 first-delivery exclusion and M2
interjection guards in the evolved reaction input, m1 onRequeueUrgent on
both dispatch paths, m2 rehydrate identity guards on all tier bases, the
UNANSWERED/QUIET instruction split with the blocker carve-out, the urgent
send_message producer, and the aligned test suites.
- t3team-actorReactionInput.ts: remove a dangling comment fragment left by
  the de-duplication that silently swallowed buildActorReactionInput into
  an unclosed block comment (ReferenceError at runtime).
- t3team-actorMessageReactor.test.ts: import the debounce constant +
  resolver from the limits module where main relocated them.
@johnnyelwailer
johnnyelwailer merged commit 775ab6d into main Sep 11, 2026
12 of 20 checks passed
johnnyelwailer added a commit that referenced this pull request Oct 12, 2026
…eting, linked repos) (#524)

* fix(web): desktop UX fixes (My Work drag, sign-in dialog, loading greeting, linked repos)

- My Work pane and startup gate get the Electron drag header (nx-nexi#199)
- Device-code account sign-in is a dialog, not a truncated sidebar button (#201)
- My Work loading screen: time-of-day greeting + vector animation, no logo (#202)
- Linked repositories: schemeless host/owner/repo gets https://, signed-in
  detection no longer fails on one stale host, Recheck re-probes, larger dialog (#203)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(web): sign-in code dialog spacing without restyling DialogPanel

DialogPanel owns its spacing (shadcn/no-restyle failed Lint). Move the gap onto
a plain wrapper, as the other dialogs do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Philip Jonientz <philip.jonientz@nexplore.ch>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant