muli-agent 2.2.0__tar.gz → 2.3.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.
- {muli_agent-2.2.0 → muli_agent-2.3.0}/CHANGELOG.md +10 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/PKG-INFO +3 -2
- {muli_agent-2.2.0 → muli_agent-2.3.0}/README.md +2 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/architecture.md +3 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/context.md +3 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/frontend.md +19 -3
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/iterations.md +10 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/review-v2.2.md +8 -1
- muli_agent-2.3.0/docs/review-v2.3.md +35 -0
- muli_agent-2.3.0/docs/task-control-api.md +48 -0
- muli_agent-2.3.0/docs/task-control-runtime.md +51 -0
- muli_agent-2.3.0/docs/task-control.md +100 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/team-wait.md +2 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/workflows.md +5 -3
- {muli_agent-2.2.0 → muli_agent-2.3.0}/pyproject.toml +1 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/scripts/check_wheel.py +13 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/__init__.py +1 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/commitments.py +19 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/conversation.py +34 -8
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/local_services.py +69 -7
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/runtime.py +314 -30
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/server.py +48 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/store.py +96 -50
- muli_agent-2.3.0/src/muli/task_control.py +818 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/team.py +214 -73
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/team_wait.py +1 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/workflows.py +19 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/state.test.mjs +53 -1
- muli_agent-2.3.0/tests/frontend/task-control.test.mjs +241 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_commitment_management.py +5 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_task_briefs.py +12 -2
- muli_agent-2.3.0/tests/test_task_control.py +792 -0
- muli_agent-2.3.0/tests/test_task_control_management.py +261 -0
- muli_agent-2.3.0/tests/test_task_control_runtime.py +1174 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/uv.lock +1 -1
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/app.mjs +182 -26
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/state.mjs +10 -3
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/styles.css +11 -0
- muli_agent-2.3.0/web/task-control.mjs +99 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/.github/workflows/ci.yml +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/.github/workflows/release.yml +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/.gitignore +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/.python-version +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/LICENSE +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/config.json.example +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/agent-continuity-v2.2.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/attention.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/channels.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/chiliarch.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/continuous-life-design.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/mcp.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/memory.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/notifications.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/operations.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/platforms.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/providers.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/publishing.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/review-round-1.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/review-v2.1.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/review-v2.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/screenshots/workbench-desktop.jpg +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/screenshots/workbench-mobile.jpg +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/self-management.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/task-briefs.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/voice.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/docs/wayland-portal-design.md +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/img/standby.png +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/main.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/scripts/release.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/__main__.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/attachments.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/attention.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/auth.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/capture.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/channel_application.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/channels/__init__.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/channels/adapters.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/channels/crypto.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/channels/http.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/channels/manager.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/channels/types.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/channels/weixin.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/compaction.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/config.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/context.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/events.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/initialization.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/integrations/chiliarch.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/integrations/manager.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/management.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/media.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/memory.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/memory_application.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/memory_pipeline.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/memory_schema.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/middleware.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/notifications.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/permissions.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/personal_io.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/platforms/__init__.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/platforms/files.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/platforms/restart_service.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/platforms/service_runner.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/plugins.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/process.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/prompts/system.txt +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/providers/__init__.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/providers/client.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/providers/codecs.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/providers/http.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/providers/streaming.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/providers/types.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/reminders.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/service.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/tools/__init__.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/tools/atspi_helper.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/tools/computer.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/tools/files.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/tools/mcp.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/tools/portal_helper.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/tools/web.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/updates.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/voice.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/src/muli/web_voice.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/fixtures/portal_fake.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/api-docs.test.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/api.test.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/attachments.test.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/contrast.test.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/management.test.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/settings.test.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/team.test.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/theme.test.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/frontend/voice-input.test.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_application_integrations.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_attention.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_auth_uploads.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_capture.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_channels.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_chiliarch.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_cli.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_compaction.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_config_concurrency.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_context.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_conversation.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_documented_config.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_initialization.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_integration_manager.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_local_services.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_management.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_mcp.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_media.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_memory.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_memory_application.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_memory_pipeline.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_notifications.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_portal.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_process_cleanup.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_process_grace.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_providers.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_readonly_source.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_release.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_reminders.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_runtime.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_safety.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_server.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_service.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_store_replay.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_team.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_team_briefs.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_team_cleanup.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_team_management.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_team_wait.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_voice.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_web_voice.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/tests/test_workflows.py +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/api-docs.html +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/api-docs.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/api.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/assets/mark.svg +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/assets/muli-avatar.svg +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/assets/muli-empty.svg +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/assets/standby.webp +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/attachments.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/index.html +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/management.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/markdown.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/settings.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/team.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/theme.mjs +0 -0
- {muli_agent-2.2.0 → muli_agent-2.3.0}/web/voice-input.mjs +0 -0
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
版本记录区分实际交付与历史回填;未完成验证的工作不作为发布完成的证据。
|
|
4
4
|
|
|
5
|
+
## [2.3.0] — 2026-10-20
|
|
6
|
+
|
|
7
|
+
- 工作账本增加主人明确暂停、追加完整要求及交主沐璃重新规划的持久控制;控制回执、修订与入站原子保存,断线重试返回原回执和当前状态,不重复追加要求。
|
|
8
|
+
- 每次模型请求固定完整说明与控制版本,审批、扩展上下文和工具启动事件等待后重新核对;旧副作用调用不能跨过新要求,纯暂停不自行调用模型或解除。已开始的操作可以收尾,原生工具调用与结果保持配对。
|
|
9
|
+
- 暂停范围覆盖同账本的多个团队根及全部后代,暂停成员仍在管理入口显示;确定的计划通知支持邮箱背压、正常重启和旧执行停靠,不用 AI 轮询,也不复活已取消的新代次。
|
|
10
|
+
- 任务详情提供暂停、补充要求和交主沐璃重新规划,沿用认证与同源写入保护。显示修订、范围和计划,较旧响应不覆盖新事实;未确认的提交保留原请求标识,认证过期清空内容。
|
|
11
|
+
- 工作流节点继承任务范围与完整说明,启动与暂停有明确先后;包含活动 DAG 的暂停请求整体拒绝。活动 DAG 的中途暂停及副作用回滚不在本版范围。
|
|
12
|
+
- 修复报告交接在暂停后误唤醒主助手、延后报告挡住无关任务与系统提醒、容量释放事件在扫描期间被忽略等时序;停止后旧报告不复活。合法旧说明在热缩预算后仍可暂停,新的主人意图清除过期的临时重规划选择。
|
|
13
|
+
- 前端新增独立暂停和未执行状态;真实浏览器发现的按钮节点渲染问题已修复,断线重试、重新登录后的迟到响应和手机布局有专项验证。
|
|
14
|
+
|
|
5
15
|
## [2.2.0] — 2026-10-20
|
|
6
16
|
|
|
7
17
|
- 持续工作账本可保存完整任务目标、逐条要求和原文证据引用;一棵团队树绑定一份任务说明,子孙继承,主助手可按修订分页读取。已登记正文独立于有损摘要,压缩或重启后完整保留;上下文预算不足明确失败,不静默截去限制。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: muli-agent
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: 沐璃:事件驱动的本地数字生命与私人多 agent 助手
|
|
5
5
|
Project-URL: Repository, https://github.com/69gg/MuLi
|
|
6
6
|
Project-URL: Documentation, https://github.com/69gg/MuLi/blob/main/README.md
|
|
@@ -52,7 +52,7 @@ MuLi 是面向个人电脑的常驻助手:聊天、管理文件、搜索资料
|
|
|
52
52
|
| 语音 | 独立默认关闭的网页录音转写草稿、本地中文声学唤醒与声纹桥接、录音参考、OpenAI 兼容 ASR/TTS;不附带本地模型 |
|
|
53
53
|
| 主动事件 | chiliarch 原生配对与 SSE 任务;持久提醒、维护结果与新版通知进入主会话,可选 Linux 桌面通知,无 AI 轮询 |
|
|
54
54
|
| 扩展 | 用户目录 Python 插件与可选 MCP;工具、提示词、模型、路由和生命周期可扩展 |
|
|
55
|
-
| 长任务 |
|
|
55
|
+
| 长任务 | 原始归档、后台分层压缩、协议回放、持久收件队列、完整任务说明、明确暂停与补充要求后的重新规划 |
|
|
56
56
|
| 管理 | 模型、权限、工具、目录、记忆、工作流、通知、服务状态与日志统一入口 |
|
|
57
57
|
|
|
58
58
|
## 启动
|
|
@@ -103,6 +103,7 @@ Linux 使用 systemd 用户服务;macOS 使用 LaunchAgent;Windows 使用当
|
|
|
103
103
|
- [架构与执行边界](docs/architecture.md)
|
|
104
104
|
- [模型协议与参数](docs/providers.md)
|
|
105
105
|
- [多 agent 工作流](docs/workflows.md)
|
|
106
|
+
- [任务暂停、补充要求与重新规划](docs/task-control.md)
|
|
106
107
|
- [上下文与长任务](docs/context.md)
|
|
107
108
|
- [个人记忆设计](docs/memory.md)
|
|
108
109
|
- [MCP 扩展](docs/mcp.md)
|
|
@@ -31,7 +31,7 @@ MuLi 是面向个人电脑的常驻助手:聊天、管理文件、搜索资料
|
|
|
31
31
|
| 语音 | 独立默认关闭的网页录音转写草稿、本地中文声学唤醒与声纹桥接、录音参考、OpenAI 兼容 ASR/TTS;不附带本地模型 |
|
|
32
32
|
| 主动事件 | chiliarch 原生配对与 SSE 任务;持久提醒、维护结果与新版通知进入主会话,可选 Linux 桌面通知,无 AI 轮询 |
|
|
33
33
|
| 扩展 | 用户目录 Python 插件与可选 MCP;工具、提示词、模型、路由和生命周期可扩展 |
|
|
34
|
-
| 长任务 |
|
|
34
|
+
| 长任务 | 原始归档、后台分层压缩、协议回放、持久收件队列、完整任务说明、明确暂停与补充要求后的重新规划 |
|
|
35
35
|
| 管理 | 模型、权限、工具、目录、记忆、工作流、通知、服务状态与日志统一入口 |
|
|
36
36
|
|
|
37
37
|
## 启动
|
|
@@ -82,6 +82,7 @@ Linux 使用 systemd 用户服务;macOS 使用 LaunchAgent;Windows 使用当
|
|
|
82
82
|
- [架构与执行边界](docs/architecture.md)
|
|
83
83
|
- [模型协议与参数](docs/providers.md)
|
|
84
84
|
- [多 agent 工作流](docs/workflows.md)
|
|
85
|
+
- [任务暂停、补充要求与重新规划](docs/task-control.md)
|
|
85
86
|
- [上下文与长任务](docs/context.md)
|
|
86
87
|
- [个人记忆设计](docs/memory.md)
|
|
87
88
|
- [MCP 扩展](docs/mcp.md)
|
|
@@ -8,6 +8,7 @@ MuLi 是服务于单个电脑用户的本地数字生命助手。界面保留十
|
|
|
8
8
|
- 原生 HTML/CSS/JavaScript 工作台,无 Node 构建链。青瓷蓝、墨青文字、简洁会话布局;状态按需展开。
|
|
9
9
|
- `providers` 负责 Chat Completions / Responses / Anthropic 协议与原生工具回放。
|
|
10
10
|
- `runtime` 负责会话、流式输出、取消、预算、独立审查;`conversation` 维护持续主身份收件队列,`team` 管理持久队友与邮箱,`workflows` 执行显式 DAG。
|
|
11
|
+
- `task_control` 在已有工作账本上保存主人暂停、追加要求和主助手确认方案的事实;SQLite 短事务同时提交修订、回执与通知意图。模型请求携带完整说明的固定快照,运行器在进入工具处理函数前核对执行版本,团队生命周期负责暂停和有证据的续办。
|
|
11
12
|
- `compaction` 后台生成和验证分层摘要,`context` 以确定性节选和完整轮次淘汰整理最终请求;原生协议数据与持久历史不改写。后台摘要和记忆提炼不占前台模型执行额度,各自保持有界队列和单请求并发。
|
|
12
13
|
- `tools` 显式注册工具;文件系统、搜索、个人记忆、无障碍电脑操作共用权限门。
|
|
13
14
|
- `store` 使用 SQLite 保存消息、事件、审批、通知、运行和记忆;图关系与来源共同检索,不引入多用户隔离的复杂度。
|
|
@@ -27,6 +28,8 @@ MuLi 是服务于单个电脑用户的本地数字生命助手。界面保留十
|
|
|
27
28
|
|
|
28
29
|
模型发起的子工作流绑定父运行。父 agent 释放共享执行名额后等待子结果,避免单并行名额下互相等待;父取消向下传播。主会话和工作流入口分别限制未完成任务数量,共享实际模型执行的并行上限。配置更新与新运行登记由同一把锁串行化,防止新配置与旧工具混用。
|
|
29
30
|
|
|
31
|
+
主人可从任务详情暂停一个账本所绑定的全部团队成员。已进入处理函数的操作允许收尾;尚未进入的旧副作用调用被拒绝,原生工具调用与结果仍完整配对。纯暂停不请求模型,也不能由模型自行解除。主人明确交主助手重新规划后,主助手须读取完整说明,在后续请求的固定快照上确认方案,再向指定未完成队友投递持久通知。通知记录证明送达,不等同于任务执行成功。活动 DAG 暂不支持中途暂停,相关请求原子拒绝,需先停止或等待结束;详见[任务控制合同](task-control.md)和[管理接口](task-control-api.md)。
|
|
32
|
+
|
|
30
33
|
## 迭代与验证
|
|
31
34
|
|
|
32
35
|
1. 补记历史 v0.0.0;建立可运行的新核心、测试与文档。
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
渠道回复也在每次模型请求开始时取快照:只有稳定主会话会从最新已消费的 owner 输入取得 `source='channel:<name>:<account>'` 的单一路由,写入 assistant 消息及 `message.completed` 的 `delivery_routes`。流式期间新渠道消息不改变原回复去向,下一安全边界才更新;最新输入来自 web、系统注意事项或 route 为空时停止出站。队友和其它会话不自动发渠道,禁止遍历历史路由广播。实际发送前渠道层还要重新校验当前 owner/群组白名单,路由不得携带 token。
|
|
12
12
|
|
|
13
|
-
`system:attention` 是本机提醒与维护事件的专用主体,不是 owner
|
|
13
|
+
`system:attention` 是本机提醒与维护事件的专用主体,不是 owner。消费器先等待活动主任务、队友、工作流及其可交接报告完成,主队列中的 owner/agent 输入也优先消费,再通过内部原子入站接口交付独立的系统资料回合。主助手先回复而队友尚未结束时仍等待,保留报告回主会话后的协调能力;明确暂停身份及其暂缓报告不算活动,不会永久拦住注意事项,系统资料也不能解除暂停。提醒不会触发原工具批次的 `not_executed` 或中断 owner 已授权工作。配置锁内再次核对同一活动条件,必要时继续等待真实任务或事件。关闭消费者只取消等待,不取消被观察的执行任务。系统资料保留 principal/source 标记与空路由,不调用 owner hooks;以它为最新触发输入的模型轮次只开放只读工具,真实 owner 下一次输入才恢复正常协调工具。管理页面可查看策略和消费者状态,详见 [系统注意事项](attention.md)。
|
|
14
14
|
|
|
15
15
|
## 后台语义压缩
|
|
16
16
|
|
|
@@ -54,6 +54,8 @@ assistant 工具调用批次与其全部结果是不可分割单位,未闭合
|
|
|
54
54
|
|
|
55
55
|
绑定队友的 task brief 以独立 system 资料参与最终预算,完整目标、限制和证据引用不经过摘要或头尾摘录;明确标注为协调资料,不能成为新的主人授权。必要说明与最新输入/原生回放放不下时清楚失败。每次上下文构建读取一个当前修订快照,等待模型并发额度期间不自动刷新,也不取消已有工具调用。主会话多任务时只保留账本索引,通过只读分页工具按需取正文,详见 [task-briefs.md](task-briefs.md)。
|
|
56
56
|
|
|
57
|
+
启用任务控制后,该快照同时冻结完整 brief、epoch 和已确认方案;异步 hooks 或容量等待不能给旧正文贴新 epoch。主助手通过只读工具选择的重规划快照与 worker 的执行 scope 分开,读取只选择下一轮,确认必须使用模型该轮实际见到的快照。暂停在工具最后一次 await 后阻挡尚未进入的副作用,先完整配齐原生批次,再持久停靠为 paused;已进入的 handler 不声称撤销。详见 [任务控制的运行边界](task-control-runtime.md)。
|
|
58
|
+
|
|
57
59
|
显式开启 session_scoped 的本地工具 factory 会按实际执行 session 生成独立 schema,不改变共享注册表或覆盖后注册的插件替换。运行时在 handler 调用期间用 ContextVar 记录宿主 caller,并在正常、异常与取消路径恢复;派生任务继承的上下文不能替代其真实 session。正文写权限仍须在 handler 内检查,不能仅依赖 schema 隐藏字段。本机 Python 扩展保持 full-trust,不是沙箱。
|
|
58
60
|
|
|
59
61
|
## Codex 参考与差异
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
- 设置:主、审阅、执行、视觉、压缩、记忆提取模型,协议和原生推理参数,称呼与提示词,权限与工具,Chiliarch / MCP,消息渠道,语音与采集,媒体与检索,插件、更新、管理计划,通知、限制、访问和用户服务。
|
|
12
12
|
- 工作流:模板、步骤、模型角色、依赖、真实运行结果和取消。
|
|
13
|
-
-
|
|
13
|
+
- 团队:按最近创建分页查看成员,可筛选进行中任务,发送协作消息、停止任务和查看子任务记录;已绑定任务说明的成员可按需查看完整目标、要求与修订。支持任务控制的后端还可在说明弹窗内暂停任务范围、追加要求并交主沐璃重新规划。实时刷新保留焦点和已展开的消息。
|
|
14
14
|
- 记忆:检索、内容与实体关系编辑、关系证据、来源、置信度及修改历史。
|
|
15
15
|
- 通知:收件箱、已读状态,以及一次或重复提醒的创建、改期、暂停和删除。
|
|
16
16
|
- 附件:本地文件库、图片和音视频预览、下载,以及将已有附件带入对话。
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
| `web/theme.mjs` | 系统主题、本地偏好和主题切换 |
|
|
45
45
|
| `web/attachments.mjs` | 安全媒体分类、同源附件 URL 与容量显示 |
|
|
46
46
|
| `web/team.mjs` | 分页请求代次、团队焦点与展开状态恢复 |
|
|
47
|
+
| `web/task-control.mjs` | 任务控制修订归并、草稿与明确重试状态;不操作 DOM 或发起网络请求 |
|
|
47
48
|
| `web/voice-input.mjs` | 显式录音、轨道回收、上传转写取消与草稿冲突处理 |
|
|
48
49
|
| `web/markdown.mjs` | 纯 DOM 的轻量 Markdown,原始 HTML 按文本显示 |
|
|
49
50
|
| `web/api-docs.html`、`web/api-docs.mjs` | 本地 OpenAPI 参考、搜索和 schema 引用跳转 |
|
|
@@ -68,6 +69,7 @@
|
|
|
68
69
|
| `GET /team?limit=50&offset=0&active_only=false` | `{members,messages,offset,limit,total,active_only,has_more}`;成员含限长消息预览,完整历史通过会话详情读取 |
|
|
69
70
|
| `POST /team/:id/messages`、`POST /team/:id/stop` | `{content}` 协作消息 / 停止成员任务 |
|
|
70
71
|
| `GET /commitments`、`GET /commitments/:id` | 有界工作账本索引 / 已认证的完整任务详情;不存在的 ID 返回 404 |
|
|
72
|
+
| `POST /commitments/:id/control` | `{action:"pause"\|"request_replan",expected_epoch,request_id,additional_requirements?}`;返回 `{receipt,current}`,409 使用 `detail:{code,message,current?}` |
|
|
71
73
|
| `GET/POST /memories`、`GET/PATCH/DELETE /memories/:id` | 记忆内容、关系、来源和置信度 |
|
|
72
74
|
| `GET /memories/graph`、`GET /memories/:id/revisions` | 关系证据与历史快照 |
|
|
73
75
|
| `GET/POST /workflows`、`DELETE /workflows/:id` | 工作流定义 |
|
|
@@ -87,7 +89,15 @@
|
|
|
87
89
|
|
|
88
90
|
密码门通过 HttpOnly / SameSite Cookie 访问服务。任何接口或 SSE 返回 401 时立即中止事件重连,显示密码输入;登录成功后重新恢复主对话。API 参考也仅加载同源文件,不依赖 Swagger CDN。
|
|
89
91
|
|
|
90
|
-
|
|
92
|
+
任务说明正文按文本展示,证据引用默认折叠。打开时按需读取当前保存的修订,不截去中段要求;旧任务没有说明时明确显示尚未登记。没有 `control` / `scope` 的 2.2 详情保持只读,不提供不能兑现的暂停按钮。关闭或打开其他对话框后,迟到的详情响应不会替换新内容;认证失效会关闭对话框并清除详情,确保密码门可访问。
|
|
93
|
+
|
|
94
|
+
任务控制沿用此弹窗,不增加首页面板。暂停必须收到宿主回执,模型文字和本地点击不会直接变成“已暂停”;已经进入工具的操作允许收尾。暂停范围使用服务端 `scope.member_count`,包含同一任务说明的全部绑定成员及其子任务,不从当前团队分页推断。存在活动工作流时显示 `workflow_active` 原因,暂停原子 409,不自动停止工作流,也不显示部分成功。暂停后可追加完整要求或留空交主沐璃重新规划;没有直接恢复按钮,关闭和刷新都不会解除暂停。
|
|
95
|
+
|
|
96
|
+
控制显示以 `current.revision` 单调归并,覆盖同 epoch 从暂停到批准的变化。紧凑写响应不含计划正文时保留已有正文,再按需 GET;只有完整详情自己的修订可标在要求正文上。后台更新不重建正在输入的补充要求,409 保留草稿并提示核对后再次明确提交;“补充要求并交沐璃”只追加要求,不提供删改旧约束的入口。SSE 的 `commitment.updated` 只使当前弹窗失效,不携带或拼接正文,也不轮询模型。
|
|
97
|
+
|
|
98
|
+
请求结果未知时保留原操作体及 `request_id`,用户重试复用相同内容,不自动换 ID 发第二次。`503 / model_unconfigured` 与 `429 / queue_full` 属于明确的事务准入失败:显示服务原因、结束本次提交并保留既有暂停及补充草稿;断线和其他无法确认提交结果的 5xx 仍按未知结果处理。同标签页的 `sessionStorage` 仅在操作已经提交、回执尚未确认期间保存该操作体,支持关窗和刷新后确认;成功或明确失败后移除,401 和退出登录清空。未提交的补充草稿只留在打开的弹窗中,关闭后不保存。该机制不保存密码或 API Key。方案确认与队友通知投递分开显示,界面不把“计划已确认”描述成全体已执行。
|
|
99
|
+
|
|
100
|
+
`run.paused` 结束当前流式状态并清理旧审批,保留排队输入;刷新回放也保持暂停,不显示为持续忙碌。工具结果的 `not_executed` 与失败、成功分别展示,`task_paused` 标为“未执行 · 已暂停”。紧凑工具事件中的非截断 `status/code` 优先保留语义,结果正文可继续按后端预算截断。新暂停后,旧 epoch 的计划只显示为“此前计划”。
|
|
91
101
|
|
|
92
102
|
SSE 事件为 `{id,type,session_id,data,created_at}`。支持消息增量、思考、消息完成、工具开始 / 完成、审批请求 / 解决、运行开始 / 完成 / 失败 / 取消。持久收件使用 `message.queued {message}`,用户消息的 `delivery_status` 为 `pending` / `delivered`;快照中的 pending 行恢复待处理数量。前端同时兼容 `conversation.queued {message_id,run_id,source}` 和 `conversation.injected {message_ids,run_id}`。用户消息完成事件不会清除助手正在流出的片段;晚到的 pending 确认也不会把已送入的消息重新排队。全局事件更新通知、提醒、工作流、团队和附件列表。
|
|
93
103
|
|
|
@@ -110,7 +120,13 @@ node --test tests/frontend/*.test.mjs
|
|
|
110
120
|
node --check web/app.mjs
|
|
111
121
|
```
|
|
112
122
|
|
|
113
|
-
当前
|
|
123
|
+
当前 93 项无依赖测试覆盖:SSE Unicode 与任意分片、回放去重、运行中恢复、补充消息不截断回复、队列快照与实时注入、晚到确认、仅附件消息确认、审批 / 停止、失败保留、时间转换、链接过滤、原始文件字节上传、安全媒体类型与 URL、脱敏设置、密码更新、MCP 原子替换、双主题文字对比度、主题偏好和本地 OpenAPI 引用。新管理回归覆盖只读状态请求、单模块及全部失败、更新未知状态、渠道密钥保留 / 清除、伪状态字段过滤、高级参数草稿合并、独立记忆提取角色、重试确认门、系统资料身份和取消收件。测试不会连接真实模型。
|
|
124
|
+
|
|
125
|
+
任务控制新增 13 条纯状态回归,覆盖旧版只读、同 epoch 修订乱序、紧凑投影保留正文、纯暂停与重规划区分、后台更新保留草稿、结果未知时原 ID 和请求体恢复、明确准入失败保留暂停及草稿、未辨认的 5xx 保留原请求、旧回执不回滚新计划、409 原子失败、401 清理后拒绝迟到回执和错误回执。另有 2 条回归直接调用应用渲染函数,用小型 DOM 替身确认控制区插入按钮节点及旧计划标签更新,3 条 reducer 回归确认暂停直播 / 回放保留队列及未执行工具展示。
|
|
126
|
+
|
|
127
|
+
任务控制界面已用独立本地假 API 在 IAB 预验:1280 × 720 浅色桌面、390 × 844 深色手机,实际点击暂停、补充要求和重规划按钮。覆盖旧计划显示为历史、模型未配置保留暂停与草稿、未知结果刷新后以原请求重试、401 后迟到响应不覆盖新操作、SSE 更新保留输入焦点及草稿、活动工作流 409 状态不变,以及无控制字段的旧任务只读。此处只证明界面渲染与交互接线,不以假接口或纯状态测试代替暂停生效证明;真实整合验收见下。
|
|
128
|
+
|
|
129
|
+
整合后另以真实服务与内存假模型完成 IAB 联测,配置和数据库仅在独立临时目录。实际点击暂停产生持久主人回执,不启动模型;刷新重开保持暂停。补充要求提交后先显示“已交沐璃 · 仍暂停”,主沐璃调用 `commitment_read`,下一次请求携带完整快照后调用 `task_approve_plan`,同 epoch 的修订更新使界面显示“新计划已确认”及计划正文。核对持久回执、工具记录和快照,原有要求及多行新增要求均完整且仅追加一次。1280 × 720 浅色桌面、390 × 844 深色手机无横向溢出或 JavaScript 错误;页面与临时服务验收后均关闭。此联测不连接真实供应商,也不采集设备或执行真实任务副作用。
|
|
114
130
|
|
|
115
131
|
本轮通过 IAB 在独立临时数据目录完成 1280 × 720 桌面及 390 × 844 手机验收:浅深主题与刷新保存、设置保存和恢复、管理页面导航、工作流创建与删除、原始文本 / 图片 / WAV / MP4 上传、图片解码和媒体控件、原始内容下载、仅附件消息、错误密码与登录 / 退出恢复。手机主对话和设置没有横向溢出。
|
|
116
132
|
|
|
@@ -71,4 +71,14 @@ v1.0.0 之后继续并行推进稳定主身份、动态团队、后台压缩、
|
|
|
71
71
|
|
|
72
72
|
整合后的故障复核发现并修复了输出截断吞掉初始游标、热改预算后合法账本阻塞主会话、实时刷新导致详情焦点丢失,以及释放调度槽后收尾协程未被关闭流程追踪的问题。随后用真实 Runtime 与事件门控继续检查停止和恢复交叠,要求旧停止只作用于当时的执行和收件,不能取消恢复后的新一代。最终测试数量及发布状态集中记录于 [2.2 审查](review-v2.2.md),未通过的检查不会计入发布声明。
|
|
73
73
|
|
|
74
|
+
最终签名提交 `568b083` 的八项发布前检查与签名标签 `v2.2.0` 的十一项发布作业全部成功;PyPI 和 GitHub Release 的 wheel/sdist 大小与 SHA-256 分别核对一致。发行链接及文件摘要见 [2.2 审查](review-v2.2.md)。
|
|
75
|
+
|
|
74
76
|
下一轮同时设计主人明确暂停、追加要求及交主沐璃重新规划的持久控制;这部分尚不属于 2.2。chiliarch 0.5.0 已独立完成签名发布与 PyPI/GitHub 文件核对,实际用户服务仍未安装或重新配对,详见 [chiliarch 联动](chiliarch.md)。
|
|
77
|
+
|
|
78
|
+
## 2.3:主人任务控制与可靠重规划
|
|
79
|
+
|
|
80
|
+
持久控制、运行器和前端分别实现后交叉审查,原始任务说明、控制版本和主模型的规划选择分开管理。明确暂停覆盖同账本的全部团队后代,计划通知必须有当前回执与代次证明;主助手不能自行解除纯暂停。活动 DAG 的中途暂停仍原子拒绝,不对未知副作用作回滚承诺。
|
|
81
|
+
|
|
82
|
+
独立审查通过门控复现并修复了完成报告越过暂停、延后报告阻塞无关工作与系统提醒、投递扫描丢失容量事件、热缩预算后误记失败,以及失效重规划选择影响无关主会话的问题。前端真实 DOM 检查又修复按钮数组渲染和暂停终态显示。最终本地 890 项 Python、93 项前端测试与类型/格式检查通过;离线包安装和真实 API 浏览器流程完成,详见 [2.3 审查](review-v2.3.md)。
|
|
83
|
+
|
|
84
|
+
发行验证期间并行研究下一轮 Linux 应用/窗口定向无障碍快照及跨平台原生后端。研究方案与未实现接口不计作 2.3 已交付功能。
|
|
@@ -28,4 +28,11 @@ IAB 使用独立临时目录、关闭全部模型与设备,验证详情全文
|
|
|
28
28
|
|
|
29
29
|
## 发布状态
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
已发布。最终签名提交 `568b083535cfd1ba0dc148944d90feb76fe21156` 经 [PR #5](https://github.com/69gg/MuLi/pull/5) 合入 main;[发布前 CI](https://github.com/69gg/MuLi/actions/runs/39300033096) 的八项检查全部成功,包括 Linux、macOS、Windows × Python 3.12/3.13、质量与发行包检查、可选 MCP 检查。签名标签 `v2.2.0` 验签通过,[发布流水线](https://github.com/69gg/MuLi/actions/runs/39300846150) 的十一项作业全部成功。
|
|
32
|
+
|
|
33
|
+
2026-10-20 02:19(Asia/Shanghai),[PyPI 2.2.0](https://pypi.org/project/muli-agent/2.2.0/) 与 [GitHub Release](https://github.com/69gg/MuLi/releases/tag/v2.2.0) 均提供以下文件;分别读取两站元数据确认大小及 SHA-256 一致,未下载发行包或修改用户安装。
|
|
34
|
+
|
|
35
|
+
| 文件 | 字节数 | SHA-256 |
|
|
36
|
+
| --- | ---: | --- |
|
|
37
|
+
| `muli_agent-2.2.0-py3-none-any.whl` | 403631 | `1785432719dc36b23563bdad79d86f72ebe3f95d39425f9be2730dc746b76b14` |
|
|
38
|
+
| `muli_agent-2.2.0.tar.gz` | 2392408 | `936b5758ba1837f86249829fcfe706cffb86e407c017e88e16d4e5fb57b1dac9` |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# 2.3 审查与验证
|
|
2
|
+
|
|
3
|
+
状态:本地整合与独立验收通过,等待最终远端 CI 与发布核验。2.2 已发布的证据见[上一版审查](review-v2.2.md)。
|
|
4
|
+
|
|
5
|
+
本轮在现有工作账本、SQLite 入站、团队生命周期和原生工具配对之上实现明确任务控制。目标是让主人能够暂停后续操作、完整追加要求,再交主沐璃重新规划。领域合同、管理接口分别见[任务控制](task-control.md)与[接口说明](task-control-api.md)。不增加模型分类器、后台轮询、平台依赖或新的 agent 身份系统。
|
|
6
|
+
|
|
7
|
+
## 独立审查
|
|
8
|
+
|
|
9
|
+
持久领域、运行器与界面分工作树实施;接口和写入边界预先约定,再由其他 agent 复核。主工作树整合 API,使用真实 LocalServices 与运行器进行联测,不以临时接口桩代替执行保障。
|
|
10
|
+
|
|
11
|
+
已经定位并纳入修复与回归的问题:
|
|
12
|
+
|
|
13
|
+
- 已暂停队友无法消费满邮箱;计划通知在精确校验后原子写入归档并排队,保留原 pending 输入,不扩容、不先恢复后补通知。
|
|
14
|
+
- 计划通知已送达,而旧模型请求随后才停靠;共用证明入口核对当前批准、执行代次、完成修订及真实 ingress/归档引用,只有匹配的通知可续排。普通消息、取消、新暂停和伪造来源不构成证明。
|
|
15
|
+
- 投递扫描期间唯一的邮箱容量释放事件可能被重入守卫丢弃;运行器记住真实重入事件,再进行一轮扫描,满邮箱且没有新事件时不自旋。
|
|
16
|
+
- 前端纯状态测试无法发现按钮数组被渲染为文字;真实浏览器预验发现后改为插入实际节点,并增加 DOM 回归。
|
|
17
|
+
- `run.paused` 与最后工具边界的未执行结果需要独立终态,不能让页面持续显示运行中,也不能显示为执行成功。
|
|
18
|
+
- 独立门控证明,最后一次执行检查后才暂停,仍可能产生完成报告并唤醒主助手;报告现在依据当前任务范围暂停状态延后,不能只判断报告本身是否为 `paused`。分页越过延后项,系统注意事项只等待可交接的报告;独立运行器的原反证已经全部转绿,包含真正启动 AttentionInbox 并接收到系统资料。
|
|
19
|
+
- 旧报告页在等待后可能已经被停止操作抑制;投递前重新读取持久状态,防止缓存页复活旧报告,同时保留后来新代次报告的交接。
|
|
20
|
+
- 热缩上下文预算后,已经知道任务暂停的队友仍先验证完整正文,可能误记失败;停靠只核对控制事实,准备新模型请求才检查正文预算。
|
|
21
|
+
- 新主人控制意图立即清除同任务的旧重规划选择,避免失效旧证据阻塞无关主聊天;旧回执的幂等重试不会清除后来合法选择。
|
|
22
|
+
|
|
23
|
+
## 验证记录
|
|
24
|
+
|
|
25
|
+
整合后单进程全量 Python 测试 **890 项通过**,前端状态与 DOM 测试 **93 项通过**。其中领域专项 81 项,运行器新增 29 项,管理 API 与原详情组合 16 项;这些属于全量或专项计数,不能相加当作更多独立测试。全部 Ruff、116 个 Python 文件格式检查、65 个源码文件严格 mypy 和离线锁文件校验通过,依赖版本未变化。
|
|
26
|
+
|
|
27
|
+
独立 reviewer 对冻结运行器候选 `c2fa525` 复核,另跑七项独立反证、正式运行器及原停止/工作流测试,共 85 项通过。核查范围包括真实主助手误唤醒、报告饥饿、系统注意事项、低预算停靠、停止交叠、失效选择、最后工具边界、原生调用配对与同代通知续办;未发现剩余确定阻塞。
|
|
28
|
+
|
|
29
|
+
候选 wheel/sdist 离线构建通过,临时隔离安装验证 CLI、静态模块、API 文档、提示词、应用生命周期和无模型暂停接口。发行名称、版本与 changelog 一致。前端先用本地 fake 响应验证真实 DOM、手机/桌面深浅主题、未知提交原 ID 重试、401 后迟到响应和工作流冲突。
|
|
30
|
+
|
|
31
|
+
最终浏览器连接整合后的真实 Runtime/API,主模型使用内存假客户端。实际点击暂停、补充多行要求并交主助手重新规划;网页依次显示暂停、已交沐璃但仍暂停、计划已确认。数据库保存三条对应回执,最终 revision=4、epoch=approved_epoch=2;三次模型请求依次读取、携带完整快照确认、回复,旧两条要求与新增多行完整保留且仅追加一次。此界面用例确认方案但未指定通知目标;队友实际续办由运行器的独立门控和重启回归证明,不能把空目标的界面验收当作成员已执行。
|
|
32
|
+
|
|
33
|
+
1280×720 浅色桌面与 390×844 深色手机均无页面或对话框横向溢出,浏览器无 JavaScript 错误。临时页面、假服务与进程均已关闭回收;无产品修复依赖更改真实配置。
|
|
34
|
+
|
|
35
|
+
验证使用假模型、事件门控、临时 SQLite 和本地浏览器服务。没有使用真实模型额度、操作用户桌面或安装/重启用户服务。活动 DAG 中途暂停、自然语言限制的形式化证明、已执行副作用回滚均不属于本版保障。远端 CI、签名标签、PyPI 文件与 GitHub Release 必须逐项核实;当前不宣称 2.3 已发布。
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# 任务控制管理接口
|
|
2
|
+
|
|
3
|
+
本接口随 2.3 提供,不属于 2.2 的能力。领域合同见 [任务控制](task-control.md),执行保障见 [运行边界](task-control-runtime.md),整合及发行验证见 [2.3 审查](review-v2.3.md)。
|
|
4
|
+
|
|
5
|
+
管理接口沿用密码认证、同源请求、JSON 请求体及现有请求大小限制。控制来源和持续主会话身份由服务确定,客户端不能提交 `principal`、`session_id` 或 `approved_epoch` 等字段。`expected_epoch` 必须是非负整数,不接受布尔值或数字字符串。
|
|
6
|
+
|
|
7
|
+
## 读取当前状态
|
|
8
|
+
|
|
9
|
+
`GET /api/commitments/{id}` 返回完整账本、任务说明,以及当前 `control` 和 `scope`。详情保留计划正文 `control.plan`;列表和写操作的返回投影只含紧凑状态,不把全文广播到每个团队成员。
|
|
10
|
+
|
|
11
|
+
`scope` 提供 `member_count`、`active_workflow_count`、`can_pause` 与 `blocked_reason`。范围由服务端计算,覆盖绑定该账本的全部队友及后代,不受网页分页影响。`workflow_active` 表示有活动工作流,当前版本需先停止它或等待完成;暂停请求不会只暂停一部分再报告成功。
|
|
12
|
+
|
|
13
|
+
## 暂停与交主沐璃重新规划
|
|
14
|
+
|
|
15
|
+
`POST /api/commitments/{id}/control` 接受两种明确操作:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{"action":"pause","expected_epoch":0,"request_id":"这次操作的稳定标识"}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{"action":"request_replan","expected_epoch":1,"request_id":"另一次操作的稳定标识","additional_requirements":"只读核查,保留原文件"}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
暂停不需要模型配置,也不自行创建重规划输入。`request_replan` 要求任务已经暂停;补充文本完整追加到原要求,空文本表示按现有要求重新规划。没有主模型或入站容量不足时,原暂停和原要求保持原样。
|
|
26
|
+
|
|
27
|
+
成功响应为 `{receipt,current}`。`receipt` 是该操作原始回执,`current` 是返回时的 `{id,revision,control,scope}`。重复同一请求不重复追加要求或入站;较新操作发生后重试旧请求,仍返回原回执与最新投影。不能把旧回执的 epoch 当成当前状态。
|
|
28
|
+
|
|
29
|
+
网页以账本 `revision` 单调接收状态;同一 epoch 的计划确认也推进 revision。紧凑响应缺少 `plan` 或 `brief` 正文不表示删除,需读取详情补齐,不能将旧正文标为新修订。请求断线后复用原 `request_id` 和原请求体确认结果,不自动生成另一项操作。
|
|
30
|
+
|
|
31
|
+
## 可恢复的错误
|
|
32
|
+
|
|
33
|
+
控制领域错误使用 `detail: {code,message,current?}`;`message` 用于显示,`code` 用于判断,能够读取当前事实时附带 `current`。
|
|
34
|
+
|
|
35
|
+
| 状态 | 常见原因 | 客户端处理 |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| 400 | 纯空白请求标识、暂停夹带要求或完整上下文超出配置预算 | 修正请求;原事实不变,不当作部分成功 |
|
|
38
|
+
| 401 | 尚未登录或认证过期 | 清空控制详情和草稿,回到登录门 |
|
|
39
|
+
| 403 / 415 | 非同源请求 / 非 JSON 写请求 | 修正请求,不重试为新操作 |
|
|
40
|
+
| 404 | 账本不存在 | 停止该操作 |
|
|
41
|
+
| 409 | epoch 冲突、请求标识内容冲突、尚未暂停或存在活动工作流 | 读取当前事实,保留未提交草稿 |
|
|
42
|
+
| 422 | 字段类型、取值或额外身份字段无效 | 修正请求,不当作提交成功 |
|
|
43
|
+
| 429 | `queue_full`,重规划入站没有容量 | 既有暂停保持,等待后重试 |
|
|
44
|
+
| 503 | `model_unconfigured` 或领域事实暂不可用 | 显示具体原因;未配置模型时先配置,任务仍暂停 |
|
|
45
|
+
|
|
46
|
+
无法确认提交结果的网络错误或不明确的服务错误,不能推断成功或失败。网页关闭不恢复任务,模型的一句“已恢复”也不是确认记录。
|
|
47
|
+
|
|
48
|
+
接口回归使用临时数据库和本地 ASGI 请求,覆盖认证、跨站请求、严格类型、原回执与最新投影、队列满原子失败、主人来源、计划全文及工作流冲突。主模型运行、任务停靠、最后工具边界和通知恢复由独立运行器测试验证;不能用 HTTP 200 代替执行保障。
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# 任务控制的运行边界
|
|
2
|
+
|
|
3
|
+
任务控制复用完整 task brief、SQLite 控制回执和原有团队调度。结构化 owner 操作只有 `pause` 与 `request_replan`;普通聊天不做意图分类,不自动暂停所有任务。计划、队友报告和系统注意事项都不是新的主人授权。持久事务与通知证明见 [task-control.md](task-control.md)。
|
|
4
|
+
|
|
5
|
+
## 宿主接线
|
|
6
|
+
|
|
7
|
+
`LocalServices` 创建 `TaskControl(runtime.store, commitments)`,与 `runtime.task_control` 共用同一实例。没有 LocalServices 的独立 Runtime 默认不启用控制,保留原接口。
|
|
8
|
+
|
|
9
|
+
- `await Runtime.submit_task_control(identifier, request)`:在配置锁内提交 owner 操作,复核仍在收尾的 workflow handles,停靠尚未执行的队友;事务完成后通知事件与调度器。调用方负责认证真实 owner。
|
|
10
|
+
- `Runtime.task_control_current(identifier)`:同步返回持久控制与当前活动 workflow 的统一投影,供详情和冲突响应使用。
|
|
11
|
+
- `await Runtime.task_control_committed(result)`:事务成功后的共同通知入口;主模型确认方案也使用它。内部提交事实与公开 `result.public()` 不混用。
|
|
12
|
+
|
|
13
|
+
纯 pause 只登记事实,清除旧重规划意图,不增加 main 输入或自动调用模型;无模型配置也可暂停。`request_replan` 明确登记主人重新规划的意图,允许追加完整要求;原始要求不被计划覆盖。启动、确认、输入容量释放和实际完成事件驱动通知投递,没有定时模型轮询。每页受 `limits.max_pending_runs` 限制,越过满邮箱继续扫描;重入事件会请求后续一轮处理,不丢失唯一容量释放唤醒。
|
|
14
|
+
|
|
15
|
+
## 同一请求中的完整快照
|
|
16
|
+
|
|
17
|
+
`PreparedRequest` 分开保存 `execution_scope` 与 `selected_replan`。执行队友和 workflow 节点从真实持久父链继承 scope;主会话仍可协调不同任务,选择待规划任务不会把它绑定成该任务的 worker。
|
|
18
|
+
|
|
19
|
+
构建请求时一次同步读取完整 brief、epoch、approved_epoch 和方案,再调用异步 context hooks。最终加入不可剪裁的 system 资料,明确标为协调资料。等待 hooks、摘要或模型名额期间不把新 epoch 贴到旧正文上;已构建请求仍使用原快照。完整正文、最新输入和原生回放超限则明确报错,不用摘要替代限制。停车只检查轻量控制事实,热缩上下文预算不会把已暂停身份误记为 failed。
|
|
20
|
+
|
|
21
|
+
主助手先通过 `commitment_read` 读取有当前 owner 重规划意图的任务,选择下一次模型请求的完整快照,再调用 `task_approve_plan(observed_epoch, plan, targets)`。同一原生工具批次中刚读取不能立刻确认;猜中持久 epoch 也不能替代宿主实际展示的快照。确认成功或 owner 意图失效后清除临时选择。targets 必须显式列出同范围的未完成队友,空列表不唤醒队友。
|
|
22
|
+
|
|
23
|
+
确认工具仅持续 main 的实际 session 可用。schema 依据真实 session 单独生成,handler 再核验 caller;工作流中角色名为 main 不升级权限。每次工具入口覆盖 ContextVar 并在正常、异常和取消后 reset,`create_task` 继承来的 main 上下文不能代替实际 worker 身份。
|
|
24
|
+
|
|
25
|
+
## 工具门禁与停靠
|
|
26
|
+
|
|
27
|
+
模型请求在进入模型及取得并发槽后复核执行快照。副作用工具在审批前检查,并在最后一次 await(包括 `tool.started` 发布)后同步再检查,然后直接进入 handler。旧 epoch 的写操作,即使新 epoch 已获确认也不能执行。原有权限与独立审查仍然生效;read 工具可完成,已进入 handler 的操作允许收尾,不声称撤回或回滚已发生的副作用。
|
|
28
|
+
|
|
29
|
+
遇到暂停或 stale 请求时,本批未执行调用逐一保存:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{"status":"not_executed","code":"task_paused","reason":"暂停原因","observed_epoch":0}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
assistant 的原生 tool_calls、参数、provider_data 和全部 tool results 保持完整配对,之后抛 `TaskPaused` 结束本次运行。它不是普通工具失败,也不继续生成成功回复。每个未执行调用发一次 `tool.completed`;除兼容的结果预览外,事件独立保留 `status=not_executed,code=task_paused`,不依赖被截断 JSON 判断状态。
|
|
36
|
+
|
|
37
|
+
Runtime 保存 `run.status=paused`、`pause_reason`、`observed_epoch`,发布 `run.paused`。团队在同一事务写该代 `team.finished(status=paused)`、completion_revision、agent 和报告。暂停是本次执行的终态,不是任务已完成;实际协程与模型名额释放,不挂一个等待批准的任务。暂停身份仍占未完成任务预算、出现在 active 列表,wait 可读取这一代结果。
|
|
38
|
+
|
|
39
|
+
暂停范围内的所有报告保留 pending,包括在最后执行检查后才完成的报告,不能自动唤醒主模型重新规划。其他 scope 的报告通过有界 keyset 分页继续交接。长期暂停不算活动执行,不因此永久拦住只读系统注意事项;注意事项仍无 owner 权限,不能解除暂停。主人明确请求重新规划、主助手确认后,再由真实事件投递积压报告。
|
|
40
|
+
|
|
41
|
+
## 可靠续办与恢复
|
|
42
|
+
|
|
43
|
+
`send` 可以有界写入暂停队友的邮箱,`resume` 不清除 control;纯 pause 下这两个入口均不能启动新模型。暂停范围也不能派生新队友。显式 stop 可取消暂停身份,并保留 2.2 固定停止批次与后来新代报告的行为。
|
|
44
|
+
|
|
45
|
+
计划通知必须同时匹配目标 scope、当前批准 epoch、generation 和 completion_revision。目标已停靠且普通邮箱满时,事务直接存档唯一真实内部通知并置 queued,不扩容或删除原 pending 输入。通知先入运行中邮箱、旧请求随后停靠时,完成边界同步核验真实通知,再以一次终态提交写 paused 日志和 queued 身份;不产生第二个 completion_revision。普通消息、取消后的旧通知、旧代或伪造来源均不能续办。
|
|
46
|
+
|
|
47
|
+
暂停与通知事实落盘,重启保留 paused 身份、原邮箱与报告;启动事件恢复可靠通知,不自动重做未知副作用。通知已投递只证明资料到达,不代表工具已执行或任务完成。
|
|
48
|
+
|
|
49
|
+
首版不支持活动 DAG 中途暂停。同 scope 存在 queued/running DAG,或状态已结束但实际收尾 task 尚未完成时,pause 返回 `workflow_active`,不改变控制事实。DAG 首次 await 前同步检查 scope 并登记运行、节点继承绑定;旧节点通过普通执行入口启动也不能逃离门禁。已批准范围可启动新 DAG,独立 DAG 不受无关暂停影响。
|
|
50
|
+
|
|
51
|
+
`tests/test_task_control_runtime.py` 使用临时 SQLite、假模型、真实 Runtime/Team/AttentionInbox 与 Event 门控验证以上边界,包括原生批次、最后 await、快照冻结、继承身份、满箱恢复、先投递后停靠、报告公平交接和真实系统注意事项。没有调用真实模型、网络或设备。
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# 任务控制持久层(2.3 开发中)
|
|
2
|
+
|
|
3
|
+
本页描述 `muli.task_control` 的同步领域层。它已经保存 owner 操作、主助手确认和可靠通知所需事实;**单独安装这一层不会暂停模型或工具,也没有新增可用的 Web 按钮/API**。Runtime 的安全边界、团队调度、workflow 准入和认证端点必须接线并通过对应测试后,才能声明任务控制生效。
|
|
4
|
+
|
|
5
|
+
它复用 SQLite objects、events、ingress 和 message_order,不启动模型、后台 worker、网络请求或定时重试,不增加依赖。所有方法同步完成,事务内没有 await。宿主负责真实身份认证、`configuration_lock`、配置预算和提交后的事件通知/唤醒。
|
|
6
|
+
|
|
7
|
+
## 持久状态与范围
|
|
8
|
+
|
|
9
|
+
旧 commitment 没有 control 时按 epoch=approved_epoch=0 读取,不迁移重写全部历史。首次 pause 建立:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"epoch": 1,
|
|
14
|
+
"approved_epoch": 0,
|
|
15
|
+
"replan_request_id": null,
|
|
16
|
+
"plan": null
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
epoch 大于 approved_epoch 表示未获当前计划确认。既有 commitment.revision 用于所有展示修订;进度编辑不改变 epoch,批准同一 epoch 仍推进 revision。纯 pause 清除 replan_request_id,不产生模型输入;没有当前真实 owner request_replan 回执,main 不能批准。
|
|
21
|
+
|
|
22
|
+
受控 commitment 的完整 brief 不再接受 generic `commitment_save` 替换。该入口仍可更新进度和证据描述,并完整保留 control;要求只能由 owner 控制入口追加。确认只写 plan,不改 goal/constraints/evidence_refs。原始要求不因压缩、计划文字或协作消息而撤销;本层不证明自然语言计划在语义上满足每条要求。
|
|
23
|
+
|
|
24
|
+
scope 包含同一 commitment 的所有根队友和后代。`resolve_scope(session_id)` 沿真实 agent/workflow 父链解析,workflow 节点通过 session.workflow_run_id 查运行,兼容没有显式 commitment_id 的旧 workflow。父链缺失、绑定冲突或环明确拒绝,不降级成无范围。
|
|
25
|
+
|
|
26
|
+
首版不实现活动 DAG 中途暂停:每次 pause 都自行在事务中查询持久 pending/queued/running workflow;宿主传入的 active_workflow_ids 仅补充仍在收尾的实际 handles,不能替代持久查询。该 scope 有活动 DAG 就返回 workflow_active,状态完全不变。当前 epoch 获批准后可以开始新 DAG;B 接线必须在首次 await 前持久登记继承绑定和活动运行,并在节点请求/工具调用上使用同一 scope/epoch 门禁。独立 DAG 不受无关任务控制。
|
|
27
|
+
|
|
28
|
+
## 同步接口
|
|
29
|
+
|
|
30
|
+
构造 `TaskControl(store, commitments)`,二者必须共用 Store。调用方只向真实已认证 owner 暴露 owner_control;Python 插件是用户信任的完整本地代码,本接口不是恶意插件沙箱。
|
|
31
|
+
|
|
32
|
+
| 方法 | 合同 |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `owner_control(id, OwnerControlRequest, *, main_session_id, mode, model_configured, max_pending, active_workflow_ids=())` | pause/request_replan 短事务;返回 ControlResult;不通知或调用模型 |
|
|
35
|
+
| `current(id, *, active_workflow_ids=())` | `{id, revision, control, scope}` 紧凑投影;control 不含 plan 正文,正文保存在 canonical commitment |
|
|
36
|
+
| `execution_snapshot(session_id)` | 返回真实执行范围的不可变 TaskSnapshot;无绑定的持续 main 返回 None |
|
|
37
|
+
| `replan_snapshot(main_session_id, id)` | 只为实际持续 main 和有效 owner 重规划意图提供选中快照 |
|
|
38
|
+
| `is_current_replan(snapshot)` | 选中快照已获批准、被新的 pause/replan 替换或事实失效时返回 False;宿主必须清除临时选择 |
|
|
39
|
+
| `guard_execution(snapshot, *, session_id, risk)` | read 不受 epoch 暂停门禁阻断;副作用要求快照及当前 epoch 均已批准且相同;本方法不代替原有权限检查 |
|
|
40
|
+
| `approve_plan(snapshot, observed_epoch, plan, targets, *, caller_session_id, max_targets)` | 真实 main、宿主已展示快照、当前 owner 意图及 epoch 校验;确认与目标 outbox 同事务 |
|
|
41
|
+
| `pending_deliveries(limit, *, after_id=None)` | 按稳定 ID 有界分页;消费者可越过暂时满箱的目标,不能总从首项重试造成饥饿 |
|
|
42
|
+
| `deliver(id, *, max_pending)` | 同步检查并投递一条计划通知;返回 DeliveryResult,不自行启动任务 |
|
|
43
|
+
| `resume_notified_target(target_session_id)` | 只读核验同代次当前计划的真实通知;供 Runtime 在已判 paused 的完成边界决定是否立即排入新请求 |
|
|
44
|
+
|
|
45
|
+
`OwnerControlRequest(action, expected_epoch, request_id, additional_requirements="")` 只支持 pause/request_replan,expected_epoch 不接受 bool。request_id 使用现有入站 ID 长度边界。pause 不能夹带修改要求;request_replan 可追加一条完整非空要求或空文本重新规划。
|
|
46
|
+
|
|
47
|
+
`ControlConflict` 与 `ControlUnavailable` 都提供 `code` 和纯中文 `message`;str(exception) 同时包含 code,供日志使用。HTTP 层建议映射为 `detail={code,message,current?}`,不得把纯字符串错误误当控制已提交。
|
|
48
|
+
|
|
49
|
+
`TaskPaused` 是专用领域结果,包含 `observed_epoch`。Runtime 接线必须先给同一模型批次尚未执行的 call 写完整 not_executed results,再将 run/agent 停靠;不得被普通工具错误捕获后继续生成“成功”,也不得套用失败分支递归取消所有后代。
|
|
50
|
+
|
|
51
|
+
## 请求快照与权限
|
|
52
|
+
|
|
53
|
+
`TaskSnapshot` 保存 session_id、commitment_id、revision、epoch、approved_epoch、replan_request_id,以及不可变序列化的 brief/plan。`context()` 从同一快照输出完整说明和明确的非授权提示;不在 context hook await 后给旧正文换上新 epoch。
|
|
54
|
+
|
|
55
|
+
Runtime 必须区分 `PreparedRequest.execution_scope` 和 `selected_replan`。main 本身仍是跨任务协调者,选中一个暂停任务不能使主助手的确认工具和其他任务协调被通用执行门禁误拦。选中状态只供当前 main 请求;批准或 owner 意图失效后清除,避免旧的大 brief 占用无关主聊天预算。
|
|
56
|
+
|
|
57
|
+
执行请求在进入模型前和取得 capacity 后检查 scope;副作用工具在审批前检查一次,在所有 await(包含 tool.started 事件)之后、进入 handler 前再同步检查一次。旧请求的 call 即使晚于新 epoch 确认返回,也不能执行。`guard_execution(..., risk="write")` 可用于非只读模型轮次准入;主协调的 selected_replan 单独在 approve_plan 内验证。
|
|
58
|
+
|
|
59
|
+
scope binding、main 身份和请求快照均来自宿主。模型仅提供 observed_epoch,不能借猜中当前整数替换它实际看到的旧快照。ContextVar 的设置与 finally reset、真实 caller 校验、工具 schema 和 handler 二次防护由 Runtime/LocalServices 接线负责。
|
|
60
|
+
|
|
61
|
+
## 原子操作与幂等
|
|
62
|
+
|
|
63
|
+
控制事务使用 BEGIN IMMEDIATE,不允许嵌套在另一个提交事务中;失败完整回滚。顺序为真实宿主身份检查、规范化请求、回执查重、CAS/容量/预算检查、完整提交。回执命中早于 expected_epoch CAS,也早于新配置下的容量/模型可用性检查。
|
|
64
|
+
|
|
65
|
+
- pause:epoch+1、清 replan_request_id、回执、commitment_revision 与紧凑事件同事务。模型未配置、上下文预算热缩或旧证据文件已删都不妨碍已有 brief 任务的纯暂停。
|
|
66
|
+
- request_replan:要求当前已暂停;主模型已配置、完整上下文预算及 main ingress 容量允许后,追加要求并 epoch+1,将 owner 回执、修订、可恢复 main 输入和事件原子保存。输入的 route=null,不广播旧渠道;受保护 task_control 引用随 claim 保留。
|
|
67
|
+
- approve_plan:同一次 owner 请求的确定方案只确认一次;approved_epoch、plan、修订、确认回执与每个目标的 task_plan_delivery 同事务。targets 去重并受宿主配置预算约束,只能是同 scope 的未完成队友。
|
|
68
|
+
|
|
69
|
+
ControlResult 提供 receipt、current、created 和可选 ingress;`public()` 仅返回 `{receipt,current}`。旧 request_id 重试返回原始 receipt 和最新 current,不能把历史对象再写回。相同 ID 内容变化为 request_id_conflict。UI 以 current.revision 单调接收,不能只比较 epoch。
|
|
70
|
+
|
|
71
|
+
既有 `Store.enqueue_input` 仍保留其他调用者依赖的 duplicate objects 语义;控制使用无中间提交的 `_enqueue_input(..., apply_objects_on_duplicate=False)`,且先查控制回执。`_put/_event/_archive_input` 同样只在调用方事务内写入。所有公开事务完成后,宿主才 bus.notify、唤醒 main 或调度队友。
|
|
72
|
+
|
|
73
|
+
控制与确认复用 `commitment.updated` 事件,载荷只有 id/revision/epoch/approved_epoch/action/control_changed,不发送完整约束、plan 或 owner 附加文字。列表的 control 摘要先预留预算再加入标题预览,保持旧数据在热缩输出预算后仍可读取。
|
|
74
|
+
|
|
75
|
+
## 有界计划通知与停靠后的满箱
|
|
76
|
+
|
|
77
|
+
每个 outbox 保存目标 session、generation、completion_revision 与 expected_status。投递要求 scope/current approved epoch 一致,并符合以下之一:
|
|
78
|
+
|
|
79
|
+
1. 目标仍具有完全相同的代次、完成修订和状态。
|
|
80
|
+
2. 原 running/queued 目标在同 generation 停靠,当前为 paused、完成修订恰好 +1,且该唯一下一修订的持久 team.finished 确认 status=paused、generation 相同。
|
|
81
|
+
|
|
82
|
+
不使用 running agent 上可能尚未更新的 run_id。cancel 会消费完成修订,因此 queued→cancelled→queued、取消后再 paused、新代次或无完成记录的伪停靠均不能通过旧投递。
|
|
83
|
+
|
|
84
|
+
- 目标尚未 paused:有界写入既有 ingress,principal=system:task_control、route=null。满箱返回 pending、无写入,不循环重试。
|
|
85
|
+
- 目标已经 paused:原生调用应已由 Runtime 补齐,此时普通 pending 邮箱可能永远无法先腾空。对通过上述 CAS 的唯一通知,使用 `_archive_input` **原子存档真实消息、置 agent=queued、标 delivery=delivered**。不消费、删除或扩容原有 pending ingress,不新增等待协程。稍后 claim 的旧输入不改变 epoch 或批准事实;下一请求仍必须使用当前 pinned brief/plan。
|
|
86
|
+
- 新 epoch/取消/代次不匹配:标 superseded,不复活任务。
|
|
87
|
+
|
|
88
|
+
DeliveryResult 包含 state、session_id、changed、wake、ingress、message、path。path=archive 时有真实 message 存档;path=ingress 时有实际 ingress。重试已 delivered 的对象不再改 agent。宿主只在方法返回后依据 wake 调度,并区分“owner 请求已接收”“计划已确认”“通知已投递”“队友实际续办”,不能用 delivered 冒充已经执行。
|
|
89
|
+
|
|
90
|
+
另一个顺序是:目标仍 running/queued 时通知已投递,旧请求随后才被 epoch 门禁判为 paused。outbox 此时已 delivered,不能依赖 pending flusher 再唤醒。`resume_notified_target` 从当前批准事实推导唯一通知 ID,复用相同代次/完成修订校验,并核实 ingress 的真实 source、principal、route=null 与受保护 task_control 引用。若通知已被 claim,还同时检查历史消息与 ingress 的 input_seq、message seq、run_id 关联;普通输入、已取消 ingress、缺失档案或旧 epoch 不能成为续办凭据。查询使用确定 ID,不扫描全部历史。
|
|
91
|
+
|
|
92
|
+
正常调用位置是 **运行器已决定本代结果为 paused,而数据库 agent 仍为 running/queued、generation=gN、completion_revision=cR 时**。返回 True 后,宿主不得 await,立即复用一次 `_finish(..., requeue=True)`:在同一事务写 `team.finished(status=paused, completion_revision=cR+1)` 和 `agent(status=queued, completion_revision=cR+1)`。完整原生调用配对必须已在此前完成;下一次请求重新构造完整快照。这个 helper 只读,不递增修订、不建消息、不启动任务。它也能核验已持久停靠且唯一下一完成事件证明正确的状态,但正常完成路径不能先 finish 再二次 finish;已经 queued 的 cR+1 不会再次通过旧证明,避免重复续排。
|
|
93
|
+
|
|
94
|
+
消费者由启动、控制确认、输入空间释放和团队实际状态事件驱动;使用 limit/after_id 有界遍历。无状态变化时等待事件,不轮询模型或持续重试。接线层须拥有关闭/取消消费者的生命周期,不能通过取消消费者传播取消 owner/队友任务。
|
|
95
|
+
|
|
96
|
+
## 本层验证与尚待集成
|
|
97
|
+
|
|
98
|
+
`tests/test_task_control.py` 使用临时 SQLite 和故障注入验证:各写点完整回滚、真实 close/reopen、旧回执 CAS 顺序、控制说明保护、同 snapshot 约束与 epoch、主/子身份区别、活动/旧结构 DAG、满箱停靠、取消后同 generation 新意图拒绝、通知存档和排队的原子性、claim 前后的唯一续办证明、实际来源/档案关联、外部 scope 与已完成目标拒绝、输出预算热缩与不泄漏正文。
|
|
99
|
+
|
|
100
|
+
本层测试不调用真实模型或外部账号。模型流期间 pause、最后 await 后工具门禁、调用配对、Team wait/active/代次、DAG admission 竞争、API 认证和浏览器交互仍需 B 与服务端/前端集成验证。
|
|
@@ -27,6 +27,8 @@ update = await runtime.team.wait(
|
|
|
27
27
|
|
|
28
28
|
每条完成记录的 `coalesced=false,count=1` 表示一次准确的终态提交,不把多代完成合并冒充全量。一次执行结束后,即使该身份已经 queued/running 下一代,仍可读到上一代完成。generation 标识执行开始次数;completion_revision 还包括尚未开始就取消的情况,不能互换。
|
|
29
29
|
|
|
30
|
+
`paused` 也是该代执行的持久终态,能唤醒已有 wait 并出现在完成日志中,但不表示任务成功或已取消。暂停身份保留在 active 筛选并计入未完成预算,不占模型槽、不挂等待批准的协程;没有当前确认方案,普通 send/resume 不能解除暂停。暂停 scope 的报告暂缓投递主助手,已有观察者仍可读取该代日志。控制与事件恢复合同见 [task-control-runtime.md](task-control-runtime.md)。
|
|
31
|
+
|
|
30
32
|
result_preview 是节选,不是完整结果。事件最多保留 2000 字预览,响应根据 `limits.max_output_chars` 继续收紧,并明确 truncated;完整会话/运行证据仍在原存档。当前 Web API 可通过已有会话详情/事件查看历史,没有新增 `/api/runs/:id` 路由。新模式先按运行时相同的 JSON 序列化方式计算长度,保留合法对象及完整 token;元数据自身放不下时报 `wait_budget_exceeded`,要求拆分目标,不交给通用工具截断器破坏 token。
|
|
31
33
|
|
|
32
34
|
## 观察范围、输入和取消
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| `await spawn(name,prompt,parent_id=None,role='worker',mode=None)` | 原子保存身份、会话和首条任务,继承父权限;返回 `agent_id` 对应的 `id`/`session_id`。角色为 worker/reviewer |
|
|
12
12
|
| `list(limit=100,offset=0)` / `get(target)` | 按页查看;target 优先用 ID,重名时不得模糊选择 |
|
|
13
13
|
| `await send(target,content,wake=True,sender=None,event_id=None)` | 接受信封,不表示已经处理;运行中在安全边界注入;`wake=False` 只写邮箱,不唤醒结束的队友 |
|
|
14
|
-
| `await resume(target)` |
|
|
14
|
+
| `await resume(target)` | 沿用身份、模式、历史和分层摘要继续;有邮箱先处理邮箱,否则明确要求核查此前未知副作用再继续;不会解除未获当前方案确认的暂停 |
|
|
15
15
|
| `await wait(targets,timeout=30,caller=None,cursor=None)` | 不带 cursor 保留终态快照;带上次 cursor 只收新完成,支持多个独立观察者;事件唤醒、不占模型槽、检测等待环 |
|
|
16
16
|
| `await stop(target)` | 取消队友及未完成后代;首次调度前取消也清理登记状态,完成的结果不改成取消 |
|
|
17
17
|
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
|
|
24
24
|
队友结果、`completion_revision`、`team.finished` 完成事件与 `team_report` 发件意图在同一事务保存。父邮箱满时保留 pending 报告,父输入被消费、队友完成或启动恢复时再投递,同 ID 去重,不使用定时 AI 轮询。主/队友父会话收到结果后可以继续统筹;资料明确标注不是新用户指令或授权。用户显式停止和关机期间的取消汇报标记 suppressed,防止自动唤醒。
|
|
25
25
|
|
|
26
|
-
服务重启把已运行队友标为 interrupted,绝不自动重做副作用;尚未开始的 queued
|
|
26
|
+
服务重启把已运行队友标为 interrupted,绝不自动重做副作用;尚未开始的 queued 任务可恢复执行,受控暂停任务保留 paused。`send`/`resume` 是继续中断身份的明确入口,但不能绕过任务控制;未消费邮箱独立保存。暂停身份占未完成预算、不占模型名额或常驻等待协程,其结果不是成功。当前暂停 scope 的报告保留 pending,不自动唤醒主助手;其他任务的报告继续按有界分页投递。主、队友和 DAG 节点统一使用 [context.md](context.md) 中的分层摘要、原始工具配对和前缀验证;控制边界见 [task-control-runtime.md](task-control-runtime.md)。
|
|
27
27
|
|
|
28
28
|
实际只读参考了 `/data1/codex/codex-rs/core/src/agent/api.rs` 的 input/delivery receipt 区分、`agent/control/mailbox.rs` 的只投递邮箱、`agent/control/residency.rs` 的身份/驻留名额分离,以及 `tools/handlers/multi_agents/{send_input,wait,resume_agent}.rs` 的生命周期。MuLi 用事件驱动协程与 SQLite 替代 Codex 的进程/驻留控制,不照搬跨工作树机制;队友不建立独立 OS 沙箱,仍是本机权限边界下的协作任务。
|
|
29
29
|
|
|
30
30
|
`tests/test_team.py` 覆盖单并发派生等待、持久身份续办、邮箱只投递、历史数与活动预算分离、父子取消、调度前取消、原子创建、父邮箱满的持久汇报,以及等待环/停止祖先拒绝;全部离线模拟模型。
|
|
31
31
|
|
|
32
|
-
管理页通过 `GET /api/team?limit=50&offset=0&active_only=false` 分页查看,最新创建的队友在前;`limit` 为 1–100,页大小不是历史 agent 总数上限。响应包含 `members`、`messages`、`offset`、`limit`、`total`、`active_only` 和 `has_more`。“进行中”筛选包含 queued/running
|
|
32
|
+
管理页通过 `GET /api/team?limit=50&offset=0&active_only=false` 分页查看,最新创建的队友在前;`limit` 为 1–100,页大小不是历史 agent 总数上限。响应包含 `members`、`messages`、`offset`、`limit`、`total`、`active_only` 和 `has_more`。“进行中”筛选包含 queued/running/paused,审批和工具等待仍属于运行中,暂停则等待明确重新规划及当前方案确认。筛选使末页消失时返回最后一个有效页。模型 `list_agents` 保持原来的从旧到新顺序及分页方式。
|
|
33
33
|
|
|
34
34
|
这是实时列表,队友新建或结束后刷新会反映最新集合,不是固定导出快照。界面同一请求代次覆盖翻页、筛选和事件刷新,迟到响应不会跳回旧页;刷新保留当前控件焦点与协作消息的展开状态,不抢页面其它区域的焦点。每位队友仅读取最近 5 条消息;页面附带最近 100 条、各最多 1000 字的预览,`truncated` 明示节选。SQL 直接投影有界正文,避免在 Python 中加载图片和原生协议块。完整证据仍在成员的运行记录中。
|
|
35
35
|
|
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
|
|
58
58
|
首次等待前即持久化运行并登记后台任务;启动事件发布期间取消,也会取消并结算已登记的运行,不留下永远 queued 的孤儿记录。`await wait(run_id)` 等待并返回最新持久化终态,使用 shield 隔离等待者与子任务;取消等待者只结束等待,父任务应在自己的取消清理中显式调用 `cancel`。父标识本身不赋予额外权限。
|
|
59
59
|
|
|
60
|
+
受控父会话启动 DAG 时,宿主在首个 await 前同步核验本轮 scope/epoch,并把真实继承绑定保存到运行;节点不能借角色名称、普通会话入口或缺少 agent 行逃离该范围。首版不支持活动 DAG 中途暂停:仍有执行或收尾 task 时拒绝 pause;DAG 完全结束后才可暂停该任务树。独立 DAG 不受无关任务控制。
|
|
61
|
+
|
|
60
62
|
`await cancel(run_id)` 取消运行、正在执行和排队的节点,并将未启动的节点标记取消。已完成的运行保持结果,重复取消幂等。`await close()` 取消所有活动工作流并拒绝新启动。
|
|
61
63
|
|
|
62
64
|
## 调度与状态
|
|
@@ -31,6 +31,7 @@ async def check_installed(target: Path) -> None:
|
|
|
31
31
|
for path in (
|
|
32
32
|
"/",
|
|
33
33
|
"/app.mjs",
|
|
34
|
+
"/task-control.mjs",
|
|
34
35
|
"/assets/muli-avatar.svg",
|
|
35
36
|
"/assets/muli-empty.svg",
|
|
36
37
|
"/api/docs",
|
|
@@ -41,7 +42,18 @@ async def check_installed(target: Path) -> None:
|
|
|
41
42
|
assert state["version"] == muli.__version__
|
|
42
43
|
assert state["configured"] is False
|
|
43
44
|
assert app.state.runtime._history("new")[0].content
|
|
44
|
-
|
|
45
|
+
task = await app.state.local_services.commitments.save(
|
|
46
|
+
"发行包任务控制检查",
|
|
47
|
+
brief={"goal": "验证离线管理入口", "constraints": [], "evidence_refs": []},
|
|
48
|
+
)
|
|
49
|
+
response = await client.post(
|
|
50
|
+
f"/api/commitments/{task['id']}/control",
|
|
51
|
+
json={"action": "pause", "expected_epoch": 0, "request_id": "wheel-check"},
|
|
52
|
+
)
|
|
53
|
+
assert response.status_code == 200, response.text
|
|
54
|
+
assert response.json()["current"]["control"]["paused"] is True
|
|
55
|
+
assert not app.state.runtime.tasks
|
|
56
|
+
print("Installed wheel: import, assets, API docs, prompts, task control and lifespan passed")
|
|
45
57
|
|
|
46
58
|
|
|
47
59
|
def main() -> None:
|