Inspiration

The important part of work is rarely a single message. A decision is made at the end of a thread, a bug report depends on details several replies earlier, and an owner or deadline may only be mentioned once. Later, somebody has to reconstruct that context, repeat it to an assistant, and manually carry the result into Jira.

We built Loop because an agent inside Slack should understand the conversation it was invited into, remember the decisions worth keeping, and help complete the work that follows. It should also be clear about where its context came from and ask for approval before taking consequential action.

What it does

Loop is a Slack-native AI teammate available through channel mentions, direct messages, /loop, and Slack's native AI Assistant pane. These entry points share the same tools, memory, safety rules, and observability.

Loop works across four connected layers:

  • Conversation context: For a reply inside a Slack thread, Loop retrieves a bounded, chronological view of that reply chain. For a top-level channel or DM request, it retrieves recent history from that surface. A follow-up such as "who owns this?" can therefore be answered from the active conversation without asking the user to repeat every detail.
  • Durable memory: Decisions and important facts can be retained beyond the current thread. Each memory carries provenance such as author, channel, workspace, source app, and thread. Hybrid retrieval combines lexical and semantic search, so a later question can find a decision even when it uses different wording.
  • Human-approved action: When a Slack conversation describes a bug, Loop can prepare a Jira issue, resolve a named teammate through Slack identity to the corresponding Jira account, check for duplicates, and show the proposed ticket in the original thread. The issue is created only after a person confirms it.
  • Proactive follow-through: Optional passive capture can retain decisions and commitments from normal channel conversation. Optional ambient mode can revisit an unresolved thread and post one bounded, context-aware nudge without silently creating external work.

Loop also understands common Slack attachments, including images, PDFs, text, code, and JSON, so the artifact being discussed can remain with the conversation.

How we built it

Slack Bolt in Socket Mode handles events from mentions, DMs, slash commands, and the Assistant surface. At the edge, Loop normalizes every request to a canonical Slack conversation or thread identity. It then retrieves bounded Slack context, collects supported attachments, and creates request-scoped provenance before the agent runs. This avoids treating an agent's internal chat history as a substitute for the actual Slack conversation and prevents context from one request leaking into another.

The agent is built with Strands Agents. Its model comes from an environment-driven provider factory, while capabilities are exposed as tools: Slack operations, memory, Jira workflows, calculation and reasoning helpers, plus MCP tools loaded at runtime. This keeps the workflow extensible without coupling it to a single model vendor or hard-coding a new Slack command for every capability.

Memory uses SQLite for local persistence and full-text retrieval. The optional hybrid backend adds local embeddings and a vector index, then fuses lexical and semantic rankings with Reciprocal Rank Fusion. Retrieval is scoped using Slack provenance, and recalled entries include their source instead of appearing as unattributed model knowledge.

Consequential Jira operations have a deterministic boundary around the agent: the model may extract and propose ticket details, but the application owns thread identity, pending state, confirmation, duplicate checks, and the final REST call. The same request identity connects the Slack exchange, Jira action, telemetry, and memory record.

Each interaction produces structured telemetry and step traces for model and tool activity. Guardrails limit tool calls, repeated actions, token usage, and risky Slack methods. Golden evaluations exercise the real agent path so context, memory, tool use, and safety behavior can be checked as the system evolves.

Challenges we ran into

Slack context is not one thing

A top-level channel message, a reply-chain thread, a DM, and an Assistant thread have different Slack APIs and identifiers. Early versions could answer the latest prompt but lose the subject of a follow-up, especially inside reply threads. We fixed this by treating Slack as the source of truth: canonicalize the thread root, retrieve the correct bounded history for that surface, order it chronologically, and fail gracefully if history is unavailable.

Short-term context and long-term memory need different rules

Recent Slack messages answer "what are we discussing now?" Durable memory answers "what did we decide before?" Mixing the two creates stale answers and privacy risks. Loop now keeps them separate, scopes both with request provenance, and clears model-side conversation history between requests.

A person's name is not a Jira identity

People naturally assign work by saying a teammate's name, but Jira expects an account ID. Loop resolves the name through Slack membership and email before querying Jira, handles ambiguous or missing matches explicitly, and never invents an assignee. Keeping creation behind an in-thread confirmation made this workflow both useful and predictable.

Agent reliability needs application-level controls

Prompts alone were not enough for duplicate prevention, confirmation, or bounded tool use. We combined model judgment with deterministic application checks, request-scoped state, guardrails, traces, and repeatable evaluations. That balance made the demo behavior easier to trust and the failures easier to diagnose.

Accomplishments that we are proud of

  • A follow-up in a Slack reply chain can rely on details from earlier replies without leaking context from another channel or user.
  • Durable decisions are recalled semantically and returned with provenance rather than as unsupported model assertions.
  • A conversational bug report becomes a reviewed Jira issue in the same thread, including practical Slack-to-Jira assignee resolution.
  • Reactive and proactive behaviors share one context, memory, safety, and tracing model across Slack mentions, DMs, slash commands, and the AI Assistant pane.
  • The system is provider-configurable, self-hostable at the application layer, and extensible through MCP while retaining deterministic controls around side effects.

What we learned

The quality of an agent depends as much on context orchestration as on the model. The most reliable design was to let Slack own recent conversation truth, let the memory layer own durable knowledge, and give every retrieved item explicit scope and provenance.

We also learned that useful autonomy is bounded autonomy. Users are comfortable letting an agent extract, search, and prepare work when they can inspect the proposal and retain control over the irreversible step. Traces and evaluations are not just developer tooling in that design; they are part of how the product earns trust.

What's next

The next step is a managed multi-workspace deployment with OAuth, workspace-level data isolation, authorization policies, retention controls, and PII redaction. We also want to add org-wide Slack search for explicitly authorized knowledge, improve reranking and near-duplicate memory consolidation at larger scale, and export traces to standard observability backends. Ambient behavior will remain opt-in while we learn how teams want nudges, ownership, and escalation policies to work in real channels.

Built With

  • ai-agents
  • anthropic
  • docker
  • evals
  • fastembed
  • fts5
  • jira
  • libsql
  • mcp
  • minimax
  • oauth2
  • observability
  • python
  • rag
  • rrf
  • slack
  • slack-ai
  • slack-ai-assistant
  • slack-bolt
  • slack-sdk
  • socket-mode
  • sqlite
  • strands-agents
  • turso
  • vector-search
Share this project:

Updates