feat: 注册所有新命令到命令系统和工具注册表

- commands.ts: 注册所有新命令(memory-stores、vault、schedule 等),
  移除 require() 动态加载,统一为 ESM import
- tools.ts: 注册 LocalMemoryRecallTool、VaultHttpFetchTool
- 补充命令测试(bridge-kick、commit、commit-push-pr、init-verifiers)
- 补充工具测试(AgentTool、RemoteTrigger、SkillTool、WebFetch、WebSearch)
- 集成测试:autonomy-lifecycle-user-flow 更新
- 探测脚本和功能文档

Co-Authored-By: glm-5-turbo <zai-org@claude-code-best.win>
This commit is contained in:
claude-code-best
2026-05-09 23:04:39 +08:00
parent efaf4afd9c
commit 6a182e45b3
25 changed files with 3148 additions and 94 deletions

View File

@@ -38,6 +38,7 @@ import {
type BackgroundRemoteSessionPrecondition,
} from 'src/tasks/RemoteAgentTask/RemoteAgentTask.js';
import { assembleToolPool } from 'src/tools.js';
import { filterParentToolsForFork } from 'src/utils/agentToolFilter.js';
import { asAgentId } from 'src/types/ids.js';
import { runWithAgentContext, type SubagentContext } from 'src/utils/agentContext.js';
import { isAgentSwarmsEnabled } from 'src/utils/agentSwarmsEnabled.js';
@@ -148,12 +149,6 @@ const baseInputSchema = lazySchema(() =>
.boolean()
.optional()
.describe('Set to true to run this agent in the background. You will be notified when it completes.'),
fork: z
.boolean()
.optional()
.describe(
'Set to true to fork from the parent conversation context. The child inherits full history, system prompt, and model. Requires FORK_SUBAGENT feature flag.',
),
}),
);
@@ -197,23 +192,24 @@ const fullInputSchema = lazySchema(() => {
// type, but call() destructures via the explicit AgentToolInput type below
// which always includes all optional fields.
export const inputSchema = lazySchema(() => {
const base = feature('KAIROS') ? fullInputSchema() : fullInputSchema().omit({ cwd: true });
return isBackgroundTasksDisabled
? !isForkSubagentEnabled()
? base.omit({ run_in_background: true, fork: true })
: base.omit({ run_in_background: true })
: !isForkSubagentEnabled()
? base.omit({ fork: true })
: base;
const schema = feature('KAIROS') ? fullInputSchema() : fullInputSchema().omit({ cwd: true });
// GrowthBook-in-lazySchema is acceptable here (unlike subagent_type, which
// was removed in 906da6c723): the divergence window is one-session-per-
// gate-flip via _CACHED_MAY_BE_STALE disk read, and worst case is either
// "schema shows a no-op param" (gate flips on mid-session: param ignored
// by forceAsync) or "schema hides a param that would've worked" (gate
// flips off mid-session: everything still runs async via memoized
// forceAsync). No Zod rejection, no crash — unlike required→optional.
return isBackgroundTasksDisabled || isForkSubagentEnabled() ? schema.omit({ run_in_background: true }) : schema;
});
type InputSchema = ReturnType<typeof inputSchema>;
// Explicit type widens the schema inference to always include all optional
// fields even when .omit() strips them for gating (cwd, run_in_background).
// subagent_type is optional; call() defaults it to general-purpose.
// fork is gated by FORK_SUBAGENT flag; when omitted or flag is off, no fork.
// subagent_type is optional; call() defaults it to general-purpose when the
// fork gate is off, or routes to the fork path when the gate is on.
type AgentToolInput = z.infer<ReturnType<typeof baseInputSchema>> & {
fork?: boolean;
name?: string;
team_name?: string;
mode?: z.infer<ReturnType<typeof permissionModeSchema>>;
@@ -327,7 +323,6 @@ export const AgentTool = buildTool({
{
prompt,
subagent_type,
fork,
description,
model: modelParam,
run_in_background,
@@ -412,11 +407,12 @@ export const AgentTool = buildTool({
return { data: spawnResult } as unknown as { data: Output };
}
// Fork routing: explicit `fork: true` parameter triggers the fork path
// (inherits parent context and model). Requires FORK_SUBAGENT flag.
// subagent_type is ignored when fork takes effect.
const isForkPath = fork === true && isForkSubagentEnabled();
const effectiveType = subagent_type ?? GENERAL_PURPOSE_AGENT.agentType;
// Fork subagent experiment routing:
// - subagent_type set: use it (explicit wins)
// - subagent_type omitted, gate on: fork path (undefined)
// - subagent_type omitted, gate off: default general-purpose
const effectiveType = subagent_type ?? (isForkSubagentEnabled() ? undefined : GENERAL_PURPOSE_AGENT.agentType);
const isForkPath = effectiveType === undefined;
let selectedAgent: AgentDefinition;
if (isForkPath) {
@@ -697,6 +693,10 @@ export const AgentTool = buildTool({
// dependency issues during test module loading.
const isCoordinator = feature('COORDINATOR_MODE') ? isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE) : false;
// Fork subagent experiment: force ALL spawns async for a unified
// <task-notification> interaction model (not just fork spawns — all of them).
const forceAsync = isForkSubagentEnabled();
// Assistant mode: force all agents async. Synchronous subagents hold the
// main loop's turn open until they complete — the daemon's inputQueue
// backs up, and the first overdue cron catch-up on spawn becomes N
@@ -710,6 +710,7 @@ export const AgentTool = buildTool({
(run_in_background === true ||
selectedAgent.background === true ||
isCoordinator ||
forceAsync ||
assistantForceAsync ||
(proactiveModule?.isProactiveActive() ?? false)) &&
!isBackgroundTasksDisabled;
@@ -778,7 +779,7 @@ export const AgentTool = buildTool({
: enhancedSystemPrompt && !worktreeInfo && !cwd
? { systemPrompt: asSystemPrompt(enhancedSystemPrompt) }
: undefined,
availableTools: isForkPath ? toolUseContext.options.tools : workerTools,
availableTools: isForkPath ? filterParentToolsForFork(toolUseContext.options.tools) : workerTools,
// Pass parent conversation when the fork-subagent path needs full
// context. useExactTools inherits thinkingConfig (runAgent.ts:624).
forkContextMessages: isForkPath ? toolUseContext.messages : undefined,
@@ -889,7 +890,7 @@ export const AgentTool = buildTool({
toolUseContext,
rootSetAppState,
agentIdForCleanup: asyncAgentId,
enableSummarization: isCoordinator || isForkPath || getSdkAgentProgressSummariesEnabled(),
enableSummarization: isCoordinator || isForkSubagentEnabled() || getSdkAgentProgressSummariesEnabled(),
getWorktreeResult: cleanupWorktreeIfNeeded,
}),
),

View File

@@ -0,0 +1,19 @@
import { describe, expect, mock, test } from 'bun:test'
mock.module('bun:bundle', () => ({
feature: (_name: string) => true,
}))
describe('resumeAgent', () => {
test('module exports resumeAgentBackground', async () => {
const mod = await import('../resumeAgent.js')
expect(typeof mod.resumeAgentBackground).toBe('function')
})
test('module exports ResumeAgentResult type (compile-time)', async () => {
// TypeScript-only: just ensure the module loads cleanly so the type
// surface is in the patch coverage trace.
const mod = await import('../resumeAgent.js')
expect(mod).toBeDefined()
})
})

View File

@@ -6,6 +6,7 @@ import type { CanUseToolFn } from 'src/hooks/useCanUseTool.js'
import type { ToolUseContext } from 'src/Tool.js'
import { registerAsyncAgent } from 'src/tasks/LocalAgentTask/LocalAgentTask.js'
import { assembleToolPool } from 'src/tools.js'
import { filterParentToolsForFork } from 'src/utils/agentToolFilter.js'
import { asAgentId } from 'src/types/ids.js'
import { runWithAgentContext } from 'src/utils/agentContext.js'
import { runWithCwdOverride } from 'src/utils/cwd.js'
@@ -160,7 +161,7 @@ export async function resumeAgentBackground({
mode: selectedAgent.permissionMode ?? 'acceptEdits',
}
const workerTools = isResumedFork
? toolUseContext.options.tools
? filterParentToolsForFork(toolUseContext.options.tools)
: assembleToolPool(workerPermissionContext, appState.mcp.tools)
const runAgentParams: Parameters<typeof runAgent>[0] = {