@easynet/agent-runtime 1.0.2 → 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.
Files changed (205) hide show
  1. package/.github/workflows/ci.yml +9 -24
  2. package/.github/workflows/release.yml +38 -26
  3. package/agent-runtime/.github/workflows/ci.yml +69 -0
  4. package/agent-runtime/.github/workflows/release.yml +118 -0
  5. package/agent-runtime/.releaserc.cjs +26 -0
  6. package/agent-runtime/config/agent.deep.yaml +25 -0
  7. package/agent-runtime/config/agent.react.yaml +24 -0
  8. package/agent-runtime/example/basic-usage.ts +49 -0
  9. package/agent-runtime/package-lock.json +7740 -0
  10. package/agent-runtime/package.json +49 -0
  11. package/agent-runtime/pnpm-lock.yaml +3712 -0
  12. package/agent-runtime/scripts/resolve-deps.js +54 -0
  13. package/agent-runtime/src/agents/deep-agent.ts +165 -0
  14. package/agent-runtime/src/agents/react-agent.helpers.ts +227 -0
  15. package/agent-runtime/src/agents/react-agent.ts +584 -0
  16. package/{src → agent-runtime/src/agents}/sub-agent.ts +2 -2
  17. package/agent-runtime/src/cli/args.ts +15 -0
  18. package/agent-runtime/src/cli/event-listener.ts +162 -0
  19. package/agent-runtime/src/cli/interactive.ts +144 -0
  20. package/agent-runtime/src/cli/runtime.ts +31 -0
  21. package/agent-runtime/src/cli/spinner.ts +23 -0
  22. package/agent-runtime/src/cli/terminal-render.ts +322 -0
  23. package/agent-runtime/src/cli/types.ts +33 -0
  24. package/agent-runtime/src/cli.ts +134 -0
  25. package/agent-runtime/src/config/helpers.ts +179 -0
  26. package/agent-runtime/src/config/index.ts +245 -0
  27. package/agent-runtime/src/config/types.ts +62 -0
  28. package/agent-runtime/src/core/context.ts +266 -0
  29. package/agent-runtime/src/index.ts +55 -0
  30. package/agent-runtime/tsconfig.json +18 -0
  31. package/apps/imessagebot/README.md +38 -0
  32. package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/README.md +33 -0
  33. package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package-lock.json +15257 -0
  34. package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package.json +55 -0
  35. package/apps/imessagebot/config/agents/deep/agent.yaml +31 -0
  36. package/apps/imessagebot/config/agents/react/agent.yaml +58 -0
  37. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/README.md +33 -0
  38. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package-lock.json +15457 -0
  39. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package.json +55 -0
  40. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/README.md +33 -0
  41. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package-lock.json +15257 -0
  42. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package.json +62 -0
  43. package/apps/imessagebot/config/agents/shared/memory.yaml +31 -0
  44. package/apps/imessagebot/config/agents/shared/model.yaml +23 -0
  45. package/apps/imessagebot/config/agents/shared/tool.yaml +13 -0
  46. package/apps/imessagebot/config/app.yaml +14 -0
  47. package/apps/imessagebot/package-lock.json +53695 -0
  48. package/apps/imessagebot/package.json +41 -0
  49. package/apps/imessagebot/pnpm-lock.yaml +1589 -0
  50. package/apps/imessagebot/scripts/resolve-deps.js +41 -0
  51. package/apps/imessagebot/scripts/test-llm.mjs +27 -0
  52. package/apps/imessagebot/scripts/validate-tools-config.mjs +174 -0
  53. package/apps/imessagebot/src/config.ts +76 -0
  54. package/apps/imessagebot/src/context.ts +35 -0
  55. package/apps/imessagebot/src/index.ts +17 -0
  56. package/apps/imessagebot/tsconfig.json +18 -0
  57. package/apps/itermbot/.github/workflows/ci.yml +61 -0
  58. package/apps/itermbot/.github/workflows/release.yml +80 -0
  59. package/apps/itermbot/.releaserc.cjs +26 -0
  60. package/apps/itermbot/README.md +82 -0
  61. package/apps/itermbot/config/app.yaml +29 -0
  62. package/apps/itermbot/config/tsconfig.json +18 -0
  63. package/apps/itermbot/macos_disk_usage_agent_plan.md +244 -0
  64. package/apps/itermbot/package-lock.json +53697 -0
  65. package/apps/itermbot/package.json +57 -0
  66. package/apps/itermbot/pnpm-lock.yaml +3966 -0
  67. package/apps/itermbot/scripts/patch-buildin-cache.sh +25 -0
  68. package/apps/itermbot/scripts/resolve-deps.js +41 -0
  69. package/apps/itermbot/scripts/test-llm.mjs +32 -0
  70. package/apps/itermbot/skills/command-explain-and-guard/SKILL.md +39 -0
  71. package/apps/itermbot/skills/command-explain-and-guard/handler.js +86 -0
  72. package/apps/itermbot/skills/disk-usage-investigate/SKILL.md +44 -0
  73. package/apps/itermbot/skills/disk-usage-investigate/handler.js +12 -0
  74. package/apps/itermbot/skills/gpu-ssh-monitor/SKILL.md +64 -0
  75. package/apps/itermbot/skills/repo-triage/SKILL.md +40 -0
  76. package/apps/itermbot/skills/repo-triage/handler.js +56 -0
  77. package/apps/itermbot/skills/test-failure-diagnose/SKILL.md +43 -0
  78. package/apps/itermbot/skills/test-failure-diagnose/handler.js +107 -0
  79. package/apps/itermbot/src/config.ts +95 -0
  80. package/apps/itermbot/src/context.ts +35 -0
  81. package/apps/itermbot/src/index.ts +223 -0
  82. package/apps/itermbot/src/iterm/session-hint.ts +40 -0
  83. package/apps/itermbot/src/iterm/target-routing.ts +419 -0
  84. package/apps/itermbot/src/startup/colors.ts +317 -0
  85. package/apps/itermbot/src/startup/diagnostics.ts +97 -0
  86. package/apps/itermbot/src/startup/ui.ts +141 -0
  87. package/config/agent.deep.yaml +25 -0
  88. package/config/agent.react.yaml +24 -0
  89. package/dist/agents/deep-agent.d.ts +37 -0
  90. package/dist/agents/deep-agent.d.ts.map +1 -0
  91. package/dist/agents/deep-agent.js +115 -0
  92. package/dist/agents/deep-agent.js.map +1 -0
  93. package/dist/agents/react-agent.d.ts +40 -0
  94. package/dist/agents/react-agent.d.ts.map +1 -0
  95. package/dist/agents/react-agent.helpers.d.ts +40 -0
  96. package/dist/agents/react-agent.helpers.d.ts.map +1 -0
  97. package/dist/agents/react-agent.helpers.js +196 -0
  98. package/dist/agents/react-agent.helpers.js.map +1 -0
  99. package/dist/agents/react-agent.js +400 -0
  100. package/dist/agents/react-agent.js.map +1 -0
  101. package/dist/agents/sub-agent.d.ts +34 -0
  102. package/dist/agents/sub-agent.d.ts.map +1 -0
  103. package/dist/agents/sub-agent.js +53 -0
  104. package/dist/agents/sub-agent.js.map +1 -0
  105. package/dist/cli/args.d.ts +8 -0
  106. package/dist/cli/args.d.ts.map +1 -0
  107. package/dist/cli/args.js +9 -0
  108. package/dist/cli/args.js.map +1 -0
  109. package/dist/cli/event-listener.d.ts +3 -0
  110. package/dist/cli/event-listener.d.ts.map +1 -0
  111. package/dist/cli/event-listener.js +131 -0
  112. package/dist/cli/event-listener.js.map +1 -0
  113. package/dist/cli/interactive.d.ts +4 -0
  114. package/dist/cli/interactive.d.ts.map +1 -0
  115. package/dist/cli/interactive.js +118 -0
  116. package/dist/cli/interactive.js.map +1 -0
  117. package/dist/cli/runtime.d.ts +8 -0
  118. package/dist/cli/runtime.d.ts.map +1 -0
  119. package/dist/cli/runtime.js +27 -0
  120. package/dist/cli/runtime.js.map +1 -0
  121. package/dist/cli/spinner.d.ts +2 -0
  122. package/dist/cli/spinner.d.ts.map +1 -0
  123. package/dist/cli/spinner.js +22 -0
  124. package/dist/cli/spinner.js.map +1 -0
  125. package/dist/cli/terminal-render.d.ts +7 -0
  126. package/dist/cli/terminal-render.d.ts.map +1 -0
  127. package/dist/cli/terminal-render.js +282 -0
  128. package/dist/cli/terminal-render.js.map +1 -0
  129. package/dist/cli/types.d.ts +29 -0
  130. package/dist/cli/types.d.ts.map +1 -0
  131. package/dist/cli/types.js +3 -0
  132. package/dist/cli/types.js.map +1 -0
  133. package/dist/cli.d.ts +4 -41
  134. package/dist/cli.d.ts.map +1 -1
  135. package/dist/cli.js +84 -588
  136. package/dist/cli.js.map +1 -1
  137. package/dist/config/helpers.d.ts +6 -0
  138. package/dist/config/helpers.d.ts.map +1 -0
  139. package/dist/config/helpers.js +164 -0
  140. package/dist/config/helpers.js.map +1 -0
  141. package/dist/config/index.d.ts +15 -0
  142. package/dist/config/index.d.ts.map +1 -0
  143. package/dist/config/index.js +160 -0
  144. package/dist/config/index.js.map +1 -0
  145. package/dist/config/types.d.ts +57 -0
  146. package/dist/config/types.d.ts.map +1 -0
  147. package/dist/config/types.js +2 -0
  148. package/dist/config/types.js.map +1 -0
  149. package/dist/context.d.ts +8 -69
  150. package/dist/context.d.ts.map +1 -1
  151. package/dist/context.js +44 -24
  152. package/dist/context.js.map +1 -1
  153. package/dist/core/context.d.ts +66 -0
  154. package/dist/core/context.d.ts.map +1 -0
  155. package/dist/core/context.js +149 -0
  156. package/dist/core/context.js.map +1 -0
  157. package/dist/deep-agent.d.ts +5 -2
  158. package/dist/deep-agent.d.ts.map +1 -1
  159. package/dist/deep-agent.js +44 -11
  160. package/dist/deep-agent.js.map +1 -1
  161. package/dist/index.d.ts +6 -6
  162. package/dist/index.d.ts.map +1 -1
  163. package/dist/index.js +6 -6
  164. package/dist/index.js.map +1 -1
  165. package/dist/middleware/malformed-tool-call-middleware.d.ts +8 -0
  166. package/dist/middleware/malformed-tool-call-middleware.d.ts.map +1 -0
  167. package/dist/middleware/malformed-tool-call-middleware.js +191 -0
  168. package/dist/middleware/malformed-tool-call-middleware.js.map +1 -0
  169. package/dist/react-agent.d.ts +2 -2
  170. package/dist/react-agent.d.ts.map +1 -1
  171. package/dist/react-agent.js +28 -9
  172. package/dist/react-agent.js.map +1 -1
  173. package/package.json +1 -1
  174. package/scripts/resolve-deps.js +54 -0
  175. package/src/agents/deep-agent.ts +165 -0
  176. package/src/agents/react-agent.helpers.ts +227 -0
  177. package/src/agents/react-agent.ts +584 -0
  178. package/src/agents/sub-agent.ts +82 -0
  179. package/src/cli/args.ts +15 -0
  180. package/src/cli/event-listener.ts +162 -0
  181. package/src/cli/interactive.ts +144 -0
  182. package/src/cli/runtime.ts +31 -0
  183. package/src/cli/spinner.ts +23 -0
  184. package/src/cli/terminal-render.ts +322 -0
  185. package/src/cli/types.ts +33 -0
  186. package/src/cli.ts +91 -702
  187. package/src/config/helpers.ts +179 -0
  188. package/src/config/index.ts +245 -0
  189. package/src/config/types.ts +62 -0
  190. package/src/core/context.ts +266 -0
  191. package/src/index.ts +13 -11
  192. package/src/middleware/malformed-tool-call-middleware.ts +239 -0
  193. package/src/types/markdown-it-terminal.d.ts +4 -0
  194. package/src/types/marked-terminal.d.ts +16 -0
  195. package/dist/config.d.ts +0 -86
  196. package/dist/config.d.ts.map +0 -1
  197. package/dist/config.js +0 -84
  198. package/dist/config.js.map +0 -1
  199. package/src/config.ts +0 -177
  200. package/src/context.ts +0 -247
  201. package/src/deep-agent.ts +0 -104
  202. package/src/react-agent.ts +0 -576
  203. /package/{src → agent-runtime/src/middleware}/malformed-tool-call-middleware.ts +0 -0
  204. /package/{src → agent-runtime/src/types}/markdown-it-terminal.d.ts +0 -0
  205. /package/{src → agent-runtime/src/types}/marked-terminal.d.ts +0 -0
@@ -0,0 +1,266 @@
1
+ import { createAgentModelRegistry } from "@easynet/agent-model";
2
+ import { createAgentMemory as createMemoryClient } from "@easynet/agent-memory/internal";
3
+ import { createLangChainAgentTools } from "@easynet/agent-tool/api";
4
+ import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
5
+ import {
6
+ createAgentEventBus,
7
+ loadYamlFile,
8
+ asObject,
9
+ type AgentEventBus,
10
+ } from "@easynet/agent-common";
11
+ import type { SkillConfig } from "@easynet/agent-skill";
12
+ import { createSkills, type EmbedFn, type SkillSet } from "@easynet/agent-skill";
13
+ import { getSkillsConfig } from "../config/index.js";
14
+ import type { BaseAgentConfig, ConfigApi } from "../config/types.js";
15
+ import { resolve, dirname } from "node:path";
16
+ export type { BaseAgentConfig } from "../config/types.js";
17
+ export type { ConfigApi } from "../config/types.js";
18
+
19
+ export type AgentName = string;
20
+
21
+ export type BotTool = ReturnType<typeof createLangChainAgentTools>[number];
22
+
23
+ /** Minimal memory interface used by the runtime. */
24
+ export interface AgentMemory {
25
+ memorize(namespace: string, type: string, contentOrUrl: string | URL, metadata?: Record<string, unknown>): Promise<unknown>;
26
+ recall(req: { namespace: string; query?: string; types?: string[]; topK?: number; budgetTokens?: number }): Promise<{ items: unknown[]; injectedText: string; traceId: string }>;
27
+ deleteByNamespace(namespace: string): Promise<void>;
28
+ getByKey(namespace: string, key: string): Promise<unknown>;
29
+ deleteByKey(namespace: string, key: string): Promise<void>;
30
+ }
31
+
32
+ export interface BotContext<TConfig extends BaseAgentConfig = BaseAgentConfig, TTool = BotTool> {
33
+ llm: BaseChatModel;
34
+ memory: AgentMemory;
35
+ tools: TTool[];
36
+ config: TConfig;
37
+ events: AgentEventBus;
38
+ skillSet?: SkillSet;
39
+ }
40
+
41
+ export interface CreateContextOptions<TTool = BotTool> {
42
+ configPath?: string;
43
+ agentName?: AgentName;
44
+ events?: AgentEventBus;
45
+ llm?: BaseChatModel;
46
+ memory?: AgentMemory;
47
+ tools?: TTool[];
48
+ }
49
+
50
+ function publishEvent(
51
+ events: AgentEventBus,
52
+ name: string,
53
+ from: string,
54
+ to: string,
55
+ payload?: unknown
56
+ ): void {
57
+ events.publish({ name, from, to, payload });
58
+ }
59
+
60
+ function injectSkillsRuntimeConfig(
61
+ config: BaseAgentConfig,
62
+ agentName: string,
63
+ values: Pick<SkillConfig, "mode" | "inject_metadata" | "embedding_threshold" | "keyword_threshold">
64
+ ): void {
65
+ const agentConfig = config.app?.agent?.[agentName];
66
+ if (!agentConfig) return;
67
+ agentConfig.skills = {
68
+ ...(agentConfig.skills ?? {}),
69
+ ...values,
70
+ };
71
+ }
72
+
73
+ export function createContextBuilders<TConfig extends BaseAgentConfig>(params: {
74
+ configApi: ConfigApi<TConfig>;
75
+ }) {
76
+ const {
77
+ loadAgentConfig,
78
+ getModelsConfigPath,
79
+ getMemoryConfigPath,
80
+ getToolConfigPath,
81
+ } = params.configApi;
82
+
83
+ const pickDefaultAgentName = (config: TConfig): AgentName | undefined => {
84
+ const names = Object.keys(config.app?.agent ?? {});
85
+ return names[0];
86
+ };
87
+
88
+ async function getAgentLlm(
89
+ config: TConfig,
90
+ events: AgentEventBus,
91
+ agentName?: AgentName
92
+ ): Promise<BaseChatModel> {
93
+ const modelsPath = getModelsConfigPath(config, agentName ?? pickDefaultAgentName(config));
94
+ publishEvent(events, "llm.init.start", "app", "llm", { configPath: modelsPath });
95
+ const modelRegistry = await createAgentModelRegistry(modelsPath);
96
+ const llm = await modelRegistry.getChatModel();
97
+ publishEvent(events, "llm.init.done", "llm", "app");
98
+ return llm;
99
+ }
100
+
101
+ async function getAgentMemory(
102
+ config: TConfig,
103
+ events: AgentEventBus,
104
+ agentName?: AgentName
105
+ ): Promise<AgentMemory> {
106
+ const memoryPath = getMemoryConfigPath(config, agentName ?? pickDefaultAgentName(config));
107
+ publishEvent(events, "memory.init.start", "app", "memory", { configPath: memoryPath });
108
+ const memory = await createMemoryClient({ configPath: memoryPath });
109
+ publishEvent(events, "memory.init.done", "memory", "app");
110
+ return memory;
111
+ }
112
+
113
+ async function getAgentTools(
114
+ config: TConfig,
115
+ events: AgentEventBus,
116
+ agentName?: AgentName
117
+ ): Promise<BotTool[]> {
118
+ const toolPath = getToolConfigPath(config, agentName ?? pickDefaultAgentName(config));
119
+ publishEvent(events, "tools.init.start", "app", "tools", { configPath: toolPath });
120
+ const tools = createLangChainAgentTools({ configFilePath: toolPath });
121
+ publishEvent(events, "tools.init.done", "tools", "app", { count: tools.length });
122
+ return tools;
123
+ }
124
+
125
+ async function createAgent(options: CreateContextOptions<BotTool> = {}): Promise<BotContext<TConfig, BotTool>> {
126
+ const config = await loadAgentConfig(options.configPath);
127
+ const agentName = options.agentName ?? pickDefaultAgentName(config);
128
+ const events = options.events ?? createAgentEventBus();
129
+ publishEvent(events, "agent.context.init.start", "app", "runtime");
130
+
131
+ const [llm, memory, tools] = await Promise.all([
132
+ options.llm ?? getAgentLlm(config, events, agentName),
133
+ options.memory ?? getAgentMemory(config, events, agentName),
134
+ options.tools ?? getAgentTools(config, events, agentName),
135
+ ]);
136
+
137
+ const appConfig = config as unknown as BaseAgentConfig;
138
+ const skillSet = await initializeSkills({
139
+ appConfig,
140
+ config,
141
+ agentName,
142
+ events,
143
+ getModelsConfigPath: (c, name) => getModelsConfigPath(c as TConfig, name),
144
+ pickDefaultAgentName: (c) => pickDefaultAgentName(c as TConfig),
145
+ });
146
+
147
+ publishEvent(events, "agent.context.init.done", "runtime", "app");
148
+ return { llm, memory, tools, config, events, skillSet };
149
+ }
150
+
151
+ async function createBotContext(
152
+ configPath?: string,
153
+ agentName?: AgentName
154
+ ): Promise<BotContext<TConfig, BotTool>> {
155
+ return createAgent({ configPath, agentName });
156
+ }
157
+
158
+ return {
159
+ getAgentLlm,
160
+ getAgentMemory,
161
+ getAgentTools,
162
+ // Backward-compatible aliases
163
+ createAgentLlm: getAgentLlm,
164
+ createAgentMemory: getAgentMemory,
165
+ createAgentTools: getAgentTools,
166
+ createAgent,
167
+ createBotContext,
168
+ };
169
+ }
170
+
171
+ async function initializeSkills(input: {
172
+ appConfig: BaseAgentConfig;
173
+ config: BaseAgentConfig;
174
+ agentName?: AgentName;
175
+ events: AgentEventBus;
176
+ getModelsConfigPath: (config: BaseAgentConfig, agentName?: AgentName) => string;
177
+ pickDefaultAgentName: (config: BaseAgentConfig) => AgentName | undefined;
178
+ }): Promise<SkillSet | undefined> {
179
+ const skillsConfig = await getSkillsConfig(input.appConfig, input.agentName);
180
+ if (!skillsConfig) return undefined;
181
+ publishEvent(input.events, "skills.init.start", "app", "skills", { path: skillsConfig.path });
182
+ const embedFn = buildSkillsEmbedFn(input.config, input.agentName, input.getModelsConfigPath, input.pickDefaultAgentName);
183
+ const skillsDir = await resolveSkillsDir(skillsConfig.path);
184
+ const skillSet = await createSkills({ path: skillsDir, embed: embedFn });
185
+ const mode = applySkillsPromptAndMetadata(input.appConfig, input.agentName, skillsConfig, skillSet);
186
+ publishEvent(input.events, "skills.init.done", "skills", "app", { count: skillSet.list().length, mode });
187
+ return skillSet;
188
+ }
189
+
190
+ function buildSkillsEmbedFn(
191
+ config: BaseAgentConfig,
192
+ agentName: AgentName | undefined,
193
+ getModelsConfigPath: (config: BaseAgentConfig, agentName?: AgentName) => string,
194
+ pickDefaultAgentName: (config: BaseAgentConfig) => AgentName | undefined,
195
+ ): EmbedFn | undefined {
196
+ const modelsPath = getModelsConfigPath(config, agentName ?? pickDefaultAgentName(config));
197
+ const lazyEmbed: EmbedFn = async (input: string | string[]) => {
198
+ const modelRegistry = await createAgentModelRegistry(modelsPath);
199
+ const embeddings = modelRegistry.getEmbeddingModel();
200
+ if (Array.isArray(input)) return embeddings.embedDocuments(input);
201
+ const one = await embeddings.embedQuery(input);
202
+ return [one];
203
+ };
204
+ return lazyEmbed;
205
+ }
206
+
207
+ function applySkillsPromptAndMetadata(
208
+ appConfig: BaseAgentConfig,
209
+ agentName: AgentName | undefined,
210
+ skillsConfig: SkillConfig,
211
+ skillSet: SkillSet,
212
+ ): string {
213
+ const mode = skillsConfig.mode ?? "prompt";
214
+ if (agentName) {
215
+ injectSkillsRuntimeConfig(appConfig, agentName, {
216
+ mode: skillsConfig.mode,
217
+ inject_metadata: skillsConfig.inject_metadata,
218
+ embedding_threshold: skillsConfig.embedding_threshold,
219
+ keyword_threshold: skillsConfig.keyword_threshold,
220
+ });
221
+ }
222
+ if (mode === "prompt" && skillsConfig.inject_metadata !== false) {
223
+ injectSkillsPromptSection(appConfig, agentName, skillSet.promptSection());
224
+ }
225
+ return mode;
226
+ }
227
+
228
+ function injectSkillsPromptSection(
229
+ appConfig: BaseAgentConfig,
230
+ agentName: AgentName | undefined,
231
+ section: string | undefined,
232
+ ): void {
233
+ if (!section || !agentName) return;
234
+ const agentConfig = appConfig.app?.agent?.[agentName];
235
+ if (!agentConfig) return;
236
+ const currentPrompt = agentConfig.systemPrompt ?? "";
237
+ agentConfig.systemPrompt = currentPrompt.trimEnd() + "\n\n" + section;
238
+ }
239
+
240
+ export interface AppContextBuilders {
241
+ getAgentLlm: (config: BaseAgentConfig, events: AgentEventBus, agentName?: AgentName) => Promise<BaseChatModel>;
242
+ getAgentMemory: (config: BaseAgentConfig, events: AgentEventBus, agentName?: AgentName) => Promise<AgentMemory>;
243
+ getAgentTools: (config: BaseAgentConfig, events: AgentEventBus, agentName?: AgentName) => Promise<BotTool[]>;
244
+ createAgentLlm: (config: BaseAgentConfig, events: AgentEventBus, agentName?: AgentName) => Promise<BaseChatModel>;
245
+ createAgentMemory: (config: BaseAgentConfig, events: AgentEventBus, agentName?: AgentName) => Promise<AgentMemory>;
246
+ createAgentTools: (config: BaseAgentConfig, events: AgentEventBus, agentName?: AgentName) => Promise<BotTool[]>;
247
+ createAgent: (options?: CreateContextOptions<BotTool>) => Promise<BotContext>;
248
+ createBotContext: (configPath?: string, agentName?: AgentName) => Promise<BotContext>;
249
+ }
250
+
251
+ /**
252
+ * Resolve skills directory path from either a YAML config file or a direct path.
253
+ * If the path ends with .yaml/.yml, loads the YAML and extracts the `path:` field.
254
+ */
255
+ async function resolveSkillsDir(skillsPath: string): Promise<string> {
256
+ if (/\.ya?ml$/i.test(skillsPath)) {
257
+ const raw = await loadYamlFile<{ path?: string }>(skillsPath);
258
+ const spec = asObject((raw as { spec?: unknown } | undefined)?.spec);
259
+ const pathRef = typeof spec?.path === "string" ? spec.path : raw?.path;
260
+ if (!pathRef) {
261
+ throw new Error(`skills config missing "path" field: ${skillsPath}`);
262
+ }
263
+ return resolve(dirname(skillsPath), pathRef);
264
+ }
265
+ return skillsPath;
266
+ }
package/src/index.ts CHANGED
@@ -1,37 +1,39 @@
1
1
  export {
2
2
  createContextBuilders,
3
- createAppContext,
4
3
  type AppContextBuilders,
5
- type BaseAppConfig,
4
+ type BaseAgentConfig,
6
5
  type BotContext,
7
6
  type BotTool,
8
7
  type CreateContextOptions,
9
8
  type ConfigApi,
10
- } from "./context.js";
9
+ } from "./core/context.js";
11
10
 
12
11
  export {
13
12
  createReactAgent,
14
13
  printReactSteps,
15
14
  type ReactAgentOptions,
16
15
  type ReactAgentRuntime,
17
- } from "./react-agent.js";
16
+ } from "./agents/react-agent.js";
18
17
 
19
18
  export {
20
19
  createDeepAgent,
21
20
  type DeepAgentOptions,
22
21
  type DeepAgentRuntime,
23
- } from "./deep-agent.js";
22
+ } from "./agents/deep-agent.js";
24
23
 
25
24
  export {
26
25
  createConfigApi,
27
- loadAppConfig,
26
+ createRuntimeConfig,
27
+ loadAgentConfig,
28
28
  getModelsConfigPath,
29
29
  getMemoryConfigPath,
30
30
  getToolConfigPath,
31
- type AppConfig,
32
- type AppConfigDefaults,
31
+ resolveDefaultAgentName,
32
+ type CreateRuntimeConfigOptions,
33
+ type AgentRuntimeConfig,
34
+ type AgentConfigDefaults,
33
35
  type AgentProfileConfig,
34
- } from "./config.js";
36
+ } from "./config/index.js";
35
37
 
36
38
  export {
37
39
  runAppCli,
@@ -43,11 +45,11 @@ export {
43
45
  export {
44
46
  malformedToolCallMiddleware,
45
47
  type MalformedToolCallMiddlewareConfig,
46
- } from "./malformed-tool-call-middleware.js";
48
+ } from "./middleware/malformed-tool-call-middleware.js";
47
49
 
48
50
  export {
49
51
  createSubAgentRunner,
50
52
  type SubAgentRunner,
51
53
  type SubAgentOptions,
52
54
  type SubAgentResult,
53
- } from "./sub-agent.js";
55
+ } from "./agents/sub-agent.js";
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Middleware that recovers from malformed tool call errors produced by OSS LLMs.
3
+ *
4
+ * When models like Ollama-hosted open-source LLMs mix chain-of-thought reasoning
5
+ * into tool call JSON arguments, the server returns a 500 parsing error.
6
+ * This middleware catches those errors, extracts the valid JSON from the raw text,
7
+ * and constructs a proper AIMessage so the agent loop can continue.
8
+ */
9
+ import { createMiddleware } from "langchain";
10
+ import { AIMessage } from "@langchain/core/messages";
11
+
12
+ export interface MalformedToolCallMiddlewareConfig {
13
+ /** Maximum retry attempts after JSON extraction fails. Default: 1 */
14
+ maxRetries?: number;
15
+ /** Log recovery attempts to stderr. Default: true */
16
+ verbose?: boolean;
17
+ }
18
+
19
+ interface OllamaToolCallError {
20
+ rawText: string;
21
+ }
22
+
23
+ interface ExtractedToolCall {
24
+ toolName: string;
25
+ args: Record<string, unknown>;
26
+ id?: string;
27
+ }
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Helpers
31
+ // ---------------------------------------------------------------------------
32
+
33
+ /**
34
+ * Detect Ollama "500 error parsing tool call" errors and extract the raw text.
35
+ * Error format: `500 error parsing tool call: raw='<raw_text>', err=invalid character...`
36
+ */
37
+ function parseOllamaToolCallError(err: Error): OllamaToolCallError | null {
38
+ const msg = err.message;
39
+ const rawMatch = msg.match(/error parsing tool call.*?raw='(.+?)',\s*err=/s);
40
+ if (!rawMatch) return null;
41
+ return { rawText: rawMatch[1] };
42
+ }
43
+
44
+ /**
45
+ * Extract valid JSON object from a string that may contain leading non-JSON text.
46
+ * Uses brace-depth counting to find matching `{...}` blocks.
47
+ */
48
+ function extractJsonObject(text: string): Record<string, unknown> | null {
49
+ for (let i = 0; i < text.length; i++) {
50
+ if (text[i] !== "{") continue;
51
+
52
+ let depth = 0;
53
+ let inString = false;
54
+ let escape = false;
55
+ let end = -1;
56
+
57
+ for (let j = i; j < text.length; j++) {
58
+ const ch = text[j];
59
+ if (escape) {
60
+ escape = false;
61
+ continue;
62
+ }
63
+ if (ch === "\\") {
64
+ escape = true;
65
+ continue;
66
+ }
67
+ if (ch === '"') {
68
+ inString = !inString;
69
+ continue;
70
+ }
71
+ if (inString) continue;
72
+ if (ch === "{") depth++;
73
+ else if (ch === "}") {
74
+ depth--;
75
+ if (depth === 0) {
76
+ end = j;
77
+ break;
78
+ }
79
+ }
80
+ }
81
+ if (end === -1) continue;
82
+
83
+ const candidate = text.slice(i, end + 1);
84
+ try {
85
+ const parsed = JSON.parse(candidate) as unknown;
86
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
87
+ return parsed as Record<string, unknown>;
88
+ }
89
+ } catch {
90
+ // not valid JSON from this position, try next '{'
91
+ }
92
+ }
93
+ return null;
94
+ }
95
+
96
+ /**
97
+ * Match extracted JSON keys against available tool schemas to infer the tool name.
98
+ */
99
+ function matchToolByArgs(
100
+ args: Record<string, unknown>,
101
+ tools: ReadonlyArray<{ name: string; schema?: unknown }>,
102
+ ): string | null {
103
+ const argKeys = new Set(Object.keys(args));
104
+ let bestMatch: string | null = null;
105
+ let bestScore = 0;
106
+
107
+ for (const tool of tools) {
108
+ const schema = tool.schema as
109
+ | { properties?: Record<string, unknown> }
110
+ | { shape?: Record<string, unknown> }
111
+ | null
112
+ | undefined;
113
+ const propKeys =
114
+ (schema && "properties" in schema && schema.properties
115
+ ? Object.keys(schema.properties)
116
+ : schema && "shape" in schema && schema.shape
117
+ ? Object.keys(schema.shape)
118
+ : null);
119
+ if (!propKeys) continue;
120
+
121
+ let score = 0;
122
+ for (const key of argKeys) {
123
+ if (propKeys.includes(key)) score++;
124
+ }
125
+ if (score > bestScore) {
126
+ bestScore = score;
127
+ bestMatch = tool.name;
128
+ }
129
+ }
130
+ return bestMatch;
131
+ }
132
+
133
+ function generateToolCallId(): string {
134
+ return `call_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
135
+ }
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Middleware
139
+ // ---------------------------------------------------------------------------
140
+
141
+ export function malformedToolCallMiddleware(
142
+ config?: MalformedToolCallMiddlewareConfig,
143
+ ) {
144
+ const maxRetries = config?.maxRetries ?? 1;
145
+ const verbose = config?.verbose ?? true;
146
+
147
+ const log = verbose
148
+ ? (msg: string) => console.error(`[malformedToolCallRecovery] ${msg}`)
149
+ : () => {};
150
+
151
+ function tryExtract(
152
+ rawText: string,
153
+ tools: ReadonlyArray<{ name: string; schema?: unknown }>,
154
+ ): ExtractedToolCall | null {
155
+ const args = extractJsonObject(rawText);
156
+ if (!args) return null;
157
+
158
+ const toolName =
159
+ matchToolByArgs(args, tools) ?? tools[0]?.name ?? "unknown_tool";
160
+ return { toolName, args, id: generateToolCallId() };
161
+ }
162
+
163
+ function buildAIMessage(tc: ExtractedToolCall): AIMessage {
164
+ return new AIMessage({
165
+ content: "",
166
+ tool_calls: [{ name: tc.toolName, args: tc.args, id: tc.id }],
167
+ });
168
+ }
169
+
170
+ return createMiddleware({
171
+ name: "malformedToolCallRecovery",
172
+
173
+ wrapModelCall: async (request, handler) => {
174
+ // --- Normal call ---
175
+ try {
176
+ return await handler(request);
177
+ } catch (error) {
178
+ const err = error instanceof Error ? error : new Error(String(error));
179
+ const parsed = parseOllamaToolCallError(err);
180
+ if (!parsed) throw err; // not our error type
181
+
182
+ log("Caught malformed tool call error. Attempting JSON extraction...");
183
+
184
+ // Cast tools to access name/schema for matching
185
+ const req = request as unknown as Record<string, unknown>;
186
+ const tools = (req.tools ?? []) as ReadonlyArray<{
187
+ name: string;
188
+ schema?: unknown;
189
+ }>;
190
+ const systemPrompt = req.systemPrompt as string | undefined;
191
+
192
+ // --- Strategy A: extract JSON directly from the error text ---
193
+ const extracted = tryExtract(parsed.rawText, tools);
194
+ if (extracted) {
195
+ log(`Extracted JSON for tool "${extracted.toolName}".`);
196
+ return buildAIMessage(extracted);
197
+ }
198
+
199
+ // --- Strategy B: retry with enhanced prompt ---
200
+ log(`JSON extraction failed. Retrying (${maxRetries} attempts)...`);
201
+
202
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
203
+ try {
204
+ const retryRequest = {
205
+ ...request,
206
+ systemPrompt:
207
+ (systemPrompt ?? "") +
208
+ "\n\nCRITICAL: Tool call arguments MUST be a single valid JSON object. " +
209
+ "Do NOT include any reasoning text before or after the JSON.",
210
+ };
211
+ return await handler(retryRequest);
212
+ } catch (retryError) {
213
+ const retryErr =
214
+ retryError instanceof Error
215
+ ? retryError
216
+ : new Error(String(retryError));
217
+ const retryParsed = parseOllamaToolCallError(retryErr);
218
+ if (retryParsed) {
219
+ const retryExtracted = tryExtract(retryParsed.rawText, tools);
220
+ if (retryExtracted) {
221
+ log(`Extracted JSON on retry ${attempt + 1} for tool "${retryExtracted.toolName}".`);
222
+ return buildAIMessage(retryExtracted);
223
+ }
224
+ }
225
+ log(`Retry ${attempt + 1}/${maxRetries} failed.`);
226
+ }
227
+ }
228
+
229
+ // --- All strategies exhausted ---
230
+ log("All recovery attempts failed. Returning error to agent loop.");
231
+ return new AIMessage({
232
+ content:
233
+ "Tool call failed due to malformed output from the model. " +
234
+ "Please try a simpler command or rephrase your request.",
235
+ });
236
+ }
237
+ },
238
+ });
239
+ }
@@ -0,0 +1,4 @@
1
+ declare module "markdown-it-terminal" {
2
+ const plugin: (...args: unknown[]) => void;
3
+ export default plugin;
4
+ }
@@ -0,0 +1,16 @@
1
+ declare module "marked-terminal" {
2
+ import type { MarkedExtension } from "marked";
3
+
4
+ export interface MarkedTerminalOptions {
5
+ [key: string]: unknown;
6
+ }
7
+
8
+ export interface HighlightOptions {
9
+ [key: string]: unknown;
10
+ }
11
+
12
+ export function markedTerminal(
13
+ options?: MarkedTerminalOptions,
14
+ highlightOptions?: HighlightOptions,
15
+ ): MarkedExtension;
16
+ }
package/dist/config.d.ts DELETED
@@ -1,86 +0,0 @@
1
- import type { ConfigApi, BaseAppConfig } from "./context.js";
2
- /** Resolved skills configuration for an agent. */
3
- export interface SkillsConfig {
4
- path: string;
5
- mode?: "prompt" | "subagent";
6
- inject_metadata?: boolean;
7
- embedding_threshold?: number;
8
- keyword_threshold?: number;
9
- }
10
- export type AgentProfileConfig = {
11
- path?: string;
12
- models?: {
13
- path?: string;
14
- };
15
- llm?: {
16
- path?: string;
17
- };
18
- memory?: {
19
- path?: string;
20
- namespace?: string;
21
- top_k?: number;
22
- budget_tokens?: number;
23
- auto_write?: boolean;
24
- auto_write_scope?: "thread" | "cross_thread" | "knowledge" | "auto";
25
- auto_write_max_chars?: number;
26
- auto_write_user?: boolean;
27
- auto_write_assistant?: boolean;
28
- };
29
- tools?: {
30
- path?: string;
31
- development?: string;
32
- production?: string;
33
- };
34
- skills?: {
35
- path?: string;
36
- mode?: "prompt" | "subagent";
37
- inject_metadata?: boolean;
38
- embedding_threshold?: number;
39
- keyword_threshold?: number;
40
- };
41
- system_prompt?: string;
42
- max_steps?: number;
43
- print_steps?: boolean;
44
- fallback_text?: string;
45
- command_window_label?: string;
46
- recursion_limit?: number;
47
- memories_path?: string;
48
- };
49
- export interface AppConfig extends BaseAppConfig {
50
- app?: {
51
- name?: string;
52
- env?: string;
53
- memory?: {
54
- namespace_mode?: "thread" | "fixed";
55
- namespace?: string;
56
- };
57
- agent?: Record<string, AgentProfileConfig>;
58
- };
59
- }
60
- export interface AppConfigDefaults {
61
- /** Default models config path, e.g. "config/agents/shared/models.yaml" */
62
- modelsPath?: string;
63
- /** Default memory config path, e.g. "config/agents/shared/memory.yaml" */
64
- memoryPath?: string;
65
- /** Default tool config path, e.g. "config/agents/shared/tool.yaml" */
66
- toolPath?: string;
67
- /** Default tool config path for development */
68
- toolDevPath?: string;
69
- /** Default tool config path for production */
70
- toolProdPath?: string;
71
- }
72
- export declare function loadAppConfig(configPath?: string): Promise<AppConfig>;
73
- export declare function getModelsConfigPath(config?: AppConfig, agentName?: string, defaults?: AppConfigDefaults): string;
74
- export declare function getMemoryConfigPath(config?: AppConfig, agentName?: string, defaults?: AppConfigDefaults): string;
75
- export declare function getToolConfigPath(config: AppConfig | undefined, agentName: string | undefined, defaults?: AppConfigDefaults): string;
76
- /**
77
- * Create a ConfigApi suitable for `createContextBuilders`.
78
- * Apps can supply defaults (e.g. custom tool paths) or use the built-in ones.
79
- */
80
- export declare function createConfigApi(defaults?: AppConfigDefaults): ConfigApi<AppConfig>;
81
- /**
82
- * Resolve the SkillsConfig for a given agent, if configured.
83
- * Resolves relative `path` to absolute using APP_ROOT.
84
- */
85
- export declare function getSkillsConfig(config: AppConfig | undefined, agentName?: string): SkillsConfig | undefined;
86
- //# sourceMappingURL=config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}