netizen-cli 0.14.0__tar.gz → 0.14.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. {netizen_cli-0.14.0/netizen_cli.egg-info → netizen_cli-0.14.2}/PKG-INFO +1 -1
  2. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/__init__.py +1 -1
  3. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/admin.css +3 -2
  4. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/admin.js +45 -20
  5. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/index.html +36 -0
  6. netizen_cli-0.14.2/netizen_cli/admin/static/progress-card.js +118 -0
  7. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/web.py +53 -3
  8. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/bindings.py +11 -7
  9. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/controls.py +15 -10
  10. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/scheduled.py +23 -5
  11. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel_app.py +48 -12
  12. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/codex_runtime.py +19 -7
  13. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/database_migrations.py +4 -0
  14. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/domain.py +1 -0
  15. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/main.py +6 -0
  16. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/management/service.py +25 -0
  17. netizen_cli-0.14.2/netizen_cli/migrations/v19.py +94 -0
  18. netizen_cli-0.14.2/netizen_cli/migrations/v20.py +96 -0
  19. netizen_cli-0.14.2/netizen_cli/progress_card_config.py +45 -0
  20. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/schedules/mcp.py +7 -4
  21. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/schedules/models.py +1 -0
  22. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/schedules/service.py +16 -6
  23. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/schedules/store.py +25 -10
  24. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/session_settings.py +10 -6
  25. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/sessions/policy.py +12 -14
  26. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/settings.py +8 -0
  27. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/worktrees/service.py +1 -1
  28. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/worktrees/settings.py +16 -16
  29. {netizen_cli-0.14.0 → netizen_cli-0.14.2/netizen_cli.egg-info}/PKG-INFO +1 -1
  30. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli.egg-info/SOURCES.txt +9 -0
  31. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/pyproject.toml +1 -1
  32. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/admin.md +19 -3
  33. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/feedback-and-files.md +11 -4
  34. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/scheduled-tasks.md +21 -1
  35. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/sessions.md +5 -1
  36. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_binding_scheduled_channel.py +6 -1
  37. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cards.py +2 -2
  38. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_channel_app.py +6 -2
  39. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cli_data.py +6 -6
  40. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_codex_runtime.py +170 -3
  41. netizen_cli-0.14.2/tests/test_configuration_publication.py +132 -0
  42. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cron_chat_targets.py +10 -3
  43. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_database_migrations.py +4 -4
  44. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_defaults_cards.py +5 -2
  45. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_migration_schema_validation.py +1 -1
  46. netizen_cli-0.14.2/tests/test_optional_progress_card_settings_migration.py +142 -0
  47. netizen_cli-0.14.2/tests/test_progress_card_config.py +107 -0
  48. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_progress_card_entrypoints.py +33 -2
  49. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_progress_card_settings_migration.py +5 -5
  50. netizen_cli-0.14.2/tests/test_schedule_auto_archive_migration.py +110 -0
  51. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_card_retry.py +3 -0
  52. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_cards.py +49 -1
  53. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_mcp.py +25 -0
  54. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_service.py +54 -0
  55. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_workspace_migration.py +2 -2
  56. netizen_cli-0.14.2/tests/test_scheduled_auto_archive.py +270 -0
  57. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_scheduled_runtime.py +12 -3
  58. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedules_storage.py +30 -1
  59. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_session_defaults_migration.py +3 -3
  60. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_session_external_runtime.py +13 -0
  61. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_session_settings.py +8 -1
  62. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_worktree_service.py +25 -0
  63. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_worktree_tools_integration.py +6 -4
  64. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/MANIFEST.in +0 -0
  65. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/README.md +0 -0
  66. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/_build.py +0 -0
  67. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/config.example.yaml +0 -0
  68. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/__main__.py +0 -0
  69. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/account_rate_limits.py +0 -0
  70. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/__init__.py +0 -0
  71. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/auth.py +0 -0
  72. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/credentials.py +0 -0
  73. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/errors.py +0 -0
  74. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/feishu.py +0 -0
  75. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/login.py +0 -0
  76. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/port_config.py +0 -0
  77. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/presentation.py +0 -0
  78. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/queries.py +0 -0
  79. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/chat-picker.css +0 -0
  80. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/chat-picker.js +0 -0
  81. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/identity.js +0 -0
  82. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/login.html +0 -0
  83. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/login.js +0 -0
  84. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/session-access.js +0 -0
  85. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/static/worktrees.js +0 -0
  86. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/transport.py +0 -0
  87. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/admin/users.py +0 -0
  88. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/builtin_skills.py +0 -0
  89. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/__init__.py +0 -0
  90. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/callbacks.py +0 -0
  91. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/chat_target.py +0 -0
  92. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/defaults.py +0 -0
  93. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/fork.py +0 -0
  94. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/model_info.py +0 -0
  95. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/pagination.py +0 -0
  96. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/questions.py +0 -0
  97. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/reply.py +0 -0
  98. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/session_ids.py +0 -0
  99. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/status.py +0 -0
  100. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/usage.py +0 -0
  101. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cards/workspace.py +0 -0
  102. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/__init__.py +0 -0
  103. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/completion_mentions.py +0 -0
  104. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/input_preparation.py +0 -0
  105. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/messages.py +0 -0
  106. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/ports.py +0 -0
  107. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/question_inputs.py +0 -0
  108. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/reactions.py +0 -0
  109. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/reply_presenter.py +0 -0
  110. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/session_pages.py +0 -0
  111. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/channel/topics.py +0 -0
  112. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/chat_avatars.py +0 -0
  113. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/chat_targets.py +0 -0
  114. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cli.py +0 -0
  115. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cli_data.py +0 -0
  116. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cli_packages.py +0 -0
  117. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cli_services.py +0 -0
  118. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cli_setup.py +0 -0
  119. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cli_update.py +0 -0
  120. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cli_update_restore.py +0 -0
  121. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/cli_update_worker.py +0 -0
  122. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/completion_mention.py +0 -0
  123. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/defaults/__init__.py +0 -0
  124. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/defaults/models.py +0 -0
  125. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/defaults/service.py +0 -0
  126. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/defaults/store.py +0 -0
  127. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/deployment/__init__.py +0 -0
  128. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/deployment/restart_worker.py +0 -0
  129. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/deployment/update_executor.py +0 -0
  130. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/deployment/update_protocol.py +0 -0
  131. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/error_messages.py +0 -0
  132. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/experience.py +0 -0
  133. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/feishu_app_onboarding.py +0 -0
  134. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/feishu_app_permissions.py +0 -0
  135. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/feishu_links.py +0 -0
  136. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/git_status.py +0 -0
  137. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/image_inputs.py +0 -0
  138. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/instance.py +0 -0
  139. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/lark_app.py +0 -0
  140. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/management/__init__.py +0 -0
  141. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/management/blocking_io.py +0 -0
  142. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/management/chat_directory.py +0 -0
  143. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/management/chat_labels.py +0 -0
  144. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/management/coordination.py +0 -0
  145. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/management/updates.py +0 -0
  146. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/markdown_images.py +0 -0
  147. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/mcp_transport.py +0 -0
  148. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/message_content.py +0 -0
  149. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/message_history.py +0 -0
  150. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/message_preparation.py +0 -0
  151. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/message_projection.py +0 -0
  152. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/migrations/__init__.py +0 -0
  153. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/migrations/schema.py +0 -0
  154. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/migrations/v14.py +0 -0
  155. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/migrations/v15.py +0 -0
  156. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/migrations/v16.py +0 -0
  157. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/migrations/v17.py +0 -0
  158. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/migrations/v18.py +0 -0
  159. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/model_settings.py +0 -0
  160. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/package_resources.py +0 -0
  161. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/pagination.py +0 -0
  162. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/partial_answers.py +0 -0
  163. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/progress_card_settings.py +0 -0
  164. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/projects.py +0 -0
  165. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/prompt_projection.py +0 -0
  166. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/quoted_context.py +0 -0
  167. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/result_images.py +0 -0
  168. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/runtime/__init__.py +0 -0
  169. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/runtime/contracts.py +0 -0
  170. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/runtime/name_writes.py +0 -0
  171. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/runtime/thread_naming.py +0 -0
  172. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/schedules/__init__.py +0 -0
  173. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/schedules/scheduler.py +0 -0
  174. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/sdk_gap_adapter.py +0 -0
  175. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/service_launcher.py +0 -0
  176. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/session_inventory.py +0 -0
  177. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/sessions/__init__.py +0 -0
  178. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/sessions/delegation.py +0 -0
  179. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/sessions/errors.py +0 -0
  180. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/sessions/mcp.py +0 -0
  181. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/sessions/presentation.py +0 -0
  182. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/sessions/schema.py +0 -0
  183. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/sessions/service.py +0 -0
  184. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/skill_references.py +0 -0
  185. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/task_text.py +0 -0
  186. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/terminal_cleanup.py +0 -0
  187. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/thread_history.py +0 -0
  188. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/turn_activity.py +0 -0
  189. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/turn_files.py +0 -0
  190. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/turn_patch_children.py +0 -0
  191. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/turn_plan_observer.py +0 -0
  192. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/user_questions.py +0 -0
  193. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/worktrees/errors.py +0 -0
  194. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/worktrees/git.py +0 -0
  195. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/worktrees/models.py +0 -0
  196. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/worktrees/policy.py +0 -0
  197. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli/worktrees/store.py +0 -0
  198. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli.egg-info/dependency_links.txt +0 -0
  199. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli.egg-info/entry_points.txt +0 -0
  200. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli.egg-info/requires.txt +0 -0
  201. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/netizen_cli.egg-info/top_level.txt +0 -0
  202. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/setup.cfg +0 -0
  203. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-lark/SKILL.md +0 -0
  204. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/SKILL.md +0 -0
  205. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/commands.md +0 -0
  206. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/concepts.md +0 -0
  207. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/faq.md +0 -0
  208. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/fork-side-goal.md +0 -0
  209. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/messages.md +0 -0
  210. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/native-capabilities.md +0 -0
  211. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/session-collaboration.md +0 -0
  212. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/status-and-control.md +0 -0
  213. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/skills/netizen-user-guide/references/user-guide.md +0 -0
  214. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_account_rate_limits.py +0 -0
  215. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_admin_update_executor_probe.py +0 -0
  216. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_bindings.py +0 -0
  217. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_channel_fixtures.py +0 -0
  218. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_channel_input_preparation.py +0 -0
  219. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_chat_avatars.py +0 -0
  220. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_chat_target_cards.py +0 -0
  221. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_check_sdk.py +0 -0
  222. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cli.py +0 -0
  223. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cli_distribution.py +0 -0
  224. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cli_integration.py +0 -0
  225. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cli_packages.py +0 -0
  226. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cli_packaging.py +0 -0
  227. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cli_services.py +0 -0
  228. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cli_setup.py +0 -0
  229. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_cli_update.py +0 -0
  230. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_codex_sdk_capabilities.py +0 -0
  231. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_codex_sdk_upgrade_0161.py +0 -0
  232. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_codex_sdk_upgrade_0163.py +0 -0
  233. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_completion_mention_cards.py +0 -0
  234. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_completion_mentions.py +0 -0
  235. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_completion_topic_delivery.py +0 -0
  236. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_defaults_channel.py +0 -0
  237. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_defaults_runtime.py +0 -0
  238. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_defaults_worktree_channel.py +0 -0
  239. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_deployment.py +0 -0
  240. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_documentation_links.py +0 -0
  241. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_domain_experience.py +0 -0
  242. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_error_messages.py +0 -0
  243. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_feishu_app_onboarding.py +0 -0
  244. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_feishu_app_permissions.py +0 -0
  245. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_file_pagination.py +0 -0
  246. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_fork_cards.py +0 -0
  247. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_git_status.py +0 -0
  248. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_goal_reply_separation.py +0 -0
  249. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_image_inputs.py +0 -0
  250. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_lark_app.py +0 -0
  251. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_lark_sdk_contract.py +0 -0
  252. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_main.py +0 -0
  253. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_markdown_images.py +0 -0
  254. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_message_content.py +0 -0
  255. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_message_history.py +0 -0
  256. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_message_material_inputs.py +0 -0
  257. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_message_preparation.py +0 -0
  258. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_model_info_cards.py +0 -0
  259. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_model_settings.py +0 -0
  260. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_name_writes.py +0 -0
  261. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_pagination.py +0 -0
  262. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_partial_answer_delivery.py +0 -0
  263. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_partial_answers.py +0 -0
  264. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_persistent_fork_bindings.py +0 -0
  265. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_persistent_fork_channel.py +0 -0
  266. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_persistent_fork_runtime.py +0 -0
  267. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_child_files.py +0 -0
  268. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_feishu_completion_mention.py +0 -0
  269. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_feishu_partial_answers.py +0 -0
  270. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_feishu_turn_file_card.py +0 -0
  271. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_persistent_fork.py +0 -0
  272. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_python_sdk.py +0 -0
  273. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_scheduled_tasks.py +0 -0
  274. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_sdk_completion_race.py +0 -0
  275. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_sdk_response_recovery.py +0 -0
  276. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_skill_execution.py +0 -0
  277. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_probe_thread_naming.py +0 -0
  278. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_progress_card_presentation.py +0 -0
  279. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_project_delete_channel.py +0 -0
  280. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_project_delete_runtime.py +0 -0
  281. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_project_deletion_storage.py +0 -0
  282. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_prompt_projection.py +0 -0
  283. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_question_cards.py +0 -0
  284. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_question_channel.py +0 -0
  285. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_question_target_parity.py +0 -0
  286. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_quoted_context.py +0 -0
  287. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_release_script.py +0 -0
  288. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_repository_hygiene.py +0 -0
  289. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_result_images.py +0 -0
  290. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_resume_channel.py +0 -0
  291. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_retired_deployment.py +0 -0
  292. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_runtime_resume_activation.py +0 -0
  293. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_runtime_thread_naming.py +0 -0
  294. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_run_now.py +0 -0
  295. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_scope_matrix.py +0 -0
  296. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_workspace_cards.py +0 -0
  297. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_workspace_service.py +0 -0
  298. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_schedule_workspace_storage.py +0 -0
  299. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_scheduled_channel.py +0 -0
  300. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_scheduled_worktrees.py +0 -0
  301. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_scheduler.py +0 -0
  302. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_scheduler_presenter.py +0 -0
  303. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_sdk_gap_adapter.py +0 -0
  304. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_service_launcher.py +0 -0
  305. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_session_card_pages.py +0 -0
  306. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_session_collaboration_app.py +0 -0
  307. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_session_defaults.py +0 -0
  308. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_session_id_cards.py +0 -0
  309. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_session_policy.py +0 -0
  310. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_session_tools.py +0 -0
  311. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_settings_projects.py +0 -0
  312. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_status_cards.py +0 -0
  313. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_status_commands.py +0 -0
  314. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_task_text.py +0 -0
  315. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_terminal_cleanup.py +0 -0
  316. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_thread_history.py +0 -0
  317. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_thread_naming.py +0 -0
  318. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_turn_activity.py +0 -0
  319. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_turn_files.py +0 -0
  320. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_turn_patch_children.py +0 -0
  321. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_turn_patch_statistics.py +0 -0
  322. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_turn_plan_observer.py +0 -0
  323. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_update_executor.py +0 -0
  324. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_usage_card.py +0 -0
  325. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_usage_control.py +0 -0
  326. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_user_guide_skill.py +0 -0
  327. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_user_questions.py +0 -0
  328. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_worktree_channel_cwd.py +0 -0
  329. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_worktree_git.py +0 -0
  330. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_worktree_new.py +0 -0
  331. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_worktree_runtime_cwd.py +0 -0
  332. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_worktree_settings.py +0 -0
  333. {netizen_cli-0.14.0 → netizen_cli-0.14.2}/tests/test_worktree_storage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netizen-cli
3
- Version: 0.14.0
3
+ Version: 0.14.2
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
@@ -1,3 +1,3 @@
1
1
  """Netizen: a thin Feishu Channel for native Codex Threads."""
2
2
 
3
- __version__ = "0.14.0"
3
+ __version__ = "0.14.2"
@@ -429,8 +429,9 @@ body.schedule-detail-open { overflow: hidden; }
429
429
  .tab-unsaved[hidden] { display: none; }
430
430
  .admin-users { padding: 0; margin: 0; list-style: none; }
431
431
  .admin-users > li { display: flex; justify-content: space-between; align-items: center; gap: 16px; border-top: 1px solid var(--line); padding: 16px 0; }
432
- .session-access-form { border: 0; padding: 0; }
433
- #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; }
434
435
  .session-access-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); align-items: start; gap: 16px; }
435
436
  .session-access-rule { min-width: 0; border: 1px solid var(--line); border-radius: 10px; padding: 20px; }
436
437
  .session-access-rule h4 { margin: 0; font-size: 16px; }
@@ -70,6 +70,7 @@ function expireAdminSession() {
70
70
  if (notice) notice.hidden = false;
71
71
  window.NetizenIdentity?.expire();
72
72
  window.NetizenSessionAccess?.expire();
73
+ window.NetizenProgressCard?.expire();
73
74
  window.NetizenWorktrees?.expire();
74
75
  setStatus("登录已过期,请重新登录。", true);
75
76
  }
@@ -2349,6 +2350,7 @@ async function loadSessionSettingsOptions() {
2349
2350
  const serial = ++editor.optionsSerial;
2350
2351
  editor.loading = true;
2351
2352
  renderSessionSettingsEditor();
2353
+ const progressRead = editor.settings.progress_card_settings === null ? window.NetizenProgressCard.read() : null;
2352
2354
  try {
2353
2355
  const data = await api("/api/v1/sessions/options");
2354
2356
  if (sessionSettingsEditor !== editor || serial !== editor.optionsSerial) return;
@@ -2359,6 +2361,7 @@ async function loadSessionSettingsOptions() {
2359
2361
  editor.models = [];
2360
2362
  editor.catalogError = `${error.message} 模型目录暂未读取,原选择保持不变。`;
2361
2363
  } finally {
2364
+ await progressRead;
2362
2365
  if (sessionSettingsEditor === editor && serial === editor.optionsSerial) {
2363
2366
  editor.loading = false;
2364
2367
  renderSessionSettingsEditor();
@@ -2768,6 +2771,7 @@ async function loadDefaultOptions(resolveChat = false) {
2768
2771
  editor.workspaceOptions = null;
2769
2772
  editor.workspaceError = "";
2770
2773
  renderDefaultSettings();
2774
+ let progressRead = editor.settings.progress_card_settings === null ? window.NetizenProgressCard.read() : null;
2771
2775
  try {
2772
2776
  let view = null;
2773
2777
  if (resolveChat && chatId) {
@@ -2810,6 +2814,11 @@ async function loadDefaultOptions(resolveChat = false) {
2810
2814
  editor.error = `${error.message} 请重新打开表单后操作。`;
2811
2815
  if (resolveChat) editor.action = null;
2812
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;
2813
2822
  if (defaultsEditor === editor && serial === editor.serial) {
2814
2823
  // Initial catalog/defaults hydration is not a user edit. Later target
2815
2824
  // resolution must not erase the original values used for discard checks.
@@ -3088,7 +3097,8 @@ function sessionSettingsFingerprint(settings) {
3088
3097
  return JSON.stringify([turn ? [turn.model_id, turn.effort_id, turn.service_tier_id] : null,
3089
3098
  settings.message_context_mode, settings.reaction_pulse_enabled,
3090
3099
  settings.progress_card_enabled, settings.completion_mention_enabled,
3091
- progressCardFields.map((field) => progressCardSettings(settings)[field.replaceAll("-", "_")])]);
3100
+ settings.progress_card_settings === null ? null
3101
+ : progressCardFields.map((field) => settings.progress_card_settings[field.replaceAll("-", "_")])]);
3092
3102
  }
3093
3103
 
3094
3104
  function confirmDiscardChanges(changed) {
@@ -3098,6 +3108,7 @@ function confirmDiscardChanges(changed) {
3098
3108
  function hasUnsavedEditors() {
3099
3109
  return projectEditorHasChanges() || sessionSettingsHasChanges() || defaultEditorHasChanges() || scheduleEditorHasChanges()
3100
3110
  || Boolean(pendingScheduleDraft?.dirty) || Boolean(window.NetizenSessionAccess?.dirty())
3111
+ || Boolean(window.NetizenProgressCard?.dirty())
3101
3112
  || Boolean(window.NetizenWorktrees?.dirty());
3102
3113
  }
3103
3114
 
@@ -3105,7 +3116,8 @@ function hasUnsavedEditors() {
3105
3116
  // existing fail-closed boundary; this helper never cancels or repeats a POST.
3106
3117
  function confirmDiscardEditors() {
3107
3118
  if (projectEditor?.saving || sessionSettingsEditor?.saving || defaultsBusy || (scheduleEditor && scheduleInput("fields").disabled)
3108
- || window.NetizenSessionAccess?.busy() || window.NetizenWorktrees?.busy()) return false;
3119
+ || window.NetizenSessionAccess?.busy() || window.NetizenProgressCard?.busy()
3120
+ || window.NetizenWorktrees?.busy()) return false;
3109
3121
  if (!confirmDiscardChanges(hasUnsavedEditors())) return false;
3110
3122
  closeProjectEditor({ confirmed: true });
3111
3123
  closeSessionSettingsEditor({ confirmed: true });
@@ -3113,6 +3125,7 @@ function confirmDiscardEditors() {
3113
3125
  if (scheduleEditor) closeScheduleEditor({ confirmed: true });
3114
3126
  cancelScheduleSelection({ confirmed: true });
3115
3127
  window.NetizenSessionAccess?.discard();
3128
+ window.NetizenProgressCard?.discard();
3116
3129
  window.NetizenWorktrees?.discard();
3117
3130
  return true;
3118
3131
  }
@@ -3379,6 +3392,8 @@ function renderScheduleTarget() {
3379
3392
  scheduleInput("chat-target").hidden = binding;
3380
3393
  if (binding) { scheduleInput("chat").required = false; chatTarget("schedule").close(); }
3381
3394
  scheduleInput("session-settings").hidden = binding;
3395
+ scheduleInput("auto-archive-field").hidden = binding;
3396
+ scheduleInput("auto-archive").disabled = binding;
3382
3397
  renderScheduleWorkspace();
3383
3398
  const target = editor.targetSession;
3384
3399
  const selected = editor.plan?.target_binding_id || target?.bindingId || "";
@@ -3432,6 +3447,7 @@ function scheduleEditorValues() {
3432
3447
  .filter((input) => input.checked).map((input) => input.value);
3433
3448
  return { name: scheduleInput("name").value, instructions: scheduleInput("instructions").value,
3434
3449
  enabled: scheduleInput("enabled").checked, targetKind: scheduleTargetKind(),
3450
+ autoArchive: binding ? null : scheduleInput("auto-archive").checked,
3435
3451
  binding: binding ? scheduleInput("binding").value : "",
3436
3452
  project: binding ? "" : scheduleInput("project").value,
3437
3453
  chat: binding ? "" : scheduleInput("chat").value, rule,
@@ -3445,7 +3461,7 @@ function scheduleEditorHasChanges() {
3445
3461
  }
3446
3462
 
3447
3463
  function captureScheduleDraft() {
3448
- const fields = ["name", "instructions", "enabled", "kind", "timezone", "time", "at", "offset", "end-at", "end-offset", "every"];
3464
+ const fields = ["name", "instructions", "enabled", "auto-archive", "kind", "timezone", "time", "at", "offset", "end-at", "end-offset", "every"];
3449
3465
  return {
3450
3466
  discardBase: structuredClone(scheduleEditor.discardBase), dirty: scheduleEditorHasChanges(),
3451
3467
  fields: fields.map((id) => ({ id, value: scheduleInput(id).value, checked: scheduleInput(id).checked })),
@@ -3550,22 +3566,19 @@ function resetScheduleEndOffset() {
3550
3566
 
3551
3567
  function defaultScheduleSessionSettings() {
3552
3568
  return { turn_settings: null, reaction_pulse_enabled: false, progress_card_enabled: true, completion_mention_enabled: true,
3553
- message_context_mode: "current-only", progress_card_settings: {
3554
- show_commentary: true, show_operations: true, show_plan: true,
3555
- commentary_limit: 4, operation_limit: 8, commentary_max_chars: 160, operation_max_chars: 120,
3556
- organization: "content",
3557
- } };
3569
+ message_context_mode: "current-only", progress_card_settings: null };
3558
3570
  }
3559
3571
 
3560
3572
  const progressCardFields = ["show-commentary", "show-operations", "show-plan", "commentary-limit",
3561
3573
  "operation-limit", "commentary-max-chars", "operation-max-chars", "organization"];
3562
3574
 
3563
3575
  function progressCardSettings(settings) {
3564
- return settings.progress_card_settings || defaultScheduleSessionSettings().progress_card_settings;
3576
+ return settings.progress_card_settings || window.NetizenProgressCard?.current();
3565
3577
  }
3566
3578
 
3567
3579
  function progressCardSettingsError(settings) {
3568
3580
  const progress = progressCardSettings(settings);
3581
+ if (!progress) return "";
3569
3582
  for (const [key, label, maximum] of [
3570
3583
  ["commentary_limit", "进展条数", 8], ["operation_limit", "操作条数", 16],
3571
3584
  ["commentary_max_chars", "进展摘要长度", 320], ["operation_max_chars", "操作摘要长度", 240],
@@ -3582,19 +3595,21 @@ function renderProgressCardSettings(input, settings) {
3582
3595
  if (!container) return;
3583
3596
  const progress = progressCardSettings(settings);
3584
3597
  container.hidden = !settings.progress_card_enabled;
3598
+ const source = input("progress-source");
3599
+ if (source) source.hidden = settings.progress_card_settings !== null;
3585
3600
  for (const node of container.querySelectorAll("input, select")) {
3586
3601
  const field = node.id.split("-progress-").at(-1);
3587
3602
  const key = field.replaceAll("-", "_");
3588
- if (field.startsWith("show-")) node.checked = progress[key];
3589
- else node.value = progress[key];
3590
- node.disabled = !settings.progress_card_enabled
3603
+ if (field.startsWith("show-")) node.checked = progress?.[key] || false;
3604
+ else node.value = progress?.[key] ?? "";
3605
+ node.disabled = !progress || !settings.progress_card_enabled
3591
3606
  || (field.startsWith("commentary-") && !progress.show_commentary)
3592
3607
  || (field.startsWith("operation-") && !progress.show_operations);
3593
3608
  }
3594
- for (const [kind, shown] of [["commentary", progress.show_commentary], ["operation", progress.show_operations]]) {
3609
+ for (const [kind, shown] of [["commentary", progress?.show_commentary], ["operation", progress?.show_operations]]) {
3595
3610
  input(`progress-${kind}-capacity`).hidden = !shown;
3596
3611
  }
3597
- const error = progressCardSettingsError(settings);
3612
+ const error = progress ? progressCardSettingsError(settings) : "读取全局配置后可编辑过程卡细项。";
3598
3613
  const note = input("progress-error");
3599
3614
  note.textContent = error;
3600
3615
  note.hidden = !error;
@@ -3662,11 +3677,13 @@ function changeSessionSettingsField(input, settings, models, field, baseTurn = n
3662
3677
  else if (field === "progress") settings.progress_card_enabled = input("progress").checked;
3663
3678
  else if (field === "completion-mention") settings.completion_mention_enabled = input("completion-mention").checked;
3664
3679
  else if (field.startsWith("progress-") && progressCardFields.includes(field.slice(9))) {
3680
+ const progress = progressCardSettings(settings);
3681
+ if (!progress) return;
3665
3682
  const key = field.slice(9).replaceAll("-", "_");
3666
3683
  const node = input(field);
3667
3684
  const value = key.startsWith("show_") ? node.checked
3668
3685
  : key === "organization" || node.value === "" ? node.value : Number(node.value);
3669
- settings.progress_card_settings = { ...progressCardSettings(settings), [key]: value };
3686
+ settings.progress_card_settings = { ...progress, [key]: value };
3670
3687
  }
3671
3688
  }
3672
3689
 
@@ -3949,6 +3966,7 @@ function openScheduleEditor(plan = null, source = {}) {
3949
3966
  chatTarget("schedule", changeScheduleChat).set({ chatId: plan?.chat_id, chat: plan?.chat });
3950
3967
  scheduleInput("instructions").value = plan?.instructions || "";
3951
3968
  scheduleInput("enabled").checked = plan?.enabled ?? true;
3969
+ scheduleInput("auto-archive").checked = plan?.auto_archive ?? false;
3952
3970
  scheduleInput("timezone").value = rule.timezone;
3953
3971
  scheduleInput("kind").value = rule.kind;
3954
3972
  scheduleInput("time").value = ["daily", "weekly"].includes(rule.kind) ? rule.at : "09:00";
@@ -4037,6 +4055,7 @@ async function saveSchedule(event) {
4037
4055
  } else {
4038
4056
  definition.project = scheduleInput("project").value.trim();
4039
4057
  definition.chat_id = scheduleInput("chat").value.trim();
4058
+ definition.auto_archive = scheduleInput("auto-archive").checked;
4040
4059
  if (scheduleWorkspaceChanged()) {
4041
4060
  definition.workspace = structuredClone(editor.workspace);
4042
4061
  if (editor.workspaceOptions?.projectAlias === definition.project) {
@@ -4310,6 +4329,9 @@ async function showSchedule(planId, returnFocus = document.activeElement) {
4310
4329
  : scheduleSessionSummary(data.plan.session_settings || defaultScheduleSessionSettings());
4311
4330
  scheduleInput("detail-settings").textContent += ` · ${data.plan.target_kind === "binding"
4312
4331
  ? scheduleInheritedWorkspace(data.plan) : workspacePolicySummary(data.plan.workspace, { scheduled: true })}`;
4332
+ if (data.plan.target_kind !== "binding") {
4333
+ scheduleInput("detail-settings").textContent += ` · 成功后自动归档:${data.plan.auto_archive ? "开启" : "关闭"}`;
4334
+ }
4313
4335
  scheduleInput("detail-rule").textContent = scheduleRuleLabel(data.plan.schedule);
4314
4336
  renderSchedulePreview("#schedule-detail-preview", data.preview);
4315
4337
  scheduleInput("detail-note").textContent = "";
@@ -4604,8 +4626,9 @@ async function refresh(tab, cursor = undefined) {
4604
4626
  if (tab === "schedules" && await loadSchedules(cursor) === false) return false;
4605
4627
  if (tab === "defaults") await loadDefaults();
4606
4628
  if (tab === "system") {
4607
- const results = await Promise.all([window.NetizenIdentity.loadUsers(), window.NetizenSessionAccess?.load()]);
4608
- if (results[1] === false) return false;
4629
+ const results = await Promise.all([window.NetizenIdentity.loadUsers(), window.NetizenSessionAccess?.load(),
4630
+ window.NetizenProgressCard?.load()]);
4631
+ if (results.includes(false)) return false;
4609
4632
  }
4610
4633
  if (state.authExpired) return false;
4611
4634
  if (isCurrentView()) setStatus("已更新。");
@@ -4643,7 +4666,7 @@ function selectSystemTab(name) {
4643
4666
  if (state.authExpired || state.systemTab === name) return;
4644
4667
  window.NetizenSelect?.close();
4645
4668
  if (state.systemTab === "admins") window.NetizenIdentity?.suspendSelector();
4646
- else window.NetizenSessionAccess?.closePickers();
4669
+ else if (state.systemTab === "access") window.NetizenSessionAccess?.closePickers();
4647
4670
  state.systemTab = name;
4648
4671
  for (const button of document.querySelectorAll("[data-system-tab]")) {
4649
4672
  const selected = button.dataset.systemTab === name;
@@ -4710,8 +4733,10 @@ for (const tab of document.querySelectorAll("[data-system-tab]")) {
4710
4733
  tab.addEventListener("keydown", (event) => {
4711
4734
  if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
4712
4735
  event.preventDefault();
4713
- const name = event.key === "Home" ? "admins" : event.key === "End" ? "access"
4714
- : tab.dataset.systemTab === "admins" ? "access" : "admins";
4736
+ const names = [...document.querySelectorAll("[data-system-tab]")].map((button) => button.dataset.systemTab);
4737
+ const index = names.indexOf(tab.dataset.systemTab);
4738
+ const name = event.key === "Home" ? names[0] : event.key === "End" ? names.at(-1)
4739
+ : names[(index + (event.key === "ArrowRight" ? 1 : names.length - 1)) % names.length];
4715
4740
  selectSystemTab(name);
4716
4741
  document.querySelector(`#system-tab-${name}`).focus();
4717
4742
  });
@@ -9,6 +9,7 @@
9
9
  <script src="/static/chat-picker.js" defer></script>
10
10
  <script src="/static/identity.js" defer></script>
11
11
  <script src="/static/session-access.js" defer></script>
12
+ <script src="/static/progress-card.js" defer></script>
12
13
  <script src="/static/worktrees.js" defer></script>
13
14
  <script src="/static/admin.js" defer></script>
14
15
  </head>
@@ -222,6 +223,7 @@
222
223
  <label><input id="session-settings-completion-mention" type="checkbox">结束时 @ 提醒</label>
223
224
  </div>
224
225
  <div id="session-settings-progress-settings" class="progress-settings" hidden>
226
+ <p id="session-settings-progress-source" class="session-settings-help" hidden>当前未单独配置,显示全局过程卡配置。修改细项后将保存为独立配置。</p>
225
227
  <label>过程卡组织方式<select id="session-settings-progress-organization"><option value="content">按内容</option><option value="time">按时间</option></select></label>
226
228
  <p class="session-settings-help">按时间交错显示进展与操作,任务清单固定在底部。执行状态和异常提示始终保留。</p>
227
229
  <div class="defaults-feedback"><label><input id="session-settings-progress-show-commentary" type="checkbox">最近进展</label></div>
@@ -305,6 +307,7 @@
305
307
  <label><input id="defaults-completion-mention" type="checkbox">结束时 @ 提醒</label>
306
308
  </div>
307
309
  <div id="defaults-progress-settings" class="progress-settings" hidden>
310
+ <p id="defaults-progress-source" class="session-settings-help" hidden>当前未单独配置,显示全局过程卡配置。修改细项后将保存为独立配置。</p>
308
311
  <label>过程卡组织方式<select id="defaults-progress-organization"><option value="content">按内容</option><option value="time">按时间</option></select></label>
309
312
  <p class="session-settings-help">按时间交错显示进展与操作,任务清单固定在底部。执行状态和异常提示始终保留。</p>
310
313
  <div class="defaults-feedback"><label><input id="defaults-progress-show-commentary" type="checkbox">最近进展</label></div>
@@ -463,6 +466,10 @@
463
466
  </div>
464
467
  </div>
465
468
  </section>
469
+ <section id="schedule-auto-archive-field" class="schedule-editor-section">
470
+ <label class="schedule-checkbox"><input id="schedule-auto-archive" type="checkbox" aria-describedby="schedule-auto-archive-help">成功后自动归档</label>
471
+ <p id="schedule-auto-archive-help" class="schedule-field-help">首轮正常结束且结果送达后归档,恢复使用 /sessions archived。</p>
472
+ </section>
466
473
  <details id="schedule-session-settings" class="schedule-editor-section schedule-session-settings" open>
467
474
  <summary>会话配置<span id="schedule-session-summary">继承 Codex · 当前消息</span></summary>
468
475
  <div class="schedule-section-fields">
@@ -488,6 +495,7 @@
488
495
  <div class="session-tabs system-tabs" role="tablist" aria-label="系统管理">
489
496
  <button id="system-tab-admins" class="session-tab" role="tab" data-system-tab="admins" aria-selected="true" aria-controls="system-admins" tabindex="0">管理员</button>
490
497
  <button id="system-tab-access" class="session-tab" role="tab" data-system-tab="access" aria-selected="false" aria-controls="system-access" tabindex="-1">跨会话工具权限<span id="session-access-unsaved" class="tab-unsaved" hidden>未保存</span></button>
498
+ <button id="system-tab-progress" class="session-tab" role="tab" data-system-tab="progress" aria-selected="false" aria-controls="system-progress" tabindex="-1">过程卡<span id="global-card-unsaved" class="tab-unsaved" hidden>未保存</span></button>
491
499
  </div>
492
500
  <div id="system-admins" class="system-tab-panel" role="tabpanel" aria-labelledby="system-tab-admins">
493
501
  <div class="section-head"><h3>管理员</h3><button id="admin-user-add" class="action" aria-expanded="false" aria-controls="admin-user-panel" disabled>添加管理员</button></div>
@@ -541,6 +549,34 @@
541
549
  </div>
542
550
  </form>
543
551
  </div>
552
+ <div id="system-progress" class="system-tab-panel" role="tabpanel" aria-labelledby="system-tab-progress" hidden>
553
+ <h3>全局过程卡配置</h3>
554
+ <p class="defaults-help">仅在本实例的会话未单独配置过程卡时使用,不覆盖会话及聊天默认配置中已有的过程卡设置。</p>
555
+ <form id="global-card-form" class="card-form global-card-form">
556
+ <fieldset id="global-card-fields" disabled>
557
+ <div id="global-card-progress-settings" class="progress-settings" hidden>
558
+ <label>过程卡组织方式<select id="global-card-progress-organization"><option value="content">按内容</option><option value="time">按时间</option></select></label>
559
+ <p class="session-settings-help">按时间交错显示进展与操作,任务清单固定在底部。执行状态和异常提示始终保留。</p>
560
+ <div class="defaults-feedback"><label><input id="global-card-progress-show-commentary" type="checkbox">最近进展</label></div>
561
+ <div id="global-card-progress-commentary-capacity" class="schedule-form-grid">
562
+ <label>进展条数(1–8)<input id="global-card-progress-commentary-limit" type="number" min="1" max="8" step="1" required></label>
563
+ <label>进展摘要长度(1–320 字符)<input id="global-card-progress-commentary-max-chars" type="number" min="1" max="320" step="1" required></label>
564
+ </div>
565
+ <div class="defaults-feedback"><label><input id="global-card-progress-show-operations" type="checkbox">最近操作</label></div>
566
+ <div id="global-card-progress-operation-capacity" class="schedule-form-grid">
567
+ <label>操作条数(1–16)<input id="global-card-progress-operation-limit" type="number" min="1" max="16" step="1" required></label>
568
+ <label>操作摘要长度(1–240 字符)<input id="global-card-progress-operation-max-chars" type="number" min="1" max="240" step="1" required></label>
569
+ </div>
570
+ <div class="defaults-feedback"><label><input id="global-card-progress-show-plan" type="checkbox">任务清单</label></div>
571
+ </div>
572
+ <p id="global-card-progress-error" class="progress-settings-error" role="status" hidden></p>
573
+ </fieldset>
574
+ <div class="session-access-footer">
575
+ <p id="global-card-note" class="defaults-field-help" role="status"></p>
576
+ <div class="actions"><button id="global-card-cancel" class="refresh" type="button" disabled>放弃修改</button><button id="global-card-save" class="action" type="submit" disabled>保存配置</button></div>
577
+ </div>
578
+ </form>
579
+ </div>
544
580
  </section>
545
581
  <section id="updates" class="panel" aria-labelledby="updates-heading" hidden>
546
582
  <div class="section-head">
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+
3
+ window.NetizenProgressCard = (() => {
4
+ const node = (name) => document.querySelector(`#global-card-${name}`);
5
+ let snapshot = null;
6
+ let draft = null;
7
+ let preview = null;
8
+ let loading = false;
9
+ let pending = false;
10
+ let expired = false;
11
+ let generation = 0;
12
+ let previewGeneration = 0;
13
+
14
+ const wrapper = () => ({ progress_card_enabled: true, progress_card_settings: draft });
15
+ const dirty = () => Boolean(snapshot) && JSON.stringify(draft) !== JSON.stringify(snapshot.settings);
16
+
17
+ function render() {
18
+ if (draft) renderProgressCardSettings(node, wrapper());
19
+ node("fields").disabled = expired || loading || pending || !snapshot?.editable;
20
+ node("save").disabled = expired || loading || pending || !snapshot?.actions.save || !dirty()
21
+ || Boolean(progressCardSettingsError(wrapper()));
22
+ node("cancel").disabled = expired || loading || pending || !dirty();
23
+ node("unsaved").hidden = !dirty();
24
+ node("save").textContent = pending ? "保存中…" : "保存配置";
25
+ node("form").setAttribute("aria-busy", String(loading || pending));
26
+ }
27
+
28
+ function discard() {
29
+ generation += 1;
30
+ loading = false;
31
+ draft = snapshot ? structuredClone(snapshot.settings) : null;
32
+ render();
33
+ }
34
+
35
+ async function read() {
36
+ const version = ++previewGeneration;
37
+ preview = null;
38
+ try {
39
+ const value = await api("/api/v1/progress-card/settings");
40
+ if (!expired && version === previewGeneration) preview = value.settings;
41
+ return value.settings;
42
+ } catch (error) {
43
+ if (!expired && version === previewGeneration) setStatus(`全局过程卡配置读取失败:${error.message}`, true);
44
+ return null;
45
+ }
46
+ }
47
+
48
+ async function load() {
49
+ if (expired || pending || !confirmDiscardChanges(dirty())) return false;
50
+ const version = ++generation;
51
+ loading = true;
52
+ render();
53
+ try {
54
+ const value = await api("/api/v1/progress-card/settings");
55
+ if (expired || version !== generation) return false;
56
+ snapshot = value;
57
+ previewGeneration += 1;
58
+ preview = value.settings;
59
+ draft = structuredClone(value.settings);
60
+ node("note").textContent = value.editable
61
+ ? "保存后,从后续任务开始使用;正在执行的卡片保持不变。" : "实例配置不可编辑。";
62
+ return true;
63
+ } catch (error) {
64
+ if (!expired && version === generation) {
65
+ if (snapshot) snapshot.actions.save = null;
66
+ node("note").textContent = "读取失败,请刷新后重试。";
67
+ }
68
+ throw error;
69
+ } finally {
70
+ if (version === generation) { loading = false; render(); }
71
+ }
72
+ }
73
+
74
+ async function save(event) {
75
+ event.preventDefault();
76
+ if (expired || loading || pending || !snapshot?.actions.save || !dirty()
77
+ || progressCardSettingsError(wrapper())) return;
78
+ const body = actionPayload(snapshot.actions.save, { settings: draft });
79
+ snapshot.actions.save = null;
80
+ pending = true;
81
+ generation += 1;
82
+ render();
83
+ try {
84
+ const value = await api("/api/v1/progress-card/settings", {
85
+ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
86
+ });
87
+ if (expired) return;
88
+ snapshot = value;
89
+ previewGeneration += 1;
90
+ preview = value.settings;
91
+ draft = structuredClone(value.settings);
92
+ node("note").textContent = "全局过程卡配置已保存,从后续任务开始使用。";
93
+ setStatus("全局过程卡配置已保存。");
94
+ } catch (error) {
95
+ if (!expired) {
96
+ node("note").textContent = "请刷新核对当前配置后再保存;本次修改未自动重试。";
97
+ setStatus(error.message, true);
98
+ }
99
+ } finally {
100
+ pending = false;
101
+ render();
102
+ }
103
+ }
104
+
105
+ for (const field of node("fields").querySelectorAll("input, select")) {
106
+ field.addEventListener("change", () => {
107
+ if (expired || loading || pending || !snapshot?.editable) return;
108
+ const settings = wrapper();
109
+ changeSessionSettingsField(node, settings, [], field.id.slice("global-card-".length));
110
+ draft = settings.progress_card_settings;
111
+ render();
112
+ });
113
+ }
114
+ node("form").addEventListener("submit", save);
115
+ node("cancel").addEventListener("click", discard);
116
+ return { load, read, current: () => preview, dirty, discard, busy: () => pending,
117
+ expire() { expired = true; generation += 1; render(); } };
118
+ })();
@@ -167,6 +167,8 @@ from ..sessions.policy import (
167
167
  SessionAccessPolicy, SessionPolicyConflict, SessionPolicyError, SessionPolicyService,
168
168
  )
169
169
  from ..worktrees.errors import WorktreeError
170
+ from ..progress_card_config import ProgressCardConfigConflict, ProgressCardConfigError
171
+ from ..progress_card_settings import ProgressCardSettings
170
172
 
171
173
 
172
174
  logger = logging.getLogger(__name__)
@@ -255,6 +257,7 @@ class AdminActionPreconditions:
255
257
  session_policy_revision: ExpectedValue[str] = ExpectedValue.dont_check()
256
258
  cascade_delete: ExpectedValue[bool] = ExpectedValue.dont_check()
257
259
  worktree_settings_revision: ExpectedValue[str] = ExpectedValue.dont_check()
260
+ progress_card_settings_revision: ExpectedValue[str] = ExpectedValue.dont_check()
258
261
 
259
262
 
260
263
  @dataclass(frozen=True, slots=True)
@@ -577,6 +580,8 @@ class AdminWebApplication:
577
580
  return Response(200, ((b"Content-Type", b"text/javascript; charset=utf-8"),), self._assets["session-access.js"])
578
581
  if route == ("GET", "/static/worktrees.js"):
579
582
  return Response(200, ((b"Content-Type", b"text/javascript; charset=utf-8"),), self._assets["worktrees.js"])
583
+ if route == ("GET", "/static/progress-card.js"):
584
+ return Response(200, ((b"Content-Type", b"text/javascript; charset=utf-8"),), self._assets["progress-card.js"])
580
585
  if route == ("GET", "/api/v1/identity"):
581
586
  _require_query_keys(context.query, set())
582
587
  session = self._authenticate(context)
@@ -608,6 +613,8 @@ class AdminWebApplication:
608
613
  return await self._worktree_branches(context)
609
614
  if route == ("GET", "/api/v1/worktrees/settings"):
610
615
  return self._worktree_settings(context)
616
+ if route == ("GET", "/api/v1/progress-card/settings"):
617
+ return self._progress_card_settings(context)
611
618
  if route == ("GET", "/api/v1/chats"):
612
619
  return await self._chats(context)
613
620
  if route == ("GET", "/api/v1/chat-labels"):
@@ -656,6 +663,7 @@ class AdminWebApplication:
656
663
  mutations: dict[str, Callable[[_RequestContext], Awaitable[Response]]] = {
657
664
  "/api/v1/worktrees/create": self._worktree_create,
658
665
  "/api/v1/worktrees/settings": self._worktree_save_settings,
666
+ "/api/v1/progress-card/settings": self._progress_card_save_settings,
659
667
  "/api/v1/worktrees/delete-preview": self._worktree_delete_preview,
660
668
  "/api/v1/worktrees/delete": self._worktree_delete,
661
669
  "/api/v1/session-tool-access/save": self._save_session_tool_access,
@@ -978,7 +986,7 @@ class AdminWebApplication:
978
986
  )
979
987
  definition = payload.get("definition", {})
980
988
  if not isinstance(definition, dict) or set(definition) - {
981
- "name", "instructions", "project", "expected_project_revision", "chat_id", "schedule", "enabled", "session_settings", "workspace", "target_kind", "target_binding_id",
989
+ "name", "instructions", "project", "expected_project_revision", "chat_id", "schedule", "enabled", "session_settings", "workspace", "target_kind", "target_binding_id", "auto_archive",
982
990
  }:
983
991
  raise AdminWebError(400, "invalid_input", "定时任务定义字段无效。")
984
992
  request: dict[str, object] = {
@@ -1104,6 +1112,46 @@ class AdminWebApplication:
1104
1112
  )
1105
1113
  return _json_response(200, {**result, "requestId": context.request.request_id})
1106
1114
 
1115
+ def _progress_card_settings(self, context: _RequestContext) -> Response:
1116
+ _require_query_keys(context.query, set())
1117
+ view = self._management.progress_card_settings()
1118
+ actions = {}
1119
+ if view["editable"]:
1120
+ actions["save"] = self._grant(
1121
+ context, "progress-card.settings", AdminActionTarget("progress-card-settings", "instance"),
1122
+ _empty_preconditions(progress_card_settings_revision=ExpectedValue.expect(view["revision"])),
1123
+ )
1124
+ return _json_response(200, {**view, "actions": actions, "requestId": context.request.request_id})
1125
+
1126
+ async def _progress_card_save_settings(self, context: _RequestContext) -> Response:
1127
+ _require_query_keys(context.query, set())
1128
+ payload, grant = self._redeem(
1129
+ context, "progress-card.settings", expected_resource="progress-card-settings",
1130
+ allowed_extra={"settings"},
1131
+ )
1132
+ if grant.target.target_id != "instance":
1133
+ raise StaleActionGrant()
1134
+ try:
1135
+ settings = ProgressCardSettings.from_dict(payload["settings"])
1136
+ except ValueError as error:
1137
+ raise AdminWebError(400, "invalid_progress_card_settings", str(error)) from None
1138
+ revision = _expected_value(
1139
+ _grant_preconditions(grant).progress_card_settings_revision, "progress card settings revision",
1140
+ )
1141
+
1142
+ async def apply() -> dict[str, Any]:
1143
+ try:
1144
+ return await self._management.update_progress_card_settings(settings, expected_revision=revision)
1145
+ except ProgressCardConfigConflict:
1146
+ raise AdminWebError(409, "stale_progress_card_settings", "过程卡配置已被修改,请刷新后再保存。") from None
1147
+ except AdminPortConfigurationError:
1148
+ raise AdminWebError(409, "configuration_changed", "实例配置文件已变化;请重启实例加载配置后再保存。") from None
1149
+ except (ProgressCardConfigError, OSError):
1150
+ raise AdminWebError(503, "configuration_unavailable", "无法保存过程卡配置,当前配置未更新。") from None
1151
+
1152
+ await self._mutation(context, "progress-card.settings", "instance", apply())
1153
+ return self._progress_card_settings(context)
1154
+
1107
1155
  async def _worktree_delete_preview(self, context: _RequestContext) -> Response:
1108
1156
  _require_query_keys(context.query, set())
1109
1157
  payload, grant = self._redeem(
@@ -2164,7 +2212,7 @@ class AdminWebApplication:
2164
2212
 
2165
2213
  async def apply() -> None:
2166
2214
  try:
2167
- service.update(policy, expected_revision=revision)
2215
+ await self._management.update_session_tool_access(service, policy, expected_revision=revision)
2168
2216
  except SessionPolicyConflict:
2169
2217
  raise AdminWebError(409, "stale_session_tool_access", "权限已被修改,请刷新后再保存。") from None
2170
2218
  except AdminPortConfigurationError:
@@ -2713,7 +2761,7 @@ def admin_access_urls(
2713
2761
  def _load_assets() -> dict[str, bytes]:
2714
2762
  root = importlib.resources.files("netizen_cli.admin").joinpath("static")
2715
2763
  assets: dict[str, bytes] = {}
2716
- for name in ("index.html", "admin.css", "admin.js", "chat-picker.js", "chat-picker.css", "login.html", "login.js", "identity.js", "session-access.js", "worktrees.js"):
2764
+ for name in ("index.html", "admin.css", "admin.js", "chat-picker.js", "chat-picker.css", "login.html", "login.js", "identity.js", "session-access.js", "worktrees.js", "progress-card.js"):
2717
2765
  assets[name] = root.joinpath(name).read_bytes()
2718
2766
  return assets
2719
2767
 
@@ -3015,6 +3063,7 @@ def _empty_preconditions(
3015
3063
  session_policy_revision: ExpectedValue[str] | None = None,
3016
3064
  cascade_delete: ExpectedValue[bool] | None = None,
3017
3065
  worktree_settings_revision: ExpectedValue[str] | None = None,
3066
+ progress_card_settings_revision: ExpectedValue[str] | None = None,
3018
3067
  ) -> AdminActionPreconditions:
3019
3068
  return AdminActionPreconditions(
3020
3069
  active_binding_id or ExpectedValue.dont_check(),
@@ -3037,6 +3086,7 @@ def _empty_preconditions(
3037
3086
  session_policy_revision or ExpectedValue.dont_check(),
3038
3087
  cascade_delete or ExpectedValue.dont_check(),
3039
3088
  worktree_settings_revision or ExpectedValue.dont_check(),
3089
+ progress_card_settings_revision or ExpectedValue.dont_check(),
3040
3090
  )
3041
3091
 
3042
3092
 
@@ -40,7 +40,7 @@ from .worktrees.models import WorktreeRecord
40
40
  from .worktrees.store import WorktreeStore, create_schema as create_worktree_schema
41
41
 
42
42
 
43
- SCHEMA_VERSION = 18
43
+ SCHEMA_VERSION = 20
44
44
 
45
45
  # Includes the running query and all submitted queries waiting for the reader.
46
46
  _ADMIN_QUERY_CAPACITY = 32
@@ -467,7 +467,7 @@ def _require_current_schema(connection: sqlite3.Connection) -> None:
467
467
  _require_project_metadata_schema(connection)
468
468
  require_schema(connection)
469
469
  require_defaults_schema(connection)
470
- from .migrations.v18 import validate
470
+ from .migrations.v20 import validate
471
471
  validate(connection)
472
472
 
473
473
 
@@ -1334,10 +1334,10 @@ class BindingStore:
1334
1334
  row["task_reactions_enabled"],
1335
1335
  row["progress_card_enabled"],
1336
1336
  row["completion_mention_enabled"],
1337
- _progress_card_settings(row["progress_card_settings_json"]).to_dict(),
1337
+ _progress_card_settings(row["progress_card_settings_json"]),
1338
1338
  )
1339
1339
  feedback_changed = current_feedback != (
1340
- *feedback_values[:3], task_feedback.progress_card_settings.to_dict(),
1340
+ *feedback_values[:3], task_feedback.progress_card_settings,
1341
1341
  )
1342
1342
  if (
1343
1343
  not settings_changed
@@ -3008,13 +3008,17 @@ def _feedback_values(
3008
3008
  int(feedback.reaction_pulse_enabled),
3009
3009
  int(feedback.progress_card_enabled),
3010
3010
  int(feedback.completion_mention_enabled),
3011
- json.dumps(feedback.progress_card_settings.to_dict(), separators=(",", ":")),
3011
+ json.dumps(
3012
+ feedback.progress_card_settings.to_dict() if feedback.progress_card_settings is not None else None,
3013
+ separators=(",", ":"),
3014
+ ),
3012
3015
  )
3013
3016
 
3014
3017
 
3015
- def _progress_card_settings(raw: str) -> ProgressCardSettings:
3018
+ def _progress_card_settings(raw: str) -> ProgressCardSettings | None:
3016
3019
  try:
3017
- return ProgressCardSettings.from_dict(json.loads(raw))
3020
+ value = json.loads(raw)
3021
+ return ProgressCardSettings.from_dict(value) if value is not None else None
3018
3022
  except (TypeError, ValueError) as error:
3019
3023
  raise RuntimeError("Binding contains invalid progress card settings") from error
3020
3024