netizen-cli 0.11.4__tar.gz → 0.12.0__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 (285) hide show
  1. {netizen_cli-0.11.4/netizen_cli.egg-info → netizen_cli-0.12.0}/PKG-INFO +1 -1
  2. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/README.md +14 -14
  3. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/config.example.yaml +11 -0
  4. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/__init__.py +1 -1
  5. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/port_config.py +6 -4
  6. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/static/admin.css +6 -0
  7. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/static/admin.js +24 -25
  8. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/static/index.html +30 -0
  9. netizen_cli-0.12.0/netizen_cli/admin/static/session-access.js +127 -0
  10. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/web.py +67 -70
  11. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/bindings.py +20 -52
  12. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/callbacks.py +4 -4
  13. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/chat_target.py +30 -7
  14. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/controls.py +21 -21
  15. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/fork.py +7 -5
  16. netizen_cli-0.12.0/netizen_cli/cards/session_ids.py +17 -0
  17. netizen_cli-0.12.0/netizen_cli/cards/status.py +245 -0
  18. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel/topics.py +33 -0
  19. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel_app.py +454 -77
  20. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/codex_runtime.py +236 -30
  21. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/domain.py +25 -0
  22. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/main.py +47 -1
  23. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/management/chat_labels.py +49 -9
  24. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/management/service.py +0 -42
  25. netizen_cli-0.12.0/netizen_cli/mcp_transport.py +231 -0
  26. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/runtime/contracts.py +1 -0
  27. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/runtime/thread_naming.py +35 -5
  28. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/schedules/mcp.py +11 -214
  29. netizen_cli-0.12.0/netizen_cli/sessions/__init__.py +1 -0
  30. netizen_cli-0.12.0/netizen_cli/sessions/errors.py +14 -0
  31. netizen_cli-0.12.0/netizen_cli/sessions/mcp.py +80 -0
  32. netizen_cli-0.12.0/netizen_cli/sessions/policy.py +128 -0
  33. netizen_cli-0.12.0/netizen_cli/sessions/presentation.py +227 -0
  34. netizen_cli-0.12.0/netizen_cli/sessions/schema.py +94 -0
  35. netizen_cli-0.12.0/netizen_cli/sessions/service.py +330 -0
  36. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/settings.py +7 -0
  37. {netizen_cli-0.11.4 → netizen_cli-0.12.0/netizen_cli.egg-info}/PKG-INFO +1 -1
  38. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli.egg-info/SOURCES.txt +30 -0
  39. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/pyproject.toml +1 -1
  40. netizen_cli-0.12.0/skills/netizen-user-guide/SKILL.md +46 -0
  41. netizen_cli-0.12.0/skills/netizen-user-guide/references/admin.md +198 -0
  42. netizen_cli-0.12.0/skills/netizen-user-guide/references/commands.md +41 -0
  43. netizen_cli-0.12.0/skills/netizen-user-guide/references/concepts.md +50 -0
  44. netizen_cli-0.12.0/skills/netizen-user-guide/references/faq.md +131 -0
  45. netizen_cli-0.12.0/skills/netizen-user-guide/references/feedback-and-files.md +119 -0
  46. netizen_cli-0.12.0/skills/netizen-user-guide/references/fork-side-goal.md +96 -0
  47. netizen_cli-0.12.0/skills/netizen-user-guide/references/messages.md +122 -0
  48. netizen_cli-0.12.0/skills/netizen-user-guide/references/native-capabilities.md +64 -0
  49. netizen_cli-0.12.0/skills/netizen-user-guide/references/scheduled-tasks.md +140 -0
  50. netizen_cli-0.12.0/skills/netizen-user-guide/references/session-collaboration.md +58 -0
  51. netizen_cli-0.12.0/skills/netizen-user-guide/references/sessions.md +149 -0
  52. netizen_cli-0.12.0/skills/netizen-user-guide/references/status-and-control.md +84 -0
  53. netizen_cli-0.12.0/skills/netizen-user-guide/references/user-guide.md +60 -0
  54. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_bindings.py +6 -22
  55. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cards.py +3 -3
  56. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_channel_app.py +143 -77
  57. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_chat_target_cards.py +17 -0
  58. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_codex_runtime.py +3 -2
  59. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_deployment.py +7 -1
  60. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_fork_cards.py +61 -0
  61. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_main.py +52 -2
  62. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_persistent_fork_channel.py +25 -3
  63. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_repository_hygiene.py +1 -2
  64. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_runtime_thread_naming.py +79 -1
  65. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_service_launcher.py +3 -1
  66. netizen_cli-0.12.0/tests/test_session_collaboration_app.py +531 -0
  67. netizen_cli-0.12.0/tests/test_session_external_runtime.py +384 -0
  68. netizen_cli-0.12.0/tests/test_session_id_cards.py +137 -0
  69. netizen_cli-0.12.0/tests/test_session_policy.py +128 -0
  70. netizen_cli-0.12.0/tests/test_session_tools.py +500 -0
  71. netizen_cli-0.12.0/tests/test_status_cards.py +186 -0
  72. netizen_cli-0.12.0/tests/test_status_commands.py +308 -0
  73. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_thread_naming.py +51 -4
  74. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_user_guide_skill.py +4 -1
  75. netizen_cli-0.11.4/skills/netizen-user-guide/SKILL.md +0 -41
  76. netizen_cli-0.11.4/skills/netizen-user-guide/references/user-guide.md +0 -1057
  77. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/MANIFEST.in +0 -0
  78. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/_build.py +0 -0
  79. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/__main__.py +0 -0
  80. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/account_rate_limits.py +0 -0
  81. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/__init__.py +0 -0
  82. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/auth.py +0 -0
  83. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/credentials.py +0 -0
  84. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/errors.py +0 -0
  85. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/feishu.py +0 -0
  86. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/login.py +0 -0
  87. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/presentation.py +0 -0
  88. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/queries.py +0 -0
  89. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/static/chat-picker.css +0 -0
  90. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/static/chat-picker.js +0 -0
  91. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/static/identity.js +0 -0
  92. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/static/login.html +0 -0
  93. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/static/login.js +0 -0
  94. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/transport.py +0 -0
  95. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/admin/users.py +0 -0
  96. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/builtin_skills.py +0 -0
  97. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/__init__.py +0 -0
  98. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/defaults.py +0 -0
  99. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/model_info.py +0 -0
  100. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/pagination.py +0 -0
  101. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/questions.py +0 -0
  102. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/reply.py +0 -0
  103. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/scheduled.py +0 -0
  104. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cards/usage.py +0 -0
  105. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel/__init__.py +0 -0
  106. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel/completion_mentions.py +0 -0
  107. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel/input_preparation.py +0 -0
  108. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel/messages.py +0 -0
  109. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel/ports.py +0 -0
  110. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel/question_inputs.py +0 -0
  111. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel/reactions.py +0 -0
  112. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/channel/reply_presenter.py +0 -0
  113. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/chat_avatars.py +0 -0
  114. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/chat_targets.py +0 -0
  115. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cli.py +0 -0
  116. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cli_data.py +0 -0
  117. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cli_packages.py +0 -0
  118. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cli_services.py +0 -0
  119. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cli_setup.py +0 -0
  120. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cli_update.py +0 -0
  121. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cli_update_restore.py +0 -0
  122. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/cli_update_worker.py +0 -0
  123. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/completion_mention.py +0 -0
  124. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/database_migrations.py +0 -0
  125. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/defaults/__init__.py +0 -0
  126. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/defaults/models.py +0 -0
  127. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/defaults/service.py +0 -0
  128. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/defaults/store.py +0 -0
  129. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/deployment/__init__.py +0 -0
  130. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/deployment/restart_worker.py +0 -0
  131. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/deployment/update_executor.py +0 -0
  132. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/deployment/update_protocol.py +0 -0
  133. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/error_messages.py +0 -0
  134. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/experience.py +0 -0
  135. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/feishu_app_onboarding.py +0 -0
  136. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/feishu_app_permissions.py +0 -0
  137. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/feishu_links.py +0 -0
  138. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/git_status.py +0 -0
  139. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/image_inputs.py +0 -0
  140. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/instance.py +0 -0
  141. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/lark_app.py +0 -0
  142. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/management/__init__.py +0 -0
  143. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/management/blocking_io.py +0 -0
  144. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/management/chat_directory.py +0 -0
  145. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/management/coordination.py +0 -0
  146. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/management/updates.py +0 -0
  147. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/markdown_images.py +0 -0
  148. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/message_content.py +0 -0
  149. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/message_history.py +0 -0
  150. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/message_preparation.py +0 -0
  151. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/message_projection.py +0 -0
  152. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/migrations/__init__.py +0 -0
  153. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/migrations/schema.py +0 -0
  154. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/migrations/v14.py +0 -0
  155. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/model_settings.py +0 -0
  156. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/package_resources.py +0 -0
  157. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/pagination.py +0 -0
  158. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/partial_answers.py +0 -0
  159. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/projects.py +0 -0
  160. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/prompt_projection.py +0 -0
  161. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/quoted_context.py +0 -0
  162. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/result_images.py +0 -0
  163. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/runtime/__init__.py +0 -0
  164. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/runtime/name_writes.py +0 -0
  165. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/schedules/__init__.py +0 -0
  166. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/schedules/models.py +0 -0
  167. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/schedules/scheduler.py +0 -0
  168. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/schedules/service.py +0 -0
  169. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/schedules/store.py +0 -0
  170. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/sdk_gap_adapter.py +0 -0
  171. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/service_launcher.py +0 -0
  172. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/session_settings.py +0 -0
  173. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/skill_references.py +0 -0
  174. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/task_text.py +0 -0
  175. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/terminal_cleanup.py +0 -0
  176. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/turn_activity.py +0 -0
  177. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/turn_files.py +0 -0
  178. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/turn_patch_children.py +0 -0
  179. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/turn_plan_observer.py +0 -0
  180. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli/user_questions.py +0 -0
  181. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli.egg-info/dependency_links.txt +0 -0
  182. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli.egg-info/entry_points.txt +0 -0
  183. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli.egg-info/requires.txt +0 -0
  184. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/netizen_cli.egg-info/top_level.txt +0 -0
  185. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/setup.cfg +0 -0
  186. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/skills/netizen-lark/SKILL.md +0 -0
  187. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_account_rate_limits.py +0 -0
  188. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_admin_update_executor_probe.py +0 -0
  189. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_binding_scheduled_channel.py +0 -0
  190. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_channel_fixtures.py +0 -0
  191. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_channel_input_preparation.py +0 -0
  192. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_chat_avatars.py +0 -0
  193. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_check_sdk.py +0 -0
  194. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cli.py +0 -0
  195. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cli_data.py +0 -0
  196. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cli_distribution.py +0 -0
  197. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cli_integration.py +0 -0
  198. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cli_packages.py +0 -0
  199. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cli_packaging.py +0 -0
  200. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cli_services.py +0 -0
  201. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cli_setup.py +0 -0
  202. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cli_update.py +0 -0
  203. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_codex_sdk_capabilities.py +0 -0
  204. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_codex_sdk_upgrade_0161.py +0 -0
  205. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_completion_mention_cards.py +0 -0
  206. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_completion_mentions.py +0 -0
  207. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_completion_topic_delivery.py +0 -0
  208. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_cron_chat_targets.py +0 -0
  209. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_database_migrations.py +0 -0
  210. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_defaults_cards.py +0 -0
  211. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_defaults_channel.py +0 -0
  212. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_defaults_runtime.py +0 -0
  213. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_documentation_links.py +0 -0
  214. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_domain_experience.py +0 -0
  215. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_error_messages.py +0 -0
  216. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_feishu_app_onboarding.py +0 -0
  217. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_feishu_app_permissions.py +0 -0
  218. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_file_pagination.py +0 -0
  219. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_git_status.py +0 -0
  220. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_image_inputs.py +0 -0
  221. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_lark_app.py +0 -0
  222. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_lark_sdk_contract.py +0 -0
  223. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_markdown_images.py +0 -0
  224. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_message_content.py +0 -0
  225. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_message_history.py +0 -0
  226. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_message_material_inputs.py +0 -0
  227. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_message_preparation.py +0 -0
  228. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_migration_schema_validation.py +0 -0
  229. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_model_info_cards.py +0 -0
  230. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_model_settings.py +0 -0
  231. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_name_writes.py +0 -0
  232. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_pagination.py +0 -0
  233. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_partial_answer_delivery.py +0 -0
  234. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_partial_answers.py +0 -0
  235. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_persistent_fork_bindings.py +0 -0
  236. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_persistent_fork_runtime.py +0 -0
  237. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_child_files.py +0 -0
  238. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_feishu_completion_mention.py +0 -0
  239. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_feishu_partial_answers.py +0 -0
  240. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_feishu_turn_file_card.py +0 -0
  241. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_persistent_fork.py +0 -0
  242. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_python_sdk.py +0 -0
  243. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_scheduled_tasks.py +0 -0
  244. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_sdk_completion_race.py +0 -0
  245. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_skill_execution.py +0 -0
  246. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_probe_thread_naming.py +0 -0
  247. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_project_delete_channel.py +0 -0
  248. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_project_delete_runtime.py +0 -0
  249. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_project_deletion_storage.py +0 -0
  250. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_prompt_projection.py +0 -0
  251. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_question_cards.py +0 -0
  252. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_question_channel.py +0 -0
  253. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_question_target_parity.py +0 -0
  254. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_quoted_context.py +0 -0
  255. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_release_script.py +0 -0
  256. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_result_images.py +0 -0
  257. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_resume_channel.py +0 -0
  258. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_retired_deployment.py +0 -0
  259. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_runtime_resume_activation.py +0 -0
  260. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_schedule_card_retry.py +0 -0
  261. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_schedule_cards.py +0 -0
  262. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_schedule_mcp.py +0 -0
  263. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_schedule_run_now.py +0 -0
  264. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_schedule_scope_matrix.py +0 -0
  265. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_schedule_service.py +0 -0
  266. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_scheduled_channel.py +0 -0
  267. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_scheduled_runtime.py +0 -0
  268. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_scheduler.py +0 -0
  269. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_scheduler_presenter.py +0 -0
  270. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_schedules_storage.py +0 -0
  271. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_sdk_gap_adapter.py +0 -0
  272. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_session_defaults.py +0 -0
  273. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_session_settings.py +0 -0
  274. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_settings_projects.py +0 -0
  275. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_task_text.py +0 -0
  276. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_terminal_cleanup.py +0 -0
  277. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_turn_activity.py +0 -0
  278. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_turn_files.py +0 -0
  279. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_turn_patch_children.py +0 -0
  280. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_turn_patch_statistics.py +0 -0
  281. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_turn_plan_observer.py +0 -0
  282. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_update_executor.py +0 -0
  283. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_usage_card.py +0 -0
  284. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_usage_control.py +0 -0
  285. {netizen_cli-0.11.4 → netizen_cli-0.12.0}/tests/test_user_questions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netizen-cli
3
- Version: 0.11.4
3
+ Version: 0.12.0
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
@@ -36,19 +36,19 @@
36
36
  - **把任务交给 Codex。** 在飞书中阅读代码、修改项目、整理资料;任务由官方 Codex SDK
37
37
  管理的原生会话执行,沿用运行主机上的工具和工作目录。
38
38
  - **边做边沟通。** 普通任务执行时继续发消息,可以补充条件或调整方向;也可以开启
39
- [过程卡](skills/netizen-user-guide/references/user-guide.md#飞书中的运行反馈)查看进展。
39
+ [过程卡](skills/netizen-user-guide/references/feedback-and-files.md#飞书中的运行反馈)查看进展。
40
40
  - **带上图片与讨论背景。** 支持图片、逐条引用,以及可选的
41
- [群聊上下文](skills/netizen-user-guide/references/user-guide.md#发送消息引用与图片)。
41
+ [群聊上下文](skills/netizen-user-guide/references/messages.md#发送消息引用与图片)。
42
42
  - **取回本轮文件。** 完成后从回复卡片中按需发送有原生执行记录的文件;发送的是点击时
43
- 的当前内容,详见[文件说明](skills/netizen-user-guide/references/user-guide.md#查看和发送本轮文件)。
43
+ 的当前内容,详见[文件说明](skills/netizen-user-guide/references/feedback-and-files.md#查看和发送本轮文件)。
44
44
  - **保留上下文,另开一条讨论。** 用
45
- [持久分支](skills/netizen-user-guide/references/user-guide.md#fork-持久分支)在当前聊天或其他群
45
+ [持久分支](skills/netizen-user-guide/references/fork-side-goal.md#fork-持久分支)在当前聊天或其他群
46
46
  创建普通话题,之后独立继续。
47
47
  - **展开临时讨论,推进持续目标。** 用
48
- [Side](skills/netizen-user-guide/references/user-guide.md#side-临时话题)另开临时话题,或用
49
- [Goal](skills/netizen-user-guide/references/user-guide.md#goal)让 Codex 围绕目标持续推进。
48
+ [Side](skills/netizen-user-guide/references/fork-side-goal.md#side-临时话题)另开临时话题,或用
49
+ [Goal](skills/netizen-user-guide/references/fork-side-goal.md#goal)让 Codex 围绕目标持续推进。
50
50
  - **让任务按时开始。** 用自然语言或 `/cron` 创建
51
- [定时任务](skills/netizen-user-guide/references/user-guide.md#定时任务),可每次在独立话题执行,也可在原会话定时继续。
51
+ [定时任务](skills/netizen-user-guide/references/scheduled-tasks.md#定时任务),可每次在独立话题执行,也可在原会话定时继续。
52
52
 
53
53
  普通会话的历史由 Codex 保存,可在 Codex App/CLI 中继续使用;App/CLI 中新增的消息不会
54
54
  自动回填飞书。多个会话可以并行,同一项目的文件目录共享,修改会相互可见。
@@ -109,7 +109,7 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
109
109
  5. 收到回复后继续交流;如果出现“本轮文件”,点击“发送”取回需要的文件。
110
110
 
111
111
  不确定下一步时发送 `/help` 查看快速开始。通过 `/defaults` 或 Admin 配好
112
- [会话默认配置](skills/netizen-user-guide/references/user-guide.md#默认会话配置与自动创建)后,
112
+ [会话默认配置](skills/netizen-user-guide/references/sessions.md#默认会话配置与自动创建)后,
113
113
  没有当前会话时也可直接发送任务:机器人会在消息所在主线或话题创建会话并执行。
114
114
  没有可用默认配置时,机器人仍会提示准备步骤;被拦下的任务需在准备好后重新发送。
115
115
 
@@ -117,7 +117,7 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
117
117
  不接受参数。普通任务执行中发来的新消息用于调整当前任务,不会排队成下一个任务。
118
118
 
119
119
  安装遇到问题可从[部署文档](docs/deployment.md)继续;已能对话时,常见疑问见
120
- [使用 FAQ](skills/netizen-user-guide/references/user-guide.md#常见问题)。
120
+ [使用 FAQ](skills/netizen-user-guide/references/faq.md#常见问题)。
121
121
 
122
122
  ## 命令
123
123
 
@@ -133,12 +133,12 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
133
133
  | 调整模型、思考强度和过程卡 | `/config`,当前会话空闲时使用 |
134
134
  | 查看当前任务和上下文用量 | `/status` |
135
135
  | 查看共享 Codex 账号额度和重置时间 | `/usage` |
136
- | 中断当前任务 | `/stop`;不保证前台工具进程退出,见[停止说明](skills/netizen-user-guide/references/user-guide.md#stop) |
136
+ | 中断当前任务 | `/stop`;不保证前台工具进程退出,见[停止说明](skills/netizen-user-guide/references/status-and-control.md#stop) |
137
137
  | 查找当前机器人的管理入口 | `/admin`;返回实际管理 URL 和实例根目录,不返回登录凭据 |
138
138
  | 管理定时任务 | `/cron`,也可以直接用自然语言描述计划 |
139
139
  | 查看当前实例提供的命令 | `/help` |
140
140
 
141
- 重命名、归档、恢复、删除等完整用法见[命令索引](skills/netizen-user-guide/references/user-guide.md#完整命令索引)。
141
+ 重命名、归档、恢复、删除等完整用法见[命令索引](skills/netizen-user-guide/references/commands.md#完整命令索引)。
142
142
  当前实例的命令开放情况以 `/help` 为准,任务状态以 `/status` 和卡片提示为准。
143
143
 
144
144
  ### 两个常见场景
@@ -159,7 +159,7 @@ Side 会在同一聊天中新建话题,可继续多轮讨论。它与原会话
159
159
  ```
160
160
 
161
161
  计划每次创建独立执行话题;暂停计划不会停止已经触发的任务。
162
- [定时任务用法](skills/netizen-user-guide/references/user-guide.md#定时任务)
162
+ [定时任务用法](skills/netizen-user-guide/references/scheduled-tasks.md#定时任务)
163
163
 
164
164
  <a id="admin-web"></a>
165
165
 
@@ -183,12 +183,12 @@ Admin 是实例必备能力;管理员可在“系统管理 → 权限管理
183
183
  - **共享环境。** 飞书应用的可用范围和群成员关系控制谁能访问;同一实例复用服务账号,
184
184
  不提供按用户或项目隔离的权限体系。Admin Web 面向受信内网,名单中的管理员权限相同。
185
185
  - **输入与输出。** 支持文本、普通图片和富文本图片,也可提供飞书 2.0 卡片、合并转发与转发话题作为
186
- [背景材料](skills/netizen-user-guide/references/user-guide.md#卡片合并转发与转发话题)。材料内部的媒体只提供未读附件描述;直接文件和音视频仍不支持。
186
+ [背景材料](skills/netizen-user-guide/references/messages.md#卡片合并转发与转发话题)。材料内部的媒体只提供未读附件描述;直接文件和音视频仍不支持。
187
187
  飞书 1.0 卡片明确不支持。
188
188
  输出文件按本轮原生记录发现,不扫描工作区补齐,也不自动上传。
189
189
  - **Codex 能力有宿主差异。** 飞书复用原生会话和工具,但并非每个 App/CLI 控件都可用;
190
190
  全新会话采用 `auto_review`,持久分支沿用原生继承权限;不提供 App 的 Ask/Custom 选择器。
191
- [完整差异与当前限制](skills/netizen-user-guide/references/user-guide.md#与-codex-appcli-的差异)
191
+ [完整差异与当前限制](skills/netizen-user-guide/references/native-capabilities.md#与-codex-appcli-的差异)
192
192
 
193
193
  <a id="用户指南-skill"></a>
194
194
 
@@ -15,6 +15,17 @@ projects:
15
15
  channel:
16
16
  securityMode: audit
17
17
 
18
+ # Optional collaboration-tool ranges; both default to all when omitted.
19
+ # Admin can update these at runtime. These rules do not restrict ordinary chat input.
20
+ # Each mode is all, only, exclude, or disabled; only/exclude use exact chat IDs.
21
+ sessionTools:
22
+ discovery:
23
+ mode: all
24
+ chatIds: []
25
+ readSend:
26
+ mode: all
27
+ chatIds: []
28
+
18
29
  # Required management UI, using this app's Feishu administrator identities.
19
30
  # It listens on all interfaces for direct access inside the trusted private network.
20
31
  adminWeb:
@@ -1,3 +1,3 @@
1
1
  """Netizen: a thin Feishu Channel for native Codex Threads."""
2
2
 
3
- __version__ = "0.11.4"
3
+ __version__ = "0.12.0"
@@ -52,7 +52,7 @@ def _identity(metadata: os.stat_result) -> tuple[int, ...]:
52
52
  )
53
53
 
54
54
 
55
- def persist_admin_port(snapshot: ConfigFileSnapshot, port: int) -> None:
55
+ def persist_admin_port(snapshot: ConfigFileSnapshot, port: int) -> ConfigFileSnapshot:
56
56
  """Preserve all YAML values and refuse a detected concurrent human edit.
57
57
 
58
58
  The caller retains its listening socket and instance lifetime lock. This
@@ -72,7 +72,7 @@ def persist_admin_port(snapshot: ConfigFileSnapshot, port: int) -> None:
72
72
  values = dict(values)
73
73
  loaded["adminWeb"] = values
74
74
  values["port"] = port
75
- _write_configuration(snapshot, loaded)
75
+ return write_configuration(snapshot, loaded)
76
76
 
77
77
 
78
78
  def _validate_port(port: int) -> None:
@@ -80,10 +80,11 @@ def _validate_port(port: int) -> None:
80
80
  raise AdminPortConfigurationError("Admin port must be from 1 to 65535")
81
81
 
82
82
 
83
- def _write_configuration(snapshot: ConfigFileSnapshot, loaded: dict) -> None:
83
+ def write_configuration(snapshot: ConfigFileSnapshot, loaded: dict) -> ConfigFileSnapshot:
84
+ """Replace the existing instance YAML after checking its exact snapshot."""
84
85
  content = yaml.safe_dump(loaded, allow_unicode=True, sort_keys=False).encode("utf-8")
85
86
  snapshot.require_unchanged()
86
- descriptor, temporary_name = tempfile.mkstemp(prefix=".admin-port-", dir=snapshot.path.parent)
87
+ descriptor, temporary_name = tempfile.mkstemp(prefix=".netizen-config-", dir=snapshot.path.parent)
87
88
  temporary = Path(temporary_name)
88
89
  try:
89
90
  with os.fdopen(descriptor, "wb") as output:
@@ -96,3 +97,4 @@ def _write_configuration(snapshot: ConfigFileSnapshot, loaded: dict) -> None:
96
97
  finally:
97
98
  with contextlib.suppress(FileNotFoundError):
98
99
  temporary.unlink()
100
+ return ConfigFileSnapshot.read(snapshot.path)
@@ -378,6 +378,12 @@ body.schedule-detail-open { overflow: hidden; }
378
378
  .system-tabs { width: fit-content; margin: 22px 0; }
379
379
  .admin-users { padding: 0; margin: 0; list-style: none; }
380
380
  .admin-users > li { display: flex; justify-content: space-between; align-items: center; gap: 16px; border-top: 1px solid var(--line); padding: 16px 0; }
381
+ .session-access { margin-top: 32px; padding-top: 18px; border-top: 1px solid var(--line); }
382
+ .session-access-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); gap: 24px; }
383
+ .session-access-grid h4 { margin: 0 0 8px; }
384
+ .session-access-grid p { min-height: 48px; color: var(--muted); line-height: 1.6; }
385
+ .session-access-grid textarea { width: 100%; resize: vertical; font-family: monospace; }
386
+ .session-access-grid [hidden] { display: none; }
381
387
  .identity-panel { max-width: 480px; border: 1px solid var(--line); border-radius: 14px; margin-bottom: 24px; padding: 20px; color: var(--ink); }
382
388
  .identity-panel h3 { margin: 0; }
383
389
  .identity-panel > p { margin: 20px 0; }
@@ -61,6 +61,7 @@ function expireAdminSession() {
61
61
  const notice = document.querySelector("#auth-expired");
62
62
  if (notice) notice.hidden = false;
63
63
  window.NetizenIdentity?.expire();
64
+ window.NetizenSessionAccess?.expire();
64
65
  setStatus("登录已过期,请重新登录。", true);
65
66
  }
66
67
 
@@ -404,7 +405,7 @@ function actionsCell(row) {
404
405
  function confirmMaterializedDelete(session) {
405
406
  const name = session.nativeTitle || "未命名 Session";
406
407
  return window.confirm(
407
- `确认永久删除这个会话?\n\n会话:${name}\nScope:${session.scopeKey}\nShort ID:${session.shortId}`
408
+ `确认永久删除这个会话?\n\n会话:${name}\nScope:${session.scopeKey}\nNetizen 会话编号:${session.shortId}`
408
409
  + "\n\n原生 Thread、spawned descendants、Codex App/CLI 历史和本地 Binding 都会永久消失,无法恢复。",
409
410
  );
410
411
  }
@@ -1608,15 +1609,16 @@ function sessionIdentityCell(row, session) {
1608
1609
  }
1609
1610
  const bindingId = document.createElement("div");
1610
1611
  bindingId.className = "id";
1611
- bindingId.textContent = `Binding ${session.shortId}`;
1612
+ bindingId.textContent = `Netizen 会话编号:${session.shortId}`;
1612
1613
  td.append(bindingId);
1613
1614
  const details = document.createElement("details");
1614
1615
  details.className = "session-identities";
1615
1616
  const summary = document.createElement("summary");
1616
1617
  summary.textContent = "会话标识";
1617
1618
  details.append(summary);
1618
- for (const [label, value] of [["Chat", session.chatId], ["Topic", session.topicId],
1619
- ["Binding", session.bindingId], ["Thread", session.nativeThreadId]]) {
1619
+ for (const [label, value] of [["飞书聊天 ID(位置)", session.chatId], ["飞书话题 ID(位置)", session.topicId],
1620
+ ["Netizen 会话编号(Binding ID,管理与切换)", session.bindingId],
1621
+ ["Codex 会话 ID(Thread ID,跨会话工具)", session.nativeThreadId]]) {
1620
1622
  if (!value) continue;
1621
1623
  const field = document.createElement("label");
1622
1624
  field.textContent = label;
@@ -1624,15 +1626,21 @@ function sessionIdentityCell(row, session) {
1624
1626
  input.type = "text";
1625
1627
  input.readOnly = true;
1626
1628
  input.value = value;
1627
- input.setAttribute("aria-label", `${label} ID,选中后可复制`);
1629
+ input.setAttribute("aria-label", `${label},完整值,选中后可复制`);
1628
1630
  input.addEventListener("focus", () => input.select());
1629
1631
  input.addEventListener("click", () => input.select());
1630
1632
  field.append(input);
1631
1633
  details.append(field);
1632
1634
  }
1633
1635
  const hint = document.createElement("small");
1634
- hint.textContent = "点击标识全选,再按 Ctrl/Cmd+C 复制。";
1636
+ hint.textContent = "点击标识全选,再按 Ctrl/Cmd+C 复制。跨会话工具使用完整 Codex 会话 ID;现有管理与 /resume 使用 Netizen 编号。";
1635
1637
  details.append(hint);
1638
+ if (!session.nativeThreadId) {
1639
+ const pending = document.createElement("p");
1640
+ pending.className = "meta-line";
1641
+ pending.textContent = "Codex 会话 ID 尚未生成,首条消息后创建。";
1642
+ details.append(pending);
1643
+ }
1636
1644
  td.append(details);
1637
1645
  row.append(td);
1638
1646
  }
@@ -1862,20 +1870,6 @@ function wireSessionActions(actions, session) {
1862
1870
  });
1863
1871
  const schedule = actionButton("创建定时任务", () => openScheduleForSession(session, schedule));
1864
1872
  secondary.append(schedule);
1865
- if (a.createLazy) secondary.append(actionButton("新建 Lazy", async () => {
1866
- if (!state.projects) await loadProjects();
1867
- const alias = window.prompt("Project alias");
1868
- if (!alias) return;
1869
- const project = state.projects.items.find((item) => item.alias === alias && item.enabled);
1870
- if (!project) return setStatus("未找到当前页中已启用的 Project,请先刷新 Projects。", true);
1871
- const activate = window.confirm("是否立即设为当前会话?");
1872
- await mutate("/api/v1/sessions/create-lazy", a.createLazy, {
1873
- projectAlias: alias,
1874
- projectRevision: project.revision,
1875
- activate,
1876
- turnSettings: null,
1877
- });
1878
- }));
1879
1873
  if (a.configure) {
1880
1874
  const button = actionButton("配置", () => openSessionSettingsEditor(session, button));
1881
1875
  actions.append(button);
@@ -2047,7 +2041,7 @@ function openSessionSettingsEditor(session, returnFocus) {
2047
2041
  privateChat, contextAvailable: !privateChat && base.message_context_mode === "catch-up",
2048
2042
  loading: true, saving: false, optionsSerial: 0 };
2049
2043
  sessionSettingsInput("editor").hidden = false;
2050
- sessionSettingsInput("identity").textContent = `${session.nativeTitle || "未命名 Session"} · ${session.shortId} · ${session.projectAlias}`;
2044
+ sessionSettingsInput("identity").textContent = `${session.nativeTitle || "未命名 Session"} · Netizen 会话编号:${session.shortId} · ${session.projectAlias}`;
2051
2045
  renderSessionSettingsEditor();
2052
2046
  if (!sessionSettingsInput("drawer").open) sessionSettingsInput("drawer").showModal();
2053
2047
  document.body.classList.toggle("session-settings-drawer-open", true);
@@ -2647,18 +2641,20 @@ function confirmDiscardChanges(changed) {
2647
2641
 
2648
2642
  function hasUnsavedEditors() {
2649
2643
  return sessionSettingsHasChanges() || defaultEditorHasChanges() || scheduleEditorHasChanges()
2650
- || Boolean(pendingScheduleDraft?.dirty);
2644
+ || Boolean(pendingScheduleDraft?.dirty) || Boolean(window.NetizenSessionAccess?.dirty());
2651
2645
  }
2652
2646
 
2653
2647
  // Navigation asks once before closing any editor. Busy submissions retain the
2654
2648
  // existing fail-closed boundary; this helper never cancels or repeats a POST.
2655
2649
  function confirmDiscardEditors() {
2656
- if (sessionSettingsEditor?.saving || defaultsBusy || (scheduleEditor && scheduleInput("fields").disabled)) return false;
2650
+ if (sessionSettingsEditor?.saving || defaultsBusy || (scheduleEditor && scheduleInput("fields").disabled)
2651
+ || window.NetizenSessionAccess?.busy()) return false;
2657
2652
  if (!confirmDiscardChanges(hasUnsavedEditors())) return false;
2658
2653
  closeSessionSettingsEditor({ confirmed: true });
2659
2654
  closeDefaultEditor({ confirmed: true });
2660
2655
  if (scheduleEditor) closeScheduleEditor({ confirmed: true });
2661
2656
  cancelScheduleSelection({ confirmed: true });
2657
+ window.NetizenSessionAccess?.discard();
2662
2658
  return true;
2663
2659
  }
2664
2660
 
@@ -2891,7 +2887,7 @@ function renderScheduleTarget() {
2891
2887
  scheduleInput("binding").value = selected;
2892
2888
  scheduleInput("select-session").hidden = Boolean(selected);
2893
2889
  scheduleInput("binding-summary").textContent = target
2894
- ? `${target.nativeTitle || target.shortId || "未命名会话"}\n${target.chatLabel || target.chatId} · ${target.projectAlias}\n会话 ${target.shortId || target.bindingId.slice(0, 8)}${target.topicId ? ` · 话题 ${target.topicId}` : ""}`
2890
+ ? `${target.nativeTitle || target.shortId || "未命名会话"}\n${target.chatLabel || target.chatId} · ${target.projectAlias}\nNetizen 会话编号:${target.shortId || target.bindingId.slice(0, 8)}${target.topicId ? ` · 话题 ${target.topicId}` : ""}`
2895
2891
  + (target.pointerState !== "current" || target.catalogState === "archived" ? "\n目标当前不是可执行的当前会话,计划将自动暂停;不会自动切换或恢复会话。" : "")
2896
2892
  : selected ? `${editor.plan.target_label || selected}\n${editor.plan.chat?.chatLabel || editor.plan.chat_id} · ${editor.plan.project_alias}`
2897
2893
  : "请前往 Sessions 找到具体会话,再点击该行的“创建定时任务”。已填写内容会在当前页面保留。";
@@ -3927,7 +3923,10 @@ async function refresh(tab, cursor = undefined) {
3927
3923
  if (tab === "updates") await loadUpdates();
3928
3924
  if (tab === "schedules" && await loadSchedules(cursor) === false) return false;
3929
3925
  if (tab === "defaults") await loadDefaults();
3930
- if (tab === "system") await window.NetizenIdentity.loadUsers();
3926
+ if (tab === "system") {
3927
+ const results = await Promise.all([window.NetizenIdentity.loadUsers(), window.NetizenSessionAccess?.load()]);
3928
+ if (results[1] === false) return false;
3929
+ }
3931
3930
  if (state.authExpired) return false;
3932
3931
  if (isCurrentView()) setStatus("已更新。");
3933
3932
  return true;
@@ -8,6 +8,7 @@
8
8
  <link rel="stylesheet" href="/static/chat-picker.css">
9
9
  <script src="/static/chat-picker.js" defer></script>
10
10
  <script src="/static/identity.js" defer></script>
11
+ <script src="/static/session-access.js" defer></script>
11
12
  <script src="/static/admin.js" defer></script>
12
13
  </head>
13
14
  <body>
@@ -389,6 +390,35 @@
389
390
  <div class="identity-panel-actions"><button id="admin-selector-retry" class="refresh" hidden>重试加载</button><button id="admin-selector-reload" class="refresh" hidden>重新加载页面</button><button id="admin-user-confirm" class="action" disabled>确认添加</button></div>
390
391
  </section>
391
392
  <ul id="admin-users" class="admin-users" aria-label="管理员名单"></ul>
393
+ <section class="session-access" aria-labelledby="session-access-heading">
394
+ <h3 id="session-access-heading">跨会话工具权限</h3>
395
+ <p class="defaults-help">仅控制 Agent 对本实例普通会话的跨会话访问。所有来源聊天共用规则,范围按目标聊天匹配,包含其中的主会话与话题。两组权限互不依赖。</p>
396
+ <form id="session-access-form" class="card-form">
397
+ <fieldset id="session-access-fields" disabled>
398
+ <div class="session-access-grid">
399
+ <section>
400
+ <h4>发现</h4><p>允许列出、搜索会话标识及位置;不能单独授权读取消息正文。</p>
401
+ <label>目标范围<select id="session-access-discovery-mode"><option value="all">全部</option><option value="only">仅指定聊天</option><option value="exclude">排除指定聊天(黑名单)</option><option value="disabled">关闭</option></select></label>
402
+ <div id="session-access-discovery-targets" hidden>
403
+ <div id="session-access-discovery-picker"></div>
404
+ <label>聊天 ID,每行一个<textarea id="session-access-discovery-chats" rows="5" maxlength="257000" placeholder="oc_…" aria-describedby="session-access-chat-help"></textarea></label>
405
+ </div>
406
+ </section>
407
+ <section>
408
+ <h4>读取与发送</h4><p>允许读取、发送和等待结果;在当前聊天创建话题并启动任务也检查此范围。关闭发现后仍可凭完整 Codex 会话 ID 操作。</p>
409
+ <label>目标范围<select id="session-access-readSend-mode"><option value="all">全部</option><option value="only">仅指定聊天</option><option value="exclude">排除指定聊天(黑名单)</option><option value="disabled">关闭</option></select></label>
410
+ <div id="session-access-readSend-targets" hidden>
411
+ <div id="session-access-readSend-picker"></div>
412
+ <label>聊天 ID,每行一个<textarea id="session-access-readSend-chats" rows="5" maxlength="257000" placeholder="oc_…" aria-describedby="session-access-chat-help"></textarea></label>
413
+ </div>
414
+ </section>
415
+ </div>
416
+ <p id="session-access-chat-help" class="defaults-field-help">可选择群聊加入名单,或填写聊天 ID(单聊也用聊天 ID,不是用户 ID)。聊天 ID 可在会话列表的“会话标识”中复制。名单为空时,“仅指定聊天”不允许任何目标,“排除指定聊天”允许全部目标。</p>
417
+ </fieldset>
418
+ <p id="session-access-note" class="defaults-field-help" role="status"></p>
419
+ <div class="actions"><button id="session-access-cancel" class="refresh" type="button" disabled>放弃修改</button><button id="session-access-save" class="action" type="submit" disabled>保存权限</button></div>
420
+ </form>
421
+ </section>
392
422
  </div>
393
423
  </section>
394
424
  <section id="updates" class="panel" aria-labelledby="updates-heading" hidden>
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+
3
+ window.NetizenSessionAccess = (() => {
4
+ const node = (id) => document.querySelector(`#session-access-${id}`);
5
+ const groups = ["discovery", "readSend"];
6
+ const pickers = new Map();
7
+ let snapshot = null;
8
+ let baseline = "";
9
+ let pending = false;
10
+ let expired = false;
11
+ let generation = 0;
12
+
13
+ function rule(group) {
14
+ const mode = node(`${group}-mode`).value;
15
+ return { mode, chatIds: ["only", "exclude"].includes(mode)
16
+ ? [...new Set(node(`${group}-chats`).value.split(/\s+/).filter(Boolean))].sort() : [] };
17
+ }
18
+
19
+ function definition() {
20
+ return Object.fromEntries(groups.map((group) => [group, rule(group)]));
21
+ }
22
+
23
+ function dirty() {
24
+ return Boolean(snapshot) && JSON.stringify(definition()) !== baseline;
25
+ }
26
+
27
+ function renderControls() {
28
+ node("fields").disabled = expired || pending || !snapshot;
29
+ for (const group of groups) {
30
+ const listed = ["only", "exclude"].includes(node(`${group}-mode`).value);
31
+ node(`${group}-targets`).hidden = !listed;
32
+ node(`${group}-chats`).disabled = expired || pending || !snapshot || !listed;
33
+ pickers.get(group)?.setDisabled(expired || pending || !snapshot || !listed);
34
+ }
35
+ node("save").disabled = expired || pending || !snapshot?.saveAction || !dirty();
36
+ node("cancel").disabled = expired || pending || !dirty();
37
+ }
38
+
39
+ function discard() {
40
+ generation += 1;
41
+ for (const group of groups) {
42
+ if (snapshot) {
43
+ node(`${group}-mode`).value = snapshot[group].mode;
44
+ node(`${group}-chats`).value = snapshot[group].chatIds.join("\n");
45
+ }
46
+ pickers.get(group)?.close();
47
+ }
48
+ baseline = JSON.stringify(definition());
49
+ renderControls();
50
+ }
51
+
52
+ function initializePickers() {
53
+ for (const group of groups) {
54
+ if (pickers.has(group)) continue;
55
+ pickers.set(group, window.createChatPicker(node(`${group}-picker`), {
56
+ id: `session-access-${group}-group`, label: "添加群聊", fetchPage: fetchChatPage,
57
+ onChange(chat) {
58
+ if (chat && !pending && !expired) {
59
+ generation += 1;
60
+ const ids = new Set(node(`${group}-chats`).value.split(/\s+/).filter(Boolean));
61
+ ids.add(chat.chatId);
62
+ node(`${group}-chats`).value = [...ids].join("\n");
63
+ pickers.get(group).reset();
64
+ renderControls();
65
+ }
66
+ },
67
+ }));
68
+ }
69
+ }
70
+
71
+ async function load() {
72
+ if (expired || pending || !confirmDiscardChanges(dirty())) return false;
73
+ initializePickers();
74
+ const version = ++generation;
75
+ const value = await api("/api/v1/session-tool-access");
76
+ // A late refresh must not overwrite edits made while the GET was pending.
77
+ if (expired || version !== generation) return false;
78
+ snapshot = value;
79
+ discard();
80
+ node("note").textContent = value.saveAction ? "保存后立即应用于新的工具访问。" : "实例配置不可编辑。";
81
+ return true;
82
+ }
83
+
84
+ async function save(event) {
85
+ event.preventDefault();
86
+ if (expired || pending || !snapshot?.saveAction || !dirty()) return;
87
+ const body = actionPayload(snapshot.saveAction, definition());
88
+ pending = true;
89
+ generation += 1;
90
+ renderControls();
91
+ try {
92
+ const value = await api("/api/v1/session-tool-access/save", {
93
+ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
94
+ });
95
+ if (expired) return;
96
+ snapshot = value;
97
+ discard();
98
+ node("note").textContent = "跨会话工具权限已保存并生效。";
99
+ setStatus("跨会话工具权限已保存。");
100
+ } catch (error) {
101
+ if (!expired) {
102
+ // A consumed grant or unknown result is never silently replayed.
103
+ snapshot.saveAction = null;
104
+ node("note").textContent = "请刷新核对当前权限后再保存;本次修改未自动重试。";
105
+ setStatus(error.message, true);
106
+ }
107
+ } finally {
108
+ pending = false;
109
+ renderControls();
110
+ }
111
+ }
112
+
113
+ for (const group of groups) {
114
+ for (const field of ["mode", "chats"]) {
115
+ node(`${group}-${field}`).addEventListener(field === "mode" ? "change" : "input", () => {
116
+ generation += 1;
117
+ renderControls();
118
+ });
119
+ }
120
+ }
121
+ node("form").addEventListener("submit", save);
122
+ node("cancel").addEventListener("click", discard);
123
+ return {
124
+ load, dirty, discard, busy: () => pending,
125
+ expire() { expired = true; generation += 1; renderControls(); },
126
+ };
127
+ })();