remove , using instead

This commit is contained in:
weishu
2026-03-16 18:29:09 +08:00
parent 02c8e12e80
commit 329d28a93c
40 changed files with 384 additions and 285 deletions
+3 -5
View File
@@ -6,9 +6,8 @@ import { Session } from "./session"
import { claudeLocalLauncher } from "./claudeLocalLauncher"
import { claudeRemoteLauncher } from "./claudeRemoteLauncher"
import { ApiClient } from "@/lib"
import type { SessionModelMode } from "@/api/types"
import type { SessionModel } from "@/api/types"
import type { ClaudePermissionMode } from "@hapi/protocol/types"
import { resolveClaudeSessionModelMode } from "./modelMode"
export type PermissionMode = ClaudePermissionMode;
@@ -24,7 +23,7 @@ export interface EnhancedMode {
interface LoopOptions {
path: string
model?: string
model?: SessionModel
permissionMode?: PermissionMode
startingMode?: 'local' | 'remote'
startedBy?: 'runner' | 'terminal'
@@ -46,7 +45,6 @@ export async function loop(opts: LoopOptions) {
const logPath = logger.logFilePath;
const startedBy = opts.startedBy ?? 'terminal';
const startingMode = opts.startingMode ?? 'local';
const modelMode: SessionModelMode = resolveClaudeSessionModelMode(opts.model)
const session = new Session({
api: opts.api,
client: opts.session,
@@ -64,7 +62,7 @@ export async function loop(opts: LoopOptions) {
startingMode,
hookSettingsPath: opts.hookSettingsPath,
permissionMode: opts.permissionMode ?? 'default',
modelMode
model: opts.model
});
await runLocalRemoteSession({
+21
View File
@@ -0,0 +1,21 @@
import { describe, expect, it } from 'vitest'
import { normalizeClaudeSessionModel } from './model'
describe('normalizeClaudeSessionModel', () => {
it('returns null when model is missing', () => {
expect(normalizeClaudeSessionModel()).toBeNull()
})
it('returns null for auto-like values', () => {
expect(normalizeClaudeSessionModel('')).toBeNull()
expect(normalizeClaudeSessionModel('auto')).toBeNull()
expect(normalizeClaudeSessionModel('default')).toBeNull()
})
it('preserves Claude aliases and full model strings', () => {
expect(normalizeClaudeSessionModel('sonnet')).toBe('sonnet')
expect(normalizeClaudeSessionModel('opus[1m]')).toBe('opus[1m]')
expect(normalizeClaudeSessionModel('claude-3-7-sonnet-latest')).toBe('claude-3-7-sonnet-latest')
expect(normalizeClaudeSessionModel(' claude-opus-4-1-20250805 ')).toBe('claude-opus-4-1-20250805')
})
})
+10
View File
@@ -0,0 +1,10 @@
import type { SessionModel } from '@/api/types'
export function normalizeClaudeSessionModel(model?: string | null): SessionModel {
const trimmedModel = model?.trim()
if (!trimmedModel || trimmedModel === 'auto' || trimmedModel === 'default') {
return null
}
return trimmedModel
}
-39
View File
@@ -1,39 +0,0 @@
import { describe, expect, it } from 'vitest'
import { resolveClaudePersistedModel, resolveClaudeSessionModelMode } from './modelMode'
describe('resolveClaudeSessionModelMode', () => {
it('returns default when model is missing', () => {
expect(resolveClaudeSessionModelMode()).toBe('default')
})
it('returns default for auto and unsupported models', () => {
expect(resolveClaudeSessionModelMode('auto')).toBe('default')
expect(resolveClaudeSessionModelMode('claude-sonnet-4-5')).toBe('default')
})
it('returns standard Claude session model modes', () => {
expect(resolveClaudeSessionModelMode('sonnet')).toBe('sonnet')
expect(resolveClaudeSessionModelMode('opus')).toBe('opus')
})
it('returns 1m Claude session model modes', () => {
expect(resolveClaudeSessionModelMode('sonnet[1m]')).toBe('sonnet[1m]')
expect(resolveClaudeSessionModelMode('opus[1m]')).toBe('opus[1m]')
})
})
describe('resolveClaudePersistedModel', () => {
it('skips missing, auto, default, and representable mode names', () => {
expect(resolveClaudePersistedModel()).toBeUndefined()
expect(resolveClaudePersistedModel('')).toBeUndefined()
expect(resolveClaudePersistedModel('auto')).toBeUndefined()
expect(resolveClaudePersistedModel('default')).toBeUndefined()
expect(resolveClaudePersistedModel('sonnet')).toBeUndefined()
expect(resolveClaudePersistedModel('opus[1m]')).toBeUndefined()
})
it('persists unsupported custom Claude model strings', () => {
expect(resolveClaudePersistedModel('claude-3-7-sonnet-latest')).toBe('claude-3-7-sonnet-latest')
expect(resolveClaudePersistedModel(' claude-opus-4-1-20250805 ')).toBe('claude-opus-4-1-20250805')
})
})
-30
View File
@@ -1,30 +0,0 @@
import type { SessionModelMode } from '@/api/types'
const CLAUDE_SESSION_MODEL_MODES = new Set<SessionModelMode>([
'sonnet',
'sonnet[1m]',
'opus',
'opus[1m]'
])
export function resolveClaudeSessionModelMode(model?: string): SessionModelMode {
const trimmedModel = model?.trim()
if (!trimmedModel) {
return 'default'
}
return CLAUDE_SESSION_MODEL_MODES.has(trimmedModel as SessionModelMode)
? trimmedModel as SessionModelMode
: 'default'
}
export function resolveClaudePersistedModel(model?: string): string | undefined {
const trimmedModel = model?.trim()
if (!trimmedModel || trimmedModel === 'auto' || trimmedModel === 'default') {
return undefined
}
return resolveClaudeSessionModelMode(trimmedModel) === 'default'
? trimmedModel
: undefined
}
+29 -21
View File
@@ -1,6 +1,6 @@
import { logger } from '@/ui/logger';
import { loop } from '@/claude/loop';
import { AgentState, SessionModelMode } from '@/api/types';
import { AgentState, SessionModel } from '@/api/types';
import { EnhancedMode, PermissionMode } from './loop';
import { MessageQueue2 } from '@/utils/MessageQueue2';
import { hashObject } from '@/utils/deterministicJson';
@@ -14,10 +14,10 @@ import { registerKillSessionHandler } from './registerKillSessionHandler';
import type { Session } from './session';
import { bootstrapSession } from '@/agent/sessionFactory';
import { createModeChangeHandler, createRunnerLifecycle, setControlledByUser } from '@/agent/runnerLifecycle';
import { isModelModeAllowedForFlavor, isPermissionModeAllowedForFlavor } from '@hapi/protocol';
import { ModelModeSchema, PermissionModeSchema } from '@hapi/protocol/schemas';
import { isPermissionModeAllowedForFlavor } from '@hapi/protocol';
import { PermissionModeSchema } from '@hapi/protocol/schemas';
import { formatMessageWithAttachments } from '@/utils/attachmentFormatter';
import { resolveClaudePersistedModel, resolveClaudeSessionModelMode } from './modelMode';
import { normalizeClaudeSessionModel } from './model';
export interface StartOptions {
model?: string
@@ -46,12 +46,13 @@ export async function runClaude(options: StartOptions = {}): Promise<void> {
}
const initialState: AgentState = {};
const initialModel = normalizeClaudeSessionModel(options.model);
const { api, session, sessionInfo } = await bootstrapSession({
flavor: 'claude',
startedBy,
workingDirectory,
agentState: initialState,
model: resolveClaudePersistedModel(options.model)
model: initialModel ?? undefined
});
logger.debug(`Session created: ${sessionInfo.id}`);
@@ -145,7 +146,7 @@ export async function runClaude(options: StartOptions = {}): Promise<void> {
// Forward messages to the queue
let currentPermissionMode: PermissionMode = options.permissionMode ?? 'default';
let currentModelMode: SessionModelMode = resolveClaudeSessionModelMode(options.model);
let currentModel: SessionModel = initialModel;
let currentFallbackModel: string | undefined = undefined; // Track current fallback model
let currentCustomSystemPrompt: string | undefined = undefined; // Track current custom system prompt
let currentAppendSystemPrompt: string | undefined = undefined; // Track current append system prompt
@@ -158,17 +159,21 @@ export async function runClaude(options: StartOptions = {}): Promise<void> {
return;
}
sessionInstance.setPermissionMode(currentPermissionMode);
sessionInstance.setModelMode(currentModelMode);
logger.debug(`[loop] Synced session modes for keepalive: permissionMode=${currentPermissionMode}, modelMode=${currentModelMode}`);
sessionInstance.setModel(currentModel);
logger.debug(`[loop] Synced session config for keepalive: permissionMode=${currentPermissionMode}, model=${currentModel ?? 'auto'}`);
};
session.onUserMessage((message) => {
const sessionPermissionMode = currentSessionRef.current?.getPermissionMode();
if (sessionPermissionMode && isPermissionModeAllowedForFlavor(sessionPermissionMode, 'claude')) {
currentPermissionMode = sessionPermissionMode as PermissionMode;
}
const sessionModel = currentSessionRef.current?.getModel();
if (sessionModel !== undefined) {
currentModel = sessionModel;
}
const messagePermissionMode = currentPermissionMode;
const messageModel = currentModelMode === 'default' ? undefined : currentModelMode;
logger.debug(`[loop] User message received with permission mode: ${currentPermissionMode}, model: ${currentModelMode}`);
const messageModel = currentModel ?? undefined;
logger.debug(`[loop] User message received with permission mode: ${currentPermissionMode}, model: ${currentModel ?? 'auto'}`);
// Resolve custom system prompt - use message.meta.customSystemPrompt if provided, otherwise use current
let messageCustomSystemPrompt = currentCustomSystemPrompt;
@@ -284,31 +289,34 @@ export async function runClaude(options: StartOptions = {}): Promise<void> {
return parsed.data as PermissionMode;
};
const resolveModelMode = (value: unknown): SessionModelMode => {
const parsed = ModelModeSchema.safeParse(value);
if (!parsed.success || !isModelModeAllowedForFlavor(parsed.data, 'claude')) {
throw new Error('Invalid model mode');
const resolveModel = (value: unknown): SessionModel => {
if (value === null) {
return null;
}
return parsed.data;
if (typeof value !== 'string') {
throw new Error('Invalid model');
}
return normalizeClaudeSessionModel(value);
};
session.rpcHandlerManager.registerHandler('set-session-config', async (payload: unknown) => {
if (!payload || typeof payload !== 'object') {
throw new Error('Invalid session config payload');
}
const config = payload as { permissionMode?: unknown; modelMode?: unknown };
const config = payload as { permissionMode?: unknown; model?: unknown };
if (config.permissionMode !== undefined) {
currentPermissionMode = resolvePermissionMode(config.permissionMode);
}
if (config.modelMode !== undefined) {
const resolvedModelMode = resolveModelMode(config.modelMode);
currentModelMode = resolvedModelMode;
if (config.model !== undefined) {
currentModel = resolveModel(config.model);
}
syncSessionModes();
return { applied: { permissionMode: currentPermissionMode, modelMode: currentModelMode } };
return { applied: { permissionMode: currentPermissionMode, model: currentModel } };
});
let loopError: unknown = null;
@@ -316,7 +324,7 @@ export async function runClaude(options: StartOptions = {}): Promise<void> {
try {
await loop({
path: workingDirectory,
model: options.model,
model: currentModel,
permissionMode: options.permissionMode,
startingMode,
messageQueue,
+6 -6
View File
@@ -2,7 +2,7 @@ import { ApiClient, ApiSessionClient } from '@/lib';
import { MessageQueue2 } from '@/utils/MessageQueue2';
import { logger } from '@/ui/logger';
import { AgentSessionBase } from '@/agent/sessionBase';
import type { SessionModelMode } from '@/api/types';
import type { SessionModel } from '@/api/types';
import type { EnhancedMode } from './loop';
import type { PermissionMode } from './loop';
import type { LocalLaunchExitReason } from '@/agent/localLaunchPolicy';
@@ -39,7 +39,7 @@ export class Session extends AgentSessionBase<EnhancedMode> {
startingMode: 'local' | 'remote';
hookSettingsPath: string;
permissionMode?: PermissionMode;
modelMode?: SessionModelMode;
model?: SessionModel;
}) {
super({
api: opts.api,
@@ -57,7 +57,7 @@ export class Session extends AgentSessionBase<EnhancedMode> {
claudeSessionId: sessionId
}),
permissionMode: opts.permissionMode,
modelMode: opts.modelMode
model: opts.model
});
this.claudeEnvVars = opts.claudeEnvVars;
@@ -68,15 +68,15 @@ export class Session extends AgentSessionBase<EnhancedMode> {
this.startedBy = opts.startedBy;
this.startingMode = opts.startingMode;
this.permissionMode = opts.permissionMode;
this.modelMode = opts.modelMode;
this.model = opts.model;
}
setPermissionMode = (mode: PermissionMode): void => {
this.permissionMode = mode;
};
setModelMode = (mode: SessionModelMode): void => {
this.modelMode = mode;
setModel = (model: SessionModel): void => {
this.model = model;
};
recordLocalLaunchFailure = (message: string, exitReason: LocalLaunchExitReason): void => {