netizen-cli 0.13.3__tar.gz → 0.14.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 (330) hide show
  1. {netizen_cli-0.13.3/netizen_cli.egg-info → netizen_cli-0.14.1}/PKG-INFO +1 -1
  2. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/README.md +3 -3
  3. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/__init__.py +1 -1
  4. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/presentation.py +11 -0
  5. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/admin.css +8 -2
  6. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/admin.js +184 -49
  7. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/index.html +77 -13
  8. netizen_cli-0.14.1/netizen_cli/admin/static/progress-card.js +118 -0
  9. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/worktrees.js +12 -12
  10. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/web.py +81 -17
  11. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/bindings.py +34 -15
  12. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/controls.py +69 -21
  13. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/defaults.py +3 -3
  14. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/reply.py +113 -47
  15. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/scheduled.py +21 -21
  16. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/workspace.py +17 -17
  17. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel_app.py +13 -9
  18. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/codex_runtime.py +97 -90
  19. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/database_migrations.py +4 -0
  20. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/defaults/service.py +4 -4
  21. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/defaults/store.py +1 -1
  22. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/domain.py +7 -0
  23. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/main.py +6 -0
  24. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/management/chat_labels.py +152 -69
  25. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/management/service.py +61 -28
  26. netizen_cli-0.14.1/netizen_cli/migrations/v18.py +132 -0
  27. netizen_cli-0.14.1/netizen_cli/migrations/v19.py +94 -0
  28. netizen_cli-0.14.1/netizen_cli/progress_card_config.py +45 -0
  29. netizen_cli-0.14.1/netizen_cli/progress_card_settings.py +42 -0
  30. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/runtime/contracts.py +4 -0
  31. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/schedules/service.py +7 -7
  32. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/schedules/store.py +6 -6
  33. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/sdk_gap_adapter.py +14 -1
  34. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/session_settings.py +17 -3
  35. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/sessions/policy.py +12 -14
  36. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/settings.py +8 -0
  37. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/turn_activity.py +49 -27
  38. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/turn_plan_observer.py +4 -0
  39. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/worktrees/policy.py +3 -3
  40. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/worktrees/service.py +15 -15
  41. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/worktrees/settings.py +16 -16
  42. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/worktrees/store.py +15 -15
  43. {netizen_cli-0.13.3 → netizen_cli-0.14.1/netizen_cli.egg-info}/PKG-INFO +1 -1
  44. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli.egg-info/SOURCES.txt +11 -0
  45. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/pyproject.toml +1 -1
  46. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/admin.md +42 -21
  47. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/concepts.md +1 -1
  48. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/feedback-and-files.md +49 -3
  49. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/fork-side-goal.md +1 -1
  50. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/native-capabilities.md +1 -1
  51. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/scheduled-tasks.md +28 -26
  52. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/session-collaboration.md +4 -4
  53. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/sessions.md +37 -31
  54. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/user-guide.md +4 -4
  55. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_binding_scheduled_channel.py +6 -1
  56. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cards.py +8 -5
  57. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_channel_app.py +7 -3
  58. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cli_data.py +6 -6
  59. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_codex_runtime.py +427 -7
  60. netizen_cli-0.14.1/tests/test_configuration_publication.py +132 -0
  61. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cron_chat_targets.py +2 -2
  62. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_database_migrations.py +4 -4
  63. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_defaults_cards.py +11 -8
  64. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_defaults_channel.py +4 -4
  65. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_migration_schema_validation.py +1 -1
  66. netizen_cli-0.14.1/tests/test_optional_progress_card_settings_migration.py +140 -0
  67. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_persistent_fork_channel.py +4 -1
  68. netizen_cli-0.14.1/tests/test_progress_card_config.py +107 -0
  69. netizen_cli-0.14.1/tests/test_progress_card_entrypoints.py +213 -0
  70. netizen_cli-0.14.1/tests/test_progress_card_presentation.py +273 -0
  71. netizen_cli-0.14.1/tests/test_progress_card_settings_migration.py +177 -0
  72. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_cards.py +8 -8
  73. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_service.py +4 -1
  74. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_workspace_cards.py +2 -2
  75. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_workspace_migration.py +15 -4
  76. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_scheduled_runtime.py +12 -3
  77. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedules_storage.py +4 -1
  78. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_sdk_gap_adapter.py +25 -0
  79. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_session_collaboration_app.py +7 -2
  80. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_session_defaults_migration.py +15 -3
  81. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_session_external_runtime.py +13 -0
  82. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_session_settings.py +55 -0
  83. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_turn_activity.py +57 -0
  84. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_turn_plan_observer.py +23 -0
  85. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_worktree_new.py +3 -3
  86. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_worktree_service.py +25 -0
  87. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_worktree_storage.py +16 -1
  88. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_worktree_tools_integration.py +6 -4
  89. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/MANIFEST.in +0 -0
  90. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/_build.py +0 -0
  91. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/config.example.yaml +0 -0
  92. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/__main__.py +0 -0
  93. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/account_rate_limits.py +0 -0
  94. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/__init__.py +0 -0
  95. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/auth.py +0 -0
  96. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/credentials.py +0 -0
  97. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/errors.py +0 -0
  98. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/feishu.py +0 -0
  99. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/login.py +0 -0
  100. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/port_config.py +0 -0
  101. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/queries.py +0 -0
  102. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/chat-picker.css +0 -0
  103. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/chat-picker.js +0 -0
  104. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/identity.js +0 -0
  105. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/login.html +0 -0
  106. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/login.js +0 -0
  107. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/static/session-access.js +0 -0
  108. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/transport.py +0 -0
  109. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/admin/users.py +0 -0
  110. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/builtin_skills.py +0 -0
  111. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/__init__.py +0 -0
  112. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/callbacks.py +0 -0
  113. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/chat_target.py +0 -0
  114. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/fork.py +0 -0
  115. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/model_info.py +0 -0
  116. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/pagination.py +0 -0
  117. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/questions.py +0 -0
  118. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/session_ids.py +0 -0
  119. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/status.py +0 -0
  120. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cards/usage.py +0 -0
  121. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/__init__.py +0 -0
  122. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/completion_mentions.py +0 -0
  123. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/input_preparation.py +0 -0
  124. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/messages.py +0 -0
  125. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/ports.py +0 -0
  126. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/question_inputs.py +0 -0
  127. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/reactions.py +0 -0
  128. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/reply_presenter.py +0 -0
  129. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/session_pages.py +0 -0
  130. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/channel/topics.py +0 -0
  131. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/chat_avatars.py +0 -0
  132. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/chat_targets.py +0 -0
  133. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cli.py +0 -0
  134. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cli_data.py +0 -0
  135. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cli_packages.py +0 -0
  136. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cli_services.py +0 -0
  137. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cli_setup.py +0 -0
  138. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cli_update.py +0 -0
  139. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cli_update_restore.py +0 -0
  140. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/cli_update_worker.py +0 -0
  141. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/completion_mention.py +0 -0
  142. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/defaults/__init__.py +0 -0
  143. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/defaults/models.py +0 -0
  144. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/deployment/__init__.py +0 -0
  145. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/deployment/restart_worker.py +0 -0
  146. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/deployment/update_executor.py +0 -0
  147. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/deployment/update_protocol.py +0 -0
  148. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/error_messages.py +0 -0
  149. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/experience.py +0 -0
  150. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/feishu_app_onboarding.py +0 -0
  151. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/feishu_app_permissions.py +0 -0
  152. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/feishu_links.py +0 -0
  153. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/git_status.py +0 -0
  154. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/image_inputs.py +0 -0
  155. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/instance.py +0 -0
  156. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/lark_app.py +0 -0
  157. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/management/__init__.py +0 -0
  158. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/management/blocking_io.py +0 -0
  159. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/management/chat_directory.py +0 -0
  160. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/management/coordination.py +0 -0
  161. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/management/updates.py +0 -0
  162. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/markdown_images.py +0 -0
  163. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/mcp_transport.py +0 -0
  164. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/message_content.py +0 -0
  165. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/message_history.py +0 -0
  166. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/message_preparation.py +0 -0
  167. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/message_projection.py +0 -0
  168. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/migrations/__init__.py +0 -0
  169. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/migrations/schema.py +0 -0
  170. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/migrations/v14.py +0 -0
  171. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/migrations/v15.py +0 -0
  172. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/migrations/v16.py +0 -0
  173. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/migrations/v17.py +0 -0
  174. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/model_settings.py +0 -0
  175. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/package_resources.py +0 -0
  176. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/pagination.py +0 -0
  177. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/partial_answers.py +0 -0
  178. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/projects.py +0 -0
  179. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/prompt_projection.py +0 -0
  180. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/quoted_context.py +0 -0
  181. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/result_images.py +0 -0
  182. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/runtime/__init__.py +0 -0
  183. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/runtime/name_writes.py +0 -0
  184. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/runtime/thread_naming.py +0 -0
  185. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/schedules/__init__.py +0 -0
  186. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/schedules/mcp.py +0 -0
  187. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/schedules/models.py +0 -0
  188. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/schedules/scheduler.py +0 -0
  189. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/service_launcher.py +0 -0
  190. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/session_inventory.py +0 -0
  191. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/sessions/__init__.py +0 -0
  192. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/sessions/delegation.py +0 -0
  193. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/sessions/errors.py +0 -0
  194. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/sessions/mcp.py +0 -0
  195. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/sessions/presentation.py +0 -0
  196. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/sessions/schema.py +0 -0
  197. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/sessions/service.py +0 -0
  198. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/skill_references.py +0 -0
  199. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/task_text.py +0 -0
  200. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/terminal_cleanup.py +0 -0
  201. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/thread_history.py +0 -0
  202. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/turn_files.py +0 -0
  203. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/turn_patch_children.py +0 -0
  204. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/user_questions.py +0 -0
  205. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/worktrees/errors.py +0 -0
  206. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/worktrees/git.py +0 -0
  207. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli/worktrees/models.py +0 -0
  208. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli.egg-info/dependency_links.txt +0 -0
  209. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli.egg-info/entry_points.txt +0 -0
  210. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli.egg-info/requires.txt +0 -0
  211. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/netizen_cli.egg-info/top_level.txt +0 -0
  212. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/setup.cfg +0 -0
  213. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-lark/SKILL.md +0 -0
  214. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/SKILL.md +0 -0
  215. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/commands.md +0 -0
  216. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/faq.md +0 -0
  217. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/messages.md +0 -0
  218. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/skills/netizen-user-guide/references/status-and-control.md +0 -0
  219. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_account_rate_limits.py +0 -0
  220. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_admin_update_executor_probe.py +0 -0
  221. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_bindings.py +0 -0
  222. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_channel_fixtures.py +0 -0
  223. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_channel_input_preparation.py +0 -0
  224. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_chat_avatars.py +0 -0
  225. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_chat_target_cards.py +0 -0
  226. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_check_sdk.py +0 -0
  227. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cli.py +0 -0
  228. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cli_distribution.py +0 -0
  229. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cli_integration.py +0 -0
  230. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cli_packages.py +0 -0
  231. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cli_packaging.py +0 -0
  232. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cli_services.py +0 -0
  233. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cli_setup.py +0 -0
  234. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_cli_update.py +0 -0
  235. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_codex_sdk_capabilities.py +0 -0
  236. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_codex_sdk_upgrade_0161.py +0 -0
  237. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_codex_sdk_upgrade_0163.py +0 -0
  238. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_completion_mention_cards.py +0 -0
  239. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_completion_mentions.py +0 -0
  240. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_completion_topic_delivery.py +0 -0
  241. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_defaults_runtime.py +0 -0
  242. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_defaults_worktree_channel.py +0 -0
  243. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_deployment.py +0 -0
  244. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_documentation_links.py +0 -0
  245. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_domain_experience.py +0 -0
  246. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_error_messages.py +0 -0
  247. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_feishu_app_onboarding.py +0 -0
  248. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_feishu_app_permissions.py +0 -0
  249. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_file_pagination.py +0 -0
  250. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_fork_cards.py +0 -0
  251. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_git_status.py +0 -0
  252. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_goal_reply_separation.py +0 -0
  253. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_image_inputs.py +0 -0
  254. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_lark_app.py +0 -0
  255. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_lark_sdk_contract.py +0 -0
  256. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_main.py +0 -0
  257. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_markdown_images.py +0 -0
  258. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_message_content.py +0 -0
  259. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_message_history.py +0 -0
  260. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_message_material_inputs.py +0 -0
  261. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_message_preparation.py +0 -0
  262. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_model_info_cards.py +0 -0
  263. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_model_settings.py +0 -0
  264. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_name_writes.py +0 -0
  265. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_pagination.py +0 -0
  266. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_partial_answer_delivery.py +0 -0
  267. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_partial_answers.py +0 -0
  268. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_persistent_fork_bindings.py +0 -0
  269. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_persistent_fork_runtime.py +0 -0
  270. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_child_files.py +0 -0
  271. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_feishu_completion_mention.py +0 -0
  272. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_feishu_partial_answers.py +0 -0
  273. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_feishu_turn_file_card.py +0 -0
  274. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_persistent_fork.py +0 -0
  275. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_python_sdk.py +0 -0
  276. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_scheduled_tasks.py +0 -0
  277. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_sdk_completion_race.py +0 -0
  278. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_sdk_response_recovery.py +0 -0
  279. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_skill_execution.py +0 -0
  280. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_probe_thread_naming.py +0 -0
  281. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_project_delete_channel.py +0 -0
  282. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_project_delete_runtime.py +0 -0
  283. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_project_deletion_storage.py +0 -0
  284. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_prompt_projection.py +0 -0
  285. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_question_cards.py +0 -0
  286. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_question_channel.py +0 -0
  287. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_question_target_parity.py +0 -0
  288. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_quoted_context.py +0 -0
  289. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_release_script.py +0 -0
  290. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_repository_hygiene.py +0 -0
  291. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_result_images.py +0 -0
  292. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_resume_channel.py +0 -0
  293. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_retired_deployment.py +0 -0
  294. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_runtime_resume_activation.py +0 -0
  295. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_runtime_thread_naming.py +0 -0
  296. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_card_retry.py +0 -0
  297. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_mcp.py +0 -0
  298. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_run_now.py +0 -0
  299. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_scope_matrix.py +0 -0
  300. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_workspace_service.py +0 -0
  301. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_schedule_workspace_storage.py +0 -0
  302. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_scheduled_channel.py +0 -0
  303. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_scheduled_worktrees.py +0 -0
  304. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_scheduler.py +0 -0
  305. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_scheduler_presenter.py +0 -0
  306. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_service_launcher.py +0 -0
  307. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_session_card_pages.py +0 -0
  308. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_session_defaults.py +0 -0
  309. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_session_id_cards.py +0 -0
  310. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_session_policy.py +0 -0
  311. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_session_tools.py +0 -0
  312. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_settings_projects.py +0 -0
  313. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_status_cards.py +0 -0
  314. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_status_commands.py +0 -0
  315. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_task_text.py +0 -0
  316. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_terminal_cleanup.py +0 -0
  317. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_thread_history.py +0 -0
  318. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_thread_naming.py +0 -0
  319. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_turn_files.py +0 -0
  320. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_turn_patch_children.py +0 -0
  321. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_turn_patch_statistics.py +0 -0
  322. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_update_executor.py +0 -0
  323. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_usage_card.py +0 -0
  324. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_usage_control.py +0 -0
  325. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_user_guide_skill.py +0 -0
  326. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_user_questions.py +0 -0
  327. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_worktree_channel_cwd.py +0 -0
  328. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_worktree_git.py +0 -0
  329. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_worktree_runtime_cwd.py +0 -0
  330. {netizen_cli-0.13.3 → netizen_cli-0.14.1}/tests/test_worktree_settings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netizen-cli
3
- Version: 0.13.3
3
+ Version: 0.14.1
4
4
  Summary: Use native Codex threads through a Feishu Channel.
5
5
  Requires-Python: <3.15,>=3.11
6
6
  Requires-Dist: h11==0.16.0
@@ -116,10 +116,10 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
116
116
  群聊和群话题中的每条请求都需要重新 **@机器人**,包括命令。`/new` 只通过卡片创建会话,
117
117
  不接受参数。普通任务执行中发来的新消息用于调整当前任务,不会排队成下一个任务。
118
118
 
119
- Git 项目可在 `/new` 中加载项目目录选项,选择已有受管 worktree 或新建 worktree;默认
119
+ Git 项目可在 `/new` 中加载 worktree 选项,选择已有受管 worktree 或新建 worktree;默认
120
120
  仍使用项目目录。新建只检出提交内容,不复制本地改动或执行环境初始化。
121
- 配置、状态查询与删除在 [Admin Worktrees](skills/netizen-user-guide/references/admin.md#worktree-配置与管理)
122
- 中完成;不同会话可以共享同一工作区。`/fork` 不提供新建或选择 worktree 的入口。
121
+ 配置、状态查询与删除在 [Admin worktree](skills/netizen-user-guide/references/admin.md#worktree-配置与管理)
122
+ 中完成;不同会话可以共享同一 worktree。`/fork` 不提供新建或选择 worktree 的入口。
123
123
 
124
124
  安装遇到问题可从[部署文档](docs/deployment.md)继续;已能对话时,常见疑问见
125
125
  [使用 FAQ](skills/netizen-user-guide/references/faq.md#常见问题)。
@@ -1,3 +1,3 @@
1
1
  """Netizen: a thin Feishu Channel for native Codex Threads."""
2
2
 
3
- __version__ = "0.13.3"
3
+ __version__ = "0.14.1"
@@ -23,6 +23,17 @@ def _chat_open_url(chat: ChatLabel) -> str:
23
23
  )
24
24
 
25
25
 
26
+ def _chat_label_json(chat: ChatLabel) -> dict[str, object]:
27
+ return {
28
+ "chatId": chat.chat_id,
29
+ "chatLabel": chat.display_name,
30
+ "chatLabelResolved": chat.resolved,
31
+ "chatMode": chat.chat_mode,
32
+ "chatType": chat.chat_type,
33
+ "chatOpenUrl": _chat_open_url(chat),
34
+ }
35
+
36
+
26
37
  def _schedule_plan_json(plan: dict[str, Any], chat: ChatLabel) -> dict[str, Any]:
27
38
  rule = plan.get("schedule")
28
39
  local_at = None
@@ -380,6 +380,11 @@ body.schedule-detail-open { overflow: hidden; }
380
380
  .defaults-feedback { display: flex; flex-wrap: wrap; gap: 18px; }
381
381
  .defaults-feedback label { display: flex; align-items: center; gap: 7px; }
382
382
  .drawer-form .defaults-feedback input { width: 16px; height: 16px; margin: 0; accent-color: var(--blue); }
383
+ .progress-settings { margin-top: 18px; padding: 16px; border: 1px solid var(--line); border-radius: 8px; }
384
+ .progress-settings > .defaults-feedback { margin-top: 16px; }
385
+ .progress-settings > .schedule-form-grid { margin-top: 10px; }
386
+ .progress-settings > .session-settings-help { margin-bottom: 0; }
387
+ .progress-settings-error { color: var(--danger); font-size: 13px; line-height: 1.6; }
383
388
  #defaults-note.error { color: var(--danger); }
384
389
  #defaults-workspace-help.error, #schedule-editor #schedule-workspace-help.error { color: var(--danger); }
385
390
  #session-settings-editor[hidden], #session-settings-editor [hidden] { display: none; }
@@ -424,8 +429,9 @@ body.schedule-detail-open { overflow: hidden; }
424
429
  .tab-unsaved[hidden] { display: none; }
425
430
  .admin-users { padding: 0; margin: 0; list-style: none; }
426
431
  .admin-users > li { display: flex; justify-content: space-between; align-items: center; gap: 16px; border-top: 1px solid var(--line); padding: 16px 0; }
427
- .session-access-form { border: 0; padding: 0; }
428
- #session-access-fields { min-width: 0; margin: 0; border: 0; padding: 0; }
432
+ .session-access-form, .global-card-form { border: 0; padding: 0; }
433
+ .global-card-form { max-width: 800px; }
434
+ #session-access-fields, #global-card-fields { min-width: 0; margin: 0; border: 0; padding: 0; }
429
435
  .session-access-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); align-items: start; gap: 16px; }
430
436
  .session-access-rule { min-width: 0; border: 1px solid var(--line); border-radius: 10px; padding: 20px; }
431
437
  .session-access-rule h4 { margin: 0; font-size: 16px; }
@@ -55,6 +55,7 @@ async function api(path, options = {}) {
55
55
  function expireAdminSession() {
56
56
  if (state.authExpired) return;
57
57
  state.authExpired = true;
58
+ invalidateSessionListRequest();
58
59
  stopUpdatePolling();
59
60
  window.NetizenSelect?.close();
60
61
  // Release these modal drawers so the page's login link remains reachable.
@@ -69,6 +70,7 @@ function expireAdminSession() {
69
70
  if (notice) notice.hidden = false;
70
71
  window.NetizenIdentity?.expire();
71
72
  window.NetizenSessionAccess?.expire();
73
+ window.NetizenProgressCard?.expire();
72
74
  window.NetizenWorktrees?.expire();
73
75
  setStatus("登录已过期,请重新登录。", true);
74
76
  }
@@ -609,7 +611,7 @@ function confirmProjectDelete(preview) {
609
611
  + "\n定时计划将被删除;后续会话清理失败也不会恢复计划。"
610
612
  + "\n\n原生会话、派生子会话、Codex App/CLI 历史和本地会话登记将永久删除,无法恢复。"
611
613
  + "\n全部会话删除确认成功后,才会删除 Project 登记;部分失败时会保留 Project 和剩余会话。"
612
- + `\n关联受管 Worktrees:${preview.worktreeCount || 0},${preview.deleteWorktrees ? "同时安全删除;失败项保留,不强制丢弃成果" : "保留目录,可在 Worktrees 页面处理;不会自动关联到同名新 Project"}。`
614
+ + `\n关联受管 worktree:${preview.worktreeCount || 0},${preview.deleteWorktrees ? "同时安全删除;失败项保留,不强制丢弃成果" : "保留目录,可在 worktree 页面处理;不会自动关联到同名新 Project"}。`
613
615
  + (preview.deleteWorktrees && preview.worktrees?.length ? `\n将删除的 checkout 根目录:\n${preview.worktrees.map((item) => `${item.id} · ${item.root}`).join("\n")}` : "")
614
616
  + `\n\n磁盘代码目录保留:${preview.project.cwd}`,
615
617
  );
@@ -665,10 +667,10 @@ async function deleteProject(project, deleteWorktrees = false) {
665
667
  if (result.remainingScheduledRunCount) {
666
668
  details.push(`仍有 ${result.remainingScheduledRunCount} 条定时执行的交接或清理未确认。`);
667
669
  }
668
- if (result.deletedWorktreeCount) message += `\n已删除 ${result.deletedWorktreeCount} 个受管 Worktree。`;
670
+ if (result.deletedWorktreeCount) message += `\n已删除 ${result.deletedWorktreeCount} 个受管 worktree。`;
669
671
  if (result.remainingWorktreeCount) {
670
- if (result.deleteWorktrees) details.push(`仍有 ${result.remainingWorktreeCount} 个受管 Worktree 未安全删除,请在 Worktrees 页面检查。`);
671
- else message += `\n已按选择保留 ${result.remainingWorktreeCount} 个受管 Worktree,可在 Worktrees 页面处理。`;
672
+ if (result.deleteWorktrees) details.push(`仍有 ${result.remainingWorktreeCount} 个受管 worktree 未安全删除,请在 worktree 页面检查。`);
673
+ else message += `\n已按选择保留 ${result.remainingWorktreeCount} 个受管 worktree,可在 worktree 页面处理。`;
672
674
  }
673
675
  return result;
674
676
  } catch (error) {
@@ -727,7 +729,7 @@ async function loadProjects(cursor = undefined) {
727
729
  cascadeLabel.className = "cascade-choice";
728
730
  const cascade = document.createElement("input");
729
731
  cascade.type = "checkbox";
730
- cascadeLabel.append(cascade, document.createTextNode("同时删除关联受管 Worktrees"));
732
+ cascadeLabel.append(cascade, document.createTextNode("同时删除关联受管 worktree"));
731
733
  actions.append(cascadeLabel);
732
734
  actions.append(actionButton(
733
735
  "删除 Project 及关联 Sessions", () => deleteProject(project, cascade.checked), true,
@@ -1824,24 +1826,33 @@ function locationFallback(session) {
1824
1826
 
1825
1827
  function sessionLocationCell(row, session, { showIdentity = true } = {}) {
1826
1828
  const td = document.createElement("td");
1829
+ td.className = "session-location";
1827
1830
  const link = document.createElement("a");
1828
1831
  link.className = "chat-link";
1829
- link.href = session.topicOpenUrl || session.chatOpenUrl;
1830
1832
  link.target = "_blank";
1831
1833
  link.rel = "noopener noreferrer";
1832
- link.textContent = session.chatLabelResolved
1833
- ? session.chatLabel
1834
- : showIdentity ? locationFallback(session) : `${chatModeLabel(session.chatMode, session.scopeKind)}(名称暂不可用)`;
1835
- link.title = session.topicOpenUrl
1836
- ? "打开飞书话题"
1837
- : "打开飞书会话";
1838
1834
  const mode = document.createElement("div");
1839
1835
  mode.className = "meta-line";
1840
1836
  mode.append(badge(chatModeLabel(session.chatMode, session.scopeKind)));
1841
1837
  td.append(link, mode);
1838
+ updateSessionLocationCell(td, session, { showIdentity });
1842
1839
  row.append(td);
1843
1840
  }
1844
1841
 
1842
+ function updateSessionLocationCell(td, session, { showIdentity = true, loading = false } = {}) {
1843
+ const link = td.querySelector(".chat-link");
1844
+ link.href = session.topicOpenUrl || session.chatOpenUrl;
1845
+ link.textContent = session.chatLabelResolved
1846
+ ? session.chatLabel
1847
+ : loading ? `${chatModeLabel(session.chatMode, session.scopeKind)}(名称加载中)`
1848
+ : showIdentity ? locationFallback(session) : `${chatModeLabel(session.chatMode, session.scopeKind)}(名称暂不可用)`;
1849
+ link.title = session.topicOpenUrl
1850
+ ? "打开飞书话题"
1851
+ : "打开飞书会话";
1852
+ td.querySelector(".badge").textContent = chatModeLabel(session.chatMode, session.scopeKind);
1853
+ td.setAttribute("aria-busy", String(loading));
1854
+ }
1855
+
1845
1856
  function sessionIdentityCell(row, session) {
1846
1857
  const td = document.createElement("td");
1847
1858
  const title = document.createElement("strong");
@@ -2039,12 +2050,58 @@ function resetSessionFilters(kind = "session") {
2039
2050
  return refresh("sessions", page);
2040
2051
  }
2041
2052
 
2053
+ let pendingSessionChatLabels = null;
2054
+
2042
2055
  function invalidateSessionListRequest() {
2043
2056
  state.sessionListRequest = (state.sessionListRequest || 0) + 1;
2057
+ // A failed replacement read can leave these same rows on screen. Their old
2058
+ // enrichment must stop showing a loading placeholder when it is invalidated.
2059
+ pendingSessionChatLabels?.();
2060
+ pendingSessionChatLabels = null;
2061
+ }
2062
+
2063
+ async function loadSessionChatLabels(kind, data, page, request) {
2064
+ const name = kind === "side" ? "sides" : "sessions";
2065
+ const isCurrent = () => !state.authExpired && request === state.sessionListRequest
2066
+ && state.tab === "sessions" && state.sessionTab === (kind === "side" ? "temporary" : "ordinary")
2067
+ && state[name] === data && state[`${kind}Page`] === page;
2068
+ if (!isCurrent()) return;
2069
+ const body = document.querySelector(`#${name}-body`);
2070
+ const targets = data.items.flatMap((item, index) => item.chatLabelResolved || !item.chatId
2071
+ ? [] : [{ item, cell: body.children[index].querySelector(".session-location") }]);
2072
+ if (!targets.length) return;
2073
+ const showIdentity = kind !== "side";
2074
+ const finish = () => {
2075
+ for (const { item, cell } of targets) updateSessionLocationCell(cell, item, { showIdentity });
2076
+ };
2077
+ pendingSessionChatLabels = finish;
2078
+ for (const { item, cell } of targets) updateSessionLocationCell(cell, item, { showIdentity, loading: true });
2079
+ try {
2080
+ const query = new URLSearchParams({ chatIds: [...new Set(targets.map(({ item }) => item.chatId))].join(",") });
2081
+ const labels = await api(`/api/v1/chat-labels?${query}`);
2082
+ if (!isCurrent()) return;
2083
+ const byChat = new Map(labels.items.map((label) => [label.chatId, label]));
2084
+ for (const { item } of targets) {
2085
+ const label = byChat.get(item.chatId);
2086
+ if (!label) continue;
2087
+ // Enrichment changes display facts only; retain exact identities, topic
2088
+ // links, runtime and action grants from the successful list response.
2089
+ for (const field of ["chatLabel", "chatLabelResolved", "chatMode", "chatType", "chatOpenUrl"]) {
2090
+ if (Object.hasOwn(label, field)) item[field] = label[field];
2091
+ }
2092
+ }
2093
+ } catch {
2094
+ // Name failures are local to the location cells. Authentication expiry is
2095
+ // already handled by api(), and list status/freshness stay unchanged.
2096
+ } finally {
2097
+ if (isCurrent()) finish();
2098
+ if (pendingSessionChatLabels === finish) pendingSessionChatLabels = null;
2099
+ }
2044
2100
  }
2045
2101
 
2046
2102
  async function loadSessionPage(kind, page, render) {
2047
- const request = state.sessionListRequest = (state.sessionListRequest || 0) + 1;
2103
+ invalidateSessionListRequest();
2104
+ const request = state.sessionListRequest;
2048
2105
  const isCurrent = () => request === state.sessionListRequest;
2049
2106
  const name = kind === "side" ? "sides" : "sessions";
2050
2107
  setStatus("正在读取服务端事实…");
@@ -2075,6 +2132,7 @@ async function loadSessionPage(kind, page, render) {
2075
2132
  }
2076
2133
  syncPageUrl();
2077
2134
  setStatus("已更新。");
2135
+ void loadSessionChatLabels(kind, data, state[`${kind}Page`], request);
2078
2136
  return true;
2079
2137
  } catch (error) {
2080
2138
  if (isCurrent()) setStatus(error.message + (state[name] ? " 当前仍显示上次成功读取的结果。" : ""), true);
@@ -2278,7 +2336,8 @@ function renderSessionSettingsEditor() {
2278
2336
  }
2279
2337
  sessionSettingsInput("note").textContent = notes.filter(Boolean).join(" ");
2280
2338
  sessionSettingsInput("note").classList.toggle("error", Boolean(editor.error));
2281
- sessionSettingsInput("save").disabled = editor.loading || editor.saving || !editor.action;
2339
+ sessionSettingsInput("save").disabled = editor.loading || editor.saving || !editor.action
2340
+ || Boolean(progressCardSettingsError(editor.settings));
2282
2341
  sessionSettingsInput("save").textContent = editor.saving ? "保存中…" : "保存";
2283
2342
  sessionSettingsInput("cancel").disabled = editor.saving;
2284
2343
  sessionSettingsInput("close").disabled = editor.saving;
@@ -2291,6 +2350,7 @@ async function loadSessionSettingsOptions() {
2291
2350
  const serial = ++editor.optionsSerial;
2292
2351
  editor.loading = true;
2293
2352
  renderSessionSettingsEditor();
2353
+ const progressRead = editor.settings.progress_card_settings === null ? window.NetizenProgressCard.read() : null;
2294
2354
  try {
2295
2355
  const data = await api("/api/v1/sessions/options");
2296
2356
  if (sessionSettingsEditor !== editor || serial !== editor.optionsSerial) return;
@@ -2301,6 +2361,7 @@ async function loadSessionSettingsOptions() {
2301
2361
  editor.models = [];
2302
2362
  editor.catalogError = `${error.message} 模型目录暂未读取,原选择保持不变。`;
2303
2363
  } finally {
2364
+ await progressRead;
2304
2365
  if (sessionSettingsEditor === editor && serial === editor.optionsSerial) {
2305
2366
  editor.loading = false;
2306
2367
  renderSessionSettingsEditor();
@@ -2366,7 +2427,7 @@ function changeSessionSettings(field) {
2366
2427
  async function saveSessionSettings(event) {
2367
2428
  event.preventDefault();
2368
2429
  const editor = sessionSettingsEditor;
2369
- if (!editor || editor.loading || editor.saving || !editor.action) return;
2430
+ if (!editor || editor.loading || editor.saving || !editor.action || progressCardSettingsError(editor.settings)) return;
2370
2431
  const action = editor.action;
2371
2432
  editor.action = null;
2372
2433
  editor.session.actions.configure = null;
@@ -2533,9 +2594,9 @@ function workspacePolicyAvailable(workspace, options, project) {
2533
2594
 
2534
2595
  function workspacePolicySummary(workspace, { scheduled = false } = {}) {
2535
2596
  if (!workspace) return "沿用原会话的实际工作目录";
2536
- if (workspace.mode === "existing") return `指定 Worktree:${workspace.worktree_id}`;
2537
- if (workspace.mode === "new") return `${scheduled ? "每次执行" : "每个新会话"}创建 Worktree · ${workspace.ref || "创建时 Project HEAD"}`;
2538
- return "项目目录";
2597
+ if (workspace.mode === "existing") return `使用已有 worktree:${workspace.worktree_id}`;
2598
+ if (workspace.mode === "new") return `${scheduled ? "每次执行" : "每个新会话"}新建 worktree · ${workspace.ref || "创建时 Project HEAD"}`;
2599
+ return "项目目录(不使用 worktree)";
2539
2600
  }
2540
2601
 
2541
2602
  function renderWorkspacePolicy(input, editor, { project, loading = false, scheduled = false } = {}) {
@@ -2549,8 +2610,8 @@ function renderWorkspacePolicy(input, editor, { project, loading = false, schedu
2549
2610
  }
2550
2611
  input("worktree-field").hidden = workspace.mode !== "existing";
2551
2612
  input("workspace-ref-field").hidden = workspace.mode !== "new";
2552
- scheduleSelectOptions(input("worktree"), [["", "选择受管 Worktree"],
2553
- ...items.map((item) => [item.id, `${item.cwd} · ${item.branch || "detached"} · ${item.id}`])],
2613
+ scheduleSelectOptions(input("worktree"), [["", "选择受管 worktree"],
2614
+ ...items.map((item) => [item.id, `已有 worktree · ${item.branch || "detached"} · ${item.cwd}`])],
2554
2615
  workspace.worktree_id || "");
2555
2616
  input("worktree").required = workspace.mode === "existing";
2556
2617
  input("workspace-ref").value = workspace.ref || "";
@@ -2563,21 +2624,21 @@ function renderWorkspacePolicy(input, editor, { project, loading = false, schedu
2563
2624
  input("workspace-refs").append(option);
2564
2625
  }
2565
2626
  const notes = [];
2566
- if (loading) notes.push("正在读取工作区选项…");
2627
+ if (loading) notes.push("正在读取 worktree 选项…");
2567
2628
  if (editor.workspaceError) notes.push(workspaceErrorMessage(editor.workspaceError));
2568
2629
  else if (options?.isGit === false) notes.push("该 Project 不是 Git 仓库,只支持项目目录。");
2569
- else if (!project) notes.push("先选择 Project,再选择工作区策略。");
2630
+ else if (!project) notes.push("先选择 Project,再选择 worktree 使用方式。");
2570
2631
  if (workspace.mode === "existing") {
2571
- notes.push(scheduled ? "各次执行共享所选 Worktree 的文件和分支;资源不可用时保留计划并跳过执行。"
2572
- : "新会话共享所选 Worktree 的文件和分支;删除或失效后保留此配置,自动创建会话时明确报错。");
2632
+ notes.push(scheduled ? "各次执行共享所选 worktree 的文件和分支;资源不可用时保留计划并跳过执行。"
2633
+ : "新会话共享所选 worktree 的文件和分支;删除或失效后保留此配置,自动创建会话时明确报错。");
2573
2634
  if (!loading && !workspacePolicyAvailable(workspace, options, project)) {
2574
- notes.push("所选 Worktree 当前不可用,请重新选择;不会切回项目目录。");
2635
+ notes.push("所选 worktree 当前不可用,请重新选择;不会切回项目目录。");
2575
2636
  }
2576
2637
  } else if (workspace.mode === "new") {
2577
2638
  notes.push(scheduled
2578
- ? "每次执行创建一份 detached Worktree;该次会话的后续消息复用。留空使用执行时 Project 的 HEAD,预览和保存不创建目录。"
2579
- : "每次自动创建新会话时创建一次 detached Worktree;后续消息复用。留空使用创建时 Project 的 HEAD,保存配置不创建目录。");
2580
- if (!loading && !available) notes.push("当前无法使用新建 Worktree 策略,请检查 Project。");
2639
+ ? "每次执行新建 detached worktree;该次会话的后续消息复用。留空使用执行时 Project 的 HEAD,预览和保存不创建目录。"
2640
+ : "每个自动创建的新会话新建 detached worktree;后续消息复用。留空使用创建时 Project 的 HEAD,保存配置不创建目录。");
2641
+ if (!loading && !available) notes.push("当前无法使用新建 worktree 方式,请检查 Project。");
2581
2642
  } else notes.push(scheduled ? "每次执行创建的会话使用 Project 的项目目录。" : "新会话使用 Project 的项目目录。");
2582
2643
  input("workspace-help").textContent = notes.join(" ");
2583
2644
  input("workspace-help").classList.toggle("error", !loading && !workspacePolicyAvailable(workspace, options, project));
@@ -2646,7 +2707,8 @@ function renderDefaultSettings() {
2646
2707
  if (editor.contextAvailable === false) notes.push("单聊不支持补齐未读上下文,请使用当前消息。");
2647
2708
  defaultsInput("note").textContent = notes.filter(Boolean).join(" ");
2648
2709
  defaultsInput("note").classList.toggle("error", Boolean(editor.error));
2649
- defaultsInput("save").disabled = defaultsBusy || editor.loading || !editor.action || !defaultWorkspaceAvailable();
2710
+ defaultsInput("save").disabled = defaultsBusy || editor.loading || !editor.action || !defaultWorkspaceAvailable()
2711
+ || Boolean(progressCardSettingsError(editor.settings));
2650
2712
  defaultsInput("save").textContent = defaultsBusy ? "保存中…" : "保存";
2651
2713
  defaultsInput("editor").setAttribute("aria-busy", String(defaultsBusy || editor.loading));
2652
2714
  }
@@ -2709,6 +2771,7 @@ async function loadDefaultOptions(resolveChat = false) {
2709
2771
  editor.workspaceOptions = null;
2710
2772
  editor.workspaceError = "";
2711
2773
  renderDefaultSettings();
2774
+ let progressRead = editor.settings.progress_card_settings === null ? window.NetizenProgressCard.read() : null;
2712
2775
  try {
2713
2776
  let view = null;
2714
2777
  if (resolveChat && chatId) {
@@ -2751,6 +2814,11 @@ async function loadDefaultOptions(resolveChat = false) {
2751
2814
  editor.error = `${error.message} 请重新打开表单后操作。`;
2752
2815
  if (resolveChat) editor.action = null;
2753
2816
  } finally {
2817
+ if (defaultsEditor === editor && serial === editor.serial
2818
+ && editor.settings.progress_card_settings === null && !progressRead) {
2819
+ progressRead = window.NetizenProgressCard.read();
2820
+ }
2821
+ await progressRead;
2754
2822
  if (defaultsEditor === editor && serial === editor.serial) {
2755
2823
  // Initial catalog/defaults hydration is not a user edit. Later target
2756
2824
  // resolution must not erase the original values used for discard checks.
@@ -2864,7 +2932,8 @@ async function defaultMutation(mode, action, extra = {}) {
2864
2932
  async function saveDefault(event) {
2865
2933
  event.preventDefault();
2866
2934
  const editor = defaultsEditor;
2867
- if (!editor || editor.loading || !editor.action || defaultsBusy || !defaultWorkspaceAvailable()) return;
2935
+ if (!editor || editor.loading || !editor.action || defaultsBusy || !defaultWorkspaceAvailable()
2936
+ || progressCardSettingsError(editor.settings)) return;
2868
2937
  if (editor.kind === "chat") {
2869
2938
  editor.loading = true;
2870
2939
  renderDefaultSettings();
@@ -3027,7 +3096,9 @@ function sessionSettingsFingerprint(settings) {
3027
3096
  const turn = settings.turn_settings;
3028
3097
  return JSON.stringify([turn ? [turn.model_id, turn.effort_id, turn.service_tier_id] : null,
3029
3098
  settings.message_context_mode, settings.reaction_pulse_enabled,
3030
- settings.progress_card_enabled, settings.completion_mention_enabled]);
3099
+ settings.progress_card_enabled, settings.completion_mention_enabled,
3100
+ settings.progress_card_settings === null ? null
3101
+ : progressCardFields.map((field) => settings.progress_card_settings[field.replaceAll("-", "_")])]);
3031
3102
  }
3032
3103
 
3033
3104
  function confirmDiscardChanges(changed) {
@@ -3037,6 +3108,7 @@ function confirmDiscardChanges(changed) {
3037
3108
  function hasUnsavedEditors() {
3038
3109
  return projectEditorHasChanges() || sessionSettingsHasChanges() || defaultEditorHasChanges() || scheduleEditorHasChanges()
3039
3110
  || Boolean(pendingScheduleDraft?.dirty) || Boolean(window.NetizenSessionAccess?.dirty())
3111
+ || Boolean(window.NetizenProgressCard?.dirty())
3040
3112
  || Boolean(window.NetizenWorktrees?.dirty());
3041
3113
  }
3042
3114
 
@@ -3044,7 +3116,8 @@ function hasUnsavedEditors() {
3044
3116
  // existing fail-closed boundary; this helper never cancels or repeats a POST.
3045
3117
  function confirmDiscardEditors() {
3046
3118
  if (projectEditor?.saving || sessionSettingsEditor?.saving || defaultsBusy || (scheduleEditor && scheduleInput("fields").disabled)
3047
- || window.NetizenSessionAccess?.busy() || window.NetizenWorktrees?.busy()) return false;
3119
+ || window.NetizenSessionAccess?.busy() || window.NetizenProgressCard?.busy()
3120
+ || window.NetizenWorktrees?.busy()) return false;
3048
3121
  if (!confirmDiscardChanges(hasUnsavedEditors())) return false;
3049
3122
  closeProjectEditor({ confirmed: true });
3050
3123
  closeSessionSettingsEditor({ confirmed: true });
@@ -3052,6 +3125,7 @@ function confirmDiscardEditors() {
3052
3125
  if (scheduleEditor) closeScheduleEditor({ confirmed: true });
3053
3126
  cancelScheduleSelection({ confirmed: true });
3054
3127
  window.NetizenSessionAccess?.discard();
3128
+ window.NetizenProgressCard?.discard();
3055
3129
  window.NetizenWorktrees?.discard();
3056
3130
  return true;
3057
3131
  }
@@ -3093,10 +3167,10 @@ function scheduleRunStatus(status) {
3093
3167
  not_started: "尚未执行", expired: "已过期,未执行", missed: "已错过",
3094
3168
  skipped_busy: "上次仍在执行,本次跳过", blocked_unknown: "上次结果待确认,本次跳过",
3095
3169
  project_disabled: "Project 已停用,本次未执行", project_unavailable: "Project 不可用,本次未执行",
3096
- workspace_unavailable: "工作区不可用,本次未执行",
3097
- workspace_creation_failed: "工作区创建失败,本次未执行",
3098
- workspace_creation_unknown: "工作区创建结果待确认,本次未执行",
3099
- workspace_handoff_failed: "工作区已保留,会话交接失败",
3170
+ workspace_unavailable: "worktree 不可用,本次未执行",
3171
+ workspace_creation_failed: "worktree 创建失败,本次未执行",
3172
+ workspace_creation_unknown: "worktree 创建结果待确认,本次未执行",
3173
+ workspace_handoff_failed: "worktree 已保留,会话交接失败",
3100
3174
  released: "调度已收尾", recovery_no_start: "服务恢复,本次未启动",
3101
3175
  publishing_unknown: "话题投递待确认", deleted: "执行会话已删除",
3102
3176
  initial_start_rejected: "启动被拒绝", publishing_failed: "话题发布失败",
@@ -3113,7 +3187,7 @@ function scheduleTriggerSource(source) {
3113
3187
 
3114
3188
  function scheduleWorktreeReceipt(run, { details = true } = {}) {
3115
3189
  if (!run?.worktree_id) return "";
3116
- const labels = [`Worktree:${run.worktree_id}`];
3190
+ const labels = [`worktree:${run.worktree_id}`];
3117
3191
  const receipt = run.worktree;
3118
3192
  if (details && receipt) {
3119
3193
  if (receipt.cwd) labels.push(`工作目录:${receipt.cwd}`);
@@ -3139,7 +3213,7 @@ function scheduleBlockedLabel(plan) {
3139
3213
  project_unavailable: "Project 不可用", target_inactive: "原会话已切走或归档,自动暂停",
3140
3214
  target_missing: "原会话不可用", target_mismatch: "原会话位置不匹配" }[plan.blocked_reason] || "";
3141
3215
  const workspace = workspaceErrorMessage(plan.workspace_error)
3142
- || ([plan.blocked_reason, plan.suspended_reason].includes("workspace_unavailable") ? "工作区不可用" : "");
3216
+ || ([plan.blocked_reason, plan.suspended_reason].includes("workspace_unavailable") ? "worktree 不可用" : "");
3143
3217
  return [primary, workspace].filter(Boolean).join(" · ");
3144
3218
  }
3145
3219
 
@@ -3153,7 +3227,7 @@ function scheduleInheritedWorkspace(plan) {
3153
3227
  const labels = [workspace.working_cwd
3154
3228
  ? `${workspace.native_cwd_unresolved ? "已登记" : "已选"}工作目录:${workspace.working_cwd}`
3155
3229
  : "工作目录尚未登记"];
3156
- if (workspace.worktree_id) labels.push(`Worktree:${workspace.worktree_id}`);
3230
+ if (workspace.worktree_id) labels.push(`worktree:${workspace.worktree_id}`);
3157
3231
  labels.push(workspace.native_cwd_unresolved
3158
3232
  ? "实际目录由原会话确定,本次未查询原生目录。" : "执行时沿用原会话的工作目录。");
3159
3233
  return labels.join(" · ");
@@ -3273,8 +3347,8 @@ function validateScheduleFields({ saving = false } = {}) {
3273
3347
  setScheduleFieldError("weekdays", "请至少选择一天。");
3274
3348
  }
3275
3349
  if (saving && !scheduleWorkspaceCanSave()) {
3276
- setScheduleFieldError("workspace", scheduleEditor.workspaceLoading ? "工作区选项仍在读取,请稍后保存。"
3277
- : workspaceErrorMessage(scheduleEditor.workspaceError) || "请选择可用的工作区策略。");
3350
+ setScheduleFieldError("workspace", scheduleEditor.workspaceLoading ? "worktree 选项仍在读取,请稍后保存。"
3351
+ : workspaceErrorMessage(scheduleEditor.workspaceError) || "请选择可用的 worktree 使用方式。");
3278
3352
  }
3279
3353
  if (scheduleFieldErrors.size) {
3280
3354
  if (scheduleEditor?.action) showScheduleFormError("请检查标红的表单项。");
@@ -3489,7 +3563,53 @@ function resetScheduleEndOffset() {
3489
3563
 
3490
3564
  function defaultScheduleSessionSettings() {
3491
3565
  return { turn_settings: null, reaction_pulse_enabled: false, progress_card_enabled: true, completion_mention_enabled: true,
3492
- message_context_mode: "current-only" };
3566
+ message_context_mode: "current-only", progress_card_settings: null };
3567
+ }
3568
+
3569
+ const progressCardFields = ["show-commentary", "show-operations", "show-plan", "commentary-limit",
3570
+ "operation-limit", "commentary-max-chars", "operation-max-chars", "organization"];
3571
+
3572
+ function progressCardSettings(settings) {
3573
+ return settings.progress_card_settings || window.NetizenProgressCard?.current();
3574
+ }
3575
+
3576
+ function progressCardSettingsError(settings) {
3577
+ const progress = progressCardSettings(settings);
3578
+ if (!progress) return "";
3579
+ for (const [key, label, maximum] of [
3580
+ ["commentary_limit", "进展条数", 8], ["operation_limit", "操作条数", 16],
3581
+ ["commentary_max_chars", "进展摘要长度", 320], ["operation_max_chars", "操作摘要长度", 240],
3582
+ ]) {
3583
+ if (!Number.isInteger(progress[key]) || progress[key] < 1 || progress[key] > maximum) {
3584
+ return `${label}必须是 1 到 ${maximum} 之间的整数。`;
3585
+ }
3586
+ }
3587
+ return "";
3588
+ }
3589
+
3590
+ function renderProgressCardSettings(input, settings) {
3591
+ const container = input("progress-settings");
3592
+ if (!container) return;
3593
+ const progress = progressCardSettings(settings);
3594
+ container.hidden = !settings.progress_card_enabled;
3595
+ const source = input("progress-source");
3596
+ if (source) source.hidden = settings.progress_card_settings !== null;
3597
+ for (const node of container.querySelectorAll("input, select")) {
3598
+ const field = node.id.split("-progress-").at(-1);
3599
+ const key = field.replaceAll("-", "_");
3600
+ if (field.startsWith("show-")) node.checked = progress?.[key] || false;
3601
+ else node.value = progress?.[key] ?? "";
3602
+ node.disabled = !progress || !settings.progress_card_enabled
3603
+ || (field.startsWith("commentary-") && !progress.show_commentary)
3604
+ || (field.startsWith("operation-") && !progress.show_operations);
3605
+ }
3606
+ for (const [kind, shown] of [["commentary", progress?.show_commentary], ["operation", progress?.show_operations]]) {
3607
+ input(`progress-${kind}-capacity`).hidden = !shown;
3608
+ }
3609
+ const error = progress ? progressCardSettingsError(settings) : "读取全局配置后可编辑过程卡细项。";
3610
+ const note = input("progress-error");
3611
+ note.textContent = error;
3612
+ note.hidden = !error;
3493
3613
  }
3494
3614
 
3495
3615
  function scheduleSessionSummary(settings) {
@@ -3535,6 +3655,7 @@ function renderSessionSettingsFields(input, settings, models, contextAvailable)
3535
3655
  input("reactions").checked = settings.reaction_pulse_enabled;
3536
3656
  input("progress").checked = settings.progress_card_enabled;
3537
3657
  input("completion-mention").checked = settings.completion_mention_enabled;
3658
+ renderProgressCardSettings(input, settings);
3538
3659
  return model;
3539
3660
  }
3540
3661
 
@@ -3552,6 +3673,15 @@ function changeSessionSettingsField(input, settings, models, field, baseTurn = n
3552
3673
  else if (field === "reactions") settings.reaction_pulse_enabled = input("reactions").checked;
3553
3674
  else if (field === "progress") settings.progress_card_enabled = input("progress").checked;
3554
3675
  else if (field === "completion-mention") settings.completion_mention_enabled = input("completion-mention").checked;
3676
+ else if (field.startsWith("progress-") && progressCardFields.includes(field.slice(9))) {
3677
+ const progress = progressCardSettings(settings);
3678
+ if (!progress) return;
3679
+ const key = field.slice(9).replaceAll("-", "_");
3680
+ const node = input(field);
3681
+ const value = key.startsWith("show_") ? node.checked
3682
+ : key === "organization" || node.value === "" ? node.value : Number(node.value);
3683
+ settings.progress_card_settings = { ...progress, [key]: value };
3684
+ }
3555
3685
  }
3556
3686
 
3557
3687
  function renderScheduleSessionSettings() {
@@ -4488,8 +4618,9 @@ async function refresh(tab, cursor = undefined) {
4488
4618
  if (tab === "schedules" && await loadSchedules(cursor) === false) return false;
4489
4619
  if (tab === "defaults") await loadDefaults();
4490
4620
  if (tab === "system") {
4491
- const results = await Promise.all([window.NetizenIdentity.loadUsers(), window.NetizenSessionAccess?.load()]);
4492
- if (results[1] === false) return false;
4621
+ const results = await Promise.all([window.NetizenIdentity.loadUsers(), window.NetizenSessionAccess?.load(),
4622
+ window.NetizenProgressCard?.load()]);
4623
+ if (results.includes(false)) return false;
4493
4624
  }
4494
4625
  if (state.authExpired) return false;
4495
4626
  if (isCurrentView()) setStatus("已更新。");
@@ -4527,7 +4658,7 @@ function selectSystemTab(name) {
4527
4658
  if (state.authExpired || state.systemTab === name) return;
4528
4659
  window.NetizenSelect?.close();
4529
4660
  if (state.systemTab === "admins") window.NetizenIdentity?.suspendSelector();
4530
- else window.NetizenSessionAccess?.closePickers();
4661
+ else if (state.systemTab === "access") window.NetizenSessionAccess?.closePickers();
4531
4662
  state.systemTab = name;
4532
4663
  for (const button of document.querySelectorAll("[data-system-tab]")) {
4533
4664
  const selected = button.dataset.systemTab === name;
@@ -4594,8 +4725,10 @@ for (const tab of document.querySelectorAll("[data-system-tab]")) {
4594
4725
  tab.addEventListener("keydown", (event) => {
4595
4726
  if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
4596
4727
  event.preventDefault();
4597
- const name = event.key === "Home" ? "admins" : event.key === "End" ? "access"
4598
- : tab.dataset.systemTab === "admins" ? "access" : "admins";
4728
+ const names = [...document.querySelectorAll("[data-system-tab]")].map((button) => button.dataset.systemTab);
4729
+ const index = names.indexOf(tab.dataset.systemTab);
4730
+ const name = event.key === "Home" ? names[0] : event.key === "End" ? names.at(-1)
4731
+ : names[(index + (event.key === "ArrowRight" ? 1 : names.length - 1)) % names.length];
4599
4732
  selectSystemTab(name);
4600
4733
  document.querySelector(`#system-tab-${name}`).focus();
4601
4734
  });
@@ -4652,7 +4785,8 @@ sessionSettingsInput("drawer").addEventListener("cancel", (event) => {
4652
4785
  closeSessionSettingsEditor();
4653
4786
  });
4654
4787
  sessionSettingsInput("editor").addEventListener("submit", saveSessionSettings);
4655
- for (const field of ["model", "effort", "tier", "context", "reactions", "progress", "completion-mention"]) {
4788
+ for (const field of ["model", "effort", "tier", "context", "reactions", "progress", "completion-mention",
4789
+ ...progressCardFields.map((name) => `progress-${name}`)]) {
4656
4790
  sessionSettingsInput(field).addEventListener("change", () => changeSessionSettings(field));
4657
4791
  }
4658
4792
  defaultsInput("new").addEventListener("click", () => openDefaultEditor(defaultsTab));
@@ -4679,7 +4813,8 @@ for (const field of ["workspace", "worktree"]) {
4679
4813
  defaultsInput(field).addEventListener("change", () => changeDefaultWorkspace(field));
4680
4814
  }
4681
4815
  defaultsInput("workspace-ref").addEventListener("input", () => changeDefaultWorkspace("workspace-ref"));
4682
- for (const field of ["model", "effort", "tier", "context", "reactions", "progress", "completion-mention"]) {
4816
+ for (const field of ["model", "effort", "tier", "context", "reactions", "progress", "completion-mention",
4817
+ ...progressCardFields.map((name) => `progress-${name}`)]) {
4683
4818
  defaultsInput(field).addEventListener("change", () => changeDefaultSettings(field));
4684
4819
  }
4685
4820
  for (const [name, delta] of [["previous", -50], ["next", 50]]) {