claude-dev-env 2.17.0 → 2.19.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.
- package/.agents/skills/AGENTS.md +4 -4
- package/.agents/skills/_shared/AGENTS.md +1 -0
- package/.agents/skills/_shared/pr-loop/AGENTS.md +1 -0
- package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +89 -0
- package/.agents/skills/_shared/pr-loop/preflight-proposal.md +5 -0
- package/.agents/skills/orchestrator/SKILL.md +6 -4
- package/.agents/skills/orchestrator-refresh/SKILL.md +9 -5
- package/.agents/skills/pr-cleanup/SKILL.md +17 -85
- package/.agents/skills/pr-cleanup/reference/process-inventory.md +15 -0
- package/.agents/skills/pr-cleanup/reference/task-seeds.md +20 -0
- package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/SKILL.md +18 -12
- package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/offense-examples.md +1 -1
- package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/task-seeds.md +3 -2
- package/.agents/skills/pr-refinement/SKILL.md +31 -0
- package/.agents/skills/{shared-extraction-audit → pr-shared-extraction}/SKILL.md +24 -7
- package/.agents/skills/{small-cl → pr-small-cl}/SKILL.md +1 -1
- package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +33 -5
- package/.agents/skills/skill-builder/SKILL.md +1 -0
- package/.agents/skills/team-advisor/SKILL.md +5 -5
- package/.agents/skills-archived/bugteam/SKILL.md +1 -1
- package/.agents/skills-archived/bugteam/test_skill_additions.py +1 -1
- package/.agents/skills-archived/comments/SKILL.md +1 -1
- package/.agents/skills-archived/descriptions/SKILL.md +1 -1
- package/.agents/skills-archived/reviews/SKILL.md +1 -1
- package/AGENTS.md +1 -1
- package/_shared/advisor/AGENTS.md +5 -5
- package/_shared/advisor/advisor-protocol.md +54 -31
- package/_shared/advisor/reference/advisor-block.md +5 -1
- package/_shared/advisor/reference/consult-format.md +1 -1
- package/_shared/advisor/reference/identity.md +28 -0
- package/_shared/advisor/reference/lifecycle.md +8 -1
- package/_shared/advisor/reference/sol-rung.md +12 -7
- package/_shared/advisor/reference/spawn-walk-log.md +6 -5
- package/_shared/advisor/reference/third-party-bind.md +5 -6
- package/_shared/advisor/reference/warm-up.md +9 -2
- package/_shared/advisor/scripts/codex_sol_advisor.py +66 -11
- package/_shared/advisor/scripts/config/advisor_scripts_constants/advisor_route_constants.py +9 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +16 -4
- package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +11 -10
- package/_shared/advisor/scripts/model_tier_run_validator.py +50 -23
- package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +108 -3
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +171 -82
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +43 -0
- package/_shared/advisor/scripts/tier_model_ids.py +75 -7
- package/_shared/pr-loop/AGENTS.md +2 -1
- package/_shared/pr-loop/preflight-proposal.md +56 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +47 -4
- package/_shared/pr-loop/worker-spawn.md +4 -3
- package/bin/ever-shipped-skills.mjs +14 -0
- package/bin/install-constants.mjs +5 -0
- package/bin/install.agents-home.test.mjs +23 -0
- package/bin/install.codex-rules.test.mjs +61 -6
- package/bin/install.mjs +57 -9
- package/bin/install.prune.test.mjs +65 -3
- package/bin/install.test.mjs +202 -2
- package/docs/CODE_RULES.md +4 -0
- package/docs/references/team-advisor-skill.md +2 -2
- package/hooks/advisory/migration_safety_advisor.py +14 -14
- package/hooks/advisory/test_migration_safety_advisor.py +120 -0
- package/hooks/atomic_file_writer.py +81 -0
- package/hooks/blocking/AGENTS.md +2 -1
- package/hooks/blocking/code_rules_banned_identifiers.py +55 -4
- package/hooks/blocking/code_rules_enforcer_test_support.py +92 -0
- package/hooks/blocking/destructive_command_blocker.py +8 -7
- package/hooks/blocking/pre_tool_use_dispatcher.py +11 -7
- package/hooks/blocking/precommit_code_rules_gate.py +14 -139
- package/hooks/blocking/sensitive_file_protector.py +8 -6
- package/hooks/blocking/session_edit_stage_gate.py +31 -1
- package/hooks/blocking/test_claude_md_orphan_file_blocker.py +171 -1
- package/hooks/blocking/test_code_rules_enforcer_agent_home_tooling.py +37 -12
- package/hooks/blocking/test_code_rules_enforcer_banned_noun_word.py +54 -0
- package/hooks/blocking/test_code_rules_enforcer_duplicate_body_hook_routing.py +15 -53
- package/hooks/blocking/test_code_rules_enforcer_ephemeral.py +13 -62
- package/hooks/blocking/test_code_rules_enforcer_narrow_edit.py +308 -0
- package/hooks/blocking/test_code_rules_enforcer_precheck_forecast.py +14 -71
- package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +14 -16
- package/hooks/blocking/test_code_rules_enforcer_split_entry_2.py +31 -46
- package/hooks/blocking/test_code_rules_enforcer_stage.py +192 -0
- package/hooks/blocking/test_code_rules_enforcer_zero_payload_alias_hook_routing.py +13 -59
- package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +42 -25
- package/hooks/blocking/test_destructive_command_blocker_no_verify.py +58 -38
- package/hooks/blocking/test_docstring_rule_gate_count_blocker.py +113 -1
- package/hooks/blocking/test_env_var_table_code_drift_blocker.py +70 -3
- package/hooks/blocking/test_hook_subprocess_support.py +296 -0
- package/hooks/blocking/test_package_inventory_stale_blocker.py +1 -1
- package/hooks/blocking/test_pii_scanner.py +81 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +32 -3
- package/hooks/blocking/test_precommit_code_rules_gate.py +11 -16
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +168 -0
- package/hooks/blocking/test_pytest_testpaths_orphan_blocker.py +25 -18
- package/hooks/blocking/test_sensitive_file_protector.py +145 -5
- package/hooks/blocking/test_session_edit_stage_gate_staging.py +64 -0
- package/hooks/blocking/test_session_edit_stage_gate_support.py +204 -0
- package/hooks/blocking/test_session_edit_stage_gate_tracker.py +116 -0
- package/hooks/blocking/test_shared_stdin_adoption.py +31 -20
- package/hooks/blocking/test_test_preflight_check.py +80 -0
- package/hooks/git-hooks/pre_commit.py +6 -2
- package/hooks/git-hooks/test_pre_push.py +57 -0
- package/hooks/hooks_constants/AGENTS.md +3 -1
- package/hooks/hooks_constants/atomic_file_writer_constants.py +4 -0
- package/hooks/hooks_constants/banned_identifiers_constants.py +1 -0
- package/hooks/hooks_constants/destructive_command_environment_constants.py +18 -0
- package/hooks/hooks_constants/destructive_command_segment_constants.py +1 -11
- package/hooks/hooks_constants/hardcoded_user_path_constants.py +9 -3
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +1 -1
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +2 -4
- package/hooks/json_file_reader.py +22 -0
- package/hooks/pending_sidecars.py +33 -0
- package/hooks/test_atomic_file_writer.py +73 -0
- package/hooks/test_json_file_reader.py +24 -0
- package/hooks/test_pending_sidecars.py +25 -0
- package/package.json +1 -1
- /package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/fetch-commands.md +0 -0
- /package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/report-template.md +0 -0
- /package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/rule-checklist.md +0 -0
- /package/.agents/skills/{shared-extraction-audit → pr-shared-extraction}/reference/examples.md +0 -0
- /package/.agents/skills/{shared-extraction-audit → pr-shared-extraction}/reference/offense-taxonomy.md +0 -0
|
@@ -40,46 +40,61 @@ CODEX_BIND_SUCCESS_TOKEN = model_tier_run_validator.CODEX_BIND_SUCCESS_TOKEN
|
|
|
40
40
|
def test_clean_single_spawn_at_top_of_slice_passes() -> None:
|
|
41
41
|
run = ModelTierRun(
|
|
42
42
|
own_tier="Opus",
|
|
43
|
-
candidate_tiers=["Fable"
|
|
43
|
+
candidate_tiers=["Fable"],
|
|
44
44
|
attempts=[{"tier": "Fable", "result": "spawned"}],
|
|
45
45
|
selected_tier="Fable",
|
|
46
46
|
)
|
|
47
47
|
assert validate_model_tier_run(run) is None
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def
|
|
50
|
+
def test_sol_codex_bind_succeeds_after_fable_when_enabled() -> None:
|
|
51
51
|
run = ModelTierRun(
|
|
52
52
|
own_tier="Opus",
|
|
53
|
-
candidate_tiers=[
|
|
54
|
-
attempts=[
|
|
53
|
+
candidate_tiers=["Fable", ADVISOR_MODEL_TIER],
|
|
54
|
+
attempts=[
|
|
55
|
+
{"tier": "Fable", "result": "unavailable"},
|
|
56
|
+
{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN},
|
|
57
|
+
],
|
|
55
58
|
selected_tier=ADVISOR_MODEL_TIER,
|
|
56
59
|
is_sol_enabled=True,
|
|
57
60
|
)
|
|
58
61
|
assert validate_model_tier_run(run) is None
|
|
59
62
|
|
|
60
63
|
|
|
61
|
-
def
|
|
64
|
+
def test_fable_success_with_sol_enabled_stops_before_sol() -> None:
|
|
62
65
|
run = ModelTierRun(
|
|
63
66
|
own_tier="Opus",
|
|
64
|
-
candidate_tiers=[
|
|
65
|
-
attempts=[
|
|
66
|
-
{"tier": ADVISOR_MODEL_TIER, "result": "unavailable"},
|
|
67
|
-
{"tier": "Fable", "result": "spawned"},
|
|
68
|
-
],
|
|
67
|
+
candidate_tiers=["Fable", ADVISOR_MODEL_TIER],
|
|
68
|
+
attempts=[{"tier": "Fable", "result": "spawned"}],
|
|
69
69
|
selected_tier="Fable",
|
|
70
70
|
is_sol_enabled=True,
|
|
71
71
|
)
|
|
72
72
|
assert validate_model_tier_run(run) is None
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def
|
|
75
|
+
def test_sol_first_walk_raises_when_sol_is_enabled() -> None:
|
|
76
76
|
run = ModelTierRun(
|
|
77
|
-
own_tier="
|
|
78
|
-
candidate_tiers=[ADVISOR_MODEL_TIER, "Fable"
|
|
77
|
+
own_tier="Opus",
|
|
78
|
+
candidate_tiers=[ADVISOR_MODEL_TIER, "Fable"],
|
|
79
79
|
attempts=[{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN}],
|
|
80
80
|
selected_tier=ADVISOR_MODEL_TIER,
|
|
81
81
|
is_sol_enabled=True,
|
|
82
82
|
)
|
|
83
|
+
with pytest.raises(ModelTierRunError):
|
|
84
|
+
validate_model_tier_run(run)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_sol_rung_follows_fable_on_third_party_cli_floor_when_enabled() -> None:
|
|
88
|
+
run = ModelTierRun(
|
|
89
|
+
own_tier="ThirdParty",
|
|
90
|
+
candidate_tiers=["Fable", ADVISOR_MODEL_TIER],
|
|
91
|
+
attempts=[
|
|
92
|
+
{"tier": "Fable", "result": "unavailable"},
|
|
93
|
+
{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN},
|
|
94
|
+
],
|
|
95
|
+
selected_tier=ADVISOR_MODEL_TIER,
|
|
96
|
+
is_sol_enabled=True,
|
|
97
|
+
)
|
|
83
98
|
|
|
84
99
|
assert validate_model_tier_run(run) is None
|
|
85
100
|
|
|
@@ -87,7 +102,7 @@ def test_sol_rung_precedes_third_party_cli_floor_when_enabled() -> None:
|
|
|
87
102
|
def test_sol_codex_result_requires_sol_candidate() -> None:
|
|
88
103
|
run = ModelTierRun(
|
|
89
104
|
own_tier="Opus",
|
|
90
|
-
candidate_tiers=["Fable"
|
|
105
|
+
candidate_tiers=["Fable"],
|
|
91
106
|
attempts=[{"tier": "Fable", "result": CODEX_BIND_SUCCESS_TOKEN}],
|
|
92
107
|
selected_tier="Fable",
|
|
93
108
|
)
|
|
@@ -98,8 +113,11 @@ def test_sol_codex_result_requires_sol_candidate() -> None:
|
|
|
98
113
|
def test_sol_spawned_result_does_not_count_as_codex_success() -> None:
|
|
99
114
|
run = ModelTierRun(
|
|
100
115
|
own_tier="Opus",
|
|
101
|
-
candidate_tiers=[
|
|
102
|
-
attempts=[
|
|
116
|
+
candidate_tiers=["Fable", ADVISOR_MODEL_TIER],
|
|
117
|
+
attempts=[
|
|
118
|
+
{"tier": "Fable", "result": "unavailable"},
|
|
119
|
+
{"tier": ADVISOR_MODEL_TIER, "result": "spawned"},
|
|
120
|
+
],
|
|
103
121
|
selected_tier=ADVISOR_MODEL_TIER,
|
|
104
122
|
is_sol_enabled=True,
|
|
105
123
|
)
|
|
@@ -107,15 +125,13 @@ def test_sol_spawned_result_does_not_count_as_codex_success() -> None:
|
|
|
107
125
|
validate_model_tier_run(run)
|
|
108
126
|
|
|
109
127
|
|
|
110
|
-
def
|
|
128
|
+
def test_exhausted_fable_walk_fails_closed() -> None:
|
|
111
129
|
run = ModelTierRun(
|
|
112
130
|
own_tier="Opus",
|
|
113
|
-
candidate_tiers=["Fable"
|
|
114
|
-
attempts=[
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
],
|
|
118
|
-
selected_tier="Opus",
|
|
131
|
+
candidate_tiers=["Fable"],
|
|
132
|
+
attempts=[{"tier": "Fable", "result": "unavailable"}],
|
|
133
|
+
selected_tier=None,
|
|
134
|
+
fallback_reason="Fable did not bind; no advisor",
|
|
119
135
|
)
|
|
120
136
|
assert validate_model_tier_run(run) is None
|
|
121
137
|
|
|
@@ -123,21 +139,18 @@ def test_fallthrough_to_floor_tier_passes() -> None:
|
|
|
123
139
|
def test_fully_exhausted_walk_with_fallback_reason_passes() -> None:
|
|
124
140
|
run = ModelTierRun(
|
|
125
141
|
own_tier="Opus",
|
|
126
|
-
candidate_tiers=["Fable"
|
|
127
|
-
attempts=[
|
|
128
|
-
{"tier": "Fable", "result": "unavailable"},
|
|
129
|
-
{"tier": "Opus", "result": "unavailable"},
|
|
130
|
-
],
|
|
142
|
+
candidate_tiers=["Fable"],
|
|
143
|
+
attempts=[{"tier": "Fable", "result": "unavailable"}],
|
|
131
144
|
selected_tier=None,
|
|
132
|
-
fallback_reason="every candidate tier failed
|
|
145
|
+
fallback_reason="every candidate tier failed",
|
|
133
146
|
)
|
|
134
147
|
assert validate_model_tier_run(run) is None
|
|
135
148
|
|
|
136
149
|
|
|
137
|
-
def
|
|
150
|
+
def test_opus_candidate_on_advisor_walk_raises() -> None:
|
|
138
151
|
run = ModelTierRun(
|
|
139
152
|
own_tier="Opus",
|
|
140
|
-
candidate_tiers=["Fable"],
|
|
153
|
+
candidate_tiers=["Fable", "Opus"],
|
|
141
154
|
attempts=[{"tier": "Fable", "result": "spawned"}],
|
|
142
155
|
selected_tier="Fable",
|
|
143
156
|
)
|
|
@@ -148,7 +161,7 @@ def test_candidate_tiers_shorter_than_ladder_slice_raises() -> None:
|
|
|
148
161
|
def test_attempt_tier_outside_candidate_slice_raises() -> None:
|
|
149
162
|
run = ModelTierRun(
|
|
150
163
|
own_tier="Opus",
|
|
151
|
-
candidate_tiers=["Fable"
|
|
164
|
+
candidate_tiers=["Fable"],
|
|
152
165
|
attempts=[{"tier": "Haiku", "result": "spawned"}],
|
|
153
166
|
selected_tier="Haiku",
|
|
154
167
|
)
|
|
@@ -159,12 +172,13 @@ def test_attempt_tier_outside_candidate_slice_raises() -> None:
|
|
|
159
172
|
def test_attempts_out_of_ladder_order_raises() -> None:
|
|
160
173
|
run = ModelTierRun(
|
|
161
174
|
own_tier="Opus",
|
|
162
|
-
candidate_tiers=["Fable",
|
|
175
|
+
candidate_tiers=["Fable", ADVISOR_MODEL_TIER],
|
|
163
176
|
attempts=[
|
|
164
|
-
{"tier":
|
|
165
|
-
{"tier": "Fable", "result": "
|
|
177
|
+
{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN},
|
|
178
|
+
{"tier": "Fable", "result": "unavailable"},
|
|
166
179
|
],
|
|
167
|
-
selected_tier=
|
|
180
|
+
selected_tier=ADVISOR_MODEL_TIER,
|
|
181
|
+
is_sol_enabled=True,
|
|
168
182
|
)
|
|
169
183
|
with pytest.raises(ModelTierRunError):
|
|
170
184
|
validate_model_tier_run(run)
|
|
@@ -173,12 +187,13 @@ def test_attempts_out_of_ladder_order_raises() -> None:
|
|
|
173
187
|
def test_selected_tier_not_first_spawned_attempt_raises() -> None:
|
|
174
188
|
run = ModelTierRun(
|
|
175
189
|
own_tier="Opus",
|
|
176
|
-
candidate_tiers=["Fable",
|
|
190
|
+
candidate_tiers=["Fable", ADVISOR_MODEL_TIER],
|
|
177
191
|
attempts=[
|
|
178
192
|
{"tier": "Fable", "result": "unavailable"},
|
|
179
|
-
{"tier":
|
|
193
|
+
{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN},
|
|
180
194
|
],
|
|
181
195
|
selected_tier="Fable",
|
|
196
|
+
is_sol_enabled=True,
|
|
182
197
|
)
|
|
183
198
|
with pytest.raises(
|
|
184
199
|
ModelTierRunError,
|
|
@@ -193,12 +208,9 @@ def test_selected_tier_not_first_spawned_attempt_raises() -> None:
|
|
|
193
208
|
def test_exhausted_walk_with_non_null_selected_tier_raises() -> None:
|
|
194
209
|
run = ModelTierRun(
|
|
195
210
|
own_tier="Opus",
|
|
196
|
-
candidate_tiers=["Fable"
|
|
197
|
-
attempts=[
|
|
198
|
-
|
|
199
|
-
{"tier": "Opus", "result": "unavailable"},
|
|
200
|
-
],
|
|
201
|
-
selected_tier="Opus",
|
|
211
|
+
candidate_tiers=["Fable"],
|
|
212
|
+
attempts=[{"tier": "Fable", "result": "unavailable"}],
|
|
213
|
+
selected_tier="Fable",
|
|
202
214
|
fallback_reason="every candidate tier failed",
|
|
203
215
|
)
|
|
204
216
|
with pytest.raises(ModelTierRunError):
|
|
@@ -208,11 +220,8 @@ def test_exhausted_walk_with_non_null_selected_tier_raises() -> None:
|
|
|
208
220
|
def test_exhausted_walk_missing_fallback_reason_raises() -> None:
|
|
209
221
|
run = ModelTierRun(
|
|
210
222
|
own_tier="Opus",
|
|
211
|
-
candidate_tiers=["Fable"
|
|
212
|
-
attempts=[
|
|
213
|
-
{"tier": "Fable", "result": "unavailable"},
|
|
214
|
-
{"tier": "Opus", "result": "unavailable"},
|
|
215
|
-
],
|
|
223
|
+
candidate_tiers=["Fable"],
|
|
224
|
+
attempts=[{"tier": "Fable", "result": "unavailable"}],
|
|
216
225
|
selected_tier=None,
|
|
217
226
|
)
|
|
218
227
|
with pytest.raises(ModelTierRunError):
|
|
@@ -233,7 +242,7 @@ def test_unknown_own_tier_raises() -> None:
|
|
|
233
242
|
def test_empty_attempts_with_null_selected_tier_raises() -> None:
|
|
234
243
|
run = ModelTierRun(
|
|
235
244
|
own_tier="Opus",
|
|
236
|
-
candidate_tiers=["Fable"
|
|
245
|
+
candidate_tiers=["Fable"],
|
|
237
246
|
attempts=[],
|
|
238
247
|
selected_tier=None,
|
|
239
248
|
fallback_reason="skipped straight to CLI fallback",
|
|
@@ -242,13 +251,14 @@ def test_empty_attempts_with_null_selected_tier_raises() -> None:
|
|
|
242
251
|
validate_model_tier_run(run)
|
|
243
252
|
|
|
244
253
|
|
|
245
|
-
def
|
|
254
|
+
def test_incomplete_fallback_walk_before_sol_raises() -> None:
|
|
246
255
|
run = ModelTierRun(
|
|
247
256
|
own_tier="Opus",
|
|
248
|
-
candidate_tiers=["Fable",
|
|
257
|
+
candidate_tiers=["Fable", ADVISOR_MODEL_TIER],
|
|
249
258
|
attempts=[{"tier": "Fable", "result": "unavailable"}],
|
|
250
259
|
selected_tier=None,
|
|
251
|
-
fallback_reason="stopped after Fable without trying
|
|
260
|
+
fallback_reason="stopped after Fable without trying Sol",
|
|
261
|
+
is_sol_enabled=True,
|
|
252
262
|
)
|
|
253
263
|
with pytest.raises(ModelTierRunError):
|
|
254
264
|
validate_model_tier_run(run)
|
|
@@ -257,12 +267,9 @@ def test_incomplete_fallback_walk_before_floor_raises() -> None:
|
|
|
257
267
|
def test_lowercase_own_tier_and_candidates_pass() -> None:
|
|
258
268
|
run = ModelTierRun(
|
|
259
269
|
own_tier="opus",
|
|
260
|
-
candidate_tiers=["fable"
|
|
261
|
-
attempts=[
|
|
262
|
-
|
|
263
|
-
{"tier": "opus", "result": "spawned"},
|
|
264
|
-
],
|
|
265
|
-
selected_tier="opus",
|
|
270
|
+
candidate_tiers=["fable"],
|
|
271
|
+
attempts=[{"tier": "fable", "result": "spawned"}],
|
|
272
|
+
selected_tier="fable",
|
|
266
273
|
)
|
|
267
274
|
assert validate_model_tier_run(run) is None
|
|
268
275
|
|
|
@@ -273,7 +280,7 @@ def test_cli_validates_json_log_file(tmp_path: Path) -> None:
|
|
|
273
280
|
json.dumps(
|
|
274
281
|
{
|
|
275
282
|
"own_tier": "Opus",
|
|
276
|
-
"candidate_tiers": ["Fable"
|
|
283
|
+
"candidate_tiers": ["Fable"],
|
|
277
284
|
"attempts": [{"tier": "Fable", "result": "spawned"}],
|
|
278
285
|
"selected_tier": "Fable",
|
|
279
286
|
}
|
|
@@ -283,6 +290,24 @@ def test_cli_validates_json_log_file(tmp_path: Path) -> None:
|
|
|
283
290
|
assert main([str(log_path)]) == 0
|
|
284
291
|
loaded_run = load_model_tier_run_from_json_path(from_path=log_path)
|
|
285
292
|
assert loaded_run.selected_tier == "Fable"
|
|
293
|
+
assert loaded_run.host_profile == "Claude"
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_cli_rejects_non_string_host_profile(tmp_path: Path) -> None:
|
|
297
|
+
log_path = tmp_path / "invalid-host-profile.json"
|
|
298
|
+
log_path.write_text(
|
|
299
|
+
json.dumps(
|
|
300
|
+
{
|
|
301
|
+
"own_tier": "Opus",
|
|
302
|
+
"candidate_tiers": ["Fable"],
|
|
303
|
+
"attempts": [{"tier": "Fable", "result": "spawned"}],
|
|
304
|
+
"selected_tier": "Fable",
|
|
305
|
+
"host_profile": 1,
|
|
306
|
+
}
|
|
307
|
+
),
|
|
308
|
+
encoding="utf-8",
|
|
309
|
+
)
|
|
310
|
+
assert main([str(log_path)]) == 2
|
|
286
311
|
|
|
287
312
|
|
|
288
313
|
def test_cli_rejects_non_boolean_sol_enabled(tmp_path: Path) -> None:
|
|
@@ -291,7 +316,7 @@ def test_cli_rejects_non_boolean_sol_enabled(tmp_path: Path) -> None:
|
|
|
291
316
|
json.dumps(
|
|
292
317
|
{
|
|
293
318
|
"own_tier": "Opus",
|
|
294
|
-
"candidate_tiers": ["Fable"
|
|
319
|
+
"candidate_tiers": ["Fable"],
|
|
295
320
|
"attempts": [{"tier": "Fable", "result": "spawned"}],
|
|
296
321
|
"selected_tier": "Fable",
|
|
297
322
|
"sol_enabled": "false",
|
|
@@ -308,10 +333,11 @@ def test_cli_rejects_incomplete_fallback_log(tmp_path: Path) -> None:
|
|
|
308
333
|
json.dumps(
|
|
309
334
|
{
|
|
310
335
|
"own_tier": "Opus",
|
|
311
|
-
"candidate_tiers": ["Fable", "
|
|
336
|
+
"candidate_tiers": ["Fable", "Sol"],
|
|
312
337
|
"attempts": [{"tier": "Fable", "result": "unavailable"}],
|
|
313
338
|
"selected_tier": None,
|
|
314
339
|
"fallback_reason": "incomplete",
|
|
340
|
+
"sol_enabled": True,
|
|
315
341
|
}
|
|
316
342
|
),
|
|
317
343
|
encoding="utf-8",
|
|
@@ -326,14 +352,14 @@ def test_cli_missing_path_returns_usage_exit_code() -> None:
|
|
|
326
352
|
def test_cli_bind_at_fable_passes() -> None:
|
|
327
353
|
run = ModelTierRun(
|
|
328
354
|
own_tier="Opus",
|
|
329
|
-
candidate_tiers=["Fable"
|
|
355
|
+
candidate_tiers=["Fable"],
|
|
330
356
|
attempts=[{"tier": "Fable", "result": "cli"}],
|
|
331
357
|
selected_tier="Fable",
|
|
332
358
|
)
|
|
333
359
|
assert validate_model_tier_run(run) is None
|
|
334
360
|
|
|
335
361
|
|
|
336
|
-
def
|
|
362
|
+
def test_cli_bind_fallthrough_to_opus_raises() -> None:
|
|
337
363
|
run = ModelTierRun(
|
|
338
364
|
own_tier="Opus",
|
|
339
365
|
candidate_tiers=["Fable", "Opus"],
|
|
@@ -343,13 +369,14 @@ def test_cli_bind_fallthrough_to_opus_passes() -> None:
|
|
|
343
369
|
],
|
|
344
370
|
selected_tier="Opus",
|
|
345
371
|
)
|
|
346
|
-
|
|
372
|
+
with pytest.raises(ModelTierRunError):
|
|
373
|
+
validate_model_tier_run(run)
|
|
347
374
|
|
|
348
375
|
|
|
349
|
-
def
|
|
376
|
+
def test_third_party_own_tier_maps_to_fable_cli_bind_passes() -> None:
|
|
350
377
|
run = ModelTierRun(
|
|
351
378
|
own_tier="ThirdParty",
|
|
352
|
-
candidate_tiers=["Fable"
|
|
379
|
+
candidate_tiers=["Fable"],
|
|
353
380
|
attempts=[{"tier": "Fable", "result": "cli"}],
|
|
354
381
|
selected_tier="Fable",
|
|
355
382
|
)
|
|
@@ -359,7 +386,7 @@ def test_third_party_own_tier_maps_to_fable_opus_cli_bind_passes() -> None:
|
|
|
359
386
|
def test_third_party_own_tier_lowercase_cli_bind_passes() -> None:
|
|
360
387
|
run = ModelTierRun(
|
|
361
388
|
own_tier="thirdparty",
|
|
362
|
-
candidate_tiers=["fable"
|
|
389
|
+
candidate_tiers=["fable"],
|
|
363
390
|
attempts=[{"tier": "fable", "result": "cli"}],
|
|
364
391
|
selected_tier="fable",
|
|
365
392
|
)
|
|
@@ -369,7 +396,7 @@ def test_third_party_own_tier_lowercase_cli_bind_passes() -> None:
|
|
|
369
396
|
def test_self_token_is_not_bind_success_raises() -> None:
|
|
370
397
|
run = ModelTierRun(
|
|
371
398
|
own_tier="Opus",
|
|
372
|
-
candidate_tiers=["Fable"
|
|
399
|
+
candidate_tiers=["Fable"],
|
|
373
400
|
attempts=[{"tier": "Fable", "result": "self"}],
|
|
374
401
|
selected_tier="Fable",
|
|
375
402
|
)
|
|
@@ -380,7 +407,7 @@ def test_self_token_is_not_bind_success_raises() -> None:
|
|
|
380
407
|
def test_third_party_self_token_is_not_bind_success_raises() -> None:
|
|
381
408
|
run = ModelTierRun(
|
|
382
409
|
own_tier="ThirdParty",
|
|
383
|
-
candidate_tiers=["Fable"
|
|
410
|
+
candidate_tiers=["Fable"],
|
|
384
411
|
attempts=[{"tier": "Fable", "result": "self"}],
|
|
385
412
|
selected_tier="Fable",
|
|
386
413
|
)
|
|
@@ -402,11 +429,8 @@ def test_third_party_host_legacy_single_tier_self_bind_raises() -> None:
|
|
|
402
429
|
def test_third_party_cli_exhausted_fail_closed_passes() -> None:
|
|
403
430
|
run = ModelTierRun(
|
|
404
431
|
own_tier="ThirdParty",
|
|
405
|
-
candidate_tiers=["Fable"
|
|
406
|
-
attempts=[
|
|
407
|
-
{"tier": "Fable", "result": "unavailable"},
|
|
408
|
-
{"tier": "Opus", "result": "unavailable"},
|
|
409
|
-
],
|
|
432
|
+
candidate_tiers=["Fable"],
|
|
433
|
+
attempts=[{"tier": "Fable", "result": "unavailable"}],
|
|
410
434
|
selected_tier=None,
|
|
411
435
|
fallback_reason=(
|
|
412
436
|
"third-party host CLI Claude-chain exhausted; fail closed"
|
|
@@ -418,11 +442,8 @@ def test_third_party_cli_exhausted_fail_closed_passes() -> None:
|
|
|
418
442
|
def test_third_party_cli_exhausted_without_fallback_reason_raises() -> None:
|
|
419
443
|
run = ModelTierRun(
|
|
420
444
|
own_tier="ThirdParty",
|
|
421
|
-
candidate_tiers=["Fable"
|
|
422
|
-
attempts=[
|
|
423
|
-
{"tier": "Fable", "result": "unavailable"},
|
|
424
|
-
{"tier": "Opus", "result": "unavailable"},
|
|
425
|
-
],
|
|
445
|
+
candidate_tiers=["Fable"],
|
|
446
|
+
attempts=[{"tier": "Fable", "result": "unavailable"}],
|
|
426
447
|
selected_tier=None,
|
|
427
448
|
)
|
|
428
449
|
with pytest.raises(ModelTierRunError):
|
|
@@ -432,7 +453,7 @@ def test_third_party_cli_exhausted_without_fallback_reason_raises() -> None:
|
|
|
432
453
|
def test_third_party_cli_selected_tier_mismatch_raises() -> None:
|
|
433
454
|
run = ModelTierRun(
|
|
434
455
|
own_tier="ThirdParty",
|
|
435
|
-
candidate_tiers=["Fable"
|
|
456
|
+
candidate_tiers=["Fable"],
|
|
436
457
|
attempts=[{"tier": "Fable", "result": "cli"}],
|
|
437
458
|
selected_tier="Opus",
|
|
438
459
|
)
|
|
@@ -443,9 +464,77 @@ def test_third_party_cli_selected_tier_mismatch_raises() -> None:
|
|
|
443
464
|
def test_claude_host_self_token_is_not_spawn_success_raises() -> None:
|
|
444
465
|
run = ModelTierRun(
|
|
445
466
|
own_tier="Opus",
|
|
446
|
-
candidate_tiers=["Fable"
|
|
467
|
+
candidate_tiers=["Fable"],
|
|
447
468
|
attempts=[{"tier": "Fable", "result": "self"}],
|
|
448
469
|
selected_tier="Fable",
|
|
449
470
|
)
|
|
450
471
|
with pytest.raises(ModelTierRunError):
|
|
451
472
|
validate_model_tier_run(run)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def test_codex_host_sol_in_session_spawn_passes() -> None:
|
|
476
|
+
run = ModelTierRun(
|
|
477
|
+
own_tier="Opus",
|
|
478
|
+
candidate_tiers=[ADVISOR_MODEL_TIER],
|
|
479
|
+
attempts=[{"tier": ADVISOR_MODEL_TIER, "result": "spawned"}],
|
|
480
|
+
selected_tier=ADVISOR_MODEL_TIER,
|
|
481
|
+
host_profile="Codex",
|
|
482
|
+
)
|
|
483
|
+
assert validate_model_tier_run(run) is None
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def test_codex_host_sol_codex_token_counts_as_success() -> None:
|
|
487
|
+
run = ModelTierRun(
|
|
488
|
+
own_tier="Opus",
|
|
489
|
+
candidate_tiers=[ADVISOR_MODEL_TIER],
|
|
490
|
+
attempts=[
|
|
491
|
+
{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN}
|
|
492
|
+
],
|
|
493
|
+
selected_tier=ADVISOR_MODEL_TIER,
|
|
494
|
+
host_profile="Codex",
|
|
495
|
+
)
|
|
496
|
+
assert validate_model_tier_run(run) is None
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def test_codex_host_fable_then_sol_walk_raises() -> None:
|
|
500
|
+
run = ModelTierRun(
|
|
501
|
+
own_tier="Opus",
|
|
502
|
+
candidate_tiers=["Fable", ADVISOR_MODEL_TIER],
|
|
503
|
+
attempts=[{"tier": ADVISOR_MODEL_TIER, "result": "spawned"}],
|
|
504
|
+
selected_tier=ADVISOR_MODEL_TIER,
|
|
505
|
+
is_sol_enabled=True,
|
|
506
|
+
host_profile="Codex",
|
|
507
|
+
)
|
|
508
|
+
with pytest.raises(ModelTierRunError):
|
|
509
|
+
validate_model_tier_run(run)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def test_codex_host_exhausted_sol_fails_closed() -> None:
|
|
513
|
+
run = ModelTierRun(
|
|
514
|
+
own_tier="Opus",
|
|
515
|
+
candidate_tiers=[ADVISOR_MODEL_TIER],
|
|
516
|
+
attempts=[{"tier": ADVISOR_MODEL_TIER, "result": "unavailable"}],
|
|
517
|
+
selected_tier=None,
|
|
518
|
+
fallback_reason="Codex in-session Sol spawn did not bind",
|
|
519
|
+
host_profile="Codex",
|
|
520
|
+
)
|
|
521
|
+
assert validate_model_tier_run(run) is None
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def test_cli_loads_codex_host_profile(tmp_path: Path) -> None:
|
|
525
|
+
log_path = tmp_path / "codex-host-walk.json"
|
|
526
|
+
log_path.write_text(
|
|
527
|
+
json.dumps(
|
|
528
|
+
{
|
|
529
|
+
"own_tier": "Opus",
|
|
530
|
+
"candidate_tiers": ["Sol"],
|
|
531
|
+
"attempts": [{"tier": "Sol", "result": "spawned"}],
|
|
532
|
+
"selected_tier": "Sol",
|
|
533
|
+
"host_profile": "Codex",
|
|
534
|
+
}
|
|
535
|
+
),
|
|
536
|
+
encoding="utf-8",
|
|
537
|
+
)
|
|
538
|
+
assert main([str(log_path)]) == 0
|
|
539
|
+
loaded_run = load_model_tier_run_from_json_path(from_path=log_path)
|
|
540
|
+
assert loaded_run.host_profile == "Codex"
|
|
@@ -20,6 +20,7 @@ from advisor_scripts_constants.model_tier_run_validator_constants import ( # no
|
|
|
20
20
|
ALL_KNOWN_TIER_NAMES,
|
|
21
21
|
ALL_MODEL_TIERS,
|
|
22
22
|
HOST_PROFILE_CLAUDE,
|
|
23
|
+
HOST_PROFILE_CODEX,
|
|
23
24
|
HOST_PROFILE_THIRD_PARTY,
|
|
24
25
|
THIRD_PARTY_MODEL_TIER,
|
|
25
26
|
)
|
|
@@ -49,6 +50,8 @@ resolve_cli_model_id = tier_model_ids.resolve_cli_model_id
|
|
|
49
50
|
resolve_codex_model_id = tier_model_ids.resolve_codex_model_id
|
|
50
51
|
canonical_tier_name = tier_model_ids.canonical_tier_name
|
|
51
52
|
detect_host_profile = tier_model_ids.detect_host_profile
|
|
53
|
+
resolve_session_identity = tier_model_ids.resolve_session_identity
|
|
54
|
+
canonical_host_profile = tier_model_ids.canonical_host_profile
|
|
52
55
|
|
|
53
56
|
SCRIPTS_ROOT = Path(__file__).parent.parent
|
|
54
57
|
DOCUMENTED_RESOLVE_ONE_LINER = (
|
|
@@ -183,6 +186,46 @@ def test_detect_host_profile_rejects_unknown_explicit_value() -> None:
|
|
|
183
186
|
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "Titan"})
|
|
184
187
|
|
|
185
188
|
|
|
189
|
+
def test_canonical_host_profile_normalizes_known_names() -> None:
|
|
190
|
+
assert canonical_host_profile("codex") == HOST_PROFILE_CODEX
|
|
191
|
+
assert canonical_host_profile(" Claude ") == HOST_PROFILE_CLAUDE
|
|
192
|
+
assert canonical_host_profile("Titan") is None
|
|
193
|
+
assert canonical_host_profile("") is None
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def test_detect_host_profile_reads_codex_override() -> None:
|
|
197
|
+
assert (
|
|
198
|
+
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "Codex"})
|
|
199
|
+
== HOST_PROFILE_CODEX
|
|
200
|
+
)
|
|
201
|
+
assert (
|
|
202
|
+
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "codex"})
|
|
203
|
+
== HOST_PROFILE_CODEX
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def test_resolve_session_identity_maps_codex_text_to_codex() -> None:
|
|
208
|
+
assert resolve_session_identity("codex") == HOST_PROFILE_CODEX
|
|
209
|
+
assert resolve_session_identity("I am Codex") == HOST_PROFILE_CODEX
|
|
210
|
+
assert resolve_session_identity("Codex CLI") == HOST_PROFILE_CODEX
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def test_resolve_session_identity_maps_claude_text_to_claude() -> None:
|
|
214
|
+
assert resolve_session_identity("claude") == HOST_PROFILE_CLAUDE
|
|
215
|
+
assert resolve_session_identity("Claude Code") == HOST_PROFILE_CLAUDE
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_resolve_session_identity_maps_neither_to_third_party() -> None:
|
|
219
|
+
assert resolve_session_identity("grok") == HOST_PROFILE_THIRD_PARTY
|
|
220
|
+
assert resolve_session_identity("cursor") == HOST_PROFILE_THIRD_PARTY
|
|
221
|
+
assert resolve_session_identity("") == HOST_PROFILE_THIRD_PARTY
|
|
222
|
+
assert resolve_session_identity(" ") == HOST_PROFILE_THIRD_PARTY
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_resolve_session_identity_prefers_codex_when_both_tokens_appear() -> None:
|
|
226
|
+
assert resolve_session_identity("claude using Codex") == HOST_PROFILE_CODEX
|
|
227
|
+
|
|
228
|
+
|
|
186
229
|
def test_documented_resolve_one_liner_runs_without_prior_path_pollution() -> None:
|
|
187
230
|
clean_environment = {
|
|
188
231
|
each_key: each_value
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
detect_host_profile(
|
|
13
13
|
setting_by_name={"ADVISOR_HOST_PROFILE": "Claude"}
|
|
14
14
|
) # ok: "Claude"
|
|
15
|
+
detect_host_profile(
|
|
16
|
+
setting_by_name={"ADVISOR_HOST_PROFILE": "Codex"}
|
|
17
|
+
) # ok: "Codex"
|
|
18
|
+
resolve_session_identity("I am Codex") # ok: "Codex"
|
|
19
|
+
resolve_session_identity("Claude Code") # ok: "Claude"
|
|
20
|
+
resolve_session_identity("grok") # ok: "ThirdParty"
|
|
15
21
|
|
|
16
22
|
Values are stable short aliases (``opus``, ``sonnet``, ``third-party``), not dated
|
|
17
23
|
full model IDs such as ``claude-opus-4-…``. The map lives in
|
|
@@ -25,6 +31,7 @@ Args and returns for the public helpers are documented on each function.
|
|
|
25
31
|
from __future__ import annotations
|
|
26
32
|
|
|
27
33
|
import os
|
|
34
|
+
import re
|
|
28
35
|
import sys
|
|
29
36
|
from collections.abc import Mapping
|
|
30
37
|
from pathlib import Path
|
|
@@ -43,8 +50,12 @@ from advisor_scripts_constants.model_tier_run_validator_constants import ( # no
|
|
|
43
50
|
ALL_KNOWN_TIER_NAMES,
|
|
44
51
|
ALL_THIRD_PARTY_TRUTHY_VALUES,
|
|
45
52
|
HOST_PROFILE_CLAUDE,
|
|
53
|
+
HOST_PROFILE_CODEX,
|
|
46
54
|
HOST_PROFILE_ENV_VAR,
|
|
47
55
|
HOST_PROFILE_THIRD_PARTY,
|
|
56
|
+
SESSION_IDENTITY_CLAUDE_TOKEN,
|
|
57
|
+
SESSION_IDENTITY_CODEX_TOKEN,
|
|
58
|
+
SESSION_IDENTITY_WORD_PATTERN,
|
|
48
59
|
THIRD_PARTY_ENV_VAR,
|
|
49
60
|
UNKNOWN_HOST_PROFILE_ERROR,
|
|
50
61
|
UNKNOWN_LADDER_NAME_ERROR,
|
|
@@ -139,6 +150,65 @@ def resolve_codex_model_id(tier: str) -> str:
|
|
|
139
150
|
return maybe_model_id
|
|
140
151
|
|
|
141
152
|
|
|
153
|
+
def canonical_host_profile(host_profile_name: str) -> str | None:
|
|
154
|
+
"""Return the Title Case host profile for ``host_profile_name``, or ``None``.
|
|
155
|
+
|
|
156
|
+
::
|
|
157
|
+
|
|
158
|
+
canonical_host_profile("codex") # ok: "Codex"
|
|
159
|
+
canonical_host_profile(" Claude ") # ok: "Claude"
|
|
160
|
+
canonical_host_profile("Titan") # ok: None
|
|
161
|
+
canonical_host_profile("") # ok: None
|
|
162
|
+
|
|
163
|
+
Strips leading and trailing whitespace, then matches any letter case
|
|
164
|
+
against ``ALL_HOST_PROFILES``.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
host_profile_name: Raw host profile text from env, a spawn log, or a
|
|
168
|
+
caller.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
The canonical Title Case host profile, or ``None`` when unknown.
|
|
172
|
+
"""
|
|
173
|
+
stripped_host_profile_name = host_profile_name.strip()
|
|
174
|
+
if not stripped_host_profile_name:
|
|
175
|
+
return None
|
|
176
|
+
host_profile_by_lower_name = {
|
|
177
|
+
each_profile.lower(): each_profile for each_profile in ALL_HOST_PROFILES
|
|
178
|
+
}
|
|
179
|
+
return host_profile_by_lower_name.get(stripped_host_profile_name.lower())
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def resolve_session_identity(identity_text: str) -> str:
|
|
183
|
+
"""Map a session's self-identified name to an advisor host profile.
|
|
184
|
+
|
|
185
|
+
::
|
|
186
|
+
|
|
187
|
+
resolve_session_identity("I am Codex") # ok: "Codex"
|
|
188
|
+
resolve_session_identity("Claude Code") # ok: "Claude"
|
|
189
|
+
resolve_session_identity("grok") # ok: "ThirdParty"
|
|
190
|
+
resolve_session_identity("") # ok: "ThirdParty"
|
|
191
|
+
|
|
192
|
+
Split the text into letter-or-digit tokens. A ``codex`` token selects
|
|
193
|
+
Codex. A ``claude`` token selects Claude. Any other identity, including
|
|
194
|
+
empty text, selects ThirdParty. When both tokens appear, Codex wins.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
identity_text: The session's named identity.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
``HOST_PROFILE_CODEX``, ``HOST_PROFILE_CLAUDE``, or
|
|
201
|
+
``HOST_PROFILE_THIRD_PARTY``.
|
|
202
|
+
"""
|
|
203
|
+
identity_word_pattern = re.compile(SESSION_IDENTITY_WORD_PATTERN)
|
|
204
|
+
all_identity_tokens = identity_word_pattern.findall(identity_text.lower())
|
|
205
|
+
if SESSION_IDENTITY_CODEX_TOKEN in all_identity_tokens:
|
|
206
|
+
return HOST_PROFILE_CODEX
|
|
207
|
+
if SESSION_IDENTITY_CLAUDE_TOKEN in all_identity_tokens:
|
|
208
|
+
return HOST_PROFILE_CLAUDE
|
|
209
|
+
return HOST_PROFILE_THIRD_PARTY
|
|
210
|
+
|
|
211
|
+
|
|
142
212
|
def detect_host_profile(
|
|
143
213
|
setting_by_name: Mapping[str, str] | None = None,
|
|
144
214
|
) -> str:
|
|
@@ -148,6 +218,8 @@ def detect_host_profile(
|
|
|
148
218
|
|
|
149
219
|
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "ThirdParty"})
|
|
150
220
|
# ok: "ThirdParty"
|
|
221
|
+
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "Codex"})
|
|
222
|
+
# ok: "Codex"
|
|
151
223
|
detect_host_profile(setting_by_name={"THIRD_PARTY": "1"}) # ok: "ThirdParty"
|
|
152
224
|
detect_host_profile(setting_by_name={}) # ok: "Claude"
|
|
153
225
|
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "X"})
|
|
@@ -163,7 +235,8 @@ def detect_host_profile(
|
|
|
163
235
|
setting_by_name: Env name → setting text (defaults to ``os.environ``).
|
|
164
236
|
|
|
165
237
|
Returns:
|
|
166
|
-
``HOST_PROFILE_THIRD_PARTY
|
|
238
|
+
``HOST_PROFILE_CODEX``, ``HOST_PROFILE_THIRD_PARTY``, or
|
|
239
|
+
``HOST_PROFILE_CLAUDE``.
|
|
167
240
|
|
|
168
241
|
Raises:
|
|
169
242
|
ValueError: When ``ADVISOR_HOST_PROFILE`` is set to an unknown name.
|
|
@@ -175,12 +248,7 @@ def detect_host_profile(
|
|
|
175
248
|
HOST_PROFILE_ENV_VAR, ""
|
|
176
249
|
).strip()
|
|
177
250
|
if explicit_host_profile:
|
|
178
|
-
|
|
179
|
-
each_profile.lower(): each_profile for each_profile in ALL_HOST_PROFILES
|
|
180
|
-
}
|
|
181
|
-
maybe_canonical_host = host_profile_by_lower_name.get(
|
|
182
|
-
explicit_host_profile.lower()
|
|
183
|
-
)
|
|
251
|
+
maybe_canonical_host = canonical_host_profile(explicit_host_profile)
|
|
184
252
|
if maybe_canonical_host is None:
|
|
185
253
|
raise ValueError(UNKNOWN_HOST_PROFILE_ERROR.format(explicit_host_profile))
|
|
186
254
|
return maybe_canonical_host
|