prizmkit 1.1.150 → 1.1.151

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.1.150",
3
- "bundledAt": "2026-07-25T15:41:05.690Z",
4
- "bundledFrom": "a759b38"
2
+ "frameworkVersion": "1.1.151",
3
+ "bundledAt": "2026-07-26T02:05:50.056Z",
4
+ "bundledFrom": "738be35"
5
5
  }
@@ -168,6 +168,7 @@ def _is_terminal_success(result: AISessionResult) -> bool:
168
168
  and result.termination_reason not in {"timeout", "stale_session"}
169
169
  and not result.fatal_error_code
170
170
  and not result.progress_summary.fatal_error_code
171
+ and result.progress_summary.stop_reason.strip().lower() != "length"
171
172
  and result.progress_summary.result_subtype == "success"
172
173
  )
173
174
 
@@ -228,6 +229,8 @@ def _next_no_progress_count(previous: object | None, current: object, count: int
228
229
 
229
230
 
230
231
  def _is_context_overflow(result: AISessionResult) -> bool:
232
+ if result.progress_summary.stop_reason.strip().lower() == "length":
233
+ return True
231
234
  if (result.fatal_error_code or "").lower() == CONTEXT_OVERFLOW:
232
235
  return True
233
236
  text = "\n".join(
@@ -277,6 +277,7 @@ class SemanticLogNormalizer:
277
277
 
278
278
  _SUCCESS_STOPS = {"stop", "end_turn", "completed", "success"}
279
279
  _ERROR_STOPS = {"error", "aborted", "failed", "cancelled", "canceled"}
280
+ _CONTEXT_STOPS = {"length"}
280
281
 
281
282
  def __init__(self):
282
283
  self.renderer = SemanticLogRenderer()
@@ -617,7 +618,10 @@ class SemanticLogNormalizer:
617
618
  will_retry = bool(event.get("willRetry"))
618
619
  if will_retry:
619
620
  return ""
620
- status = "error" if stop_reason in self._ERROR_STOPS else "success"
621
+ if stop_reason in self._CONTEXT_STOPS:
622
+ status = CONTEXT_OVERFLOW_CODE
623
+ else:
624
+ status = "error" if stop_reason in self._ERROR_STOPS else "success"
621
625
  details = f"status={status}"
622
626
  if stop_reason:
623
627
  details += f" stop_reason={stop_reason}"
@@ -768,6 +772,16 @@ class ProgressTracker:
768
772
  self._current_tool_id = ""
769
773
  self._pi_retrying_terminal_error = False
770
774
 
775
+ def _record_pi_length_stop(self):
776
+ """Normalize Pi's token-limit stop into the existing context continuation path."""
777
+ self.result_subtype = "error"
778
+ self.stop_reason = "length"
779
+ self.last_result_is_error = True
780
+ self.api_error_code = CONTEXT_OVERFLOW_CODE
781
+ self.fatal_error_code = CONTEXT_OVERFLOW_CODE
782
+ self.terminal_result_text = "stop_reason=length"
783
+ self.terminal_success_at = ""
784
+
771
785
  def process_event(self, event):
772
786
  """Process a single stream-json event and update state.
773
787
 
@@ -846,7 +860,9 @@ class ProgressTracker:
846
860
  stop_reason = str(message.get("stopReason") or "")
847
861
  if stop_reason:
848
862
  self.stop_reason = stop_reason
849
- if stop_reason in {"error", "aborted"}:
863
+ if stop_reason == "length":
864
+ self._record_pi_length_stop()
865
+ elif stop_reason in {"error", "aborted"}:
850
866
  error_text = str(message.get("errorMessage") or stop_reason)
851
867
  self.result_subtype = "error"
852
868
  self.last_result_is_error = True
@@ -880,6 +896,8 @@ class ProgressTracker:
880
896
  messages = event.get("messages") if isinstance(event.get("messages"), list) else []
881
897
  final_message = messages[-1] if messages and isinstance(messages[-1], dict) else {}
882
898
  final_stop_reason = str(final_message.get("stopReason") or self.stop_reason or "")
899
+ if final_stop_reason == "length":
900
+ self._record_pi_length_stop()
883
901
  will_retry = bool(event.get("willRetry"))
884
902
  if will_retry:
885
903
  self._pi_retrying_terminal_error = bool(
@@ -3795,6 +3795,25 @@ def test_terminal_success_without_semantic_completion_is_skipped(tmp_path):
3795
3795
  assert classification.fatal_error_code == ""
3796
3796
 
3797
3797
 
3798
+ def test_pi_length_stop_uses_existing_context_overflow_continuation(tmp_path):
3799
+ from dataclasses import replace
3800
+
3801
+ from prizmkit_runtime.runner_classification import classify_session
3802
+ from prizmkit_runtime.status import ProgressSummary
3803
+
3804
+ base_head = _init_git_repo_for_invalid_checkpoint_test(tmp_path)
3805
+ raw = replace(
3806
+ _terminal_success_result(tmp_path),
3807
+ progress_summary=ProgressSummary(result_subtype="success", stop_reason="length"),
3808
+ )
3809
+
3810
+ classification = classify_session(raw, project_root=tmp_path, base_head=base_head)
3811
+
3812
+ assert classification.session_status == "context_overflow"
3813
+ assert classification.reason == "context_overflow"
3814
+ assert classification.fatal_error_code == "context_overflow"
3815
+
3816
+
3798
3817
  def test_terminal_success_text_does_not_hide_provider_failure(tmp_path):
3799
3818
  from prizmkit_runtime.runner_classification import classify_session
3800
3819
  from prizmkit_runtime.runner_models import PromptGenerationResult
@@ -994,6 +994,29 @@ def test_progress_parser_does_not_mark_failed_pi_agent_end_as_success():
994
994
  assert "provider request failed" in data["errors"]
995
995
 
996
996
 
997
+ def test_progress_parser_maps_pi_length_stop_to_context_overflow():
998
+ module = _load_progress_parser_module()
999
+ tracker = module.ProgressTracker()
1000
+ tracker.process_event({"type": "session", "version": 3, "id": "pi-session", "cwd": "/tmp/project"})
1001
+ tracker.process_event({"type": "agent_start"})
1002
+ message = {
1003
+ "role": "assistant",
1004
+ "content": [{"type": "thinking", "thinking": "Planning the next test"}],
1005
+ "stopReason": "length",
1006
+ }
1007
+ tracker.process_event({"type": "message_end", "message": message})
1008
+ tracker.process_event({"type": "agent_end", "willRetry": False, "messages": [message]})
1009
+
1010
+ data = tracker.to_dict()
1011
+
1012
+ assert data["result_subtype"] == "error"
1013
+ assert data["stop_reason"] == "length"
1014
+ assert data["last_result_is_error"] is True
1015
+ assert data["api_error_code"] == "context_overflow"
1016
+ assert data["fatal_error_code"] == "context_overflow"
1017
+ assert data["terminal_success_at"] == ""
1018
+
1019
+
997
1020
  def test_progress_parser_recovers_from_retrying_pi_attempt_before_final_success():
998
1021
  module = _load_progress_parser_module()
999
1022
  tracker = module.ProgressTracker()
@@ -1190,6 +1213,20 @@ def test_semantic_log_normalizer_emits_terminal_metadata_once():
1190
1213
  assert "[FINAL] Done" in rendered
1191
1214
 
1192
1215
 
1216
+ def test_semantic_log_normalizer_labels_pi_length_as_context_overflow():
1217
+ module = _load_progress_parser_module()
1218
+ normalizer = module.SemanticLogNormalizer()
1219
+ rendered = normalizer.process_event({
1220
+ "type": "agent_end",
1221
+ "willRetry": False,
1222
+ "messages": [{"role": "assistant", "content": [], "stopReason": "length"}],
1223
+ })
1224
+ rendered += normalizer.finish()
1225
+
1226
+ assert rendered.count("[SESSION_END]") == 1
1227
+ assert "status=context_overflow stop_reason=length" in rendered
1228
+
1229
+
1193
1230
  def test_semantic_log_normalizer_preserves_identical_text_from_distinct_turns():
1194
1231
  module = _load_progress_parser_module()
1195
1232
  normalizer = module.SemanticLogNormalizer()
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.150",
2
+ "version": "1.1.151",
3
3
  "skills": {
4
4
  "prizmkit": {
5
5
  "description": "Framework introduction and navigation for the formal single-requirement lifecycle, project initialization, Prizm docs, and independent deployment.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.1.150",
3
+ "version": "1.1.151",
4
4
  "description": "Create a new PrizmKit-powered project with clean initialization — no framework dev files, just what you need.",
5
5
  "type": "module",
6
6
  "bin": {