Skip to content

Commit 0eb0ac9

Browse files
committed
added feature for user to ask about up to the past 10 alerts
1 parent 5d49403 commit 0eb0ac9

4 files changed

Lines changed: 151 additions & 63 deletions

File tree

MESSAGING.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,20 @@ preferences" reply). Examples:
157157
- `What does this mean for rates?`
158158
- `Summarize the whole report`
159159
- `Break this down for me`
160+
- `Why is the Waller news good for equities?`
160161

161-
This works for about **30 minutes** after the alert. The agent recognizes
162-
follow-ups by question phrasing (messages ending in `?`, or starting with
163-
why/what/how/explain/summarize, etc.). If you instead want to change settings,
164-
phrase it as a preference (e.g. "alert me on CPI") and it'll be saved rather
165-
than answered.
162+
The agent keeps up to **10 recent alerts** per chat, each for **30 minutes**
163+
after it was sent. The agent recognizes follow-ups by question phrasing (messages
164+
ending in `?`, or starting with why/what/how/explain/summarize, etc.).
165+
166+
**Multiple alerts in the window:** If several alerts are active, you don't need
167+
to do anything special. Just ask naturally — the agent passes all recent alerts
168+
to the AI and it works out which one your question is about from the wording. If
169+
your question is ambiguous, the AI picks the most relevant alert and briefly says
170+
which one it chose.
171+
172+
If you instead want to change settings, phrase it as a preference (e.g. "alert
173+
me on CPI") and it'll be saved rather than answered.
166174

167175
---
168176

@@ -178,5 +186,5 @@ than answered.
178186
| Get fewer / only big alerts | `threshold 0.8` |
179187
| Get more alerts | `threshold 0.3` |
180188
| Only trust reputable sources | `only alert me from reputable sources` |
181-
| Ask about the last alert | `why is this hawkish?` |
189+
| Ask about a recent alert | `why is this hawkish?`, `what does the Waller news mean?` |
182190
| See what's saved | send any preference message; the reply echoes it back |

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ npm start
135135
`npm start` runs one process that:
136136

137137
1. **Reactive (iMessage)**: listens on Spectrum `app.messages`, extracts preferences with Grok, and replies.
138-
2. **Follow-ups**: after a proactive alert, answer questions about that headline (e.g. “Why is this hawkish?”) via threaded `message.reply()`.
138+
2. **Follow-ups**: after one or more proactive alerts, answer questions about any recent alert (up to 10, within 30 minutes each). The agent uses the LLM to infer which alert the question is about from the wording, so users just ask naturally — no thread-reply gymnastics required.
139139
3. **Proactive (ZeroMQ)**: subscribes to `cpp_engine` headlines in the background, analyzes each with Grok, and pushes iMessage alerts when a user’s preferences match.
140140

141141
Outbound messages to the same chat are ordered so preference confirmations are not interrupted by a concurrent alert (see `TECHNICAL.md`).
@@ -232,17 +232,17 @@ Troubleshooting (alerts):
232232

233233
### What to test (alert follow-ups)
234234

235-
After you receive a proactive macro alert:
235+
After you receive one or more proactive macro alerts:
236236

237-
1. Reply in the same chat with a question about that alert, e.g. `Why is this hawkish?` or `Summarize the whole report`.
238-
2. You should get a threaded analysis reply (not a “Got it — saved preferences” message).
239-
3. Follow-ups work for ~30 minutes after the alert; send a new preference message (e.g. “alert me on CPI”) if you want to change settings instead.
237+
1. Reply in the same chat with a question, e.g. `Why is this hawkish?` or `What does the Waller news mean for rate cuts?`
238+
2. You should get a threaded analysis reply (not a "Got it — saved preferences" message).
239+
3. If several alerts are active, the agent passes all of them to the AI which picks the most relevant one based on your question — you don't need to do anything special to reference a specific alert.
240+
4. Each alert stays in context for **30 minutes** after it was sent; up to 10 alerts are tracked per chat. Send a new preference message (e.g. "alert me on CPI") if you want to change settings instead.
240241

241242
Troubleshooting (follow-ups):
242243

243-
- Got a preferences reply instead of analysis: phrase your message as a question, or include words like “why”, “summarize”, or “hawkish”. Messages like “alert me on CPI” are treated as preference updates.
244-
- “I don't have a recent alert in context”: no alert was stored for this chat yet, or the 30-minute context window expired.
245-
244+
- Got a preferences reply instead of analysis: phrase your message as a question, or include words like "why", "summarize", or "hawkish". Messages like "alert me on CPI" are treated as preference updates.
245+
- No alerts active yet: wait for an alert to arrive (or inject one with `npm run pub`), then ask your question within the 30-minute window.
246246
### Debug-only: standalone ZeroMQ subscriber
247247

248248
`npm run sub` runs `subscriber.ts` alone — it only logs headlines that pass the hardcoded C++ macro keyword filter. Use it to verify ZMQ wiring without Spectrum or Grok:

TECHNICAL.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,20 +138,20 @@ When a user sends a text message, `ts_agent` extracts macro trading preferences
138138
- Key: `space.id` (conversation id)
139139
- Value: `{ trackedKeywords: string[], watchlist: string[], severityThreshold: number, sourceTrustThreshold: number }`
140140
- `spacesById: Map<string, Space>` — caches the Spectrum `space` handle for proactive outbound sends (populated on each inbound message)
141-
- `lastAlertBySpace: Map<string, AlertContext>`headline + Grok analysis for conversational follow-ups (~30 minute TTL)
141+
- `alertHistoryBySpace: Map<string, AlertContext[]>`ordered newest-first list of up to 10 recent alert contexts per space, each with a 30-minute TTL. Used for conversational follow-ups. Each `AlertContext` includes the headline, source, Grok analysis, send timestamp, and the `OutboundMessage.id` of the alert we sent (used for best-effort thread-reply matching on platforms that expose it).
142142

143143
This is intentionally **ephemeral** (memory-only). Persisting to a DB can be added later once the preference schema stabilizes.
144144

145145
### Alert follow-ups
146146

147-
After a proactive alert is sent, the agent stores `AlertContext` for that `space.id`. On later inbound text:
147+
After a proactive alert is sent, the agent appends `AlertContext` to a per-space history (`alertHistoryBySpace`). On later inbound text:
148148

149-
1. If there is recent alert context and the message looks like a **follow-up question** (e.g. ends with `?`, starts with “why”/“summarize”, mentions hawkish/dovish), it is **not** treated as a preference update.
150-
2. Grok receives the stored headline/analysis plus the user’s question.
151-
3. The response is sent with `message.reply()` so it threads in iMessage.
152-
153-
Preference-shaped messages (e.g. “alert me on CPI”, “threshold 0.5”) still go through preference extraction even if alert context exists.
149+
1. **Follow-up detection**: if the message looks like a question (ends with `?`, starts with why/summarize/explain/what/how, mentions hawkish/dovish, etc.) **and** at least one unexpired alert is in the history, it is routed as a follow-up rather than a preference update.
150+
2. **Multi-alert resolution**: all unexpired alert contexts (up to 10, each within 30 minutes of being sent) are passed to Grok together. The model identifies which alert the question is most relevant to and answers about it. For an unambiguous question it answers directly; for an ambiguous one it briefly notes which alert it chose.
151+
3. **Thread-reply matching (best-effort)**: if the platform delivers the message as a Spectrum `content.type === "reply"` pointing to one of our sent alert message IDs, only that specific alert context is passed — no multi-alert resolution needed. Note: the iMessage provider does **not** expose thread-originator GUIDs on plain inbound text messages at the Spectrum level, so long-press → Reply in iMessage arrives as a plain text message and takes the multi-alert resolution path above. The thread-reply path is preserved for platforms that do expose this.
152+
4. The response is sent with `message.reply()` so it threads in iMessage.
154153

154+
Preference-shaped messages (e.g. "alert me on CPI", "threshold 0.5") still go through preference extraction even if alert history exists.
155155
### LLM preference extraction
156156

157157
Preferences are extracted by calling an **OpenAI-compatible** chat completions endpoint.
@@ -212,7 +212,7 @@ User preferences (tracked keywords + watchlist tickers) live only in the agent,
212212
1. Parse JSON frame (including the `source` publisher); dedupe by `ts` + headline (or headline alone) for ~2 minutes.
213213
2. Call Grok (`analyzeHeadlineWithLlm`, passing the headline + source) → `{ sentiment, severity, summary, sourceTrust }`. `sourceTrust` (0–1) is Grok's credibility rating of the publisher; an absent/garbled rating defaults to ~0.3 (low).
214214
3. For each `userPreferences` entry, enqueue `spaceOutbound.run(spaceId, "alert", …)` (waits if that space has an active alert hold).
215-
4. Inside the alert task, re-check keywords, `severity >= severityThreshold`, **and** `sourceTrust >= sourceTrustThreshold` against current preferences, then `app.send(space, alertText)` and store `lastAlertBySpace`. The alert text and stored context include the source name and its `low`/`medium`/`high` trust label. (`severityThreshold` is the user-facing “threshold”; it gates on market impact, not bullish/bearish sentiment.)
215+
4. Inside the alert task, re-check keywords, `severity >= severityThreshold`, **and** `sourceTrust >= sourceTrustThreshold` against current preferences, then `app.send(space, alertText)` and append the result to `alertHistoryBySpace` (along with the `OutboundMessage.id` for best-effort thread-reply matching). The alert text and stored context include the source name and its `low`/`medium`/`high` trust label. (`severityThreshold` is the user-facing “threshold”; it gates on market impact, not bullish/bearish sentiment.)
216216

217217
If no user has messaged since startup, preferences are empty and headlines are logged but not alerted. If preferences exist but `spacesById` lacks that `space.id`, the agent logs that the user must message first (Spectrum needs a cached conversation handle for outbound iMessage).
218218

0 commit comments

Comments
 (0)