Chatbot Channels (Web + Slack + Teams + API + MCP + CLI)
How GridWonk delivers consistent chatbot behavior across all surfaces using a shared orchestration core.
Availability for trial and prepaid workspaces
Unrestricted assistant chat is unavailable for workspaces enrolled in trial or prepaid processing. Enabling a chat channel or purchasing a pack does not remove that restriction. Use the supported public plugin tools, packet intake and review, and public help instead. The channel behavior below applies where ordinary chat is authorized. See processing allowances.
GridWonk provides conversational assistance across six surfaces: the web app, Slack, Microsoft Teams, the public API, the MCP gateway, and the CLI. All use the same shared orchestration core, but authorized context, available capabilities, and presentation can differ by channel.
Slack and Teams are enabled by default for every organization, but each organization admin can turn either transport off from the workspace Features page. A platform admin can override the setting from the organization's Features tab. A connected Slack workspace or Microsoft 365 tenant is still required before that transport can reach organization data.
Shared Core
All channels call executeChatbot() — a single async generator. Ordinary chat follows this
pipeline; the console's selected-finding route has the stricter boundary described under
Web Chat:
- Canonical FAQ — Instant deterministic answers filtered to the caller's effective audience (zero LLM cost)
- Direct tool fast paths — Uploaded-document inventory and narrow requirement lookups can call
query_user_documentsorpublished_requirementsbefore classification - Query classification — Intent detection and tool profile selection
- Requirements preflight — Preserve review-pending or reviewed-empty release wording; use generic no-coverage guidance only for a true not-published result
- Route planning — Choose
direct_llm,eve, or a typed deterministic clarification, refusal, or unavailable result - Execution and recovery — Run the selected lane; a direct-model failure before output may recover through Eve, but an Eve-accepted turn is never replayed through another runtime
- Persistence — Session and message recording
- Analytics — Structured telemetry for every interaction
For ordinary chat, all channels share:
- Same query classification categories and confidence thresholds
- Same routing and typed recovery behavior
- Same utility/state-aware context model
- Same canonical FAQ responses
- Same direct-tool metadata for fast-path
published_requirementsandquery_user_documentsanswers - Same requirements preflight logic
- Same MCP tool controls and safety posture
The administrator's Database Access control follows the turn across every channel. Before an Eve turn starts, the shared server signs the resolved control into its short-lived delegated token; database-backed product tools refuse the call unless that verified value is explicitly enabled.
The web adapter derives its docs audience from both the trusted platform role and the active organization context. A platform administrator impersonating a customer is therefore restricted to public docs and public quick answers for the full request.
Every channel carries the authenticated user and organization scope into session reads, message history, and completion writes. Opaque session IDs do not grant access by themselves, and caller metadata cannot choose a different organization.
The requirements fast path and preflight use the same reader as the published REST API, SDK, CLI,
and MCP gateway: every published row except the ones a utility reviewer explicitly rejected, each
carrying reviewStatus (approved or pending). A scope whose utility review is unfinished still
returns its rows, labelled pending, alongside an honest review-progress state. The tool-backed agent
loop uses that reader unconditionally. The temporary cutover flag and legacy fallback were removed
after the production cutover proved stable. The authenticated project/work context still chooses
the exact utility and state.
Across every channel, rows awaiting approved customer wording are omitted from usable requirement results. If all matches are pending, the response says the wording is under review; counts of available requirements exclude those hidden rows. Zero-row answers also preserve the difference between review-pending, reviewed-but-empty, and not-published results.
Supported retrieval, requirements, and orchestration turns use Eve when the deployment audience and that channel's independent gate permit it. If Eve is unavailable or cannot safely prepare the turn, the core returns a typed clarification, refusal, or recoverable error. Once Eve accepts a turn, the core does not replay it through the Claude Agent SDK.
For a safe, self-contained documentation question, Eve can emit its required first search_docs
call directly instead of paying for a model request whose only permitted result is that same call.
Context-dependent follow-ups stay on the normal model-controlled search path. This runtime detail is
shared across every enabled transport and does not change the channel event contract.
Eve is enabled per channel, not globally. Each channel is opened deliberately after its own authorization and disconnect checks pass, so it is normal for the web app to be answering on Eve while API, CLI, or MCP remain disabled. Enabled channels emit the same event contract; this does not guarantee identical evidence access or answers. Rollback selects a previously accepted Eve deployment instead of replaying customer turns through the Agent SDK.
Self-service availability
Self-service processing remains unlaunched. Metered packet-processing workspaces are refused at the shared chat boundary before FAQ routing, classification, or model execution. This applies to web chat, Slack, Teams, API, MCP, and CLI turns that reach that boundary; channel installation, Eve flags, and packet-processing credit do not grant generic chat access. Existing eligible workspaces keep the channel-specific authorization and rollout controls described here.
Channel Adapters
Each surface maps the shared ChatbotEvent stream to its native format:
| Channel | Adapter | Output Format |
|---|---|---|
| Web Chat | lib/chat/transports/console-ui-message-projection.ts (via app/api/chat/route.ts) | Vercel AI SDK UIMessageStream |
| Slack | lib/chat-sdk/bot.ts + stream-adapter.ts | Native processing status in Agent View; threaded replies can edit progress into the final reply, while root direct messages post one final message; legacy AsyncIterable<string> fallback |
| Microsoft Teams | lib/chat-sdk/bot.ts + @chat-adapter/teams | Single posted message; user-scoped thread continuity |
| Public API | app/api/v1/agents/chat/route.ts + sse-transformer.ts | Server-Sent Events (SSE) |
| MCP | lib/mcp/public-gateway.ts (chat tool) | Single buffered tool result |
| CLI | packages/cli → the public API endpoint | Rendered terminal output over SSE |
Web Chat
Finding-card Explain this finding actions carry a structured reference to the project, saved review, and finding—not browser-supplied source text or document URLs. The server rechecks current access, resolves the review's saved requirement snapshot, and verifies the source edition's content fingerprint before preparing bounded, exact source passages. The evidence is loaded before model execution with raw text offsets and page labels; page or list boundaries do not establish which section governs a requirement. Embedded source text is data, not instructions. Common-practice origins remain distinct from utility text, and publication is not presented as human approval.
The server persists the selected finding before model execution. Typed follow-ups, resumed conversations, and retries retain it and bypass generic requirement-list and platform-help shortcuts. The console shows the selection with Clear finding; choosing another finding replaces it, and changing user, organization, or project clears it. Access is checked again for every turn. Unavailable or incomplete evidence returns an explicit limitation: older unpinned reviews, unverified form-model fields, missing source editions, fingerprint failures, ambiguous citations, and reading limits are reported rather than replaced by current requirements or a guessed answer.
Finding answers bypass both the general tool-backed answer loop and docs-only Eve retrieval. A stateless, tool-free selector can choose known passage identifiers, but its prose is never displayed or saved. The server always includes the saved citation, may add validated nearby passages, and renders literal quotations with their source edition and page labels. Governing section scope, equipment applicability, and whether the applicant's drawing passes remain unconfirmed. Invalid model output returns a fixed source-first response, not a second general-agent attempt.
Clearing a finding restores ordinary routing in a fresh conversational context so previous finding answers are not reused as evidence. This change does not select a different model or change Eve rollout controls. Selected-packet metadata still comes from the current browser session and active organization, including visible teammate attachments. A denied lookup remains unavailable rather than falling back to personal File Library totals. Saved-finding selection is console-only; it creates no authentication bridge or new source access for Slack, Teams, public API, MCP, or CLI credentials.
The web adapter authenticates the session and organization before consuming the request. It rejects serialized chat bodies over 256 KiB before JSON parsing; invalid JSON and non-object bodies stop at the adapter, so oversized transcripts cannot reach the shared chatbot core.
The in-app assistant at /assistant provides the richest experience:
- Real-time text streaming with sentence-boundary buffering
- Tool call visibility (expandable sections showing what the AI is doing)
- Reasoning display (thinking blocks)
- Attachment UI support (PDF, JSON). Attachments can be selected in the chat UI, but the
/assistantpage does not yet process uploaded files for analysis in that surface. - Session metadata for conversation continuity
Slack Bot
The GridWonk Slack app provides:
- Threaded conversations with multi-turn context
- Native processing status in Agent View; threaded replies can update a progress message into the final answer, while root direct messages post one final message
- Citations listed under Sources at the end of the reply
- Error messages formatted as blockquotes. If a turn half-answered and then failed, the partial answer is kept and marked incomplete rather than discarded — and a turn that failed having said nothing shows the reason alone, without claiming an answer was cut short. A failed turn always carries a reason; a blank explanation is replaced with a generic one rather than shown empty. The same holds when the connection behind a turn drops rather than the turn reporting a failure: text the assistant had already composed but not yet posted is still delivered, along with any citations it had gathered, instead of being lost with the connection
- Rate limiting per user
- Slash commands including
/gk help,/gk requirements,/gk status,/gk context, and the feature-gated/gk research,/gk research status, and/gk research stop
/gk status links to the console for validation job monitoring; it is not a native Slack job list.
The Slack transport and the Teams transport use the same Chat SDK 4.41.1 release train. GridWonk
keeps chat, the Slack adapter, the Redis state adapter, and the Teams adapter pinned to that exact
version together. The repository also carries a versioned product-bot manifest for Slack Agent
View, but publishing that manifest is a separate maintenance-window action; merging or deploying
the application does not irreversibly switch the Slack app to Agent View.
When Agent View is enabled, the Slack adapter supplies native session status, automatic titles, stop handling, app-home/context lifecycle events, and cross-instance turn cancellation through its Redis state. GridWonk passes the SDK turn cancellation signal into the shared chatbot core. Thread context comes from the SDK transcript instead of Slack channel history because Agent View direct message history exposes only the user's side. The custom uninstall event remains responsible for deleting the adapter installation and marking the organization installation inactive.
Organization administrators can separately enable Assistant skill sets and assign versioned instruction bundles for interconnection research, project coordination, or deficiency review. The Slack adapter resolves those audited organization assignments and supplies them as trusted prompt guidance. A bundle never adds a tool, widens data access, or bypasses an ordinary authorization or confirmation check. Other transports do not accept client-selected bundles.
When Deep Research is enabled for the organization, /gk research starts a bounded durable
workflow. The default lane searches only current procedure sources that back published packet-review
requirements for a release-switched utility. Organization-private documents are searched only when
the user explicitly selects that lane. Paid web search is also explicit and remains disabled for
customer organizations. Every lane fails closed when its source is unavailable. Slack returns a durable, organization-scoped report link with citations, while
status and stop inspect or request cancellation of the user's run. The workflow checks its
durable cancellation marker between collection and synthesis steps.
Slack installation callbacks consume signed state once and re-check the current approved platform administrator before storing a workspace mapping, so copied or stale callbacks cannot replay an older installation authorization.
Direct-message replies that finish after the webhook response recover Slack's raw D… channel ID
from the signed event or encoded thread ID, reload that event's workspace installation, and bind its
bot token before posting. This keeps delayed answers on the correct organization's Slack connection
even after request-local credential context has ended, without leaking Chat SDK's slack:D… thread
encoding into Slack's channel API.
Installation: An organization admin connects their own workspace from the workspace
Features page, or a platform admin installs it via the "Add to Slack" button on
/admin/integrations; both start the same OAuth flow for the active organization, and the callback returns each to the
page the flow started from. A workspace already connected to a different organization is refused
for org admins — only a platform admin can move a workspace between organizations. The OAuth callback at app/api/chat-sdk/slack/callback/route.ts stores the workspace bot token in Redis and provisions a chat_platform_installations record mapping the Slack workspace to a GridWonk organization. Both records are required for replies, so reauthorizing from /admin/integrations restores them together.
Mention the installed GridWonk app in a channel, or message it directly. Follow-up messages in the same thread continue the conversation. See Set up and use GridWonk in Slack for the complete administrator and member walkthrough.
User linking: Slack users must be linked to a GridWonk account. The auth bridge (lib/chat-sdk/auth-bridge.ts) resolves Slack workspace IDs → GridWonk organizations and Slack user IDs → GridWonk user accounts via chat_platform_installations and chat_user_mappings tables. Unlinked users receive a sign-up/link prompt directing them to /chat/slack/link.
For ordinary Slack access, the linked account must have an active, unexpired organization membership. Revoked or expired memberships are denied even when the account retains a platform-admin role.
Public API
The POST /api/v1/agents/chat endpoint provides (citations require includeSources: true):
- SSE streaming for real-time responses
- Full orchestration pipeline (FAQ, classification, routing)
- Session continuity via
sessionId - Structured event types (
content,usage,tool_use,tool_result,complete,error, andsourcewhen opted in) - Completion diagnostics that include
directToolNamewhen a direct tool fast path answered without a model-backed tool event
When an API client disconnects, the streaming adapter closes the upstream chatbot iterator as well; the turn can therefore release its session and lease promptly rather than waiting for timeout. The close takes effect at the turn's next pause point, so a request already in flight finishes before the release — prompt, not instant. Every other channel gets this for free, because each drains its turn with a loop that closes the stream on the way out; the API lane is the one that had to ask.
See Chat Streaming API for full endpoint documentation.
MCP
The hosted MCP gateway exposes a chat tool that runs the same pipeline. Unlike the streaming
channels it returns a single buffered result, so a long answer arrives all at once rather than
progressively. The result carries the answer's citations both inline (a Sources list at the end
of the text) and structurally on sources, plus the tools the turn ran on tools.
CLI
gridwonk agents chat routes through the public API endpoint and renders the SSE stream in your
terminal. It carries its own channel label, so CLI usage is distinguishable from other API traffic
in your organization's analytics. The CLI opts into citation frames: human-readable output appends
a de-duplicated Sources list, while --json emits the structured source events as NDJSON.
Microsoft Teams
The Teams channel is shipped and account-controlled. The feature defaults to on for every organization; an organization admin can turn it off from Features, and a platform admin can force it on or off from the organization detail page. GridWonk operates one single-tenant Azure Bot and requires its app ID, client secret, and home-tenant ID before registering the Teams webhook.
An organization administrator starts a customer connection from GridWonk. The server creates a
random Redis-backed code that expires after 10 minutes and opens a personal bot chat with
connect <code> prefilled. The bot accepts that command only in a 1:1 chat, consumes the code once,
and reads the Microsoft tenant ID from the signed Teams activity. It then returns a browser
confirmation that works only for the same signed-in GridWonk user. Confirmation rechecks current
organization-admin authority and the Teams feature before atomically creating the tenant mapping
and linking that Teams user. Expired or replayed codes, Redis failures, missing tenant IDs, and
cross-organization tenant conflicts fail closed; only a current GridWonk platform administrator may
resolve a tenant reassignment.
From the workspace Features page, the customer-facing flow has two steps: install GridWonk from the Teams Store, then open the generated private bot chat and send the one-time connection command. The same page shows the connected tenant and lets an organization administrator disconnect or reactivate it. Customers do not supply Azure credentials; GridWonk owns the bot registration and Store app.
After connection, @mention the bot in a channel or message it 1:1. Each additional person links their own Teams identity to their GridWonk account through a separate one-time secure link before the assistant answers them. Teams replies arrive as single messages (no in-place streaming status), and each person keeps their own conversation memory across follow-up turns. In shared channel threads, session keys include the Teams user id and stored thread state is reused only for the same author, so one member cannot inherit another member's session or selected work context. The same organization scoping, identity, and audit rules apply as on Slack.
Who can see internal documentation
Most documentation is public to signed-in users. A small set of internal pages is restricted to GridWonk platform staff, and that restriction is applied per request on every channel — never inherited from where the question was asked.
| Channel | Internal docs reachable by |
|---|---|
| Web app | A platform administrator working natively in the GridWonk organization. Administrators impersonating a customer see exactly what that customer sees. |
| Slack | A Slack user who has linked their account and is a platform administrator |
| Microsoft Teams | A Teams user who has linked their account and is a platform administrator |
| Public API / CLI / MCP | Not by default. An API key is a weaker credential than a signed-in session, so keys do not inherit their creator's internal access unless the deployment explicitly opts in. Acting on behalf of another user never grants you their access. |
Everyone else — including every customer organization on every channel — receives public documentation only, and a test API key never receives internal documentation at all.
Operational Verification
- Send the same query to web chat and Slack — verify matching routing decisions in logs
- Ask a canonical FAQ question — all channels should return the same instant response
- Check session analytics —
executionMode,taskCategory, androutingReasonshould be consistent - Trigger a direct-model failure before output — verify one Eve recovery attempt and no
agent_sdkexecution record - Ask the same documentation question on every enabled channel — final text, citations, and completion metadata should match, whichever lane answered
- Confirm an unlinked Slack user is prompted to link rather than answered, and that a non-admin cannot retrieve internal documentation on any channel
- From an unconnected Microsoft tenant, confirm an ordinary question cannot reach product data and only a private
connect <code>command can begin tenant setup