Files
claude-code/src/assistant/index.ts
unraid c7e1c50b86 feat: 添加服务层增强与零散改进
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-22 22:38:10 +08:00

136 lines
3.5 KiB
TypeScript

import { readFileSync } from 'fs'
import { join } from 'path'
import { getKairosActive, getSessionId } from '../bootstrap/state.js'
import type { AppState } from '../state/AppState.js'
import { formatAgentId } from '../utils/agentId.js'
import { getCwd } from '../utils/cwd.js'
import { getClaudeConfigHomeDir } from '../utils/envUtils.js'
import { TEAM_LEAD_NAME } from '../utils/swarm/constants.js'
import {
getTeamFilePath,
registerTeamForSessionCleanup,
sanitizeName,
writeTeamFileAsync,
type TeamFile,
} from '../utils/swarm/teamHelpers.js'
import { assignTeammateColor } from '../utils/swarm/teammateLayoutManager.js'
import {
ensureTasksDir,
resetTaskList,
setLeaderTeamName,
} from '../utils/tasks.js'
let _assistantForced = false
/**
* Whether the current session is in assistant (KAIROS) daemon mode.
* Wraps the bootstrap kairosActive state set by main.tsx after gate check.
*/
export function isAssistantMode(): boolean {
return getKairosActive()
}
/**
* Mark this session as forced assistant mode (--assistant flag).
* Skips the GrowthBook gate check — daemon is pre-entitled.
*/
export function markAssistantForced(): void {
_assistantForced = true
}
export function isAssistantForced(): boolean {
return _assistantForced
}
/**
* Pre-create an in-process team so Agent(name) can spawn teammates
* without TeamCreate.
*
* Creates a session-scoped assistant team file and returns a full team
* context object matching AppState.teamContext.
*/
export async function initializeAssistantTeam(): Promise<
AppState['teamContext']
> {
const sessionId = getSessionId()
const teamName = sanitizeName(`assistant-${sessionId.slice(0, 8)}`)
const leadAgentId = formatAgentId(TEAM_LEAD_NAME, teamName)
const teamFilePath = getTeamFilePath(teamName)
const now = Date.now()
const cwd = getCwd()
const color = assignTeammateColor(leadAgentId)
const teamFile: TeamFile = {
name: teamName,
description: 'Assistant mode in-process team',
createdAt: now,
leadAgentId,
leadSessionId: sessionId,
members: [
{
agentId: leadAgentId,
name: TEAM_LEAD_NAME,
agentType: 'assistant',
color,
joinedAt: now,
tmuxPaneId: '',
cwd,
subscriptions: [],
backendType: 'in-process',
},
],
}
await writeTeamFileAsync(teamName, teamFile)
registerTeamForSessionCleanup(teamName)
await resetTaskList(teamName)
await ensureTasksDir(teamName)
setLeaderTeamName(teamName)
return {
teamName,
teamFilePath,
leadAgentId,
selfAgentId: leadAgentId,
selfAgentName: TEAM_LEAD_NAME,
isLeader: true,
selfAgentColor: color,
teammates: {
[leadAgentId]: {
name: TEAM_LEAD_NAME,
agentType: 'assistant',
color,
tmuxSessionName: 'in-process',
tmuxPaneId: 'leader',
cwd,
spawnedAt: now,
},
},
}
}
/**
* Assistant-specific system prompt addendum loaded from ~/.claude/agents/assistant.md.
* Returns empty string if the file doesn't exist.
*/
export function getAssistantSystemPromptAddendum(): string {
try {
return readFileSync(
join(getClaudeConfigHomeDir(), 'agents', 'assistant.md'),
'utf-8',
)
} catch {
return ''
}
}
/**
* How assistant mode was activated. Used for diagnostics/analytics.
* - 'daemon': via --assistant flag (Agent SDK daemon)
* - 'gate': via GrowthBook gate check
*/
export function getAssistantActivationPath(): string | undefined {
if (!isAssistantMode()) return undefined
return _assistantForced ? 'daemon' : 'gate'
}