@rubytech/create-maxy-code 0.1.536 → 0.1.538
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/package.json +1 -1
- package/payload/platform/plugins/admin/hooks/__tests__/mailbox-inject.test.sh +11 -5
- package/payload/platform/plugins/admin/hooks/mailbox-inject.sh +12 -7
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +135 -4
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +10 -0
- package/payload/platform/plugins/docs/PLUGIN.md +2 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +54 -3
- package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +75 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +2 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +22 -7
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +16 -0
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +1 -1
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +19 -3
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +67 -4
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +23 -2
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +6 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +22 -3
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts +8 -2
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +32 -27
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.js +5 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.js.map +1 -1
- package/payload/server/{chunk-44Z4ZFYS.js → chunk-FQO5RDWZ.js} +2 -0
- package/payload/server/{manager-AJSPM6X2.js → manager-EBZQRV5V.js} +1 -1
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/{AdminLoginScreens-BpHVvUsH.js → AdminLoginScreens-CfXQwKIY.js} +1 -1
- package/payload/server/public/assets/AdminLoginScreens-CfXQwKIY.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-CfXQwKIY.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-y0_mzlDF.js +2 -0
- package/payload/server/public/assets/AdminShell-y0_mzlDF.js.br +0 -0
- package/payload/server/public/assets/AdminShell-y0_mzlDF.js.gz +0 -0
- package/payload/server/public/assets/{activity-B3WEyiTh.js → activity-h8-sjHz9.js} +1 -1
- package/payload/server/public/assets/activity-h8-sjHz9.js.br +0 -0
- package/payload/server/public/assets/activity-h8-sjHz9.js.gz +0 -0
- package/payload/server/public/assets/admin-BzhhLC7C.js +1 -0
- package/payload/server/public/assets/admin-BzhhLC7C.js.br +0 -0
- package/payload/server/public/assets/admin-BzhhLC7C.js.gz +0 -0
- package/payload/server/public/assets/{agents-CmYDQbdM.js → agents-DLuhIIgn.js} +1 -1
- package/payload/server/public/assets/agents-DLuhIIgn.js.br +0 -0
- package/payload/server/public/assets/agents-DLuhIIgn.js.gz +0 -0
- package/payload/server/public/assets/{browser-CP8TnIUm.js → browser-FAnTY27n.js} +1 -1
- package/payload/server/public/assets/browser-FAnTY27n.js.br +0 -0
- package/payload/server/public/assets/browser-FAnTY27n.js.gz +0 -0
- package/payload/server/public/assets/calendar-C_PkqlkQ.js +1 -0
- package/payload/server/public/assets/calendar-C_PkqlkQ.js.br +0 -0
- package/payload/server/public/assets/calendar-C_PkqlkQ.js.gz +0 -0
- package/payload/server/public/assets/chat-DW81Ra89.js +1 -0
- package/payload/server/public/assets/chat-DW81Ra89.js.br +0 -0
- package/payload/server/public/assets/chat-DW81Ra89.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-zV-WRzHb.js +1 -0
- package/payload/server/public/assets/chevron-left-zV-WRzHb.js.br +0 -0
- package/payload/server/public/assets/chevron-right-fnINU4bx.js +1 -0
- package/payload/server/public/assets/chevron-right-fnINU4bx.js.br +0 -0
- package/payload/server/public/assets/clock-2fSxBl6d.js +1 -0
- package/payload/server/public/assets/clock-2fSxBl6d.js.br +0 -0
- package/payload/server/public/assets/copy-Rde_TL9Q.js +1 -0
- package/payload/server/public/assets/copy-Rde_TL9Q.js.br +0 -0
- package/payload/server/public/assets/copy-Rde_TL9Q.js.gz +0 -0
- package/payload/server/public/assets/data-S5g4wIDe.js +1 -0
- package/payload/server/public/assets/data-S5g4wIDe.js.br +0 -0
- package/payload/server/public/assets/data-S5g4wIDe.js.gz +0 -0
- package/payload/server/public/assets/{file-text-lsHlk4lR.js → file-text-DMCUVg80.js} +1 -1
- package/payload/server/public/assets/file-text-DMCUVg80.js.br +0 -0
- package/payload/server/public/assets/file-text-DMCUVg80.js.gz +0 -0
- package/payload/server/public/assets/graph-Bo-WcsKS.js +51 -0
- package/payload/server/public/assets/graph-Bo-WcsKS.js.br +0 -0
- package/payload/server/public/assets/graph-Bo-WcsKS.js.gz +0 -0
- package/payload/server/public/assets/{graph-labels-Ci5Jz1oY.js → graph-labels-g-JodMWQ.js} +1 -1
- package/payload/server/public/assets/graph-labels-g-JodMWQ.js.br +0 -0
- package/payload/server/public/assets/graph-labels-g-JodMWQ.js.gz +0 -0
- package/payload/server/public/assets/operator-X_LcOnAI.js +1 -0
- package/payload/server/public/assets/operator-X_LcOnAI.js.br +0 -0
- package/payload/server/public/assets/operator-X_LcOnAI.js.gz +0 -0
- package/payload/server/public/assets/page-Cz0p1mBf.js +1 -0
- package/payload/server/public/assets/page-Cz0p1mBf.js.br +0 -0
- package/payload/server/public/assets/page-Cz0p1mBf.js.gz +0 -0
- package/payload/server/public/assets/page-Dzs3MsFL.js +32 -0
- package/payload/server/public/assets/page-Dzs3MsFL.js.br +0 -0
- package/payload/server/public/assets/page-Dzs3MsFL.js.gz +0 -0
- package/payload/server/public/assets/play-CraN5kUU.js +1 -0
- package/payload/server/public/assets/play-CraN5kUU.js.br +0 -0
- package/payload/server/public/assets/play-CraN5kUU.js.gz +0 -0
- package/payload/server/public/assets/{public-BNAVdC2B.js → public--NUjaJ-q.js} +1 -1
- package/payload/server/public/assets/public--NUjaJ-q.js.br +0 -0
- package/payload/server/public/assets/public--NUjaJ-q.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-D6DZb7tR.js → rotate-ccw-DshFgg2T.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-DshFgg2T.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-DshFgg2T.js.gz +0 -0
- package/payload/server/public/assets/routines-CfAS4gri.js +2 -0
- package/payload/server/public/assets/routines-CfAS4gri.js.br +0 -0
- package/payload/server/public/assets/routines-CfAS4gri.js.gz +0 -0
- package/payload/server/public/assets/{skills-9S_nNIpO.js → skills-CM6ryWpx.js} +1 -1
- package/payload/server/public/assets/skills-CM6ryWpx.js.br +0 -0
- package/payload/server/public/assets/skills-CM6ryWpx.js.gz +0 -0
- package/payload/server/public/assets/tasks-B07m9LIA.js +3 -0
- package/payload/server/public/assets/tasks-B07m9LIA.js.br +0 -0
- package/payload/server/public/assets/tasks-B07m9LIA.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-BcpCZDHe.js → triangle-alert-CKHyS_gC.js} +1 -1
- package/payload/server/public/assets/triangle-alert-CKHyS_gC.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-CKHyS_gC.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-DqTnYnz5.js → useCopyFeedback-CmawEsVi.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-CmawEsVi.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-CmawEsVi.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CVHqKsqK.js +14 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CVHqKsqK.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CVHqKsqK.js.gz +0 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-CYEDydoq.css → useSubAccountSwitcher-yN4lhH2x.css} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-yN4lhH2x.css.br +0 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-CYEDydoq.css.gz → useSubAccountSwitcher-yN4lhH2x.css.gz} +0 -0
- package/payload/server/public/assets/useVoiceRecorder-DpQTvvqD.js +2 -0
- package/payload/server/public/assets/useVoiceRecorder-DpQTvvqD.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-DpQTvvqD.js.gz +0 -0
- package/payload/server/public/assets/{wrench-DvUj2R3p.js → wrench-BPOEUNJB.js} +1 -1
- package/payload/server/public/assets/wrench-BPOEUNJB.js.br +0 -0
- package/payload/server/public/assets/wrench-BPOEUNJB.js.gz +0 -0
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +7 -7
- package/payload/server/public/chat.html +13 -13
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +13 -13
- package/payload/server/public/routines.html +6 -6
- package/payload/server/public/skills.html +5 -5
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +989 -511
- package/payload/server/public/assets/AdminLoginScreens-BpHVvUsH.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-BpHVvUsH.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-BT7VQIu7.js +0 -2
- package/payload/server/public/assets/AdminShell-BT7VQIu7.js.br +0 -0
- package/payload/server/public/assets/AdminShell-BT7VQIu7.js.gz +0 -0
- package/payload/server/public/assets/activity-B3WEyiTh.js.br +0 -0
- package/payload/server/public/assets/activity-B3WEyiTh.js.gz +0 -0
- package/payload/server/public/assets/admin-BEKL9Bq_.js +0 -1
- package/payload/server/public/assets/admin-BEKL9Bq_.js.br +0 -0
- package/payload/server/public/assets/admin-BEKL9Bq_.js.gz +0 -0
- package/payload/server/public/assets/agents-CmYDQbdM.js.br +0 -0
- package/payload/server/public/assets/agents-CmYDQbdM.js.gz +0 -0
- package/payload/server/public/assets/browser-CP8TnIUm.js.br +0 -0
- package/payload/server/public/assets/browser-CP8TnIUm.js.gz +0 -0
- package/payload/server/public/assets/calendar-DM6Bl8Ra.js +0 -1
- package/payload/server/public/assets/calendar-DM6Bl8Ra.js.br +0 -0
- package/payload/server/public/assets/calendar-DM6Bl8Ra.js.gz +0 -0
- package/payload/server/public/assets/chat-BJWdNwCN.js +0 -1
- package/payload/server/public/assets/chat-BJWdNwCN.js.br +0 -2
- package/payload/server/public/assets/chat-BJWdNwCN.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-DU8Es8Xq.js +0 -1
- package/payload/server/public/assets/chevron-right-D8HTOkyy.js +0 -1
- package/payload/server/public/assets/chevron-right-D8HTOkyy.js.br +0 -0
- package/payload/server/public/assets/clock-BdNuUyUU.js +0 -1
- package/payload/server/public/assets/clock-BdNuUyUU.js.br +0 -0
- package/payload/server/public/assets/clock-BdNuUyUU.js.gz +0 -0
- package/payload/server/public/assets/data-EjLIuy-W.js +0 -1
- package/payload/server/public/assets/data-EjLIuy-W.js.br +0 -0
- package/payload/server/public/assets/data-EjLIuy-W.js.gz +0 -0
- package/payload/server/public/assets/file-text-lsHlk4lR.js.br +0 -0
- package/payload/server/public/assets/file-text-lsHlk4lR.js.gz +0 -0
- package/payload/server/public/assets/graph-B0awsgVj.js +0 -51
- package/payload/server/public/assets/graph-B0awsgVj.js.br +0 -0
- package/payload/server/public/assets/graph-B0awsgVj.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-Ci5Jz1oY.js.br +0 -0
- package/payload/server/public/assets/graph-labels-Ci5Jz1oY.js.gz +0 -0
- package/payload/server/public/assets/maximize-2-DRndYysc.js +0 -1
- package/payload/server/public/assets/maximize-2-DRndYysc.js.br +0 -0
- package/payload/server/public/assets/maximize-2-DRndYysc.js.gz +0 -0
- package/payload/server/public/assets/operator-tiI-kz6v.js +0 -1
- package/payload/server/public/assets/operator-tiI-kz6v.js.br +0 -0
- package/payload/server/public/assets/operator-tiI-kz6v.js.gz +0 -0
- package/payload/server/public/assets/page-RkwjxAgF.js +0 -32
- package/payload/server/public/assets/page-RkwjxAgF.js.br +0 -0
- package/payload/server/public/assets/page-RkwjxAgF.js.gz +0 -0
- package/payload/server/public/assets/page-ZG7SXMO0.js +0 -1
- package/payload/server/public/assets/page-ZG7SXMO0.js.br +0 -0
- package/payload/server/public/assets/page-ZG7SXMO0.js.gz +0 -0
- package/payload/server/public/assets/public-BNAVdC2B.js.br +0 -0
- package/payload/server/public/assets/public-BNAVdC2B.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-D6DZb7tR.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-D6DZb7tR.js.gz +0 -0
- package/payload/server/public/assets/routines-BVcPovu-.js +0 -2
- package/payload/server/public/assets/routines-BVcPovu-.js.br +0 -0
- package/payload/server/public/assets/routines-BVcPovu-.js.gz +0 -0
- package/payload/server/public/assets/skills-9S_nNIpO.js.br +0 -0
- package/payload/server/public/assets/skills-9S_nNIpO.js.gz +0 -0
- package/payload/server/public/assets/tasks-D-D2efaL.js +0 -3
- package/payload/server/public/assets/tasks-D-D2efaL.js.br +0 -0
- package/payload/server/public/assets/tasks-D-D2efaL.js.gz +0 -0
- package/payload/server/public/assets/time-entry-format-CKpHcWWs.js +0 -1
- package/payload/server/public/assets/time-entry-format-CKpHcWWs.js.br +0 -0
- package/payload/server/public/assets/time-entry-format-CKpHcWWs.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-BcpCZDHe.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-BcpCZDHe.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-DqTnYnz5.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-DqTnYnz5.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CYEDydoq.css.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-ny9tP8am.js +0 -14
- package/payload/server/public/assets/useSubAccountSwitcher-ny9tP8am.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-ny9tP8am.js.gz +0 -0
- package/payload/server/public/assets/useVoiceRecorder-DcgtZl-4.js +0 -2
- package/payload/server/public/assets/useVoiceRecorder-DcgtZl-4.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-DcgtZl-4.js.gz +0 -0
- package/payload/server/public/assets/wrench-DvUj2R3p.js.br +0 -2
- package/payload/server/public/assets/wrench-DvUj2R3p.js.gz +0 -0
package/package.json
CHANGED
|
@@ -105,14 +105,20 @@ else
|
|
|
105
105
|
fi
|
|
106
106
|
rm -rf "$A"
|
|
107
107
|
|
|
108
|
-
# --- Case 2: ACCOUNT_DIR unset
|
|
108
|
+
# --- Case 2: ACCOUNT_DIR unset (a public spawn) -> silent -------------------
|
|
109
|
+
# Task 2271 — ACCOUNT_DIR is stamped for admin and specialist spawns only
|
|
110
|
+
# (pty-spawner.ts, the `isSpecialistSpawn || role === admin` gate) and
|
|
111
|
+
# unconditionally on the rc-spawn path. Its ABSENCE therefore means a
|
|
112
|
+
# role:public spawn, and a public visitor must never receive the operator's
|
|
113
|
+
# mailbox list. The cwd fallback this case used to assert was handing a stranger
|
|
114
|
+
# the HOUSE's addresses, because a public spawn's cwd is the house account dir.
|
|
109
115
|
A=$(newacct)
|
|
110
116
|
OUT=$(cd "$A" && printf '%s' "$STDIN" | env -u ACCOUNT_DIR LOG_DIR="$A/logs" bash "$HOOK" 2>/dev/null); EX=$?
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
ok "ACCOUNT_DIR unset ->
|
|
117
|
+
if [[ "$EX" -eq 0 && -z "$OUT" ]] \
|
|
118
|
+
&& /usr/bin/grep -q "op=skip reason=no-account-dir" "$A/logs/mailbox-inject.log"; then
|
|
119
|
+
ok "ACCOUNT_DIR unset (public spawn) -> exit 0, no block emitted"
|
|
114
120
|
else
|
|
115
|
-
bad "
|
|
121
|
+
bad "public-spawn silence (exit=$EX stdout=$OUT log=$(cat "$A/logs/mailbox-inject.log" 2>/dev/null))"
|
|
116
122
|
fi
|
|
117
123
|
rm -rf "$A"
|
|
118
124
|
|
|
@@ -37,16 +37,21 @@ command -v node >/dev/null 2>&1 || exit 0
|
|
|
37
37
|
|
|
38
38
|
# --- account dir -----------------------------------------------------------
|
|
39
39
|
# ACCOUNT_DIR is stamped unconditionally on the rc-spawn path (rc-daemon.ts
|
|
40
|
-
# buildRcChildEnv)
|
|
41
|
-
# (pty-spawner.ts, the `isSpecialistSpawn || role === admin` gate)
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
40
|
+
# buildRcChildEnv) and, on the pty-spawner path, only for admin/specialist
|
|
41
|
+
# (pty-spawner.ts, the `isSpecialistSpawn || role === admin` gate).
|
|
42
|
+
#
|
|
43
|
+
# Task 2271 — requiring it IS the role gate. This hook is wired unconditionally
|
|
44
|
+
# for every account (provision-account-dir.sh) with no role of its own, and the
|
|
45
|
+
# previous `$PWD` fallback existed to serve the spawns that carry no
|
|
46
|
+
# ACCOUNT_DIR. On that path those are exactly the role:public ones: a public
|
|
47
|
+
# WhatsApp or webchat visitor, or a passive intake whose only tool is
|
|
48
|
+
# work-create. None of them should receive the operator's mailbox list, and a
|
|
49
|
+
# public visitor session was receiving the HOUSE's, because a public spawn's cwd
|
|
50
|
+
# is the house account dir. The block's only prior protection was a sentence
|
|
51
|
+
# asking the model not to recite it.
|
|
45
52
|
ACCT=""
|
|
46
53
|
if [ -n "${ACCOUNT_DIR:-}" ] && [ -d "${ACCOUNT_DIR}" ]; then
|
|
47
54
|
ACCT="$ACCOUNT_DIR"
|
|
48
|
-
elif [ -f "$PWD/.claude/settings.json" ] && [ -d "$PWD/secrets" ]; then
|
|
49
|
-
ACCT="$PWD"
|
|
50
55
|
fi
|
|
51
56
|
|
|
52
57
|
# --- log dir ---------------------------------------------------------------
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:08b03d80446b9b08b4b64926a3ac7d7f55a6f07d4182512dcca1d107c78cc9a2
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -1661,6 +1661,86 @@ Changes are per-account and take effect immediately.
|
|
|
1661
1661
|
|
|
1662
1662
|
Brand isolation extends to the device filesystem. Every Maxy install provisions an SMB share scoped to that brand's install folder, credentialled by the brand's install owner and the Maxy PIN. A device that hosts more than one brand carries one share per brand; tearing one brand down never exposes another brand's files. See [Samba Share](./samba.md) for the credential model, per-OS mount syntax, and peer-brand lifecycle.
|
|
1663
1663
|
|
|
1664
|
+
---
|
|
1665
|
+
# What Wakes a Session
|
|
1666
|
+
Source: https://docs.getmaxy.com/channel-wake-and-prompt.md
|
|
1667
|
+
|
|
1668
|
+
# What wakes a session, and what is in its prompt
|
|
1669
|
+
|
|
1670
|
+
Two questions an agent cannot answer from inside itself, and both have caused real faults: *what woke me?* and *which files did my prompt come from?* This page answers both. When the answer is not here, the logs have it: `logs-read --tail server`, filtered on the `[whatsapp:*]` tags.
|
|
1671
|
+
|
|
1672
|
+
## What wakes a session
|
|
1673
|
+
|
|
1674
|
+
Five things start or resume a WhatsApp session. They are distinguishable, and the distinction matters, because two of them are written by the platform rather than by the person named as the sender.
|
|
1675
|
+
|
|
1676
|
+
**An admin phone or an account manager messages the line.** This resumes that person's own admin session. There is no per-account admin session: the session id is a hash of the account plus the person, so one person messaging from two channels resumes one thread, and two managers on one account have two threads.
|
|
1677
|
+
|
|
1678
|
+
**A public sender messages the line.** Three things follow, in this order, and each is independent of the ones after it.
|
|
1679
|
+
|
|
1680
|
+
1. The account's registered managers are notified, verbatim, unless the account has turned that off. This fires before any agent starts, so it survives a spawn that fails.
|
|
1681
|
+
2. Every bound manager's own admin session is woken with a `public-relay` turn carrying the sender's message as fenced content. This is the carrier for anything the account needs to do about the visitor: translate, put a request to a registered counterparty, carry the answer back.
|
|
1682
|
+
3. The account's public agent answers the visitor, if the account has one and has enabled it. If not, the visitor receives nothing at all, and steps 1 and 2 have already happened regardless.
|
|
1683
|
+
|
|
1684
|
+
**The scheduler fires a due event.** This raises a `schedule` turn on the destination's own session, carrying the event's prompt.
|
|
1685
|
+
|
|
1686
|
+
**A passive account-manager binding receives a message.** This spawns a constrained one-shot that files a task and never replies. It cannot hold a conversation or answer against existing state.
|
|
1687
|
+
|
|
1688
|
+
## Reading why you woke
|
|
1689
|
+
|
|
1690
|
+
Every turn carries a `source`. Three values exist:
|
|
1691
|
+
|
|
1692
|
+
| `source` | Meaning |
|
|
1693
|
+
|---|---|
|
|
1694
|
+
| `user` | A real person sent this message to the line. Also the value when the field is absent. |
|
|
1695
|
+
| `schedule` | The scheduler wrote this turn. The named destination did not send it. |
|
|
1696
|
+
| `public-relay` | The platform wrote this turn because a public visitor messaged the account. The named sender is a manager being told, not someone who wrote to you. |
|
|
1697
|
+
|
|
1698
|
+
A `public-relay` turn also says so in its own first two lines. If a turn claims a manager wrote something they plainly would not have written, check the source before answering them.
|
|
1699
|
+
|
|
1700
|
+
## Which files compose the prompt
|
|
1701
|
+
|
|
1702
|
+
The system prompt is assembled per spawn from one agent directory, and **which directory depends on the role**:
|
|
1703
|
+
|
|
1704
|
+
- A `role:'public'` spawn reads `agents/<the account's public-agent slug>/`.
|
|
1705
|
+
- Every other role reads `agents/admin/`.
|
|
1706
|
+
|
|
1707
|
+
From that one directory it takes `IDENTITY.md` and `SOUL.md`, verbatim.
|
|
1708
|
+
|
|
1709
|
+
The consequence catches people out: **an instruction written into `agents/admin/SOUL.md` is never read by a public spawn**, and an instruction written into a public agent's `SOUL.md` is never read by an admin turn. Before writing an instruction into a role file, decide which role will actually be running when it needs to apply. If the answer is "the admin agent, woken by a stranger's message", the file is `agents/admin/SOUL.md` and the trigger is the `public-relay` dispatch above.
|
|
1710
|
+
|
|
1711
|
+
A public agent's directory also carries `KNOWLEDGE.md` and `config.json`. An account can hold as many public agents as it likes; only one is bound to WhatsApp at a time, and that binding is per account.
|
|
1712
|
+
|
|
1713
|
+
## The switches, and what each one stops
|
|
1714
|
+
|
|
1715
|
+
Four settings sit on this path. They are not interchangeable, and reaching for the wrong one is how an operator ends up silencing more than they meant.
|
|
1716
|
+
|
|
1717
|
+
| Setting | What it stops | What it leaves running |
|
|
1718
|
+
|---|---|---|
|
|
1719
|
+
| `dmPolicy: disabled` | Public senders are refused at the gate. | Admin and manager inbound. |
|
|
1720
|
+
| `publicAgentEnabled: false` | The visitor gets no reply. | The manager notification and the relay. |
|
|
1721
|
+
| `managerNotifyEnabled: false` | The verbatim copy of the visitor's message. | The relay, and any reply the woken turn sends. |
|
|
1722
|
+
| `dispatchInbound: false` | Every agent turn on that account's socket. | Storage: messages are still stored and readable. |
|
|
1723
|
+
|
|
1724
|
+
`managerNotifyEnabled` is the fine-grained one, and the one to reach for when the notifications are unwanted but the work is not. Unbinding a manager is not a substitute: the manager registry is shared by the notification, the relay and `whatsapp-notify-manager`, so unbinding silences all three.
|
|
1725
|
+
|
|
1726
|
+
Read the current value with `whatsapp-config {action:'get-manager-notify-enabled'}` and set it with `set-manager-notify-enabled`. Both are scoped to the calling session's own account.
|
|
1727
|
+
|
|
1728
|
+
## When the answer is not here
|
|
1729
|
+
|
|
1730
|
+
`logs-read --tail server` and filter on the tags. One public inbound writes this chain, in order, with `account=` and `sender=` on each line:
|
|
1731
|
+
|
|
1732
|
+
```
|
|
1733
|
+
[whatsapp:wire] op=stanza
|
|
1734
|
+
[wa-notify] op=eligible reason=<how the sender was admitted>
|
|
1735
|
+
[whatsapp:route] op=routed agentType=public
|
|
1736
|
+
[whatsapp:notify] op=public-inbound enabled=0|1 outcome=sent|failed|no-recipients|disabled
|
|
1737
|
+
[whatsapp:relay] op=dispatch outcome=sent|failed|no-recipients
|
|
1738
|
+
[whatsapp:public-agent] op=route enabled=… slug=… spawned=0|1
|
|
1739
|
+
[whatsapp:public-agent] op=spawn-ok | op=spawn-failed
|
|
1740
|
+
```
|
|
1741
|
+
|
|
1742
|
+
Three standing audits reconcile that chain rather than waiting for someone to notice. `op=public-notify-reconcile` reports `gap`, `relayGap` and `englishGap`, each of which counts a step that a routed inbound reached and the next one did not. `op=spawn-census` reports attempts against successes. A non-zero gap, or attempts with no successes, is a fault even when nothing has been reported.
|
|
1743
|
+
|
|
1664
1744
|
---
|
|
1665
1745
|
# Settings
|
|
1666
1746
|
Source: https://docs.getmaxy.com/settings.md
|
|
@@ -3535,6 +3615,46 @@ scheduling heartbeat and report on every tick including zero:
|
|
|
3535
3615
|
`op=dispatch-mode-none` (a routine that fires and delivers nothing) and
|
|
3536
3616
|
`op=null-timezone` (a routine that fires at the box's hour).
|
|
3537
3617
|
|
|
3618
|
+
### Tasks
|
|
3619
|
+
|
|
3620
|
+
The Tasks surface (`/tasks`) lists the account's `:Task` nodes on Open,
|
|
3621
|
+
Completed and Clients tabs and records billable time against each one. Its
|
|
3622
|
+
routes are all account-scoped:
|
|
3623
|
+
|
|
3624
|
+
| Mount | Purpose |
|
|
3625
|
+
|---|---|
|
|
3626
|
+
| `/tasks-list` | Open/Completed partition, plus the standing censuses described below. |
|
|
3627
|
+
| `/task-timer-start`, `/task-timer-stop` | Open and close a `:TimeEntry`. Timers on different tasks run concurrently; there is no auto-stop. |
|
|
3628
|
+
| `/task-complete` | Status → completed. Does **not** stop a running timer. |
|
|
3629
|
+
| `/task-time-adjust` | Manual time edit. Writes one `:TimeAdjustment`; 409s while the task has an open `:TimeEntry`. |
|
|
3630
|
+
| `/task-delete` | Removes the `:Task` **and** every `:TimeEntry` and `:TimeAdjustment` on it. Nothing is recoverable and there is no restore route. 409s while the task has an open `:TimeEntry`; 404s a wrong-account task. |
|
|
3631
|
+
| `/task-assign`, `/task-session-open`, `/task-session-link` | Assignee writes, and the Session control's spawn-or-resume path. |
|
|
3632
|
+
|
|
3633
|
+
**Row controls.** Each row carries a burger — the same `SessionRowActions`
|
|
3634
|
+
component the sidebar's session rows use — holding Edit time, Reset logged time
|
|
3635
|
+
to 0:00, Session and Delete. Only Complete (icon only) and Start/Stop stay
|
|
3636
|
+
inline, on open rows; a completed row carries the burger alone. A row whose
|
|
3637
|
+
`:Task` has no `taskId` is unactionable and shows a "Needs repair" marker with no
|
|
3638
|
+
burger. While a row's timer runs, Edit time, Reset and Delete are refused and
|
|
3639
|
+
each tooltip says why. Delete opens a confirm anchored beside the row that names
|
|
3640
|
+
the task and its logged time before it writes.
|
|
3641
|
+
|
|
3642
|
+
**Observability.** `[task-delete]` emits one lifeline per deletion keyed by
|
|
3643
|
+
`deletionId`: `op=request` (carrying `confirmed`), `op=guard`, `op=reject
|
|
3644
|
+
reason=…`, `op=persisted` and `op=error`. `op=persisted` carries `remaining=`,
|
|
3645
|
+
a **fresh read after the delete** counting what still holds that `taskId` — the
|
|
3646
|
+
delete's own counters agree with themselves by construction, so only the read-back
|
|
3647
|
+
proves the rows are gone. A non-zero `remaining` also emits `op=incomplete` at
|
|
3648
|
+
error level.
|
|
3649
|
+
|
|
3650
|
+
Separately, `/tasks-list` reconciles time records against tasks on **every**
|
|
3651
|
+
compute and reports `op=orphan-time-records`, or
|
|
3652
|
+
`op=orphan-time-records-census-failed` when the census itself could not run. This
|
|
3653
|
+
is not aimed at `task-delete`, which is atomic and self-verifying: it catches any
|
|
3654
|
+
other path that removes a `:Task` without its records — the graph trash tooling
|
|
3655
|
+
handles `:Task` and knows nothing about `:TimeEntry` or `:TimeAdjustment`, and
|
|
3656
|
+
strands both silently.
|
|
3657
|
+
|
|
3538
3658
|
### Skills
|
|
3539
3659
|
|
|
3540
3660
|
The Skills surface (`/skills`) is the admin view of the account's operator-
|
|
@@ -4131,9 +4251,20 @@ a browser that is already running it re-ran the same launch check and changed
|
|
|
4131
4251
|
nothing anyone could see, so it now appears only when the browser is unreachable
|
|
4132
4252
|
or has no tabs open.
|
|
4133
4253
|
|
|
4134
|
-
The
|
|
4135
|
-
the
|
|
4136
|
-
|
|
4254
|
+
The browser opens beside the conversation from the globe button at the top
|
|
4255
|
+
right of the chat header. That button is there only while the browser is
|
|
4256
|
+
closed; once it is open you close it with the cross at the top right of the
|
|
4257
|
+
browser itself, and the button comes back. Beside the cross is a full-screen
|
|
4258
|
+
control that gives the browser the whole width and hides the chat, and the same
|
|
4259
|
+
control returns you to the split.
|
|
4260
|
+
|
|
4261
|
+
Drag the divider between the chat and the browser to change how the width is
|
|
4262
|
+
shared. Double-click it to go back to an even split. The chat can never be
|
|
4263
|
+
pushed below a readable width, and the choice is remembered for next time. The
|
|
4264
|
+
pane is hidden on narrow screens where there is no room for two columns, and
|
|
4265
|
+
the page is rendered at the pane's own size so it fills it.
|
|
4266
|
+
|
|
4267
|
+
There is no Browser item in the sidebar: the browser lives beside the chat.
|
|
4137
4268
|
|
|
4138
4269
|
Tab opening and closing go to the admin server, which is the only thing that can
|
|
4139
4270
|
reach the browser's control port. Typing an address, going back and going
|
|
@@ -9,6 +9,16 @@ Invoked by the admin agent directly.
|
|
|
9
9
|
|
|
10
10
|
This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
|
|
11
11
|
|
|
12
|
+
## 2026-07-31 (0.1.538)
|
|
13
|
+
|
|
14
|
+
- A message from someone outside your team now also reaches the account's manager in their own chat, written in plain English, and each account can turn the separate raw notification off.
|
|
15
|
+
- The device browser pane can be toggled, opened full screen and resized.
|
|
16
|
+
|
|
17
|
+
## 2026-07-31 (0.1.537)
|
|
18
|
+
|
|
19
|
+
- A sub-account's public agent now runs entirely in that account: its own folder, its own settings, and no falling back to the main account's agent. A standing check reports an account switched on with no agent of its own.
|
|
20
|
+
- Task rows keep Complete and Start to hand, with the rest behind a menu, and a task can now be deleted along with its time records after a confirm. A standing check reports any time record left behind.
|
|
21
|
+
|
|
12
22
|
## 2026-07-31 (0.1.536)
|
|
13
23
|
|
|
14
24
|
- You can now switch on a public agent per account to answer WhatsApp messages from people outside your team, with a standing check that reports an account switched on but pointing at an agent that no longer exists.
|
|
@@ -23,6 +23,7 @@ Load these when users ask about Maxy features or need guidance:
|
|
|
23
23
|
- **Telegram** → `references/telegram-guide.md` — Telegram setup, the bot, daily use
|
|
24
24
|
- **Settings** → `references/settings.md` — output style, effort level, account preferences
|
|
25
25
|
- **Access Control** → `references/access-control.md` — who can chat with your public agent, invitations, authentication
|
|
26
|
+
- **What wakes a session** → `references/channel-wake-and-prompt.md` — the five things that start or resume a WhatsApp session, the `source` value each carries, which agent directory composes the prompt for which role, and the four switches on the public-inbound path. Load before writing an instruction into any role file, or when an agent cannot say why it woke.
|
|
26
27
|
- **Projects** → `references/projects-guide.md` — creating projects, tracking health, phase transitions, completing
|
|
27
28
|
- **Workflows** → `references/workflows.md` — saving and running multi-step processes, declared inputs, scheduling, run history
|
|
28
29
|
- **Slides** → `references/slides.md` — generating, theming, and reviewing HTML slide decks with `/slides`
|
|
@@ -51,6 +52,7 @@ Load these when performing admin tasks or diagnosing platform behaviour:
|
|
|
51
52
|
- references/telegram-guide.md
|
|
52
53
|
- references/settings.md
|
|
53
54
|
- references/access-control.md
|
|
55
|
+
- references/channel-wake-and-prompt.md
|
|
54
56
|
- references/projects-guide.md
|
|
55
57
|
- references/troubleshooting.md
|
|
56
58
|
- references/platform.md
|
|
@@ -258,6 +258,46 @@ scheduling heartbeat and report on every tick including zero:
|
|
|
258
258
|
`op=dispatch-mode-none` (a routine that fires and delivers nothing) and
|
|
259
259
|
`op=null-timezone` (a routine that fires at the box's hour).
|
|
260
260
|
|
|
261
|
+
### Tasks
|
|
262
|
+
|
|
263
|
+
The Tasks surface (`/tasks`) lists the account's `:Task` nodes on Open,
|
|
264
|
+
Completed and Clients tabs and records billable time against each one. Its
|
|
265
|
+
routes are all account-scoped:
|
|
266
|
+
|
|
267
|
+
| Mount | Purpose |
|
|
268
|
+
|---|---|
|
|
269
|
+
| `/tasks-list` | Open/Completed partition, plus the standing censuses described below. |
|
|
270
|
+
| `/task-timer-start`, `/task-timer-stop` | Open and close a `:TimeEntry`. Timers on different tasks run concurrently; there is no auto-stop. |
|
|
271
|
+
| `/task-complete` | Status → completed. Does **not** stop a running timer. |
|
|
272
|
+
| `/task-time-adjust` | Manual time edit. Writes one `:TimeAdjustment`; 409s while the task has an open `:TimeEntry`. |
|
|
273
|
+
| `/task-delete` | Removes the `:Task` **and** every `:TimeEntry` and `:TimeAdjustment` on it. Nothing is recoverable and there is no restore route. 409s while the task has an open `:TimeEntry`; 404s a wrong-account task. |
|
|
274
|
+
| `/task-assign`, `/task-session-open`, `/task-session-link` | Assignee writes, and the Session control's spawn-or-resume path. |
|
|
275
|
+
|
|
276
|
+
**Row controls.** Each row carries a burger — the same `SessionRowActions`
|
|
277
|
+
component the sidebar's session rows use — holding Edit time, Reset logged time
|
|
278
|
+
to 0:00, Session and Delete. Only Complete (icon only) and Start/Stop stay
|
|
279
|
+
inline, on open rows; a completed row carries the burger alone. A row whose
|
|
280
|
+
`:Task` has no `taskId` is unactionable and shows a "Needs repair" marker with no
|
|
281
|
+
burger. While a row's timer runs, Edit time, Reset and Delete are refused and
|
|
282
|
+
each tooltip says why. Delete opens a confirm anchored beside the row that names
|
|
283
|
+
the task and its logged time before it writes.
|
|
284
|
+
|
|
285
|
+
**Observability.** `[task-delete]` emits one lifeline per deletion keyed by
|
|
286
|
+
`deletionId`: `op=request` (carrying `confirmed`), `op=guard`, `op=reject
|
|
287
|
+
reason=…`, `op=persisted` and `op=error`. `op=persisted` carries `remaining=`,
|
|
288
|
+
a **fresh read after the delete** counting what still holds that `taskId` — the
|
|
289
|
+
delete's own counters agree with themselves by construction, so only the read-back
|
|
290
|
+
proves the rows are gone. A non-zero `remaining` also emits `op=incomplete` at
|
|
291
|
+
error level.
|
|
292
|
+
|
|
293
|
+
Separately, `/tasks-list` reconciles time records against tasks on **every**
|
|
294
|
+
compute and reports `op=orphan-time-records`, or
|
|
295
|
+
`op=orphan-time-records-census-failed` when the census itself could not run. This
|
|
296
|
+
is not aimed at `task-delete`, which is atomic and self-verifying: it catches any
|
|
297
|
+
other path that removes a `:Task` without its records — the graph trash tooling
|
|
298
|
+
handles `:Task` and knows nothing about `:TimeEntry` or `:TimeAdjustment`, and
|
|
299
|
+
strands both silently.
|
|
300
|
+
|
|
261
301
|
### Skills
|
|
262
302
|
|
|
263
303
|
The Skills surface (`/skills`) is the admin view of the account's operator-
|
|
@@ -854,9 +894,20 @@ a browser that is already running it re-ran the same launch check and changed
|
|
|
854
894
|
nothing anyone could see, so it now appears only when the browser is unreachable
|
|
855
895
|
or has no tabs open.
|
|
856
896
|
|
|
857
|
-
The
|
|
858
|
-
the
|
|
859
|
-
|
|
897
|
+
The browser opens beside the conversation from the globe button at the top
|
|
898
|
+
right of the chat header. That button is there only while the browser is
|
|
899
|
+
closed; once it is open you close it with the cross at the top right of the
|
|
900
|
+
browser itself, and the button comes back. Beside the cross is a full-screen
|
|
901
|
+
control that gives the browser the whole width and hides the chat, and the same
|
|
902
|
+
control returns you to the split.
|
|
903
|
+
|
|
904
|
+
Drag the divider between the chat and the browser to change how the width is
|
|
905
|
+
shared. Double-click it to go back to an even split. The chat can never be
|
|
906
|
+
pushed below a readable width, and the choice is remembered for next time. The
|
|
907
|
+
pane is hidden on narrow screens where there is no room for two columns, and
|
|
908
|
+
the page is rendered at the pane's own size so it fills it.
|
|
909
|
+
|
|
910
|
+
There is no Browser item in the sidebar: the browser lives beside the chat.
|
|
860
911
|
|
|
861
912
|
Tab opening and closing go to the admin server, which is the only thing that can
|
|
862
913
|
reach the browser's control port. Typing an address, going back and going
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# What wakes a session, and what is in its prompt
|
|
2
|
+
|
|
3
|
+
Two questions an agent cannot answer from inside itself, and both have caused real faults: *what woke me?* and *which files did my prompt come from?* This page answers both. When the answer is not here, the logs have it: `logs-read --tail server`, filtered on the `[whatsapp:*]` tags.
|
|
4
|
+
|
|
5
|
+
## What wakes a session
|
|
6
|
+
|
|
7
|
+
Five things start or resume a WhatsApp session. They are distinguishable, and the distinction matters, because two of them are written by the platform rather than by the person named as the sender.
|
|
8
|
+
|
|
9
|
+
**An admin phone or an account manager messages the line.** This resumes that person's own admin session. There is no per-account admin session: the session id is a hash of the account plus the person, so one person messaging from two channels resumes one thread, and two managers on one account have two threads.
|
|
10
|
+
|
|
11
|
+
**A public sender messages the line.** Three things follow, in this order, and each is independent of the ones after it.
|
|
12
|
+
|
|
13
|
+
1. The account's registered managers are notified, verbatim, unless the account has turned that off. This fires before any agent starts, so it survives a spawn that fails.
|
|
14
|
+
2. Every bound manager's own admin session is woken with a `public-relay` turn carrying the sender's message as fenced content. This is the carrier for anything the account needs to do about the visitor: translate, put a request to a registered counterparty, carry the answer back.
|
|
15
|
+
3. The account's public agent answers the visitor, if the account has one and has enabled it. If not, the visitor receives nothing at all, and steps 1 and 2 have already happened regardless.
|
|
16
|
+
|
|
17
|
+
**The scheduler fires a due event.** This raises a `schedule` turn on the destination's own session, carrying the event's prompt.
|
|
18
|
+
|
|
19
|
+
**A passive account-manager binding receives a message.** This spawns a constrained one-shot that files a task and never replies. It cannot hold a conversation or answer against existing state.
|
|
20
|
+
|
|
21
|
+
## Reading why you woke
|
|
22
|
+
|
|
23
|
+
Every turn carries a `source`. Three values exist:
|
|
24
|
+
|
|
25
|
+
| `source` | Meaning |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `user` | A real person sent this message to the line. Also the value when the field is absent. |
|
|
28
|
+
| `schedule` | The scheduler wrote this turn. The named destination did not send it. |
|
|
29
|
+
| `public-relay` | The platform wrote this turn because a public visitor messaged the account. The named sender is a manager being told, not someone who wrote to you. |
|
|
30
|
+
|
|
31
|
+
A `public-relay` turn also says so in its own first two lines. If a turn claims a manager wrote something they plainly would not have written, check the source before answering them.
|
|
32
|
+
|
|
33
|
+
## Which files compose the prompt
|
|
34
|
+
|
|
35
|
+
The system prompt is assembled per spawn from one agent directory, and **which directory depends on the role**:
|
|
36
|
+
|
|
37
|
+
- A `role:'public'` spawn reads `agents/<the account's public-agent slug>/`.
|
|
38
|
+
- Every other role reads `agents/admin/`.
|
|
39
|
+
|
|
40
|
+
From that one directory it takes `IDENTITY.md` and `SOUL.md`, verbatim.
|
|
41
|
+
|
|
42
|
+
The consequence catches people out: **an instruction written into `agents/admin/SOUL.md` is never read by a public spawn**, and an instruction written into a public agent's `SOUL.md` is never read by an admin turn. Before writing an instruction into a role file, decide which role will actually be running when it needs to apply. If the answer is "the admin agent, woken by a stranger's message", the file is `agents/admin/SOUL.md` and the trigger is the `public-relay` dispatch above.
|
|
43
|
+
|
|
44
|
+
A public agent's directory also carries `KNOWLEDGE.md` and `config.json`. An account can hold as many public agents as it likes; only one is bound to WhatsApp at a time, and that binding is per account.
|
|
45
|
+
|
|
46
|
+
## The switches, and what each one stops
|
|
47
|
+
|
|
48
|
+
Four settings sit on this path. They are not interchangeable, and reaching for the wrong one is how an operator ends up silencing more than they meant.
|
|
49
|
+
|
|
50
|
+
| Setting | What it stops | What it leaves running |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `dmPolicy: disabled` | Public senders are refused at the gate. | Admin and manager inbound. |
|
|
53
|
+
| `publicAgentEnabled: false` | The visitor gets no reply. | The manager notification and the relay. |
|
|
54
|
+
| `managerNotifyEnabled: false` | The verbatim copy of the visitor's message. | The relay, and any reply the woken turn sends. |
|
|
55
|
+
| `dispatchInbound: false` | Every agent turn on that account's socket. | Storage: messages are still stored and readable. |
|
|
56
|
+
|
|
57
|
+
`managerNotifyEnabled` is the fine-grained one, and the one to reach for when the notifications are unwanted but the work is not. Unbinding a manager is not a substitute: the manager registry is shared by the notification, the relay and `whatsapp-notify-manager`, so unbinding silences all three.
|
|
58
|
+
|
|
59
|
+
Read the current value with `whatsapp-config {action:'get-manager-notify-enabled'}` and set it with `set-manager-notify-enabled`. Both are scoped to the calling session's own account.
|
|
60
|
+
|
|
61
|
+
## When the answer is not here
|
|
62
|
+
|
|
63
|
+
`logs-read --tail server` and filter on the tags. One public inbound writes this chain, in order, with `account=` and `sender=` on each line:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
[whatsapp:wire] op=stanza
|
|
67
|
+
[wa-notify] op=eligible reason=<how the sender was admitted>
|
|
68
|
+
[whatsapp:route] op=routed agentType=public
|
|
69
|
+
[whatsapp:notify] op=public-inbound enabled=0|1 outcome=sent|failed|no-recipients|disabled
|
|
70
|
+
[whatsapp:relay] op=dispatch outcome=sent|failed|no-recipients
|
|
71
|
+
[whatsapp:public-agent] op=route enabled=… slug=… spawned=0|1
|
|
72
|
+
[whatsapp:public-agent] op=spawn-ok | op=spawn-failed
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Three standing audits reconcile that chain rather than waiting for someone to notice. `op=public-notify-reconcile` reports `gap`, `relayGap` and `englishGap`, each of which counts a step that a routed inbound reached and the next one did not. `op=spawn-census` reports attempts against successes. A non-zero gap, or attempts with no successes, is a fault even when nothing has been reported.
|
|
@@ -82,8 +82,10 @@ Activate when the user asks about WhatsApp — connecting, linking, checking sta
|
|
|
82
82
|
- Group observation — group messages are stored, persisted, and browsable, but the agent never responds in a group (observation-only)
|
|
83
83
|
- DM policy enforcement (open, allowlist, disabled)
|
|
84
84
|
- Conversation browsing — list active conversations, read message history, inspect group metadata
|
|
85
|
+
- Inbound media surface — `extractMessage` ([`inbound/extract.ts`](../../ui/app/lib/whatsapp/inbound/extract.ts)) handles image, audio, video, document and sticker messages, giving the agent the caption where one exists and a synthetic `[document: <name>]` / `[sticker]` label where none does, so a media message never reaches the agent as an empty body
|
|
85
86
|
- Inbound voice note transcription (OGG Opus → text via local whisper.cpp)
|
|
86
87
|
- Outbound voice note normalization (OGG Opus via ffmpeg)
|
|
88
|
+
- Inbound location sharing — a shared pin arrives as `[location: <name> — <address> — <lat>,<lon>]` and a live share as `[live location: <lat>,<lon>]`, on the same `extracted.text` field that carries a voice transcript, so an agent can read and relay a coordinate as text with no further platform work. A live share is enrolled for updates so the pin keeps moving rather than freezing at the first fix, and the operator's `/whatsapp` reader draws a map on the bubble. **There is no outbound location send:** the agent cannot put a pin into a chat, only text and documents. See [channels-whatsapp.md](references/channels-whatsapp.md)
|
|
87
89
|
- Auto-reconnection with exponential backoff
|
|
88
90
|
- Business hours gating — public messages gated by `OpeningHoursSpecification` from Neo4j, with optional after-hours auto-reply via the `afterHoursMessage` config field
|
|
89
91
|
|
|
@@ -175,10 +175,12 @@ lifelineTool(server, "whatsapp-config", [
|
|
|
175
175
|
" - add-registered-party (register a known non-operator counterparty — a driver, a supplier, a contractor — on an account's own number; requires `phone`, `account` and `party`. Their messages to THAT account's number reach its PUBLIC agent carrying the label, and they are NOT granted admin. Use this, not add-account-manager, for anyone who is not an operator of the account)",
|
|
176
176
|
" - remove-registered-party (clear a phone's registered-party registration; requires `phone`)",
|
|
177
177
|
" - list-registered-parties (show phone → account and label)",
|
|
178
|
-
" - set-public-agent (set the
|
|
179
|
-
" - get-public-agent (show the resolved public-agent slug for DMs)",
|
|
180
|
-
" - list-public-agents (slug and display name for each
|
|
181
|
-
" - set-public-agent-enabled (turn public-agent answering on or off; requires boolean `enabled`.
|
|
178
|
+
" - set-public-agent (set the public-agent slug for non-admin DMs; requires `slug`. Scoped to your own account by default; a house admin may pass `accountId` to set another account's, or omit it to set the install-wide top-level default. The slug must name an agent that exists in THAT account's own agents directory.)",
|
|
179
|
+
" - get-public-agent (show the resolved public-agent slug for DMs on this account. A sub-account resolves only its own agent — it never inherits the house's.)",
|
|
180
|
+
" - list-public-agents (slug and display name for each agent THIS account can name as its public agent, from its own agents directory)",
|
|
181
|
+
" - set-public-agent-enabled (turn public-agent answering on or off; requires boolean `enabled`. Scoped to your own account by default; a house admin may pass `accountId` for another, or omit it for the install-wide top-level default. Refuses to enable when no public agent is configured for that scope, or when the configured slug has no agent on disk. OFF by default: while off, a public sender's message is stored and the account managers are notified, but the sender receives nothing)",
|
|
182
|
+
" - set-manager-notify-enabled (turn the raw public-inbound notification on or off for this account; requires boolean `enabled`. ON by default. While on, every message from a public sender is forwarded verbatim to the account's registered managers. Turning it off stops that copy and leaves the admin relay, and the translated reply that relay produces, untouched — so a manager who wants the summary but not the raw firehose says so here rather than by unbinding themselves. Scoped to your own account; a house admin may pass `accountId` for another, or omit it for the install-wide top-level default.)",
|
|
183
|
+
" - get-manager-notify-enabled (read whether the raw public-inbound notification fires for this account, and whether that value is the account's own or the default)",
|
|
182
184
|
" - update-config (change any WhatsApp config field — pass `fields` as a JSON string of field/value pairs)",
|
|
183
185
|
" - get-config (return the full current WhatsApp config object)",
|
|
184
186
|
" - set-dispatch-inbound (turn a paired socket into an OBSERVE socket or back; requires `accountId` and boolean `dispatchInbound`. false = the account's WhatsApp is read-only: messages are stored and readable but never dispatched to an agent. Scoped to your own account.)",
|
|
@@ -187,7 +189,7 @@ lifelineTool(server, "whatsapp-config", [
|
|
|
187
189
|
" - schema (return all config field definitions — names, types, defaults, descriptions, constraints)",
|
|
188
190
|
" - list-groups (return WhatsApp groups the account belongs to — names, JIDs, participant counts)",
|
|
189
191
|
"Groups are observation-only — the agent never responds in a group, so there is no per-group config. list-groups still lists them for reference.",
|
|
190
|
-
"Public-agent
|
|
192
|
+
"Public-agent resolution at routing time (DMs): an account answers with the agent named in its OWN config, from its OWN agents directory. There is no inheritance — a sub-account that names no agent of its own answers nobody. The top-level publicAgent is the HOUSE's own value, not a default for everyone.",
|
|
191
193
|
"A phone may be a house admin AND a sub-account manager at the same time — both registrations are accepted, and the number the sender messaged decides which scope they get: the house number gives house scope, the sub-account's own number gives that sub-account's scope. A manager bound to a sub-account with no number of its own is reached through the house number. A manager phone is not reachable by whatsapp-send-admin (message it directly instead).",
|
|
192
194
|
"Three inbound sender classes exist: an OPERATOR (an admin phone or an account manager — admin session), a REGISTERED PARTY (a known counterparty — public session carrying its label), and an unknown visitor (whatever the DM policy allows). A registered party is never granted admin, and the registration applies only on the registered account's own number: on the house number they take the house DM policy like anyone else. A phone cannot hold a party registration and an admin or manager registration at the same time — either write is refused with reason 'party-registration-shadowed', because the operator arms are checked first and the party registration would never be reached.",
|
|
193
195
|
"Phone numbers must be E.164 format (e.g. +441234567890).",
|
|
@@ -206,6 +208,8 @@ lifelineTool(server, "whatsapp-config", [
|
|
|
206
208
|
"get-public-agent",
|
|
207
209
|
"list-public-agents",
|
|
208
210
|
"set-public-agent-enabled",
|
|
211
|
+
"set-manager-notify-enabled",
|
|
212
|
+
"get-manager-notify-enabled",
|
|
209
213
|
"update-config",
|
|
210
214
|
"get-config",
|
|
211
215
|
"set-dispatch-inbound",
|
|
@@ -220,9 +224,9 @@ lifelineTool(server, "whatsapp-config", [
|
|
|
220
224
|
account: z.string().optional().describe("Account UUID whose own number this phone is a known counterparty on (required for add-registered-party). Their messages to THAT account's number reach its public agent; on the house number they take the house DM policy instead."),
|
|
221
225
|
party: z.string().optional().describe("Label for this counterparty (required for add-registered-party), e.g. 'driver'. Lowercase letters, digits and hyphens, 1-64 characters. The platform carries it to the session and never interprets it; the account's own skill decides what it means."),
|
|
222
226
|
slug: z.string().optional().describe("Agent slug (required for set-public-agent)"),
|
|
223
|
-
enabled: z.boolean().optional().describe("On/off value (required for set-public-agent-enabled). true = public senders are answered by the resolved public agent; false = they receive nothing."),
|
|
227
|
+
enabled: z.boolean().optional().describe("On/off value (required for set-public-agent-enabled and set-manager-notify-enabled). For set-public-agent-enabled: true = public senders are answered by the resolved public agent; false = they receive nothing. For set-manager-notify-enabled: true = every public inbound is forwarded verbatim to the account's managers; false = it is not, and the admin relay is unaffected."),
|
|
224
228
|
fields: z.string().optional().describe('JSON string of config fields to update (required for update-config, e.g. \'{"dmPolicy":"open","allowFrom":["*"]}\')'),
|
|
225
|
-
accountId: z.string().optional().describe('Account ID. Defaults to the calling session’s own account; the house admin may pass another account id explicitly. Used by list-groups, set-dispatch-inbound, set-account-policy and get-
|
|
229
|
+
accountId: z.string().optional().describe('Account ID. Defaults to the calling session’s own account; the house admin may pass another account id explicitly. Used by list-groups, set-dispatch-inbound, set-account-policy, get-account-policy, set-public-agent, get-public-agent, set-public-agent-enabled, list-public-agents, set-manager-notify-enabled and get-manager-notify-enabled.'),
|
|
226
230
|
dispatchInbound: z.boolean().optional().describe("For set-dispatch-inbound: false makes the account an observe socket (stored + readable, never dispatched); true restores agent dispatch."),
|
|
227
231
|
policy: z.object({
|
|
228
232
|
dmPolicy: z.enum(["open", "allowlist", "disabled"]).optional(),
|
|
@@ -295,6 +299,17 @@ lifelineTool(server, "whatsapp-config", [
|
|
|
295
299
|
const sourceLabel = source ? ` (source: ${source})` : "";
|
|
296
300
|
return textResult(`WhatsApp public agent: ${agentSlug}${sourceLabel}`);
|
|
297
301
|
}
|
|
302
|
+
// Task 2277 — its own formatter. The tail fallback renders `result.message`,
|
|
303
|
+
// which this read does not carry, so without this the agent would be told
|
|
304
|
+
// "Done." and learn nothing about the value it asked for.
|
|
305
|
+
if (action === "get-manager-notify-enabled") {
|
|
306
|
+
const on = result.enabled === true;
|
|
307
|
+
const source = result.source;
|
|
308
|
+
const sourceLabel = source === "default" ? " (the default; this account has set no value of its own)" : ` (source: ${source})`;
|
|
309
|
+
return textResult(on
|
|
310
|
+
? `Raw public-inbound notification is ON${sourceLabel}. Every message from a public sender is forwarded verbatim to this account's managers.`
|
|
311
|
+
: `Raw public-inbound notification is OFF${sourceLabel}. Public senders' messages are not forwarded verbatim; the admin relay and its reply are unaffected.`);
|
|
312
|
+
}
|
|
298
313
|
if (action === "list-public-agents") {
|
|
299
314
|
const agents = result.agents ?? [];
|
|
300
315
|
if (agents.length === 0)
|