taskuary 0.3.3.3__tar.gz → 0.3.3.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.
- {taskuary-0.3.3.3/taskuary.egg-info → taskuary-0.3.3.4}/PKG-INFO +3 -3
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/README.md +2 -2
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/pyproject.toml +1 -1
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/agents.py +30 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/assistant.py +49 -3
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/blackboard.py +16 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/channels.py +59 -2
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/coder.py +11 -3
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/concierge.py +208 -74
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/funnel.py +199 -70
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/general.py +31 -9
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/github.py +43 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/histgen.py +15 -3
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/messengers.py +28 -5
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/outbound.py +40 -11
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/outbox.py +49 -13
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/remote_assistant.py +22 -6
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/reports.py +25 -3
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/responder.py +8 -2
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/server.py +325 -58
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/session_artifacts.py +3 -3
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/store.py +156 -11
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/terminal.py +101 -13
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/verdicts.py +5 -4
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/wabridge.py +2 -0
- taskuary-0.3.3.3/taskuary/web/assets/GeneralWorkspace-BpOeJv4L.js → taskuary-0.3.3.4/taskuary/web/assets/GeneralWorkspace-CugBH3Pj.js +2 -2
- taskuary-0.3.3.3/taskuary/web/assets/HubView-BhP6xVxh.js → taskuary-0.3.3.4/taskuary/web/assets/HubView-CKPusAgu.js +1 -1
- taskuary-0.3.3.4/taskuary/web/assets/index-Dw2KclKc.css +32 -0
- taskuary-0.3.3.4/taskuary/web/assets/index-HgTGxZl1.js +1140 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/web/index.html +2 -2
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/whatsapp/bridge.mjs +45 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/whatsapp/package.json +1 -1
- taskuary-0.3.3.4/taskuary/whatsapp/roster.mjs +83 -0
- taskuary-0.3.3.4/taskuary/whatsapp/roster.test.mjs +43 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4/taskuary.egg-info}/PKG-INFO +3 -3
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary.egg-info/SOURCES.txt +10 -4
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_agent_environment.py +28 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_agent_wall.py +14 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_answered_yourself.py +47 -4
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_api.py +7 -2
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_assistant.py +17 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_assistant_reactions.py +43 -11
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_chat_doors.py +69 -0
- taskuary-0.3.3.4/tests/test_chat_freshness.py +85 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_concierge.py +96 -15
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_core.py +15 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_funnel.py +206 -11
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_general.py +37 -0
- taskuary-0.3.3.4/tests/test_github_images.py +117 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_histgen.py +7 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_lifecycle.py +20 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_live.py +7 -1
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_messengers.py +18 -2
- taskuary-0.3.3.4/tests/test_outbox.py +77 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_remote_assistant.py +5 -3
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_report_order_and_research.py +5 -4
- taskuary-0.3.3.4/tests/test_report_refusal.py +137 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_send_targets.py +57 -5
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_session_artifacts.py +6 -4
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_terminal.py +60 -11
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_wabridge.py +3 -2
- taskuary-0.3.3.3/taskuary/web/assets/index-B0RiuVsJ.js +0 -1140
- taskuary-0.3.3.3/taskuary/web/assets/index-BVYz6QiM.css +0 -32
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/LICENSE +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/setup.cfg +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/__init__.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/aisetup.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/artifacts.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/aws.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/azure.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/browserview.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/calendar.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/categories.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/ci.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/cli.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/climodels.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/clis.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/compose.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/config.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/context.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/counsel.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/db.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/demo.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/deps.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/desktop.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/devtools.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/digest.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/docsync.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/evalset.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/evening.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/guard.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/handbook.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/handraise.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/hooks.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/hub.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/imapmail.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/imessage.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/ingest.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/intacct.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/interview.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/invoice_workflow.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/knowledge.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/learn.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/learnedgraph.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/live.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/llm.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/logs.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/mcp.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/msauth.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/mssql.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/ownwork.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/phone.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/playbooks.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/pm.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/policy.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/problems.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/projects.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/promptmap.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/proof.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/proposals.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/quickbooks.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/rank.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/research.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/reshape.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/routing.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/scopes.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/selfclose.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/semantic.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/senders.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/setup.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/sharepoint.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/sheets.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/skills/soul-interview/SKILL.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/spawn.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/teller.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/templates/coder.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/templates/counsel.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/templates/digest.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/templates/how-it-works.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/templates/learned.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/templates/playbook.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/templates/soul.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/templates/style.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/templates/triage.md +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/testing.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/toil.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/triage.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/update.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/voice.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/waitroom.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/web/assets/GeneralWorkspace--IqTbJGE.css +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/web/favicon.ico +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/web/favicon.png +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/webguard.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/whatsapp/policy.mjs +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/whatsapp/policy.test.mjs +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/whoami.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/witness.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary/zoho.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary.egg-info/dependency_links.txt +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary.egg-info/entry_points.txt +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary.egg-info/requires.txt +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/taskuary.egg-info/top_level.txt +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_agent_identity.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_agent_source.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_aisetup.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_answers.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_assistant_opens.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_assistant_verdicts.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_async_triage.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_audit_chain.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_audit_fixes.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_aws_region.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_blackboard.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_browser_session.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_browserview.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_calendar.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_calendar_today.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_categories.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_cc.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_cc_triage.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_chat_is_not_one_task.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_chat_queue.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_climodels.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_clis.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_cloud.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_cloud_sources.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_compose.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_compose_sources.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_compose_workflows.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_config.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_context.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_counsel.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_cwlogs_paging.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_demo.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_deps.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_desktop.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_devtools.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_diff_review.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_digest_brief.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_docs_and_llm.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_docs_flow.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_duplicate_connectors.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_evening_brief.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_factory.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_feed_working.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_finished_chat_stays_finished.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_floating_assistant.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_follow_up_verdict.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_github_auto.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_guard.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_handbook.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_handbook_topics.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_handoff_channels.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_handraise.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_harvest.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_home_is_never_yours.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_ignore_sender.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_imap_history.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_imapmail.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_imessage.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_intacct.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_invoice_workflow.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_kind_dispatch.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_knowledge.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_learnedgraph.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_local_file.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_mail_folders.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_mark_read.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_mcp.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_meeting_prep.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_message_thread.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_msauth.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_mssql.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_noop_ending.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_not_coding.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_note_retrieval.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_outbound_reports.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_own_rows_no_reply.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_playbooks.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_pm.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_poll_overlap.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_poll_parallel.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_poll_sources.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_pr_comments.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_problems.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_problems_dismiss.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_projects.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_promptmap_and_catalog.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_proof_ci_proposals.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_pypi_downloads.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_quickbooks.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_rank.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_rate_limit.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_reclassify.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_reply_channels.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_reply_voice.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_report_alerts.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_report_triage.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_research.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_reviews.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_scopes.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_seed_fits_a_tty.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_seeded_reports.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_semantic.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_setup.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_sheets_sharepoint.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_someone_else_replied.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_soul_interview.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_stay_open.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_stay_open_doors.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_store_app_cli.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_store_hotpaths.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_sync_clock.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_task_identity.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_teller.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_toil.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_triage_dataset.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_triage_images.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_update.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_update_refused.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_urgent_and_handoff.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_verdict_paths.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_verdict_sticks.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_vision.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_voice.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_waitroom.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_watermark.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_web_entrypoint.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_webguard.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_whatsapp_documents.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_whatsapp_images.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_whoami.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_withdrawn.py +0 -0
- {taskuary-0.3.3.3 → taskuary-0.3.3.4}/tests/test_witness.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: taskuary
|
|
3
|
-
Version: 0.3.3.
|
|
3
|
+
Version: 0.3.3.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
|
|
@@ -62,7 +62,7 @@ Dynamic: license-file
|
|
|
62
62
|
# Taskuary
|
|
63
63
|
|
|
64
64
|
[](https://github.com/ldbumble/taskuary/actions/workflows/ci.yml)
|
|
65
|
-
[](https://pypi.org/project/taskuary/)
|
|
66
66
|
[](https://github.com/ldbumble/taskuary)
|
|
67
67
|
[](LICENSE)
|
|
68
68
|
|
|
@@ -73,7 +73,7 @@ your agents, and brings decisions back to you. Nothing sends or ships without yo
|
|
|
73
73
|
|
|
74
74
|

|
|
75
75
|
|
|
76
|
-
Taskuary is early—currently **v0.3.3.
|
|
76
|
+
Taskuary is early—currently **v0.3.3.4**—so breaking changes are still possible before 1.0.
|
|
77
77
|
|
|
78
78
|
<p align="center">
|
|
79
79
|
<a href="https://taskuary.com/demo/"><img
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Taskuary
|
|
2
2
|
|
|
3
3
|
[](https://github.com/ldbumble/taskuary/actions/workflows/ci.yml)
|
|
4
|
-
[](https://pypi.org/project/taskuary/)
|
|
5
5
|
[](https://github.com/ldbumble/taskuary)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@ your agents, and brings decisions back to you. Nothing sends or ships without yo
|
|
|
12
12
|
|
|
13
13
|

|
|
14
14
|
|
|
15
|
-
Taskuary is early—currently **v0.3.3.
|
|
15
|
+
Taskuary is early—currently **v0.3.3.4**—so breaking changes are still possible before 1.0.
|
|
16
16
|
|
|
17
17
|
<p align="center">
|
|
18
18
|
<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.3.
|
|
7
|
+
version = "0.3.3.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" }
|
|
@@ -13,6 +13,27 @@ from . import spawn
|
|
|
13
13
|
from .store import task_ref
|
|
14
14
|
from .clis import preset_args
|
|
15
15
|
|
|
16
|
+
_CLI_CHILDREN = set()
|
|
17
|
+
_CLI_CHILDREN_LOCK = threading.Lock()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def shutdown_cli_children():
|
|
21
|
+
"""Stop headless CLI calls that are still running when Taskuary shuts down.
|
|
22
|
+
|
|
23
|
+
Normal calls remove themselves when their one-shot process exits. Keeping the live Popen
|
|
24
|
+
objects here gives the application lifespan a deterministic cleanup road instead of leaving
|
|
25
|
+
an invisible Claude/Codex process behind after the server is gone.
|
|
26
|
+
"""
|
|
27
|
+
with _CLI_CHILDREN_LOCK:
|
|
28
|
+
children = list(_CLI_CHILDREN)
|
|
29
|
+
_CLI_CHILDREN.clear()
|
|
30
|
+
for child in children:
|
|
31
|
+
try:
|
|
32
|
+
if child.poll() is None: child.kill()
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
return len(children)
|
|
36
|
+
|
|
16
37
|
|
|
17
38
|
def _git(cwd, *args):
|
|
18
39
|
try:
|
|
@@ -371,6 +392,13 @@ def run_cli(profile: dict, prompt: str, trace, resume: str = None, cancel=None,
|
|
|
371
392
|
# to the saved profile, which is also used to open the interactive terminal TUI).
|
|
372
393
|
is_codex = _cli_name(name) == 'codex' and bool(args) and args[0] in ('exec', 'e')
|
|
373
394
|
if is_codex and '--json' not in args: args.append('--json')
|
|
395
|
+
# Same reasoning for the trust check: codex refuses to start outside a git repo with "Not
|
|
396
|
+
# inside a trusted directory and --skip-git-repo-check was not specified", and half of what
|
|
397
|
+
# this app asks a CLI runs in ~/.taskuary/scratch on purpose - the classifier, the drafter,
|
|
398
|
+
# STYLE.md generation - none of which is a checkout. Headlessly there is nobody to answer the
|
|
399
|
+
# trust prompt, so every one of those died before the model was reached (issue #34). The flag
|
|
400
|
+
# only relaxes WHERE codex may run; the sandbox flags decide what it may do.
|
|
401
|
+
if is_codex and '--skip-git-repo-check' not in args: args.append('--skip-git-repo-check')
|
|
374
402
|
cmd = _resolve_cmd(name) + args
|
|
375
403
|
# which model works it: profile default, or a per-run override from the UI. The flag
|
|
376
404
|
# name is configurable because every CLI spells it differently (claude/codex: --model).
|
|
@@ -393,6 +421,7 @@ def run_cli(profile: dict, prompt: str, trace, resume: str = None, cancel=None,
|
|
|
393
421
|
p = spawn.popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
394
422
|
text=True, encoding='utf-8', errors='replace', cwd=cwd, shell=False,
|
|
395
423
|
env=env)
|
|
424
|
+
with _CLI_CHILDREN_LOCK: _CLI_CHILDREN.add(p)
|
|
396
425
|
except PermissionError as e:
|
|
397
426
|
# which() found something that cannot be executed from here. "Not installed" sent people
|
|
398
427
|
# off to reinstall a CLI that was already there; the reason is in denied_msg.
|
|
@@ -466,6 +495,7 @@ def run_cli(profile: dict, prompt: str, trace, resume: str = None, cancel=None,
|
|
|
466
495
|
p.wait()
|
|
467
496
|
finally:
|
|
468
497
|
killer.cancel()
|
|
498
|
+
with _CLI_CHILDREN_LOCK: _CLI_CHILDREN.discard(p)
|
|
469
499
|
err_t.join(5) # the exit code can land before the stderr reader has appended - 'boom' read as 'no output' on a fast CI box
|
|
470
500
|
if p.returncode != 0:
|
|
471
501
|
if cancel is not None and cancel.is_set(): raise RuntimeError('cancelled')
|
|
@@ -327,7 +327,8 @@ CONTRACT = ('\n\nYou are writing your POST on the owner\'s Timeline - the short
|
|
|
327
327
|
'already and yours is ignored for those; this is for idea:* lines>", '
|
|
328
328
|
'"why": "<one line: what this rests on - the mail, the date, the silence, the pattern - named as it appears in what you '
|
|
329
329
|
'were given (sender, subject, mid, TQ-ref), so the owner can check it>", "mid": <the message id it is '
|
|
330
|
-
'about, or null>, "task": "<idea:* only - a task title the owner could accept as-is, or null>"
|
|
330
|
+
'about, or null>, "task": "<idea:* only - a task title the owner could accept as-is, or null>", '
|
|
331
|
+
'"kind": "<when task is not null: coding if it needs a repository/system changed, otherwise general>"}], '
|
|
331
332
|
'"notes": "<your note to the next check, under 120 words: FACTS AND TIMINGS ONLY - what you looked at and found nothing in, '
|
|
332
333
|
'the date or silence length at which something becomes worth raising, a fact you settled so it need not be worked out again. '
|
|
333
334
|
'Never a standing rule about what to ignore or what is noise: the instruction decides that, and a note that says '
|
|
@@ -558,6 +559,8 @@ def parse(store, text: str, cands: list, max_lines: int = MAX_LINES) -> list:
|
|
|
558
559
|
mid = s.get('mid') if isinstance(s.get('mid'), int) else None
|
|
559
560
|
title = _short(s.get('task'), 120) or None
|
|
560
561
|
act = {'type': 'task', 'mid': mid, 'title': title} if title and mid else {'type': 'message', 'mid': mid} if mid else {'type': 'note'}
|
|
562
|
+
if act['type'] == 'task' and str(s.get('kind') or '').lower() in ('coding', 'general'):
|
|
563
|
+
act['kind'] = str(s['kind']).lower()
|
|
561
564
|
# a line that NAMES a task belongs to it, whatever else it carries: without the tid the
|
|
562
565
|
# pipe could not tell that an agent had the work, so "TQ-0329 July financials hasn't moved"
|
|
563
566
|
# sat in 'slipped' while the task said in_progress (the owner, 2026-09-03)
|
|
@@ -1101,9 +1104,40 @@ def act(store, idea_id: int, verb: str, actor: str = 'owner', llm=None, days: in
|
|
|
1101
1104
|
elif verb == 'task':
|
|
1102
1105
|
if not a.get('mid'): raise ValueError('this idea is not about a message, so there is nothing to make a task from')
|
|
1103
1106
|
from . import ingest
|
|
1104
|
-
|
|
1107
|
+
message = store.get_message(a['mid']) or {}
|
|
1108
|
+
prior = store.get_task(message.get('TaskId')) if message.get('TaskId') else None
|
|
1109
|
+
# The task kind is the router. The old path always launched `_auto_code`, even when the
|
|
1110
|
+
# message already belonged to a GENERAL task; that is how TQ-0367 was silently reopened
|
|
1111
|
+
# in a repository after it had been completed. An explicit kind from this assistant's
|
|
1112
|
+
# one triage wins, then the existing task kind, with coding retained only as the legacy
|
|
1113
|
+
# default for older idea rows that carry neither.
|
|
1114
|
+
kind = str(a.get('kind') or (prior or {}).get('Kind') or 'coding').lower()
|
|
1115
|
+
if kind not in ('coding', 'general'): kind = 'coding'
|
|
1116
|
+
if prior and prior.get('Status') in ('done', 'dropped'):
|
|
1117
|
+
# This is NEW work noticed after an earlier task was completed. A message can point
|
|
1118
|
+
# at only one task, so do not steal it from history or rename/reopen the completed
|
|
1119
|
+
# task. Carry the evidence into a fresh assistant-owned task instead.
|
|
1120
|
+
title = str(a.get('title') or i.get('Text') or 'Assistant follow-up').strip()[:200]
|
|
1121
|
+
source_text = str(message.get('BodyText') or '').strip()
|
|
1122
|
+
summary = str(i.get('Text') or '').strip()
|
|
1123
|
+
if source_text and source_text not in summary: summary += f'\n\nSource message: {source_text}'
|
|
1124
|
+
tid = store.create_task({'Title': title, 'Summary': summary[:2000], 'Kind': kind,
|
|
1125
|
+
'Source': 'assistant', 'SourceRef': f'assistant:idea:{idea_id}'}, actor)
|
|
1126
|
+
store.add_comment(tid, 'assistant', 'assistant_agent',
|
|
1127
|
+
f'Created from assistant idea {idea_id}; related completed task {task_ref(prior["TaskId"])}.')
|
|
1128
|
+
store.audit('task', tid, 'create_from_assistant_idea', actor,
|
|
1129
|
+
detail={'idea_id': idea_id, 'message_id': a['mid'], 'related_task_id': prior['TaskId']})
|
|
1130
|
+
else:
|
|
1131
|
+
tid = ingest.task_from_message(store, a['mid'], actor, kind)
|
|
1105
1132
|
if a.get('title'): store.update_task(tid, {'Title': str(a['title'])[:200]}, actor)
|
|
1106
|
-
|
|
1133
|
+
task = store.get_task(tid) or {}
|
|
1134
|
+
brief = str(a.get('title') or i.get('Text') or task.get('Title') or '').strip()
|
|
1135
|
+
why = str(a.get('why') or '').strip()
|
|
1136
|
+
if why: brief += f'\n\nWhy the assistant raised it: {why}'
|
|
1137
|
+
if kind == 'general':
|
|
1138
|
+
ingest._spawn(_auto_general, store, tid, brief)
|
|
1139
|
+
elif store.get_settings().get('coder_auto_enabled') == '1':
|
|
1140
|
+
ingest._spawn(ingest._auto_code, store, tid)
|
|
1107
1141
|
out |= {'taskId': tid, 'ref': task_ref(tid)}
|
|
1108
1142
|
elif verb == 'snooze':
|
|
1109
1143
|
until = (datetime.now() + timedelta(days=max(1, int(days or 1)))).strftime('%Y-%m-%d %H:%M:%S')
|
|
@@ -1119,3 +1153,15 @@ def act(store, idea_id: int, verb: str, actor: str = 'owner', llm=None, days: in
|
|
|
1119
1153
|
else: learn.learn_from(store, ev)
|
|
1120
1154
|
store.audit('idea', idea_id, verb, actor, detail={'kind': i.get('Kind')})
|
|
1121
1155
|
return out
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
def _auto_general(store, tid: int, brief: str) -> None:
|
|
1159
|
+
"""The regular-agent counterpart to ingest._auto_code for an accepted assistant idea."""
|
|
1160
|
+
from . import general
|
|
1161
|
+
try:
|
|
1162
|
+
session = general.start_session(store, tid, actor='router')
|
|
1163
|
+
store.add_comment(tid, 'router', 'agent', 'sent to the regular agent from the assistant')
|
|
1164
|
+
session.send_prompt(brief)
|
|
1165
|
+
except Exception as e:
|
|
1166
|
+
logger.warning(f'regular-agent dispatch failed for task {tid}: {e}')
|
|
1167
|
+
store.add_comment(tid, 'router', 'agent', f'Regular-agent start failed: {str(e)[:200]}')
|
|
@@ -99,6 +99,22 @@ def wall(store, cwd: str = '', limit: int = 12) -> list:
|
|
|
99
99
|
return store.notes(norm(cwd) if cwd else None, limit)
|
|
100
100
|
|
|
101
101
|
|
|
102
|
+
def live_wall(store, cwd: str = '', limit: int = 60) -> list:
|
|
103
|
+
"""Task notes for sessions alive now, plus the shared house lane.
|
|
104
|
+
|
|
105
|
+
Board notes are durable, but the UI calls this surface Live handoff. Filtering only at the
|
|
106
|
+
daily roll-up left notes from closed sessions looking current for hours (or indefinitely).
|
|
107
|
+
"""
|
|
108
|
+
from . import terminal as term
|
|
109
|
+
try:
|
|
110
|
+
live_tids = {int(s['taskId']) for s in term.live_sessions(tail=0, details=False)
|
|
111
|
+
if s.get('taskId')}
|
|
112
|
+
except Exception:
|
|
113
|
+
live_tids = set()
|
|
114
|
+
rows = store.notes(norm(cwd) if cwd else None, max(int(limit) * 5, 300))
|
|
115
|
+
return [n for n in rows if not n.get('TaskId') or int(n['TaskId']) in live_tids][:int(limit)]
|
|
116
|
+
|
|
117
|
+
|
|
102
118
|
def house_wall(store, limit: int = 8) -> list:
|
|
103
119
|
"""The lane with no checkout in it: what the assistant chat and the owner leave for
|
|
104
120
|
everyone. A chat has no repository, so this is the whole of its wall."""
|
|
@@ -466,6 +466,57 @@ def _same_words(a: str, b: str, n: int = 160) -> bool:
|
|
|
466
466
|
return bool(x) and x == y
|
|
467
467
|
|
|
468
468
|
|
|
469
|
+
def retire_draft_answered_elsewhere(store, tid: int | None, sent: dict) -> list:
|
|
470
|
+
"""Resolve reply drafts made obsolete by a reply the owner sent in the native app.
|
|
471
|
+
|
|
472
|
+
A provider sync can land well after triage wrote its draft. The draft belongs to the
|
|
473
|
+
inbound message it was written against, so an owner line later on that same conversation
|
|
474
|
+
is the missing verdict: the reply already went out. Action approvals are deliberately
|
|
475
|
+
excluded -- sending a chat line cannot approve an unrelated proposed action.
|
|
476
|
+
"""
|
|
477
|
+
conv, sent_at = sent.get('ConversationId'), sent.get('SentAt')
|
|
478
|
+
if not conv or not sent_at:
|
|
479
|
+
return []
|
|
480
|
+
stale = [rv for rv in store.list_reviews('pending')
|
|
481
|
+
if rv.get('TaskId') == tid and rv.get('Kind') != 'action'
|
|
482
|
+
and rv.get('ConversationId') == conv
|
|
483
|
+
and (not rv.get('SentAt') or rv['SentAt'] <= sent_at)]
|
|
484
|
+
if not stale:
|
|
485
|
+
return []
|
|
486
|
+
|
|
487
|
+
place = SENT_FROM.get(sent.get('Channel'), 'outside Taskuary')
|
|
488
|
+
for rv in stale:
|
|
489
|
+
store.decide_review(rv['ReviewId'], 'superseded', None, 'you',
|
|
490
|
+
f'The owner replied {place}; this draft was not sent.')
|
|
491
|
+
|
|
492
|
+
# Treat this exactly like a successful send through Review. Reply-only work closes, while
|
|
493
|
+
# owner-controlled work and a task whose coding agent is still running keep their safeguards.
|
|
494
|
+
if tid:
|
|
495
|
+
from . import verdicts
|
|
496
|
+
verdicts._settle_task_after_sent_reply(store, stale[0], 'you', True)
|
|
497
|
+
|
|
498
|
+
task = store.get_task(tid) if tid else {}
|
|
499
|
+
body = re.sub(r'\s+', ' ', sent.get('BodyText') or '').strip()
|
|
500
|
+
excerpt = f': "{body[:180]}"' if body else ''
|
|
501
|
+
if not tid:
|
|
502
|
+
subject = (store.get_message(stale[0].get('MessageId')) or {}).get('Subject') or 'this thread'
|
|
503
|
+
where = subject
|
|
504
|
+
state = ''
|
|
505
|
+
else:
|
|
506
|
+
where = f'TQ-{tid:04d}'
|
|
507
|
+
state = ' The task is still open because it has other work in progress.' \
|
|
508
|
+
if task.get('Status') not in ('done', 'dropped') else ' I marked the task done.'
|
|
509
|
+
from . import concierge, funnel, general
|
|
510
|
+
concierge.record(store, general.dock_task(store)[0]['TaskId'], 'assistant',
|
|
511
|
+
f'You replied {place} on {where}{excerpt}. '
|
|
512
|
+
f'I removed the unused draft; the reply was taken care of.{state}')
|
|
513
|
+
funnel.invalidate()
|
|
514
|
+
# The review transition already wakes the UI, but this final wake happens after the durable
|
|
515
|
+
# Assistant line was written, avoiding a race where an open chat fetched one write too early.
|
|
516
|
+
store._poke('feed-changed', 'task-changed', task_id=tid)
|
|
517
|
+
return stale
|
|
518
|
+
|
|
519
|
+
|
|
469
520
|
def ingest_own_message(store, msg: dict, why: str, keep_unmatched: bool = True) -> int:
|
|
470
521
|
"""Anything YOU sent - a mail reply, a line in a chat - never gets its own timeline row and
|
|
471
522
|
never becomes work: it rides INSIDE the thread as a 'context' message, which is what the
|
|
@@ -486,6 +537,8 @@ def ingest_own_message(store, msg: dict, why: str, keep_unmatched: bool = True)
|
|
|
486
537
|
'SentAt': msg.get('sent_at'), 'BodyText': msg.get('body'),
|
|
487
538
|
'SourceLink': msg.get('source_link'), 'Status': 'context',
|
|
488
539
|
'MailMetaJson': json.dumps(msg.get('mail_meta')) if msg.get('mail_meta') else None})
|
|
540
|
+
sent = store.get_message(mid) or {}
|
|
541
|
+
retire_draft_answered_elsewhere(store, tid, sent)
|
|
489
542
|
if not tid: return 1
|
|
490
543
|
store.add_route(mid, tid, 'attach', None, why, [], 'router')
|
|
491
544
|
# ...unless Taskuary is reading back its OWN send. That reply already has its line on the
|
|
@@ -745,7 +798,7 @@ def ingest_github_issues(store, src: dict, tok: str, since, llm=None, file_only=
|
|
|
745
798
|
issues_mode, prs_mode = gh_modes(src, file_only)
|
|
746
799
|
if issues_mode == 'off' and prs_mode == 'off': return 0
|
|
747
800
|
n = 0
|
|
748
|
-
from .github import list_items
|
|
801
|
+
from .github import body_images, list_items
|
|
749
802
|
for i in reversed(list_items(tok, repo, since=since.astimezone().isoformat())):
|
|
750
803
|
if TQ_ISSUE.match(i.get('title') or ''): continue
|
|
751
804
|
is_pr = 'pull_request' in i
|
|
@@ -761,6 +814,10 @@ def ingest_github_issues(store, src: dict, tok: str, since, llm=None, file_only=
|
|
|
761
814
|
'from_name': who, 'from_email': f'{who}@users.noreply.github.com',
|
|
762
815
|
'conversation_id': f"gh:{repo}#{i['number']}", 'sent_at': _local(i.get('updated_at') or ''),
|
|
763
816
|
'source_link': i.get('html_url'), 'source_name': repo,
|
|
817
|
+
# the screenshot IS the report: read it before the row exists, or the classifier
|
|
818
|
+
# judges an issue template whose headings are empty (see images_for_triage above)
|
|
819
|
+
'images': (body_images(tok, i.get('body') or '')
|
|
820
|
+
if str(store.get_settings().get('vision_enabled') or '1') == '1' else []),
|
|
764
821
|
'no_auto': not gh_auto_ok(src, i.get('author_association'))},
|
|
765
822
|
llm=llm, file_only=mode == 'feed')
|
|
766
823
|
n += out['status'] != 'duplicate'
|
|
@@ -897,7 +954,7 @@ def _poll_jobs(store, only=None):
|
|
|
897
954
|
# tool-only card, a Sync that pulled nothing, and no error anywhere).
|
|
898
955
|
phone_guide = (c['Type'] == 'whatsapp'
|
|
899
956
|
and store.get_settings().get('phone_assistant') == '1'
|
|
900
|
-
and '
|
|
957
|
+
and bool(_cfg(c).get('assistant_chat') or _cfg(c).get('notify_chat')))
|
|
901
958
|
if (not roles & {'trigger', 'feed'} and not phone_guide
|
|
902
959
|
and not (c['Type'] == 'github' and _gh_explicit(store))
|
|
903
960
|
and not (c['Type'] in CLOUD and _cloud_explicit(store, CH2SRC[c['Type']]))): continue
|
|
@@ -224,8 +224,8 @@ def wrap(store, tid: int, close: bool = True, actor: str = 'owner', sid: str = N
|
|
|
224
224
|
report = resolution_text(rep)
|
|
225
225
|
store.add_comment(tid, actor, 'human', 'Closed the session - wrapped up from what was on screen.')
|
|
226
226
|
store.add_comment(tid, agent, 'agent', f'CODER REPORT\n{report}')
|
|
227
|
-
# The compact result
|
|
228
|
-
#
|
|
227
|
+
# The compact result and final answer become the readable Markdown artifact. terminal.py has
|
|
228
|
+
# already filed the raw PTY stream for recovery; duplicating it here made the reader unusable.
|
|
229
229
|
artifact = None
|
|
230
230
|
try:
|
|
231
231
|
from . import session_artifacts
|
|
@@ -283,7 +283,15 @@ def raise_reply(store, task_id: int, mid: int, run_id: int, rep: dict,
|
|
|
283
283
|
'Status': 'pending', 'Reason': 'coder finished the work - reply awaiting approval'})
|
|
284
284
|
# The report is intentionally compact UI copy. Draft from the complete final response (or
|
|
285
285
|
# transcript fallback), so every question the session answered remains available here.
|
|
286
|
-
try:
|
|
286
|
+
try:
|
|
287
|
+
try: deliver = json.loads((held or {}).get('Deliver') or '{}') or {}
|
|
288
|
+
except (TypeError, ValueError): deliver = {}
|
|
289
|
+
if deliver.get('channel') and deliver.get('kind') != 'zoho_invoice':
|
|
290
|
+
from . import outbox
|
|
291
|
+
outbox.redraft_review(store, store.get_review(rid),
|
|
292
|
+
complete_result or resolution_text(rep))
|
|
293
|
+
else:
|
|
294
|
+
responder.write_draft(store, task_id, rid, complete_result or resolution_text(rep), 'coder')
|
|
287
295
|
except Exception as e: logger.warning(f'reply draft failed for task {task_id}: {e}')
|
|
288
296
|
# the ping that matters most: work FINISHED and its reply is sitting in Review on you
|
|
289
297
|
if (store.get_settings().get('notify_level') or 'needs_me') != 'off':
|