clawcompany 0.2.0 → 0.3.0

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 (2) hide show
  1. package/dist/index.js +236 -39
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import { join } from "path";
14
14
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
15
15
  function banner() {
16
16
  console.log("");
17
- console.log(" \u{1F99E} ClawCompany v0.2.0");
17
+ console.log(" \u{1F99E} ClawCompany v0.3.0");
18
18
  console.log(" Build for OPC. Every human being is a chairman.");
19
19
  console.log("");
20
20
  }
@@ -205,7 +205,7 @@ COST AWARENESS:
205
205
  budgetTier: "earn",
206
206
  budgetMonthly: null,
207
207
  maxTokensPerTask: null,
208
- tools: ["http", "filesystem"],
208
+ tools: ["http", "filesystem", "web_fetch", "web_search"],
209
209
  skills: [],
210
210
  isBuiltin: true,
211
211
  isActive: true,
@@ -276,7 +276,7 @@ Own marketing strategy, content creation, brand voice, growth initiatives. Write
276
276
  budgetTier: "earn",
277
277
  budgetMonthly: null,
278
278
  maxTokensPerTask: null,
279
- tools: ["http", "filesystem"],
279
+ tools: ["http", "filesystem", "web_fetch", "web_search"],
280
280
  skills: [],
281
281
  isBuiltin: true,
282
282
  isActive: true,
@@ -300,7 +300,7 @@ Conduct deep research \u2014 gather information, evaluate sources, analyze compe
300
300
  budgetTier: "earn",
301
301
  budgetMonthly: null,
302
302
  maxTokensPerTask: null,
303
- tools: ["http", "filesystem"],
303
+ tools: ["http", "filesystem", "web_fetch", "web_search"],
304
304
  skills: [],
305
305
  isBuiltin: true,
306
306
  isActive: true,
@@ -315,7 +315,7 @@ Conduct deep research \u2014 gather information, evaluate sources, analyze compe
315
315
  systemPrompt: `You are an Analyst. You report to the CFO or CEO.
316
316
 
317
317
  Analyze data, detect patterns, calculate metrics, build models. Show calculations step by step. Present findings in tables. State assumptions. Quantify confidence levels.`,
318
- model: "claude-sonnet-4-6",
318
+ model: "gpt-5-mini",
319
319
  provider: "clawapi",
320
320
  reportsTo: "cfo",
321
321
  canDelegateTo: ["worker"],
@@ -323,7 +323,7 @@ Analyze data, detect patterns, calculate metrics, build models. Show calculation
323
323
  budgetTier: "save",
324
324
  budgetMonthly: null,
325
325
  maxTokensPerTask: null,
326
- tools: ["http", "filesystem", "code_interpreter"],
326
+ tools: ["http", "filesystem", "code_interpreter", "web_fetch", "web_search"],
327
327
  skills: [],
328
328
  isBuiltin: true,
329
329
  isActive: true,
@@ -391,7 +391,7 @@ Prepare briefings, format reports, summarize documents, organize information. Ma
391
391
  budgetTier: "save",
392
392
  budgetMonthly: null,
393
393
  maxTokensPerTask: null,
394
- tools: ["filesystem", "http"],
394
+ tools: ["filesystem", "http", "web_fetch", "web_search"],
395
395
  skills: [],
396
396
  isBuiltin: true,
397
397
  isActive: true,
@@ -1248,8 +1248,7 @@ var OpenAICompatibleProvider = class _OpenAICompatibleProvider {
1248
1248
  // reasoning model timeout → fast model
1249
1249
  "gpt-5.4": "claude-sonnet-4-6",
1250
1250
  // GPT timeout → Sonnet
1251
- "claude-opus-4-6": "claude-sonnet-4-6",
1252
- "claude-sonnet-4-6": "gemini-3.1-flash-lite"
1251
+ "claude-opus-4-6": "claude-sonnet-4-6"
1253
1252
  // Opus timeout → Sonnet
1254
1253
  };
1255
1254
  async chat(params) {
@@ -1270,11 +1269,14 @@ var OpenAICompatibleProvider = class _OpenAICompatibleProvider {
1270
1269
  const url = `${this.baseUrl}/chat/completions`;
1271
1270
  const body = {
1272
1271
  model: params.model,
1273
- messages: params.messages,
1272
+ messages: this.toApiMessages(params.messages),
1274
1273
  temperature: params.temperature ?? 0.7,
1275
1274
  max_tokens: params.maxTokens ?? 4096,
1276
- stream: params.stream ?? false
1275
+ stream: true
1277
1276
  };
1277
+ if (this.isReasoningModel(params.model)) {
1278
+ delete body.temperature;
1279
+ }
1278
1280
  if (params.tools?.length) {
1279
1281
  body.tools = params.tools;
1280
1282
  }
@@ -1294,25 +1296,88 @@ var OpenAICompatibleProvider = class _OpenAICompatibleProvider {
1294
1296
  this.id
1295
1297
  );
1296
1298
  }
1297
- const data = await response.json();
1298
- const choice = data.choices?.[0];
1299
+ let content = "";
1300
+ let model = params.model;
1301
+ let finishReason = "stop";
1302
+ let promptTokens = 0;
1303
+ let completionTokens = 0;
1304
+ const toolCallBuffers = /* @__PURE__ */ new Map();
1305
+ const reader = response.body.getReader();
1306
+ const decoder = new TextDecoder();
1307
+ let buffer = "";
1308
+ while (true) {
1309
+ const { done, value } = await reader.read();
1310
+ if (done) break;
1311
+ buffer += decoder.decode(value, { stream: true });
1312
+ const lines = buffer.split("\n");
1313
+ buffer = lines.pop() ?? "";
1314
+ for (const line of lines) {
1315
+ if (!line.startsWith("data: ") || line === "data: [DONE]") continue;
1316
+ try {
1317
+ const chunk = JSON.parse(line.slice(6));
1318
+ const delta = chunk.choices?.[0]?.delta;
1319
+ if (delta?.content) content += delta.content;
1320
+ if (chunk.model) model = chunk.model;
1321
+ const fr = chunk.choices?.[0]?.finish_reason;
1322
+ if (fr) finishReason = fr;
1323
+ if (delta?.tool_calls) {
1324
+ for (const tc of delta.tool_calls) {
1325
+ const idx = tc.index ?? 0;
1326
+ if (!toolCallBuffers.has(idx)) {
1327
+ toolCallBuffers.set(idx, { id: tc.id ?? `call_${idx}`, name: "", args: "" });
1328
+ }
1329
+ const buf = toolCallBuffers.get(idx);
1330
+ if (tc.id) buf.id = tc.id;
1331
+ if (tc.function?.name) buf.name = tc.function.name;
1332
+ if (tc.function?.arguments) buf.args += tc.function.arguments;
1333
+ }
1334
+ }
1335
+ if (chunk.usage) {
1336
+ promptTokens = chunk.usage.prompt_tokens ?? 0;
1337
+ completionTokens = chunk.usage.completion_tokens ?? 0;
1338
+ }
1339
+ } catch {
1340
+ }
1341
+ }
1342
+ }
1343
+ const toolCalls = [];
1344
+ for (const [, buf] of [...toolCallBuffers.entries()].sort((a, b) => a[0] - b[0])) {
1345
+ toolCalls.push({ id: buf.id, type: "function", function: { name: buf.name, arguments: buf.args } });
1346
+ }
1299
1347
  return {
1300
- content: choice?.message?.content ?? "",
1301
- model: data.model ?? params.model,
1348
+ content,
1349
+ model,
1302
1350
  provider: this.id,
1303
1351
  usage: {
1304
- inputTokens: data.usage?.prompt_tokens ?? 0,
1305
- outputTokens: data.usage?.completion_tokens ?? 0,
1306
- cost: calculateCost(
1307
- params.model,
1308
- data.usage?.prompt_tokens ?? 0,
1309
- data.usage?.completion_tokens ?? 0
1310
- )
1352
+ inputTokens: promptTokens,
1353
+ outputTokens: completionTokens,
1354
+ cost: calculateCost(params.model, promptTokens, completionTokens)
1311
1355
  },
1312
- toolCalls: choice?.message?.tool_calls,
1313
- finishReason: choice?.finish_reason ?? "stop"
1356
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
1357
+ finishReason
1314
1358
  };
1315
1359
  }
1360
+ /** Convert internal message format to OpenAI API format */
1361
+ toApiMessages(messages) {
1362
+ return messages.map((msg) => {
1363
+ const out = {
1364
+ role: msg.role,
1365
+ content: msg.content
1366
+ };
1367
+ if (msg.toolCalls) {
1368
+ out.tool_calls = msg.toolCalls;
1369
+ if (!out.content) out.content = null;
1370
+ }
1371
+ if (msg.role === "tool" && msg.toolCallId) {
1372
+ out.tool_call_id = msg.toolCallId;
1373
+ }
1374
+ if (msg.name) out.name = msg.name;
1375
+ return out;
1376
+ });
1377
+ }
1378
+ isReasoningModel(model) {
1379
+ return /^(o1|o3|gpt-5-mini)/.test(model);
1380
+ }
1316
1381
  async listModels() {
1317
1382
  if (Array.isArray(this.config.models)) {
1318
1383
  return this.config.models;
@@ -1588,6 +1653,10 @@ var ToolExecutor = class {
1588
1653
  return this.execHttp(args);
1589
1654
  case "code_interpreter":
1590
1655
  return this.execCode(args);
1656
+ case "web_fetch":
1657
+ return this.execWebFetch(args);
1658
+ case "web_search":
1659
+ return this.execWebSearch(args);
1591
1660
  default:
1592
1661
  return `Unknown tool: ${toolName}`;
1593
1662
  }
@@ -1648,6 +1717,88 @@ ${text.slice(0, 5e3)}`;
1648
1717
  }
1649
1718
  return `Unsupported language: ${language}`;
1650
1719
  }
1720
+ async execWebFetch(args) {
1721
+ const { url, maxLength } = args;
1722
+ const limit = maxLength ?? 8e3;
1723
+ try {
1724
+ const response = await fetch(url, {
1725
+ headers: {
1726
+ "User-Agent": "ClawCompany/1.0 (AI Agent)",
1727
+ "Accept": "text/html,application/xhtml+xml,text/plain,application/json"
1728
+ },
1729
+ redirect: "follow",
1730
+ signal: AbortSignal.timeout(15e3)
1731
+ });
1732
+ if (!response.ok) {
1733
+ return `Error: HTTP ${response.status} ${response.statusText}`;
1734
+ }
1735
+ const contentType = response.headers.get("content-type") ?? "";
1736
+ const raw = await response.text();
1737
+ if (contentType.includes("application/json")) {
1738
+ return raw.slice(0, limit);
1739
+ }
1740
+ if (contentType.includes("text/html")) {
1741
+ const text = raw.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<nav[^>]*>[\s\S]*?<\/nav>/gi, "").replace(/<footer[^>]*>[\s\S]*?<\/footer>/gi, "").replace(/<header[^>]*>[\s\S]*?<\/header>/gi, "").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/\s+/g, " ").trim();
1742
+ return text.slice(0, limit);
1743
+ }
1744
+ return raw.slice(0, limit);
1745
+ } catch (err) {
1746
+ if (err.name === "TimeoutError") return "Error: Request timed out (15s)";
1747
+ return `Error: ${err.message}`;
1748
+ }
1749
+ }
1750
+ async execWebSearch(args) {
1751
+ const { query, maxResults } = args;
1752
+ const limit = Math.min(maxResults ?? 5, 10);
1753
+ try {
1754
+ const encoded = encodeURIComponent(query);
1755
+ const response = await fetch(`https://html.duckduckgo.com/html/?q=${encoded}`, {
1756
+ headers: {
1757
+ "User-Agent": "ClawCompany/1.0 (AI Agent)"
1758
+ },
1759
+ signal: AbortSignal.timeout(1e4)
1760
+ });
1761
+ if (!response.ok) {
1762
+ return `Error: Search failed with HTTP ${response.status}`;
1763
+ }
1764
+ const html = await response.text();
1765
+ const results = [];
1766
+ const resultBlocks = html.split('class="result__body"');
1767
+ for (let i = 1; i < resultBlocks.length && results.length < limit; i++) {
1768
+ const block = resultBlocks[i];
1769
+ const titleMatch = block.match(/class="result__a"[^>]*>([^<]+)</);
1770
+ const title = titleMatch?.[1]?.trim() ?? "";
1771
+ const urlMatch = block.match(/href="\/\/duckduckgo\.com\/l\/\?[^"]*uddg=([^&"]+)/);
1772
+ const url = urlMatch?.[1] ? decodeURIComponent(urlMatch[1]) : "";
1773
+ const snippetMatch = block.match(/class="result__snippet"[^>]*>([\s\S]*?)<\/a>/);
1774
+ const snippet = snippetMatch?.[1]?.replace(/<[^>]+>/g, "")?.replace(/\s+/g, " ")?.trim() ?? "";
1775
+ if (title && url) {
1776
+ results.push({ title, url, snippet });
1777
+ }
1778
+ }
1779
+ if (results.length === 0) {
1780
+ return `No results found for "${query}"`;
1781
+ }
1782
+ let output = `Search results for "${query}":
1783
+
1784
+ `;
1785
+ for (let i = 0; i < results.length; i++) {
1786
+ output += `${i + 1}. ${results[i].title}
1787
+ `;
1788
+ output += ` URL: ${results[i].url}
1789
+ `;
1790
+ if (results[i].snippet) {
1791
+ output += ` ${results[i].snippet}
1792
+ `;
1793
+ }
1794
+ output += "\n";
1795
+ }
1796
+ return output;
1797
+ } catch (err) {
1798
+ if (err.name === "TimeoutError") return "Error: Search timed out (10s)";
1799
+ return `Error: ${err.message}`;
1800
+ }
1801
+ }
1651
1802
  };
1652
1803
 
1653
1804
  // ../packages/tools/src/index.ts
@@ -1714,6 +1865,36 @@ var BUILTIN_TOOLS = {
1714
1865
  required: ["language", "code"]
1715
1866
  }
1716
1867
  }
1868
+ },
1869
+ web_fetch: {
1870
+ type: "function",
1871
+ function: {
1872
+ name: "web_fetch",
1873
+ description: "Fetch a web page and extract its text content. Use this to read articles, documentation, blog posts, or any public web page. Returns cleaned text without HTML tags.",
1874
+ parameters: {
1875
+ type: "object",
1876
+ properties: {
1877
+ url: { type: "string", description: "URL of the web page to fetch" },
1878
+ maxLength: { type: "number", description: "Max characters to return (default: 8000)" }
1879
+ },
1880
+ required: ["url"]
1881
+ }
1882
+ }
1883
+ },
1884
+ web_search: {
1885
+ type: "function",
1886
+ function: {
1887
+ name: "web_search",
1888
+ description: "Search the web for current information. Returns a list of results with titles, URLs, and snippets. Use this to find up-to-date facts, news, prices, or research topics.",
1889
+ parameters: {
1890
+ type: "object",
1891
+ properties: {
1892
+ query: { type: "string", description: "Search query" },
1893
+ maxResults: { type: "number", description: "Max results to return (default: 5, max: 10)" }
1894
+ },
1895
+ required: ["query"]
1896
+ }
1897
+ }
1717
1898
  }
1718
1899
  };
1719
1900
  function getToolsForRole(toolNames) {
@@ -1739,7 +1920,7 @@ var AgentExecutor = class {
1739
1920
  content: this.buildTaskPrompt(task)
1740
1921
  }
1741
1922
  ];
1742
- const MAX_TURNS = 10;
1923
+ const MAX_TURNS = 15;
1743
1924
  for (let turn = 0; turn < MAX_TURNS; turn++) {
1744
1925
  const response = await this.router.chatAsRole(
1745
1926
  role2.id,
@@ -1951,24 +2132,40 @@ ${truncated}
1951
2132
  `;
1952
2133
  }
1953
2134
  }
1954
- const response = await this.router.chatAsRole(ws.assignTo, [
1955
- {
1956
- role: "user",
1957
- content: `## Task: ${ws.title}
1958
-
1959
- ${ws.description}
2135
+ const role2 = this.router.getRole(ws.assignTo);
2136
+ if (!role2) throw new Error(`Role "${ws.assignTo}" not found`);
2137
+ const result = await this.executor.execute(role2, {
2138
+ id: ws.id,
2139
+ companyId: "default",
2140
+ missionId: ws.missionId,
2141
+ workStreamId: ws.id,
2142
+ title: ws.title,
2143
+ description: `${ws.description}
1960
2144
 
1961
2145
  Complexity: ${ws.estimatedComplexity}${context}
1962
2146
 
1963
- Today's date is ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}. Complete this task. Provide your output clearly and concisely.`
1964
- }
1965
- ]);
2147
+ Today's date is ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.`,
2148
+ assignedTo: ws.assignTo,
2149
+ createdBy: "ceo",
2150
+ reportTo: "ceo",
2151
+ status: "in_progress",
2152
+ priority: 1,
2153
+ tokensIn: 0,
2154
+ tokensOut: 0,
2155
+ cost: 0,
2156
+ modelUsed: role2.model,
2157
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2158
+ });
2159
+ if (result.toolCallCount > 0) {
2160
+ console.log(` \u{1F527} ${result.toolCallCount} tool calls executed`);
2161
+ }
1966
2162
  return {
1967
- content: response.content,
1968
- cost: response.usage.cost,
1969
- tokensIn: response.usage.inputTokens,
1970
- tokensOut: response.usage.outputTokens,
1971
- model: response.model
2163
+ content: result.output,
2164
+ cost: result.cost,
2165
+ tokensIn: result.tokensIn,
2166
+ tokensOut: result.tokensOut,
2167
+ model: result.modelUsed,
2168
+ toolCallCount: result.toolCallCount
1972
2169
  };
1973
2170
  }
1974
2171
  topologicalSort(workStreams) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawcompany",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Build for OPC. Every human being is a chairman. AI company infrastructure — one key, 9 roles, 4 models.",
5
5
  "type": "module",
6
6
  "bin": { "clawcompany": "dist/index.js" },