Download src/tools/AgentTool/resumeAgent.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 9.34 kB
-
https://huggingface.co/chenbhao/codev/resolve/e91c8197c9918ff9e5df3140e8c36282bb30e9a2/src/tools/AgentTool/resumeAgent.ts
- Command line
-
hf download hf://chenbhao/codev@e91c8197c9918ff9e5df3140e8c36282bb30e9a2/src/tools/AgentTool/resumeAgent.ts
-
curl -L -o resumeAgent.ts https://huggingface.co/chenbhao/codev/resolve/e91c8197c9918ff9e5df3140e8c36282bb30e9a2/src/tools/AgentTool/resumeAgent.ts
9.34 kB
| import { promises as fsp } from 'fs' | |
| import { getSdkAgentProgressSummariesEnabled } from '../../bootstrap/state.js' | |
| import { getSystemPrompt } from '../../constants/prompts.js' | |
| import { isCoordinatorMode } from '../../coordinator/coordinatorMode.js' | |
| import type { CanUseToolFn } from '../../hooks/useCanUseTool.js' | |
| import type { ToolUseContext } from '../../Tool.js' | |
| import { registerAsyncAgent } from '../../tasks/LocalAgentTask/LocalAgentTask.js' | |
| import { assembleToolPool } from '../../tools.js' | |
| import { asAgentId } from '../../types/ids.js' | |
| import { runWithAgentContext } from '../../utils/agentContext.js' | |
| import { runWithCwdOverride } from '../../utils/cwd.js' | |
| import { logForDebugging } from '../../utils/debug.js' | |
| import { | |
| createUserMessage, | |
| filterOrphanedThinkingOnlyMessages, | |
| filterUnresolvedToolUses, | |
| filterWhitespaceOnlyAssistantMessages, | |
| } from '../../utils/messages.js' | |
| import { getAgentModel } from '../../utils/model/agent.js' | |
| import { getQuerySourceForAgent } from '../../utils/promptCategory.js' | |
| import { | |
| getAgentTranscript, | |
| readAgentMetadata, | |
| } from '../../utils/sessionStorage.js' | |
| import { buildEffectiveSystemPrompt } from '../../utils/systemPrompt.js' | |
| import type { SystemPrompt } from '../../utils/systemPromptType.js' | |
| import { getTaskOutputPath } from '../../utils/task/diskOutput.js' | |
| import { getParentSessionId } from '../../utils/teammate.js' | |
| import { reconstructForSubagentResume } from '../../utils/toolResultStorage.js' | |
| import { runAsyncAgentLifecycle } from './agentToolUtils.js' | |
| import { GENERAL_PURPOSE_AGENT } from './built-in/generalPurposeAgent.js' | |
| import { FORK_AGENT, isForkSubagentEnabled } from './forkSubagent.js' | |
| import type { AgentDefinition } from './loadAgentsDir.js' | |
| import { isBuiltInAgent } from './loadAgentsDir.js' | |
| import { runAgent } from './runAgent.js' | |
| export type ResumeAgentResult = { | |
| agentId: string | |
| description: string | |
| outputFile: string | |
| } | |
| export async function resumeAgentBackground({ | |
| agentId, | |
| prompt, | |
| toolUseContext, | |
| canUseTool, | |
| invokingRequestId, | |
| }: { | |
| agentId: string | |
| prompt: string | |
| toolUseContext: ToolUseContext | |
| canUseTool: CanUseToolFn | |
| invokingRequestId?: string | |
| }): Promise<ResumeAgentResult> { | |
| const startTime = Date.now() | |
| const appState = toolUseContext.getAppState() | |
| // In-process teammates get a no-op setAppState; setAppStateForTasks | |
| // reaches the root store so task registration/progress/kill stay visible. | |
| const rootSetAppState = | |
| toolUseContext.setAppStateForTasks ?? toolUseContext.setAppState | |
| const permissionMode = appState.toolPermissionContext.mode | |
| const [transcript, meta] = await Promise.all([ | |
| getAgentTranscript(asAgentId(agentId)), | |
| readAgentMetadata(asAgentId(agentId)), | |
| ]) | |
| if (!transcript) { | |
| throw new Error(`No transcript found for agent ID: ${agentId}`) | |
| } | |
| const resumedMessages = filterWhitespaceOnlyAssistantMessages( | |
| filterOrphanedThinkingOnlyMessages( | |
| filterUnresolvedToolUses(transcript.messages), | |
| ), | |
| ) | |
| const resumedReplacementState = reconstructForSubagentResume( | |
| toolUseContext.contentReplacementState, | |
| resumedMessages, | |
| transcript.contentReplacements, | |
| ) | |
| // Best-effort: if the original worktree was removed externally, fall back | |
| // to parent cwd rather than crashing on chdir later. | |
| const resumedWorktreePath = meta?.worktreePath | |
| ? await fsp.stat(meta.worktreePath).then( | |
| s => (s.isDirectory() ? meta.worktreePath : undefined), | |
| () => { | |
| logForDebugging( | |
| `Resumed worktree ${meta.worktreePath} no longer exists; falling back to parent cwd`, | |
| ) | |
| return undefined | |
| }, | |
| ) | |
| : undefined | |
| if (resumedWorktreePath) { | |
| // Bump mtime so stale-worktree cleanup doesn't delete a just-resumed worktree (#22355) | |
| const now = new Date() | |
| await fsp.utimes(resumedWorktreePath, now, now) | |
| } | |
| // Skip filterDeniedAgents re-gating — original spawn already passed permission checks | |
| let selectedAgent: AgentDefinition | |
| let isResumedFork = false | |
| if (meta?.agentType === FORK_AGENT.agentType) { | |
| selectedAgent = FORK_AGENT | |
| isResumedFork = true | |
| } else if (meta?.agentType) { | |
| const found = toolUseContext.options.agentDefinitions.activeAgents.find( | |
| a => a.agentType === meta.agentType, | |
| ) | |
| selectedAgent = found ?? GENERAL_PURPOSE_AGENT | |
| } else { | |
| selectedAgent = GENERAL_PURPOSE_AGENT | |
| } | |
| const uiDescription = meta?.description ?? '(resumed)' | |
| let forkParentSystemPrompt: SystemPrompt | undefined | |
| if (isResumedFork) { | |
| if (toolUseContext.renderedSystemPrompt) { | |
| forkParentSystemPrompt = toolUseContext.renderedSystemPrompt | |
| } else { | |
| const mainThreadAgentDefinition = appState.agent | |
| ? appState.agentDefinitions.activeAgents.find( | |
| a => a.agentType === appState.agent, | |
| ) | |
| : undefined | |
| const additionalWorkingDirectories = Array.from( | |
| appState.toolPermissionContext.additionalWorkingDirectories.keys(), | |
| ) | |
| const defaultSystemPrompt = await getSystemPrompt( | |
| toolUseContext.options.tools, | |
| toolUseContext.options.mainLoopModel, | |
| additionalWorkingDirectories, | |
| toolUseContext.options.mcpClients, | |
| ) | |
| forkParentSystemPrompt = buildEffectiveSystemPrompt({ | |
| mainThreadAgentDefinition, | |
| toolUseContext, | |
| customSystemPrompt: toolUseContext.options.customSystemPrompt, | |
| defaultSystemPrompt, | |
| appendSystemPrompt: toolUseContext.options.appendSystemPrompt, | |
| }) | |
| } | |
| if (!forkParentSystemPrompt) { | |
| throw new Error( | |
| 'Cannot resume fork agent: unable to reconstruct parent system prompt', | |
| ) | |
| } | |
| } | |
| // Resolve model for analytics metadata (runAgent resolves its own internally) | |
| const resolvedAgentModel = getAgentModel( | |
| selectedAgent.model, | |
| toolUseContext.options.mainLoopModel, | |
| undefined, | |
| permissionMode, | |
| ) | |
| const workerPermissionContext = { | |
| ...appState.toolPermissionContext, | |
| mode: selectedAgent.permissionMode ?? 'acceptEdits', | |
| } | |
| const workerTools = isResumedFork | |
| ? toolUseContext.options.tools | |
| : assembleToolPool(workerPermissionContext, appState.mcp.tools) | |
| const runAgentParams: Parameters<typeof runAgent>[0] = { | |
| agentDefinition: selectedAgent, | |
| promptMessages: [ | |
| ...resumedMessages, | |
| createUserMessage({ content: prompt }), | |
| ], | |
| toolUseContext, | |
| canUseTool, | |
| isAsync: true, | |
| querySource: getQuerySourceForAgent( | |
| selectedAgent.agentType, | |
| isBuiltInAgent(selectedAgent), | |
| ), | |
| model: undefined, | |
| // Fork resume: pass parent's system prompt (cache-identical prefix). | |
| // Non-fork: undefined → runAgent recomputes under wrapWithCwd so | |
| // getCwd() sees resumedWorktreePath. | |
| override: isResumedFork | |
| ? { systemPrompt: forkParentSystemPrompt } | |
| : undefined, | |
| availableTools: workerTools, | |
| // Transcript already contains the parent context slice from the | |
| // original fork. Re-supplying it would cause duplicate tool_use IDs. | |
| forkContextMessages: undefined, | |
| ...(isResumedFork && { useExactTools: true }), | |
| // Re-persist so metadata survives runAgent's writeAgentMetadata overwrite | |
| worktreePath: resumedWorktreePath, | |
| description: meta?.description, | |
| contentReplacementState: resumedReplacementState, | |
| } | |
| // Skip name-registry write — original entry persists from the initial spawn | |
| const agentBackgroundTask = registerAsyncAgent({ | |
| agentId, | |
| description: uiDescription, | |
| prompt, | |
| selectedAgent, | |
| setAppState: rootSetAppState, | |
| toolUseId: toolUseContext.toolUseId, | |
| }) | |
| const metadata = { | |
| prompt, | |
| resolvedAgentModel, | |
| isBuiltInAgent: isBuiltInAgent(selectedAgent), | |
| startTime, | |
| agentType: selectedAgent.agentType, | |
| isAsync: true, | |
| } | |
| const asyncAgentContext = { | |
| agentId, | |
| parentSessionId: getParentSessionId(), | |
| agentType: 'subagent' as const, | |
| subagentName: selectedAgent.agentType, | |
| isBuiltIn: isBuiltInAgent(selectedAgent), | |
| invokingRequestId, | |
| invocationKind: 'resume' as const, | |
| invocationEmitted: false, | |
| } | |
| const wrapWithCwd = <T>(fn: () => T): T => | |
| resumedWorktreePath ? runWithCwdOverride(resumedWorktreePath, fn) : fn() | |
| void runWithAgentContext(asyncAgentContext, () => | |
| wrapWithCwd(() => | |
| runAsyncAgentLifecycle({ | |
| taskId: agentBackgroundTask.agentId, | |
| abortController: agentBackgroundTask.abortController!, | |
| makeStream: onCacheSafeParams => | |
| runAgent({ | |
| ...runAgentParams, | |
| override: { | |
| ...runAgentParams.override, | |
| agentId: asAgentId(agentBackgroundTask.agentId), | |
| abortController: agentBackgroundTask.abortController!, | |
| }, | |
| onCacheSafeParams, | |
| }), | |
| metadata, | |
| description: uiDescription, | |
| toolUseContext, | |
| rootSetAppState, | |
| agentIdForCleanup: agentId, | |
| enableSummarization: | |
| isCoordinatorMode() || | |
| isForkSubagentEnabled() || | |
| getSdkAgentProgressSummariesEnabled(), | |
| getWorktreeResult: async () => | |
| resumedWorktreePath ? { worktreePath: resumedWorktreePath } : {}, | |
| }), | |
| ), | |
| ) | |
| return { | |
| agentId, | |
| description: uiDescription, | |
| outputFile: getTaskOutputPath(agentId), | |
| } | |
| } | |