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.
@@ -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 };
@@ -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: '这是已安装目录,不表示依赖工具已启用。同名项用 sourceId 精确加载;未找到可换关键词,仅加载当前阶段相关技能。',
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: '这是已安装目录,不表示依赖工具已启用。同名项用 sourceId 精确加载;未找到可换关键词,仅加载当前阶段相关技能。',
61
+ note: '宿主提供有效工具集合时已按依赖筛选;目录不授予执行权限。同名项用 sourceId 精确加载,仅加载当前阶段相关技能。',
62
62
  }),
63
63
  };
64
64
  },
@@ -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);
@@ -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[];
@@ -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 }