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
package/dist/brain/api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 脑执行的 API 实现(2026-10-29):不起 CLI,应用在服务进程里直连百炼的 Chat Completions(AI SDK + @ai-sdk/alibaba,cotutor-api-prototype 验过这条路)。
|
|
3
|
+
* - 发到哪按模型选(src/shared/qianwen.ts 的 compatibleLine):Token Plan 有这个模型、本机有 token-plan.json 就走 Token Plan(2026-11-02 起),否则百炼按量
|
|
3
4
|
* - 系统提示 = runner 给的 systemBody(老师正文或 activity + 板书写法);消息 = 上下文包 + 孩子的话,本轮的照片直接附成图,不靠老师自己去读
|
|
4
5
|
* - 同一话题的模型历史应用自己存:<日期>.<话题>.brain.jsonl,一行一轮(会话 id、模型、这轮的用户消息、模型回的全部消息、成没成);
|
|
5
6
|
* 接着话题时把同一会话的那几行拼回去。照片只记相对 workspace 根的路径,发的时候再编码。没回完的一轮只留写出来的正文,下一条消息前头注明
|
|
@@ -20,11 +21,11 @@ import { isStepCount, streamText } from 'ai';
|
|
|
20
21
|
import { randomUUID } from 'node:crypto';
|
|
21
22
|
import { createWriteStream } from 'node:fs';
|
|
22
23
|
import { appendFile, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
23
|
-
import { dirname, extname, isAbsolute, join, relative } from 'node:path';
|
|
24
|
-
import { getRuntime } from "./
|
|
24
|
+
import { dirname, extname, isAbsolute, join, relative, sep } from 'node:path';
|
|
25
|
+
import { getRuntime } from "./runtime.js";
|
|
26
|
+
import { parseEventLine } from "./log.js";
|
|
25
27
|
import { createPartialReader } from "./stream.js";
|
|
26
28
|
import { parseTranscript, toolCalls, toolSummary } from "./transcript.js";
|
|
27
|
-
import { compatibleBase, DASHSCOPE_HOST } from "../shared/qianwen.js";
|
|
28
29
|
import { isApiRuntime } from "../schema/index.js";
|
|
29
30
|
import { readOnlyTools } from "./api-tools.js";
|
|
30
31
|
/** 计划里记的 cli 名:API 的轮次按它分流 */
|
|
@@ -39,7 +40,7 @@ export const FACTORY_API_RUNTIMES = {
|
|
|
39
40
|
const DEFAULT_STEPS = 6;
|
|
40
41
|
/** 想一点:没有思考档的模型思考最多多少 token(有档的发 reasoning_effort,见 effortOf) */
|
|
41
42
|
const THINK_LITTLE = 1024;
|
|
42
|
-
/**
|
|
43
|
+
/** 有思考档的模型:qwen3.8 系列(2026-10-31 照当时 Qwen Code 的档对过),百炼兼容接口认 reasoning_effort */
|
|
43
44
|
const TIERED = /^qwen3\.8-/;
|
|
44
45
|
/** 一张照片最大多少字节(百炼按 base64 收,太大的不附,记一条提醒) */
|
|
45
46
|
const IMAGE_MAX = 10 * 1024 * 1024;
|
|
@@ -48,18 +49,6 @@ const MIME = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
|
|
|
48
49
|
const CODEC = 'ai-sdk@7';
|
|
49
50
|
/** 上一轮没回完:接在下一条消息前头 */
|
|
50
51
|
const UNFINISHED = '(应用注:上一轮你没有回完,上面是写出来的那部分;孩子可能听到了一些,也可能没有,别当它已经说完。)';
|
|
51
|
-
/** .log 的一行 → 统一事件(API 轮次的日志就是一行一条统一事件);不是这种行回 [] */
|
|
52
|
-
export function parseEventLine(line) {
|
|
53
|
-
if (!line.startsWith('{"kind":'))
|
|
54
|
-
return [];
|
|
55
|
-
try {
|
|
56
|
-
const e = JSON.parse(line);
|
|
57
|
-
return typeof e.kind === 'string' ? [e] : [];
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return [];
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
52
|
/** 政策 effort → 思考开关与思考量(同 qwen:low 不想,孩子在等;带工具的轮次至少想一点,不然照片题连资料都不查) */
|
|
64
53
|
export function thinkingOf(effort, tools) {
|
|
65
54
|
if (effort === 'high')
|
|
@@ -69,7 +58,7 @@ export function thinkingOf(effort, tools) {
|
|
|
69
58
|
return { enableThinking: false };
|
|
70
59
|
}
|
|
71
60
|
/**
|
|
72
|
-
*
|
|
61
|
+
* 有思考档的模型发 reasoning_effort(照当时 Qwen Code 的档):想一点 = low、不限 = xhigh;不想或没有档 → null,照 thinkingOf 发。
|
|
73
62
|
* low 是它自己看着想:闲聊想一两百 token,认照片题想得多;thinking_budget 是死卡,1024 时有照片的话题每轮都想满(2026-10-31)
|
|
74
63
|
*/
|
|
75
64
|
export function effortOf(model, effort, tools) {
|
|
@@ -87,12 +76,6 @@ export function effortFetch(effort, base = fetch) {
|
|
|
87
76
|
return base(input, { ...init, body: JSON.stringify({ ...body, reasoning_effort: effort }) });
|
|
88
77
|
};
|
|
89
78
|
}
|
|
90
|
-
/**
|
|
91
|
-
* 百炼 Chat Completions 的端点:知道业务空间 id 就走它的域名(原型验过,同 realtime);只有按量 key 就走公网域名(qwen 那边按量的线也走它,2026-10-29 真跑通)
|
|
92
|
-
*/
|
|
93
|
-
export function bailianBase(workspaceId) {
|
|
94
|
-
return workspaceId ? `https://${workspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1` : compatibleBase(DASHSCOPE_HOST);
|
|
95
|
-
}
|
|
96
79
|
/** 报错只说状态码:SDK 的异常里带着整个请求 */
|
|
97
80
|
function failure(err) {
|
|
98
81
|
const status = err && typeof err === 'object' && 'statusCode' in err ? err.statusCode : undefined;
|
|
@@ -213,12 +196,12 @@ async function pastOf(file, session, root, current) {
|
|
|
213
196
|
}
|
|
214
197
|
export class ApiBrain {
|
|
215
198
|
workspace;
|
|
216
|
-
|
|
217
|
-
/** 测试与探针:…/compatible-mode/v1(假百炼)
|
|
199
|
+
line;
|
|
200
|
+
/** 测试与探针:…/compatible-mode/v1(假百炼);不给按模型选线 */
|
|
218
201
|
endpoint;
|
|
219
202
|
constructor(opts) {
|
|
220
203
|
this.workspace = opts.workspace;
|
|
221
|
-
this.
|
|
204
|
+
this.line = opts.line;
|
|
222
205
|
this.endpoint = opts.endpoint;
|
|
223
206
|
}
|
|
224
207
|
plan(req) {
|
|
@@ -247,11 +230,11 @@ export class ApiBrain {
|
|
|
247
230
|
const session = plan.session ?? `api-${randomUUID()}`;
|
|
248
231
|
event({ kind: 'session', id: session });
|
|
249
232
|
const images = req.images ?? [];
|
|
250
|
-
const stored = images.map((p) => { const rel = relative(root, p); return rel.startsWith('..') || isAbsolute(rel) ? p : rel; });
|
|
233
|
+
const stored = images.map((p) => { const rel = relative(root, p); return rel.startsWith('..') || isAbsolute(rel) ? p : rel.split(sep).join('/'); });
|
|
251
234
|
const record = { v: 1, session, at: new Date().toISOString(), model: runtime.model, codec: CODEC, user: { text: req.prompt, images: stored }, messages: [], ok: false };
|
|
252
235
|
let final;
|
|
253
|
-
const
|
|
254
|
-
if (!
|
|
236
|
+
const line = this.line(runtime.model);
|
|
237
|
+
if (!line && !this.endpoint) {
|
|
255
238
|
final = { ok: false, text: null, reason: 'auth:没有百炼按量 key' };
|
|
256
239
|
warnings.push('百炼还没配好(按量 key):家长端「首页」里填,这位老师的运行时是 API 的,没有它说不了话');
|
|
257
240
|
}
|
|
@@ -267,7 +250,7 @@ export class ApiBrain {
|
|
|
267
250
|
}
|
|
268
251
|
const user = await userMessage(text, attach, root, warnings);
|
|
269
252
|
const tier = effortOf(runtime.model, req.effort, req.tools);
|
|
270
|
-
const alibaba = createAlibaba({ apiKey:
|
|
253
|
+
const alibaba = createAlibaba({ apiKey: line?.key ?? 'sk-endpoint', baseURL: this.endpoint ?? line.baseURL, includeUsage: true, ...(tier ? { fetch: effortFetch(tier) } : {}) });
|
|
271
254
|
// 断流看门狗:stall.ms 没有一块流到就停(工具在本进程里跑,几毫秒的事,不另算)
|
|
272
255
|
const abort = new AbortController();
|
|
273
256
|
let stalled = false;
|
|
@@ -296,7 +279,7 @@ export class ApiBrain {
|
|
|
296
279
|
try {
|
|
297
280
|
const result = streamText({
|
|
298
281
|
model: alibaba(runtime.model),
|
|
299
|
-
...(req.systemBody
|
|
282
|
+
...(req.systemBody ? { system: req.systemBody } : {}),
|
|
300
283
|
messages: [...past, user],
|
|
301
284
|
...(req.tools !== 'off' ? { tools: readOnlyTools(req.readDirs, { images }) } : {}),
|
|
302
285
|
stopWhen: isStepCount(runtime.maxSteps ?? DEFAULT_STEPS),
|
|
@@ -399,13 +382,13 @@ export class ApiBrain {
|
|
|
399
382
|
hooks.onExit(Date.now());
|
|
400
383
|
await new Promise((r) => out.end(r));
|
|
401
384
|
// 没配凭证的那轮连请求都没发:不进历史
|
|
402
|
-
if (
|
|
385
|
+
if (line || this.endpoint)
|
|
403
386
|
await appendFile(files.history, `${JSON.stringify(record)}\n`).catch(() => { });
|
|
404
387
|
const logText = await readFile(files.log, 'utf8').catch(() => '');
|
|
405
388
|
const transcript = parseTranscript(logText, parseEventLine);
|
|
406
389
|
const tools = toolCalls(logText, parseEventLine).slice(0, 200);
|
|
407
390
|
return { transcript: { ...transcript, final: transcript.final ?? final }, tools, warnings };
|
|
408
391
|
};
|
|
409
|
-
return {
|
|
392
|
+
return { text: () => reader.text(), done: go() };
|
|
410
393
|
}
|
|
411
394
|
}
|
package/dist/brain/index.d.ts
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 脑执行:一轮教学里「让脑想、写出正文」的那一步。
|
|
3
|
-
* runner
|
|
3
|
+
* runner 拼好上下文包与系统提示交到这里;这里请模型(应用直连百炼,./api.ts)、读输出,随流回报正文与工具,收尾给出整轮的转录。
|
|
4
4
|
* 执行完不等于这一轮交付完:板书解析、配音、记忆与日记的收尾归 runner。
|
|
5
|
-
*
|
|
5
|
+
* 2026-11-02 以前还能起 Qwen Code(命令行);那时的轮次照样读得出(./log.ts),运行时用不了了(./runtime.ts)。
|
|
6
6
|
*/
|
|
7
|
-
import { type CliEvent } from './cli/adapters.ts';
|
|
8
|
-
import { type RunPlan } from './cli/run-plan.ts';
|
|
9
7
|
import type { ToolCall, Transcript } from './transcript.ts';
|
|
10
|
-
import {
|
|
11
|
-
import type { ToolSet } from './
|
|
12
|
-
import type {
|
|
13
|
-
|
|
14
|
-
export type {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
19
|
-
export { adapterFor, factoryRuntimes, parserOf, prepareCli, processEnv, runtimeIssue, toolKind, type ToolKind } from './cli/adapters.ts';
|
|
8
|
+
import type { CotutorConfig, Policy } from '../schema/index.ts';
|
|
9
|
+
import type { ToolSet } from './types.ts';
|
|
10
|
+
import type { RunPlan } from './runtime.ts';
|
|
11
|
+
import { type BrainLine } from './api.ts';
|
|
12
|
+
export type { ToolKind, ToolSet } from './types.ts';
|
|
13
|
+
export { toolKind } from './types.ts';
|
|
14
|
+
export { parseLogLine } from './log.ts';
|
|
15
|
+
export { RuntimeError, getRuntime, runtimeIssue, type RunPlan } from './runtime.ts';
|
|
20
16
|
export { FACTORY_API_RUNTIMES } from './api.ts';
|
|
21
|
-
/**
|
|
17
|
+
/** 一轮要脑做的:哪个运行时、接哪个会话、系统提示、这条消息(上下文包 + 孩子的话)、思考量、工具 */
|
|
22
18
|
export interface BrainRequest {
|
|
23
19
|
/** 运行时名,缺省 cotutor.json 的 runtimes.default */
|
|
24
20
|
runtime?: string;
|
|
@@ -29,35 +25,24 @@ export interface BrainRequest {
|
|
|
29
25
|
} | null;
|
|
30
26
|
agent: string;
|
|
31
27
|
prompt: string;
|
|
32
|
-
|
|
28
|
+
/** 系统提示:老师正文(或 activity)+ 板书写法 */
|
|
33
29
|
systemBody?: string;
|
|
34
|
-
boardFile?: string;
|
|
35
30
|
effort?: Policy['effort'];
|
|
36
31
|
tools: ToolSet;
|
|
37
|
-
/**
|
|
32
|
+
/** 只读工具读得到的目录(workspace 根、vault) */
|
|
38
33
|
readDirs: readonly string[];
|
|
39
|
-
/** 本轮附的照片(绝对路径)
|
|
34
|
+
/** 本轮附的照片(绝对路径),直接附成图 */
|
|
40
35
|
images?: readonly string[];
|
|
41
|
-
/** 整段压缩(
|
|
36
|
+
/** 整段压缩(上一轮结束时上下文过了 32k):之前的模型历史不再发,digest 是应用照索引拼的前情;prompt 这一轮带笔记全文 */
|
|
42
37
|
compact?: {
|
|
43
38
|
digest: string;
|
|
44
39
|
};
|
|
45
|
-
/** 回复停在写成正文的工具调用上了吗(板书的规矩,runner 交进来);CLI 据此 resume 一次告诉老师不会有结果,没给就不查。API 不用它 */
|
|
46
|
-
stuckOnTool?: (text: string) => boolean;
|
|
47
|
-
}
|
|
48
|
-
/** 接预热好的进程要对得上的:老师目录(进程 cwd)、日期、这个话题末条 job(新开记 null) */
|
|
49
|
-
export interface WarmKey {
|
|
50
|
-
cwd: string;
|
|
51
|
-
date: string;
|
|
52
|
-
lastJob: string | null;
|
|
53
40
|
}
|
|
54
41
|
export interface BrainHooks {
|
|
55
|
-
/** 起了一次(第一次、断流接着跑、工具调用写成正文后接着跑);warmBornAt = 接过来的预热进程起来的时刻 */
|
|
56
42
|
onStart(s: {
|
|
57
43
|
cli: string;
|
|
58
44
|
runtime: string;
|
|
59
45
|
resume: boolean;
|
|
60
|
-
warmBornAt?: number;
|
|
61
46
|
}): void;
|
|
62
47
|
/** 当前回复的正文变了(读 BrainRun.text()) */
|
|
63
48
|
onText(): void;
|
|
@@ -65,23 +50,16 @@ export interface BrainHooks {
|
|
|
65
50
|
name: string;
|
|
66
51
|
sub: boolean;
|
|
67
52
|
}): void;
|
|
68
|
-
|
|
69
|
-
idleMs: number;
|
|
70
|
-
retry: number;
|
|
71
|
-
kept: number;
|
|
72
|
-
}): void;
|
|
73
|
-
/** 进程都退了,还没读日志收尾;at = Date.now() */
|
|
53
|
+
/** 模型回完了,还没读日志收尾;at = Date.now() */
|
|
74
54
|
onExit(at: number): void;
|
|
75
55
|
}
|
|
76
56
|
export interface BrainRun {
|
|
77
|
-
/**
|
|
78
|
-
warm: boolean;
|
|
79
|
-
/** 当前这条回复的正文(断流接着跑的,含被杀那次已经写出的) */
|
|
57
|
+
/** 当前这条回复的正文 */
|
|
80
58
|
text(): string;
|
|
81
59
|
done: Promise<BrainResult>;
|
|
82
60
|
}
|
|
83
61
|
export interface BrainResult {
|
|
84
|
-
/** final
|
|
62
|
+
/** final 总有:没收尾的记成没成 */
|
|
85
63
|
transcript: Transcript & {
|
|
86
64
|
final: NonNullable<Transcript['final']>;
|
|
87
65
|
};
|
|
@@ -90,56 +68,30 @@ export interface BrainResult {
|
|
|
90
68
|
/** 给家长端这一轮的提醒,按出现顺序 */
|
|
91
69
|
warnings: string[];
|
|
92
70
|
}
|
|
93
|
-
/**
|
|
71
|
+
/** 这一轮落在哪:统一事件、stderr、这个话题的模型历史 */
|
|
94
72
|
export interface BrainFiles {
|
|
95
73
|
log: string;
|
|
96
74
|
err: string;
|
|
97
75
|
history: string;
|
|
98
76
|
}
|
|
99
77
|
export interface BrainExecutor {
|
|
100
|
-
/**
|
|
78
|
+
/** 定这一轮怎么跑:哪个运行时、接不接着会话(run 文件照记) */
|
|
101
79
|
plan(req: BrainRequest): RunPlan;
|
|
102
|
-
/**
|
|
103
|
-
prepare(plan: RunPlan): Promise<void>;
|
|
104
|
-
/**
|
|
105
|
-
* 跑一轮。同步返回:接预热进程在调用的这一拍里(调用方查完 busy 到这里之间不能有 await)。
|
|
106
|
-
* files:原始输出与 stderr 落哪;stall:断流看门狗
|
|
107
|
-
*/
|
|
80
|
+
/** 跑一轮。files:统一事件与 stderr 落哪;stall:断流看门狗 */
|
|
108
81
|
start(req: BrainRequest, plan: RunPlan, ctx: {
|
|
109
82
|
tutor: string;
|
|
110
|
-
key: WarmKey;
|
|
111
83
|
files: BrainFiles;
|
|
112
84
|
stall: Policy['stall'];
|
|
113
85
|
}, hooks: BrainHooks): BrainRun;
|
|
114
|
-
/** 这个运行时能不能预热(消息走 stdin 的才行) */
|
|
115
|
-
canWarm(runtime: Runtime): boolean;
|
|
116
|
-
/** 给下一轮起好进程;这个位置已有对得上的就不动 */
|
|
117
|
-
warm(tutor: string, slot: Slot, plan: RunPlan, key: WarmKey): void;
|
|
118
|
-
warmed(tutor: string, slot?: Slot): boolean;
|
|
119
|
-
dropWarm(tutor: string, slot?: Slot): void;
|
|
120
|
-
/** 杀掉所有预热的进程(测试收尾;服务退出时它们的 stdin 断了会自己退) */
|
|
121
|
-
close(): void;
|
|
122
86
|
}
|
|
123
87
|
/**
|
|
124
|
-
*
|
|
88
|
+
* line:这个模型发到哪、带哪把 key(runner 给 compatibleLine);endpoint:测试与探针指到假百炼(…/compatible-mode/v1)
|
|
125
89
|
*/
|
|
126
90
|
export declare function createBrain(opts: {
|
|
127
91
|
workspace: () => {
|
|
128
92
|
config: CotutorConfig;
|
|
129
93
|
root: string;
|
|
130
94
|
};
|
|
131
|
-
|
|
132
|
-
warmIdleMs?: number;
|
|
133
|
-
auth?: () => {
|
|
134
|
-
key: string;
|
|
135
|
-
workspaceId: string | null;
|
|
136
|
-
} | null;
|
|
95
|
+
line?: BrainLine;
|
|
137
96
|
endpoint?: string;
|
|
138
97
|
}): BrainExecutor;
|
|
139
|
-
/** 这个运行时关得住工具吗:关得住的,孩子那轮不带工具时,只有工具才用得上的路径(refs、vault)不进上下文包 */
|
|
140
|
-
export declare function controlsTools(runtime: Runtime): boolean;
|
|
141
|
-
/**
|
|
142
|
-
* 读一轮的 .log:API 的轮次是一行一条统一事件,CLI 的按运行时的适配器读(运行时后来删了或改了按 stream-json)。
|
|
143
|
-
* 两种行长得不一样(统一事件以 {"kind": 开头),一行一行认,运行时从 CLI 换成 API 以后,以前的轮次照样读得出
|
|
144
|
-
*/
|
|
145
|
-
export declare function logParser(runtimes: Readonly<Record<string, unknown>>, name: string | undefined): (line: string) => CliEvent[];
|
package/dist/brain/index.js
CHANGED
|
@@ -1,74 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* 两种实现,按 cotutor.json 里这轮的运行时分:{run, resume} 起 CLI(./cli/run.ts,各家方言在 ./cli/ 的适配器);{api, model} 应用直连模型(./api.ts)。
|
|
6
|
-
*/
|
|
7
|
-
import { controlsTools as cliControlsTools, parserForRuntime } from "./cli/adapters.js";
|
|
8
|
-
import { getRuntime } from "./cli/run-plan.js";
|
|
9
|
-
import { isApiRuntime } from "../schema/index.js";
|
|
10
|
-
import { API_CLI, ApiBrain, parseEventLine } from "./api.js";
|
|
11
|
-
import { CliBrain } from "./cli/run.js";
|
|
12
|
-
/**
|
|
13
|
-
* CLI 那一家给外面用的几样(经 brain 的出口给,外面不直接引 clis/):工具名属于哪一类(家长端看原文、板书写法有没有被读);
|
|
14
|
-
* 出厂的运行时模板(init 写 cotutor.json);doctor 体检 CLI 运行时用的(退役没有、适配器、读输出、备机器文件、起进程的环境)
|
|
15
|
-
*/
|
|
16
|
-
export { adapterFor, factoryRuntimes, parserOf, prepareCli, processEnv, runtimeIssue, toolKind } from "./cli/adapters.js";
|
|
1
|
+
import { ApiBrain } from "./api.js";
|
|
2
|
+
export { toolKind } from "./types.js";
|
|
3
|
+
export { parseLogLine } from "./log.js";
|
|
4
|
+
export { RuntimeError, getRuntime, runtimeIssue } from "./runtime.js";
|
|
17
5
|
export { FACTORY_API_RUNTIMES } from "./api.js";
|
|
18
6
|
/**
|
|
19
|
-
*
|
|
7
|
+
* line:这个模型发到哪、带哪把 key(runner 给 compatibleLine);endpoint:测试与探针指到假百炼(…/compatible-mode/v1)
|
|
20
8
|
*/
|
|
21
9
|
export function createBrain(opts) {
|
|
22
|
-
return new
|
|
23
|
-
}
|
|
24
|
-
/** 按这轮的运行时分给 CLI 或 API;预热只有 CLI 有 */
|
|
25
|
-
class Brains {
|
|
26
|
-
workspace;
|
|
27
|
-
cli;
|
|
28
|
-
api;
|
|
29
|
-
constructor(workspace, cli, api) {
|
|
30
|
-
this.workspace = workspace;
|
|
31
|
-
this.cli = cli;
|
|
32
|
-
this.api = api;
|
|
33
|
-
}
|
|
34
|
-
plan(req) {
|
|
35
|
-
return isApiRuntime(getRuntime(this.workspace().config, req.runtime).runtime) ? this.api.plan(req) : this.cli.plan(req);
|
|
36
|
-
}
|
|
37
|
-
prepare(plan) {
|
|
38
|
-
return plan.cli === API_CLI ? Promise.resolve() : this.cli.prepare(plan);
|
|
39
|
-
}
|
|
40
|
-
start(req, plan, ctx, hooks) {
|
|
41
|
-
return plan.cli === API_CLI ? this.api.start(req, plan, ctx, hooks) : this.cli.start(req, plan, ctx, hooks);
|
|
42
|
-
}
|
|
43
|
-
canWarm(runtime) {
|
|
44
|
-
return !isApiRuntime(runtime) && this.cli.canWarm(runtime);
|
|
45
|
-
}
|
|
46
|
-
warm(tutor, slot, plan, key) {
|
|
47
|
-
if (plan.cli !== API_CLI)
|
|
48
|
-
this.cli.warm(tutor, slot, plan, key);
|
|
49
|
-
}
|
|
50
|
-
warmed(tutor, slot) {
|
|
51
|
-
return this.cli.warmed(tutor, slot);
|
|
52
|
-
}
|
|
53
|
-
dropWarm(tutor, slot) {
|
|
54
|
-
this.cli.dropWarm(tutor, slot);
|
|
55
|
-
}
|
|
56
|
-
close() {
|
|
57
|
-
this.cli.close();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/** 这个运行时关得住工具吗:关得住的,孩子那轮不带工具时,只有工具才用得上的路径(refs、vault)不进上下文包 */
|
|
61
|
-
export function controlsTools(runtime) {
|
|
62
|
-
return isApiRuntime(runtime) || cliControlsTools(runtime);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* 读一轮的 .log:API 的轮次是一行一条统一事件,CLI 的按运行时的适配器读(运行时后来删了或改了按 stream-json)。
|
|
66
|
-
* 两种行长得不一样(统一事件以 {"kind": 开头),一行一行认,运行时从 CLI 换成 API 以后,以前的轮次照样读得出
|
|
67
|
-
*/
|
|
68
|
-
export function logParser(runtimes, name) {
|
|
69
|
-
const cli = parserForRuntime(runtimes, name);
|
|
70
|
-
return (line) => {
|
|
71
|
-
const e = parseEventLine(line);
|
|
72
|
-
return e.length ? e : cli(line);
|
|
73
|
-
};
|
|
10
|
+
return new ApiBrain({ workspace: opts.workspace, line: opts.line ?? (() => null), endpoint: opts.endpoint });
|
|
74
11
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 一轮的 .log 怎么读:API 轮次一行一条统一事件({"kind": 开头,./api.ts 写的);2026-11-02 以前的轮次是 Qwen Code 的 stream-json(./stream-json.ts)。
|
|
3
|
+
* 两种行长得不一样,一行一行认,同一份记录里换过也读得出。
|
|
4
|
+
*/
|
|
5
|
+
import { parseStreamJson } from "./stream-json.js";
|
|
6
|
+
/** 统一事件的一行;不是这种行回 [] */
|
|
7
|
+
export function parseEventLine(line) {
|
|
8
|
+
if (!line.startsWith('{"kind":'))
|
|
9
|
+
return [];
|
|
10
|
+
try {
|
|
11
|
+
const e = JSON.parse(line);
|
|
12
|
+
return typeof e.kind === 'string' ? [e] : [];
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** .log 的一行 → 统一事件:先当统一事件,不是再当旧的 stream-json */
|
|
19
|
+
export function parseLogLine(line) {
|
|
20
|
+
const e = parseEventLine(line);
|
|
21
|
+
return e.length ? e : parseStreamJson(line);
|
|
22
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 这一轮用哪个运行时、接不接着会话。运行时只有 API 一种(`{api: "bailian", model}`,./api.ts)。
|
|
3
|
+
* 2026-11-02 起不再起命令行(Qwen Code;Claude CLI 10-28 就去掉了):旧 cotutor.json 里写着 `{run, resume}` 的照样读得进来,
|
|
4
|
+
* 但用不了——doctor 报出来,用到它的那位老师发不出去,说怎么改。以前那种运行时下聊的话题照样能看(sameRuntime:接不上、不记账)。
|
|
5
|
+
*/
|
|
6
|
+
import { type ApiRuntime, type CotutorConfig, type Runtime } from '../schema/index.ts';
|
|
7
|
+
import type { ToolSet } from './types.ts';
|
|
8
|
+
export interface RunPlan {
|
|
9
|
+
/** 运行时名(runtimes 里的键) */
|
|
10
|
+
runtime: string;
|
|
11
|
+
/** 哪种脑跑的(API 的是 api;旧轮次的 run.json 里是 CLI 的适配器名) */
|
|
12
|
+
cli: string;
|
|
13
|
+
/** 这轮带不带工具 */
|
|
14
|
+
tools: ToolSet;
|
|
15
|
+
/** 旧轮次起 CLI 的命令行;API 的是空 */
|
|
16
|
+
argv: string[];
|
|
17
|
+
/** 这次是接着会话还是新开 */
|
|
18
|
+
resume: boolean;
|
|
19
|
+
/** 接着的会话 id */
|
|
20
|
+
session: string | null;
|
|
21
|
+
env: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
export declare class RuntimeError extends Error {
|
|
24
|
+
}
|
|
25
|
+
/** 命令行运行时(旧配置里的 `{run, resume}`)用不了,说怎么改;API 运行时 null */
|
|
26
|
+
export declare function runtimeIssue(runtime: Runtime): string | null;
|
|
27
|
+
export declare function getRuntime(config: CotutorConfig, name?: string): {
|
|
28
|
+
name: string;
|
|
29
|
+
runtime: ApiRuntime;
|
|
30
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 这一轮用哪个运行时、接不接着会话。运行时只有 API 一种(`{api: "bailian", model}`,./api.ts)。
|
|
3
|
+
* 2026-11-02 起不再起命令行(Qwen Code;Claude CLI 10-28 就去掉了):旧 cotutor.json 里写着 `{run, resume}` 的照样读得进来,
|
|
4
|
+
* 但用不了——doctor 报出来,用到它的那位老师发不出去,说怎么改。以前那种运行时下聊的话题照样能看(sameRuntime:接不上、不记账)。
|
|
5
|
+
*/
|
|
6
|
+
import { isApiRuntime } from "../schema/index.js";
|
|
7
|
+
export class RuntimeError extends Error {
|
|
8
|
+
}
|
|
9
|
+
/** 命令行运行时(旧配置里的 `{run, resume}`)用不了,说怎么改;API 运行时 null */
|
|
10
|
+
export function runtimeIssue(runtime) {
|
|
11
|
+
return isApiRuntime(runtime) ? null : '命令行运行时(Qwen Code 等)2026-11-02 起不再支持:把 runtimes.default 和 tutors.*.runtime 改成 api 或 api-flash(应用直连百炼),再删掉这一条';
|
|
12
|
+
}
|
|
13
|
+
export function getRuntime(config, name) {
|
|
14
|
+
const n = name ?? config.runtimes.default;
|
|
15
|
+
const p = config.runtimes[n];
|
|
16
|
+
if (n === 'default' || !p || typeof p === 'string') {
|
|
17
|
+
const known = Object.keys(config.runtimes).filter((k) => k !== 'default');
|
|
18
|
+
throw new RuntimeError(`运行时 "${n}" 不存在;cotutor.json 的 runtimes 里有:${known.join('、')}`);
|
|
19
|
+
}
|
|
20
|
+
if (!isApiRuntime(p))
|
|
21
|
+
throw new RuntimeError(`运行时 "${n}":${runtimeIssue(p)}`);
|
|
22
|
+
return { name: n, runtime: p };
|
|
23
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Qwen Code
|
|
3
|
-
*
|
|
2
|
+
* 旧轮次的 .log:2026-11-02 以前老师的脑是起 Qwen Code(或更早的 Claude Code),输出是 stream-json(一行一个 JSON),这里读成统一事件,
|
|
3
|
+
* 那时的记录照样能看(看原文、读了什么、转录)。不再起 CLI,只读。Qwen Code 0.25 的输出:
|
|
4
4
|
* - `{"type":"system","subtype":"init","session_id","tools":[…]}`(qwen 的 json 模式叫 session_start)
|
|
5
5
|
* - `{"type":"stream_event","event":{…}}`:`--include-partial-messages` 才有;message_start 开新的一条,content_block_delta 的 text_delta 是正文、thinking_delta 是在想
|
|
6
6
|
* - `{"type":"assistant","message":{"id","content":[text / tool_use 块],"usage"}}`:一条整的回复;usage 是这一次请求自己的
|
|
@@ -14,5 +14,3 @@ import type { CliEvent } from './types.ts';
|
|
|
14
14
|
/** 结果块的文字:字符串原样,块数组拼各块的 text */
|
|
15
15
|
export declare const blockText: (c: unknown) => string;
|
|
16
16
|
export declare function parseStreamJson(line: string): CliEvent[];
|
|
17
|
-
/** stream-json 输入的一条用户消息(`--input-format stream-json` 一行一条) */
|
|
18
|
-
export declare function streamJsonUserMessage(prompt: string): string;
|
|
@@ -80,7 +80,3 @@ export function parseStreamJson(line) {
|
|
|
80
80
|
}
|
|
81
81
|
return out;
|
|
82
82
|
}
|
|
83
|
-
/** stream-json 输入的一条用户消息(`--input-format stream-json` 一行一条) */
|
|
84
|
-
export function streamJsonUserMessage(prompt) {
|
|
85
|
-
return `${JSON.stringify({ type: 'user', message: { role: 'user', content: prompt } })}\n`;
|
|
86
|
-
}
|
package/dist/brain/stream.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* 流式:老师还在说的时候,从脑的输出事件里拼出「当前这条顶层回复的正文」,给板书的 partial 解析用。
|
|
3
|
+
* 增量来的每一小段是一条 delta,每条新回复前有 start;只有整条 assistant 的(旧轮次的一些 CLI)也认:
|
|
4
4
|
* delta 往当前文本上追加,start 时当前段带围栏或 H2 就留下(与 kid-view 的 kidSource 同一条规则:老师板书完再用工具,卡不丢)、否则丢掉,
|
|
5
5
|
* 新段从空开始;顶层 assistant 的正文直接当作当前段(与拼出来的一样)。text() = 留下的段 + 当前段。
|
|
6
|
-
*
|
|
6
|
+
* 子代理的一律跳过(旧轮次的 CLI 才有)。这里只认统一事件(./log.ts 读)。
|
|
7
7
|
*/
|
|
8
|
-
import { type CliEvent } from './
|
|
8
|
+
import { type CliEvent } from './types.ts';
|
|
9
9
|
export interface PartialReader {
|
|
10
10
|
/** 喂一块 stdout(原始字节,行没收完的先攒着);返回文本有没有变 */
|
|
11
11
|
feed(chunk: string): boolean;
|
|
@@ -16,5 +16,5 @@ export interface PartialReader {
|
|
|
16
16
|
/** 只要当前这段(还没收到整条 assistant 的那段;断流时它没进会话,要递回给老师) */
|
|
17
17
|
current(): string;
|
|
18
18
|
}
|
|
19
|
-
/** parse:一行输出 →
|
|
19
|
+
/** parse:一行输出 → 统一事件;缺省 parseLogLine(统一事件,旧轮次的 stream-json 也认) */
|
|
20
20
|
export declare function createPartialReader(parse?: (line: string) => CliEvent[]): PartialReader;
|
package/dist/brain/stream.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* 流式:老师还在说的时候,从脑的输出事件里拼出「当前这条顶层回复的正文」,给板书的 partial 解析用。
|
|
3
|
+
* 增量来的每一小段是一条 delta,每条新回复前有 start;只有整条 assistant 的(旧轮次的一些 CLI)也认:
|
|
4
4
|
* delta 往当前文本上追加,start 时当前段带围栏或 H2 就留下(与 kid-view 的 kidSource 同一条规则:老师板书完再用工具,卡不丢)、否则丢掉,
|
|
5
5
|
* 新段从空开始;顶层 assistant 的正文直接当作当前段(与拼出来的一样)。text() = 留下的段 + 当前段。
|
|
6
|
-
*
|
|
6
|
+
* 子代理的一律跳过(旧轮次的 CLI 才有)。这里只认统一事件(./log.ts 读)。
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import { textsOf } from "./
|
|
8
|
+
import { parseLogLine } from "./log.js";
|
|
9
|
+
import { textsOf } from "./types.js";
|
|
10
10
|
const KEEP = /^\s*(?:```|~~~|## )/m;
|
|
11
|
-
/** parse:一行输出 →
|
|
12
|
-
export function createPartialReader(parse =
|
|
11
|
+
/** parse:一行输出 → 统一事件;缺省 parseLogLine(统一事件,旧轮次的 stream-json 也认) */
|
|
12
|
+
export function createPartialReader(parse = parseLogLine) {
|
|
13
13
|
let rest = '';
|
|
14
14
|
let text = '';
|
|
15
15
|
// 当前段收到过整条 assistant 了(已进会话,不算「断在半截」)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CliEvent, CliFinal } from './
|
|
2
|
-
/** 一行输出 → 统一事件(
|
|
1
|
+
import type { CliEvent, CliFinal } from './types.ts';
|
|
2
|
+
/** 一行输出 → 统一事件(./log.ts) */
|
|
3
3
|
export type LineParser = (line: string) => CliEvent[];
|
|
4
4
|
export interface TranscriptItem {
|
|
5
5
|
/** text 说话 / tool 用工具 / tool-error 工具报错 / done 收尾 */
|
|
@@ -12,7 +12,7 @@ export type TranscriptRow = TranscriptItem | {
|
|
|
12
12
|
kind: 'fold';
|
|
13
13
|
tools: TranscriptItem[];
|
|
14
14
|
};
|
|
15
|
-
/** 收尾:result 的最终正文、成没成、费用或 token(见
|
|
15
|
+
/** 收尾:result 的最终正文、成没成、费用或 token(见 ./types.ts 的 CliFinal) */
|
|
16
16
|
export type TranscriptFinal = CliFinal;
|
|
17
17
|
export interface Transcript {
|
|
18
18
|
sessionId: string | null;
|
package/dist/brain/transcript.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* 收尾的最终正文单独取出作 final
|
|
2
|
+
* 一轮的转录(.log,一行一个 JSON)→ 家长视图条目 + 最终文本。起初抄自 growth-apps 的 gen-transcript(2026-08-26,claude 2.1.220),
|
|
3
|
+
* 现在读成统一事件再拼(./log.ts:API 轮次的统一事件,旧轮次的 stream-json):子代理的条目标 sub(旧轮次的 CLI 才有),
|
|
4
|
+
* 收尾的最终正文单独取出作 final,孩子视图只看它。
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { parseLogLine } from "./log.js";
|
|
7
7
|
import { tokenText } from "../shared/tokens.js";
|
|
8
8
|
const FOLD_AT = 3;
|
|
9
9
|
/**
|
|
@@ -58,7 +58,7 @@ export function toolArg(input) {
|
|
|
58
58
|
}
|
|
59
59
|
return '';
|
|
60
60
|
}
|
|
61
|
-
export function toolCalls(text, parse =
|
|
61
|
+
export function toolCalls(text, parse = parseLogLine) {
|
|
62
62
|
const calls = [];
|
|
63
63
|
const byId = new Map();
|
|
64
64
|
for (const line of text.split('\n')) {
|
|
@@ -86,7 +86,7 @@ export function toolCalls(text, parse = parseStreamJson) {
|
|
|
86
86
|
}
|
|
87
87
|
return calls;
|
|
88
88
|
}
|
|
89
|
-
export function parseTranscript(text, parse =
|
|
89
|
+
export function parseTranscript(text, parse = parseLogLine) {
|
|
90
90
|
let sessionId = null;
|
|
91
91
|
const items = [];
|
|
92
92
|
let final = null;
|