@x-otto/prompt 0.1.0-alpha.21 → 0.1.0-alpha.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/prompts/lead-guidance.md +37 -52
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/local-provider.ts","../src/http-provider.ts","../src/prompt-factory.ts","../src/sub-agent-prompt.ts","../src/prompt-manager.ts","../src/constants.ts","../src/lesson-injection.ts","../src/tool-gated-sections.ts"],"mappings":";;;UAAiB,WAAA;EACf,GAAA;EACA,OAAA;AAAA;AAAA,UAGe,cAAA;EACf,IAAA,CAAK,GAAA,WAAc,OAAA,CAAQ,WAAA;EAC3B,IAAA,IAAQ,OAAA;AAAA;AAAA,UAGO,oBAAA;EACf,IAAA;EACA,OAAA;EACA,SAAA;AAAA;AAAA,UAGe,qBAAA;EACf,IAAA;EACA,OAAA;EACA,OAAA,SAAgB,OAAA;IAAU,KAAA;EAAA;EAC1B,UAAA,SAAmB,OAAA,CAAQ,MAAA;EAC3B,KAAA,UAAe,UAAA,CAAW,KAAA;EAC1B,SAAA;AAAA;AAAA,KAGU,qBAAA,GAAwB,oBAAA,GAAuB,qBAAA;AAAA,UAE1C,MAAA;EACf,IAAA;EACA,OAAA;EACA,OAAA;AAAA;;;;;AA9BF;;;;;AAKA;cCOa,mBAAA,YAA+B,cAAA;EAAA,iBACzB,OAAA;EAAA,iBACA,SAAA;cAEL,OAAA,EAAS,IAAA,CAAK,oBAAA;EAKpB,IAAA,CAAK,GAAA,WAAc,OAAA,CAAQ,WAAA;EAa3B,IAAA,CAAA,GAAQ,OAAA;EAAA,QAIA,IAAA;AAAA;;;cCpCH,kBAAA,YAA8B,cAAA;EAAA,iBACxB,OAAA;EAAA,iBACA,OAAA;EAAA,iBACA,UAAA;EAAA,iBACA,KAAA;EAAA,iBACA,SAAA;cAEL,OAAA,EAAS,IAAA,CAAK,qBAAA;EAQpB,IAAA,CAAK,GAAA,WAAc,OAAA,CAAQ,WAAA;EAe3B,IAAA,CAAA,GAAQ,OAAA;EAAA,QAQA,OAAA;AAAA;;;;;AFxChB;;iBGQgB,oBAAA,CAAqB,OAAA,EAAS,qBAAA,GAAwB,cAAA;;;cCFzD,mBAAA;AAAA,UA2BI,qBAAA;EACf,SAAA;EACA,eAAA;EACA,SAAA;AAAA;AJ/BF;;;;;;;;;AAAA,iBImDgB,sBAAA,CACd,OAAA,EAAS,cAAA,EACT,OAAA,GAAS,qBAAA;AAAA,iBA6BK,6BAAA,CACd,SAAA,UACA,OAAA,GAAS,qBAAA;;;;;iBAkBK,mBAAA,CACd,QAAA,EAAU,cAAA,IACV,SAAA,WACC,cAAA;;;KCrGS,YAAA;AAAA,KAEA,mBAAA;EAEN,MAAA;AAAA;EAGA,MAAA;EACA,SAAA;EACA,OAAA,GAAU,YAAA;EACV,OAAA,GAAU,qBAAA;EACV,UAAA;AAAA;AAAA,UAKW,oBAAA;EACf,QAAA,EAAU,cAAA;ELnBK;;;;;;;EK2Bf,iBAAA,GAAoB,WAAA;AAAA;;ALxBtB;;;;;;;;;UKqCiB,cAAA;EAAA,SACN,MAAA;EAAA,SACA,QAAA;EACT,MAAA;AAAA;AAAA,cAGW,aAAA;EAAA,iBACM,QAAA;EAAA,iBACA,iBAAA;EAAA,QACT,YAAA;cAEI,OAAA,EAAS,oBAAA;EAKf,IAAA,CAAK,GAAA,WAAc,OAAA;EL5CzB;;;;;;;;;EK0DM,QAAA,CAAS,mBAAA,GAAsB,WAAA,WAAsB,OAAA,CAAQ,cAAA;EAqB7D,cAAA,CACJ,OAAA,GAAS,mBAAA,EACT,mBAAA,GAAsB,WAAA,WACrB,OAAA;EL/EM;AAGX;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/local-provider.ts","../src/http-provider.ts","../src/prompt-factory.ts","../src/sub-agent-prompt.ts","../src/prompt-manager.ts","../src/constants.ts","../src/lesson-injection.ts","../src/tool-gated-sections.ts"],"mappings":";;;UAAiB,WAAA;EACf,GAAA;EACA,OAAA;AAAA;AAAA,UAGe,cAAA;EACf,IAAA,CAAK,GAAA,WAAc,OAAA,CAAQ,WAAA;EAC3B,IAAA,IAAQ,OAAA;AAAA;AAAA,UAGO,oBAAA;EACf,IAAA;EACA,OAAA;EACA,SAAA;AAAA;AAAA,UAGe,qBAAA;EACf,IAAA;EACA,OAAA;EACA,OAAA,SAAgB,OAAA;IAAU,KAAA;EAAA;EAC1B,UAAA,SAAmB,OAAA,CAAQ,MAAA;EAC3B,KAAA,UAAe,UAAA,CAAW,KAAA;EAC1B,SAAA;AAAA;AAAA,KAGU,qBAAA,GAAwB,oBAAA,GAAuB,qBAAA;AAAA,UAE1C,MAAA;EACf,IAAA;EACA,OAAA;EACA,OAAA;AAAA;;;;;AA9BF;;;;;AAKA;cCOa,mBAAA,YAA+B,cAAA;EAAA,iBACzB,OAAA;EAAA,iBACA,SAAA;cAEL,OAAA,EAAS,IAAA,CAAK,oBAAA;EAKpB,IAAA,CAAK,GAAA,WAAc,OAAA,CAAQ,WAAA;EAa3B,IAAA,CAAA,GAAQ,OAAA;EAAA,QAIA,IAAA;AAAA;;;cCpCH,kBAAA,YAA8B,cAAA;EAAA,iBACxB,OAAA;EAAA,iBACA,OAAA;EAAA,iBACA,UAAA;EAAA,iBACA,KAAA;EAAA,iBACA,SAAA;cAEL,OAAA,EAAS,IAAA,CAAK,qBAAA;EAQpB,IAAA,CAAK,GAAA,WAAc,OAAA,CAAQ,WAAA;EAe3B,IAAA,CAAA,GAAQ,OAAA;EAAA,QAQA,OAAA;AAAA;;;;;AFxChB;;iBGQgB,oBAAA,CAAqB,OAAA,EAAS,qBAAA,GAAwB,cAAA;;;cCFzD,mBAAA;AAAA,UA2BI,qBAAA;EACf,SAAA;EACA,eAAA;EACA,SAAA;AAAA;AJ/BF;;;;;;;;;AAAA,iBImDgB,sBAAA,CACd,OAAA,EAAS,cAAA,EACT,OAAA,GAAS,qBAAA;AAAA,iBA6BK,6BAAA,CACd,SAAA,UACA,OAAA,GAAS,qBAAA;;;;;iBAkBK,mBAAA,CACd,QAAA,EAAU,cAAA,IACV,SAAA,WACC,cAAA;;;KCrGS,YAAA;AAAA,KAEA,mBAAA;EAEN,MAAA;AAAA;EAGA,MAAA;EACA,SAAA;EACA,OAAA,GAAU,YAAA;EACV,OAAA,GAAU,qBAAA;EACV,UAAA;AAAA;AAAA,UAKW,oBAAA;EACf,QAAA,EAAU,cAAA;ELnBK;;;;;;;EK2Bf,iBAAA,GAAoB,WAAA;AAAA;;ALxBtB;;;;;;;;;UKqCiB,cAAA;EAAA,SACN,MAAA;EAAA,SACA,QAAA;EACT,MAAA;AAAA;AAAA,cAGW,aAAA;EAAA,iBACM,QAAA;EAAA,iBACA,iBAAA;EAAA,QACT,YAAA;cAEI,OAAA,EAAS,oBAAA;EAKf,IAAA,CAAK,GAAA,WAAc,OAAA;EL5CzB;;;;;;;;;EK0DM,QAAA,CAAS,mBAAA,GAAsB,WAAA,WAAsB,OAAA,CAAQ,cAAA;EAqB7D,cAAA,CACJ,OAAA,GAAS,mBAAA,EACT,mBAAA,GAAsB,WAAA,WACrB,OAAA;EL/EM;AAGX;;;;;EK4GE,oBAAA,CAAqB,OAAA,EAAS,mBAAA;EAUxB,0BAAA,CAAA,GAA8B,OAAA;AAAA;;;cC1IzB,mBAAA;;;iBCHG,oBAAA,CAAqB,OAAA,EAAS,MAAA,IAAU,QAAA;;;;;;APFxD;;;;;AAKA;;;;;;;;;;;;;;;iBQ4BgB,sBAAA,CACd,OAAA,UACA,mBAAA,EAAqB,WAAA,sBACrB,OAAA;ER1Be,+CQ4Bb,iBAAA,GAAoB,WAAA;EACpB,mBAAA,IAAuB,UAAA;EACvB,aAAA,IAAiB,UAAA;AAAA"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/local-provider.ts","../src/http-provider.ts","../src/prompt-factory.ts","../src/sub-agent-prompt.ts","../src/tool-gated-sections.ts","../src/prompt-manager.ts","../src/constants.ts","../src/lesson-injection.ts"],"sourcesContent":["import { readFile, readdir } from 'node:fs/promises'\nimport { join, relative, sep } from 'node:path'\nimport type { LocalProviderOptions, PromptEntry, PromptProvider } from './types'\n\n/**\n * Local file system prompt provider\n * Reads markdown files from the specified directory as prompt content\n *\n * Directory structure maps to key:\n * baseDir/lead-guidance.md → \"lead-guidance\"\n * baseDir/lesson/runtime-lessons.md → \"lesson/runtime-lessons\"\n */\nexport class LocalPromptProvider implements PromptProvider {\n private readonly baseDir: string\n private readonly extension: string\n\n constructor(options: Omit<LocalProviderOptions, 'type'>) {\n this.baseDir = options.baseDir\n this.extension = options.extension ?? '.md'\n }\n\n async load(key: string): Promise<PromptEntry | null> {\n const filePath = join(this.baseDir, `${key}${this.extension}`)\n try {\n const content = await readFile(filePath, 'utf-8')\n return {\n key,\n content: content.trim(),\n }\n } catch {\n return null\n }\n }\n\n async list(): Promise<string[]> {\n return this.walk(this.baseDir)\n }\n\n private async walk(dir: string): Promise<string[]> {\n const keys: string[] = []\n try {\n const entries = await readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n const subKeys = await this.walk(fullPath)\n keys.push(...subKeys)\n } else if (entry.name.endsWith(this.extension)) {\n const rel = relative(this.baseDir, fullPath)\n const key = rel.slice(0, -this.extension.length).split(sep).join('/')\n keys.push(key)\n }\n }\n } catch {}\n return keys\n }\n}\n","import type { RemoteProviderOptions, PromptEntry, PromptProvider } from './types'\n\nexport class HttpPromptProvider implements PromptProvider {\n private readonly baseUrl: string\n private readonly getAuth?: () => Promise<{ token: string }>\n private readonly getHeaders?: () => Promise<Record<string, string>>\n private readonly fetch: typeof globalThis.fetch\n private readonly timeoutMs: number\n\n constructor(options: Omit<RemoteProviderOptions, 'type'>) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '')\n this.getAuth = options.getAuth\n this.getHeaders = options.getHeaders\n this.fetch = options.fetch ?? globalThis.fetch\n this.timeoutMs = options.timeoutMs ?? 10_000\n }\n\n async load(key: string): Promise<PromptEntry | null> {\n try {\n const response = await this.request(`/prompts/${encodeURIComponent(key)}`)\n // Fail-soft: a single prompt load is best-effort. Any non-ok status\n // (404 or server error) and any network/parse failure resolves to null\n // (treated as not-found). list() deliberately differs and throws.\n if (!response.ok) {\n return null\n }\n return (await response.json()) as PromptEntry\n } catch {\n return null\n }\n }\n\n async list(): Promise<string[]> {\n const response = await this.request('/prompts')\n if (!response.ok) {\n throw new Error(`Remote prompt list failed: ${response.status}`)\n }\n return (await response.json()) as string[]\n }\n\n private async request(path: string, init?: RequestInit): Promise<Response> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n }\n\n if (this.getAuth) {\n const { token } = await this.getAuth()\n headers['Authorization'] = `Bearer ${token}`\n }\n if (this.getHeaders) {\n Object.assign(headers, await this.getHeaders())\n }\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), this.timeoutMs)\n\n try {\n return await this.fetch(`${this.baseUrl}${path}`, {\n ...init,\n headers: { ...headers, ...(init?.headers as Record<string, string>) },\n signal: controller.signal,\n })\n } finally {\n clearTimeout(timer)\n }\n }\n}\n","import { LocalPromptProvider } from './local-provider'\nimport { HttpPromptProvider } from './http-provider'\nimport type { PromptProvider, PromptProviderOptions } from './types'\n\n/**\n * Create the corresponding PromptProvider instance based on options.\n * Pattern is consistent with createPersistence in the persistence package.\n */\nexport function createPromptProvider(options: PromptProviderOptions): PromptProvider {\n switch (options.type) {\n case 'local':\n return new LocalPromptProvider({\n baseDir: options.baseDir,\n extension: options.extension,\n })\n case 'remote':\n return new HttpPromptProvider({\n baseUrl: options.baseUrl,\n getAuth: options.getAuth,\n getHeaders: options.getHeaders,\n fetch: options.fetch,\n timeoutMs: options.timeoutMs,\n })\n default:\n throw new Error(`Unknown prompt provider type: ${(options as { type: string }).type}`)\n }\n}\n","import type { AgentProfile } from '@x-otto/agent'\n\n// 向后兼容 re-export:AgentProfile 已迁移到 @x-otto/agent(agent 领域层),\n// 此 re-export 保持现有消费方 import 路径不变,可逐步迁移到直接从 @x-otto/agent 引用。\nexport type { AgentProfile } from '@x-otto/agent'\n\nexport const SUBAGENT_GUARDRAILS = [\n 'You are a sub-agent: do not interact with the user directly; do not expand the task scope; on completion, report your conclusion and any remaining risks.',\n].join('\\n')\n\n/**\n * 通用子代理 prompt 模板(模块级常量,单源)。\n *\n * 不外部化为 .md:子代理护栏是稳定的**内部行为契约**(几乎不变、非用户可编辑内容),\n * 不满足外部化门槛(对比 lead-guidance 235 行/可远程加载/非开发者可编辑)。外部化会引入\n * .md + .ts fallback 双源(同步调用点 assembleGenericSubAgentPrompt 需降级常量),\n * 为形式牺牲单源,是负优化(2026-08-14 复审回退)。\n */\nconst GENERIC_SUBAGENT_TEMPLATE = [\n 'You are a sub-agent \"{agent_name}\" in the Otto system.',\n 'You are responsible for executing one clearly scoped sub-task. Do not interact with users directly, handle overall planning, or further delegate to other sub-agents.',\n '',\n '## Current Task',\n '- Title: {task_title}',\n '- Description: {task_description}',\n '- File scope: {task_files}',\n '',\n '## Execution Requirements',\n '- Stay focused on the current task; do not expand scope.',\n '- Read relevant code before making changes.',\n '- After completion, provide a concise conclusion and note any remaining risks or blockers.',\n].join('\\n')\n\nexport interface SubAgentPromptContext {\n taskTitle?: string\n taskDescription?: string\n taskFiles?: string[]\n}\n\nfunction buildContextReplacements(context: SubAgentPromptContext): Record<string, string> {\n return {\n '{task_title}': context.taskTitle ?? 'not provided',\n '{task_description}': context.taskDescription ?? 'not provided',\n '{task_files}': context.taskFiles?.join(', ') ?? 'not provided',\n }\n}\n\n/**\n * Assemble the system prompt for a sub-agent based on agent profile and task context.\n * Replaces `{task_*}` placeholders.\n *\n * 未知占位符 fail-loud(不静默):替换完成后若模板仍有 `{word}` 残留(用户自定义 profile\n * 写了 `{task_*}` 之外的占位符),console.warn 提示——防模型看到未替换的占位符原文。\n * 只 warn 不抛错:内置三占位符有 'not provided' 缺省(buildContextReplacements),\n * 残留只可能来自自定义模板的笔误/扩展占位符,warn 足够暴露问题。\n */\nexport function assembleSubAgentPrompt(\n profile: AgentProfile,\n context: SubAgentPromptContext = {},\n): string {\n let prompt = profile.systemPromptTemplate\n\n const replacements = buildContextReplacements(context)\n\n for (const [placeholder, value] of Object.entries(replacements)) {\n prompt = prompt.replaceAll(placeholder, value)\n }\n\n warnUnresolvedPlaceholders(prompt, profile.name)\n return prompt\n}\n\n/** 扫描 `{word}` 形式残留占位符(未被子代理替换逻辑消费的),warn 提示。 */\nfunction warnUnresolvedPlaceholders(prompt: string, profileName: string): void {\n const unresolved = new Set<string>()\n for (const match of prompt.matchAll(/\\{([a-z][a-z0-9_]*)\\}/gi)) {\n unresolved.add(match[0])\n }\n if (unresolved.size > 0) {\n console.warn(\n `[prompt] sub-agent profile \"${profileName}\" template has unresolved placeholder(s): ` +\n `${[...unresolved].join(', ')} — expected {task_title}/{task_description}/{task_files}. ` +\n 'The literal placeholder will reach the model; fix the template or extend the replacement map.',\n )\n }\n}\n\nexport function assembleGenericSubAgentPrompt(\n agentName: string,\n context: SubAgentPromptContext = {},\n): string {\n const replacements = {\n ...buildContextReplacements(context),\n '{agent_name}': agentName,\n }\n\n let prompt = GENERIC_SUBAGENT_TEMPLATE\n for (const [placeholder, value] of Object.entries(replacements)) {\n prompt = prompt.replaceAll(placeholder, value)\n }\n return prompt\n}\n\n/**\n * Find the matching profile from agent-profiles data.\n * Prefers exact match by name, then fuzzy match by capabilities.\n */\nexport function resolveAgentProfile(\n profiles: AgentProfile[],\n agentName: string,\n): AgentProfile | undefined {\n const exact = profiles.find((p) => p.name === agentName)\n if (exact) {\n return exact\n }\n\n const lower = agentName.toLowerCase()\n return profiles.find(\n (p) =>\n lower.endsWith(p.name.toLowerCase()) ||\n p.capabilities.some((c) => lower.includes(c.toLowerCase())) ||\n p.taskTypes.some((t) => lower.includes(t.toLowerCase())),\n )\n}\n","/**\n * tool-gated-sections.ts\n *\n * RFC-101:lead-guidance.md 按已解析能力键集合分段门控。\n *\n * markdown 内联标记 `<!-- requires-capability: X -->...<!-- /requires-capability -->` 标注段落对\n * 特定**能力**(非具体工具名)的依赖。`gateByToolAvailability` 在组装期扫描并剔除能力不可用的\n * 段落(含标记本身),消除悬空引用(如 minimal/standard 工具集会话看到引用了不存在能力的操作指引)。\n *\n * RFC-057 §3 D9 / M94-01:`packages/prompt` 是能力层,不得硬编码宿主工具名字面量——本模块与\n * `lead-guidance.md` 只认识语义能力键(如 `delegation`/`task-observability`),能力键到具体工具名\n * 的映射表下沉到宿主层(`@x-otto/coding`)。\n *\n * 纯函数,不访问全局状态、不做 I/O(RFC-101 重要事项规则 1)。\n */\n\n// 能力键字符集:字母/数字/连字符(如 `task-observability`),首字符不能是连字符,\n// 避免与标记结尾的 ` -->` 产生贪婪匹配歧义。\nconst CAPABILITY_NAME = '[a-zA-Z0-9][a-zA-Z0-9-]*'\nconst REQUIRES_CAPABILITY_PATTERN = new RegExp(\n `<!--\\\\s*requires-capability:\\\\s*(${CAPABILITY_NAME})\\\\s*-->([\\\\s\\\\S]*?)<!--\\\\s*/requires-capability\\\\s*-->`,\n 'g',\n)\nconst OPEN_TAG_PATTERN = new RegExp(`<!--\\\\s*requires-capability:\\\\s*(${CAPABILITY_NAME})\\\\s*-->`, 'g')\n\n/**\n * 扫描 `content` 中的 `requires-capability` 标记区块:\n * - `enabledCapabilities` 为 `undefined` → 不做任何剔除,但仍清理标记语法(标记本身不应泄漏到最终 prompt)。\n * - 标记的能力键在 `enabledCapabilities` 中 → 保留区块内容,剔除标记。\n * - 标记的能力键不在 `enabledCapabilities` 中 → 整段剔除(含内容与标记)。\n * - 未闭合标记(无匹配 `/requires-capability`)→ 保守处理:不剔除任何内容,原样保留(含标记本身),\n * 并触发 `onUnclosedTag`(RFC-101 重要事项规则 6:system prompt 组装失败是致命故障,必须优雅降级)。\n */\nexport function gateByToolAvailability(\n content: string,\n enabledCapabilities: ReadonlySet<string> | undefined,\n options?: {\n /** 已知能力键全集(用于检测标记拼写错误/能力已重命名)。缺省时不做未知能力键检测。 */\n knownCapabilities?: ReadonlySet<string>\n onUnknownCapability?: (capability: string) => void\n onUnclosedTag?: (capability: string) => void\n },\n): string {\n const gated = content.replace(REQUIRES_CAPABILITY_PATTERN, (_match, capability: string, body: string) => {\n if (options?.knownCapabilities && !options.knownCapabilities.has(capability)) {\n options.onUnknownCapability?.(capability)\n }\n\n if (!enabledCapabilities) {\n return body\n }\n\n return enabledCapabilities.has(capability) ? body : ''\n })\n\n if (options?.onUnclosedTag) {\n // 剩余的开标记(未被上面成对匹配消费掉)即未闭合——原样保留在 gated 中,仅上报观测。\n for (const match of gated.matchAll(OPEN_TAG_PATTERN)) {\n options.onUnclosedTag(match[1] ?? '')\n }\n }\n\n return gated\n}\n","import {\n assembleGenericSubAgentPrompt,\n assembleSubAgentPrompt,\n type AgentProfile,\n type SubAgentPromptContext,\n} from './sub-agent-prompt'\nimport { gateByToolAvailability } from './tool-gated-sections'\nimport type { PromptProvider } from './types'\n\nexport type PromptPreset = 'main' | 'subagent'\n\nexport type PromptPresetOptions =\n | {\n preset?: 'main'\n }\n | {\n preset: 'subagent'\n agentName: string\n profile?: AgentProfile\n context?: SubAgentPromptContext\n promptMode?: 'append' | 'replace'\n }\n\nconst LEAD_GUIDANCE_KEY = 'lead-guidance'\n\nexport interface PromptManagerOptions {\n provider: PromptProvider\n /**\n * 已知能力键全集(终局审查 2026-07-18 S2 接线):传入后 `assemble()` 对 `lead-guidance.md`\n * 中的 `requires-capability` 标记做拼写/漂移检测——标记的能力键不在此集合中时经\n * `onUnknownCapability` 告警(console.warn),防止\"能力键改名/写错 → 段落静默消失\"。\n * 真源是宿主层 `CAPABILITY_TOOL_MAP`(@x-otto/coding capability-tool-map.ts)的键集合,\n * 经组装根注入(本层不依赖 coding,保持 RFC-057 D9 能力层边界)。缺省不检测(向后兼容)。\n */\n knownCapabilities?: ReadonlySet<string>\n}\n\n/**\n * 结构化 prompt 组装产物。将 stable 前缀(进 prompt cache)与 volatile 尾段(cache 断点之后)\n * 分离,让下游不再字符串拼接而是按 lane 取用。\n *\n * - `stable`:会话内稳定的 system 前缀(lead-guidance.md 经能力门控后的内容)。\n * - `volatile`:逐轮可变的尾段。PromptManager 当前不产生 volatile 内容(动态段由\n * @x-otto/runtime 的 hook 体系注入),此字段为结构预留——未来 PromptManager 可注册\n * section 时自然填充。\n * - `render()`:拼接 stable + volatile 的便捷方法(向后兼容 assemblePreset 的 string 返回)。\n */\nexport interface PromptAssembly {\n readonly stable: string\n readonly volatile: string\n render(): string\n}\n\nexport class PromptManager {\n private readonly provider: PromptProvider\n private readonly knownCapabilities?: ReadonlySet<string>\n private leadGuidance: string | null = null\n\n constructor(options: PromptManagerOptions) {\n this.provider = options.provider\n this.knownCapabilities = options.knownCapabilities\n }\n\n async load(key: string): Promise<string | null> {\n const entry = await this.provider.load(key)\n return entry?.content ?? null\n }\n\n /**\n * 组装 lead-guidance.md 为结构化 PromptAssembly。\n *\n * `enabledCapabilities`:本会话已解析的能力键集合(RFC-101,见 `tool-gated-sections.ts`)。传入时\n * 对 `lead-guidance.md` 中 `<!-- requires-capability: X -->` 标记的段落做门控——`X` 不在集合中则\n * 剔除该段落,消除悬空引用。缺省(`undefined`)保持向后兼容:不剔除任何段落内容,仅清理标记语法本身。\n * 能力键本身不是工具名(RFC-057 D9/M94-01:能力层不得硬编码宿主工具名)——具体映射由宿主层\n * (`@x-otto/coding`)的 `CAPABILITY_TOOL_MAP` 负责,本层只消费已转换好的能力键集合。\n */\n async assemble(enabledCapabilities?: ReadonlySet<string>): Promise<PromptAssembly> {\n if (this.leadGuidance === null) {\n this.leadGuidance = (await this.load(LEAD_GUIDANCE_KEY)) ?? ''\n }\n const stable = gateByToolAvailability(this.leadGuidance, enabledCapabilities, {\n knownCapabilities: this.knownCapabilities,\n onUnknownCapability: (capability) =>\n console.warn(\n `[prompt] lead-guidance.md references unknown capability \"${capability}\" ` +\n '(typo or renamed key in CAPABILITY_TOOL_MAP?) — the gated section is silently dropped',\n ),\n onUnclosedTag: (capability) =>\n console.warn(`[prompt] lead-guidance.md has an unclosed requires-capability tag: \"${capability}\"`),\n })\n return {\n stable,\n volatile: '',\n render: () => stable,\n }\n }\n\n async assemblePreset(\n options: PromptPresetOptions = { preset: 'main' },\n enabledCapabilities?: ReadonlySet<string>,\n ): Promise<string> {\n if (options.preset === 'subagent') {\n if (!options.profile) {\n return assembleGenericSubAgentPrompt(options.agentName, options.context)\n }\n if (options.profile.promptMode === 'append') {\n // S6(09-07 缓存正确性/体量修复):append 模式子代理复用主提示词基底(跨子代理\n // 共享缓存),但 Workflow Guidance(含 Background tasks 子节)与 Model Slot\n // Guidance 是主代理(orchestrator root)的编排指引——子代理规则是\"execute\n // directly, never re-delegate\"(Sub-task design 末条),这段对它是纯负担\n // (每轮多付 ~1K tokens)且与自身规则矛盾。剔除 delegation/task-observability\n // 能力键复用 RFC-101 既有 requires-capability 门控机制,零新机制。见\n // docs/review/system-prompt-compression-codex-cc-dsh-2026-09-07.md §0/§2.2。\n const subAgentCapabilities = enabledCapabilities\n ? new Set([...enabledCapabilities].filter((c) => c !== 'delegation' && c !== 'task-observability'))\n : enabledCapabilities\n return (await this.assemble(subAgentCapabilities)).render()\n }\n return assembleSubAgentPrompt(options.profile, options.context)\n }\n\n return (await this.assemble(enabledCapabilities)).render()\n }\n\n /**\n * append 模式子代理的**角色块** —— 渲染后的 profile 模板,由宿主注入为\n * **volatile system 尾段**(落在 prompt cache 断点之后)。append 的 system prompt 主体由\n * `assemblePreset` 返回基底(共享、进缓存),角色差异走此尾段——既得专门化又不击穿跨子代理缓存。\n * 仅 append 模式返回值;replace/缺省(模板已是 system prompt 主体)/无 profile 返回 undefined。\n */\n assembleSubAgentTail(options: PromptPresetOptions): string | undefined {\n if (options.preset !== 'subagent') {\n return undefined\n }\n if (options.profile && options.profile.promptMode === 'append') {\n return assembleSubAgentPrompt(options.profile, options.context)\n }\n return undefined\n }\n\n async loadRuntimeLessonsTemplate(): Promise<string | null> {\n return this.load('lesson/runtime-lessons')\n }\n}\n","import { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nexport const BUILTIN_PROMPTS_DIR = resolve(__dirname, '..', 'prompts')\n","import type { Lesson } from './types'\n\nexport function buildLessonInjection(lessons: Lesson[], template?: string): string {\n if (lessons.length === 0) {\n return ''\n }\n\n const lines = lessons.map(\n (l, i) => `${i + 1}. [${l.tags.join(', ')}] ${l.trigger} → ${l.insight}`,\n )\n\n const t =\n template ??\n `### Runtime Lessons\nThe following lessons were learned from previous sessions:\n{lessons}`\n return t.replace('{lessons}', lines.join('\\n'))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/local-provider.ts","../src/http-provider.ts","../src/prompt-factory.ts","../src/sub-agent-prompt.ts","../src/tool-gated-sections.ts","../src/prompt-manager.ts","../src/constants.ts","../src/lesson-injection.ts"],"sourcesContent":["import { readFile, readdir } from 'node:fs/promises'\nimport { join, relative, sep } from 'node:path'\nimport type { LocalProviderOptions, PromptEntry, PromptProvider } from './types'\n\n/**\n * Local file system prompt provider\n * Reads markdown files from the specified directory as prompt content\n *\n * Directory structure maps to key:\n * baseDir/lead-guidance.md → \"lead-guidance\"\n * baseDir/lesson/runtime-lessons.md → \"lesson/runtime-lessons\"\n */\nexport class LocalPromptProvider implements PromptProvider {\n private readonly baseDir: string\n private readonly extension: string\n\n constructor(options: Omit<LocalProviderOptions, 'type'>) {\n this.baseDir = options.baseDir\n this.extension = options.extension ?? '.md'\n }\n\n async load(key: string): Promise<PromptEntry | null> {\n const filePath = join(this.baseDir, `${key}${this.extension}`)\n try {\n const content = await readFile(filePath, 'utf-8')\n return {\n key,\n content: content.trim(),\n }\n } catch {\n return null\n }\n }\n\n async list(): Promise<string[]> {\n return this.walk(this.baseDir)\n }\n\n private async walk(dir: string): Promise<string[]> {\n const keys: string[] = []\n try {\n const entries = await readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n const subKeys = await this.walk(fullPath)\n keys.push(...subKeys)\n } else if (entry.name.endsWith(this.extension)) {\n const rel = relative(this.baseDir, fullPath)\n const key = rel.slice(0, -this.extension.length).split(sep).join('/')\n keys.push(key)\n }\n }\n } catch {}\n return keys\n }\n}\n","import type { RemoteProviderOptions, PromptEntry, PromptProvider } from './types'\n\nexport class HttpPromptProvider implements PromptProvider {\n private readonly baseUrl: string\n private readonly getAuth?: () => Promise<{ token: string }>\n private readonly getHeaders?: () => Promise<Record<string, string>>\n private readonly fetch: typeof globalThis.fetch\n private readonly timeoutMs: number\n\n constructor(options: Omit<RemoteProviderOptions, 'type'>) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '')\n this.getAuth = options.getAuth\n this.getHeaders = options.getHeaders\n this.fetch = options.fetch ?? globalThis.fetch\n this.timeoutMs = options.timeoutMs ?? 10_000\n }\n\n async load(key: string): Promise<PromptEntry | null> {\n try {\n const response = await this.request(`/prompts/${encodeURIComponent(key)}`)\n // Fail-soft: a single prompt load is best-effort. Any non-ok status\n // (404 or server error) and any network/parse failure resolves to null\n // (treated as not-found). list() deliberately differs and throws.\n if (!response.ok) {\n return null\n }\n return (await response.json()) as PromptEntry\n } catch {\n return null\n }\n }\n\n async list(): Promise<string[]> {\n const response = await this.request('/prompts')\n if (!response.ok) {\n throw new Error(`Remote prompt list failed: ${response.status}`)\n }\n return (await response.json()) as string[]\n }\n\n private async request(path: string, init?: RequestInit): Promise<Response> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n }\n\n if (this.getAuth) {\n const { token } = await this.getAuth()\n headers['Authorization'] = `Bearer ${token}`\n }\n if (this.getHeaders) {\n Object.assign(headers, await this.getHeaders())\n }\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), this.timeoutMs)\n\n try {\n return await this.fetch(`${this.baseUrl}${path}`, {\n ...init,\n headers: { ...headers, ...(init?.headers as Record<string, string>) },\n signal: controller.signal,\n })\n } finally {\n clearTimeout(timer)\n }\n }\n}\n","import { LocalPromptProvider } from './local-provider'\nimport { HttpPromptProvider } from './http-provider'\nimport type { PromptProvider, PromptProviderOptions } from './types'\n\n/**\n * Create the corresponding PromptProvider instance based on options.\n * Pattern is consistent with createPersistence in the persistence package.\n */\nexport function createPromptProvider(options: PromptProviderOptions): PromptProvider {\n switch (options.type) {\n case 'local':\n return new LocalPromptProvider({\n baseDir: options.baseDir,\n extension: options.extension,\n })\n case 'remote':\n return new HttpPromptProvider({\n baseUrl: options.baseUrl,\n getAuth: options.getAuth,\n getHeaders: options.getHeaders,\n fetch: options.fetch,\n timeoutMs: options.timeoutMs,\n })\n default:\n throw new Error(`Unknown prompt provider type: ${(options as { type: string }).type}`)\n }\n}\n","import type { AgentProfile } from '@x-otto/agent'\n\n// 向后兼容 re-export:AgentProfile 已迁移到 @x-otto/agent(agent 领域层),\n// 此 re-export 保持现有消费方 import 路径不变,可逐步迁移到直接从 @x-otto/agent 引用。\nexport type { AgentProfile } from '@x-otto/agent'\n\nexport const SUBAGENT_GUARDRAILS = [\n 'You are a sub-agent: do not interact with the user directly; do not expand the task scope; on completion, report your conclusion and any remaining risks.',\n].join('\\n')\n\n/**\n * 通用子代理 prompt 模板(模块级常量,单源)。\n *\n * 不外部化为 .md:子代理护栏是稳定的**内部行为契约**(几乎不变、非用户可编辑内容),\n * 不满足外部化门槛(对比 lead-guidance 235 行/可远程加载/非开发者可编辑)。外部化会引入\n * .md + .ts fallback 双源(同步调用点 assembleGenericSubAgentPrompt 需降级常量),\n * 为形式牺牲单源,是负优化(2026-08-14 复审回退)。\n */\nconst GENERIC_SUBAGENT_TEMPLATE = [\n 'You are a sub-agent \"{agent_name}\" in the Otto system.',\n 'You are responsible for executing one clearly scoped sub-task. Do not interact with users directly, handle overall planning, or further delegate to other sub-agents.',\n '',\n '## Current Task',\n '- Title: {task_title}',\n '- Description: {task_description}',\n '- File scope: {task_files}',\n '',\n '## Execution Requirements',\n '- Stay focused on the current task; do not expand scope.',\n '- Read relevant code before making changes.',\n '- After completion, provide a concise conclusion and note any remaining risks or blockers.',\n].join('\\n')\n\nexport interface SubAgentPromptContext {\n taskTitle?: string\n taskDescription?: string\n taskFiles?: string[]\n}\n\nfunction buildContextReplacements(context: SubAgentPromptContext): Record<string, string> {\n return {\n '{task_title}': context.taskTitle ?? 'not provided',\n '{task_description}': context.taskDescription ?? 'not provided',\n '{task_files}': context.taskFiles?.join(', ') ?? 'not provided',\n }\n}\n\n/**\n * Assemble the system prompt for a sub-agent based on agent profile and task context.\n * Replaces `{task_*}` placeholders.\n *\n * 未知占位符 fail-loud(不静默):替换完成后若模板仍有 `{word}` 残留(用户自定义 profile\n * 写了 `{task_*}` 之外的占位符),console.warn 提示——防模型看到未替换的占位符原文。\n * 只 warn 不抛错:内置三占位符有 'not provided' 缺省(buildContextReplacements),\n * 残留只可能来自自定义模板的笔误/扩展占位符,warn 足够暴露问题。\n */\nexport function assembleSubAgentPrompt(\n profile: AgentProfile,\n context: SubAgentPromptContext = {},\n): string {\n let prompt = profile.systemPromptTemplate\n\n const replacements = buildContextReplacements(context)\n\n for (const [placeholder, value] of Object.entries(replacements)) {\n prompt = prompt.replaceAll(placeholder, value)\n }\n\n warnUnresolvedPlaceholders(prompt, profile.name)\n return prompt\n}\n\n/** 扫描 `{word}` 形式残留占位符(未被子代理替换逻辑消费的),warn 提示。 */\nfunction warnUnresolvedPlaceholders(prompt: string, profileName: string): void {\n const unresolved = new Set<string>()\n for (const match of prompt.matchAll(/\\{([a-z][a-z0-9_]*)\\}/gi)) {\n unresolved.add(match[0])\n }\n if (unresolved.size > 0) {\n console.warn(\n `[prompt] sub-agent profile \"${profileName}\" template has unresolved placeholder(s): ` +\n `${[...unresolved].join(', ')} — expected {task_title}/{task_description}/{task_files}. ` +\n 'The literal placeholder will reach the model; fix the template or extend the replacement map.',\n )\n }\n}\n\nexport function assembleGenericSubAgentPrompt(\n agentName: string,\n context: SubAgentPromptContext = {},\n): string {\n const replacements = {\n ...buildContextReplacements(context),\n '{agent_name}': agentName,\n }\n\n let prompt = GENERIC_SUBAGENT_TEMPLATE\n for (const [placeholder, value] of Object.entries(replacements)) {\n prompt = prompt.replaceAll(placeholder, value)\n }\n return prompt\n}\n\n/**\n * Find the matching profile from agent-profiles data.\n * Prefers exact match by name, then fuzzy match by capabilities.\n */\nexport function resolveAgentProfile(\n profiles: AgentProfile[],\n agentName: string,\n): AgentProfile | undefined {\n const exact = profiles.find((p) => p.name === agentName)\n if (exact) {\n return exact\n }\n\n const lower = agentName.toLowerCase()\n return profiles.find(\n (p) =>\n lower.endsWith(p.name.toLowerCase()) ||\n p.capabilities.some((c) => lower.includes(c.toLowerCase())) ||\n p.taskTypes.some((t) => lower.includes(t.toLowerCase())),\n )\n}\n","/**\n * tool-gated-sections.ts\n *\n * RFC-101:lead-guidance.md 按已解析能力键集合分段门控。\n *\n * markdown 内联标记 `<!-- requires-capability: X -->...<!-- /requires-capability -->` 标注段落对\n * 特定**能力**(非具体工具名)的依赖。`gateByToolAvailability` 在组装期扫描并剔除能力不可用的\n * 段落(含标记本身),消除悬空引用(如 minimal/standard 工具集会话看到引用了不存在能力的操作指引)。\n *\n * RFC-057 §3 D9 / M94-01:`packages/prompt` 是能力层,不得硬编码宿主工具名字面量——本模块与\n * `lead-guidance.md` 只认识语义能力键(如 `delegation`/`task-observability`),能力键到具体工具名\n * 的映射表下沉到宿主层(`@x-otto/coding`)。\n *\n * 纯函数,不访问全局状态、不做 I/O(RFC-101 重要事项规则 1)。\n */\n\n// 能力键字符集:字母/数字/连字符(如 `task-observability`),首字符不能是连字符,\n// 避免与标记结尾的 ` -->` 产生贪婪匹配歧义。\nconst CAPABILITY_NAME = '[a-zA-Z0-9][a-zA-Z0-9-]*'\nconst REQUIRES_CAPABILITY_PATTERN = new RegExp(\n `<!--\\\\s*requires-capability:\\\\s*(${CAPABILITY_NAME})\\\\s*-->([\\\\s\\\\S]*?)<!--\\\\s*/requires-capability\\\\s*-->`,\n 'g',\n)\nconst OPEN_TAG_PATTERN = new RegExp(`<!--\\\\s*requires-capability:\\\\s*(${CAPABILITY_NAME})\\\\s*-->`, 'g')\n\n/**\n * 扫描 `content` 中的 `requires-capability` 标记区块:\n * - `enabledCapabilities` 为 `undefined` → 不做任何剔除,但仍清理标记语法(标记本身不应泄漏到最终 prompt)。\n * - 标记的能力键在 `enabledCapabilities` 中 → 保留区块内容,剔除标记。\n * - 标记的能力键不在 `enabledCapabilities` 中 → 整段剔除(含内容与标记)。\n * - 未闭合标记(无匹配 `/requires-capability`)→ 保守处理:不剔除任何内容,原样保留(含标记本身),\n * 并触发 `onUnclosedTag`(RFC-101 重要事项规则 6:system prompt 组装失败是致命故障,必须优雅降级)。\n */\nexport function gateByToolAvailability(\n content: string,\n enabledCapabilities: ReadonlySet<string> | undefined,\n options?: {\n /** 已知能力键全集(用于检测标记拼写错误/能力已重命名)。缺省时不做未知能力键检测。 */\n knownCapabilities?: ReadonlySet<string>\n onUnknownCapability?: (capability: string) => void\n onUnclosedTag?: (capability: string) => void\n },\n): string {\n const gated = content.replace(REQUIRES_CAPABILITY_PATTERN, (_match, capability: string, body: string) => {\n if (options?.knownCapabilities && !options.knownCapabilities.has(capability)) {\n options.onUnknownCapability?.(capability)\n }\n\n if (!enabledCapabilities) {\n return body\n }\n\n return enabledCapabilities.has(capability) ? body : ''\n })\n\n if (options?.onUnclosedTag) {\n // 剩余的开标记(未被上面成对匹配消费掉)即未闭合——原样保留在 gated 中,仅上报观测。\n for (const match of gated.matchAll(OPEN_TAG_PATTERN)) {\n options.onUnclosedTag(match[1] ?? '')\n }\n }\n\n return gated\n}\n","import {\n assembleGenericSubAgentPrompt,\n assembleSubAgentPrompt,\n type AgentProfile,\n type SubAgentPromptContext,\n} from './sub-agent-prompt'\nimport { gateByToolAvailability } from './tool-gated-sections'\nimport type { PromptProvider } from './types'\n\nexport type PromptPreset = 'main' | 'subagent'\n\nexport type PromptPresetOptions =\n | {\n preset?: 'main'\n }\n | {\n preset: 'subagent'\n agentName: string\n profile?: AgentProfile\n context?: SubAgentPromptContext\n promptMode?: 'append' | 'replace'\n }\n\nconst LEAD_GUIDANCE_KEY = 'lead-guidance'\n\nexport interface PromptManagerOptions {\n provider: PromptProvider\n /**\n * 已知能力键全集(终局审查 2026-07-18 S2 接线):传入后 `assemble()` 对 `lead-guidance.md`\n * 中的 `requires-capability` 标记做拼写/漂移检测——标记的能力键不在此集合中时经\n * `onUnknownCapability` 告警(console.warn),防止\"能力键改名/写错 → 段落静默消失\"。\n * 真源是宿主层 `CAPABILITY_TOOL_MAP`(@x-otto/coding capability-tool-map.ts)的键集合,\n * 经组装根注入(本层不依赖 coding,保持 RFC-057 D9 能力层边界)。缺省不检测(向后兼容)。\n */\n knownCapabilities?: ReadonlySet<string>\n}\n\n/**\n * 结构化 prompt 组装产物。将 stable 前缀(进 prompt cache)与 volatile 尾段(cache 断点之后)\n * 分离,让下游不再字符串拼接而是按 lane 取用。\n *\n * - `stable`:会话内稳定的 system 前缀(lead-guidance.md 经能力门控后的内容)。\n * - `volatile`:逐轮可变的尾段。PromptManager 当前不产生 volatile 内容(动态段由\n * @x-otto/runtime 的 hook 体系注入),此字段为结构预留——未来 PromptManager 可注册\n * section 时自然填充。\n * - `render()`:拼接 stable + volatile 的便捷方法(向后兼容 assemblePreset 的 string 返回)。\n */\nexport interface PromptAssembly {\n readonly stable: string\n readonly volatile: string\n render(): string\n}\n\nexport class PromptManager {\n private readonly provider: PromptProvider\n private readonly knownCapabilities?: ReadonlySet<string>\n private leadGuidance: string | null = null\n\n constructor(options: PromptManagerOptions) {\n this.provider = options.provider\n this.knownCapabilities = options.knownCapabilities\n }\n\n async load(key: string): Promise<string | null> {\n const entry = await this.provider.load(key)\n return entry?.content ?? null\n }\n\n /**\n * 组装 lead-guidance.md 为结构化 PromptAssembly。\n *\n * `enabledCapabilities`:本会话已解析的能力键集合(RFC-101,见 `tool-gated-sections.ts`)。传入时\n * 对 `lead-guidance.md` 中 `<!-- requires-capability: X -->` 标记的段落做门控——`X` 不在集合中则\n * 剔除该段落,消除悬空引用。缺省(`undefined`)保持向后兼容:不剔除任何段落内容,仅清理标记语法本身。\n * 能力键本身不是工具名(RFC-057 D9/M94-01:能力层不得硬编码宿主工具名)——具体映射由宿主层\n * (`@x-otto/coding`)的 `CAPABILITY_TOOL_MAP` 负责,本层只消费已转换好的能力键集合。\n */\n async assemble(enabledCapabilities?: ReadonlySet<string>): Promise<PromptAssembly> {\n if (this.leadGuidance === null) {\n this.leadGuidance = (await this.load(LEAD_GUIDANCE_KEY)) ?? ''\n }\n const stable = gateByToolAvailability(this.leadGuidance, enabledCapabilities, {\n knownCapabilities: this.knownCapabilities,\n onUnknownCapability: (capability) =>\n console.warn(\n `[prompt] lead-guidance.md references unknown capability \"${capability}\" ` +\n '(typo or renamed key in CAPABILITY_TOOL_MAP?) — the gated section is silently dropped',\n ),\n onUnclosedTag: (capability) =>\n console.warn(`[prompt] lead-guidance.md has an unclosed requires-capability tag: \"${capability}\"`),\n })\n return {\n stable,\n volatile: '',\n render: () => stable,\n }\n }\n\n async assemblePreset(\n options: PromptPresetOptions = { preset: 'main' },\n enabledCapabilities?: ReadonlySet<string>,\n ): Promise<string> {\n if (options.preset === 'subagent') {\n if (!options.profile) {\n return assembleGenericSubAgentPrompt(options.agentName, options.context)\n }\n if (options.profile.promptMode === 'append') {\n // S6(09-07 缓存正确性/体量修复):append 模式子代理复用主提示词基底(跨子代理\n // 共享缓存),但 Workflow Guidance(含 Background tasks 子节)是主代理\n // (orchestrator root)的编排指引——子代理规则是\"execute directly, never\n // re-delegate\"(Sub-task design 末条),这段对它是纯负担(每轮多付 ~1K tokens)\n // 且与自身规则矛盾。剔除 delegation/task-observability 能力键复用 RFC-101 既有\n // requires-capability 门控机制,零新机制。见\n // docs/review/system-prompt-compression-codex-cc-dsh-2026-09-07.md §0/§2.2;\n // Model Slot Guidance 独立小节已随 09-11 T3 压缩删除(内容下沉到 task_delegate\n // description),不再是本剔除逻辑覆盖的对象。\n const subAgentCapabilities = enabledCapabilities\n ? new Set([...enabledCapabilities].filter((c) => c !== 'delegation' && c !== 'task-observability'))\n : enabledCapabilities\n return (await this.assemble(subAgentCapabilities)).render()\n }\n return assembleSubAgentPrompt(options.profile, options.context)\n }\n\n return (await this.assemble(enabledCapabilities)).render()\n }\n\n /**\n * append 模式子代理的**角色块** —— 渲染后的 profile 模板,由宿主注入为\n * **volatile system 尾段**(落在 prompt cache 断点之后)。append 的 system prompt 主体由\n * `assemblePreset` 返回基底(共享、进缓存),角色差异走此尾段——既得专门化又不击穿跨子代理缓存。\n * 仅 append 模式返回值;replace/缺省(模板已是 system prompt 主体)/无 profile 返回 undefined。\n */\n assembleSubAgentTail(options: PromptPresetOptions): string | undefined {\n if (options.preset !== 'subagent') {\n return undefined\n }\n if (options.profile && options.profile.promptMode === 'append') {\n return assembleSubAgentPrompt(options.profile, options.context)\n }\n return undefined\n }\n\n async loadRuntimeLessonsTemplate(): Promise<string | null> {\n return this.load('lesson/runtime-lessons')\n }\n}\n","import { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nexport const BUILTIN_PROMPTS_DIR = resolve(__dirname, '..', 'prompts')\n","import type { Lesson } from './types'\n\nexport function buildLessonInjection(lessons: Lesson[], template?: string): string {\n if (lessons.length === 0) {\n return ''\n }\n\n const lines = lessons.map(\n (l, i) => `${i + 1}. [${l.tags.join(', ')}] ${l.trigger} → ${l.insight}`,\n )\n\n const t =\n template ??\n `### Runtime Lessons\nThe following lessons were learned from previous sessions:\n{lessons}`\n return t.replace('{lessons}', lines.join('\\n'))\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x-otto/prompt",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.22",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist",
|
|
6
6
|
"prompts"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"tag": "alpha"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@x-otto/agent": "0.1.0-alpha.
|
|
23
|
+
"@x-otto/agent": "0.1.0-alpha.22"
|
|
24
24
|
},
|
|
25
25
|
"private": false,
|
|
26
26
|
"scripts": {
|
package/prompts/lead-guidance.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
### Identity & Environment
|
|
2
2
|
|
|
3
|
-
You are otto, an AI assistant
|
|
3
|
+
You are otto, an AI assistant in the otto agent framework for software engineering, research, analysis, writing, and more. You have tools to read/write/search files, run shell commands, and delegate scoped sub-tasks (implementation, review, testing, research, debugging) to sub-agents that fork/inherit your context and tools — no fixed personas. Earlier turns in the same session remain available.
|
|
4
4
|
|
|
5
5
|
Runtime facts:
|
|
6
|
-
- Agent loop: receive → call tools → read results → continue until done.
|
|
7
|
-
-
|
|
8
|
-
- Working directory = the user's project root (given at session start); commands run in the user's default shell. Stay inside the project unless explicitly requested
|
|
9
|
-
- Tools run under a permission mode you do not control (fully autonomous → read-only). A blocked/denied call is a boundary decision, not an error: do not repeat it, and never route around it without approval
|
|
6
|
+
- Agent loop: receive → call tools → read results → continue until done. Batch independent read-only calls in one round; sequence only calls that need an earlier result.
|
|
7
|
+
- In long conversations, treat the current state of files as the source of truth, not early memories; re-read or search before editing when unsure.
|
|
8
|
+
- Working directory = the user's project root (given at session start); commands run in the user's default shell. Stay inside the project unless explicitly requested.
|
|
9
|
+
- Tools run under a permission mode you do not control (fully autonomous → read-only). A blocked/denied call is a boundary decision, not an error: do not repeat it, and never route around it without approval; find out why and narrow, ask, or switch tool.
|
|
10
10
|
- `<system-reminder>`-style tagged blocks in tool results or user messages are framework-injected state, not the user's words: never relay them as user instructions or mention their literal tags.
|
|
11
11
|
|
|
12
12
|
#### Whose Word Wins (Conflict Ladder)
|
|
@@ -53,51 +53,50 @@ Judge by reversibility and blast radius, not a keyword list:
|
|
|
53
53
|
|
|
54
54
|
### Output & Communication
|
|
55
55
|
|
|
56
|
-
- **Language**: match the user's language (Chinese in → Chinese out; English in → English out). If
|
|
57
|
-
- **Style**: lead with the answer or action, not the reasoning; one sentence beats three; no transitions
|
|
58
|
-
- **Code and locations**: fenced blocks with language identifiers; locations as `path:line` relative to project root
|
|
59
|
-
- **Tables**: only for short enumerable facts (file names, line numbers, pass/fail); reasoning
|
|
56
|
+
- **Language**: match the user's language (Chinese in → Chinese out; English in → English out). If UI locale differs, follow the message.
|
|
57
|
+
- **Style**: lead with the answer or action, not the reasoning; one sentence beats three; no transitions or filler. After acting, confirm briefly what was done. No emojis unless asked; no horizontal rules or separators.
|
|
58
|
+
- **Code and locations**: fenced blocks with language identifiers; locations as `path:line` relative to project root.
|
|
59
|
+
- **Tables**: only for short enumerable facts (file names, line numbers, pass/fail); reasoning in prose.
|
|
60
60
|
- **Review requests**: findings first, ordered by severity with file:line, then open questions; change summary last. No findings → say so and name residual risks.
|
|
61
|
-
- **Tool failures**: diagnose before retrying; never repeat the identical failed call. After 3 consecutive failures
|
|
62
|
-
- **Wrap-up**: verify in proportion to the change — the narrowest
|
|
63
|
-
- **Closing**: match the work — trivial/single-file → one line; substantial/multi-file → short summary (what changed, by file
|
|
61
|
+
- **Tool failures**: diagnose before retrying; never repeat the identical failed call. After 3 consecutive failures, change strategy. Escalate only when genuinely stuck.
|
|
62
|
+
- **Wrap-up**: verify in proportion to the change — the narrowest check that exercises what you touched, not the whole suite. Cannot verify (no test, cannot run) → say so and give the exact command; never imply success.
|
|
63
|
+
- **Closing**: match the work — trivial/single-file → one line; substantial/multi-file → short summary (what changed, by file, residual risks/follow-ups).
|
|
64
64
|
|
|
65
65
|
<!-- requires-capability: delegation -->
|
|
66
66
|
### Workflow Guidance
|
|
67
67
|
|
|
68
|
-
You are the primary agent (orchestrator root): you own task creation, execution, and quality, and you **delegate and parallelize** by default for non-trivial work.
|
|
68
|
+
You are the primary agent (orchestrator root): you own task creation, execution, and quality, and you **delegate and parallelize** by default for non-trivial work.
|
|
69
69
|
|
|
70
70
|
#### Routing by size
|
|
71
71
|
|
|
72
|
-
- **Direct** (single file,
|
|
73
|
-
- **Lightweight** (2-3 files, clear scope): brief inline plan, execute, delegate
|
|
74
|
-
- **Full pipeline** (cross-module, multi-angle, design needed): todo-list plan → parallel
|
|
72
|
+
- **Direct** (single file, <3 trivial steps): do it yourself — a known file or one grep.
|
|
73
|
+
- **Lightweight** (2-3 files, clear scope): brief inline plan, execute, delegate what parallelizes.
|
|
74
|
+
- **Full pipeline** (cross-module, multi-angle, design needed): todo-list plan → parallel sub-tasks → read-only review on critical slices → synthesize.
|
|
75
75
|
|
|
76
76
|
Pick the path silently; do not announce the tier.
|
|
77
77
|
|
|
78
78
|
#### Critical-path analysis (before any multi-part request)
|
|
79
79
|
|
|
80
|
-
1.
|
|
80
|
+
1. Separate **blocking** steps (the next action needs the result) from independent sidecars.
|
|
81
81
|
2. Do the immediate blocker yourself — never hand off the critical blocker and idle on it.
|
|
82
|
-
3. Spawn one sub-agent per independent step, **batched in a single round**
|
|
82
|
+
3. Spawn one sub-agent per independent step, **batched in a single round** whenever scopes don't overlap.
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
Beyond coding (research/audit/survey): one sub-agent per angle, file group, or claim. Implementation: split into **disjoint write scopes** and delegate each slice. Large-output work (big searches, log-heavy commands): delegate to keep your context clean.
|
|
85
85
|
|
|
86
|
-
#### Sub-task design
|
|
86
|
+
#### Sub-task design
|
|
87
87
|
|
|
88
88
|
- Each sub-task is **concrete, self-contained, bounded** — narrowed to the exact output you need next.
|
|
89
|
-
- **Synthesize the spec yourself
|
|
90
|
-
-
|
|
91
|
-
- Never duplicate delegated work: if you delegated a search/analysis, do not run it yourself or redo it after results return — while sub-agents run, do meaningful non-overlapping work (do not reflexively wait or poll); when results return, **synthesize** — read the findings, form the next concrete spec — then integrate or direct follow-up.
|
|
89
|
+
- **Synthesize the spec yourself** (file paths, line numbers, exactly what to do). Never "based on your findings" / "handle the rest" — that delegates understanding, not work.
|
|
90
|
+
- Never duplicate delegated work: if you delegated a search/analysis, don't redo it — while sub-agents run, do non-overlapping work; when results return, **synthesize** and direct follow-up.
|
|
92
91
|
- If you ARE a sub-agent (depth > 0): execute directly, never re-delegate.
|
|
93
92
|
|
|
94
93
|
#### Verification gate (you own it)
|
|
95
94
|
|
|
96
95
|
- **Non-trivial** = 3+ file edits, backend/API/data-model changes, infra/security changes, or anything cross-module. **Trivial** = rename/format, single-line fix, doc tweak → self-check only.
|
|
97
96
|
- Contract: non-trivial implementation on your turn — by you, a fork, or a sub-agent — gets independent verification **before you report completion**. You report to the user; you own the gate.
|
|
98
|
-
-
|
|
97
|
+
- Verification = **delegating a read-only review sub-task** (a fork/inherit sub-agent with a concrete review spec, or a declared review agent). The reviewer reads/searches/runs, never edits. No task-specific rubric → default: priority tags, verify-before-flag, PASS/FAIL/PARTIAL verdict.
|
|
99
98
|
- Findings → route the concrete items to the implementer, fix, re-verify. You drive the loop.
|
|
100
|
-
- A review finding is a **claim to verify against source, not an order** —
|
|
99
|
+
- A review finding is a **claim to verify against source, not an order** — trace the call chain before accepting a "missing/unwired/dead-code" verdict; confirm real issues, reject false positives with refuting evidence (file:line).
|
|
101
100
|
<!-- /requires-capability -->
|
|
102
101
|
|
|
103
102
|
<!-- requires-capability: task-observability -->
|
|
@@ -111,49 +110,35 @@ Applies beyond coding — research/audit/survey: one sub-agent per angle, file g
|
|
|
111
110
|
|
|
112
111
|
**Clarify → Plan → Execute → Verify → Conclude**, plan state carried in the todo list, not prose markers. Simple requests may collapse phases.
|
|
113
112
|
|
|
114
|
-
- **Clarify** before changing anything: read the relevant docs and code; ask only when genuinely ambiguous; no edits
|
|
115
|
-
- **Plan**: non-trivial work gets a todo-list plan kept current
|
|
116
|
-
- **Verify / Conclude**:
|
|
113
|
+
- **Clarify** before changing anything: read the relevant docs and code; ask only when genuinely ambiguous; no edits yet.
|
|
114
|
+
- **Plan**: non-trivial work gets a todo-list plan kept current; simple tasks stay inline.
|
|
115
|
+
- **Verify / Conclude**: no "done" without evidence; close in proportion to the work.
|
|
117
116
|
|
|
118
117
|
### User Interjection Triage
|
|
119
118
|
|
|
120
119
|
A new user message mid-task never means reflexively abandoning current work. Triage in order:
|
|
121
120
|
|
|
122
|
-
1. **
|
|
123
|
-
2. **
|
|
124
|
-
3. **
|
|
125
|
-
|
|
126
|
-
Every message either modifies the current task or becomes a tracked item; the user never has to repeat themselves.
|
|
121
|
+
1. **Related** to the current task → integrate now (adjust plan, update todos).
|
|
122
|
+
2. **Urgent** (explicit "urgent"/"now"/"stop" or something broken/blocking) → checkpoint, switch, return after resolution.
|
|
123
|
+
3. **Unrelated** → add a pending todo, continue, process the queue in priority order.
|
|
127
124
|
|
|
128
125
|
### Completion & Honesty
|
|
129
126
|
|
|
130
127
|
Completion is **unproven until verified against actual current state** — not intent, memory, or a plausible-looking answer.
|
|
131
128
|
|
|
132
129
|
- **Verify before claiming done.** Non-trivial work: the Verification gate passes first.
|
|
133
|
-
- **Report faithfully.** Tests fail → say so with the output. Skipped a verification step → say so. Never claim "all tests pass" over failing output; never present partial
|
|
134
|
-
- **Don't gold-plate.** Exactly what was asked — no unrequested features, refactors, speculative abstractions, comments, or error handling for impossible cases. Three similar lines beat a premature abstraction;
|
|
130
|
+
- **Report faithfully.** Tests fail → say so with the output. Skipped a verification step → say so. Never claim "all tests pass" over failing output; never present partial/broken work as done. State confirmed passes plainly.
|
|
131
|
+
- **Don't gold-plate.** Exactly what was asked — no unrequested features, refactors, speculative abstractions, comments, or error handling for impossible cases. Three similar lines beat a premature abstraction; do not leave work half-done.
|
|
135
132
|
- **Read before you edit; don't guess.** Never modify code you have not read. When an approach fails, diagnose (read the error, check assumptions) before switching — no blind retries, no abandoning a viable approach after one failure.
|
|
136
133
|
- **Persist.** Keep going until the task is resolved end-to-end this turn, unless the user asked only for a plan/answer or is blocked on a decision only they can make.
|
|
137
134
|
|
|
138
135
|
### Budget & Wrap-up
|
|
139
136
|
|
|
140
|
-
You work under budgets (tool-call turns, session cost) and a finite context window. Four
|
|
137
|
+
You work under budgets (tool-call turns, session cost) and a finite context window. Four signals:
|
|
141
138
|
|
|
142
|
-
- **Context near the
|
|
143
|
-
- **Tool-turn / cost counters** (e.g. `<engine_budget dimension="tokens">`
|
|
144
|
-
- **Todo continuation rounds** (e.g. `<engine_continuation round="2" cap="12">`):
|
|
145
|
-
- **Explicit wrap-up instruction**: no new tool work
|
|
139
|
+
- **Context near the limit**: the engine compacts and continues — never stop unfinished work for a high counter.
|
|
140
|
+
- **Tool-turn / cost counters** (e.g. `<engine_budget dimension="tokens">`): a bounded-resource signal — finish existing work over new lines; bounds *scope expansion*, not task completion.
|
|
141
|
+
- **Todo continuation rounds** (e.g. `<engine_continuation round="2" cap="12">`): auto-continues unfinished todos — a soft admission budget, not a completion bound.
|
|
142
|
+
- **Explicit wrap-up instruction**: no new tool work — summarize progress, convert what remains/blocked into tracked todos, give a clear next step.
|
|
146
143
|
|
|
147
144
|
**Never** mark work complete because a budget ran out — report the true state.
|
|
148
|
-
|
|
149
|
-
<!-- requires-capability: delegation -->
|
|
150
|
-
### Model Slot Guidance
|
|
151
|
-
|
|
152
|
-
Slots for dispatched sub-tasks: `normal` standard execution · `thinking` deep reasoning · `compact` lightweight/fast (search, quick lookups; cheaper — not a summarization slot) · `critique` code review and verification (same tier as `thinking`; use for quality-critical review passes) · `vision` image understanding.
|
|
153
|
-
<!-- /requires-capability -->
|
|
154
|
-
|
|
155
|
-
<!-- requires-capability: file-state-refresh -->
|
|
156
|
-
### File State Refresh
|
|
157
|
-
|
|
158
|
-
When the conversation has grown long and your context may have missed file changes, use the file-state refresh tool to refresh the workspace state.
|
|
159
|
-
<!-- /requires-capability -->
|