webbee 0.3.9__tar.gz → 0.3.10__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 (85) hide show
  1. {webbee-0.3.9 → webbee-0.3.10}/CHANGELOG.md +6 -0
  2. {webbee-0.3.9 → webbee-0.3.10}/PKG-INFO +1 -1
  3. {webbee-0.3.9 → webbee-0.3.10}/pyproject.toml +1 -1
  4. webbee-0.3.10/src/webbee/__init__.py +1 -0
  5. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/repl.py +10 -7
  6. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/session.py +6 -1
  7. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/steer.py +11 -6
  8. {webbee-0.3.9 → webbee-0.3.10}/tests/test_repl.py +7 -4
  9. {webbee-0.3.9 → webbee-0.3.10}/tests/test_session.py +21 -0
  10. {webbee-0.3.9 → webbee-0.3.10}/tests/test_steer.py +51 -7
  11. webbee-0.3.9/src/webbee/__init__.py +0 -1
  12. {webbee-0.3.9 → webbee-0.3.10}/.github/workflows/publish.yml +0 -0
  13. {webbee-0.3.9 → webbee-0.3.10}/.gitignore +0 -0
  14. {webbee-0.3.9 → webbee-0.3.10}/LICENSE +0 -0
  15. {webbee-0.3.9 → webbee-0.3.10}/README.md +0 -0
  16. {webbee-0.3.9 → webbee-0.3.10}/install.sh +0 -0
  17. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/account.py +0 -0
  18. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/banner_art.py +0 -0
  19. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/boot.py +0 -0
  20. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/checkpoints.py +0 -0
  21. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/cli.py +0 -0
  22. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/clipboard.py +0 -0
  23. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/coding_context.py +0 -0
  24. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/commands.py +0 -0
  25. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/config.py +0 -0
  26. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/consent.py +0 -0
  27. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/details.py +0 -0
  28. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/events.py +0 -0
  29. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/frames.py +0 -0
  30. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/__init__.py +0 -0
  31. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/chunker.py +0 -0
  32. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/embed.py +0 -0
  33. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/graph.py +0 -0
  34. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/indexer.py +0 -0
  35. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/models.py +0 -0
  36. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/query.py +0 -0
  37. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/service.py +0 -0
  38. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/store.py +0 -0
  39. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/vectors.py +0 -0
  40. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/intel/watch.py +0 -0
  41. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/output_pane.py +0 -0
  42. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/remote.py +0 -0
  43. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/render.py +0 -0
  44. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/repo.py +0 -0
  45. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/sessions.py +0 -0
  46. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/stream.py +0 -0
  47. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/thread.py +0 -0
  48. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/tokens.py +0 -0
  49. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/tools.py +0 -0
  50. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/tui.py +0 -0
  51. {webbee-0.3.9 → webbee-0.3.10}/src/webbee/update.py +0 -0
  52. {webbee-0.3.9 → webbee-0.3.10}/tests/__init__.py +0 -0
  53. {webbee-0.3.9 → webbee-0.3.10}/tests/test_account.py +0 -0
  54. {webbee-0.3.9 → webbee-0.3.10}/tests/test_checkpoints.py +0 -0
  55. {webbee-0.3.9 → webbee-0.3.10}/tests/test_cli.py +0 -0
  56. {webbee-0.3.9 → webbee-0.3.10}/tests/test_clipboard.py +0 -0
  57. {webbee-0.3.9 → webbee-0.3.10}/tests/test_commands.py +0 -0
  58. {webbee-0.3.9 → webbee-0.3.10}/tests/test_config.py +0 -0
  59. {webbee-0.3.9 → webbee-0.3.10}/tests/test_cpc_contract_stable.py +0 -0
  60. {webbee-0.3.9 → webbee-0.3.10}/tests/test_details.py +0 -0
  61. {webbee-0.3.9 → webbee-0.3.10}/tests/test_events.py +0 -0
  62. {webbee-0.3.9 → webbee-0.3.10}/tests/test_freeze_fix.py +0 -0
  63. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_chunker.py +0 -0
  64. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_embed.py +0 -0
  65. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_graph.py +0 -0
  66. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_indexer.py +0 -0
  67. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_no_numpy.py +0 -0
  68. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_query.py +0 -0
  69. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_service.py +0 -0
  70. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_store.py +0 -0
  71. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_vectors.py +0 -0
  72. {webbee-0.3.9 → webbee-0.3.10}/tests/test_intel_watch.py +0 -0
  73. {webbee-0.3.9 → webbee-0.3.10}/tests/test_marathon.py +0 -0
  74. {webbee-0.3.9 → webbee-0.3.10}/tests/test_packaging.py +0 -0
  75. {webbee-0.3.9 → webbee-0.3.10}/tests/test_render.py +0 -0
  76. {webbee-0.3.9 → webbee-0.3.10}/tests/test_repo.py +0 -0
  77. {webbee-0.3.9 → webbee-0.3.10}/tests/test_sessions.py +0 -0
  78. {webbee-0.3.9 → webbee-0.3.10}/tests/test_stream.py +0 -0
  79. {webbee-0.3.9 → webbee-0.3.10}/tests/test_thread.py +0 -0
  80. {webbee-0.3.9 → webbee-0.3.10}/tests/test_tokens.py +0 -0
  81. {webbee-0.3.9 → webbee-0.3.10}/tests/test_tools.py +0 -0
  82. {webbee-0.3.9 → webbee-0.3.10}/tests/test_tui.py +0 -0
  83. {webbee-0.3.9 → webbee-0.3.10}/tests/test_tui_hardening.py +0 -0
  84. {webbee-0.3.9 → webbee-0.3.10}/tests/test_update.py +0 -0
  85. {webbee-0.3.9 → webbee-0.3.10}/tests/test_version.py +0 -0
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.10
4
+
5
+ - Remote instructions picked up by an idle terminal now carry a dedup id, so
6
+ an at-least-once delivery can never run — or bill — the same instruction
7
+ twice.
8
+
3
9
  ## 0.3.9
4
10
 
5
11
  - Internal restructure: the session engine was split into focused modules
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webbee
3
- Version: 0.3.9
3
+ Version: 0.3.10
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.9"
3
+ version = "0.3.10"
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.10"
@@ -214,14 +214,16 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
214
214
  await _run_turn(line)
215
215
  return "continue"
216
216
 
217
- async def _run_turn(line: str, surface: str = "") -> None:
217
+ async def _run_turn(line: str, surface: str = "", steer_iid: str = "") -> None:
218
218
  """ONE agent turn -- the SAME path for a typed line and an idle-steer
219
219
  pickup (liveness v2 §B), which threads the queued item's origin
220
- `surface` into the turn so the kernel stamps provenance start-path.
221
- Only the echo differs at the call sites: user_echo for a typed line,
222
- foreign_turn for a remote one."""
220
+ `surface` (provenance) and dedup `steer_iid` (kernel dedup ring) into
221
+ the turn start-path. Only the echo differs at the call sites:
222
+ user_echo for a typed line, foreign_turn for a remote one."""
223
223
  _sink.begin_turn()
224
224
  kw = {"surface": surface} if surface else {}
225
+ if steer_iid:
226
+ kw["steer_iid"] = steer_iid
225
227
  try:
226
228
  text = await agent.run(line, _sink, marathon=not once,
227
229
  goal=(line if not once else ""), **kw)
@@ -236,11 +238,12 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
236
238
  return
237
239
  _sink.end_turn(text)
238
240
 
239
- async def _steer_submit(text: str, surface: str) -> None:
241
+ async def _steer_submit(text: str, surface: str, steer_iid: str = "") -> None:
240
242
  """webbee.steer hands a drained remote instruction here: render it as
241
- the remote user's own line, then run it as a normal turn."""
243
+ the remote user's own line, then run it as a normal turn (carrying the
244
+ item's dedup iid so the kernel can drop an at-least-once twin)."""
242
245
  _sink.foreign_turn(surface, "user", text)
243
- await _run_turn(text, surface=surface)
246
+ await _run_turn(text, surface=surface, steer_iid=steer_iid)
244
247
 
245
248
  def _cancel_background() -> None:
246
249
  for _t in (watcher_task, steer_task):
@@ -45,7 +45,7 @@ class AgentSession:
45
45
  return {"Authorization": f"Bearer {token}"}
46
46
 
47
47
  async def run(self, task: str, sink, *, marathon: bool = False, goal: str = "",
48
- surface: str = "") -> str:
48
+ surface: str = "", steer_iid: str = "") -> str:
49
49
  import httpx
50
50
 
51
51
  from webbee.tools import LocalToolExecutor
@@ -72,6 +72,11 @@ class AgentSession:
72
72
  # [surface] tags). Additive-only -- a typed turn's body is
73
73
  # byte-identical to before.
74
74
  body["surface"] = surface
75
+ if steer_iid:
76
+ # steer-iid-dedup: a pickup also carries the queued item's dedup id
77
+ # so the kernel's ring can drop an at-least-once twin. Same
78
+ # additive-only contract -- a typed turn has none, key omitted.
79
+ body["steer_iid"] = steer_iid
75
80
  if marathon:
76
81
  body["marathon"] = True
77
82
  body["goal"] = goal
@@ -58,8 +58,9 @@ async def poll_idle_steer(cfg, token_provider, *, workspace: str, is_busy,
58
58
  live_session_id=lambda: "",
59
59
  interval: float = _POLL_INTERVAL) -> None:
60
60
  """Run forever (until cancelled): every ~`interval`s of idle time, drain
61
- the pending-steer queue and hand the FIRST item to `submit(text, surface)`
62
- -- the repl's normal turn path. Seams (all injected by the repl wiring):
61
+ the pending-steer queue and hand the FIRST item to
62
+ `submit(text, surface, steer_iid)` -- the repl's normal turn path. Seams
63
+ (all injected by the repl wiring):
63
64
  * is_busy() -- sync; True while a turn is running. Checked BEFORE
64
65
  the destructive fetch (never drain mid-turn) and
65
66
  AGAIN right before submit (a locally-typed line
@@ -67,9 +68,12 @@ async def poll_idle_steer(cfg, token_provider, *, workspace: str, is_busy,
67
68
  backlog instead of being lost).
68
69
  * live_session_id()-- sync; the agent's gateway-issued session id once a
69
70
  turn has run ("" before). Wins over derivation.
70
- * submit(text, surface) -- async; renders the remote line and runs the
71
- turn. Runs INSIDE this task, so polling is
72
- naturally paused for the turn's whole duration."""
71
+ * submit(text, surface, steer_iid) -- async; renders the remote line and
72
+ runs the turn. Runs INSIDE this task, so polling
73
+ is naturally paused for the turn's whole duration.
74
+ steer_iid = the queued item's dedup id ("" on an
75
+ older gateway), threaded into the turn POST so the
76
+ kernel's dedup ring can drop an at-least-once twin."""
73
77
  from webbee.thread import fetch_pending_steer
74
78
  derived = ""
75
79
  backlog: deque = deque()
@@ -97,7 +101,8 @@ async def poll_idle_steer(cfg, token_provider, *, workspace: str, is_busy,
97
101
  if is_busy():
98
102
  backlog.appendleft(item) # a local line won the race -- defer, don't drop
99
103
  continue
100
- await submit(text, str(item.get("surface") or "telegram"))
104
+ await submit(text, str(item.get("surface") or "telegram"),
105
+ str(item.get("iid") or ""))
101
106
  if _cancel_absorbed():
102
107
  raise asyncio.CancelledError
103
108
  except asyncio.CancelledError:
@@ -590,10 +590,11 @@ class SurfaceAgent(FakeAgent):
590
590
  with a typed turn (the plain fallback read_line is sync)."""
591
591
  session_id = "marathon-user-1-rab12cd34ef56"
592
592
 
593
- async def run(self, task, sink, *, marathon=False, goal="", surface=""):
593
+ async def run(self, task, sink, *, marathon=False, goal="", surface="",
594
+ steer_iid=""):
594
595
  self.tasks.append(task)
595
596
  self.runs.append({"task": task, "marathon": marathon, "goal": goal,
596
- "surface": surface})
597
+ "surface": surface, "steer_iid": steer_iid})
597
598
  await asyncio.sleep(0)
598
599
  return f"answer:{task}"
599
600
 
@@ -606,7 +607,7 @@ def test_steer_pickup_renders_remote_line_and_runs_tagged_turn(monkeypatch):
606
607
  marathon=True, live_session_id=lambda: "", **kw):
607
608
  captured["marathon"] = marathon
608
609
  captured["live_sid"] = live_session_id()
609
- await submit("push the fix", "telegram")
610
+ await submit("push the fix", "telegram", "iid-42")
610
611
 
611
612
  monkeypatch.setattr(SP, "poll_idle_steer", spy_poller)
612
613
 
@@ -617,9 +618,11 @@ def test_steer_pickup_renders_remote_line_and_runs_tagged_turn(monkeypatch):
617
618
  sink, agent = _run(read_line=_lines("hello", "again", "/exit"), agent=agent)
618
619
  # the remote user's line renders tagged with its origin surface...
619
620
  assert ("telegram", "user", "push the fix") in getattr(sink, "foreign", [])
620
- # ...and the SAME turn path ran it, threading surface into the turn kwargs
621
+ # ...and the SAME turn path ran it, threading surface + the item's dedup
622
+ # iid into the turn kwargs (steer-iid-dedup pickup path)
621
623
  steer_runs = [r for r in agent.runs if r["task"] == "push the fix"]
622
624
  assert steer_runs and steer_runs[0]["surface"] == "telegram"
625
+ assert steer_runs[0]["steer_iid"] == "iid-42"
623
626
  assert steer_runs[0]["marathon"] is True # normal marathon turn
624
627
  # a picked-up turn ends like any other -- end_turn fired, dock leaves busy
625
628
  assert "answer:push the fix" in sink.turns
@@ -961,3 +961,24 @@ def test_run_omits_surface_key_for_plain_typed_turns(monkeypatch):
961
961
  # to before -- no `surface` key at all.
962
962
  body = _run_turn_capture_body(monkeypatch)
963
963
  assert "surface" not in body
964
+
965
+
966
+ def test_run_threads_steer_iid_into_session_post_body(monkeypatch):
967
+ # steer-iid-dedup pickup path: a picked-up remote instruction carries the
968
+ # queue entry's dedup id so the kernel ring can drop an at-least-once twin.
969
+ body = _run_turn_capture_body(monkeypatch, surface="telegram", steer_iid="iid-42")
970
+ assert body["steer_iid"] == "iid-42"
971
+ assert body["surface"] == "telegram"
972
+
973
+
974
+ def test_run_omits_steer_iid_key_for_plain_typed_turns(monkeypatch):
975
+ # A typed turn has no dedup id -- key omitted, body byte-identical to today.
976
+ body = _run_turn_capture_body(monkeypatch)
977
+ assert "steer_iid" not in body
978
+
979
+
980
+ def test_run_omits_steer_iid_key_when_pickup_item_had_none(monkeypatch):
981
+ # Older gateway: /pending-steer items without `iid` -> "" -> key omitted.
982
+ body = _run_turn_capture_body(monkeypatch, surface="telegram", steer_iid="")
983
+ assert "steer_iid" not in body
984
+ assert body["surface"] == "telegram"
@@ -85,7 +85,7 @@ def test_poll_submits_first_item_with_surface(monkeypatch):
85
85
  import webbee.thread as TH
86
86
  monkeypatch.setattr(TH, "fetch_pending_steer", fake_fetch)
87
87
 
88
- async def submit(text, surface):
88
+ async def submit(text, surface, steer_iid=""):
89
89
  submitted.append((text, surface))
90
90
 
91
91
  _drive(SP.poll_idle_steer(_Cfg(), _tp, workspace=".", is_busy=lambda: False,
@@ -97,6 +97,50 @@ def test_poll_submits_first_item_with_surface(monkeypatch):
97
97
  assert polled[0] == "marathon-user-1-rlive"
98
98
 
99
99
 
100
+ def test_poll_passes_item_iid_to_submit(monkeypatch):
101
+ # steer-iid-dedup pickup path: /pending-steer items carry the queue entry's
102
+ # `iid`; the poller must hand it to submit so the turn POST carries
103
+ # steer_iid and the kernel's dedup ring can drop an at-least-once twin.
104
+ submitted = []
105
+
106
+ async def fake_fetch(cfg, tp, session_id):
107
+ return [{"text": "push the fix", "surface": "telegram", "ts": 1,
108
+ "iid": "iid-42"}]
109
+
110
+ import webbee.thread as TH
111
+ monkeypatch.setattr(TH, "fetch_pending_steer", fake_fetch)
112
+
113
+ async def submit(text, surface, steer_iid=""):
114
+ submitted.append((text, surface, steer_iid))
115
+
116
+ _drive(SP.poll_idle_steer(_Cfg(), _tp, workspace=".", is_busy=lambda: False,
117
+ submit=submit, live_session_id=lambda: "marathon-u-r1",
118
+ interval=0.01),
119
+ until=lambda: submitted)
120
+ assert submitted[0] == ("push the fix", "telegram", "iid-42")
121
+
122
+
123
+ def test_poll_item_without_iid_submits_empty_iid(monkeypatch):
124
+ # An older gateway's items carry no `iid` -- submit gets "" (the turn POST
125
+ # then omits steer_iid entirely), never a crash.
126
+ submitted = []
127
+
128
+ async def fake_fetch(cfg, tp, session_id):
129
+ return [{"text": "resume", "surface": "telegram", "ts": 2}]
130
+
131
+ import webbee.thread as TH
132
+ monkeypatch.setattr(TH, "fetch_pending_steer", fake_fetch)
133
+
134
+ async def submit(text, surface, steer_iid=""):
135
+ submitted.append((text, surface, steer_iid))
136
+
137
+ _drive(SP.poll_idle_steer(_Cfg(), _tp, workspace=".", is_busy=lambda: False,
138
+ submit=submit, live_session_id=lambda: "marathon-u-r1",
139
+ interval=0.01),
140
+ until=lambda: submitted)
141
+ assert submitted[0] == ("resume", "telegram", "")
142
+
143
+
100
144
  def test_poll_no_items_never_submits(monkeypatch):
101
145
  polled = []
102
146
  submitted = []
@@ -108,7 +152,7 @@ def test_poll_no_items_never_submits(monkeypatch):
108
152
  import webbee.thread as TH
109
153
  monkeypatch.setattr(TH, "fetch_pending_steer", fake_fetch)
110
154
 
111
- async def submit(text, surface):
155
+ async def submit(text, surface, steer_iid=""):
112
156
  submitted.append((text, surface))
113
157
 
114
158
  _drive(SP.poll_idle_steer(_Cfg(), _tp, workspace=".", is_busy=lambda: False,
@@ -132,7 +176,7 @@ def test_poll_silent_on_network_error_and_recovers(monkeypatch):
132
176
  import webbee.thread as TH
133
177
  monkeypatch.setattr(TH, "fetch_pending_steer", flaky_fetch)
134
178
 
135
- async def submit(text, surface):
179
+ async def submit(text, surface, steer_iid=""):
136
180
  submitted.append((text, surface))
137
181
 
138
182
  # No exception escapes the poller; the tick after the blip succeeds.
@@ -156,7 +200,7 @@ def test_poll_never_fetches_while_busy(monkeypatch):
156
200
  import webbee.thread as TH
157
201
  monkeypatch.setattr(TH, "fetch_pending_steer", fake_fetch)
158
202
 
159
- async def submit(text, surface):
203
+ async def submit(text, surface, steer_iid=""):
160
204
  submitted.append((text, surface))
161
205
 
162
206
  ticks = {"n": 0}
@@ -192,7 +236,7 @@ def test_multi_item_drain_runs_in_order_one_per_tick_nothing_lost(monkeypatch):
192
236
  import webbee.thread as TH
193
237
  monkeypatch.setattr(TH, "fetch_pending_steer", fake_fetch)
194
238
 
195
- async def submit(text, surface):
239
+ async def submit(text, surface, steer_iid=""):
196
240
  submitted.append((text, surface))
197
241
 
198
242
  # The gateway drain returns each item exactly ONCE -- items 2..n of a batch
@@ -221,7 +265,7 @@ def test_item_deferred_not_lost_when_turn_starts_mid_tick(monkeypatch):
221
265
  def is_busy():
222
266
  return busy_answers.pop(0) if busy_answers else False
223
267
 
224
- async def submit(text, surface):
268
+ async def submit(text, surface, steer_iid=""):
225
269
  submitted.append((text, surface))
226
270
 
227
271
  _drive(SP.poll_idle_steer(_Cfg(), _tp, workspace=".", is_busy=is_busy,
@@ -245,7 +289,7 @@ def test_poller_exits_when_submitted_turn_absorbs_the_cancel(monkeypatch):
245
289
  import webbee.thread as TH
246
290
  monkeypatch.setattr(TH, "fetch_pending_steer", fake_fetch)
247
291
 
248
- async def swallowing_submit(text, surface):
292
+ async def swallowing_submit(text, surface, steer_iid=""):
249
293
  in_submit.set()
250
294
  try:
251
295
  await asyncio.sleep(3600)
@@ -1 +0,0 @@
1
- __version__ = "0.3.9"
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