webbee 0.3.5__tar.gz → 0.3.7__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.
- {webbee-0.3.5 → webbee-0.3.7}/CHANGELOG.md +17 -0
- {webbee-0.3.5 → webbee-0.3.7}/PKG-INFO +1 -1
- {webbee-0.3.5 → webbee-0.3.7}/pyproject.toml +1 -1
- webbee-0.3.7/src/webbee/__init__.py +1 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/render.py +14 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/repl.py +48 -12
- webbee-0.3.7/src/webbee/session.py +512 -0
- webbee-0.3.7/src/webbee/steer.py +106 -0
- webbee-0.3.7/src/webbee/thread.py +68 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_marathon.py +2 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_render.py +18 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_repl.py +114 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_session.py +269 -0
- webbee-0.3.7/tests/test_steer.py +266 -0
- webbee-0.3.7/tests/test_thread.py +266 -0
- webbee-0.3.5/src/webbee/__init__.py +0 -1
- webbee-0.3.5/src/webbee/session.py +0 -361
- webbee-0.3.5/src/webbee/thread.py +0 -29
- webbee-0.3.5/tests/test_thread.py +0 -131
- {webbee-0.3.5 → webbee-0.3.7}/.github/workflows/publish.yml +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/.gitignore +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/LICENSE +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/README.md +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/install.sh +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/account.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/banner_art.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/checkpoints.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/cli.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/clipboard.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/commands.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/config.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/details.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/events.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/frames.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/__init__.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/chunker.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/embed.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/graph.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/indexer.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/models.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/query.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/service.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/store.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/vectors.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/intel/watch.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/output_pane.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/remote.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/repo.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/sessions.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/stream.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/tools.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/tui.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/src/webbee/update.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/__init__.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_account.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_checkpoints.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_cli.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_clipboard.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_commands.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_config.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_cpc_contract_stable.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_details.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_events.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_freeze_fix.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_chunker.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_embed.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_graph.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_indexer.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_no_numpy.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_query.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_service.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_store.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_vectors.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_intel_watch.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_packaging.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_repo.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_sessions.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_stream.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_tools.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_tui.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_tui_hardening.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_update.py +0 -0
- {webbee-0.3.5 → webbee-0.3.7}/tests/test_version.py +0 -0
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.7
|
|
4
|
+
|
|
5
|
+
- Terminal liveness: a consent answered from Telegram/panel no longer
|
|
6
|
+
freezes the terminal — the `approve? y/n` prompt dismisses itself
|
|
7
|
+
("answered from another surface") and the turn keeps rendering live.
|
|
8
|
+
- Idle pickup: an open, idle terminal now picks up instructions sent from
|
|
9
|
+
Telegram/panel within seconds and runs them as normal turns, tagged with
|
|
10
|
+
their origin. If no terminal is open, the instruction waits (up to an
|
|
11
|
+
hour) and Telegram says so instead of failing.
|
|
12
|
+
|
|
13
|
+
## 0.3.6
|
|
14
|
+
|
|
15
|
+
- Boot replay shows only the conversation: flattened tool traffic
|
|
16
|
+
(`[tool_use …]` / `[tool_result] …` blocks the agent keeps as its own
|
|
17
|
+
working memory) no longer floods the boot screen — each replayed message
|
|
18
|
+
shows its human-readable part, pure tool messages are skipped.
|
|
19
|
+
|
|
3
20
|
## 0.3.5
|
|
4
21
|
|
|
5
22
|
- Surface-aware conversation: turns steered from Telegram/the panel render
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.7"
|
|
@@ -233,6 +233,20 @@ class RichSink:
|
|
|
233
233
|
self.console.print(_pad(Text(f"{who}{tag}: {_clean(text)}", style=_BEE)))
|
|
234
234
|
self._nudge()
|
|
235
235
|
|
|
236
|
+
def consent_dismissed(self, note: str) -> None:
|
|
237
|
+
"""Liveness A: the pending consent was answered on ANOTHER surface
|
|
238
|
+
(e.g. relayed from Telegram) — the kernel park is over. Retire the
|
|
239
|
+
pinned prompt so the dock leaves `approve? y/n`: cancel the armed
|
|
240
|
+
Future (consent_pending() flips False, so the toolbar repaints out of
|
|
241
|
+
the consent state on the next _nudge) and print ONE note-style line
|
|
242
|
+
so the scrollback shows why the y/n prompt vanished."""
|
|
243
|
+
if self._consent is not None and not self._consent.done():
|
|
244
|
+
self._consent.cancel()
|
|
245
|
+
self._consent = None
|
|
246
|
+
self._consent_summary = ""
|
|
247
|
+
self.console.print(_pad(Text(_clean(note), style=_BEE)))
|
|
248
|
+
self._nudge()
|
|
249
|
+
|
|
236
250
|
def clear(self) -> None:
|
|
237
251
|
"""/clear: wipe the pane/screen + reset the session counters."""
|
|
238
252
|
self.console.clear()
|
|
@@ -134,6 +134,7 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
|
|
|
134
134
|
intel = None # assigned by _boot -- IntelService, or None (off/base-install/boot failure)
|
|
135
135
|
shadow = None # assigned by _boot -- ShadowGit, or None (git unavailable / boot failure)
|
|
136
136
|
watcher_task = None # assigned by _boot -- background watchfiles task, cancelled on exit
|
|
137
|
+
steer_task = None # assigned by _boot -- idle-steer poller (webbee.steer), cancelled on exit
|
|
137
138
|
|
|
138
139
|
def _cycle() -> None:
|
|
139
140
|
state["mode"] = next_mode(state["mode"])
|
|
@@ -269,21 +270,42 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
|
|
|
269
270
|
|
|
270
271
|
# A task for the agent.
|
|
271
272
|
_sink.user_echo(line)
|
|
273
|
+
await _run_turn(line)
|
|
274
|
+
return "continue"
|
|
275
|
+
|
|
276
|
+
async def _run_turn(line: str, surface: str = "") -> None:
|
|
277
|
+
"""ONE agent turn -- the SAME path for a typed line and an idle-steer
|
|
278
|
+
pickup (liveness v2 §B), which threads the queued item's origin
|
|
279
|
+
`surface` into the turn so the kernel stamps provenance start-path.
|
|
280
|
+
Only the echo differs at the call sites: user_echo for a typed line,
|
|
281
|
+
foreign_turn for a remote one."""
|
|
272
282
|
_sink.begin_turn()
|
|
283
|
+
kw = {"surface": surface} if surface else {}
|
|
273
284
|
try:
|
|
274
|
-
text = await agent.run(line, _sink, marathon=not once,
|
|
285
|
+
text = await agent.run(line, _sink, marathon=not once,
|
|
286
|
+
goal=(line if not once else ""), **kw)
|
|
275
287
|
except (KeyboardInterrupt, asyncio.CancelledError):
|
|
276
288
|
_sink.abort()
|
|
277
289
|
_sink.note("Interrupted.")
|
|
278
|
-
return
|
|
290
|
+
return
|
|
279
291
|
except Exception as e: # network/auth/etc — never crash the REPL
|
|
280
292
|
_sink.note(f"Error: {type(e).__name__}: {e}")
|
|
281
|
-
return
|
|
293
|
+
return
|
|
282
294
|
_sink.end_turn(text)
|
|
283
|
-
|
|
295
|
+
|
|
296
|
+
async def _steer_submit(text: str, surface: str) -> None:
|
|
297
|
+
"""webbee.steer hands a drained remote instruction here: render it as
|
|
298
|
+
the remote user's own line, then run it as a normal turn."""
|
|
299
|
+
_sink.foreign_turn(surface, "user", text)
|
|
300
|
+
await _run_turn(text, surface=surface)
|
|
301
|
+
|
|
302
|
+
def _cancel_background() -> None:
|
|
303
|
+
for _t in (watcher_task, steer_task):
|
|
304
|
+
if _t is not None:
|
|
305
|
+
_t.cancel()
|
|
284
306
|
|
|
285
307
|
async def _boot(s) -> None:
|
|
286
|
-
nonlocal _sink, agent, intel, watcher_task, shadow
|
|
308
|
+
nonlocal _sink, agent, intel, watcher_task, shadow, steer_task
|
|
287
309
|
_sink = s
|
|
288
310
|
# Cache git branch OFF the event loop (subprocess.run blocks it). Only
|
|
289
311
|
# /status reads it; recomputing it per input line froze the dock.
|
|
@@ -296,13 +318,19 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
|
|
|
296
318
|
# never a boot blocker (network down, no such session yet, etc.).
|
|
297
319
|
try:
|
|
298
320
|
from imperal_mcp.client import ImperalClient
|
|
299
|
-
from webbee.thread import fetch_recent_thread,
|
|
321
|
+
from webbee.thread import (conversational_text, fetch_recent_thread,
|
|
322
|
+
truncate_for_display)
|
|
300
323
|
_iid = await ImperalClient(cfg, token_provider).whoami()
|
|
301
324
|
_msgs = await fetch_recent_thread(cfg, token_provider, f"marathon-{_iid}-rboot")
|
|
325
|
+
_shown = 0
|
|
302
326
|
for _m in _msgs[-40:]:
|
|
327
|
+
_text = conversational_text(_m.get("content", ""))
|
|
328
|
+
if not _text:
|
|
329
|
+
continue # pure tool traffic -- mind-food, not conversation
|
|
303
330
|
_sink.foreign_turn(_m.get("surface", "terminal"), _m.get("role", ""),
|
|
304
|
-
truncate_for_display(
|
|
305
|
-
|
|
331
|
+
truncate_for_display(_text))
|
|
332
|
+
_shown += 1
|
|
333
|
+
if _shown:
|
|
306
334
|
_sink.note("— live —")
|
|
307
335
|
except Exception:
|
|
308
336
|
pass # replay is best-effort; never block boot
|
|
@@ -327,6 +355,16 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
|
|
|
327
355
|
except Exception:
|
|
328
356
|
shadow = None
|
|
329
357
|
agent = agent_factory(cfg, token_provider, workspace, state["mode"])
|
|
358
|
+
# Liveness v2 §B: idle-steer pickup. All poll/drain logic lives in
|
|
359
|
+
# webbee.steer -- this is wiring only: the sink's live turn state
|
|
360
|
+
# gates polling, the agent's session id (once a turn has run) is the
|
|
361
|
+
# gateway truth, and _steer_submit is the normal turn path.
|
|
362
|
+
from webbee import steer as _steer
|
|
363
|
+
steer_task = asyncio.ensure_future(_steer.poll_idle_steer(
|
|
364
|
+
cfg, token_provider, workspace=workspace, marathon=not once,
|
|
365
|
+
is_busy=lambda: bool(getattr(_sink, "is_busy", None) and _sink.is_busy()),
|
|
366
|
+
live_session_id=lambda: getattr(agent, "session_id", ""),
|
|
367
|
+
submit=_steer_submit))
|
|
330
368
|
|
|
331
369
|
if use_dock:
|
|
332
370
|
ok = False
|
|
@@ -363,8 +401,7 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
|
|
|
363
401
|
stop_turn=lambda: agent.stop(),
|
|
364
402
|
)
|
|
365
403
|
finally:
|
|
366
|
-
|
|
367
|
-
watcher_task.cancel()
|
|
404
|
+
_cancel_background()
|
|
368
405
|
except Exception:
|
|
369
406
|
ok = False
|
|
370
407
|
finally:
|
|
@@ -398,5 +435,4 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
|
|
|
398
435
|
if await _handle(line) == "exit":
|
|
399
436
|
return
|
|
400
437
|
finally:
|
|
401
|
-
|
|
402
|
-
watcher_task.cancel()
|
|
438
|
+
_cancel_background()
|