deepparallel 0.5.4__tar.gz → 0.5.5__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 (83) hide show
  1. {deepparallel-0.5.4 → deepparallel-0.5.5}/PKG-INFO +1 -1
  2. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/__init__.py +1 -1
  3. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cli.py +23 -11
  4. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cockpit.py +20 -0
  5. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cockpit_observe.py +9 -2
  6. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/PKG-INFO +1 -1
  7. {deepparallel-0.5.4 → deepparallel-0.5.5}/pyproject.toml +1 -1
  8. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_cockpit.py +41 -0
  9. {deepparallel-0.5.4 → deepparallel-0.5.5}/README.md +0 -0
  10. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/agent.py +0 -0
  11. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/backend.py +0 -0
  12. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/branding.py +0 -0
  13. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cockpit_panel.py +0 -0
  14. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cockpit_sim.py +0 -0
  15. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/config.py +0 -0
  16. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/crowe_id.py +0 -0
  17. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/dsml.py +0 -0
  18. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/fusion.py +0 -0
  19. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/licensing.py +0 -0
  20. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/registry.json +0 -0
  21. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/renderer.py +0 -0
  22. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/research/__init__.py +0 -0
  23. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/research/conduit.py +0 -0
  24. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/research/provider.py +0 -0
  25. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/routing.example.json +0 -0
  26. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/routing.py +0 -0
  27. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/serve.py +0 -0
  28. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/supply_chain.py +0 -0
  29. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/system_prompt.txt +0 -0
  30. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/__init__.py +0 -0
  31. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/codeast.py +0 -0
  32. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/edit.py +0 -0
  33. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/files.py +0 -0
  34. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/mcp.py +0 -0
  35. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/registry.py +0 -0
  36. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/sandbox.py +0 -0
  37. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/search.py +0 -0
  38. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/shell.py +0 -0
  39. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/vision.py +0 -0
  40. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/web.py +0 -0
  41. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/userinput.py +0 -0
  42. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/SOURCES.txt +0 -0
  43. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/dependency_links.txt +0 -0
  44. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/entry_points.txt +0 -0
  45. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/requires.txt +0 -0
  46. {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/top_level.txt +0 -0
  47. {deepparallel-0.5.4 → deepparallel-0.5.5}/setup.cfg +0 -0
  48. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_agent.py +0 -0
  49. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_backend.py +0 -0
  50. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_backend_chat.py +0 -0
  51. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_backend_stream.py +0 -0
  52. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_branding.py +0 -0
  53. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_cli.py +0 -0
  54. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_cockpit_panel.py +0 -0
  55. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_cockpit_sim.py +0 -0
  56. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_config.py +0 -0
  57. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_crowe_backend.py +0 -0
  58. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_crowe_gateway_backend.py +0 -0
  59. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_crowe_id_auth.py +0 -0
  60. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_crowe_payment_required.py +0 -0
  61. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_dsml.py +0 -0
  62. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_fusion.py +0 -0
  63. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_issuer_signer.py +0 -0
  64. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_licensing.py +0 -0
  65. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_renderer.py +0 -0
  66. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_research.py +0 -0
  67. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_research_provider.py +0 -0
  68. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_routing.py +0 -0
  69. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_serve.py +0 -0
  70. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_spinner_color.py +0 -0
  71. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_supply_chain.py +0 -0
  72. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tool_registry.py +0 -0
  73. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_codeast.py +0 -0
  74. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_edit.py +0 -0
  75. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_files.py +0 -0
  76. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_mcp.py +0 -0
  77. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_sandbox.py +0 -0
  78. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_search.py +0 -0
  79. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_shell.py +0 -0
  80. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_vision.py +0 -0
  81. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_web.py +0 -0
  82. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_userinput.py +0 -0
  83. {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_userinput_paste.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepparallel
3
- Version: 0.5.4
3
+ Version: 0.5.5
4
4
  Summary: DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic.
5
5
  Author-email: Michael Crowe <michael@crowelogic.com>
6
6
  License: Apache-2.0
@@ -1,3 +1,3 @@
1
1
  """DeepParallel CLI package."""
2
2
 
3
- __version__ = "0.5.4"
3
+ __version__ = "0.5.5"
@@ -17,7 +17,6 @@ apply to chat and run.
17
17
  from __future__ import annotations
18
18
 
19
19
  import os
20
- import subprocess
21
20
  import sys
22
21
  from dataclasses import replace
23
22
  from pathlib import Path
@@ -295,6 +294,13 @@ def _agent_repl(backend: Backend, settings: Settings, renderer: Renderer) -> Non
295
294
  mode = settings.fusion_mode if settings.fusion_mode in ("reason", "escalate") else "off"
296
295
  deep_next = False
297
296
  auto = settings.auto_approve
297
+ cockpit = Cockpit(os.path.abspath(os.path.join(".cockpit", "events.jsonl")))
298
+ cockpit_on = cockpit.in_waveterm()
299
+ observer = make_observer(cockpit)
300
+ if cockpit_on:
301
+ cockpit.events_path.parent.mkdir(parents=True, exist_ok=True)
302
+ cockpit.events_path.write_text("")
303
+ cockpit.status("start", "cockpit ready")
298
304
  while True:
299
305
  bits = ([mode] if mode != "off" else []) + (["auto"] if auto else [])
300
306
  tag = f"[{' · '.join(bits)}] " if bits else ""
@@ -309,7 +315,7 @@ def _agent_repl(backend: Backend, settings: Settings, renderer: Renderer) -> Non
309
315
  break
310
316
  if user_msg == "/help":
311
317
  branding.info(
312
- "/quit · /reset · /info · /tools · /auto · /fast //fuse //escalate //deep · prompt"
318
+ "/quit · /reset · /info · /tools · /auto · /cockpit · /fast //fuse //escalate //deep · prompt"
313
319
  )
314
320
  continue
315
321
  if user_msg in {"/auto", "/yes"}:
@@ -337,6 +343,17 @@ def _agent_repl(backend: Backend, settings: Settings, renderer: Renderer) -> Non
337
343
  deep_next = True
338
344
  branding.info("next prompt runs as a multi-model deep query")
339
345
  continue
346
+ if user_msg == "/cockpit":
347
+ cockpit_on = not cockpit_on
348
+ if cockpit_on:
349
+ cockpit.events_path.parent.mkdir(parents=True, exist_ok=True)
350
+ cockpit.events_path.write_text("")
351
+ cockpit._panel_opened = False
352
+ branding.info("cockpit ON - the data window opens when research starts.")
353
+ else:
354
+ cockpit.status("done", "cockpit complete")
355
+ branding.info("cockpit OFF.")
356
+ continue
340
357
 
341
358
  messages.append({"role": "user", "content": user_msg})
342
359
  if deep_next:
@@ -354,12 +371,15 @@ def _agent_repl(backend: Backend, settings: Settings, renderer: Renderer) -> Non
354
371
  auto_approve=auto,
355
372
  stream=True,
356
373
  guardian=guardian,
374
+ on_event=observer if cockpit_on else None,
357
375
  )
358
376
  if mode in ("reason", "escalate"):
359
377
  branding.info("reasoned by CroweLM Reason · answered by DeepParallel")
360
378
  except Exception as e: # noqa: BLE001 - surface as friendly message
361
379
  renderer.error(_translate_error(e))
362
380
 
381
+ if cockpit_on:
382
+ cockpit.status("done", "cockpit complete")
363
383
  branding.attribution_footer()
364
384
 
365
385
 
@@ -508,15 +528,7 @@ def cockpit(ctx: click.Context, assume_yes: bool, prompt: tuple[str, ...]) -> No
508
528
  open(events_path, "w").close()
509
529
  cp = Cockpit(events_path)
510
530
  cp.status("start", "cockpit online")
511
- if cp.in_waveterm():
512
- try:
513
- subprocess.Popen(
514
- ["wsh", "run", "--", "dp", "_cockpit-panel", events_path],
515
- stdout=subprocess.DEVNULL,
516
- stderr=subprocess.DEVNULL,
517
- )
518
- except Exception: # noqa: BLE001 - the panel block is best-effort
519
- pass
531
+ cp.ensure_panel()
520
532
  backend = _wrap_fusion(backend, settings)
521
533
  system = load_system_prompt()
522
534
  messages = _build_messages([], system, " ".join(prompt))
@@ -45,6 +45,7 @@ class Cockpit:
45
45
 
46
46
  def __init__(self, events_path: str = ".cockpit/events.jsonl"):
47
47
  self.events_path = Path(events_path)
48
+ self._panel_opened = False
48
49
 
49
50
  # --- environment -------------------------------------------------------
50
51
 
@@ -119,6 +120,25 @@ class Cockpit:
119
120
  """Best-effort `wsh notify`. Silent no-op off WaveTerm or on failure."""
120
121
  self._run_wsh(["notify", message])
121
122
 
123
+ def ensure_panel(self) -> None:
124
+ """Open the live data-window block once per session (idempotent).
125
+
126
+ Spawns `dp _cockpit-panel <abs-path>` in an adjacent WaveTerm block on
127
+ the first call so the cockpit appears only when research actually
128
+ starts; repeat calls and runs outside WaveTerm are no-ops.
129
+ """
130
+ if self._panel_opened or not self.in_waveterm():
131
+ return
132
+ self._panel_opened = True
133
+ try:
134
+ subprocess.Popen(
135
+ ["wsh", "run", "--", "dp", "_cockpit-panel", str(self.events_path.resolve())],
136
+ stdout=subprocess.DEVNULL,
137
+ stderr=subprocess.DEVNULL,
138
+ )
139
+ except Exception: # noqa: BLE001 - the panel block is best-effort
140
+ pass
141
+
122
142
  # --- contract event helpers -------------------------------------------
123
143
 
124
144
  def status(self, phase: str, message: str) -> None:
@@ -31,8 +31,11 @@ def make_observer(cockpit) -> Callable[[str, dict, str], None]:
31
31
  try:
32
32
  if isinstance(obj, dict) and "error" not in obj:
33
33
  if name == "web_search":
34
+ results = obj.get("results") or []
35
+ if results:
36
+ cockpit.ensure_panel()
34
37
  provider = obj.get("provider", "web")
35
- for r in (obj.get("results") or [])[:8]:
38
+ for r in results[:8]:
36
39
  url = r.get("url")
37
40
  if not url:
38
41
  continue
@@ -45,11 +48,15 @@ def make_observer(cockpit) -> Callable[[str, dict, str], None]:
45
48
  elif name == "web_fetch":
46
49
  url = obj.get("url") or (args or {}).get("url", "")
47
50
  if url:
51
+ cockpit.ensure_panel()
48
52
  cockpit.source(url=url, title=obj.get("title", ""), provider="fetch")
49
53
  if counters["blocks"] < _MAX_WEB_BLOCKS and cockpit.open_web(url):
50
54
  counters["blocks"] += 1
51
55
  elif name == "mcp_search":
52
- for s in (obj.get("servers") or [])[:5]:
56
+ servers = obj.get("servers") or []
57
+ if servers:
58
+ cockpit.ensure_panel()
59
+ for s in servers[:5]:
53
60
  cockpit.source(url="", title=s.get("name", ""), provider="mcp")
54
61
  cockpit.status("tool", name)
55
62
  except Exception: # noqa: BLE001 - observation must never break the loop
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepparallel
3
- Version: 0.5.4
3
+ Version: 0.5.5
4
4
  Summary: DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic.
5
5
  Author-email: Michael Crowe <michael@crowelogic.com>
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deepparallel"
7
- version = "0.5.4"
7
+ version = "0.5.5"
8
8
  description = "DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic."
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -239,3 +239,44 @@ def test_helpers_emit_contract_events(cockpit, off_wave):
239
239
  assert by_type["citation"] == {"ref": 1, "url": "https://c", "title": "Cited"}
240
240
  assert by_type["claim"] == {"text": "a claim", "grounded": True, "confidence": 0.9}
241
241
  assert by_type["sim"] == {"step": 3, "arm": "A", "metric": "accuracy", "value": 0.42}
242
+
243
+
244
+ # --- native lazy panel ------------------------------------------------------
245
+
246
+
247
+ def test_ensure_panel_noop_off_waveterm(cockpit, off_wave, monkeypatch):
248
+ calls = {"n": 0}
249
+ monkeypatch.setattr(subprocess, "Popen", lambda *a, **k: calls.__setitem__("n", calls["n"] + 1))
250
+ cockpit.ensure_panel()
251
+ assert calls["n"] == 0 # nothing spawned outside WaveTerm
252
+
253
+
254
+ def test_ensure_panel_opens_once_in_waveterm(cockpit, in_wave, monkeypatch):
255
+ calls = {"n": 0}
256
+ monkeypatch.setattr(subprocess, "Popen", lambda *a, **k: calls.__setitem__("n", calls["n"] + 1))
257
+ cockpit.ensure_panel()
258
+ cockpit.ensure_panel()
259
+ cockpit.ensure_panel()
260
+ assert calls["n"] == 1 # idempotent: one panel per session
261
+
262
+
263
+ def test_observer_opens_panel_on_grounding(cockpit, in_wave, monkeypatch):
264
+ from deepparallel.cockpit_observe import make_observer
265
+
266
+ calls = {"n": 0}
267
+ monkeypatch.setattr(subprocess, "Popen", lambda *a, **k: calls.__setitem__("n", calls["n"] + 1))
268
+ obs = make_observer(cockpit)
269
+ obs("web_search", {}, json.dumps({"provider": "web", "results": [{"url": "https://a", "title": "A"}]}))
270
+ assert calls["n"] == 1 # research triggers the data window
271
+ obs("web_search", {}, json.dumps({"provider": "web", "results": [{"url": "https://b", "title": "B"}]}))
272
+ assert calls["n"] == 1 # stays one
273
+
274
+
275
+ def test_observer_no_panel_without_grounding(cockpit, in_wave, monkeypatch):
276
+ from deepparallel.cockpit_observe import make_observer
277
+
278
+ calls = {"n": 0}
279
+ monkeypatch.setattr(subprocess, "Popen", lambda *a, **k: calls.__setitem__("n", calls["n"] + 1))
280
+ obs = make_observer(cockpit)
281
+ obs("run_shell", {}, json.dumps({"return_code": 0, "stdout": "ok"})) # no grounding
282
+ assert calls["n"] == 0 # trivial tool use does not open the cockpit
File without changes
File without changes