taskuary 0.3.7.2__tar.gz → 0.3.7.4__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 (574) hide show
  1. {taskuary-0.3.7.2/taskuary.egg-info → taskuary-0.3.7.4}/PKG-INFO +3 -3
  2. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/README.md +2 -2
  3. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/pyproject.toml +1 -1
  4. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/aidefaults.py +1 -1
  5. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/assistant.py +4 -6
  6. taskuary-0.3.7.4/taskuary/background.py +125 -0
  7. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/channels.py +3 -0
  8. taskuary-0.3.7.4/taskuary/chatgptauth.py +268 -0
  9. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/concierge.py +89 -12
  10. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/connectorcatalog.json +14 -0
  11. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/funnel.py +5 -3
  12. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/general.py +8 -2
  13. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/hooks.py +70 -8
  14. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/ingest.py +24 -35
  15. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/llm.py +102 -6
  16. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/problems.py +17 -4
  17. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_reads.py +9 -2
  18. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/remote_assistant.py +8 -3
  19. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/server.py +66 -8
  20. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/settings_defaults.json +0 -2
  21. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/settings_schema.json +0 -14
  22. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/store.py +9 -6
  23. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/terminal.py +44 -12
  24. taskuary-0.3.7.2/taskuary/web/assets/AssistantGame-BZJj3XwN.js → taskuary-0.3.7.4/taskuary/web/assets/AssistantGame-BY2_QJhD.js +3 -3
  25. taskuary-0.3.7.2/taskuary/web/assets/GameScene-ATV2SyYO.js → taskuary-0.3.7.4/taskuary/web/assets/GameScene-CfqHp5AM.js +1 -1
  26. taskuary-0.3.7.2/taskuary/web/assets/HubView-DtpvsbeL.js → taskuary-0.3.7.4/taskuary/web/assets/HubView-vKlOnOiz.js +1 -1
  27. taskuary-0.3.7.2/taskuary/web/assets/StudioScene-D4IlNRXi.js → taskuary-0.3.7.4/taskuary/web/assets/StudioScene-Dq2jBqvJ.js +1 -1
  28. taskuary-0.3.7.2/taskuary/web/assets/TasksView-B7m8zBYu.js → taskuary-0.3.7.4/taskuary/web/assets/TasksView-kfwgqrNS.js +1 -1
  29. taskuary-0.3.7.4/taskuary/web/assets/index-BUNEVOG_.css +32 -0
  30. taskuary-0.3.7.2/taskuary/web/assets/index-BKrB4ObA.js → taskuary-0.3.7.4/taskuary/web/assets/index-CMKdW3on.js +152 -152
  31. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/index.html +2 -2
  32. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/workerstate.py +18 -4
  33. {taskuary-0.3.7.2 → taskuary-0.3.7.4/taskuary.egg-info}/PKG-INFO +3 -3
  34. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary.egg-info/SOURCES.txt +13 -7
  35. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_advisor_idea_words.py +2 -2
  36. taskuary-0.3.7.4/tests/test_api_web.py +131 -0
  37. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant_ideas.py +5 -3
  38. taskuary-0.3.7.4/tests/test_background_work.py +193 -0
  39. taskuary-0.3.7.4/tests/test_canvas_relay.py +112 -0
  40. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chat_is_not_one_task.py +12 -42
  41. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chat_retention.py +1 -1
  42. taskuary-0.3.7.4/tests/test_chatgptauth.py +155 -0
  43. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_concierge.py +38 -4
  44. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_context.py +2 -1
  45. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_continue_session.py +32 -0
  46. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_core.py +2 -2
  47. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_freshness_walk.py +13 -6
  48. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_funnel.py +20 -1
  49. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_hooks_events.py +19 -0
  50. taskuary-0.3.7.4/tests/test_model_list.py +30 -0
  51. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_one_pty_one_geometry.py +159 -108
  52. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_phone_browse_pass.py +69 -0
  53. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_problems.py +19 -0
  54. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_problems_dismiss.py +9 -4
  55. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_pypi_downloads.py +27 -0
  56. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_remote_sections.py +15 -10
  57. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_terminal.py +37 -0
  58. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_triage_joins.py +15 -0
  59. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_zz_toolcatalog.py +4 -1
  60. taskuary-0.3.7.2/taskuary/web/assets/index-DUxLnhhx.css +0 -32
  61. taskuary-0.3.7.2/tests/test_canvas_relay.py +0 -52
  62. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/LICENSE +0 -0
  63. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/setup.cfg +0 -0
  64. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/__init__.py +0 -0
  65. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/acp.py +0 -0
  66. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/agents.py +0 -0
  67. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/aisetup.py +0 -0
  68. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/alchemy.py +0 -0
  69. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/appfacts.py +0 -0
  70. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/artifacts.py +0 -0
  71. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/assistantblocks.py +0 -0
  72. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/autoreply.py +0 -0
  73. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/aws.py +0 -0
  74. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/azure.py +0 -0
  75. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/blackboard.py +0 -0
  76. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/brief.py +0 -0
  77. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/browserview.py +0 -0
  78. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/calendar.py +0 -0
  79. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/categories.py +0 -0
  80. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/chains.py +0 -0
  81. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/chatformat.py +0 -0
  82. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/chatservers.py +0 -0
  83. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/ci.py +0 -0
  84. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/cli.py +0 -0
  85. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/cli_connections.py +0 -0
  86. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/cli_install_terminal.py +0 -0
  87. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/cliinstall.py +0 -0
  88. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/climodels.py +0 -0
  89. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/clipool.py +0 -0
  90. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/clis.py +0 -0
  91. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/clisetup.py +0 -0
  92. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/coder.py +0 -0
  93. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/compose.py +0 -0
  94. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/config.py +0 -0
  95. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/connectorcatalog.py +0 -0
  96. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/context.py +0 -0
  97. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/continuity.py +0 -0
  98. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/counsel.py +0 -0
  99. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/databases.py +0 -0
  100. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/db.py +0 -0
  101. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/demo.py +0 -0
  102. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/deps.py +0 -0
  103. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/desktop.py +0 -0
  104. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/devtools.py +0 -0
  105. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/digest.py +0 -0
  106. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/docsync.py +0 -0
  107. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/doorway_browse.py +0 -0
  108. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/evalset.py +0 -0
  109. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/evening.py +0 -0
  110. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/files.py +0 -0
  111. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/funnel_presentation.py +0 -0
  112. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/funnel_selection.py +0 -0
  113. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/ghcloseout.py +0 -0
  114. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/github.py +0 -0
  115. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/guard.py +0 -0
  116. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/handbook.py +0 -0
  117. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/handraise.py +0 -0
  118. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/histgen.py +0 -0
  119. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/hub.py +0 -0
  120. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/images.py +0 -0
  121. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/imapmail.py +0 -0
  122. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/imessage.py +0 -0
  123. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/intacct.py +0 -0
  124. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/interview.py +0 -0
  125. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/invoice_workflow.py +0 -0
  126. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/jev.py +0 -0
  127. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/knowledge.py +0 -0
  128. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/lanes.json +0 -0
  129. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/learn.py +0 -0
  130. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/learnedgraph.py +0 -0
  131. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/linkedin.py +0 -0
  132. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/live.py +0 -0
  133. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/logs.py +0 -0
  134. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/lookups.py +0 -0
  135. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/markets.py +0 -0
  136. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/mcp.py +0 -0
  137. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/messengers.py +0 -0
  138. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/msauth.py +0 -0
  139. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/mssql.py +0 -0
  140. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/operations.py +0 -0
  141. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/outbound.py +0 -0
  142. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/outbox.py +0 -0
  143. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/ownwork.py +0 -0
  144. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/playbooks.py +0 -0
  145. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/pm.py +0 -0
  146. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/policy.py +0 -0
  147. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing.py +0 -0
  148. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_all.py +0 -0
  149. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_inventory.py +0 -0
  150. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_membership.py +0 -0
  151. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_navigation.py +0 -0
  152. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_order.py +0 -0
  153. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_projection.py +0 -0
  154. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_rail.py +0 -0
  155. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_startup.py +0 -0
  156. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/processing_unread.py +0 -0
  157. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/projects.py +0 -0
  158. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/promptmap.py +0 -0
  159. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/proof.py +0 -0
  160. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/proposals.py +0 -0
  161. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/quickbooks.py +0 -0
  162. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/rank.py +0 -0
  163. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/redact.py +0 -0
  164. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/remind.py +0 -0
  165. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/reports.py +0 -0
  166. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/research.py +0 -0
  167. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/reshape.py +0 -0
  168. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/responder.py +0 -0
  169. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/retention.py +0 -0
  170. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/robinhood.py +0 -0
  171. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/routing.py +0 -0
  172. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/routingmemory.py +0 -0
  173. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/scopes.py +0 -0
  174. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/selfclose.py +0 -0
  175. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/semantic.py +0 -0
  176. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/senders.py +0 -0
  177. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/session_artifacts.py +0 -0
  178. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/sessionfiles.py +0 -0
  179. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/settings_schema.py +0 -0
  180. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/setup.py +0 -0
  181. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/sharepoint.py +0 -0
  182. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/sheets.py +0 -0
  183. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/simplefin.py +0 -0
  184. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/skillimport.py +0 -0
  185. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/skills/soul-interview/SKILL.md +0 -0
  186. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/skills/taskuary-setup/SKILL.md +0 -0
  187. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/social.py +0 -0
  188. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/spawn.py +0 -0
  189. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/taskstate.py +0 -0
  190. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/teller.py +0 -0
  191. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/agent.md +0 -0
  192. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/analyst.md +0 -0
  193. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/coder.md +0 -0
  194. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/coordinator.md +0 -0
  195. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/counsel.md +0 -0
  196. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/digest.md +0 -0
  197. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/history/counsel-0.3.3.5.md +0 -0
  198. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/history/counsel-0.3.6.8.md +0 -0
  199. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/history/counsel-0.3.6.9-examples.md +0 -0
  200. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/history/counsel-0.3.6.9-remind.md +0 -0
  201. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/history/counsel-0.3.6.9.md +0 -0
  202. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/how-it-works.md +0 -0
  203. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/learned.md +0 -0
  204. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/marketer.md +0 -0
  205. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/playbook.md +0 -0
  206. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/profile.md +0 -0
  207. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/researcher.md +0 -0
  208. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/soul.md +0 -0
  209. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/style.md +0 -0
  210. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/trader.md +0 -0
  211. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/templates/triage.md +0 -0
  212. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/testing.py +0 -0
  213. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/toil.py +0 -0
  214. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/toolcatalog.py +0 -0
  215. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/treg.py +0 -0
  216. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/triage.py +0 -0
  217. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/triage_kind_rules.json +0 -0
  218. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/update.py +0 -0
  219. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/verdicts.py +0 -0
  220. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/voice.py +0 -0
  221. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/wabridge.py +0 -0
  222. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/waitroom.py +0 -0
  223. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/walk.py +0 -0
  224. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/assets/RoundedBoxGeometry-BE4rf3sD.js +0 -0
  225. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/favicon.ico +0 -0
  226. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/favicon.png +0 -0
  227. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/walk/assistant.png +0 -0
  228. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/walk/board.png +0 -0
  229. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/walk/connections.png +0 -0
  230. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/walk/docs.png +0 -0
  231. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/walk/hub.png +0 -0
  232. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/walk/reports.png +0 -0
  233. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/walk/settings.png +0 -0
  234. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/web/walk/tasks.png +0 -0
  235. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/webguard.py +0 -0
  236. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/whatsapp/bridge.mjs +0 -0
  237. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/whatsapp/package.json +0 -0
  238. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/whatsapp/policy.mjs +0 -0
  239. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/whatsapp/poll.mjs +0 -0
  240. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/whatsapp/roster.mjs +0 -0
  241. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/whoami.py +0 -0
  242. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/witness.py +0 -0
  243. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/workflows.py +0 -0
  244. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary/zoho.py +0 -0
  245. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary.egg-info/dependency_links.txt +0 -0
  246. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary.egg-info/entry_points.txt +0 -0
  247. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary.egg-info/requires.txt +0 -0
  248. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/taskuary.egg-info/top_level.txt +0 -0
  249. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_acp.py +0 -0
  250. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_answer_route.py +0 -0
  251. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_answers_and_reminders.py +0 -0
  252. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_asking.py +0 -0
  253. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_environment.py +0 -0
  254. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_identity.py +0 -0
  255. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_profiles.py +0 -0
  256. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_reply.py +0 -0
  257. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_sentence.py +0 -0
  258. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_source.py +0 -0
  259. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_agent_wall.py +0 -0
  260. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_ai_defaults.py +0 -0
  261. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_aisetup.py +0 -0
  262. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_alchemy.py +0 -0
  263. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_an_inbox_is_not_a_destination.py +0 -0
  264. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_answered_yourself.py +0 -0
  265. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_answers.py +0 -0
  266. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_api.py +0 -0
  267. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_api_worker_signals.py +0 -0
  268. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_app_uptime.py +0 -0
  269. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_appfacts.py +0 -0
  270. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_approval_interrupt.py +0 -0
  271. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant.py +0 -0
  272. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant_acts.py +0 -0
  273. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant_blocks.py +0 -0
  274. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant_can_look_things_up.py +0 -0
  275. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant_doorways.py +0 -0
  276. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant_notifications.py +0 -0
  277. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant_presentation.py +0 -0
  278. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant_reactions.py +0 -0
  279. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_assistant_verdicts.py +0 -0
  280. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_async_triage.py +0 -0
  281. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_audit_chain.py +0 -0
  282. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_audit_fixes.py +0 -0
  283. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_auto_start.py +0 -0
  284. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_autoreply.py +0 -0
  285. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_aws_region.py +0 -0
  286. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_azure_discovery.py +0 -0
  287. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_banner_is_one_rule.py +0 -0
  288. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_blackboard.py +0 -0
  289. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_brain_layer.py +0 -0
  290. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_brain_reaches_the_session.py +0 -0
  291. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_browser_session.py +0 -0
  292. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_browser_workflow.py +0 -0
  293. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_browserview.py +0 -0
  294. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_button_promises.py +0 -0
  295. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_calendar.py +0 -0
  296. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_calendar_today.py +0 -0
  297. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_canvas_sections.py +0 -0
  298. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_card_story_phone.py +0 -0
  299. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_categories.py +0 -0
  300. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cc.py +0 -0
  301. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cc_triage.py +0 -0
  302. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chat_doors.py +0 -0
  303. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chat_echo_mail.py +0 -0
  304. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chat_freshness.py +0 -0
  305. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chat_proposals.py +0 -0
  306. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chat_queue.py +0 -0
  307. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chat_relationship.py +0 -0
  308. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chat_setup.py +0 -0
  309. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chatformat.py +0 -0
  310. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chatservers.py +0 -0
  311. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_chinese_clis.py +0 -0
  312. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_clean_context.py +0 -0
  313. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cli_connections.py +0 -0
  314. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cli_failure.py +0 -0
  315. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cli_help.py +0 -0
  316. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cli_install.py +0 -0
  317. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cli_install_terminal.py +0 -0
  318. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cli_on_fresh_path.py +0 -0
  319. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cli_setup.py +0 -0
  320. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cli_update.py +0 -0
  321. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_climodels.py +0 -0
  322. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_clipool.py +0 -0
  323. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_clis.py +0 -0
  324. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_closeout.py +0 -0
  325. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cloud.py +0 -0
  326. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cloud_sources.py +0 -0
  327. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_coder_session_continuity.py +0 -0
  328. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_completion_freshness.py +0 -0
  329. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_compose.py +0 -0
  330. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_compose_sources.py +0 -0
  331. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_compose_workflows.py +0 -0
  332. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_concierge_counsel.py +0 -0
  333. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_config.py +0 -0
  334. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_context_refresh_freshness.py +0 -0
  335. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_coordination.py +0 -0
  336. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_correction_doors.py +0 -0
  337. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_counsel.py +0 -0
  338. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_counsel_consumers.py +0 -0
  339. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_counsel_migration.py +0 -0
  340. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_counsel_sections.py +0 -0
  341. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_current_restore.py +0 -0
  342. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_cwlogs_paging.py +0 -0
  343. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_databases.py +0 -0
  344. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_demo.py +0 -0
  345. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_deps.py +0 -0
  346. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_desktop.py +0 -0
  347. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_devin.py +0 -0
  348. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_devtools.py +0 -0
  349. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_diff_review.py +0 -0
  350. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_digest_brief.py +0 -0
  351. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_dispatch_retries.py +0 -0
  352. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_docs_and_llm.py +0 -0
  353. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_docs_flow.py +0 -0
  354. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_duplicate_connectors.py +0 -0
  355. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_email_chains.py +0 -0
  356. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_email_identity_routing.py +0 -0
  357. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_ending_a_run_writes_the_answer.py +0 -0
  358. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_evening_brief.py +0 -0
  359. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_every_card_is_reachable.py +0 -0
  360. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_every_cli_startable.py +0 -0
  361. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_explicit_completion.py +0 -0
  362. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_factory.py +0 -0
  363. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_failed_acts.py +0 -0
  364. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_feed_working.py +0 -0
  365. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_files.py +0 -0
  366. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_final_answer_capture.py +0 -0
  367. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_finished_chat_stays_finished.py +0 -0
  368. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_floating_assistant.py +0 -0
  369. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_follow_up_verdict.py +0 -0
  370. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_fresh_evaluation.py +0 -0
  371. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_freshness.py +0 -0
  372. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_general.py +0 -0
  373. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_github_auto.py +0 -0
  374. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_github_images.py +0 -0
  375. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_guard.py +0 -0
  376. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_handbook.py +0 -0
  377. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_handbook_topics.py +0 -0
  378. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_handoff_channels.py +0 -0
  379. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_handraise.py +0 -0
  380. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_harvest.py +0 -0
  381. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_histgen.py +0 -0
  382. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_home_is_never_yours.py +0 -0
  383. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_hooks_version.py +0 -0
  384. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_ideas_triage.py +0 -0
  385. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_ignore_sender.py +0 -0
  386. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_ignore_sender_quiets_its_work.py +0 -0
  387. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_images.py +0 -0
  388. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_imap_catchup.py +0 -0
  389. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_imap_history.py +0 -0
  390. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_imapmail.py +0 -0
  391. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_imessage.py +0 -0
  392. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_inline_pictures.py +0 -0
  393. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_intacct.py +0 -0
  394. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_interrupted_work.py +0 -0
  395. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_invite_is_not_work.py +0 -0
  396. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_invoice_workflow.py +0 -0
  397. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_jev.py +0 -0
  398. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_judge_reads_the_conclusion.py +0 -0
  399. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_kind_dispatch.py +0 -0
  400. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_knowledge.py +0 -0
  401. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_lane_vocabulary.py +0 -0
  402. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_learnedgraph.py +0 -0
  403. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_lifecycle.py +0 -0
  404. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_lifespan_loops_are_guarded.py +0 -0
  405. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_linkedin.py +0 -0
  406. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_linkedin_signin.py +0 -0
  407. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_live.py +0 -0
  408. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_local_file.py +0 -0
  409. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_lookups.py +0 -0
  410. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_mail_catchup.py +0 -0
  411. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_mail_fetch_cost.py +0 -0
  412. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_mail_folders.py +0 -0
  413. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_mark_read.py +0 -0
  414. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_markets.py +0 -0
  415. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_mcp.py +0 -0
  416. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_meeting_prep.py +0 -0
  417. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_message_thread.py +0 -0
  418. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_messengers.py +0 -0
  419. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_morning_line.py +0 -0
  420. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_msauth.py +0 -0
  421. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_mssql.py +0 -0
  422. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_multi_question.py +0 -0
  423. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_muse.py +0 -0
  424. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_no_pydantic_v1.py +0 -0
  425. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_noop_ending.py +0 -0
  426. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_not_coding.py +0 -0
  427. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_note_retrieval.py +0 -0
  428. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_onboarding_walk.py +0 -0
  429. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_one_app_one_reason.py +0 -0
  430. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_only_your_own_thread.py +0 -0
  431. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_openai_compatible_brains.py +0 -0
  432. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_operations.py +0 -0
  433. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_outbound_reports.py +0 -0
  434. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_outbox.py +0 -0
  435. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_own_rows_no_reply.py +0 -0
  436. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_person_name.py +0 -0
  437. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_phone_picks.py +0 -0
  438. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_phone_walk_ux.py +0 -0
  439. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_playbook_setup.py +0 -0
  440. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_playbooks.py +0 -0
  441. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_pm.py +0 -0
  442. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_poll_lanes.py +0 -0
  443. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_poll_overlap.py +0 -0
  444. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_poll_parallel.py +0 -0
  445. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_poll_sources.py +0 -0
  446. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_pr_comments.py +0 -0
  447. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_procedure_selection.py +0 -0
  448. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_processing_demo_replay.py +0 -0
  449. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_processing_ledger.py +0 -0
  450. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_profile_is_a_role.py +0 -0
  451. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_projects.py +0 -0
  452. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_prompt_images.py +0 -0
  453. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_prompt_sources.py +0 -0
  454. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_promptmap_and_catalog.py +0 -0
  455. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_proof_ci_proposals.py +0 -0
  456. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_quickbooks.py +0 -0
  457. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_qwen.py +0 -0
  458. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_rail_decisions.py +0 -0
  459. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_rank.py +0 -0
  460. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_rate_limit.py +0 -0
  461. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_read_text.py +0 -0
  462. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_recent_closures.py +0 -0
  463. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_reclassify.py +0 -0
  464. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_redact.py +0 -0
  465. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_redact_doors.py +0 -0
  466. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_remind.py +0 -0
  467. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_remote_assistant.py +0 -0
  468. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_reply_always_drafts.py +0 -0
  469. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_reply_attachments.py +0 -0
  470. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_reply_channels.py +0 -0
  471. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_reply_envelope.py +0 -0
  472. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_reply_needs_a_correspondent.py +0 -0
  473. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_reply_sources.py +0 -0
  474. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_reply_voice.py +0 -0
  475. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_repo_choice_triage.py +0 -0
  476. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_report_alerts.py +0 -0
  477. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_report_deliver.py +0 -0
  478. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_report_headline.py +0 -0
  479. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_report_one_road.py +0 -0
  480. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_report_order_and_research.py +0 -0
  481. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_report_prompt_isolation.py +0 -0
  482. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_report_refusal.py +0 -0
  483. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_report_routing.py +0 -0
  484. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_report_triage.py +0 -0
  485. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_research.py +0 -0
  486. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_research_more.py +0 -0
  487. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_review_task.py +0 -0
  488. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_reviews.py +0 -0
  489. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_robinhood.py +0 -0
  490. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_routing_memory.py +0 -0
  491. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_s3_poll_paging.py +0 -0
  492. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_scopes.py +0 -0
  493. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_seed_fits_a_tty.py +0 -0
  494. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_seeded_reports.py +0 -0
  495. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_selector_vocabulary.py +0 -0
  496. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_semantic.py +0 -0
  497. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_send_outcomes.py +0 -0
  498. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_send_targets.py +0 -0
  499. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_sender_trust.py +0 -0
  500. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_sender_words.py +0 -0
  501. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_session_artifacts.py +0 -0
  502. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_session_continuity.py +0 -0
  503. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_session_endings.py +0 -0
  504. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_session_files.py +0 -0
  505. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_settings_defaults_json.py +0 -0
  506. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_settings_schema.py +0 -0
  507. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_setup.py +0 -0
  508. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_setup_skill.py +0 -0
  509. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_setup_walk.py +0 -0
  510. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_sheets_sharepoint.py +0 -0
  511. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_simplefin.py +0 -0
  512. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_skillimport.py +0 -0
  513. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_social.py +0 -0
  514. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_someone_else_replied.py +0 -0
  515. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_soul_interview.py +0 -0
  516. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_startup_contract.py +0 -0
  517. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_stay_open.py +0 -0
  518. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_stay_open_doors.py +0 -0
  519. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_store_app_cli.py +0 -0
  520. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_store_hotpaths.py +0 -0
  521. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_store_pokes.py +0 -0
  522. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_sync_clock.py +0 -0
  523. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_task_ask.py +0 -0
  524. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_task_checklist.py +0 -0
  525. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_task_controls_operations.py +0 -0
  526. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_task_identity.py +0 -0
  527. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_task_refs.py +0 -0
  528. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_task_tools.py +0 -0
  529. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_taskstate.py +0 -0
  530. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_telegram_buttons.py +0 -0
  531. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_teller.py +0 -0
  532. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_the_exe_ships_what_the_wheel_ships.py +0 -0
  533. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_the_split_holds.py +0 -0
  534. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_toil.py +0 -0
  535. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_tracker_revisions.py +0 -0
  536. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_treg.py +0 -0
  537. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_triage_cleanup.py +0 -0
  538. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_triage_dataset.py +0 -0
  539. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_triage_errors.py +0 -0
  540. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_triage_images.py +0 -0
  541. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_triage_pr_rule.py +0 -0
  542. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_triage_retry_sweep.py +0 -0
  543. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_triage_urgent.py +0 -0
  544. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_update.py +0 -0
  545. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_update_refused.py +0 -0
  546. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_urgent_and_handoff.py +0 -0
  547. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_verdict_paths.py +0 -0
  548. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_verdict_shape.py +0 -0
  549. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_verdict_sticks.py +0 -0
  550. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_vision.py +0 -0
  551. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_voice.py +0 -0
  552. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_wabridge.py +0 -0
  553. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_wabridge_log_trim.py +0 -0
  554. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_waitroom.py +0 -0
  555. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_walk_order.py +0 -0
  556. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_watermark.py +0 -0
  557. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_web_entrypoint.py +0 -0
  558. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_webguard.py +0 -0
  559. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_whatsapp_documents.py +0 -0
  560. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_whatsapp_images.py +0 -0
  561. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_whoami.py +0 -0
  562. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_why_not_started.py +0 -0
  563. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_withdrawn.py +0 -0
  564. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_witness.py +0 -0
  565. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_worker_brief.py +0 -0
  566. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_worker_events.py +0 -0
  567. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_worker_resumes.py +0 -0
  568. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_worker_scenarios.py +0 -0
  569. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_worker_surfaces.py +0 -0
  570. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_workerstate_said.py +0 -0
  571. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_workflows.py +0 -0
  572. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_zz_chipverbs.py +0 -0
  573. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_zz_lastword.py +0 -0
  574. {taskuary-0.3.7.2 → taskuary-0.3.7.4}/tests/test_zz_setupchat.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskuary
3
- Version: 0.3.7.2
3
+ Version: 0.3.7.4
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.2&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.4&asof=2026-10-01T1249)](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.2**—so breaking changes are still possible before 1.0.
86
+ Taskuary is early—currently **v0.3.7.4**—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
@@ -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.2&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.4&asof=2026-10-01T1249)](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.2**—so breaking changes are still possible before 1.0.
21
+ Taskuary is early—currently **v0.3.7.4**—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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "taskuary"
7
- version = "0.3.7.2"
7
+ version = "0.3.7.4"
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" }
@@ -211,7 +211,7 @@ def _resolved(cat: dict, model: str) -> str:
211
211
  # say it rather than showing an empty field that looks unconfigured.
212
212
  _CONN_DEFAULT = {'anthropic': 'claude-opus-5', 'openai': 'gpt-4o-mini', 'openrouter': 'openrouter/auto',
213
213
  'meta': 'muse-spark-1.2', 'ollama': '(required - name the model you pulled)',
214
- 'azure_openai': '(the deployment name on the card)'}
214
+ 'azure_openai': '(the deployment name on the card)', 'chatgpt': '(the first model your plan lists)'}
215
215
  _CONN_MODELS = {'anthropic': ['claude-opus-5', 'claude-sonnet-5', 'claude-haiku-4-5'],
216
216
  'openai': ['gpt-4o-mini', 'gpt-4o'],
217
217
  'openrouter': ['openrouter/auto', 'meta-llama/llama-3.3-70b-instruct'],
@@ -417,12 +417,10 @@ def health_ideas(store, now: datetime = None) -> list:
417
417
  'text': f"{r['title']} is a workflow that has never run - its clock is {r['schedule'] or 'not set'}.",
418
418
  'action': {'type': 'health', 'tab': 'Reports', 'hash': f"report={r['source_id']}", 'source_id': r['source_id'],
419
419
  'why': 'a workflow that never ran is either mis-clocked or waiting on a sign-in'}})
420
- for c in appfacts.connections(store):
421
- if c['active'] and c['last_error']:
422
- out.append({'key': f"health:connection:{c['connector_id']}", 'kind': 'health', 'sig': re.sub(r'\d+', '#', _short(c['last_error'], 60)),
423
- 'text': f"{c['name']} is erroring - {_short(c['last_error'], 80)}.",
424
- 'action': {'type': 'health', 'tab': 'Connections', 'hash': f"connector={c['type']}", 'connector_id': c['connector_id'],
425
- 'why': 'a connection that errors reads nothing until somebody looks'}})
420
+ # An erroring CONNECTION is not raised here: the rail already carries it as its own "stopped answering" row
421
+ # (funnel.broken_connections), and said twice it became an idea, then a task, then a coding agent investigating
422
+ # a connection the owner was looking at (the owner, 2026-09-30: "don't need both ... if it fails we don't need
423
+ # advisor to tell us").
426
424
  try:
427
425
  waiting = len(store.pending_triage(limit=50))
428
426
  brain = str(store.get_setting('triage_ai') or '').strip()
@@ -0,0 +1,125 @@
1
+ """What a Claude run left RUNNING when its turn ended - background shells, monitors, subagents. Claude's own word
2
+ first: its Stop payload lists them (`from_hook`); a Claude that does not send that list is read off its own
3
+ transcript instead (`pending` - the Stop hook names it: `transcript_path`).
4
+
5
+ Stop is the end of a RESPONSE. A coder that starts a long pull in the background, arms a monitor on it and says
6
+ "re-pulling Jan 2024 now" ends its turn there, and the pane sits at a bare prompt with "1 shell, 1 monitor still
7
+ running" under it - so every surface read "coder is waiting on you" while the work went on (the owner, 2026-10-01:
8
+ "since there is a sub agent doing work the main chat might wait but the process is still going"). When a job
9
+ ends Claude is woken by its notification, takes a turn, and Stops again, so each Stop re-reads this.
10
+
11
+ The transcript says when a job STARTS (the tool result carries `backgroundTaskId`, a monitor's `taskId` +
12
+ `timeoutMs`, a subagent's `isAsync` + `agentId`) and when it ENDS (a `<task-notification>` with a `<status>`,
13
+ a monitor's "[Monitor expired" event, a TaskStop). Claude itself says some stops leave no marker - a shell killed
14
+ from its UI - so every job also carries its own deadline, and one past it is not counted.
15
+ """
16
+ import json, re, time
17
+ from datetime import datetime
18
+
19
+ SHELL_MS = 1_800_000 # Bash run_in_background's default timeout
20
+ _TID = re.compile(r'<task-id>([^<]+)</task-id>')
21
+ _ENDED = re.compile(r'<status>|\[Monitor expired')
22
+ # cheap substring gate: a transcript runs to tens of MB and only these lines can start or end a job
23
+ _MARKS = ('backgroundTaskId', '"timeoutMs"', 'async_launched', '<task-notification>', '"run_in_background":true',
24
+ '"name":"Monitor"', '"name":"TaskStop"', '"name":"Agent"', '"name":"Task"')
25
+
26
+
27
+ def _epoch(ts) -> float:
28
+ try: return datetime.fromisoformat(str(ts).replace('Z', '+00:00')).timestamp()
29
+ except ValueError: return time.time()
30
+
31
+
32
+ def _started(r: dict, inp: dict, at: float):
33
+ "(id, job) for a tool result that put work in the background, else None"
34
+ if r.get('backgroundTaskId'):
35
+ return r['backgroundTaskId'], {'kind': 'shell', 'what': inp.get('description') or str(inp.get('command') or '')[:120],
36
+ 'until': at + (inp.get('timeout') or SHELL_MS) / 1000}
37
+ if r.get('taskId') and 'timeoutMs' in r:
38
+ return r['taskId'], {'kind': 'monitor', 'what': inp.get('description') or '',
39
+ 'until': None if r.get('persistent') else at + (r.get('timeoutMs') or 0) / 1000}
40
+ if r.get('isAsync') and r.get('agentId'):
41
+ return r['agentId'], {'kind': 'agent', 'what': r.get('description') or inp.get('description') or '', 'until': None}
42
+
43
+
44
+ def pending(path, now: float = None) -> list:
45
+ """The jobs still running as of `now`: [{id, kind: shell|monitor|agent, what, until (epoch s, None = no deadline)}]."""
46
+ now, uses, jobs = now or time.time(), {}, {}
47
+ try: f = open(path, encoding='utf-8', errors='replace')
48
+ except (OSError, TypeError): return []
49
+ with f:
50
+ for l in f:
51
+ if not any(m in l for m in _MARKS): continue
52
+ try: o = json.loads(l)
53
+ except ValueError: continue
54
+ c = o.get('content') if o.get('type') == 'queue-operation' else (o.get('message') or {}).get('content')
55
+ if isinstance(c, str):
56
+ # the queued copy counts as well as the delivered one: the job has ended even before Claude reads it
57
+ if '<task-notification>' in c and _ENDED.search(c):
58
+ for i in _TID.findall(c): jobs.pop(i, None)
59
+ continue
60
+ for b in c if isinstance(c, list) else []:
61
+ if not isinstance(b, dict): continue
62
+ if b.get('type') == 'tool_use':
63
+ uses[b.get('id')] = b
64
+ if b.get('name') == 'TaskStop':
65
+ inp = b.get('input') or {}
66
+ jobs.pop(str(inp.get('task_id') or inp.get('shell_id') or ''), None)
67
+ elif b.get('type') == 'tool_result' and isinstance(o.get('toolUseResult'), dict):
68
+ got = _started(o['toolUseResult'], (uses.get(b.get('tool_use_id')) or {}).get('input') or {}, _epoch(o.get('timestamp')))
69
+ if got: jobs[got[0]] = got[1]
70
+ return [dict(id=k, **v) for k, v in jobs.items() if v['until'] is None or v['until'] > now]
71
+
72
+
73
+ def from_hook(p: dict):
74
+ """The jobs Claude itself says are running, off its Stop payload - None when this Claude does not send the list.
75
+
76
+ Claude 2.1.286 carries `background_tasks` on Stop and SubagentStop (measured 2026-10-01; the hooks reference does
77
+ not list it): [{id, type: shell|subagent, status, description, command|agent_type}], shrinking as each job ends.
78
+ A monitor is a `shell` there. The CLI's own word, so it needs no deadline; the transcript read is for a Claude
79
+ without it. `ambient` is the SDK's mark for housekeeping a host should not count as activity."""
80
+ got = p.get('background_tasks')
81
+ if not isinstance(got, list): return None
82
+ return [{'id': str(t.get('id') or ''), 'kind': 'agent' if t.get('type') == 'subagent' else str(t.get('type') or 'task'),
83
+ 'what': str(t.get('description') or t.get('command') or '')[:120], 'until': None}
84
+ for t in got if isinstance(t, dict) and t.get('status', 'running') == 'running' and not t.get('ambient')]
85
+
86
+
87
+ def is_wake(prompt) -> bool:
88
+ "a turn the CLI took because background work reported - a UserPromptSubmit nobody typed (Claude's, Copilot's)"
89
+ return str(prompt or '').lstrip().startswith(('<task-notification>', '<system_notification>'))
90
+
91
+
92
+ # COPILOT (1.0.86, measured 2026-10-01) sends no list on Stop, but says both ends in its hooks: a shell started with
93
+ # `mode: async` answers "<command started in background with shellId: 0>", and its end is a Notification
94
+ # `shell_completed` naming "(shellId: 0)" - then Copilot wakes and takes a turn on it. `detach` is the other way: a
95
+ # server left to outlive the session, which nobody wakes for, so it is not counted. No timeout is said, so a cap.
96
+ _STARTED = re.compile(r'started in background with shellId:\s*([\w-]+)')
97
+ _SHELL_ID = re.compile(r'shellId:\s*([\w-]+)')
98
+ CAP_S = 7200
99
+
100
+
101
+ def track(jobs: dict, p: dict) -> None:
102
+ "keep a session's own running jobs from its hook events (`jobs` lives on the pane: a pane and its jobs die together)"
103
+ ev = p.get('hook_event_name')
104
+ if ev == 'PostToolUse':
105
+ inp, res = p.get('tool_input') or {}, p.get('tool_result') or p.get('tool_response') or {}
106
+ m = _STARTED.search(str(res.get('text_result_for_llm') if isinstance(res, dict) else res))
107
+ if m and isinstance(inp, dict) and not inp.get('detach'):
108
+ jobs[m.group(1)] = {'id': m.group(1), 'kind': 'shell', 'what': str(inp.get('description') or inp.get('command') or '')[:120],
109
+ 'until': time.time() + CAP_S}
110
+ elif ev == 'Notification' and str(p.get('notification_type') or '').startswith('shell_'):
111
+ m = _SHELL_ID.search(str(p.get('message') or ''))
112
+ if m: jobs.pop(m.group(1), None)
113
+
114
+
115
+ def live(jobs, now: float = None) -> list:
116
+ "the ones of a recorded list still inside their deadline"
117
+ now = now or time.time()
118
+ return [j for j in jobs or [] if isinstance(j, dict) and (j.get('until') is None or j['until'] > now)]
119
+
120
+
121
+ def summary(jobs) -> str:
122
+ "'1 shell, 1 monitor still running' - the words Claude's own footer uses"
123
+ n = {}
124
+ for j in jobs: n[j['kind']] = n.get(j['kind'], 0) + 1
125
+ return ', '.join(f"{v} {k}{'' if v == 1 else 's'}" for k, v in n.items()) + ' still running' if n else ''
@@ -769,6 +769,9 @@ def retire_draft_answered_elsewhere(store, tid: int | None, sent: dict) -> list:
769
769
  where = f'TQ-{tid:04d}'
770
770
  state = ' The task is still open because it has other work in progress.' \
771
771
  if task.get('Status') not in ('done', 'dropped') else ' I marked the task done.'
772
+ # ...said in the Assistant's conversation - and never as a dead end: the page offers Next under the newest line that has
773
+ # nothing to press (the owner, 2026-09-30: "it can write that and close task ... but then should move to next or at least
774
+ # have buttons to navigate")
772
775
  from . import concierge, funnel, general
773
776
  concierge.record(store, general.dock_task(store)[0]['TaskId'], 'assistant',
774
777
  f'You replied {place} on {where}{excerpt}. '
@@ -0,0 +1,268 @@
1
+ """Sign in with ChatGPT - the owner's ChatGPT plan as a brain, no API key.
2
+
3
+ OpenAI's plan sharing for open-source, locally hosted apps (DevDay 2026, developers.openai.com/siwc):
4
+ the owner signs in once in their own browser, OpenAI registers a client for THIS install on the fly
5
+ (dynamic registration - there is no Taskuary app id to ship), and model calls are billed to their
6
+ ChatGPT allowance under a weekly cap they set per app. The Codex CLI already rode the plan for coding;
7
+ this is the same plan for the one-message jobs - triage, the Assistant, reports - which only ever
8
+ spoke to API keys.
9
+
10
+ The shape is msauth's: the refresh token is the card's secret, access tokens are minted from it and
11
+ cached, a rotated refresh token is handed to on_rotate so it survives a restart. What differs is the
12
+ sign-in itself: a browser redirect to a loopback listener on 127.0.0.1 (the only redirect OpenAI
13
+ takes for desktop apps), with PKCE, state and nonce fresh per attempt.
14
+
15
+ Limits the card states (developers.openai.com/siwc/.../preview-limitations): images, files, function tools and web
16
+ search where the model accepts them; no image generation, file search, code interpreter, computer use or hosted
17
+ connectors. Streamed and never stored on OpenAI's side; a cap reached stops the brain until the owner raises it.
18
+ """
19
+ import base64, hashlib, json, re, secrets, threading, time, uuid
20
+ from http.server import BaseHTTPRequestHandler, HTTPServer
21
+ from urllib.parse import parse_qs, urlencode, urlparse
22
+ import requests
23
+ from loguru import logger
24
+
25
+ AUTH = 'https://auth.openai.com'
26
+ API = 'https://api.openai.com/v1'
27
+ SCOPES = 'openid profile email offline_access resource.invoke chatgpt.tokens.use.direct'
28
+ PLAN_SCOPE = 'chatgpt.tokens.use.direct' # without it the sign-in worked and no call will
29
+ PORT = 1455 # OpenAI's example; anything on loopback is taken, so a busy port is not fatal
30
+ AGENT_NAME = 'Taskuary'
31
+
32
+ _CACHE, _LOCK = {}, threading.Lock() # refresh token -> (access, expires_at, current refresh token)
33
+ _FLOWS = {} # flow id -> one sign-in in progress
34
+ on_rotate = None # set by the server: (connector id, new refresh token) -> None
35
+
36
+
37
+ def host_id(cfg: dict) -> str:
38
+ """This install's stable, opaque id - usage is counted per host. Minted once, kept on the card."""
39
+ return (cfg or {}).get('host_id') or f'urn:uuid:{uuid.uuid4()}'
40
+
41
+
42
+ def _b64(b: bytes) -> str: return base64.urlsafe_b64encode(b).rstrip(b'=').decode()
43
+
44
+
45
+ class _Callback(BaseHTTPRequestHandler):
46
+ """One GET, the browser coming back from auth.openai.com. Everything else is a 404."""
47
+ def do_GET(self):
48
+ u = urlparse(self.path)
49
+ f = self.server.flow
50
+ if u.path != '/auth/callback':
51
+ self.send_response(404); self.end_headers(); return
52
+ q = {k: v[0] for k, v in parse_qs(u.query).items()}
53
+ if q.get('state') != f['state']: f['error'] = 'the sign-in came back for a different attempt - start it again'
54
+ elif q.get('error'): f['error'] = q.get('error_description') or q['error']
55
+ else: f['code'], f['client_id'], f['scope'] = q.get('code'), q.get('client_id') or f['client_id'], q.get('scope', '')
56
+ ok = not f.get('error')
57
+ body = (f"<!doctype html><meta charset=utf-8><title>Taskuary</title><body style='font:15px system-ui;margin:3em'>"
58
+ f"<h2>{'Signed in to ChatGPT' if ok else 'The sign-in did not finish'}</h2>"
59
+ f"<p>{'You can close this tab - Taskuary is finishing on its own.' if ok else f.get('error', '')}</p>").encode()
60
+ self.send_response(200); self.send_header('Content-Type', 'text/html; charset=utf-8'); self.end_headers()
61
+ self.wfile.write(body)
62
+ threading.Thread(target=self.server.shutdown, daemon=True).start()
63
+ def log_message(self, *a): pass # the request line would carry the code into the log
64
+
65
+
66
+ class _Server(HTTPServer):
67
+ # HTTPServer sets SO_REUSEADDR, and on Windows that lets a SECOND socket bind a port another one is listening on -
68
+ # a Codex login or an earlier sign-in holding 1455 would then share it, and the browser could come back to the
69
+ # wrong listener. Refused instead, so the next line moves to a free port.
70
+ allow_reuse_address = False
71
+
72
+
73
+ def _listen() -> HTTPServer:
74
+ for port in (PORT, 0):
75
+ try: return _Server(('127.0.0.1', port), _Callback)
76
+ except OSError: continue # a Codex login holding 1455 is not a reason to fail
77
+ raise RuntimeError('could not open a local port for the sign-in to return to')
78
+
79
+
80
+ def start(cfg: dict) -> dict:
81
+ """Begin a sign-in: the URL to open in the browser, and a flow id to poll with."""
82
+ for k, v in list(_FLOWS.items()):
83
+ if time.time() - v['at'] > 900: _stop(k)
84
+ verifier = _b64(secrets.token_bytes(32))
85
+ srv = _listen()
86
+ redirect = f'http://127.0.0.1:{srv.server_address[1]}/auth/callback'
87
+ flow = {'state': secrets.token_urlsafe(24), 'nonce': secrets.token_urlsafe(24), 'verifier': verifier, 'redirect': redirect,
88
+ 'host_id': host_id(cfg), 'client_id': (cfg or {}).get('client_id') or 'dynamic_agent_client', 'at': time.time(), 'srv': srv}
89
+ srv.flow = flow
90
+ threading.Thread(target=srv.serve_forever, daemon=True).start()
91
+ q = {'client_id': flow['client_id'], 'response_type': 'code', 'redirect_uri': redirect, 'scope': SCOPES,
92
+ 'resource': API, 'state': flow['state'], 'nonce': flow['nonce'], 'code_challenge_method': 'S256',
93
+ 'code_challenge': _b64(hashlib.sha256(verifier.encode()).digest())}
94
+ # a first sign-in registers this install; a card that already holds a client id signs back in to it
95
+ if q['client_id'] == 'dynamic_agent_client': q.update(agent_name_hint=AGENT_NAME, ext_agent_host_id=flow['host_id'])
96
+ fid = secrets.token_urlsafe(12)
97
+ _FLOWS[fid] = flow
98
+ return {'flow': fid, 'url': f'{AUTH}/api/accounts/authorize?{urlencode(q)}'}
99
+
100
+
101
+ def _stop(fid):
102
+ f = _FLOWS.pop(fid, None)
103
+ if f:
104
+ try: f['srv'].shutdown(); f['srv'].server_close()
105
+ except Exception: pass
106
+
107
+
108
+ def poll(fid: str) -> dict:
109
+ """{'pending': True} until the browser comes back; then the tokens and who signed in. Raises on refusal."""
110
+ f = _FLOWS.get(fid)
111
+ if not f: raise RuntimeError('no such sign-in in progress - start it again')
112
+ if f.get('error'): _stop(fid); raise RuntimeError(f['error'])
113
+ if not f.get('code'):
114
+ if time.time() - f['at'] > 900: _stop(fid); raise RuntimeError('the sign-in took too long - start it again')
115
+ return {'pending': True}
116
+ _stop(fid)
117
+ r = requests.post(f'{AUTH}/api/accounts/oauth/token', timeout=20, data={
118
+ 'grant_type': 'authorization_code', 'client_id': f['client_id'], 'code': f['code'],
119
+ 'code_verifier': f['verifier'], 'redirect_uri': f['redirect'], 'resource': API})
120
+ if r.status_code != 200: raise RuntimeError(f'OpenAI refused the sign-in ({r.status_code}): {_err(r)}')
121
+ t = r.json()
122
+ granted = str(t.get('scope') or f.get('scope') or '')
123
+ if PLAN_SCOPE not in granted.split():
124
+ raise RuntimeError('signed in, but ChatGPT plan usage was not granted - sign in again and allow it')
125
+ if not t.get('refresh_token'): raise RuntimeError('OpenAI returned no refresh token - offline access was not granted')
126
+ who = _identity(t.get('id_token'), f['nonce'])
127
+ return {'client_id': f['client_id'], 'host_id': f['host_id'], 'refresh_token': t['refresh_token'],
128
+ 'access_token': t['access_token'], 'expires_in': t.get('expires_in'), 'scope': granted, **who}
129
+
130
+
131
+ def _identity(id_token: str, nonce: str) -> dict:
132
+ """Who signed in, from the ID token. It came straight from the token endpoint over TLS, which OpenID Connect
133
+ accepts in place of a signature check (Core 3.1.3.7); issuer, expiry and our nonce are still checked."""
134
+ if not id_token: return {'sub': '', 'email': '', 'name': ''}
135
+ try:
136
+ part = id_token.split('.')[1]
137
+ c = json.loads(base64.urlsafe_b64decode(part + '=' * (-len(part) % 4)))
138
+ except (IndexError, ValueError): raise RuntimeError('OpenAI sent an ID token that does not parse')
139
+ if c.get('nonce') != nonce: raise RuntimeError('the sign-in answer does not belong to this attempt (nonce) - start it again')
140
+ if not str(c.get('iss') or '').startswith(AUTH): raise RuntimeError(f"unexpected token issuer {c.get('iss')!r}")
141
+ if c.get('exp') and float(c['exp']) < time.time(): raise RuntimeError('the sign-in answer had already expired - start it again')
142
+ return {'sub': c.get('sub', ''), 'email': c.get('email', ''), 'name': c.get('name', '')}
143
+
144
+
145
+ _DEAD = {'invalid_grant', 'invalid_refresh_token', 'token_expired', 'refresh_token_expired', 'refresh_token_invalidated', 'refresh_token_reused'}
146
+
147
+
148
+ def refresh(cfg: dict, refresh_token: str) -> dict:
149
+ r = requests.post(f'{AUTH}/api/accounts/oauth/token', timeout=20, data={
150
+ 'grant_type': 'refresh_token', 'client_id': cfg.get('client_id') or '', 'refresh_token': refresh_token, 'resource': API})
151
+ if r.status_code != 200:
152
+ code = _code(r)
153
+ if code in _DEAD: raise RuntimeError('the ChatGPT sign-in has lapsed - sign in again on the ChatGPT card')
154
+ raise RuntimeError(f'OpenAI would not renew the ChatGPT sign-in ({r.status_code}): {_err(r)}')
155
+ return r.json()
156
+
157
+
158
+ def access_token(cfg: dict, refresh_token: str) -> str:
159
+ """A live access token - cached until a minute before expiry; the mint is inside the lock so two callers never rotate
160
+ the refresh token out from under each other (a reused one is refused: refresh_token_reused)."""
161
+ if not refresh_token: raise RuntimeError('not signed in - click "Sign in with ChatGPT" on the ChatGPT card')
162
+ with _LOCK:
163
+ hit = _CACHE.get(refresh_token)
164
+ if hit and hit[1] > time.time() + 60: return hit[0]
165
+ rt_now = hit[2] if hit else refresh_token
166
+ t = refresh(cfg, rt_now)
167
+ new_rt = t.get('refresh_token') or rt_now
168
+ _CACHE[refresh_token] = (t['access_token'], time.time() + int(t.get('expires_in') or 3600), new_rt)
169
+ if new_rt != refresh_token and on_rotate and cfg.get('_cid'):
170
+ try: on_rotate(cfg['_cid'], new_rt)
171
+ except Exception as e: logger.warning(f'could not persist the rotated ChatGPT refresh token: {e}')
172
+ return t['access_token']
173
+
174
+
175
+ def revoke(cfg: dict, refresh_token: str) -> None:
176
+ """Sign out: end the renewable session at OpenAI, not just forget it here."""
177
+ try:
178
+ conf = requests.get(f'{AUTH}/.well-known/openid-configuration', timeout=15).json()
179
+ requests.post(conf['revocation_endpoint'], timeout=15, data={
180
+ 'token': refresh_token, 'token_type_hint': 'refresh_token', 'client_id': cfg.get('client_id') or ''})
181
+ except Exception as e: logger.warning(f'could not revoke the ChatGPT session at OpenAI: {e}')
182
+ with _LOCK: _CACHE.pop(refresh_token, None)
183
+
184
+
185
+ def models(token: str) -> list:
186
+ """[(slug, display name)] the plan offers - the ones OpenAI marks for listing."""
187
+ r = requests.get(f'{API}/models', headers={'Authorization': f'Bearer {token}'}, timeout=20)
188
+ if r.status_code != 200: raise RuntimeError(f'OpenAI would not list the plan models ({r.status_code}): {_err(r)}')
189
+ # the docs show `data`; the plan answers with `models` (slug / display_name / visibility), the shape Codex caches
190
+ # in ~/.codex/models_cache.json - read either, and say what came back rather than "no models" when neither is there
191
+ j = r.json()
192
+ rows = j.get('models') or j.get('data') or []
193
+ if not rows: raise RuntimeError(f'OpenAI listed no plan models (the answer carried: {sorted(j) or "nothing"})')
194
+ return [(m.get('slug') or m.get('id'), m.get('display_name') or m.get('slug') or m.get('id'))
195
+ for m in rows if m.get('visibility', 'list') == 'list' and (m.get('slug') or m.get('id'))]
196
+
197
+
198
+ # what each refusal means, and whether trying again can help (errors-and-recovery)
199
+ _CALL_ERRORS = {
200
+ 'subscription_sharing_usage_limit_exceeded': 'the weekly cap for Taskuary on your ChatGPT plan is used up - raise it in ChatGPT → Settings, or wait for it to reset',
201
+ 'subscription_sharing_user_not_eligible': 'this ChatGPT account is not eligible to share its plan with apps',
202
+ 'subscription_sharing_unsupported_capability': 'the plan does not serve part of this request (an image or a tool this model does not take)',
203
+ 'subscription_sharing_invalid_user': 'the ChatGPT sign-in is no longer valid - sign in again on the ChatGPT card',
204
+ }
205
+
206
+
207
+ class Unsupported(RuntimeError):
208
+ """The plan cannot serve something in this request (an image, a tool) - worth asking again without it."""
209
+
210
+
211
+ _OPTIONAL = ('text', 'max_output_tokens', 'instructions')
212
+
213
+
214
+ def complete(token: str, model: str, system: str, user: str, max_tokens: int, want: dict = None, timeout: int = 120,
215
+ images=None, web: bool = False) -> str:
216
+ """One answer, streamed (the plan serves nothing else) and never stored. Only `response.completed` is success:
217
+ a stream that stops early is a failure, not a short answer."""
218
+ content = [{'type': 'input_image', 'image_url': f'data:{ct};base64,{b64}'} for ct, b64 in (images or [])]
219
+ # no max_output_tokens: the plan's endpoint refuses it ("Unsupported parameter", measured 2026-09-30)
220
+ body = {'model': model, 'instructions': system, 'store': False, 'stream': True,
221
+ 'input': [{'role': 'user', 'content': content + [{'type': 'input_text', 'text': user}]}]}
222
+ if web: body['tools'] = [{'type': 'web_search'}]
223
+ if want: body['text'] = {'format': {'type': 'json_schema', 'name': want.get('name') or 'answer',
224
+ 'schema': want.get('schema') or want, 'strict': True}}
225
+ # An OPTIONAL field the plan names as unsupported is taken out and the question asked again - the schema then
226
+ # lives in the prompt, as it does for every brain that cannot carry one. `tools` is the caller's to drop.
227
+ for _ in range(3):
228
+ r = requests.post(f'{API}/responses', json=body, stream=True, timeout=timeout,
229
+ headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'})
230
+ if r.status_code == 200: break
231
+ field = (re.search(r'Unsupported parameter: ([\w]+)', _err(r)) or [None, None])[1]
232
+ r.close()
233
+ if field == 'tools': raise Unsupported(f'the plan does not take tools on this model: {_err(r)}')
234
+ if field in _OPTIONAL and field in body: body.pop(field); continue
235
+ code = _code(r)
236
+ if code == 'subscription_sharing_unsupported_capability': raise Unsupported(_CALL_ERRORS[code])
237
+ raise RuntimeError(_CALL_ERRORS.get(code) or f'ChatGPT plan call failed ({r.status_code}): {_err(r)}')
238
+ with r:
239
+ if r.status_code != 200: raise RuntimeError(f'ChatGPT plan call failed ({r.status_code}): {_err(r)}')
240
+ out, done = [], False
241
+ for line in r.iter_lines(decode_unicode=True):
242
+ if not line or not line.startswith('data:'): continue
243
+ data = line[5:].strip()
244
+ if data == '[DONE]': break
245
+ try: ev = json.loads(data)
246
+ except ValueError: continue
247
+ kind = ev.get('type')
248
+ if kind == 'response.output_text.delta': out.append(ev.get('delta') or '')
249
+ elif kind == 'response.completed': done = True; break
250
+ elif kind in ('response.failed', 'error'):
251
+ err = (ev.get('response') or {}).get('error') or ev.get('error') or {}
252
+ raise RuntimeError(_CALL_ERRORS.get(err.get('code')) or f"ChatGPT plan call failed: {err.get('message') or err.get('code') or kind}")
253
+ if not done: raise RuntimeError('the ChatGPT plan answer stopped before it finished - try again')
254
+ return ''.join(out)
255
+
256
+
257
+ def _code(r) -> str:
258
+ try: j = r.json()
259
+ except ValueError: return ''
260
+ e = j.get('error')
261
+ return (e.get('code') if isinstance(e, dict) else e) or ''
262
+
263
+
264
+ def _err(r) -> str:
265
+ try:
266
+ j = r.json(); e = j.get('error')
267
+ return (e.get('message') if isinstance(e, dict) else j.get('error_description') or e or j.get('detail')) or r.text[:300]
268
+ except ValueError: return r.text[:300]