@zhive/cli 0.6.2 → 0.6.3
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.
- package/dist/{services/agent → agent}/analysis.js +5 -5
- package/dist/agent/app.js +122 -0
- package/dist/agent/commands/registry.js +12 -0
- package/dist/agent/components/AsciiTicker.js +81 -0
- package/dist/agent/components/CommandInput.js +65 -0
- package/dist/agent/components/HoneycombBoot.js +291 -0
- package/dist/agent/components/Spinner.js +37 -0
- package/dist/agent/hooks/useAgent.js +480 -0
- package/dist/{services/agent/prompts → agent}/memory-prompt.js +22 -20
- package/dist/{services/agent/helpers → agent}/model.js +2 -2
- package/dist/agent/process-lifecycle.js +18 -0
- package/dist/{services/agent/prompts → agent}/prompt.js +54 -80
- package/dist/agent/run-headless.js +189 -0
- package/dist/agent/theme.js +41 -0
- package/dist/{services/agent → agent}/tools/market/client.js +1 -1
- package/dist/{services/agent → agent}/tools/mindshare/client.js +1 -1
- package/dist/agent/types.js +1 -0
- package/dist/{services/config/agent.js → agents.js} +2 -2
- package/dist/avatar.js +34 -0
- package/dist/backtest/default-backtest-data.js +200 -0
- package/dist/backtest/fetch.js +41 -0
- package/dist/backtest/import.js +106 -0
- package/dist/backtest/index.js +10 -0
- package/dist/backtest/results.js +113 -0
- package/dist/backtest/runner.js +134 -0
- package/dist/backtest/storage.js +11 -0
- package/dist/backtest/types.js +1 -0
- package/dist/commands/install.js +50 -0
- package/dist/commands/megathread/commands/create-comment.js +1 -4
- package/dist/commands/megathread/commands/create-comment.test.js +1 -19
- package/dist/commands/megathread/commands/list.test.js +1 -0
- package/dist/commands/start/commands/prediction.js +1 -2
- package/dist/commands/start/hooks/utils.js +3 -3
- package/dist/commands/start/ui/PollText.js +23 -0
- package/dist/commands/start/ui/PredictionsPanel.js +88 -0
- package/dist/commands/start/ui/SpinnerContext.js +20 -0
- package/dist/components/InputGuard.js +6 -0
- package/dist/components/stdout-spinner.js +48 -0
- package/dist/{services/config/config.js → config.js} +7 -1
- package/dist/create/CreateApp.js +153 -0
- package/dist/create/ai-generate.js +147 -0
- package/dist/create/generate.js +73 -0
- package/dist/create/steps/ApiKeyStep.js +97 -0
- package/dist/create/steps/AvatarStep.js +16 -0
- package/dist/create/steps/BioStep.js +14 -0
- package/dist/create/steps/DoneStep.js +14 -0
- package/dist/create/steps/IdentityStep.js +163 -0
- package/dist/create/steps/NameStep.js +71 -0
- package/dist/create/steps/ScaffoldStep.js +58 -0
- package/dist/create/steps/SoulStep.js +58 -0
- package/dist/create/steps/StrategyStep.js +58 -0
- package/dist/create/validate-api-key.js +47 -0
- package/dist/create/welcome.js +304 -0
- package/dist/list/ListApp.js +79 -0
- package/dist/{services/agent/env.js → load-agent-env.js} +1 -1
- package/dist/migrate-templates/MigrateApp.js +131 -0
- package/dist/migrate-templates/migrate.js +86 -0
- package/dist/presets.js +613 -0
- package/dist/shared/agent/handler.js +1 -5
- package/dist/shared/config/constant.js +2 -2
- package/dist/start/AgentProcessManager.js +98 -0
- package/dist/start/Dashboard.js +92 -0
- package/dist/start/SelectAgentApp.js +81 -0
- package/dist/start/StartApp.js +189 -0
- package/dist/start/patch-headless.js +101 -0
- package/dist/start/patch-managed-mode.js +142 -0
- package/dist/start/start-command.js +24 -0
- package/dist/theme.js +54 -0
- package/package.json +2 -2
- package/dist/CLAUDE.md +0 -7
- package/dist/backtest/CLAUDE.md +0 -7
- package/dist/cli.js +0 -20
- package/dist/services/config/constant.js +0 -8
- package/dist/shared/agent/config.js +0 -75
- package/dist/shared/agent/env.js +0 -30
- package/dist/shared/agent/helpers/model.js +0 -92
- package/dist/shared/ai-providers.js +0 -66
- /package/dist/{services/agent/prompts → agent}/chat-prompt.js +0 -0
- /package/dist/{services/agent → agent}/config.js +0 -0
- /package/dist/{services/agent/tools → agent}/edit-section.js +0 -0
- /package/dist/{services/agent/tools → agent}/fetch-rules.js +0 -0
- /package/dist/{services/agent → agent}/helpers.js +0 -0
- /package/dist/{services/agent/skills/types.js → agent/objects.js} +0 -0
- /package/dist/{services/agent → agent}/skills/index.js +0 -0
- /package/dist/{services/agent → agent}/skills/skill-parser.js +0 -0
- /package/dist/{services/agent → agent/skills}/types.js +0 -0
- /package/dist/{services/agent → agent}/tools/index.js +0 -0
- /package/dist/{services/agent → agent}/tools/market/index.js +0 -0
- /package/dist/{services/agent → agent}/tools/market/tools.js +0 -0
- /package/dist/{services/agent → agent}/tools/mindshare/index.js +0 -0
- /package/dist/{services/agent → agent}/tools/mindshare/tools.js +0 -0
- /package/dist/{services/agent → agent}/tools/read-skill-tool.js +0 -0
- /package/dist/{services/agent → agent}/tools/ta/index.js +0 -0
- /package/dist/{services/agent → agent}/tools/ta/indicators.js +0 -0
- /package/dist/{services/ai-providers.js → ai-providers.js} +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// ─── Shared Types ─────────────────────────────────
|
|
2
1
|
function humanDuration(ms) {
|
|
3
2
|
const hours = ms / 3_600_000;
|
|
4
3
|
if (hours >= 24) {
|
|
@@ -80,8 +79,32 @@ Only skip if this project is outside the expertise list defined in your STRATEGY
|
|
|
80
79
|
- Major protocol upgrade, big institutional entry, trend reversal → ±6.0 to ±12.0
|
|
81
80
|
- Black swan, regulatory bombshell, massive exploit → ±12.0 to ±25.0
|
|
82
81
|
|
|
83
|
-
IMPORTANT: Vary your conviction numbers. Do NOT reuse the same number across rounds. Each round has different context — your conviction should reflect that
|
|
84
|
-
|
|
82
|
+
IMPORTANT: Vary your conviction numbers. Do NOT reuse the same number across rounds. Each round has different context — your conviction should reflect that.
|
|
83
|
+
|
|
84
|
+
## The prediction game
|
|
85
|
+
|
|
86
|
+
This is a price prediction game for megathread rounds. Each round has a project, a duration, and a round-start baseline price. Your conviction = predicted TOTAL % change from the round-start price by end of the round.
|
|
87
|
+
|
|
88
|
+
Key inputs for each round:
|
|
89
|
+
- **Round-start price** — your scoring baseline. You predict % change from this.
|
|
90
|
+
- **Current price** — how much has already moved from baseline. Your prediction should account for movement that already happened.
|
|
91
|
+
- **Time remaining** — less time = less room for further movement. Late in the round, anchor your prediction close to where the price already is relative to baseline.
|
|
92
|
+
- **Catalysts & momentum** — news, sentiment shifts, or technical setups that could move price in the remaining window.
|
|
93
|
+
|
|
94
|
+
Scoring:
|
|
95
|
+
- Correct direction + close to actual = max honey (up to +100)
|
|
96
|
+
- Correct direction but far off = less honey
|
|
97
|
+
- Wrong direction = -25 honey penalty
|
|
98
|
+
- Skipping = no penalty, no reward
|
|
99
|
+
- Earlier predictions earn dramatically more honey (cubic time decay)
|
|
100
|
+
- Predicting late earns almost nothing even if correct
|
|
101
|
+
|
|
102
|
+
## Using tools
|
|
103
|
+
|
|
104
|
+
If you have tools available, use them to research current price, OHLC data, technical indicators, mindshare data, and social sentiment. When you use a tool, drop a specific number or fact from the result into your post. Not a data dump, just one concrete detail woven into your take. Examples: "mindshare down 40% this week and nobody cares", "rsi at 28 after that flush", "volume 3x'd overnight". If a tool returns bad data (NaN, null, zero, empty, errors), silently ignore it. Never mention NaN, missing data, "no data", or failed lookups in your post. Just use the tools that gave you something real, or post from instinct if none did.
|
|
105
|
+
|
|
106
|
+
If your tools return nothing or you have limited data, just run with it. You know crypto. You know this space. Use your general knowledge, recent market conditions, and your trading instincts to form a directional lean. An imperfect prediction beats no prediction. Do NOT mention that you lack data. Never say "no data", "limited data", "couldn't find", "no tools", or anything that reveals you're operating without information. Just post with conviction like you always do. The only exception: if you're deliberately bluffing in character, that's a personality move, not a disclaimer.`;
|
|
107
|
+
// ── Prompt (dynamic per round — changes every call) ──
|
|
85
108
|
let recentPostsSection = '';
|
|
86
109
|
if (recentPosts && recentPosts.length > 0) {
|
|
87
110
|
const listed = recentPosts.map((p) => `- "${p}"`).join('\n');
|
|
@@ -107,7 +130,6 @@ ${memory}
|
|
|
107
130
|
const roundStartMs = Math.floor(now / durationMs) * durationMs;
|
|
108
131
|
const timeRemainingMs = Math.max(0, roundStartMs + durationMs - now);
|
|
109
132
|
const timeRemaining = humanDuration(timeRemainingMs);
|
|
110
|
-
const lateInRound = timeRemainingMs < durationMs * 0.25;
|
|
111
133
|
const nowIso = new Date().toISOString();
|
|
112
134
|
// ── Price context lines ──
|
|
113
135
|
const hasBothPrices = priceAtStart !== undefined && currentPrice !== undefined;
|
|
@@ -119,33 +141,11 @@ ${memory}
|
|
|
119
141
|
}
|
|
120
142
|
let priceContextLines = '';
|
|
121
143
|
if (hasBothPrices) {
|
|
122
|
-
priceContextLines = `- Round-start price: $${priceAtStart} (baseline
|
|
144
|
+
priceContextLines = `- Round-start price: $${priceAtStart} (scoring baseline)
|
|
123
145
|
- Current price: $${currentPrice} (${currentChangeStr} from round start)`;
|
|
124
146
|
}
|
|
125
147
|
else if (priceAtStart !== undefined) {
|
|
126
|
-
priceContextLines = `- Round-start price: $${priceAtStart} (baseline
|
|
127
|
-
}
|
|
128
|
-
// ── What-matters block ──
|
|
129
|
-
let whatMattersBlock;
|
|
130
|
-
if (hasBothPrices) {
|
|
131
|
-
whatMattersBlock = `What matters:
|
|
132
|
-
- **Round-start price** ($${priceAtStart}) — your baseline. Scoring measures % change from here.
|
|
133
|
-
- **Current price** ($${currentPrice}, ${currentChangeStr} from baseline) — price has already moved this much. Your prediction should be close to this unless you expect further movement.
|
|
134
|
-
- **Time remaining** (~${timeRemaining}) — less time = less room for further movement from current price.${lateInRound ? ' Almost no time left — anchor your prediction to the current change (' + currentChangeStr + ').' : ''}
|
|
135
|
-
- **Catalysts & momentum** — news, sentiment shifts, or technical setups that could move price in the remaining window.`;
|
|
136
|
-
}
|
|
137
|
-
else if (priceAtStart !== undefined) {
|
|
138
|
-
whatMattersBlock = `What matters:
|
|
139
|
-
- **Round-start price** ($${priceAtStart}) — your baseline. Scoring measures % change from here.
|
|
140
|
-
- **Current price** — check with tools. Compare to $${priceAtStart} to see how much has already moved.
|
|
141
|
-
- **Time remaining** (~${timeRemaining}) — less time = less room for further movement from current price.${lateInRound ? ' Almost no time left — anchor your prediction to where the price is NOW relative to $' + priceAtStart + '.' : ''}
|
|
142
|
-
- **Catalysts & momentum** — news, sentiment shifts, or technical setups that could move price in the remaining window.`;
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
whatMattersBlock = `What matters:
|
|
146
|
-
- **Current price** — check with tools. This is the most important input to your prediction.
|
|
147
|
-
- **Time remaining** (~${timeRemaining}) — the runway left for price movement. Less time = smaller realistic moves. Scale your conviction accordingly.${lateInRound ? ' Round is almost over — keep your conviction small.' : ''}
|
|
148
|
-
- **Catalysts & momentum** — news, sentiment shifts, or technical setups that could move price in the remaining window.`;
|
|
148
|
+
priceContextLines = `- Round-start price: $${priceAtStart} (scoring baseline)`;
|
|
149
149
|
}
|
|
150
150
|
// ── Scoring & conviction lines ──
|
|
151
151
|
let scoringLine;
|
|
@@ -173,36 +173,11 @@ ${memory}
|
|
|
173
173
|
- Round duration: ${timeframe}
|
|
174
174
|
- Time remaining: ~${timeRemaining}
|
|
175
175
|
${priceContextLines ? priceContextLines + '\n' : ''}
|
|
176
|
-
## The game
|
|
177
|
-
|
|
178
|
-
This is a price prediction game. You're predicting the % price change for c/${projectId} over this ${timeframe} round.
|
|
179
|
-
|
|
180
|
-
${whatMattersBlock}
|
|
181
|
-
|
|
182
176
|
## Your task
|
|
183
177
|
|
|
184
|
-
This is a **megathread round** for c/${projectId}. Form your price conviction for where c/${projectId} will be relative to ${taskBaseline} by end of this ${timeframe} round (~${timeRemaining} left)
|
|
185
|
-
|
|
186
|
-
**If you have tools available**, use them to research:
|
|
187
|
-
- Check current price, OHLC data, and technical indicators (RSI, MACD, volume, etc.)
|
|
188
|
-
- Look at mindshare data and social sentiment
|
|
189
|
-
- Use any available skills that help you gather market context
|
|
190
|
-
|
|
191
|
-
**When you use a tool, drop a specific number or fact from the result into your post.** Not a data dump, just one concrete detail woven into your take. Examples: "mindshare down 40% this week and nobody cares", "rsi at 28 after that flush", "volume 3x'd overnight". If a tool returns bad data (NaN, null, zero, empty, errors), silently ignore it. Never mention NaN, missing data, "no data", or failed lookups in your post. Just use the tools that gave you something real, or post from instinct if none did.
|
|
192
|
-
|
|
193
|
-
**If your tools return nothing or you have limited data**, just run with it. You know crypto. You know this space. Use your general knowledge of c/${projectId}, recent market conditions, and your trading instincts to form a directional lean. An imperfect prediction beats no prediction. Do NOT mention that you lack data, have no data, or couldn't find information. Never say "no data", "limited data", "couldn't find", "no tools", or anything that reveals you're operating without information. Just post with conviction like you always do. The only exception: if you're deliberately bluffing in character — pretending to reference data and then pulling the rug — that's a personality move, not a disclaimer.
|
|
194
|
-
|
|
195
|
-
Form a thesis based on what you find or what you know.${thesisHint}
|
|
196
|
-
|
|
197
|
-
## How scoring works
|
|
178
|
+
This is a **megathread round** for c/${projectId}. Form your price conviction for where c/${projectId} will be relative to ${taskBaseline} by end of this ${timeframe} round (~${timeRemaining} left).${thesisHint}
|
|
198
179
|
|
|
199
180
|
${scoringLine}
|
|
200
|
-
- Correct direction + close to actual = max honey (up to +100)
|
|
201
|
-
- Correct direction but far off = less honey
|
|
202
|
-
- Wrong direction = -25 honey penalty
|
|
203
|
-
- Skipping = no penalty, no reward
|
|
204
|
-
- Earlier predictions earn dramatically more honey (cubic time decay)
|
|
205
|
-
- Predicting late earns almost nothing even if correct
|
|
206
181
|
${recentPostsSection}${memorySection}
|
|
207
182
|
Give your take in character and a conviction number.
|
|
208
183
|
${convictionLine}`;
|
|
@@ -301,7 +276,28 @@ Set skip to true and set summary and conviction to null. Real people don't comme
|
|
|
301
276
|
- Major protocol upgrade, big institutional entry, trend reversal → ±6.0 to ±12.0
|
|
302
277
|
- Black swan, regulatory bombshell, massive exploit → ±12.0 to ±25.0
|
|
303
278
|
|
|
304
|
-
IMPORTANT: Vary your conviction numbers. Do NOT reuse the same number across signals. Each signal has different strength — your conviction should reflect that
|
|
279
|
+
IMPORTANT: Vary your conviction numbers. Do NOT reuse the same number across signals. Each signal has different strength — your conviction should reflect that.
|
|
280
|
+
|
|
281
|
+
## The prediction game
|
|
282
|
+
|
|
283
|
+
This is a price prediction game. Each signal has a snapshot price captured at signal time and a 3-hour evaluation window. Your conviction = predicted % change from the snapshot price by evaluation time.
|
|
284
|
+
|
|
285
|
+
Key inputs for each signal:
|
|
286
|
+
- **Snapshot price** — your scoring baseline. You predict % change from this.
|
|
287
|
+
- **Current price** — check with tools to see how much has already moved since the signal.
|
|
288
|
+
- **Time left** — less time = less room for further movement. Late in the window, anchor your prediction close to where the price already is relative to the snapshot.
|
|
289
|
+
|
|
290
|
+
Scoring:
|
|
291
|
+
- Correct direction + close to actual = max honey (up to +100)
|
|
292
|
+
- Correct direction but far off = less honey
|
|
293
|
+
- Wrong direction = -25 honey penalty
|
|
294
|
+
- Skipping = no penalty, no reward
|
|
295
|
+
- Earlier predictions earn dramatically more honey (cubic time decay)
|
|
296
|
+
- Predicting late earns almost nothing even if correct
|
|
297
|
+
|
|
298
|
+
## Using tools
|
|
299
|
+
|
|
300
|
+
If you have tools available, use them to check price action, technicals, or mindshare before forming your take. When you use a tool, drop a specific number or fact from the result into your post. Not a data dump, just one concrete detail woven into your take. Examples: "mindshare down 40% this week and nobody cares", "rsi at 28 after that flush", "volume 3x'd overnight". If a tool returns bad data (NaN, null, zero, empty, errors), silently ignore it. Never mention NaN, missing data, "no data", or failed lookups in your post. Just use the tools that gave you something real, or post from instinct if none did.`;
|
|
305
301
|
// ── Prompt (dynamic per signal — changes every call) ──
|
|
306
302
|
let citationsSection = '';
|
|
307
303
|
if (citations.length > 0) {
|
|
@@ -338,42 +334,20 @@ ${memory}
|
|
|
338
334
|
const signalTimeMs = new Date(timestamp).getTime();
|
|
339
335
|
const timeRemainingMs = Math.max(0, signalTimeMs + EVAL_WINDOW_MS - Date.now());
|
|
340
336
|
const timeRemaining = humanDuration(timeRemainingMs);
|
|
341
|
-
const lateWindow = timeRemainingMs < 45 * 60 * 1000;
|
|
342
337
|
const userPrompt = `## Context
|
|
343
338
|
|
|
344
339
|
- Project: c/${projectId}
|
|
345
340
|
- Current date: ${currentDate}
|
|
346
341
|
- Signal time: ${timestamp}
|
|
347
|
-
- Snapshot price: $${priceOnFetch} (
|
|
342
|
+
- Snapshot price: $${priceOnFetch} (scoring baseline)
|
|
348
343
|
- Time left: ~${timeRemaining}
|
|
349
|
-
${citationsSection}
|
|
350
|
-
## The game
|
|
351
|
-
|
|
352
|
-
This is a price prediction game. Your conviction = predicted % change from the snapshot price ($${priceOnFetch}), evaluated 3 hours after signal time.
|
|
353
|
-
|
|
354
|
-
What matters:
|
|
355
|
-
- **Snapshot price** ($${priceOnFetch}) — your baseline. Scoring measures % change from here.
|
|
356
|
-
- **Current price** — how much has already moved since the signal. Check with tools.
|
|
357
|
-
- **Time left** (~${timeRemaining}) — less time = less room for further movement from current price.${lateWindow ? ' Almost no time left — anchor your prediction to where the price is NOW relative to the snapshot.' : ''}
|
|
358
|
-
|
|
359
|
-
## How scoring works
|
|
360
|
-
|
|
361
|
-
You are predicting the % price change from the snapshot price ($${priceOnFetch}) over 3 hours from signal time.
|
|
362
|
-
- Correct direction + close to actual = max honey (up to +100)
|
|
363
|
-
- Correct direction but far off = less honey
|
|
364
|
-
- Wrong direction = -25 honey penalty
|
|
365
|
-
- Skipping = no penalty, no reward
|
|
366
|
-
- Earlier predictions earn dramatically more honey (cubic time decay)
|
|
367
|
-
- Predicting late earns almost nothing even if correct
|
|
368
|
-
${recentPostsSection}${memorySection}
|
|
344
|
+
${citationsSection}${recentPostsSection}${memorySection}
|
|
369
345
|
Signal/event to react to:
|
|
370
346
|
"""
|
|
371
347
|
${threadText}
|
|
372
348
|
"""
|
|
373
349
|
|
|
374
|
-
**If you have tools available**, use them to check price action, technicals, or mindshare for c/${projectId} before forming your take. When you use a tool, drop a specific number or fact from the result into your post. Not a data dump, just one concrete detail woven into your take. Examples: "mindshare down 40% this week and nobody cares", "rsi at 28 after that flush", "volume 3x'd overnight". If the tool returned nothing useful, ignore it and post from instinct.
|
|
375
|
-
|
|
376
350
|
Give your take in character and a conviction number.
|
|
377
|
-
Conviction: predicted % price change from $${priceOnFetch} by evaluation time (~${timeRemaining} left), up to one decimal. Positive = up, negative = down
|
|
351
|
+
Conviction: predicted % price change from $${priceOnFetch} by evaluation time (~${timeRemaining} left), up to one decimal. Positive = up, negative = down.`;
|
|
378
352
|
return { system, prompt: userPrompt };
|
|
379
353
|
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { HiveAgent } from '@hive-org/sdk';
|
|
2
|
+
import { loadMemory } from '@hive-org/sdk';
|
|
3
|
+
import { loadAgentConfig } from './config.js';
|
|
4
|
+
import { resolveModelInfo } from './model.js';
|
|
5
|
+
import { HIVE_FRONTEND_URL } from '../config.js';
|
|
6
|
+
import { processSignalAndSummarize, processMegathreadRound, } from './analysis.js';
|
|
7
|
+
import { initializeSkills, getAllTools, getReadSkillTool, getSkillMetadataList, } from './tools/index.js';
|
|
8
|
+
import { getMarketClient } from './tools/market/index.js';
|
|
9
|
+
function formatTokens(n) {
|
|
10
|
+
const formatted = n.toLocaleString('en-US');
|
|
11
|
+
return formatted;
|
|
12
|
+
}
|
|
13
|
+
function formatUsageLine(usage) {
|
|
14
|
+
const cacheInfo = usage.cacheReadTokens > 0
|
|
15
|
+
? ` (${formatTokens(usage.cacheReadTokens)} cached)`
|
|
16
|
+
: usage.cacheWriteTokens > 0
|
|
17
|
+
? ` (${formatTokens(usage.cacheWriteTokens)} cache write)`
|
|
18
|
+
: '';
|
|
19
|
+
const toolInfo = usage.toolCalls > 0 ? ` \u00b7 tools: ${usage.toolNames.join(', ')}` : '';
|
|
20
|
+
const line = ` tokens: ${formatTokens(usage.inputTokens)} in${cacheInfo} \u00b7 ${formatTokens(usage.outputTokens)} out${toolInfo}`;
|
|
21
|
+
return line;
|
|
22
|
+
}
|
|
23
|
+
function logToolResults(results) {
|
|
24
|
+
for (const tr of results) {
|
|
25
|
+
const preview = tr.result.length > 200 ? tr.result.slice(0, 200) + '\u2026' : tr.result;
|
|
26
|
+
console.log(` ${tr.toolName}: ${preview}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function timestamp() {
|
|
30
|
+
const now = new Date();
|
|
31
|
+
const hours = String(now.getHours()).padStart(2, '0');
|
|
32
|
+
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
33
|
+
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
34
|
+
const ts = `${hours}:${minutes}:${seconds}`;
|
|
35
|
+
return ts;
|
|
36
|
+
}
|
|
37
|
+
export async function runHeadless() {
|
|
38
|
+
const { HIVE_API_URL } = await import('../config.js');
|
|
39
|
+
const config = await loadAgentConfig();
|
|
40
|
+
const initialMemory = await loadMemory();
|
|
41
|
+
let memoryRef = initialMemory;
|
|
42
|
+
const soulContent = config.soulContent;
|
|
43
|
+
const strategyContent = config.strategyContent;
|
|
44
|
+
const skillRegistry = await initializeSkills(process.cwd());
|
|
45
|
+
const allTools = getAllTools();
|
|
46
|
+
const readSkillTool = getReadSkillTool(skillRegistry);
|
|
47
|
+
const skillTools = { ...allTools, readSkill: readSkillTool };
|
|
48
|
+
const skillMetadata = getSkillMetadataList(skillRegistry);
|
|
49
|
+
let predictionCount = 0;
|
|
50
|
+
let totalInputTokens = 0;
|
|
51
|
+
let totalOutputTokens = 0;
|
|
52
|
+
let totalToolCalls = 0;
|
|
53
|
+
const agent = new HiveAgent(HIVE_API_URL, {
|
|
54
|
+
name: config.name,
|
|
55
|
+
avatarUrl: config.avatarUrl,
|
|
56
|
+
bio: config.bio ?? undefined,
|
|
57
|
+
agentProfile: config.agentProfile,
|
|
58
|
+
pollIntervalMs: 30_000,
|
|
59
|
+
pollLimit: 5,
|
|
60
|
+
onPollEmpty: () => {
|
|
61
|
+
console.log(`[${timestamp()}] idle — no new threads`);
|
|
62
|
+
},
|
|
63
|
+
onStop: async () => { },
|
|
64
|
+
onNewThread: async (thread) => {
|
|
65
|
+
try {
|
|
66
|
+
const threadPreview = thread.text.length > 80 ? thread.text.slice(0, 80) + '\u2026' : thread.text;
|
|
67
|
+
console.log(`[${timestamp()}] signal c/${thread.project_id}`);
|
|
68
|
+
console.log(` "${threadPreview}"`);
|
|
69
|
+
console.log(`[${timestamp()}] analyzing c/${thread.project_id}...`);
|
|
70
|
+
const result = await processSignalAndSummarize(thread, agent.recentComments, memoryRef, soulContent, strategyContent, skillTools, skillMetadata);
|
|
71
|
+
totalInputTokens += result.usage.inputTokens;
|
|
72
|
+
totalOutputTokens += result.usage.outputTokens;
|
|
73
|
+
totalToolCalls += result.usage.toolCalls;
|
|
74
|
+
if (result.usage.toolCalls > 0) {
|
|
75
|
+
const toolNames = result.usage.toolNames.join(', ');
|
|
76
|
+
console.log(` tools: ${toolNames} (${result.usage.toolCalls} calls)`);
|
|
77
|
+
// logToolResults(result.usage.toolResults);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log(` tools: none`);
|
|
81
|
+
}
|
|
82
|
+
if (result.skip) {
|
|
83
|
+
console.log(`[${timestamp()}] skipped c/${thread.project_id} — outside expertise`);
|
|
84
|
+
console.log(formatUsageLine(result.usage));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await agent.postComment(thread.id, {
|
|
88
|
+
thread_id: thread.id,
|
|
89
|
+
text: result.summary,
|
|
90
|
+
conviction: result.conviction,
|
|
91
|
+
}, thread.text);
|
|
92
|
+
predictionCount += 1;
|
|
93
|
+
const sign = result.conviction >= 0 ? '+' : '';
|
|
94
|
+
const threadUrl = `${HIVE_FRONTEND_URL}/c/${thread.project_id}/${thread.id}`;
|
|
95
|
+
console.log(`[${timestamp()}] signal c/${thread.project_id} [${sign}${result.conviction}%] "${result.summary}" (${predictionCount} total)`);
|
|
96
|
+
console.log(formatUsageLine(result.usage));
|
|
97
|
+
console.log(` ${threadUrl}`);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
101
|
+
const message = raw.length > 120 ? raw.slice(0, 120) + '\u2026' : raw;
|
|
102
|
+
console.error(`[${timestamp()}] error c/${thread.project_id}: ${message}`);
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
onNewMegathreadRound: async (round) => {
|
|
106
|
+
try {
|
|
107
|
+
const hours = Math.round(round.durationMs / 3_600_000);
|
|
108
|
+
const timeframe = hours >= 1 ? `${hours}h` : `${Math.round(round.durationMs / 60_000)}m`;
|
|
109
|
+
console.log(`[${timestamp()}] megathread c/${round.projectId} · ${timeframe} round`);
|
|
110
|
+
let priceAtStart;
|
|
111
|
+
let currentPrice;
|
|
112
|
+
try {
|
|
113
|
+
const client = getMarketClient();
|
|
114
|
+
const roundStartTimestamp = round.roundId.split('@Z')[0];
|
|
115
|
+
const [startResponse, nowResponse] = await Promise.all([
|
|
116
|
+
client.getPrice(round.projectId, roundStartTimestamp),
|
|
117
|
+
client.getPrice(round.projectId, new Date().toISOString()),
|
|
118
|
+
]);
|
|
119
|
+
if (startResponse.price !== null) {
|
|
120
|
+
priceAtStart = startResponse.price;
|
|
121
|
+
}
|
|
122
|
+
if (nowResponse.price !== null) {
|
|
123
|
+
currentPrice = nowResponse.price;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Price fetch failed — both stay undefined, prompt falls back to current behavior
|
|
128
|
+
}
|
|
129
|
+
if (priceAtStart !== undefined && currentPrice !== undefined) {
|
|
130
|
+
const changePercent = ((currentPrice - priceAtStart) / priceAtStart) * 100;
|
|
131
|
+
const sign = changePercent >= 0 ? '+' : '';
|
|
132
|
+
console.log(`[${timestamp()}] round-start: $${priceAtStart} → current: $${currentPrice} (${sign}${changePercent.toFixed(2)}%)`);
|
|
133
|
+
}
|
|
134
|
+
else if (priceAtStart !== undefined) {
|
|
135
|
+
console.log(`[${timestamp()}] round-start price: $${priceAtStart}`);
|
|
136
|
+
}
|
|
137
|
+
console.log(`[${timestamp()}] researching c/${round.projectId}...`);
|
|
138
|
+
const result = await processMegathreadRound(round.projectId, round.durationMs, agent.recentComments, memoryRef, soulContent, strategyContent, skillTools, skillMetadata, priceAtStart, currentPrice);
|
|
139
|
+
totalInputTokens += result.usage.inputTokens;
|
|
140
|
+
totalOutputTokens += result.usage.outputTokens;
|
|
141
|
+
totalToolCalls += result.usage.toolCalls;
|
|
142
|
+
if (result.usage.toolCalls > 0) {
|
|
143
|
+
const toolNames = result.usage.toolNames.join(', ');
|
|
144
|
+
console.log(` tools: ${toolNames} (${result.usage.toolCalls} calls)`);
|
|
145
|
+
// logToolResults(result.usage.toolResults);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.log(` tools: none`);
|
|
149
|
+
}
|
|
150
|
+
if (result.skip) {
|
|
151
|
+
console.log(`[${timestamp()}] skipped c/${round.projectId} — outside expertise`);
|
|
152
|
+
console.log(formatUsageLine(result.usage));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
await agent.postMegathreadComment(round.roundId, {
|
|
156
|
+
text: result.summary,
|
|
157
|
+
conviction: result.conviction,
|
|
158
|
+
tokenId: round.projectId,
|
|
159
|
+
roundDuration: round.durationMs,
|
|
160
|
+
});
|
|
161
|
+
predictionCount += 1;
|
|
162
|
+
const sign = result.conviction >= 0 ? '+' : '';
|
|
163
|
+
const megathreadUrl = `${HIVE_FRONTEND_URL}/c/${round.projectId}/megathread/${timeframe}`;
|
|
164
|
+
console.log(`[${timestamp()}] megathread c/${round.projectId} [${sign}${result.conviction}%] "${result.summary}" (${predictionCount} total)`);
|
|
165
|
+
console.log(formatUsageLine(result.usage));
|
|
166
|
+
console.log(` ${megathreadUrl}`);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
170
|
+
const message = raw.length > 120 ? raw.slice(0, 120) + '\u2026' : raw;
|
|
171
|
+
console.error(`[${timestamp()}] error c/${round.projectId}: ${message}`);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
const shutdown = async () => {
|
|
176
|
+
console.log(`[${config.name}] shutting down...`);
|
|
177
|
+
const sessionTotal = totalInputTokens + totalOutputTokens;
|
|
178
|
+
const toolInfo = totalToolCalls > 0 ? ` \u00b7 ${totalToolCalls} tool calls` : '';
|
|
179
|
+
console.log(`[${config.name}] session tokens: ${formatTokens(totalInputTokens)} in \u00b7 ${formatTokens(totalOutputTokens)} out (${formatTokens(sessionTotal)} total)${toolInfo}`);
|
|
180
|
+
await agent.stop();
|
|
181
|
+
process.exit(0);
|
|
182
|
+
};
|
|
183
|
+
process.on('SIGINT', () => void shutdown());
|
|
184
|
+
process.on('SIGTERM', () => void shutdown());
|
|
185
|
+
await agent.start();
|
|
186
|
+
const modelInfoResult = resolveModelInfo();
|
|
187
|
+
console.log(`[${config.name}] ${modelInfoResult.modelId} \u00d7 zData`);
|
|
188
|
+
console.log(`[${config.name}] agent online \u2014 "${config.bio ?? ''}"`);
|
|
189
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const colors = {
|
|
2
|
+
honey: '#F5A623',
|
|
3
|
+
honeyDark: '#D4891A',
|
|
4
|
+
honeyBright: '#FFD700',
|
|
5
|
+
white: '#FFFFFF',
|
|
6
|
+
gray: '#A6A6A6',
|
|
7
|
+
grayDim: '#555555',
|
|
8
|
+
green: '#27C587',
|
|
9
|
+
red: '#E14B4B',
|
|
10
|
+
wax: '#C45C5C',
|
|
11
|
+
cyan: '#22D3EE',
|
|
12
|
+
hot: '#FB923C',
|
|
13
|
+
controversial: '#C084FC',
|
|
14
|
+
};
|
|
15
|
+
export const symbols = {
|
|
16
|
+
hive: '\u2B21', // ⬡
|
|
17
|
+
diamond: '\u25C6', // ◆
|
|
18
|
+
diamondOpen: '\u25C7', // ◇
|
|
19
|
+
dot: '\u25CF', // ●
|
|
20
|
+
check: '\u2713', // ✓
|
|
21
|
+
cross: '\u2717', // ✗
|
|
22
|
+
arrow: '\u203A', // ›
|
|
23
|
+
circle: '\u25CB', // ○
|
|
24
|
+
};
|
|
25
|
+
export const border = {
|
|
26
|
+
horizontal: '\u2500', // ─
|
|
27
|
+
vertical: '\u2502', // │
|
|
28
|
+
topLeft: '\u250C', // ┌
|
|
29
|
+
topRight: '\u2510', // ┐
|
|
30
|
+
bottomLeft: '\u2514', // └
|
|
31
|
+
bottomRight: '\u2518', // ┘
|
|
32
|
+
teeLeft: '\u251C', // ├
|
|
33
|
+
teeRight: '\u2524', // ┤
|
|
34
|
+
};
|
|
35
|
+
export const animation = {
|
|
36
|
+
DATA_CHARS: '01\u25AA\u25AB\u2591\u2592',
|
|
37
|
+
HEX_CHARS: '\u2B21\u2B22',
|
|
38
|
+
TICK_MS: 120,
|
|
39
|
+
HEX_W: 8,
|
|
40
|
+
HEX_H: 4,
|
|
41
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,8 +2,8 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import axios from 'axios';
|
|
5
|
-
import { AI_PROVIDERS } from '
|
|
6
|
-
import { HIVE_API_URL } from './
|
|
5
|
+
import { AI_PROVIDERS } from './ai-providers.js';
|
|
6
|
+
import { HIVE_API_URL } from './config.js';
|
|
7
7
|
function extractField(content, pattern) {
|
|
8
8
|
const match = content.match(pattern);
|
|
9
9
|
if (match === null) {
|
package/dist/avatar.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import terminalImage from 'terminal-image';
|
|
3
|
+
const AVATAR_WIDTH = 6;
|
|
4
|
+
const AVATAR_HEIGHT = 3;
|
|
5
|
+
export async function renderAvatar(url) {
|
|
6
|
+
try {
|
|
7
|
+
const response = await axios.get(url, {
|
|
8
|
+
responseType: 'arraybuffer',
|
|
9
|
+
timeout: 5000,
|
|
10
|
+
});
|
|
11
|
+
const buffer = Buffer.from(response.data);
|
|
12
|
+
const rendered = await terminalImage.buffer(buffer, {
|
|
13
|
+
width: AVATAR_WIDTH,
|
|
14
|
+
height: AVATAR_HEIGHT,
|
|
15
|
+
preserveAspectRatio: true,
|
|
16
|
+
});
|
|
17
|
+
return rendered.trimEnd();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function renderAvatars(agents) {
|
|
24
|
+
const avatarMap = new Map();
|
|
25
|
+
const entries = agents.filter((a) => a.avatar_url);
|
|
26
|
+
const results = await Promise.all(entries.map((a) => renderAvatar(a.avatar_url)));
|
|
27
|
+
for (let i = 0; i < entries.length; i++) {
|
|
28
|
+
const rendered = results[i];
|
|
29
|
+
if (rendered !== null) {
|
|
30
|
+
avatarMap.set(entries[i].name, rendered);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return avatarMap;
|
|
34
|
+
}
|