subagents (written by an agent, for an agent)
so meta.
This commit is contained in:
parent
09445cb565
commit
40cab7ca49
@ -8,8 +8,6 @@ You are spawned as a child process by the Master Control Console (MCC) in Gadget
|
|||||||
|
|
||||||
You will generally be tasked with exploring a software repository to learn about the software architecture, how things are imlemented, the structure of a class and it's methods, the order of operations as they are performed by the software, or even just read and summarize documentation looking for a specific piece of knowledge for the MCC.
|
You will generally be tasked with exploring a software repository to learn about the software architecture, how things are imlemented, the structure of a class and it's methods, the order of operations as they are performed by the software, or even just read and summarize documentation looking for a specific piece of knowledge for the MCC.
|
||||||
|
|
||||||
{{tool_aggression}}
|
|
||||||
|
|
||||||
This is your job.
|
This is your job.
|
||||||
|
|
||||||
### CHAT SESSION
|
### CHAT SESSION
|
||||||
@ -8,8 +8,6 @@ You are spawned as a child process by the Master Control Console (MCC) in Gadget
|
|||||||
|
|
||||||
You will generally be tasked with exploring a software repository to learn about the software architecture, how things are imlemented, the structure of a class and it's methods, or even just read and summarize documentation looking for a specific piece of knowledge for the MCC.
|
You will generally be tasked with exploring a software repository to learn about the software architecture, how things are imlemented, the structure of a class and it's methods, or even just read and summarize documentation looking for a specific piece of knowledge for the MCC.
|
||||||
|
|
||||||
{{tool_aggression}}
|
|
||||||
|
|
||||||
This is your job.
|
This is your job.
|
||||||
|
|
||||||
### CHAT SESSION
|
### CHAT SESSION
|
||||||
@ -2,23 +2,30 @@
|
|||||||
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
// Licensed under the Apache License, Version 2.0
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
import path from "node:path";
|
||||||
|
import fs from "node:fs/promises";
|
||||||
|
|
||||||
import env from "../config/env.ts";
|
import env from "../config/env.ts";
|
||||||
|
|
||||||
import { Socket } from "socket.io-client";
|
import { Socket } from "socket.io-client";
|
||||||
import {
|
import {
|
||||||
IAiChatOptions,
|
type IAiChatOptions,
|
||||||
IAiChatResponse,
|
type IAiChatResponse,
|
||||||
IAiResponseStreamFn,
|
type IAiResponseStreamFn,
|
||||||
|
type IAiTool,
|
||||||
type IContextChatMessage,
|
type IContextChatMessage,
|
||||||
} from "@gadget/ai";
|
} from "@gadget/ai";
|
||||||
import {
|
import {
|
||||||
IChatSession,
|
type IAiProvider,
|
||||||
IChatTurn,
|
type IChatSession,
|
||||||
IUser,
|
type IChatTurn,
|
||||||
ServerToClientEvents,
|
type IChatSubagentProcess,
|
||||||
ClientToServerEvents,
|
type IChatToolCall,
|
||||||
|
type IUser,
|
||||||
|
type ServerToClientEvents,
|
||||||
|
type ClientToServerEvents,
|
||||||
ChatSessionMode,
|
ChatSessionMode,
|
||||||
IProject,
|
type IProject,
|
||||||
} from "@gadget/api";
|
} from "@gadget/api";
|
||||||
|
|
||||||
import AiService from "./ai.ts";
|
import AiService from "./ai.ts";
|
||||||
@ -40,6 +47,7 @@ import {
|
|||||||
PlanFileWriteTool,
|
PlanFileWriteTool,
|
||||||
PlanListTool,
|
PlanListTool,
|
||||||
ShellExecTool,
|
ShellExecTool,
|
||||||
|
SubagentTool,
|
||||||
type DroneToolboxEnvironment,
|
type DroneToolboxEnvironment,
|
||||||
} from "../tools/index.ts";
|
} from "../tools/index.ts";
|
||||||
|
|
||||||
@ -65,6 +73,8 @@ const toolboxEnv: DroneToolboxEnvironment = {
|
|||||||
|
|
||||||
class AgentService extends GadgetService {
|
class AgentService extends GadgetService {
|
||||||
private toolbox = new AiToolbox(toolboxEnv);
|
private toolbox = new AiToolbox(toolboxEnv);
|
||||||
|
private currentWorkOrder: IAgentWorkOrder | null = null;
|
||||||
|
private currentSocket: DroneSocket | null = null;
|
||||||
|
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return "AgentService";
|
return "AgentService";
|
||||||
@ -109,10 +119,17 @@ class AgentService extends GadgetService {
|
|||||||
this.toolbox.register(new PlanFileEditTool(this.toolbox), [ChatSessionMode.Plan]);
|
this.toolbox.register(new PlanFileEditTool(this.toolbox), [ChatSessionMode.Plan]);
|
||||||
this.toolbox.register(new PlanListTool(this.toolbox), [ChatSessionMode.Plan]);
|
this.toolbox.register(new PlanListTool(this.toolbox), [ChatSessionMode.Plan]);
|
||||||
|
|
||||||
|
// Chat tools — subagent spawning: available in all modes
|
||||||
|
const subagentTool = new SubagentTool(this.toolbox);
|
||||||
|
subagentTool.setSpawner((agentType, prompt) => this.spawnSubagent(agentType, prompt));
|
||||||
|
this.toolbox.register(subagentTool, readOnlyModes);
|
||||||
|
|
||||||
this.log.info("started");
|
this.log.info("started");
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop(): Promise<void> {
|
async stop(): Promise<void> {
|
||||||
|
this.currentWorkOrder = null;
|
||||||
|
this.currentSocket = null;
|
||||||
this.log.info("stopped");
|
this.log.info("stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +137,9 @@ class AgentService extends GadgetService {
|
|||||||
workOrder: IAgentWorkOrder,
|
workOrder: IAgentWorkOrder,
|
||||||
socket: DroneSocket,
|
socket: DroneSocket,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
this.currentWorkOrder = workOrder;
|
||||||
|
this.currentSocket = socket;
|
||||||
|
|
||||||
const { turn } = workOrder;
|
const { turn } = workOrder;
|
||||||
let toolCallCount = 0;
|
let toolCallCount = 0;
|
||||||
let inputTokens = 0;
|
let inputTokens = 0;
|
||||||
@ -433,6 +453,259 @@ class AgentService extends GadgetService {
|
|||||||
cacheDir,
|
cacheDir,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async spawnSubagent(
|
||||||
|
agentType: string,
|
||||||
|
prompt: string,
|
||||||
|
): Promise<string> {
|
||||||
|
const workOrder = this.currentWorkOrder;
|
||||||
|
if (!workOrder) {
|
||||||
|
return JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: "OPERATION_FAILED",
|
||||||
|
message: "No active work order. Subagent cannot be spawned outside of a work order.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const turn = workOrder.turn;
|
||||||
|
const session = turn.session as IChatSession;
|
||||||
|
|
||||||
|
if (!session.user) {
|
||||||
|
return JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: "OPERATION_FAILED",
|
||||||
|
message: "ChatSession must be populated with user data.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = session.user as IUser;
|
||||||
|
const provider = turn.provider as IAiProvider;
|
||||||
|
|
||||||
|
if (!provider || typeof provider === "string") {
|
||||||
|
return JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: "OPERATION_FAILED",
|
||||||
|
message: "Turn provider must be populated.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the subagent loop
|
||||||
|
const startTime = Date.now();
|
||||||
|
const systemPrompt = await this.buildSubagentSystemPrompt(agentType);
|
||||||
|
const messages: IContextChatMessage[] = [
|
||||||
|
{ createdAt: new Date(), role: "system", content: systemPrompt },
|
||||||
|
{ createdAt: new Date(), role: "user", content: prompt },
|
||||||
|
];
|
||||||
|
|
||||||
|
const tools = this.getToolsForSubagent(agentType);
|
||||||
|
const streamHandler = this.currentSocket
|
||||||
|
? this.makeStreamHandler(this.currentSocket)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
let fullResponse = "";
|
||||||
|
let fullThinking = "";
|
||||||
|
const subagentToolCalls: IChatToolCall[] = [];
|
||||||
|
let toolCallCount = 0;
|
||||||
|
let inputTokens = 0;
|
||||||
|
let outputTokens = 0;
|
||||||
|
let thinkingTokenCount = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let continueLoop = true;
|
||||||
|
while (continueLoop) {
|
||||||
|
continueLoop = false;
|
||||||
|
|
||||||
|
const chatOptions: IAiChatOptions = {
|
||||||
|
context: messages,
|
||||||
|
tools,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.log.info("subagent loop iteration", {
|
||||||
|
agentType,
|
||||||
|
messagesCount: messages.length,
|
||||||
|
toolsAvailable: tools.map((t) => t.name),
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await AiService.chat(
|
||||||
|
provider,
|
||||||
|
{
|
||||||
|
modelId: turn.llm,
|
||||||
|
params: { reasoning: false, temperature: 0.8, topP: 0.9, topK: 40 },
|
||||||
|
},
|
||||||
|
chatOptions,
|
||||||
|
streamHandler,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.doneReason === "load" && !response.response && !response.thinking) {
|
||||||
|
throw new Error("Subagent model failed to respond (still loading or error)");
|
||||||
|
}
|
||||||
|
|
||||||
|
inputTokens += response.stats?.tokenCounts?.input ?? 0;
|
||||||
|
if (response.thinking) {
|
||||||
|
thinkingTokenCount += Math.ceil(response.thinking.length / 4);
|
||||||
|
}
|
||||||
|
if (response.response) {
|
||||||
|
fullResponse += response.response;
|
||||||
|
outputTokens += Math.ceil(response.response.length / 4);
|
||||||
|
}
|
||||||
|
if (response.thinking) {
|
||||||
|
fullThinking = (fullThinking + response.thinking).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process tool calls
|
||||||
|
if (response.toolCalls && response.toolCalls.length > 0) {
|
||||||
|
continueLoop = true;
|
||||||
|
toolCallCount += response.toolCalls.length;
|
||||||
|
|
||||||
|
messages.push({
|
||||||
|
createdAt: new Date(),
|
||||||
|
role: "assistant",
|
||||||
|
content: response.response,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const toolCall of response.toolCalls) {
|
||||||
|
const toolArgsRaw = toolCall.function.arguments;
|
||||||
|
const result = await this.executeTool(
|
||||||
|
toolCall.function.name,
|
||||||
|
toolArgsRaw,
|
||||||
|
);
|
||||||
|
|
||||||
|
subagentToolCalls.push({
|
||||||
|
callId: toolCall.callId,
|
||||||
|
name: toolCall.function.name,
|
||||||
|
parameters: toolArgsRaw,
|
||||||
|
response: result,
|
||||||
|
});
|
||||||
|
|
||||||
|
messages.push({
|
||||||
|
createdAt: new Date(),
|
||||||
|
role: "tool",
|
||||||
|
callId: toolCall.callId,
|
||||||
|
toolName: toolCall.function.name,
|
||||||
|
content: result,
|
||||||
|
});
|
||||||
|
|
||||||
|
inputTokens += Math.ceil(toolArgsRaw.length / 4);
|
||||||
|
outputTokens += Math.ceil(result.length / 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we have a response if the subagent only made tool calls
|
||||||
|
if (!fullResponse.trim()) {
|
||||||
|
if (toolCallCount > 0) {
|
||||||
|
fullResponse = `Subagent task completed. Executed ${toolCallCount} tool calls to perform the requested actions.`;
|
||||||
|
} else {
|
||||||
|
fullResponse = "Subagent task completed without additional actions.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const endTime = Date.now();
|
||||||
|
const durationMs = endTime - startTime;
|
||||||
|
const durationSeconds = Math.floor(durationMs / 1000);
|
||||||
|
const minutes = Math.floor(durationSeconds / 60);
|
||||||
|
const seconds = durationSeconds % 60;
|
||||||
|
const durationLabel = `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "00")}`;
|
||||||
|
|
||||||
|
const result: IChatSubagentProcess = {
|
||||||
|
prompt,
|
||||||
|
thinking: fullThinking || undefined,
|
||||||
|
response: fullResponse,
|
||||||
|
toolCalls: subagentToolCalls,
|
||||||
|
stats: {
|
||||||
|
toolCallCount,
|
||||||
|
inputTokens,
|
||||||
|
thinkingTokenCount,
|
||||||
|
responseTokens: outputTokens,
|
||||||
|
durationMs,
|
||||||
|
durationLabel,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return JSON.stringify({ success: true, data: result });
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
this.log.error("subagent execution failed", { agentType, error: errorMessage });
|
||||||
|
|
||||||
|
return JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: "SUBAGENT_FAILED",
|
||||||
|
message: errorMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getToolsForSubagent(agentType: string): IAiTool[] {
|
||||||
|
const mode = agentType === "explore"
|
||||||
|
? ChatSessionMode.Plan
|
||||||
|
: ChatSessionMode.Build;
|
||||||
|
|
||||||
|
const tools = Array.from(this.toolbox.getModeSet(mode) || []);
|
||||||
|
|
||||||
|
if (agentType === "explore") {
|
||||||
|
return tools.filter((t) =>
|
||||||
|
!t.name.startsWith("plan_") &&
|
||||||
|
t.name !== "subagent"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tools.filter((t) => t.name !== "subagent");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async buildSubagentSystemPrompt(agentType: string): Promise<string> {
|
||||||
|
const promptDir = path.resolve(env.installDir, "data", "prompts", "subagent", agentType);
|
||||||
|
const templatePath = path.join(promptDir, "system.md");
|
||||||
|
|
||||||
|
let template: string;
|
||||||
|
try {
|
||||||
|
template = await fs.readFile(templatePath, "utf-8");
|
||||||
|
} catch {
|
||||||
|
this.log.error("subagent prompt template not found", { agentType, templatePath });
|
||||||
|
throw new Error(`Subagent prompt template not found: ${templatePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const workOrder = this.currentWorkOrder;
|
||||||
|
if (!workOrder) return template;
|
||||||
|
|
||||||
|
const session = workOrder.turn.session as IChatSession;
|
||||||
|
const user = session?.user as IUser | undefined;
|
||||||
|
const project = session?.project as IProject | undefined;
|
||||||
|
const provider = workOrder.turn.provider as IAiProvider | undefined;
|
||||||
|
|
||||||
|
// Build session block matching the format used in gadget-code's buildSystemPrompt
|
||||||
|
const sessionBlock = [
|
||||||
|
`Session ID: ${session?._id ?? "unknown"}`,
|
||||||
|
`Session Name: ${session?.name ?? "unknown"}`,
|
||||||
|
`Session Created: ${session?.createdAt?.toISOString() ?? "unknown"}`,
|
||||||
|
`AI Provider: ${provider?.name ?? "unknown"}`,
|
||||||
|
`AI API: ${provider?.apiType ?? "unknown"}`,
|
||||||
|
`Model: ${workOrder.turn.llm}`,
|
||||||
|
`Project: ${project?.name ?? "unknown"}`,
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const personaBlock = [
|
||||||
|
`User ID: ${user?._id ?? "unknown"}`,
|
||||||
|
`Name: ${user?.displayName ?? "unknown"}`,
|
||||||
|
`Is Admin: ${user?.flags?.isAdmin ? "Yes" : "No"}`,
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
template = template.replace("{{session_block}}", sessionBlock);
|
||||||
|
template = template.replace("{{persona_block}}", personaBlock);
|
||||||
|
|
||||||
|
// Append AGENTS.md if available in the project root
|
||||||
|
try {
|
||||||
|
const projectRoot = this.toolbox.env.workspace?.projectDir;
|
||||||
|
if (projectRoot) {
|
||||||
|
const agentsMdPath = path.join(projectRoot, "AGENTS.md");
|
||||||
|
const agentsMd = await fs.readFile(agentsMdPath, "utf-8");
|
||||||
|
template += "\n\n## AGENT CONFIGURATION\n\n" + agentsMd;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// AGENTS.md not found or inaccessible - that's fine
|
||||||
|
}
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AgentService };
|
export { AgentService };
|
||||||
|
|||||||
4
gadget-drone/src/tools/chat/index.ts
Normal file
4
gadget-drone/src/tools/chat/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
export { SubagentTool } from "./subagent.ts";
|
||||||
108
gadget-drone/src/tools/chat/subagent.ts
Normal file
108
gadget-drone/src/tools/chat/subagent.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
import type { IAiLogger, IToolArguments, IToolDefinition } from "@gadget/ai";
|
||||||
|
import { formatError } from "@gadget/ai";
|
||||||
|
import { DroneTool } from "../tool.ts";
|
||||||
|
|
||||||
|
const VALID_AGENT_TYPES = ["explore", "general"] as const;
|
||||||
|
type AgentType = (typeof VALID_AGENT_TYPES)[number];
|
||||||
|
|
||||||
|
export class SubagentTool extends DroneTool {
|
||||||
|
private _spawnSubagent: ((agentType: string, prompt: string) => Promise<string>) | null = null;
|
||||||
|
|
||||||
|
setSpawner(fn: typeof this._spawnSubagent): void {
|
||||||
|
this._spawnSubagent = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return "subagent";
|
||||||
|
}
|
||||||
|
|
||||||
|
get category(): string {
|
||||||
|
return "chat";
|
||||||
|
}
|
||||||
|
|
||||||
|
get definition(): IToolDefinition {
|
||||||
|
return {
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: this.name,
|
||||||
|
description:
|
||||||
|
"Spawn a subagent to perform a specific task. The subagent will execute the task and return its result. Use 'explore' agent type for research and information gathering tasks. Use 'general' agent type for general-purpose task execution.",
|
||||||
|
parameters: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
agent_type: {
|
||||||
|
type: "string",
|
||||||
|
enum: [...VALID_AGENT_TYPES],
|
||||||
|
description:
|
||||||
|
"The type of subagent to spawn. Use 'explore' for research and information gathering. Use 'general' for general-purpose task execution.",
|
||||||
|
},
|
||||||
|
prompt: {
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"The task description and instructions for the subagent. Be specific about what information to find or what task to perform.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["agent_type", "prompt"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(args: IToolArguments, logger: IAiLogger): Promise<string> {
|
||||||
|
const { agent_type, prompt } = args;
|
||||||
|
|
||||||
|
if (!agent_type) {
|
||||||
|
return formatError({
|
||||||
|
code: "MISSING_PARAMETER",
|
||||||
|
message: "The 'agent_type' parameter is required.",
|
||||||
|
parameter: "agent_type",
|
||||||
|
expected: "Either 'explore' or 'general'",
|
||||||
|
recoveryHint: "Specify either 'explore' or 'general' for the agent_type parameter.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VALID_AGENT_TYPES.includes(agent_type as AgentType)) {
|
||||||
|
return formatError({
|
||||||
|
code: "INVALID_PARAMETER",
|
||||||
|
message: `Invalid agent_type: '${agent_type}'. Must be one of: ${VALID_AGENT_TYPES.join(", ")}`,
|
||||||
|
parameter: "agent_type",
|
||||||
|
expected: `One of: ${VALID_AGENT_TYPES.join(", ")}`,
|
||||||
|
recoveryHint: "Use 'explore' for research tasks or 'general' for general tasks.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prompt || typeof prompt !== "string") {
|
||||||
|
return formatError({
|
||||||
|
code: "MISSING_PARAMETER",
|
||||||
|
message: "The 'prompt' parameter is required and must be a string.",
|
||||||
|
parameter: "prompt",
|
||||||
|
expected: "A non-empty string describing the task for the subagent.",
|
||||||
|
recoveryHint: "Provide specific instructions for the subagent to execute.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._spawnSubagent) {
|
||||||
|
return formatError({
|
||||||
|
code: "OPERATION_FAILED",
|
||||||
|
message: "SubagentTool has not been initialized with a spawner function.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("spawning subagent", { agentType: agent_type, promptLength: (prompt as string).length });
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await this._spawnSubagent(agent_type as string, prompt as string);
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
logger.error("subagent execution failed", { agentType: agent_type, error: message });
|
||||||
|
return JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: "SUBAGENT_FAILED",
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
export { AiToolbox, type DroneToolboxEnvironment } from "./toolbox.ts";
|
export { AiToolbox, type DroneToolboxEnvironment } from "./toolbox.ts";
|
||||||
export { DroneTool } from "./tool.ts";
|
export { DroneTool } from "./tool.ts";
|
||||||
|
export * from "./chat/index.ts";
|
||||||
export * from "./system/index.ts";
|
export * from "./system/index.ts";
|
||||||
export * from "./network/index.ts";
|
export * from "./network/index.ts";
|
||||||
export * from "./plan/index.ts";
|
export * from "./plan/index.ts";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user