@caupulican/pi-adaptative 0.80.25 → 0.80.27
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/CHANGELOG.md +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts +4 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +104 -18
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/builtin.d.ts +3 -0
- package/dist/core/extensions/builtin.d.ts.map +1 -0
- package/dist/core/extensions/builtin.js +247 -0
- package/dist/core/extensions/builtin.js.map +1 -0
- package/dist/core/resource-loader.d.ts +15 -2
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +231 -155
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +20 -20
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/builtin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmK3D,wBAAgB,oCAAoC,CACnD,cAAc,EAAE,MAAM,MAAM,EAAE,EAC9B,WAAW,EAAE,MAAM,QAAQ,EAAE,GAC3B,cAAc,EAAE,CAqHlB","sourcesContent":["import type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport { Type } from \"typebox\";\nimport { estimateTokens } from \"../compaction/compaction.ts\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from \"../messages.ts\";\nimport type { CompactionEntry, SessionEntry } from \"../session-manager.ts\";\nimport type { ToolDefinition, ToolInfo } from \"./types.ts\";\n\ntype ContextAuditParams = {\n\tmaxItems?: number;\n\tminTokens?: number;\n\tquery?: string;\n\tincludePreviews?: boolean;\n};\n\ntype AuditRow = {\n\tkind: string;\n\trole?: string;\n\tentryId?: string;\n\ttimestamp?: string;\n\ttokens: number;\n\tchars: number;\n\tlabel: string;\n\tpreview: string;\n};\n\nconst DEFAULT_MAX_ITEMS = 40;\nconst MAX_MAX_ITEMS = 200;\nconst DEFAULT_PREVIEW_CHARS = 220;\nconst MAX_PREVIEW_CHARS = 600;\n\nfunction estimateTextTokens(text: string): number {\n\treturn Math.ceil(text.length / 4);\n}\n\nfunction cap(text: string, limit = DEFAULT_PREVIEW_CHARS): string {\n\tconst compact = text.replace(/\\s+/g, \" \").trim();\n\treturn compact.length > limit ? `${compact.slice(0, Math.max(0, limit - 1))}…` : compact;\n}\n\nfunction contentText(content: unknown): string {\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.map((part) => {\n\t\t\tif (!part || typeof part !== \"object\") return \"\";\n\t\t\tconst typed = part as { type?: string; text?: string; thinking?: string; name?: string; arguments?: unknown };\n\t\t\tif (typed.type === \"text\") return typed.text || \"\";\n\t\t\tif (typed.type === \"thinking\") return `[thinking ${typed.thinking?.length ?? 0} chars]`;\n\t\t\tif (typed.type === \"toolCall\")\n\t\t\t\treturn `[toolCall ${typed.name || \"unknown\"} ${JSON.stringify(typed.arguments ?? {})}]`;\n\t\t\tif (typed.type === \"image\") return \"[image]\";\n\t\t\treturn \"\";\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction messagePreview(message: AgentMessage): string {\n\tswitch (message.role) {\n\t\tcase \"assistant\":\n\t\tcase \"user\":\n\t\tcase \"toolResult\":\n\t\tcase \"custom\":\n\t\t\treturn contentText((message as { content?: unknown }).content);\n\t\tcase \"bashExecution\":\n\t\t\treturn `Ran ${message.command}\\n${message.output}`;\n\t\tcase \"branchSummary\":\n\t\tcase \"compactionSummary\":\n\t\t\treturn message.summary;\n\t\tdefault:\n\t\t\treturn \"\";\n\t}\n}\n\nfunction messageLabel(message: AgentMessage): string {\n\tif (message.role === \"assistant\") {\n\t\tconst toolCalls = message.content.filter((part) => part.type === \"toolCall\").length;\n\t\treturn toolCalls > 0 ? `assistant (${toolCalls} tool call${toolCalls === 1 ? \"\" : \"s\"})` : \"assistant\";\n\t}\n\tif (message.role === \"toolResult\") return `tool result: ${message.toolName}`;\n\tif (message.role === \"custom\") return `custom: ${message.customType}`;\n\tif (message.role === \"bashExecution\") return \"bash execution\";\n\tif (message.role === \"branchSummary\") return \"branch summary\";\n\tif (message.role === \"compactionSummary\") return \"compaction summary\";\n\treturn message.role;\n}\n\nfunction addRow(rows: AuditRow[], entry: SessionEntry, message: AgentMessage, kindOverride?: string) {\n\tconst preview = messagePreview(message);\n\trows.push({\n\t\tkind: kindOverride || entry.type,\n\t\trole: message.role,\n\t\tentryId: entry.id,\n\t\ttimestamp: entry.timestamp,\n\t\ttokens: estimateTokens(message),\n\t\tchars: preview.length,\n\t\tlabel: messageLabel(message),\n\t\tpreview: cap(preview),\n\t});\n}\n\nfunction messageFromEntry(entry: SessionEntry): AgentMessage | undefined {\n\tif (entry.type === \"message\") return entry.message;\n\tif (entry.type === \"custom_message\") {\n\t\treturn createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp);\n\t}\n\tif (entry.type === \"branch_summary\" && entry.summary) {\n\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t}\n\treturn undefined;\n}\n\nfunction latestCompaction(entries: SessionEntry[]): CompactionEntry | undefined {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry?.type === \"compaction\") return entry;\n\t}\n\treturn undefined;\n}\n\nfunction activeContextRows(entries: SessionEntry[]): AuditRow[] {\n\tconst rows: AuditRow[] = [];\n\tconst compaction = latestCompaction(entries);\n\tif (!compaction) {\n\t\tfor (const entry of entries) {\n\t\t\tconst message = messageFromEntry(entry);\n\t\t\tif (message) addRow(rows, entry, message);\n\t\t}\n\t\treturn rows;\n\t}\n\n\tconst compactionMessage = createCompactionSummaryMessage(\n\t\tcompaction.summary,\n\t\tcompaction.tokensBefore,\n\t\tcompaction.timestamp,\n\t);\n\taddRow(rows, compaction, compactionMessage, \"compaction\");\n\n\tconst compactionIndex = entries.findIndex((entry) => entry.id === compaction.id);\n\tlet foundFirstKept = false;\n\tfor (let index = 0; index < compactionIndex; index++) {\n\t\tconst entry = entries[index];\n\t\tif (entry.id === compaction.firstKeptEntryId) foundFirstKept = true;\n\t\tif (!foundFirstKept) continue;\n\t\tconst message = messageFromEntry(entry);\n\t\tif (message) addRow(rows, entry, message);\n\t}\n\tfor (let index = compactionIndex + 1; index < entries.length; index++) {\n\t\tconst entry = entries[index];\n\t\tconst message = messageFromEntry(entry);\n\t\tif (message) addRow(rows, entry, message);\n\t}\n\treturn rows;\n}\n\nfunction groupRows(rows: AuditRow[]): Array<[string, { count: number; tokens: number; chars: number }]> {\n\tconst groups = new Map<string, { count: number; tokens: number; chars: number }>();\n\tfor (const row of rows) {\n\t\tconst key = row.label;\n\t\tconst current = groups.get(key) ?? { count: 0, tokens: 0, chars: 0 };\n\t\tcurrent.count += 1;\n\t\tcurrent.tokens += row.tokens;\n\t\tcurrent.chars += row.chars;\n\t\tgroups.set(key, current);\n\t}\n\treturn [...groups.entries()].sort((a, b) => b[1].tokens - a[1].tokens);\n}\n\nexport function createCoreDiagnosticsToolDefinitions(\n\tgetActiveTools: () => string[],\n\tgetAllTools: () => ToolInfo[],\n): ToolDefinition[] {\n\treturn [\n\t\t{\n\t\t\tname: \"context_audit\",\n\t\t\tlabel: \"Context Audit\",\n\t\t\tdescription:\n\t\t\t\t\"Audit the current provider-visible context composition: model window usage, system prompt estimate, active tool schema estimate, active session message rows, and heaviest context contributors.\",\n\t\t\tpromptSnippet: \"Audit current loaded context composition before optimizing context usage.\",\n\t\t\tpromptGuidelines: [\n\t\t\t\t\"Use context_audit when the user asks what is consuming context, why the footer shows a high percentage, or which messages/tools/system prompt content are loaded.\",\n\t\t\t\t\"Keep output bounded; use query/minTokens/maxItems to narrow rather than dumping full context.\",\n\t\t\t\t\"Treat token counts as estimates except provider usage from ctx.getContextUsage, which is still model/provider dependent.\",\n\t\t\t],\n\t\t\tparameters: Type.Object(\n\t\t\t\t{\n\t\t\t\t\tmaxItems: Type.Optional(\n\t\t\t\t\t\tType.Number({ description: \"Maximum heaviest session-context rows to show. Default 40, max 200.\" }),\n\t\t\t\t\t),\n\t\t\t\t\tminTokens: Type.Optional(\n\t\t\t\t\t\tType.Number({\n\t\t\t\t\t\t\tdescription: \"Only show session-context rows with at least this many estimated tokens.\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tquery: Type.Optional(\n\t\t\t\t\t\tType.String({ description: \"Case-insensitive filter over row label and preview.\" }),\n\t\t\t\t\t),\n\t\t\t\t\tincludePreviews: Type.Optional(\n\t\t\t\t\t\tType.Boolean({ description: \"Include bounded row previews. Defaults true.\" }),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\t{ additionalProperties: false },\n\t\t\t),\n\t\t\tasync execute(_toolCallId, params: ContextAuditParams, _signal, _onUpdate, ctx) {\n\t\t\t\tconst maxItems = Math.max(1, Math.min(MAX_MAX_ITEMS, Math.floor(params.maxItems ?? DEFAULT_MAX_ITEMS)));\n\t\t\t\tconst minTokens = Math.max(0, Math.floor(params.minTokens ?? 0));\n\t\t\t\tconst includePreviews = params.includePreviews !== false;\n\t\t\t\tconst query = params.query?.trim().toLowerCase();\n\n\t\t\t\tconst branch = ctx.sessionManager.getBranch();\n\t\t\t\tconst rows = activeContextRows(branch);\n\t\t\t\tconst contextUsage = ctx.getContextUsage();\n\t\t\t\tconst systemPrompt = ctx.getSystemPrompt?.() || \"\";\n\t\t\t\tconst activeTools = new Set(getActiveTools());\n\t\t\t\tconst allTools = getAllTools();\n\t\t\t\tconst activeToolInfos = allTools.filter((tool) => activeTools.has(tool.name));\n\t\t\t\tconst systemTokens = estimateTextTokens(systemPrompt);\n\t\t\t\tconst toolSchemaChars = JSON.stringify(\n\t\t\t\t\tactiveToolInfos.map((tool) => ({\n\t\t\t\t\t\tname: tool.name,\n\t\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\t\tparameters: tool.parameters,\n\t\t\t\t\t\tpromptGuidelines: tool.promptGuidelines,\n\t\t\t\t\t})),\n\t\t\t\t).length;\n\t\t\t\tconst toolSchemaTokens = Math.ceil(toolSchemaChars / 4);\n\t\t\t\tconst rowTokenSum = rows.reduce((sum, row) => sum + row.tokens, 0);\n\t\t\t\tconst usageText = contextUsage\n\t\t\t\t\t? contextUsage.tokens === null || contextUsage.percent === null\n\t\t\t\t\t\t? `provider usage: unknown/${contextUsage.contextWindow} tokens (usually right after compaction)`\n\t\t\t\t\t\t: `provider usage: ${contextUsage.tokens}/${contextUsage.contextWindow} tokens (${contextUsage.percent.toFixed(1)}%)`\n\t\t\t\t\t: \"provider usage: unavailable (no active model/context window)\";\n\t\t\t\tconst providerTokens = contextUsage?.tokens ?? null;\n\t\t\t\tconst unattributed =\n\t\t\t\t\tproviderTokens === null\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: Math.max(0, providerTokens - systemTokens - toolSchemaTokens - rowTokenSum);\n\n\t\t\t\tlet filtered = rows.filter((row) => row.tokens >= minTokens);\n\t\t\t\tif (query) {\n\t\t\t\t\tfiltered = filtered.filter((row) => `${row.label}\\n${row.preview}`.toLowerCase().includes(query));\n\t\t\t\t}\n\t\t\t\tconst heaviest = [...filtered].sort((a, b) => b.tokens - a.tokens).slice(0, maxItems);\n\t\t\t\tconst groupLines = groupRows(rows)\n\t\t\t\t\t.slice(0, 12)\n\t\t\t\t\t.map(([label, group]) => `- ${label}: ${group.tokens} est tok across ${group.count} row(s)`);\n\t\t\t\tconst rowLines = heaviest.map((row, index) => {\n\t\t\t\t\tconst base = `${index + 1}. ${row.tokens} est tok · ${row.label} · ${row.entryId ?? \"no-entry\"}`;\n\t\t\t\t\treturn includePreviews\n\t\t\t\t\t\t? `${base}\\n ${cap(row.preview, MAX_PREVIEW_CHARS) || \"(no text preview)\"}`\n\t\t\t\t\t\t: base;\n\t\t\t\t});\n\n\t\t\t\tconst lines = [\n\t\t\t\t\t\"Context audit\",\n\t\t\t\t\tusageText,\n\t\t\t\t\t`active branch rows: ${rows.length}; session row estimate: ${rowTokenSum} tokens`,\n\t\t\t\t\t`system prompt estimate: ${systemTokens} tokens (${systemPrompt.length} chars)`,\n\t\t\t\t\t`active tool schema estimate: ${toolSchemaTokens} tokens across ${activeToolInfos.length} active tool(s)`,\n\t\t\t\t\tunattributed === null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: `provider-reported remainder not mapped by chars/4 rows: ${unattributed} tokens`,\n\t\t\t\t\t\"\",\n\t\t\t\t\t\"Largest groups:\",\n\t\t\t\t\t...(groupLines.length ? groupLines : [\"- none\"]),\n\t\t\t\t\t\"\",\n\t\t\t\t\t`Heaviest rows${query ? ` matching ${JSON.stringify(params.query)}` : \"\"}:`,\n\t\t\t\t\t...(rowLines.length ? rowLines : [\"- none\"]),\n\t\t\t\t].filter((line): line is string => line !== undefined);\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: lines.join(\"\\n\") }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tcontextUsage,\n\t\t\t\t\t\tsystemPrompt: {\n\t\t\t\t\t\t\tchars: systemPrompt.length,\n\t\t\t\t\t\t\testimatedTokens: systemTokens,\n\t\t\t\t\t\t\tpreview: cap(systemPrompt, MAX_PREVIEW_CHARS),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tactiveTools: activeToolInfos.map((tool) => tool.name),\n\t\t\t\t\t\ttoolSchemaEstimate: { chars: toolSchemaChars, estimatedTokens: toolSchemaTokens },\n\t\t\t\t\t\trowTokenSum,\n\t\t\t\t\t\trows,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t},\n\t];\n}\n"]}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { estimateTokens } from "../compaction/compaction.js";
|
|
3
|
+
import { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from "../messages.js";
|
|
4
|
+
const DEFAULT_MAX_ITEMS = 40;
|
|
5
|
+
const MAX_MAX_ITEMS = 200;
|
|
6
|
+
const DEFAULT_PREVIEW_CHARS = 220;
|
|
7
|
+
const MAX_PREVIEW_CHARS = 600;
|
|
8
|
+
function estimateTextTokens(text) {
|
|
9
|
+
return Math.ceil(text.length / 4);
|
|
10
|
+
}
|
|
11
|
+
function cap(text, limit = DEFAULT_PREVIEW_CHARS) {
|
|
12
|
+
const compact = text.replace(/\s+/g, " ").trim();
|
|
13
|
+
return compact.length > limit ? `${compact.slice(0, Math.max(0, limit - 1))}…` : compact;
|
|
14
|
+
}
|
|
15
|
+
function contentText(content) {
|
|
16
|
+
if (typeof content === "string")
|
|
17
|
+
return content;
|
|
18
|
+
if (!Array.isArray(content))
|
|
19
|
+
return "";
|
|
20
|
+
return content
|
|
21
|
+
.map((part) => {
|
|
22
|
+
if (!part || typeof part !== "object")
|
|
23
|
+
return "";
|
|
24
|
+
const typed = part;
|
|
25
|
+
if (typed.type === "text")
|
|
26
|
+
return typed.text || "";
|
|
27
|
+
if (typed.type === "thinking")
|
|
28
|
+
return `[thinking ${typed.thinking?.length ?? 0} chars]`;
|
|
29
|
+
if (typed.type === "toolCall")
|
|
30
|
+
return `[toolCall ${typed.name || "unknown"} ${JSON.stringify(typed.arguments ?? {})}]`;
|
|
31
|
+
if (typed.type === "image")
|
|
32
|
+
return "[image]";
|
|
33
|
+
return "";
|
|
34
|
+
})
|
|
35
|
+
.filter(Boolean)
|
|
36
|
+
.join("\n");
|
|
37
|
+
}
|
|
38
|
+
function messagePreview(message) {
|
|
39
|
+
switch (message.role) {
|
|
40
|
+
case "assistant":
|
|
41
|
+
case "user":
|
|
42
|
+
case "toolResult":
|
|
43
|
+
case "custom":
|
|
44
|
+
return contentText(message.content);
|
|
45
|
+
case "bashExecution":
|
|
46
|
+
return `Ran ${message.command}\n${message.output}`;
|
|
47
|
+
case "branchSummary":
|
|
48
|
+
case "compactionSummary":
|
|
49
|
+
return message.summary;
|
|
50
|
+
default:
|
|
51
|
+
return "";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function messageLabel(message) {
|
|
55
|
+
if (message.role === "assistant") {
|
|
56
|
+
const toolCalls = message.content.filter((part) => part.type === "toolCall").length;
|
|
57
|
+
return toolCalls > 0 ? `assistant (${toolCalls} tool call${toolCalls === 1 ? "" : "s"})` : "assistant";
|
|
58
|
+
}
|
|
59
|
+
if (message.role === "toolResult")
|
|
60
|
+
return `tool result: ${message.toolName}`;
|
|
61
|
+
if (message.role === "custom")
|
|
62
|
+
return `custom: ${message.customType}`;
|
|
63
|
+
if (message.role === "bashExecution")
|
|
64
|
+
return "bash execution";
|
|
65
|
+
if (message.role === "branchSummary")
|
|
66
|
+
return "branch summary";
|
|
67
|
+
if (message.role === "compactionSummary")
|
|
68
|
+
return "compaction summary";
|
|
69
|
+
return message.role;
|
|
70
|
+
}
|
|
71
|
+
function addRow(rows, entry, message, kindOverride) {
|
|
72
|
+
const preview = messagePreview(message);
|
|
73
|
+
rows.push({
|
|
74
|
+
kind: kindOverride || entry.type,
|
|
75
|
+
role: message.role,
|
|
76
|
+
entryId: entry.id,
|
|
77
|
+
timestamp: entry.timestamp,
|
|
78
|
+
tokens: estimateTokens(message),
|
|
79
|
+
chars: preview.length,
|
|
80
|
+
label: messageLabel(message),
|
|
81
|
+
preview: cap(preview),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function messageFromEntry(entry) {
|
|
85
|
+
if (entry.type === "message")
|
|
86
|
+
return entry.message;
|
|
87
|
+
if (entry.type === "custom_message") {
|
|
88
|
+
return createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp);
|
|
89
|
+
}
|
|
90
|
+
if (entry.type === "branch_summary" && entry.summary) {
|
|
91
|
+
return createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
function latestCompaction(entries) {
|
|
96
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
97
|
+
const entry = entries[index];
|
|
98
|
+
if (entry?.type === "compaction")
|
|
99
|
+
return entry;
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
function activeContextRows(entries) {
|
|
104
|
+
const rows = [];
|
|
105
|
+
const compaction = latestCompaction(entries);
|
|
106
|
+
if (!compaction) {
|
|
107
|
+
for (const entry of entries) {
|
|
108
|
+
const message = messageFromEntry(entry);
|
|
109
|
+
if (message)
|
|
110
|
+
addRow(rows, entry, message);
|
|
111
|
+
}
|
|
112
|
+
return rows;
|
|
113
|
+
}
|
|
114
|
+
const compactionMessage = createCompactionSummaryMessage(compaction.summary, compaction.tokensBefore, compaction.timestamp);
|
|
115
|
+
addRow(rows, compaction, compactionMessage, "compaction");
|
|
116
|
+
const compactionIndex = entries.findIndex((entry) => entry.id === compaction.id);
|
|
117
|
+
let foundFirstKept = false;
|
|
118
|
+
for (let index = 0; index < compactionIndex; index++) {
|
|
119
|
+
const entry = entries[index];
|
|
120
|
+
if (entry.id === compaction.firstKeptEntryId)
|
|
121
|
+
foundFirstKept = true;
|
|
122
|
+
if (!foundFirstKept)
|
|
123
|
+
continue;
|
|
124
|
+
const message = messageFromEntry(entry);
|
|
125
|
+
if (message)
|
|
126
|
+
addRow(rows, entry, message);
|
|
127
|
+
}
|
|
128
|
+
for (let index = compactionIndex + 1; index < entries.length; index++) {
|
|
129
|
+
const entry = entries[index];
|
|
130
|
+
const message = messageFromEntry(entry);
|
|
131
|
+
if (message)
|
|
132
|
+
addRow(rows, entry, message);
|
|
133
|
+
}
|
|
134
|
+
return rows;
|
|
135
|
+
}
|
|
136
|
+
function groupRows(rows) {
|
|
137
|
+
const groups = new Map();
|
|
138
|
+
for (const row of rows) {
|
|
139
|
+
const key = row.label;
|
|
140
|
+
const current = groups.get(key) ?? { count: 0, tokens: 0, chars: 0 };
|
|
141
|
+
current.count += 1;
|
|
142
|
+
current.tokens += row.tokens;
|
|
143
|
+
current.chars += row.chars;
|
|
144
|
+
groups.set(key, current);
|
|
145
|
+
}
|
|
146
|
+
return [...groups.entries()].sort((a, b) => b[1].tokens - a[1].tokens);
|
|
147
|
+
}
|
|
148
|
+
export function createCoreDiagnosticsToolDefinitions(getActiveTools, getAllTools) {
|
|
149
|
+
return [
|
|
150
|
+
{
|
|
151
|
+
name: "context_audit",
|
|
152
|
+
label: "Context Audit",
|
|
153
|
+
description: "Audit the current provider-visible context composition: model window usage, system prompt estimate, active tool schema estimate, active session message rows, and heaviest context contributors.",
|
|
154
|
+
promptSnippet: "Audit current loaded context composition before optimizing context usage.",
|
|
155
|
+
promptGuidelines: [
|
|
156
|
+
"Use context_audit when the user asks what is consuming context, why the footer shows a high percentage, or which messages/tools/system prompt content are loaded.",
|
|
157
|
+
"Keep output bounded; use query/minTokens/maxItems to narrow rather than dumping full context.",
|
|
158
|
+
"Treat token counts as estimates except provider usage from ctx.getContextUsage, which is still model/provider dependent.",
|
|
159
|
+
],
|
|
160
|
+
parameters: Type.Object({
|
|
161
|
+
maxItems: Type.Optional(Type.Number({ description: "Maximum heaviest session-context rows to show. Default 40, max 200." })),
|
|
162
|
+
minTokens: Type.Optional(Type.Number({
|
|
163
|
+
description: "Only show session-context rows with at least this many estimated tokens.",
|
|
164
|
+
})),
|
|
165
|
+
query: Type.Optional(Type.String({ description: "Case-insensitive filter over row label and preview." })),
|
|
166
|
+
includePreviews: Type.Optional(Type.Boolean({ description: "Include bounded row previews. Defaults true." })),
|
|
167
|
+
}, { additionalProperties: false }),
|
|
168
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
169
|
+
const maxItems = Math.max(1, Math.min(MAX_MAX_ITEMS, Math.floor(params.maxItems ?? DEFAULT_MAX_ITEMS)));
|
|
170
|
+
const minTokens = Math.max(0, Math.floor(params.minTokens ?? 0));
|
|
171
|
+
const includePreviews = params.includePreviews !== false;
|
|
172
|
+
const query = params.query?.trim().toLowerCase();
|
|
173
|
+
const branch = ctx.sessionManager.getBranch();
|
|
174
|
+
const rows = activeContextRows(branch);
|
|
175
|
+
const contextUsage = ctx.getContextUsage();
|
|
176
|
+
const systemPrompt = ctx.getSystemPrompt?.() || "";
|
|
177
|
+
const activeTools = new Set(getActiveTools());
|
|
178
|
+
const allTools = getAllTools();
|
|
179
|
+
const activeToolInfos = allTools.filter((tool) => activeTools.has(tool.name));
|
|
180
|
+
const systemTokens = estimateTextTokens(systemPrompt);
|
|
181
|
+
const toolSchemaChars = JSON.stringify(activeToolInfos.map((tool) => ({
|
|
182
|
+
name: tool.name,
|
|
183
|
+
description: tool.description,
|
|
184
|
+
parameters: tool.parameters,
|
|
185
|
+
promptGuidelines: tool.promptGuidelines,
|
|
186
|
+
}))).length;
|
|
187
|
+
const toolSchemaTokens = Math.ceil(toolSchemaChars / 4);
|
|
188
|
+
const rowTokenSum = rows.reduce((sum, row) => sum + row.tokens, 0);
|
|
189
|
+
const usageText = contextUsage
|
|
190
|
+
? contextUsage.tokens === null || contextUsage.percent === null
|
|
191
|
+
? `provider usage: unknown/${contextUsage.contextWindow} tokens (usually right after compaction)`
|
|
192
|
+
: `provider usage: ${contextUsage.tokens}/${contextUsage.contextWindow} tokens (${contextUsage.percent.toFixed(1)}%)`
|
|
193
|
+
: "provider usage: unavailable (no active model/context window)";
|
|
194
|
+
const providerTokens = contextUsage?.tokens ?? null;
|
|
195
|
+
const unattributed = providerTokens === null
|
|
196
|
+
? null
|
|
197
|
+
: Math.max(0, providerTokens - systemTokens - toolSchemaTokens - rowTokenSum);
|
|
198
|
+
let filtered = rows.filter((row) => row.tokens >= minTokens);
|
|
199
|
+
if (query) {
|
|
200
|
+
filtered = filtered.filter((row) => `${row.label}\n${row.preview}`.toLowerCase().includes(query));
|
|
201
|
+
}
|
|
202
|
+
const heaviest = [...filtered].sort((a, b) => b.tokens - a.tokens).slice(0, maxItems);
|
|
203
|
+
const groupLines = groupRows(rows)
|
|
204
|
+
.slice(0, 12)
|
|
205
|
+
.map(([label, group]) => `- ${label}: ${group.tokens} est tok across ${group.count} row(s)`);
|
|
206
|
+
const rowLines = heaviest.map((row, index) => {
|
|
207
|
+
const base = `${index + 1}. ${row.tokens} est tok · ${row.label} · ${row.entryId ?? "no-entry"}`;
|
|
208
|
+
return includePreviews
|
|
209
|
+
? `${base}\n ${cap(row.preview, MAX_PREVIEW_CHARS) || "(no text preview)"}`
|
|
210
|
+
: base;
|
|
211
|
+
});
|
|
212
|
+
const lines = [
|
|
213
|
+
"Context audit",
|
|
214
|
+
usageText,
|
|
215
|
+
`active branch rows: ${rows.length}; session row estimate: ${rowTokenSum} tokens`,
|
|
216
|
+
`system prompt estimate: ${systemTokens} tokens (${systemPrompt.length} chars)`,
|
|
217
|
+
`active tool schema estimate: ${toolSchemaTokens} tokens across ${activeToolInfos.length} active tool(s)`,
|
|
218
|
+
unattributed === null
|
|
219
|
+
? undefined
|
|
220
|
+
: `provider-reported remainder not mapped by chars/4 rows: ${unattributed} tokens`,
|
|
221
|
+
"",
|
|
222
|
+
"Largest groups:",
|
|
223
|
+
...(groupLines.length ? groupLines : ["- none"]),
|
|
224
|
+
"",
|
|
225
|
+
`Heaviest rows${query ? ` matching ${JSON.stringify(params.query)}` : ""}:`,
|
|
226
|
+
...(rowLines.length ? rowLines : ["- none"]),
|
|
227
|
+
].filter((line) => line !== undefined);
|
|
228
|
+
return {
|
|
229
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
230
|
+
details: {
|
|
231
|
+
contextUsage,
|
|
232
|
+
systemPrompt: {
|
|
233
|
+
chars: systemPrompt.length,
|
|
234
|
+
estimatedTokens: systemTokens,
|
|
235
|
+
preview: cap(systemPrompt, MAX_PREVIEW_CHARS),
|
|
236
|
+
},
|
|
237
|
+
activeTools: activeToolInfos.map((tool) => tool.name),
|
|
238
|
+
toolSchemaEstimate: { chars: toolSchemaChars, estimatedTokens: toolSchemaTokens },
|
|
239
|
+
rowTokenSum,
|
|
240
|
+
rows,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=builtin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin.js","sourceRoot":"","sources":["../../../src/core/extensions/builtin.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport { Type } from \"typebox\";\nimport { estimateTokens } from \"../compaction/compaction.ts\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from \"../messages.ts\";\nimport type { CompactionEntry, SessionEntry } from \"../session-manager.ts\";\nimport type { ToolDefinition, ToolInfo } from \"./types.ts\";\n\ntype ContextAuditParams = {\n\tmaxItems?: number;\n\tminTokens?: number;\n\tquery?: string;\n\tincludePreviews?: boolean;\n};\n\ntype AuditRow = {\n\tkind: string;\n\trole?: string;\n\tentryId?: string;\n\ttimestamp?: string;\n\ttokens: number;\n\tchars: number;\n\tlabel: string;\n\tpreview: string;\n};\n\nconst DEFAULT_MAX_ITEMS = 40;\nconst MAX_MAX_ITEMS = 200;\nconst DEFAULT_PREVIEW_CHARS = 220;\nconst MAX_PREVIEW_CHARS = 600;\n\nfunction estimateTextTokens(text: string): number {\n\treturn Math.ceil(text.length / 4);\n}\n\nfunction cap(text: string, limit = DEFAULT_PREVIEW_CHARS): string {\n\tconst compact = text.replace(/\\s+/g, \" \").trim();\n\treturn compact.length > limit ? `${compact.slice(0, Math.max(0, limit - 1))}…` : compact;\n}\n\nfunction contentText(content: unknown): string {\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.map((part) => {\n\t\t\tif (!part || typeof part !== \"object\") return \"\";\n\t\t\tconst typed = part as { type?: string; text?: string; thinking?: string; name?: string; arguments?: unknown };\n\t\t\tif (typed.type === \"text\") return typed.text || \"\";\n\t\t\tif (typed.type === \"thinking\") return `[thinking ${typed.thinking?.length ?? 0} chars]`;\n\t\t\tif (typed.type === \"toolCall\")\n\t\t\t\treturn `[toolCall ${typed.name || \"unknown\"} ${JSON.stringify(typed.arguments ?? {})}]`;\n\t\t\tif (typed.type === \"image\") return \"[image]\";\n\t\t\treturn \"\";\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction messagePreview(message: AgentMessage): string {\n\tswitch (message.role) {\n\t\tcase \"assistant\":\n\t\tcase \"user\":\n\t\tcase \"toolResult\":\n\t\tcase \"custom\":\n\t\t\treturn contentText((message as { content?: unknown }).content);\n\t\tcase \"bashExecution\":\n\t\t\treturn `Ran ${message.command}\\n${message.output}`;\n\t\tcase \"branchSummary\":\n\t\tcase \"compactionSummary\":\n\t\t\treturn message.summary;\n\t\tdefault:\n\t\t\treturn \"\";\n\t}\n}\n\nfunction messageLabel(message: AgentMessage): string {\n\tif (message.role === \"assistant\") {\n\t\tconst toolCalls = message.content.filter((part) => part.type === \"toolCall\").length;\n\t\treturn toolCalls > 0 ? `assistant (${toolCalls} tool call${toolCalls === 1 ? \"\" : \"s\"})` : \"assistant\";\n\t}\n\tif (message.role === \"toolResult\") return `tool result: ${message.toolName}`;\n\tif (message.role === \"custom\") return `custom: ${message.customType}`;\n\tif (message.role === \"bashExecution\") return \"bash execution\";\n\tif (message.role === \"branchSummary\") return \"branch summary\";\n\tif (message.role === \"compactionSummary\") return \"compaction summary\";\n\treturn message.role;\n}\n\nfunction addRow(rows: AuditRow[], entry: SessionEntry, message: AgentMessage, kindOverride?: string) {\n\tconst preview = messagePreview(message);\n\trows.push({\n\t\tkind: kindOverride || entry.type,\n\t\trole: message.role,\n\t\tentryId: entry.id,\n\t\ttimestamp: entry.timestamp,\n\t\ttokens: estimateTokens(message),\n\t\tchars: preview.length,\n\t\tlabel: messageLabel(message),\n\t\tpreview: cap(preview),\n\t});\n}\n\nfunction messageFromEntry(entry: SessionEntry): AgentMessage | undefined {\n\tif (entry.type === \"message\") return entry.message;\n\tif (entry.type === \"custom_message\") {\n\t\treturn createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp);\n\t}\n\tif (entry.type === \"branch_summary\" && entry.summary) {\n\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t}\n\treturn undefined;\n}\n\nfunction latestCompaction(entries: SessionEntry[]): CompactionEntry | undefined {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry?.type === \"compaction\") return entry;\n\t}\n\treturn undefined;\n}\n\nfunction activeContextRows(entries: SessionEntry[]): AuditRow[] {\n\tconst rows: AuditRow[] = [];\n\tconst compaction = latestCompaction(entries);\n\tif (!compaction) {\n\t\tfor (const entry of entries) {\n\t\t\tconst message = messageFromEntry(entry);\n\t\t\tif (message) addRow(rows, entry, message);\n\t\t}\n\t\treturn rows;\n\t}\n\n\tconst compactionMessage = createCompactionSummaryMessage(\n\t\tcompaction.summary,\n\t\tcompaction.tokensBefore,\n\t\tcompaction.timestamp,\n\t);\n\taddRow(rows, compaction, compactionMessage, \"compaction\");\n\n\tconst compactionIndex = entries.findIndex((entry) => entry.id === compaction.id);\n\tlet foundFirstKept = false;\n\tfor (let index = 0; index < compactionIndex; index++) {\n\t\tconst entry = entries[index];\n\t\tif (entry.id === compaction.firstKeptEntryId) foundFirstKept = true;\n\t\tif (!foundFirstKept) continue;\n\t\tconst message = messageFromEntry(entry);\n\t\tif (message) addRow(rows, entry, message);\n\t}\n\tfor (let index = compactionIndex + 1; index < entries.length; index++) {\n\t\tconst entry = entries[index];\n\t\tconst message = messageFromEntry(entry);\n\t\tif (message) addRow(rows, entry, message);\n\t}\n\treturn rows;\n}\n\nfunction groupRows(rows: AuditRow[]): Array<[string, { count: number; tokens: number; chars: number }]> {\n\tconst groups = new Map<string, { count: number; tokens: number; chars: number }>();\n\tfor (const row of rows) {\n\t\tconst key = row.label;\n\t\tconst current = groups.get(key) ?? { count: 0, tokens: 0, chars: 0 };\n\t\tcurrent.count += 1;\n\t\tcurrent.tokens += row.tokens;\n\t\tcurrent.chars += row.chars;\n\t\tgroups.set(key, current);\n\t}\n\treturn [...groups.entries()].sort((a, b) => b[1].tokens - a[1].tokens);\n}\n\nexport function createCoreDiagnosticsToolDefinitions(\n\tgetActiveTools: () => string[],\n\tgetAllTools: () => ToolInfo[],\n): ToolDefinition[] {\n\treturn [\n\t\t{\n\t\t\tname: \"context_audit\",\n\t\t\tlabel: \"Context Audit\",\n\t\t\tdescription:\n\t\t\t\t\"Audit the current provider-visible context composition: model window usage, system prompt estimate, active tool schema estimate, active session message rows, and heaviest context contributors.\",\n\t\t\tpromptSnippet: \"Audit current loaded context composition before optimizing context usage.\",\n\t\t\tpromptGuidelines: [\n\t\t\t\t\"Use context_audit when the user asks what is consuming context, why the footer shows a high percentage, or which messages/tools/system prompt content are loaded.\",\n\t\t\t\t\"Keep output bounded; use query/minTokens/maxItems to narrow rather than dumping full context.\",\n\t\t\t\t\"Treat token counts as estimates except provider usage from ctx.getContextUsage, which is still model/provider dependent.\",\n\t\t\t],\n\t\t\tparameters: Type.Object(\n\t\t\t\t{\n\t\t\t\t\tmaxItems: Type.Optional(\n\t\t\t\t\t\tType.Number({ description: \"Maximum heaviest session-context rows to show. Default 40, max 200.\" }),\n\t\t\t\t\t),\n\t\t\t\t\tminTokens: Type.Optional(\n\t\t\t\t\t\tType.Number({\n\t\t\t\t\t\t\tdescription: \"Only show session-context rows with at least this many estimated tokens.\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tquery: Type.Optional(\n\t\t\t\t\t\tType.String({ description: \"Case-insensitive filter over row label and preview.\" }),\n\t\t\t\t\t),\n\t\t\t\t\tincludePreviews: Type.Optional(\n\t\t\t\t\t\tType.Boolean({ description: \"Include bounded row previews. Defaults true.\" }),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\t{ additionalProperties: false },\n\t\t\t),\n\t\t\tasync execute(_toolCallId, params: ContextAuditParams, _signal, _onUpdate, ctx) {\n\t\t\t\tconst maxItems = Math.max(1, Math.min(MAX_MAX_ITEMS, Math.floor(params.maxItems ?? DEFAULT_MAX_ITEMS)));\n\t\t\t\tconst minTokens = Math.max(0, Math.floor(params.minTokens ?? 0));\n\t\t\t\tconst includePreviews = params.includePreviews !== false;\n\t\t\t\tconst query = params.query?.trim().toLowerCase();\n\n\t\t\t\tconst branch = ctx.sessionManager.getBranch();\n\t\t\t\tconst rows = activeContextRows(branch);\n\t\t\t\tconst contextUsage = ctx.getContextUsage();\n\t\t\t\tconst systemPrompt = ctx.getSystemPrompt?.() || \"\";\n\t\t\t\tconst activeTools = new Set(getActiveTools());\n\t\t\t\tconst allTools = getAllTools();\n\t\t\t\tconst activeToolInfos = allTools.filter((tool) => activeTools.has(tool.name));\n\t\t\t\tconst systemTokens = estimateTextTokens(systemPrompt);\n\t\t\t\tconst toolSchemaChars = JSON.stringify(\n\t\t\t\t\tactiveToolInfos.map((tool) => ({\n\t\t\t\t\t\tname: tool.name,\n\t\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\t\tparameters: tool.parameters,\n\t\t\t\t\t\tpromptGuidelines: tool.promptGuidelines,\n\t\t\t\t\t})),\n\t\t\t\t).length;\n\t\t\t\tconst toolSchemaTokens = Math.ceil(toolSchemaChars / 4);\n\t\t\t\tconst rowTokenSum = rows.reduce((sum, row) => sum + row.tokens, 0);\n\t\t\t\tconst usageText = contextUsage\n\t\t\t\t\t? contextUsage.tokens === null || contextUsage.percent === null\n\t\t\t\t\t\t? `provider usage: unknown/${contextUsage.contextWindow} tokens (usually right after compaction)`\n\t\t\t\t\t\t: `provider usage: ${contextUsage.tokens}/${contextUsage.contextWindow} tokens (${contextUsage.percent.toFixed(1)}%)`\n\t\t\t\t\t: \"provider usage: unavailable (no active model/context window)\";\n\t\t\t\tconst providerTokens = contextUsage?.tokens ?? null;\n\t\t\t\tconst unattributed =\n\t\t\t\t\tproviderTokens === null\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: Math.max(0, providerTokens - systemTokens - toolSchemaTokens - rowTokenSum);\n\n\t\t\t\tlet filtered = rows.filter((row) => row.tokens >= minTokens);\n\t\t\t\tif (query) {\n\t\t\t\t\tfiltered = filtered.filter((row) => `${row.label}\\n${row.preview}`.toLowerCase().includes(query));\n\t\t\t\t}\n\t\t\t\tconst heaviest = [...filtered].sort((a, b) => b.tokens - a.tokens).slice(0, maxItems);\n\t\t\t\tconst groupLines = groupRows(rows)\n\t\t\t\t\t.slice(0, 12)\n\t\t\t\t\t.map(([label, group]) => `- ${label}: ${group.tokens} est tok across ${group.count} row(s)`);\n\t\t\t\tconst rowLines = heaviest.map((row, index) => {\n\t\t\t\t\tconst base = `${index + 1}. ${row.tokens} est tok · ${row.label} · ${row.entryId ?? \"no-entry\"}`;\n\t\t\t\t\treturn includePreviews\n\t\t\t\t\t\t? `${base}\\n ${cap(row.preview, MAX_PREVIEW_CHARS) || \"(no text preview)\"}`\n\t\t\t\t\t\t: base;\n\t\t\t\t});\n\n\t\t\t\tconst lines = [\n\t\t\t\t\t\"Context audit\",\n\t\t\t\t\tusageText,\n\t\t\t\t\t`active branch rows: ${rows.length}; session row estimate: ${rowTokenSum} tokens`,\n\t\t\t\t\t`system prompt estimate: ${systemTokens} tokens (${systemPrompt.length} chars)`,\n\t\t\t\t\t`active tool schema estimate: ${toolSchemaTokens} tokens across ${activeToolInfos.length} active tool(s)`,\n\t\t\t\t\tunattributed === null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: `provider-reported remainder not mapped by chars/4 rows: ${unattributed} tokens`,\n\t\t\t\t\t\"\",\n\t\t\t\t\t\"Largest groups:\",\n\t\t\t\t\t...(groupLines.length ? groupLines : [\"- none\"]),\n\t\t\t\t\t\"\",\n\t\t\t\t\t`Heaviest rows${query ? ` matching ${JSON.stringify(params.query)}` : \"\"}:`,\n\t\t\t\t\t...(rowLines.length ? rowLines : [\"- none\"]),\n\t\t\t\t].filter((line): line is string => line !== undefined);\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: lines.join(\"\\n\") }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tcontextUsage,\n\t\t\t\t\t\tsystemPrompt: {\n\t\t\t\t\t\t\tchars: systemPrompt.length,\n\t\t\t\t\t\t\testimatedTokens: systemTokens,\n\t\t\t\t\t\t\tpreview: cap(systemPrompt, MAX_PREVIEW_CHARS),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tactiveTools: activeToolInfos.map((tool) => tool.name),\n\t\t\t\t\t\ttoolSchemaEstimate: { chars: toolSchemaChars, estimatedTokens: toolSchemaTokens },\n\t\t\t\t\t\trowTokenSum,\n\t\t\t\t\t\trows,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t},\n\t];\n}\n"]}
|
|
@@ -21,6 +21,12 @@ export interface ResourceExtensionPaths {
|
|
|
21
21
|
metadata: PathMetadata;
|
|
22
22
|
}>;
|
|
23
23
|
}
|
|
24
|
+
export interface ResourceReloadOptions {
|
|
25
|
+
/** Throw instead of accepting a hot reload that produced extension load/conflict errors. */
|
|
26
|
+
failOnExtensionErrors?: boolean;
|
|
27
|
+
/** Keep the previous extension generation alive until commitReload()/rollbackReload(). */
|
|
28
|
+
deferExtensionDispose?: boolean;
|
|
29
|
+
}
|
|
24
30
|
export interface ResourceLoader {
|
|
25
31
|
getExtensions(): LoadExtensionsResult;
|
|
26
32
|
getSkills(): {
|
|
@@ -44,7 +50,9 @@ export interface ResourceLoader {
|
|
|
44
50
|
getSystemPrompt(): string | undefined;
|
|
45
51
|
getAppendSystemPrompt(): string[];
|
|
46
52
|
extendResources(paths: ResourceExtensionPaths): void;
|
|
47
|
-
reload(): Promise<void>;
|
|
53
|
+
reload(options?: ResourceReloadOptions): Promise<void>;
|
|
54
|
+
commitReload?(): void;
|
|
55
|
+
rollbackReload?(): void;
|
|
48
56
|
}
|
|
49
57
|
export declare function scanContextFileThreats(content: string): string[];
|
|
50
58
|
export declare function loadProjectContextFiles(options: {
|
|
@@ -149,6 +157,7 @@ export declare class DefaultResourceLoader implements ResourceLoader {
|
|
|
149
157
|
private extensionThemeSourceInfos;
|
|
150
158
|
private lastPromptPaths;
|
|
151
159
|
private lastThemePaths;
|
|
160
|
+
private pendingReloadSnapshot;
|
|
152
161
|
constructor(options: DefaultResourceLoaderOptions);
|
|
153
162
|
getExtensions(): LoadExtensionsResult;
|
|
154
163
|
getSkills(): {
|
|
@@ -172,7 +181,11 @@ export declare class DefaultResourceLoader implements ResourceLoader {
|
|
|
172
181
|
getSystemPrompt(): string | undefined;
|
|
173
182
|
getAppendSystemPrompt(): string[];
|
|
174
183
|
extendResources(paths: ResourceExtensionPaths): void;
|
|
175
|
-
|
|
184
|
+
private createSnapshot;
|
|
185
|
+
private restoreSnapshot;
|
|
186
|
+
commitReload(): void;
|
|
187
|
+
rollbackReload(): void;
|
|
188
|
+
reload(options?: ResourceReloadOptions): Promise<void>;
|
|
176
189
|
private normalizeExtensionPaths;
|
|
177
190
|
private updateSkillsFromPaths;
|
|
178
191
|
private updatePromptsFromPaths;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-loader.d.ts","sourceRoot":"","sources":["../../src/core/resource-loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, resolve, sep } from \"node:path\";\nimport chalk from \"chalk\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { loadThemeFromPath, type Theme } from \"../modes/interactive/theme/theme.ts\";\nimport type { ResourceDiagnostic } from \"./diagnostics.ts\";\n\nexport type { ResourceCollision, ResourceDiagnostic } from \"./diagnostics.ts\";\n\nimport { canonicalizePath, isLocalPath, resolvePath } from \"../utils/paths.ts\";\nimport { createEventBus, type EventBus } from \"./event-bus.ts\";\nimport {\n\tcreateExtensionRuntime,\n\tdisposeExtensionEventSubscriptions,\n\tloadExtensionFromFactory,\n\tloadExtensions,\n} from \"./extensions/loader.ts\";\nimport type { Extension, ExtensionFactory, ExtensionRuntime, LoadExtensionsResult } from \"./extensions/types.ts\";\nimport { DefaultPackageManager, type PathMetadata } from \"./package-manager.ts\";\nimport type { PromptTemplate } from \"./prompt-templates.ts\";\nimport { loadPromptTemplates } from \"./prompt-templates.ts\";\nimport {\n\tmergeResourceProfileMap,\n\tparseResourceProfileBlocks,\n\tstripResourceProfileBlocks,\n} from \"./resource-profile-blocks.ts\";\nimport { matchesResourceProfilePattern, type ResourceProfileSettings, SettingsManager } from \"./settings-manager.ts\";\nimport type { Skill } from \"./skills.ts\";\nimport { loadSkills } from \"./skills.ts\";\nimport { createSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\nexport interface ResourceExtensionPaths {\n\tskillPaths?: Array<{ path: string; metadata: PathMetadata }>;\n\tpromptPaths?: Array<{ path: string; metadata: PathMetadata }>;\n\tthemePaths?: Array<{ path: string; metadata: PathMetadata }>;\n}\n\nexport interface ResourceLoader {\n\tgetExtensions(): LoadExtensionsResult;\n\tgetSkills(): { skills: Skill[]; diagnostics: ResourceDiagnostic[] };\n\tgetPrompts(): { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] };\n\tgetThemes(): { themes: Theme[]; diagnostics: ResourceDiagnostic[] };\n\tgetAgentsFiles(): { agentsFiles: Array<{ path: string; content?: string }> };\n\tgetSystemPrompt(): string | undefined;\n\tgetAppendSystemPrompt(): string[];\n\textendResources(paths: ResourceExtensionPaths): void;\n\treload(): Promise<void>;\n}\n\nfunction resolvePromptInput(input: string | undefined, description: string): string | undefined {\n\tif (!input) {\n\t\treturn undefined;\n\t}\n\n\tif (existsSync(input)) {\n\t\ttry {\n\t\t\treturn readFileSync(input, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${description} file ${input}: ${error}`));\n\t\t\treturn input;\n\t\t}\n\t}\n\n\treturn input;\n}\n\nconst CONTEXT_THREAT_PATTERNS: Array<{ label: string; pattern: RegExp }> = [\n\t{\n\t\tlabel: \"instruction override\",\n\t\tpattern:\n\t\t\t/\\b(?:ignore|disregard|override|bypass)\\b.{0,80}\\b(?:previous|prior|above|system|developer|agent)\\b.{0,80}\\binstructions?\\b/i,\n\t},\n\t{\n\t\tlabel: \"secret exfiltration\",\n\t\tpattern:\n\t\t\t/\\b(?:reveal|print|dump|exfiltrate|send|upload)\\b.{0,80}\\b(?:secrets?|tokens?|api[_ -]?keys?|credentials?|environment variables?|\\.env)\\b/i,\n\t},\n\t{\n\t\tlabel: \"hidden instruction\",\n\t\tpattern: /\\b(?:do not tell|don't tell|hide this from)\\b.{0,80}\\b(?:user|operator|developer)\\b/i,\n\t},\n];\n\nexport function scanContextFileThreats(content: string): string[] {\n\treturn CONTEXT_THREAT_PATTERNS.filter(({ pattern }) => pattern.test(content)).map(({ label }) => label);\n}\n\nfunction sanitizeContextFileContent(filePath: string, content: string): string {\n\tconst profileFreeContent = stripResourceProfileBlocks(content);\n\tconst findings = scanContextFileThreats(profileFreeContent);\n\tif (findings.length === 0) return profileFreeContent;\n\tconsole.error(chalk.yellow(`Warning: Blocked context file ${filePath}: ${findings.join(\", \")}`));\n\treturn `[BLOCKED: ${filePath} contained potential prompt injection (${findings.join(\", \")}). Content not loaded.]`;\n}\n\nfunction loadContextFilesFromDir(dir: string): Array<{ path: string; content: string }> {\n\tconst candidates = [\"AGENTS.md\", \"AGENTS.MD\", \"CLAUDE.md\", \"CLAUDE.MD\", \"GEMINI.md\", \"GEMINI.MD\"];\n\tconst files: Array<{ path: string; content: string }> = [];\n\tfor (const filename of candidates) {\n\t\tconst filePath = join(dir, filename);\n\t\tif (existsSync(filePath)) {\n\t\t\ttry {\n\t\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\tcontent: sanitizeContextFileContent(filePath, content),\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\nexport function loadProjectContextFiles(options: {\n\tcwd: string;\n\tagentDir: string;\n\tprojectTrusted?: boolean;\n}): Array<{ path: string; content?: string }> {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\n\tconst contextFiles: Array<{ path: string; content?: string }> = [];\n\tconst seenPaths = new Set<string>();\n\n\tfor (const globalContext of loadContextFilesFromDir(resolvedAgentDir)) {\n\t\tcontextFiles.push(globalContext);\n\t\tseenPaths.add(globalContext.path);\n\t}\n\n\tif (options.projectTrusted !== false) {\n\t\tconst ancestorContextFiles: Array<{ path: string; content?: string }> = [];\n\n\t\tlet currentDir = resolvedCwd;\n\t\tconst root = resolve(\"/\");\n\n\t\twhile (true) {\n\t\t\tconst contextFilesInDir = loadContextFilesFromDir(currentDir).filter(\n\t\t\t\t(contextFile) => !seenPaths.has(contextFile.path),\n\t\t\t);\n\t\t\tif (contextFilesInDir.length > 0) {\n\t\t\t\tancestorContextFiles.unshift(...contextFilesInDir);\n\t\t\t\tfor (const contextFile of contextFilesInDir) {\n\t\t\t\t\tseenPaths.add(contextFile.path);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentDir === root) break;\n\n\t\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\t\tif (parentDir === currentDir) break;\n\t\t\tcurrentDir = parentDir;\n\t\t}\n\n\t\tcontextFiles.push(...ancestorContextFiles);\n\t}\n\n\treturn contextFiles;\n}\n\nexport interface DefaultResourceLoaderOptions {\n\tcwd: string;\n\tagentDir: string;\n\tsettingsManager?: SettingsManager;\n\teventBus?: EventBus;\n\tadditionalExtensionPaths?: string[];\n\tadditionalSkillPaths?: string[];\n\tadditionalPromptTemplatePaths?: string[];\n\tadditionalThemePaths?: string[];\n\textensionFactories?: ExtensionFactory[];\n\tnoExtensions?: boolean;\n\tnoSkills?: boolean;\n\tnoPromptTemplates?: boolean;\n\tnoThemes?: boolean;\n\tnoContextFiles?: boolean;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string[];\n\textensionsOverride?: (base: LoadExtensionsResult) => LoadExtensionsResult;\n\tskillsOverride?: (base: { skills: Skill[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tskills: Skill[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tpromptsOverride?: (base: { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tprompts: PromptTemplate[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tthemesOverride?: (base: { themes: Theme[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tthemes: Theme[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tagentsFilesOverride?: (base: { agentsFiles: Array<{ path: string; content?: string }> }) => {\n\t\tagentsFiles: Array<{ path: string; content?: string }>;\n\t};\n\tsystemPromptOverride?: (base: string | undefined) => string | undefined;\n\tappendSystemPromptOverride?: (base: string[]) => string[];\n}\n\nexport class DefaultResourceLoader implements ResourceLoader {\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate settingsManager: SettingsManager;\n\tprivate eventBus: EventBus;\n\tprivate packageManager: DefaultPackageManager;\n\tprivate additionalExtensionPaths: string[];\n\tprivate additionalSkillPaths: string[];\n\tprivate additionalPromptTemplatePaths: string[];\n\tprivate additionalThemePaths: string[];\n\tprivate extensionFactories: ExtensionFactory[];\n\tprivate noExtensions: boolean;\n\tprivate noSkills: boolean;\n\tprivate noPromptTemplates: boolean;\n\tprivate noThemes: boolean;\n\tprivate noContextFiles: boolean;\n\tprivate systemPromptSource?: string;\n\tprivate appendSystemPromptSource?: string[];\n\tprivate extensionsOverride?: (base: LoadExtensionsResult) => LoadExtensionsResult;\n\tprivate skillsOverride?: (base: { skills: Skill[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tskills: Skill[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tprivate promptsOverride?: (base: { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tprompts: PromptTemplate[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tprivate themesOverride?: (base: { themes: Theme[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tthemes: Theme[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tprivate agentsFilesOverride?: (base: { agentsFiles: Array<{ path: string; content?: string }> }) => {\n\t\tagentsFiles: Array<{ path: string; content?: string }>;\n\t};\n\tprivate systemPromptOverride?: (base: string | undefined) => string | undefined;\n\tprivate appendSystemPromptOverride?: (base: string[]) => string[];\n\n\tprivate extensionsResult: LoadExtensionsResult;\n\tprivate skills: Skill[];\n\tprivate skillDiagnostics: ResourceDiagnostic[];\n\tprivate prompts: PromptTemplate[];\n\tprivate promptDiagnostics: ResourceDiagnostic[];\n\tprivate themes: Theme[];\n\tprivate themeDiagnostics: ResourceDiagnostic[];\n\tprivate agentsFiles: Array<{ path: string; content?: string }>;\n\tprivate systemPrompt?: string;\n\tprivate appendSystemPrompt: string[];\n\tprivate lastSkillPaths: string[];\n\tprivate extensionSkillSourceInfos: Map<string, SourceInfo>;\n\tprivate extensionPromptSourceInfos: Map<string, SourceInfo>;\n\tprivate extensionThemeSourceInfos: Map<string, SourceInfo>;\n\tprivate lastPromptPaths: string[];\n\tprivate lastThemePaths: string[];\n\n\tconstructor(options: DefaultResourceLoaderOptions) {\n\t\tthis.cwd = resolvePath(options.cwd);\n\t\tthis.agentDir = resolvePath(options.agentDir);\n\t\tthis.settingsManager = options.settingsManager ?? SettingsManager.create(this.cwd, this.agentDir);\n\t\tthis.eventBus = options.eventBus ?? createEventBus();\n\t\tthis.packageManager = new DefaultPackageManager({\n\t\t\tcwd: this.cwd,\n\t\t\tagentDir: this.agentDir,\n\t\t\tsettingsManager: this.settingsManager,\n\t\t});\n\t\tthis.additionalExtensionPaths = options.additionalExtensionPaths ?? [];\n\t\tthis.additionalSkillPaths = options.additionalSkillPaths ?? [];\n\t\tthis.additionalPromptTemplatePaths = options.additionalPromptTemplatePaths ?? [];\n\t\tthis.additionalThemePaths = options.additionalThemePaths ?? [];\n\t\tthis.extensionFactories = options.extensionFactories ?? [];\n\t\tthis.noExtensions = options.noExtensions ?? false;\n\t\tthis.noSkills = options.noSkills ?? false;\n\t\tthis.noPromptTemplates = options.noPromptTemplates ?? false;\n\t\tthis.noThemes = options.noThemes ?? false;\n\t\tthis.noContextFiles = options.noContextFiles ?? false;\n\t\tthis.systemPromptSource = options.systemPrompt;\n\t\tthis.appendSystemPromptSource = options.appendSystemPrompt;\n\t\tthis.extensionsOverride = options.extensionsOverride;\n\t\tthis.skillsOverride = options.skillsOverride;\n\t\tthis.promptsOverride = options.promptsOverride;\n\t\tthis.themesOverride = options.themesOverride;\n\t\tthis.agentsFilesOverride = options.agentsFilesOverride;\n\t\tthis.systemPromptOverride = options.systemPromptOverride;\n\t\tthis.appendSystemPromptOverride = options.appendSystemPromptOverride;\n\n\t\tthis.extensionsResult = { extensions: [], errors: [], runtime: createExtensionRuntime() };\n\t\tthis.skills = [];\n\t\tthis.skillDiagnostics = [];\n\t\tthis.prompts = [];\n\t\tthis.promptDiagnostics = [];\n\t\tthis.themes = [];\n\t\tthis.themeDiagnostics = [];\n\t\tthis.agentsFiles = [];\n\t\tthis.appendSystemPrompt = [];\n\t\tthis.lastSkillPaths = [];\n\t\tthis.extensionSkillSourceInfos = new Map();\n\t\tthis.extensionPromptSourceInfos = new Map();\n\t\tthis.extensionThemeSourceInfos = new Map();\n\t\tthis.lastPromptPaths = [];\n\t\tthis.lastThemePaths = [];\n\t}\n\n\tgetExtensions(): LoadExtensionsResult {\n\t\treturn this.extensionsResult;\n\t}\n\n\tgetSkills(): { skills: Skill[]; diagnostics: ResourceDiagnostic[] } {\n\t\treturn { skills: this.skills, diagnostics: this.skillDiagnostics };\n\t}\n\n\tgetPrompts(): { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] } {\n\t\treturn { prompts: this.prompts, diagnostics: this.promptDiagnostics };\n\t}\n\n\tgetThemes(): { themes: Theme[]; diagnostics: ResourceDiagnostic[] } {\n\t\treturn { themes: this.themes, diagnostics: this.themeDiagnostics };\n\t}\n\n\tgetAgentsFiles(): { agentsFiles: Array<{ path: string; content?: string }> } {\n\t\treturn { agentsFiles: this.agentsFiles };\n\t}\n\n\tgetSystemPrompt(): string | undefined {\n\t\treturn this.systemPrompt;\n\t}\n\n\tgetAppendSystemPrompt(): string[] {\n\t\treturn this.appendSystemPrompt;\n\t}\n\n\textendResources(paths: ResourceExtensionPaths): void {\n\t\tconst skillPaths = this.normalizeExtensionPaths(paths.skillPaths ?? []);\n\t\tconst promptPaths = this.normalizeExtensionPaths(paths.promptPaths ?? []);\n\t\tconst themePaths = this.normalizeExtensionPaths(paths.themePaths ?? []);\n\n\t\tfor (const entry of skillPaths) {\n\t\t\tthis.extensionSkillSourceInfos.set(entry.path, createSourceInfo(entry.path, entry.metadata));\n\t\t}\n\t\tfor (const entry of promptPaths) {\n\t\t\tthis.extensionPromptSourceInfos.set(entry.path, createSourceInfo(entry.path, entry.metadata));\n\t\t}\n\t\tfor (const entry of themePaths) {\n\t\t\tthis.extensionThemeSourceInfos.set(entry.path, createSourceInfo(entry.path, entry.metadata));\n\t\t}\n\n\t\tif (skillPaths.length > 0) {\n\t\t\tthis.lastSkillPaths = this.mergePaths(\n\t\t\t\tthis.lastSkillPaths,\n\t\t\t\tskillPaths.map((entry) => entry.path),\n\t\t\t);\n\t\t\tthis.updateSkillsFromPaths(this.lastSkillPaths);\n\t\t}\n\n\t\tif (promptPaths.length > 0) {\n\t\t\tthis.lastPromptPaths = this.mergePaths(\n\t\t\t\tthis.lastPromptPaths,\n\t\t\t\tpromptPaths.map((entry) => entry.path),\n\t\t\t);\n\t\t\tthis.updatePromptsFromPaths(this.lastPromptPaths);\n\t\t}\n\n\t\tif (themePaths.length > 0) {\n\t\t\tthis.lastThemePaths = this.mergePaths(\n\t\t\t\tthis.lastThemePaths,\n\t\t\t\tthemePaths.map((entry) => entry.path),\n\t\t\t);\n\t\t\tthis.updateThemesFromPaths(this.lastThemePaths);\n\t\t}\n\t}\n\n\tasync reload(): Promise<void> {\n\t\t// The replaced extension generation must release its shared event bus\n\t\t// subscriptions, or every reload pins the old module graph in memory.\n\t\tdisposeExtensionEventSubscriptions(this.extensionsResult.extensions);\n\t\tawait this.settingsManager.reload();\n\t\tconst resolvedPaths = await this.packageManager.resolve();\n\t\tconst cliExtensionPaths = await this.packageManager.resolveExtensionSources(this.additionalExtensionPaths, {\n\t\t\ttemporary: true,\n\t\t});\n\t\tconst metadataByPath = new Map<string, PathMetadata>();\n\n\t\tthis.extensionSkillSourceInfos = new Map();\n\t\tthis.extensionPromptSourceInfos = new Map();\n\t\tthis.extensionThemeSourceInfos = new Map();\n\n\t\t// Helper to extract enabled paths and store metadata\n\t\tconst getEnabledResources = (\n\t\t\tresources: Array<{ path: string; enabled: boolean; metadata: PathMetadata }>,\n\t\t): Array<{ path: string; enabled: boolean; metadata: PathMetadata }> => {\n\t\t\tfor (const r of resources) {\n\t\t\t\tif (!metadataByPath.has(r.path)) {\n\t\t\t\t\tmetadataByPath.set(r.path, r.metadata);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn resources.filter((r) => r.enabled);\n\t\t};\n\n\t\tconst getEnabledPaths = (\n\t\t\tresources: Array<{ path: string; enabled: boolean; metadata: PathMetadata }>,\n\t\t): string[] => getEnabledResources(resources).map((r) => r.path);\n\t\tconst enabledExtensions = getEnabledPaths(resolvedPaths.extensions);\n\t\tconst enabledSkillResources = getEnabledResources(resolvedPaths.skills);\n\t\tconst enabledPrompts = getEnabledPaths(resolvedPaths.prompts);\n\t\tconst enabledThemes = getEnabledPaths(resolvedPaths.themes);\n\n\t\tconst mapSkillPath = (resource: { path: string; metadata: PathMetadata }): string => {\n\t\t\tif (resource.metadata.source !== \"auto\" && resource.metadata.origin !== \"package\") {\n\t\t\t\treturn resource.path;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst stats = statSync(resource.path);\n\t\t\t\tif (!stats.isDirectory()) {\n\t\t\t\t\treturn resource.path;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn resource.path;\n\t\t\t}\n\t\t\tconst skillFile = join(resource.path, \"SKILL.md\");\n\t\t\tif (existsSync(skillFile)) {\n\t\t\t\tif (!metadataByPath.has(skillFile)) {\n\t\t\t\t\tmetadataByPath.set(skillFile, resource.metadata);\n\t\t\t\t}\n\t\t\t\treturn skillFile;\n\t\t\t}\n\t\t\treturn resource.path;\n\t\t};\n\n\t\tconst enabledSkills = enabledSkillResources.map(mapSkillPath);\n\n\t\t// Add CLI paths metadata\n\t\tfor (const r of cliExtensionPaths.extensions) {\n\t\t\tif (!metadataByPath.has(r.path)) {\n\t\t\t\tmetadataByPath.set(r.path, { source: \"cli\", scope: \"temporary\", origin: \"top-level\" });\n\t\t\t}\n\t\t}\n\t\tfor (const r of cliExtensionPaths.skills) {\n\t\t\tif (!metadataByPath.has(r.path)) {\n\t\t\t\tmetadataByPath.set(r.path, { source: \"cli\", scope: \"temporary\", origin: \"top-level\" });\n\t\t\t}\n\t\t}\n\n\t\tconst cliEnabledExtensions = getEnabledPaths(cliExtensionPaths.extensions);\n\t\tconst cliEnabledSkills = getEnabledPaths(cliExtensionPaths.skills);\n\t\tconst cliEnabledPrompts = getEnabledPaths(cliExtensionPaths.prompts);\n\t\tconst cliEnabledThemes = getEnabledPaths(cliExtensionPaths.themes);\n\n\t\tconst extensionPaths = this.noExtensions\n\t\t\t? cliEnabledExtensions\n\t\t\t: this.mergePaths(cliEnabledExtensions, enabledExtensions);\n\n\t\tconst extensionsResult = await loadExtensions(extensionPaths, this.cwd, this.eventBus);\n\t\tconst inlineExtensions = await this.loadExtensionFactories(extensionsResult.runtime);\n\t\textensionsResult.extensions.push(...inlineExtensions.extensions);\n\t\textensionsResult.errors.push(...inlineExtensions.errors);\n\n\t\t// Detect extension conflicts (tools, commands, flags with same names from different extensions)\n\t\t// Keep all extensions loaded. Conflicts are reported as diagnostics, and precedence is handled by load order.\n\t\tconst conflicts = this.detectExtensionConflicts(extensionsResult.extensions);\n\t\tfor (const conflict of conflicts) {\n\t\t\textensionsResult.errors.push({ path: conflict.path, error: conflict.message });\n\t\t}\n\n\t\tfor (const p of this.additionalExtensionPaths) {\n\t\t\tif (isLocalPath(p)) {\n\t\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\t\tif (!existsSync(resolved)) {\n\t\t\t\t\textensionsResult.errors.push({ path: resolved, error: `Extension path does not exist: ${resolved}` });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.extensionsResult = this.extensionsOverride ? this.extensionsOverride(extensionsResult) : extensionsResult;\n\t\tthis.applyExtensionSourceInfo(this.extensionsResult.extensions, metadataByPath);\n\n\t\tconst skillPaths = this.noSkills\n\t\t\t? this.mergePaths(cliEnabledSkills, this.additionalSkillPaths)\n\t\t\t: this.mergePaths([...cliEnabledSkills, ...enabledSkills], this.additionalSkillPaths);\n\n\t\tthis.lastSkillPaths = skillPaths;\n\t\tthis.updateSkillsFromPaths(skillPaths, metadataByPath);\n\t\tfor (const p of this.additionalSkillPaths) {\n\t\t\tif (isLocalPath(p)) {\n\t\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\t\tif (!existsSync(resolved) && !this.skillDiagnostics.some((d) => d.path === resolved)) {\n\t\t\t\t\tthis.skillDiagnostics.push({ type: \"error\", message: \"Skill path does not exist\", path: resolved });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst promptPaths = this.noPromptTemplates\n\t\t\t? this.mergePaths(cliEnabledPrompts, this.additionalPromptTemplatePaths)\n\t\t\t: this.mergePaths([...cliEnabledPrompts, ...enabledPrompts], this.additionalPromptTemplatePaths);\n\n\t\tthis.lastPromptPaths = promptPaths;\n\t\tthis.updatePromptsFromPaths(promptPaths, metadataByPath);\n\t\tfor (const p of this.additionalPromptTemplatePaths) {\n\t\t\tif (isLocalPath(p)) {\n\t\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\t\tif (!existsSync(resolved) && !this.promptDiagnostics.some((d) => d.path === resolved)) {\n\t\t\t\t\tthis.promptDiagnostics.push({\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\tmessage: \"Prompt template path does not exist\",\n\t\t\t\t\t\tpath: resolved,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst themePaths = this.noThemes\n\t\t\t? this.mergePaths(cliEnabledThemes, this.additionalThemePaths)\n\t\t\t: this.mergePaths([...cliEnabledThemes, ...enabledThemes], this.additionalThemePaths);\n\n\t\tthis.lastThemePaths = themePaths;\n\t\tthis.updateThemesFromPaths(themePaths, metadataByPath);\n\t\tfor (const p of this.additionalThemePaths) {\n\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\tif (!existsSync(resolved) && !this.themeDiagnostics.some((d) => d.path === resolved)) {\n\t\t\t\tthis.themeDiagnostics.push({ type: \"error\", message: \"Theme path does not exist\", path: resolved });\n\t\t\t}\n\t\t}\n\n\t\tconst rawAgentsFiles = this.noContextFiles\n\t\t\t? []\n\t\t\t: loadProjectContextFiles({\n\t\t\t\t\tcwd: this.cwd,\n\t\t\t\t\tagentDir: this.agentDir,\n\t\t\t\t\tprojectTrusted: this.settingsManager.isProjectTrusted(),\n\t\t\t\t});\n\t\tconst agentEmbeddedProfiles: Record<string, ResourceProfileSettings> = {};\n\t\tconst activeProfileNames = this.settingsManager.getActiveResourceProfileNames();\n\t\tfor (const file of rawAgentsFiles) {\n\t\t\ttry {\n\t\t\t\tconst rawContent = readFileSync(file.path, \"utf-8\");\n\t\t\t\tconst { profiles } = parseResourceProfileBlocks(rawContent, { profileNames: activeProfileNames });\n\t\t\t\tObject.assign(agentEmbeddedProfiles, mergeResourceProfileMap(agentEmbeddedProfiles, profiles));\n\t\t\t} catch {}\n\t\t}\n\t\tthis.settingsManager.addDiscoveredResourceProfileDefinitions(agentEmbeddedProfiles);\n\t\tconst agentProfileFilter = this.settingsManager.getResourceProfileFilter(\"agents\");\n\t\tconst agentsFiles = {\n\t\t\tagentsFiles: rawAgentsFiles\n\t\t\t\t.filter((file) => {\n\t\t\t\t\tconst allowed =\n\t\t\t\t\t\tagentProfileFilter.allow.length === 0 ||\n\t\t\t\t\t\tmatchesResourceProfilePattern(file.path, agentProfileFilter.allow, this.cwd);\n\t\t\t\t\tconst blocked = matchesResourceProfilePattern(file.path, agentProfileFilter.block, this.cwd);\n\t\t\t\t\treturn allowed && !blocked;\n\t\t\t\t})\n\t\t\t\t.map((file) => ({\n\t\t\t\t\t...file,\n\t\t\t\t\tcontent: file.content ? stripResourceProfileBlocks(file.content) : file.content,\n\t\t\t\t})),\n\t\t};\n\t\tconst resolvedAgentsFiles = this.agentsFilesOverride ? this.agentsFilesOverride(agentsFiles) : agentsFiles;\n\t\tthis.agentsFiles = resolvedAgentsFiles.agentsFiles;\n\n\t\tconst baseSystemPrompt = resolvePromptInput(\n\t\t\tthis.systemPromptSource ?? this.discoverSystemPromptFile(),\n\t\t\t\"system prompt\",\n\t\t);\n\t\tthis.systemPrompt = this.systemPromptOverride ? this.systemPromptOverride(baseSystemPrompt) : baseSystemPrompt;\n\n\t\tconst appendSources =\n\t\t\tthis.appendSystemPromptSource ??\n\t\t\t(this.discoverAppendSystemPromptFile() ? [this.discoverAppendSystemPromptFile()!] : []);\n\t\tconst baseAppend = appendSources\n\t\t\t.map((s) => resolvePromptInput(s, \"append system prompt\"))\n\t\t\t.filter((s): s is string => s !== undefined);\n\t\tthis.appendSystemPrompt = this.appendSystemPromptOverride\n\t\t\t? this.appendSystemPromptOverride(baseAppend)\n\t\t\t: baseAppend;\n\t}\n\n\tprivate normalizeExtensionPaths(\n\t\tentries: Array<{ path: string; metadata: PathMetadata }>,\n\t): Array<{ path: string; metadata: PathMetadata }> {\n\t\treturn entries.map((entry) => {\n\t\t\tconst metadata = entry.metadata.baseDir\n\t\t\t\t? { ...entry.metadata, baseDir: this.resolveResourcePath(entry.metadata.baseDir) }\n\t\t\t\t: entry.metadata;\n\t\t\treturn {\n\t\t\t\tpath: this.resolveResourcePath(entry.path),\n\t\t\t\tmetadata,\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate updateSkillsFromPaths(skillPaths: string[], metadataByPath?: Map<string, PathMetadata>): void {\n\t\tlet skillsResult: { skills: Skill[]; diagnostics: ResourceDiagnostic[] };\n\t\tif (this.noSkills && skillPaths.length === 0) {\n\t\t\tskillsResult = { skills: [], diagnostics: [] };\n\t\t} else {\n\t\t\tskillsResult = loadSkills({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.agentDir,\n\t\t\t\tskillPaths,\n\t\t\t\tincludeDefaults: false,\n\t\t\t});\n\t\t}\n\t\tconst resolvedSkills = this.skillsOverride ? this.skillsOverride(skillsResult) : skillsResult;\n\t\tthis.skills = resolvedSkills.skills.map((skill) => ({\n\t\t\t...skill,\n\t\t\tsourceInfo:\n\t\t\t\tthis.findSourceInfoForPath(skill.filePath, this.extensionSkillSourceInfos, metadataByPath) ??\n\t\t\t\tskill.sourceInfo ??\n\t\t\t\tthis.getDefaultSourceInfoForPath(skill.filePath),\n\t\t}));\n\t\tthis.skillDiagnostics = resolvedSkills.diagnostics;\n\t}\n\n\tprivate updatePromptsFromPaths(promptPaths: string[], metadataByPath?: Map<string, PathMetadata>): void {\n\t\tlet promptsResult: { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] };\n\t\tif (this.noPromptTemplates && promptPaths.length === 0) {\n\t\t\tpromptsResult = { prompts: [], diagnostics: [] };\n\t\t} else {\n\t\t\tconst allPrompts = loadPromptTemplates({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.agentDir,\n\t\t\t\tpromptPaths,\n\t\t\t\tincludeDefaults: false,\n\t\t\t});\n\t\t\tpromptsResult = this.dedupePrompts(allPrompts);\n\t\t}\n\t\tconst resolvedPrompts = this.promptsOverride ? this.promptsOverride(promptsResult) : promptsResult;\n\t\tthis.prompts = resolvedPrompts.prompts.map((prompt) => ({\n\t\t\t...prompt,\n\t\t\tsourceInfo:\n\t\t\t\tthis.findSourceInfoForPath(prompt.filePath, this.extensionPromptSourceInfos, metadataByPath) ??\n\t\t\t\tprompt.sourceInfo ??\n\t\t\t\tthis.getDefaultSourceInfoForPath(prompt.filePath),\n\t\t}));\n\t\tthis.promptDiagnostics = resolvedPrompts.diagnostics;\n\t}\n\n\tprivate updateThemesFromPaths(themePaths: string[], metadataByPath?: Map<string, PathMetadata>): void {\n\t\tlet themesResult: { themes: Theme[]; diagnostics: ResourceDiagnostic[] };\n\t\tif (this.noThemes && themePaths.length === 0) {\n\t\t\tthemesResult = { themes: [], diagnostics: [] };\n\t\t} else {\n\t\t\tconst loaded = this.loadThemes(themePaths, false);\n\t\t\tconst deduped = this.dedupeThemes(loaded.themes);\n\t\t\tthemesResult = { themes: deduped.themes, diagnostics: [...loaded.diagnostics, ...deduped.diagnostics] };\n\t\t}\n\t\tconst resolvedThemes = this.themesOverride ? this.themesOverride(themesResult) : themesResult;\n\t\tthis.themes = resolvedThemes.themes.map((theme) => {\n\t\t\tconst sourcePath = theme.sourcePath;\n\t\t\ttheme.sourceInfo = sourcePath\n\t\t\t\t? (this.findSourceInfoForPath(sourcePath, this.extensionThemeSourceInfos, metadataByPath) ??\n\t\t\t\t\ttheme.sourceInfo ??\n\t\t\t\t\tthis.getDefaultSourceInfoForPath(sourcePath))\n\t\t\t\t: theme.sourceInfo;\n\t\t\treturn theme;\n\t\t});\n\t\tthis.themeDiagnostics = resolvedThemes.diagnostics;\n\t}\n\n\tprivate applyExtensionSourceInfo(extensions: Extension[], metadataByPath: Map<string, PathMetadata>): void {\n\t\tfor (const extension of extensions) {\n\t\t\textension.sourceInfo =\n\t\t\t\tthis.findSourceInfoForPath(extension.path, undefined, metadataByPath) ??\n\t\t\t\tthis.getDefaultSourceInfoForPath(extension.path);\n\t\t\tfor (const command of extension.commands.values()) {\n\t\t\t\tcommand.sourceInfo = extension.sourceInfo;\n\t\t\t}\n\t\t\tfor (const tool of extension.tools.values()) {\n\t\t\t\ttool.sourceInfo = extension.sourceInfo;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate findSourceInfoForPath(\n\t\tresourcePath: string,\n\t\textraSourceInfos?: Map<string, SourceInfo>,\n\t\tmetadataByPath?: Map<string, PathMetadata>,\n\t): SourceInfo | undefined {\n\t\tif (!resourcePath) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (resourcePath.startsWith(\"<\")) {\n\t\t\treturn this.getDefaultSourceInfoForPath(resourcePath);\n\t\t}\n\n\t\tconst normalizedResourcePath = resolve(resourcePath);\n\t\tif (extraSourceInfos) {\n\t\t\tfor (const [sourcePath, sourceInfo] of extraSourceInfos.entries()) {\n\t\t\t\tconst normalizedSourcePath = resolve(sourcePath);\n\t\t\t\tif (\n\t\t\t\t\tnormalizedResourcePath === normalizedSourcePath ||\n\t\t\t\t\tnormalizedResourcePath.startsWith(`${normalizedSourcePath}${sep}`)\n\t\t\t\t) {\n\t\t\t\t\treturn { ...sourceInfo, path: resourcePath };\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (metadataByPath) {\n\t\t\tconst exact = metadataByPath.get(normalizedResourcePath) ?? metadataByPath.get(resourcePath);\n\t\t\tif (exact) {\n\t\t\t\treturn createSourceInfo(resourcePath, exact);\n\t\t\t}\n\n\t\t\tfor (const [sourcePath, metadata] of metadataByPath.entries()) {\n\t\t\t\tconst normalizedSourcePath = resolve(sourcePath);\n\t\t\t\tif (\n\t\t\t\t\tnormalizedResourcePath === normalizedSourcePath ||\n\t\t\t\t\tnormalizedResourcePath.startsWith(`${normalizedSourcePath}${sep}`)\n\t\t\t\t) {\n\t\t\t\t\treturn createSourceInfo(resourcePath, metadata);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate getDefaultSourceInfoForPath(filePath: string): SourceInfo {\n\t\tif (filePath.startsWith(\"<\") && filePath.endsWith(\">\")) {\n\t\t\treturn {\n\t\t\t\tpath: filePath,\n\t\t\t\tsource: filePath.slice(1, -1).split(\":\")[0] || \"temporary\",\n\t\t\t\tscope: \"temporary\",\n\t\t\t\torigin: \"top-level\",\n\t\t\t};\n\t\t}\n\n\t\tconst normalizedPath = resolve(filePath);\n\t\tconst agentRoots = [\n\t\t\tjoin(this.agentDir, \"skills\"),\n\t\t\tjoin(this.agentDir, \"prompts\"),\n\t\t\tjoin(this.agentDir, \"themes\"),\n\t\t\tjoin(this.agentDir, \"extensions\"),\n\t\t];\n\t\tconst projectRoots = [\n\t\t\tjoin(this.cwd, CONFIG_DIR_NAME, \"skills\"),\n\t\t\tjoin(this.cwd, CONFIG_DIR_NAME, \"prompts\"),\n\t\t\tjoin(this.cwd, CONFIG_DIR_NAME, \"themes\"),\n\t\t\tjoin(this.cwd, CONFIG_DIR_NAME, \"extensions\"),\n\t\t];\n\n\t\tfor (const root of agentRoots) {\n\t\t\tif (this.isUnderPath(normalizedPath, root)) {\n\t\t\t\treturn { path: filePath, source: \"local\", scope: \"user\", origin: \"top-level\", baseDir: root };\n\t\t\t}\n\t\t}\n\n\t\tfor (const root of projectRoots) {\n\t\t\tif (this.isUnderPath(normalizedPath, root)) {\n\t\t\t\treturn { path: filePath, source: \"local\", scope: \"project\", origin: \"top-level\", baseDir: root };\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tsource: \"local\",\n\t\t\tscope: \"temporary\",\n\t\t\torigin: \"top-level\",\n\t\t\tbaseDir: statSync(normalizedPath).isDirectory() ? normalizedPath : resolve(normalizedPath, \"..\"),\n\t\t};\n\t}\n\n\tprivate mergePaths(primary: string[], additional: string[]): string[] {\n\t\tconst merged: string[] = [];\n\t\tconst seen = new Set<string>();\n\n\t\tfor (const p of [...primary, ...additional]) {\n\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\tconst canonicalPath = canonicalizePath(resolved);\n\t\t\tif (seen.has(canonicalPath)) continue;\n\t\t\tseen.add(canonicalPath);\n\t\t\tmerged.push(resolved);\n\t\t}\n\n\t\treturn merged;\n\t}\n\n\tprivate resolveResourcePath(p: string): string {\n\t\treturn resolvePath(p, this.cwd, { trim: true });\n\t}\n\n\tprivate loadThemes(\n\t\tpaths: string[],\n\t\tincludeDefaults: boolean = true,\n\t): {\n\t\tthemes: Theme[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t} {\n\t\tconst themes: Theme[] = [];\n\t\tconst diagnostics: ResourceDiagnostic[] = [];\n\t\tif (includeDefaults) {\n\t\t\tconst defaultDirs = [join(this.agentDir, \"themes\"), join(this.cwd, CONFIG_DIR_NAME, \"themes\")];\n\n\t\t\tfor (const dir of defaultDirs) {\n\t\t\t\tthis.loadThemesFromDir(dir, themes, diagnostics);\n\t\t\t}\n\t\t}\n\n\t\tfor (const p of paths) {\n\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\tif (!existsSync(resolved)) {\n\t\t\t\tdiagnostics.push({ type: \"warning\", message: \"theme path does not exist\", path: resolved });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst stats = statSync(resolved);\n\t\t\t\tif (stats.isDirectory()) {\n\t\t\t\t\tthis.loadThemesFromDir(resolved, themes, diagnostics);\n\t\t\t\t} else if (stats.isFile() && resolved.endsWith(\".json\")) {\n\t\t\t\t\tthis.loadThemeFromFile(resolved, themes, diagnostics);\n\t\t\t\t} else {\n\t\t\t\t\tdiagnostics.push({ type: \"warning\", message: \"theme path is not a json file\", path: resolved });\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"failed to read theme path\";\n\t\t\t\tdiagnostics.push({ type: \"warning\", message, path: resolved });\n\t\t\t}\n\t\t}\n\n\t\treturn { themes, diagnostics };\n\t}\n\n\tprivate loadThemesFromDir(dir: string, themes: Theme[], diagnostics: ResourceDiagnostic[]): void {\n\t\tif (!existsSync(dir)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\t\t\tfor (const entry of entries) {\n\t\t\t\tlet isFile = entry.isFile();\n\t\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tisFile = statSync(join(dir, entry.name)).isFile();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!isFile) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!entry.name.endsWith(\".json\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.loadThemeFromFile(join(dir, entry.name), themes, diagnostics);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read theme directory\";\n\t\t\tdiagnostics.push({ type: \"warning\", message, path: dir });\n\t\t}\n\t}\n\n\tprivate loadThemeFromFile(filePath: string, themes: Theme[], diagnostics: ResourceDiagnostic[]): void {\n\t\ttry {\n\t\t\tthemes.push(loadThemeFromPath(filePath));\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to load theme\";\n\t\t\tdiagnostics.push({ type: \"warning\", message, path: filePath });\n\t\t}\n\t}\n\n\tprivate async loadExtensionFactories(runtime: ExtensionRuntime): Promise<{\n\t\textensions: Extension[];\n\t\terrors: Array<{ path: string; error: string }>;\n\t}> {\n\t\tconst extensions: Extension[] = [];\n\t\tconst errors: Array<{ path: string; error: string }> = [];\n\n\t\tfor (const [index, factory] of this.extensionFactories.entries()) {\n\t\t\tconst extensionPath = `<inline:${index + 1}>`;\n\t\t\ttry {\n\t\t\t\tconst extension = await loadExtensionFromFactory(factory, this.cwd, this.eventBus, runtime, extensionPath);\n\t\t\t\textensions.push(extension);\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"failed to load extension\";\n\t\t\t\terrors.push({ path: extensionPath, error: message });\n\t\t\t}\n\t\t}\n\n\t\treturn { extensions, errors };\n\t}\n\n\tprivate dedupePrompts(prompts: PromptTemplate[]): { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] } {\n\t\tconst seen = new Map<string, PromptTemplate>();\n\t\tconst diagnostics: ResourceDiagnostic[] = [];\n\n\t\tfor (const prompt of prompts) {\n\t\t\tconst existing = seen.get(prompt.name);\n\t\t\tif (existing) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"collision\",\n\t\t\t\t\tmessage: `name \"/${prompt.name}\" collision`,\n\t\t\t\t\tpath: prompt.filePath,\n\t\t\t\t\tcollision: {\n\t\t\t\t\t\tresourceType: \"prompt\",\n\t\t\t\t\t\tname: prompt.name,\n\t\t\t\t\t\twinnerPath: existing.filePath,\n\t\t\t\t\t\tloserPath: prompt.filePath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tseen.set(prompt.name, prompt);\n\t\t\t}\n\t\t}\n\n\t\treturn { prompts: Array.from(seen.values()), diagnostics };\n\t}\n\n\tprivate dedupeThemes(themes: Theme[]): { themes: Theme[]; diagnostics: ResourceDiagnostic[] } {\n\t\tconst seen = new Map<string, Theme>();\n\t\tconst diagnostics: ResourceDiagnostic[] = [];\n\n\t\tfor (const t of themes) {\n\t\t\tconst name = t.name ?? \"unnamed\";\n\t\t\tconst existing = seen.get(name);\n\t\t\tif (existing) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"collision\",\n\t\t\t\t\tmessage: `name \"${name}\" collision`,\n\t\t\t\t\tpath: t.sourcePath,\n\t\t\t\t\tcollision: {\n\t\t\t\t\t\tresourceType: \"theme\",\n\t\t\t\t\t\tname,\n\t\t\t\t\t\twinnerPath: existing.sourcePath ?? \"<builtin>\",\n\t\t\t\t\t\tloserPath: t.sourcePath ?? \"<builtin>\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tseen.set(name, t);\n\t\t\t}\n\t\t}\n\n\t\treturn { themes: Array.from(seen.values()), diagnostics };\n\t}\n\n\tprivate discoverSystemPromptFile(): string | undefined {\n\t\tconst projectPath = join(this.cwd, CONFIG_DIR_NAME, \"SYSTEM.md\");\n\t\tif (this.settingsManager.isProjectTrusted() && existsSync(projectPath)) {\n\t\t\treturn projectPath;\n\t\t}\n\n\t\tconst globalPath = join(this.agentDir, \"SYSTEM.md\");\n\t\tif (existsSync(globalPath)) {\n\t\t\treturn globalPath;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate discoverAppendSystemPromptFile(): string | undefined {\n\t\tconst projectPath = join(this.cwd, CONFIG_DIR_NAME, \"APPEND_SYSTEM.md\");\n\t\tif (this.settingsManager.isProjectTrusted() && existsSync(projectPath)) {\n\t\t\treturn projectPath;\n\t\t}\n\n\t\tconst globalPath = join(this.agentDir, \"APPEND_SYSTEM.md\");\n\t\tif (existsSync(globalPath)) {\n\t\t\treturn globalPath;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate isUnderPath(target: string, root: string): boolean {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t}\n\n\tprivate detectExtensionConflicts(extensions: Extension[]): Array<{ path: string; message: string }> {\n\t\tconst conflicts: Array<{ path: string; message: string }> = [];\n\n\t\t// Track which extension registered each tool and flag\n\t\tconst toolOwners = new Map<string, string>();\n\t\tconst flagOwners = new Map<string, string>();\n\n\t\tfor (const ext of extensions) {\n\t\t\t// Check tools\n\t\t\tfor (const toolName of ext.tools.keys()) {\n\t\t\t\tconst existingOwner = toolOwners.get(toolName);\n\t\t\t\tif (existingOwner && existingOwner !== ext.path) {\n\t\t\t\t\tconflicts.push({\n\t\t\t\t\t\tpath: ext.path,\n\t\t\t\t\t\tmessage: `Tool \"${toolName}\" conflicts with ${existingOwner}`,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\ttoolOwners.set(toolName, ext.path);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check flags\n\t\t\tfor (const flagName of ext.flags.keys()) {\n\t\t\t\tconst existingOwner = flagOwners.get(flagName);\n\t\t\t\tif (existingOwner && existingOwner !== ext.path) {\n\t\t\t\t\tconflicts.push({\n\t\t\t\t\t\tpath: ext.path,\n\t\t\t\t\t\tmessage: `Flag \"--${flagName}\" conflicts with ${existingOwner}`,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tflagOwners.set(flagName, ext.path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn conflicts;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"resource-loader.d.ts","sourceRoot":"","sources":["../../src/core/resource-loader.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,KAAK,EAAE,MAAM,qCAAqC,CAAC;AACpF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG9E,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAO/D,OAAO,KAAK,EAAa,gBAAgB,EAAoB,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACjH,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAO5D,OAAO,EAA+D,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACrH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC,MAAM,WAAW,sBAAsB;IACtC,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IAC7D,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IAC9D,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,qBAAqB;IACrC,4FAA4F;IAC5F,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,0FAA0F;IAC1F,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC9B,aAAa,IAAI,oBAAoB,CAAC;IACtC,SAAS,IAAI;QAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,CAAC;IACpE,UAAU,IAAI;QAAE,OAAO,EAAE,cAAc,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,CAAC;IAC/E,SAAS,IAAI;QAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,CAAC;IACpE,cAAc,IAAI;QAAE,WAAW,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;IAC7E,eAAe,IAAI,MAAM,GAAG,SAAS,CAAC;IACtC,qBAAqB,IAAI,MAAM,EAAE,CAAC;IAClC,eAAe,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACrD,MAAM,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,YAAY,CAAC,IAAI,IAAI,CAAC;IACtB,cAAc,CAAC,IAAI,IAAI,CAAC;CACxB;AAuDD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAEhE;AA8BD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAwC5C;AAED,MAAM,WAAW,4BAA4B;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;IACpC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,6BAA6B,CAAC,EAAE,MAAM,EAAE,CAAC;IACzC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACxC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,oBAAoB,CAAC;IAC1E,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,KAAK;QAClF,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,WAAW,EAAE,kBAAkB,EAAE,CAAC;KAClC,CAAC;IACF,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,cAAc,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,KAAK;QAC7F,OAAO,EAAE,cAAc,EAAE,CAAC;QAC1B,WAAW,EAAE,kBAAkB,EAAE,CAAC;KAClC,CAAC;IACF,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,KAAK;QAClF,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,WAAW,EAAE,kBAAkB,EAAE,CAAC;KAClC,CAAC;IACF,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,KAAK;QAC3F,WAAW,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACvD,CAAC;IACF,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,0BAA0B,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;CAC1D;AAED,qBAAa,qBAAsB,YAAW,cAAc;IAC3D,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,wBAAwB,CAAW;IAC3C,OAAO,CAAC,oBAAoB,CAAW;IACvC,OAAO,CAAC,6BAA6B,CAAW;IAChD,OAAO,CAAC,oBAAoB,CAAW;IACvC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,kBAAkB,CAAC,CAAS;IACpC,OAAO,CAAC,wBAAwB,CAAC,CAAW;IAC5C,OAAO,CAAC,kBAAkB,CAAC,CAAuD;IAClF,OAAO,CAAC,cAAc,CAAC,CAGrB;IACF,OAAO,CAAC,eAAe,CAAC,CAGtB;IACF,OAAO,CAAC,cAAc,CAAC,CAGrB;IACF,OAAO,CAAC,mBAAmB,CAAC,CAE1B;IACF,OAAO,CAAC,oBAAoB,CAAC,CAAmD;IAChF,OAAO,CAAC,0BAA0B,CAAC,CAA+B;IAElE,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,WAAW,CAA4C;IAC/D,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,kBAAkB,CAAW;IACrC,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,yBAAyB,CAA0B;IAC3D,OAAO,CAAC,0BAA0B,CAA0B;IAC5D,OAAO,CAAC,yBAAyB,CAA0B;IAC3D,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,qBAAqB,CAAqC;IAElE,YAAY,OAAO,EAAE,4BAA4B,EA6ChD;IAED,aAAa,IAAI,oBAAoB,CAEpC;IAED,SAAS,IAAI;QAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,CAElE;IAED,UAAU,IAAI;QAAE,OAAO,EAAE,cAAc,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,CAE7E;IAED,SAAS,IAAI;QAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,CAElE;IAED,cAAc,IAAI;QAAE,WAAW,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAE3E;IAED,eAAe,IAAI,MAAM,GAAG,SAAS,CAEpC;IAED,qBAAqB,IAAI,MAAM,EAAE,CAEhC;IAED,eAAe,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAsCnD;IAED,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,eAAe;IAmBvB,YAAY,IAAI,IAAI,CAInB;IAED,cAAc,IAAI,IAAI,CAKrB;IAEK,MAAM,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA8N/D;IAED,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,qBAAqB;IAuB7B,OAAO,CAAC,sBAAsB;IAwB9B,OAAO,CAAC,qBAAqB;IAsB7B,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,qBAAqB;IA8C7B,OAAO,CAAC,2BAA2B;IA6CnC,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,UAAU;IA0ClB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,iBAAiB;YASX,sBAAsB;IAqBpC,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,8BAA8B;IActC,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,wBAAwB;CAqChC","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, resolve, sep } from \"node:path\";\nimport chalk from \"chalk\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { loadThemeFromPath, type Theme } from \"../modes/interactive/theme/theme.ts\";\nimport type { ResourceDiagnostic } from \"./diagnostics.ts\";\n\nexport type { ResourceCollision, ResourceDiagnostic } from \"./diagnostics.ts\";\n\nimport { canonicalizePath, isLocalPath, resolvePath } from \"../utils/paths.ts\";\nimport { createEventBus, type EventBus } from \"./event-bus.ts\";\nimport {\n\tcreateExtensionRuntime,\n\tdisposeExtensionEventSubscriptions,\n\tloadExtensionFromFactory,\n\tloadExtensions,\n} from \"./extensions/loader.ts\";\nimport type { Extension, ExtensionFactory, ExtensionRuntime, LoadExtensionsResult } from \"./extensions/types.ts\";\nimport { DefaultPackageManager, type PathMetadata } from \"./package-manager.ts\";\nimport type { PromptTemplate } from \"./prompt-templates.ts\";\nimport { loadPromptTemplates } from \"./prompt-templates.ts\";\nimport {\n\tmergeResourceProfileMap,\n\tparseResourceProfileBlocks,\n\tstripResourceProfileBlocks,\n} from \"./resource-profile-blocks.ts\";\nimport { matchesResourceProfilePattern, type ResourceProfileSettings, SettingsManager } from \"./settings-manager.ts\";\nimport type { Skill } from \"./skills.ts\";\nimport { loadSkills } from \"./skills.ts\";\nimport { createSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\nexport interface ResourceExtensionPaths {\n\tskillPaths?: Array<{ path: string; metadata: PathMetadata }>;\n\tpromptPaths?: Array<{ path: string; metadata: PathMetadata }>;\n\tthemePaths?: Array<{ path: string; metadata: PathMetadata }>;\n}\n\nexport interface ResourceReloadOptions {\n\t/** Throw instead of accepting a hot reload that produced extension load/conflict errors. */\n\tfailOnExtensionErrors?: boolean;\n\t/** Keep the previous extension generation alive until commitReload()/rollbackReload(). */\n\tdeferExtensionDispose?: boolean;\n}\n\nexport interface ResourceLoader {\n\tgetExtensions(): LoadExtensionsResult;\n\tgetSkills(): { skills: Skill[]; diagnostics: ResourceDiagnostic[] };\n\tgetPrompts(): { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] };\n\tgetThemes(): { themes: Theme[]; diagnostics: ResourceDiagnostic[] };\n\tgetAgentsFiles(): { agentsFiles: Array<{ path: string; content?: string }> };\n\tgetSystemPrompt(): string | undefined;\n\tgetAppendSystemPrompt(): string[];\n\textendResources(paths: ResourceExtensionPaths): void;\n\treload(options?: ResourceReloadOptions): Promise<void>;\n\tcommitReload?(): void;\n\trollbackReload?(): void;\n}\n\ninterface ResourceLoaderSnapshot {\n\textensionsResult: LoadExtensionsResult;\n\tskills: Skill[];\n\tskillDiagnostics: ResourceDiagnostic[];\n\tprompts: PromptTemplate[];\n\tpromptDiagnostics: ResourceDiagnostic[];\n\tthemes: Theme[];\n\tthemeDiagnostics: ResourceDiagnostic[];\n\tagentsFiles: Array<{ path: string; content?: string }>;\n\tsystemPrompt?: string;\n\tappendSystemPrompt: string[];\n\tlastSkillPaths: string[];\n\textensionSkillSourceInfos: Map<string, SourceInfo>;\n\textensionPromptSourceInfos: Map<string, SourceInfo>;\n\textensionThemeSourceInfos: Map<string, SourceInfo>;\n\tlastPromptPaths: string[];\n\tlastThemePaths: string[];\n}\n\nfunction resolvePromptInput(input: string | undefined, description: string): string | undefined {\n\tif (!input) {\n\t\treturn undefined;\n\t}\n\n\tif (existsSync(input)) {\n\t\ttry {\n\t\t\treturn readFileSync(input, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${description} file ${input}: ${error}`));\n\t\t\treturn input;\n\t\t}\n\t}\n\n\treturn input;\n}\n\nconst CONTEXT_THREAT_PATTERNS: Array<{ label: string; pattern: RegExp }> = [\n\t{\n\t\tlabel: \"instruction override\",\n\t\tpattern:\n\t\t\t/\\b(?:ignore|disregard|override|bypass)\\b.{0,80}\\b(?:previous|prior|above|system|developer|agent)\\b.{0,80}\\binstructions?\\b/i,\n\t},\n\t{\n\t\tlabel: \"secret exfiltration\",\n\t\tpattern:\n\t\t\t/\\b(?:reveal|print|dump|exfiltrate|send|upload)\\b.{0,80}\\b(?:secrets?|tokens?|api[_ -]?keys?|credentials?|environment variables?|\\.env)\\b/i,\n\t},\n\t{\n\t\tlabel: \"hidden instruction\",\n\t\tpattern: /\\b(?:do not tell|don't tell|hide this from)\\b.{0,80}\\b(?:user|operator|developer)\\b/i,\n\t},\n];\n\nexport function scanContextFileThreats(content: string): string[] {\n\treturn CONTEXT_THREAT_PATTERNS.filter(({ pattern }) => pattern.test(content)).map(({ label }) => label);\n}\n\nfunction sanitizeContextFileContent(filePath: string, content: string): string {\n\tconst profileFreeContent = stripResourceProfileBlocks(content);\n\tconst findings = scanContextFileThreats(profileFreeContent);\n\tif (findings.length === 0) return profileFreeContent;\n\tconsole.error(chalk.yellow(`Warning: Blocked context file ${filePath}: ${findings.join(\", \")}`));\n\treturn `[BLOCKED: ${filePath} contained potential prompt injection (${findings.join(\", \")}). Content not loaded.]`;\n}\n\nfunction loadContextFilesFromDir(dir: string): Array<{ path: string; content: string }> {\n\tconst candidates = [\"AGENTS.md\", \"AGENTS.MD\", \"CLAUDE.md\", \"CLAUDE.MD\", \"GEMINI.md\", \"GEMINI.MD\"];\n\tconst files: Array<{ path: string; content: string }> = [];\n\tfor (const filename of candidates) {\n\t\tconst filePath = join(dir, filename);\n\t\tif (existsSync(filePath)) {\n\t\t\ttry {\n\t\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\tcontent: sanitizeContextFileContent(filePath, content),\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\nexport function loadProjectContextFiles(options: {\n\tcwd: string;\n\tagentDir: string;\n\tprojectTrusted?: boolean;\n}): Array<{ path: string; content?: string }> {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\n\tconst contextFiles: Array<{ path: string; content?: string }> = [];\n\tconst seenPaths = new Set<string>();\n\n\tfor (const globalContext of loadContextFilesFromDir(resolvedAgentDir)) {\n\t\tcontextFiles.push(globalContext);\n\t\tseenPaths.add(globalContext.path);\n\t}\n\n\tif (options.projectTrusted !== false) {\n\t\tconst ancestorContextFiles: Array<{ path: string; content?: string }> = [];\n\n\t\tlet currentDir = resolvedCwd;\n\t\tconst root = resolve(\"/\");\n\n\t\twhile (true) {\n\t\t\tconst contextFilesInDir = loadContextFilesFromDir(currentDir).filter(\n\t\t\t\t(contextFile) => !seenPaths.has(contextFile.path),\n\t\t\t);\n\t\t\tif (contextFilesInDir.length > 0) {\n\t\t\t\tancestorContextFiles.unshift(...contextFilesInDir);\n\t\t\t\tfor (const contextFile of contextFilesInDir) {\n\t\t\t\t\tseenPaths.add(contextFile.path);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentDir === root) break;\n\n\t\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\t\tif (parentDir === currentDir) break;\n\t\t\tcurrentDir = parentDir;\n\t\t}\n\n\t\tcontextFiles.push(...ancestorContextFiles);\n\t}\n\n\treturn contextFiles;\n}\n\nexport interface DefaultResourceLoaderOptions {\n\tcwd: string;\n\tagentDir: string;\n\tsettingsManager?: SettingsManager;\n\teventBus?: EventBus;\n\tadditionalExtensionPaths?: string[];\n\tadditionalSkillPaths?: string[];\n\tadditionalPromptTemplatePaths?: string[];\n\tadditionalThemePaths?: string[];\n\textensionFactories?: ExtensionFactory[];\n\tnoExtensions?: boolean;\n\tnoSkills?: boolean;\n\tnoPromptTemplates?: boolean;\n\tnoThemes?: boolean;\n\tnoContextFiles?: boolean;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string[];\n\textensionsOverride?: (base: LoadExtensionsResult) => LoadExtensionsResult;\n\tskillsOverride?: (base: { skills: Skill[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tskills: Skill[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tpromptsOverride?: (base: { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tprompts: PromptTemplate[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tthemesOverride?: (base: { themes: Theme[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tthemes: Theme[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tagentsFilesOverride?: (base: { agentsFiles: Array<{ path: string; content?: string }> }) => {\n\t\tagentsFiles: Array<{ path: string; content?: string }>;\n\t};\n\tsystemPromptOverride?: (base: string | undefined) => string | undefined;\n\tappendSystemPromptOverride?: (base: string[]) => string[];\n}\n\nexport class DefaultResourceLoader implements ResourceLoader {\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate settingsManager: SettingsManager;\n\tprivate eventBus: EventBus;\n\tprivate packageManager: DefaultPackageManager;\n\tprivate additionalExtensionPaths: string[];\n\tprivate additionalSkillPaths: string[];\n\tprivate additionalPromptTemplatePaths: string[];\n\tprivate additionalThemePaths: string[];\n\tprivate extensionFactories: ExtensionFactory[];\n\tprivate noExtensions: boolean;\n\tprivate noSkills: boolean;\n\tprivate noPromptTemplates: boolean;\n\tprivate noThemes: boolean;\n\tprivate noContextFiles: boolean;\n\tprivate systemPromptSource?: string;\n\tprivate appendSystemPromptSource?: string[];\n\tprivate extensionsOverride?: (base: LoadExtensionsResult) => LoadExtensionsResult;\n\tprivate skillsOverride?: (base: { skills: Skill[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tskills: Skill[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tprivate promptsOverride?: (base: { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tprompts: PromptTemplate[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tprivate themesOverride?: (base: { themes: Theme[]; diagnostics: ResourceDiagnostic[] }) => {\n\t\tthemes: Theme[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t};\n\tprivate agentsFilesOverride?: (base: { agentsFiles: Array<{ path: string; content?: string }> }) => {\n\t\tagentsFiles: Array<{ path: string; content?: string }>;\n\t};\n\tprivate systemPromptOverride?: (base: string | undefined) => string | undefined;\n\tprivate appendSystemPromptOverride?: (base: string[]) => string[];\n\n\tprivate extensionsResult: LoadExtensionsResult;\n\tprivate skills: Skill[];\n\tprivate skillDiagnostics: ResourceDiagnostic[];\n\tprivate prompts: PromptTemplate[];\n\tprivate promptDiagnostics: ResourceDiagnostic[];\n\tprivate themes: Theme[];\n\tprivate themeDiagnostics: ResourceDiagnostic[];\n\tprivate agentsFiles: Array<{ path: string; content?: string }>;\n\tprivate systemPrompt?: string;\n\tprivate appendSystemPrompt: string[];\n\tprivate lastSkillPaths: string[];\n\tprivate extensionSkillSourceInfos: Map<string, SourceInfo>;\n\tprivate extensionPromptSourceInfos: Map<string, SourceInfo>;\n\tprivate extensionThemeSourceInfos: Map<string, SourceInfo>;\n\tprivate lastPromptPaths: string[];\n\tprivate lastThemePaths: string[];\n\tprivate pendingReloadSnapshot: ResourceLoaderSnapshot | undefined;\n\n\tconstructor(options: DefaultResourceLoaderOptions) {\n\t\tthis.cwd = resolvePath(options.cwd);\n\t\tthis.agentDir = resolvePath(options.agentDir);\n\t\tthis.settingsManager = options.settingsManager ?? SettingsManager.create(this.cwd, this.agentDir);\n\t\tthis.eventBus = options.eventBus ?? createEventBus();\n\t\tthis.packageManager = new DefaultPackageManager({\n\t\t\tcwd: this.cwd,\n\t\t\tagentDir: this.agentDir,\n\t\t\tsettingsManager: this.settingsManager,\n\t\t});\n\t\tthis.additionalExtensionPaths = options.additionalExtensionPaths ?? [];\n\t\tthis.additionalSkillPaths = options.additionalSkillPaths ?? [];\n\t\tthis.additionalPromptTemplatePaths = options.additionalPromptTemplatePaths ?? [];\n\t\tthis.additionalThemePaths = options.additionalThemePaths ?? [];\n\t\tthis.noExtensions = options.noExtensions ?? false;\n\t\tthis.extensionFactories = options.extensionFactories ?? [];\n\t\tthis.noSkills = options.noSkills ?? false;\n\t\tthis.noPromptTemplates = options.noPromptTemplates ?? false;\n\t\tthis.noThemes = options.noThemes ?? false;\n\t\tthis.noContextFiles = options.noContextFiles ?? false;\n\t\tthis.systemPromptSource = options.systemPrompt;\n\t\tthis.appendSystemPromptSource = options.appendSystemPrompt;\n\t\tthis.extensionsOverride = options.extensionsOverride;\n\t\tthis.skillsOverride = options.skillsOverride;\n\t\tthis.promptsOverride = options.promptsOverride;\n\t\tthis.themesOverride = options.themesOverride;\n\t\tthis.agentsFilesOverride = options.agentsFilesOverride;\n\t\tthis.systemPromptOverride = options.systemPromptOverride;\n\t\tthis.appendSystemPromptOverride = options.appendSystemPromptOverride;\n\n\t\tthis.extensionsResult = { extensions: [], errors: [], runtime: createExtensionRuntime() };\n\t\tthis.skills = [];\n\t\tthis.skillDiagnostics = [];\n\t\tthis.prompts = [];\n\t\tthis.promptDiagnostics = [];\n\t\tthis.themes = [];\n\t\tthis.themeDiagnostics = [];\n\t\tthis.agentsFiles = [];\n\t\tthis.appendSystemPrompt = [];\n\t\tthis.lastSkillPaths = [];\n\t\tthis.extensionSkillSourceInfos = new Map();\n\t\tthis.extensionPromptSourceInfos = new Map();\n\t\tthis.extensionThemeSourceInfos = new Map();\n\t\tthis.lastPromptPaths = [];\n\t\tthis.lastThemePaths = [];\n\t}\n\n\tgetExtensions(): LoadExtensionsResult {\n\t\treturn this.extensionsResult;\n\t}\n\n\tgetSkills(): { skills: Skill[]; diagnostics: ResourceDiagnostic[] } {\n\t\treturn { skills: this.skills, diagnostics: this.skillDiagnostics };\n\t}\n\n\tgetPrompts(): { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] } {\n\t\treturn { prompts: this.prompts, diagnostics: this.promptDiagnostics };\n\t}\n\n\tgetThemes(): { themes: Theme[]; diagnostics: ResourceDiagnostic[] } {\n\t\treturn { themes: this.themes, diagnostics: this.themeDiagnostics };\n\t}\n\n\tgetAgentsFiles(): { agentsFiles: Array<{ path: string; content?: string }> } {\n\t\treturn { agentsFiles: this.agentsFiles };\n\t}\n\n\tgetSystemPrompt(): string | undefined {\n\t\treturn this.systemPrompt;\n\t}\n\n\tgetAppendSystemPrompt(): string[] {\n\t\treturn this.appendSystemPrompt;\n\t}\n\n\textendResources(paths: ResourceExtensionPaths): void {\n\t\tconst skillPaths = this.normalizeExtensionPaths(paths.skillPaths ?? []);\n\t\tconst promptPaths = this.normalizeExtensionPaths(paths.promptPaths ?? []);\n\t\tconst themePaths = this.normalizeExtensionPaths(paths.themePaths ?? []);\n\n\t\tfor (const entry of skillPaths) {\n\t\t\tthis.extensionSkillSourceInfos.set(entry.path, createSourceInfo(entry.path, entry.metadata));\n\t\t}\n\t\tfor (const entry of promptPaths) {\n\t\t\tthis.extensionPromptSourceInfos.set(entry.path, createSourceInfo(entry.path, entry.metadata));\n\t\t}\n\t\tfor (const entry of themePaths) {\n\t\t\tthis.extensionThemeSourceInfos.set(entry.path, createSourceInfo(entry.path, entry.metadata));\n\t\t}\n\n\t\tif (skillPaths.length > 0) {\n\t\t\tthis.lastSkillPaths = this.mergePaths(\n\t\t\t\tthis.lastSkillPaths,\n\t\t\t\tskillPaths.map((entry) => entry.path),\n\t\t\t);\n\t\t\tthis.updateSkillsFromPaths(this.lastSkillPaths);\n\t\t}\n\n\t\tif (promptPaths.length > 0) {\n\t\t\tthis.lastPromptPaths = this.mergePaths(\n\t\t\t\tthis.lastPromptPaths,\n\t\t\t\tpromptPaths.map((entry) => entry.path),\n\t\t\t);\n\t\t\tthis.updatePromptsFromPaths(this.lastPromptPaths);\n\t\t}\n\n\t\tif (themePaths.length > 0) {\n\t\t\tthis.lastThemePaths = this.mergePaths(\n\t\t\t\tthis.lastThemePaths,\n\t\t\t\tthemePaths.map((entry) => entry.path),\n\t\t\t);\n\t\t\tthis.updateThemesFromPaths(this.lastThemePaths);\n\t\t}\n\t}\n\n\tprivate createSnapshot(): ResourceLoaderSnapshot {\n\t\treturn {\n\t\t\textensionsResult: this.extensionsResult,\n\t\t\tskills: this.skills,\n\t\t\tskillDiagnostics: this.skillDiagnostics,\n\t\t\tprompts: this.prompts,\n\t\t\tpromptDiagnostics: this.promptDiagnostics,\n\t\t\tthemes: this.themes,\n\t\t\tthemeDiagnostics: this.themeDiagnostics,\n\t\t\tagentsFiles: this.agentsFiles,\n\t\t\tsystemPrompt: this.systemPrompt,\n\t\t\tappendSystemPrompt: this.appendSystemPrompt,\n\t\t\tlastSkillPaths: this.lastSkillPaths,\n\t\t\textensionSkillSourceInfos: this.extensionSkillSourceInfos,\n\t\t\textensionPromptSourceInfos: this.extensionPromptSourceInfos,\n\t\t\textensionThemeSourceInfos: this.extensionThemeSourceInfos,\n\t\t\tlastPromptPaths: this.lastPromptPaths,\n\t\t\tlastThemePaths: this.lastThemePaths,\n\t\t};\n\t}\n\n\tprivate restoreSnapshot(snapshot: ResourceLoaderSnapshot): void {\n\t\tthis.extensionsResult = snapshot.extensionsResult;\n\t\tthis.skills = snapshot.skills;\n\t\tthis.skillDiagnostics = snapshot.skillDiagnostics;\n\t\tthis.prompts = snapshot.prompts;\n\t\tthis.promptDiagnostics = snapshot.promptDiagnostics;\n\t\tthis.themes = snapshot.themes;\n\t\tthis.themeDiagnostics = snapshot.themeDiagnostics;\n\t\tthis.agentsFiles = snapshot.agentsFiles;\n\t\tthis.systemPrompt = snapshot.systemPrompt;\n\t\tthis.appendSystemPrompt = snapshot.appendSystemPrompt;\n\t\tthis.lastSkillPaths = snapshot.lastSkillPaths;\n\t\tthis.extensionSkillSourceInfos = snapshot.extensionSkillSourceInfos;\n\t\tthis.extensionPromptSourceInfos = snapshot.extensionPromptSourceInfos;\n\t\tthis.extensionThemeSourceInfos = snapshot.extensionThemeSourceInfos;\n\t\tthis.lastPromptPaths = snapshot.lastPromptPaths;\n\t\tthis.lastThemePaths = snapshot.lastThemePaths;\n\t}\n\n\tcommitReload(): void {\n\t\tif (!this.pendingReloadSnapshot) return;\n\t\tdisposeExtensionEventSubscriptions(this.pendingReloadSnapshot.extensionsResult.extensions);\n\t\tthis.pendingReloadSnapshot = undefined;\n\t}\n\n\trollbackReload(): void {\n\t\tif (!this.pendingReloadSnapshot) return;\n\t\tdisposeExtensionEventSubscriptions(this.extensionsResult.extensions);\n\t\tthis.restoreSnapshot(this.pendingReloadSnapshot);\n\t\tthis.pendingReloadSnapshot = undefined;\n\t}\n\n\tasync reload(options: ResourceReloadOptions = {}): Promise<void> {\n\t\tconst snapshot = this.createSnapshot();\n\t\tthis.pendingReloadSnapshot = undefined;\n\t\ttry {\n\t\t\tawait this.settingsManager.reload();\n\t\t\tconst resolvedPaths = await this.packageManager.resolve();\n\t\t\tconst cliExtensionPaths = await this.packageManager.resolveExtensionSources(this.additionalExtensionPaths, {\n\t\t\t\ttemporary: true,\n\t\t\t});\n\t\t\tconst metadataByPath = new Map<string, PathMetadata>();\n\n\t\t\tthis.extensionSkillSourceInfos = new Map();\n\t\t\tthis.extensionPromptSourceInfos = new Map();\n\t\t\tthis.extensionThemeSourceInfos = new Map();\n\n\t\t\t// Helper to extract enabled paths and store metadata\n\t\t\tconst getEnabledResources = (\n\t\t\t\tresources: Array<{ path: string; enabled: boolean; metadata: PathMetadata }>,\n\t\t\t): Array<{ path: string; enabled: boolean; metadata: PathMetadata }> => {\n\t\t\t\tfor (const r of resources) {\n\t\t\t\t\tif (!metadataByPath.has(r.path)) {\n\t\t\t\t\t\tmetadataByPath.set(r.path, r.metadata);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn resources.filter((r) => r.enabled);\n\t\t\t};\n\n\t\t\tconst getEnabledPaths = (\n\t\t\t\tresources: Array<{ path: string; enabled: boolean; metadata: PathMetadata }>,\n\t\t\t): string[] => getEnabledResources(resources).map((r) => r.path);\n\t\t\tconst enabledExtensions = getEnabledPaths(resolvedPaths.extensions);\n\t\t\tconst enabledSkillResources = getEnabledResources(resolvedPaths.skills);\n\t\t\tconst enabledPrompts = getEnabledPaths(resolvedPaths.prompts);\n\t\t\tconst enabledThemes = getEnabledPaths(resolvedPaths.themes);\n\n\t\t\tconst mapSkillPath = (resource: { path: string; metadata: PathMetadata }): string => {\n\t\t\t\tif (resource.metadata.source !== \"auto\" && resource.metadata.origin !== \"package\") {\n\t\t\t\t\treturn resource.path;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(resource.path);\n\t\t\t\t\tif (!stats.isDirectory()) {\n\t\t\t\t\t\treturn resource.path;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\treturn resource.path;\n\t\t\t\t}\n\t\t\t\tconst skillFile = join(resource.path, \"SKILL.md\");\n\t\t\t\tif (existsSync(skillFile)) {\n\t\t\t\t\tif (!metadataByPath.has(skillFile)) {\n\t\t\t\t\t\tmetadataByPath.set(skillFile, resource.metadata);\n\t\t\t\t\t}\n\t\t\t\t\treturn skillFile;\n\t\t\t\t}\n\t\t\t\treturn resource.path;\n\t\t\t};\n\n\t\t\tconst enabledSkills = enabledSkillResources.map(mapSkillPath);\n\n\t\t\t// Add CLI paths metadata\n\t\t\tfor (const r of cliExtensionPaths.extensions) {\n\t\t\t\tif (!metadataByPath.has(r.path)) {\n\t\t\t\t\tmetadataByPath.set(r.path, { source: \"cli\", scope: \"temporary\", origin: \"top-level\" });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const r of cliExtensionPaths.skills) {\n\t\t\t\tif (!metadataByPath.has(r.path)) {\n\t\t\t\t\tmetadataByPath.set(r.path, { source: \"cli\", scope: \"temporary\", origin: \"top-level\" });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst cliEnabledExtensions = getEnabledPaths(cliExtensionPaths.extensions);\n\t\t\tconst cliEnabledSkills = getEnabledPaths(cliExtensionPaths.skills);\n\t\t\tconst cliEnabledPrompts = getEnabledPaths(cliExtensionPaths.prompts);\n\t\t\tconst cliEnabledThemes = getEnabledPaths(cliExtensionPaths.themes);\n\n\t\t\tconst extensionPaths = this.noExtensions\n\t\t\t\t? cliEnabledExtensions\n\t\t\t\t: this.mergePaths(cliEnabledExtensions, enabledExtensions);\n\n\t\t\tconst extensionsResult = await loadExtensions(extensionPaths, this.cwd, this.eventBus);\n\t\t\tconst inlineExtensions = await this.loadExtensionFactories(extensionsResult.runtime);\n\t\t\textensionsResult.extensions.push(...inlineExtensions.extensions);\n\t\t\textensionsResult.errors.push(...inlineExtensions.errors);\n\n\t\t\t// Detect extension conflicts (tools, commands, flags with same names from different extensions)\n\t\t\t// Keep all extensions loaded. Conflicts are reported as diagnostics, and precedence is handled by load order.\n\t\t\tconst conflicts = this.detectExtensionConflicts(extensionsResult.extensions);\n\t\t\tfor (const conflict of conflicts) {\n\t\t\t\textensionsResult.errors.push({ path: conflict.path, error: conflict.message });\n\t\t\t}\n\n\t\t\tfor (const p of this.additionalExtensionPaths) {\n\t\t\t\tif (isLocalPath(p)) {\n\t\t\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\t\t\tif (!existsSync(resolved)) {\n\t\t\t\t\t\textensionsResult.errors.push({ path: resolved, error: `Extension path does not exist: ${resolved}` });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst resolvedExtensionsResult = this.extensionsOverride\n\t\t\t\t? this.extensionsOverride(extensionsResult)\n\t\t\t\t: extensionsResult;\n\t\t\tif (options.failOnExtensionErrors && resolvedExtensionsResult.errors.length > 0) {\n\t\t\t\tconst summary = resolvedExtensionsResult.errors\n\t\t\t\t\t.slice(0, 6)\n\t\t\t\t\t.map((error) => `${error.path}: ${error.error}`)\n\t\t\t\t\t.join(\"; \");\n\t\t\t\tthrow new Error(`Extension reload failed preflight: ${summary}`);\n\t\t\t}\n\t\t\tthis.extensionsResult = resolvedExtensionsResult;\n\t\t\tthis.applyExtensionSourceInfo(this.extensionsResult.extensions, metadataByPath);\n\n\t\t\tconst skillPaths = this.noSkills\n\t\t\t\t? this.mergePaths(cliEnabledSkills, this.additionalSkillPaths)\n\t\t\t\t: this.mergePaths([...cliEnabledSkills, ...enabledSkills], this.additionalSkillPaths);\n\n\t\t\tthis.lastSkillPaths = skillPaths;\n\t\t\tthis.updateSkillsFromPaths(skillPaths, metadataByPath);\n\t\t\tfor (const p of this.additionalSkillPaths) {\n\t\t\t\tif (isLocalPath(p)) {\n\t\t\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\t\t\tif (!existsSync(resolved) && !this.skillDiagnostics.some((d) => d.path === resolved)) {\n\t\t\t\t\t\tthis.skillDiagnostics.push({ type: \"error\", message: \"Skill path does not exist\", path: resolved });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst promptPaths = this.noPromptTemplates\n\t\t\t\t? this.mergePaths(cliEnabledPrompts, this.additionalPromptTemplatePaths)\n\t\t\t\t: this.mergePaths([...cliEnabledPrompts, ...enabledPrompts], this.additionalPromptTemplatePaths);\n\n\t\t\tthis.lastPromptPaths = promptPaths;\n\t\t\tthis.updatePromptsFromPaths(promptPaths, metadataByPath);\n\t\t\tfor (const p of this.additionalPromptTemplatePaths) {\n\t\t\t\tif (isLocalPath(p)) {\n\t\t\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\t\t\tif (!existsSync(resolved) && !this.promptDiagnostics.some((d) => d.path === resolved)) {\n\t\t\t\t\t\tthis.promptDiagnostics.push({\n\t\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\t\tmessage: \"Prompt template path does not exist\",\n\t\t\t\t\t\t\tpath: resolved,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst themePaths = this.noThemes\n\t\t\t\t? this.mergePaths(cliEnabledThemes, this.additionalThemePaths)\n\t\t\t\t: this.mergePaths([...cliEnabledThemes, ...enabledThemes], this.additionalThemePaths);\n\n\t\t\tthis.lastThemePaths = themePaths;\n\t\t\tthis.updateThemesFromPaths(themePaths, metadataByPath);\n\t\t\tfor (const p of this.additionalThemePaths) {\n\t\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\t\tif (!existsSync(resolved) && !this.themeDiagnostics.some((d) => d.path === resolved)) {\n\t\t\t\t\tthis.themeDiagnostics.push({ type: \"error\", message: \"Theme path does not exist\", path: resolved });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst rawAgentsFiles = this.noContextFiles\n\t\t\t\t? []\n\t\t\t\t: loadProjectContextFiles({\n\t\t\t\t\t\tcwd: this.cwd,\n\t\t\t\t\t\tagentDir: this.agentDir,\n\t\t\t\t\t\tprojectTrusted: this.settingsManager.isProjectTrusted(),\n\t\t\t\t\t});\n\t\t\tconst agentEmbeddedProfiles: Record<string, ResourceProfileSettings> = {};\n\t\t\tconst activeProfileNames = this.settingsManager.getActiveResourceProfileNames();\n\t\t\tfor (const file of rawAgentsFiles) {\n\t\t\t\ttry {\n\t\t\t\t\tconst rawContent = readFileSync(file.path, \"utf-8\");\n\t\t\t\t\tconst { profiles } = parseResourceProfileBlocks(rawContent, { profileNames: activeProfileNames });\n\t\t\t\t\tObject.assign(agentEmbeddedProfiles, mergeResourceProfileMap(agentEmbeddedProfiles, profiles));\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t\tthis.settingsManager.addDiscoveredResourceProfileDefinitions(agentEmbeddedProfiles);\n\t\t\tconst agentProfileFilter = this.settingsManager.getResourceProfileFilter(\"agents\");\n\t\t\tconst agentsFiles = {\n\t\t\t\tagentsFiles: rawAgentsFiles\n\t\t\t\t\t.filter((file) => {\n\t\t\t\t\t\tconst allowed =\n\t\t\t\t\t\t\tagentProfileFilter.allow.length === 0 ||\n\t\t\t\t\t\t\tmatchesResourceProfilePattern(file.path, agentProfileFilter.allow, this.cwd);\n\t\t\t\t\t\tconst blocked = matchesResourceProfilePattern(file.path, agentProfileFilter.block, this.cwd);\n\t\t\t\t\t\treturn allowed && !blocked;\n\t\t\t\t\t})\n\t\t\t\t\t.map((file) => ({\n\t\t\t\t\t\t...file,\n\t\t\t\t\t\tcontent: file.content ? stripResourceProfileBlocks(file.content) : file.content,\n\t\t\t\t\t})),\n\t\t\t};\n\t\t\tconst resolvedAgentsFiles = this.agentsFilesOverride ? this.agentsFilesOverride(agentsFiles) : agentsFiles;\n\t\t\tthis.agentsFiles = resolvedAgentsFiles.agentsFiles;\n\n\t\t\tconst baseSystemPrompt = resolvePromptInput(\n\t\t\t\tthis.systemPromptSource ?? this.discoverSystemPromptFile(),\n\t\t\t\t\"system prompt\",\n\t\t\t);\n\t\t\tthis.systemPrompt = this.systemPromptOverride ? this.systemPromptOverride(baseSystemPrompt) : baseSystemPrompt;\n\n\t\t\tconst appendSources =\n\t\t\t\tthis.appendSystemPromptSource ??\n\t\t\t\t(this.discoverAppendSystemPromptFile() ? [this.discoverAppendSystemPromptFile()!] : []);\n\t\t\tconst baseAppend = appendSources\n\t\t\t\t.map((s) => resolvePromptInput(s, \"append system prompt\"))\n\t\t\t\t.filter((s): s is string => s !== undefined);\n\t\t\tthis.appendSystemPrompt = this.appendSystemPromptOverride\n\t\t\t\t? this.appendSystemPromptOverride(baseAppend)\n\t\t\t\t: baseAppend;\n\t\t\tif (options.deferExtensionDispose) {\n\t\t\t\tthis.pendingReloadSnapshot = snapshot;\n\t\t\t} else {\n\t\t\t\tdisposeExtensionEventSubscriptions(snapshot.extensionsResult.extensions);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (this.extensionsResult !== snapshot.extensionsResult) {\n\t\t\t\tdisposeExtensionEventSubscriptions(this.extensionsResult.extensions);\n\t\t\t}\n\t\t\tthis.restoreSnapshot(snapshot);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate normalizeExtensionPaths(\n\t\tentries: Array<{ path: string; metadata: PathMetadata }>,\n\t): Array<{ path: string; metadata: PathMetadata }> {\n\t\treturn entries.map((entry) => {\n\t\t\tconst metadata = entry.metadata.baseDir\n\t\t\t\t? { ...entry.metadata, baseDir: this.resolveResourcePath(entry.metadata.baseDir) }\n\t\t\t\t: entry.metadata;\n\t\t\treturn {\n\t\t\t\tpath: this.resolveResourcePath(entry.path),\n\t\t\t\tmetadata,\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate updateSkillsFromPaths(skillPaths: string[], metadataByPath?: Map<string, PathMetadata>): void {\n\t\tlet skillsResult: { skills: Skill[]; diagnostics: ResourceDiagnostic[] };\n\t\tif (this.noSkills && skillPaths.length === 0) {\n\t\t\tskillsResult = { skills: [], diagnostics: [] };\n\t\t} else {\n\t\t\tskillsResult = loadSkills({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.agentDir,\n\t\t\t\tskillPaths,\n\t\t\t\tincludeDefaults: false,\n\t\t\t});\n\t\t}\n\t\tconst resolvedSkills = this.skillsOverride ? this.skillsOverride(skillsResult) : skillsResult;\n\t\tthis.skills = resolvedSkills.skills.map((skill) => ({\n\t\t\t...skill,\n\t\t\tsourceInfo:\n\t\t\t\tthis.findSourceInfoForPath(skill.filePath, this.extensionSkillSourceInfos, metadataByPath) ??\n\t\t\t\tskill.sourceInfo ??\n\t\t\t\tthis.getDefaultSourceInfoForPath(skill.filePath),\n\t\t}));\n\t\tthis.skillDiagnostics = resolvedSkills.diagnostics;\n\t}\n\n\tprivate updatePromptsFromPaths(promptPaths: string[], metadataByPath?: Map<string, PathMetadata>): void {\n\t\tlet promptsResult: { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] };\n\t\tif (this.noPromptTemplates && promptPaths.length === 0) {\n\t\t\tpromptsResult = { prompts: [], diagnostics: [] };\n\t\t} else {\n\t\t\tconst allPrompts = loadPromptTemplates({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.agentDir,\n\t\t\t\tpromptPaths,\n\t\t\t\tincludeDefaults: false,\n\t\t\t});\n\t\t\tpromptsResult = this.dedupePrompts(allPrompts);\n\t\t}\n\t\tconst resolvedPrompts = this.promptsOverride ? this.promptsOverride(promptsResult) : promptsResult;\n\t\tthis.prompts = resolvedPrompts.prompts.map((prompt) => ({\n\t\t\t...prompt,\n\t\t\tsourceInfo:\n\t\t\t\tthis.findSourceInfoForPath(prompt.filePath, this.extensionPromptSourceInfos, metadataByPath) ??\n\t\t\t\tprompt.sourceInfo ??\n\t\t\t\tthis.getDefaultSourceInfoForPath(prompt.filePath),\n\t\t}));\n\t\tthis.promptDiagnostics = resolvedPrompts.diagnostics;\n\t}\n\n\tprivate updateThemesFromPaths(themePaths: string[], metadataByPath?: Map<string, PathMetadata>): void {\n\t\tlet themesResult: { themes: Theme[]; diagnostics: ResourceDiagnostic[] };\n\t\tif (this.noThemes && themePaths.length === 0) {\n\t\t\tthemesResult = { themes: [], diagnostics: [] };\n\t\t} else {\n\t\t\tconst loaded = this.loadThemes(themePaths, false);\n\t\t\tconst deduped = this.dedupeThemes(loaded.themes);\n\t\t\tthemesResult = { themes: deduped.themes, diagnostics: [...loaded.diagnostics, ...deduped.diagnostics] };\n\t\t}\n\t\tconst resolvedThemes = this.themesOverride ? this.themesOverride(themesResult) : themesResult;\n\t\tthis.themes = resolvedThemes.themes.map((theme) => {\n\t\t\tconst sourcePath = theme.sourcePath;\n\t\t\ttheme.sourceInfo = sourcePath\n\t\t\t\t? (this.findSourceInfoForPath(sourcePath, this.extensionThemeSourceInfos, metadataByPath) ??\n\t\t\t\t\ttheme.sourceInfo ??\n\t\t\t\t\tthis.getDefaultSourceInfoForPath(sourcePath))\n\t\t\t\t: theme.sourceInfo;\n\t\t\treturn theme;\n\t\t});\n\t\tthis.themeDiagnostics = resolvedThemes.diagnostics;\n\t}\n\n\tprivate applyExtensionSourceInfo(extensions: Extension[], metadataByPath: Map<string, PathMetadata>): void {\n\t\tfor (const extension of extensions) {\n\t\t\textension.sourceInfo =\n\t\t\t\tthis.findSourceInfoForPath(extension.path, undefined, metadataByPath) ??\n\t\t\t\tthis.getDefaultSourceInfoForPath(extension.path);\n\t\t\tfor (const command of extension.commands.values()) {\n\t\t\t\tcommand.sourceInfo = extension.sourceInfo;\n\t\t\t}\n\t\t\tfor (const tool of extension.tools.values()) {\n\t\t\t\ttool.sourceInfo = extension.sourceInfo;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate findSourceInfoForPath(\n\t\tresourcePath: string,\n\t\textraSourceInfos?: Map<string, SourceInfo>,\n\t\tmetadataByPath?: Map<string, PathMetadata>,\n\t): SourceInfo | undefined {\n\t\tif (!resourcePath) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (resourcePath.startsWith(\"<\")) {\n\t\t\treturn this.getDefaultSourceInfoForPath(resourcePath);\n\t\t}\n\n\t\tconst normalizedResourcePath = resolve(resourcePath);\n\t\tif (extraSourceInfos) {\n\t\t\tfor (const [sourcePath, sourceInfo] of extraSourceInfos.entries()) {\n\t\t\t\tconst normalizedSourcePath = resolve(sourcePath);\n\t\t\t\tif (\n\t\t\t\t\tnormalizedResourcePath === normalizedSourcePath ||\n\t\t\t\t\tnormalizedResourcePath.startsWith(`${normalizedSourcePath}${sep}`)\n\t\t\t\t) {\n\t\t\t\t\treturn { ...sourceInfo, path: resourcePath };\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (metadataByPath) {\n\t\t\tconst exact = metadataByPath.get(normalizedResourcePath) ?? metadataByPath.get(resourcePath);\n\t\t\tif (exact) {\n\t\t\t\treturn createSourceInfo(resourcePath, exact);\n\t\t\t}\n\n\t\t\tfor (const [sourcePath, metadata] of metadataByPath.entries()) {\n\t\t\t\tconst normalizedSourcePath = resolve(sourcePath);\n\t\t\t\tif (\n\t\t\t\t\tnormalizedResourcePath === normalizedSourcePath ||\n\t\t\t\t\tnormalizedResourcePath.startsWith(`${normalizedSourcePath}${sep}`)\n\t\t\t\t) {\n\t\t\t\t\treturn createSourceInfo(resourcePath, metadata);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate getDefaultSourceInfoForPath(filePath: string): SourceInfo {\n\t\tif (filePath.startsWith(\"<\") && filePath.endsWith(\">\")) {\n\t\t\treturn {\n\t\t\t\tpath: filePath,\n\t\t\t\tsource: filePath.slice(1, -1).split(\":\")[0] || \"temporary\",\n\t\t\t\tscope: \"temporary\",\n\t\t\t\torigin: \"top-level\",\n\t\t\t};\n\t\t}\n\n\t\tconst normalizedPath = resolve(filePath);\n\t\tconst agentRoots = [\n\t\t\tjoin(this.agentDir, \"skills\"),\n\t\t\tjoin(this.agentDir, \"prompts\"),\n\t\t\tjoin(this.agentDir, \"themes\"),\n\t\t\tjoin(this.agentDir, \"extensions\"),\n\t\t];\n\t\tconst projectRoots = [\n\t\t\tjoin(this.cwd, CONFIG_DIR_NAME, \"skills\"),\n\t\t\tjoin(this.cwd, CONFIG_DIR_NAME, \"prompts\"),\n\t\t\tjoin(this.cwd, CONFIG_DIR_NAME, \"themes\"),\n\t\t\tjoin(this.cwd, CONFIG_DIR_NAME, \"extensions\"),\n\t\t];\n\n\t\tfor (const root of agentRoots) {\n\t\t\tif (this.isUnderPath(normalizedPath, root)) {\n\t\t\t\treturn { path: filePath, source: \"local\", scope: \"user\", origin: \"top-level\", baseDir: root };\n\t\t\t}\n\t\t}\n\n\t\tfor (const root of projectRoots) {\n\t\t\tif (this.isUnderPath(normalizedPath, root)) {\n\t\t\t\treturn { path: filePath, source: \"local\", scope: \"project\", origin: \"top-level\", baseDir: root };\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tsource: \"local\",\n\t\t\tscope: \"temporary\",\n\t\t\torigin: \"top-level\",\n\t\t\tbaseDir: statSync(normalizedPath).isDirectory() ? normalizedPath : resolve(normalizedPath, \"..\"),\n\t\t};\n\t}\n\n\tprivate mergePaths(primary: string[], additional: string[]): string[] {\n\t\tconst merged: string[] = [];\n\t\tconst seen = new Set<string>();\n\n\t\tfor (const p of [...primary, ...additional]) {\n\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\tconst canonicalPath = canonicalizePath(resolved);\n\t\t\tif (seen.has(canonicalPath)) continue;\n\t\t\tseen.add(canonicalPath);\n\t\t\tmerged.push(resolved);\n\t\t}\n\n\t\treturn merged;\n\t}\n\n\tprivate resolveResourcePath(p: string): string {\n\t\treturn resolvePath(p, this.cwd, { trim: true });\n\t}\n\n\tprivate loadThemes(\n\t\tpaths: string[],\n\t\tincludeDefaults: boolean = true,\n\t): {\n\t\tthemes: Theme[];\n\t\tdiagnostics: ResourceDiagnostic[];\n\t} {\n\t\tconst themes: Theme[] = [];\n\t\tconst diagnostics: ResourceDiagnostic[] = [];\n\t\tif (includeDefaults) {\n\t\t\tconst defaultDirs = [join(this.agentDir, \"themes\"), join(this.cwd, CONFIG_DIR_NAME, \"themes\")];\n\n\t\t\tfor (const dir of defaultDirs) {\n\t\t\t\tthis.loadThemesFromDir(dir, themes, diagnostics);\n\t\t\t}\n\t\t}\n\n\t\tfor (const p of paths) {\n\t\t\tconst resolved = this.resolveResourcePath(p);\n\t\t\tif (!existsSync(resolved)) {\n\t\t\t\tdiagnostics.push({ type: \"warning\", message: \"theme path does not exist\", path: resolved });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst stats = statSync(resolved);\n\t\t\t\tif (stats.isDirectory()) {\n\t\t\t\t\tthis.loadThemesFromDir(resolved, themes, diagnostics);\n\t\t\t\t} else if (stats.isFile() && resolved.endsWith(\".json\")) {\n\t\t\t\t\tthis.loadThemeFromFile(resolved, themes, diagnostics);\n\t\t\t\t} else {\n\t\t\t\t\tdiagnostics.push({ type: \"warning\", message: \"theme path is not a json file\", path: resolved });\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"failed to read theme path\";\n\t\t\t\tdiagnostics.push({ type: \"warning\", message, path: resolved });\n\t\t\t}\n\t\t}\n\n\t\treturn { themes, diagnostics };\n\t}\n\n\tprivate loadThemesFromDir(dir: string, themes: Theme[], diagnostics: ResourceDiagnostic[]): void {\n\t\tif (!existsSync(dir)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\t\t\tfor (const entry of entries) {\n\t\t\t\tlet isFile = entry.isFile();\n\t\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tisFile = statSync(join(dir, entry.name)).isFile();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!isFile) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!entry.name.endsWith(\".json\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.loadThemeFromFile(join(dir, entry.name), themes, diagnostics);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read theme directory\";\n\t\t\tdiagnostics.push({ type: \"warning\", message, path: dir });\n\t\t}\n\t}\n\n\tprivate loadThemeFromFile(filePath: string, themes: Theme[], diagnostics: ResourceDiagnostic[]): void {\n\t\ttry {\n\t\t\tthemes.push(loadThemeFromPath(filePath));\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to load theme\";\n\t\t\tdiagnostics.push({ type: \"warning\", message, path: filePath });\n\t\t}\n\t}\n\n\tprivate async loadExtensionFactories(runtime: ExtensionRuntime): Promise<{\n\t\textensions: Extension[];\n\t\terrors: Array<{ path: string; error: string }>;\n\t}> {\n\t\tconst extensions: Extension[] = [];\n\t\tconst errors: Array<{ path: string; error: string }> = [];\n\n\t\tfor (const [index, factory] of this.extensionFactories.entries()) {\n\t\t\tconst extensionPath = `<inline:${index + 1}>`;\n\t\t\ttry {\n\t\t\t\tconst extension = await loadExtensionFromFactory(factory, this.cwd, this.eventBus, runtime, extensionPath);\n\t\t\t\textensions.push(extension);\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"failed to load extension\";\n\t\t\t\terrors.push({ path: extensionPath, error: message });\n\t\t\t}\n\t\t}\n\n\t\treturn { extensions, errors };\n\t}\n\n\tprivate dedupePrompts(prompts: PromptTemplate[]): { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] } {\n\t\tconst seen = new Map<string, PromptTemplate>();\n\t\tconst diagnostics: ResourceDiagnostic[] = [];\n\n\t\tfor (const prompt of prompts) {\n\t\t\tconst existing = seen.get(prompt.name);\n\t\t\tif (existing) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"collision\",\n\t\t\t\t\tmessage: `name \"/${prompt.name}\" collision`,\n\t\t\t\t\tpath: prompt.filePath,\n\t\t\t\t\tcollision: {\n\t\t\t\t\t\tresourceType: \"prompt\",\n\t\t\t\t\t\tname: prompt.name,\n\t\t\t\t\t\twinnerPath: existing.filePath,\n\t\t\t\t\t\tloserPath: prompt.filePath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tseen.set(prompt.name, prompt);\n\t\t\t}\n\t\t}\n\n\t\treturn { prompts: Array.from(seen.values()), diagnostics };\n\t}\n\n\tprivate dedupeThemes(themes: Theme[]): { themes: Theme[]; diagnostics: ResourceDiagnostic[] } {\n\t\tconst seen = new Map<string, Theme>();\n\t\tconst diagnostics: ResourceDiagnostic[] = [];\n\n\t\tfor (const t of themes) {\n\t\t\tconst name = t.name ?? \"unnamed\";\n\t\t\tconst existing = seen.get(name);\n\t\t\tif (existing) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"collision\",\n\t\t\t\t\tmessage: `name \"${name}\" collision`,\n\t\t\t\t\tpath: t.sourcePath,\n\t\t\t\t\tcollision: {\n\t\t\t\t\t\tresourceType: \"theme\",\n\t\t\t\t\t\tname,\n\t\t\t\t\t\twinnerPath: existing.sourcePath ?? \"<builtin>\",\n\t\t\t\t\t\tloserPath: t.sourcePath ?? \"<builtin>\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tseen.set(name, t);\n\t\t\t}\n\t\t}\n\n\t\treturn { themes: Array.from(seen.values()), diagnostics };\n\t}\n\n\tprivate discoverSystemPromptFile(): string | undefined {\n\t\tconst projectPath = join(this.cwd, CONFIG_DIR_NAME, \"SYSTEM.md\");\n\t\tif (this.settingsManager.isProjectTrusted() && existsSync(projectPath)) {\n\t\t\treturn projectPath;\n\t\t}\n\n\t\tconst globalPath = join(this.agentDir, \"SYSTEM.md\");\n\t\tif (existsSync(globalPath)) {\n\t\t\treturn globalPath;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate discoverAppendSystemPromptFile(): string | undefined {\n\t\tconst projectPath = join(this.cwd, CONFIG_DIR_NAME, \"APPEND_SYSTEM.md\");\n\t\tif (this.settingsManager.isProjectTrusted() && existsSync(projectPath)) {\n\t\t\treturn projectPath;\n\t\t}\n\n\t\tconst globalPath = join(this.agentDir, \"APPEND_SYSTEM.md\");\n\t\tif (existsSync(globalPath)) {\n\t\t\treturn globalPath;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate isUnderPath(target: string, root: string): boolean {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t}\n\n\tprivate detectExtensionConflicts(extensions: Extension[]): Array<{ path: string; message: string }> {\n\t\tconst conflicts: Array<{ path: string; message: string }> = [];\n\n\t\t// Track which extension registered each tool and flag\n\t\tconst toolOwners = new Map<string, string>();\n\t\tconst flagOwners = new Map<string, string>();\n\n\t\tfor (const ext of extensions) {\n\t\t\t// Check tools\n\t\t\tfor (const toolName of ext.tools.keys()) {\n\t\t\t\tconst existingOwner = toolOwners.get(toolName);\n\t\t\t\tif (existingOwner && existingOwner !== ext.path) {\n\t\t\t\t\tconflicts.push({\n\t\t\t\t\t\tpath: ext.path,\n\t\t\t\t\t\tmessage: `Tool \"${toolName}\" conflicts with ${existingOwner}`,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\ttoolOwners.set(toolName, ext.path);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check flags\n\t\t\tfor (const flagName of ext.flags.keys()) {\n\t\t\t\tconst existingOwner = flagOwners.get(flagName);\n\t\t\t\tif (existingOwner && existingOwner !== ext.path) {\n\t\t\t\t\tconflicts.push({\n\t\t\t\t\t\tpath: ext.path,\n\t\t\t\t\t\tmessage: `Flag \"--${flagName}\" conflicts with ${existingOwner}`,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tflagOwners.set(flagName, ext.path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn conflicts;\n\t}\n}\n"]}
|