Skip to content

feat(slack): Agent Sessions lifecycle, native stop and automatic titles (#215) - #303

Merged
patrick-chinchill merged 7 commits into
mainfrom
sync/4.41-sl10
Oct 1, 2026
Merged

patrick-chinchill merged 7 commits into
mainfrom
sync/4.41-sl10

Conversation

@patrick-chinchill

@patrick-chinchill patrick-chinchill commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Slack Agent Sessions lifecycle and native Stop, all gated on agent_view=True (issue #215). Built on #201 (turn cancellation, TurnSignal, end_typing, agent-session events) and #214 (agent_view, open_dm bridge). PR A and PR B from the issue land together (~1.05k LOC including tests and docs, under the split threshold).

Upstream commits mapped

What changed (src/chat_sdk/adapters/slack/)

  • set_session_status(channel_id, thread_ts, status, *, initiator_user_id=None, title=None) / _rename_agent_session via client.api_call("agents.sessions.setStatus" | "agents.sessions.rename", json=...).
  • supports_turn_cancellation returns agent_view.
  • start_typing: under agent_view, None sets the session to processing and "" to active (compared with == "", never or), with initiator_user_id; failures are logged with no legacy fallback. A custom status still uses assistant.threads.setStatus.
  • end_typing(thread_id, status="active") (agent_view + thread ts only).
  • set_assistant_status: agent_view + empty/whitespace status sets the session active; otherwise loading_messages is explicit, then configured, then [status] under agent_view. set_assistant_title renames the session under agent_view.
  • stream(): breaks at the top of each chunk iteration when options.signal.aborted, then finalizes through stop() as usual. Under agent_view: final stop() gets session_status (options.session_status or "active"), rotation stop() gets "processing", and the post+edit fallback, first-stop-failure fallback and final-stop-expiry returns call end_typing.
  • Events: agent_session_stopped runs a task that awaits chat.abort_turn (warns), sets the session active (warns), then calls process_agent_session_stopped with streaming_message_ts; no thread lock; pinned, logging done-callback, shielded wait_until. agent_session_title_changed calls process_agent_session_title_changed.
  • SlackAdapterConfig.session_title (SlackSessionTitle = bool | resolver, None default = agent_view) + SlackSessionTitleContext; applied by the open_dm bridge task after processing a top-level human DM with text.

Tests ported (index.test.ts, not fidelity-mapped)

"routes custom labels and clearing separately on an agent_view DM thread", "uses native $expected without custom text in agent view" (it.each), "does not fall back to a legacy label when native status fails", "surfaces custom status text via the legacy API and clears via the sessions lifecycle", "passes agent loading messages $expected" (it.each), "renames an agent session under agentView", "finishes agent streams with an active session status" (test_slack_api.py), "keeps the agent session processing while the reply continues" (test_slack_stream_rotation.py), "aborts and activates an agent session when the user stops it", "dispatches agent session title changes", "automatically titles a top-level agent_view DM session", "skips automatic titles when sessionTitle is disabled", "does not retitle an agent_view DM follow-up".

Python-specific: legacy mode makes no agents.sessions.* call (typing, end_typing, status, title; stream stop payload unchanged); "" vs None; an aborted signal mid-stream stops reading and awaits stop() exactly once; fallback and final-expiry end_typing with the requested status; the stop event aborts a real Chat turn that holds the thread lock (asyncio.Event gates, no sleeps); abort/activation failures still dispatch; title trimming and 80-char cut, sync/async resolvers, None/blank skip, resolver failure logged, bot/empty messages skipped, off without agent_view.

Fidelity

index.test.ts is not mapped, so the target report is unchanged and scripts/fidelity_target.json is not modified:
Delta vs committed report (HEAD): missing 100 -> 100 (+0)

Divergences

None added to the non-parity table. Notes (documented in docs/UPSTREAM_SYNC.md):

  • Upstream's non-bridge processMessage(...).then(applyConfiguredSessionTitle) branch is unreachable (the bridge takes every agent_view top-level DM first, same guard), so it is not ported; a code comment cites index.ts:3537-3557.
  • agents.sessions.setStatus / rename go through _with_token_kwargs (upstream withToken, landed in [4.41/SL8b] Slack: Enterprise Grid org-wide installs, authorizations[] routing, retry marker (split from #213) #268 and merged in here), so org-wide installs carry the event's team_id.
  • The title cut is 80 code points (upstream slice counts UTF-16 units).

Consumer impact (Slack, agent_view=True only; nothing changes without it)

  • thread.start_typing() / start_typing() without a status now calls agents.sessions.setStatus (processing) instead of assistant.threads.setStatus "Typing..."; start_typing(tid, "") sets active. Replies after typing move the session back to active via end_typing.
  • set_assistant_status(..., "") sets the session active; set_assistant_title calls agents.sessions.rename.
  • Native streams send session_status on chat.stopStream (active by default) and processing on rotation; an aborted turn finalizes the message with what was streamed.
  • The native Stop button (agent_session_stopped) now aborts the running turn (thread.signal, cross-process via the state backend), sets the session active, and runs on_agent_session_stopped handlers.
  • Top-level DMs are auto-titled from their first line by default (session_title=False opts out).

Live check pending: the Stop button cancelling a streaming reply and the session returning to active still need verification in an agent_view workspace.

Validation

ruff check/format, audit_test_quality (0 hard failures), verify_test_fidelity --check-docs, --strict against chat@4.31.0 (730 real tests, all matched), pytest 7882 passed, 24 skipped (after merging main at 78787c6), pyrefly 0 errors.

Review (gpt-6-astra)

  • Round 1: two P2s. (a) Stop handlers outlive the _stop task registered with wait_until: identical to upstream (index.ts:3980-3990, processAgentSessionStopped is fire-and-forget and registers its own task with the same waitUntil); kept, with a parity comment. (b) Breaking out of async for on abort left a generator input suspended (JS for await closes it on break): fixed, the input is closed before breaking.
  • Round 2: one P2, the close could raise BaseExceptionGroup([GeneratorExit()]) (TaskGroup across yield) and skip stop(): fixed with the existing aclose_quietly helper, with a test.
  • Round 3: no actionable findings.

Part of #215 (stays open until the live agent_view Stop-button check is recorded)
Part of #184

Merge gate

Final HEAD: e1773ef (includes main at 78787c6).

Independent review findings (5):

  • Fixed, major: the live agent_view Stop-button check from [4.41/SL10] Slack Agent Sessions lifecycle + native stop #215's acceptance criteria has not been run. The footer is now "Part of [4.41/SL10] Slack Agent Sessions lifecycle + native stop #215", so merging leaves the issue open for that check. The "pending" notes stay in CHANGELOG.md and docs/UPSTREAM_SYNC.md. The live check itself is still not done.
  • Fixed, minor: the session-title test now has a case where the first line is under 80 characters and a second line follows (" Summarize this \nmore detail" -> "Summarize this"). Mutating the code to drop the first-line split now fails it.
  • Fixed, minor: new test test_first_stop_failure_fallback_ends_the_agent_session. All deltas stay buffered, then stop() fails, and the test asserts the post+edit result and one setStatus with the requested status. Deleting that end_typing now fails it.
  • Fixed, minor: the legacy test now asserts that set_assistant_status("D1", "1.2", "Working...") sends no loading_messages. Removing the and self._agent_view gate now fails it.
  • Fixed, minor: {"subtype": "file_share"} added to the skip parametrize. Removing the subtype guard now fails it.

Found while merging main (#268, Enterprise Grid): main now has _with_token_kwargs (upstream withToken). Upstream wraps agents.sessions.setStatus / rename in withToken (adapter-slack/src/index.ts:4317, 4336), so both calls now go through it. Two cases were added to _WITH_TOKEN_CALL_SITES in tests/test_slack_enterprise_grid.py; both fail without the fix. The "no per-call helper" note in docs and in this body is updated.

gpt-6-astra: 2 rounds in this pass, both clean.

  • 36160c5: no actionable findings.
  • e1773ef (after merging main): no actionable findings. This is the final HEAD.

Earlier rounds 1-3 on f8da06d are recorded above.

Bots: CodeRabbit was rate-limited, with no review or inline comments. No gemini comments.

CI: green on e1773ef: Lint & Type Check, test (3.12), test (3.13), CodeQL / Analyze.

Local full validation on e1773ef:

  • ruff check and format: clean.
  • audit_test_quality: 0 hard failures.
  • --check-docs: pass.
  • --strict against chat@4.31.0: all matched.
  • pytest: 7882 passed, 24 skipped.
  • pyrefly: 0 errors.
  • Fidelity: Delta vs committed report (HEAD): missing 100 -> 100 (+0).

…es (#215)

Slack half of vercel/chat 2ce2be00 (#862) plus 8b6d7f3a (#882) and
2cc8cc3f (#897), gated on agent_view: agents.sessions.setStatus/rename,
supports_turn_cancellation, start_typing/end_typing session states,
stream() signal checks and session_status on stop, agent_session_stopped
(abort_turn without the thread lock) and agent_session_title_changed
events, and the session_title config applied by the open_dm bridge.

Closes #215
Part of #184
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 45 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 8447e53d-ff6f-4aba-8f6f-815bcda19020

📥 Commits

Reviewing files that changed from the base of the PR and between 78787c6 and e1773ef.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • docs/UPSTREAM_SYNC.md
  • src/chat_sdk/adapters/slack/adapter.py
  • src/chat_sdk/adapters/slack/types.py
  • tests/test_slack_agent_sessions.py
  • tests/test_slack_api.py
  • tests/test_slack_enterprise_grid.py
  • tests/test_slack_stream_rotation.py
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

@patrick-chinchill
patrick-chinchill marked this pull request as ready for review October 1, 2026 09:57
@patrick-chinchill

Copy link
Copy Markdown
Collaborator Author

Merge gate: CI green (Lint & Type Check, test (3.12), test (3.13), CodeQL / Analyze (actions, python)) on e1773ef; local Codex review (gpt-6-astra, xhigh, --base origin/main) on e1773ef: "No actionable regressions found against the specified merge base. Validation passed: 7,882 tests, 24 skipped, lint checks, and type checking"; 5 astra rounds (3 on the initial branch, 2 after merging main, last 3 clean); CodeRabbit rate-limited (no review/inline comments), no gemini comments. origin/main (78787c6) is already an ancestor of HEAD, so no re-merge was needed. #215 stays open for the live agent_view Stop-button check. Merging with --admin (Protect Main requires a code-owner approval).

@patrick-chinchill
patrick-chinchill merged commit a78919d into main Oct 1, 2026
7 checks passed
@patrick-chinchill
patrick-chinchill deleted the sync/4.41-sl10 branch October 1, 2026 10:07
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