Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 75 additions & 24 deletions src/commands/ultraplan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,40 @@ import { ALL_MODEL_CONFIGS } from '../utils/model/configs.js';
import { updateTaskState } from '../utils/task/framework.js';
import { archiveRemoteSession, teleportToRemote } from '../utils/teleport.js';
import { pollForApprovedExitPlanMode, UltraplanPollError } from '../utils/ultraplan/ccrSession.js';
import {
getPromptText,
getDialogConfig,
getPromptIdentifier,
type PromptIdentifier
} from '../utils/ultraplan/prompt.js';
import { registerCleanup } from '../utils/cleanupRegistry.js';


// TODO(prod-hardening): OAuth token may go stale over the 30min poll;
// consider refresh.

// Multi-agent exploration is slow; 30min timeout.
/**
* Multi-agent exploration is slow; 30min timeout.
*
* @deprecated use getUltraplanTimeoutMs()
*/
const ULTRAPLAN_TIMEOUT_MS = 30 * 60 * 1000;

export const CCR_TERMS_URL = 'https://code.claude.com/docs/en/claude-code-on-the-web';

export function getUltraplanTimeoutMs(): number {
return getFeatureValue_CACHED_MAY_BE_STALE('tengu_ultraplan_timeout_seconds', 1800) * 1000
}

/**
* 是否启用 ultraplan, 默认启用
*
* @returns
*/
export function isUltraplanEnabled(): boolean {
return getFeatureValue_CACHED_MAY_BE_STALE<{enabled: boolean} | null>('tengu_ultraplan_config', { enabled: true })?.enabled === true
}
Comment on lines +53 to +60
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's see the full context of the file and the specific lines mentioned
head -100 src/commands/ultraplan.tsx | tail -60

Repository: claude-code-best/claude-code

Length of output: 2968


🏁 Script executed:

# Check lines around 522-523 where the function is used
sed -n '515,530p' src/commands/ultraplan.tsx

Repository: claude-code-best/claude-code

Length of output: 445


🏁 Script executed:

# Search for getFeatureValue_CACHED_MAY_BE_STALE definition and usage
rg "getFeatureValue_CACHED_MAY_BE_STALE" -A 3 -B 3

Repository: claude-code-best/claude-code

Length of output: 50385


🏁 Script executed:

# Search for feature flag imports to understand the standard pattern being used
rg "import.*feature.*from" --type ts --type tsx

Repository: claude-code-best/claude-code

Length of output: 99


🏁 Script executed:

# Check what tools are used for feature flag management
rg "feature\(" -B 2 -A 2 --type ts --type tsx | head -50

Repository: claude-code-best/claude-code

Length of output: 99


🏁 Script executed:

# Search for any feature flag references to ultraplan
rg "ultraplan|ULTRAPLAN" --type ts -i | grep -i "feature\|gate" | head -20

Repository: claude-code-best/claude-code

Length of output: 1564


🏁 Script executed:

# Check how getUltraplanTimeoutMs is implemented (similar function nearby)
sed -n '40,60p' src/commands/ultraplan.tsx

Repository: claude-code-best/claude-code

Length of output: 688


🏁 Script executed:

# Search for definition of getFeatureValue_CACHED_MAY_BE_STALE to understand its purpose
rg "export.*function.*getFeatureValue_CACHED_MAY_BE_STALE" -A 5

Repository: claude-code-best/claude-code

Length of output: 488


🏁 Script executed:

# Check how the ultraplan command is registered using the feature() binary gate
sed -n '520,530p' src/commands/ultraplan.tsx

Repository: claude-code-best/claude-code

Length of output: 382


🏁 Script executed:

# Look at how src/commands.ts handles the ultraplan feature gate
rg "const ultraplan = feature" -A 5 -B 5

Repository: claude-code-best/claude-code

Length of output: 1172


🏁 Script executed:

# Check similar patterns - how voice mode handles dual gating
rg "voiceModeEnabled\|voiceMode" src/voice/voiceModeEnabled.ts | head -20

Repository: claude-code-best/claude-code

Length of output: 54


🏁 Script executed:

# Check if there are other uses of isUltraplanEnabled in the command registration
grep -r "isUltraplanEnabled" src/ --include="*.ts" --include="*.tsx"

Repository: claude-code-best/claude-code

Length of output: 214


🏁 Script executed:

# Verify the pattern - check how voice mode combines feature() and getFeatureValue
sed -n '1,30p' src/voice/voiceModeEnabled.ts

Repository: claude-code-best/claude-code

Length of output: 1440


🏁 Script executed:

# Look at the full context to understand if isUltraplanEnabled is called from within the feature gate or outside
sed -n '515,535p' src/commands/ultraplan.tsx

Repository: claude-code-best/claude-code

Length of output: 445


Change the default value in isUltraplanEnabled() to fail-safe.

isUltraplanEnabled() defaults to { enabled: true }, which means if the GrowthBook cache is missing or stale, the command becomes available to everyone. For an opt-in rollout, the default must be null or false so the feature only appears when explicitly configured.

Suggested fix
 export function isUltraplanEnabled(): boolean {
-  return getFeatureValue_CACHED_MAY_BE_STALE<{enabled: boolean} | null>('tengu_ultraplan_config', { enabled: true })?.enabled === true
+  return getFeatureValue_CACHED_MAY_BE_STALE<{ enabled: boolean } | null>(
+    'tengu_ultraplan_config',
+    null,
+  )?.enabled === true;
 }

This follows the same pattern used elsewhere in the codebase for opt-in features (e.g., voice mode with tengu_amber_quartz_disabled kill-switch).

Also applies to: 523

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/ultraplan.tsx` around lines 53 - 60, The default in
isUltraplanEnabled() is currently { enabled: true } which makes the feature on
when the GrowthBook cache is missing; change the fallback passed to
getFeatureValue_CACHED_MAY_BE_STALE('tengu_ultraplan_config', ...) to a
fail-safe value (either null or { enabled: false }) so the function returns true
only when the feature is explicitly enabled. Update the call site in
isUltraplanEnabled() (and any duplicate at the other referenced location) to use
that fail-safe default and keep the rest of the boolean check (?.enabled ===
true) unchanged.


// CCR runs against the first-party API — use the canonical ID, not the
// provider-specific string getModelStrings() would return (which may be a
// Bedrock ARN or Vertex ID on the local CLI). Read at call time, not module
Expand Down Expand Up @@ -62,6 +87,7 @@ const DEFAULT_INSTRUCTIONS: string = (typeof _rawPrompt === 'string' ? _rawPromp
// so the override path is DCE'd from external builds).
// Shell-set env only, so top-level process.env read is fine
// — settings.env never injects this.
// @deprecated use buildUltraplanPrompt()
/* eslint-disable custom-rules/no-process-env-top-level, custom-rules/no-sync-fs -- ant-only dev override; eager top-level read is the point (crash at startup, not silently inside the slash-command try/catch) */
const ULTRAPLAN_INSTRUCTIONS: string =
process.env.USER_TYPE === 'ant' && process.env.ULTRAPLAN_PROMPT_FILE
Expand All @@ -73,12 +99,14 @@ const ULTRAPLAN_INSTRUCTIONS: string =
* Assemble the initial CCR user message. seedPlan and blurb stay outside the
* system-reminder so the browser renders them; scaffolding is hidden.
*/
export function buildUltraplanPrompt(blurb: string, seedPlan?: string): string {
export function buildUltraplanPrompt(blurb: string, seedPlan?: string, promptId?: PromptIdentifier): string {
const parts: string[] = [];
if (seedPlan) {
parts.push('Here is a draft plan to refine:', '', seedPlan, '');
}
parts.push(ULTRAPLAN_INSTRUCTIONS);
// parts.push(ULTRAPLAN_INSTRUCTIONS)
parts.push(getPromptText(promptId!));
Comment on lines +102 to +108
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Default promptId inside buildUltraplanPrompt().

The helper accepts promptId?, but Line 108 non-null asserts it before indexing the prompt table. Any caller that omits the id will throw instead of using the configured default prompt.

Suggested fix
 export function buildUltraplanPrompt(blurb: string, seedPlan?: string, promptId?: PromptIdentifier): string {
   const parts: string[] = [];
+  const resolvedPromptId = promptId ?? getPromptIdentifier();
   if (seedPlan) {
     parts.push('Here is a draft plan to refine:', '', seedPlan, '');
   }
-  parts.push(getPromptText(promptId!));
+  parts.push(getPromptText(resolvedPromptId));
 
   if (blurb) {
     parts.push('', blurb);
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function buildUltraplanPrompt(blurb: string, seedPlan?: string, promptId?: PromptIdentifier): string {
const parts: string[] = [];
if (seedPlan) {
parts.push('Here is a draft plan to refine:', '', seedPlan, '');
}
parts.push(ULTRAPLAN_INSTRUCTIONS);
// parts.push(ULTRAPLAN_INSTRUCTIONS)
parts.push(getPromptText(promptId!));
export function buildUltraplanPrompt(blurb: string, seedPlan?: string, promptId?: PromptIdentifier): string {
const parts: string[] = [];
const resolvedPromptId = promptId ?? getPromptIdentifier();
if (seedPlan) {
parts.push('Here is a draft plan to refine:', '', seedPlan, '');
}
// parts.push(ULTRAPLAN_INSTRUCTIONS)
parts.push(getPromptText(resolvedPromptId));
if (blurb) {
parts.push('', blurb);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/ultraplan.tsx` around lines 102 - 108, The function
buildUltraplanPrompt currently non‑null asserts promptId when calling
getPromptText(promptId!), which will throw if callers omit the optional
promptId; change this to use a fallback default instead—e.g. compute const
effectivePromptId = promptId ?? DEFAULT_PROMPT_ID (or call your configured
getter like getDefaultPromptId()) and call getPromptText(effectivePromptId) so
missing promptId uses the configured default instead of crashing.


if (blurb) {
parts.push('', blurb);
}
Expand All @@ -98,7 +126,7 @@ function startDetachedPoll(
try {
const { plan, rejectCount, executionTarget } = await pollForApprovedExitPlanMode(
sessionId,
ULTRAPLAN_TIMEOUT_MS,
getUltraplanTimeoutMs(),
phase => {
if (phase === 'needs_input') logEvent('tengu_ultraplan_awaiting_input', {});
updateTaskState<RemoteAgentTaskState>(taskId, setAppState, t => {
Expand Down Expand Up @@ -258,6 +286,7 @@ export async function stopUltraplan(
export async function launchUltraplan(opts: {
blurb: string;
seedPlan?: string;
promptIdentifier?: PromptIdentifier;
getAppState: () => AppState;
setAppState: (f: (prev: AppState) => AppState) => void;
signal: AbortSignal;
Expand All @@ -272,7 +301,7 @@ export async function launchUltraplan(opts: {
*/
onSessionReady?: (msg: string) => void;
}): Promise<string> {
const { blurb, seedPlan, getAppState, setAppState, signal, disconnectedBridge, onSessionReady } = opts;
const { blurb, seedPlan, promptIdentifier, getAppState, setAppState, signal, disconnectedBridge, onSessionReady } = opts;

const { ultraplanSessionUrl: active, ultraplanLaunching } = getAppState();
if (active || ultraplanLaunching) {
Expand All @@ -292,22 +321,24 @@ export async function launchUltraplan(opts: {
'Usage: /ultraplan \\<prompt\\>, or include "ultraplan" anywhere',
'in your prompt',
'',
'Advanced multi-agent plan mode with our most powerful model',
'(Opus). Runs in Claude Code on the web. When the plan is ready,',
'you can execute it in the web session or send it back here.',
'Terminal stays free while the remote plans.',
'Requires /login.',
// 'Advanced multi-agent plan mode with our most powerful model',
// '(Opus). Runs in Claude Code on the web. When the plan is ready,',
// 'you can execute it in the web session or send it back here.',
// 'Terminal stays free while the remote plans.',
// 'Requires /login.',
...getDialogConfig().usageBlurb,
'',
`Terms: ${CCR_TERMS_URL}`,
].join('\n');
}

// Set synchronously before the detached flow to prevent duplicate launches
// during the teleportToRemote window.
setAppState(prev => (prev.ultraplanLaunching ? prev : { ...prev, ultraplanLaunching: true }));
setAppState(prev => prev.ultraplanLaunching ? prev : { ...prev, ultraplanLaunching: true });
void launchDetached({
blurb,
seedPlan,
promptIdentifier,
getAppState,
setAppState,
signal,
Expand All @@ -319,56 +350,62 @@ export async function launchUltraplan(opts: {
async function launchDetached(opts: {
blurb: string;
seedPlan?: string;
promptIdentifier?: PromptIdentifier;
getAppState: () => AppState;
setAppState: (f: (prev: AppState) => AppState) => void;
signal: AbortSignal;
onSessionReady?: (msg: string) => void;
}): Promise<void> {
const { blurb, seedPlan, getAppState, setAppState, signal, onSessionReady } = opts;
const { blurb, seedPlan, promptIdentifier = getPromptIdentifier(), getAppState, setAppState, signal, onSessionReady } = opts;
// Hoisted so the catch block can archive the remote session if an error
// occurs after teleportToRemote succeeds (avoids 30min orphan).
let sessionId: string | undefined;
try {
const model = getUltraplanModel();
// const model = getUltraplanModel()

const eligibility = await checkRemoteAgentEligibility();
if (!eligibility.eligible) {
logEvent('tengu_ultraplan_create_failed', {
reason: 'precondition' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
precondition_errors: (eligibility as { errors: Array<{ type: string }> }).errors
precondition_errors: eligibility.errors
.map(e => e.type)
.join(',') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
});
const reasons = (eligibility as { errors: Array<{ type: string }> }).errors.map(formatPreconditionError).join('\n');
const reasons = eligibility.errors.map(formatPreconditionError).join('\n');
enqueuePendingNotification({
value: `ultraplan: cannot launch remote session —\n${reasons}`,
mode: 'task-notification',
});
return;
}

const prompt = buildUltraplanPrompt(blurb, seedPlan);
const prompt = buildUltraplanPrompt(blurb, seedPlan, promptIdentifier);
let bundleFailMsg: string | undefined;
let createFailMsg: string | undefined;
const session = await teleportToRemote({
initialMessage: prompt,
description: blurb || 'Refine local plan',
model,
// model,
permissionMode: 'plan',
ultraplan: true,
signal,
useDefaultEnvironment: true,
onBundleFail: msg => {
bundleFailMsg = msg;
},
});
onCreateFail: msg => {
createFailMsg = msg;
},
})
if (!session) {
let failMsg = bundleFailMsg ?? createFailMsg;
logEvent('tengu_ultraplan_create_failed', {
reason: (bundleFailMsg
? 'bundle_fail'
: 'teleport_null') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
: createFailMsg ? 'create_api_fail' : 'teleport_null') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
});
enqueuePendingNotification({
value: `ultraplan: session creation failed${bundleFailMsg ? ` — ${bundleFailMsg}` : ''}. See --debug for details.`,
value: `ultraplan: session creation failed${failMsg ? ` — ${failMsg}` : ''}. See --debug for details.`,
mode: 'task-notification',
});
return;
Expand All @@ -384,7 +421,8 @@ async function launchDetached(opts: {
onSessionReady?.(buildSessionReadyMessage(url));
logEvent('tengu_ultraplan_launched', {
has_seed_plan: Boolean(seedPlan),
model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
prompt_identifier: promptIdentifier as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
// model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
});
Comment on lines +424 to 426
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing comma causes syntax error.

Line 424 is missing a trailing comma before the commented-out model property on line 425, which would cause a TypeScript syntax error.

🐛 Proposed fix
     logEvent('tengu_ultraplan_launched', {
       has_seed_plan: Boolean(seedPlan),
-      prompt_identifier: promptIdentifier as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
-      // model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
+      prompt_identifier: promptIdentifier as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
+      // model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
prompt_identifier: promptIdentifier as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
// model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
});
prompt_identifier: promptIdentifier as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
// model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/ultraplan.tsx` around lines 424 - 426, The object literal ending
at the call that sets prompt_identifier: promptIdentifier as
AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS is missing a trailing
comma which causes a TypeScript syntax error; add a comma after that property
(before the commented-out model line) so the object (in the function/constructor
where prompt_identifier and the commented model line are defined) is
syntactically valid.

// TODO(#23985): replace registerRemoteAgentTask + startDetachedPoll with
// ExitPlanModeScanner inside startRemoteSessionPolling.
Expand All @@ -400,6 +438,11 @@ async function launchDetached(opts: {
isUltraplan: true,
});
startDetachedPoll(taskId, session.id, url, getAppState, setAppState);
registerCleanup(async()=>{
if(getAppState().ultraplanSessionUrl === url) {
await archiveRemoteSession(session.id, 1500)
}
});
} catch (e) {
logError(e);
logEvent('tengu_ultraplan_create_failed', {
Expand All @@ -409,6 +452,13 @@ async function launchDetached(opts: {
value: `ultraplan: unexpected error — ${errorMessage(e)}`,
mode: 'task-notification',
});

enqueuePendingNotification({
value: `Ultraplan hit an unexpected error during launch. Wait for the user's next instructions.`,
mode: 'task-notification',
isMeta: true
});

if (sessionId) {
// Error after teleport succeeded — archive so the remote doesn't sit
// running for 30min with nobody polling it.
Expand All @@ -417,11 +467,11 @@ async function launchDetached(opts: {
);
// ultraplanSessionUrl may have been set before the throw; clear it so
// the "already polling" guard doesn't block future launches.
setAppState(prev => (prev.ultraplanSessionUrl ? { ...prev, ultraplanSessionUrl: undefined } : prev));
setAppState(prev => prev.ultraplanSessionUrl ? { ...prev, ultraplanSessionUrl: undefined } : prev);
}
} finally {
// No-op on success: the url-setting setAppState already cleared this.
setAppState(prev => (prev.ultraplanLaunching ? { ...prev, ultraplanLaunching: undefined } : prev));
setAppState(prev => prev.ultraplanLaunching ? { ...prev, ultraplanLaunching: undefined } : prev);
}
}

Expand Down Expand Up @@ -469,6 +519,7 @@ export default {
name: 'ultraplan',
description: `~10–30 min · Claude Code on the web drafts an advanced plan you can edit and approve. See ${CCR_TERMS_URL}`,
argumentHint: '<prompt>',
isEnabled: () => true,
// isEnabled: () => process.env.USER_TYPE === 'ant',
isEnabled: () => isUltraplanEnabled(),
load: () => Promise.resolve({ call }),
} satisfies Command;
Loading
Loading