Skip to content

Unify workforce cloud auth session#234

Merged
khaliqgant merged 3 commits into
mainfrom
auth-unify-2108
Jun 13, 2026
Merged

Unify workforce cloud auth session#234
khaliqgant merged 3 commits into
mainfrom
auth-unify-2108

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Bumps @agent-relay/cloud to ^8.7.0 and uses the canonical Agent Relay cloud session/workspace contract for workforce login and deploy auth.
  • Makes agentworkforce login a thin wrapper around the shared cloud session and canonical relay workspace key pin instead of writing active.json / login.json shadow state.
  • Removes deploy fallback reads from legacy workspace-token stores; preserves only complete WORKFORCE_WORKSPACE_ID + WORKFORCE_WORKSPACE_TOKEN as the explicit CI override.
  • Updates destroy/deploy behavior so legacy active.json no longer selects cloud URL/workspace state.

References AgentWorkforce/cloud#2108 and the agreed design in cloud docs/design/auth-unification-2108.md.

Validation

  • npm view @agent-relay/cloud@8.7.0 version => 8.7.0
  • pnpm install
  • pnpm --filter @agentworkforce/deploy test ✅ (197/197)
  • pnpm --filter @agentworkforce/cli build
  • pnpm --filter @agentworkforce/cli exec node --test dist/deploy-command.test.js dist/destroy-command.test.js ✅ (19/19)
  • git diff --check
  • Hygiene greps ✅: no file:/Users/... package refs, no agent-relay workspace use, no .agent-relay/cloud-auth.json credential path in touched CLI/deploy package sources.

Known local validation caveat: full pnpm --filter @agentworkforce/cli test still fails in local-personas.test only. I reproduced the same local-personas failure class on the untouched base branch in /tmp/workforce-auth-base-check at 74c045d, so this PR did not introduce it. CI at this PR head remains the merge gate per the cloud#2108 process.

Review in cubic

Co-Authored-By: OpenAI <codex@openai.com>
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@khaliqgant, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 42 minutes and 5 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6da3b5d4-10b5-4753-b38f-46c4eadf5aaf

📥 Commits

Reviewing files that changed from the base of the PR and between 0ffd00e and 908bf23.

📒 Files selected for processing (10)
  • packages/cli/src/deploy-command.ts
  • packages/cli/src/destroy-command.test.ts
  • packages/cli/src/list-command.ts
  • packages/deploy/src/cloud-url.test.ts
  • packages/deploy/src/cloud-url.ts
  • packages/deploy/src/deploy.test.ts
  • packages/deploy/src/deploy.ts
  • packages/deploy/src/integrations-list.ts
  • packages/deploy/src/login.test.ts
  • packages/deploy/src/login.ts
📝 Walkthrough

Walkthrough

This PR upgrades @agent-relay/cloud to v8.7.0 and refactors workspace authentication across CLI and deploy packages to use cloud session APIs and cloud-based workspace resolution instead of file-based active-workspace pointers and legacy shared-auth mechanisms.

Changes

Cloud session auth migration

Layer / File(s) Summary
Dependency updates
packages/cli/package.json, packages/deploy/package.json
Both packages bump @agent-relay/cloud from ^6.0.17 to ^8.7.0.
Deploy login.ts core auth refactor
packages/deploy/src/login.ts, packages/deploy/src/deploy.ts
WorkspaceAuthToken extended with optional workspace identity and descriptor fields. ensureCloudSession replaces multi-tier auth resolution; workspace token storage APIs deprecated to no-ops/throws. Legacy active-workspace pointer functions return null or no-op. resolveWorkspaceToken rewritten to canonicalize cloud URL, use env credentials as early exit, call ensureCloudSession, resolve workspace descriptor via cloud API, and return session token plus workspace fields.
Deploy login test validation
packages/deploy/src/login.test.ts, packages/deploy/src/deploy.test.ts
Test environment helper switched to withWorkspaceEnv capturing WORKFORCE_* and CLOUD_API_* variables. New test cases verify CI credential preservation, env token resolution, and legacy API isolation (reads return null, writes throw instruction, clears no-op). Deploy test error messages updated to match new "agent-relay workspace" terminology.
CLI deploy/destroy command auth integration
packages/cli/src/deploy-command.ts, packages/cli/src/destroy-command.ts
Deploy-command updated to use ensureCloudSession and setWorkspaceKey; auth recovery calls new session API with force: true, interactive: true; login flow calls resolveWorkspaceForLogin to fetch workspace descriptor and pin via setWorkspaceKey. Destroy-command removed call to readActiveWorkspace for cloud URL fallback; now uses only --cloud-url flag and internal resolveCloudUrl fallbacks.
CLI command test validation
packages/cli/src/deploy-command.test.ts, packages/cli/src/destroy-command.test.ts
Deploy-command tests mock ensureCloudSession, cloud workspace resolve endpoint calls, and setWorkspaceKey pinning; error-path tests verify guards prevent key pinning on auth/listing failures. Destroy-command tests updated to assert error messages reference "agent-relay workspace", confirm legacy active.json cloudUrl is ignored when flag/env not set, and verify canonical cloud URL used for requests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • AgentWorkforce/workforce#118: Prior PR extended destroy-command/list to consult readActiveWorkspace for cloud URL fallback, which this PR now removes to disable legacy auth fallbacks entirely.

Poem

🐰 Cloud sessions bloom in morning light,
No files to shuffle, no auth to fight—
Workspace keys now flow from APIs above,
Cleaner paths and sessions we love!
The relay marches on, spring so bright. 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.23% 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
Title check ✅ Passed The title 'Unify workforce cloud auth session' is concise and directly describes the main architectural change—adopting a unified canonical cloud session pattern for workforce authentication.
Description check ✅ Passed The description is comprehensive and on-topic, explaining the migration to @agent-relay/cloud@8.7.0, removal of legacy shadow state, and validation steps performed.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch auth-unify-2108

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 and usage tips.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request migrates workspace and authentication management from a custom local active.json and legacy keychain files to the canonical @agent-relay/cloud SDK session and active workspace store. It updates dependencies, refactors CLI commands (login, logout, destroy), and updates the deployment orchestrator's token resolution logic. Feedback on the changes suggests awaiting setWorkspaceKey before exiting the process to prevent premature termination, safely catching potential JSON parsing errors when resolving workspace descriptors, and ensuring the key field is validated in normalizeWorkspaceDescriptor to avoid silent authentication failures.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/cli/src/deploy-command.ts Outdated
Comment on lines 179 to 181
deployCommandDeps.setWorkspaceKey(workspaceName, descriptor.key);
process.stdout.write(`\nlogged in: ${workspaceName}\n`);
process.exit(0);

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

If setWorkspaceKey performs asynchronous I/O (such as writing to a configuration file or keychain) and returns a Promise, calling process.exit(0) immediately afterward without awaiting it can terminate the Node.js process prematurely. This can result in incomplete writes or silent persistence failures. We should await the call to ensure the workspace key is fully persisted before exiting.

Suggested change
deployCommandDeps.setWorkspaceKey(workspaceName, descriptor.key);
process.stdout.write(`\nlogged in: ${workspaceName}\n`);
process.exit(0);
await deployCommandDeps.setWorkspaceKey(workspaceName, descriptor.key);
process.stdout.write("\nlogged in: " + workspaceName + "\n");
process.exit(0);

Comment thread packages/deploy/src/login.ts Outdated
const text = await response.text().catch(() => '');
throw new Error(`workspace resolve failed for ${workspace}: ${response.status} ${text}`.trim());
}
return normalizeWorkspaceDescriptor(await response.json(), session.auth.apiUrl || args.apiUrl);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If the server returns a non-JSON response or an empty body, calling response.json() directly will throw a raw SyntaxError. We should safely parse the JSON with a .catch(() => null) and let normalizeWorkspaceDescriptor handle the invalid payload gracefully, similar to how it is done in resolveWorkspaceForLogin in packages/cli/src/deploy-command.ts.

  const payload = await response.json().catch(() => null);
  return normalizeWorkspaceDescriptor(payload, session.auth.apiUrl || args.apiUrl);

Comment thread packages/deploy/src/login.ts Outdated
Comment on lines +189 to +191
if (!relaycastWorkspaceId || !relayfileWorkspaceId || !relayauthWorkspaceId) {
throw new Error('workspace resolve returned an incomplete descriptor');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The validation check for an incomplete workspace descriptor does not verify if key is present, unlike the corresponding check in resolveWorkspaceForLogin within packages/cli/src/deploy-command.ts. If key is missing or empty, it will be silently set to an empty string, which could lead to authentication failures later. We should ensure key is validated as well.

Suggested change
if (!relaycastWorkspaceId || !relayfileWorkspaceId || !relayauthWorkspaceId) {
throw new Error('workspace resolve returned an incomplete descriptor');
}
if (!key || !relaycastWorkspaceId || !relayfileWorkspaceId || !relayauthWorkspaceId) {
throw new Error('workspace resolve returned an incomplete descriptor');
}

Co-Authored-By: OpenAI <codex@openai.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/cli/src/deploy-command.ts (2)

163-175: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor WORKFORCE_WORKSPACE_ID before listing workspaces.

LOGIN_USAGE documents the env var as the default, but this branch only checks opts.workspace. On accounts that cannot list /api/v1/workspaces, agentworkforce login will still take the 403 failure path instead of resolving the preconfigured workspace.

Suggested fix
-    if (opts.workspace) {
-      chosen = opts.workspace;
+    const requestedWorkspace = opts.workspace ?? process.env.WORKFORCE_WORKSPACE_ID?.trim();
+    if (requestedWorkspace) {
+      chosen = requestedWorkspace;
     } else {
       workspaces = await listWorkspacesForLogin(auth, apiUrl);
       if (workspaces.length === 0) {
         throw new Error(
           'no workspaces are accessible from this account. Create one at https://agentrelay.com/cloud, '
🤖 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 `@packages/cli/src/deploy-command.ts` around lines 163 - 175, The code only
checks opts.workspace before calling listWorkspacesForLogin; change the branch
logic in the deploy flow (the block that assigns chosen using opts.workspace,
listWorkspacesForLogin, and pickWorkspaceInteractive) to first honor the
WORKFORCE_WORKSPACE_ID environment variable (process.env.WORKFORCE_WORKSPACE_ID)
as the default workspace if present, then fallback to opts.workspace, and only
if neither is set call listWorkspacesForLogin(auth, apiUrl) and possibly
pickWorkspaceInteractive(workspaces, io); this avoids calling the workspace list
API when a preconfigured WORKFORCE_WORKSPACE_ID is available.

197-207: ⚠️ Potential issue | 🟠 Major

Fix runLogout so it actually clears the canonical workspace selection written by runLogin

runLogin persists the active workspace via deployCommandDeps.setWorkspaceKey(...) (packages/cli/src/deploy-command.ts). But runLogout only calls clearActiveWorkspace() and clearStoredWorkspaceToken(...)—both are explicit no-ops in packages/deploy/src/login.ts because workspace selection now lives in @agent-relay/cloud’s canonical workspace store. As a result, agentworkforce logout can print workspace login cleared without clearing the canonical workspace pin (unless --cloud-auth happens to clear stored cloud auth).

  • packages/cli/src/deploy-command.ts#L190-L207: update runLogout to call the appropriate @agent-relay/cloud API to clear the workspace key/pointer written by setWorkspaceKey (or adjust messaging/flags if no such clearing exists).
  • packages/cli/src/deploy-command.test.ts#L262-L285, #L287-L310, #L312-L335: update these tests to assert the canonical workspace-clearing behavior (they currently only verify the CLI-level deps are invoked, but the real implementations are no-ops).
🤖 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 `@packages/cli/src/deploy-command.ts` around lines 197 - 207, Update runLogout
so it clears the canonical workspace pointer in `@agent-relay/cloud` (the same
canonical key that runLogin writes via setWorkspaceKey). In
packages/cli/src/deploy-command.ts (lines 197-207) import and invoke the cloud
workspace-clearing API (e.g., cloud.clearWorkspaceKey(...) or
cloud.setWorkspaceKey(null) — whichever the cloud package exposes) in addition
to the existing deployCommandDeps.clearActiveWorkspace() and
clearStoredWorkspaceToken(opts.workspace), and adjust the final message if
needed; keep references to deployCommandDeps.clearActiveWorkspace and
deployCommandDeps.clearStoredWorkspaceToken. In
packages/cli/src/deploy-command.test.ts (lines 262-285, 287-310, 312-335) update
the tests to mock/spy the `@agent-relay/cloud` workspace API and assert that the
canonical workspace-clearing call is invoked when running logout (replace or
augment the existing assertions that only check the CLI-level deps).
🧹 Nitpick comments (1)
packages/deploy/src/login.ts (1)

160-163: ⚡ Quick win

Redundant session initialization.

ensureCloudSession is called here inside resolveWorkspaceDescriptor, but the parent function resolveWorkspaceToken already calls it on line 129. Consider passing the existing session as a parameter to avoid the redundant initialization.

♻️ Refactor to reuse the session

Update the function signature to accept a session parameter:

 async function resolveWorkspaceDescriptor(args: {
   requestedWorkspace?: string;
   apiUrl: string;
+  session: { auth: StoredAuth; client: { fetch: (path: string, init?: RequestInit) => Promise<Response> } };
 }): Promise<ActiveWorkspaceDescriptor> {
   const workspace = args.requestedWorkspace?.trim();
   if (!workspace) {
     return resolveActiveWorkspace({
       apiUrl: args.apiUrl,
       interactive: false
     });
   }

-  const session = await ensureCloudSession({
-    apiUrl: args.apiUrl,
-    interactive: false
-  });
-  const response = await session.client.fetch(
+  const response = await args.session.client.fetch(
     `/api/v1/workspaces/${encodeURIComponent(workspace)}/resolve`,
     { method: 'GET' }
   );
   if (!response.ok) {
     const text = await response.text().catch(() => '');
     throw new Error(`workspace resolve failed for ${workspace}: ${response.status} ${text}`.trim());
   }
-  return normalizeWorkspaceDescriptor(await response.json(), session.auth.apiUrl || args.apiUrl);
+  return normalizeWorkspaceDescriptor(await response.json(), args.session.auth.apiUrl || args.apiUrl);
 }

Then update the call site on line 135:

   const descriptor = await resolveWorkspaceDescriptor({
     requestedWorkspace: requestedWorkspace || envWorkspace,
-    apiUrl: session.auth.apiUrl || cloudUrl
+    apiUrl: session.auth.apiUrl || cloudUrl,
+    session
   });
🤖 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 `@packages/deploy/src/login.ts` around lines 160 - 163,
resolveWorkspaceDescriptor currently calls ensureCloudSession redundantly;
change its signature to accept an existing Session (e.g., add a parameter named
session), remove the internal call to ensureCloudSession inside
resolveWorkspaceDescriptor, and use the passed session where needed. Then update
the caller resolveWorkspaceToken to pass the session it already obtains (from
its ensureCloudSession call) into resolveWorkspaceDescriptor. Ensure
function/method names referenced are resolveWorkspaceDescriptor,
resolveWorkspaceToken, and ensureCloudSession and update all call sites
accordingly.
🤖 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 `@packages/cli/src/deploy-command.ts`:
- Around line 501-529: The resolveWorkspaceForLogin function currently only
validates key and relaycastWorkspaceId but must match the deploy contract by
also requiring relayfileWorkspaceId and relayauthWorkspaceId; update
resolveWorkspaceForLogin to read these fields from the parsed record (use
readString(record, 'relayfileWorkspaceId') and readString(record,
'relayauthWorkspaceId')), include them in the returned LoginWorkspaceDescriptor
(when present) and throw the same "incomplete descriptor" error if either is
missing so partially provisioned workspaces are rejected upfront.
- Around line 123-133: The recovery path in createDeployAuthRecovery().recover()
logs the active workspace but never persists it, which can leave subsequent
deploy/destroy flows without a pinned workspace; after obtaining session and
computing activeWorkspace (the same value logged), call the same
workspace-persistence used in runLogin() (setWorkspaceKey) via
deployCommandDeps.setWorkspaceKey(activeWorkspace) before returning the token;
additionally, update parseLoginArgs() to treat
process.env.WORKFORCE_WORKSPACE_ID as the default when --workspace is not
provided, and ensure resolveWorkspaceForLogin() returns/normalizes both
relayfileWorkspaceId and relayauthWorkspaceId (not just key +
relaycastWorkspaceId) so its payload matches `@agentworkforce/deploy`’s expected
descriptor contract.

---

Outside diff comments:
In `@packages/cli/src/deploy-command.ts`:
- Around line 163-175: The code only checks opts.workspace before calling
listWorkspacesForLogin; change the branch logic in the deploy flow (the block
that assigns chosen using opts.workspace, listWorkspacesForLogin, and
pickWorkspaceInteractive) to first honor the WORKFORCE_WORKSPACE_ID environment
variable (process.env.WORKFORCE_WORKSPACE_ID) as the default workspace if
present, then fallback to opts.workspace, and only if neither is set call
listWorkspacesForLogin(auth, apiUrl) and possibly
pickWorkspaceInteractive(workspaces, io); this avoids calling the workspace list
API when a preconfigured WORKFORCE_WORKSPACE_ID is available.
- Around line 197-207: Update runLogout so it clears the canonical workspace
pointer in `@agent-relay/cloud` (the same canonical key that runLogin writes via
setWorkspaceKey). In packages/cli/src/deploy-command.ts (lines 197-207) import
and invoke the cloud workspace-clearing API (e.g., cloud.clearWorkspaceKey(...)
or cloud.setWorkspaceKey(null) — whichever the cloud package exposes) in
addition to the existing deployCommandDeps.clearActiveWorkspace() and
clearStoredWorkspaceToken(opts.workspace), and adjust the final message if
needed; keep references to deployCommandDeps.clearActiveWorkspace and
deployCommandDeps.clearStoredWorkspaceToken. In
packages/cli/src/deploy-command.test.ts (lines 262-285, 287-310, 312-335) update
the tests to mock/spy the `@agent-relay/cloud` workspace API and assert that the
canonical workspace-clearing call is invoked when running logout (replace or
augment the existing assertions that only check the CLI-level deps).

---

Nitpick comments:
In `@packages/deploy/src/login.ts`:
- Around line 160-163: resolveWorkspaceDescriptor currently calls
ensureCloudSession redundantly; change its signature to accept an existing
Session (e.g., add a parameter named session), remove the internal call to
ensureCloudSession inside resolveWorkspaceDescriptor, and use the passed session
where needed. Then update the caller resolveWorkspaceToken to pass the session
it already obtains (from its ensureCloudSession call) into
resolveWorkspaceDescriptor. Ensure function/method names referenced are
resolveWorkspaceDescriptor, resolveWorkspaceToken, and ensureCloudSession and
update all call sites accordingly.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 6d6a7a20-c199-4b70-a7c3-80537a3afccb

📥 Commits

Reviewing files that changed from the base of the PR and between 74c045d and 0ffd00e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • packages/cli/package.json
  • packages/cli/src/deploy-command.test.ts
  • packages/cli/src/deploy-command.ts
  • packages/cli/src/destroy-command.test.ts
  • packages/cli/src/destroy-command.ts
  • packages/deploy/package.json
  • packages/deploy/src/deploy.test.ts
  • packages/deploy/src/deploy.ts
  • packages/deploy/src/login.test.ts
  • packages/deploy/src/login.ts

Comment on lines 123 to 133
io.info(`cloud: starting login because integration auth failed (${reason})`);
const auth = await deployCommandDeps.ensureAuthenticated(cloudUrl, { force: true });
const apiUrl = normalizeCloudUrl(auth.apiUrl || cloudUrl);
const activeWorkspace = opts.workspace ?? workspace;
await deployCommandDeps.writeActiveWorkspace({
workspace: activeWorkspace,
cloudUrl: apiUrl
const session = await deployCommandDeps.ensureCloudSession({
apiUrl: cloudUrl,
force: true,
interactive: true
});
const activeWorkspace = opts.workspace ?? workspace;
io.info(`cloud: logged in for workspace ${activeWorkspace}; retrying integration check`);
return {
token: auth.accessToken
token: session.auth.accessToken
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and display relevant sections in the CLI deploy command
file="packages/cli/src/deploy-command.ts"
echo "=== $file (context around 100-220) ==="
sed -n '80,240p' "$file" | cat -n

# Find and show createDeployAuthRecovery / recover() region
echo
echo "=== $file (search: createDeployAuthRecovery, recover, runLogin, setWorkspaceKey) ==="
rg -n "createDeployAuthRecovery|recover\(|runLogin|setWorkspaceKey|resolveWorkspaceForLogin|runLogout|clearActiveWorkspace|LOGIN_USAGE|workspace" "$file"

# Inspect deploy-layer login implementation for resolve/persist behavior
depfile="packages/deploy/src/login.ts"
echo
echo "=== $depfile (context around workspace resolve/persist) ==="
ls -la "$depfile" 2>/dev/null || true
if [ -f "$depfile" ]; then
  rg -n "resolve.*workspace|setWorkspaceKey|clearActiveWorkspace|workspace.*key|resolveWorkspaceForLogin|/resolve" "$depfile"
  sed -n '1,260p' "$depfile" | cat -n
fi

# Inspect any helper implementations for resolveWorkspaceForLogin and workspace pinning
echo
echo "=== Search for resolveWorkspaceForLogin and setWorkspaceKey definitions in repo ==="
rg -n "function resolveWorkspaceForLogin|resolveWorkspaceForLogin\s*=\s*|setWorkspaceKey\s*\(" packages | head -n 50

# Show where setWorkspaceKey is imported/used in CLI
echo
echo "=== $file imports (top of file) ==="
sed -n '1,120p' "$file" | cat -n

Repository: AgentWorkforce/workforce

Length of output: 30778


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="packages/cli/src/deploy-command.ts"

echo "=== $file (context around LOGIN_USAGE workspace default text) ==="
sed -n '220,290p' "$file" | cat -n

echo
echo "=== $file (context around parseLoginArgs env handling) ==="
sed -n '290,380p' "$file" | cat -n

echo
echo "=== $file (context around resolveWorkspaceForLogin implementation) ==="
sed -n '470,560p' "$file" | cat -n

echo
echo "=== $file (context around LOGIN_USAGE full section for --workspace default) ==="
rg -n "LOGIN_USAGE|--workspace <name>.*WORKFORCE_WORKSPACE_ID|--workspace <name>\\s+Workforce workspace" "$file"

Repository: AgentWorkforce/workforce

Length of output: 13043


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="packages/cli/src/deploy-command.ts"

echo "=== Search WORKFORCE_WORKSPACE_ID/WORKFORCE_WORKSPACE_TOKEN usage in $file ==="
rg -n "WORKFORCE_WORKSPACE_ID|WORKFORCE_WORKSPACE_TOKEN" "$file" || true

echo
echo "=== Show parseLoginArgs implementation ==="
rg -n "function parseLoginArgs" "$file"
start=$(rg -n "function parseLoginArgs" "$file" | cut -d: -f1 | head -n1)
sed -n "$((start-10)),$((start+80))p" "$file" | cat -n

echo
echo "=== Show any env-based workspace default used before listWorkspacesForLogin ==="
rg -n "listWorkspacesForLogin|WORKFORCE_WORKSPACE_ID|resolveWorkspaceForLogin|opts\\.workspace" "$file" | head -n 100

Repository: AgentWorkforce/workforce

Length of output: 4693


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="packages/cli/src/deploy-command.ts"
echo "=== Search normalizeCloudUrl in $file ==="
rg -n "normalizeCloudUrl" "$file"

Repository: AgentWorkforce/workforce

Length of output: 345


Pin the workspace during deploy auth recovery.

createDeployAuthRecovery().recover() re-authenticates and logs activeWorkspace, but it never resolves/persists the canonical workspace key (unlike runLogin() which calls setWorkspaceKey). This can leave the next default deploy/destroy path without an active workspace pin.

  • Also, login help claims --workspace defaults to WORKFORCE_WORKSPACE_ID or prompt, but parseLoginArgs() reads only argv—env won’t bypass the /api/v1/workspaces 403 flow.
  • resolveWorkspaceForLogin() accepts /resolve payloads with only key + relaycastWorkspaceId, while @agentworkforce/deploy’s normalization requires relayfileWorkspaceId and relayauthWorkspaceId, so descriptor contract drift can slip through and fail later.
Suggested fix
       const session = await deployCommandDeps.ensureCloudSession({
         apiUrl: cloudUrl,
         force: true,
         interactive: true
       });
       const activeWorkspace = opts.workspace ?? workspace;
+      if (activeWorkspace) {
+        const apiUrl = canonicalizeCloudUrl(normalizeCloudUrl(session.auth.apiUrl || cloudUrl));
+        const descriptor = await resolveWorkspaceForLogin(session.auth, apiUrl, activeWorkspace);
+        deployCommandDeps.setWorkspaceKey(
+          descriptor.name ?? descriptor.slug ?? activeWorkspace,
+          descriptor.key
+        );
+      }
       io.info(`cloud: logged in for workspace ${activeWorkspace}; retrying integration check`);
       return {
         token: session.auth.accessToken
       };
🤖 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 `@packages/cli/src/deploy-command.ts` around lines 123 - 133, The recovery path
in createDeployAuthRecovery().recover() logs the active workspace but never
persists it, which can leave subsequent deploy/destroy flows without a pinned
workspace; after obtaining session and computing activeWorkspace (the same value
logged), call the same workspace-persistence used in runLogin()
(setWorkspaceKey) via deployCommandDeps.setWorkspaceKey(activeWorkspace) before
returning the token; additionally, update parseLoginArgs() to treat
process.env.WORKFORCE_WORKSPACE_ID as the default when --workspace is not
provided, and ensure resolveWorkspaceForLogin() returns/normalizes both
relayfileWorkspaceId and relayauthWorkspaceId (not just key +
relaycastWorkspaceId) so its payload matches `@agentworkforce/deploy`’s expected
descriptor contract.

Comment thread packages/cli/src/deploy-command.ts
@agent-relay-code

Copy link
Copy Markdown
Contributor

I reviewed the checked-out PR artifacts for AgentWorkforce/cloud PR #2112. The prompt named AgentWorkforce/workforce PR #234, but .workforce/context.json points to cloud #2112, so I used the local PR metadata.

No code changes are left in the working tree from my review. I validated Gemini’s finding and attempted the minimal fix, but dependency installation was repeatedly terminated before I could run CI-style verification, so I reverted the unverified edits per your instruction.

Findings

packages/web/lib/integrations/provider-status.ts:299
liveNangoInitialSyncSucceeded can throw if called with a malformed schedule object, for example { ok: true } or a syncs array containing null. The function is a resilience path around live Nango status, so it should defensively check Array.isArray(schedules.syncs) and use sync?.status.

Addressed comments

  • gemini-code-assist[bot] on packages/web/lib/integrations/provider-status.ts:299: valid finding. I did not leave the fix because npm ci, npm ci --ignore-scripts, and a narrow test-tool install were all terminated before tests/typecheck could run.
  • coderabbitai[bot]: rate-limit notice only; no code issue to address.
  • agent-relay-code[bot] comment 4698323934: review harness exited 137; no concrete code issue to validate.
  • agent-relay-code[bot] comment 4698341112: duplicate harness-exit notice; no concrete code issue to validate.
  • agent-relay-code[bot] comment 4698359631: duplicate harness-exit notice; no concrete code issue to validate.

Advisory Notes

Minimal proposed fix, once CI can run:

export function liveNangoInitialSyncSucceeded(
  schedules: {
    ok?: boolean;
    syncs?: readonly ({ status?: string | null } | null | undefined)[];
  } | null,
): boolean {
  return (
    schedules?.ok === true &&
    Array.isArray(schedules.syncs) &&
    schedules.syncs.length > 0 &&
    schedules.syncs.every((sync) => sync?.status === "SUCCESS")
  );
}

Add tests for { ok: true } and [null, { status: "SUCCESS" }] returning false.

Verification was not completed: local dependency installation was killed during package extraction, so I could not run the focused Vitest files, typecheck, or canonical CI commands.

Co-Authored-By: OpenAI <codex@openai.com>
@khaliqgant khaliqgant merged commit 1858da9 into main Jun 13, 2026
3 checks passed
@khaliqgant khaliqgant deleted the auth-unify-2108 branch June 13, 2026 11:33
@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented the PR-scoped fixes for the auth/workspace changes and verified the current checkout with the repo’s build/test path. I used corepack pnpm because the bare pnpm shim is not available on PATH here.

Addressed comments

  • Gemini Code Assist: setWorkspaceKey needed to be awaited before process.exit; fixed in deploy-command.ts and deploy-command.ts.
  • Gemini Code Assist: invalid JSON from workspace resolve should flow through descriptor normalization; fixed in login.ts.
  • Gemini Code Assist: descriptor completeness check missed key; fixed in login.ts.
  • CodeRabbit: auth recovery after deploy login should pin the canonical relay workspace key; fixed in deploy-command.ts.
  • CodeRabbit: agentworkforce login help promised WORKFORCE_WORKSPACE_ID, but parsing ignored it; fixed in deploy-command.ts with coverage in deploy-command.test.ts.
  • CodeRabbit: CLI workspace resolve descriptors must require relay service IDs; fixed in deploy-command.ts and deploy-command.ts, with coverage in deploy-command.test.ts.
  • CodeRabbit: deploy login path redundantly called ensureCloudSession; fixed by reusing the existing session in login.ts and login.ts.
  • CodeRabbit: logout should clear the canonical workspace pointer; no code change. Current @agent-relay/cloud@8.7.0 exposes setters/read-write store helpers but no supported clear-workspace API, and hand-editing SDK storage would be outside this PR’s auth-unification scope.
  • CodeRabbit: rate-limit/walkthrough/status comments; no code change, informational only.
  • CodeRabbit: docstring coverage warning; no code change, broad quality threshold outside this PR’s changed auth/workspace behavior.
  • agent-relay-code[bot]: comment referenced AgentWorkforce/cloud PR #2112 and files not present in this checkout; no code change, invalid for this PR.

Advisory Notes

  • If logout must clear the canonical relay workspace pointer, add or consume a supported clear API in @agent-relay/cloud in a separate focused change. I did not invent store mutation semantics inside this PR.
  • The docstring coverage warning is a repo-wide quality gate concern, not a demonstrated regression in this PR’s changed files.

Verification

Passed locally:

  • corepack pnpm install --frozen-lockfile
  • corepack pnpm -r run build
  • corepack pnpm -r lint
  • corepack pnpm -r typecheck
  • corepack pnpm exec tsc -p examples/tsconfig.json --noEmit
  • corepack pnpm -r test

I also updated two affected expectations to match the current cloud-session-first auth contract without weakening assertions: deploy.test.ts and destroy-command.test.ts.

@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented a scoped fix for the PR’s workspace-resolve path.

The issue I found: explicit workspace resolution used custom payload normalization that only accepted flat /workspaces/:id/resolve responses. The upgraded @agent-relay/cloud SDK accepts wrapped { workspace: ... } descriptors and aliases like workspaceKey, id, and organization_id, so the PR could fail against a valid cloud response.

Fixed in:

Added/updated coverage:

Addressed comments

  • No bot or reviewer comments were present in the supplied .workforce/context.json or other .workforce files.
  • Self-review: explicit workspace resolve descriptors did not accept the SDK-compatible wrapped payload/alias shape; fixed in packages/deploy/src/login.ts:181 and packages/cli/src/deploy-command.ts:516.

Advisory Notes

None.

Local validation run in CI workflow order:

  • pnpm -r run build
  • pnpm run lint
  • pnpm run typecheck
  • pnpm run test

All passed locally. Note: the sandbox Node version is v25.6.0; the GitHub workflow pins Node 22.14.0, so I’m not printing READY because I cannot confirm remote check status or mergeability from here.

@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented one PR-scoped fix: agentworkforce login now calls setActiveWorkspace() after setWorkspaceKey(), so logging into a workspace actually makes it active in the canonical Agent Relay workspace store.

Addressed comments

  • Codex review: setWorkspaceKey() only initializes active when none exists, so login could store a new workspace key without switching to it. Fixed in deploy-command.ts and covered in deploy-command.test.ts.
  • External bot/reviewer comments: none were available in .workforce metadata; only pr.diff, changed-files.txt, and context.json were present.

Advisory Notes

None.

Validation run locally:

  • pnpm -r run build
  • pnpm run lint
  • pnpm run typecheck
  • pnpm run test
  • pnpm run check

All completed successfully.

@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented PR-scoped fixes for the auth/workspace migration.

Addressed comments

  • Gemini Code Assist: setWorkspaceKey must be awaited before exit. Current checkout already awaited it; I kept that and added setActiveWorkspace after it in packages/cli/src/deploy-command.ts:188.
  • Gemini Code Assist: invalid workspace-resolve JSON should fail through descriptor validation; fixed/covered in packages/deploy/src/login.ts:176 and packages/deploy/src/login.test.ts:123.
  • Gemini Code Assist: descriptor completeness must include key; fixed in packages/deploy/src/login.ts:187.
  • CodeRabbit: deploy auth recovery should pin the canonical relay workspace key; fixed in packages/cli/src/deploy-command.ts:133.
  • CodeRabbit: agentworkforce login help mentions WORKFORCE_WORKSPACE_ID, but parsing ignored it; fixed in packages/cli/src/deploy-command.ts:425 with coverage in packages/cli/src/deploy-command.test.ts:200.
  • CodeRabbit: CLI workspace resolve descriptors should require relay service IDs; fixed in packages/cli/src/deploy-command.ts:483 and packages/cli/src/deploy-command.ts:538.
  • CodeRabbit: requested-workspace resolution redundantly opened another cloud session; fixed by reusing the existing session in packages/deploy/src/login.ts:137 and packages/deploy/src/login.ts:164.
  • agent-relay-code[bot] 4698412979: workspace-resolve needed to accept wrapped SDK-compatible descriptors and aliases; fixed in packages/deploy/src/login.ts:180 and packages/cli/src/deploy-command.ts:530.
  • agent-relay-code[bot] 4698439852: setWorkspaceKey() does not switch active workspace when one already exists; fixed in packages/cli/src/deploy-command.ts:189 with coverage in packages/cli/src/deploy-command.test.ts:57.
  • agent-relay-code[bot] 4698385536: referenced AgentWorkforce/cloud PR #2112 and files not present in this checkout; invalid for this PR.
  • CodeRabbit rate-limit/docstring coverage notes: no code change; informational or repo-wide advisory, not a demonstrated PR regression.

Advisory Notes

  • Remote mergeability and full required-check state were not available through the current connector surface. The commit combined-status surface only reported CodeRabbit: success, so I am not printing READY.

Verification

Passed locally on the current checkout:

  • corepack pnpm install --frozen-lockfile
  • corepack pnpm -r run build
  • corepack pnpm -r lint
  • corepack pnpm -r typecheck
  • corepack pnpm exec tsc -p examples/tsconfig.json --noEmit
  • corepack pnpm -r test

@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented PR-scoped fixes for #234 and revalidated locally.

Changes made:

  • agentworkforce login now stores the workspace key and switches the canonical active Agent Relay workspace.
  • Deploy auth recovery now resolves, stores, and activates the workspace key after browser reauth.
  • Login workspace resolution now accepts SDK-compatible wrapped descriptors/aliases and rejects incomplete relay descriptors.
  • Deploy workspace resolution now reuses the existing cloud session, accepts the same descriptor aliases, and validates all required relay IDs.
  • Logout now clears the canonical active workspace pointer while preserving stored workspace keys.

Addressed comments

  • Gemini Code Assist: setWorkspaceKey must be awaited before exit. Current checkout already awaited it; kept at deploy-command.ts and added active switching at deploy-command.ts.
  • Gemini Code Assist: non-JSON workspace resolve responses should go through descriptor validation. Fixed in login.ts.
  • Gemini Code Assist: workspace descriptors must validate key. Fixed in login.ts.
  • CodeRabbit: deploy auth recovery should pin the canonical workspace key. Fixed in deploy-command.ts.
  • CodeRabbit: WORKFORCE_WORKSPACE_ID was documented for login but not parsed. Fixed in deploy-command.ts, covered in deploy-command.test.ts.
  • CodeRabbit: CLI /resolve validation must require relay service IDs. Fixed in deploy-command.ts, covered in deploy-command.test.ts.
  • CodeRabbit: logout should clear the canonical active workspace pointer. Fixed in login.ts, covered in login.test.ts.
  • agent-relay-code[bot] 4698412979: workspace resolve needed wrapped SDK-compatible descriptors and aliases. Fixed in login.ts and deploy-command.ts.
  • agent-relay-code[bot] 4698439852: setWorkspaceKey() does not switch the active workspace when one already exists. Fixed in deploy-command.ts, covered in deploy-command.test.ts.
  • agent-relay-code[bot] 4698467821 / 4698402094: combined prior review summary. Revalidated each item against current checkout; remaining valid items were fixed at the lines above.
  • agent-relay-code[bot] 4698385536: referenced AgentWorkforce/cloud PR/files not present in this checkout; invalid for this PR.
  • CodeRabbit rate-limit/walkthrough/docstring coverage comment: no code change; informational or repo-wide advisory, not a demonstrated PR regression.

Advisory Notes

  • The CodeRabbit docstring coverage warning is repo-wide and not a PR-scoped auth/workspace regression.
  • I could not verify remote GitHub required-check state or mergeability through the available connector surface, so I am not marking this READY.

Verification

Passed locally with corepack pnpm equivalents because bare pnpm is not on PATH in this sandbox:

  • corepack pnpm install --frozen-lockfile
  • corepack pnpm -r run build
  • corepack pnpm -r lint
  • corepack pnpm -r typecheck
  • corepack pnpm exec tsc -p examples/tsconfig.json --noEmit
  • corepack pnpm -r test

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