@zhijiewang/openharness 2.1.0 → 2.3.1

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 (231) hide show
  1. package/README.md +4 -4
  2. package/dist/DeferredTool.js +3 -1
  3. package/dist/Tool.d.ts +1 -1
  4. package/dist/agents/roles.js +58 -62
  5. package/dist/commands/cybergotchi.d.ts +1 -1
  6. package/dist/commands/cybergotchi.js +30 -30
  7. package/dist/commands/index.js +288 -132
  8. package/dist/components/App.d.ts +1 -1
  9. package/dist/components/App.js +6 -6
  10. package/dist/components/CompanionFooter.d.ts +1 -1
  11. package/dist/components/CompanionFooter.js +6 -8
  12. package/dist/components/CybergotchiBubble.js +5 -5
  13. package/dist/components/CybergotchiPanel.d.ts +1 -1
  14. package/dist/components/CybergotchiPanel.js +7 -7
  15. package/dist/components/CybergotchiPanelConnected.js +2 -2
  16. package/dist/components/CybergotchiSetup.js +26 -24
  17. package/dist/components/CybergotchiSprite.d.ts +1 -1
  18. package/dist/components/CybergotchiSprite.js +8 -12
  19. package/dist/components/DiffView.d.ts +1 -1
  20. package/dist/components/DiffView.js +10 -10
  21. package/dist/components/ErrorBoundary.d.ts +1 -1
  22. package/dist/components/ErrorBoundary.js +1 -1
  23. package/dist/components/InitWizard.js +65 -33
  24. package/dist/components/Markdown.js +2 -4
  25. package/dist/components/Messages.js +4 -4
  26. package/dist/components/PermissionPrompt.d.ts +1 -1
  27. package/dist/components/PermissionPrompt.js +15 -17
  28. package/dist/components/REPL.d.ts +1 -1
  29. package/dist/components/REPL.js +74 -49
  30. package/dist/components/Spinner.js +2 -2
  31. package/dist/components/TextInput.js +35 -29
  32. package/dist/components/ToolCallDisplay.js +3 -5
  33. package/dist/cybergotchi/bones.d.ts +1 -1
  34. package/dist/cybergotchi/bones.js +8 -8
  35. package/dist/cybergotchi/config.d.ts +2 -2
  36. package/dist/cybergotchi/config.js +13 -13
  37. package/dist/cybergotchi/events.d.ts +5 -5
  38. package/dist/cybergotchi/events.js +7 -7
  39. package/dist/cybergotchi/needs.d.ts +2 -2
  40. package/dist/cybergotchi/needs.js +7 -9
  41. package/dist/cybergotchi/personality.d.ts +2 -2
  42. package/dist/cybergotchi/personality.js +2 -2
  43. package/dist/cybergotchi/species.d.ts +1 -1
  44. package/dist/cybergotchi/species.js +145 -217
  45. package/dist/cybergotchi/speech.d.ts +2 -2
  46. package/dist/cybergotchi/speech.js +43 -43
  47. package/dist/cybergotchi/types.d.ts +4 -4
  48. package/dist/cybergotchi/types.js +26 -26
  49. package/dist/cybergotchi/useCybergotchi.d.ts +1 -1
  50. package/dist/cybergotchi/useCybergotchi.js +29 -25
  51. package/dist/git/index.js +11 -9
  52. package/dist/harness/checkpoints.js +29 -21
  53. package/dist/harness/config.d.ts +3 -3
  54. package/dist/harness/config.js +15 -9
  55. package/dist/harness/context-warning.d.ts +1 -1
  56. package/dist/harness/context-warning.js +1 -1
  57. package/dist/harness/cost.js +1 -1
  58. package/dist/harness/credentials.js +13 -13
  59. package/dist/harness/hooks.js +7 -5
  60. package/dist/harness/keybindings.js +20 -18
  61. package/dist/harness/marketplace.d.ts +3 -3
  62. package/dist/harness/marketplace.js +55 -42
  63. package/dist/harness/memory.d.ts +23 -5
  64. package/dist/harness/memory.js +142 -41
  65. package/dist/harness/onboarding.js +30 -10
  66. package/dist/harness/plugins.d.ts +9 -1
  67. package/dist/harness/plugins.js +54 -30
  68. package/dist/harness/rules.js +12 -7
  69. package/dist/harness/sandbox.js +15 -15
  70. package/dist/harness/session-db.d.ts +55 -0
  71. package/dist/harness/session-db.js +165 -0
  72. package/dist/harness/session.d.ts +1 -1
  73. package/dist/harness/session.js +34 -15
  74. package/dist/harness/store.d.ts +3 -3
  75. package/dist/harness/store.js +6 -4
  76. package/dist/harness/submit-handler.d.ts +4 -4
  77. package/dist/harness/submit-handler.js +25 -23
  78. package/dist/harness/telemetry.d.ts +1 -1
  79. package/dist/harness/telemetry.js +23 -19
  80. package/dist/harness/traces.d.ts +2 -2
  81. package/dist/harness/traces.js +39 -33
  82. package/dist/harness/verification.d.ts +1 -1
  83. package/dist/harness/verification.js +50 -44
  84. package/dist/lsp/client.js +44 -40
  85. package/dist/main.js +114 -59
  86. package/dist/mcp/DeferredMcpTool.d.ts +4 -4
  87. package/dist/mcp/DeferredMcpTool.js +9 -5
  88. package/dist/mcp/McpTool.d.ts +4 -4
  89. package/dist/mcp/McpTool.js +8 -4
  90. package/dist/mcp/client.d.ts +2 -2
  91. package/dist/mcp/client.js +21 -21
  92. package/dist/mcp/loader.d.ts +1 -1
  93. package/dist/mcp/loader.js +17 -12
  94. package/dist/mcp/registry.d.ts +3 -3
  95. package/dist/mcp/registry.js +97 -97
  96. package/dist/mcp/schema.d.ts +1 -1
  97. package/dist/mcp/schema.js +16 -16
  98. package/dist/mcp/server.d.ts +1 -1
  99. package/dist/mcp/server.js +21 -21
  100. package/dist/mcp/types.d.ts +3 -3
  101. package/dist/providers/anthropic.d.ts +2 -2
  102. package/dist/providers/anthropic.js +10 -9
  103. package/dist/providers/base.d.ts +1 -1
  104. package/dist/providers/index.js +10 -3
  105. package/dist/providers/llamacpp.d.ts +2 -2
  106. package/dist/providers/llamacpp.js +1 -3
  107. package/dist/providers/ollama.d.ts +2 -2
  108. package/dist/providers/ollama.js +3 -4
  109. package/dist/providers/openai.d.ts +2 -2
  110. package/dist/providers/openai.js +3 -5
  111. package/dist/providers/openrouter.d.ts +2 -2
  112. package/dist/providers/router.d.ts +1 -1
  113. package/dist/providers/router.js +7 -7
  114. package/dist/query/compress.d.ts +2 -2
  115. package/dist/query/compress.js +22 -21
  116. package/dist/query/context-manager.d.ts +1 -1
  117. package/dist/query/context-manager.js +5 -5
  118. package/dist/query/errors.js +1 -1
  119. package/dist/query/index.d.ts +1 -1
  120. package/dist/query/index.js +42 -24
  121. package/dist/query/tools.js +15 -12
  122. package/dist/query/types.d.ts +3 -1
  123. package/dist/query.d.ts +1 -1
  124. package/dist/query.js +1 -1
  125. package/dist/remote/auth.d.ts +2 -2
  126. package/dist/remote/auth.js +8 -8
  127. package/dist/remote/server.d.ts +3 -3
  128. package/dist/remote/server.js +60 -60
  129. package/dist/renderer/cells.js +9 -9
  130. package/dist/renderer/colors.js +24 -6
  131. package/dist/renderer/diff.d.ts +2 -2
  132. package/dist/renderer/diff.js +27 -19
  133. package/dist/renderer/differ.d.ts +1 -1
  134. package/dist/renderer/differ.js +9 -9
  135. package/dist/renderer/image.js +19 -19
  136. package/dist/renderer/index.d.ts +6 -6
  137. package/dist/renderer/index.js +163 -93
  138. package/dist/renderer/input.js +66 -48
  139. package/dist/renderer/layout.d.ts +6 -6
  140. package/dist/renderer/layout.js +163 -124
  141. package/dist/renderer/markdown.d.ts +2 -2
  142. package/dist/renderer/markdown.js +173 -54
  143. package/dist/renderer/session-browser.d.ts +2 -2
  144. package/dist/renderer/session-browser.js +19 -21
  145. package/dist/repl.d.ts +5 -5
  146. package/dist/repl.js +311 -198
  147. package/dist/sdk/index.d.ts +5 -5
  148. package/dist/sdk/index.js +32 -26
  149. package/dist/services/AgentDispatcher.d.ts +3 -3
  150. package/dist/services/AgentDispatcher.js +33 -29
  151. package/dist/services/CronExecutor.d.ts +4 -4
  152. package/dist/services/CronExecutor.js +12 -8
  153. package/dist/services/EvaluatorLoop.d.ts +3 -3
  154. package/dist/services/EvaluatorLoop.js +29 -21
  155. package/dist/services/MetaHarness.d.ts +1 -1
  156. package/dist/services/MetaHarness.js +34 -32
  157. package/dist/services/PipelineExecutor.d.ts +1 -1
  158. package/dist/services/PipelineExecutor.js +23 -25
  159. package/dist/services/SkillExtractor.d.ts +43 -0
  160. package/dist/services/SkillExtractor.js +163 -0
  161. package/dist/services/StreamingToolExecutor.d.ts +2 -2
  162. package/dist/services/StreamingToolExecutor.js +11 -7
  163. package/dist/services/a2a.d.ts +8 -8
  164. package/dist/services/a2a.js +44 -34
  165. package/dist/services/agent-messaging.d.ts +33 -15
  166. package/dist/services/agent-messaging.js +65 -13
  167. package/dist/services/cron.js +16 -16
  168. package/dist/tools/AgentTool/index.d.ts +5 -2
  169. package/dist/tools/AgentTool/index.js +25 -39
  170. package/dist/tools/AskUserTool/index.js +1 -1
  171. package/dist/tools/BashTool/index.d.ts +2 -2
  172. package/dist/tools/BashTool/index.js +18 -10
  173. package/dist/tools/CronTool/index.js +30 -12
  174. package/dist/tools/DiagnosticsTool/index.js +28 -22
  175. package/dist/tools/EnterPlanModeTool/index.js +93 -14
  176. package/dist/tools/EnterWorktreeTool/index.js +7 -3
  177. package/dist/tools/ExitPlanModeTool/index.d.ts +22 -1
  178. package/dist/tools/ExitPlanModeTool/index.js +20 -5
  179. package/dist/tools/ExitWorktreeTool/index.js +11 -4
  180. package/dist/tools/FileEditTool/index.js +3 -5
  181. package/dist/tools/FileReadTool/index.js +16 -10
  182. package/dist/tools/FileWriteTool/index.js +2 -2
  183. package/dist/tools/GlobTool/index.js +5 -9
  184. package/dist/tools/GrepTool/index.d.ts +2 -2
  185. package/dist/tools/GrepTool/index.js +14 -9
  186. package/dist/tools/ImageReadTool/index.js +2 -2
  187. package/dist/tools/KillProcessTool/index.js +11 -7
  188. package/dist/tools/LSTool/index.js +3 -3
  189. package/dist/tools/MemoryTool/index.d.ts +5 -5
  190. package/dist/tools/MemoryTool/index.js +28 -14
  191. package/dist/tools/MonitorTool/index.js +24 -19
  192. package/dist/tools/MultiEditTool/index.js +9 -5
  193. package/dist/tools/NotebookEditTool/index.js +3 -3
  194. package/dist/tools/ParallelAgentTool/index.d.ts +4 -4
  195. package/dist/tools/ParallelAgentTool/index.js +12 -6
  196. package/dist/tools/PipelineTool/index.js +3 -3
  197. package/dist/tools/PowerShellTool/index.js +10 -6
  198. package/dist/tools/RemoteTriggerTool/index.js +8 -4
  199. package/dist/tools/ScheduleWakeupTool/index.d.ts +42 -0
  200. package/dist/tools/ScheduleWakeupTool/index.js +115 -0
  201. package/dist/tools/SendMessageTool/index.js +25 -7
  202. package/dist/tools/SessionSearchTool/index.d.ts +15 -0
  203. package/dist/tools/SessionSearchTool/index.js +36 -0
  204. package/dist/tools/SkillTool/index.d.ts +3 -0
  205. package/dist/tools/SkillTool/index.js +39 -9
  206. package/dist/tools/TaskCreateTool/index.d.ts +2 -2
  207. package/dist/tools/TaskCreateTool/index.js +2 -2
  208. package/dist/tools/TaskGetTool/index.js +2 -2
  209. package/dist/tools/TaskListTool/index.js +3 -5
  210. package/dist/tools/TaskOutputTool/index.js +2 -2
  211. package/dist/tools/TaskStopTool/index.js +3 -3
  212. package/dist/tools/TaskUpdateTool/index.d.ts +4 -4
  213. package/dist/tools/TaskUpdateTool/index.js +2 -2
  214. package/dist/tools/ToolSearchTool/index.js +9 -6
  215. package/dist/tools/WebFetchTool/index.js +1 -1
  216. package/dist/tools/WebSearchTool/index.js +2 -6
  217. package/dist/tools.js +31 -30
  218. package/dist/types/permissions.js +15 -9
  219. package/dist/utils/bash-safety.d.ts +1 -1
  220. package/dist/utils/bash-safety.js +64 -54
  221. package/dist/utils/diff-algorithm.d.ts +3 -3
  222. package/dist/utils/diff-algorithm.js +7 -7
  223. package/dist/utils/fs.js +3 -3
  224. package/dist/utils/safe-env.js +1 -1
  225. package/dist/utils/theme-data.d.ts +1 -1
  226. package/dist/utils/theme-data.js +1 -1
  227. package/dist/utils/theme.d.ts +1 -1
  228. package/dist/utils/theme.js +1 -1
  229. package/dist/utils/tool-summary.d.ts +1 -1
  230. package/dist/utils/tool-summary.js +27 -9
  231. package/package.json +10 -3
@@ -4,8 +4,8 @@
4
4
  * Other MCP clients (IDE extensions, other agents) can connect and use
5
5
  * openHarness's tools (Bash, Read, Write, Edit, Glob, Grep, etc.)
6
6
  */
7
- import { createInterface } from 'node:readline';
8
- import { zodToJsonSchemaSimple } from './schema.js';
7
+ import { createInterface } from "node:readline";
8
+ import { zodToJsonSchemaSimple } from "./schema.js";
9
9
  export class McpServer {
10
10
  tools;
11
11
  context;
@@ -16,78 +16,78 @@ export class McpServer {
16
16
  /** Start listening on stdio */
17
17
  start() {
18
18
  const rl = createInterface({ input: process.stdin });
19
- rl.on('line', async (line) => {
19
+ rl.on("line", async (line) => {
20
20
  try {
21
21
  const req = JSON.parse(line);
22
22
  const res = await this.handleRequest(req);
23
23
  if (res && req.id !== undefined) {
24
- process.stdout.write(JSON.stringify(res) + '\n');
24
+ process.stdout.write(`${JSON.stringify(res)}\n`);
25
25
  }
26
26
  }
27
27
  catch {
28
28
  // Ignore parse errors
29
29
  }
30
30
  });
31
- process.stderr.write('[mcp-server] OpenHarness MCP server ready\n');
31
+ process.stderr.write("[mcp-server] OpenHarness MCP server ready\n");
32
32
  }
33
33
  async handleRequest(req) {
34
34
  const id = req.id ?? null;
35
35
  switch (req.method) {
36
- case 'initialize':
36
+ case "initialize":
37
37
  return {
38
- jsonrpc: '2.0',
38
+ jsonrpc: "2.0",
39
39
  id,
40
40
  result: {
41
- protocolVersion: '2024-11-05',
42
- serverInfo: { name: 'openharness', version: '0.6.0' },
41
+ protocolVersion: "2024-11-05",
42
+ serverInfo: { name: "openharness", version: "0.6.0" },
43
43
  capabilities: { tools: { listChanged: false } },
44
44
  },
45
45
  };
46
- case 'notifications/initialized':
46
+ case "notifications/initialized":
47
47
  return null; // notification, no response
48
- case 'tools/list':
48
+ case "tools/list":
49
49
  return {
50
- jsonrpc: '2.0',
50
+ jsonrpc: "2.0",
51
51
  id,
52
52
  result: {
53
- tools: this.tools.map(t => ({
53
+ tools: this.tools.map((t) => ({
54
54
  name: t.name,
55
55
  description: t.prompt().slice(0, 200),
56
56
  inputSchema: zodToJsonSchemaSimple(t.inputSchema),
57
57
  })),
58
58
  },
59
59
  };
60
- case 'tools/call': {
60
+ case "tools/call": {
61
61
  const { name, arguments: args } = req.params ?? {};
62
- const tool = this.tools.find(t => t.name === name);
62
+ const tool = this.tools.find((t) => t.name === name);
63
63
  if (!tool) {
64
- return { jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } };
64
+ return { jsonrpc: "2.0", id, error: { code: -32601, message: `Unknown tool: ${name}` } };
65
65
  }
66
66
  const parsed = tool.inputSchema.safeParse(args);
67
67
  if (!parsed.success) {
68
- return { jsonrpc: '2.0', id, error: { code: -32602, message: parsed.error.message } };
68
+ return { jsonrpc: "2.0", id, error: { code: -32602, message: parsed.error.message } };
69
69
  }
70
70
  try {
71
71
  const result = await tool.call(parsed.data, this.context);
72
72
  return {
73
- jsonrpc: '2.0',
73
+ jsonrpc: "2.0",
74
74
  id,
75
75
  result: {
76
- content: [{ type: 'text', text: result.output }],
76
+ content: [{ type: "text", text: result.output }],
77
77
  isError: result.isError,
78
78
  },
79
79
  };
80
80
  }
81
81
  catch (err) {
82
82
  return {
83
- jsonrpc: '2.0',
83
+ jsonrpc: "2.0",
84
84
  id,
85
85
  error: { code: -32000, message: err instanceof Error ? err.message : String(err) },
86
86
  };
87
87
  }
88
88
  }
89
89
  default:
90
- return { jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown method: ${req.method}` } };
90
+ return { jsonrpc: "2.0", id, error: { code: -32601, message: `Unknown method: ${req.method}` } };
91
91
  }
92
92
  }
93
93
  }
@@ -1,12 +1,12 @@
1
1
  /** Minimal MCP protocol types (JSON-RPC 2.0 over stdio) */
2
2
  export interface JsonRpcRequest {
3
- jsonrpc: '2.0';
3
+ jsonrpc: "2.0";
4
4
  id: number;
5
5
  method: string;
6
6
  params?: unknown;
7
7
  }
8
8
  export interface JsonRpcResponse {
9
- jsonrpc: '2.0';
9
+ jsonrpc: "2.0";
10
10
  id: number;
11
11
  result?: unknown;
12
12
  error?: {
@@ -18,7 +18,7 @@ export interface McpToolDef {
18
18
  name: string;
19
19
  description?: string;
20
20
  inputSchema: {
21
- type: 'object';
21
+ type: "object";
22
22
  properties?: Record<string, {
23
23
  type?: string;
24
24
  description?: string;
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Anthropic provider — Claude models via the Anthropic Messages API.
3
3
  */
4
- import type { Message } from "../types/message.js";
5
4
  import type { StreamEvent } from "../types/events.js";
6
- import type { Provider, APIToolDef, ModelInfo, ProviderConfig } from "./base.js";
5
+ import type { Message } from "../types/message.js";
6
+ import type { APIToolDef, ModelInfo, Provider, ProviderConfig } from "./base.js";
7
7
  export declare class AnthropicProvider implements Provider {
8
8
  readonly name = "anthropic";
9
9
  private apiKey;
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Anthropic provider — Claude models via the Anthropic Messages API.
3
3
  */
4
- import { createAssistantMessage } from "../types/message.js";
5
4
  import { IMAGE_PREFIX } from "../tools/ImageReadTool/index.js";
5
+ import { createAssistantMessage } from "../types/message.js";
6
6
  export class AnthropicProvider {
7
7
  name = "anthropic";
8
8
  apiKey;
@@ -47,7 +47,7 @@ export class AnthropicProvider {
47
47
  }
48
48
  else if (msg.role === "tool" && msg.toolResults?.length) {
49
49
  const content = msg.toolResults.map((tr) => {
50
- if (!tr.isError && tr.output.startsWith(IMAGE_PREFIX + ":")) {
50
+ if (!tr.isError && tr.output.startsWith(`${IMAGE_PREFIX}:`)) {
51
51
  const [, mediaType, data] = tr.output.split(":");
52
52
  return {
53
53
  type: "tool_result",
@@ -86,9 +86,7 @@ export class AnthropicProvider {
86
86
  const m = model ?? this.defaultModel;
87
87
  // Prompt caching: send system prompt as content blocks with cache_control.
88
88
  // Anthropic caches matching prefixes — 90% cost reduction on repeat turns.
89
- const systemBlocks = [
90
- { type: "text", text: systemPrompt, cache_control: { type: "ephemeral" } },
91
- ];
89
+ const systemBlocks = [{ type: "text", text: systemPrompt, cache_control: { type: "ephemeral" } }];
92
90
  const body = {
93
91
  model: m,
94
92
  max_tokens: 8192,
@@ -133,7 +131,7 @@ export class AnthropicProvider {
133
131
  let currentToolId = "";
134
132
  let currentToolName = "";
135
133
  let currentToolArgs = "";
136
- let inThinkingBlock = false;
134
+ let _inThinkingBlock = false;
137
135
  while (true) {
138
136
  const { done, value } = await reader.read();
139
137
  if (done)
@@ -172,7 +170,7 @@ export class AnthropicProvider {
172
170
  };
173
171
  }
174
172
  if (block?.type === "thinking") {
175
- inThinkingBlock = true;
173
+ _inThinkingBlock = true;
176
174
  }
177
175
  break;
178
176
  }
@@ -190,7 +188,7 @@ export class AnthropicProvider {
190
188
  break;
191
189
  }
192
190
  case "content_block_stop": {
193
- inThinkingBlock = false;
191
+ _inThinkingBlock = false;
194
192
  if (currentToolId) {
195
193
  let parsedArgs = {};
196
194
  if (currentToolArgs) {
@@ -198,7 +196,10 @@ export class AnthropicProvider {
198
196
  parsedArgs = JSON.parse(currentToolArgs);
199
197
  }
200
198
  catch {
201
- yield { type: "error", message: `Malformed tool args for ${currentToolName}: ${currentToolArgs.slice(0, 200)}` };
199
+ yield {
200
+ type: "error",
201
+ message: `Malformed tool args for ${currentToolName}: ${currentToolArgs.slice(0, 200)}`,
202
+ };
202
203
  }
203
204
  }
204
205
  yield {
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Base provider interface — every LLM provider implements this.
3
3
  */
4
- import type { Message } from "../types/message.js";
5
4
  import type { StreamEvent } from "../types/events.js";
5
+ import type { Message } from "../types/message.js";
6
6
  export type ModelInfo = {
7
7
  id: string;
8
8
  provider: string;
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Provider factory — create the right provider from a model string.
3
3
  */
4
+ import { AnthropicProvider } from "./anthropic.js";
5
+ import { LlamaCppProvider } from "./llamacpp.js";
4
6
  import { OllamaProvider } from "./ollama.js";
5
7
  import { OpenAIProvider } from "./openai.js";
6
- import { AnthropicProvider } from "./anthropic.js";
7
8
  import { OpenRouterProvider } from "./openrouter.js";
8
- import { LlamaCppProvider } from "./llamacpp.js";
9
9
  /**
10
10
  * Create a provider from a model string like "ollama/llama3" or "gpt-4o".
11
11
  */
@@ -61,7 +61,14 @@ function guessProviderFromModel(model) {
61
61
  return "anthropic";
62
62
  if (model.includes("gguf") || model.startsWith("llamacpp"))
63
63
  return "llamacpp";
64
- if (model.includes("llama") || model.includes("mistral") || model.includes("phi") || model.includes("qwen") || model.includes("gemma") || model.includes("deepseek") || model.includes("codestral") || model.includes("starcoder"))
64
+ if (model.includes("llama") ||
65
+ model.includes("mistral") ||
66
+ model.includes("phi") ||
67
+ model.includes("qwen") ||
68
+ model.includes("gemma") ||
69
+ model.includes("deepseek") ||
70
+ model.includes("codestral") ||
71
+ model.includes("starcoder"))
65
72
  return "ollama";
66
73
  return "openai"; // default fallback
67
74
  }
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * LlamaCpp provider — local LLM inference via llama-server OpenAI-compatible REST API.
3
3
  */
4
- import type { Message } from "../types/message.js";
5
4
  import type { StreamEvent } from "../types/events.js";
6
- import type { Provider, APIToolDef, ModelInfo, ProviderConfig } from "./base.js";
5
+ import type { Message } from "../types/message.js";
6
+ import type { APIToolDef, ModelInfo, Provider, ProviderConfig } from "./base.js";
7
7
  export declare class LlamaCppProvider implements Provider {
8
8
  readonly name = "llamacpp";
9
9
  private baseUrl;
@@ -7,9 +7,7 @@ export class LlamaCppProvider {
7
7
  baseUrl;
8
8
  defaultModel;
9
9
  constructor(config) {
10
- this.baseUrl = (config.baseUrl ?? "http://localhost:8080")
11
- .replace(/\/$/, "")
12
- .replace(/\/v1$/, "");
10
+ this.baseUrl = (config.baseUrl ?? "http://localhost:8080").replace(/\/$/, "").replace(/\/v1$/, "");
13
11
  this.defaultModel = config.defaultModel ?? "";
14
12
  }
15
13
  convertMessages(messages, systemPrompt) {
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Ollama provider — local LLM inference via Ollama REST API.
3
3
  */
4
- import type { Message } from "../types/message.js";
5
4
  import type { StreamEvent } from "../types/events.js";
6
- import type { Provider, APIToolDef, ModelInfo, ProviderConfig } from "./base.js";
5
+ import type { Message } from "../types/message.js";
6
+ import type { APIToolDef, ModelInfo, Provider, ProviderConfig } from "./base.js";
7
7
  export declare class OllamaProvider implements Provider {
8
8
  readonly name = "ollama";
9
9
  private baseUrl;
@@ -189,7 +189,7 @@ export class OllamaProvider {
189
189
  };
190
190
  const args = typeof tc.function?.arguments === "string"
191
191
  ? JSON.parse(tc.function.arguments)
192
- : tc.function?.arguments ?? {};
192
+ : (tc.function?.arguments ?? {});
193
193
  yield {
194
194
  type: "tool_call_complete",
195
195
  callId,
@@ -253,7 +253,7 @@ export class OllamaProvider {
253
253
  toolName: tc.function?.name ?? "unknown",
254
254
  arguments: typeof tc.function?.arguments === "string"
255
255
  ? JSON.parse(tc.function.arguments)
256
- : tc.function?.arguments ?? {},
256
+ : (tc.function?.arguments ?? {}),
257
257
  }));
258
258
  }
259
259
  return createAssistantMessage(content, toolCalls);
@@ -271,8 +271,7 @@ export class OllamaProvider {
271
271
  // Detect vision support from model families
272
272
  // Presence of "clip" or "llava" in families indicates vision capability
273
273
  const families = m.details?.families ?? [];
274
- const supportsVision = Array.isArray(families) &&
275
- families.some((f) => f.includes("clip") || f.includes("llava"));
274
+ const supportsVision = Array.isArray(families) && families.some((f) => f.includes("clip") || f.includes("llava"));
276
275
  return {
277
276
  id: m.name,
278
277
  provider: "ollama",
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * OpenAI-compatible provider — works with OpenAI, DeepSeek, Groq, Together, etc.
3
3
  */
4
- import type { Message } from "../types/message.js";
5
4
  import type { StreamEvent } from "../types/events.js";
6
- import type { Provider, APIToolDef, ModelInfo, ProviderConfig } from "./base.js";
5
+ import type { Message } from "../types/message.js";
6
+ import type { APIToolDef, ModelInfo, Provider, ProviderConfig } from "./base.js";
7
7
  export declare class OpenAIProvider implements Provider {
8
8
  readonly name: string;
9
9
  private apiKey;
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * OpenAI-compatible provider — works with OpenAI, DeepSeek, Groq, Together, etc.
3
3
  */
4
- import { createAssistantMessage } from "../types/message.js";
5
4
  import { IMAGE_PREFIX } from "../tools/ImageReadTool/index.js";
5
+ import { createAssistantMessage } from "../types/message.js";
6
6
  export class OpenAIProvider {
7
7
  name;
8
8
  apiKey;
@@ -35,7 +35,7 @@ export class OpenAIProvider {
35
35
  }
36
36
  else if (msg.role === "tool" && msg.toolResults?.length) {
37
37
  for (const tr of msg.toolResults) {
38
- if (!tr.isError && tr.output.startsWith(IMAGE_PREFIX + ":")) {
38
+ if (!tr.isError && tr.output.startsWith(`${IMAGE_PREFIX}:`)) {
39
39
  const [, mediaType, data] = tr.output.split(":");
40
40
  out.push({
41
41
  role: "tool",
@@ -129,9 +129,7 @@ export class OpenAIProvider {
129
129
  const inputTokens = chunk.usage.prompt_tokens ?? 0;
130
130
  const outputTokens = chunk.usage.completion_tokens ?? 0;
131
131
  const info = this.getModelInfo(m);
132
- const cost = (inputTokens * (info?.inputCostPerMtok ?? 0) +
133
- outputTokens * (info?.outputCostPerMtok ?? 0)) /
134
- 1_000_000;
132
+ const cost = (inputTokens * (info?.inputCostPerMtok ?? 0) + outputTokens * (info?.outputCostPerMtok ?? 0)) / 1_000_000;
135
133
  yield { type: "cost_update", inputTokens, outputTokens, cost, model: m };
136
134
  }
137
135
  const delta = chunk.choices?.[0]?.delta;
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * OpenRouter provider — aggregated LLM access via OpenAI-compatible API.
3
3
  */
4
- import type { Message } from "../types/message.js";
5
4
  import type { StreamEvent } from "../types/events.js";
6
- import type { Provider, APIToolDef, ModelInfo, ProviderConfig } from "./base.js";
5
+ import type { Message } from "../types/message.js";
6
+ import type { APIToolDef, ModelInfo, Provider, ProviderConfig } from "./base.js";
7
7
  export declare class OpenRouterProvider implements Provider {
8
8
  readonly name = "openrouter";
9
9
  private apiKey;
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Saves ~20% cost and ~30% latency by avoiding expensive models for simple tasks.
10
10
  */
11
- export type ModelTier = 'fast' | 'balanced' | 'powerful';
11
+ export type ModelTier = "fast" | "balanced" | "powerful";
12
12
  export type RouterConfig = {
13
13
  fast?: string;
14
14
  balanced?: string;
@@ -19,27 +19,27 @@ export class ModelRouter {
19
19
  select(context) {
20
20
  // High-context pressure → use fast model to minimize token cost
21
21
  if (context.contextUsage && context.contextUsage > 0.8) {
22
- return this.route('fast', 'context pressure > 80%');
22
+ return this.route("fast", "context pressure > 80%");
23
23
  }
24
24
  // Roles that require deep reasoning → powerful
25
- const powerfulRoles = ['code-reviewer', 'evaluator', 'architect', 'security-auditor'];
25
+ const powerfulRoles = ["code-reviewer", "evaluator", "architect", "security-auditor"];
26
26
  if (context.role && powerfulRoles.includes(context.role)) {
27
- return this.route('powerful', `role: ${context.role}`);
27
+ return this.route("powerful", `role: ${context.role}`);
28
28
  }
29
29
  // Early exploration turns (1-2) → fast
30
30
  if (context.turn <= 2 && context.hadToolCalls) {
31
- return this.route('fast', 'early exploration');
31
+ return this.route("fast", "early exploration");
32
32
  }
33
33
  // Tool-heavy turns (3+ tool calls) → fast (just dispatching)
34
34
  if (context.toolCallCount >= 3) {
35
- return this.route('fast', 'tool-heavy turn');
35
+ return this.route("fast", "tool-heavy turn");
36
36
  }
37
37
  // Final response (no tool calls) → powerful for quality
38
38
  if (context.isFinalResponse) {
39
- return this.route('powerful', 'final response');
39
+ return this.route("powerful", "final response");
40
40
  }
41
41
  // Default → balanced
42
- return this.route('balanced', 'default');
42
+ return this.route("balanced", "default");
43
43
  }
44
44
  route(tier, reason) {
45
45
  const model = this.config[tier] ?? this.defaultModel;
@@ -2,8 +2,8 @@
2
2
  * Message compression — multi-phase strategies to keep conversation
3
3
  * within the context window.
4
4
  */
5
- import type { Message } from "../types/message.js";
6
5
  import type { Provider } from "../providers/base.js";
6
+ import type { Message } from "../types/message.js";
7
7
  /**
8
8
  * Semantic importance scoring for messages.
9
9
  * Higher score = more important to keep during compression.
@@ -18,5 +18,5 @@ export declare function compressMessages(messages: Message[], targetTokens: numb
18
18
  /**
19
19
  * LLM-assisted summarization of older messages.
20
20
  */
21
- export declare function summarizeConversation(provider: Provider, messages: Message[], model: string | undefined, targetTokens: number): Promise<Message[]>;
21
+ export declare function summarizeConversation(provider: Provider, messages: Message[], model: string | undefined, _targetTokens: number): Promise<Message[]>;
22
22
  //# sourceMappingURL=compress.d.ts.map
@@ -2,9 +2,9 @@
2
2
  * Message compression — multi-phase strategies to keep conversation
3
3
  * within the context window.
4
4
  */
5
- import { createUserMessage } from "../types/message.js";
6
- import { defaultEstimateTokens } from "../providers/base.js";
7
5
  import { emitHook } from "../harness/hooks.js";
6
+ import { defaultEstimateTokens } from "../providers/base.js";
7
+ import { createUserMessage } from "../types/message.js";
8
8
  const DEFAULT_KEEP_LAST = 10;
9
9
  /**
10
10
  * Semantic importance scoring for messages.
@@ -15,15 +15,15 @@ export function scoreMessage(msg, index, total) {
15
15
  return Infinity;
16
16
  let score = 0;
17
17
  // Role weight: user intent > tool decisions > assistant text
18
- if (msg.role === 'user')
18
+ if (msg.role === "user")
19
19
  score += 30;
20
- else if (msg.role === 'assistant' && msg.toolCalls?.length)
20
+ else if (msg.role === "assistant" && msg.toolCalls?.length)
21
21
  score += 20;
22
- else if (msg.role === 'assistant')
22
+ else if (msg.role === "assistant")
23
23
  score += 10;
24
- else if (msg.role === 'system')
24
+ else if (msg.role === "system")
25
25
  score += 25; // system messages are usually important
26
- else if (msg.role === 'tool')
26
+ else if (msg.role === "tool")
27
27
  score += 5;
28
28
  // Recency bonus: recent messages get +0 to +20
29
29
  const recencyFactor = index / total;
@@ -71,11 +71,11 @@ export function compressMessages(messages, targetTokens) {
71
71
  continue;
72
72
  if (result[i].role === "tool" && result[i].content.length > 500) {
73
73
  const c = result[i].content;
74
- result[i] = { ...result[i], content: c.slice(0, 200) + "\n...[truncated]...\n" + c.slice(-100) };
74
+ result[i] = { ...result[i], content: `${c.slice(0, 200)}\n...[truncated]...\n${c.slice(-100)}` };
75
75
  }
76
76
  if (result[i].role === "assistant" && result[i].content.length > 2000) {
77
77
  const c = result[i].content;
78
- result[i] = { ...result[i], content: c.slice(0, 500) + "\n...[truncated]...\n" + c.slice(-200) };
78
+ result[i] = { ...result[i], content: `${c.slice(0, 500)}\n...[truncated]...\n${c.slice(-200)}` };
79
79
  }
80
80
  }
81
81
  // AutoCompact Phase 1: Replace old tool results with stub
@@ -96,7 +96,7 @@ export function compressMessages(messages, targetTokens) {
96
96
  let lowestIdx = -1;
97
97
  for (let i = 0; i < result.length - keepLast; i++) {
98
98
  const msg = result[i];
99
- if (msg.role === 'system' || msg.meta?.pinned)
99
+ if (msg.role === "system" || msg.meta?.pinned)
100
100
  continue;
101
101
  const score = scoreMessage(msg, i, result.length);
102
102
  if (score < lowestScore) {
@@ -119,8 +119,7 @@ export function compressMessages(messages, targetTokens) {
119
119
  const filtered = result.filter((msg) => {
120
120
  if (msg.role !== "tool")
121
121
  return true;
122
- return (msg.toolResults?.length ?? 0) > 0 &&
123
- msg.toolResults.every((tr) => validCallIds.has(tr.callId));
122
+ return (msg.toolResults?.length ?? 0) > 0 && msg.toolResults.every((tr) => validCallIds.has(tr.callId));
124
123
  });
125
124
  emitHook("postCompact", {});
126
125
  return filtered;
@@ -128,28 +127,30 @@ export function compressMessages(messages, targetTokens) {
128
127
  /**
129
128
  * LLM-assisted summarization of older messages.
130
129
  */
131
- export async function summarizeConversation(provider, messages, model, targetTokens) {
130
+ export async function summarizeConversation(provider, messages, model, _targetTokens) {
132
131
  const keepRecent = Math.min(6, messages.length - 1);
133
132
  const older = messages.slice(0, messages.length - keepRecent);
134
133
  const recent = messages.slice(messages.length - keepRecent);
135
134
  if (older.length < 2)
136
135
  return messages;
137
- const pinned = older.filter(m => m.meta?.pinned);
138
- const summarizable = older.filter(m => !m.meta?.pinned);
136
+ const pinned = older.filter((m) => m.meta?.pinned);
137
+ const summarizable = older.filter((m) => !m.meta?.pinned);
139
138
  if (summarizable.length < 2)
140
139
  return messages;
141
- const olderText = summarizable.map(m => {
142
- const prefix = m.role === 'user' ? 'User' : m.role === 'assistant' ? 'Assistant' : 'System';
140
+ const olderText = summarizable
141
+ .map((m) => {
142
+ const prefix = m.role === "user" ? "User" : m.role === "assistant" ? "Assistant" : "System";
143
143
  let text = `${prefix}: ${m.content.slice(0, 500)}`;
144
144
  if (m.toolCalls?.length) {
145
- text += `\n [Used tools: ${m.toolCalls.map(tc => tc.toolName).join(', ')}]`;
145
+ text += `\n [Used tools: ${m.toolCalls.map((tc) => tc.toolName).join(", ")}]`;
146
146
  }
147
147
  return text;
148
- }).join('\n\n');
148
+ })
149
+ .join("\n\n");
149
150
  const summaryPrompt = `Summarize this conversation history in 2-4 sentences, preserving key decisions, file paths mentioned, and what was accomplished:\n\n${olderText.slice(0, 3000)}`;
150
- const summaryResponse = await provider.complete([createUserMessage(summaryPrompt)], 'You are a conversation summarizer. Be concise and factual. Preserve important details like file paths and decisions.', undefined, model);
151
+ const summaryResponse = await provider.complete([createUserMessage(summaryPrompt)], "You are a conversation summarizer. Be concise and factual. Preserve important details like file paths and decisions.", undefined, model);
151
152
  const summaryMessage = {
152
- role: 'system',
153
+ role: "system",
153
154
  content: `[Conversation summary: ${summaryResponse.content}]`,
154
155
  uuid: `summary-${Date.now()}`,
155
156
  timestamp: Date.now(),
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * Based on the "context engineering" pattern from Anthropic's harness research.
11
11
  */
12
- import type { Message } from '../types/message.js';
12
+ import type { Message } from "../types/message.js";
13
13
  export type ContextBudget = {
14
14
  /** Max tokens for a single tool output */
15
15
  toolOutputMax: number;
@@ -9,8 +9,8 @@
9
9
  *
10
10
  * Based on the "context engineering" pattern from Anthropic's harness research.
11
11
  */
12
- import { getContextWindow } from '../harness/cost.js';
13
- import { estimateMessagesTokens } from './compress.js';
12
+ import { getContextWindow } from "../harness/cost.js";
13
+ import { estimateMessagesTokens } from "./compress.js";
14
14
  const DEFAULT_BUDGET = {
15
15
  toolOutputMax: 10_000,
16
16
  perTool: {},
@@ -46,9 +46,9 @@ export class ContextManager {
46
46
  const keepHead = Math.floor(maxChars * 0.7);
47
47
  const keepTail = Math.floor(maxChars * 0.2);
48
48
  const truncated = output.length - keepHead - keepTail;
49
- return output.slice(0, keepHead)
50
- + `\n\n[...${truncated.toLocaleString()} chars truncated (budget: ${budget} tokens)...]\n\n`
51
- + output.slice(-keepTail);
49
+ return (output.slice(0, keepHead) +
50
+ `\n\n[...${truncated.toLocaleString()} chars truncated (budget: ${budget} tokens)...]\n\n` +
51
+ output.slice(-keepTail));
52
52
  }
53
53
  /**
54
54
  * Fold a sub-agent's full output into a concise summary.
@@ -9,7 +9,7 @@ export function isRateLimitError(err) {
9
9
  }
10
10
  export function isOverloadError(err) {
11
11
  const msg = err.message.toLowerCase();
12
- return msg.includes("503") || msg.includes("overloaded") || msg.includes("service unavailable") || msg.includes("529");
12
+ return (msg.includes("503") || msg.includes("overloaded") || msg.includes("service unavailable") || msg.includes("529"));
13
13
  }
14
14
  export function isPromptTooLongError(err) {
15
15
  const msg = err.message.toLowerCase();
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import type { StreamEvent } from "../types/events.js";
11
11
  import type { QueryConfig } from "./types.js";
12
- export type { QueryConfig, QueryLoopState } from "./types.js";
13
12
  export { compressMessages } from "./compress.js";
13
+ export type { QueryConfig, QueryLoopState } from "./types.js";
14
14
  export declare function query(userMessage: string, config: QueryConfig, existingMessages?: import("../types/message.js").Message[]): AsyncGenerator<StreamEvent, void>;
15
15
  //# sourceMappingURL=index.d.ts.map