cotutor 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +249 -21
- package/NOTICE +42 -0
- package/README.md +115 -20
- package/avatars/chinese.webp +0 -0
- package/avatars/english.webp +0 -0
- package/avatars/math.webp +0 -0
- package/bin/cotutor.js +15 -5
- package/cards/canvas/card.css +8 -0
- package/cards/canvas/card.md +68 -0
- package/cards/choice/card.css +18 -0
- package/cards/choice/card.md +55 -0
- package/cards/code/card.css +5 -0
- package/cards/code/card.md +46 -0
- package/cards/dictation/card.css +10 -0
- package/cards/dictation/card.md +97 -0
- package/cards/image/card.css +10 -0
- package/cards/image/card.md +52 -0
- package/cards/lecture/card.css +9 -0
- package/cards/lecture/card.md +69 -0
- package/cards/paint/card.css +20 -0
- package/cards/paint/card.md +77 -0
- package/cards/photo/card.css +22 -0
- package/cards/photo/card.md +61 -0
- package/cards/read/card.css +6 -0
- package/cards/read/card.md +51 -0
- package/cards/text/card.css +3 -0
- package/cards/text/card.md +69 -0
- package/cards/tianzige/card.css +17 -0
- package/cards/tianzige/card.md +78 -0
- package/cards/tutor/card.css +79 -0
- package/cards/tutor/card.md +149 -0
- package/cards/word/card.css +24 -0
- package/cards/word/card.md +91 -0
- package/cards//346/235/277/344/271/246/346/200/216/344/271/210/345/206/231.md +159 -0
- package/dist/app/activity-eval-rules.d.ts +62 -0
- package/dist/app/activity-eval-rules.js +276 -0
- package/dist/app/activity-eval.d.ts +65 -0
- package/dist/app/activity-eval.js +159 -0
- package/dist/app/build.d.ts +11 -0
- package/dist/app/build.js +45 -0
- package/dist/app/cli/activities.d.ts +70 -0
- package/dist/app/cli/activities.js +196 -0
- package/dist/app/cli/cert.d.ts +12 -0
- package/dist/app/cli/cert.js +49 -0
- package/dist/app/cli/chalk/bake.d.ts +10 -0
- package/dist/app/cli/chalk/bake.js +58 -0
- package/dist/app/cli/chalk/browser.d.ts +16 -0
- package/dist/app/cli/chalk/browser.js +157 -0
- package/dist/app/cli/chalk/build.d.ts +14 -0
- package/dist/app/cli/chalk/build.js +100 -0
- package/dist/app/cli/chalk/dub.d.ts +51 -0
- package/dist/app/cli/chalk/dub.js +106 -0
- package/dist/app/cli/chalk/load-scene.d.ts +34 -0
- package/dist/app/cli/chalk/load-scene.js +117 -0
- package/dist/app/cli/chalk/main.d.ts +7 -0
- package/dist/app/cli/chalk/main.js +199 -0
- package/dist/app/cli/chalk/serve.d.ts +4 -0
- package/dist/app/cli/chalk/serve.js +28 -0
- package/dist/app/cli/chalk/snap.d.ts +65 -0
- package/dist/app/cli/chalk/snap.js +103 -0
- package/dist/app/cli/chalk/typecheck.d.ts +7 -0
- package/dist/app/cli/chalk/typecheck.js +51 -0
- package/dist/app/cli/doctor.d.ts +24 -0
- package/dist/app/cli/doctor.js +553 -0
- package/dist/app/cli/firewall.d.ts +24 -0
- package/dist/app/cli/firewall.js +79 -0
- package/dist/app/cli/grades.d.ts +14 -0
- package/dist/app/cli/grades.js +44 -0
- package/dist/{cli → app/cli}/init.d.ts +3 -1
- package/dist/app/cli/init.js +158 -0
- package/dist/app/cli/main.js +1038 -0
- package/dist/app/cli/migrate.d.ts +49 -0
- package/dist/app/cli/migrate.js +195 -0
- package/dist/app/cli/port-holder.d.ts +16 -0
- package/dist/app/cli/port-holder.js +34 -0
- package/dist/app/cli/port.d.ts +37 -0
- package/dist/app/cli/port.js +116 -0
- package/dist/app/cli/sandbox.d.ts +25 -0
- package/dist/app/cli/sandbox.js +166 -0
- package/dist/app/cli/serve-log.d.ts +13 -0
- package/dist/app/cli/serve-log.js +90 -0
- package/dist/app/cli/serve.d.ts +51 -0
- package/dist/app/cli/serve.js +168 -0
- package/dist/app/cli/service-windows.d.ts +50 -0
- package/dist/app/cli/service-windows.js +117 -0
- package/dist/app/cli/service.d.ts +138 -0
- package/dist/app/cli/service.js +429 -0
- package/dist/app/cli/shipped.d.ts +30 -0
- package/dist/app/cli/shipped.js +47 -0
- package/dist/app/cli/skeleton.d.ts +38 -0
- package/dist/app/cli/skeleton.js +161 -0
- package/dist/app/cli/skills.d.ts +59 -0
- package/dist/app/cli/skills.js +199 -0
- package/dist/app/cli/themes.d.ts +52 -0
- package/dist/app/cli/themes.js +148 -0
- package/dist/app/cli/tray-icons.d.ts +11 -0
- package/dist/app/cli/tray-icons.js +87 -0
- package/dist/app/cli/tray-ids.d.ts +6 -0
- package/dist/app/cli/tray-ids.js +6 -0
- package/dist/app/cli/tray-mac.d.ts +8 -0
- package/dist/app/cli/tray-mac.js +140 -0
- package/dist/app/cli/tray-run.d.ts +1 -0
- package/dist/app/cli/tray-run.js +33 -0
- package/dist/app/cli/tray-windows.d.ts +5 -0
- package/dist/app/cli/tray-windows.js +166 -0
- package/dist/app/cli/tray.d.ts +205 -0
- package/dist/app/cli/tray.js +618 -0
- package/dist/{cli → app/cli}/tutors.d.ts +15 -26
- package/dist/{cli → app/cli}/tutors.js +86 -110
- package/dist/app/cli/usage.d.ts +2 -0
- package/dist/app/cli/usage.js +35 -0
- package/dist/app/dictation/recognize.d.ts +18 -0
- package/dist/app/dictation/recognize.js +6 -0
- package/dist/app/dictation/route.d.ts +52 -0
- package/dist/app/dictation/route.js +511 -0
- package/dist/app/diff.d.ts +11 -0
- package/dist/app/diff.js +71 -0
- package/dist/app/http/access.d.ts +62 -0
- package/dist/app/http/access.js +180 -0
- package/dist/app/http/app.d.ts +241 -0
- package/dist/app/http/app.js +1159 -0
- package/dist/app/http/avatars.d.ts +17 -0
- package/dist/app/http/avatars.js +35 -0
- package/dist/app/http/dev-activities.d.ts +115 -0
- package/dist/app/http/dev-activities.js +193 -0
- package/dist/app/http/dev-overview.d.ts +108 -0
- package/dist/app/http/dev-overview.js +209 -0
- package/dist/app/http/dev-tutor-files.d.ts +80 -0
- package/dist/app/http/dev-tutor-files.js +151 -0
- package/dist/app/http/parent-home.d.ts +51 -0
- package/dist/app/http/parent-home.js +266 -0
- package/dist/app/http/parent-look.d.ts +2 -0
- package/dist/app/http/parent-look.js +151 -0
- package/dist/app/http/range.d.ts +9 -0
- package/dist/app/http/range.js +19 -0
- package/dist/app/http/raw-view.d.ts +117 -0
- package/dist/app/http/raw-view.js +149 -0
- package/dist/app/http/send-file.d.ts +7 -0
- package/dist/app/http/send-file.js +29 -0
- package/dist/{server → app/http}/stage.d.ts +9 -0
- package/dist/{server → app/http}/stage.js +34 -6
- package/dist/app/http/theme.d.ts +13 -0
- package/dist/app/http/theme.js +41 -0
- package/dist/app/http/voice-preview.d.ts +4 -0
- package/dist/app/http/voice-preview.js +34 -0
- package/dist/app/machine/bailian.d.ts +26 -0
- package/dist/app/machine/bailian.js +87 -0
- package/dist/app/mock/mock-live.d.ts +16 -0
- package/dist/app/mock/mock-live.js +140 -0
- package/dist/{server → app/mock}/mock.d.ts +38 -5
- package/dist/app/mock/mock.js +1125 -0
- package/dist/app/replay.d.ts +94 -0
- package/dist/app/replay.js +170 -0
- package/dist/app/skills/analyze-doc.d.ts +3 -0
- package/dist/app/skills/analyze-doc.js +47 -0
- package/dist/app/skills/home-doc.d.ts +5 -0
- package/dist/app/skills/home-doc.js +112 -0
- package/dist/app/skills/tune-doc.d.ts +3 -0
- package/dist/app/skills/tune-doc.js +80 -0
- package/dist/app/talk/list.d.ts +11 -0
- package/dist/app/talk/list.js +9 -0
- package/dist/app/talk/route.d.ts +16 -0
- package/dist/app/talk/route.js +142 -0
- package/dist/app/workspace.d.ts +63 -0
- package/dist/{cli → app}/workspace.js +100 -52
- package/dist/app/ws/route.d.ts +31 -0
- package/dist/app/ws/route.js +33 -0
- package/dist/app/ws/ws.d.ts +31 -0
- package/dist/app/ws/ws.js +169 -0
- package/dist/board/canvas-chat.d.ts +97 -0
- package/dist/board/canvas-chat.js +175 -0
- package/dist/board/card-guards.d.ts +15 -0
- package/dist/board/card-guards.js +38 -0
- package/dist/board/cards/canvas.d.ts +41 -0
- package/dist/board/cards/canvas.js +83 -0
- package/dist/{cards → board/cards}/choice.js +0 -12
- package/dist/{cards → board/cards}/code.js +0 -3
- package/dist/board/cards/dictation.d.ts +42 -0
- package/dist/board/cards/dictation.js +105 -0
- package/dist/board/cards/docs.d.ts +47 -0
- package/dist/board/cards/docs.js +152 -0
- package/dist/{cards → board/cards}/image.js +1 -10
- package/dist/board/cards/index.d.ts +62 -0
- package/dist/board/cards/index.js +144 -0
- package/dist/{cards → board/cards}/kind.d.ts +10 -4
- package/dist/board/cards/kind.js +9 -0
- package/dist/board/cards/lecture.d.ts +26 -0
- package/dist/board/cards/lecture.js +62 -0
- package/dist/board/cards/paint.d.ts +38 -0
- package/dist/board/cards/paint.js +85 -0
- package/dist/board/cards/photo.d.ts +15 -0
- package/dist/board/cards/photo.js +29 -0
- package/dist/{cards → board/cards}/read.js +1 -10
- package/dist/board/cards/text.d.ts +20 -0
- package/dist/board/cards/text.js +59 -0
- package/dist/board/cards/tianzige.d.ts +16 -0
- package/dist/board/cards/tianzige.js +64 -0
- package/dist/board/cards/tutor.d.ts +72 -0
- package/dist/board/cards/tutor.js +134 -0
- package/dist/board/cards/word.d.ts +20 -0
- package/dist/board/cards/word.js +76 -0
- package/dist/board/lecture.d.ts +100 -0
- package/dist/board/lecture.js +204 -0
- package/dist/board/paper.d.ts +20 -0
- package/dist/board/paper.js +76 -0
- package/dist/board/parse.d.ts +118 -0
- package/dist/board/parse.js +324 -0
- package/dist/board/presentation.d.ts +289 -0
- package/dist/board/presentation.js +613 -0
- package/dist/board/sections.d.ts +16 -0
- package/dist/board/sections.js +130 -0
- package/dist/brain/api-tools.d.ts +8 -0
- package/dist/brain/api-tools.js +191 -0
- package/dist/brain/api.d.ts +57 -0
- package/dist/brain/api.js +394 -0
- package/dist/brain/index.d.ts +97 -0
- package/dist/brain/index.js +11 -0
- package/dist/brain/log.d.ts +5 -0
- package/dist/brain/log.js +22 -0
- package/dist/brain/runtime.d.ts +30 -0
- package/dist/brain/runtime.js +23 -0
- package/dist/brain/stream-json.d.ts +16 -0
- package/dist/brain/stream-json.js +82 -0
- package/dist/brain/stream.d.ts +20 -0
- package/dist/brain/stream.js +70 -0
- package/dist/brain/transcript.d.ts +45 -0
- package/dist/brain/transcript.js +132 -0
- package/dist/brain/types.d.ts +99 -0
- package/dist/brain/types.js +14 -0
- package/dist/chalk/actions.d.ts +37 -0
- package/dist/chalk/actions.js +69 -0
- package/dist/chalk/bundle.d.ts +26 -0
- package/dist/chalk/bundle.js +69 -0
- package/dist/chalk/check.d.ts +28 -0
- package/dist/chalk/check.js +291 -0
- package/dist/chalk/circle.d.ts +64 -0
- package/dist/chalk/circle.js +193 -0
- package/dist/chalk/clock.d.ts +84 -0
- package/dist/chalk/clock.js +84 -0
- package/dist/chalk/geometry.d.ts +44 -0
- package/dist/chalk/geometry.js +139 -0
- package/dist/chalk/glyphs/capitals.d.ts +12 -0
- package/dist/chalk/glyphs/capitals.js +264 -0
- package/dist/chalk/glyphs/index.d.ts +21 -0
- package/dist/chalk/glyphs/index.js +18 -0
- package/dist/chalk/glyphs/letters.d.ts +13 -0
- package/dist/chalk/glyphs/letters.js +280 -0
- package/dist/chalk/glyphs/metrics.d.ts +33 -0
- package/dist/chalk/glyphs/metrics.js +28 -0
- package/dist/chalk/glyphs/registry.d.ts +7 -0
- package/dist/chalk/glyphs/registry.js +8 -0
- package/dist/chalk/glyphs/sample.d.ts +32 -0
- package/dist/chalk/glyphs/sample.js +102 -0
- package/dist/chalk/glyphs/skeleton.d.ts +64 -0
- package/dist/chalk/glyphs/skeleton.js +126 -0
- package/dist/chalk/glyphs/types.d.ts +58 -0
- package/dist/chalk/glyphs/types.js +17 -0
- package/dist/chalk/index.d.ts +23 -0
- package/dist/chalk/index.js +15 -0
- package/dist/chalk/manifest.d.ts +41 -0
- package/dist/chalk/manifest.js +6 -0
- package/dist/chalk/paint.d.ts +180 -0
- package/dist/chalk/paint.js +210 -0
- package/dist/chalk/pointer.d.ts +8 -0
- package/dist/chalk/pointer.js +21 -0
- package/dist/chalk/prepare.d.ts +11 -0
- package/dist/chalk/prepare.js +15 -0
- package/dist/chalk/scene-schema.d.ts +199 -0
- package/dist/chalk/scene-schema.js +91 -0
- package/dist/chalk/seed.d.ts +3 -0
- package/dist/chalk/seed.js +10 -0
- package/dist/chalk/text-metrics.d.ts +8 -0
- package/dist/chalk/text-metrics.js +156 -0
- package/dist/chalk/timeline.d.ts +47 -0
- package/dist/chalk/timeline.js +84 -0
- package/dist/chalk/types.d.ts +150 -0
- package/dist/chalk/types.js +8 -0
- package/dist/lib/index.d.ts +14 -12
- package/dist/lib/index.js +14 -12
- package/dist/media/draw.d.ts +23 -0
- package/dist/media/draw.js +44 -0
- package/dist/media/mp3.d.ts +6 -0
- package/dist/media/mp3.js +58 -0
- package/dist/media/mp4.d.ts +1 -0
- package/dist/media/mp4.js +58 -0
- package/dist/media/tts-voices.d.ts +18 -0
- package/dist/media/tts-voices.js +606 -0
- package/dist/media/tts.d.ts +56 -0
- package/dist/media/tts.js +174 -0
- package/dist/media/vision.d.ts +25 -0
- package/dist/media/vision.js +71 -0
- package/dist/memory/commit.d.ts +67 -0
- package/dist/memory/commit.js +130 -0
- package/dist/memory/context.d.ts +86 -0
- package/dist/memory/context.js +97 -0
- package/dist/memory/diary.d.ts +84 -0
- package/dist/memory/diary.js +183 -0
- package/dist/memory/grade.d.ts +2 -0
- package/dist/memory/grade.js +7 -0
- package/dist/{lib → memory}/plan.d.ts +1 -1
- package/dist/{lib → memory}/plan.js +1 -1
- package/dist/memory/proposals.d.ts +11 -0
- package/dist/memory/proposals.js +43 -0
- package/dist/memory/retrieval.d.ts +44 -0
- package/dist/memory/retrieval.js +157 -0
- package/dist/{lib → memory}/timetable.d.ts +0 -3
- package/dist/{lib → memory}/timetable.js +0 -11
- package/dist/memory/vault-notes.d.ts +165 -0
- package/dist/memory/vault-notes.js +359 -0
- package/dist/memory/vault.d.ts +70 -0
- package/dist/memory/vault.js +169 -0
- package/dist/realtime/bailian.d.ts +120 -0
- package/dist/realtime/bailian.js +289 -0
- package/dist/realtime/cues.d.ts +51 -0
- package/dist/realtime/cues.js +94 -0
- package/dist/realtime/listen.d.ts +32 -0
- package/dist/realtime/listen.js +53 -0
- package/dist/realtime/preview.d.ts +23 -0
- package/dist/realtime/preview.js +38 -0
- package/dist/realtime/prompt.d.ts +8 -0
- package/dist/realtime/prompt.js +13 -0
- package/dist/realtime/voices.d.ts +21 -0
- package/dist/realtime/voices.js +71 -0
- package/dist/runtime/activities.d.ts +29 -0
- package/dist/runtime/activities.js +72 -0
- package/dist/runtime/activity-state.d.ts +22 -0
- package/dist/runtime/activity-state.js +83 -0
- package/dist/runtime/activity.d.ts +63 -0
- package/dist/runtime/activity.js +428 -0
- package/dist/runtime/agent-file.d.ts +24 -0
- package/dist/runtime/agent-file.js +59 -0
- package/dist/runtime/auto-bookkeep.d.ts +30 -0
- package/dist/runtime/auto-bookkeep.js +84 -0
- package/dist/{lib → runtime/context}/context-pack.d.ts +3 -3
- package/dist/runtime/context/context-pack.js +182 -0
- package/dist/runtime/context/thread-digest.d.ts +4 -0
- package/dist/runtime/context/thread-digest.js +35 -0
- package/dist/runtime/context/tutor-rules.d.ts +25 -0
- package/dist/runtime/context/tutor-rules.js +47 -0
- package/dist/runtime/delivery/delivery.d.ts +94 -0
- package/dist/runtime/delivery/delivery.js +145 -0
- package/dist/runtime/delivery/playback.d.ts +25 -0
- package/dist/runtime/delivery/playback.js +29 -0
- package/dist/runtime/delivery/player.d.ts +145 -0
- package/dist/runtime/delivery/player.js +221 -0
- package/dist/runtime/delivery/queue.d.ts +89 -0
- package/dist/runtime/delivery/queue.js +59 -0
- package/dist/runtime/home/edit.d.ts +39 -0
- package/dist/runtime/home/edit.js +132 -0
- package/dist/runtime/home/home.d.ts +191 -0
- package/dist/runtime/home/home.js +318 -0
- package/dist/runtime/home/store.d.ts +142 -0
- package/dist/runtime/home/store.js +455 -0
- package/dist/runtime/journal/conversation.d.ts +46 -0
- package/dist/runtime/journal/conversation.js +89 -0
- package/dist/runtime/journal/events.d.ts +27 -0
- package/dist/runtime/journal/events.js +136 -0
- package/dist/runtime/journal/kid-log.d.ts +105 -0
- package/dist/runtime/journal/kid-log.js +133 -0
- package/dist/runtime/journal/ledger.d.ts +17 -0
- package/dist/{lib → runtime/journal}/ledger.js +7 -40
- package/dist/runtime/journal/mix.d.ts +42 -0
- package/dist/runtime/journal/mix.js +129 -0
- package/dist/runtime/journal/mouth-index.d.ts +26 -0
- package/dist/runtime/journal/mouth-index.js +34 -0
- package/dist/runtime/journal/mouth.d.ts +54 -0
- package/dist/runtime/journal/mouth.js +102 -0
- package/dist/runtime/journal/recorder.d.ts +91 -0
- package/dist/runtime/journal/recorder.js +148 -0
- package/dist/runtime/journal/store.d.ts +123 -0
- package/dist/runtime/journal/store.js +316 -0
- package/dist/runtime/kid-input.d.ts +16 -0
- package/dist/runtime/kid-input.js +194 -0
- package/dist/runtime/kid-view.d.ts +124 -0
- package/dist/runtime/kid-view.js +157 -0
- package/dist/runtime/lecture.d.ts +86 -0
- package/dist/runtime/lecture.js +237 -0
- package/dist/runtime/live/call.d.ts +231 -0
- package/dist/runtime/live/call.js +1244 -0
- package/dist/runtime/live/turns.d.ts +48 -0
- package/dist/runtime/live/turns.js +102 -0
- package/dist/runtime/runner.d.ts +243 -0
- package/dist/runtime/runner.js +777 -0
- package/dist/runtime/talk-run.d.ts +21 -0
- package/dist/runtime/talk-run.js +34 -0
- package/dist/scenes/dictation/match.d.ts +57 -0
- package/dist/scenes/dictation/match.js +255 -0
- package/dist/scenes/dictation/recognize.d.ts +86 -0
- package/dist/scenes/dictation/recognize.js +163 -0
- package/dist/scenes/dictation/store.d.ts +112 -0
- package/dist/scenes/dictation/store.js +99 -0
- package/dist/scenes/look.d.ts +26 -0
- package/dist/scenes/look.js +1 -0
- package/dist/scenes/paint/painter.d.ts +84 -0
- package/dist/scenes/paint/painter.js +171 -0
- package/dist/scenes/paint/prompt.d.ts +23 -0
- package/dist/scenes/paint/prompt.js +36 -0
- package/dist/scenes/talk/en-words.d.ts +8 -0
- package/dist/scenes/talk/en-words.js +44 -0
- package/dist/scenes/talk/list.d.ts +44 -0
- package/dist/scenes/talk/list.js +88 -0
- package/dist/scenes/talk/store.d.ts +27 -0
- package/dist/scenes/talk/store.js +61 -0
- package/dist/scenes/talk/warmup.d.ts +16 -0
- package/dist/scenes/talk/warmup.js +39 -0
- package/dist/schema/activity.d.ts +149 -0
- package/dist/schema/activity.js +113 -0
- package/dist/schema/board.d.ts +143 -2
- package/dist/schema/board.js +15 -3
- package/dist/schema/config.d.ts +339 -103
- package/dist/schema/config.js +269 -84
- package/dist/schema/context-pack.d.ts +76 -14
- package/dist/schema/context-pack.js +91 -10
- package/dist/{lib/conversation.d.ts → schema/conversation-files.d.ts} +11 -33
- package/dist/schema/conversation-files.js +26 -0
- package/dist/schema/conversation.d.ts +391 -49
- package/dist/schema/conversation.js +135 -19
- package/dist/schema/events.d.ts +138 -0
- package/dist/schema/events.js +1 -0
- package/dist/schema/home.d.ts +56 -0
- package/dist/schema/home.js +35 -0
- package/dist/schema/index.d.ts +14 -3
- package/dist/schema/index.js +14 -3
- package/dist/schema/kid-device.d.ts +20 -0
- package/dist/schema/kid-device.js +32 -0
- package/dist/schema/ledger.d.ts +8 -63
- package/dist/schema/ledger.js +7 -31
- package/dist/schema/memory.d.ts +73 -0
- package/dist/schema/memory.js +47 -0
- package/dist/schema/plan.d.ts +2 -2
- package/dist/schema/plan.js +2 -2
- package/dist/schema/sections.d.ts +32 -25
- package/dist/schema/sections.js +21 -22
- package/dist/schema/theme.d.ts +25 -0
- package/dist/schema/theme.js +26 -0
- package/dist/schema/timetable.d.ts +2 -2
- package/dist/schema/timetable.js +2 -2
- package/dist/schema/workspace.d.ts +68 -0
- package/dist/schema/workspace.js +37 -0
- package/dist/shared/default-port.d.ts +2 -0
- package/dist/shared/default-port.js +2 -0
- package/dist/shared/errors.d.ts +8 -0
- package/dist/shared/errors.js +13 -0
- package/dist/shared/home.d.ts +2 -0
- package/dist/shared/home.js +16 -0
- package/dist/shared/human-notes.d.ts +5 -0
- package/dist/shared/human-notes.js +7 -0
- package/dist/shared/qianwen.d.ts +54 -0
- package/dist/shared/qianwen.js +113 -0
- package/dist/shared/site-url.d.ts +2 -0
- package/dist/shared/site-url.js +5 -0
- package/dist/shared/time.d.ts +9 -0
- package/dist/shared/time.js +22 -0
- package/dist/shared/tokens.d.ts +18 -0
- package/dist/shared/tokens.js +11 -0
- package/dist/shared/wav.d.ts +10 -0
- package/dist/shared/wav.js +50 -0
- package/dist/stage/chunk-226TNLQA.js +129 -0
- package/dist/stage/chunk-24LGQEKE.js +1 -0
- package/dist/stage/chunk-25XV6UY3.js +10 -0
- package/dist/stage/chunk-2AZI3NHJ.js +1 -0
- package/dist/stage/chunk-2BQZTTBE.js +1 -0
- package/dist/stage/chunk-2M7CGY3Y.js +11 -0
- package/dist/stage/chunk-2VR4WFQ7.js +10 -0
- package/dist/stage/chunk-2ZYXBUWG.js +9 -0
- package/dist/stage/chunk-32LAWMGY.js +1 -0
- package/dist/stage/chunk-3LEMTSOO.js +11 -0
- package/dist/stage/chunk-3QCKGZYE.js +32 -0
- package/dist/stage/chunk-44P66WFA.js +1 -0
- package/dist/stage/chunk-4AM6FRR6.js +7 -0
- package/dist/stage/chunk-4QDE4SUK.js +1 -0
- package/dist/stage/chunk-4QESHV3U.js +1 -0
- package/dist/stage/chunk-4SG5QPHB.js +2 -0
- package/dist/stage/chunk-4T4UVCOC.js +1 -0
- package/dist/stage/chunk-4WUEPPG3.js +34 -0
- package/dist/stage/chunk-4YCN6UZ5.js +99 -0
- package/dist/stage/chunk-5V7ZY2AP.js +1 -0
- package/dist/stage/chunk-5VS5Y2VJ.js +6 -0
- package/dist/stage/chunk-5W4NO2ZD.js +62 -0
- package/dist/stage/chunk-65CRNICM.js +1 -0
- package/dist/stage/chunk-66MULO7F.js +5 -0
- package/dist/stage/chunk-6COFQTBS.js +162 -0
- package/dist/stage/chunk-6VZG3M3I.js +8 -0
- package/dist/stage/chunk-7HVDH43T.js +1 -0
- package/dist/stage/chunk-7JLJN4EZ.js +8 -0
- package/dist/stage/chunk-7QDSP4LN.js +1 -0
- package/dist/stage/chunk-7R6OQAO3.js +1 -0
- package/dist/stage/chunk-7VTTHZKW.js +7 -0
- package/dist/stage/chunk-ATHR5KEN.js +10 -0
- package/dist/stage/chunk-B6R5LTP2.js +36 -0
- package/dist/stage/chunk-BD4WE264.js +1 -0
- package/dist/stage/chunk-BONANU47.js +206 -0
- package/dist/stage/chunk-BTVW7OFK.js +89 -0
- package/dist/stage/chunk-BUTJIDHG.js +8 -0
- package/dist/stage/chunk-C2IPYMML.js +84 -0
- package/dist/stage/chunk-C54FTPPV.js +9 -0
- package/dist/stage/chunk-C6LOUF2Z.js +90 -0
- package/dist/stage/chunk-CFF3PILO.js +339 -0
- package/dist/stage/chunk-CI2FQ2JY.js +9 -0
- package/dist/stage/chunk-CW5O4RV7.js +12 -0
- package/dist/stage/chunk-CYABNQYD.js +1 -0
- package/dist/stage/chunk-D7FFA4NS.js +15 -0
- package/dist/stage/chunk-DUNNXU6Z.js +8 -0
- package/dist/stage/chunk-E3E4Q6JF.js +6 -0
- package/dist/stage/chunk-EB3U3LH7.js +9 -0
- package/dist/stage/chunk-EGSZ736B.js +8 -0
- package/dist/stage/chunk-EOBXW5VQ.js +2 -0
- package/dist/stage/chunk-ERZC3B7Y.js +11 -0
- package/dist/stage/chunk-ESDOOBIH.js +11 -0
- package/dist/stage/chunk-EVU4QAL4.js +178 -0
- package/dist/stage/chunk-EVVZI2NY.js +39 -0
- package/dist/stage/chunk-FBKAZUKD.js +39 -0
- package/dist/stage/chunk-FDVKYQJN.js +1 -0
- package/dist/stage/chunk-FGPVYHVR.js +5 -0
- package/dist/stage/chunk-FHUD4AZC.js +1 -0
- package/dist/stage/chunk-FKUJ2DMY.js +96 -0
- package/dist/stage/chunk-FNZGVIJV.js +1 -0
- package/dist/stage/chunk-GBYSEGYM.js +78 -0
- package/dist/stage/chunk-GCO4TDHX.js +1 -0
- package/dist/stage/chunk-GKTML2LG.js +10 -0
- package/dist/stage/chunk-GMDIHHXQ.js +11 -0
- package/dist/stage/chunk-GOUNACI3.js +10 -0
- package/dist/stage/chunk-GXAYATPF.js +3 -0
- package/dist/stage/chunk-H2K2DDXV.js +8 -0
- package/dist/stage/chunk-H3UJI6ZY.js +13 -0
- package/dist/stage/chunk-H6PUGCME.js +7 -0
- package/dist/stage/chunk-HNBUIMIG.js +1 -0
- package/dist/stage/chunk-HRZY2BJQ.js +10 -0
- package/dist/stage/chunk-HSPJGALW.js +30 -0
- package/dist/stage/chunk-IBYORHIP.js +25 -0
- package/dist/stage/chunk-IHF4EWTY.js +1 -0
- package/dist/stage/chunk-ISBRPNKI.js +1 -0
- package/dist/stage/chunk-IZ3OI3C2.js +2 -0
- package/dist/stage/chunk-JEFZAC2R.js +1 -0
- package/dist/stage/chunk-JFIGRDPO.js +1 -0
- package/dist/stage/chunk-JFVYBOOC.js +9 -0
- package/dist/stage/chunk-JGD4TUTQ.js +1 -0
- package/dist/stage/chunk-JO4BEHDH.js +4 -0
- package/dist/stage/chunk-JOWBS42C.js +1 -0
- package/dist/stage/chunk-JP3TM2TG.js +1 -0
- package/dist/stage/chunk-JVK2C52O.js +1 -0
- package/dist/stage/chunk-K4I76XBX.js +14 -0
- package/dist/stage/chunk-KA6CZZ4M.js +1 -0
- package/dist/stage/chunk-KESSJ36M.js +1 -0
- package/dist/stage/chunk-LKATEVRU.js +1 -0
- package/dist/stage/chunk-LS4EJ2EI.js +1 -0
- package/dist/stage/chunk-LUKJQC2X.js +70 -0
- package/dist/stage/chunk-LWULR3SM.js +1 -0
- package/dist/stage/chunk-LWVRFMKX.js +2 -0
- package/dist/stage/chunk-LXC4ZBPZ.js +1 -0
- package/dist/stage/chunk-M2UP255H.js +1 -0
- package/dist/stage/chunk-MAPPXUZQ.js +231 -0
- package/dist/stage/chunk-MBMPW7Z5.js +1 -0
- package/dist/stage/chunk-MD3HMMTK.js +1 -0
- package/dist/stage/chunk-MLPCUIKR.js +17 -0
- package/dist/stage/chunk-MNQIRPDQ.js +8 -0
- package/dist/stage/chunk-MRG7MJVS.js +1 -0
- package/dist/stage/chunk-MU6EXW72.js +10 -0
- package/dist/stage/chunk-MVU2X6LU.js +4 -0
- package/dist/stage/chunk-N6UA5CZV.js +36 -0
- package/dist/stage/chunk-NLZIT2TJ.js +7 -0
- package/dist/stage/chunk-NOJR3J7M.js +1 -0
- package/dist/stage/chunk-NT7ARDOL.js +1 -0
- package/dist/stage/chunk-NWOGMPZH.js +1 -0
- package/dist/stage/chunk-NXE4KMG3.js +40 -0
- package/dist/stage/chunk-O4YCZXD2.js +257 -0
- package/dist/stage/chunk-OAJZ7HLK.js +1 -0
- package/dist/stage/chunk-OCFCFASO.js +1 -0
- package/dist/stage/chunk-ODGPG5RY.js +7 -0
- package/dist/stage/chunk-ODUEA52Q.js +3 -0
- package/dist/stage/chunk-OGDJ3FCI.js +1 -0
- package/dist/stage/chunk-OLDJ55TT.js +1 -0
- package/dist/stage/chunk-OOCL4ATI.js +2 -0
- package/dist/stage/chunk-OQEFDUDV.js +1 -0
- package/dist/stage/chunk-OQWVEWG7.js +4 -0
- package/dist/stage/chunk-OUIX6BGK.js +1 -0
- package/dist/stage/chunk-OV7OZYUO.js +7 -0
- package/dist/stage/chunk-PFGDY33O.js +1 -0
- package/dist/stage/chunk-PFJDUJB3.js +139 -0
- package/dist/stage/chunk-PGEW76ER.js +1 -0
- package/dist/stage/chunk-PP76TAQX.js +32 -0
- package/dist/stage/chunk-PTBOP4OT.js +2 -0
- package/dist/stage/chunk-PX2RVHCK.js +1 -0
- package/dist/stage/chunk-Q7K3PNAD.js +1 -0
- package/dist/stage/chunk-QDDPLZDB.js +1 -0
- package/dist/stage/chunk-QLGLZ77F.js +1 -0
- package/dist/stage/chunk-QOPFDFHI.js +1 -0
- package/dist/stage/chunk-QXT4KAEJ.js +1 -0
- package/dist/stage/chunk-R4YVZCIT.js +1 -0
- package/dist/stage/chunk-R6P6EHWA.js +62 -0
- package/dist/stage/chunk-RCH3OXBI.js +9 -0
- package/dist/stage/chunk-RHPE44I2.js +1 -0
- package/dist/stage/chunk-RJ2R4FVC.js +1 -0
- package/dist/stage/chunk-RJH2YVTD.js +1 -0
- package/dist/stage/chunk-RLWWW5OB.js +6 -0
- package/dist/stage/chunk-S3WO35Z7.js +120 -0
- package/dist/stage/chunk-S7ZGX6MW.js +1 -0
- package/dist/stage/chunk-SIAWX27B.js +1 -0
- package/dist/stage/chunk-SLG4X622.js +1 -0
- package/dist/stage/chunk-SLJRBC77.js +168 -0
- package/dist/stage/chunk-SRORWQJZ.js +1 -0
- package/dist/stage/chunk-STZTUIIF.js +2 -0
- package/dist/stage/chunk-SVQ6MWNV.js +9 -0
- package/dist/stage/chunk-TL3HLL2S.js +132 -0
- package/dist/stage/chunk-U3DOE6SF.js +24 -0
- package/dist/stage/chunk-UABQIZKW.js +6 -0
- package/dist/stage/chunk-UDK3R764.js +1 -0
- package/dist/stage/chunk-UFF4E54B.js +41 -0
- package/dist/stage/chunk-UFP5Q4AY.js +1 -0
- package/dist/stage/chunk-UMH2TSW2.js +72 -0
- package/dist/stage/chunk-UTASVNF5.js +10 -0
- package/dist/stage/chunk-UW7ML5ML.js +88 -0
- package/dist/stage/chunk-V3J7F6OP.js +1 -0
- package/dist/stage/chunk-VEHP5B5Q.js +5 -0
- package/dist/stage/chunk-VRXWB733.js +9 -0
- package/dist/stage/chunk-VVDHBBS5.js +5 -0
- package/dist/stage/chunk-VYFOH6FU.js +106 -0
- package/dist/stage/chunk-W3VZTLQS.js +3 -0
- package/dist/stage/chunk-W7WNRWTZ.js +8 -0
- package/dist/stage/chunk-WHUCV53C.js +1 -0
- package/dist/stage/chunk-WULMYHIF.js +1 -0
- package/dist/stage/chunk-X54EX7AW.js +9 -0
- package/dist/stage/chunk-X565ISZM.js +1 -0
- package/dist/stage/chunk-XGZMDECB.js +1 -0
- package/dist/stage/chunk-XHONC4RF.js +172 -0
- package/dist/stage/chunk-XIK4XQ6E.js +24 -0
- package/dist/stage/chunk-XJQQWFTE.js +1 -0
- package/dist/stage/chunk-XM4WOU3I.js +13 -0
- package/dist/stage/chunk-XM7Y5HP5.js +38 -0
- package/dist/stage/chunk-XNYYRGD5.js +292 -0
- package/dist/stage/chunk-XOYVR3SV.js +1 -0
- package/dist/stage/chunk-XQIW5HCE.js +1 -0
- package/dist/stage/chunk-YEWL7FUW.js +8 -0
- package/dist/stage/chunk-YI55PY3V.js +1 -0
- package/dist/stage/chunk-YM63HI6W.js +1 -0
- package/dist/stage/chunk-YNLGLU4P.js +9 -0
- package/dist/stage/chunk-YTNMNOWP.js +1 -0
- package/dist/stage/chunk-YV62XKTC.js +1 -0
- package/dist/stage/chunk-YVBA3QW6.js +1 -0
- package/dist/stage/chunk-Z2SY2HOC.js +1 -0
- package/dist/stage/chunk-ZBTCQEBK.js +10 -0
- package/dist/stage/chunk-ZCUFN74D.js +1 -0
- package/dist/stage/chunk-ZHOULXEJ.js +11 -0
- package/dist/stage/chunk-ZTRZIET2.js +1 -0
- package/dist/stage/index.html +1 -0
- package/dist/stage/stage.css +1 -1
- package/dist/stage/stage.js +1 -4383
- package/dist/ui/dev/dev-page.d.ts +1 -0
- package/dist/ui/dev/dev-page.js +2021 -0
- package/dist/ui/dev/dev-theme.d.ts +6 -0
- package/dist/ui/dev/dev-theme.js +67 -0
- package/dist/ui/dev/voice-gallery-page.d.ts +1 -0
- package/dist/ui/dev/voice-gallery-page.js +121 -0
- package/dist/ui/dev/voice-scenes.d.ts +21 -0
- package/dist/ui/dev/voice-scenes.js +30 -0
- package/dist/ui/dictation/en-box.d.ts +2 -0
- package/dist/ui/dictation/en-box.js +86 -0
- package/dist/ui/dictation/en-page.d.ts +1 -0
- package/dist/ui/dictation/en-page.js +484 -0
- package/dist/ui/dictation/page.d.ts +1 -0
- package/dist/ui/dictation/page.js +1084 -0
- package/dist/ui/dictation/parent-page.d.ts +1 -0
- package/dist/ui/dictation/parent-page.js +234 -0
- package/dist/ui/dictation/reel-mode.d.ts +1 -0
- package/dist/ui/dictation/reel-mode.js +87 -0
- package/dist/ui/dictation/reel-page.d.ts +1 -0
- package/dist/ui/dictation/reel-page.js +62 -0
- package/dist/ui/dictation/reel.d.ts +163 -0
- package/dist/ui/dictation/reel.js +436 -0
- package/dist/ui/dictation/ui.d.ts +44 -0
- package/dist/ui/dictation/ui.js +80 -0
- package/dist/ui/dictation/unlock.d.ts +7 -0
- package/dist/ui/dictation/unlock.js +16 -0
- package/dist/ui/kid/audio-client.d.ts +11 -0
- package/dist/ui/kid/audio-client.js +74 -0
- package/dist/ui/kid/board-mark-art.d.ts +37 -0
- package/dist/ui/kid/board-mark-art.js +5 -0
- package/dist/ui/kid/board-mark.d.ts +24 -0
- package/dist/ui/kid/board-mark.js +58 -0
- package/dist/ui/kid/home-tiles.d.ts +14 -0
- package/dist/ui/kid/home-tiles.js +44 -0
- package/dist/ui/kid/kid-log.d.ts +15 -0
- package/dist/ui/kid/kid-log.js +87 -0
- package/dist/ui/kid/kid-nav.d.ts +11 -0
- package/dist/ui/kid/kid-nav.js +22 -0
- package/dist/ui/kid/kid-page.d.ts +9 -0
- package/dist/ui/kid/kid-page.js +3684 -0
- package/dist/ui/kid/letters.d.ts +26 -0
- package/dist/ui/kid/letters.js +51 -0
- package/dist/ui/kid/photo-edit.d.ts +83 -0
- package/dist/ui/kid/photo-edit.js +106 -0
- package/dist/ui/kid/photo-ui.d.ts +23 -0
- package/dist/ui/kid/photo-ui.js +60 -0
- package/dist/ui/kid/reel.d.ts +419 -0
- package/dist/ui/kid/reel.js +648 -0
- package/dist/ui/kid/tianzige.d.ts +8 -0
- package/dist/ui/kid/tianzige.js +45 -0
- package/dist/ui/kid/tutor-face.d.ts +35 -0
- package/dist/ui/kid/tutor-face.js +84 -0
- package/dist/ui/kid/voice-ui.d.ts +70 -0
- package/dist/ui/kid/voice-ui.js +316 -0
- package/dist/ui/kid/voice-view.d.ts +71 -0
- package/dist/ui/kid/voice-view.js +75 -0
- package/dist/ui/kid/word-draw.d.ts +2 -0
- package/dist/ui/kid/word-draw.js +107 -0
- package/dist/ui/kid/word-emoji.d.ts +10 -0
- package/dist/ui/kid/word-emoji.js +68 -0
- package/dist/ui/kid/word-layout.d.ts +25 -0
- package/dist/ui/kid/word-layout.js +29 -0
- package/dist/ui/parent/parent-home-page.d.ts +1 -0
- package/dist/ui/parent/parent-home-page.js +722 -0
- package/dist/ui/parent/parent-page.d.ts +2 -0
- package/dist/ui/parent/parent-page.js +511 -0
- package/dist/ui/parent/parent-settings-page.d.ts +1 -0
- package/dist/ui/parent/parent-settings-page.js +133 -0
- package/dist/ui/parent/qr-page.d.ts +8 -0
- package/dist/ui/parent/qr-page.js +66 -0
- package/dist/ui/shared/brand.d.ts +38 -0
- package/dist/ui/shared/brand.js +53 -0
- package/dist/ui/shared/favicon.d.ts +12 -0
- package/dist/ui/shared/favicon.js +96 -0
- package/dist/ui/shared/icon.d.ts +9 -0
- package/dist/ui/shared/icon.js +35 -0
- package/dist/ui/shared/inline-lib.d.ts +1 -0
- package/dist/ui/shared/inline-lib.js +28 -0
- package/dist/ui/shared/logo.d.ts +31 -0
- package/dist/ui/shared/logo.js +43 -0
- package/dist/ui/shared/page-dom.d.ts +12 -0
- package/dist/ui/shared/page-dom.js +36 -0
- package/dist/ui/shared/png.d.ts +1 -0
- package/dist/ui/shared/png.js +45 -0
- package/dist/ui/shared/qr-svg.d.ts +3 -0
- package/dist/ui/shared/qr-svg.js +32 -0
- package/dist/ui/shared/qr.d.ts +14 -0
- package/dist/ui/shared/qr.js +281 -0
- package/dist/ui/shared/reel-dock.d.ts +22 -0
- package/dist/ui/shared/reel-dock.js +365 -0
- package/dist/ui/shared/tutor-color.d.ts +15 -0
- package/dist/ui/shared/tutor-color.js +47 -0
- package/dist/ui/talk/page.d.ts +6 -0
- package/dist/ui/talk/page.js +294 -0
- package/grades/grade-2/README.md +11 -0
- package/grades/grade-2/VALIDATION.md +41 -0
- package/grades/grade-2/agents/chinese-tutor/activities//344/275/240/350/257/264/346/210/221/347/224/273.md +157 -0
- package/grades/grade-2/agents/chinese-tutor/activities//344/275/240/350/257/264/346/210/221/347/224/273./344/276/213/345/255/220.md +78 -0
- package/grades/grade-2/agents/chinese-tutor/activities//344/276/213/345/255/220/345/233/276//345/200/237/344/274/236.jpg +0 -0
- package/grades/grade-2/agents/chinese-tutor/activities//344/276/213/345/255/220/345/233/276//345/260/217/347/214/264/346/221/230/346/241/203.jpg +0 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/206/231/350/257/235.md +64 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/206/231/350/257/235./344/276/213/345/255/220.md +31 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/220/254/345/206/231.md +48 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240.md +79 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/260/217/350/257/276/345/240/202.md +53 -0
- package/grades/grade-2/agents/chinese-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232.md +81 -0
- package/grades/grade-2/agents/chinese-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/chinese-tutor/activities//347/234/213/345/233/276/350/257/264/350/257/235.md +94 -0
- package/grades/grade-2/agents/chinese-tutor/activities//347/234/213/345/233/276/350/257/264/350/257/235./344/276/213/345/255/220.md +63 -0
- package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240.md +82 -0
- package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/chinese-tutor.md +59 -0
- package/grades/grade-2/agents/english-tutor/activities//345/217/243/350/257/255/350/257/276.md +149 -0
- package/grades/grade-2/agents/english-tutor/activities//345/217/243/350/257/255/350/257/276./344/276/213/345/255/220.md +13 -0
- package/grades/grade-2/agents/english-tutor/activities//345/220/254/345/206/231.md +47 -0
- package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240.md +78 -0
- package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/english-tutor/activities//345/260/217/350/257/276/345/240/202.md +53 -0
- package/grades/grade-2/agents/english-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232.md +80 -0
- package/grades/grade-2/agents/english-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240.md +86 -0
- package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/english-tutor.md +57 -0
- package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240.md +78 -0
- package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/math-tutor/activities//345/260/217/350/257/276/345/240/202.md +56 -0
- package/grades/grade-2/agents/math-tutor/activities//345/260/217/350/257/276/345/240/202./344/276/213/345/255/220.md +52 -0
- package/grades/grade-2/agents/math-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232.md +80 -0
- package/grades/grade-2/agents/math-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240.md +81 -0
- package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/math-tutor.md +51 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/index.json +44 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-1.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-2.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-3.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-4.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-5.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/bake.json +1 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/manifest.json +63 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/scene.json +247 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen.md +40 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen.ts +26 -0
- package/grades/grade-2/examples//345/271/263/345/235/207/345/210/206./344/276/213/345/255/220.md +24 -0
- package/grades/grade-2/examples//346/225/260/345/255/246/345/271/263/345/235/207/345/210/206.md +42 -0
- package/grades/grade-2/validation/2026-10-27.json +4410 -0
- package/package.json +27 -15
- package/skills/chalk-cli/SKILL.md +62 -0
- package/skills/chalk-scene/SKILL.md +91 -0
- package/skills/chalk-teaching/SKILL.md +82 -0
- package/skills/chalk-teaching/models//344/272/244/351/224/231/346/225/260/345/210/227/345/210/206/350/241/214.md +8 -0
- package/skills/chalk-teaching/models//345/207/221/345/215/201.md +7 -0
- package/skills/chalk-teaching/models//345/221/250/346/234/237/350/247/204/345/276/213.md +6 -0
- package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/345/242/236/345/207/217.md +6 -0
- package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/346/257/224/345/244/232/345/260/221-/346/261/202/345/244/247/346/225/260/346/261/202/345/260/217/346/225/260.md +7 -0
- package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/346/257/224/345/244/232/345/260/221-/346/261/202/345/267/256.md +6 -0
- package/skills/chalk-teaching/models//345/260/217/346/243/222/350/277/233/344/275/215/345/212/240.md +7 -0
- package/skills/chalk-teaching/models//345/260/217/346/243/222/351/200/200/344/275/215/345/207/217.md +6 -0
- package/skills/chalk-teaching/models//346/216/222/351/230/237/347/202/271/351/230/265.md +7 -0
- package/skills/chalk-teaching/models//346/227/266/351/227/264/346/225/260/350/275/264.md +6 -0
- package/skills/chalk-teaching/models//346/245/274/345/261/202/351/227/264/351/232/224/345/233/276.md +7 -0
- package/skills/chalk-teaching/models//347/240/264/345/215/201.md +7 -0
- package/skills/chalk-teaching/models//347/253/226/345/274/217/351/200/200/344/275/215.md +6 -0
- package/skills/chalk-teaching/models//347/272/277/346/256/265/345/233/276.md +7 -0
- package/skills/chalk-teaching/models-index.md +24 -0
- package/skills/chalk-teaching/references//350/256/262/350/247/243/346/250/241/346/235/277/344/270/211/346/241/243.md +32 -0
- package/skills/chalk-teaching/references//350/257/264/346/230/216/346/226/207/344/273/266.md +102 -0
- package/skills/chalk-verify/SKILL.md +57 -0
- package/skills/chalk-verify/references//350/257/204/345/210/244/346/240/207/345/207/206.md +69 -0
- package/skills/cotutor-analyze/SKILL.md +106 -0
- package/skills/cotutor-analyze/references//345/221/275/344/273/244/344/270/216/346/226/207/344/273/266.md +31 -0
- package/skills/cotutor-board/SKILL.md +373 -0
- package/skills/cotutor-board/references/README.md +38 -0
- package/skills/cotutor-board/references/canvas.md +68 -0
- package/skills/cotutor-board/references/choice.md +55 -0
- package/skills/cotutor-board/references/code.md +46 -0
- package/skills/cotutor-board/references/image.md +52 -0
- package/skills/cotutor-board/references/lecture.md +69 -0
- package/skills/cotutor-board/references/paint.md +77 -0
- package/skills/cotutor-board/references/photo.md +61 -0
- package/skills/cotutor-board/references/read.md +51 -0
- package/skills/cotutor-board/references/text.md +69 -0
- package/skills/cotutor-board/references/tianzige.md +78 -0
- package/skills/cotutor-board/references/word.md +91 -0
- package/skills/cotutor-home/SKILL.md +170 -0
- package/skills/cotutor-home/references/dictation.md +97 -0
- package/skills/cotutor-home/references/tutor.md +149 -0
- package/skills/cotutor-home/references//345/221/275/344/273/244.md +21 -0
- package/skills/cotutor-home/references//351/246/226/351/241/265/350/257/255/346/263/225.md +143 -0
- package/skills/cotutor-tune/SKILL.md +147 -0
- package/skills/cotutor-tune/references//345/255/227/346/256/265.md +53 -0
- package/skills/cotutor-tutor/SKILL.md +253 -0
- package/skills/cotutor-tutor/references//344/270/212/344/270/213/346/226/207/345/214/205.md +78 -0
- package/themes/default/kid.css +86 -0
- package/themes/default/theme.json +26 -0
- package/agents/chinese-tutor.md +0 -67
- package/agents/homework-tutor.md +0 -17
- package/agents/math-tutor.md +0 -68
- package/agents/planner.md +0 -18
- package/agents/reading-tutor.md +0 -65
- package/agents/scene-maker.md +0 -41
- package/dist/cards/canvas.d.ts +0 -22
- package/dist/cards/canvas.js +0 -65
- package/dist/cards/fill.d.ts +0 -14
- package/dist/cards/fill.js +0 -64
- package/dist/cards/index.d.ts +0 -48
- package/dist/cards/index.js +0 -115
- package/dist/cards/kind.js +0 -7
- package/dist/cards/scene.d.ts +0 -24
- package/dist/cards/scene.js +0 -67
- package/dist/cards/text.d.ts +0 -18
- package/dist/cards/text.js +0 -41
- package/dist/cli/cert.d.ts +0 -7
- package/dist/cli/cert.js +0 -36
- package/dist/cli/doctor.d.ts +0 -22
- package/dist/cli/doctor.js +0 -355
- package/dist/cli/init.js +0 -116
- package/dist/cli/main.js +0 -243
- package/dist/cli/serve.d.ts +0 -30
- package/dist/cli/serve.js +0 -56
- package/dist/cli/skeleton.d.ts +0 -39
- package/dist/cli/skeleton.js +0 -184
- package/dist/cli/skills.d.ts +0 -40
- package/dist/cli/skills.js +0 -170
- package/dist/cli/workspace.d.ts +0 -69
- package/dist/lib/agent-file.d.ts +0 -10
- package/dist/lib/agent-file.js +0 -16
- package/dist/lib/board.d.ts +0 -15
- package/dist/lib/board.js +0 -107
- package/dist/lib/context-pack.js +0 -55
- package/dist/lib/conversation.js +0 -88
- package/dist/lib/kid-board.d.ts +0 -148
- package/dist/lib/kid-board.js +0 -294
- package/dist/lib/kid-view.d.ts +0 -63
- package/dist/lib/kid-view.js +0 -93
- package/dist/lib/ledger.d.ts +0 -29
- package/dist/lib/run-plan.d.ts +0 -30
- package/dist/lib/run-plan.js +0 -32
- package/dist/lib/sections.d.ts +0 -12
- package/dist/lib/sections.js +0 -151
- package/dist/lib/stream.d.ts +0 -15
- package/dist/lib/stream.js +0 -69
- package/dist/lib/transcript.d.ts +0 -36
- package/dist/lib/transcript.js +0 -95
- package/dist/server/app.d.ts +0 -74
- package/dist/server/app.js +0 -406
- package/dist/server/kid-page.d.ts +0 -1
- package/dist/server/kid-page.js +0 -768
- package/dist/server/mock.js +0 -403
- package/dist/server/parent-page.d.ts +0 -8
- package/dist/server/parent-page.js +0 -393
- package/dist/server/runner.d.ts +0 -64
- package/dist/server/runner.js +0 -277
- package/dist/server/scene-props.d.ts +0 -16
- package/dist/server/scene-props.js +0 -39
- package/dist/server/store.d.ts +0 -43
- package/dist/server/store.js +0 -210
- package/dist/server/tts.d.ts +0 -51
- package/dist/server/tts.js +0 -99
- /package/dist/{cli → app/cli}/main.d.ts +0 -0
- /package/dist/{cards → board/cards}/choice.d.ts +0 -0
- /package/dist/{cards → board/cards}/code.d.ts +0 -0
- /package/dist/{cards → board/cards}/image.d.ts +0 -0
- /package/dist/{cards → board/cards}/read.d.ts +0 -0
|
@@ -0,0 +1,777 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 发消息 = 拼上下文包 → 按运行时 spawn / resume → stream-json 落 conversations/<老师>/<日期>.<job>.log → 索引更新
|
|
3
|
+
* (最终文本 → 板书节 section + 家长尾巴 parentText + 解析提醒 warnings)。
|
|
4
|
+
* 流式:stdout 经本进程落盘,同时喂 PartialReader 拼当前回复正文 → parseBoard(partial) → 内存里的 partial section
|
|
5
|
+
* (孩子端 pending 条目带着它,卡随围栏闭合逐张出现);讲稿不配音,交给实时的嘴照念。
|
|
6
|
+
* 点读段、单词等卡的资产在索引写好之后后台配,孩子端点到还没好的段等它到,等不到就不出声。
|
|
7
|
+
* 一老师同时只跑一条(老师还在回上一条就 409),跨天自动新开(索引按本地日期分文件,新文件没 session 就不带 --resume)。
|
|
8
|
+
* 话题(2026-09-11):一天可多个,每个话题自己的会话(index.sessions[thread]);newThread / 系统消息 / 今天第一条开新话题(不 resume、不带旧卡),
|
|
9
|
+
* 指定 thread 接着今天的旧话题(resume 它的会话),缺省接当前话题。
|
|
10
|
+
* 埋点(2026-09-11):每轮记 timing(进程起的时刻、首卡、进程退出、配音收尾,毫秒),家长视图每轮一行。
|
|
11
|
+
* 进程 cwd 是老师目录 agents/<name>/。
|
|
12
|
+
*/
|
|
13
|
+
import { lecturePack, readLecture, storedMarks } from "./lecture.js";
|
|
14
|
+
import { appendFile, mkdir, readFile } from 'node:fs/promises';
|
|
15
|
+
import { isAbsolute, join, relative, sep } from 'node:path';
|
|
16
|
+
import { buildContextPack } from "./context/context-pack.js";
|
|
17
|
+
import { addMessage, applyRun, changedCards, jobId, sameRuntime, sessionFor } from "./journal/conversation.js";
|
|
18
|
+
import { localDate, localMinute } from "../shared/time.js";
|
|
19
|
+
import { cardVoice, cardId, conversationFiles, threads } from "../schema/index.js";
|
|
20
|
+
import { bookkeepingPrompt, kidQuestions, textbookHeadings } from "../memory/diary.js";
|
|
21
|
+
import { cardAssets, cardLabel, describeCard, tutorCardKinds } from "../board/cards/index.js";
|
|
22
|
+
import { boardGuideFor, cardGuidesFor } from "../board/cards/docs.js";
|
|
23
|
+
import { activityCards, parseActivity, renderTutorSection } from "./activity.js";
|
|
24
|
+
import { findActivityFile } from "./activities.js";
|
|
25
|
+
import { activityState, paintGate, paintTool, readActivity, threadActivity } from "./activity-state.js";
|
|
26
|
+
import { createBrain, getRuntime } from "../brain/index.js";
|
|
27
|
+
import { lineFlags, parseBoard } from "../board/parse.js";
|
|
28
|
+
import { readyBeats, beatsOf } from "../board/presentation.js";
|
|
29
|
+
import { deriveKidView, truncateReply } from "./kid-view.js";
|
|
30
|
+
import { parseSections } from "../board/sections.js";
|
|
31
|
+
import { threadDigest } from "./context/thread-digest.js";
|
|
32
|
+
import { beatTimings } from "./journal/events.js";
|
|
33
|
+
import {} from "../schema/index.js";
|
|
34
|
+
import { BOARD_CARD_KINDS, MEMORY_MAX_PER_TURN, MEMORY_TIDY_CAP, VAULT_PACK_ROLES, isApiRuntime, resolvePolicy } from "../schema/index.js";
|
|
35
|
+
import { UsageError } from "../shared/errors.js";
|
|
36
|
+
import { readAgentBody, readCardStates, readIndex, snapshotSources, writeIndex, writeRunFile } from "./journal/store.js";
|
|
37
|
+
import { readTextbooks, scanVault, writeVaultMemory } from "../memory/vault.js";
|
|
38
|
+
import { learningContext } from "../memory/context.js";
|
|
39
|
+
import { childChanges, planDiary, planMemory, recoverPending, writeDiaryBlock } from "../memory/commit.js";
|
|
40
|
+
import { kidSaid } from "../memory/proposals.js";
|
|
41
|
+
import { memoryCount, textHash, tidyMemoryPrompt } from "../memory/vault-notes.js";
|
|
42
|
+
import { TUTOR_RULES_PATH, takesTutorRules, tutorRulesBody, BOARD_GUIDE_IN_SYSTEM, BOARD_GUIDE_PATH, boardGuideBody, boardGuideReads } from "./context/tutor-rules.js";
|
|
43
|
+
import { DubQueue } from "../media/tts.js";
|
|
44
|
+
import { continueContext } from "./home/store.js";
|
|
45
|
+
import { kidInput } from "./kid-input.js";
|
|
46
|
+
import { compatibleLine } from "../shared/qianwen.js";
|
|
47
|
+
import { DRAW_ONLY_TEXT, drawingPackLine } from "../board/paper.js";
|
|
48
|
+
import { makeDraw } from "../media/draw.js";
|
|
49
|
+
import { paintLines, startPaints } from "../scenes/paint/painter.js";
|
|
50
|
+
export class BusyError extends Error {
|
|
51
|
+
constructor(tutor, job) {
|
|
52
|
+
super(`${tutor} 还在回上一条(${job}),等它说完再发`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** API 运行时的长话题:上一轮结束时上下文(tokens.context)过了这么多,这一轮整段压缩(2026-10-29 定 32k) */
|
|
56
|
+
export const COMPACT_TOKENS = 32_000;
|
|
57
|
+
/**
|
|
58
|
+
* 这一轮该不该整段压缩:这个话题最近一轮报的上下文(只有 API 运行时报)过了 COMPACT_TOKENS,而且上一轮不是刚压缩过的
|
|
59
|
+
* (压缩完还过线,说明系统提示与笔记本身就大,再压也小不下去,隔一轮再看)
|
|
60
|
+
*/
|
|
61
|
+
function dueCompact(messages, ths, thread) {
|
|
62
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
63
|
+
if (ths[i] !== thread)
|
|
64
|
+
continue;
|
|
65
|
+
if (messages[i].compacted)
|
|
66
|
+
return false;
|
|
67
|
+
if (messages[i].tokens?.context !== undefined)
|
|
68
|
+
return messages[i].tokens.context >= COMPACT_TOKENS;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 上下文包的取材(《obsidian仓库设计.md》§7 每轮那行,2026-09-17 改):课程表命中的时段 + 老师守则(有脸的老师)+ 当前学期 + 档案与入口文件的原文(按 entryChars 截)
|
|
74
|
+
* + 参考路径 + 本周计划里本老师的行 + 最近 14 天日记里本学科的「- 观察:」行。
|
|
75
|
+
* 守则与笔记原文总是带上;同一话题里没改过的由 send 换成「未变」。
|
|
76
|
+
*/
|
|
77
|
+
export async function gatherContext(ws, tutor, input, report) {
|
|
78
|
+
const t = ws.config.tutors[tutor];
|
|
79
|
+
const policy = resolvePolicy(ws.config, tutor);
|
|
80
|
+
const pack = { at: localMinute(input.at), focus: input.focus, plan: [], recent: [] };
|
|
81
|
+
// vault 那几段(src/memory/context.ts):activity 话题的记忆是它自己的(agent: <老师>/<activity>);档案、入口文件、日记观察照旧共用
|
|
82
|
+
const mem = await learningContext(ws, { at: input.at, agent: input.agent ?? tutor, subject: t?.subject, display: t?.display, teacher: Boolean(t) && tutor.endsWith('-tutor'), ...(input.query ? { query: input.query } : {}), limits: { entryChars: policy.contextPack.entryChars, planLines: policy.contextPack.planLines, recent: policy.contextPack.recent } }, report);
|
|
83
|
+
if (mem.slot)
|
|
84
|
+
pack.slot = mem.slot;
|
|
85
|
+
if (mem.semester)
|
|
86
|
+
pack.semester = mem.semester;
|
|
87
|
+
pack.notes = [];
|
|
88
|
+
// 有脸的老师的公共守则:机器技能 cotutor-tutor 的原文,排在家长笔记前面(不截;同一话题没改过由 send 换成「未变」)
|
|
89
|
+
if (takesTutorRules(tutor)) {
|
|
90
|
+
try {
|
|
91
|
+
pack.notes.push({ role: 'rules', path: TUTOR_RULES_PATH, text: tutorRulesBody(await readFile(join(ws.root, TUTOR_RULES_PATH), 'utf8'), input.kinds ?? tutorCardKinds(ws.config.tutors[tutor]?.cards) ?? undefined) });
|
|
92
|
+
pack.rules = TUTOR_RULES_PATH;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
pack.rules = `缺:${TUTOR_RULES_PATH} 不在(cotutor upgrade 补),照你老师文件里的做`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
pack.notes.push(...mem.notes);
|
|
99
|
+
pack.profile = mem.profile;
|
|
100
|
+
if (mem.child !== undefined)
|
|
101
|
+
pack.child = mem.child;
|
|
102
|
+
if (mem.entry !== undefined)
|
|
103
|
+
pack.entry = mem.entry;
|
|
104
|
+
pack.memory = mem.memory;
|
|
105
|
+
if (mem.lesson)
|
|
106
|
+
pack.lesson = mem.lesson;
|
|
107
|
+
if (mem.refs)
|
|
108
|
+
pack.refs = mem.refs;
|
|
109
|
+
pack.plan = mem.plan;
|
|
110
|
+
pack.recent = mem.recent;
|
|
111
|
+
if (mem.lookup.length)
|
|
112
|
+
pack.lookup = mem.lookup;
|
|
113
|
+
pack.vault = vaultPack(ws.paths);
|
|
114
|
+
return { ...pack, retrieved: mem.retrieved };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* activity 的那两段:`activity:`(名字、参数、应用算的)每条都带;
|
|
118
|
+
* 「老师怎么带」那节(按参数与这个话题的卡裁过、剥了注释)+ 这个 activity 多带的卡的写法,作为一条笔记放进话题第一条(<cotutor-activity>),
|
|
119
|
+
* 续话题没改过写「未变」(同守则)。文件不在、读不了 → activityGuide 写「缺」,老师照自己的文件做。返回给这轮的提醒。
|
|
120
|
+
*/
|
|
121
|
+
export async function attachActivity(ws, tutor, pack, run, where, inSystem = false) {
|
|
122
|
+
pack.activity = { name: run.name, params: run.params, state: {} };
|
|
123
|
+
const path = relative(ws.root, findActivityFile(ws.root, tutor, run.name)).split(sep).join('/');
|
|
124
|
+
const text = await readFile(join(ws.root, path), 'utf8').catch(() => null);
|
|
125
|
+
if (text === null) {
|
|
126
|
+
pack.activityGuide = `缺:${path} 不在,照你老师文件里的做`;
|
|
127
|
+
return [`activity ${run.name}:${path} 不在,老师没拿到「老师怎么带」`];
|
|
128
|
+
}
|
|
129
|
+
const a = parseActivity(text).activity;
|
|
130
|
+
pack.activity.state = await activityState(ws, tutor, where.date, where.messages, where.thread, run, a, where.now);
|
|
131
|
+
// activity 已经是这个会话的系统提示:不再在第一条里带原文
|
|
132
|
+
if (inSystem) {
|
|
133
|
+
pack.activityGuide = ACTIVITY_IN_SYSTEM;
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
const mine = tutorCardKinds(ws.config.tutors[tutor]?.cards);
|
|
137
|
+
const cards = activityCards(a, mine ?? undefined);
|
|
138
|
+
const body = renderTutorSection(a, { params: run.params, cards });
|
|
139
|
+
if (body === null)
|
|
140
|
+
return [];
|
|
141
|
+
// 这个 activity 要用、老师的板书写法里没有的卡:写法跟在后面(不动系统提示)
|
|
142
|
+
const extra = mine ? a.cards.filter((k) => !mine.includes(k)) : [];
|
|
143
|
+
const notes = pack.notes ?? [];
|
|
144
|
+
const at = Math.max(notes.findIndex((n) => n.role === 'boardGuide'), notes.findIndex((n) => n.role === 'rules'));
|
|
145
|
+
notes.splice(at + 1, 0, { role: 'activityGuide', path, text: extra.length ? `${body}\n\n## 这个 activity 多用的卡\n\n${cardGuidesFor(extra)}` : body });
|
|
146
|
+
pack.notes = notes;
|
|
147
|
+
pack.activityGuide = path;
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
/** activity 在系统提示里时,上下文包 activityGuide 那一行 */
|
|
151
|
+
export const ACTIVITY_IN_SYSTEM = '已在你的系统提示里(这个话题的 activity,照它带)';
|
|
152
|
+
export async function activityParts(ws, tutor, run) {
|
|
153
|
+
const a = await readActivity(ws, tutor, run.name);
|
|
154
|
+
if (!a?.tutor)
|
|
155
|
+
return null;
|
|
156
|
+
const kinds = [...new Set(['text', ...a.cards.filter((k) => BOARD_CARD_KINDS.includes(k))])];
|
|
157
|
+
const body = renderTutorSection(a, { params: run.params, cards: kinds }) ?? '';
|
|
158
|
+
const display = ws.config.tutors[tutor]?.display ?? tutor;
|
|
159
|
+
return { body, kinds, guide: boardGuideFor(kinds), memory: { agent: `${tutor}/${run.name}`, display: `${display}·${run.name}` } };
|
|
160
|
+
}
|
|
161
|
+
/** 这位老师的板书写法:没写 cards → 出厂的 cotutor-board SKILL.md;写了 → 按清单从包里现拼(boardGuideFor)。读不到 SKILL.md → null */
|
|
162
|
+
export async function boardGuideOf(ws, tutor) {
|
|
163
|
+
const kinds = tutorCardKinds(ws.config.tutors[tutor]?.cards);
|
|
164
|
+
if (kinds)
|
|
165
|
+
return boardGuideFor(kinds);
|
|
166
|
+
try {
|
|
167
|
+
return boardGuideBody(await readFile(join(ws.root, BOARD_GUIDE_PATH), 'utf8'));
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 系统提示(2026-09-19 起板书写法跟着进系统提示):有脸的老师、板书没关才带板书写法,上下文包只写一行「已在系统提示里」。
|
|
175
|
+
* activity 话题:系统提示换成 activity 的人设与做法 + 它的卡的写法;工具人(没有守则)的 activity 退回老师文件 + 第一条带原文(attachActivity)。
|
|
176
|
+
*/
|
|
177
|
+
async function systemParts(ws, tutor, policy, pack, act = null) {
|
|
178
|
+
const boardOff = policy.board === 'off';
|
|
179
|
+
if (act && takesTutorRules(tutor)) {
|
|
180
|
+
if (!boardOff)
|
|
181
|
+
pack.boardGuide = BOARD_GUIDE_IN_SYSTEM;
|
|
182
|
+
return { systemBody: boardOff ? act.body : `${act.body}\n\n${act.guide}`, activityInSystem: true };
|
|
183
|
+
}
|
|
184
|
+
const agentBody = await readAgentBody(ws, tutor);
|
|
185
|
+
if (!takesTutorRules(tutor) || boardOff)
|
|
186
|
+
return { systemBody: agentBody, activityInSystem: false };
|
|
187
|
+
const guide = await boardGuideOf(ws, tutor);
|
|
188
|
+
if (!guide) {
|
|
189
|
+
pack.boardGuide = `缺:${BOARD_GUIDE_PATH} 不在(cotutor upgrade 补),照你会的写`;
|
|
190
|
+
return { systemBody: agentBody, activityInSystem: false };
|
|
191
|
+
}
|
|
192
|
+
pack.boardGuide = BOARD_GUIDE_IN_SYSTEM;
|
|
193
|
+
return { systemBody: `${agentBody}\n\n${guide}`, activityInSystem: false };
|
|
194
|
+
}
|
|
195
|
+
/** 这轮带的笔记版本:role → `路径@hash`(记进消息;同一话题下一轮对得上就不再带原文) */
|
|
196
|
+
export function noteVersions(pack) {
|
|
197
|
+
return Object.fromEntries((pack.notes ?? []).map((n) => [n.role, `${n.path}@${textHash(n.text)}`]));
|
|
198
|
+
}
|
|
199
|
+
/** 续会话时:上一轮这个话题已经带过、版本没变的笔记去掉原文,YAML 里注明「未变」 */
|
|
200
|
+
export function dropSeenNotes(pack, seen) {
|
|
201
|
+
const now = noteVersions(pack);
|
|
202
|
+
const keep = (pack.notes ?? []).filter((n) => seen[n.role] !== now[n.role]);
|
|
203
|
+
const out = { ...pack, notes: keep };
|
|
204
|
+
for (const n of pack.notes ?? [])
|
|
205
|
+
if (!keep.includes(n))
|
|
206
|
+
out[n.role] = `${pack[n.role]}(未变,原文在本话题前面)`;
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
/** 这个话题里最近一次记下的笔记版本(新话题、没记过 → {}) */
|
|
210
|
+
export function seenNotes(messages, thread) {
|
|
211
|
+
const ths = threads(messages);
|
|
212
|
+
for (let i = messages.length - 1; i >= 0; i--)
|
|
213
|
+
if (ths[i] === thread && messages[i].notes)
|
|
214
|
+
return messages[i].notes;
|
|
215
|
+
return {};
|
|
216
|
+
}
|
|
217
|
+
/** 只读工具读得到的目录:workspace 根(作业照片、技能)与 vault(教材、日记) */
|
|
218
|
+
export function readDirsOf(ws) {
|
|
219
|
+
return [...new Set([ws.root, ws.paths.vault])];
|
|
220
|
+
}
|
|
221
|
+
/** 干跑:现在给这位老师发这句话,上下文包会是什么样、每段从哪来、截了多少;不起模型、不写盘 */
|
|
222
|
+
export async function packDryRun(ws, tutor, input) {
|
|
223
|
+
const t = ws.config.tutors[tutor];
|
|
224
|
+
if (!t)
|
|
225
|
+
throw new UsageError(`没有叫 ${tutor} 的老师;cotutor.json 的 tutors 里有:${Object.keys(ws.config.tutors).join('、')}`);
|
|
226
|
+
const report = {};
|
|
227
|
+
const act = input.activity ? await activityParts(ws, tutor, input.activity) : null;
|
|
228
|
+
const pack = await gatherContext(ws, tutor, { from: input.from, at: input.at, ...(input.from === 'kid' ? { query: input.text } : {}), ...(act ? { agent: act.memory.agent, kinds: act.kinds } : {}) }, report);
|
|
229
|
+
const policy = resolvePolicy(ws.config, tutor);
|
|
230
|
+
if (policy.board === 'off')
|
|
231
|
+
pack.board = 'off';
|
|
232
|
+
// 和 send 同一条规矩:孩子的话不带工具时,只有工具才用得上的路径不进包
|
|
233
|
+
if (policy.tools !== 'on' && input.from === 'kid') {
|
|
234
|
+
delete pack.refs;
|
|
235
|
+
delete pack.vault;
|
|
236
|
+
}
|
|
237
|
+
const parts = await systemParts(ws, tutor, policy, pack, act);
|
|
238
|
+
if (input.activity)
|
|
239
|
+
await attachActivity(ws, tutor, pack, input.activity, { date: localDate(input.at), messages: [], thread: '', now: input.at.getTime() }, parts.activityInSystem);
|
|
240
|
+
return { prompt: buildContextPack(pack, input.text, policy.contextPack), pack, report };
|
|
241
|
+
}
|
|
242
|
+
/** 上下文包的 vault: 段:root 绝对,其余角色相对 root;在 root 外面(家长把日记指到别处)就给绝对路径 */
|
|
243
|
+
export function vaultPack(paths) {
|
|
244
|
+
const root = paths.vault;
|
|
245
|
+
const out = { root };
|
|
246
|
+
for (const r of VAULT_PACK_ROLES) {
|
|
247
|
+
const p = paths[r];
|
|
248
|
+
if (!p)
|
|
249
|
+
continue;
|
|
250
|
+
const rel = relative(root, p);
|
|
251
|
+
out[r] = !rel || rel.startsWith('..') || isAbsolute(rel) ? p : rel.split(sep).join('/');
|
|
252
|
+
}
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
export class Runner {
|
|
256
|
+
active = new Map();
|
|
257
|
+
/** 还在后台跑的资产生成(测试与关服前 flush) */
|
|
258
|
+
background = new Set();
|
|
259
|
+
getWs;
|
|
260
|
+
opts;
|
|
261
|
+
listeners = new Set();
|
|
262
|
+
/** noteEvent 每个事件文件一条追加链 */
|
|
263
|
+
notes = new Map();
|
|
264
|
+
/** 脑执行:请模型、读输出(src/brain/) */
|
|
265
|
+
brain;
|
|
266
|
+
/** key、假端点从哪读的环境(测试注入) */
|
|
267
|
+
get env() {
|
|
268
|
+
return this.opts.env;
|
|
269
|
+
}
|
|
270
|
+
/** 当前的 workspace(cotutor.json 热重载后是新的;实时老师经它拿) */
|
|
271
|
+
get ws() {
|
|
272
|
+
return this.getWs();
|
|
273
|
+
}
|
|
274
|
+
/** 画图卡交给谁画(测试与 mock 注入假的) */
|
|
275
|
+
draw;
|
|
276
|
+
constructor(getWs, opts = {}) {
|
|
277
|
+
this.getWs = getWs;
|
|
278
|
+
this.opts = opts;
|
|
279
|
+
this.draw = opts.draw ?? makeDraw({ env: opts.env });
|
|
280
|
+
// API 运行时的 key 与配音、画图同一套选法(~/.config/cotutor/):Token Plan 有这个模型就走它,否则百炼按量;只有按量 key、没有业务空间 id 也能用(走公网域名)。
|
|
281
|
+
// COTUTOR_BRAIN_ENDPOINT:测试与探针指到假百炼
|
|
282
|
+
const env = opts.env ?? process.env;
|
|
283
|
+
this.brain = createBrain({ workspace: () => { const ws = this.getWs(); return { config: ws.config, root: ws.root }; }, line: (model) => compatibleLine(model, env), endpoint: env.COTUTOR_BRAIN_ENDPOINT || undefined });
|
|
284
|
+
}
|
|
285
|
+
/** 孩子端发一条的验与拼(HTTP 与实时老师共用,./kid-input.ts):拼成 send 的入参,或回 reject;不起老师 */
|
|
286
|
+
accept(tutor, body, date, deviceId) {
|
|
287
|
+
return kidInput(this.getWs(), tutor, body, date, deviceId);
|
|
288
|
+
}
|
|
289
|
+
running(tutor) {
|
|
290
|
+
const a = this.active.get(tutor);
|
|
291
|
+
return a ? { job: a.job, date: a.date } : null;
|
|
292
|
+
}
|
|
293
|
+
/** 等所有后台资产生成完(测试用) */
|
|
294
|
+
async flush() {
|
|
295
|
+
while (this.background.size)
|
|
296
|
+
await Promise.all([...this.background]);
|
|
297
|
+
}
|
|
298
|
+
/** 订阅每一轮的事件(runtime/journal/events.ts;cotutor send 现场打印、serve --trace);返回退订函数。事件同时追加到 <日期>.<job>.events.jsonl */
|
|
299
|
+
onEvent(fn) {
|
|
300
|
+
this.listeners.add(fn);
|
|
301
|
+
return () => void this.listeners.delete(fn);
|
|
302
|
+
}
|
|
303
|
+
/** 别处给某一轮补事件(实时老师的嘴,src/runtime/live/call.ts;t 已经换成离这一轮老师进程起来的毫秒):追加到 events.jsonl、给订阅者;这一轮跑完了也行 */
|
|
304
|
+
async noteEvent(tutor, date, job, event) {
|
|
305
|
+
const file = conversationFiles(this.getWs().dirs.conversations, tutor, date).events(job);
|
|
306
|
+
// 同一个文件按序追加(几条一起来时不乱序)
|
|
307
|
+
const chain = (this.notes.get(file) ?? Promise.resolve()).then(() => appendFile(file, `${JSON.stringify(event)}\n`)).catch(() => { });
|
|
308
|
+
this.notes.set(file, chain);
|
|
309
|
+
void chain.finally(() => { if (this.notes.get(file) === chain)
|
|
310
|
+
this.notes.delete(file); });
|
|
311
|
+
await chain;
|
|
312
|
+
for (const fn of this.listeners) {
|
|
313
|
+
try {
|
|
314
|
+
fn({ tutor, date, job, event });
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
/* 订阅者的错不影响 */
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/** 正在回的那条已经出来的板书(流式);没在跑或还没出卡 → null */
|
|
322
|
+
partial(tutor) {
|
|
323
|
+
return this.active.get(tutor)?.partial ?? null;
|
|
324
|
+
}
|
|
325
|
+
async send(tutor, input) {
|
|
326
|
+
const ws = this.getWs();
|
|
327
|
+
const t = ws.config.tutors[tutor];
|
|
328
|
+
if (!t)
|
|
329
|
+
throw new UsageError(`没有叫 ${tutor} 的老师;cotutor.json 的 tutors 里有:${Object.keys(ws.config.tutors).join('、')}`);
|
|
330
|
+
if (!t.enabled)
|
|
331
|
+
throw new UsageError(`${t.display} 已关闭(cotutor.json tutors.${tutor}.enabled = false),打开再发`);
|
|
332
|
+
const photos = input.photos?.filter(Boolean) ?? [];
|
|
333
|
+
// 小课堂画的一处带一张截图:和照片一样进上下文包 photos:(排在孩子拍的后面),这一轮带工具读图;消息的 photos 只记孩子拍的
|
|
334
|
+
const shots = input.lecture?.marks?.flatMap((m) => (m.image ? [m.image] : [])) ?? [];
|
|
335
|
+
// 纸上交的画:那张画与清空前存的那几版也给老师 Read,排在照片与小课堂截图后面
|
|
336
|
+
const drawn = input.drawing ? [input.drawing.image, ...(input.drawing.cleared ?? [])] : [];
|
|
337
|
+
const seen = [...photos, ...shots, ...drawn];
|
|
338
|
+
const text = input.text.trim() || (input.action === 'submit' ? '(交了答案,没说话)' : photos.length ? (photos.length === 1 ? '(拍了一张)' : `(拍了 ${photos.length} 张)`) : input.drawing ? DRAW_ONLY_TEXT : '');
|
|
339
|
+
if (!text)
|
|
340
|
+
throw new UsageError('消息是空的');
|
|
341
|
+
const busy = this.active.get(tutor);
|
|
342
|
+
if (busy)
|
|
343
|
+
throw new BusyError(tutor, busy.job);
|
|
344
|
+
const now = (this.opts.now ?? (() => new Date()))();
|
|
345
|
+
const date = input.date ?? localDate(now);
|
|
346
|
+
// 占位要在第一个 await 之前(2026-10-17):查完 busy 到 spawn 之间十几个 await,孩子连点两下两条都过检查、算出同一个 job、起两个进程。
|
|
347
|
+
// 占位先进 map,job 等读了索引再填;起不来(索引坏、话题不在、CLI 没起)就放掉占位
|
|
348
|
+
const active = { job: '(准备中)', date, partial: null, done: new Promise(() => { }) };
|
|
349
|
+
this.active.set(tutor, active);
|
|
350
|
+
try {
|
|
351
|
+
return await this.begin(ws, tutor, input, { t, now, date, photos, seen, text }, active);
|
|
352
|
+
}
|
|
353
|
+
catch (err) {
|
|
354
|
+
if (this.active.get(tutor) === active)
|
|
355
|
+
this.active.delete(tutor);
|
|
356
|
+
throw err;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** send 的后半:占位已在 active 里,这里读索引、拼包、起进程;抛了由 send 放掉占位 */
|
|
360
|
+
async begin(ws, tutor, input, prep, active) {
|
|
361
|
+
const { t, now, date, photos, seen, text } = prep;
|
|
362
|
+
const index = await readIndex(ws, tutor, date);
|
|
363
|
+
const job = jobId(now, index.messages.length + 1);
|
|
364
|
+
// 话题:新开(newThread / 没指定话题的系统消息 / 今天第一条)= 自己的 job;指定的要在这天的索引里;缺省接当前话题。会话按话题 resume
|
|
365
|
+
const known = threads(index.messages);
|
|
366
|
+
let thread;
|
|
367
|
+
if (input.newThread || (input.from === 'system' && !input.thread) || !known.length)
|
|
368
|
+
thread = job;
|
|
369
|
+
else if (input.thread) {
|
|
370
|
+
if (!known.includes(input.thread))
|
|
371
|
+
throw new UsageError(`${date} 没有话题 ${input.thread};有:${[...new Set(known)].join('、')}`);
|
|
372
|
+
thread = input.thread;
|
|
373
|
+
}
|
|
374
|
+
else
|
|
375
|
+
thread = known[known.length - 1];
|
|
376
|
+
const { name: runtimeName, runtime } = getRuntime(ws.config, input.runtime ?? t.runtime);
|
|
377
|
+
// 话题只属于开它的那种运行时(2026-10-31):会话是别的运行时的(换过运行时)就接不上,这条开新话题,不把两种会话混在一个话题里;记账在 bookkeep 里先跳过了
|
|
378
|
+
if (thread !== job && !input.bookkeep && !sameRuntime(index, thread, runtimeName))
|
|
379
|
+
thread = job;
|
|
380
|
+
const fresh = thread === job;
|
|
381
|
+
const session = fresh ? null : sessionFor(index, thread);
|
|
382
|
+
// 长话题整段压缩(API 运行时):之前的模型历史不再发,前情照索引拼;这一轮笔记带全文,不写「未变」(原文所在的那几轮不发了)
|
|
383
|
+
const compact = !fresh && isApiRuntime(runtime) && dueCompact(index.messages, known, thread);
|
|
384
|
+
// activity:新话题照这条带的,续话题照话题第一条记的。系统提示、卡、记忆都是它的(记账轮也是,接着同一个会话);上下文包的 activity 段记账轮不带
|
|
385
|
+
// 隔天「接着」是新话题:没带 activity 就沿用被接的那个话题的(参数也是那时的),不然退回老师文件,没有那套做法和卡
|
|
386
|
+
const threadRun = fresh ? (input.activity ?? (input.continues ? threadActivity((await readIndex(ws, tutor, input.continues.date)).messages, input.continues.thread) : null)) : threadActivity(index.messages, thread);
|
|
387
|
+
const act = threadRun ? await activityParts(ws, tutor, threadRun) : null;
|
|
388
|
+
// 取材的查询:孩子这句话,加首页按钮的讲法(系统任务不查)
|
|
389
|
+
const query = input.from === 'kid' ? [text, input.home?.brief].filter(Boolean).join('\n') : '';
|
|
390
|
+
const gathered = await gatherContext(ws, tutor, { from: input.from, at: now, focus: input.focus, ...(query ? { query } : {}), ...(act ? { agent: act.memory.agent, kinds: act.kinds } : {}) });
|
|
391
|
+
const policy = resolvePolicy(ws.config, tutor);
|
|
392
|
+
const { systemBody, activityInSystem } = await systemParts(ws, tutor, policy, gathered, act);
|
|
393
|
+
const activityRun = input.bookkeep ? null : threadRun;
|
|
394
|
+
const activityWarn = activityRun ? await attachActivity(ws, tutor, gathered, activityRun, { date, messages: index.messages, thread, now: now.getTime() }, activityInSystem) : [];
|
|
395
|
+
// 家长笔记原文:新会话整篇带;续会话时这个话题带过、没改的只写「未变」
|
|
396
|
+
const notes = noteVersions(gathered);
|
|
397
|
+
const pack = session && !compact ? dropSeenNotes(gathered, seenNotes(index.messages, thread)) : gathered;
|
|
398
|
+
// 入口文件是可选的(2026-10-30,《vault重构方案》§8:ray 的三篇都是空壳,讲法偏好家长写在首页按钮里):缺了不再每轮提醒,doctor 里提示一次
|
|
399
|
+
if (policy.board === 'off')
|
|
400
|
+
pack.board = 'off';
|
|
401
|
+
// 这个话题里上一轮之后孩子在卡上做的事:逐张 describe 进上下文包,也记进这条消息(家长视图「孩子在板书上做的」);新话题不带
|
|
402
|
+
// 画板交上来的图(状态里是相对 workspace 根的路径):包里那行换成绝对路径(Read 只认绝对路径,老师的 cwd 在 agents/<名>/),消息里照旧记相对的
|
|
403
|
+
const changed = fresh || input.bookkeep ? [] : changedCards(index, await readCardStates(ws, tutor, date), thread);
|
|
404
|
+
let drawings = 0;
|
|
405
|
+
const cards = await Promise.all(changed.map(async (c) => {
|
|
406
|
+
const card = index.messages.find((m) => m.job === c.job)?.section?.cards[c.n];
|
|
407
|
+
const id = cardId(c.job, c.n);
|
|
408
|
+
if (!card)
|
|
409
|
+
return { card: id, text: JSON.stringify(c.file.state) };
|
|
410
|
+
const st = card.kind === 'canvas' ? c.file.state : null;
|
|
411
|
+
const image = typeof st?.image === 'string' ? st.image : undefined;
|
|
412
|
+
// 清空前存的那几版也是图,一样换绝对路径、一样要 Read
|
|
413
|
+
const cleared = Array.isArray(st?.cleared) ? st.cleared : [];
|
|
414
|
+
if (!image && !cleared.some((x) => typeof x?.image === 'string'))
|
|
415
|
+
return { card: id, text: describeCard(card, c.file.state) };
|
|
416
|
+
drawings++;
|
|
417
|
+
const abs = { ...st, ...(image ? { image: join(ws.root, image) } : {}), cleared: cleared.map((x) => (typeof x?.image === 'string' ? { ...x, image: join(ws.root, x.image) } : x)) };
|
|
418
|
+
return { card: id, text: describeCard(card, c.file.state), pack: describeCard(card, abs) };
|
|
419
|
+
}));
|
|
420
|
+
// 这轮带不带工具(policy.tools,2026-10-04):孩子说的话缺省不带,只凭上下文包答;带照片的、交了画的(要 Read 看图)、
|
|
421
|
+
// 系统任务带只读的(以前是整份白名单,照片里一句话就能让老师拿 Bash 读写 workspace 外);policy.tools: on 的老师总是整份。
|
|
422
|
+
// 不带的那轮,只有工具才用得上的路径(refs / vault)不进包
|
|
423
|
+
const tools = policy.tools === 'on' ? 'on' : input.from !== 'kid' || seen.length > 0 || drawings > 0 ? 'read' : 'off';
|
|
424
|
+
if (tools === 'off') {
|
|
425
|
+
delete pack.refs;
|
|
426
|
+
delete pack.vault;
|
|
427
|
+
}
|
|
428
|
+
// 画图卡:这个话题上一节有画图卡,告诉老师画好没有(老师看不到图)
|
|
429
|
+
if (!fresh && !input.bookkeep) {
|
|
430
|
+
const prevAt = index.messages.map((m, i) => ({ m, th: known[i] })).reverse().find((x) => x.th === thread && x.m.result === 'ok' && x.m.section);
|
|
431
|
+
if (prevAt?.m.section?.cards.some((c) => c.kind === 'paint'))
|
|
432
|
+
for (const line of await paintLines(ws, tutor, date, prevAt.m.job, prevAt.m.section))
|
|
433
|
+
cards.push({ card: line.slice(0, line.indexOf(' ')), text: line.slice(line.indexOf(' ') + 1) });
|
|
434
|
+
}
|
|
435
|
+
if (cards.length)
|
|
436
|
+
pack.cards = cards.map((c) => `${c.card} ${c.pack ?? c.text}`);
|
|
437
|
+
if (seen.length) {
|
|
438
|
+
pack.photos = seen;
|
|
439
|
+
pack.photoFiles = seen.map((p) => join(ws.root, p));
|
|
440
|
+
}
|
|
441
|
+
// 纸上的画排在 photos 最后:它自己,再是清掉的那几版
|
|
442
|
+
if (input.drawing)
|
|
443
|
+
pack.drawing = drawingPackLine(input.drawing, seen.length - (input.drawing.cleared?.length ?? 0));
|
|
444
|
+
if (input.home)
|
|
445
|
+
pack.home = input.home;
|
|
446
|
+
// 小课堂:课包现读;读不出来(删了、坏了)就不带,这条照发,家长端提醒
|
|
447
|
+
const lecture = input.lecture ? await readLecture(ws, input.lecture.bundle) : null;
|
|
448
|
+
const marks = input.lecture?.marks?.length ? storedMarks(lecture, input.lecture.marks, photos.length) : [];
|
|
449
|
+
if (input.lecture && lecture)
|
|
450
|
+
pack.lecture = lecturePack(lecture, input.lecture, marks);
|
|
451
|
+
const lectureWarn = input.lecture && !lecture ? [`小课堂:${input.lecture.bundle} 读不出来(bundles/ 与 lectures/ 下都没有能放的),老师没拿到课的内容`] : [];
|
|
452
|
+
const continued = input.continues && fresh ? (input.continues.pack ?? (await continueContext(ws, tutor, input.continues.date, input.continues.thread))) : null;
|
|
453
|
+
if (continued)
|
|
454
|
+
pack.continue = continued;
|
|
455
|
+
if (input.live) {
|
|
456
|
+
const l = input.live;
|
|
457
|
+
pack.live = { ...(l.caught ? { caught: l.caught } : {}), ...(l.cut !== undefined ? { cut: l.cut } : {}), ...(l.warmup?.length ? { warmup: l.warmup } : {}), ...(l.phone !== undefined ? { phone: l.phone } : {}), ...(l.app ? { app: l.app } : {}), ...(l.draw?.length ? { draw: l.draw } : {}) };
|
|
458
|
+
}
|
|
459
|
+
const prompt = buildContextPack(pack, text, policy.contextPack);
|
|
460
|
+
const req = { agent: tutor, prompt, systemBody, runtime: input.runtime ?? t.runtime, effort: policy.effort, tools, readDirs: readDirsOf(ws), session, ...(compact ? { compact: { digest: threadDigest(index.messages.filter((_, i) => known[i] === thread)) } } : {}), ...(pack.photoFiles?.length ? { images: pack.photoFiles } : {}) };
|
|
461
|
+
const plan = this.brain.plan(req);
|
|
462
|
+
// 原声:落在这轮旁边(删话题一起删);写不下就当没有,消息照发
|
|
463
|
+
const started = addMessage(index, { job, thread, at: pack.at, from: input.from, text, focus: input.focus, ...(input.action ? { action: input.action } : {}), ...(cards.length ? { cards: cards.map(({ card, text }) => ({ card, text })) } : {}), ...(photos.length ? { photos } : {}), ...(input.drawing ? { drawing: input.drawing } : {}), ...(input.device ? { device: input.device } : {}), ...(input.deviceId ? { deviceId: input.deviceId } : {}), ...(input.bookkeep ? { bookkeep: input.bookkeep } : {}), ...(input.tidy ? { tidy: true } : {}), ...(input.replayOf ? { replayOf: input.replayOf } : {}), ...(input.eval ? { eval: true } : {}), ...(input.live ? { live: true } : {}), ...(input.via ? { via: input.via } : {}), ...(continued && input.continues ? { continues: { date: input.continues.date, thread: input.continues.thread } } : {}), ...(input.lecture ? { lecture: { bundle: input.lecture.bundle, title: lecture?.title ?? input.lecture.bundle, watchedMs: input.lecture.watchedMs, finished: input.lecture.finished, pauses: input.lecture.pauses, ...(input.lecture.again ? { again: true } : {}), ...(lecture?.video ? { video: true } : {}), ...(marks.length ? { marks } : {}), ...(input.lecture.log?.length ? { log: input.lecture.log } : {}) } } : {}), ...(Object.keys(notes).length ? { notes } : {}), ...(fresh && activityRun ? { activity: activityRun } : {}), ...(fresh && input.talk ? { talk: input.talk } : {}), ...(compact ? { compacted: true } : {}), ...(lectureWarn.length || activityWarn.length ? { warnings: [...lectureWarn, ...activityWarn] } : {}), result: 'running', artifacts: [], runtime: plan.runtime });
|
|
464
|
+
await writeIndex(ws, started);
|
|
465
|
+
// 运行记录(《vault重构方案》§7):除了老师文件与技能,还记这轮带进上下文的每篇笔记、替老师查的每个文件,事后重建得出老师看到的是哪一版
|
|
466
|
+
const notesSent = (pack.notes ?? []).map((n) => ({ role: n.role, path: n.path, hash: textHash(n.text), chars: n.text.length, ...(n.cut ? { cut: true } : {}) }));
|
|
467
|
+
await writeRunFile(ws, tutor, date, job, { at: pack.at, prompt, plan, agentBody: true, sources: { ...(await snapshotSources(ws, tutor)), notes: notesSent, retrieved: gathered.retrieved } });
|
|
468
|
+
active.job = job;
|
|
469
|
+
if (input.live)
|
|
470
|
+
active.live = true;
|
|
471
|
+
// 实时的一轮讲稿是嘴念的,不按 replyMaxChars 截(截了孩子听到半句:10-20「题目已经…」后面「你补上 ēng 就行」没了)
|
|
472
|
+
const runPolicy = input.live ? { ...policy, replyMaxChars: Number.MAX_SAFE_INTEGER } : policy;
|
|
473
|
+
active.done = this.spawn(ws, tutor, date, job, thread, req, plan, runPolicy, active).finally(() => {
|
|
474
|
+
if (this.active.get(tutor) === active)
|
|
475
|
+
this.active.delete(tutor);
|
|
476
|
+
});
|
|
477
|
+
return { tutor, date, job, thread, plan, done: active.done };
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* 记账(《obsidian仓库设计.md》§4 / §6):家长晚上点一次。这天索引里每个还没记过、有孩子的话或打了分的话题,各起一轮记账任务
|
|
481
|
+
* (from: system、resume 那个话题的会话,消息正文是 bookkeepingPrompt),顺着跑,老师回的「## 记账」段由 settleDiary 落进日记。
|
|
482
|
+
* 立刻返回排了哪些、跳过哪些;链在后台跑(flush 会等,done 也行)。老师正忙 → BusyError。
|
|
483
|
+
* tidy: false 记完不整理记忆(自动记账记今天的话题时,孩子晚上可能还来,src/runtime/auto-bookkeep.ts)。
|
|
484
|
+
*/
|
|
485
|
+
async bookkeep(tutor, date, only, opts = {}) {
|
|
486
|
+
const ws = this.getWs();
|
|
487
|
+
const t = ws.config.tutors[tutor];
|
|
488
|
+
if (!t)
|
|
489
|
+
throw new UsageError(`没有叫 ${tutor} 的老师;cotutor.json 的 tutors 里有:${Object.keys(ws.config.tutors).join('、')}`);
|
|
490
|
+
// 上次记到一半断了的(写完日记、没记进索引):先对照日记补记,不然这里当没记过又记一遍。老师正跑着时它的 pending 是真在写,不碰
|
|
491
|
+
const read = await readIndex(ws, tutor, date);
|
|
492
|
+
const recovered = this.active.get(tutor) ? { index: read, changed: false } : await recoverPending(ws, read);
|
|
493
|
+
if (recovered.changed)
|
|
494
|
+
await writeIndex(ws, recovered.index);
|
|
495
|
+
const index = recovered.index;
|
|
496
|
+
// 整理记忆那轮自成一个话题,不是学的东西,不记账
|
|
497
|
+
const all = [...new Set(threads(index.messages).filter((_, i) => !index.messages[i].tidy))];
|
|
498
|
+
const queued = [];
|
|
499
|
+
const skipped = [];
|
|
500
|
+
const runtimeName = t.runtime ?? ws.config.runtimes.default;
|
|
501
|
+
for (const th of only ?? all) {
|
|
502
|
+
if (!all.includes(th))
|
|
503
|
+
skipped.push({ thread: th, why: `${date} 没有这个话题` });
|
|
504
|
+
else if (index.booked[th])
|
|
505
|
+
skipped.push({ thread: th, why: `记过了(${index.booked[th]})` });
|
|
506
|
+
// 换过运行时:老师接不上那时的对话,硬记就是编(2026-10-31 副本上试过,还被整理进了记忆)
|
|
507
|
+
else if (!sameRuntime(index, th, runtimeName))
|
|
508
|
+
skipped.push({ thread: th, why: `这个话题是在「${index.sessions[th].runtime}」下聊的,老师现在用「${runtimeName}」,接不上那时的对话,不记账`, oldRuntime: index.sessions[th].runtime });
|
|
509
|
+
else if (!only && !kidQuestions(index.messages, th).length && index.ratings[th] === undefined)
|
|
510
|
+
skipped.push({ thread: th, why: '没有孩子的话也没打分' });
|
|
511
|
+
else
|
|
512
|
+
queued.push(th);
|
|
513
|
+
}
|
|
514
|
+
if (!queued.length)
|
|
515
|
+
return { queued, skipped, done: Promise.resolve() };
|
|
516
|
+
const busy = this.active.get(tutor);
|
|
517
|
+
if (busy)
|
|
518
|
+
throw new BusyError(tutor, busy.job);
|
|
519
|
+
const headings = textbookHeadings(await readTextbooks(ws));
|
|
520
|
+
const chain = (async () => {
|
|
521
|
+
for (const th of queued) {
|
|
522
|
+
try {
|
|
523
|
+
const idx = await readIndex(ws, tutor, date);
|
|
524
|
+
// 话题里有照片:提示词多一句,让 summary 把册子 / 页 / 题号 / 题面写成文字(日记不存图,《obsidian仓库设计.md》§5)
|
|
525
|
+
const ths = threads(idx.messages);
|
|
526
|
+
const photos = idx.messages.reduce((n, m, i) => n + (ths[i] === th ? (m.photos?.length ?? 0) : 0), 0);
|
|
527
|
+
const started = await this.send(tutor, { from: 'system', text: bookkeepingPrompt({ thread: th, rating: idx.ratings[th], keepScore: ws.config.vault.keepScore, headings, photos }), thread: th, date, bookkeep: { thread: th } });
|
|
528
|
+
await started.done;
|
|
529
|
+
}
|
|
530
|
+
catch {
|
|
531
|
+
/* 这条记不了(老师忙 / 起不来),下一条照记;索引里没记 booked,再点一次记账会重试 */
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
// 账记完,这位老师整理一遍自己的记忆(新会话,结果直接落盘;《obsidian仓库设计.md》§8)
|
|
535
|
+
if (opts.tidy === false)
|
|
536
|
+
return;
|
|
537
|
+
try {
|
|
538
|
+
const tidy = await this.tidyMemory(tutor, date);
|
|
539
|
+
if (tidy)
|
|
540
|
+
await tidy.done;
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
/* 整理不了(老师忙 / 起不来)不影响记账;下次记账再整理 */
|
|
544
|
+
}
|
|
545
|
+
})();
|
|
546
|
+
this.background.add(chain);
|
|
547
|
+
void chain.finally(() => this.background.delete(chain));
|
|
548
|
+
return { queued, skipped, done: chain };
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* 记账后整理记忆:这位老师有记忆文件、里面有条目才起一轮(新会话、from: system、tidy);没有就返回 null。
|
|
552
|
+
* 老师回的「## 记忆」段照单落盘(settleMemory 不限条数)。
|
|
553
|
+
*/
|
|
554
|
+
async tidyMemory(tutor, date) {
|
|
555
|
+
const ws = this.getWs();
|
|
556
|
+
const memory = (await scanVault(ws)).notes.find((n) => n.props.cotutor === 'memory' && n.props.agent?.trim() === tutor);
|
|
557
|
+
const count = memory ? memoryCount(memory.text) : 0;
|
|
558
|
+
if (!count)
|
|
559
|
+
return null;
|
|
560
|
+
return this.send(tutor, { from: 'system', text: tidyMemoryPrompt({ date, count, cap: MEMORY_TIDY_CAP, diaryFile: join(ws.paths.diary, `${date}.md`) }), date, tidy: true });
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* 「## 记忆」段 → 建议与要怎么改,还不写:讲课的轮每轮最多 MEMORY_MAX_PER_TURN 条(整理轮不限);回放、eval 不写。
|
|
564
|
+
* 证据对孩子在这个话题里说过的话(整理轮自成话题,对这一天的);有脸的老师写的 `孩子:` 进孩子的记忆。
|
|
565
|
+
* seen = 这轮上下文包里老师看到的那两篇的版本(消息的 notes.memory / notes.child,`路径@hash`)
|
|
566
|
+
*/
|
|
567
|
+
async planMemoryOf(ws, tutor, date, items, asked, said, clip, owner) {
|
|
568
|
+
if (asked?.replayOf || asked?.eval)
|
|
569
|
+
return { proposals: [], plan: null, child: null, warnings: [asked.eval ? 'eval 不写记忆' : '回放不写记忆'] };
|
|
570
|
+
const seenOf = (v) => {
|
|
571
|
+
const at = v?.lastIndexOf('@') ?? -1;
|
|
572
|
+
return v && at > 0 ? { path: v.slice(0, at), hash: v.slice(at + 1), clip } : undefined;
|
|
573
|
+
};
|
|
574
|
+
const seen = seenOf(asked?.notes?.memory);
|
|
575
|
+
const childSeen = seenOf(asked?.notes?.child);
|
|
576
|
+
const shared = Boolean(ws.config.tutors[tutor]) && tutor.endsWith('-tutor');
|
|
577
|
+
return planMemory(ws, { agent: owner?.agent ?? tutor, display: owner?.display ?? ws.config.tutors[tutor]?.display ?? tutor, items, max: asked?.tidy ? null : MEMORY_MAX_PER_TURN, date, said, shared, ...(seen ? { seen } : {}), ...(childSeen ? { childSeen } : {}) });
|
|
578
|
+
}
|
|
579
|
+
async spawn(ws, tutor, date, job, thread, req, plan, policy, active) {
|
|
580
|
+
const replyMaxChars = policy.replyMaxChars;
|
|
581
|
+
const files = conversationFiles(ws.dirs.conversations, tutor, date);
|
|
582
|
+
// 埋点:从这一轮起算,首卡 = partial 板书第一次有卡(孩子端第一次看到东西),done = 进程退出,dubbed = 配音收尾
|
|
583
|
+
const t0 = Date.now();
|
|
584
|
+
const timing = { startedAt: new Date(t0).toISOString() };
|
|
585
|
+
const since = () => Date.now() - t0;
|
|
586
|
+
// 事件:每道工序的关键点发一条,追加到 events.jsonl(按序),同时给内存里的订阅者;写盘失败不影响这一轮
|
|
587
|
+
let eventsChain = Promise.resolve();
|
|
588
|
+
const events = [];
|
|
589
|
+
const emit = (e) => {
|
|
590
|
+
const event = { t: since(), ...e };
|
|
591
|
+
events.push(event);
|
|
592
|
+
eventsChain = eventsChain.then(() => appendFile(files.events(job), `${JSON.stringify(event)}\n`)).catch(() => { });
|
|
593
|
+
for (const fn of this.listeners) {
|
|
594
|
+
try {
|
|
595
|
+
fn({ tutor, date, job, event });
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
/* 订阅者的错不影响这一轮 */
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
// 讲稿不配音(嘴照念):拍关就就绪;卡自己的声音(点读段、单词)照配
|
|
603
|
+
const voice = cardVoice(ws.config.tutors, tutor);
|
|
604
|
+
const queue = voice ? new DubQueue(voice, files.err(job), { cacheDir: ws.dirs.tts, env: this.opts.env }) : null;
|
|
605
|
+
if (queue)
|
|
606
|
+
queue.report = (e) => emit(e.kind === 'queued' ? { lane: 'tts', kind: 'queued', label: e.label } : e.kind === 'done' ? { lane: 'tts', kind: 'done', label: e.label, ms: e.ms, file: e.file ?? '' } : { lane: 'tts', kind: 'failed', label: e.label, ms: e.ms, error: e.error ?? '?' });
|
|
607
|
+
let cardsSeen = 0;
|
|
608
|
+
let linesSeen = 0;
|
|
609
|
+
// 就绪(流式):重算前几拍就绪(卡等它),涨了发 ready:beat;第一拍就绪记 firstReadyMs、发 ready:speak(嘴能开口了)
|
|
610
|
+
let readySeen = 0;
|
|
611
|
+
const speak = () => {
|
|
612
|
+
if (timing.firstReadyMs !== undefined)
|
|
613
|
+
return;
|
|
614
|
+
timing.firstReadyMs = since();
|
|
615
|
+
emit({ lane: 'ready', kind: 'speak' });
|
|
616
|
+
};
|
|
617
|
+
const settleReady = () => {
|
|
618
|
+
if (!active.partial)
|
|
619
|
+
return;
|
|
620
|
+
const n = readyBeats(active.partial, { done: false });
|
|
621
|
+
if (n <= readySeen)
|
|
622
|
+
return;
|
|
623
|
+
const beats = beatsOf(active.partial);
|
|
624
|
+
for (; readySeen < n; readySeen++) {
|
|
625
|
+
speak();
|
|
626
|
+
emit({ lane: 'ready', kind: 'beat', beat: readySeen, card: beats[readySeen]?.card ?? null, first: readySeen === 0 });
|
|
627
|
+
}
|
|
628
|
+
active.partial = { ...active.partial, ready: n };
|
|
629
|
+
};
|
|
630
|
+
// 流式:脑的正文变了就(节流 150ms)重解析成 partial 板书
|
|
631
|
+
let dirty = false;
|
|
632
|
+
let timer = null;
|
|
633
|
+
const reparse = () => {
|
|
634
|
+
timer = null;
|
|
635
|
+
if (!dirty)
|
|
636
|
+
return;
|
|
637
|
+
dirty = false;
|
|
638
|
+
// 先剥「## 记账」再解析,和定稿的 deriveKidView 同一条路;不剥的话固定段写在前面的那轮流式时一张卡都出不来(2026-09-13 控制台里看见的)
|
|
639
|
+
const { section } = parseBoard(parseSections(brainRun.text()).body, { partial: true });
|
|
640
|
+
const lines = section.lines.map((l) => ({ ...l, text: truncateReply(l.text, replyMaxChars).text, audio: null }));
|
|
641
|
+
active.partial = section.cards.length || lines.length ? { cards: section.cards, lines, partial: true, ready: readySeen } : null;
|
|
642
|
+
if (section.cards.length && timing.firstCardMs === undefined)
|
|
643
|
+
timing.firstCardMs = since();
|
|
644
|
+
for (; cardsSeen < section.cards.length; cardsSeen++)
|
|
645
|
+
emit({ lane: 'main', kind: 'card', card: cardsSeen, label: `${section.cards[cardsSeen].kind} ${cardLabel(section.cards[cardsSeen])}`.trim() });
|
|
646
|
+
for (; linesSeen < lines.length; linesSeen++)
|
|
647
|
+
emit({ lane: 'main', kind: 'line', line: linesSeen, text: lines[linesSeen].text, ...lineFlags(lines[linesSeen], section.cards) });
|
|
648
|
+
settleReady();
|
|
649
|
+
};
|
|
650
|
+
// 请老师(src/brain/):正文、工具随流回报
|
|
651
|
+
const brainRun = this.brain.start(req, plan, { tutor, files: { log: files.log(job), err: files.err(job), history: files.brain(thread) }, stall: policy.stall }, {
|
|
652
|
+
onStart: (s) => emit({ lane: 'main', kind: 'start', cli: s.cli, runtime: s.runtime, resume: s.resume }),
|
|
653
|
+
onText: () => {
|
|
654
|
+
dirty = true;
|
|
655
|
+
if (!timer)
|
|
656
|
+
timer = setTimeout(reparse, 150);
|
|
657
|
+
},
|
|
658
|
+
onTool: (tl) => emit({ lane: 'main', kind: 'tool', name: tl.name, sub: tl.sub }),
|
|
659
|
+
onExit: (at) => {
|
|
660
|
+
timing.doneMs = at - t0;
|
|
661
|
+
if (timer)
|
|
662
|
+
clearTimeout(timer);
|
|
663
|
+
},
|
|
664
|
+
});
|
|
665
|
+
const { transcript, tools, warnings } = await brainRun.done;
|
|
666
|
+
emit({ lane: 'main', kind: 'exit', ok: transcript.final.ok, ...(transcript.final.reason ? { reason: transcript.final.reason } : {}), ...(transcript.final.inTokens !== undefined ? { tokens: { in: transcript.final.inTokens, out: transcript.final.outTokens ?? 0 } } : {}), ...(transcript.final.numTurns !== undefined ? { turns: transcript.final.numTurns } : {}) });
|
|
667
|
+
const kidView = deriveKidView(transcript, { replyMaxChars }, { live: active.live });
|
|
668
|
+
kidView.warnings.push(...warnings);
|
|
669
|
+
// 整块出的(没走流式):卡与句这时才第一次见到,也发一遍
|
|
670
|
+
if (kidView.section) {
|
|
671
|
+
for (; cardsSeen < kidView.section.cards.length; cardsSeen++)
|
|
672
|
+
emit({ lane: 'main', kind: 'card', card: cardsSeen, label: `${kidView.section.cards[cardsSeen].kind} ${cardLabel(kidView.section.cards[cardsSeen])}`.trim() });
|
|
673
|
+
for (; linesSeen < kidView.section.lines.length; linesSeen++)
|
|
674
|
+
emit({ lane: 'main', kind: 'line', line: linesSeen, text: kidView.section.lines[linesSeen].text, ...lineFlags(kidView.section.lines[linesSeen], kidView.section.cards) });
|
|
675
|
+
}
|
|
676
|
+
if (timing.firstReadyMs === undefined && (kidView.section?.lines.length || kidView.kidText))
|
|
677
|
+
timing.firstReadyMs = since();
|
|
678
|
+
if (kidView.section)
|
|
679
|
+
emit({ lane: 'ready', kind: 'all', cards: kidView.section.cards.length, lines: kidView.section.lines.length });
|
|
680
|
+
if (kidView.section)
|
|
681
|
+
timing.beats = beatTimings(events, beatsOf(kidView.section));
|
|
682
|
+
// 重新读索引再并入:跑的这段时间里别的字段(比如家长改了别的)不被旧对象盖掉
|
|
683
|
+
const latest = await readIndex(ws, tutor, date);
|
|
684
|
+
// 板书写法已经递到手里(系统提示或话题第一条),老师还用工具去读它 → 预载没起作用,家长端这轮上点名(换模型 / CLI / 版本后最先坏的地方)
|
|
685
|
+
const reread = takesTutorRules(tutor) && policy.board !== 'off' ? boardGuideReads(tools) : [];
|
|
686
|
+
if (reread.length)
|
|
687
|
+
kidView.warnings.push(`板书写法已经递给老师了,这轮它还是用工具去读了一遍(${reread.join(';')}):多一次模型来回。cotutor doctor 的 runtime.*.board 看这个运行时走的哪条递法`);
|
|
688
|
+
// 清单外的卡(《卡片协议.md》「谁拿到哪些卡」):照常解析、照常显示,只在这轮记一条,家长据此调清单
|
|
689
|
+
// activity 话题的卡是 activity 自己的(加 text);别的话题照老师的清单
|
|
690
|
+
const jobAt = latest.messages.findIndex((m) => m.job === job);
|
|
691
|
+
const run = jobAt >= 0 ? threadActivity(latest.messages, threads(latest.messages)[jobAt]) : null;
|
|
692
|
+
const actFile = run ? await readActivity(ws, tutor, run.name) : null;
|
|
693
|
+
const kinds = actFile ? ['text', ...actFile.cards] : tutorCardKinds(ws.config.tutors[tutor]?.cards);
|
|
694
|
+
const outside = kinds && kidView.section ? [...new Set(kidView.section.cards.map((c) => c.kind).filter((k) => !kinds.includes(k)))] : [];
|
|
695
|
+
if (outside.length)
|
|
696
|
+
kidView.warnings.push(actFile ? `板书里有这个 activity 清单外的卡:${outside.join('、')}(照常显示;要它就加进 agents/${tutor}/activities/${run.name}.md 头上的 cards)` : `板书里有清单外的卡:${outside.join('、')}(照常显示;要它就加进 cotutor.json tutors.${tutor}.cards)`);
|
|
697
|
+
let next = applyRun(latest, job, { transcript, kidView, runtime: plan.runtime, timing, tools });
|
|
698
|
+
const patch = (f) => { next = { ...next, messages: next.messages.map((m) => (m.job === job ? f(m) : m)) }; };
|
|
699
|
+
const asked = latest.messages.find((m) => m.job === job);
|
|
700
|
+
// 老师不直接写 vault,回的段由应用落盘;写了什么、没写成为什么都在这条的 warnings 里(家长视图看 remembered 与提醒)
|
|
701
|
+
// 「## 记忆」段:每条是建议,证据对不上孩子说过的不记;其余增 / 改 / 删落进这位 agent 的记忆文件,memoryLines 留给删话题时撤
|
|
702
|
+
const memory = kidView.memory.length ? await this.planMemoryOf(ws, tutor, date, kidView.memory, asked, kidSaid(latest.messages, asked?.tidy ? null : thread), policy.contextPack.entryChars, actFile && run ? { agent: `${tutor}/${run.name}`, display: `${ws.config.tutors[tutor]?.display ?? tutor}·${run.name}` } : undefined) : null;
|
|
703
|
+
if (memory?.proposals.length)
|
|
704
|
+
patch((m) => ({ ...m, memoryProposals: memory.proposals }));
|
|
705
|
+
// 记账那轮:「## 记账」段 → 这个话题在日记里的一段;写进去的原文记在这轮上,删话题时从日记里摘掉(memory/vault.ts 的 undoThreadVault)
|
|
706
|
+
const bookThread = asked?.bookkeep?.thread;
|
|
707
|
+
const t = ws.config.tutors[tutor];
|
|
708
|
+
const diary = bookThread ? planDiary({ date, index: next, thread: bookThread, bookkeeping: kidView.bookkeeping, subject: t?.subject ?? t?.display ?? tutor, keepScore: ws.config.vault.keepScore }) : null;
|
|
709
|
+
const vaultWarnings = [...(memory?.warnings ?? []), ...(diary ? ('warning' in diary ? [diary.warning] : diary.notes) : [])];
|
|
710
|
+
// 先在索引上记一笔 pending,再写 vault,写完换成结果(《运行层行为设计》§5):中间断了,下次记账、删话题前由 recoverPending 对照文件补
|
|
711
|
+
const mp = memory?.plan;
|
|
712
|
+
const cp = memory?.child;
|
|
713
|
+
const db = diary && 'block' in diary && bookThread ? { file: diary.file, block: diary.block, thread: bookThread } : null;
|
|
714
|
+
const pendingOf = (x) => ({ file: x.file, base: x.base === null ? null : textHash(x.base), hash: textHash(x.text), changes: x.changes, lines: x.lines });
|
|
715
|
+
if (mp || cp || db) {
|
|
716
|
+
const pending = { ...(mp ? { memory: pendingOf(mp) } : {}), ...(cp ? { child: pendingOf(cp) } : {}), ...(db ? { diary: db } : {}) };
|
|
717
|
+
patch((m) => ({ ...m, pending }));
|
|
718
|
+
await writeIndex(ws, next);
|
|
719
|
+
patch(({ pending: _, ...m }) => m);
|
|
720
|
+
}
|
|
721
|
+
const remembered = [];
|
|
722
|
+
for (const [x, child] of [[mp, false], [cp, true]]) {
|
|
723
|
+
if (!x)
|
|
724
|
+
continue;
|
|
725
|
+
const w = await writeVaultMemory(ws, x).catch((err) => `${child ? '孩子的记忆' : '记忆'}没写进去:${err instanceof Error ? err.message : String(err)}`);
|
|
726
|
+
if (w) {
|
|
727
|
+
vaultWarnings.push(w);
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
remembered.push(...(child ? childChanges(x.changes) : x.changes));
|
|
731
|
+
patch((m) => (child ? { ...m, childLines: x.lines } : { ...m, memoryLines: x.lines }));
|
|
732
|
+
}
|
|
733
|
+
if (remembered.length)
|
|
734
|
+
patch((m) => ({ ...m, remembered }));
|
|
735
|
+
if (db) {
|
|
736
|
+
try {
|
|
737
|
+
await writeDiaryBlock(ws, date, db.block);
|
|
738
|
+
patch((m) => ({ ...m, diaryBlock: db.block }));
|
|
739
|
+
next = { ...next, booked: { ...next.booked, [db.thread]: job } };
|
|
740
|
+
emit({ lane: 'ledger', kind: 'diary', thread: db.thread, file: db.file });
|
|
741
|
+
}
|
|
742
|
+
catch (err) {
|
|
743
|
+
vaultWarnings.push(`记账:日记没写进去:${err instanceof Error ? err.message : String(err)}`);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (vaultWarnings.length)
|
|
747
|
+
patch((m) => ({ ...m, warnings: [...(m.warnings ?? []), ...vaultWarnings] }));
|
|
748
|
+
await writeIndex(ws, next);
|
|
749
|
+
emit({ lane: 'index', kind: 'written', warnings: next.messages.find((m) => m.job === job)?.warnings?.length ?? 0 });
|
|
750
|
+
await eventsChain;
|
|
751
|
+
// 资产:点读段逐段配音到 .cards/<n>/<k>.mp3,后台跑,不拦着这一轮收尾;孩子端每次拉 today 都会看到新生成好的
|
|
752
|
+
if (queue && kidView.section) {
|
|
753
|
+
const jobs = [];
|
|
754
|
+
kidView.section.cards.forEach((card, n) => {
|
|
755
|
+
const list = cardAssets(card);
|
|
756
|
+
if (!list.length)
|
|
757
|
+
return;
|
|
758
|
+
jobs.push(mkdir(files.cardAssetsDir(job, n), { recursive: true }).then(() => Promise.all(list.map((a) => queue.add(files.cardAsset(job, n, a.file), a.text, `第 ${n + 1} 张卡的 ${a.file} `)))));
|
|
759
|
+
});
|
|
760
|
+
if (jobs.length) {
|
|
761
|
+
const bg = Promise.all(jobs).then(() => undefined, () => undefined);
|
|
762
|
+
this.background.add(bg);
|
|
763
|
+
void bg.finally(() => this.background.delete(bg));
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
// 画图卡:后台画,一张一张排着;回放不画
|
|
767
|
+
if (kidView.section?.cards.some((c) => c.kind === 'paint')) {
|
|
768
|
+
const clock = () => (this.opts.now?.() ?? new Date()).getTime();
|
|
769
|
+
const gate = await paintGate(ws, tutor, date, job, clock).catch(() => null);
|
|
770
|
+
const painter = await paintTool(ws, tutor, date, job).catch(() => null);
|
|
771
|
+
const bg = startPaints(ws, tutor, date, job, kidView.section, { draw: this.draw, now: this.opts.now, replay: Boolean(latest.messages.find((m) => m.job === job)?.replayOf), ...(gate ? { gate } : {}), painter });
|
|
772
|
+
this.background.add(bg);
|
|
773
|
+
void bg.finally(() => this.background.delete(bg));
|
|
774
|
+
}
|
|
775
|
+
return next;
|
|
776
|
+
}
|
|
777
|
+
}
|