Repository navigation
feat(cli): add fork-session resume flag #4159
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 2 commits
bf51af0
e00b46f
5cd1793
4bfc17d
9f499eb
bf80a88
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 | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -47,6 +47,7 @@ import { hideBin } from 'yargs/helpers'; | |||||||||||||||||||||||||||||||||||||||
| import * as fs from 'node:fs'; | ||||||||||||||||||||||||||||||||||||||||
| import * as path from 'node:path'; | ||||||||||||||||||||||||||||||||||||||||
| import { homedir } from 'node:os'; | ||||||||||||||||||||||||||||||||||||||||
| import { randomUUID } from 'node:crypto'; | ||||||||||||||||||||||||||||||||||||||||
| import stripJsonComments from 'strip-json-comments'; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| import { resolvePath } from '../utils/resolvePath.js'; | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -159,6 +160,11 @@ export interface CliArgs { | |||||||||||||||||||||||||||||||||||||||
| resume: string | undefined; | ||||||||||||||||||||||||||||||||||||||||
| /** Specify a session ID without session resumption */ | ||||||||||||||||||||||||||||||||||||||||
| sessionId: string | undefined; | ||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||
| * Create a new forked session from the resumed session. Must be used with | ||||||||||||||||||||||||||||||||||||||||
| * --resume or --continue. | ||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||
| forkSession?: boolean | undefined; | ||||||||||||||||||||||||||||||||||||||||
| maxSessionTurns: number | undefined; | ||||||||||||||||||||||||||||||||||||||||
| coreTools: string[] | undefined; | ||||||||||||||||||||||||||||||||||||||||
| excludeTools: string[] | undefined; | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -803,6 +809,12 @@ export async function parseArguments(): Promise<CliArgs> { | |||||||||||||||||||||||||||||||||||||||
| type: 'string', | ||||||||||||||||||||||||||||||||||||||||
| description: 'Specify a session ID for this run.', | ||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||
| .option('fork-session', { | ||||||||||||||||||||||||||||||||||||||||
| type: 'boolean', | ||||||||||||||||||||||||||||||||||||||||
| description: | ||||||||||||||||||||||||||||||||||||||||
| 'Create a new forked session from the resumed session. Must be used with --resume or --continue.', | ||||||||||||||||||||||||||||||||||||||||
| default: false, | ||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||
| .option('max-session-turns', { | ||||||||||||||||||||||||||||||||||||||||
| type: 'number', | ||||||||||||||||||||||||||||||||||||||||
| description: 'Maximum number of session turns', | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -900,9 +912,13 @@ export async function parseArguments(): Promise<CliArgs> { | |||||||||||||||||||||||||||||||||||||||
| if (argv['continue'] && argv['resume']) { | ||||||||||||||||||||||||||||||||||||||||
| return 'Cannot use both --continue and --resume together. Use --continue to resume the latest session, or --resume <sessionId> to resume a specific session.'; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| if (argv['sessionId'] && (argv['continue'] || argv['resume'])) { | ||||||||||||||||||||||||||||||||||||||||
| const hasResume = argv['resume'] !== undefined; | ||||||||||||||||||||||||||||||||||||||||
| if (argv['sessionId'] && (argv['continue'] || hasResume)) { | ||||||||||||||||||||||||||||||||||||||||
| return 'Cannot use --session-id with --continue or --resume. Use --session-id to start a new session with a specific ID, or use --continue/--resume to resume an existing session.'; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| if (argv['forkSession'] && !(argv['continue'] || hasResume)) { | ||||||||||||||||||||||||||||||||||||||||
| return '--fork-session must be used with --resume or --continue.'; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||
| argv['sessionId'] && | ||||||||||||||||||||||||||||||||||||||||
| !isValidSessionId(argv['sessionId'] as string) | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1517,6 +1533,11 @@ export async function loadCliConfig( | |||||||||||||||||||||||||||||||||||||||
| sessionData = await sessionService.loadLastSession(); | ||||||||||||||||||||||||||||||||||||||||
| if (sessionData) { | ||||||||||||||||||||||||||||||||||||||||
| sessionId = sessionData.conversation.sessionId; | ||||||||||||||||||||||||||||||||||||||||
| } else if (argv.forkSession) { | ||||||||||||||||||||||||||||||||||||||||
| writeStderrLine( | ||||||||||||||||||||||||||||||||||||||||
| 'Cannot use --fork-session with --continue: no saved session found to fork.', | ||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1532,6 +1553,18 @@ export async function loadCliConfig( | |||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
Collaborator
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. [Suggestion] — DeepSeek/deepseek-v4-pro via Qwen Code /review |
||||||||||||||||||||||||||||||||||||||||
| if (argv.forkSession && sessionId) { | ||||||||||||||||||||||||||||||||||||||||
| const sourceSessionId = sessionId; | ||||||||||||||||||||||||||||||||||||||||
| const forkedSessionId = randomUUID(); | ||||||||||||||||||||||||||||||||||||||||
| await sessionService.forkSession(sourceSessionId, forkedSessionId); | ||||||||||||||||||||||||||||||||||||||||
|
Collaborator
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. [Suggestion]
Suggested change
— DeepSeek/deepseek-v4-pro via Qwen Code /review |
||||||||||||||||||||||||||||||||||||||||
| sessionId = forkedSessionId; | ||||||||||||||||||||||||||||||||||||||||
| sessionData = await sessionService.loadSession(forkedSessionId); | ||||||||||||||||||||||||||||||||||||||||
| if (!sessionData) { | ||||||||||||||||||||||||||||||||||||||||
| writeStderrLine(`Failed to load forked session ${forkedSessionId}.`); | ||||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } else if (argv['sessionId']) { | ||||||||||||||||||||||||||||||||||||||||
| // Use provided session ID without session resumption | ||||||||||||||||||||||||||||||||||||||||
| // Check if session ID is already in use | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
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.
[Suggestion] 该断言读取 mock 内部数组
mock.calls[0]?.[1]来验证config.getSessionId(),与上方 L936 已断言forkSession以(sourceSessionId, config.getSessionId())调用构成同义反复。建议改为更直接、有意义的断言:— DeepSeek/deepseek-v4-pro via Qwen Code /review