@@ -7,7 +7,7 @@ import * as l10n from '@vscode/l10n';
77import { Raw } from '@vscode/prompt-tsx' ;
88import type { CancellationToken , ChatRequest , ChatResponseProgressPart , ChatResponseReferencePart , ChatResponseStream , ChatResult , LanguageModelToolInformation , Progress } from 'vscode' ;
99import { IAuthenticationChatUpgradeService } from '../../../platform/authentication/common/authenticationUpgrade' ;
10- import { IChatHookService , StopHookInput , StopHookOutput , SubagentStartHookInput , SubagentStartHookOutput , SubagentStopHookInput , SubagentStopHookOutput } from '../../../platform/chat/common/chatHookService' ;
10+ import { IChatHookService , SessionStartHookInput , SessionStartHookOutput , StopHookInput , StopHookOutput , SubagentStartHookInput , SubagentStartHookOutput , SubagentStopHookInput , SubagentStopHookOutput } from '../../../platform/chat/common/chatHookService' ;
1111import { FetchStreamSource , IResponsePart } from '../../../platform/chat/common/chatMLFetcher' ;
1212import { CanceledResult , ChatFetchResponseType , ChatResponse } from '../../../platform/chat/common/commonTypes' ;
1313import { IConfigurationService } from '../../../platform/configuration/common/configurationService' ;
@@ -296,6 +296,46 @@ export abstract class ToolCallingLoop<TOptions extends IToolCallingLoopOptions =
296296 this . _logService . trace ( `[ToolCallingLoop] Stop hook blocked stopping: ${ reasons . join ( '; ' ) } ` ) ;
297297 }
298298
299+ /**
300+ * Called when a session starts to allow hooks to provide additional context.
301+ * @param input The session start hook input containing source
302+ * @param token Cancellation token
303+ * @returns Result containing additional context from hooks
304+ */
305+ protected async executeSessionStartHook ( input : SessionStartHookInput , token : CancellationToken ) : Promise < SubagentStartHookResult > {
306+ try {
307+ const results = await this . _chatHookService . executeHook ( 'SessionStart' , {
308+ toolInvocationToken : this . options . request . toolInvocationToken ,
309+ input : input
310+ } , token ) ;
311+
312+ // Collect additionalContext from all successful hook results
313+ const additionalContexts : string [ ] = [ ] ;
314+ for ( const result of results ) {
315+ if ( result . success === true ) {
316+ const output = result . output ;
317+ if ( typeof output === 'object' && output !== null ) {
318+ const hookOutput = output as SessionStartHookOutput ;
319+ if ( hookOutput . additionalContext ) {
320+ additionalContexts . push ( hookOutput . additionalContext ) ;
321+ this . _logService . trace ( `[ToolCallingLoop] SessionStart hook provided context: ${ hookOutput . additionalContext . substring ( 0 , 100 ) } ...` ) ;
322+ }
323+ }
324+ } else if ( result . success === false ) {
325+ const errorMessage = typeof result . output === 'string' ? result . output : 'Unknown error' ;
326+ this . _logService . error ( `[ToolCallingLoop] SessionStart hook error: ${ errorMessage } ` ) ;
327+ }
328+ }
329+
330+ return {
331+ additionalContext : additionalContexts . length > 0 ? additionalContexts . join ( '\n' ) : undefined
332+ } ;
333+ } catch ( error ) {
334+ this . _logService . error ( '[ToolCallingLoop] Error executing SessionStart hook' , error ) ;
335+ return { } ;
336+ }
337+ }
338+
299339 /**
300340 * Called when a subagent starts to allow hooks to provide additional context.
301341 * @param input The subagent start hook input containing agent_id and agent_type
@@ -404,6 +444,38 @@ export abstract class ToolCallingLoop<TOptions extends IToolCallingLoopOptions =
404444 }
405445 }
406446
447+ /**
448+ * Executes start hooks (SessionStart for regular sessions, SubagentStart for subagents).
449+ * Should be called before run() to allow hooks to provide context before the first prompt.
450+ *
451+ * - For subagents: Always executes SubagentStart hook
452+ * - For regular sessions: Only executes SessionStart hook on the first turn
453+ */
454+ public async runStartHooks ( token : CancellationToken ) : Promise < void > {
455+ // Execute SubagentStart hook for subagent requests, or SessionStart hook for first turn of regular sessions
456+ if ( this . options . request . subAgentInvocationId ) {
457+ const startHookResult = await this . executeSubagentStartHook ( {
458+ agent_id : this . options . request . subAgentInvocationId ,
459+ agent_type : this . options . request . subAgentName ?? 'default'
460+ } , token ) ;
461+ if ( startHookResult . additionalContext ) {
462+ this . additionalHookContext = startHookResult . additionalContext ;
463+ this . _logService . info ( `[ToolCallingLoop] SubagentStart hook provided context for subagent ${ this . options . request . subAgentInvocationId } ` ) ;
464+ }
465+ } else {
466+ const isFirstTurn = this . options . conversation . turns . length === 1 ;
467+ if ( isFirstTurn ) {
468+ const startHookResult = await this . executeSessionStartHook ( {
469+ source : 'new'
470+ } , token ) ;
471+ if ( startHookResult . additionalContext ) {
472+ this . additionalHookContext = startHookResult . additionalContext ;
473+ this . _logService . info ( '[ToolCallingLoop] SessionStart hook provided context for session' ) ;
474+ }
475+ }
476+ }
477+ }
478+
407479 public async run ( outputStream : ChatResponseStream | undefined , token : CancellationToken ) : Promise < IToolCallLoopResult > {
408480 let i = 0 ;
409481 let lastResult : IToolCallSingleResult | undefined ;
0 commit comments