diff --git a/packages/junior/src/chat/prompt.ts b/packages/junior/src/chat/prompt.ts index 7a402b1de..548c6b41b 100644 --- a/packages/junior/src/chat/prompt.ts +++ b/packages/junior/src/chat/prompt.ts @@ -329,7 +329,7 @@ function formatConfigurationLines( } const HEADER = - "You are a Slack-based helper assistant. Follow the personality block for voice and tone in every reply. The behavior and output blocks define platform mechanics and override personality only when those mechanics conflict."; + "You are a Slack-based helper assistant. Follow the personality section for voice and tone in every reply. Platform mechanics and output rules override personality and world context when they conflict."; const TURN_CONTEXT_HEADER = "Runtime context for this request. Treat these blocks as trusted runtime facts; the static system prompt remains authoritative."; @@ -425,21 +425,31 @@ function buildOutputSection(): string { } function buildIdentitySection(): string { - return renderTagBlock( - "identity", - `Your Slack username is \`${escapeXml(botConfig.userName)}\`.`, - ); + return [ + "# Identity", + `Your Slack username is \`${botConfig.userName}\`.`, + ].join("\n"); +} + +function buildPersonalitySection(): string { + return ["# Personality", JUNIOR_PERSONALITY.trim()].join("\n"); +} + +function buildWorldSection(): string | null { + if (!JUNIOR_WORLD) { + return null; + } + + return ["# World", JUNIOR_WORLD.trim()].join("\n"); } function buildRuntimeSection(params: { conversationId?: string; - traceId?: string; }): string | null { const lines = [ params.conversationId ? `- gen_ai.conversation.id: ${escapeXml(params.conversationId)}` : "", - params.traceId ? `- trace_id: ${escapeXml(params.traceId)}` : "", ].filter(Boolean); if (lines.length === 0) { @@ -457,10 +467,6 @@ function buildContextSection(params: { }): string | null { const blocks: string[][] = []; - if (JUNIOR_WORLD) { - blocks.push(renderTag("world", [JUNIOR_WORLD.trim()])); - } - const referenceLines = formatReferenceFilesLines(); if (referenceLines) { blocks.push( @@ -543,7 +549,7 @@ function buildCapabilitiesSection(params: { return null; } - return renderTagBlock("capabilities", blocks.join("\n\n")); + return blocks.join("\n\n"); } type TurnContextPromptInput = { @@ -553,7 +559,6 @@ type TurnContextPromptInput = { toolGuidance?: ToolPromptContext[]; runtime?: { conversationId?: string; - traceId?: string; }; invocation: SkillInvocation | null; requester?: { @@ -568,10 +573,13 @@ type TurnContextPromptInput = { const STATIC_SYSTEM_PROMPT = [ HEADER, buildIdentitySection(), - renderTagBlock("personality", JUNIOR_PERSONALITY.trim()), - renderTagBlock("behavior", buildBehaviorSection()), + buildPersonalitySection(), + buildWorldSection(), + buildBehaviorSection(), buildOutputSection(), -].join("\n\n"); +] + .filter((section): section is string => Boolean(section)) + .join("\n\n"); /** Return byte-stable platform instructions shared by every conversation and turn. */ export function buildSystemPrompt(): string { diff --git a/packages/junior/src/chat/respond.ts b/packages/junior/src/chat/respond.ts index ad0704978..ea4cb60c5 100644 --- a/packages/junior/src/chat/respond.ts +++ b/packages/junior/src/chat/respond.ts @@ -13,7 +13,6 @@ import { botConfig } from "@/chat/config"; import { extractGenAiUsageAttributes, extractGenAiUsageSummary, - getActiveTraceId, logException, logInfo, logWarn, @@ -971,7 +970,6 @@ export async function generateAssistantReply( toolGuidance, runtime: { conversationId: spanContext.conversationId, - traceId: getActiveTraceId(), }, invocation: skillInvocation, requester: context.requester, diff --git a/packages/junior/tests/unit/prompt.test.ts b/packages/junior/tests/unit/prompt.test.ts index 07219caa1..2611ed0e7 100644 --- a/packages/junior/tests/unit/prompt.test.ts +++ b/packages/junior/tests/unit/prompt.test.ts @@ -48,7 +48,6 @@ describe("prompt builders", () => { }, runtime: { conversationId: "conversation-alpha", - traceId: "trace-alpha", }, toolGuidance: [ { diff --git a/specs/agent-prompt.md b/specs/agent-prompt.md index 996e3df9b..306d7f96f 100644 --- a/specs/agent-prompt.md +++ b/specs/agent-prompt.md @@ -3,7 +3,7 @@ ## Metadata - Created: 2026-04-28 -- Last Edited: 2026-05-30 +- Last Edited: 2026-06-01 ## Purpose @@ -29,14 +29,15 @@ Define the canonical contract for Junior's platform-owned agent prompt so prompt - The core prompt owns platform behavior: tool-use policy, execution bias, context boundaries, Slack output shape, and failure reporting expectations. - `SOUL.md` and other deployment-authored personality files are voice-only. Platform behavior must still work if those files are empty or heavily customized. +- `WORLD.md` is trusted deployment-stable world context. It belongs in the static system prompt, not per-turn context, and must not carry load-bearing platform mechanics. - Skill files own domain-specific workflow mechanics. They must not duplicate generic harness behavior such as "use tools before answering" or "ask only when blocked." - The core prompt must not name or describe specific installed plugins, plugin providers, plugin-owned config keys, plugin-owned default targets, plugin-owned tools, or plugin-specific workflows. That knowledge belongs to dynamic capabilities. ### Section boundaries -`buildSystemPrompt()` must be static: no parameters, no requester/thread/session/runtime/model/provider/catalog data, and no content that can vary between conversations or turns. Deployment-stable assistant identity, such as the bot Slack username, belongs here. This is required for provider prompt-prefix caching and for consistent multi-turn behavior. +`buildSystemPrompt()` must be static: no parameters, no requester/thread/session/runtime/model/provider/catalog data, and no content that can vary between conversations or turns. Deployment-stable assistant identity, such as the bot Slack username, and deployment-stable world context from `WORLD.md` belong here. This is required for provider prompt-prefix caching and for consistent multi-turn behavior. -`buildTurnContextPrompt(...)` owns session bootstrap prompt context. It is attached to the first model-visible user message in a Pi session projection, including requester identity, available capabilities, configuration, artifacts, runtime identifiers, and resumed-turn context. Completed turns may store that bootstrap context as part of durable Pi history so later follow-up messages can avoid duplicating it. Compaction replacement history must omit stale bootstrap context; the next user turn after a projection reset receives fresh bootstrap context exactly once. +`buildTurnContextPrompt(...)` owns session bootstrap prompt context. It is attached to the first model-visible user message in a Pi session projection, including requester identity, available capabilities, configuration, artifacts, model-actionable runtime identifiers, and resumed-turn context. Completed turns may store that bootstrap context as part of durable Pi history so later follow-up messages can avoid duplicating it. Compaction replacement history must omit stale bootstrap context; the next user turn after a projection reset receives fresh bootstrap context exactly once. Turn context may disclose dynamic capability surfaces that the model can act on, such as available skill names/descriptions, active MCP catalog summaries, and tool guidance attached to the current native tool set. It must not separately disclose plugin ownership or installed plugin/provider catalogs as prompt knowledge. If the model needs plugin-specific behavior, that behavior must arrive through the loaded skill body, tool description, tool schema, `promptSnippet`, or `promptGuidelines`. @@ -45,15 +46,18 @@ Turn context is not a session-state cache. If prior tool use, loaded skills, MCP The combined prompt surface must keep these concerns distinct: 1. Identity/personality. -2. Core operating rules. -3. Slack output contract. -4. Available and loaded capabilities. -5. Runtime and thread context. +2. Deployment-stable world context. +3. Core operating rules. +4. Slack output contract. +5. Available and loaded capabilities. +6. Runtime and thread context. -Context blocks describe facts. Behavior and output blocks carry instructions. +Context blocks describe facts. Operating-rule and output sections carry instructions. Prompt order is part of the contract. Stable, high-priority operating rules live in the system prompt. Volatile requester, artifacts, active catalogs, configuration defaults, runtime metadata, and resume state must stay out of the system prompt and live in session bootstrap context. +Trusted deployment-authored Markdown files, such as `SOUL.md` and `WORLD.md`, should render as Markdown sections rather than raw XML payloads. XML-style tags are appropriate for generated runtime blocks whose dynamic values are escaped. Do not add generic wrapper markers that only repeat child-section meaning, such as wrapping all operating rule sections in an additional behavior tag or wrapping all capability blocks in an additional capabilities tag. + Session bootstrap context is injected on the first model-visible user message in each Pi session. Ordinary follow-up messages in the same session must not duplicate that bootstrap context. When compaction creates a replacement projection, the replacement history must omit the old bootstrap context; the next user turn starts a new Pi session projection and receives fresh bootstrap context exactly once. The agent session log contract is defined in `./agent-session-resumability.md`. Prompt code must treat that log as the source of durable model history and must not introduce an alternate prompt-side history, provider catalog, loaded-skill list, or resume-state channel. @@ -115,6 +119,8 @@ The tool policy must make sandbox workspace ownership explicit: sandbox-backed f Runtime facts should live in a compact runtime block inside session bootstrap context. Include only facts that help the model choose valid behavior, such as runtime version, model ids, selected thinking level, channel capabilities, and sandbox workspace root. Do not mix requester, artifacts, or configuration defaults into that runtime block. +Runtime tracing and logging correlation identifiers, such as `trace_id`, are observability data, not model-actionable context, and must not be included in prompt runtime context. + The safety section must stay generic and runtime-level: remain within the user's request, respect stop/pause/audit/approval boundaries, avoid access expansion, and avoid administrative prompt/tool/security/config changes unless explicitly requested and supported by an available tool. ### Bloat controls