Repository navigation
feat(sdk,cli): flow requirements and integration connect prompts on deploy/schedule/run --cloud - #465
Conversation
…rom a flow; flows check prints REQUIRES Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…un --cloud submit Every hosted verb derives what the flow requires (flowRequirements) and checks each integration against the workspace's relayfile connection status. A missing one is offered in the terminal through the same connect-session flow agentworkforce deploy uses (POST …/integrations/connect-session → browser → poll); --no-connect, --json or no TTY refuse with integration_not_connected. A harness-credential refusal names agent-relay cloud connect <harness>. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reachedNext included review available in 25 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (18)
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. Comment |
There was a problem hiding this comment.
Devin Review found 4 potential issues.
4 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| if (isCompiledSpec(flow)) { | ||
| for (const step of flow.steps) { | ||
| if (step.type !== 'llm' && step.type !== 'agent') continue; | ||
| const named = step.type === 'agent' && step.agent !== undefined ? flow.agents?.[step.agent]?.cli : undefined; | ||
| const cli = step.cli ?? named ?? flow.cli; | ||
| need(cli === undefined ? fallback : harnessFromCli(cli), `step "${step.id}"`); | ||
| } |
There was a problem hiding this comment.
🔴 YAML helpers bypass connection checks
For YAML helper steps, flowRequirements records Claude instead of the helper provider. The hosted command submits without checking that integration, so helper execution can fail.
Learn more
YAML helper syntax is lowered by expandYamlHelpers into an agent step containing a versioned helper envelope. The compiled-spec branch treats that step as a normal agent and never recovers the envelope's provider. It therefore reports a Claude harness and no integration. The pre-submit connection gate sees no integration and allows submission, although the helper worker later requires that provider's relayfile mount.
Example: A YAML step containing slack.post compiles to an agent with external surface /slack. flows schedule --no-connect skips Slack status entirely, creates the schedule, and its first helper execution can fail for missing Slack credentials.
Recommended fix: In the compiled-spec branch, call helperCall before normal agent handling. Declare the returned provider as an integration and skip harness inference for helper envelopes.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Fixed in 123d0e2: the compiled-spec branch calls helperCall(step) first; a helper envelope declares its provider as an integration (slack (step "notify")) and skips harness inference. Test: reads a YAML helper step as its provider, never as a harness.
| const declaredAgents = requirements.harnesses | ||
| .filter((harness): harness is FlowAgentHarness => (FLOW_AGENT_HARNESSES as readonly string[]).includes(harness)); | ||
| const agents = input.agents ?? (declaredAgents.length > 0 ? declaredAgents : ['claude']); |
There was a problem hiding this comment.
🔴 Gemini declarations activate Claude
When a source declares Gemini, declaredAgents drops it and agents can default to Claude. Activation checks Claude while deployed runs still invoke Gemini.
Learn more
flowRequirements recognizes Gemini as a supported harness, but the deployment request accepts only FlowAgentHarness, whose current catalog excludes Gemini. Filtering silently changes the declared requirement rather than rejecting or preserving it. The source itself remains unchanged, so its cli: "gemini" call still needs Gemini at execution time.
Example: A flow with one f.agent(..., { cli: "gemini" }) produces requirements.harnesses = ["gemini"]. The filter produces an empty array, so inputs.agents becomes ["claude"]; activation can pass with Claude connected even though the run uses Gemini.
Recommended fix: Make the deployment harness catalog match FLOW_HARNESSES if Cloud supports Gemini. Otherwise reject unsupported declared harnesses before HTTP instead of filtering them and substituting Claude.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Fixed in 123d0e2: a declared harness outside FLOW_AGENT_HARNESSES (gemini) now refuses unsupported_source before HTTP, naming the declaration (gemini (agent "review")), unless --agents overrides it. Nothing is substituted. Test: refuses a declared harness Cloud cannot run instead of substituting Claude.
| const bodies = [flow.body, ...(flow.handlers ?? []).map(handler => handler.body)] | ||
| .flatMap(body => typeof body === 'function' ? [Function.prototype.toString.call(body)] : []); |
There was a problem hiding this comment.
🟡 Unused handlers block hosted commands
When only a handler body uses an integration, flowRequirements still requires it. Hosted launches execute the default body, so unrelated credentials can block submission.
Learn more
Authored handlers currently declare triggers, but Cloud deployment and schedule dispatch invoke the flow's default body. Scanning handler bodies treats code that Cloud does not execute as a runtime dependency. The connection gate can then refuse a valid hosted command because that unused handler dependency is absent.
Example: A flow's default body runs f.run("true"), while its schedule.every("5m") handler contains f.slack.post(...). flows schedule --no-connect refuses unless Slack is connected, although every scheduled fire executes only the default body.
Recommended fix: Separate trigger requirements from executable-body requirements. For the current hosted dispatch contract, inspect provider declarations on handlers but scan helper and harness calls only in the default body.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const connections = await ensureFlowConnections({ | ||
| path, prompt: cliConnectPrompt(io, { noConnect, json }), | ||
| }, { signal: controller.signal }); |
There was a problem hiding this comment.
🟡 Invalid commands connect integrations first
With required integrations, runCloudCli checks and can connect them before validating authored input. runCloudScheduleCli likewise connects before validating schedule arguments, causing side effects for refused commands.
Learn more
The connection workflow can create a browser session and alter workspace credentials. Local validation is deterministic and requires no Cloud request, but it currently runs afterward. The same ordering occurs in runCloudScheduleCli, before input parsing and schedule validation inside scheduleInCloud.
Example: A Slack flow invoked with malformed --input first offers to connect Slack. After the user completes that connection, JSON parsing fails and no run is submitted. A Slack schedule with --every 7m behaves similarly before rejecting the inexpressible interval.
Recommended fix: Prepare and validate the cloud submission, authored input, cron, interval, timezone, and declared schedule before calling ensureFlowConnections. Reuse the prepared result during submission to avoid loading the flow twice.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Fixed in 123d0e2: run --cloud parses --input before ensureFlowConnections, and schedule runs the new validateScheduleArgs (cron/every/tz, both-flags) plus input parsing first, so a refused command never opens a browser. Test: refuses bad arguments before it connects anything asserts zero Cloud calls.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4405869. Configure here.
…l into an unrelated exception Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ead of becoming claude, validate before connecting Addresses the first review round: a compiled helper envelope names its provider rather than a harness; a source declaring a harness Cloud cannot run is refused before HTTP instead of silently deploying with claude; run --cloud and schedule validate their arguments before any connect prompt; only the default body is scanned for helper and worker calls (handlers are not dispatched yet, flows #301); the harness remedy sees the derived harnesses even when no integration is required. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review swarm: maintainabilityNo fresh transcript was produced for run |
Review swarm: historyNo fresh transcript was produced for run |
Review swarm: structureNo fresh transcript was produced for run |
Review swarm: FAILED
Cloud run: |

Why
Deploying
examples/stale-issues(headertools: { slack: true }, bodyf.slack.post) to Cloud succeeds today and fails on the first run withhelper_slack.credential_missing: neitherflows deploy,flows schedulenorflows run --cloudasks whether the workspace has Slack connected, and the deploy wizard's required set is GitHub + ticket sources only.agentworkforce deployalready solves this for proactive agents with the relayfile connect-session prompt; this PR gives flows the same behaviour.What
flowRequirements(definition | compiled spec, { sources, repository, projectCli })(packages/sdk/src/flow-requirements.ts) →{ integrations: [{ provider, from: 'tools'|'source'|'helper', detail }], harnesses, harnessUses, mcp }, from inert declarations only:slack (tools.slack)tools.<helper>: trueheader flagnotion (tools.relayfile)tools.relayfilemountlinear (f.linear)root.<namespace>/root["namespace"]recognitionpreflightHelpersuses, so what preflight would refuse for a missing mount is what this namesgithub (on github issues)providerDeclaration(.on(github.issues()))github (--on github)/github (deploy target)--onsources; every deployment run lands in--repoclaude (agent "review"),codex (llm step),claude (step "b")cli:on eachf.agent/f.llmcall (basename-matched to claude/codex/gemini), else nearestflows.jsoncli, else Cloud's defaultclaude; compiled specs use step → named agent → flowclimcp fs (tools.mcp)tools.mcpflows checkprintsREQUIRES slack (tools.slack), github (deploy target), claude (agent "review")and carriesrequirementsin--json.Connect before submit (
packages/sdk/src/cloud-connect.ts,cli/cloud-connect-cli.ts): beforeflows deploy,flows scheduleandflows run --cloudsubmit, each required integration is checked withGET /api/v1/workspaces/<id>/integrations/<provider>/status?scope=workspace(list fallback on 404;unknown_provideris named, not treated as disconnected). A missing one prompts:Yes →
POST …/integrations/connect-session { allowedIntegrations: ["slack"], scope: { kind: "workspace" } }→connectLink(alsosessionUrl/url) opened detached in the browser (FLOWS_NO_BROWSER=1prints only) → status polled every 2s for up to 5 minutes.--no-connect,--json, no TTY, or an answer of no refuse withintegration_not_connected(exit 2) naming the provider, the declaration and both remedies.--draftskips the check, as Cloud does. A flow that requires nothing contacts nothing extra.flows deploynow defaults--agentsto the harnesses the source declares, sendsrequirementsin the deploy body (Cloud ignores unknown fields today; AgentWorkforce/cloud#3788 reads it), printsrequires:/connected:lines, and appendsConnect it with: agent-relay cloud connect <harness>toflow_model_not_connected/cli_credentials_missingrefusals — there is no status route for harness credentials, so the refusal is where the remedy is named.Tests
tests/flow-requirements.test.ts— derivation on in-process definitions, a compiled spec, and the shippedstale-issues,software-factory,pr-review-pipelineexamples;flows checkoutput and--json.tests/cloud-connect.test.ts— status/list/unknown-provider reads; prompt accept/decline/timeout/refused session; connect-session body and browser opening;cliConnectPromptTTY/--json/--no-connectrules and answer parsing;harnessRemedy;flows deploy/run --cloud/schedulerefusing before any submit,run --cloudsubmitting after a prompted connect,--draftskipping,--jsonrefusing.tests/cloud-deploy.test.ts,tests/cli.test.tsupdated for the new calls/fields.Not verified live: a real browser connect against production (the mocked contract mirrors
workforce/packages/deploy/src/connect.tsand cloud'sconnect-session/[provider]/statusroutes).🤖 Generated with Claude Code
Note
Medium Risk
Changes pre-submit behavior and Cloud API calls for all hosted CLI paths; failures are mostly early refusals, but interactive connect and new deploy fields need correct workspace/auth handling in CI and production.
Overview
Adds
flowRequirementsso the SDK can infer integrations, coding-agent harnesses, and MCP needs from static flow declarations (headers, default body, triggers, deploy context).flows checknow prints aREQUIRES …line and exposesrequirementsin--json.Before
flows deploy,flows schedule, orflows run --cloudsubmit, the CLI checks workspace integration status and can prompt to connect via Cloud’s connect-session flow (browser + poll).--no-connect,--json, non-TTY, or--draftdeploy skip or refuse instead of prompting; missing integrations exit 2 withintegration_not_connected.flows deploydefaults--agentsfrom declaredcli:harnesses, sendsrequirementsin the deploy payload, printsrequires:/connected:, and appendsagent-relay cloud connect <harness>on harness credential refusals. Unsupported declared harnesses (e.g. gemini) are refused unless--agentsoverrides.Reviewed by Cursor Bugbot for commit 123d0e2. Bugbot is set up for automated code reviews on this repo. Configure here.
Agent Relay sessions
claudesessionaa342204· opened viagh pr create· last active 2026-09-18