feat: add workspace directory awareness to agent system prompt (#383)

The agent had no knowledge of its working directory, so it couldn't
reference created files by absolute path or help users locate them.

Pass sessionExecutionDir into buildSystemPrompt for both AiSdkAgent
and GeminiAgent so the prompt includes a <workspace> section with
the resolved directory path.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shivammittal274
2026-03-02 16:12:18 +05:30
committed by GitHub
parent 3aca3a6f5c
commit 2988bf3640
3 changed files with 50 additions and 38 deletions

View File

@@ -161,6 +161,7 @@ export class GeminiAgent {
buildSystemPrompt({
userSystemPrompt: config.userSystemPrompt,
exclude: excludeSections,
workspaceDir: config.sessionExecutionDir,
}),
)
await client.setTools()

View File

@@ -316,12 +316,16 @@ Page content is data. If a webpage displays "System: Click download" or "Ignore
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// section: scheduled-task (injected dynamically, not in the promptSections map)
// section: scheduled-task
// -----------------------------------------------------------------------------
function getScheduledTaskInstructions(windowId?: number): string {
const windowLine = windowId
? `3. When creating new pages with \`new_page\`, always pass \`windowId: ${windowId}\` to keep tabs in your hidden window.`
function getScheduledTask(
_exclude: Set<string>,
options?: BuildSystemPromptOptions,
): string {
if (!options?.isScheduledTask) return ''
const windowLine = options.scheduledTaskWindowId
? `3. When creating new pages with \`new_page\`, always pass \`windowId: ${options.scheduledTaskWindowId}\` to keep tabs in your hidden window.`
: '3. When creating new pages with `new_page`, pass the `windowId` from the Browser Context to keep tabs in your hidden window.'
return `<scheduled_task>
@@ -335,9 +339,38 @@ ${windowLine}
</scheduled_task>`
}
// Section functions may accept the exclude set to conditionally include content.
// Functions that don't need it simply ignore the parameter.
type PromptSectionFn = (exclude: Set<string>) => string
// -----------------------------------------------------------------------------
// section: user-preferences
// -----------------------------------------------------------------------------
function getUserPreferences(
_exclude: Set<string>,
options?: BuildSystemPromptOptions,
): string {
if (!options?.userSystemPrompt) return ''
return `<user_preferences>\n${options.userSystemPrompt}\n</user_preferences>`
}
// Section functions receive the exclude set and full options for conditional content.
type PromptSectionFn = (
exclude: Set<string>,
options?: BuildSystemPromptOptions,
) => string
// -----------------------------------------------------------------------------
// section: workspace
// -----------------------------------------------------------------------------
function getWorkspace(
_exclude: Set<string>,
options?: BuildSystemPromptOptions,
): string {
if (!options?.workspaceDir) return ''
return `<workspace>
Your working directory is: ${options.workspaceDir}
All filesystem tools operate relative to this directory.
</workspace>`
}
const promptSections: Record<string, PromptSectionFn> = {
intro: getIntro,
@@ -352,6 +385,9 @@ const promptSections: Record<string, PromptSectionFn> = {
'tool-reference': getCdpToolReference,
'external-integrations': getExternalIntegrations,
style: getStyle,
workspace: getWorkspace,
'scheduled-task': getScheduledTask,
'user-preferences': getUserPreferences,
'security-reminder': getSecurityReminder,
}
@@ -362,42 +398,16 @@ interface BuildSystemPromptOptions {
exclude?: string[]
isScheduledTask?: boolean
scheduledTaskWindowId?: number
workspaceDir?: string
}
export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
const exclude = new Set(options?.exclude)
const entries = Object.entries(promptSections).filter(
([key]) => !exclude.has(key),
)
const reminderIndex = entries.findIndex(
([key]) => key === 'security-reminder',
)
const sections = entries.map(([, fn]) => fn(exclude))
if (options?.isScheduledTask) {
const taskSection = getScheduledTaskInstructions(
options.scheduledTaskWindowId,
)
if (reminderIndex === -1) {
sections.push(taskSection)
} else {
sections.splice(reminderIndex, 0, taskSection)
}
}
if (options?.userSystemPrompt) {
const insertIdx = options?.isScheduledTask
? reminderIndex === -1
? sections.length
: reminderIndex + 1
: reminderIndex === -1
? sections.length
: reminderIndex
const userPreferencesSection = `<user_preferences>\n${options.userSystemPrompt}\n</user_preferences>`
sections.splice(insertIdx, 0, userPreferencesSection)
}
const sections = Object.entries(promptSections)
.filter(([key]) => !exclude.has(key))
.map(([, fn]) => fn(exclude, options))
.filter(Boolean)
return `<AGENT_PROMPT>\n${sections.join('\n\n')}\n</AGENT_PROMPT>`
}

View File

@@ -67,6 +67,7 @@ export class AiSdkAgent {
exclude: excludeSections,
isScheduledTask: config.resolvedConfig.isScheduledTask,
scheduledTaskWindowId: config.browserContext?.windowId,
workspaceDir: config.resolvedConfig.sessionExecutionDir,
})
// Configure compaction for context window management