feat(chat): owner private project admission with native queue and recovery - #5541
huangruiteng wants to merge 3 commits into
Conversation
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | reasoning_effort=xhigh
动机
想从个人 App 私聊讨论本机工作区的人,需要把消息接入已有会话队列,并能看清目标、排队与恢复状态。
此前只能从本机项目对话进入;当前设置页可选择已验证 App、授权工作区和执行器,私聊文字复用原生队列,并支持状态、停止和新会话命令。
合成 App 的打包设置旅程已验证连接、工作区撤销提示、恢复、重新加载和解绑;旧群锁键与无私聊配置的鉴权路径仍有回归,Core 归属检查也失败。
本次不声称已替换真实 Bot,不验收媒体、实时模型账户或长期管家职责;上游项目对话仍需独立修复。
当前 head 的三项阻塞需要修复;真实私聊、安装服务和上游依赖资格尚未完成。
改动思路
共享授权由 TypeScript 的 binding/context 规则决定;Python 负责原子存储、HTTP 和 provider IO。请求先持久化 scoped identity,再关联已有 Chat Session/Turn 队列;准入 ACK 与最终回复分别记录并验证。设置页复用现有 Lark 入口。这个归属合理,但 provider HTTP glue 当前错误地留在 Core,必须修复。
具体改动
评审绑定 5541@49562a4a77206c7af333365906bce67a62c6e872,精确 base 为 1e0bedc160a5db421741cf26ff6cc8830e8fd51c。全 diff 共 29 个文件;包含未改动的周边调用路径一起检查,未沿用旧批准。
关键代码讲解
loopx/control_plane/collaboration/conversation_binding.ts:65的planConversationBinding / resolveBoundConversation:Own typed exact binding decisions。输入为 Revision-CAS configure and exact source;关键约束是 No private payload can manufacture authority; context change creates fresh binding。经 Python atomic binding IO adapts typed result 交给 Bound native Session context,失败由 Missing/revoked/stale scope rejects; no alternate Goal 处理。loopx/capabilities/native_chat/external_conversations.py:22的ChatExternalConversations.admit / _admit_prepared:Durable request correlation into existing queue。输入为 Verified source/request content + current binding;关键约束是 Same scoped client identity maps to one native Turn。经 ChatSessionStore + runtime.enqueue 交给 Exact receipt/status/recover,失败由 Conflicting content denied; prepared retry resolves existing Turn 处理。loopx/extensions/lark/private_conversations.py:104的LarkPrivateConversations.admit / reconcile:Provider source verification and separate reply effects。输入为 Fresh App/sender/chat + original source message;关键约束是 No group/unknown sender/media fallback into owner scope。经 Controller.external_conversations; real reply verification journal 交给 Original private source admission/final reply,失败由 Unknown delivery remains recoverable/held, not blind resend 处理。loopx/extensions/lark/goal_topic_runtime_service.py:249的LarkGoalTopicRuntimeService._poll_profile consumer key:Single consumer fence。输入为 Existing enabled legacy group, no private rows, old owner alive;关键约束是 One machine/App/source consumer must own stream。经 try_exclusive_file_lock -> stream callback 交给 Listener health/status,失败由 Current key bypasses old lock; standby not honored 处理。apps/presentation/dashboard/src/features/personal-workspace/private-conversation-panel.tsx:8的PrivateConversationPanel:Packaged owner setup/readback。输入为 Explicit selection and fresh backend list;关键约束是 Truthful unavailable state, no hidden Goal。经 Private HTTP routes and native binding store 交给 Desktop/narrow UI and persisted binding,失败由 Revoked workspace visible; restore/reload recover same binding 处理。
规范基准:docs/architecture/rfcs/app-conversation-and-async-inbox-v0.md @ 1e0bedc160a5db421741cf26ff6cc8830e8fd51c,逐项判断如下。
- Ordinary workspace conversations: bounded implementation checkpoint:Ordinary local-owner read scope must not implicitly create a Goal or portfolio authority. →
implemented。Real native Session/store tests and packaged configuration readbacks; source context is owner-only workspace_read. - Acceptance:Lost response after durable accept must recover the same scoped request with one logical Turn/provider call; conflicting content cannot borrow identity. →
implemented。Private conversation native tests include duplicate/prepared recovery/conflict/queue full and restart with real store, external model protocol doubled. - Dispatch:ACK records durable queued admission rather than claiming model execution. →
implemented。Existing queue/status/cancel/new source tests; receipts separate admission and final delivery. - Scope:Return/update remains exact original source/session/Turn; independent consumer ownership must not escape existing scope. →
not_met。Real native POSIX lock pair: base stays standby; head reaches stream callback while old owner is alive.
对主干的风险
1. [P1] loopx/extensions/lark/goal_topic_runtime_service.py:252
触发:Existing group binding has no bot_app_id and private bindings are empty; a prior listener still holds sha256(profile) native lock.
观察:New raw-profile fallback acquires a different file lock and starts the stream path while the existing consumer owner is still running.
最小修复:Preserve sha256(profile) for legacy group fallback. Use consumer_ref only for explicit verified private bindings, or implement/prove an atomic single-owner migration.
回归:Hold the old key in a separate process and run real _active_profile_configs/_poll_profile at base/head with zero private bindings; current head must stay standby with no stream start.
2. [P2] loopx/chat_lark_api.py:593
触发:POST existing group connection preview or malformed request while conversation_bindings exists but contains zero rows.
观察:Valid preview body remains identical but auth status --verify calls increase1→2; malformed missing required fields now performs1 auth call instead of0 before the same400 validation.
最小修复:Validate structural/Goal requirements before provider IO; run private collision guard only when relevant private bindings exist and reuse already-required App observation.
回归:Replay real HTTP with empty private bindings: valid preview full body and auth count match base; malformed shape must reject before auth. Also cover real private binding and alias collision.
3. [P2] loopx/capabilities/native_chat/private_conversation_api.py:27
触发:Core native_chat private HTTP endpoint is imported in the shipped handler.
观察:Core imports Lark extension runtime/config and identity helpers; native architecture ownership test fails at head and passes at base.
最小修复:Move provider-specific private HTTP composition to the Lark extension; keep generic binding/session owner in native_chat and wire the handler through chatserver.
回归:uv run --extra test python -m pytest -q tests/architecture/test_control_plane_import_boundaries.py::test_lark_projection_sinks_are_owned_by_the_extension_layer; rerun real private HTTP and packaged configuration journey.
证据边界:锁键比较使用真实 native file lock 与独立旧 owner 进程,stream callback 是观察替身;这证明新 consumer 准入越过旧 fence,未声称已产生真实 Lark 双重收信。HTTP 比较走实际服务、Core 与 store,只有外部 CLI IO 为替身;合法 preview 与 malformed request 的完整响应一致,但 auth 次数分别从1→2、0→1。Core import guard 在 base1 pass,在 head 列出两个 forbidden edges。
验证:changed/native组104 passed/1 architecture failure;相邻与语义193 passed;typed binding4 passed;TS、Ruff、diff和打包 build通过,已有 bundle chunk warning保留。实际打包设置页完成 connect → root revoked → restore → reload → disconnect,原生读回验证 binding 复用与解绑;桌面和390×844视口显示同一目标和可行动状态。合成 App、模型协议和独立临时 workspace 不证明真实已安装 listener/model资格。
语义与 CI 对齐
本次依 capability 当前 policy revision18 完成 typed ownership、domain neutrality、behavior disclosure、guidance/obligation、default-off 和 authority lenses。changed-diff advisory 的空结果不覆盖动态构造;共享规则按 native checks 与反例判断。未查询、轮询或等待远端 CI,结论来自本地当前 head 的必要证据。
我的整体评价
原生队列和打包配置是一项可用的有界增量,重复准入、撤销和恢复有本地证据。长期 consumer ownership 与未开启私聊的群路径发生回归,不能以新功能正常掩盖;Core/provider 归属也需要压回现有 owner。上游#5540是独立依赖,其旧结论不替代本 PR判断,依赖更新后还需重新核验。
Future-facing pass:Useful related refactor now required: keep generic binding/session authority in TS/native_chat and move provider HTTP glue to Lark extension. Reuse existing queue and reply verification; do not add another consumer/process manager. No broad migration proposed.
English verdict: REQUEST_CHANGES. Repair the private-disabled legacy consumer lease and extra auth regressions, and move provider HTTP imports out of Core; successful private/UI paths do not erase those blockers. No CI wait or merge requested.
Private Chat has no ordinary project binding, and waiting for a model answer in the event handler delays follow-up admission. This increment adds Settings → Lark → Owner private Chat: independently verify one non-default App and its owner, select an authorized read-only workspace and executor, and read back listener/pending/recovery state.
The shared typed Core owns the App/audience grant, exact Session context, native Turn FIFO and stable request/stop targets. The provider supplies current provenance, reads the exact canonical source, then journals admission and delivery separately. Replies use the existing Inbox transport and provider readback. A slow Turn no longer occupies the private-message listener; later text persists in the canonical queue and another App proceeds independently. App-scoped leases reuse the existing service. Context replacement/revocation, conflicting redelivery and unknown senders fail closed; an ambiguous send without a receipt is retained for recovery instead of blindly resent.
Commands are
/status,/stopand/new; setup also offers exact-revision disconnect. Rendered lark-cli text is preserved, including literal JSON text. Unsupported attachments receive an explicit response without a model Turn. No hidden Goal, new model runner, replacement scheduler or provider-owned Session authority is introduced.Dependency: #5540, with base
codex/personal-native-bots-20261004at1e0bedc160a5db421741cf26ff6cc8830e8fd51c. Only this successor diff is proposed here. Unmerged #5538 is neither copied nor a dependency. After #5540 merges, retarget this PR to main and requalify the resulting exact head. Runtime/authority changes remain for maintainer review and merge.Validation on this source content:
manager_waits_for_actual_turn_in_its_own_audience_sessionandreal_chat_entrypoint_serves_while_binding_discovery_is_blocked) reproduced on unchanged exact main99839aeb8. They remain failures, not waived checks.Scope still open: registered Agent selection, steward portfolio/long commissions, real incremental output, host media and permission callbacks. Both bindings in the canary are ordinary project conversations. This slice does not claim the complete two-role Bot outcome or promote an installation; #5540 has unresolved Python CI and a separate Release Artifacts browser timeout.
Current-head CI follow-up: the
49562a4a77206c7af333365906bce67a62c6e872Python run finished failed (four test shards, pytest aggregate and merge gate; 74 failure entries). The newly introduced Lark projection import-boundary regression is fixed and locally validated in stacked successor #5542 by moving provider-only setup into the extension. The remaining failures are not all attributed, and this is not a CI waiver. #5542 also completes the bounded, explicitly confirmed native steward commission slice. Neither branch is promoted or self-merged.