@botbotgo/runtime 1.0.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/.github/workflows/ci.yml +46 -0
- package/.github/workflows/release.yml +79 -0
- package/README.md +71 -0
- package/config/examples/memory.yaml +39 -0
- package/config/examples/model.yaml +21 -0
- package/config/examples/runtime.yaml +44 -0
- package/config/examples/skills.yaml +8 -0
- package/config/examples/tool.yaml +16 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +41 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +140 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/resolveRuntimeConfig.d.ts +83 -0
- package/dist/config/resolveRuntimeConfig.d.ts.map +1 -0
- package/dist/config/resolveRuntimeConfig.js +85 -0
- package/dist/config/resolveRuntimeConfig.js.map +1 -0
- package/dist/config/resources.d.ts +112 -0
- package/dist/config/resources.d.ts.map +1 -0
- package/dist/config/resources.js +20 -0
- package/dist/config/resources.js.map +1 -0
- package/dist/config/runtimeConfigLoader.d.ts +28 -0
- package/dist/config/runtimeConfigLoader.d.ts.map +1 -0
- package/dist/config/runtimeConfigLoader.js +38 -0
- package/dist/config/runtimeConfigLoader.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/agentMiddleware.d.ts +3 -0
- package/dist/runtime/agentMiddleware.d.ts.map +1 -0
- package/dist/runtime/agentMiddleware.js +2 -0
- package/dist/runtime/agentMiddleware.js.map +1 -0
- package/dist/runtime/bootstrap/runtimeFactory.d.ts +7 -0
- package/dist/runtime/bootstrap/runtimeFactory.d.ts.map +1 -0
- package/dist/runtime/bootstrap/runtimeFactory.js +139 -0
- package/dist/runtime/bootstrap/runtimeFactory.js.map +1 -0
- package/dist/runtime/bootstrap/runtimeModuleInitializer.d.ts +5 -0
- package/dist/runtime/bootstrap/runtimeModuleInitializer.d.ts.map +1 -0
- package/dist/runtime/bootstrap/runtimeModuleInitializer.js +32 -0
- package/dist/runtime/bootstrap/runtimeModuleInitializer.js.map +1 -0
- package/dist/runtime/context/agentContext.d.ts +5 -0
- package/dist/runtime/context/agentContext.d.ts.map +1 -0
- package/dist/runtime/context/agentContext.js +19 -0
- package/dist/runtime/context/agentContext.js.map +1 -0
- package/dist/runtime/context/globalContext.d.ts +19 -0
- package/dist/runtime/context/globalContext.d.ts.map +1 -0
- package/dist/runtime/context/globalContext.js +47 -0
- package/dist/runtime/context/globalContext.js.map +1 -0
- package/dist/runtime/events/eventPublisher.d.ts +15 -0
- package/dist/runtime/events/eventPublisher.d.ts.map +1 -0
- package/dist/runtime/events/eventPublisher.js +23 -0
- package/dist/runtime/events/eventPublisher.js.map +1 -0
- package/dist/runtime/execution/agentRunExecutor.d.ts +38 -0
- package/dist/runtime/execution/agentRunExecutor.d.ts.map +1 -0
- package/dist/runtime/execution/agentRunExecutor.js +378 -0
- package/dist/runtime/execution/agentRunExecutor.js.map +1 -0
- package/dist/runtime/execution/agentRunExecutor.types.d.ts +37 -0
- package/dist/runtime/execution/agentRunExecutor.types.d.ts.map +1 -0
- package/dist/runtime/execution/agentRunExecutor.types.js +2 -0
- package/dist/runtime/execution/agentRunExecutor.types.js.map +1 -0
- package/dist/runtime/execution/agentRunProgress.d.ts +8 -0
- package/dist/runtime/execution/agentRunProgress.d.ts.map +1 -0
- package/dist/runtime/execution/agentRunProgress.js +46 -0
- package/dist/runtime/execution/agentRunProgress.js.map +1 -0
- package/dist/runtime/execution/agentRunState.d.ts +44 -0
- package/dist/runtime/execution/agentRunState.d.ts.map +1 -0
- package/dist/runtime/execution/agentRunState.js +17 -0
- package/dist/runtime/execution/agentRunState.js.map +1 -0
- package/dist/runtime/execution/policies/runCompletionPolicy.d.ts +5 -0
- package/dist/runtime/execution/policies/runCompletionPolicy.d.ts.map +1 -0
- package/dist/runtime/execution/policies/runCompletionPolicy.js +30 -0
- package/dist/runtime/execution/policies/runCompletionPolicy.js.map +1 -0
- package/dist/runtime/execution/policies/runErrorPolicy.d.ts +7 -0
- package/dist/runtime/execution/policies/runErrorPolicy.d.ts.map +1 -0
- package/dist/runtime/execution/policies/runErrorPolicy.js +40 -0
- package/dist/runtime/execution/policies/runErrorPolicy.js.map +1 -0
- package/dist/runtime/execution/policies/runPromptBuilder.d.ts +10 -0
- package/dist/runtime/execution/policies/runPromptBuilder.d.ts.map +1 -0
- package/dist/runtime/execution/policies/runPromptBuilder.js +34 -0
- package/dist/runtime/execution/policies/runPromptBuilder.js.map +1 -0
- package/dist/runtime/execution/policies/runSummaryFormatter.d.ts +6 -0
- package/dist/runtime/execution/policies/runSummaryFormatter.d.ts.map +1 -0
- package/dist/runtime/execution/policies/runSummaryFormatter.js +47 -0
- package/dist/runtime/execution/policies/runSummaryFormatter.js.map +1 -0
- package/dist/runtime/execution/policies/runtimeDelay.d.ts +4 -0
- package/dist/runtime/execution/policies/runtimeDelay.d.ts.map +1 -0
- package/dist/runtime/execution/policies/runtimeDelay.js +6 -0
- package/dist/runtime/execution/policies/runtimeDelay.js.map +1 -0
- package/dist/runtime/execution/policies/toolConfigReader.d.ts +8 -0
- package/dist/runtime/execution/policies/toolConfigReader.d.ts.map +1 -0
- package/dist/runtime/execution/policies/toolConfigReader.js +91 -0
- package/dist/runtime/execution/policies/toolConfigReader.js.map +1 -0
- package/dist/runtime/human-loop/consoleHumanLoop.d.ts +21 -0
- package/dist/runtime/human-loop/consoleHumanLoop.d.ts.map +1 -0
- package/dist/runtime/human-loop/consoleHumanLoop.js +102 -0
- package/dist/runtime/human-loop/consoleHumanLoop.js.map +1 -0
- package/dist/runtime/human-loop/humanLoop.d.ts +44 -0
- package/dist/runtime/human-loop/humanLoop.d.ts.map +1 -0
- package/dist/runtime/human-loop/humanLoop.js +36 -0
- package/dist/runtime/human-loop/humanLoop.js.map +1 -0
- package/dist/runtime/index.d.ts +10 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +6 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/middleware/agentToolMiddleware.d.ts +30 -0
- package/dist/runtime/middleware/agentToolMiddleware.d.ts.map +1 -0
- package/dist/runtime/middleware/agentToolMiddleware.js +259 -0
- package/dist/runtime/middleware/agentToolMiddleware.js.map +1 -0
- package/dist/runtime/middleware/commandPolicy.d.ts +8 -0
- package/dist/runtime/middleware/commandPolicy.d.ts.map +1 -0
- package/dist/runtime/middleware/commandPolicy.js +55 -0
- package/dist/runtime/middleware/commandPolicy.js.map +1 -0
- package/dist/runtime/middleware/frameworkPrompt.d.ts +4 -0
- package/dist/runtime/middleware/frameworkPrompt.d.ts.map +1 -0
- package/dist/runtime/middleware/frameworkPrompt.js +44 -0
- package/dist/runtime/middleware/frameworkPrompt.js.map +1 -0
- package/dist/runtime/middleware/index.d.ts +4 -0
- package/dist/runtime/middleware/index.d.ts.map +1 -0
- package/dist/runtime/middleware/index.js +3 -0
- package/dist/runtime/middleware/index.js.map +1 -0
- package/dist/runtime/middleware/skillPathMap.d.ts +3 -0
- package/dist/runtime/middleware/skillPathMap.d.ts.map +1 -0
- package/dist/runtime/middleware/skillPathMap.js +44 -0
- package/dist/runtime/middleware/skillPathMap.js.map +1 -0
- package/dist/runtime/middleware/toolArgsNormalizer.d.ts +21 -0
- package/dist/runtime/middleware/toolArgsNormalizer.d.ts.map +1 -0
- package/dist/runtime/middleware/toolArgsNormalizer.js +227 -0
- package/dist/runtime/middleware/toolArgsNormalizer.js.map +1 -0
- package/dist/runtime/middleware/toolCallGuard.d.ts +8 -0
- package/dist/runtime/middleware/toolCallGuard.d.ts.map +1 -0
- package/dist/runtime/middleware/toolCallGuard.js +60 -0
- package/dist/runtime/middleware/toolCallGuard.js.map +1 -0
- package/dist/runtime/middleware/toolFsHandlers.d.ts +12 -0
- package/dist/runtime/middleware/toolFsHandlers.d.ts.map +1 -0
- package/dist/runtime/middleware/toolFsHandlers.js +79 -0
- package/dist/runtime/middleware/toolFsHandlers.js.map +1 -0
- package/dist/runtime/middleware/toolRequestParser.d.ts +14 -0
- package/dist/runtime/middleware/toolRequestParser.d.ts.map +1 -0
- package/dist/runtime/middleware/toolRequestParser.js +72 -0
- package/dist/runtime/middleware/toolRequestParser.js.map +1 -0
- package/dist/runtime/middleware/types.d.ts +87 -0
- package/dist/runtime/middleware/types.d.ts.map +1 -0
- package/dist/runtime/middleware/types.js +3 -0
- package/dist/runtime/middleware/types.js.map +1 -0
- package/dist/runtime/providers/backendResolver.d.ts +26 -0
- package/dist/runtime/providers/backendResolver.d.ts.map +1 -0
- package/dist/runtime/providers/backendResolver.js +73 -0
- package/dist/runtime/providers/backendResolver.js.map +1 -0
- package/dist/runtime/providers/index.d.ts +14 -0
- package/dist/runtime/providers/index.d.ts.map +1 -0
- package/dist/runtime/providers/index.js +10 -0
- package/dist/runtime/providers/index.js.map +1 -0
- package/dist/runtime/providers/langchainRuntime.d.ts +11 -0
- package/dist/runtime/providers/langchainRuntime.d.ts.map +1 -0
- package/dist/runtime/providers/langchainRuntime.js +45 -0
- package/dist/runtime/providers/langchainRuntime.js.map +1 -0
- package/dist/runtime/providers/localShellBackend.d.ts +10 -0
- package/dist/runtime/providers/localShellBackend.d.ts.map +1 -0
- package/dist/runtime/providers/localShellBackend.js +23 -0
- package/dist/runtime/providers/localShellBackend.js.map +1 -0
- package/dist/runtime/providers/openaiDeepAgent.d.ts +9 -0
- package/dist/runtime/providers/openaiDeepAgent.d.ts.map +1 -0
- package/dist/runtime/providers/openaiDeepAgent.js +7 -0
- package/dist/runtime/providers/openaiDeepAgent.js.map +1 -0
- package/dist/runtime/providers/openaiLocalAgent.d.ts +17 -0
- package/dist/runtime/providers/openaiLocalAgent.d.ts.map +1 -0
- package/dist/runtime/providers/openaiLocalAgent.js +26 -0
- package/dist/runtime/providers/openaiLocalAgent.js.map +1 -0
- package/dist/runtime/providers/openaiLocalAgentFactory.d.ts +36 -0
- package/dist/runtime/providers/openaiLocalAgentFactory.d.ts.map +1 -0
- package/dist/runtime/providers/openaiLocalAgentFactory.js +139 -0
- package/dist/runtime/providers/openaiLocalAgentFactory.js.map +1 -0
- package/dist/runtime/providers/shared/agentProbe.d.ts +4 -0
- package/dist/runtime/providers/shared/agentProbe.d.ts.map +1 -0
- package/dist/runtime/providers/shared/agentProbe.js +17 -0
- package/dist/runtime/providers/shared/agentProbe.js.map +1 -0
- package/dist/runtime/providers/shared/moduleFallback.d.ts +2 -0
- package/dist/runtime/providers/shared/moduleFallback.d.ts.map +1 -0
- package/dist/runtime/providers/shared/moduleFallback.js +34 -0
- package/dist/runtime/providers/shared/moduleFallback.js.map +1 -0
- package/dist/runtime/providers/shared/resourceCloser.d.ts +6 -0
- package/dist/runtime/providers/shared/resourceCloser.d.ts.map +1 -0
- package/dist/runtime/providers/shared/resourceCloser.js +30 -0
- package/dist/runtime/providers/shared/resourceCloser.js.map +1 -0
- package/dist/runtime/runtimeService.d.ts +109 -0
- package/dist/runtime/runtimeService.d.ts.map +1 -0
- package/dist/runtime/runtimeService.js +90 -0
- package/dist/runtime/runtimeService.js.map +1 -0
- package/dist/runtime/stream/agentMessages.d.ts +40 -0
- package/dist/runtime/stream/agentMessages.d.ts.map +1 -0
- package/dist/runtime/stream/agentMessages.js +44 -0
- package/dist/runtime/stream/agentMessages.js.map +1 -0
- package/dist/runtime/stream/agentStream.d.ts +31 -0
- package/dist/runtime/stream/agentStream.d.ts.map +1 -0
- package/dist/runtime/stream/agentStream.js +106 -0
- package/dist/runtime/stream/agentStream.js.map +1 -0
- package/dist/runtime/stream/messageSummary.d.ts +20 -0
- package/dist/runtime/stream/messageSummary.d.ts.map +1 -0
- package/dist/runtime/stream/messageSummary.js +150 -0
- package/dist/runtime/stream/messageSummary.js.map +1 -0
- package/dist/runtime/stream/runArtifacts.d.ts +9 -0
- package/dist/runtime/stream/runArtifacts.d.ts.map +1 -0
- package/dist/runtime/stream/runArtifacts.js +48 -0
- package/dist/runtime/stream/runArtifacts.js.map +1 -0
- package/dist/state/index.d.ts +7 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +4 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/runState.d.ts +36 -0
- package/dist/state/runState.d.ts.map +1 -0
- package/dist/state/runState.js +51 -0
- package/dist/state/runState.js.map +1 -0
- package/dist/state/todos.d.ts +19 -0
- package/dist/state/todos.d.ts.map +1 -0
- package/dist/state/todos.js +45 -0
- package/dist/state/todos.js.map +1 -0
- package/dist/state/workspaceRunSession.d.ts +32 -0
- package/dist/state/workspaceRunSession.d.ts.map +1 -0
- package/dist/state/workspaceRunSession.js +67 -0
- package/dist/state/workspaceRunSession.js.map +1 -0
- package/dist/state/workspaceState.d.ts +94 -0
- package/dist/state/workspaceState.d.ts.map +1 -0
- package/dist/state/workspaceState.js +119 -0
- package/dist/state/workspaceState.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/example/.tsbuildinfo +1 -0
- package/example/build/.tsbuildinfo +1 -0
- package/example/config/memory.yaml +35 -0
- package/example/config/model.yaml +14 -0
- package/example/config/runtime.yaml +46 -0
- package/example/config/tool.yaml +24 -0
- package/example/index.ts +159 -0
- package/example/package-lock.json +1396 -0
- package/example/package.json +26 -0
- package/example/serve-output.mjs +52 -0
- package/example/tsconfig.json +19 -0
- package/package.json +73 -0
- package/scripts/resolve-deps.js +40 -0
- package/src/config/index.ts +29 -0
- package/src/config/loader.ts +174 -0
- package/src/config/resolveRuntimeConfig.ts +160 -0
- package/src/config/resources.ts +152 -0
- package/src/config/runtimeConfigLoader.ts +68 -0
- package/src/external-modules.d.ts +20 -0
- package/src/index.ts +36 -0
- package/src/runtime/agentMiddleware.ts +6 -0
- package/src/runtime/bootstrap/runtimeFactory.ts +212 -0
- package/src/runtime/bootstrap/runtimeModuleInitializer.ts +34 -0
- package/src/runtime/context/agentContext.ts +24 -0
- package/src/runtime/context/globalContext.ts +70 -0
- package/src/runtime/events/eventPublisher.ts +45 -0
- package/src/runtime/execution/agentRunExecutor.ts +499 -0
- package/src/runtime/execution/agentRunExecutor.types.ts +39 -0
- package/src/runtime/execution/agentRunProgress.ts +67 -0
- package/src/runtime/execution/agentRunState.ts +66 -0
- package/src/runtime/execution/policies/runCompletionPolicy.ts +30 -0
- package/src/runtime/execution/policies/runErrorPolicy.ts +50 -0
- package/src/runtime/execution/policies/runPromptBuilder.ts +39 -0
- package/src/runtime/execution/policies/runSummaryFormatter.ts +57 -0
- package/src/runtime/execution/policies/runtimeDelay.ts +5 -0
- package/src/runtime/execution/policies/toolConfigReader.ts +107 -0
- package/src/runtime/human-loop/consoleHumanLoop.ts +113 -0
- package/src/runtime/human-loop/humanLoop.ts +90 -0
- package/src/runtime/index.ts +36 -0
- package/src/runtime/middleware/agentToolMiddleware.ts +329 -0
- package/src/runtime/middleware/commandPolicy.ts +65 -0
- package/src/runtime/middleware/frameworkPrompt.ts +51 -0
- package/src/runtime/middleware/index.ts +7 -0
- package/src/runtime/middleware/skillPathMap.ts +49 -0
- package/src/runtime/middleware/toolArgsNormalizer.ts +277 -0
- package/src/runtime/middleware/toolCallGuard.ts +73 -0
- package/src/runtime/middleware/toolFsHandlers.ts +94 -0
- package/src/runtime/middleware/toolRequestParser.ts +84 -0
- package/src/runtime/middleware/types.ts +91 -0
- package/src/runtime/providers/backendResolver.ts +127 -0
- package/src/runtime/providers/index.ts +19 -0
- package/src/runtime/providers/langchainRuntime.ts +67 -0
- package/src/runtime/providers/localShellBackend.ts +54 -0
- package/src/runtime/providers/openaiDeepAgent.ts +17 -0
- package/src/runtime/providers/openaiLocalAgent.ts +35 -0
- package/src/runtime/providers/openaiLocalAgentFactory.ts +185 -0
- package/src/runtime/providers/shared/agentProbe.ts +24 -0
- package/src/runtime/providers/shared/moduleFallback.ts +41 -0
- package/src/runtime/providers/shared/resourceCloser.ts +30 -0
- package/src/runtime/runtimeService.ts +205 -0
- package/src/runtime/stream/agentMessages.ts +72 -0
- package/src/runtime/stream/agentStream.ts +168 -0
- package/src/runtime/stream/messageSummary.ts +164 -0
- package/src/runtime/stream/runArtifacts.ts +72 -0
- package/src/state/index.ts +19 -0
- package/src/state/runState.ts +84 -0
- package/src/state/todos.ts +59 -0
- package/src/state/workspaceRunSession.ts +96 -0
- package/src/state/workspaceState.ts +199 -0
- package/src/types/easynet-agent-common.d.ts +62 -0
- package/src/types.ts +1 -0
- package/test/integration/runtime-live.integration.test.ts +30 -0
- package/test/unit/config/loader.test.ts +429 -0
- package/test/unit/runtime/agentContext.test.ts +23 -0
- package/test/unit/runtime/agentRuntime.test.ts +99 -0
- package/test/unit/runtime/consoleHumanLoop.test.ts +52 -0
- package/test/unit/runtime/events.test.ts +11 -0
- package/test/unit/runtime/globalContext.test.ts +34 -0
- package/test/unit/runtime/humanLoop.test.ts +104 -0
- package/test/unit/runtime/toolArgsNormalizer.test.ts +136 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import type { AgentRuntimeEventPublisher } from "../events/eventPublisher.js";
|
|
2
|
+
import type { HumanLoopDecision, HumanLoopRequest } from "../human-loop/humanLoop.js";
|
|
3
|
+
import { FrameworkPrompt } from "./frameworkPrompt.js";
|
|
4
|
+
import { ToolArgsNormalizer } from "./toolArgsNormalizer.js";
|
|
5
|
+
import { ToolCallGuard } from "./toolCallGuard.js";
|
|
6
|
+
import { ToolFsHandlers } from "./toolFsHandlers.js";
|
|
7
|
+
import { ToolRequestParser } from "./toolRequestParser.js";
|
|
8
|
+
import { buildSkillPathMap, expandSkillRoots } from "./skillPathMap.js";
|
|
9
|
+
import type {
|
|
10
|
+
AgentMiddlewareLike,
|
|
11
|
+
CreateAgentToolMiddlewareOptions,
|
|
12
|
+
ToolArgs,
|
|
13
|
+
} from "./types.js";
|
|
14
|
+
|
|
15
|
+
export class AgentToolMiddlewareFactory {
|
|
16
|
+
private readonly rootDir: string;
|
|
17
|
+
private readonly skillRoots: string[];
|
|
18
|
+
private readonly skillPathMap: Record<string, string>;
|
|
19
|
+
private readonly createMiddleware: CreateAgentToolMiddlewareOptions["createMiddleware"];
|
|
20
|
+
private readonly ToolMessage: CreateAgentToolMiddlewareOptions["ToolMessage"];
|
|
21
|
+
private readonly events?: AgentRuntimeEventPublisher;
|
|
22
|
+
private readonly executeTimeoutMs: number;
|
|
23
|
+
private readonly blockTaskTool: boolean;
|
|
24
|
+
private readonly allowedToolNames?: string[];
|
|
25
|
+
private readonly logger: (message: string) => void;
|
|
26
|
+
private readonly persistTodos?: (todos: unknown[]) => Promise<void> | void;
|
|
27
|
+
private readonly humanLoop?: CreateAgentToolMiddlewareOptions["humanLoop"];
|
|
28
|
+
private readonly humanLoopToolPolicy?: CreateAgentToolMiddlewareOptions["humanLoopToolPolicy"];
|
|
29
|
+
private readonly beforeToolCall?: CreateAgentToolMiddlewareOptions["beforeToolCall"];
|
|
30
|
+
private readonly afterToolCall?: CreateAgentToolMiddlewareOptions["afterToolCall"];
|
|
31
|
+
|
|
32
|
+
public constructor(options: CreateAgentToolMiddlewareOptions) {
|
|
33
|
+
this.rootDir = options.rootDir;
|
|
34
|
+
this.skillRoots = expandSkillRoots(options.skillRoots ?? []);
|
|
35
|
+
this.skillPathMap = options.skillPathMap ?? buildSkillPathMap(this.skillRoots);
|
|
36
|
+
this.createMiddleware = options.createMiddleware;
|
|
37
|
+
this.ToolMessage = options.ToolMessage;
|
|
38
|
+
this.events = options.events as AgentRuntimeEventPublisher | undefined;
|
|
39
|
+
this.executeTimeoutMs = options.executeTimeoutMs ?? 60_000;
|
|
40
|
+
this.blockTaskTool = options.blockTaskTool ?? false;
|
|
41
|
+
this.allowedToolNames = options.allowedToolNames?.filter((name) => typeof name === "string" && name.trim().length > 0);
|
|
42
|
+
this.logger = options.logger ?? (() => {});
|
|
43
|
+
this.persistTodos = options.persistTodos;
|
|
44
|
+
this.humanLoop = options.humanLoop;
|
|
45
|
+
this.humanLoopToolPolicy = options.humanLoopToolPolicy;
|
|
46
|
+
this.beforeToolCall = options.beforeToolCall;
|
|
47
|
+
this.afterToolCall = options.afterToolCall;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public create(): AgentMiddlewareLike {
|
|
51
|
+
return this.createMiddleware({
|
|
52
|
+
name: "AgentToolMiddleware",
|
|
53
|
+
wrapModelCall: async (request, handler) => this.wrapModelCall(request, handler),
|
|
54
|
+
wrapToolCall: async (request, handler) => this.wrapToolCall(request, handler),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private async wrapModelCall(request: unknown, handler: (request: unknown) => unknown): Promise<unknown> {
|
|
59
|
+
const modelRequest = ToolRequestParser.toModelRequest(request);
|
|
60
|
+
return handler({
|
|
61
|
+
...modelRequest,
|
|
62
|
+
systemPrompt: FrameworkPrompt.build(
|
|
63
|
+
modelRequest.systemPrompt,
|
|
64
|
+
this.blockTaskTool,
|
|
65
|
+
this.allowedToolNames,
|
|
66
|
+
this.skillPathMap,
|
|
67
|
+
),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private async wrapToolCall(request: unknown, handler: (request: unknown) => unknown): Promise<unknown> {
|
|
72
|
+
const toolRequest = ToolRequestParser.toMiddlewareRequest(request);
|
|
73
|
+
const name = toolRequest.toolCall.name;
|
|
74
|
+
const toolCallId = toolRequest.toolCall.id;
|
|
75
|
+
|
|
76
|
+
if (this.blockTaskTool && name === "task") {
|
|
77
|
+
this.events?.emit({
|
|
78
|
+
name: "agent.runtime2.tool.call.blocked",
|
|
79
|
+
from: "agent-runtime2.middleware",
|
|
80
|
+
to: name,
|
|
81
|
+
payload: { toolCallId, reason: "task_tool_disabled" },
|
|
82
|
+
});
|
|
83
|
+
return new this.ToolMessage({
|
|
84
|
+
content: "Error: Do not use the task tool. Use execute(), write_file(), and read_file() directly.",
|
|
85
|
+
tool_call_id: toolCallId ?? "blocked",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const args = ToolRequestParser.toToolArgs(toolRequest.toolCall.args);
|
|
90
|
+
const blocked = ToolCallGuard.maybeBlock(
|
|
91
|
+
name,
|
|
92
|
+
args,
|
|
93
|
+
toolCallId,
|
|
94
|
+
this.ToolMessage,
|
|
95
|
+
this.rootDir,
|
|
96
|
+
this.allowedToolNames,
|
|
97
|
+
Object.values(this.skillPathMap),
|
|
98
|
+
);
|
|
99
|
+
if (blocked) {
|
|
100
|
+
this.events?.emit({
|
|
101
|
+
name: "agent.runtime2.tool.call.blocked",
|
|
102
|
+
from: "agent-runtime2.middleware",
|
|
103
|
+
to: name,
|
|
104
|
+
payload: { toolCallId, args, reason: "policy_violation" },
|
|
105
|
+
});
|
|
106
|
+
return blocked;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let nextArgs = ToolArgsNormalizer.normalizeToolArgs(
|
|
110
|
+
this.rootDir,
|
|
111
|
+
this.skillRoots,
|
|
112
|
+
name,
|
|
113
|
+
args,
|
|
114
|
+
this.skillPathMap,
|
|
115
|
+
);
|
|
116
|
+
nextArgs = await this.expandSyntheticFileEdits(name, nextArgs);
|
|
117
|
+
|
|
118
|
+
const syntheticResult = await this.handleSyntheticToolResponse(name, toolRequest.toolCall.id, nextArgs);
|
|
119
|
+
if (syntheticResult) {
|
|
120
|
+
return syntheticResult;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const humanLoopResult = await this.requestHumanApprovalIfNeeded(toolRequest, name, nextArgs);
|
|
124
|
+
if (humanLoopResult?.result) {
|
|
125
|
+
return humanLoopResult.result;
|
|
126
|
+
}
|
|
127
|
+
if (humanLoopResult?.args) {
|
|
128
|
+
nextArgs = humanLoopResult.args;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const override = await this.beforeToolCall?.({
|
|
132
|
+
threadId: toolRequest.threadId,
|
|
133
|
+
name,
|
|
134
|
+
args,
|
|
135
|
+
nextArgs,
|
|
136
|
+
request: ToolRequestParser.toHookRequest(toolRequest),
|
|
137
|
+
rootDir: this.rootDir,
|
|
138
|
+
});
|
|
139
|
+
if (override?.result) {
|
|
140
|
+
return override.result;
|
|
141
|
+
}
|
|
142
|
+
if (override?.args) {
|
|
143
|
+
nextArgs = override.args;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return this.invokeToolHandler(toolRequest, name, nextArgs, handler);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private async expandSyntheticFileEdits(name: string, args: ToolArgs): Promise<ToolArgs> {
|
|
150
|
+
if (name !== "edit_file") {
|
|
151
|
+
return args;
|
|
152
|
+
}
|
|
153
|
+
return ToolFsHandlers.expandEditFileContent(this.rootDir, args);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private async handleSyntheticToolResponse(
|
|
157
|
+
name: string,
|
|
158
|
+
toolCallId: string | undefined,
|
|
159
|
+
args: ToolArgs,
|
|
160
|
+
): Promise<unknown | null> {
|
|
161
|
+
if (name === "write_file" && typeof args.content === "string") {
|
|
162
|
+
const result = await ToolFsHandlers.handleWriteFile(this.rootDir, args, this.logger);
|
|
163
|
+
if (result) {
|
|
164
|
+
return new this.ToolMessage({ content: result.content, tool_call_id: toolCallId ?? "write_file" });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (name === "ls" || name === "inspect_files") {
|
|
168
|
+
const result = await ToolFsHandlers.handleListFiles(this.rootDir, args);
|
|
169
|
+
if (result) {
|
|
170
|
+
this.logger(`[tool] ${name}: ${String(args.path ?? ".")}`);
|
|
171
|
+
this.logger(`[tool] ${name} done in 0ms`);
|
|
172
|
+
return new this.ToolMessage({ content: result.content, tool_call_id: toolCallId ?? name });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private async invokeToolHandler(
|
|
179
|
+
toolRequest: ReturnType<typeof ToolRequestParser.toMiddlewareRequest>,
|
|
180
|
+
name: string,
|
|
181
|
+
nextArgs: ToolArgs,
|
|
182
|
+
handler: (request: unknown) => unknown,
|
|
183
|
+
): Promise<unknown> {
|
|
184
|
+
this.events?.emit({
|
|
185
|
+
name: "agent.runtime2.tool.call.start",
|
|
186
|
+
from: "agent-runtime2.middleware",
|
|
187
|
+
to: name,
|
|
188
|
+
payload: { toolCallId: toolRequest.toolCall.id, args: nextArgs },
|
|
189
|
+
});
|
|
190
|
+
this.logToolStart(name, nextArgs);
|
|
191
|
+
|
|
192
|
+
const startedAt = Date.now();
|
|
193
|
+
try {
|
|
194
|
+
const result = await this.runTool(name, nextArgs, toolRequest, handler);
|
|
195
|
+
const durationMs = Date.now() - startedAt;
|
|
196
|
+
this.logger(`[tool] ${name} done in ${durationMs}ms`);
|
|
197
|
+
if (name === "write_todos" && Array.isArray(nextArgs.todos)) {
|
|
198
|
+
await this.persistTodos?.(nextArgs.todos);
|
|
199
|
+
}
|
|
200
|
+
await this.afterToolCall?.({
|
|
201
|
+
threadId: toolRequest.threadId,
|
|
202
|
+
name,
|
|
203
|
+
args: nextArgs,
|
|
204
|
+
result,
|
|
205
|
+
request: ToolRequestParser.toAfterHookRequest(toolRequest),
|
|
206
|
+
rootDir: this.rootDir,
|
|
207
|
+
});
|
|
208
|
+
this.events?.emit({
|
|
209
|
+
name: "agent.runtime2.tool.call.done",
|
|
210
|
+
from: name,
|
|
211
|
+
to: "agent-runtime2.middleware",
|
|
212
|
+
payload: { toolCallId: toolRequest.toolCall.id, args: nextArgs, durationMs, result },
|
|
213
|
+
});
|
|
214
|
+
return result;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
this.events?.emit({
|
|
217
|
+
name: "agent.runtime2.tool.call.error",
|
|
218
|
+
from: name,
|
|
219
|
+
to: "agent-runtime2.middleware",
|
|
220
|
+
payload: {
|
|
221
|
+
toolCallId: toolRequest.toolCall.id,
|
|
222
|
+
args: nextArgs,
|
|
223
|
+
durationMs: Date.now() - startedAt,
|
|
224
|
+
error: error instanceof Error ? error.message : String(error),
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private async requestHumanApprovalIfNeeded(
|
|
232
|
+
toolRequest: ReturnType<typeof ToolRequestParser.toMiddlewareRequest>,
|
|
233
|
+
name: string,
|
|
234
|
+
args: ToolArgs,
|
|
235
|
+
): Promise<{ args?: ToolArgs; result?: unknown } | void> {
|
|
236
|
+
const request = this.humanLoopToolPolicy?.buildRequest({
|
|
237
|
+
threadId: toolRequest.threadId,
|
|
238
|
+
toolCallId: toolRequest.toolCall.id,
|
|
239
|
+
toolName: name,
|
|
240
|
+
args,
|
|
241
|
+
rootDir: this.rootDir,
|
|
242
|
+
});
|
|
243
|
+
if (!request || !this.humanLoop) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.events?.emit({
|
|
248
|
+
name: "agent.runtime2.human_loop.requested",
|
|
249
|
+
from: "agent-runtime2.middleware",
|
|
250
|
+
to: "human",
|
|
251
|
+
payload: request,
|
|
252
|
+
});
|
|
253
|
+
const decision = await this.humanLoop.request(request);
|
|
254
|
+
this.events?.emit({
|
|
255
|
+
name: "agent.runtime2.human_loop.resolved",
|
|
256
|
+
from: "human",
|
|
257
|
+
to: "agent-runtime2.middleware",
|
|
258
|
+
payload: {
|
|
259
|
+
request,
|
|
260
|
+
decision,
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return this.resolveHumanLoopDecision(toolRequest.toolCall.id, request, decision);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private resolveHumanLoopDecision(
|
|
268
|
+
toolCallId: string | undefined,
|
|
269
|
+
request: HumanLoopRequest,
|
|
270
|
+
decision: HumanLoopDecision,
|
|
271
|
+
): { args?: ToolArgs; result?: unknown } {
|
|
272
|
+
if (decision.action === "approve") {
|
|
273
|
+
return decision.revisedArgs ? { args: decision.revisedArgs } : {};
|
|
274
|
+
}
|
|
275
|
+
if (decision.action === "revise") {
|
|
276
|
+
if (decision.revisedArgs) {
|
|
277
|
+
return { args: decision.revisedArgs };
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
result: new this.ToolMessage({
|
|
281
|
+
content: decision.message ?? "Error: human review requested revised args but none were provided.",
|
|
282
|
+
tool_call_id: toolCallId ?? request.id,
|
|
283
|
+
}),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
result: new this.ToolMessage({
|
|
288
|
+
content: decision.message ?? `Rejected by human reviewer for tool ${request.toolName}.`,
|
|
289
|
+
tool_call_id: toolCallId ?? request.id,
|
|
290
|
+
}),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private async runTool(
|
|
295
|
+
name: string,
|
|
296
|
+
nextArgs: ToolArgs,
|
|
297
|
+
toolRequest: ReturnType<typeof ToolRequestParser.toMiddlewareRequest>,
|
|
298
|
+
handler: (request: unknown) => unknown,
|
|
299
|
+
): Promise<unknown> {
|
|
300
|
+
const runPromise = Promise.resolve(handler({
|
|
301
|
+
...toolRequest,
|
|
302
|
+
toolCall: { ...toolRequest.toolCall, args: nextArgs },
|
|
303
|
+
}));
|
|
304
|
+
if (name !== "execute") {
|
|
305
|
+
return runPromise;
|
|
306
|
+
}
|
|
307
|
+
return Promise.race([
|
|
308
|
+
runPromise,
|
|
309
|
+
new Promise<never>((_, reject) =>
|
|
310
|
+
setTimeout(() => reject(new Error(`execute timeout after ${this.executeTimeoutMs / 1000}s`)), this.executeTimeoutMs)
|
|
311
|
+
),
|
|
312
|
+
]);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private logToolStart(name: string, args: ToolArgs): void {
|
|
316
|
+
if (name === "execute" && typeof args.command === "string") {
|
|
317
|
+
const command = args.command.length > 120 ? `${args.command.slice(0, 117)}…` : args.command;
|
|
318
|
+
this.logger(`[tool] execute: ${command}`);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (name === "write_file") {
|
|
322
|
+
const filePath = String(args.file_path ?? args.path ?? "");
|
|
323
|
+
const length = typeof args.content === "string" ? args.content.length : 0;
|
|
324
|
+
this.logger(`[tool] write_file: ${filePath} (${length} chars)`);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
this.logger(`[tool] ${name}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ToolMessageConstructorLike, ToolMessageLike } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export class CommandPolicy {
|
|
4
|
+
public static maybeBlockExecuteCommand(
|
|
5
|
+
command: string,
|
|
6
|
+
toolCallId: string | undefined,
|
|
7
|
+
ToolMessage: ToolMessageConstructorLike,
|
|
8
|
+
rootDir: string,
|
|
9
|
+
allowedPathPrefixes: string[] = [],
|
|
10
|
+
): ToolMessageLike | null {
|
|
11
|
+
const message = this.getViolationMessage(command, rootDir, allowedPathPrefixes);
|
|
12
|
+
return message ? new ToolMessage({ content: message, tool_call_id: toolCallId ?? "execute" }) : null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private static getViolationMessage(command: string, rootDir: string, allowedPathPrefixes: string[]): string | null {
|
|
16
|
+
if (/\bls\b/.test(command) && command.includes("?")) {
|
|
17
|
+
return "Error: Do not run fuzzy ls probes with unknown paths. Continue with known workflow commands.";
|
|
18
|
+
}
|
|
19
|
+
if ((/\bbash\b/i.test(command) || /\/bin\/bash\b/i.test(command) || /\bzsh\b/i.test(command)) && command.includes("/scripts/")) {
|
|
20
|
+
return "Error: Do not wrap skill scripts with `bash`. Execute the script path directly.";
|
|
21
|
+
}
|
|
22
|
+
if (/\bchmod\s+\+x\b/i.test(command)) {
|
|
23
|
+
return "Error: Do not run chmod +x. Use direct execution of the target command or script.";
|
|
24
|
+
}
|
|
25
|
+
if (/\b(?:ba)?sh\s+-lc\b/i.test(command)) {
|
|
26
|
+
return "Error: Do not wrap command execution with shell -lc. Execute the target command directly.";
|
|
27
|
+
}
|
|
28
|
+
if (/\b(curl|wget)\b/i.test(command)) {
|
|
29
|
+
return "Error: Do not execute direct network fetch commands. Use provided scripts/tools and then write results with write_file/edit_file.";
|
|
30
|
+
}
|
|
31
|
+
if (/\|\s*reportgen\b/i.test(command)) {
|
|
32
|
+
return "Error: Do not pipe output to ad-hoc generators. Use write_file/edit_file for artifact generation.";
|
|
33
|
+
}
|
|
34
|
+
if (/(^|[\s;|&])(?:echo|cat|printf)[^|&;]*>\s*[^\s]+/i.test(command)) {
|
|
35
|
+
return "Error: Do not write files via shell redirection in execute. Use write_file/edit_file tools.";
|
|
36
|
+
}
|
|
37
|
+
return this.containsAbsolutePathOutsideWorkspace(command, rootDir, allowedPathPrefixes)
|
|
38
|
+
? "Error: Do not execute commands with absolute paths outside workspace root. Use workspace-relative paths only."
|
|
39
|
+
: null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private static containsAbsolutePathOutsideWorkspace(
|
|
43
|
+
command: string,
|
|
44
|
+
rootDir: string,
|
|
45
|
+
allowedPathPrefixes: string[],
|
|
46
|
+
): boolean {
|
|
47
|
+
const normalizedRoot = rootDir.replace(/\\/g, "/");
|
|
48
|
+
const normalizedAllowed = allowedPathPrefixes.map((entry) => entry.replace(/\\/g, "/"));
|
|
49
|
+
const pathLikeMatches = [...command.matchAll(/(?:^|[\s=>(;|&])(["']?)(\/[^\s"'`<>|&;]+)\1/g)];
|
|
50
|
+
for (const match of pathLikeMatches) {
|
|
51
|
+
const pathToken = match[2];
|
|
52
|
+
if (!pathToken || pathToken.startsWith("//")) continue;
|
|
53
|
+
if (this.isSystemPath(pathToken)) continue;
|
|
54
|
+
if (pathToken === normalizedRoot || pathToken.startsWith(`${normalizedRoot}/`)) continue;
|
|
55
|
+
if (normalizedAllowed.some((prefix) => pathToken === prefix || pathToken.startsWith(`${prefix}/`))) continue;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private static isSystemPath(pathToken: string): boolean {
|
|
62
|
+
return ["/usr/", "/bin/", "/etc/", "/var/", "/dev/", "/proc/", "/System/", "/opt/"]
|
|
63
|
+
.some((prefix) => pathToken.startsWith(prefix));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export class FrameworkPrompt {
|
|
2
|
+
public static build(
|
|
3
|
+
currentPrompt: string | undefined,
|
|
4
|
+
blockTaskTool: boolean,
|
|
5
|
+
allowedToolNames?: string[],
|
|
6
|
+
skillPathMap?: Record<string, string>,
|
|
7
|
+
): string {
|
|
8
|
+
const baseRules = [
|
|
9
|
+
"Framework execution rules:",
|
|
10
|
+
"- Follow SKILL instructions exactly as the source of truth.",
|
|
11
|
+
"- Execute required workflow steps in order; do not skip required steps.",
|
|
12
|
+
"- Do not add extra exploratory or redundant operations outside the SKILL workflow.",
|
|
13
|
+
"- Support both tool calls and script commands: use whichever the SKILL explicitly allows for each step.",
|
|
14
|
+
"- If a SKILL allows both tool and script for the same step, prefer the safer/more structured tool path.",
|
|
15
|
+
"- For independent operations with no data dependency, prefer parallel tool calls to reduce latency.",
|
|
16
|
+
"- Do not stop after intermediate reads/planning. Continue autonomously until required outputs are produced.",
|
|
17
|
+
'- Return strict JSON for tool arguments (example: {"offset":0}, never "offset=0").',
|
|
18
|
+
"- Use workspace-relative paths only; do not invent absolute filesystem paths unless provided by runtime placeholders.",
|
|
19
|
+
"- Do not inspect script source files unless explicitly requested.",
|
|
20
|
+
"- Runtime path variable `${WORKSPACE}` expands to the workspace root.",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
if (blockTaskTool) {
|
|
24
|
+
baseRules.push("- The task tool is disabled. Never call task; complete work using available file and execute tools.");
|
|
25
|
+
}
|
|
26
|
+
if (allowedToolNames?.length) {
|
|
27
|
+
baseRules.push(`- Tool allowlist is enforced by config. Only call these tools: ${allowedToolNames.join(", ")}.`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const skillEntries = Object.entries(skillPathMap ?? {});
|
|
31
|
+
if (skillEntries.length > 0) {
|
|
32
|
+
baseRules.push("- Active skills for this run:");
|
|
33
|
+
for (const [skillId, skillPath] of skillEntries) {
|
|
34
|
+
baseRules.push(` - ${skillId} => ${skillPath}`);
|
|
35
|
+
}
|
|
36
|
+
baseRules.push("- Use `${SKILL_PATH:<skill-id>}` to reference a specific skill directory from the active skill mapping above.");
|
|
37
|
+
if (skillEntries.length === 1) {
|
|
38
|
+
baseRules.push("- Because only one skill is active, `${SKILL_PATH}` also resolves to that skill directory.");
|
|
39
|
+
}
|
|
40
|
+
baseRules.push("- `${SKILL_PATH...}` is only for skill-owned assets such as scripts/, references/, templates, and static inputs explicitly required by the SKILL.");
|
|
41
|
+
baseRules.push("- Do not use `${SKILL_PATH...}` for exploration, listing, globbing, discovery, or inspection. Do not call ls/glob/read_file on a skill path unless the user explicitly asks for skill source or contents.");
|
|
42
|
+
baseRules.push("- If a SKILL provides an explicit script command using `${SKILL_PATH...}` and `${WORKSPACE}`, execute that command directly instead of exploring the skill directory first.");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const frameworkPrompt = baseRules.join("\n");
|
|
46
|
+
if (!currentPrompt?.trim() || currentPrompt.includes("Framework execution rules:")) {
|
|
47
|
+
return currentPrompt?.trim() ? currentPrompt : frameworkPrompt;
|
|
48
|
+
}
|
|
49
|
+
return `${currentPrompt}\n\n${frameworkPrompt}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
export function buildSkillPathMap(skillRoots: string[]): Record<string, string> {
|
|
5
|
+
const counts = new Map<string, number>();
|
|
6
|
+
const result: Record<string, string> = {};
|
|
7
|
+
|
|
8
|
+
for (const skillRoot of expandSkillRoots(skillRoots)) {
|
|
9
|
+
const normalizedRoot = skillRoot.replace(/\\/g, "/");
|
|
10
|
+
const baseName = sanitizeSkillId(basename(normalizedRoot) || "skill");
|
|
11
|
+
const seen = counts.get(baseName) ?? 0;
|
|
12
|
+
counts.set(baseName, seen + 1);
|
|
13
|
+
const id = seen === 0 ? baseName : `${baseName}_${seen + 1}`;
|
|
14
|
+
result[id] = normalizedRoot;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function sanitizeSkillId(value: string): string {
|
|
21
|
+
return value
|
|
22
|
+
.trim()
|
|
23
|
+
.replace(/[^A-Za-z0-9._-]+/g, "-")
|
|
24
|
+
.replace(/^-+|-+$/g, "")
|
|
25
|
+
.toLowerCase() || "skill";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function expandSkillRoots(skillRoots: string[]): string[] {
|
|
29
|
+
const expanded = skillRoots.flatMap((skillRoot) => {
|
|
30
|
+
const directSkill = join(skillRoot, "SKILL.md");
|
|
31
|
+
if (existsSync(directSkill)) {
|
|
32
|
+
return [skillRoot];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
return readdirSync(skillRoot, { withFileTypes: true })
|
|
37
|
+
.filter((entry) => entry.isDirectory())
|
|
38
|
+
.map((entry) => join(skillRoot, entry.name))
|
|
39
|
+
.filter((entryPath) => {
|
|
40
|
+
const skillFile = join(entryPath, "SKILL.md");
|
|
41
|
+
return existsSync(skillFile) && statSync(entryPath).isDirectory();
|
|
42
|
+
});
|
|
43
|
+
} catch {
|
|
44
|
+
return [skillRoot];
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return expanded.length > 0 ? expanded : skillRoots;
|
|
49
|
+
}
|