@c4t4/heyamigo 0.7.4 → 0.7.5

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.
@@ -70,7 +70,7 @@ export function reloadAsyncSystemPrompt() {
70
70
  }
71
71
  function buildPrompt(task) {
72
72
  const lines = [
73
- `You are a BACKGROUND WORKER. The chat already got its ack ("on it, will report back"). Your output does NOT go to chat by default it routes through markers to memory files, same way the main chat Claude routes things.`,
73
+ `You are a BACKGROUND WORKER doing a delayed chat reply. The chat already got an ack ("on it, will report back"). Now you do the work, and your output IS the follow-up chat reply the full answer the owner is waiting for.`,
74
74
  ``,
75
75
  `TASK:`,
76
76
  task.description,
@@ -80,27 +80,32 @@ function buildPrompt(task) {
80
80
  ``,
81
81
  `Sender: ${task.senderName ?? task.senderNumber}`,
82
82
  ``,
83
- `HOW TO ROUTE YOUR FINDINGS (markers, at the END of your output, one per line):`,
84
- `- [JOURNAL:<slug> <one-line finding>] for each distinct finding that belongs in a journal. ONE marker per finding ten findings = ten markers, not one long paragraph. Only use slugs that already exist (check the Journals list in your preamble), or emit [JOURNAL-NEW:<slug> — <purpose>] first to create one in the same output.`,
85
- `- [JOURNAL-NEW:<slug> <one-line purpose>] to create a new journal when the task clearly needs tracking but no journal covers it yet. Propose the slug yourself, conservatively.`,
83
+ `HOW TO OUTPUT:`,
84
+ `- Write the full answer as a natural chat reply. Same voice, same style as the main chat Claude. What the owner would have gotten if you'd answered inline, just delayed.`,
85
+ `- Open with a short "about the X you asked about..." reference so the owner knows which task this is (they may have asked for several). One sentence, then the content.`,
86
+ `- Concrete findings, no filler. Numbers, names, dates. If you found 10 creators, list them — don't say "multiple creators".`,
87
+ `- If the task failed or hit a wall (login wall, empty page, bot-detection, timeout), say so honestly and briefly. Don't fabricate.`,
88
+ ``,
89
+ `OPTIONAL MARKERS (at the END of your output, same pattern as main chat):`,
90
+ `- [JOURNAL:<slug> — <one-line finding>] for any finding that belongs in an active journal. These run IN ADDITION to your chat reply — they file structured entries in journals/<slug>/entries.jsonl for future reference, dedup, and cross-session memory. Use existing slugs only (check [Journals: active] in your preamble). ONE marker per finding.`,
91
+ `- [JOURNAL-NEW:<slug> — <one-line purpose>] if the task clearly deserves a new journal that doesn't exist yet. Conservative — only when the topic is a recurring tracking surface, not a one-off.`,
86
92
  `- [DIGEST: <one-line reason>] if you learned something durable about the owner or chat that should update the profile/brief.`,
87
93
  ``,
88
94
  `CONSTRAINTS:`,
89
95
  `- Do NOT emit [ASYNC:...]. No recursive delegation.`,
90
- `- Do NOT frame your output as a chat message. No "here's what I found:", no "About the task:". The markers ARE the output.`,
91
- `- Keep any pre-marker text SHORT — one sentence max, or empty. Long pre-marker prose is suppressed and not sent to chat. Put the real content inside markers.`,
92
- `- If the task failed or the tools didn't produce a usable result (login wall, empty page, bot-detection, timeout), output a short clean message (no markers) explaining what happened. That short text IS sent to chat so the owner knows. Do not fabricate findings.`,
93
- `- Stay fully in character.`,
96
+ `- Markers are bonus persistence, not a substitute for the chat reply. Always write the chat reply first.`,
97
+ `- Stay fully in character (personality).`,
98
+ ``,
99
+ `EXAMPLE for an IG scrape of rivoara_official (with journal tracking):`,
100
+ `About the @rivoara_official check: bio is "Premium shower filter for HT aftercare". Last 3 posts: day-5 routine walkthrough, filter-science deep dive, Turkey clinic partnership announcement. Grid is clean, ~200 followers. Pattern: aftercare positioning is the lead, product is secondary.`,
94
101
  ``,
95
- `EXAMPLE for an IG scrape task:`,
96
102
  `[JOURNAL:rivoara-spy — IG bio: "Premium shower filter for HT aftercare"]`,
97
- `[JOURNAL:rivoara-spy — IG post: day-5 routine angle live]`,
98
- `[JOURNAL:rivoara-spy — IG post: Turkey clinic partnership visible in post 3]`,
103
+ `[JOURNAL:rivoara-spy — IG recent posts: day-5 routine, filter science, Turkey clinic partnership]`,
99
104
  ``,
100
105
  `EXAMPLE for a failure:`,
101
- `Instagram hit login wall on @rivoara_official after 2 navigation attempts. No public data accessible. Auth needs refreshing.`,
106
+ `About the @rivoara_official check: Instagram threw a login wall after the first navigation. Can't read the bio or posts without auth. The VNC Chrome session looks expired — worth re-logging.`,
102
107
  ``,
103
- `Do the work now. Then emit your markers.`,
108
+ `Do the work now. Write the reply. Markers optional at the end.`,
104
109
  ];
105
110
  return lines.join('\n');
106
111
  }
@@ -223,35 +228,34 @@ async function runTask(task) {
223
228
  reason: digest,
224
229
  });
225
230
  }
226
- // Decide what to send to chat.
227
- const leftover = clean.trim();
231
+ // The clean (marker-stripped) text IS the chat reply. Always send it when
232
+ // present. Markers fired in parallel above are bonus persistence —
233
+ // journal entries, digests, new journal creation — not a substitute for
234
+ // the chat reply.
235
+ const chatText = clean.trim();
228
236
  const anyMarkerFired = appendedCount > 0 || journalCreates.length > 0 || digest !== null;
229
- let chatText = null;
230
- if (!anyMarkerFired) {
231
- // No markers — fall back to sending the output as a chat message so the
232
- // owner isn't left with silence. Covers both "Claude ignored the marker
233
- // rule" and legitimate "short failure explanation" cases.
234
- chatText = leftover || null;
235
- }
236
- else if (leftover.length > 0 && leftover.length <= 400) {
237
- // Markers fired AND a short pre-marker line — likely an intentional
238
- // failure explanation or completion note. Send it.
239
- chatText = leftover;
237
+ if (chatText.length > 0) {
238
+ await initiate({ jid: task.jid, text: chatText });
240
239
  }
241
- else if (leftover.length > 400) {
242
- // Long pre-marker prose despite markers firing Claude didn't follow
243
- // the routing contract. Suppress the prose; log for inspection.
244
- logger.warn({
245
- id: task.id,
240
+ else if (anyMarkerFired) {
241
+ // Worker emitted only markers, no chat text. That's contract-breaking
242
+ // (chat reply is the primary output) but recoverable send a short
243
+ // completion note so the owner isn't left with silence.
244
+ const bits = [];
245
+ if (appendedCount > 0) {
246
+ bits.push(`${appendedCount} journal ${appendedCount === 1 ? 'entry' : 'entries'}`);
247
+ }
248
+ if (journalCreates.length > 0) {
249
+ bits.push(`${journalCreates.length} journal${journalCreates.length === 1 ? '' : 's'} created`);
250
+ }
251
+ if (digest)
252
+ bits.push('digest scheduled');
253
+ await initiate({
246
254
  jid: task.jid,
247
- chars: leftover.length,
248
- }, 'async task produced long pre-marker prose, suppressing chat send');
249
- }
250
- // Otherwise: markers fired, no leftover — success, silent. Findings live
251
- // in the journal files now.
252
- if (chatText) {
253
- await initiate({ jid: task.jid, text: chatText });
255
+ text: `Done. ${bits.join(', ')}.`,
256
+ });
254
257
  }
258
+ // Else: no chat text AND no markers — worker produced nothing. Log only.
255
259
  logger.info({
256
260
  id: task.id,
257
261
  jid: task.jid,
@@ -259,7 +263,7 @@ async function runTask(task) {
259
263
  appended: appendedCount,
260
264
  createdJournals: journalCreates.length,
261
265
  digestFired: !!digest,
262
- chatSent: chatText ? chatText.length : 0,
266
+ chatSent: chatText.length,
263
267
  }, 'async task completed');
264
268
  }
265
269
  function titleCaseSlug(slug) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4t4/heyamigo",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "WhatsApp AI bot powered by Claude with long-term memory, browser control, and role-based access",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",