ccx-native 0.0.0-stage → 0.1.0

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 (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -2
  3. package/bin/ccx +56 -0
  4. package/ccx/__init__.py +163 -0
  5. package/ccx/__main__.py +28 -0
  6. package/ccx/claude/__init__.py +0 -0
  7. package/ccx/claude/analytics.py +466 -0
  8. package/ccx/claude/attach.py +762 -0
  9. package/ccx/claude/context.py +465 -0
  10. package/ccx/claude/delivery.py +806 -0
  11. package/ccx/claude/external.py +253 -0
  12. package/ccx/claude/host/__init__.py +0 -0
  13. package/ccx/claude/host/events.py +297 -0
  14. package/ccx/claude/host/hooks.py +438 -0
  15. package/ccx/claude/host/pty.py +250 -0
  16. package/ccx/claude/host/session.py +5184 -0
  17. package/ccx/claude/host/subagents.py +169 -0
  18. package/ccx/claude/host/successor.py +267 -0
  19. package/ccx/claude/managed_turns.py +240 -0
  20. package/ccx/claude/oauth.py +176 -0
  21. package/ccx/claude/settings.py +339 -0
  22. package/ccx/claude/spawn.py +557 -0
  23. package/ccx/claude/tasks.py +72 -0
  24. package/ccx/claude/telemetry.py +282 -0
  25. package/ccx/claude/transcript.py +2281 -0
  26. package/ccx/claude/usage.py +837 -0
  27. package/ccx/claude/usage_control.py +152 -0
  28. package/ccx/cli/__init__.py +0 -0
  29. package/ccx/cli/codex.py +1009 -0
  30. package/ccx/cli/commands/__init__.py +0 -0
  31. package/ccx/cli/commands/_common.py +407 -0
  32. package/ccx/cli/commands/control.py +161 -0
  33. package/ccx/cli/commands/create.py +669 -0
  34. package/ccx/cli/commands/read.py +1020 -0
  35. package/ccx/cli/commands/turns.py +1081 -0
  36. package/ccx/cli/controller.py +757 -0
  37. package/ccx/cli/main.py +301 -0
  38. package/ccx/cli/parser.py +1324 -0
  39. package/ccx/cli/skills.py +215 -0
  40. package/ccx/cli/statusline.py +160 -0
  41. package/ccx/client.py +1900 -0
  42. package/ccx/codex/__init__.py +0 -0
  43. package/ccx/codex/attach_tty.py +267 -0
  44. package/ccx/codex/client.py +732 -0
  45. package/ccx/codex/completion.py +121 -0
  46. package/ccx/codex/control.py +288 -0
  47. package/ccx/codex/daemon/__init__.py +0 -0
  48. package/ccx/codex/daemon/__main__.py +4 -0
  49. package/ccx/codex/daemon/daemon.py +1556 -0
  50. package/ccx/codex/daemon/models.py +140 -0
  51. package/ccx/codex/daemon/native.py +1590 -0
  52. package/ccx/codex/daemon/release.py +367 -0
  53. package/ccx/codex/daemon/worker.py +2396 -0
  54. package/ccx/codex/delivery.py +758 -0
  55. package/ccx/codex/delivery_recovery.py +232 -0
  56. package/ccx/codex/evidence.py +827 -0
  57. package/ccx/codex/history.py +158 -0
  58. package/ccx/codex/notify_history.py +140 -0
  59. package/ccx/codex/ops.py +666 -0
  60. package/ccx/codex/paths.py +70 -0
  61. package/ccx/codex/protocol.py +805 -0
  62. package/ccx/codex/provenance.py +105 -0
  63. package/ccx/codex/read.py +29 -0
  64. package/ccx/codex/records.py +51 -0
  65. package/ccx/codex/reports.py +177 -0
  66. package/ccx/codex/runtime.py +1412 -0
  67. package/ccx/codex/settle.py +363 -0
  68. package/ccx/codex/telemetry.py +792 -0
  69. package/ccx/codex/types.py +659 -0
  70. package/ccx/codex/worker_environment.py +25 -0
  71. package/ccx/constants.py +288 -0
  72. package/ccx/controllers/__init__.py +0 -0
  73. package/ccx/controllers/controller.py +1449 -0
  74. package/ccx/controllers/live_vars.py +1706 -0
  75. package/ccx/controllers/live_vars_runner.py +216 -0
  76. package/ccx/controllers/notify.py +213 -0
  77. package/ccx/controllers/orchestrator.py +193 -0
  78. package/ccx/controllers/policy.py +57 -0
  79. package/ccx/controllers/store.py +681 -0
  80. package/ccx/errors.py +634 -0
  81. package/ccx/notify/__init__.py +0 -0
  82. package/ccx/notify/__main__.py +5 -0
  83. package/ccx/notify/client.py +116 -0
  84. package/ccx/notify/daemon.py +480 -0
  85. package/ccx/notify/deliver.py +304 -0
  86. package/ccx/notify/paths.py +51 -0
  87. package/ccx/notify/registry.py +649 -0
  88. package/ccx/notify/select.py +750 -0
  89. package/ccx/notify/settle.py +684 -0
  90. package/ccx/ops/__init__.py +0 -0
  91. package/ccx/ops/background.py +115 -0
  92. package/ccx/ops/cancel.py +289 -0
  93. package/ccx/ops/codex_import.py +96 -0
  94. package/ccx/ops/common.py +397 -0
  95. package/ccx/ops/compact.py +337 -0
  96. package/ccx/ops/compact_ops.py +550 -0
  97. package/ccx/ops/context_events.py +48 -0
  98. package/ccx/ops/conversation.py +1540 -0
  99. package/ccx/ops/conversation_paging.py +1338 -0
  100. package/ccx/ops/create.py +56 -0
  101. package/ccx/ops/creation.py +952 -0
  102. package/ccx/ops/creation_steps.py +579 -0
  103. package/ccx/ops/event_feed.py +710 -0
  104. package/ccx/ops/forking.py +711 -0
  105. package/ccx/ops/input.py +556 -0
  106. package/ccx/ops/maintenance.py +295 -0
  107. package/ccx/ops/report.py +770 -0
  108. package/ccx/ops/semantics.py +490 -0
  109. package/ccx/ops/send.py +961 -0
  110. package/ccx/ops/steer_ops.py +867 -0
  111. package/ccx/ops/terminate.py +383 -0
  112. package/ccx/ops/turn_reports.py +306 -0
  113. package/ccx/ops/wait.py +762 -0
  114. package/ccx/ops/worker_events.py +636 -0
  115. package/ccx/rpc/__init__.py +0 -0
  116. package/ccx/rpc/local_rpc.py +1602 -0
  117. package/ccx/rpc/rpc.py +559 -0
  118. package/ccx/rpc/serve.py +374 -0
  119. package/ccx/rpc/transport.py +409 -0
  120. package/ccx/rpc/view.py +125 -0
  121. package/ccx/store/__init__.py +0 -0
  122. package/ccx/store/discovery.py +170 -0
  123. package/ccx/store/identity.py +105 -0
  124. package/ccx/store/journal_io.py +79 -0
  125. package/ccx/store/liveness.py +194 -0
  126. package/ccx/store/paths.py +254 -0
  127. package/ccx/store/record_cache.py +40 -0
  128. package/ccx/store/records.py +309 -0
  129. package/ccx/store/root.py +804 -0
  130. package/ccx/store/runtime_presence.py +137 -0
  131. package/ccx/store/store.py +2332 -0
  132. package/ccx/store/worker_state.py +86 -0
  133. package/ccx/system/__init__.py +0 -0
  134. package/ccx/system/bounded.py +296 -0
  135. package/ccx/system/cwd_paths.py +25 -0
  136. package/ccx/system/engine_binary.py +59 -0
  137. package/ccx/system/executable.py +50 -0
  138. package/ccx/system/oslayer.py +667 -0
  139. package/ccx/system/pipes.py +46 -0
  140. package/ccx/system/sanitize.py +43 -0
  141. package/ccx/types.py +705 -0
  142. package/ccx/version.py +29 -0
  143. package/ccx/workers.py +2081 -0
  144. package/ccx.py +9 -0
  145. package/package.json +40 -4
  146. package/pyproject.toml +87 -0
  147. package/skills/ccx/SKILL.md +155 -0
  148. package/skills/ccx/references/controllers.md +244 -0
  149. package/skills/ccx/references/troubleshooting.md +94 -0
  150. package/skills/ccx/references/variables.md +62 -0
@@ -0,0 +1,684 @@
1
+ from __future__ import annotations
2
+ from typing import Any
3
+ from ccx.errors import SessionUnavailable
4
+ from ccx.ops.turn_reports import codex_native_turn_evidence as _codex_native_turn_evidence
5
+ from ccx.notify.registry import NOTIFY_REPORT_MAX_CHARS, mutate_registration, now_ms, read_registration
6
+ from ccx.notify.select import _compact_effect_receipt, _future_events, _future_ref_before, _original_notification_registration
7
+
8
+
9
+ def _future_settlement(reg: dict[str, Any]) -> dict[str, Any] | None:
10
+ """Select the first future native R once, then reuse exact receiptless proof."""
11
+ from ccx import constants
12
+ from ccx.claude.managed_turns import report_window_for_turn_ref, settlement_for_turn_ref
13
+ from ccx.ops.wait import wait_worker
14
+ from ccx.store.root import require_ccx_active
15
+
16
+ worker_id = reg["worker"]["workerId"]
17
+ child = f"claude:{worker_id}"
18
+ store = require_ccx_active(constants.CCX_ROOT)
19
+ op = reg["operation"]
20
+ cursor = op["armCursor"]
21
+ events = _future_events(worker_id, cursor)
22
+ ref = op["selectedTurnRef"]
23
+ if ref is None and events is not None:
24
+ seen: set[str] = set()
25
+ for row in events:
26
+ candidate = row.get("turn_ref")
27
+ if not isinstance(candidate, str) or not candidate.startswith("ccx-turn:claude:v1:"):
28
+ continue
29
+ if candidate not in seen:
30
+ seen.add(candidate)
31
+ if (row.get("type") in {"turn_start", "turn_identity_recovered"}
32
+ and not _future_ref_before(worker_id, cursor, candidate)):
33
+ def freeze(value: dict[str, Any]) -> None:
34
+ if (value["state"] == "armed"
35
+ and value["operation"]["selectedTurnRef"] is None):
36
+ value["operation"]["selectedTurnRef"] = candidate
37
+ updated = mutate_registration(reg["eventId"], freeze)
38
+ if updated is not None:
39
+ reg = updated
40
+ ref = reg["operation"]["selectedTurnRef"]
41
+ break
42
+ record = store.record(child)
43
+ state = (record.get("lifecycle") or {}).get("state")
44
+ base = {"workerState": state, "report": None, "error": None}
45
+ if ref is not None:
46
+ window, available = report_window_for_turn_ref(worker_id, ref)
47
+ offset = window.get("startOffset") if isinstance(window, dict) else None
48
+ if not available or type(offset) is not int or offset < 0:
49
+ # Late compact Stop and missed-hook turns are unknown, not
50
+ # a completed model turn or a permanently pending registration.
51
+ terminal, retained = settlement_for_turn_ref(worker_id, ref)
52
+ if retained and terminal and terminal.get("turnRef") == ref:
53
+ return {**base, "status": "unknown", "turnStatus": "unknown",
54
+ "turnRef": ref}
55
+ else:
56
+ # The selected turn is immutable across later turns and terminal retries.
57
+ result = _receiptless_settlement(reg)
58
+ if result is not None and result.get("status") != "waiting":
59
+ return result
60
+ # Gate is actionable only when an after-arm request and this same native R
61
+ # are both positively served; never adopt a pre-arm/foreign input gate.
62
+ if ref is not None and events is not None and any(
63
+ e.get("type") in {"permission_requested", "elicitation_requested"}
64
+ or (e.get("type") == "state" and e.get("to") == "waiting")
65
+ for e in events):
66
+ try:
67
+ status = wait_worker(child, timeout=.25,
68
+ expected_generation=store.generation)
69
+ except (SessionUnavailable, OSError):
70
+ status = {}
71
+ gate = status.get("waiting_for")
72
+ if (status.get("worker") == child and status.get("state") == "waiting"
73
+ and status.get("active_turn_ref") == ref
74
+ and isinstance(gate, dict) and gate.get("id")
75
+ and any((e.get("type") == "state" and e.get("to") == "waiting")
76
+ or (e.get("type") in {"permission_requested", "elicitation_requested"}
77
+ and str(e.get("request_id") or e.get("requestId")) == str(gate["id"]))
78
+ for e in events)):
79
+ return {**base, "status": "waiting", "turnStatus": "waiting",
80
+ "turnRef": ref, "workerState": "waiting", "waitingFor": gate}
81
+ if state in {"dead", "crashed", "idle-killed", "budget-stopped"}:
82
+ return {**base, "status": state, "turnStatus": None,
83
+ "error": (record.get("lifecycle") or {}).get("lastError")}
84
+ if state in {"idle", "busy", "waiting", "booting", "resuming"}:
85
+ from ccx.store.liveness import lifecycle_host_alive
86
+ from ccx.store.paths import sock_path
87
+ lifecycle = record.get("lifecycle") or {}
88
+ since = lifecycle.get("since")
89
+ pid = lifecycle.get("hostPid")
90
+ grace = 30_000 if state in {"booting", "resuming"} else 3_000
91
+ if (type(since) in {int, float} and now_ms() - since >= grace
92
+ and (not sock_path(worker_id).exists()
93
+ or (pid and not lifecycle_host_alive(lifecycle)))):
94
+ return {**base, "status": "host-dead", "turnStatus": None,
95
+ "error": "original Claude host is gone while future arm waits"}
96
+ return None
97
+
98
+
99
+ def _receiptless_codex_report(store, child: str, worker_id: str, thread_id: str,
100
+ turn_id: str) -> str | None:
101
+ """Only an already-serving original app-server may supply exact final text."""
102
+ from ccx.codex import runtime as codex_runtime
103
+ from ccx.ops.turn_reports import codex_source_report
104
+
105
+ try:
106
+ reply = codex_runtime.daemon_call(
107
+ {"op": "thread-history", "workerId": worker_id,
108
+ "threadId": thread_id, "turnId": turn_id,
109
+ "observeSeconds": 2}, timeout=2,
110
+ short=child,
111
+ )
112
+ except (SessionUnavailable, OSError):
113
+ return None
114
+ if (reply.get("transport") != "served" or reply.get("ok") is not True
115
+ or reply.get("workerId") != worker_id or reply.get("threadId") != thread_id):
116
+ return None
117
+ try:
118
+ report = codex_source_report(
119
+ reply.get("thread"), worker_id, thread_id, turn_id,
120
+ expected_source=store.codex_source(child),
121
+ )
122
+ except SessionUnavailable:
123
+ return None
124
+ return report["text"] or None
125
+
126
+
127
+ def _receiptless_settlement(reg: dict[str, Any]) -> dict[str, Any] | None:
128
+ """Read the frozen native turn, never a guessed send receipt or newer turn."""
129
+ from ccx import constants
130
+ from ccx.ops.wait import wait_worker
131
+ from ccx.store.root import require_ccx_active
132
+
133
+ store = require_ccx_active(constants.CCX_ROOT)
134
+ worker = reg["worker"]
135
+ child = f"{worker['engine']}:{worker['workerId']}"
136
+ operation = reg["operation"]
137
+ ref = operation["selectedTurnRef"]
138
+ record = store.record(child)
139
+ base = {"workerState": (record.get("lifecycle") or {}).get("state"),
140
+ "report": None}
141
+ if worker["engine"] == "claude":
142
+ from ccx.claude.managed_turns import report_window_for_turn_ref, settlement_for_turn_ref
143
+ window, available = report_window_for_turn_ref(worker["workerId"], ref)
144
+ start = window.get("startOffset") if isinstance(window, dict) else None
145
+ if not available or type(start) is not int or start < 0:
146
+ return None # A fallback compact Stop is not a model completion.
147
+ base["turnRef"] = ref
148
+ terminal, available = settlement_for_turn_ref(worker["workerId"], ref)
149
+ if available and terminal is not None and terminal.get("turnRef") == ref:
150
+ if terminal.get("eventType") != "turn_complete":
151
+ status = "unknown" # turn_settled is liveness, never semantic success
152
+ else:
153
+ status = "interrupted" if terminal.get("interrupted") else "completed"
154
+ result = {**base, "status": status, "turnStatus": status,
155
+ "error": terminal.get("evidence") if status == "unknown" else None}
156
+ if status == "completed" and window.get("endOffsetKnown") is True:
157
+ result["report"], result["reportTruncated"] = _truncate_report(
158
+ _receiptless_claude_report(store, child, worker["workerId"], ref, window))
159
+ return result
160
+ else:
161
+ turn_id = operation["turnId"]
162
+ base["turnId"] = turn_id
163
+ proof = _codex_native_turn_evidence(child, record,
164
+ operation["nativeSourceId"], turn_id)
165
+ if proof is None:
166
+ return None
167
+ if proof["status"] is not None:
168
+ status = proof["status"]
169
+ report = (_receiptless_codex_report(store, child, worker["workerId"],
170
+ operation["nativeSourceId"], turn_id)
171
+ if status == "completed" else None)
172
+ report, truncated = _truncate_report(report)
173
+ return {**base, "status": status, "turnStatus": status,
174
+ "report": report, "reportTruncated": truncated, "error": proof["error"]}
175
+ # A gate is actionable only while the exact frozen native R remains active.
176
+ try:
177
+ snapshot = wait_worker(child, timeout=0, expected_generation=store.generation)
178
+ except (SessionUnavailable, OSError):
179
+ return None
180
+ store.revalidate()
181
+ if snapshot.get("worker") != child or snapshot.get("active_turn_ref") != ref:
182
+ return None
183
+ if snapshot.get("state") != "waiting":
184
+ return None
185
+ gate = (snapshot.get("waiting_for") if worker["engine"] == "claude" else
186
+ snapshot.get("pendingApprovals") or snapshot.get("pending_approvals"))
187
+ if worker["engine"] == "claude" and not (isinstance(gate, dict) and gate.get("id")):
188
+ return None
189
+ if worker["engine"] == "codex" and not gate:
190
+ return None
191
+ return {**base, "status": "waiting", "turnStatus": "waiting",
192
+ "workerState": "waiting", "waitingFor": gate}
193
+
194
+
195
+ def _receiptless_claude_report(store, child: str, worker_id: str, ref: str,
196
+ window: dict[str, Any]) -> str | None:
197
+ from ccx.ops.turn_reports import claude_source_report
198
+ from ccx.store.root import StorageIntegrityError
199
+
200
+ try:
201
+ result = claude_source_report(store, child, worker_id, ref, window)
202
+ return result["text"] or None
203
+ except (StorageIntegrityError, OSError, ValueError, SessionUnavailable):
204
+ return None
205
+
206
+
207
+ def _truncate_report(text: str | None) -> tuple[str | None, bool]:
208
+ if text is None:
209
+ return None, False
210
+ if NOTIFY_REPORT_MAX_CHARS <= 0 or len(text) <= NOTIFY_REPORT_MAX_CHARS:
211
+ return text, False
212
+ return text[:NOTIFY_REPORT_MAX_CHARS], True
213
+
214
+
215
+ def _compact_observation(reg: dict[str, Any]) -> dict[str, Any]:
216
+ """One read-only original-operation view; never start or publish a compact."""
217
+ from ccx.errors import CcxError
218
+ from ccx.store.root import GenerationError
219
+ store, _parent, child = _original_notification_registration(reg)
220
+ operation = reg["operation"]
221
+ engine = reg["worker"]["engine"]
222
+ operation_id = operation["operationId"]
223
+ receipt = _compact_effect_receipt(
224
+ store, child, engine, reg["worker"]["workerId"], operation)
225
+ state = receipt.get("state") if receipt else None
226
+ if state in (None, "prepared"):
227
+ return {"operationStatus": "not-started", "terminal": False}
228
+ if state == "rejected":
229
+ return {"operationStatus": "rejected", "terminal": True}
230
+ try:
231
+ if engine == "claude":
232
+ from ccx.ops.compact import compact_status
233
+ view = compact_status(child, operation_id=operation_id, publish=False)
234
+ status = view.get("operation_status")
235
+ terminal = status in {"succeeded", "noop", "failed"} or (
236
+ status == "unknown" and not str(view.get("reason") or "").startswith(
237
+ "original Claude compact source unavailable"))
238
+ return {**view, "operationStatus": status, "terminal": terminal}
239
+ from ccx.codex.ops import compact_observation
240
+ view = compact_observation(child, operation_id)
241
+ status = view.get("status")
242
+ record = store.record(child)
243
+ last = record.get("lastCompact")
244
+ last_terminal = (isinstance(last, dict)
245
+ and last.get("operationId") == operation_id
246
+ and (last.get("status") == "unknown"
247
+ or last.get("nativeTurnSettled") is True))
248
+ return {**view, "operationStatus": status,
249
+ "terminal": (status in {"succeeded", "failed"} or
250
+ (status == "unknown" and last_terminal))}
251
+ except (CcxError, GenerationError, OSError, ValueError):
252
+ # Lost original evidence is never a compact completion.
253
+ return {"operationStatus": "unknown", "terminal": False}
254
+
255
+
256
+ def _compact_settlement(reg: dict[str, Any]) -> dict[str, Any] | None:
257
+ """Freeze only the original native compact's proven verdict, never a model R."""
258
+ viewed = _compact_observation(reg)
259
+ if not viewed["terminal"]:
260
+ return None
261
+ status = viewed["operationStatus"]
262
+ result = {
263
+ "status": "completed" if status in {"succeeded", "noop"} else status,
264
+ "workerState": (_original_notification_registration(reg)[0].record(
265
+ f"{reg['worker']['engine']}:{reg['worker']['workerId']}"
266
+ ).get("lifecycle") or {}).get("state"),
267
+ "operationType": "compact",
268
+ "operationId": reg["operation"]["operationId"],
269
+ "operationStatus": status,
270
+ "compacted": (status == "succeeded" if status in {"succeeded", "noop"}
271
+ else None),
272
+ "report": None,
273
+ "error": viewed.get("reason") if status in {"failed", "unknown"} else None,
274
+ }
275
+ if reg["worker"]["engine"] == "claude":
276
+ result.update(message=viewed.get("message"),
277
+ preTokens=viewed.get("pre_tokens"),
278
+ postTokens=viewed.get("post_tokens"),
279
+ durationMs=viewed.get("duration_ms"),
280
+ evidenceId=viewed.get("evidence_uuid"))
281
+ else:
282
+ result.update(compactTurnId=viewed.get("compactTurnId"),
283
+ contextItemId=viewed.get("contextItemId"),
284
+ nativeTurnStatus=viewed.get("nativeTurnStatus"),
285
+ contextBefore=viewed.get("contextBefore"),
286
+ contextAfter=viewed.get("contextAfter"))
287
+ return result
288
+
289
+
290
+ def _settlement_wake_marker(
291
+ reg: dict[str, Any], *, active: bool, pending_wake: bool,
292
+ last_turn_id: str | None
293
+ ) -> int | None:
294
+ """Persist/clear the durable wake-grace start for recursive settlement."""
295
+ event_id = reg.get("eventId")
296
+ if not isinstance(event_id, str) or not event_id:
297
+ return None
298
+ state = reg.get("settlementState")
299
+ state = state if isinstance(state, dict) else {}
300
+ current = state.get("pendingWakeSinceMs")
301
+ if active or not pending_wake:
302
+ if current is not None:
303
+ def clear(value: dict[str, Any]) -> None:
304
+ settlement_state = value.get("settlementState")
305
+ if isinstance(settlement_state, dict):
306
+ settlement_state.pop("pendingWakeSinceMs", None)
307
+ if not settlement_state:
308
+ value.pop("settlementState", None)
309
+ mutate_registration(event_id, clear)
310
+ return None
311
+ if (type(current) is int and
312
+ state.get("lastTurnId") == last_turn_id):
313
+ return current
314
+
315
+ started = now_ms()
316
+ def mark(value: dict[str, Any]) -> None:
317
+ settlement_state = value.setdefault("settlementState", {})
318
+ if settlement_state.get("lastTurnId") != last_turn_id:
319
+ settlement_state["pendingWakeSinceMs"] = started
320
+ else:
321
+ settlement_state.setdefault("pendingWakeSinceMs", started)
322
+ settlement_state["lastTurnId"] = last_turn_id
323
+ updated = mutate_registration(event_id, mark)
324
+ persisted = (
325
+ (updated.get("settlementState") or {}).get("pendingWakeSinceMs")
326
+ if isinstance(updated, dict)
327
+ else None
328
+ )
329
+ return persisted if isinstance(persisted, int) else started
330
+
331
+
332
+ def _recursive_codex_settlement(
333
+ reg: dict[str, Any],
334
+ base: dict[str, Any],
335
+ registration_snapshot: list[dict[str, Any]] | None = None,
336
+ ) -> dict[str, Any] | None:
337
+ worker = reg.get("worker") if isinstance(reg.get("worker"), dict) else {}
338
+ worker_id = worker["workerId"]
339
+ from ccx.codex.settle import DEFAULT_WAKE_GRACE_S, settlement_progress
340
+
341
+ progress = settlement_progress(f"codex:{worker_id}",
342
+ registrations=registration_snapshot)
343
+ active = bool(progress.get("activeTurnId"))
344
+ pending_wake = bool(progress.get("pendingWakeCount")) and not bool(
345
+ progress.get("outstandingCount")
346
+ )
347
+ wake_since = _settlement_wake_marker(
348
+ reg, active=active, pending_wake=pending_wake,
349
+ last_turn_id=(progress.get("lastTurn") or {}).get("turnId"))
350
+ if (
351
+ pending_wake
352
+ and not active
353
+ and isinstance(wake_since, int)
354
+ and now_ms() - wake_since >= int(DEFAULT_WAKE_GRACE_S * 1000)
355
+ ):
356
+ return {
357
+ **base,
358
+ "status": "delivery_gap",
359
+ "settlementVerdict": "delivery_gap",
360
+ "settlement": {
361
+ "verdict": "delivery_gap",
362
+ "outstandingCount": progress.get("outstandingCount"),
363
+ "outstanding": progress.get("outstanding"),
364
+ "pendingWakeCount": progress.get("pendingWakeCount"),
365
+ "pendingWake": progress.get("pendingWake"),
366
+ },
367
+ "error": (
368
+ "a descendant final notification was delivered after the "
369
+ "target's last completed turn, but no wake turn was observed "
370
+ "inside the recursive settlement wake grace"
371
+ ),
372
+ }
373
+
374
+ final = (
375
+ None
376
+ if progress.get("settlementVerdict") == "pending"
377
+ else progress
378
+ )
379
+ if final is None:
380
+ return None
381
+
382
+ _settlement_wake_marker(reg, active=True, pending_wake=False,
383
+ last_turn_id=(progress.get("lastTurn") or {}).get("turnId"))
384
+ verdict = final.get("settlementVerdict")
385
+ summary = {
386
+ "verdict": verdict,
387
+ "outstandingCount": final.get("outstandingCount"),
388
+ "outstanding": final.get("outstanding"),
389
+ "pendingWakeCount": final.get("pendingWakeCount"),
390
+ "pendingWake": final.get("pendingWake"),
391
+ }
392
+ if verdict == "settled":
393
+ return {
394
+ **base,
395
+ "settlementVerdict": "settled",
396
+ "settlement": summary,
397
+ }
398
+ return {
399
+ **base,
400
+ "status": verdict,
401
+ "workerState": final.get("state") or base.get("workerState"),
402
+ "settlementVerdict": verdict,
403
+ "settlement": summary,
404
+ "error": final.get("error") or base.get("error"),
405
+ }
406
+
407
+
408
+ def _original_codex_settlement(reg: dict[str, Any]) -> dict[str, Any] | None:
409
+ """Freeze only the fenced original semantic Codex delivery's outcome.
410
+
411
+ This is a receipt consumer, not a second native turn observer. An optional
412
+ single read-only native reconciliation uses the already accepted core.
413
+ """
414
+ from ccx.codex import delivery as codex_delivery
415
+ from ccx.ops.wait import reconcile_turn, wait_worker
416
+ from ccx.errors import SessionUnavailable
417
+
418
+ store, _parent, child = _original_notification_registration(reg)
419
+ operation = reg["operation"]
420
+ token = operation["deliveryId"]
421
+ receipt = codex_delivery.read_receipt(child, token)
422
+ if receipt is None:
423
+ # Publication requires this original prepared receipt. Its later loss
424
+ # never turns an old cached report into a completed child result.
425
+ return None
426
+ if (receipt.get("promptSha256") != operation.get("promptSha256")
427
+ or receipt.get("requestedMode") != operation.get("requestedMode")
428
+ or receipt.get("callerMetadata") != operation.get("callerMetadata")
429
+ or receipt.get("clientUserMessageId") != operation.get("clientUserMessageId")):
430
+ raise SessionUnavailable("Notification does not match its delivery; inspect the worker")
431
+ base = {"workerState": (store.record(child).get("lifecycle") or {}).get("state"),
432
+ "turnId": receipt.get("turnId"), "report": None}
433
+ if receipt["state"] == "rejected":
434
+ return {**base, "status": "rejected", "turnStatus": "rejected",
435
+ "error": receipt.get("rejectionMessage") or receipt.get("rejectionCode")}
436
+ if receipt["state"] == "prepared":
437
+ # Registration follows prepare; this send has never reached a
438
+ # native call in this state. A retryable refusal is not a settlement.
439
+ return None
440
+ if receipt["state"] != "accepted":
441
+ # owned/submitted and lost-ACK remain unresolved, never completed.
442
+ return None
443
+ turn_id = receipt.get("turnId")
444
+ if (not isinstance(turn_id, str) or not turn_id
445
+ or operation.get("turnId") not in (None, turn_id)):
446
+ raise SessionUnavailable("Notification turn does not match its delivery; inspect the worker")
447
+
448
+ def completed(value: dict[str, Any]) -> dict[str, Any] | None:
449
+ proof = value.get("settlement")
450
+ native = store.record(child).get("native") or {}
451
+ if not isinstance(proof, dict):
452
+ return None
453
+ if (proof.get("workerId") != reg["worker"]["workerId"]
454
+ or proof.get("deliveryId") != token
455
+ or proof.get("turnId") != turn_id
456
+ or proof.get("threadId") != native.get("threadId")
457
+ or proof.get("nativeSource") != store.codex_source(child)
458
+ or proof.get("status") != "completed"
459
+ or not isinstance(proof.get("text"), str)
460
+ or not isinstance(proof.get("nativeEvidenceSha256"), str)
461
+ or len(proof["nativeEvidenceSha256"]) != 64):
462
+ raise SessionUnavailable("original Codex completed receipt lacks turn/source proof")
463
+ return {**base, "status": "completed", "turnStatus": "completed",
464
+ "turnId": turn_id, "report": proof["text"], "error": None}
465
+
466
+ existing = completed(receipt)
467
+ if existing is not None:
468
+ return existing
469
+ try:
470
+ observed = reconcile_turn(child, delivery_id=token,
471
+ expected_generation=store.generation)
472
+ except (SessionUnavailable, OSError):
473
+ observed = None
474
+ store.revalidate()
475
+ if isinstance(observed, dict):
476
+ row = observed.get("delivery") or {}
477
+ parent = observed.get("worker") or {}
478
+ if (row.get("delivery_id") == token and row.get("turn_id") == turn_id
479
+ and parent.get("worker_id") == reg["worker"]["workerId"]):
480
+ receipt = codex_delivery.read_receipt(child, token) or receipt
481
+ existing = completed(receipt)
482
+ if existing is not None:
483
+ return existing
484
+ native_outcome = observed.get("native_status")
485
+ if native_outcome in {"failed", "interrupted", "cancelled"}:
486
+ return {**base, "status": native_outcome,
487
+ "turnStatus": native_outcome, "turnId": turn_id,
488
+ "error": observed.get("native_error"), "report": None}
489
+ # Current gate/terminal is advisory; it must belong to the same accepted
490
+ # turn, and must never turn a submitted-unknown/old gate into completion.
491
+ try:
492
+ snapshot = wait_worker(child, timeout=0,
493
+ expected_generation=store.generation)
494
+ except (SessionUnavailable, OSError):
495
+ return None
496
+ store.revalidate()
497
+ if (snapshot.get("worker") != child
498
+ or snapshot.get("active_turn_ref") != codex_delivery.turn_ref_for(turn_id)):
499
+ return None
500
+ if snapshot.get("state") == "waiting":
501
+ pending = snapshot.get("pending_approvals") or snapshot.get("pendingApprovals") or []
502
+ return {**base, "status": "waiting", "turnStatus": "waiting",
503
+ "turnId": turn_id, "waitingFor": pending,
504
+ "workerState": "waiting", "report": None}
505
+ return None
506
+
507
+
508
+ def _original_claude_settlement(reg: dict[str, Any]) -> dict[str, Any] | None:
509
+ """Only original Claude receipt/Stop or its exact start-unique turn gate."""
510
+ from ccx.claude import delivery as claude_delivery
511
+ from ccx.errors import SessionUnavailable
512
+ from ccx.claude.managed_turns import settlement_for_turn_ref
513
+ from ccx.ops.wait import reconcile_turn, wait_worker
514
+ from ccx.store.root import NativeEffectIndeterminate
515
+
516
+ store, _parent, child = _original_notification_registration(reg)
517
+ operation = reg["operation"]
518
+ token = operation["deliveryId"]
519
+ try:
520
+ receipt = claude_delivery.read_receipt(child, token)
521
+ except claude_delivery.ClaudeDeliveryError as exc:
522
+ raise SessionUnavailable("original Claude receipt unreadable") from exc
523
+ if receipt is None:
524
+ return None
525
+ if (receipt.get("workerId") != reg["worker"]["workerId"]
526
+ or receipt.get("deliveryId") != token
527
+ or receipt.get("requestedMode") != operation["requestedMode"]
528
+ or receipt.get("promptSha256") != operation["promptSha256"]
529
+ or receipt.get("promptByteCount") != operation["promptByteCount"]
530
+ or receipt.get("cap") != operation["cap"]
531
+ or receipt.get("callerMetadata") != operation["callerMetadata"]):
532
+ raise SessionUnavailable("Claude notification belongs to another worker; check its ID")
533
+ native = store.record(child).get("native") or {}
534
+ sid = native.get("sessionId")
535
+ if not isinstance(sid, str) or not sid:
536
+ raise SessionUnavailable("original Claude notification native session unbound")
537
+ base = {"workerState": (store.record(child).get("lifecycle") or {}).get("state"),
538
+ "turnRef": receipt.get("turnRef"), "report": None}
539
+ if receipt["state"] == "rejected":
540
+ return {**base, "status": "rejected", "turnStatus": "rejected",
541
+ "error": receipt.get("rejectionMessage") or receipt.get("rejectionCode")}
542
+ # Prepared (including submitted-unknown host refusal) and host-owned
543
+ # before original placement have no proven native turn and never complete.
544
+ if receipt["state"] != "accepted":
545
+ return None
546
+ turn_ref = receipt.get("turnRef")
547
+ if (receipt.get("route") != "new_turn" or receipt.get("placement") != "new_turn"
548
+ or not isinstance(turn_ref, str)
549
+ or not turn_ref.startswith("ccx-turn:claude:v1:")
550
+ or len(turn_ref) <= len("ccx-turn:claude:v1:")):
551
+ return None
552
+
553
+ # Existing original reconciler already validates global intent, native
554
+ # SID/source/spec/turnRef and forward-repairs the two Stop/effect writes.
555
+ try:
556
+ observed = reconcile_turn(child, delivery_id=token,
557
+ expected_generation=store.generation)
558
+ except (SessionUnavailable, NativeEffectIndeterminate,
559
+ claude_delivery.ClaudeDeliveryError, OSError):
560
+ observed = None
561
+ store.revalidate()
562
+ if isinstance(observed, dict) and observed.get("status") == "settled":
563
+ row = observed.get("delivery") or {}
564
+ try:
565
+ receipt = claude_delivery.read_receipt(child, token) or receipt
566
+ except claude_delivery.ClaudeDeliveryError as exc:
567
+ raise SessionUnavailable("original Claude settlement unreadable") from exc
568
+ proof = receipt.get("settlement")
569
+ if (row.get("delivery_id") == token and row.get("turn_ref") == turn_ref
570
+ and isinstance(proof, dict) and proof.get("workerId") == reg["worker"]["workerId"]
571
+ and proof.get("deliveryId") == token and proof.get("turnRef") == turn_ref
572
+ and proof.get("nativeSource") == f"claude-session:{sid}"
573
+ and proof.get("status") == "completed"
574
+ and isinstance(proof.get("text"), str)):
575
+ return {**base, "status": "completed", "turnStatus": "completed",
576
+ "turnRef": turn_ref, "report": proof["text"], "error": None}
577
+
578
+ # A positive Stop receipt whose companion effect is still inconclusive
579
+ # cannot be replaced by a contradictory negative sidecar observation.
580
+ if isinstance(receipt.get("settlement"), dict):
581
+ return None
582
+ # Retained sidecar is exact by turnRef, and can close interrupted/unknown
583
+ # without inventing a final assistant message, failed or cancelled result.
584
+ terminal, available = settlement_for_turn_ref(reg["worker"]["workerId"], turn_ref)
585
+ if available and isinstance(terminal, dict) and terminal.get("turnRef") == turn_ref:
586
+ if terminal.get("outcome") in {"interrupted", "unknown"}:
587
+ status = terminal["outcome"]
588
+ return {**base, "status": status, "turnStatus": status,
589
+ "turnRef": turn_ref, "report": None,
590
+ "error": terminal.get("evidence")}
591
+
592
+ # A currently blocked gate is actionable only on this accepted native
593
+ # turn. An older/successor gate or host-owned pre-start slot cannot wake it.
594
+ try:
595
+ snapshot = wait_worker(child, timeout=0,
596
+ expected_generation=store.generation)
597
+ except (SessionUnavailable, OSError):
598
+ return None
599
+ store.revalidate()
600
+ waiting = snapshot.get("waiting_for")
601
+ if (snapshot.get("worker") == child and snapshot.get("state") == "waiting"
602
+ and snapshot.get("active_turn_ref") == turn_ref
603
+ and isinstance(waiting, dict) and waiting.get("id")):
604
+ return {**base, "status": "waiting", "turnStatus": "waiting",
605
+ "turnRef": turn_ref, "workerState": "waiting",
606
+ "waitingFor": waiting, "report": None}
607
+ return None
608
+
609
+
610
+ def settlement_for_registration(
611
+ reg: dict[str, Any],
612
+ registration_snapshot: list[dict[str, Any]] | None = None,
613
+ ) -> dict[str, Any] | None:
614
+ _original_notification_registration(reg)
615
+ if reg.get("settlementMode") == "orchestration":
616
+ # A notifier/client may hold an older snapshot. Read the persisted
617
+ # original freeze and wake clock before any new turn/report observation.
618
+ current = read_registration(reg["eventId"])
619
+ if current is None:
620
+ return None
621
+ _original_notification_registration(current)
622
+ reg = current
623
+ frozen = reg.get("operationSettlement")
624
+ if not isinstance(frozen, dict):
625
+ base = _original_notification_settlement(reg)
626
+ if base is None:
627
+ return None
628
+ def freeze(row):
629
+ if row.get("state") == "armed":
630
+ row.setdefault("operationSettlement", dict(base))
631
+ updated = mutate_registration(reg["eventId"], freeze)
632
+ if updated is None or not isinstance(updated.get("operationSettlement"), dict):
633
+ return None
634
+ reg = updated
635
+ frozen = reg["operationSettlement"]
636
+ return _recursive_codex_settlement(reg, frozen, registration_snapshot)
637
+ if reg["worker"]["engine"] == "controller":
638
+ from ccx.controllers.notify import settlement
639
+ return settlement(reg)
640
+ return _original_notification_settlement(reg)
641
+
642
+
643
+ def _original_notification_settlement(reg: dict[str, Any]) -> dict[str, Any] | None:
644
+ if reg["source"] == "compact" or (
645
+ reg["source"] == "notify" and reg["operation"].get("kind") in {
646
+ "claude-compact", "codex-compact"}):
647
+ return _compact_settlement(reg)
648
+ if reg["source"] == "notify" and reg["operation"]["kind"] == "claude-next-turn":
649
+ return _future_settlement(reg)
650
+ if (reg["source"] in {"notify", "new"}
651
+ and reg["operation"].get("selectionMode") == "status-only"):
652
+ state = reg["operation"]["stableState"]
653
+ return {"status": state, "workerState": state,
654
+ "turnStatus": None, "report": None, "error": None}
655
+ if reg["source"] == "input":
656
+ _store, _parent, child = _original_notification_registration(reg)
657
+ operation = reg["operation"]
658
+ intent = _store.read_receipt(child, "answer", operation["responseId"])
659
+ if intent is None or intent["state"] != "accepted":
660
+ return None # A submitted-unknown / rejected gate is never completed.
661
+ if reg["worker"]["engine"] == "claude":
662
+ original = _store.record(child).get("waitingFor")
663
+ if isinstance(original, dict) and original.get("id") == operation["gateId"]:
664
+ return None # Original permission remains unresolved, not a new gate.
665
+ else:
666
+ current = _store.record(child).get("pendingApprovals") or []
667
+ if any(isinstance(gate, dict) and gate.get("gateId") == operation["gateId"]
668
+ and gate.get("turnId") == operation["turnId"] for gate in current):
669
+ return None
670
+ observed = _receiptless_settlement(reg)
671
+ if observed is not None and observed.get("status") == "waiting":
672
+ gates = observed.get("waitingFor") or []
673
+ if reg["worker"]["engine"] == "claude":
674
+ if not isinstance(gates, dict) or gates.get("id") in {None, operation["gateId"]}:
675
+ return None # A new actionable gate, on the same frozen R.
676
+ elif not any(isinstance(gate, dict) and gate.get("gateId") != operation["gateId"]
677
+ and gate.get("turnId") == operation["turnId"] for gate in gates):
678
+ return None
679
+ return observed
680
+ if reg["source"] == "notify" and reg["operation"].get("nativeProof") == "original-native-turn":
681
+ return _receiptless_settlement(reg)
682
+ if reg["worker"]["engine"] == "claude":
683
+ return _original_claude_settlement(reg)
684
+ return _original_codex_settlement(reg)