Repository navigation
flows: refresh an expired agent-relay cloud login instead of asking for a re-login - #614
Conversation
`.git/info/exclude` lists `/summary.md` alongside `/plan.md` and `/reviewed-plan.md` as a relayflow working file, so the verification report was never meant to land in the repository. The file stays on disk; only the tracked copy goes away. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`snapshotWorkspaceFiles` threw on any non-ENOENT error, so an agent step's
artifact scan failed the step whenever a file in the scanned tree could be
seen but not read. `bun build --compile` creates exactly such a file: it
opens its output in its *cwd* with `O_CREAT|O_EXCL` and mode 000, writes the
whole executable into it, and only then renames it onto `--outfile`. With
`bundle-typescript.ts` running that with cwd set to the flow's own directory,
any tree a build is running in holds an unreadable file for as long as the
compile takes — tens of megabytes, seconds — and a neighbouring agent step
that had done its work died on it:
Error: EACCES: permission denied, open
'.../packages/sdk/tests/fixtures/.ee96ae00de70a543-00000000.bun-build'
❯ walk src/agent-artifacts.ts:68:15
❯ Module.snapshotWorkspaceFiles src/agent-artifacts.ts:37:3
An unreadable file is now recorded by size and reason (`7:unreadable:EACCES`)
instead of being dropped or failing the scan, so the `artifacts` list stays
complete and the marker can never be mistaken for a content hash. Everything
else still propagates — an unreadable *directory* included, since a scan that
cannot enumerate a subtree does not know what it is missing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
|
Relayflow: the adversarial review did not pass. This branch is not approved: the flow stopped here and did not mark it ready to merge. Review of PR #614Reviewed head: Verdict: request changes. One issue remains; P2 — Unreadable-file signatures silently lose same-size artifact editsLocation: The unrelated artifact-scanning change replaces a read failure with a signature containing only size and errno. If a file is unreadable at both snapshots, a same-size content rewrite yields identical signatures and
Remove this unrelated change from the login-refresh PR, or represent unreadability explicitly and handle it without treating equal size/errno as evidence of unchanged content. Add regression coverage for a same-size write through an open descriptor between two unreadable snapshots. ReproductionExecuted as UID 1001 (not root). The following script writes Command, run from cat > /tmp/pr614-review/artifact-repro.ts <<'EOF'
import { chmod, mkdtemp, open, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { snapshotWorkspaceFiles, diffWorkspaceFiles } from '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/src/agent-artifacts.ts';
const dir = await mkdtemp(join(tmpdir(), 'pr614-artifacts-'));
try {
const path = join(dir, 'report.txt');
await writeFile(path, 'aaaa');
const writer = await open(path, 'r+');
try {
await chmod(path, 0o000);
const before = await snapshotWorkspaceFiles(dir);
await writer.write('bbbb', 0, 'utf8');
const after = await snapshotWorkspaceFiles(dir);
console.log(JSON.stringify({ before: [...before], after: [...after], changed: diffWorkspaceFiles(before, after) }));
} finally { await writer.close(); }
} finally { await rm(dir, { recursive: true, force: true }); }
EOF
npx vite-node /tmp/pr614-review/artifact-repro.tsCaptured output: The actual Refresh implementation and review scopeRead the full PR diff, its changed tests, the RFC, and the installed The refresh tests cover persistence before use, explicit credential precedence, missing/expired refresh tokens, HTTP and transport errors, invalid responses, lock contention, concurrent callers, timeout, and cancellation. Existing artifact tests do not cover the finding above. Verification evidenceInitial invocation from the repository root could not locate Vitest: npx vitest run packages/sdk/tests/cloud-auth-refresh.test.ts packages/sdk/tests/cloud-read.test.ts packages/sdk/tests/cloud-run.test.ts packages/sdk/tests/cloud-mirror-session.test.ts packages/sdk/tests/agent-artifacts.test.tsCaptured output: Re-ran from npx vitest run tests/cloud-auth-refresh.test.ts tests/cloud-read.test.ts tests/cloud-run.test.ts tests/cloud-mirror-session.test.ts tests/agent-artifacts.test.tsCaptured output: Command, from npm run typecheckCaptured output: All PR comments and reviewsThe only issue comment is CodeRabbit's skipped-review notice. No submitted reviews or inline comments were returned. The comment does not constitute review approval. Commands and captured output: gh api --paginate repos/AgentWorkforce/flows/issues/614/comments --jq '.[] | {author: .user.login, url: .html_url, body: .body}'{"author":"coderabbitai[bot]","body":"\u003c!-- This is an auto-generated comment: summarize by coderabbit.ai --\u003e\n\u003c!-- This is an auto-generated comment: skip review by coderabbit.ai --\u003e\n\n\u003e [!IMPORTANT]\n\u003e ## Review skipped\n\u003e \n\u003e Bot user detected.\n\u003e \n\u003e To trigger a single review, invoke the `@coderabbitai review` command.\n\u003e \n\u003e \u003cdetails\u003e\n\u003e \u003csummary\u003e⚙️ Run configuration\u003c/summary\u003e\n\u003e \n\u003e - **Configuration used**: Organization UI\n\u003e - **Review profile**: CHILL\n\u003e - **Plan**: Advanced\n\u003e - **Run ID**: `964a7358-0666-4345-b922-9355a07cf9f9`\n\u003e \n\u003e \u003c/details\u003e\n\u003e \n\u003e You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.\n\u003e \n\u003e Use the checkbox below for a quick retry:\n\u003e - [ ] \u003c!-- {\"checkboxId\":\"e9bb8d72-00e8-4f67-9cb2-caf3b22574fe\"} --\u003e 🔍 Trigger review\n\n\u003c!-- end of auto-generated comment: skip review by coderabbit.ai --\u003e\n\n\u003c!-- autopilot:start --\u003e\n- [ ] \u003c!-- {\"checkboxId\":\"2708ad07-9f24-4260-9c11-7dc76a49f2e3\"} --\u003e \u003cstrong title=\"Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts\"\u003eAutopilot\u003c/strong\u003e · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts\n\u003c!-- autopilot:end --\u003e\n\u003c!-- tips_start --\u003e\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss\u0026utm_medium=github\u0026utm_campaign=AgentWorkforce/flows\u0026utm_content=614)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n\u003cdetails\u003e\n\u003csummary\u003e❤️ Share\u003c/summary\u003e\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A\u0026url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit\u0026text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai\u0026mini=true\u0026title=Great%20tool%20for%20code%20review%20-%20CodeRabbit\u0026summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n\u003c/details\u003e\n\n\n\u003csub\u003eComment `@coderabbitai help` to get the list of available commands.\u003c/sub\u003e\n\n\u003c!-- tips_end --\u003e","url":"https://github.com/AgentWorkforce/flows/pull/614#issuecomment-5993506891"}
gh api --paginate repos/AgentWorkforce/flows/pulls/614/comments[]gh api --paginate repos/AgentWorkforce/flows/pulls/614/reviews[] |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 445ee43a8a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…are-garden-8b26a3c4 Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
The size+errno signature for unreadable files made a same-size rewrite through an open descriptor look unchanged, so a completed step could report an incomplete artifact list. That change was unrelated to the login refresh; this restores main's behaviour (the scan refuses with EACCES) and adds the reviewer's same-size-rewrite regression. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
…place a refresh outcome - runInCloud prepares (validates) the submission before resolving the connection, so an invalid spec or unsupported source never posts the refresh token or rotates the credential file. - refreshCloudLogin no longer returns from `finally`: a failed lock release cannot turn a persisted rotation into auth_store_unwritable or swallow a cancellation. A leftover lock is reclaimed through the stale window. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
|
Addressed review feedback (merged origin/main first):
Local: cloud-auth-refresh, cloud-deploy, cloud-read, cloud-run, cloud-mirror-session, agent-artifacts → 293 passed; typecheck + typecheck:tests clean. |
|
@coderabbitai review |
|
@codex review |
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 72e56c4. Configure here.
…missing run Read CLIs map any http_error 404 to cloud_run_not_found, so an expired login whose refresh route answered 404 was reported as an unknown run. Refresh statuses a poll may retry (408/429/5xx) stay http_error; any other status is now a configuration refusal (auth_refresh_failed) that names the re-login remedy. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
|
@codex review |
|
Codex Review: Didn't find any major issues. 🎉 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |

Expired agent-relay access tokens now refresh through
/api/v1/auth/token/refreshbefore Cloud requests, hosted submissions, and mirror resume lookup. The rotated access and refresh tokens are persisted atomically at mode0600, under the relay-compatible directory lock. ExplicittokenandFLOWS_CLOUD_TOKENprecedence is preserved, including empty explicit credentials.The store is re-read under the lock; concurrent callers reuse a completed rotation or post the latest refresh token. Unknown top-level fields survive writes, and all filesystem paths honour
AGENT_RELAY_HOME. Refresh requests enforce the existing HTTPS/base-path policy, stay on the issuing deployment, refuse redirects, and produce no stdout. A server-selected base URL must also identify the same deployment.Credential rejection (400/401/403), malformed responses, transport failures, other HTTP statuses, lock contention, and filesystem errors retain distinct classifications. Persistence failure refuses with
auth_store_unwritable, naming the path and errno before any authenticated request uses the new token. Lock acquisition is bounded by five seconds and the request timeout; the shared stale-lock window remains 30 seconds.This implements the file contract locally because flows has no runtime dependency on
@agent-relay/cloud; invoking another CLI would require its binary and couple credential handling to its output. The existing mirror registration catch remains responsible for reporting projection errors without failing a local run. Normal store-backed requests read the small file twice; there is deliberately no cache hiding relay-side rotations.There is no proactive renewal or renewal triggered by a non-refresh request's 401. Long polling can renew on the first request after the stored expiry. The standalone store reader in
workflows/stuck-run-triage.flow.tsis outside this change. No workflow or gate files were edited.Validation: 280 targeted tests passed, including 44 new store/refresh cases, CLI JSON logs, and hosted submission. Both TypeScript checks passed. The original expired-login refusal cases in
cloud-read.test.tsandcloud-deploy.test.tsare unchanged and included in that run. Mutation checks detected both discarded refresh-token rotation and a removed explicit-credential bypass; both source files were restored byte-for-byte and the selected tests passed again.Full-suite verification remains blocked:
npm teststopped intest:prepbecause rustup has no configured default toolchain. Its Vitest phase did not run. No live credentials oragent-relay cloud whoamiwere used.Literal commands and captured output follow. Test commands ran from
packages/sdk.Targeted regression suite
TypeScript source and type tests
TypeScript regression-test compilation
Full gate — blocked during setup
npm testThe mutation procedure temporarily replaced
refreshToken: payload.refreshTokenwithrefreshToken: login.refreshToken, ran the rotation test, restored the original bytes, and re-ran. It then removedif (explicitCloudToken(options) !== undefined) return;, ran the precedence tests, restored the original bytes, and re-ran. Both mutations exited 1; both restored runs exited 0.rotation — mutated failure
npx vitest run tests/cloud-auth-refresh.test.ts -t 'persists rotation'rotation — restored pass
npx vitest run tests/cloud-auth-refresh.test.ts -t 'persists rotation'precedence — mutated failure
npx vitest run tests/cloud-auth-refresh.test.ts -t 'explicit precedence'precedence — restored pass
npx vitest run tests/cloud-auth-refresh.test.ts -t 'explicit precedence'Unchanged-test comparison, run from the repository root:
Relay contract evidence is from the installed
@agent-relay/cloud@12.4.1, not a dependency added to this repository. The following are literal excerpts from that version; line numbers refer to the publisheddist/files.@agent-relay/cloud@12.4.1 dist/types.js:14:@agent-relay/cloud@12.4.1 dist/auth.js:14:@agent-relay/cloud@12.4.1 dist/auth.js:62:@agent-relay/cloud@12.4.1 dist/auth.js:97:@agent-relay/cloud@12.4.1 dist/auth.js:203:@agent-relay/cloud@12.4.1 dist/auth.js:448:@agent-relay/cloud@12.4.1 dist/auth.js:464:@agent-relay/cloud@12.4.1 dist/api-client.js:9:Checks
Relayflow ran this repository's checks (.relayflow/check.sh) and they passed.
What ran (.relayflow/check.sh)
Fixes #464
Note
High Risk
Changes authentication credential lifecycle, refresh-token handling, and on-disk token persistence with locking—security-sensitive paths where bugs could leak tokens, skip rotation, or leave callers in a bad auth state.
Overview
Expired
agent-relay cloud loginsessions can renew automatically instead of always failing with a re-login prompt. Before authenticated Cloud traffic, the SDK checks stored access-token expiry and, when a valid refresh token exists, calls/api/v1/auth/token/refresh, then atomically persists rotated tokens under the relay-compatible lock atcloud-auth.json(mode0600, honouringAGENT_RELAY_HOME).cloudFetchand callers now go through asyncresolveCloudConnection, which refreshes then re-reads the store (no in-memory cache).FLOWS_CLOUD_TOKEN/ explicittokenstill bypass the store entirely.runInCloudprepares/validates the submission before refresh so bad flows never post a refresh token. Mirror resume lookup uses the same resolver for deployment-aligned base URLs.New configuration outcomes include
auth_store_unwritable(cannot save rotation) and refinedauth_expired/auth_refresh_failedhandling so refresh 404s are not mistaken for missing runs. Docs add thecloud_configuration (auth_store_unwritable)refusal and describe proactive expiry-based renewal (not 401-triggered).A small artifact-scan regression test ensures unreadable files are not treated as unchanged when size alone matches.
Reviewed by Cursor Bugbot for commit d8247ae. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Renews an expired agent-relay cloud login on first use so flows no longer require a re-login. Expired access tokens are renewed via
/api/v1/auth/token/refreshbefore Cloud requests, hosted submissions, and mirror resume lookup, with both rotated tokens persisted atomically at mode0600under the relay-compatible lock. ExplicittokenandFLOWS_CLOUD_TOKENcredentials still bypass the store entirely.EACCESagain, guarded by a regression test.http_error.Written for commit d8247ae. Summary will update on new commits.