AI Chat is a desktop chat prototype built with Angular and Electron. It mimics a messaging app UI and lets each chat be powered by an Agent class. An agent can be a simple scripted flow, a local AI-backed assistant, or any custom conversation engine you want to plug in.
The project is useful as a foundation for:
- guided support or intake flows
- chatbot experiments
- local AI chat interfaces
- form-like conversations where answers need validation
The app opens as an Electron desktop window and renders the UI with Angular. Chats are stored locally in SQLite, so messages and chat metadata survive app restarts.
Each chat has:
- a
Chatmodel with messages and UI metadata - a
Clientside that sends user messages - a
Supporterside that sends agent messages - an
Agentinstance that decides how the conversation should progress
Out of the box, the codebase already includes two agent types:
MockAgentin src/agents/MockAgent.ts: a scripted flow that asks for greeting, name, and age with validationAiAgentin src/agents/AiAgent.ts: sends the latest user message to a local OpenAI-compatible endpoint and replies with the model output
At the moment, the home screen loads chats with AiAgent by default in src/app/home/home-component.ts.
High-level flow:
- Electron starts from
main.js, the compiled output generated from main.ts. - The Angular app is loaded from
dist/ai-chat/browser/index.html. - SQLite tables for chats and messages are initialized in the Electron process.
- Angular loads chats through
ChatService. - Each chat is hydrated with an
Agentand aSupporter. - When the user sends a message,
Clientappends it and triggerssupporter.respond(). - The agent validates the last answer if needed, then decides the next question or reply.
Important files:
- main.ts: Electron app bootstrapping and service/handler registration
- ipc/: Electron IPC handlers compiled by
tsconfig.electron.json - preload.js: exposes safe IPC bridge to the renderer
- src/services/db.service.ts: Angular wrapper around Electron IPC database calls
- src/services/chat.service.ts: chat creation, hydration, persistence
- src/classes/Agent.ts: base class for all agents
- src/classes/Question.ts: question model with validators and suggested answers
- src/classes/Supporter.ts: helper used by agents to send messages
- Node.js
- npm
- Windows desktop environment for Electron
npm installnpm startThis script:
- builds the Angular app
- launches the Electron window
Useful commands:
npm run build
npm run watch
npm testNotes:
npm startis the main way to run the desktop appnpm run watchrebuilds Angular in watch mode, but does not restart Electron by itself- chat and message data are stored in a SQLite database under Electron's user data directory
- Electron TypeScript is compiled with
tsconfig.electron.json;package.jsonpoints to the emittedmain.js. TypeScript imports use.jsextensions becausemoduleandmoduleResolutionare set toNodeNext.
AiAgent is wired to a local OpenAI-compatible chat endpoint in src/services/ai.service.ts:
http://localhost:1234/v1/chat/completionsThis is currently set up for tools like LM Studio or another local server exposing the same API shape.
Before using AiAgent, make sure:
- a local model server is running on port
1234 - the selected model name in
AiServiceexists in that server
An agent is the conversation brain behind a chat. It decides:
- what message to send first
- how to react to the latest user message
- whether the user answer is valid
- what question or response comes next
All agents extend the base Agent class:
init(chat, supporter)runs when the agent is attached to a chat- use
initto send the first message for an empty chat respond()runs after the user sends a messagelastQuestionis used to validate answers to the previous prompt
Use the Supporter helper inside the agent:
supporter.ask(question)to send aQuestionsupporter.answer(answer)to send anAnswersupporter.sendMessage(message)to send a regular message
Create a file under src/agents, for example:
src/agents/WelcomeAgent.ts
Example:
export class WelcomeAgent extends Agent {
override init(chat: Chat, supporter: Supporter) {
super.init(chat, supporter);
if (chat.messages.length === 0) {
this.lastQuestion = new Question('What is your email?', {
validator: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
validationErrorMessage: 'Please enter a valid email address.',
});
this.lastQuestion.tag = 'email';
supporter.ask(this.lastQuestion);
}
}
override async respond(): Promise<void> {
super.respond();
if (this.lastQuestion?.tag === 'email') {
this.supporter.sendMessage('Thanks, we saved your email.');
}
}
}After creating the class, declare it in src/app/app-agents.module.ts.
@AgentsModule({
agents: {
AiAgent,
FlowAgent,
MockAgent,
WelcomeAgent
},
})
export class AppAgentsModule {}- always call
super.init(...)andsuper.respond() - use
Questionwhen you want built-in validation - store conversation state in
lastQuestion.tagor by inspecting previous messages - use
chat.messagesif you need full conversation history - keep agent logic focused on conversation flow, not persistence
Agents must be declared in src/app/app-agents.module.ts. This module is registered at app startup and is what AgentsService uses to know which agent classes exist.
Current example:
@AgentsModule({
agents: {
AiAgent,
FlowAgent,
MockAgent,
},
})
export class AppAgentsModule {}If you add WelcomeAgent, register it there too:
import { WelcomeAgent } from '../agents/WelcomeAgent';
@AgentsModule({
agents: {
AiAgent,
FlowAgent,
MockAgent,
WelcomeAgent,
},
})
export class AppAgentsModule {}After the agent is declared, it can be created and used when chats are loaded or created. The simplest place to start is src/app/home/home-component.ts.
Current examples:
- loading existing chats:
this.chatService.getChats() - creating a new chat:
initialAgent: Agent = new MockAgent(this.injector)
To use your custom agent, replace new MockAgent() with your own class instance. replacing agents via the home component is only needed for the inital agent, for the following agents use supporter.setAgent(new SomeAgent()) in the previous agent
If your agent needs Angular services, follow the AiAgent pattern and pass Injector into the constructor.
The Question class supports:
- regex validation
- structured validator specs
- validation error messages
- predefined possible answers
That makes it a good fit for:
- onboarding flows
- support troubleshooting trees
- lead capture
- step-by-step forms in chat format
MockAgent is the best reference in this repo for a guided flow with validation.
src/
agents/ Agent implementations
app/ Angular UI components
classes/ Core chat, message, question, client, supporter models
services/ AI, database, Electron, profile, and chat services
main.ts Electron main process and SQLite logic
preload.js Electron preload bridge
This project is a local desktop chat app where each conversation is powered by an agent. Angular handles the UI, Electron provides the desktop shell, SQLite stores the data, and agents define the conversation behavior. If you want to extend the app, the main entry point is usually creating a new Agent class and wiring it into chat creation.