@astranova-live/cli 0.2.5 → 0.2.6

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.
Files changed (2) hide show
  1. package/dist/astra.js +428 -129
  2. package/package.json +1 -1
package/dist/astra.js CHANGED
@@ -59,6 +59,9 @@ function memoryPath(agentName) {
59
59
  function pendingClaimPath(agentName) {
60
60
  return path.join(agentDir(agentName), "pending_claim.json");
61
61
  }
62
+ function epochBudgetPath(agentName) {
63
+ return path.join(agentDir(agentName), "epoch_budget.json");
64
+ }
62
65
  function cachePath(fileName) {
63
66
  return path.join(_root(), ".cache", fileName);
64
67
  }
@@ -99,7 +102,11 @@ var ConfigSchema = z.object({
99
102
  apiBase: z.string().default("https://agents.astranova.live"),
100
103
  preferences: z.object({
101
104
  theme: z.enum(["dark", "light"]).default("dark")
102
- }).default({})
105
+ }).default({}),
106
+ autopilot: z.object({
107
+ mode: z.enum(["off", "semi", "full"]).default("off"),
108
+ intervalMs: z.number().min(6e4).max(36e5).default(3e5)
109
+ }).default({ mode: "off", intervalMs: 3e5 })
103
110
  });
104
111
  var CredentialsSchema = z.object({
105
112
  agent_name: z.string(),
@@ -164,6 +171,15 @@ function saveConfig(config) {
164
171
  ensureBaseStructure();
165
172
  writeFileSecure(configPath(), JSON.stringify(config, null, 2));
166
173
  }
174
+ function loadAutopilotConfig() {
175
+ const config = loadConfig();
176
+ return config?.autopilot ?? { mode: "off", intervalMs: 3e5 };
177
+ }
178
+ function saveAutopilotConfig(autopilot) {
179
+ const config = loadConfig();
180
+ if (!config) return;
181
+ saveConfig({ ...config, autopilot });
182
+ }
167
183
  function loadState() {
168
184
  return readJsonFile(statePath(), StateSchema);
169
185
  }
@@ -253,6 +269,22 @@ function loadPendingClaim(agentName) {
253
269
  return null;
254
270
  }
255
271
  }
272
+ function loadEpochBudget(agentName) {
273
+ const filePath = epochBudgetPath(agentName);
274
+ if (!fs2.existsSync(filePath)) return null;
275
+ try {
276
+ return JSON.parse(fs2.readFileSync(filePath, "utf-8"));
277
+ } catch {
278
+ return null;
279
+ }
280
+ }
281
+ function saveEpochBudget(agentName, data) {
282
+ try {
283
+ ensureDir(agentDir(agentName));
284
+ writeFileSecure(epochBudgetPath(agentName), JSON.stringify(data));
285
+ } catch {
286
+ }
287
+ }
256
288
  function clearPendingClaim(agentName) {
257
289
  const filePath = pendingClaimPath(agentName);
258
290
  if (fs2.existsSync(filePath)) {
@@ -1054,7 +1086,8 @@ async function runOnboarding() {
1054
1086
  model,
1055
1087
  auth,
1056
1088
  apiBase: "https://agents.astranova.live",
1057
- preferences: { theme: "dark" }
1089
+ preferences: { theme: "dark" },
1090
+ autopilot: { mode: "off", intervalMs: 3e5 }
1058
1091
  };
1059
1092
  saveConfig(config);
1060
1093
  clack3.log.success(`Provider set to ${provider} (${model})`);
@@ -1358,17 +1391,50 @@ function loadMemory(agentName) {
1358
1391
  }
1359
1392
 
1360
1393
  // src/ui/App.tsx
1361
- import { useState as useState4, useCallback as useCallback2, useRef as useRef2 } from "react";
1394
+ import { useState as useState4, useCallback as useCallback2, useRef as useRef2, useEffect as useEffect3 } from "react";
1362
1395
  import { Box as Box7, Text as Text8, useApp, useInput } from "ink";
1363
1396
 
1364
1397
  // src/ui/StatusBar.tsx
1365
1398
  import React, { useState, useEffect, useRef, useCallback } from "react";
1366
1399
  import { Box, Text } from "ink";
1400
+
1401
+ // src/autopilot/scheduler.ts
1402
+ var CALLS_PER_AUTOPILOT_TURN = 3;
1403
+ var EPOCH_BUDGET = 10;
1404
+ var BUDGET_BUFFER = 2;
1405
+ var SEMI_TRIGGER_MSG = "AUTOPILOT CHECK: Analyze market and propose a trade if signal is clear. Ask me to confirm before executing.";
1406
+ var FULL_TRIGGER_MSG = "AUTOPILOT CHECK: Analyze market and execute a trade if signal is clear. If uncertain, skip. Keep response to 2-3 lines max.";
1407
+ function buildAutopilotTrigger(mode) {
1408
+ switch (mode) {
1409
+ case "semi":
1410
+ return SEMI_TRIGGER_MSG;
1411
+ case "full":
1412
+ return FULL_TRIGGER_MSG;
1413
+ case "off":
1414
+ return null;
1415
+ }
1416
+ }
1417
+ function formatInterval(ms) {
1418
+ const minutes = Math.round(ms / 6e4);
1419
+ return `${minutes}m`;
1420
+ }
1421
+ function parseInterval(input) {
1422
+ const match = input.match(/^(\d+)m$/);
1423
+ if (!match) return null;
1424
+ const minutes = parseInt(match[1], 10);
1425
+ if (minutes < 1 || minutes > 60) return null;
1426
+ return minutes * 6e4;
1427
+ }
1428
+
1429
+ // src/ui/StatusBar.tsx
1367
1430
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1368
- var POLL_INTERVAL_MS = 3e4;
1431
+ var POLL_INTERVAL_MS = 6e4;
1369
1432
  var StatusBar = React.memo(function StatusBar2({
1370
1433
  agentName,
1371
- journeyStage
1434
+ journeyStage,
1435
+ autopilotMode = "off",
1436
+ autopilotIntervalMs = 3e5,
1437
+ onEpochChange
1372
1438
  }) {
1373
1439
  const [data, setData] = useState({ market: null, portfolio: null });
1374
1440
  const mounted = useRef(true);
@@ -1383,7 +1449,10 @@ var StatusBar = React.memo(function StatusBar2({
1383
1449
  market: marketRes ?? prev.market,
1384
1450
  portfolio: portfolioRes ?? prev.portfolio
1385
1451
  }));
1386
- }, [agentName]);
1452
+ if (marketRes && onEpochChange) {
1453
+ onEpochChange(marketRes.epochId);
1454
+ }
1455
+ }, [agentName, onEpochChange]);
1387
1456
  useEffect(() => {
1388
1457
  mounted.current = true;
1389
1458
  if (!canFetchData) return;
@@ -1395,50 +1464,64 @@ var StatusBar = React.memo(function StatusBar2({
1395
1464
  };
1396
1465
  }, [canFetchData, poll]);
1397
1466
  const { market, portfolio } = data;
1398
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: "100%", children: /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
1399
- /* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "AstraNova" }),
1400
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1401
- /* @__PURE__ */ jsx(Text, { color: "white", children: agentName }),
1402
- canFetchData && market && /* @__PURE__ */ jsxs(Fragment, { children: [
1403
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1404
- /* @__PURE__ */ jsx(Text, { color: "yellow", children: "$NOVA " }),
1405
- /* @__PURE__ */ jsx(Text, { color: "white", children: formatPrice(market.price) }),
1406
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1407
- /* @__PURE__ */ jsx(Text, { color: moodColor(market.mood), children: market.mood })
1408
- ] }),
1409
- canFetchData && portfolio && /* @__PURE__ */ jsxs(Fragment, { children: [
1467
+ const apActive = autopilotMode !== "off";
1468
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: "100%", children: /* @__PURE__ */ jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [
1469
+ /* @__PURE__ */ jsxs(Box, { children: [
1470
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "#00ff00", children: "AstraNova" }),
1410
1471
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1411
- /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
1412
- formatNum(portfolio.cash),
1413
- " $SIM"
1472
+ /* @__PURE__ */ jsx(Text, { color: "#ff8800", children: agentName }),
1473
+ canFetchData && market && /* @__PURE__ */ jsxs(Fragment, { children: [
1474
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1475
+ /* @__PURE__ */ jsx(Text, { color: "#ffff00", children: "$NOVA " }),
1476
+ /* @__PURE__ */ jsx(Text, { color: "white", children: formatPrice(market.price) }),
1477
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1478
+ /* @__PURE__ */ jsx(Text, { color: moodColor(market.mood), children: market.mood })
1414
1479
  ] }),
1415
- portfolio.tokens > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1480
+ canFetchData && portfolio && /* @__PURE__ */ jsxs(Fragment, { children: [
1481
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1482
+ /* @__PURE__ */ jsxs(Text, { color: "#00ffff", children: [
1483
+ formatNum(portfolio.cash),
1484
+ " $SIM"
1485
+ ] }),
1486
+ portfolio.tokens > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1487
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1488
+ /* @__PURE__ */ jsxs(Text, { color: "#ff00ff", children: [
1489
+ formatNum(portfolio.tokens),
1490
+ " $NOVA"
1491
+ ] })
1492
+ ] }),
1493
+ portfolio.pnl !== 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1494
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1495
+ /* @__PURE__ */ jsxs(Text, { color: portfolio.pnl >= 0 ? "#00ff00" : "#ff4444", children: [
1496
+ "P&L ",
1497
+ portfolio.pnl >= 0 ? "+" : "",
1498
+ formatNum(portfolio.pnl),
1499
+ " (",
1500
+ portfolio.pnlPct >= 0 ? "+" : "",
1501
+ portfolio.pnlPct.toFixed(1),
1502
+ "%)"
1503
+ ] })
1504
+ ] }),
1416
1505
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1417
- /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
1418
- formatNum(portfolio.tokens),
1419
- " $NOVA"
1506
+ /* @__PURE__ */ jsxs(Text, { color: "#e2f902", children: [
1507
+ "Val ",
1508
+ formatNum(portfolio.portfolioValue)
1420
1509
  ] })
1421
1510
  ] }),
1422
- portfolio.pnl !== 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1511
+ !canFetchData && /* @__PURE__ */ jsxs(Fragment, { children: [
1423
1512
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1424
- /* @__PURE__ */ jsxs(Text, { color: portfolio.pnl >= 0 ? "green" : "red", children: [
1425
- "P&L ",
1426
- portfolio.pnl >= 0 ? "+" : "",
1427
- formatNum(portfolio.pnl),
1428
- " (",
1429
- portfolio.pnlPct >= 0 ? "+" : "",
1430
- portfolio.pnlPct.toFixed(1),
1431
- "%)"
1432
- ] })
1513
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "pending verification" })
1514
+ ] }),
1515
+ canFetchData && !market && !portfolio && /* @__PURE__ */ jsxs(Fragment, { children: [
1516
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1517
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "loading..." })
1433
1518
  ] })
1434
1519
  ] }),
1435
- !canFetchData && /* @__PURE__ */ jsxs(Fragment, { children: [
1436
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1437
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "pending verification" })
1438
- ] }),
1439
- canFetchData && !market && !portfolio && /* @__PURE__ */ jsxs(Fragment, { children: [
1440
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
1441
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "loading..." })
1520
+ apActive && /* @__PURE__ */ jsxs(Text, { color: "#00ff00", children: [
1521
+ "AP: \u25CF ",
1522
+ autopilotMode.toUpperCase(),
1523
+ " ",
1524
+ formatInterval(autopilotIntervalMs)
1442
1525
  ] })
1443
1526
  ] }) });
1444
1527
  });
@@ -1457,12 +1540,12 @@ function moodColor(mood) {
1457
1540
  switch (mood) {
1458
1541
  case "euphoria":
1459
1542
  case "bullish":
1460
- return "green";
1543
+ return "#00ff00";
1461
1544
  case "fear":
1462
1545
  case "bearish":
1463
- return "red";
1546
+ return "#ff4444";
1464
1547
  case "crab":
1465
- return "yellow";
1548
+ return "#ffff00";
1466
1549
  default:
1467
1550
  return "white";
1468
1551
  }
@@ -1474,7 +1557,8 @@ async function fetchMarket(agentName) {
1474
1557
  const m = d.market ?? d;
1475
1558
  return {
1476
1559
  price: m.price ?? 0,
1477
- mood: m.mood ?? ""
1560
+ mood: m.mood ?? "",
1561
+ epochId: m.epoch?.global ?? 0
1478
1562
  };
1479
1563
  }
1480
1564
  async function fetchPortfolio(agentName) {
@@ -1487,9 +1571,13 @@ async function fetchPortfolio(agentName) {
1487
1571
  if (!result.ok) return null;
1488
1572
  const d = result.data;
1489
1573
  const p = d.portfolio ?? d;
1574
+ const cash = p.cash ?? 0;
1575
+ const tokens = p.tokens ?? 0;
1576
+ const currentPrice = p.currentPrice ?? 0;
1490
1577
  return {
1491
- cash: p.cash ?? 0,
1492
- tokens: p.tokens ?? 0,
1578
+ cash,
1579
+ tokens,
1580
+ portfolioValue: p.portfolioValue ?? cash + tokens * currentPrice,
1493
1581
  pnl: p.pnl ?? 0,
1494
1582
  pnlPct: p.pnlPct ?? 0
1495
1583
  };
@@ -1513,35 +1601,26 @@ function PortfolioCard({ data }) {
1513
1601
  const pnlPct = data.pnlPct ?? 0;
1514
1602
  const earned = data.totalEarned ? Number(data.totalEarned) / 1e9 : 0;
1515
1603
  const claimable = data.claimable ? Number(data.claimable) / 1e9 : 0;
1516
- const pnlColor = pnl >= 0 ? "green" : "red";
1604
+ const pnlColor = pnl >= 0 ? "#00ff00" : "#ff4444";
1517
1605
  const pnlSign = pnl >= 0 ? "+" : "";
1518
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, marginY: 1, children: [
1519
- /* @__PURE__ */ jsx2(Box2, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: " Portfolio Overview " }) }),
1520
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", children: [
1521
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: "50%", children: [
1522
- /* @__PURE__ */ jsx2(Row, { label: "$SIM Balance", value: formatNum2(cash), color: "cyan" }),
1523
- /* @__PURE__ */ jsx2(Row, { label: "$NOVA Holdings", value: tokens > 0 ? formatNum2(tokens) : "\u2014", color: "magenta" }),
1524
- /* @__PURE__ */ jsx2(Row, { label: "$NOVA Price", value: price > 0 ? formatPrice2(price) : "\u2014", color: "yellow" })
1525
- ] }),
1526
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: "50%", children: [
1527
- /* @__PURE__ */ jsx2(Row, { label: "Portfolio Value", value: formatNum2(value), color: "white" }),
1528
- /* @__PURE__ */ jsx2(Row, { label: "P&L", value: `${pnlSign}${formatNum2(pnl)} (${pnlSign}${pnlPct.toFixed(1)}%)`, color: pnlColor }),
1529
- (data.hasWallet !== void 0 || data.walletLocal !== void 0) && /* @__PURE__ */ jsx2(
1530
- Row,
1531
- {
1532
- label: "Wallet",
1533
- value: data.hasWallet ? "registered" : data.walletLocal ? "needs registration" : "not set",
1534
- color: data.hasWallet ? "green" : data.walletLocal ? "yellow" : "gray"
1535
- }
1536
- )
1537
- ] })
1538
- ] }),
1606
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingLeft: 1, marginY: 1, children: [
1607
+ /* @__PURE__ */ jsx2(Box2, { marginBottom: 1, children: /* @__PURE__ */ jsx2(Text2, { bold: true, color: "#00ffff", children: "Portfolio Overview" }) }),
1608
+ /* @__PURE__ */ jsx2(Row, { label: "$SIM Balance", value: formatNum2(cash), color: "#00ffff" }),
1609
+ /* @__PURE__ */ jsx2(Row, { label: "$NOVA Holdings", value: tokens > 0 ? formatNum2(tokens) : "\u2014", color: "#ff00ff" }),
1610
+ /* @__PURE__ */ jsx2(Row, { label: "$NOVA Price", value: price > 0 ? formatPrice2(price) : "\u2014", color: "#ffff00" }),
1611
+ /* @__PURE__ */ jsx2(Row, { label: "Portfolio Value", value: formatNum2(value), color: "white" }),
1612
+ /* @__PURE__ */ jsx2(Row, { label: "P&L", value: `${pnlSign}${formatNum2(pnl)} (${pnlSign}${pnlPct.toFixed(1)}%)`, color: pnlColor }),
1613
+ (data.hasWallet !== void 0 || data.walletLocal !== void 0) && /* @__PURE__ */ jsx2(
1614
+ Row,
1615
+ {
1616
+ label: "Wallet",
1617
+ value: data.hasWallet ? "registered" : data.walletLocal ? "needs registration" : "not set",
1618
+ color: data.hasWallet ? "#00ff00" : data.walletLocal ? "#ffff00" : "gray"
1619
+ }
1620
+ ),
1539
1621
  (earned > 0 || claimable > 0) && /* @__PURE__ */ jsxs2(Fragment2, { children: [
1540
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, marginBottom: 0, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2500".repeat(36) }) }),
1541
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", children: [
1542
- /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", width: "50%", children: /* @__PURE__ */ jsx2(Row, { label: "$ASTRA Earned", value: earned > 0 ? formatAstra(earned) : "\u2014", color: "yellow" }) }),
1543
- /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", width: "50%", children: /* @__PURE__ */ jsx2(Row, { label: "Claimable", value: claimable > 0 ? formatAstra(claimable) : "\u2014", color: claimable > 0 ? "green" : "gray" }) })
1544
- ] })
1622
+ /* @__PURE__ */ jsx2(Row, { label: "$ASTRA Earned", value: earned > 0 ? formatAstra(earned) : "\u2014", color: "#ffff00" }),
1623
+ /* @__PURE__ */ jsx2(Row, { label: "Claimable", value: claimable > 0 ? formatAstra(claimable) : "\u2014", color: claimable > 0 ? "#00ff00" : "gray" })
1545
1624
  ] })
1546
1625
  ] });
1547
1626
  }
@@ -1572,38 +1651,29 @@ function formatAstra(n) {
1572
1651
 
1573
1652
  // src/ui/RewardsCard.tsx
1574
1653
  import { Box as Box3, Text as Text3 } from "ink";
1575
- import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1654
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1576
1655
  function RewardsCard({ data }) {
1577
1656
  const total = data.totalAstra ? Number(data.totalAstra) / 1e9 : 0;
1578
1657
  const epoch = data.epochAstra ? Number(data.epochAstra) / 1e9 : 0;
1579
1658
  const bonus = data.bonusAstra ? Number(data.bonusAstra) / 1e9 : 0;
1580
- const statusColor = data.claimStatus === "claimable" ? "green" : data.claimStatus === "sent" ? "cyan" : "yellow";
1581
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, marginY: 1, children: [
1582
- /* @__PURE__ */ jsxs3(Box3, { justifyContent: "center", marginBottom: 1, children: [
1583
- /* @__PURE__ */ jsx3(Text3, { bold: true, color: "yellow", children: " $ASTRA Rewards " }),
1659
+ const statusColor = data.claimStatus === "claimable" ? "#00ff00" : data.claimStatus === "sent" ? "#00ffff" : "#ffff00";
1660
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingLeft: 1, marginY: 1, children: [
1661
+ /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
1662
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "#ffff00", children: "$ASTRA Rewards" }),
1584
1663
  data.seasonId && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
1585
- " \u2014 ",
1664
+ " ",
1586
1665
  data.seasonId
1587
1666
  ] })
1588
1667
  ] }),
1589
- /* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", children: [
1590
- /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", width: "50%", children: [
1591
- /* @__PURE__ */ jsx3(Row2, { label: "Total Earned", value: formatAstra2(total), color: "yellow" }),
1592
- /* @__PURE__ */ jsx3(Row2, { label: "Epoch Rewards", value: formatAstra2(epoch), color: "cyan" }),
1593
- /* @__PURE__ */ jsx3(Row2, { label: "Season Bonus", value: formatAstra2(bonus), color: "magenta" })
1594
- ] }),
1595
- /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", width: "50%", children: [
1596
- /* @__PURE__ */ jsx3(Row2, { label: "Status", value: data.claimStatus ?? "\u2014", color: statusColor }),
1597
- /* @__PURE__ */ jsx3(Row2, { label: "Epochs Rewarded", value: data.epochsRewarded?.toString() ?? "\u2014", color: "white" }),
1598
- data.bestEpochPnl !== void 0 && data.bestEpochPnl > 0 && /* @__PURE__ */ jsx3(Row2, { label: "Best Epoch P&L", value: `+${data.bestEpochPnl.toFixed(2)}`, color: "green" })
1599
- ] })
1600
- ] }),
1601
- data.txSignature && /* @__PURE__ */ jsxs3(Fragment3, { children: [
1602
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2500".repeat(36) }) }),
1603
- /* @__PURE__ */ jsxs3(Box3, { children: [
1604
- /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Tx: " }),
1605
- /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: data.txSignature })
1606
- ] })
1668
+ /* @__PURE__ */ jsx3(Row2, { label: "Total Earned", value: formatAstra2(total), color: "#ffff00" }),
1669
+ /* @__PURE__ */ jsx3(Row2, { label: "Epoch Rewards", value: formatAstra2(epoch), color: "#00ffff" }),
1670
+ /* @__PURE__ */ jsx3(Row2, { label: "Season Bonus", value: formatAstra2(bonus), color: "#ff00ff" }),
1671
+ /* @__PURE__ */ jsx3(Row2, { label: "Status", value: data.claimStatus ?? "\u2014", color: statusColor }),
1672
+ /* @__PURE__ */ jsx3(Row2, { label: "Epochs Rewarded", value: data.epochsRewarded?.toString() ?? "\u2014", color: "white" }),
1673
+ data.bestEpochPnl !== void 0 && data.bestEpochPnl > 0 && /* @__PURE__ */ jsx3(Row2, { label: "Best Epoch P&L", value: `+${data.bestEpochPnl.toFixed(2)}`, color: "#00ff00" }),
1674
+ data.txSignature && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
1675
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Tx: " }),
1676
+ /* @__PURE__ */ jsx3(Text3, { color: "#00ffff", children: data.txSignature })
1607
1677
  ] })
1608
1678
  ] });
1609
1679
  }
@@ -1776,12 +1846,23 @@ function ChatView({
1776
1846
  streamingText
1777
1847
  }) {
1778
1848
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", flexGrow: 1, flexShrink: 1, overflow: "hidden", paddingX: 1, children: [
1779
- messages.map((msg, i) => /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
1780
- /* @__PURE__ */ jsx5(Text5, { bold: true, color: msg.role === "user" ? "green" : "cyan", children: msg.role === "user" ? " You" : " Agent" }),
1781
- /* @__PURE__ */ jsx5(Box5, { marginLeft: 1, children: msg.role === "assistant" ? /* @__PURE__ */ jsx5(MarkdownText, { children: msg.content }) : /* @__PURE__ */ jsx5(Text5, { wrap: "wrap", children: msg.content }) })
1782
- ] }, i)),
1849
+ messages.map((msg, i) => {
1850
+ if (msg.role === "log") {
1851
+ return /* @__PURE__ */ jsx5(Box5, { paddingLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: msg.content }) }, i);
1852
+ }
1853
+ if (msg.role === "autopilot") {
1854
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
1855
+ /* @__PURE__ */ jsx5(Text5, { bold: true, color: "#ff00ff", children: " Autopilot" }),
1856
+ /* @__PURE__ */ jsx5(Box5, { marginLeft: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "wrap", children: msg.content }) })
1857
+ ] }, i);
1858
+ }
1859
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
1860
+ /* @__PURE__ */ jsx5(Text5, { bold: true, color: msg.role === "user" ? "#00ff00" : "#00ffff", children: msg.role === "user" ? " You" : " Agent" }),
1861
+ /* @__PURE__ */ jsx5(Box5, { marginLeft: 1, children: msg.role === "assistant" ? /* @__PURE__ */ jsx5(MarkdownText, { children: msg.content }) : /* @__PURE__ */ jsx5(Text5, { wrap: "wrap", children: msg.content }) })
1862
+ ] }, i);
1863
+ }),
1783
1864
  streamingText !== void 0 && streamingText.length > 0 && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
1784
- /* @__PURE__ */ jsx5(Text5, { bold: true, color: "cyan", children: " Agent" }),
1865
+ /* @__PURE__ */ jsx5(Text5, { bold: true, color: "#00ffff", children: " Agent" }),
1785
1866
  /* @__PURE__ */ jsx5(Box5, { marginLeft: 1, children: /* @__PURE__ */ jsx5(MarkdownText, { children: streamingText }) })
1786
1867
  ] })
1787
1868
  ] });
@@ -1803,8 +1884,8 @@ function Input({
1803
1884
  onSubmit(trimmed);
1804
1885
  setValue("");
1805
1886
  };
1806
- return /* @__PURE__ */ jsx6(Box6, { width: "100%", borderStyle: "round", borderColor: isActive ? "yellow" : "gray", children: /* @__PURE__ */ jsxs6(Box6, { paddingX: 1, width: "100%", children: [
1807
- /* @__PURE__ */ jsx6(Text6, { color: isActive ? "yellow" : "gray", bold: true, children: "\u203A " }),
1887
+ return /* @__PURE__ */ jsxs6(Box6, { width: "100%", paddingX: 2, paddingY: 1, children: [
1888
+ /* @__PURE__ */ jsx6(Text6, { color: isActive ? "yellow" : "gray", bold: true, children: "\u276F\u276F " }),
1808
1889
  /* @__PURE__ */ jsx6(
1809
1890
  TextInput,
1810
1891
  {
@@ -1813,10 +1894,10 @@ function Input({
1813
1894
  onSubmit: handleSubmit,
1814
1895
  focus: isActive,
1815
1896
  showCursor: isActive,
1816
- placeholder: isActive ? "Type a message..." : "Waiting for response..."
1897
+ placeholder: ""
1817
1898
  }
1818
1899
  )
1819
- ] }) });
1900
+ ] });
1820
1901
  }
1821
1902
 
1822
1903
  // src/ui/Spinner.tsx
@@ -2029,6 +2110,21 @@ function buildSystemPrompt(skillContext, tradingContext, walletContext, rewardsC
2029
2110
  if (profile.season !== void 0) {
2030
2111
  parts.push(`Season: ${profile.season}`);
2031
2112
  }
2113
+ const apMode = profile.autopilotMode ?? "off";
2114
+ if (apMode !== "off") {
2115
+ parts.push("", "---", "");
2116
+ parts.push(`## AUTOPILOT: ${apMode.toUpperCase()}`);
2117
+ parts.push("");
2118
+ parts.push(`On AUTOPILOT CHECK triggers:`);
2119
+ parts.push(`- GET /api/v1/market/state + GET /api/v1/portfolio`);
2120
+ parts.push(`- Apply strategy from memory.md (default: momentum + balance)`);
2121
+ if (apMode === "semi") {
2122
+ parts.push(`- SEMI: propose trade, wait for explicit user approval`);
2123
+ } else {
2124
+ parts.push(`- FULL: execute if signal clear; skip if uncertain`);
2125
+ }
2126
+ parts.push(`- Max 2-3 lines. No action = "Market checked \u2014 holding."`);
2127
+ }
2032
2128
  if (memoryContent && memoryContent.trim()) {
2033
2129
  parts.push("", "---", "");
2034
2130
  parts.push("## Agent Memory (persistent across sessions)", "");
@@ -3476,6 +3572,7 @@ async function runResponsesApiTurn(messages, systemPrompt, callbacks, turnConfig
3476
3572
  toolResultParts.push({
3477
3573
  type: "tool-result",
3478
3574
  toolCallId: tc.callId,
3575
+ toolName: tc.name,
3479
3576
  result: errorResult
3480
3577
  });
3481
3578
  codexInput.push({
@@ -3511,6 +3608,7 @@ async function runResponsesApiTurn(messages, systemPrompt, callbacks, turnConfig
3511
3608
  toolResultParts.push({
3512
3609
  type: "tool-result",
3513
3610
  toolCallId: tc.callId,
3611
+ toolName: tc.name,
3514
3612
  result: toolResult
3515
3613
  });
3516
3614
  codexInput.push({
@@ -3541,6 +3639,7 @@ async function runResponsesApiTurn(messages, systemPrompt, callbacks, turnConfig
3541
3639
  toolResultParts.push({
3542
3640
  type: "tool-result",
3543
3641
  toolCallId: tc.callId,
3642
+ toolName: tc.name,
3544
3643
  result: errorResult
3545
3644
  });
3546
3645
  codexInput.push({
@@ -3828,6 +3927,7 @@ function App({
3828
3927
  memoryContent,
3829
3928
  initialCoreMessages,
3830
3929
  initialChatMessages,
3930
+ initialAutopilotConfig,
3831
3931
  debug
3832
3932
  }) {
3833
3933
  const { exit } = useApp();
@@ -3838,20 +3938,131 @@ function App({
3838
3938
  initialCoreMessages ?? []
3839
3939
  );
3840
3940
  const providerRef = useRef2(loadConfig()?.provider ?? "unknown");
3841
- const [streamingText, setStreamingText] = useState4(
3842
- void 0
3843
- );
3941
+ const [streamingText, setStreamingText] = useState4(void 0);
3844
3942
  const [isLoading, setIsLoading] = useState4(false);
3943
+ const isLoadingRef = useRef2(false);
3944
+ useEffect3(() => {
3945
+ isLoadingRef.current = isLoading;
3946
+ }, [isLoading]);
3845
3947
  const [toolName, setToolName] = useState4(void 0);
3948
+ const [autopilotMode, setAutopilotMode] = useState4(initialAutopilotConfig?.mode ?? "off");
3949
+ const [autopilotIntervalMs, setAutopilotIntervalMs] = useState4(initialAutopilotConfig?.intervalMs ?? 3e5);
3950
+ const epochCallCountRef = useRef2(0);
3951
+ const epochIdRef = useRef2(null);
3952
+ useEffect3(() => {
3953
+ const saved = loadEpochBudget(agentName);
3954
+ if (saved) {
3955
+ epochIdRef.current = saved.epochId;
3956
+ epochCallCountRef.current = saved.callCount;
3957
+ }
3958
+ }, [agentName]);
3959
+ const coreMessagesRef = useRef2(coreMessages);
3960
+ useEffect3(() => {
3961
+ coreMessagesRef.current = coreMessages;
3962
+ }, [coreMessages]);
3963
+ const chatMessagesRef = useRef2(chatMessages);
3964
+ useEffect3(() => {
3965
+ chatMessagesRef.current = chatMessages;
3966
+ }, [chatMessages]);
3967
+ const sendMessageRef = useRef2(async () => {
3968
+ });
3846
3969
  useInput((_input, key) => {
3847
3970
  if (key.ctrl && _input === "c") {
3848
3971
  exit();
3849
3972
  }
3850
3973
  });
3974
+ const handleEpochChange = useCallback2((newEpoch) => {
3975
+ if (epochIdRef.current === newEpoch) return;
3976
+ epochIdRef.current = newEpoch;
3977
+ epochCallCountRef.current = 0;
3978
+ saveEpochBudget(agentName, { epochId: newEpoch, callCount: 0 });
3979
+ }, [agentName]);
3980
+ const addLogEntry = useCallback2((action, detail) => {
3981
+ const time = formatTime(/* @__PURE__ */ new Date());
3982
+ const text3 = detail ? `\u27F3 ${time} ${action} \u2014 ${detail}` : `\u27F3 ${time} ${action}`;
3983
+ setChatMessages((prev) => [...prev, { role: "log", content: text3 }]);
3984
+ }, []);
3985
+ const runAutopilotTurn = useCallback2(
3986
+ async (triggerMsg) => {
3987
+ const currentCore = coreMessagesRef.current;
3988
+ const newCoreMessages = [
3989
+ ...currentCore,
3990
+ { role: "user", content: triggerMsg }
3991
+ ];
3992
+ setIsLoading(true);
3993
+ try {
3994
+ const result = await runAgentTurn(
3995
+ newCoreMessages,
3996
+ skillContext,
3997
+ tradingContext,
3998
+ walletContext,
3999
+ rewardsContext,
4000
+ onboardingContext,
4001
+ apiContext,
4002
+ { ...profile, autopilotMode },
4003
+ {
4004
+ onTextChunk: () => {
4005
+ },
4006
+ onToolCallStart: () => {
4007
+ },
4008
+ onToolCallEnd: () => {
4009
+ }
4010
+ },
4011
+ memoryContent
4012
+ );
4013
+ const baseCoreMessages = result.compactedMessages ?? newCoreMessages;
4014
+ const updatedCore = [...baseCoreMessages, ...result.responseMessages];
4015
+ setCoreMessages(updatedCore);
4016
+ const responseText = result.text.trim();
4017
+ if (responseText) {
4018
+ const firstLine = responseText.split("\n")[0].slice(0, 80);
4019
+ const detail = responseText.length > 80 ? responseText.slice(0, 120) : void 0;
4020
+ addLogEntry(firstLine, detail !== firstLine ? detail : void 0);
4021
+ } else {
4022
+ addLogEntry("checked \u2192 no response");
4023
+ }
4024
+ const updatedChat = chatMessagesRef.current;
4025
+ saveSession({
4026
+ agentName,
4027
+ provider: providerRef.current,
4028
+ sessionId,
4029
+ coreMessages: updatedCore,
4030
+ chatMessages: updatedChat
4031
+ });
4032
+ } catch (error) {
4033
+ const message = error instanceof Error ? error.message : "Unknown error";
4034
+ addLogEntry("error", message);
4035
+ } finally {
4036
+ setIsLoading(false);
4037
+ }
4038
+ },
4039
+ [skillContext, tradingContext, walletContext, rewardsContext, onboardingContext, apiContext, profile, autopilotMode, agentName, sessionId, memoryContent, addLogEntry]
4040
+ );
4041
+ useEffect3(() => {
4042
+ if (autopilotMode === "off") return;
4043
+ const interval = setInterval(() => {
4044
+ if (isLoadingRef.current) return;
4045
+ if (epochCallCountRef.current + CALLS_PER_AUTOPILOT_TURN > EPOCH_BUDGET - BUDGET_BUFFER) {
4046
+ addLogEntry("Budget reached \u2014 skipping until next epoch");
4047
+ return;
4048
+ }
4049
+ epochCallCountRef.current += CALLS_PER_AUTOPILOT_TURN;
4050
+ saveEpochBudget(agentName, { epochId: epochIdRef.current ?? 0, callCount: epochCallCountRef.current });
4051
+ const trigger = buildAutopilotTrigger(autopilotMode);
4052
+ if (!trigger) return;
4053
+ if (autopilotMode === "semi") {
4054
+ void sendMessageRef.current(trigger, "autopilot");
4055
+ } else {
4056
+ void runAutopilotTurn(trigger);
4057
+ }
4058
+ }, autopilotIntervalMs);
4059
+ return () => clearInterval(interval);
4060
+ }, [autopilotMode, autopilotIntervalMs, addLogEntry, runAutopilotTurn]);
3851
4061
  const sendMessage = useCallback2(
3852
- async (userText) => {
4062
+ async (userText, displayRole = "user") => {
3853
4063
  if (userText.startsWith("/")) {
3854
- const cmd = userText.split(" ")[0].toLowerCase();
4064
+ const parts = userText.trim().split(/\s+/);
4065
+ const cmd = parts[0].toLowerCase();
3855
4066
  if (cmd === "/exit" || cmd === "/quit" || cmd === "/q") {
3856
4067
  exit();
3857
4068
  return;
@@ -3868,6 +4079,77 @@ function App({
3868
4079
  ]);
3869
4080
  return;
3870
4081
  }
4082
+ if (cmd === "/auto") {
4083
+ const sub = parts[1]?.toLowerCase();
4084
+ if (!sub || sub === "status") {
4085
+ const modeLabel = autopilotMode.toUpperCase();
4086
+ const intervalLabel = formatInterval(autopilotIntervalMs);
4087
+ const budgetLabel = `${epochCallCountRef.current}/${EPOCH_BUDGET}`;
4088
+ setChatMessages((prev) => [
4089
+ ...prev,
4090
+ { role: "user", content: userText },
4091
+ { role: "assistant", content: `**Autopilot Status**
4092
+
4093
+ Mode: ${modeLabel}
4094
+ Interval: ${intervalLabel}
4095
+ Epoch Budget: ${budgetLabel} calls used` }
4096
+ ]);
4097
+ return;
4098
+ }
4099
+ if (sub === "on" || sub === "semi") {
4100
+ setAutopilotMode("semi");
4101
+ saveAutopilotConfig({ mode: "semi", intervalMs: autopilotIntervalMs });
4102
+ setChatMessages((prev) => [
4103
+ ...prev,
4104
+ { role: "user", content: userText },
4105
+ { role: "assistant", content: `Autopilot enabled: **SEMI** mode (every ${formatInterval(autopilotIntervalMs)}). I'll propose trades for your approval.` }
4106
+ ]);
4107
+ addLogEntry("Mode set to SEMI");
4108
+ return;
4109
+ }
4110
+ if (sub === "full") {
4111
+ setAutopilotMode("full");
4112
+ saveAutopilotConfig({ mode: "full", intervalMs: autopilotIntervalMs });
4113
+ setChatMessages((prev) => [
4114
+ ...prev,
4115
+ { role: "user", content: userText },
4116
+ { role: "assistant", content: `Autopilot enabled: **FULL** mode (every ${formatInterval(autopilotIntervalMs)}). I'll execute trades autonomously \u2014 check the log panel.` }
4117
+ ]);
4118
+ addLogEntry("Mode set to FULL");
4119
+ return;
4120
+ }
4121
+ if (sub === "off") {
4122
+ setAutopilotMode("off");
4123
+ saveAutopilotConfig({ mode: "off", intervalMs: autopilotIntervalMs });
4124
+ setChatMessages((prev) => [
4125
+ ...prev,
4126
+ { role: "user", content: userText },
4127
+ { role: "assistant", content: "Autopilot disabled." }
4128
+ ]);
4129
+ addLogEntry("Autopilot OFF");
4130
+ return;
4131
+ }
4132
+ const parsed = parseInterval(sub);
4133
+ if (parsed !== null) {
4134
+ setAutopilotIntervalMs(parsed);
4135
+ const newMode = autopilotMode === "off" ? "semi" : autopilotMode;
4136
+ setAutopilotMode(newMode);
4137
+ saveAutopilotConfig({ mode: newMode, intervalMs: parsed });
4138
+ setChatMessages((prev) => [
4139
+ ...prev,
4140
+ { role: "user", content: userText },
4141
+ { role: "assistant", content: `Autopilot interval set to **${formatInterval(parsed)}**. Mode: ${newMode.toUpperCase()}.` }
4142
+ ]);
4143
+ addLogEntry(`Interval set to ${formatInterval(parsed)}`);
4144
+ return;
4145
+ }
4146
+ setChatMessages((prev) => [
4147
+ ...prev,
4148
+ { role: "user", content: userText },
4149
+ { role: "assistant", content: "Usage: `/auto on` \xB7 `/auto full` \xB7 `/auto off` \xB7 `/auto 5m` \xB7 `/auto status`" }
4150
+ ]);
4151
+ return;
4152
+ }
3871
4153
  if (cmd === "/help" || cmd === "/?") {
3872
4154
  setChatMessages((prev) => [
3873
4155
  ...prev,
@@ -3886,13 +4168,13 @@ function App({
3886
4168
  " `/buy <amt>` \u2014 Buy $NOVA (e.g. `/buy 500`)",
3887
4169
  " `/sell <amt>`\u2014 Sell $NOVA (e.g. `/sell 200`)",
3888
4170
  "",
3889
- "**Ask me about**",
4171
+ "**Autopilot**",
3890
4172
  "",
3891
- ' "how do epochs and seasons work?"',
3892
- ' "what are the trading rules?"',
3893
- ' "how do I earn $ASTRA?"',
3894
- ' "show the season leaderboard"',
3895
- ' "what is $ASTRA token supply?"',
4173
+ " `/auto on` \u2014 Enable semi-auto mode",
4174
+ " `/auto full` \u2014 Enable full-auto mode",
4175
+ " `/auto off` \u2014 Disable autopilot",
4176
+ " `/auto 5m` \u2014 Set interval (1m-60m)",
4177
+ " `/auto status` \u2014 Show autopilot status",
3896
4178
  "",
3897
4179
  "**System**",
3898
4180
  "",
@@ -3912,8 +4194,8 @@ function App({
3912
4194
  "/trades": "Show my recent trade history.",
3913
4195
  "/board": "Show recent posts from the board.",
3914
4196
  "/wallet": "Check my wallet status \u2014 do I have one set up?",
3915
- "/buy": `Buy ${userText.split(" ").slice(1).join(" ") || "some"} $NOVA.`,
3916
- "/sell": `Sell ${userText.split(" ").slice(1).join(" ") || "some"} $NOVA.`
4197
+ "/buy": `Buy ${parts.slice(1).join(" ") || "some"} $NOVA.`,
4198
+ "/sell": `Sell ${parts.slice(1).join(" ") || "some"} $NOVA.`
3917
4199
  };
3918
4200
  if (shortcuts[cmd]) {
3919
4201
  userText = shortcuts[cmd];
@@ -3928,7 +4210,7 @@ function App({
3928
4210
  }
3929
4211
  setChatMessages((prev) => [
3930
4212
  ...prev,
3931
- { role: "user", content: userText }
4213
+ { role: displayRole, content: userText }
3932
4214
  ]);
3933
4215
  const newCoreMessages = [
3934
4216
  ...coreMessages,
@@ -3946,7 +4228,7 @@ function App({
3946
4228
  rewardsContext,
3947
4229
  onboardingContext,
3948
4230
  apiContext,
3949
- profile,
4231
+ { ...profile, autopilotMode },
3950
4232
  {
3951
4233
  onTextChunk: (chunk) => {
3952
4234
  setStreamingText((prev) => (prev ?? "") + chunk);
@@ -3969,13 +4251,13 @@ function App({
3969
4251
  updatedChat = [
3970
4252
  marker,
3971
4253
  ...recentChat,
3972
- { role: "user", content: userText },
4254
+ { role: displayRole, content: userText },
3973
4255
  { role: "assistant", content: result.text }
3974
4256
  ];
3975
4257
  } else {
3976
4258
  updatedChat = [
3977
4259
  ...chatMessages,
3978
- { role: "user", content: userText },
4260
+ { role: displayRole, content: userText },
3979
4261
  { role: "assistant", content: result.text }
3980
4262
  ];
3981
4263
  }
@@ -4013,8 +4295,11 @@ ${stack}
4013
4295
  setToolName(void 0);
4014
4296
  }
4015
4297
  },
4016
- [coreMessages, chatMessages, skillContext, tradingContext, walletContext, rewardsContext, onboardingContext, apiContext, profile, agentName, sessionId]
4298
+ [coreMessages, chatMessages, skillContext, tradingContext, walletContext, rewardsContext, onboardingContext, apiContext, profile, autopilotMode, agentName, sessionId, memoryContent, addLogEntry]
4017
4299
  );
4300
+ useEffect3(() => {
4301
+ sendMessageRef.current = sendMessage;
4302
+ }, [sendMessage]);
4018
4303
  const handleSubmit = useCallback2(
4019
4304
  (userText) => {
4020
4305
  void sendMessage(userText);
@@ -4026,13 +4311,25 @@ ${stack}
4026
4311
  isLoading && toolName && /* @__PURE__ */ jsx7(Spinner, { label: `Calling ${toolName}...` }),
4027
4312
  isLoading && !toolName && /* @__PURE__ */ jsx7(Spinner, { label: streamingText ? "Thinking..." : void 0 }),
4028
4313
  /* @__PURE__ */ jsx7(Box7, { flexShrink: 0, width: "100%", children: /* @__PURE__ */ jsx7(Input, { isActive: !isLoading, onSubmit: handleSubmit }) }),
4029
- /* @__PURE__ */ jsx7(Box7, { flexShrink: 0, width: "100%", children: /* @__PURE__ */ jsx7(StatusBar_default, { agentName, journeyStage: profile.journeyStage ?? "full" }) }),
4030
- /* @__PURE__ */ jsxs8(Box7, { flexShrink: 0, width: "100%", paddingX: 2, justifyContent: "space-between", children: [
4314
+ /* @__PURE__ */ jsx7(Box7, { flexShrink: 0, width: "100%", children: /* @__PURE__ */ jsx7(
4315
+ StatusBar_default,
4316
+ {
4317
+ agentName,
4318
+ journeyStage: profile.journeyStage ?? "full",
4319
+ autopilotMode,
4320
+ autopilotIntervalMs,
4321
+ onEpochChange: handleEpochChange
4322
+ }
4323
+ ) }),
4324
+ /* @__PURE__ */ jsxs8(Box7, { flexShrink: 0, width: "100%", paddingX: 2, marginTop: 1, justifyContent: "space-between", children: [
4031
4325
  /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "/help \xB7 /portfolio \xB7 /market \xB7 /exit" }),
4032
- /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "Ctrl+C quit" })
4326
+ /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "/auto on\xB7off\xB7set \xB7 Ctrl+C quit" })
4033
4327
  ] })
4034
4328
  ] });
4035
4329
  }
4330
+ function formatTime(d) {
4331
+ return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
4332
+ }
4036
4333
 
4037
4334
  // src/bin/astra.ts
4038
4335
  function detectJourneyStage(params) {
@@ -4150,6 +4447,7 @@ async function main() {
4150
4447
  console.log(" No previous session found. Starting fresh.\n");
4151
4448
  }
4152
4449
  }
4450
+ const initialAutopilotConfig = loadAutopilotConfig();
4153
4451
  const { waitUntilExit } = render(
4154
4452
  React5.createElement(App, {
4155
4453
  agentName,
@@ -4164,6 +4462,7 @@ async function main() {
4164
4462
  memoryContent,
4165
4463
  initialCoreMessages,
4166
4464
  initialChatMessages,
4465
+ initialAutopilotConfig,
4167
4466
  debug
4168
4467
  })
4169
4468
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astranova-live/cli",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Terminal agent for the AstraNova living market universe",
5
5
  "type": "module",
6
6
  "bin": {