nexo-brain 2.6.17 → 2.6.20
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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +17 -5
- package/bin/nexo-brain.js +17 -12
- package/package.json +1 -1
- package/src/agent_runner.py +3 -0
- package/src/client_preferences.py +12 -14
- package/src/client_sync.py +42 -20
- package/src/cognitive/_search.py +30 -3
- package/src/doctor/providers/runtime.py +325 -0
- package/src/plugins/cognitive_memory.py +4 -0
- package/src/runtime_power.py +8 -4
- package/src/scripts/deep-sleep/apply_findings.py +393 -0
- package/src/scripts/deep-sleep/collect.py +221 -0
- package/src/scripts/deep-sleep/synthesize-prompt.md +13 -0
- package/src/scripts/deep-sleep/synthesize.py +1 -0
|
@@ -54,18 +54,162 @@ def _codex_bootstrap_config_status() -> dict:
|
|
|
54
54
|
"error": str(exc),
|
|
55
55
|
}
|
|
56
56
|
managed = bool(payload.get("nexo", {}).get("codex", {}).get("bootstrap_managed"))
|
|
57
|
+
mcp_managed = bool(payload.get("nexo", {}).get("codex", {}).get("mcp_managed"))
|
|
57
58
|
initial_messages = payload.get("initial_messages", [])
|
|
58
59
|
has_initial_messages = bool(initial_messages)
|
|
60
|
+
mcp_server = payload.get("mcp_servers", {}).get("nexo", {})
|
|
59
61
|
return {
|
|
60
62
|
"exists": True,
|
|
61
63
|
"path": str(path),
|
|
62
64
|
"bootstrap_managed": managed,
|
|
65
|
+
"mcp_managed": mcp_managed,
|
|
63
66
|
"has_initial_messages": has_initial_messages,
|
|
64
67
|
"model": str(payload.get("model", "") or ""),
|
|
65
68
|
"reasoning_effort": str(payload.get("model_reasoning_effort", "") or ""),
|
|
69
|
+
"has_mcp_server": isinstance(mcp_server, dict) and bool(mcp_server.get("command")) and bool(mcp_server.get("args")),
|
|
70
|
+
"mcp_runtime_home": str((mcp_server.get("env") or {}).get("NEXO_HOME", "") or ""),
|
|
71
|
+
"mcp_runtime_root": str((mcp_server.get("env") or {}).get("NEXO_CODE", "") or ""),
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
|
|
75
|
+
def _claude_desktop_shared_brain_status() -> dict:
|
|
76
|
+
path = Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json"
|
|
77
|
+
if not path.is_file():
|
|
78
|
+
return {"exists": False, "path": str(path), "shared_brain_managed": False}
|
|
79
|
+
try:
|
|
80
|
+
payload = json.loads(path.read_text())
|
|
81
|
+
except Exception as exc:
|
|
82
|
+
return {
|
|
83
|
+
"exists": True,
|
|
84
|
+
"path": str(path),
|
|
85
|
+
"shared_brain_managed": False,
|
|
86
|
+
"error": str(exc),
|
|
87
|
+
}
|
|
88
|
+
mcp_server = (payload.get("mcpServers") or {}).get("nexo", {})
|
|
89
|
+
metadata = ((payload.get("nexo") or {}).get("claude_desktop") or {})
|
|
90
|
+
return {
|
|
91
|
+
"exists": True,
|
|
92
|
+
"path": str(path),
|
|
93
|
+
"has_mcp_server": isinstance(mcp_server, dict) and bool(mcp_server.get("command")) and bool(mcp_server.get("args")),
|
|
94
|
+
"shared_brain_managed": bool(metadata.get("shared_brain_managed")),
|
|
95
|
+
"shared_brain_mode": str(metadata.get("shared_brain_mode", "") or ""),
|
|
96
|
+
"managed_runtime_home": str(metadata.get("managed_runtime_home", "") or ""),
|
|
97
|
+
"managed_runtime_root": str(metadata.get("managed_runtime_root", "") or ""),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _recent_codex_session_parity_status(*, days: int = 7, max_files: int = 24) -> dict:
|
|
102
|
+
roots = [
|
|
103
|
+
Path.home() / ".codex" / "sessions",
|
|
104
|
+
Path.home() / ".codex" / "archived_sessions",
|
|
105
|
+
]
|
|
106
|
+
cutoff = time.time() - (days * 86400)
|
|
107
|
+
candidates: list[tuple[float, Path]] = []
|
|
108
|
+
for root in roots:
|
|
109
|
+
if not root.exists():
|
|
110
|
+
continue
|
|
111
|
+
for path in root.rglob("*.jsonl"):
|
|
112
|
+
try:
|
|
113
|
+
mtime = path.stat().st_mtime
|
|
114
|
+
except OSError:
|
|
115
|
+
continue
|
|
116
|
+
if mtime >= cutoff:
|
|
117
|
+
candidates.append((mtime, path))
|
|
118
|
+
candidates.sort(key=lambda item: item[0], reverse=True)
|
|
119
|
+
files = [path for _, path in candidates[:max_files]]
|
|
120
|
+
|
|
121
|
+
status = {
|
|
122
|
+
"files": len(files),
|
|
123
|
+
"bootstrap_sessions": 0,
|
|
124
|
+
"startup_sessions": 0,
|
|
125
|
+
"heartbeat_sessions": 0,
|
|
126
|
+
"origins": set(),
|
|
127
|
+
"samples": [],
|
|
128
|
+
}
|
|
129
|
+
for path in files:
|
|
130
|
+
saw_bootstrap = False
|
|
131
|
+
saw_startup = False
|
|
132
|
+
saw_heartbeat = False
|
|
133
|
+
origin = ""
|
|
134
|
+
try:
|
|
135
|
+
with path.open() as fh:
|
|
136
|
+
for raw in fh:
|
|
137
|
+
if (
|
|
138
|
+
not saw_bootstrap
|
|
139
|
+
and (
|
|
140
|
+
"NEXO Shared Brain for Codex" in raw
|
|
141
|
+
or "<!-- nexo-codex-agents-version:" in raw
|
|
142
|
+
or "You are NEXO" in raw
|
|
143
|
+
)
|
|
144
|
+
):
|
|
145
|
+
saw_bootstrap = True
|
|
146
|
+
try:
|
|
147
|
+
event = json.loads(raw)
|
|
148
|
+
except Exception:
|
|
149
|
+
continue
|
|
150
|
+
payload = event.get("payload", {})
|
|
151
|
+
if event.get("type") == "session_meta" and isinstance(payload, dict):
|
|
152
|
+
origin = str(payload.get("originator", "") or payload.get("source", "") or "")
|
|
153
|
+
if event.get("type") != "response_item" or not isinstance(payload, dict):
|
|
154
|
+
continue
|
|
155
|
+
if payload.get("type") != "function_call":
|
|
156
|
+
continue
|
|
157
|
+
name = str(payload.get("name", "") or "")
|
|
158
|
+
if name in {"mcp__nexo__nexo_startup", "nexo_startup"}:
|
|
159
|
+
saw_startup = True
|
|
160
|
+
elif name in {"mcp__nexo__nexo_heartbeat", "nexo_heartbeat"}:
|
|
161
|
+
saw_heartbeat = True
|
|
162
|
+
if saw_bootstrap and saw_startup and saw_heartbeat and origin:
|
|
163
|
+
break
|
|
164
|
+
except Exception:
|
|
165
|
+
continue
|
|
166
|
+
if origin:
|
|
167
|
+
status["origins"].add(origin)
|
|
168
|
+
if saw_bootstrap:
|
|
169
|
+
status["bootstrap_sessions"] += 1
|
|
170
|
+
if saw_startup:
|
|
171
|
+
status["startup_sessions"] += 1
|
|
172
|
+
if saw_heartbeat:
|
|
173
|
+
status["heartbeat_sessions"] += 1
|
|
174
|
+
status["samples"].append(
|
|
175
|
+
{
|
|
176
|
+
"file": str(path),
|
|
177
|
+
"bootstrap": saw_bootstrap,
|
|
178
|
+
"startup": saw_startup,
|
|
179
|
+
"heartbeat": saw_heartbeat,
|
|
180
|
+
"origin": origin,
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
status["origins"] = sorted(status["origins"])
|
|
184
|
+
return status
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _client_assumption_regressions() -> list[str]:
|
|
188
|
+
src_root = NEXO_CODE / "src"
|
|
189
|
+
if not src_root.is_dir():
|
|
190
|
+
return []
|
|
191
|
+
allowed_claude_projects = {
|
|
192
|
+
(src_root / "scripts" / "deep-sleep" / "collect.py").resolve(),
|
|
193
|
+
}
|
|
194
|
+
offenders: list[str] = []
|
|
195
|
+
for path in src_root.rglob("*.py"):
|
|
196
|
+
try:
|
|
197
|
+
text = path.read_text()
|
|
198
|
+
except Exception:
|
|
199
|
+
continue
|
|
200
|
+
resolved = path.resolve()
|
|
201
|
+
if ".claude/projects" in text and resolved not in allowed_claude_projects:
|
|
202
|
+
offenders.append(f"{path.relative_to(NEXO_CODE)} hardcodes ~/.claude/projects")
|
|
203
|
+
collect_path = src_root / "scripts" / "deep-sleep" / "collect.py"
|
|
204
|
+
try:
|
|
205
|
+
collect_text = collect_path.read_text()
|
|
206
|
+
except Exception:
|
|
207
|
+
collect_text = ""
|
|
208
|
+
if collect_text and (".claude/projects" in collect_text) and (".codex" not in collect_text or "find_codex_session_files" not in collect_text):
|
|
209
|
+
offenders.append("deep-sleep/collect.py references Claude transcripts without Codex transcript parity")
|
|
210
|
+
return offenders
|
|
211
|
+
|
|
212
|
+
|
|
69
213
|
def _file_age_seconds(path: Path) -> float | None:
|
|
70
214
|
"""Return file age in seconds, or None if not found."""
|
|
71
215
|
try:
|
|
@@ -1093,6 +1237,11 @@ def check_client_bootstrap_parity(fix: bool = False) -> DoctorCheck:
|
|
|
1093
1237
|
severity = "warn"
|
|
1094
1238
|
evidence.append(f"codex config missing managed bootstrap injection at {codex_config.get('path')}")
|
|
1095
1239
|
repair_plan.append("Run `nexo clients sync` or `nexo update` so plain Codex sessions inherit the NEXO bootstrap")
|
|
1240
|
+
elif codex_config.get("exists") and not codex_config.get("has_mcp_server"):
|
|
1241
|
+
status = "degraded"
|
|
1242
|
+
severity = "warn"
|
|
1243
|
+
evidence.append(f"codex config missing managed `mcp_servers.nexo` at {codex_config.get('path')}")
|
|
1244
|
+
repair_plan.append("Re-sync Codex so manual sessions keep the shared brain even if `codex mcp add` state drifts")
|
|
1096
1245
|
elif codex_config.get("exists"):
|
|
1097
1246
|
evidence.append(
|
|
1098
1247
|
"codex config bootstrap managed"
|
|
@@ -1140,6 +1289,151 @@ def check_client_bootstrap_parity(fix: bool = False) -> DoctorCheck:
|
|
|
1140
1289
|
)
|
|
1141
1290
|
|
|
1142
1291
|
|
|
1292
|
+
def check_codex_session_parity() -> DoctorCheck:
|
|
1293
|
+
try:
|
|
1294
|
+
schedule = _load_json(SCHEDULE_FILE) if SCHEDULE_FILE.is_file() else {}
|
|
1295
|
+
except Exception:
|
|
1296
|
+
schedule = {}
|
|
1297
|
+
prefs = normalize_client_preferences(schedule)
|
|
1298
|
+
wants_codex = bool(
|
|
1299
|
+
prefs.get("interactive_clients", {}).get("codex")
|
|
1300
|
+
or prefs.get("default_terminal_client") == "codex"
|
|
1301
|
+
or (prefs.get("automation_enabled", True) and prefs.get("automation_backend") == "codex")
|
|
1302
|
+
)
|
|
1303
|
+
if not wants_codex:
|
|
1304
|
+
return DoctorCheck(
|
|
1305
|
+
id="runtime.codex_sessions",
|
|
1306
|
+
tier="runtime",
|
|
1307
|
+
status="healthy",
|
|
1308
|
+
severity="info",
|
|
1309
|
+
summary="Codex session parity check skipped (Codex not selected)",
|
|
1310
|
+
)
|
|
1311
|
+
|
|
1312
|
+
audit = _recent_codex_session_parity_status()
|
|
1313
|
+
if audit["files"] == 0:
|
|
1314
|
+
return DoctorCheck(
|
|
1315
|
+
id="runtime.codex_sessions",
|
|
1316
|
+
tier="runtime",
|
|
1317
|
+
status="degraded",
|
|
1318
|
+
severity="warn",
|
|
1319
|
+
summary="No recent Codex sessions found to verify startup discipline",
|
|
1320
|
+
repair_plan=[
|
|
1321
|
+
"Start Codex through `nexo chat` at least once so doctor can verify recent NEXO startup behavior",
|
|
1322
|
+
],
|
|
1323
|
+
escalation_prompt=(
|
|
1324
|
+
"Codex is selected, but there are no recent durable Codex sessions to inspect. "
|
|
1325
|
+
"NEXO cannot prove that manual Codex sessions are entering the shared-brain startup flow."
|
|
1326
|
+
),
|
|
1327
|
+
)
|
|
1328
|
+
|
|
1329
|
+
evidence = [
|
|
1330
|
+
f"recent codex sessions inspected: {audit['files']}",
|
|
1331
|
+
f"bootstrap markers seen in {audit['bootstrap_sessions']}/{audit['files']}",
|
|
1332
|
+
f"nexo_startup seen in {audit['startup_sessions']}/{audit['files']}",
|
|
1333
|
+
f"nexo_heartbeat seen in {audit['heartbeat_sessions']}/{audit['files']}",
|
|
1334
|
+
]
|
|
1335
|
+
if audit["origins"]:
|
|
1336
|
+
evidence.append(f"origins: {', '.join(audit['origins'])}")
|
|
1337
|
+
|
|
1338
|
+
status = "healthy"
|
|
1339
|
+
severity = "info"
|
|
1340
|
+
repair_plan: list[str] = []
|
|
1341
|
+
if audit["bootstrap_sessions"] == 0:
|
|
1342
|
+
status = "degraded"
|
|
1343
|
+
severity = "warn"
|
|
1344
|
+
repair_plan.append("Run `nexo update` or `nexo clients sync` so plain Codex sessions inherit the managed bootstrap")
|
|
1345
|
+
if audit["startup_sessions"] == 0:
|
|
1346
|
+
status = "degraded"
|
|
1347
|
+
severity = "warn"
|
|
1348
|
+
repair_plan.append("Use `nexo chat` or keep the global Codex bootstrap intact so sessions actually call `nexo_startup`")
|
|
1349
|
+
|
|
1350
|
+
return DoctorCheck(
|
|
1351
|
+
id="runtime.codex_sessions",
|
|
1352
|
+
tier="runtime",
|
|
1353
|
+
status=status,
|
|
1354
|
+
severity=severity,
|
|
1355
|
+
summary="Recent Codex sessions show NEXO startup discipline" if status == "healthy" else "Recent Codex sessions need stronger NEXO startup discipline",
|
|
1356
|
+
evidence=evidence,
|
|
1357
|
+
repair_plan=repair_plan,
|
|
1358
|
+
escalation_prompt=(
|
|
1359
|
+
"Codex is selected, but recent durable Codex sessions are not consistently showing NEXO bootstrap markers or `nexo_startup`. "
|
|
1360
|
+
"Manual Codex sessions may still be starting too plain."
|
|
1361
|
+
) if status != "healthy" else "",
|
|
1362
|
+
)
|
|
1363
|
+
|
|
1364
|
+
|
|
1365
|
+
def check_claude_desktop_shared_brain() -> DoctorCheck:
|
|
1366
|
+
try:
|
|
1367
|
+
schedule = _load_json(SCHEDULE_FILE) if SCHEDULE_FILE.is_file() else {}
|
|
1368
|
+
except Exception:
|
|
1369
|
+
schedule = {}
|
|
1370
|
+
prefs = normalize_client_preferences(schedule)
|
|
1371
|
+
wants_desktop = bool(prefs.get("interactive_clients", {}).get("claude_desktop"))
|
|
1372
|
+
installed = detect_installed_clients().get("claude_desktop", {}).get("installed", False)
|
|
1373
|
+
status_info = _claude_desktop_shared_brain_status()
|
|
1374
|
+
|
|
1375
|
+
if not wants_desktop and not installed:
|
|
1376
|
+
return DoctorCheck(
|
|
1377
|
+
id="runtime.claude_desktop",
|
|
1378
|
+
tier="runtime",
|
|
1379
|
+
status="healthy",
|
|
1380
|
+
severity="info",
|
|
1381
|
+
summary="Claude Desktop shared-brain check skipped (client not installed)",
|
|
1382
|
+
)
|
|
1383
|
+
|
|
1384
|
+
evidence = [
|
|
1385
|
+
f"config: {status_info.get('path')}",
|
|
1386
|
+
f"shared brain mode: {status_info.get('shared_brain_mode') or 'mcp_only'}",
|
|
1387
|
+
]
|
|
1388
|
+
if status_info.get("managed_runtime_home"):
|
|
1389
|
+
evidence.append(f"runtime home: {status_info.get('managed_runtime_home')}")
|
|
1390
|
+
|
|
1391
|
+
if status_info.get("error"):
|
|
1392
|
+
return DoctorCheck(
|
|
1393
|
+
id="runtime.claude_desktop",
|
|
1394
|
+
tier="runtime",
|
|
1395
|
+
status="degraded",
|
|
1396
|
+
severity="warn",
|
|
1397
|
+
summary="Claude Desktop config is unreadable",
|
|
1398
|
+
evidence=evidence + [status_info["error"]],
|
|
1399
|
+
repair_plan=["Repair Claude Desktop config JSON and re-run `nexo clients sync`"],
|
|
1400
|
+
)
|
|
1401
|
+
|
|
1402
|
+
if not status_info.get("exists") or not status_info.get("has_mcp_server"):
|
|
1403
|
+
return DoctorCheck(
|
|
1404
|
+
id="runtime.claude_desktop",
|
|
1405
|
+
tier="runtime",
|
|
1406
|
+
status="degraded",
|
|
1407
|
+
severity="warn",
|
|
1408
|
+
summary="Claude Desktop is not pointed at the shared NEXO brain",
|
|
1409
|
+
evidence=evidence,
|
|
1410
|
+
repair_plan=["Run `nexo clients sync` so Claude Desktop shares the same local brain"],
|
|
1411
|
+
escalation_prompt=(
|
|
1412
|
+
"Claude Desktop is installed or enabled, but its MCP config does not show the shared `nexo` runtime."
|
|
1413
|
+
),
|
|
1414
|
+
)
|
|
1415
|
+
|
|
1416
|
+
if not status_info.get("shared_brain_managed"):
|
|
1417
|
+
return DoctorCheck(
|
|
1418
|
+
id="runtime.claude_desktop",
|
|
1419
|
+
tier="runtime",
|
|
1420
|
+
status="degraded",
|
|
1421
|
+
severity="warn",
|
|
1422
|
+
summary="Claude Desktop shares NEXO, but managed metadata is missing",
|
|
1423
|
+
evidence=evidence,
|
|
1424
|
+
repair_plan=["Re-sync Claude Desktop so doctor can verify the managed shared-brain contract"],
|
|
1425
|
+
)
|
|
1426
|
+
|
|
1427
|
+
return DoctorCheck(
|
|
1428
|
+
id="runtime.claude_desktop",
|
|
1429
|
+
tier="runtime",
|
|
1430
|
+
status="healthy",
|
|
1431
|
+
severity="info",
|
|
1432
|
+
summary="Claude Desktop shared-brain parity OK (MCP-only mode)",
|
|
1433
|
+
evidence=evidence,
|
|
1434
|
+
)
|
|
1435
|
+
|
|
1436
|
+
|
|
1143
1437
|
def check_transcript_source_parity() -> DoctorCheck:
|
|
1144
1438
|
"""Check whether Deep Sleep can see transcript sources for the selected clients."""
|
|
1145
1439
|
try:
|
|
@@ -1207,6 +1501,34 @@ def check_transcript_source_parity() -> DoctorCheck:
|
|
|
1207
1501
|
)
|
|
1208
1502
|
|
|
1209
1503
|
|
|
1504
|
+
def check_client_assumption_regressions() -> DoctorCheck:
|
|
1505
|
+
offenders = _client_assumption_regressions()
|
|
1506
|
+
if not offenders:
|
|
1507
|
+
return DoctorCheck(
|
|
1508
|
+
id="runtime.client_assumptions",
|
|
1509
|
+
tier="runtime",
|
|
1510
|
+
status="healthy",
|
|
1511
|
+
severity="info",
|
|
1512
|
+
summary="No new Claude-only runtime path assumptions detected",
|
|
1513
|
+
)
|
|
1514
|
+
return DoctorCheck(
|
|
1515
|
+
id="runtime.client_assumptions",
|
|
1516
|
+
tier="runtime",
|
|
1517
|
+
status="critical",
|
|
1518
|
+
severity="error",
|
|
1519
|
+
summary=f"Detected {len(offenders)} client-parity regression(s) in runtime source",
|
|
1520
|
+
evidence=offenders[:10],
|
|
1521
|
+
repair_plan=[
|
|
1522
|
+
"Replace Claude-only transcript or hook assumptions with shared client abstractions",
|
|
1523
|
+
"Keep Deep Sleep and startup flows aware of both Claude Code and Codex surfaces",
|
|
1524
|
+
],
|
|
1525
|
+
escalation_prompt=(
|
|
1526
|
+
"A runtime source file drifted back to a Claude-only assumption. "
|
|
1527
|
+
"Audit the offending file and restore client-agnostic parity before shipping."
|
|
1528
|
+
),
|
|
1529
|
+
)
|
|
1530
|
+
|
|
1531
|
+
|
|
1210
1532
|
def run_runtime_checks(fix: bool = False) -> list[DoctorCheck]:
|
|
1211
1533
|
"""Run all runtime-tier checks. Read-only by default."""
|
|
1212
1534
|
return [
|
|
@@ -1216,7 +1538,10 @@ def run_runtime_checks(fix: bool = False) -> list[DoctorCheck]:
|
|
|
1216
1538
|
check_cron_freshness(),
|
|
1217
1539
|
check_client_backend_preferences(),
|
|
1218
1540
|
check_client_bootstrap_parity(fix=fix),
|
|
1541
|
+
check_codex_session_parity(),
|
|
1542
|
+
check_claude_desktop_shared_brain(),
|
|
1219
1543
|
check_transcript_source_parity(),
|
|
1544
|
+
check_client_assumption_regressions(),
|
|
1220
1545
|
check_launchagent_integrity(fix=fix),
|
|
1221
1546
|
check_personal_script_registry(fix=fix),
|
|
1222
1547
|
check_skill_health(fix=fix),
|
|
@@ -65,6 +65,10 @@ def handle_cognitive_retrieve(
|
|
|
65
65
|
mode_parts.append(f"spreading={spreading_depth}")
|
|
66
66
|
elif spreading_depth is None:
|
|
67
67
|
mode_parts.append("spreading=AUTO")
|
|
68
|
+
if results:
|
|
69
|
+
top_score = float(results[0].get("score", 0.0) or 0.0)
|
|
70
|
+
confidence = "high" if top_score >= 0.82 else "medium" if top_score >= 0.66 else "low"
|
|
71
|
+
mode_parts.append(f"top_confidence={confidence}")
|
|
68
72
|
header = f"COGNITIVE RETRIEVE — query: '{query}' | {len(results)} results ({', '.join(mode_parts)})\n\n"
|
|
69
73
|
return header + formatted
|
|
70
74
|
|
package/src/runtime_power.py
CHANGED
|
@@ -65,6 +65,10 @@ MACOS_FDA_PROBE_PATHS = (
|
|
|
65
65
|
Path.home() / "Library" / "Safari",
|
|
66
66
|
Path.home() / "Library" / "Application Support" / "AddressBook",
|
|
67
67
|
)
|
|
68
|
+
DEFAULT_CLAUDE_CODE_MODEL = "claude-opus-4-6[1m]"
|
|
69
|
+
DEFAULT_CLAUDE_CODE_REASONING_EFFORT = ""
|
|
70
|
+
DEFAULT_CODEX_MODEL = "gpt-5.4"
|
|
71
|
+
DEFAULT_CODEX_REASONING_EFFORT = "xhigh"
|
|
68
72
|
|
|
69
73
|
|
|
70
74
|
def _schedule_defaults() -> dict:
|
|
@@ -81,12 +85,12 @@ def _schedule_defaults() -> dict:
|
|
|
81
85
|
"automation_backend": "claude_code",
|
|
82
86
|
"client_runtime_profiles": {
|
|
83
87
|
"claude_code": {
|
|
84
|
-
"model":
|
|
85
|
-
"reasoning_effort":
|
|
88
|
+
"model": DEFAULT_CLAUDE_CODE_MODEL,
|
|
89
|
+
"reasoning_effort": DEFAULT_CLAUDE_CODE_REASONING_EFFORT,
|
|
86
90
|
},
|
|
87
91
|
"codex": {
|
|
88
|
-
"model":
|
|
89
|
-
"reasoning_effort":
|
|
92
|
+
"model": DEFAULT_CODEX_MODEL,
|
|
93
|
+
"reasoning_effort": DEFAULT_CODEX_REASONING_EFFORT,
|
|
90
94
|
},
|
|
91
95
|
},
|
|
92
96
|
"client_install_preferences": {
|