cluxion-agentplugin-preprocessing 0.3.7__tar.gz → 0.3.9__tar.gz

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 (90) hide show
  1. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/PKG-INFO +1 -1
  2. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/pyproject.toml +1 -1
  3. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/__init__.py +1 -1
  4. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/plugin.py +4 -0
  5. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/runner.py +20 -2
  6. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/clarification.py +17 -5
  7. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_clarification.py +25 -0
  8. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/test_runner.py +35 -0
  9. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/.github/profile/README.md +0 -0
  10. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/.gitignore +0 -0
  11. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/Docs/README.md +0 -0
  12. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/LICENSE +0 -0
  13. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/README.md +0 -0
  14. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/adapters/claude/.claude-plugin/plugin.json +0 -0
  15. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/adapters/claude/skills/preprocess/SKILL.md +0 -0
  16. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/adapters/codex/config-snippet.toml +0 -0
  17. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/cluxion-Docs/README.md +0 -0
  18. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/cluxion-Docs/architecture.md +0 -0
  19. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/cluxion-Docs/harness-logic.md +0 -0
  20. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/cluxion-Docs/honesty-preprocessing.md +0 -0
  21. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/cluxion-Docs/install-and-operations.md +0 -0
  22. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/cluxion-Docs/security.md +0 -0
  23. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/Cargo.lock +0 -0
  24. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/Cargo.toml +0 -0
  25. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/pyproject.toml +0 -0
  26. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/src/context.rs +0 -0
  27. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/src/dispatch.rs +0 -0
  28. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/src/guard.rs +0 -0
  29. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/src/lib.rs +0 -0
  30. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/src/main.rs +0 -0
  31. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/src/queue.rs +0 -0
  32. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/rust/cluxion_queue/src/types.rs +0 -0
  33. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/cli.py +0 -0
  34. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/doctor/__init__.py +0 -0
  35. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/doctor/catalog.json +0 -0
  36. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/doctor/framework.py +0 -0
  37. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/doctor/probes.py +0 -0
  38. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/guard_watch.py +0 -0
  39. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/hermes_config.py +0 -0
  40. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/plugin.yaml +0 -0
  41. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_agentplugin_preprocessing/schemas.py +0 -0
  42. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/__init__.py +0 -0
  43. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/__main__.py +0 -0
  44. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/adapters/__init__.py +0 -0
  45. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/adapters/contract.py +0 -0
  46. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/adapters/grok_build.py +0 -0
  47. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/adapters/hermes.py +0 -0
  48. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/adapters/spec.py +0 -0
  49. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/bootstrap.py +0 -0
  50. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/cli.py +0 -0
  51. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/__init__.py +0 -0
  52. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/context_compress.py +0 -0
  53. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/dispatch_store.py +0 -0
  54. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/harness.py +0 -0
  55. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/intent.py +0 -0
  56. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/ledger.py +0 -0
  57. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/ledger_codec.py +0 -0
  58. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/plan_codec.py +0 -0
  59. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/preprocess.py +0 -0
  60. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/types.py +0 -0
  61. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/core/work_queue.py +0 -0
  62. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/guard_daemon_host.py +0 -0
  63. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/models/__init__.py +0 -0
  64. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/models/supervisor.py +0 -0
  65. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/models/vllm_mlx.py +0 -0
  66. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/resources/__init__.py +0 -0
  67. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/resources/guard_bridge.py +0 -0
  68. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/resources/py_queue.py +0 -0
  69. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/resources/queue_bridge.py +0 -0
  70. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/resources/rust_bridge.py +0 -0
  71. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/web/__init__.py +0 -0
  72. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/src/cluxion_runtime/web/browser_bridge.py +0 -0
  73. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_browser_bridge.py +0 -0
  74. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_cluxion_runtime_spine.py +0 -0
  75. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_context_compress.py +0 -0
  76. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_contract.py +0 -0
  77. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_dispatch_store.py +0 -0
  78. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_guard.py +0 -0
  79. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_ledger.py +0 -0
  80. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_py_queue_concurrency.py +0 -0
  81. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_queue_backends.py +0 -0
  82. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_runtime_adapter_cli.py +0 -0
  83. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_rust_queue.py +0 -0
  84. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/runtime/test_supervisor.py +0 -0
  85. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/test_bootstrap.py +0 -0
  86. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/test_doctor.py +0 -0
  87. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/test_guard_watch.py +0 -0
  88. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/test_hermes_config.py +0 -0
  89. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/test_packaging_policy.py +0 -0
  90. {cluxion_agentplugin_preprocessing-0.3.7 → cluxion_agentplugin_preprocessing-0.3.9}/tests/test_plugin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cluxion-agentplugin-preprocessing
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: Universal agent plugin for Cluxion preprocessing, honesty contracts, clarification, Rust work queue, and resource-aware harness handoff.
5
5
  Project-URL: Homepage, https://github.com/cluxion/cluxion-Agentplugin-preprocessing
6
6
  Project-URL: Repository, https://github.com/cluxion/cluxion-Agentplugin-preprocessing
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "cluxion-agentplugin-preprocessing"
7
- version = "0.3.7"
7
+ version = "0.3.9"
8
8
  description = "Universal agent plugin for Cluxion preprocessing, honesty contracts, clarification, Rust work queue, and resource-aware harness handoff."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -2,6 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "0.3.7"
5
+ __version__ = "0.3.9"
6
6
 
7
7
  __all__ = ["__version__"]
@@ -247,6 +247,10 @@ def _handle_browser_type(args: dict[str, object], **_: object) -> str:
247
247
 
248
248
 
249
249
  def _handle_doctor(args: dict[str, object], **_: object) -> str:
250
+ return _json_result(lambda: _run_doctor(args))
251
+
252
+
253
+ def _run_doctor(args: dict[str, object]) -> str:
250
254
  pkg = "cluxion_agentplugin_preprocessing.doctor"
251
255
  catalog_path = Path(str(importlib.resources.files(pkg).joinpath("catalog.json")))
252
256
  result = run_doctor(
@@ -65,6 +65,7 @@ def bootstrap(payload: Mapping[str, object], *, command_runner: CommandRunner |
65
65
  def serve_local(payload: Mapping[str, object], *, command_runner: CommandRunner | None = None) -> RuntimeResult:
66
66
  """Run cluxion-runtime serve-local with dry-run by default."""
67
67
  model = _required_str(payload, "model")
68
+ port = _port(payload, "port", 23003)
68
69
  command = [
69
70
  _runtime_binary(None),
70
71
  "serve-local",
@@ -73,7 +74,7 @@ def serve_local(payload: Mapping[str, object], *, command_runner: CommandRunner
73
74
  "--host",
74
75
  _str(payload, "host", "127.0.0.1"),
75
76
  "--port",
76
- str(_int(payload, "port", 23003)),
77
+ str(port),
77
78
  "--max-tokens",
78
79
  str(_int(payload, "max_tokens", 128_000)),
79
80
  ]
@@ -89,6 +90,7 @@ def serve_local(payload: Mapping[str, object], *, command_runner: CommandRunner
89
90
  def hermes_config(payload: Mapping[str, object], *, command_runner: CommandRunner | None = None) -> RuntimeResult:
90
91
  """Render Hermes config patch for a local endpoint."""
91
92
  model = _required_str(payload, "model")
93
+ port = _port(payload, "port", 23003)
92
94
  command = (
93
95
  _runtime_binary(None),
94
96
  "hermes-local-config",
@@ -97,7 +99,7 @@ def hermes_config(payload: Mapping[str, object], *, command_runner: CommandRunne
97
99
  "--host",
98
100
  _str(payload, "host", "127.0.0.1"),
99
101
  "--port",
100
- str(_int(payload, "port", 23003)),
102
+ str(port),
101
103
  "--context-length",
102
104
  str(_int(payload, "context_length", 131_072)),
103
105
  "--provider-key",
@@ -144,6 +146,12 @@ def queue_brief(payload: Mapping[str, object], *, command_runner: CommandRunner
144
146
 
145
147
  def context_compress(payload: Mapping[str, object], *, command_runner: CommandRunner | None = None) -> RuntimeResult:
146
148
  """Compress conversation context deterministically once it exceeds the trigger ratio."""
149
+ messages = payload.get("messages")
150
+ if not isinstance(messages, list):
151
+ raise ValueError("messages must be a list of objects")
152
+ for i, m in enumerate(messages):
153
+ if not isinstance(m, dict) or not isinstance(m.get("content"), str):
154
+ raise ValueError(f"message[{i}] must be an object with string content")
147
155
  command = (_runtime_binary(None), "context-compress", "--json-stdin")
148
156
  stdin = json.dumps(dict(payload), ensure_ascii=False)
149
157
  return _execute_json(command, stdin, command_runner)
@@ -318,6 +326,16 @@ def _int(payload: Mapping[str, object], key: str, default: int) -> int:
318
326
  return value if value > 0 else default
319
327
 
320
328
 
329
+ def _port(payload: Mapping[str, object], key: str, default: int) -> int:
330
+ try:
331
+ value = int(payload.get(key, default))
332
+ except (TypeError, ValueError):
333
+ value = default
334
+ if not (1 <= value <= 65535):
335
+ raise ValueError(f"{key} must be between 1 and 65535")
336
+ return value
337
+
338
+
321
339
  def _non_negative_int(payload: Mapping[str, object], key: str, default: int) -> int:
322
340
  try:
323
341
  value = int(payload.get(key, default))
@@ -9,6 +9,7 @@ mixed-language on purpose — they classify user input, they are not output.
9
9
  from __future__ import annotations
10
10
 
11
11
  import os
12
+ import re
12
13
  from dataclasses import dataclass
13
14
 
14
15
  from cluxion_runtime.core.types import WorkIntent, WorkItem
@@ -125,11 +126,10 @@ def _question(question_id: str, locale: str) -> ClarificationQuestion:
125
126
  return ClarificationQuestion(question_id, prompt, why)
126
127
 
127
128
 
128
- _AMBIGUOUS_KEYWORDS = (
129
+ # PHRASES: multi-word or longer markers — use plain substring match
130
+ _AMBIGUOUS_PHRASES = (
129
131
  "maybe",
130
132
  "perhaps",
131
- "either",
132
- "or",
133
133
  "not sure",
134
134
  "unsure",
135
135
  "아마",
@@ -139,6 +139,18 @@ _AMBIGUOUS_KEYWORDS = (
139
139
  "헷갈",
140
140
  "애매",
141
141
  )
142
+ # WORDS: short standalone tokens — match on whole-word boundaries only
143
+ _AMBIGUOUS_WORDS = ("or", "either")
144
+
145
+
146
+ def _has_ambiguous(text: str) -> bool:
147
+ """Return True if text contains ambiguous phrasing or choice words."""
148
+ t = text.lower()
149
+ if any(p in t for p in _AMBIGUOUS_PHRASES):
150
+ return True
151
+ return any(re.search(r"\b" + re.escape(w) + r"\b", t) for w in _AMBIGUOUS_WORDS)
152
+
153
+
142
154
  _SCOPE_KEYWORDS = ("all", "everything", "전부", "전체", "모든")
143
155
  _TARGET_MISSING_KEYWORDS = ("fix", "implement", "refactor", "patch", "수정", "구현", "리팩터", "패치")
144
156
  _LOW_CONFIDENCE_THRESHOLD = 0.62
@@ -155,7 +167,7 @@ def assess_clarification(item: WorkItem, intent: WorkIntent) -> ClarificationRes
155
167
  reasons.append("low_intent_confidence")
156
168
  questions.append(_question("intent_direction", locale))
157
169
 
158
- if _has_any(text, _AMBIGUOUS_KEYWORDS):
170
+ if _has_ambiguous(text):
159
171
  reasons.append("ambiguous_language")
160
172
  questions.append(_question("disambiguate_choice", locale))
161
173
 
@@ -204,7 +216,7 @@ def assess_clarification(item: WorkItem, intent: WorkIntent) -> ClarificationRes
204
216
  def _needs_direction_confirmation(text: str, intent: WorkIntent) -> bool:
205
217
  if intent.category == "general" and len(text) < 240:
206
218
  return False
207
- if _has_any(text, _AMBIGUOUS_KEYWORDS):
219
+ if _has_ambiguous(text):
208
220
  return True
209
221
  if intent.category in {"engineering", "security", "documentation", "local_model"}:
210
222
  return True
@@ -68,3 +68,28 @@ def test_resolve_locale_fallbacks() -> None:
68
68
  assert clarification.resolve_locale("한국어 텍스트") == "ko"
69
69
  assert clarification.resolve_locale("plain", explicit="ko-KR") == "ko"
70
70
  assert clarification.resolve_locale("plain", explicit="fr") == "en"
71
+
72
+
73
+ # Regression tests for word-boundary fix on 'or'/'either'
74
+ def test_clear_prompts_no_false_positive_clarification() -> None:
75
+ clear_prompts = [
76
+ "Generate a sales report",
77
+ "Optimize the code base",
78
+ "sort a list of numbers",
79
+ "What is the hex code for the color blue?",
80
+ ]
81
+ for prompt in clear_prompts:
82
+ item = WorkItem(f"w-clear-{prompt[:10]}", prompt)
83
+ plan = build_harness_plan(item)
84
+ assert plan.clarification_required is False, f"False positive on: {prompt}"
85
+
86
+
87
+ def test_real_or_choice_still_requires_clarification() -> None:
88
+ or_prompts = [
89
+ "Should I use X or Y?",
90
+ "do A or B?",
91
+ ]
92
+ for prompt in or_prompts:
93
+ item = WorkItem(f"w-or-{prompt[:10]}", prompt)
94
+ plan = build_harness_plan(item)
95
+ assert plan.clarification_required is True, f"Missed ambiguity on: {prompt}"
@@ -176,3 +176,38 @@ def _isolated_env(key: str, value: str) -> Iterator[None]:
176
176
  os.environ.pop(key, None)
177
177
  else:
178
178
  os.environ[key] = old_value
179
+
180
+
181
+ def test_serve_local_rejects_out_of_range_port() -> None:
182
+ try:
183
+ runner.serve_local({"model": "test", "port": 999999999})
184
+ except ValueError as exc:
185
+ assert "port must be between 1 and 65535" in str(exc)
186
+ else:
187
+ raise AssertionError("expected ValueError")
188
+
189
+
190
+ def test_hermes_config_rejects_invalid_port() -> None:
191
+ try:
192
+ runner.hermes_config({"model": "test", "port": 0})
193
+ except ValueError as exc:
194
+ assert "port must be between 1 and 65535" in str(exc)
195
+ else:
196
+ raise AssertionError("expected ValueError")
197
+
198
+
199
+ def test_context_compress_rejects_malformed_messages() -> None:
200
+ try:
201
+ runner.context_compress({"messages": [42, "x", None, {"role": "user"}]})
202
+ except ValueError as exc:
203
+ assert "message[0] must be an object with string content" in str(exc)
204
+ else:
205
+ raise AssertionError("expected ValueError")
206
+
207
+
208
+ def test_context_compress_accepts_valid_messages_structure() -> None:
209
+ try:
210
+ runner.context_compress({"messages": [{"content": "hello world"}]})
211
+ except ValueError as exc:
212
+ assert "must be an object with string content" not in str(exc)
213
+ # ok if other error like runtime missing