History

Enhance system prompt context handling by adding support for environment-specific information such as PostHog project ID, GitHub token, and Vercel token. Update related interfaces and functions to accommodate the new context parameter, improving the agent's ability to utilize contextual data during task execution.

Rhys Sullivan committed 9 months ago

Commit 66259751 parent 7a5c9454 files · +55 −7Browse files

Unified · Split

4 files
  1. apps/server/src/index.ts +14 −1
  2. apps/server/src/task-runner.ts +3 −0
  3. packages/core/src/agent.ts +29 −3
  4. packages/core/src/discovery.ts +9 −3
apps/server/src/index.ts+14−1
@@ -189,7 +189,20 @@ console.log("Loading tools..."); · 188 unmodified lines
189const tools = await loadTools();189const tools = await loadTools();
190const model = createPiAiModel({ apiKey });190const model = createPiAiModel({ apiKey });
191 191
192const app = createApp({ tools, model });192// Build context for the system prompt
193const contextLines: string[] = [];
194if (POSTHOG_PROJECT_ID) {
195 contextLines.push(`- PostHog project ID: ${POSTHOG_PROJECT_ID} (use this for PostHog API calls that require a project_id)`);
196}
197if (GITHUB_TOKEN) {
198 contextLines.push(`- GitHub: authenticated via personal access token`);
199}
200if (VERCEL_TOKEN) {
201 contextLines.push(`- Vercel: authenticated via personal token`);
202}
203const context = contextLines.length > 0 ? contextLines.join("\n") : undefined;
204
205const app = createApp({ tools, model, context });
193 206
194app.listen(PORT);207app.listen(PORT);
195 208
apps/server/src/task-runner.ts+3−0
@@ -17,6 +17,8 @@ import { · 16 unmodified lines
17export interface TaskRunnerOptions {17export interface TaskRunnerOptions {
18 readonly tools: ToolTree;18 readonly tools: ToolTree;
19 readonly model: LanguageModel;19 readonly model: LanguageModel;
20 /** Additional context for the system prompt (project IDs, org info, etc.) */
21 readonly context?: string | undefined;
20}22}
21 23
22/**24/**
@@ -36,6 +38,7 @@ export async function runTask( · 13 unmodified lines
36 const agent = createAgent({38 const agent = createAgent({
37 tools: options.tools,39 tools: options.tools,
38 model: options.model,40 model: options.model,
41 context: options.context,
39 requestApproval: (request: ApprovalRequest): Promise<ApprovalDecision> => {42 requestApproval: (request: ApprovalRequest): Promise<ApprovalDecision> => {
40 // Emit the approval_request event so SSE subscribers see it43 // Emit the approval_request event so SSE subscribers see it
41 emitTaskEvent(taskId, {44 emitTaskEvent(taskId, {
packages/core/src/agent.ts+29−3
@@ -79,6 +79,12 @@ export interface AgentOptions { · 78 unmodified lines
79 * Defaults to 50.79 * Defaults to 50.
80 */80 */
81 readonly discoveryThreshold?: number | undefined;81 readonly discoveryThreshold?: number | undefined;
82 /**
83 * Additional context to include in the system prompt.
84 * Use this to provide environment-specific info like project IDs,
85 * usernames, organization names, etc. that the agent may need.
86 */
87 readonly context?: string | undefined;
82}88}
83 89
84export interface AgentResult {90export interface AgentResult {
@@ -181,7 +187,7 @@ function prepareToolsForAgent( · 96 unmodified lines
181// System prompt187// System prompt
182// ---------------------------------------------------------------------------188// ---------------------------------------------------------------------------
183 189
184function buildSystemPrompt(guidance: string, discoveryMode: boolean): string {190function buildSystemPrompt(guidance: string, discoveryMode: boolean, context?: string): string {
185 const discoveryInstructions = discoveryMode191 const discoveryInstructions = discoveryMode
186 ? `192 ? `
187## Tool Discovery193## Tool Discovery
@@ -222,13 +228,21 @@ for (const issue of issues) { · 34 unmodified lines
222 228
223return { closed: results.length, issues: results };229return { closed: results.length, issues: results };
224\`\`\`230\`\`\`
231`
232 : "";
233
234 const contextSection = context
235 ? `
236## Context
237
238${context}
225`239`
226 : "";240 : "";
227 241
228 return `You are an AI assistant that executes tasks by generating TypeScript code.242 return `You are an AI assistant that executes tasks by generating TypeScript code.
229 243
230You have access to a set of tools via the \`tools\` object. When the user asks you to do something, generate TypeScript code that calls these tools to accomplish the task.244You have access to a set of tools via the \`tools\` object. When the user asks you to do something, generate TypeScript code that calls these tools to accomplish the task.
231 245${contextSection}
232## Available Tools246## Available Tools
233 247
234${guidance}248${guidance}
@@ -241,6 +255,17 @@ ${discoveryInstructions} · 6 unmodified lines
241- No \`fetch\`, \`process\`, \`require\`, or \`import\` — use tools for all external interactions255- No \`fetch\`, \`process\`, \`require\`, or \`import\` — use tools for all external interactions
242- Write clean, straightforward TypeScript — use for loops, map, filter, Promise.all as needed256- Write clean, straightforward TypeScript — use for loops, map, filter, Promise.all as needed
243- Handle errors gracefully with try/catch — if a tool call fails or is denied, continue with remaining work257- Handle errors gracefully with try/catch — if a tool call fails or is denied, continue with remaining work
258- **Pagination:** If a list endpoint's return type has \`pagination\`, \`next\`, \`cursor\`, or similar fields, the results may be paginated. Check \`pagination.next\` (or equivalent) in the response — if it's not null, there are more pages. Paginate when the user needs complete results. Example pattern:
259 \`\`\`ts
260 let allItems = [];
261 let cursor = undefined;
262 while (true) {
263 const resp = await tools.api.list({ limit: 100, since: cursor });
264 allItems.push(...resp.items);
265 if (!resp.pagination?.next) break;
266 cursor = resp.pagination.next;
267 }
268 \`\`\`
244- Return a structured result from your script, then summarize what happened269- Return a structured result from your script, then summarize what happened
245- Be concise and accurate — base your response on actual tool results, not assumptions`;270- Be concise and accurate — base your response on actual tool results, not assumptions`;
246}271}
@@ -261,6 +286,7 @@ export function createAgent(options: AgentOptions): { · 14 unmodified lines
261 maxCodeRuns = 100,286 maxCodeRuns = 100,
262 timeoutMs = 30_000,287 timeoutMs = 30_000,
263 discoveryThreshold = DISCOVERY_THRESHOLD,288 discoveryThreshold = DISCOVERY_THRESHOLD,
289 context,
264 } = options;290 } = options;
265 291
266 const { sandboxTools, promptGuidance, toolDeclarations, discoveryMode } =292 const { sandboxTools, promptGuidance, toolDeclarations, discoveryMode } =
@@ -278,7 +304,7 @@ export function createAgent(options: AgentOptions): { · 11 unmodified lines
278 304
279 return {305 return {
280 async run(prompt: string): Promise<AgentResult> {306 async run(prompt: string): Promise<AgentResult> {
281 const systemPrompt = buildSystemPrompt(promptGuidance, discoveryMode);307 const systemPrompt = buildSystemPrompt(promptGuidance, discoveryMode, context);
282 const runs: CodeRun[] = [];308 const runs: CodeRun[] = [];
283 const allReceipts: ToolCallReceipt[] = [];309 const allReceipts: ToolCallReceipt[] = [];
284 310
packages/core/src/discovery.ts+9−3
@@ -48,13 +48,19 @@ export function buildToolIndex(tree: ToolTree): ToolIndexEntry[] { · 47 unmodified lines
48 const path = prefix ? `${prefix}.${key}` : key;48 const path = prefix ? `${prefix}.${key}` : key;
49 if (isToolDefinition(value)) {49 if (isToolDefinition(value)) {
50 const tool = value as ToolDefinition;50 const tool = value as ToolDefinition;
51 const returnsType = getReturnsTypeString(tool);
52 // Detect paginated endpoints from return type
53 const isPaginated = /\bpagination\b|\bnext_cursor\b|\bhas_more\b|\bnextPage\b/.test(returnsType);
54 const description = isPaginated
55 ? `${tool.description} (⚠️ paginated — loop until pagination.next is null to get all results)`
56 : tool.description;
51 entries.push({57 entries.push({
52 path,58 path,
53 description: tool.description,59 description,
54 approval: tool.approval,60 approval: tool.approval,
55 argsType: getArgsTypeString(tool),61 argsType: getArgsTypeString(tool),
56 returnsType: getReturnsTypeString(tool),62 returnsType,
57 searchText: `${path} ${tool.description}`.toLowerCase(),63 searchText: `${path} ${description}`.toLowerCase(),
58 });64 });
59 } else {65 } else {
60 walk(value as ToolTree, path);66 walk(value as ToolTree, path);