bots-garden/sidekickspublic Fork
⑂ feature/1st-release
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/sidekicks.git
git clone ssh://git@rickub.com/bots-garden/sidekicks.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. d580a32 · on feature/1st-release · k33g · 3d ago
agents-et-evenements.md · 200 lines · 14.6 KBmarkdown
Blame HistoryOpen raw

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

Workspaces (milestone M4)

Le workspace d'un agent est son dossier workspace, atteint selon runtime.type.

Runtime Fichiers Changements suivis Terminal Suppression
local directement notifications du système de fichiers (pas dans .git, node_modules), une demi-seconde après le dernier changement $SHELL -l (sinon bash, sinon sh) dans un pseudo-terminal vers la corbeille du système (~/.Trash, ou la corbeille freedesktop.org) ; définitive si la corbeille est sur un autre disque
container docker exec dans workspacePath (par défaut : le même chemin que workspace) dates et tailles comparées toutes les 2 s docker exec -it : bash s'il existe, sinon sh définitive
sbx sbx exec dans workspacePath (par défaut : le même chemin que workspace, où sbx monte le dossier de l'hôte) dates et tailles comparées toutes les 2 s sbx exec -it : bash s'il existe, sinon sh définitive

Les chemins sont relatifs au dossier et utilisent /. Un chemin qui sort du dossier (.., absolu, lien symbolique vers l'extérieur) est refusé. Le dossier lui-même ne peut pas être supprimé.

Fichiers

Champ Signification
name, path le nom de l'entrée, et son chemin depuis le dossier
dir un dossier
size, modTime taille en octets, dernière modification

Un fichier ouvert pour modification a path, content, version ; version identifie le contenu.

WorkspaceService

Méthode Effet
Info(agent) {location, problem} : où est le dossier (local · /chemin, container nom · /chemin), ou pourquoi il ne peut pas être affiché.
List(agent, dir) Les entrées de dir ("" est le dossier), dossiers d'abord, puis par nom.
Read(agent, path) Un fichier texte de 1 Mo au plus ; un fichier binaire ou plus gros est une erreur.
Save(agent, path, content, version) Écrit le fichier s'il a toujours version ; sinon {conflict: true} et rien n'est écrit. Une version vide écrase. Le fichier est écrit à côté puis déplacé à sa place.
Create(agent, dir, name) Crée un fichier vide ; refusé si le nom existe ou contient /.
Remove(agent, path) Supprime un fichier ou un dossier avec son contenu (voir le tableau ci-dessus).
Watch(agent), Unwatch() Suit les changements du dossier d'un agent, annoncés par workspace:changed.

TerminalService

Méthode Effet
Open(agent, cols, rows) Démarre un shell dans le dossier de l'agent ; renvoie son id.
Write(id, data) Envoie des frappes.
Resize(id, cols, rows) Change la taille, en caractères.
Close(id) Raccroche le shell et tout ce qu'il a lancé (les groupes de processus de ses descendants), tâches de fond comprises, puis tue ce qui reste au bout d'une seconde ; dans un conteneur, à l'intérieur du conteneur aussi.

Événements

Événement Contenu Envoyé quand
workspace:changed {agent} des fichiers ont changé dans le dossier suivi
terminal:output {id, data} le terminal a affiché quelque chose ; regroupé toutes les 16 ms ou 32 Ko, sans jamais couper un caractère
terminal:exit {id} le shell s'est terminé

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.

  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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# 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 |

## Workspaces (milestone M4)

Le workspace d'un agent est son dossier `workspace`, atteint selon `runtime.type`.

| Runtime | Fichiers | Changements suivis | Terminal | Suppression |
| --- | --- | --- | --- | --- |
| `local` | directement | notifications du système de fichiers (pas dans `.git`, `node_modules`), une demi-seconde après le dernier changement | `$SHELL -l` (sinon bash, sinon sh) dans un pseudo-terminal | vers la corbeille du système (`~/.Trash`, ou la corbeille freedesktop.org) ; définitive si la corbeille est sur un autre disque |
| `container` | `docker exec` dans `workspacePath` (par défaut : le même chemin que `workspace`) | dates et tailles comparées toutes les 2 s | `docker exec -it` : bash s'il existe, sinon sh | définitive |
| `sbx` | `sbx exec` dans `workspacePath` (par défaut : le même chemin que `workspace`, où sbx monte le dossier de l'hôte) | dates et tailles comparées toutes les 2 s | `sbx exec -it` : bash s'il existe, sinon sh | définitive |

Les chemins sont relatifs au dossier et utilisent `/`. Un chemin qui sort du dossier (`..`, absolu, lien symbolique vers l'extérieur) est refusé. Le dossier lui-même ne peut pas être supprimé.

### Fichiers

| Champ | Signification |
| --- | --- |
| `name`, `path` | le nom de l'entrée, et son chemin depuis le dossier |
| `dir` | un dossier |
| `size`, `modTime` | taille en octets, dernière modification |

Un fichier ouvert pour modification a `path, content, version` ; `version` identifie le contenu.

### `WorkspaceService`

| Méthode | Effet |
| --- | --- |
| `Info(agent)` | `{location, problem}` : où est le dossier (`local · /chemin`, `container nom · /chemin`), ou pourquoi il ne peut pas être affiché. |
| `List(agent, dir)` | Les entrées de `dir` (`""` est le dossier), dossiers d'abord, puis par nom. |
| `Read(agent, path)` | Un fichier texte de 1 Mo au plus ; un fichier binaire ou plus gros est une erreur. |
| `Save(agent, path, content, version)` | Écrit le fichier s'il a toujours `version` ; sinon `{conflict: true}` et rien n'est écrit. Une `version` vide écrase. Le fichier est écrit à côté puis déplacé à sa place. |
| `Create(agent, dir, name)` | Crée un fichier vide ; refusé si le nom existe ou contient `/`. |
| `Remove(agent, path)` | Supprime un fichier ou un dossier avec son contenu (voir le tableau ci-dessus). |
| `Watch(agent)`, `Unwatch()` | Suit les changements du dossier d'un agent, annoncés par `workspace:changed`. |

### `TerminalService`

| Méthode | Effet |
| --- | --- |
| `Open(agent, cols, rows)` | Démarre un shell dans le dossier de l'agent ; renvoie son id. |
| `Write(id, data)` | Envoie des frappes. |
| `Resize(id, cols, rows)` | Change la taille, en caractères. |
| `Close(id)` | Raccroche le shell et tout ce qu'il a lancé (les groupes de processus de ses descendants), tâches de fond comprises, puis tue ce qui reste au bout d'une seconde ; dans un conteneur, à l'intérieur du conteneur aussi. |

### Événements

| Événement | Contenu | Envoyé quand |
| --- | --- | --- |
| `workspace:changed` | `{agent}` | des fichiers ont changé dans le dossier suivi |
| `terminal:output` | `{id, data}` | le terminal a affiché quelque chose ; regroupé toutes les 16 ms ou 32 Ko, sans jamais couper un caractère |
| `terminal:exit` | `{id}` | le shell s'est terminé |

## 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.