@kroxy/kroxy 1.0.17 → 1.0.18

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.
@@ -2,8 +2,9 @@
2
2
  * kroxy_autoagent — Autonomous multi-step orchestrator.
3
3
  *
4
4
  * Takes a natural-language goal, decomposes it into 2–4 subtasks via Claude
5
- * (or a rule-based fallback if ANTHROPIC_API_KEY is unset), fires kroxy_hire
6
- * for each subtask sequentially, and returns a unified deliverable.
5
+ * through Lava's gateway (or a rule-based fallback if keys are unset), fires
6
+ * kroxy_hire for all subtasks in parallel, then synthesizes outputs into a
7
+ * unified deliverable via Lava.
7
8
  */
8
9
  import { Type } from '@sinclair/typebox';
9
10
  import { executeHire } from './hire.js';
@@ -20,8 +21,9 @@ export const autoagentParams = Type.Object({
20
21
  });
21
22
  // ─── Decomposition ────────────────────────────────────────────────────────────
22
23
  /**
23
- * Ask Claude to decompose the goal into 2–4 subtasks.
24
- * Falls back to rule-based decomposer if ANTHROPIC_API_KEY is absent.
24
+ * Ask Claude (via Lava) to decompose the goal into 2–4 subtasks.
25
+ * Falls back to keyword-based decomposer if LAVA_SECRET_KEY or
26
+ * ANTHROPIC_API_KEY are not set.
25
27
  */
26
28
  async function decomposeGoal(goal) {
27
29
  const hasLava = process.env.LAVA_SECRET_KEY && process.env.ANTHROPIC_API_KEY;
@@ -39,13 +41,13 @@ async function decomposeGoal(goal) {
39
41
  });
40
42
  const parsed = JSON.parse(text.trim());
41
43
  if (Array.isArray(parsed) && parsed.length > 0)
42
- return parsed.slice(0, 4);
44
+ return { subtasks: parsed.slice(0, 4), viaLava: true };
43
45
  }
44
46
  catch {
45
47
  // Fall through to rule-based
46
48
  }
47
49
  }
48
- return ruleBasedDecompose(goal);
50
+ return { subtasks: ruleBasedDecompose(goal), viaLava: false };
49
51
  }
50
52
  /** Keyword-based decomposer — works without any API key. */
51
53
  function ruleBasedDecompose(goal) {
@@ -70,30 +72,73 @@ function ruleBasedDecompose(goal) {
70
72
  }
71
73
  return subtasks.slice(0, 4);
72
74
  }
75
+ // ─── Synthesis ────────────────────────────────────────────────────────────────
76
+ /**
77
+ * Use Lava/Claude to merge individual subtask deliverables into a single
78
+ * coherent summary. Best-effort — silently skipped if Lava is unavailable.
79
+ */
80
+ async function synthesizeDeliverables(goal, receipts) {
81
+ const hasLava = process.env.LAVA_SECRET_KEY && process.env.ANTHROPIC_API_KEY;
82
+ if (!hasLava || receipts.length === 0)
83
+ return null;
84
+ const snippets = receipts
85
+ .filter(d => d?.deliverable?.summary)
86
+ .map((d, i) => `[Subtask ${i + 1}]\n${d.deliverable.summary}`)
87
+ .join('\n\n');
88
+ if (!snippets)
89
+ return null;
90
+ try {
91
+ return await lavaAnthropic({
92
+ model: 'claude-haiku-4-5-20251001',
93
+ max_tokens: 512,
94
+ system: 'You are a synthesis assistant. Given outputs from multiple AI agent subtasks, ' +
95
+ 'write a concise unified summary (3–5 sentences) that integrates all key findings ' +
96
+ 'into a coherent deliverable for the original goal.',
97
+ messages: [{ role: 'user', content: `Original goal: ${goal}\n\nSubtask outputs:\n${snippets}` }],
98
+ });
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
73
104
  // ─── Orchestrator ─────────────────────────────────────────────────────────────
74
105
  export async function executeAutoagent(params) {
75
106
  const goal = params.goal;
76
107
  const maxBudget = params.maxBudget ?? 20;
77
108
  // 1. Decompose
78
- const subtasks = await decomposeGoal(goal);
109
+ const { subtasks, viaLava } = await decomposeGoal(goal);
79
110
  const pricePerTask = parseFloat((maxBudget / subtasks.length).toFixed(2));
111
+ const modeLabel = viaLava
112
+ ? 'LLM decomposition via Lava'
113
+ : '⚠ Keyword fallback — set LAVA_SECRET_KEY + ANTHROPIC_API_KEY for intelligent decomposition';
80
114
  const lines = [
81
115
  `AutoAgent: ${goal.slice(0, 80)}`,
82
116
  `${'─'.repeat(50)}`,
83
117
  `Budget: $${maxBudget} USDC across ${subtasks.length} subtask${subtasks.length === 1 ? '' : 's'} ($${pricePerTask} each)`,
118
+ `Mode: ${modeLabel}`,
84
119
  '',
85
120
  ];
121
+ // 2. List all subtasks upfront before firing
122
+ subtasks.forEach(({ task, capability }, i) => {
123
+ lines.push(`[${i + 1}/${subtasks.length}] ${capability.toUpperCase()}: ${task.slice(0, 70)}`);
124
+ });
125
+ lines.push('', `Firing all ${subtasks.length} subtask${subtasks.length === 1 ? '' : 's'} in parallel…`, '');
126
+ // 3. Fire all hires concurrently
127
+ const settled = await Promise.allSettled(
128
+ subtasks.map(({ task, capability }) =>
129
+ executeHire({ task, maxPrice: pricePerTask, capability })
130
+ )
131
+ );
86
132
  const receipts = [];
87
133
  let totalSpent = 0;
88
- // 2. Fire kroxy_hire for each subtask sequentially
89
- for (let i = 0; i < subtasks.length; i++) {
134
+ for (let i = 0; i < settled.length; i++) {
135
+ const result = settled[i];
90
136
  const { task, capability } = subtasks[i];
91
- lines.push(`[${i + 1}/${subtasks.length}] ${capability.toUpperCase()}: ${task.slice(0, 70)}`);
92
- try {
93
- const result = await executeHire({ task, maxPrice: pricePerTask, capability });
94
- const d = result.details;
95
- const paid = parseFloat(d?.amountPaid ?? '0');
96
- totalSpent += paid;
137
+ lines.push(`[${i + 1}] ${capability.toUpperCase()}: ${task.slice(0, 60)}`);
138
+ if (result.status === 'fulfilled') {
139
+ const d = result.value.details;
140
+ const paid = parseFloat(String(d?.amountPaid ?? '0'));
141
+ totalSpent += isNaN(paid) ? 0 : paid;
97
142
  lines.push(` ✓ Completed in ${d?.duration ?? '?'} — paid $${d?.amountPaid ?? '?'}`);
98
143
  lines.push(` Escrow: ${d?.escrowId ?? '?'}`);
99
144
  if (d?.deliverable?.summary) {
@@ -102,15 +147,20 @@ export async function executeAutoagent(params) {
102
147
  lines.push('');
103
148
  receipts.push(d);
104
149
  }
105
- catch (err) {
106
- const msg = err instanceof Error ? err.message : String(err);
150
+ else {
151
+ const msg = result.reason instanceof Error ? result.reason.message : String(result.reason);
107
152
  lines.push(` ✗ Failed: ${msg.slice(0, 120)}`);
108
153
  lines.push('');
109
154
  }
110
155
  }
156
+ // 4. Synthesize outputs into a unified deliverable (via Lava — best-effort)
157
+ const synthesis = await synthesizeDeliverables(goal, receipts);
111
158
  lines.push(`${'─'.repeat(50)}`);
112
159
  lines.push(`Total spent: $${totalSpent.toFixed(2)} USDC / $${maxBudget} budget`);
113
160
  lines.push(`Subtasks completed: ${receipts.length}/${subtasks.length}`);
161
+ if (synthesis) {
162
+ lines.push('', '── Unified Deliverable ──', synthesis);
163
+ }
114
164
  return {
115
165
  content: [{ type: 'text', text: lines.join('\n') }],
116
166
  details: {
@@ -119,7 +169,8 @@ export async function executeAutoagent(params) {
119
169
  receipts,
120
170
  totalSpent: totalSpent.toFixed(2),
121
171
  budgetUsed: `$${totalSpent.toFixed(2)} / $${maxBudget}`,
172
+ synthesis: synthesis ?? undefined,
122
173
  },
123
174
  };
124
175
  }
125
- //# sourceMappingURL=autoagent.js.map
176
+ //# sourceMappingURL=autoagent.js.map
@@ -5,16 +5,19 @@ const DEFAULT_API_URL = 'https://api-production-1b45.up.railway.app';
5
5
  export const hireParams = Type.Object({
6
6
  task: Type.String({ description: 'What you want the hired agent to do, e.g. "Research the top AI payment startups"' }),
7
7
  maxPrice: Type.Optional(Type.Number({ minimum: 0.01, description: 'Maximum USDC budget, default 5.00' })),
8
- capability: Type.Optional(Type.String({ description: 'Agent capability to match: research, writing, coding. Auto-detected from task if omitted.' })),
8
+ capability: Type.Optional(Type.String({ description: 'Agent capability to match: research, writing, coding, planning. Auto-detected from task if omitted.' })),
9
9
  minRep: Type.Optional(Type.Number({ minimum: 0, maximum: 100, description: 'Minimum reputation score (0–100) required. Default: 0 (any agent accepted).' })),
10
10
  });
11
11
  function detectCapability(task) {
12
- if (/research|find|search|analyz|summariz|look up/i.test(task))
13
- return 'research';
14
- if (/write|draft|essay|blog|article|copy/i.test(task))
15
- return 'writing';
16
- if (/code|script|function|implement|build|program/i.test(task))
12
+ // Check coding first — "build" would otherwise fall through to planning
13
+ if (/code|script|function|implement|develop|engineer|program|smart contract/i.test(task))
17
14
  return 'coding';
15
+ if (/write|draft|essay|blog|article|copy|content|landing page|marketing|email/i.test(task))
16
+ return 'writing';
17
+ if (/plan|roadmap|strateg|architect|design|outline|milestone|go-to-market/i.test(task))
18
+ return 'planning';
19
+ if (/research|find|search|analyz|summariz|look up|investigat|gather|survey/i.test(task))
20
+ return 'research';
18
21
  return 'research';
19
22
  }
20
23
  function buildConditions(nexusUrl, jobId) {
@@ -16,6 +16,8 @@
16
16
  "KROXY_AGENT_WALLET": { "type": "string" },
17
17
  "KROXY_AGENT_PRIVATE_KEY": { "type": "string" },
18
18
  "NEXUS_URL": { "type": "string" },
19
+ "LAVA_SECRET_KEY": { "type": "string" },
20
+ "ANTHROPIC_API_KEY": { "type": "string" },
19
21
  "KROXY_DEMO_MODE": {
20
22
  "type": "string",
21
23
  "default": "1"
@@ -31,6 +33,14 @@
31
33
  "KROXY_AGENT_PRIVATE_KEY": {
32
34
  "label": "Kroxy Private Key",
33
35
  "sensitive": true
36
+ },
37
+ "LAVA_SECRET_KEY": {
38
+ "label": "Lava Secret Key (enables intelligent decomposition)",
39
+ "sensitive": true
40
+ },
41
+ "ANTHROPIC_API_KEY": {
42
+ "label": "Anthropic API Key (required with Lava)",
43
+ "sensitive": true
34
44
  }
35
45
  }
36
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kroxy/kroxy",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "type": "module",
5
5
  "description": "Trustless agent-to-agent payments via USDC escrow on Base blockchain",
6
6
  "license": "MIT",