taskuary 0.3.7.0__tar.gz → 0.3.7.2__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 (568) hide show
  1. {taskuary-0.3.7.0/taskuary.egg-info → taskuary-0.3.7.2}/PKG-INFO +30 -27
  2. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/README.md +29 -26
  3. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/pyproject.toml +1 -1
  4. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/aidefaults.py +21 -4
  5. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/chatformat.py +17 -1
  6. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/cli.py +1 -1
  7. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/concierge.py +28 -9
  8. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/demo.py +1 -0
  9. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/doorway_browse.py +72 -9
  10. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/evalset.py +49 -1
  11. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/funnel.py +10 -2
  12. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/funnel_selection.py +3 -2
  13. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/jev.py +34 -0
  14. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/llm.py +27 -0
  15. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/lookups.py +12 -4
  16. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/remote_assistant.py +54 -10
  17. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/server.py +98 -15
  18. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/settings_schema.json +2 -2
  19. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/store.py +12 -6
  20. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/terminal.py +12 -2
  21. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/toolcatalog.py +8 -3
  22. taskuary-0.3.7.0/taskuary/web/assets/AssistantGame-CgEOViMV.js → taskuary-0.3.7.2/taskuary/web/assets/AssistantGame-BZJj3XwN.js +3 -3
  23. taskuary-0.3.7.0/taskuary/web/assets/GameScene-ME4FVYZZ.js → taskuary-0.3.7.2/taskuary/web/assets/GameScene-ATV2SyYO.js +1 -1
  24. taskuary-0.3.7.0/taskuary/web/assets/HubView-BANr9hv3.js → taskuary-0.3.7.2/taskuary/web/assets/HubView-DtpvsbeL.js +1 -1
  25. taskuary-0.3.7.0/taskuary/web/assets/StudioScene-D19mVoMT.js → taskuary-0.3.7.2/taskuary/web/assets/StudioScene-D4IlNRXi.js +1 -1
  26. taskuary-0.3.7.0/taskuary/web/assets/TasksView-Cy_KQBIk.js → taskuary-0.3.7.2/taskuary/web/assets/TasksView-B7m8zBYu.js +1 -1
  27. taskuary-0.3.7.2/taskuary/web/assets/index-BKrB4ObA.js +602 -0
  28. taskuary-0.3.7.2/taskuary/web/assets/index-DUxLnhhx.css +32 -0
  29. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/index.html +2 -2
  30. {taskuary-0.3.7.0 → taskuary-0.3.7.2/taskuary.egg-info}/PKG-INFO +30 -27
  31. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary.egg-info/SOURCES.txt +10 -7
  32. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_ai_defaults.py +16 -0
  33. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_api.py +20 -0
  34. taskuary-0.3.7.2/tests/test_button_promises.py +32 -0
  35. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_coder_session_continuity.py +16 -0
  36. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_continue_session.py +16 -0
  37. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_funnel.py +3 -3
  38. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_morning_line.py +8 -5
  39. taskuary-0.3.7.2/tests/test_phone_browse_pass.py +144 -0
  40. taskuary-0.3.7.2/tests/test_prompt_images.py +38 -0
  41. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_task_tools.py +12 -0
  42. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_terminal.py +26 -0
  43. taskuary-0.3.7.0/taskuary/web/assets/index-B-MVokO_.css +0 -32
  44. taskuary-0.3.7.0/taskuary/web/assets/index-Bn3FS1dn.js +0 -598
  45. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/LICENSE +0 -0
  46. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/setup.cfg +0 -0
  47. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/__init__.py +0 -0
  48. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/acp.py +0 -0
  49. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/agents.py +0 -0
  50. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/aisetup.py +0 -0
  51. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/alchemy.py +0 -0
  52. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/appfacts.py +0 -0
  53. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/artifacts.py +0 -0
  54. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/assistant.py +0 -0
  55. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/assistantblocks.py +0 -0
  56. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/autoreply.py +0 -0
  57. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/aws.py +0 -0
  58. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/azure.py +0 -0
  59. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/blackboard.py +0 -0
  60. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/brief.py +0 -0
  61. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/browserview.py +0 -0
  62. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/calendar.py +0 -0
  63. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/categories.py +0 -0
  64. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/chains.py +0 -0
  65. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/channels.py +0 -0
  66. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/chatservers.py +0 -0
  67. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/ci.py +0 -0
  68. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/cli_connections.py +0 -0
  69. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/cli_install_terminal.py +0 -0
  70. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/cliinstall.py +0 -0
  71. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/climodels.py +0 -0
  72. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/clipool.py +0 -0
  73. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/clis.py +0 -0
  74. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/clisetup.py +0 -0
  75. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/coder.py +0 -0
  76. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/compose.py +0 -0
  77. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/config.py +0 -0
  78. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/connectorcatalog.json +0 -0
  79. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/connectorcatalog.py +0 -0
  80. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/context.py +0 -0
  81. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/continuity.py +0 -0
  82. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/counsel.py +0 -0
  83. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/databases.py +0 -0
  84. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/db.py +0 -0
  85. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/deps.py +0 -0
  86. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/desktop.py +0 -0
  87. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/devtools.py +0 -0
  88. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/digest.py +0 -0
  89. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/docsync.py +0 -0
  90. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/evening.py +0 -0
  91. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/files.py +0 -0
  92. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/funnel_presentation.py +0 -0
  93. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/general.py +0 -0
  94. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/ghcloseout.py +0 -0
  95. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/github.py +0 -0
  96. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/guard.py +0 -0
  97. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/handbook.py +0 -0
  98. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/handraise.py +0 -0
  99. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/histgen.py +0 -0
  100. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/hooks.py +0 -0
  101. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/hub.py +0 -0
  102. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/images.py +0 -0
  103. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/imapmail.py +0 -0
  104. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/imessage.py +0 -0
  105. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/ingest.py +0 -0
  106. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/intacct.py +0 -0
  107. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/interview.py +0 -0
  108. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/invoice_workflow.py +0 -0
  109. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/knowledge.py +0 -0
  110. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/lanes.json +0 -0
  111. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/learn.py +0 -0
  112. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/learnedgraph.py +0 -0
  113. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/linkedin.py +0 -0
  114. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/live.py +0 -0
  115. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/logs.py +0 -0
  116. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/markets.py +0 -0
  117. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/mcp.py +0 -0
  118. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/messengers.py +0 -0
  119. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/msauth.py +0 -0
  120. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/mssql.py +0 -0
  121. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/operations.py +0 -0
  122. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/outbound.py +0 -0
  123. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/outbox.py +0 -0
  124. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/ownwork.py +0 -0
  125. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/playbooks.py +0 -0
  126. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/pm.py +0 -0
  127. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/policy.py +0 -0
  128. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/problems.py +0 -0
  129. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing.py +0 -0
  130. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_all.py +0 -0
  131. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_inventory.py +0 -0
  132. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_membership.py +0 -0
  133. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_navigation.py +0 -0
  134. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_order.py +0 -0
  135. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_projection.py +0 -0
  136. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_rail.py +0 -0
  137. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_reads.py +0 -0
  138. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_startup.py +0 -0
  139. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/processing_unread.py +0 -0
  140. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/projects.py +0 -0
  141. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/promptmap.py +0 -0
  142. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/proof.py +0 -0
  143. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/proposals.py +0 -0
  144. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/quickbooks.py +0 -0
  145. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/rank.py +0 -0
  146. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/redact.py +0 -0
  147. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/remind.py +0 -0
  148. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/reports.py +0 -0
  149. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/research.py +0 -0
  150. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/reshape.py +0 -0
  151. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/responder.py +0 -0
  152. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/retention.py +0 -0
  153. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/robinhood.py +0 -0
  154. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/routing.py +0 -0
  155. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/routingmemory.py +0 -0
  156. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/scopes.py +0 -0
  157. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/selfclose.py +0 -0
  158. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/semantic.py +0 -0
  159. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/senders.py +0 -0
  160. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/session_artifacts.py +0 -0
  161. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/sessionfiles.py +0 -0
  162. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/settings_defaults.json +0 -0
  163. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/settings_schema.py +0 -0
  164. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/setup.py +0 -0
  165. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/sharepoint.py +0 -0
  166. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/sheets.py +0 -0
  167. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/simplefin.py +0 -0
  168. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/skillimport.py +0 -0
  169. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/skills/soul-interview/SKILL.md +0 -0
  170. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/skills/taskuary-setup/SKILL.md +0 -0
  171. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/social.py +0 -0
  172. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/spawn.py +0 -0
  173. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/taskstate.py +0 -0
  174. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/teller.py +0 -0
  175. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/agent.md +0 -0
  176. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/analyst.md +0 -0
  177. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/coder.md +0 -0
  178. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/coordinator.md +0 -0
  179. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/counsel.md +0 -0
  180. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/digest.md +0 -0
  181. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/history/counsel-0.3.3.5.md +0 -0
  182. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/history/counsel-0.3.6.8.md +0 -0
  183. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/history/counsel-0.3.6.9-examples.md +0 -0
  184. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/history/counsel-0.3.6.9-remind.md +0 -0
  185. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/history/counsel-0.3.6.9.md +0 -0
  186. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/how-it-works.md +0 -0
  187. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/learned.md +0 -0
  188. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/marketer.md +0 -0
  189. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/playbook.md +0 -0
  190. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/profile.md +0 -0
  191. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/researcher.md +0 -0
  192. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/soul.md +0 -0
  193. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/style.md +0 -0
  194. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/trader.md +0 -0
  195. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/templates/triage.md +0 -0
  196. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/testing.py +0 -0
  197. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/toil.py +0 -0
  198. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/treg.py +0 -0
  199. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/triage.py +0 -0
  200. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/triage_kind_rules.json +0 -0
  201. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/update.py +0 -0
  202. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/verdicts.py +0 -0
  203. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/voice.py +0 -0
  204. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/wabridge.py +0 -0
  205. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/waitroom.py +0 -0
  206. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/walk.py +0 -0
  207. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/assets/RoundedBoxGeometry-BE4rf3sD.js +0 -0
  208. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/favicon.ico +0 -0
  209. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/favicon.png +0 -0
  210. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/walk/assistant.png +0 -0
  211. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/walk/board.png +0 -0
  212. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/walk/connections.png +0 -0
  213. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/walk/docs.png +0 -0
  214. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/walk/hub.png +0 -0
  215. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/walk/reports.png +0 -0
  216. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/walk/settings.png +0 -0
  217. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/web/walk/tasks.png +0 -0
  218. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/webguard.py +0 -0
  219. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/whatsapp/bridge.mjs +0 -0
  220. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/whatsapp/package.json +0 -0
  221. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/whatsapp/policy.mjs +0 -0
  222. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/whatsapp/poll.mjs +0 -0
  223. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/whatsapp/roster.mjs +0 -0
  224. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/whoami.py +0 -0
  225. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/witness.py +0 -0
  226. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/workerstate.py +0 -0
  227. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/workflows.py +0 -0
  228. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary/zoho.py +0 -0
  229. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary.egg-info/dependency_links.txt +0 -0
  230. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary.egg-info/entry_points.txt +0 -0
  231. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary.egg-info/requires.txt +0 -0
  232. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/taskuary.egg-info/top_level.txt +0 -0
  233. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_acp.py +0 -0
  234. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_advisor_idea_words.py +0 -0
  235. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_answer_route.py +0 -0
  236. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_answers_and_reminders.py +0 -0
  237. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_asking.py +0 -0
  238. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_environment.py +0 -0
  239. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_identity.py +0 -0
  240. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_profiles.py +0 -0
  241. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_reply.py +0 -0
  242. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_sentence.py +0 -0
  243. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_source.py +0 -0
  244. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_agent_wall.py +0 -0
  245. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_aisetup.py +0 -0
  246. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_alchemy.py +0 -0
  247. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_an_inbox_is_not_a_destination.py +0 -0
  248. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_answered_yourself.py +0 -0
  249. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_answers.py +0 -0
  250. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_api_worker_signals.py +0 -0
  251. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_app_uptime.py +0 -0
  252. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_appfacts.py +0 -0
  253. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_approval_interrupt.py +0 -0
  254. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant.py +0 -0
  255. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant_acts.py +0 -0
  256. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant_blocks.py +0 -0
  257. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant_can_look_things_up.py +0 -0
  258. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant_doorways.py +0 -0
  259. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant_ideas.py +0 -0
  260. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant_notifications.py +0 -0
  261. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant_presentation.py +0 -0
  262. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant_reactions.py +0 -0
  263. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_assistant_verdicts.py +0 -0
  264. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_async_triage.py +0 -0
  265. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_audit_chain.py +0 -0
  266. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_audit_fixes.py +0 -0
  267. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_auto_start.py +0 -0
  268. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_autoreply.py +0 -0
  269. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_aws_region.py +0 -0
  270. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_azure_discovery.py +0 -0
  271. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_banner_is_one_rule.py +0 -0
  272. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_blackboard.py +0 -0
  273. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_brain_layer.py +0 -0
  274. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_brain_reaches_the_session.py +0 -0
  275. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_browser_session.py +0 -0
  276. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_browser_workflow.py +0 -0
  277. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_browserview.py +0 -0
  278. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_calendar.py +0 -0
  279. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_calendar_today.py +0 -0
  280. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_canvas_relay.py +0 -0
  281. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_canvas_sections.py +0 -0
  282. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_card_story_phone.py +0 -0
  283. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_categories.py +0 -0
  284. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cc.py +0 -0
  285. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cc_triage.py +0 -0
  286. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chat_doors.py +0 -0
  287. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chat_echo_mail.py +0 -0
  288. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chat_freshness.py +0 -0
  289. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chat_is_not_one_task.py +0 -0
  290. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chat_proposals.py +0 -0
  291. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chat_queue.py +0 -0
  292. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chat_relationship.py +0 -0
  293. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chat_retention.py +0 -0
  294. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chat_setup.py +0 -0
  295. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chatformat.py +0 -0
  296. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chatservers.py +0 -0
  297. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_chinese_clis.py +0 -0
  298. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_clean_context.py +0 -0
  299. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cli_connections.py +0 -0
  300. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cli_failure.py +0 -0
  301. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cli_help.py +0 -0
  302. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cli_install.py +0 -0
  303. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cli_install_terminal.py +0 -0
  304. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cli_on_fresh_path.py +0 -0
  305. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cli_setup.py +0 -0
  306. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cli_update.py +0 -0
  307. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_climodels.py +0 -0
  308. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_clipool.py +0 -0
  309. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_clis.py +0 -0
  310. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_closeout.py +0 -0
  311. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cloud.py +0 -0
  312. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cloud_sources.py +0 -0
  313. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_completion_freshness.py +0 -0
  314. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_compose.py +0 -0
  315. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_compose_sources.py +0 -0
  316. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_compose_workflows.py +0 -0
  317. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_concierge.py +0 -0
  318. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_concierge_counsel.py +0 -0
  319. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_config.py +0 -0
  320. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_context.py +0 -0
  321. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_context_refresh_freshness.py +0 -0
  322. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_coordination.py +0 -0
  323. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_core.py +0 -0
  324. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_correction_doors.py +0 -0
  325. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_counsel.py +0 -0
  326. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_counsel_consumers.py +0 -0
  327. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_counsel_migration.py +0 -0
  328. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_counsel_sections.py +0 -0
  329. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_current_restore.py +0 -0
  330. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_cwlogs_paging.py +0 -0
  331. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_databases.py +0 -0
  332. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_demo.py +0 -0
  333. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_deps.py +0 -0
  334. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_desktop.py +0 -0
  335. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_devin.py +0 -0
  336. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_devtools.py +0 -0
  337. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_diff_review.py +0 -0
  338. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_digest_brief.py +0 -0
  339. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_dispatch_retries.py +0 -0
  340. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_docs_and_llm.py +0 -0
  341. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_docs_flow.py +0 -0
  342. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_duplicate_connectors.py +0 -0
  343. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_email_chains.py +0 -0
  344. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_email_identity_routing.py +0 -0
  345. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_ending_a_run_writes_the_answer.py +0 -0
  346. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_evening_brief.py +0 -0
  347. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_every_card_is_reachable.py +0 -0
  348. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_every_cli_startable.py +0 -0
  349. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_explicit_completion.py +0 -0
  350. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_factory.py +0 -0
  351. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_failed_acts.py +0 -0
  352. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_feed_working.py +0 -0
  353. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_files.py +0 -0
  354. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_final_answer_capture.py +0 -0
  355. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_finished_chat_stays_finished.py +0 -0
  356. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_floating_assistant.py +0 -0
  357. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_follow_up_verdict.py +0 -0
  358. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_fresh_evaluation.py +0 -0
  359. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_freshness.py +0 -0
  360. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_freshness_walk.py +0 -0
  361. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_general.py +0 -0
  362. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_github_auto.py +0 -0
  363. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_github_images.py +0 -0
  364. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_guard.py +0 -0
  365. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_handbook.py +0 -0
  366. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_handbook_topics.py +0 -0
  367. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_handoff_channels.py +0 -0
  368. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_handraise.py +0 -0
  369. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_harvest.py +0 -0
  370. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_histgen.py +0 -0
  371. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_home_is_never_yours.py +0 -0
  372. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_hooks_events.py +0 -0
  373. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_hooks_version.py +0 -0
  374. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_ideas_triage.py +0 -0
  375. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_ignore_sender.py +0 -0
  376. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_ignore_sender_quiets_its_work.py +0 -0
  377. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_images.py +0 -0
  378. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_imap_catchup.py +0 -0
  379. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_imap_history.py +0 -0
  380. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_imapmail.py +0 -0
  381. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_imessage.py +0 -0
  382. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_inline_pictures.py +0 -0
  383. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_intacct.py +0 -0
  384. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_interrupted_work.py +0 -0
  385. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_invite_is_not_work.py +0 -0
  386. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_invoice_workflow.py +0 -0
  387. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_jev.py +0 -0
  388. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_judge_reads_the_conclusion.py +0 -0
  389. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_kind_dispatch.py +0 -0
  390. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_knowledge.py +0 -0
  391. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_lane_vocabulary.py +0 -0
  392. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_learnedgraph.py +0 -0
  393. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_lifecycle.py +0 -0
  394. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_lifespan_loops_are_guarded.py +0 -0
  395. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_linkedin.py +0 -0
  396. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_linkedin_signin.py +0 -0
  397. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_live.py +0 -0
  398. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_local_file.py +0 -0
  399. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_lookups.py +0 -0
  400. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_mail_catchup.py +0 -0
  401. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_mail_fetch_cost.py +0 -0
  402. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_mail_folders.py +0 -0
  403. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_mark_read.py +0 -0
  404. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_markets.py +0 -0
  405. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_mcp.py +0 -0
  406. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_meeting_prep.py +0 -0
  407. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_message_thread.py +0 -0
  408. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_messengers.py +0 -0
  409. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_msauth.py +0 -0
  410. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_mssql.py +0 -0
  411. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_multi_question.py +0 -0
  412. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_muse.py +0 -0
  413. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_no_pydantic_v1.py +0 -0
  414. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_noop_ending.py +0 -0
  415. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_not_coding.py +0 -0
  416. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_note_retrieval.py +0 -0
  417. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_onboarding_walk.py +0 -0
  418. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_one_app_one_reason.py +0 -0
  419. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_one_pty_one_geometry.py +0 -0
  420. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_only_your_own_thread.py +0 -0
  421. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_openai_compatible_brains.py +0 -0
  422. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_operations.py +0 -0
  423. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_outbound_reports.py +0 -0
  424. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_outbox.py +0 -0
  425. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_own_rows_no_reply.py +0 -0
  426. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_person_name.py +0 -0
  427. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_phone_picks.py +0 -0
  428. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_phone_walk_ux.py +0 -0
  429. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_playbook_setup.py +0 -0
  430. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_playbooks.py +0 -0
  431. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_pm.py +0 -0
  432. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_poll_lanes.py +0 -0
  433. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_poll_overlap.py +0 -0
  434. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_poll_parallel.py +0 -0
  435. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_poll_sources.py +0 -0
  436. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_pr_comments.py +0 -0
  437. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_problems.py +0 -0
  438. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_problems_dismiss.py +0 -0
  439. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_procedure_selection.py +0 -0
  440. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_processing_demo_replay.py +0 -0
  441. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_processing_ledger.py +0 -0
  442. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_profile_is_a_role.py +0 -0
  443. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_projects.py +0 -0
  444. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_prompt_sources.py +0 -0
  445. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_promptmap_and_catalog.py +0 -0
  446. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_proof_ci_proposals.py +0 -0
  447. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_pypi_downloads.py +0 -0
  448. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_quickbooks.py +0 -0
  449. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_qwen.py +0 -0
  450. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_rail_decisions.py +0 -0
  451. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_rank.py +0 -0
  452. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_rate_limit.py +0 -0
  453. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_read_text.py +0 -0
  454. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_recent_closures.py +0 -0
  455. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_reclassify.py +0 -0
  456. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_redact.py +0 -0
  457. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_redact_doors.py +0 -0
  458. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_remind.py +0 -0
  459. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_remote_assistant.py +0 -0
  460. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_remote_sections.py +0 -0
  461. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_reply_always_drafts.py +0 -0
  462. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_reply_attachments.py +0 -0
  463. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_reply_channels.py +0 -0
  464. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_reply_envelope.py +0 -0
  465. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_reply_needs_a_correspondent.py +0 -0
  466. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_reply_sources.py +0 -0
  467. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_reply_voice.py +0 -0
  468. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_repo_choice_triage.py +0 -0
  469. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_report_alerts.py +0 -0
  470. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_report_deliver.py +0 -0
  471. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_report_headline.py +0 -0
  472. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_report_one_road.py +0 -0
  473. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_report_order_and_research.py +0 -0
  474. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_report_prompt_isolation.py +0 -0
  475. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_report_refusal.py +0 -0
  476. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_report_routing.py +0 -0
  477. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_report_triage.py +0 -0
  478. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_research.py +0 -0
  479. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_research_more.py +0 -0
  480. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_review_task.py +0 -0
  481. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_reviews.py +0 -0
  482. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_robinhood.py +0 -0
  483. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_routing_memory.py +0 -0
  484. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_s3_poll_paging.py +0 -0
  485. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_scopes.py +0 -0
  486. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_seed_fits_a_tty.py +0 -0
  487. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_seeded_reports.py +0 -0
  488. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_selector_vocabulary.py +0 -0
  489. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_semantic.py +0 -0
  490. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_send_outcomes.py +0 -0
  491. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_send_targets.py +0 -0
  492. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_sender_trust.py +0 -0
  493. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_sender_words.py +0 -0
  494. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_session_artifacts.py +0 -0
  495. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_session_continuity.py +0 -0
  496. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_session_endings.py +0 -0
  497. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_session_files.py +0 -0
  498. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_settings_defaults_json.py +0 -0
  499. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_settings_schema.py +0 -0
  500. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_setup.py +0 -0
  501. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_setup_skill.py +0 -0
  502. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_setup_walk.py +0 -0
  503. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_sheets_sharepoint.py +0 -0
  504. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_simplefin.py +0 -0
  505. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_skillimport.py +0 -0
  506. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_social.py +0 -0
  507. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_someone_else_replied.py +0 -0
  508. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_soul_interview.py +0 -0
  509. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_startup_contract.py +0 -0
  510. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_stay_open.py +0 -0
  511. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_stay_open_doors.py +0 -0
  512. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_store_app_cli.py +0 -0
  513. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_store_hotpaths.py +0 -0
  514. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_store_pokes.py +0 -0
  515. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_sync_clock.py +0 -0
  516. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_task_ask.py +0 -0
  517. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_task_checklist.py +0 -0
  518. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_task_controls_operations.py +0 -0
  519. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_task_identity.py +0 -0
  520. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_task_refs.py +0 -0
  521. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_taskstate.py +0 -0
  522. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_telegram_buttons.py +0 -0
  523. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_teller.py +0 -0
  524. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_the_exe_ships_what_the_wheel_ships.py +0 -0
  525. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_the_split_holds.py +0 -0
  526. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_toil.py +0 -0
  527. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_tracker_revisions.py +0 -0
  528. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_treg.py +0 -0
  529. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_triage_cleanup.py +0 -0
  530. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_triage_dataset.py +0 -0
  531. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_triage_errors.py +0 -0
  532. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_triage_images.py +0 -0
  533. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_triage_joins.py +0 -0
  534. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_triage_pr_rule.py +0 -0
  535. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_triage_retry_sweep.py +0 -0
  536. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_triage_urgent.py +0 -0
  537. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_update.py +0 -0
  538. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_update_refused.py +0 -0
  539. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_urgent_and_handoff.py +0 -0
  540. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_verdict_paths.py +0 -0
  541. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_verdict_shape.py +0 -0
  542. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_verdict_sticks.py +0 -0
  543. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_vision.py +0 -0
  544. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_voice.py +0 -0
  545. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_wabridge.py +0 -0
  546. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_wabridge_log_trim.py +0 -0
  547. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_waitroom.py +0 -0
  548. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_walk_order.py +0 -0
  549. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_watermark.py +0 -0
  550. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_web_entrypoint.py +0 -0
  551. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_webguard.py +0 -0
  552. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_whatsapp_documents.py +0 -0
  553. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_whatsapp_images.py +0 -0
  554. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_whoami.py +0 -0
  555. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_why_not_started.py +0 -0
  556. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_withdrawn.py +0 -0
  557. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_witness.py +0 -0
  558. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_worker_brief.py +0 -0
  559. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_worker_events.py +0 -0
  560. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_worker_resumes.py +0 -0
  561. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_worker_scenarios.py +0 -0
  562. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_worker_surfaces.py +0 -0
  563. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_workerstate_said.py +0 -0
  564. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_workflows.py +0 -0
  565. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_zz_chipverbs.py +0 -0
  566. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_zz_lastword.py +0 -0
  567. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/tests/test_zz_setupchat.py +0 -0
  568. {taskuary-0.3.7.0 → taskuary-0.3.7.2}/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.7.0
3
+ Version: 0.3.7.2
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.7.0&asof=2026-09-28T1810)](https://pypi.org/project/taskuary/)
72
+ [![PyPI](https://img.shields.io/pypi/v/taskuary.svg?cacheSeconds=300&release=0.3.7.2&asof=2026-09-28T1810)](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.7.0**—so breaking changes are still possible before 1.0.
86
+ Taskuary is early—currently **v0.3.7.2**—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
@@ -100,47 +100,50 @@ Taskuary is early—currently **v0.3.7.0**—so breaking changes are still possi
100
100
  One request, from arrival to your approval. Follow Ruth's request for the latest vendor spend
101
101
  numbers through the real app, using fictional demo data.
102
102
 
103
- ### 1. Connect every system. Keep control.
103
+ ### 1. Everything lands in one place
104
104
 
105
- Mail, chats, issue trackers, alerts, and reports land on one Timeline. See what arrived,
106
- what became a task, and what needs you without opening every system in turn.
105
+ Mail, chats, issue trackers, alerts, and reports arrive on one rail, sorted by what they need:
106
+ **Urgent**, **On you**, **For later**, the Advisor's ideas, and **FYI**. Beside it, the Assistant
107
+ says what came in and who is waiting, so you never open each system in turn.
107
108
 
108
- ![An annotated Timeline: each item shows its original time below its source—Email, Teams, WhatsApp, GitHub, SQL report, Assistant, Daily digest, or Calendar.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/01-timeline-sources-and-times.png)
109
+ ![The work rail sorting today's items into Urgent, On you, Advisor ideas, and FYI, beside the Assistant's morning summary of who is waiting.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/01-timeline-sources-and-times.png?v=95295dd)
109
110
 
110
- ### 2. Turn incoming work into tasks
111
+ ### 2. Let the Assistant walk you through it
111
112
 
112
- Ruth asks for the August total, the change from July, and a breakdown by category.
113
- Taskuary creates a task with the original request and assigns it to an agent.
113
+ Choose **Walk me through my tasks**. The Assistant puts one item on the table at a time, as the
114
+ same task card you would open yourself, with the next actions under it: **Next**, **Write reply**,
115
+ **Start an agent**. The buttons always do the same thing; the chat line is for everything else,
116
+ and the Assistant already has the message, the thread, and your past decisions to answer from.
114
117
 
115
- ![Ruth's request becomes TQ-0018, with its instructions, owner, and agent work together on the task page.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/02-task.png)
118
+ ![The Assistant bringing Ruth's request into the chat as task TQ-0018, with Next, Write reply, and Start an agent under it.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/05-assistant.png?v=95295dd)
116
119
 
117
- ### 3. Watch the agent work
120
+ ### 3. Every request becomes a task with its whole story
118
121
 
119
- Open the task to follow the analysis. Here, the general agent prepares the numbers,
120
- checks that the categories add up, and drafts a reply.
122
+ Open any item to see how it got here: the message, what triage decided and why, which agent
123
+ has it, and where the reply stands.
121
124
 
122
- ![The general agent's completed vendor spend analysis, with category totals, comparison, and source.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/03-agent.png)
125
+ ![Ruth's request on its task: the message, the triage decision, the agent, and the reply still to write.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/02-task.png?v=95295dd)
123
126
 
124
- ### 4. Approve the outcome
127
+ ### 4. The agent works inside the chat
125
128
 
126
- The reply waits in **Review**, beside the request that started it. Read it, edit it,
127
- and choose **Approve & send** when it is ready.
129
+ **Start an agent** and it works in the same card. Here the analyst prepares the numbers, checks
130
+ that the categories add up, names its source, and drafts the reply. Nothing is sent.
128
131
 
129
- ![Review shows Ruth's original request and the prepared reply, with Approve & send waiting for the owner.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/04-review.png)
132
+ ![The analyst's finished vendor spend analysis inside the chat, with category totals, the change from July, and its source.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/03-agent.png?v=95295dd)
130
133
 
131
- ### 5. Let the Assistant walk you through it
134
+ ### 5. The last word is yours
132
135
 
133
- Choose **Walk me through my tasks**. The Assistant brings one item into the conversation,
134
- explains what needs your attention, and puts the next action within reach.
136
+ The prepared reply waits on the task, beside the request that started it. Read it, edit it,
137
+ and choose **Approve & send** when it is ready.
135
138
 
136
- ![A close-up of the Assistant bringing Ruth's request into the conversation, with a link to its task.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/05-assistant.png)
139
+ ![The drafted reply to Ruth waiting on its task, with Approve & send, Reject, and Regenerate with AI.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/04-review.png?v=95295dd)
137
140
 
138
- ### 6. Start your day with the daily digest
141
+ ### 6. Start your day before it gets busy
139
142
 
140
- Open your morning brief to see what people need, what is in flight, and what is on your calendar.
141
- The day's meetings sit above the digest, so Ruth's request has a clear deadline: the operations review.
143
+ Each morning the Assistant opens with today's meetings on a timeline and who needs a word from you.
144
+ Ruth's request has a clear deadline: the 11:30 operations review.
142
145
 
143
- ![An animated close-up of the morning digest and calendar, showing the operations review and vendor planning meeting.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/06-morning.gif)
146
+ ![An animated close-up of the morning: today's meetings on a timeline, then the people and agents waiting.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/06-morning.gif?v=95295dd)
144
147
 
145
148
  ## Key features
146
149
 
@@ -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.7.0&asof=2026-09-28T1810)](https://pypi.org/project/taskuary/)
7
+ [![PyPI](https://img.shields.io/pypi/v/taskuary.svg?cacheSeconds=300&release=0.3.7.2&asof=2026-09-28T1810)](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.7.0**—so breaking changes are still possible before 1.0.
21
+ Taskuary is early—currently **v0.3.7.2**—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
@@ -35,47 +35,50 @@ Taskuary is early—currently **v0.3.7.0**—so breaking changes are still possi
35
35
  One request, from arrival to your approval. Follow Ruth's request for the latest vendor spend
36
36
  numbers through the real app, using fictional demo data.
37
37
 
38
- ### 1. Connect every system. Keep control.
38
+ ### 1. Everything lands in one place
39
39
 
40
- Mail, chats, issue trackers, alerts, and reports land on one Timeline. See what arrived,
41
- what became a task, and what needs you without opening every system in turn.
40
+ Mail, chats, issue trackers, alerts, and reports arrive on one rail, sorted by what they need:
41
+ **Urgent**, **On you**, **For later**, the Advisor's ideas, and **FYI**. Beside it, the Assistant
42
+ says what came in and who is waiting, so you never open each system in turn.
42
43
 
43
- ![An annotated Timeline: each item shows its original time below its source—Email, Teams, WhatsApp, GitHub, SQL report, Assistant, Daily digest, or Calendar.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/01-timeline-sources-and-times.png)
44
+ ![The work rail sorting today's items into Urgent, On you, Advisor ideas, and FYI, beside the Assistant's morning summary of who is waiting.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/01-timeline-sources-and-times.png?v=95295dd)
44
45
 
45
- ### 2. Turn incoming work into tasks
46
+ ### 2. Let the Assistant walk you through it
46
47
 
47
- Ruth asks for the August total, the change from July, and a breakdown by category.
48
- Taskuary creates a task with the original request and assigns it to an agent.
48
+ Choose **Walk me through my tasks**. The Assistant puts one item on the table at a time, as the
49
+ same task card you would open yourself, with the next actions under it: **Next**, **Write reply**,
50
+ **Start an agent**. The buttons always do the same thing; the chat line is for everything else,
51
+ and the Assistant already has the message, the thread, and your past decisions to answer from.
49
52
 
50
- ![Ruth's request becomes TQ-0018, with its instructions, owner, and agent work together on the task page.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/02-task.png)
53
+ ![The Assistant bringing Ruth's request into the chat as task TQ-0018, with Next, Write reply, and Start an agent under it.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/05-assistant.png?v=95295dd)
51
54
 
52
- ### 3. Watch the agent work
55
+ ### 3. Every request becomes a task with its whole story
53
56
 
54
- Open the task to follow the analysis. Here, the general agent prepares the numbers,
55
- checks that the categories add up, and drafts a reply.
57
+ Open any item to see how it got here: the message, what triage decided and why, which agent
58
+ has it, and where the reply stands.
56
59
 
57
- ![The general agent's completed vendor spend analysis, with category totals, comparison, and source.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/03-agent.png)
60
+ ![Ruth's request on its task: the message, the triage decision, the agent, and the reply still to write.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/02-task.png?v=95295dd)
58
61
 
59
- ### 4. Approve the outcome
62
+ ### 4. The agent works inside the chat
60
63
 
61
- The reply waits in **Review**, beside the request that started it. Read it, edit it,
62
- and choose **Approve & send** when it is ready.
64
+ **Start an agent** and it works in the same card. Here the analyst prepares the numbers, checks
65
+ that the categories add up, names its source, and drafts the reply. Nothing is sent.
63
66
 
64
- ![Review shows Ruth's original request and the prepared reply, with Approve & send waiting for the owner.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/04-review.png)
67
+ ![The analyst's finished vendor spend analysis inside the chat, with category totals, the change from July, and its source.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/03-agent.png?v=95295dd)
65
68
 
66
- ### 5. Let the Assistant walk you through it
69
+ ### 5. The last word is yours
67
70
 
68
- Choose **Walk me through my tasks**. The Assistant brings one item into the conversation,
69
- explains what needs your attention, and puts the next action within reach.
71
+ The prepared reply waits on the task, beside the request that started it. Read it, edit it,
72
+ and choose **Approve & send** when it is ready.
70
73
 
71
- ![A close-up of the Assistant bringing Ruth's request into the conversation, with a link to its task.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/05-assistant.png)
74
+ ![The drafted reply to Ruth waiting on its task, with Approve & send, Reject, and Regenerate with AI.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/04-review.png?v=95295dd)
72
75
 
73
- ### 6. Start your day with the daily digest
76
+ ### 6. Start your day before it gets busy
74
77
 
75
- Open your morning brief to see what people need, what is in flight, and what is on your calendar.
76
- The day's meetings sit above the digest, so Ruth's request has a clear deadline: the operations review.
78
+ Each morning the Assistant opens with today's meetings on a timeline and who needs a word from you.
79
+ Ruth's request has a clear deadline: the 11:30 operations review.
77
80
 
78
- ![An animated close-up of the morning digest and calendar, showing the operations review and vendor planning meeting.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/06-morning.gif)
81
+ ![An animated close-up of the morning: today's meetings on a timeline, then the people and agents waiting.](https://raw.githubusercontent.com/ldbumble/taskuary/master/docs/readme/06-morning.gif?v=95295dd)
79
82
 
80
83
  ## Key features
81
84
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "taskuary"
7
- version = "0.3.7.0"
7
+ version = "0.3.7.2"
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" }
@@ -18,7 +18,7 @@ model for coding sessions and a LIGHT model for the one-message jobs - triage, d
18
18
  summaries, the digest (llm.make_cli_llm). Naming the same CLI as both the coder and the triage
19
19
  brain is normal and cheap; it is the light gear that keeps it cheap.
20
20
  """
21
- import json
21
+ import json, re
22
22
 
23
23
  from . import climodels
24
24
 
@@ -106,7 +106,7 @@ def resolve(store, cfg, slot_key: str) -> dict:
106
106
  cli = _cli_of(cfg, store, name) if name else ''
107
107
  cat = climodels.catalog(cli)
108
108
  model, effort = climodels.split_pick(_gears(cfg, store, name).get('model') or '')
109
- out.update(model=model, effort=effort, choices=cat['choices'], efforts=_efforts(cat, model),
109
+ out.update(model=model, effort=effort, choices=cat['choices'], efforts=_efforts(cat, model), resolved=_resolved(cat, model),
110
110
  owner=f'the {cli} coding profile' if cli else '', owner_link='agents', cli=cli,
111
111
  display=cli,
112
112
  ready=bool(name))
@@ -159,7 +159,9 @@ def resolve(store, cfg, slot_key: str) -> dict:
159
159
  from .llm import ASSISTANT_EFFORT
160
160
  effort = ASSISTANT_EFFORT.get(cli, '') # what concierge.brain runs when the pick names no effort
161
161
  said = (f"{dflt[7:]} effort" if dflt.startswith('effort:') else dflt) if dflt else f'the {cli} default'
162
- out.update(model=model, effort=effort, choices=cat['choices'], efforts=_efforts(cat, model),
162
+ # a blank box runs the gear's default model, so its efforts and version are that model's
163
+ seen = model or ('' if dflt.startswith('effort:') else dflt)
164
+ out.update(model=model, effort=effort, choices=cat['choices'], efforts=_efforts(cat, seen), resolved=_resolved(cat, seen),
163
165
  default_hint=f"{said} - the {'Assistant' if slot_key == 'concierge_ai' else 'light'} default",
164
166
  owner=owner, owner_link='' if slot_key == 'concierge_ai' else 'agents', cli=cli)
165
167
  if not light and not dflt: out['note'] = f'no light model set - this runs on the {cli} coding model, which is the expensive gear'
@@ -184,10 +186,25 @@ def resolve(store, cfg, slot_key: str) -> dict:
184
186
  return out
185
187
 
186
188
 
189
+ def _entry(cat: dict, model: str) -> dict:
190
+ """The catalogue row a model names: the exact id, else an alias (`sonnet`) read as the NEWEST id of that family, which is
191
+ what the CLI itself runs - so an alias pick shows its efforts and the version it lands on."""
192
+ ms = cat.get('models') or []
193
+ if (hit := next((m for m in ms if m.get('id') == model), None)): return hit
194
+ fam = [m for m in ms if model and re.fullmatch(rf'claude-{re.escape(model)}-\d[\w-]*', str(m.get('id')))]
195
+ return max(fam, key=lambda m: [int(n) for n in re.findall(r'\d+', m['id'])], default={})
196
+
197
+
187
198
  def _efforts(cat: dict, model: str) -> list:
188
199
  """The reasoning levels THIS model offers, per the CLI's own catalogue. Empty for every CLI
189
200
  whose effort flag we cannot spell - climodels documents why muse is deliberately empty."""
190
- return next((m.get('efforts') or [] for m in cat.get('models') or [] if m.get('id') == model), [])
201
+ return _entry(cat, model).get('efforts') or []
202
+
203
+
204
+ def _resolved(cat: dict, model: str) -> str:
205
+ """The id an alias lands on ('' when the pick already is an id, or nothing is known)."""
206
+ i = _entry(cat, model).get('id') or ''
207
+ return i if i != model else ''
191
208
 
192
209
 
193
210
  # What a blank model box actually means per provider, copied from llm.make_llm so the UI can
@@ -188,6 +188,22 @@ def split(text: str, limit: int = HARD) -> list:
188
188
  return out
189
189
 
190
190
 
191
+ def json_rows(text: str) -> str:
192
+ """Rows that arrived as one JSON object per line - a report whose summary never came, the demo's - as a markdown table,
193
+ so the door lays them out like any other table ({"site": "Lakeview", "headcount": 112} went to the phone as that, braces
194
+ and quotes, 2026-09-30 phone test). Anything that is not ALL flat objects is left exactly as it was."""
195
+ import json
196
+ lines = [l.strip() for l in str(text or '').splitlines() if l.strip()]
197
+ if len(lines) < 2: return text
198
+ try: rows = [json.loads(l) for l in lines]
199
+ except ValueError: return text
200
+ if not all(isinstance(r, dict) and r and all(not isinstance(v, (dict, list)) for v in r.values()) for r in rows): return text
201
+ cols = list(dict.fromkeys(k for r in rows for k in r))
202
+ cell = lambda v: '' if v is None else str(v).replace('|', '/')
203
+ return '\n'.join(['| ' + ' | '.join(cols) + ' |', '|' + '---|' * len(cols)]
204
+ + ['| ' + ' | '.join(cell(r.get(c)) for c in cols) + ' |' for r in rows])
205
+
206
+
191
207
  def blocks(body, limit: int = HARD) -> list:
192
208
  """A report as the messages a chat should receive: one per section, in order.
193
209
 
@@ -200,7 +216,7 @@ def blocks(body, limit: int = HARD) -> list:
200
216
  out = []
201
217
  for title, text in sections(strip_raw(body)):
202
218
  head = f'## {title}' if title else ''
203
- text = text.strip()
219
+ text = json_rows(text.strip()).strip()
204
220
  if not text: continue
205
221
  for piece in split(text, max(200, limit - len(head) - 2)):
206
222
  out.append(f'{head}\n{piece}'.strip() if head else piece)
@@ -80,7 +80,7 @@ def main():
80
80
  'email. Prints what it WOULD change and writes nothing unless --apply.')
81
81
  ap.add_argument('--apply', action='store_true', help='with --backfill-asks: actually write the changes')
82
82
  ap.add_argument('--include-closed', action='store_true', help='with --backfill-asks: closed tasks too')
83
- ap.add_argument('--evalset', choices=['build', 'share', 'evaluate', 'ablate'], metavar='ACTION',
83
+ ap.add_argument('--evalset', choices=['build', 'share', 'evaluate', 'ablate', 'jev'], metavar='ACTION',
84
84
  help='triage dataset: build (labelled cases from your verdicts -> ~/.taskuary/eval), '
85
85
  'evaluate (score the configured AI over them), ablate (score with and without memory), '
86
86
  'share (anonymised copy for tests/data)')
@@ -76,7 +76,11 @@ CHIP_HINTS = {'not_ours': 'File it - the card asks whether just this once, from
76
76
  'regular_agent': 'An agent takes it - triage picks a coding or a non-coding one, and you can change it on the card',
77
77
  'mine': "A task on your own list - no agent starts", 'next': 'Read it and move on',
78
78
  'defer': 'Put the task away until a day - it is Upcoming in Tasks and back on your rail that morning',
79
- 'continue': 'Pick the agent up where it left off - say what to tell it, or continue as is'}
79
+ 'continue': 'Pick the agent up where it left off - say what to tell it, or continue as is',
80
+ # the words that said nothing on hover: each line is what the handler does (TaskPage's own titles are the same sentences)
81
+ 'stop_agent': "Writes up what the session did, ends it, and drafts the reply to whoever asked (with nothing to write up it just stops it) - the task stays open",
82
+ 'close': 'Closes the task - it stops coming back to Work',
83
+ 'rerun': 'Runs the report again in the background - it comes back here when it is done'}
80
84
  # per kind, in the order they are offered. `next` is last on every one of them: moving on is always available,
81
85
  # and it is the one word that is never a decision about the thing itself.
82
86
  # THE SHORT LIST (the owner, 2026-09-25, word by word): eight buttons. Tomorrow and Later are gone - Next on
@@ -848,7 +852,12 @@ def _ask(store, llm, tid: int, item: dict | None, instruction: str, pile_items:
848
852
  return say, options, (decision or {}).get('verb') or ''
849
853
 
850
854
 
851
- def record(store, tid: int, role: str, text: str, card: dict = None):
855
+ # A line said to the PHONE only (the day's opener): kept in the conversation so the model and the desk know it was said,
856
+ # but never drawn by the desktop chat, whose own welcome already shows the day (the owner, 2026-09-30: "that's only for the phone")
857
+ PHONE_ACTOR = 'assistant-phone'
858
+
859
+
860
+ def record(store, tid: int, role: str, text: str, card: dict = None, phone: bool = False):
852
861
  # A presentation revision is transport freshness, not conversation meaning. Persisting it
853
862
  # made the same adjacent card into a second durable turn when surfacing changed only its read
854
863
  # state. Keep every semantic field, including nested FYI cards, and let the current HTTP
@@ -863,7 +872,7 @@ def record(store, tid: int, role: str, text: str, card: dict = None):
863
872
  return out
864
873
  saved_card = durable(card) if card else None
865
874
  body = redact(text).strip() + (f"\n\n{MARK}{json.dumps(saved_card, default=str)} -->" if saved_card else '')
866
- actor = 'owner' if role == 'user' else 'assistant'
875
+ actor = 'owner' if role == 'user' else PHONE_ACTOR if phone else 'assistant'
867
876
  actor_type = general.USER_TYPE if role == 'user' else general.ASSISTANT_TYPE
868
877
  # A double click must not duplicate the owner's words: those two calls may reach the backend
869
878
  # before React disables the button. The same guard also makes empty-pipe auto-advance
@@ -911,6 +920,7 @@ def record_related(store, dock_tid: int, item: dict | None, role: str, text: str
911
920
  def history(store, tid: int) -> list:
912
921
  out = []
913
922
  for c in general.chat_rows(store, tid):
923
+ if c.get('Actor') == PHONE_ACTOR: continue # said to the phone; the model still reads it through chat_rows
914
924
  body = c.get('Body') or ''
915
925
  m = _MARK.search(body)
916
926
  card = None
@@ -1518,7 +1528,11 @@ def call_turn(store, tid: int, call: dict, item: dict | None, text: str, actor:
1518
1528
  # ...but an AGENT is never put on an old task by default: a new ask typed while something sits on the table
1519
1529
  # became a run on that task (the owner, 2026-09-29: "if i ask for task it should not merge with old one or agent
1520
1530
  # run unless i ask"). These two must NAME the task; a new job is task.create_from_text, which the miss says.
1521
- if kind in toolcatalog.ATTACH_AGENT and not ref:
1531
+ # ...but CONTINUING is not attaching: it reopens the task's OWN saved session (the owner, 2026-09-30: "continue session
1532
+ # and tell the coding agent to merge it in" on a close-out card was refused for want of a ref, and the retry
1533
+ # fell to the close-out itself). On the table, with a session to reopen, the table's task is what they mean.
1534
+ resumes = kind == 'agent.continue' and not ref and it.get('tid') and store.resumable_session(it['tid'])
1535
+ if kind in toolcatalog.ATTACH_AGENT and not ref and not resumes:
1522
1536
  raise CallMiss(f"{kind} puts an agent on a task that already exists - name it (its TQ ref, the one on the table "
1523
1537
  "included). A new job is task.create_from_text. Nothing was started.")
1524
1538
  t = int(ref.group(1)) if ref else it.get('tid')
@@ -1865,7 +1879,9 @@ def surface(store, key: str = None, llm=None, actor: str = 'owner', only: str =
1865
1879
  f"I'll bring {'it' if n == 1 else 'them'} round again in a while - or open {'it' if n == 1 else 'one'} now.")
1866
1880
  # ...and the way to them NOW, by name: a Next under this line only said it again (2026-09-23, six days running).
1867
1881
  # What waits on the owner first - a close-out, an agent's question - then the rest.
1868
- opens = [{'verb': 'open', 'key': i['key'], 'label': f"Open {i.get('ref') or _title_cut(i.get('title') or 'it', 40)}"}
1882
+ # ...each saying WHAT it is, not a bare number (the owner, 2026-09-30: "have to write what they are about a little")
1883
+ opens = [{'verb': 'open', 'key': i['key'], 'label': ' · '.join(x for x in (
1884
+ f"Open {i.get('ref') or ''}".strip(), _title_cut(' '.join(str(i.get('title') or 'it').split()), 50)) if x)}
1869
1885
  for i in sorted(waiting, key=lambda i: not funnel.on_you(i))[:3]]
1870
1886
  elif only and left:
1871
1887
  # the filtered set is done; what remains is the rest of the pipe - offer it rather than call the day over
@@ -1896,7 +1912,7 @@ def surface(store, key: str = None, llm=None, actor: str = 'owner', only: str =
1896
1912
  return surface(store, None, llm, actor, only, trace, cancel, include_surfaced, exclude) if not key else {'item': None, 'say': say, 'options': [], 'chips': walk_chips(len(p['items'])), 'left': len(p['items'])}
1897
1913
  # FYIs have no action to take, so the normal walk brings four together. A row explicitly
1898
1914
  # clicked on the Timeline still opens by itself (`key` is set); only Next/Walk batches them.
1899
- if not key and item['lane'] == 'fyi':
1915
+ if not key and funnel.batchable(item):
1900
1916
  # a batch already assembled (funnel_selection._batch, which next_item returns on a processing store)
1901
1917
  # IS the batch: passed to fyi_batch as `first` it became its own member, "someone - 4 fyi" (2026-09-18)
1902
1918
  batch = item.get('items') if item.get('kind') == 'fyis' else funnel.fyi_batch(store, item)
@@ -2721,7 +2737,7 @@ def _chat_proposed(store, dock_tid: int, oid: str) -> bool:
2721
2737
 
2722
2738
 
2723
2739
  def say(store, text: str, key: str = None, llm=None, actor: str = 'owner', trace=None, cancel=None, item: dict | None = None,
2724
- open_card: str | None = None) -> dict:
2740
+ open_card: str | None = None, images: list | None = None) -> dict:
2725
2741
  """The owner's words, answered briefly - about the item on the table when there is one. The MODEL interprets
2726
2742
  them (PW-121): a question is answered, a subject named is pulled in, and a decision becomes a PROPOSAL the
2727
2743
  owner confirms (PW-123/124) - except Next, which moves the walk and marks nothing, and a reply request, which
@@ -2765,10 +2781,13 @@ def say(store, text: str, key: str = None, llm=None, actor: str = 'owner', trace
2765
2781
  # THE CARD BROWSED OPEN in the canvas (the canvas redesign, 2026-09-29): "this", "it", "set it up"
2766
2782
  # mean that card - a connector, a settings group, a report - when no item is on the table
2767
2783
  + (f"ON SCREEN NOW: {_cut(open_card, 300)} - 'this' means that card; its own operations set it up\n\n" if open_card and not item else '')
2768
- + f"The owner says: {text}\nAnswer them, briefly. If a look-up would answer it, CALL it now instead of saying you will. "
2784
+ + f"The owner says: {text}\n"
2785
+ # A PICTURE WITH THE LINE (the owner, 2026-09-30): an API brain sees it, a CLI brain reads the file it names
2786
+ + ("ATTACHED IMAGES (read these files - they are what the owner is showing you)\n" + "\n".join(images) + "\n" if images else '')
2787
+ + f"Answer them, briefly. If a look-up would answer it, CALL it now instead of saying you will. "
2769
2788
  + ('If this is a decision about the item on the table, CALL it (bucket table).' if item else
2770
2789
  'If they ask for something to be done, CALL it now - the card is their confirmation.'),
2771
- max_tokens=MAX_TOKENS) or '').strip()
2790
+ max_tokens=MAX_TOKENS, **({'images': general._images(images)} if images else {})) or '').strip()
2772
2791
  raw, call = parse_call(raw)
2773
2792
  # A LOOK-UP runs at once and comes straight back, because it changes nothing and waits for
2774
2793
  # nobody. The model then answers with what it read - one round only, so a question can never
@@ -47,6 +47,7 @@ ALLOWED_WRITES = (
47
47
  r'^/api/tasks$', # make a task
48
48
  r'^/api/tasks/\d+$', # ...and change it
49
49
  r'^/api/tasks/\d+/(comments|waitroom|not-a-task|assistant/(session|messages|stream|cancel))$',
50
+ r'^/api/prompt-image$', # a picture for a prompt: saved in the demo's own home
50
51
  r'^/api/messages/\d+/(file|promote|read)$', # the triage verdicts: the whole funnel
51
52
  r'^/api/reviews?/\d+/(hold|drop|edit)$', # ...but never /send
52
53
  r'^/api/board/notes', # the wall
@@ -62,14 +62,31 @@ def _connected(store) -> dict:
62
62
  return out
63
63
 
64
64
 
65
- def browse(store, area: str, section: str = None, open_: str = None) -> tuple[str, list]:
65
+ POLL_MAX, PAGE = 12, 10 # WhatsApp's poll holds twelve (whatsapp/poll.mjs cuts the rest SILENTLY); a page leaves room
66
+
67
+
68
+ def paged(rows: list, up: list, act: dict, page: int = 0) -> list:
69
+ """The picks for one page: ten rows, More when there are more, then the way back - never past twelve. The Connections
70
+ menu offered seventeen and a list of cards twelve plus Back, so the last five sections and Back could not be tapped
71
+ (2026-09-30 phone test)."""
72
+ if len(rows) + len(up) <= POLL_MAX: return rows + up
73
+ page = max(0, int(page or 0))
74
+ here = rows[page * PAGE:(page + 1) * PAGE]
75
+ more = [(f'More ({len(rows) - (page + 1) * PAGE} left)', {**act, 'page': page + 1})] if (page + 1) * PAGE < len(rows) else \
76
+ [('From the top', {**act, 'page': 0})]
77
+ return here + more + up[:POLL_MAX - len(here) - 1]
78
+
79
+
80
+ def browse(store, area: str, section: str = None, open_: str = None, page: int = 0) -> tuple[str, list]:
66
81
  """(what to say, the picks under it) for one browse step. Back is always a pick; so is the way up."""
67
- if area == 'connections': return _connections(store, section, open_)
68
- if area == 'settings': return _settings(store, section, open_)
82
+ if area == 'connections': return _connections(store, section, open_, page)
83
+ if area == 'settings': return _settings(store, section, open_, page)
84
+ if area == 'reports': return _reports(store, open_, page)
85
+ if area == 'hub': return _hub(store, open_, page)
69
86
  return f'I cannot browse {area} here yet - it is on the desktop.', []
70
87
 
71
88
 
72
- def _connections(store, section, open_):
89
+ def _connections(store, section, open_, page=0):
73
90
  from . import connectorcatalog
74
91
  cards = [c for c in connectorcatalog.cards() if c.get('group')]
75
92
  state = _connected(store)
@@ -87,18 +104,62 @@ def _connections(store, section, open_):
87
104
  rows = [c for c in cards if c['group'] == section]
88
105
  lines = [f"· {c['title']} - {state.get(c['type']) or ('planned' if c.get('planned') else 'not connected')}" for c in rows]
89
106
  picks = [(c['title'], {'t': 'browse', 'area': 'connections', 'section': section, 'open': c['type']}) for c in rows if not c.get('planned')]
90
- return f"CONNECTIONS · {section.upper()} · {len(rows)}\n" + '\n'.join(lines), picks[:12] + up
107
+ return (f"CONNECTIONS · {section.upper()} · {len(rows)}\n" + '\n'.join(lines),
108
+ paged(picks, up, {'t': 'browse', 'area': 'connections', 'section': section}, page))
91
109
  on = sum(1 for v in state.values() if v == 'connected')
92
110
  lines = [f"{g} · {sum(1 for c in cards if c['group'] == g)}" for g in groups]
93
111
  return (f'Connections - {on} connected. Pick a section:\n' + '\n'.join(f'· {x}' for x in lines),
94
- [(g, {'t': 'browse', 'area': 'connections', 'section': g}) for g in groups])
112
+ paged([(g, {'t': 'browse', 'area': 'connections', 'section': g}) for g in groups], [], {'t': 'browse', 'area': 'connections'}, page))
113
+
114
+
115
+ def _reports(store, open_, page=0):
116
+ """REPORTS, the desktop sidebar's list as picks (the owner, 2026-09-30): each one's clock and how its last run went, then
117
+ one opened - what it is, where it goes, what the last run said. Running or changing one is words: the model proposes it."""
118
+ from . import appfacts
119
+ rows = appfacts.reports(store)
120
+ up = [('Back to Reports', {'t': 'browse', 'area': 'reports'})]
121
+ def last(r): return 'never ran' if r['last_ok'] is None else 'last run FAILED' if r['last_ok'] is False else f"ran {r['last_at'][:16]}"
122
+ if open_:
123
+ r = next((x for x in rows if str(x['source_id']) == str(open_)), None)
124
+ if not r: return 'That report is not there any more.', up
125
+ said = str(r['last_said'] or '').strip()
126
+ text = (f"{'WORKFLOW' if r['workflow'] else 'REPORT'} · {r['title']}{'' if r['active'] else ' (off)'}\n"
127
+ f"When: {r['schedule'] or 'no clock - it runs when asked'}\nReaches you: {r['reach']}\nGoes: {r['goes']}\n{last(r).capitalize()}"
128
+ + (f"\n> {said}" if said else '')
129
+ + '\n\nSay "run it now", or what to change, and I propose it for your yes.')
130
+ return text, up
131
+ if not rows: return 'No reports or workflows are set up yet. Say "set up a report" and I walk you through one.', []
132
+ lines = [f"· {r['title']}{'' if r['active'] else ' (off)'} - {r['schedule'] or 'no clock'}; {last(r)}" for r in rows]
133
+ return (f'REPORTS · {len(rows)}\n' + '\n'.join(lines),
134
+ paged([(r['title'], {'t': 'browse', 'area': 'reports', 'open': r['source_id']}) for r in rows], [], {'t': 'browse', 'area': 'reports'}, page))
135
+
136
+
137
+ def _hub(store, open_, page=0):
138
+ """THE HUB, newest first as the desktop lists it (the owner, 2026-09-30): what the agents worked out, each one openable."""
139
+ up = [('Back to the Hub', {'t': 'browse', 'area': 'hub'})]
140
+ if open_:
141
+ e = store.lore_get(int(open_))
142
+ if not e: return 'That entry is not on the Hub any more.', up
143
+ body = str(e.get('Body') or '').strip()
144
+ text = (f"HUB · {e.get('Topic') or 'general'}\n*{e.get('Title') or ''}*\n"
145
+ f"by {e.get('Author') or 'an agent'} · score {e.get('Score') or 0} · {len(store.lore_comments(int(open_)) or [])} comments\n\n"
146
+ + (body[:1500] + ('…' if len(body) > 1500 else '')))
147
+ return text, up
148
+ posts = store.lore_posts(None, None, 40, 'new', 'live', None)
149
+ if not posts: return 'Nothing on the Hub yet - agents post what they work out as they go.', []
150
+ lines = [f"· {p.get('Title') or ''} ({p.get('Topic') or 'general'}, {p.get('Score') or 0})" for p in posts[:20]]
151
+ n = store.lore_count()
152
+ n = n.get('posts', len(posts)) if isinstance(n, dict) else n
153
+ return (f"HUB · {n} entries, newest first\n" + '\n'.join(lines),
154
+ paged([((p.get('Title') or '')[:90], {'t': 'browse', 'area': 'hub', 'open': p['LoreId']}) for p in posts[:20]], [],
155
+ {'t': 'browse', 'area': 'hub'}, page))
95
156
 
96
157
 
97
158
  def _schema() -> dict:
98
159
  return json.loads((Path(__file__).with_name('settings_schema.json')).read_text(encoding='utf-8'))
99
160
 
100
161
 
101
- def _settings(store, section, open_):
162
+ def _settings(store, section, open_, page=0):
102
163
  schema, st = _schema(), store.get_settings()
103
164
  knobs = schema.get('knobs') or {}
104
165
  up = [('Back to Settings', {'t': 'browse', 'area': 'settings'})]
@@ -106,6 +167,7 @@ def _settings(store, section, open_):
106
167
  v = st.get(k)
107
168
  kind = (knobs.get(k) or {}).get('type')
108
169
  if kind == 'switch': return 'on' if str(v if v is not None else '1').strip() not in ('0', 'false', 'off', '') else 'off'
170
+ if str(v).strip() == '*': return 'every other one set up, in order' # backup_brains' own wildcard, said in words
109
171
  return str(v) if v not in (None, '') else '(not set)'
110
172
  if open_:
111
173
  m = knobs.get(open_) or {'label': open_}
@@ -116,7 +178,8 @@ def _settings(store, section, open_):
116
178
  rows = [(k, m) for k, m in knobs.items() if m.get('group') == section]
117
179
  lines = [f"· {m.get('label') or k}: {value(k)}" for k, m in rows]
118
180
  return (f"SETTINGS · {section.upper()}\n" + ('\n'.join(lines) or 'Its controls are on the desktop.'),
119
- [(m.get('label') or k, {'t': 'browse', 'area': 'settings', 'section': section, 'open': k}) for k, m in rows][:12] + up)
181
+ paged([(m.get('label') or k, {'t': 'browse', 'area': 'settings', 'section': section, 'open': k}) for k, m in rows], up,
182
+ {'t': 'browse', 'area': 'settings', 'section': section}, page))
120
183
  groups = [g for g in schema.get('groups') or [] if any(m.get('group') == g for m in knobs.values())]
121
184
  return ('Settings - pick a section:\n' + '\n'.join(f'· {g}' for g in groups),
122
- [(g, {'t': 'browse', 'area': 'settings', 'section': g}) for g in groups])
185
+ paged([(g, {'t': 'browse', 'area': 'settings', 'section': g}) for g in groups], [], {'t': 'browse', 'area': 'settings'}, page))