feat(sdk): re-export github primitive from root entry#823
Conversation
…l + cloud adapters
Mirrors the github-primitive's adapter shape so the same workflow file
runs locally (gh CLI / Slack token) and in cloud (Nango). Two
flagship verbs:
- postMessage — fire-and-forget human notification (PR opened,
workflow done, etc.). Resolves @-mentions and #channel names at
step time.
- askQuestion — block the workflow on a human reply. Configurable
timeout, replyMatch (regex/choice/any), allowedReplyFrom, and
Block Kit interactive forms. Resumable across sandbox restarts
via run-record metadata so retries don't re-ask.
Captures the cultural change the primitive enables: agents should ask
for clarification when blocked rather than guess. Includes two
recipes ("Announce + Done", "Ask Before You Guess") that the
writing-agent-relay-workflows skill will pick up when v1 ships.
Cloud-runtime auth reuses the workspace's existing Nango Slack
connection — no new SST resource bindings. Slack tokens don't rotate
per-call, so the proxy form (nango.proxy({ endpoint: '/chat.postMessage' }))
is the right shape — avoids the get-token-vs-proxy confusion the
github-primitive had to work through.
Phasing:
A — postMessage + resolvers
B — askQuestion (blocking, resumable)
C — Block Kit interactive forms + utility verbs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Exposes the bundled `@agent-relay/github-primitive` from the root
`@agent-relay/sdk` so workflow authors no longer need the subpath
import. Two new shapes added alongside the existing `/github` subpath:
- `import { github } from '@agent-relay/sdk'` — full namespaced surface,
no collision risk with other root exports.
- `import { createGitHubStep, GitHubClient } from '@agent-relay/sdk'` —
curated helpers for the common workflow-author path.
Avoided a flat `export *` because the primitive ships ~40 generic-named
action helpers (createFile, readFile, getUser, errorMessage, ...) that
would pollute the root namespace.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds a Slack primitive package implementation (types, actions, runtimes, adapter, client, workflow-step, tests, examples, configs), finalizes multiple trajectory artifacts with portable project/path normalization and retrospectives, appends a compacted trajectory summary for Apr 10–May 8, and updates Slack design and implementation specs. ChangesTrajectory Management
Slack Primitive Implementation
Spec Updates
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Suggested reviewers
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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 @.trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.json:
- Around line 316-326: The compacted artifact
compact_j5u7qhaw4q6a_2026-05-08.json contains absolute, machine-specific
filesystem paths embedded in strings like the entries starting with
"fix-unit-tests", "fix-regressions", and "implement-fixes"; update the code that
writes these compacted findings to sanitize/redact local paths before
serialization by (1) adding a sanitizePath utility that replaces user/home/drive
prefixes and full absolute paths with a portable placeholder (e.g.,
"<REDACTED_PATH>" or relative repo paths), (2) applying sanitizePath to any
fields used in the messages/entries (the code paths that construct those strings
for "fix-unit-tests", "update-tests", "implement-fixes", etc.) right before
writing the JSON, and (3) add tests to ensure entries no longer contain patterns
matching ^/|[A-Za-z]:\\|/Users/ or other OS-specific absolute path forms.
In @.trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.md:
- Line 27: The documentation line currently uses incorrect casing for the
product name; update the phrase "**Web-only changes gate the heavy core test,
package validation, rust CI, and node compatibility workflows**" (the
user-facing sentence in the compacted trajectory text) to use the official
product casing "GitHub" wherever the platform name appears (e.g., replace any
occurrences of "Github" or "github" with "GitHub") so the documentation uses the
correct branding.
In @.trajectories/completed/2026-05/traj_1776113772922_bc92f121.md:
- Line 34: Replace the embedded absolute path
'/Users/khaliqgant/Projects/AgentWorkforce/relay' in the committed artifact with
a neutral placeholder like REPO_ROOT or "tracked parent worktree"; locate the
offending string in the trajectory file (the line containing the cwd copy under
.relay/workspace) and update it to the placeholder, then re-commit the file so
the PR no longer contains user-specific filesystem details.
In @.trajectories/index.json:
- Around line 10-11: The index currently stores host-absolute paths in the
"path" fields of .trajectories/index.json; change the generator that writes
these entries so it stores repo-relative paths instead (compute path relative to
the repository root and write that string into the "path" key), leaving
"compactedInto" values unchanged; update the code that builds each index entry
(the logic that assigns the "path" property for trajectory entries) to use
path.relative(repoRoot, absolutePath) or equivalent and apply the same change
for all listed entries.
In `@specs/slack-primitive.md`:
- Line 52: Update the Slack token prefix example in the Slack primitive
documentation: replace the incorrect token example "xoxb-_" with the correct
"xoxb-*" in specs/slack-primitive.md (look for the string "xoxb-_" or the
sentence mentioning "bot user OAuth token (xoxb-_)") so the example uses the
standard bot token prefix "xoxb-*".
- Around line 237-258: The fenced code block that lists the package tree
(starting with the line "packages/slack-primitive/") is missing a language
identifier and triggers MD040; add a language tag (e.g., "text") right after the
opening triple backticks so the block begins with "```text" instead of "```" to
satisfy markdownlint and preserve formatting.
🪄 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: b921f08d-c615-4f17-a6cf-2078ddfb0e60
📒 Files selected for processing (16)
.trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.json.trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.md.trajectories/completed/2026-05/traj_1775914133873_35667beb.json.trajectories/completed/2026-05/traj_1775914133873_35667beb.md.trajectories/completed/2026-05/traj_1776073106646_1839be2d.json.trajectories/completed/2026-05/traj_1776073106646_1839be2d.md.trajectories/completed/2026-05/traj_1776113772922_bc92f121.json.trajectories/completed/2026-05/traj_1776113772922_bc92f121.md.trajectories/completed/2026-05/traj_3b3p1z4y7qlo.json.trajectories/completed/2026-05/traj_3b3p1z4y7qlo.md.trajectories/completed/2026-05/traj_o9cx33xn5u39.json.trajectories/completed/2026-05/traj_o9cx33xn5u39.md.trajectories/index.jsonpackages/sdk/src/github.tspackages/sdk/src/index.tsspecs/slack-primitive.md
Walked through §8 open questions and folded answers into the body: - DM support deferred to v2 (limitation noted in §4.3) - Slack Connect verification moved to Phase A implementation work - Audit-trail persistence assigned to the runner; tracked as #825 - channel optional for postMessage (reuses sage notify-channel resolver), required for askQuestion - Retry idempotency keyed on (runId, stepName); folded into §4.3 resumability Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
♻️ Duplicate comments (2)
specs/slack-primitive.md (2)
52-52:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix Slack token prefix examples to avoid invalid-looking tokens.
Line 52 still shows
xoxb-_/xoxp-_; use wildcard form (xoxb-*,xoxp-*) so the examples match standard token-prefix notation.Suggested patch
-For Slack, we expect the connection to be a **bot user OAuth token** (xoxb-_), not user-token (xoxp-_). Posting and reading replies both work with `chat:write`, `channels:history`, and `groups:history` scopes. The primitive validates scopes on first call and throws a typed error early if they're missing. +For Slack, we expect the connection to be a **bot user OAuth token** (xoxb-*), not user-token (xoxp-*). Posting and reading replies both work with `chat:write`, `channels:history`, and `groups:history` scopes. The primitive validates scopes on first call and throws a typed error early if they're missing.🤖 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 `@specs/slack-primitive.md` at line 52, Update the example token prefixes by replacing the literal strings "xoxb-_" and "xoxp-_" with the wildcard forms "xoxb-*" and "xoxp-*" in the Slack primitive documentation so the examples use standard token-prefix notation; search for occurrences of xoxb-_ and xoxp-_ (the examples in the Slack description) and swap them to xoxb-* and xoxp-* respectively.
241-262:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd a language tag to the fenced package-tree block (MD040).
Line 241 opens a fenced block without a language identifier; markdownlint will keep flagging this.
Suggested patch
-``` +```text packages/slack-primitive/ src/ index.ts // public exports @@ examples/ end-to-end-ask-question.ts notify-on-pr.ts</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@specs/slack-primitive.mdaround lines 241 - 262, The fenced code block
showing the package tree in specs/slack-primitive.md is missing a language tag
which triggers markdownlint MD040; update the opening fence fromto include a language (e.g.,text) so the block becomes ```text and save—this targets
the package-tree fenced block that lists packages/slack-primitive/ and its src/
and examples/ entries.</details> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.Duplicate comments:
In@specs/slack-primitive.md:
- Line 52: Update the example token prefixes by replacing the literal strings
"xoxb-" and "xoxp-" with the wildcard forms "xoxb-" and "xoxp-" in the Slack
primitive documentation so the examples use standard token-prefix notation;
search for occurrences of xoxb-_ and xoxp-_ (the examples in the Slack
description) and swap them to xoxb-* and xoxp-* respectively.- Around line 241-262: The fenced code block showing the package tree in
specs/slack-primitive.md is missing a language tag which triggers markdownlint
MD040; update the opening fence fromto include a language (e.g.,text)
so the block becomes ```text and save—this targets the package-tree fenced block
that lists packages/slack-primitive/ and its src/ and examples/ entries.</details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: Organization UI **Review profile**: CHILL **Plan**: Pro Plus **Run ID**: `b99e36db-5936-460e-bc4f-16346cffd1dd` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between b2f4cbb8d1117cf0be5ff50d47bfb1d6471c68a0 and c997b219ef51607db16dc368ea7f4b7a38ed7114. </details> <details> <summary>📒 Files selected for processing (1)</summary> * `specs/slack-primitive.md` </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
CodeRabbit flagged privacy/portability issues on the trajectory artifacts and two minor formatting issues on the Slack primitive spec. - Sanitize machine-specific paths in .trajectories/* — replace /Users/khaliqgant/... and /Users/will/... with <repo-root>/<home> placeholders. Path fields in .trajectories/index.json normalized to repo-relative paths so lookup works across environments. - specs/slack-primitive.md: wrap token-prefix examples in backticks (xoxb-* / xoxp-*) so markdown doesn't eat the asterisks. - specs/slack-primitive.md: add 'text' language tag to the package-tree fenced code block so markdownlint MD040 stops complaining. Skipped: the "use GitHub casing" comment on the compacted .md — it triggered on the literal '.github/workflows/' directory path, not the product name. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 @.trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.md:
- Line 12: Replace the malformed token text `at*live*\*` in the markdown with
the exact literal token wrapped in backticks so it renders literally;
specifically find the string `at*live*\*` and change it to `at*live*` (i.e., a
single backticked token) so readers see the intended token text unparsed by
markdown.
🪄 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: eebc1ced-b82b-405f-8ed3-25196c193467
📒 Files selected for processing (34)
.trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.json.trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.md.trajectories/completed/2026-04/traj_05xg7j388bc4.json.trajectories/completed/2026-04/traj_0t92gxaz6igh.json.trajectories/completed/2026-04/traj_1776105988184_29f1270c.json.trajectories/completed/2026-04/traj_222ha5671idc.json.trajectories/completed/2026-04/traj_3b3p1z4y7qlo.json.trajectories/completed/2026-04/traj_4zqhfqw7g28l.json.trajectories/completed/2026-04/traj_530xmbfeljyb.json.trajectories/completed/2026-04/traj_703m7sqyq89t.json.trajectories/completed/2026-04/traj_8oh4r5km5eic.json.trajectories/completed/2026-04/traj_9tt55is74dq5.json.trajectories/completed/2026-04/traj_abjovknvcijv.json.trajectories/completed/2026-04/traj_avmkyoo2s3rt.json.trajectories/completed/2026-04/traj_d48czxmgx4ac.json.trajectories/completed/2026-04/traj_dw8ihhdb8ip7.json.trajectories/completed/2026-04/traj_e5i62wdjx0jd.json.trajectories/completed/2026-04/traj_g3muawdq6bsb.json.trajectories/completed/2026-04/traj_mk0t0cgn4ytq.json.trajectories/completed/2026-04/traj_o8kgzhfu6jth.json.trajectories/completed/2026-04/traj_qb54w47qwod6.json.trajectories/completed/2026-04/traj_rs2bt3x0fqba.json.trajectories/completed/2026-04/traj_tjadoebpscps.json.trajectories/completed/2026-04/traj_tv1x9pamkqad.json.trajectories/completed/2026-04/traj_ui5omrgz819d.json.trajectories/completed/2026-04/traj_w0xpsaoxuiyw.json.trajectories/completed/2026-05/traj_1776073106646_1839be2d.json.trajectories/completed/2026-05/traj_1776113772922_bc92f121.json.trajectories/completed/2026-05/traj_1776113772922_bc92f121.md.trajectories/completed/2026-05/traj_3b3p1z4y7qlo.json.trajectories/completed/2026-05/traj_o9cx33xn5u39.json.trajectories/completed/traj_1776105620545_9dcebb3d.json.trajectories/index.jsonspecs/slack-primitive.md
✅ Files skipped from review due to trivial changes (27)
- .trajectories/completed/2026-04/traj_0t92gxaz6igh.json
- .trajectories/completed/2026-04/traj_tv1x9pamkqad.json
- .trajectories/completed/2026-04/traj_05xg7j388bc4.json
- .trajectories/completed/2026-04/traj_w0xpsaoxuiyw.json
- .trajectories/completed/2026-04/traj_4zqhfqw7g28l.json
- .trajectories/completed/2026-04/traj_530xmbfeljyb.json
- .trajectories/completed/2026-04/traj_8oh4r5km5eic.json
- .trajectories/completed/2026-04/traj_d48czxmgx4ac.json
- .trajectories/completed/2026-04/traj_o8kgzhfu6jth.json
- .trajectories/completed/2026-04/traj_ui5omrgz819d.json
- .trajectories/completed/2026-04/traj_rs2bt3x0fqba.json
- .trajectories/completed/2026-04/traj_avmkyoo2s3rt.json
- .trajectories/completed/traj_1776105620545_9dcebb3d.json
- .trajectories/completed/2026-04/traj_703m7sqyq89t.json
- .trajectories/completed/2026-04/traj_9tt55is74dq5.json
- .trajectories/completed/2026-04/traj_abjovknvcijv.json
- .trajectories/completed/2026-04/traj_3b3p1z4y7qlo.json
- .trajectories/completed/2026-05/traj_1776113772922_bc92f121.md
- .trajectories/completed/2026-04/traj_tjadoebpscps.json
- .trajectories/completed/2026-04/traj_222ha5671idc.json
- .trajectories/completed/2026-04/traj_dw8ihhdb8ip7.json
- .trajectories/completed/2026-04/traj_1776105988184_29f1270c.json
- .trajectories/completed/2026-04/traj_qb54w47qwod6.json
- .trajectories/completed/2026-04/traj_mk0t0cgn4ytq.json
- .trajectories/completed/2026-05/traj_3b3p1z4y7qlo.json
- .trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.json
- .trajectories/completed/2026-05/traj_1776073106646_1839be2d.json
🚧 Files skipped from review as they are similar to previous changes (3)
- .trajectories/completed/2026-05/traj_o9cx33xn5u39.json
- .trajectories/completed/2026-05/traj_1776113772922_bc92f121.json
- .trajectories/index.json
Implements packages/slack-primitive following the design spec at specs/slack-primitive.md and the implementation prompt at specs/slack-primitive-impl.md. Phase A only — local Web API runtime, postMessage + resolveUser + resolveChannel. - Local runtime via @slack/web-api, SLACK_BOT_TOKEN env auth. - createSlackStep workflow helper with postMessage action. - Mention resolution: emails via users.lookupByEmail, handles via user-cache, raw IDs pass through. Unresolved mentions logged as soft error on step output. - Channel name resolution via conversations.list; IDs pass through. - Example workflow + smoke-test docs in examples/. - Unit tests cover token-missing, channel resolution, mention success and soft-fail, and templating substitution. Out of scope: askQuestion, alternate-runtime adapter, Block Kit and utility verbs, runner schema for askQuestion audit trail (#825). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # .trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.json # .trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.md # .trajectories/completed/2026-05/traj_1776073106646_1839be2d.json # .trajectories/completed/2026-05/traj_1776113772922_bc92f121.md # .trajectories/completed/2026-05/traj_o9cx33xn5u39.json # .trajectories/index.json # packages/sdk/src/index.ts # specs/slack-primitive.md
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
packages/slack-primitive/src/actions/resolve-user.ts (1)
4-4:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winConsider a simpler email pattern to avoid ReDoS risk.
CodeQL flags this regex as polynomial, which can cause ReDoS on pathological inputs like
a@a.repeated many times. While the risk is low here (mentions come from workflow configs, not untrusted user input), it's worth using a simpler pattern.🛡️ Proposed fix
-const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; +const EMAIL_PATTERN = /^[^@\s]+@[^@\s]+$/;This simpler pattern avoids the polynomial behavior while still distinguishing email-like strings from handles. For production-grade email validation, consider a dedicated library, but for mention resolution this lightweight check is sufficient.
🤖 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/slack-primitive/src/actions/resolve-user.ts` at line 4, The current EMAIL_PATTERN constant can trigger ReDoS; replace the regex-based check with a linear-time heuristic: implement a small isLikelyEmail helper used in place of EMAIL_PATTERN that (1) returns false if the string contains whitespace, (2) finds the single '@' (indexOf === lastIndexOf and not at ends), and (3) ensures there is at least one '.' after the '@' with characters on both sides; update usages in resolve-user.ts (replace EMAIL_PATTERN references) to call this helper so email-like detection remains cheap and ReDoS-safe.
🤖 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/slack-primitive/src/actions/post-message.ts`:
- Line 22: The code currently calls resolveChannel(slack, params.channel)
unconditionally; add explicit handling for an omitted channel in the postMessage
flow: if params.channel is undefined, first try to read
process.env.SLACK_DEFAULT_CHANNEL and use that value, and only if that env var
is also undefined throw a clear, descriptive Error stating that the channel is
missing and instructing the caller to provide a channel or set
SLACK_DEFAULT_CHANNEL (this avoids a cryptic failure inside resolveChannel);
then pass the resolved channel to resolveChannel(slack, resolvedChannel) instead
of params.channel.
In `@packages/slack-primitive/src/adapter.ts`:
- Around line 48-53: The isAuthenticated method can throw if
this.slack.auth.test() rejects; change isAuthenticated (the async
isAuthenticated() method) to catch errors from this.slack.auth.test() and return
false on any failure instead of letting the exception bubble; keep the existing
behavior of returning Boolean(this.config.token) when this.slack.auth is falsy,
but wrap the call to this.slack.auth.test() (and the subsequent response.ok
check) in a try/catch that returns false on catch so callers get a simple
boolean readiness result.
In `@packages/slack-primitive/src/types.ts`:
- Around line 96-102: PostMessageParams currently requires channel but the spec
allows omitting it; update the PostMessageParams interface in types.ts so the
channel property is optional (change channel: string to channel?: string) and
ensure any code using PostMessageParams (e.g., the postMessage handler/consumer
functions) handles the fallback resolution when channel is undefined.
In `@packages/slack-primitive/src/workflow-step.ts`:
- Around line 292-324: The projection logic in buildOutputProjection returns
null for failed steps when mode is the default data/result path because it uses
result.data ?? (result.output ? result.output : null), which causes
formatStepOutput to surface the string "null" instead of the real error; update
the data-path branch in buildOutputProjection to prefer result.error when both
data and output are empty (i.e., coalesce to result.error before falling back to
null) so the real Slack failure is preserved, and make the same change in the
other occurrence referenced (lines ~154-157) that builds the data/output
projection; locate buildOutputProjection and the alternate occurrence and adjust
the null-coalescing order to check result.error before returning null.
- Around line 398-422: normalizeResolvedParams currently coerces
numeric/boolean-looking strings via coerceScalar which breaks Slack-specific
fields (e.g., threadTs) and later readers
(readOptionalString/readRequiredString). Change coerceScalar so it no longer
converts plain "true"/"false"/"null" or numeric strings to booleans/numbers;
only parse values when they are explicit JSON objects/arrays or explicitly
quoted JSON strings (i.e., keep the JSON.parse branch for values starting with
'{', '[' or wrapped in quotes) and otherwise return the original string
unchanged; keep normalizeResolvedParams calling coerceScalar but ensure it
preserves numeric/boolean-looking strings as strings so Slack readers work
correctly.
---
Duplicate comments:
In `@packages/slack-primitive/src/actions/resolve-user.ts`:
- Line 4: The current EMAIL_PATTERN constant can trigger ReDoS; replace the
regex-based check with a linear-time heuristic: implement a small isLikelyEmail
helper used in place of EMAIL_PATTERN that (1) returns false if the string
contains whitespace, (2) finds the single '@' (indexOf === lastIndexOf and not
at ends), and (3) ensures there is at least one '.' after the '@' with
characters on both sides; update usages in resolve-user.ts (replace
EMAIL_PATTERN references) to call this helper so email-like detection remains
cheap and ReDoS-safe.
🪄 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: b6f2f2e7-938c-4636-838d-5e2554c0541b
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (26)
.trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.json.trajectories/completed/2026-05/traj_2tqxnib25omk.json.trajectories/completed/2026-05/traj_60qc24ufr96g.json.trajectories/completed/2026-05/traj_itgr2w8qs3xn.json.trajectories/completed/2026-05/traj_m7mpv7j8n78h.json.trajectories/completed/2026-05/traj_o9cx33xn5u39.json.trajectories/completed/2026-05/traj_vkozdglobkyg.json.trajectories/index.jsonpackages/slack-primitive/examples/README.mdpackages/slack-primitive/examples/notify-on-pr.tspackages/slack-primitive/package.jsonpackages/slack-primitive/src/__tests__/post-message.test.tspackages/slack-primitive/src/actions/post-message.tspackages/slack-primitive/src/actions/resolve-channel.tspackages/slack-primitive/src/actions/resolve-user.tspackages/slack-primitive/src/adapter.tspackages/slack-primitive/src/client.tspackages/slack-primitive/src/index.tspackages/slack-primitive/src/local-runtime.tspackages/slack-primitive/src/types.tspackages/slack-primitive/src/workflow-step.tspackages/slack-primitive/tsconfig.examples.jsonpackages/slack-primitive/tsconfig.jsonpackages/slack-primitive/vitest.config.tsspecs/slack-primitive-impl.mdspecs/slack-primitive.md
✅ Files skipped from review due to trivial changes (12)
- .trajectories/completed/2026-05/traj_60qc24ufr96g.json
- packages/slack-primitive/tsconfig.json
- packages/slack-primitive/examples/README.md
- .trajectories/completed/2026-05/traj_2tqxnib25omk.json
- packages/slack-primitive/tsconfig.examples.json
- packages/slack-primitive/src/index.ts
- .trajectories/completed/2026-05/traj_itgr2w8qs3xn.json
- packages/slack-primitive/vitest.config.ts
- .trajectories/completed/2026-05/traj_vkozdglobkyg.json
- packages/slack-primitive/package.json
- specs/slack-primitive-impl.md
- .trajectories/compacted/compact_j5u7qhaw4q6a_2026-05-08.json
…blish wiring The Phase A primitive previously hard-failed when SLACK_BOT_TOKEN wasn't set. That breaks the realistic CLI flow: most users don't have a bot token locally — they have a relay session and a workspace that's already connected to Slack via ricky's Nango app. This adds two new runtimes alongside `local`: - `cloud-relay`: posts via relay-cloud's POST /api/v1/slack/post-message (cloud PR #493). Activated when CLOUD_API_TOKEN + CLOUD_API_URL are set. The cloud endpoint uses the workspace's existing Nango Slack connection — no per-user bot token needed. - `noop`: postMessage logs a warning and returns a placeholder ts. Activated when no tokens are set. Lets workflows run end-to-end in CI / smoke environments without hard-failing on missing Slack creds. Selection priority: cloud-relay → local → noop. Override with `runtime: 'local' | 'cloud-relay' | 'noop' | 'auto'`. In cloud-relay mode, resolveUser/resolveChannel throw `unsupported_in_cloud_relay` (Phase A intentionally exposes only postMessage). Mention resolution is local-only; cloud-relay surfaces unresolved mentions as warnings on the step output. Also wires slack-primitive as an SDK internal dep, mirroring the github primitive shape: - packages/sdk/package.json: dep + ./slack subpath export - packages/sdk/src/slack.ts: re-exports the full surface - packages/sdk/src/index.ts: `export * as slack` + curated `{ createSlackStep, SlackClient }` from the root - .github/workflows/publish.yml: pack + install in smoke build, publish-sdk-internal-deps matrix, dry-run loop, publish_if_missing chain - .github/workflows/verify-publish-sdk.yml: package-availability check Tests: 22 new vitest cases (cloud-relay 11, noop 3, runtime-selection 8) covering auth requirements, success path with thread_ts/unfurl forwarding, mention warnings, error mapping (rate_limited / not_connected / slack_error / upstream_error), resolve rejection, and the auto-detect priority order. Related: AgentWorkforce/cloud#493 (the cloud-side endpoint). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
.github/workflows/publish.yml (1)
1261-1264: ⚡ Quick winConsider removing duplicate internal-dep publish loops in
publish-sdk-only.This job already depends on
publish-sdk-internal-deps, but the same package list is maintained and published again here. That duplication is easy to drift (as seen by repeated list edits) and adds avoidable complexity.♻️ Suggested simplification
- - name: Dry run SDK internal deps - if: github.event.inputs.dry_run == 'true' - run: | - set -euo pipefail - for package in config github-primitive slack-primitive workflow-types; do - echo "Dry run - would publish `@agent-relay/`${package}" - (cd "packages/${package}" && npm publish --dry-run --access public --tag ${{ github.event.inputs.tag }} --ignore-scripts) - done - @@ - - name: Publish SDK internal deps to NPM - if: github.event.inputs.dry_run != 'true' - run: | - set -euo pipefail - VERSION="${{ needs.build.outputs.new_version }}" - - publish_if_missing() { - local package="$1" - local name="@agent-relay/${package}" - if npm view "${name}@${VERSION}" version >/dev/null 2>&1; then - echo "✓ ${name}@${VERSION} is already published" - return - fi - - echo "Publishing ${name}@${VERSION}" - (cd "packages/${package}" && npm publish --access public --provenance --tag ${{ github.event.inputs.tag }} --ignore-scripts) - } - - publish_if_missing config - publish_if_missing workflow-types - publish_if_missing github-primitive - publish_if_missing slack-primitive + # Internal deps are already handled by `publish-sdk-internal-deps` (job dependency).Also applies to: 1271-1292
🤖 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 @.github/workflows/publish.yml around lines 1261 - 1264, The publish-sdk-only job duplicates the internal package publish loop (the "for package in config github-primitive slack-primitive workflow-types; do" block) even though it depends on publish-sdk-internal-deps; remove the duplicated loop from the publish-sdk-only job and either rely on the dependent job (publish-sdk-internal-deps) to publish those internal packages or centralize the package list into a single reusable input/variable used by both jobs; update publish-sdk-only to only publish the SDK artifacts it is responsible for (or reference a shared package list/matrix) so the package list is not maintained in two places.
🤖 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.
Nitpick comments:
In @.github/workflows/publish.yml:
- Around line 1261-1264: The publish-sdk-only job duplicates the internal
package publish loop (the "for package in config github-primitive
slack-primitive workflow-types; do" block) even though it depends on
publish-sdk-internal-deps; remove the duplicated loop from the publish-sdk-only
job and either rely on the dependent job (publish-sdk-internal-deps) to publish
those internal packages or centralize the package list into a single reusable
input/variable used by both jobs; update publish-sdk-only to only publish the
SDK artifacts it is responsible for (or reference a shared package list/matrix)
so the package list is not maintained in two places.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d2b1641e-771a-4522-8fc6-b03cd82faca9
📒 Files selected for processing (14)
.github/workflows/publish.yml.github/workflows/verify-publish-sdk.ymlpackages/sdk/package.jsonpackages/sdk/src/index.tspackages/sdk/src/slack.tspackages/slack-primitive/examples/README.mdpackages/slack-primitive/src/__tests__/cloud-relay-runtime.test.tspackages/slack-primitive/src/__tests__/noop-runtime.test.tspackages/slack-primitive/src/__tests__/runtime-selection.test.tspackages/slack-primitive/src/adapter.tspackages/slack-primitive/src/cloud-relay-runtime.tspackages/slack-primitive/src/index.tspackages/slack-primitive/src/noop-runtime.tspackages/slack-primitive/src/types.ts
✅ Files skipped from review due to trivial changes (7)
- packages/slack-primitive/src/tests/noop-runtime.test.ts
- .github/workflows/verify-publish-sdk.yml
- packages/sdk/src/slack.ts
- packages/slack-primitive/src/tests/runtime-selection.test.ts
- packages/slack-primitive/src/noop-runtime.ts
- packages/slack-primitive/src/index.ts
- packages/slack-primitive/examples/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/slack-primitive/src/types.ts
# Conflicts: # .trajectories/index.json # packages/sdk/package.json
Summary
@agent-relay/github-primitive(already a bundled dep) from the root@agent-relay/sdkso workflow authors don't need the/githubsubpath.import { github } from '@agent-relay/sdk'— namespaced full surface, no collision riskimport { createGitHubStep, GitHubClient } from '@agent-relay/sdk'— curated helpers for the common workflow pathexport *from the root: the primitive ships ~40 generic-named action helpers (createFile,readFile,getUser,errorMessage, …) that would pollute the root namespace.Marked as an early PR for visibility — happy to iterate on which symbols (if any) belong unprefixed at the root.
Branch contents
This branch (
primitive) also contains pre-existing unrelated commits ahead ofmain:75f4d4f2spec(slack-primitive): design for workflow ↔ human messaging24d1180ctrajectories cleanupc5a3ad27merge fromspec/slack-primitiveThe github export is
b2f4cbb8. Let me know if the slack-primitive spec / trajectories commits should be split into their own PRs.Test plan
npm run buildinpackages/sdkis clean (no name-collision errors)dist/index.jsanddist/index.d.tscontain bothexport * as githuband the curatedcreateGitHubStep/GitHubClientre-exportsimport { createGitHubStep } from '@agent-relay/sdk'resolves and runs🤖 Generated with Claude Code