cotutor 0.3.0 → 0.4.1
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 +9 -7
- package/NOTICE +3 -3
- package/README.md +65 -18
- package/avatars/chinese.webp +0 -0
- package/avatars/english.webp +0 -0
- package/avatars/math.webp +0 -0
- package/bin/cotutor.js +13 -3
- package/cards/photo/card.css +10 -3
- package/cards/photo/card.md +2 -2
- package/cards/tutor/card.css +13 -0
- package/cards/tutor/card.md +2 -1
- package/cards//346/235/277/344/271/246/346/200/216/344/271/210/345/206/231.md +1 -1
- package/dist/app/activity-eval.js +3 -2
- package/dist/app/cli/activities.d.ts +8 -1
- package/dist/app/cli/activities.js +42 -4
- package/dist/app/cli/cert.d.ts +11 -0
- package/dist/app/cli/cert.js +36 -9
- package/dist/app/cli/chalk/browser.d.ts +2 -2
- package/dist/app/cli/chalk/browser.js +22 -7
- package/dist/app/cli/chalk/main.js +3 -2
- package/dist/app/cli/doctor.d.ts +11 -5
- package/dist/app/cli/doctor.js +156 -181
- package/dist/app/cli/firewall.d.ts +24 -0
- package/dist/app/cli/firewall.js +79 -0
- package/dist/app/cli/init.js +8 -5
- package/dist/app/cli/main.js +379 -27
- package/dist/app/cli/migrate.d.ts +2 -11
- package/dist/app/cli/migrate.js +1 -59
- package/dist/app/cli/port-holder.d.ts +16 -0
- package/dist/app/cli/port-holder.js +34 -0
- package/dist/app/cli/port.d.ts +37 -0
- package/dist/app/cli/port.js +116 -0
- package/dist/app/cli/sandbox.d.ts +25 -0
- package/dist/app/cli/sandbox.js +166 -0
- package/dist/app/cli/serve-log.d.ts +13 -0
- package/dist/app/cli/serve-log.js +90 -0
- package/dist/app/cli/serve.d.ts +18 -4
- package/dist/app/cli/serve.js +78 -8
- package/dist/app/cli/service-windows.d.ts +50 -0
- package/dist/app/cli/service-windows.js +117 -0
- package/dist/app/cli/service.d.ts +152 -0
- package/dist/app/cli/service.js +443 -0
- package/dist/app/cli/skeleton.d.ts +1 -1
- package/dist/app/cli/skeleton.js +13 -13
- package/dist/app/cli/skills.d.ts +5 -1
- package/dist/app/cli/skills.js +14 -4
- package/dist/app/cli/tray-icons.d.ts +11 -0
- package/dist/app/cli/tray-icons.js +87 -0
- package/dist/app/cli/tray-ids.d.ts +6 -0
- package/dist/app/cli/tray-ids.js +6 -0
- package/dist/app/cli/tray-mac.d.ts +8 -0
- package/dist/app/cli/tray-mac.js +140 -0
- package/dist/app/cli/tray-run.d.ts +1 -0
- package/dist/app/cli/tray-run.js +42 -0
- package/dist/app/cli/tray-windows.d.ts +5 -0
- package/dist/app/cli/tray-windows.js +166 -0
- package/dist/app/cli/tray.d.ts +236 -0
- package/dist/app/cli/tray.js +670 -0
- package/dist/app/cli/usage.d.ts +1 -1
- package/dist/app/cli/usage.js +11 -6
- package/dist/app/dictation/route.js +3 -2
- package/dist/app/http/app.d.ts +5 -10
- package/dist/app/http/app.js +47 -98
- package/dist/app/http/avatars.d.ts +17 -0
- package/dist/app/http/avatars.js +35 -0
- package/dist/app/http/dev-tutor-files.js +1 -1
- package/dist/app/http/parent-home.d.ts +14 -0
- package/dist/app/http/parent-home.js +69 -6
- package/dist/app/http/parent-look.d.ts +2 -0
- package/dist/app/http/parent-look.js +151 -0
- package/dist/app/http/raw-view.js +3 -3
- package/dist/app/http/voice-preview.d.ts +4 -0
- package/dist/app/http/voice-preview.js +34 -0
- package/dist/app/machine/bailian.d.ts +0 -2
- package/dist/app/machine/bailian.js +5 -6
- package/dist/app/machine/update.d.ts +25 -0
- package/dist/app/machine/update.js +88 -0
- package/dist/app/mock/mock-live.js +1 -1
- package/dist/app/mock/mock.d.ts +4 -2
- package/dist/app/mock/mock.js +20 -12
- package/dist/app/replay.js +4 -4
- package/dist/app/skills/home-doc.js +1 -1
- package/dist/app/workspace.d.ts +9 -4
- package/dist/app/workspace.js +33 -10
- package/dist/app/ws/route.d.ts +8 -2
- package/dist/app/ws/route.js +7 -3
- package/dist/board/parse.d.ts +0 -2
- package/dist/board/parse.js +0 -6
- package/dist/board/presentation.d.ts +9 -15
- package/dist/board/presentation.js +8 -10
- package/dist/brain/api-tools.js +2 -1
- package/dist/brain/api.d.ts +12 -17
- package/dist/brain/api.js +16 -33
- package/dist/brain/index.d.ts +23 -71
- package/dist/brain/index.js +6 -69
- package/dist/brain/log.d.ts +5 -0
- package/dist/brain/log.js +22 -0
- package/dist/brain/runtime.d.ts +30 -0
- package/dist/brain/runtime.js +23 -0
- package/dist/brain/{cli/stream-json.d.ts → stream-json.d.ts} +2 -4
- package/dist/brain/{cli/stream-json.js → stream-json.js} +0 -4
- package/dist/brain/stream.d.ts +5 -5
- package/dist/brain/stream.js +7 -7
- package/dist/brain/transcript.d.ts +3 -3
- package/dist/brain/transcript.js +6 -6
- package/dist/brain/{cli/types.d.ts → types.d.ts} +3 -48
- package/dist/brain/types.js +14 -0
- package/dist/chalk/glyphs/skeleton.d.ts +3 -3
- package/dist/chalk/glyphs/skeleton.js +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +1 -1
- package/dist/memory/diary.js +3 -3
- package/dist/realtime/bailian.d.ts +1 -1
- package/dist/realtime/bailian.js +3 -2
- package/dist/realtime/preview.js +1 -1
- package/dist/realtime/voices.d.ts +2 -0
- package/dist/realtime/voices.js +57 -57
- package/dist/runtime/activities.d.ts +13 -1
- package/dist/runtime/activities.js +28 -2
- package/dist/runtime/activity-state.js +2 -2
- package/dist/runtime/agent-file.d.ts +3 -3
- package/dist/runtime/agent-file.js +3 -3
- package/dist/runtime/context/tutor-rules.d.ts +2 -2
- package/dist/runtime/context/tutor-rules.js +3 -3
- package/dist/runtime/home/home.d.ts +12 -1
- package/dist/runtime/home/home.js +8 -1
- package/dist/runtime/home/store.d.ts +1 -1
- package/dist/runtime/home/store.js +36 -21
- package/dist/runtime/journal/mix.d.ts +15 -6
- package/dist/runtime/journal/mix.js +60 -19
- package/dist/runtime/journal/recorder.d.ts +58 -13
- package/dist/runtime/journal/recorder.js +106 -16
- package/dist/runtime/journal/store.d.ts +4 -4
- package/dist/runtime/journal/store.js +6 -6
- package/dist/runtime/kid-input.js +2 -3
- package/dist/runtime/live/call.d.ts +12 -0
- package/dist/runtime/live/call.js +51 -18
- package/dist/runtime/live/turns.js +1 -1
- package/dist/runtime/runner.d.ts +9 -55
- package/dist/runtime/runner.js +52 -173
- package/dist/scenes/paint/painter.js +4 -3
- package/dist/schema/config.d.ts +43 -22
- package/dist/schema/config.js +45 -47
- package/dist/schema/conversation.d.ts +6 -0
- package/dist/schema/home.d.ts +11 -1
- package/dist/schema/home.js +7 -2
- package/dist/schema/workspace.d.ts +27 -0
- package/dist/schema/workspace.js +28 -0
- package/dist/shared/default-port.d.ts +2 -0
- package/dist/shared/default-port.js +2 -0
- package/dist/shared/qianwen.d.ts +14 -10
- package/dist/shared/qianwen.js +26 -22
- package/dist/shared/site-url.d.ts +2 -0
- package/dist/shared/site-url.js +5 -0
- package/dist/shared/upgrade.d.ts +9 -0
- package/dist/shared/upgrade.js +23 -0
- package/dist/ui/dev/dev-page.js +25 -252
- package/dist/ui/dev/dev-theme.d.ts +1 -1
- package/dist/ui/dev/dev-theme.js +1 -1
- package/dist/ui/dev/voice-gallery-page.js +1 -1
- package/dist/ui/kid/board-mark.d.ts +2 -2
- package/dist/ui/kid/board-mark.js +2 -2
- package/dist/ui/kid/home-tiles.d.ts +14 -0
- package/dist/ui/kid/home-tiles.js +44 -0
- package/dist/ui/kid/kid-page.js +68 -103
- package/dist/ui/kid/voice-ui.d.ts +1 -1
- package/dist/ui/kid/voice-ui.js +1 -1
- package/dist/ui/kid/word-emoji.js +5 -3
- package/dist/ui/parent/parent-home-page.js +337 -7
- package/dist/ui/parent/parent-page.js +6 -2
- package/dist/ui/parent/parent-settings-page.d.ts +1 -0
- package/dist/ui/parent/parent-settings-page.js +133 -0
- package/dist/ui/{dev → parent}/qr-page.d.ts +2 -2
- package/dist/ui/{dev → parent}/qr-page.js +19 -14
- package/dist/ui/shared/brand.d.ts +3 -1
- package/dist/ui/shared/brand.js +3 -1
- package/grades/grade-2/README.md +1 -1
- package/grades/grade-2/VALIDATION.md +1 -1
- package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240.md +1 -1
- 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
- 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
- package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240.md +1 -1
- package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240.md +1 -1
- 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
- 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
- package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240.md +1 -1
- package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240.md +1 -1
- 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
- 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
- package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240.md +1 -1
- package/package.json +6 -2
- package/skills/cotutor-analyze/references//345/221/275/344/273/244/344/270/216/346/226/207/344/273/266.md +2 -2
- package/skills/cotutor-board/references/photo.md +2 -2
- package/skills/cotutor-home/SKILL.md +1 -0
- package/skills/cotutor-home/references/tutor.md +2 -1
- package/skills/cotutor-home/references//351/246/226/351/241/265/350/257/255/346/263/225.md +2 -1
- package/skills/cotutor-tune/references//345/255/227/346/256/265.md +6 -3
- package/skills/cotutor-tutor/SKILL.md +5 -13
- package/dist/brain/cli/adapters.d.ts +0 -60
- package/dist/brain/cli/adapters.js +0 -91
- package/dist/brain/cli/qwen.d.ts +0 -11
- package/dist/brain/cli/qwen.js +0 -141
- package/dist/brain/cli/run-plan.d.ts +0 -59
- package/dist/brain/cli/run-plan.js +0 -71
- package/dist/brain/cli/run.d.ts +0 -35
- package/dist/brain/cli/run.js +0 -256
- package/dist/brain/cli/types.js +0 -3
- package/dist/brain/cli/warm.d.ts +0 -53
- package/dist/brain/cli/warm.js +0 -103
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* 选哪个模型、预算、时限还在 cotutor.json 的运行时模板里(数据);应用的其余部分只认这里的统一事件,不认哪家的 JSON。
|
|
5
|
-
* 接一个新 CLI:照 qwen.ts 写一个文件,在 index.ts 的 ADAPTERS 里登记。
|
|
2
|
+
* 脑的统一事件与工具:API 脑(./api.ts)写进 .log、孩子端的流式板书、家长端看原文、转录都只认这些。
|
|
3
|
+
* 名字里的 Cli 是 2026-11-02 以前起 Qwen Code 时留下的;那时的 .log 是 stream-json,由 ./stream-json.ts 读成同样的事件。
|
|
6
4
|
*/
|
|
7
|
-
import type { CliRuntime, Policy } from '../../schema/index.ts';
|
|
8
5
|
/** 工具的类别:应用要知道「读了哪个文件」「是不是用技能回读板书写法」时按类别认,不比工具名 */
|
|
9
6
|
export type ToolKind = 'read' | 'shell' | 'search' | 'edit' | 'skill' | 'other';
|
|
7
|
+
export declare const toolKind: (name: string) => ToolKind;
|
|
10
8
|
/**
|
|
11
9
|
* 这轮带不带工具(政策 tools 与消息种类定):off 一个不带;read 只读(看照片、读 vault、查教材);
|
|
12
10
|
* on 与 read 一样只读,不提供 shell 与写入工具
|
|
@@ -99,46 +97,3 @@ export type CliEvent = {
|
|
|
99
97
|
kind: 'result';
|
|
100
98
|
final: CliFinal;
|
|
101
99
|
};
|
|
102
|
-
/** 运行时模板里与适配器有关的占位能拿到的值 */
|
|
103
|
-
export interface PlanContext {
|
|
104
|
-
/** workspace 根(适配器的私有文件放在它的 .cotutor/ 下);测试与不关心的调用可以不给 */
|
|
105
|
-
root?: string;
|
|
106
|
-
effort: Policy['effort'];
|
|
107
|
-
/** 这轮带不带工具 */
|
|
108
|
-
tools: ToolSet;
|
|
109
|
-
}
|
|
110
|
-
export interface CliAdapter {
|
|
111
|
-
/** 适配器名:运行时的 cli 字段,或 run[0] 的文件名 */
|
|
112
|
-
name: string;
|
|
113
|
-
/** 一行原始输出 → 统一事件;读不懂的行返回 [] */
|
|
114
|
-
parse(line: string): CliEvent[];
|
|
115
|
-
/** stdin 模式(运行时 stdin: "stream-json")下写进 stdin 的那一行用户消息,带换行 */
|
|
116
|
-
stdinMessage(prompt: string): string;
|
|
117
|
-
/** 模板里 `{tools}` 填什么(一个参数里的文字) */
|
|
118
|
-
toolList(set: ToolSet): string;
|
|
119
|
-
/** 模板里独占一个参数的 `{toolArgs}` 展开成哪几个参数(可以是零个);readDirs = 这轮该读得到的目录(workspace 根、vault),会话 cwd 是老师目录,它们在 cwd 外面 */
|
|
120
|
-
toolArgs(set: ToolSet, ctx: {
|
|
121
|
-
readDirs: readonly string[];
|
|
122
|
-
}): string[];
|
|
123
|
-
toolKind(name: string): ToolKind;
|
|
124
|
-
/** 这轮带或不带工具时,进程起来报的工具表(init 事件)应该正好是哪几样;多出来的记提醒(CLI 升版本会带进新工具)。不给 = 不核对 */
|
|
125
|
-
expectedTools?(set: ToolSet): readonly string[];
|
|
126
|
-
/** 起进程前把这个 CLI 在 workspace 里要的机器文件备好(幂等,内容一样就不写) */
|
|
127
|
-
prepare?(root: string): Promise<void>;
|
|
128
|
-
/** 由这轮的计划定下来的环境变量(进预热的比对:变了的备用进程不能用);没有就不写 */
|
|
129
|
-
planEnv?(ctx: PlanContext): Record<string, string>;
|
|
130
|
-
/** 起进程前最后过一遍环境(认证等);不改就原样返回 */
|
|
131
|
-
env(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
132
|
-
/** cotutor doctor 查这个 CLI 时多看几样(version 是 `--version` 的第一行);不给就只查装没装 */
|
|
133
|
-
check?(ctx: {
|
|
134
|
-
version: string;
|
|
135
|
-
env: NodeJS.ProcessEnv;
|
|
136
|
-
}): {
|
|
137
|
-
name: string;
|
|
138
|
-
ok: boolean;
|
|
139
|
-
detail: string;
|
|
140
|
-
fix?: string;
|
|
141
|
-
}[];
|
|
142
|
-
/** 出厂运行时模板:键是运行时名 */
|
|
143
|
-
runtimes(): Record<string, CliRuntime>;
|
|
144
|
-
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 脑的统一事件与工具:API 脑(./api.ts)写进 .log、孩子端的流式板书、家长端看原文、转录都只认这些。
|
|
3
|
+
* 名字里的 Cli 是 2026-11-02 以前起 Qwen Code 时留下的;那时的 .log 是 stream-json,由 ./stream-json.ts 读成同样的事件。
|
|
4
|
+
*/
|
|
5
|
+
/** 工具名 → 类别:API 脑的三样(./api-tools.ts)与旧轮次里 Qwen Code、Claude Code 的工具名;不认识的是 other */
|
|
6
|
+
const TOOL_KINDS = {
|
|
7
|
+
read_file: 'read', read_many_files: 'read', zoom_image: 'read', list_directory: 'read', glob: 'search', grep_search: 'search',
|
|
8
|
+
run_shell_command: 'shell', edit: 'edit', write_file: 'edit', notebook_edit: 'edit', skill: 'skill',
|
|
9
|
+
Read: 'read', Bash: 'shell', Grep: 'search', Glob: 'search', Edit: 'edit', Write: 'edit', NotebookEdit: 'edit', MultiEdit: 'edit', Skill: 'skill',
|
|
10
|
+
};
|
|
11
|
+
export const toolKind = (name) => TOOL_KINDS[name] ?? 'other';
|
|
12
|
+
/** 一条回复的正文段 / 工具调用 */
|
|
13
|
+
export const textsOf = (parts) => parts.flatMap((p) => (p.type === 'text' ? [p.text] : []));
|
|
14
|
+
export const toolsOf = (parts) => parts.flatMap((p) => (p.type === 'tool' ? [p.tool] : []));
|
|
@@ -35,9 +35,9 @@ export interface GlyphPenOptions {
|
|
|
35
35
|
/** 采样步长(归一化单位);缺省 SAMPLE_STEP。调小更平滑,但动画 DOM 体积约按平方涨 */
|
|
36
36
|
sampleStep?: number;
|
|
37
37
|
/**
|
|
38
|
-
* 字形表(provider):缺省内置拉丁包(a–z / A–Z)
|
|
39
|
-
* (
|
|
40
|
-
*
|
|
38
|
+
* 字形表(provider):缺省内置拉丁包(a–z / A–Z)。别的字形做成同构的 Map 从这里传入,就和字母共用同一支笔;
|
|
39
|
+
* 归一化约定由传入方定并写明(拉丁 = 四线三格;汉字可取格子高 1、底线 y=0、顶 y=-1)。
|
|
40
|
+
* 汉字要逐笔写时从 hanzi-writer-data 的 medians 现做(本包已依赖它,田字格卡就读它),不另挑字表。
|
|
41
41
|
*/
|
|
42
42
|
glyphs?: ReadonlyMap<string, Glyph>;
|
|
43
43
|
}
|
|
@@ -45,7 +45,7 @@ export class UnknownGlyphError extends Error {
|
|
|
45
45
|
/** node 的 strip-only 类型擦除不支持构造函数参数属性,字段显式声明 */
|
|
46
46
|
ch;
|
|
47
47
|
constructor(ch) {
|
|
48
|
-
super(`字形表未收录 '${ch}'。内置包只有 a–z 与 A–Z
|
|
48
|
+
super(`字形表未收录 '${ch}'。内置包只有 a–z 与 A–Z;别的字形做成同构的 Map 经 glyphs 选项传入。`);
|
|
49
49
|
this.ch = ch;
|
|
50
50
|
}
|
|
51
51
|
}
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -11,5 +11,5 @@ export * from '../memory/plan.ts';
|
|
|
11
11
|
export * from '../runtime/journal/ledger.ts';
|
|
12
12
|
export * from '../runtime/journal/conversation.ts';
|
|
13
13
|
export * from '../shared/time.ts';
|
|
14
|
-
export * from '../brain/
|
|
14
|
+
export * from '../brain/runtime.ts';
|
|
15
15
|
export * from '../memory/timetable.ts';
|
package/dist/lib/index.js
CHANGED
|
@@ -11,5 +11,5 @@ export * from "../memory/plan.js";
|
|
|
11
11
|
export * from "../runtime/journal/ledger.js";
|
|
12
12
|
export * from "../runtime/journal/conversation.js";
|
|
13
13
|
export * from "../shared/time.js";
|
|
14
|
-
export * from "../brain/
|
|
14
|
+
export * from "../brain/runtime.js";
|
|
15
15
|
export * from "../memory/timetable.js";
|
package/dist/memory/diary.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { threads } from "../schema/index.js";
|
|
1
|
+
import { threads, viaSendsLabel } from "../schema/index.js";
|
|
2
2
|
/** 孩子在这个话题里说的话:from: kid、不是「继续」/ 交答案这种动作、非空;多行并成一行 */
|
|
3
3
|
export function kidQuestions(messages, thread) {
|
|
4
4
|
const ths = threads(messages);
|
|
5
5
|
const out = [];
|
|
6
6
|
messages.forEach((m, i) => {
|
|
7
|
-
// 首页的开场 /
|
|
8
|
-
if (ths[i] !== thread || m.from !== 'kid' || m.action || (m.via &&
|
|
7
|
+
// 首页的开场 / 接着按钮、方砖发的是按钮上的字,不是孩子说的
|
|
8
|
+
if (ths[i] !== thread || m.from !== 'kid' || m.action || (m.via && viaSendsLabel(m.via)))
|
|
9
9
|
return;
|
|
10
10
|
const text = m.text.replace(/\s+/g, ' ').trim();
|
|
11
11
|
if (text && text !== '继续')
|
|
@@ -55,7 +55,7 @@ export interface RealtimeEvents {
|
|
|
55
55
|
onError?(message: string): void;
|
|
56
56
|
onClose?(reason: string): void;
|
|
57
57
|
}
|
|
58
|
-
/** 百炼按量的 key 与业务空间 id(Token Plan 没有 omni realtime
|
|
58
|
+
/** 百炼按量的 key 与业务空间 id(Token Plan 没有 omni realtime;都在 ~/.config/cotutor/bailian.json,见 src/shared/qianwen.ts) */
|
|
59
59
|
export declare function dashscopeAuth(env?: NodeJS.ProcessEnv): {
|
|
60
60
|
key: string;
|
|
61
61
|
workspaceId: string;
|
package/dist/realtime/bailian.js
CHANGED
|
@@ -28,7 +28,7 @@ export const OUTPUT_RATE = 24000;
|
|
|
28
28
|
export const MAX_IMAGE_B64 = 262144 - 1024;
|
|
29
29
|
const TRANSCRIBE_MODEL = 'qwen3-asr-flash-realtime';
|
|
30
30
|
const HEARTBEAT_PCM = Buffer.alloc(INPUT_RATE * 2);
|
|
31
|
-
/** 百炼按量的 key 与业务空间 id(Token Plan 没有 omni realtime
|
|
31
|
+
/** 百炼按量的 key 与业务空间 id(Token Plan 没有 omni realtime;都在 ~/.config/cotutor/bailian.json,见 src/shared/qianwen.ts) */
|
|
32
32
|
export function dashscopeAuth(env = process.env) {
|
|
33
33
|
const key = qianwenKeys(env).payg;
|
|
34
34
|
const workspaceId = bailianWorkspaceId(env);
|
|
@@ -95,8 +95,9 @@ export class RealtimeSession {
|
|
|
95
95
|
async connect() {
|
|
96
96
|
let url = this.opts.endpoint;
|
|
97
97
|
let key = '';
|
|
98
|
+
// 指了端点的是测试与探针的假百炼,不看 key
|
|
98
99
|
if (url)
|
|
99
|
-
key =
|
|
100
|
+
key = 'sk-test';
|
|
100
101
|
else {
|
|
101
102
|
const auth = this.opts.auth ?? dashscopeAuth(this.opts.env);
|
|
102
103
|
if (!auth)
|
package/dist/realtime/preview.js
CHANGED
|
@@ -17,7 +17,7 @@ export async function previewVoice(o) {
|
|
|
17
17
|
});
|
|
18
18
|
const why = await s.connect();
|
|
19
19
|
if (why !== null)
|
|
20
|
-
return { error: why === 'no_auth' ? '没有百炼的 key 或业务空间 id(
|
|
20
|
+
return { error: why === 'no_auth' ? '没有百炼的 key 或业务空间 id(家长端「首页」里填,存在 ~/.config/cotutor/bailian.json)' : `连不上:${why}` };
|
|
21
21
|
s.createResponse(readCue([o.text]));
|
|
22
22
|
const until = Date.now() + (o.timeoutMs ?? 20_000);
|
|
23
23
|
while (!done && !err && Date.now() < until)
|
package/dist/realtime/voices.js
CHANGED
|
@@ -6,64 +6,64 @@
|
|
|
6
6
|
* 英语老师的讲稿是中文里夹英文(「apple 苹果,banana 香蕉」):2026-10-20 真连(照念 → 转写回字)Serena、Tina、Maia、Liora Mira、Mione、Theo Calm 都念对,
|
|
7
7
|
* Jennifer、Mia 的中文带英文腔(Jennifer:「腿」成了 tway,「它是什么颜色」没了),trait 里记着。
|
|
8
8
|
*/
|
|
9
|
-
const v = (voice, name, trait, lang = '普通话') => ({ voice, name, trait, lang });
|
|
9
|
+
const v = (voice, name, gender, trait, lang = '普通话') => ({ voice, name, gender, trait, lang });
|
|
10
10
|
export const REALTIME_VOICES = [
|
|
11
|
-
v('Tina', '甜甜', '甜美温暖,解决问题果断(缺省)'),
|
|
12
|
-
v('Cindy', '林欣宜', '台湾腔嗲嗲的小姐姐', '台湾口音'),
|
|
13
|
-
v('Liora Mira', '清欢', '温柔的烟火人间感'),
|
|
14
|
-
v('Raymond', '林川野', '声音清亮,爱吃外卖的宅男'),
|
|
15
|
-
v('Zane', '泽恩', '磁性迷人'),
|
|
16
|
-
v('Katerina', '卡捷琳娜', '御姐音,韵律感强'),
|
|
17
|
-
v('Ryan', '甜茶', '节奏感强,戏感足'),
|
|
18
|
-
v('Mia', '舒然', '细腻,生活美学风格的温柔博主;中英夹着说时中文带英文腔'),
|
|
19
|
-
v('Cici', '绵绵', '邻家妹妹,活泼软糯'),
|
|
20
|
-
v('Theo Calm', '予安', '沉静,治愈感'),
|
|
21
|
-
v('Serena', '苏瑶', '温柔小姐姐'),
|
|
22
|
-
v('Maia', '四月', '知性与温柔'),
|
|
23
|
-
v('Evan', '江晨', '男大学生,年下奶狗'),
|
|
24
|
-
v('Qiao', '小乔妹', '表面甜妹,个性十足', '台湾口音'),
|
|
25
|
-
v('Momo', '茉兔', '撒娇搞怪'),
|
|
26
|
-
v('Wil', '伟伦', '深圳长大的港台腔小哥哥'),
|
|
27
|
-
v('Angel', '安琪', '带台式口音,甜美'),
|
|
28
|
-
v('Li Cassian', '李公公', '话中有留白,善察言观色'),
|
|
29
|
-
v('Joyner', '阿逗', '搞笑、夸张、接地气'),
|
|
30
|
-
v('Gold', '金爷', '西海岸黑人说唱歌手风格'),
|
|
31
|
-
v('Jennifer', '詹妮弗', '电影质感的美语女声;中英夹着说时中文带英文腔(苹果念成 pingguo)'),
|
|
32
|
-
v('Aiden', '艾登', '精通厨艺的美语大男孩'),
|
|
33
|
-
v('Mione', '敏儿', '成熟知性的英国邻家妹妹'),
|
|
34
|
-
v('Sunny', '晴儿', '甜美的川妹子', '四川话'),
|
|
35
|
-
v('Dylan', '晓东', '北京胡同长大的少年', '北京话'),
|
|
36
|
-
v('Eric', '程川', '成都市井男子,跳脱', '四川话'),
|
|
37
|
-
v('Peter', '李彼得', '天津相声,专业捧哏', '天津话'),
|
|
38
|
-
v('Joseph Chen', '阿樸伯', '南洋老华侨', '闽南话'),
|
|
39
|
-
v('Marcus', '秦川', '老陕风格,面宽话短', '陕西话'),
|
|
40
|
-
v('Li', '老李', '南京话伯伯,爱骂骂咧咧', '南京话'),
|
|
41
|
-
v('Rocky', '阿强', '幽默风趣,在线陪聊', '粤语'),
|
|
42
|
-
v('Kiki', '阿清', '甜美的港妹闺蜜', '粤语'),
|
|
43
|
-
v('Sohee', '素熙', '温柔开朗的韩国欧尼'),
|
|
44
|
-
v('Eliška', '艾莉卡', '中欧风格'),
|
|
45
|
-
v('Alek', '阿列克', '战斗民族的冷与暖'),
|
|
46
|
-
v('Arda', '阿尔达', '干净利落,温润'),
|
|
47
|
-
v('Dolce', '多尔切', '慵懒的意大利大叔'),
|
|
48
|
-
v('Lenn', '莱恩', '德国青年,理性中带叛逆'),
|
|
49
|
-
v('Ono Anna', '小野杏', '鬼灵精怪的青梅竹马'),
|
|
50
|
-
v('Sonrisa', '索尼莎', '热情开朗的拉美大姐'),
|
|
51
|
-
v('Bodega', '博德加', '热情的西班牙大叔'),
|
|
52
|
-
v('Andre', '安德雷', '磁性、沉稳的男生'),
|
|
53
|
-
v('Radio Gol', '拉迪奥·戈尔', '足球解说风格'),
|
|
54
|
-
v('Rizky', '阿力', '印尼青年,声线个性'),
|
|
55
|
-
v('Roya', '萝雅', '热爱运动的女孩'),
|
|
56
|
-
v('Hana', '阿幸', '越南成熟姐姐'),
|
|
57
|
-
v('Jakub', '雅克', '波兰文艺青年,声线磁性'),
|
|
58
|
-
v('Griet', '海娜', '荷兰成熟文艺女性'),
|
|
59
|
-
v('Marina', '玛丽娜', '多元文化城市长大的女孩'),
|
|
60
|
-
v('Siiri', '西芮', '内敛温柔,语速舒缓'),
|
|
61
|
-
v('Ingrid', '林恩', '挪威乡村姑娘'),
|
|
62
|
-
v('Sigga', '海娜', '冰岛小镇知性女青年'),
|
|
63
|
-
v('Bea', '雅娜', '爱喝咖啡的甜美姐姐'),
|
|
64
|
-
v('Chloe', '思怡', '马来西亚白领女生'),
|
|
65
|
-
v('Emilien', '埃米尔安', '浪漫的法国大哥哥'),
|
|
66
|
-
v('longanlingxin', '龙安灵心', '知心温暖音'),
|
|
11
|
+
v('Tina', '甜甜', '女', '甜美温暖,解决问题果断(缺省)'),
|
|
12
|
+
v('Cindy', '林欣宜', '女', '台湾腔嗲嗲的小姐姐', '台湾口音'),
|
|
13
|
+
v('Liora Mira', '清欢', '女', '温柔的烟火人间感'),
|
|
14
|
+
v('Raymond', '林川野', '男', '声音清亮,爱吃外卖的宅男'),
|
|
15
|
+
v('Zane', '泽恩', '男', '磁性迷人'),
|
|
16
|
+
v('Katerina', '卡捷琳娜', '女', '御姐音,韵律感强'),
|
|
17
|
+
v('Ryan', '甜茶', '男', '节奏感强,戏感足'),
|
|
18
|
+
v('Mia', '舒然', '女', '细腻,生活美学风格的温柔博主;中英夹着说时中文带英文腔'),
|
|
19
|
+
v('Cici', '绵绵', '女', '邻家妹妹,活泼软糯'),
|
|
20
|
+
v('Theo Calm', '予安', '男', '沉静,治愈感'),
|
|
21
|
+
v('Serena', '苏瑶', '女', '温柔小姐姐'),
|
|
22
|
+
v('Maia', '四月', '女', '知性与温柔'),
|
|
23
|
+
v('Evan', '江晨', '男', '男大学生,年下奶狗'),
|
|
24
|
+
v('Qiao', '小乔妹', '女', '表面甜妹,个性十足', '台湾口音'),
|
|
25
|
+
v('Momo', '茉兔', '女', '撒娇搞怪'),
|
|
26
|
+
v('Wil', '伟伦', '男', '深圳长大的港台腔小哥哥'),
|
|
27
|
+
v('Angel', '安琪', '女', '带台式口音,甜美'),
|
|
28
|
+
v('Li Cassian', '李公公', '男', '话中有留白,善察言观色'),
|
|
29
|
+
v('Joyner', '阿逗', '男', '搞笑、夸张、接地气'),
|
|
30
|
+
v('Gold', '金爷', '男', '西海岸黑人说唱歌手风格'),
|
|
31
|
+
v('Jennifer', '詹妮弗', '女', '电影质感的美语女声;中英夹着说时中文带英文腔(苹果念成 pingguo)'),
|
|
32
|
+
v('Aiden', '艾登', '男', '精通厨艺的美语大男孩'),
|
|
33
|
+
v('Mione', '敏儿', '女', '成熟知性的英国邻家妹妹'),
|
|
34
|
+
v('Sunny', '晴儿', '女', '甜美的川妹子', '四川话'),
|
|
35
|
+
v('Dylan', '晓东', '男', '北京胡同长大的少年', '北京话'),
|
|
36
|
+
v('Eric', '程川', '男', '成都市井男子,跳脱', '四川话'),
|
|
37
|
+
v('Peter', '李彼得', '男', '天津相声,专业捧哏', '天津话'),
|
|
38
|
+
v('Joseph Chen', '阿樸伯', '男', '南洋老华侨', '闽南话'),
|
|
39
|
+
v('Marcus', '秦川', '男', '老陕风格,面宽话短', '陕西话'),
|
|
40
|
+
v('Li', '老李', '男', '南京话伯伯,爱骂骂咧咧', '南京话'),
|
|
41
|
+
v('Rocky', '阿强', '男', '幽默风趣,在线陪聊', '粤语'),
|
|
42
|
+
v('Kiki', '阿清', '女', '甜美的港妹闺蜜', '粤语'),
|
|
43
|
+
v('Sohee', '素熙', '女', '温柔开朗的韩国欧尼'),
|
|
44
|
+
v('Eliška', '艾莉卡', '女', '中欧风格'),
|
|
45
|
+
v('Alek', '阿列克', '男', '战斗民族的冷与暖'),
|
|
46
|
+
v('Arda', '阿尔达', '男', '干净利落,温润'),
|
|
47
|
+
v('Dolce', '多尔切', '男', '慵懒的意大利大叔'),
|
|
48
|
+
v('Lenn', '莱恩', '男', '德国青年,理性中带叛逆'),
|
|
49
|
+
v('Ono Anna', '小野杏', '女', '鬼灵精怪的青梅竹马'),
|
|
50
|
+
v('Sonrisa', '索尼莎', '女', '热情开朗的拉美大姐'),
|
|
51
|
+
v('Bodega', '博德加', '男', '热情的西班牙大叔'),
|
|
52
|
+
v('Andre', '安德雷', '男', '磁性、沉稳的男生'),
|
|
53
|
+
v('Radio Gol', '拉迪奥·戈尔', '男', '足球解说风格'),
|
|
54
|
+
v('Rizky', '阿力', '男', '印尼青年,声线个性'),
|
|
55
|
+
v('Roya', '萝雅', '女', '热爱运动的女孩'),
|
|
56
|
+
v('Hana', '阿幸', '女', '越南成熟姐姐'),
|
|
57
|
+
v('Jakub', '雅克', '男', '波兰文艺青年,声线磁性'),
|
|
58
|
+
v('Griet', '海娜', '女', '荷兰成熟文艺女性'),
|
|
59
|
+
v('Marina', '玛丽娜', '女', '多元文化城市长大的女孩'),
|
|
60
|
+
v('Siiri', '西芮', '女', '内敛温柔,语速舒缓'),
|
|
61
|
+
v('Ingrid', '林恩', '女', '挪威乡村姑娘'),
|
|
62
|
+
v('Sigga', '海娜', '女', '冰岛小镇知性女青年'),
|
|
63
|
+
v('Bea', '雅娜', '女', '爱喝咖啡的甜美姐姐'),
|
|
64
|
+
v('Chloe', '思怡', '女', '马来西亚白领女生'),
|
|
65
|
+
v('Emilien', '埃米尔安', '男', '浪漫的法国大哥哥'),
|
|
66
|
+
v('longanlingxin', '龙安灵心', '女', '知心温暖音'),
|
|
67
67
|
];
|
|
68
68
|
/** 这个模型认不认这个音色(工作台、doctor 拦写错的) */
|
|
69
69
|
export function isRealtimeVoice(voice) {
|
|
@@ -12,6 +12,18 @@ export declare const EXAMPLES_SUFFIX = ".\u4F8B\u5B50";
|
|
|
12
12
|
export declare function listActivities(agentsDir: string, tutor?: string, opts?: {
|
|
13
13
|
examples?: boolean;
|
|
14
14
|
}): Promise<ActivityRef[]>;
|
|
15
|
-
/** workspace 里那份的位置 */
|
|
15
|
+
/** workspace 里那份的位置(就是这个名字,不认改过的名;装、升级、写都用它) */
|
|
16
16
|
export declare function activityFile(root: string, tutor: string, name: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* 改过名的出厂 activity:旧名 → 新名。2026-11-03 批改 → 检查作业(孩子端方砖上的字)。
|
|
19
|
+
* upgrade 把 workspace 里的旧文件挪成新名(src/app/cli/activities.ts);旧草稿里的 `activity 批改`、旧话题记的名字照样认(activityName)
|
|
20
|
+
*/
|
|
21
|
+
export declare const ACTIVITY_RENAMES: Readonly<Record<string, string>>;
|
|
22
|
+
/**
|
|
23
|
+
* 这个名字在这位老师家里实际是哪个文件:有同名的就是它;旧名的文件挪走了认新名;新名的还没有(没 upgrade 过)认旧名。都没有 → 原样
|
|
24
|
+
* (读的时候用:跑话题、首页检查、方砖;lint 的 fileName 也要用这个,不然 name 对不上)
|
|
25
|
+
*/
|
|
26
|
+
export declare function activityName(root: string, tutor: string, name: string): string;
|
|
27
|
+
/** 读的时候找的那份:认改过的名(activityName) */
|
|
28
|
+
export declare function findActivityFile(root: string, tutor: string, name: string): string;
|
|
17
29
|
export declare function activityRunFor(root: string, tutor: string, name: string, raw?: string): Promise<ActivityRun>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* 读 activity 文件(从 app/cli/activities.ts 拆出来,2026-10-29,行为不变):在哪、每位老师有哪几个、按「名=值」的参数拼成一次跑。
|
|
3
3
|
* 运行层每轮都要读,所以不放在命令行那一层;装、升级、还原、检查仍在 app/cli/activities.ts。
|
|
4
4
|
*/
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
5
6
|
import { readFile, readdir } from 'node:fs/promises';
|
|
6
7
|
import { join } from 'node:path';
|
|
7
8
|
import { parseActivity, resolveParams } from "./activity.js";
|
|
@@ -22,12 +23,37 @@ export async function listActivities(agentsDir, tutor, opts = {}) {
|
|
|
22
23
|
}
|
|
23
24
|
return out;
|
|
24
25
|
}
|
|
25
|
-
/** workspace 里那份的位置 */
|
|
26
|
+
/** workspace 里那份的位置(就是这个名字,不认改过的名;装、升级、写都用它) */
|
|
26
27
|
export function activityFile(root, tutor, name) {
|
|
27
28
|
return join(root, 'agents', tutor, ACTIVITIES_DIR, `${name}.md`);
|
|
28
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* 改过名的出厂 activity:旧名 → 新名。2026-11-03 批改 → 检查作业(孩子端方砖上的字)。
|
|
32
|
+
* upgrade 把 workspace 里的旧文件挪成新名(src/app/cli/activities.ts);旧草稿里的 `activity 批改`、旧话题记的名字照样认(activityName)
|
|
33
|
+
*/
|
|
34
|
+
export const ACTIVITY_RENAMES = { 批改: '检查作业' };
|
|
35
|
+
/**
|
|
36
|
+
* 这个名字在这位老师家里实际是哪个文件:有同名的就是它;旧名的文件挪走了认新名;新名的还没有(没 upgrade 过)认旧名。都没有 → 原样
|
|
37
|
+
* (读的时候用:跑话题、首页检查、方砖;lint 的 fileName 也要用这个,不然 name 对不上)
|
|
38
|
+
*/
|
|
39
|
+
export function activityName(root, tutor, name) {
|
|
40
|
+
const has = (n) => existsSync(activityFile(root, tutor, n));
|
|
41
|
+
if (has(name))
|
|
42
|
+
return name;
|
|
43
|
+
const to = ACTIVITY_RENAMES[name];
|
|
44
|
+
if (to && has(to))
|
|
45
|
+
return to;
|
|
46
|
+
const from = Object.keys(ACTIVITY_RENAMES).find((k) => ACTIVITY_RENAMES[k] === name);
|
|
47
|
+
if (from && has(from))
|
|
48
|
+
return from;
|
|
49
|
+
return name;
|
|
50
|
+
}
|
|
51
|
+
/** 读的时候找的那份:认改过的名(activityName) */
|
|
52
|
+
export function findActivityFile(root, tutor, name) {
|
|
53
|
+
return activityFile(root, tutor, activityName(root, tutor, name));
|
|
54
|
+
}
|
|
29
55
|
export async function activityRunFor(root, tutor, name, raw = '') {
|
|
30
|
-
const file =
|
|
56
|
+
const file = findActivityFile(root, tutor, name);
|
|
31
57
|
const text = await readFile(file, 'utf8').catch(() => null);
|
|
32
58
|
if (text === null)
|
|
33
59
|
throw new UsageError(`${file} 不在;cotutor activity check ${tutor} 看这位老师有哪些`);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 数这个话题画了几张、今天还剩几张、上一张为什么没画。纯判断在 src/runtime/activity.ts(gatePasses / failingGate),这里只管读盘。
|
|
4
4
|
*/
|
|
5
5
|
import { readFile } from 'node:fs/promises';
|
|
6
|
-
import {
|
|
6
|
+
import { findActivityFile } from "./activities.js";
|
|
7
7
|
import { failingGate, parseActivity } from "./activity.js";
|
|
8
8
|
import { threads } from "../schema/index.js";
|
|
9
9
|
import {} from "../schema/index.js";
|
|
@@ -15,7 +15,7 @@ export function threadActivity(messages, thread) {
|
|
|
15
15
|
}
|
|
16
16
|
/** 读 workspace 里那份 activity 文件;不在 → null */
|
|
17
17
|
export async function readActivity(ws, tutor, name) {
|
|
18
|
-
const text = await readFile(
|
|
18
|
+
const text = await readFile(findActivityFile(ws.root, tutor, name), 'utf8').catch(() => null);
|
|
19
19
|
return text === null ? null : parseActivity(text).activity;
|
|
20
20
|
}
|
|
21
21
|
/** 这个话题里的画图卡,文件顺序:哪一轮第几张 */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 老师文件(agents/<name>.md):YAML frontmatter + 正文即系统提示。
|
|
3
|
-
* 这里只认平铺的 `key: value` frontmatter(
|
|
4
|
-
*
|
|
3
|
+
* 这里只认平铺的 `key: value` frontmatter(name / description / maxTurns / permissionMode / memory,当初两个 CLI 的公共子集,应用只读 name);
|
|
4
|
+
* 正文是系统提示的前半(后面接板书写法)。
|
|
5
5
|
*/
|
|
6
|
-
/**
|
|
6
|
+
/** 老师文件 frontmatter 只写这几个键(人设 / 政策 / 开关在 cotutor.json);技能 cotutor-tune 的字段表从这里取 */
|
|
7
7
|
export declare const AGENT_FRONTMATTER_KEYS: readonly ["name", "description", "maxTurns", "permissionMode", "memory"];
|
|
8
8
|
export interface AgentFile {
|
|
9
9
|
frontmatter: Record<string, string>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 老师文件(agents/<name>.md):YAML frontmatter + 正文即系统提示。
|
|
3
|
-
* 这里只认平铺的 `key: value` frontmatter(
|
|
4
|
-
*
|
|
3
|
+
* 这里只认平铺的 `key: value` frontmatter(name / description / maxTurns / permissionMode / memory,当初两个 CLI 的公共子集,应用只读 name);
|
|
4
|
+
* 正文是系统提示的前半(后面接板书写法)。
|
|
5
5
|
*/
|
|
6
|
-
/**
|
|
6
|
+
/** 老师文件 frontmatter 只写这几个键(人设 / 政策 / 开关在 cotutor.json);技能 cotutor-tune 的字段表从这里取 */
|
|
7
7
|
export const AGENT_FRONTMATTER_KEYS = ['name', 'description', 'maxTurns', 'permissionMode', 'memory'];
|
|
8
8
|
export function parseAgentFile(text) {
|
|
9
9
|
const open = /^---[^\S\n]*\n/.exec(text);
|
|
@@ -15,8 +15,8 @@ export declare function boardGuideBody(skillMd: string): string;
|
|
|
15
15
|
/** 上下文包 boardGuide: 行在预载时写的话(事实陈述,不让老师自查) */
|
|
16
16
|
export declare const BOARD_GUIDE_IN_SYSTEM = "\u5DF2\u5728\u4F60\u7684\u7CFB\u7EDF\u63D0\u793A\u91CC(\u300C# \u677F\u4E66\u600E\u4E48\u5199\u300D),\u4E0D\u7528\u8BFB";
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* 认三种:技能工具(claude 的 Skill、qwen 的 skill
|
|
18
|
+
* 回归检查:板书写法已经在系统提示里,老师这轮还用工具去读它 → 系统提示里那份没起作用(换模型后最先坏的地方)。
|
|
19
|
+
* 认三种:技能工具(旧轮次 claude 的 Skill、qwen 的 skill,按工具类别认)点名 cotutor-board;任何工具的参数里带 cotutor-board/SKILL.md(read_file 等),或旧轮次按老师裁的那份 .cotutor/board/。
|
|
20
20
|
* references/<种类>.md 是按需读的,不算。
|
|
21
21
|
*/
|
|
22
22
|
export declare function boardGuideReads(tools: readonly {
|
|
@@ -7,7 +7,7 @@ import { parseAgentFile } from "../agent-file.js";
|
|
|
7
7
|
import { stripHumanNotes } from "../../shared/human-notes.js";
|
|
8
8
|
import { applyCardGuards } from "../../board/card-guards.js";
|
|
9
9
|
import { BOARD_CARD_KINDS } from "../../schema/config.js";
|
|
10
|
-
import { toolKind } from "../../brain/
|
|
10
|
+
import { toolKind } from "../../brain/index.js";
|
|
11
11
|
import { SKILLS_DIR } from "../../schema/workspace.js";
|
|
12
12
|
export const TUTOR_SKILL = 'cotutor-tutor';
|
|
13
13
|
/** 相对 workspace 根 */
|
|
@@ -38,8 +38,8 @@ export function boardGuideBody(skillMd) {
|
|
|
38
38
|
/** 上下文包 boardGuide: 行在预载时写的话(事实陈述,不让老师自查) */
|
|
39
39
|
export const BOARD_GUIDE_IN_SYSTEM = '已在你的系统提示里(「# 板书怎么写」),不用读';
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
42
|
-
* 认三种:技能工具(claude 的 Skill、qwen 的 skill
|
|
41
|
+
* 回归检查:板书写法已经在系统提示里,老师这轮还用工具去读它 → 系统提示里那份没起作用(换模型后最先坏的地方)。
|
|
42
|
+
* 认三种:技能工具(旧轮次 claude 的 Skill、qwen 的 skill,按工具类别认)点名 cotutor-board;任何工具的参数里带 cotutor-board/SKILL.md(read_file 等),或旧轮次按老师裁的那份 .cotutor/board/。
|
|
43
43
|
* references/<种类>.md 是按需读的,不算。
|
|
44
44
|
*/
|
|
45
45
|
export function boardGuideReads(tools) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 解析走板书同一个解析器(place = home);永不抛错,问题都带行号(1 起)与修法。要读盘的引用(话题在不在)由调用方查好传进来。
|
|
4
4
|
*/
|
|
5
5
|
import type { TutorButton } from '../../board/cards/index.ts';
|
|
6
|
-
import { type Activity } from '../../schema/index.ts';
|
|
6
|
+
import { type Activity, type HomeTileId } from '../../schema/index.ts';
|
|
7
7
|
import type { BoardCard } from '../../board/presentation.ts';
|
|
8
8
|
export type HomeIssueLevel = 'fix' | 'note';
|
|
9
9
|
export interface HomeIssue {
|
|
@@ -130,6 +130,17 @@ export type KidHomeButton = {
|
|
|
130
130
|
video?: true;
|
|
131
131
|
resume?: Resume;
|
|
132
132
|
};
|
|
133
|
+
/** 孩子端老师卡上常驻的一块方砖(HOME_TILES):label = activity 的名字;resume = 今天从这个 activity 进去的话题(和 activity 按钮同一个入口) */
|
|
134
|
+
export interface KidHomeTile {
|
|
135
|
+
id: HomeTileId;
|
|
136
|
+
label: string;
|
|
137
|
+
resume?: Resume;
|
|
138
|
+
}
|
|
139
|
+
/** 这位老师牌上的方砖:开着的(on,牌上的先后)里,家里那个 activity 用得了的才出 */
|
|
140
|
+
export declare function kidTiles(on: readonly HomeTileId[], opts: {
|
|
141
|
+
usable: (activity: string) => boolean;
|
|
142
|
+
resumes?: ReadonlyMap<string, Resume>;
|
|
143
|
+
}): KidHomeTile[];
|
|
133
144
|
export declare const NEW_THREAD_LABEL = "\u65B0\u8BDD\u9898";
|
|
134
145
|
/** 一个话题是从哪个入口进去的:小课堂(按课包)、activity(按名字,参数不比)、自由话题(新话题按钮或没有按钮);别的按钮(开场、接着)进去的 → null */
|
|
135
146
|
export declare function resumeKey(first: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LECTURE_ACTIVITY } from "../../schema/index.js";
|
|
1
|
+
import { HOME_TILES, LECTURE_ACTIVITY } from "../../schema/index.js";
|
|
2
2
|
import { resolveParams } from "../activity.js";
|
|
3
3
|
import { parseBoard } from "../../board/parse.js";
|
|
4
4
|
const DAY_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
@@ -227,6 +227,13 @@ export function homeOwners(cards, tutors) {
|
|
|
227
227
|
return who ?? cur;
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
|
+
/** 这位老师牌上的方砖:开着的(on,牌上的先后)里,家里那个 activity 用得了的才出 */
|
|
231
|
+
export function kidTiles(on, opts) {
|
|
232
|
+
return HOME_TILES.filter((t) => on.includes(t.id) && opts.usable(t.activity)).map((t) => {
|
|
233
|
+
const r = opts.resumes?.get(`activity:${t.activity}`);
|
|
234
|
+
return { id: t.id, label: t.activity, ...(r ? { resume: r } : {}) };
|
|
235
|
+
});
|
|
236
|
+
}
|
|
230
237
|
export const NEW_THREAD_LABEL = '新话题';
|
|
231
238
|
const RESUME_MAX = 10;
|
|
232
239
|
/** 一个话题是从哪个入口进去的:小课堂(按课包)、activity(按名字,参数不比)、自由话题(新话题按钮或没有按钮);别的按钮(开场、接着)进去的 → null */
|
|
@@ -116,7 +116,7 @@ export declare function messageVia(via: HomeVia, r: ResolvedVia): MessageVia;
|
|
|
116
116
|
export declare function continueContext(ws: Workspace, tutor: string, date: string, thread: string): Promise<NonNullable<ContextPack['continue']> | null>;
|
|
117
117
|
export interface HomeClick {
|
|
118
118
|
tutor: string;
|
|
119
|
-
button:
|
|
119
|
+
button: HomeVia['button'];
|
|
120
120
|
label: string;
|
|
121
121
|
uses: {
|
|
122
122
|
date: string;
|