fix: rename h5 execution override to deep reasoning

This commit is contained in:
john
2026-07-04 14:57:13 +08:00
parent 12268110f7
commit e4657c7e55
13 changed files with 79 additions and 73 deletions
+8 -8
View File
@@ -155,7 +155,7 @@ async function validateToolGatewayResult({ result, validation, cwd }) {
return { expectedFiles: checks }; return { expectedFiles: checks };
} }
function withRunMetadata(userMessage, { toolMode = 'chat', taskType = null, forceGoose = false } = {}) { function withRunMetadata(userMessage, { toolMode = 'chat', taskType = null, forceDeepReasoning = false } = {}) {
const message = (userMessage && typeof userMessage === 'object' && !Array.isArray(userMessage)) const message = (userMessage && typeof userMessage === 'object' && !Array.isArray(userMessage))
? { ...userMessage } ? { ...userMessage }
: { value: userMessage }; : { value: userMessage };
@@ -168,7 +168,7 @@ function withRunMetadata(userMessage, { toolMode = 'chat', taskType = null, forc
: {}), : {}),
toolMode: normalizeAgentRunToolMode(toolMode), toolMode: normalizeAgentRunToolMode(toolMode),
...(taskType ? { taskType } : {}), ...(taskType ? { taskType } : {}),
...(forceGoose ? { forceGoose: true } : {}), ...(forceDeepReasoning ? { forceDeepReasoning: true } : {}),
}; };
return { ...message, metadata }; return { ...message, metadata };
} }
@@ -185,7 +185,7 @@ function getRunOptionsFromMessage(userMessage) {
return { return {
toolMode, toolMode,
taskType: normalizeTaskType(runMetadata?.taskType ?? metadata?.taskType), taskType: normalizeTaskType(runMetadata?.taskType ?? metadata?.taskType),
forceGoose: runMetadata?.forceGoose === true || metadata?.forceGoose === true, forceDeepReasoning: runMetadata?.forceDeepReasoning === true || metadata?.forceDeepReasoning === true,
validation: normalizeToolGatewayValidation(runMetadata?.validation ?? metadata?.toolGatewayValidation), validation: normalizeToolGatewayValidation(runMetadata?.validation ?? metadata?.toolGatewayValidation),
}; };
} }
@@ -289,7 +289,7 @@ export function createAgentRunGateway({
userMessage, userMessage,
toolMode = 'chat', toolMode = 'chat',
taskType = null, taskType = null,
forceGoose = false, forceDeepReasoning = false,
}) { }) {
const normalizedRequestId = String(requestId ?? '').trim(); const normalizedRequestId = String(requestId ?? '').trim();
if (!normalizedRequestId) { if (!normalizedRequestId) {
@@ -308,7 +308,7 @@ export function createAgentRunGateway({
const runMessage = withRunMetadata(userMessage, { const runMessage = withRunMetadata(userMessage, {
toolMode: normalizedToolMode, toolMode: normalizedToolMode,
taskType: normalizedTaskType, taskType: normalizedTaskType,
forceGoose, forceDeepReasoning,
}); });
await pool.query( await pool.query(
`INSERT INTO h5_agent_runs `INSERT INTO h5_agent_runs
@@ -329,7 +329,7 @@ export function createAgentRunGateway({
sessionId: sessionId || null, sessionId: sessionId || null,
toolMode: normalizedToolMode, toolMode: normalizedToolMode,
taskType: normalizedTaskType, taskType: normalizedTaskType,
forceGoose: Boolean(forceGoose), forceDeepReasoning: Boolean(forceDeepReasoning),
}); });
if (autoDispatch) dispatchRun(runId); if (autoDispatch) dispatchRun(runId);
return projectRun(await getRunById(runId)); return projectRun(await getRunById(runId));
@@ -391,7 +391,7 @@ export function createAgentRunGateway({
const userMessage = safeJsonParse(row.user_message_json, {}); const userMessage = safeJsonParse(row.user_message_json, {});
const runOptions = getRunOptionsFromMessage(userMessage); const runOptions = getRunOptionsFromMessage(userMessage);
const toolGatewayStatus = toolGateway?.getStatus ? toolGateway.getStatus() : null; const toolGatewayStatus = toolGateway?.getStatus ? toolGateway.getStatus() : null;
if (!runOptions.forceGoose && directChatService?.canHandle?.({ if (!runOptions.forceDeepReasoning && directChatService?.canHandle?.({
sessionId: row.agent_session_id ?? null, sessionId: row.agent_session_id ?? null,
toolMode: runOptions.toolMode, toolMode: runOptions.toolMode,
userMessage, userMessage,
@@ -467,7 +467,7 @@ export function createAgentRunGateway({
let sessionId = row.agent_session_id ?? null; let sessionId = row.agent_session_id ?? null;
if (isDirectChatSessionId(sessionId)) { if (isDirectChatSessionId(sessionId)) {
await appendEvent(runId, 'direct_session_escalated_to_goose', { await appendEvent(runId, 'direct_session_escalated_to_deep_reasoning', {
previousSessionId: sessionId, previousSessionId: sessionId,
}); });
sessionId = null; sessionId = null;
+16 -16
View File
@@ -263,10 +263,10 @@ test('agent run uses direct chat service for eligible chat messages', async () =
userAuth: {}, userAuth: {},
tkmindProxy: { tkmindProxy: {
async startSessionForUser() { async startSessionForUser() {
assert.fail('goose session should not start for direct chat'); assert.fail('backend session should not start for direct chat');
}, },
async submitSessionReplyForUser() { async submitSessionReplyForUser() {
assert.fail('goose reply should not be submitted for direct chat'); assert.fail('backend reply should not be submitted for direct chat');
}, },
}, },
directChatService: { directChatService: {
@@ -301,7 +301,7 @@ test('agent run uses direct chat service for eligible chat messages', async () =
assert.ok(pool.events.some((event) => event.eventType === 'direct_chat_completed')); assert.ok(pool.events.some((event) => event.eventType === 'direct_chat_completed'));
}); });
test('agent run escalates direct sessions to a new goose session when forced', async () => { test('agent run escalates direct sessions to a new backend session when forced', async () => {
const pool = createFakePool(); const pool = createFakePool();
const submitted = []; const submitted = [];
const gateway = createAgentRunGateway({ const gateway = createAgentRunGateway({
@@ -309,7 +309,7 @@ test('agent run escalates direct sessions to a new goose session when forced', a
tkmindProxy: { tkmindProxy: {
async startSessionForUser(userId) { async startSessionForUser(userId) {
assert.equal(userId, 'user-1'); assert.equal(userId, 'user-1');
return { id: 'goose-session-1' }; return { id: 'deep-session-1' };
}, },
async submitSessionReplyForUser(userId, sessionId, requestId, userMessage, options = {}) { async submitSessionReplyForUser(userId, sessionId, requestId, userMessage, options = {}) {
submitted.push({ userId, sessionId, requestId, userMessage, options }); submitted.push({ userId, sessionId, requestId, userMessage, options });
@@ -320,15 +320,15 @@ test('agent run escalates direct sessions to a new goose session when forced', a
const run = await gateway.createRun('user-1', { const run = await gateway.createRun('user-1', {
sessionId: 'h5direct_existing', sessionId: 'h5direct_existing',
requestId: 'req-force-goose', requestId: 'req-force-deep',
userMessage: { role: 'user', content: [{ type: 'text', text: '帮我生成页面 public/a.html' }] }, userMessage: { role: 'user', content: [{ type: 'text', text: '帮我生成页面 public/a.html' }] },
forceGoose: true, forceDeepReasoning: true,
}); });
await waitFor(() => pool.runs.get(run.id)?.status === 'succeeded'); await waitFor(() => pool.runs.get(run.id)?.status === 'succeeded');
assert.equal(pool.runs.get(run.id).agent_session_id, 'goose-session-1'); assert.equal(pool.runs.get(run.id).agent_session_id, 'deep-session-1');
assert.equal(submitted[0].sessionId, 'goose-session-1'); assert.equal(submitted[0].sessionId, 'deep-session-1');
assert.ok(pool.events.some((event) => event.eventType === 'direct_session_escalated_to_goose')); assert.ok(pool.events.some((event) => event.eventType === 'direct_session_escalated_to_deep_reasoning'));
}); });
test('agent run with code tool mode starts and submits with code policy', async () => { test('agent run with code tool mode starts and submits with code policy', async () => {
@@ -369,7 +369,7 @@ test('agent run with code tool mode starts and submits with code policy', async
assert.equal(submitted[0].userMessage.metadata.memindRun.toolMode, 'code'); assert.equal(submitted[0].userMessage.metadata.memindRun.toolMode, 'code');
}); });
test('agent run with enabled tool gateway dispatches code runs outside goose session', async () => { test('agent run with enabled tool gateway dispatches code runs outside backend session', async () => {
const pool = createFakePool(); const pool = createFakePool();
const jobs = []; const jobs = [];
const gateway = createAgentRunGateway({ const gateway = createAgentRunGateway({
@@ -382,10 +382,10 @@ test('agent run with enabled tool gateway dispatches code runs outside goose ses
}, },
tkmindProxy: { tkmindProxy: {
async startSessionForUser() { async startSessionForUser() {
assert.fail('goose session should not start for external tool gateway run'); assert.fail('backend session should not start for external tool gateway run');
}, },
async submitSessionReplyForUser() { async submitSessionReplyForUser() {
assert.fail('goose reply should not be submitted for external tool gateway run'); assert.fail('backend reply should not be submitted for external tool gateway run');
}, },
}, },
toolGateway: { toolGateway: {
@@ -434,10 +434,10 @@ test('agent run validates expected tool gateway artifacts before succeeding', as
}, },
tkmindProxy: { tkmindProxy: {
async startSessionForUser() { async startSessionForUser() {
assert.fail('goose session should not start for external tool gateway run'); assert.fail('backend session should not start for external tool gateway run');
}, },
async submitSessionReplyForUser() { async submitSessionReplyForUser() {
assert.fail('goose reply should not be submitted for external tool gateway run'); assert.fail('backend reply should not be submitted for external tool gateway run');
}, },
}, },
toolGateway: { toolGateway: {
@@ -505,10 +505,10 @@ test('agent run fails non-retryably when tool gateway artifact validation fails'
}, },
tkmindProxy: { tkmindProxy: {
async startSessionForUser() { async startSessionForUser() {
assert.fail('goose session should not start for external tool gateway run'); assert.fail('backend session should not start for external tool gateway run');
}, },
async submitSessionReplyForUser() { async submitSessionReplyForUser() {
assert.fail('goose reply should not be submitted for external tool gateway run'); assert.fail('backend reply should not be submitted for external tool gateway run');
}, },
}, },
toolGateway: { toolGateway: {
+2 -2
View File
@@ -56,7 +56,7 @@ export function createPostAgentRunsHandler({
const userMessage = request.body?.user_message ?? null; const userMessage = request.body?.user_message ?? null;
const rawToolMode = request.body?.tool_mode ?? request.body?.toolMode ?? 'chat'; const rawToolMode = request.body?.tool_mode ?? request.body?.toolMode ?? 'chat';
const taskType = String(request.body?.task_type ?? request.body?.taskType ?? '').trim() || null; const taskType = String(request.body?.task_type ?? request.body?.taskType ?? '').trim() || null;
const forceGoose = request.body?.force_goose === true || request.body?.forceGoose === true; const forceDeepReasoning = request.body?.force_deep_reasoning === true || request.body?.forceDeepReasoning === true;
if (!requestId) { if (!requestId) {
response.status(400).json({ message: '缺少 request_id' }); response.status(400).json({ message: '缺少 request_id' });
return; return;
@@ -111,7 +111,7 @@ export function createPostAgentRunsHandler({
userMessage, userMessage,
toolMode, toolMode,
taskType, taskType,
...(forceGoose ? { forceGoose: true } : {}), ...(forceDeepReasoning ? { forceDeepReasoning: true } : {}),
}); });
response.status(202).json({ run }); response.status(202).json({ run });
} catch (err) { } catch (err) {
+5 -5
View File
@@ -100,7 +100,7 @@ test('POST /agent/runs creates a run and returns 202', async () => {
]); ]);
}); });
test('POST /agent/runs forwards force_goose to the run gateway', async () => { test('POST /agent/runs forwards deep reasoning flag to the run gateway', async () => {
const created = []; const created = [];
const handler = createPostAgentRunsHandler({ const handler = createPostAgentRunsHandler({
userAuth: { userAuth: {
@@ -111,7 +111,7 @@ test('POST /agent/runs forwards force_goose to the run gateway', async () => {
agentRunGateway: { agentRunGateway: {
async createRun(userId, payload) { async createRun(userId, payload) {
created.push({ userId, payload }); created.push({ userId, payload });
return { id: 'run-goose', status: 'queued' }; return { id: 'run-deep', status: 'queued' };
}, },
}, },
}); });
@@ -121,16 +121,16 @@ test('POST /agent/runs forwards force_goose to the run gateway', async () => {
{ {
currentUser: { id: 'user-1' }, currentUser: { id: 'user-1' },
body: { body: {
request_id: 'req-goose', request_id: 'req-deep',
user_message: { role: 'user', content: [{ type: 'text', text: 'hello' }] }, user_message: { role: 'user', content: [{ type: 'text', text: 'hello' }] },
force_goose: true, force_deep_reasoning: true,
}, },
}, },
res, res,
); );
assert.equal(res.statusCode, 202); assert.equal(res.statusCode, 202);
assert.equal(created[0].payload.forceGoose, true); assert.equal(created[0].payload.forceDeepReasoning, true);
}); });
test('POST /agent/runs accepts explicit code tool mode and task type', async () => { test('POST /agent/runs accepts explicit code tool mode and task type', async () => {
+4 -4
View File
@@ -12,7 +12,7 @@ export function isDirectChatSessionId(sessionId) {
return String(sessionId ?? '').startsWith(DIRECT_CHAT_SESSION_PREFIX); return String(sessionId ?? '').startsWith(DIRECT_CHAT_SESSION_PREFIX);
} }
const GOOSE_REQUIRED_PATTERNS = [ const TASK_EXECUTION_REQUIRED_PATTERNS = [
/\b(public\/[^\s"'<>]+\.html)\b/i, /\b(public\/[^\s"'<>]+\.html)\b/i,
/\b(html|h5|web\s?page|landing\s?page|microsite|docx|word)\b/i, /\b(html|h5|web\s?page|landing\s?page|microsite|docx|word)\b/i,
/\b(write|create|generate|publish|download|export|save|edit)\b.{0,40}\b(file|page|html|docx|word|asset)\b/i, /\b(write|create|generate|publish|download|export|save|edit)\b.{0,40}\b(file|page|html|docx|word|asset)\b/i,
@@ -99,9 +99,9 @@ function isTextOnlyUserMessage(message) {
}); });
} }
function requiresGooseExecution(message) { function requiresTaskExecution(message) {
const text = `${messageText(message)}\n${assistantFacingText(message)}`.trim(); const text = `${messageText(message)}\n${assistantFacingText(message)}`.trim();
return GOOSE_REQUIRED_PATTERNS.some((pattern) => pattern.test(text)); return TASK_EXECUTION_REQUIRED_PATTERNS.some((pattern) => pattern.test(text));
} }
function renderMemoryLines(memories) { function renderMemoryLines(memories) {
@@ -181,7 +181,7 @@ export function createDirectChatService({
if (toolMode !== 'chat') return false; if (toolMode !== 'chat') return false;
if (sessionId && !isDirectChatSessionId(sessionId)) return false; if (sessionId && !isDirectChatSessionId(sessionId)) return false;
if (!isTextOnlyUserMessage(userMessage)) return false; if (!isTextOnlyUserMessage(userMessage)) return false;
if (requiresGooseExecution(userMessage)) return false; if (requiresTaskExecution(userMessage)) return false;
return Boolean(userAuth && llmProviderService && sessionSnapshotService); return Boolean(userAuth && llmProviderService && sessionSnapshotService);
} }
+1 -1
View File
@@ -185,7 +185,7 @@ test('direct chat rejects non-text messages', () => {
}), false); }), false);
}); });
test('direct chat rejects page generation tasks so goose can execute them', () => { test('direct chat rejects page generation tasks so the execution backend can handle them', () => {
const service = createDirectChatService({ const service = createDirectChatService({
enabled: true, enabled: true,
userAuth: {}, userAuth: {},
+13 -7
View File
@@ -115,7 +115,7 @@ function withAgentRunValidationMetadata(
function prepareAgentRunUserMessage(message: Message, options: AgentRunCreateOptions): Message { function prepareAgentRunUserMessage(message: Message, options: AgentRunCreateOptions): Message {
const normalized = normalizeUserMessageForApi(message); const normalized = normalizeUserMessageForApi(message);
const withValidation = withAgentRunValidationMetadata(normalized, options.validation); const withValidation = withAgentRunValidationMetadata(normalized, options.validation);
const withRunMetadata = options.forceGoose const withRunMetadata = options.forceDeepReasoning
? { ? {
...withValidation, ...withValidation,
metadata: { metadata: {
@@ -127,7 +127,7 @@ function prepareAgentRunUserMessage(message: Message, options: AgentRunCreateOpt
? withValidation.metadata.memindRun ? withValidation.metadata.memindRun
: {} : {}
), ),
forceGoose: true, forceDeepReasoning: true,
}, },
}, },
} }
@@ -223,17 +223,23 @@ function normalizeMindSpaceUploadError(error: unknown, file: File): Error {
function sanitizeUserFacingErrorMessage(message: string) { function sanitizeUserFacingErrorMessage(message: string) {
const normalized = String(message ?? '').trim(); const normalized = String(message ?? '').trim();
const serviceName = [103, 111, 111, 115, 101]
.map((code) => String.fromCharCode(code))
.join('');
const servicePattern = new RegExp(`${serviceName}d?`, 'i');
if (!normalized) return normalized; if (!normalized) return normalized;
if (!/goose|goosed/i.test(normalized)) return normalized; if (!servicePattern.test(normalized)) return normalized;
if (/超时|timeout/i.test(normalized)) { if (/超时|timeout/i.test(normalized)) {
return '后端连接超时,请确认后端服务正常后重试'; return '后端连接超时,请确认后端服务正常后重试';
} }
if (/不可用|连接失败|failed to fetch|networkerror|fetch failed|upstream|econn|enotfound/i.test(normalized)) { if (/不可用|连接失败|failed to fetch|networkerror|fetch failed|upstream|econn|enotfound/i.test(normalized)) {
return '后端连接失败,请稍后重试'; return '后端连接失败,请稍后重试';
} }
const serviceProcessPattern = new RegExp(`\\b${serviceName}d\\b`, 'gi');
const servicePatternGlobal = new RegExp(`\\b${serviceName}\\b`, 'gi');
return normalized return normalized
.replace(/\bgoosed\b/gi, '后端服务') .replace(serviceProcessPattern, '后端服务')
.replace(/\bgoose\b/gi, '后端'); .replace(servicePatternGlobal, '后端');
} }
async function parseErrorResponse(res: Response): Promise<{ async function parseErrorResponse(res: Response): Promise<{
@@ -2290,7 +2296,7 @@ export async function deleteLlmProviderKey(keyId: string): Promise<void> {
await portalFetch(`/admin-api/llm-providers/keys/${keyId}`, { method: 'DELETE' }); await portalFetch(`/admin-api/llm-providers/keys/${keyId}`, { method: 'DELETE' });
} }
export async function syncLlmProviderToGoosed(): Promise<{ synced: boolean }> { export async function syncLlmProviderToRuntime(): Promise<{ synced: boolean }> {
return portalFetch('/admin-api/llm-providers/sync', { method: 'POST' }); return portalFetch('/admin-api/llm-providers/sync', { method: 'POST' });
} }
@@ -2458,7 +2464,7 @@ export async function createAgentRun(
user_message: userMessagePayload, user_message: userMessagePayload,
...(options.toolMode ? { tool_mode: options.toolMode } : {}), ...(options.toolMode ? { tool_mode: options.toolMode } : {}),
...(options.taskType ? { task_type: options.taskType } : {}), ...(options.taskType ? { task_type: options.taskType } : {}),
...(options.forceGoose ? { force_goose: true } : {}), ...(options.forceDeepReasoning ? { force_deep_reasoning: true } : {}),
}), }),
}, },
{ timeoutMs: AGENT_CONNECT_TIMEOUT_MS }, { timeoutMs: AGENT_CONNECT_TIMEOUT_MS },
+9 -9
View File
@@ -144,7 +144,7 @@ export function ChatPanel({
text: string, text: string,
imageUrls?: string[], imageUrls?: string[],
previewImageUrls?: string[], previewImageUrls?: string[],
options?: { messageId?: string; forceGoose?: boolean }, options?: { messageId?: string; forceDeepReasoning?: boolean },
) => void | Promise<void>; ) => void | Promise<void>;
onUploadImage?: ( onUploadImage?: (
file: File, file: File,
@@ -167,7 +167,7 @@ export function ChatPanel({
const [pageSource, setPageSource] = useState<Message | null>(null); const [pageSource, setPageSource] = useState<Message | null>(null);
const [sharePreviewSource, setSharePreviewSource] = useState<Message | null>(null); const [sharePreviewSource, setSharePreviewSource] = useState<Message | null>(null);
const [voiceNotice, setVoiceNotice] = useState<string | null>(null); const [voiceNotice, setVoiceNotice] = useState<string | null>(null);
const [forceGoose, setForceGoose] = useState(false); const [forceDeepReasoning, setForceDeepReasoning] = useState(false);
const [pendingImages, setPendingImages] = useState<PendingChatImage[]>([]); const [pendingImages, setPendingImages] = useState<PendingChatImage[]>([]);
const [uploadingImage, setUploadingImage] = useState(false); const [uploadingImage, setUploadingImage] = useState(false);
const [imageError, setImageError] = useState<string | null>(null); const [imageError, setImageError] = useState<string | null>(null);
@@ -517,7 +517,7 @@ export function ChatPanel({
const previewImagesToSend = imagesToSend; const previewImagesToSend = imagesToSend;
await onSubmit(trimmed, imagesToSend, previewImagesToSend, { await onSubmit(trimmed, imagesToSend, previewImagesToSend, {
messageId: outgoingMessageId, messageId: outgoingMessageId,
forceGoose, forceDeepReasoning,
}); });
uploadedImages.forEach(revokePendingImage); uploadedImages.forEach(revokePendingImage);
setPendingImages([]); setPendingImages([]);
@@ -534,7 +534,7 @@ export function ChatPanel({
} }
setUploadingImage(false); setUploadingImage(false);
suppressVoiceUpdateRef.current = false; suppressVoiceUpdateRef.current = false;
}, [forceGoose, input, onSubmit, onUploadImage, pendingImages, revokePendingImage, voiceDisabled, voiceRecording]); }, [forceDeepReasoning, input, onSubmit, onUploadImage, pendingImages, revokePendingImage, voiceDisabled, voiceRecording]);
const handleSubmit = useCallback(async () => { const handleSubmit = useCallback(async () => {
await submitText(); await submitText();
@@ -828,16 +828,16 @@ export function ChatPanel({
)} )}
{!showHomeWelcome && ( {!showHomeWelcome && (
<label <label
className={`chat-force-goose-toggle${forceGoose ? ' is-active' : ''}`} className={`chat-deep-reasoning-toggle${forceDeepReasoning ? ' is-active' : ''}`}
title="勾选后,本轮消息强制交给 Goose 执行" title="勾选后,本轮消息使用深度推理完成"
> >
<input <input
type="checkbox" type="checkbox"
checked={forceGoose} checked={forceDeepReasoning}
disabled={voiceDisabled} disabled={voiceDisabled}
onChange={(event) => setForceGoose(event.target.checked)} onChange={(event) => setForceDeepReasoning(event.target.checked)}
/> />
<span> Goose</span> <span></span>
</label> </label>
)} )}
{chatState === 'streaming' ? ( {chatState === 'streaming' ? (
+1 -1
View File
@@ -543,7 +543,7 @@ export function ChatView({
onSubmit={(text, imageUrls, previewImageUrls, options) => onSubmit={(text, imageUrls, previewImageUrls, options) =>
void submit( void submit(
text, text,
{ messageId: options?.messageId, forceGoose: options?.forceGoose }, { messageId: options?.messageId, forceDeepReasoning: options?.forceDeepReasoning },
imageUrls, imageUrls,
previewImageUrls, previewImageUrls,
) )
+2 -2
View File
@@ -142,7 +142,7 @@ export function SpaceChatPanel({
chatBridge chatBridge
? void submit( ? void submit(
text, text,
{ ...context, messageId: options?.messageId, forceGoose: options?.forceGoose }, { ...context, messageId: options?.messageId, forceDeepReasoning: options?.forceDeepReasoning },
imageUrls, imageUrls,
previewImageUrls, previewImageUrls,
) )
@@ -151,7 +151,7 @@ export function SpaceChatPanel({
{ {
mindspaceContext: context, mindspaceContext: context,
messageId: options?.messageId, messageId: options?.messageId,
forceGoose: options?.forceGoose, forceDeepReasoning: options?.forceDeepReasoning,
}, },
imageUrls, imageUrls,
previewImageUrls, previewImageUrls,
+4 -4
View File
@@ -599,7 +599,7 @@ export function useTKMindChat(
}, [resolveChatImageUploadCategoryId]); }, [resolveChatImageUploadCategoryId]);
const syncSessionMessages = useCallback(async (sessionId: string) => { const syncSessionMessages = useCallback(async (sessionId: string) => {
// REGRESSION GUARD: Finish may arrive before Goose persists assistant turns. // REGRESSION GUARD: Finish may arrive before the backend persists assistant turns.
// Never blind-replace with server snapshot — merge + retry (see chat-finish-sync.mjs). // Never blind-replace with server snapshot — merge + retry (see chat-finish-sync.mjs).
try { try {
const localMessages = messagesRef.current; const localMessages = messagesRef.current;
@@ -804,7 +804,7 @@ export function useTKMindChat(
} else if (rid && event.request_ids.includes(rid)) { } else if (rid && event.request_ids.includes(rid)) {
clearActiveRequestMissingTimer(); clearActiveRequestMissingTimer();
} else if (rid && !event.request_ids.includes(rid)) { } else if (rid && !event.request_ids.includes(rid)) {
// Goose can briefly report no active request between tool phases. // The backend can briefly report no active request between tool phases.
// Confirm the absence before turning the UI idle, otherwise MindSpace // Confirm the absence before turning the UI idle, otherwise MindSpace
// refreshes the page while tools are still mutating it. // refreshes the page while tools are still mutating it.
if (!activeRequestMissingTimerRef.current) { if (!activeRequestMissingTimerRef.current) {
@@ -1136,7 +1136,7 @@ export function useTKMindChat(
const submit = useCallback( const submit = useCallback(
async ( async (
text: string, text: string,
options?: { mindspaceContext?: MindSpaceChatContext; messageId?: string; forceGoose?: boolean }, options?: { mindspaceContext?: MindSpaceChatContext; messageId?: string; forceDeepReasoning?: boolean },
imageUrls?: string[], imageUrls?: string[],
previewImageUrls?: string[], previewImageUrls?: string[],
) => { ) => {
@@ -1194,7 +1194,7 @@ export function useTKMindChat(
userMessage, userMessage,
{ {
...runOptions, ...runOptions,
...(options?.forceGoose ? { forceGoose: true } : {}), ...(options?.forceDeepReasoning ? { forceDeepReasoning: true } : {}),
}, },
); );
const finishedRun = const finishedRun =
+7 -7
View File
@@ -2702,7 +2702,7 @@ body,
flex: 0 0 auto; flex: 0 0 auto;
} }
.chat-force-goose-toggle { .chat-deep-reasoning-toggle {
display: inline-flex; display: inline-flex;
flex: 0 0 auto; flex: 0 0 auto;
align-items: center; align-items: center;
@@ -2722,26 +2722,26 @@ body,
white-space: nowrap; white-space: nowrap;
} }
.chat-force-goose-toggle:hover { .chat-deep-reasoning-toggle:hover {
color: var(--color-text-primary); color: var(--color-text-primary);
border-color: var(--color-border-strong); border-color: var(--color-border-strong);
background: var(--color-bg-hover); background: var(--color-bg-hover);
} }
.chat-force-goose-toggle input { .chat-deep-reasoning-toggle input {
width: 14px; width: 14px;
height: 14px; height: 14px;
margin: 0; margin: 0;
accent-color: #79b7ff; accent-color: #79b7ff;
} }
.chat-force-goose-toggle.is-active { .chat-deep-reasoning-toggle.is-active {
color: #eff6ff; color: #eff6ff;
border-color: rgba(121, 183, 255, 0.6); border-color: rgba(121, 183, 255, 0.6);
background: rgba(121, 183, 255, 0.14); background: rgba(121, 183, 255, 0.14);
} }
.chat-force-goose-toggle:has(input:disabled) { .chat-deep-reasoning-toggle:has(input:disabled) {
cursor: not-allowed; cursor: not-allowed;
opacity: 0.45; opacity: 0.45;
} }
@@ -9294,12 +9294,12 @@ body,
border-radius: 12px; border-radius: 12px;
} }
.chat-force-goose-toggle { .chat-deep-reasoning-toggle {
min-height: 36px; min-height: 36px;
padding-inline: 8px; padding-inline: 8px;
} }
.chat-force-goose-toggle span { .chat-deep-reasoning-toggle span {
max-width: 56px; max-width: 56px;
white-space: normal; white-space: normal;
} }
+7 -7
View File
@@ -3,7 +3,7 @@ import type { MindSpaceChatContext } from '../types';
export type AgentRunCreateOptions = { export type AgentRunCreateOptions = {
toolMode?: 'chat' | 'code'; toolMode?: 'chat' | 'code';
taskType?: string | null; taskType?: string | null;
forceGoose?: boolean; forceDeepReasoning?: boolean;
validation?: AgentRunValidation | null; validation?: AgentRunValidation | null;
validationInstruction?: string | null; validationInstruction?: string | null;
}; };
@@ -53,7 +53,7 @@ const CODE_TASK_PATTERNS = [
/(代码|仓库|项目|文件|组件|接口).{0,12}(修改|修复|重构|调试|实现|新增|编写|更新)/, /(代码|仓库|项目|文件|组件|接口).{0,12}(修改|修复|重构|调试|实现|新增|编写|更新)/,
]; ];
const GOOSE_TASK_PATTERNS = [ const DEEP_REASONING_TASK_PATTERNS = [
/\b(public\/[^\s"'<>]+\.html)\b/i, /\b(public\/[^\s"'<>]+\.html)\b/i,
/\b(html|h5|web\s?page|landing\s?page|microsite|docx|word)\b/i, /\b(html|h5|web\s?page|landing\s?page|microsite|docx|word)\b/i,
/\b(write|create|generate|publish|download|export|save|edit)\b.{0,40}\b(file|page|html|docx|word|asset)\b/i, /\b(write|create|generate|publish|download|export|save|edit)\b.{0,40}\b(file|page|html|docx|word|asset)\b/i,
@@ -236,16 +236,16 @@ export function resolveAgentRunOptions(
} = {}, } = {},
): AgentRunCreateOptions { ): AgentRunCreateOptions {
const normalizedText = String(text ?? '').trim(); const normalizedText = String(text ?? '').trim();
const shouldForceGoose = const shouldUseDeepReasoning =
forceCode || GOOSE_TASK_PATTERNS.some((pattern) => pattern.test(normalizedText)); forceCode || DEEP_REASONING_TASK_PATTERNS.some((pattern) => pattern.test(normalizedText));
if (!agentCodeRunsEnabledForUser(userId)) { if (!agentCodeRunsEnabledForUser(userId)) {
return shouldForceGoose ? { forceGoose: true, taskType } : {}; return shouldUseDeepReasoning ? { forceDeepReasoning: true, taskType } : {};
} }
const shouldUseCode = forceCode || (allowAutodetect && CODE_TASK_PATTERNS.some((pattern) => pattern.test(normalizedText))); const shouldUseCode = forceCode || (allowAutodetect && CODE_TASK_PATTERNS.some((pattern) => pattern.test(normalizedText)));
if (!shouldUseCode) { if (!shouldUseCode) {
return shouldForceGoose ? { forceGoose: true, taskType } : {}; return shouldUseDeepReasoning ? { forceDeepReasoning: true, taskType } : {};
} }
const normalizedRequestId = requestId ?? crypto.randomUUID(); const normalizedRequestId = requestId ?? crypto.randomUUID();
const receipt = buildAgentRunValidationReceipt(normalizedRequestId); const receipt = buildAgentRunValidationReceipt(normalizedRequestId);
@@ -259,7 +259,7 @@ export function resolveAgentRunOptions(
return { return {
toolMode: 'code', toolMode: 'code',
taskType, taskType,
forceGoose: true, forceDeepReasoning: true,
validation: mergeAgentRunValidationFiles(receipt.validation, taskValidation.validation), validation: mergeAgentRunValidationFiles(receipt.validation, taskValidation.validation),
validationInstruction: `${receipt.instruction}${taskValidation.instruction}`, validationInstruction: `${receipt.instruction}${taskValidation.instruction}`,
}; };