clouds-coder 2026.3.16__tar.gz → 2026.3.17__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.
@@ -75,6 +75,8 @@ AGENT_MAX_OUTPUT_TOKENS = 16384
75
75
  OLLAMA_THINKING_TOOL_BUFFER = 4096
76
76
  WATCHDOG_INTENT_NO_TOOL_THRESHOLD = 2
77
77
  WATCHDOG_REPEAT_NO_TOOL_THRESHOLD = 2
78
+ WATCHDOG_INTENT_NO_TOOL_THRESHOLD_SINGLE = 4
79
+ WATCHDOG_REPEAT_NO_TOOL_THRESHOLD_SINGLE = 4
78
80
  WATCHDOG_STATE_STALL_THRESHOLD = 6
79
81
  WATCHDOG_CONTEXT_STALL_THRESHOLD = 2
80
82
  WATCHDOG_REPEAT_SIMILARITY_THRESHOLD = 0.85
@@ -2354,7 +2356,7 @@ def try_read_text(path: Path, max_bytes: int = 400_000) -> str | None:
2354
2356
  except Exception:
2355
2357
  return None
2356
2358
 
2357
- def make_unified_diff(path: str, old_text: str, new_text: str, max_lines: int = 400) -> str:
2359
+ def make_unified_diff(path: str, old_text: str, new_text: str, max_lines: int = 400) -> tuple[str, int, int]:
2358
2360
  old_lines = old_text.splitlines()
2359
2361
  new_lines = new_text.splitlines()
2360
2362
  diff = list(
@@ -2366,9 +2368,12 @@ def make_unified_diff(path: str, old_text: str, new_text: str, max_lines: int =
2366
2368
  lineterm="",
2367
2369
  )
2368
2370
  )
2369
- if len(diff) > max_lines:
2371
+ added = sum(1 for ln in diff if ln.startswith("+") and not ln.startswith("+++"))
2372
+ deleted = sum(1 for ln in diff if ln.startswith("-") and not ln.startswith("---"))
2373
+ if max_lines and len(diff) > max_lines:
2370
2374
  diff = diff[:max_lines] + [f"... diff truncated, total lines={len(diff)}"]
2371
- return "\n".join(diff) if diff else f"@@ no textual diff for {path}"
2375
+ text = "\n".join(diff) if diff else f"@@ no textual diff for {path}"
2376
+ return text, added, deleted
2372
2377
 
2373
2378
  def _skip_row(text: str) -> dict:
2374
2379
  msg = str(text or "").strip() or "⋮"
@@ -7226,6 +7231,7 @@ class SessionState:
7226
7231
  self.multimodal_capability_cache: dict[str, dict] = {}
7227
7232
  self.failed_selections: list[str] = []
7228
7233
  self.todo = TodoManager()
7234
+ self.single_advance_prompt_enhance = False
7229
7235
  self.skills = SkillStore(skills_root)
7230
7236
  self.skill_load_cache: dict[str, dict] = {}
7231
7237
  self.skills_last_refresh_ts = 0.0
@@ -8900,6 +8906,19 @@ class SessionState:
8900
8906
  budget = int(self.runtime_round_budget or 0)
8901
8907
  html_block = f"{html_hint}\n\n" if html_hint else ""
8902
8908
  research_block = f"{research_hint}\n\n" if research_hint else ""
8909
+ _is_single_no_enhance = (
8910
+ runtime_mode == EXECUTION_MODE_SINGLE
8911
+ and not self.single_advance_prompt_enhance
8912
+ )
8913
+ skill_hint = (
8914
+ "Use load_skill for workspace-paths and tool-best-practices if needed. "
8915
+ if _is_single_no_enhance
8916
+ else (
8917
+ "Use load_skill for guidance on specific topics "
8918
+ "(workspace-paths, task-management, tool-best-practices, context-management). "
8919
+ "If execution stalls, load_skill('execution-degradation-recovery'). "
8920
+ )
8921
+ )
8903
8922
  return (
8904
8923
  f"You are a coding agent. Workspace: {self.files_root}. "
8905
8924
  f"Task level={runtime_level}, mode={runtime_mode}, "
@@ -8908,9 +8927,7 @@ class SessionState:
8908
8927
  f"{_detect_os_shell_instruction()} "
8909
8928
  "Use tools to inspect, edit, and execute. "
8910
8929
  "Call finish_current_task when done. "
8911
- "Use load_skill for guidance on specific topics "
8912
- "(workspace-paths, task-management, tool-best-practices, context-management). "
8913
- "If execution stalls, load_skill('execution-degradation-recovery'). "
8930
+ f"{skill_hint}"
8914
8931
  f"{html_block}"
8915
8932
  f"{research_block}"
8916
8933
  f"{model_language_instruction(self.ui_language)}\n\n"
@@ -14005,10 +14022,13 @@ class SessionState:
14005
14022
  bb = self._ensure_blackboard()
14006
14023
  dq = self._normalize_decomposition_queue_state(bb.get("decomposition_queue", {}))
14007
14024
  trigger_reason = ""
14025
+ _is_single = self._effective_execution_mode() == EXECUTION_MODE_SINGLE
14026
+ _intent_th = WATCHDOG_INTENT_NO_TOOL_THRESHOLD_SINGLE if _is_single else WATCHDOG_INTENT_NO_TOOL_THRESHOLD
14027
+ _repeat_th = WATCHDOG_REPEAT_NO_TOOL_THRESHOLD_SINGLE if _is_single else WATCHDOG_REPEAT_NO_TOOL_THRESHOLD
14008
14028
  if not bool(dq.get("active", False)):
14009
- if int(wd.get("intent_no_tool_streak", 0) or 0) >= int(WATCHDOG_INTENT_NO_TOOL_THRESHOLD):
14029
+ if int(wd.get("intent_no_tool_streak", 0) or 0) >= int(_intent_th):
14010
14030
  trigger_reason = "intent-without-tool-call"
14011
- elif int(wd.get("repeat_no_tool_streak", 0) or 0) >= int(WATCHDOG_REPEAT_NO_TOOL_THRESHOLD):
14031
+ elif int(wd.get("repeat_no_tool_streak", 0) or 0) >= int(_repeat_th):
14012
14032
  trigger_reason = "repeated-no-tool-reply"
14013
14033
  elif (
14014
14034
  self._watchdog_context_near_limit()
@@ -18582,11 +18602,9 @@ class SessionState:
18582
18602
  self._emit("status", {"summary": summary})
18583
18603
  out = f"{out}\n{summary}"
18584
18604
  after_text = try_read_text(fp, max_bytes=CODE_PREVIEW_STAGE_MAX_BYTES) or ""
18585
- diff = make_unified_diff(rel, before_text, after_text)
18605
+ diff, added, deleted = make_unified_diff(rel, before_text, after_text)
18586
18606
  numbered = make_numbered_diff(before_text, after_text)
18587
18607
  numbered_text = render_numbered_diff_text(numbered)
18588
- added = sum(1 for ln in diff.splitlines() if ln.startswith("+") and not ln.startswith("+++"))
18589
- deleted = sum(1 for ln in diff.splitlines() if ln.startswith("-") and not ln.startswith("---"))
18590
18608
  code_stage = self._record_code_preview_stage(
18591
18609
  rel_path=rel,
18592
18610
  before_text=before_text,
@@ -18635,11 +18653,9 @@ class SessionState:
18635
18653
  self._emit("status", {"summary": summary})
18636
18654
  out = f"{out}\n{summary}"
18637
18655
  after_text = try_read_text(fp, max_bytes=CODE_PREVIEW_STAGE_MAX_BYTES) or ""
18638
- diff = make_unified_diff(rel, before_text, after_text)
18656
+ diff, added, deleted = make_unified_diff(rel, before_text, after_text)
18639
18657
  numbered = make_numbered_diff(before_text, after_text)
18640
18658
  numbered_text = render_numbered_diff_text(numbered)
18641
- added = sum(1 for ln in diff.splitlines() if ln.startswith("+") and not ln.startswith("+++"))
18642
- deleted = sum(1 for ln in diff.splitlines() if ln.startswith("-") and not ln.startswith("---"))
18643
18659
  code_stage = self._record_code_preview_stage(
18644
18660
  rel_path=rel,
18645
18661
  before_text=before_text,
@@ -19265,7 +19281,7 @@ class SessionState:
19265
19281
  for tc in tool_calls
19266
19282
  ]
19267
19283
  self._append_agent_context_message(role_key, assistant, mirror_to_global=True)
19268
- if text.strip() or thinking_text:
19284
+ if (text.strip() or thinking_text) and not tool_calls:
19269
19285
  emit_text = text if text.strip() else "[thinking-only output]"
19270
19286
  self._emit_agent_message(role_key, emit_text, summary=f"{self._agent_display_name(role_key)} response")
19271
19287
  if not tool_calls:
@@ -20251,7 +20267,7 @@ class SessionState:
20251
20267
  for tc in tool_calls
20252
20268
  ]
20253
20269
  self.messages.append(assistant)
20254
- if text.strip() or thinking_text:
20270
+ if (text.strip() or thinking_text) and not tool_calls:
20255
20271
  emit_text = text if text.strip() else "[thinking-only output]"
20256
20272
  emit_summary = "assistant message" if text.strip() else "assistant thinking-only message"
20257
20273
  self._emit(
@@ -20409,6 +20425,8 @@ class SessionState:
20409
20425
  fault_counter = 0
20410
20426
  last_fault_reason = ""
20411
20427
  self._prune_runtime_retry_hints()
20428
+ if self.todo.has_open_items():
20429
+ self._mark_all_done_silently("single-mode endpoint exit")
20412
20430
  self._emit(
20413
20431
  "status",
20414
20432
  {
@@ -20426,6 +20444,8 @@ class SessionState:
20426
20444
  fault_counter = 0
20427
20445
  last_fault_reason = ""
20428
20446
  self._prune_runtime_retry_hints()
20447
+ if self.todo.has_open_items():
20448
+ self._mark_all_done_silently("single-mode conclusive/substantial exit")
20429
20449
  self._emit(
20430
20450
  "status",
20431
20451
  {
@@ -21014,6 +21034,10 @@ class SessionState:
21014
21034
  except Exception as exc:
21015
21035
  self._emit("error", {"summary": f"agent error: {exc}", "trace": traceback.format_exc()})
21016
21036
  finally:
21037
+ if self.todo.has_open_items() and not self.cancel_requested:
21038
+ _last = self._latest_agent_assistant_text(single_role) or ""
21039
+ if self._looks_like_conclusive_reply(_last):
21040
+ self._mark_all_done_silently(f"single-mode conclusive exit by {single_role}")
21017
21041
  dropped_pending_inputs = 0
21018
21042
  removed_runtime_hints = 0
21019
21043
  with self.lock:
@@ -21526,6 +21550,7 @@ class SessionManager:
21526
21550
  self.arbiter_max_tokens = max(24, min(256, int(arbiter_max_tokens or ARBITER_DEFAULT_MAX_TOKENS)))
21527
21551
  self.arbiter_temperature = max(0.0, min(1.0, float(arbiter_temperature if arbiter_temperature is not None else ARBITER_DEFAULT_TEMPERATURE)))
21528
21552
  self.execution_mode = normalize_execution_mode(execution_mode, default=EXECUTION_MODE_SYNC)
21553
+ self.single_advance_prompt_enhance = False
21529
21554
  env_ok, env_tags, _ = probe_ollama_environment(ollama_base)
21530
21555
  self.ollama_env_available = bool(env_ok)
21531
21556
  self.ollama_env_tags: list[str] = list(env_tags)
@@ -21792,6 +21817,7 @@ class SessionManager:
21792
21817
  min(1.0, float(self.arbiter_temperature if self.arbiter_temperature is not None else ARBITER_DEFAULT_TEMPERATURE)),
21793
21818
  )
21794
21819
  sess.execution_mode = normalize_execution_mode(self.execution_mode, default=EXECUTION_MODE_SYNC)
21820
+ sess.single_advance_prompt_enhance = bool(self.single_advance_prompt_enhance)
21795
21821
  sess._apply_active_profile()
21796
21822
  sess.updated_at = now_ts()
21797
21823
  sess._persist()
@@ -22889,6 +22915,7 @@ function _deltaScheduleRender(flags={}){
22889
22915
  if(S.deltaRenderRaf)return;
22890
22916
  S.deltaRenderRaf=requestAnimationFrame(()=>{
22891
22917
  S.deltaRenderRaf=0;
22918
+ if(S.refreshInFlight)return;
22892
22919
  const needChat=!!S.deltaRenderChat;
22893
22920
  const needBoards=!!S.deltaRenderBoards;
22894
22921
  const needSessions=!!S.deltaRenderSessions;
@@ -23048,7 +23075,7 @@ function onRuntimeEvent(evt){
23048
23075
  const typ=String(evt.type||'');
23049
23076
  if(typ==='render_frame'){_renderBridgeEnqueue(evt.data||{});return{handled:true,needsSnapshot:false}}
23050
23077
  if(typ==='render_bridge'){const d=evt.data||{};const summary=String(d?.summary||'').trim();if(summary){_renderBridgeShow();_renderBridgeUpdateMeta(summary,true);_renderBridgeHideLater(30000)}return{handled:true,needsSnapshot:false}}
23051
- if(typ==='compact'){scheduleCompactRefreshBurst(COMPACT_AUTO_REFRESH_COUNT);const reason=parseCompactReason(evt.data||{});if(reason==='auto'||reason.startsWith('truncation-rescue')){const pct=Number(evt.data?.context_left_percent_before);const left=Number(evt.data?.context_left_before);const limit=Number(evt.data?.context_limit_before);const pctTxt=Number.isFinite(pct)?pct.toFixed(1):'-';const leftTxt=Number.isFinite(left)&&Number.isFinite(limit)?`${left}/${limit}`:'-';showCompactToast(`${t('compact_auto')}:${pctTxt}% left (${leftTxt}) · delta-sync`)}_deltaAppendActivity(typ,evt.data||{},Number(evt?.ts||Date.now()/1000));_deltaScheduleRender({boards:true,sessions:true});return{handled:true,needsSnapshot:false}}
23078
+ if(typ==='compact'){scheduleCompactRefreshBurst(COMPACT_AUTO_REFRESH_COUNT);const reason=parseCompactReason(evt.data||{});if(reason==='auto'||reason.startsWith('truncation-rescue')){const pct=Number(evt.data?.context_left_percent_before);const left=Number(evt.data?.context_left_before);const limit=Number(evt.data?.context_limit_before);const pctTxt=Number.isFinite(pct)?pct.toFixed(1):'-';const leftTxt=Number.isFinite(left)&&Number.isFinite(limit)?`${left}/${limit}`:'-';showCompactToast(`${t('compact_auto')}:${pctTxt}% left (${leftTxt}) · delta-sync`)}_deltaAppendActivity(typ,evt.data||{},Number(evt?.ts||Date.now()/1000));return{handled:true,needsSnapshot:false}}
23052
23079
  return _deltaApplyRuntimeEvent(evt);
23053
23080
  }
23054
23081
  function _deltaStartWatchdog(){
@@ -24909,23 +24936,21 @@ async function refreshSnapshot(opt={}){
24909
24936
  const chatEl=E('chat');
24910
24937
  const scrolling=_chatVirtIsUserScrolling(chatEl);
24911
24938
  const feedSig=feedSignature(S.snap);
24912
- if(forceFull||feedSig!==S.lastFeedSig){
24913
- S.lastFeedSig=feedSig;
24914
- if(scrolling&&chatEl){
24915
- _chatVirtDebounceWhileScrolling(chatEl,'_virtScrollSyncTimer',()=>renderChat('snapshot'));
24916
- }else{
24917
- if(chatEl)_chatVirtCancelDebounce(chatEl,'_virtScrollSyncTimer');
24918
- renderChat();
24919
- }
24920
- }
24921
24939
  const boardSig=boardsSignature(S.snap);
24922
- if(forceFull||boardSig!==S.lastBoardsSig){
24923
- S.lastBoardsSig=boardSig;
24940
+ const needChat=forceFull||feedSig!==S.lastFeedSig;
24941
+ const needBoards=forceFull||boardSig!==S.lastBoardsSig;
24942
+ if(needChat)S.lastFeedSig=feedSig;
24943
+ if(needBoards)S.lastBoardsSig=boardSig;
24944
+ if(needChat||needBoards){
24945
+ const doRender=()=>{
24946
+ if(needChat)renderChat('snapshot');
24947
+ if(needBoards)renderBoards();
24948
+ };
24924
24949
  if(scrolling&&chatEl){
24925
- _chatVirtDebounceWhileScrolling(chatEl,'_virtBoardsSyncTimer',()=>renderBoards(),CHAT_SCROLL_SYNC_DEBOUNCE_MS+20);
24950
+ _chatVirtDebounceWhileScrolling(chatEl,'_virtScrollSyncTimer',doRender);
24926
24951
  }else{
24927
- if(chatEl)_chatVirtCancelDebounce(chatEl,'_virtBoardsSyncTimer');
24928
- renderBoards();
24952
+ if(chatEl){_chatVirtCancelDebounce(chatEl,'_virtScrollSyncTimer');_chatVirtCancelDebounce(chatEl,'_virtBoardsSyncTimer');}
24953
+ doRender();
24929
24954
  }
24930
24955
  }
24931
24956
  renderActivePreview(false);
@@ -26027,6 +26052,8 @@ class AppContext:
26027
26052
  cfg_max_output_tokens = cfg.get("max_output_tokens")
26028
26053
  if cfg_max_output_tokens is not None:
26029
26054
  self.max_output_tokens = max(256, int(cfg_max_output_tokens))
26055
+ if "single_advance_prompt_enhance" in cfg:
26056
+ self.single_advance_prompt_enhance = bool(cfg["single_advance_prompt_enhance"])
26030
26057
 
26031
26058
  def normalized_profiles() -> tuple[dict[str, dict], str]:
26032
26059
  rows: dict[str, dict] = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clouds-coder
3
- Version: 2026.3.16
3
+ Version: 2026.3.17
4
4
  Summary: Clouds Coder: local-first Cloud CLI coder runtime with Web UI and Skills Studio.
5
5
  Author: Clouds Coder Contributors
6
6
  License: MIT
@@ -801,7 +801,3 @@ flowchart TD
801
801
  ## 13. License
802
802
 
803
803
  This project is released under the MIT License. See [LICENSE](./LICENSE).
804
-
805
- ---
806
-
807
- If you plan to publish on GitHub, recommended next step is to add a small `CHANGELOG.md` and one architecture diagram image under `docs/` for faster onboarding.
@@ -774,7 +774,3 @@ flowchart TD
774
774
  ## 13. License
775
775
 
776
776
  This project is released under the MIT License. See [LICENSE](./LICENSE).
777
-
778
- ---
779
-
780
- If you plan to publish on GitHub, recommended next step is to add a small `CHANGELOG.md` and one architecture diagram image under `docs/` for faster onboarding.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clouds-coder
3
- Version: 2026.3.16
3
+ Version: 2026.3.17
4
4
  Summary: Clouds Coder: local-first Cloud CLI coder runtime with Web UI and Skills Studio.
5
5
  Author: Clouds Coder Contributors
6
6
  License: MIT
@@ -801,7 +801,3 @@ flowchart TD
801
801
  ## 13. License
802
802
 
803
803
  This project is released under the MIT License. See [LICENSE](./LICENSE).
804
-
805
- ---
806
-
807
- If you plan to publish on GitHub, recommended next step is to add a small `CHANGELOG.md` and one architecture diagram image under `docs/` for faster onboarding.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "clouds-coder"
7
- version = "2026.3.16"
7
+ version = "2026.3.17"
8
8
  description = "Clouds Coder: local-first Cloud CLI coder runtime with Web UI and Skills Studio."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"