cotutor 0.3.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.
Files changed (205) hide show
  1. package/LICENSE +9 -7
  2. package/NOTICE +3 -3
  3. package/README.md +36 -13
  4. package/avatars/chinese.webp +0 -0
  5. package/avatars/english.webp +0 -0
  6. package/avatars/math.webp +0 -0
  7. package/bin/cotutor.js +13 -3
  8. package/cards/photo/card.css +10 -3
  9. package/cards/photo/card.md +2 -2
  10. package/cards/tutor/card.css +13 -0
  11. package/cards/tutor/card.md +2 -1
  12. package/cards//346/235/277/344/271/246/346/200/216/344/271/210/345/206/231.md +1 -1
  13. package/dist/app/activity-eval.js +3 -2
  14. package/dist/app/cli/activities.d.ts +8 -1
  15. package/dist/app/cli/activities.js +42 -4
  16. package/dist/app/cli/cert.d.ts +5 -0
  17. package/dist/app/cli/cert.js +20 -7
  18. package/dist/app/cli/chalk/browser.d.ts +2 -2
  19. package/dist/app/cli/chalk/browser.js +22 -7
  20. package/dist/app/cli/chalk/main.js +3 -2
  21. package/dist/app/cli/doctor.d.ts +6 -5
  22. package/dist/app/cli/doctor.js +107 -171
  23. package/dist/app/cli/firewall.d.ts +24 -0
  24. package/dist/app/cli/firewall.js +79 -0
  25. package/dist/app/cli/init.js +8 -5
  26. package/dist/app/cli/main.js +347 -24
  27. package/dist/app/cli/migrate.d.ts +2 -11
  28. package/dist/app/cli/migrate.js +1 -59
  29. package/dist/app/cli/port-holder.d.ts +16 -0
  30. package/dist/app/cli/port-holder.js +34 -0
  31. package/dist/app/cli/port.d.ts +37 -0
  32. package/dist/app/cli/port.js +116 -0
  33. package/dist/app/cli/sandbox.d.ts +25 -0
  34. package/dist/app/cli/sandbox.js +166 -0
  35. package/dist/app/cli/serve-log.d.ts +13 -0
  36. package/dist/app/cli/serve-log.js +90 -0
  37. package/dist/app/cli/serve.d.ts +18 -4
  38. package/dist/app/cli/serve.js +78 -8
  39. package/dist/app/cli/service-windows.d.ts +50 -0
  40. package/dist/app/cli/service-windows.js +117 -0
  41. package/dist/app/cli/service.d.ts +138 -0
  42. package/dist/app/cli/service.js +429 -0
  43. package/dist/app/cli/skeleton.d.ts +1 -1
  44. package/dist/app/cli/skeleton.js +13 -13
  45. package/dist/app/cli/skills.d.ts +5 -1
  46. package/dist/app/cli/skills.js +14 -4
  47. package/dist/app/cli/tray-icons.d.ts +11 -0
  48. package/dist/app/cli/tray-icons.js +87 -0
  49. package/dist/app/cli/tray-ids.d.ts +6 -0
  50. package/dist/app/cli/tray-ids.js +6 -0
  51. package/dist/app/cli/tray-mac.d.ts +8 -0
  52. package/dist/app/cli/tray-mac.js +140 -0
  53. package/dist/app/cli/tray-run.d.ts +1 -0
  54. package/dist/app/cli/tray-run.js +33 -0
  55. package/dist/app/cli/tray-windows.d.ts +5 -0
  56. package/dist/app/cli/tray-windows.js +166 -0
  57. package/dist/app/cli/tray.d.ts +205 -0
  58. package/dist/app/cli/tray.js +618 -0
  59. package/dist/app/cli/usage.d.ts +1 -1
  60. package/dist/app/cli/usage.js +11 -6
  61. package/dist/app/dictation/route.js +3 -2
  62. package/dist/app/http/app.d.ts +5 -10
  63. package/dist/app/http/app.js +45 -98
  64. package/dist/app/http/avatars.d.ts +17 -0
  65. package/dist/app/http/avatars.js +35 -0
  66. package/dist/app/http/dev-tutor-files.js +1 -1
  67. package/dist/app/http/parent-home.d.ts +14 -0
  68. package/dist/app/http/parent-home.js +69 -6
  69. package/dist/app/http/parent-look.d.ts +2 -0
  70. package/dist/app/http/parent-look.js +151 -0
  71. package/dist/app/http/raw-view.js +3 -3
  72. package/dist/app/http/voice-preview.d.ts +4 -0
  73. package/dist/app/http/voice-preview.js +34 -0
  74. package/dist/app/machine/bailian.d.ts +0 -2
  75. package/dist/app/machine/bailian.js +5 -6
  76. package/dist/app/mock/mock-live.js +1 -1
  77. package/dist/app/mock/mock.d.ts +4 -2
  78. package/dist/app/mock/mock.js +20 -12
  79. package/dist/app/replay.js +4 -4
  80. package/dist/app/skills/home-doc.js +1 -1
  81. package/dist/app/workspace.d.ts +9 -4
  82. package/dist/app/workspace.js +33 -10
  83. package/dist/app/ws/route.d.ts +8 -2
  84. package/dist/app/ws/route.js +7 -3
  85. package/dist/board/parse.d.ts +0 -2
  86. package/dist/board/parse.js +0 -6
  87. package/dist/board/presentation.d.ts +9 -15
  88. package/dist/board/presentation.js +8 -10
  89. package/dist/brain/api-tools.js +2 -1
  90. package/dist/brain/api.d.ts +12 -17
  91. package/dist/brain/api.js +16 -33
  92. package/dist/brain/index.d.ts +23 -71
  93. package/dist/brain/index.js +6 -69
  94. package/dist/brain/log.d.ts +5 -0
  95. package/dist/brain/log.js +22 -0
  96. package/dist/brain/runtime.d.ts +30 -0
  97. package/dist/brain/runtime.js +23 -0
  98. package/dist/brain/{cli/stream-json.d.ts → stream-json.d.ts} +2 -4
  99. package/dist/brain/{cli/stream-json.js → stream-json.js} +0 -4
  100. package/dist/brain/stream.d.ts +5 -5
  101. package/dist/brain/stream.js +7 -7
  102. package/dist/brain/transcript.d.ts +3 -3
  103. package/dist/brain/transcript.js +6 -6
  104. package/dist/brain/{cli/types.d.ts → types.d.ts} +3 -48
  105. package/dist/brain/types.js +14 -0
  106. package/dist/chalk/glyphs/skeleton.d.ts +3 -3
  107. package/dist/chalk/glyphs/skeleton.js +1 -1
  108. package/dist/lib/index.d.ts +1 -1
  109. package/dist/lib/index.js +1 -1
  110. package/dist/memory/diary.js +3 -3
  111. package/dist/realtime/bailian.d.ts +1 -1
  112. package/dist/realtime/bailian.js +3 -2
  113. package/dist/realtime/preview.js +1 -1
  114. package/dist/realtime/voices.d.ts +2 -0
  115. package/dist/realtime/voices.js +57 -57
  116. package/dist/runtime/activities.d.ts +13 -1
  117. package/dist/runtime/activities.js +28 -2
  118. package/dist/runtime/activity-state.js +2 -2
  119. package/dist/runtime/agent-file.d.ts +3 -3
  120. package/dist/runtime/agent-file.js +3 -3
  121. package/dist/runtime/context/tutor-rules.d.ts +2 -2
  122. package/dist/runtime/context/tutor-rules.js +3 -3
  123. package/dist/runtime/home/home.d.ts +12 -1
  124. package/dist/runtime/home/home.js +8 -1
  125. package/dist/runtime/home/store.d.ts +1 -1
  126. package/dist/runtime/home/store.js +36 -21
  127. package/dist/runtime/journal/mix.d.ts +15 -6
  128. package/dist/runtime/journal/mix.js +60 -19
  129. package/dist/runtime/journal/recorder.d.ts +58 -13
  130. package/dist/runtime/journal/recorder.js +106 -16
  131. package/dist/runtime/journal/store.d.ts +4 -4
  132. package/dist/runtime/journal/store.js +6 -6
  133. package/dist/runtime/kid-input.js +2 -3
  134. package/dist/runtime/live/call.d.ts +12 -0
  135. package/dist/runtime/live/call.js +51 -18
  136. package/dist/runtime/live/turns.js +1 -1
  137. package/dist/runtime/runner.d.ts +9 -55
  138. package/dist/runtime/runner.js +52 -173
  139. package/dist/scenes/paint/painter.js +4 -3
  140. package/dist/schema/config.d.ts +43 -22
  141. package/dist/schema/config.js +45 -47
  142. package/dist/schema/conversation.d.ts +6 -0
  143. package/dist/schema/home.d.ts +11 -1
  144. package/dist/schema/home.js +7 -2
  145. package/dist/schema/workspace.d.ts +27 -0
  146. package/dist/schema/workspace.js +28 -0
  147. package/dist/shared/default-port.d.ts +2 -0
  148. package/dist/shared/default-port.js +2 -0
  149. package/dist/shared/qianwen.d.ts +14 -10
  150. package/dist/shared/qianwen.js +26 -22
  151. package/dist/shared/site-url.d.ts +2 -0
  152. package/dist/shared/site-url.js +5 -0
  153. package/dist/ui/dev/dev-page.js +25 -252
  154. package/dist/ui/dev/dev-theme.d.ts +1 -1
  155. package/dist/ui/dev/dev-theme.js +1 -1
  156. package/dist/ui/dev/voice-gallery-page.js +1 -1
  157. package/dist/ui/kid/board-mark.d.ts +2 -2
  158. package/dist/ui/kid/board-mark.js +2 -2
  159. package/dist/ui/kid/home-tiles.d.ts +14 -0
  160. package/dist/ui/kid/home-tiles.js +44 -0
  161. package/dist/ui/kid/kid-page.js +68 -103
  162. package/dist/ui/kid/voice-ui.d.ts +1 -1
  163. package/dist/ui/kid/voice-ui.js +1 -1
  164. package/dist/ui/kid/word-emoji.js +5 -3
  165. package/dist/ui/parent/parent-home-page.js +315 -7
  166. package/dist/ui/parent/parent-page.js +6 -2
  167. package/dist/ui/parent/parent-settings-page.d.ts +1 -0
  168. package/dist/ui/parent/parent-settings-page.js +133 -0
  169. package/dist/ui/{dev → parent}/qr-page.d.ts +2 -2
  170. package/dist/ui/{dev → parent}/qr-page.js +19 -14
  171. package/dist/ui/shared/brand.d.ts +3 -1
  172. package/dist/ui/shared/brand.js +3 -1
  173. package/grades/grade-2/README.md +1 -1
  174. package/grades/grade-2/VALIDATION.md +1 -1
  175. package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240.md +1 -1
  176. package/grades/grade-2/agents/chinese-tutor/activities//346/{211/271/346/224/271.md → 243/200/346/237/245/344/275/234/344/270/232.md} +4 -4
  177. package/grades/grade-2/agents/chinese-tutor/activities//346/{211/271/346/224/271. → 243/200/346/237/245/344/275/234/344/270/232.}/344/276/213/345/255/220.md +2 -2
  178. package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240.md +1 -1
  179. package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240.md +1 -1
  180. package/grades/grade-2/agents/english-tutor/activities//346/{211/271/346/224/271.md → 243/200/346/237/245/344/275/234/344/270/232.md} +4 -4
  181. package/grades/grade-2/agents/english-tutor/activities//346/{211/271/346/224/271. → 243/200/346/237/245/344/275/234/344/270/232.}/344/276/213/345/255/220.md +2 -2
  182. package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240.md +1 -1
  183. package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240.md +1 -1
  184. package/grades/grade-2/agents/math-tutor/activities//346/{211/271/346/224/271.md → 243/200/346/237/245/344/275/234/344/270/232.md} +4 -4
  185. package/grades/grade-2/agents/math-tutor/activities//346/{211/271/346/224/271. → 243/200/346/237/245/344/275/234/344/270/232.}/344/276/213/345/255/220.md +2 -2
  186. package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240.md +1 -1
  187. package/package.json +2 -1
  188. package/skills/cotutor-analyze/references//345/221/275/344/273/244/344/270/216/346/226/207/344/273/266.md +2 -2
  189. package/skills/cotutor-board/references/photo.md +2 -2
  190. package/skills/cotutor-home/SKILL.md +1 -0
  191. package/skills/cotutor-home/references/tutor.md +2 -1
  192. package/skills/cotutor-home/references//351/246/226/351/241/265/350/257/255/346/263/225.md +2 -1
  193. package/skills/cotutor-tune/references//345/255/227/346/256/265.md +6 -3
  194. package/skills/cotutor-tutor/SKILL.md +5 -13
  195. package/dist/brain/cli/adapters.d.ts +0 -60
  196. package/dist/brain/cli/adapters.js +0 -91
  197. package/dist/brain/cli/qwen.d.ts +0 -11
  198. package/dist/brain/cli/qwen.js +0 -141
  199. package/dist/brain/cli/run-plan.d.ts +0 -59
  200. package/dist/brain/cli/run-plan.js +0 -71
  201. package/dist/brain/cli/run.d.ts +0 -35
  202. package/dist/brain/cli/run.js +0 -256
  203. package/dist/brain/cli/types.js +0 -3
  204. package/dist/brain/cli/warm.d.ts +0 -53
  205. package/dist/brain/cli/warm.js +0 -103
@@ -1,7 +1,7 @@
1
1
  export interface ListenInfo {
2
- /** https://<本机名>.local:<端口>/ */
2
+ /** https://<电脑名>.local/(端口不是 443 时带端口) */
3
3
  name: string;
4
- /** https://<局域网 IPv4>:<端口>/;没连网 → null */
4
+ /** https://<局域网 IPv4>/(同上);没连网 → null */
5
5
  ip: string | null;
6
6
  https: boolean;
7
7
  }
@@ -1,20 +1,20 @@
1
1
  /**
2
- * 扫码页 /dev/qr(工作台):在这台电脑上打开,iPad / iPhone 用相机扫,不用输地址。每次请求现画,不落文件。
2
+ * 二维码的打印页 /parent/qr:家长端「扫码打开」那块的「打印 ›」开它。每次请求现画,不落文件。
3
3
  * 编什么由服务端定(ListenInfo,listen 之后才知道真端口),不看请求的 Host——家长多半是从 localhost 打开这一页的,照抄就编成了 localhost。
4
4
  * 默认编 <本机名>.local(不随 Wi-Fi 变);个别设备会把它解析到走不通的 IPv6(一直转圈),页脚有一条换成局域网 IP 的退路(?via=ip)。
5
5
  * 两张码:孩子端 `/`(缺省)与家长端 `/parent`(?to=parent),页脚互相换。
6
- * 没有脚本;打印(⌘P)只留卡面,可以贴书桌。不在孩子端的入口里。样子用工作台的色板(dev-theme.ts)。
6
+ * 没有脚本;打印(⌘P)只留卡面与页脚的品牌标记,可以贴书桌。不在孩子端的入口里。色板同家长端各页。
7
7
  */
8
8
  import { qrNotes, qrSvg } from "../shared/qr-svg.js";
9
9
  import { faviconLinks } from "../shared/favicon.js";
10
- import { DEV_CSS } from "./dev-theme.js";
10
+ import { BRAND_FOOTER } from "../shared/brand.js";
11
11
  const esc = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
12
12
  export function qrPage(title, info, via = 'name', to = 'kid') {
13
13
  const useIp = via === 'ip' && info.ip !== null;
14
14
  const base = useIp ? info.ip : info.name;
15
15
  const url = to === 'parent' ? `${base}parent` : base;
16
16
  const svg = qrSvg(url) ?? '<p>地址太长,编不进二维码</p>';
17
- const q = (v, t) => `/dev/qr${v === 'ip' ? '?via=ip' : ''}${t === 'parent' ? (v === 'ip' ? '&' : '?') + 'to=parent' : ''}`;
17
+ const q = (v, t) => `/parent/qr${v === 'ip' ? '?via=ip' : ''}${t === 'parent' ? (v === 'ip' ? '&' : '?') + 'to=parent' : ''}`;
18
18
  const other = useIp
19
19
  ? `<a href="${q('name', to)}">换回主机名那张</a>(不随 Wi-Fi 变,适合打印)`
20
20
  : info.ip
@@ -23,8 +23,8 @@ export function qrPage(title, info, via = 'name', to = 'kid') {
23
23
  // 换到另一张时带上现在真用的那种地址(要了 ip 但没连网 → 仍是主机名,不给 via=ip 的退路)
24
24
  const cur = useIp ? 'ip' : 'name';
25
25
  const swap = to === 'parent'
26
- ? `这张是家长看的(板书、答案、给家长的话,只读)。<a href="${q(cur, 'kid')}">孩子那张</a>`
27
- : `家长在 iPad 上看孩子的板书:<a href="${q(cur, 'parent')}">家长那张</a>`;
26
+ ? `这张是家长用的:看记录、排首页。<a href="${q(cur, 'kid')}">孩子那张</a>`
27
+ : `家长自己的手机扫另一张:<a href="${q(cur, 'parent')}">家长那张</a>`;
28
28
  return `<!doctype html>
29
29
  <html lang="zh-CN">
30
30
  <head>
@@ -33,17 +33,21 @@ export function qrPage(title, info, via = 'name', to = 'kid') {
33
33
  ${faviconLinks()}
34
34
  <title>扫码打开 · ${esc(title)}${to === 'parent' ? ' · 家长' : ''}</title>
35
35
  <style>
36
- ${DEV_CSS}
37
- body { min-height:100vh; display:flex; align-items:center; justify-content:center; font-size:15px; line-height:1.6; }
38
- main { background:var(--surface); border-radius:var(--r-card); padding:32px 36px 28px; margin:16px; max-width:440px; box-shadow:var(--lift),0 0 0 1px var(--line); }
36
+ :root { --paper:#f6f5f1; --ink:#2b2b2b; --dim:#6b675f; --mute:#8a8781; --line:#efece5; --card:#fff; --accent:#b4531f; }
37
+ * { box-sizing:border-box; }
38
+ html, body { margin:0; background:var(--paper); color:var(--ink); font:15px/1.6 -apple-system,"PingFang SC","Hiragino Sans GB",sans-serif; }
39
+ body { min-height:100vh; display:flex; flex-direction:column; align-items:center; justify-content:center; padding:16px; }
40
+ main { background:var(--card); border-radius:16px; padding:32px 36px 28px; max-width:440px; width:100%; box-shadow:0 1px 3px #0000000d, 0 0 0 1px var(--line); }
39
41
  h1 { margin:0 0 16px; font-size:24px; font-weight:650; text-align:center; letter-spacing:-.01em; }
40
- h1 small { display:block; font-size:13.5px; font-weight:400; color:var(--muted); letter-spacing:0; }
42
+ h1 small { display:block; font-size:13.5px; font-weight:400; color:var(--mute); letter-spacing:0; }
41
43
  .qr svg { display:block; width:100%; max-width:340px; height:auto; margin:0 auto; }
42
- .url { margin:10px 0 18px; text-align:center; font:500 14px/1.4 var(--mono); word-break:break-all; background:var(--ground); border-radius:var(--r-ctl); padding:8px 12px; }
43
- ul { margin:0; padding-left:20px; font-size:13.5px; color:var(--ink-2); }
44
+ .url { margin:10px 0 18px; text-align:center; font:500 14px/1.4 ui-monospace,"SF Mono",Menlo,monospace; word-break:break-all; background:var(--paper); border-radius:8px; padding:8px 12px; }
45
+ ul { margin:0; padding-left:20px; font-size:13.5px; color:var(--dim); }
44
46
  li + li { margin-top:4px; }
45
- .other { margin:16px 0 0; font-size:13px; color:var(--muted); text-align:center; }
46
- @media print { body { background:#fff; min-height:0; } main { box-shadow:none; margin:0 auto; } .url { background:none; } .other { display:none; } }
47
+ .other { margin:16px 0 0; font-size:13px; color:var(--mute); text-align:center; }
48
+ .other a { color:var(--accent); }
49
+ .ct-brand { margin-top:20px; }
50
+ @media print { html, body { background:#fff; } body { min-height:0; } main { box-shadow:none; margin:0 auto; } .url { background:none; } .other { display:none; } }
47
51
  </style>
48
52
  </head>
49
53
  <body>
@@ -55,6 +59,7 @@ ${DEV_CSS}
55
59
  <p class="other">${swap}</p>
56
60
  ${other ? `<p class="other">${other}</p>` : ''}
57
61
  </main>
62
+ ${BRAND_FOOTER}
58
63
  </body>
59
64
  </html>
60
65
  `;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * cotutor 的品牌标记(LICENSE 附加条件第 2 条):logo、名字、仓库链接,只在这里定义一份。
3
- * 家长端各页的页脚用 BRAND_FOOTER,工作台的页头用 brandLogo + BRAND_CSS;「XX的老师们」前面的那颗用 titleLogo(孩子端首页、家长端记录与首页,
3
+ * 家长端各页的页脚用 BRAND_FOOTER,工作台的页头用 brandLogo + BRAND_CSS(logo 点了回概览,仓库链接是它下面那条「GitHub · 好用就点个 ★」);「XX的老师们」前面的那颗用 titleLogo(孩子端首页、家长端记录与首页,
4
4
  * ):家长端彩色;孩子端跟标题同色、不带链接(首页的颜色是分给老师的,孩子端也没有外链)。
5
5
  * 孩子端回首页的胶囊「‹ logo」用 BACK_LOGO:单色、不带链接、不动。
6
6
  * 听写的录像页(/parent/dictation/reel)是全屏播放器,不放。
@@ -11,6 +11,8 @@
11
11
  */
12
12
  export declare const BRAND_NAME = "cotutor";
13
13
  export declare const BRAND_URL = "https://github.com/TarsLab/cotutor";
14
+ /** GitHub 的标(Octicons mark-github,16×16):工作台页头「GitHub · 好用就点个 ★」那条仓库链接用 */
15
+ export declare const GITHUB_MARK = "<svg width=\"15\" height=\"15\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path fill=\"currentColor\" d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z\"/></svg>";
14
16
  export interface BrandLogoOpts {
15
17
  /** 边长(px) */
16
18
  size?: number;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * cotutor 的品牌标记(LICENSE 附加条件第 2 条):logo、名字、仓库链接,只在这里定义一份。
3
- * 家长端各页的页脚用 BRAND_FOOTER,工作台的页头用 brandLogo + BRAND_CSS;「XX的老师们」前面的那颗用 titleLogo(孩子端首页、家长端记录与首页,
3
+ * 家长端各页的页脚用 BRAND_FOOTER,工作台的页头用 brandLogo + BRAND_CSS(logo 点了回概览,仓库链接是它下面那条「GitHub · 好用就点个 ★」);「XX的老师们」前面的那颗用 titleLogo(孩子端首页、家长端记录与首页,
4
4
  * ):家长端彩色;孩子端跟标题同色、不带链接(首页的颜色是分给老师的,孩子端也没有外链)。
5
5
  * 孩子端回首页的胶囊「‹ logo」用 BACK_LOGO:单色、不带链接、不动。
6
6
  * 听写的录像页(/parent/dictation/reel)是全屏播放器,不放。
@@ -12,6 +12,8 @@
12
12
  import { LOGO_CLAY, LOGO_INK, logoBody, logoGeom } from "./logo.js";
13
13
  export const BRAND_NAME = 'cotutor';
14
14
  export const BRAND_URL = 'https://github.com/TarsLab/cotutor';
15
+ /** GitHub 的标(Octicons mark-github,16×16):工作台页头「GitHub · 好用就点个 ★」那条仓库链接用 */
16
+ export const GITHUB_MARK = '<svg width="15" height="15" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>';
15
17
  /** logo 的 SVG(几何在 src/ui/shared/logo.ts,网页图标与主屏幕图标同一份) */
16
18
  export function brandLogo({ size = 18, outer = 'currentColor', inner = outer, innerOpacity = 1 } = {}) {
17
19
  return `<svg class="ct-logo" width="${size}" height="${size}" viewBox="0 0 100 100" aria-hidden="true">${logoBody(logoGeom(size), { outer, inner, innerOpacity })}</svg>`;
@@ -6,6 +6,6 @@
6
6
 
7
7
  安装: `cotutor init ming --grade 2`。文件拷进 workspace 后属于家长;upgrade 只换未修改的。老师 ID 与现有 activity 名称保持稳定,看图说话的按钮可显示为「看图写话」。
8
8
 
9
- 三科提供预习、批改、复习;语文另有看图说话、写话、听写与表达活动;英语另有口语课、听写;小课堂继续使用提前做好的课包,看完再问。典型题型的完整示例见 `examples/数学平均分.md`。
9
+ 三科提供预习、检查作业、复习(孩子端老师牌上常驻三块方砖);语文另有看图说话、写话、听写与表达活动;英语另有口语课、听写;小课堂继续使用提前做好的课包,看完再问。典型题型的完整示例见 `examples/数学平均分.md`。
10
10
 
11
11
  验证:所有 activity 及例子必须通过静态检查;真实模型的评估结果见 [验证记录](VALIDATION.md),命令见仓库 `docs/年级教学包.md`。静态通过不表示教学效果已经验证。
@@ -19,7 +19,7 @@
19
19
  - 英语:qwen3.8-flash;早期试跑用 low,最终默认采用 medium。
20
20
  - 模拟孩子:qwen3.8-flash。
21
21
 
22
- 三科预习、批改、复习与语文写话均跑过缺材料/给定范围多轮/结束边界。口语课验证了按给定词表热身;平均分小课堂验证了看懂就收、主动要求练一道后的互动。口语通话后半段沿用原有流程,本次没有进行真人音频通话验收。
22
+ 三科预习、批改(2026-11-03 改名检查作业,教法没改,只改了名字和开场那句「带孩子检查作业」)、复习与语文写话均跑过缺材料/给定范围多轮/结束边界。口语课验证了按给定词表热身;平均分小课堂验证了看懂就收、主动要求练一道后的互动。口语通话后半段沿用原有流程,本次没有进行真人音频通话验收。
23
23
 
24
24
  完整批次每个例子跑 1 次,两项复习修正后的完整例子各跑 3 次,九个共性 activity 的缺材料开场各跑 3 次。轮次上限只是评估停止条件,不代表孩子已经完成学习。
25
25
 
@@ -76,4 +76,4 @@ cards: photo image read tianzige
76
76
 
77
77
  ## 怎么挂
78
78
 
79
- 首页老师卡里写 `activity 复习`。下面可填「范围:」和「重点:」。
79
+ 孩子端这位老师的牌上常驻一块「复习」方砖(家长端首页「每天都在」里能关),点了不带参数,老师先请孩子拍材料。要带范围,首页老师卡里写 `activity 复习`,下面填「范围:」和「重点:」。
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: 批改
2
+ name: 检查作业
3
3
  description: 看已完成的语文作业,一次订正一处
4
4
  cards: photo image read tianzige
5
5
  ---
@@ -7,7 +7,7 @@ cards: photo image read tianzige
7
7
  <!-- 给人看
8
8
  谁读:「老师怎么带」是独立系统提示,不读老师文件。
9
9
  能改:家长自己的文件;upgrade 保留改过的。
10
- 改了:cotutor activity check chinese-tutor 批改;再跑同名例子。
10
+ 改了:cotutor activity check chinese-tutor 检查作业;再跑同名例子。
11
11
  -->
12
12
 
13
13
  ## 家长填的
@@ -19,7 +19,7 @@ cards: photo image read tianzige
19
19
 
20
20
  ## 老师怎么带
21
21
 
22
- 你是这个家的语文老师,带孩子批改。默认面向中国大陆小学二年级。
22
+ 你是这个家的语文老师,带孩子检查作业。默认面向中国大陆小学二年级。
23
23
  教材版本、已学内容和孩子基础看档案与学科入口文件。
24
24
 
25
25
  第一句: 本次材料不足时,照下面的开场示例回复。材料齐了就从第一步开始。
@@ -78,4 +78,4 @@ cards: photo image read tianzige
78
78
 
79
79
  ## 怎么挂
80
80
 
81
- 首页老师卡里写 `activity 批改`。下面可填「范围:」和「重点:」。
81
+ 孩子端这位老师的牌上常驻一块「检查作业」方砖(家长端首页「每天都在」里能关),点了不带参数,老师先请孩子拍材料。要带范围,首页老师卡里写 `activity 检查作业`,下面填「范围:」和「重点:」。
@@ -1,10 +1,10 @@
1
1
  ---
2
- activity: 批改
2
+ activity: 检查作业
3
3
  ---
4
4
 
5
5
  ## 缺少材料
6
6
 
7
- 孩子: 我想批改
7
+ 孩子: 我想检查作业
8
8
  最多轮: 1
9
9
  判:
10
10
  - 出拍给我看卡
@@ -79,4 +79,4 @@ kē dǒu
79
79
 
80
80
  ## 怎么挂
81
81
 
82
- 首页老师卡里写 `activity 预习`。下面可填「范围:」和「重点:」。
82
+ 孩子端这位老师的牌上常驻一块「预习」方砖(家长端首页「每天都在」里能关),点了不带参数,老师先请孩子拍材料。要带范围,首页老师卡里写 `activity 预习`,下面填「范围:」和「重点:」。
@@ -75,4 +75,4 @@ cards: photo image word read choice
75
75
 
76
76
  ## 怎么挂
77
77
 
78
- 首页老师卡里写 `activity 复习`。下面可填「范围:」和「重点:」。
78
+ 孩子端这位老师的牌上常驻一块「复习」方砖(家长端首页「每天都在」里能关),点了不带参数,老师先请孩子拍材料。要带范围,首页老师卡里写 `activity 复习`,下面填「范围:」和「重点:」。
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: 批改
2
+ name: 检查作业
3
3
  description: 核对已完成的英语作业,用词句示例帮助订正
4
4
  cards: photo image word read choice
5
5
  ---
@@ -7,7 +7,7 @@ cards: photo image word read choice
7
7
  <!-- 给人看
8
8
  谁读:「老师怎么带」是独立系统提示,不读老师文件。
9
9
  能改:家长自己的文件;upgrade 保留改过的。
10
- 改了:cotutor activity check english-tutor 批改;再跑同名例子。
10
+ 改了:cotutor activity check english-tutor 检查作业;再跑同名例子。
11
11
  -->
12
12
 
13
13
  ## 家长填的
@@ -19,7 +19,7 @@ cards: photo image word read choice
19
19
 
20
20
  ## 老师怎么带
21
21
 
22
- 你是这个家的英语老师,带孩子批改。默认面向中国大陆小学二年级。
22
+ 你是这个家的英语老师,带孩子检查作业。默认面向中国大陆小学二年级。
23
23
  教材版本、已学内容和孩子基础看档案与学科入口文件。
24
24
 
25
25
  第一句: 本次材料不足时,照下面的开场示例回复。材料齐了就从第一步开始。
@@ -77,4 +77,4 @@ cards: photo image word read choice
77
77
 
78
78
  ## 怎么挂
79
79
 
80
- 首页老师卡里写 `activity 批改`。下面可填「范围:」和「重点:」。
80
+ 孩子端这位老师的牌上常驻一块「检查作业」方砖(家长端首页「每天都在」里能关),点了不带参数,老师先请孩子拍材料。要带范围,首页老师卡里写 `activity 检查作业`,下面填「范围:」和「重点:」。
@@ -1,10 +1,10 @@
1
1
  ---
2
- activity: 批改
2
+ activity: 检查作业
3
3
  ---
4
4
 
5
5
  ## 缺少材料
6
6
 
7
- 孩子: 我想批改
7
+ 孩子: 我想检查作业
8
8
  最多轮: 1
9
9
  判:
10
10
  - 出拍给我看卡
@@ -83,4 +83,4 @@ Apple,苹果。
83
83
 
84
84
  ## 怎么挂
85
85
 
86
- 首页老师卡里写 `activity 预习`。下面可填「范围:」和「重点:」。
86
+ 孩子端这位老师的牌上常驻一块「预习」方砖(家长端首页「每天都在」里能关),点了不带参数,老师先请孩子拍材料。要带范围,首页老师卡里写 `activity 预习`,下面填「范围:」和「重点:」。
@@ -75,4 +75,4 @@ cards: photo image canvas choice
75
75
 
76
76
  ## 怎么挂
77
77
 
78
- 首页老师卡里写 `activity 复习`。下面可填「范围:」和「重点:」。
78
+ 孩子端这位老师的牌上常驻一块「复习」方砖(家长端首页「每天都在」里能关),点了不带参数,老师先请孩子拍材料。要带范围,首页老师卡里写 `activity 复习`,下面填「范围:」和「重点:」。
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: 批改
2
+ name: 检查作业
3
3
  description: 核对已完成的数学作业,找到并订正一个具体问题
4
4
  cards: photo image canvas choice
5
5
  ---
@@ -7,7 +7,7 @@ cards: photo image canvas choice
7
7
  <!-- 给人看
8
8
  谁读:「老师怎么带」是独立系统提示,不读老师文件。
9
9
  能改:家长自己的文件;upgrade 保留改过的。
10
- 改了:cotutor activity check math-tutor 批改;再跑同名例子。
10
+ 改了:cotutor activity check math-tutor 检查作业;再跑同名例子。
11
11
  -->
12
12
 
13
13
  ## 家长填的
@@ -19,7 +19,7 @@ cards: photo image canvas choice
19
19
 
20
20
  ## 老师怎么带
21
21
 
22
- 你是这个家的数学老师,带孩子批改。默认面向中国大陆小学二年级。
22
+ 你是这个家的数学老师,带孩子检查作业。默认面向中国大陆小学二年级。
23
23
  教材版本、已学内容和孩子基础看档案与学科入口文件。
24
24
 
25
25
  第一句: 本次材料不足时,照下面的开场示例回复。材料齐了就从第一步开始。
@@ -77,4 +77,4 @@ cards: photo image canvas choice
77
77
 
78
78
  ## 怎么挂
79
79
 
80
- 首页老师卡里写 `activity 批改`。下面可填「范围:」和「重点:」。
80
+ 孩子端这位老师的牌上常驻一块「检查作业」方砖(家长端首页「每天都在」里能关),点了不带参数,老师先请孩子拍材料。要带范围,首页老师卡里写 `activity 检查作业`,下面填「范围:」和「重点:」。
@@ -1,10 +1,10 @@
1
1
  ---
2
- activity: 批改
2
+ activity: 检查作业
3
3
  ---
4
4
 
5
5
  ## 缺少材料
6
6
 
7
- 孩子: 我想批改
7
+ 孩子: 我想检查作业
8
8
  最多轮: 1
9
9
  判:
10
10
  - 出拍给我看卡
@@ -78,4 +78,4 @@ cards: photo image canvas choice
78
78
 
79
79
  ## 怎么挂
80
80
 
81
- 首页老师卡里写 `activity 预习`。下面可填「范围:」和「重点:」。
81
+ 孩子端这位老师的牌上常驻一块「预习」方砖(家长端首页「每天都在」里能关),点了不带参数,老师先请孩子拍材料。要带范围,首页老师卡里写 `activity 预习`,下面填「范围:」和「重点:」。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cotutor",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "AI tutors that co-teach with parents at home: real-time voice, an interactive whiteboard, and learning memory in your own Obsidian vault. Self-hosted; built for China's elementary curriculum.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -45,6 +45,7 @@
45
45
  "dist",
46
46
  "grades",
47
47
  "themes",
48
+ "avatars",
48
49
  "cards",
49
50
  "skills",
50
51
  "README.md",
@@ -3,8 +3,8 @@
3
3
  ## 分析用的命令(在 workspace 根跑;完整用法 cotutor --help)
4
4
 
5
5
  ```
6
- cotutor doctor [--workspace <dir>] [--json] [--live] 逐项体检;--live 真起一次老师与配音(花一分钱)把 API 层的坑摆出来
7
- cotutor send <老师> [<消息>] [--from kid|system] [--runtime <名>] [--new] [--photo a.jpg,b.png] [--activity <名> [--param '名=值;名=值']] [--lane main,tts] [--quiet] 终端里发一条,现场按道打印每道工序的事件,说完打印结果(与页面同一条路;--new 开新话题;--photo 带作业照片,拷进 captures/,有照片时消息可以空;--activity 像孩子点了首页的 activity 按钮,新开话题,参数同按钮下面那几行;--quiet 只要结果)
6
+ cotutor doctor [--workspace <dir>] [--json] [--live] 逐项体检;--live 真连一次百炼实时模型、真合成一句配音(花一分钱)把 API 层的坑摆出来(真跑老师用 cotutor send)
7
+ cotutor send <老师> [<消息>] [--from kid|system] [--runtime <名>] [--new] [--photo a.jpg,b.png] [--activity <名> [--param '名=值;名=值']] [--lane main,tts] [--quiet] 终端里发一条,现场按道打印每道工序的事件,说完打印结果(孩子的话照实时写,同孩子端:上下文包带 live:、不补提问卡,只是没有嘴;--from system 不算;--new 开新话题;--photo 带作业照片,拷进 captures/,有照片时消息可以空;--activity 像孩子点了首页的 activity 按钮,新开话题,参数同按钮下面那几行;--quiet 只要结果)
8
8
  cotutor pack <老师> [<消息>] [--from kid|system] [--at <ISO时间>] [--activity <名> [--param '名=值;名=值']] [--json] 干跑上下文包(--activity 看 activity 话题第一条老师拿到什么):不起模型,打印现在会发给老师的那份 + 每段来自哪个文件、那里一共几条、按政策带了几条(改了档案 / 日记 / 计划立刻看效果)
9
9
  cotutor replay <老师> <job> [<日期>] [--runtime <名>] [--json] 回放一轮:同一问按现在的 vault 与提示词再跑一遍(落 evals/,不进孩子的对话、不配音),跑完并排打印上下文包 / 讲稿 / 卡 / 读了什么的 diff
10
10
  cotutor compare <老师> [<evalJob>] [<日期>] [--json] 再看一次回放的对照(不给 evalJob 就列这天回放过哪些)
@@ -48,13 +48,13 @@
48
48
 
49
49
  ## 孩子看到什么
50
50
 
51
- 紫色卡:左边一个相机,右边大字写要拍什么。它在最后一节时,卡下沿写「等你拍…」,旁边「拍照」「相册」两颗按钮;点了进发照片屏(能裁、能圈、能配一句话),发给你。拍来的照片缩在卡下沿,配的话跟在旁边,点照片看大图。
51
+ 紫色卡:左边一个相机,右边大字写要拍什么。它在最后一节时,卡下沿写「等你拍…」,旁边「拍照」「相册」两颗按钮;点了进发照片屏(能裁、能圈、能配一句话),发给你。拍来的照片放大在这张卡里,配的话写在照片下面,点照片看大图。
52
52
 
53
53
  ## 你会收回什么
54
54
 
55
55
  卡上没有状态。孩子拍来的照片随下一条消息到你手里,和他自己拍的作业照片一样:上下文包里有 `photos:` 与 `photoFiles:`,照板书写法的「作业照片」读。他配的话就是这条消息的字;没配就是「(拍了 N 张)」。
56
56
 
57
- 孩子没拍、直接说话(「我找不到那幅图」),那句话也写在这张卡下沿。
57
+ 孩子没拍、直接说话(「我找不到那幅图」),那句话也写在这张卡里。
58
58
 
59
59
  ## 状态的形状
60
60
 
@@ -79,6 +79,7 @@ description: 排 cotutor 孩子端的首页。孩子学完之后,家长在 works
79
79
  - 看完怎么聊,在这位老师的 `agents/<老师>/activities/小课堂.md`;没有这份,小课堂按钮不出(首页检查报)。
80
80
  - 小课堂的讲法写孩子在这一课常卡在哪,别写课里已经讲过的。
81
81
  - 「activity」是老师的一种做法,写在 `agents/<老师>/activities/<名>.md`(如语文老师的「你说我画」)。`cotutor activity check <老师>` 列出这位老师有哪些、每个要家长填什么。
82
+ - 预习、检查作业、复习三块方砖一直在牌上(老师家里有那个文件就有,家长端「每天都在」能关),点了参数全用缺省、老师先请孩子拍材料。不用为它们摆按钮;要指定范围(「第 22 课 1–3 自然段」)才摆 `activity 预习 我要预习小蝌蚪找妈妈`,下面填 `范围:`。
82
83
  - 看图说话、你说我画都是语文老师的 activity:`activity 看图说话`、`activity 你说我画`。孩子在新话题里拍看图说话,老师不走那套;要他照那套说,就摆按钮。
83
84
  - 小课堂看完要接着照某个 activity 聊(如看图说话的课),在小课堂按钮下面挂一行 `activity: 看图说话`。
84
85
  - activity 按钮写 `activity <名> [孩子看到的字]`,下面一行一个参数(`词: 播种、羞愧`),照那个文件「## 家长填的」填;同一个 activity 填不同的参数可以是两个按钮(你说我画、我来出题)。
@@ -25,6 +25,7 @@
25
25
  - `讲法: …` 挂在上一个按钮下面,写给老师的:从哪开始、重点是什么、讲到哪收。可以写多行。孩子看不到。
26
26
  - `留到: <日期>` 挂在上一个按钮下面:这个按钮留到那天(含那天),过了孩子就看不到。不写就一直留着。
27
27
  - `新话题` 不用写,应用永远放第一个。
28
+ - 预习、检查作业、复习三块方砖不用写:老师家里有那个 activity 文件就常驻在牌上(家长端「每天都在」能关),点了参数全用缺省、老师先请孩子拍材料。要带范围、重点,才另写一个 `activity 预习` 按钮;两个都留着,方砖不占按钮的名额。
28
29
 
29
30
  ## 例子
30
31
 
@@ -137,7 +138,7 @@ activity 的名字写错、参数写错或少了要填的,解析得过,首页检
137
138
 
138
139
  ## 孩子看到什么
139
140
 
140
- 首页最上面,一位老师一张卡:头像、名字,下面一个学科色大气泡「新话题 · 想问我什么?」,然后是你写的(开场按钮与 activity 按钮是带 ▶ 的大胶囊,接着按钮带 ↻;小课堂是一张带预览图的海报,图上一个播放键,下面写课名与课长)。点开场或接着按钮,老师页打开、按钮上的字立刻发出去,老师接着就讲;点新话题是空白的老师页,等孩子开口。今天从同一个按钮聊过的,在老师页的空白板上接(新话题一个胶囊;activity、小课堂先问接着还是重新开始),不占按钮。今天的条数用完、或服务不通,整张卡变灰。讲法孩子永远看不到。
141
+ 首页最上面,一位老师一张卡:头像、名字,下面一个学科色大气泡「新话题 · 想问我什么?」,再下面一行三块方砖(预习、检查作业、复习,图在上字在下,不写小字),然后是你写的(开场按钮与 activity 按钮是带 ▶ 的大胶囊,接着按钮带 ↻;小课堂是一张带预览图的海报,图上一个播放键,下面写课名与课长)。点开场或接着按钮,老师页打开、按钮上的字立刻发出去,老师接着就讲;点新话题是空白的老师页,等孩子开口。今天从同一个按钮聊过的,在老师页的空白板上接(新话题一个胶囊;activity、小课堂先问接着还是重新开始),不占按钮。今天的条数用完、或服务不通,整张卡变灰。讲法孩子永远看不到。
141
142
 
142
143
  ## 你会收回什么
143
144
 
@@ -7,7 +7,7 @@
7
7
  - **第一个 `## ` 起是给家长的**(「## 为什么这么排」),孩子看不到。
8
8
  - 围栏外的普通行首页不显示(检查会提醒),要给孩子看的写进卡里。
9
9
 
10
- 应用保证的:每位孩子端露出的老师都有一张老师卡(没写就补一张只有「新话题」的);「新话题」永远是第一个按钮;应用不加「接着刚才的」——今天从同一个入口聊过的,孩子进老师页后在空白板上接(新话题是一个胶囊,activity、小课堂先问接着那个还是重新开始);引用坏了(话题找不到)的按钮不出现。
10
+ 应用保证的:每位孩子端露出的老师都有一张老师卡(没写就补一张只有「新话题」的);「新话题」永远是第一个按钮;老师家里有预习、检查作业、复习这几个 activity 的,新话题下面常驻这几块方砖(不用写,家长端能关,不占按钮的名额);应用不加「接着刚才的」——今天从同一个入口聊过的,孩子进老师页后在空白板上接(新话题是一个胶囊,activity、小课堂先问接着那个还是重新开始);引用坏了(话题找不到)的按钮不出现。
11
11
 
12
12
  老师卡的按钮最多 4 个,每个最多 16 个字。
13
13
 
@@ -28,6 +28,7 @@
28
28
  - `讲法: …` 挂在上一个按钮下面,写给老师的:从哪开始、重点是什么、讲到哪收。可以写多行。孩子看不到。
29
29
  - `留到: <日期>` 挂在上一个按钮下面:这个按钮留到那天(含那天),过了孩子就看不到。不写就一直留着。
30
30
  - `新话题` 不用写,应用永远放第一个。
31
+ - 预习、检查作业、复习三块方砖不用写:老师家里有那个 activity 文件就常驻在牌上(家长端「每天都在」能关),点了参数全用缺省、老师先请孩子拍材料。要带范围、重点,才另写一个 `activity 预习` 按钮;两个都留着,方砖不占按钮的名额。
31
32
 
32
33
  ```tutor chinese-tutor
33
34
  我要预习小蝌蚪找妈妈
@@ -10,7 +10,7 @@ frontmatter 只写应用支持的这几个键:`name`、`description`、`maxTurns
10
10
  |---|---|---|---|
11
11
  | `display` | 必需 | | 显示名:孩子端头像下的字,计划文件的 H2 标题 |
12
12
  | `subject` | 可选 | | 学科:与课程表的学科列、观察的 subject、vault 入口文件的 subject 属性对齐即归到这位老师 |
13
- | `avatar` | 可选 | | 头像:一个 emoji,或 workspace 根以内的图片相对路径(如 avatars/math-tutor.png;figshot pick --workspace 会写这个) |
13
+ | `avatar` | 可选 | | 头像:一个 emoji,或 workspace 根以内的图片相对路径(如 avatars/math-tutor.png;figshot pick --workspace 会写这个);不写 = 语文、数学、英语用出厂头像,其余显示学科首字 |
14
14
  | `color` | 可选 | | 学科色 #rrggbb:头像的环、牌的边、新话题气泡(白字压在上面,要够深,doctor 会查)、听写页的按钮;不写 = 语文粉、数学蓝、英语绿,其余按先后自动分、不撞色 |
15
15
  | `voice` | 可选 | | 卡的配音用的音色 id(百炼 Qwen-Audio-TTS,如 qwen-audio-3.0-tts-plus-longcanzhuyue;工作台音色页「卡的配音」听着挑;点读、单词卡、听写念词的声音);不配这些卡就没有声音。老师开口的嘴在 live.voices |
16
16
  | `enabled` | 可选 | true | 开关:false 时两端都不见 |
@@ -18,6 +18,9 @@ frontmatter 只写应用支持的这几个键:`name`、`description`、`maxTurns
18
18
  | `cards` | 可选 | | 这位老师的板书写法里讲哪几种卡(text / read / choice / image / tianzige / word / lecture / canvas / paint / photo / code);不写 = 全部,text 总是带着。只裁递给老师的写法,写了清单外的卡照常显示 |
19
19
  | `runtime` | 可选 | | 这位老师用的运行时(runtimes 里的键;不配用 default)——预算、时限不同的老师配自己的 |
20
20
  | `extras` | 可选 | | 老师卡上的常驻入口:dictation 听写(含拍照听写)、talk 口语课(要 talk.enabled);不写按学科给缺省:语文有听写,其余没有;[] = 都不要 |
21
+ | `tiles.preview` | 可选 | | |
22
+ | `tiles.check` | 可选 | | |
23
+ | `tiles.review` | 可选 | | |
21
24
 
22
25
  `policy` 是下面这张表的子集,写了的覆盖 policyDefaults,没写的继承。
23
26
 
@@ -30,7 +33,7 @@ frontmatter 只写应用支持的这几个键:`name`、`description`、`maxTurns
30
33
  | `contextPack.planLines` | 必需 | 10 | 上下文包带本周计划里这位老师的前几行 |
31
34
  | `contextPack.entryChars` | 必需 | 4000 | 档案与这位老师的入口文件(vault 里 cotutor: subject 那篇)原文各最多带多少字;超出截断并在上下文包里注明 |
32
35
  | `effort` | 必需 | low | 老师动笔前想多久:low = 想得少、开口快(缺省,孩子等 10–15 秒);medium = 多想一会儿(算题的老师);high = 最慢最细。填进运行时模板的 {effort};模板里没有 {effort} 的运行时不受影响 |
33
- | `tools` | 必需 | off | 孩子说的话,老师带不带工具:off = 不带,只凭上下文包答,开口快(缺省);on = 带只读工具(查教材、读 vault,慢)。带照片的那条、记账,总是带只读工具(read_file / glob / grep_search)。模板里没有 {tools} / {toolArgs} 的运行时不受影响 |
36
+ | `tools` | 必需 | off | 孩子说的话,老师带不带工具:off = 不带,只凭上下文包答,开口快(缺省);on = 带只读工具(查教材、读 vault,慢)。带照片的那条、记账,总是带只读工具(read_file / glob / grep_search) |
34
37
  | `board` | 必需 | auto | 板书:auto = 讲题讲概念时老师出卡(缺省);off = 只说话不出卡 |
35
38
  | `stall.ms` | 必需 | 30000 | 老师进程多少毫秒没有任何输出就当断流(缺省 30000;工具在跑时不算;0 = 不看,交给 CLI 自己约 180 秒的重试) |
36
39
  | `stall.retries` | 必需 | 2 | 一轮里断流后最多接着跑几次(缺省 2);用完还断,这轮按出错收尾 |
@@ -38,7 +41,7 @@ frontmatter 只写应用支持的这几个键:`name`、`description`、`maxTurns
38
41
  ## 相关命令(完整用法 cotutor --help)
39
42
 
40
43
  ```
41
- cotutor doctor [--workspace <dir>] [--json] [--live] 逐项体检;--live 真起一次老师与配音(花一分钱)把 API 层的坑摆出来
44
+ cotutor doctor [--workspace <dir>] [--json] [--live] 逐项体检;--live 真连一次百炼实时模型、真合成一句配音(花一分钱)把 API 层的坑摆出来(真跑老师用 cotutor send)
42
45
  cotutor upgrade [--workspace <dir>] [--force <老师>]... 老师文件、activity 与 skill 换新版:没改过的直接换,改过的只报 diff(--force <老师> 或 --force <老师>/<名> 才覆盖,原文留 .bak)
43
46
  cotutor upgrade --config [--dry-run] [--workspace <dir>] cotutor.json 补缺:新出厂老师 / 运行时 / 命令模板旗标(只加缺的,你改过的值不动)
44
47
  cotutor add <老师名> --display <显示名> [--subject <学科>] [--avatar <emoji|图片相对路径>] [--hidden] 加一位自家的老师:出模板文件、进 cotutor.json、建目录(名字以 -tutor 结尾的是老师,figshot 给它做头像)
@@ -167,19 +167,15 @@ description: cotutor 有脸的老师共同的守则:上下文包怎么看、三
167
167
 
168
168
  实时的板是这个话题的笔记和练习。孩子听完、说完,回头看的是它。
169
169
 
170
- 孩子拍来古诗练习,圈了第一题。这是新的一件事,第一张卡写节头:
170
+ 孩子拍来古诗练习,圈了第一题。先讲,问他:
171
171
 
172
172
  ````
173
- ```text
174
- # 补读音:瀑、布、穷、尽
175
- ```
176
-
177
173
  这一题给四个字补读音,都在《望庐山瀑布》里。
178
174
  先看「瀑」。山上冲下来的大水,叫瀑布。
179
175
  你读读看,「瀑」怎么念?
180
176
  ````
181
177
 
182
- 四个字他都补好了。还是这一题,不写节头,把定下来的记上:
178
+ 四个字他都补好了。把定下来的记上:
183
179
 
184
180
  ````
185
181
  ```text
@@ -189,12 +185,8 @@ description: cotutor 有脸的老师共同的守则:上下文包怎么看、三
189
185
  四个都补好了,这一题做完了。
190
186
  ````
191
187
 
192
- - 新的一件事,第一张卡写节头:只有一行 `# 标题` 的 `text` 卡。
193
- - 换了一道题、孩子问了别的,才算新的一件事。
194
- - 拍来新的一道题,也先写节头,作业那张卡跟在它后面。
195
- <!-- 为什么:回放 10-20 2113-13(拍来第二题)两次都没写节头:板书写法「作业照片」说第一张卡放作业,压过了这条。 -->
196
- - 节头一行说清这件事,不抄孩子的原话。
197
- - 还是这件事,就不写节头。你的卡接在上一节后面。
188
+ <!-- 为什么没有节头的规矩:10-21 加过「新的一件事,第一张卡写节头」,到 11-04 一共 12 轮实时,0 轮照做(该写的 6 轮)。
189
+ 它还和板书写法「作业照片:第一张卡放作业」打架。11-04 去掉。老师自己写了只有一行 # 标题的 text 卡,板上照样起新一节(presentation.ts 的 liveHead)。 -->
198
190
  - 讲完的、孩子答过的,记成一张笔记卡。
199
191
  - 要孩子做的出成练习,照「一步一步教」。
200
192
  - 板上不写这一步要他自己想的答案,只放题和提示。
@@ -204,7 +196,7 @@ description: cotutor 有脸的老师共同的守则:上下文包怎么看、三
204
196
  - 卡写在哪句前后都行,不用一张卡讲三句。
205
197
  - 讲稿里的 `[词]` 照旧在卡上标出来。
206
198
  - 笔记可以比讲稿写得全。孩子打断了,板上的还在。
207
- <!-- 为什么:实时的板不配音、卡总晚于嘴,不用和讲稿一拍一拍对上。板上原来多半是应用拼的:节头是孩子的原话,末句的问话补成提问卡(10-20 晚语文 9 张文字卡 7 张是补的)。 -->
199
+ <!-- 为什么:实时的板不配音、卡总晚于嘴,不用和讲稿一拍一拍对上。板上原来多半是应用拼的:孩子的原话当节头,末句的问话补成提问卡(10-20 晚语文 9 张文字卡 7 张是补的)。 -->
208
200
 
209
201
  {{有 lecture}}
210
202
  ## 想要小课堂
@@ -1,60 +0,0 @@
1
- import { type CliRuntime, type Policy } from '../../schema/index.ts';
2
- import type { CliAdapter, CliEvent, ToolKind, ToolSet } from './types.ts';
3
- export { textsOf, toolsOf, type AssistantPart, type CliAdapter, type CliEvent, type CliFinal, type ToolKind, type ToolResult, type ToolSet, type ToolUse } from './types.ts';
4
- /** 登记在这里的才认;接一个新 CLI 加一行 */
5
- export declare const ADAPTERS: readonly CliAdapter[];
6
- /** 自定义壳脚本与测试进程的 stream-json 协议,不准备 Qwen 的家目录与认证。 */
7
- export declare const STREAM_JSON: CliAdapter;
8
- /** 运行时的 CLI 名:cli 字段,或 run[0] 的文件名 */
9
- export declare function runtimeCli(runtime: {
10
- run: readonly string[];
11
- cli?: string;
12
- }): string;
13
- export declare function adapterNamed(name: string): CliAdapter | null;
14
- /** 旧配置仍可读取,但退役的运行器不能通过通用协议兜底启动。 */
15
- export declare function runtimeIssue(runtime: {
16
- run: readonly string[];
17
- resume?: readonly string[];
18
- cli?: string;
19
- }): string | null;
20
- export declare function adapterFor(runtime: {
21
- run: readonly string[];
22
- resume?: readonly string[];
23
- cli?: string;
24
- }): CliAdapter;
25
- /** 工具名属于哪一类:问遍登记的适配器(各家工具名不重),都不认就是 other。老数据只存了名字,也能认 */
26
- export declare function toolKind(name: string): ToolKind;
27
- /** 模板管得了这轮带不带工具吗:用了 `{tools}` 或 `{toolArgs}`。管不了的,应用也就不替它省掉上下文包里只有工具才用得上的路径 */
28
- export declare function controlsTools(runtime: CliRuntime): boolean;
29
- /** 出厂运行时:各适配器的模板合在一起(default 由 skeleton 定) */
30
- export declare function factoryRuntimes(): Record<string, CliRuntime>;
31
- export interface FillVars {
32
- agent: string;
33
- prompt: string;
34
- session?: string;
35
- agentBody?: string;
36
- systemBody?: string;
37
- boardFile?: string;
38
- effort?: Policy['effort'];
39
- /** 这轮带不带工具;不给 = 带 */
40
- tools?: ToolSet;
41
- /** 这轮该读得到的目录(workspace 根、vault) */
42
- readDirs?: readonly string[];
43
- }
44
- /**
45
- * 填模板:独占一个参数的 `{toolArgs}` 换成适配器给的几个参数(可以是零个),`{tools}` 换成适配器的工具名单,其余占位照 schema 的 fillRuntime。
46
- */
47
- export declare function fillArgs(adapter: CliAdapter, argv: readonly string[], vars: FillVars): string[];
48
- /** 起老师进程的环境:基础环境 + 这轮计划里适配器定的变量,再交给适配器补齐认证环境 */
49
- export declare function processEnv(plan: {
50
- cli: string;
51
- env: Record<string, string>;
52
- }, base: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
53
- /** 按计划里记的适配器名读输出(老数据没有就按 stream-json) */
54
- export declare function parserOf(cli: string | undefined): (line: string) => CliEvent[];
55
- /** 按运行时名读输出(落盘的老轮次只记了运行时名;运行时后来删了或改了就按 stream-json)。API 运行时的日志不归这里读,见 src/brain/index.ts 的 logParser */
56
- export declare function parserForRuntime(runtimes: Readonly<Record<string, unknown>>, name: string | undefined): (line: string) => CliEvent[];
57
- /** 起进程前让这个 CLI 的适配器备好 workspace 里的机器文件(qwen 的隔离家目录);没有这一步的 CLI 什么都不做 */
58
- export declare function prepareCli(cli: string, root: string): Promise<void>;
59
- /** 进程起来报的工具表里,不该有的那几样(适配器不核对就是空) */
60
- export declare function unexpectedTools(cli: string, set: ToolSet, tools: readonly string[]): string[];