wcgw 3.0.1rc2__py3-none-any.whl → 3.0.1rc3__py3-none-any.whl

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.

Potentially problematic release.


This version of wcgw might be problematic. Click here for more details.

@@ -32,7 +32,8 @@ from ...types_ import (
32
32
  from ..encoder import EncoderDecoder
33
33
  from ..modes import BashCommandMode, FileEditMode, WriteIfEmptyMode
34
34
 
35
- PROMPT_CONST = "#" + "@wcgw@#"
35
+ PROMPT_CONST = "wcgw→" + " "
36
+ PROMPT_STATEMENT = "export GIT_PAGER=cat PAGER=cat PROMPT_COMMAND= PS1='wcgw→'' '"
36
37
  BASH_CLF_OUTPUT = Literal["repl", "pending"]
37
38
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
38
39
 
@@ -79,9 +80,7 @@ def get_tmpdir() -> str:
79
80
 
80
81
  def check_if_screen_command_available() -> bool:
81
82
  try:
82
- subprocess.run(
83
- ["screen", "-v"], capture_output=True, check=True, timeout=CONFIG.timeout
84
- )
83
+ subprocess.run(["screen", "-v"], capture_output=True, check=True, timeout=0.2)
85
84
  return True
86
85
  except (subprocess.CalledProcessError, FileNotFoundError):
87
86
  return False
@@ -100,7 +99,7 @@ def cleanup_all_screens_with_name(name: str, console: Console) -> None:
100
99
  capture_output=True,
101
100
  text=True,
102
101
  check=True,
103
- timeout=CONFIG.timeout,
102
+ timeout=0.2,
104
103
  )
105
104
  output = result.stdout
106
105
  except subprocess.CalledProcessError as e:
@@ -146,23 +145,21 @@ def start_shell(
146
145
  **os.environ,
147
146
  "PS1": PROMPT_CONST,
148
147
  "TMPDIR": get_tmpdir(),
149
- "TERM": "vt100",
148
+ "TERM": "xterm-256color",
150
149
  }
151
150
  try:
152
151
  shell = pexpect.spawn(
153
152
  cmd,
154
153
  env=overrideenv, # type: ignore[arg-type]
155
- echo=False,
154
+ echo=True,
156
155
  encoding="utf-8",
157
156
  timeout=CONFIG.timeout,
158
157
  cwd=initial_dir,
159
158
  codec_errors="backslashreplace",
160
159
  dimensions=(500, 160),
161
160
  )
162
- shell.sendline(
163
- f"export PROMPT_COMMAND= PS1={PROMPT_CONST}"
164
- ) # Unset prompt command to avoid interfering
165
- shell.expect(PROMPT_CONST, timeout=CONFIG.timeout)
161
+ shell.sendline(PROMPT_STATEMENT) # Unset prompt command to avoid interfering
162
+ shell.expect(PROMPT_CONST, timeout=0.2)
166
163
  except Exception as e:
167
164
  console.print(traceback.format_exc())
168
165
  console.log(f"Error starting shell: {e}. Retrying without rc ...")
@@ -170,13 +167,13 @@ def start_shell(
170
167
  shell = pexpect.spawn(
171
168
  "/bin/bash --noprofile --norc",
172
169
  env=overrideenv, # type: ignore[arg-type]
173
- echo=False,
170
+ echo=True,
174
171
  encoding="utf-8",
175
172
  timeout=CONFIG.timeout,
176
173
  codec_errors="backslashreplace",
177
174
  )
178
- shell.sendline(f"export PS1={PROMPT_CONST}")
179
- shell.expect(PROMPT_CONST, timeout=CONFIG.timeout)
175
+ shell.sendline(PROMPT_STATEMENT)
176
+ shell.expect(PROMPT_CONST, timeout=0.2)
180
177
 
181
178
  shellid = "wcgw." + time.strftime("%H%M%S")
182
179
  if over_screen:
@@ -184,20 +181,11 @@ def start_shell(
184
181
  raise ValueError("Screen command not available")
185
182
  # shellid is just hour, minute, second number
186
183
  shell.sendline(f"trap 'screen -X -S {shellid} quit' EXIT")
187
- shell.expect(PROMPT_CONST, timeout=CONFIG.timeout)
184
+ shell.expect(PROMPT_CONST, timeout=0.2)
188
185
 
189
186
  shell.sendline(f"screen -q -S {shellid} /bin/bash --noprofile --norc")
190
187
  shell.expect(PROMPT_CONST, timeout=CONFIG.timeout)
191
188
 
192
- shell.sendline("stty -icanon -echo")
193
- shell.expect(PROMPT_CONST, timeout=CONFIG.timeout)
194
-
195
- shell.sendline("set +o pipefail")
196
- shell.expect(PROMPT_CONST, timeout=CONFIG.timeout)
197
-
198
- shell.sendline("export GIT_PAGER=cat PAGER=cat")
199
- shell.expect(PROMPT_CONST, timeout=CONFIG.timeout)
200
-
201
189
  return shell, shellid
202
190
 
203
191
 
@@ -234,7 +222,9 @@ def requires_shell(
234
222
  ) -> Callable[Concatenate["BashState", P], R]:
235
223
  def wrapper(self: "BashState", /, *args: P.args, **kwargs: P.kwargs) -> R:
236
224
  if not self._shell_loading.is_set():
237
- if not self._shell_loading.wait(timeout=CONFIG.timeout):
225
+ if not self._shell_loading.wait(
226
+ timeout=CONFIG.timeout * 2
227
+ ): # Twice in worst case if screen fails
238
228
  raise RuntimeError("Shell initialization timeout")
239
229
 
240
230
  if self._shell_error:
@@ -278,7 +268,6 @@ class BashState:
278
268
  )
279
269
  self._mode = mode or Modes.wcgw
280
270
  self._whitelist_for_overwrite: set[str] = whitelist_for_overwrite or set()
281
- self._prompt = PROMPT_CONST
282
271
  self._bg_expect_thread: Optional[threading.Thread] = None
283
272
  self._bg_expect_thread_stop_event = threading.Event()
284
273
  self._shell = None
@@ -296,6 +285,7 @@ class BashState:
296
285
  if self._shell is not None:
297
286
  return
298
287
  self._init_shell()
288
+ self.run_bg_expect_thread()
299
289
  except Exception as e:
300
290
  self._shell_error = e
301
291
  finally:
@@ -308,18 +298,19 @@ class BashState:
308
298
  self, shell: "pexpect.spawn[str]", pattern: Any, timeout: Optional[float] = -1
309
299
  ) -> int:
310
300
  self.close_bg_expect_thread()
311
- return shell.expect(pattern, timeout)
301
+ output = shell.expect(pattern, timeout)
302
+ return output
312
303
 
313
304
  @requires_shell
314
305
  def send(self, shell: "pexpect.spawn[str]", s: str | bytes) -> int:
306
+ self.close_bg_expect_thread()
315
307
  output = shell.send(s)
316
- self.run_bg_expect_thread()
317
308
  return output
318
309
 
319
310
  @requires_shell
320
311
  def sendline(self, shell: "pexpect.spawn[str]", s: str | bytes) -> int:
312
+ self.close_bg_expect_thread()
321
313
  output = shell.sendline(s)
322
- self.run_bg_expect_thread()
323
314
  return output
324
315
 
325
316
  @property
@@ -329,6 +320,7 @@ class BashState:
329
320
 
330
321
  @requires_shell
331
322
  def sendintr(self, shell: "pexpect.spawn[str]") -> None:
323
+ self.close_bg_expect_thread()
332
324
  shell.sendintr()
333
325
 
334
326
  @property
@@ -336,8 +328,7 @@ class BashState:
336
328
  def before(self, shell: "pexpect.spawn[str]") -> Optional[str]:
337
329
  return shell.before
338
330
 
339
- @requires_shell
340
- def run_bg_expect_thread(self, shell: "pexpect.spawn[str]") -> None:
331
+ def run_bg_expect_thread(self) -> None:
341
332
  """
342
333
  Run background expect thread for handling shell interactions.
343
334
  """
@@ -346,11 +337,16 @@ class BashState:
346
337
  while True:
347
338
  if self._bg_expect_thread_stop_event.is_set():
348
339
  break
349
- output = shell.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=0.1)
340
+ if self._shell is None:
341
+ time.sleep(0.1)
342
+ continue
343
+ output = self._shell.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=0.1)
350
344
  if output == 0:
351
345
  break
352
346
 
353
- if self._bg_expect_thread:
347
+ if self._bg_expect_thread and self._bg_expect_thread.is_alive():
348
+ if not self._bg_expect_thread_stop_event.is_set():
349
+ return
354
350
  self.close_bg_expect_thread()
355
351
 
356
352
  self._bg_expect_thread = threading.Thread(
@@ -403,28 +399,20 @@ class BashState:
403
399
 
404
400
  def _ensure_env_and_bg_jobs(self) -> Optional[int]:
405
401
  # Do not add @requires_shell decorator here, as it will cause deadlock
406
-
407
402
  self.close_bg_expect_thread()
408
403
  assert self._shell is not None, "Bad state, shell is not initialized"
409
- if self._prompt != PROMPT_CONST:
410
- return None
411
404
  quick_timeout = 0.2 if not self.over_screen else 1
412
405
  # First reset the prompt in case venv was sourced or other reasons.
413
- self._shell.sendline(f"export PS1={self._prompt}")
414
- self._shell.expect(self._prompt, timeout=quick_timeout)
406
+ self._shell.sendline(PROMPT_STATEMENT)
407
+ self._shell.expect(PROMPT_CONST, timeout=quick_timeout)
415
408
  # Reset echo also if it was enabled
416
- self._shell.sendline("stty -icanon -echo")
417
- self._shell.expect(self._prompt, timeout=quick_timeout)
418
- self._shell.sendline("set +o pipefail")
419
- self._shell.expect(self._prompt, timeout=quick_timeout)
420
- self._shell.sendline("export GIT_PAGER=cat PAGER=cat")
421
- self._shell.expect(self._prompt, timeout=quick_timeout)
422
- self._shell.sendline("jobs | wc -l")
409
+ command = "jobs | wc -l"
410
+ self._shell.sendline(command)
423
411
  before = ""
424
412
  counts = 0
425
413
  while not _is_int(before): # Consume all previous output
426
414
  try:
427
- self._shell.expect(self._prompt, timeout=quick_timeout)
415
+ self._shell.expect(PROMPT_CONST, timeout=quick_timeout)
428
416
  except pexpect.TIMEOUT:
429
417
  self.console.print(f"Couldn't get exit code, before: {before}")
430
418
  raise
@@ -434,20 +422,18 @@ class BashState:
434
422
  before_val = str(before_val)
435
423
  assert isinstance(before_val, str)
436
424
  before_lines = render_terminal_output(before_val)
437
- before = "\n".join(before_lines).strip()
425
+ before = "\n".join(before_lines).replace(command, "").strip()
438
426
  counts += 1
439
427
  if counts > 100:
440
428
  raise ValueError(
441
429
  "Error in understanding shell output. This shouldn't happen, likely shell is in a bad state, please reset it"
442
430
  )
443
-
444
431
  try:
445
432
  return int(before)
446
433
  except ValueError:
447
434
  raise ValueError(f"Malformed output: {before}")
448
435
 
449
436
  def _init_shell(self) -> None:
450
- self._prompt = PROMPT_CONST
451
437
  self._state: Literal["repl"] | datetime.datetime = "repl"
452
438
  self._is_in_docker: Optional[str] = ""
453
439
  # Ensure self._cwd exists
@@ -474,13 +460,15 @@ class BashState:
474
460
  self.over_screen = False
475
461
 
476
462
  self._pending_output = ""
477
- self._ensure_env_and_bg_jobs()
463
+ try:
464
+ self._ensure_env_and_bg_jobs()
465
+ except ValueError as e:
466
+ self.console.log("Error while running _ensure_env_and_bg_jobs" + str(e))
478
467
 
479
468
  def set_pending(self, last_pending_output: str) -> None:
480
469
  if not isinstance(self._state, datetime.datetime):
481
470
  self._state = datetime.datetime.now()
482
471
  self._pending_output = last_pending_output
483
- self.run_bg_expect_thread()
484
472
 
485
473
  def set_repl(self) -> None:
486
474
  self._state = "repl"
@@ -505,12 +493,12 @@ class BashState:
505
493
 
506
494
  @property
507
495
  def prompt(self) -> str:
508
- return self._prompt
496
+ return PROMPT_CONST
509
497
 
510
498
  @requires_shell
511
499
  def update_cwd(self, shell: "pexpect.spawn[str]") -> str:
512
500
  shell.sendline("pwd")
513
- shell.expect(self._prompt, timeout=0.2)
501
+ shell.expect(PROMPT_CONST, timeout=0.2)
514
502
  before_val = shell.before
515
503
  if not isinstance(before_val, str):
516
504
  before_val = str(before_val)
@@ -637,9 +625,8 @@ def _incremental_text(text: str, last_pending_output: str) -> str:
637
625
  # text = render_terminal_output(text[-100_000:])
638
626
  text = text[-100_000:]
639
627
 
640
- last_pending_output_rendered_lines = render_terminal_output(last_pending_output)
641
- last_pending_output_rendered = "\n".join(last_pending_output_rendered_lines)
642
- last_rendered_lines = last_pending_output_rendered.split("\n")
628
+ last_rendered_lines = render_terminal_output(last_pending_output)
629
+ last_pending_output_rendered = "\n".join(last_rendered_lines)
643
630
  if not last_rendered_lines:
644
631
  return rstrip(render_terminal_output(text))
645
632
 
@@ -690,6 +677,27 @@ def execute_bash(
690
677
  bash_arg: BashCommand,
691
678
  max_tokens: Optional[int],
692
679
  timeout_s: Optional[float],
680
+ ) -> tuple[str, float]:
681
+ try:
682
+ output, cost = _execute_bash(bash_state, enc, bash_arg, max_tokens, timeout_s)
683
+
684
+ # Remove echo if it's a command
685
+ if isinstance(bash_arg.action, Command):
686
+ command = bash_arg.action.command.strip()
687
+ if output.startswith(command):
688
+ output = output[len(command) :]
689
+
690
+ finally:
691
+ bash_state.run_bg_expect_thread()
692
+ return output, cost
693
+
694
+
695
+ def _execute_bash(
696
+ bash_state: BashState,
697
+ enc: EncoderDecoder[int],
698
+ bash_arg: BashCommand,
699
+ max_tokens: Optional[int],
700
+ timeout_s: Optional[float],
693
701
  ) -> tuple[str, float]:
694
702
  try:
695
703
  is_interrupt = False
@@ -1,5 +1,4 @@
1
1
  import importlib
2
- import json
3
2
  import logging
4
3
  import os
5
4
  from typing import Any
@@ -9,7 +8,7 @@ import mcp_wcgw.types as types
9
8
  from mcp_wcgw.server import NotificationOptions, Server
10
9
  from mcp_wcgw.server.models import InitializationOptions
11
10
  from mcp_wcgw.types import Tool as ToolParam
12
- from pydantic import AnyUrl, ValidationError
11
+ from pydantic import AnyUrl
13
12
 
14
13
  from wcgw.client.modes import KTS
15
14
  from wcgw.client.tool_prompts import TOOL_PROMPTS
@@ -18,7 +17,13 @@ from ...types_ import (
18
17
  Initialize,
19
18
  )
20
19
  from ..bash_state.bash_state import CONFIG, BashState
21
- from ..tools import Context, default_enc, get_tool_output, which_tool_name
20
+ from ..tools import (
21
+ Context,
22
+ default_enc,
23
+ get_tool_output,
24
+ parse_tool_by_name,
25
+ which_tool_name,
26
+ )
22
27
 
23
28
  server = Server("wcgw")
24
29
 
@@ -104,20 +109,7 @@ async def handle_call_tool(
104
109
  raise ValueError("Missing arguments")
105
110
 
106
111
  tool_type = which_tool_name(name)
107
-
108
- try:
109
- tool_call = tool_type(**arguments)
110
- except ValidationError:
111
-
112
- def try_json(x: str) -> Any:
113
- if not isinstance(x, str):
114
- return x
115
- try:
116
- return json.loads(x)
117
- except json.JSONDecodeError:
118
- return x
119
-
120
- tool_call = tool_type(**{k: try_json(v) for k, v in arguments.items()})
112
+ tool_call = parse_tool_by_name(name, arguments)
121
113
 
122
114
  try:
123
115
  assert BASH_STATE
wcgw/client/tools.py CHANGED
@@ -10,6 +10,7 @@ from os.path import expanduser
10
10
  from pathlib import Path
11
11
  from tempfile import NamedTemporaryFile
12
12
  from typing import (
13
+ Any,
13
14
  Callable,
14
15
  Literal,
15
16
  Optional,
@@ -21,7 +22,7 @@ from typing import (
21
22
  from openai.types.chat import (
22
23
  ChatCompletionMessageParam,
23
24
  )
24
- from pydantic import BaseModel, TypeAdapter
25
+ from pydantic import BaseModel, TypeAdapter, ValidationError
25
26
  from syntax_checker import check_syntax
26
27
 
27
28
  from wcgw.client.bash_state.bash_state import get_status
@@ -549,6 +550,23 @@ def which_tool_name(name: str) -> Type[TOOLS]:
549
550
  raise ValueError(f"Unknown tool name: {name}")
550
551
 
551
552
 
553
+ def parse_tool_by_name(name: str, arguments: dict[str, Any]) -> TOOLS:
554
+ tool_type = which_tool_name(name)
555
+ try:
556
+ return tool_type(**arguments)
557
+ except ValidationError:
558
+
559
+ def try_json(x: str) -> Any:
560
+ if not isinstance(x, str):
561
+ return x
562
+ try:
563
+ return json.loads(x)
564
+ except json.JSONDecodeError:
565
+ return x
566
+
567
+ return tool_type(**{k: try_json(v) for k, v in arguments.items()})
568
+
569
+
552
570
  TOOL_CALLS: list[TOOLS] = []
553
571
 
554
572
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcgw
3
- Version: 3.0.1rc2
3
+ Version: 3.0.1rc3
4
4
  Summary: Shell and coding agent on claude and chatgpt
5
5
  Project-URL: Homepage, https://github.com/rusiaaman/wcgw
6
6
  Author-email: Aman Rusia <gapypi@arcfu.com>
@@ -7,14 +7,14 @@ wcgw/client/diff-instructions.txt,sha256=tmJ9Fu9XdO_72lYXQQNY9RZyx91bjxrXJf9d_KB
7
7
  wcgw/client/memory.py,sha256=8LdYsOhvCOoC1kfvDr85kNy07WnhPMvE6B2FRM2w85Y,2902
8
8
  wcgw/client/modes.py,sha256=dBkiMNQevTFNkhplrDsDuSIyeU-wLmAIfAa9Dqowvk8,10387
9
9
  wcgw/client/tool_prompts.py,sha256=j82aYnlr1pcinsOYitSrlyYz4-9K3KcqnAfGqot21NQ,4158
10
- wcgw/client/tools.py,sha256=eXAGPKm7ViG2OgF1m_yLTPehtmkhQFWDoiLEkV7fOMQ,22660
11
- wcgw/client/bash_state/bash_state.py,sha256=dNCx2KRQSQHYihTsfIqtQNsd8j27CoSMH4I026Cz8pU,29308
10
+ wcgw/client/tools.py,sha256=caj10NHeK5tZ5Gi0L_oXzPcKBycgXeNaLEbXAzYTpqo,23170
11
+ wcgw/client/bash_state/bash_state.py,sha256=2cxzYN6x-vEJ4oasrCgoRyVQKpARKONcaNDLfLPDTG8,29476
12
12
  wcgw/client/encoder/__init__.py,sha256=Y-8f43I6gMssUCWpX5rLYiAFv3D-JPRs4uNEejPlke8,1514
13
13
  wcgw/client/file_ops/diff_edit.py,sha256=sIwXSSkWYff_Dp3oHfefqtSuFqLrxbhKvzkCoLuLqDE,18679
14
14
  wcgw/client/file_ops/search_replace.py,sha256=Napa7IWaYPGMNdttunKyRDkb90elZE7r23B_o_htRxo,5585
15
15
  wcgw/client/mcp_server/Readme.md,sha256=2Z88jj1mf9daYGW1CWaldcJ0moy8owDumhR2glBY3A8,109
16
16
  wcgw/client/mcp_server/__init__.py,sha256=mm7xhBIPwJpRT3u-Qsj4cKVMpVyucJoKRlbMP_gRRB0,343
17
- wcgw/client/mcp_server/server.py,sha256=aF8SLaCsPisQsQ0fOUOpTfP2S4EGEugaLupjCyhHA8o,5371
17
+ wcgw/client/mcp_server/server.py,sha256=ayK6qbzCveoQW7RO80m10cAIS3m-hvxzd15XhjiyxmE,5055
18
18
  wcgw/client/repo_ops/display_tree.py,sha256=E5q9mrGBb57NyvudSmRIG-fj4FUqupbzjmARpX8X0XY,4166
19
19
  wcgw/client/repo_ops/path_prob.py,sha256=SWf0CDn37rtlsYRQ51ufSxay-heaQoVIhr1alB9tZ4M,2144
20
20
  wcgw/client/repo_ops/paths_model.vocab,sha256=M1pXycYDQehMXtpp-qAgU7rtzeBbCOiJo4qcYFY0kqk,315087
@@ -25,7 +25,7 @@ wcgw/relay/serve.py,sha256=Ofq6PjW3zVVA2-9MVviGRiUESTD3sXb-482Q4RV13q8,8664
25
25
  wcgw/relay/static/privacy.txt,sha256=s9qBdbx2SexCpC_z33sg16TptmAwDEehMCLz4L50JLc,529
26
26
  wcgw_cli/__init__.py,sha256=TNxXsTPgb52OhakIda9wTRh91cqoBqgQRx5TxjzQQFU,21
27
27
  wcgw_cli/__main__.py,sha256=wcCrL4PjG51r5wVKqJhcoJPTLfHW0wNbD31DrUN0MWI,28
28
- wcgw_cli/anthropic_client.py,sha256=hpeqfLxYgfJpLgYmCRvE9r1Iu0ubGiBqmgEFzwKIvCM,18966
28
+ wcgw_cli/anthropic_client.py,sha256=2QLFBLbMeQuixF7Pz9j_hINHTG1CF9IYQZeri7zFuF0,18964
29
29
  wcgw_cli/cli.py,sha256=-7FBe_lahKyUOhf65iurTA1M1gXXXAiT0OVKQVcZKKo,948
30
30
  wcgw_cli/openai_client.py,sha256=oMFAaOkvXQtOY7choylVRJfaF2SnWvRc02ygQhlhVqY,15995
31
31
  wcgw_cli/openai_utils.py,sha256=xGOb3W5ALrIozV7oszfGYztpj0FnXdD7jAxm5lEIVKY,2439
@@ -51,8 +51,8 @@ mcp_wcgw/shared/memory.py,sha256=dBsOghxHz8-tycdSVo9kSujbsC8xb_tYsGmuJobuZnw,281
51
51
  mcp_wcgw/shared/progress.py,sha256=ymxOsb8XO5Mhlop7fRfdbmvPodANj7oq6O4dD0iUcnw,1048
52
52
  mcp_wcgw/shared/session.py,sha256=e44a0LQOW8gwdLs9_DE9oDsxqW2U8mXG3d5KT95bn5o,10393
53
53
  mcp_wcgw/shared/version.py,sha256=d2LZii-mgsPIxpshjkXnOTUmk98i0DT4ff8VpA_kAvE,111
54
- wcgw-3.0.1rc2.dist-info/METADATA,sha256=J7O32iQuFetYvpv1pNkdHMsHbeV7o-3HYd6QNUzMszk,13005
55
- wcgw-3.0.1rc2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
- wcgw-3.0.1rc2.dist-info/entry_points.txt,sha256=vd3tj1_Kzfp55LscJ8-6WFMM5hm9cWTfNGFCrWBnH3Q,124
57
- wcgw-3.0.1rc2.dist-info/licenses/LICENSE,sha256=BvY8xqjOfc3X2qZpGpX3MZEmF-4Dp0LqgKBbT6L_8oI,11142
58
- wcgw-3.0.1rc2.dist-info/RECORD,,
54
+ wcgw-3.0.1rc3.dist-info/METADATA,sha256=ymiydLYH56IAV_8FKq98BrGf-tt7Vwumtc8nUBvzs50,13005
55
+ wcgw-3.0.1rc3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
+ wcgw-3.0.1rc3.dist-info/entry_points.txt,sha256=vd3tj1_Kzfp55LscJ8-6WFMM5hm9cWTfNGFCrWBnH3Q,124
57
+ wcgw-3.0.1rc3.dist-info/licenses/LICENSE,sha256=BvY8xqjOfc3X2qZpGpX3MZEmF-4Dp0LqgKBbT6L_8oI,11142
58
+ wcgw-3.0.1rc3.dist-info/RECORD,,
@@ -35,7 +35,7 @@ from wcgw.client.tools import (
35
35
  default_enc,
36
36
  get_tool_output,
37
37
  initialize,
38
- which_tool_name,
38
+ parse_tool_by_name,
39
39
  )
40
40
 
41
41
 
@@ -379,9 +379,9 @@ def loop(
379
379
  tool_input = str(tc["input"])
380
380
  tool_id = str(tc["id"])
381
381
 
382
- tool_parsed = which_tool_name(
383
- tool_name
384
- ).model_validate_json(tool_input)
382
+ tool_parsed = parse_tool_by_name(
383
+ tool_name, json.loads(tool_input)
384
+ )
385
385
 
386
386
  system_console.print(
387
387
  f"\n---------------------------------------\n# Assistant invoked tool: {tool_parsed}"