minimal-agent 0.18.7 → 0.18.8
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/package.json +1 -1
- package/src/context/persistContext.js +5 -1
- package/src/context/persistContext.ts +5 -2
- package/src/loop.js +42 -20
- package/src/loop.ts +39 -18
- package/src/memory/fileStore.js +2 -34
- package/src/memory/fileStore.ts +2 -26
- package/src/prompts/policyRegistry.js +4 -8
- package/src/prompts/policyRegistry.ts +6 -8
- package/src/prompts/promptAssembly.js +16 -23
- package/src/prompts/promptAssembly.ts +22 -27
- package/src/prompts/visualPresentation.js +28 -0
- package/src/prompts/visualPresentation.ts +26 -0
- package/src/runtime/agentRuntimeContext.js +3 -0
- package/src/runtime/agentRuntimeContext.ts +4 -0
- package/src/runtime/fileLock.js +75 -0
- package/src/runtime/fileLock.ts +73 -0
- package/src/runtime/finalAnswer.js +4 -2
- package/src/runtime/finalAnswer.ts +6 -2
- package/src/runtime/outputContract.js +17 -0
- package/src/runtime/outputContract.ts +34 -0
- package/src/session/runAgentTurn.js +7 -2
- package/src/session/runAgentTurn.ts +7 -2
- package/src/session/runtimeStore.js +11 -28
- package/src/session/runtimeStore.ts +9 -23
- package/src/tools/agent/agent.js +7 -5
- package/src/tools/agent/agent.ts +7 -4
- package/src/tools/skill/searchSkills.js +2 -2
- package/src/tools/skill/searchSkills.ts +2 -2
- package/src/tools/skill/skill.js +4 -0
- package/src/tools/skill/skill.ts +3 -0
- package/src/types.ts +4 -0
- package/src/web/protocol.ts +3 -1
package/src/tools/agent/agent.js
CHANGED
|
@@ -128,10 +128,10 @@ const SUBAGENT_COMMON_APPENDIX = `# 你是子 agent(fork 分身)
|
|
|
128
128
|
*/
|
|
129
129
|
const PROMPT_CACHE_TTL_MS = 60_000;
|
|
130
130
|
const promptCache = new Map();
|
|
131
|
-
async function buildSubSystemPrompt(cwd, def, allTools, conversationMode = 'private') {
|
|
131
|
+
async function buildSubSystemPrompt(cwd, def, allTools, conversationMode = 'private', output) {
|
|
132
132
|
// Effective capabilities are part of prompt identity; a child must never
|
|
133
133
|
// reuse a cached parent-capability prompt (notably Desktop WebBrowser).
|
|
134
|
-
const key = `${cwd}::${def.type}::${conversationMode}::${allTools.map((tool) => tool.name).join(',')}`;
|
|
134
|
+
const key = `${cwd}::${def.type}::${conversationMode}::${JSON.stringify(output ?? null)}::${allTools.map((tool) => tool.name).join(',')}`;
|
|
135
135
|
const hit = promptCache.get(key);
|
|
136
136
|
if (hit && Date.now() - hit.at < PROMPT_CACHE_TTL_MS)
|
|
137
137
|
return hit.value;
|
|
@@ -143,7 +143,7 @@ async function buildSubSystemPrompt(cwd, def, allTools, conversationMode = 'priv
|
|
|
143
143
|
profile: def.type === 'general' ? 'subagent-general' : 'subagent-explore',
|
|
144
144
|
includeProjectInstructions: true, // 子 agent 在同一仓库干活,项目约定必须继承
|
|
145
145
|
includePlugins: false, // 插件是宿主命令面,子 agent 不该被引去激活插件
|
|
146
|
-
runtime: { conversationMode },
|
|
146
|
+
runtime: { conversationMode, output },
|
|
147
147
|
});
|
|
148
148
|
const value = [base, SUBAGENT_COMMON_APPENDIX, def.promptAppendix].join('\n\n');
|
|
149
149
|
promptCache.set(key, { value, at: Date.now() });
|
|
@@ -298,6 +298,7 @@ async function runAgentTaskImpl(input, signal, ctx, deps = {}) {
|
|
|
298
298
|
? 'denied'
|
|
299
299
|
: 'read-only';
|
|
300
300
|
const childRuntime = createAgentRuntimeContext({
|
|
301
|
+
output: ctx.output,
|
|
301
302
|
surface: 'subagent',
|
|
302
303
|
profile: def.type === 'general' ? 'subagent-general' : 'subagent-explore',
|
|
303
304
|
conversationMode: ctx.conversationMode,
|
|
@@ -318,6 +319,7 @@ async function runAgentTaskImpl(input, signal, ctx, deps = {}) {
|
|
|
318
319
|
includeProjectInstructions: true,
|
|
319
320
|
includePlugins: false,
|
|
320
321
|
runtime: {
|
|
322
|
+
output: childRuntime.output,
|
|
321
323
|
surface: 'subagent',
|
|
322
324
|
conversationMode: ctx.conversationMode,
|
|
323
325
|
cwd,
|
|
@@ -341,10 +343,10 @@ async function runAgentTaskImpl(input, signal, ctx, deps = {}) {
|
|
|
341
343
|
};
|
|
342
344
|
// ---- 子 history:一条 system(压缩按 role 定位,必须存在)+ runQuery 自己 push 任务书 ----
|
|
343
345
|
const history = deps.initialHistory?.length ? [...deps.initialHistory] : [
|
|
344
|
-
{ role: 'system', content: await buildSubSystemPrompt(cwd, def, subTools, ctx.conversationMode) },
|
|
346
|
+
{ role: 'system', content: await buildSubSystemPrompt(cwd, def, subTools, ctx.conversationMode, childRuntime.output) },
|
|
345
347
|
];
|
|
346
348
|
if (deps.initialHistory?.length) {
|
|
347
|
-
const system = await buildSubSystemPrompt(cwd, def, subTools, ctx.conversationMode);
|
|
349
|
+
const system = await buildSubSystemPrompt(cwd, def, subTools, ctx.conversationMode, childRuntime.output);
|
|
348
350
|
const index = history.findIndex((message) => message.role === 'system');
|
|
349
351
|
if (index >= 0)
|
|
350
352
|
history[index] = { role: 'system', content: system };
|
package/src/tools/agent/agent.ts
CHANGED
|
@@ -183,10 +183,11 @@ async function buildSubSystemPrompt(
|
|
|
183
183
|
def: AgentTypeDef,
|
|
184
184
|
allTools: Tool[],
|
|
185
185
|
conversationMode: ToolCallContext['conversationMode'] = 'private',
|
|
186
|
+
output?: ToolCallContext['output'],
|
|
186
187
|
): Promise<string> {
|
|
187
188
|
// Effective capabilities are part of prompt identity; a child must never
|
|
188
189
|
// reuse a cached parent-capability prompt (notably Desktop WebBrowser).
|
|
189
|
-
const key = `${cwd}::${def.type}::${conversationMode}::${allTools.map((tool) => tool.name).join(',')}`;
|
|
190
|
+
const key = `${cwd}::${def.type}::${conversationMode}::${JSON.stringify(output ?? null)}::${allTools.map((tool) => tool.name).join(',')}`;
|
|
190
191
|
const hit = promptCache.get(key);
|
|
191
192
|
if (hit && Date.now() - hit.at < PROMPT_CACHE_TTL_MS) return hit.value;
|
|
192
193
|
|
|
@@ -198,7 +199,7 @@ async function buildSubSystemPrompt(
|
|
|
198
199
|
profile: def.type === 'general' ? 'subagent-general' : 'subagent-explore',
|
|
199
200
|
includeProjectInstructions: true, // 子 agent 在同一仓库干活,项目约定必须继承
|
|
200
201
|
includePlugins: false, // 插件是宿主命令面,子 agent 不该被引去激活插件
|
|
201
|
-
runtime: { conversationMode },
|
|
202
|
+
runtime: { conversationMode, output },
|
|
202
203
|
});
|
|
203
204
|
const value = [base, SUBAGENT_COMMON_APPENDIX, def.promptAppendix].join('\n\n');
|
|
204
205
|
promptCache.set(key, { value, at: Date.now() });
|
|
@@ -374,6 +375,7 @@ async function runAgentTaskImpl(
|
|
|
374
375
|
? 'denied'
|
|
375
376
|
: 'read-only';
|
|
376
377
|
const childRuntime = createAgentRuntimeContext({
|
|
378
|
+
output: ctx.output,
|
|
377
379
|
surface: 'subagent',
|
|
378
380
|
profile: def.type === 'general' ? 'subagent-general' : 'subagent-explore',
|
|
379
381
|
conversationMode: ctx.conversationMode,
|
|
@@ -397,6 +399,7 @@ async function runAgentTaskImpl(
|
|
|
397
399
|
includeProjectInstructions: true,
|
|
398
400
|
includePlugins: false,
|
|
399
401
|
runtime: {
|
|
402
|
+
output: childRuntime.output,
|
|
400
403
|
surface: 'subagent',
|
|
401
404
|
conversationMode: ctx.conversationMode,
|
|
402
405
|
cwd,
|
|
@@ -421,10 +424,10 @@ async function runAgentTaskImpl(
|
|
|
421
424
|
|
|
422
425
|
// ---- 子 history:一条 system(压缩按 role 定位,必须存在)+ runQuery 自己 push 任务书 ----
|
|
423
426
|
const history: RunQueryOptions['history'] = deps.initialHistory?.length ? [...deps.initialHistory] : [
|
|
424
|
-
{ role: 'system', content: await buildSubSystemPrompt(cwd, def, subTools, ctx.conversationMode) },
|
|
427
|
+
{ role: 'system', content: await buildSubSystemPrompt(cwd, def, subTools, ctx.conversationMode, childRuntime.output) },
|
|
425
428
|
];
|
|
426
429
|
if (deps.initialHistory?.length) {
|
|
427
|
-
const system = await buildSubSystemPrompt(cwd, def, subTools, ctx.conversationMode);
|
|
430
|
+
const system = await buildSubSystemPrompt(cwd, def, subTools, ctx.conversationMode, childRuntime.output);
|
|
428
431
|
const index = history.findIndex((message) => message.role === 'system');
|
|
429
432
|
if (index >= 0) history[index] = { role: 'system', content: system };
|
|
430
433
|
else history.unshift({ role: 'system', content: system });
|
|
@@ -24,7 +24,7 @@ export const searchSkillsTool = {
|
|
|
24
24
|
}),
|
|
25
25
|
callWithContext: async (input, signal, ctx) => {
|
|
26
26
|
signal?.throwIfAborted();
|
|
27
|
-
const skills = visibleSkills(await discoverSkills(ctx.cwd, ctx));
|
|
27
|
+
const skills = visibleSkills(await discoverSkills(ctx.cwd, ctx), ctx.availableToolNames?.());
|
|
28
28
|
signal?.throwIfAborted();
|
|
29
29
|
const terms = (input.query ?? '')
|
|
30
30
|
.toLocaleLowerCase()
|
|
@@ -48,7 +48,7 @@ export const searchSkillsTool = {
|
|
|
48
48
|
total: ranked.length,
|
|
49
49
|
nextOffset: offset + page.length < ranked.length ? offset + page.length : null,
|
|
50
50
|
skills: page.map(({ skill }) => skill),
|
|
51
|
-
note: '
|
|
51
|
+
note: '宿主提供有效工具集合时已按依赖筛选;目录不授予执行权限。同名项用 sourceId 精确加载,仅加载当前阶段相关技能。',
|
|
52
52
|
}),
|
|
53
53
|
};
|
|
54
54
|
},
|
|
@@ -28,7 +28,7 @@ export const searchSkillsTool: Tool<z.infer<typeof schema>> = {
|
|
|
28
28
|
}),
|
|
29
29
|
callWithContext: async (input, signal, ctx) => {
|
|
30
30
|
signal?.throwIfAborted();
|
|
31
|
-
const skills = visibleSkills(await discoverSkills(ctx.cwd, ctx));
|
|
31
|
+
const skills = visibleSkills(await discoverSkills(ctx.cwd, ctx), ctx.availableToolNames?.());
|
|
32
32
|
signal?.throwIfAborted();
|
|
33
33
|
const terms = (input.query ?? '')
|
|
34
34
|
.toLocaleLowerCase()
|
|
@@ -58,7 +58,7 @@ export const searchSkillsTool: Tool<z.infer<typeof schema>> = {
|
|
|
58
58
|
nextOffset:
|
|
59
59
|
offset + page.length < ranked.length ? offset + page.length : null,
|
|
60
60
|
skills: page.map(({ skill }) => skill),
|
|
61
|
-
note: '
|
|
61
|
+
note: '宿主提供有效工具集合时已按依赖筛选;目录不授予执行权限。同名项用 sourceId 精确加载,仅加载当前阶段相关技能。',
|
|
62
62
|
}),
|
|
63
63
|
};
|
|
64
64
|
},
|
package/src/tools/skill/skill.js
CHANGED
|
@@ -27,6 +27,10 @@ async function call(input, context) {
|
|
|
27
27
|
ok: false,
|
|
28
28
|
error: `Skill 名称有歧义:${input.name}。请选择 sourceId 后重试。\n${JSON.stringify(found.map(({ sourceId, sourceDir }) => ({ sourceId, sourceDir })))}`,
|
|
29
29
|
};
|
|
30
|
+
const available = context.availableToolNames?.();
|
|
31
|
+
const missing = available && found[0].requiresTools?.filter(name => !available.has(name));
|
|
32
|
+
if (missing?.length)
|
|
33
|
+
return { ok: false, error: `Skill ${input.name} 当前缺少工具:${missing.join(', ')}。不能通过技能扩大执行权限。` };
|
|
30
34
|
// Search roots are ordered: project skill intentionally overrides the bundled skill.
|
|
31
35
|
const path = join(found[0].sourceDir, 'SKILL.md');
|
|
32
36
|
const content = await readFile(path);
|
package/src/tools/skill/skill.ts
CHANGED
|
@@ -42,6 +42,9 @@ async function call(
|
|
|
42
42
|
ok: false,
|
|
43
43
|
error: `Skill 名称有歧义:${input.name}。请选择 sourceId 后重试。\n${JSON.stringify(found.map(({ sourceId, sourceDir }) => ({ sourceId, sourceDir })))}`,
|
|
44
44
|
};
|
|
45
|
+
const available = context.availableToolNames?.();
|
|
46
|
+
const missing = available && found[0]!.requiresTools?.filter(name => !available.has(name));
|
|
47
|
+
if (missing?.length) return { ok: false, error: `Skill ${input.name} 当前缺少工具:${missing.join(', ')}。不能通过技能扩大执行权限。` };
|
|
45
48
|
// Search roots are ordered: project skill intentionally overrides the bundled skill.
|
|
46
49
|
const path = join(found[0]!.sourceDir, 'SKILL.md');
|
|
47
50
|
const content = await readFile(path);
|
package/src/types.ts
CHANGED
|
@@ -570,6 +570,10 @@ export type ToolResult = { problem?: ToolProblemOutcome; observation?: ReadObser
|
|
|
570
570
|
* 避免按工具名写特判分支。
|
|
571
571
|
*/
|
|
572
572
|
export interface ToolCallContext {
|
|
573
|
+
/** Host presentation only; cannot grant tools to a delegated agent. */
|
|
574
|
+
output?: import('./runtime/outputContract.js').OutputContract;
|
|
575
|
+
/** Live effective catalog, including per-turn restrictions. */
|
|
576
|
+
availableToolNames?: () => ReadonlySet<string>;
|
|
573
577
|
messageProvenance?: ConversationProvenance;
|
|
574
578
|
agentHandoff?: {
|
|
575
579
|
questionIds?: readonly string[];
|
package/src/web/protocol.ts
CHANGED
|
@@ -292,6 +292,7 @@ export interface SkillInfo {
|
|
|
292
292
|
name: string;
|
|
293
293
|
description: string;
|
|
294
294
|
source?: 'builtin' | 'workspace';
|
|
295
|
+
unavailableReason?: string;
|
|
295
296
|
}
|
|
296
297
|
|
|
297
298
|
export type ApiFormat = 'openai' | 'anthropic';
|
|
@@ -2195,6 +2196,7 @@ export type ClientFrame =
|
|
|
2195
2196
|
| { t: 'answer'; requestId: string; answers: AskAnswer[] }
|
|
2196
2197
|
| { t: 'cancelAsk'; requestId: string }
|
|
2197
2198
|
| { t: 'abort' }
|
|
2199
|
+
| { t: 'compact/abort'; requestId: string }
|
|
2198
2200
|
| { t: 'new'; dir: string; sessionId?: string }
|
|
2199
2201
|
| { t: 'compact'; dir: string; sessionId?: string; requestId?: string }
|
|
2200
2202
|
| { t: 'listDirs' }
|
|
@@ -3060,7 +3062,7 @@ export type ServerFrame =
|
|
|
3060
3062
|
ok: boolean;
|
|
3061
3063
|
message: string;
|
|
3062
3064
|
}
|
|
3063
|
-
| { t: 'skills'; skills: SkillInfo[] }
|
|
3065
|
+
| { t: 'skills'; skills: SkillInfo[]; dir?: string | null }
|
|
3064
3066
|
| { t: 'server_error'; message: string }
|
|
3065
3067
|
| { t: 'resource/crons/list'; tasks: CronTaskWithRuns[]; missedCount: number }
|
|
3066
3068
|
| { t: 'resource/crons/draft'; draft: CronDraftResult }
|