@mostajs/chatbot 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  **Auteur** : Dr Hamid MADANI <drmdh@msn.com>
4
4
 
5
+ ## 0.3.0 — 2026-06-11
6
+ - **Agent assignable (générique)** : l'assistant devient un **acteur de 1ʳᵉ classe**. Nouveau `src/agent.ts` —
7
+ `AgentIdentity {id,name,kind:"assistant"}` (config `createChatbot({agent:{id?,name?}})`, défaut `assistant`/`Assistant`)
8
+ + **`AssignableAgentPort`** (contrat d'**injection** : l'app hôte fournit la liaison de SON domaine — CRM→Employee/commande,
9
+ race-event→un autre acteur). `createChatbot` accepte `agent?`/`assignable?` et expose `agent()`, `isAgent(id)`,
10
+ `ensureAssignable()`, `assignableLabel(id)`. Le module reste **agnostique** (aucune notion d'« Employee »). Compat **100 %**
11
+ (options additives). Exports : `DEFAULT_AGENT`, `resolveAgent`, types `AgentIdentity`/`AgentConfig`/`AssignableAgentPort`.
12
+
5
13
  ## 0.2.0 — 2026-06-10
6
14
  - **Extraction en modules (façon @mostajs/orm)** : la couche LLM et la boucle/routeur quittent le chatbot.
7
15
  - **`@mostajs/llm`** : dialectes (1 par IA) + registre + types function-calling (déplacés depuis `src/dialects/`).
package/README.md CHANGED
@@ -4,17 +4,18 @@
4
4
 
5
5
  Assistant / copilote / agent conversationnel **générique et réutilisable** pour les apps `@mostajs/*`.
6
6
  **Activable/désactivable** et **entièrement piloté par `.env`**. Backends pluggables (`scripted` sans IA,
7
- `ai` avec LLM), **providers LLM pluggables avec fallback** (`anthropic`/`claude-opus-4-8`, `deepseek`),
8
- ancrage **tool-use** sur les données de l'app, **recherche web** optionnelle. Registres façon dialectes ORM.
7
+ `ai` avec LLM, `router` hybride), **providers LLM pluggables avec fallback** (`anthropic`/`claude-opus-4-8`,
8
+ `deepseek`), ancrage **tool-use** sur les données de l'app, **recherche web** optionnelle, et l'assistant
9
+ peut devenir un **membre du personnel assignable** dans votre domaine. Registres façon dialectes ORM.
9
10
 
10
- > Statut : **0.0.1 (scaffold)**. Livrables DEVRULES #1-#3 dans `docs/`. Voir `llms.txt` pour l'API complète.
11
+ > Statut : **0.3.0**. Voir `llms.txt` pour l'API dense et `CHANGELOG.md` pour l'historique.
11
12
 
12
13
  ## Configuration (`.env` — tout par config)
13
14
  ```
14
15
  CHATBOT_ENABLED=1 # interrupteur maître (défaut 0)
15
16
  CHATBOT_ROLES=admin,employe # vide = tous
16
17
  CHATBOT_MODE=assistant # assistant | agent
17
- CHATBOT_BACKEND=ai # scripted (défaut) | ai
18
+ CHATBOT_BACKEND=router # scripted (défaut) | ai | router (hybride : intents sans IA, IA en repli)
18
19
  CHATBOT_PROVIDERS=deepseek,anthropic # liste priorité + fallback
19
20
  CHATBOT_MODEL= # vide ⇒ défaut du provider
20
21
  ANTHROPIC_API_KEY=... # provider anthropic
@@ -24,27 +25,127 @@ CHATBOT_WEB_SEARCH=0 # recherche web (anthropic)
24
25
  CHATBOT_EFFORT=high
25
26
  ```
26
27
 
27
- ## Usage (Next.js)
28
+ ## 1. Intégrer le chatbot dans n'importe quelle application
29
+
30
+ L'app fournit son **profil métier** (`domain`), ses **outils** (`tools`) et, en option, ses **intents**
31
+ déterministes. Le module route vers le backend configuré (`.env`).
32
+
28
33
  ```ts
29
- // serveur
30
- import { createChatbot, registerTool } from "@mostajs/chatbot";
31
- registerTool({ name:"orders_by_state", description:"Compter les commandes par état",
32
- schema:{ type:"object", properties:{ state:{type:"string"} }, required:["state"] },
33
- permission:"order.read", run: async ({state}, actor) => /* requête dépôt */ 0 });
34
- const bot = createChatbot({ domain:{ appName:"CRM TRADING",
35
- description:"Société de négoce : commandes, fournisseurs, proformats.", locale:"fr" } });
36
- if (bot.enabled()) { const answer = await bot.ask([{role:"user",content:"aide"}], actor); }
34
+ // serveur (un seul endroit) — lib/chatbot.ts
35
+ import { createChatbot } from "@mostajs/chatbot";
36
+
37
+ const bot = createChatbot({
38
+ domain: {
39
+ appName: "Mon App",
40
+ description: "Décrit le métier injecté dans le prompt système.",
41
+ locale: "fr",
42
+ rules: ["Toujours demander confirmation avant une action.", "Ne jamais exposer de secret."],
43
+ },
44
+ // ...tools / intents : voir §3
45
+ });
46
+
47
+ // Server action consommée par le widget
48
+ export async function askAction(text: string): Promise<string> {
49
+ const actor = await getActor(); // { id, role, permissions }
50
+ if (!bot.enabled()) return "Assistant désactivé.";
51
+ return bot.ask([{ role: "user", content: text }], actor);
52
+ }
37
53
  ```
38
54
  ```tsx
39
- // client (widget)
55
+ // client (widget bulle + panneau)
40
56
  import { Chatbot } from "@mostajs/chatbot/client";
41
- <Chatbot send={askServerAction} title="Assistant TRADING" />
57
+ <Chatbot send={askAction} title="Assistant" greeting="Bonjour 👋" />
58
+ ```
59
+
60
+ > Le widget est `"use client"`. Importez `createChatbot` (serveur) à part et passez-lui un `send()` (server action).
61
+
62
+ ## 2. Faire de l'assistant un **membre du personnel** (agent assignable)
63
+
64
+ L'assistant est un **acteur de première classe** : il a une **identité** (`agent`) et, via le contrat
65
+ d'injection **`AssignableAgentPort`**, l'app le **branche comme acteur assignable de SON domaine** — un
66
+ employé qu'on assigne à une tâche, un opérateur, un agent de traitement… Le module reste **agnostique** :
67
+ il ne connaît ni « employé », ni « commande » ; **vous** fournissez la liaison.
68
+
69
+ ```ts
70
+ import { createChatbot, type AssignableAgentPort } from "@mostajs/chatbot";
71
+
72
+ // L'adaptateur de VOTRE domaine : ici, l'agent est matérialisé comme un "employé" assignable.
73
+ // (Une autre app le brancherait sur une autre entité — ou pas du tout.)
74
+ const staffAgent: AssignableAgentPort = {
75
+ // seed idempotent → renvoie l'id de l'agent côté hôte (apparaît alors dans vos listes d'assignation)
76
+ ensure: async () => upsertEmployee({ email: "assistant@app.bot", name: "🤖 Assistant" }),
77
+ // cet id d'acteur est-il l'agent ?
78
+ isAgent: async (id) => (await getEmployee(id))?.email === "assistant@app.bot",
79
+ label: () => "🤖 Assistant",
80
+ };
81
+
82
+ const bot = createChatbot({
83
+ domain: { appName: "Mon App", description: "…", locale: "fr" },
84
+ agent: { id: "assistant", name: "🤖 Assistant" }, // identité (défaut: { id:"assistant", name:"Assistant" })
85
+ assignable: staffAgent, // liaison au domaine
86
+ // tools/intents…
87
+ });
88
+
89
+ await bot.ensureAssignable(); // garantit que l'agent existe comme acteur assignable (au boot)
90
+ bot.agent(); // { id:"assistant", name:"🤖 Assistant", kind:"assistant" }
91
+ await bot.isAgent(someActorId); // true si cet acteur EST l'assistant
92
+ bot.assignableLabel(someActorId); // libellé d'affichage côté hôte
42
93
  ```
43
94
 
95
+ Schéma : **le module possède le *concept*** (identité + `AssignableAgentPort`), **votre app possède la
96
+ *liaison*** (l'adaptateur ci-dessus). Vous pouvez ainsi assigner un travail à l'assistant comme à un humain,
97
+ et — combiné aux outils (§3) — lui faire **traiter** ce travail.
98
+
99
+ ## 3. Lui faire **exécuter des actions** (outils gardés)
100
+
101
+ Un **outil** = une fonction de votre domaine que l'assistant peut appeler. Lecture seule par défaut ;
102
+ les actions sensibles sont **gardées par permission** (RBAC) et, idéalement, par une **confirmation**
103
+ (human-in-the-loop). Le backend `ai` exécute la boucle tool-use ; le backend `router` peut exécuter un
104
+ outil **sans IA** via un intent (slash-commande / regex) — coût zéro, fiable.
105
+
106
+ ```ts
107
+ import { createChatbot, registerTool } from "@mostajs/chatbot";
108
+
109
+ // Lecture
110
+ registerTool({
111
+ name: "order_status", description: "État d'une commande par numéro",
112
+ schema: { type: "object", properties: { order: { type: "string" } }, required: ["order"] },
113
+ permission: "order.read",
114
+ run: async ({ order }, actor) => findOrder(order), // retourné à l'assistant
115
+ });
116
+
117
+ // Action (écriture) — gardée par permission + confirmation explicite
118
+ registerTool({
119
+ name: "transition_order", description: "Faire avancer une commande à l'état suivant",
120
+ schema: { type: "object", properties: { order: {type:"string"}, to: {type:"string"}, confirm: {type:"boolean"} },
121
+ required: ["order", "to"] },
122
+ permission: "order.transition",
123
+ run: async ({ order, to, confirm }, actor) => {
124
+ if (!confirm) return { preview: `Confirmer : passer ${order} → ${to} ?` }; // rien modifié
125
+ return doTransition(order, to, actor); // exécute + audite
126
+ },
127
+ });
128
+
129
+ const bot = createChatbot({
130
+ domain: { appName: "Mon App", description: "…" },
131
+ tools: [/* … vos outils, ou via le registre ci-dessus */],
132
+ // Intent déterministe (router) : "/transition <order> <to> confirmer" → outil, SANS IA
133
+ intents: [{ name:"transition", command:"transition", tool:"transition_order",
134
+ args: ({ tokens }) => ({ order: tokens[0], to: tokens[1], confirm: tokens.includes("confirmer") }) }],
135
+ });
136
+ ```
137
+
138
+ > **Gouvernance (OWASP LLM)** : outils en lecture par défaut, gate RBAC sur les écritures, confirmation
139
+ > avant tout effet de bord, secrets uniquement via `.env`. Ne jamais exécuter une instruction venant des données.
140
+
44
141
  ## Ajouter un provider LLM (comme un dialecte)
45
142
  ```ts
46
143
  import { registerLlmProvider } from "@mostajs/chatbot";
47
144
  registerLlmProvider({ key:"openai", async *stream(messages, tools, opts){ /* SSE */ } });
48
145
  ```
49
146
 
147
+ ## Architecture (couches façon ORM)
148
+ `@mostajs/llm` (dialectes IA) ← `@mostajs/btool-use` (boucle tool-use) / `@mostajs/intent-router`
149
+ (routeur déterministe) ← **`@mostajs/chatbot`** (backends + `createChatbot` + widget + agent).
150
+
50
151
  Peers : `@mostajs/config`, `@anthropic-ai/sdk` (optionnel), `react` (optionnel). DeepSeek : sans dépendance.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @mostajs/chatbot — l'assistant comme **acteur de première classe**, et le contrat permettant à une
3
+ * app hôte de le brancher comme **agent assignable** dans SON domaine. Générique et agnostique :
4
+ * le chatbot ne sait rien de « Employee », « commande », « cycliste »… ; l'hôte fournit la liaison.
5
+ *
6
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
7
+ */
8
+ /** Identité de l'assistant en tant qu'acteur (id stable + nom affichable). */
9
+ export interface AgentIdentity {
10
+ /** Id stable de l'agent côté chatbot (défaut « assistant »). */
11
+ id: string;
12
+ /** Nom affichable (défaut « Assistant »). */
13
+ name: string;
14
+ kind: "assistant";
15
+ }
16
+ /** Configuration optionnelle de l'identité de l'agent passée à `createChatbot`. */
17
+ export type AgentConfig = {
18
+ id?: string;
19
+ name?: string;
20
+ };
21
+ /**
22
+ * Contrat d'**injection** : comment l'app hôte matérialise l'agent comme **acteur assignable** dans
23
+ * son propre domaine (CRM → Employee assigné aux commandes ; race-event → un autre acteur ; etc.).
24
+ * Le module possède le *concept* ; l'hôte possède la *liaison*. Toutes les méthodes sont optionnelles
25
+ * à fournir côté hôte sauf `ensure`/`isAgent`.
26
+ */
27
+ export interface AssignableAgentPort {
28
+ /** Seed idempotent de l'agent comme acteur assignable de l'hôte ; renvoie son id côté hôte. */
29
+ ensure(): Promise<string>;
30
+ /** Cet id d'acteur (côté hôte) est-il l'agent-assistant ? */
31
+ isAgent(id: string | undefined): boolean | Promise<boolean>;
32
+ /** Libellé d'affichage optionnel de l'agent côté hôte (ex. « 🤖 Assistant TRADING »). */
33
+ label?(id: string): string;
34
+ }
35
+ /** Identité par défaut si l'app n'en fournit aucune. */
36
+ export declare const DEFAULT_AGENT: AgentIdentity;
37
+ /** Résout l'identité de l'agent depuis la config (valeurs par défaut sûres). */
38
+ export declare function resolveAgent(cfg?: AgentConfig): AgentIdentity;
39
+ //# sourceMappingURL=agent.d.ts.map
package/dist/agent.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @mostajs/chatbot — l'assistant comme **acteur de première classe**, et le contrat permettant à une
3
+ * app hôte de le brancher comme **agent assignable** dans SON domaine. Générique et agnostique :
4
+ * le chatbot ne sait rien de « Employee », « commande », « cycliste »… ; l'hôte fournit la liaison.
5
+ *
6
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
7
+ */
8
+ /** Identité par défaut si l'app n'en fournit aucune. */
9
+ export const DEFAULT_AGENT = { id: "assistant", name: "Assistant", kind: "assistant" };
10
+ /** Résout l'identité de l'agent depuis la config (valeurs par défaut sûres). */
11
+ export function resolveAgent(cfg) {
12
+ return { id: cfg?.id ?? DEFAULT_AGENT.id, name: cfg?.name ?? DEFAULT_AGENT.name, kind: "assistant" };
13
+ }
14
+ //# sourceMappingURL=agent.js.map
package/dist/client.d.ts CHANGED
@@ -1,6 +1,14 @@
1
- export declare function Chatbot({ send, title, greeting }: {
1
+ interface Msg {
2
+ role: "user" | "assistant";
3
+ content: string;
4
+ }
5
+ export declare function Chatbot({ send, title, greeting, loadHistory, onExport, onReset }: {
2
6
  send: (text: string) => Promise<string>;
3
7
  title?: string;
4
8
  greeting?: string;
9
+ loadHistory?: () => Promise<Msg[]>;
10
+ onExport?: (format: "html" | "pdf") => Promise<string | void>;
11
+ onReset?: () => Promise<void>;
5
12
  }): import("react").JSX.Element;
13
+ export {};
6
14
  //# sourceMappingURL=client.d.ts.map
package/dist/client.js CHANGED
@@ -1,17 +1,31 @@
1
1
  /**
2
- * @mostajs/chatbot/client — widget React minimal (bulle + panneau). L'app fournit `send(text)`
3
- * (qui appelle son endpoint serveur → createChatbot). Styles : classes `cbot-*` côté hôte.
4
- * (0.0.1 : non-streaming. Streaming SSE en 0.3.)
2
+ * @mostajs/chatbot/client — widget React (bulle + panneau). L'app fournit `send(text)` (→ endpoint
3
+ * serveur → createChatbot). Optionnel : `loadHistory` (reprend le fil à la 1ʳᵉ ouverture), `onExport`
4
+ * (exporte la conversation, ouvre l'URL renvoyée), `onReset` (nouvelle conversation). Styles `cbot-*` côté hôte.
5
5
  * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
6
6
  */
7
7
  "use client";
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
9
  import { useState } from "react";
10
- export function Chatbot({ send, title = "Assistant", greeting = "Bonjour 👋" }) {
10
+ export function Chatbot({ send, title = "Assistant", greeting = "Bonjour 👋", loadHistory, onExport, onReset }) {
11
11
  const [open, setOpen] = useState(false);
12
12
  const [msgs, setMsgs] = useState([{ role: "assistant", content: greeting }]);
13
13
  const [val, setVal] = useState("");
14
14
  const [busy, setBusy] = useState(false);
15
+ const [loaded, setLoaded] = useState(false);
16
+ async function toggle() {
17
+ const willOpen = !open;
18
+ setOpen(willOpen);
19
+ if (willOpen && !loaded && loadHistory) {
20
+ setLoaded(true);
21
+ try {
22
+ const h = await loadHistory();
23
+ if (h && h.length)
24
+ setMsgs([{ role: "assistant", content: greeting }, ...h]);
25
+ }
26
+ catch { /* ignore */ }
27
+ }
28
+ }
15
29
  const submit = async (e) => {
16
30
  e.preventDefault();
17
31
  const t = val.trim();
@@ -29,6 +43,27 @@ export function Chatbot({ send, title = "Assistant", greeting = "Bonjour 👋" }
29
43
  }
30
44
  setBusy(false);
31
45
  };
32
- return (_jsxs("div", { className: "cbot", children: [open ? (_jsxs("div", { className: "cbot-panel", role: "dialog", children: [_jsxs("div", { className: "cbot-head", children: [_jsx("span", { children: title }), _jsx("button", { className: "cbot-x", onClick: () => setOpen(false), "aria-label": "Fermer", children: "\u00D7" })] }), _jsxs("div", { className: "cbot-msgs", children: [msgs.map((m, i) => _jsx("div", { className: `cbot-msg ${m.role}`, children: m.content }, i)), busy ? _jsx("div", { className: "cbot-msg assistant cbot-typing", children: "\u2026" }) : null] }), _jsxs("form", { className: "cbot-form", onSubmit: submit, children: [_jsx("input", { value: val, onChange: (e) => setVal(e.target.value), placeholder: "Votre question\u2026" }), _jsx("button", { className: "cbot-send", type: "submit", disabled: busy, children: "\u27A4" })] })] })) : null, _jsx("button", { className: "cbot-fab", onClick: () => setOpen((v) => !v), "aria-label": title, children: "\uD83E\uDD16" })] }));
46
+ async function doExport(fmt) {
47
+ if (!onExport || busy)
48
+ return;
49
+ setBusy(true);
50
+ try {
51
+ const url = await onExport(fmt);
52
+ if (typeof url === "string" && url)
53
+ window.open(url, "_blank");
54
+ }
55
+ catch { /* ignore */ }
56
+ setBusy(false);
57
+ }
58
+ async function doReset() {
59
+ if (!onReset)
60
+ return;
61
+ try {
62
+ await onReset();
63
+ }
64
+ catch { /* ignore */ }
65
+ setMsgs([{ role: "assistant", content: greeting }]);
66
+ }
67
+ return (_jsxs("div", { className: "cbot", children: [open ? (_jsxs("div", { className: "cbot-panel", role: "dialog", children: [_jsxs("div", { className: "cbot-head", children: [_jsx("span", { children: title }), _jsxs("span", { style: { display: "flex", gap: 4, alignItems: "center" }, children: [onReset ? _jsx("button", { className: "cbot-x", title: "Nouvelle conversation", onClick: doReset, children: "\uD83D\uDD04" }) : null, onExport ? _jsx("button", { className: "cbot-x", title: "Exporter (HTML)", onClick: () => doExport("html"), children: "\u2913" }) : null, onExport ? _jsx("button", { className: "cbot-x", title: "Exporter (PDF)", style: { fontSize: ".7rem" }, onClick: () => doExport("pdf"), children: "PDF" }) : null, _jsx("button", { className: "cbot-x", onClick: () => setOpen(false), "aria-label": "Fermer", children: "\u00D7" })] })] }), _jsxs("div", { className: "cbot-msgs", children: [msgs.map((m, i) => _jsx("div", { className: `cbot-msg ${m.role}`, children: m.content }, i)), busy ? _jsx("div", { className: "cbot-msg assistant cbot-typing", children: "\u2026" }) : null] }), _jsxs("form", { className: "cbot-form", onSubmit: submit, children: [_jsx("input", { value: val, onChange: (e) => setVal(e.target.value), placeholder: "Votre question\u2026" }), _jsx("button", { className: "cbot-send", type: "submit", disabled: busy, children: "\u27A4" })] })] })) : null, _jsx("button", { className: "cbot-fab", onClick: toggle, "aria-label": title, children: "\uD83E\uDD16" })] }));
33
68
  }
34
69
  //# sourceMappingURL=client.js.map
package/dist/index.d.ts CHANGED
@@ -16,19 +16,34 @@ import "./backends/scripted.js";
16
16
  import "./backends/ai.js";
17
17
  import "./backends/router.js";
18
18
  import type { ChatChunk, ChatMessage, ChatActor, DomainProfile, ChatTool, ChatIntent } from "./types.js";
19
+ import { type AgentConfig, type AgentIdentity, type AssignableAgentPort } from "./agent.js";
19
20
  export interface ChatbotOptions {
20
21
  domain: DomainProfile;
21
22
  tools?: ChatTool[];
22
23
  intents?: ChatIntent[];
24
+ /** Identité de l'assistant comme acteur (id/nom) — défaut `{ id:"assistant", name:"Assistant" }`. */
25
+ agent?: AgentConfig;
26
+ /** Liaison (DI) rendant l'agent **assignable** dans le domaine de l'app hôte (cf. `AssignableAgentPort`). */
27
+ assignable?: AssignableAgentPort;
23
28
  }
24
29
  export declare function createChatbot(options: ChatbotOptions): {
25
30
  enabled: () => boolean;
26
31
  stream: (messages: ChatMessage[], actor: ChatActor) => AsyncIterable<ChatChunk>;
27
32
  ask: (messages: ChatMessage[], actor: ChatActor) => Promise<string>;
33
+ /** Identité de l'assistant (acteur de 1ʳᵉ classe). */
34
+ agent: () => AgentIdentity;
35
+ /** Cet id d'acteur (côté hôte) est-il l'agent ? Délègue au port d'assignation s'il est fourni. */
36
+ isAgent: (id?: string) => Promise<boolean>;
37
+ /** Garantit (seed idempotent) l'agent comme acteur assignable de l'hôte → renvoie son id côté hôte. */
38
+ ensureAssignable: () => Promise<string>;
39
+ /** Libellé d'affichage de l'agent côté hôte (si le port en fournit un). */
40
+ assignableLabel: (id: string) => string | undefined;
28
41
  };
29
42
  export type { ChatbotOptions as Options };
30
43
  export type { AskContext, ChatChunk, ChatMessage, ChatActor, DomainProfile, ChatTool, ChatIntent, IChatBackend, ILlmProvider, ILlmDialect, ToolCall, LlmMessage, LlmTurn, ChatbotMode, LlmStreamOpts, } from "./types.js";
31
44
  export { registerBackend, getBackend, listBackends, registerTool, getTool, listTools } from "./registry.js";
45
+ export { DEFAULT_AGENT, resolveAgent } from "./agent.js";
46
+ export type { AgentIdentity, AgentConfig, AssignableAgentPort } from "./agent.js";
32
47
  export { registerLlmProvider, getLlmProvider, listLlmProviders, registerDialect, getDialect, listDialects, getSupportedDialects, AbstractLlmDialect, OpenAiDialect, DeepSeekDialect, MistralDialect, GroqDialect, OllamaDialect, AnthropicDialect, } from "@mostajs/llm";
33
48
  export { runToolLoop } from "@mostajs/btool-use";
34
49
  export { matchIntent, runIntent } from "@mostajs/intent-router";
package/dist/index.js CHANGED
@@ -17,7 +17,10 @@ import "./backends/ai.js";
17
17
  import "./backends/router.js";
18
18
  import { getBackend } from "./registry.js";
19
19
  import { chatbotEnabled, chatbotBackend, chatbotAllowsRole, chatbotMode } from "./config.js";
20
+ import { resolveAgent } from "./agent.js";
20
21
  export function createChatbot(options) {
22
+ const agent = resolveAgent(options.agent);
23
+ const assignable = options.assignable;
21
24
  async function* stream(messages, actor) {
22
25
  if (!chatbotAllowsRole(actor.role)) {
23
26
  yield { type: "error", text: "Assistant désactivé pour votre profil." };
@@ -34,9 +37,23 @@ export function createChatbot(options) {
34
37
  out += c.text;
35
38
  return out;
36
39
  }
37
- return { enabled: () => chatbotEnabled(), stream, ask };
40
+ return {
41
+ enabled: () => chatbotEnabled(),
42
+ stream,
43
+ ask,
44
+ /** Identité de l'assistant (acteur de 1ʳᵉ classe). */
45
+ agent: () => agent,
46
+ /** Cet id d'acteur (côté hôte) est-il l'agent ? Délègue au port d'assignation s'il est fourni. */
47
+ isAgent: async (id) => (assignable ? !!(await assignable.isAgent(id)) : id === agent.id),
48
+ /** Garantit (seed idempotent) l'agent comme acteur assignable de l'hôte → renvoie son id côté hôte. */
49
+ ensureAssignable: async () => (assignable ? assignable.ensure() : agent.id),
50
+ /** Libellé d'affichage de l'agent côté hôte (si le port en fournit un). */
51
+ assignableLabel: (id) => assignable?.label?.(id),
52
+ };
38
53
  }
39
54
  export { registerBackend, getBackend, listBackends, registerTool, getTool, listTools } from "./registry.js";
55
+ // Agent : l'assistant comme acteur assignable (générique ; l'app hôte fournit la liaison de domaine).
56
+ export { DEFAULT_AGENT, resolveAgent } from "./agent.js";
40
57
  // Réexports de la couche LLM (compat : ces symboles vivaient dans @mostajs/chatbot avant l'extraction).
41
58
  export { registerLlmProvider, getLlmProvider, listLlmProviders, registerDialect, getDialect, listDialects, getSupportedDialects, AbstractLlmDialect, OpenAiDialect, DeepSeekDialect, MistralDialect, GroqDialect, OllamaDialect, AnthropicDialect, } from "@mostajs/llm";
42
59
  export { runToolLoop } from "@mostajs/btool-use";
package/llms.txt CHANGED
@@ -10,9 +10,11 @@ défaut, RBAC) ; le module route vers le **backend** configuré et, en mode `ai`
10
10
  (avec fallback). Aucune logique d'une app particulière dans le module (réutilisable partout : CRM, santé, …).
11
11
 
12
12
  ## EXPORTS
13
- - `.` (serveur-safe) : `createChatbot({domain,tools})` → `{enabled(), stream(messages,actor), ask(messages,actor)}` ;
13
+ - `.` (serveur-safe) : `createChatbot({domain,tools,intents?,agent?,assignable?})` → `{enabled(), stream, ask,
14
+ agent(), isAgent(id), ensureAssignable(), assignableLabel(id)}` ;
14
15
  `registerBackend/getBackend/listBackends`, `registerLlmProvider/getLlmProvider/listLlmProviders`,
15
- `registerTool/getTool/listTools` ; toute la config (`chatbot*`) ; types.
16
+ `registerTool/getTool/listTools` ; **agent** : `DEFAULT_AGENT`/`resolveAgent` + types `AgentIdentity`/`AgentConfig`/`AssignableAgentPort` ;
17
+ toute la config (`chatbot*`) ; types.
16
18
  - `./server` : built-ins concrets (`scriptedBackend`, `aiBackend`, `anthropicProvider`, `deepseekProvider`).
17
19
  - `./client` (React) : `<Chatbot send=… title? greeting? />` (widget bulle+panneau ; SSE en 0.3).
18
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/chatbot",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Assistant / copilote / agent conversationnel générique et réutilisable pour apps @mostajs/* — backends pluggables (scripted | ai), providers LLM pluggables (anthropic/claude-opus-4-8 par défaut), ancrage tool-use, recherche web optionnelle. Activable/désactivable par .env.",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "AGPL-3.0-or-later",