webbee 0.3.44__tar.gz → 0.3.45__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 (129) hide show
  1. {webbee-0.3.44 → webbee-0.3.45}/CHANGELOG.md +16 -0
  2. {webbee-0.3.44 → webbee-0.3.45}/PKG-INFO +1 -1
  3. {webbee-0.3.44 → webbee-0.3.45}/pyproject.toml +1 -1
  4. webbee-0.3.45/src/webbee/__init__.py +1 -0
  5. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/render.py +51 -7
  6. {webbee-0.3.44 → webbee-0.3.45}/tests/test_render.py +43 -0
  7. webbee-0.3.44/src/webbee/__init__.py +0 -1
  8. {webbee-0.3.44 → webbee-0.3.45}/.github/workflows/publish.yml +0 -0
  9. {webbee-0.3.44 → webbee-0.3.45}/.gitignore +0 -0
  10. {webbee-0.3.44 → webbee-0.3.45}/LICENSE +0 -0
  11. {webbee-0.3.44 → webbee-0.3.45}/README.md +0 -0
  12. {webbee-0.3.44 → webbee-0.3.45}/install.sh +0 -0
  13. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/account.py +0 -0
  14. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/active_sessions.py +0 -0
  15. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/banner_art.py +0 -0
  16. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/boot.py +0 -0
  17. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/checkpoints.py +0 -0
  18. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/cli.py +0 -0
  19. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/clipboard.py +0 -0
  20. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/clipboard_read.py +0 -0
  21. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/coding_context.py +0 -0
  22. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/commands.py +0 -0
  23. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/config.py +0 -0
  24. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/consent.py +0 -0
  25. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/details.py +0 -0
  26. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/events.py +0 -0
  27. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/frames.py +0 -0
  28. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/home.py +0 -0
  29. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/home_view.py +0 -0
  30. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/http.py +0 -0
  31. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/instance_lock.py +0 -0
  32. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/__init__.py +0 -0
  33. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/chunker.py +0 -0
  34. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/embed.py +0 -0
  35. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/graph.py +0 -0
  36. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/indexer.py +0 -0
  37. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/models.py +0 -0
  38. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/query.py +0 -0
  39. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/service.py +0 -0
  40. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/store.py +0 -0
  41. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/vectors.py +0 -0
  42. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/intel/watch.py +0 -0
  43. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/mode_store.py +0 -0
  44. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/newtab_mode.py +0 -0
  45. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/output_pane.py +0 -0
  46. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/queue_panel.py +0 -0
  47. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/reflow.py +0 -0
  48. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/remote.py +0 -0
  49. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/repl.py +0 -0
  50. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/repo.py +0 -0
  51. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/selection.py +0 -0
  52. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/session.py +0 -0
  53. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/sessions.py +0 -0
  54. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/sizing.py +0 -0
  55. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/slots.py +0 -0
  56. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/steer.py +0 -0
  57. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/stream.py +0 -0
  58. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/tab_store.py +0 -0
  59. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/tabs.py +0 -0
  60. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/thread.py +0 -0
  61. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/tier_store.py +0 -0
  62. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/todo_panel.py +0 -0
  63. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/tokens.py +0 -0
  64. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/tools.py +0 -0
  65. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/tui.py +0 -0
  66. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/update.py +0 -0
  67. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/urlopen.py +0 -0
  68. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/wallet.py +0 -0
  69. {webbee-0.3.44 → webbee-0.3.45}/src/webbee/worktrees.py +0 -0
  70. {webbee-0.3.44 → webbee-0.3.45}/tests/__init__.py +0 -0
  71. {webbee-0.3.44 → webbee-0.3.45}/tests/conftest.py +0 -0
  72. {webbee-0.3.44 → webbee-0.3.45}/tests/test_account.py +0 -0
  73. {webbee-0.3.44 → webbee-0.3.45}/tests/test_active_sessions.py +0 -0
  74. {webbee-0.3.44 → webbee-0.3.45}/tests/test_audit_2026_07_25_findings.py +0 -0
  75. {webbee-0.3.44 → webbee-0.3.45}/tests/test_checkpoints.py +0 -0
  76. {webbee-0.3.44 → webbee-0.3.45}/tests/test_cli.py +0 -0
  77. {webbee-0.3.44 → webbee-0.3.45}/tests/test_clipboard.py +0 -0
  78. {webbee-0.3.44 → webbee-0.3.45}/tests/test_clipboard_read.py +0 -0
  79. {webbee-0.3.44 → webbee-0.3.45}/tests/test_coding_context.py +0 -0
  80. {webbee-0.3.44 → webbee-0.3.45}/tests/test_commands.py +0 -0
  81. {webbee-0.3.44 → webbee-0.3.45}/tests/test_config.py +0 -0
  82. {webbee-0.3.44 → webbee-0.3.45}/tests/test_cpc_contract_stable.py +0 -0
  83. {webbee-0.3.44 → webbee-0.3.45}/tests/test_details.py +0 -0
  84. {webbee-0.3.44 → webbee-0.3.45}/tests/test_events.py +0 -0
  85. {webbee-0.3.44 → webbee-0.3.45}/tests/test_freeze_fix.py +0 -0
  86. {webbee-0.3.44 → webbee-0.3.45}/tests/test_home.py +0 -0
  87. {webbee-0.3.44 → webbee-0.3.45}/tests/test_home_view.py +0 -0
  88. {webbee-0.3.44 → webbee-0.3.45}/tests/test_instance_lock.py +0 -0
  89. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_chunker.py +0 -0
  90. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_embed.py +0 -0
  91. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_graph.py +0 -0
  92. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_indexer.py +0 -0
  93. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_no_numpy.py +0 -0
  94. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_query.py +0 -0
  95. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_service.py +0 -0
  96. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_store.py +0 -0
  97. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_vectors.py +0 -0
  98. {webbee-0.3.44 → webbee-0.3.45}/tests/test_intel_watch.py +0 -0
  99. {webbee-0.3.44 → webbee-0.3.45}/tests/test_marathon.py +0 -0
  100. {webbee-0.3.44 → webbee-0.3.45}/tests/test_mode_store.py +0 -0
  101. {webbee-0.3.44 → webbee-0.3.45}/tests/test_newtab_mode.py +0 -0
  102. {webbee-0.3.44 → webbee-0.3.45}/tests/test_packaging.py +0 -0
  103. {webbee-0.3.44 → webbee-0.3.45}/tests/test_queue_manage_0337.py +0 -0
  104. {webbee-0.3.44 → webbee-0.3.45}/tests/test_reflow.py +0 -0
  105. {webbee-0.3.44 → webbee-0.3.45}/tests/test_repl.py +0 -0
  106. {webbee-0.3.44 → webbee-0.3.45}/tests/test_repo.py +0 -0
  107. {webbee-0.3.44 → webbee-0.3.45}/tests/test_session.py +0 -0
  108. {webbee-0.3.44 → webbee-0.3.45}/tests/test_sessions.py +0 -0
  109. {webbee-0.3.44 → webbee-0.3.45}/tests/test_sizing.py +0 -0
  110. {webbee-0.3.44 → webbee-0.3.45}/tests/test_slots.py +0 -0
  111. {webbee-0.3.44 → webbee-0.3.45}/tests/test_steer.py +0 -0
  112. {webbee-0.3.44 → webbee-0.3.45}/tests/test_stream.py +0 -0
  113. {webbee-0.3.44 → webbee-0.3.45}/tests/test_tab_restore_0337.py +0 -0
  114. {webbee-0.3.44 → webbee-0.3.45}/tests/test_tabs.py +0 -0
  115. {webbee-0.3.44 → webbee-0.3.45}/tests/test_terminal_ux_0336.py +0 -0
  116. {webbee-0.3.44 → webbee-0.3.45}/tests/test_thread.py +0 -0
  117. {webbee-0.3.44 → webbee-0.3.45}/tests/test_tier_store.py +0 -0
  118. {webbee-0.3.44 → webbee-0.3.45}/tests/test_todo_panel.py +0 -0
  119. {webbee-0.3.44 → webbee-0.3.45}/tests/test_tokens.py +0 -0
  120. {webbee-0.3.44 → webbee-0.3.45}/tests/test_tools.py +0 -0
  121. {webbee-0.3.44 → webbee-0.3.45}/tests/test_tui.py +0 -0
  122. {webbee-0.3.44 → webbee-0.3.45}/tests/test_tui_hardening.py +0 -0
  123. {webbee-0.3.44 → webbee-0.3.45}/tests/test_update.py +0 -0
  124. {webbee-0.3.44 → webbee-0.3.45}/tests/test_update_badge_0340.py +0 -0
  125. {webbee-0.3.44 → webbee-0.3.45}/tests/test_urlopen.py +0 -0
  126. {webbee-0.3.44 → webbee-0.3.45}/tests/test_version.py +0 -0
  127. {webbee-0.3.44 → webbee-0.3.45}/tests/test_version_badge_corner.py +0 -0
  128. {webbee-0.3.44 → webbee-0.3.45}/tests/test_wallet.py +0 -0
  129. {webbee-0.3.44 → webbee-0.3.45}/tests/test_worktrees.py +0 -0
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.45
4
+
5
+ Consent prompts stop being an easy-to-miss detail:
6
+
7
+ - **Both the kernel consent (`? approve ...`) and the local remote-control
8
+ yes/no ask now print as a bordered card, right in the conversation, the
9
+ moment they're asked** -- the same proven Panel this terminal already
10
+ uses for the billing and login prompts, so it reflows correctly to
11
+ whatever width the terminal actually has (a phone's SSH client, a narrow
12
+ tmux pane, a full window). The question and the "how to answer"
13
+ instructions (`y/yes -> do it`, `n/no -> skip it`, or type your own
14
+ reply) now live in the SAME box -- nothing to go hunting for in a
15
+ toolbar hint several rows away. The underlying protocol is unchanged:
16
+ still a bare raw string relayed verbatim for the kernel to interpret
17
+ (ICNLI, safe-by-default) -- this is a display-only change.
18
+
3
19
  ## 0.3.44
4
20
 
5
21
  Three real Home-dashboard bugs, found and fixed:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webbee
3
- Version: 0.3.44
3
+ Version: 0.3.45
4
4
  Summary: Webbee 🐝 — the Imperal Cloud coding agent in your terminal
5
5
  Project-URL: Homepage, https://imperal.io
6
6
  Project-URL: Documentation, https://docs.imperal.io
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "webbee"
3
- version = "0.3.44"
3
+ version = "0.3.45"
4
4
  description = "Webbee 🐝 — the Imperal Cloud coding agent in your terminal"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1 @@
1
+ __version__ = "0.3.45"
@@ -614,12 +614,38 @@ class RichSink:
614
614
  NEVER interpret (the kernel decides, ICNLI). When the dock is running
615
615
  the reply comes through the pinned box via an asyncio.Future (no
616
616
  blocking input on the event loop); otherwise fall back to the injected
617
- sync reader (tests / non-tty)."""
617
+ sync reader (tests / non-tty).
618
+
619
+ webbee-code-consent-card-v1: this used to be one dim, easy-to-miss
620
+ line in the feed plus a SEPARATE hint buried in the toolbar below --
621
+ several users said it wasn't obvious what was even being asked, let
622
+ alone how to answer it. Now it's a bordered card printed RIGHT IN
623
+ THE CONVERSATION, at the moment it's asked -- same proven Panel this
624
+ module already uses for the billing/login prompts, so it reflows to
625
+ whatever width the terminal actually has (phone SSH client, a narrow
626
+ tmux pane, a full-width window -- all get a readable card, not a
627
+ clipped line). The question AND the "how to answer" instructions
628
+ live in the SAME box, so there's nothing to hunt for elsewhere on
629
+ screen. The protocol contract is unchanged: still a bare raw string
630
+ relayed verbatim for the kernel to interpret (ICNLI, safe-by-default)."""
618
631
  label = _clean(f"{app_id}·{tool}" if app_id else tool)
619
- sal = _clean(_salient_arg(args))
620
- w = self.console.width
621
- self.console.print(Text.assemble((" ? approve ", "yellow"), (label, "dim"),
622
- ((" " + sal[:trunc(w, 0.4, 60)]) if sal else "", "dim")))
632
+ sal = _clean(_salient_arg(args))[:500]
633
+ body = Text()
634
+ body.append(label, style=f"bold {_ACCENT}")
635
+ if sal:
636
+ body.append("\n" + sal, style="white")
637
+ body.append("\n\n")
638
+ body.append("y", style="bold green")
639
+ body.append(" / ", style="dim")
640
+ body.append("yes", style="bold green")
641
+ body.append(" → do it ", style="dim")
642
+ body.append("n", style="bold red")
643
+ body.append(" / ", style="dim")
644
+ body.append("no", style="bold red")
645
+ body.append(" → skip it\n", style="dim")
646
+ body.append("or just type your own reply in any language — Enter to send", style="dim")
647
+ self.console.print(_pad(Panel(body, title="🔐 wants to do this — approve?",
648
+ border_style=_BEE)))
623
649
  fut = self._arm_consent(label, sal)
624
650
  if fut is None: # non-tty / no running app
625
651
  raw = self._input(" ")
@@ -643,8 +669,26 @@ class RichSink:
643
669
  all return False, so the caller keeps the current mode. This is a
644
670
  LOCAL policy decision, not a kernel consent, so the reply is
645
671
  interpreted here rather than relayed (ICNLI raw-relay applies to
646
- kernel consents only)."""
647
- self.console.print(_pad(Text("⚠ " + _clean(question), style=f"bold {_BEE}")))
672
+ kernel consents only).
673
+
674
+ webbee-code-consent-card-v1: same bordered-card treatment as
675
+ ask_consent -- a bare "⚠ ..." line was just as easy to miss as the
676
+ old one-line kernel consent was. The card reflows to the terminal's
677
+ real width on any device, and states the y/n instructions right
678
+ where the question is instead of leaving them implicit."""
679
+ body = Text()
680
+ body.append(_clean(question), style=f"bold {_BEE}")
681
+ body.append("\n\n")
682
+ body.append("y", style="bold green")
683
+ body.append(" / ", style="dim")
684
+ body.append("yes", style="bold green")
685
+ body.append(" → allow ", style="dim")
686
+ body.append("n", style="bold red")
687
+ body.append(" / ", style="dim")
688
+ body.append("no", style="bold red")
689
+ body.append(" → stay as-is (default)", style="dim")
690
+ self.console.print(_pad(Panel(body, title="⚠ another surface is asking",
691
+ border_style=_BEE)))
648
692
  fut = self._arm_consent(question, question)
649
693
  try:
650
694
  if fut is None: # non-tty / no dock → sync reader
@@ -299,6 +299,49 @@ def test_consent_shows_human_summary_not_dict():
299
299
  assert "notes·delete_note" in out
300
300
 
301
301
 
302
+ # ── webbee-code-consent-card-v1: the consent card is a real, hard-to-miss
303
+ # bordered block printed IN the conversation, with the y/n instructions
304
+ # living right next to the question -- not a dim one-liner + a separate
305
+ # toolbar hint the user has to go hunting for. ────────────────────────────
306
+
307
+ def test_consent_is_a_bordered_card_with_inline_instructions():
308
+ console = Console(record=True, width=90)
309
+ s = RichSink(console=console, live_enabled=False,
310
+ input_fn=lambda p: "yes", clock=lambda: 0.0)
311
+ asyncio.run(s.ask_consent("notes", "delete_note", {"title": "Q3 budget"}))
312
+ out = console.export_text()
313
+ # a real Panel border, not a bare printed line
314
+ assert "\u256d" in out or "\u2500" in out # rounded-corner or horizontal rule glyph
315
+ assert "approve?" in out
316
+ # the how-to-answer instructions are IN the card, not somewhere else
317
+ assert "yes" in out and "no" in out
318
+ assert "do it" in out and "skip it" in out
319
+ assert "notes·delete_note" in out
320
+
321
+
322
+ def test_consent_card_reflows_to_narrow_terminal_width():
323
+ # A phone SSH client / narrow tmux pane: the card must not crash or
324
+ # silently drop the question -- Panel reflows, same as billing/login.
325
+ console = Console(record=True, width=28)
326
+ s = RichSink(console=console, live_enabled=False,
327
+ input_fn=lambda p: "y", clock=lambda: 0.0)
328
+ asyncio.run(s.ask_consent("mail", "delete", {"query": "old newsletters"}))
329
+ out = console.export_text()
330
+ assert "mail" in out and "delete" in out
331
+ assert "yes" in out and "no" in out
332
+
333
+
334
+ def test_ask_yes_no_is_also_a_bordered_card_with_instructions():
335
+ console = Console(record=True, width=90)
336
+ s = RichSink(console=console, live_enabled=False,
337
+ input_fn=lambda p: "y", clock=lambda: 0.0)
338
+ asyncio.run(s.ask_yes_no("switch to autopilot — allow? [y/n]"))
339
+ out = console.export_text()
340
+ assert "\u256d" in out or "\u2500" in out
341
+ assert "allow" in out.lower()
342
+ assert "yes" in out and "no" in out
343
+
344
+
302
345
  def test_user_echo_is_unboxed_line():
303
346
  s = _sink()
304
347
  s.user_echo("delete my Q3 note")
@@ -1 +0,0 @@
1
- __version__ = "0.3.44"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes