@zhive/cli 0.6.6 → 0.6.8
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/commands/agent/commands/profile.js +3 -15
- package/dist/commands/agent/commands/profile.test.js +2 -23
- package/dist/commands/create/presets/index.js +1 -1
- package/dist/commands/create/presets/options.js +18 -15
- package/dist/commands/create/ui/steps/IdentityStep.js +3 -2
- package/dist/commands/doctor/commands/index.js +5 -11
- package/dist/commands/indicator/commands/bollinger.js +37 -0
- package/dist/commands/indicator/commands/ema.js +37 -0
- package/dist/commands/indicator/commands/index.js +14 -0
- package/dist/commands/indicator/commands/macd.js +51 -0
- package/dist/commands/indicator/commands/rsi.js +37 -0
- package/dist/commands/indicator/commands/sma.js +37 -0
- package/dist/commands/market/commands/index.js +5 -0
- package/dist/commands/market/commands/price.js +25 -0
- package/dist/commands/megathread/commands/create-comment.js +2 -7
- package/dist/commands/megathread/commands/create-comment.test.js +3 -30
- package/dist/commands/megathread/commands/create-comments.js +2 -7
- package/dist/commands/megathread/commands/list.js +5 -10
- package/dist/commands/megathread/commands/list.test.js +3 -21
- package/dist/commands/migrate-templates/ui/MigrateApp.js +1 -1
- package/dist/commands/shared/utils.js +12 -0
- package/dist/commands/start/commands/prediction.js +1 -1
- package/dist/commands/start/commands/skills.test.js +1 -2
- package/dist/components/MultiSelectPrompt.js +3 -3
- package/dist/index.js +4 -0
- package/dist/shared/agent/analysis.js +2 -12
- package/dist/shared/agent/cache.js +10 -0
- package/dist/shared/agent/handler.js +3 -9
- package/dist/shared/agent/prompts/megathread.js +0 -8
- package/dist/shared/agent/tools/execute-skill-tool.js +2 -1
- package/dist/shared/agent/tools/formatting.js +0 -19
- package/dist/shared/agent/tools/market/client.js +3 -3
- package/dist/shared/agent/tools/market/tools.js +88 -312
- package/dist/shared/agent/tools/market/utils.js +71 -0
- package/dist/shared/agent/tools/mindshare/tools.js +1 -1
- package/dist/shared/agent/tools/ta/index.js +3 -1
- package/dist/shared/agent/utils.js +44 -0
- package/dist/shared/config/agent.js +4 -0
- package/dist/shared/config/agent.test.js +0 -5
- package/dist/shared/ta/error.js +12 -0
- package/dist/shared/ta/service.js +93 -0
- package/dist/shared/ta/utils.js +16 -0
- package/package.json +2 -1
|
@@ -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, scanAgents } from '../../../shared/config/agent.js';
|
|
@@ -52,12 +52,7 @@ export function createMegathreadCreateCommentsCommand() {
|
|
|
52
52
|
}
|
|
53
53
|
process.exit(1);
|
|
54
54
|
}
|
|
55
|
-
const
|
|
56
|
-
if (!credentials?.apiKey) {
|
|
57
|
-
console.error(styled.red(`${symbols.cross} No credentials found for agent "${agentName}". The agent may need to be registered first.`));
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
const client = new HiveClient(HIVE_API_URL, credentials.apiKey);
|
|
55
|
+
const client = new HiveClient(HIVE_API_URL, agentConfig.apiKey);
|
|
61
56
|
const payload = {
|
|
62
57
|
comments: [],
|
|
63
58
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { durationMsToTimeframe, HiveClient } from '@zhive/sdk';
|
|
1
2
|
import { Command } from 'commander';
|
|
2
|
-
import { HiveClient, durationMsToTimeframe, loadConfig } from '@zhive/sdk';
|
|
3
|
-
import { styled, symbols, border } from '../../shared/theme.js';
|
|
4
|
-
import { HIVE_API_URL } from '../../../shared/config/constant.js';
|
|
5
|
-
import { findAgentByName, scanAgents } from '../../../shared/config/agent.js';
|
|
6
3
|
import z from 'zod';
|
|
4
|
+
import { findAgentByName, scanAgents } from '../../../shared/config/agent.js';
|
|
5
|
+
import { HIVE_API_URL } from '../../../shared/config/constant.js';
|
|
7
6
|
import { printZodError } from '../../shared/ validation.js';
|
|
7
|
+
import { border, styled, symbols } from '../../shared/theme.js';
|
|
8
8
|
const VALID_TIMEFRAMES = ['1h', '4h', '24h'];
|
|
9
9
|
const ListMegathreadOptionsSchema = z.object({
|
|
10
10
|
agent: z.string(),
|
|
@@ -50,12 +50,7 @@ export function createMegathreadListCommand() {
|
|
|
50
50
|
}
|
|
51
51
|
process.exit(1);
|
|
52
52
|
}
|
|
53
|
-
const
|
|
54
|
-
if (!config?.apiKey) {
|
|
55
|
-
console.error(styled.red(`${symbols.cross} No credentials found for agent "${agentName}". The agent may need to be registered first.`));
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
const client = new HiveClient(HIVE_API_URL, config.apiKey);
|
|
53
|
+
const client = new HiveClient(HIVE_API_URL, agentConfig.apiKey);
|
|
59
54
|
try {
|
|
60
55
|
const rounds = await client.getUnpredictedRounds(timeframes);
|
|
61
56
|
console.log('');
|
|
@@ -17,7 +17,6 @@ vi.mock('@zhive/sdk', async () => {
|
|
|
17
17
|
HiveClient: vi.fn().mockImplementation(() => ({
|
|
18
18
|
getUnpredictedRounds: vi.fn(),
|
|
19
19
|
})),
|
|
20
|
-
loadConfig: vi.fn(),
|
|
21
20
|
TIMEFRAME_DURATION_MS: {
|
|
22
21
|
H1: 3600000,
|
|
23
22
|
H4: 14400000,
|
|
@@ -30,10 +29,9 @@ vi.mock('@zhive/sdk', async () => {
|
|
|
30
29
|
},
|
|
31
30
|
};
|
|
32
31
|
});
|
|
33
|
-
import { HiveClient
|
|
32
|
+
import { HiveClient } from '@zhive/sdk';
|
|
34
33
|
import { createMegathreadListCommand } from './list.js';
|
|
35
34
|
const MockHiveClient = HiveClient;
|
|
36
|
-
const mockLoadConfig = loadConfig;
|
|
37
35
|
function createMockActiveRound(overrides = {}) {
|
|
38
36
|
return {
|
|
39
37
|
roundId: 'round-123',
|
|
@@ -88,21 +86,18 @@ describe('createMegathreadListCommand', () => {
|
|
|
88
86
|
expect(consoleErrorOutput.length).toBeGreaterThan(0);
|
|
89
87
|
});
|
|
90
88
|
it('accepts valid timeframe values', async () => {
|
|
91
|
-
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
92
89
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
93
90
|
const command = createMegathreadListCommand();
|
|
94
91
|
await command.parseAsync(['--agent', 'test-agent', '--timeframe', '1h,4h'], { from: 'user' });
|
|
95
92
|
expect(mockGetUnpredictedRounds).toHaveBeenCalledWith(['1h', '4h']);
|
|
96
93
|
});
|
|
97
94
|
it('accepts single valid timeframe', async () => {
|
|
98
|
-
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
99
95
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
100
96
|
const command = createMegathreadListCommand();
|
|
101
97
|
await command.parseAsync(['--agent', 'test-agent', '--timeframe', '24h'], { from: 'user' });
|
|
102
98
|
expect(mockGetUnpredictedRounds).toHaveBeenCalledWith(['24h']);
|
|
103
99
|
});
|
|
104
100
|
it('passes undefined when no timeframe filter specified', async () => {
|
|
105
|
-
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
106
101
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
107
102
|
const command = createMegathreadListCommand();
|
|
108
103
|
await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
|
|
@@ -122,21 +117,13 @@ describe('createMegathreadListCommand', () => {
|
|
|
122
117
|
});
|
|
123
118
|
describe('credentials validation', () => {
|
|
124
119
|
it('shows error when credentials are missing', async () => {
|
|
125
|
-
mockLoadConfig.mockResolvedValue(null);
|
|
126
120
|
const command = createMegathreadListCommand();
|
|
127
|
-
await expect(command.parseAsync(['--agent', '
|
|
128
|
-
expect(consoleErrorOutput.join('\n')).toContain('
|
|
129
|
-
});
|
|
130
|
-
it('shows error when credentials have no API key', async () => {
|
|
131
|
-
mockLoadConfig.mockResolvedValue({ apiKey: null });
|
|
132
|
-
const command = createMegathreadListCommand();
|
|
133
|
-
await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
134
|
-
expect(consoleErrorOutput.join('\n')).toContain('No credentials found');
|
|
121
|
+
await expect(command.parseAsync(['--agent', 'no-cred-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
122
|
+
expect(consoleErrorOutput.join('\n')).toContain('Agent "no-cred-agent" not found');
|
|
135
123
|
});
|
|
136
124
|
});
|
|
137
125
|
describe('rounds display', () => {
|
|
138
126
|
it('shows message when no unpredicted rounds available', async () => {
|
|
139
|
-
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
140
127
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
141
128
|
const command = createMegathreadListCommand();
|
|
142
129
|
await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
|
|
@@ -149,7 +136,6 @@ describe('createMegathreadListCommand', () => {
|
|
|
149
136
|
createMockActiveRound({ roundId: 'round-1', projectId: 'bitcoin', durationMs: 3600000 }),
|
|
150
137
|
createMockActiveRound({ roundId: 'round-2', projectId: 'ethereum', durationMs: 14400000 }),
|
|
151
138
|
];
|
|
152
|
-
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
153
139
|
mockGetUnpredictedRounds.mockResolvedValue(mockRounds);
|
|
154
140
|
const command = createMegathreadListCommand();
|
|
155
141
|
await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
|
|
@@ -168,7 +154,6 @@ describe('createMegathreadListCommand', () => {
|
|
|
168
154
|
const mockRounds = [
|
|
169
155
|
createMockActiveRound({ roundId: 'round-1', projectId: 'bitcoin', durationMs: 7200000 }),
|
|
170
156
|
];
|
|
171
|
-
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
172
157
|
mockGetUnpredictedRounds.mockResolvedValue(mockRounds);
|
|
173
158
|
const command = createMegathreadListCommand();
|
|
174
159
|
await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
|
|
@@ -178,7 +163,6 @@ describe('createMegathreadListCommand', () => {
|
|
|
178
163
|
});
|
|
179
164
|
describe('API error handling', () => {
|
|
180
165
|
it('shows error when API call fails', async () => {
|
|
181
|
-
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
182
166
|
mockGetUnpredictedRounds.mockRejectedValue(new Error('Network error'));
|
|
183
167
|
const command = createMegathreadListCommand();
|
|
184
168
|
await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
@@ -186,7 +170,6 @@ describe('createMegathreadListCommand', () => {
|
|
|
186
170
|
expect(consoleErrorOutput.join('\n')).toContain('Network error');
|
|
187
171
|
});
|
|
188
172
|
it('handles non-Error exceptions', async () => {
|
|
189
|
-
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
190
173
|
mockGetUnpredictedRounds.mockRejectedValue('String error');
|
|
191
174
|
const command = createMegathreadListCommand();
|
|
192
175
|
await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
@@ -196,7 +179,6 @@ describe('createMegathreadListCommand', () => {
|
|
|
196
179
|
});
|
|
197
180
|
describe('works with different fixture agents', () => {
|
|
198
181
|
it('works with empty-agent', async () => {
|
|
199
|
-
mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
200
182
|
mockGetUnpredictedRounds.mockResolvedValue([]);
|
|
201
183
|
const command = createMegathreadListCommand();
|
|
202
184
|
await command.parseAsync(['--agent', 'empty-agent'], { from: 'user' });
|
|
@@ -112,7 +112,7 @@ export function MigrateApp() {
|
|
|
112
112
|
if (phase === 'selecting') {
|
|
113
113
|
const oldStyleAgents = agents.filter((a) => a.isOldStyle);
|
|
114
114
|
const newStyleAgents = agents.filter((a) => !a.isOldStyle);
|
|
115
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Migrate agents to @zhive/cli"] }), _jsx(Text, { color: colors.gray, children: border.horizontal.repeat(termWidth - 4) }), _jsxs(Text, { color: colors.gray, children: ["Use ", styled.white('↑↓'), " to navigate, ", styled.white('
|
|
115
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Migrate agents to @zhive/cli"] }), _jsx(Text, { color: colors.gray, children: border.horizontal.repeat(termWidth - 4) }), _jsxs(Text, { color: colors.gray, children: ["Use ", styled.white('↑↓'), " to navigate, ", styled.white('spacebar'), " press to toggle,", ' ', styled.white('enter'), " to confirm"] }), _jsx(Text, { children: " " }), oldStyleAgents.map((agent, i) => {
|
|
116
116
|
const isCursor = i === cursor;
|
|
117
117
|
const prefix = agent.selected ? symbols.check : symbols.diamondOpen;
|
|
118
118
|
const prefixColor = agent.selected ? colors.green : colors.gray;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { scanAgents } from '../../shared/config/agent.js';
|
|
2
|
+
import { styled, symbols } from './theme.js';
|
|
3
|
+
export const printAgentNotFoundHelper = async (agentName) => {
|
|
4
|
+
const agents = await scanAgents();
|
|
5
|
+
if (agents.length === 0) {
|
|
6
|
+
console.error(styled.red(`${symbols.cross} No agents found. Create one with: npx @zhive/cli@latest create`));
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
const availableNames = agents.map((a) => a.name).join(', ');
|
|
10
|
+
console.error(styled.red(`${symbols.cross} Agent "${agentName}" not found. Available agents: ${availableNames}`));
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { loadConfig } from '@zhive/sdk';
|
|
2
1
|
import { HIVE_API_URL } from '../../../shared/config/constant.js';
|
|
3
2
|
import { extractErrorMessage } from '../../../shared/agent/utils.js';
|
|
4
3
|
import { styled } from '../../shared/theme.js';
|
|
4
|
+
import { loadConfig } from '@zhive/sdk';
|
|
5
5
|
export async function fetchMyPredictions(apiKey, limit = 10) {
|
|
6
6
|
try {
|
|
7
7
|
const url = `${HIVE_API_URL}/megathread-comment/me?limit=${limit}&onlyResolved=true`;
|
|
@@ -3,9 +3,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import { fetchSkills, formatSkills, skillsSlashCommand } from './skills.js';
|
|
5
5
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const FIXTURES_DIR = path.join(__dirname, '../../../tests/__fixtures__/mock-hive');
|
|
7
6
|
vi.mock('../../../shared/config/constant.js', () => ({
|
|
8
|
-
getHiveDir: vi.fn(() => path.join(__dirname, '
|
|
7
|
+
getHiveDir: vi.fn(() => path.join(__dirname, '../../../../__fixtures__/mock-hive')),
|
|
9
8
|
}));
|
|
10
9
|
function createMockSkill(overrides = {}) {
|
|
11
10
|
const skill = {
|
|
@@ -2,7 +2,7 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import { colors, symbols } from '../commands/shared/theme.js';
|
|
5
|
-
export function MultiSelectPrompt({ label, items, defaultSelected, onSubmit, }) {
|
|
5
|
+
export function MultiSelectPrompt({ label, items, defaultSelected, hint, onSubmit, }) {
|
|
6
6
|
const allValues = new Set(items.map((i) => i.value));
|
|
7
7
|
const [selected, setSelected] = useState(defaultSelected ?? allValues);
|
|
8
8
|
const [cursor, setCursor] = useState(0);
|
|
@@ -35,11 +35,11 @@ export function MultiSelectPrompt({ label, items, defaultSelected, onSubmit, })
|
|
|
35
35
|
});
|
|
36
36
|
const highlightedItem = items[cursor];
|
|
37
37
|
const highlightedDescription = highlightedItem?.description;
|
|
38
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [symbols.arrow, " "] }), _jsx(Text, { color: colors.white, bold: true, children: label })] }), _jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsx(Text, { color: colors.grayDim, italic: true, children: "All selected by default
|
|
38
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [symbols.arrow, " "] }), _jsx(Text, { color: colors.white, bold: true, children: label })] }), _jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsx(Text, { color: colors.grayDim, italic: true, children: hint ?? "All selected by default — deselect what you don't want" }) }), _jsx(Box, { flexDirection: "column", marginLeft: 2, children: items.map((item, i) => {
|
|
39
39
|
const isCursor = i === cursor;
|
|
40
40
|
const isSelected = selected.has(item.value);
|
|
41
41
|
const checkbox = isSelected ? '◆' : '◇';
|
|
42
42
|
const itemColor = isCursor ? colors.honey : colors.white;
|
|
43
43
|
return (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [isCursor ? symbols.diamond : ' ', " "] }), _jsxs(Text, { color: isSelected ? colors.honey : colors.grayDim, children: [checkbox, " "] }), _jsx(Text, { color: itemColor, children: item.label })] }, item.value));
|
|
44
|
-
}) }), highlightedDescription && (_jsx(Box, { marginLeft: 4, marginTop: 1, children: _jsxs(Text, { color: colors.gray, italic: true, children: [symbols.arrow, " ", highlightedDescription] }) })), _jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsxs(Text, { color: colors.grayDim, children: [_jsx(Text, { color: colors.honey, children: "
|
|
44
|
+
}) }), highlightedDescription && (_jsx(Box, { marginLeft: 4, marginTop: 1, children: _jsxs(Text, { color: colors.gray, italic: true, children: [symbols.arrow, " ", highlightedDescription] }) })), _jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsxs(Text, { color: colors.grayDim, children: [_jsx(Text, { color: colors.honey, children: "spacebar" }), " press to toggle ", _jsx(Text, { color: colors.honey, children: "enter" }), ' ', "confirm"] }) })] }));
|
|
45
45
|
}
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,8 @@ 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
12
|
import { createDoctorCommand } from './commands/doctor/commands/index.js';
|
|
13
|
+
import { createIndicatorCommand } from './commands/indicator/commands/index.js';
|
|
14
|
+
import { createMarketCommand } from './commands/market/commands/index.js';
|
|
13
15
|
const require = createRequire(import.meta.url);
|
|
14
16
|
const packageJson = require('../package.json');
|
|
15
17
|
const program = new Command();
|
|
@@ -23,6 +25,8 @@ program.addCommand(createStartAllCommand());
|
|
|
23
25
|
program.addCommand(createRunCommand());
|
|
24
26
|
program.addCommand(createMigrateTemplatesCommand());
|
|
25
27
|
program.addCommand(createDoctorCommand());
|
|
28
|
+
program.addCommand(createIndicatorCommand());
|
|
29
|
+
program.addCommand(createMarketCommand());
|
|
26
30
|
// Show help with exit code 0 when no arguments provided
|
|
27
31
|
const args = process.argv.slice(2);
|
|
28
32
|
if (args.length === 0) {
|
|
@@ -8,18 +8,8 @@ import { wrapAISDK } from 'langsmith/experimental/vercel';
|
|
|
8
8
|
import { buildScreenPrompt } from './prompts/prompt.js';
|
|
9
9
|
import { buildMegathreadInputPrompt, buildMegathreadSystemPrompt, } from './prompts/megathread.js';
|
|
10
10
|
import { clearSubagentUsage, getSubagentUsage, } from './tools/index.js';
|
|
11
|
+
import { cacheableSystem } from './cache.js';
|
|
11
12
|
const { ToolLoopAgent, generateText, generateObject, Output } = wrapAISDK(ai);
|
|
12
|
-
// ─── Cache Helpers ─────────────────────────────────
|
|
13
|
-
function cacheableSystem(content) {
|
|
14
|
-
const message = {
|
|
15
|
-
role: 'system',
|
|
16
|
-
content,
|
|
17
|
-
providerOptions: {
|
|
18
|
-
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
return message;
|
|
22
|
-
}
|
|
23
13
|
// ─── Screen Schema (Quick Engage Check) ─────────────
|
|
24
14
|
const screenSchema = z.object({
|
|
25
15
|
engage: z.boolean().describe('true to analyze, false to skip'),
|
|
@@ -29,7 +19,7 @@ const megathreadPredictionSchema = z.object({
|
|
|
29
19
|
summary: z
|
|
30
20
|
.string()
|
|
31
21
|
.min(1)
|
|
32
|
-
.max(
|
|
22
|
+
.max(200)
|
|
33
23
|
.nullable()
|
|
34
24
|
.describe('Your CT-style take on this project. Short, punchy, in character. Think tweet, not essay. null if skipping.'),
|
|
35
25
|
conviction: z
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { processMegathreadRound, screenMegathreadRound, } from './analysis.js';
|
|
2
|
-
import { getMarketClient } from './tools/market/index.js';
|
|
3
2
|
import { extractErrorMessage } from './utils.js';
|
|
4
|
-
|
|
5
|
-
const client = getMarketClient();
|
|
6
|
-
const response = await client.getPrice(projectId, timestamp);
|
|
7
|
-
return response.price ?? undefined;
|
|
8
|
-
};
|
|
3
|
+
import { getPrice } from '../ta/service.js';
|
|
9
4
|
export async function fetchRoundPrices(projectId, roundTimestamp, currentTime) {
|
|
10
5
|
let priceAtStart;
|
|
11
6
|
let currentPrice;
|
|
12
7
|
try {
|
|
13
|
-
const client = getMarketClient();
|
|
14
8
|
[priceAtStart, currentPrice] = await Promise.all([
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
getPrice({ project: projectId, at: roundTimestamp }),
|
|
10
|
+
getPrice({ project: projectId, at: currentTime ?? new Date().toISOString() }),
|
|
17
11
|
]);
|
|
18
12
|
}
|
|
19
13
|
catch {
|
|
@@ -41,14 +41,6 @@ ${formatSkillList(runtime.skills)}
|
|
|
41
41
|
|
|
42
42
|
Use the \`executeSkill\` tool to delegate a task to a specialized subagent:
|
|
43
43
|
|
|
44
|
-
\`\`\`
|
|
45
|
-
executeSkill({
|
|
46
|
-
skillId: "ta", // Skill with the expertise you need
|
|
47
|
-
task: "Analyze RSI and MACD for BTC. Is momentum bullish or bearish?",
|
|
48
|
-
context: "Current price: $65,000" // Optional extra context
|
|
49
|
-
})
|
|
50
|
-
\`\`\`
|
|
51
|
-
|
|
52
44
|
The subagent will use its expertise to complete YOUR task. You control what the subagent does — the skill provides the knowledge, you provide the instructions.`;
|
|
53
45
|
}
|
|
54
46
|
const system = `You are an agent who will be participated in price prediction game. You will be given a context called megathread round. Each round has a project, a duration, and a round-start baseline price. Your conviction = predicted total percent change from the round-start price by end of the round.
|
|
@@ -3,6 +3,7 @@ import * as ai from 'ai';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { wrapAISDK } from 'langsmith/experimental/vercel';
|
|
5
5
|
import { getAllTools } from './index.js';
|
|
6
|
+
import { cacheableSystem } from '../cache.js';
|
|
6
7
|
const { ToolLoopAgent } = wrapAISDK(ai);
|
|
7
8
|
let _subagentUsageLog = [];
|
|
8
9
|
export function clearSubagentUsage() {
|
|
@@ -110,7 +111,7 @@ export function createExecuteSkillTool(skillRegistry, config) {
|
|
|
110
111
|
async function runSubagent({ model, systemPrompt, userPrompt, tools, maxOutputTokens, }) {
|
|
111
112
|
const agent = new ToolLoopAgent({
|
|
112
113
|
model,
|
|
113
|
-
instructions: systemPrompt,
|
|
114
|
+
instructions: cacheableSystem(systemPrompt),
|
|
114
115
|
tools,
|
|
115
116
|
maxOutputTokens,
|
|
116
117
|
});
|
|
@@ -17,25 +17,6 @@ export function signPrefix(value) {
|
|
|
17
17
|
const prefix = value >= 0 ? '+' : '';
|
|
18
18
|
return prefix;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Truncate a timeseries array for display, keeping head and tail with a `null` gap in between.
|
|
22
|
-
* Returns the original array unchanged if it fits within `maxDisplay`.
|
|
23
|
-
*/
|
|
24
|
-
export function truncateTimeseries(data, maxDisplay = 20, headCount = 5, tailCount = 10) {
|
|
25
|
-
if (data.length <= maxDisplay) {
|
|
26
|
-
return data;
|
|
27
|
-
}
|
|
28
|
-
const truncated = [...data.slice(0, headCount), null, ...data.slice(-tailCount)];
|
|
29
|
-
return truncated;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Label for the truncation gap (e.g. `"... (42 more points) ..."`).
|
|
33
|
-
*/
|
|
34
|
-
export function truncationLabel(totalLength, shownCount) {
|
|
35
|
-
const hidden = totalLength - shownCount;
|
|
36
|
-
const label = `... (${hidden} more) ...`;
|
|
37
|
-
return label;
|
|
38
|
-
}
|
|
39
20
|
/**
|
|
40
21
|
* Format a period-change percentage from first → last value.
|
|
41
22
|
* Returns e.g. `"Period change: +12.34%"`.
|
|
@@ -7,7 +7,7 @@ export class MarketClient {
|
|
|
7
7
|
this._baseUrl = baseUrl;
|
|
8
8
|
}
|
|
9
9
|
async getPrice(projectId, timestamp) {
|
|
10
|
-
const url = `${this._baseUrl}/market/price/${encodeURIComponent(projectId)}?timestamp=${encodeURIComponent(timestamp)}`;
|
|
10
|
+
const url = `${this._baseUrl}/market/price/${encodeURIComponent(projectId)}?timestamp=${encodeURIComponent(new Date(timestamp).toISOString())}`;
|
|
11
11
|
const response = await fetch(url);
|
|
12
12
|
if (!response.ok) {
|
|
13
13
|
const text = await response.text();
|
|
@@ -18,8 +18,8 @@ export class MarketClient {
|
|
|
18
18
|
}
|
|
19
19
|
async getOHLC(id, from, to, interval = 'daily') {
|
|
20
20
|
const params = new URLSearchParams({
|
|
21
|
-
from,
|
|
22
|
-
to,
|
|
21
|
+
from: new Date(from).toISOString(),
|
|
22
|
+
to: new Date(to).toISOString(),
|
|
23
23
|
interval,
|
|
24
24
|
});
|
|
25
25
|
const url = `${this._baseUrl}/market/ohlc/${encodeURIComponent(id)}?${params.toString()}`;
|