boss-agent-cli 2.0.0__tar.gz → 2.0.1__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 (388) hide show
  1. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CHANGELOG.md +5 -0
  2. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/PKG-INFO +1 -1
  3. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/capability-matrix.en.md +2 -2
  4. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/capability-matrix.md +2 -2
  5. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/commands.en.md +4 -4
  6. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/commands.md +4 -4
  7. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/pyproject.toml +1 -1
  8. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/__init__.py +1 -1
  9. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/ai_cmd.py +1 -1
  10. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat.py +6 -2
  11. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat_export.py +20 -17
  12. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat_snapshot.py +16 -15
  13. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat_summary.py +10 -5
  14. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat_utils.py +16 -0
  15. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chatmsg.py +17 -6
  16. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/contact_lookup.py +53 -10
  17. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/exchange.py +10 -5
  18. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/mark.py +3 -2
  19. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/pipeline.py +1 -1
  20. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/schema.py +4 -4
  21. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/mcp_tools.py +5 -5
  22. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/actions.py +52 -19
  23. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/catalog.py +1 -1
  24. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/prompts.py +5 -1
  25. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chat_export_extended.py +39 -3
  26. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chat_snapshot_extended.py +98 -0
  27. boss_agent_cli-2.0.1/tests/test_chatmsg_uid_flow.py +227 -0
  28. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_commands.py +4 -3
  29. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_contact_lookup.py +91 -1
  30. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_new_commands.py +2 -2
  31. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_wizard.py +63 -1
  32. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/uv.lock +1 -1
  33. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.codecov.yml +0 -0
  34. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.devcontainer/devcontainer.json +0 -0
  35. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.dockerignore +0 -0
  36. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.gitattributes +0 -0
  37. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/CODEOWNERS +0 -0
  38. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  39. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  40. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/ISSUE_TEMPLATE/documentation.yml +0 -0
  41. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  42. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  43. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/dependabot.yml +0 -0
  44. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/social-preview.png +0 -0
  45. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/ci.yml +0 -0
  46. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/docs.yml +0 -0
  47. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/pages.yml +0 -0
  48. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/release.yml +0 -0
  49. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/triage-cleanup.yml +0 -0
  50. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/update-contributors.yml +0 -0
  51. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.gitignore +0 -0
  52. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.pre-commit-config.yaml +0 -0
  53. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CODE_OF_CONDUCT.md +0 -0
  54. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CONTRIBUTING.en.md +0 -0
  55. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CONTRIBUTING.md +0 -0
  56. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CONTRIBUTORS.svg +0 -0
  57. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/Dockerfile +0 -0
  58. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/LICENSE +0 -0
  59. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/README.en.md +0 -0
  60. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/README.md +0 -0
  61. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/ROADMAP.en.md +0 -0
  62. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/ROADMAP.md +0 -0
  63. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/SECURITY.md +0 -0
  64. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/demo-en.tape +0 -0
  65. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/demo-zh.tape +0 -0
  66. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/demo_showcase.py +0 -0
  67. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/demo_showcase_en.py +0 -0
  68. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/index.html +0 -0
  69. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/llms.txt +0 -0
  70. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/README.md +0 -0
  71. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/animations.jsx +0 -0
  72. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/app.jsx +0 -0
  73. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/boss-agent-cli-promo.html +0 -0
  74. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/lib.jsx +0 -0
  75. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/scenes.jsx +0 -0
  76. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/showcase/boss-agent-cli-showcase-thumb.jpg +0 -0
  77. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docker-compose.yml +0 -0
  78. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/adr/0001-default-low-risk-assistance-mode.md +0 -0
  79. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/adr/0002-explicit-research-operating-mode.md +0 -0
  80. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/agent-hosts.en.md +0 -0
  81. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/agent-hosts.md +0 -0
  82. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/agent-quickstart.en.md +0 -0
  83. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/agent-quickstart.md +0 -0
  84. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/assets/logo.png +0 -0
  85. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/assets/star-history-dark.svg +0 -0
  86. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/assets/star-history.svg +0 -0
  87. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/getting-started.en.md +0 -0
  88. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/getting-started.md +0 -0
  89. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/ai-models.en.md +0 -0
  90. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/ai-models.md +0 -0
  91. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/claude-code.md +0 -0
  92. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/codex.md +0 -0
  93. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/cursor.md +0 -0
  94. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/docker.md +0 -0
  95. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/python-sdk.md +0 -0
  96. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/shell-agent.md +0 -0
  97. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/windsurf.md +0 -0
  98. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/maintainer/branch-protection.md +0 -0
  99. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/maintainer/labels.md +0 -0
  100. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/maintainer/release-checklist.md +0 -0
  101. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/marketing/awesome-submissions.md +0 -0
  102. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/marketing/en-launch-story.md +0 -0
  103. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/marketing/promotion-article.md +0 -0
  104. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/marketing/zh-launch-story.md +0 -0
  105. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/platform-abstraction.en.md +0 -0
  106. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/platform-abstraction.md +0 -0
  107. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/platform-risk.en.md +0 -0
  108. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/platform-risk.md +0 -0
  109. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/readme-badge-policy.md +0 -0
  110. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/bridge-grpc.md +0 -0
  111. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/README.md +0 -0
  112. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/lagou.md +0 -0
  113. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/liepin.md +0 -0
  114. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/zhaopin-recruiter-evaluation.md +0 -0
  115. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/zhaopin.md +0 -0
  116. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/zhipin.md +0 -0
  117. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/smoke-testing.md +0 -0
  118. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/troubleshooting.en.md +0 -0
  119. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/troubleshooting.md +0 -0
  120. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/README.md +0 -0
  121. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/results/20260624T103356Z-fixture.json +0 -0
  122. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/results/README.md +0 -0
  123. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/results/necessary-conditions-2026-06-24.json +0 -0
  124. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/run_eval.py +0 -0
  125. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/scenarios.json +0 -0
  126. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/examples/opencode/opencode.json +0 -0
  127. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/extension/background.js +0 -0
  128. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/extension/manifest.json +0 -0
  129. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/glama.json +0 -0
  130. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/mcp-server/README.en.md +0 -0
  131. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/mcp-server/README.md +0 -0
  132. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/mcp-server/server.py +0 -0
  133. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/README.md +0 -0
  134. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/package_portable.py +0 -0
  135. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/probe_recruiter_chat_frontend.py +0 -0
  136. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/quality_baseline.py +0 -0
  137. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/smoke_p0.py +0 -0
  138. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/update_contributors.py +0 -0
  139. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/__init__.py +0 -0
  140. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/config.py +0 -0
  141. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/local_models.py +0 -0
  142. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/prompts.py +0 -0
  143. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/service.py +0 -0
  144. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/__init__.py +0 -0
  145. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/_base_client.py +0 -0
  146. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/boss.yaml +0 -0
  147. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/browser_client.py +0 -0
  148. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/browser_source.py +0 -0
  149. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/client.py +0 -0
  150. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/endpoints.py +0 -0
  151. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/endpoints_loader.py +0 -0
  152. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/httpx_helpers.py +0 -0
  153. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/models.py +0 -0
  154. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/recruiter.yaml +0 -0
  155. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/recruiter_client.py +0 -0
  156. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/recruiter_endpoints.py +0 -0
  157. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/recruiter_resume.py +0 -0
  158. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/throttle.py +0 -0
  159. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/zhilian.yaml +0 -0
  160. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/zhilian_client.py +0 -0
  161. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/zhipin_errors.py +0 -0
  162. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/__init__.py +0 -0
  163. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/browser.py +0 -0
  164. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/cookie_extract.py +0 -0
  165. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/curl_import.py +0 -0
  166. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/health.py +0 -0
  167. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/manager.py +0 -0
  168. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/qr_login.py +0 -0
  169. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/token_store.py +0 -0
  170. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/__init__.py +0 -0
  171. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/adapters.py +0 -0
  172. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/boss_adapter.py +0 -0
  173. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/config.py +0 -0
  174. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/decision.py +0 -0
  175. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/events.py +0 -0
  176. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/execution.py +0 -0
  177. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/mock_adapter.py +0 -0
  178. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/models.py +0 -0
  179. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/reply_ai.py +0 -0
  180. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/runner.py +0 -0
  181. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/safety.py +0 -0
  182. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/scoring.py +0 -0
  183. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/storage.py +0 -0
  184. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_adapter.py +0 -0
  185. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_browser.py +0 -0
  186. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_browser_actions.py +0 -0
  187. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_cdp.py +0 -0
  188. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_selectors.py +0 -0
  189. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/bridge/__init__.py +0 -0
  190. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/bridge/client.py +0 -0
  191. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/bridge/daemon.py +0 -0
  192. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/bridge/protocol.py +0 -0
  193. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/cache/__init__.py +0 -0
  194. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/cache/store.py +0 -0
  195. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/chat_summary.py +0 -0
  196. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/__init__.py +0 -0
  197. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/_platform.py +0 -0
  198. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/_recruiter_platform.py +0 -0
  199. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/agent.py +0 -0
  200. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/ai_local.py +0 -0
  201. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/apply.py +0 -0
  202. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/cities.py +0 -0
  203. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/clean.py +0 -0
  204. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/config_cmd.py +0 -0
  205. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/crawl.py +0 -0
  206. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/detail.py +0 -0
  207. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/digest.py +0 -0
  208. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/doctor.py +0 -0
  209. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/doctor_checks.py +0 -0
  210. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/export.py +0 -0
  211. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/favorites.py +0 -0
  212. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/friend_list_pages.py +0 -0
  213. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/greet.py +0 -0
  214. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/history.py +0 -0
  215. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/interviews.py +0 -0
  216. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/login.py +0 -0
  217. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/logout.py +0 -0
  218. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/me.py +0 -0
  219. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/platforms.py +0 -0
  220. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/preset.py +0 -0
  221. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recommend.py +0 -0
  222. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/__init__.py +0 -0
  223. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/accept_resume.py +0 -0
  224. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/applications.py +0 -0
  225. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/candidates.py +0 -0
  226. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/chat.py +0 -0
  227. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/download_resume.py +0 -0
  228. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/jobs.py +0 -0
  229. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/recommendations.py +0 -0
  230. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/reply.py +0 -0
  231. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/request_resume.py +0 -0
  232. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/resume.py +0 -0
  233. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/resume_parser.py +0 -0
  234. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/register.py +0 -0
  235. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/resume_cmd.py +0 -0
  236. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/search.py +0 -0
  237. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/shortlist.py +0 -0
  238. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/show.py +0 -0
  239. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/stats.py +0 -0
  240. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/status.py +0 -0
  241. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/watch.py +0 -0
  242. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/wizard.py +0 -0
  243. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/compliance.py +0 -0
  244. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/config.py +0 -0
  245. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/__init__.py +0 -0
  246. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/exporter.py +0 -0
  247. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/hooks.py +0 -0
  248. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/operations.py +0 -0
  249. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/service.py +0 -0
  250. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/transport.py +0 -0
  251. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/digest.py +0 -0
  252. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/display.py +0 -0
  253. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/hooks.py +0 -0
  254. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/index_cache.py +0 -0
  255. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/main.py +0 -0
  256. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/match_score.py +0 -0
  257. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/mcp_args.py +0 -0
  258. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/mcp_server.py +0 -0
  259. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/output.py +0 -0
  260. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/pipeline_state.py +0 -0
  261. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/__init__.py +0 -0
  262. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/base.py +0 -0
  263. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/recruiter_base.py +0 -0
  264. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/zhilian.py +0 -0
  265. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/zhipin.py +0 -0
  266. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/zhipin_recruiter.py +0 -0
  267. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/py.typed +0 -0
  268. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/__init__.py +0 -0
  269. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/export.py +0 -0
  270. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/models.py +0 -0
  271. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/store.py +0 -0
  272. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/templates.py +0 -0
  273. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/search_filters.py +0 -0
  274. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/__init__.py +0 -0
  275. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/live_crawl.py +0 -0
  276. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/models.py +0 -0
  277. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/preflight.py +0 -0
  278. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/renderer.py +0 -0
  279. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/runner.py +0 -0
  280. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/store.py +0 -0
  281. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/conftest.py +0 -0
  282. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/fixtures/favorites_card.json +0 -0
  283. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_automation.py +0 -0
  284. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_automation_cross_validation.py +0 -0
  285. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_crawl.py +0 -0
  286. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_docs.py +0 -0
  287. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_error_contract.py +0 -0
  288. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_host_examples.py +0 -0
  289. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_commands.py +0 -0
  290. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_config.py +0 -0
  291. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_config_cmd.py +0 -0
  292. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_cover_letter.py +0 -0
  293. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_fit.py +0 -0
  294. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_local.py +0 -0
  295. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_prompts.py +0 -0
  296. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_resume_optimize.py +0 -0
  297. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_service.py +0 -0
  298. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_suggest_keywords.py +0 -0
  299. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_api.py +0 -0
  300. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_api_client_methods.py +0 -0
  301. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_api_client_retry.py +0 -0
  302. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_api_httpx_helpers.py +0 -0
  303. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_apply.py +0 -0
  304. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth.py +0 -0
  305. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth_and_login_extended.py +0 -0
  306. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth_browser.py +0 -0
  307. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth_manager_extended.py +0 -0
  308. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth_manager_flow.py +0 -0
  309. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_bridge.py +0 -0
  310. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_bridge_extended.py +0 -0
  311. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_browser_client.py +0 -0
  312. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_browser_source.py +0 -0
  313. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_cache.py +0 -0
  314. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_cache_extended.py +0 -0
  315. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chat_summary.py +0 -0
  316. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chat_summary_command.py +0 -0
  317. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chatmsg_extended.py +0 -0
  318. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_clean_cmd.py +0 -0
  319. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_cli_invariants.py +0 -0
  320. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_compliance.py +0 -0
  321. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_config_cmd.py +0 -0
  322. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_cookie_extract_regression.py +0 -0
  323. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_coverage_second_sprint.py +0 -0
  324. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_crawl.py +0 -0
  325. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_crawl_cli.py +0 -0
  326. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_curl_auth_import.py +0 -0
  327. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_digest.py +0 -0
  328. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_digest_command.py +0 -0
  329. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_display.py +0 -0
  330. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_doctor_extended.py +0 -0
  331. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_doctor_zhilian.py +0 -0
  332. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_endpoints_loader.py +0 -0
  333. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_eval_harness.py +0 -0
  334. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_export_extended.py +0 -0
  335. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_favorites.py +0 -0
  336. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_greet_detail_extended.py +0 -0
  337. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_greet_extended.py +0 -0
  338. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_hooks.py +0 -0
  339. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_index_cache.py +0 -0
  340. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_job_card_browser.py +0 -0
  341. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_job_item_lid.py +0 -0
  342. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_match_score.py +0 -0
  343. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_mcp_handler_registration.py +0 -0
  344. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_mcp_server.py +0 -0
  345. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_mcp_server_protocol.py +0 -0
  346. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_open_source_docs.py +0 -0
  347. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_output.py +0 -0
  348. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_pipeline_commands.py +0 -0
  349. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_pipeline_state.py +0 -0
  350. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_platform_base.py +0 -0
  351. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_platform_cli.py +0 -0
  352. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_platform_not_supported.py +0 -0
  353. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_platforms_cmd.py +0 -0
  354. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_portable_package.py +0 -0
  355. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_preset.py +0 -0
  356. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_probe_script.py +0 -0
  357. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_public_api.py +0 -0
  358. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_qr_login.py +0 -0
  359. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_quality_baseline.py +0 -0
  360. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_recruiter_client_retry.py +0 -0
  361. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_recruiter_recommendations_and_read.py +0 -0
  362. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_recruiter_resume_attachments.py +0 -0
  363. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_recruiter_safety_regressions.py +0 -0
  364. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_commands.py +0 -0
  365. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_export.py +0 -0
  366. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_import_compat.py +0 -0
  367. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_init_hints.py +0 -0
  368. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_models.py +0 -0
  369. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_store.py +0 -0
  370. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_templates.py +0 -0
  371. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_schema_contract.py +0 -0
  372. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_schema_formats.py +0 -0
  373. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_schema_mcp_tools.py +0 -0
  374. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_search_filters.py +0 -0
  375. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_search_pipeline.py +0 -0
  376. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_shortlist.py +0 -0
  377. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_smoke_p0.py +0 -0
  378. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_stats.py +0 -0
  379. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_system_proxy.py +0 -0
  380. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_throttle.py +0 -0
  381. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_watch.py +0 -0
  382. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_watch_run_all.py +0 -0
  383. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_welfare_filter.py +0 -0
  384. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhilian_browser_automation.py +0 -0
  385. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhilian_client.py +0 -0
  386. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhilian_interview_data.py +0 -0
  387. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhilian_stub.py +0 -0
  388. {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhipin_errors.py +0 -0
@@ -4,6 +4,11 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.0.1] - 2026-09-29
8
+
9
+ ### Fixed
10
+ - `boss chat` 现在公开跨请求稳定的联系人 `uid`,`chatmsg` / `chat-summary` / `mark` / `exchange` 优先按 `uid` 重新解析联系人并使用本次沟通列表返回的动态 `securityId`;沟通快照、diff 与导出映射同步改用 `uid` 主键,避免轮换令牌导致联系人查找必然失败和快照重复。既有 `security_id` 参数名与无 `uid` 的旧快照仍兼容,但旧令牌仅作兜底且可能已经失效。
11
+
7
12
  ## [2.0.0] - 2026-09-15
8
13
 
9
14
  ### Changed(含 Breaking Change)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: boss-agent-cli
3
- Version: 2.0.0
3
+ Version: 2.0.1
4
4
  Summary: AI Agent 专用的 BOSS 直聘求职 CLI 工具
5
5
  Project-URL: Homepage, https://github.com/can4hou6joeng4/boss-agent-cli
6
6
  Project-URL: Repository, https://github.com/can4hou6joeng4/boss-agent-cli
@@ -43,8 +43,8 @@ Compatibility modes `assisted` and `research` can both call every implemented ca
43
43
  | Capability | CLI command | Login required | Transport |
44
44
  |---|---|---|---|
45
45
  | Conversation list | `boss chat` | Yes | Existing-browser read session when Bridge is connected; otherwise httpx with stored credentials |
46
- | Message history | `boss chatmsg [--raw]` | Yes | Existing-browser read session when Bridge is connected; otherwise httpx with stored credentials; `--raw` preserves structured body/link/job-card fields |
47
- | Conversation summary | `boss chat-summary` | Yes | Platform adapter + local processing |
46
+ | Message history | `boss chatmsg [--raw]` | Yes | Prefer the stable `uid` returned by `boss chat`; use the existing-browser read session when Bridge is connected, otherwise httpx with stored credentials; `--raw` preserves structured body/link/job-card fields |
47
+ | Conversation summary | `boss chat-summary` | Yes | Prefer the stable `uid`; platform adapter + local processing |
48
48
  | Contact labels | `boss mark` | Yes | Platform adapter |
49
49
  | Contact exchange | `boss exchange` | Yes | Platform adapter |
50
50
  | Interview invites | `boss interviews` | Yes | httpx |
@@ -43,8 +43,8 @@
43
43
  | 能力 | CLI 命令 | 需要登录 | 通道 |
44
44
  |---|---|---|---|
45
45
  | 沟通列表 | `boss chat` | 是 | Bridge 已连接时复用现有浏览器只读会话;否则使用本地凭据的 httpx |
46
- | 聊天消息 | `boss chatmsg [--raw]` | 是 | Bridge 已连接时复用现有浏览器只读会话;否则使用本地凭据的 httpx;`--raw` 保留结构化 body/链接/职位卡片字段 |
47
- | 聊天摘要 | `boss chat-summary` | 是 | 平台适配器 + 本地处理 |
46
+ | 聊天消息 | `boss chatmsg [--raw]` | 是 | 优先接受 `boss chat` 输出的稳定 `uid`;Bridge 已连接时复用现有浏览器只读会话,否则使用本地凭据的 httpx;`--raw` 保留结构化 body/链接/职位卡片字段 |
47
+ | 聊天摘要 | `boss chat-summary` | 是 | 优先接受稳定 `uid`;平台适配器 + 本地处理 |
48
48
  | 联系人标签 | `boss mark` | 是 | 平台适配器 |
49
49
  | 交换联系方式 | `boss exchange` | 是 | 平台适配器 |
50
50
  | 面试邀请 | `boss interviews` | 是 | httpx |
@@ -105,16 +105,16 @@ After every page, `<data-dir>/crawl/runs/<run_id>/jobs.json`, `jobs.csv`, and a
105
105
  | `boss greet <sid> <jid>` | Greet a recruiter; duplicates return `ALREADY_GREETED` |
106
106
  | `boss batch-greet <query>` | Search and greet up to an explicit `--limit`; supports `--dry-run`; on `ACCOUNT_RISK` / `ENVIRONMENT_RISK` the batch stops immediately with `ok:false`, and already-greeted items are returned in `error.details.greeted` |
107
107
  | `boss apply <sid> <jid>` | Apply or start a conversation; duplicates return `ALREADY_APPLIED` |
108
- | `boss exchange <sid>` | Request a phone-number or WeChat exchange |
108
+ | `boss exchange <uid-or-sid>` | Request a phone-number or WeChat exchange; prefer the stable `uid` returned by `boss chat` |
109
109
 
110
110
  ## Conversation track
111
111
 
112
112
  | Command | Description |
113
113
  |---------|-------------|
114
114
  | `boss chat` | List conversations with pagination and source filters |
115
- | `boss chatmsg <sid> [--raw]` | Read message history; `--raw` preserves structured body/link/card fields |
116
- | `boss chat-summary <sid>` | Build a structured conversation summary |
117
- | `boss mark <sid> --label X` | Add or remove a contact label |
115
+ | `boss chatmsg <uid-or-sid> [--raw]` | Read message history; prefer the stable `uid` returned by `boss chat`; `--raw` preserves structured body/link/card fields |
116
+ | `boss chat-summary <uid-or-sid>` | Build a structured conversation summary; prefer the stable `uid` |
117
+ | `boss mark <uid-or-sid> --label X` | Add or remove a contact label; prefer the stable `uid` |
118
118
  | `boss interviews` | Interview invitations |
119
119
  | `boss history` | Browsing history |
120
120
 
@@ -101,16 +101,16 @@ boss crawl stop <run_id>
101
101
  | `boss greet <sid> <jid>` | 向指定招聘者打招呼;重复记录返回 `ALREADY_GREETED` |
102
102
  | `boss batch-greet <query>` | 搜索后按显式 `--limit` 批量打招呼,支持 `--dry-run`;命中 `ACCOUNT_RISK` / `ENVIRONMENT_RISK` 立即停批并返回 `ok:false`,已成功项随 `error.details.greeted` 带回 |
103
103
  | `boss apply <sid> <jid>` | 发起投递或立即沟通;重复记录返回 `ALREADY_APPLIED` |
104
- | `boss exchange <sid>` | 请求交换手机号或微信 |
104
+ | `boss exchange <uid-or-sid>` | 请求交换手机号或微信;优先使用 `boss chat` 输出的稳定 `uid` |
105
105
 
106
106
  ## 沟通跟进
107
107
 
108
108
  | 命令 | 说明 |
109
109
  |------|------|
110
110
  | `boss chat` | 查看沟通列表,支持分页和来源筛选 |
111
- | `boss chatmsg <sid> [--raw]` | 查看聊天历史;`--raw` 保留结构化 body、链接和职位卡片字段 |
112
- | `boss chat-summary <sid>` | 基于聊天历史生成结构化摘要 |
113
- | `boss mark <sid> --label X` | 添加或移除联系人标签 |
111
+ | `boss chatmsg <uid-or-sid> [--raw]` | 查看聊天历史;优先使用 `boss chat` 输出的稳定 `uid`,`--raw` 保留结构化 body、链接和职位卡片字段 |
112
+ | `boss chat-summary <uid-or-sid>` | 基于聊天历史生成结构化摘要;优先使用稳定 `uid` |
113
+ | `boss mark <uid-or-sid> --label X` | 添加或移除联系人标签;优先使用稳定 `uid` |
114
114
  | `boss interviews` | 面试邀请 |
115
115
  | `boss history` | 浏览历史 |
116
116
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "boss-agent-cli"
7
- version = "2.0.0"
7
+ version = "2.0.1"
8
8
  description = "AI Agent 专用的 BOSS 直聘求职 CLI 工具"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
28
28
  )
29
29
  from boss_agent_cli.resume.models import ResumeData, ResumeFile
30
30
 
31
- __version__ = "2.0.0"
31
+ __version__ = "2.0.1"
32
32
 
33
33
  _LAZY_EXPORT_MODULES = {
34
34
  "AuthManager": "boss_agent_cli.auth.manager",
@@ -501,7 +501,7 @@ def ai_reply_cmd(ctx: click.Context, recruiter_message: str, context: str, resum
501
501
  render=lambda d: render_ai_result(d, title="ai-reply"),
502
502
  hints={"next_actions": [
503
503
  "复制草稿到 BOSS 聊天框发送",
504
- "boss chatmsg <security_id> 查看完整聊天历史",
504
+ "boss chatmsg <uid> 查看完整聊天历史(uid 见 boss chat 输出)",
505
505
  ]},
506
506
  )
507
507
 
@@ -99,6 +99,9 @@ def chat_cmd(ctx: click.Context, page: int, from_who: str | None, days: int | No
99
99
  "last_time": last_time_str,
100
100
  "last_ts": last_ts,
101
101
  "msg_status": msg_status_label,
102
+ # uid 是跨请求稳定的联系人标识,chatmsg/chat-summary/mark/exchange
103
+ # 都应优先使用它;security_id 是每请求轮换的令牌,仅作参考。
104
+ "uid": item.get("uid") or "",
102
105
  "security_id": item.get("securityId") or "",
103
106
  "encrypt_job_id": item.get("encryptJobId") or "",
104
107
  "unread": item.get("unreadMsgCount") or 0,
@@ -156,7 +159,7 @@ def chat_cmd(ctx: click.Context, page: int, from_who: str | None, days: int | No
156
159
  ),
157
160
  hints={"next_actions": [
158
161
  "boss detail <security_id> — 查看职位详情",
159
- "boss chatmsg <security_id> — 查看消息后继续沟通",
162
+ "boss chatmsg <uid> — 查看消息后继续沟通(uid 见上方输出)",
160
163
  ]},
161
164
  )
162
165
  return
@@ -179,6 +182,7 @@ def chat_cmd(ctx: click.Context, page: int, from_who: str | None, days: int | No
179
182
  ("Boss", "name", "bold cyan"),
180
183
  ("职称", "title", "dim"),
181
184
  ("公司", "brand_name", "green"),
185
+ ("uid", "uid", "dim"),
182
186
  ("发起方", "initiated_by", "magenta"),
183
187
  ("未读", "unread", "red"),
184
188
  ("已读", "msg_status", "dim"),
@@ -192,7 +196,7 @@ def chat_cmd(ctx: click.Context, page: int, from_who: str | None, days: int | No
192
196
  render=_render,
193
197
  hints={"next_actions": [
194
198
  "boss detail <security_id> — 查看职位详情",
195
- "boss chatmsg <security_id> — 查看消息后继续沟通",
199
+ "boss chatmsg <uid> — 查看消息后继续沟通(uid 见上方输出)",
196
200
  ]},
197
201
  )
198
202
 
@@ -7,7 +7,7 @@ import io
7
7
  import json
8
8
  from typing import Any
9
9
 
10
- from boss_agent_cli.commands.chat_utils import sanitize_csv_cell, escape_md_cell, GROUP_ORDER
10
+ from boss_agent_cli.commands.chat_utils import sanitize_csv_cell, escape_md_cell, contact_identity, GROUP_ORDER
11
11
 
12
12
 
13
13
  def prepare_render_data(
@@ -32,7 +32,7 @@ def prepare_render_data(
32
32
  me_read = sum(1 for x in me_items if x.get("msg_status") == "已读")
33
33
  me_unread = len(me_items) - me_read
34
34
 
35
- added_ids = {item.get("security_id") for item in diff_result.get("added", [])}
35
+ added_ids = {contact_identity(item) for item in diff_result.get("added", [])}
36
36
 
37
37
  # diff 摘要
38
38
  diff_summary = None
@@ -82,12 +82,13 @@ def prepare_render_data(
82
82
  for item in group_items:
83
83
  global_idx += 1
84
84
  sid = item.get("security_id", "")
85
- is_new = sid in added_ids
85
+ key = contact_identity(item)
86
+ is_new = key in added_ids
86
87
  ref = f"S{global_idx}"
87
88
  msg = str(item.get("last_msg") or "-")
88
89
  unread = item.get("unread") or 0
89
90
  rows.append({
90
- "ref": ref, "is_new": is_new, "sid": sid,
91
+ "ref": ref, "is_new": is_new, "sid": sid, "key": key,
91
92
  "brand_name": item.get("brand_name") or "-",
92
93
  "name": item.get("name") or "-",
93
94
  "title": item.get("title") or "-",
@@ -96,7 +97,7 @@ def prepare_render_data(
96
97
  "msg_status": item.get("msg_status") or "-",
97
98
  "last_msg": msg,
98
99
  })
99
- id_map.append((ref, sid, f"{item.get('brand_name') or '-'} {item.get('name') or '-'}"))
100
+ id_map.append((ref, key, f"{item.get('brand_name') or '-'} {item.get('name') or '-'}"))
100
101
 
101
102
  sections.append({"subtitle": subtitle, "rows": rows})
102
103
 
@@ -135,7 +136,7 @@ def _render_csv(friends: list[dict[str, Any]]) -> str:
135
136
  fields = [
136
137
  "name", "title", "brand_name", "initiated_by",
137
138
  "msg_status", "unread", "last_msg", "last_time",
138
- "security_id", "encrypt_job_id",
139
+ "uid", "security_id", "encrypt_job_id",
139
140
  ]
140
141
  buf = io.StringIO(newline="")
141
142
  writer = csv.DictWriter(buf, fieldnames=fields, extrasaction="ignore")
@@ -205,15 +206,16 @@ def _render_markdown(
205
206
 
206
207
  if rd["id_map"]:
207
208
  lines.append("<details>")
208
- lines.append("<summary>security_id 映射表(点击展开)</summary>")
209
+ lines.append("<summary>联系人标识映射表(点击展开)</summary>")
209
210
  lines.append("")
210
- lines.append("| 编号 | 公司/联系人 | security_id |")
211
- lines.append("|------|------------|-------------|")
212
- for ref, sid, label in rd["id_map"]:
213
- lines.append(f"| {ref} | {label} | {sid} |")
211
+ lines.append("| 编号 | 公司/联系人 | 联系人标识(uid 优先) |")
212
+ lines.append("|------|------------|--------------------------|")
213
+ for ref, key, label in rd["id_map"]:
214
+ lines.append(f"| {ref} | {label} | {key} |")
214
215
  lines.append("")
215
216
  lines.append("</details>")
216
217
  lines.append("")
218
+ lines.append("> 优先使用 uid 调用 `boss chatmsg <uid>`;缺少 uid 的旧记录会回退展示可能已失效的 security_id。")
217
219
 
218
220
  return "\n".join(lines) + "\n"
219
221
 
@@ -224,7 +226,7 @@ def _render_html(
224
226
  days: int | None,
225
227
  diff_result: dict[str, Any],
226
228
  ) -> str:
227
- """渲染为 HTML 格式,含分组、diff 标记和 security_id 映射。"""
229
+ """渲染为 HTML 格式,含分组、diff 标记和联系人标识映射。"""
228
230
  esc = _html.escape
229
231
  rd = prepare_render_data(friends, from_who, diff_result)
230
232
 
@@ -284,17 +286,18 @@ def _render_html(
284
286
  <tbody>{''.join(rrows)}</tbody>
285
287
  </table>"""
286
288
 
287
- # security_id 映射表
289
+ # 联系人标识映射表
288
290
  map_rows = []
289
- for ref, sid, label in rd["id_map"]:
290
- map_rows.append(f"<tr><td>{ref}</td><td>{esc(label)}</td><td class='sid'>{esc(sid)}</td></tr>")
291
+ for ref, key, label in rd["id_map"]:
292
+ map_rows.append(f"<tr><td>{ref}</td><td>{esc(label)}</td><td class='sid'>{esc(key)}</td></tr>")
291
293
  map_html = f"""
292
294
  <details>
293
- <summary>security_id 映射表(点击展开)</summary>
295
+ <summary>联系人标识映射表(点击展开)</summary>
294
296
  <table>
295
- <thead><tr><th>编号</th><th>公司/联系人</th><th>security_id</th></tr></thead>
297
+ <thead><tr><th>编号</th><th>公司/联系人</th><th>联系人标识(uid 优先)</th></tr></thead>
296
298
  <tbody>{''.join(map_rows)}</tbody>
297
299
  </table>
300
+ <p>优先使用 uid;缺少 uid 的旧记录会回退展示可能已失效的 security_id。</p>
298
301
  </details>""" if map_rows else ""
299
302
 
300
303
  return f"""<!DOCTYPE html>
@@ -5,13 +5,14 @@ import json
5
5
  import os
6
6
  from typing import Any
7
7
 
8
+ from boss_agent_cli.commands.chat_utils import contact_identity
8
9
  from boss_agent_cli.output import Logger
9
10
 
10
11
 
11
12
  def save_snapshot_and_diff(
12
13
  snapshot_dir: str, friends: list[dict[str, Any]], logger: Logger
13
14
  ) -> dict[str, Any]:
14
- """保存当日 JSON 快照(按 security_id 合并)并与上次对比。"""
15
+ """保存当日 JSON 快照(uid 优先,旧数据回退 security_id)并与上次对比。"""
15
16
  os.makedirs(snapshot_dir, exist_ok=True)
16
17
  today = datetime.date.today().isoformat()
17
18
  snapshot_path = os.path.join(snapshot_dir, f"{today}.json")
@@ -24,16 +25,16 @@ def save_snapshot_and_diff(
24
25
  prev_today = json.load(f)
25
26
  if isinstance(prev_today, list):
26
27
  for item in prev_today:
27
- if isinstance(item, dict) and item.get("security_id"):
28
- existing[item["security_id"]] = item
28
+ if isinstance(item, dict) and contact_identity(item):
29
+ existing[contact_identity(item)] = item
29
30
  except (json.JSONDecodeError, OSError):
30
31
  pass
31
32
 
32
33
  # 用当前数据更新(新数据优先)
33
34
  for item in friends:
34
- sid = item.get("security_id")
35
- if sid:
36
- existing[sid] = item
35
+ key = contact_identity(item)
36
+ if key:
37
+ existing[key] = item
37
38
 
38
39
  merged = list(existing.values())
39
40
 
@@ -50,22 +51,22 @@ def save_snapshot_and_diff(
50
51
  if prev_friends is None:
51
52
  return {"is_first": True, "added": [], "removed": [], "new_unread": []}
52
53
 
53
- # 用 security_id 做 key 对比
54
- curr_map = {item["security_id"]: item for item in merged if item.get("security_id")}
55
- prev_map = {item["security_id"]: item for item in prev_friends if item.get("security_id")}
54
+ # 用稳定标识(uid 优先)做 key 对比
55
+ curr_map = {contact_identity(item): item for item in merged if contact_identity(item)}
56
+ prev_map = {contact_identity(item): item for item in prev_friends if contact_identity(item)}
56
57
  curr_ids = set(curr_map)
57
58
  prev_ids = set(prev_map)
58
59
 
59
- added = [curr_map[sid] for sid in (curr_ids - prev_ids)]
60
- removed = [prev_map[sid] for sid in (prev_ids - curr_ids)]
60
+ added = [curr_map[key] for key in (curr_ids - prev_ids)]
61
+ removed = [prev_map[key] for key in (prev_ids - curr_ids)]
61
62
 
62
63
  # 新增未读:检测任何未读增量
63
64
  new_unread = []
64
- for sid in (curr_ids & prev_ids):
65
- prev_unread = prev_map[sid].get("unread", 0) or 0
66
- curr_unread = curr_map[sid].get("unread", 0) or 0
65
+ for key in (curr_ids & prev_ids):
66
+ prev_unread = prev_map[key].get("unread", 0) or 0
67
+ curr_unread = curr_map[key].get("unread", 0) or 0
67
68
  if curr_unread > prev_unread:
68
- new_unread.append(curr_map[sid])
69
+ new_unread.append(curr_map[key])
69
70
 
70
71
  return {
71
72
  "is_first": False,
@@ -4,12 +4,12 @@ from boss_agent_cli.auth.manager import AuthManager
4
4
  from boss_agent_cli.chat_summary import summarize_messages
5
5
  from boss_agent_cli.compliance import require_compliance_allowed
6
6
  from boss_agent_cli.commands._platform import get_platform_instance
7
- from boss_agent_cli.commands.contact_lookup import resolve_friend_or_emit
7
+ from boss_agent_cli.commands.contact_lookup import current_friend_security_id_or_emit, resolve_friend_or_emit
8
8
  from boss_agent_cli.display import boss_command_for_ctx, handle_auth_errors, handle_not_supported, handle_output, handle_platform_error_output, render_message_panel
9
9
 
10
10
 
11
11
  @click.command("chat-summary")
12
- @click.argument("security_id")
12
+ @click.argument("security_id", metavar="UID_OR_SECURITY_ID")
13
13
  @click.option("--page", default=1, help="页码")
14
14
  @click.option("--count", default=20, help="每页消息数量")
15
15
  @click.pass_context
@@ -28,9 +28,13 @@ def chat_summary_cmd(ctx: click.Context, security_id: str, page: int, count: int
28
28
  return
29
29
  gid = str(friend_item.get("uid", ""))
30
30
  friend_name = friend_item.get("name") or "-"
31
+ # securityId 是每请求轮换的令牌,必须用本次 friend_list 返回的新值。
32
+ fresh_security_id = current_friend_security_id_or_emit(ctx, "chat-summary", friend_item)
33
+ if fresh_security_id is None:
34
+ return
31
35
 
32
36
  try:
33
- resp = platform.chat_history(gid, security_id, page=page, count=count)
37
+ resp = platform.chat_history(gid, fresh_security_id, page=page, count=count)
34
38
  except NotImplementedError as exc:
35
39
  handle_not_supported(ctx, "chat-summary", exc, fallback_message="当前平台不支持聊天记录能力")
36
40
  return
@@ -45,7 +49,8 @@ def chat_summary_cmd(ctx: click.Context, security_id: str, page: int, count: int
45
49
  ctx,
46
50
  "chat-summary",
47
51
  {
48
- "security_id": security_id,
52
+ "uid": gid,
53
+ "security_id": fresh_security_id,
49
54
  "name": friend_name,
50
55
  **summary,
51
56
  },
@@ -53,7 +58,7 @@ def chat_summary_cmd(ctx: click.Context, security_id: str, page: int, count: int
53
58
  hints={
54
59
  "next_actions": [
55
60
  boss_command_for_ctx(ctx, "chat"),
56
- boss_command_for_ctx(ctx, f"chatmsg {security_id}"),
61
+ boss_command_for_ctx(ctx, f"chatmsg {gid}"),
57
62
  ]
58
63
  },
59
64
  )
@@ -1,5 +1,7 @@
1
1
  """Chat 共享常量和安全工具函数。"""
2
2
 
3
+ from typing import Any
4
+
3
5
  # relationType 映射:API 返回值 → 可读标签(key 类型放宽为 object,兼容 API 返回 None/未知值走 default)
4
6
  RELATION_LABELS: dict[object, str] = {1: "对方主动", 2: "我主动", 3: "投递"}
5
7
  FROM_FILTER = {"boss": 1, "me": 2}
@@ -9,6 +11,20 @@ MSG_STATUS_LABELS = {1: "未读", 2: "已读"}
9
11
  GROUP_ORDER = ["对方主动", "我主动", "投递"]
10
12
 
11
13
 
14
+ def contact_identity(item: dict[str, Any]) -> str:
15
+ """返回联系人的稳定标识:优先 ``uid``,缺失时回退 ``security_id``。
16
+
17
+ ``security_id`` 是 BOSS 每次请求轮换的加密令牌,同一个联系人在两次请求中
18
+ 会拿到不同的值。因此凡是需要「跨请求比较同一个联系人」的场景
19
+ (快照合并、diff、映射表)都必须以 ``uid`` 为主键,
20
+ 仅对缺少 uid 的历史快照数据回退到 ``security_id``。
21
+ """
22
+ uid = item.get("uid")
23
+ if uid not in (None, ""):
24
+ return str(uid)
25
+ return str(item.get("security_id") or "")
26
+
27
+
12
28
  def sanitize_csv_cell(value: str) -> str:
13
29
  """防止 CSV 公式注入:以 =+@- 开头的值前置单引号;过滤 Tab 和回车。"""
14
30
  if not isinstance(value, str):
@@ -6,7 +6,7 @@ import click
6
6
 
7
7
  from boss_agent_cli.auth.manager import AuthManager
8
8
  from boss_agent_cli.compliance import require_compliance_allowed
9
- from boss_agent_cli.commands.contact_lookup import resolve_friend_or_emit
9
+ from boss_agent_cli.commands.contact_lookup import current_friend_security_id_or_emit, resolve_friend_or_emit
10
10
  from boss_agent_cli.commands._platform import get_platform_instance
11
11
  from boss_agent_cli.display import boss_command_for_ctx, handle_auth_errors, handle_not_supported, handle_output, handle_platform_error_output, render_simple_list
12
12
 
@@ -17,14 +17,17 @@ _MSG_TYPE_MAP = {
17
17
 
18
18
 
19
19
  @click.command("chatmsg")
20
- @click.argument("security_id")
20
+ @click.argument("security_id", metavar="UID_OR_SECURITY_ID")
21
21
  @click.option("--page", default=1, help="页码")
22
22
  @click.option("--count", default=20, help="每页消息数量")
23
23
  @click.option("--raw", "show_raw", is_flag=True, default=False, help="输出保真结构化消息字段")
24
24
  @click.pass_context
25
25
  @handle_auth_errors("chatmsg")
26
26
  def chatmsg_cmd(ctx: click.Context, security_id: str, page: int, count: int, show_raw: bool) -> None:
27
- """查看与指定好友的聊天消息历史"""
27
+ """查看与指定好友的聊天消息历史
28
+
29
+ SECURITY_ID 可传 boss chat 输出的 uid(推荐,跨请求稳定)或 security_id。
30
+ """
28
31
  if not require_compliance_allowed(ctx, "chatmsg"):
29
32
  ctx.exit(1)
30
33
 
@@ -38,15 +41,23 @@ def chatmsg_cmd(ctx: click.Context, security_id: str, page: int, count: int, sho
38
41
  "chatmsg",
39
42
  platform,
40
43
  security_id,
41
- not_found_message=f"未在沟通列表中找到 security_id={security_id},请确认该联系人存在",
44
+ not_found_message=(
45
+ f"未在沟通列表中找到联系人 {security_id},请确认该联系人存在"
46
+ "(可用 boss chat 输出的 uid 或 security_id)"
47
+ ),
42
48
  )
43
49
  if friend_item is None:
44
50
  return
45
51
  gid = str(friend_item.get("uid", ""))
46
52
  friend_name = friend_item.get("name") or "-"
53
+ # securityId 是每请求轮换的令牌,必须用本次 friend_list 返回的新值,
54
+ # 否则会拿一个已失效的令牌去请求消息历史。
55
+ fresh_security_id = current_friend_security_id_or_emit(ctx, "chatmsg", friend_item)
56
+ if fresh_security_id is None:
57
+ return
47
58
 
48
59
  try:
49
- resp = platform.chat_history(gid, security_id, page=page, count=count)
60
+ resp = platform.chat_history(gid, fresh_security_id, page=page, count=count)
50
61
  except NotImplementedError as exc:
51
62
  handle_not_supported(ctx, "chatmsg", exc, fallback_message="当前平台不支持聊天记录能力")
52
63
  return
@@ -77,7 +88,7 @@ def chatmsg_cmd(ctx: click.Context, security_id: str, page: int, count: int, sho
77
88
  render=_render,
78
89
  hints={"next_actions": [
79
90
  f"{boss_command_for_ctx(ctx, 'chat')} — 返回沟通列表",
80
- f"{boss_command_for_ctx(ctx, f'detail {security_id}')} — 查看职位详情",
91
+ f"{boss_command_for_ctx(ctx, f'detail {fresh_security_id}')} — 查看职位详情",
81
92
  ]},
82
93
  )
83
94
 
@@ -5,20 +5,24 @@ class FriendLookupLimitExceeded(RuntimeError):
5
5
  """Raised when paginated friend lookup cannot prove completion safely."""
6
6
 
7
7
 
8
- def find_friend_by_security_id(
8
+ def find_friend(
9
9
  platform: Any,
10
- security_id: str,
10
+ identifier: str | int,
11
11
  *,
12
12
  start_page: int = 1,
13
13
  max_pages: int = 50,
14
14
  ) -> tuple[dict[str, Any] | None, dict[str, Any] | None]:
15
- """分页遍历沟通列表,按 security_id 查找联系人。
15
+ """分页遍历沟通列表,按 ``uid`` 或 ``security_id`` 查找联系人。
16
16
 
17
17
  返回 (friend_item, error_response):
18
18
  - 找到联系人:返回 (item, None)
19
19
  - 平台返回失败响应:返回 (None, raw_response)
20
20
  - 遍历完成仍未找到:返回 (None, None)
21
21
  """
22
+ needle = str(identifier).strip()
23
+ if not needle:
24
+ return None, None
25
+
22
26
  page = start_page
23
27
  terminated = False
24
28
  seen_signatures: set[tuple[str, ...]] = set()
@@ -29,11 +33,18 @@ def find_friend_by_security_id(
29
33
 
30
34
  platform_data = platform.unwrap_data(resp) or {}
31
35
  items = platform_data.get("result") or platform_data.get("friendList") or []
32
- for item in items:
33
- if item.get("securityId") == security_id:
36
+ dict_items = [item for item in items if isinstance(item, dict)]
37
+ # 同一页先完整检查稳定 uid,再回退到轮换的 securityId。这样即使某个
38
+ # securityId 恰好与另一条记录的 uid 文本相同,也不会命中错误联系人。
39
+ for item in dict_items:
40
+ if str(item.get("uid") or "").strip() == needle:
41
+ return item, None
42
+ for item in dict_items:
43
+ if str(item.get("securityId") or "") == needle:
34
44
  return item, None
35
45
 
36
- signature = tuple(str(item.get("securityId", "")) for item in items if isinstance(item, dict))
46
+ # securityId 会在请求间轮换,不能用于重复页判定;有 uid 时必须优先用 uid。
47
+ signature = tuple(str(item.get("uid") or item.get("securityId") or "") for item in dict_items)
37
48
  if signature in seen_signatures:
38
49
  terminated = True
39
50
  break
@@ -50,25 +61,57 @@ def find_friend_by_security_id(
50
61
  return None, None
51
62
 
52
63
 
64
+ # 向后兼容:旧名称保留为别名(匹配逻辑已放宽为 uid 或 security_id)
65
+ find_friend_by_security_id = find_friend
66
+
67
+
68
+ def current_friend_security_id_or_emit(
69
+ ctx: Any,
70
+ command: str,
71
+ friend_item: dict[str, Any],
72
+ ) -> str | None:
73
+ """读取本次 friend_list 返回的 securityId;缺失时安全停止。
74
+
75
+ 调用方传入的标识可能是 uid,绝不能在 securityId 缺失时把 uid 猜作
76
+ securityId,尤其不能用于联系方式交换等写请求。
77
+ """
78
+ from boss_agent_cli.display import handle_error_output
79
+
80
+ security_id = str(friend_item.get("securityId") or friend_item.get("security_id") or "").strip()
81
+ if security_id:
82
+ return security_id
83
+ handle_error_output(
84
+ ctx,
85
+ command,
86
+ code="NETWORK_ERROR",
87
+ message="沟通列表返回的联系人缺少当前 securityId,已停止执行;请刷新沟通列表后重试",
88
+ )
89
+ return None
90
+
91
+
53
92
  def resolve_friend_or_emit(
54
93
  ctx: Any,
55
94
  command: str,
56
95
  platform: Any,
57
- security_id: str,
96
+ identifier: str,
58
97
  *,
59
98
  not_found_message: str | None = None,
60
99
  ) -> dict[str, Any] | None:
61
- """按 security_id 定位联系人;失败时输出统一错误信封并返回 None。
100
+ """按 uid(或 security_id)定位联系人;失败时输出统一错误信封并返回 None。
62
101
 
63
102
  封装 mark/exchange/chatmsg/chat-summary 共用的「解析 + 错误处理」样板:
64
103
  NotImplementedError→NOT_SUPPORTED、FriendLookupLimitExceeded→NETWORK_ERROR、
65
104
  平台失败→按错误码、未找到→JOB_NOT_FOUND。成功返回 friend_item;否则输出错误信封
66
105
  并返回 None(调用方应立即 return)。
106
+
107
+ 注意:调用方**不要**把 identifier 直接当作 securityId 传给平台 API——
108
+ securityId 是每请求轮换的令牌,必须使用本次 friend_list 返回的
109
+ ``friend_item["securityId"]``。
67
110
  """
68
111
  from boss_agent_cli.display import handle_error_output, handle_not_supported, handle_platform_error_output
69
112
 
70
113
  try:
71
- friend_item, friends_error = find_friend_by_security_id(platform, security_id)
114
+ friend_item, friends_error = find_friend(platform, identifier)
72
115
  except NotImplementedError as exc:
73
116
  handle_not_supported(ctx, command, exc, fallback_message="当前平台不支持沟通列表能力")
74
117
  return None
@@ -90,7 +133,7 @@ def resolve_friend_or_emit(
90
133
  ctx,
91
134
  command,
92
135
  code="JOB_NOT_FOUND",
93
- message=not_found_message or f"未在沟通列表中找到 security_id={security_id}",
136
+ message=not_found_message or f"未在沟通列表中找到联系人 {identifier}",
94
137
  )
95
138
  return None
96
139
  return friend_item
@@ -3,12 +3,12 @@ import click
3
3
  from boss_agent_cli.auth.manager import AuthManager
4
4
  from boss_agent_cli.compliance import require_compliance_allowed
5
5
  from boss_agent_cli.commands._platform import get_platform_instance
6
- from boss_agent_cli.commands.contact_lookup import resolve_friend_or_emit
6
+ from boss_agent_cli.commands.contact_lookup import current_friend_security_id_or_emit, resolve_friend_or_emit
7
7
  from boss_agent_cli.display import boss_command_for_ctx, handle_auth_errors, handle_not_supported, handle_output, handle_platform_error_output, render_message_panel
8
8
 
9
9
 
10
10
  @click.command("exchange")
11
- @click.argument("security_id")
11
+ @click.argument("security_id", metavar="UID_OR_SECURITY_ID")
12
12
  @click.option("--type", "exchange_type", default="phone", type=click.Choice(["phone", "wechat"]), help="交换类型:phone=手机号 / wechat=微信")
13
13
  @click.pass_context
14
14
  @handle_auth_errors("exchange")
@@ -30,9 +30,13 @@ def exchange_cmd(ctx: click.Context, security_id: str, exchange_type: str) -> No
30
30
  return
31
31
  uid = str(friend_item.get("uid", ""))
32
32
  friend_name: str = friend_item.get("name") or "-"
33
+ # securityId 是每请求轮换的令牌,必须用本次 friend_list 返回的新值。
34
+ fresh_security_id = current_friend_security_id_or_emit(ctx, "exchange", friend_item)
35
+ if fresh_security_id is None:
36
+ return
33
37
 
34
38
  try:
35
- resp = platform.exchange_contact(security_id, uid, friend_name, exchange_type=type_id)
39
+ resp = platform.exchange_contact(fresh_security_id, uid, friend_name, exchange_type=type_id)
36
40
  except NotImplementedError as exc:
37
41
  handle_not_supported(ctx, "exchange", exc, fallback_message=f"当前平台不支持{type_label}交换能力")
38
42
  return
@@ -41,7 +45,8 @@ def exchange_cmd(ctx: click.Context, security_id: str, exchange_type: str) -> No
41
45
  return
42
46
 
43
47
  data = {
44
- "security_id": security_id,
48
+ "uid": uid,
49
+ "security_id": fresh_security_id,
45
50
  "name": friend_name,
46
51
  "type": type_label,
47
52
  "message": f"已向 {friend_name} 发送{type_label}交换请求",
@@ -51,6 +56,6 @@ def exchange_cmd(ctx: click.Context, security_id: str, exchange_type: str) -> No
51
56
  render=lambda d: render_message_panel(d, title="exchange"),
52
57
  hints={"next_actions": [
53
58
  f"{boss_command_for_ctx(ctx, 'chat')} — 返回沟通列表",
54
- f"{boss_command_for_ctx(ctx, f'chatmsg {security_id}')} — 查看聊天记录",
59
+ f"{boss_command_for_ctx(ctx, f'chatmsg {uid}')} — 查看聊天记录",
55
60
  ]},
56
61
  )