praxis-agent 0.28.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/session-service.d.ts +1 -0
- package/dist/application/session-service.js +129 -13
- package/dist/application/subagent-service.js +7 -1
- package/dist/application/top-level-agent-manager.js +8 -0
- package/dist/cli-runtime.js +96 -33
- package/dist/compatibility/claude/context.d.ts +19 -4
- package/dist/compatibility/claude/context.js +198 -24
- package/dist/core/context.d.ts +13 -0
- package/dist/core/prompt-composer.d.ts +27 -0
- package/dist/core/prompt-composer.js +99 -0
- package/dist/core/runtime.d.ts +4 -0
- package/dist/core/runtime.js +5 -0
- package/dist/mcp/claude-mcp-tools.d.ts +9 -0
- package/dist/mcp/claude-mcp-tools.js +25 -1
- package/dist/providers/anthropic-compatible.d.ts +3 -0
- package/dist/providers/anthropic-compatible.js +78 -7
- package/dist/providers/anthropic-prompt-cache.d.ts +11 -0
- package/dist/providers/anthropic-prompt-cache.js +58 -0
- package/dist/providers/environment.js +12 -0
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, matchesGlob, relative, resolve, sep } from 'node:path';
|
|
2
|
+
import { PromptComposer, } from '../../core/prompt-composer.js';
|
|
2
3
|
import { renderClaudeDynamicSystemContext, renderClaudeDynamicUserContext, } from './dynamic-context.js';
|
|
3
4
|
function renderResources(title, resources) {
|
|
4
5
|
const rendered = resources
|
|
@@ -40,22 +41,32 @@ export class ClaudeConditionalRuleResolver {
|
|
|
40
41
|
}
|
|
41
42
|
export class ClaudeContextAssembler {
|
|
42
43
|
options;
|
|
44
|
+
composer = new PromptComposer();
|
|
45
|
+
snapshots = new Map();
|
|
43
46
|
constructor(options) {
|
|
44
47
|
this.options = options;
|
|
45
48
|
}
|
|
46
49
|
async assemble(options = {}) {
|
|
47
|
-
const
|
|
50
|
+
const snapshot = this.snapshot(options);
|
|
51
|
+
const mode = options.mode ??
|
|
52
|
+
(this.options.systemPrompt !== undefined
|
|
53
|
+
? 'custom'
|
|
54
|
+
: this.options.bare
|
|
55
|
+
? 'bare'
|
|
56
|
+
: 'default');
|
|
57
|
+
const resources = mode === 'bare'
|
|
58
|
+
? { instructions: [], conditionalRules: [], memoryIndex: null }
|
|
59
|
+
: await this.loadResources(snapshot, options.cwd);
|
|
48
60
|
const sections = [
|
|
49
61
|
renderResources('Instructions', resources.instructions),
|
|
50
62
|
renderResources('Auto-memory', resources.memoryIndex ? [limitMemoryIndex(resources.memoryIndex)] : []),
|
|
51
63
|
].filter((section) => section !== null);
|
|
52
|
-
const
|
|
53
|
-
if (this.options.systemPrompt !== undefined) {
|
|
54
|
-
messages.push({ role: 'system', content: this.options.systemPrompt });
|
|
55
|
-
}
|
|
64
|
+
const sessionSections = [];
|
|
56
65
|
if (sections.length > 0) {
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
sessionSections.push({
|
|
67
|
+
id: 'shared-resources',
|
|
68
|
+
placement: 'system',
|
|
69
|
+
stability: 'session',
|
|
59
70
|
content: `# Shared Claude context
|
|
60
71
|
|
|
61
72
|
Instructions are ordered from broadest to most specific. Auto-memory is background context and does not override instructions.
|
|
@@ -63,36 +74,199 @@ Instructions are ordered from broadest to most specific. Auto-memory is backgrou
|
|
|
63
74
|
${sections.join('\n\n')}`,
|
|
64
75
|
});
|
|
65
76
|
}
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
const tailSections = [];
|
|
78
|
+
if (mode !== 'bare') {
|
|
79
|
+
sessionSections.push({
|
|
80
|
+
id: 'current-date',
|
|
81
|
+
placement: 'system',
|
|
82
|
+
stability: 'session',
|
|
83
|
+
content: `# Current date\n${this.currentDate(snapshot)}`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (mode !== 'bare') {
|
|
87
|
+
const guidance = await this.loadSessionGuidance(snapshot);
|
|
88
|
+
if (guidance?.trim()) {
|
|
89
|
+
sessionSections.push({
|
|
90
|
+
id: 'session-guidance',
|
|
91
|
+
placement: 'system',
|
|
92
|
+
stability: 'session',
|
|
93
|
+
content: guidance,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const mcpInstructions = await this.loadMcpInstructions(snapshot);
|
|
97
|
+
tailSections.push(...mcpInstructions
|
|
98
|
+
.filter(({ instructions }) => instructions.trim().length > 0)
|
|
99
|
+
.sort((left, right) => left.server.localeCompare(right.server))
|
|
100
|
+
.map(({ server, instructions }) => ({
|
|
101
|
+
id: `mcp-instructions:${server}`,
|
|
102
|
+
placement: 'system',
|
|
103
|
+
stability: 'volatile',
|
|
104
|
+
content: `# MCP server instructions: ${server}\n${instructions.trimEnd()}`,
|
|
105
|
+
})));
|
|
106
|
+
}
|
|
107
|
+
if (mode !== 'bare' && this.options.loadDynamicContext !== undefined) {
|
|
108
|
+
const dynamic = await this.loadDynamicContext(snapshot, options.cwd);
|
|
70
109
|
if (this.options.excludeDynamicSystemPromptSections) {
|
|
71
110
|
if (dynamic.memory) {
|
|
72
|
-
|
|
111
|
+
sessionSections.push({
|
|
112
|
+
id: 'memory-mechanics',
|
|
113
|
+
placement: 'system',
|
|
114
|
+
stability: 'session',
|
|
115
|
+
content: dynamic.memory,
|
|
116
|
+
});
|
|
73
117
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
118
|
+
tailSections.push({
|
|
119
|
+
id: 'relocated-runtime-context',
|
|
120
|
+
placement: 'first-user',
|
|
121
|
+
stability: 'session',
|
|
122
|
+
content: renderClaudeDynamicUserContext({
|
|
123
|
+
environment: dynamic.environment,
|
|
124
|
+
...(dynamic.gitStatus ? { gitStatus: dynamic.gitStatus } : {}),
|
|
125
|
+
}),
|
|
77
126
|
});
|
|
78
127
|
}
|
|
79
128
|
else {
|
|
80
|
-
|
|
81
|
-
|
|
129
|
+
sessionSections.push({
|
|
130
|
+
id: 'runtime-context',
|
|
131
|
+
placement: 'system',
|
|
132
|
+
stability: 'session',
|
|
82
133
|
content: renderClaudeDynamicSystemContext(dynamic),
|
|
83
134
|
});
|
|
84
135
|
}
|
|
85
136
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
137
|
+
for (const section of options.additionalSections ?? []) {
|
|
138
|
+
const target = section.stability === 'volatile' || section.placement === 'first-user'
|
|
139
|
+
? tailSections
|
|
140
|
+
: sessionSections;
|
|
141
|
+
target.push(section);
|
|
91
142
|
}
|
|
143
|
+
const composition = this.composer.compose({
|
|
144
|
+
mode,
|
|
145
|
+
...(options.baseSystemPrompt !== undefined
|
|
146
|
+
? { baseSystemPrompt: options.baseSystemPrompt }
|
|
147
|
+
: this.options.systemPrompt !== undefined
|
|
148
|
+
? { baseSystemPrompt: this.options.systemPrompt }
|
|
149
|
+
: {}),
|
|
150
|
+
...(this.options.appendSystemPrompt !== undefined
|
|
151
|
+
? { appendSystemPrompt: this.options.appendSystemPrompt }
|
|
152
|
+
: {}),
|
|
153
|
+
sessionSections,
|
|
154
|
+
tailSections,
|
|
155
|
+
});
|
|
92
156
|
return {
|
|
93
|
-
systemMessages:
|
|
94
|
-
|
|
157
|
+
systemMessages: composition.systemMessages,
|
|
158
|
+
promptSections: composition.sections,
|
|
159
|
+
stableSystemSectionCount: composition.stableSystemSectionCount,
|
|
160
|
+
...(composition.firstUserMessageContext
|
|
161
|
+
? { firstUserMessageContext: composition.firstUserMessageContext }
|
|
162
|
+
: {}),
|
|
95
163
|
};
|
|
96
164
|
}
|
|
165
|
+
invalidate(options) {
|
|
166
|
+
for (const [key, snapshot] of this.snapshots) {
|
|
167
|
+
if (options.lifecycleId !== undefined &&
|
|
168
|
+
snapshot.lifecycleId !== options.lifecycleId)
|
|
169
|
+
continue;
|
|
170
|
+
if (options.reason === 'resource-reload' ||
|
|
171
|
+
options.reason === 'compact') {
|
|
172
|
+
delete snapshot.resources;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (options.reason === 'tool-pool') {
|
|
176
|
+
delete snapshot.mcpInstructions;
|
|
177
|
+
delete snapshot.sessionGuidance;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
this.snapshots.delete(key);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
snapshot(options) {
|
|
184
|
+
if (!options.lifecycleId)
|
|
185
|
+
return undefined;
|
|
186
|
+
const key = JSON.stringify([options.lifecycleId, options.cwd ?? null]);
|
|
187
|
+
let snapshot = this.snapshots.get(key);
|
|
188
|
+
if (!snapshot) {
|
|
189
|
+
snapshot = {
|
|
190
|
+
lifecycleId: options.lifecycleId,
|
|
191
|
+
...(options.cwd ? { cwd: options.cwd } : {}),
|
|
192
|
+
};
|
|
193
|
+
this.snapshots.set(key, snapshot);
|
|
194
|
+
}
|
|
195
|
+
return snapshot;
|
|
196
|
+
}
|
|
197
|
+
loadResources(snapshot, cwd) {
|
|
198
|
+
if (!snapshot)
|
|
199
|
+
return this.options.loadResources(cwd);
|
|
200
|
+
if (!snapshot.resources) {
|
|
201
|
+
const pending = this.options.loadResources(cwd);
|
|
202
|
+
snapshot.resources = pending;
|
|
203
|
+
void pending.catch(() => {
|
|
204
|
+
if (snapshot.resources === pending)
|
|
205
|
+
delete snapshot.resources;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return snapshot.resources;
|
|
209
|
+
}
|
|
210
|
+
loadDynamicContext(snapshot, cwd) {
|
|
211
|
+
const load = this.options.loadDynamicContext;
|
|
212
|
+
if (!load)
|
|
213
|
+
throw new Error('Dynamic context loader is unavailable');
|
|
214
|
+
if (!snapshot)
|
|
215
|
+
return load(cwd);
|
|
216
|
+
if (!snapshot.dynamic) {
|
|
217
|
+
const pending = load(cwd);
|
|
218
|
+
snapshot.dynamic = pending;
|
|
219
|
+
void pending.catch(() => {
|
|
220
|
+
if (snapshot.dynamic === pending)
|
|
221
|
+
delete snapshot.dynamic;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return snapshot.dynamic;
|
|
225
|
+
}
|
|
226
|
+
loadMcpInstructions(snapshot) {
|
|
227
|
+
const load = this.options.loadMcpInstructions;
|
|
228
|
+
if (!load)
|
|
229
|
+
return Promise.resolve([]);
|
|
230
|
+
if (!snapshot)
|
|
231
|
+
return load();
|
|
232
|
+
if (!snapshot.mcpInstructions) {
|
|
233
|
+
const pending = load();
|
|
234
|
+
snapshot.mcpInstructions = pending;
|
|
235
|
+
void pending.catch(() => {
|
|
236
|
+
if (snapshot.mcpInstructions === pending)
|
|
237
|
+
delete snapshot.mcpInstructions;
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return snapshot.mcpInstructions;
|
|
241
|
+
}
|
|
242
|
+
loadSessionGuidance(snapshot) {
|
|
243
|
+
const load = this.options.loadSessionGuidance;
|
|
244
|
+
if (!load)
|
|
245
|
+
return Promise.resolve(undefined);
|
|
246
|
+
if (!snapshot)
|
|
247
|
+
return load();
|
|
248
|
+
if (!snapshot.sessionGuidance) {
|
|
249
|
+
const pending = load();
|
|
250
|
+
snapshot.sessionGuidance = pending;
|
|
251
|
+
void pending.catch(() => {
|
|
252
|
+
if (snapshot.sessionGuidance === pending)
|
|
253
|
+
delete snapshot.sessionGuidance;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return snapshot.sessionGuidance;
|
|
257
|
+
}
|
|
258
|
+
currentDate(snapshot) {
|
|
259
|
+
if (snapshot?.currentDate)
|
|
260
|
+
return snapshot.currentDate;
|
|
261
|
+
const now = (this.options.now ?? (() => new Date()))();
|
|
262
|
+
const date = [
|
|
263
|
+
String(now.getFullYear()).padStart(4, '0'),
|
|
264
|
+
String(now.getMonth() + 1).padStart(2, '0'),
|
|
265
|
+
String(now.getDate()).padStart(2, '0'),
|
|
266
|
+
].join('-');
|
|
267
|
+
if (snapshot)
|
|
268
|
+
snapshot.currentDate = date;
|
|
269
|
+
return date;
|
|
270
|
+
}
|
|
97
271
|
}
|
|
98
272
|
//# sourceMappingURL=context.js.map
|
package/dist/core/context.d.ts
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
import type { ModelMessage } from './runtime.js';
|
|
2
|
+
import type { PromptCompositionMode, PromptSection } from './prompt-composer.js';
|
|
2
3
|
export type SystemContextMessage = Extract<ModelMessage, {
|
|
3
4
|
role: 'system';
|
|
4
5
|
}>;
|
|
5
6
|
export interface AssembledContext {
|
|
6
7
|
systemMessages: readonly SystemContextMessage[];
|
|
7
8
|
firstUserMessageContext?: string;
|
|
9
|
+
promptSections?: readonly PromptSection[];
|
|
10
|
+
stableSystemSectionCount?: number;
|
|
8
11
|
}
|
|
9
12
|
export interface ContextAssemblyOptions {
|
|
10
13
|
cwd?: string;
|
|
14
|
+
lifecycleId?: string;
|
|
15
|
+
mode?: PromptCompositionMode;
|
|
16
|
+
baseSystemPrompt?: string;
|
|
17
|
+
additionalSections?: readonly PromptSection[];
|
|
18
|
+
}
|
|
19
|
+
export type ContextInvalidationReason = 'clear' | 'compact' | 'fork' | 'resource-reload' | 'restore' | 'tool-pool' | 'cwd' | 'worktree';
|
|
20
|
+
export interface ContextInvalidationOptions {
|
|
21
|
+
lifecycleId?: string;
|
|
22
|
+
reason: ContextInvalidationReason;
|
|
11
23
|
}
|
|
12
24
|
export interface ContextAssembler {
|
|
13
25
|
assemble(options?: ContextAssemblyOptions): Promise<AssembledContext>;
|
|
26
|
+
invalidate?(options: ContextInvalidationOptions): void;
|
|
14
27
|
}
|
|
15
28
|
export declare function injectFirstUserMessageContext(messages: readonly ModelMessage[], context: string | undefined): ModelMessage[];
|
|
16
29
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SystemContextMessage } from './context.js';
|
|
2
|
+
export type PromptSectionStability = 'static' | 'session' | 'volatile';
|
|
3
|
+
export type PromptSectionPlacement = 'system' | 'first-user';
|
|
4
|
+
export type PromptCompositionMode = 'default' | 'custom' | 'bare' | 'agent' | 'subagent';
|
|
5
|
+
export interface PromptSection {
|
|
6
|
+
id: string;
|
|
7
|
+
content: string;
|
|
8
|
+
stability: PromptSectionStability;
|
|
9
|
+
placement: PromptSectionPlacement;
|
|
10
|
+
}
|
|
11
|
+
export interface PromptComposition {
|
|
12
|
+
sections: readonly PromptSection[];
|
|
13
|
+
systemMessages: readonly SystemContextMessage[];
|
|
14
|
+
firstUserMessageContext?: string;
|
|
15
|
+
stableSystemSectionCount: number;
|
|
16
|
+
}
|
|
17
|
+
export interface PromptCompositionOptions {
|
|
18
|
+
mode: PromptCompositionMode;
|
|
19
|
+
baseSystemPrompt?: string;
|
|
20
|
+
appendSystemPrompt?: string;
|
|
21
|
+
sessionSections?: readonly PromptSection[];
|
|
22
|
+
tailSections?: readonly PromptSection[];
|
|
23
|
+
}
|
|
24
|
+
export declare class PromptComposer {
|
|
25
|
+
compose(options: PromptCompositionOptions): PromptComposition;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=prompt-composer.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const PRODUCT_POLICY = `# Praxis
|
|
2
|
+
|
|
3
|
+
You are Praxis, a local CLI coding agent. Work until the user's request is genuinely handled or a concrete blocker requires their input.
|
|
4
|
+
|
|
5
|
+
- Inspect relevant context before acting and use available tools deliberately.
|
|
6
|
+
- Preserve user data, existing changes, and project-specific instructions.
|
|
7
|
+
- Prefer focused, verifiable changes and report important outcomes clearly.
|
|
8
|
+
- Use a scratch or work area when the runtime provides one; keep temporary artifacts out of the user's project.
|
|
9
|
+
- Summarize or clear bulky intermediate results once their useful facts have been retained.
|
|
10
|
+
- Treat the context window as bounded: preserve decisions and evidence before reducing detail.
|
|
11
|
+
- Keep responses concise and use the user's language unless they request otherwise.`;
|
|
12
|
+
function baseSection(mode, content) {
|
|
13
|
+
if (mode === 'bare')
|
|
14
|
+
return undefined;
|
|
15
|
+
if (mode === 'default') {
|
|
16
|
+
return {
|
|
17
|
+
id: 'product-policy',
|
|
18
|
+
content: PRODUCT_POLICY,
|
|
19
|
+
placement: 'system',
|
|
20
|
+
stability: 'static',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (content === undefined || content.trim().length === 0) {
|
|
24
|
+
throw new Error(`${mode} prompt mode requires a base system prompt`);
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
id: mode === 'custom'
|
|
28
|
+
? 'custom-system'
|
|
29
|
+
: mode === 'agent'
|
|
30
|
+
? 'agent-policy'
|
|
31
|
+
: 'subagent-policy',
|
|
32
|
+
content,
|
|
33
|
+
placement: 'system',
|
|
34
|
+
stability: 'session',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function normalizedSections(sections, group) {
|
|
38
|
+
return sections
|
|
39
|
+
.filter((section) => section.content.trim().length > 0)
|
|
40
|
+
.map((section) => {
|
|
41
|
+
if (!section.id.trim())
|
|
42
|
+
throw new Error('Prompt section id is required');
|
|
43
|
+
if (group === 'sessionSections' && section.stability === 'volatile') {
|
|
44
|
+
throw new Error('sessionSections cannot contain volatile prompt sections');
|
|
45
|
+
}
|
|
46
|
+
if (group === 'tailSections' &&
|
|
47
|
+
section.placement === 'system' &&
|
|
48
|
+
section.stability !== 'volatile') {
|
|
49
|
+
throw new Error('tailSections system entries must be volatile prompt sections');
|
|
50
|
+
}
|
|
51
|
+
return { ...section };
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export class PromptComposer {
|
|
55
|
+
compose(options) {
|
|
56
|
+
const base = baseSection(options.mode, options.baseSystemPrompt);
|
|
57
|
+
const sessionSections = normalizedSections(options.sessionSections ?? [], 'sessionSections');
|
|
58
|
+
const append = options.appendSystemPrompt?.trim()
|
|
59
|
+
? {
|
|
60
|
+
id: 'append-system',
|
|
61
|
+
content: options.appendSystemPrompt,
|
|
62
|
+
placement: 'system',
|
|
63
|
+
stability: 'session',
|
|
64
|
+
}
|
|
65
|
+
: undefined;
|
|
66
|
+
const tailSections = normalizedSections(options.tailSections ?? [], 'tailSections');
|
|
67
|
+
const sections = [
|
|
68
|
+
...(base ? [base] : []),
|
|
69
|
+
...sessionSections,
|
|
70
|
+
...(append ? [append] : []),
|
|
71
|
+
...tailSections,
|
|
72
|
+
];
|
|
73
|
+
const identities = new Set();
|
|
74
|
+
for (const section of sections) {
|
|
75
|
+
if (identities.has(section.id)) {
|
|
76
|
+
throw new Error(`Duplicate prompt section ${section.id}`);
|
|
77
|
+
}
|
|
78
|
+
identities.add(section.id);
|
|
79
|
+
}
|
|
80
|
+
const systemSections = sections.filter((section) => section.placement === 'system');
|
|
81
|
+
const firstVolatileSystemIndex = systemSections.findIndex((section) => section.stability === 'volatile');
|
|
82
|
+
const firstUserMessageContext = sections
|
|
83
|
+
.filter((section) => section.placement === 'first-user')
|
|
84
|
+
.map((section) => section.content)
|
|
85
|
+
.join('\n\n');
|
|
86
|
+
return {
|
|
87
|
+
sections,
|
|
88
|
+
systemMessages: systemSections.map((section) => ({
|
|
89
|
+
role: 'system',
|
|
90
|
+
content: section.content,
|
|
91
|
+
})),
|
|
92
|
+
stableSystemSectionCount: firstVolatileSystemIndex < 0
|
|
93
|
+
? systemSections.length
|
|
94
|
+
: firstVolatileSystemIndex,
|
|
95
|
+
...(firstUserMessageContext ? { firstUserMessageContext } : {}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=prompt-composer.js.map
|
package/dist/core/runtime.d.ts
CHANGED
|
@@ -119,6 +119,8 @@ export type ModelStreamEvent = {
|
|
|
119
119
|
};
|
|
120
120
|
export interface ModelRequest {
|
|
121
121
|
messages: readonly ModelMessage[];
|
|
122
|
+
/** Number of leading system messages that form the stable prompt prefix. */
|
|
123
|
+
stableSystemMessageCount?: number;
|
|
122
124
|
tools?: readonly ModelToolDefinition[];
|
|
123
125
|
webSearch?: ModelWebSearch;
|
|
124
126
|
signal?: AbortSignal;
|
|
@@ -450,6 +452,8 @@ export interface AgentRuntimeOptions {
|
|
|
450
452
|
}
|
|
451
453
|
export interface AgentRunRequest {
|
|
452
454
|
messages: readonly ModelMessage[];
|
|
455
|
+
/** Number of leading system messages that form the stable prompt prefix. */
|
|
456
|
+
stableSystemMessageCount?: number;
|
|
453
457
|
cwd?: string;
|
|
454
458
|
toolResultDirectory?: string;
|
|
455
459
|
observer?: AgentRunObserver;
|
package/dist/core/runtime.js
CHANGED
|
@@ -342,6 +342,11 @@ export class AgentRuntime {
|
|
|
342
342
|
this.emit({ type: 'state', state: 'awaiting-model' });
|
|
343
343
|
const providerRequest = {
|
|
344
344
|
messages: prepareProviderMessages(messages, this.provider.capabilities.images === true, this.provider.capabilities.documents === true),
|
|
345
|
+
...(request.stableSystemMessageCount === undefined
|
|
346
|
+
? {}
|
|
347
|
+
: {
|
|
348
|
+
stableSystemMessageCount: request.stableSystemMessageCount,
|
|
349
|
+
}),
|
|
345
350
|
};
|
|
346
351
|
if (definitions.length > 0)
|
|
347
352
|
providerRequest.tools = definitions;
|
|
@@ -35,6 +35,7 @@ export interface ClaudeMcpRuntime {
|
|
|
35
35
|
authenticate(name: string): Promise<void>;
|
|
36
36
|
reload(): Promise<void>;
|
|
37
37
|
tools(name: string): Promise<readonly ClaudeMcpToolInspection[]>;
|
|
38
|
+
instructions?(): readonly ClaudeMcpServerInstruction[];
|
|
38
39
|
connectAgent?(options: {
|
|
39
40
|
specs: readonly unknown[];
|
|
40
41
|
base: ToolRegistry;
|
|
@@ -47,6 +48,10 @@ export interface ClaudeMcpRuntime {
|
|
|
47
48
|
/** Release MCP transports and child processes owned by this runtime. */
|
|
48
49
|
close?(): Promise<void>;
|
|
49
50
|
}
|
|
51
|
+
export interface ClaudeMcpServerInstruction {
|
|
52
|
+
server: string;
|
|
53
|
+
instructions: string;
|
|
54
|
+
}
|
|
50
55
|
export interface ClaudeMcpConfigurationStatus {
|
|
51
56
|
name: string;
|
|
52
57
|
path: string;
|
|
@@ -67,6 +72,7 @@ export interface ClaudeMcpToolRegistryOptions {
|
|
|
67
72
|
signal?: AbortSignal;
|
|
68
73
|
eventSink?: RuntimeEventSink;
|
|
69
74
|
onPromptsChanged?: (prompts: readonly ClaudeMcpPromptDefinition[]) => void;
|
|
75
|
+
onInstructionsChanged?: (instructions: readonly ClaudeMcpServerInstruction[]) => void;
|
|
70
76
|
authenticateServer?: (name: string) => Promise<void>;
|
|
71
77
|
reloadResources?: () => Promise<readonly ClaudeJsonResource[]>;
|
|
72
78
|
onElicitation?: (request: {
|
|
@@ -93,6 +99,7 @@ export declare class ClaudeMcpToolRegistry implements ToolRegistry, ClaudeMcpRun
|
|
|
93
99
|
private readonly serverClients;
|
|
94
100
|
private readonly reconnectableServers;
|
|
95
101
|
private readonly serverCapabilities;
|
|
102
|
+
private readonly serverInstructions;
|
|
96
103
|
private readonly reconnectingServers;
|
|
97
104
|
private readonly promptOperations;
|
|
98
105
|
private promptResultDirectoryPromise;
|
|
@@ -104,6 +111,7 @@ export declare class ClaudeMcpToolRegistry implements ToolRegistry, ClaudeMcpRun
|
|
|
104
111
|
definitions(): readonly ModelToolDefinition[];
|
|
105
112
|
schedulingPolicy(call: ModelToolCall): import("../core/runtime.js").ToolSchedulingPolicy;
|
|
106
113
|
serverStatuses(): readonly ClaudeMcpServerStatus[];
|
|
114
|
+
instructions(): readonly ClaudeMcpServerInstruction[];
|
|
107
115
|
inspect(): Promise<readonly ClaudeMcpServerStatus[]>;
|
|
108
116
|
reconnect(name: string): Promise<void>;
|
|
109
117
|
authenticate(name: string): Promise<void>;
|
|
@@ -137,6 +145,7 @@ export declare class ClaudeMcpToolRegistry implements ToolRegistry, ClaudeMcpRun
|
|
|
137
145
|
private ensurePromptConnected;
|
|
138
146
|
private reconnectServer;
|
|
139
147
|
private publishPrompts;
|
|
148
|
+
private publishInstructions;
|
|
140
149
|
private assertOpenGeneration;
|
|
141
150
|
private trackPromptOperation;
|
|
142
151
|
private promptResultDirectory;
|
|
@@ -680,6 +680,7 @@ export class ClaudeMcpToolRegistry {
|
|
|
680
680
|
serverClients = new Map();
|
|
681
681
|
reconnectableServers = new Map();
|
|
682
682
|
serverCapabilities = new Map();
|
|
683
|
+
serverInstructions = new Map();
|
|
683
684
|
reconnectingServers = new Map();
|
|
684
685
|
promptOperations = new Set();
|
|
685
686
|
promptResultDirectoryPromise;
|
|
@@ -705,10 +706,11 @@ export class ClaudeMcpToolRegistry {
|
|
|
705
706
|
definitions() {
|
|
706
707
|
return [
|
|
707
708
|
...this.options.base.definitions(),
|
|
709
|
+
...(this.resourceServers.size > 0 ? MCP_RESOURCE_TOOL_DEFINITIONS : []),
|
|
708
710
|
...[...this.connectedTools.entries()]
|
|
709
711
|
.filter(([name]) => !this.reservedTools.has(name))
|
|
712
|
+
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
|
|
710
713
|
.map(([, tool]) => tool.definition),
|
|
711
|
-
...(this.resourceServers.size > 0 ? MCP_RESOURCE_TOOL_DEFINITIONS : []),
|
|
712
714
|
];
|
|
713
715
|
}
|
|
714
716
|
schedulingPolicy(call) {
|
|
@@ -732,6 +734,11 @@ export class ClaudeMcpToolRegistry {
|
|
|
732
734
|
status,
|
|
733
735
|
}));
|
|
734
736
|
}
|
|
737
|
+
instructions() {
|
|
738
|
+
return [...this.serverInstructions]
|
|
739
|
+
.map(([server, instructions]) => ({ server, instructions }))
|
|
740
|
+
.sort((left, right) => left.server.localeCompare(right.server));
|
|
741
|
+
}
|
|
735
742
|
async inspect() {
|
|
736
743
|
return this.runtimeStatuses();
|
|
737
744
|
}
|
|
@@ -763,6 +770,7 @@ export class ClaudeMcpToolRegistry {
|
|
|
763
770
|
this.statuses.clear();
|
|
764
771
|
this.reconnectableServers.clear();
|
|
765
772
|
this.serverCapabilities.clear();
|
|
773
|
+
this.serverInstructions.clear();
|
|
766
774
|
const ambientSensitiveValues = sensitiveEnvironmentValues(process.env);
|
|
767
775
|
const warn = (message) => this.options.onWarning?.(redactSensitiveText(message, ambientSensitiveValues));
|
|
768
776
|
await this.configure(this.options.reloadResources
|
|
@@ -962,6 +970,7 @@ export class ClaudeMcpToolRegistry {
|
|
|
962
970
|
await Promise.allSettled([...this.clients].map((client) => client.close()));
|
|
963
971
|
this.clients.clear();
|
|
964
972
|
this.serverClients.clear();
|
|
973
|
+
this.serverInstructions.clear();
|
|
965
974
|
const directory = await this.promptResultDirectoryPromise?.catch(() => undefined);
|
|
966
975
|
if (directory)
|
|
967
976
|
await rm(directory, { recursive: true, force: true });
|
|
@@ -1052,7 +1061,9 @@ export class ClaudeMcpToolRegistry {
|
|
|
1052
1061
|
if (this.closed || this.serverClients.get(serverName) !== client)
|
|
1053
1062
|
return;
|
|
1054
1063
|
this.serverCapabilities.delete(serverName);
|
|
1064
|
+
this.serverInstructions.delete(serverName);
|
|
1055
1065
|
this.statuses.set(serverName, { name: serverName, status: 'failed' });
|
|
1066
|
+
this.publishInstructions();
|
|
1056
1067
|
};
|
|
1057
1068
|
client.setRequestHandler(ElicitRequestSchema, async (request) => {
|
|
1058
1069
|
if (!this.options.onElicitation)
|
|
@@ -1131,6 +1142,13 @@ export class ClaudeMcpToolRegistry {
|
|
|
1131
1142
|
...(capabilities?.resources ? ['resources'] : []),
|
|
1132
1143
|
...(capabilities?.prompts ? ['prompts'] : []),
|
|
1133
1144
|
]);
|
|
1145
|
+
const serverInstruction = client.getInstructions()?.trim();
|
|
1146
|
+
if (serverInstruction) {
|
|
1147
|
+
this.serverInstructions.set(serverName, redactSensitiveText(serverInstruction, sensitiveValues));
|
|
1148
|
+
}
|
|
1149
|
+
else {
|
|
1150
|
+
this.serverInstructions.delete(serverName);
|
|
1151
|
+
}
|
|
1134
1152
|
for (const [name, tool] of connectedTools)
|
|
1135
1153
|
this.connectedTools.set(name, tool);
|
|
1136
1154
|
if (capabilities?.resources) {
|
|
@@ -1157,6 +1175,7 @@ export class ClaudeMcpToolRegistry {
|
|
|
1157
1175
|
status: 'connected',
|
|
1158
1176
|
statusDetail: 'connected',
|
|
1159
1177
|
});
|
|
1178
|
+
this.publishInstructions();
|
|
1160
1179
|
}
|
|
1161
1180
|
catch (error) {
|
|
1162
1181
|
await client.close().catch(() => undefined);
|
|
@@ -1383,6 +1402,7 @@ export class ClaudeMcpToolRegistry {
|
|
|
1383
1402
|
this.resourceServers.delete(serverName);
|
|
1384
1403
|
this.promptServers.delete(serverName);
|
|
1385
1404
|
this.serverCapabilities.delete(serverName);
|
|
1405
|
+
this.serverInstructions.delete(serverName);
|
|
1386
1406
|
await this.connectServer(serverName, reconnectable.config, reconnectable.sensitiveValues, expectedGeneration);
|
|
1387
1407
|
if (this.statuses.get(serverName)?.status !== 'connected') {
|
|
1388
1408
|
throw new Error(`MCP server ${serverName} could not reconnect`);
|
|
@@ -1393,6 +1413,10 @@ export class ClaudeMcpToolRegistry {
|
|
|
1393
1413
|
if (!this.closed)
|
|
1394
1414
|
this.options.onPromptsChanged?.(this.prompts());
|
|
1395
1415
|
}
|
|
1416
|
+
publishInstructions() {
|
|
1417
|
+
if (!this.closed)
|
|
1418
|
+
this.options.onInstructionsChanged?.(this.instructions());
|
|
1419
|
+
}
|
|
1396
1420
|
assertOpenGeneration(expectedGeneration) {
|
|
1397
1421
|
if (this.closed || expectedGeneration !== this.generation) {
|
|
1398
1422
|
throw new Error('MCP registry is closed');
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ModelProvider, type ModelRequest, type ModelStreamEvent, type ModelThinkingConfig } from '../core/runtime.js';
|
|
2
|
+
import { type AnthropicPromptCachePolicy } from './anthropic-prompt-cache.js';
|
|
2
3
|
export interface AnthropicCompatibleProviderOptions {
|
|
3
4
|
baseUrl: string;
|
|
4
5
|
apiKey: string;
|
|
@@ -6,6 +7,7 @@ export interface AnthropicCompatibleProviderOptions {
|
|
|
6
7
|
maxOutputTokens?: number;
|
|
7
8
|
anthropicVersion?: string;
|
|
8
9
|
webSearch?: boolean;
|
|
10
|
+
promptCaching?: AnthropicPromptCachePolicy;
|
|
9
11
|
contextWindowTokens?: number;
|
|
10
12
|
thinking?: ModelThinkingConfig;
|
|
11
13
|
maxStreamBufferBytes?: number;
|
|
@@ -29,6 +31,7 @@ export declare class AnthropicCompatibleProvider implements ModelProvider {
|
|
|
29
31
|
private readonly maxToolMetadataBytes;
|
|
30
32
|
private readonly maxErrorBodyBytes;
|
|
31
33
|
private readonly thinking;
|
|
34
|
+
private readonly promptCaching;
|
|
32
35
|
constructor(options: AnthropicCompatibleProviderOptions);
|
|
33
36
|
complete(request: ModelRequest): AsyncIterable<ModelStreamEvent>;
|
|
34
37
|
}
|