-
Notifications
You must be signed in to change notification settings - Fork 15k
Add Ultraplan Feature for Advanced Multi-Agent Planning #232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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 | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // 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 | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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 | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Default The helper accepts 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if (blurb) { | ||||||||||||||||||||||||||||||||||||||||||
| parts.push('', blurb); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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 => { | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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; | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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) { | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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, | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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; | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing comma causes syntax error. Line 424 is missing a trailing comma before the commented-out 🐛 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| // TODO(#23985): replace registerRemoteAgentTask + startDetachedPoll with | ||||||||||||||||||||||||||||||||||||||||||
| // ExitPlanModeScanner inside startRemoteSessionPolling. | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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', { | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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. | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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; | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 2968
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 445
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 50385
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 99
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 99
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 1564
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 688
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 488
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 382
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 1172
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 54
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 214
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 1440
🏁 Script executed:
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 benullorfalseso 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_disabledkill-switch).Also applies to: 523
🤖 Prompt for AI Agents