lv24-opencode-consilium 0.0.0-stage → 1.4.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/LICENSE +21 -0
- package/README.md +499 -3
- package/README.ru.md +500 -0
- package/README.zh.md +188 -0
- package/bin/cli.js +19 -0
- package/bin/mcp.js +24 -0
- package/consilium.config.example.json +84 -0
- package/dist/agents.d.ts +79 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +1051 -0
- package/dist/agents.js.map +1 -0
- package/dist/artifacts.d.ts +75 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +464 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1392 -0
- package/dist/cli.js.map +1 -0
- package/dist/compat.d.ts +160 -0
- package/dist/compat.d.ts.map +1 -0
- package/dist/compat.js +419 -0
- package/dist/compat.js.map +1 -0
- package/dist/config.d.ts +112 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +728 -0
- package/dist/config.js.map +1 -0
- package/dist/consensus.d.ts +73 -0
- package/dist/consensus.d.ts.map +1 -0
- package/dist/consensus.js +273 -0
- package/dist/consensus.js.map +1 -0
- package/dist/defaults.d.ts +34 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +102 -0
- package/dist/defaults.js.map +1 -0
- package/dist/doctor.d.ts +27 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +798 -0
- package/dist/doctor.js.map +1 -0
- package/dist/fallback.d.ts +50 -0
- package/dist/fallback.d.ts.map +1 -0
- package/dist/fallback.js +134 -0
- package/dist/fallback.js.map +1 -0
- package/dist/git.d.ts +28 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +71 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +653 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +20 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +1215 -0
- package/dist/mcp.js.map +1 -0
- package/dist/models.d.ts +97 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +873 -0
- package/dist/models.js.map +1 -0
- package/dist/plugin.d.ts +59 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +223 -0
- package/dist/plugin.js.map +1 -0
- package/dist/presets.d.ts +27 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +86 -0
- package/dist/presets.js.map +1 -0
- package/dist/replay.d.ts +37 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +103 -0
- package/dist/replay.js.map +1 -0
- package/dist/report.d.ts +28 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +300 -0
- package/dist/report.js.map +1 -0
- package/dist/smart-experts.d.ts +44 -0
- package/dist/smart-experts.d.ts.map +1 -0
- package/dist/smart-experts.js +242 -0
- package/dist/smart-experts.js.map +1 -0
- package/dist/stream.d.ts +50 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +219 -0
- package/dist/stream.js.map +1 -0
- package/dist/tool-run.d.ts +49 -0
- package/dist/tool-run.d.ts.map +1 -0
- package/dist/tool-run.js +143 -0
- package/dist/tool-run.js.map +1 -0
- package/dist/types.d.ts +352 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/variants.d.ts +30 -0
- package/dist/variants.d.ts.map +1 -0
- package/dist/variants.js +111 -0
- package/dist/variants.js.map +1 -0
- package/dist/verify.d.ts +33 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +80 -0
- package/dist/verify.js.map +1 -0
- package/dist/watch.d.ts +73 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +350 -0
- package/dist/watch.js.map +1 -0
- package/docs/session-and-plugin.md +642 -0
- package/package.json +90 -6
- package/schema/config.schema.json +100 -0
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
# Сессии `opencode run` и нативный V2-плагин Consilium
|
|
2
|
+
|
|
3
|
+
> **Статус на 1.4.0:** рекомендации этого документа реализованы в `src/plugin.ts`,
|
|
4
|
+
> но с двумя отличиями: `Plugin.define` в `@opencode/plugin@2.2.6` — identity-функция
|
|
5
|
+
> на рантайме, поэтому плагин экспортирует типизированный объект `{ id, setup }`
|
|
6
|
+
> и импортирует `@opencode/plugin` только как тип; рабочая директория берётся из
|
|
7
|
+
> `ctx.location.directory`, а не из `process.cwd()`. Рантайм-зависимостей у пакета
|
|
8
|
+
> нет, `@opencode/plugin` остаётся devDependency (не `dependencies`/`peerDependencies`).
|
|
9
|
+
> Разделы 5–6 ниже — исходное исследование и чек-лист; актуальная реализация в
|
|
10
|
+
> `src/plugin.ts`, `src/tool-run.ts`, `package.json` (`exports["./server"]`).
|
|
11
|
+
|
|
12
|
+
Рабочий пакет WP5. Только исследование и документация: исходники проекта не менялись.
|
|
13
|
+
|
|
14
|
+
Окружение проверки:
|
|
15
|
+
|
|
16
|
+
| Параметр | Значение |
|
|
17
|
+
|---|---|
|
|
18
|
+
| OpenCode | **v2.2.6** (`opencode --version`) |
|
|
19
|
+
| ОС / оболочка | Windows, PowerShell 5.1 |
|
|
20
|
+
| Проект | `C:\0project\opencode-consilium` |
|
|
21
|
+
| Модель прогонов | `opencode-go/muse-spark-1.3-contributor` (стоимость прогонов `cost: 0`) |
|
|
22
|
+
| Документация | `https://opencode.ai/docs/...`, переключатель **V2 (latest)** |
|
|
23
|
+
| Хранилище сессий | `C:\Users\<user>\.local\share\opencode\opencode.db` (SQLite, наблюдалось) |
|
|
24
|
+
|
|
25
|
+
Все идентификаторы сессий ниже сокращены: `ses_e46f6d52…AqTXO`.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. `opencode run`: фактическая семантика флагов
|
|
30
|
+
|
|
31
|
+
`opencode run --help` на v2.2.6 (дословно, значимые строки):
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
FLAGS
|
|
35
|
+
--continue, -c Continue the last session
|
|
36
|
+
--session, -s string Session ID to continue, or to create if it does not exist
|
|
37
|
+
--fork Fork the session before continuing
|
|
38
|
+
--worktree, -w [=name] Start a new session in a new git worktree, optionally with this name
|
|
39
|
+
--format choice Output format (choices: default, json)
|
|
40
|
+
--title string Session title
|
|
41
|
+
--archived Create the session archived so it stays out of session lists (useful for scripted or CI runs)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`opencode session --help` на v2.2.6 — подкоманд архивации нет:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
SUBCOMMANDS
|
|
48
|
+
list List top-level sessions in the current project, newest first
|
|
49
|
+
delete Delete a session and its child sessions
|
|
50
|
+
export Export session data as JSON
|
|
51
|
+
import Import session data from a JSON file or URL
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`opencode session list --help`: `--max-count, -n` (по умолчанию 100), `--format` = `table | json`.
|
|
55
|
+
Флага «показать архивные» нет.
|
|
56
|
+
|
|
57
|
+
### Сводка
|
|
58
|
+
|
|
59
|
+
| Флаг | Что делает (проверено) |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `--session <id>` | Продолжает сессию, **если она есть**, и **создаёт с ровно этим id**, если её нет |
|
|
62
|
+
| `--format json` | NDJSON-поток событий; `sessionID` есть в каждом событии (и в корне, и в `part`) |
|
|
63
|
+
| `--fork` | Создаёт **новую** сессию-копию перед продолжением; заголовок получает суффикс `(fork #N)`, контекст родителя переносится |
|
|
64
|
+
| `--archived` | Ставит `time.archived` в записи сессии, **но `opencode session list` её всё равно показывает** |
|
|
65
|
+
| `--continue` | «Последняя сессия» — не исполнялось (машинно-зависимо, для автоматизации не рекомендуется) |
|
|
66
|
+
| `--worktree` | Не исполнялось: создаёт git worktree (побочный эффект в репозитории) |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 2. Эмпирические прогоны
|
|
71
|
+
|
|
72
|
+
### T1. Создание сессии обычным запуском
|
|
73
|
+
|
|
74
|
+
```powershell
|
|
75
|
+
opencode run --format json -m opencode-go/muse-spark-1.3-contributor `
|
|
76
|
+
--title 'WP5 probe' 'Reply with exactly: OK'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Наблюдаемый вывод (3 строки NDJSON, сокращено):
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
{"type":"step_start","sessionID":"ses_e46f6d52…AqTXO", ...}
|
|
83
|
+
{"type":"text","sessionID":"ses_e46f6d52…AqTXO","part":{"type":"text","text":"OK", ...}}
|
|
84
|
+
{"type":"step_finish","sessionID":"ses_e46f6d52…AqTXO","part":{...,"cost":0,
|
|
85
|
+
"tokens":{"input":16226,"output":10,"reasoning":7,"cache":{"read":1521,"write":0}}}}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Вывод: `--format json` — поток отдельных JSON-объектов (не один документ); `sessionID` доступен уже в первом событии;
|
|
89
|
+
`step_finish.part.tokens` и `part.cost` дают расход по шагу.
|
|
90
|
+
|
|
91
|
+
### T2. Продолжение той же сессии — контекст сохраняется
|
|
92
|
+
|
|
93
|
+
```powershell
|
|
94
|
+
opencode run --format json -m opencode-go/muse-spark-1.3-contributor `
|
|
95
|
+
--session ses_e46f6d52…AqTXO 'What did I ask you to reply before? One word.'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Наблюдаемый вывод:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
{"type":"text","sessionID":"ses_e46f6d52…AqTXO","part":{"type":"text","text":"OK", ...}}
|
|
102
|
+
{"type":"step_finish","sessionID":"ses_e46f6d52…AqTXO","part":{...,"cost":0,
|
|
103
|
+
"tokens":{"input":134,"output":10,"reasoning":141,"cache":{"read":17649,"write":0}}}}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Вывод: модель ответила `OK`, то есть предыдущая инструкция была в контексте. `sessionID` в JSON тот же;
|
|
107
|
+
`cache.read = 17649` — провайдер переиспользовал префикс промпта (экономия денег, но **не** контекстного окна).
|
|
108
|
+
|
|
109
|
+
### T3. Создание сессии с заданным id
|
|
110
|
+
|
|
111
|
+
```powershell
|
|
112
|
+
opencode run --format json -m opencode-go/muse-spark-1.3-contributor `
|
|
113
|
+
--session ses_0000000000000000000000000 --title 'WP5 create-by-id' 'Reply with exactly: NEWID'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Наблюдаемый вывод:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
{"type":"step_start","sessionID":"ses_0000000000000000000000000", ...}
|
|
120
|
+
{"type":"text","sessionID":"ses_0000000000000000000000000","part":{"type":"text","text":"NEWID", ...}}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Вывод: id создаётся **ровно таким**, каким передан (`ses_` + цифры принят). Это позволяет Consilium держать
|
|
124
|
+
детерминированные id на проект/эксперта, не парся ответ.
|
|
125
|
+
|
|
126
|
+
### T4. `--fork` — новая сессия, контекст скопирован
|
|
127
|
+
|
|
128
|
+
```powershell
|
|
129
|
+
opencode run --format json -m opencode-go/muse-spark-1.3-contributor `
|
|
130
|
+
--session ses_e46f6d52…AqTXO --fork --title 'WP5 fork probe' `
|
|
131
|
+
'What did I ask you to reply before? One word.'
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Наблюдаемый вывод:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
{"type":"step_start","sessionID":"ses_e46f3f2b…xuU9R", ...} # НОВЫЙ id
|
|
138
|
+
{"type":"text","sessionID":"ses_e46f3f2b…xuU9R","part":{"type":"text","text":"OK", ...}}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
В `opencode session list`: `ses_e46f3f2b…xuU9R WP5 probe (fork #1)`.
|
|
142
|
+
Вывод: `--fork` не продолжает родителя, а клонирует его в новую сессию (заголовок `(fork #1)`) и уже там отвечает;
|
|
143
|
+
контекст родителя перенесён. Исходная сессия не меняется.
|
|
144
|
+
|
|
145
|
+
### T5. `--archived` — архивность ставится, но из списка не убирает
|
|
146
|
+
|
|
147
|
+
```powershell
|
|
148
|
+
opencode run --format json -m opencode-go/muse-spark-1.3-contributor `
|
|
149
|
+
--archived --title 'WP5 archived probe 2' 'Reply with exactly: A2'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`opencode session export <id>` показывает, что флаг сработал:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
"time": {
|
|
156
|
+
"created": 1794105937241,
|
|
157
|
+
"updated": 1794105939080,
|
|
158
|
+
"busy": 1794105937306,
|
|
159
|
+
"idle": 1794105939085,
|
|
160
|
+
"archived": 1794105937257
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Но `opencode session list` сразу после создания — и повторно, на второй архивной сессии (`WP5 archived probe`), —
|
|
165
|
+
содержит обе архивные сессии, и в `table`, и в `json`:
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
ses_e46f3f2b…WfK WP5 archived probe 2 08.11.2026, 05:45:39
|
|
169
|
+
ses_e46f6754…Qju WP5 archived probe 08.11.2026, 05:42:54
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Вывод: на v2.2.6 `--archived` **не гарантирует** «stays out of session lists» для CLI. Скрывает ли их TUI —
|
|
173
|
+
не проверялось. Для чистой истории остаётся `opencode session delete <id>` или детерминированные id.
|
|
174
|
+
|
|
175
|
+
### T6. Конкурентные запуски на одной сессии — потеря запроса
|
|
176
|
+
|
|
177
|
+
Два параллельных процесса с **одной** сессией:
|
|
178
|
+
|
|
179
|
+
```powershell
|
|
180
|
+
opencode run ... --session ses_1111111111111111111111111 'Reply with exactly: C1'
|
|
181
|
+
opencode run ... --session ses_1111111111111111111111111 'Reply with exactly: C2'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Оба процесса вернули **побайтово одинаковый** поток, включая один и тот же `messageID` `msg_1b90d509d…` и текст `C2`:
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
{"type":"text","sessionID":"ses_1111111111111111111111111","part":{"type":"text","text":"C2", ...}}
|
|
188
|
+
{"type":"step_finish",...,"tokens":{"input":17758,"output":11,"reasoning":164,"cache":{"read":0,"write":0}}}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Вывод: одна сессия = **один писатель**. Второй процесс примкнул к уже выполняющемуся ходу; промпт `C1` не был
|
|
192
|
+
обработан как отдельный ход. Параллельные эксперты Consilium не должны делить сессию.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 3. Рекомендация для Consilium: `--session`
|
|
197
|
+
|
|
198
|
+
### 3.1. Текущее состояние кода (проверено чтением)
|
|
199
|
+
|
|
200
|
+
| Место | Факт |
|
|
201
|
+
|---|---|
|
|
202
|
+
| `src/types.ts:208` | `ConsiliumConfig.session?: string` — «Переиспользовать сессию OpenCode» |
|
|
203
|
+
| `src/types.ts:253` | `RunOptions.session?: string` |
|
|
204
|
+
| `src/cli.ts:222` | CLI принимает `--session` |
|
|
205
|
+
| `src/agents.ts:295-329` | `buildRunArgs` умеет `--session=<id>` с проверкой `SESSION_PATTERN` |
|
|
206
|
+
| `src/agents.ts:692-696` | `runAgentProcess` **не передаёт** `session` в `buildRunArgs` |
|
|
207
|
+
| `src/stream.ts` | `sessionID` из событий не извлекается |
|
|
208
|
+
|
|
209
|
+
То есть опция объявлена, но **не подключена**: сейчас `--session` до `opencode run` не доходит.
|
|
210
|
+
|
|
211
|
+
### 3.2. Точный маппинг флагов
|
|
212
|
+
|
|
213
|
+
| Опция Consilium | Аргумент CLI | Когда использовать |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| `session: <id>` | `--session=<id>` | Явное продолжение существующей сессии |
|
|
216
|
+
| `session: 'auto'` (новая политика) | без флага, затем взять `sessionID` из первого события | Значение по умолчанию: новая сессия на запуск |
|
|
217
|
+
| `forkSession: true` | `--fork` (вместе с `--session`) | Ветвление уже накопленного контекста |
|
|
218
|
+
| `archiveSession: true` | `--archived` | Только как пометка в данных; **не** скрывает из `session list` |
|
|
219
|
+
| `standalone: true` (уже дефолт) | `--standalone` (`agents.ts:303-305`) | Обязательно сохранить: плагин работает внутри сервера OpenCode, а дочерние прогоны не должны в него переиспользовать |
|
|
220
|
+
| `title: <строка>` | `--title "consilium: <preset>: <короткая задача>"` | Навигация в списке сессий |
|
|
221
|
+
|
|
222
|
+
Формат id: `ses_` + `[a-z0-9]` (проверено на цифровом id). Не использовать `:` `/` `#` в самостоятельно
|
|
223
|
+
сгенерированных id, даже если `SESSION_PATTERN` их допускает: поведение CLI/TUI для них не проверялось.
|
|
224
|
+
|
|
225
|
+
### 3.3. Политика: новая сессия на запуск, переиспользование — opt-in
|
|
226
|
+
|
|
227
|
+
**По умолчанию** — новая сессия на каждый `run()`:
|
|
228
|
+
|
|
229
|
+
* контекст не растёт между запусками (см. 3.4);
|
|
230
|
+
* нет конфликтов между параллельными экспертами (T6);
|
|
231
|
+
* `sessionID` фиксируется из первого события и пишется в `ConsiliumResult.metadata.sessionID` — этого достаточно,
|
|
232
|
+
чтобы потом явно продолжить (`--session <id>`).
|
|
233
|
+
|
|
234
|
+
**Opt-in переиспользование** — только **per-expert**, не общая сессия:
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
ses_consilium_<projectSlug>_<expert> # у каждого эксперта своя нить
|
|
238
|
+
ses_consilium_<projectSlug>_chair # у председателя своя
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Почему per-expert: эксперты запускаются параллельно (`callExpertsParallel`), а параллельный запуск на одной сессии
|
|
242
|
+
теряет часть промптов (T6). Отдельные id полностью снимают конфликт и дают каждому эксперту собственный
|
|
243
|
+
накопленный контекст между раундами.
|
|
244
|
+
|
|
245
|
+
Продолжение «как было» — через `--fork`, если нужно ответвление без порчи основной нити (T4).
|
|
246
|
+
|
|
247
|
+
### 3.4. Ловушки
|
|
248
|
+
|
|
249
|
+
1. **Рост контекста.** Первый ход «Reply with exactly: OK» — `input 16226`; второй ход в той же сессии —
|
|
250
|
+
`input 134 + cache.read 17649`. То есть промпт вырос примерно на 1.5k токенов при ответе в два токена.
|
|
251
|
+
В реальном Consilium каждый ход добавляет промпт эксперта + ответ; при переиспользовании рост линейный и
|
|
252
|
+
упирается в контекстное окно дешёвых моделей. Кэш удешевляет токены, но окно не расширяет.
|
|
253
|
+
2. **Архивные сессии не скрываются** из `opencode session list` (T5). Не строить на `--archived` гарантий
|
|
254
|
+
«чистого» списка; для уборки — `opencode session delete`.
|
|
255
|
+
3. **Конкурентность.** Одна сессия — один писатель (T6). Никогда не передавать общий `--session` параллельным
|
|
256
|
+
экспертам.
|
|
257
|
+
4. **`--continue`** продолжает «последнюю» сессию и в автоматизации опасен: зависит от того, что делал пользователь.
|
|
258
|
+
5. **Сессии привязаны к проекту.** `session list` — «in the current project»; в записи есть `directory`
|
|
259
|
+
(наблюдалось `"directory":"C:\\0project\\opencode-consilium"`). При запуске из другого каталога id не найдётся.
|
|
260
|
+
6. **Плагин внутри сервера.** Инструмент плагина выполняется в процессе сервера OpenCode. Нельзя использовать
|
|
261
|
+
`sessionID` вызывающей сессии (`ToolContext.sessionID`) для прогонов совета: ответы совета смешаются с ходом
|
|
262
|
+
агента-вызывающего. Только `--standalone` + отдельные id.
|
|
263
|
+
7. **`--worktree`** создаёт git worktree — в автоматическом прогоне это побочный эффект в рабочем дереве.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 4. V2 plugin API (по документации)
|
|
268
|
+
|
|
269
|
+
Источники: `https://opencode.ai/docs/build/plugins`, `.../plugins/server`, `.../plugins/migrate-v1`,
|
|
270
|
+
`https://opencode.ai/docs/mcp-servers`, схема `https://opencode.ai/config.json`.
|
|
271
|
+
|
|
272
|
+
### 4.1. Минимальная форма
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
import { Plugin } from "@opencode/plugin"
|
|
276
|
+
|
|
277
|
+
export default Plugin.define({
|
|
278
|
+
id: "example",
|
|
279
|
+
async setup(ctx) {
|
|
280
|
+
await ctx.storage.set("loaded", true)
|
|
281
|
+
},
|
|
282
|
+
})
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Цитата (Overview): «A plugin is one package with a server part, a TUI part, a GUI part, or any combination»;
|
|
286
|
+
«The plugin's id comes from its server part's `Plugin.define`».
|
|
287
|
+
`setup` может вернуть cleanup-функцию («runs when the plugin unloads»).
|
|
288
|
+
|
|
289
|
+
### 4.2. Пакет и точки входа (parts)
|
|
290
|
+
|
|
291
|
+
```jsonc
|
|
292
|
+
{
|
|
293
|
+
"name": "opencode-acme-plugin",
|
|
294
|
+
"version": "1.0.0",
|
|
295
|
+
"type": "module",
|
|
296
|
+
"exports": {
|
|
297
|
+
".": "./src/index.ts",
|
|
298
|
+
"./server": "./src/server.ts",
|
|
299
|
+
"./tui": "./src/tui.tsx",
|
|
300
|
+
"./rpc": "./src/rpc.ts"
|
|
301
|
+
},
|
|
302
|
+
"files": ["src", "dist"],
|
|
303
|
+
"dependencies": { "@opencode/plugin": "latest" }
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
* Server part — `.` **или** `./server`; TUI — `./tui`; GUI — `./gui-experimental`; RPC — `./rpc`.
|
|
308
|
+
* «One entry loads every part.»
|
|
309
|
+
* Локальный каталог без `package.json`/`exports` грузит файлы по именам в корне: `server` (иначе `index`), `tui`, `rpc`.
|
|
310
|
+
|
|
311
|
+
### 4.3. Подключение в конфиге
|
|
312
|
+
|
|
313
|
+
```jsonc
|
|
314
|
+
{
|
|
315
|
+
"$schema": "https://opencode.ai/config.json",
|
|
316
|
+
"plugins": [
|
|
317
|
+
"opencode-acme-plugin",
|
|
318
|
+
"opencode-acme-plugin@1.2.0",
|
|
319
|
+
"./plugins/local",
|
|
320
|
+
"/absolute/path/plugin",
|
|
321
|
+
"file:///home/me/plugins/local",
|
|
322
|
+
{
|
|
323
|
+
"package": "@acme/opencode-plugin",
|
|
324
|
+
"options": { "agent": "reviewer", "strict": true }
|
|
325
|
+
}
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Схема конфига v2 подтверждает: `plugins` — массив `string | { package: string; options?: object }`
|
|
331
|
+
(`additionalProperties: false`). Плагины из `.opencode/plugins/` подхватываются автоматически.
|
|
332
|
+
Значения опций доступны в `ctx.options: Readonly<Record<string, any>>`.
|
|
333
|
+
|
|
334
|
+
**Важно (V2):** «Configured local paths must name directories… a configured `./plugin/local.ts` file is skipped
|
|
335
|
+
with a warning in the log.» То есть в `plugins` указывается каталог, а не `.ts`-файл.
|
|
336
|
+
|
|
337
|
+
### 4.4. Кастомные инструменты
|
|
338
|
+
|
|
339
|
+
Регистрация — синхронный transform в `setup`:
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
await ctx.tool.transform((editor) => {
|
|
343
|
+
editor.namespace({ name: "acme", description: "Customer account tools" })
|
|
344
|
+
editor.add({
|
|
345
|
+
name: "greeting",
|
|
346
|
+
description: "Create a greeting",
|
|
347
|
+
input: {
|
|
348
|
+
type: "object",
|
|
349
|
+
properties: { name: { type: "string" } },
|
|
350
|
+
required: ["name"],
|
|
351
|
+
additionalProperties: false,
|
|
352
|
+
},
|
|
353
|
+
options: { namespace: "acme", codemode: true },
|
|
354
|
+
async execute(input, context) {
|
|
355
|
+
await context.progress({ status: "greeting" })
|
|
356
|
+
return { content: `Hello ${(input as { name: string }).name}!` }
|
|
357
|
+
},
|
|
358
|
+
})
|
|
359
|
+
})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Факты из документации и типов `@opencode/plugin@2.2.6`:
|
|
363
|
+
|
|
364
|
+
* `input` — JSON Schema (тип `Tool.ValueSchema`: `JsonSchema | Effect Schema | StandardSchemaV1`);
|
|
365
|
+
в `execute` приходит `unknown`, валидация — на стороне плагина.
|
|
366
|
+
* `execute(input, context)` возвращает `Tool.Result`: `{ content?: string | Content[]; metadata?; output? }`.
|
|
367
|
+
`context` содержит `signal: AbortSignal` и `progress(update) => Promise<void>`.
|
|
368
|
+
* Эффективное имя инструмента = `namespace` + `_` + `name`; точки и недопустимые символы заменяются на `_`:
|
|
369
|
+
`namespace: "acme"` + `name: "greeting"` → `acme_greeting`.
|
|
370
|
+
* `options: { codemode: true }` кладёт инструмент в Code Mode (группировка по namespace, как у MCP):
|
|
371
|
+
ожидаемо `tools.acme.greeting(...)`. Для MCP это документировано явно
|
|
372
|
+
(`tools.<server>.<tool>`); для плагинов тот же namespace+codemode показан в примере — точное имя в Code Mode
|
|
373
|
+
стоит проверить при первом запуске.
|
|
374
|
+
* `editor.update(id, ...)` / `editor.remove(id)` работают по эффективному имени; `transform` возвращает
|
|
375
|
+
`Registration` с `dispose()`; `ctx.tool.reload()` переигрывает transforms, не перезапуская `setup`.
|
|
376
|
+
* Трансформация «replayable»: колбэк синхронный, дешёвый, без одноразовых побочных эффектов; внешние данные
|
|
377
|
+
загружать **до** регистрации и захватывать в замыкании.
|
|
378
|
+
|
|
379
|
+
### 4.5. Хуки
|
|
380
|
+
|
|
381
|
+
В V2 хуки регистрируются на домене, владеющем операцией, и получают один мутируемый объект события:
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
await ctx.tool.hook("execute.before", (event) => { /* event.tool, event.input */ })
|
|
385
|
+
await ctx.shell.hook("create.before", (event) => { event.env.COMPANY_ENV = "development" })
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Карта миграции V1→V2 (из `migrate-v1`): `event → ctx.event.subscribe()`, `dispose → cleanup из setup`,
|
|
389
|
+
`config/tool map → domain transform`, `tool.execute.before → ctx.tool.hook("execute.before")`,
|
|
390
|
+
`shell.env → ctx.shell.hook("create.before")`, `chat.params → ctx.session.hook("context")` и т.д.
|
|
391
|
+
|
|
392
|
+
### 4.6. V1 vs V2
|
|
393
|
+
|
|
394
|
+
| | V1 | V2 (v2.2.6) |
|
|
395
|
+
|---|---|---|
|
|
396
|
+
| Пакет | `@opencode-ai/plugin` | `@opencode/plugin` |
|
|
397
|
+
| Экспорт | функция `async ({ directory, project, client }) => ({ tool, ...hooks })` | `export default Plugin.define({ id, setup(ctx) })` |
|
|
398
|
+
| Ключ конфига | `plugin` + кортеж `[path, options]` | `plugins` + `{ package, options }` |
|
|
399
|
+
| Инструменты | `tool()`-хелпер, `args: { ... }` | `ctx.tool.transform` + JSON Schema, `execute → { content }` |
|
|
400
|
+
| Локальный путь | мог быть файлом | **только каталог** |
|
|
401
|
+
|
|
402
|
+
Цитата: «**V1 plugin implementations do not run in V2.** Moving a file or renaming its config entry is not enough.»
|
|
403
|
+
И обратное: «V1 calls `server()`… V2 reads the default export's `id` and `setup()`… ignoring `server()`».
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## 5. Дизайн нативного V2-плагина: `src/plugin.ts`
|
|
408
|
+
|
|
409
|
+
### 5.1. Идея
|
|
410
|
+
|
|
411
|
+
Один инструмент `consilium_run`, который вызывает **существующую библиотеку** (`Consilium` из `src/index.ts`),
|
|
412
|
+
без вызова собственного CLI (`bin/cli.js`) и без дублирования логики MCP. Плагин — тонкий адаптер:
|
|
413
|
+
валидация входа → вызов use-case → форматирование `Tool.Result`. (Сама библиотека по-прежнему запускает
|
|
414
|
+
`opencode run` для экспертов — это её штатная работа.)
|
|
415
|
+
|
|
416
|
+
Чтобы не дублировать `src/mcp.ts` (`toolConsiliumRun`, строки 472-566), общую часть вынести в
|
|
417
|
+
`src/tool-run.ts` (application-слой) и использовать из обоих адаптеров:
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
// src/tool-run.ts — общий use-case (новый файл, позже)
|
|
421
|
+
export interface ToolRunHooks {
|
|
422
|
+
progress?: (update: { status: string; progress?: number; total?: number }) => Promise<void> | void
|
|
423
|
+
signal?: AbortSignal
|
|
424
|
+
}
|
|
425
|
+
export async function runConsiliumTool(
|
|
426
|
+
args: Record<string, unknown>,
|
|
427
|
+
hooks: ToolRunHooks = {}
|
|
428
|
+
): Promise<{ text: string; metadata: Record<string, unknown>; isError: boolean }>
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Тогда `src/mcp.ts` и `src/plugin.ts` — только транспорты (JSON-RPC / V2 tool), а правила (`task` обязателен,
|
|
432
|
+
`output_file` внутри workspace, пресеты, таймауты) живут в одном месте.
|
|
433
|
+
|
|
434
|
+
### 5.2. Точка входа
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
// src/plugin.ts (новый файл)
|
|
438
|
+
import { Plugin } from "@opencode/plugin"
|
|
439
|
+
import { runConsiliumTool } from "./tool-run.js"
|
|
440
|
+
|
|
441
|
+
export default Plugin.define({
|
|
442
|
+
id: "consilium",
|
|
443
|
+
async setup(ctx) {
|
|
444
|
+
const options = ctx.options as {
|
|
445
|
+
configPath?: string
|
|
446
|
+
defaultPreset?: "fast" | "deep" | "security" | "review" | "free"
|
|
447
|
+
standalone?: boolean
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
await ctx.tool.transform((editor) => {
|
|
451
|
+
editor.namespace({ name: "consilium", description: "Expert council tools" })
|
|
452
|
+
editor.add({
|
|
453
|
+
name: "run",
|
|
454
|
+
description:
|
|
455
|
+
"Run a council of expert AI agents through the OpenCode CLI. " +
|
|
456
|
+
"Experts analyze the task in parallel and the chair synthesizes the final answer.",
|
|
457
|
+
input: {
|
|
458
|
+
type: "object",
|
|
459
|
+
properties: {
|
|
460
|
+
task: { type: "string", minLength: 1, description: "Task for the expert council." },
|
|
461
|
+
experts: { type: "array", items: { type: "string" } },
|
|
462
|
+
chair: { type: "string" },
|
|
463
|
+
preset: { type: "string", enum: ["fast", "deep", "security", "review", "free"] },
|
|
464
|
+
timeout_ms: { type: "number", exclusiveMinimum: 0 },
|
|
465
|
+
session: {
|
|
466
|
+
type: "string",
|
|
467
|
+
description:
|
|
468
|
+
"OpenCode session id to continue. Omit to start a fresh session per run (recommended).",
|
|
469
|
+
},
|
|
470
|
+
fork: { type: "boolean", description: "Fork the given session before continuing." },
|
|
471
|
+
},
|
|
472
|
+
required: ["task"],
|
|
473
|
+
additionalProperties: false,
|
|
474
|
+
},
|
|
475
|
+
options: { namespace: "consilium", codemode: true },
|
|
476
|
+
async execute(input, context) {
|
|
477
|
+
const result = await runConsiliumTool(input as Record<string, unknown>, {
|
|
478
|
+
// runConsiliumTool подписывается на signal и вызывает consilium.cancel()
|
|
479
|
+
signal: context.signal,
|
|
480
|
+
progress: (update) => context.progress(update),
|
|
481
|
+
})
|
|
482
|
+
if (result.isError) throw new Error(result.text)
|
|
483
|
+
return { content: result.text, metadata: result.metadata }
|
|
484
|
+
},
|
|
485
|
+
})
|
|
486
|
+
})
|
|
487
|
+
},
|
|
488
|
+
})
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Замечания по реализации:
|
|
492
|
+
|
|
493
|
+
* `setup` остаётся дешёвым: колбэк `ctx.tool.transform` синхронный, поэтому `Consilium` создаётся внутри
|
|
494
|
+
`execute`, а не в `setup`.
|
|
495
|
+
* `input` приходит как `unknown` — валидировать теми же хелперами, что в `mcp.ts`
|
|
496
|
+
(`requireNonEmptyString`, `optionalStringArray`, `optionalEnum`, `resolveOutputFile`).
|
|
497
|
+
* `context.signal` → `Consilium.cancel()` (`src/index.ts`), чтобы остановка хода останавливала и совет.
|
|
498
|
+
* `ctx.location.directory` — рабочая директория проекта: передавать как `cwd` конфига, а не полагаться на
|
|
499
|
+
`process.cwd()` сервера.
|
|
500
|
+
* `ctx.options.standalone` пробрасывать в `ConsiliumConfig.standalone` (по умолчанию `true`): дочерние прогоны
|
|
501
|
+
не должны подключаться к серверу, внутри которого живёт плагин.
|
|
502
|
+
|
|
503
|
+
### 5.3. Экспорт пакета
|
|
504
|
+
|
|
505
|
+
`package.json` (изменения — позже, не в этом WP):
|
|
506
|
+
|
|
507
|
+
```jsonc
|
|
508
|
+
{
|
|
509
|
+
"type": "module",
|
|
510
|
+
"exports": {
|
|
511
|
+
".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
|
|
512
|
+
"./server": { "types": "./dist/plugin.d.ts", "import": "./dist/plugin.js" },
|
|
513
|
+
"./config": "./dist/config.js",
|
|
514
|
+
"./mcp": "./dist/mcp.js"
|
|
515
|
+
},
|
|
516
|
+
"dependencies": {
|
|
517
|
+
"@opencode/plugin": "^2.2.6"
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
* Server part публикуется как `./server` — это документированное имя части; корень (`.`) уже занят библиотекой.
|
|
523
|
+
* `@opencode/plugin` должен быть `dependency` (как в документации: `"dependencies": { "@opencode/plugin": "latest" }`);
|
|
524
|
+
версия должна соответствовать целевому OpenCode. Для v2.2.6 актуальна `2.2.6`.
|
|
525
|
+
* У пакета `@opencode/plugin@2.2.6` тяжёлые транзитивные зависимости (`effect`, `@opencode/ai`, `@opencode/client`,
|
|
526
|
+
`@opencode/schema`, `@opencode/protocol`). Если размер установки критичен — проверить `peerDependencies`-вариант
|
|
527
|
+
отдельным экспериментом (в доках он не описан).
|
|
528
|
+
|
|
529
|
+
### 5.4. Конфиг
|
|
530
|
+
|
|
531
|
+
```jsonc
|
|
532
|
+
{
|
|
533
|
+
"$schema": "https://opencode.ai/config.json",
|
|
534
|
+
"plugins": [
|
|
535
|
+
{
|
|
536
|
+
"package": "lv24-opencode-consilium",
|
|
537
|
+
"options": {
|
|
538
|
+
"configPath": "consilium.config.json",
|
|
539
|
+
"defaultPreset": "review",
|
|
540
|
+
"standalone": true
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
]
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Локальная разработка без публикации — каталог, не файл:
|
|
548
|
+
|
|
549
|
+
```jsonc
|
|
550
|
+
{ "plugins": [{ "package": "./plugin", "options": {} }] }
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
```text
|
|
554
|
+
plugin/
|
|
555
|
+
index.ts # export { default } from "../dist/plugin.js"
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
(Либо `.opencode/plugins/consilium/index.ts` — такой каталог подхватывается автоматически.)
|
|
559
|
+
|
|
560
|
+
### 5.5. Почему только V2
|
|
561
|
+
|
|
562
|
+
1. Реализация V1 **не исполняется** в V2 (цитата из `migrate-v1`), а V2-объект `{ id, setup }` не является
|
|
563
|
+
функцией, которую ждёт V1.
|
|
564
|
+
2. Ключ конфига и формат записи различаются (`plugin` + кортеж против `plugins` + объект), автоматическая
|
|
565
|
+
нормализация покрывает конфиг, но не код.
|
|
566
|
+
3. Используемые API — `Plugin.define`, `ctx.tool.transform`, `options.codemode`/`namespace`, `ctx.options`,
|
|
567
|
+
`ToolContext.signal` — существуют только в V2.
|
|
568
|
+
4. Целевая версия зафиксирована: установленный OpenCode — v2.2.6.
|
|
569
|
+
|
|
570
|
+
Если позже понадобится V1, документация даёт безопасный способ: экспортировать объект, развернув `Plugin.define`
|
|
571
|
+
и добавив `server()`:
|
|
572
|
+
|
|
573
|
+
```ts
|
|
574
|
+
export default {
|
|
575
|
+
...Plugin.define({ id: "consilium", setup }),
|
|
576
|
+
async server() {
|
|
577
|
+
return { /* V1 hooks */ }
|
|
578
|
+
},
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
V1 вызовет `server()`, V2 прочитает `id`/`setup` и проигнорирует `server()`.
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
## 6. Чек-лист интеграции (файлы, которые надо будет менять)
|
|
587
|
+
|
|
588
|
+
Ничего из перечисленного в этом WP не изменялось.
|
|
589
|
+
|
|
590
|
+
| Файл | Изменение | Зачем |
|
|
591
|
+
|---|---|---|
|
|
592
|
+
| `package.json` | `exports["./server"]` → `dist/plugin.js`; `dependencies["@opencode/plugin"] = "^2.2.6"` | Публикация server part и `Plugin.define` |
|
|
593
|
+
| `src/plugin.ts` | **новый**: `Plugin.define({ id: "consilium" })`, инструмент `consilium_run` | Нативный вход для OpenCode v2 |
|
|
594
|
+
| `src/tool-run.ts` | **новый**: общий use-case `runConsiliumTool(args, hooks)` | Убрать дублирование между `mcp.ts` и `plugin.ts` |
|
|
595
|
+
| `src/mcp.ts` | `toolConsiliumRun` → вызов `runConsiliumTool` | Один источник валидации и форматирования |
|
|
596
|
+
| `src/agents.ts` | `runAgentProcess` (строки 692-696): прокинуть `session`/`fork` в `buildRunArgs` | Опция `session` сейчас мертва |
|
|
597
|
+
| `src/stream.ts` | добавить `sessionID` в `ParsedStream` (есть в каждом событии) | Возвращать id созданной сессии |
|
|
598
|
+
| `src/index.ts` | `RunOptions.session` → эксперты/председатель; писать `metadata.sessionID` | Управляемое переиспользование и трассировка |
|
|
599
|
+
| `src/types.ts` | `ConsiliumPluginOptions` (configPath, defaultPreset, standalone), `forkSession?: boolean` | Типы для `ctx.options` и новых опций |
|
|
600
|
+
| `schema/config.schema.json` | добавить `session` (сейчас поля нет), `forkSession` | Схема отстаёт от `types.ts` |
|
|
601
|
+
| `opencode.json` | блок `plugins` (dogfood) — только после публикации или через `./plugin` | Проверка «в бою» |
|
|
602
|
+
| `test/plugin.test.mjs` | **новый**: валидация входа инструмента, маппинг аргументов, отмена по `signal` | Регрессии |
|
|
603
|
+
| `README.md`, `README.ru.md`, `README.zh.md`, `docs/*` | раздел «Плагин OpenCode v2» с примером `plugins` | Пользовательская документация |
|
|
604
|
+
| `bin/cli.js`, `bin/mcp.js` | без изменений | Точки входа CLI/MCP не затрагиваются |
|
|
605
|
+
|
|
606
|
+
Порядок безопасного включения:
|
|
607
|
+
|
|
608
|
+
1. `src/tool-run.ts` + рефакторинг `mcp.ts` (поведение MCP не меняется, тесты `test/mcp.test.mjs` зелёные).
|
|
609
|
+
2. `src/plugin.ts` + `exports["./server"]` + зависимость.
|
|
610
|
+
3. Локальная проверка: `plugin/index.ts` → `.opencode/plugins/consilium/index.ts`, запуск `opencode` в репозитории,
|
|
611
|
+
проверка, что инструмент виден и `consilium_run` отвечает.
|
|
612
|
+
4. Только потом — `plugins` в `opencode.json` и публикация.
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## 7. Что осталось непроверенным
|
|
617
|
+
|
|
618
|
+
| Пункт | Статус |
|
|
619
|
+
|---|---|
|
|
620
|
+
| Фактическая загрузка плагина в v2.2.6 | Не проверялось (нужен локальный `plugins`-конфиг; в этом WP запрещены правки конфигов) |
|
|
621
|
+
| Точное имя инструмента в Code Mode (`tools.consilium.run`) | Выведено из документации и примера; проверить при первом запуске |
|
|
622
|
+
| `--worktree` | Не исполнялось: создаёт git worktree |
|
|
623
|
+
| `--continue` | Не исполнялось: зависит от «последней» сессии пользователя |
|
|
624
|
+
| Скрывает ли TUI архивные сессии | Неизвестно; CLI-список их показывает |
|
|
625
|
+
| Поведение `@opencode/plugin` как `peerDependency` | В доках описан только `dependencies` |
|
|
626
|
+
| Поведение нестандартных символов в самостоятельно заданном `session` id | Проверен только цифровой суффикс |
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## 8. Источники
|
|
631
|
+
|
|
632
|
+
* `https://opencode.ai/docs/build/plugins` — обзор, parts, `exports`, загрузка, публикация.
|
|
633
|
+
* `https://opencode.ai/docs/build/plugins/server` — `Plugin.define`, `ctx.options`, `ctx.tool.transform`,
|
|
634
|
+
`ToolEditor`, хуки, `Support V1`.
|
|
635
|
+
* `https://opencode.ai/docs/build/plugins/migrate-v1` — «V1 plugin implementations do not run in V2»,
|
|
636
|
+
`plugin` → `plugins`, таблица соответствия хуков, миграция инструментов.
|
|
637
|
+
* `https://opencode.ai/docs/mcp-servers` — `mcp.servers`, `codemode` (по умолчанию `true`), имена
|
|
638
|
+
`<server>_<tool>` и `tools.<server>.<tool>`.
|
|
639
|
+
* `https://opencode.ai/config.json` — `plugins: Array<string | { package, options }>`, наличие legacy `plugin`.
|
|
640
|
+
* Типы `@opencode/plugin@2.2.6` (`dist/promise/plugin.d.ts`, `dist/promise/tool.d.ts`) и
|
|
641
|
+
`@opencode/schema@2.2.6` (`dist/tool.d.ts`): `Context`, `Tool.Info`, `Tool.Options`, `Tool.Result`, `ToolContext`.
|
|
642
|
+
* Собственные прогоны `opencode` v2.2.6 в `C:\0project\opencode-consilium` (T1-T6).
|