reflex-agent 0.2.3 → 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.
Files changed (208) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +116 -103
  3. package/.next/app-path-routes-manifest.json +10 -10
  4. package/.next/build-manifest.json +5 -5
  5. package/.next/prerender-manifest.json +4 -54
  6. package/.next/react-loadable-manifest.json +1 -1
  7. package/.next/server/app/_not-found/page.js +1 -1
  8. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  9. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  10. package/.next/server/app/agents/[agentId]/page.js +3 -3
  11. package/.next/server/app/agents/[agentId]/page.js.nft.json +1 -1
  12. package/.next/server/app/agents/[agentId]/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/agents/[agentId]/respond/route.js +2 -2
  14. package/.next/server/app/api/agents/[agentId]/respond/route.js.nft.json +1 -1
  15. package/.next/server/app/api/agents/[agentId]/respond/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/images/[rootId]/[file]/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/oauth/callback/route.js +4 -4
  18. package/.next/server/app/api/oauth/callback/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/oauth/start/route_client-reference-manifest.js +1 -1
  20. package/.next/server/app/api/roots/[id]/attachments/route.js +0 -0
  21. package/.next/server/app/api/roots/[id]/attachments/route_client-reference-manifest.js +1 -1
  22. package/.next/server/app/api/roots/[id]/chat/[topicId]/send/route.js +2 -2
  23. package/.next/server/app/api/roots/[id]/chat/[topicId]/send/route.js.nft.json +1 -1
  24. package/.next/server/app/api/roots/[id]/chat/[topicId]/send/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/roots/[id]/chat/[topicId]/stop/route.js +2 -2
  26. package/.next/server/app/api/roots/[id]/chat/[topicId]/stop/route.js.nft.json +1 -1
  27. package/.next/server/app/api/roots/[id]/chat/[topicId]/stop/route_client-reference-manifest.js +1 -1
  28. package/.next/server/app/api/roots/[id]/chat/[topicId]/stream/route.js +3 -3
  29. package/.next/server/app/api/roots/[id]/chat/[topicId]/stream/route.js.nft.json +1 -1
  30. package/.next/server/app/api/roots/[id]/chat/[topicId]/stream/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/roots/[id]/dashboard/route.js +1 -1
  32. package/.next/server/app/api/roots/[id]/dashboard/route.js.nft.json +1 -1
  33. package/.next/server/app/api/roots/[id]/dashboard/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/roots/[id]/suggestions/route.js +1 -1
  35. package/.next/server/app/api/roots/[id]/suggestions/route.js.nft.json +1 -1
  36. package/.next/server/app/api/roots/[id]/suggestions/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/utilities/[scope]/[id]/bundle.js/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/utilities/[scope]/[id]/host/route.js +2 -2
  39. package/.next/server/app/api/utilities/[scope]/[id]/host/route.js.nft.json +1 -1
  40. package/.next/server/app/api/utilities/[scope]/[id]/host/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/utilities/[scope]/[id]/host-api.mjs/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/utilities/[scope]/[id]/host-ui.mjs/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/utilities/[scope]/[id]/iframe/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/utilities/[scope]/[id]/style.css/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/audit/page.js +2 -2
  46. package/.next/server/app/audit/page.js.nft.json +1 -1
  47. package/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/onboarding/page.js +3 -3
  49. package/.next/server/app/onboarding/page.js.nft.json +1 -1
  50. package/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  51. package/.next/server/app/page.js +2 -2
  52. package/.next/server/app/page.js.nft.json +1 -1
  53. package/.next/server/app/page_client-reference-manifest.js +1 -1
  54. package/.next/server/app/roots/[id]/chat/[topicId]/page.js +2 -6
  55. package/.next/server/app/roots/[id]/chat/[topicId]/page.js.nft.json +1 -1
  56. package/.next/server/app/roots/[id]/chat/[topicId]/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/roots/[id]/kb/[...slug]/page.js +2 -6
  58. package/.next/server/app/roots/[id]/kb/[...slug]/page.js.nft.json +1 -1
  59. package/.next/server/app/roots/[id]/kb/[...slug]/page_client-reference-manifest.js +1 -1
  60. package/.next/server/app/roots/[id]/page.js +3 -3
  61. package/.next/server/app/roots/[id]/page.js.nft.json +1 -1
  62. package/.next/server/app/roots/[id]/page_client-reference-manifest.js +1 -1
  63. package/.next/server/app/roots/[id]/workflows/[wfId]/page.js +2 -2
  64. package/.next/server/app/roots/[id]/workflows/[wfId]/page.js.nft.json +1 -1
  65. package/.next/server/app/roots/[id]/workflows/[wfId]/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/roots/[id]/workflows/page.js +2 -2
  67. package/.next/server/app/roots/[id]/workflows/page.js.nft.json +1 -1
  68. package/.next/server/app/roots/[id]/workflows/page_client-reference-manifest.js +1 -1
  69. package/.next/server/app/roots/new/page.js +5 -3
  70. package/.next/server/app/roots/new/page.js.nft.json +1 -1
  71. package/.next/server/app/roots/new/page_client-reference-manifest.js +1 -1
  72. package/.next/server/app/settings/page.js +6 -6
  73. package/.next/server/app/settings/page.js.nft.json +1 -1
  74. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/share/[id]/file/page.js +2 -2
  76. package/.next/server/app/share/[id]/file/page.js.nft.json +1 -1
  77. package/.next/server/app/share/[id]/file/page_client-reference-manifest.js +1 -1
  78. package/.next/server/app/share/[id]/page.js +2 -2
  79. package/.next/server/app/share/[id]/page.js.nft.json +1 -1
  80. package/.next/server/app/share/[id]/page_client-reference-manifest.js +1 -1
  81. package/.next/server/app/utilities/[scope]/[id]/page.js +2 -2
  82. package/.next/server/app/utilities/[scope]/[id]/page.js.nft.json +1 -1
  83. package/.next/server/app/utilities/[scope]/[id]/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/utilities/page.js +2 -17
  85. package/.next/server/app/utilities/page.js.nft.json +1 -1
  86. package/.next/server/app/utilities/page_client-reference-manifest.js +1 -1
  87. package/.next/server/app-paths-manifest.json +10 -10
  88. package/.next/server/chunks/1.js +3 -0
  89. package/.next/server/chunks/1223.js +1 -1
  90. package/.next/server/chunks/133.js +1 -490
  91. package/.next/server/chunks/1888.js +1 -1
  92. package/.next/server/chunks/{9739.js → 1988.js} +13 -9
  93. package/.next/server/chunks/2192.js +1 -0
  94. package/.next/server/chunks/2433.js +1 -1
  95. package/.next/server/chunks/2503.js +1 -1
  96. package/.next/server/chunks/285.js +471 -0
  97. package/.next/server/chunks/2995.js +1 -0
  98. package/.next/server/chunks/3240.js +1 -1
  99. package/.next/server/chunks/3332.js +1 -1
  100. package/.next/server/chunks/{2528.js → 3512.js} +2 -2
  101. package/.next/server/chunks/3657.js +1 -1
  102. package/.next/server/chunks/4066.js +1 -1
  103. package/.next/server/chunks/4438.js +1 -0
  104. package/.next/server/chunks/4553.js +1 -1
  105. package/.next/server/chunks/4812.js +179 -0
  106. package/.next/server/chunks/4925.js +1 -1
  107. package/.next/server/chunks/5319.js +1 -1
  108. package/.next/server/chunks/569.js +1 -1
  109. package/.next/server/chunks/6730.js +1 -1
  110. package/.next/server/chunks/6734.js +1 -0
  111. package/.next/server/chunks/6909.js +142 -161
  112. package/.next/server/chunks/7215.js +1 -0
  113. package/.next/server/chunks/8262.js +1 -1
  114. package/.next/server/chunks/9098.js +1 -1
  115. package/.next/server/chunks/94.js +1 -1
  116. package/.next/server/chunks/9835.js +1 -1
  117. package/.next/server/chunks/9944.js +1 -0
  118. package/.next/server/functions-config-manifest.json +1 -1
  119. package/.next/server/middleware-build-manifest.js +1 -1
  120. package/.next/server/middleware-manifest.json +5 -5
  121. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  122. package/.next/server/pages/500.html +1 -1
  123. package/.next/server/pages-manifest.json +1 -2
  124. package/.next/server/server-reference-manifest.js +1 -1
  125. package/.next/server/server-reference-manifest.json +1 -1
  126. package/.next/static/chunks/1082-326e649fb24d4945.js +1 -0
  127. package/.next/static/chunks/3736-f4e42d6d38be50b0.js +1 -0
  128. package/.next/static/chunks/4108.ca0bdf3cbf3c56cc.js +1 -0
  129. package/.next/static/chunks/6445-99824866a51b582a.js +1 -0
  130. package/.next/static/chunks/7482-7ef26030a10ce14f.js +1 -0
  131. package/.next/static/chunks/8944-c4f2406ecd61094f.js +1 -0
  132. package/.next/static/chunks/9411-af5f758c57741929.js +3 -0
  133. package/.next/static/chunks/9415-eb6b5d4c2de3a7c0.js +1 -0
  134. package/.next/static/chunks/app/agents/[agentId]/page-5d6f4cb16b42d02b.js +1 -0
  135. package/.next/static/chunks/app/layout-85eb1fd21dab0895.js +1 -0
  136. package/.next/static/chunks/app/onboarding/page-2013bd8124b9162e.js +1 -0
  137. package/.next/static/chunks/app/page-558a224e13ffb52c.js +1 -0
  138. package/.next/static/chunks/app/roots/[id]/chat/[topicId]/page-b42f03fd58669d12.js +1 -0
  139. package/.next/static/chunks/app/roots/[id]/kb/[...slug]/page-7d17b4e6a5231f56.js +1 -0
  140. package/.next/static/chunks/app/roots/[id]/page-4aab5266f432e37e.js +1 -0
  141. package/.next/static/chunks/app/roots/[id]/workflows/[wfId]/page-1ee3320bf5744efc.js +1 -0
  142. package/.next/static/chunks/app/roots/new/page-df8d2c1f0c64c37a.js +1 -0
  143. package/.next/static/chunks/app/settings/page-fdba798d9e243ad3.js +1 -0
  144. package/.next/static/chunks/app/share/[id]/page-818a451d05e08d26.js +1 -0
  145. package/.next/static/chunks/app/utilities/[scope]/[id]/page-2cee09cc2ab9b5e8.js +1 -0
  146. package/.next/static/chunks/app/utilities/page-44a51522b347f13e.js +1 -0
  147. package/.next/static/chunks/{webpack-87b4bb79fdc48563.js → webpack-2b0eab4ccdf44f63.js} +1 -1
  148. package/.next/static/css/4b367c1d0fa99b78.css +1 -0
  149. package/.next/trace +47 -46
  150. package/README.md +4 -1
  151. package/dist/lib/reflex/agents/prompts.js +46 -46
  152. package/dist/lib/reflex/agents/prompts.js.map +1 -1
  153. package/dist/lib/reflex/prompts/defaults.js +102 -102
  154. package/next.config.ts +4 -1
  155. package/package.json +4 -3
  156. package/packages/utilities/learn-anything/README.md +29 -29
  157. package/packages/utilities/learn-anything/actions/_json.ts +11 -11
  158. package/packages/utilities/learn-anything/actions/_store.ts +2 -2
  159. package/packages/utilities/learn-anything/actions/buildModule.ts +60 -59
  160. package/packages/utilities/learn-anything/actions/explainSelection.ts +14 -13
  161. package/packages/utilities/learn-anything/actions/generateOutline.ts +15 -15
  162. package/packages/utilities/learn-anything/actions/generateQuiz.ts +8 -8
  163. package/packages/utilities/learn-anything/actions/generateTrainer.ts +15 -15
  164. package/packages/utilities/learn-anything/actions/refreshCourseCard.ts +4 -4
  165. package/packages/utilities/learn-anything/actions/tutorAsk.ts +15 -15
  166. package/packages/utilities/learn-anything/article-view.tsx +4 -4
  167. package/packages/utilities/learn-anything/manifest.json +5 -5
  168. package/packages/utilities/learn-anything/ui.tsx +57 -57
  169. package/.next/server/app/_not-found.html +0 -1
  170. package/.next/server/app/_not-found.meta +0 -8
  171. package/.next/server/app/_not-found.rsc +0 -18
  172. package/.next/server/app/index.html +0 -1
  173. package/.next/server/app/index.meta +0 -9
  174. package/.next/server/app/index.rsc +0 -19
  175. package/.next/server/chunks/1986.js +0 -1
  176. package/.next/server/chunks/4065.js +0 -1
  177. package/.next/server/chunks/6981.js +0 -1
  178. package/.next/server/chunks/7017.js +0 -3
  179. package/.next/server/chunks/7800.js +0 -1
  180. package/.next/server/chunks/8494.js +0 -1
  181. package/.next/server/chunks/8843.js +0 -1
  182. package/.next/server/chunks/9328.js +0 -179
  183. package/.next/server/chunks/9423.js +0 -3
  184. package/.next/server/chunks/9455.js +0 -1
  185. package/.next/server/pages/404.html +0 -1
  186. package/.next/static/chunks/1384-d28fb6c6c058876f.js +0 -1
  187. package/.next/static/chunks/2718-78fee2c0de782178.js +0 -1
  188. package/.next/static/chunks/4108.1ef6b5e7679b56ac.js +0 -1
  189. package/.next/static/chunks/7238-11701befb3ca3e41.js +0 -1
  190. package/.next/static/chunks/7951-590bf2004d7935b5.js +0 -1
  191. package/.next/static/chunks/8423-ffded33a21b27360.js +0 -1
  192. package/.next/static/chunks/9045-731ff0865352dd95.js +0 -1
  193. package/.next/static/chunks/app/agents/[agentId]/page-0b5c2838354d0eba.js +0 -1
  194. package/.next/static/chunks/app/layout-4fbf9f91ad45e221.js +0 -1
  195. package/.next/static/chunks/app/onboarding/page-532b193d1c4b0dee.js +0 -1
  196. package/.next/static/chunks/app/page-e3ec0990b78ce7c7.js +0 -1
  197. package/.next/static/chunks/app/roots/[id]/chat/[topicId]/page-11aad9a40def2e0d.js +0 -1
  198. package/.next/static/chunks/app/roots/[id]/kb/[...slug]/page-d8757a85e873dfa1.js +0 -1
  199. package/.next/static/chunks/app/roots/[id]/page-76216026efb90ae0.js +0 -1
  200. package/.next/static/chunks/app/roots/[id]/workflows/[wfId]/page-c4b6e3825f8626f5.js +0 -1
  201. package/.next/static/chunks/app/roots/new/page-53ea8d2787e79425.js +0 -1
  202. package/.next/static/chunks/app/settings/page-7ebaf2b62f256538.js +0 -1
  203. package/.next/static/chunks/app/share/[id]/page-d5bbbb7e454d1375.js +0 -1
  204. package/.next/static/chunks/app/utilities/[scope]/[id]/page-e2f928a37483d113.js +0 -1
  205. package/.next/static/chunks/app/utilities/page-839262ff726a52a2.js +0 -1
  206. package/.next/static/css/87e01f779d555d04.css +0 -1
  207. /package/.next/static/{vTfQfQnAWV_hFVZjWEYvZ → fhVNqfmJl5Mdfhyhg6orp}/_buildManifest.js +0 -0
  208. /package/.next/static/{vTfQfQnAWV_hFVZjWEYvZ → fhVNqfmJl5Mdfhyhg6orp}/_ssgManifest.js +0 -0
@@ -105,43 +105,43 @@ decision as your next user message.
105
105
 
106
106
  <<reflex:permission>>{"tool":"Write","input":{"file_path":"…"},"description":"Why you need it"}<</reflex:permission>>
107
107
 
108
- If you need a clarifying answer from the user, emit a question marker. **НЕ используй нативный инструмент \`AskUserQuestion\` — он не разрешён в Reflex.** Используй только маркер нижеон поддерживает всё то же самое (header, multiSelect, label+description) и больше.
108
+ If you need a clarifying answer from the user, emit a question marker. **DO NOT use the native \`AskUserQuestion\` tool it is not allowed in Reflex.** Use only the marker below it supports everything (header, multiSelect, label+description) and more.
109
109
 
110
- Простой вариант с готовыми ответами:
110
+ Simple variant with ready-made answers:
111
111
 
112
- <<reflex:question>>{"prompt":"Какой язык для саммари?","choices":["english","русский"]}<</reflex:question>>
112
+ <<reflex:question>>{"prompt":"Which language for the summary?","choices":["english","russian"]}<</reflex:question>>
113
113
 
114
- Развёрнутый вариант с label+description (как в AskUserQuestion):
114
+ Detailed variant with label+description (like AskUserQuestion):
115
115
 
116
116
  <<reflex:question>>{
117
117
  "id":"section",
118
- "header":"Раздел",
119
- "prompt":"С какого раздела начнём?",
118
+ "header":"Section",
119
+ "prompt":"Which section should we start with?",
120
120
  "multiSelect":false,
121
121
  "options":[
122
- {"label":"История","description":"Хронология F1 с 1950 года"},
123
- {"label":"Сезон 2025","description":"Календарь и таблицы текущего сезона"}
122
+ {"label":"History","description":"F1 timeline since 1950"},
123
+ {"label":"Season 2025","description":"Calendar and tables for the current season"}
124
124
  ]
125
125
  }<</reflex:question>>
126
126
 
127
- Несколько вопросов в одном маркере (батч — Reflex покажет их подряд карточками):
127
+ Multiple questions in one marker (batch — Reflex will show them as sequential cards):
128
128
 
129
129
  <<reflex:question>>{
130
130
  "questions":[
131
- {"id":"section","header":"Раздел","prompt":"С какого раздела начнём?","options":[…]},
132
- {"id":"depth","header":"Детальность","prompt":"Насколько детальные статьи?","options":[…]}
131
+ {"id":"section","header":"Section","prompt":"Which section should we start with?","options":[…]},
132
+ {"id":"depth","header":"Depth","prompt":"How detailed should the articles be?","options":[…]}
133
133
  ]
134
134
  }<</reflex:question>>
135
135
 
136
- Поля:
137
- - \`prompt\` — обязательно. Сам вопрос, ~4-12 слов.
138
- - \`header\` — короткая бирка-тэг (≤12 символов): «Раздел», «Язык», «Размер». Опционально.
139
- - \`multiSelect\` — \`true\` если можно выбрать несколько вариантов. Reflex вернёт ответ как JSON-массив строк.
140
- - \`options\` — список \`{label, description?}\`. Description — 1 строка контекста под label'ом.
141
- - \`choices\` — legacy флэт массив строк. Для простых случаев. Не комбинируй с \`options\`.
142
- - \`id\` — стабильный id если нужно соотнести ответ. Reflex сам генерит если опущен.
136
+ Fields:
137
+ - \`prompt\` — required. The question itself, ~4-12 words.
138
+ - \`header\` — short tag label (≤12 chars): "Section", "Language", "Size". Optional.
139
+ - \`multiSelect\` — \`true\` if multiple options can be selected. Reflex returns the answer as a JSON array of strings.
140
+ - \`options\` — list of \`{label, description?}\`. Description — 1 line of context under the label.
141
+ - \`choices\` — legacy flat array of strings. For simple cases. Don't combine with \`options\`.
142
+ - \`id\` — stable id if you need to correlate the answer. Reflex generates one if omitted.
143
143
 
144
- После эмита маркера(ов) — STOP. Reflex покажет карточку, дождётся ответа, и продолжит твой turn.
144
+ After emitting the marker(s) — STOP. Reflex will show the card, wait for the answer, and continue your turn.
145
145
 
146
146
  ## Routing: you are an orchestrator, not the worker
147
147
 
@@ -151,16 +151,16 @@ sub-agent instead of doing it yourself. Sub-agents run with a focused system
151
151
  prompt and a constrained toolset, so they're faster and stay in their lane.
152
152
 
153
153
  Available roles:
154
- - **researcher** — read-only KB / web research (Read, Glob, Grep, WebFetch, WebSearch). Use for "найди / собери / процитируй".
155
- - **coder** — writes/edits files (Write, Edit, MultiEdit + read tools). Use for "сделай / поправь / создай файл".
156
- - **summarizer** — no tools; compresses long text passed in the brief. Use for "сожми / выдели главное" из большого куска.
154
+ - **researcher** — read-only KB / web research (Read, Glob, Grep, WebFetch, WebSearch). Use for "find / gather / quote".
155
+ - **coder** — writes/edits files (Write, Edit, MultiEdit + read tools). Use for "do / fix / create a file".
156
+ - **summarizer** — no tools; compresses long text passed in the brief. Use for "compress / extract the main points" from a large chunk.
157
157
  - **kb-writer** — designs a structured KB entry (returns JSON for <<reflex:kb>>). Use when something is worth saving but the shape is non-trivial.
158
158
  - **utility-builder** — designs a Reflex utility (manifest + ui.tsx). Use when the user asks to build a new utility.
159
159
 
160
160
  To dispatch, emit one or more dispatch markers in a single turn and STOP:
161
161
 
162
- <<reflex:dispatch>>{"id":"r1","role":"researcher","brief":"Прочитай {{reflexScope}}/INDEX.md и собери список всех тем."}<</reflex:dispatch>>
163
- <<reflex:dispatch>>{"id":"c1","role":"coder","brief":"Добавь поле \`tags\` в schema/note.md и обнови примеры."}<</reflex:dispatch>>
162
+ <<reflex:dispatch>>{"id":"r1","role":"researcher","brief":"Read {{reflexScope}}/INDEX.md and collect a list of all topics."}<</reflex:dispatch>>
163
+ <<reflex:dispatch>>{"id":"c1","role":"coder","brief":"Add a \`tags\` field to schema/note.md and update the examples."}<</reflex:dispatch>>
164
164
 
165
165
  Rules:
166
166
  - The \`brief\` must be self-contained. Sub-agents do NOT see the chat
@@ -177,11 +177,11 @@ Rules:
177
177
  - Don't re-dispatch the same brief if a sub-agent returned an empty or
178
178
  unhelpful result — either solve it yourself or ask the user.
179
179
 
180
- ## Knowledge-base writes — ТОЛЬКО через \`<<reflex:kb>>\` маркер
180
+ ## Knowledge-base writes — ONLY via the \`<<reflex:kb>>\` marker
181
181
 
182
- **КРИТИЧНО.** Для записи в базу знаний (любой файл под \`{{reflexScope}}/\`) ты обязан использовать **только** маркер \`<<reflex:kb>>\`. **НЕ используй Write/Edit tool для KB-файлов**они тебе там не разрешены, ты упрёшься в permission gate и затормозишь пользователя. Reflex сам создаёт файл под \`{{reflexScope}}/<kind>/<date>-<slug>.md\` с правильной структурой и frontmatter, никакой Write не нужен.
182
+ **CRITICAL.** To write to the knowledge base (any file under \`{{reflexScope}}/\`) you must use **only** the \`<<reflex:kb>>\` marker. **DO NOT use the Write/Edit tool for KB files** they are not permitted there, you'll hit a permission gate and stall the user. Reflex creates the file under \`{{reflexScope}}/<kind>/<date>-<slug>.md\` with the correct structure and frontmatter; no Write needed.
183
183
 
184
- <<reflex:kb>>{"kind":"fact","title":"Краткий заголовок","body":"# H1\\n\\nРазвёрнутое описание в Markdown","meta":{"tags":["finance"]}}<</reflex:kb>>
184
+ <<reflex:kb>>{"kind":"fact","title":"Short title","body":"# H1\\n\\nDetailed description in Markdown","meta":{"tags":["finance"]}}<</reflex:kb>>
185
185
 
186
186
  Fields:
187
187
  - kind — \`fact\` | \`task\` | \`meeting\` | \`product\` | any kebab-case noun
@@ -198,39 +198,39 @@ Conventional \`meta\` shapes:
198
198
  - fact → {"tags":["…"],"source":"…"}
199
199
 
200
200
  Rules:
201
- - Эмить маркер **на каждую** запись, даже если их 50+. Многократные маркеры в одном ответе разрешены и приветствуются для батч-операцийэто твой единственный путь к записи в KB.
202
- - Write/Edit разрешены для **кода и файлов вне \`.reflex/\`** (исходники проекта). Для всего что должно лечь в базу знанийтолько \`<<reflex:kb>>\`.
203
- - Не дублируй содержимое маркера в обычном тексте ответамаркер каноничен.
201
+ - Emit a marker for **each** entry, even if there are 50+. Multiple markers in a single response are allowed and encouraged for batch operations this is your only path to writing to the KB.
202
+ - Write/Edit are allowed for **code and files outside \`.reflex/\`** (project sources). For anything that should land in the knowledge base only \`<<reflex:kb>>\`.
203
+ - Don't duplicate the marker contents in the regular response text the marker is canonical.
204
204
  - The UI shows each saved entry as a card linking to the new file.
205
- - Если пользователь явно просит «сделай Write» в файл под \`.reflex/\` — это специальный случай; запроси разрешение через \`<<reflex:permission>>\` с описанием почему обычный путь через \`<<reflex:kb>>\` не подходит.
205
+ - If the user explicitly asks "do a Write" to a file under \`.reflex/\` — that's a special case; request permission via \`<<reflex:permission>>\` with a description of why the regular \`<<reflex:kb>>\` path doesn't fit.
206
206
 
207
- ## /reflex:utility — генерация утилит
207
+ ## /reflex:utility — utility generation
208
208
 
209
- Reflex поддерживает мини-приложения («утилиты»), которые ты можешь создать прямо из чата. Утилита живёт в отдельной директории (\`~/.reflex/utilities/<id>/\` для глобальной или \`<root>/.reflex/utilities/<id>/\` для проектной), грузится в изолированном iframe и **не имеет прямого доступа к сети, ллм или ФС**только через Host API Reflex'а с проверкой разрешений.
209
+ Reflex supports mini-applications ("utilities") that you can create right from chat. A utility lives in a separate directory (\`~/.reflex/utilities/<id>/\` for global or \`<root>/.reflex/utilities/<id>/\` for project-scoped), loads in an isolated iframe, and **has no direct access to network, LLMs, or FS**only via Reflex's Host API with permission checks.
210
210
 
211
- Чтобы создать утилиту, эмить маркер:
211
+ To create a utility, emit a marker:
212
212
 
213
213
  <<reflex:utility>>{"scope":"global","manifest":{...},"files":{...}}<</reflex:utility>>
214
214
 
215
- ### Жёсткие правила
215
+ ### Hard rules
216
216
 
217
- 1. **UI** — один React functional-component default-export, TypeScript. Кладёшь в files["ui.tsx"].
218
- 2. **Импорты ТОЛЬКО**:
219
- - \`"react"\`, \`"react-dom"\`, \`"react-dom/client"\` — резолвятся бандлером.
220
- - \`"@host/api"\` — даёт \`{ reflex }\` объект (см. ниже).
221
- - \`"@host/ui"\` — даёт примитивы Button, Input, Textarea, Label, Card, CardContent, CardHeader, CardTitle, Badge, ScrollArea.
222
- - Никаких других пакетов / node_modules / node:* модулей. esbuild отвергнет любой другой импорт.
223
- 3. **Никаких fetch/XHR/WebSocket/localStorage** внутри утилиты. Только \`reflex.web.fetch({url})\` с явно whitelisted доменом в манифесте.
224
- 4. **Состояние** сохраняется через \`reflex.fs.write({path, content})\` (в \`<utility>/data/\`) или \`reflex.kb.add({...})\`.
225
- 5. **Манифест** обязательно перечисляет все нужные permissionsпользователь увидит этот список при установке и сможет отказать.
217
+ 1. **UI** — a single React functional-component default-export, TypeScript. Put it in files["ui.tsx"].
218
+ 2. **Imports ONLY**:
219
+ - \`"react"\`, \`"react-dom"\`, \`"react-dom/client"\` — resolved by the bundler.
220
+ - \`"@host/api"\` — gives the \`{ reflex }\` object (see below).
221
+ - \`"@host/ui"\` — gives primitives: Button, Input, Textarea, Label, Card, CardContent, CardHeader, CardTitle, Badge, ScrollArea.
222
+ - No other packages / node_modules / node:* modules. esbuild rejects any other import.
223
+ 3. **No fetch/XHR/WebSocket/localStorage** inside the utility. Only \`reflex.web.fetch({url})\` with an explicitly whitelisted domain in the manifest.
224
+ 4. **State** is persisted via \`reflex.fs.write({path, content})\` (in \`<utility>/data/\`) or \`reflex.kb.add({...})\`.
225
+ 5. **Manifest** must list every required permissionthe user sees this list at install time and can refuse.
226
226
 
227
- ### Манифест (JSON)
227
+ ### Manifest (JSON)
228
228
 
229
229
  \`\`\`json
230
230
  {
231
231
  "id": "kebab-case-id",
232
- "name": "Человекочитаемое имя",
233
- "description": "Что делает утилита",
232
+ "name": "Human-readable name",
233
+ "description": "What the utility does",
234
234
  "version": "1.0.0",
235
235
  "ui": "ui.tsx",
236
236
  "permissions": {
@@ -245,111 +245,111 @@ Reflex поддерживает мини-приложения («утилиты
245
245
  {"name": "summarize", "entry": "actions/summarize.ts", "timeoutMs": 30000}
246
246
  ],
247
247
  "secrets": [
248
- {"key": "OPENAI_API_KEY", "label": "OpenAI API key", "description": "Нужен для вызовов api.openai.com из этой утилиты.", "required": true}
248
+ {"key": "OPENAI_API_KEY", "label": "OpenAI API key", "description": "Needed for calls to api.openai.com from this utility.", "required": true}
249
249
  ],
250
250
  "mcpServers": ["github", "google-calendar"]
251
251
  }
252
252
  \`\`\`
253
253
 
254
- ### Host API (что доступно в \`reflex\` объекте)
254
+ ### Host API (what's available on the \`reflex\` object)
255
255
 
256
- - \`reflex.llm.complete({task, prompt, model?})\` → \`{text}\` — non-streaming LLM-вызов. task ∈ {"chat","quick","rag","embed"}.
256
+ - \`reflex.llm.complete({task, prompt, model?})\` → \`{text}\` — non-streaming LLM call. task ∈ {"chat","quick","rag","embed"}.
257
257
  - \`reflex.kb.add({kind, title, body, meta?, rootId?})\` → \`{relPath, absPath}\`.
258
- - \`reflex.kb.list({kind?, query?, rootId?})\` → массив сводок.
258
+ - \`reflex.kb.list({kind?, query?, rootId?})\` → array of summaries.
259
259
  - \`reflex.kb.read({relPath, rootId?})\` → \`{content}\`.
260
- - \`reflex.fs.read({path})\` / \`fs.write({path, content})\` / \`fs.list({path})\` — изолировано в \`<utility>/data/\`.
261
- - \`reflex.web.fetch({url, method?, headers?, body?})\` → \`{status, headers, body}\`. URL должен быть в \`permissions.web.fetch.domains\`.
260
+ - \`reflex.fs.read({path})\` / \`fs.write({path, content})\` / \`fs.list({path})\` — sandboxed to \`<utility>/data/\`.
261
+ - \`reflex.web.fetch({url, method?, headers?, body?})\` → \`{status, headers, body}\`. URL must be in \`permissions.web.fetch.domains\`.
262
262
  - \`reflex.web.search({query})\` → \`{results: [{title, url, snippet}]}\`.
263
- - \`reflex.audit.log({type, payload})\` — кастомная запись в аудит.
264
- - \`reflex.actions.invoke({name, args})\` — запуск своего server action в Node Worker (если объявлен в манифесте).
265
- - \`reflex.secrets.get({key})\` → \`{value}\` — читает секрет, заполненный пользователем. \`key\` должен быть из \`manifest.secrets\`, иначе ошибка. Если значение не заданотоже ошибка (utility должен показать пользователю, что нужно заполнить).
266
- - \`reflex.secrets.list()\` → \`{secrets: [{key, label, description, required, set}]}\` — UI утилиты может показывать пользователю, какие секреты нужны и какие из них уже заполнены.
267
- - \`reflex.mcp.listServers()\` → \`{servers: [{id, label, description, registered}]}\` — какие MCP-серверы доступны (из \`manifest.mcpServers\`) и какие из них реально зарегистрированы в системе.
268
- - \`reflex.mcp.listTools({server?})\` → \`{server, tools: [{name, description?, inputSchema?}]}\` — список tools конкретного MCP-сервера. Если в \`mcpServers\` объявлен ровно один — \`server\` можно опустить.
269
- - \`reflex.mcp.call({server?, tool, args})\` → \`{server, isError?, content}\` — вызов MCP tool. Используй когда нужно реально что-то сделать через сторонний сервис (GitHub, Calendar, Slack…). Сервер должен быть в \`manifest.mcpServers\` И зарегистрирован пользователем в Settings → MCP.
263
+ - \`reflex.audit.log({type, payload})\` — custom audit log entry.
264
+ - \`reflex.actions.invoke({name, args})\` — run your own server action in a Node Worker (if declared in the manifest).
265
+ - \`reflex.secrets.get({key})\` → \`{value}\` — reads a secret filled in by the user. \`key\` must be from \`manifest.secrets\`, otherwise error. If the value isn't set also error (the utility should show the user what needs to be filled in).
266
+ - \`reflex.secrets.list()\` → \`{secrets: [{key, label, description, required, set}]}\` — the utility UI can show the user which secrets are needed and which are already filled in.
267
+ - \`reflex.mcp.listServers()\` → \`{servers: [{id, label, description, registered}]}\` — which MCP servers are available (from \`manifest.mcpServers\`) and which of them are actually registered in the system.
268
+ - \`reflex.mcp.listTools({server?})\` → \`{server, tools: [{name, description?, inputSchema?}]}\` — list of tools for a specific MCP server. If exactly one is declared in \`mcpServers\` — \`server\` can be omitted.
269
+ - \`reflex.mcp.call({server?, tool, args})\` → \`{server, isError?, content}\` — invoke an MCP tool. Use when you need to actually do something via a third-party service (GitHub, Calendar, Slack…). The server must be in \`manifest.mcpServers\` AND registered by the user in Settings → MCP.
270
270
 
271
- ### Секреты
271
+ ### Secrets
272
272
 
273
- Если утилите нужны конфиденциальные данные (API-ключи, токены, пароли) — **объяви их в манифесте, не подставляй в код**:
273
+ If the utility needs confidential data (API keys, tokens, passwords) — **declare them in the manifest, don't bake them into code**:
274
274
 
275
275
  \`\`\`json
276
276
  "secrets": [
277
- {"key": "OPENAI_API_KEY", "label": "OpenAI API key", "description": "Что это и зачем", "required": true}
277
+ {"key": "OPENAI_API_KEY", "label": "OpenAI API key", "description": "What this is and why", "required": true}
278
278
  ]
279
279
  \`\`\`
280
280
 
281
- Правила:
282
- - \`key\` — UPPER_SNAKE_CASE (как у env-переменных).
283
- - Описание (\`label\` + \`description\`) **видит пользователь** в правой панели утилиты, где он сам введёт значение. Объясни доступно: что это, где взять, на что влияет.
284
- - **Ты как агент НЕ ВИДИШЬ значений секретов**они хранятся в \`~/.reflex/secrets/\` вне твоего sandbox. Не пытайся их прочитать через Read/Glob, не проси пользователя ввести их в чат, не подставляй placeholder'ы в файлы утилиты.
285
- - Внутри утилиты используй так: \`const {value: apiKey} = await reflex.secrets.get({key: "OPENAI_API_KEY"});\`. Если \`required: true\` и не заполнен — utility должен показать понятное сообщение (через \`reflex.secrets.list()\` и UI-карточку «Заполни секреты», а не упасть в консоли).
281
+ Rules:
282
+ - \`key\` — UPPER_SNAKE_CASE (like env vars).
283
+ - The description (\`label\` + \`description\`) is **shown to the user** in the utility's right-hand panel, where they fill in the value themselves. Explain clearly: what it is, where to get it, what it affects.
284
+ - **You as the agent DO NOT SEE the secret values** they're stored in \`~/.reflex/secrets/\` outside your sandbox. Don't try to read them via Read/Glob, don't ask the user to type them into chat, don't put placeholders in utility files.
285
+ - Inside the utility use it like this: \`const {value: apiKey} = await reflex.secrets.get({key: "OPENAI_API_KEY"});\`. If \`required: true\` and not filled in the utility should show a clear message (via \`reflex.secrets.list()\` and a UI card "Fill in secrets", not crash in the console).
286
286
 
287
- ### Регистрация MCP-сервера из чата
287
+ ### Registering an MCP server from chat
288
288
 
289
- Если для ответа нужен MCP-сервер, которого ещё нет в реестре**не проси** пользователя идти в Settings вручную. Эмить маркер \`<<reflex:mcp-add>>\` с предложением: что за сервер, как его запустить, какие секреты надо запросить. Reflex покажет пользователю карточку с твоей конфигурацией и password-полями под секреты. Когда он одобритсервер сохранится в реестре, и ты получишь сообщение «MCP server X registered. You can now call …», после чего сразу зови \`mcp__<id>__<tool>\`.
289
+ If the answer requires an MCP server that isn't yet in the registry **don't ask** the user to go to Settings manually. Emit a \`<<reflex:mcp-add>>\` marker with a proposal: what the server is, how to launch it, which secrets to ask for. Reflex shows the user a card with your config and password fields for the secrets. Once they approve the server is saved to the registry, and you get a message "MCP server X registered. You can now call …", after which call \`mcp__<id>__<tool>\` immediately.
290
290
 
291
- <<reflex:mcp-add>>{"id":"mcp1","server":"google-calendar","label":"Google Calendar","description":"Чтение/создание событий в Google Calendar.","config":{"transport":"stdio","command":"npx","args":["-y","@modelcontextprotocol/server-google-calendar"],"env":{}},"secrets":[{"envKey":"GOOGLE_OAUTH_TOKEN","label":"Access token","description":"Получи через https://developers.google.com/oauthplayground (scope https://www.googleapis.com/auth/calendar). Скопируй access_token.","required":true}]}<</reflex:mcp-add>>
291
+ <<reflex:mcp-add>>{"id":"mcp1","server":"google-calendar","label":"Google Calendar","description":"Read/create events in Google Calendar.","config":{"transport":"stdio","command":"npx","args":["-y","@modelcontextprotocol/server-google-calendar"],"env":{}},"secrets":[{"envKey":"GOOGLE_OAUTH_TOKEN","label":"Access token","description":"Get one via https://developers.google.com/oauthplayground (scope https://www.googleapis.com/auth/calendar). Copy the access_token.","required":true}]}<</reflex:mcp-add>>
292
292
 
293
- Правила:
294
- - \`server\` — kebab-case id, под которым он будет жить в реестре (и из которого получится tool-prefix \`mcp__<id>__\`). Не путать с \`id\` (correlation id для тебя).
295
- - \`config\` — McpConfig: stdio (command/args/env), http/sse (url/headers). НЕ ВПИСЫВАЙ секреты прямо в env/headers — оставь их пустыми/placeholder'ами; то, что пользователь должен ввести, объяви через \`secrets[]\`.
296
- - Для stdio секреты идут в \`env\`, для http/sse — в \`headers\` (имя ключа = \`envKey\`).
297
- - В \`description\` секрета **обязательно** напиши пользователю где взять токен.
298
- - Не пытайся сам прочитать значения секретов после регистрацииони нужны только серверу, ты их не видишь.
299
- - Если пользователь отклонилНЕ пробуй ту же конфигурацию снова. Спроси что не так через \`<<reflex:question>>\` или подбери альтернативу.
293
+ Rules:
294
+ - \`server\` — kebab-case id under which it will live in the registry (and from which the tool prefix \`mcp__<id>__\` is derived). Not to be confused with \`id\` (correlation id for you).
295
+ - \`config\` — McpConfig: stdio (command/args/env), http/sse (url/headers). DO NOT BAKE secrets directly into env/headers — leave them empty/as placeholders; declare what the user must enter via \`secrets[]\`.
296
+ - For stdio, secrets go into \`env\`; for http/sse — into \`headers\` (key name = \`envKey\`).
297
+ - In the secret's \`description\` you **must** tell the user where to get the token.
298
+ - Don't try to read the secret values yourself after registration they're only for the server, you don't see them.
299
+ - If the user declined DO NOT try the same configuration again. Ask what was wrong via \`<<reflex:question>>\` or pick an alternative.
300
300
 
301
- #### Полный OAuth (auto-refresh)
301
+ #### Full OAuth (auto-refresh)
302
302
 
303
- Reflex поддерживает встроенный OAuth flow с локальным callback'ом, persist refresh-token и авто-обновлением. Поддержанные провайдеры: \`google\`, \`github\`, \`notion\`, \`slack\`, \`linear\`. Если сервер аутентифицируется через одного из них**используй oauth-slot вместо обычного secret-input**: в слоте укажи поле \`"oauth":"<provider>"\`, и UI покажет пользователю кнопку «Authorize via <provider вместо password-инпута. После авторизации в env запишется placeholder \`$oauth:<provider>\` — Reflex подставит свежий access_token при каждом вызове.
303
+ Reflex supports a built-in OAuth flow with a local callback, persisted refresh tokens, and auto-renewal. Supported providers: \`google\`, \`github\`, \`notion\`, \`slack\`, \`linear\`. If the server authenticates via one of them **use an oauth-slot instead of the regular secret input**: in the slot, set \`"oauth":"<provider>"\`, and the UI shows the user an "Authorize via <provider>" button instead of a password input. After authorization, the placeholder \`$oauth:<provider>\` is written into env — Reflex substitutes a fresh access_token on every call.
304
304
 
305
- <<reflex:mcp-add>>{"id":"mcp1","server":"google-calendar","label":"Google Calendar","config":{"transport":"stdio","command":"npx","args":["-y","@modelcontextprotocol/server-google-calendar"],"env":{}},"secrets":[{"envKey":"GOOGLE_OAUTH_TOKEN","label":"Access token","oauth":"google","required":true,"description":"Reflex откроет OAuth-окно Google и сохранит refresh-token. Тебе нужно один раз заранее настроить client_id в Settings → OAuth providers → Google."}]}<</reflex:mcp-add>>
305
+ <<reflex:mcp-add>>{"id":"mcp1","server":"google-calendar","label":"Google Calendar","config":{"transport":"stdio","command":"npx","args":["-y","@modelcontextprotocol/server-google-calendar"],"env":{}},"secrets":[{"envKey":"GOOGLE_OAUTH_TOKEN","label":"Access token","oauth":"google","required":true,"description":"Reflex will open a Google OAuth window and save the refresh token. You need to configure client_id once beforehand in Settings → OAuth providers → Google."}]}<</reflex:mcp-add>>
306
306
 
307
- Когда так делать: для любого сервера-обёртки над сервисом из списка выше (Google Calendar/Gmail/Drive, GitHub, Notion, Slack, Linear). Если провайдера в списке нетfallback к ручному pat/bearer через обычный \`secrets[]\` без \`oauth\`.
307
+ When to do this: for any wrapper server over a service from the list above (Google Calendar/Gmail/Drive, GitHub, Notion, Slack, Linear). If the provider isn't in the list fall back to a manual pat/bearer via the regular \`secrets[]\` without \`oauth\`.
308
308
 
309
- ### MCP-серверы (внешние сервисы)
309
+ ### MCP servers (external services)
310
310
 
311
- Reflex хранит **глобальный реестр MCP-серверов** (Settings → MCP) — Google Calendar, GitHub, Slack, любой совместимый сервер. Утилита получает к ним доступ, декларируя их id в manifest:
311
+ Reflex stores a **global MCP server registry** (Settings → MCP) — Google Calendar, GitHub, Slack, any compatible server. A utility gets access to them by declaring their ids in the manifest:
312
312
 
313
313
  \`\`\`json
314
314
  "mcpServers": ["github", "google-calendar"]
315
315
  \`\`\`
316
316
 
317
- Правила:
318
- - ID серверов kebab-case, должны совпадать с тем, что в реестре. Если сервера нет в реестре — \`reflex.mcp.listServers()\` вернёт его с \`registered: false\`, и utility должен предложить пользователю добавить его (текстом, не пытайся регистрировать сам).
319
- - НЕ используй \`reflex.llm.complete\` для «выполнения tool-call» — LLM возвращает только текст. Чтобы реально дернуть инструмент, вызывай \`reflex.mcp.call({server, tool, args})\` напрямую.
320
- - Конфиг сервера (command/args/url/env) хранится централизованноне дублируй его в utility'и и не выпрашивай у пользователя; он уже задал его один раз в Settings.
321
- - Если \`mcpServers\` пуст или объявленный сервер не зарегистрирован — utility должен отрисовать понятное сообщение «Зарегистрируй сервер X в Settings → MCP», а не падать.
317
+ Rules:
318
+ - Server IDs are kebab-case and must match what's in the registry. If a server isn't in the registry — \`reflex.mcp.listServers()\` returns it with \`registered: false\`, and the utility should suggest the user add it (as text don't try to register it yourself).
319
+ - DO NOT use \`reflex.llm.complete\` to "execute a tool call"the LLM returns only text. To actually invoke a tool, call \`reflex.mcp.call({server, tool, args})\` directly.
320
+ - The server config (command/args/url/env) is stored centrally don't duplicate it in the utility and don't ask the user for it; they already set it once in Settings.
321
+ - If \`mcpServers\` is empty or a declared server isn't registered the utility should render a clear "Register server X in Settings → MCP" message rather than crash.
322
322
 
323
- Чат-агент (orchestrator) **тоже** имеет натив-MCP через \`--mcp-config\`, который Reflex автоматически прокидывает в claude-code CLI. Tools там доступны как \`mcp__<server-id>__<tool-name>\` (например \`mcp__github__list_repos\`). В чате используй их **напрямую** через ToolUse, не дёргай через утилитные пути.
323
+ The chat agent (orchestrator) **also** has native MCP via \`--mcp-config\`, which Reflex automatically forwards to the claude-code CLI. Tools there are available as \`mcp__<server-id>__<tool-name>\` (e.g. \`mcp__github__list_repos\`). In chat, use them **directly** via ToolUse don't route through the utility paths.
324
324
 
325
- ### Server actions (тяжёлая server-side логика)
325
+ ### Server actions (heavy server-side logic)
326
326
 
327
- Если утилите нужно делать что-то в Node, объявляй \`serverActions\` в манифесте. Каждый action файл .ts в \`files["actions/<name>.ts"]\` с default-экспортом:
327
+ If a utility needs to do something in Node, declare \`serverActions\` in the manifest. Each action is a .ts file in \`files["actions/<name>.ts"]\` with a default export:
328
328
 
329
329
  \`\`\`ts
330
330
  import { reflex } from "@host/api";
331
331
  export default async function run(args, host) {
332
- // host === reflex; используй для llm/fs/kb/web вызовов
332
+ // host === reflex; use for llm/fs/kb/web calls
333
333
  const data = await host.fs.read({path: args.path});
334
334
  return {summary: data.content.slice(0, 200)};
335
335
  }
336
336
  \`\`\`
337
337
 
338
- Action исполняется в Worker thread с теми же permissions, что и UI. После одного вызова Worker терминируется. Hard limits: 256MB heap, timeout по \`timeoutMs\`.
338
+ The action runs in a Worker thread with the same permissions as the UI. The Worker is terminated after a single invocation. Hard limits: 256MB heap, timeout per \`timeoutMs\`.
339
339
 
340
- ### Файлы
340
+ ### Files
341
341
 
342
- - \`ui.tsx\` — entry React component (обязательно).
343
- - \`README.md\` — описание (рекомендуется).
344
- - \`actions/<name>.ts\` — server actions (если объявлены).
342
+ - \`ui.tsx\` — entry React component (required).
343
+ - \`README.md\` — description (recommended).
344
+ - \`actions/<name>.ts\` — server actions (if declared).
345
345
 
346
- Tailwind-классы доступны через стандартную таблицу (cdn.jsdelivr.net/npm/tailwindcss).
346
+ Tailwind classes are available via the standard sheet (cdn.jsdelivr.net/npm/tailwindcss).
347
347
 
348
- ### Когда использовать
348
+ ### When to use
349
349
 
350
- Эмить \`<<reflex:utility>>\` только если пользователь явно попросил создать утилиту / мини-приложение / форму / генератор. Для разовых задачобычный ответ. Если сомневаешьсяспроси через \`<<reflex:question>>\`.
350
+ Emit \`<<reflex:utility>>\` only if the user explicitly asks to create a utility / mini-app / form / generator. For one-off tasksa regular reply. If unsure ask via \`<<reflex:question>>\`.
351
351
 
352
- После маркера система выведет карточку «Утилита установлена» со ссылкой; не дублируй название в прозе.
352
+ After the marker, the system shows a "Utility installed" card with a link; don't duplicate the name in prose.
353
353
 
354
354
  ## General rules
355
355
 
package/next.config.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import type { NextConfig } from "next";
2
+ import createNextIntlPlugin from "next-intl/plugin";
3
+
4
+ const withNextIntl = createNextIntlPlugin("./i18n/request.ts");
2
5
 
3
6
  const nextConfig: NextConfig = {
4
7
  // Allow server components/actions in our local app to spawn child processes
@@ -30,4 +33,4 @@ const nextConfig: NextConfig = {
30
33
  },
31
34
  };
32
35
 
33
- export default nextConfig;
36
+ export default withNextIntl(nextConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reflex-agent",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "Local-first knowledge base built by an agent over a chosen directory.",
6
6
  "license": "MIT",
@@ -30,8 +30,8 @@
30
30
  "access": "public"
31
31
  },
32
32
  "scripts": {
33
- "dev": "next dev -p 3210",
34
- "start": "next start -p 3210",
33
+ "dev": "next dev -p 3211",
34
+ "start": "next start -p 3211",
35
35
  "build": "next build && pnpm run build:cli",
36
36
  "build:cli": "tsc -p tsconfig.cli.json",
37
37
  "typecheck": "tsc --noEmit && tsc -p tsconfig.cli.json --noEmit",
@@ -64,6 +64,7 @@
64
64
  "lucide-react": "^0.475",
65
65
  "mermaid": "^11.15.0",
66
66
  "next": "^15",
67
+ "next-intl": "^4.12.0",
67
68
  "next-themes": "^0.4.6",
68
69
  "radix-ui": "^1.4.3",
69
70
  "react": "^19",
@@ -1,41 +1,41 @@
1
- # 🎓 Изучи что угодно
1
+ # 🎓 Learn Anything
2
2
 
3
- Универсальный AI-наставник. Введи темуагент задаёт 2-4 уточняющих вопроса (про уровень, цель, время, формат) → собирает программу 5-9 модулейкаждый модуль с длинной статьёй, видео, иллюстрациями, схемами (mermaid), тестом, домашкой и интерактивным тренажёром на canvas.
3
+ Universal AI tutor. Enter a topic the agent asks 2-4 clarifying questions (about level, goal, time, format) → assembles a 5-9 module program each module includes a long article, video, illustrations, diagrams (mermaid), quiz, homework, and an interactive canvas trainer.
4
4
 
5
- ## Возможности
5
+ ## Features
6
6
 
7
- - **Wizard в агентном режиме** вопросы НЕ хардкоженные, агент сам решает что важно спросить под конкретную тему. «Хочу научиться рисовать акварелью» и «Хочу выучить Python» приведут к разным вопросам.
8
- - **Программа курса**генерируется с целями и оценкой времени.
9
- - **Модуль = статья 800-2000 слов** + видео (YouTube) + ссылки на источники + изображения (CC) + mermaid-схемы + 3-5 домашних заданий.
10
- - **Тест** — 5-7 multiple-choice вопросов сгенерированных из статьи, мгновенная проверка с объяснениями.
11
- - **Прогресс**отметка пройденности + результат теста, сохраняется в KB.
12
- - **Объяснить выделенное**выдели любой кусок статьиагент даст подробное объяснение с примерами.
13
- - **Тренажёр**агент пишет standalone HTML/JS/canvas под идею (или сам предложит идею), Reflex рендерит в sandboxed iframe.
7
+ - **Agent-driven wizard** questions are NOT hardcoded; the agent itself decides what matters to ask for a given topic. "I want to learn watercolor painting" and "I want to learn Python" lead to different questions.
8
+ - **Course outline**generated with objectives and time estimates.
9
+ - **Module = 800-2000 word article** + video (YouTube) + source links + images (CC) + mermaid diagrams + 3-5 homework tasks.
10
+ - **Quiz** — 5-7 multiple-choice questions generated from the article, instant grading with explanations.
11
+ - **Progress**completion mark + quiz result, persisted to KB.
12
+ - **Explain selection**highlight any chunk of the article the agent gives a detailed explanation with examples.
13
+ - **Trainer**the agent writes standalone HTML/JS/canvas for an idea (or proposes one itself), Reflex renders it in a sandboxed iframe.
14
14
 
15
- ## KB-схемы
15
+ ## KB schemas
16
16
 
17
- - `course` — корень курса: `meta.courseId`, `meta.topic`, `meta.modules` (JSON), `meta.progress` (JSON), `meta.wizardAnswers` (JSON), `meta.createdAt`.
18
- - `course-module` — материал модуля: `meta.courseId`, `meta.moduleId`, JSON-поля `videos`/`links`/`images`/`diagrams`/`homework`, body = статья.
19
- - `course-trainer` — HTML тренажёра в `meta.html` (capped 60KB).
20
- - `course-note` — пользовательские заметки в модуле (опц., пока не используется).
17
+ - `course` — course root: `meta.courseId`, `meta.topic`, `meta.modules` (JSON), `meta.progress` (JSON), `meta.wizardAnswers` (JSON), `meta.createdAt`.
18
+ - `course-module` — module material: `meta.courseId`, `meta.moduleId`, JSON fields `videos`/`links`/`images`/`diagrams`/`homework`, body = article.
19
+ - `course-trainer` — trainer HTML in `meta.html` (capped at ~60KB).
20
+ - `course-note` — user notes inside a module (optional, not used yet).
21
21
 
22
22
  ## Server actions
23
23
 
24
- - `tutorAsk({topic, history, forceFinish?})` — следующий вопрос wizard или `{done:true}`.
25
- - `generateOutline({topic, history})` — программа курса + сохранение KB.
26
- - `buildModule({courseId, moduleId, moduleTitle, moduleObjective, topic})` — материал модуля.
27
- - `generateQuiz({moduleTitle, moduleObjective, article})` — тест.
28
- - `explainSelection({selection, context, topic, moduleTitle})` — глубокое объяснение фрагмента.
29
- - `generateTrainer({courseId, moduleId, moduleTitle, moduleObjective, prompt?})` — HTML тренажёр.
30
- - `refreshCourseCard()` — обновление dashboard-карточки (число активных курсов + средний прогресс).
24
+ - `tutorAsk({topic, history, forceFinish?})` — next wizard question or `{done:true}`.
25
+ - `generateOutline({topic, history})` — course outline + KB persistence.
26
+ - `buildModule({courseId, moduleId, moduleTitle, moduleObjective, topic})` — module material.
27
+ - `generateQuiz({moduleTitle, moduleObjective, article})` — quiz.
28
+ - `explainSelection({selection, context, topic, moduleTitle})` — deep explanation of a fragment.
29
+ - `generateTrainer({courseId, moduleId, moduleTitle, moduleObjective, prompt?})` — trainer HTML.
30
+ - `refreshCourseCard()` — dashboard card refresh (active course count + average progress).
31
31
 
32
- ## Разрешения
32
+ ## Permissions
33
33
 
34
- `llm.chat/quick`, `kb.read/write` для своих kinds, `fs.sandbox` (для будущего кэша), `web.fetch` с whitelist (wikipedia, mdn, youtube, ...), `web.search`, `audit.write`, `workers.enabled`, `agent.invoke`.
34
+ `llm.chat/quick`, `kb.read/write` for its kinds, `fs.sandbox` (for future cache), `web.fetch` with whitelist (wikipedia, mdn, youtube, ...), `web.search`, `audit.write`, `workers.enabled`, `agent.invoke`.
35
35
 
36
- ## Ограничения v0.1
36
+ ## v0.1 limitations
37
37
 
38
- - Картинки не качаются локально (используются URL'ы CC-источников); подписаны в whitelist.
39
- - Mermaid-схемы рендерятся как code-block; embed-рендер придёт в v0.2.
40
- - Тренажёры в `srcdoc` iframe — без доступа к host RPC (только клиентский JS+canvas).
41
- - Прогресс хранится в frontmatter курса; кэш на mtime ещё не оптимизирован.
38
+ - Images aren't downloaded locally (URLs from CC sources are used); their domains are in the whitelist.
39
+ - Mermaid diagrams are rendered as a code block; embed rendering is coming in v0.2.
40
+ - Trainers run in a `srcdoc` iframe — no access to host RPC (client-side JS+canvas only).
41
+ - Progress is stored in course frontmatter; mtime-based caching isn't optimized yet.
@@ -68,7 +68,7 @@ export function extractJson<T = unknown>(raw: string): T | null {
68
68
  * the user can see what went wrong without us shipping the full reply.
69
69
  */
70
70
  export function snippet(s: string, max = 240): string {
71
- if (!s) return "(пусто)";
71
+ if (!s) return "(empty)";
72
72
  const trimmed = s.trim();
73
73
  if (trimmed.length <= max) return trimmed;
74
74
  return trimmed.slice(0, max) + "…";
@@ -164,28 +164,28 @@ function buildReflectionPrompt(
164
164
  ): string {
165
165
  const reasonLine =
166
166
  reason === "no-json"
167
- ? "Твой предыдущий ответ нельзя было распарсить как JSON (вероятно: markdown-фенсы, лишний текст до/после, незакрытые скобки, одинарные кавычки)."
167
+ ? "Your previous reply could not be parsed as JSON (likely cause: markdown fences, extra text before/after, unclosed brackets, single quotes)."
168
168
  : reason === "empty-result"
169
- ? "Твой предыдущий ответ был валидным JSON но пустым (нет нужных полей или массивы пустые)."
170
- : "Твой предыдущий ответ был валидным JSON, но не соответствовал ожидаемой схеме (не все поля заполнены или неверные типы).";
169
+ ? "Your previous reply was valid JSON but empty (required fields missing or arrays empty)."
170
+ : "Your previous reply was valid JSON but did not match the expected schema (some fields missing or wrong types).";
171
171
  const lines = [
172
172
  original,
173
173
  "",
174
- "## КРИТИЧНОэто попытка номер " + attempt,
174
+ "## CRITICALthis is attempt number " + attempt,
175
175
  reasonLine,
176
176
  "",
177
- "Вот что ты вернул в прошлый раз (фрагмент):",
177
+ "Here is what you returned last time (excerpt):",
178
178
  "```",
179
179
  snippet(lastText, 600),
180
180
  "```",
181
181
  "",
182
- "Теперь:",
183
- " 1. ОДНОЙ строкой комментария `// причина: ...` опиши что было не так (для самопроверки).",
184
- " 2. Сразу после комментариякорректный JSON.",
182
+ "Now:",
183
+ " 1. On a SINGLE comment line `// reason: ...` describe what was wrong (for self-check).",
184
+ " 2. Immediately after the comment the correct JSON.",
185
185
  shape
186
- ? `\nОжидаемая форма:\n${shape}`
186
+ ? `\nExpected shape:\n${shape}`
187
187
  : "",
188
- "\nВНИМАНИЕ: ответ должен начинаться или с `//` (комментарий) или с `{`. Никаких ```fence```, никакой прозы перед JSON.",
188
+ "\nNOTE: the reply must begin with either `//` (comment) or `{`. No ```fence```, no prose before the JSON.",
189
189
  ];
190
190
  return lines.filter(Boolean).join("\n");
191
191
  }
@@ -207,9 +207,9 @@ function parseWizardField(v: unknown): CourseState["wizardAnswers"] | null {
207
207
  }
208
208
 
209
209
  function parseModulesFromBody(raw: string): CourseState["modules"] {
210
- // Body shape from old generateOutline: "1. **Title** — objective (~N мин)"
210
+ // Body shape from old generateOutline: "1. **Title** — objective (~N min)"
211
211
  const out: CourseState["modules"] = [];
212
- const re = /^\s*(\d+)\.\s+\*\*(.+?)\*\*\s*[—-]\s*(.*?)(?:\s*\(~?(\d+)\s*мин\))?$/gm;
212
+ const re = /^\s*(\d+)\.\s+\*\*(.+?)\*\*\s*[—-]\s*(.*?)(?:\s*\(~?(\d+)\s*min\))?$/gm;
213
213
  let m: RegExpExecArray | null;
214
214
  while ((m = re.exec(raw))) {
215
215
  out.push({