Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions canvas/developers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ Two packages own this territory:

## The big picture

![DataManager architecture: the Actor calls primitives.data.*, which lands on DataManager (contract in base.py, implementations in ops/, schemas in types/); ContextRegistry supplies destinations; SimulatedDataManager mirrors the same contract in memory; persistence flows through unisdk HTTP to Orchestra, where contexts hold log events across personal Data/… and Teams/{id}/Data/… roots with federated reads between them.](/images/developers/datamanager-architecture.png)
<img className="block dark:hidden" src="/images/developers/datamanager-architecture-light.svg" alt="DataManager architecture: the Actor calls primitives.data.*, which lands on DataManager (contract in base.py, implementations in ops/, schemas in types/); ContextRegistry supplies destinations; SimulatedDataManager mirrors the same contract in memory; persistence flows through unisdk HTTP to Orchestra, where contexts hold log events across personal Data/… and Teams/{id}/Data/… roots with federated reads between them." />
<img className="hidden dark:block" src="/images/developers/datamanager-architecture-dark.svg" alt="DataManager architecture: the Actor calls primitives.data.*, which lands on DataManager (contract in base.py, implementations in ops/, schemas in types/); ContextRegistry supplies destinations; SimulatedDataManager mirrors the same contract in memory; persistence flows through unisdk HTTP to Orchestra, where contexts hold log events across personal Data/… and Teams/{id}/Data/… roots with federated reads between them." />

Both packages follow the same layered pattern, shared with every state
manager in the repo:
Expand Down Expand Up @@ -129,7 +130,8 @@ Context paths resolve through three private helpers in `DataManager`:
`federated_ranked_search`, and `federated_reduce` from
[`unify/common/federated_search.py`](https://github.com/unifyai/unify/blob/main/unify/common/federated_search.py).

![Destination scopes: write_root routes each write to exactly one destination — the personal root or a team root, each holding Data and Dashboards contexts — while read_roots federates reads across personal plus all teams; data_scope additionally lets a personal tile read team data.](/images/developers/destination-scopes.png)
<img className="block dark:hidden" src="/images/developers/destination-scopes-light.svg" alt="Destination scopes: write_root routes each write to exactly one destination — the personal root or a team root, each holding Data and Dashboards contexts — while read_roots federates reads across personal plus all teams; data_scope additionally lets a personal tile read team data." />
<img className="hidden dark:block" src="/images/developers/destination-scopes-dark.svg" alt="Destination scopes: write_root routes each write to exactly one destination — the personal root or a team root, each holding Data and Dashboards contexts — while read_roots federates reads across personal plus all teams; data_scope additionally lets a personal tile read team data." />

`ContextRegistry`
([`unify/common/context_registry.py`](https://github.com/unifyai/unify/blob/main/unify/common/context_registry.py))
Expand Down Expand Up @@ -315,7 +317,8 @@ bindings; *the Actor never writes bridge API calls*.

## Life of a live tile

![Live tile lifecycle: the Actor calls create_tile with HTML, data bindings, and on_data; DashboardManager validates and dry-runs the bindings, stores the tile row and registers its token; the Console TileViewer renders the iframe and injects the UnifyData bridge, which runs live filter/reduce/join queries against the Orchestra bridge at render time; results are handed to on_data so the chart updates with fresh data.](/images/developers/live-tile-lifecycle.png)
<img className="block dark:hidden" src="/images/developers/live-tile-lifecycle-light.svg" alt="Live tile lifecycle: the Actor calls create_tile with HTML, data bindings, and on_data; DashboardManager validates and dry-runs the bindings, stores the tile row and registers its token; the Console TileViewer renders the iframe and injects the UnifyData bridge, which runs live filter/reduce/join queries against the Orchestra bridge at render time; results are handed to on_data so the chart updates with fresh data." />
<img className="hidden dark:block" src="/images/developers/live-tile-lifecycle-dark.svg" alt="Live tile lifecycle: the Actor calls create_tile with HTML, data bindings, and on_data; DashboardManager validates and dry-runs the bindings, stores the tile row and registers its token; the Console TileViewer renders the iframe and injects the UnifyData bridge, which runs live filter/reduce/join queries against the Orchestra bridge at render time; results are handed to on_data so the chart updates with fresh data." />

The `create_tile` pipeline in `DashboardManager`, step by step:

Expand Down
7 changes: 4 additions & 3 deletions communication/behavior.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ Your assistant isn't purely reactive:
tells you on an appropriate channel.
- **Scheduled work** — [tasks](/tasks/overview) you've scheduled can end
with a message, an email, or a call at the time you chose.
- **Gentle follow-ups** — if a conversation goes quiet while something needs
your input, it may send a friendly follow-up email. Ask it to stop and it
stops.
- **Gentle follow-ups** — if a conversation with your T-W1N goes quiet while
something needs your input, it may send a friendly follow-up email. This
re-engagement is specific to your T-W1N; hired teammates don't do it. Ask it
to stop and it stops.
- **Ringing you** — when a live conversation beats a wall of text, it can
ring you on [Unify Meet](/communication/unify-meet), and falls back to chat
if you don't pick up.
Expand Down
3 changes: 2 additions & 1 deletion communication/developers/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ The communication stack is two cooperating pieces:
back through the gateway via
[`unify/comms/primitives.py`](https://github.com/unifyai/unify/blob/main/unify/comms/primitives.py).

![unify.gateway architecture: providers feed adapters, which normalize to envelopes delivered through an EnvelopeSink to the ConversationManager; the ConversationManager sends outbound via channels](/images/developers/gateway-architecture.png)
<img className="block dark:hidden" src="/images/developers/gateway-architecture-light.svg" alt="unify.gateway architecture: providers feed adapters, which normalize to envelopes delivered through an EnvelopeSink to the ConversationManager; the ConversationManager sends outbound via channels" />
<img className="hidden dark:block" src="/images/developers/gateway-architecture-dark.svg" alt="unify.gateway architecture: providers feed adapters, which normalize to envelopes delivered through an EnvelopeSink to the ConversationManager; the ConversationManager sends outbound via channels" />

## The wire contract: envelopes

Expand Down
14 changes: 11 additions & 3 deletions communication/developers/conversation-manager.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ state, consumes events, runs the reasoning loop, and delegates outbound
I/O. Its package README describes it as the layer that interfaces with
users while delegating complex reasoning to the `Actor`.

![Inbound event lifecycle: envelope → CommsManager → EventBroker → ConversationManager.wait_for_events → side effects → slow brain turn → comms tools / act / wait](/images/developers/event-lifecycle.png)
<img className="block dark:hidden" src="/images/developers/event-lifecycle-light.svg" alt="Inbound event lifecycle: envelope → CommsManager → EventBroker → ConversationManager.wait_for_events → side effects → slow brain turn → comms tools / act / wait" />
<img className="hidden dark:block" src="/images/developers/event-lifecycle-dark.svg" alt="Inbound event lifecycle: envelope → CommsManager → EventBroker → ConversationManager.wait_for_events → side effects → slow brain turn → comms tools / act / wait" />

## The event model

Expand All @@ -23,7 +24,10 @@ Subclasses auto-register via `__init_subclass__`, serialize with

The catalogue is broad and symmetric: for most channels there's a
`*Received` and a `*Sent` class (`SMSReceived`/`SMSSent`,
`EmailReceived`/`EmailSent`, `UnifyMessageReceived`/`UnifyMessageSent`…),
`EmailReceived`/`EmailSent`, `UnifyMessageReceived`/`UnifyMessageSent`, and —
for the org-installed Teams app — separate DM and channel pairs
`MsTeamsBotMessageReceived`/`MsTeamsBotMessageSent` and
`MsTeamsBotChannelMessageReceived`/`MsTeamsBotChannelMessageSent`…),
plus call-lifecycle events (`PhoneCallReceived`, `PhoneCallAnswered`,
`PhoneCallEnded`, `UnifyMeetStarted`…), per-utterance voice events
(`InboundPhoneUtterance`, `OutboundUnifyMeetUtterance`…), reactions,
Expand Down Expand Up @@ -52,7 +56,11 @@ Two mapping layers connect the world to handlers:
defines the `Medium` enum — the single source of truth for channel types
(`UNIFY_MESSAGE`, `EMAIL`, `SMS_MESSAGE`, `WHATSAPP_MESSAGE`,
`WHATSAPP_CALL`, `PHONE_CALL`, `UNIFY_MEET`, `GOOGLE_MEET`, `TEAMS_MEET`,
Slack/Discord/Teams variants…). Each medium registers a `MediumInfo` with a
Slack/Discord variants, and both the delegated-Graph
(`TEAMS_MESSAGE`, `TEAMS_CHANNEL_MESSAGE`) and org-installed bot
(`MS_TEAMS_BOT_MESSAGE`, `MS_TEAMS_BOT_CHANNEL_MESSAGE`) Teams mediums…).
The two org-installed bot send tools are gated behind the assistant's
`has_ms_teams_bot` capability. Each medium registers a `MediumInfo` with a
**mode**: `TEXT`, `CALL`, or `MEET`
([`cm_types/mode.py`](https://github.com/unifyai/unify/blob/main/unify/conversation_manager/cm_types/mode.py)).
Mode is what gates behavior — voice tools like `guide_voice_agent` only
Expand Down
6 changes: 4 additions & 2 deletions communication/developers/gateway.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ its router(s):
| [`gmail/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/gmail/views.py) | `send_email` (threading headers), `watch_email` (Pub/Sub push watch), attachment fetch | `googleapiclient`, `google.oauth2` |
| [`outlook/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/outlook/views.py) | Same shape over Microsoft Graph subscriptions | `msgraph` via [`common/graph.py`](https://github.com/unifyai/unify/blob/main/unify/gateway/common/graph.py) |
| [`email/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/email/views.py) | Provider-agnostic dispatcher — routes `send_email` to Gmail or Outlook based on how the assistant's mailbox is connected | — |
| [`teams/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/teams/views.py) | Chats, group chats, channels, watches, and meeting creation (`create_meeting.py`: instant links vs scheduled calendar events) | `msgraph` |
| [`teams/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/teams/views.py) | Delegated Microsoft Graph Teams: chats, group chats, channels, watches, and meeting creation (`create_meeting.py`: instant links vs scheduled calendar events) | `msgraph` |
| [`ms_teams_bot/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/ms_teams_bot/views.py) | Org-installed Bot Framework Teams app: pending-install / tenant-bind / install upsert, `send` (DM + channel/group), Bot Connector token minting from the bot's home tenant | Bot Framework Connector |
| [`slack/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/slack/views.py) | Workspace install upsert, `send_slack_message`, user lookup by email | Slack Web API |
| [`discord/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/discord/views.py) | `send_discord_message`, bot pool registration and status | Discord REST |
| [`drive/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/drive/views.py) / [`sharepoint/`](https://github.com/unifyai/unify/blob/main/unify/gateway/channels/sharepoint/views.py) | File browsing for the workspace file picker (not messaging) | Google Drive API, `msgraph` |
Expand All @@ -63,7 +64,8 @@ its router(s):
|---|---|---|
| [`twilio.py`](https://github.com/unifyai/unify/blob/main/unify/gateway/adapters/twilio.py) | SMS, voice calls, call status, WhatsApp messages/reactions/calls/call status | `msg`, `call`, `call_answered`, `call_not_answered`, `whatsapp`, `whatsapp_reaction`, `whatsapp_call`, … |
| [`google.py`](https://github.com/unifyai/unify/blob/main/unify/gateway/adapters/google.py) | Gmail Pub/Sub push notifications, Google OAuth callback and revoke | `email` |
| [`microsoft.py`](https://github.com/unifyai/unify/blob/main/unify/gateway/adapters/microsoft.py) | Outlook and Teams Graph notifications, Microsoft OAuth callback, a shared `/microsoft/router` delegator | `email`, `teams_chat`, `teams_channel` |
| [`microsoft.py`](https://github.com/unifyai/unify/blob/main/unify/gateway/adapters/microsoft.py) | Outlook and delegated-Teams Graph notifications, Microsoft OAuth callback, a shared `/microsoft/router` delegator | `email`, `teams_chat`, `teams_channel` |
| [`ms_teams_bot.py`](https://github.com/unifyai/unify/blob/main/unify/gateway/adapters/ms_teams_bot.py) | Bot Framework activity webhook for the org-installed Teams app; validates the inbound Bot Framework JWT ([`ms_teams_bot_auth.py`](https://github.com/unifyai/unify/blob/main/unify/gateway/adapters/ms_teams_bot_auth.py)), resolves the assistant via the dispatcher (token addressing + coordinator fallback) | `ms_teams_bot` |
| [`slack.py`](https://github.com/unifyai/unify/blob/main/unify/gateway/adapters/slack.py) | Slack Events API, with a two-pass sender-identity resolution for coordinator routing and message dedup | `slack` |
| [`internal.py`](https://github.com/unifyai/unify/blob/main/unify/gateway/adapters/internal.py) | Console/runtime routes: chat messages, reactions, attachments, Unify Meet rings, system events, pre-hire chat logging, wake-ups | `unify_message`, `unify_meet`, `api_message`, `unity_system_event`, … |

Expand Down
3 changes: 2 additions & 1 deletion communication/developers/voice.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ sub-second conversational reflexes *and* deep reasoning, and no single
loop provides both. The runtime splits the job between two brains that
share one conversation.

![Voice call dual-brain architecture: the caller connects through a LiveKit room to the fast brain (Assistant agent with STT/VAD/TTS), which exchanges utterance events and FastBrainNotifications with the slow brain (ConversationManager), supported by fillers/barge-in handling and the LivekitCallManager](/images/developers/voice-dual-brain.png)
<img className="block dark:hidden" src="/images/developers/voice-dual-brain-light.svg" alt="Voice call dual-brain architecture: the caller connects through a LiveKit room to the fast brain (Assistant agent with STT/VAD/TTS), which exchanges utterance events and FastBrainNotifications with the slow brain (ConversationManager), supported by fillers/barge-in handling and the LivekitCallManager" />
<img className="hidden dark:block" src="/images/developers/voice-dual-brain-dark.svg" alt="Voice call dual-brain architecture: the caller connects through a LiveKit room to the fast brain (Assistant agent with STT/VAD/TTS), which exchanges utterance events and FastBrainNotifications with the slow brain (ConversationManager), supported by fillers/barge-in handling and the LivekitCallManager" />

## The two brains

Expand Down
13 changes: 7 additions & 6 deletions communication/email.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ Your assistant can send and receive email from a real mailbox — replying in
threads, handling attachments, and looping in cc'd colleagues, just like any
teammate.

<Note>
**If this is your T-W1N** (your digital twin): its email is platform-managed
and already set up — there's nothing to create or configure. T-W1N acts on
*your* behalf, so it uses your own connected account rather than a mailbox of
its own. The setup below is for **hired teammates**.
</Note>

## How your assistant gets an email address

Your assistant uses a **Google (Gmail) or Microsoft (Outlook / Microsoft 365)
Expand All @@ -32,12 +39,6 @@ mailbox, which means email from your assistant lands like email from a person.
</Step>
</Steps>

<Note>
**T-W1N, your digital twin, is the exception**: it's meant to act on *your*
behalf, so you connect your own account to it. Its contact details are
platform-managed — there's nothing to create or configure.
</Note>

Connecting email is free — there are no setup or monthly credit costs for
email contacts.

Expand Down
76 changes: 51 additions & 25 deletions communication/microsoft-teams.mdx
Original file line number Diff line number Diff line change
@@ -1,35 +1,61 @@
---
title: "Microsoft Teams"
description: "Chats, group chats, channels, and meetings with your assistant"
description: "Bring your assistants into Teams with the org-installed Unify app"
---

If your organization runs on Microsoft Teams, your assistant fits right in —
one-on-one chats, group chats, channel threads, and even creating and joining
Teams meetings.
If your organization runs on Microsoft Teams, the **Unify Teams app** brings
your assistants right into it — one-on-one chats, group chats, and channel
threads. It's an org-wide app your admin installs once, so everyone can reach
your assistants without any per-assistant setup. This is the Teams equivalent
of the [Slack app](/communication/slack): the assistant shows up as the shared
Unify bot, not as a person.

<Note>
There's a second, separate way to use Teams: an assistant can act through
its **own connected Microsoft 365 account** (the same account as its
[mailbox](/workspace/email)) — appearing as that user rather than the shared
app. That path is part of the workspace connection and is documented under
[Workspace → Teams](/workspace/teams).
</Note>

## Setup

Teams comes with the **Microsoft 365 connection** — the same one used for
[Outlook email](/communication/email). When you connect a Microsoft account
for your assistant, grant the **Teams** feature and Teams chat is enabled.
A Microsoft admin installs the **Unify Teams app** into your organization's
Teams. Once it's installed, every assistant in the org — and your coordinator
— is reachable through it; there's nothing to configure per assistant.

## Messaging

- **1:1 chat** — message your assistant in Teams like any colleague.
- **Group chats** — your assistant can take part in group conversations with
two or more people, and can start them too.
- **Channels** — @mention your assistant in a channel and it replies in the
thread; it can also post to channels and even create new ones.
- **Attachments** — your assistant can send files in Teams messages.

## Meetings

Your assistant is unusually capable with Teams meetings:

- **Create instant meetings** — ask for a meeting link and it generates one
on the spot.
- **Schedule meetings** — it can put a Teams meeting on the calendar with
invites sent to attendees.
- **Join meetings** — send your assistant a Teams meeting link and it joins
the call and participates with its voice, like any remote attendee. See
[Meetings](/communication/meetings).
- **1:1 chat** — message the Unify app in Teams and your coordinator picks up
the conversation.
- **Group chats** — add the app to a group chat and @mention it to bring it
in; it replies in the same conversation.
- **Channels** — @mention the app in a channel and it replies in the thread,
keeping channels tidy.

## Reaching a specific teammate

Because the app is shared, addressing works like Slack:

- In a **channel or group chat**, @mention the app and include the teammate's
name to reach a specific assistant (for example, `@Unify Alex, can you…`).
@mention it with no name and the message goes to your **coordinator**, who
can hand off from there.
- In a **1:1 chat**, messages go to your coordinator — there's no name to add,
so naming a teammate isn't used here.

<Note>
Teams only delivers channel and group-chat messages to the app when it's
**@mentioned**, so remember the mention when you want the assistant to see a
message in a shared space. In a 1:1 chat every message is delivered.
</Note>

## Good to know

- Your assistant only speaks in a channel when it's mentioned or directly
relevant — it won't spam your team's conversations.
- It's the same assistant across every surface: a task you discussed in Teams
can be delivered over email, and vice versa.
- **Meetings** are separate from the app. Your assistant can join and host
Teams meetings — see [Meetings](/workspace/meetings) for joining, and
[Workspace → Teams](/workspace/teams) for creating and scheduling them.
Loading
Loading