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.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +116 -103
- package/.next/app-path-routes-manifest.json +10 -10
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +4 -54
- package/.next/react-loadable-manifest.json +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/agents/[agentId]/page.js +3 -3
- package/.next/server/app/agents/[agentId]/page.js.nft.json +1 -1
- package/.next/server/app/agents/[agentId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/[agentId]/respond/route.js +2 -2
- package/.next/server/app/api/agents/[agentId]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/agents/[agentId]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/images/[rootId]/[file]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/oauth/callback/route.js +4 -4
- package/.next/server/app/api/oauth/callback/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/oauth/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/roots/[id]/attachments/route.js +0 -0
- package/.next/server/app/api/roots/[id]/attachments/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/roots/[id]/chat/[topicId]/send/route.js +2 -2
- package/.next/server/app/api/roots/[id]/chat/[topicId]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/roots/[id]/chat/[topicId]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/roots/[id]/chat/[topicId]/stop/route.js +2 -2
- package/.next/server/app/api/roots/[id]/chat/[topicId]/stop/route.js.nft.json +1 -1
- package/.next/server/app/api/roots/[id]/chat/[topicId]/stop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/roots/[id]/chat/[topicId]/stream/route.js +3 -3
- package/.next/server/app/api/roots/[id]/chat/[topicId]/stream/route.js.nft.json +1 -1
- package/.next/server/app/api/roots/[id]/chat/[topicId]/stream/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/roots/[id]/dashboard/route.js +1 -1
- package/.next/server/app/api/roots/[id]/dashboard/route.js.nft.json +1 -1
- package/.next/server/app/api/roots/[id]/dashboard/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/roots/[id]/suggestions/route.js +1 -1
- package/.next/server/app/api/roots/[id]/suggestions/route.js.nft.json +1 -1
- package/.next/server/app/api/roots/[id]/suggestions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/utilities/[scope]/[id]/bundle.js/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/utilities/[scope]/[id]/host/route.js +2 -2
- package/.next/server/app/api/utilities/[scope]/[id]/host/route.js.nft.json +1 -1
- package/.next/server/app/api/utilities/[scope]/[id]/host/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/utilities/[scope]/[id]/host-api.mjs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/utilities/[scope]/[id]/host-ui.mjs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/utilities/[scope]/[id]/iframe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/utilities/[scope]/[id]/style.css/route_client-reference-manifest.js +1 -1
- package/.next/server/app/audit/page.js +2 -2
- package/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/server/app/onboarding/page.js +3 -3
- package/.next/server/app/onboarding/page.js.nft.json +1 -1
- package/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +2 -2
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/roots/[id]/chat/[topicId]/page.js +2 -6
- package/.next/server/app/roots/[id]/chat/[topicId]/page.js.nft.json +1 -1
- package/.next/server/app/roots/[id]/chat/[topicId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/roots/[id]/kb/[...slug]/page.js +2 -6
- package/.next/server/app/roots/[id]/kb/[...slug]/page.js.nft.json +1 -1
- package/.next/server/app/roots/[id]/kb/[...slug]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/roots/[id]/page.js +3 -3
- package/.next/server/app/roots/[id]/page.js.nft.json +1 -1
- package/.next/server/app/roots/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/roots/[id]/workflows/[wfId]/page.js +2 -2
- package/.next/server/app/roots/[id]/workflows/[wfId]/page.js.nft.json +1 -1
- package/.next/server/app/roots/[id]/workflows/[wfId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/roots/[id]/workflows/page.js +2 -2
- package/.next/server/app/roots/[id]/workflows/page.js.nft.json +1 -1
- package/.next/server/app/roots/[id]/workflows/page_client-reference-manifest.js +1 -1
- package/.next/server/app/roots/new/page.js +5 -3
- package/.next/server/app/roots/new/page.js.nft.json +1 -1
- package/.next/server/app/roots/new/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/page.js +6 -6
- package/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/share/[id]/file/page.js +2 -2
- package/.next/server/app/share/[id]/file/page.js.nft.json +1 -1
- package/.next/server/app/share/[id]/file/page_client-reference-manifest.js +1 -1
- package/.next/server/app/share/[id]/page.js +2 -2
- package/.next/server/app/share/[id]/page.js.nft.json +1 -1
- package/.next/server/app/share/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/utilities/[scope]/[id]/page.js +2 -2
- package/.next/server/app/utilities/[scope]/[id]/page.js.nft.json +1 -1
- package/.next/server/app/utilities/[scope]/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/utilities/page.js +2 -17
- package/.next/server/app/utilities/page.js.nft.json +1 -1
- package/.next/server/app/utilities/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +10 -10
- package/.next/server/chunks/1.js +3 -0
- package/.next/server/chunks/1223.js +1 -1
- package/.next/server/chunks/133.js +1 -490
- package/.next/server/chunks/1888.js +1 -1
- package/.next/server/chunks/{9739.js → 1988.js} +13 -9
- package/.next/server/chunks/2192.js +1 -0
- package/.next/server/chunks/2433.js +1 -1
- package/.next/server/chunks/2503.js +1 -1
- package/.next/server/chunks/285.js +471 -0
- package/.next/server/chunks/2995.js +1 -0
- package/.next/server/chunks/3240.js +1 -1
- package/.next/server/chunks/3332.js +1 -1
- package/.next/server/chunks/{2528.js → 3512.js} +2 -2
- package/.next/server/chunks/3657.js +1 -1
- package/.next/server/chunks/4066.js +1 -1
- package/.next/server/chunks/4438.js +1 -0
- package/.next/server/chunks/4553.js +1 -1
- package/.next/server/chunks/4812.js +179 -0
- package/.next/server/chunks/4925.js +1 -1
- package/.next/server/chunks/5319.js +1 -1
- package/.next/server/chunks/569.js +1 -1
- package/.next/server/chunks/6730.js +1 -1
- package/.next/server/chunks/6734.js +1 -0
- package/.next/server/chunks/6909.js +142 -161
- package/.next/server/chunks/7215.js +1 -0
- package/.next/server/chunks/8262.js +1 -1
- package/.next/server/chunks/9098.js +1 -1
- package/.next/server/chunks/94.js +1 -1
- package/.next/server/chunks/9835.js +1 -1
- package/.next/server/chunks/9944.js +1 -0
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages-manifest.json +1 -2
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/1082-326e649fb24d4945.js +1 -0
- package/.next/static/chunks/3736-f4e42d6d38be50b0.js +1 -0
- package/.next/static/chunks/4108.ca0bdf3cbf3c56cc.js +1 -0
- package/.next/static/chunks/6445-99824866a51b582a.js +1 -0
- package/.next/static/chunks/7482-7ef26030a10ce14f.js +1 -0
- package/.next/static/chunks/8944-c4f2406ecd61094f.js +1 -0
- package/.next/static/chunks/9411-af5f758c57741929.js +3 -0
- package/.next/static/chunks/9415-eb6b5d4c2de3a7c0.js +1 -0
- package/.next/static/chunks/app/agents/[agentId]/page-5d6f4cb16b42d02b.js +1 -0
- package/.next/static/chunks/app/layout-85eb1fd21dab0895.js +1 -0
- package/.next/static/chunks/app/onboarding/page-2013bd8124b9162e.js +1 -0
- package/.next/static/chunks/app/page-558a224e13ffb52c.js +1 -0
- package/.next/static/chunks/app/roots/[id]/chat/[topicId]/page-b42f03fd58669d12.js +1 -0
- package/.next/static/chunks/app/roots/[id]/kb/[...slug]/page-7d17b4e6a5231f56.js +1 -0
- package/.next/static/chunks/app/roots/[id]/page-4aab5266f432e37e.js +1 -0
- package/.next/static/chunks/app/roots/[id]/workflows/[wfId]/page-1ee3320bf5744efc.js +1 -0
- package/.next/static/chunks/app/roots/new/page-df8d2c1f0c64c37a.js +1 -0
- package/.next/static/chunks/app/settings/page-fdba798d9e243ad3.js +1 -0
- package/.next/static/chunks/app/share/[id]/page-818a451d05e08d26.js +1 -0
- package/.next/static/chunks/app/utilities/[scope]/[id]/page-2cee09cc2ab9b5e8.js +1 -0
- package/.next/static/chunks/app/utilities/page-44a51522b347f13e.js +1 -0
- package/.next/static/chunks/{webpack-87b4bb79fdc48563.js → webpack-2b0eab4ccdf44f63.js} +1 -1
- package/.next/static/css/4b367c1d0fa99b78.css +1 -0
- package/.next/trace +47 -46
- package/README.md +4 -1
- package/dist/lib/reflex/agents/prompts.js +46 -46
- package/dist/lib/reflex/agents/prompts.js.map +1 -1
- package/dist/lib/reflex/prompts/defaults.js +102 -102
- package/next.config.ts +4 -1
- package/package.json +4 -3
- package/packages/utilities/learn-anything/README.md +29 -29
- package/packages/utilities/learn-anything/actions/_json.ts +11 -11
- package/packages/utilities/learn-anything/actions/_store.ts +2 -2
- package/packages/utilities/learn-anything/actions/buildModule.ts +60 -59
- package/packages/utilities/learn-anything/actions/explainSelection.ts +14 -13
- package/packages/utilities/learn-anything/actions/generateOutline.ts +15 -15
- package/packages/utilities/learn-anything/actions/generateQuiz.ts +8 -8
- package/packages/utilities/learn-anything/actions/generateTrainer.ts +15 -15
- package/packages/utilities/learn-anything/actions/refreshCourseCard.ts +4 -4
- package/packages/utilities/learn-anything/actions/tutorAsk.ts +15 -15
- package/packages/utilities/learn-anything/article-view.tsx +4 -4
- package/packages/utilities/learn-anything/manifest.json +5 -5
- package/packages/utilities/learn-anything/ui.tsx +57 -57
- package/.next/server/app/_not-found.html +0 -1
- package/.next/server/app/_not-found.meta +0 -8
- package/.next/server/app/_not-found.rsc +0 -18
- package/.next/server/app/index.html +0 -1
- package/.next/server/app/index.meta +0 -9
- package/.next/server/app/index.rsc +0 -19
- package/.next/server/chunks/1986.js +0 -1
- package/.next/server/chunks/4065.js +0 -1
- package/.next/server/chunks/6981.js +0 -1
- package/.next/server/chunks/7017.js +0 -3
- package/.next/server/chunks/7800.js +0 -1
- package/.next/server/chunks/8494.js +0 -1
- package/.next/server/chunks/8843.js +0 -1
- package/.next/server/chunks/9328.js +0 -179
- package/.next/server/chunks/9423.js +0 -3
- package/.next/server/chunks/9455.js +0 -1
- package/.next/server/pages/404.html +0 -1
- package/.next/static/chunks/1384-d28fb6c6c058876f.js +0 -1
- package/.next/static/chunks/2718-78fee2c0de782178.js +0 -1
- package/.next/static/chunks/4108.1ef6b5e7679b56ac.js +0 -1
- package/.next/static/chunks/7238-11701befb3ca3e41.js +0 -1
- package/.next/static/chunks/7951-590bf2004d7935b5.js +0 -1
- package/.next/static/chunks/8423-ffded33a21b27360.js +0 -1
- package/.next/static/chunks/9045-731ff0865352dd95.js +0 -1
- package/.next/static/chunks/app/agents/[agentId]/page-0b5c2838354d0eba.js +0 -1
- package/.next/static/chunks/app/layout-4fbf9f91ad45e221.js +0 -1
- package/.next/static/chunks/app/onboarding/page-532b193d1c4b0dee.js +0 -1
- package/.next/static/chunks/app/page-e3ec0990b78ce7c7.js +0 -1
- package/.next/static/chunks/app/roots/[id]/chat/[topicId]/page-11aad9a40def2e0d.js +0 -1
- package/.next/static/chunks/app/roots/[id]/kb/[...slug]/page-d8757a85e873dfa1.js +0 -1
- package/.next/static/chunks/app/roots/[id]/page-76216026efb90ae0.js +0 -1
- package/.next/static/chunks/app/roots/[id]/workflows/[wfId]/page-c4b6e3825f8626f5.js +0 -1
- package/.next/static/chunks/app/roots/new/page-53ea8d2787e79425.js +0 -1
- package/.next/static/chunks/app/settings/page-7ebaf2b62f256538.js +0 -1
- package/.next/static/chunks/app/share/[id]/page-d5bbbb7e454d1375.js +0 -1
- package/.next/static/chunks/app/utilities/[scope]/[id]/page-e2f928a37483d113.js +0 -1
- package/.next/static/chunks/app/utilities/page-839262ff726a52a2.js +0 -1
- package/.next/static/css/87e01f779d555d04.css +0 -1
- /package/.next/static/{vTfQfQnAWV_hFVZjWEYvZ → fhVNqfmJl5Mdfhyhg6orp}/_buildManifest.js +0 -0
- /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.
|
|
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":"
|
|
112
|
+
<<reflex:question>>{"prompt":"Which language for the summary?","choices":["english","russian"]}<</reflex:question>>
|
|
113
113
|
|
|
114
|
-
|
|
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":"
|
|
123
|
-
{"label":"
|
|
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
|
-
|
|
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":"
|
|
132
|
-
{"id":"depth","header":"
|
|
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\` —
|
|
138
|
-
- \`header\` —
|
|
139
|
-
- \`multiSelect\` — \`true\`
|
|
140
|
-
- \`options\` —
|
|
141
|
-
- \`choices\` — legacy
|
|
142
|
-
- \`id\` —
|
|
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
|
-
|
|
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":"
|
|
163
|
-
<<reflex:dispatch>>{"id":"c1","role":"coder","brief":"
|
|
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 —
|
|
180
|
+
## Knowledge-base writes — ONLY via the \`<<reflex:kb>>\` marker
|
|
181
181
|
|
|
182
|
-
|
|
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":"
|
|
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
|
-
-
|
|
202
|
-
- Write/Edit
|
|
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
|
-
-
|
|
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
|
|
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** —
|
|
218
|
-
2.
|
|
219
|
-
- \`"react"\`, \`"react-dom"\`, \`"react-dom/client"\` —
|
|
220
|
-
- \`"@host/api"\` —
|
|
221
|
-
- \`"@host/ui"\` —
|
|
222
|
-
-
|
|
223
|
-
3.
|
|
224
|
-
4.
|
|
225
|
-
5.
|
|
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 permission — the user sees this list at install time and can refuse.
|
|
226
226
|
|
|
227
|
-
###
|
|
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": "
|
|
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 (
|
|
254
|
+
### Host API (what's available on the \`reflex\` object)
|
|
255
255
|
|
|
256
|
-
- \`reflex.llm.complete({task, prompt, model?})\` → \`{text}\` — non-streaming LLM
|
|
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})\` —
|
|
261
|
-
- \`reflex.web.fetch({url, method?, headers?, body?})\` → \`{status, headers, body}\`. URL
|
|
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})\` —
|
|
265
|
-
- \`reflex.secrets.get({key})\` → \`{value}\` —
|
|
266
|
-
- \`reflex.secrets.list()\` → \`{secrets: [{key, label, description, required, set}]}\` — UI
|
|
267
|
-
- \`reflex.mcp.listServers()\` → \`{servers: [{id, label, description, registered}]}\` —
|
|
268
|
-
- \`reflex.mcp.listTools({server?})\` → \`{server, tools: [{name, description?, inputSchema?}]}\` —
|
|
269
|
-
- \`reflex.mcp.call({server?, tool, args})\` → \`{server, isError?, content}\` —
|
|
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
|
-
|
|
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": "
|
|
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 (
|
|
283
|
-
-
|
|
284
|
-
-
|
|
285
|
-
-
|
|
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
|
-
###
|
|
287
|
+
### Registering an MCP server from chat
|
|
288
288
|
|
|
289
|
-
|
|
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":"
|
|
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
|
|
295
|
-
- \`config\` — McpConfig: stdio (command/args/env), http/sse (url/headers).
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
-
|
|
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
|
-
####
|
|
301
|
+
#### Full OAuth (auto-refresh)
|
|
302
302
|
|
|
303
|
-
Reflex
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
-
|
|
319
|
-
-
|
|
320
|
-
-
|
|
321
|
-
-
|
|
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
|
-
|
|
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 (
|
|
325
|
+
### Server actions (heavy server-side logic)
|
|
326
326
|
|
|
327
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
|
346
|
+
Tailwind classes are available via the standard sheet (cdn.jsdelivr.net/npm/tailwindcss).
|
|
347
347
|
|
|
348
|
-
###
|
|
348
|
+
### When to use
|
|
349
349
|
|
|
350
|
-
|
|
350
|
+
Emit \`<<reflex:utility>>\` only if the user explicitly asks to create a utility / mini-app / form / generator. For one-off tasks — a 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.
|
|
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
|
|
34
|
-
"start": "next start -p
|
|
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
|
-
|
|
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
|
-
- **
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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` —
|
|
18
|
-
- `course-module` —
|
|
19
|
-
- `course-trainer` — HTML
|
|
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?})` —
|
|
25
|
-
- `generateOutline({topic, history})` —
|
|
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()` —
|
|
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`
|
|
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
|
-
##
|
|
36
|
+
## v0.1 limitations
|
|
37
37
|
|
|
38
|
-
-
|
|
39
|
-
- Mermaid
|
|
40
|
-
-
|
|
41
|
-
-
|
|
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
|
-
? "
|
|
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
|
-
? "
|
|
170
|
-
: "
|
|
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
|
-
"##
|
|
174
|
+
"## CRITICAL — this 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.
|
|
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
|
-
? `\
|
|
186
|
+
? `\nExpected shape:\n${shape}`
|
|
187
187
|
: "",
|
|
188
|
-
"\
|
|
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
|
|
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({
|