taskuary 0.3.6.2__tar.gz → 0.3.6.3__tar.gz

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 (520) hide show
  1. {taskuary-0.3.6.2/taskuary.egg-info → taskuary-0.3.6.3}/PKG-INFO +5 -5
  2. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/README.md +4 -4
  3. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/pyproject.toml +5 -4
  4. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/assistant.py +24 -12
  5. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/channels.py +9 -0
  6. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/cli.py +28 -0
  7. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/coder.py +61 -1
  8. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/concierge.py +25 -17
  9. taskuary-0.3.6.3/taskuary/connectorcatalog.json +2397 -0
  10. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/funnel.py +13 -5
  11. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/funnel_selection.py +3 -9
  12. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/general.py +6 -0
  13. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/ingest.py +50 -1
  14. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_all.py +1 -1
  15. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_unread.py +23 -4
  16. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/remote_assistant.py +196 -60
  17. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/reports.py +45 -0
  18. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/routing.py +5 -4
  19. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/scopes.py +3 -0
  20. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/selfclose.py +19 -2
  21. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/server.py +24 -4
  22. taskuary-0.3.6.3/taskuary/settings_schema.json +483 -0
  23. taskuary-0.3.6.3/taskuary/social.py +241 -0
  24. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/store.py +79 -26
  25. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/agent.md +2 -1
  26. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/coder.md +4 -3
  27. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/triage.md +5 -5
  28. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/triage.py +37 -13
  29. taskuary-0.3.6.3/taskuary/triage_kind_rules.json +18 -0
  30. taskuary-0.3.6.2/taskuary/web/assets/HubView-BjL1r12I.js → taskuary-0.3.6.3/taskuary/web/assets/HubView-BiQkrYWk.js +1 -1
  31. taskuary-0.3.6.2/taskuary/web/assets/StudioScene-DXPO8NCz.js → taskuary-0.3.6.3/taskuary/web/assets/StudioScene-GfvlCIY_.js +1 -1
  32. taskuary-0.3.6.2/taskuary/web/assets/index-D5A7qNvG.js → taskuary-0.3.6.3/taskuary/web/assets/index-58W9N7HI.js +148 -148
  33. taskuary-0.3.6.2/taskuary/web/assets/index-Bsld__8Y.css → taskuary-0.3.6.3/taskuary/web/assets/index-Cpzoo5vC.css +1 -1
  34. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/index.html +2 -2
  35. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/workerstate.py +5 -1
  36. {taskuary-0.3.6.2 → taskuary-0.3.6.3/taskuary.egg-info}/PKG-INFO +5 -5
  37. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary.egg-info/SOURCES.txt +15 -4
  38. taskuary-0.3.6.3/tests/test_agent_reply.py +101 -0
  39. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_api.py +2 -0
  40. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant.py +21 -3
  41. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_blocks.py +17 -10
  42. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_reactions.py +5 -4
  43. taskuary-0.3.6.3/tests/test_banner_is_one_rule.py +32 -0
  44. taskuary-0.3.6.3/tests/test_chat_echo_mail.py +73 -0
  45. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chatformat.py +2 -1
  46. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_core.py +3 -3
  47. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_digest_brief.py +4 -4
  48. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_docs_and_llm.py +9 -6
  49. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_every_card_is_reachable.py +22 -0
  50. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_funnel.py +46 -1
  51. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_kind_dispatch.py +29 -3
  52. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_lane_vocabulary.py +8 -2
  53. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_morning_line.py +3 -1
  54. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_not_coding.py +8 -8
  55. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_onboarding_walk.py +1 -1
  56. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_phone_walk_ux.py +58 -4
  57. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_pypi_downloads.py +31 -2
  58. taskuary-0.3.6.3/tests/test_read_text.py +36 -0
  59. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_remote_assistant.py +7 -9
  60. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_reply_always_drafts.py +5 -5
  61. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_report_order_and_research.py +1 -1
  62. taskuary-0.3.6.3/tests/test_same_failure_is_quiet.py +30 -0
  63. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_seeded_reports.py +41 -9
  64. taskuary-0.3.6.3/tests/test_social.py +263 -0
  65. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_the_exe_ships_what_the_wheel_ships.py +11 -5
  66. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_urgent_and_handoff.py +1 -1
  67. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_verdict_shape.py +14 -0
  68. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_walk_order.py +13 -7
  69. taskuary-0.3.6.3/tests/test_workerstate_said.py +19 -0
  70. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/LICENSE +0 -0
  71. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/setup.cfg +0 -0
  72. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/__init__.py +0 -0
  73. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/acp.py +0 -0
  74. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/agents.py +0 -0
  75. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/aidefaults.py +0 -0
  76. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/aisetup.py +0 -0
  77. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/alchemy.py +0 -0
  78. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/appfacts.py +0 -0
  79. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/artifacts.py +0 -0
  80. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/assistantblocks.py +0 -0
  81. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/aws.py +0 -0
  82. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/azure.py +0 -0
  83. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/blackboard.py +0 -0
  84. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/brief.py +0 -0
  85. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/browserview.py +0 -0
  86. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/calendar.py +0 -0
  87. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/categories.py +0 -0
  88. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/chains.py +0 -0
  89. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/chatformat.py +0 -0
  90. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/chatservers.py +0 -0
  91. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/ci.py +0 -0
  92. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/cli_connections.py +0 -0
  93. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/cli_install_terminal.py +0 -0
  94. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/cliinstall.py +0 -0
  95. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/climodels.py +0 -0
  96. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/clis.py +0 -0
  97. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/clisetup.py +0 -0
  98. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/compose.py +0 -0
  99. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/config.py +0 -0
  100. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/connectorcatalog.py +0 -0
  101. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/context.py +0 -0
  102. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/continuity.py +0 -0
  103. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/counsel.py +0 -0
  104. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/databases.py +0 -0
  105. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/db.py +0 -0
  106. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/demo.py +0 -0
  107. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/deps.py +0 -0
  108. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/desktop.py +0 -0
  109. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/devtools.py +0 -0
  110. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/digest.py +0 -0
  111. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/docsync.py +0 -0
  112. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/evalset.py +0 -0
  113. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/evening.py +0 -0
  114. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/files.py +0 -0
  115. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/funnel_presentation.py +0 -0
  116. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/github.py +0 -0
  117. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/guard.py +0 -0
  118. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/handbook.py +0 -0
  119. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/handraise.py +0 -0
  120. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/histgen.py +0 -0
  121. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/hooks.py +0 -0
  122. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/hub.py +0 -0
  123. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/images.py +0 -0
  124. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/imapmail.py +0 -0
  125. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/imessage.py +0 -0
  126. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/intacct.py +0 -0
  127. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/interview.py +0 -0
  128. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/invoice_workflow.py +0 -0
  129. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/jev.py +0 -0
  130. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/knowledge.py +0 -0
  131. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/lanes.json +0 -0
  132. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/learn.py +0 -0
  133. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/learnedgraph.py +0 -0
  134. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/linkedin.py +0 -0
  135. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/live.py +0 -0
  136. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/llm.py +0 -0
  137. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/logs.py +0 -0
  138. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/markets.py +0 -0
  139. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/mcp.py +0 -0
  140. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/messengers.py +0 -0
  141. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/msauth.py +0 -0
  142. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/mssql.py +0 -0
  143. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/operations.py +0 -0
  144. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/outbound.py +0 -0
  145. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/outbox.py +0 -0
  146. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/ownwork.py +0 -0
  147. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/phone.py +0 -0
  148. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/playbooks.py +0 -0
  149. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/pm.py +0 -0
  150. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/policy.py +0 -0
  151. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/problems.py +0 -0
  152. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing.py +0 -0
  153. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_inventory.py +0 -0
  154. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_membership.py +0 -0
  155. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_navigation.py +0 -0
  156. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_order.py +0 -0
  157. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_projection.py +0 -0
  158. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_rail.py +0 -0
  159. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_reads.py +0 -0
  160. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/processing_startup.py +0 -0
  161. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/projects.py +0 -0
  162. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/promptmap.py +0 -0
  163. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/proof.py +0 -0
  164. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/proposals.py +0 -0
  165. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/quickbooks.py +0 -0
  166. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/rank.py +0 -0
  167. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/redact.py +0 -0
  168. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/research.py +0 -0
  169. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/reshape.py +0 -0
  170. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/responder.py +0 -0
  171. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/retention.py +0 -0
  172. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/robinhood.py +0 -0
  173. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/routingmemory.py +0 -0
  174. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/semantic.py +0 -0
  175. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/senders.py +0 -0
  176. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/session_artifacts.py +0 -0
  177. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/sessionfiles.py +0 -0
  178. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/settings_schema.py +0 -0
  179. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/setup.py +0 -0
  180. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/sharepoint.py +0 -0
  181. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/sheets.py +0 -0
  182. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/simplefin.py +0 -0
  183. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/skillimport.py +0 -0
  184. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/skills/soul-interview/SKILL.md +0 -0
  185. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/skills/taskuary-setup/SKILL.md +0 -0
  186. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/spawn.py +0 -0
  187. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/teller.py +0 -0
  188. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/analyst.md +0 -0
  189. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/coordinator.md +0 -0
  190. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/counsel.md +0 -0
  191. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/digest.md +0 -0
  192. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/history/counsel-0.3.3.5.md +0 -0
  193. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/how-it-works.md +0 -0
  194. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/learned.md +0 -0
  195. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/marketer.md +0 -0
  196. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/playbook.md +0 -0
  197. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/profile.md +0 -0
  198. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/researcher.md +0 -0
  199. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/soul.md +0 -0
  200. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/style.md +0 -0
  201. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/templates/trader.md +0 -0
  202. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/terminal.py +0 -0
  203. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/testing.py +0 -0
  204. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/toil.py +0 -0
  205. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/toolcatalog.py +0 -0
  206. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/treg.py +0 -0
  207. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/update.py +0 -0
  208. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/verdicts.py +0 -0
  209. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/voice.py +0 -0
  210. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/wabridge.py +0 -0
  211. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/waitroom.py +0 -0
  212. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/walk.py +0 -0
  213. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/favicon.ico +0 -0
  214. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/favicon.png +0 -0
  215. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/walk/assistant.png +0 -0
  216. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/walk/board.png +0 -0
  217. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/walk/connections.png +0 -0
  218. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/walk/docs.png +0 -0
  219. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/walk/hub.png +0 -0
  220. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/walk/reports.png +0 -0
  221. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/walk/settings.png +0 -0
  222. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/web/walk/tasks.png +0 -0
  223. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/webguard.py +0 -0
  224. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/whatsapp/bridge.mjs +0 -0
  225. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/whatsapp/package.json +0 -0
  226. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/whatsapp/policy.mjs +0 -0
  227. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/whatsapp/policy.test.mjs +0 -0
  228. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/whatsapp/roster.mjs +0 -0
  229. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/whatsapp/roster.test.mjs +0 -0
  230. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/whoami.py +0 -0
  231. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/witness.py +0 -0
  232. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/workflows.py +0 -0
  233. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary/zoho.py +0 -0
  234. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary.egg-info/dependency_links.txt +0 -0
  235. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary.egg-info/entry_points.txt +0 -0
  236. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary.egg-info/requires.txt +0 -0
  237. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/taskuary.egg-info/top_level.txt +0 -0
  238. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_acp.py +0 -0
  239. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_agent_answer_route.py +0 -0
  240. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_agent_environment.py +0 -0
  241. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_agent_identity.py +0 -0
  242. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_agent_profiles.py +0 -0
  243. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_agent_sentence.py +0 -0
  244. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_agent_source.py +0 -0
  245. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_agent_wall.py +0 -0
  246. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_ai_defaults.py +0 -0
  247. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_aisetup.py +0 -0
  248. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_alchemy.py +0 -0
  249. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_an_inbox_is_not_a_destination.py +0 -0
  250. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_answered_yourself.py +0 -0
  251. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_answers.py +0 -0
  252. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_api_worker_signals.py +0 -0
  253. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_app_uptime.py +0 -0
  254. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_appfacts.py +0 -0
  255. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_approval_interrupt.py +0 -0
  256. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_acts.py +0 -0
  257. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_can_look_things_up.py +0 -0
  258. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_doorways.py +0 -0
  259. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_ideas.py +0 -0
  260. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_notifications.py +0 -0
  261. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_opens.py +0 -0
  262. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_presentation.py +0 -0
  263. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_assistant_verdicts.py +0 -0
  264. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_async_triage.py +0 -0
  265. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_audit_chain.py +0 -0
  266. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_audit_fixes.py +0 -0
  267. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_auto_start.py +0 -0
  268. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_aws_region.py +0 -0
  269. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_blackboard.py +0 -0
  270. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_brain_layer.py +0 -0
  271. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_brain_reaches_the_session.py +0 -0
  272. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_browser_session.py +0 -0
  273. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_browser_workflow.py +0 -0
  274. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_browserview.py +0 -0
  275. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_calendar.py +0 -0
  276. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_calendar_today.py +0 -0
  277. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_categories.py +0 -0
  278. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cc.py +0 -0
  279. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cc_triage.py +0 -0
  280. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chat_doors.py +0 -0
  281. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chat_freshness.py +0 -0
  282. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chat_is_not_one_task.py +0 -0
  283. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chat_proposals.py +0 -0
  284. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chat_queue.py +0 -0
  285. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chat_relationship.py +0 -0
  286. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chat_retention.py +0 -0
  287. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chat_setup.py +0 -0
  288. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chatservers.py +0 -0
  289. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_chinese_clis.py +0 -0
  290. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_clean_context.py +0 -0
  291. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cli_connections.py +0 -0
  292. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cli_failure.py +0 -0
  293. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cli_install.py +0 -0
  294. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cli_install_terminal.py +0 -0
  295. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cli_on_fresh_path.py +0 -0
  296. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cli_setup.py +0 -0
  297. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cli_update.py +0 -0
  298. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_climodels.py +0 -0
  299. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_clis.py +0 -0
  300. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cloud.py +0 -0
  301. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cloud_sources.py +0 -0
  302. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_coder_session_continuity.py +0 -0
  303. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_completion_freshness.py +0 -0
  304. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_compose.py +0 -0
  305. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_compose_sources.py +0 -0
  306. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_compose_workflows.py +0 -0
  307. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_concierge.py +0 -0
  308. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_concierge_counsel.py +0 -0
  309. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_config.py +0 -0
  310. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_context.py +0 -0
  311. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_context_refresh_freshness.py +0 -0
  312. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_coordination.py +0 -0
  313. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_correction_doors.py +0 -0
  314. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_counsel.py +0 -0
  315. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_counsel_consumers.py +0 -0
  316. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_counsel_migration.py +0 -0
  317. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_counsel_sections.py +0 -0
  318. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_current_restore.py +0 -0
  319. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_cwlogs_paging.py +0 -0
  320. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_databases.py +0 -0
  321. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_demo.py +0 -0
  322. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_deps.py +0 -0
  323. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_desktop.py +0 -0
  324. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_devin.py +0 -0
  325. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_devtools.py +0 -0
  326. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_diff_review.py +0 -0
  327. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_dispatch_retries.py +0 -0
  328. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_docs_flow.py +0 -0
  329. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_duplicate_connectors.py +0 -0
  330. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_email_chains.py +0 -0
  331. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_email_identity_routing.py +0 -0
  332. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_ending_a_run_writes_the_answer.py +0 -0
  333. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_evening_brief.py +0 -0
  334. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_every_cli_startable.py +0 -0
  335. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_explicit_completion.py +0 -0
  336. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_factory.py +0 -0
  337. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_feed_working.py +0 -0
  338. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_files.py +0 -0
  339. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_final_answer_capture.py +0 -0
  340. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_finished_chat_stays_finished.py +0 -0
  341. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_floating_assistant.py +0 -0
  342. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_follow_up_verdict.py +0 -0
  343. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_fresh_evaluation.py +0 -0
  344. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_freshness.py +0 -0
  345. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_freshness_walk.py +0 -0
  346. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_general.py +0 -0
  347. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_github_auto.py +0 -0
  348. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_github_images.py +0 -0
  349. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_guard.py +0 -0
  350. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_handbook.py +0 -0
  351. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_handbook_topics.py +0 -0
  352. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_handoff_channels.py +0 -0
  353. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_handraise.py +0 -0
  354. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_harvest.py +0 -0
  355. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_histgen.py +0 -0
  356. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_home_is_never_yours.py +0 -0
  357. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_hooks_events.py +0 -0
  358. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_hooks_version.py +0 -0
  359. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_ideas_triage.py +0 -0
  360. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_ignore_sender.py +0 -0
  361. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_ignore_sender_quiets_its_work.py +0 -0
  362. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_images.py +0 -0
  363. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_imap_catchup.py +0 -0
  364. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_imap_history.py +0 -0
  365. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_imapmail.py +0 -0
  366. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_imessage.py +0 -0
  367. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_intacct.py +0 -0
  368. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_interrupted_work.py +0 -0
  369. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_invite_is_not_work.py +0 -0
  370. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_invoice_workflow.py +0 -0
  371. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_jev.py +0 -0
  372. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_judge_reads_the_conclusion.py +0 -0
  373. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_knowledge.py +0 -0
  374. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_learnedgraph.py +0 -0
  375. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_lifecycle.py +0 -0
  376. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_lifespan_loops_are_guarded.py +0 -0
  377. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_linkedin.py +0 -0
  378. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_linkedin_signin.py +0 -0
  379. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_live.py +0 -0
  380. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_local_file.py +0 -0
  381. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_mail_catchup.py +0 -0
  382. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_mail_fetch_cost.py +0 -0
  383. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_mail_folders.py +0 -0
  384. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_mark_read.py +0 -0
  385. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_markets.py +0 -0
  386. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_mcp.py +0 -0
  387. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_meeting_prep.py +0 -0
  388. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_message_thread.py +0 -0
  389. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_messengers.py +0 -0
  390. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_msauth.py +0 -0
  391. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_mssql.py +0 -0
  392. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_muse.py +0 -0
  393. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_noop_ending.py +0 -0
  394. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_note_retrieval.py +0 -0
  395. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_one_app_one_reason.py +0 -0
  396. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_one_pty_one_geometry.py +0 -0
  397. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_only_your_own_thread.py +0 -0
  398. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_openai_compatible_brains.py +0 -0
  399. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_operations.py +0 -0
  400. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_outbound_reports.py +0 -0
  401. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_outbox.py +0 -0
  402. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_own_rows_no_reply.py +0 -0
  403. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_playbook_setup.py +0 -0
  404. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_playbooks.py +0 -0
  405. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_pm.py +0 -0
  406. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_poll_lanes.py +0 -0
  407. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_poll_overlap.py +0 -0
  408. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_poll_parallel.py +0 -0
  409. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_poll_sources.py +0 -0
  410. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_pr_comments.py +0 -0
  411. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_problems.py +0 -0
  412. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_problems_dismiss.py +0 -0
  413. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_procedure_selection.py +0 -0
  414. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_processing_demo_replay.py +0 -0
  415. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_processing_ledger.py +0 -0
  416. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_profile_is_a_role.py +0 -0
  417. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_projects.py +0 -0
  418. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_prompt_sources.py +0 -0
  419. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_promptmap_and_catalog.py +0 -0
  420. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_proof_ci_proposals.py +0 -0
  421. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_quickbooks.py +0 -0
  422. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_qwen.py +0 -0
  423. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_rank.py +0 -0
  424. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_rate_limit.py +0 -0
  425. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_recent_closures.py +0 -0
  426. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_reclassify.py +0 -0
  427. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_redact.py +0 -0
  428. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_redact_doors.py +0 -0
  429. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_reply_attachments.py +0 -0
  430. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_reply_channels.py +0 -0
  431. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_reply_envelope.py +0 -0
  432. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_reply_needs_a_correspondent.py +0 -0
  433. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_reply_sources.py +0 -0
  434. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_reply_voice.py +0 -0
  435. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_repo_choice_triage.py +0 -0
  436. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_report_alerts.py +0 -0
  437. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_report_deliver.py +0 -0
  438. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_report_headline.py +0 -0
  439. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_report_prompt_isolation.py +0 -0
  440. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_report_reach.py +0 -0
  441. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_report_refusal.py +0 -0
  442. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_report_routing.py +0 -0
  443. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_report_triage.py +0 -0
  444. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_research.py +0 -0
  445. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_research_more.py +0 -0
  446. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_review_task.py +0 -0
  447. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_reviews.py +0 -0
  448. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_robinhood.py +0 -0
  449. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_routing_memory.py +0 -0
  450. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_s3_poll_paging.py +0 -0
  451. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_scopes.py +0 -0
  452. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_seed_fits_a_tty.py +0 -0
  453. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_selector_vocabulary.py +0 -0
  454. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_semantic.py +0 -0
  455. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_send_outcomes.py +0 -0
  456. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_send_targets.py +0 -0
  457. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_sender_trust.py +0 -0
  458. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_sender_words.py +0 -0
  459. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_session_artifacts.py +0 -0
  460. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_session_continuity.py +0 -0
  461. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_session_files.py +0 -0
  462. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_settings_schema.py +0 -0
  463. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_setup.py +0 -0
  464. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_setup_skill.py +0 -0
  465. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_setup_walk.py +0 -0
  466. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_sheets_sharepoint.py +0 -0
  467. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_simplefin.py +0 -0
  468. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_skillimport.py +0 -0
  469. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_someone_else_replied.py +0 -0
  470. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_soul_interview.py +0 -0
  471. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_startup_contract.py +0 -0
  472. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_stay_open.py +0 -0
  473. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_stay_open_doors.py +0 -0
  474. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_store_app_cli.py +0 -0
  475. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_store_hotpaths.py +0 -0
  476. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_store_pokes.py +0 -0
  477. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_sync_clock.py +0 -0
  478. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_task_ask.py +0 -0
  479. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_task_checklist.py +0 -0
  480. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_task_controls_operations.py +0 -0
  481. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_task_identity.py +0 -0
  482. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_teller.py +0 -0
  483. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_terminal.py +0 -0
  484. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_the_split_holds.py +0 -0
  485. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_toil.py +0 -0
  486. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_tracker_revisions.py +0 -0
  487. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_treg.py +0 -0
  488. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_triage_dataset.py +0 -0
  489. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_triage_errors.py +0 -0
  490. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_triage_images.py +0 -0
  491. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_triage_pr_rule.py +0 -0
  492. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_triage_retry_sweep.py +0 -0
  493. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_update.py +0 -0
  494. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_update_refused.py +0 -0
  495. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_verdict_paths.py +0 -0
  496. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_verdict_sticks.py +0 -0
  497. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_vision.py +0 -0
  498. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_voice.py +0 -0
  499. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_wabridge.py +0 -0
  500. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_wabridge_log_trim.py +0 -0
  501. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_waitroom.py +0 -0
  502. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_watermark.py +0 -0
  503. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_web_entrypoint.py +0 -0
  504. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_webguard.py +0 -0
  505. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_whatsapp_documents.py +0 -0
  506. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_whatsapp_images.py +0 -0
  507. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_whoami.py +0 -0
  508. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_why_not_started.py +0 -0
  509. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_withdrawn.py +0 -0
  510. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_witness.py +0 -0
  511. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_worker_brief.py +0 -0
  512. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_worker_events.py +0 -0
  513. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_worker_resumes.py +0 -0
  514. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_worker_scenarios.py +0 -0
  515. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_worker_surfaces.py +0 -0
  516. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_workflows.py +0 -0
  517. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_zz_chipverbs.py +0 -0
  518. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_zz_lastword.py +0 -0
  519. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_zz_setupchat.py +0 -0
  520. {taskuary-0.3.6.2 → taskuary-0.3.6.3}/tests/test_zz_toolcatalog.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskuary
3
- Version: 0.3.6.2
3
+ Version: 0.3.6.3
4
4
  Summary: Automate your job - the local-first operating system for your work: AI triage in, coding agents out.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/ldbumble/taskuary
@@ -69,7 +69,7 @@ Dynamic: license-file
69
69
 
70
70
  [![CI](https://github.com/ldbumble/taskuary/actions/workflows/ci.yml/badge.svg)](https://github.com/ldbumble/taskuary/actions/workflows/ci.yml)
71
71
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ldbumble/taskuary/badge)](https://scorecard.dev/viewer/?uri=github.com/ldbumble/taskuary)
72
- [![PyPI](https://img.shields.io/pypi/v/taskuary.svg?cacheSeconds=300&release=0.3.6.2&asof=2026-09-22g)](https://pypi.org/project/taskuary/)
72
+ [![PyPI](https://img.shields.io/pypi/v/taskuary.svg?cacheSeconds=300&release=0.3.6.3&asof=2026-09-22h)](https://pypi.org/project/taskuary/)
73
73
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776ab.svg)](https://github.com/ldbumble/taskuary)
74
74
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
75
75
  [![Stars](https://img.shields.io/github/stars/ldbumble/taskuary?style=flat&color=d4a72c&label=%E2%98%85%20stars)](https://github.com/ldbumble/taskuary/stargazers)
@@ -83,7 +83,7 @@ your agents, and brings decisions back to you. Nothing sends or ships without yo
83
83
 
84
84
  ![The Taskuary Studio assembling as work arrives and AI agents take their seats.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/hero.gif?v=workspace)
85
85
 
86
- Taskuary is early—currently **v0.3.6.2**—so breaking changes are still possible before 1.0.
86
+ Taskuary is early—currently **v0.3.6.3**—so breaking changes are still possible before 1.0.
87
87
 
88
88
  <p align="center">
89
89
  <a href="https://taskuary.com/demo/"><img
@@ -270,10 +270,10 @@ outside systems, send messages, run tools, or start agents. Its changes reset wh
270
270
 
271
271
  ## Installs
272
272
 
273
- ![Daily installs of taskuary from PyPI, mirror traffic excluded](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/downloads.svg)
273
+ ![Daily installs of taskuary from PyPI, mirror traffic excluded](https://raw.githubusercontent.com/ldbumble/taskuary/stats/downloads.svg)
274
274
 
275
275
  Updated daily from PyPI with mirror traffic excluded. The raw series is
276
- [docs/downloads.csv](docs/downloads.csv).
276
+ [downloads.csv on the `stats` branch](https://github.com/ldbumble/taskuary/blob/stats/downloads.csv).
277
277
 
278
278
  ## Documentation
279
279
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![CI](https://github.com/ldbumble/taskuary/actions/workflows/ci.yml/badge.svg)](https://github.com/ldbumble/taskuary/actions/workflows/ci.yml)
6
6
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ldbumble/taskuary/badge)](https://scorecard.dev/viewer/?uri=github.com/ldbumble/taskuary)
7
- [![PyPI](https://img.shields.io/pypi/v/taskuary.svg?cacheSeconds=300&release=0.3.6.2&asof=2026-09-22g)](https://pypi.org/project/taskuary/)
7
+ [![PyPI](https://img.shields.io/pypi/v/taskuary.svg?cacheSeconds=300&release=0.3.6.3&asof=2026-09-22h)](https://pypi.org/project/taskuary/)
8
8
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776ab.svg)](https://github.com/ldbumble/taskuary)
9
9
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
10
10
  [![Stars](https://img.shields.io/github/stars/ldbumble/taskuary?style=flat&color=d4a72c&label=%E2%98%85%20stars)](https://github.com/ldbumble/taskuary/stargazers)
@@ -18,7 +18,7 @@ your agents, and brings decisions back to you. Nothing sends or ships without yo
18
18
 
19
19
  ![The Taskuary Studio assembling as work arrives and AI agents take their seats.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/hero.gif?v=workspace)
20
20
 
21
- Taskuary is early—currently **v0.3.6.2**—so breaking changes are still possible before 1.0.
21
+ Taskuary is early—currently **v0.3.6.3**—so breaking changes are still possible before 1.0.
22
22
 
23
23
  <p align="center">
24
24
  <a href="https://taskuary.com/demo/"><img
@@ -205,10 +205,10 @@ outside systems, send messages, run tools, or start agents. Its changes reset wh
205
205
 
206
206
  ## Installs
207
207
 
208
- ![Daily installs of taskuary from PyPI, mirror traffic excluded](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/downloads.svg)
208
+ ![Daily installs of taskuary from PyPI, mirror traffic excluded](https://raw.githubusercontent.com/ldbumble/taskuary/stats/downloads.svg)
209
209
 
210
210
  Updated daily from PyPI with mirror traffic excluded. The raw series is
211
- [docs/downloads.csv](docs/downloads.csv).
211
+ [downloads.csv on the `stats` branch](https://github.com/ldbumble/taskuary/blob/stats/downloads.csv).
212
212
 
213
213
  ## Documentation
214
214
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "taskuary"
7
- version = "0.3.6.2"
7
+ version = "0.3.6.3"
8
8
  description = "Automate your job - the local-first operating system for your work: AI triage in, coding agents out."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -71,10 +71,11 @@ include = ["taskuary*"]
71
71
  [tool.setuptools.package-data]
72
72
  # whatsapp/ too: the bridge error tells you to run `node bridge.mjs` out of the package, and
73
73
  # a pip install without it is an instruction pointing at a directory that does not exist.
74
- # lanes.json is the ONE lane vocabulary the desktop imports at build time and funnel.py reads at
75
- # runtime - a wheel without it cannot name a lane at all.
74
+ # *.json: every data file at the package root is read at runtime - lanes.json is the ONE lane
75
+ # vocabulary, and triage_kind_rules.json, settings_schema.json and connectorcatalog.json were all
76
+ # missing from the wheel once (the container died on the first; 2026-09-23). Name them by glob.
76
77
  # A GLOB HERE DOES NOT CROSS A DIRECTORY SEPARATOR: `web/*` matches nothing inside `web/assets/`,
77
78
  # which is why that line exists - and `web/walk/*` had to be added after the setup walk's nine
78
79
  # screenshots shipped broken. Every new subdirectory under web/ needs its own line, or its files
79
80
  # are simply absent from the wheel while the repo tree looks right.
80
- taskuary = ["lanes.json", "web/*", "web/assets/*", "web/walk/*", "templates/*.md", "templates/history/*.md", "skills/*/*.md", "whatsapp/*"]
81
+ taskuary = ["*.json", "web/*", "web/assets/*", "web/walk/*", "templates/*.md", "templates/history/*.md", "skills/*/*.md", "whatsapp/*"]
@@ -25,7 +25,7 @@ it brings in until it says something useful and surprising"). Handed only subjec
25
25
  wrote 'no content given' in its own notes; so the check now reads WHAT PEOPLE SAID (the words of every
26
26
  human thread of the last two days, the owner's lines marked), who is OUT OF OFFICE (from auto-replies -
27
27
  a chase to someone away is worse than silence), the CALENDAR, and the actual words in every rolled-up
28
- arrival (including machine mail), with each report's schedule and each failure's cause beside the count. That is where "Yittie said exporting freezes the
28
+ arrival (including machine mail), with each report's schedule, when it last ran and what that run did, and each failure's cause beside the count. That is where "Yittie said exporting freezes the
29
29
  app - and she is in Monday's meeting" comes from.
30
30
 
31
31
  It also leaves itself a NOTE: each check ends with what it looked at and found nothing in, when
@@ -503,7 +503,7 @@ def blk_label(bid: str) -> str:
503
503
  # ── the model's pass: its own read, given what it already said ───────────────────────────────
504
504
  CONTRACT = ('\n\nYou are writing your POST on the owner\'s Timeline - the short list of things worth saying right now. You get '
505
505
  'CANDIDATES the hub found itself (each with a key), WHAT PEOPLE SAID (the words, by thread), who is OUT OF OFFICE, the '
506
- 'CALENDAR, what arrived (with each report\'s schedule and each failure\'s cause), what got done, what is open, and WHAT '
506
+ 'CALENDAR, what arrived (with each report\'s schedule, when it last ran and what that run did, and each failure\'s cause), what got done, what is open, and WHAT '
507
507
  'YOU ALREADY SAID. Answer JSON only: {"say": [{"key": "<a candidate key, or idea:<short-slug> for a thought of your own>", '
508
508
  '"text": "<one line, under 30 words, first person: the fact and what I would do - quote the phrase or name the cause when there is one>", '
509
509
  '"section": "<people|loose|ideas|systems - which part of the post this belongs under: people = what somebody said '
@@ -536,14 +536,26 @@ def _schedules(store) -> dict:
536
536
  nothing without its clock: 25 digests in two days on an on_startup report is 25 launches, not a
537
537
  scheduler bug, and two seeded reports at one timestamp is one launch, not a restart to explain."""
538
538
  from . import reports
539
- out = {}
539
+ out, st = {}, store.get_settings()
540
540
  for src in store.list_sources(active_only=False):
541
541
  if src.get('Channel') != 'report': continue
542
542
  try: c = json.loads(src.get('ConfigJson') or '{}')
543
543
  except ValueError: continue
544
- out[c.get('title') or src.get('Address')] = reports.schedule_words(c)
544
+ out[c.get('title') or src.get('Address')] = reports.schedule_words(c) + _last_run(st, src)
545
545
  return out
546
546
 
547
+ def _last_run(settings: dict, src: dict) -> str:
548
+ """'; last ran Tue 22 Sep 20:37, nothing to report so nothing was filed' - or ''. The clock alone
549
+ misled: a routed report whose judge holds a run back files NO message, so the newest mid read as
550
+ the last firing and a 140-minute timer looked stalled two runs after it fired on time (TQ-0685).
551
+ The run record on the source (reports.LAST_RUN) is the receipt the arrivals cannot carry."""
552
+ from .reports import LAST_RUN
553
+ try: rec = json.loads(settings.get(f"{LAST_RUN}{src['SourceId']}") or '{}')
554
+ except (ValueError, TypeError): rec = {}
555
+ if not rec.get('at'): return ''
556
+ what = 'failed' if rec.get('failed') else f"posted mid {rec['message_id']}" if rec.get('message_id') else 'nothing to report so nothing was filed'
557
+ return f"; last ran {_when(rec['at'])}, {what}"
558
+
547
559
  _FAILS = re.compile(r'fail|error|denied|timeout|could not|unable', re.I)
548
560
  _GH_FAILED = re.compile(r'^(.+?) Failed in ', re.M) # the job lines of GitHub's "Run failed" mail
549
561
  def _cause(r: dict) -> str:
@@ -852,7 +864,7 @@ def system_checks(store, source_ids=None, inline=None) -> str:
852
864
  ids = saved if source_ids is None else _ids(source_ids)
853
865
  subs = own if inline is None else _inline(inline)
854
866
  if not ids and not subs:
855
- return '(none selected - add a data source, or choose saved data views, on Reports -> Assistant)'
867
+ return '(none selected - add a data source, or choose saved data views, on Reports -> Advisor)'
856
868
  from . import reports
857
869
  found = {s['SourceId']: s for s in store.list_sources(active_only=False) if s.get('Channel') == 'report'}
858
870
  jobs = [] # (title, cfg to render, or a note to print instead)
@@ -862,7 +874,7 @@ def system_checks(store, source_ids=None, inline=None) -> str:
862
874
  try: cfg_ = json.loads(src.get('ConfigJson') or '{}')
863
875
  except ValueError: cfg_ = {}
864
876
  title = str(cfg_.get('title') or src.get('Address') or f'report {sid}')
865
- if cfg_.get('type') == 'assistant': jobs.append((title, None, 'Skipped: an Assistant cannot watch itself.'))
877
+ if cfg_.get('type') == 'assistant': jobs.append((title, None, 'Skipped: an Advisor cannot watch itself.'))
866
878
  else: jobs.append((title, cfg_, None))
867
879
  for i, sub in enumerate(subs, 1):
868
880
  jobs.append((str(sub.get('label') or sub.get('title') or '').strip() or f"{sub.get('type')} #{i}", sub, None))
@@ -1166,10 +1178,10 @@ def _idea_message(store, i: dict, a: dict, report_title=None) -> tuple:
1166
1178
  active = bool(task and task.get('Status') in ('open', 'in_progress', 'waiting'))
1167
1179
  working = bool(active and any(r.get('Status') == 'running' for r in store.list_runs(tid)))
1168
1180
  stamp = i.get('LastSaid') or i.get('FirstSeen') or datetime.now().strftime('%Y-%m-%d %H:%M:%S')
1169
- who = report_title or 'Assistant'
1181
+ who = report_title or 'Advisor'
1170
1182
  # one arrival per idea: the stamp in the id made every re-say a fresh message, and a fresh task once the first closed
1171
1183
  msg = {'external_id': f"idea:{i['IdeaId']}", 'channel': CHANNEL, 'from_name': who, 'source_name': who,
1172
- 'conversation_id': str(i.get('Key') or f"idea:{i['IdeaId']}"), 'subject': f"Assistant idea: {_cut(i.get('Text'), 100)}",
1184
+ 'conversation_id': str(i.get('Key') or f"idea:{i['IdeaId']}"), 'subject': f"Advisor idea: {_cut(i.get('Text'), 100)}",
1173
1185
  'sent_at': stamp, 'body': str(i.get('Text') or '') + (f"\n\nwhy: {a.get('why')}" if a.get('why') else ''),
1174
1186
  'idea_context': {'report': report_title, 'kind': i.get('Kind'),
1175
1187
  'linked_task': f"{task_ref(tid)} [{task.get('Status')}] {_short(task.get('Title'), 80)}" if task else None,
@@ -1448,7 +1460,7 @@ def _run(store, llm, instruction, watch_source_ids, watch_sources, systems_only=
1448
1460
  if systems_only and not (_ids(watch_source_ids) or _inline(watch_sources)):
1449
1461
  why = ('this report reads nothing: no Taskuary block is ticked and no data source is chosen '
1450
1462
  '- tick a block, or choose a source, on the report')
1451
- logger.warning(f"assistant: {report_title or 'Assistant'} ran and read nothing - {why}")
1463
+ logger.warning(f"assistant: {report_title or 'Advisor'} ran and read nothing - {why}")
1452
1464
  return {'ran': True, 'said': 0, 'reads_nothing': True, 'summary': why, 'inputs': '',
1453
1465
  'reviewed': {'notes': '', 'scope': 'nothing', 'systems': 0, 'why': why}}
1454
1466
  state = {i['Key']: i for i in store.list_ideas()}
@@ -1522,7 +1534,7 @@ def _run(store, llm, instruction, watch_source_ids, watch_sources, systems_only=
1522
1534
  # chose "every run", in which case the check still says it ran, in one line, with what it
1523
1535
  # read behind it. A setting that promises every run and then shows nothing is a lie.
1524
1536
  if not always_post: return {'ran': True, 'said': 0, 'reviewed': rv, 'inputs': read}
1525
- who = (report_title or 'Assistant') if own_post else 'Assistant'
1537
+ who = (report_title or 'Advisor') if own_post else 'Advisor'
1526
1538
  me = f'assistant:{report_id}' if own_post else 'assistant'
1527
1539
  mid = store.add_message({'TaskId': None, 'ExternalId': f'{me}:{stamp}', 'ConversationId': me, 'Channel': CHANNEL,
1528
1540
  'SourceName': who, 'Subject': f'{who} - nothing to report', 'FromName': who,
@@ -1551,7 +1563,7 @@ def _run(store, llm, instruction, watch_source_ids, watch_sources, systems_only=
1551
1563
  head = rows[0]['Text'] if len(rows[0]['Text']) <= 90 else rows[0]['Text'][:90].rsplit(' ', 1)[0] + '…'
1552
1564
  subj = head + (f' (+{len(rows) - 1} more)' if len(rows) > 1 else '')
1553
1565
  identity = f'assistant:{report_id}' if own_post else 'assistant'
1554
- name = (report_title or 'Assistant') if own_post else 'Assistant'
1566
+ name = (report_title or 'Advisor') if own_post else 'Advisor'
1555
1567
  mid = store.add_message({'TaskId': None, 'ExternalId': f'{identity}:{stamp}', 'ConversationId': identity, 'Channel': CHANNEL,
1556
1568
  'SourceName': name, 'Subject': subj, 'FromName': name, 'SentAt': stamp,
1557
1569
  'BodyText': body, 'Status': 'feed'})
@@ -1623,7 +1635,7 @@ def act(store, idea_id: int, verb: str, actor: str = 'owner', llm=None, days: in
1623
1635
  # This is NEW work noticed after an earlier task was completed. A message can point
1624
1636
  # at only one task, so do not steal it from history or rename/reopen the completed
1625
1637
  # task. Carry the evidence into a fresh assistant-owned task instead.
1626
- title = str(a.get('title') or i.get('Text') or 'Assistant follow-up').strip()[:200]
1638
+ title = str(a.get('title') or i.get('Text') or 'Advisor follow-up').strip()[:200]
1627
1639
  source_text = str(message.get('BodyText') or '').strip()
1628
1640
  summary = str(i.get('Text') or '').strip()
1629
1641
  if source_text and source_text not in summary: summary += f'\n\nSource message: {source_text}'
@@ -336,6 +336,15 @@ def test_connector(store, cid: int) -> dict:
336
336
  elif c['Type'] == 'robinhood':
337
337
  from . import robinhood
338
338
  detail = robinhood.test(store, c)
339
+ elif c['Type'] in ('bluesky', 'mastodon'):
340
+ # whoami, like LinkedIn: the one call that proves the credential AND comes back
341
+ # with something the owner recognises. Posting would be a strange thing for Test.
342
+ from .reports import REGISTRY, resolve_cfg
343
+ rtype = f"{c['Type']}_me"
344
+ out = REGISTRY[rtype](resolve_cfg(store, {'type': rtype}))
345
+ row = (out.get('rows') or [{}])[0]
346
+ detail = (f"signed in as {row.get('handle') or '?'}"
347
+ f" - {row.get('followers')} followers, {row.get('posts')} posts")
339
348
  elif c['Type'] == 'linkedin':
340
349
  # whoami, which is the one call that proves the token AND returns something the
341
350
  # owner recognises - a post would be a strange thing for a Test button to do
@@ -122,6 +122,11 @@ def main():
122
122
  'this transcript, and draft the reply the sender gets (you are not '
123
123
  'sending it - the owner approves it). Give one sentence on what you did '
124
124
  'or found. Do not run this while waiting on the owner.')
125
+ ap.add_argument('--reply', metavar='TEXT',
126
+ help="write the reply the person who asked will get, in your own words - it becomes this "
127
+ "task's pending reply for the owner to approve (you are not sending it), and "
128
+ "finishing keeps it instead of redrafting. Use - to read it from stdin.")
129
+ ap.add_argument('--reply-file', metavar='PATH', help='--reply, read from a file (for a long or multi-line reply)')
125
130
  args = ap.parse_args()
126
131
  if args.demo:
127
132
  import os, tempfile
@@ -133,6 +138,29 @@ def main():
133
138
  # --done goes over HTTP, unlike --note. A note is a database row and any process can write
134
139
  # one; ENDING a task needs the live session's scrollback, which exists only inside the
135
140
  # running server - this process would find no transcript and wrap an empty one.
141
+ if args.reply is not None or args.reply_file:
142
+ import os, sys, requests
143
+ try: sys.stdout.reconfigure(encoding='utf-8', errors='replace')
144
+ except (AttributeError, OSError): pass
145
+ tid = os.environ.get('TASKUARY_TASK')
146
+ if not str(tid).isdigit():
147
+ print('not in a Taskuary session - TASKUARY_TASK is not set, so there is no task to reply on')
148
+ return
149
+ if args.reply_file: text = open(args.reply_file, encoding='utf-8').read()
150
+ else: text = sys.stdin.read() if args.reply == '-' else args.reply
151
+ srv = config.load()['server']
152
+ host = '127.0.0.1' if srv.get('host') in ('0.0.0.0', '::', '', None) else srv['host']
153
+ base = f"http://{host}:{srv.get('port') or 7787}"
154
+ hdr = {'X-Taskuary-Token': srv['token']} if srv.get('token') else {}
155
+ try:
156
+ r = requests.post(f'{base}/api/agent/reply', timeout=60, headers=hdr,
157
+ json={'task_id': int(tid), 'text': text, 'agent': os.environ.get('TASKUARY_AGENT') or 'agent'})
158
+ out = r.json() if r.headers.get('content-type', '').startswith('application/json') else {}
159
+ except Exception as e:
160
+ print(f'could not reach Taskuary at {base}: {e}'); return
161
+ print('reply saved on the task, waiting on the owner to approve and send it.' if out.get('ok')
162
+ else f"not saved: {out.get('why') or out.get('detail') or r.text[:200]}")
163
+ return
136
164
  if args.done is not None:
137
165
  import os, sys, requests
138
166
  try: sys.stdout.reconfigure(encoding='utf-8', errors='replace')
@@ -105,6 +105,50 @@ def reply_target(store, task_id: int):
105
105
  return next((m['MessageId'] for m in reversed(store.list_messages(task_id)) if m.get('Status') != 'context'), None)
106
106
 
107
107
 
108
+ # ── the agent's own words ───────────────────────────────────────────────────────────────
109
+ # The agent that did the work often writes the better answer: it knows what it found, and a second
110
+ # model redrafting from its transcript loses the nuance (the owner, 2026-09-23: "sometimes the agent
111
+ # that did the work is better for response than another ai generating it based off of transcript").
112
+ # It used to have no way to hand that answer over - it asked "should I draft the reply?", wrote it
113
+ # on its own screen, and the task's reply stayed empty until the responder wrote a different one.
114
+ # Now it says so: `taskuary --reply` (a shell) or a [[TASKUARY-REPLY]] block (the general chat), the
115
+ # text becomes the task's pending reply as written, and the end of the run keeps it.
116
+ AGENT_DRAFT = 'agent:'
117
+
118
+ def agent_drafted(rv) -> bool:
119
+ return bool(rv) and str(rv.get('DraftBy') or '').startswith(AGENT_DRAFT) and bool(str(rv.get('DraftText') or '').strip())
120
+
121
+
122
+ def own_draft(store, task_id: int):
123
+ """The pending reply the agent wrote itself, if there is one."""
124
+ return next((r for r in (store.pending_review(task_id, 'draft_reply', live_only=False),
125
+ store.pending_review(task_id, 'draft', live_only=False)) if agent_drafted(r)), None)
126
+
127
+
128
+ def agent_reply(store, task_id: int, text: str, agent: str = 'agent', run_id: int = None) -> dict:
129
+ """Put the agent's reply on the task as the pending draft - the same review the responder would
130
+ write into (held, live or new; one per answer, PW-236), marked as the agent's. Nothing is sent:
131
+ the owner approves it like every other reply."""
132
+ text = str(text or '').strip()
133
+ if not text: return {'ok': False, 'why': 'no reply text'}
134
+ held = store.held_review(task_id) or {}
135
+ mid = reply_target(store, task_id) or held.get('MessageId')
136
+ if not mid: return {'ok': False, 'why': 'nobody is waiting on a reply on this task - there is no one to draft it to'}
137
+ live = None if held else (store.pending_review(task_id, 'draft_reply', live_only=False) or store.pending_review(task_id, 'draft', live_only=False))
138
+ why = f'{agent} wrote this reply in its session - approve to send'
139
+ if held:
140
+ rid = held['ReviewId']; store.unhold_review(rid, why)
141
+ elif live:
142
+ rid = live['ReviewId']; store.update_review_reason(rid, why, run_id)
143
+ else:
144
+ rid = store.add_review({'TaskId': task_id, 'MessageId': mid, 'RunId': run_id, 'Kind': 'draft_reply', 'Status': 'pending', 'Reason': why})
145
+ rv = store.get_review(rid) or {}
146
+ if rv.get('MessageId') != mid: store.update_review_message(rid, mid)
147
+ store.update_review_draft(rid, text, run_id or rv.get('RunId'), by=AGENT_DRAFT + agent)
148
+ store.add_comment(task_id, agent, 'agent', f'{agent} drafted the reply itself - it is on the task, waiting on your approval.')
149
+ return {'ok': True, 'review_id': rid, 'message_id': mid}
150
+
151
+
108
152
  # ── the cheap ending ────────────────────────────────────────────────────────────────────
109
153
  # Almost everything a keyboard can do goes to the coding agent (the owner's rule), and the
110
154
  # whole bargain is that an agent with nothing to do says "nothing to do here" and stops CHEAPLY.
@@ -213,7 +257,7 @@ def finish(store, task_id: int, rep: dict, run_id: int = None, actor: str = 'cod
213
257
  # nobody can send does not hold a task the owner just closed
214
258
  if owner_done and mid and not can_send: mid = None
215
259
  # a held draft is proof somebody IS waiting on an answer, so it is never quietly dropped here
216
- if mid and not held and nobody_waiting(store, mid, rep):
260
+ if mid and not held and not own_draft(store, task_id) and nobody_waiting(store, mid, rep):
217
261
  store.add_comment(task_id, actor, 'agent', 'Nothing needed doing here and the sender is not waiting on an '
218
262
  'answer - filed with the report, no reply drafted.')
219
263
  mid = None
@@ -432,6 +476,16 @@ def raise_reply(store, task_id: int, mid: int, run_id: int, rep: dict,
432
476
  rid = store.add_review({'TaskId': task_id, 'MessageId': mid, 'RunId': run_id, 'Kind': 'draft_reply', 'Status': 'pending',
433
477
  'Reason': why if fresh.get('state') in ('changed', 'unresolved') else 'coder finished the work - reply awaiting approval'})
434
478
  if (held or live) and (held or live).get('MessageId') != mid: store.update_review_message(rid, mid)
479
+ rv = store.get_review(rid) or {}
480
+ if agent_drafted(rv):
481
+ # the agent that did the work wrote this reply itself (agent_reply): its words stand and no second
482
+ # model rewrites them - only a thread that moved on (or could not be checked) is flagged for a reread
483
+ who = str(rv.get('DraftBy'))[len(AGENT_DRAFT):] or 'the agent'
484
+ store.update_review_reason(rid, f"{who}'s own reply from the session - approve to send"
485
+ + (' (the thread moved on after it was written - reread it)' if fresh.get('state') == 'changed' else ''), run_id)
486
+ if fresh.get('state') in ('changed', 'unresolved'): store.mark_review_stale(rid)
487
+ _notify_done(store, task_id, rid)
488
+ return
435
489
  src = complete_result or resolution_text(rep)
436
490
  if fresh.get('state') == 'changed' and fresh.get('latest'):
437
491
  l = fresh['latest']
@@ -450,6 +504,10 @@ def raise_reply(store, task_id: int, mid: int, run_id: int, rep: dict,
450
504
  except Exception as e: logger.warning(f'reply draft failed for task {task_id}: {e}')
451
505
  # a refresh that failed is an unresolved freshness state the owner sees: the draft waits, stale, for one that succeeds
452
506
  if fresh.get('state') == 'unresolved': store.mark_review_stale(rid)
507
+ _notify_done(store, task_id, rid)
508
+
509
+
510
+ def _notify_done(store, task_id: int, rid: int) -> None:
453
511
  # the ping that matters most: work FINISHED and its reply is sitting on the task on you
454
512
  if (store.get_settings().get('notify_level') or 'needs_me') != 'off':
455
513
  from .outbound import notify
@@ -463,3 +521,5 @@ def raise_reply(store, task_id: int, mid: int, run_id: int, rep: dict,
463
521
  try: notify(store, f'{task_ref(task_id)} is done - the reply is drafted and waiting on '
464
522
  f'your approval on the task.\n{head}{tail}')
465
523
  except Exception as e: logger.warning(f'notify failed for task {task_id}: {e}')
524
+
525
+
@@ -67,7 +67,7 @@ CHIP_WORDS = {'approve': 'Send the reply', 'redraft': 'Redraft it', 'reply': 'Re
67
67
  'not_ours_sender': 'Ignore this sender', 'block_sender': 'Block them in Settings',
68
68
  'archive': 'Archive it', 'close': 'Close the task',
69
69
  'done': 'Handled', 'later': 'Later', 'skip': 'Tomorrow', 'next': 'Next', 'answer_agent': 'Answer it',
70
- 'stop_agent': 'Stop the agent', 'rerun': 'Run it again', 'split': 'Split it in two',
70
+ 'stop_agent': 'Save and end session', 'rerun': 'Run it again', 'split': 'Split it in two',
71
71
  'prep': 'Prep me', 'followup': 'Draft a follow-up'}
72
72
  # What the word will actually DO, on hover - written where the difference matters. "Ignore this
73
73
  # sender" and "Block them in Settings" are one line apart and not remotely the same act.
@@ -78,10 +78,12 @@ CHIP_HINTS = {'not_ours_sender': 'Their mail keeps arriving and stays readable -
78
78
  'mine': "Your own list - no agent starts", 'next': 'Read it and move on'}
79
79
  # per kind, in the order they are offered. `next` is last on every one of them: moving on is always available,
80
80
  # and it is the one word that is never a decision about the thing itself.
81
- CHIPS = {'review': ('approve', 'close', 'redraft', 'not_ours', 'next'), 'action': ('approve', 'not_ours', 'next'),
82
- 'agent': ('answer_agent', 'stop_agent', 'next'), 'meeting': ('prep', 'regular_agent', 'next'),
81
+ # `skip` (Tomorrow) on the kinds that come back after the hour: passing one with Next returns it in an
82
+ # hour, and Tomorrow is how to silence it until the morning instead (2026-09-23)
83
+ CHIPS = {'review': ('approve', 'close', 'redraft', 'not_ours', 'skip', 'next'), 'action': ('approve', 'not_ours', 'skip', 'next'),
84
+ 'agent': ('answer_agent', 'stop_agent', 'skip', 'next'), 'meeting': ('prep', 'regular_agent', 'next'),
83
85
  'report': ('rerun', 'regular_agent', 'next'), 'agentdone': ('close', 'reply', 'next'),
84
- 'wrapup': ('close', 'next'), 'idea': ('followup', 'mine', 'done', 'next'), 'task': ('close', 'next'),
86
+ 'wrapup': ('close', 'next'), 'idea': ('followup', 'mine', 'done', 'next'), 'task': ('close', 'skip', 'next'),
85
87
  'asked': ('reply', 'regular_agent', 'coder', 'mine', 'not_ours', 'not_ours_sender', 'next'),
86
88
  'todo': ('reply', 'regular_agent', 'coder', 'mine', 'not_ours', 'not_ours_sender', 'next'),
87
89
  'fyi': ('not_ours', 'not_ours_sender', 'block_sender', 'mine', 'next'),
@@ -1894,11 +1896,14 @@ PROPOSALS = {
1894
1896
  'close': ('task.complete', 'Close the task', True), 'done': ('item.settle', 'Mark it handled', True),
1895
1897
  'later': ('item.settle', 'Push it back', True), 'skip': ('item.settle', 'Skip until tomorrow', True),
1896
1898
  'approve': ('review.approve', 'Send the reply', True), 'answer_agent': ('agent.answer', 'Send the answer to the agent', True),
1897
- 'stop_agent': ('agent.stop', 'Stop the agent', False), 'rerun': ('report.rerun', 'Run the report again', True),
1899
+ 'stop_agent': ('agent.stop', 'Save and end session', False), 'rerun': ('report.rerun', 'Run the report again', True),
1898
1900
  'remember': ('memory.remember', 'Remember it', False), 'split': ('task.split', 'Split it in two', False),
1899
1901
  'clear': ('pipe.clear', 'Clear them from the pipe', False), 'setup': ('task.setup', 'Open the walk-through', False),
1900
1902
  }
1901
- AUTO = ('done', 'skip', 'later', 'close') # settles what is on the table; nothing leaves, nothing is handed off
1903
+ # ...and ending the agent's session, which the task page's own button does on the click: it writes the
1904
+ # session up and drafts the reply for your yes - nothing leaves (the owner, 2026-09-23: "it should be save
1905
+ # end session as well same as in task", having been asked to confirm a card that read "wrap: false")
1906
+ AUTO = ('done', 'skip', 'later', 'close', 'stop_agent') # settles what is on the table; nothing leaves, nothing is handed off
1902
1907
  # the operations that take the item off the table, so the walk moves on after them (the page reads
1903
1908
  # `settles` off the proposal it is holding; a chat comes back a turn later and has only the kind)
1904
1909
  SETTLING_KINDS = frozenset(kind for kind, _label, settles in PROPOSALS.values() if settles)
@@ -2031,12 +2036,15 @@ def propose_for(store, dock_tid: int, decision: dict, item: dict | None, text: s
2031
2036
  elif verb == 'stop_agent':
2032
2037
  end = _agent_task(store, item, text)
2033
2038
  if not end: raise ValueError('no agent is running right now - nothing to stop')
2034
- asked_wrap = wrap = bool(re.search(r"\b(wrap|finished|it'?s done)\b", text, re.I))
2035
- if wrap: # a wrap writes the report FROM the transcript: without one there is nothing to wrap
2036
- from . import terminal as term
2037
- try: wrap = bool((term.transcript_for(store, end) or ('',))[0].strip())
2038
- except Exception: wrap = False
2039
- target, params, label = end, {'wrap': wrap}, ('Wrap it up' if wrap else label)
2039
+ # THE TASK PAGE'S "Save and end session", whatever the words: the session is written up whenever
2040
+ # there is a transcript to write it from (a wrap writes the report FROM it), and simply ended when
2041
+ # there is none. It used to write up only on the word "wrap" - so the button stopped an agent and
2042
+ # threw its work away, where the page's button of the same meaning kept it.
2043
+ asked_wrap = bool(re.search(r"\b(wrap|finished|it'?s done)\b", text, re.I))
2044
+ from . import terminal as term
2045
+ try: wrap = bool((term.transcript_for(store, end) or ('',))[0].strip())
2046
+ except Exception: wrap = False
2047
+ target, params = end, {'wrap': wrap}
2040
2048
  if asked_wrap and not wrap: note = ' There is no transcript to write a report from yet, so there is nothing to wrap - this stops the agent and the task stays open.'
2041
2049
  elif verb == 'rerun': target = it.get('source_id')
2042
2050
  elif verb == 'remember':
@@ -2063,7 +2071,7 @@ def propose_for(store, dock_tid: int, decision: dict, item: dict | None, text: s
2063
2071
  head = f"Changed to: {label} - {summary}" if op['version'] > 1 else f"{label}: {summary}"
2064
2072
  say_ = lead + f"{head}.{note} Nothing has been started - confirm below, or tell me what to change."
2065
2073
  return {**op, 'verb': verb, 'label': label, 'summary': summary, 'settles': bool(settles and not elsewhere),
2066
- 'key': it.get('key'), 'ref': it.get('ref'), 'tid': it.get('tid'), 'say': say_}
2074
+ 'key': it.get('key'), 'ref': it.get('ref'), 'tid': it.get('tid'), 'say': say_, 'note': note.strip()}
2067
2075
 
2068
2076
 
2069
2077
  def propose_direct(store, verb: str, key: str, text: str = '', actor: str = 'owner', table: bool = False) -> dict:
@@ -2082,7 +2090,7 @@ def propose_direct(store, verb: str, key: str, text: str = '', actor: str = 'own
2082
2090
  record_related(store, tid, item, 'user', f"{PROPOSALS[verb][1]}: {item.get('title') or item.get('ref') or key}")
2083
2091
  prop = propose_for(store, tid, {'verb': verb, 'text': text or ''}, item, text or '', actor, table=item if table else None)
2084
2092
  prop['settles'] = bool(table and PROPOSALS[verb][2])
2085
- if table and verb in AUTO: prop.update(auto=True, say=f"{prop['label']} - {_where(item)}.")
2093
+ if table and verb in AUTO: prop.update(auto=True, say=f"{prop['label']} - {_where(item)}." + (f" {prop['note']}" if prop.get('note') else ''))
2086
2094
  record_related(store, tid, item, 'assistant', prop['say'], {'kind': 'proposal', 'key': prop.get('key'), 'title': prop['label'], 'op': prop['id'],
2087
2095
  'tid': prop.get('tid'), 'ref': prop.get('ref'), 'lane': item.get('lane')})
2088
2096
  return prop
@@ -2264,7 +2272,6 @@ def describe_op(store, op: dict) -> tuple:
2264
2272
  if kind == 'preference.exclude_sender': label = 'Silence this sender' if p.get('scope') == 'sender' else 'File it and remember this kind'
2265
2273
  if kind == 'item.settle': label = {'later': 'Push it back', 'skip': 'Skip until tomorrow'}.get(str(p.get('verb')), 'Mark it handled')
2266
2274
  if kind == 'task.complete' and p.get('agent'): label = 'Close the task and stop its agent'
2267
- if kind == 'agent.stop' and p.get('wrap'): label = 'Wrap it up'
2268
2275
  if kind == 'report.create': label = 'Create the report'
2269
2276
  if kind == 'connection.create': label = 'Create the connection'
2270
2277
  if kind in toolcatalog.INSTANT or kind == 'report.delete':
@@ -2324,7 +2331,7 @@ def _outcome_line(kind: str, p: dict, o: dict | None) -> str:
2324
2331
  if o.get('existing'): return f" {o.get('agent') or 'An agent'} was already on it."
2325
2332
  if o.get('started') or o.get('chat'): return f" {o.get('agent') or 'The agent'} is on it - moving on."
2326
2333
  if kind == 'item.settle' and o.get('closed'): return f" {task_ref(int(o['closed']))} closed."
2327
- if kind == 'agent.stop' and not p.get('wrap'): return ' The task stays open - say close it when you want it closed.'
2334
+ if kind == 'agent.stop': return ' The task stays open - say close it when you want it closed.'
2328
2335
  if kind == 'memory.remember': return ' A memory settles nothing: the walk is where it was.'
2329
2336
  if kind == 'report.create' and o.get('sourceId'):
2330
2337
  return f" \"{o.get('title')}\" is on the Reports tab{' and runs on its schedule' if o.get('enabled') else ', switched off'}."
@@ -2536,7 +2543,8 @@ def say(store, text: str, key: str = None, llm=None, actor: str = 'owner', trace
2536
2543
  # 2026-09-07: "I did already - it should close it; only confirm when you are not sure"). A hand-off, a
2537
2544
  # send, a rule or a verb aimed elsewhere still waits for the button.
2538
2545
  if verb in AUTO and not elsewhere:
2539
- prop.update(auto=True, say=f"{prop['label']} - {_where(target_item or {})}.")
2546
+ # ...and a run at once still says what it could not do ("nothing to wrap")
2547
+ prop.update(auto=True, say=f"{prop['label']} - {_where(target_item or {})}." + (f" {prop['note']}" if prop.get('note') else ''))
2540
2548
  rec('assistant', prop['say'], {'kind': 'proposal', 'key': prop.get('key'), 'title': prop['label'], 'op': prop['id'],
2541
2549
  'tid': prop.get('tid'), 'ref': prop.get('ref'), 'lane': (target_item or {}).get('lane')})
2542
2550
  return {'say': prop['say'], 'options': [], 'decision': None, 'proposal': prop}