@zhive/cli 0.6.3 → 0.6.4
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/CLAUDE.md +7 -0
- package/dist/backtest/CLAUDE.md +7 -0
- package/dist/cli.js +20 -0
- package/dist/commands/agent/commands/profile.js +3 -2
- package/dist/commands/agent/commands/profile.test.js +10 -12
- package/dist/commands/doctor/commands/index.js +93 -0
- package/dist/commands/megathread/commands/create-comment.js +4 -9
- package/dist/commands/megathread/commands/create-comment.test.js +15 -173
- package/dist/commands/megathread/commands/list.js +5 -5
- package/dist/commands/megathread/commands/list.test.js +14 -14
- package/dist/commands/start/commands/prediction.js +3 -4
- package/dist/commands/start/hooks/useChat.js +40 -41
- package/dist/commands/start/services/command-registry.js +1 -1
- package/dist/index.js +2 -0
- package/dist/{agent → services/agent}/analysis.js +5 -5
- package/dist/{load-agent-env.js → services/agent/env.js} +1 -1
- package/dist/{agent → services/agent/helpers}/model.js +2 -2
- package/dist/{agent → services/agent/prompts}/memory-prompt.js +20 -22
- package/dist/{agent → services/agent/prompts}/prompt.js +80 -54
- package/dist/{agent → services/agent}/tools/market/client.js +1 -1
- package/dist/{agent → services/agent}/tools/mindshare/client.js +1 -1
- package/dist/{agents.js → services/config/agent.js} +2 -2
- package/dist/{config.js → services/config/config.js} +1 -7
- package/dist/services/config/constant.js +8 -0
- package/dist/shared/agent/config.js +75 -0
- package/dist/shared/agent/env.js +30 -0
- package/dist/shared/agent/helpers/model.js +92 -0
- package/dist/shared/ai-providers.js +66 -0
- package/dist/shared/config/agent.js +0 -11
- package/dist/shared/config/agent.test.js +4 -35
- package/package.json +2 -2
- package/dist/agent/app.js +0 -122
- package/dist/agent/commands/registry.js +0 -12
- package/dist/agent/components/AsciiTicker.js +0 -81
- package/dist/agent/components/CommandInput.js +0 -65
- package/dist/agent/components/HoneycombBoot.js +0 -291
- package/dist/agent/components/Spinner.js +0 -37
- package/dist/agent/hooks/useAgent.js +0 -480
- package/dist/agent/objects.js +0 -1
- package/dist/agent/process-lifecycle.js +0 -18
- package/dist/agent/run-headless.js +0 -189
- package/dist/agent/theme.js +0 -41
- package/dist/avatar.js +0 -34
- package/dist/backtest/default-backtest-data.js +0 -200
- package/dist/backtest/fetch.js +0 -41
- package/dist/backtest/import.js +0 -106
- package/dist/backtest/index.js +0 -10
- package/dist/backtest/results.js +0 -113
- package/dist/backtest/runner.js +0 -134
- package/dist/backtest/storage.js +0 -11
- package/dist/backtest/types.js +0 -1
- package/dist/commands/install.js +0 -50
- package/dist/commands/start/ui/PollText.js +0 -23
- package/dist/commands/start/ui/PredictionsPanel.js +0 -88
- package/dist/commands/start/ui/SpinnerContext.js +0 -20
- package/dist/components/InputGuard.js +0 -6
- package/dist/components/stdout-spinner.js +0 -48
- package/dist/create/CreateApp.js +0 -153
- package/dist/create/ai-generate.js +0 -147
- package/dist/create/generate.js +0 -73
- package/dist/create/steps/ApiKeyStep.js +0 -97
- package/dist/create/steps/AvatarStep.js +0 -16
- package/dist/create/steps/BioStep.js +0 -14
- package/dist/create/steps/DoneStep.js +0 -14
- package/dist/create/steps/IdentityStep.js +0 -163
- package/dist/create/steps/NameStep.js +0 -71
- package/dist/create/steps/ScaffoldStep.js +0 -58
- package/dist/create/steps/SoulStep.js +0 -58
- package/dist/create/steps/StrategyStep.js +0 -58
- package/dist/create/validate-api-key.js +0 -47
- package/dist/create/welcome.js +0 -304
- package/dist/list/ListApp.js +0 -79
- package/dist/migrate-templates/MigrateApp.js +0 -131
- package/dist/migrate-templates/migrate.js +0 -86
- package/dist/presets.js +0 -613
- package/dist/start/AgentProcessManager.js +0 -98
- package/dist/start/Dashboard.js +0 -92
- package/dist/start/SelectAgentApp.js +0 -81
- package/dist/start/StartApp.js +0 -189
- package/dist/start/patch-headless.js +0 -101
- package/dist/start/patch-managed-mode.js +0 -142
- package/dist/start/start-command.js +0 -24
- package/dist/theme.js +0 -54
- /package/dist/{agent → services/agent}/config.js +0 -0
- /package/dist/{agent → services/agent}/helpers.js +0 -0
- /package/dist/{agent → services/agent/prompts}/chat-prompt.js +0 -0
- /package/dist/{agent → services/agent}/skills/index.js +0 -0
- /package/dist/{agent → services/agent}/skills/skill-parser.js +0 -0
- /package/dist/{agent → services/agent}/skills/types.js +0 -0
- /package/dist/{agent → services/agent/tools}/edit-section.js +0 -0
- /package/dist/{agent → services/agent/tools}/fetch-rules.js +0 -0
- /package/dist/{agent → services/agent}/tools/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/market/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/market/tools.js +0 -0
- /package/dist/{agent → services/agent}/tools/mindshare/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/mindshare/tools.js +0 -0
- /package/dist/{agent → services/agent}/tools/read-skill-tool.js +0 -0
- /package/dist/{agent → services/agent}/tools/ta/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/ta/indicators.js +0 -0
- /package/dist/{agent → services/agent}/types.js +0 -0
- /package/dist/{ai-providers.js → services/ai-providers.js} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { HiveClient, durationMsToTimeframe } from '@zhive/sdk';
|
|
2
|
+
import { HiveClient, durationMsToTimeframe, loadConfig } from '@zhive/sdk';
|
|
3
3
|
import { styled, symbols, border } from '../../shared/theme.js';
|
|
4
4
|
import { HIVE_API_URL } from '../../../shared/config/constant.js';
|
|
5
|
-
import { findAgentByName,
|
|
5
|
+
import { findAgentByName, scanAgents } from '../../../shared/config/agent.js';
|
|
6
6
|
import z from 'zod';
|
|
7
7
|
import { printZodError } from '../../shared/ validation.js';
|
|
8
8
|
const VALID_TIMEFRAMES = ['1h', '4h', '24h'];
|
|
@@ -50,12 +50,12 @@ export function createMegathreadListCommand() {
|
|
|
50
50
|
}
|
|
51
51
|
process.exit(1);
|
|
52
52
|
}
|
|
53
|
-
const
|
|
54
|
-
if (!
|
|
53
|
+
const config = await loadConfig(agentConfig.dir);
|
|
54
|
+
if (!config?.apiKey) {
|
|
55
55
|
console.error(styled.red(`${symbols.cross} No credentials found for agent "${agentName}". The agent may need to be registered first.`));
|
|
56
56
|
process.exit(1);
|
|
57
57
|
}
|
|
58
|
-
const client = new HiveClient(HIVE_API_URL,
|
|
58
|
+
const client = new HiveClient(HIVE_API_URL, config.apiKey);
|
|
59
59
|
try {
|
|
60
60
|
const rounds = await client.getUnpredictedRounds(timeframes);
|
|
61
61
|
console.log('');
|
|
@@ -17,7 +17,7 @@ vi.mock('@zhive/sdk', async () => {
|
|
|
17
17
|
HiveClient: vi.fn().mockImplementation(() => ({
|
|
18
18
|
getUnpredictedRounds: vi.fn(),
|
|
19
19
|
})),
|
|
20
|
-
|
|
20
|
+
loadConfig: vi.fn(),
|
|
21
21
|
TIMEFRAME_DURATION_MS: {
|
|
22
22
|
H1: 3600000,
|
|
23
23
|
H4: 14400000,
|
|
@@ -30,10 +30,10 @@ vi.mock('@zhive/sdk', async () => {
|
|
|
30
30
|
},
|
|
31
31
|
};
|
|
32
32
|
});
|
|
33
|
-
import { HiveClient,
|
|
33
|
+
import { HiveClient, loadConfig } from '@zhive/sdk';
|
|
34
34
|
import { createMegathreadListCommand } from './list.js';
|
|
35
35
|
const MockHiveClient = HiveClient;
|
|
36
|
-
const
|
|
36
|
+
const mockLoadConfig = loadConfig;
|
|
37
37
|
function createMockActiveRound(overrides = {}) {
|
|
38
38
|
return {
|
|
39
39
|
roundId: 'round-123',
|
|
@@ -88,21 +88,21 @@ describe('createMegathreadListCommand', () => {
|
|
|
88
88
|
expect(consoleErrorOutput.length).toBeGreaterThan(0);
|
|
89
89
|
});
|
|
90
90
|
it('accepts valid timeframe values', async () => {
|
|
91
|
-
|
|
91
|
+
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
92
92
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
93
93
|
const command = createMegathreadListCommand();
|
|
94
94
|
await command.parseAsync(['--agent', 'test-agent', '--timeframe', '1h,4h'], { from: 'user' });
|
|
95
95
|
expect(mockGetUnpredictedRounds).toHaveBeenCalledWith(['1h', '4h']);
|
|
96
96
|
});
|
|
97
97
|
it('accepts single valid timeframe', async () => {
|
|
98
|
-
|
|
98
|
+
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
99
99
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
100
100
|
const command = createMegathreadListCommand();
|
|
101
101
|
await command.parseAsync(['--agent', 'test-agent', '--timeframe', '24h'], { from: 'user' });
|
|
102
102
|
expect(mockGetUnpredictedRounds).toHaveBeenCalledWith(['24h']);
|
|
103
103
|
});
|
|
104
104
|
it('passes undefined when no timeframe filter specified', async () => {
|
|
105
|
-
|
|
105
|
+
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
106
106
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
107
107
|
const command = createMegathreadListCommand();
|
|
108
108
|
await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
|
|
@@ -122,13 +122,13 @@ describe('createMegathreadListCommand', () => {
|
|
|
122
122
|
});
|
|
123
123
|
describe('credentials validation', () => {
|
|
124
124
|
it('shows error when credentials are missing', async () => {
|
|
125
|
-
|
|
125
|
+
mockLoadConfig.mockResolvedValue(null);
|
|
126
126
|
const command = createMegathreadListCommand();
|
|
127
127
|
await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
128
128
|
expect(consoleErrorOutput.join('\n')).toContain('No credentials found for agent "test-agent"');
|
|
129
129
|
});
|
|
130
130
|
it('shows error when credentials have no API key', async () => {
|
|
131
|
-
|
|
131
|
+
mockLoadConfig.mockResolvedValue({ apiKey: null });
|
|
132
132
|
const command = createMegathreadListCommand();
|
|
133
133
|
await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
134
134
|
expect(consoleErrorOutput.join('\n')).toContain('No credentials found');
|
|
@@ -136,7 +136,7 @@ describe('createMegathreadListCommand', () => {
|
|
|
136
136
|
});
|
|
137
137
|
describe('rounds display', () => {
|
|
138
138
|
it('shows message when no unpredicted rounds available', async () => {
|
|
139
|
-
|
|
139
|
+
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
140
140
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
141
141
|
const command = createMegathreadListCommand();
|
|
142
142
|
await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
|
|
@@ -149,7 +149,7 @@ describe('createMegathreadListCommand', () => {
|
|
|
149
149
|
createMockActiveRound({ roundId: 'round-1', projectId: 'bitcoin', durationMs: 3600000 }),
|
|
150
150
|
createMockActiveRound({ roundId: 'round-2', projectId: 'ethereum', durationMs: 14400000 }),
|
|
151
151
|
];
|
|
152
|
-
|
|
152
|
+
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
153
153
|
mockGetUnpredictedRounds.mockResolvedValue(mockRounds);
|
|
154
154
|
const command = createMegathreadListCommand();
|
|
155
155
|
await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
|
|
@@ -168,7 +168,7 @@ describe('createMegathreadListCommand', () => {
|
|
|
168
168
|
const mockRounds = [
|
|
169
169
|
createMockActiveRound({ roundId: 'round-1', projectId: 'bitcoin', durationMs: 7200000 }),
|
|
170
170
|
];
|
|
171
|
-
|
|
171
|
+
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
172
172
|
mockGetUnpredictedRounds.mockResolvedValue(mockRounds);
|
|
173
173
|
const command = createMegathreadListCommand();
|
|
174
174
|
await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
|
|
@@ -178,7 +178,7 @@ describe('createMegathreadListCommand', () => {
|
|
|
178
178
|
});
|
|
179
179
|
describe('API error handling', () => {
|
|
180
180
|
it('shows error when API call fails', async () => {
|
|
181
|
-
|
|
181
|
+
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
182
182
|
mockGetUnpredictedRounds.mockRejectedValue(new Error('Network error'));
|
|
183
183
|
const command = createMegathreadListCommand();
|
|
184
184
|
await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
@@ -186,7 +186,7 @@ describe('createMegathreadListCommand', () => {
|
|
|
186
186
|
expect(consoleErrorOutput.join('\n')).toContain('Network error');
|
|
187
187
|
});
|
|
188
188
|
it('handles non-Error exceptions', async () => {
|
|
189
|
-
|
|
189
|
+
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
190
190
|
mockGetUnpredictedRounds.mockRejectedValue('String error');
|
|
191
191
|
const command = createMegathreadListCommand();
|
|
192
192
|
await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
@@ -196,7 +196,7 @@ describe('createMegathreadListCommand', () => {
|
|
|
196
196
|
});
|
|
197
197
|
describe('works with different fixture agents', () => {
|
|
198
198
|
it('works with empty-agent', async () => {
|
|
199
|
-
|
|
199
|
+
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
200
200
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
201
201
|
const command = createMegathreadListCommand();
|
|
202
202
|
await command.parseAsync(['--agent', 'empty-agent'], { from: 'user' });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { loadConfig } from '@zhive/sdk';
|
|
2
2
|
import { HIVE_API_URL } from '../../../shared/config/constant.js';
|
|
3
3
|
import { extractErrorMessage } from '../../../shared/agent/utils.js';
|
|
4
4
|
import { styled } from '../../shared/theme.js';
|
|
@@ -50,13 +50,12 @@ function getOutcomeStr(pred) {
|
|
|
50
50
|
return `[WIN] +${pred.honey.toFixed(2)} Honey`;
|
|
51
51
|
}
|
|
52
52
|
if (pred.wax > 0) {
|
|
53
|
-
return `[
|
|
53
|
+
return `[LOSS] +${pred.wax.toFixed(2)} Wax`;
|
|
54
54
|
}
|
|
55
55
|
return '[LOSS]';
|
|
56
56
|
}
|
|
57
57
|
export async function predictionSlashCommand(agentName, callbacks) {
|
|
58
|
-
const
|
|
59
|
-
const credentials = await loadCredentials(filePath);
|
|
58
|
+
const credentials = await loadConfig();
|
|
60
59
|
if (!credentials?.apiKey) {
|
|
61
60
|
callbacks.onError?.('Agent not registered yet. Wait for agent to start.');
|
|
62
61
|
return;
|
|
@@ -7,7 +7,6 @@ import { fetchRulesTool } from '../../../shared/agent/tools/fetch-rules.js';
|
|
|
7
7
|
import { extractErrorMessage } from '../../../shared/agent/utils.js';
|
|
8
8
|
import { loadAgentConfig } from '../../../shared/config/agent.js';
|
|
9
9
|
import { getModel } from '../../../shared/config/ai-providers.js';
|
|
10
|
-
import { backtestSlashCommand } from '../commands/backtest.js';
|
|
11
10
|
import { predictionSlashCommand } from '../commands/prediction.js';
|
|
12
11
|
import { skillsSlashCommand } from '../commands/skills.js';
|
|
13
12
|
import { SLASH_COMMANDS } from '../services/command-registry.js';
|
|
@@ -73,46 +72,46 @@ export function useChat(agentName) {
|
|
|
73
72
|
const memoryOutput = memoryRef.current || 'No memory stored yet.';
|
|
74
73
|
addChatActivity({ type: 'chat-agent', text: memoryOutput });
|
|
75
74
|
},
|
|
76
|
-
'/backtest': async () => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
},
|
|
75
|
+
// '/backtest': async () => {
|
|
76
|
+
// const config: RunnerConfig = {
|
|
77
|
+
// agentPath: process.cwd(),
|
|
78
|
+
// soulContent: soulContentRef.current,
|
|
79
|
+
// strategyContent: strategyContentRef.current,
|
|
80
|
+
// agentName: agentName,
|
|
81
|
+
// };
|
|
82
|
+
// await backtestSlashCommand(parts.slice(1), config, {
|
|
83
|
+
// onFetchStart: (numThreads) => {
|
|
84
|
+
// addChatActivity({
|
|
85
|
+
// type: 'chat-agent',
|
|
86
|
+
// text: `Fetching ${numThreads} resolved threads...`,
|
|
87
|
+
// });
|
|
88
|
+
// },
|
|
89
|
+
// onFetchError: (error: string) => {
|
|
90
|
+
// addChatActivity({
|
|
91
|
+
// type: 'chat-agent',
|
|
92
|
+
// text: `API fetch failed (${error}), falling back to default dataset...`,
|
|
93
|
+
// });
|
|
94
|
+
// addChatActivity({
|
|
95
|
+
// type: 'chat-agent',
|
|
96
|
+
// text: 'Starting backtest against default dataset...',
|
|
97
|
+
// });
|
|
98
|
+
// },
|
|
99
|
+
// onThreadStart: (index, total, thread) => {
|
|
100
|
+
// addChatActivity({
|
|
101
|
+
// type: 'chat-agent',
|
|
102
|
+
// text: `Processing ${index + 1}/${total}: ${thread.project_name}...`,
|
|
103
|
+
// });
|
|
104
|
+
// },
|
|
105
|
+
// onBacktestSuccess: (report: string) => {
|
|
106
|
+
// addChatActivity({ type: 'chat-agent', text: report });
|
|
107
|
+
// sessionMessagesRef.current.push({ role: 'assistant', content: report });
|
|
108
|
+
// },
|
|
109
|
+
// onBacktestError: (err: unknown) => {
|
|
110
|
+
// const errMessage = extractErrorMessage(err);
|
|
111
|
+
// addChatActivity({ type: 'chat-error', text: `Backtest failed: ${errMessage}` });
|
|
112
|
+
// },
|
|
113
|
+
// });
|
|
114
|
+
// },
|
|
116
115
|
'/prediction': async () => {
|
|
117
116
|
await predictionSlashCommand(agentName, {
|
|
118
117
|
onFetchStart: () => {
|
|
@@ -3,7 +3,7 @@ export const SLASH_COMMANDS = [
|
|
|
3
3
|
{ name: '/help', description: 'Show available commands' },
|
|
4
4
|
{ name: '/clear', description: 'Clear chat history' },
|
|
5
5
|
{ name: '/memory', description: 'Show current memory state' },
|
|
6
|
-
{ name: '/backtest', description: 'Run agent against test set (/backtest <num> fetches from API)' },
|
|
6
|
+
// { name: '/backtest', description: 'Run agent against test set (/backtest <num> fetches from API)' },
|
|
7
7
|
{ name: '/prediction', description: 'Show your last 10 resolved predictions' },
|
|
8
8
|
];
|
|
9
9
|
export function filterCommands(prefix) {
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { createStartCommand } from './commands/start/commands/index.js';
|
|
|
9
9
|
import { createStartAllCommand } from './commands/start-all/commands/index.js';
|
|
10
10
|
import { createRunCommand } from './commands/run/commands/index.js';
|
|
11
11
|
import { createMigrateTemplatesCommand } from './commands/migrate-templates/commands/index.js';
|
|
12
|
+
import { createDoctorCommand } from './commands/doctor/commands/index.js';
|
|
12
13
|
const require = createRequire(import.meta.url);
|
|
13
14
|
const packageJson = require('../package.json');
|
|
14
15
|
const program = new Command();
|
|
@@ -21,6 +22,7 @@ program.addCommand(createStartCommand());
|
|
|
21
22
|
program.addCommand(createStartAllCommand());
|
|
22
23
|
program.addCommand(createRunCommand());
|
|
23
24
|
program.addCommand(createMigrateTemplatesCommand());
|
|
25
|
+
program.addCommand(createDoctorCommand());
|
|
24
26
|
// Show help with exit code 0 when no arguments provided
|
|
25
27
|
const args = process.argv.slice(2);
|
|
26
28
|
if (args.length === 0) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { generateText, Output, ToolLoopAgent } from 'ai';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { buildAnalystPrompt, buildMegathreadPrompt } from './prompt.js';
|
|
4
|
-
import { loadMemory, saveMemory, getMemoryLineCount, MEMORY_SOFT_LIMIT } from '@hive-org/sdk';
|
|
5
|
-
import { buildMemoryExtractionPrompt } from './memory-prompt.js';
|
|
3
|
+
import { buildAnalystPrompt, buildMegathreadPrompt } from './prompts/prompt.js';
|
|
4
|
+
import { loadMemory, saveMemory, getMemoryLineCount, MEMORY_SOFT_LIMIT, } from '@hive-org/sdk';
|
|
5
|
+
import { buildMemoryExtractionPrompt } from './prompts/memory-prompt.js';
|
|
6
6
|
import { stripCodeFences } from './helpers.js';
|
|
7
|
-
import { getModel } from './model.js';
|
|
7
|
+
import { getModel } from './helpers/model.js';
|
|
8
8
|
// ─── Cache Helpers ─────────────────────────────────
|
|
9
9
|
function cacheableSystem(content) {
|
|
10
10
|
const message = {
|
|
@@ -146,7 +146,7 @@ export async function extractAndSaveMemory(sessionMessages) {
|
|
|
146
146
|
const model = await getModel();
|
|
147
147
|
const { text } = await generateText({
|
|
148
148
|
model,
|
|
149
|
-
|
|
149
|
+
prompt,
|
|
150
150
|
});
|
|
151
151
|
const cleaned = stripCodeFences(text);
|
|
152
152
|
await saveMemory(cleaned);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AI_PROVIDERS } from '
|
|
2
|
-
import { getAgentProviderKeys } from '../
|
|
1
|
+
import { AI_PROVIDERS } from '../../ai-providers.js';
|
|
2
|
+
import { getAgentProviderKeys } from '../env.js';
|
|
3
3
|
const PROVIDERS = [
|
|
4
4
|
{
|
|
5
5
|
label: 'Anthropic',
|
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
export function buildMemoryExtractionPrompt(context) {
|
|
2
2
|
const { currentMemory, sessionMessages, lineCount } = context;
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
let sessionSection = '';
|
|
4
|
+
if (sessionMessages.length > 0) {
|
|
5
|
+
const listed = sessionMessages
|
|
6
|
+
.map((m) => `${m.role === 'user' ? 'Operator' : 'Agent'}: ${m.content}`)
|
|
7
|
+
.join('\n');
|
|
8
|
+
sessionSection = `\n## Session Chat Log\n\n${listed}\n`;
|
|
9
|
+
}
|
|
10
|
+
const currentMemorySection = currentMemory.trim().length > 0
|
|
11
|
+
? `\n## Current MEMORY.md\n\n\`\`\`markdown\n${currentMemory}\n\`\`\`\n`
|
|
12
|
+
: '\n## Current MEMORY.md\n\n(empty - this is a fresh agent)\n';
|
|
13
|
+
const consolidationNote = lineCount > 200
|
|
14
|
+
? `\n**IMPORTANT: The current memory is ${lineCount} lines, exceeding the 200-line soft limit. Aggressively consolidate: merge related items, remove outdated or low-value entries, and keep only the most important context.**\n`
|
|
15
|
+
: '';
|
|
16
|
+
const prompt = `You are an AI trading agent's memory system. Your job is to maintain conversational continuity between sessions with the agent's operator.
|
|
17
|
+
${currentMemorySection}${consolidationNote}
|
|
18
|
+
## Session Activity
|
|
19
|
+
${sessionSection}
|
|
20
|
+
## Instructions
|
|
5
21
|
|
|
6
|
-
Review the session
|
|
22
|
+
Review the session chat log above and update the agent's MEMORY.md file. This memory is about **conversational continuity** — making the agent feel like it remembers past sessions with its operator.
|
|
7
23
|
|
|
8
24
|
Focus on extracting:
|
|
9
25
|
1. **Topics discussed** — what subjects came up in conversation (e.g., "we talked about ETH gas fees", "operator asked about macro outlook")
|
|
@@ -25,23 +41,5 @@ Follow these rules:
|
|
|
25
41
|
6. **Keep it under ~200 lines** — This file is injected into every prompt, so brevity matters.
|
|
26
42
|
|
|
27
43
|
Output the complete updated MEMORY.md content. Start with \`# Memory\` as the top-level header. Output ONLY the markdown content, no code fences or explanation.`;
|
|
28
|
-
|
|
29
|
-
let sessionSection = '';
|
|
30
|
-
if (sessionMessages.length > 0) {
|
|
31
|
-
const listed = sessionMessages
|
|
32
|
-
.map((m) => `${m.role === 'user' ? 'Operator' : 'Agent'}: ${m.content}`)
|
|
33
|
-
.join('\n');
|
|
34
|
-
sessionSection = `\n## Session Chat Log\n\n${listed}\n`;
|
|
35
|
-
}
|
|
36
|
-
const currentMemorySection = currentMemory.trim().length > 0
|
|
37
|
-
? `\n## Current MEMORY.md\n\n\`\`\`markdown\n${currentMemory}\n\`\`\`\n`
|
|
38
|
-
: '\n## Current MEMORY.md\n\n(empty - this is a fresh agent)\n';
|
|
39
|
-
const consolidationNote = lineCount > 200
|
|
40
|
-
? `\n**IMPORTANT: The current memory is ${lineCount} lines, exceeding the 200-line soft limit. Aggressively consolidate: merge related items, remove outdated or low-value entries, and keep only the most important context.**\n`
|
|
41
|
-
: '';
|
|
42
|
-
const prompt = `${currentMemorySection}${consolidationNote}
|
|
43
|
-
## Session Activity
|
|
44
|
-
${sessionSection}
|
|
45
|
-
Update the MEMORY.md based on the session activity above.`;
|
|
46
|
-
return { system, prompt };
|
|
44
|
+
return prompt;
|
|
47
45
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// ─── Shared Types ─────────────────────────────────
|
|
1
2
|
function humanDuration(ms) {
|
|
2
3
|
const hours = ms / 3_600_000;
|
|
3
4
|
if (hours >= 24) {
|
|
@@ -79,32 +80,8 @@ Only skip if this project is outside the expertise list defined in your STRATEGY
|
|
|
79
80
|
- Major protocol upgrade, big institutional entry, trend reversal → ±6.0 to ±12.0
|
|
80
81
|
- Black swan, regulatory bombshell, massive exploit → ±12.0 to ±25.0
|
|
81
82
|
|
|
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) ──
|
|
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
|
+
// ── Prompt (dynamic per signal — changes every call) ──
|
|
108
85
|
let recentPostsSection = '';
|
|
109
86
|
if (recentPosts && recentPosts.length > 0) {
|
|
110
87
|
const listed = recentPosts.map((p) => `- "${p}"`).join('\n');
|
|
@@ -130,6 +107,7 @@ ${memory}
|
|
|
130
107
|
const roundStartMs = Math.floor(now / durationMs) * durationMs;
|
|
131
108
|
const timeRemainingMs = Math.max(0, roundStartMs + durationMs - now);
|
|
132
109
|
const timeRemaining = humanDuration(timeRemainingMs);
|
|
110
|
+
const lateInRound = timeRemainingMs < durationMs * 0.25;
|
|
133
111
|
const nowIso = new Date().toISOString();
|
|
134
112
|
// ── Price context lines ──
|
|
135
113
|
const hasBothPrices = priceAtStart !== undefined && currentPrice !== undefined;
|
|
@@ -141,11 +119,33 @@ ${memory}
|
|
|
141
119
|
}
|
|
142
120
|
let priceContextLines = '';
|
|
143
121
|
if (hasBothPrices) {
|
|
144
|
-
priceContextLines = `- Round-start price: $${priceAtStart} (scoring
|
|
122
|
+
priceContextLines = `- Round-start price: $${priceAtStart} (baseline for scoring)
|
|
145
123
|
- Current price: $${currentPrice} (${currentChangeStr} from round start)`;
|
|
146
124
|
}
|
|
147
125
|
else if (priceAtStart !== undefined) {
|
|
148
|
-
priceContextLines = `- Round-start price: $${priceAtStart} (scoring
|
|
126
|
+
priceContextLines = `- Round-start price: $${priceAtStart} (baseline for scoring)`;
|
|
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.`;
|
|
149
149
|
}
|
|
150
150
|
// ── Scoring & conviction lines ──
|
|
151
151
|
let scoringLine;
|
|
@@ -173,11 +173,36 @@ ${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
|
+
|
|
176
182
|
## Your task
|
|
177
183
|
|
|
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)
|
|
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
|
|
179
198
|
|
|
180
199
|
${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
|
|
181
206
|
${recentPostsSection}${memorySection}
|
|
182
207
|
Give your take in character and a conviction number.
|
|
183
208
|
${convictionLine}`;
|
|
@@ -276,28 +301,7 @@ Set skip to true and set summary and conviction to null. Real people don't comme
|
|
|
276
301
|
- Major protocol upgrade, big institutional entry, trend reversal → ±6.0 to ±12.0
|
|
277
302
|
- Black swan, regulatory bombshell, massive exploit → ±12.0 to ±25.0
|
|
278
303
|
|
|
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.`;
|
|
304
|
+
IMPORTANT: Vary your conviction numbers. Do NOT reuse the same number across signals. Each signal has different strength — your conviction should reflect that.`;
|
|
301
305
|
// ── Prompt (dynamic per signal — changes every call) ──
|
|
302
306
|
let citationsSection = '';
|
|
303
307
|
if (citations.length > 0) {
|
|
@@ -334,20 +338,42 @@ ${memory}
|
|
|
334
338
|
const signalTimeMs = new Date(timestamp).getTime();
|
|
335
339
|
const timeRemainingMs = Math.max(0, signalTimeMs + EVAL_WINDOW_MS - Date.now());
|
|
336
340
|
const timeRemaining = humanDuration(timeRemainingMs);
|
|
341
|
+
const lateWindow = timeRemainingMs < 45 * 60 * 1000;
|
|
337
342
|
const userPrompt = `## Context
|
|
338
343
|
|
|
339
344
|
- Project: c/${projectId}
|
|
340
345
|
- Current date: ${currentDate}
|
|
341
346
|
- Signal time: ${timestamp}
|
|
342
|
-
- Snapshot price: $${priceOnFetch} (
|
|
347
|
+
- Snapshot price: $${priceOnFetch} (captured at signal time)
|
|
343
348
|
- Time left: ~${timeRemaining}
|
|
344
|
-
${citationsSection}
|
|
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}
|
|
345
369
|
Signal/event to react to:
|
|
346
370
|
"""
|
|
347
371
|
${threadText}
|
|
348
372
|
"""
|
|
349
373
|
|
|
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
|
+
|
|
350
376
|
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.`;
|
|
377
|
+
Conviction: predicted % price change from $${priceOnFetch} by evaluation time (~${timeRemaining} left), up to one decimal. Positive = up, negative = down. 0 = neutral.`;
|
|
352
378
|
return { system, prompt: userPrompt };
|
|
353
379
|
}
|
|
@@ -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 './constant.js';
|
|
7
7
|
function extractField(content, pattern) {
|
|
8
8
|
const match = content.match(pattern);
|
|
9
9
|
if (match === null) {
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import
|
|
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
|
-
}
|
|
3
|
+
import { getHiveDir } from './constant.js';
|
|
10
4
|
export async function readConfig() {
|
|
11
5
|
try {
|
|
12
6
|
const configPath = path.join(getHiveDir(), 'config.json');
|