Skip to content

feat(surface): dependency-free hosted completion-signal helper (2.0.54) - #677

Merged
miyaontherelay merged 2 commits into
mainfrom
ms-lead/surface-signals-2054
Oct 19, 2026
Merged

miyaontherelay merged 2 commits into
mainfrom
ms-lead/surface-signals-2054

Conversation

@miyaontherelay

@miyaontherelay miyaontherelay commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Why

Hosted Cloud runs materialize only the hash-pinned @relayflows/surface next to the flow source (cloud packages/core/src/bootstrap/lib/relayflow-v2-executor.ts, materializeRelayflowV2SurfacePackage). @relayflows/sdk, where waitForCloudSignal lives, can't be imported there, so a hosted flow has no way to wait on a completion signal.

What

  • New subpath @relayflows/surface/signals exporting waitForSignal(name, { waitMs?, signal?, runId?, apiUrl?, token?, requestTimeoutMs? }).
    • Defaults: runId = RELAYFLOW_V2_CLOUD_RUN_ID (Cloud's run ID, not the engine's RELAYFLOW_RUN_ID); token = RELAYFLOW_V2_SIGNAL_READ_TOKEN; apiUrl = FLOWS_CLOUD_URL (the SDK's cloud-http variable), else https://agentrelay.com/cloud.
    • Dependencies: the built dist/signals.js imports only node:timers/promises and calls Cloud through global fetch.
    • Call it in the authored body. Only that process holds the read token; f.run children don't.
  • One implementation. The held-GET loop is now holdSignalWait in Surface. It covers name and run-ID validation, waitMs <= 25000, the 250 ms → 5 s abort-aware fast-204 backoff with reset after a held 204, and error classification (caller abort passes through, deadline or reset is transient_error, malformed JSON or bad shape is invalid_response). The SDK's waitForCloudSignal now delegates to it. It keeps its public signature, its credential resolution (stored login and refresh, then the FLOWS_CLOUD_TOKEN > hosted read-token fallback), CloudFlowError, and its own timer, so the existing SDK suite runs unchanged.
  • pack-release.mjs now requires dist/signals.{js,d.ts} in the surface tarball.
  • Docs: a new SURFACE.md section covering hosted completion signals.

Required Cloud change (must land before Cloud pins Surface 2.0.54)

Cloud's executor compares the published exports map exactly (JSON.stringify equality) before applying the Bun envelope. A 2.0.54 manifest that contains ./signals therefore fails with Relayflow v2 authored Surface package has an unsupported export shape for every hosted TS flow, not only flows that use signals. The envelope also writes a fixed exports map, so ./signals would not resolve even if that check passed. tests/signals-envelope.test.ts replicates both behaviors. Cloud's root currently pins surface 2.0.52, so nothing breaks until Cloud bumps.

The changes, in packages/core/src/bootstrap/lib/relayflow-v2-executor.ts (cloud main 5b31e1016):

  1. L3991–4024: accept the legacy shape or the legacy shape with "./signals": { types: "./dist/signals.d.ts", import: "./dist/signals.js", require: "./dist/signals.js" } appended last. Key order matters for the stringify compare.
  2. L4048–4053: add "./signals": "./signals.js" to the envelope exports, but only when the manifest has it. A pre-2.0.54 derived package then stays byte-identical, so resume hashes still match.
  3. After L4078: when the manifest has it, write signals.js and signals.d.ts containing export * from './dist/signals.js';\n (flag: "wx", mode: 0o600).
  4. Bump Cloud's @relayflows/surface pin and authority to 2.0.54.
  5. Recommended: set FLOWS_CLOUD_URL in the CLI childEnv (~L859–867) for non-prod stages. Otherwise the helper falls back to https://agentrelay.com/cloud.

Note

certifiedModuleBody (authored-parallel-eligibility.ts) only certifies modules whose single import is import { flow } from '@relayflows/surface'. A flow that also imports @relayflows/surface/signals therefore runs its Promise.all f.run groups on the serial scheduler. This is documented in SURFACE.md and left unchanged here.

Verification

All commands were run locally (Node 26.11.1, Bun 1.4.2) in a fresh worktree off origin/main (v2.0.53), with real npm ci in packages/surface and packages/sdk.

  • packages/surface: tsc build OK, tsc --noEmit OK, tsc -p tsconfig.test.json OK, npm run typecheck:regressions OK. vitest run gives 13 files / 80 tests passed: 18 new in signals.test.ts and 6 new in signals-envelope.test.ts, which runs under both node and bun.
  • scripts/surface-package-gate.sh passes end to end: PACKED_RUNTIME_OK, PACKED_RUNTIME_REFUSAL_OK, PACKED_SIGNALS_OK imports=node:timers/promises, PACKED_TYPESCRIPT_OK, the SDK typecheck against the packed tarball, and the SDK authored-flow.test.ts (38 passed). Locally I added --backend=copyfile to bun install because of a cache issue on this disk; the script in the PR is unchanged in that respect.
  • packages/sdk: npm run typecheck OK, npm run build OK, npm run typecheck:tests OK. tests/cloud-signal.test.ts passes 17/17, unmodified. The cloud-run, connect, deploy, schedule, sync and signal files pass 320/320 with TMPDIR=/tmp. My machine's TMPDIR contains a space, which breaks vite's loader for those files; that is environmental and unrelated to this change.
  • What failed when I reverted each piece:
    • Surface backoff cap set from 5 s to 4 s: 2 tests fail, 1 in the Surface suite and 1 in the unmodified SDK cloud-signal.test.ts. This shows the SDK really delegates to Surface.
    • Hosted run-ID default switched to RELAYFLOW_RUN_ID: 3 tests fail.
    • ./signals export removed from package.json: 3 tests fail in the envelope suite.
  • npm run check:docs reports AUTHORING.md drift that already exists on main (the secrets? fields). This PR doesn't touch it.

Not run: the full SDK suite and the Bun-standalone authored-node-runtime.test.ts, because both need a kernel cargo build. The envelope test covers the subpath under Node and Bun module resolution on the envelope Cloud writes.

Not merged and not published.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Note

Medium Risk
Introduces run-bound signal-read credentials and shared Cloud wait logic used by hosted flows and the SDK; shipping Surface 2.0.54 without the documented Cloud envelope update would break hosted TS flows at manifest check time.

Overview
Adds @relayflows/surface/signals so hosted flows can waitForSignal without importing the SDK, using only Node built-ins and fetch with hosted env defaults (RELAYFLOW_V2_CLOUD_RUN_ID, RELAYFLOW_V2_SIGNAL_READ_TOKEN, FLOWS_CLOUD_URL).

The held-GET loop (validation, 25s holds, fast-204 backoff, error codes) lives in Surface as holdSignalWait; waitForCloudSignal in the SDK now delegates there while keeping its own credential resolution and CloudFlowError. package.json exports ./signals, docs/SURFACE.md documents usage and scheduler implications, and new unit/envelope tests assert wire behavior and Cloud’s Bun materialization shape ( ./signals needs a companion Cloud executor change before pinning this Surface version).

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


Agent Relay sessions

  • claude session a7fef635 · opened via gh pr create · last active 2026-10-19

Hosted Cloud runs materialize only the pinned @relayflows/surface next to
the flow source, so the SDK's waitForCloudSignal cannot be imported there.

- Add @relayflows/surface/signals: waitForSignal(name, opts?) with hosted
  defaults (RELAYFLOW_V2_CLOUD_RUN_ID, RELAYFLOW_V2_SIGNAL_READ_TOKEN,
  FLOWS_CLOUD_URL else https://agentrelay.com/cloud). Imports only
  node:timers/promises and uses global fetch.
- Move the held-GET wait (path validation, waitMs <= 25000, fast-204
  backoff 250ms->5s with reset, error classification) into the Surface as
  holdSignalWait; the SDK's waitForCloudSignal now delegates to it with its
  own credential resolution, hosted-token fallback, and CloudFlowError.
- Tests: Surface unit tests mirroring the SDK suite plus hosted defaults;
  packed-consumer gate imports the subpath from the tarball and asserts it
  imports only Node built-ins; a replica of Cloud's Bun compatibility
  envelope shows the subpath needs a companion Cloud change.
- Docs: SURFACE.md usage for hosted flows.

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

@greptile-apps greptile-apps 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.

miyaontherelay has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b47e22a6-1dc5-4961-8ced-2ba6e71078a0
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@capy-ai capy-ai 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.

Capy found 1 potential issue (1 medium).

Open in Capy

Comment thread scripts/surface-package-gate.sh Outdated
Flows AGENTS.md: never edit a gate that judges your own work. The
surface-package-gate.sh and pack-release.mjs assertions for
./signals move to a follow-up PR that lands after this one.

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

@greptile-apps greptile-apps 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.

miyaontherelay has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@capy-ai capy-ai 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.

Capy found no issues.

Open in Capy

@miyaontherelay
miyaontherelay merged commit 9df33d2 into main Oct 19, 2026
9 checks passed
@miyaontherelay
miyaontherelay deleted the ms-lead/surface-signals-2054 branch October 19, 2026 10:39
miyaontherelay added a commit that referenced this pull request Oct 19, 2026
Follow-up to #677 (kept separate per AGENTS.md: never edit a gate that
judges your own work). pack-release.mjs requires dist/signals.js and
dist/signals.d.ts in the Surface tarball, and surface-package-gate.sh
checks the subpath resolves from the packed tarball and imports only
Node built-ins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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