clawcompany 0.2.0 → 0.4.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 +510 -123
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9,89 +9,6 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
- // src/utils.ts
13
- import { join } from "path";
14
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
15
- function banner() {
16
- console.log("");
17
- console.log(" \u{1F99E} ClawCompany v0.2.0");
18
- console.log(" Build for OPC. Every human being is a chairman.");
19
- console.log("");
20
- }
21
- function getConfigDir() {
22
- const dir = join(process.env.HOME ?? "~", ".clawcompany");
23
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
24
- return dir;
25
- }
26
- function getConfigPath() {
27
- return join(getConfigDir(), "config.json");
28
- }
29
- function configExists() {
30
- return existsSync(getConfigPath());
31
- }
32
- function readConfig() {
33
- if (!configExists()) return null;
34
- try {
35
- return JSON.parse(readFileSync(getConfigPath(), "utf-8"));
36
- } catch {
37
- return null;
38
- }
39
- }
40
- function writeConfig(config) {
41
- writeFileSync(getConfigPath(), JSON.stringify(config, null, 2));
42
- }
43
- async function apiGet(path, port = 3200) {
44
- const res = await fetch(`http://localhost:${port}${path}`);
45
- if (!res.ok) throw new Error(`API error: ${res.status}`);
46
- return res.json();
47
- }
48
- async function apiPost(path, body, port = 3200) {
49
- const res = await fetch(`http://localhost:${port}${path}`, {
50
- method: "POST",
51
- headers: { "Content-Type": "application/json" },
52
- body: JSON.stringify(body)
53
- });
54
- if (!res.ok) {
55
- const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
56
- throw new Error(err.error ?? `API error: ${res.status}`);
57
- }
58
- return res.json();
59
- }
60
- async function validateClawApiKey(key) {
61
- try {
62
- const res = await fetch("https://clawapi.org/api/v1/chat/completions", {
63
- method: "POST",
64
- headers: {
65
- "Authorization": `Bearer ${key}`,
66
- "Content-Type": "application/json"
67
- },
68
- body: JSON.stringify({
69
- model: "gpt-oss-20b",
70
- messages: [{ role: "user", content: "hi" }],
71
- max_tokens: 5
72
- })
73
- });
74
- if (res.status === 401) return { valid: false, error: "Invalid key. Check your key at clawapi.org" };
75
- if (!res.ok) return { valid: false, error: `ClawAPI returned ${res.status}. Try again later.` };
76
- return { valid: true };
77
- } catch (err) {
78
- return { valid: false, error: `Cannot reach ClawAPI: ${err.message}. Check your internet connection.` };
79
- }
80
- }
81
- async function isServerRunning(port = 3200) {
82
- try {
83
- const res = await fetch(`http://localhost:${port}/api/health`);
84
- return res.ok;
85
- } catch {
86
- return false;
87
- }
88
- }
89
- var init_utils = __esm({
90
- "src/utils.ts"() {
91
- "use strict";
92
- }
93
- });
94
-
95
12
  // ../packages/shared/src/types.ts
96
13
  var init_types = __esm({
97
14
  "../packages/shared/src/types.ts"() {
@@ -205,7 +122,7 @@ COST AWARENESS:
205
122
  budgetTier: "earn",
206
123
  budgetMonthly: null,
207
124
  maxTokensPerTask: null,
208
- tools: ["http", "filesystem"],
125
+ tools: ["http", "filesystem", "web_fetch", "web_search"],
209
126
  skills: [],
210
127
  isBuiltin: true,
211
128
  isActive: true,
@@ -276,7 +193,7 @@ Own marketing strategy, content creation, brand voice, growth initiatives. Write
276
193
  budgetTier: "earn",
277
194
  budgetMonthly: null,
278
195
  maxTokensPerTask: null,
279
- tools: ["http", "filesystem"],
196
+ tools: ["http", "filesystem", "web_fetch", "web_search"],
280
197
  skills: [],
281
198
  isBuiltin: true,
282
199
  isActive: true,
@@ -300,7 +217,7 @@ Conduct deep research \u2014 gather information, evaluate sources, analyze compe
300
217
  budgetTier: "earn",
301
218
  budgetMonthly: null,
302
219
  maxTokensPerTask: null,
303
- tools: ["http", "filesystem"],
220
+ tools: ["http", "filesystem", "web_fetch", "web_search"],
304
221
  skills: [],
305
222
  isBuiltin: true,
306
223
  isActive: true,
@@ -315,7 +232,7 @@ Conduct deep research \u2014 gather information, evaluate sources, analyze compe
315
232
  systemPrompt: `You are an Analyst. You report to the CFO or CEO.
316
233
 
317
234
  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",
235
+ model: "gpt-5-mini",
319
236
  provider: "clawapi",
320
237
  reportsTo: "cfo",
321
238
  canDelegateTo: ["worker"],
@@ -323,7 +240,7 @@ Analyze data, detect patterns, calculate metrics, build models. Show calculation
323
240
  budgetTier: "save",
324
241
  budgetMonthly: null,
325
242
  maxTokensPerTask: null,
326
- tools: ["http", "filesystem", "code_interpreter"],
243
+ tools: ["http", "filesystem", "code_interpreter", "web_fetch", "web_search"],
327
244
  skills: [],
328
245
  isBuiltin: true,
329
246
  isActive: true,
@@ -391,7 +308,7 @@ Prepare briefings, format reports, summarize documents, organize information. Ma
391
308
  budgetTier: "save",
392
309
  budgetMonthly: null,
393
310
  maxTokensPerTask: null,
394
- tools: ["filesystem", "http"],
311
+ tools: ["filesystem", "http", "web_fetch", "web_search"],
395
312
  skills: [],
396
313
  isBuiltin: true,
397
314
  isActive: true,
@@ -856,6 +773,153 @@ var init_market = __esm({
856
773
  }
857
774
  });
858
775
 
776
+ // ../packages/shared/src/memory.ts
777
+ function createEmptyMemory() {
778
+ return {
779
+ version: 1,
780
+ chairman: {
781
+ domains: [],
782
+ commonMissions: [],
783
+ preferences: []
784
+ },
785
+ company: {
786
+ missionCount: 0,
787
+ learnings: [],
788
+ domainKnowledge: [],
789
+ goodPatterns: [],
790
+ recentMissions: []
791
+ },
792
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
793
+ };
794
+ }
795
+ function buildMemoryContext(memory) {
796
+ const parts = [];
797
+ const ch = memory.chairman;
798
+ if (ch.language) parts.push(`Chairman language: ${ch.language}`);
799
+ if (ch.outputStyle) parts.push(`Output style: ${ch.outputStyle}`);
800
+ if (ch.domains.length > 0) parts.push(`Focus areas: ${ch.domains.slice(0, 5).join(", ")}`);
801
+ if (ch.preferences.length > 0) parts.push(`Preferences: ${ch.preferences.slice(0, 5).join("; ")}`);
802
+ const co = memory.company;
803
+ if (co.missionCount > 0) parts.push(`Missions completed: ${co.missionCount}`);
804
+ if (co.learnings.length > 0) parts.push(`Key learnings: ${co.learnings.slice(-5).join("; ")}`);
805
+ if (co.domainKnowledge.length > 0) parts.push(`Domain knowledge: ${co.domainKnowledge.slice(-5).join("; ")}`);
806
+ if (co.recentMissions.length > 0) {
807
+ const recent = co.recentMissions.slice(-3).map((m) => m.goal).join("; ");
808
+ parts.push(`Recent missions: ${recent}`);
809
+ }
810
+ if (parts.length === 0) return "";
811
+ return `
812
+
813
+ ## Company Memory
814
+ ${parts.join("\n")}`;
815
+ }
816
+ function updateMemoryFromMission(memory, mission, chairmanMessage) {
817
+ const updated = structuredClone(memory);
818
+ const now = (/* @__PURE__ */ new Date()).toISOString();
819
+ updated.updatedAt = now;
820
+ if (chairmanMessage || mission.goal) {
821
+ const text = chairmanMessage ?? mission.goal;
822
+ const hasChinese = /[\u4e00-\u9fff]/.test(text);
823
+ const hasJapanese = /[\u3040-\u309f\u30a0-\u30ff]/.test(text);
824
+ if (hasChinese) updated.chairman.language = "zh";
825
+ else if (hasJapanese) updated.chairman.language = "ja";
826
+ else if (!updated.chairman.language) updated.chairman.language = "en";
827
+ }
828
+ const domainKeywords = extractDomains(mission.goal);
829
+ for (const d of domainKeywords) {
830
+ if (!updated.chairman.domains.includes(d)) {
831
+ updated.chairman.domains.push(d);
832
+ }
833
+ }
834
+ if (updated.chairman.domains.length > 10) {
835
+ updated.chairman.domains = updated.chairman.domains.slice(-10);
836
+ }
837
+ const missionType = classifyMission(mission.goal);
838
+ if (missionType && !updated.chairman.commonMissions.includes(missionType)) {
839
+ updated.chairman.commonMissions.push(missionType);
840
+ }
841
+ if (updated.chairman.commonMissions.length > 8) {
842
+ updated.chairman.commonMissions = updated.chairman.commonMissions.slice(-8);
843
+ }
844
+ updated.company.missionCount++;
845
+ const rolesUsed = [...new Set(mission.workStreams.map((ws) => ws.assignedTo))];
846
+ updated.company.recentMissions.push({
847
+ goal: mission.goal.slice(0, 100),
848
+ date: now.split("T")[0],
849
+ cost: mission.cost,
850
+ duration: mission.duration,
851
+ workStreamCount: mission.workStreams.length,
852
+ rolesUsed,
853
+ success: mission.workStreams.every((ws) => ws.status === "completed")
854
+ });
855
+ if (updated.company.recentMissions.length > 10) {
856
+ updated.company.recentMissions = updated.company.recentMissions.slice(-10);
857
+ }
858
+ return updated;
859
+ }
860
+ function extractDomains(text) {
861
+ const domains = [];
862
+ const lower = text.toLowerCase();
863
+ const domainMap = {
864
+ bitcoin: "crypto",
865
+ btc: "crypto",
866
+ ethereum: "crypto",
867
+ eth: "crypto",
868
+ "\u6BD4\u7279\u5E01": "crypto",
869
+ "\u4EE5\u592A\u574A": "crypto",
870
+ "\u52A0\u5BC6": "crypto",
871
+ defi: "crypto",
872
+ crypto: "crypto",
873
+ trading: "finance",
874
+ investment: "finance",
875
+ revenue: "finance",
876
+ stock: "finance",
877
+ market: "finance",
878
+ ai: "ai/ml",
879
+ "machine learning": "ai/ml",
880
+ llm: "ai/ml",
881
+ agent: "ai/ml",
882
+ "\u4EBA\u5DE5\u667A\u80FD": "ai/ml",
883
+ marketing: "marketing",
884
+ seo: "marketing",
885
+ content: "marketing",
886
+ brand: "marketing",
887
+ "\u8425\u9500": "marketing",
888
+ code: "engineering",
889
+ api: "engineering",
890
+ software: "engineering",
891
+ deploy: "engineering",
892
+ legal: "legal",
893
+ contract: "legal",
894
+ compliance: "legal",
895
+ ecommerce: "ecommerce",
896
+ product: "ecommerce",
897
+ shopify: "ecommerce"
898
+ };
899
+ for (const [keyword, domain] of Object.entries(domainMap)) {
900
+ if (lower.includes(keyword) && !domains.includes(domain)) {
901
+ domains.push(domain);
902
+ }
903
+ }
904
+ return domains;
905
+ }
906
+ function classifyMission(goal) {
907
+ const lower = goal.toLowerCase();
908
+ if (lower.includes("analyz") || lower.includes("\u5206\u6790")) return "analysis";
909
+ if (lower.includes("research") || lower.includes("\u7814\u7A76") || lower.includes("\u8C03\u7814")) return "research";
910
+ if (lower.includes("write") || lower.includes("draft") || lower.includes("\u5199")) return "writing";
911
+ if (lower.includes("compare") || lower.includes("\u5BF9\u6BD4") || lower.includes("\u6BD4\u8F83")) return "comparison";
912
+ if (lower.includes("build") || lower.includes("create") || lower.includes("\u5F00\u53D1")) return "building";
913
+ if (lower.includes("plan") || lower.includes("strategy") || lower.includes("\u7B56\u7565")) return "strategy";
914
+ if (lower.includes("price") || lower.includes("cost") || lower.includes("\u4EF7\u683C")) return "pricing";
915
+ return null;
916
+ }
917
+ var init_memory = __esm({
918
+ "../packages/shared/src/memory.ts"() {
919
+ "use strict";
920
+ }
921
+ });
922
+
859
923
  // ../packages/shared/src/index.ts
860
924
  var init_src = __esm({
861
925
  "../packages/shared/src/index.ts"() {
@@ -864,6 +928,106 @@ var init_src = __esm({
864
928
  init_defaults();
865
929
  init_api_paths();
866
930
  init_market();
931
+ init_memory();
932
+ }
933
+ });
934
+
935
+ // src/utils.ts
936
+ import { join } from "path";
937
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
938
+ function banner() {
939
+ console.log("");
940
+ console.log(" \u{1F99E} ClawCompany v0.1.0");
941
+ console.log(" Build for OPC. Every human being is a chairman.");
942
+ console.log("");
943
+ }
944
+ function getConfigDir() {
945
+ const dir = join(process.env.HOME ?? "~", ".clawcompany");
946
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
947
+ return dir;
948
+ }
949
+ function getConfigPath() {
950
+ return join(getConfigDir(), "config.json");
951
+ }
952
+ function configExists() {
953
+ return existsSync(getConfigPath());
954
+ }
955
+ function readConfig() {
956
+ if (!configExists()) return null;
957
+ try {
958
+ return JSON.parse(readFileSync(getConfigPath(), "utf-8"));
959
+ } catch {
960
+ return null;
961
+ }
962
+ }
963
+ function writeConfig(config) {
964
+ writeFileSync(getConfigPath(), JSON.stringify(config, null, 2));
965
+ }
966
+ function getMemoryPath() {
967
+ return join(getConfigDir(), "memory.json");
968
+ }
969
+ function readMemory() {
970
+ try {
971
+ if (existsSync(getMemoryPath())) {
972
+ return JSON.parse(readFileSync(getMemoryPath(), "utf-8"));
973
+ }
974
+ } catch {
975
+ }
976
+ return createEmptyMemory();
977
+ }
978
+ function writeMemory(memory) {
979
+ writeFileSync(getMemoryPath(), JSON.stringify(memory, null, 2));
980
+ }
981
+ async function apiGet(path, port = 3200) {
982
+ const res = await fetch(`http://localhost:${port}${path}`);
983
+ if (!res.ok) throw new Error(`API error: ${res.status}`);
984
+ return res.json();
985
+ }
986
+ async function apiPost(path, body, port = 3200) {
987
+ const res = await fetch(`http://localhost:${port}${path}`, {
988
+ method: "POST",
989
+ headers: { "Content-Type": "application/json" },
990
+ body: JSON.stringify(body)
991
+ });
992
+ if (!res.ok) {
993
+ const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
994
+ throw new Error(err.error ?? `API error: ${res.status}`);
995
+ }
996
+ return res.json();
997
+ }
998
+ async function validateClawApiKey(key) {
999
+ try {
1000
+ const res = await fetch("https://clawapi.org/api/v1/chat/completions", {
1001
+ method: "POST",
1002
+ headers: {
1003
+ "Authorization": `Bearer ${key}`,
1004
+ "Content-Type": "application/json"
1005
+ },
1006
+ body: JSON.stringify({
1007
+ model: "gpt-oss-20b",
1008
+ messages: [{ role: "user", content: "hi" }],
1009
+ max_tokens: 5
1010
+ })
1011
+ });
1012
+ if (res.status === 401) return { valid: false, error: "Invalid key. Check your key at clawapi.org" };
1013
+ if (!res.ok) return { valid: false, error: `ClawAPI returned ${res.status}. Try again later.` };
1014
+ return { valid: true };
1015
+ } catch (err) {
1016
+ return { valid: false, error: `Cannot reach ClawAPI: ${err.message}. Check your internet connection.` };
1017
+ }
1018
+ }
1019
+ async function isServerRunning(port = 3200) {
1020
+ try {
1021
+ const res = await fetch(`http://localhost:${port}/api/health`);
1022
+ return res.ok;
1023
+ } catch {
1024
+ return false;
1025
+ }
1026
+ }
1027
+ var init_utils = __esm({
1028
+ "src/utils.ts"() {
1029
+ "use strict";
1030
+ init_src();
867
1031
  }
868
1032
  });
869
1033
 
@@ -1248,8 +1412,7 @@ var OpenAICompatibleProvider = class _OpenAICompatibleProvider {
1248
1412
  // reasoning model timeout → fast model
1249
1413
  "gpt-5.4": "claude-sonnet-4-6",
1250
1414
  // GPT timeout → Sonnet
1251
- "claude-opus-4-6": "claude-sonnet-4-6",
1252
- "claude-sonnet-4-6": "gemini-3.1-flash-lite"
1415
+ "claude-opus-4-6": "claude-sonnet-4-6"
1253
1416
  // Opus timeout → Sonnet
1254
1417
  };
1255
1418
  async chat(params) {
@@ -1270,11 +1433,14 @@ var OpenAICompatibleProvider = class _OpenAICompatibleProvider {
1270
1433
  const url = `${this.baseUrl}/chat/completions`;
1271
1434
  const body = {
1272
1435
  model: params.model,
1273
- messages: params.messages,
1436
+ messages: this.toApiMessages(params.messages),
1274
1437
  temperature: params.temperature ?? 0.7,
1275
1438
  max_tokens: params.maxTokens ?? 4096,
1276
- stream: params.stream ?? false
1439
+ stream: true
1277
1440
  };
1441
+ if (this.isReasoningModel(params.model)) {
1442
+ delete body.temperature;
1443
+ }
1278
1444
  if (params.tools?.length) {
1279
1445
  body.tools = params.tools;
1280
1446
  }
@@ -1294,25 +1460,88 @@ var OpenAICompatibleProvider = class _OpenAICompatibleProvider {
1294
1460
  this.id
1295
1461
  );
1296
1462
  }
1297
- const data = await response.json();
1298
- const choice = data.choices?.[0];
1463
+ let content = "";
1464
+ let model = params.model;
1465
+ let finishReason = "stop";
1466
+ let promptTokens = 0;
1467
+ let completionTokens = 0;
1468
+ const toolCallBuffers = /* @__PURE__ */ new Map();
1469
+ const reader = response.body.getReader();
1470
+ const decoder = new TextDecoder();
1471
+ let buffer = "";
1472
+ while (true) {
1473
+ const { done, value } = await reader.read();
1474
+ if (done) break;
1475
+ buffer += decoder.decode(value, { stream: true });
1476
+ const lines = buffer.split("\n");
1477
+ buffer = lines.pop() ?? "";
1478
+ for (const line of lines) {
1479
+ if (!line.startsWith("data: ") || line === "data: [DONE]") continue;
1480
+ try {
1481
+ const chunk = JSON.parse(line.slice(6));
1482
+ const delta = chunk.choices?.[0]?.delta;
1483
+ if (delta?.content) content += delta.content;
1484
+ if (chunk.model) model = chunk.model;
1485
+ const fr = chunk.choices?.[0]?.finish_reason;
1486
+ if (fr) finishReason = fr;
1487
+ if (delta?.tool_calls) {
1488
+ for (const tc of delta.tool_calls) {
1489
+ const idx = tc.index ?? 0;
1490
+ if (!toolCallBuffers.has(idx)) {
1491
+ toolCallBuffers.set(idx, { id: tc.id ?? `call_${idx}`, name: "", args: "" });
1492
+ }
1493
+ const buf = toolCallBuffers.get(idx);
1494
+ if (tc.id) buf.id = tc.id;
1495
+ if (tc.function?.name) buf.name = tc.function.name;
1496
+ if (tc.function?.arguments) buf.args += tc.function.arguments;
1497
+ }
1498
+ }
1499
+ if (chunk.usage) {
1500
+ promptTokens = chunk.usage.prompt_tokens ?? 0;
1501
+ completionTokens = chunk.usage.completion_tokens ?? 0;
1502
+ }
1503
+ } catch {
1504
+ }
1505
+ }
1506
+ }
1507
+ const toolCalls = [];
1508
+ for (const [, buf] of [...toolCallBuffers.entries()].sort((a, b) => a[0] - b[0])) {
1509
+ toolCalls.push({ id: buf.id, type: "function", function: { name: buf.name, arguments: buf.args } });
1510
+ }
1299
1511
  return {
1300
- content: choice?.message?.content ?? "",
1301
- model: data.model ?? params.model,
1512
+ content,
1513
+ model,
1302
1514
  provider: this.id,
1303
1515
  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
- )
1516
+ inputTokens: promptTokens,
1517
+ outputTokens: completionTokens,
1518
+ cost: calculateCost(params.model, promptTokens, completionTokens)
1311
1519
  },
1312
- toolCalls: choice?.message?.tool_calls,
1313
- finishReason: choice?.finish_reason ?? "stop"
1520
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
1521
+ finishReason
1314
1522
  };
1315
1523
  }
1524
+ /** Convert internal message format to OpenAI API format */
1525
+ toApiMessages(messages) {
1526
+ return messages.map((msg) => {
1527
+ const out = {
1528
+ role: msg.role,
1529
+ content: msg.content
1530
+ };
1531
+ if (msg.toolCalls) {
1532
+ out.tool_calls = msg.toolCalls;
1533
+ if (!out.content) out.content = null;
1534
+ }
1535
+ if (msg.role === "tool" && msg.toolCallId) {
1536
+ out.tool_call_id = msg.toolCallId;
1537
+ }
1538
+ if (msg.name) out.name = msg.name;
1539
+ return out;
1540
+ });
1541
+ }
1542
+ isReasoningModel(model) {
1543
+ return /^(o1|o3|gpt-5-mini)/.test(model);
1544
+ }
1316
1545
  async listModels() {
1317
1546
  if (Array.isArray(this.config.models)) {
1318
1547
  return this.config.models;
@@ -1588,6 +1817,10 @@ var ToolExecutor = class {
1588
1817
  return this.execHttp(args);
1589
1818
  case "code_interpreter":
1590
1819
  return this.execCode(args);
1820
+ case "web_fetch":
1821
+ return this.execWebFetch(args);
1822
+ case "web_search":
1823
+ return this.execWebSearch(args);
1591
1824
  default:
1592
1825
  return `Unknown tool: ${toolName}`;
1593
1826
  }
@@ -1648,6 +1881,88 @@ ${text.slice(0, 5e3)}`;
1648
1881
  }
1649
1882
  return `Unsupported language: ${language}`;
1650
1883
  }
1884
+ async execWebFetch(args) {
1885
+ const { url, maxLength } = args;
1886
+ const limit = maxLength ?? 8e3;
1887
+ try {
1888
+ const response = await fetch(url, {
1889
+ headers: {
1890
+ "User-Agent": "ClawCompany/1.0 (AI Agent)",
1891
+ "Accept": "text/html,application/xhtml+xml,text/plain,application/json"
1892
+ },
1893
+ redirect: "follow",
1894
+ signal: AbortSignal.timeout(15e3)
1895
+ });
1896
+ if (!response.ok) {
1897
+ return `Error: HTTP ${response.status} ${response.statusText}`;
1898
+ }
1899
+ const contentType = response.headers.get("content-type") ?? "";
1900
+ const raw = await response.text();
1901
+ if (contentType.includes("application/json")) {
1902
+ return raw.slice(0, limit);
1903
+ }
1904
+ if (contentType.includes("text/html")) {
1905
+ 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();
1906
+ return text.slice(0, limit);
1907
+ }
1908
+ return raw.slice(0, limit);
1909
+ } catch (err) {
1910
+ if (err.name === "TimeoutError") return "Error: Request timed out (15s)";
1911
+ return `Error: ${err.message}`;
1912
+ }
1913
+ }
1914
+ async execWebSearch(args) {
1915
+ const { query, maxResults } = args;
1916
+ const limit = Math.min(maxResults ?? 5, 10);
1917
+ try {
1918
+ const encoded = encodeURIComponent(query);
1919
+ const response = await fetch(`https://html.duckduckgo.com/html/?q=${encoded}`, {
1920
+ headers: {
1921
+ "User-Agent": "ClawCompany/1.0 (AI Agent)"
1922
+ },
1923
+ signal: AbortSignal.timeout(1e4)
1924
+ });
1925
+ if (!response.ok) {
1926
+ return `Error: Search failed with HTTP ${response.status}`;
1927
+ }
1928
+ const html = await response.text();
1929
+ const results = [];
1930
+ const resultBlocks = html.split('class="result__body"');
1931
+ for (let i = 1; i < resultBlocks.length && results.length < limit; i++) {
1932
+ const block = resultBlocks[i];
1933
+ const titleMatch = block.match(/class="result__a"[^>]*>([^<]+)</);
1934
+ const title = titleMatch?.[1]?.trim() ?? "";
1935
+ const urlMatch = block.match(/href="\/\/duckduckgo\.com\/l\/\?[^"]*uddg=([^&"]+)/);
1936
+ const url = urlMatch?.[1] ? decodeURIComponent(urlMatch[1]) : "";
1937
+ const snippetMatch = block.match(/class="result__snippet"[^>]*>([\s\S]*?)<\/a>/);
1938
+ const snippet = snippetMatch?.[1]?.replace(/<[^>]+>/g, "")?.replace(/\s+/g, " ")?.trim() ?? "";
1939
+ if (title && url) {
1940
+ results.push({ title, url, snippet });
1941
+ }
1942
+ }
1943
+ if (results.length === 0) {
1944
+ return `No results found for "${query}"`;
1945
+ }
1946
+ let output = `Search results for "${query}":
1947
+
1948
+ `;
1949
+ for (let i = 0; i < results.length; i++) {
1950
+ output += `${i + 1}. ${results[i].title}
1951
+ `;
1952
+ output += ` URL: ${results[i].url}
1953
+ `;
1954
+ if (results[i].snippet) {
1955
+ output += ` ${results[i].snippet}
1956
+ `;
1957
+ }
1958
+ output += "\n";
1959
+ }
1960
+ return output;
1961
+ } catch (err) {
1962
+ if (err.name === "TimeoutError") return "Error: Search timed out (10s)";
1963
+ return `Error: ${err.message}`;
1964
+ }
1965
+ }
1651
1966
  };
1652
1967
 
1653
1968
  // ../packages/tools/src/index.ts
@@ -1714,6 +2029,36 @@ var BUILTIN_TOOLS = {
1714
2029
  required: ["language", "code"]
1715
2030
  }
1716
2031
  }
2032
+ },
2033
+ web_fetch: {
2034
+ type: "function",
2035
+ function: {
2036
+ name: "web_fetch",
2037
+ 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.",
2038
+ parameters: {
2039
+ type: "object",
2040
+ properties: {
2041
+ url: { type: "string", description: "URL of the web page to fetch" },
2042
+ maxLength: { type: "number", description: "Max characters to return (default: 8000)" }
2043
+ },
2044
+ required: ["url"]
2045
+ }
2046
+ }
2047
+ },
2048
+ web_search: {
2049
+ type: "function",
2050
+ function: {
2051
+ name: "web_search",
2052
+ 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.",
2053
+ parameters: {
2054
+ type: "object",
2055
+ properties: {
2056
+ query: { type: "string", description: "Search query" },
2057
+ maxResults: { type: "number", description: "Max results to return (default: 5, max: 10)" }
2058
+ },
2059
+ required: ["query"]
2060
+ }
2061
+ }
1717
2062
  }
1718
2063
  };
1719
2064
  function getToolsForRole(toolNames) {
@@ -1739,7 +2084,7 @@ var AgentExecutor = class {
1739
2084
  content: this.buildTaskPrompt(task)
1740
2085
  }
1741
2086
  ];
1742
- const MAX_TURNS = 10;
2087
+ const MAX_TURNS = 15;
1743
2088
  for (let turn = 0; turn < MAX_TURNS; turn++) {
1744
2089
  const response = await this.router.chatAsRole(
1745
2090
  role2.id,
@@ -1814,6 +2159,11 @@ var TaskOrchestrator = class {
1814
2159
  this.executor = new AgentExecutor(router, new ToolExecutor());
1815
2160
  }
1816
2161
  executor;
2162
+ memoryContext = "";
2163
+ /** Set company memory context to inject into all agent prompts */
2164
+ setMemoryContext(ctx) {
2165
+ this.memoryContext = ctx;
2166
+ }
1817
2167
  /**
1818
2168
  * Phase 2: CEO decomposes mission into work streams.
1819
2169
  * Human (Chairman) gives the mission → CEO breaks it down.
@@ -1832,7 +2182,8 @@ var TaskOrchestrator = class {
1832
2182
  Priority: ${mission.priority}
1833
2183
  ${mission.deadline ? `Deadline: ${mission.deadline}` : ""}
1834
2184
  ${mission.budgetLimit ? `Budget limit: $${mission.budgetLimit}` : ""}
1835
-
2185
+ ${this.memoryContext ? `${this.memoryContext}
2186
+ ` : ""}
1836
2187
  Your team:
1837
2188
  ${roleList}
1838
2189
 
@@ -1951,24 +2302,41 @@ ${truncated}
1951
2302
  `;
1952
2303
  }
1953
2304
  }
1954
- const response = await this.router.chatAsRole(ws.assignTo, [
1955
- {
1956
- role: "user",
1957
- content: `## Task: ${ws.title}
1958
-
1959
- ${ws.description}
2305
+ const role2 = this.router.getRole(ws.assignTo);
2306
+ if (!role2) throw new Error(`Role "${ws.assignTo}" not found`);
2307
+ const result = await this.executor.execute(role2, {
2308
+ id: ws.id,
2309
+ companyId: "default",
2310
+ missionId: ws.missionId,
2311
+ workStreamId: ws.id,
2312
+ title: ws.title,
2313
+ description: `${ws.description}
1960
2314
 
1961
- Complexity: ${ws.estimatedComplexity}${context}
2315
+ Complexity: ${ws.estimatedComplexity}${context}${this.memoryContext ? `
2316
+ ${this.memoryContext}` : ""}
1962
2317
 
1963
- Today's date is ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}. Complete this task. Provide your output clearly and concisely.`
1964
- }
1965
- ]);
2318
+ Today's date is ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.`,
2319
+ assignedTo: ws.assignTo,
2320
+ createdBy: "ceo",
2321
+ reportTo: "ceo",
2322
+ status: "in_progress",
2323
+ priority: 1,
2324
+ tokensIn: 0,
2325
+ tokensOut: 0,
2326
+ cost: 0,
2327
+ modelUsed: role2.model,
2328
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2329
+ });
2330
+ if (result.toolCallCount > 0) {
2331
+ console.log(` \u{1F527} ${result.toolCallCount} tool calls executed`);
2332
+ }
1966
2333
  return {
1967
- content: response.content,
1968
- cost: response.usage.cost,
1969
- tokensIn: response.usage.inputTokens,
1970
- tokensOut: response.usage.outputTokens,
1971
- model: response.model
2334
+ content: result.output,
2335
+ cost: result.cost,
2336
+ tokensIn: result.tokensIn,
2337
+ tokensOut: result.tokensOut,
2338
+ model: result.modelUsed,
2339
+ toolCallCount: result.toolCallCount
1972
2340
  };
1973
2341
  }
1974
2342
  topologicalSort(workStreams) {
@@ -2033,6 +2401,12 @@ async function runInProcess(goal, userConfig) {
2033
2401
  await registry.loadFromConfig(clawConfig.providers);
2034
2402
  const router = new ModelRouter(registry, clawConfig);
2035
2403
  const orchestrator = new TaskOrchestrator(router);
2404
+ const memory = readMemory();
2405
+ const memoryCtx = buildMemoryContext(memory);
2406
+ if (memoryCtx) {
2407
+ orchestrator.setMemoryContext(memoryCtx);
2408
+ console.log(" \u{1F9E0} Company memory loaded\n");
2409
+ }
2036
2410
  console.log(" Phase 2: CEO decomposing...");
2037
2411
  const mission = {
2038
2412
  id: `mission-${Date.now()}`,
@@ -2050,6 +2424,19 @@ async function runInProcess(goal, userConfig) {
2050
2424
  console.log(" Phase 3-5: Executing...");
2051
2425
  const report = await orchestrator.executeMission(mission, workStreams);
2052
2426
  console.log(" Phase 6: Delivering to Chairman\n");
2427
+ const updatedMemory = updateMemoryFromMission(memory, {
2428
+ goal,
2429
+ cost: report.totalCost,
2430
+ duration: report.totalTimeSeconds,
2431
+ workStreams: report.workStreams.map((ws) => ({
2432
+ title: ws.title,
2433
+ assignedTo: ws.assignedTo,
2434
+ status: ws.status,
2435
+ output: ws.output
2436
+ }))
2437
+ }, goal);
2438
+ writeMemory(updatedMemory);
2439
+ console.log(" \u{1F9E0} Memory updated\n");
2053
2440
  const result = {
2054
2441
  status: "completed",
2055
2442
  mission: report.mission,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawcompany",
3
- "version": "0.2.0",
3
+ "version": "0.4.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" },