feat: /goal命令能力支持,参考codex实现 (#1261)

* feat: /goal命令能力支持,参考codex实现

* fix: 修复promp和提示词不一致的问题

* fix: 修复 goal 功能多项 AI 审查问题

- prompt 中 update 行为描述与运行时不一致(no-op → error)
- src/commands/goal/ 使用相对路径导入,改为 src/* 别名
- /goal 命令标记 bridgeSafe 但含交互式对话框,改为 false
- useGoalContinuation 中 origin 使用 as unknown as string 强转,改为直接传字符串
- ResumeConversation 路径缺少 goal hydration,补齐恢复逻辑
- onCancel 在非查询状态下误暂停 goal,加 queryGuard 守卫
- resumeGoal 允许从终态恢复,收紧为仅允许 paused 状态
- buildGoalContextBlock 生成畸形 XML 属性,改为合法 budget 属性

* fix: 修复剩余AI审查的问题

* fix: 防止goal状态丢失

* fix: 修复Biome规范错误问题

* fix: 修复部分情况下goal无法启动的问题

* fix: 增加断网后状态默认设置为PAUSE机制、完成暂停-恢复状态切换,且正常进行前端渲染。设置达到max turn后处理逻辑。

* fix: 修复终端异常断开情况,resume续跑;修复用户消息排队信息被goal输出信息覆盖的问题。

* fix: apply biome formatting to pass CI lint check

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: skip slash command echo in setUserInputOnProcessing to prevent UI flash

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: moyu <moyu@kingsoft.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
moy16
2026-06-14 10:44:10 +08:00
committed by GitHub
parent 5bfe6fa590
commit 3e3e1de81b
28 changed files with 2248 additions and 30 deletions

View File

@@ -45,6 +45,7 @@ import { isVimModeEnabled } from './PromptInput/utils.js';
import { computeHitRate, tokenSignature } from '../utils/cacheStats.js';
import { onResponse as cacheOnResponse, getCacheStatsState, initCacheStatsState } from '../utils/cacheStatsState.js';
import { BuiltinStatusLine } from './BuiltinStatusLine.js';
import { formatTokens } from 'src/utils/format.js';
// ---------------------------------------------------------------------------
// CachePill — cache hit-rate + 1-hour TTL countdown pill
@@ -156,6 +157,51 @@ function CachePill({ messages }: CachePillProps): React.ReactNode {
);
}
function GoalPill(): React.ReactNode {
if (!feature('GOAL')) return null;
const { getGoal, formatGoalStatusLabel } =
require('../services/goal/goalState.js') as typeof import('../services/goal/goalState.js');
const goal = getGoal();
if (!goal) return null;
const truncatedObj = goal.objective.length > 30 ? `${goal.objective.slice(0, 27)}` : goal.objective;
const budget =
goal.tokenBudget !== null
? `${formatTokens(goal.tokensUsed)}/${formatTokens(goal.tokenBudget)}`
: formatTokens(goal.tokensUsed);
const statusLabel = formatGoalStatusLabel(goal.status);
let statusNode: React.ReactNode;
switch (goal.status) {
case 'active':
statusNode = <Text color="ansi:green">{statusLabel}</Text>;
break;
case 'paused':
case 'budget_limited':
case 'usage_limited':
statusNode = <Text color="ansi:yellow">{statusLabel}</Text>;
break;
case 'blocked':
statusNode = <Text color="ansi:red">{statusLabel}</Text>;
break;
case 'complete':
statusNode = <Text color="ansi:cyan">{statusLabel}</Text>;
break;
default:
statusNode = <Text>{statusLabel}</Text>;
}
return (
<Text>
{statusNode}
<Text dimColor>{' · '}</Text>
<Text dimColor>{truncatedObj}</Text>
<Text dimColor>{' · '}</Text>
<Text>{budget}</Text>
</Text>
);
}
export function statusLineShouldDisplay(settings: ReadonlySettings): boolean {
// Assistant mode: statusline fields (model, permission mode, cwd) reflect the
// REPL/daemon process, not what the agent child is actually running. Hide it.
@@ -519,6 +565,7 @@ function StatusLineInner({ messagesRef, lastAssistantMessageId, vimMode }: Props
totalCostUsd={getTotalCost()}
rateLimits={builtinRateLimits}
/>
<GoalPill />
<CachePill messages={messagesRef.current} />
</Box>
)}