@tyvm/knowhow 0.0.21 → 0.0.23

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 (172) hide show
  1. package/package.json +3 -1
  2. package/src/agents/base/base.ts +16 -7
  3. package/src/agents/configurable/ConfigAgent.ts +5 -3
  4. package/src/agents/developer/developer.ts +3 -4
  5. package/src/agents/index.ts +26 -2
  6. package/src/agents/patcher/patcher.ts +3 -5
  7. package/src/agents/researcher/researcher.ts +3 -4
  8. package/src/agents/tools/agentCall.ts +5 -2
  9. package/src/agents/tools/executeScript/README.md +78 -0
  10. package/src/agents/tools/executeScript/definition.ts +73 -0
  11. package/src/agents/tools/executeScript/examples/dependency-injection-validation.ts +272 -0
  12. package/src/agents/tools/executeScript/examples/quick-test.ts +74 -0
  13. package/src/agents/tools/executeScript/examples/serialization-test.ts +321 -0
  14. package/src/agents/tools/executeScript/examples/test-runner.ts +197 -0
  15. package/src/agents/tools/executeScript/index.ts +93 -0
  16. package/src/agents/tools/index.ts +1 -0
  17. package/src/agents/tools/list.ts +2 -1
  18. package/src/agents/vim/vim.ts +3 -4
  19. package/src/ai.ts +2 -1
  20. package/src/chat.ts +4 -2
  21. package/src/cli.ts +7 -15
  22. package/src/clients/index.ts +23 -9
  23. package/src/dataset/diffs/test.ts +2 -1
  24. package/src/index.ts +3 -3
  25. package/src/services/AgentService.ts +9 -10
  26. package/src/services/EventService.ts +0 -2
  27. package/src/services/GitHub.ts +0 -1
  28. package/src/services/KnowhowClient.ts +0 -3
  29. package/src/services/Mcp.ts +0 -2
  30. package/src/services/S3.ts +0 -1
  31. package/src/services/Tools.ts +63 -8
  32. package/src/services/flags.ts +0 -1
  33. package/src/services/index.ts +56 -0
  34. package/src/services/modules/index.ts +53 -0
  35. package/src/{modules → services/modules}/types.ts +16 -5
  36. package/src/services/script-execution/SandboxContext.ts +278 -0
  37. package/src/services/script-execution/ScriptExecutor.ts +339 -0
  38. package/src/services/script-execution/ScriptPolicy.ts +236 -0
  39. package/src/services/script-execution/ScriptTracer.ts +249 -0
  40. package/src/services/script-execution/types.ts +134 -0
  41. package/src/worker.ts +3 -3
  42. package/tests/integration/fileblocks/readwrite.test.ts +2 -1
  43. package/tests/integration/patching.test.ts +5 -5
  44. package/ts_build/src/agents/base/base.d.ts +9 -4
  45. package/ts_build/src/agents/base/base.js +7 -10
  46. package/ts_build/src/agents/base/base.js.map +1 -1
  47. package/ts_build/src/agents/configurable/ConfigAgent.d.ts +2 -2
  48. package/ts_build/src/agents/configurable/ConfigAgent.js +2 -2
  49. package/ts_build/src/agents/configurable/ConfigAgent.js.map +1 -1
  50. package/ts_build/src/agents/developer/developer.d.ts +2 -3
  51. package/ts_build/src/agents/developer/developer.js +3 -4
  52. package/ts_build/src/agents/developer/developer.js.map +1 -1
  53. package/ts_build/src/agents/index.d.ts +11 -2
  54. package/ts_build/src/agents/index.js +19 -3
  55. package/ts_build/src/agents/index.js.map +1 -1
  56. package/ts_build/src/agents/patcher/patcher.d.ts +2 -3
  57. package/ts_build/src/agents/patcher/patcher.js +3 -4
  58. package/ts_build/src/agents/patcher/patcher.js.map +1 -1
  59. package/ts_build/src/agents/researcher/researcher.d.ts +2 -3
  60. package/ts_build/src/agents/researcher/researcher.js +3 -4
  61. package/ts_build/src/agents/researcher/researcher.js.map +1 -1
  62. package/ts_build/src/agents/tools/agentCall.js +4 -4
  63. package/ts_build/src/agents/tools/agentCall.js.map +1 -1
  64. package/ts_build/src/agents/tools/executeScript/definition.d.ts +2 -0
  65. package/ts_build/src/agents/tools/executeScript/definition.js +70 -0
  66. package/ts_build/src/agents/tools/executeScript/definition.js.map +1 -0
  67. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.d.ts +18 -0
  68. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js +192 -0
  69. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js.map +1 -0
  70. package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +3 -0
  71. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +65 -0
  72. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +1 -0
  73. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +15 -0
  74. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +266 -0
  75. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +1 -0
  76. package/ts_build/src/agents/tools/executeScript/examples/simple-example.d.ts +20 -0
  77. package/ts_build/src/agents/tools/executeScript/examples/simple-example.js +35 -0
  78. package/ts_build/src/agents/tools/executeScript/examples/simple-example.js.map +1 -0
  79. package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +4 -0
  80. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +198 -0
  81. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +1 -0
  82. package/ts_build/src/agents/tools/executeScript/handler.d.ts +27 -0
  83. package/ts_build/src/agents/tools/executeScript/handler.js +64 -0
  84. package/ts_build/src/agents/tools/executeScript/handler.js.map +1 -0
  85. package/ts_build/src/agents/tools/executeScript/index.d.ts +27 -0
  86. package/ts_build/src/agents/tools/executeScript/index.js +70 -0
  87. package/ts_build/src/agents/tools/executeScript/index.js.map +1 -0
  88. package/ts_build/src/agents/tools/executeScript.d.ts +29 -0
  89. package/ts_build/src/agents/tools/executeScript.js +124 -0
  90. package/ts_build/src/agents/tools/executeScript.js.map +1 -0
  91. package/ts_build/src/agents/tools/index.d.ts +1 -0
  92. package/ts_build/src/agents/tools/index.js +1 -0
  93. package/ts_build/src/agents/tools/index.js.map +1 -1
  94. package/ts_build/src/agents/tools/list.js +2 -0
  95. package/ts_build/src/agents/tools/list.js.map +1 -1
  96. package/ts_build/src/agents/vim/vim.d.ts +2 -3
  97. package/ts_build/src/agents/vim/vim.js +3 -4
  98. package/ts_build/src/agents/vim/vim.js.map +1 -1
  99. package/ts_build/src/ai.js +2 -1
  100. package/ts_build/src/ai.js.map +1 -1
  101. package/ts_build/src/chat.js +10 -9
  102. package/ts_build/src/chat.js.map +1 -1
  103. package/ts_build/src/cli.js +12 -19
  104. package/ts_build/src/cli.js.map +1 -1
  105. package/ts_build/src/clients/index.d.ts +9 -2
  106. package/ts_build/src/clients/index.js +17 -4
  107. package/ts_build/src/clients/index.js.map +1 -1
  108. package/ts_build/src/dataset/diffs/test.js +2 -1
  109. package/ts_build/src/dataset/diffs/test.js.map +1 -1
  110. package/ts_build/src/index.js +10 -10
  111. package/ts_build/src/index.js.map +1 -1
  112. package/ts_build/src/services/AgentService.d.ts +7 -3
  113. package/ts_build/src/services/AgentService.js +11 -10
  114. package/ts_build/src/services/AgentService.js.map +1 -1
  115. package/ts_build/src/services/EventService.d.ts +0 -1
  116. package/ts_build/src/services/EventService.js +1 -2
  117. package/ts_build/src/services/EventService.js.map +1 -1
  118. package/ts_build/src/services/GitHub.d.ts +0 -1
  119. package/ts_build/src/services/GitHub.js +1 -2
  120. package/ts_build/src/services/GitHub.js.map +1 -1
  121. package/ts_build/src/services/KnowhowClient.d.ts +0 -1
  122. package/ts_build/src/services/KnowhowClient.js +1 -2
  123. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  124. package/ts_build/src/services/Mcp.d.ts +0 -1
  125. package/ts_build/src/services/Mcp.js +1 -2
  126. package/ts_build/src/services/Mcp.js.map +1 -1
  127. package/ts_build/src/services/S3.d.ts +0 -1
  128. package/ts_build/src/services/S3.js +1 -2
  129. package/ts_build/src/services/S3.js.map +1 -1
  130. package/ts_build/src/services/Tools.d.ts +22 -1
  131. package/ts_build/src/services/Tools.js +32 -6
  132. package/ts_build/src/services/Tools.js.map +1 -1
  133. package/ts_build/src/services/flags.d.ts +0 -1
  134. package/ts_build/src/services/flags.js +1 -2
  135. package/ts_build/src/services/flags.js.map +1 -1
  136. package/ts_build/src/services/index.d.ts +25 -0
  137. package/ts_build/src/services/index.js +42 -1
  138. package/ts_build/src/services/index.js.map +1 -1
  139. package/ts_build/src/services/modules/example-usage.d.ts +11 -0
  140. package/ts_build/src/services/modules/example-usage.js +43 -0
  141. package/ts_build/src/services/modules/example-usage.js.map +1 -0
  142. package/ts_build/src/services/modules/index.d.ts +4 -0
  143. package/ts_build/src/services/modules/index.js +44 -0
  144. package/ts_build/src/services/modules/index.js.map +1 -0
  145. package/ts_build/src/services/modules/types.d.ts +47 -0
  146. package/ts_build/src/services/modules/types.js +3 -0
  147. package/ts_build/src/services/modules/types.js.map +1 -0
  148. package/ts_build/src/services/script-execution/SandboxContext.d.ts +34 -0
  149. package/ts_build/src/services/script-execution/SandboxContext.js +186 -0
  150. package/ts_build/src/services/script-execution/SandboxContext.js.map +1 -0
  151. package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +17 -0
  152. package/ts_build/src/services/script-execution/ScriptExecutor.js +211 -0
  153. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +1 -0
  154. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +27 -0
  155. package/ts_build/src/services/script-execution/ScriptPolicy.js +150 -0
  156. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +1 -0
  157. package/ts_build/src/services/script-execution/ScriptTracer.d.ts +19 -0
  158. package/ts_build/src/services/script-execution/ScriptTracer.js +186 -0
  159. package/ts_build/src/services/script-execution/ScriptTracer.js.map +1 -0
  160. package/ts_build/src/services/script-execution/types.d.ts +108 -0
  161. package/ts_build/src/services/script-execution/types.js +3 -0
  162. package/ts_build/src/services/script-execution/types.js.map +1 -0
  163. package/ts_build/src/services/singletons.d.ts +17 -0
  164. package/ts_build/src/services/singletons.js +28 -0
  165. package/ts_build/src/services/singletons.js.map +1 -0
  166. package/ts_build/src/worker.js +4 -3
  167. package/ts_build/src/worker.js.map +1 -1
  168. package/ts_build/tests/integration/fileblocks/readwrite.test.js +10 -9
  169. package/ts_build/tests/integration/fileblocks/readwrite.test.js.map +1 -1
  170. package/ts_build/tests/integration/patching.test.js +9 -10
  171. package/ts_build/tests/integration/patching.test.js.map +1 -1
  172. package/src/modules/index.ts +0 -37
@@ -1,6 +1,6 @@
1
1
  import * as fs from "fs";
2
2
  import { Message } from "../../clients/types";
3
- import { BaseAgent } from "../base/base";
3
+ import { AgentContext, BaseAgent } from "../base/base";
4
4
  import { readFile, writeFile, execAsync, mkdir } from "../../utils";
5
5
  import { openai, singlePrompt, Models } from "../../ai";
6
6
  import { BASE_PROMPT } from "../base/prompt";
@@ -11,8 +11,8 @@ export class VimAgent extends BaseAgent {
11
11
 
12
12
  toolPath = ".knowhow/tools/vim";
13
13
 
14
- constructor() {
15
- super();
14
+ constructor(context: AgentContext) {
15
+ super(context);
16
16
  // this.disableTool("patchFile");
17
17
  this.setModelPreferences([
18
18
  { model: Models.anthropic.Sonnet4, provider: "anthropic" },
@@ -150,4 +150,3 @@ export class VimAgent extends BaseAgent {
150
150
  }
151
151
  }
152
152
 
153
- export const Vimmer = new VimAgent();
package/src/ai.ts CHANGED
@@ -11,7 +11,7 @@ const config = getConfigSync();
11
11
  const OPENAI_KEY = process.env.OPENAI_KEY;
12
12
 
13
13
  import { Models } from "./types";
14
- import { Agents } from "./services";
14
+ import { services } from "./services";
15
15
  export { Models };
16
16
 
17
17
  export const openai = new OpenAI({
@@ -20,6 +20,7 @@ export const openai = new OpenAI({
20
20
  });
21
21
 
22
22
  export async function singlePrompt(userPrompt: string, model = "", agent = "") {
23
+ const { Agents } = services();
23
24
  if (agent) {
24
25
  const agentConfig = await Agents.getAgent(agent);
25
26
  if (!agentConfig) {
package/src/chat.ts CHANGED
@@ -13,10 +13,10 @@ import { Marked } from "./utils";
13
13
  import { ask } from "./utils";
14
14
  import { Plugins } from "./plugins/plugins";
15
15
  import { queryEmbedding, getConfiguredEmbeddingMap } from "./embeddings";
16
- import { Agents } from "./services/AgentService";
16
+ import { services } from "./services/";
17
17
  import { FlagsService } from "./services/flags";
18
18
  import { IAgent } from "./agents/interface";
19
- import { Clients, Message } from "./clients";
19
+ import { Message } from "./clients";
20
20
  import { recordAudio, voiceToText } from "./microphone";
21
21
  import { Models } from "./ai";
22
22
  import { BaseAgent } from "./agents";
@@ -129,6 +129,7 @@ The user has asked:
129
129
  { role: "user", content: gptPrompt },
130
130
  ] as Message[];
131
131
 
132
+ const { Clients } = services();
132
133
  const response = await Clients.createCompletion(provider, {
133
134
  messages: thread,
134
135
  model,
@@ -182,6 +183,7 @@ export async function chatLoop<E extends GptQuestionEmbedding>(
182
183
  embeddings: Embeddable<E>[],
183
184
  plugins: string[] = []
184
185
  ) {
186
+ const { Agents, Clients } = services();
185
187
  let activeAgent = Agents.getAgent("Developer") as BaseAgent;
186
188
  let provider = "openai" as keyof typeof Clients.clients;
187
189
  let model = ChatModelDefaults[provider];
package/src/cli.ts CHANGED
@@ -4,32 +4,24 @@ import { generate, embed, upload, chat } from "./index";
4
4
  import { init } from "./config";
5
5
 
6
6
  import { download, purge } from ".";
7
- import { Agents } from "./services/AgentService";
8
- import { Researcher } from "./agents/researcher/researcher";
9
- import { Patcher } from "./agents/patcher/patcher";
10
- import { Vimmer } from "./agents/vim/vim";
11
- import { Developer } from "./agents/developer/developer";
12
- import { Tools } from "./services";
13
7
  import { includedTools } from "./agents/tools/list";
14
- import * as allTools from "./agents/tools/index";
15
- import { Mcp } from "./services/Mcp";
8
+ import * as allTools from "./agents/tools";
9
+ import { services } from "./services";
16
10
  import { login } from "./login";
17
11
  import { worker } from "./worker";
18
- import { Clients } from "./clients";
12
+ import { agents } from "./agents";
19
13
 
20
14
  const command = process.argv[2];
21
15
 
22
16
  async function main() {
17
+ const { Tools, Agents, Mcp, Clients } = services();
18
+ const { Researcher, Developer, Patcher } = agents();
23
19
  Agents.registerAgent(Researcher);
24
20
  Agents.registerAgent(Patcher);
25
21
  Agents.registerAgent(Developer);
26
- Agents.loadAgentsFromConfig();
27
- Tools.addTools(includedTools);
22
+ Agents.loadAgentsFromConfig(services());
28
23
 
29
- const toolFunctions = Object.entries(allTools)
30
- .filter(([_, value]) => typeof value === 'function')
31
- .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
32
- Tools.addFunctions(toolFunctions);
24
+ Tools.defineTools(includedTools, allTools);
33
25
 
34
26
  await Mcp.connectToConfigured(Tools);
35
27
  await Clients.registerConfiguredModels();
@@ -153,13 +153,24 @@ export class AIClient {
153
153
  );
154
154
  }
155
155
 
156
- private providerHasModel(provider: string, model: string): boolean {
156
+ providerHasModel(provider: string, model: string): boolean {
157
157
  const models = this.clientModels[provider];
158
158
  if (!models) return false;
159
159
  return models.includes(model);
160
160
  }
161
161
 
162
- private detectProviderModel(provider: string, model?: string) {
162
+ findModel(modelPrefix: string) {
163
+ for (const provider of Object.keys(this.clientModels)) {
164
+ const models = this.clientModels[provider];
165
+ const foundModel = models.find((m) => m.startsWith(modelPrefix));
166
+ if (foundModel) {
167
+ return { provider, model: foundModel };
168
+ }
169
+ }
170
+ return undefined;
171
+ }
172
+
173
+ detectProviderModel(provider: string, model?: string) {
163
174
  if (this.providerHasModel(provider, model)) {
164
175
  return { provider, model };
165
176
  }
@@ -170,18 +181,21 @@ export class AIClient {
170
181
  const inferredProvider = split[0];
171
182
  const inferredModel = split.slice(1).join("/");
172
183
 
184
+ // Exact match
173
185
  if (this.providerHasModel(inferredProvider, inferredModel)) {
174
186
  return { provider: inferredProvider, model: inferredModel };
175
187
  }
176
- }
177
188
 
178
- const providers = Object.keys(this.clientModels);
179
- const foundProvider = providers.find((p) =>
180
- this.providerHasModel(p, model)
181
- );
189
+ // Starts with match
190
+ const foundBySplit = this.findModel(inferredModel);
191
+ if (foundBySplit) {
192
+ return foundBySplit;
193
+ }
194
+ }
182
195
 
183
- if (foundProvider) {
184
- return { provider: foundProvider, model };
196
+ const foundByModel = this.findModel(model);
197
+ if (foundByModel) {
198
+ return foundByModel;
185
199
  }
186
200
 
187
201
  return { provider, model };
@@ -15,6 +15,7 @@ import {
15
15
  hunksToPatch,
16
16
  } from "../../agents/tools/patch";
17
17
  import { md5Hash } from "../../hashes";
18
+ import { services } from "../../services";
18
19
  const dataset = [];
19
20
 
20
21
  class PatchTestAgent extends PatchingAgent {
@@ -44,7 +45,7 @@ class PatchTestAgent extends PatchingAgent {
44
45
  }
45
46
 
46
47
  async function testDataset() {
47
- const patchAgent = new PatchTestAgent();
48
+ const patchAgent = new PatchTestAgent(services());
48
49
 
49
50
  let successCount = 0;
50
51
  let attempts = 0;
package/src/index.ts CHANGED
@@ -42,10 +42,8 @@ import { abort } from "process";
42
42
  import { chatLoop } from "./chat";
43
43
  import { convertToText } from "./conversion";
44
44
  import { Plugins } from "./plugins/plugins";
45
- import { AwsS3 } from "./services/S3";
46
- import { GitHub } from "./services/GitHub";
47
45
  import { knowhowMcpClient } from "./services/Mcp";
48
- import { knowhowApiClient } from "./services/KnowhowClient";
46
+ import { services } from "./services/";
49
47
  import { Models } from "./types";
50
48
 
51
49
  export * as clients from "./clients";
@@ -96,6 +94,7 @@ export async function purge(globPath: string) {
96
94
 
97
95
  export async function upload() {
98
96
  const config = await getConfig();
97
+ const { AwsS3, knowhowApiClient } = services();
99
98
 
100
99
  for (const source of config.embedSources) {
101
100
  const bucketName = source.remote;
@@ -324,6 +323,7 @@ export async function chat() {
324
323
 
325
324
  export async function download() {
326
325
  const config = await getConfig();
326
+ const { AwsS3, GitHub, knowhowApiClient } = services();
327
327
 
328
328
  for (const source of config.embedSources) {
329
329
  const { remote, remoteType } = source;
@@ -1,18 +1,19 @@
1
1
  import { getConfigSync } from "../config";
2
2
  import { IAgent } from "../agents/interface";
3
+ import { EventService } from "./EventService";
4
+ import { ToolsService } from "./Tools";
3
5
  import { ConfigAgent } from "../agents/configurable/ConfigAgent";
4
- import { Events } from "./EventService";
5
- import { Tools } from "./Tools";
6
+ import { AgentContext } from "src/agents/base/base";
6
7
 
7
8
  export class AgentService {
8
9
  private agents: Map<string, IAgent> = new Map();
9
10
 
10
- constructor() {
11
+ constructor(private tools: ToolsService, private events: EventService) {
11
12
  this.wireUp();
12
13
  }
13
14
 
14
15
  public wireUp() {
15
- Tools.addTool({
16
+ this.tools.addTool({
16
17
  type: "function",
17
18
  function: {
18
19
  name: "agentCall",
@@ -35,13 +36,13 @@ export class AgentService {
35
36
  },
36
37
  },
37
38
  });
38
- Events.on("agents:register", (data) => {
39
+ this.events.on("agents:register", (data) => {
39
40
  console.log(`Agent registered: ${data.name}`);
40
41
  const { name, agent } = data;
41
42
  this.registerAgentByName(name, agent);
42
43
  });
43
44
 
44
- Events.on("agents:call", (data) => {
45
+ this.events.on("agents:call", (data) => {
45
46
  console.log(`Agent called: ${data.name}`);
46
47
  const { name, query, resolve, reject } = data;
47
48
  this.callAgent(name, query).then(resolve).catch(reject);
@@ -77,12 +78,12 @@ export class AgentService {
77
78
  });
78
79
  }
79
80
 
80
- public loadAgentsFromConfig() {
81
+ public loadAgentsFromConfig(context: AgentContext) {
81
82
  const config = getConfigSync();
82
83
  const agents = config.agents || [];
83
84
 
84
85
  for (const agent of agents) {
85
- this.registerAgent(new ConfigAgent(agent));
86
+ this.registerAgent(new ConfigAgent(agent, context));
86
87
  }
87
88
  }
88
89
 
@@ -94,5 +95,3 @@ export class AgentService {
94
95
  return agent.call(query);
95
96
  }
96
97
  }
97
-
98
- export const Agents = new AgentService();
@@ -16,5 +16,3 @@ export class EventService extends EventEmitter {
16
16
  });
17
17
  }
18
18
  }
19
-
20
- export const Events = new EventService();
@@ -57,4 +57,3 @@ export class GitHubService {
57
57
  }
58
58
  }
59
59
 
60
- export const GitHub = new GitHubService();
@@ -108,6 +108,3 @@ export class KnowhowSimpleClient {
108
108
  }
109
109
  }
110
110
 
111
- export const knowhowApiClient = new KnowhowSimpleClient(
112
- process.env.KNOWHOW_API_URL
113
- );
@@ -286,5 +286,3 @@ export class McpService {
286
286
  return transformed;
287
287
  }
288
288
  }
289
-
290
- export const Mcp = new McpService();
@@ -123,4 +123,3 @@ export class S3Service {
123
123
  }
124
124
  }
125
125
 
126
- export const AwsS3 = new S3Service();
@@ -1,6 +1,9 @@
1
1
  import { ChatCompletionTool } from "openai/resources/chat";
2
2
  import { replaceEscapedNewLines, restoreEscapedNewLines } from "../utils";
3
3
  import { includedTools } from "../agents/tools/list";
4
+ import { AgentService } from "./AgentService";
5
+ import { EventService } from "./EventService";
6
+ import { AIClient } from "../clients";
4
7
  import { Tool, ToolCall } from "../clients/types";
5
8
  import {
6
9
  ToolOverrideRegistration,
@@ -9,8 +12,20 @@ import {
9
12
  ToolWrapper,
10
13
  createPatternMatcher,
11
14
  } from "./types";
15
+ import { PluginService } from "../plugins/plugins";
16
+
17
+ export interface ToolContext {
18
+ Agents?: AgentService;
19
+ Events?: EventService;
20
+ Clients?: AIClient;
21
+ Tools?: ToolsService;
22
+ Plugins?: PluginService;
23
+ metadata?: { [key: string]: any };
24
+ }
12
25
 
13
26
  export class ToolsService {
27
+ private context: ToolContext = {};
28
+
14
29
  tools = [] as Tool[];
15
30
  private overrides: ToolOverrideRegistration[] = [];
16
31
  private wrappers: ToolWrapperRegistration[] = [];
@@ -18,6 +33,29 @@ export class ToolsService {
18
33
 
19
34
  functions = {};
20
35
 
36
+ constructor(context?: ToolContext) {
37
+ if (context) {
38
+ this.context = { ...context, Tools: this };
39
+ } else {
40
+ this.context = { Tools: this };
41
+ }
42
+ }
43
+
44
+ getContext(): ToolContext {
45
+ return this.context;
46
+ }
47
+
48
+ setContext(context: ToolContext): void {
49
+ this.context = { ...context, Tools: this };
50
+ }
51
+
52
+ addContext<K extends keyof ToolContext, V extends ToolContext[K]>(
53
+ key: K,
54
+ value: V
55
+ ): void {
56
+ this.context[key] = value;
57
+ }
58
+
21
59
  getTools() {
22
60
  return this.tools;
23
61
  }
@@ -54,6 +92,9 @@ export class ToolsService {
54
92
  this.originalFunctions[name] = func;
55
93
  }
56
94
 
95
+ // Auto-bind function to this ToolsService instance
96
+ const boundFunc = func.bind(this);
97
+
57
98
  // Check for overrides first
58
99
  const override = this.findMatchingOverride(name);
59
100
  if (override) {
@@ -67,7 +108,7 @@ export class ToolsService {
67
108
  // Check for wrappers
68
109
  const wrappers = this.findMatchingWrappers(name);
69
110
  if (wrappers.length > 0) {
70
- let wrappedFunction = func;
111
+ let wrappedFunction = boundFunc;
71
112
 
72
113
  // Apply wrappers in priority order
73
114
  for (const wrapperReg of wrappers) {
@@ -82,8 +123,8 @@ export class ToolsService {
82
123
  return;
83
124
  }
84
125
 
85
- // No overrides or wrappers, use original function
86
- this.functions[name] = func;
126
+ // No overrides or wrappers, use bound function
127
+ this.functions[name] = boundFunc;
87
128
  }
88
129
 
89
130
  setFunctions(names: string[], funcs: ((...args: any) => any)[]) {
@@ -97,23 +138,39 @@ export class ToolsService {
97
138
  }
98
139
 
99
140
  addTools(tools: Tool[]) {
100
- this.tools.push(...tools);
141
+ // Prevent duplicate tool names
142
+ const existingTools = this.getToolNames();
143
+ const filteredTools = tools.filter(
144
+ (tool) => !existingTools.includes(tool.function.name)
145
+ );
146
+
147
+ this.tools.push(...filteredTools);
101
148
  }
102
149
 
103
150
  addFunctions(fns: { [fnName: string]: (...args: any) => any }) {
104
151
  for (const fnName of Object.keys(fns)) {
152
+ if (typeof fns[fnName] !== "function") {
153
+ // Skip non-function entries
154
+ continue;
155
+ }
105
156
  this.setFunction(fnName, fns[fnName]);
106
157
  }
107
158
  }
108
159
 
160
+ defineTools(
161
+ tools: Tool[],
162
+ functions: { [fnName: string]: ((...args: any) => any) | any }
163
+ ) {
164
+ this.addTools(tools);
165
+ this.addFunctions(functions);
166
+ }
167
+
109
168
  async callTool(toolCall: ToolCall, enabledTools = this.getToolNames()) {
110
169
  const functionName = toolCall.function.name;
111
170
  const functionArgs = JSON.parse(
112
171
  restoreEscapedNewLines(toolCall.function.arguments)
113
172
  );
114
173
 
115
- console.log(toolCall);
116
-
117
174
  try {
118
175
  // Check if tool is enabled
119
176
  if (!enabledTools.includes(functionName)) {
@@ -285,5 +342,3 @@ export class ToolsService {
285
342
  }
286
343
  }
287
344
  }
288
-
289
- export const Tools = new ToolsService();
@@ -49,4 +49,3 @@ export class FlagsService {
49
49
  }
50
50
  }
51
51
 
52
- export const Flags = new FlagsService();
@@ -1,3 +1,14 @@
1
+ import { Clients } from "../clients";
2
+ import { Plugins } from "../plugins/plugins";
3
+ import { AgentService } from "./AgentService";
4
+ import { EventService } from "./EventService";
5
+ import { FlagsService } from "./flags";
6
+ import { GitHubService } from "./GitHub";
7
+ import { KnowhowSimpleClient } from "./KnowhowClient";
8
+ import { McpService } from "./Mcp";
9
+ import { S3Service } from "./S3";
10
+ import { ToolsService } from "./Tools";
11
+
1
12
  export * from "./AgentService";
2
13
  export * from "./EventService";
3
14
  export * from "./flags";
@@ -6,3 +17,48 @@ export * from "./S3";
6
17
  export * from "./Tools";
7
18
  export * as MCP from "./Mcp";
8
19
  export * from "./EmbeddingService";
20
+ export { Clients } from "../clients";
21
+ export { Plugins };
22
+
23
+ let Singletons = {} as {
24
+ Tools: ToolsService;
25
+ Events: EventService;
26
+ Agents: AgentService;
27
+ Flags: FlagsService;
28
+ GitHub: GitHubService;
29
+ Mcp: McpService;
30
+ AwsS3: S3Service;
31
+ knowhowApiClient: KnowhowSimpleClient;
32
+ Plugins: typeof Plugins;
33
+ Clients: typeof Clients;
34
+ };
35
+
36
+ export const services = (): typeof Singletons => {
37
+ if (Object.keys(Singletons).length === 0) {
38
+ const Tools = new ToolsService();
39
+ const Events = new EventService();
40
+ const Agents = new AgentService(Tools, Events);
41
+ Singletons = {
42
+ Tools,
43
+ Events,
44
+ Agents,
45
+
46
+ Flags: new FlagsService(),
47
+ GitHub: new GitHubService(),
48
+ Mcp: new McpService(),
49
+ AwsS3: new S3Service(),
50
+ knowhowApiClient: new KnowhowSimpleClient(process.env.KNOWHOW_API_URL),
51
+ Plugins,
52
+ Clients,
53
+ };
54
+
55
+ Singletons.Tools.setContext({
56
+ Agents,
57
+ Events,
58
+ Plugins,
59
+ Clients,
60
+ });
61
+ }
62
+
63
+ return Singletons;
64
+ };
@@ -0,0 +1,53 @@
1
+ import { getConfig } from "../../config";
2
+ import { KnowhowModule, ModuleContext } from "./types";
3
+ import { ToolsService } from "../Tools";
4
+ import { services } from "../";
5
+ import { EventService } from "../EventService";
6
+
7
+ export class ModulesService {
8
+ async loadModulesFromConfig(context?: ModuleContext) {
9
+ const config = await getConfig();
10
+
11
+ // If no context provided, fall back to global singletons
12
+ if (!context) {
13
+ const { Clients, Plugins, Agents, Tools } = services();
14
+ context = {
15
+ Agents,
16
+ Plugins,
17
+ Clients,
18
+ Tools,
19
+ };
20
+ }
21
+
22
+ // Use the toolsService from context
23
+ const toolsService = context.Tools;
24
+ const agentService = context.Agents;
25
+ const pluginService = context.Plugins;
26
+ const clients = context.Clients;
27
+
28
+ const modules = config.modules || [];
29
+
30
+ for (const modulePath of modules) {
31
+ const importedModule = require(modulePath) as KnowhowModule;
32
+ await importedModule.init({ config, cwd: process.cwd() });
33
+
34
+ for (const agent of importedModule.agents) {
35
+ agentService.registerAgent(agent);
36
+ }
37
+
38
+ for (const tool of importedModule.tools) {
39
+ toolsService.addTool(tool.definition);
40
+ toolsService.setFunction(tool.definition.function.name, tool.handler);
41
+ }
42
+
43
+ for (const plugin of importedModule.plugins) {
44
+ pluginService.registerPlugin(plugin.name, plugin.plugin);
45
+ }
46
+
47
+ for (const client of importedModule.clients) {
48
+ clients.registerClient(client.provider, client.client);
49
+ clients.registerModels(client.provider, client.models);
50
+ }
51
+ }
52
+ }
53
+ }
@@ -1,8 +1,12 @@
1
- import { Plugin } from "../plugins/types";
2
- import { IAgent } from "../agents/interface";
3
- import { Tool } from "../clients/types";
4
- import { Config } from "../types";
5
- import { GenericClient } from "../clients/types";
1
+ import { Plugin } from "../../plugins/types";
2
+ import { IAgent } from "../../agents/interface";
3
+ import { Tool } from "../../clients/types";
4
+ import { Config } from "../../types";
5
+ import { GenericClient } from "../../clients/types";
6
+ import { AgentService } from "../AgentService";
7
+ import { PluginService } from "../../plugins/plugins";
8
+ import { AIClient } from "../../clients";
9
+ import { ToolsService } from "../Tools";
6
10
 
7
11
  /*
8
12
  *
@@ -38,6 +42,13 @@ export type InitParams = {
38
42
  cwd: string;
39
43
  };
40
44
 
45
+ export interface ModuleContext {
46
+ Agents: AgentService;
47
+ Plugins: PluginService;
48
+ Clients: AIClient;
49
+ Tools: ToolsService;
50
+ }
51
+
41
52
  export interface KnowhowModule {
42
53
  init: (params: InitParams) => Promise<void>;
43
54
  commands: ModuleChatCommand[];