Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/architecture/rfcs/app-conversation-and-async-inbox-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -2055,6 +2055,16 @@ unfinished declaration; later native inputs must be correlated to Core. This
observation does **not** certify settled effects. A failed or signal-terminated
command cannot acquire replay authority through this route.

One legacy source without typed unfinished work can also receive its own result
review when an earlier, distinct source with exactly the same question, Session,
binding and audience already has a qualified, delivered result review completed
after the legacy answer. Core pins that canonical evidence and the legacy native
final, not a prose failure classifier. The sibling answer never counts as delivery
to this source, supplies an antecedent, settles effects or creates missing work
intent. The original worker independently checks current artifacts and returns
one read-only answer under this source's own idempotent identity. A legacy review
cannot seed another legacy review; changed evidence and exhausted attempts hold.

A new result review now requires an explicit observation that no original
writable adapter is attached. An inconclusive effect readback while that owner
is still attached remains a hold; it must not replace the owner with a model
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,13 @@ completed Turn 继续明确保留,本路径不提供通用动作或执行重
原生最终答复和未完声明,后续原生输入也必须能关联到 Core。该观测**不证明效果
已经结清**;失败或被信号终止的命令不能借此取得重放权限。

没有 typed 未完声明的旧请求也可获得一次自己的结果复核,但只限同一 Session、
binding 和受众下,更早的独立来源提出完全相同的问题,且其合格结果复核在该旧
答复之后完成并已投递。Core 固定这项规范证据与原生最终答复,不按失败措辞分类。
另一条答复不算本条已收到,也不能补充原指代、结清效果或制造缺失的业务意图。
原 worker 独立检查当前产物,沿本条原消息自己的幂等身份补回一次只读答案。
旧请求复核不能再作为其他旧请求的准入依据;证据变化或次数用尽继续保持等待。

新的结果复核现在要求明确观测到没有挂接原可写 adapter。原进程仍挂接时,效果
读回暂时没有结论,应保持等待,不能为复述已投递的部分答复而用模型复核替换
原进程。已有回复 worker 的探测等待间隔继续生效;后续取得新鲜效果结清证明后,
Expand Down
14 changes: 10 additions & 4 deletions loopx/capabilities/native_chat/codex_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ def turns():


def completed_result_review_readback(session: CodexChatAgentSession, turn_id: str, *,
known_turn_ids: set[str]) -> dict[str, Any] | None:
known_turn_ids: set[str], allow_untyped: bool = False) -> dict[str, Any] | None:
"""Read a closed partial result, never certify its effects for execution.

A failed command (including a signal exit) is evidence to inspect, not
Expand Down Expand Up @@ -543,13 +543,15 @@ def turns() -> list[dict[str, Any]]:
return None
if users != 1 or len(finals) != 1:
return None
work = parse_agent_response(finals[0], protected_paths=[session.work_dir]).get("unfinished_work")
if (work is None or turns() != inventory
response = parse_agent_response(finals[0], protected_paths=[session.work_dir])
work = response.get("unfinished_work")
if (work is None and not allow_untyped or turns() != inventory
or list(session._control_items("thread/items/list", {"turnId": turn_id}, deadline)) != items):
return None
return {"thread_id": session.thread_id, "turn_id": turn_id,
"read_only_review": True, "completed_result_review": True,
"unfinished_work": work, "visible_response": finals[0],
**({"untyped_result_review": True, "response": response} if work is None else {}),
"native_inventory_sha256": hashlib.sha256(json.dumps([target, items],
sort_keys=True, separators=(",", ":")).encode()).hexdigest()}
except (CodexChatAgentError, OSError, TypeError, ValueError):
Expand Down Expand Up @@ -654,6 +656,7 @@ def failed_request_observation(*, codex_bin: str, codex_home: Path,
thread_ids: list[str], turn_id: str,
read_only_review: bool = False,
completed_result_review: bool = False,
allow_untyped_result_review: bool = False,
known_turn_ids: set[str] | None = None,
work_dir: Path | None = None,
model_auth_home: Path | None = None) -> dict[str, Any] | None:
Expand All @@ -667,6 +670,8 @@ def failed_request_observation(*, codex_bin: str, codex_home: Path,

if completed_result_review and not read_only_review:
raise ValueError("a completed result observation permits only read-only review")
if allow_untyped_result_review and not (completed_result_review and read_only_review):
raise ValueError("untyped result evidence permits only a completed read-only review")
process = subprocess.Popen([codex_bin, "app-server", "--listen", "stdio://"],
cwd=str(codex_home), env={**os.environ, "CODEX_HOME": str(codex_home)},
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
Expand All @@ -683,7 +688,8 @@ def failed_request_observation(*, codex_bin: str, codex_home: Path,
native.thread_id = thread_id
native.response_timeout_sec = max(.01, deadline - time.monotonic())
if completed_result_review:
proof = completed_result_review_readback(native, turn_id, known_turn_ids=known_turn_ids or set())
proof = completed_result_review_readback(native, turn_id, known_turn_ids=known_turn_ids or set(),
allow_untyped=allow_untyped_result_review)
else:
reader = interrupted_result_readback if read_only_review else failed_request_readback
proof = reader(native, turn_id)
Expand Down
Loading
Loading