Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
8a57944
Add Contact Center module
MikeAlhayek Jun 29, 2026
a387652
Address feedback
MikeAlhayek Jun 29, 2026
f90a595
Add Contact Center agents, queues, reservations, assignment, and dial…
MikeAlhayek Jun 29, 2026
56602a7
fix NRE
MikeAlhayek Jun 29, 2026
7760740
Add Contact Center inbound voice routing and Telephony incoming-call …
MikeAlhayek Jun 29, 2026
fae2c01
Add phase 2
MikeAlhayek Jun 29, 2026
aca69af
[Refactor] Align Contact Center admin and voice routing
MikeAlhayek Jun 29, 2026
79bf91c
[Fix] Add Contact Center skills and presence cleanup
MikeAlhayek Jun 29, 2026
0b4dbc0
Use Soft Phone for agent workspace
MikeAlhayek Jun 30, 2026
de7113c
[Fix] Render contact center soft phone tab
MikeAlhayek Jun 30, 2026
2c0396d
Fix softphone
MikeAlhayek Jun 30, 2026
db2359d
Fix softphone
MikeAlhayek Jun 30, 2026
3035735
Close gaps
MikeAlhayek Jun 30, 2026
2e18a98
Close gaps
MikeAlhayek Jun 30, 2026
06ff84c
Contact Center: complete Phase 3 routing depth (G7) + land real-time/…
MikeAlhayek Jul 1, 2026
061c8af
Contact Center: signed provider voice webhook adapters + idempotency …
MikeAlhayek Jul 1, 2026
5cb4675
Contact Center: compare-and-set on reservation creation (G2/P0 #7)
MikeAlhayek Jul 1, 2026
7d351ed
Contact Center: transfer/conference taxonomy + orchestration (G1, com…
MikeAlhayek Jul 1, 2026
2bfe4b4
Contact Center: inbound entry points (Phase 8)
MikeAlhayek Jul 1, 2026
663060b
Contact Center: callback scheduling (Phases 5/10)
MikeAlhayek Jul 1, 2026
d613f33
Contact Center: daily event-metrics projection (Phase 12)
MikeAlhayek Jul 1, 2026
ba7ea94
Contact Center: recording + live-monitoring orchestration (Phase 9)
MikeAlhayek Jul 1, 2026
01bfc6a
Contact Center: optional OrchardCore Workflows bridge (Phase 11)
MikeAlhayek Jul 1, 2026
ee57213
Contact Center: interaction-event data retention (Phase 13)
MikeAlhayek Jul 1, 2026
fe87f6a
Contact Center: AI assist extensibility seam (Phase 14)
MikeAlhayek Jul 1, 2026
915422f
DialPad: complete inbound + outbound as the Contact Center phone prov…
MikeAlhayek Jul 1, 2026
18f2ad4
Finish core implementations
MikeAlhayek Jul 1, 2026
b9a5859
Finish core implementations
MikeAlhayek Jul 1, 2026
b50eb98
Merge origin/main into ma/add-contact-center
MikeAlhayek Jul 1, 2026
94f7cd4
[Security] Harden provider webhook logging
MikeAlhayek Jul 1, 2026
0420f36
Update Automatic workflow
MikeAlhayek Jul 1, 2026
526002f
[Feature] Align agent workspace and update resources
MikeAlhayek Jul 1, 2026
5b75a45
[Fix] Improve live dashboard agent status
MikeAlhayek Jul 1, 2026
03d5ff8
[Fix] Align select picker styling
MikeAlhayek Jul 1, 2026
518d953
Add Reporting Module
MikeAlhayek Jul 2, 2026
ecb83fe
Add activity loading
MikeAlhayek Jul 2, 2026
d8c2038
fix NRC
MikeAlhayek Jul 4, 2026
ca27a61
Merge branch 'main' into ma/add-contact-center
MikeAlhayek Jul 6, 2026
c363440
Use ai-chat-ui preview.98 in Resources
MikeAlhayek Jul 6, 2026
add5e71
cleanup
MikeAlhayek Jul 6, 2026
f9c2c5f
Add more function to activities bulk management
MikeAlhayek Jul 7, 2026
f7fbf86
fix activity batch
MikeAlhayek Jul 7, 2026
3a5d740
Add Asterisk
MikeAlhayek Jul 7, 2026
dba67ad
Merge branch 'main' into ma/add-contact-center
MikeAlhayek Jul 7, 2026
f7bf564
Improve SoftPhone and add Asterisk provider
MikeAlhayek Jul 9, 2026
2675ac7
Review hardening: fix CA1873 warnings, agent reserve guard, Asterisk …
MikeAlhayek Jul 10, 2026
1a6a0fc
fix call flow
MikeAlhayek Jul 10, 2026
38d5ffb
Harden telephony state synchronization
MikeAlhayek Jul 10, 2026
6290e3c
Fix provider-authoritative soft phone recovery
MikeAlhayek Jul 10, 2026
983f9ea
Harden real-time telephony recovery
MikeAlhayek Jul 10, 2026
22180ad
Fix inbound telephony release synchronization
MikeAlhayek Jul 10, 2026
9b9df60
Fix recurring inbound call synchronization
MikeAlhayek Jul 10, 2026
4014064
Self-heal zombie inbound offers via provider truth
MikeAlhayek Jul 11, 2026
126afd4
Harden Contact Center telephony self-healing and event pipeline
MikeAlhayek Jul 11, 2026
7e5329b
Add omnichannel activity management enhancements
MikeAlhayek Jul 11, 2026
66932aa
Merge branch 'main' into ma/add-contact-center
MikeAlhayek Jul 11, 2026
2862813
Refine omnichannel activity enhancements
MikeAlhayek Jul 11, 2026
c156dcd
Reuse primary contact phone columns
MikeAlhayek Jul 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1,532 changes: 1,532 additions & 0 deletions .github/contact-center/PLAN.md

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ CrestApps.OrchardCore is a collection of open-source modules for **Orchard Core
**Target Framework**: .NET 10.0 (net10.0)
**Architecture**: Modular, multi-tenant application framework

## Contact Center module (active, multi-phase project)

The **Contact Center** module set is a large, multi-phase orchestration layer being built between the CRM (Omnichannel) and Telephony modules. Before doing any Contact Center work (anything under `src/**/CrestApps.OrchardCore.ContactCenter*`), **ALWAYS read the durable plan first**:

- **Plan & progress:** [`.github/contact-center/PLAN.md`](contact-center/PLAN.md)

That document is the source of truth for the Contact Center architecture, phased scope, MVP definition, conceptual data model, event catalog, and current progress. Always read its **Progress status** section to see what is done and what is next, start at the lowest incomplete phase, and update that section after each meaningful change.

Key rules for this module set:

- Respect the layer boundary: **CRM (Omnichannel) owns business work data, Contact Center owns orchestration, Telephony owns media execution.** `OmnichannelActivity` remains the universal work item; `Interaction` is communication history for one attempt and never owns workflow or disposition.
- **Never** write competitor product names in code, comments, identifiers, or public docs. Adopt only the generic, industry-standard terminology defined in the plan's terminology/metrics section.
- Group related capabilities into separate, feature-gated Orchard modules/features (the plan's module breakdown), the way commercial platforms separate licensed capabilities.

## Working Effectively

### Prerequisites and Environment Setup
Expand Down
3 changes: 3 additions & 0 deletions CrestApps.OrchardCore.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Folder Name="/src/" />
<Folder Name="/src/Abstractions/">
<Project Path="src/Abstractions/CrestApps.OrchardCore.Abstractions/CrestApps.OrchardCore.Abstractions.csproj" />
<Project Path="src/Abstractions/CrestApps.OrchardCore.ContactCenter.Abstractions/CrestApps.OrchardCore.ContactCenter.Abstractions.csproj" />
<Project Path="src/Abstractions/CrestApps.OrchardCore.ContentTransfer.Abstractions/CrestApps.OrchardCore.ContentTransfer.Abstractions.csproj" />
<Project Path="src/Abstractions/CrestApps.OrchardCore.DncRegistry.Abstractions/CrestApps.OrchardCore.DncRegistry.Abstractions.csproj" />
<Project Path="src/Abstractions/CrestApps.OrchardCore.PhoneNumbers.Abstractions/CrestApps.OrchardCore.PhoneNumbers.Abstractions.csproj" />
Expand All @@ -27,6 +28,7 @@
<Project Path="src/Core/CrestApps.OrchardCore.AI.Chat.Core/CrestApps.OrchardCore.AI.Chat.Core.csproj" />
<Project Path="src/Core/CrestApps.OrchardCore.AI.Chat.Interactions.Core/CrestApps.OrchardCore.AI.Chat.Interactions.Core.csproj" />
<Project Path="src/Core/CrestApps.OrchardCore.AI.Core/CrestApps.OrchardCore.AI.Core.csproj" />
<Project Path="src/Core/CrestApps.OrchardCore.ContactCenter.Core/CrestApps.OrchardCore.ContactCenter.Core.csproj" />
<Project Path="src/Core/CrestApps.OrchardCore.ContentTransfer.Core/CrestApps.OrchardCore.ContentTransfer.Core.csproj" />
<Project Path="src/Core/CrestApps.OrchardCore.Core/CrestApps.OrchardCore.Core.csproj" />
<Project Path="src/Core/CrestApps.OrchardCore.Omnichannel.Core/CrestApps.OrchardCore.Omnichannel.Core.csproj" />
Expand Down Expand Up @@ -61,6 +63,7 @@
<Project Path="src/Modules/CrestApps.OrchardCore.AI.Prompting/CrestApps.OrchardCore.AI.Prompting.csproj" />
<Project Path="src/Modules/CrestApps.OrchardCore.AI/CrestApps.OrchardCore.AI.csproj" />
<Project Path="src/Modules/CrestApps.OrchardCore.AzureAIInference/CrestApps.OrchardCore.AzureAIInference.csproj" />
<Project Path="src/Modules/CrestApps.OrchardCore.ContactCenter/CrestApps.OrchardCore.ContactCenter.csproj" />
<Project Path="src/Modules/CrestApps.OrchardCore.ContentAccessControl/CrestApps.OrchardCore.ContentAccessControl.csproj" />
<Project Path="src/Modules/CrestApps.OrchardCore.ContentFields/CrestApps.OrchardCore.ContentFields.csproj" />
<Project Path="src/Modules/CrestApps.OrchardCore.ContentTransfer/CrestApps.OrchardCore.ContentTransfer.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
namespace CrestApps.OrchardCore.ContactCenter;

/// <summary>
/// Contains constant values shared across the Contact Center module set.
/// </summary>
public static class ContactCenterConstants
{
/// <summary>
/// The YesSql collection name used to store Contact Center documents in isolation from other modules.
/// </summary>
public const string CollectionName = "ContactCenter";

/// <summary>
/// The current schema version applied to newly published Contact Center domain events.
/// </summary>
public const int CurrentEventSchemaVersion = 1;

/// <summary>
/// Identifies a system actor for events that are not originated by an interactive user.
/// </summary>
public const string SystemActor = "system";

/// <summary>
/// Contains the feature identifiers exposed by the Contact Center module set.
/// </summary>
public static class Feature
{
/// <summary>
/// The identifier of the base Contact Center feature.
/// </summary>
public const string Area = "CrestApps.OrchardCore.ContactCenter";

/// <summary>
/// The identifier of the agent, presence, and queue-membership feature.
/// </summary>
public const string Agents = "CrestApps.OrchardCore.ContactCenter.Agents";

/// <summary>
/// The identifier of the queue and reservation feature.
/// </summary>
public const string Queues = "CrestApps.OrchardCore.ContactCenter.Queues";

/// <summary>
/// The identifier of the outbound dialer feature.
/// </summary>
public const string Dialer = "CrestApps.OrchardCore.ContactCenter.Dialer";

/// <summary>
/// The identifier of the inbound voice integration feature.
/// </summary>
public const string Voice = "CrestApps.OrchardCore.ContactCenter.Voice";

/// <summary>
/// The identifier of the real-time agent and supervisor experience feature.
/// </summary>
public const string RealTime = "CrestApps.OrchardCore.ContactCenter.RealTime";
}

/// <summary>
/// Contains the well-known names of the Contact Center components that originate domain events.
/// </summary>
public static class Components
{
/// <summary>
/// The interaction management component.
/// </summary>
public const string Interactions = "Interactions";

/// <summary>
/// The queue management component.
/// </summary>
public const string Queues = "Queues";

/// <summary>
/// The routing engine component.
/// </summary>
public const string Routing = "Routing";

/// <summary>
/// The agent and presence management component.
/// </summary>
public const string Agents = "Agents";

/// <summary>
/// The voice channel adapter component.
/// </summary>
public const string Voice = "Voice";

/// <summary>
/// The outbound dialer component.
/// </summary>
public const string Dialer = "Dialer";

/// <summary>
/// The wrap-up and disposition component.
/// </summary>
public const string WrapUp = "WrapUp";

/// <summary>
/// The call session management component.
/// </summary>
public const string CallSessions = "CallSessions";

/// <summary>
/// The real-time agent and supervisor experience component.
/// </summary>
public const string RealTime = "RealTime";
}

/// <summary>
/// Contains the canonical Contact Center domain event type names.
/// Names are channel-neutral and stable so they can be persisted, projected, and replayed.
/// </summary>
public static class Events
{
/// <summary>
/// Raised when a new interaction is created.
/// </summary>
public const string InteractionCreated = "InteractionCreated";

/// <summary>
/// Raised when an interaction is linked to a CRM activity.
/// </summary>
public const string InteractionLinkedToActivity = "InteractionLinkedToActivity";

/// <summary>
/// Raised when an activity is reserved by routing, a dialer, or an agent.
/// </summary>
public const string ActivityReserved = "ActivityReserved";

/// <summary>
/// Raised when an activity assignment changes.
/// </summary>
public const string ActivityAssignmentChanged = "ActivityAssignmentChanged";

/// <summary>
/// Raised when an activity disposition is applied.
/// </summary>
public const string ActivityDispositionApplied = "ActivityDispositionApplied";

/// <summary>
/// Raised when the communication session for an interaction starts.
/// </summary>
public const string InteractionStarted = "InteractionStarted";

/// <summary>
/// Raised when an interaction is updated.
/// </summary>
public const string InteractionUpdated = "InteractionUpdated";

/// <summary>
/// Raised when an interaction is transferred.
/// </summary>
public const string InteractionTransferred = "InteractionTransferred";

/// <summary>
/// Raised when the communication session for an interaction ends.
/// </summary>
public const string InteractionEnded = "InteractionEnded";

/// <summary>
/// Raised when an interaction fails.
/// </summary>
public const string InteractionFailed = "InteractionFailed";

/// <summary>
/// Raised when routing evaluates a queued activity and its candidate agents.
/// </summary>
public const string RoutingDecisionMade = "RoutingDecisionMade";

/// <summary>
/// Raised when an activity is added to a queue.
/// </summary>
public const string QueueItemAdded = "QueueItemAdded";

/// <summary>
/// Raised when a queue item is reserved for an agent.
/// </summary>
public const string QueueItemReserved = "QueueItemReserved";

/// <summary>
/// Raised when a queue item is assigned to an agent.
/// </summary>
public const string QueueItemAssigned = "QueueItemAssigned";

/// <summary>
/// Raised when a queue item leaves the queue.
/// </summary>
public const string QueueItemDequeued = "QueueItemDequeued";

/// <summary>
/// Raised when a waiting queue item is moved to an overflow queue.
/// </summary>
public const string QueueItemOverflowed = "QueueItemOverflowed";

/// <summary>
/// Raised when an agent signs in.
/// </summary>
public const string AgentSignedIn = "AgentSignedIn";

/// <summary>
/// Raised when an agent signs out.
/// </summary>
public const string AgentSignedOut = "AgentSignedOut";

/// <summary>
/// Raised when an agent presence state changes.
/// </summary>
public const string AgentPresenceChanged = "AgentPresenceChanged";

/// <summary>
/// Raised when an agent is reserved for an offer.
/// </summary>
public const string AgentReserved = "AgentReserved";

/// <summary>
/// Raised when an agent reservation is released.
/// </summary>
public const string AgentReleased = "AgentReleased";

/// <summary>
/// Raised when a dialer run starts.
/// </summary>
public const string DialerRunStarted = "DialerRunStarted";

/// <summary>
/// Raised when a dialer attempt is scheduled.
/// </summary>
public const string DialerAttemptScheduled = "DialerAttemptScheduled";

/// <summary>
/// Raised when a dialer attempt starts dialing.
/// </summary>
public const string DialerAttemptStarted = "DialerAttemptStarted";

/// <summary>
/// Raised when a dialer attempt completes.
/// </summary>
public const string DialerAttemptCompleted = "DialerAttemptCompleted";

/// <summary>
/// Raised when the outbound compliance gate suppresses a dialing attempt.
/// </summary>
public const string DialSuppressed = "DialSuppressed";

/// <summary>
/// Raised when a callback is scheduled.
/// </summary>
public const string CallbackScheduled = "CallbackScheduled";

/// <summary>
/// Raised when a call session is created for an interaction.
/// </summary>
public const string CallSessionCreated = "CallSessionCreated";

/// <summary>
/// Raised when a call session state changes.
/// </summary>
public const string CallSessionUpdated = "CallSessionUpdated";

/// <summary>
/// Raised when a live call is connected (bridged) to an agent.
/// </summary>
public const string CallConnected = "CallConnected";

/// <summary>
/// Raised when a call session ends.
/// </summary>
public const string CallEnded = "CallEnded";

/// <summary>
/// Raised when an agent accepts an offered interaction.
/// </summary>
public const string OfferAccepted = "OfferAccepted";

/// <summary>
/// Raised when an agent declines an offered interaction.
/// </summary>
public const string OfferDeclined = "OfferDeclined";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<RootNamespace>CrestApps.OrchardCore.ContactCenter</RootNamespace>
<Title>CrestApps OrchardCore Contact Center Abstractions</Title>
<Description>
$(CrestAppsDescription)

Provider-agnostic abstractions and shared domain vocabulary for the CrestApps OrchardCore
Contact Center module set. Contains the interaction, channel, routing, and event constants
and enumerations shared across Contact Center features and channel adapters.
</Description>
<PackageTags>$(PackageTags) ContactCenter Interactions Abstractions</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="OrchardCore.Infrastructure.Abstractions" />
</ItemGroup>

</Project>
Loading
Loading