mirror of
https://github.com/langchain-ai/deepagents.git
synced 2026-07-21 17:25:26 -04:00
feat(quickjs)!: upgrade to 0.2.0 quickjs-rs (#4067)
* minor bump because the shape of snapshots have changed * upgrading quickjs-rs to use the memory sanboxed version of the quickjs runtime
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
A [`deepagents`](../deepagents) middleware that gives an agent a persistent, sandboxed **JavaScript REPL** tool, backed by [`quickjs-rs`](../../../quickjs-wasm) (QuickJS embedded via PyO3 + rquickjs).
|
||||
|
||||
Instead of issuing N serial tool calls, the model can write one block of JavaScript that orchestrates work in-loop — variables and functions defined in one call survive into the next, `Promise.all` runs concurrent work, and (opt-in) agent tools are callable from inside the REPL as `await tools.<name>(...)`.
|
||||
Instead of issuing N serial tool calls, the model can write one block of JavaScript that orchestrates work in-loop — variables and functions defined in one call survive into the next, `Promise.all` runs concurrent work, configured subagents are dispatchable as `await task({...})`, and (opt-in) agent tools are callable from inside the REPL as `await tools.<name>(...)`.
|
||||
|
||||
```python
|
||||
from deepagents import create_deep_agent
|
||||
@@ -23,6 +23,7 @@ agent = create_deep_agent(
|
||||
- [Console capture](#console-capture)
|
||||
- [Timeouts and memory](#timeouts-and-memory)
|
||||
- [Result formatting](#result-formatting)
|
||||
- [Dispatching subagents (`task`)](#dispatching-subagents-task)
|
||||
- [Programmatic tool calling (PTC)](#programmatic-tool-calling-ptc)
|
||||
- [Configuration reference](#configuration-reference)
|
||||
- [Errors the model can see](#errors-the-model-can-see)
|
||||
@@ -73,11 +74,11 @@ The middleware:
|
||||
|
||||
### Persistence
|
||||
|
||||
The REPL is module-flavoured: top-level `let`/`const`/`function` persist across `eval` calls in the same thread. By default (`mode="thread"`), state also persists across turns in the same LangGraph `thread_id` by snapshotting after each run and restoring before the next.
|
||||
The REPL is module-flavoured: top-level `let`/`const`/`function` persist across `eval` calls in the same thread. The `mode` setting controls how far that persistence reaches:
|
||||
|
||||
Set `mode="call"` to reset REPL state on every `eval` tool call.
|
||||
Set `mode="turn"` to persist state only within each turn.
|
||||
Snapshot payloads are capped by `max_snapshot_bytes` (defaults to `memory_limit`); oversized snapshots are dropped instead of persisted.
|
||||
- `mode="thread"` (default) — state persists across calls **and** across turns in the same LangGraph `thread_id`.
|
||||
- `mode="turn"` — state persists across calls within a turn only.
|
||||
- `mode="call"` — each `eval` call runs in a fresh REPL.
|
||||
|
||||
```js
|
||||
// call 1
|
||||
@@ -151,6 +152,52 @@ Results and stdout are independently truncated to `max_result_chars` (default 40
|
||||
|
||||
Numeric rendering follows Node's REPL convention — whole-valued floats (`42.0`) render as integers (`42`) so the model isn't confused by JS's single numeric type.
|
||||
|
||||
## Dispatching subagents (`task`)
|
||||
|
||||
When the host agent is a Deep Agents agent that has a `task` tool, the middleware exposes a top-level `task(...)` primitive inside the REPL (on by default; disable with `subagents=False`). The model dispatches a configured subagent and orchestrates the rest — fan-out, filtering, multi-stage flow, synthesis — in plain JavaScript:
|
||||
|
||||
```js
|
||||
const result = await task({
|
||||
description: "Review src/auth.ts for SQL injection. Cite line numbers.",
|
||||
subagentType: "reviewer", // configured subagent name (required)
|
||||
responseSchema: { /* JSON Schema */ }, // optional structured output
|
||||
});
|
||||
```
|
||||
|
||||
`task` runs a full agentic loop for the selected subagent and resolves to its final result. With `responseSchema`, the resolved value is already a typed JS value matching the schema. Each dispatch is stateless — `description` is the only prompt the subagent receives, so make it self-contained.
|
||||
|
||||
Because the model holds intermediate state in JS, it can fan out concurrently (the bridge caps concurrency per REPL), filter results between passes, and merge everything in one `eval` call instead of one model round-trip per subagent.
|
||||
|
||||
This is distinct from PTC: `task` is always a top-level global (never under `tools`), and it is available whenever the host exposes a Deep Agents `task` tool — independent of the `ptc=` allowlist.
|
||||
|
||||
```js
|
||||
// One eval call: classify in parallel, then deep-review only the risky files.
|
||||
const tagged = await Promise.all(files.map(async (f) => ({
|
||||
file: f,
|
||||
...(await task({
|
||||
description: `Classify ${f} as handler, util, test, or config.`,
|
||||
subagentType: "reviewer",
|
||||
responseSchema: {
|
||||
type: "object",
|
||||
properties: { kind: { type: "string" }, risky: { type: "boolean" } },
|
||||
required: ["kind", "risky"],
|
||||
},
|
||||
})),
|
||||
})));
|
||||
|
||||
const reviews = await Promise.all(
|
||||
tagged.filter((t) => t.kind === "handler" && t.risky).map(async (t) => ({
|
||||
file: t.file,
|
||||
review: await task({
|
||||
description: `Deep security review of ${t.file}. Cite line numbers.`,
|
||||
subagentType: "reviewer",
|
||||
}),
|
||||
})),
|
||||
);
|
||||
```
|
||||
|
||||
> **Approval:** `task(...)` runs inside an already-approved `eval` invocation and does **not** trigger parent-level `interrupt_on` / HITL approval per dispatch. Gate the `eval` tool itself, add approval middleware inside subagent specs, or set `subagents=False` if per-dispatch parent approval is required.
|
||||
|
||||
## Programmatic tool calling (PTC)
|
||||
|
||||
PTC is the reason to use this middleware over a plain code-interpreter tool. When configured, each exposed tool is available inside the REPL as:
|
||||
@@ -202,7 +249,7 @@ Enums, `anyOf` unions, nested objects, and arrays are all supported by the schem
|
||||
|
||||
- Each PTC-exposed tool gets a QuickJS host-function bridge registered under a generated `__tools_*` global symbol. The bridge is async, so the guest sees `tools.x(...)` as returning a `Promise`.
|
||||
- `globalThis.tools` is rebuilt every turn from the currently-exposed name set. So if an upstream middleware filters tools on a per-turn basis, the `tools` namespace follows along.
|
||||
- When the bridge invokes a tool, it forwards the `ToolRuntime` captured from the outer `eval` call — so subagent tools like `task` see graph `state`, `store`, `context`, and a synthesised child `tool_call_id`.
|
||||
- When the bridge invokes a tool, it forwards the `ToolRuntime` captured from the outer `eval` call — so the tool sees graph `state`, `store`, `context`, and a synthesised child `tool_call_id`. (Subagent dispatch has its own top-level `task(...)` primitive — see [Dispatching subagents](#dispatching-subagents-task) — and is not routed through the `tools` namespace.)
|
||||
- Tool return values are coerced to strings: strings pass through, `ToolMessage`s get unwrapped, a `Command` has its last-message content extracted, everything else gets `json.dumps`'d.
|
||||
|
||||
## Configuration reference
|
||||
@@ -215,6 +262,7 @@ CodeInterpreterMiddleware(
|
||||
tool_name="eval", # what the model calls it
|
||||
max_result_chars=4000, # result/stdout truncation, each
|
||||
capture_console=True, # install console.log/warn/error bridge
|
||||
subagents=True, # expose global `task(...)` when host has a Deep Agents task tool
|
||||
mode="thread", # "thread" | "turn" | "call"
|
||||
max_snapshot_bytes=None, # defaults to `memory_limit`; larger snapshots are dropped
|
||||
ptc=None, # None | list[str] | list[BaseTool]
|
||||
|
||||
@@ -24,7 +24,7 @@ keywords = ["deepagents", "langchain", "langgraph", "repl", "javascript", "quick
|
||||
|
||||
dependencies = [
|
||||
"deepagents>=0.6.8,<0.7.0",
|
||||
"quickjs-rs>=0.1.2,<0.2.0",
|
||||
"quickjs-rs>=0.2.0,<0.3.0",
|
||||
"langchain>=1.3.9,<2.0.0",
|
||||
"langchain-core>=1.4.7,<2.0.0",
|
||||
"langgraph>=1.2.5,<2.0.0",
|
||||
|
||||
@@ -1125,9 +1125,17 @@ async def test_async_task_global_invokes_runner(repl: _ThreadREPL) -> None:
|
||||
runnable = RunnableLambda(_sync, afunc=_async)
|
||||
|
||||
outcome = await repl.eval_async(
|
||||
"globalThis.task = null;"
|
||||
"delete globalThis.task;"
|
||||
"task.extra = 1;"
|
||||
# The `task` global is bound writable:false / configurable:false, so under
|
||||
# strict-mode eval every tamper attempt must THROW (a stronger guarantee
|
||||
# than the old sloppy-mode silent no-op). Each attempt is required to
|
||||
# raise, and `task` must survive intact and remain callable afterwards.
|
||||
"const mustThrow = (label, fn) => {"
|
||||
" try { fn(); } catch (e) { return; }"
|
||||
" throw new Error('task binding is mutable: ' + label);"
|
||||
"};"
|
||||
"mustThrow('assign', () => { globalThis.task = null; });"
|
||||
"mustThrow('delete', () => { delete globalThis.task; });"
|
||||
"mustThrow('addProp', () => { task.extra = 1; });"
|
||||
"if (!Object.isFrozen(task) || task.extra !== undefined) {"
|
||||
" throw new Error('task binding is mutable');"
|
||||
"}"
|
||||
|
||||
Generated
+26
-18
@@ -923,7 +923,7 @@ requires-dist = [
|
||||
{ name = "langchain", specifier = ">=1.3.9,<2.0.0" },
|
||||
{ name = "langchain-core", specifier = ">=1.4.7,<2.0.0" },
|
||||
{ name = "langgraph", specifier = ">=1.2.5,<2.0.0" },
|
||||
{ name = "quickjs-rs", specifier = ">=0.1.2,<0.2.0" },
|
||||
{ name = "quickjs-rs", specifier = ">=0.2.0,<0.3.0" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
@@ -1727,25 +1727,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "quickjs-rs"
|
||||
version = "0.1.2"
|
||||
version = "0.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/96/59/4c596144ee2dfe49024cd3abf32a97ba62c34cd9f0f72e6fe23021a73181/quickjs_rs-0.1.2.tar.gz", hash = "sha256:95c42dcb40f067ae3b95eb3f79836cc8bcab62d4fd4cb276970ca2a211e687c7", size = 155522, upload-time = "2026-05-05T04:32:48.315Z" }
|
||||
dependencies = [
|
||||
{ name = "wasmtime" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3f/59/263e19bcff54b7e7993de3f79da25c23b7d7296d84c13d31a3e38ea0ec7f/quickjs_rs-0.2.0.tar.gz", hash = "sha256:db1a8d72ddfc0e766772622adb510f91aa8626d87afd843893c1e2a360ee1ce8", size = 483392, upload-time = "2026-06-18T00:08:53.928Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/cd/0375e8324d581e3c8c54c167359643063cbd6a824a73d0d5d37ad84ecad0/quickjs_rs-0.1.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4a03488c73626a129072eae966bcadede77dcc79cfe36f2ea3b0f1ae351bae75", size = 1358448, upload-time = "2026-05-05T04:32:20.562Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/c3/f5f98bb3f7e4d8df209c470592d700b54d3f26b91c5b3203133d5940e0c4/quickjs_rs-0.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf0941f4d6903ac4ce24d3734f71c4391aff403d34774e66405f25045a538ad5", size = 1271913, upload-time = "2026-05-05T04:32:22.821Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/18/4452836b1d94a3e9f4a9294c06ae4c4e6a0e8ec6e4842cf95ac7cc51fec1/quickjs_rs-0.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71f22f01b871800978b8bc23aeeb9edc4c708ada2552da1bfe15227af1717e11", size = 1296910, upload-time = "2026-05-05T04:32:24.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/eb/591cd04c38cbc0770a0609ed87fdb1121e38af64b78b507d67d4ad31f7cc/quickjs_rs-0.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efb68efe12b600e18dc7b8709dfb3bd61acf3246683713757101bec6db2dd5ad", size = 1395102, upload-time = "2026-05-05T04:32:26.838Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/d1/4d2cb369278c0b6333b68224d35a37161ba1408dc42588c7033756a62416/quickjs_rs-0.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:93c1ac55e811f582c1b7affd92274ded945e77009010d4a028bfa2d6577b8322", size = 1304595, upload-time = "2026-05-05T04:32:28.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/01/e50258535da0f38167d1df6e8370027a1629f3553ecebe188a501eaee826/quickjs_rs-0.1.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:543ae2a983f677f7c83457fbea6fb33abf938215b512439340c90768c15b0b96", size = 1358211, upload-time = "2026-05-05T04:32:30.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/32/6f2c18a39388b1f45fecbc8639053b15b5a7d8d5345dba1886e7ae120dc7/quickjs_rs-0.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68bb46c3909bd40768da20f0ffbdd1e0da70702e0638711f5e2bb31d778dd92a", size = 1272808, upload-time = "2026-05-05T04:32:32.176Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/f2/77f04f177df196674e926eb7a019127cea5551fa8174d621300536888be9/quickjs_rs-0.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b3d5bde9f2c12bfce1702f9d1bc2ccfa14f3993211e3d9ed8eceaf7069d432d", size = 1295774, upload-time = "2026-05-05T04:32:33.987Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/cb/ee09b2d9797d248ca9c492e88ded527798693bb013f5d921c9fb07229f8f/quickjs_rs-0.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7dc9d446b00c24437ae22ba0acefc238abfbdca223716bd8c2ce4223b836d14", size = 1394148, upload-time = "2026-05-05T04:32:35.662Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/a7/72c50e1f52ac928902657f19ca0f549676e6be84252e5d3fa286cecc6c01/quickjs_rs-0.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:b4229db014881f56828c8ffd120b6be822f2886d570ab1bea99cfe70da83f401", size = 1302547, upload-time = "2026-05-05T04:32:37.29Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/e2/6497237126abea9f058e72904d406f31d0bde387796f67b13cdb7b86184c/quickjs_rs-0.1.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:673a5c9824905754f67f52e7d05a23ca8094828b44cd389dd63976caa109420f", size = 1358790, upload-time = "2026-05-05T04:32:39.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/55/f5fc4bf2c237a8570ce57ee8f9ddd76bc34b8c4e0013d408bc3a697dea0e/quickjs_rs-0.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2af1fa9cca2009e8ab69d39d5b4579b59ec69607fb2cf4394cccfab14bd89caa", size = 1273427, upload-time = "2026-05-05T04:32:41.254Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/bf/88760ad6331a08d1749678d50127d3d7ba7309a815fb4c325d4d6ffa740e/quickjs_rs-0.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:806eec8c3ba699931372e34743b3894795eb382f2ee70321a25795228524840f", size = 1295904, upload-time = "2026-05-05T04:32:43.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/b9/7f0aef4047a7a2469a9945cdb493b5e8937249b036c8b53699c84eed9e0b/quickjs_rs-0.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed6aa295b80f5ebc7b077af37895a70234c566961735ca9c803a3fe8be511ec", size = 1394390, upload-time = "2026-05-05T04:32:45.052Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/db/9e3bfba55953fe5af5f39f1144b033fe572ae55a5e1bc68a4408f636cb54/quickjs_rs-0.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:606a7b4da741ceb51e4f79723ad366f9272f69bca4f438b985d1b54c56c8fcc0", size = 1302778, upload-time = "2026-05-05T04:32:46.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/22/7caf4552a7880f205c95099c3c3e6d36f146af4e34a0b3baa12d49c47ea1/quickjs_rs-0.2.0-py3-none-any.whl", hash = "sha256:cb661d40ee3ba6d3673a75729223315fd4f0b22087373156425fa4227fa2adc6", size = 454517, upload-time = "2026-06-18T00:08:52.445Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2050,6 +2039,25 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/d0/5bf7cbf1ac138c92b9ac21066d18faf4d7e7f651047b700eb192ca4b9fdb/uuid_utils-0.14.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:258186964039a8e36db10810c1ece879d229b01331e09e9030bc5dcabe231bd2", size = 364700, upload-time = "2026-02-20T22:50:21.732Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime"
|
||||
version = "45.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/ff/db9cfc61d988bc15303134bb174176a29839976876dfd18c3a12548ad291/wasmtime-45.0.0.tar.gz", hash = "sha256:2ad4bf7ca286ceea35c1e420d10b368d7f83faf9a5ffde87b4ee334a9b7f55f3", size = 128297, upload-time = "2026-05-26T17:57:39.131Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/75/56/7d941adba273210dcf4198266a47f472a5eeca20172005b443af71a9a3e7/wasmtime-45.0.0-py3-none-android_26_arm64_v8a.whl", hash = "sha256:4e843795b53e66c71313f2254731467372e5e1549227cf14accb9e2d57701c10", size = 8659052, upload-time = "2026-05-26T17:57:12.338Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/81/c4d81ebf3db8aa28f789a9569640f30790d5234c509a0234cd502aa2638b/wasmtime-45.0.0-py3-none-android_26_x86_64.whl", hash = "sha256:35e713f907264e470f3bc9b592b81b8ed0f8f5651725d9f07a5d52beb0642e38", size = 9619373, upload-time = "2026-05-26T17:57:14.979Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/c7/7594da7fa8a3bc5e765733ad57aac9b7b27262c4afa47521bd500e4a4574/wasmtime-45.0.0-py3-none-any.whl", hash = "sha256:6251ee5074a8b8bfaa98e6e99cb5d49d6d0f2320b3265d5aa6c2ee5df5fb4519", size = 8019034, upload-time = "2026-05-26T17:57:20.138Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/76/7d0e440ca03a717a97889dbb7b68f952c20ed4ffd3f59addf9553579e1d5/wasmtime-45.0.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:3579b0ec6d001750d66ec7089aaeee2c048f88328c82743e15f099af01b0cf84", size = 9401625, upload-time = "2026-05-26T17:57:22.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/0b/a81b5daf5adea482ecb68d9615f6a348486ab4d8e980a915d4420e57ee4d/wasmtime-45.0.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:31d10f25c330cebcfb364e9a357123deeec96c41725ff2bba91b705587f38a93", size = 8255954, upload-time = "2026-05-26T17:57:24.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/8c/e9019a28e908214031310aefd78e4755221d02303190b54b2c85cb69573e/wasmtime-45.0.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:5d1416ec6da8cd87c29e2e9eb074358c91839c2fff971fe428c8921eaae68e73", size = 9681185, upload-time = "2026-05-26T17:57:26.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/56/ed5f492bd553a31c8e28d621f8256f2c7b1a133b28f73525d96ca355891a/wasmtime-45.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:a499f6ab0eebb70dca83d6a4904b743cd122f322af3abe86af08ad753533d946", size = 8582001, upload-time = "2026-05-26T17:57:28.883Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/12/9b41740da83f51014b88181c9086de0ed75d736a5329baff7323c4fb6eff/wasmtime-45.0.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bef65282b7de744106a91da43e4d06ba19d2d587bc54abb83b3e757f0c4fc030", size = 8633462, upload-time = "2026-05-26T17:57:31.423Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/63/49d8317706a108d9ed1d4166d0fc710796da1b20e591a98a96575dec367a/wasmtime-45.0.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a0b6ca14b4628a5d1ffa91ccf2c0f2c58fa171f126ec085d564b09d5795395dd", size = 9712524, upload-time = "2026-05-26T17:57:33.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/71/8e31ea472ceb934e7261ac59a786e82cd82b4d4dcb7c870d498aa9c3c21e/wasmtime-45.0.0-py3-none-win_amd64.whl", hash = "sha256:1736a70a48f713aaf1a878514d29cc6f554213b5431e04447813a3b9b4320381", size = 8019039, upload-time = "2026-05-26T17:57:36.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/d1/ac536e92ac95a02e137be5b6829f15b87d5eef93ace32e5ee8035155b839/wasmtime-45.0.0-py3-none-win_arm64.whl", hash = "sha256:ae9726590e6d90c6305b8b507c93468b145204d4390aa9a2e29e26babcae110e", size = 6845659, upload-time = "2026-05-26T17:57:37.696Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "watchdog"
|
||||
version = "6.0.0"
|
||||
|
||||
Reference in New Issue
Block a user