feat(teacher): 连接 Yuxi 老师并提供项目与会话只读工具

This commit is contained in:
2026-09-23 09:56:11 +08:00
parent 1f2ad3fb3a
commit e35a13079e
14 changed files with 886 additions and 30 deletions

View File

@@ -56,9 +56,14 @@ export function createTeacherReadTools(access: TeacherReadAccess) {
return {
definitions: teacherReadToolDefinitions,
async execute(name: string, rawArguments: string, signal: AbortSignal, maxBytes = 2400): Promise<string> {
return (await this.executeResult(name, rawArguments, signal, maxBytes)).content;
},
async executeResult(name: string, rawArguments: string, signal: AbortSignal, maxBytes = 2400): Promise<{ status: 'success' | 'error'; content: string; truncated: boolean }> {
signal.throwIfAborted();
access.assertCurrent();
let result: string;
let status: 'success' | 'error' = 'success';
let truncated = false;
try {
const args = JSON.parse(rawArguments) as Record<string, unknown>;
if (!args || typeof args !== 'object' || Array.isArray(args)) throw new Error('Expected an object.');
@@ -71,6 +76,7 @@ export function createTeacherReadTools(access: TeacherReadAccess) {
}
case 'read_project_file': {
const file = await files.content(access.projectPath, projectPath(args.path));
truncated = file.truncated;
result = file.path + '\n' + lines(file.content, args)
+ (file.truncated ? '\n[File exceeds the 256 KiB text preview limit; only its beginning is available.]' : '');
break;
@@ -90,13 +96,15 @@ export function createTeacherReadTools(access: TeacherReadAccess) {
throw new Error('Only list_project_files, read_project_file and read_conversation are available.');
}
} catch (error) {
status = 'error';
// Keep local OS paths and unrelated application data out of model errors.
result = 'Read failed. Check the relative path, message id and line range. '
+ (error instanceof SyntaxError ? 'Tool arguments must be valid JSON.' : 'Only current-project text files and current-conversation messages are available.');
}
signal.throwIfAborted();
access.assertCurrent();
return excerptTeacherText(result, maxBytes);
const content = excerptTeacherText(result, maxBytes);
return { status, content, truncated: truncated || content !== result };
},
};
}