1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
# Référence : agents, channels et événements
## États d'un agent
| État | Affiché (`en` · `fr`) | Signification |
| --- | --- | --- |
| `stopped` | stopped · arrêté | Aucun processus. L'état initial de chaque agent. |
| `starting` | starting… · démarre… | Le processus est lancé et la poignée de main ACP (`initialize`) est en cours. |
| `ready` | ready · prêt | En marche et inoccupé. |
| `busy` | busy · occupé | En marche et utilisé par au moins une conversation. |
| `error` | error · erreur | Le démarrage a échoué, ou le processus s'est arrêté seul (crash). Le statut porte la raison, suivie des dernières lignes stderr de l'agent en cas d'échec au démarrage. |
`outdated` (affiché « restart to apply changes », ou « à redémarrer » en français) marque un agent en marche dont la définition a changé dans la configuration ; un redémarrage l'applique.
## Transitions
| Depuis | Événement | Vers |
| --- | --- | --- |
| `stopped`, `error` | démarrage demandé (▶ dans le panneau Agents, ou première utilisation) | `starting` |
| `starting` | la poignée de main réussit | `ready` |
| `starting` | la poignée de main échoue, protocole autre qu'ACP v1, délai dépassé (30 s) | `error` |
| `ready` | utilisé | `busy` |
| `busy` | le dernier utilisateur a fini | `ready` |
| `ready`, `busy` | arrêt demandé (■), fermeture de l'application, agent retiré de la configuration | `stopped` |
| `ready` | inutilisé depuis `limits.stopIdleAgentsAfter` (vérifié toutes les 30 s) | `stopped` |
| `ready` | un autre agent doit démarrer et celui-ci est l'agent inoccupé utilisé le moins récemment | `stopped` |
| `ready`, `busy` | le processus s'arrête seul | `error` (jamais redémarré automatiquement) |
| `starting` | arrêt demandé | `stopped` dès la fin du démarrage |
Quand `limits.maxRunningAgents` agents tournent et qu'ils sont tous `busy`, un démarrage est refusé avec le message `too many agents running: N agent(s) busy (…), the limit is N`.
Arrêter un agent ferme son entrée standard, attend 5 secondes, puis tue son groupe de processus (l'agent et ce qu'il a lancé, comme `mm` sous `scripts/agents.sh`).
## Événements du Go vers l'interface
| Événement | Charge | Envoyé quand |
| --- | --- | --- |
| `config:changed` | `{path, valid, problems[]}` | Le fichier de configuration a été enregistré et rechargé (valide ou non). |
| `agent:state` | `{agent, state, error?}` | Un agent change d'état ou sa définition change. |
| `agent:log` | `{agent, line}` | Un agent écrit une ligne sur sa sortie d'erreur. |
Les noms et types Go sont dans `internal/events` ; les constantes et types TypeScript sont générés dans `frontend/bindings/events` et `frontend/bindings/…/internal/events`.
## Services appelés par l'interface
| Service | Méthode | Effet |
| --- | --- | --- |
| `AgentService` | `List()` | Tous les agents avec `name, title, icon, description, state, error, outdated`, dans l'ordre de la configuration. |
| | `Start(name)`, `Stop(name)`, `Restart(name)` | Comme dans les transitions ci-dessus ; une erreur est renvoyée à l'interface. |
| | `Logs(name)` | Les 200 dernières lignes stderr de l'agent. |
| `ConfigService` | `Current()` | `path, source, created, valid, problems[], warnings[], theme, language, historyProblem`. |
## Channels (milestones M2 et M3)
Un channel contient une conversation entre l'utilisateur et un ou plusieurs agents. Une conversation directe est un channel à un seul membre, sans nom, dont `direct` vaut le nom de son agent. Les channels, les membres et les messages terminés sont gardés dans l'historique du fichier de configuration (ci-dessous) ; une réponse en cours est gardée quand elle se termine.
### Channels
| Champ | Signification |
| --- | --- |
| `id` | identifiant triable |
| `name`, `description` | 1 à 64 caractères, unique sans tenir compte de la casse ; vide pour une conversation directe |
| `direct` | l'agent d'une conversation directe ; vide pour un channel |
| `members` | noms des agents, dans l'ordre d'arrivée |
### Qui répond à un message
| Le message | Répondu par |
| --- | --- |
| `/agent:commande arguments` (`agent` est un agent de la configuration) | cet agent, qui reçoit `/commande arguments` |
| contient des mentions `@nom` d'agents de la configuration | les agents mentionnés, l'un après l'autre, dans l'ordre des mentions ; un agent mentionné qui n'est pas membre rejoint d'abord le channel |
| sans mention | le dernier agent qui a répondu dans le channel ; avec un seul membre, ce membre |
| sans mention, personne n'a encore répondu, plusieurs membres | personne : message système `no-recipient` |
| tout message d'une conversation directe | son agent ; les mentions y sont du simple texte |
Un message qui commence par `/` (éventuellement après des mentions) est une commande : il est envoyé seul. Tout autre message est envoyé avec les messages du channel que l'agent n'a pas encore vus (au plus les 20 derniers, chacun coupé à 1500 caractères). Un agent répond à un seul channel à la fois ; un channel qui l'attend indique à qui c'est le tour.
### Messages
| Champ | Signification |
| --- | --- |
| `id`, `channel`, `createdAt` | identité |
| `agent` | l'auteur d'un message d'agent, ou l'agent dont parle un message système |
| `role` | `user`, `agent` ou `system` |
| `parts` | `text` (Markdown), `thought` (raisonnement), `tool` (un appel d'outil), `plan` (étapes d'un plan) |
| `done` | faux pendant qu'un message d'agent s'affiche |
| `queued` | un message de l'utilisateur qui attend la fin des réponses en cours |
| `stopReason` | comment un message d'agent s'est terminé : `end_turn`, `cancelled`, `max_tokens`, `max_turn_requests`, `refusal` |
| `error` | pourquoi un message d'agent s'est terminé sans raison d'arrêt (l'explication de l'agent), ou `cancelled` pour un message en attente abandonné par Stop |
| `code` | messages système : `agent-joined`, `agent-left`, `no-recipient`, `agent-unavailable` (impossible de le démarrer ou de le joindre ; `error` dit pourquoi) |
Un appel d'outil a `id, title, kind` (`read, edit, delete, move, search, execute, think, fetch, other`), `status` (`pending, in_progress, completed, failed`), `input` (JSON compact), `output`, `locations`, `diffs` (`path, oldText, newText, newFile`).
### Événements
| Événement | Contenu | Envoyé quand |
| --- | --- | --- |
| `channels:changed` | `{channels[]}` | un channel est créé, renommé ou supprimé, ou ses membres changent |
| `message:updated` | `{channel, message}` | un message est ajouté, ou terminé |
| `message:delta` | `{channel, messageId, index, kind, text?, part?}` | `text` s'ajoute à la partie `index` (texte, raisonnement), ou `part` la remplace (outil, plan) |
| `turn:changed` | `{channel, busy, queued, answering}` | le channel commence ou arrête de répondre, ou le tour d'un autre agent commence ; `queued` messages attendent |
| `permission:requested` | `{id, channel, agent, messageId, toolCall, options[], rejections}` | un agent demande à utiliser un outil ; `rejections` compte les refus déjà donnés pendant ce tour |
| `permission:resolved` | `{id, channel}` | la demande a reçu une réponse, ou a été annulée par Stop |
| `usage:changed` | `{channel, agent, usage: {used, size}}` | un agent a indiqué l'occupation de sa fenêtre de contexte (`size` vaut 0 si elle est inconnue) |
| `commands:changed` | `{agent, commands[]}` | un agent a annoncé ses commandes slash (`name, description, hint`) |
### `ChannelService`
| Méthode | Effet |
| --- | --- |
| `List()` | Tous les channels et conversations directes, du plus ancien au plus récent. |
| `Create(name, description)` | Crée un channel. Un nom déjà pris ou vide est une erreur. |
| `Rename(id, name, description)` | Renomme un channel (pas une conversation directe). |
| `Delete(id)` | Arrête ses réponses, puis supprime le channel et tout son historique. |
| `AddMember(id, agent)`, `RemoveMember(id, agent)` | Invite ou retire un agent ; le channel affiche `agent-joined` ou `agent-left`. |
| `Direct(agent)` | La conversation directe avec `agent`, créée la première fois. |
| `Send(id, text)` | Ajoute un message de l'utilisateur et renvoie son id ; les agents qui répondent sont démarrés si besoin ; le message attend si le channel répond déjà. Un texte vide ou un channel inconnu sont des erreurs. |
| `Cancel(id)` | Arrête la réponse en cours (`session/cancel`), les agents qui devaient encore répondre et les messages en attente ; répond « cancelled » aux demandes de permission en attente. |
| `AnswerPermission(id, optionId)` | Répond à une demande en attente avec l'une de ses options. |
| `Conversation(id)` | `channel, messages` (les 100 derniers), `more, busy, queued, answering, usage{agent: …}, permissions`. |
| `Older(id, beforeId)` | `{messages, more}` : les 100 messages précédant `beforeId`. |
| `Commands(agent)` | Les dernières commandes annoncées par l'agent, gardées d'un lancement à l'autre ; vide s'il n'en a jamais annoncé. |
| `Prepare(id)` | Démarre les membres du channel qui n'ont jamais annoncé leurs commandes et ouvre leur session pour ce channel (aucun modèle n'est chargé) ; rend la main une fois les sessions ouvertes, les commandes arrivent par `commands:changed`. Un membre qui répond ailleurs est ignoré. La session sert la première réponse du membre, à qui l'on donne quand même le passé du channel. |
### Historique
| | |
| --- | --- |
| Emplacement | `<dossier de config utilisateur>/sidekicks/data/<16 caractères hexadécimaux>/` : une base Pebble par fichier de configuration (l'empreinte de son chemin absolu). Le dossier de config utilisateur est `~/Library/Application Support` sous macOS, `$XDG_CONFIG_HOME` ou `~/.config` sous Linux. |
| Gardé | les channels, les membres (avec l'id de session ACP de chaque agent et le dernier message qu'il a vu), les messages terminés, les dernières commandes de chaque agent |
| Non gardé | les demandes de permission en attente, les messages en attente, une réponse interrompue par la fermeture |
| Indisponible | si la base ne peut pas être ouverte (un autre Sidekicks utilise le même fichier), l'historique vit en mémoire jusqu'à la fermeture, et `ConfigService.Current().historyProblem` dit pourquoi |
| Après un redémarrage | un agent qui sait faire `session/load` reprend sa session ; sinon, son premier prompt dans un channel contient les derniers messages de ce channel |
### Méthodes Settings de `ConfigService`
| Méthode | Effet |
| --- | --- |
| `Content()` | Le texte du fichier de configuration. |
| `Validate(text)` | Les problèmes et avertissements de `text`, avec `line, column, message, warning`, sans enregistrer. |
| `Save(text)` | Écrit `text` s'il est valide ; sinon renvoie ses problèmes et n'écrit rien. |
| `Schema()` | Le JSON Schema de la configuration. |
| `Open(path)` | Arrête les réponses et tous les agents, ferme l'historique, charge et surveille `path` avec son propre historique, le mémorise. |
| `Choose()` | Affiche le sélecteur de fichier natif et ouvre le fichier choisi ; renvoie `""` en cas d'annulation. |
| `Recent()` | Les fichiers de configuration ouverts depuis l'application, du plus récent au plus ancien (8 au plus). |
### Arrêt d'un agent
L'entrée standard de l'agent est fermée ; après 5 s, l'agent et ses enfants reçoivent SIGTERM (le temps pour `scripts/agents.sh` de décharger le modèle) ; 3 s plus tard, SIGKILL.
|