feat: initial public release (MAESTRO)

This commit is contained in:
oss-sync
2026-06-03 05:08:00 +00:00
commit f5c7666f6b
823 changed files with 184150 additions and 0 deletions
+672
View File
@@ -0,0 +1,672 @@
import { ToolDef } from '../../llm/openai-compat.js';
import { logger } from '../../logger.js';
import {
ToolContext,
ToolResult,
ToolsConfig,
ALL_TOOL_DEFS,
getToolDefs as getCoreToolDefs,
executeCoreTools,
} from './core.js';
import { saveRawData, logRawDownload, RAW_SAVE_TOOLS, RAW_LOG_ONLY_TOOLS } from './raw-save.js';
import { saveStructuredBlocks } from './structured-blocks.js';
import type { McpAggregator } from '../../mcp/aggregator.js';
import type { McpRuntimeConfig } from '../../mcp/config.js';
export type { ToolContext, ToolResult, ToolsConfig };
let _mcpAggregator: McpAggregator | null = null;
export function setMcpAggregator(agg: McpAggregator | null): void {
_mcpAggregator = agg;
}
type ExecuteCtxWithMcp = ToolContext & {
ownerId?: string | null;
jobId?: string | null;
mcpConfig?: McpRuntimeConfig;
mcpQuotaState?: { files: number; bytes: number };
};
// 外部モジュール(他チームが実装)のインターフェース型
interface ToolModule {
TOOL_DEFS: Record<string, ToolDef>;
executeTool(name: string, input: Record<string, unknown>, ctx: ToolContext): Promise<ToolResult | null>;
}
// 外部モジュールを動的にロード(存在しない場合は null)
async function tryLoadModule(specifier: string): Promise<ToolModule | null> {
try {
const mod = await import(specifier) as ToolModule;
return mod;
} catch {
return null;
}
}
// 外部モジュールのキャッシュ
let _webModule: ToolModule | null | undefined = undefined;
let _imageModule: ToolModule | null | undefined = undefined;
let _dataModule: ToolModule | null | undefined = undefined;
let _officeModule: ToolModule | null | undefined = undefined;
let _reviewModule: ToolModule | null | undefined = undefined;
let _xModule: ToolModule | null | undefined = undefined;
let _orchestrationModule: ToolModule | null | undefined = undefined;
let _browserModule: ToolModule | null | undefined = undefined;
let _mapsModule: ToolModule | null | undefined = undefined;
let _youtubeModule: ToolModule | null | undefined = undefined;
let _piecesModule: ToolModule | null | undefined = undefined;
let _amazonModule: ToolModule | null | undefined = undefined;
let _speechModule: ToolModule | null | undefined = undefined;
let _checklistModule: ToolModule | null | undefined = undefined;
let _knowledgeModule: ToolModule | null | undefined = undefined;
let _msLearnModule: ToolModule | null | undefined = undefined;
let _slideModule: ToolModule | null | undefined = undefined;
let _userFolderModule: ToolModule | null | undefined = undefined;
async function getWebModule(): Promise<ToolModule | null> {
if (_webModule === undefined) {
_webModule = await tryLoadModule('./web.js');
if (_webModule) logger.debug('[tools/index] web module loaded');
}
return _webModule;
}
async function getImageModule(): Promise<ToolModule | null> {
if (_imageModule === undefined) {
_imageModule = await tryLoadModule('./image.js');
if (_imageModule) logger.debug('[tools/index] image module loaded');
}
return _imageModule;
}
async function getDataModule(): Promise<ToolModule | null> {
if (_dataModule === undefined) {
_dataModule = await tryLoadModule('./data.js');
if (_dataModule) logger.debug('[tools/index] data module loaded');
}
return _dataModule;
}
async function getOfficeModule(): Promise<ToolModule | null> {
if (_officeModule === undefined) {
_officeModule = await tryLoadModule('./office.js');
if (_officeModule) logger.debug('[tools/index] office module loaded');
}
return _officeModule;
}
async function getReviewModule(): Promise<ToolModule | null> {
if (_reviewModule === undefined) {
_reviewModule = await tryLoadModule('./review.js');
if (_reviewModule) logger.debug('[tools/index] review module loaded');
}
return _reviewModule;
}
async function getXModule(): Promise<ToolModule | null> {
if (_xModule === undefined) {
_xModule = await tryLoadModule('./x.js');
if (_xModule) logger.debug('[tools/index] x module loaded');
}
return _xModule;
}
async function getOrchestrationModule(): Promise<ToolModule | null> {
if (_orchestrationModule === undefined) {
_orchestrationModule = await tryLoadModule('./orchestration.js');
if (_orchestrationModule) logger.debug('[tools/index] orchestration module loaded');
}
return _orchestrationModule;
}
async function getBrowserModule(): Promise<ToolModule | null> {
if (_browserModule === undefined) {
_browserModule = await tryLoadModule('./browser.js');
if (_browserModule) logger.debug('[tools/index] browser module loaded');
}
return _browserModule;
}
async function getMapsModule(): Promise<ToolModule | null> {
if (_mapsModule === undefined) {
_mapsModule = await tryLoadModule('./maps.js');
if (_mapsModule) logger.debug('[tools/index] maps module loaded');
}
return _mapsModule;
}
async function getYoutubeModule(): Promise<ToolModule | null> {
if (_youtubeModule === undefined) {
_youtubeModule = await tryLoadModule('./youtube.js');
if (_youtubeModule) logger.debug('[tools/index] youtube module loaded');
}
return _youtubeModule;
}
async function getPiecesModule(): Promise<ToolModule | null> {
if (_piecesModule === undefined) {
_piecesModule = await tryLoadModule('./pieces.js');
if (_piecesModule) logger.debug('[tools/index] pieces module loaded');
}
return _piecesModule;
}
async function getAmazonModule(): Promise<ToolModule | null> {
if (_amazonModule === undefined) {
_amazonModule = await tryLoadModule('./amazon.js');
if (_amazonModule) logger.debug('[tools/index] amazon module loaded');
}
return _amazonModule;
}
async function getSpeechModule(): Promise<ToolModule | null> {
if (_speechModule === undefined) {
_speechModule = await tryLoadModule('./speech.js');
if (_speechModule) logger.debug('[tools/index] speech module loaded');
}
return _speechModule;
}
async function getChecklistModule(): Promise<ToolModule | null> {
if (_checklistModule === undefined) {
_checklistModule = await tryLoadModule('./checklist.js');
if (_checklistModule) logger.debug('[tools/index] checklist module loaded');
}
return _checklistModule;
}
async function getKnowledgeModule(): Promise<ToolModule | null> {
if (_knowledgeModule === undefined) {
_knowledgeModule = await tryLoadModule('./knowledge.js');
if (_knowledgeModule) logger.debug('[tools/index] knowledge module loaded');
}
return _knowledgeModule;
}
async function getSlideModule(): Promise<ToolModule | null> {
if (_slideModule === undefined) {
_slideModule = await tryLoadModule('./slide.js');
if (_slideModule) logger.debug('[tools/index] slide module loaded');
}
return _slideModule;
}
async function getMsLearnModule(): Promise<ToolModule | null> {
if (_msLearnModule === undefined) {
_msLearnModule = await tryLoadModule('./ms-learn.js');
if (_msLearnModule) logger.debug('[tools/index] ms-learn module loaded');
}
return _msLearnModule;
}
let _docsModule: ToolModule | null | undefined;
async function getDocsModule(): Promise<ToolModule | null> {
if (_docsModule === undefined) {
_docsModule = await tryLoadModule('./docs.js');
if (_docsModule) logger.debug('[tools/index] docs module loaded');
}
return _docsModule;
}
let _missionModule: ToolModule | null | undefined;
async function getMissionModule(): Promise<ToolModule | null> {
if (_missionModule === undefined) {
_missionModule = await tryLoadModule('./mission.js');
if (_missionModule) logger.debug('[tools/index] mission module loaded');
}
return _missionModule;
}
async function getUserFolderModule(): Promise<ToolModule | null> {
if (_userFolderModule === undefined) {
_userFolderModule = await tryLoadModule('./user-folder.js');
if (_userFolderModule) logger.debug('[tools/index] user-folder module loaded');
}
return _userFolderModule;
}
let _brainstormModule: ToolModule | null | undefined;
async function getBrainstormModule(): Promise<ToolModule | null> {
if (_brainstormModule === undefined) {
_brainstormModule = await tryLoadModule('./brainstorm.js');
if (_brainstormModule) logger.debug('[tools/index] brainstorm module loaded');
}
return _brainstormModule;
}
let _appDocsModule: ToolModule | null | undefined;
async function getAppDocsModule(): Promise<ToolModule | null> {
if (_appDocsModule === undefined) {
_appDocsModule = await tryLoadModule('./app-docs.js');
if (_appDocsModule) logger.debug('[tools/index] app-docs module loaded');
}
return _appDocsModule;
}
let _sshModule: ToolModule | null | undefined;
async function getSshModule(): Promise<ToolModule | null> {
if (_sshModule === undefined) {
_sshModule = await tryLoadModule('./ssh.js');
if (_sshModule) logger.debug('[tools/index] ssh module loaded');
}
return _sshModule;
}
let _sshConsoleModule: ToolModule | null | undefined;
async function getSshConsoleModule(): Promise<ToolModule | null> {
if (_sshConsoleModule === undefined) {
_sshConsoleModule = await tryLoadModule('./ssh-console.js');
if (_sshConsoleModule) logger.debug('[tools/index] ssh-console module loaded');
}
return _sshConsoleModule;
}
let _notesModule: ToolModule | null | undefined;
async function getNotesModule(): Promise<ToolModule | null> {
if (_notesModule === undefined) {
_notesModule = await tryLoadModule('./notes.js');
if (_notesModule) logger.debug('[tools/index] notes module loaded');
}
return _notesModule;
}
let _dashboardModule: ToolModule | null | undefined = undefined;
async function getDashboardModule(): Promise<ToolModule | null> {
if (_dashboardModule === undefined) {
_dashboardModule = await tryLoadModule('./dashboard.js');
if (_dashboardModule) logger.debug('[tools/index] dashboard module loaded');
}
return _dashboardModule;
}
/**
* 全モジュールのツール定義を統合して返す。
* allowedTools と editAllowed に応じてフィルタリングする。
*/
export async function getToolDefs(
allowedTools: string[],
editAllowed: boolean,
options?: { vlmEnabled?: boolean; ownerId?: string | null; mcpDisabled?: boolean },
): Promise<ToolDef[]> {
// 全ツール定義を収集
const allDefs: Record<string, ToolDef> = { ...ALL_TOOL_DEFS };
const webMod = await getWebModule();
if (webMod) Object.assign(allDefs, webMod.TOOL_DEFS);
const imageMod = await getImageModule();
if (imageMod) Object.assign(allDefs, imageMod.TOOL_DEFS);
const dataMod = await getDataModule();
if (dataMod) Object.assign(allDefs, dataMod.TOOL_DEFS);
const officeMod = await getOfficeModule();
if (officeMod) Object.assign(allDefs, officeMod.TOOL_DEFS);
const reviewMod = await getReviewModule();
if (reviewMod) Object.assign(allDefs, reviewMod.TOOL_DEFS);
const xMod = await getXModule();
if (xMod) Object.assign(allDefs, xMod.TOOL_DEFS);
const orchestrationMod = await getOrchestrationModule();
if (orchestrationMod) Object.assign(allDefs, orchestrationMod.TOOL_DEFS);
const browserMod = await getBrowserModule();
if (browserMod) Object.assign(allDefs, browserMod.TOOL_DEFS);
const mapsMod = await getMapsModule();
if (mapsMod) Object.assign(allDefs, mapsMod.TOOL_DEFS);
const youtubeMod = await getYoutubeModule();
if (youtubeMod) Object.assign(allDefs, youtubeMod.TOOL_DEFS);
const piecesMod = await getPiecesModule();
if (piecesMod) Object.assign(allDefs, piecesMod.TOOL_DEFS);
const amazonMod = await getAmazonModule();
if (amazonMod) Object.assign(allDefs, amazonMod.TOOL_DEFS);
const speechMod = await getSpeechModule();
if (speechMod) Object.assign(allDefs, speechMod.TOOL_DEFS);
const checklistMod = await getChecklistModule();
if (checklistMod) Object.assign(allDefs, checklistMod.TOOL_DEFS);
const knowledgeMod = await getKnowledgeModule();
if (knowledgeMod) Object.assign(allDefs, knowledgeMod.TOOL_DEFS);
const msLearnMod = await getMsLearnModule();
if (msLearnMod) Object.assign(allDefs, msLearnMod.TOOL_DEFS);
const slideMod = await getSlideModule();
if (slideMod) Object.assign(allDefs, slideMod.TOOL_DEFS);
const docsMod = await getDocsModule();
if (docsMod) Object.assign(allDefs, docsMod.TOOL_DEFS);
const missionMod = await getMissionModule();
if (missionMod) Object.assign(allDefs, missionMod.TOOL_DEFS);
const userFolderMod = await getUserFolderModule();
if (userFolderMod) Object.assign(allDefs, userFolderMod.TOOL_DEFS);
const brainstormMod = await getBrainstormModule();
if (brainstormMod) Object.assign(allDefs, brainstormMod.TOOL_DEFS);
const appDocsMod = await getAppDocsModule();
if (appDocsMod) Object.assign(allDefs, appDocsMod.TOOL_DEFS);
const sshMod = await getSshModule();
if (sshMod) Object.assign(allDefs, sshMod.TOOL_DEFS);
const sshConsoleMod = await getSshConsoleModule();
if (sshConsoleMod) Object.assign(allDefs, sshConsoleMod.TOOL_DEFS);
const notesMod = await getNotesModule();
if (notesMod) Object.assign(allDefs, notesMod.TOOL_DEFS);
const dashboardMod = await getDashboardModule();
if (dashboardMod) Object.assign(allDefs, dashboardMod.TOOL_DEFS);
const { TOOL_DEFS: skillToolDefs } = await import('./skills.js');
Object.assign(allDefs, skillToolDefs);
// メタツール: piece の allowed_tools に書かれていなくても常に利用可能
// - ReadToolDoc: 全ツールのドキュメント参照
// - CreateChecklist / CheckItem / GetChecklist: 進捗管理 (複数ステップタスクで使用)
// - MissionUpdate: タスクの目標 / 進捗のピン止めメモを更新 (会話が長くなって
// 最初の要件を見失わないため。常時上書き可能、未指定フィールドは保持)
// - ListUserAssets / RunUserScript: ユーザーフォルダのスクリプト探索・実行
// - Brainstorm: 着手前 or 行き詰まり時の多アプローチ比較 (issue #247)
// - ReadAppDoc / ListAppDocs / GetMyOrchestratorState: Help アシスタント用 (#help piece)
// ただし他の piece からも参照できるようにメタ扱い
const META_TOOLS = ['ReadToolDoc', 'CreateChecklist', 'CheckItem', 'GetChecklist', 'MissionUpdate', 'ListUserAssets', 'RunUserScript', 'UpdateUserMemory', 'ReadUserMemory', 'ReadUserTemplate', 'RenderUserTemplate', 'WriteUserScript', 'WriteUserTemplate', 'Brainstorm', 'ReadAppDoc', 'ListAppDocs', 'GetMyOrchestratorState', 'ReadSkill', 'ListSkills', 'InstallSkill'];
const effectiveAllowed = [...allowedTools];
for (const meta of META_TOOLS) {
if (!effectiveAllowed.includes(meta) && meta in allDefs) {
effectiveAllowed.push(meta);
}
}
const staticDefs = effectiveAllowed
.filter((name) => {
if (!editAllowed && (name === 'Write' || name === 'Edit')) return false;
if (!options?.vlmEnabled && name === 'ReadImage') return false;
return name in allDefs;
})
.map((name) => allDefs[name]!);
const mcpDefs =
_mcpAggregator && options?.ownerId && !options?.mcpDisabled
? await _mcpAggregator.getToolDefs(options.ownerId, allowedTools)
: [];
return [...staticDefs, ...mcpDefs];
}
/**
* ツールを実行する内部ルーター。
* core → web → image → data の順で各モジュールに委譲し、
* 最初に null でない結果を返したモジュールの結果を使う。
*/
async function executeToolInner(
name: string,
input: Record<string, unknown>,
ctx: ToolContext,
): Promise<ToolResult> {
if (name.startsWith('mcp__')) {
if (ctx.mcpDisabled) {
return { output: 'MCP tools are disabled for this task (options.mcpDisabled)', isError: true };
}
if (!_mcpAggregator) {
return { output: 'MCP サブシステムが初期化されていません', isError: true };
}
const ctxWithMcp = ctx as ExecuteCtxWithMcp;
if (!ctxWithMcp.mcpQuotaState) {
ctxWithMcp.mcpQuotaState = { files: 0, bytes: 0 };
}
if (!ctxWithMcp.mcpConfig) {
return { output: 'MCP 設定が ToolContext に伝播していません', isError: true };
}
const result = await _mcpAggregator.executeTool(name, input, {
workspacePath: ctx.workspacePath,
ownerId: ctxWithMcp.ownerId ?? '',
jobId: ctxWithMcp.jobId ?? '',
config: ctxWithMcp.mcpConfig,
quotaState: ctxWithMcp.mcpQuotaState,
});
if (result === null) {
return { output: `MCP ツール dispatch が ${name} に対して null を返しました`, isError: true };
}
return { output: result.output, isError: result.isError };
}
// core ツール
const coreResult = await executeCoreTools(name, input, ctx);
if (coreResult !== null) return coreResult;
// web ツール
const webMod = await getWebModule();
if (webMod) {
const webResult = await webMod.executeTool(name, input, ctx);
if (webResult !== null) return webResult;
}
// image ツール
const imageMod = await getImageModule();
if (imageMod) {
const imageResult = await imageMod.executeTool(name, input, ctx);
if (imageResult !== null) return imageResult;
}
// data ツール
const dataMod = await getDataModule();
if (dataMod) {
const dataResult = await dataMod.executeTool(name, input, ctx);
if (dataResult !== null) return dataResult;
}
// office ツール
const officeMod = await getOfficeModule();
if (officeMod) {
const officeResult = await officeMod.executeTool(name, input, ctx);
if (officeResult !== null) return officeResult;
}
// review ツール
const reviewMod = await getReviewModule();
if (reviewMod) {
const reviewResult = await reviewMod.executeTool(name, input, ctx);
if (reviewResult !== null) return reviewResult;
}
// x tools
const xMod = await getXModule();
if (xMod) {
const xResult = await xMod.executeTool(name, input, ctx);
if (xResult !== null) return xResult;
}
// orchestration ツール
const orchestrationMod = await getOrchestrationModule();
if (orchestrationMod) {
const orchestrationResult = await orchestrationMod.executeTool(name, input, ctx);
if (orchestrationResult !== null) return orchestrationResult;
}
// browser ツール
const browserMod = await getBrowserModule();
if (browserMod) {
const browserResult = await browserMod.executeTool(name, input, ctx);
if (browserResult !== null) return browserResult;
}
// maps ツール
const mapsMod = await getMapsModule();
if (mapsMod) {
const mapsResult = await mapsMod.executeTool(name, input, ctx);
if (mapsResult !== null) return mapsResult;
}
// youtube ツール
const youtubeMod = await getYoutubeModule();
if (youtubeMod) {
const youtubeResult = await youtubeMod.executeTool(name, input, ctx);
if (youtubeResult !== null) return youtubeResult;
}
// pieces ツール
const piecesMod = await getPiecesModule();
if (piecesMod) {
const piecesResult = await piecesMod.executeTool(name, input, ctx);
if (piecesResult !== null) return piecesResult;
}
// amazon ツール
const amazonMod = await getAmazonModule();
if (amazonMod) {
const amazonResult = await amazonMod.executeTool(name, input, ctx);
if (amazonResult !== null) return amazonResult;
}
// speech ツール
const speechMod = await getSpeechModule();
if (speechMod) {
const speechResult = await speechMod.executeTool(name, input, ctx);
if (speechResult !== null) return speechResult;
}
// checklist ツール
const checklistMod = await getChecklistModule();
if (checklistMod) {
const checklistResult = await checklistMod.executeTool(name, input, ctx);
if (checklistResult !== null) return checklistResult;
}
// knowledge ツール
const knowledgeMod = await getKnowledgeModule();
if (knowledgeMod) {
const knowledgeResult = await knowledgeMod.executeTool(name, input, ctx);
if (knowledgeResult !== null) return knowledgeResult;
}
// ms-learn ツール
const msLearnMod = await getMsLearnModule();
if (msLearnMod) {
const msLearnResult = await msLearnMod.executeTool(name, input, ctx);
if (msLearnResult !== null) return msLearnResult;
}
// slide ツール (SetTheme / AddSlide / BuildPptx / ResetSlides)
const slideMod = await getSlideModule();
if (slideMod) {
const slideResult = await slideMod.executeTool(name, input, ctx);
if (slideResult !== null) return slideResult;
}
// docs ツール (ReadToolDoc)
const docsMod = await getDocsModule();
if (docsMod) {
const docsResult = await docsMod.executeTool(name, input, ctx);
if (docsResult !== null) return docsResult;
}
// mission ツール (MissionUpdate)
const missionMod = await getMissionModule();
if (missionMod) {
const missionResult = await missionMod.executeTool(name, input, ctx);
if (missionResult !== null) return missionResult;
}
// user-folder ツール (ListUserAssets / RunUserScript)
const userFolderMod = await getUserFolderModule();
if (userFolderMod) {
const userFolderResult = await userFolderMod.executeTool(name, input, ctx);
if (userFolderResult !== null) return userFolderResult;
}
// brainstorm ツール (Brainstorm)
const brainstormMod = await getBrainstormModule();
if (brainstormMod) {
const brainstormResult = await brainstormMod.executeTool(name, input, ctx);
if (brainstormResult !== null) return brainstormResult;
}
// app-docs ツール (ReadAppDoc / ListAppDocs / GetMyOrchestratorState)
const appDocsMod = await getAppDocsModule();
if (appDocsMod) {
const appDocsResult = await appDocsMod.executeTool(name, input, ctx);
if (appDocsResult !== null) return appDocsResult;
}
// ssh ツール (SshExec / SshUpload / SshDownload)
const sshMod = await getSshModule();
if (sshMod) {
const sshResult = await sshMod.executeTool(name, input, ctx);
if (sshResult !== null) return sshResult;
}
// ssh-console ツール (SshConsoleEnsure / Send / Snapshot)
const sshConsoleMod = await getSshConsoleModule();
if (sshConsoleMod) {
const sshConsoleResult = await sshConsoleMod.executeTool(name, input, ctx);
if (sshConsoleResult !== null) return sshConsoleResult;
}
// notes ツール (SearchNotes / ReadNote / WriteNote)
const notesMod = await getNotesModule();
if (notesMod) {
const notesResult = await notesMod.executeTool(name, input, ctx);
if (notesResult !== null) return notesResult;
}
// dashboard ツール (UpdateDashboardWidget)
const dashboardMod = await getDashboardModule();
if (dashboardMod) {
const dashboardResult = await dashboardMod.executeTool(name, input, ctx);
if (dashboardResult !== null) return dashboardResult;
}
// skills ツール (ReadSkill)
const { executeSkillTool } = await import('./skills.js');
const skillResult = executeSkillTool(name, input, ctx);
if (skillResult !== null) return skillResult;
return { output: `Unknown tool: ${name}`, isError: true };
}
/**
* ツールを実行するルーター(生データ自動保存ラッパー付き)。
* executeToolInner に委譲した後、成功した対象ツールの結果を logs/raw/ に保存する。
*/
export async function executeTool(
name: string,
input: Record<string, unknown>,
ctx: ToolContext,
): Promise<ToolResult> {
logger.debug(`[tools/index] executing ${name}`);
const result = await executeToolInner(name, input, ctx);
// raw保存ラッパー: 成功した対象ツールの結果を logs/raw/ に保存
if (ctx.workspacePath && !result.isError) {
if (RAW_SAVE_TOOLS.has(name)) {
saveRawData(ctx.workspacePath, name, result.output);
} else if (RAW_LOG_ONLY_TOOLS.has(name)) {
const pathMatch = result.output.match(/-> (.+?) \(/);
if (pathMatch?.[1]) {
logRawDownload(ctx.workspacePath, name, pathMatch[1], result.output.length);
}
}
// 構造化データ保存: structuredBlocks があれば logs/structured/ に保存
if (result.structuredBlocks?.length) {
saveStructuredBlocks(ctx.workspacePath, result.structuredBlocks);
}
}
return result;
}
// 同期版 getToolDefs のラッパー(後方互換のため、コアツールのみを返す同期版)
export { getCoreToolDefs as getCoreToolDefs };