@rubytech/create-maxy-code 0.1.538 → 0.1.539

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 (195) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +28 -2
  3. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +7 -0
  4. package/payload/platform/plugins/docs/references/admin-ui.md +26 -0
  5. package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +1 -1
  6. package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
  7. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-description.test.d.ts +2 -0
  8. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-description.test.d.ts.map +1 -0
  9. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-description.test.js +59 -0
  10. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-description.test.js.map +1 -0
  11. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -1
  12. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +6 -0
  13. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -1
  14. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
  15. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +10 -6
  16. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
  17. package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +11 -7
  18. package/payload/platform/services/webchat-channel/dist/notification.d.ts +31 -0
  19. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  20. package/payload/platform/services/webchat-channel/dist/notification.js +31 -1
  21. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  22. package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
  23. package/payload/platform/services/webchat-channel/dist/server.js +15 -1
  24. package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
  25. package/payload/server/{chunk-FQO5RDWZ.js → chunk-S4FIEM3L.js} +60 -10
  26. package/payload/server/{manager-EBZQRV5V.js → manager-5QV2EAZ2.js} +1 -1
  27. package/payload/server/public/activity.html +5 -5
  28. package/payload/server/public/agents.html +4 -4
  29. package/payload/server/public/assets/{AdminLoginScreens-CfXQwKIY.js → AdminLoginScreens-BpHnjHp_.js} +1 -1
  30. package/payload/server/public/assets/AdminLoginScreens-BpHnjHp_.js.br +0 -0
  31. package/payload/server/public/assets/AdminLoginScreens-BpHnjHp_.js.gz +0 -0
  32. package/payload/server/public/assets/AdminShell-D24evn4t.js +2 -0
  33. package/payload/server/public/assets/AdminShell-D24evn4t.js.br +0 -0
  34. package/payload/server/public/assets/AdminShell-D24evn4t.js.gz +0 -0
  35. package/payload/server/public/assets/{activity-h8-sjHz9.js → activity-ZVUTB5uZ.js} +1 -1
  36. package/payload/server/public/assets/activity-ZVUTB5uZ.js.br +0 -0
  37. package/payload/server/public/assets/activity-ZVUTB5uZ.js.gz +0 -0
  38. package/payload/server/public/assets/{admin-BzhhLC7C.js → admin-XJqQz3QJ.js} +1 -1
  39. package/payload/server/public/assets/admin-XJqQz3QJ.js.br +0 -0
  40. package/payload/server/public/assets/admin-XJqQz3QJ.js.gz +0 -0
  41. package/payload/server/public/assets/{agents-DLuhIIgn.js → agents-Dc9SNC9N.js} +1 -1
  42. package/payload/server/public/assets/agents-Dc9SNC9N.js.br +0 -0
  43. package/payload/server/public/assets/agents-Dc9SNC9N.js.gz +0 -0
  44. package/payload/server/public/assets/{browser-FAnTY27n.js → browser-CCedMIga.js} +1 -1
  45. package/payload/server/public/assets/browser-CCedMIga.js.br +0 -0
  46. package/payload/server/public/assets/browser-CCedMIga.js.gz +0 -0
  47. package/payload/server/public/assets/calendar-BDoMLP2j.js +1 -0
  48. package/payload/server/public/assets/calendar-BDoMLP2j.js.br +0 -0
  49. package/payload/server/public/assets/calendar-BDoMLP2j.js.gz +0 -0
  50. package/payload/server/public/assets/chat-BAhoqTY9.js +1 -0
  51. package/payload/server/public/assets/chat-BAhoqTY9.js.br +0 -0
  52. package/payload/server/public/assets/chat-BAhoqTY9.js.gz +0 -0
  53. package/payload/server/public/assets/chevron-left-NGLHgPjF.js +1 -0
  54. package/payload/server/public/assets/chevron-right-B4B7OkG8.js +1 -0
  55. package/payload/server/public/assets/chevron-right-B4B7OkG8.js.br +0 -0
  56. package/payload/server/public/assets/clock-7IMwxJdH.js +1 -0
  57. package/payload/server/public/assets/clock-7IMwxJdH.js.br +0 -0
  58. package/payload/server/public/assets/clock-7IMwxJdH.js.gz +0 -0
  59. package/payload/server/public/assets/{copy-Rde_TL9Q.js → copy-PkR4EFD6.js} +1 -1
  60. package/payload/server/public/assets/copy-PkR4EFD6.js.br +0 -0
  61. package/payload/server/public/assets/copy-PkR4EFD6.js.gz +0 -0
  62. package/payload/server/public/assets/data-yQYhw2IR.js +1 -0
  63. package/payload/server/public/assets/data-yQYhw2IR.js.br +1 -0
  64. package/payload/server/public/assets/data-yQYhw2IR.js.gz +0 -0
  65. package/payload/server/public/assets/{file-text-DMCUVg80.js → file-text-BA9p7TLU.js} +1 -1
  66. package/payload/server/public/assets/file-text-BA9p7TLU.js.br +0 -0
  67. package/payload/server/public/assets/file-text-BA9p7TLU.js.gz +0 -0
  68. package/payload/server/public/assets/{graph-Bo-WcsKS.js → graph-C8cQ6Nqf.js} +1 -1
  69. package/payload/server/public/assets/graph-C8cQ6Nqf.js.br +0 -0
  70. package/payload/server/public/assets/graph-C8cQ6Nqf.js.gz +0 -0
  71. package/payload/server/public/assets/{graph-labels-g-JodMWQ.js → graph-labels-CiK412vE.js} +1 -1
  72. package/payload/server/public/assets/graph-labels-CiK412vE.js.br +0 -0
  73. package/payload/server/public/assets/graph-labels-CiK412vE.js.gz +0 -0
  74. package/payload/server/public/assets/{operator-X_LcOnAI.js → operator-CaQAEUBu.js} +1 -1
  75. package/payload/server/public/assets/operator-CaQAEUBu.js.br +0 -0
  76. package/payload/server/public/assets/operator-CaQAEUBu.js.gz +0 -0
  77. package/payload/server/public/assets/page-CpJpyoPS.js +32 -0
  78. package/payload/server/public/assets/page-CpJpyoPS.js.br +0 -0
  79. package/payload/server/public/assets/page-CpJpyoPS.js.gz +0 -0
  80. package/payload/server/public/assets/page-D0ShNyGE.js +1 -0
  81. package/payload/server/public/assets/page-D0ShNyGE.js.br +0 -0
  82. package/payload/server/public/assets/page-D0ShNyGE.js.gz +0 -0
  83. package/payload/server/public/assets/{play-CraN5kUU.js → play-huZpq9ra.js} +1 -1
  84. package/payload/server/public/assets/play-huZpq9ra.js.br +0 -0
  85. package/payload/server/public/assets/play-huZpq9ra.js.gz +0 -0
  86. package/payload/server/public/assets/{public--NUjaJ-q.js → public--8RWWGjF.js} +1 -1
  87. package/payload/server/public/assets/public--8RWWGjF.js.br +0 -0
  88. package/payload/server/public/assets/public--8RWWGjF.js.gz +0 -0
  89. package/payload/server/public/assets/{rotate-ccw-DshFgg2T.js → rotate-ccw-g7l4ZSR3.js} +1 -1
  90. package/payload/server/public/assets/rotate-ccw-g7l4ZSR3.js.br +0 -0
  91. package/payload/server/public/assets/rotate-ccw-g7l4ZSR3.js.gz +0 -0
  92. package/payload/server/public/assets/{routines-CfAS4gri.js → routines-DzFcPNMb.js} +1 -1
  93. package/payload/server/public/assets/routines-DzFcPNMb.js.br +0 -0
  94. package/payload/server/public/assets/routines-DzFcPNMb.js.gz +0 -0
  95. package/payload/server/public/assets/{skills-CM6ryWpx.js → skills-5vl_Bt9U.js} +1 -1
  96. package/payload/server/public/assets/skills-5vl_Bt9U.js.br +0 -0
  97. package/payload/server/public/assets/skills-5vl_Bt9U.js.gz +0 -0
  98. package/payload/server/public/assets/{tasks-B07m9LIA.js → tasks-DQHtJ8Pq.js} +1 -1
  99. package/payload/server/public/assets/tasks-DQHtJ8Pq.js.br +0 -0
  100. package/payload/server/public/assets/tasks-DQHtJ8Pq.js.gz +0 -0
  101. package/payload/server/public/assets/{triangle-alert-CKHyS_gC.js → triangle-alert-Cy4Z0uFv.js} +1 -1
  102. package/payload/server/public/assets/triangle-alert-Cy4Z0uFv.js.br +0 -0
  103. package/payload/server/public/assets/triangle-alert-Cy4Z0uFv.js.gz +0 -0
  104. package/payload/server/public/assets/{useCopyFeedback-CmawEsVi.js → useCopyFeedback-BwK1RhmF.js} +1 -1
  105. package/payload/server/public/assets/useCopyFeedback-BwK1RhmF.js.br +0 -0
  106. package/payload/server/public/assets/useCopyFeedback-BwK1RhmF.js.gz +0 -0
  107. package/payload/server/public/assets/{useSubAccountSwitcher-yN4lhH2x.css → useSubAccountSwitcher-DCkivCBS.css} +1 -1
  108. package/payload/server/public/assets/useSubAccountSwitcher-DCkivCBS.css.br +0 -0
  109. package/payload/server/public/assets/{useSubAccountSwitcher-yN4lhH2x.css.gz → useSubAccountSwitcher-DCkivCBS.css.gz} +0 -0
  110. package/payload/server/public/assets/{useVoiceRecorder-DpQTvvqD.js → useVoiceRecorder-DgyAGgmF.js} +1 -1
  111. package/payload/server/public/assets/useVoiceRecorder-DgyAGgmF.js.br +0 -0
  112. package/payload/server/public/assets/useVoiceRecorder-DgyAGgmF.js.gz +0 -0
  113. package/payload/server/public/assets/{wrench-BPOEUNJB.js → wrench-CICORrSc.js} +1 -1
  114. package/payload/server/public/assets/wrench-CICORrSc.js.br +0 -0
  115. package/payload/server/public/assets/wrench-CICORrSc.js.gz +0 -0
  116. package/payload/server/public/browser.html +4 -4
  117. package/payload/server/public/calendar.html +8 -7
  118. package/payload/server/public/chat.html +13 -13
  119. package/payload/server/public/data.html +11 -11
  120. package/payload/server/public/graph.html +9 -9
  121. package/payload/server/public/index.html +14 -14
  122. package/payload/server/public/operator.html +14 -14
  123. package/payload/server/public/public.html +13 -13
  124. package/payload/server/public/routines.html +6 -6
  125. package/payload/server/public/skills.html +5 -5
  126. package/payload/server/public/tasks.html +6 -6
  127. package/payload/server/server.js +149 -46
  128. package/payload/server/public/assets/AdminLoginScreens-CfXQwKIY.js.br +0 -0
  129. package/payload/server/public/assets/AdminLoginScreens-CfXQwKIY.js.gz +0 -0
  130. package/payload/server/public/assets/AdminShell-y0_mzlDF.js +0 -2
  131. package/payload/server/public/assets/AdminShell-y0_mzlDF.js.br +0 -0
  132. package/payload/server/public/assets/AdminShell-y0_mzlDF.js.gz +0 -0
  133. package/payload/server/public/assets/activity-h8-sjHz9.js.br +0 -0
  134. package/payload/server/public/assets/activity-h8-sjHz9.js.gz +0 -0
  135. package/payload/server/public/assets/admin-BzhhLC7C.js.br +0 -0
  136. package/payload/server/public/assets/admin-BzhhLC7C.js.gz +0 -0
  137. package/payload/server/public/assets/agents-DLuhIIgn.js.br +0 -0
  138. package/payload/server/public/assets/agents-DLuhIIgn.js.gz +0 -0
  139. package/payload/server/public/assets/browser-FAnTY27n.js.br +0 -0
  140. package/payload/server/public/assets/browser-FAnTY27n.js.gz +0 -0
  141. package/payload/server/public/assets/calendar-C_PkqlkQ.js +0 -1
  142. package/payload/server/public/assets/calendar-C_PkqlkQ.js.br +0 -0
  143. package/payload/server/public/assets/calendar-C_PkqlkQ.js.gz +0 -0
  144. package/payload/server/public/assets/chat-DW81Ra89.js +0 -1
  145. package/payload/server/public/assets/chat-DW81Ra89.js.br +0 -0
  146. package/payload/server/public/assets/chat-DW81Ra89.js.gz +0 -0
  147. package/payload/server/public/assets/chevron-left-zV-WRzHb.js +0 -1
  148. package/payload/server/public/assets/chevron-left-zV-WRzHb.js.br +0 -0
  149. package/payload/server/public/assets/chevron-right-fnINU4bx.js +0 -1
  150. package/payload/server/public/assets/chevron-right-fnINU4bx.js.br +0 -0
  151. package/payload/server/public/assets/clock-2fSxBl6d.js +0 -1
  152. package/payload/server/public/assets/clock-2fSxBl6d.js.br +0 -0
  153. package/payload/server/public/assets/copy-Rde_TL9Q.js.br +0 -0
  154. package/payload/server/public/assets/copy-Rde_TL9Q.js.gz +0 -0
  155. package/payload/server/public/assets/data-S5g4wIDe.js +0 -1
  156. package/payload/server/public/assets/data-S5g4wIDe.js.br +0 -0
  157. package/payload/server/public/assets/data-S5g4wIDe.js.gz +0 -0
  158. package/payload/server/public/assets/file-text-DMCUVg80.js.br +0 -0
  159. package/payload/server/public/assets/file-text-DMCUVg80.js.gz +0 -0
  160. package/payload/server/public/assets/graph-Bo-WcsKS.js.br +0 -0
  161. package/payload/server/public/assets/graph-Bo-WcsKS.js.gz +0 -0
  162. package/payload/server/public/assets/graph-labels-g-JodMWQ.js.br +0 -0
  163. package/payload/server/public/assets/graph-labels-g-JodMWQ.js.gz +0 -0
  164. package/payload/server/public/assets/operator-X_LcOnAI.js.br +0 -0
  165. package/payload/server/public/assets/operator-X_LcOnAI.js.gz +0 -0
  166. package/payload/server/public/assets/page-Cz0p1mBf.js +0 -1
  167. package/payload/server/public/assets/page-Cz0p1mBf.js.br +0 -0
  168. package/payload/server/public/assets/page-Cz0p1mBf.js.gz +0 -0
  169. package/payload/server/public/assets/page-Dzs3MsFL.js +0 -32
  170. package/payload/server/public/assets/page-Dzs3MsFL.js.br +0 -0
  171. package/payload/server/public/assets/page-Dzs3MsFL.js.gz +0 -0
  172. package/payload/server/public/assets/play-CraN5kUU.js.br +0 -0
  173. package/payload/server/public/assets/play-CraN5kUU.js.gz +0 -0
  174. package/payload/server/public/assets/public--NUjaJ-q.js.br +0 -0
  175. package/payload/server/public/assets/public--NUjaJ-q.js.gz +0 -0
  176. package/payload/server/public/assets/rotate-ccw-DshFgg2T.js.br +0 -0
  177. package/payload/server/public/assets/rotate-ccw-DshFgg2T.js.gz +0 -0
  178. package/payload/server/public/assets/routines-CfAS4gri.js.br +0 -0
  179. package/payload/server/public/assets/routines-CfAS4gri.js.gz +0 -0
  180. package/payload/server/public/assets/skills-CM6ryWpx.js.br +0 -0
  181. package/payload/server/public/assets/skills-CM6ryWpx.js.gz +0 -0
  182. package/payload/server/public/assets/tasks-B07m9LIA.js.br +0 -0
  183. package/payload/server/public/assets/tasks-B07m9LIA.js.gz +0 -0
  184. package/payload/server/public/assets/triangle-alert-CKHyS_gC.js.br +0 -0
  185. package/payload/server/public/assets/triangle-alert-CKHyS_gC.js.gz +0 -0
  186. package/payload/server/public/assets/useCopyFeedback-CmawEsVi.js.br +0 -0
  187. package/payload/server/public/assets/useCopyFeedback-CmawEsVi.js.gz +0 -0
  188. package/payload/server/public/assets/useSubAccountSwitcher-yN4lhH2x.css.br +0 -0
  189. package/payload/server/public/assets/useVoiceRecorder-DpQTvvqD.js.br +0 -0
  190. package/payload/server/public/assets/useVoiceRecorder-DpQTvvqD.js.gz +0 -0
  191. package/payload/server/public/assets/wrench-BPOEUNJB.js.br +0 -0
  192. package/payload/server/public/assets/wrench-BPOEUNJB.js.gz +0 -0
  193. /package/payload/server/public/assets/{useSubAccountSwitcher-CVHqKsqK.js → useSubAccountSwitcher-C7TtIbR3.js} +0 -0
  194. /package/payload/server/public/assets/{useSubAccountSwitcher-CVHqKsqK.js.br → useSubAccountSwitcher-C7TtIbR3.js.br} +0 -0
  195. /package/payload/server/public/assets/{useSubAccountSwitcher-CVHqKsqK.js.gz → useSubAccountSwitcher-C7TtIbR3.js.gz} +0 -0
@@ -195,7 +195,7 @@ import {
195
195
  waitForConnection,
196
196
  warnNonUuidAccount,
197
197
  writeChatMetadataMerged
198
- } from "./chunk-FQO5RDWZ.js";
198
+ } from "./chunk-S4FIEM3L.js";
199
199
  import {
200
200
  ACCOUNTS_DIR,
201
201
  BIN_DIR,
@@ -231,6 +231,7 @@ import {
231
231
  getDefaultAccountId,
232
232
  getRecentMessages,
233
233
  getSession,
234
+ getUserTimezone,
234
235
  isActiveAgentSlug,
235
236
  isValidAccountId,
236
237
  linkConversationTranscript,
@@ -5024,7 +5025,7 @@ async function defaultHttpGet(url) {
5024
5025
  }
5025
5026
  }
5026
5027
  async function defaultResolve(accountId) {
5027
- const { resolveSocket: resolveSocket2 } = await import("./manager-EBZQRV5V.js");
5028
+ const { resolveSocket: resolveSocket2 } = await import("./manager-5QV2EAZ2.js");
5028
5029
  return resolveSocket2(accountId);
5029
5030
  }
5030
5031
  async function fetchAvatar(accountId, jid, deps = {}) {
@@ -10651,6 +10652,12 @@ function unwrapChannel(text) {
10651
10652
  var COMPOSED_ADMIN_FRAME = /^(?:## Scheduled automation \(platform-verified\)\n[\s\S]*?\n\n)?(?:## Standing rules\n[\s\S]*?\n\n)?## Context\n([\s\S]*)\n\n## Instruction\n[\s\S]*$/;
10652
10653
  var SCHEDULED_MARKER_PREFIX = "## Scheduled automation (platform-verified)\n";
10653
10654
  var UPLOAD_ANNOTATION = /(?:\n\n)?\[The operator uploaded \d+ file\(s\): [\s\S]*\]$/;
10655
+ var REPLY_NOTE = /^\[In reply to an earlier message in this conversation(?:\.|: "([\s\S]*?)")\]\n\n/;
10656
+ function splitReplyNote(payload) {
10657
+ const m = payload.match(REPLY_NOTE);
10658
+ if (!m) return { body: payload };
10659
+ return { body: payload.slice(m[0].length), quoted: { body: m[1] ?? "" } };
10660
+ }
10654
10661
  function rawFromComposedAdminFrame(text) {
10655
10662
  const context = text.match(COMPOSED_ADMIN_FRAME)?.[1];
10656
10663
  if (context === void 0) return void 0;
@@ -10662,8 +10669,10 @@ function uploadCountOf(text) {
10662
10669
  const n = Number(text.match(UPLOAD_COUNT)?.[1]);
10663
10670
  return Number.isInteger(n) && n > 0 ? n : void 0;
10664
10671
  }
10665
- function operatorInboundTurn(u, ts) {
10666
- const rawBody = rawFromComposedAdminFrame(u.text);
10672
+ function operatorInboundTurn(u, ts, uuid) {
10673
+ const framed = rawFromComposedAdminFrame(u.text);
10674
+ const split = framed === void 0 ? void 0 : splitReplyNote(framed);
10675
+ const rawBody = split?.body;
10667
10676
  const uploadCount = uploadCountOf(u.text);
10668
10677
  const scheduled = rawBody !== void 0 && u.text.startsWith(SCHEDULED_MARKER_PREFIX);
10669
10678
  const text = rawBody === void 0 ? u.text.replace(PUBLIC_UPLOAD_ANNOTATION, "") : u.text;
@@ -10674,7 +10683,9 @@ function operatorInboundTurn(u, ts) {
10674
10683
  ...u.source ? { source: u.source } : {},
10675
10684
  ...rawBody !== void 0 ? { rawBody } : {},
10676
10685
  ...uploadCount !== void 0 ? { uploadCount } : {},
10677
- ...scheduled ? { scheduled: true } : {}
10686
+ ...scheduled ? { scheduled: true } : {},
10687
+ ...uuid !== void 0 ? { uuid } : {},
10688
+ ...split?.quoted !== void 0 ? { quoted: split.quoted } : {}
10678
10689
  };
10679
10690
  }
10680
10691
  function asString(content) {
@@ -10718,17 +10729,17 @@ function erroredToolUseIds(lines) {
10718
10729
  }
10719
10730
  return ids;
10720
10731
  }
10721
- function assistantTurns(content, ts, promoteFinal = false, erroredIds = /* @__PURE__ */ new Set()) {
10732
+ function assistantTurns(content, ts, promoteFinal = false, erroredIds = /* @__PURE__ */ new Set(), uuid) {
10722
10733
  if (!Array.isArray(content)) return [];
10723
10734
  const out = [];
10724
10735
  for (const block of content) {
10725
10736
  if (!block || typeof block !== "object") continue;
10726
10737
  const b = block;
10727
10738
  if (b.type === "text" && typeof b.text === "string") {
10728
- out.push(promoteFinal && b.text.trim().length > 0 ? { kind: "agent-webchat-final", text: b.text, ts } : { kind: "agent-text", text: b.text, ts });
10739
+ out.push(promoteFinal && b.text.trim().length > 0 ? { kind: "agent-webchat-final", text: b.text, ts, ...uuid !== void 0 ? { uuid } : {} } : { kind: "agent-text", text: b.text, ts });
10729
10740
  } else if (b.type === "tool_use" && (b.name === WA_REPLY_TOOL || b.name === WEBCHAT_REPLY_TOOL || b.name === CHANNEL_REPLY_TOOL)) {
10730
10741
  const text = b.input?.text;
10731
- out.push({ kind: "agent-reply", text: typeof text === "string" ? text : "", ts });
10742
+ out.push({ kind: "agent-reply", text: typeof text === "string" ? text : "", ts, ...uuid !== void 0 ? { uuid } : {} });
10732
10743
  } else if (b.type === "tool_use" && (b.name === WA_REPLY_DOCUMENT_TOOL || b.name === CHANNEL_REPLY_DOCUMENT_TOOL)) {
10733
10744
  const input = b.input ?? {};
10734
10745
  const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
@@ -10764,6 +10775,10 @@ function toolResultTurns(content, ts) {
10764
10775
  }
10765
10776
  return out;
10766
10777
  }
10778
+ function parseTranscriptLine(line) {
10779
+ const turns = parseTranscript([line]);
10780
+ return turns.length > 0 ? turns[0] : null;
10781
+ }
10767
10782
  function lastTurnTs(turns) {
10768
10783
  for (let i = turns.length - 1; i >= 0; i--) {
10769
10784
  if (turns[i].ts) return turns[i].ts;
@@ -10773,12 +10788,12 @@ function lastTurnTs(turns) {
10773
10788
  function isModelGated(turns) {
10774
10789
  return turns.some((t) => t.kind === "agent-error" && t.code === "model_unavailable");
10775
10790
  }
10776
- function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress) {
10791
+ function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, uuid) {
10777
10792
  const armed2 = queuedPendingSuppress.get(u.text) ?? 0;
10778
10793
  if (armed2 > 0) {
10779
10794
  queuedPendingSuppress.set(u.text, armed2 - 1);
10780
10795
  } else {
10781
- out.push(operatorInboundTurn(u, ts));
10796
+ out.push(operatorInboundTurn(u, ts, uuid));
10782
10797
  }
10783
10798
  }
10784
10799
  function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(), turnCtx = {}) {
@@ -10807,7 +10822,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
10807
10822
  }
10808
10823
  const isEnd = msg?.stop_reason === "end_turn";
10809
10824
  const promoteFinal = isEnd && turnCtx.lastChannelSource === "webchat";
10810
- out.push(...assistantTurns(msg?.content, ts, promoteFinal, erroredIds));
10825
+ out.push(...assistantTurns(msg?.content, ts, promoteFinal, erroredIds, asString(row.uuid) ?? void 0));
10811
10826
  continue;
10812
10827
  }
10813
10828
  if (row.type === "queue-operation" && row.operation === "enqueue") {
@@ -10829,7 +10844,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
10829
10844
  const u = unwrapChannel(att.prompt);
10830
10845
  turnCtx.lastChannelSource = u.source;
10831
10846
  if (!isScheduleSource(att.prompt)) {
10832
- renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress);
10847
+ renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, asString(row.uuid) ?? void 0);
10833
10848
  }
10834
10849
  }
10835
10850
  continue;
@@ -10858,7 +10873,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
10858
10873
  const u = unwrapChannel(text);
10859
10874
  turnCtx.lastChannelSource = u.source;
10860
10875
  if (!isScheduleSource(text)) {
10861
- renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress);
10876
+ renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, asString(row.uuid) ?? void 0);
10862
10877
  }
10863
10878
  } else if (row.isMeta === true) {
10864
10879
  out.push({ kind: "harness-internal", text, ts });
@@ -11158,15 +11173,27 @@ data: ${JSON.stringify(obj)}
11158
11173
  const { buf, end } = readFrom(jsonlPath, 0);
11159
11174
  offset = end;
11160
11175
  let n = 0;
11176
+ let delivered = 0;
11177
+ let identified = 0;
11178
+ let quotedTurns = 0;
11161
11179
  for (const turn of parseTranscript(buf.toString("utf8").split("\n"), queuedPendingSuppress, turnCtx)) {
11162
11180
  send(controller, enrichAdminAttachments(enrichDownloads(turn, scratchpadRoot), sessionId), offset);
11163
11181
  actx.lastParentTurns.push(turn);
11164
11182
  if (turn.kind === "agent-error") {
11165
11183
  console.log(`[wa-stream] op=agent-error conn=${connId} sessionId=${sessionId} code=${turn.code} source=jsonl`);
11166
11184
  }
11185
+ if (turn.kind === "operator-inbound" || turn.kind === "agent-reply" || turn.kind === "agent-webchat-final") {
11186
+ delivered++;
11187
+ if (turn.uuid !== void 0) identified++;
11188
+ if (turn.kind === "operator-inbound" && turn.quoted !== void 0) quotedTurns++;
11189
+ }
11167
11190
  n++;
11168
11191
  }
11169
11192
  console.log(`[wa-stream] op=backlog conn=${connId} turns=${n} bytes=${end}`);
11193
+ console.log(`[session-reader] op=turn-id-census sessionId=${sessionId} delivered=${delivered} identified=${identified}`);
11194
+ if (quotedTurns > 0) {
11195
+ console.log(`[session-reader] op=chat-quote sessionId=${sessionId} quoted=${quotedTurns}`);
11196
+ }
11170
11197
  } catch {
11171
11198
  controller.enqueue(encoder.encode('event: error\ndata: "read-failed"\n\n'));
11172
11199
  }
@@ -12249,6 +12276,35 @@ function locateSession(sessionId) {
12249
12276
  const meta = readSidecarMeta(join27(dir, `${sessionId}.meta.json`));
12250
12277
  return { projectDir: dir, channel: meta.channel, accountId: meta.accountId };
12251
12278
  }
12279
+ function resolveQuoted(sessionId, quotedUuid, sendId) {
12280
+ if (!quotedUuid) return void 0;
12281
+ const log2 = (present, chars) => console.log(`[webchat:inbound] op=reply-quote sendId=${sendId} target=${quotedUuid} present=${present} chars=${chars}`);
12282
+ if (!sessionId || !UUID_PATTERN.test(quotedUuid)) {
12283
+ log2(false, 0);
12284
+ return void 0;
12285
+ }
12286
+ const { projectDir } = locateSession(sessionId);
12287
+ if (!projectDir) {
12288
+ log2(false, 0);
12289
+ return void 0;
12290
+ }
12291
+ let line;
12292
+ try {
12293
+ const needle = `"uuid":"${quotedUuid}"`;
12294
+ line = readFileSync21(join27(projectDir, `${sessionId}.jsonl`), "utf8").split("\n").find((l) => l.includes(needle));
12295
+ } catch {
12296
+ log2(false, 0);
12297
+ return void 0;
12298
+ }
12299
+ if (line === void 0) {
12300
+ log2(false, 0);
12301
+ return void 0;
12302
+ }
12303
+ const turn = parseTranscriptLine(line);
12304
+ const text = turn === null ? void 0 : turn.kind === "operator-inbound" ? turn.rawBody ?? turn.text : "text" in turn ? turn.text : void 0;
12305
+ log2(true, text ? [...text].length : 0);
12306
+ return text ? { id: quotedUuid, text } : { id: quotedUuid };
12307
+ }
12252
12308
  function locateSidecar(sessionId) {
12253
12309
  const cfg = claudeConfigDir();
12254
12310
  if (!cfg) return null;
@@ -12501,6 +12557,7 @@ function createWebchatRoutes(deps) {
12501
12557
  let target;
12502
12558
  let files = [];
12503
12559
  let clearSignal = false;
12560
+ let quotedUuid;
12504
12561
  const contentType = c.req.header("content-type") ?? "";
12505
12562
  const isMultipart = contentType.startsWith("multipart/form-data");
12506
12563
  if (isMultipart) {
@@ -12522,6 +12579,8 @@ function createWebchatRoutes(deps) {
12522
12579
  target = rawTarget;
12523
12580
  }
12524
12581
  clearSignal = formData.get("clearSignal") === "true";
12582
+ const rawQuoted = formData.get("quotedUuid");
12583
+ quotedUuid = typeof rawQuoted === "string" && rawQuoted ? rawQuoted : void 0;
12525
12584
  } else {
12526
12585
  const body = await c.req.json().catch(() => null);
12527
12586
  const typed = body?.text;
@@ -12537,6 +12596,7 @@ function createWebchatRoutes(deps) {
12537
12596
  target = rawTarget;
12538
12597
  }
12539
12598
  clearSignal = body?.clearSignal === true;
12599
+ quotedUuid = typeof body?.quotedUuid === "string" && body.quotedUuid ? body.quotedUuid : void 0;
12540
12600
  }
12541
12601
  let deliveryKey = WEBCHAT_ADMIN_KEY;
12542
12602
  let deliverySessionId;
@@ -12656,8 +12716,9 @@ ${note}` : note;
12656
12716
  }
12657
12717
  const adminUserId = requesterUserId ?? void 0;
12658
12718
  const inboundStartedAt = Date.now();
12659
- const inbound = deliverySessionId !== void 0 ? await deps.handleInbound({ role: "admin", accountId: deliveryAccountId, key: deliveryKey, text, sessionId: deliverySessionId, adminUserId }) : await deps.handleInbound({ role: "admin", accountId: bootstrapAccountId, key: deliveryKey, text, adminUserId });
12660
12719
  const sendId = sendIdFromResolve ?? ++sendSeq;
12720
+ const replyContext = resolveQuoted(deliverySessionId, quotedUuid, sendId);
12721
+ const inbound = deliverySessionId !== void 0 ? await deps.handleInbound({ role: "admin", accountId: deliveryAccountId, key: deliveryKey, text, sessionId: deliverySessionId, adminUserId, ...replyContext ? { replyContext } : {} }) : await deps.handleInbound({ role: "admin", accountId: bootstrapAccountId, key: deliveryKey, text, adminUserId });
12661
12722
  let turnSessionId = deliverySessionId;
12662
12723
  if (turnSessionId === void 0) {
12663
12724
  const canonAccount = scopeAccountId ? validateAccountId2(scopeAccountId) : null;
@@ -14135,6 +14196,22 @@ var client_error_default = app13;
14135
14196
 
14136
14197
  // server/routes/admin/session.ts
14137
14198
  import { randomUUID as randomUUID5 } from "crypto";
14199
+
14200
+ // server/lib/session-timezone.ts
14201
+ async function resolveSessionTimezone(accountId) {
14202
+ try {
14203
+ const tz = await getUserTimezone(accountId);
14204
+ if (tz) return { timezone: tz, timezoneSource: "account" };
14205
+ } catch (err) {
14206
+ const message = err instanceof Error ? err.message : String(err);
14207
+ console.error(
14208
+ `[admin-session] op=timezone-resolve outcome=failed accountId=${accountId.slice(0, 8)} err="${message}"`
14209
+ );
14210
+ }
14211
+ return { timezone: null, timezoneSource: "browser" };
14212
+ }
14213
+
14214
+ // server/routes/admin/session.ts
14138
14215
  async function resolveUserIdentity(accountId, userId) {
14139
14216
  const result = await loadAdminUserName(userId);
14140
14217
  if (result.source === "neo4j") {
@@ -14172,6 +14249,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName, rol
14172
14249
  }
14173
14250
  console.log(`[session] admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId} sessionId=${initialSessionId ?? "unbound"} cacheKey=${cacheKey.slice(0, 8)}`);
14174
14251
  console.log(`[admin-session] role=${role ?? "null"} cacheKey=${cacheKey.slice(0, 8)} phase=create eager-ensured=${initialSessionId ? "true" : "false"}`);
14252
+ const { timezone, timezoneSource } = await resolveSessionTimezone(accountId);
14175
14253
  return {
14176
14254
  session_key: signedSessionToken,
14177
14255
  agent_id: "admin",
@@ -14181,6 +14259,8 @@ async function createAdminSession(accountId, thinkingView, userId, userName, rol
14181
14259
  role: role ?? null,
14182
14260
  thinkingView: effectiveThinkingView,
14183
14261
  businessName,
14262
+ timezone,
14263
+ timezoneSource,
14184
14264
  sessionId: initialSessionId
14185
14265
  };
14186
14266
  }
@@ -14225,6 +14305,7 @@ app14.get("/", async (c) => {
14225
14305
  const account = validateAccountId2(accountId);
14226
14306
  const thinkingView = account?.config.thinkingView ?? "default";
14227
14307
  const businessName = await fetchAccountName(accountId) || void 0;
14308
+ const { timezone, timezoneSource } = await resolveSessionTimezone(accountId);
14228
14309
  const role = getRoleForSession(cacheKey);
14229
14310
  console.log(`[admin-session] role=${role ?? "null"} accountId=${accountId.slice(0, 8)} cacheKey=${cacheKey.slice(0, 8)} phase=restore`);
14230
14311
  const restoredUserId = getUserIdForSession(cacheKey);
@@ -14247,6 +14328,8 @@ app14.get("/", async (c) => {
14247
14328
  role: role ?? null,
14248
14329
  thinkingView,
14249
14330
  businessName,
14331
+ timezone,
14332
+ timezoneSource,
14250
14333
  sessionId: getSessionIdForSession(cacheKey) ?? null
14251
14334
  });
14252
14335
  });
@@ -23238,6 +23321,7 @@ function serializeMeetingICS(m, opts) {
23238
23321
  }
23239
23322
  lines.push(`SUMMARY:${escapeText(m.title ?? "")}`);
23240
23323
  if (m.location) lines.push(`LOCATION:${escapeText(m.location)}`);
23324
+ if (m.description) lines.push(`DESCRIPTION:${escapeText(m.description)}`);
23241
23325
  if (opts.includeAttendees) {
23242
23326
  for (const a of m.attendees) {
23243
23327
  const line = attendeeLine(a);
@@ -23408,7 +23492,7 @@ function normalizeAttendees(raw) {
23408
23492
  };
23409
23493
  });
23410
23494
  }
23411
- var EDITABLE_FIELDS = ["title", "startsAt", "endsAt", "location", "privateNote"];
23495
+ var EDITABLE_FIELDS = ["title", "startsAt", "endsAt", "location", "description", "privateNote"];
23412
23496
  var CREATE_FIELDS = EDITABLE_FIELDS;
23413
23497
  function buildCreateParams(accountId, body) {
23414
23498
  const params = {
@@ -23426,6 +23510,7 @@ var MEETING_COLS = `elementId(m) AS id, m.title AS title,
23426
23510
  toString(m.startsAt) AS startsAt, toString(m.endsAt) AS endsAt,
23427
23511
  m.location AS location, coalesce(m.isAllDay, false) AS isAllDay,
23428
23512
  'meeting' AS kind, m.uid AS uid, m.privateNote AS privateNote,
23513
+ m.description AS description, m.timezone AS timezone,
23429
23514
  [(att:Person)-[:ATTENDED]->(m) | {name: coalesce(att.displayName, att.email), email: att.email}] AS attendees`;
23430
23515
  var MEETING_RETURN = `RETURN ${MEETING_COLS}`;
23431
23516
  function mapMeeting(r) {
@@ -23438,29 +23523,21 @@ function mapMeeting(r) {
23438
23523
  isAllDay: r.get("isAllDay"),
23439
23524
  kind: r.get("kind"),
23440
23525
  attendees: normalizeAttendees(r.get("attendees")),
23441
- privateNote: r.get("privateNote")
23526
+ privateNote: r.get("privateNote"),
23527
+ // Task 2280 — the meeting's own body, and the zone it was created in. Both
23528
+ // were stored and projected by nothing.
23529
+ description: r.get("description"),
23530
+ timezone: r.get("timezone")
23442
23531
  };
23443
23532
  }
23444
- app49.get("/meetings", requireAdminSession, async (c) => {
23445
- const cacheKey = c.var.cacheKey;
23446
- const accountId = getAccountIdForSession(cacheKey);
23447
- if (!accountId) {
23448
- console.error('[calendar] op=admin-read auth-rejected reason="no account for session"');
23449
- return c.json({ error: "Account not found for session" }, 401);
23450
- }
23451
- const from = c.req.query("from");
23452
- const to = c.req.query("to");
23453
- if (!from || !to) return c.json({ error: "from and to (ISO) required" }, 400);
23454
- const session = getSession();
23455
- try {
23456
- const res = await session.run(
23457
- `MATCH (m:Meeting {accountId: $accountId})
23533
+ var GET_UNION = `MATCH (m:Meeting {accountId: $accountId})
23458
23534
  WHERE NOT (m.endsAt IS NOT NULL AND m.endsAt <= datetime($from))
23459
23535
  AND m.startsAt < datetime($to)
23460
23536
  RETURN elementId(m) AS id, m.title AS title,
23461
23537
  toString(m.startsAt) AS startsAt, toString(m.endsAt) AS endsAt,
23462
23538
  m.location AS location, coalesce(m.isAllDay, false) AS isAllDay,
23463
23539
  'meeting' AS kind, m.privateNote AS privateNote,
23540
+ m.description AS description, m.timezone AS timezone,
23464
23541
  [(p:Person)-[:ATTENDED]->(m) | {name: coalesce(p.displayName, p.email), email: p.email}] AS attendees
23465
23542
  UNION
23466
23543
  MATCH (e:Event {accountId: $accountId})
@@ -23472,6 +23549,7 @@ app49.get("/meetings", requireAdminSession, async (c) => {
23472
23549
  CASE WHEN e.endDate IS NULL THEN null ELSE toString(datetime(e.endDate)) END AS endsAt,
23473
23550
  e.location AS location, false AS isAllDay,
23474
23551
  'event' AS kind, null AS privateNote,
23552
+ e.description AS description, e.timezone AS timezone,
23475
23553
  [] AS attendees
23476
23554
  UNION
23477
23555
  MATCH (t:Task {accountId: $accountId})
@@ -23482,9 +23560,21 @@ app49.get("/meetings", requireAdminSession, async (c) => {
23482
23560
  toString(datetime(t.dueDate)) AS startsAt, null AS endsAt,
23483
23561
  null AS location, true AS isAllDay,
23484
23562
  'task' AS kind, null AS privateNote,
23485
- [] AS attendees`,
23486
- { accountId, from, to }
23487
- );
23563
+ t.description AS description, null AS timezone,
23564
+ [] AS attendees`;
23565
+ app49.get("/meetings", requireAdminSession, async (c) => {
23566
+ const cacheKey = c.var.cacheKey;
23567
+ const accountId = getAccountIdForSession(cacheKey);
23568
+ if (!accountId) {
23569
+ console.error('[calendar] op=admin-read auth-rejected reason="no account for session"');
23570
+ return c.json({ error: "Account not found for session" }, 401);
23571
+ }
23572
+ const from = c.req.query("from");
23573
+ const to = c.req.query("to");
23574
+ if (!from || !to) return c.json({ error: "from and to (ISO) required" }, 400);
23575
+ const session = getSession();
23576
+ try {
23577
+ const res = await session.run(GET_UNION, { accountId, from, to });
23488
23578
  const meetings = res.records.map(mapMeeting).sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime());
23489
23579
  const counts = { meeting: 0, event: 0, task: 0 };
23490
23580
  for (const m of meetings) counts[m.kind]++;
@@ -23632,6 +23722,7 @@ app49.get("/meetings/:id/ics", requireAdminSession, async (c) => {
23632
23722
  startsAt: mapped.startsAt,
23633
23723
  endsAt: mapped.endsAt,
23634
23724
  location: mapped.location,
23725
+ description: mapped.description,
23635
23726
  isAllDay: mapped.isAllDay,
23636
23727
  attendees: mapped.attendees
23637
23728
  };
@@ -23668,7 +23759,7 @@ app49.patch("/meetings/:id", requireAdminSession, async (c) => {
23668
23759
  const body = await safeJson(c) ?? {};
23669
23760
  const fields = EDITABLE_FIELDS.filter((f) => f in body);
23670
23761
  if (fields.length === 0) {
23671
- return c.json({ error: "No editable field supplied (title, startsAt, endsAt, location, privateNote)" }, 400);
23762
+ return c.json({ error: "No editable field supplied (title, startsAt, endsAt, location, description, privateNote)" }, 400);
23672
23763
  }
23673
23764
  const setClauses = fields.map(
23674
23765
  (f) => f === "startsAt" || f === "endsAt" ? `m.${f} = datetime($${f})` : `m.${f} = $${f}`
@@ -29155,7 +29246,7 @@ async function dispatchAdminRelay(deps, event) {
29155
29246
  var routed = 0;
29156
29247
  var notified = 0;
29157
29248
  var relayed = 0;
29158
- var englishNotified = 0;
29249
+ var answered = 0;
29159
29250
  function noteRouted() {
29160
29251
  routed += 1;
29161
29252
  }
@@ -29165,24 +29256,24 @@ function noteNotified() {
29165
29256
  function noteRelayed() {
29166
29257
  relayed += 1;
29167
29258
  }
29168
- function noteEnglishNotified() {
29169
- englishNotified += 1;
29259
+ function noteAnswered() {
29260
+ answered += 1;
29170
29261
  }
29171
29262
  function readPublicInboundCensus() {
29172
29263
  return {
29173
29264
  routed,
29174
29265
  notified,
29175
29266
  relayed,
29176
- englishNotified,
29267
+ answered,
29177
29268
  gap: Math.max(0, routed - notified),
29178
29269
  relayGap: Math.max(0, routed - relayed),
29179
- englishGap: Math.max(0, relayed - englishNotified)
29270
+ answerGap: Math.max(0, relayed - answered)
29180
29271
  };
29181
29272
  }
29182
29273
  function runPublicNotifyReconcile() {
29183
29274
  const c = readPublicInboundCensus();
29184
29275
  console.error(
29185
- `[whatsapp:notify] op=public-notify-reconcile routed=${c.routed} notified=${c.notified} relayed=${c.relayed} englishNotified=${c.englishNotified} gap=${c.gap} relayGap=${c.relayGap} englishGap=${c.englishGap}`
29276
+ `[whatsapp:notify] op=public-notify-reconcile routed=${c.routed} notified=${c.notified} relayed=${c.relayed} answered=${c.answered} gap=${c.gap} relayGap=${c.relayGap} answerGap=${c.answerGap}`
29186
29277
  );
29187
29278
  }
29188
29279
 
@@ -29924,13 +30015,25 @@ var WebchatGateway = class _WebchatGateway {
29924
30015
  `[preference-inject] op=inject channel=web accountId=${input.accountId} injected=${injected} owner=${owner8} source=${res.source}`
29925
30016
  );
29926
30017
  }
30018
+ const messageId = `wc-${++this.seq}`;
29927
30019
  this.hub.deliver(
29928
- { key: input.key, text: input.text, messageId: `wc-${++this.seq}`, standingRules },
30020
+ {
30021
+ key: input.key,
30022
+ text: input.text,
30023
+ messageId,
30024
+ standingRules,
30025
+ ...input.replyContext ? { replyContext: input.replyContext } : {}
30026
+ },
29929
30027
  Date.now()
29930
30028
  );
30029
+ if (input.replyContext) {
30030
+ console.error(
30031
+ `[webchat:inbound] op=reply-quote-forward key=${keyDisplay(input.key)} messageId=${messageId} target=${input.replyContext.id}`
30032
+ );
30033
+ }
29931
30034
  if (willDeliverNow) this.lastDeliveredAt.set(input.key, Date.now());
29932
30035
  console.error(
29933
- `[webchat:inbound] op=received key=${keyDisplay(input.key)} role=${input.role} bytes=${bytes} delivery=${willDeliverNow ? "immediate" : "queued"}`
30036
+ `[webchat:inbound] op=received key=${keyDisplay(input.key)} messageId=${messageId} role=${input.role} bytes=${bytes} delivery=${willDeliverNow ? "immediate" : "queued"}`
29934
30037
  );
29935
30038
  if (input.role === "admin") {
29936
30039
  this.recoverable.set(input.key, {
@@ -33595,7 +33698,7 @@ init({
33595
33698
  }
33596
33699
  try {
33597
33700
  const houseForRelay = resolveAccount();
33598
- let englishCounted = false;
33701
+ let answerCounted = false;
33599
33702
  const relayAccountId = msg.effectiveAccountId;
33600
33703
  await dispatchAdminRelay(
33601
33704
  {
@@ -33603,9 +33706,9 @@ init({
33603
33706
  handleInbound: (input) => waGateway.handleInbound(input),
33604
33707
  sendText: async (accountId, destination, text) => {
33605
33708
  await sendWhatsAppTextForRelay(accountId, destination, text);
33606
- if (!englishCounted) {
33607
- englishCounted = true;
33608
- noteEnglishNotified();
33709
+ if (!answerCounted) {
33710
+ answerCounted = true;
33711
+ noteAnswered();
33609
33712
  }
33610
33713
  },
33611
33714
  houseAccountId: msg.accountId