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,1590 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import sys
6
+ import time
7
+ from typing import Any
8
+ from ccx.codex.delivery import (
9
+ route as send_receipt_route,
10
+ )
11
+ from ccx.codex.protocol import AppServerClient, ProtocolError, ProtocolNotSent, ProtocolTransportError
12
+ from ccx.codex.reports import select_report_for_turn
13
+ from ccx.codex.records import now_ms
14
+ from ccx.codex.daemon.models import WorkerKilled, _TurnLaunch, _native_history_budget
15
+ import ccx.codex.daemon.worker as daemon_worker
16
+ import ccx.codex.daemon.release as daemon_release
17
+ from typing import TYPE_CHECKING
18
+ if TYPE_CHECKING:
19
+ from ccx.codex.daemon.worker import Worker
20
+
21
+
22
+ _CODEX_NATIVE_START_HELD_LIMIT = 16
23
+
24
+
25
+ _CODEX_NATIVE_START_UNSAFE_LIMIT = 32
26
+
27
+
28
+ _CODEX_NATIVE_START_RETRY_PER_PASS = 2
29
+
30
+
31
+ _CODEX_NATIVE_START_ID_MAX_BYTES = 2048
32
+
33
+
34
+ _CODEX_NATIVE_START_REPLY_MAX_BYTES = 64 * 1024
35
+
36
+
37
+ def _original_caller_config(worker_id: str) -> dict[str, Any]:
38
+ """Per-thread shell env naming the exact full-UUID caller.
39
+
40
+ Codex applies it per start/resume request, so an original thread gets it
41
+ again on every resume, not only at its first thread/start.
42
+ """
43
+ from ccx.controllers.live_vars import codex_thread_config
44
+ return codex_thread_config(worker_id)
45
+
46
+
47
+ def _with_default_effort(config: dict[str, Any], args: dict[str, Any]) -> dict[str, Any]:
48
+ """Make the worker's effort the thread default, not only a per-turn value.
49
+
50
+ Turns typed in an attached TUI carry no effort, so a thread whose CCX turn
51
+ has not run yet (fresh or just revived) would otherwise use Codex's default.
52
+ """
53
+ if args.get("effort"):
54
+ return {**config, "model_reasoning_effort": args["effort"]}
55
+ return config
56
+
57
+
58
+ def _original_subscription_in_this_runtime(
59
+ daemon, target: str, thread_id: str, record: dict[str, Any],
60
+ ) -> bool:
61
+ """Original activation's current daemon/app-server lifetime, not PID alone."""
62
+ from ccx.store.root import _read_json
63
+ import hashlib
64
+ store = daemon._uuid_store
65
+ client = daemon.client
66
+ if (daemon.stopping or not daemon.ready.is_set() or client is None
67
+ or client.process.poll() is not None):
68
+ return False
69
+ original = store.native_activation(target)
70
+ owner = store.resolve(target)
71
+ token = original.get("submissionId")
72
+ source = f"ccx-worker:{owner.worker_id}"
73
+ if (original.get("state") != "confirmed"
74
+ or original.get("source") != source
75
+ or (original.get("nativeBinding") or {}).get("threadId") != thread_id
76
+ or not isinstance(token, str) or not token):
77
+ return False
78
+ key = hashlib.sha256(("activate\x00" + token).encode()).hexdigest()
79
+ try:
80
+ with store.lease():
81
+ with store._gen("runtime-metadata", "codex-native-effects",
82
+ owner.worker_id) as directory:
83
+ effect = _read_json(directory, key + ".json")
84
+ except FileNotFoundError:
85
+ return False
86
+ if not isinstance(effect, dict):
87
+ return False
88
+ candidate = effect.get("nativeCandidate") or {}
89
+ return bool(
90
+ effect.get("kind") == "activate"
91
+ and effect.get("workerId") == owner.worker_id
92
+ and effect.get("engine") == "codex"
93
+ and effect.get("generation") == store.generation
94
+ and effect.get("token") == token
95
+ and effect.get("source") == source
96
+ and effect.get("createSha256") == record.get("createSha256")
97
+ and effect.get("daemonInstanceId") == daemon.instance_id
98
+ and effect.get("appServerPid") == client.process.pid
99
+ and isinstance(candidate, dict)
100
+ and candidate.get("threadId") == thread_id
101
+ )
102
+
103
+
104
+ def _load_original_native_worker(
105
+ daemon, worker_id: str, timeout: float | None,
106
+ ) -> dict[str, Any]:
107
+ """Ensure one source-proven full-UUID thread is natively subscribed.
108
+
109
+ Thread/read alone is never native loadedness after park. Existing
110
+ per-worker load/kill locks and release fences own the race.
111
+ """
112
+ from ccx.store.root import NativeActionDenied
113
+ if timeout is not None and (not isinstance(timeout, (int, float)) or timeout <= 0):
114
+ return {"ok": False, "error": "invalid full-UUID load timeout"}
115
+ budget = min(float(timeout) if timeout is not None else 90., 90.)
116
+ handle = "codex:" + worker_id
117
+ try:
118
+ daemon._uuid_store.authorize_native_action(handle, "resume")
119
+ worker = daemon._adopt_original_native_worker(handle, read_timeout=budget)
120
+ original_native = (worker.record.get("native") or {}).get("threadId")
121
+ with worker.load_lock:
122
+ with daemon.lock:
123
+ if daemon.workers.get(worker_id) is not worker or daemon.stopping:
124
+ raise WorkerKilled("original Codex Worker unloaded during resume")
125
+ with worker.lock:
126
+ if worker.killed.is_set() or worker.kill_intent.is_set():
127
+ raise WorkerKilled("original Codex Worker is being killed")
128
+ lifecycle = worker.record["lifecycle"]
129
+ if isinstance(worker.record.get("nativeTurnStart"), dict):
130
+ raise RuntimeError("original native turn/start is unresolved; no resume")
131
+ if lifecycle["state"] in ("busy", "waiting"):
132
+ # Exact source-verified read-only adoption remains a
133
+ # valid low-level load result for an old active gate,
134
+ # but it is not an idle/subscribed resume. The public
135
+ # ensure_ready insists on loaded=True and state=idle.
136
+ return {"ok": True, "changed": False,
137
+ "loaded": bool(worker.loaded),
138
+ **worker.public_status()}
139
+ if worker.loaded:
140
+ if lifecycle["state"] == "prepared":
141
+ # Original promptless native activation only wrote
142
+ # the initial prepared lifecycle. Matching the
143
+ # original current app-server effect+thread/read
144
+ # proves the subscribed zero-turn thread is ready.
145
+ worker._commit_state("idle", activeTurnId=None)
146
+ if worker.record["lifecycle"]["state"] != "idle":
147
+ raise RuntimeError("original Codex worker is not idle")
148
+ return {"ok": True, "changed": False, "loaded": True,
149
+ **worker.public_status()}
150
+ # An earlier resume may have timed out while Codex kept
151
+ # loading the thread. Resuming again is safe: Codex attaches
152
+ # to a thread it already has loaded.
153
+ if not isinstance(original_native, str) or not original_native:
154
+ raise NativeActionDenied("original native Codex threadId missing")
155
+ if not os.path.isdir(worker.record["cwd"]):
156
+ raise RuntimeError("original Codex workspace no longer exists")
157
+ args = worker.record.get("createArgs") or {}
158
+ params = {"cwd": worker.record["cwd"],
159
+ "sandbox": args.get("sandbox"),
160
+ "approvalPolicy": args.get("approvalPolicy"),
161
+ "serviceName": "ccx",
162
+ "config": _with_default_effort(
163
+ _original_caller_config(worker.worker_id), args)}
164
+ if args.get("model"):
165
+ params["model"] = args["model"]
166
+ def publish_resume():
167
+ with worker.lock:
168
+ if worker.killed.is_set() or worker.kill_intent.is_set():
169
+ raise WorkerKilled("original Codex Worker killed before native resume")
170
+ current = worker.record["lifecycle"]
171
+ current["state"] = "resuming"
172
+ current["since"] = now_ms()
173
+ current["activeTurnId"] = None
174
+ worker.record["pendingApprovals"] = []
175
+ worker._save()
176
+ try:
177
+ reply = worker._resume_exact_thread(
178
+ original_native, params, timeout=budget,
179
+ before_submit=publish_resume,
180
+ )
181
+ except ProtocolError as error:
182
+ # An answered JSON-RPC rejection is not an uncertain
183
+ # subscription. A local timeout/transport loss is: its
184
+ # request_id is absent, so preserve the no-replay fence.
185
+ received = (error.request_id is not None or
186
+ (isinstance(error.__cause__, ProtocolError)
187
+ and error.__cause__.request_id is not None))
188
+ if received and not isinstance(error, ProtocolTransportError):
189
+ with worker.lock:
190
+ worker._commit_state(
191
+ "crashed", lastError=f"original native Codex resume rejected: {error}",
192
+ )
193
+ raise
194
+ native_thread = reply.get("thread") if isinstance(reply, dict) else None
195
+ native_status = (native_thread.get("status") if isinstance(native_thread, dict)
196
+ else None)
197
+ # The independent source-fenced thread/read proved the source
198
+ # even if the reply omits its optional threadSource field.
199
+ if not isinstance(native_thread, dict):
200
+ error_message = "original native Codex resume reply missing thread"
201
+ elif native_thread.get("id") != original_native:
202
+ error_message = "Codex resumed the wrong thread; inspect the worker"
203
+ elif native_thread.get("threadSource") not in (
204
+ None, daemon._uuid_store.codex_source(handle)
205
+ ):
206
+ error_message = "Codex thread belongs to another worker; inspect its handle"
207
+ elif not isinstance(native_status, dict):
208
+ error_message = "original native Codex resume reply missing thread status"
209
+ elif native_status.get("type") not in ("idle", "systemError"):
210
+ error_message = (
211
+ "original native Codex resume reply not ready: "
212
+ f"status {native_status.get('type')!r}"
213
+ )
214
+ else:
215
+ error_message = None
216
+ if error_message is not None:
217
+ with worker.lock:
218
+ if worker.killed.is_set() or worker.kill_intent.is_set():
219
+ raise WorkerKilled("original Codex Worker killed during resume")
220
+ worker._commit_state("crashed", lastError=error_message)
221
+ raise RuntimeError(error_message)
222
+ with worker.lock:
223
+ if worker.killed.is_set() or worker.kill_intent.is_set():
224
+ raise WorkerKilled("original Codex Worker killed during resume")
225
+ worker.record["pendingApprovals"] = []
226
+ worker.record["activeTurnError"] = None
227
+ worker.record["lifecycle"]["lastError"] = None
228
+ worker.record["lifecycle"]["activeTurnId"] = None
229
+ worker._commit_state("idle")
230
+ worker._resumed_system_error_pending = (
231
+ native_status.get("type") == "systemError"
232
+ )
233
+ worker.loaded = True
234
+ return {"ok": True, "changed": True, "loaded": True,
235
+ **worker.public_status()}
236
+ except WorkerKilled as error:
237
+ if "worker" in locals():
238
+ daemon_release._abandon_killed_load(daemon, worker)
239
+ return {"ok": False, "error": str(error)}
240
+ except Exception as error:
241
+ # On a lost native reply the resuming record is deliberately held
242
+ # and native_threads is a possible-subscription cleanup obligation.
243
+ return {"ok": False, "error": str(error)}
244
+
245
+
246
+ def _adopt_original_native_worker(
247
+ daemon, target: str, *, read_timeout: float = 1.,
248
+ ) -> "Worker":
249
+ """Register one immutable full-UUID thread in the original daemon index.
250
+
251
+ App-server notifications, lifecycle locks and native send/observe
252
+ share one Worker instance. Merely
253
+ knowing a threadId is never authority: compare exact durable native
254
+ source and confirmed creation claim before constructing anything.
255
+ This never calls thread/start or thread/resume.
256
+ """
257
+ from ccx.store.root import NativeActionDenied
258
+ store = daemon._uuid_store
259
+ owner = store.authorize_native_action(target, "send")
260
+ if owner.engine != "codex":
261
+ raise NativeActionDenied("daemon adoption requires Codex full UUID")
262
+ rec = store.record(owner.worker)
263
+ native = rec.get("native") or {}
264
+ thread_id = native.get("threadId")
265
+ source = store.codex_source(owner.worker)
266
+ if (not isinstance(thread_id, str) or not thread_id
267
+ or native.get("threadSource") != source):
268
+ raise NativeActionDenied("native threadId/source was not confirmed")
269
+ # A live Worker is a cache of the immutable original global owner;
270
+ # never key it by a short/native thread ID. Construct before taking
271
+ # the daemon-global lock to avoid serializing journal inspections.
272
+ with daemon._lifecycle_lock(owner.worker_id):
273
+ # After a daemon restart the original native thread may no
274
+ # longer exist. A recorded threadId alone is not proof this
275
+ # app-server owns it. Read the exact original native source
276
+ # before adopting; never call thread/start or thread/resume
277
+ # as a fallback for a missing/different native thread.
278
+ if daemon.client is None:
279
+ raise RuntimeError("Codex app-server unavailable for adoption")
280
+ observed=daemon.client.request(
281
+ "thread/read",
282
+ {"threadId":thread_id,"includeTurns":False},
283
+ timeout=read_timeout,
284
+ )
285
+ native_proof=(
286
+ observed.get("thread") if isinstance(observed,dict) else None
287
+ )
288
+ if (not isinstance(native_proof,dict)
289
+ or native_proof.get("id")!=thread_id
290
+ or native_proof.get("threadSource") != source):
291
+ raise RuntimeError(
292
+ "original Codex native thread/read source absent or foreign; "
293
+ "never recreate an original thread"
294
+ )
295
+ with daemon.lock:
296
+ existing = daemon.workers.get(owner.worker_id)
297
+ if existing is not None:
298
+ if (existing.worker_id != owner.worker_id
299
+ or daemon.by_thread.get(thread_id) != owner.worker_id):
300
+ raise RuntimeError(
301
+ "Codex registry contradicts original full UUID"
302
+ )
303
+ return existing
304
+ if daemon.stopping or not daemon.ready.is_set():
305
+ raise RuntimeError("Codex singleton not accepting new workers")
306
+ bound = daemon.by_thread.get(thread_id)
307
+ if bound is not None and bound != owner.worker_id:
308
+ raise RuntimeError("native thread already owned by another UUID")
309
+ worker = daemon_worker.Worker(daemon, rec)
310
+ # Native membership is an unsubscribe obligation, not readiness.
311
+ # A parked or old-daemon thread/read can see history without a
312
+ # current app-server subscription. Only original current-epoch
313
+ # activation with an unparked record proves the first subscription.
314
+ worker.native_threads.add(thread_id)
315
+ worker.loaded = (rec["lifecycle"].get("state") in ("prepared", "idle", "busy", "waiting")
316
+ and _original_subscription_in_this_runtime(daemon, owner.worker, thread_id, rec))
317
+ with daemon.lock:
318
+ prior = daemon.workers.get(owner.worker_id)
319
+ if prior is not None:
320
+ return prior
321
+ if daemon.by_thread.get(thread_id) not in (None, owner.worker_id):
322
+ raise RuntimeError("native thread ownership changed")
323
+ daemon.workers[owner.worker_id] = worker
324
+ daemon.by_thread[thread_id] = owner.worker_id
325
+ daemon.empty_since = None
326
+ # A newly adopted app-server is not evidence that a command from a
327
+ # previous native runtime still runs. Repair a surviving stale record.
328
+ with worker.lock:
329
+ if worker._lose_native_background_locked():
330
+ worker._save()
331
+ worker.refresh_parent_notifications()
332
+ return worker
333
+
334
+
335
+ def _native_start_retryable_save_error(daemon, error: BaseException) -> bool:
336
+ """Retry only recognized transient persistence failures, not corruption."""
337
+ import errno
338
+ from ccx.store.root import GenerationBusy
339
+ return isinstance(error, GenerationBusy) or (
340
+ isinstance(error, OSError)
341
+ and getattr(error, "errno", None) in {
342
+ errno.ENOSPC, errno.EIO, errno.EAGAIN, errno.EINTR, errno.EBUSY,
343
+ }
344
+ )
345
+
346
+
347
+ def _mark_unsafe_native_start_locked(
348
+ daemon, hold_key: tuple[str, str, str], held: dict[str, Any],
349
+ detail: str,
350
+ ) -> None:
351
+ """Record a bounded unsafe diagnostic without pinning native capacity.
352
+
353
+ Caller holds only `_native_start_hold_lock`. Original one-shot effect
354
+ marker/claim stay untouched, and this rejected evidence is never used
355
+ to write a candidate or to justify a new native dispatch.
356
+ """
357
+ if daemon._native_start_held.get(hold_key) is not held:
358
+ return
359
+ held["unsafe"] = True
360
+ held["error"] = detail
361
+ del daemon._native_start_held[hold_key]
362
+ daemon._native_start_unsafe[hold_key] = detail[:250]
363
+ daemon._native_start_unsafe_total += 1
364
+ if len(daemon._native_start_unsafe) > _CODEX_NATIVE_START_UNSAFE_LIMIT:
365
+ del daemon._native_start_unsafe[next(iter(daemon._native_start_unsafe))]
366
+
367
+
368
+ def _native_start_counts(daemon) -> dict[str, int]:
369
+ """Bounded daemon health, no exposure of native IDs or source tokens."""
370
+ with daemon._native_start_hold_lock:
371
+ pending = sum(value["candidate"] is None
372
+ and value.get("rejection") is None
373
+ for value in daemon._native_start_held.values())
374
+ held = len(daemon._native_start_held) - pending
375
+ return {
376
+ "pending": pending, "held": held,
377
+ "unsafe": len(daemon._native_start_unsafe),
378
+ "unsafeTotal": daemon._native_start_unsafe_total,
379
+ "capacity": _CODEX_NATIVE_START_HELD_LIMIT,
380
+ }
381
+
382
+
383
+ def _janitor_native_start_pass(daemon) -> None:
384
+ """Retry a bounded number of transient original saves every janitor tick.
385
+
386
+ No native requests or extra threads. Retained evidence is never evicted
387
+ on age; timed-out still-in-flight PendingRequest must continue to pin
388
+ the daemon. Backoff bounds repeated disk/lock work. Explicit daemon
389
+ stop/cutover wins immediately and is never blocked by retry loops.
390
+ """
391
+ if daemon.stop.is_set():
392
+ return
393
+ now = time.monotonic()
394
+ with daemon._native_start_hold_lock:
395
+ attempts = [key for key, held in daemon._native_start_held.items()
396
+ if (held["candidate"] is not None
397
+ or held.get("rejection") is not None)
398
+ and held["error"] is not None
399
+ and held.get("retryAfter", float("inf")) <= now]
400
+ attempts = attempts[:_CODEX_NATIVE_START_RETRY_PER_PASS]
401
+ for key in attempts:
402
+ if daemon.stop.is_set():
403
+ return
404
+ try:
405
+ daemon._save_held_codex_candidate(key)
406
+ except Exception as error: # noqa: BLE001
407
+ # Do not let a janitor exception disable idle shutdown or
408
+ # notifications for unrelated workers.
409
+ daemon_release._log_janitor_failure(daemon, "original native candidate", error)
410
+
411
+
412
+ def _save_held_codex_candidate(
413
+ daemon, hold_key: tuple[str, str, str],
414
+ ) -> dict[str, Any]:
415
+ """Retry fsync of only the original native reply outcome, never dispatch.
416
+
417
+ Called once from the app-server reader callback, or later from a
418
+ native-activation-status socket handler. Both use the same pinned
419
+ generation and original effect lock; a concurrent successful fsync is
420
+ idempotent. Unsafe disk identities and a changed generation stay
421
+ blocked in memory, not overwritten on any later status request.
422
+ """
423
+ from ccx.store.root import StorageIntegrityError, _read_json, _write_json, _flock_at
424
+ import hashlib
425
+ with daemon._native_start_hold_lock:
426
+ held = daemon._native_start_held.get(hold_key)
427
+ if held is None:
428
+ unsafe = daemon._native_start_unsafe.get(hold_key)
429
+ return ({"status": "unsafe", "error": unsafe}
430
+ if unsafe is not None else {"status": "not-held"})
431
+ if held["unsafe"]:
432
+ return {"status": "unsafe", "error": held["error"]}
433
+ if held["candidate"] is None and held.get("rejection") is None:
434
+ return {"status": "not-held"}
435
+ original = dict(held["original"])
436
+ candidate = dict(held["candidate"]) if held["candidate"] is not None else None
437
+ rejection = dict(held["rejection"]) if held.get("rejection") is not None else None
438
+ generation, worker_id, token = hold_key
439
+ key = hashlib.sha256(("activate" + "\x00" + token).encode()).hexdigest()
440
+ store = daemon._uuid_store
441
+ try:
442
+ if generation != store.generation:
443
+ raise StorageIntegrityError("held native reply belongs to another generation")
444
+ with store.lease():
445
+ with store._gen("runtime-metadata", "codex-native-effects",
446
+ worker_id) as directory:
447
+ with _flock_at(directory, key + ".lock", exclusive=True,
448
+ create=False, timeout=.5):
449
+ saved = _read_json(directory, key + ".json")
450
+ if (not isinstance(saved, dict) or any(
451
+ saved.get(k) != v for k, v in original.items())):
452
+ raise StorageIntegrityError(
453
+ "original Codex effect marker absent/changed; no evidence rewrite"
454
+ )
455
+ if (candidate is None) == (rejection is None):
456
+ raise StorageIntegrityError(
457
+ "original Codex native response outcome is contradictory"
458
+ )
459
+ field, intended, opposite = (
460
+ ("nativeCandidate", candidate, "nativeRejected")
461
+ if candidate is not None
462
+ else ("nativeRejected", rejection, "nativeCandidate")
463
+ )
464
+ if (saved.get(opposite) is not None
465
+ or saved.get("nativeNotSubmitted") is not None):
466
+ raise StorageIntegrityError(
467
+ "original Codex native outcome conflicts with local non-submission"
468
+ )
469
+ previous = saved.get(field)
470
+ if previous is not None and previous != intended:
471
+ raise StorageIntegrityError(
472
+ "original Codex native outcome changed; no overwrite"
473
+ )
474
+ if previous is None:
475
+ _write_json(directory, key + ".json", {
476
+ **saved, field: intended,
477
+ })
478
+ if rejection is not None:
479
+ # A marker is always fsynced before terminal claim transition.
480
+ # Failure to seal the claim keeps this exact rejection held for
481
+ # bounded maintenance/status retry; the durable marker also
482
+ # permits a new CLI to reconcile entirely offline.
483
+ sealed = store.reconcile_codex_native_rejection(
484
+ f"codex:{worker_id}"
485
+ )
486
+ if (not isinstance(sealed, dict)
487
+ or sealed.get("code") != rejection["code"]
488
+ or sealed.get("message") != rejection["message"]):
489
+ raise StorageIntegrityError("original native rejection not sealed")
490
+ except Exception as error:
491
+ retryable = _native_start_retryable_save_error(daemon, error)
492
+ # Do not silently discard original response evidence after one
493
+ # ENOSPC/lease-contention failure. Unsafe identity/generation
494
+ # failures are visible and *never* reattempted by status.
495
+ detail = f"{type(error).__name__}: {error}"[:250]
496
+ with daemon._native_start_hold_lock:
497
+ if daemon._native_start_held.get(hold_key) is held:
498
+ held["error"] = detail
499
+ if not retryable:
500
+ _mark_unsafe_native_start_locked(daemon, hold_key, held, detail)
501
+ else:
502
+ # First retry is no sooner than one janitor tick; each
503
+ # failed retry backs off, capped at 30 seconds.
504
+ failures = min(6, held.get("retryFailures", 0) + 1)
505
+ held["retryFailures"] = failures
506
+ held["retryAfter"] = time.monotonic() + min(
507
+ 30.0, float(2 ** (failures - 1))
508
+ )
509
+ print(
510
+ f"ccx codexd: original Codex start reply pending disk save "
511
+ f"for {worker_id} ({'transient' if retryable else 'unsafe'}): {detail}",
512
+ file=sys.stderr, flush=True,
513
+ )
514
+ return {"status": "transient" if retryable else "unsafe",
515
+ "error": detail}
516
+ with daemon._native_start_hold_lock:
517
+ if daemon._native_start_held.get(hold_key) is held:
518
+ del daemon._native_start_held[hold_key]
519
+ return {"status": "saved"}
520
+
521
+
522
+ def _native_codex_local_proof(daemon, original: dict, reason: str,
523
+ stage: str) -> dict[str, Any]:
524
+ """Bounded local attribution, not a cryptographically signed proof."""
525
+ from ccx.store.store import _digest
526
+ import hashlib
527
+ message = reason.encode("utf-8")
528
+ if not message or len(message) > 1024:
529
+ raise ValueError("local Codex refusal reason must be <=1024 bytes")
530
+ fields = {
531
+ "stage": stage, "reason": reason,
532
+ "reasonSha256": hashlib.sha256(message).hexdigest(),
533
+ "nativeRequestWritten": False,
534
+ "appServerPid": original["appServerPid"],
535
+ }
536
+ return {**fields, "localEvidenceSha256": _digest({
537
+ "workerId": original["workerId"],
538
+ "generation": original["generation"],
539
+ "createSha256": original["createSha256"],
540
+ "source": original["source"],
541
+ "submissionId": original["token"],
542
+ "daemonInstanceId": original["daemonInstanceId"],
543
+ "daemonPid": original["daemonPid"],
544
+ "nativeNotSubmitted": fields,
545
+ })}
546
+
547
+
548
+ def _record_original_codex_local_refusal(daemon, worker_id: str, record: dict,
549
+ source: str, token: str, *,
550
+ stage: str, reason: str) -> dict[str, Any]:
551
+ """fsync a pre-IPC local refusal under the original one-shot effect lock.
552
+
553
+ Recheck the existing same-ID held/unsafe reservations under self.lock,
554
+ in the same order as _submit_original_codex_thread. No competitor can
555
+ register a native writer between this check and the immutable marker
556
+ write. Never infer refusal from a missing marker or a timeout; only
557
+ this controlled pre-dispatch branch has positive zero-native-write proof.
558
+ """
559
+ from ccx.store.root import _read_json, _write_json, _flock_at
560
+ import hashlib
561
+ store = daemon._uuid_store
562
+ hold_key = (store.generation, worker_id, token)
563
+ original = {
564
+ "workerId": worker_id, "engine": "codex",
565
+ "generation": store.generation, "kind": "activate",
566
+ "token": token, "state": "submitted-unknown",
567
+ "source": source, "createSha256": record["createSha256"],
568
+ "daemonInstanceId": daemon.instance_id,
569
+ "daemonPid": os.getpid(),
570
+ "appServerPid": (daemon.client.process.pid if daemon.client else None),
571
+ **({"nativeMethod": "thread/fork", "forkAnchor": record["forkAnchor"]}
572
+ if record.get("forkAnchor") is not None else {}),
573
+ }
574
+ result = {
575
+ "ok": False, "error": reason, "workerId": worker_id,
576
+ "localRefusalReason": reason, "localRefusalProven": False,
577
+ }
578
+ key = hashlib.sha256(("activate\x00" + token).encode()).hexdigest()
579
+ try:
580
+ # Reentrant lock is intentionally held while writing the one-shot
581
+ # marker: new same-ID reservations need this exact lock first.
582
+ with daemon.lock:
583
+ with daemon._native_start_hold_lock:
584
+ if (hold_key in daemon._native_start_held
585
+ or hold_key in daemon._native_start_unsafe):
586
+ result["error"] = (
587
+ reason + "; existing same-ID native attempt unresolved"
588
+ )
589
+ return result
590
+ with store.lease():
591
+ with store._gen("runtime-metadata", "codex-native-effects",
592
+ worker_id, create=True) as directory:
593
+ with _flock_at(directory, key + ".lock", exclusive=True,
594
+ create=True, timeout=.5):
595
+ if _read_json(directory, key + ".json") is not None:
596
+ result["error"] = (
597
+ reason + "; original effect already fenced; "
598
+ "cannot prove non-submission"
599
+ )
600
+ return result
601
+ proof = _native_codex_local_proof(daemon, original, reason, stage)
602
+ _write_json(directory, key + ".json", {
603
+ **original, "nativeNotSubmitted": proof,
604
+ })
605
+ # From here onward the original marker alone permits offline
606
+ # reconciliation after crash or any transient claim-write failure.
607
+ result["localRefusalProven"] = True
608
+ except Exception as error:
609
+ result["error"] = (
610
+ f"{reason}; original not-submitted proof unavailable: "
611
+ f"{type(error).__name__}: {error}"
612
+ )[:400]
613
+ return result
614
+ try:
615
+ sealed = store.reconcile_codex_local_refusal(f"codex:{worker_id}")
616
+ if not isinstance(sealed, dict) or sealed.get("reason") != reason:
617
+ raise RuntimeError("local refusal terminal claim not sealed")
618
+ except Exception as error:
619
+ # Never lose the fsynced marker merely because the CLI's original
620
+ # claim lock is busy or its terminal claim fsync transiently fails.
621
+ result["error"] = (
622
+ f"{reason}; original marker durable, terminal claim pending: "
623
+ f"{type(error).__name__}: {error}"
624
+ )[:400]
625
+ return result
626
+
627
+
628
+ def _submit_original_codex_thread(daemon, worker_id: str, record: dict,
629
+ source: str, token: str, *,
630
+ admission_deadline: float | None = None) -> dict[str, Any]:
631
+ """Fence and write one native thread/start, not its slow completion.
632
+
633
+ Reserve bounded daemon memory before native admission. Original
634
+ positive provider evidence stays held until its marker is fsynced,
635
+ including after a transient callback failure. No native request is
636
+ ever submitted from status or the completion callback.
637
+ """
638
+ from ccx.store.root import NativeEffectIndeterminate, _read_json, _write_json, _flock_at
639
+ import hashlib
640
+ store = daemon._uuid_store
641
+ client = daemon.client
642
+ # Compatibility for explicit internal callers predating the deadline
643
+ # argument; regular CLI always supplies the original shared budget.
644
+ if admission_deadline is None:
645
+ admission_deadline = time.monotonic() + 2.0
646
+ if time.monotonic() >= admission_deadline:
647
+ # A generic elapsed deadline is not a proof of local non-write.
648
+ # Without an exact original ProtocolNotSent or pre-IPC gate,
649
+ # retain the original submitted-unknown fence.
650
+ raise NativeEffectIndeterminate("original Codex admission deadline expired")
651
+ if client is None or not daemon.ready.is_set():
652
+ return _record_original_codex_local_refusal(daemon, worker_id, record, source, token,
653
+ stage="not-ready", reason="Codex app-server not ready",
654
+ )
655
+ try:
656
+ caller_config = _original_caller_config(worker_id)
657
+ except Exception as error:
658
+ return _record_original_codex_local_refusal(daemon, worker_id, record, source, token, stage="shell-env",
659
+ reason=f"managed shell environment unavailable: {type(error).__name__}",
660
+ )
661
+ key = hashlib.sha256(("activate" + "\x00" + token).encode()).hexdigest()
662
+ args = record.get("createArgs") or {}
663
+ start_params = {
664
+ "cwd": record["cwd"],
665
+ "sandbox": args.get("sandbox"),
666
+ "approvalPolicy": args.get("approvalPolicy"),
667
+ "serviceName": "ccx", "threadSource": source,
668
+ "config": _with_default_effort(caller_config, args),
669
+ }
670
+ if args.get("model"):
671
+ start_params["model"] = args["model"]
672
+ fork = record.get("forkAnchor")
673
+ method = "thread/fork" if fork is not None else "thread/start"
674
+ request_params = dict(start_params)
675
+ if fork is not None:
676
+ request_params.pop("serviceName") # Not in pinned ThreadForkParams.
677
+ request_params["threadId"] = fork["sourceNativeThreadId"]
678
+ request_params["lastTurnId"] = fork["lastTurnId"]
679
+ request_params["excludeTurns"] = True
680
+ original = {
681
+ "workerId": worker_id, "engine": "codex",
682
+ "generation": store.generation,
683
+ "kind": "activate", "token": token,
684
+ "state": "submitted-unknown", "source": source,
685
+ "createSha256": record["createSha256"],
686
+ "daemonInstanceId": daemon.instance_id,
687
+ "daemonPid": os.getpid(),
688
+ "appServerPid": client.process.pid,
689
+ **({"nativeMethod": method, "forkAnchor": fork} if fork is not None else {}),
690
+ }
691
+ hold_key = (store.generation, worker_id, token)
692
+ held: dict[str, Any] = {
693
+ "original": original, "candidate": None, "rejection": None,
694
+ "unsafe": False, "error": None,
695
+ }
696
+ # Total order against the janitor's idle-exit decision. Reset linger
697
+ # at admission, not when (possibly slow) provider reply arrives.
698
+ with daemon.lock:
699
+ if daemon.stopping or daemon.stop.is_set():
700
+ return _record_original_codex_local_refusal(daemon, worker_id, record, source, token,
701
+ stage="daemon-stopping", reason="Codex daemon is stopping; no native start",
702
+ )
703
+ with daemon._native_start_hold_lock:
704
+ if hold_key in daemon._native_start_held or hold_key in daemon._native_start_unsafe:
705
+ raise NativeEffectIndeterminate(
706
+ "same original Codex native start already recorded; no replay"
707
+ )
708
+ full = len(daemon._native_start_held) >= _CODEX_NATIVE_START_HELD_LIMIT
709
+ if not full:
710
+ daemon._native_start_held[hold_key] = held
711
+ if full:
712
+ return _record_original_codex_local_refusal(daemon, worker_id, record, source, token,
713
+ stage="capacity", reason=(
714
+ "Codex native start retention full; new provider submission refused"
715
+ ),
716
+ )
717
+ daemon.empty_since = None
718
+
719
+ def capture_native_result(answer) -> None:
720
+ # Runs on the app-server reader (or immediately upon callback
721
+ # registration for a fast reply). Do not call any provider RPC.
722
+ try:
723
+ provider_rejection = answer.original_provider_rejection()
724
+ if provider_rejection is not None:
725
+ message = provider_rejection["message"]
726
+ from ccx.store.store import _digest
727
+ rejected = {
728
+ **provider_rejection,
729
+ "messageSha256": hashlib.sha256(message.encode("utf-8")).hexdigest(),
730
+ "appServerPid": original["appServerPid"],
731
+ "nativeErrorSha256": _digest({
732
+ "code": provider_rejection["code"],
733
+ "message": message,
734
+ "requestId": provider_rejection["requestId"],
735
+ "appServerPid": original["appServerPid"],
736
+ }),
737
+ }
738
+ # Retain exact original coded provider reply before any
739
+ # claim/marker fsync, just as for an accepted candidate.
740
+ # A numeric code on a local transport exception is not
741
+ # proof of provider rejection.
742
+ with daemon._native_start_hold_lock:
743
+ if daemon._native_start_held.get(hold_key) is not held:
744
+ raise NativeEffectIndeterminate(
745
+ "original provider rejection retention replaced"
746
+ )
747
+ held["rejection"] = rejected
748
+ daemon._save_held_codex_candidate(hold_key)
749
+ return
750
+ native = answer.result()
751
+ thread = native.get("thread") if isinstance(native, dict) else None
752
+ candidate_id = thread.get("id") if isinstance(thread, dict) else None
753
+ if not isinstance(candidate_id, str) or not candidate_id:
754
+ with daemon._native_start_hold_lock:
755
+ if daemon._native_start_held.get(hold_key) is held:
756
+ del daemon._native_start_held[hold_key]
757
+ return # no positive candidate; provider rejection is handled above
758
+ if fork is not None and candidate_id == fork["sourceNativeThreadId"]:
759
+ raise ValueError("native fork response reused its source thread ID")
760
+ if len(candidate_id.encode("utf-8")) > _CODEX_NATIVE_START_ID_MAX_BYTES:
761
+ raise ValueError("provider native thread id exceeds retained bound")
762
+ # Fork responses may include enormous copied history. Hash and
763
+ # bound a shallow response without serializing those turns;
764
+ # independent thread/read is the only history/point proof.
765
+ reply = (dict(native, thread={k: v for k, v in thread.items()
766
+ if k != "turns"})
767
+ if fork is not None else native)
768
+ raw = json.dumps(reply, sort_keys=True,
769
+ separators=(",", ":"), allow_nan=False).encode()
770
+ if len(raw) > _CODEX_NATIVE_START_REPLY_MAX_BYTES:
771
+ raise ValueError("provider native reply exceeds retained bound")
772
+ candidate = {
773
+ "threadId": candidate_id,
774
+ # Source is the original immutable request, not provider
775
+ # attribution. Independent thread/read is still mandatory.
776
+ "submittedSource": source,
777
+ "appServerPid": original["appServerPid"],
778
+ "nativeReplySha256": hashlib.sha256(raw).hexdigest(),
779
+ }
780
+ with daemon._native_start_hold_lock:
781
+ if daemon._native_start_held.get(hold_key) is not held:
782
+ raise NativeEffectIndeterminate(
783
+ "original reply retention was replaced before disk save"
784
+ )
785
+ held["candidate"] = candidate # before any filesystem call
786
+ daemon._save_held_codex_candidate(hold_key)
787
+ except (ProtocolError, ProtocolTransportError) as error:
788
+ # No confirmed positive reply. Never infer rejection from
789
+ # a timeout, missing code or transport loss.
790
+ with daemon._native_start_hold_lock:
791
+ if daemon._native_start_held.get(hold_key) is held:
792
+ del daemon._native_start_held[hold_key]
793
+ print(
794
+ f"ccx codexd: Codex original start outcome unconfirmed "
795
+ f"for {worker_id}: {error}",
796
+ file=sys.stderr, flush=True,
797
+ )
798
+ except Exception as error:
799
+ detail = f"{type(error).__name__}: {error}"[:250]
800
+ with daemon._native_start_hold_lock:
801
+ _mark_unsafe_native_start_locked(daemon, hold_key, held, detail)
802
+ print(
803
+ f"ccx codexd: cannot retain original native reply safely "
804
+ f"for {worker_id}: {detail}",
805
+ file=sys.stderr, flush=True,
806
+ )
807
+
808
+ pending = None
809
+ local_reason = None
810
+ local_marker_saved = False
811
+ local_marker_error = None
812
+ try:
813
+ with store.lease():
814
+ with store._gen("runtime-metadata", "codex-native-effects",
815
+ worker_id, create=True) as directory:
816
+ with _flock_at(directory, key + ".lock", exclusive=True,
817
+ create=True, timeout=.5):
818
+ if _read_json(directory, key + ".json") is not None:
819
+ raise NativeEffectIndeterminate(
820
+ "original Codex native start already submitted; "
821
+ "no replay after uncertain provider response"
822
+ )
823
+ _write_json(directory, key + ".json", original)
824
+ store.revalidate()
825
+ # sole native IPC write, after both original fences.
826
+ try:
827
+ pending = client.begin_request(
828
+ method, request_params,
829
+ write_deadline=admission_deadline,
830
+ )
831
+ except ProtocolNotSent as transport_error:
832
+ # AppServerClient knows that this exact
833
+ # request wrote zero bytes. Unlike a generic
834
+ # ProtocolError, numeric errno, timeout or partial
835
+ # ProtocolTransportError, it is attributable to
836
+ # the current thread/start attempt. The original
837
+ # per-ID effect lock is still held; no second
838
+ # writer can overtake this terminal marker.
839
+ if (type(client) is not AppServerClient
840
+ or type(transport_error) is not ProtocolNotSent
841
+ or transport_error.method != method):
842
+ raise
843
+ local_reason = (
844
+ f"Codex original {method} not submitted: "
845
+ + transport_error.message
846
+ )
847
+ proof = _native_codex_local_proof(daemon, original, local_reason, "transport-zero-bytes",
848
+ )
849
+ try:
850
+ _write_json(directory, key + ".json", {
851
+ **original, "nativeNotSubmitted": proof,
852
+ })
853
+ local_marker_saved = True
854
+ except Exception as persistence_error:
855
+ local_marker_error = (
856
+ f"{type(persistence_error).__name__}: "
857
+ f"{persistence_error}"
858
+ )
859
+ finally:
860
+ if pending is not None:
861
+ # Also register after a post-dispatch generation/lease failure:
862
+ # a delivered provider reply must remain capturable.
863
+ pending.add_done_callback(capture_native_result)
864
+ else:
865
+ # No positive original PendingRequest exists to retain.
866
+ with daemon._native_start_hold_lock:
867
+ if daemon._native_start_held.get(hold_key) is held:
868
+ del daemon._native_start_held[hold_key]
869
+ if local_reason is not None:
870
+ result = {
871
+ "ok": False, "workerId": worker_id,
872
+ "localRefusalReason": local_reason,
873
+ "localRefusalProven": local_marker_saved,
874
+ "error": (local_reason if local_marker_saved
875
+ else local_reason + "; original refusal marker unavailable: "
876
+ + str(local_marker_error)),
877
+ }
878
+ if local_marker_saved:
879
+ try:
880
+ sealed = store.reconcile_codex_local_refusal(
881
+ f"codex:{worker_id}"
882
+ )
883
+ if sealed is None or sealed.get("reason") != local_reason:
884
+ raise RuntimeError("native zero-byte refusal did not seal")
885
+ except Exception as error:
886
+ result["error"] = (
887
+ local_reason + "; durable marker retained, claim pending: "
888
+ + str(error)
889
+ )
890
+ return result
891
+ return {
892
+ "ok": True, "queued": True,
893
+ "workerId": worker_id, "engine": "codex",
894
+ "source": source, "submissionId": token,
895
+ # Transport admission, never native thread binding.
896
+ }
897
+
898
+
899
+ def _observe_original_codex_thread(daemon, message: dict[str, Any]) -> dict[str, Any]:
900
+ """Observe an exact original candidate or replay its coded refusal.
901
+
902
+ A provider thread with the same source tag but no local one-shot reply
903
+ candidate is not attributable to this dispatch. No thread/list scans,
904
+ native resume, or speculative recreation are allowed.
905
+ """
906
+ from ccx.store.root import NativeEffectIndeterminate, _read_json, _flock_at, _HASH
907
+ from ccx.store.store import _digest
908
+ import hashlib
909
+ store = daemon._uuid_store
910
+ owner = store.resolve(message.get("workerId"))
911
+ if owner.engine != "codex":
912
+ raise NativeEffectIndeterminate("Codex original full worker UUID required")
913
+ wid = owner.worker_id
914
+ source = f"ccx-worker:{wid}"
915
+ activation = store.native_activation(owner.worker)
916
+ token = message.get("submissionId")
917
+ if (activation.get("source") != source
918
+ or activation.get("submissionId") != token
919
+ or not isinstance(token, str) or not token):
920
+ raise NativeEffectIndeterminate("original submitted Codex activation unproven")
921
+ if activation.get("state") == "rejected":
922
+ sealed = store.reconcile_codex_native_rejection(owner.worker)
923
+ if sealed is None:
924
+ raise NativeEffectIndeterminate("original rejected claim lost provider proof")
925
+ return {"ok": True, **sealed}
926
+ if activation.get("state") == "local-refused":
927
+ local = store.reconcile_codex_local_refusal(owner.worker)
928
+ if local is None:
929
+ raise NativeEffectIndeterminate("original local-refused claim lost proof")
930
+ return {"ok": True, **local}
931
+ # For submitted-unknown first allow the held reply to persist. An
932
+ # unconditional effect read here could contend with the callback
933
+ # before its original result was saved, or change original tamper
934
+ # diagnostics. Marker-to-claim recovery follows the held step below.
935
+ if activation.get("state") != "submitted-unknown":
936
+ raise NativeEffectIndeterminate("original submitted Codex activation unproven")
937
+ record = store.record(owner.worker)
938
+ native = record.get("native") or {}
939
+ if (record.get("fingerprintVersion") != 2
940
+ or record.get("terminalOnly") or record.get("nativeCreate")
941
+ or native.get("threadId") is not None
942
+ or native.get("nativeState") != "not-started-proven"):
943
+ raise NativeEffectIndeterminate("original Codex first native record not eligible")
944
+ # Compare the immutable source/preimage, not mutable current display
945
+ # name, short, provider thread id, or reconstructed operation hash.
946
+ with store.lease():
947
+ original_owner = store._owner(wid)
948
+ claim = store._claim(wid)
949
+ if not isinstance(original_owner, dict) or not isinstance(claim, dict):
950
+ raise NativeEffectIndeterminate("Codex owner/create claim missing")
951
+ fork = record.get("forkAnchor")
952
+ if fork is not None:
953
+ from ccx.store.store import fingerprint_v2, codex_fork_lineage
954
+ lineage = record.get("forkedFrom")
955
+ if (not isinstance(fork, dict) or fork.get("engine") != "codex"
956
+ or fork.get("cwd") != record.get("cwd")
957
+ or not isinstance(fork.get("createArgs"), dict)
958
+ or fingerprint_v2(
959
+ engine="codex", cwd=fork["cwd"],
960
+ create_args=fork["createArgs"], fork_anchor=fork,
961
+ ) != record["createSha256"]
962
+ or not isinstance(lineage, dict)
963
+ or lineage != codex_fork_lineage(fork, child_id=wid)):
964
+ raise NativeEffectIndeterminate("Codex original fork lineage/point changed")
965
+ store._check_original_create_intent(
966
+ original_owner, engine="codex", worker_id=wid,
967
+ fingerprint=record["createSha256"], native={}, anchor=fork,
968
+ )
969
+ if (claim.get("createSha256") != record["createSha256"]
970
+ or claim.get("fingerprintVersion") != 2
971
+ or claim.get("nativeBinding") != {}
972
+ or claim.get("forkAnchor") != fork):
973
+ raise NativeEffectIndeterminate("Codex original create hash mismatch")
974
+ key = hashlib.sha256(("activate" + "\x00" + token).encode()).hexdigest()
975
+ # Status handler may retry only an in-memory original positive native
976
+ # reply whose callback met a recognized transient disk/lock failure.
977
+ # Outside the owner lease: never nest a second lease across a native
978
+ # observation, and never invoke thread/start, resume or list here.
979
+ held = daemon._save_held_codex_candidate((store.generation, wid, token))
980
+ if held["status"] == "saved":
981
+ # A status handler may have just sealed the original rejection.
982
+ # The same exact provider proof is also available offline from
983
+ # its marker/claim; no native thread/read or native start needed.
984
+ sealed = store.reconcile_codex_native_rejection(owner.worker)
985
+ if sealed is not None:
986
+ return {"ok": True, **sealed}
987
+ if held["status"] == "unsafe":
988
+ raise NativeEffectIndeterminate(
989
+ "original Codex candidate persistence unsafe: " + held["error"]
990
+ )
991
+ if held["status"] == "transient":
992
+ return {"ok": True, "status": "indeterminate", "workerId": wid,
993
+ "candidatePersistence": "transient", "error": held["error"]}
994
+ with store.lease():
995
+ try:
996
+ with store._gen("runtime-metadata", "codex-native-effects",
997
+ wid) as directory:
998
+ with _flock_at(directory, key + ".lock", exclusive=False,
999
+ create=False, timeout=.5):
1000
+ saved = _read_json(directory, key + ".json")
1001
+ except FileNotFoundError:
1002
+ return {"ok": True, "status": "indeterminate", "workerId": wid}
1003
+ if saved is None:
1004
+ return {"ok": True, "status": "indeterminate", "workerId": wid}
1005
+ if saved.get("nativeNotSubmitted") is not None:
1006
+ local = store.reconcile_codex_local_refusal(owner.worker)
1007
+ if local is None:
1008
+ raise NativeEffectIndeterminate("original local refusal marker unproven")
1009
+ return {"ok": True, **local}
1010
+ if saved.get("nativeRejected") is not None:
1011
+ sealed = store.reconcile_codex_native_rejection(owner.worker)
1012
+ if sealed is None:
1013
+ raise NativeEffectIndeterminate("original rejected marker not reconciled")
1014
+ return {"ok": True, **sealed}
1015
+ if (not isinstance(saved, dict) or any(saved.get(k) != v for k, v in {
1016
+ "workerId": wid, "engine": "codex", "generation": store.generation,
1017
+ "kind": "activate", "token": token, "state": "submitted-unknown",
1018
+ "source": source, "createSha256": record["createSha256"],
1019
+ }.items()) or (
1020
+ (saved.get("nativeMethod") != "thread/fork"
1021
+ or saved.get("forkAnchor") != fork)
1022
+ if fork is not None else
1023
+ ("nativeMethod" in saved or "forkAnchor" in saved)
1024
+ ) or not isinstance(saved.get("daemonInstanceId"), str)
1025
+ or not saved["daemonInstanceId"]
1026
+ or type(saved.get("daemonPid")) is not int
1027
+ or saved["daemonPid"] <= 0
1028
+ or type(saved.get("appServerPid")) is not int
1029
+ or saved["appServerPid"] <= 0):
1030
+ raise NativeEffectIndeterminate("original Codex one-shot marker invalid")
1031
+ if saved.get("nativeRejected") is not None:
1032
+ raise NativeEffectIndeterminate("native rejected marker contradicts candidate")
1033
+ candidate = saved.get("nativeCandidate")
1034
+ if candidate is None:
1035
+ return {"ok": True, "status": "indeterminate", "workerId": wid}
1036
+ if (not isinstance(candidate, dict)
1037
+ or not isinstance(candidate.get("threadId"), str)
1038
+ or not candidate["threadId"]
1039
+ or (fork is not None and candidate["threadId"] == fork["sourceNativeThreadId"])
1040
+ or candidate.get("submittedSource") != source
1041
+ or candidate.get("appServerPid") != saved["appServerPid"]
1042
+ or not isinstance(candidate.get("nativeReplySha256"), str)
1043
+ or not _HASH.fullmatch(candidate["nativeReplySha256"])):
1044
+ raise NativeEffectIndeterminate("Codex candidate lacks original effect proof")
1045
+ client = daemon.client
1046
+ if client is None or not daemon.ready.is_set():
1047
+ raise NativeEffectIndeterminate("Codex app-server not available to inspect")
1048
+ if fork is None:
1049
+ observed = client.request("thread/read", {
1050
+ "threadId": candidate["threadId"], "includeTurns": False,
1051
+ }, timeout=2)
1052
+ thread = observed.get("thread") if isinstance(observed, dict) else None
1053
+ else:
1054
+ from ccx.codex.history import read_original_thread
1055
+ try:
1056
+ thread = read_original_thread(
1057
+ client.request, candidate["threadId"],
1058
+ deadline=time.monotonic() + 2, limit=1, newest_first=True,
1059
+ expected_source=source,
1060
+ )
1061
+ except ValueError as error:
1062
+ raise NativeEffectIndeterminate(
1063
+ "independent Codex fork read did not prove original source"
1064
+ ) from error
1065
+ if (not isinstance(thread, dict)
1066
+ or thread.get("id") != candidate["threadId"]
1067
+ or thread.get("threadSource") != source):
1068
+ raise NativeEffectIndeterminate(
1069
+ "independent Codex thread/read did not prove original candidate source"
1070
+ )
1071
+ if fork is not None:
1072
+ turns = thread.get("turns")
1073
+ last = turns[-1] if isinstance(turns, list) and turns else None
1074
+ if (thread.get("forkedFromId") != fork["sourceNativeThreadId"]
1075
+ or not isinstance(last, dict)
1076
+ or last.get("id") != fork["lastTurnId"]
1077
+ or last.get("status") != "completed"):
1078
+ raise NativeEffectIndeterminate(
1079
+ "independent Codex fork thread/read lacks original source/point proof"
1080
+ )
1081
+ evidence = {
1082
+ "workerId": wid, "threadSource": source,
1083
+ "threadId": candidate["threadId"], "submissionId": token,
1084
+ "daemonPid": saved["daemonPid"],
1085
+ "appServerPid": saved["appServerPid"],
1086
+ }
1087
+ if fork is not None:
1088
+ # These facts come from one independent native read; the original
1089
+ # candidate stays the ordinary response/thread/PID/hash shape.
1090
+ evidence.update(
1091
+ nativeMethod="thread/fork", forkAnchor=fork,
1092
+ forkedFromId=fork["sourceNativeThreadId"],
1093
+ lastTurnId=fork["lastTurnId"], lastTurnStatus="completed",
1094
+ nativeReplySha256=candidate["nativeReplySha256"],
1095
+ )
1096
+ store.revalidate()
1097
+ return {
1098
+ "accepted": True, "workerId": wid, "engine": "codex",
1099
+ "source": source, "submissionId": token,
1100
+ "nativeEvidenceSha256": _digest(evidence),
1101
+ "nativeBinding": {"threadId": candidate["threadId"]},
1102
+ }
1103
+
1104
+
1105
+ def _original_full_uuid_native(daemon, message: dict[str, Any]) -> dict[str, Any]:
1106
+ """One generation-bound Codex native action through the owning daemon.
1107
+
1108
+ A client must have durably fsynced the original generation-bound
1109
+ native intent before this daemon can touch the provider. The daemon
1110
+ independently gates exact full owner/source/thread/receipt and
1111
+ publishes a second per-operation local single-dispatch marker before
1112
+ the app-server write: a lost socket response never replays native.
1113
+ """
1114
+ from ccx.store.root import NativeEffectIndeterminate, _read_json, _write_json, _flock_at
1115
+ from ccx.store.store import _digest
1116
+ from ccx.codex.delivery import read_receipt as semantic_receipt
1117
+ from ccx.store.identity import EngineMismatch
1118
+ import hashlib
1119
+ store = daemon._uuid_store
1120
+ store.revalidate()
1121
+ owner = store.resolve(message.get("workerId"))
1122
+ if owner.engine != "codex":
1123
+ raise EngineMismatch("Codex daemon needs exact owned Codex full UUID")
1124
+ worker_id = owner.worker_id
1125
+ action = message.get("op")
1126
+ record = store.record(owner.worker)
1127
+ native = record.get("native") or {}
1128
+ if not isinstance(native, dict):
1129
+ raise RuntimeError("Codex original native binding malformed")
1130
+ # First native activation still issues only a freshly minted UUID tag.
1131
+ source = (f"ccx-worker:{worker_id}" if action == "native-activate"
1132
+ else store.codex_source(owner.worker))
1133
+ if action == "native-observe":
1134
+ if native.get("threadSource") != source:
1135
+ raise RuntimeError("native Codex thread source not bound")
1136
+ thread_id = native.get("threadId")
1137
+ turn_id = message.get("turnId")
1138
+ delivery_id = message.get("deliveryId")
1139
+ if (not isinstance(thread_id,str) or not isinstance(turn_id,str)
1140
+ or not turn_id or not isinstance(delivery_id,str)
1141
+ or not delivery_id):
1142
+ raise RuntimeError("native completion requires original thread, turn and delivery")
1143
+ semantic = semantic_receipt(owner.worker, delivery_id)
1144
+ original_effect = store.read_receipt(
1145
+ owner.worker, "codex-send", delivery_id,
1146
+ )
1147
+ if (not isinstance(semantic,dict)
1148
+ or semantic.get("state") != "accepted"
1149
+ or semantic.get("turnId") != turn_id
1150
+ or not isinstance(original_effect,dict)
1151
+ or original_effect.get("state") != "accepted"
1152
+ or original_effect.get("workerId") != worker_id):
1153
+ raise RuntimeError(
1154
+ "native turn completion has no exact original accepted"
1155
+ " delivery and effect receipt"
1156
+ )
1157
+ deadline = time.monotonic() + _native_history_budget(message)
1158
+ from ccx.codex.history import read_original_thread
1159
+ thread = read_original_thread(
1160
+ daemon.client.request, thread_id, deadline=deadline,
1161
+ exact_turn_id=turn_id, expected_source=source,
1162
+ )
1163
+ if (not isinstance(thread,dict)
1164
+ or thread.get("id") != thread_id
1165
+ or thread.get("threadSource") != source):
1166
+ raise RuntimeError("native thread/read has no original full UUID source")
1167
+ matches = [
1168
+ turn for turn in (thread.get("turns") or [])
1169
+ if isinstance(turn,dict) and turn.get("id") == turn_id
1170
+ ]
1171
+ if len(matches) != 1:
1172
+ return {"ok":True, "status":"pending", "workerId":worker_id}
1173
+ native_status = matches[0].get("status")
1174
+ if native_status in ("failed", "interrupted", "cancelled"):
1175
+ # thread/read can report a still-running turn as interrupted;
1176
+ # trust it once this runtime no longer runs that turn.
1177
+ with daemon.lock:
1178
+ live = daemon.workers.get(owner.worker_id)
1179
+ if (live is not None and live.loaded and
1180
+ live.record["lifecycle"].get("activeTurnId") == turn_id):
1181
+ return {"ok":True, "status":"pending", "workerId":worker_id}
1182
+ return {"ok":True, "status":native_status,
1183
+ "workerId":worker_id, "turnId":turn_id,
1184
+ "deliveryId":delivery_id,
1185
+ "error":matches[0].get("error")}
1186
+ if native_status != "completed":
1187
+ return {"ok":True, "status":"pending", "workerId":worker_id}
1188
+ # A completed turn without an agent message is still completed.
1189
+ # Phase-aware final_answer wins over later phase-less text;
1190
+ # commentary alone is not a final reply.
1191
+ selected = select_report_for_turn(
1192
+ matches[0], live_report=record.get("lastReport"))
1193
+ text = selected["text"] if selected is not None else ""
1194
+ return {
1195
+ "ok":True, "status":"completed", "workerId":worker_id,
1196
+ "engine":"codex", "nativeSource":source,
1197
+ "threadId":thread_id, "turnId":turn_id,
1198
+ "deliveryId":delivery_id, "text":text,
1199
+ "nativeEvidenceSha256":_digest({
1200
+ "source":source, "threadId":thread_id,
1201
+ "turnId":turn_id,"status":"completed",
1202
+ "text":text,
1203
+ }),
1204
+ }
1205
+
1206
+ if action == "native-activate":
1207
+ activation = store.native_activation(owner.worker)
1208
+ if (activation.get("state") != "submitted-unknown"
1209
+ or activation.get("source") != source
1210
+ or activation.get("submissionId") != message.get("submissionId")
1211
+ or native.get("threadId") is not None):
1212
+ raise RuntimeError("original Codex thread/create submitted claim unproven")
1213
+ token = activation["submissionId"]
1214
+ if (record.get("fingerprintVersion") != 2
1215
+ or record.get("terminalOnly") or record.get("nativeCreate")
1216
+ or native.get("nativeState") != "not-started-proven"):
1217
+ raise NativeEffectIndeterminate("original first Codex native record invalid")
1218
+ with store.lease():
1219
+ owner_entry, creation_claim = store._owner(worker_id), store._claim(worker_id)
1220
+ if not isinstance(owner_entry, dict) or not isinstance(creation_claim, dict):
1221
+ raise NativeEffectIndeterminate("original Codex create claim missing")
1222
+ fork = record.get("forkAnchor")
1223
+ if fork is not None:
1224
+ from ccx.store.store import fingerprint_v2, codex_fork_lineage
1225
+ lineage = record.get("forkedFrom")
1226
+ if (not isinstance(fork, dict) or fork.get("engine") != "codex"
1227
+ or fork.get("cwd") != record.get("cwd")
1228
+ or not isinstance(fork.get("createArgs"), dict)
1229
+ or fingerprint_v2(
1230
+ engine="codex", cwd=fork["cwd"],
1231
+ create_args=fork["createArgs"], fork_anchor=fork,
1232
+ ) != record["createSha256"]
1233
+ or not isinstance(lineage, dict)
1234
+ or lineage != codex_fork_lineage(fork, child_id=worker_id)):
1235
+ raise NativeEffectIndeterminate("original Codex frozen fork claim invalid")
1236
+ store._check_original_create_intent(
1237
+ owner_entry, engine="codex", worker_id=worker_id,
1238
+ fingerprint=record["createSha256"], native={}, anchor=fork,
1239
+ )
1240
+ if (creation_claim.get("createSha256") != record["createSha256"]
1241
+ or creation_claim.get("nativeBinding") != {}
1242
+ or creation_claim.get("fingerprintVersion") != 2
1243
+ or creation_claim.get("forkAnchor") != fork):
1244
+ raise NativeEffectIndeterminate("original Codex create hash changed")
1245
+ # Absolute elapsed budget from the CLI's original <=2-second
1246
+ # admission. Never reset it after this daemon's generation/effect
1247
+ # lease work. Older internal callers receive a bounded local
1248
+ # budget; no proof of no-submission is inferred from a timeout.
1249
+ admission_deadline = message.get("nativeAdmissionDeadline")
1250
+ if admission_deadline is not None:
1251
+ import math
1252
+ if (type(admission_deadline) not in (int, float)
1253
+ or not math.isfinite(admission_deadline)
1254
+ or admission_deadline - time.monotonic() > 2.0):
1255
+ raise NativeEffectIndeterminate("invalid Codex admission deadline")
1256
+ return daemon._submit_original_codex_thread(
1257
+ worker_id, record, source, token,
1258
+ admission_deadline=admission_deadline,
1259
+ )
1260
+ elif action == "native-send":
1261
+ from ccx.codex.delivery import intent_spec, requested_mode, mark_owned, mark_submitted
1262
+ from ccx.codex.provenance import parse_client_message_id
1263
+ from ccx.claude.usage import _merge_caps
1264
+ from ccx.workers import _check_caps
1265
+
1266
+ caps = message.get("cap")
1267
+ if caps is not None:
1268
+ _check_caps(caps, engine="codex")
1269
+
1270
+ store.authorize_native_action(owner.worker, "send")
1271
+ thread_id = native.get("threadId")
1272
+ if (not isinstance(thread_id, str) or not thread_id
1273
+ or native.get("threadSource") != source
1274
+ or thread_id != message.get("threadId")):
1275
+ raise RuntimeError("original Codex native thread differs")
1276
+ token = message.get("deliveryId")
1277
+ if not isinstance(token, str) or not token:
1278
+ raise RuntimeError("original delivery token required")
1279
+ receipt = semantic_receipt(owner.worker, token)
1280
+ effect = store.read_receipt(owner.worker, "codex-send", token)
1281
+ if not isinstance(receipt, dict):
1282
+ raise RuntimeError("original Codex semantic receipt absent")
1283
+ if receipt.get("cap") != caps:
1284
+ raise RuntimeError("original Codex cap differs from immutable delivery")
1285
+ mode = requested_mode(receipt)
1286
+ if (receipt.get("state") != (
1287
+ "submitted" if mode == "new_turn_only" else "prepared")
1288
+ or receipt.get("clientUserMessageId")
1289
+ != message.get("clientUserMessageId")
1290
+ or not isinstance(effect, dict)
1291
+ or effect.get("state") != "submitted-unknown"
1292
+ or effect.get("action") != "send"
1293
+ or effect.get("workerId") != worker_id
1294
+ or effect.get("generation") != store.generation
1295
+ or effect.get("intentSha256") != _digest(intent_spec(receipt))):
1296
+ raise RuntimeError("original Codex send effect/semantic receipt unproven")
1297
+ if (not isinstance(message.get("text"), str)
1298
+ or hashlib.sha256(message["text"].encode()).hexdigest()
1299
+ != receipt.get("promptSha256")):
1300
+ raise RuntimeError("original Codex send payload changed")
1301
+ original_worker = daemon._adopt_original_native_worker(owner.worker)
1302
+ if daemon.client is None or not daemon.ready.is_set():
1303
+ raise RuntimeError("Codex app-server not ready")
1304
+
1305
+ def refused(code: str, detail: str, *,
1306
+ current: dict | None = None) -> dict:
1307
+ frozen = current if current is not None else receipt
1308
+ chosen = send_receipt_route(frozen)
1309
+ target = frozen.get("targetTurnId")
1310
+ client_id = frozen.get("clientUserMessageId")
1311
+ return {
1312
+ "accepted": False, "nativeStatus": "rejected",
1313
+ "rejectionCode": code, "rejectionMessage": detail,
1314
+ "workerId": worker_id, "engine": "codex",
1315
+ "nativeSource": source, "threadId": thread_id,
1316
+ "deliveryId": token, "route": chosen,
1317
+ "targetTurnId": target, "clientUserMessageId": client_id,
1318
+ "nativeEvidenceSha256": _digest({
1319
+ "source": source, "threadId": thread_id,
1320
+ "deliveryId": token, "code": code,
1321
+ "route": chosen, "targetTurnId": target,
1322
+ "clientUserMessageId": client_id,
1323
+ }),
1324
+ }
1325
+
1326
+ # Same operation lock as cancel/kill/compact, before store.lease.
1327
+ if original_worker.killed.is_set() or original_worker.kill_intent.is_set():
1328
+ return refused("worker-killed", "original worker is being killed")
1329
+ with original_worker.operation_lock:
1330
+ if not daemon._still_registered(original_worker):
1331
+ return refused("not-registered", "original worker is no longer registered")
1332
+ store.revalidate()
1333
+ latest = store.record(owner.worker)
1334
+ bound = latest.get("native") or {}
1335
+ if (bound.get("threadId") != thread_id
1336
+ or bound.get("threadSource") != source):
1337
+ raise RuntimeError("original Codex native binding changed")
1338
+ with original_worker.lock:
1339
+ lifecycle = original_worker.record.get("lifecycle") or {}
1340
+ state = lifecycle.get("state")
1341
+ live = (original_worker.loaded
1342
+ and not original_worker.killed.is_set()
1343
+ and not original_worker.kill_intent.is_set()
1344
+ and not original_worker.teardown_claimed)
1345
+ busy_compact = bool(original_worker.record.get("activeCompact"))
1346
+ current_turn = lifecycle.get("activeTurnId")
1347
+ launch_pending = original_worker.launch is not None
1348
+ route_kind, target_turn = None, None
1349
+ if live and not busy_compact and not launch_pending:
1350
+ if state == "idle" and mode != "steer_only":
1351
+ route_kind = "new_turn"
1352
+ elif state == "busy" and mode != "new_turn_only":
1353
+ if isinstance(current_turn, str) and current_turn:
1354
+ route_kind, target_turn = "steer", current_turn
1355
+ if route_kind is None:
1356
+ if (live and not busy_compact and not launch_pending
1357
+ and state == "busy" and mode == "new_turn_only"):
1358
+ # Positive, lock-held proof: the live original owns a
1359
+ # turn, so a strict new turn is refused before any RPC.
1360
+ return refused(
1361
+ "busy", f"original worker cannot accept {mode} in state=busy",
1362
+ )
1363
+ return refused(
1364
+ "compaction-busy" if busy_compact else "worker-state",
1365
+ ("compaction owns the input surface" if busy_compact else
1366
+ f"original worker cannot accept {mode} in state={state}"),
1367
+ )
1368
+
1369
+ # Preflight before the provider turn, using a cached window or
1370
+ # one bounded account read when no fresh signal exists.
1371
+ if caps:
1372
+ trip = daemon._cap_at_admission(caps)
1373
+ if trip:
1374
+ key, used, cap = trip
1375
+ return refused(
1376
+ "budget-stopped", f"usage cap {key} already exceeded "
1377
+ f"({used:g}% >= {cap:g}%); no "
1378
+ f"{'message' if route_kind == 'steer' else 'turn'} delivered",
1379
+ )
1380
+
1381
+ if mode != "new_turn_only":
1382
+ receipt = mark_owned(
1383
+ owner.worker, token, route_kind=route_kind,
1384
+ target_turn_id=target_turn,
1385
+ )
1386
+ receipt = mark_submitted(
1387
+ owner.worker, token, runtime_id=daemon.instance_id,
1388
+ )
1389
+ else:
1390
+ receipt = semantic_receipt(owner.worker, token)
1391
+ if (receipt.get("state") != "submitted"
1392
+ or send_receipt_route(receipt) != route_kind
1393
+ or receipt.get("targetTurnId") != target_turn):
1394
+ raise RuntimeError("native Codex route not frozen in original receipt")
1395
+ client_id = receipt.get("clientUserMessageId")
1396
+ identity = parse_client_message_id(client_id)
1397
+ if (not isinstance(identity, dict)
1398
+ or identity.get("workerId") != worker_id
1399
+ or identity.get("kind") != (
1400
+ "steer" if route_kind == "steer" else "start")):
1401
+ raise RuntimeError("original Codex native client message provenance invalid")
1402
+
1403
+ # A later asynchronous turn notification can still move R;
1404
+ # expectedTurnId makes such a native steer a positive refusal.
1405
+ with original_worker.lock:
1406
+ current = original_worker.record.get("lifecycle") or {}
1407
+ if (original_worker.killed.is_set()
1408
+ or original_worker.kill_intent.is_set()
1409
+ or not original_worker.loaded
1410
+ or original_worker.record.get("activeCompact")
1411
+ or current.get("state") != (
1412
+ "busy" if route_kind == "steer" else "idle")
1413
+ or (route_kind == "steer"
1414
+ and current.get("activeTurnId") != target_turn)):
1415
+ return refused(
1416
+ "worker-changed",
1417
+ "original native turn or input owner changed before request",
1418
+ current=receipt,
1419
+ )
1420
+
1421
+ key = hashlib.sha256(("send" + "\x00" + token).encode()).hexdigest()
1422
+ with store.lease():
1423
+ with store._gen(
1424
+ "runtime-metadata", "codex-native-effects", worker_id,
1425
+ create=True,
1426
+ ) as directory:
1427
+ with _flock_at(
1428
+ directory, key+".lock", exclusive=True,
1429
+ create=True, timeout=.5,
1430
+ ):
1431
+ if _read_json(directory, key+".json") is not None:
1432
+ raise NativeEffectIndeterminate(
1433
+ "this original native Codex request was already "
1434
+ "submitted, provider result unknown; no replay"
1435
+ )
1436
+ _write_json(directory, key+".json", {
1437
+ "workerId": worker_id, "engine": "codex",
1438
+ "generation": store.generation, "kind": "send",
1439
+ "token": token, "requestedMode": mode,
1440
+ "route": route_kind, "targetTurnId": target_turn,
1441
+ "clientUserMessageId": client_id,
1442
+ "intentSha256": effect["intentSha256"],
1443
+ "state": "submitted-unknown",
1444
+ })
1445
+ store.revalidate()
1446
+ if route_kind == "steer":
1447
+ try:
1448
+ daemon.client.request("turn/steer", {
1449
+ "threadId": thread_id,
1450
+ "expectedTurnId": target_turn,
1451
+ "input": [{"type": "text", "text": message["text"]}],
1452
+ "clientUserMessageId": client_id,
1453
+ }, timeout=2)
1454
+ except ProtocolError as error:
1455
+ if (not isinstance(error, ProtocolTransportError)
1456
+ and error.request_id is not None):
1457
+ return refused(
1458
+ "native-rejected", str(error), current=receipt,
1459
+ )
1460
+ raise
1461
+ turn_id = target_turn
1462
+ else:
1463
+ turn_params = {
1464
+ "threadId": thread_id,
1465
+ "input": [{"type": "text", "text": message["text"]}],
1466
+ "clientUserMessageId": client_id,
1467
+ }
1468
+ for setting in ("model", "effort"):
1469
+ value = (latest.get("createArgs") or {}).get(setting)
1470
+ if value:
1471
+ turn_params[setting] = value
1472
+ with original_worker.lock:
1473
+ if (original_worker.killed.is_set()
1474
+ or original_worker.kill_intent.is_set()):
1475
+ raise RuntimeError(
1476
+ "original worker is being killed; no native turn/start"
1477
+ )
1478
+ launch = _TurnLaunch()
1479
+ original_worker.launch = launch
1480
+ lifecycle = original_worker.record["lifecycle"]
1481
+ pre_launch_lifecycle = dict(lifecycle)
1482
+ lifecycle["state"] = "busy"
1483
+ lifecycle["since"] = now_ms()
1484
+ lifecycle["activeTurnId"] = None
1485
+ original_worker.record["nativeTurnStart"] = (
1486
+ original_worker._native_turn_start_marker(
1487
+ kind="send", client_message_id=client_id,
1488
+ delivery_id=token,
1489
+ )
1490
+ )
1491
+ original_worker._save()
1492
+ turn_id = None
1493
+ try:
1494
+ response = daemon.client.request(
1495
+ "turn/start", turn_params, timeout=2,
1496
+ )
1497
+ turn = response.get("turn") if isinstance(response, dict) else None
1498
+ turn_id = turn.get("id") if isinstance(turn, dict) else None
1499
+ if not isinstance(turn_id, str) or not turn_id:
1500
+ raise NativeEffectIndeterminate(
1501
+ "Codex turn/start reply lacks exact native turn"
1502
+ )
1503
+ except Exception as error:
1504
+ with original_worker.lock:
1505
+ if (isinstance(error, ProtocolError)
1506
+ and not isinstance(error, ProtocolTransportError)
1507
+ and error.request_id is not None):
1508
+ original_worker.record["nativeTurnStart"] = None
1509
+ original_worker.record["lifecycle"] = pre_launch_lifecycle
1510
+ return refused(
1511
+ "native-rejected", str(error), current=receipt,
1512
+ )
1513
+ original_worker._mark_native_turn_start_unknown_locked(error)
1514
+ raise
1515
+ finally:
1516
+ with original_worker.lock:
1517
+ if original_worker.launch is launch:
1518
+ original_worker.launch = None
1519
+ launch.turn_id = turn_id
1520
+ if turn_id:
1521
+ original_worker.record["nativeTurnStart"] = None
1522
+ original_worker.record["historyKnownEmpty"] = False
1523
+ if original_worker.record["lifecycle"].get("state") == "busy":
1524
+ original_worker.record["lifecycle"]["activeTurnId"] = (
1525
+ original_worker.record["lifecycle"].get("activeTurnId")
1526
+ or turn_id
1527
+ )
1528
+ try:
1529
+ original_worker._save()
1530
+ finally:
1531
+ launch.done.set()
1532
+ if original_worker.killed.is_set():
1533
+ original_worker._interrupt_turn(turn_id)
1534
+ daemon_release._abandon_killed_load(daemon, original_worker)
1535
+ raise RuntimeError(
1536
+ "original worker killed during native turn/start"
1537
+ )
1538
+ if caps:
1539
+ with original_worker.lock:
1540
+ if (original_worker.record["lifecycle"].get("activeTurnId")
1541
+ == turn_id
1542
+ and (original_worker.record.get("quotaStop") or {}).get("turnId")
1543
+ != turn_id):
1544
+ original_worker.cap_turn_id = turn_id
1545
+ original_worker.active_caps = _merge_caps([
1546
+ original_worker.active_caps
1547
+ if route_kind == "steer" else None,
1548
+ caps,
1549
+ ])
1550
+ original_worker.check_active_caps(
1551
+ daemon._cap_data_from_cache())
1552
+ return {
1553
+ "accepted": True, "workerId": worker_id,
1554
+ "engine": "codex", "nativeSource": source,
1555
+ "threadId": thread_id, "deliveryId": token,
1556
+ "clientUserMessageId": client_id,
1557
+ "route": route_kind, "targetTurnId": target_turn,
1558
+ "turnId": turn_id,
1559
+ "nativeEvidenceSha256": _digest({
1560
+ "source": source, "threadId": thread_id,
1561
+ "turnId": turn_id, "deliveryId": token,
1562
+ "route": route_kind, "targetTurnId": target_turn,
1563
+ "clientUserMessageId": client_id,
1564
+ }),
1565
+ }
1566
+ else:
1567
+ raise RuntimeError("unknown native Codex effect")
1568
+
1569
+
1570
+ def _original_history_binding(daemon, message: dict[str, Any]):
1571
+ """Resolve one immutable original Codex worker/thread for read-only RPC."""
1572
+ ctx = daemon._uuid_store
1573
+ owner = ctx.resolve(message.get("workerId"))
1574
+ if owner.engine != "codex":
1575
+ raise ValueError("Codex history needs original full Codex worker UUID")
1576
+ original = ctx.record(owner.worker)
1577
+ native = original.get("native") or {}
1578
+ tid = native.get("threadId") if isinstance(native, dict) else None
1579
+ source = ctx.codex_source(owner.worker)
1580
+ claim = ctx.native_activation(owner.worker)
1581
+ binding = claim.get("nativeBinding")
1582
+ if (not isinstance(tid, str) or not tid
1583
+ or native.get("threadSource") != source
1584
+ or claim.get("state") != "confirmed"
1585
+ or claim.get("source") != source
1586
+ or not isinstance(binding, dict)
1587
+ or binding.get("threadId") != tid
1588
+ or message.get("threadId", tid) != tid):
1589
+ raise ValueError("native thread not positively bound to original full UUID")
1590
+ return owner, tid, source