feat: integrate pixel teacher presence and resilient classroom preview

This commit is contained in:
鲨鱼辣椒
2026-09-23 18:23:53 +08:00
parent 12d800ec51
commit 7951cca700
60 changed files with 3313 additions and 180 deletions

View File

@@ -3,10 +3,11 @@ import { isAIGatewayUserContextMissing } from '../../../shared/ai-gateway-error-
import { getAIGatewayErrorKind } from '../../../shared/ai-gateway-error-kind';
export function projectPiProviderFailure(message: unknown): CodingRuntimePublicError {
if (typeof message === 'string' && isAIGatewayUserContextMissing(message)) {
if (typeof message === 'string' && (isAIGatewayUserContextMissing(message)
|| message.toLowerCase().includes('works square login session is missing or expired'))) {
return {
code: 'CODING_PROVIDER_AUTH_REQUIRED',
message: '模型服务身份上下文无效,请重试;若仍失败请重新登录。',
message: '登录已失效,请重新登录后继续。',
recoverable: true,
};
}

View File

@@ -321,8 +321,10 @@ function reconcileLiveIds(
if (node.kind === 'message') {
const live = liveNodes.find((candidate) => candidate.kind === 'message'
&& !used.has(candidate.id)
&& (candidate.sourceEntryId === node.sourceEntryId
|| messageSignature(candidate) === messageSignature(node)));
&& Boolean(node.sourceEntryId) && candidate.sourceEntryId === node.sourceEntryId)
?? liveNodes.find((candidate) => candidate.kind === 'message'
&& !used.has(candidate.id) && !candidate.sourceEntryId
&& messageSignature(candidate) === messageSignature(node));
if (!live || live.kind !== 'message') return node;
used.add(live.id);
return {
@@ -383,12 +385,16 @@ export async function projectPiSessionSnapshot(
const path = activePath(response.entries, response.leafId);
const durableNodes = await projectEntries(path, input);
const nodes = reconcileLiveIds(durableNodes, input.snapshot.nodes);
const preserveTerminalRun = input.workerGeneration === input.snapshot.cursor.workerGeneration
&& Boolean(input.snapshot.run.runId)
&& (input.snapshot.run.status === 'error'
|| (input.snapshot.run.status === 'idle' && input.snapshot.run.terminalReason !== undefined));
return {
...structuredClone(input.snapshot),
nodes,
run: state.isStreaming === true
? { ...structuredClone(input.snapshot.run), status: 'running' }
: { status: 'idle' },
: preserveTerminalRun ? structuredClone(input.snapshot.run) : { status: 'idle' },
queue: { items: [] },
context: projectedContext(input, state),
pendingInteractions: [],