Skip to content
Open
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
11 changes: 11 additions & 0 deletions docs/architecture/rfcs/agent-loop-effect-interpreter-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,17 @@ shape merely because of its size. Optional memory participation now has one
compact, signed envelope projection; the Codex CLI adapter preserves default-off
isolation. This does not qualify installed heartbeat/App adoption or model value.

The shared DSH/cloud host conversion now retains verified runtime recall context
and returns opted-in reflections to the existing outer validator. Previously it
dropped both despite the signed participation facts. Host guidance reuses the
Codex CLI wording, and native Turn settlement invokes the existing ingest observer
for a freshly verified ingest binding on other hosts as well. File/SQLite native
CLI cases cover off, recall-only, ingest-only, invalidated configuration and
provider failure; the DSH runner and provider are hermetic. The independent
reflection attestation and accountable settlement still precede provider writes.
This qualifies host IO and callback wiring, not live provider/model benefit,
installed adoption or the three convergence acceptances below.

Bounded planning context now remains inline instead of forcing a full-decision
read under envelope budget pressure. Its existing owner retains the 3 KiB bound;
the shared DSH/cloud adapter transports the signed contribution and, for saved
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,14 @@ R5 短包投影也完整保留已有 CLI 结算计划,包括 effect identity
envelope 投影,Codex CLI 保持关闭隔离;这不代表安装态 heartbeat/App 或模型收益
已验收。

共享 DSH/cloud host 转换现在保留已核验的运行时 recall 上下文,并把已开启 ingest
时返回的反思交给既有外层 validator;此前虽有签名参与事实,两者仍在转换中丢失。
宿主指导复用 Codex CLI 原文,其他宿主的新鲜 ingest 绑定也经原生 Turn 结算调用
既有 observer。File/SQLite 原生 CLI 覆盖关闭、仅 recall、仅 ingest、配置失效和
provider 失败,DSH runner 与 provider 均为隔离 fixture。独立反思证明与可记账结算
仍先于 provider 写入。本切片只验证宿主 IO 与 callback 接线,不证明真实 provider/
模型收益、安装采用或以下三项收敛验收。

有界规划上下文现在保持内联,不再因短包预算压力而强制读取整份决策;既有 owner 的
3 KiB 限制保留。共享 DSH/cloud adapter 传递签名内容,历史引用同时保留原捕获路由和
哈希。thin renderer 仅在存在 `agent_context.detail_ref` 时要求能力补读,不能把也用于
Expand Down
19 changes: 19 additions & 0 deletions docs/reference/protocols/turn-envelope-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,25 @@ post-settlement ingest retain their existing rules. Provider failure remains
fail-open. The host adapter is Python IO transport over the existing capability
resolver and TypeScript envelope owner, not a new enablement policy.

The shared DSH/cloud conversion also preserves the runtime recall packet only
when the signed participation and fresh Goal/Agent binding permit it. Its local
`reward_memory_instructions` projection reuses the same Codex CLI clauses;
ingest-only input omits recalled context. Enabled reflections pass unchanged to
the outer validator, which retains the existing bounds and material-result rule.
Native Turn settlement runs the existing ingest observer for verified enabled
ingest on other hosts too; ordinary validation is still insufficient to write
memory. Unconfigured, disabled and stale inputs add no memory guidance, result
field or new observer on those hosts. Codex CLI's existing disabled behavior is
unchanged. File/SQLite CLI and hermetic host/provider tests qualify this IO
boundary, not live memory service, model adherence or installed activation.

中文:共享 DSH/cloud 转换只在签名参与事实及新鲜 Goal/Agent 绑定允许时保留运行时
recall 包;本地指导投影复用 Codex CLI 原条款,仅 ingest 不携带召回内容。已开启的
反思原样交给外层 validator,原大小和 material 规则保留。其他宿主已核验开启 ingest
时也调用既有结算后 observer,普通验证仍不能授权写记忆。关闭、未配置或失效时不
新增指导、反思字段或 observer,Codex CLI 原关闭行为不变。隔离 File/SQLite CLI
及宿主/provider fixture 仅证明 IO 边界,不代表真实服务、模型采用或安装已生效。

This is a bounded shared-facts step. Heartbeat still has its existing full and
compact rendering paths; it is not yet the same installed host journey. The
remaining convergence work is tracked in the existing
Expand Down
4 changes: 3 additions & 1 deletion loopx/cli_commands/turn_run_once.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
run_loopx_turn_once,
selected_turn_todo,
)
from ..control_plane.turn_driver.executor import reward_memory_automation_enabled
from ..quota import spend_quota_slot
from ..state_refresh import refresh_state_run
from ..control_plane.todos.path_resolution import resolve_todo_state_path
Expand Down Expand Up @@ -923,7 +924,8 @@ def on_managed_start_admitted() -> None:
scheduler=scheduler if args.execute else None,
post_settlement=(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 同步新增非 Codex 回调的既有生命周期读回

本分支为已核验启用 ingest 的 DSH/generic-cli 接入观察器,但 reward_memory_host_coverage() 仍报告 automatic_ingest=uncovered,并经 configuration/status/Markdown 对外展示。已有完整同头原生回调与当前 exact-blob 生产投影验证了这个矛盾;请同步原 coverage owner 并补真实调用后的读回断言,保留关闭/Lark 边界。完整证据、历史批准更正和验收边界见本 review 正文。

post_settlement_reward_memory
if args.execute and args.host == "codex-cli"
if args.execute and (args.host == "codex-cli"
or reward_memory_automation_enabled(payload, operation="automatic_ingest"))
else None
),
admit_start=managed_cadence.admit if args.execute else None,
Expand Down
8 changes: 4 additions & 4 deletions loopx/control_plane/reward_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


def reward_memory_host_coverage() -> list[dict[str, str]]:
"""Project actual host lifecycle wiring without inferring parity."""
"""Project source lifecycle wiring, not Agent enablement or provider qualification."""

return [
{
Expand All @@ -18,12 +18,12 @@ def reward_memory_host_coverage() -> list[dict[str, str]]:
{
"host_id": "generic_cli_turn",
"automatic_recall": "connected",
"automatic_ingest": "uncovered",
"automatic_ingest": "connected_post_settlement",
},
{
"host_id": "dsh_turn",
"automatic_recall": "connected_task_body",
"automatic_ingest": "uncovered",
"automatic_recall": "connected",
"automatic_ingest": "connected_post_settlement",
},
{
"host_id": "codex_app_quota",
Expand Down
12 changes: 2 additions & 10 deletions loopx/control_plane/turn_driver/codex_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
HOST_RESULT_TEXT_LIMITS,
LOOPX_TURN_HOST_REQUEST_SCHEMA_VERSION,
reward_memory_automation_enabled,
reward_memory_prompt_instructions,
)
from .execution_profile import require_supported_reasoning_effort
from .host_failure import BuiltInHostError
Expand Down Expand Up @@ -288,16 +289,7 @@ def _prompt(request: Mapping[str, Any]) -> str:
"For user_action_required, wait, or iteration_failed, leave material-only fields empty and explain the stop in summary. iteration_failed ends only this iteration and never requests a retry or successor.",
'completed_phases must be exactly ["host_execute","typed_result"], and turn_key must match the request.',
]
recall = _mapping(request.get("reward_memory_recall"))
if (reward_memory_automation_enabled(request, operation="automatic_recall")
and _mapping(recall.get("context")).get("guidance")):
instructions.append(
"When reward_memory_recall contains guidance, treat it as private, non-authoritative decision context: apply it only when it fits current evidence and never treat it as new action authority."
)
if reward_memory_automation_enabled(request, operation="automatic_ingest"):
instructions.append(
"Set reward_memory_reflection_json to an empty string unless independent task evidence established a reusable experience. For eligible evidence, return one compact JSON object using schema_version=turn_reward_memory_reflection_v1, status=eligible, a configured surface_id, outcome_kind in research|simulation|real|engineering, content_summary, reasoning_summary, confidence in low|medium|high, and 1-5 opaque evidence_refs. Also include experience using schema_version=procedural_experience_contract_v0 with non-empty applicability and limitations lists, observed_outcome, attribution, the same evidence_refs, and future_behavior containing trigger, action, validation, and stop_condition. A fact recap without a future behavior change and non-generalization boundary is not eligible memory. Legacy v0 reflections are audit-only and cannot become durable memory. Never use your own summary as evidence. Settlement may ingest it only when the caller-declared Todo validator attests the exact reflection digest and evidence; ordinary validator success remains awaiting and makes no provider write."
)
instructions.extend(reward_memory_prompt_instructions(request))
boundary = _mapping(_mapping(request.get("turn_envelope")).get("boundary"))
if boundary.get("checkpointed_boundary_authority"):
instructions.append(
Expand Down
17 changes: 17 additions & 0 deletions loopx/control_plane/turn_driver/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,23 @@ def reward_memory_automation_enabled(
)


def reward_memory_prompt_instructions(request: Mapping[str, Any]) -> list[str]:
"""Share optional capability guidance without changing its admission owner."""
instructions: list[str] = []
value = request.get("reward_memory_recall")
recall = value if isinstance(value, Mapping) else {}
if (reward_memory_automation_enabled(request, operation="automatic_recall")
and isinstance(recall.get("context"), Mapping) and recall["context"].get("guidance")):
instructions.append(
"When reward_memory_recall contains guidance, treat it as private, non-authoritative decision context: apply it only when it fits current evidence and never treat it as new action authority."
)
if reward_memory_automation_enabled(request, operation="automatic_ingest"):
instructions.append(
"Set reward_memory_reflection_json to an empty string unless independent task evidence established a reusable experience. For eligible evidence, return one compact JSON object using schema_version=turn_reward_memory_reflection_v1, status=eligible, a configured surface_id, outcome_kind in research|simulation|real|engineering, content_summary, reasoning_summary, confidence in low|medium|high, and 1-5 opaque evidence_refs. Also include experience using schema_version=procedural_experience_contract_v0 with non-empty applicability and limitations lists, observed_outcome, attribution, the same evidence_refs, and future_behavior containing trigger, action, validation, and stop_condition. A fact recap without a future behavior change and non-generalization boundary is not eligible memory. Legacy v0 reflections are audit-only and cannot become durable memory. Never use your own summary as evidence. Settlement may ingest it only when the caller-declared Todo validator attests the exact reflection digest and evidence; ordinary validator success remains awaiting and makes no provider write."
)
return instructions


def build_loopx_turn_host_request(plan: Mapping[str, Any]) -> dict[str, Any]:
transaction = (
plan.get("transaction") if isinstance(plan.get("transaction"), dict) else {}
Expand Down
14 changes: 14 additions & 0 deletions loopx/control_plane/turn_driver/host_candidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from ..work_items.delivery_batch_scale import DELIVERY_BATCH_SCALE_CHOICES
from ..work_items.delivery_outcome import DELIVERY_OUTCOME_CHOICES
from .driver import selected_turn_todo
from .executor import reward_memory_automation_enabled, reward_memory_prompt_instructions

LOOPX_TURN_HOST_REQUEST_SCHEMA = "loopx_turn_host_request_v0"
LOOPX_TURN_RESULT_SCHEMA = "loopx_turn_result_v0"
Expand Down Expand Up @@ -124,6 +125,14 @@ def extract_turn_authority(request: Mapping[str, Any]) -> dict[str, Any]:
# binding; this adapter neither reads the file nor re-enters admission.
authority["detail_ref"] = _mapping(envelope.get("detail_ref"))
authority["action_signature"] = signature
recall_enabled = reward_memory_automation_enabled(request, operation="automatic_recall")
ingest_enabled = reward_memory_automation_enabled(request, operation="automatic_ingest")
if recall_enabled or ingest_enabled:
memory = _mapping(request.get("reward_memory_recall"))
if not recall_enabled:
memory["context"] = None
authority["reward_memory_recall"] = memory
authority["reward_memory_instructions"] = reward_memory_prompt_instructions(request)
return authority


Expand Down Expand Up @@ -345,6 +354,11 @@ def build_result(
# Preserve the structured executor input verbatim. The executor
# owns type, size, JSON and semantic validation.
result[field] = candidate[field]
if ("reward_memory_reflection_json" in candidate
and reward_memory_automation_enabled(request, operation="automatic_ingest")):
# The outer executor validates bounds, material outcome, exact digest
# attestation and evidence. This adapter must not silently drop its input.
result["reward_memory_reflection_json"] = candidate["reward_memory_reflection_json"]

has_path_delta = "path_delta_mode" in candidate or "agent_vision_json" in candidate
unchanged_reason = candidate.get("vision_unchanged_reason")
Expand Down
Loading
Loading