@rubytech/taskmaster 1.16.3 → 1.17.0

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 (45) hide show
  1. package/dist/agents/tools/logs-read-tool.js +9 -0
  2. package/dist/agents/tools/memory-tool.js +1 -0
  3. package/dist/auto-reply/group-activation.js +2 -0
  4. package/dist/auto-reply/reply/commands-session.js +28 -11
  5. package/dist/build-info.json +3 -3
  6. package/dist/config/group-policy.js +16 -0
  7. package/dist/config/zod-schema.providers-whatsapp.js +2 -0
  8. package/dist/control-ui/assets/{index-Bd75cI7J.js → index-Beuhzjy_.js} +525 -492
  9. package/dist/control-ui/assets/index-Beuhzjy_.js.map +1 -0
  10. package/dist/control-ui/assets/index-XqRo9tNW.css +1 -0
  11. package/dist/control-ui/index.html +2 -2
  12. package/dist/cron/preloaded.js +27 -23
  13. package/dist/gateway/protocol/index.js +7 -2
  14. package/dist/gateway/protocol/schema/logs-chat.js +6 -0
  15. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -0
  16. package/dist/gateway/protocol/schema/sessions-transcript.js +1 -0
  17. package/dist/gateway/protocol/schema/sessions.js +6 -1
  18. package/dist/gateway/protocol/schema/whatsapp.js +24 -0
  19. package/dist/gateway/protocol/schema.js +1 -0
  20. package/dist/gateway/public-chat/session-token.js +52 -0
  21. package/dist/gateway/public-chat-api.js +40 -13
  22. package/dist/gateway/server-methods/logs.js +17 -1
  23. package/dist/gateway/server-methods/public-chat.js +5 -0
  24. package/dist/gateway/server-methods/sessions-transcript.js +30 -6
  25. package/dist/gateway/server-methods/whatsapp-conversations.js +387 -0
  26. package/dist/gateway/server-methods-list.js +6 -0
  27. package/dist/gateway/server-methods.js +7 -0
  28. package/dist/gateway/server.impl.js +3 -1
  29. package/dist/gateway/sessions-patch.js +1 -1
  30. package/dist/hooks/bundled/ride-dispatch/HOOK.md +7 -6
  31. package/dist/hooks/bundled/ride-dispatch/handler.js +75 -30
  32. package/dist/memory/manager.js +3 -3
  33. package/dist/tui/tui-command-handlers.js +1 -1
  34. package/dist/web/auto-reply/monitor/group-activation.js +12 -10
  35. package/dist/web/auto-reply/monitor/group-gating.js +23 -2
  36. package/dist/web/auto-reply/monitor/on-message.js +27 -5
  37. package/dist/web/auto-reply/monitor/process-message.js +64 -53
  38. package/dist/web/inbound/monitor.js +30 -0
  39. package/extensions/whatsapp/src/channel.ts +1 -1
  40. package/package.json +1 -1
  41. package/skills/log-review/SKILL.md +17 -4
  42. package/skills/log-review/references/review-protocol.md +4 -4
  43. package/taskmaster-docs/USER-GUIDE.md +14 -0
  44. package/dist/control-ui/assets/index-Bd75cI7J.js.map +0 -1
  45. package/dist/control-ui/assets/index-BkymP95Y.css +0 -1
@@ -182,10 +182,10 @@ export async function processMessage(params) {
182
182
  // ── End opening hours gate ──────────────────────────────────────────
183
183
  // Send ack reaction immediately upon message receipt (post-gating).
184
184
  // Suppress when running silently on un-mentioned group messages.
185
- if (params.suppressDelivery) {
185
+ if (params.contextOnly) {
186
186
  shouldClearGroupHistory = false;
187
187
  }
188
- if (!params.suppressDelivery)
188
+ if (!params.contextOnly)
189
189
  maybeSendAckReaction({
190
190
  cfg: params.cfg,
191
191
  msg: params.msg,
@@ -315,6 +315,21 @@ export async function processMessage(params) {
315
315
  }, "failed updating session meta");
316
316
  });
317
317
  trackBackgroundTask(params.backgroundTasks, metaTask);
318
+ // contextOnly mode: accumulate user message in transcript but skip LLM.
319
+ // This prevents phantom assistant messages that corrupt conversation history.
320
+ if (params.contextOnly) {
321
+ // Store user message in transcript so context accumulates for the next mentioned reply
322
+ void appendUserMessageToSessionTranscript({
323
+ agentId: params.route.agentId,
324
+ sessionKey: params.route.sessionKey,
325
+ text: combinedBody,
326
+ storePath,
327
+ }).catch((err) => {
328
+ params.replyLogger.warn({ error: formatError(err) }, "contextOnly: failed to store user turn");
329
+ });
330
+ params.replyLogger.info({ correlationId, from: fromDisplay }, "context-only mode: user message stored, LLM skipped");
331
+ return false;
332
+ }
318
333
  const { queuedFinal } = await dispatchReplyWithBufferedBlockDispatcher({
319
334
  ctx: ctxPayload,
320
335
  cfg: params.cfg,
@@ -328,60 +343,56 @@ export async function processMessage(params) {
328
343
  logVerbose("Stripped stray HEARTBEAT_OK token from web reply");
329
344
  }
330
345
  },
331
- deliver: params.suppressDelivery
332
- ? async () => {
333
- /* Silently discard — agent ran but reply is suppressed (no mention) */
346
+ deliver: async (payload, info) => {
347
+ await deliverWebReply({
348
+ replyResult: payload,
349
+ msg: params.msg,
350
+ maxMediaBytes: params.maxMediaBytes,
351
+ textLimit,
352
+ chunkMode,
353
+ replyLogger: params.replyLogger,
354
+ connectionId: params.connectionId,
355
+ // Tool + block updates are noisy; skip their log lines.
356
+ skipLog: info.kind !== "final",
357
+ tableMode,
358
+ });
359
+ didSendReply = true;
360
+ if (info.kind === "tool") {
361
+ params.rememberSentText(payload.text, {});
362
+ return;
334
363
  }
335
- : async (payload, info) => {
336
- await deliverWebReply({
337
- replyResult: payload,
338
- msg: params.msg,
339
- maxMediaBytes: params.maxMediaBytes,
340
- textLimit,
341
- chunkMode,
342
- replyLogger: params.replyLogger,
343
- connectionId: params.connectionId,
344
- // Tool + block updates are noisy; skip their log lines.
345
- skipLog: info.kind !== "final",
346
- tableMode,
347
- });
348
- didSendReply = true;
349
- if (info.kind === "tool") {
350
- params.rememberSentText(payload.text, {});
351
- return;
364
+ const shouldLog = info.kind === "final" && payload.text ? true : undefined;
365
+ params.rememberSentText(payload.text, {
366
+ combinedBody,
367
+ combinedBodySessionKey: params.route.sessionKey,
368
+ logVerboseMessage: shouldLog,
369
+ });
370
+ if (info.kind === "final") {
371
+ const fromDisplay = params.msg.chatType === "group" ? conversationId : (params.msg.from ?? "unknown");
372
+ const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
373
+ whatsappOutboundLog.info(`Auto-replied to ${fromDisplay}${hasMedia ? " (media)" : ""}`);
374
+ if (shouldLogVerbose()) {
375
+ const preview = payload.text != null ? elide(payload.text, 400) : "<media>";
376
+ whatsappOutboundLog.debug(`Reply body: ${preview}${hasMedia ? " (media)" : ""}`);
352
377
  }
353
- const shouldLog = info.kind === "final" && payload.text ? true : undefined;
354
- params.rememberSentText(payload.text, {
355
- combinedBody,
356
- combinedBodySessionKey: params.route.sessionKey,
357
- logVerboseMessage: shouldLog,
378
+ // Fire message:outbound hook for conversation archiving
379
+ const outboundHookEvent = createInternalHookEvent("message", "outbound", params.route.sessionKey, {
380
+ from: params.msg.to, // Agent is sending
381
+ to: params.msg.senderE164 ?? params.msg.from, // To the user
382
+ text: payload.text,
383
+ timestamp: Date.now(),
384
+ chatType: params.msg.chatType,
385
+ agentId: params.route.agentId,
386
+ channel: "whatsapp",
387
+ hasMedia,
388
+ senderE164: params.msg.senderE164,
389
+ groupSubject: params.msg.groupSubject,
390
+ conversationId,
391
+ cfg: params.cfg,
358
392
  });
359
- if (info.kind === "final") {
360
- const fromDisplay = params.msg.chatType === "group" ? conversationId : (params.msg.from ?? "unknown");
361
- const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
362
- whatsappOutboundLog.info(`Auto-replied to ${fromDisplay}${hasMedia ? " (media)" : ""}`);
363
- if (shouldLogVerbose()) {
364
- const preview = payload.text != null ? elide(payload.text, 400) : "<media>";
365
- whatsappOutboundLog.debug(`Reply body: ${preview}${hasMedia ? " (media)" : ""}`);
366
- }
367
- // Fire message:outbound hook for conversation archiving
368
- const outboundHookEvent = createInternalHookEvent("message", "outbound", params.route.sessionKey, {
369
- from: params.msg.to, // Agent is sending
370
- to: params.msg.senderE164 ?? params.msg.from, // To the user
371
- text: payload.text,
372
- timestamp: Date.now(),
373
- chatType: params.msg.chatType,
374
- agentId: params.route.agentId,
375
- channel: "whatsapp",
376
- hasMedia,
377
- senderE164: params.msg.senderE164,
378
- groupSubject: params.msg.groupSubject,
379
- conversationId,
380
- cfg: params.cfg,
381
- });
382
- void triggerInternalHook(outboundHookEvent);
383
- }
384
- },
393
+ void triggerInternalHook(outboundHookEvent);
394
+ }
395
+ },
385
396
  onError: (err, info) => {
386
397
  const label = info.kind === "tool"
387
398
  ? "tool update"
@@ -395,5 +395,35 @@ export async function monitorWebInbox(options) {
395
395
  },
396
396
  // IPC surface (sendMessage/sendPoll/sendReaction/sendComposingTo)
397
397
  ...sendApi,
398
+ // Conversation browser methods
399
+ listConversations: async () => {
400
+ const entries = [];
401
+ for (const [jid, cached] of groupMetaCache) {
402
+ entries.push({
403
+ jid,
404
+ type: "group",
405
+ name: cached.subject ?? jid,
406
+ lastMessageTimestamp: undefined,
407
+ });
408
+ }
409
+ return entries;
410
+ },
411
+ getMessages: async (_jid, _limit) => {
412
+ // No in-memory message store — Baileys makeInMemoryStore is not used.
413
+ return [];
414
+ },
415
+ getGroupMetadata: async (jid) => {
416
+ const meta = await sock.groupMetadata(jid);
417
+ return {
418
+ subject: meta.subject,
419
+ participants: (meta.participants ?? []).map((p) => ({
420
+ jid: p.id,
421
+ name: p.name ?? undefined,
422
+ admin: p.admin === "admin" || p.admin === "superadmin",
423
+ })),
424
+ creation: meta.creation,
425
+ desc: meta.desc ?? undefined,
426
+ };
427
+ },
398
428
  };
399
429
  }
@@ -56,7 +56,7 @@ export const whatsappPlugin: ChannelPlugin<ResolvedWhatsAppAccount> = {
56
56
  reactions: true,
57
57
  media: true,
58
58
  },
59
- reload: { configPrefixes: ["web"], noopPrefixes: ["channels.whatsapp"] },
59
+ reload: { configPrefixes: ["web", "channels.whatsapp"], noopPrefixes: [] },
60
60
  gatewayMethods: ["web.login.start", "web.login.wait"],
61
61
  configSchema: buildChannelConfigSchema(WhatsAppConfigSchema),
62
62
  config: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.16.3",
3
+ "version": "1.17.0",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -20,12 +20,13 @@ Load `references/review-protocol.md` for the full review and analysis protocol.
20
20
 
21
21
  ### Rules
22
22
 
23
- 1. **Scan system logs** — use `logs_read(action: "system")` for gateway-level errors and warnings
23
+ 1. **Scan system logs** — use `logs_read(action: "system", minLevel: "warn")` to retrieve only warnings and errors. Do not call without `minLevel` — full logs will overflow context.
24
24
  2. **Scan session logs** — use `logs_read(action: "sessions")` across all agents for tool failures and agent errors
25
25
  3. **Triage by severity** — critical, warning, info. Deduplicate repeated errors.
26
- 4. **Analyse each issue** — plain-language explanation, likely root cause, suggested admin action
27
- 5. **Escalation guidance** — for issues beyond local resolution, advise forwarding this report to the Taskmaster public agent on WhatsApp for support
28
- 6. **All-clear confirmation** — if no issues found, confirm briefly so admins know the review ran
26
+ 4. **Deduplicate** — group identical messages from repeated occurrences and report as "N× [message]" rather than listing each separately.
27
+ 5. **Analyse each issue** — plain-language explanation, likely root cause, suggested admin action
28
+ 6. **Escalation guidance** — for issues beyond local resolution, advise forwarding this report to the Taskmaster public agent on WhatsApp for support
29
+ 7. **All-clear confirmation** — if no issues found, confirm briefly so admins know the review ran
29
30
 
30
31
  ### Output Format
31
32
 
@@ -37,8 +38,20 @@ The report should follow this structure:
37
38
  - **Escalation** — if any issues warrant Taskmaster support, advise forwarding this report
38
39
  - **Summary** — issue count or all-clear confirmation
39
40
 
41
+ Each individual issue entry must be formatted exactly as:
42
+
43
+ ```
44
+ **N. Title** _(HH:MM or HH:MM–HH:MM Nx)_
45
+ - What: ...
46
+ - Why: ...
47
+ - Action: ...
48
+ ```
49
+
50
+ The time in parentheses is mandatory. Extract it from the log line's `time` field (UTC ISO format, e.g. `2026-03-05T10:35:22.000Z`) — take the `HH:MM` part, which is characters 11–15 of the string (e.g. `10:35`). For repeated occurrences show the first–last range and count: `08:36–09:48 (3×)`. Never omit the time — if the log line has no `time` field, write `(?:??)` as a placeholder.
51
+
40
52
  ### Constraints
41
53
 
54
+ - **Only report what is directly evidenced in the log data returned by the tools.** Do not infer, extrapolate, or report issues that do not appear verbatim in the log lines. Every issue in the report must be traceable to a specific log entry you received.
42
55
  - Keep total execution under 2 minutes
43
56
  - Be honest — if nothing surfaced, say "all clear" rather than padding the report
44
57
  - Use plain language — the admin is not a developer
@@ -4,9 +4,7 @@ Run this protocol on schedule or when triggered by an admin. Be thorough in anal
4
4
 
5
5
  ## Step 1: System Log Scan
6
6
 
7
- Use `logs_read` with `action: "system"` to pull recent gateway logs.
8
-
9
- Focus on entries containing `[ERROR]` and `[WARN]`. Ignore routine info-level entries unless they reveal a pattern (e.g. repeated restarts, auth refresh cycles).
7
+ Use `logs_read(action: "system", minLevel: "warn")` to pull only warnings and errors. **Never call without `minLevel`** — the full log will overflow context.
10
8
 
11
9
  Look for:
12
10
  - Service crashes or unexpected restarts
@@ -18,7 +16,7 @@ Look for:
18
16
 
19
17
  ## Step 2: Session Log Scan
20
18
 
21
- Use `logs_read` with `action: "sessions"` to pull recent session transcripts across all agents.
19
+ Use `logs_read(action: "sessions")` to pull recent error entries across all agents. Only error-type entries are returned — tool failures, agent exceptions, and crashed sessions.
22
20
 
23
21
  Look for:
24
22
  - Tool call failures or errors
@@ -46,6 +44,8 @@ For each issue provide:
46
44
 
47
45
  Be specific with actions. "Check the logs" is not a useful suggestion — the whole point of this skill is that the admin doesn't have to.
48
46
 
47
+ **CRITICAL: Only report issues present in the log data you received.** Do not draw on your knowledge of the codebase, providers, or past conversations to invent or infer issues. If a provider name, error code, or warning does not appear in a log line you read, it must not appear in the report. Fabricating issues destroys trust.
48
+
49
49
  ## Step 5: Escalation Guidance
50
50
 
51
51
  If any issue meets these criteria, advise the admin to escalate to Taskmaster support:
@@ -1166,6 +1166,20 @@ Occasionally, AI providers like Claude experience temporary outages or slowdowns
1166
1166
  2. **Check your logs** — If replies are failing, open **Advanced → Logs → Session Logs** and filter by "errors" to see what's happening.
1167
1167
  3. **Wait it out** — Most AI service outages resolve within minutes to hours. Your assistant will automatically use the primary provider again once it's back.
1168
1168
 
1169
+ ### Sharing logs with support
1170
+
1171
+ If you're still stuck after trying the steps above, export your logs and send them to Taskmaster support on WhatsApp. Logs give the support team exact error messages and timing, which speeds up diagnosis.
1172
+
1173
+ 1. Go to the **Advanced** tab in the Control Panel and tap **Logs**
1174
+ 2. Select the view that covers your issue:
1175
+ - **Session Logs** — for problems with replies, conversations, or tool errors
1176
+ - **System Logs** — for connection, startup, or gateway issues
1177
+ 3. Tap **Filters** to open the filter panel
1178
+ 4. Tap **Export** — the browser downloads a log file to your device
1179
+ 5. Send the file to Taskmaster support on WhatsApp
1180
+
1181
+ > **Tip:** If you're not sure which view to use, export both — Session Logs and System Logs — and send them together.
1182
+
1169
1183
  ### "sudo: unable to resolve host taskmaster" warning?
1170
1184
 
1171
1185
  This harmless warning appears when the Pi's hostname isn't listed in `/etc/hosts`. Every `sudo` command still works — it's just a cosmetic message. To fix it, open a terminal on the Pi and run: