simmer-automaton 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/api.d.ts CHANGED
@@ -46,8 +46,46 @@ export interface BriefingPosition {
46
46
  resolves_at: string | null;
47
47
  source: string | null;
48
48
  }
49
+ export interface PositionsPosition {
50
+ market_id: string;
51
+ question: string;
52
+ shares_yes: number;
53
+ shares_no: number;
54
+ current_price: number;
55
+ avg_cost: number;
56
+ pnl: number;
57
+ cost_basis: number;
58
+ status: string;
59
+ resolves_at: string | null;
60
+ venue: string;
61
+ currency: string;
62
+ sources: string[];
63
+ }
64
+ export interface BriefingVenue {
65
+ currency: string;
66
+ balance: number | null;
67
+ pnl: number;
68
+ positions_count: number;
69
+ redeemable_count?: number;
70
+ positions_needing_attention: BriefingPosition[];
71
+ actions: string[];
72
+ by_skill?: Record<string, {
73
+ count: number;
74
+ pnl: number;
75
+ }>;
76
+ }
49
77
  export interface BriefingResponse {
50
- portfolio: {
78
+ venues: {
79
+ simmer: BriefingVenue | null;
80
+ polymarket: BriefingVenue | null;
81
+ kalshi: BriefingVenue | null;
82
+ };
83
+ opportunities: {
84
+ new_markets: Array<Record<string, unknown>>;
85
+ recommended_skills: Array<Record<string, unknown>>;
86
+ };
87
+ risk_alerts: string[];
88
+ performance: {
51
89
  total_pnl: number;
52
90
  pnl_percent: number;
53
91
  win_rate: number;
@@ -55,16 +93,12 @@ export interface BriefingResponse {
55
93
  total_agents: number;
56
94
  settled_pnl: number;
57
95
  };
58
- positions: BriefingPosition[];
59
- recent_trades: Array<{
60
- market_question: string;
61
- action: string;
62
- side: string;
63
- shares: number;
64
- cost: number;
65
- source: string | null;
66
- created_at: string;
67
- }>;
96
+ checked_at: string;
97
+ sdk_update: {
98
+ current: string;
99
+ latest: string;
100
+ message: string;
101
+ } | null;
68
102
  }
69
103
  export interface SkillOutcome {
70
104
  skill_slug: string;
@@ -122,6 +156,9 @@ export declare class SimmerApi {
122
156
  since: string;
123
157
  }>;
124
158
  getBriefing(): Promise<BriefingResponse>;
159
+ getPositions(): Promise<{
160
+ positions: PositionsPosition[];
161
+ }>;
125
162
  postCycle(data: {
126
163
  active_skills: string[];
127
164
  cycle_number: number;
package/dist/api.js CHANGED
@@ -73,6 +73,9 @@ export class SimmerApi {
73
73
  async getBriefing() {
74
74
  return this.request("/api/sdk/briefing");
75
75
  }
76
+ async getPositions() {
77
+ return this.request("/api/sdk/positions?status=active");
78
+ }
76
79
  async postCycle(data) {
77
80
  return this.request("/api/sdk/automaton/cycle", {
78
81
  method: "POST",
package/dist/index.js CHANGED
@@ -85,8 +85,7 @@ async function refreshState(logger) {
85
85
  if (cachedState.venue) {
86
86
  config.venue = cachedState.venue;
87
87
  }
88
- // Compute tier (totalPnl = 0 for now, will be enriched when P&L tracking is added)
89
- currentTier = computeTier(cachedState, 0);
88
+ currentTier = computeTier(cachedState, cachedPortfolio?.totalPnl ?? 0);
90
89
  // Sync banditState from fetched skills — preserve memory for existing, seed new ones
91
90
  const existingBySlug = new Map(banditState.map((s) => [s.slug, s]));
92
91
  banditState = cachedSkills.map((skill) => {
@@ -178,15 +177,15 @@ function buildPromptContext() {
178
177
  lines.push("");
179
178
  lines.push(`**Portfolio:** ${cachedPortfolio.positionCount} positions | P&L: ${fmtCurrency(cachedPortfolio.totalPnl)} | Recent trades: ${cachedPortfolio.recentTradeCount}`);
180
179
  if (cachedPortfolio.positions.length > 0) {
181
- // Show top 3 by absolute PnL
182
- const sorted = [...cachedPortfolio.positions].sort((a, b) => Math.abs(b.pnl) - Math.abs(a.pnl));
183
- const top = sorted.slice(0, 3);
180
+ // Show positions needing attention (significant moves or nearing expiry)
181
+ lines.push(" Needing attention:");
182
+ const top = cachedPortfolio.positions.slice(0, 3);
184
183
  for (const p of top) {
185
184
  const pnlStr = p.pnl >= 0 ? `+${fmtCurrency(p.pnl)}` : fmtCurrency(p.pnl);
186
185
  lines.push(` - ${p.question.slice(0, 60)} | ${p.side} ${p.shares} shares @ ${p.avg_entry.toFixed(2)} → ${p.current_price.toFixed(2)} | ${pnlStr}`);
187
186
  }
188
- if (sorted.length > 3) {
189
- lines.push(` - ...and ${sorted.length - 3} more (use /simmer portfolio for full list)`);
187
+ if (cachedPortfolio.positions.length > 3) {
188
+ lines.push(` - ...and ${cachedPortfolio.positions.length - 3} more needing attention`);
190
189
  }
191
190
  }
192
191
  }
@@ -425,12 +424,13 @@ export default function register(pluginApi) {
425
424
  // Fetch portfolio snapshot for prompt context
426
425
  try {
427
426
  const briefing = await api.getBriefing();
428
- const positions = briefing.positions || [];
427
+ const venue = briefing.venues?.simmer;
428
+ const attentionPositions = venue?.positions_needing_attention || [];
429
429
  cachedPortfolio = {
430
- totalPnl: briefing.portfolio?.total_pnl ?? 0,
431
- positionCount: positions.length,
432
- positions,
433
- recentTradeCount: briefing.recent_trades?.length ?? 0,
430
+ totalPnl: briefing.performance?.total_pnl ?? 0,
431
+ positionCount: venue?.positions_count ?? 0,
432
+ positions: attentionPositions,
433
+ recentTradeCount: 0,
434
434
  };
435
435
  }
436
436
  catch (e) {
@@ -623,14 +623,17 @@ export default function register(pluginApi) {
623
623
  }
624
624
  if (subcommand === "portfolio") {
625
625
  try {
626
- const briefing = await api.getBriefing();
627
- const positions = briefing.positions || [];
626
+ const res = await api.getPositions();
627
+ const positions = res.positions || [];
628
628
  if (positions.length === 0) {
629
629
  return { text: "No open positions." };
630
630
  }
631
631
  const lines = positions.map((p) => {
632
+ const shares = p.shares_yes + p.shares_no;
633
+ const side = p.shares_yes > 0 && p.shares_no > 0 ? "both" : (p.shares_yes > 0 ? "yes" : "no");
632
634
  const pnlStr = p.pnl >= 0 ? `+${fmtCurrency(p.pnl)}` : fmtCurrency(p.pnl);
633
- return `${p.question.slice(0, 55)} | ${p.side} ${p.shares}sh @ ${p.avg_entry.toFixed(2)} → ${p.current_price.toFixed(2)} | ${pnlStr}${p.source ? ` [${p.source}]` : ""}`;
635
+ const src = p.sources?.length ? ` [${p.sources[0]}]` : "";
636
+ return `${p.question.slice(0, 55)} | ${side} ${shares.toFixed(1)}sh @ ${p.avg_cost.toFixed(2)} → ${p.current_price.toFixed(2)} | ${pnlStr}${src}`;
634
637
  });
635
638
  const totalPnl = positions.reduce((sum, p) => sum + p.pnl, 0);
636
639
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simmer-automaton",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
4
4
  "description": "Simmer Automaton plugin for OpenClaw — autonomous trading skill orchestration",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/api.ts CHANGED
@@ -51,8 +51,45 @@ export interface BriefingPosition {
51
51
  source: string | null;
52
52
  }
53
53
 
54
+ export interface PositionsPosition {
55
+ market_id: string;
56
+ question: string;
57
+ shares_yes: number;
58
+ shares_no: number;
59
+ current_price: number;
60
+ avg_cost: number;
61
+ pnl: number;
62
+ cost_basis: number;
63
+ status: string;
64
+ resolves_at: string | null;
65
+ venue: string;
66
+ currency: string;
67
+ sources: string[];
68
+ }
69
+
70
+ export interface BriefingVenue {
71
+ currency: string;
72
+ balance: number | null;
73
+ pnl: number;
74
+ positions_count: number;
75
+ redeemable_count?: number;
76
+ positions_needing_attention: BriefingPosition[];
77
+ actions: string[];
78
+ by_skill?: Record<string, { count: number; pnl: number }>;
79
+ }
80
+
54
81
  export interface BriefingResponse {
55
- portfolio: {
82
+ venues: {
83
+ simmer: BriefingVenue | null;
84
+ polymarket: BriefingVenue | null;
85
+ kalshi: BriefingVenue | null;
86
+ };
87
+ opportunities: {
88
+ new_markets: Array<Record<string, unknown>>;
89
+ recommended_skills: Array<Record<string, unknown>>;
90
+ };
91
+ risk_alerts: string[];
92
+ performance: {
56
93
  total_pnl: number;
57
94
  pnl_percent: number;
58
95
  win_rate: number;
@@ -60,16 +97,8 @@ export interface BriefingResponse {
60
97
  total_agents: number;
61
98
  settled_pnl: number;
62
99
  };
63
- positions: BriefingPosition[];
64
- recent_trades: Array<{
65
- market_question: string;
66
- action: string;
67
- side: string;
68
- shares: number;
69
- cost: number;
70
- source: string | null;
71
- created_at: string;
72
- }>;
100
+ checked_at: string;
101
+ sdk_update: { current: string; latest: string; message: string } | null;
73
102
  }
74
103
 
75
104
  export interface SkillOutcome {
@@ -176,6 +205,10 @@ export class SimmerApi {
176
205
  return this.request("/api/sdk/briefing");
177
206
  }
178
207
 
208
+ async getPositions(): Promise<{ positions: PositionsPosition[] }> {
209
+ return this.request("/api/sdk/positions?status=active");
210
+ }
211
+
179
212
  async postCycle(data: {
180
213
  active_skills: string[];
181
214
  cycle_number: number;
package/src/index.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { SimmerApi } from "./api.js";
11
- import type { AutomatonState, Skill, SkillOutcome, BriefingPosition } from "./api.js";
11
+ import type { AutomatonState, Skill, SkillOutcome, BriefingPosition, PositionsPosition } from "./api.js";
12
12
  import { selectSkills, tierMaxSkills, type SkillState } from "./bandit.js";
13
13
  import { computeTier, type Tier } from "./tiers.js";
14
14
  import { generateTuningHints, computeTuningChanges, type ConfigChange } from "./tuning.js";
@@ -117,8 +117,7 @@ async function refreshState(logger: { info: (m: string) => void; error: (m: stri
117
117
  if (cachedState.venue) {
118
118
  config.venue = cachedState.venue;
119
119
  }
120
- // Compute tier (totalPnl = 0 for now, will be enriched when P&L tracking is added)
121
- currentTier = computeTier(cachedState, 0);
120
+ currentTier = computeTier(cachedState, cachedPortfolio?.totalPnl ?? 0);
122
121
 
123
122
  // Sync banditState from fetched skills — preserve memory for existing, seed new ones
124
123
  const existingBySlug = new Map(banditState.map((s) => [s.slug, s]));
@@ -219,15 +218,15 @@ function buildPromptContext(): string {
219
218
  lines.push("");
220
219
  lines.push(`**Portfolio:** ${cachedPortfolio.positionCount} positions | P&L: ${fmtCurrency(cachedPortfolio.totalPnl)} | Recent trades: ${cachedPortfolio.recentTradeCount}`);
221
220
  if (cachedPortfolio.positions.length > 0) {
222
- // Show top 3 by absolute PnL
223
- const sorted = [...cachedPortfolio.positions].sort((a, b) => Math.abs(b.pnl) - Math.abs(a.pnl));
224
- const top = sorted.slice(0, 3);
221
+ // Show positions needing attention (significant moves or nearing expiry)
222
+ lines.push(" Needing attention:");
223
+ const top = cachedPortfolio.positions.slice(0, 3);
225
224
  for (const p of top) {
226
225
  const pnlStr = p.pnl >= 0 ? `+${fmtCurrency(p.pnl)}` : fmtCurrency(p.pnl);
227
226
  lines.push(` - ${p.question.slice(0, 60)} | ${p.side} ${p.shares} shares @ ${p.avg_entry.toFixed(2)} → ${p.current_price.toFixed(2)} | ${pnlStr}`);
228
227
  }
229
- if (sorted.length > 3) {
230
- lines.push(` - ...and ${sorted.length - 3} more (use /simmer portfolio for full list)`);
228
+ if (cachedPortfolio.positions.length > 3) {
229
+ lines.push(` - ...and ${cachedPortfolio.positions.length - 3} more needing attention`);
231
230
  }
232
231
  }
233
232
  }
@@ -497,12 +496,13 @@ export default function register(pluginApi: PluginApi) {
497
496
  // Fetch portfolio snapshot for prompt context
498
497
  try {
499
498
  const briefing = await api.getBriefing();
500
- const positions = briefing.positions || [];
499
+ const venue = briefing.venues?.simmer;
500
+ const attentionPositions = venue?.positions_needing_attention || [];
501
501
  cachedPortfolio = {
502
- totalPnl: briefing.portfolio?.total_pnl ?? 0,
503
- positionCount: positions.length,
504
- positions,
505
- recentTradeCount: briefing.recent_trades?.length ?? 0,
502
+ totalPnl: briefing.performance?.total_pnl ?? 0,
503
+ positionCount: venue?.positions_count ?? 0,
504
+ positions: attentionPositions,
505
+ recentTradeCount: 0,
506
506
  };
507
507
  } catch (e) {
508
508
  ctx.logger.warn(`[simmer] Failed to fetch briefing: ${e}`);
@@ -703,16 +703,19 @@ export default function register(pluginApi: PluginApi) {
703
703
 
704
704
  if (subcommand === "portfolio") {
705
705
  try {
706
- const briefing = await api.getBriefing();
707
- const positions = briefing.positions || [];
706
+ const res = await api.getPositions();
707
+ const positions = res.positions || [];
708
708
  if (positions.length === 0) {
709
709
  return { text: "No open positions." };
710
710
  }
711
- const lines = positions.map((p: BriefingPosition) => {
711
+ const lines = positions.map((p: PositionsPosition) => {
712
+ const shares = p.shares_yes + p.shares_no;
713
+ const side = p.shares_yes > 0 && p.shares_no > 0 ? "both" : (p.shares_yes > 0 ? "yes" : "no");
712
714
  const pnlStr = p.pnl >= 0 ? `+${fmtCurrency(p.pnl)}` : fmtCurrency(p.pnl);
713
- return `${p.question.slice(0, 55)} | ${p.side} ${p.shares}sh @ ${p.avg_entry.toFixed(2)} → ${p.current_price.toFixed(2)} | ${pnlStr}${p.source ? ` [${p.source}]` : ""}`;
715
+ const src = p.sources?.length ? ` [${p.sources[0]}]` : "";
716
+ return `${p.question.slice(0, 55)} | ${side} ${shares.toFixed(1)}sh @ ${p.avg_cost.toFixed(2)} → ${p.current_price.toFixed(2)} | ${pnlStr}${src}`;
714
717
  });
715
- const totalPnl = positions.reduce((sum: number, p: BriefingPosition) => sum + p.pnl, 0);
718
+ const totalPnl = positions.reduce((sum: number, p: PositionsPosition) => sum + p.pnl, 0);
716
719
  return {
717
720
  text: `Positions (${positions.length}) | Total P&L: ${fmtCurrency(totalPnl)}\n\n${lines.join("\n")}`,
718
721
  };