horizon-code 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "horizon-code",
3
- "version": "0.3.2",
3
+ "version": "0.5.0",
4
4
  "description": "AI-powered trading strategy terminal for Polymarket",
5
5
  "type": "module",
6
6
  "bin": {
package/src/ai/client.ts CHANGED
@@ -242,7 +242,7 @@ export async function* chat(
242
242
  const serverMessages = messages.map((m) => ({ role: m.role, content: m.content }));
243
243
 
244
244
  let step = 0;
245
- const MAX_STEPS = 5;
245
+ const MAX_STEPS = 15;
246
246
 
247
247
  while (step < MAX_STEPS) {
248
248
  step++;
@@ -379,7 +379,7 @@ export async function* chat(
379
379
  // Add as assistant message — the LLM sees it already ran the tools
380
380
  serverMessages.push({ role: "assistant", content: assistantParts.join("\n\n") });
381
381
  // Prompt the LLM to continue with its response based on the tool results
382
- serverMessages.push({ role: "user", content: "Now respond to the user based on the tool results above. Do NOT call the same tools again." });
382
+ serverMessages.push({ role: "user", content: `Now respond to the user based on the tool results above. Do NOT call the same tools again. [Step ${step}/${MAX_STEPS} used]` });
383
383
 
384
384
  // Reset structured output state for next turn
385
385
  if (structuredActive) {
package/src/app.ts CHANGED
@@ -456,8 +456,8 @@ export class App {
456
456
  if (m.proc.exitCode === null) {
457
457
  alive++;
458
458
  // Filter out __HZ_METRICS__ lines from visible logs
459
- const stdoutLines = m.stdout.slice(-30);
460
- const stderrLines = m.stderr.slice(-10).filter((l: string) => !l.startsWith("__HZ_METRICS__"));
459
+ const stdoutLines = m.stdout.slice(-100);
460
+ const stderrLines = m.stderr.slice(-50).filter((l: string) => !l.startsWith("__HZ_METRICS__"));
461
461
  latestLogs = stdoutLines.join("\n") + (stderrLines.length > 0 ? "\n--- stderr ---\n" + stderrLines.join("\n") : "");
462
462
 
463
463
  // Parse latest metrics from this process
@@ -466,8 +466,22 @@ export class App {
466
466
  localMetricsData = metrics;
467
467
  localProcessStartedAt = m.startedAt;
468
468
  }
469
- } else if (now - m.startedAt > 300000) {
470
- deadPids.push(pid);
469
+ } else {
470
+ // Process exited — check if it crashed (only notify once)
471
+ const exitCode = m.proc.exitCode;
472
+ if (exitCode !== null && exitCode !== 0 && !(m as any)._crashNotified) {
473
+ (m as any)._crashNotified = true;
474
+ const lastStderr = m.stderr.slice(-5).join("\n");
475
+ const crashMsg = `[CRASH] Process ${pid} exited with code ${exitCode}`;
476
+ this.codePanel.appendLog(crashMsg);
477
+ if (lastStderr) {
478
+ this.codePanel.appendLog(lastStderr);
479
+ }
480
+ }
481
+ // Clean up dead processes after 5 minutes
482
+ if (now - m.startedAt > 300000) {
483
+ deadPids.push(pid);
484
+ }
471
485
  }
472
486
  }
473
487
  for (const pid of deadPids) runningProcesses.delete(pid);
@@ -485,13 +499,13 @@ export class App {
485
499
  total_pnl: localMetricsData.pnl,
486
500
  realized_pnl: localMetricsData.rpnl,
487
501
  unrealized_pnl: localMetricsData.upnl,
488
- total_exposure: 0,
502
+ total_exposure: localMetricsData.exposure ?? 0,
489
503
  position_count: localMetricsData.positions,
490
504
  open_order_count: localMetricsData.orders,
491
- win_rate: 0,
492
- total_trades: 0,
493
- max_drawdown_pct: 0,
494
- sharpe_ratio: 0,
505
+ win_rate: localMetricsData.win_rate ?? 0,
506
+ total_trades: localMetricsData.trades ?? 0,
507
+ max_drawdown_pct: localMetricsData.max_dd ?? 0,
508
+ sharpe_ratio: localMetricsData.sharpe ?? 0,
495
509
  profit_factor: 0,
496
510
  avg_return_per_trade: 0,
497
511
  gross_profit: 0,
@@ -907,7 +921,8 @@ export class App {
907
921
  this.authenticated = loggedIn;
908
922
 
909
923
  const cfg = loadConfig();
910
- const email = cfg.user_email || getUser()?.email || undefined;
924
+ const rawEmail = cfg.user_email || getUser()?.email;
925
+ const email = typeof rawEmail === "string" ? rawEmail : undefined;
911
926
 
912
927
  if (!loggedIn) {
913
928
  this.splash.setAuthStatus(false);
@@ -339,9 +339,9 @@ export class CodePanel {
339
339
 
340
340
  appendLog(line: string): void {
341
341
  this._logs += (this._logs ? "\n" : "") + line;
342
- // Keep last 200 lines
342
+ // Keep last 1000 lines
343
343
  const lines = this._logs.split("\n");
344
- if (lines.length > 200) this._logs = lines.slice(-200).join("\n");
344
+ if (lines.length > 1000) this._logs = lines.slice(-1000).join("\n");
345
345
  if (this._activeTab === "logs") this.updateLogsContent();
346
346
  }
347
347
 
@@ -17,7 +17,6 @@ export class Footer {
17
17
  id: "footer",
18
18
  height: 1,
19
19
  width: "100%",
20
- backgroundColor: COLORS.bgDarker,
21
20
  flexDirection: "row",
22
21
  alignItems: "center",
23
22
  paddingLeft: 2,
@@ -31,7 +31,6 @@ export class InputBar {
31
31
  width: "100%",
32
32
  flexDirection: "column",
33
33
  paddingLeft: 4,
34
- backgroundColor: COLORS.bgSecondary,
35
34
  });
36
35
  this.acBox.visible = false;
37
36
  this.container.add(this.acBox);
@@ -52,7 +51,6 @@ export class InputBar {
52
51
  width: "100%",
53
52
  flexDirection: "row",
54
53
  alignItems: "center",
55
- backgroundColor: COLORS.bgDarker,
56
54
  paddingLeft: 2,
57
55
  });
58
56
  this.container.add(inputRow);
@@ -34,7 +34,6 @@ export class TabBar {
34
34
  width: "100%",
35
35
  flexDirection: "row",
36
36
  alignItems: "center",
37
- backgroundColor: COLORS.bgDarker,
38
37
  flexShrink: 0,
39
38
  paddingLeft: 1,
40
39
  });
@@ -214,7 +214,10 @@ export async function loginWithBrowser(): Promise<{ success: boolean; error?: st
214
214
  // Save everything
215
215
  const config = loadConfig();
216
216
  saveSessionTokens(config, sd.session.access_token, sd.session.refresh_token);
217
- config.user_email = data.email || sd.session.user.email;
217
+ // Defensively extract email server might return string or object
218
+ const rawEmail = data.email ?? sd.session.user.email ?? sd.session.user?.user_metadata?.email;
219
+ const email = typeof rawEmail === "string" ? rawEmail : typeof rawEmail === "object" && rawEmail !== null ? (rawEmail as any).address ?? (rawEmail as any).email ?? String(rawEmail) : "";
220
+ config.user_email = email;
218
221
  config.user_id = sd.session.user.id;
219
222
 
220
223
  if (!config.api_key) {
@@ -227,7 +230,7 @@ export async function loginWithBrowser(): Promise<{ success: boolean; error?: st
227
230
  }
228
231
 
229
232
  saveConfig(config);
230
- return { success: true, email: data.email };
233
+ return { success: true, email };
231
234
  }
232
235
  }
233
236
  if (res.status === 410) return { success: false, error: "Session expired. Type /login to sign in again." };
@@ -249,7 +252,8 @@ export async function loginWithPassword(email: string, password: string): Promis
249
252
 
250
253
  const config = loadConfig();
251
254
  saveSessionTokens(config, data.session.access_token, data.session.refresh_token);
252
- config.user_email = data.session.user.email ?? email;
255
+ const userEmail = data.session.user.email;
256
+ config.user_email = typeof userEmail === "string" ? userEmail : email;
253
257
  config.user_id = data.session.user.id;
254
258
 
255
259
  if (!config.api_key) {