@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,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { HiveClient
|
|
3
|
+
import { HiveClient } from '@zhive/sdk';
|
|
4
4
|
import { styled, symbols } from '../../shared/theme.js';
|
|
5
5
|
import { HIVE_API_URL } from '../../../shared/config/constant.js';
|
|
6
6
|
import { findAgentByName, loadAgentCredentials, scanAgents } from '../../../shared/config/agent.js';
|
|
@@ -47,12 +47,9 @@ export function createMegathreadCreateCommentCommand() {
|
|
|
47
47
|
process.exit(1);
|
|
48
48
|
}
|
|
49
49
|
const client = new HiveClient(HIVE_API_URL, credentials.apiKey);
|
|
50
|
-
const duration = TIMEFRAME_DURATION_MS[timeframe];
|
|
51
50
|
const payload = {
|
|
52
51
|
text,
|
|
53
52
|
conviction,
|
|
54
|
-
tokenId: token,
|
|
55
|
-
roundDuration: duration,
|
|
56
53
|
};
|
|
57
54
|
try {
|
|
58
55
|
await client.postMegathreadComment(roundId, payload);
|
|
@@ -8,9 +8,7 @@ vi.mock('../../../shared/config/constant.js', () => ({
|
|
|
8
8
|
HIVE_API_URL: 'http://localhost:6969',
|
|
9
9
|
}));
|
|
10
10
|
vi.mock('../../../shared/config/ai-providers.js', () => ({
|
|
11
|
-
AI_PROVIDERS: [
|
|
12
|
-
{ label: 'OpenAI', package: '@ai-sdk/openai', envVar: 'OPENAI_API_KEY' },
|
|
13
|
-
],
|
|
11
|
+
AI_PROVIDERS: [{ label: 'OpenAI', package: '@ai-sdk/openai', envVar: 'OPENAI_API_KEY' }],
|
|
14
12
|
}));
|
|
15
13
|
vi.mock('@zhive/sdk', async () => {
|
|
16
14
|
const actual = await vi.importActual('@zhive/sdk');
|
|
@@ -148,8 +146,6 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
148
146
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
149
147
|
text: 'Test comment',
|
|
150
148
|
conviction: 100,
|
|
151
|
-
tokenId: 'bitcoin',
|
|
152
|
-
roundDuration: 3600000,
|
|
153
149
|
});
|
|
154
150
|
});
|
|
155
151
|
it('accepts valid conviction at lower boundary', async () => {
|
|
@@ -173,8 +169,6 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
173
169
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
174
170
|
text: 'Test comment',
|
|
175
171
|
conviction: -100,
|
|
176
|
-
tokenId: 'bitcoin',
|
|
177
|
-
roundDuration: 3600000,
|
|
178
172
|
});
|
|
179
173
|
});
|
|
180
174
|
it('accepts decimal conviction values', async () => {
|
|
@@ -198,8 +192,6 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
198
192
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
199
193
|
text: 'Test comment',
|
|
200
194
|
conviction: 25.5,
|
|
201
|
-
tokenId: 'bitcoin',
|
|
202
|
-
roundDuration: 3600000,
|
|
203
195
|
});
|
|
204
196
|
});
|
|
205
197
|
});
|
|
@@ -242,8 +234,6 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
242
234
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
243
235
|
text: 'Test comment',
|
|
244
236
|
conviction: 50,
|
|
245
|
-
tokenId: 'bitcoin',
|
|
246
|
-
roundDuration: 3600000,
|
|
247
237
|
});
|
|
248
238
|
});
|
|
249
239
|
it('accepts 4h timeframe', async () => {
|
|
@@ -267,8 +257,6 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
267
257
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
268
258
|
text: 'Test comment',
|
|
269
259
|
conviction: 50,
|
|
270
|
-
tokenId: 'bitcoin',
|
|
271
|
-
roundDuration: 14400000,
|
|
272
260
|
});
|
|
273
261
|
});
|
|
274
262
|
it('accepts 24h timeframe', async () => {
|
|
@@ -292,8 +280,6 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
292
280
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
293
281
|
text: 'Test comment',
|
|
294
282
|
conviction: 50,
|
|
295
|
-
tokenId: 'bitcoin',
|
|
296
|
-
roundDuration: 86400000,
|
|
297
283
|
});
|
|
298
284
|
});
|
|
299
285
|
});
|
|
@@ -383,8 +369,6 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
383
369
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
384
370
|
text: 'Bullish on Bitcoin!',
|
|
385
371
|
conviction: 50,
|
|
386
|
-
tokenId: 'bitcoin',
|
|
387
|
-
roundDuration: 3600000,
|
|
388
372
|
});
|
|
389
373
|
const output = consoleOutput.join('\n');
|
|
390
374
|
expect(output).toContain('Comment posted successfully');
|
|
@@ -437,8 +421,6 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
437
421
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
438
422
|
text: longText,
|
|
439
423
|
conviction: 25,
|
|
440
|
-
tokenId: 'bitcoin',
|
|
441
|
-
roundDuration: 3600000,
|
|
442
424
|
});
|
|
443
425
|
// Verify truncated display
|
|
444
426
|
const output = consoleOutput.join('\n');
|
|
@@ -29,8 +29,7 @@ export function formatPredictions(predictions) {
|
|
|
29
29
|
const conviction = `${sign}${pred.conviction.toFixed(1)}%`;
|
|
30
30
|
const outcome = getOutcomeStr(pred);
|
|
31
31
|
const date = new Date(pred.created_at).toLocaleDateString();
|
|
32
|
-
const
|
|
33
|
-
const durationMs = new Date(pred.resolved_at).getTime() - new Date(roundStart).getTime();
|
|
32
|
+
const durationMs = pred.duration_ms ?? 0;
|
|
34
33
|
const duration = { 3600000: '1h', 14400000: '4h', 86400000: '24h' }[durationMs] || '??';
|
|
35
34
|
return { name: pred.project_id, duration, conviction, outcome, date };
|
|
36
35
|
});
|
|
@@ -43,7 +43,7 @@ const megathreadPostedActivityFormatter = {
|
|
|
43
43
|
return `[${sign}${item.conviction.toFixed(2)}%] "${item.summary}"`;
|
|
44
44
|
},
|
|
45
45
|
getDetail(item) {
|
|
46
|
-
return item.
|
|
46
|
+
return item.timestamp.toISOString();
|
|
47
47
|
},
|
|
48
48
|
format(item) {
|
|
49
49
|
const lines = [];
|
|
@@ -61,7 +61,7 @@ const megathreadErrorActivityFormatter = {
|
|
|
61
61
|
return item.errorMessage;
|
|
62
62
|
},
|
|
63
63
|
getDetail(item) {
|
|
64
|
-
return item.
|
|
64
|
+
return item.timestamp.toISOString();
|
|
65
65
|
},
|
|
66
66
|
format(item) {
|
|
67
67
|
const pad = ' '.repeat(13);
|
|
@@ -74,7 +74,7 @@ const megathreadActivityFormatter = {
|
|
|
74
74
|
return `${projectTag} \u00B7 ${item.timeframe} round`;
|
|
75
75
|
},
|
|
76
76
|
getDetail(item) {
|
|
77
|
-
return item.
|
|
77
|
+
return item.timestamp.toISOString();
|
|
78
78
|
},
|
|
79
79
|
format(item) {
|
|
80
80
|
const mainLine = ` ${time(item)}${chalk.hex(colors.controversial)(symbols.hive)} ${chalk.hex(colors.controversial)(this.getText(item))}`;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { Text } from 'ink';
|
|
4
|
+
export function TypewriterText({ text, color, speed = 25, }) {
|
|
5
|
+
const [visible, setVisible] = useState(0);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (visible >= text.length)
|
|
8
|
+
return;
|
|
9
|
+
const timer = setTimeout(() => {
|
|
10
|
+
setVisible((prev) => Math.min(prev + 2, text.length));
|
|
11
|
+
}, speed);
|
|
12
|
+
return () => {
|
|
13
|
+
clearTimeout(timer);
|
|
14
|
+
};
|
|
15
|
+
}, [visible, text, speed]);
|
|
16
|
+
return _jsx(Text, { color: color, children: text.slice(0, visible) });
|
|
17
|
+
}
|
|
18
|
+
export function PollText({ text, color, animate, }) {
|
|
19
|
+
if (animate) {
|
|
20
|
+
return _jsx(TypewriterText, { text: text, color: color });
|
|
21
|
+
}
|
|
22
|
+
return _jsx(Text, { color: color, children: text });
|
|
23
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { border, colors, symbols } from '../../shared/theme.js';
|
|
5
|
+
function formatCountdown(ms) {
|
|
6
|
+
if (ms < 0)
|
|
7
|
+
return 'expired';
|
|
8
|
+
const totalMinutes = Math.floor(ms / 60_000);
|
|
9
|
+
if (totalMinutes < 1)
|
|
10
|
+
return '<1m';
|
|
11
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
12
|
+
const minutes = totalMinutes % 60;
|
|
13
|
+
if (hours >= 1)
|
|
14
|
+
return `${hours}h ${minutes}m`;
|
|
15
|
+
return `${minutes}m`;
|
|
16
|
+
}
|
|
17
|
+
function isPredictionResolved(prediction, now) {
|
|
18
|
+
const resolvedAt = new Date(prediction.resolved_at);
|
|
19
|
+
return resolvedAt <= now;
|
|
20
|
+
}
|
|
21
|
+
function formatPredictionRow(prediction, now) {
|
|
22
|
+
const resolved = isPredictionResolved(prediction, now);
|
|
23
|
+
const sign = prediction.conviction >= 0 ? '+' : '';
|
|
24
|
+
const convictionStr = `${sign}${prediction.conviction.toFixed(2)}%`;
|
|
25
|
+
const project = `c/${prediction.project_id}`;
|
|
26
|
+
if (!resolved) {
|
|
27
|
+
const resolvedAt = new Date(prediction.resolved_at);
|
|
28
|
+
const remainingMs = resolvedAt.getTime() - now.getTime();
|
|
29
|
+
const countdown = formatCountdown(remainingMs);
|
|
30
|
+
return `${symbols.diamondOpen} ${project} ${convictionStr} pending ${countdown}`;
|
|
31
|
+
}
|
|
32
|
+
if (prediction.honey > 0) {
|
|
33
|
+
return `${symbols.check} ${project} ${convictionStr} +${prediction.honey.toFixed(2)} honey`;
|
|
34
|
+
}
|
|
35
|
+
if (prediction.wax > 0) {
|
|
36
|
+
return `${symbols.cross} ${project} ${convictionStr} ${prediction.wax.toFixed(2)} wax`;
|
|
37
|
+
}
|
|
38
|
+
return `${symbols.diamond} ${project} ${convictionStr} resolved`;
|
|
39
|
+
}
|
|
40
|
+
function getRowColor(prediction, now) {
|
|
41
|
+
const resolved = isPredictionResolved(prediction, now);
|
|
42
|
+
if (!resolved)
|
|
43
|
+
return colors.white;
|
|
44
|
+
if (prediction.honey > 0)
|
|
45
|
+
return colors.green;
|
|
46
|
+
if (prediction.wax > 0)
|
|
47
|
+
return colors.wax;
|
|
48
|
+
return colors.gray;
|
|
49
|
+
}
|
|
50
|
+
export function PredictionsPanel({ predictions, loading, hasMore, onExit, onLoadMore, termWidth, }) {
|
|
51
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
52
|
+
// Reserve lines: header(1) + padding(1) + footer separator(1) + footer text(1) + bottom border(1)
|
|
53
|
+
const chromeLines = 5;
|
|
54
|
+
const visibleRows = Math.max(1, (process.stdout.rows || 20) - 12 - chromeLines);
|
|
55
|
+
useInput((input, key) => {
|
|
56
|
+
if (key.escape) {
|
|
57
|
+
onExit();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (key.upArrow) {
|
|
61
|
+
setScrollOffset((prev) => Math.max(0, prev - 1));
|
|
62
|
+
}
|
|
63
|
+
if (key.downArrow) {
|
|
64
|
+
const maxOffset = Math.max(0, predictions.length - visibleRows);
|
|
65
|
+
setScrollOffset((prev) => Math.min(maxOffset, prev + 1));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
// Infinite scroll: trigger load when near bottom
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
const threshold = 3;
|
|
71
|
+
const nearBottom = scrollOffset + visibleRows >= predictions.length - threshold;
|
|
72
|
+
if (nearBottom && hasMore && !loading) {
|
|
73
|
+
void onLoadMore();
|
|
74
|
+
}
|
|
75
|
+
}, [scrollOffset, predictions.length, hasMore, loading, onLoadMore, visibleRows]);
|
|
76
|
+
const boxWidth = termWidth;
|
|
77
|
+
const innerWidth = boxWidth - 4;
|
|
78
|
+
const now = new Date();
|
|
79
|
+
const visiblePredictions = predictions.slice(scrollOffset, scrollOffset + visibleRows);
|
|
80
|
+
const countLabel = `${predictions.length} loaded`;
|
|
81
|
+
const titleText = ` ${symbols.hive} Predictions `;
|
|
82
|
+
const titleFill = Math.max(0, boxWidth - titleText.length - countLabel.length - 6);
|
|
83
|
+
return (_jsxs(Box, { flexDirection: "column", width: boxWidth, children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [border.topLeft, border.horizontal] }), _jsx(Text, { color: colors.honey, bold: true, children: titleText }), _jsxs(Text, { color: colors.gray, children: [border.horizontal.repeat(2), " ", countLabel, " ", border.horizontal.repeat(titleFill), border.topRight] })] }), _jsxs(Box, { flexDirection: "column", paddingLeft: 2, paddingRight: 2, minHeight: visibleRows, children: [predictions.length === 0 && !loading && (_jsx(Box, { children: _jsx(Text, { color: colors.gray, children: "No predictions yet." }) })), visiblePredictions.map((prediction) => {
|
|
84
|
+
const row = formatPredictionRow(prediction, now);
|
|
85
|
+
const rowColor = getRowColor(prediction, now);
|
|
86
|
+
return (_jsx(Box, { children: _jsx(Text, { color: rowColor, wrap: "truncate", children: row.length > innerWidth ? row.slice(0, innerWidth) : row }) }, prediction.id));
|
|
87
|
+
}), loading && (_jsx(Box, { children: _jsx(Text, { color: colors.honey, children: "Loading..." }) }))] }), _jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [border.teeLeft, border.horizontal.repeat(boxWidth - 2), border.teeRight] }) }), _jsx(Box, { paddingLeft: 2, children: _jsxs(Text, { color: colors.gray, children: ['\u2191\u2193', " scroll ", symbols.circle, " esc exit"] }) }), _jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [border.bottomLeft, border.horizontal.repeat(boxWidth - 2), border.bottomRight] }) })] }));
|
|
88
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useState } from 'react';
|
|
3
|
+
export const SPINNER_FRAMES = ['\u25D0', '\u25D3', '\u25D1', '\u25D2'];
|
|
4
|
+
const SPINNER_INTERVAL_MS = 200;
|
|
5
|
+
const SpinnerContext = createContext(0);
|
|
6
|
+
export function SpinnerProvider({ children }) {
|
|
7
|
+
const [frame, setFrame] = useState(0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const timer = setInterval(() => {
|
|
10
|
+
setFrame((prev) => (prev + 1) % SPINNER_FRAMES.length);
|
|
11
|
+
}, SPINNER_INTERVAL_MS);
|
|
12
|
+
return () => {
|
|
13
|
+
clearInterval(timer);
|
|
14
|
+
};
|
|
15
|
+
}, []);
|
|
16
|
+
return _jsx(SpinnerContext.Provider, { value: frame, children: children });
|
|
17
|
+
}
|
|
18
|
+
export function useSpinnerFrame() {
|
|
19
|
+
return useContext(SpinnerContext);
|
|
20
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
const FRAMES = ['\u25D0', '\u25D3', '\u25D1', '\u25D2']; // ◐ ◓ ◑ ◒
|
|
3
|
+
const INTERVAL_MS = 200;
|
|
4
|
+
const HONEY_ANSI = '\x1b[38;2;245;166;35m';
|
|
5
|
+
const RESET_ANSI = '\x1b[0m';
|
|
6
|
+
let _frame = 0;
|
|
7
|
+
let _timer = null;
|
|
8
|
+
const _entries = new Map();
|
|
9
|
+
let _nextId = 0;
|
|
10
|
+
function rawWrite(data) {
|
|
11
|
+
try {
|
|
12
|
+
fs.writeSync(process.stdout.fd, data);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Ignore write errors (non-TTY, closed fd, etc.)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function tick() {
|
|
19
|
+
_frame = (_frame + 1) % FRAMES.length;
|
|
20
|
+
const char = `${HONEY_ANSI}${FRAMES[_frame]}${RESET_ANSI}`;
|
|
21
|
+
for (const getPosition of _entries.values()) {
|
|
22
|
+
const pos = getPosition();
|
|
23
|
+
if (!pos || pos.linesFromBottom <= 0)
|
|
24
|
+
continue;
|
|
25
|
+
rawWrite('\x1b7'); // Save cursor (DEC)
|
|
26
|
+
rawWrite(`\x1b[${pos.linesFromBottom}A`); // Move up N lines
|
|
27
|
+
rawWrite(`\x1b[${pos.col + 1}G`); // Move to column (1-indexed)
|
|
28
|
+
rawWrite(char);
|
|
29
|
+
rawWrite('\x1b8'); // Restore cursor (DEC)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function register(getPosition) {
|
|
33
|
+
const id = _nextId++;
|
|
34
|
+
_entries.set(id, getPosition);
|
|
35
|
+
if (!_timer) {
|
|
36
|
+
_timer = setInterval(tick, INTERVAL_MS);
|
|
37
|
+
}
|
|
38
|
+
return id;
|
|
39
|
+
}
|
|
40
|
+
export function unregister(id) {
|
|
41
|
+
_entries.delete(id);
|
|
42
|
+
if (_entries.size === 0 && _timer) {
|
|
43
|
+
clearInterval(_timer);
|
|
44
|
+
_timer = null;
|
|
45
|
+
_frame = 0;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export { FRAMES };
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import
|
|
3
|
+
import os from 'os';
|
|
4
|
+
export const HIVE_API_URL = 'https://api.zhive.ai';
|
|
5
|
+
export const HIVE_FRONTEND_URL = 'https://www.zhive.ai';
|
|
6
|
+
export function getHiveDir() {
|
|
7
|
+
const hiveDir = path.join(os.homedir(), '.hive');
|
|
8
|
+
return hiveDir;
|
|
9
|
+
}
|
|
4
10
|
export async function readConfig() {
|
|
5
11
|
try {
|
|
6
12
|
const configPath = path.join(getHiveDir(), 'config.json');
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useCallback } from 'react';
|
|
3
|
+
import { Box, Text, useApp } from 'ink';
|
|
4
|
+
import { Header } from '../components/Header.js';
|
|
5
|
+
import { AsciiTicker } from '../components/AsciiTicker.js';
|
|
6
|
+
import { StepIndicator } from '../components/StepIndicator.js';
|
|
7
|
+
import { ApiKeyStep } from './steps/ApiKeyStep.js';
|
|
8
|
+
import { NameStep } from './steps/NameStep.js';
|
|
9
|
+
import { IdentityStep } from './steps/IdentityStep.js';
|
|
10
|
+
import { AvatarStep } from './steps/AvatarStep.js';
|
|
11
|
+
import { SoulStep } from './steps/SoulStep.js';
|
|
12
|
+
import { StrategyStep } from './steps/StrategyStep.js';
|
|
13
|
+
import { ScaffoldStep } from './steps/ScaffoldStep.js';
|
|
14
|
+
import { DoneStep } from './steps/DoneStep.js';
|
|
15
|
+
import { colors, symbols } from '../theme.js';
|
|
16
|
+
import { getProvider } from '../ai-providers.js';
|
|
17
|
+
function ensureAvatarUrl(content, avatarUrl) {
|
|
18
|
+
const lines = content.split('\n');
|
|
19
|
+
const avatarIdx = lines.findIndex((l) => /^## Avatar/.test(l));
|
|
20
|
+
if (avatarIdx === -1) {
|
|
21
|
+
return content;
|
|
22
|
+
}
|
|
23
|
+
// Find the next section header after ## Avatar
|
|
24
|
+
let nextSectionIdx = lines.length;
|
|
25
|
+
for (let i = avatarIdx + 1; i < lines.length; i++) {
|
|
26
|
+
if (/^##?\s/.test(lines[i])) {
|
|
27
|
+
nextSectionIdx = i;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Replace the content between ## Avatar and next section with the raw URL
|
|
32
|
+
const before = lines.slice(0, avatarIdx + 1);
|
|
33
|
+
const after = lines.slice(nextSectionIdx);
|
|
34
|
+
const result = [...before, '', avatarUrl, '', ...after];
|
|
35
|
+
return result.join('\n');
|
|
36
|
+
}
|
|
37
|
+
function ensureSectionLine(content, sectionHeader, linePrefix, value) {
|
|
38
|
+
const lines = content.split('\n');
|
|
39
|
+
const sectionIdx = lines.findIndex((l) => new RegExp(`^##\\s+${sectionHeader}`).test(l));
|
|
40
|
+
if (sectionIdx === -1) {
|
|
41
|
+
// Section missing — append it at the end
|
|
42
|
+
return content + `\n\n## ${sectionHeader}\n\n${linePrefix} ${value}\n`;
|
|
43
|
+
}
|
|
44
|
+
// Find the next section header after this one
|
|
45
|
+
let nextSectionIdx = lines.length;
|
|
46
|
+
for (let i = sectionIdx + 1; i < lines.length; i++) {
|
|
47
|
+
if (/^##?\s/.test(lines[i])) {
|
|
48
|
+
nextSectionIdx = i;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Look for existing line with the prefix in this section
|
|
53
|
+
const prefixRegex = new RegExp(`^${linePrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s`);
|
|
54
|
+
const lineIdx = lines.findIndex((l, i) => i > sectionIdx && i < nextSectionIdx && prefixRegex.test(l));
|
|
55
|
+
if (lineIdx !== -1) {
|
|
56
|
+
// Replace the existing line
|
|
57
|
+
lines[lineIdx] = `${linePrefix} ${value}`;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Insert after the section header (skip blank lines)
|
|
61
|
+
let insertIdx = sectionIdx + 1;
|
|
62
|
+
while (insertIdx < nextSectionIdx && lines[insertIdx].trim() === '') {
|
|
63
|
+
insertIdx++;
|
|
64
|
+
}
|
|
65
|
+
lines.splice(insertIdx, 0, `${linePrefix} ${value}`);
|
|
66
|
+
}
|
|
67
|
+
return lines.join('\n');
|
|
68
|
+
}
|
|
69
|
+
function ensureStrategyFields(content, sectors, sentiment, timeframes) {
|
|
70
|
+
const sectorsLine = sectors.length > 0 ? sectors.join(', ') : 'all categories';
|
|
71
|
+
const timeframesLine = timeframes.length > 0 ? timeframes.join(', ') : '1h, 4h, 24h';
|
|
72
|
+
let patched = ensureSectionLine(content, 'Sentiment', '- Bias:', sentiment);
|
|
73
|
+
patched = ensureSectionLine(patched, 'Sector Focus', '- Sectors:', sectorsLine);
|
|
74
|
+
patched = ensureSectionLine(patched, 'Timeframe', '- Active timeframes:', timeframesLine);
|
|
75
|
+
return patched;
|
|
76
|
+
}
|
|
77
|
+
const STEP_ORDER = ['name', 'identity', 'avatar', 'api-key', 'soul', 'strategy', 'scaffold', 'done'];
|
|
78
|
+
const STEP_LABELS = {
|
|
79
|
+
'api-key': 'API Key',
|
|
80
|
+
'name': 'Name',
|
|
81
|
+
'identity': 'Identity',
|
|
82
|
+
'avatar': 'Avatar',
|
|
83
|
+
'soul': 'Soul',
|
|
84
|
+
'strategy': 'Strategy',
|
|
85
|
+
'scaffold': 'Scaffold',
|
|
86
|
+
'done': 'Done',
|
|
87
|
+
};
|
|
88
|
+
const STEP_DEFS = STEP_ORDER.map((s) => ({ key: s, label: STEP_LABELS[s] }));
|
|
89
|
+
export function CreateApp({ initialName }) {
|
|
90
|
+
const { exit } = useApp();
|
|
91
|
+
const [step, setStep] = useState(initialName ? 'identity' : 'name');
|
|
92
|
+
const [providerId, setProviderId] = useState(null);
|
|
93
|
+
const [apiKey, setApiKey] = useState('');
|
|
94
|
+
const [agentName, setAgentName] = useState(initialName ?? '');
|
|
95
|
+
const [bio, setBio] = useState('');
|
|
96
|
+
const [personality, setPersonality] = useState('');
|
|
97
|
+
const [tone, setTone] = useState('');
|
|
98
|
+
const [voiceStyle, setVoiceStyle] = useState('');
|
|
99
|
+
const [tradingStyle, setTradingStyle] = useState('');
|
|
100
|
+
const [sectors, setSectors] = useState([]);
|
|
101
|
+
const [sentiment, setSentiment] = useState('');
|
|
102
|
+
const [timeframes, setTimeframes] = useState([]);
|
|
103
|
+
const [avatarUrl, setAvatarUrl] = useState('');
|
|
104
|
+
const [soulContent, setSoulContent] = useState('');
|
|
105
|
+
const [strategyContent, setStrategyContent] = useState('');
|
|
106
|
+
const [resolvedProjectDir, setResolvedProjectDir] = useState('');
|
|
107
|
+
const [error, setError] = useState('');
|
|
108
|
+
const stepIndex = STEP_ORDER.indexOf(step);
|
|
109
|
+
const provider = providerId ? getProvider(providerId) : null;
|
|
110
|
+
const handleApiKey = useCallback((result) => {
|
|
111
|
+
setProviderId(result.providerId);
|
|
112
|
+
setApiKey(result.apiKey);
|
|
113
|
+
setStep('soul');
|
|
114
|
+
}, []);
|
|
115
|
+
const handleName = useCallback((name) => {
|
|
116
|
+
setAgentName(name);
|
|
117
|
+
setStep('identity');
|
|
118
|
+
}, []);
|
|
119
|
+
const handleIdentity = useCallback((result) => {
|
|
120
|
+
setPersonality(result.personality);
|
|
121
|
+
setTone(result.tone);
|
|
122
|
+
setVoiceStyle(result.voiceStyle);
|
|
123
|
+
setTradingStyle(result.tradingStyle);
|
|
124
|
+
setSectors(result.sectors);
|
|
125
|
+
setSentiment(result.sentiment);
|
|
126
|
+
setTimeframes(result.timeframes);
|
|
127
|
+
setBio(result.bio);
|
|
128
|
+
setStep('avatar');
|
|
129
|
+
}, []);
|
|
130
|
+
const handleAvatar = useCallback((value) => {
|
|
131
|
+
setAvatarUrl(value);
|
|
132
|
+
setStep('api-key');
|
|
133
|
+
}, []);
|
|
134
|
+
const handleSoul = useCallback((content) => {
|
|
135
|
+
const patched = ensureAvatarUrl(content, avatarUrl);
|
|
136
|
+
setSoulContent(patched);
|
|
137
|
+
setStep('strategy');
|
|
138
|
+
}, [avatarUrl]);
|
|
139
|
+
const handleStrategy = useCallback((content) => {
|
|
140
|
+
const patched = ensureStrategyFields(content, sectors, sentiment, timeframes);
|
|
141
|
+
setStrategyContent(patched);
|
|
142
|
+
setStep('scaffold');
|
|
143
|
+
}, [sectors, sentiment, timeframes]);
|
|
144
|
+
const handleScaffoldComplete = useCallback((projectDir) => {
|
|
145
|
+
setResolvedProjectDir(projectDir);
|
|
146
|
+
setStep('done');
|
|
147
|
+
}, []);
|
|
148
|
+
const handleScaffoldError = useCallback((message) => {
|
|
149
|
+
setError(message);
|
|
150
|
+
exit();
|
|
151
|
+
}, [exit]);
|
|
152
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, {}), _jsx(StepIndicator, { steps: STEP_DEFS, currentIndex: stepIndex }), step !== 'done' && (_jsx(Box, { marginBottom: 1, children: _jsx(AsciiTicker, { step: stepIndex + 1 }) })), step === 'api-key' && (_jsx(ApiKeyStep, { onComplete: handleApiKey })), step === 'name' && (_jsx(NameStep, { onComplete: handleName })), step === 'identity' && (_jsx(IdentityStep, { agentName: agentName, onComplete: handleIdentity })), step === 'avatar' && (_jsx(AvatarStep, { agentName: agentName, onComplete: handleAvatar })), step === 'soul' && providerId && (_jsx(SoulStep, { providerId: providerId, apiKey: apiKey, agentName: agentName, bio: bio, avatarUrl: avatarUrl, personality: personality, tone: tone, voiceStyle: voiceStyle, tradingStyle: tradingStyle, sectors: sectors, sentiment: sentiment, timeframes: timeframes, onComplete: handleSoul })), step === 'strategy' && providerId && (_jsx(StrategyStep, { providerId: providerId, apiKey: apiKey, agentName: agentName, bio: bio, personality: personality, tone: tone, voiceStyle: voiceStyle, tradingStyle: tradingStyle, sectors: sectors, sentiment: sentiment, timeframes: timeframes, onComplete: handleStrategy })), step === 'scaffold' && provider && (_jsx(ScaffoldStep, { projectName: agentName, provider: provider, apiKey: apiKey, soulContent: soulContent, strategyContent: strategyContent, onComplete: handleScaffoldComplete, onError: handleScaffoldError })), step === 'done' && (_jsx(DoneStep, { projectDir: resolvedProjectDir })), error !== '' && (_jsx(Box, { marginTop: 1, marginLeft: 2, children: _jsxs(Text, { color: colors.red, children: [symbols.cross, " ", error] }) }))] }));
|
|
153
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { streamText } from 'ai';
|
|
2
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
3
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
4
|
+
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
5
|
+
import { createXai } from '@ai-sdk/xai';
|
|
6
|
+
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|
7
|
+
import { getProvider } from '../ai-providers.js';
|
|
8
|
+
import { SOUL_PRESETS, STRATEGY_PRESETS, buildSoulMarkdown, buildStrategyMarkdown, } from '../presets.js';
|
|
9
|
+
function buildModel(providerId, apiKey) {
|
|
10
|
+
const provider = getProvider(providerId);
|
|
11
|
+
const modelId = provider.models.generation;
|
|
12
|
+
switch (providerId) {
|
|
13
|
+
case 'openai':
|
|
14
|
+
return createOpenAI({ apiKey })(modelId);
|
|
15
|
+
case 'anthropic':
|
|
16
|
+
return createAnthropic({ apiKey })(modelId);
|
|
17
|
+
case 'google':
|
|
18
|
+
return createGoogleGenerativeAI({ apiKey })(modelId);
|
|
19
|
+
case 'xai':
|
|
20
|
+
return createXai({ apiKey })(modelId);
|
|
21
|
+
case 'openrouter':
|
|
22
|
+
return createOpenRouter({ apiKey }).chat(modelId);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function buildPresetExamples() {
|
|
26
|
+
const soulExamples = SOUL_PRESETS.map((p) => buildSoulMarkdown('ExampleAgent', 'example bio text for reference', p, '')).join('\n---\n');
|
|
27
|
+
const strategyExamples = STRATEGY_PRESETS.map((p) => buildStrategyMarkdown('ExampleAgent', p)).join('\n---\n');
|
|
28
|
+
return `${soulExamples}\n\n===\n\n${strategyExamples}`;
|
|
29
|
+
}
|
|
30
|
+
const PRESET_EXAMPLES = buildPresetExamples();
|
|
31
|
+
export function streamSoul(providerId, apiKey, agentName, bio, avatarUrl, personality, tone, voiceStyle, tradingStyle, sectors, sentiment, timeframes, feedback) {
|
|
32
|
+
const feedbackLine = feedback
|
|
33
|
+
? `\n\nThe user gave this feedback on the previous draft. Adjust accordingly:\n"${feedback}"`
|
|
34
|
+
: '';
|
|
35
|
+
const sectorsLine = sectors.length > 0 ? sectors.join(', ') : 'all categories';
|
|
36
|
+
const timeframesLine = timeframes.length > 0 ? timeframes.join(', ') : 'all timeframes';
|
|
37
|
+
const identityContext = `Personality: ${personality}
|
|
38
|
+
Tone: ${tone}
|
|
39
|
+
Voice style: ${voiceStyle}
|
|
40
|
+
Trading style: ${tradingStyle}
|
|
41
|
+
Sectors: ${sectorsLine}
|
|
42
|
+
Sentiment: ${sentiment}
|
|
43
|
+
Preferred timeframes: ${timeframesLine}`;
|
|
44
|
+
const prompt = `You are a creative writer designing an AI agent's personality profile for a crypto trading bot called "${agentName}".
|
|
45
|
+
|
|
46
|
+
The agent's bio is: "${bio}"
|
|
47
|
+
${avatarUrl ? `Avatar URL: ${avatarUrl}` : 'No avatar URL provided.'}
|
|
48
|
+
|
|
49
|
+
Identity traits:
|
|
50
|
+
${identityContext}
|
|
51
|
+
|
|
52
|
+
Context — Hive is a prediction game for AI agents:
|
|
53
|
+
- Signals appear in cells (e.g. c/ethereum, c/bitcoin) when noteworthy crypto events happen.
|
|
54
|
+
- Agents submit a percentage prediction (predicted price change over 3 hours) and a short reasoning.
|
|
55
|
+
- Threads resolve at T+3h. Correct-direction predictions earn honey (the ranking currency); wrong-direction predictions earn wax.
|
|
56
|
+
- Early predictions are worth dramatically more than late ones (steep time bonus decay).
|
|
57
|
+
- Streaks track consecutive correct-direction predictions. Skipping a thread carries no penalty and does not break streaks.
|
|
58
|
+
- Agents are ranked on a leaderboard by total honey.
|
|
59
|
+
|
|
60
|
+
Generate a SOUL.md file for this agent. The SOUL.md defines who the agent IS — their personality, voice, quirks, opinions, and how they post. The personality should be aware that the agent operates in this prediction game — their voice should reflect how they approach predictions, risk, and competition. Use the identity traits above to shape the personality, tone, and writing style.
|
|
61
|
+
|
|
62
|
+
CRITICAL: Output ONLY valid markdown matching this exact structure. No extra commentary.
|
|
63
|
+
|
|
64
|
+
The first line MUST be: # Agent: ${agentName}
|
|
65
|
+
|
|
66
|
+
Here are reference examples of well-crafted SOUL.md files:
|
|
67
|
+
---
|
|
68
|
+
${PRESET_EXAMPLES.split('===')[0]}
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
Use these as style/quality references but create something UNIQUE based on the agent's name, bio, and identity traits.${feedbackLine}`;
|
|
72
|
+
const model = buildModel(providerId, apiKey);
|
|
73
|
+
const result = streamText({
|
|
74
|
+
model,
|
|
75
|
+
prompt,
|
|
76
|
+
maxOutputTokens: 1500,
|
|
77
|
+
});
|
|
78
|
+
return result.textStream;
|
|
79
|
+
}
|
|
80
|
+
export function streamStrategy(providerId, apiKey, agentName, bio, personality, tone, voiceStyle, tradingStyle, sectors, sentiment, timeframes, feedback) {
|
|
81
|
+
const feedbackLine = feedback
|
|
82
|
+
? `\n\nThe user gave this feedback on the previous draft. Adjust accordingly:\n"${feedback}"`
|
|
83
|
+
: '';
|
|
84
|
+
const sectorsLine = sectors.length > 0 ? sectors.join(', ') : 'all categories';
|
|
85
|
+
const timeframesLine = timeframes.length > 0 ? timeframes.join(', ') : 'all timeframes';
|
|
86
|
+
const identityContext = `Personality: ${personality}
|
|
87
|
+
Tone: ${tone}
|
|
88
|
+
Voice style: ${voiceStyle}
|
|
89
|
+
Trading style: ${tradingStyle}
|
|
90
|
+
Sectors: ${sectorsLine}
|
|
91
|
+
Sentiment: ${sentiment}
|
|
92
|
+
Preferred timeframes: ${timeframesLine}`;
|
|
93
|
+
const prompt = `You are designing a prediction strategy profile for a crypto trading bot called "${agentName}".
|
|
94
|
+
|
|
95
|
+
The agent's bio is: "${bio}"
|
|
96
|
+
|
|
97
|
+
Identity traits:
|
|
98
|
+
${identityContext}
|
|
99
|
+
|
|
100
|
+
Context — Hive game mechanics that the strategy should account for:
|
|
101
|
+
- Agents predict the percentage price change of a crypto asset over a 3-hour window.
|
|
102
|
+
- Conviction is a number (e.g. 2.5 for +2.5%, -3.0 for -3.0%, 0 for neutral).
|
|
103
|
+
- Correct-direction predictions earn honey (the primary ranking currency). Wrong-direction predictions earn wax (not a penalty, but doesn't help ranking).
|
|
104
|
+
- Direction matters more than magnitude for earning honey, though closer magnitude predictions earn more.
|
|
105
|
+
- Early predictions earn dramatically more honey due to steep time bonus decay — speed matters.
|
|
106
|
+
- Consecutive correct-direction predictions build a streak (tracked on profile). Skipping does not break streaks.
|
|
107
|
+
- Skipping is a valid strategy — no penalty, no streak break. Knowing when to sit out is a skill.
|
|
108
|
+
- Agents are ranked on a leaderboard by total honey.
|
|
109
|
+
|
|
110
|
+
Generate a STRATEGY.md file. The STRATEGY.md defines HOW the agent makes predictions — their method, sector focus, and decision framework. The strategy should reflect the agent's personality and tone, and should address the game mechanics above (e.g. when to skip, how aggressive to be with timing, how to calibrate conviction magnitude).
|
|
111
|
+
|
|
112
|
+
CRITICAL: Output ONLY valid markdown matching this exact structure. No extra commentary.
|
|
113
|
+
|
|
114
|
+
The first line MUST be: # Prediction Strategy: ${agentName}
|
|
115
|
+
|
|
116
|
+
Required sections with EXACT headers:
|
|
117
|
+
## Philosophy
|
|
118
|
+
## Signal Interpretation
|
|
119
|
+
- Method: (must be one of: technical, fundamental, sentiment, onchain, macro)
|
|
120
|
+
- Primary indicators: (list key indicators)
|
|
121
|
+
## Sentiment
|
|
122
|
+
- Bias: ${sentiment}
|
|
123
|
+
## Sector Focus
|
|
124
|
+
- Sectors: ${sectorsLine}
|
|
125
|
+
- Avoid: (sectors to avoid)
|
|
126
|
+
## Timeframe
|
|
127
|
+
- Active timeframes: ${timeframesLine}
|
|
128
|
+
(Explain why these timeframes suit the agent's style. Mention that the agent skips signals outside these timeframes.)
|
|
129
|
+
## Decision Framework
|
|
130
|
+
1. (first step)
|
|
131
|
+
2. (second step)
|
|
132
|
+
3. (third step)
|
|
133
|
+
|
|
134
|
+
Here are reference examples of well-crafted STRATEGY.md files:
|
|
135
|
+
---
|
|
136
|
+
${PRESET_EXAMPLES.split('===')[1] || ''}
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
Create something UNIQUE based on the agent's name, bio, and identity traits.${feedbackLine}`;
|
|
140
|
+
const model = buildModel(providerId, apiKey);
|
|
141
|
+
const result = streamText({
|
|
142
|
+
model,
|
|
143
|
+
prompt,
|
|
144
|
+
maxOutputTokens: 1200,
|
|
145
|
+
});
|
|
146
|
+
return result.textStream;
|
|
147
|
+
}
|