cotutor 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +247 -21
- package/NOTICE +42 -0
- package/README.md +107 -24
- package/bin/cotutor.js +4 -4
- 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 +15 -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 +66 -0
- package/cards/tutor/card.md +148 -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 +158 -0
- package/dist/app/build.d.ts +11 -0
- package/dist/app/build.js +45 -0
- package/dist/app/cli/activities.d.ts +63 -0
- package/dist/app/cli/activities.js +158 -0
- package/dist/app/cli/cert.d.ts +7 -0
- package/dist/app/cli/cert.js +36 -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 +142 -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 +198 -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 +23 -0
- package/dist/app/cli/doctor.js +617 -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 +155 -0
- package/dist/app/cli/main.js +715 -0
- package/dist/app/cli/migrate.d.ts +58 -0
- package/dist/app/cli/migrate.js +253 -0
- package/dist/app/cli/serve.d.ts +37 -0
- package/dist/app/cli/serve.js +98 -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 +55 -0
- package/dist/app/cli/skills.js +189 -0
- package/dist/app/cli/themes.d.ts +52 -0
- package/dist/app/cli/themes.js +148 -0
- package/dist/app/cli/tutors.d.ts +73 -0
- package/dist/app/cli/tutors.js +250 -0
- package/dist/app/cli/usage.d.ts +2 -0
- package/dist/app/cli/usage.js +30 -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 +510 -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 +246 -0
- package/dist/app/http/app.js +1212 -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 +37 -0
- package/dist/app/http/parent-home.js +203 -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/app/http/stage.d.ts +24 -0
- package/dist/app/http/stage.js +97 -0
- package/dist/app/http/theme.d.ts +13 -0
- package/dist/app/http/theme.js +41 -0
- package/dist/app/machine/bailian.d.ts +28 -0
- package/dist/app/machine/bailian.js +88 -0
- package/dist/app/mock/mock-live.d.ts +16 -0
- package/dist/app/mock/mock-live.js +140 -0
- package/dist/app/mock/mock.d.ts +89 -0
- package/dist/app/mock/mock.js +1117 -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 +58 -0
- package/dist/{cli → app}/workspace.js +82 -36
- package/dist/app/ws/route.d.ts +25 -0
- package/dist/app/ws/route.js +29 -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/board/cards/choice.d.ts +20 -0
- package/dist/board/cards/choice.js +64 -0
- package/dist/board/cards/code.d.ts +9 -0
- package/dist/board/cards/code.js +10 -0
- 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/board/cards/image.d.ts +13 -0
- package/dist/board/cards/image.js +26 -0
- package/dist/board/cards/index.d.ts +62 -0
- package/dist/board/cards/index.js +144 -0
- package/dist/board/cards/kind.d.ts +33 -0
- 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/board/cards/read.d.ts +8 -0
- package/dist/board/cards/read.js +18 -0
- 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 +120 -0
- package/dist/board/parse.js +330 -0
- package/dist/board/presentation.d.ts +295 -0
- package/dist/board/presentation.js +615 -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 +190 -0
- package/dist/brain/api.d.ts +62 -0
- package/dist/brain/api.js +411 -0
- package/dist/brain/cli/adapters.d.ts +60 -0
- package/dist/brain/cli/adapters.js +91 -0
- package/dist/brain/cli/qwen.d.ts +11 -0
- package/dist/brain/cli/qwen.js +141 -0
- package/dist/brain/cli/run-plan.d.ts +59 -0
- package/dist/brain/cli/run-plan.js +71 -0
- package/dist/brain/cli/run.d.ts +35 -0
- package/dist/brain/cli/run.js +256 -0
- package/dist/brain/cli/stream-json.d.ts +18 -0
- package/dist/brain/cli/stream-json.js +86 -0
- package/dist/brain/cli/types.d.ts +144 -0
- package/dist/brain/cli/types.js +3 -0
- package/dist/brain/cli/warm.d.ts +53 -0
- package/dist/brain/cli/warm.js +103 -0
- package/dist/brain/index.d.ts +145 -0
- package/dist/brain/index.js +74 -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/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 -8
- package/dist/lib/index.js +14 -8
- 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 +3 -1
- package/dist/{lib → memory}/plan.js +10 -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/memory/timetable.d.ts +19 -0
- package/dist/memory/timetable.js +95 -0
- 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 +288 -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 +19 -0
- package/dist/realtime/voices.js +71 -0
- package/dist/runtime/activities.d.ts +17 -0
- package/dist/runtime/activities.js +46 -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 +180 -0
- package/dist/runtime/home/home.js +311 -0
- package/dist/runtime/home/store.d.ts +142 -0
- package/dist/runtime/home/store.js +440 -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 -32
- package/dist/runtime/journal/mix.d.ts +33 -0
- package/dist/runtime/journal/mix.js +88 -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 +46 -0
- package/dist/runtime/journal/recorder.js +58 -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 +195 -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 +219 -0
- package/dist/runtime/live/call.js +1211 -0
- package/dist/runtime/live/turns.d.ts +48 -0
- package/dist/runtime/live/turns.js +102 -0
- package/dist/runtime/runner.d.ts +289 -0
- package/dist/runtime/runner.js +898 -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 +170 -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 +200 -0
- package/dist/schema/board.js +42 -0
- package/dist/schema/config.d.ts +353 -80
- package/dist/schema/config.js +293 -76
- package/dist/schema/context-pack.d.ts +82 -13
- package/dist/schema/context-pack.js +97 -10
- package/dist/schema/conversation-files.d.ts +43 -0
- package/dist/schema/conversation-files.js +26 -0
- package/dist/schema/conversation.d.ts +459 -13
- package/dist/schema/conversation.js +148 -8
- package/dist/schema/events.d.ts +138 -0
- package/dist/schema/events.js +1 -0
- package/dist/schema/home.d.ts +46 -0
- package/dist/schema/home.js +30 -0
- package/dist/schema/index.d.ts +19 -4
- package/dist/schema/index.js +19 -4
- package/dist/schema/json-schema.d.ts +2 -0
- package/dist/schema/json-schema.js +23 -0
- package/dist/schema/kid-device.d.ts +20 -0
- package/dist/schema/kid-device.js +32 -0
- package/dist/schema/ledger.d.ts +9 -64
- 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 +26 -0
- package/dist/schema/timetable.js +18 -0
- package/dist/schema/workspace.d.ts +41 -0
- package/dist/schema/workspace.js +9 -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 +50 -0
- package/dist/shared/qianwen.js +109 -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/Assistant-Bold-ZDZZ6JHA.woff2 +0 -0
- package/dist/stage/Assistant-Medium-DZ25RZU3.woff2 +0 -0
- package/dist/stage/Assistant-Regular-PLF2XOGW.woff2 +0 -0
- package/dist/stage/Assistant-SemiBold-CZ5MX6FK.woff2 +0 -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 +11 -0
- package/dist/stage/stage.css +1 -0
- package/dist/stage/stage.js +1 -0
- package/dist/ui/dev/dev-page.d.ts +1 -0
- package/dist/ui/dev/dev-page.js +2248 -0
- package/dist/ui/dev/dev-theme.d.ts +6 -0
- package/dist/ui/dev/dev-theme.js +67 -0
- package/dist/ui/dev/qr-page.d.ts +8 -0
- package/dist/ui/dev/qr-page.js +61 -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/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 +3719 -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 +66 -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 +414 -0
- package/dist/ui/parent/parent-page.d.ts +2 -0
- package/dist/ui/parent/parent-page.js +507 -0
- package/dist/ui/shared/brand.d.ts +36 -0
- package/dist/ui/shared/brand.js +51 -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/211/271/346/224/271.md +81 -0
- package/grades/grade-2/agents/chinese-tutor/activities//346/211/271/346/224/271./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/211/271/346/224/271.md +80 -0
- package/grades/grade-2/agents/english-tutor/activities//346/211/271/346/224/271./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/211/271/346/224/271.md +80 -0
- package/grades/grade-2/agents/math-tutor/activities//346/211/271/346/224/271./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 +32 -12
- 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 +169 -0
- package/skills/cotutor-home/references/dictation.md +97 -0
- package/skills/cotutor-home/references/tutor.md +148 -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 +142 -0
- package/skills/cotutor-tune/SKILL.md +147 -0
- package/skills/cotutor-tune/references//345/255/227/346/256/265.md +50 -0
- package/skills/cotutor-tutor/SKILL.md +261 -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-teacher.md +0 -19
- package/agents/homework-aide.md +0 -17
- package/agents/math-teacher.md +0 -19
- package/agents/planner.md +0 -18
- package/agents/reading-teacher.md +0 -19
- package/dist/cli/doctor.d.ts +0 -19
- package/dist/cli/doctor.js +0 -190
- package/dist/cli/init.js +0 -114
- package/dist/cli/main.js +0 -123
- package/dist/cli/serve.d.ts +0 -19
- package/dist/cli/serve.js +0 -22
- package/dist/cli/skeleton.d.ts +0 -21
- package/dist/cli/skeleton.js +0 -82
- package/dist/cli/workspace.d.ts +0 -61
- package/dist/lib/agent-file.d.ts +0 -10
- package/dist/lib/agent-file.js +0 -16
- package/dist/lib/context-pack.js +0 -46
- package/dist/lib/conversation.d.ts +0 -28
- package/dist/lib/conversation.js +0 -44
- package/dist/lib/kid-view.d.ts +0 -26
- package/dist/lib/kid-view.js +0 -38
- package/dist/lib/ledger.d.ts +0 -21
- package/dist/lib/sections.d.ts +0 -12
- package/dist/lib/sections.js +0 -151
- package/dist/lib/transcript.d.ts +0 -36
- package/dist/lib/transcript.js +0 -95
- package/dist/server/app.d.ts +0 -13
- package/dist/server/app.js +0 -45
- /package/dist/{cli → app/cli}/main.d.ts +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export declare const REALTIME_MODEL = "qwen3.8-omni-flash-realtime";
|
|
2
|
+
export declare const REALTIME_VOICE = "Serena";
|
|
3
|
+
export declare const INPUT_RATE = 16000;
|
|
4
|
+
export declare const OUTPUT_RATE = 24000;
|
|
5
|
+
/** 服务端一帧最多 262144 字节(2026-10-19 真连:超了就 close 1009,整条连接没了);一张图的 base64 连同 JSON 的壳得在这以内 */
|
|
6
|
+
export declare const MAX_IMAGE_B64: number;
|
|
7
|
+
export type TurnMode = 'manual' | 'semantic_vad' | 'server_vad';
|
|
8
|
+
/** VAD 判出说完以后服务端自己做不做:回一次(create_response)、孩子开口时掐掉在出的回答(interrupt_response);缺省都做(口语课聊那段) */
|
|
9
|
+
export interface VadOptions {
|
|
10
|
+
createResponse?: boolean;
|
|
11
|
+
interruptResponse?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface RealtimeOptions {
|
|
14
|
+
/** 系统提示(人设 + 规矩;口语课还有口语单) */
|
|
15
|
+
instructions: string;
|
|
16
|
+
voice?: string;
|
|
17
|
+
model?: string;
|
|
18
|
+
turn?: TurnMode;
|
|
19
|
+
/** VAD 判「说完了」的静音毫秒(一年级孩子想词慢,缺省比百炼的 800 长) */
|
|
20
|
+
silenceMs?: number;
|
|
21
|
+
/** VAD 判出说完以后服务端自己回不回、掐不掐(实时老师的一直听两样都关,由应用定) */
|
|
22
|
+
vad?: VadOptions;
|
|
23
|
+
/** 测试用:整个端点(含 ?model=);不给就按 workspaceId 拼 */
|
|
24
|
+
endpoint?: string;
|
|
25
|
+
/** 指定 key 与业务空间 id(家长端「试连」还没存的那份);不给按 dashscopeAuth 找 */
|
|
26
|
+
auth?: {
|
|
27
|
+
key: string;
|
|
28
|
+
workspaceId: string;
|
|
29
|
+
};
|
|
30
|
+
env?: NodeJS.ProcessEnv;
|
|
31
|
+
/** 握手后等 session.updated 最多多久 */
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface Usage {
|
|
35
|
+
input: number;
|
|
36
|
+
output: number;
|
|
37
|
+
inputAudio: number;
|
|
38
|
+
outputAudio: number;
|
|
39
|
+
}
|
|
40
|
+
export interface RealtimeEvents {
|
|
41
|
+
/** 一块老师的声音(PCM16 24k 单声道) */
|
|
42
|
+
onAudio?(pcm: Buffer): void;
|
|
43
|
+
/** 老师的字:delta 一截截来,done 时给整句 */
|
|
44
|
+
onTutorText?(delta: string, done: string | null): void;
|
|
45
|
+
/** 孩子那句转写完了 */
|
|
46
|
+
onKidText?(text: string): void;
|
|
47
|
+
/** VAD:孩子开口 / 停了 */
|
|
48
|
+
onSpeech?(state: 'started' | 'stopped'): void;
|
|
49
|
+
/** semantic_vad 判出这一段不算一句(附和声、噪声):不会有 committed 与转写;不是错 */
|
|
50
|
+
onRejected?(): void;
|
|
51
|
+
/** 孩子那句 commit 了,转写失败 */
|
|
52
|
+
onKidFailed?(): void;
|
|
53
|
+
/** 老师一次回答开始 / 结束(status 来自服务端:completed / cancelled / incomplete / failed) */
|
|
54
|
+
onResponse?(state: 'created' | 'done', status: string | null, usage: Usage | null): void;
|
|
55
|
+
onError?(message: string): void;
|
|
56
|
+
onClose?(reason: string): void;
|
|
57
|
+
}
|
|
58
|
+
/** 百炼按量的 key 与业务空间 id(Token Plan 没有 omni realtime;key 怎么找见 src/shared/qianwen.ts,业务空间 id 先看家长端配的、再看环境、再看 voxtell 的配置) */
|
|
59
|
+
export declare function dashscopeAuth(env?: NodeJS.ProcessEnv): {
|
|
60
|
+
key: string;
|
|
61
|
+
workspaceId: string;
|
|
62
|
+
} | null;
|
|
63
|
+
export declare const endpointOf: (workspaceId: string, model: string) => string;
|
|
64
|
+
/** 轮次模式 → session.turn_detection */
|
|
65
|
+
export declare function turnDetection(turn: TurnMode, silenceMs: number, vad?: VadOptions): Record<string, unknown> | null;
|
|
66
|
+
/** session.update 的正文 */
|
|
67
|
+
export declare function sessionConfig(o: {
|
|
68
|
+
instructions: string;
|
|
69
|
+
voice: string;
|
|
70
|
+
turn: TurnMode;
|
|
71
|
+
silenceMs: number;
|
|
72
|
+
vad?: VadOptions;
|
|
73
|
+
}): Record<string, unknown>;
|
|
74
|
+
export declare class RealtimeSession {
|
|
75
|
+
readonly model: string;
|
|
76
|
+
readonly voice: string;
|
|
77
|
+
private ws;
|
|
78
|
+
private ready;
|
|
79
|
+
private closed;
|
|
80
|
+
private turn;
|
|
81
|
+
private readonly silenceMs;
|
|
82
|
+
private tutorText;
|
|
83
|
+
private pendingUpdate;
|
|
84
|
+
/** 正在出的回答数(response.cancel 只在有回答时发,不然服务端报错) */
|
|
85
|
+
private active;
|
|
86
|
+
private readonly opts;
|
|
87
|
+
private readonly ev;
|
|
88
|
+
constructor(opts: RealtimeOptions, ev?: RealtimeEvents);
|
|
89
|
+
get isReady(): boolean;
|
|
90
|
+
/** 老师说到一半、还没 done 的字(挂断时记进字幕用) */
|
|
91
|
+
get partialTutorText(): string;
|
|
92
|
+
get turnMode(): TurnMode;
|
|
93
|
+
get responding(): boolean;
|
|
94
|
+
/** 连上、配好会话;回 null 成功,否则是原因(no_auth / connect / timeout / <服务端的话>) */
|
|
95
|
+
connect(): Promise<string | null>;
|
|
96
|
+
private handle;
|
|
97
|
+
private sendRaw;
|
|
98
|
+
/** 一块孩子的声音(PCM16 16k 单声道) */
|
|
99
|
+
appendAudio(pcm: Buffer): boolean;
|
|
100
|
+
/** 一张图(JPEG)跟着这一段声音交:百炼要先有声音再有图(Qwen-Live-Harness 也这样),commit 时一起进对话;太大(见 MAX_IMAGE_B64)不发、回 false,调用方先缩 */
|
|
101
|
+
appendImage(jpeg: Buffer): boolean;
|
|
102
|
+
/** 手动轮次:松手 */
|
|
103
|
+
commit(): boolean;
|
|
104
|
+
clearAudio(): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* 心跳:1 秒全零的声音接着 clear,不 commit、不要回答(照 Qwen-Live-Harness 的静音心跳)。百炼 300 秒没输入就关会话(user_idle_timeout)。
|
|
107
|
+
* 2026-10-20 真连(scripts/probe-live-idle.mjs,手动轮次,每 30 秒一次):不发的 302 秒被关;只 append 不 clear 的 300 秒报 response_idle_timeout
|
|
108
|
+
* (缓冲里有输入一直没回答),会话就死了;append 完马上 clear 的过了 400 秒照常说话。
|
|
109
|
+
*/
|
|
110
|
+
heartbeat(): boolean;
|
|
111
|
+
/** 让老师说;instructions = 只管这一次回答的指令(response.instructions,不进对话历史、不改系统提示) */
|
|
112
|
+
createResponse(instructions?: string): boolean;
|
|
113
|
+
/** 老师正在说就掐掉(没在说不发,服务端会报错) */
|
|
114
|
+
cancel(): boolean;
|
|
115
|
+
/** 应用塞一条文字进对话(口语课跟读段「现在读这句」、切段的指令);role 缺省 user */
|
|
116
|
+
addText(text: string, role?: 'user' | 'system'): boolean;
|
|
117
|
+
/** 中途在手动与 VAD 之间切(只给 type,silence 喂过音频后改不了);等 session.updated,错或超时回 false */
|
|
118
|
+
setTurn(turn: TurnMode, timeoutMs?: number): Promise<boolean>;
|
|
119
|
+
close(reason?: string): void;
|
|
120
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 百炼 Qwen-Omni-Realtime 的 WebSocket 客户端:口语课和实时老师共用。
|
|
3
|
+
* 一次通话一条会话。服务端连,key 不出服务端;孩子端的声音由调用方推进来,老师的声音从这里出去。
|
|
4
|
+
*
|
|
5
|
+
* 协议(OpenAI realtime 同族,2026-10-10 真连核对):
|
|
6
|
+
* 连上先收 session.created;发 session.update,收 session.updated 后才能喂音频。
|
|
7
|
+
* 上行:input_audio_buffer.append {audio: base64 PCM16 16k 单声道} / commit / clear;input_image_buffer.append {image: base64 JPEG}(先有声音再有图,commit 时一起交);response.create / cancel;
|
|
8
|
+
* conversation.item.create {item: {type: message, role, content: [{type: input_text, text}]}}。
|
|
9
|
+
* 下行:response.audio.delta {delta: base64 PCM16 24k};response.audio_transcript.delta / .done(老师的字);
|
|
10
|
+
* conversation.item.input_audio_transcription.completed {transcript}(孩子那句,qwen3-asr-flash-realtime 转的);
|
|
11
|
+
* input_audio_buffer.speech_started / speech_stopped(VAD);response.done {response: {status, usage}};error。
|
|
12
|
+
* 端点要业务空间 id:wss://<WorkspaceId>.cn-beijing.maas.aliyuncs.com/api-ws/v1/realtime?model=…(公网域名 dashscope.aliyuncs.com 连上 1 秒就被关)。
|
|
13
|
+
*
|
|
14
|
+
* 轮次:manual = 手动(实时老师的按住说话:松手 commit);semantic_vad / server_vad = 服务端判(实时老师的一直听、热身后打电话那段;一直听时 create_response / interrupt_response 关着,回不回、掐不掐由应用定)。
|
|
15
|
+
* 一直听的真连(2026-10-20,scripts/probe-live-vad.mjs):孩子还在说时就有 conversation.item.created(不算收下)、转写片段;speech_stopped 与 committed 同一刻,
|
|
16
|
+
* 转写约 0.2 秒后到;semantic_vad 不收的一段回 error「not accepted by semantic turn detection」(走 onRejected,不是错);commit 了 300 秒没回答报 response_idle_timeout,会话死。
|
|
17
|
+
* semantic_vad 一两个词的回答(好的、Yes、Red)不报开口,嘴先问了也不报;噪声、「嗯」、笑声也不报。server_vad 短的都收,「嗯」、笑声也报(噪声不报)。
|
|
18
|
+
* 服务端的规矩(2026-10-10 真连):turn_detection 的 silence_duration_ms 喂过音频就不能再改(切成 null 再切回来也回到缺省 800),
|
|
19
|
+
* type 在 null 与 vad 之间随时能切。所以跟读与聊各开一条会话(跟读手动;聊 semantic_vad 带自己的 silence),setTurn 只在不改 silence 时用。
|
|
20
|
+
* 不抛:连不上、断了、服务端报错都走 onClose / onError;send 在没连上时丢弃并回 false。
|
|
21
|
+
*/
|
|
22
|
+
import { bailianWorkspaceId, qianwenKeys, workspaceHost } from "../shared/qianwen.js";
|
|
23
|
+
export const REALTIME_MODEL = 'qwen3.8-omni-flash-realtime';
|
|
24
|
+
export const REALTIME_VOICE = 'Serena';
|
|
25
|
+
export const INPUT_RATE = 16000;
|
|
26
|
+
export const OUTPUT_RATE = 24000;
|
|
27
|
+
/** 服务端一帧最多 262144 字节(2026-10-19 真连:超了就 close 1009,整条连接没了);一张图的 base64 连同 JSON 的壳得在这以内 */
|
|
28
|
+
export const MAX_IMAGE_B64 = 262144 - 1024;
|
|
29
|
+
const TRANSCRIBE_MODEL = 'qwen3-asr-flash-realtime';
|
|
30
|
+
const HEARTBEAT_PCM = Buffer.alloc(INPUT_RATE * 2);
|
|
31
|
+
/** 百炼按量的 key 与业务空间 id(Token Plan 没有 omni realtime;key 怎么找见 src/shared/qianwen.ts,业务空间 id 先看家长端配的、再看环境、再看 voxtell 的配置) */
|
|
32
|
+
export function dashscopeAuth(env = process.env) {
|
|
33
|
+
const key = qianwenKeys(env).payg;
|
|
34
|
+
const workspaceId = bailianWorkspaceId(env);
|
|
35
|
+
return key && workspaceId ? { key, workspaceId } : null;
|
|
36
|
+
}
|
|
37
|
+
export const endpointOf = (workspaceId, model) => `wss://${workspaceHost(workspaceId)}/api-ws/v1/realtime?model=${model}`;
|
|
38
|
+
/** 轮次模式 → session.turn_detection */
|
|
39
|
+
export function turnDetection(turn, silenceMs, vad = {}) {
|
|
40
|
+
if (turn === 'manual')
|
|
41
|
+
return null;
|
|
42
|
+
return { type: turn, threshold: 0.5, prefix_padding_ms: 300, silence_duration_ms: silenceMs, create_response: vad.createResponse ?? true, interrupt_response: vad.interruptResponse ?? true };
|
|
43
|
+
}
|
|
44
|
+
/** session.update 的正文 */
|
|
45
|
+
export function sessionConfig(o) {
|
|
46
|
+
return {
|
|
47
|
+
modalities: ['text', 'audio'],
|
|
48
|
+
instructions: o.instructions,
|
|
49
|
+
voice: o.voice,
|
|
50
|
+
audio: {
|
|
51
|
+
input: { format: { type: 'pcm', sample_rate: INPUT_RATE, sample_format: 's16le', channels: 1 } },
|
|
52
|
+
output: { voice: o.voice, format: { type: 'pcm', sample_rate: OUTPUT_RATE } },
|
|
53
|
+
},
|
|
54
|
+
turn_detection: turnDetection(o.turn, o.silenceMs, o.vad),
|
|
55
|
+
input_audio_transcription: { model: TRANSCRIBE_MODEL },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const usageOf = (r) => {
|
|
59
|
+
const u = r?.usage;
|
|
60
|
+
if (!u)
|
|
61
|
+
return null;
|
|
62
|
+
const n = (v) => (typeof v === 'number' ? v : 0);
|
|
63
|
+
const inD = (u.input_token_details ?? u.input_tokens_details);
|
|
64
|
+
const outD = (u.output_token_details ?? u.output_tokens_details);
|
|
65
|
+
return { input: n(u.input_tokens), output: n(u.output_tokens), inputAudio: n(inD?.audio_tokens), outputAudio: n(outD?.audio_tokens) };
|
|
66
|
+
};
|
|
67
|
+
export class RealtimeSession {
|
|
68
|
+
model;
|
|
69
|
+
voice;
|
|
70
|
+
ws = null;
|
|
71
|
+
ready = false;
|
|
72
|
+
closed = false;
|
|
73
|
+
turn;
|
|
74
|
+
silenceMs;
|
|
75
|
+
tutorText = '';
|
|
76
|
+
pendingUpdate = null;
|
|
77
|
+
/** 正在出的回答数(response.cancel 只在有回答时发,不然服务端报错) */
|
|
78
|
+
active = 0;
|
|
79
|
+
opts;
|
|
80
|
+
ev;
|
|
81
|
+
constructor(opts, ev = {}) {
|
|
82
|
+
this.opts = opts;
|
|
83
|
+
this.ev = ev;
|
|
84
|
+
this.model = opts.model ?? REALTIME_MODEL;
|
|
85
|
+
this.voice = opts.voice ?? REALTIME_VOICE;
|
|
86
|
+
this.turn = opts.turn ?? 'manual';
|
|
87
|
+
this.silenceMs = opts.silenceMs ?? 1200;
|
|
88
|
+
}
|
|
89
|
+
get isReady() { return this.ready && !this.closed; }
|
|
90
|
+
/** 老师说到一半、还没 done 的字(挂断时记进字幕用) */
|
|
91
|
+
get partialTutorText() { return this.tutorText; }
|
|
92
|
+
get turnMode() { return this.turn; }
|
|
93
|
+
get responding() { return this.active > 0; }
|
|
94
|
+
/** 连上、配好会话;回 null 成功,否则是原因(no_auth / connect / timeout / <服务端的话>) */
|
|
95
|
+
async connect() {
|
|
96
|
+
let url = this.opts.endpoint;
|
|
97
|
+
let key = '';
|
|
98
|
+
if (url)
|
|
99
|
+
key = this.opts.env?.DASHSCOPE_API_KEY ?? 'sk-test';
|
|
100
|
+
else {
|
|
101
|
+
const auth = this.opts.auth ?? dashscopeAuth(this.opts.env);
|
|
102
|
+
if (!auth)
|
|
103
|
+
return 'no_auth';
|
|
104
|
+
key = auth.key;
|
|
105
|
+
url = endpointOf(auth.workspaceId, this.model);
|
|
106
|
+
}
|
|
107
|
+
const timeoutMs = this.opts.timeoutMs ?? 10_000;
|
|
108
|
+
return new Promise((resolve) => {
|
|
109
|
+
let settled = false;
|
|
110
|
+
const finish = (why) => { if (!settled) {
|
|
111
|
+
settled = true;
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
resolve(why);
|
|
114
|
+
} };
|
|
115
|
+
const timer = setTimeout(() => { finish('timeout'); this.close('timeout'); }, timeoutMs);
|
|
116
|
+
let ws;
|
|
117
|
+
try {
|
|
118
|
+
ws = new WebSocket(url, { headers: { authorization: `Bearer ${key}` } });
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
finish(`connect:${err instanceof Error ? err.message : String(err)}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.ws = ws;
|
|
125
|
+
ws.onerror = () => { finish('connect'); };
|
|
126
|
+
ws.onclose = (e) => {
|
|
127
|
+
this.ready = false;
|
|
128
|
+
finish(`closed:${e.code}`);
|
|
129
|
+
if (!this.closed) {
|
|
130
|
+
this.closed = true;
|
|
131
|
+
this.ev.onClose?.(`closed:${e.code}${e.reason ? ` ${e.reason}` : ''}`);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
ws.onmessage = (m) => {
|
|
135
|
+
let e;
|
|
136
|
+
try {
|
|
137
|
+
e = JSON.parse(String(m.data));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (e.type === 'session.created') {
|
|
143
|
+
this.sendRaw({ type: 'session.update', session: sessionConfig({ instructions: this.opts.instructions, voice: this.voice, turn: this.turn, silenceMs: this.silenceMs, vad: this.opts.vad }) });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (e.type === 'session.updated') {
|
|
147
|
+
if (!this.ready) {
|
|
148
|
+
this.ready = true;
|
|
149
|
+
finish(null);
|
|
150
|
+
}
|
|
151
|
+
if (this.pendingUpdate) {
|
|
152
|
+
clearTimeout(this.pendingUpdate.timer);
|
|
153
|
+
this.pendingUpdate.resolve(true);
|
|
154
|
+
this.pendingUpdate = null;
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (e.type === 'error') {
|
|
159
|
+
const err = e.error;
|
|
160
|
+
const msg = `${err?.code ? `${err.code}: ` : ''}${err?.message ?? JSON.stringify(e).slice(0, 200)}`;
|
|
161
|
+
if (!this.ready) {
|
|
162
|
+
finish(msg);
|
|
163
|
+
this.close('error');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (this.pendingUpdate) {
|
|
167
|
+
clearTimeout(this.pendingUpdate.timer);
|
|
168
|
+
this.pendingUpdate.resolve(false);
|
|
169
|
+
this.pendingUpdate = null;
|
|
170
|
+
}
|
|
171
|
+
// semantic_vad 不收的一段(2026-10-20 照 Qwen-Live-Harness:不致命,回到在听)
|
|
172
|
+
if (/not accepted by semantic turn detection/i.test(err?.message ?? '')) {
|
|
173
|
+
this.ev.onRejected?.();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.ev.onError?.(msg);
|
|
177
|
+
// user_idle_timeout / response_idle_timeout:服务端说会话关了,连接却还开着,再发什么都没回音(2026-10-19 ray 那次挂了 10 分钟才 close),当场关
|
|
178
|
+
if (err?.code?.endsWith('_idle_timeout'))
|
|
179
|
+
this.close(`idle:${err.code}`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.handle(e);
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
handle(e) {
|
|
187
|
+
switch (e.type) {
|
|
188
|
+
case 'response.audio.delta':
|
|
189
|
+
if (typeof e.delta === 'string')
|
|
190
|
+
this.ev.onAudio?.(Buffer.from(e.delta, 'base64'));
|
|
191
|
+
break;
|
|
192
|
+
case 'response.audio_transcript.delta':
|
|
193
|
+
case 'response.text.delta':
|
|
194
|
+
if (typeof e.delta === 'string') {
|
|
195
|
+
this.tutorText += e.delta;
|
|
196
|
+
this.ev.onTutorText?.(e.delta, null);
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
case 'response.audio_transcript.done':
|
|
200
|
+
case 'response.text.done': {
|
|
201
|
+
const whole = typeof e.transcript === 'string' ? e.transcript : typeof e.text === 'string' ? e.text : this.tutorText;
|
|
202
|
+
this.tutorText = '';
|
|
203
|
+
this.ev.onTutorText?.('', whole);
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
case 'conversation.item.input_audio_transcription.completed':
|
|
207
|
+
if (typeof e.transcript === 'string')
|
|
208
|
+
this.ev.onKidText?.(e.transcript.trim());
|
|
209
|
+
break;
|
|
210
|
+
case 'conversation.item.input_audio_transcription.failed':
|
|
211
|
+
this.ev.onKidFailed?.();
|
|
212
|
+
break;
|
|
213
|
+
case 'input_audio_buffer.speech_started':
|
|
214
|
+
this.ev.onSpeech?.('started');
|
|
215
|
+
break;
|
|
216
|
+
case 'input_audio_buffer.speech_stopped':
|
|
217
|
+
this.ev.onSpeech?.('stopped');
|
|
218
|
+
break;
|
|
219
|
+
case 'response.created':
|
|
220
|
+
this.active++;
|
|
221
|
+
this.ev.onResponse?.('created', null, null);
|
|
222
|
+
break;
|
|
223
|
+
case 'response.done': {
|
|
224
|
+
this.active = Math.max(0, this.active - 1);
|
|
225
|
+
const r = e.response;
|
|
226
|
+
this.ev.onResponse?.('done', r?.status ?? null, usageOf(r));
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
default: break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
sendRaw(msg) {
|
|
233
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
|
|
234
|
+
return false;
|
|
235
|
+
this.ws.send(JSON.stringify(msg));
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
/** 一块孩子的声音(PCM16 16k 单声道) */
|
|
239
|
+
appendAudio(pcm) { return this.isReady && this.sendRaw({ type: 'input_audio_buffer.append', audio: pcm.toString('base64') }); }
|
|
240
|
+
/** 一张图(JPEG)跟着这一段声音交:百炼要先有声音再有图(Qwen-Live-Harness 也这样),commit 时一起进对话;太大(见 MAX_IMAGE_B64)不发、回 false,调用方先缩 */
|
|
241
|
+
appendImage(jpeg) {
|
|
242
|
+
const image = jpeg.toString('base64');
|
|
243
|
+
return this.isReady && image.length <= MAX_IMAGE_B64 && this.sendRaw({ type: 'input_image_buffer.append', image });
|
|
244
|
+
}
|
|
245
|
+
/** 手动轮次:松手 */
|
|
246
|
+
commit() { return this.isReady && this.sendRaw({ type: 'input_audio_buffer.commit' }); }
|
|
247
|
+
clearAudio() { return this.isReady && this.sendRaw({ type: 'input_audio_buffer.clear' }); }
|
|
248
|
+
/**
|
|
249
|
+
* 心跳:1 秒全零的声音接着 clear,不 commit、不要回答(照 Qwen-Live-Harness 的静音心跳)。百炼 300 秒没输入就关会话(user_idle_timeout)。
|
|
250
|
+
* 2026-10-20 真连(scripts/probe-live-idle.mjs,手动轮次,每 30 秒一次):不发的 302 秒被关;只 append 不 clear 的 300 秒报 response_idle_timeout
|
|
251
|
+
* (缓冲里有输入一直没回答),会话就死了;append 完马上 clear 的过了 400 秒照常说话。
|
|
252
|
+
*/
|
|
253
|
+
heartbeat() { return this.appendAudio(HEARTBEAT_PCM) && this.clearAudio(); }
|
|
254
|
+
/** 让老师说;instructions = 只管这一次回答的指令(response.instructions,不进对话历史、不改系统提示) */
|
|
255
|
+
createResponse(instructions) { return this.isReady && this.sendRaw({ type: 'response.create', ...(instructions ? { response: { instructions } } : {}) }); }
|
|
256
|
+
/** 老师正在说就掐掉(没在说不发,服务端会报错) */
|
|
257
|
+
cancel() { return this.isReady && this.active > 0 && this.sendRaw({ type: 'response.cancel' }); }
|
|
258
|
+
/** 应用塞一条文字进对话(口语课跟读段「现在读这句」、切段的指令);role 缺省 user */
|
|
259
|
+
addText(text, role = 'user') {
|
|
260
|
+
return this.isReady && this.sendRaw({ type: 'conversation.item.create', item: { type: 'message', role, content: [{ type: 'input_text', text }] } });
|
|
261
|
+
}
|
|
262
|
+
/** 中途在手动与 VAD 之间切(只给 type,silence 喂过音频后改不了);等 session.updated,错或超时回 false */
|
|
263
|
+
setTurn(turn, timeoutMs = 5000) {
|
|
264
|
+
if (!this.isReady)
|
|
265
|
+
return Promise.resolve(false);
|
|
266
|
+
return new Promise((resolve) => {
|
|
267
|
+
if (this.pendingUpdate) {
|
|
268
|
+
clearTimeout(this.pendingUpdate.timer);
|
|
269
|
+
this.pendingUpdate.resolve(false);
|
|
270
|
+
}
|
|
271
|
+
const timer = setTimeout(() => { this.pendingUpdate = null; resolve(false); }, timeoutMs);
|
|
272
|
+
this.pendingUpdate = { resolve: (ok) => { if (ok)
|
|
273
|
+
this.turn = turn; resolve(ok); }, timer };
|
|
274
|
+
this.sendRaw({ type: 'session.update', session: { turn_detection: turn === 'manual' ? null : { type: turn, create_response: true, interrupt_response: true } } });
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
close(reason = 'bye') {
|
|
278
|
+
if (this.closed)
|
|
279
|
+
return;
|
|
280
|
+
this.closed = true;
|
|
281
|
+
this.ready = false;
|
|
282
|
+
try {
|
|
283
|
+
this.ws?.close(1000, reason);
|
|
284
|
+
}
|
|
285
|
+
catch { /* 已经断了 */ }
|
|
286
|
+
this.ev.onClose?.(reason);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 实时老师每次让嘴开口的那句指令(response.instructions):纯函数。
|
|
3
|
+
* 嘴自己不决定说什么,只两件事:几种场合先说一句(拍照、没听清、热身)、把脑写的讲稿原样念。说法是 2026-10-19 P1 实测过的(scripts/probe-live.mjs cue):
|
|
4
|
+
* 「原样说」和那句一起放进 response.instructions,4/4 一字不差;只放 system 消息、或要说的话当用户消息发,它会改写、会把问题自己答了。
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 孩子这一轮是怎么来的:按住说的、打字的、拍的照片、交的卡、点了首页的按钮(activity 的开场、接着);
|
|
8
|
+
* answer = 按住说的、上一句念给他的是问句(看图说话这种一问一答);empty = 按住了但转写是空的;
|
|
9
|
+
* warm = 口语单拍好、进来上口语课,嘴先说要热身
|
|
10
|
+
*/
|
|
11
|
+
export type CatchKind = 'voice' | 'answer' | 'text' | 'photo' | 'card' | 'start' | 'empty' | 'warm';
|
|
12
|
+
/** 这一轮嘴先说的那句的指令;null = 嘴不出声。en = 口语课打电话那段 */
|
|
13
|
+
export declare function catchCue(kind: CatchKind, lang?: 'zh' | 'en'): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* 交给嘴之前,把它念不好的写法换成念法(板上、字幕照旧是原文)。2026-10-20 真连(照念 → 声音转写回字):× + − = ÷ > 1/2 都念对;
|
|
16
|
+
* 空括号念成「空格」「括号」(「每份都是( )个」成了「每份都是空格」),□ 念得含糊(「□+5=12」听成「正五等于十二」)。
|
|
17
|
+
* 空括号、下划线的空照数学老师的说法念「几」(「二四得几」「每份都是几个」),□ 念「方框」;换了以后「方框+5」的 + 念成「正」,
|
|
18
|
+
* 所以运算符一律换成字(+ 加、− 减、× 乘、÷ 除以、= 等于、> 大于、< 小于;ASCII 的 - 只在两个数之间算减)。
|
|
19
|
+
*/
|
|
20
|
+
export declare function spoken(text: string): string;
|
|
21
|
+
/** 照念脑写的这一拍讲稿(几句连着念);句子换成念法(spoken)、原样拼起来放进「」 */
|
|
22
|
+
export declare function readCue(texts: readonly string[]): string;
|
|
23
|
+
/**
|
|
24
|
+
* 边画边聊:画板开着时嘴可以自己说一句(看图说、问一句),也接住孩子画着说的话。
|
|
25
|
+
* 应用定题:什么时候说、说哪个颜色、问不问、说过什么、孩子答过什么;嘴只看图(那一刻导出的 jpeg 跟着这一段声音进了对话)说一两句具体的。
|
|
26
|
+
* 10-22 探针(qwen3.8-omni-flash-realtime,10-21 晚那张的四个时刻):清楚的形状认得出,照题目猜会错(乌云说成山、浪说成黄河)、
|
|
27
|
+
* 会原样重复上一句、会用大人词(生动、翻涌)。所以:不给它照题目猜,只用颜色问、用「像……」;把它说过的原话给它,不许重复。
|
|
28
|
+
*/
|
|
29
|
+
export interface ChatAsk {
|
|
30
|
+
/** 画板卡的题目(纸没有);只让嘴知道他想画什么,不许照它猜 */
|
|
31
|
+
prompt?: string;
|
|
32
|
+
/** 说哪种颜色(「红」);null / 没有 = 整张 */
|
|
33
|
+
color?: string | null;
|
|
34
|
+
/** 问不问(上一问没回就只说不问) */
|
|
35
|
+
ask?: boolean;
|
|
36
|
+
/** 画了多久(「约 3 分钟」) */
|
|
37
|
+
minutes?: string;
|
|
38
|
+
/** 各颜色几笔、在哪(笔多的在前) */
|
|
39
|
+
colors?: {
|
|
40
|
+
name: string;
|
|
41
|
+
strokes: number;
|
|
42
|
+
where: string;
|
|
43
|
+
}[];
|
|
44
|
+
/** 清空重画过几次 */
|
|
45
|
+
cleared?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 边画边聊嘴那一次的指令:look = 应用觉得该说一句了(孩子换了颜色、停笔很久),对面没说话,那段声音是静音;
|
|
49
|
+
* reply = 孩子按着头像画着说了一句(听他的声音)。said = 这张画上之前说过的,一句一行(「孩子:…」「你:…」)
|
|
50
|
+
*/
|
|
51
|
+
export declare function chatCue(kind: 'look' | 'reply', a: ChatAsk, said?: readonly string[]): string;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 实时老师每次让嘴开口的那句指令(response.instructions):纯函数。
|
|
3
|
+
* 嘴自己不决定说什么,只两件事:几种场合先说一句(拍照、没听清、热身)、把脑写的讲稿原样念。说法是 2026-10-19 P1 实测过的(scripts/probe-live.mjs cue):
|
|
4
|
+
* 「原样说」和那句一起放进 response.instructions,4/4 一字不差;只放 system 消息、或要说的话当用户消息发,它会改写、会把问题自己答了。
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 嘴先说的那句;没写的那几种嘴不出声,等脑的第一句。
|
|
8
|
+
* 10-20 晚 Ray 真用:「好,我讲给你听」「我想一想怎么讲」每轮都有,和孩子说的对不上(「老师。」也接成讲给你听),脑还常把它再说一遍;脑的第一句 3–5 秒就到
|
|
9
|
+
*/
|
|
10
|
+
const CATCH = {
|
|
11
|
+
// 拍照脑要读 14–29 秒,先说一句;说明白看的是孩子发的图片
|
|
12
|
+
photo: '孩子拍来了照片。只说一句「我看一看图片」,不说你看到了什么,说完就停。',
|
|
13
|
+
// 听不清由应用判(转写是空的),不交给脑,嘴不说孩子就不知道要再说一遍
|
|
14
|
+
empty: '孩子按了说话,但什么都没说清。只说一句「没听清,再说一遍吧」,说完就停。',
|
|
15
|
+
warm: '孩子拍好了今天的口语单,要开始上口语课。只说一句「好,我们先热热身!」,说完就停。',
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* 口语课打电话那段:老师说英文,嘴先说的也换英文。
|
|
19
|
+
* 指令照旧用中文写(嘴听得懂),要它说的是英文
|
|
20
|
+
*/
|
|
21
|
+
const CATCH_EN = {
|
|
22
|
+
photo: '孩子拍来了照片。只说一句「Let me look at your picture.」,不说你看到了什么,说完就停。',
|
|
23
|
+
empty: '孩子说了,但什么都没说清。只说一句「Sorry? Say it again.」,说完就停。',
|
|
24
|
+
warm: '孩子要开始上口语课。只说一句「Okay, let\'s start!」,说完就停。',
|
|
25
|
+
};
|
|
26
|
+
/** 这一轮嘴先说的那句的指令;null = 嘴不出声。en = 口语课打电话那段 */
|
|
27
|
+
export function catchCue(kind, lang = 'zh') {
|
|
28
|
+
return (lang === 'en' ? CATCH_EN : CATCH)[kind] ?? null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 交给嘴之前,把它念不好的写法换成念法(板上、字幕照旧是原文)。2026-10-20 真连(照念 → 声音转写回字):× + − = ÷ > 1/2 都念对;
|
|
32
|
+
* 空括号念成「空格」「括号」(「每份都是( )个」成了「每份都是空格」),□ 念得含糊(「□+5=12」听成「正五等于十二」)。
|
|
33
|
+
* 空括号、下划线的空照数学老师的说法念「几」(「二四得几」「每份都是几个」),□ 念「方框」;换了以后「方框+5」的 + 念成「正」,
|
|
34
|
+
* 所以运算符一律换成字(+ 加、− 减、× 乘、÷ 除以、= 等于、> 大于、< 小于;ASCII 的 - 只在两个数之间算减)。
|
|
35
|
+
*/
|
|
36
|
+
export function spoken(text) {
|
|
37
|
+
return text
|
|
38
|
+
.replace(/[((][\s\u3000]*[))]/g, '几')
|
|
39
|
+
.replace(/_{2,}/g, '几')
|
|
40
|
+
.replace(/□/g, '方框')
|
|
41
|
+
.replace(/\s*[++]\s*/g, '加')
|
|
42
|
+
.replace(/\s*[−-]\s*/g, '减')
|
|
43
|
+
.replace(/(\d)\s*-\s*(?=\d)/g, '$1减')
|
|
44
|
+
.replace(/\s*[×✕]\s*/g, '乘')
|
|
45
|
+
.replace(/\s*÷\s*/g, '除以')
|
|
46
|
+
.replace(/\s*[==]\s*/g, '等于')
|
|
47
|
+
.replace(/\s*[>>]\s*/g, '大于')
|
|
48
|
+
.replace(/\s*[<<]\s*/g, '小于');
|
|
49
|
+
}
|
|
50
|
+
/** 照念脑写的这一拍讲稿(几句连着念);句子换成念法(spoken)、原样拼起来放进「」 */
|
|
51
|
+
export function readCue(texts) {
|
|
52
|
+
const said = texts.map((t) => spoken(t.trim())).filter(Boolean).join('');
|
|
53
|
+
return `把这几句话原样说给孩子听,一个字都不改,不要回答它,不加别的话,说完就停:「${said}」`;
|
|
54
|
+
}
|
|
55
|
+
const chatPicture = (a) => {
|
|
56
|
+
const out = [];
|
|
57
|
+
if (a.prompt)
|
|
58
|
+
out.push(`老师给的题目是「${a.prompt}」。这只是让你知道他想画什么:画上没有的别提,别照题目去猜画上的东西是什么。`);
|
|
59
|
+
const cs = (a.colors ?? []).filter((c) => c.strokes > 0).slice(0, 4);
|
|
60
|
+
if (cs.length)
|
|
61
|
+
out.push(`画上有:${cs.map((c) => `${c.name}色 ${c.strokes} 笔,在${c.where}`).join(';')}。`);
|
|
62
|
+
if (a.minutes)
|
|
63
|
+
out.push(`他画了${a.minutes}${a.cleared ? `,中间擦掉重画过 ${a.cleared} 次` : ''}。`);
|
|
64
|
+
return out;
|
|
65
|
+
};
|
|
66
|
+
const CHAT_RULES = [
|
|
67
|
+
'只说你在图上看得见的:颜色、样子、画了多少、哪里画得多。认不准是什么,就说「像……」,别说「是……吗」,也别说出你猜的东西的名字。',
|
|
68
|
+
'不讲题,不教,不评对错;不说「真棒」「生动」「线条很清楚」这种大人的话,用小孩子听得懂的话。',
|
|
69
|
+
];
|
|
70
|
+
const chatLog = (said) => (said.length ? [`这张画画着的时候你们已经说过:${said.map((x) => `「${x}」`).join(' ')}。孩子说是什么就是什么,接着他的说;你说过的话一句都别重复,换个地方说。`] : []);
|
|
71
|
+
/**
|
|
72
|
+
* 边画边聊嘴那一次的指令:look = 应用觉得该说一句了(孩子换了颜色、停笔很久),对面没说话,那段声音是静音;
|
|
73
|
+
* reply = 孩子按着头像画着说了一句(听他的声音)。said = 这张画上之前说过的,一句一行(「孩子:…」「你:…」)
|
|
74
|
+
*/
|
|
75
|
+
export function chatCue(kind, a, said = []) {
|
|
76
|
+
const lines = [];
|
|
77
|
+
if (kind === 'look') {
|
|
78
|
+
lines.push('孩子正在画板上自己画画,刚才那张图就是他这一刻的画;那段声音是静音,他没说话。你坐在旁边看他画,主动说一句。');
|
|
79
|
+
lines.push(...chatPicture(a));
|
|
80
|
+
lines.push(a.color ? `说说他刚画完的${a.color}色那部分。` : '说说整张画。');
|
|
81
|
+
lines.push(...CHAT_RULES);
|
|
82
|
+
lines.push(a.ask === false ? '这次只说一句,不问。' : `说完问一句,问${a.color ? `${a.color}色画的` : '他画的'}是什么,只问这一个。`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
lines.push('孩子正在画板上自己画画,一边画一边跟你说了一句(听他的声音),刚才那张图是他这一刻的画。像坐在旁边看他画的老师那样,接住他的话。');
|
|
86
|
+
lines.push(...chatPicture(a));
|
|
87
|
+
lines.push(...CHAT_RULES);
|
|
88
|
+
lines.push('他说是什么就是什么,顺着他说,说一句你在图上看得见的;可以再问一句,也可以不问。');
|
|
89
|
+
lines.push('他要是问题目、问怎么做、要你教他,别答,说「按住『给老师看』跟老师说,老师好好看看」。');
|
|
90
|
+
}
|
|
91
|
+
lines.push(...chatLog(said));
|
|
92
|
+
lines.push('一共一两句,短,说完就停。');
|
|
93
|
+
return lines.join('\n');
|
|
94
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 一直听:VAD 切出来、转写回来的一句,收下(算孩子对老师说的,同按下 + 松手)还是放过。纯函数。
|
|
3
|
+
* 按顺序:空的(咳嗽、碰桌子)→ 只有语气词、老师上一句又不是问句 → 是嘴自己的回声(开口时嘴的声音还在放,且和嘴最近说过的字重合过六成)→ 其余收下。
|
|
4
|
+
* 回声只在嘴出声的时候才有:嘴停了以后孩子照着说一遍(跟读、复述老师的话)字一样也是孩子的(2026-10-20 评估口语课时改)。
|
|
5
|
+
* 「不是对老师说的」(孩子跟家长说话)不在这里判(待)。
|
|
6
|
+
*/
|
|
7
|
+
export type Skip = 'empty' | 'filler' | 'echo';
|
|
8
|
+
export type Verdict = {
|
|
9
|
+
take: true;
|
|
10
|
+
} | {
|
|
11
|
+
take: false;
|
|
12
|
+
why: Skip;
|
|
13
|
+
};
|
|
14
|
+
/** 嘴说过的字留多久用来比回声 */
|
|
15
|
+
export declare const ECHO_WINDOW_MS = 10000;
|
|
16
|
+
/** 只留字:去标点、空白,英文小写 */
|
|
17
|
+
export declare const bare: (s: string) => string;
|
|
18
|
+
/** text 有几成落在 said 里(按相邻两字比,中英都行) */
|
|
19
|
+
export declare function overlap(text: string, said: string): number;
|
|
20
|
+
/**
|
|
21
|
+
* 判一句。asked = 老师上一句是问句(孩子在答,「嗯」「对」就是回答);said = 嘴最近说过的话(带说完的墙钟),now 用来挑 ECHO_WINDOW_MS 以内的;
|
|
22
|
+
* mouthOn = 孩子开口那一刻嘴的声音还在放(或刚放完 300 毫秒内),false 时不比回声
|
|
23
|
+
*/
|
|
24
|
+
export declare function judge(text: string, o: {
|
|
25
|
+
asked: boolean;
|
|
26
|
+
said: {
|
|
27
|
+
text: string;
|
|
28
|
+
at: number;
|
|
29
|
+
}[];
|
|
30
|
+
now: number;
|
|
31
|
+
mouthOn?: boolean;
|
|
32
|
+
}): Verdict;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 一直听:VAD 切出来、转写回来的一句,收下(算孩子对老师说的,同按下 + 松手)还是放过。纯函数。
|
|
3
|
+
* 按顺序:空的(咳嗽、碰桌子)→ 只有语气词、老师上一句又不是问句 → 是嘴自己的回声(开口时嘴的声音还在放,且和嘴最近说过的字重合过六成)→ 其余收下。
|
|
4
|
+
* 回声只在嘴出声的时候才有:嘴停了以后孩子照着说一遍(跟读、复述老师的话)字一样也是孩子的(2026-10-20 评估口语课时改)。
|
|
5
|
+
* 「不是对老师说的」(孩子跟家长说话)不在这里判(待)。
|
|
6
|
+
*/
|
|
7
|
+
/** 嘴说过的字留多久用来比回声 */
|
|
8
|
+
export const ECHO_WINDOW_MS = 10_000;
|
|
9
|
+
/** 重合到这么多算回声 */
|
|
10
|
+
const ECHO_RATIO = 0.6;
|
|
11
|
+
/** 语气词与笑声:中文按字(全是这些字就算,「哈哈哈」「嗯嗯」也算),英文按词(server_vad 把「嗯」转成 And.,2026-10-20 探针) */
|
|
12
|
+
const FILLER_CHARS = new Set([...'嗯啊哦呃哈额唔噢诶欸哎呵嘿']);
|
|
13
|
+
const FILLER_WORDS = new Set(['um', 'uh', 'hmm', 'mm', 'mhm', 'ah', 'oh', 'eh', 'en', 'and', 'haha', 'hahaha']);
|
|
14
|
+
const isFiller = (text) => {
|
|
15
|
+
const t = bare(text);
|
|
16
|
+
if (t && [...t].every((c) => FILLER_CHARS.has(c)))
|
|
17
|
+
return true;
|
|
18
|
+
const words = text.toLowerCase().split(/[^a-z]+/).filter(Boolean);
|
|
19
|
+
return words.length > 0 && words.length <= 3 && words.every((w) => FILLER_WORDS.has(w)) && !/[\u4e00-\u9fff]/.test(text);
|
|
20
|
+
};
|
|
21
|
+
/** 只留字:去标点、空白,英文小写 */
|
|
22
|
+
export const bare = (s) => s.toLowerCase().replace(/[\s\p{P}\p{S}]/gu, '');
|
|
23
|
+
/** 相邻两个字一组;一个字的就是它自己 */
|
|
24
|
+
function pairs(s) {
|
|
25
|
+
const cs = [...s];
|
|
26
|
+
if (cs.length < 2)
|
|
27
|
+
return cs;
|
|
28
|
+
return cs.slice(1).map((c, i) => cs[i] + c);
|
|
29
|
+
}
|
|
30
|
+
/** text 有几成落在 said 里(按相邻两字比,中英都行) */
|
|
31
|
+
export function overlap(text, said) {
|
|
32
|
+
const t = pairs(bare(text));
|
|
33
|
+
if (!t.length)
|
|
34
|
+
return 0;
|
|
35
|
+
const s = new Set(pairs(bare(said)));
|
|
36
|
+
return t.filter((p) => s.has(p)).length / t.length;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 判一句。asked = 老师上一句是问句(孩子在答,「嗯」「对」就是回答);said = 嘴最近说过的话(带说完的墙钟),now 用来挑 ECHO_WINDOW_MS 以内的;
|
|
40
|
+
* mouthOn = 孩子开口那一刻嘴的声音还在放(或刚放完 300 毫秒内),false 时不比回声
|
|
41
|
+
*/
|
|
42
|
+
export function judge(text, o) {
|
|
43
|
+
const t = bare(text);
|
|
44
|
+
if (!t)
|
|
45
|
+
return { take: false, why: 'empty' };
|
|
46
|
+
if (!o.asked && isFiller(text))
|
|
47
|
+
return { take: false, why: 'filler' };
|
|
48
|
+
const recent = o.said.filter((x) => o.now - x.at <= ECHO_WINDOW_MS).map((x) => x.text).join('');
|
|
49
|
+
// 两个字以内的不比回声(「对」「好的」碰巧在嘴的话里也常见)
|
|
50
|
+
if (o.mouthOn !== false && recent && t.length > 2 && overlap(t, recent) >= ECHO_RATIO)
|
|
51
|
+
return { take: false, why: 'echo' };
|
|
52
|
+
return { take: true };
|
|
53
|
+
}
|