monkeybot-cli 0.8.0__tar.gz → 0.9.0__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 (89) hide show
  1. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/PKG-INFO +2 -2
  2. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/pyproject.toml +2 -2
  3. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/chat_tool_display.py +1 -1
  4. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/chat_tui.py +3 -3
  5. monkeybot_cli-0.9.0/src/monkeybot_cli/commands/trace.py +174 -0
  6. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/main.py +2 -1
  7. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/providers.py +0 -2
  8. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/browser/SKILL.md +3 -2
  9. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/monkeybot.example.yaml +8 -27
  10. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_tui.py +12 -3
  11. monkeybot_cli-0.9.0/tests/test_trace_cmd.py +85 -0
  12. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/uv.lock +9 -91
  13. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/.gitignore +0 -0
  14. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/skills/monkeybot/SKILL.md +0 -0
  15. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/skills/monkeybot/references/config-sections.md +0 -0
  16. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/__init__.py +0 -0
  17. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/chat_file_index.py +0 -0
  18. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/chat_local_shell.py +0 -0
  19. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/chat_renderer.py +0 -0
  20. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/chat_session.py +0 -0
  21. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/chat_status_bar.py +0 -0
  22. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/chat_theme.py +0 -0
  23. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/chat_tui_widgets.py +0 -0
  24. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/commands/__init__.py +0 -0
  25. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/commands/chat.py +0 -0
  26. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/commands/doctor.py +0 -0
  27. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/commands/loop.py +0 -0
  28. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/commands/new.py +0 -0
  29. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/commands/refresh.py +0 -0
  30. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/commands/run_cmd.py +0 -0
  31. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/commands/talk.py +0 -0
  32. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/commands/validate.py +0 -0
  33. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/compat.py +0 -0
  34. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/config_resolve.py +0 -0
  35. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/exit_commands.py +0 -0
  36. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/extras_catalog.py +0 -0
  37. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/gateway_health.py +0 -0
  38. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/opensandbox_lifecycle.py +0 -0
  39. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/output.py +0 -0
  40. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/process_tree.py +0 -0
  41. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/realtime/__init__.py +0 -0
  42. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/realtime/audio_io.py +0 -0
  43. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/realtime/client.py +0 -0
  44. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/realtime/gateway_manager.py +0 -0
  45. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/realtime/push_to_talk.py +0 -0
  46. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/realtime/session.py +0 -0
  47. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/realtime/session_controller.py +0 -0
  48. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/realtime/talk_ui.py +0 -0
  49. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/realtime/wire_encode.py +0 -0
  50. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/runtime_python.py +0 -0
  51. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold.py +0 -0
  52. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/AGENT.md +0 -0
  53. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/Dockerfile +0 -0
  54. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/__init__.py +0 -0
  55. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/command_allowlist.yaml +0 -0
  56. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/dockerignore +0 -0
  57. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/env.example +0 -0
  58. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/goal/SKILL.md +0 -0
  59. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/image-generator/SKILL.md +0 -0
  60. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/image-generator/generate_image.py +0 -0
  61. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/loop/SKILL.md +0 -0
  62. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/mcp.json +0 -0
  63. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/opensandbox.docker.toml +0 -0
  64. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/otel-collector.example.yaml +0 -0
  65. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/scaffold_defaults/permissions.yaml +0 -0
  66. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/session_controller.py +0 -0
  67. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/src/monkeybot_cli/terminal_markdown.py +0 -0
  68. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_continue.py +0 -0
  69. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_e2e.py +0 -0
  70. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_errors.py +0 -0
  71. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_file_index.py +0 -0
  72. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_grounding.py +0 -0
  73. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_local_shell.py +0 -0
  74. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_renderer_parity.py +0 -0
  75. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_session.py +0 -0
  76. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_status_bar.py +0 -0
  77. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_theme.py +0 -0
  78. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_chat_tool_display.py +0 -0
  79. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_cli.py +0 -0
  80. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_config_resolve.py +0 -0
  81. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_doctor.py +0 -0
  82. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_extras_catalog.py +0 -0
  83. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_gateway_health.py +0 -0
  84. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_opensandbox_lifecycle.py +0 -0
  85. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_realtime_session_controller.py +0 -0
  86. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_run_cmd.py +0 -0
  87. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_runtime_python.py +0 -0
  88. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_scaffold.py +0 -0
  89. {monkeybot_cli-0.8.0 → monkeybot_cli-0.9.0}/tests/test_terminal_markdown.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: monkeybot-cli
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: CLI to create, configure, validate, and chat with monkeybot agents.
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: httpx>=0.27.0
7
7
  Requires-Dist: monkeybot-browser-mcp<1,>=0.5.0
8
- Requires-Dist: monkeybot[cli]<4,>=3.1.0
8
+ Requires-Dist: monkeybot[cli]<4,>=3.8.0
9
9
  Requires-Dist: python-dotenv>=1.0.0
10
10
  Requires-Dist: pyyaml>=6.0.2
11
11
  Requires-Dist: textual>=8.2.8
@@ -4,13 +4,13 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "monkeybot-cli"
7
- version = "0.8.0"
7
+ version = "0.9.0"
8
8
  description = "CLI to create, configure, validate, and chat with monkeybot agents."
9
9
  requires-python = ">=3.11"
10
10
  dependencies = [
11
11
  # `cli` pulls realtime + typer so `monkeybot talk` works out of the box (text mode).
12
12
  # Audio mic/PTT needs the agent (or this env) to also install monkeybot[cli-realtime].
13
- "monkeybot[cli]>=3.1.0,<4",
13
+ "monkeybot[cli]>=3.8.0,<4",
14
14
  "monkeybot-browser-mcp>=0.5.0,<1",
15
15
  "httpx>=0.27.0",
16
16
  "pyyaml>=6.0.2",
@@ -41,7 +41,7 @@ _EDIT_TOOLS = frozenset(
41
41
  {"write_file", "write", "edit_file", "apply_patch", "str_replace"}
42
42
  )
43
43
  _PATH_TOOLS = _READ_TOOLS | _EDIT_TOOLS
44
- _SEARCH_TOOLS = frozenset({"grep", "search", "web_search"})
44
+ _SEARCH_TOOLS = frozenset({"grep", "web_search"})
45
45
  _HINT_KEYS = frozenset(
46
46
  {
47
47
  "argv",
@@ -105,7 +105,7 @@ _SLASH_SPECS: tuple[tuple[str, str], ...] = (
105
105
  ("/timestamps", "Toggle turn timestamps"),
106
106
  ("/copy", "Copy last assistant reply"),
107
107
  ("/export", "Export transcript to a markdown file"),
108
- ("/export-trace", "Export full debug trace (ndjson) for evals"),
108
+ ("/export-trace", "Export the debug trace directory (ndjson + subagents)"),
109
109
  ("/bye", "Exit chat"),
110
110
  )
111
111
 
@@ -1686,10 +1686,10 @@ class ChatApp(App[int]):
1686
1686
  )
1687
1687
  return
1688
1688
  ts = datetime.now().strftime("%Y%m%d_%H%M%S")
1689
- dest = self.agent_root / "data" / f"trace_export_{ts}.ndjson"
1689
+ dest = self.agent_root / "data" / f"trace_export_{ts}"
1690
1690
  try:
1691
1691
  dest.parent.mkdir(parents=True, exist_ok=True)
1692
- shutil.copyfile(src, dest)
1692
+ shutil.copytree(session_dir, dest)
1693
1693
  except OSError as exc:
1694
1694
  self._mount_system(f"Trace export failed: {exc}", error=True)
1695
1695
  return
@@ -0,0 +1,174 @@
1
+ """monkeybot trace — list and digest session transcripts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ from monkeybot.core.layout import resolve_workspace_root
11
+ from monkeybot.core.persistence.transcript import (
12
+ TRANSCRIPT_FILENAME,
13
+ resolve_session_artifact_dir,
14
+ )
15
+ from monkeybot.core.persistence.transcript_reader import (
16
+ build_digest,
17
+ digest_to_json,
18
+ iter_session_dirs,
19
+ records_by_seq,
20
+ render_digest_markdown,
21
+ scan_session_signals,
22
+ )
23
+
24
+ from monkeybot_cli.config_resolve import resolve_agent_root, resolve_config
25
+
26
+
27
+ def _workspace(args: argparse.Namespace) -> Path:
28
+ cwd = Path(args.cwd).expanduser().resolve() if args.cwd else None
29
+ config_path = resolve_config(args.config, cwd=cwd)
30
+ agent_root = resolve_agent_root(cwd=cwd, config_path=config_path)
31
+ return resolve_workspace_root(agent_root=agent_root, config_path=config_path)
32
+
33
+
34
+ def _transcripts_root(workspace: Path) -> Path:
35
+ return workspace / ".monkeybot" / "transcripts"
36
+
37
+
38
+ def _resolve_target(raw: str | None, workspace: Path) -> Path | None:
39
+ """Session directory from an id, a directory, or a transcript.ndjson path."""
40
+ if raw:
41
+ candidate = Path(raw).expanduser()
42
+ if candidate.is_file() and candidate.name == TRANSCRIPT_FILENAME:
43
+ return candidate.parent
44
+ if candidate.is_dir() and (candidate / TRANSCRIPT_FILENAME).is_file():
45
+ return candidate
46
+ session_dir = resolve_session_artifact_dir(workspace, raw)
47
+ if (session_dir / TRANSCRIPT_FILENAME).is_file():
48
+ return session_dir
49
+ return None
50
+ sessions = iter_session_dirs(_transcripts_root(workspace))
51
+ return sessions[0] if sessions else None
52
+
53
+
54
+ def _print_list(workspace: Path, *, as_json: bool) -> int:
55
+ sessions = iter_session_dirs(_transcripts_root(workspace))
56
+ rows = []
57
+ for session_dir in sessions:
58
+ signals = scan_session_signals(session_dir)
59
+ rows.append(
60
+ {
61
+ "dir": session_dir.name,
62
+ "path": str(session_dir),
63
+ "score": signals.score(),
64
+ "errors": sum(signals.tool_errors.values()),
65
+ "interventions": sum(signals.interventions.values()),
66
+ "steers": signals.steers,
67
+ "verdicts": signals.verdicts,
68
+ "empty": signals.empty_assistant,
69
+ "summaries": signals.summaries,
70
+ "slow": len(signals.slow_tools),
71
+ "repeated": len(signals.repeated_calls),
72
+ }
73
+ )
74
+ if as_json:
75
+ print(json.dumps(rows, indent=2))
76
+ return 0
77
+ if not rows:
78
+ print(f"No transcripts under {_transcripts_root(workspace)}")
79
+ return 0
80
+ for row in rows:
81
+ print(
82
+ f"{row['dir']} score={row['score']} errors={row['errors']} "
83
+ f"interventions={row['interventions']} steers={row['steers']} "
84
+ f"verdicts={row['verdicts']} empty={row['empty']} "
85
+ f"summaries={row['summaries']} slow={row['slow']} repeated={row['repeated']}"
86
+ )
87
+ return 0
88
+
89
+
90
+ def _read_failure(exc: OSError) -> int:
91
+ print(f"Transcript read failed: {exc}", file=sys.stderr)
92
+ return 1
93
+
94
+
95
+ def run_trace_list(args: argparse.Namespace) -> int:
96
+ try:
97
+ return _print_list(_workspace(args), as_json=bool(args.json))
98
+ except OSError as exc:
99
+ return _read_failure(exc)
100
+
101
+
102
+ def run_trace_digest(args: argparse.Namespace) -> int:
103
+ workspace = _workspace(args)
104
+ try:
105
+ session_dir = _resolve_target(args.target, workspace)
106
+ if session_dir is None:
107
+ target = args.target or "(latest)"
108
+ print(f"No transcript found for {target}", file=sys.stderr)
109
+ return 1
110
+ seqs = args.seq or []
111
+ if seqs:
112
+ found = records_by_seq(session_dir, seqs)
113
+ missing = False
114
+ for seq in seqs:
115
+ record = found.get(seq)
116
+ if record is None:
117
+ print(f"No record with seq {seq} in {session_dir}", file=sys.stderr)
118
+ missing = True
119
+ continue
120
+ print(json.dumps(record, ensure_ascii=False, indent=2, default=str))
121
+ return 1 if missing else 0
122
+ digest = build_digest(session_dir, full=bool(args.full))
123
+ except OSError as exc:
124
+ return _read_failure(exc)
125
+ if args.json:
126
+ print(json.dumps(digest_to_json(digest), ensure_ascii=False, indent=2, default=str))
127
+ return 0
128
+ print(render_digest_markdown(digest, max_chars=args.max_chars))
129
+ return 0
130
+
131
+
132
+ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
133
+ trace = subparsers.add_parser(
134
+ "trace",
135
+ help="List and digest session transcripts for debugging and retros",
136
+ )
137
+ sub = trace.add_subparsers(dest="trace_command", required=True)
138
+ common = argparse.ArgumentParser(add_help=False)
139
+ common.add_argument("--cwd", help="Agent project root")
140
+ common.add_argument("--config", help="Path to monkeybot.yaml")
141
+
142
+ listing = sub.add_parser(
143
+ "list",
144
+ parents=[common],
145
+ help="List captured sessions, newest first",
146
+ )
147
+ listing.add_argument("--json", action="store_true", help="JSON output")
148
+ listing.set_defaults(func=run_trace_list)
149
+
150
+ digest = sub.add_parser(
151
+ "digest",
152
+ parents=[common],
153
+ help="Condensed timeline of one session",
154
+ )
155
+ digest.add_argument(
156
+ "target",
157
+ nargs="?",
158
+ help="Session id, session directory, or transcript.ndjson path (default: newest)",
159
+ )
160
+ digest.add_argument("--json", action="store_true", help="JSON output")
161
+ digest.add_argument("--full", action="store_true", help="Do not clip excerpts")
162
+ digest.add_argument(
163
+ "--max-chars",
164
+ type=int,
165
+ default=0,
166
+ help="Cap markdown length (0 = no cap)",
167
+ )
168
+ digest.add_argument(
169
+ "--seq",
170
+ type=int,
171
+ action="append",
172
+ help="Print one resolved record instead of the digest (repeatable)",
173
+ )
174
+ digest.set_defaults(func=run_trace_digest)
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import argparse
6
6
  import sys
7
7
 
8
- from monkeybot_cli.commands import chat, doctor, loop, new, refresh, run_cmd, talk, validate
8
+ from monkeybot_cli.commands import chat, doctor, loop, new, refresh, run_cmd, talk, trace, validate
9
9
  from monkeybot_cli.runtime_python import RuntimeUpgradeError, report_runtime_upgrade_error
10
10
 
11
11
 
@@ -24,6 +24,7 @@ def build_parser() -> argparse.ArgumentParser:
24
24
  chat.register(sub)
25
25
  talk.register(sub)
26
26
  loop.register(sub)
27
+ trace.register(sub)
27
28
  return parser
28
29
 
29
30
 
@@ -111,8 +111,6 @@ def extra_module(extra: str) -> str:
111
111
  "cli": "typer",
112
112
  "cli-realtime": "pyaudio",
113
113
  "evals": "deepeval",
114
- "knowledge-ast": "tree_sitter_language_pack",
115
- "knowledge-media": "pypdf",
116
114
  }
117
115
  return mapping.get(extra, extra)
118
116
 
@@ -34,8 +34,9 @@ only. Pass `expected_origin` so a login cannot land on the wrong site, and check
34
34
  the returned `origin` before reporting success.
35
35
 
36
36
  Tabs use aliases (`t1`, `t2`, …). Reads (`get_elements`, `page_info`, `js`,
37
- `read_tabs`) never move focus; actions do. At most five agent-controlled tabs —
38
- if you hit the cap, ask the user which to close, then `browser_close_tab`. Close
37
+ `read_tabs`) never move focus; actions do. At most 10 agent-controlled tabs —
38
+ if you hit the cap, ask the user which to close, then `browser_close_tab`. If the
39
+ error scope is `app`, the Spaces window is full across chats, not this chat's cap. Close
39
40
  tabs you opened. `browser_login` still targets the tab the user has focused.
40
41
 
41
42
  Finish browser sessions with `browser_stop`.
@@ -147,33 +147,6 @@ emission:
147
147
  # (any value other than the terse-set means off).
148
148
  style: off
149
149
 
150
- # Unified knowledge layer: local FTS + ANN embeddings + note↔workspace links + search.
151
- # Embeddings stay off by default until configured.
152
- # Index path is always {root}/index.sqlite (not configurable).
153
- knowledge:
154
- enabled: true
155
- search:
156
- default_limit: 10
157
- indexer:
158
- chunk_tokens: 700
159
- chunk_overlap_ratio: 0.12
160
- embeddings:
161
- enabled: false
162
- # provider: nvidia | openai | voyage | gemini | openai_compatible
163
- # model / dimensions / base_url are optional — per-provider defaults apply.
164
- provider: nvidia
165
- # model: nvidia/nemotron-3-embed-1b # openai → text-embedding-3-small
166
- # dimensions: 1024 # openai → 1536; voyage → 512; gemini → 768
167
- # base_url: https://integrate.api.nvidia.com/v1
168
- # openai → https://api.openai.com/v1
169
- # voyage → https://api.voyageai.com/v1
170
- # openai_compatible → required (custom gateway)
171
- batch_size: 32
172
- # Auth env (by provider): NVIDIA_API_KEY | OPENAI_API_KEY | VOYAGE_API_KEY | GEMINI_API_KEY
173
- store:
174
- type: sqlite
175
- path: .monkeybot/knowledge/vectors.sqlite
176
-
177
150
  subagents:
178
151
  timeout_sec: 600
179
152
  max_turns: 1000
@@ -241,6 +214,14 @@ sandbox:
241
214
  server_url: http://localhost:18080
242
215
  image: python:3.12
243
216
  ttl_seconds: 1800
217
+ # Background command caps. Jobs longer than max_job_seconds fail and should
218
+ # move to a broker-owned build. renew_interval_seconds extends the worker
219
+ # while a job is running (0 renews once at start). await_* bounds await_command.
220
+ max_background_jobs: 4
221
+ max_job_seconds: 3300
222
+ renew_interval_seconds: 300
223
+ await_default_wait_seconds: 300
224
+ await_max_wait_seconds: 600
244
225
  # Set false only for a remote sandbox server with no shared filesystem. It
245
226
  # then runs compute-only commands and refuses workspace/skills file access.
246
227
  shared_filesystem: true
@@ -641,7 +641,7 @@ def test_slash_export_trace_no_session(tmp_path: Path) -> None:
641
641
  async with app.run_test() as pilot:
642
642
  app.query_one("#prompt", Composer).post_message(Composer.Submitted("/export-trace"))
643
643
  await pilot.pause()
644
- assert not list((tmp_path / "data").glob("trace_export_*.ndjson"))
644
+ assert not list((tmp_path / "data").glob("trace_export_*"))
645
645
 
646
646
  asyncio.run(_run())
647
647
 
@@ -665,11 +665,20 @@ def test_slash_export_trace_copies_ndjson(tmp_path: Path) -> None:
665
665
  (session_dir / "transcript.ndjson").write_text(
666
666
  '{"seq":1,"type":"SessionManifest"}\n', encoding="utf-8"
667
667
  )
668
+ child = session_dir / "subagents" / "child-1"
669
+ child.mkdir(parents=True)
670
+ (child / "transcript.ndjson").write_text(
671
+ '{"seq":1,"type":"UserMessage"}\n', encoding="utf-8"
672
+ )
668
673
  app.query_one("#prompt", Composer).post_message(Composer.Submitted("/export-trace"))
669
674
  await pilot.pause()
670
- exports = list((tmp_path / "data").glob("trace_export_*.ndjson"))
675
+ exports = list((tmp_path / "data").glob("trace_export_*"))
671
676
  assert len(exports) == 1
672
- assert exports[0].read_text(encoding="utf-8") == '{"seq":1,"type":"SessionManifest"}\n'
677
+ assert exports[0].is_dir()
678
+ assert (exports[0] / "transcript.ndjson").read_text(encoding="utf-8") == (
679
+ '{"seq":1,"type":"SessionManifest"}\n'
680
+ )
681
+ assert (exports[0] / "subagents" / "child-1" / "transcript.ndjson").is_file()
673
682
 
674
683
  asyncio.run(_run())
675
684
 
@@ -0,0 +1,85 @@
1
+ """CLI coverage for `monkeybot trace list` and `digest`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ import pytest
9
+
10
+ from monkeybot_cli.main import main
11
+
12
+
13
+ def _session(tmp_path: Path) -> Path:
14
+ session = tmp_path / "workspace" / ".monkeybot" / "transcripts" / "20260101T000000Z_sess-1"
15
+ session.mkdir(parents=True)
16
+ records = [
17
+ {"type": "SessionManifest", "session_id": "sess-1", "model": "m"},
18
+ {"seq": 1, "type": "UserMessage", "content": "hello"},
19
+ {
20
+ "seq": 2,
21
+ "type": "HarnessIntervention",
22
+ "intervention": "doom_loop",
23
+ "detail": "stuck",
24
+ "inner_turn": 1,
25
+ },
26
+ {"seq": 3, "type": "ProviderResponse", "text": "recovered", "tool_requests": []},
27
+ ]
28
+ (session / "transcript.ndjson").write_text(
29
+ "".join(json.dumps(row) + "\n" for row in records),
30
+ encoding="utf-8",
31
+ )
32
+ return session
33
+
34
+
35
+ def test_trace_list_digest_and_seq(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
36
+ _session(tmp_path)
37
+ assert main(["trace", "list", "--cwd", str(tmp_path)]) == 0
38
+ listed = capsys.readouterr().out
39
+ assert "sess-1" in listed
40
+ assert "interventions=1" in listed
41
+ assert "score=1" in listed
42
+
43
+ assert main(["trace", "digest", "sess-1", "--cwd", str(tmp_path)]) == 0
44
+ digest = capsys.readouterr().out
45
+ assert "@seq 2" in digest
46
+ assert "doom_loop" in digest
47
+ assert "recovered" in digest
48
+
49
+ assert main(["trace", "digest", "sess-1", "--cwd", str(tmp_path), "--seq", "2"]) == 0
50
+ record = json.loads(capsys.readouterr().out)
51
+ assert record["intervention"] == "doom_loop"
52
+ assert record["detail"] == "stuck"
53
+
54
+
55
+ def test_trace_digest_missing_session(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
56
+ assert main(["trace", "digest", "missing", "--cwd", str(tmp_path)]) == 1
57
+ err = capsys.readouterr().err
58
+ assert "No transcript found" in err
59
+
60
+
61
+ def test_trace_read_failure_exits_nonzero(
62
+ tmp_path: Path, capsys: pytest.CaptureFixture[str]
63
+ ) -> None:
64
+ session = _session(tmp_path)
65
+ ndjson = session / "transcript.ndjson"
66
+ ndjson.chmod(0)
67
+ try:
68
+ assert main(["trace", "digest", "sess-1", "--cwd", str(tmp_path)]) == 1
69
+ assert "Transcript read failed" in capsys.readouterr().err
70
+ assert main(["trace", "list", "--cwd", str(tmp_path)]) == 1
71
+ assert "Transcript read failed" in capsys.readouterr().err
72
+ finally:
73
+ ndjson.chmod(0o644)
74
+
75
+
76
+ def test_trace_digest_unreadable_root_exits_nonzero(
77
+ tmp_path: Path, capsys: pytest.CaptureFixture[str]
78
+ ) -> None:
79
+ root = _session(tmp_path).parent
80
+ root.chmod(0)
81
+ try:
82
+ assert main(["trace", "digest", "--cwd", str(tmp_path)]) == 1
83
+ assert "Transcript read failed" in capsys.readouterr().err
84
+ finally:
85
+ root.chmod(0o755)
@@ -617,7 +617,7 @@ wheels = [
617
617
 
618
618
  [[package]]
619
619
  name = "monkeybot"
620
- version = "3.1.0"
620
+ version = "3.8.0"
621
621
  source = { editable = "../" }
622
622
  dependencies = [
623
623
  { name = "aiosqlite" },
@@ -625,7 +625,7 @@ dependencies = [
625
625
  { name = "google-genai" },
626
626
  { name = "httpx" },
627
627
  { name = "mcp" },
628
- { name = "numpy" },
628
+ { name = "pillow" },
629
629
  { name = "pydantic" },
630
630
  { name = "python-dotenv" },
631
631
  { name = "python-ulid" },
@@ -664,12 +664,11 @@ requires-dist = [
664
664
  { name = "google-genai", marker = "extra == 'vertex'", specifier = ">=1.0.0" },
665
665
  { name = "httpx", specifier = ">=0.27.0" },
666
666
  { name = "langfuse", marker = "extra == 'evals'", specifier = ">=3.0.0" },
667
- { name = "mcp", specifier = ">=1.0.0,<2" },
667
+ { name = "mcp", specifier = ">=1.27.1,<2" },
668
668
  { name = "mempalace", marker = "extra == 'memory'", specifier = ">=3.7.0,<4" },
669
669
  { name = "monkeybot", extras = ["cli"], marker = "extra == 'cli-realtime'" },
670
670
  { name = "monkeybot", extras = ["realtime"], marker = "extra == 'cli'" },
671
671
  { name = "monkeybot", extras = ["realtime"], marker = "extra == 'realtime-gemini'" },
672
- { name = "numpy", specifier = ">=1.26.0,<2.5" },
673
672
  { name = "openai", marker = "extra == 'huggingface'", specifier = ">=1.0" },
674
673
  { name = "openai", marker = "extra == 'nvidia'", specifier = ">=1.0" },
675
674
  { name = "openai", marker = "extra == 'ollama'", specifier = ">=1.0" },
@@ -680,16 +679,15 @@ requires-dist = [
680
679
  { name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'observability'", specifier = ">=1.28" },
681
680
  { name = "opentelemetry-instrumentation-fastapi", marker = "extra == 'observability'", specifier = ">=0.62b1" },
682
681
  { name = "opentelemetry-sdk", marker = "extra == 'observability'", specifier = ">=1.28" },
682
+ { name = "pillow", specifier = ">=10.0.0" },
683
683
  { name = "pyaudio", marker = "extra == 'cli-realtime'", specifier = ">=0.2.11" },
684
684
  { name = "pydantic", specifier = ">=2.10.0" },
685
685
  { name = "pynput", marker = "extra == 'cli-realtime'", specifier = ">=1.7.6" },
686
686
  { name = "pypdf", marker = "extra == 'huggingface'", specifier = ">=6.14.2" },
687
- { name = "pypdf", marker = "extra == 'knowledge-media'", specifier = ">=6.14.2" },
688
687
  { name = "pypdf", marker = "extra == 'nvidia'", specifier = ">=6.14.2" },
689
688
  { name = "pypdf", marker = "extra == 'ollama'", specifier = ">=6.14.2" },
690
689
  { name = "pypdf", marker = "extra == 'openai'", specifier = ">=6.14.2" },
691
690
  { name = "pypdf", marker = "extra == 'openrouter'", specifier = ">=6.14.2" },
692
- { name = "python-docx", marker = "extra == 'knowledge-media'", specifier = ">=1.1.0" },
693
691
  { name = "python-dotenv", specifier = ">=1.0.0" },
694
692
  { name = "python-ulid", specifier = ">=2.0.0" },
695
693
  { name = "pyyaml", specifier = ">=6.0.2" },
@@ -698,20 +696,18 @@ requires-dist = [
698
696
  { name = "tiktoken", marker = "extra == 'ollama'", specifier = ">=0.7.0" },
699
697
  { name = "tiktoken", marker = "extra == 'openai'", specifier = ">=0.7.0" },
700
698
  { name = "tiktoken", marker = "extra == 'openrouter'", specifier = ">=0.7.0" },
701
- { name = "tree-sitter-language-pack", marker = "extra == 'knowledge-ast'", specifier = ">=1.6" },
702
699
  { name = "typer", marker = "extra == 'cli'", specifier = ">=0.12.0" },
703
700
  { name = "uvicorn", extras = ["standard"], specifier = ">=0.32.0" },
704
701
  { name = "websockets", marker = "extra == 'realtime'", specifier = ">=14.0" },
705
702
  ]
706
- provides-extras = ["memory", "gemini", "vertex", "claude", "vertex-claude", "bedrock", "openai", "huggingface", "ollama", "nvidia", "openrouter", "sandbox", "scheduler", "postgres", "firestore", "gcs", "aws", "council", "web-search", "evals", "realtime", "realtime-gemini", "cli", "cli-realtime", "observability", "knowledge-ast", "knowledge-media"]
703
+ provides-extras = ["memory", "gemini", "vertex", "claude", "vertex-claude", "bedrock", "openai", "huggingface", "ollama", "nvidia", "openrouter", "sandbox", "scheduler", "postgres", "firestore", "gcs", "aws", "council", "web-search", "evals", "realtime", "realtime-gemini", "cli", "cli-realtime", "observability"]
707
704
 
708
705
  [package.metadata.requires-dev]
709
706
  dev = [
710
707
  { name = "anthropic", extras = ["vertex", "bedrock"], specifier = ">=0.40.0" },
711
708
  { name = "asgi-lifespan", specifier = ">=2.1.0" },
712
- { name = "monkeybot", extras = ["knowledge-ast"] },
713
- { name = "monkeybot", extras = ["knowledge-media"] },
714
709
  { name = "monkeybot", extras = ["memory"] },
710
+ { name = "monkeybot", extras = ["postgres"] },
715
711
  { name = "monkeybot-cli", editable = "." },
716
712
  { name = "mypy", specifier = ">=1.13.0" },
717
713
  { name = "opentelemetry-api", specifier = ">=1.28" },
@@ -719,6 +715,7 @@ dev = [
719
715
  { name = "opentelemetry-instrumentation-fastapi", specifier = ">=0.62b1" },
720
716
  { name = "opentelemetry-sdk", specifier = ">=1.28" },
721
717
  { name = "pre-commit", specifier = ">=4.0.0" },
718
+ { name = "pypdf", specifier = ">=6.14.2" },
722
719
  { name = "pytest", specifier = ">=8.3.0" },
723
720
  { name = "pytest-asyncio", specifier = ">=0.24.0" },
724
721
  { name = "pytest-cov", specifier = ">=6.0.0" },
@@ -730,7 +727,7 @@ dev = [
730
727
 
731
728
  [[package]]
732
729
  name = "monkeybot-browser-mcp"
733
- version = "0.5.0"
730
+ version = "0.8.1"
734
731
  source = { editable = "../integrations/browser-mcp" }
735
732
  dependencies = [
736
733
  { name = "browser-harness" },
@@ -756,7 +753,7 @@ dev = [{ name = "pytest", specifier = ">=8.0.0" }]
756
753
 
757
754
  [[package]]
758
755
  name = "monkeybot-cli"
759
- version = "0.6.0"
756
+ version = "0.9.0"
760
757
  source = { editable = "." }
761
758
  dependencies = [
762
759
  { name = "httpx" },
@@ -791,85 +788,6 @@ dev = [
791
788
  { name = "ruff", specifier = ">=0.8.0" },
792
789
  ]
793
790
 
794
- [[package]]
795
- name = "numpy"
796
- version = "2.4.6"
797
- source = { registry = "https://pypi.org/simple" }
798
- sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" }
799
- wheels = [
800
- { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" },
801
- { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" },
802
- { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" },
803
- { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" },
804
- { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" },
805
- { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" },
806
- { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" },
807
- { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" },
808
- { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" },
809
- { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" },
810
- { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" },
811
- { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" },
812
- { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" },
813
- { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" },
814
- { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" },
815
- { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" },
816
- { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" },
817
- { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" },
818
- { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" },
819
- { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" },
820
- { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" },
821
- { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" },
822
- { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" },
823
- { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" },
824
- { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" },
825
- { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" },
826
- { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" },
827
- { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" },
828
- { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" },
829
- { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" },
830
- { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" },
831
- { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" },
832
- { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" },
833
- { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" },
834
- { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" },
835
- { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" },
836
- { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" },
837
- { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" },
838
- { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" },
839
- { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" },
840
- { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" },
841
- { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" },
842
- { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" },
843
- { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" },
844
- { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" },
845
- { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" },
846
- { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" },
847
- { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" },
848
- { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" },
849
- { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" },
850
- { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" },
851
- { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" },
852
- { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" },
853
- { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" },
854
- { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" },
855
- { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" },
856
- { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" },
857
- { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" },
858
- { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" },
859
- { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" },
860
- { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" },
861
- { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" },
862
- { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" },
863
- { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" },
864
- { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" },
865
- { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" },
866
- { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" },
867
- { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" },
868
- { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" },
869
- { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" },
870
- { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" },
871
- ]
872
-
873
791
  [[package]]
874
792
  name = "packaging"
875
793
  version = "26.2"
File without changes