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 属性
This commit is contained in:
parent
beb01cb455
commit
c551f5ae81
|
|
@ -6,10 +6,10 @@ import * as React from 'react';
|
|||
|
||||
import { Box, Text } from '@anthropic/ink';
|
||||
|
||||
import type { GoalState } from '../../types/logs.js';
|
||||
import { Select } from '../../components/CustomSelect/index.js';
|
||||
import { PermissionDialog } from '../../components/permissions/PermissionDialog.js';
|
||||
import { formatGoalElapsed, formatGoalStatusLabel } from '../../services/goal/goalState.js';
|
||||
import type { GoalState } from 'src/types/logs.js';
|
||||
import { Select } from 'src/components/CustomSelect/index.js';
|
||||
import { PermissionDialog } from 'src/components/permissions/PermissionDialog.js';
|
||||
import { formatGoalElapsed, formatGoalStatusLabel } from 'src/services/goal/goalState.js';
|
||||
|
||||
type Props = {
|
||||
currentGoal: GoalState;
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
import * as React from 'react';
|
||||
|
||||
import type { LocalJSXCommandContext } from '../../commands.js';
|
||||
import type { LocalJSXCommandOnDone } from '../../types/command.js';
|
||||
import type { LocalJSXCommandContext } from 'src/commands.js';
|
||||
import type { LocalJSXCommandOnDone } from 'src/types/command.js';
|
||||
import {
|
||||
clearGoal,
|
||||
completeGoal,
|
||||
|
|
@ -26,8 +26,8 @@ import {
|
|||
pauseGoal,
|
||||
resumeGoal,
|
||||
setGoal,
|
||||
} from '../../services/goal/goalState.js';
|
||||
import { persistCurrentGoal, persistGoalClear } from '../../services/goal/goalStorage.js';
|
||||
} from 'src/services/goal/goalState.js';
|
||||
import { persistCurrentGoal, persistGoalClear } from 'src/services/goal/goalStorage.js';
|
||||
import { GoalReplaceConfirmDialog } from './GoalReplaceConfirmDialog.js';
|
||||
|
||||
const MAX_OBJECTIVE_CHARS = 4000;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { Command } from '../../commands.js'
|
||||
import type { Command } from 'src/commands.js'
|
||||
|
||||
const goal = {
|
||||
type: 'local-jsx',
|
||||
|
|
@ -6,7 +6,7 @@ const goal = {
|
|||
description:
|
||||
'Set or view a persistent goal that drives auto-continuation across turns',
|
||||
argumentHint: '[<objective> | status | clear | pause | resume | complete]',
|
||||
bridgeSafe: true,
|
||||
bridgeSafe: false,
|
||||
load: () => import('./goal.js'),
|
||||
} satisfies Command
|
||||
|
||||
|
|
|
|||
|
|
@ -21,22 +21,22 @@
|
|||
*/
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import { logForDebugging } from 'src/utils/debug.js'
|
||||
import {
|
||||
getGoal,
|
||||
incrementGoalTurns,
|
||||
MAX_GOAL_TURNS,
|
||||
} from '../services/goal/goalState.js'
|
||||
} from 'src/services/goal/goalState.js'
|
||||
import { persistCurrentGoal } from 'src/services/goal/goalStorage.js'
|
||||
import {
|
||||
buildBudgetLimitPrompt,
|
||||
buildContinuationPrompt,
|
||||
} from 'src/services/goal/prompts.js'
|
||||
import { enqueue } from 'src/utils/messageQueueManager.js'
|
||||
|
||||
function hookLog(msg: string): void {
|
||||
logForDebugging(`[goal] hook: ${msg}`)
|
||||
}
|
||||
import { persistCurrentGoal } from '../services/goal/goalStorage.js'
|
||||
import {
|
||||
buildBudgetLimitPrompt,
|
||||
buildContinuationPrompt,
|
||||
} from '../services/goal/prompts.js'
|
||||
import { enqueue } from '../utils/messageQueueManager.js'
|
||||
|
||||
export type UseGoalContinuationOpts = {
|
||||
isLoading: boolean
|
||||
|
|
@ -94,7 +94,7 @@ export function useGoalContinuation(opts: UseGoalContinuationOpts): void {
|
|||
mode: 'prompt',
|
||||
priority: 'later',
|
||||
isMeta: true,
|
||||
origin: { kind: 'goal-budget-limit' } as unknown as string,
|
||||
origin: 'goal-budget-limit',
|
||||
skipSlashCommands: true,
|
||||
})
|
||||
return
|
||||
|
|
@ -127,7 +127,7 @@ export function useGoalContinuation(opts: UseGoalContinuationOpts): void {
|
|||
mode: 'prompt',
|
||||
priority: 'later',
|
||||
isMeta: true,
|
||||
origin: { kind: 'goal-continuation' } as unknown as string,
|
||||
origin: 'goal-continuation',
|
||||
skipSlashCommands: true,
|
||||
})
|
||||
}, [
|
||||
|
|
|
|||
|
|
@ -2544,7 +2544,10 @@ export function REPL({
|
|||
|
||||
// Ctrl+C during an active goal turn pauses the goal so the
|
||||
// continuation loop stops. The user can /goal resume to continue later.
|
||||
if (feature('GOAL')) {
|
||||
// Guard: only pause when a query is actually in flight. onCancel() is
|
||||
// also called from the restore/edit flow (idle), and pausing then would
|
||||
// incorrectly stop the next continuation.
|
||||
if (feature('GOAL') && queryGuard.getSnapshot()) {
|
||||
const { getGoal, pauseGoal } =
|
||||
require('../services/goal/goalState.js') as typeof import('../services/goal/goalState.js');
|
||||
const { persistCurrentGoal } =
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { feature } from 'bun:bundle';
|
||||
import type { UUID } from 'crypto';
|
||||
import { dirname } from 'path';
|
||||
import React from 'react';
|
||||
import { useTerminalSize } from 'src/hooks/useTerminalSize.js';
|
||||
|
|
@ -296,6 +297,14 @@ export function ResumeConversation({
|
|||
}
|
||||
}
|
||||
|
||||
if (feature('GOAL') && log.goal && result.sessionId) {
|
||||
const { hydrateGoalFromTranscript } =
|
||||
require('src/services/goal/goalStorage.js') as typeof import('src/services/goal/goalStorage.js');
|
||||
const goalsMap = new Map<UUID, import('src/types/logs.js').GoalState>();
|
||||
goalsMap.set(result.sessionId, log.goal);
|
||||
hydrateGoalFromTranscript(goalsMap, result.sessionId);
|
||||
}
|
||||
|
||||
if (feature('CONTEXT_COLLAPSE')) {
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
(
|
||||
|
|
|
|||
|
|
@ -91,12 +91,7 @@ export function resumeGoal(sessionId?: string): GoalState | null {
|
|||
const id = resolveSessionId(sessionId)
|
||||
const goal = goals.get(id)
|
||||
if (!goal) return null
|
||||
if (
|
||||
goal.status !== 'paused' &&
|
||||
goal.status !== 'blocked' &&
|
||||
goal.status !== 'budget_limited' &&
|
||||
goal.status !== 'usage_limited'
|
||||
) {
|
||||
if (goal.status !== 'paused') {
|
||||
return null
|
||||
}
|
||||
const now = Date.now()
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ export function buildGoalContextBlock(goal: GoalState): string {
|
|||
const elapsedMs = getActiveElapsedMs(goal)
|
||||
const budget =
|
||||
goal.tokenBudget !== null
|
||||
? ` | budget: ${goal.tokensUsed}/${goal.tokenBudget}`
|
||||
? ` budget="${goal.tokenBudget}"`
|
||||
: ''
|
||||
|
||||
return [
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user