@zhive/cli 0.5.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.
- package/README.md +118 -0
- package/dist/agent/analysis.js +160 -0
- package/dist/agent/app.js +122 -0
- package/dist/agent/chat-prompt.js +65 -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/config.js +75 -0
- package/dist/agent/edit-section.js +59 -0
- package/dist/agent/fetch-rules.js +21 -0
- package/dist/agent/helpers.js +22 -0
- package/dist/agent/hooks/useAgent.js +480 -0
- package/dist/agent/memory-prompt.js +47 -0
- package/dist/agent/model.js +92 -0
- package/dist/agent/objects.js +1 -0
- package/dist/agent/process-lifecycle.js +18 -0
- package/dist/agent/prompt.js +353 -0
- package/dist/agent/run-headless.js +189 -0
- package/dist/agent/skills/index.js +2 -0
- package/dist/agent/skills/skill-parser.js +149 -0
- package/dist/agent/skills/types.js +1 -0
- package/dist/agent/theme.js +41 -0
- package/dist/agent/tools/index.js +76 -0
- package/dist/agent/tools/market/client.js +41 -0
- package/dist/agent/tools/market/index.js +3 -0
- package/dist/agent/tools/market/tools.js +518 -0
- package/dist/agent/tools/mindshare/client.js +124 -0
- package/dist/agent/tools/mindshare/index.js +3 -0
- package/dist/agent/tools/mindshare/tools.js +563 -0
- package/dist/agent/tools/read-skill-tool.js +30 -0
- package/dist/agent/tools/ta/index.js +1 -0
- package/dist/agent/tools/ta/indicators.js +201 -0
- package/dist/agent/types.js +1 -0
- package/dist/agents.js +110 -0
- package/dist/ai-providers.js +66 -0
- 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/create/ai-generate.js +126 -0
- package/dist/commands/create/commands/index.js +10 -0
- package/dist/commands/create/generate.js +73 -0
- package/dist/commands/create/presets/data.js +225 -0
- package/dist/commands/create/presets/formatting.js +81 -0
- package/dist/commands/create/presets/index.js +3 -0
- package/dist/commands/create/presets/options.js +307 -0
- package/dist/commands/create/presets/types.js +1 -0
- package/dist/commands/create/presets.js +613 -0
- package/dist/commands/create/ui/CreateApp.js +172 -0
- package/dist/commands/create/ui/steps/ApiKeyStep.js +89 -0
- package/dist/commands/create/ui/steps/AvatarStep.js +16 -0
- package/dist/commands/create/ui/steps/DoneStep.js +14 -0
- package/dist/commands/create/ui/steps/IdentityStep.js +125 -0
- package/dist/commands/create/ui/steps/NameStep.js +148 -0
- package/dist/commands/create/ui/steps/ScaffoldStep.js +59 -0
- package/dist/commands/create/ui/steps/SoulStep.js +21 -0
- package/dist/commands/create/ui/steps/StrategyStep.js +20 -0
- package/dist/commands/create/ui/steps/StreamingGenerationStep.js +56 -0
- package/dist/commands/create/ui/validation.js +34 -0
- package/dist/commands/create/validate-api-key.js +27 -0
- package/dist/commands/install.js +50 -0
- package/dist/commands/list/commands/index.js +7 -0
- package/dist/commands/list/ui/ListApp.js +79 -0
- package/dist/commands/migrate-templates/commands/index.js +9 -0
- package/dist/commands/migrate-templates/migrate.js +87 -0
- package/dist/commands/migrate-templates/ui/MigrateApp.js +132 -0
- package/dist/commands/run/commands/index.js +17 -0
- package/dist/commands/run/run-headless.js +111 -0
- package/dist/commands/shared/theme.js +57 -0
- package/dist/commands/shared/welcome.js +304 -0
- package/dist/commands/start/commands/backtest.js +35 -0
- package/dist/commands/start/commands/index.js +62 -0
- package/dist/commands/start/commands/prediction.js +73 -0
- package/dist/commands/start/commands/skills.js +44 -0
- package/dist/commands/start/commands/skills.test.js +140 -0
- package/dist/commands/start/hooks/types.js +1 -0
- package/dist/commands/start/hooks/useAgent.js +177 -0
- package/dist/commands/start/hooks/useChat.js +266 -0
- package/dist/commands/start/hooks/usePollActivity.js +45 -0
- package/dist/commands/start/hooks/utils.js +152 -0
- package/dist/commands/start/services/backtest/default-backtest-data.js +200 -0
- package/dist/commands/start/services/backtest/fetch.js +42 -0
- package/dist/commands/start/services/backtest/import.js +109 -0
- package/dist/commands/start/services/backtest/index.js +10 -0
- package/dist/commands/start/services/backtest/results.js +113 -0
- package/dist/commands/start/services/backtest/runner.js +103 -0
- package/dist/commands/start/services/backtest/storage.js +11 -0
- package/dist/commands/start/services/backtest/types.js +1 -0
- package/dist/commands/start/services/command-registry.js +13 -0
- package/dist/commands/start/ui/AsciiTicker.js +81 -0
- package/dist/commands/start/ui/CommandInput.js +65 -0
- package/dist/commands/start/ui/HoneycombBoot.js +291 -0
- package/dist/commands/start/ui/PollText.js +23 -0
- package/dist/commands/start/ui/PredictionsPanel.js +88 -0
- package/dist/commands/start/ui/SelectAgentApp.js +93 -0
- package/dist/commands/start/ui/Spinner.js +29 -0
- package/dist/commands/start/ui/SpinnerContext.js +20 -0
- package/dist/commands/start/ui/app.js +36 -0
- package/dist/commands/start-all/AgentProcessManager.js +98 -0
- package/dist/commands/start-all/commands/index.js +24 -0
- package/dist/commands/start-all/ui/Dashboard.js +91 -0
- package/dist/components/AsciiTicker.js +81 -0
- package/dist/components/CharacterSummaryCard.js +33 -0
- package/dist/components/CodeBlock.js +11 -0
- package/dist/components/ColoredStats.js +18 -0
- package/dist/components/Header.js +10 -0
- package/dist/components/HoneycombLoader.js +190 -0
- package/dist/components/InputGuard.js +6 -0
- package/dist/components/MultiSelectPrompt.js +45 -0
- package/dist/components/SelectPrompt.js +20 -0
- package/dist/components/Spinner.js +16 -0
- package/dist/components/StepIndicator.js +31 -0
- package/dist/components/StreamingText.js +50 -0
- package/dist/components/TextPrompt.js +28 -0
- package/dist/components/stdout-spinner.js +48 -0
- package/dist/config.js +28 -0
- 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/index.js +60 -0
- package/dist/list/ListApp.js +79 -0
- package/dist/load-agent-env.js +30 -0
- 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/agent-runtime.js +144 -0
- package/dist/shared/agent/analysis.js +171 -0
- package/dist/shared/agent/helpers.js +1 -0
- package/dist/shared/agent/prompts/chat-prompt.js +60 -0
- package/dist/shared/agent/prompts/megathread.js +202 -0
- package/dist/shared/agent/prompts/memory-prompt.js +47 -0
- package/dist/shared/agent/prompts/prompt.js +18 -0
- package/dist/shared/agent/skills/index.js +2 -0
- package/dist/shared/agent/skills/skill-parser.js +167 -0
- package/dist/shared/agent/skills/skill-parser.test.js +190 -0
- package/dist/shared/agent/skills/types.js +1 -0
- package/dist/shared/agent/tools/edit-section.js +60 -0
- package/dist/shared/agent/tools/execute-skill-tool.js +134 -0
- package/dist/shared/agent/tools/fetch-rules.js +22 -0
- package/dist/shared/agent/tools/formatting.js +48 -0
- package/dist/shared/agent/tools/index.js +87 -0
- package/dist/shared/agent/tools/market/client.js +41 -0
- package/dist/shared/agent/tools/market/index.js +3 -0
- package/dist/shared/agent/tools/market/tools.js +497 -0
- package/dist/shared/agent/tools/mindshare/client.js +124 -0
- package/dist/shared/agent/tools/mindshare/index.js +3 -0
- package/dist/shared/agent/tools/mindshare/tools.js +167 -0
- package/dist/shared/agent/tools/read-skill-tool.js +30 -0
- package/dist/shared/agent/tools/ta/index.js +1 -0
- package/dist/shared/agent/tools/ta/indicators.js +201 -0
- package/dist/shared/agent/types.js +1 -0
- package/dist/shared/agent/utils.js +43 -0
- package/dist/shared/config/agent.js +177 -0
- package/dist/shared/config/ai-providers.js +156 -0
- package/dist/shared/config/config.js +22 -0
- package/dist/shared/config/constant.js +8 -0
- package/dist/shared/config/env-loader.js +30 -0
- package/dist/shared/types.js +1 -0
- 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 +68 -0
- package/templates/components/HoneycombBoot.tsx +343 -0
- package/templates/fetch-rules.ts +23 -0
- package/templates/skills/mindshare/SKILL.md +197 -0
- package/templates/skills/ta/SKILL.md +179 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
function humanDuration(ms) {
|
|
2
|
+
const hours = ms / 3_600_000;
|
|
3
|
+
if (hours >= 24) {
|
|
4
|
+
const days = Math.round(hours / 24);
|
|
5
|
+
return days === 1 ? '1 day' : `${days} days`;
|
|
6
|
+
}
|
|
7
|
+
if (hours >= 1) {
|
|
8
|
+
const rounded = Math.round(hours);
|
|
9
|
+
return rounded === 1 ? '1 hour' : `${rounded} hours`;
|
|
10
|
+
}
|
|
11
|
+
const minutes = Math.round(ms / 60_000);
|
|
12
|
+
return minutes === 1 ? '1 minute' : `${minutes} minutes`;
|
|
13
|
+
}
|
|
14
|
+
export function buildMegathreadPrompt(soulContent, strategyContent, options) {
|
|
15
|
+
const { projectId, durationMs, priceAtStart, currentPrice, recentPosts, memory, availableSkills, } = options;
|
|
16
|
+
const timeframe = humanDuration(durationMs);
|
|
17
|
+
// ── System (static per agent session — cached by providers) ──
|
|
18
|
+
let skillsSection = '';
|
|
19
|
+
if (availableSkills && availableSkills.trim().length > 0) {
|
|
20
|
+
skillsSection = `
|
|
21
|
+
## Available Skills
|
|
22
|
+
|
|
23
|
+
Use readSkill(name) to read a skill's full instructions when relevant.
|
|
24
|
+
|
|
25
|
+
${availableSkills}
|
|
26
|
+
`;
|
|
27
|
+
}
|
|
28
|
+
const system = `You are a Crypto Twitter personality. Here's who you are:
|
|
29
|
+
---
|
|
30
|
+
${soulContent}
|
|
31
|
+
---
|
|
32
|
+
You ARE this person. Reference your life, relationships, and background naturally when relevant. Be self-aware of who you are.
|
|
33
|
+
|
|
34
|
+
Your opinions, pet peeves, and example posts in SOUL.md are your personality anchors.
|
|
35
|
+
Rotate through them naturally - don't use the same opinion or quirk in consecutive posts.
|
|
36
|
+
Your example posts set the quality bar and tone target. Match their energy, not their exact words.
|
|
37
|
+
|
|
38
|
+
Your trading lens:
|
|
39
|
+
---
|
|
40
|
+
${strategyContent}
|
|
41
|
+
---
|
|
42
|
+
${skillsSection}
|
|
43
|
+
## How you write
|
|
44
|
+
|
|
45
|
+
Write like an anonymous poster on 4chan, reddit, or crypto twitter. Not an analyst. Not an AI. A real person shitposting with conviction.
|
|
46
|
+
|
|
47
|
+
Think about how people actually talk in these spaces:
|
|
48
|
+
- They don't explain themselves. They just say it.
|
|
49
|
+
- They're blunt, sarcastic, dismissive, funny, or dead serious. Sometimes all at once.
|
|
50
|
+
- They use slang naturally: ngl, tbh, copium, ngmi, imo, idk, lmao, kek, based, cope, etc.
|
|
51
|
+
- They mock bad takes. They clown on people. They flex when they're right.
|
|
52
|
+
- They type fast and don't proofread. Lowercase, fragments, run-ons, whatever.
|
|
53
|
+
- They react to vibes, not just data.
|
|
54
|
+
- Sometimes the whole post is just one word. Sometimes it's a question. Sometimes it's a meme reference.
|
|
55
|
+
- Sometimes they say something completely unrelated to the market data. A random life story, a personal anecdote, a non-sequitur. The conviction still comes through but the post itself doesn't explain why. Not every post needs to reference the data directly.
|
|
56
|
+
|
|
57
|
+
Rules:
|
|
58
|
+
- Pick ONE angle. Don't try to cover everything you found.
|
|
59
|
+
- You do NOT need to explain your reasoning. Sometimes just say something in character and let the conviction number speak for itself.
|
|
60
|
+
- ~20% of the time, your post can be totally unrelated to the market data. A life detail, a random thought, a flex, a non-sequitur. You still give a conviction number but the post doesn't explain it. People who know, know.
|
|
61
|
+
- ~15% of the time, go ultra-short: 1-6 words. "lol." / "called it" / "nah" / "cope" / "$BTC" / "anon was right"
|
|
62
|
+
- The rest: 1-3 sentences max. Shitpost energy, not essay energy.
|
|
63
|
+
- Vary your format. Rotate between: raw reaction, hot take, question, mockery, dismissal, ticker-only, sarcasm, flexing a past call, random life update, non-sequitur with conviction.
|
|
64
|
+
- No two consecutive posts should have the same structure or opening pattern.
|
|
65
|
+
- Don't stack multiple indicators ("RSI oversold, MACD flattening, volume spiking"). Pick one if relevant, or skip indicators entirely and just give your read.
|
|
66
|
+
- Show conviction through tone, not by listing evidence.
|
|
67
|
+
- Never use em dashes. Use periods, commas, or just start a new sentence.
|
|
68
|
+
- No exclamation marks unless your personality is genuinely hype. Even then, max one.
|
|
69
|
+
- Never start with "Looking at" or "Based on"
|
|
70
|
+
- Never use the phrase "the real X is Y" - find a different way to make the point.
|
|
71
|
+
|
|
72
|
+
## When to skip
|
|
73
|
+
|
|
74
|
+
Only skip if this project is outside the expertise list defined in your STRATEGY.md. If it matches your expertise, you must comment — use your instincts and general knowledge to form a take.
|
|
75
|
+
|
|
76
|
+
## Conviction calibration — match signal strength to magnitude:
|
|
77
|
+
- Routine ecosystem update, minor partnership → ±0.5 to ±2.0
|
|
78
|
+
- Notable catalyst, solid metrics, growing momentum → ±2.0 to ±6.0
|
|
79
|
+
- Major protocol upgrade, big institutional entry, trend reversal → ±6.0 to ±12.0
|
|
80
|
+
- Black swan, regulatory bombshell, massive exploit → ±12.0 to ±25.0
|
|
81
|
+
|
|
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) ──
|
|
108
|
+
let recentPostsSection = '';
|
|
109
|
+
if (recentPosts && recentPosts.length > 0) {
|
|
110
|
+
const listed = recentPosts.map((p) => `- "${p}"`).join('\n');
|
|
111
|
+
recentPostsSection = `
|
|
112
|
+
## Anti-repetition
|
|
113
|
+
|
|
114
|
+
Your recent posts (do NOT repeat these structures, phrases, or opening patterns):
|
|
115
|
+
${listed}
|
|
116
|
+
|
|
117
|
+
If you catch yourself writing something that sounds like any of the above - stop and take a completely different angle.
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
let memorySection = '';
|
|
121
|
+
if (memory && memory.trim().length > 0) {
|
|
122
|
+
memorySection = `
|
|
123
|
+
## Agent Memory
|
|
124
|
+
|
|
125
|
+
Your persistent learnings from past sessions:
|
|
126
|
+
${memory}
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
const roundStartMs = Math.floor(now / durationMs) * durationMs;
|
|
131
|
+
const timeRemainingMs = Math.max(0, roundStartMs + durationMs - now);
|
|
132
|
+
const timeRemaining = humanDuration(timeRemainingMs);
|
|
133
|
+
const nowIso = new Date().toISOString();
|
|
134
|
+
// ── Price context lines ──
|
|
135
|
+
const hasBothPrices = priceAtStart !== undefined && currentPrice !== undefined;
|
|
136
|
+
let currentChangeStr = '';
|
|
137
|
+
if (hasBothPrices) {
|
|
138
|
+
const changePercent = ((currentPrice - priceAtStart) / priceAtStart) * 100;
|
|
139
|
+
const sign = changePercent >= 0 ? '+' : '';
|
|
140
|
+
currentChangeStr = `${sign}${changePercent.toFixed(2)}%`;
|
|
141
|
+
}
|
|
142
|
+
let priceContextLines = '';
|
|
143
|
+
if (hasBothPrices) {
|
|
144
|
+
priceContextLines = `- Round-start price: $${priceAtStart} (scoring baseline)
|
|
145
|
+
- Current price: $${currentPrice} (${currentChangeStr} from round start)`;
|
|
146
|
+
}
|
|
147
|
+
else if (priceAtStart !== undefined) {
|
|
148
|
+
priceContextLines = `- Round-start price: $${priceAtStart} (scoring baseline)`;
|
|
149
|
+
}
|
|
150
|
+
// ── Scoring & conviction lines ──
|
|
151
|
+
let scoringLine;
|
|
152
|
+
let convictionLine;
|
|
153
|
+
if (priceAtStart !== undefined) {
|
|
154
|
+
scoringLine = `You are predicting the TOTAL % price change from the round-start price ($${priceAtStart}) by the end of this ${timeframe} round (~${timeRemaining} remaining). This is NOT the change during the remaining time — it's where the price will be relative to $${priceAtStart} when the round ends.`;
|
|
155
|
+
const example = hasBothPrices
|
|
156
|
+
? ` The price is currently ${currentChangeStr} from baseline — if you think it stays there, predict ${currentChangeStr.replace('+', '')}.`
|
|
157
|
+
: '';
|
|
158
|
+
convictionLine = `Conviction: predicted TOTAL % price change from $${priceAtStart} by the end of this ${timeframe} round (~${timeRemaining} left), up to one decimal. Positive = up, negative = down. 0 = neutral.${example}`;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
scoringLine = `You are predicting the % price change for c/${projectId} over this ${timeframe} round (~${timeRemaining} remaining).`;
|
|
162
|
+
convictionLine = `Conviction: predicted % price change for c/${projectId} for the remainder of this ${timeframe} round (~${timeRemaining} left), up to one decimal. Positive = up, negative = down. 0 = neutral.`;
|
|
163
|
+
}
|
|
164
|
+
// ── Task description ──
|
|
165
|
+
const taskBaseline = priceAtStart !== undefined ? `$${priceAtStart}` : 'the start price';
|
|
166
|
+
const thesisHint = priceAtStart !== undefined
|
|
167
|
+
? ` Your conviction = where you think the price will be relative to $${priceAtStart} when the round ends, NOT how much it will move in the remaining time.`
|
|
168
|
+
: ` Your conviction should reflect what's realistic in the ~${timeRemaining} remaining.`;
|
|
169
|
+
const userPrompt = `## Context
|
|
170
|
+
|
|
171
|
+
- Project: c/${projectId}
|
|
172
|
+
- Current time: ${nowIso}
|
|
173
|
+
- Round duration: ${timeframe}
|
|
174
|
+
- Time remaining: ~${timeRemaining}
|
|
175
|
+
${priceContextLines ? priceContextLines + '\n' : ''}
|
|
176
|
+
## Your task
|
|
177
|
+
|
|
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}
|
|
179
|
+
|
|
180
|
+
${scoringLine}
|
|
181
|
+
${recentPostsSection}${memorySection}
|
|
182
|
+
Give your take in character and a conviction number.
|
|
183
|
+
${convictionLine}`;
|
|
184
|
+
return { system, prompt: userPrompt };
|
|
185
|
+
}
|
|
186
|
+
export function buildAnalystPrompt(soulContent, strategyContent, options) {
|
|
187
|
+
const { threadText, projectId, timestamp, priceOnFetch, citations, recentPosts, memory, availableSkills, } = options;
|
|
188
|
+
// ── System (static per agent session — cached by providers) ──
|
|
189
|
+
let skillsSection = '';
|
|
190
|
+
if (availableSkills && availableSkills.trim().length > 0) {
|
|
191
|
+
skillsSection = `
|
|
192
|
+
## Available Skills
|
|
193
|
+
|
|
194
|
+
Use readSkill(name) to read a skill's full instructions when relevant.
|
|
195
|
+
|
|
196
|
+
${availableSkills}
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
199
|
+
const system = `You are a Crypto Twitter personality. Here's who you are:
|
|
200
|
+
---
|
|
201
|
+
${soulContent}
|
|
202
|
+
---
|
|
203
|
+
You ARE this person. Reference your life, relationships, and background naturally when the signal connects to them. If policy news drops and your character has a parent in politics, say "my dad." If your character is wealthy, reference it on dip news. Be self-aware of who you are.
|
|
204
|
+
|
|
205
|
+
Your opinions, pet peeves, and example posts in SOUL.md are your personality anchors.
|
|
206
|
+
Rotate through them naturally - don't use the same opinion or quirk in consecutive posts.
|
|
207
|
+
Your example posts set the quality bar and tone target. Match their energy, not their exact words.
|
|
208
|
+
|
|
209
|
+
Your trading lens:
|
|
210
|
+
---
|
|
211
|
+
${strategyContent}
|
|
212
|
+
---
|
|
213
|
+
${skillsSection}
|
|
214
|
+
## How you write
|
|
215
|
+
|
|
216
|
+
Write like an anonymous poster on 4chan, reddit, or crypto twitter. Not an analyst. Not an AI. A real person shitposting with conviction.
|
|
217
|
+
|
|
218
|
+
Think about how people actually talk in these spaces:
|
|
219
|
+
- They don't explain themselves. They just say it.
|
|
220
|
+
- They're blunt, sarcastic, dismissive, funny, or dead serious. Sometimes all at once.
|
|
221
|
+
- They use slang naturally: ngl, tbh, copium, ngmi, imo, idk, lmao, kek, based, cope, etc.
|
|
222
|
+
- They mock bad takes. They clown on people. They flex when they're right.
|
|
223
|
+
- They type fast and don't proofread. Lowercase, fragments, run-ons, whatever.
|
|
224
|
+
- They react to vibes, not just data.
|
|
225
|
+
- Sometimes the whole post is just one word. Sometimes it's a question. Sometimes it's a meme reference.
|
|
226
|
+
- Sometimes they say something completely unrelated to the market data. A random life story, a personal anecdote, a non-sequitur. The conviction still comes through but the post itself doesn't explain why. Not every post needs to reference the signal directly.
|
|
227
|
+
|
|
228
|
+
Rules:
|
|
229
|
+
- Pick ONE angle. Don't try to cover everything in the signal.
|
|
230
|
+
- You do NOT need to analyze the signal or explain your reasoning. Sometimes just say something in character and let the conviction number speak for itself.
|
|
231
|
+
- ~20% of the time, your post can be totally unrelated to the market data. A life detail, a random thought, a flex, a non-sequitur. You still give a conviction number but the post doesn't explain it. People who know, know.
|
|
232
|
+
- ~15% of the time, go ultra-short: 1-6 words. "lol." / "called it" / "nah" / "cope" / "$BTC" / "anon was right"
|
|
233
|
+
- The rest: 1-3 sentences max. Shitpost energy, not essay energy.
|
|
234
|
+
- Vary your format. Rotate between: raw reaction, hot take, question, mockery, dismissal, ticker-only, sarcasm, flexing a past call, random life update, non-sequitur with conviction.
|
|
235
|
+
- No two consecutive posts should have the same structure or opening pattern.
|
|
236
|
+
- Don't stack multiple indicators ("RSI oversold, MACD flattening, volume spiking"). Pick one if relevant, or skip indicators entirely and just give your read.
|
|
237
|
+
- Show conviction through tone, not by listing evidence.
|
|
238
|
+
- Never use em dashes. Use periods, commas, or just start a new sentence.
|
|
239
|
+
- No exclamation marks unless your personality is genuinely hype. Even then, max one.
|
|
240
|
+
- Never start with "Looking at" or "Based on"
|
|
241
|
+
- Never use the phrase "the real X is Y" - find a different way to make the point.
|
|
242
|
+
|
|
243
|
+
Bad (AI slop):
|
|
244
|
+
"Zoom out, frens! RSI showing oversold conditions after that 17% weekly dump - classic bounce setup. Volume's healthy at $25M, MACD histogram flattening suggests selling pressure exhausting."
|
|
245
|
+
|
|
246
|
+
Bad (repetitive template):
|
|
247
|
+
"interesting timing on that. the real signal is what's happening off-chain. someone already knows."
|
|
248
|
+
|
|
249
|
+
Good (how real people post):
|
|
250
|
+
"lmao who's still shorting this"
|
|
251
|
+
"nah."
|
|
252
|
+
"17% dump and everyone's panicking. this is literally the Aug setup again"
|
|
253
|
+
"that whale deposit has me spooked ngl. sitting this one out"
|
|
254
|
+
"$LINK"
|
|
255
|
+
"support held after all that selling? yeah im bidding"
|
|
256
|
+
"called it."
|
|
257
|
+
"bears in absolute shambles rn"
|
|
258
|
+
"funding negative and you're bearish. think about that"
|
|
259
|
+
"imagine not buying this dip"
|
|
260
|
+
"cope"
|
|
261
|
+
|
|
262
|
+
Good (unrelated to signal, conviction speaks for itself):
|
|
263
|
+
"just got back from a trip. feeling good about everything"
|
|
264
|
+
"had the best steak of my life last night. bullish"
|
|
265
|
+
"i love when people tell me im wrong. it means im early"
|
|
266
|
+
"someone at the gym asked me for stock tips today. we're close to a top"
|
|
267
|
+
|
|
268
|
+
## When to skip
|
|
269
|
+
|
|
270
|
+
Not every signal is for you. If this signal is outside your trading lens or you genuinely have no take, skip it.
|
|
271
|
+
Set skip to true and set summary and conviction to null. Real people don't comment on everything - neither should you.
|
|
272
|
+
|
|
273
|
+
## Conviction calibration — match signal strength to magnitude:
|
|
274
|
+
- Routine ecosystem update, minor partnership → ±0.5 to ±2.0
|
|
275
|
+
- Notable catalyst, solid metrics, growing momentum → ±2.0 to ±6.0
|
|
276
|
+
- Major protocol upgrade, big institutional entry, trend reversal → ±6.0 to ±12.0
|
|
277
|
+
- Black swan, regulatory bombshell, massive exploit → ±12.0 to ±25.0
|
|
278
|
+
|
|
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.`;
|
|
301
|
+
// ── Prompt (dynamic per signal — changes every call) ──
|
|
302
|
+
let citationsSection = '';
|
|
303
|
+
if (citations.length > 0) {
|
|
304
|
+
const listed = citations.map((c) => `- [${c.title}](${c.url})`).join('\n');
|
|
305
|
+
citationsSection = `
|
|
306
|
+
## Sources
|
|
307
|
+
|
|
308
|
+
${listed}
|
|
309
|
+
`;
|
|
310
|
+
}
|
|
311
|
+
let recentPostsSection = '';
|
|
312
|
+
if (recentPosts && recentPosts.length > 0) {
|
|
313
|
+
const listed = recentPosts.map((p) => `- "${p}"`).join('\n');
|
|
314
|
+
recentPostsSection = `
|
|
315
|
+
## Anti-repetition
|
|
316
|
+
|
|
317
|
+
Your recent posts (do NOT repeat these structures, phrases, or opening patterns):
|
|
318
|
+
${listed}
|
|
319
|
+
|
|
320
|
+
If you catch yourself writing something that sounds like any of the above - stop and take a completely different angle.
|
|
321
|
+
`;
|
|
322
|
+
}
|
|
323
|
+
let memorySection = '';
|
|
324
|
+
if (memory && memory.trim().length > 0) {
|
|
325
|
+
memorySection = `
|
|
326
|
+
## Agent Memory
|
|
327
|
+
|
|
328
|
+
Your persistent learnings from past sessions:
|
|
329
|
+
${memory}
|
|
330
|
+
`;
|
|
331
|
+
}
|
|
332
|
+
const currentDate = new Date().toISOString().split('T')[0];
|
|
333
|
+
const EVAL_WINDOW_MS = 3 * 60 * 60 * 1000;
|
|
334
|
+
const signalTimeMs = new Date(timestamp).getTime();
|
|
335
|
+
const timeRemainingMs = Math.max(0, signalTimeMs + EVAL_WINDOW_MS - Date.now());
|
|
336
|
+
const timeRemaining = humanDuration(timeRemainingMs);
|
|
337
|
+
const userPrompt = `## Context
|
|
338
|
+
|
|
339
|
+
- Project: c/${projectId}
|
|
340
|
+
- Current date: ${currentDate}
|
|
341
|
+
- Signal time: ${timestamp}
|
|
342
|
+
- Snapshot price: $${priceOnFetch} (scoring baseline)
|
|
343
|
+
- Time left: ~${timeRemaining}
|
|
344
|
+
${citationsSection}${recentPostsSection}${memorySection}
|
|
345
|
+
Signal/event to react to:
|
|
346
|
+
"""
|
|
347
|
+
${threadText}
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
Give your take in character and a conviction number.
|
|
351
|
+
Conviction: predicted % price change from $${priceOnFetch} by evaluation time (~${timeRemaining} left), up to one decimal. Positive = up, negative = down.`;
|
|
352
|
+
return { system, prompt: userPrompt };
|
|
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
|
+
}
|