Skip to content

Release 0.2.1: shared native harness lifecycle and reliability fixes - #39

Merged
greenpolo merged 13 commits into
mainfrom
refactor/shared-harness
Sep 22, 2026
Merged

greenpolo merged 13 commits into
mainfrom
refactor/shared-harness

Conversation

@greenpolo

Copy link
Copy Markdown
Owner

Prepare 0.2.1 with shared native-harness lifecycle handling, preserved Cursor recovery, updated model defaults, and the outstanding quick issue fixes.

Cursor, Antigravity and Grok now share session leases, in-flight exchange handling, durable completion and response helpers. Concurrent conflicting prompts are refused before dispatch, and Cursor migrations retain native identities and pending run IDs. Provider authentication, permissions and native execution remain provider-owned.

The release also updates documentation and plugin versions, requires multi-core ~0.2.1 for each provider, improves unsupported Workflow worker diagnostics (#32), and explicitly records WSL2 as untested (#21). These issue references are partial follow-ups, not closure claims.

Validation: npm run check and npm run test:mod locally; required Linux, macOS and Windows CI must pass before merge. No new paid provider probes or WSL2 verification were performed.

greenpolo and others added 13 commits September 21, 2026 12:48
…nd prompt modules

Slice 0 of the shared-harness refactor: six composition-only modules under
plugins/multi-core/src/gateway/, with no provider file touched yet.

- harness-session.ts: HarnessSessionStore owns the lock file, the busy flag and
  the load gate; acquire() and loadOnly() are the only ways to a record and both
  refuse a busy identity with HarnessBusyError, so a provider cannot re-add
  queuing. It also holds readJson, atomicJson (explicit platform) and the
  persisted-state validators.
- harness-exchange.ts: HarnessEvent, HarnessExchange and ExchangeRegistry
  (start/get/all/observe) with an optional retain guard for an uncertain run,
  plus replayPersisted for a completed identical request.
- harness-response.ts: HarnessResponse (text, optional multi-block display rows,
  held-back terminal events) over one HarnessUsageFields vocabulary.
- harness-notices.ts: interrupted, policy and rewind notices, safeText, stderr
  diagnostics, continuation, historyRewound, harnessHistoryHash, terminalSuffix.
- harness-process.ts: runNativeCli with the SIGINT/SIGTERM/SIGKILL cascade,
  nativeEnvironment, redactStderr and NativeCliError; every event grammar plugs
  in through parseLine and finish, which stay provider-side.
- harness-prompt.ts: prepareNativePrompt and validateNativeMessages, with
  catalogue stripping and empty-turn dropping off by default.

Every platform-dependent path takes an explicit platform option. Six node:test
suites cover the new API, including the busy refusal, disk replay, rewind
detection, notices, the process cascade with a fake spawn and prompt preparation
with catalogues on and off.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112UmL1KthnW34TDdkcCJww
…se prompts while busy

Antigravity now composes the shared harness plumbing instead of carrying its
own copy. Moved out of plugins/multi-antigravity/src (-663 lines):

- harness.ts: the session store (lock, load gate, busy flag, versioned read,
  validators, atomic save), the exchange registry and its `cachedExecute`
  replay, `HarnessResponse`, the interrupted/policy/rewind notices,
  `safeText`, the stderr diagnostics filter, `continuation`,
  `historyRewound` and the JSON/hash helpers.
- cli.ts: the process lifecycle (spawn, NDJSON framing, output limits,
  SIGINT/SIGTERM/SIGKILL escalation across the tree, cancellation settling,
  pipe teardown, stderr redaction) and the environment deny list.
- request.ts: the prompt builder, the Messages validator, the history hash
  and the terminal suffix.

Kept provider-side, per the plan's do-not-touch list: the init/step/result
grammar, `finish` gating a failed exit on `status === 'SUCCESS'`, the
stream-json stdin delivery and its argument-size choice, the usage
vocabulary adapter and `usageDelta`, the `denied_actions` diagnostics, the
permission maps, and the `nativeSpelling(body.model)` key normalisation in
`handle()` that keeps `[1m]` and plain spellings one request identity.

Busy behaviour: 502 retryable -> 400 deterministic. `HarnessSessionStore`
is now the only path to a session record, `acquire()` refuses a busy agent
with `HarnessBusyError`, and that call moved inside `execute()`'s `try` so
the refusal is wrapped. `AntigravityProviderError` maps `HarnessBusyError`
to 400. A 502 is retryable, so Claude re-sent a prompt whose history stops
at the still-running turn and the retry resumed the native conversation
with stale history, forwarding a paid turn a second time. New tests pin the
refusal at the harness (one native call) and over HTTP (400, not 502).

Adopting the store also fixes the missing `platform` on the session write,
which previously bypassed the Windows atomic-write path.

No shared-module changes were needed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112UmL1KthnW34TDdkcCJww
Grok adopts the six plugins/multi-core/src/gateway/harness-*.ts modules
extracted in slice 0, as the reference implementation for the shared-harness
refactor:

- harness-session.ts: HarnessSessionStore replaces Grok's own session map,
  lock handling, and Saved/Session validators. GrokBusyError is deleted;
  GrokProviderError now maps HarnessBusyError to status 400 directly.
- harness-exchange.ts: ExchangeRegistry replaces the hand-rolled exchange
  map/observe logic; replayPersisted replaces serve()/cachedExecute().
- harness-response.ts: HarnessResponse/HarnessUsageFields replace Grok's own
  response-builder class, via a 6-line GrokUsage -> HarnessUsageFields
  adapter kept in harness.ts.
- harness-notices.ts: writeNotices, interruptedNotice, safeText,
  stderrDiagnostics, continuation, and historyRewound replace their
  Grok-local copies.
- harness-process.ts: runNativeCli replaces spawnGrok's process-lifecycle
  cascade. GrokCliError now extends the shared NativeCliError so
  `instanceof GrokCliError` still holds for every failure path; a small
  adapter rewraps the rare bare-NativeCliError rejection runNativeCli can
  raise before any Grok parser state exists.
- harness-prompt.ts: prepareNativePrompt/validateNativeMessages replace
  prepareGrokRequest's own message-flattening and validation; request.ts
  is now a thin wrapper supplying Grok's preamble and
  stripCatalogues/dropEmptyTurns options. grokHistoryHash now delegates to
  harnessHistoryHash({ stripReminders: true }).

Grok-owned pieces stay as the plan requires: NDJSON parsing (parseGrokLine
and friends), `finish` semantics (finishValue), toolset verification
(parseTools/sameTools), TRANSIENT_SPAWN/permanentFailure/spawnSystemCode,
the GrokUsage adapter, and the errors.ts advice tables.

Fixed a busy-lock leak the coordinator flagged after the Antigravity slice
landed: HarnessSessionStore.acquire() marks a session busy before
continuation() can validate the request, so continuation()/historyRewound()
now run inside execute()'s try/finally (extracted into openTurn() to keep
execute() under the complexity limit) instead of a separate session()
helper outside it. A new test pins that a rejected continuation still
releases the record for the next request. The store's persisted `provider`
field stays the lowercase 'grok' discriminator; ExchangeRegistry and
replayPersisted use the capitalized 'Grok' display tag so exchange/replay
messages read exactly as they did before this slice.

No shared-module changes were needed; harness-process.ts, harness-session.ts,
harness-exchange.ts, harness-response.ts, harness-notices.ts, and
harness-prompt.ts are used exactly as slice 0 defined them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112UmL1KthnW34TDdkcCJww
…nd notice modules and refuse prompts while busy

What moved out of plugins/multi-cursor/src/:

- Session state: the record map, lock file, busy flag, load gate, atomic save
  and persisted-state validation now live in HarnessSessionStore. Cursor keeps
  its SDK agent lifecycle (create/resume, closeAgent, configureSession,
  recoverRun/recoverSession, archiveReply, persistDispatch, 32-agent eviction);
  only the lock/persist/validate layer is shared. The record identity is
  [cwd, scope] and the SDK-specific fields (agentId, pendingRun) plus their
  validator stay provider-side.
- Exchange plumbing: the exchange map, forwarding emitter, settle/retain rule
  and observe() are ExchangeRegistry; the disk replay path is replayPersisted.
- HarnessResponse is the shared one with multiBlock: true, so mod display rows
  keep their own content blocks. Cursor's TokenUsage is mapped into
  HarnessUsageFields in one adapter, and the terminal events are now taken from
  takeTerminalEvents() and delivered on the same path as before: recorded into
  the replay, held back until the turn is durably committed.
- Notices: continuation, historyRewound, harnessHistoryHash (no reminder
  stripping, as Cursor hashed history today), terminalSuffix, interruptedNotice
  and writeNotices. cursorHistoryHash and cursorTerminalSuffix are deleted from
  request.ts.
- Cursor does not adopt harness-process or harness-prompt: the whole SDK agent
  lifecycle and prepareCursorRequest stay Cursor-side.

Exchange retention is expressed through the registry's retain option so the
crash/restart ordering the harness test pins is unchanged: a finished turn stays
addressable for replay, a failure that may have run stays so its uncertainty is
reported instead of repeating native edits, and a failure that provably ran
nothing is dropped.

Busy behaviour change (user visible): a prompt that arrives while a run owns the
agent was answered 502, which is retryable, so Claude resent the same prompt --
whose history stops at the still-running turn -- and the retry resumed the native
conversation with stale history, forwarding a paid turn again. Both Cursor gates
(taking the turn, and the interrupted-state recovery gate) now raise
HarnessBusyError through HarnessSessionStore.acquire()/loadOnly(), and
cursorFailure maps it to a deterministic 400 before the CODE_STATUS table. There
is no queue and no parameter to add one.

Session record version 2 -> 3 for the shared provider/identity header. A version
2 record is ignored rather than refused, so the next turn starts a fresh native
agent instead of failing to load.

Shared-module additions (additive only, no signature breaks) for the integrator
to reconcile with the parallel Grok and Antigravity slices:

- HarnessSessionStore: new `transient` constructor option (live-only record keys
  stripped by save(); Cursor passes agent/run/policy), new forget(identity) so a
  provider can drop a cached record while keeping or releasing its lock itself,
  and sessionFile(identity) changed from private to public for the reads that
  must not take the lock (Cursor's billed-usage query).
- ExchangeRegistry: new `size` getter and evictSettled(), which Cursor's
  256-exchange cap needs; a running exchange is never dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112UmL1KthnW34TDdkcCJww
…tus mapping

server.ts's errorStatus() already forwards a single failure.status field for
all three *ProviderError classes with one 502 default; each provider already
makes HarnessBusyError deterministic (400) at its own boundary, so there is no
duplicated switch left to unify there. Grok and Antigravity now read the
exchange registry's size getter instead of all().length, matching Cursor.

Document the shared harness-*.ts modules in ARCHITECTURE.md (state, isolation,
platform layer) and AGENTS.md's code map, and note in docs/cursor.md and
docs/antigravity.md that a prompt sent while a run is in flight is refused
with a deterministic error instead of queued, matching docs/grok.md. Record
the refactor, the Antigravity/Cursor busy-refusal status change, the Cursor
session record version bump, and the Cursor token estimate fallback change in
CHANGELOG.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112UmL1KthnW34TDdkcCJww
… shared harness

Cursor's interrupted-state recovery now acquires the session record for its whole
duration, so a second request is refused instead of dispatching a run whose
pendingRun the finishing recovery would clear.
Replay validation is provider-parameterized: Cursor accepts its display-only
tool_use rows, Grok and Antigravity stay text-only, so a mod-row turn replays
from the record and from its archived response file instead of failing forever.
Cursor's 32-agent budget counts records that hold a native SDK agent, and the
replay path's use of the record lock is documented rather than removed.
HarnessSessionStore takes a display tag, so a busy refusal names Antigravity
rather than its on-disk discriminator.
continuation() names the provider whose native session could not be continued.
Antigravity wraps loadOnly, so a first-load race is answered 400 instead of a
retryable 502.
Antigravity rewraps a synchronous spawn failure as AntigravityCliError, keeping
the operating system's own code.
CHANGELOG records the Grok transient-spawn status change and the Cursor replay
lock, and counts the behaviour changes correctly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112UmL1KthnW34TDdkcCJww
Share scoped session leases and durable completion, preserve Cursor legacy records, and bound SDK capacity across creation and billing queries. Extract conversation normalization and inject provider-owned harness and usage adapters through the existing Mods control plane.
Reduce the OpenAI profile to Claude-hosted compatibility, remove the legacy fleet ban, and distinguish conventions from execution contracts. Document Claude Mods with the maintainer's explicit integration requirement, repair platform-specific skills and stale operational claims, and record refactor behavior and verification limits.
…ember model releases

Cursor keeps the pending run when a completion save fails, so a retry recovers instead of re-dispatching. Idle eviction and disk-only replay release record locks without deleting native state, temporary billing handles count toward the SDK budget, legacy `pending` records migrate, and ambiguous v2/v3 ownership reports a documented recovery path. Shared normalization rejects malformed messages again, `loadOnly` is removed, and the opt-in OpenAI instructions probe matches the compatibility note.

Codex workers `openai-luna` and `openai-sol` now run `gpt-6-luna` and `gpt-6-sol`, and the no-Claude fallback starts on `gpt-6-luna`. The Cursor picker shows Grok 4.7 instead of Grok 4.6. Zen adds `gpt-6-luna` and `gpt-6-sol` to its catalog.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014HE2qVAwqeZ1AZALmufezm
Copilot AI lite review requested due to automatic review settings September 22, 2026 19:50
@greenpolo
greenpolo merged commit e029a41 into main Sep 22, 2026
7 checks passed

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Seven unresolved findings remain, including two critical Cursor replay-identity issues and lifecycle, cancellation, rewind, validation, and malformed-tool handling issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 High severity · 2 Medium severity

Open (4)
What changed in this PR

Release 0.2.1 centralizes native harness lifecycle, replay, prompt normalization, and completion handling for Cursor, Antigravity, and Grok, alongside model, usage, documentation, test, and version updates.

Changes:

  • Adds shared native session, exchange, response, prompt, and completion utilities.
  • Improves provider recovery, busy-request handling, model defaults, and usage reporting.
  • Updates documentation, platform guidance, plugin metadata, and regression coverage.

Review findings:

  • Critical (2 votes): Cursor exchange identity still hashes raw request bodies, allowing cache-marker changes to miss replay and rerun native turns.
  • Critical (1 vote): Cursor’s normalized history hash is not used at the exchange-key call site, causing the same replay risk.
  • Moderate (2 votes): Completion archives have no bounded cleanup policy.
  • Moderate (2 votes): Canceled observers can receive cached SSE events before abort handling.
  • Moderate (1 vote): Duplicate assistant content can defeat rewind detection.
  • Moderate (1 vote): Malformed non-array stop_sequences values can be silently ignored.
  • Moderate (1 vote): null tool entries can produce generic dereference errors instead of validation errors.
File Reviewed changes
test/​unit/​native-zen-models.test.ts Covers new Zen model catalog entries.
test/​unit/​native-tool-discovery.test.ts Updates discovery model coverage.
test/​unit/​native-openai-approval.test.ts Updates OpenAI approval model coverage.
test/​unit/​native-mod-routes.test.ts Updates native route fixtures.
test/​unit/​native-launcher.test.ts Updates launcher catalog coverage.
test/​unit/​native-harness-response.test.ts Tests shared response streaming and durable completion.
test/​unit/​native-harness-prompt.test.ts Tests shared prompt preparation.
test/​unit/​native-harness-process.test.ts Tests native process execution and cancellation.
test/​unit/​native-harness-notices.test.ts Tests notices and replay/history handling.
test/​unit/​native-harness-interface.test.ts Tests native error status handling.
test/​unit/​native-harness-exchange.test.ts Tests exchange sharing, replay, and cancellation.
test/​unit/​native-grok-harness.test.ts Tests Grok lifecycle handling.
test/​unit/​native-gateway.test.ts Updates gateway model and instruction coverage.
test/​unit/​native-cursor-request.test.ts Tests Cursor normalization and validation.
test/​unit/​native-cursor-models.test.ts Updates Cursor picker defaults.
test/​unit/​native-cursor-mod-rows.test.ts Tests Cursor display-row replay.
test/​unit/​native-cursor-harness-http.test.ts Tests Cursor busy-request HTTP behavior.
test/​unit/​native-approval.test.ts Updates provider approval fixtures.
test/​unit/​native-antigravity-harness.test.ts Tests Antigravity busy and race handling.
test/​unit/​native-antigravity-gateway.test.ts Tests Antigravity gateway refusals.
test/​unit/​native-antigravity-cli.test.ts Tests CLI spawn error wrapping.
test/​unit/​mod-policy.test.ts Tests unsupported Workflow diagnostics.
test/​live/​native-translation.ts Updates the live translation model.
test/​live/​native-reviewer.ts Updates the live reviewer model.
test/​live/​native-provider-approval.ts Updates the live approval model.
test/​live/​native-permissions.ts Updates live permission defaults.
test/​live/​native-openai-instructions.ts Tests current instruction behavior.
test/​live/​native-openai-cache.ts Updates the live cache probe model.
test/​live/​native-main-switch.ts Updates live model switching.
test/​live/​native-approval-worker.ts Updates the live worker model.
README.md Clarifies setup, platform, and asset documentation.
PRIVACY.md Documents local usage, receipts, and provider lookups.
plugins/​multi-zen/​src/​usage-adapter.ts Adds Zen usage integration.
plugins/​multi-zen/​src/​models.ts Adds Zen catalog entries.
plugins/​multi-zen/​skills/​connect/​SKILL.md Adds shell-specific connection commands.
plugins/​multi-zen/​.claude-plugin/​plugin.json Bumps the Zen plugin version.
plugins/​multi-openai/​src/​usage.ts Updates the Codex client version.
plugins/​multi-openai/​src/​usage-adapter.ts Adds OpenAI usage integration.
plugins/​multi-openai/​src/​responses.ts Uses shared conversation normalization.
plugins/​multi-openai/​src/​models.ts Updates OpenAI default mappings.
plugins/​multi-openai/​src/​instructions.ts Updates compatibility instruction loading.
plugins/​multi-openai/​src/​instructions.md Replaces inherited workflow instructions.
plugins/​multi-openai/​skills/​login/​SKILL.md Adds shell-specific login commands.
plugins/​multi-openai/​.claude-plugin/​plugin.json Bumps the OpenAI plugin version.
plugins/​multi-grok/​src/​usage.ts Clarifies native credential handling.
plugins/​multi-grok/​src/​usage-adapter.ts Adds Grok usage integration.
plugins/​multi-grok/​src/​request.ts Uses shared native prompt and history helpers.
plugins/​multi-grok/​skills/​login/​SKILL.md Updates login guidance and shell commands.
plugins/​multi-grok/​.claude-plugin/​plugin.json Bumps the Grok plugin version.
plugins/​multi-cursor/​src/​usage-adapter.ts Adds Cursor usage integration.
plugins/​multi-cursor/​src/​session-record.ts Adds Cursor record migration and recovery.
plugins/​multi-cursor/​src/​request.ts Uses shared normalization and validation.
plugins/​multi-cursor/​src/​models.ts Updates Cursor picker defaults.
plugins/​multi-cursor/​src/​errors.ts Maps busy errors to deterministic refusals.
plugins/​multi-cursor/​skills/​login/​SKILL.md Adds shell-specific login commands.
plugins/​multi-cursor/​.claude-plugin/​plugin.json Bumps the Cursor plugin version.
plugins/​multi-core/​src/​launcher.ts Wires usage readers and new defaults.
plugins/​multi-core/​src/​gateway/​server.ts Uses shared harness contracts and usage adapters.
plugins/​multi-core/​src/​gateway/​provider-usage.ts Exposes provider usage interfaces.
plugins/​multi-core/​src/​gateway/​native-harness.ts Defines shared native harness boundaries.
plugins/​multi-core/​src/​gateway/​mode-hook.ts Improves unsupported worker diagnostics.
plugins/​multi-core/​src/​gateway/​harness-response.ts Builds bounded native responses.
plugins/​multi-core/​src/​gateway/​harness-prompt.ts Normalizes native prompts.
plugins/​multi-core/​src/​gateway/​harness-notices.ts Provides notices and history helpers.
plugins/​multi-core/​src/​gateway/​harness-exchange.ts Coordinates in-flight exchanges and replay.
plugins/​multi-core/​src/​gateway/​harness-completion.ts Persists responses before terminal events.
plugins/​multi-core/​src/​gateway/​conversation.ts Centralizes message and media normalization.
plugins/​multi-core/​skills/​uninstall/​SKILL.md Updates shell-specific uninstall guidance.
plugins/​multi-core/​skills/​status/​SKILL.md Updates shell-specific status guidance.
plugins/​multi-core/​skills/​setup/​SKILL.md Updates shell-specific setup guidance.
plugins/​multi-core/​hooks/​tests/​workers.test.ts Updates worker model fixtures.
plugins/​multi-antigravity/​src/​usage-adapter.ts Adds Antigravity usage integration.
plugins/​multi-antigravity/​src/​request.ts Uses shared native prompt helpers.
plugins/​multi-antigravity/​skills/​connect/​SKILL.md Adds shell-specific connection commands.
plugins/​multi-antigravity/​.claude-plugin/​plugin.json Bumps the Antigravity plugin version.
package.json Bumps the release version and adds a live test script.
package-lock.json Synchronizes package version metadata.
docs/​zen.md Updates Zen setup and model documentation.
docs/​usage.md Updates provider and receipt documentation.
docs/​platform-support.md Records verification scope and WSL2 status.
docs/​permissions.md Documents native admission and Workflow limitations.
docs/​openai.md Updates OpenAI models and instruction behavior.
docs/​installation.md Updates setup and launcher guidance.
docs/​grok.md Documents Grok usage and busy-request handling.
docs/​cursor.md Documents Cursor recovery and lifecycle behavior.
docs/​claude-mods.md Documents the Claude Mods integration boundary.
docs/​assets/​README.md Clarifies illustration provenance.
docs/​antigravity.md Documents Antigravity busy-request handling.
CONTRIBUTING.md Documents Mods test requirements.
CLAUDE.md Adds Mods contribution guidance.
CHANGELOG.md Adds the 0.2.1 release notes.
ARCHITECTURE.md Describes shared harness architecture.
AGENTS.md Updates architecture and verification guidance.
.github/​ISSUE_TEMPLATE/​feature_request.yml Adds Grok to feature options.
.github/​ISSUE_TEMPLATE/​bug_report.yml Adds Grok to bug options.
.claude-plugin/​plugin.json Bumps the root plugin version.
.claude-plugin/​marketplace.json Bumps marketplace plugin versions.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 387 to 388
{ ...body, stream: undefined },
cursorPermissionPolicy(permissions).identity,
Comment on lines +2 to 5
import { normalizeConversation, textContent } from '../../multi-core/src/gateway/conversation.ts';
import { isDirectToolAvailable } from '../../multi-core/src/gateway/direct-tools.ts';
import type { MessagesRequest } from '../../multi-core/src/gateway/messages.ts';
import { estimateTextTokens } from '../../multi-core/src/gateway/tokens.ts';
Comment on lines +73 to +75
await atomicJson(
path.join(args.stateDirectory, `${replay.key}.response.json`),
{ response, events: replay.events },
Comment on lines +105 to +124
async observe(
exchange: HarnessExchange<M>,
signal: AbortSignal,
emit?: Emit,
): Promise<MessagesResponse> {
exchange.observers++;
const aborted = Promise.withResolvers<never>();
void aborted.promise.catch(() => {});
const cancel = () => aborted.reject(signal.reason);
signal.addEventListener('abort', cancel, { once: true });
try {
if (emit) {
exchange.listeners.add(emit);
for (const event of exchange.events) {
emit(...event);
}
}
signal.throwIfAborted();
return await Promise.race([exchange.result, aborted.promise]);
} finally {
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.

2 participants