@easynet/agent-runtime 1.0.3 → 1.0.4
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 +9 -24
- package/.github/workflows/release.yml +14 -35
- package/agent-runtime/.github/workflows/ci.yml +69 -0
- package/agent-runtime/.github/workflows/release.yml +118 -0
- package/agent-runtime/.releaserc.cjs +26 -0
- package/agent-runtime/config/agent.deep.yaml +25 -0
- package/agent-runtime/config/agent.react.yaml +24 -0
- package/agent-runtime/example/basic-usage.ts +49 -0
- package/agent-runtime/package-lock.json +7740 -0
- package/agent-runtime/package.json +49 -0
- package/agent-runtime/pnpm-lock.yaml +3712 -0
- package/agent-runtime/scripts/resolve-deps.js +54 -0
- package/agent-runtime/src/agents/deep-agent.ts +165 -0
- package/agent-runtime/src/agents/react-agent.helpers.ts +227 -0
- package/agent-runtime/src/agents/react-agent.ts +584 -0
- package/{src → agent-runtime/src/agents}/sub-agent.ts +2 -2
- package/agent-runtime/src/cli/args.ts +15 -0
- package/agent-runtime/src/cli/event-listener.ts +162 -0
- package/agent-runtime/src/cli/interactive.ts +144 -0
- package/agent-runtime/src/cli/runtime.ts +31 -0
- package/agent-runtime/src/cli/spinner.ts +23 -0
- package/agent-runtime/src/cli/terminal-render.ts +322 -0
- package/agent-runtime/src/cli/types.ts +33 -0
- package/agent-runtime/src/cli.ts +134 -0
- package/agent-runtime/src/config/helpers.ts +179 -0
- package/agent-runtime/src/config/index.ts +245 -0
- package/agent-runtime/src/config/types.ts +62 -0
- package/agent-runtime/src/core/context.ts +266 -0
- package/agent-runtime/src/index.ts +55 -0
- package/agent-runtime/tsconfig.json +18 -0
- package/apps/imessagebot/README.md +38 -0
- package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/README.md +33 -0
- package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package-lock.json +15257 -0
- package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package.json +55 -0
- package/apps/imessagebot/config/agents/deep/agent.yaml +31 -0
- package/apps/imessagebot/config/agents/react/agent.yaml +58 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/README.md +33 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package-lock.json +15457 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package.json +55 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/README.md +33 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package-lock.json +15257 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package.json +62 -0
- package/apps/imessagebot/config/agents/shared/memory.yaml +31 -0
- package/apps/imessagebot/config/agents/shared/model.yaml +23 -0
- package/apps/imessagebot/config/agents/shared/tool.yaml +13 -0
- package/apps/imessagebot/config/app.yaml +14 -0
- package/apps/imessagebot/package-lock.json +53695 -0
- package/apps/imessagebot/package.json +41 -0
- package/apps/imessagebot/pnpm-lock.yaml +1589 -0
- package/apps/imessagebot/scripts/resolve-deps.js +41 -0
- package/apps/imessagebot/scripts/test-llm.mjs +27 -0
- package/apps/imessagebot/scripts/validate-tools-config.mjs +174 -0
- package/apps/imessagebot/src/config.ts +76 -0
- package/apps/imessagebot/src/context.ts +35 -0
- package/apps/imessagebot/src/index.ts +17 -0
- package/apps/imessagebot/tsconfig.json +18 -0
- package/apps/itermbot/.github/workflows/ci.yml +61 -0
- package/apps/itermbot/.github/workflows/release.yml +80 -0
- package/apps/itermbot/.releaserc.cjs +26 -0
- package/apps/itermbot/README.md +82 -0
- package/apps/itermbot/config/app.yaml +29 -0
- package/apps/itermbot/config/tsconfig.json +18 -0
- package/apps/itermbot/macos_disk_usage_agent_plan.md +244 -0
- package/apps/itermbot/package-lock.json +53697 -0
- package/apps/itermbot/package.json +57 -0
- package/apps/itermbot/pnpm-lock.yaml +3966 -0
- package/apps/itermbot/scripts/patch-buildin-cache.sh +25 -0
- package/apps/itermbot/scripts/resolve-deps.js +41 -0
- package/apps/itermbot/scripts/test-llm.mjs +32 -0
- package/apps/itermbot/skills/command-explain-and-guard/SKILL.md +39 -0
- package/apps/itermbot/skills/command-explain-and-guard/handler.js +86 -0
- package/apps/itermbot/skills/disk-usage-investigate/SKILL.md +44 -0
- package/apps/itermbot/skills/disk-usage-investigate/handler.js +12 -0
- package/apps/itermbot/skills/gpu-ssh-monitor/SKILL.md +64 -0
- package/apps/itermbot/skills/repo-triage/SKILL.md +40 -0
- package/apps/itermbot/skills/repo-triage/handler.js +56 -0
- package/apps/itermbot/skills/test-failure-diagnose/SKILL.md +43 -0
- package/apps/itermbot/skills/test-failure-diagnose/handler.js +107 -0
- package/apps/itermbot/src/config.ts +95 -0
- package/apps/itermbot/src/context.ts +35 -0
- package/apps/itermbot/src/index.ts +223 -0
- package/apps/itermbot/src/iterm/session-hint.ts +40 -0
- package/apps/itermbot/src/iterm/target-routing.ts +419 -0
- package/apps/itermbot/src/startup/colors.ts +317 -0
- package/apps/itermbot/src/startup/diagnostics.ts +97 -0
- package/apps/itermbot/src/startup/ui.ts +141 -0
- package/config/agent.deep.yaml +25 -0
- package/config/agent.react.yaml +24 -0
- package/dist/agents/deep-agent.d.ts +37 -0
- package/dist/agents/deep-agent.d.ts.map +1 -0
- package/dist/agents/deep-agent.js +115 -0
- package/dist/agents/deep-agent.js.map +1 -0
- package/dist/agents/react-agent.d.ts +40 -0
- package/dist/agents/react-agent.d.ts.map +1 -0
- package/dist/agents/react-agent.helpers.d.ts +40 -0
- package/dist/agents/react-agent.helpers.d.ts.map +1 -0
- package/dist/agents/react-agent.helpers.js +196 -0
- package/dist/agents/react-agent.helpers.js.map +1 -0
- package/dist/agents/react-agent.js +400 -0
- package/dist/agents/react-agent.js.map +1 -0
- package/dist/agents/sub-agent.d.ts +34 -0
- package/dist/agents/sub-agent.d.ts.map +1 -0
- package/dist/agents/sub-agent.js +53 -0
- package/dist/agents/sub-agent.js.map +1 -0
- package/dist/cli/args.d.ts +8 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +9 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/event-listener.d.ts +3 -0
- package/dist/cli/event-listener.d.ts.map +1 -0
- package/dist/cli/event-listener.js +131 -0
- package/dist/cli/event-listener.js.map +1 -0
- package/dist/cli/interactive.d.ts +4 -0
- package/dist/cli/interactive.d.ts.map +1 -0
- package/dist/cli/interactive.js +118 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/cli/runtime.d.ts +8 -0
- package/dist/cli/runtime.d.ts.map +1 -0
- package/dist/cli/runtime.js +27 -0
- package/dist/cli/runtime.js.map +1 -0
- package/dist/cli/spinner.d.ts +2 -0
- package/dist/cli/spinner.d.ts.map +1 -0
- package/dist/cli/spinner.js +22 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/terminal-render.d.ts +7 -0
- package/dist/cli/terminal-render.d.ts.map +1 -0
- package/dist/cli/terminal-render.js +282 -0
- package/dist/cli/terminal-render.js.map +1 -0
- package/dist/cli/types.d.ts +29 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +3 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli.d.ts +4 -41
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +84 -588
- package/dist/cli.js.map +1 -1
- package/dist/config/helpers.d.ts +6 -0
- package/dist/config/helpers.d.ts.map +1 -0
- package/dist/config/helpers.js +164 -0
- package/dist/config/helpers.js.map +1 -0
- package/dist/config/index.d.ts +15 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +160 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +57 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/context.d.ts +8 -69
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +44 -24
- package/dist/context.js.map +1 -1
- package/dist/core/context.d.ts +66 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +149 -0
- package/dist/core/context.js.map +1 -0
- package/dist/deep-agent.d.ts +5 -2
- package/dist/deep-agent.d.ts.map +1 -1
- package/dist/deep-agent.js +44 -11
- package/dist/deep-agent.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/middleware/malformed-tool-call-middleware.d.ts +8 -0
- package/dist/middleware/malformed-tool-call-middleware.d.ts.map +1 -0
- package/dist/middleware/malformed-tool-call-middleware.js +191 -0
- package/dist/middleware/malformed-tool-call-middleware.js.map +1 -0
- package/dist/react-agent.d.ts +2 -2
- package/dist/react-agent.d.ts.map +1 -1
- package/dist/react-agent.js +28 -9
- package/dist/react-agent.js.map +1 -1
- package/package.json +1 -1
- package/scripts/resolve-deps.js +54 -0
- package/src/agents/deep-agent.ts +165 -0
- package/src/agents/react-agent.helpers.ts +227 -0
- package/src/agents/react-agent.ts +584 -0
- package/src/agents/sub-agent.ts +82 -0
- package/src/cli/args.ts +15 -0
- package/src/cli/event-listener.ts +162 -0
- package/src/cli/interactive.ts +144 -0
- package/src/cli/runtime.ts +31 -0
- package/src/cli/spinner.ts +23 -0
- package/src/cli/terminal-render.ts +322 -0
- package/src/cli/types.ts +33 -0
- package/src/cli.ts +91 -702
- package/src/config/helpers.ts +179 -0
- package/src/config/index.ts +245 -0
- package/src/config/types.ts +62 -0
- package/src/core/context.ts +266 -0
- package/src/index.ts +13 -11
- package/src/middleware/malformed-tool-call-middleware.ts +239 -0
- package/src/types/markdown-it-terminal.d.ts +4 -0
- package/src/types/marked-terminal.d.ts +16 -0
- package/dist/config.d.ts +0 -86
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -84
- package/dist/config.js.map +0 -1
- package/src/config.ts +0 -177
- package/src/context.ts +0 -247
- package/src/deep-agent.ts +0 -104
- package/src/react-agent.ts +0 -576
- /package/{src → agent-runtime/src/middleware}/malformed-tool-call-middleware.ts +0 -0
- /package/{src → agent-runtime/src/types}/markdown-it-terminal.d.ts +0 -0
- /package/{src → agent-runtime/src/types}/marked-terminal.d.ts +0 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
import { createAgent as createLangChainAgent } from "langchain";
|
|
2
|
+
import type { BaseMessageLike } from "@langchain/core/messages";
|
|
3
|
+
import { DynamicStructuredTool } from "@langchain/core/tools";
|
|
4
|
+
import type { LanguageModelLike } from "@langchain/core/language_models/base";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { extractLastMessageText, stripNullishObjectFields, summarizeForLog } from "@easynet/agent-common";
|
|
7
|
+
import type { Skill } from "@easynet/agent-skill";
|
|
8
|
+
import type { BaseAgentConfig, BotContext } from "../core/context.js";
|
|
9
|
+
import {
|
|
10
|
+
buildPtcHint,
|
|
11
|
+
deriveFallbackTextFromMessages,
|
|
12
|
+
extractSchemaKeys,
|
|
13
|
+
hasToolMessages,
|
|
14
|
+
inferMemoryTypeByStructure,
|
|
15
|
+
inferMemoryTypeWithModel,
|
|
16
|
+
pickAgentConfig,
|
|
17
|
+
pickFirstAgentName,
|
|
18
|
+
printReactSteps,
|
|
19
|
+
readAppBoolean,
|
|
20
|
+
readAppString,
|
|
21
|
+
type ToolRunStats,
|
|
22
|
+
} from "./react-agent.helpers.js";
|
|
23
|
+
|
|
24
|
+
export interface ReactAgentRuntime {
|
|
25
|
+
agent: unknown;
|
|
26
|
+
run: (userMessage: string) => Promise<{ text: string; messages?: unknown }>;
|
|
27
|
+
remember: (content: string, type?: "thread" | "cross_thread" | "knowledge" | "auto") => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ReactAgentOptions {
|
|
31
|
+
agentName?: string;
|
|
32
|
+
systemPrompt?: string;
|
|
33
|
+
namespace?: string;
|
|
34
|
+
topK?: number;
|
|
35
|
+
budgetTokens?: number;
|
|
36
|
+
maxSteps?: number;
|
|
37
|
+
printSteps?: boolean;
|
|
38
|
+
fallbackText?: string;
|
|
39
|
+
commandWindowLabel?: string;
|
|
40
|
+
middleware?: unknown[];
|
|
41
|
+
autoWriteMemory?: boolean;
|
|
42
|
+
ptc?: {
|
|
43
|
+
enabled?: boolean;
|
|
44
|
+
maxRetries?: number;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type RuntimeTool = {
|
|
49
|
+
name: string;
|
|
50
|
+
description?: string;
|
|
51
|
+
schema: Record<string, unknown>;
|
|
52
|
+
invoke: (args: unknown) => Promise<unknown>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type PublishEvent = (name: string, from: string, to: string, payload?: unknown) => void;
|
|
56
|
+
|
|
57
|
+
function applyDefaultToolChoice(model: unknown): void {
|
|
58
|
+
const m = model as {
|
|
59
|
+
bindTools?: (tools: unknown, opts?: Record<string, unknown>) => unknown;
|
|
60
|
+
};
|
|
61
|
+
const orig = m.bindTools?.bind(model);
|
|
62
|
+
if (!orig) return;
|
|
63
|
+
m.bindTools = function (tools: unknown, opts?: Record<string, unknown>) {
|
|
64
|
+
return orig(tools, { ...opts, tool_choice: "auto" });
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
type ReactRuntimeConfig = {
|
|
69
|
+
agentName?: string;
|
|
70
|
+
configReact?: Record<string, unknown> & {
|
|
71
|
+
systemPrompt?: string;
|
|
72
|
+
maxSteps?: number;
|
|
73
|
+
printSteps?: boolean;
|
|
74
|
+
fallbackText?: string;
|
|
75
|
+
commandWindowLabel?: string;
|
|
76
|
+
skills?: {
|
|
77
|
+
mode?: string;
|
|
78
|
+
embedding_threshold?: number;
|
|
79
|
+
keyword_threshold?: number;
|
|
80
|
+
};
|
|
81
|
+
memory?: {
|
|
82
|
+
namespace?: string;
|
|
83
|
+
top_k?: number;
|
|
84
|
+
budget_tokens?: number;
|
|
85
|
+
auto_write?: boolean;
|
|
86
|
+
auto_write_scope?: "thread" | "cross_thread" | "knowledge" | "auto";
|
|
87
|
+
auto_write_max_chars?: number;
|
|
88
|
+
auto_write_user?: boolean;
|
|
89
|
+
auto_write_assistant?: boolean;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
namespace?: string;
|
|
93
|
+
topK?: number;
|
|
94
|
+
budgetTokens?: number;
|
|
95
|
+
systemPrompt?: string;
|
|
96
|
+
maxSteps?: number;
|
|
97
|
+
printSteps?: boolean;
|
|
98
|
+
fallbackText?: string;
|
|
99
|
+
commandWindowLabel?: string;
|
|
100
|
+
autoWriteMemory: boolean;
|
|
101
|
+
autoWriteScope: "thread" | "cross_thread" | "knowledge" | "auto";
|
|
102
|
+
autoWriteMaxChars: number;
|
|
103
|
+
autoWriteUser: boolean;
|
|
104
|
+
autoWriteAssistant: boolean;
|
|
105
|
+
ptcEnabled: boolean;
|
|
106
|
+
ptcMaxRetries: number;
|
|
107
|
+
invokeTimeoutMs: number;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
function resolveRuntimeConfig<TConfig extends BaseAgentConfig>(ctx: BotContext<TConfig>, options: ReactAgentOptions): ReactRuntimeConfig {
|
|
111
|
+
const agentName = options.agentName ?? pickFirstAgentName(ctx.config);
|
|
112
|
+
const configReact = pickAgentConfig(ctx.config, agentName);
|
|
113
|
+
const appConfig = ctx.config.app;
|
|
114
|
+
return {
|
|
115
|
+
agentName,
|
|
116
|
+
configReact,
|
|
117
|
+
namespace: options.namespace ?? configReact?.memory?.namespace,
|
|
118
|
+
topK: options.topK ?? configReact?.memory?.top_k,
|
|
119
|
+
budgetTokens: options.budgetTokens ?? configReact?.memory?.budget_tokens,
|
|
120
|
+
systemPrompt: options.systemPrompt ?? configReact?.systemPrompt,
|
|
121
|
+
maxSteps: options.maxSteps ?? configReact?.maxSteps,
|
|
122
|
+
printSteps: options.printSteps ?? configReact?.printSteps ?? readAppBoolean(appConfig, "printSteps"),
|
|
123
|
+
fallbackText: options.fallbackText ?? configReact?.fallbackText ?? readAppString(appConfig, "fallbackText"),
|
|
124
|
+
commandWindowLabel: options.commandWindowLabel ?? configReact?.commandWindowLabel ?? readAppString(appConfig, "commandWindowLabel"),
|
|
125
|
+
autoWriteMemory: options.autoWriteMemory ?? (configReact?.memory?.auto_write ?? true),
|
|
126
|
+
autoWriteScope: configReact?.memory?.auto_write_scope ?? "thread",
|
|
127
|
+
autoWriteMaxChars: configReact?.memory?.auto_write_max_chars ?? 2400,
|
|
128
|
+
autoWriteUser: configReact?.memory?.auto_write_user ?? true,
|
|
129
|
+
autoWriteAssistant: configReact?.memory?.auto_write_assistant ?? true,
|
|
130
|
+
ptcEnabled: options.ptc?.enabled ?? true,
|
|
131
|
+
ptcMaxRetries: Math.max(0, options.ptc?.maxRetries ?? 1),
|
|
132
|
+
invokeTimeoutMs: Math.max(
|
|
133
|
+
5000,
|
|
134
|
+
Number(
|
|
135
|
+
(configReact as { invokeTimeoutMs?: unknown } | undefined)?.invokeTimeoutMs
|
|
136
|
+
?? (appConfig as { invokeTimeoutMs?: unknown } | undefined)?.invokeTimeoutMs
|
|
137
|
+
?? 90000,
|
|
138
|
+
) || 90000,
|
|
139
|
+
),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function createAliasGenerator() {
|
|
144
|
+
const usedNames = new Set<string>();
|
|
145
|
+
return (raw: string, index: number): string => {
|
|
146
|
+
const segments = raw.split(".");
|
|
147
|
+
const base = segments[0] === "file" && segments.length > 2
|
|
148
|
+
? segments.slice(2).join("-")
|
|
149
|
+
: segments.pop() || `tool_${index + 1}`;
|
|
150
|
+
const normalized = base.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 48) || `tool_${index + 1}`;
|
|
151
|
+
let name = normalized;
|
|
152
|
+
let suffix = 2;
|
|
153
|
+
while (usedNames.has(name)) {
|
|
154
|
+
name = `${normalized}_${suffix}`;
|
|
155
|
+
suffix += 1;
|
|
156
|
+
}
|
|
157
|
+
usedNames.add(name);
|
|
158
|
+
return name;
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function buildAliasedTools(args: {
|
|
163
|
+
runtimeTools: RuntimeTool[];
|
|
164
|
+
publish: PublishEvent;
|
|
165
|
+
currentToolStatsRef: { current: ToolRunStats | null };
|
|
166
|
+
}): DynamicStructuredTool[] {
|
|
167
|
+
const toAlias = createAliasGenerator();
|
|
168
|
+
return args.runtimeTools.map((tool, idx) => {
|
|
169
|
+
const alias = toAlias(tool.name, idx);
|
|
170
|
+
return new DynamicStructuredTool({
|
|
171
|
+
name: alias,
|
|
172
|
+
description: tool.description ?? `Tool ${alias}`,
|
|
173
|
+
schema: tool.schema,
|
|
174
|
+
func: async (inputArgs: unknown) => {
|
|
175
|
+
const sanitizedArgs = stripNullishObjectFields((inputArgs ?? {}) as Record<string, unknown>);
|
|
176
|
+
if (args.currentToolStatsRef.current) args.currentToolStatsRef.current.invoked += 1;
|
|
177
|
+
args.publish("agent.react.tool.invoke.start", "react-agent", alias, { args: sanitizedArgs });
|
|
178
|
+
try {
|
|
179
|
+
const raw = await tool.invoke(sanitizedArgs);
|
|
180
|
+
if (args.currentToolStatsRef.current) args.currentToolStatsRef.current.succeeded += 1;
|
|
181
|
+
args.publish("agent.react.tool.invoke.done", alias, "react-agent", { result: raw, resultPreview: summarizeForLog(raw) });
|
|
182
|
+
return typeof raw === "string" ? raw : raw == null ? "" : JSON.stringify(raw);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
185
|
+
if (args.currentToolStatsRef.current) {
|
|
186
|
+
args.currentToolStatsRef.current.failed += 1;
|
|
187
|
+
args.currentToolStatsRef.current.lastError = message;
|
|
188
|
+
}
|
|
189
|
+
args.publish("agent.react.tool.invoke.error", alias, "react-agent", { error: message });
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function maybeAddActivateSkillTool<TConfig extends BaseAgentConfig>(
|
|
198
|
+
allTools: DynamicStructuredTool[],
|
|
199
|
+
ctx: BotContext<TConfig>,
|
|
200
|
+
config: ReactRuntimeConfig,
|
|
201
|
+
): void {
|
|
202
|
+
const skillsConfig = pickAgentConfig(ctx.config, config.agentName)?.skills;
|
|
203
|
+
if (!ctx.skillSet || skillsConfig?.mode !== "subagent" || ctx.skillSet.list().length === 0) return;
|
|
204
|
+
|
|
205
|
+
const skillNames = ctx.skillSet.list().map((skill: Skill) => skill.name);
|
|
206
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- zod enum tuple constraints
|
|
207
|
+
allTools.push(new DynamicStructuredTool({
|
|
208
|
+
name: "activateSkill",
|
|
209
|
+
description: `Activate a skill by name to get its detailed instructions. Available skills: ${skillNames.join(", ")}`,
|
|
210
|
+
schema: z.object({
|
|
211
|
+
skillName: z.enum(skillNames as [string, ...string[]]).describe("Name of the skill to activate"),
|
|
212
|
+
}),
|
|
213
|
+
func: async (args: { skillName: string }) => {
|
|
214
|
+
try {
|
|
215
|
+
return await ctx.skillSet!.read(args.skillName);
|
|
216
|
+
} catch (err) {
|
|
217
|
+
return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
}) as any);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function recallInjectedText<TConfig extends BaseAgentConfig>(
|
|
224
|
+
ctx: BotContext<TConfig>,
|
|
225
|
+
config: ReactRuntimeConfig,
|
|
226
|
+
userMessage: string,
|
|
227
|
+
): Promise<string> {
|
|
228
|
+
if (!config.namespace) return "";
|
|
229
|
+
const recalled = await ctx.memory.recall({
|
|
230
|
+
namespace: config.namespace,
|
|
231
|
+
query: userMessage,
|
|
232
|
+
...(typeof config.topK === "number" ? { topK: config.topK } : {}),
|
|
233
|
+
...(typeof config.budgetTokens === "number" ? { budgetTokens: config.budgetTokens } : {}),
|
|
234
|
+
});
|
|
235
|
+
return recalled.injectedText;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function buildEffectiveMessage<TConfig extends BaseAgentConfig>(
|
|
239
|
+
ctx: BotContext<TConfig>,
|
|
240
|
+
config: ReactRuntimeConfig,
|
|
241
|
+
userMessage: string,
|
|
242
|
+
injectedText: string,
|
|
243
|
+
publish: PublishEvent,
|
|
244
|
+
): Promise<string> {
|
|
245
|
+
let skillBlock = "";
|
|
246
|
+
if (ctx.skillSet) {
|
|
247
|
+
const skillMatchOptions = {
|
|
248
|
+
...(typeof config.configReact?.skills?.embedding_threshold === "number"
|
|
249
|
+
? { embeddingThreshold: config.configReact.skills.embedding_threshold }
|
|
250
|
+
: {}),
|
|
251
|
+
...(typeof config.configReact?.skills?.keyword_threshold === "number"
|
|
252
|
+
? { keywordThreshold: config.configReact.skills.keyword_threshold }
|
|
253
|
+
: {}),
|
|
254
|
+
};
|
|
255
|
+
const match = await ctx.skillSet.match(userMessage, skillMatchOptions);
|
|
256
|
+
if (match) {
|
|
257
|
+
publish("agent.react.skill.matched", "react-agent", "skill", { skill: match.skill.name, score: match.score });
|
|
258
|
+
skillBlock = `[Matched skill: ${match.skill.name}]\nFollow these instructions step by step:\n\n${match.instructions}\n\n`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const contextBlock = injectedText.trim().length > 0 ? `[Relevant memory]\n${injectedText}\n\n` : "";
|
|
263
|
+
return skillBlock + contextBlock + userMessage;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function invokeWithOptionalRecursion(
|
|
267
|
+
agent: { invoke: (input: unknown, config?: { recursionLimit?: number }) => Promise<unknown> },
|
|
268
|
+
messageContent: string,
|
|
269
|
+
conversationHistory: BaseMessageLike[],
|
|
270
|
+
maxSteps: number | undefined,
|
|
271
|
+
invokeTimeoutMs: number,
|
|
272
|
+
currentToolStatsRef: { current: ToolRunStats | null },
|
|
273
|
+
): Promise<{ out: unknown; stats: ToolRunStats | null }> {
|
|
274
|
+
const invokeInput = { messages: [...conversationHistory, { role: "user" as const, content: messageContent }] };
|
|
275
|
+
currentToolStatsRef.current = { invoked: 0, succeeded: 0, failed: 0 };
|
|
276
|
+
try {
|
|
277
|
+
const invokePromise = typeof maxSteps === "number"
|
|
278
|
+
? agent.invoke(invokeInput, { recursionLimit: maxSteps })
|
|
279
|
+
: agent.invoke(invokeInput);
|
|
280
|
+
const out = await Promise.race([
|
|
281
|
+
invokePromise,
|
|
282
|
+
new Promise<never>((_, reject) => {
|
|
283
|
+
setTimeout(() => reject(new Error(`Agent invoke timed out after ${invokeTimeoutMs}ms`)), invokeTimeoutMs);
|
|
284
|
+
}),
|
|
285
|
+
]);
|
|
286
|
+
return { out, stats: currentToolStatsRef.current };
|
|
287
|
+
} finally {
|
|
288
|
+
currentToolStatsRef.current = null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function runWithPtcIfNeeded(args: {
|
|
293
|
+
agent: { invoke: (input: unknown, config?: { recursionLimit?: number }) => Promise<unknown> };
|
|
294
|
+
effectiveMessage: string;
|
|
295
|
+
userMessage: string;
|
|
296
|
+
ptcEnabled: boolean;
|
|
297
|
+
ptcMaxRetries: number;
|
|
298
|
+
toolSignatures: string[];
|
|
299
|
+
publish: PublishEvent;
|
|
300
|
+
maxSteps?: number;
|
|
301
|
+
invokeTimeoutMs: number;
|
|
302
|
+
conversationHistory: BaseMessageLike[];
|
|
303
|
+
currentToolStatsRef: { current: ToolRunStats | null };
|
|
304
|
+
}): Promise<{ result: unknown; messages: unknown; extractedText: string; stats: ToolRunStats | null }> {
|
|
305
|
+
let { out: result, stats } = await invokeWithOptionalRecursion(
|
|
306
|
+
args.agent,
|
|
307
|
+
args.effectiveMessage,
|
|
308
|
+
args.conversationHistory,
|
|
309
|
+
args.maxSteps,
|
|
310
|
+
args.invokeTimeoutMs,
|
|
311
|
+
args.currentToolStatsRef,
|
|
312
|
+
);
|
|
313
|
+
let messages = (result as { messages?: unknown } | null | undefined)?.messages ?? result;
|
|
314
|
+
let extractedText = extractLastMessageText(result).trim();
|
|
315
|
+
|
|
316
|
+
if (!args.ptcEnabled || args.ptcMaxRetries <= 0) {
|
|
317
|
+
return { result, messages, extractedText, stats };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let retries = 0;
|
|
321
|
+
while (retries < args.ptcMaxRetries) {
|
|
322
|
+
const noToolActivity = (stats?.invoked ?? 0) === 0 && !hasToolMessages(messages);
|
|
323
|
+
const onlyToolFailure = (stats?.invoked ?? 0) > 0 && (stats?.succeeded ?? 0) === 0;
|
|
324
|
+
if (!noToolActivity && !onlyToolFailure) break;
|
|
325
|
+
|
|
326
|
+
args.publish("agent.react.ptc.retry", "react-agent", "react-agent", {
|
|
327
|
+
retry: retries + 1,
|
|
328
|
+
reason: noToolActivity ? "no_tool_activity" : "tool_failed",
|
|
329
|
+
stats,
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const ptcHint = buildPtcHint({
|
|
333
|
+
taskIntent: args.userMessage,
|
|
334
|
+
stats: stats ?? { invoked: 0, succeeded: 0, failed: 0 },
|
|
335
|
+
toolSignatures: args.toolSignatures,
|
|
336
|
+
});
|
|
337
|
+
const retried = await invokeWithOptionalRecursion(
|
|
338
|
+
args.agent,
|
|
339
|
+
`${args.effectiveMessage}\n\n${ptcHint}`,
|
|
340
|
+
args.conversationHistory,
|
|
341
|
+
args.maxSteps,
|
|
342
|
+
args.invokeTimeoutMs,
|
|
343
|
+
args.currentToolStatsRef,
|
|
344
|
+
);
|
|
345
|
+
result = retried.out;
|
|
346
|
+
stats = retried.stats;
|
|
347
|
+
messages = (result as { messages?: unknown } | null | undefined)?.messages ?? result;
|
|
348
|
+
extractedText = extractLastMessageText(result).trim();
|
|
349
|
+
retries += 1;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return { result, messages, extractedText, stats };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function updateConversationHistory(history: BaseMessageLike[], userMessage: string, text: string): void {
|
|
356
|
+
history.push({ role: "user", content: userMessage });
|
|
357
|
+
if (text.trim().length > 0) history.push({ role: "assistant", content: text });
|
|
358
|
+
if (history.length > 24) {
|
|
359
|
+
history.splice(0, history.length - 24);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function findItermCommandTool(tools: RuntimeTool[]): RuntimeTool | null {
|
|
364
|
+
return tools.find((tool) => tool.name.includes("itermRunCommandInSession")) ?? null;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function extractItermOutput(raw: unknown): {
|
|
368
|
+
output: string;
|
|
369
|
+
command: string;
|
|
370
|
+
isOutputTruncated: boolean;
|
|
371
|
+
readError: string;
|
|
372
|
+
sessionId: string;
|
|
373
|
+
totalBufferLines: number;
|
|
374
|
+
returnedLineCount: number;
|
|
375
|
+
} {
|
|
376
|
+
if (!raw || typeof raw !== "object") {
|
|
377
|
+
return {
|
|
378
|
+
output: "",
|
|
379
|
+
command: "pwd && ls -la",
|
|
380
|
+
isOutputTruncated: false,
|
|
381
|
+
readError: "",
|
|
382
|
+
sessionId: "",
|
|
383
|
+
totalBufferLines: 0,
|
|
384
|
+
returnedLineCount: 0,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
const result = (raw as { result?: unknown }).result;
|
|
388
|
+
if (!result || typeof result !== "object") {
|
|
389
|
+
return {
|
|
390
|
+
output: "",
|
|
391
|
+
command: "pwd && ls -la",
|
|
392
|
+
isOutputTruncated: false,
|
|
393
|
+
readError: "",
|
|
394
|
+
sessionId: "",
|
|
395
|
+
totalBufferLines: 0,
|
|
396
|
+
returnedLineCount: 0,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
const rec = result as Record<string, unknown>;
|
|
400
|
+
return {
|
|
401
|
+
output: typeof rec.output === "string" ? rec.output : "",
|
|
402
|
+
command: typeof rec.command === "string" && rec.command.trim() ? rec.command : "pwd && ls -la",
|
|
403
|
+
isOutputTruncated: rec.isOutputTruncated === true,
|
|
404
|
+
readError: typeof rec.readError === "string" ? rec.readError.trim() : "",
|
|
405
|
+
sessionId: typeof rec.sessionId === "string" ? rec.sessionId : "",
|
|
406
|
+
totalBufferLines:
|
|
407
|
+
typeof rec.totalBufferLines === "number" && Number.isFinite(rec.totalBufferLines)
|
|
408
|
+
? rec.totalBufferLines
|
|
409
|
+
: 0,
|
|
410
|
+
returnedLineCount:
|
|
411
|
+
typeof rec.returnedLineCount === "number" && Number.isFinite(rec.returnedLineCount)
|
|
412
|
+
? rec.returnedLineCount
|
|
413
|
+
: 0,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async function fallbackWithTargetPanelContext(args: {
|
|
418
|
+
runtimeTools: RuntimeTool[];
|
|
419
|
+
userMessage: string;
|
|
420
|
+
publish: PublishEvent;
|
|
421
|
+
}): Promise<string | null> {
|
|
422
|
+
const itermTool = findItermCommandTool(args.runtimeTools);
|
|
423
|
+
if (!itermTool) return null;
|
|
424
|
+
const fallbackCommand = "pwd && ls -la";
|
|
425
|
+
args.publish("agent.react.tool.invoke.start", "react-agent", itermTool.name, {
|
|
426
|
+
args: { command: fallbackCommand, maxOutputLines: 200, outputOffsetLines: 0 },
|
|
427
|
+
forced: true,
|
|
428
|
+
reason: "no_tool_activity",
|
|
429
|
+
});
|
|
430
|
+
try {
|
|
431
|
+
const raw = await itermTool.invoke({
|
|
432
|
+
command: fallbackCommand,
|
|
433
|
+
maxOutputLines: 200,
|
|
434
|
+
outputOffsetLines: 0,
|
|
435
|
+
});
|
|
436
|
+
args.publish("agent.react.tool.invoke.done", itermTool.name, "react-agent", {
|
|
437
|
+
forced: true,
|
|
438
|
+
reason: "no_tool_activity",
|
|
439
|
+
resultPreview: summarizeForLog(raw),
|
|
440
|
+
});
|
|
441
|
+
const parsed = extractItermOutput(raw);
|
|
442
|
+
if (!parsed.output.trim()) {
|
|
443
|
+
if (parsed.readError) {
|
|
444
|
+
return `Executed \`${parsed.command}\` in target panel, but reading terminal output failed: ${parsed.readError}`;
|
|
445
|
+
}
|
|
446
|
+
const diagnostics = [
|
|
447
|
+
parsed.sessionId ? `sessionId=${parsed.sessionId}` : "",
|
|
448
|
+
`bufferLines=${parsed.totalBufferLines}`,
|
|
449
|
+
`returnedLines=${parsed.returnedLineCount}`,
|
|
450
|
+
].filter(Boolean).join(", ");
|
|
451
|
+
return `Executed \`${parsed.command}\` in target panel, but no output was returned (${diagnostics}).`;
|
|
452
|
+
}
|
|
453
|
+
return [
|
|
454
|
+
"I collected context from target panel and used it as the source of truth.",
|
|
455
|
+
"",
|
|
456
|
+
`Command: \`${parsed.command}\``,
|
|
457
|
+
"",
|
|
458
|
+
"```",
|
|
459
|
+
parsed.output,
|
|
460
|
+
"```",
|
|
461
|
+
parsed.isOutputTruncated ? "\nNote: output is truncated; run paginated reads for full coverage." : "",
|
|
462
|
+
].join("\n");
|
|
463
|
+
} catch (err) {
|
|
464
|
+
args.publish("agent.react.tool.invoke.error", itermTool.name, "react-agent", {
|
|
465
|
+
forced: true,
|
|
466
|
+
reason: "no_tool_activity",
|
|
467
|
+
error: err instanceof Error ? err.message : String(err),
|
|
468
|
+
});
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
async function autoWriteMemoryIfNeeded<TConfig extends BaseAgentConfig>(
|
|
474
|
+
ctx: BotContext<TConfig>,
|
|
475
|
+
config: ReactRuntimeConfig,
|
|
476
|
+
userMessage: string,
|
|
477
|
+
text: string,
|
|
478
|
+
publish: PublishEvent,
|
|
479
|
+
): Promise<void> {
|
|
480
|
+
if (!config.namespace || !config.autoWriteMemory) return;
|
|
481
|
+
|
|
482
|
+
const scope = config.autoWriteScope === "auto"
|
|
483
|
+
? inferMemoryTypeByStructure(`${userMessage}\n${text}`)
|
|
484
|
+
: config.autoWriteScope;
|
|
485
|
+
const writeItems: string[] = [];
|
|
486
|
+
if (config.autoWriteUser && userMessage.trim().length > 0) writeItems.push(`[user]\n${userMessage.trim()}`);
|
|
487
|
+
if (config.autoWriteAssistant && text.trim().length > 0) writeItems.push(`[assistant]\n${text.trim()}`);
|
|
488
|
+
|
|
489
|
+
const content = writeItems.join("\n\n").slice(0, Math.max(0, config.autoWriteMaxChars));
|
|
490
|
+
if (!content.length) return;
|
|
491
|
+
|
|
492
|
+
try {
|
|
493
|
+
publish("agent.react.memory.write.start", "react-agent", "memory", { type: scope, auto: true });
|
|
494
|
+
await ctx.memory.memorize(config.namespace, scope, content);
|
|
495
|
+
publish("agent.react.memory.write.done", "memory", "react-agent", { type: scope, auto: true });
|
|
496
|
+
} catch (err) {
|
|
497
|
+
publish("agent.react.memory.write.error", "memory", "react-agent", {
|
|
498
|
+
auto: true,
|
|
499
|
+
error: err instanceof Error ? err.message : String(err),
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export { printReactSteps } from "./react-agent.helpers.js";
|
|
505
|
+
|
|
506
|
+
export function createReactAgent<TConfig extends BaseAgentConfig>(ctx: BotContext<TConfig>, options: ReactAgentOptions = {}) {
|
|
507
|
+
const publish: PublishEvent = (name, from, to, payload) => {
|
|
508
|
+
ctx.events.publish({ name, from, to, payload });
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const runtimeTools = ctx.tools as unknown as RuntimeTool[];
|
|
512
|
+
const config = resolveRuntimeConfig(ctx, options);
|
|
513
|
+
const currentToolStatsRef = { current: null as ToolRunStats | null };
|
|
514
|
+
|
|
515
|
+
const aliasedTools = buildAliasedTools({ runtimeTools, publish, currentToolStatsRef });
|
|
516
|
+
const allTools = [...aliasedTools];
|
|
517
|
+
maybeAddActivateSkillTool(allTools, ctx, config);
|
|
518
|
+
const toolSignatures = aliasedTools.map((tool) => {
|
|
519
|
+
const keys = extractSchemaKeys((tool as unknown as { schema?: unknown }).schema);
|
|
520
|
+
return keys.length > 0 ? `${tool.name}(${keys.join(",")})` : `${tool.name}(...)`;
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
applyDefaultToolChoice(ctx.llm);
|
|
524
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- heavily overloaded createAgent
|
|
525
|
+
const agent = createLangChainAgent({
|
|
526
|
+
model: ctx.llm as LanguageModelLike,
|
|
527
|
+
tools: allTools,
|
|
528
|
+
systemPrompt: config.systemPrompt,
|
|
529
|
+
...(options.middleware?.length ? { middleware: options.middleware } : {}),
|
|
530
|
+
} as any) as unknown as { invoke: (input: unknown, cfg?: { recursionLimit?: number }) => Promise<unknown> };
|
|
531
|
+
|
|
532
|
+
const conversationHistory: BaseMessageLike[] = [];
|
|
533
|
+
|
|
534
|
+
return {
|
|
535
|
+
agent,
|
|
536
|
+
async run(userMessage: string): Promise<{ text: string; messages?: unknown }> {
|
|
537
|
+
publish("agent.react.run.start", "user", "react-agent", { userMessage });
|
|
538
|
+
const injectedText = await recallInjectedText(ctx, config, userMessage);
|
|
539
|
+
const effectiveMessage = await buildEffectiveMessage(ctx, config, userMessage, injectedText, publish);
|
|
540
|
+
const runResult = await runWithPtcIfNeeded({
|
|
541
|
+
agent,
|
|
542
|
+
effectiveMessage,
|
|
543
|
+
userMessage,
|
|
544
|
+
ptcEnabled: config.ptcEnabled,
|
|
545
|
+
ptcMaxRetries: config.ptcMaxRetries,
|
|
546
|
+
toolSignatures,
|
|
547
|
+
publish,
|
|
548
|
+
maxSteps: typeof config.maxSteps === "number" ? config.maxSteps : 8,
|
|
549
|
+
invokeTimeoutMs: config.invokeTimeoutMs,
|
|
550
|
+
conversationHistory,
|
|
551
|
+
currentToolStatsRef,
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
if (config.printSteps === true) printReactSteps(runResult.messages);
|
|
555
|
+
const noToolActivity = (runResult.stats?.invoked ?? 0) === 0 && !hasToolMessages(runResult.messages);
|
|
556
|
+
const forcedText = noToolActivity
|
|
557
|
+
? await fallbackWithTargetPanelContext({ runtimeTools, userMessage, publish })
|
|
558
|
+
: null;
|
|
559
|
+
const text = forcedText ?? (runResult.extractedText || (config.fallbackText
|
|
560
|
+
? deriveFallbackTextFromMessages((Array.isArray(runResult.messages) ? runResult.messages : []) as BaseMessageLike[], {
|
|
561
|
+
fallbackText: config.fallbackText,
|
|
562
|
+
commandWindowLabel: config.commandWindowLabel,
|
|
563
|
+
})
|
|
564
|
+
: runResult.extractedText));
|
|
565
|
+
|
|
566
|
+
updateConversationHistory(conversationHistory, userMessage, text);
|
|
567
|
+
await autoWriteMemoryIfNeeded(ctx, config, userMessage, text, publish);
|
|
568
|
+
publish("agent.react.run.done", "react-agent", "user", { text });
|
|
569
|
+
return { text, messages: runResult.messages };
|
|
570
|
+
},
|
|
571
|
+
async remember(content: string, type: "thread" | "cross_thread" | "knowledge" | "auto" = "auto") {
|
|
572
|
+
const resolvedType = type === "auto"
|
|
573
|
+
? (await inferMemoryTypeWithModel(ctx.llm as LanguageModelLike, content)) ?? inferMemoryTypeByStructure(content)
|
|
574
|
+
: type;
|
|
575
|
+
publish("agent.react.memory.write.start", "react-agent", "memory", { type: resolvedType });
|
|
576
|
+
if (!config.namespace) {
|
|
577
|
+
publish("agent.react.memory.write.skipped", "react-agent", "memory", { reason: "namespace_not_configured" });
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
await ctx.memory.memorize(config.namespace, resolvedType, content);
|
|
581
|
+
publish("agent.react.memory.write.done", "memory", "react-agent", { type: resolvedType });
|
|
582
|
+
},
|
|
583
|
+
};
|
|
584
|
+
}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { createReactAgent } from "./react-agent.js";
|
|
9
9
|
import { createDeepAgent } from "./deep-agent.js";
|
|
10
|
-
import { malformedToolCallMiddleware } from "
|
|
11
|
-
import type { BotContext } from "
|
|
10
|
+
import { malformedToolCallMiddleware } from "../middleware/malformed-tool-call-middleware.js";
|
|
11
|
+
import type { BotContext } from "../core/context.js";
|
|
12
12
|
|
|
13
13
|
export interface SubAgentOptions {
|
|
14
14
|
/** Agent kind: react or deep. */
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DEEP, REACT, type AgentKind } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export interface ParsedArgs {
|
|
4
|
+
kind: AgentKind;
|
|
5
|
+
query?: string;
|
|
6
|
+
explicitKind: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function parseArgs(argv: string[] = process.argv.slice(2)): ParsedArgs {
|
|
10
|
+
const first = argv[0]?.toLowerCase();
|
|
11
|
+
const explicitKind = first === REACT || first === DEEP;
|
|
12
|
+
const kind: AgentKind = first === DEEP ? DEEP : REACT;
|
|
13
|
+
const query = explicitKind ? (argv[1] ?? undefined) : (first ?? undefined);
|
|
14
|
+
return { kind, query, explicitKind };
|
|
15
|
+
}
|