@spfunctions/cli 1.1.7 → 1.1.9
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/client.d.ts +1 -0
- package/dist/client.js +3 -0
- package/dist/commands/agent.js +89 -3
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare class SFClient {
|
|
|
18
18
|
injectSignal(id: string, type: string, content: string, source?: string): Promise<any>;
|
|
19
19
|
evaluate(id: string): Promise<any>;
|
|
20
20
|
getFeed(hours?: number, limit?: number): Promise<any>;
|
|
21
|
+
getChanges(id: string, since: string): Promise<any>;
|
|
21
22
|
updateThesis(id: string, data: Record<string, unknown>): Promise<any>;
|
|
22
23
|
publish(id: string, slug: string, description?: string): Promise<any>;
|
|
23
24
|
unpublish(id: string): Promise<any>;
|
package/dist/client.js
CHANGED
|
@@ -65,6 +65,9 @@ class SFClient {
|
|
|
65
65
|
async getFeed(hours = 24, limit = 200) {
|
|
66
66
|
return this.request('GET', `/api/feed?hours=${hours}&limit=${limit}`);
|
|
67
67
|
}
|
|
68
|
+
async getChanges(id, since) {
|
|
69
|
+
return this.request('GET', `/api/thesis/${id}/changes?since=${encodeURIComponent(since)}`);
|
|
70
|
+
}
|
|
68
71
|
async updateThesis(id, data) {
|
|
69
72
|
return this.request('PATCH', `/api/thesis/${id}`, data);
|
|
70
73
|
}
|
package/dist/commands/agent.js
CHANGED
|
@@ -433,6 +433,13 @@ async function agentCommand(thesisId, opts) {
|
|
|
433
433
|
let isProcessing = false;
|
|
434
434
|
// Cache for positions (fetched by /pos or get_positions tool)
|
|
435
435
|
let cachedPositions = null;
|
|
436
|
+
// ── Heartbeat polling state ───────────────────────────────────────────────
|
|
437
|
+
// Background poll delta endpoint every 60s.
|
|
438
|
+
// If confidence changed ≥ 3%, auto-trigger agent analysis.
|
|
439
|
+
// If agent is busy (isProcessing), queue and deliver after agent finishes.
|
|
440
|
+
let lastPollTimestamp = new Date().toISOString();
|
|
441
|
+
let pendingHeartbeatDelta = null; // queued delta when agent is busy
|
|
442
|
+
let heartbeatPollTimer = null;
|
|
436
443
|
// ── Inline confirmation mechanism ─────────────────────────────────────────
|
|
437
444
|
// Tools can call promptUser() during execution to ask the user a question.
|
|
438
445
|
// This temporarily unlocks the editor, waits for input, then resumes.
|
|
@@ -710,8 +717,18 @@ async function agentCommand(thesisId, opts) {
|
|
|
710
717
|
}
|
|
711
718
|
}
|
|
712
719
|
cachedPositions = positions;
|
|
720
|
+
const formatted = positions.map((p) => ({
|
|
721
|
+
ticker: p.ticker,
|
|
722
|
+
side: p.side,
|
|
723
|
+
quantity: p.quantity,
|
|
724
|
+
avg_price: `${p.average_price_paid}¢`,
|
|
725
|
+
current_price: `${p.current_value}¢`,
|
|
726
|
+
unrealized_pnl: `$${(p.unrealized_pnl / 100).toFixed(2)}`,
|
|
727
|
+
total_cost: `$${(p.total_cost / 100).toFixed(2)}`,
|
|
728
|
+
realized_pnl: `$${(p.realized_pnl / 100).toFixed(2)}`,
|
|
729
|
+
}));
|
|
713
730
|
return {
|
|
714
|
-
content: [{ type: 'text', text: JSON.stringify(
|
|
731
|
+
content: [{ type: 'text', text: JSON.stringify(formatted, null, 2) }],
|
|
715
732
|
details: {},
|
|
716
733
|
};
|
|
717
734
|
},
|
|
@@ -1188,6 +1205,7 @@ Short-term markets (weekly/monthly contracts) settle into hard data that calibra
|
|
|
1188
1205
|
- If a causal tree node probability seriously contradicts the market price, point it out.
|
|
1189
1206
|
- Use Chinese if the user writes in Chinese, English if they write in English.
|
|
1190
1207
|
- For any question about prices, positions, or P&L, ALWAYS call a tool to get fresh data first. Never answer price-related questions using the cached data in this system prompt.
|
|
1208
|
+
- Prices are in cents (e.g. 35¢). P&L, cost, and balance are in dollars (e.g. $90.66). Tool outputs are pre-formatted with units — do not re-convert.
|
|
1191
1209
|
- Align tables. Be precise with numbers to the cent.
|
|
1192
1210
|
|
|
1193
1211
|
## Strategy rules
|
|
@@ -1328,6 +1346,12 @@ ${ctx.lastEvaluation?.summary ? `Latest evaluation summary: ${ctx.lastEvaluation
|
|
|
1328
1346
|
isProcessing = false;
|
|
1329
1347
|
persistSession();
|
|
1330
1348
|
flushRender();
|
|
1349
|
+
// Deliver queued heartbeat notification if any
|
|
1350
|
+
if (pendingHeartbeatDelta) {
|
|
1351
|
+
const delta = pendingHeartbeatDelta;
|
|
1352
|
+
pendingHeartbeatDelta = null;
|
|
1353
|
+
handleHeartbeatDelta(delta);
|
|
1354
|
+
}
|
|
1331
1355
|
}
|
|
1332
1356
|
if (event.type === 'tool_execution_start') {
|
|
1333
1357
|
const toolLine = new MutableLine(C.zinc600(` \u26A1 ${event.toolName}...`));
|
|
@@ -1865,6 +1889,8 @@ Output a structured summary. Be concise but preserve every important detail —
|
|
|
1865
1889
|
};
|
|
1866
1890
|
// ── Ctrl+C handler ─────────────────────────────────────────────────────────
|
|
1867
1891
|
function cleanup() {
|
|
1892
|
+
if (heartbeatPollTimer)
|
|
1893
|
+
clearInterval(heartbeatPollTimer);
|
|
1868
1894
|
if (currentLoader)
|
|
1869
1895
|
currentLoader.stop();
|
|
1870
1896
|
persistSession();
|
|
@@ -1949,6 +1975,59 @@ Output a structured summary. Be concise but preserve every important detail —
|
|
|
1949
1975
|
addSystemText(buildWelcomeDashboard(latestContext, initialPositions));
|
|
1950
1976
|
addSystemText(' ' + sessionStatus);
|
|
1951
1977
|
addSpacer();
|
|
1978
|
+
// ── Heartbeat delta handler ───────────────────────────────────────────────
|
|
1979
|
+
const HEARTBEAT_CONFIDENCE_THRESHOLD = 0.03; // 3%
|
|
1980
|
+
function handleHeartbeatDelta(delta) {
|
|
1981
|
+
const absDelta = Math.abs(delta.confidenceDelta || 0);
|
|
1982
|
+
const confPct = Math.round((delta.confidence || 0) * 100);
|
|
1983
|
+
const deltaPct = Math.round((delta.confidenceDelta || 0) * 100);
|
|
1984
|
+
const sign = deltaPct > 0 ? '+' : '';
|
|
1985
|
+
if (absDelta >= HEARTBEAT_CONFIDENCE_THRESHOLD) {
|
|
1986
|
+
// Big change → auto-trigger agent analysis
|
|
1987
|
+
const arrow = deltaPct > 0 ? '\u25B2' : '\u25BC';
|
|
1988
|
+
const color = deltaPct > 0 ? C.emerald : C.red;
|
|
1989
|
+
addSystemText(color(` ${arrow} Heartbeat: confidence ${sign}${deltaPct}% → ${confPct}%`));
|
|
1990
|
+
if (delta.latestSummary) {
|
|
1991
|
+
addSystemText(C.zinc400(` ${delta.latestSummary.slice(0, 100)}`));
|
|
1992
|
+
}
|
|
1993
|
+
addSpacer();
|
|
1994
|
+
// Update header
|
|
1995
|
+
headerBar.setFromContext({ ...latestContext, confidence: delta.confidence, lastEvaluation: { confidenceDelta: delta.confidenceDelta } }, initialPositions || undefined);
|
|
1996
|
+
tui.requestRender();
|
|
1997
|
+
// Auto-trigger agent
|
|
1998
|
+
isProcessing = true;
|
|
1999
|
+
const prompt = `[HEARTBEAT ALERT] Confidence just changed ${sign}${deltaPct}% to ${confPct}%. ${delta.evaluationCount} evaluation(s) since last check. Latest: "${(delta.latestSummary || '').slice(0, 150)}". Briefly analyze what happened and whether any action is needed. Be concise.`;
|
|
2000
|
+
agent.prompt(prompt).catch((err) => {
|
|
2001
|
+
addSystemText(C.red(`Error: ${err.message}`));
|
|
2002
|
+
isProcessing = false;
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
else if (absDelta > 0) {
|
|
2006
|
+
// Small change → silent notification line only
|
|
2007
|
+
addSystemText(C.zinc600(` \u2500 heartbeat: ${confPct}% (${sign}${deltaPct}%) \u2014 ${delta.evaluationCount || 0} eval(s)`));
|
|
2008
|
+
tui.requestRender();
|
|
2009
|
+
}
|
|
2010
|
+
// absDelta === 0: truly nothing changed, stay silent
|
|
2011
|
+
}
|
|
2012
|
+
// ── Start heartbeat polling ───────────────────────────────────────────────
|
|
2013
|
+
heartbeatPollTimer = setInterval(async () => {
|
|
2014
|
+
try {
|
|
2015
|
+
const delta = await sfClient.getChanges(resolvedThesisId, lastPollTimestamp);
|
|
2016
|
+
lastPollTimestamp = new Date().toISOString();
|
|
2017
|
+
if (!delta.changed)
|
|
2018
|
+
return;
|
|
2019
|
+
if (isProcessing || pendingPrompt) {
|
|
2020
|
+
// Agent is busy — queue for delivery after agent_end
|
|
2021
|
+
pendingHeartbeatDelta = delta;
|
|
2022
|
+
}
|
|
2023
|
+
else {
|
|
2024
|
+
handleHeartbeatDelta(delta);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
catch {
|
|
2028
|
+
// Silent — don't spam errors from background polling
|
|
2029
|
+
}
|
|
2030
|
+
}, 60_000); // every 60 seconds
|
|
1952
2031
|
// ── Start TUI ──────────────────────────────────────────────────────────────
|
|
1953
2032
|
tui.start();
|
|
1954
2033
|
}
|
|
@@ -2069,7 +2148,14 @@ async function runPlainTextAgent(params) {
|
|
|
2069
2148
|
pos.unrealized_pnl = Math.round((livePrice - pos.average_price_paid) * pos.quantity);
|
|
2070
2149
|
}
|
|
2071
2150
|
}
|
|
2072
|
-
|
|
2151
|
+
const formatted = positions.map((p) => ({
|
|
2152
|
+
ticker: p.ticker, side: p.side, quantity: p.quantity,
|
|
2153
|
+
avg_price: `${p.average_price_paid}¢`, current_price: `${p.current_value}¢`,
|
|
2154
|
+
unrealized_pnl: `$${(p.unrealized_pnl / 100).toFixed(2)}`,
|
|
2155
|
+
total_cost: `$${(p.total_cost / 100).toFixed(2)}`,
|
|
2156
|
+
realized_pnl: `$${(p.realized_pnl / 100).toFixed(2)}`,
|
|
2157
|
+
}));
|
|
2158
|
+
return { content: [{ type: 'text', text: JSON.stringify(formatted, null, 2) }], details: {} };
|
|
2073
2159
|
},
|
|
2074
2160
|
},
|
|
2075
2161
|
{
|
|
@@ -2241,7 +2327,7 @@ ${edgesSummary}
|
|
|
2241
2327
|
|
|
2242
2328
|
${ctx.lastEvaluation?.summary ? `Latest evaluation: ${ctx.lastEvaluation.summary.slice(0, 300)}` : ''}
|
|
2243
2329
|
|
|
2244
|
-
Rules: Be concise. Use tools when needed. Don't ask "anything else?".`;
|
|
2330
|
+
Rules: Be concise. Use tools when needed. Don't ask "anything else?". Prices are in cents (e.g. 35¢). P&L, cost, and balance are in dollars (e.g. $90.66). Tool outputs are pre-formatted with units — do not re-convert.`;
|
|
2245
2331
|
// ── Create agent ──────────────────────────────────────────────────────────
|
|
2246
2332
|
const agent = new Agent({
|
|
2247
2333
|
initialState: { systemPrompt, model, tools, thinkingLevel: 'off' },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spfunctions/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.9",
|
|
4
4
|
"description": "Prediction market intelligence CLI. Causal thesis model, 24/7 Kalshi/Polymarket scan, live orderbook, edge detection. Interactive agent mode with tool calling.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sf": "./dist/index.js"
|