feat: smart Slack link — preserve and auto-resume questions after account linking#2118
feat: smart Slack link — preserve and auto-resume questions after account linking#2118omar-inkeep merged 20 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 0c759d0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
PR Review Summary
(8) Total Issues | Risk: Medium
🟠⚠️ Major (4) 🟠⚠️
🟠 1) resume-intent.ts Duplicate utility function: findAgentByIdentifierViaApi
Issue: The new findAgentByIdentifierViaApi function (lines 218-284) reimplements the same logic as the existing fetchAgentsFromManageApi + findAgentByIdentifier functions in commands/index.ts (lines 53-146). Both functions fetch projects, then agents for each project, then find by name/ID. The implementations are nearly identical but with minor differences.
Why: Code duplication increases maintenance burden. If the API contract changes or a bug is found, both implementations need updating. The ~66 lines of duplicated code could be a single shared utility.
Fix: Extract fetchAgentsFromManageApi to a shared utility module (e.g., slack/services/utils.ts or a new slack/services/agent-lookup.ts) and import it in both commands/index.ts and resume-intent.ts.
Refs:
- resume-intent.ts:218-284 — new duplicate implementation
- commands/index.ts:53-146 — existing implementation
🟠 2) resume-intent.ts Missing timeouts on external calls
Issue: Several external calls lack explicit timeouts: findWorkspaceConnectionByTeamId (line 29), signSlackUserToken (line 39-45), and resolveEffectiveAgent (line 147). In a waitUntil background context, these could hold resources indefinitely if the underlying service is slow.
Why: The file correctly uses timeouts for fetch calls (lines 225, 301), but other async operations that involve DB or Nango calls don't have timeout protection. Inconsistent timeout handling can lead to resource leaks in degraded conditions.
Fix: Either wrap these calls in a timeout (using AbortController or a Promise.race pattern), or use the existing timedOp helper from events/utils.ts. Also consider extracting the hardcoded timeout values (10_000, 30_000) into named constants like INTERNAL_FETCH_TIMEOUT_MS used in commands/index.ts.
Refs:
🟠 3) users.ts:270-273 waitUntil unavailable silently drops resume work
Issue: When waitUntil is not available (line 270-273), the resumeWork promise is created but never awaited or scheduled. The response is returned immediately at line 276, and the background work may be terminated before completion.
Why: In environments without waitUntil (some deployment contexts), the user's question will never be resumed after linking. This is a silent failure that depends on the deployment environment, with no indication to operators or users.
Fix: Either await the promise as a fallback, or log when waitUntil is unavailable:
const waitUntil = await getWaitUntil();
if (waitUntil) {
waitUntil(resumeWork);
} else {
logger.warn({ entryPoint: intent.entryPoint }, 'waitUntil not available, awaiting resume work synchronously');
await resumeWork;
}Refs:
🟠 4) slack-link-token.ts Type allows illegal states — consider discriminated union
Issue: The SlackLinkIntentSchema uses a flat structure with optional fields instead of a discriminated union. The entryPoint field acts as a discriminant but the schema does not enforce that entry-point-specific fields are present. For example, a mention intent can be constructed without agentId/projectId, and a run_command intent without agentIdentifier.
Why: The defensive runtime checks in resumeMention (lines 97-101) and resumeRunCommand (lines 184-188) indicate the type permits invalid data flow. TypeScript won't catch these errors at compile time.
Fix: Use a Zod discriminated union to enforce entry-point-specific required fields:
const MentionIntentSchema = z.object({
entryPoint: z.literal('mention'),
question: z.string().min(1).max(2000),
channelId: z.string().min(1),
threadTs: z.string().optional(),
messageTs: z.string().optional(),
agentId: z.string().min(1), // required for mention
projectId: z.string().min(1), // required for mention
});
// ... similar for QuestionCommandIntentSchema, RunCommandIntentSchema
export const SlackLinkIntentSchema = z.discriminatedUnion('entryPoint', [
MentionIntentSchema,
QuestionCommandIntentSchema,
RunCommandIntentSchema,
]);Refs:
Inline Comments:
- 🟠 Major:
resume-intent.ts:106Silent failure when timestamps missing - 🟠 Major:
resume-intent.ts:331Silent failure when run API returns non-OK - 🟠 Major:
resume-intent.ts:266-268Empty catch swallows errors without logging - 🟠 Major:
resume-intent.ts:279-281Outer catch swallows all errors including timeouts
🟡 Minor (2) 🟡
Inline Comments:
- 🟡 Minor:
resume-intent.ts:98Full intent object logged may expose user question content - 🟡 Minor:
link/page.tsx:161-166Circular guidance — user is already on this page from Slack
💭 Consider (2) 💭
💭 1) resume-intent.ts Missing tracing spans for observability
Issue: The resumeSmartLinkIntent function doesn't create OpenTelemetry spans, making it difficult to trace resume operations in production.
Why: Since these run in waitUntil background context where failures are harder to observe, tracing would help with debugging.
Fix: Add tracer.startActiveSpan() with relevant attributes (entryPoint, teamId, channelId).
Refs: handleAppMention uses spans
💭 2) resume-intent.ts:109-113 Acknowledgment message lacks resumption context
Issue: The "Preparing a response..." message shown when resuming lacks context that the user's original question is being answered after linking.
Why: The user has been away doing account linking — a message like "Answering your question..." would provide better continuity.
Fix: Change text to '_Answering your question..._' or similar.
💡 APPROVE WITH SUGGESTIONS
Summary: This is a well-designed feature that elegantly solves the friction in the account linking flow. The JWT-based stateless approach is clean and the test coverage is solid (34 new tests). The most actionable items are the silent failure paths in resume-intent.ts where users could complete linking but never receive their answer due to missing error notifications. The code duplication with findAgentByIdentifierViaApi should be consolidated, and the type design would benefit from a discriminated union to prevent illegal states at compile time.
Discarded (12)
| Location | Issue | Reason Discarded |
|---|---|---|
commands.mdx:13 |
Ambiguous "it" pronoun | LOW confidence, context makes meaning clear |
installation.mdx:64 |
Sentence structure style | LOW confidence, minor style preference |
blocks/index.ts:221-231 |
Hardcoded strings vs i18n | INFO level, matches existing pattern in PR |
services/index.ts |
Barrel export missing | Internal-only module, not consumer-facing |
resume-intent.ts:67-86 |
New logging event pattern | INFO level, reasonable new pattern |
slack-link-token.ts:12-24 |
Schema follows patterns correctly | Positive finding, no action needed |
blocks/index.ts:221-230 |
Two message styles for linking | INFO level, intentional design difference |
resume-intent.ts:371 |
Generic error message | Addressed by broader silent failure findings |
link/page.tsx:124-171 |
Duplicate card layout | MEDIUM confidence, reasonable for distinct states |
link/page.tsx:125 |
isExpiredError reachability | LOW confidence, likely defensive coding |
link/page.tsx:151-159 |
Button vs Link for navigation | SSR complexity makes onClick reasonable |
login/page.tsx:279 |
Inconsistent Suspense fallback | Minor styling inconsistency |
Reviewers (11)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
pr-review-sre |
9 | 2 | 1 | 0 | 0 | 0 | 6 |
pr-review-errors |
6 | 0 | 0 | 0 | 4 | 0 | 2 |
pr-review-tests |
6 | 0 | 0 | 0 | 0 | 0 | 6 |
pr-review-consistency |
6 | 1 | 0 | 0 | 0 | 0 | 5 |
pr-review-product |
5 | 0 | 1 | 0 | 1 | 0 | 3 |
pr-review-frontend |
4 | 0 | 0 | 0 | 1 | 0 | 3 |
pr-review-docs |
2 | 0 | 0 | 0 | 0 | 0 | 2 |
pr-review-types |
1 | 1 | 0 | 0 | 0 | 0 | 0 |
pr-review-security-iam |
1 | 0 | 0 | 0 | 1 | 0 | 0 |
pr-review-standards |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
pr-review-breaking-changes |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Total | 40 | 4 | 2 | 0 | 7 | 0 | 27 |
Note: pr-review-breaking-changes confirmed backward compatibility is properly maintained via optional intent field.
7a288ae to
813c6aa
Compare
There was a problem hiding this comment.
PR Review Summary
(5) Total Issues | Risk: Medium
🟠⚠️ Major (2) 🟠⚠️
Inline Comments:
- 🟠 Major:
slack-link-token.ts:12-22Flat schema permits illegal states — discriminated union recommended - 🟠 Major:
resume-intent.ts:241-324Duplicate agent lookup function reimplements existing logic
🟡 Minor (1) 🟡
Inline Comments:
- 🟡 Minor:
.changeset/satisfactory-coffee-cow.md:3Missing@inkeep/agents-work-appsin changeset
💭 Consider (1) 💭
Inline Comments:
- 💭 Consider:
link/page.tsx:131-139Extract duplicate expired token check to helper
🕐 Pending Recommendations (2)
These issues were raised in the prior review and appear to still be relevant:
- 🟠
resume-intent.ts:124Silent failure when timestamps missing — user receives no feedback - 🟠
resume-intent.ts:388Silent failure when run API returns non-OK response — user waits indefinitely
💡 APPROVE WITH SUGGESTIONS
Summary: The smart Slack link feature is well-structured with good test coverage and comprehensive error handling improvements from the prior review. However, two issues warrant attention before merge:
-
Schema design: The flat
SlackLinkIntentSchemapermits invalid field combinations that require runtime validation. A discriminated union would make illegal states unrepresentable and simplify theresume-intent.tsswitch logic. -
Code duplication: The new
findAgentByIdentifierViaApifunction duplicates ~84 lines of existing logic incommands/index.ts. Extracting to a shared utility would reduce maintenance burden.
The two Pending Recommendations from the prior review (silent failures at lines 124 and 388) also appear unresolved — the code logs errors but doesn't notify users when resume fails due to missing timestamps or API errors.
Discarded (12)
| Location | Issue | Reason Discarded |
|---|---|---|
resume-intent.ts |
Missing timeouts on findWorkspaceConnectionByTeamId |
These are internal DB/service calls with their own connection timeouts; adding explicit timeouts adds complexity without clear benefit |
resume-intent.ts |
response_url 30-minute expiry window risk | Documented Slack behavior; the code already falls back to bot channel post — this is defense-in-depth, not a bug |
resume-intent.ts |
No retry logic for transient failures | Fire-and-forget via waitUntil is intentional; adding retries would complicate the flow for marginal benefit |
resume-intent.ts:152 |
agentName: intent.agentId passes ID as name |
Intentional fallback when agent name isn't available in JWT; acceptable UX tradeoff |
resume-intent.ts |
Question content logged in error paths | Reviewed the code — only questionLength is logged, not the full question. Finding was based on outdated code. |
link/page.tsx |
Suspense fallback inconsistency | The Suspense boundary wraps the entire page; the loader inside SlackLinkForm handles the auth loading state. Not a bug. |
link/page.tsx |
Missing error boundary | Pre-existing pattern across the app; not introduced by this PR |
commands.mdx |
Ambiguous "you'll automatically receive" phrasing | The docs are accurate — users do automatically receive the prompt. Minor stylistic preference. |
installation.mdx |
Missing "What to tell users" section | Documentation is adequate for the target audience; additional sections are nice-to-have |
overview.mdx |
Feature list could use more detail | Existing level of detail is consistent with sibling docs |
resume-intent.ts |
Test coverage gaps | Tests exist for core paths; additional edge case tests are nice-to-have but not blocking |
link/page.tsx |
Success toast could be more specific | UX preference; current "Account Linked!" message is clear and consistent |
Reviewers (9)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
pr-review-types |
1 | 0 | 0 | 0 | 1 | 0 | 0 |
pr-review-consistency |
5 | 0 | 0 | 0 | 2 | 0 | 3 |
pr-review-sre |
8 | 0 | 0 | 0 | 0 | 0 | 8 |
pr-review-tests |
7 | 0 | 0 | 0 | 0 | 0 | 7 |
pr-review-standards |
4 | 0 | 0 | 0 | 0 | 2 | 2 |
pr-review-frontend |
3 | 0 | 0 | 0 | 1 | 0 | 2 |
pr-review-errors |
1 | 0 | 0 | 0 | 0 | 0 | 1 |
pr-review-docs |
3 | 0 | 0 | 0 | 0 | 0 | 3 |
pr-review-product |
4 | 0 | 0 | 0 | 0 | 0 | 4 |
| Total | 36 | 0 | 0 | 0 | 4 | 2 | 30 |
Add SlackLinkIntentSchema and SlackLinkIntent type to slack-link-token.ts. Intent carries entry point, question, channel context, and agent info so the user's original question can be resumed after account linking. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…nked path
Add createSmartLinkMessage() to blocks/index.ts with Section + Actions
(primary Link Account button) + Context ("This only needs to happen once.").
Replace plain text ephemeral in app-mention.ts unlinked path with JWT
signed with intent (mention entry point) and new Block Kit message.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
handleQuestionCommand now signs JWT with question_command intent. handleRunCommand now signs JWT with run_command intent + agentIdentifier. Both paths truncate questions to 2000 chars and use createSmartLinkMessage. generateLinkCodeWithIntent accepts optional intent parameter. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…esume logic Co-Authored-By: Claude Opus 4.6 <[email protected]>
…tUntil Co-Authored-By: Claude Opus 4.6 <[email protected]>
…uidance Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
PR Review Summary
(1) Total Issues | Risk: Medium
Delta Review Scope
This re-review covers changes since the last automated review (feb547e49):
- 1 commit (
ef96c3e78 fix test) - 2 lines changed — test fixture adding
grantAccessToMembers: true
The delta does not address the blocking issue from the previous review.
🟠⚠️ Major (1) 🟠⚠️
Inline Comments:
- 🟠 Major:
resume-intent.ts:39-45Missing channel auth context in resumed execution JWT
🟡 Minor (1) 🟡
Inline Comments:
- 🟡 Minor:
users.ts:306-308Inconsistent response shape in concurrent conflict handler
🚫 REQUEST CHANGES
Summary: The blocking issue from the previous review remains unresolved. The signSlackUserToken call in resume-intent.ts (lines 39-45) still does not include channel authorization context (slackAuthorized, slackAuthSource, slackChannelId, slackAuthorizedProjectId).
Impact: Users who have access via channel-level authorization (grantAccessToMembers: true) but lack explicit project membership will:
- ✅ Get their question captured correctly when unlinked
- ✅ Successfully complete account linking
- ❌ Fail with 403 when the resumed question executes (SpiceDB denies access)
- ✅ Succeed if they ask the same question again directly after linking
This creates a confusing UX where the "smart link" feature appears broken for a specific (but common) authorization configuration.
Recommended fix: See the inline comment at resume-intent.ts:45 for implementation guidance. The pattern already exists in app-mention.ts and commands/index.ts — it just needs to be applied to the resume path.
Discarded (0)
No findings discarded in this delta review.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| Delta review — no subagents dispatched | — | — | — | — | — | — | — |
Note: This is a delta re-review. The delta (1 commit, 2 lines) consists of test fixture updates — no substantive logic changes requiring full subagent review. The blocking issue from the previous review was identified by pr-review-security-iam and pr-review-consistency.
| slackTeamId: teamId, | ||
| slackUserId, | ||
| slackEnterpriseId, | ||
| }); |
There was a problem hiding this comment.
🟠 MAJOR: Missing channel auth context in resumed execution JWT
Issue: The signSlackUserToken call here does NOT include the channel authorization context parameters (slackAuthorized, slackAuthSource, slackChannelId, slackAuthorizedProjectId) that direct execution paths now include.
Why: This creates an authorization inconsistency:
- User asks question in channel with
grantAccessToMembersenabled (but user lacks explicit project membership) - User is prompted to link account, intent is stored in JWT
- User links account,
resumeSmartLinkIntentfires - Execution fails with 403 'insufficient permissions' because the token lacks
slackAuthorized: true - If user asks the same question again after linking, it succeeds because direct path includes channel auth
Direct paths (app-mention.ts:272-281, commands/index.ts:530-536) include these fields — resumed paths should too for consistent authorization.
Fix: For entry points that already have agent context (like mention with intent.agentId/intent.projectId), include channel auth. For question_command and run_command, resolveEffectiveAgent() is already called — use its result:
// For resumeMention - re-resolve agent config to get fresh auth state
const agentConfig = await resolveEffectiveAgent({
tenantId,
teamId,
channelId: intent.channelId,
});
const slackUserToken = await signSlackUserToken({
inkeepUserId,
tenantId,
slackTeamId: teamId,
slackUserId,
slackEnterpriseId,
slackAuthorized: agentConfig?.grantAccessToMembers ?? false,
slackAuthSource: agentConfig?.source === 'none' ? undefined : agentConfig?.source,
slackChannelId: intent.channelId,
slackAuthorizedProjectId: agentConfig?.projectId,
});For resumeCommand and resumeRunCommand, pass the resolved agent's auth context to a shared token-signing call.
Refs:
Ito Test Report ✅30 test cases ran. 30 passed. All test cases for the Smart Slack Link feature (PR #2118) passed verification. The tests comprehensively covered the ✅ Passed (30)
📋 View Recording |
There was a problem hiding this comment.
PR Review Summary
(1) Total Issues | Risk: Low
Delta Review Scope
This re-review covers changes since the last automated review (ef96c3e7884f):
- 1 commit (
d8c64e656 Update smart-link so it works with google and sso methods) - Key changes: Login page auto-signin support for Google/SSO, auth method propagation through invitation flow
✅ Prior Blocking Issue: RESOLVED
The MAJOR blocking issue from the previous review ("Missing channel auth context in resumed execution JWT") has been fully addressed:
| Component | Status |
|---|---|
getChannelAuthClaims helper added |
✅ Lines 22-30 |
resumeMention uses channel auth |
✅ Lines 147-156 |
resumeCommand uses channel auth |
✅ Lines 215-218 |
resumeRunCommand uses channel auth |
✅ Lines 256-259 |
| Test coverage for channel auth claims | ✅ All three entry points verified |
The implementation correctly re-resolves resolveEffectiveAgent for each entry point and includes slackAuthorized, slackAuthSource, slackChannelId, and slackAuthorizedProjectId in the signed JWT token — matching the pattern used in direct execution paths.
🟡 Minor (1) 🟡
Inline Comments:
- 🟡 Minor:
commands/index.ts:73-74Leftover debugconsole.logstatement
💭 Consider (1) 💭
💭 1) login/page.tsx:161-223 Auto-signin effect complexity
Issue: The useEffect for auto-signin has many dependencies (11 items) and complex conditional logic with multiple auth method checks.
Why: This is manageable complexity for the use case, but the effect could benefit from early-return guards being consolidated. The current implementation is correct but could be slightly cleaner.
Fix: Consider extracting the auto-signin logic into a separate hook (e.g., useAutoSignIn) for better testability and separation of concerns. Not blocking — the current implementation works correctly.
💡 APPROVE WITH SUGGESTIONS
Summary: The prior blocking issue about missing channel auth context has been fully resolved. The new delta adds support for Google/SSO auto-signin when users follow invitation links with email hints, which improves the onboarding UX for organizations using OAuth providers.
The only actionable item is removing the debug console.log statement in commands/index.ts:73. This is a quick 1-click fix via the inline suggestion.
🎉 The smart Slack link feature is ready to ship after removing the debug statement!
Discarded (0)
No findings discarded in this delta review.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| orchestrator-only | — | 0 | 1 | 0 | 1 | 0 | 0 |
Note: This was a focused delta re-review. No sub-agent reviewers were dispatched as the delta was small and the prior blocking issue was the primary focus.
|
|
||
| const message = createJwtLinkMessage(linkUrl, LINK_CODE_TTL_MINUTES); | ||
| console.log('linkResult', linkResult); | ||
| const message = buildLinkPromptMessage(linkResult); |
There was a problem hiding this comment.
🟡 Minor: Leftover debug statement
Issue: console.log('linkResult', linkResult) is a debug statement that should be removed before merge.
Why: Console logs in production code are unprofessional, can leak sensitive data (the linkResult contains URLs with JWT tokens), and add noise to server logs.
Fix: (1-click apply)
| const message = buildLinkPromptMessage(linkResult); | |
| const message = buildLinkPromptMessage(linkResult); |
Refs:
There was a problem hiding this comment.
PR Review Summary
(0) Total Issues | Risk: Low
Delta Review Scope
This re-review covers changes since the last automated review (d8c64e6561ae):
- 1 commit (
9248df82e remove log) - 1 line deleted — removed debug
console.log('linkResult', linkResult)statement
✅ Prior Suggestion: RESOLVED
The only remaining suggestion from the previous review was the debug console.log statement in commands/index.ts:73. This has been removed in the delta commit.
| Issue | Status |
|---|---|
Debug console.log in commands/index.ts |
✅ Removed in 9248df82e |
Prior Review Summary
All prior issues have now been addressed:
| Category | Status |
|---|---|
| Silent failure when timestamps missing | ✅ Resolved |
| Silent failure when run API returns non-OK | ✅ Resolved |
| Empty catch blocks swallowing errors | ✅ Resolved |
Missing @inkeep/agents-work-apps in changeset |
✅ Resolved |
| Duplicate expired token check | ✅ Resolved (helper extracted) |
| Circular guidance on link page | ✅ Resolved |
| Full intent object logged | ✅ Resolved |
| Channel auth context in resumed execution JWT | ✅ Resolved |
Debug console.log statement |
✅ Resolved (this delta) |
| Flat schema design | Declined with valid rationale |
| Duplicate agent lookup function | Declined with valid rationale |
✅ APPROVE
Summary: The smart Slack link feature is ready to merge. 🎉
This delta cleanly removes the debug statement that was the only remaining suggestion. The feature provides an excellent UX improvement — unlinked Slack users now have their questions preserved in JWT intent claims and automatically resumed after account linking. The implementation includes:
- Stateless JWT-based intent preservation across all 3 entry points (
@mention,/inkeep <q>,/inkeep run "agent" <q>) - Comprehensive error handling with user-facing feedback via
postErrorToChannel - Channel auth context correctly propagated through resumed execution paths
- Solid test coverage (~500 lines of new tests)
- Updated documentation reflecting the new auto-prompt flow
The declined suggestions (discriminated union schema, agent lookup deduplication) have reasonable justifications — the flat schema keeps JWT payloads simple, and the agent lookup implementations serve different authorization models.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| orchestrator-only | — | 0 | 0 | 0 | 0 | 0 | 0 |
Note: This was a trivial delta re-review (1 line removed). No sub-agent reviewers were dispatched.
|
Claude encountered an error —— View job PR Review in Progress
Reviewing PR #2118: feat: smart Slack link — preserve and auto-resume questions after account linking Review Scope: Delta review (
Delta Analysis:
The prior review on Analyzing the delta to determine if any substantive PR-specific changes require re-review... |
|
🔎💬 Inkeep AI search and chat service is syncing content for source 'Inkeep Agent Framework Docs' |
Ito Test Report ✅35 test cases ran. 35 passed. This test run validated the Smart Slack Link feature from PR #2118, which introduces automatic account linking prompts with intent preservation and auto-resume functionality. All route behaviors, edge cases, security measures, and end-to-end redirect chains passed verification. The new link page splash for unauthenticated users, returnUrl preservation through OAuth flows, expired token handling, open redirect prevention, and Block Kit message formatting all work as expected. ✅ Passed (35)
📋 View Recording |
Summary
waitUntilto execute the original question/inkeep <q>,/inkeep run "agent" <q>)/loginHow it works
Files changed (15 files, ~1200 lines including tests)
agents-core/.../slack-link-token.tsSlackLinkIntentSchema+intentfield to payloadapp-mention.ts,commands/index.tsblocks/index.tscreateSmartLinkMessage()builderresume-intent.ts(new)routes/users.tswaitUntil(resumeSmartLinkIntent(...)), normalize expired token errorslink/page.tsxlogin/page.tsxslack/commands.mdx,installation.mdx,overview.mdxslack-link-token.test.ts,resume-intent.test.ts,app-mention.test.ts.changeset/satisfactory-coffee-cow.mdTest plan
Manual QA scenarios verified via browser automation, direct function testing, and code review. Updated as tests complete.
Automated
pnpm typecheckpassespnpm lintpassesVisual / UI (browser automation)
not-a-valid-jwt-token. Token validation is server-side only./login?returnUrl=/link?token=<full-expired-jwt>, preserving the complete return URLDirect function testing (verifySlackLinkToken)
verifySlackLinkToken(expiredJWT)returns{ valid: false, error: '"exp" claim timestamp check failed' }verifySlackLinkToken('not-a-valid-jwt-token')returns{ valid: false, error: 'Invalid Compact JWS' }verifySlackLinkToken(validJWT)returns{ valid: true, payload: { ...intent, slack, tenantId } }with full intent preservationBug found and fixed during QA
"exp" claim timestamp check failedbut the link page UI checkserror.includes('expired'). The word "expired" never appeared, so the expired-specific UI messaging (prompting users to run/inkeep linkagain) would never render. Fixed: verify-token handler now normalizes expired errors to'Token expired. Please run /inkeep link in Slack to get a new one.'Code review
createSmartLinkMessage()produces correct structure: Section text + Actions with primary Link Account button + Context blockresumeSmartLinkIntent()wrapped in top-level try/catch, never throws, logs structured events for success and failureNot testable without live Slack workspace
/inkeep <question>by unlinked user — Same flow → question answered in channel · Skipped: requires live Slack workspace/inkeep run "agent" <question>— Same flow → answered with specified agent · Skipped: requires live Slack workspace/inkeep link(no question) — Existing flow unchanged (no intent in JWT) · Skipped: requires live Slack workspace🤖 Generated with Claude Code