Skip to content

feat: add native T3 runtime adapter - #2

Merged
EtanHey merged 4 commits into
mainfrom
t3layer/p2-native-adapter
Jul 31, 2026
Merged

feat: add native T3 runtime adapter#2
EtanHey merged 4 commits into
mainfrom
t3layer/p2-native-adapter

Conversation

@EtanHey

@EtanHey EtanHey commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Outcome

Adds the production NativeRuntime adapter over the immutable T3 Code runtime-client artifact and aligns the facade contract with native runtime modes/options.

Scope

  • pin verified runtime-client-v0.0.31-rpc.2 release artifact
  • create scoped Effect RPC sessions with one-use socket acquisition
  • map project/create/spawn/send to canonical orchestration commands
  • reconcile shell and detail subscriptions at proven monotonic watermarks
  • replay-align independently synchronized streams without suppressing same-sequence detail updates
  • sanitize adapter failures and deterministically close iterators/sessions

P3 identity/hierarchy, P4 lifecycle RPCs, P5 recovery, P6 MCP, and P7 UI remain out of scope.

Verification

  • Bun 1.3.11 frozen install
  • 115 tests passed, 0 failed, 271 expectations
  • tsc --noEmit
  • Prettier 3.8.1 on changed parseable files
  • git diff --check
  • Gitleaks over the complete changed diff
  • independent adversarial review: SHIP_SLICE

Next gate

After merge, run the live programmatic spawn → wait → send → wait proof against the connected T3 Code Alpha environment.


Note

High Risk
Large new real-time orchestration layer (auth, WebSocket sessions, stream reconciliation) plus a breaking createT3Facade API change; incorrect watermark logic could yield stale or missing thread updates.

Overview
Introduces a production NativeRuntime implementation that talks to T3 Code through the pinned @t3tools/runtime-client artifact: scoped WebSocket RPC sessions (one-use socket URLs), orchestration command mapping for projects/spawn/send, and reconcileThread to merge shell vs detail subscriptions into monotonic NativeThreadObservation snapshots (catch-up, pending-only shell advances, resume boundaries, timeouts, sanitized errors).

Facade/config alignment: experiment config now requires interactionMode: "default"; createT3Facade must receive runtimeMode / interactionMode (no default options) and send forwards those on startTurn. ModelSelection.options is optional and may use boolean values; evidence uses optionCount only.

Adds effect dependency and broad contract + adapter test coverage (~115 tests).

Reviewed by Cursor Bugbot for commit a74a0e7. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Added a native runtime adapter for managing projects, threads, commands, subscriptions, and real-time updates.
    • Added runtime and interaction mode configuration, with "default" supported for interaction mode.
    • Added support for optional model settings, including boolean values.
    • Included runtime and interaction modes in command dispatch and activity evidence.
  • Bug Fixes

    • Improved handling of missing model settings, missed updates, connection issues, and session cleanup.

Note

Add native T3 runtime adapter backed by WebSocket session factory

  • Introduces createT3NativeRuntime — a concrete NativeRuntime implementation that dispatches project/thread/turn commands over a WebSocket-backed RPC session, with connection and alignment timeouts and structured error codes via NativeRuntimeAdapterError.
  • Adds createDefaultSessionFactory with lazy factory loading, readiness waiting, scope management, and best-effort cleanup.
  • Stream reconciliation via reconcileThread coordinates shell and detail projections to yield consistent NativeThreadSnapshot values, with skew tolerance and deletion handling.
  • Updates createT3Facade to require runtimeMode and interactionMode at construction time and forwards both fields in startTurn dispatches and evidence records.
  • Extends createConfig to require and validate an interactionMode: "default" field on ExperimentConfig.
  • Risk: createT3Facade options are no longer optional — callers that relied on the default empty object will need to provide runtimeMode and interactionMode.

Macroscope summarized a74a0e7.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds required interaction-mode configuration, typed facade dispatch fields, optional model options, and a native runtime adapter. The adapter manages RPC sessions, command projection, project and thread operations, and synchronized shell/detail stream reconciliation.

Changes

Runtime integration

Layer / File(s) Summary
Configuration and facade contracts
package.json, src/config.ts, src/facade.ts, test/config.test.ts, test/facade.contract.test.ts
The configuration requires interactionMode: "default". The facade uses typed runtime and interaction modes. Model options are optional and support boolean values. Dispatch and evidence include the configured modes.
Session and command adapter
src/nativeRuntime.ts, test/native-runtime-adapter.test.ts
The native runtime opens scoped WebSocket sessions, validates inputs, projects commands, maps errors, lists projects, retrieves threads, and starts resumable subscriptions.
Shell and detail stream reconciliation
src/nativeRuntime.ts, test/native-runtime-adapter.test.ts
The adapter aligns shell and detail streams by sequence, applies snapshots and events, handles missing or deleted threads, emits native snapshots, and closes iterators and sessions.
Facade dispatch and evidence coverage
test/facade.send.test.ts, test/facade.spawn.test.ts, test/facade.wait.test.ts
Facade tests now pass explicit dispatch modes and verify mode fields, optional and boolean model options, evidence values, retries, reconciliation, spawn, and wait behavior.

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

Sequence Diagram(s)

sequenceDiagram
  participant NativeRuntime
  participant RuntimeClientSession
  participant OrchestrationWebSocket
  participant ShellStream
  participant DetailStream
  NativeRuntime->>RuntimeClientSession: open authorized session
  RuntimeClientSession->>OrchestrationWebSocket: acquire socket URL and connect
  NativeRuntime->>RuntimeClientSession: dispatch projected command
  RuntimeClientSession->>OrchestrationWebSocket: send RPC command
  NativeRuntime->>ShellStream: subscribe to shell stream
  NativeRuntime->>DetailStream: subscribe to detail stream
  ShellStream-->>NativeRuntime: return sequenced shell state
  DetailStream-->>NativeRuntime: return sequenced detail state
  NativeRuntime->>NativeRuntime: reconcile streams by sequence
  NativeRuntime-->>NativeRuntime: emit native thread snapshot
Loading

Possibly related PRs

  • EtanHey/t3layer#1: Introduces the T3 facade that this change extends and connects to the native runtime adapter.

Poem

A rabbit hops through streams aligned,
Shell and detail states now combined.
Modes march in, commands take flight,
Evidence records each step just right.
RPC doors close when work is done—
Reconciled snapshots greet the sun.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a native T3 runtime adapter.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3layer/p2-native-adapter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ee87e68. Configure here.

Comment thread src/nativeRuntime.ts
throw new NativeRuntimeAdapterError("projection_invalid");
}
detail = alignedDetail;
shell = alignedShell;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Aligned replay skips shell guard

Medium Severity

After alignInitialVersions, the first reconciled emission uses alignedInitialSequence with latestVersionAt detail/shell pairing but never runs shellCanAdvanceDetail or the pending-only shell-ahead checks used elsewhere. catchUpDetailThrough also marks detail validated through targetSequence when the stream hits synchronized, even if detail versions never reached that sequence, so stale detail can be published under a newer shell watermark.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ee87e68. Configure here.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

WAIVED — verified false positive. The fresh lagging-stream subscription replays from the explicit afterSequence and its per-stream completion marker proves there is no omitted detail event through the target. The exact shell-first detail-only AB regression and initial replay regression pass; an independent adversarial reviewer revalidated this after the timeout hardening.

Comment thread src/nativeRuntime.ts
return false;
}

async function catchUpDetailThrough(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High src/nativeRuntime.ts:466

catchUpDetailThrough discards snapshot sequences from the detail replay stream, so alignInitialVersions does not advance targetSequence when the detail catch-up returns a snapshot newer than the current target. When the detail stream replays a snapshot at sequence 20 while the target is 11, the function returns { deleted: false } without reporting sequence 20, so the caller marks detail validated only through 11 and emits a snapshot at 11 instead of 20. The analogous catchUpShellThrough returns the latest snapshotSequence for exactly this reason, but catchUpDetailThrough does not propagate it. Consider returning the latest snapshot sequence from catchUpDetailThrough and updating targetSequence in alignInitialVersions when the detail catch-up returns a newer sequence.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/nativeRuntime.ts around line 466:

`catchUpDetailThrough` discards snapshot sequences from the detail replay stream, so `alignInitialVersions` does not advance `targetSequence` when the detail catch-up returns a snapshot newer than the current target. When the detail stream replays a snapshot at sequence 20 while the target is 11, the function returns `{ deleted: false }` without reporting sequence 20, so the caller marks detail validated only through 11 and emits a snapshot at 11 instead of 20. The analogous `catchUpShellThrough` returns the latest `snapshotSequence` for exactly this reason, but `catchUpDetailThrough` does not propagate it. Consider returning the latest snapshot sequence from `catchUpDetailThrough` and updating `targetSequence` in `alignInitialVersions` when the detail catch-up returns a newer sequence.

Comment thread src/nativeRuntime.ts
Comment on lines +666 to +668
if (next.result.done) {
detailDone = true;
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High src/nativeRuntime.ts:666

When a WebSocket stream closes cleanly before its synchronization marker arrives, reconcileThread silently exits and getThread returns undefined (or subscribeThread ends) as though the thread does not exist, instead of surfacing a transport_unavailable error. Additionally, if one stream completes after synchronizing while the other remains open, the loop continues indefinitely waiting for updates that can never arrive, and may keep emitting pending-only shell updates using stale detail.

Both detailDone and shellDone are set without verifying that synchronization occurred. The guard at line 692 throws when detailDone && !detailSynchronized, but only inside a shellSynchronized block, so a detail stream that closes before synchronizing is not checked when the shell stream is also unsynchronized. Likewise, shellDone without shellSynchronized is never checked. This diverges from readShellSnapshot and the catch-up helpers, which treat premature iterator completion as a transport failure. Consider throwing transport_unavailable whenever either iterator returns done before its corresponding synchronized flag is set.

-        if (next.result.done) {
-          detailDone = true;
-        } else {
+        if (next.result.done) {
+          detailDone = true;
+          if (!detailSynchronized && detailFailure === undefined) {
+            throw new NativeRuntimeAdapterError("transport_unavailable");
+          }
+        } else {
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/nativeRuntime.ts around lines 666-668:

When a WebSocket stream closes cleanly before its synchronization marker arrives, `reconcileThread` silently exits and `getThread` returns `undefined` (or `subscribeThread` ends) as though the thread does not exist, instead of surfacing a `transport_unavailable` error. Additionally, if one stream completes after synchronizing while the other remains open, the loop continues indefinitely waiting for updates that can never arrive, and may keep emitting pending-only shell updates using stale detail.

Both `detailDone` and `shellDone` are set without verifying that synchronization occurred. The guard at line 692 throws when `detailDone && !detailSynchronized`, but only inside a `shellSynchronized` block, so a detail stream that closes before synchronizing is not checked when the shell stream is also unsynchronized. Likewise, `shellDone` without `shellSynchronized` is never checked. This diverges from `readShellSnapshot` and the catch-up helpers, which treat premature iterator completion as a transport failure. Consider throwing `transport_unavailable` whenever either iterator returns `done` before its corresponding `synchronized` flag is set.

@EtanHey

EtanHey commented Jul 31, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/facade.ts`:
- Around line 97-99: Update the ClientOrchestrationCommand validation used by
spawnCommand so model option values accept the same string | boolean contract as
ModelSelection.options[].value before Schema.decodeUnknownSync runs. Prefer
aligning the decoded schema; otherwise project boolean values before decoding
while preserving string values and the existing facade boundary behavior.

In `@src/nativeRuntime.ts`:
- Around line 718-764: Extract the pure emission-pair selection logic from
reconcileThread into a dedicated helper using detailVersions, shellVersions,
alignedInitialSequence, and threadId as inputs. Have it return the selected
detail/shell/commonSequence pair, an explicit wait result for unresolved skew,
or an absent result when the thread is missing; preserve the existing
projection_invalid error and pending-only advancement rules. Replace the current
inline block with this helper so reconcileThread retains only stream
coordination and state updates.
- Around line 948-950: Update the createProject, startThread, and startTurn
handlers so projectCommand, spawnCommand, and turnCommand are constructed inside
their respective dispatch callbacks. Catch Schema.decodeUnknownSync failures at
that boundary and map them to NativeRuntimeAdapterError with the bare
"command_rejected" code, ensuring malformed commands never reach
AmbiguousDispatchError or expose user-provided input.
- Around line 213-225: Bound both connection phases using a new connectTimeoutMs
field on T3NativeRuntimeOptions, threading it through openSession into
createDefaultSessionFactory. Apply the timeout to factory.connect and
session.ready, mapping expiry to transport_unavailable while preserving the
existing scope-close cleanup path for pending sockets.
- Around line 549-580: Bound the initial alignment loop around
catchUpDetailThrough and catchUpShellThrough with an attempt limit or deadline,
incrementing and checking it on each iteration before opening new subscriptions.
When the bound is exceeded, throw
NativeRuntimeAdapterError("transport_unavailable"); preserve the existing
deleted result and snapshotSequence advancement behavior for attempts that
remain within the bound.

In `@test/facade.contract.test.ts`:
- Around line 144-145: Remove the broad JSON.stringify(evidence) assertion
checking for the substring "true" in the evidence contract test. Retain the
fastMode absence assertion, which specifically verifies that the option is not
exposed.

In `@test/native-runtime-adapter.test.ts`:
- Line 534: Await both rejected-promise assertions in
test/native-runtime-adapter.test.ts at lines 534-534 and 1024-1024: add await to
the expect(...).rejects chains for runtime.getThread("thread-1") and
sessionFactory.connect(connection), respectively, so both rejection outcomes are
actually verified.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a7145af4-aa4e-4af5-8351-553fb05ddaaf

📥 Commits

Reviewing files that changed from the base of the PR and between ea8e243 and ee87e68.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • package.json
  • src/config.ts
  • src/facade.ts
  • src/nativeRuntime.ts
  • test/config.test.ts
  • test/facade.contract.test.ts
  • test/facade.send.test.ts
  • test/facade.spawn.test.ts
  • test/facade.wait.test.ts
  • test/native-runtime-adapter.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Macroscope - Correctness Check
🔇 Additional comments (18)
test/facade.send.test.ts (1)

4-8: LGTM!

Also applies to: 44-44, 58-59, 78-79, 121-121, 136-137, 182-182, 219-219, 284-284, 340-340, 354-355, 416-416, 471-471, 528-528, 636-636, 686-686, 738-738, 791-791

test/facade.spawn.test.ts (1)

4-8: LGTM!

Also applies to: 57-57, 171-171, 256-256, 337-337, 434-434, 514-514, 580-580, 634-634, 723-723, 817-817

test/facade.wait.test.ts (1)

4-8: LGTM!

Also applies to: 104-104, 192-192, 287-287, 345-345, 385-385, 446-446, 497-497, 551-551, 610-610, 660-660, 713-713, 764-764, 826-826, 878-878, 930-930, 984-984, 1038-1038, 1089-1089, 1146-1146, 1199-1199, 1247-1247

src/config.ts (1)

8-8: LGTM!

Also applies to: 19-19, 30-30, 81-95

src/facade.ts (1)

1-5: LGTM!

Also applies to: 40-41, 63-64, 108-109, 211-214, 397-397, 493-493, 580-581, 590-591

test/config.test.ts (1)

11-11: LGTM!

Also applies to: 33-33, 42-51

test/facade.contract.test.ts (1)

5-13: LGTM!

Also applies to: 15-60, 63-143, 148-186

src/nativeRuntime.ts (8)

36-51: LGTM!

Also applies to: 122-175


188-212: LGTM!

Also applies to: 226-251


253-344: LGTM!


346-397: LGTM!


399-464: LGTM!


466-537: LGTM!


585-717: LGTM!

Also applies to: 765-800, 802-837


839-947: LGTM!

Also applies to: 951-997

test/native-runtime-adapter.test.ts (2)

21-205: LGTM!


207-533: LGTM!

Also applies to: 540-1003, 1005-1023, 1027-1047

package.json (1)

14-16: 📐 Maintainability & Code Quality

No change needed

effect@4.0.0-beta.102 exposes the required subpaths, including effect/unstable/socket/Socket, and the common core subpaths used by src/nativeRuntime.ts.

Comment thread src/facade.ts
Comment on lines +97 to +99
readonly options?: ReadonlyArray<{
readonly id: string;
readonly value: string;
readonly value: string | boolean;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Locate the model selection option value schema in `@t3tools/runtime-client` typings.
set -uo pipefail

fd -t f -e d.ts . node_modules/@t3tools/runtime-client 2>/dev/null | head -50

# Search declaration files for the option value type and the command schema.
rg -n -C 6 'modelSelection|ModelSelection' --iglob '*.d.ts' node_modules/@t3tools/runtime-client 2>/dev/null | head -120
rg -n -C 10 'ClientOrchestrationCommand' --iglob '*.d.ts' node_modules/@t3tools/runtime-client 2>/dev/null | head -80

Repository: EtanHey/t3layer

Length of output: 153


🏁 Script executed:

#!/bin/bash
set -uo pipefail

# Locate relevant source files and inspect package dependency/version declarations.
printf 'Candidate files:\n'
git ls-files | rg '(^src/(facade|nativeRuntime)\.ts$|test/facade\.contract\.test\.ts$|package\.json$|pnpm-lock\.yaml$|package-lock\.json$|yarn\.lock$)' || true

printf '\nFacade excerpt:\n'
if [ -f src/facade.ts ]; then sed -n '80,115p' src/facade.ts | cat -n; fi

printf '\nnativeRuntime spawnCommand excerpt:\n'
if [ -f src/nativeRuntime.ts ]; then sed -n '830,895p' src/nativeRuntime.ts | cat -n; fi

printf '\nRuntime-client dependency references:\n'
rg -n '"`@t3tools/runtime-client`"|`@t3tools/runtime-client`' package.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null || true

Repository: EtanHey/t3layer

Length of output: 4071


🌐 Web query:

EtanHey t3code runtime-client v0.0.31-rpc.2 ModelSelection options value bool

💡 Result:

The query appears to reference components or configuration options within a software project, likely related to the pingdotgg/t3code ecosystem or similar TypeScript-based agent/runtime tooling [1][2][3]. While v0.0.31 of the t3code project exists [4][5], and there are various "runtime-client" packages and "ModelSelection" patterns across different developer tools (such as honeycomb, sipp, or lmstudio) [6][7][8], there is no public, authoritative documentation confirming a specific configuration option for "ModelSelection" that takes a boolean value in a package named runtime-client version v0.0.31-rpc.2 [1][4][5]. It is highly likely that the term "ModelSelection" refers to a custom interface or an internal configuration object used within a specific, perhaps private or highly specialized, implementation [3][6]. If you are working with a specific codebase, you should check the type definitions (e.g.,.d.ts files) within your local node_modules or the specific source repository for that package, as it may be an undocumented or internal-only API change introduced in that specific version [4].

Citations:


🏁 Script executed:

#!/bin/bash
set -uo pipefail

printf 'Package manager / dependency availability:\n'
ls -la | rg '(^node_modules$|^pnpm-lock\.yaml$|^package-lock\.json$|^yarn\.lock$|package\.json$)' || true
node -e "console.log(JSON.parse(require('fs').readFileSync('package.json','utf8')).dependencies?{}:{})" 2>/dev/null || true

printf '\nLocate runtime-client files anywhere in checked-in fs:\n'
git ls-files | rg 'runtime-client|t3tools' || true

printf '\nInspect relevant tests around boolean option:\n'

Repository: EtanHey/t3layer

Length of output: 365


🌐 Web query:

github.com EtanHey t3code package.json "@t3tools/runtime-client" "0.0.31-rpc.2"

💡 Result:

The repository github.com/EtanHey/t3code does not exist. The main repository for the "T3 Code" project—a web GUI for coding agents—is maintained under the organization pingdotgg as github.com/pingdotgg/t3code [1][2]. Etan Heyman (GitHub profile: github.com/EtanHey) is a contributor to the pingdotgg/t3code repository [3][4][5]. Regarding the specific package version mentioned, @t3tools/runtime-client 0.0.31-rpc.2, there is no evidence of this package name or version in the pingdotgg/t3code codebase. The project uses a monorepo structure where client-side runtime logic is located in the packages/client-runtime directory [6][7], and dependencies are typically managed via workspace protocols or internal catalogs rather than external npm-style versioning like 0.0.31-rpc.2 [8][9][10]. The project relies heavily on the Effect-TS ecosystem [11][12][8].

Citations:


Validate boolean model options against the runtime-client schema before decoding.

spawnCommand passes input.modelSelection unchanged to Schema.decodeUnknownSync(ClientOrchestrationCommand). If ClientOrchestrationCommand still accepts only string option values, a boolean option from ModelSelection can throw and bypass the facade boundary. Align the decoded schema with ModelSelection.options[].value: string | boolean, or add a pre-decode projection that converts/supports booleans.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/facade.ts` around lines 97 - 99, Update the ClientOrchestrationCommand
validation used by spawnCommand so model option values accept the same string |
boolean contract as ModelSelection.options[].value before
Schema.decodeUnknownSync runs. Prefer aligning the decoded schema; otherwise
project boolean values before decoding while preserving string values and the
existing facade boundary behavior.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

WAIVED — verified false positive against the pinned rpc.2 artifact. @t3tools/runtime-client ModelSelection.options[].value is Schema.Union(string, boolean) in dist/index.d.mts, and the boolean dispatch contract test passes.

Comment thread src/nativeRuntime.ts Outdated
Comment thread src/nativeRuntime.ts
Comment thread src/nativeRuntime.ts
Comment thread src/nativeRuntime.ts
Comment thread test/facade.contract.test.ts Outdated
Comment thread test/native-runtime-adapter.test.ts Outdated
@EtanHey
EtanHey merged commit ff53324 into main Jul 31, 2026
2 of 3 checks passed
Comment thread src/nativeRuntime.ts
}
}

async function readShellSnapshot(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High src/nativeRuntime.ts:926

readShellSnapshot awaits iterator.next() with no timeout, so listProjects() hangs indefinitely when the shell subscription connects but never emits a snapshot or completion marker. Unlike catchUpShellThrough and catchUpDetailThrough, which wrap each iterator.next() call in withTransportTimeout using a deadline, readShellSnapshot awaits the raw promise. The session opened by listProjects is never closed because the function never returns. Consider applying the same timeout wrapper used by the other catch-up helpers.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/nativeRuntime.ts around line 926:

`readShellSnapshot` awaits `iterator.next()` with no timeout, so `listProjects()` hangs indefinitely when the shell subscription connects but never emits a snapshot or completion marker. Unlike `catchUpShellThrough` and `catchUpDetailThrough`, which wrap each `iterator.next()` call in `withTransportTimeout` using a deadline, `readShellSnapshot` awaits the raw promise. The session opened by `listProjects` is never closed because the function never returns. Consider applying the same timeout wrapper used by the other catch-up helpers.

Comment thread src/nativeRuntime.ts
const previousShell = shellVersions
.at(-2)
?.snapshot.threads.find((candidate) => candidate.id === threadId);
const provenPendingOnlyShellAdvance =

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High src/nativeRuntime.ts:875

In reconcileThread, the proven pending-only shell advance emits the current (stale) detail at the shell's sequence even though the matching detail event has not yet arrived. When that detail item later arrives at the same sequence, commonSequence <= lastEmitted suppresses it, so subscribers permanently miss the detail update (e.g. an assistant message). Consider waiting for the detail stream to catch up to that sequence before emitting, or allowing a corrected same-sequence emission.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/nativeRuntime.ts around line 875:

In `reconcileThread`, the proven pending-only shell advance emits the current (stale) detail at the shell's sequence even though the matching detail event has not yet arrived. When that detail item later arrives at the same sequence, `commonSequence <= lastEmitted` suppresses it, so subscribers permanently miss the detail update (e.g. an assistant message). Consider waiting for the detail stream to catch up to that sequence before emitting, or allowing a corrected same-sequence emission.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant