mirror of
https://github.com/software-mansion/argent.git
synced 2026-09-14 19:27:14 +08:00
feat/android-atomic-clear
69 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
60c8edecbd |
fix(flow): surface a weak clear a composed flow buried in its sub-run
`execLeafStep` promoted a weak-pass note for `keyboard` and `run-sequence` steps. `flow-execute` is the third registered nested orchestrator and was not read: it keeps its sub-run's reports inside `result`, `flowExecuteOutcome` answers `undefined` on a green sub-run, and the CLI's `StepReport` has no `result` field — so a composed flow whose clear took an unverified path reported a clean green with the warning where nothing renders or counts it. `flow-add-step` records a raw `tool: flow-execute` step on four branches, so this is a spelling flows really carry. Reproduced on an API 36 emulator with the helper too old for the atomic path, against two real flows (an inner `tool: keyboard` clear, an outer `tool: flow-execute`): before OUTER step 0 pass flow-execute warning=NONE after OUTER step 0 pass flow-execute warning=keyboard clear: the atomic … Nested `warning`s are taken whole rather than filtered to `keyboard` notes: unlike a `note`, that field already means one thing — this step passed in a way that weakens it as proof — and the sub-run's own `execLeafStep` has applied the tool rule at its level, so composition to any depth needs no second rule. Repeats are dropped. The enumeration now lives in one function, since three docblocks name it: both `StepReport.warning`s and the `flow-execute` tool description said the note reaches a report three ways, and this is the fourth. |
||
|
|
679c78a7b8 |
docs(keyboard,flow): correct what warns, what a password box verifies, and how a helper goes stale
Six statements this PR left contradicting the code it shipped:
- `StepReport.warning` and `argent-cli`'s copy enumerated the raisers without
the `run-sequence` route, and without saying that `keyboard` is the only
tool read this way.
- `reliability-and-recovery.md` said raw `tool:` steps never warn. A raw
`keyboard` clear does, and the same file's own table lists that row.
- `live-authoring.md` and `argent-qa-flows` both said a password box can never
produce a verified clear. The helper reads one back whenever the write is
empty, and says so in its own comment: only `{ clear: true, text: "..." }` on
a password box is unverifiable.
- `flow-yaml.md`'s canonical `clear` section, which both new passages link back
to, never mentioned the note or the warning at all.
- The `flow-execute` description listed only the idle and selector-less-gesture
warning families, so an agent reading it could not learn that a clear warns.
- `helper_outdated` was blamed on "whatever started first, possibly from
another argent install". `spawnHelper` always starts a fresh `am instrument`
and connects to the port that process announces, so a foreign helper is never
on our socket. The real routes are a lagging release and a stale install
cache.
Also: the `keyboard` description now says the verified Android path is one
accessibility edit rather than key events, so `{ clear, text }` fires no
per-key handler there; and the last copy of the pre-atomic "~41s" worst case
is reconciled with the budgets the tool actually declares.
|
||
|
|
1a0a073723 |
docs(flow): enumerate the keyboard-clear warning wherever warnings are listed
The runner gained a third family of step warning and nothing that enumerates
warnings, or tells an author how to resolve one, was updated for it. Five sites
still described the raisers as the idle wait plus the selector-less gesture:
`StepReport.warning` in flow-run and in argent-cli, `argent-qa-flows` §5 (whose
"resolve every passing-step warning" is unconditional and whose remedies covered
only those two), `live-authoring.md`, and the classification table in
`reliability-and-recovery.md`.
It is not a rare shape: `{ clear: true, text: … }` into any Android password
field can never verify — the read-back sees bullets — so it warns on every run.
The remedy differs by note: a helper that is missing, too old, or could not start
is environment, while a field that cannot be read back can never produce a
verified clear and has to be gated on an app result instead.
|
||
|
|
abe5ff3c56 |
refactor(flow): generalize when's block machinery so a second block directive can't miss a site (#748)
`when` is the only directive with a sibling `steps:` list, and four runner sites asked `kind === "when"` directly to decide whether a step has authored children to expand as skip lines. A second block directive would have had to remember all four, and a forgotten one silently drops a whole block from the report — exactly the invariant `reportBlockSkipped` exists to hold. This introduces `blockSteps(step)` as the single runtime predicate and `BLOCK_DIRECTIVE_KEYS` as its parse-time counterpart, and extracts the parse-side block plumbing (`assertBlockDepth`, `parseBlockSteps`) that a second directive must share — notably the depth cap, renamed `MAX_BLOCK_DEPTH`, which has to be ONE counter across directives or an alternating nesting chain evades it and reopens the cyclic-YAML-alias hole it was added to close. No behavior change, with one deliberate exception: the depth-cap message now reads `block directives nest deeper than 20 levels` where it said `when blocks nest deeper than 20 levels` — the cap stopped being `when`'s the moment it became shared, and a message naming only `when` would misdirect an author who hit it through a different directive. Every other error message, their ordering, and the report shapes are untouched. 559 flow tests pass unchanged. --- **Stack** 1. **#748 — you are here** · `refactor(flow)`: generalize when's block machinery → `main` 2. #749 · `feat(flow)`: add `repeat:` blocks → #748 |
||
|
|
4a11317706 |
chore: bump to 0.20.0 (#765)
Version-only bump of the workspace to 0.20.0, same shape as #741 (the 0.19.0 bump). ## What changed - All 16 packages under `packages/*` → `0.20.0` - `package-lock.json` — regenerated with `npm install --package-lock-only --ignore-scripts` on Node 24, matching what the Lockfile job runs. Version-only: 16 insertions, 16 deletions, nothing else. - `server.json` — the MCP registry manifest, in **both** places it carries a version: the top-level `version` and `packages[0].version`. It lives outside `packages/*`, so a workspace sweep leaves it behind and the registry then rejects the publish because `packages[].version` names a version npm has never seen. ## Verification - `npm run check:versions` → "All workspace packages and server.json are at 0.20.0." - `npx prettier --check` clean on every touched file - No `"0.19.0"` string left anywhere in the repo No code changes. |
||
|
|
96ea94f87a |
feat(flow): add an idle condition that waits for the screen to stop moving (#728)
> Stacked on #574. Replaces the route-fingerprint approach that #727 and #729 carried; both are closed. > > Rebased off the #715-#726 chain so it can merge on #574 alone. That chain owns `DirectiveOutcome.warning` / `StepReport.warning` and the `indeterminate` scoring rule; the small amount of each that this condition needs is carried here, scoped to `idle`, and will conflict textually with #724/#725 for whichever lands second. ## One check a selector cannot express ```yaml - await: { idle: true } ``` Has the screen stopped moving? Flows were substituting fixed `wait:` steps for it, which either wait too long on every run or too little on the run that mattered. ## Why it reads pixels as well as the tree Each signal is blind to what the other sees. The tree cannot see presentation-layer motion. An iOS push or modal dismissal commits its hierarchy up front and then animates a layer for a few hundred milliseconds; a cross-fade or a scrim moves no node at all. Measured on a real Android emulator: a page animating a large element continuously reads as a single static `WebView` node, and the existing `await-screen-idle` tool called it settled in **465 ms, five times out of five**. On a deterministic Chromium page whose DOM is byte-identical across the animation, that tool returned `settled: true` in **407 ms** while this condition failed 8/8. Pixels alone would not do either: they cannot see a tree still churning behind an unchanged surface, and anything animated forever (a video, a shimmer) would make a pixel-only settle unsatisfiable on a screen the tree calls ready. Unlike that tool, this cannot be silently stepped over. Readiness is not an acceptance criterion — the verdict belongs to the identity and outcome checks around it — so a screen that never settles still **passes**, carrying a `warning` that says what the green actually bought. That is what makes it safe to persist in a flow: it reports on every run without ever failing one over a video or a shimmer. The only thing that stops a run is a tree source that cannot be read. ## The rule the design follows The absence of evidence is never evidence. - A capture that did not arrive cannot complete the hold. - Neither can a single agreeing pair. Every animation that reverses has a turning point, and two samples straddling it come back identical while the screen is moving. On a live 3 s cross-fade that passed a default-shaped step on roughly **one run in three**; settling now takes two consecutive still intervals, so `minStableMs: 0` still means three reads. - A round is never started without the budget to observe it with. A round begun with nothing left neither captures nor reads, and both absences were otherwise recorded as facts about the device. - No verdict comes from a latch. A screen that settles and then moves again, or goes blank, has not settled. ## `timeout:` is a real bound No describe path takes an abort signal, so the tree read is raced against what is left of the budget. Without that, a wedged ViewInspector RPC ran 2.25 s past an 8000 ms budget, and a busy Android screen ran 5.4-6.0 s against a 3000 ms one. A read that runs out of step budget is the step ending, not the source failing, and only the latter is reported as an environment problem. ## Failure modes stay apart They call for opposite responses. A screen that never stopped moving is a verdict about the app. A tree that could not be read is not, and it names the foreground check first, because a backgrounded app reads identically to an uninstrumented one and relaunching is the wrong repair. A run whose captures never produced a comparable pair still settles on the tree and says so in a warning, rather than passing off half a proof as the whole one. ## Capture routing and tolerance Captures route exactly as the `screenshot` tool routes them, so tvOS and Vega, which have no simulator-server backend but are perfectly screenshottable through `xcrun` and the emulator console, are covered rather than written off. The pixel comparison owns its tolerance rather than borrowing `screenshot-diff`'s. That one holds a baseline stored across sessions, machines and OS versions against a live capture and must absorb real drift; this one holds two captures one poll apart from a single session, where a static screen reads back byte-identical (measured: zero changed pixels across five consecutive pairs on an idle simulator). The margin is load-bearing, because uniform change is all-or-nothing: at the baseline tolerance any cross-fade slower than ~2 s counted zero changed pixels and read as settled mid-animation. ## Not a screen check A dropped tap leaves the source screen perfectly idle. It belongs after the element check that names the destination, never instead of one. #730 documents the pairing. ## Verification - 3307 tests pass; typecheck, `typecheck:tests`, lint and prettier clean. Eleven mutations applied to the source, all eleven caught by tests. - Driven end to end against a real iOS simulator, a real Android emulator, and a Chromium app through a tool-server built from this branch: static screens pass with no warning (proving captures ran), animating screens pass carrying the warning that names the motion, and the pre-existing tool disagrees exactly where it should. --------- Co-authored-by: Hubert Gancarczyk <claude-hubert.gancarczyk@swmansion.com> |
||
|
|
afd722924f |
feat(flow): support concurrent flow recordings (#574)
## Problem
Only one flow could be recorded at a time, anywhere on the machine.
Recording state was three module globals in `flow-utils.ts` — harmless
in a per-client process, but the tool-server is a **host-wide singleton
per install bundle**. Every MCP client, every subagent sharing a
parent's connection, every `argent` CLI call and every project on the
machine attaches to the same process, the same `Registry`, and those
same three globals. HTTP requests are served concurrently with no
serialization.
Three failures followed, all reproduced against the pre-change code:
1. **A second recording clobbered the first.** `startRecordingSession`
just overwrote, so the first agent's next `flow-add-step` silently
appended into the *other* agent's flow file — in a different project.
2. **Any agent could finish any agent's recording.**
`flow-finish-recording`'s schema was literally `z.object({})`.
3. **Replay stomped recording.** `resolveFlowFilePath` called
`setActiveProjectRoot`, so a `flow-execute` in project B rebound the
global root mid-recording of project A. Already worked around, locally
only, in `flow-add-step`.
## Approach
Recording state is now a `Map` keyed by the resolved flow file path,
`<project_root>/.argent/flows/<name>.yaml` — the identity of the
artifact being built, so "same key" means "same output file" and two
sessions on one key are a genuine collision rather than an accident of
scoping. `flow-add-step`, `flow-add-echo` and `flow-finish-recording`
each take `name` + `project_root`, so every call is self-contained;
required rather than optional-with-fallback, since a fallback would
succeed while an agent is alone and fail only once a second recording
exists.
`resolveFlowFilePath` is pure, which fixes defect 3 and retires
`FLOW_PROJECT_ROOT_REQUIRED` (the state it guarded can no longer exist).
Rejected alternatives — keying by device udid (flows are deliberately
device-portable, and `flow-add-echo`/`flow-finish-recording` have no
device at all), an opaque `recordingId` (an agent that loses the token
cannot recover), and per-caller identity from the transport
(`ToolContext` carries no caller identity, and subagents share their
parent's MCP connection, so it would fail the exact scenario being
fixed).
### Locking, and why readers needed more than a lock
Every mutation of one flow file runs under a lock keyed by that path —
appends, `flow-start-recording`'s truncate-and-register, and
`flow-finish-recording`'s read-summarize-clear. Keyed by file rather
than owned by the session, because a restart *replaces* the session: a
session-owned lock cannot exclude the operation that supersedes it. Per
file, not global, so two recordings never queue behind each other. An
append whose session was finished, restarted or evicted meanwhile fails
with `FLOW_NO_ACTIVE_RECORDING` rather than writing into a different
take.
The lock serializes writers only, and no reader of a flow YAML can join
it — `flow-execute`'s own load, its `run:` fragment load,
`flow-read-prerequisite`, `flow-add-step`'s sibling-fragment check, and
the `argent` CLI reading from another process entirely, where an
in-process lock cannot reach. Both writers used a plain `fs.writeFile`,
which opens `O_TRUNC`, so a reader could land between the truncate and
the write. That case is silent: `parseFlow("")` returns `{ steps: [] }`
with no error and `summarize` derives `ok` from "no failures", so a
`flow-execute` racing an append reports a **top-level PASS having
replayed zero steps**. Both writes now go through a sibling temp file
and a rename, so a reader sees either the whole old file or the whole
new one — and unlike the lock, that also holds cross-process.
### `stop-all-simulator-servers` gets a `devices` scope
Every agent is told to call this at session end, and unscoped it walked
the whole registry — so agent A wrapping up tore down agent B's devtools
mid-recording, degrading B's flow to brittle coordinate taps. It now
takes an optional `devices` scope and reports `unmatched` ids, so a
mistyped id doesn't read as a clean machine.
Which namespaces count as "owned by a device" turned out to be the
load-bearing part, because nothing cascades to most of them and
`unmatched` turns any omission into an actively wrong diagnosis — a
correct device id reported as a typo. The set now covers `AXService`
(the in-sim ax daemon, spawned `--timeout 3600`; an iOS session that
only ran boot/launch/describe owns this and nothing else),
`ScreenRecordingSession` (an ffmpeg child plus the touch-visualizer
overlay it enabled on the device), `NativeProfilerSession` (an xctrace
child, or an on-device perfetto process and its trace file) and
`JsRuntimeDebugger` (a bound loopback server, the CDP socket to Metro, a
log handle). The debugger URNs interpose the Metro port —
`<ns>:<port>:<deviceId>` — so the matcher understands both shapes,
consuming only the first colon so a wireless adb serial after the port
still compares whole.
`stop-simulator-server` shared none of this and had drifted: it looked
URNs up with an exact, case-sensitive `services.get()`, so a lower-cased
UDID silently no-op'd there while the scoped stop-all reaped it. Both
tools now share one matcher. Their namespace *sets* stay deliberately
different, documented where they are defined — `stop-simulator-server`
is also the documented recovery for a wedged transport, and widening it
to devtools/AX would make a routine retry drop the native-devtools
connection another agent's recording depends on, which is the hazard the
`devices` scope exists to prevent.
## Verification
**End-to-end, three devices, one shared tool-server** (iPhone 16 Pro,
iPhone 17 Pro, Pixel 3a): three interleaved recordings, plus an
unrelated `flow-execute` in a fourth project while all three were live.
Both iOS devices held their own live native-devtools concurrently (two
per-UDID sockets, `connected: true`). Each YAML contained exactly its
own steps in order, taps captured as selectors rather than coordinates,
and neither picked up another's or the third root's path. All three
replayed green — also through the **released 0.17.0 CLI** against the
modified server, which checks wire compatibility. A device-scoped
teardown of sim 1 left sim 2 and the emulator running.
Re-run against the final build:
- Two interleaved recordings across two project roots on one device,
with a **separate process** polling one of the flow files as fast as it
could for the duration. Zero empty or truncated observations; each YAML
held exactly its own steps; both replayed green (6/6 and 5/5). Against
the pre-change in-place write the same reader observes zero-step reads
interleaved among the real ones — the silent-PASS input.
- A scoped teardown of the iPhone reaped `AXService` and
`NativeDevtools` for that UDID only, reported no `unmatched`, and left
the second sim and the emulator running. Lower-casing the UDID matched;
a genuinely bogus id was still reported.
- With a screen recording live on the Pixel 3a, a scoped teardown reaped
`ScreenRecordingSession` and the **ffmpeg child actually exited** (1 →
0). Before the fix it survived to the 180 s cap while the tool called
the correct serial a typo.
The pre-change repros were re-run afterwards to confirm observed
behavior changed, not just the tests.
`npx vitest run` in `packages/tool-server` (295 files, 3154 tests) and
`packages/argent-cli` (280), `tsc --build`, `tsc --noEmit` on both test
projects, eslint and prettier — all green.
## Review notes
Several rounds of review found real defects in this change; each was
reproduced before fixing and re-verified after. Worth knowing when
reading the diff, since the tests exist to pin them:
- `flow-start-recording` truncated the `.yaml` outside any lock, so a
step from the take being discarded landed in the freshly reset file and
was reported as success (26-29 of 200 runs).
- `flow-finish-recording` never took the lock; its `await fs.readFile`
was a yield during which a concurrent append committed, leaving the
reported summary disagreeing with disk (11-16 of 200).
- A failed finish destroyed the recording. Hand-editing the `.yaml`
mid-recording is a documented workflow, so `parseFlow` can legitimately
throw — and the error told the agent to call `flow-start-recording`,
which truncates the very take it would recover.
- `unmatched` reported a device this session had just stopped, because
`disposeService` returns a node to IDLE and keeps it.
- A bare IP claimed every wireless-adb device at that address, since an
adb serial is itself `ip:port`.
- The eviction backstop stamped with `Date.now()`; ties at millisecond
resolution let it drop the session that was just used.
- `requireRecordingSession` ended with "Call flow-start-recording first"
— reached for a key that was finished, superseded or evicted as well as
one never started, and on those branches the file on disk is fully
populated while that call truncates it and reports no `restarted`.
- The atomic write regressed twice on its own terms: its scratch name
was derived from the flow file's basename, which has no length cap, so a
long flow name that appended fine before failed `ENAMETOOLONG`; and the
cleanup guard began after the temp file was created, leaking a scratch
file into a committed directory on any write that failed after opening.
- Two tests passed against implementations they claimed to reject: the
`stop-simulator-server` narrowness guard used a udid that classifies as
*android*, where the iOS-only services it guards can never appear, so
widening the iOS branch kept it green; and the client-mode finish
asserted only the *shape* of `savedTo`, never its content, though that
directive is the only thing that lands the file in client mode.
## Notes for reviewers
- Client/remote persist mode is the least-exercised path, and now has
its own concurrency tests (overlapping appends, and a restart
superseding an in-flight one) — it previously had none. The
`project_root` key is byte-stable across the file boundary (`kind:
"probe"` passes it through unchanged), which is what keeps a remote
recording addressable from `flow-add-step`, which declares no file
input.
- Two spellings of one flow file still mint two sessions — and two
independent locks — over one file, which bypasses every guarantee here.
Two ways in: the root spelled two ways (a symlink, or a case variant on
APFS), and the likelier one, the *name* cased two ways on a
case-insensitive volume. Neither is normalized away because the correct
normalization is the filesystem's: case-folding the key would wrongly
merge two distinct flows on ext4, and resolving symlinks is impossible
in client mode, where the root does not exist on this host. Documented
at `getFlowPath`.
- No recording tool can detect that another agent took its key.
`flow-add-step`, `flow-add-echo` and `flow-finish-recording` all
re-resolve the key on every call, so after a takeover they act on
whichever take is now live and report success: an append lands in the
other agent's file, and a finish finishes and clears it. The only case
that fails loudly is the narrow one the liveness check covers - an
append already IN FLIGHT when the restart lands. There is no caller
identity to key ownership on (see the rejected alternatives above), so
this is spelled out for the agent under "Pick a name unique to your
task" in `argent-create-flow` rather than mechanized.
- Skew hazard in one direction only: a *new* client against an *older*
tool-server. Zod strips the undeclared `name`/`project_root`, and the
old server falls back to its module globals — so concurrent recordings
silently collapse onto one session. The other direction (released 0.17.0
CLI against this server) was verified end-to-end.
- `packages/argent-private/docs/reference.md` still describes
`flow-add-step` as appending to "the current recording", and names two
tool ids that do not exist (`flow-insert-echo`/`flow-run` are file
names; the ids are `flow-add-echo`/`flow-execute`). It is a submodule,
so the fix cannot ride on this branch.
- A restart reported `discardedSteps` from the superseded session's
in-memory flow while it truncates the *file*. Hand-editing the `.yaml`
mid-recording is documented, so the two diverge: four steps wiped, one
reported. It now counts the file, and reports no number at all when that
file cannot be read or parsed - 0 is the answer a genuinely empty take
gives. Client mode still counts from memory (this host cannot see the
client's file), and the description now says which mode it is promising.
- `ChromiumJsRuntimeDebugger` cascades from `ChromiumCdp` and was
therefore already being torn down - silently, while `NetworkInspector`
and `ReactProfilerSession` cascade the same way and were named.
`stopped` is documented as the services that were live and got shut
down, so it is listed now too.
- The client-mode case named for a superseded in-flight append restarted
*between* calls, so the next append re-resolved the key and succeeded -
the guard was never reached, and neutering it left the whole file green.
It now parks an append in its live sub-tool call across the restart.
- `stop-tools.test.ts`' mock handed the tool the live service map where
the real `getSnapshot` copies, so a cascade rewrote the state the sweep
was still reading and the answer depended on map insertion order. The
mock copies and recurses like `_teardown` now, and the cascade case runs
under both orders.
- Prose the code contradicted, each verified against source before
rewriting: Vega owns `JsRuntimeDebugger`/`NetworkInspector` once the
debugger has run (and can match anything at all through an ERROR node);
"nothing here cascades" was denied twice in its own file; an iOS
boot/launch/describe session owns `NativeDevtools` too; `:tcp` is
mintable but unreached in production; the tool-server is a singleton
*per install bundle*, not per machine; `argent flow list` does enumerate
`.argent/flows`; the stop-tools comments narrated a "before" that only
existed on this branch.
Found while verifying, out of scope, worth their own issues: a `tool:
flow-execute` self-reference recurses past both cycle guards (`run:` is
guarded, a nested `flow-execute` reseeds the stack); react-profiler
artifacts collide host-wide on a second-resolution filename;
`stop-metro` is port-scoped and kills another agent's bundler;
`debugger-reload-metro`'s HTTP fallback broadcasts to every attached
client. A tool's `capability` is enforced only by the HTTP layer, not by
`registry.invokeTool`, so a call that reaches the registry another way -
`flow-add-step` takes `command` as a bare string - can resolve a service
the tool declares it does not support, leaving an ERROR node behind
(e.g. `SimulatorServer:<vega serial>`).
---------
Co-authored-by: Hubert Gancarczyk <claude-hubert.gancarczyk@swmansion.com>
|
||
|
|
6d907cc86b |
feat(flow): boot a Chromium instance per launch step (#585)
A `launch:` on Chromium now starts its own Electron instance, so nested e2e flows and mid-flow relaunches work as they do on iOS/Android. Previously the runner booted one app per run and rejected any second chromium launch. A chromium device id _is_ its CDP port, so a launch that starts an app from scratch has to boot one and move the run onto it: `state.device` became reassignable (steps already read it per call), and `state.owned` tracks what the runner booted, torn down in reverse at run end. An instance the runner did not boot — pinned via `--device`, or auto-detected — is attached to, never killed. Relaunching the same app kills its instance and waits for the process to actually exit, or the replacement loses a race against the app's single-instance lock. The runner needs a device before step 1, so `resolveRunDevice` follows a leading `run:` chain to find the launch the run _begins_ with: a fragment composing a chromium e2e flow now boots instead of failing with "No booted chromium device found". A launch declaring no app id for the run's platform stays an error — the run's platform is fixed start to finish. Also fixes a pre-existing teardown leak: the spawned handle is the npm `electron` node wrapper, not Electron, so signalling it left the app and its helpers running (reparented to launchd, still in the dock). Teardown now sweeps the child's process group. **Behavior change:** a fragment leading with `run:` into a chromium e2e flow now boots a fresh instance where it previously attached to a running one; pass `--device` to keep attaching. ## Tests Cover the boot/attach/settle three-way, teardown ordering and the group sweep, device rebinding after a nested launch, same-app retire ordering, cancellation mid-boot, both "no chromium app declared" guards, and the leading-`run:` chain (multi-hop, cyclic, no-launch). The guard, exit-wait and sweep tests are mutation-checked. |
||
|
|
566663bde4 |
chore: bump to 0.19.0 (#741)
Version-only bump of the workspace to 0.19.0, same shape as #603 (the 0.18.1 bump). ## What changed - All 16 packages under `packages/*` → `0.19.0` - `package-lock.json` — regenerated with `npm install --package-lock-only --ignore-scripts` on Node 24, matching what the Lockfile job runs. Version-only: 16 insertions, 16 deletions, nothing else. - `server.json` — the MCP registry manifest, in **both** places it carries a version: the top-level `version` and `packages[0].version`. It lives outside `packages/*`, so a workspace sweep leaves it behind and the registry then rejects the publish because `packages[].version` names a version npm has never seen. ## Verification - `npm run check:versions` → "All workspace packages and server.json are at 0.19.0." - `npx prettier --check` clean on every touched file - No `"0.18.1"` string left anywhere in the repo No code changes. |
||
|
|
22033e1418 |
feat(flow): allow to run every flow in a directory from flow run (#576)
`argent flow run` now accepts a directory and runs every flow in it sequentially: ``` argent flow run .argent/flows --recursive ``` Discovery uses the same acceptance rules as `flow list` (lowercase `.yaml`, safe stem, readable file), sorted for a deterministic run order. `-r`/`--recursive` walks subdirectories too, skipping dot-directories and `node_modules` and never following directory symlinks. A directory named `foo.yaml` still batches (stat wins over extension). Batch output is failures-only: each flow prints a `[i/n] <path>` header, its failing/errored steps — numbered exactly as a single-mode rerun would number them — and a per-flow summary; the run ends with a `PASS/FAIL — N flows: X passed, Y failed, Z skipped` verdict. `--json` prints one aggregate object with per-flow reports. Exit code reflects the batch (0 pass, 1 any failure, 2 discovery/config errors). `--output` exports keep the single-run `<output>/<flow>/` layout, keyed by subdirectory in recursive runs so same-stem flows in different directories can't clobber each other's snapshots. **Failure semantics:** a flow failing its steps — or one the tool-server rejects as invalid (bad YAML, unparseable step) — fails alone and the batch continues. An infra error (transport death, unclassified failure) stops the batch and counts the remaining flows skipped. To make that distinction possible, tool-error responses now carry the server's failure signal (`error_code`/`error_kind`) on the wire — on the streaming terminal error line and on JSON error bodies — and the tools client surfaces server-answered errors as a typed `ToolInvocationError`. Against a pre-signal server this degrades to stopping on any throw. Not breaking: single-file runs behave exactly as before. |
||
|
|
cc2f907ce9 |
feat(flows): resolve run: directives from explicit YAML paths (#567)
A `run:` value is now a YAML path resolved against the directory of the flow file that contains the step, matching the CLI cutover: ```yaml - run: login.yaml - run: ../shared/login.yaml # cross-directory composition now works ``` Bare names fail at parse with a migration hint (`run: login` → `run: login.yaml`). The filename stem stays the fragment's report attribution; reports also carry the as-written path as `target`. Cycle detection compares realpath-canonical paths (same stem in two dirs ≠ cycle; same file via symlink/spelling = cycle). Snapshot baselines are unchanged — keyed by and stored beside the root flow. An uploaded root flow with `run:` steps is now rejected up front with a contract error instead of a mid-run temp-dir ENOENT (its referenced files stayed on the client, and relative resolution in the temp dir could reach host files); same for a relative chromium app path. The recorder emits the sibling form (`run: <name>.yaml`). **Breaking:** existing `run: <name>` directives fail at parse until renamed to `run: <name>.yaml`. Snapshot baselines for a saved flow whose YAML is a symlink now anchor beside the real file (the symlink target's directory) — move any committed `.argent/flows/__baselines__/<flow>/` set beside the real file once, or the next run fails with a missing baseline. A `run:` target must also resolve inside the project root or beside/below the flow file that references it (arbitrary host YAML is no longer reachable), and an uploaded flow with a `snapshot` step is rejected up front (its baselines cannot anchor to the per-call temp dir). |
||
|
|
a644d52d39 |
feat(flows): run CLI flows from explicit paths (#568)
`argent flow run` now takes an explicit YAML path instead of a saved-flow name: ``` argent flow run .argent/flows/checkout.yaml --platform ios ``` The path may point anywhere on the local filesystem; the filename stem names the run's report/artifacts, so it must be `[A-Za-z0-9_-]+` with a lowercase `.yaml` extension. A bare name fails with the exact replacement command, and `argent flow list` prints paths instead of names. Because sibling `run:` files and `__baselines__` resolve beside the YAML, the CLI now requires the auto-started local tool server and refuses `ARGENT_TOOLS_URL` / `argent link` routing. The `flow-execute` tool stays remotely callable via `name` + `project_root`, and gains a `flow_path` parameter (exactly one of `name`/`flow_path`) that is only honored when the file-input boundary verified it as a real host path — uploads and boundary bypasses are rejected, so it can't execute arbitrary server YAML. Also: the flow-name pattern is now a single shared export from `@argent/registry`, and the recorder composes a `flow_path` call into a `run:` step when it targets a sibling of the recording. |
||
|
|
19782f4e1a |
feat(secrets): resolve {{secret:…}} from .env files, not just the environment (#734)
## Why
A credential could only reach a typing tool through an
`ARGENT_SECRET_<NAME>` environment variable. That is the right CI
contract — and stays the highest-priority source — but interactively it
is poor ergonomics: an editor-hosted tool-server inherits the
environment its editor was launched with, so changing a credential means
restarting the editor, and there is nowhere to write one down that
survives a reboot. "Is loading these from a `.env` file on the roadmap?"
is the recurring ask.
## What
`{{secret:NAME}}` now resolves through an ordered chain, first match
wins, re-read on every resolution — so editing a file applies to the
next keystroke with **no server restart** (the same runtime-read design
`ios.additionalDeviceSets` already uses).
| # | Source | Which keys it exposes |
| - | ------ | --------------------- |
| 1 | `ARGENT_SECRET_<NAME>` in the environment | prefixed variables
only — unchanged, still wins |
| 2 | `<project>/.argent/secrets.env` | every key (gitignore it) |
| 3 | `<project>/.env.local`, then `<project>/.env` | **only
`ARGENT_SECRET_`-prefixed keys** |
| 4 | `~/.argent/secrets.env` | every key, any project |
Covers `keyboard`, `paste`, `run-sequence` keyboard steps, and flow
`type:` steps — all four already funnel through the same resolution
point, so nothing else changed.
Also adds `argent secrets [list] [--json]` so a setup is verifiable
without an agent: names only, never values, shadowed names marked, and a
warning when a linked remote tool-server means resolution happens on a
different machine.
## The one design decision worth reviewing
**Row 3 is why this isn't just "read the `.env`".** An agent composes
the tool call, so anything reachable through `{{secret:…}}` is
exfiltratable by a prompt-injected one — type it into a field, then read
the field back. "Which values may argent type" has to stay an allowlist
the user wrote deliberately.
So: a *dedicated* argent secrets file exists for no other purpose and
therefore **is** the allowlist; a file the app *shares* keeps the prefix
as the allowlist, exactly as the process environment does. Your
`STRIPE_SECRET_KEY` sitting in the same `.env` stays unreachable —
verified live, not just asserted. Adding `ARGENT_SECRET_` to one line is
the deliberate act that exposes it.
Two smaller calls, both reversible:
- The chain lives in `@argent/configuration-core`, not the tool-server,
so the CLI reads the same code; the tool-server keeps only the
placeholder engine.
- The project scope is discovered by the **cwd walk-up every other
project-scoped configuration uses**. A per-request project root (header
→ `ToolContext`) was considered and rejected as new protocol surface
across two client paths; reusing flows' module-global
`setActiveProjectRoot` was rejected as a cross-project leak on a reused
server. The cost is that a tool-server whose cwd sits outside the
project skips project files — mitigated by the failure now listing every
path it consulted, and by `~/.argent/secrets.env` needing no project at
all.
## Verification
Unit + integration: tool-server 3122, configuration-core 122 (19 new),
argent-cli 370 (6 new). Lint and prettier clean. All four publish
bundles build.
Live, driving this branch's tool-server on :3001 against iPhone 17 Pro /
iOS 26.4 and the Electron testbed:
- global `~/.argent/secrets.env` → typed correctly; the response echoed
only the placeholder
- prefixed key in the project `.env` → typed; **unprefixed
`STRIPE_SECRET_KEY` in that same file → rejected**
- `.argent/secrets.env` beat `.env`; the environment variable beat both
- edited a secrets file mid-session → applied on the very next call,
same server pid
- a chromium flow with `type: { text: "{{secret:APP_PASSWORD}}" }` → 3/3
pass, asserting on the greeting the app rendered, so the *correct value*
provably landed
- an undefined name inside a flow surfaces the full source list in the
step report
Sample of the failure output (no value ever appears):
```
Unknown secret "LOGIN_PASSWORD" — no source on the machine running the tool-server defines
LOGIN_PASSWORD. Available secrets: APP_USER.
Sources consulted, first match wins:
1. environment (ARGENT_SECRET_*) — 0 secrets
2. /path/to/project/.argent/secrets.env — found, 1 secret
3. /path/to/project/.env.local — not found
4. /path/to/project/.env — found, 1 secret
5. /Users/me/.argent/secrets.env — not found
To make it available, ask the user to add `LOGIN_PASSWORD=…` to …
```
## Out of scope
No general flow variable/parameter system — a non-secret external value
(a test account's email, a staging tenant id) can use the same
placeholder, but argent treats every such value as a secret: redacted
from errors, never echoed, so it is not for something a report should
show. No config key pointing at an arbitrary secrets file. Remote
(`argent link`) still resolves on the remote host, which the CLI now
says out loud.
|
||
|
|
2278115028 |
fix(flow): run a device-less flow without a device, and say why its counts are zero (#649)
Fixes #644. ## Before ``` $ argent flow run echo-only # .argent/flows/echo-only.yaml: two `echo` steps [Tool:flow-execute] No booted device found. Pass a device id or platform explicitly. Available devices: 18BE573F-… (ios, Shutdown), <70 more> ``` and when a device *was* booted: ``` PASS on 18BE573F-… — 0 passed, 0 failed, 0 errored, 0 skipped ``` ## After ``` $ argent flow run echo-only # nothing booted Flow "echo-only" › first step › second step PASS — 0 passed, 0 failed, 0 errored, 0 skipped (no test steps) ``` ## Symptom 1 — the device Whether a device is needed is decided from the flow's own steps, before resolution. Classification is **per step kind and defaults to needing one**, so a kind added later inherits today's behaviour instead of silently running against no device — and the `never` binding makes leaving a new kind unclassified a compile error, backed by a test keyed on the step union so it fails to build rather than to run. Two classifications worth calling out: - **`when` always needs a device**, whatever its body contains, because the guard reads one itself (its platform, or its view tree). A `when: {platform: ios}` block containing only narration still resolves. - **`run` needs one without the fragment being read here.** The named flow is resolved at run time; resolving it a second time would duplicate that lookup and could disagree with it if the file changed in between. A deliberate over-approximation: composing a narration-only fragment still resolves a device. Stated in the code and in the tool description. For `tool:` steps the tool's own schema decides, via a superset of the keys the runner injects — `device` is included because a nested `flow-execute` step drives a device without being handed the run's own. A tool that declares no input at all (`stop-all-simulator-servers`) is device-free, and reading its absent schema must not throw — it previously would have. `ExecState.device` is nullable, which is the safety mechanism rather than just typing: the compiler enumerates every site that acts on a device, each of which now says so via one `deviceEnv()` narrowing. `flow-actions.ts`, `flow-visual.ts` and `ActionEnv` are untouched. The executor also re-checks each step against the **same predicate**, so if the scan and the executor ever disagree the step reports it instead of failing obscurely deeper in. A run that resolved no device reports `device: ""`, and neither renderer claims it ran somewhere. ## Symptom 2 — the counters Reproducing this showed the issue's framing is slightly off: `--json` doesn't only disagree with the text, it disagrees with **itself** — counters all zero while its own `steps[]` marks both steps `"pass"`. So the CLI text was a faithful rendering of the server's counters. **The counters are right and stay as they are.** Narration is deliberately excluded, and it should be: after a hard stop every remaining step *including narration* is reported skipped, so counting it would inflate `skipped` on every real failing run — a worse and far more common lie than the vacuous one being reported here. It would also contradict the JUnit model in #578, where echo is `<system-out>` and not a `<testcase>`. What was missing was saying *why* zero. Both the CLI and the MCP renderer now append `(no test steps)`, and only on a pass — a cancelled run can be a FAIL with all four counters zero, where "no test steps" would read as though the failure had no cause. No wire format changes: `steps[].kind === "echo"` already distinguishes counted from uncounted steps, so the JSON is self-describing without widening the status enum. ## Conflicts, for whoever lands second This is a hot area — seven open PRs touch `flow-run.ts`. - **#578** rewrites the CLI summary line I also change (one hunk, one 7-line function, semantically independent — resolution is keep-both), and its `attachFailureDiagnostics` dereferences `env.device.platform`. **It will need a null-device guard**, or every failure in a device-less flow silently degrades to `read-failed` diagnostics. Happy to rebase onto it rather than the reverse. - **#585** rewrites `resolveRunDevice` at the same seam and edits the same description sentence. - **#538** rewrites `flow-device.ts`. Worth noting it makes `resolveFlowDevice` reject a physical iPhone for flows — skipping resolution means a narration-only flow now passes on a physical-iPhone-only host, which I think is desirable but is a second behaviour change on this seam. All new tests are in new files, since every existing flow test file in the blast radius is rewritten by an open PR. ## Behaviour changes a reviewer should weigh - A device-free flow no longer prints `PASS on <id>` **even when a device is booted**. That is the point: otherwise an identical flow reports differently in CI (no simulator) than on a laptop with one open. An explicit `--device` is still honoured and still named. - `--platform` is ignored for a device-free flow, where it previously errored with nothing booted. - A 0.18.1 CLI against a newer server would print `Flow "x" on ` — cosmetic version skew. ## Verification Live against the built CLI: device-less flow with nothing booted (the reported bug) and with a device booted (identical output, no ambient attribution); `--json` showing `device: ""`; explicit `--device` still naming it; a mixed flow and a composed flow both still demanding a device; a mixed flow with a device booted still reporting `PASS on <id> — 1 passed`. 24 new tests. tool-server 3103 passed, CLI 284 passed, MCP 78 passed — no existing test modified. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- > **#697 is stacked on this branch** — it conflicts with this one in `flow-run.ts`'s import block, so it was rebased on top. Merge this first. |
||
|
|
81de9f66a6 |
fix(config): say what a rejected value should have been, and prune the group it left behind (#650)
Fixes #643. ## Before / after ``` $ argent config set ios.additionalDeviceSets "~/Library/Developer/Radon/Devices" --scope project Error: Invalid value for config key "ios.additionalDeviceSets". # ← the whole message ``` ``` $ argent config set ios.additionalDeviceSets "~/Library/Developer/Radon/Devices" --scope project Error: Invalid value for config key "ios.additionalDeviceSets": expected an array of strings. Did you mean: argent config set ios.additionalDeviceSets '["~/Library/Developer/Radon/Devices"]' --scope project Run `argent config list` to see each key's expected value. ``` ``` $ argent config unset ios.additionalDeviceSets --scope project $ cat .argent/config.json { "ios": {} } → {} ``` ## Symptom 1 — the message The issue names three gaps: no expected type, no example, no pointer to `config list`. All three are closed, plus one the issue didn't ask for and I think is the actual fix. **The suggestion carries the user's own value.** A generic `Example: … '["~/DeviceSets/ci"]'` would hand someone a path they have never heard of and expect them to spot the difference. Where the correction is mechanical — a single item offered to a list-valued key — the error offers back what they typed, wrapped, shell-quoted, and **on the scope they asked for**. Pasting it works; verified live that it lands in project scope, not global. Nothing is coerced. The issue floated accepting a bare string and storing it wrapped; I didn't, because `config set` persists, gets committed, and is read by other machines, so a value silently stored in a shape the user didn't type is expensive to debug and `coerceCliValue` already has one silent fallback. Showing the corrected command costs the user one keystroke and keeps the store honest. If a reviewer prefers coercion, this is the smaller change to build on. **The expected shape comes from the validator, not a per-key string** — `asStringArray` *is* "an array of strings", so swapping a key's `parse` swaps its description with it and the two cannot drift. A bespoke validator can override via a new optional `expected`, and degrades to today's exact message if it says nothing. **`config list` states it too.** The issue notes `list` doesn't close the gap either — its description describes the *elements* but never says the value is a list. It now shows `value: an array of strings, e.g. ["~/DeviceSets/ci"]`, and `list --json` carries both fields. `telemetry.enabled` deliberately gets no example: `setConfigValue` refuses it before validating (it's managed by `argent telemetry`), so a "here's how to set it" hint would point at a command you may not run. A schema guard test keeps that honest as keys are added — and asserts every example **actually round-trips through its own validator**, so an example can never ship that reproduces the error it exists to fix. ## Symptom 2 — the empty group Deleting a key now removes any container the delete emptied, stopping at the first ancestor that still holds something. **This is file hygiene, not a behaviour change.** I traced every raw reader of the document — `readConfigObject`, and telemetry's `consent.ts` and `notice.ts`, which each do their own read — and an empty parent is already indistinguishable from an absent one: `getAtPath` returns `undefined` either way, before `parse`, so the merge layer never sees a container at all. The only observable difference is the bytes on disk, which is exactly the complaint. Guards worth noting: - **The root always survives** — an emptied config is `{}`, never a deleted file. `.argent/` is itself a project-root marker, so removing the file wouldn't restore the prior state anyway, and `updateConfig` is a shared publish path with a lock/rename protocol that has no defined meaning for file removal. - **A no-op unset still doesn't touch the file.** That fast path exists so an unset that removes nothing can't materialise a project config and dirty git status; pruning lives inside the mutate callback, which the fast path returns before reaching. So a group someone deliberately left empty by hand is never tidied away — pinned by a test asserting the file stays byte-identical. - Siblings at any level stop the unwind, including an empty array value, which is preserved. **Two existing tests asserted the old behaviour and now assert the new one** — `config-access.test.ts:47` and `config.test.ts:109` (the latter is `clearRememberedAgent`'s sibling-preservation test, so it doubles as proof siblings survive). ## Known same-class case, not fixed here `resetFirstRunNotice` (`packages/telemetry/src/notice.ts`) does its own inline delete and still leaves `{"notices":{}}`. It doesn't go through `deleteAtPath`, so it doesn't inherit this. It's a one-line change to route it through the same function — happy to do it here or as a follow-up, but it pulls the telemetry package into a config PR so I left it out by default. ## Verification Live against the built CLI: the reported case; pasting the suggestion back and confirming it lands in **project** scope; unset leaving `{}`; a sibling key surviving; a non-array key correctly getting the generic example and *no* bogus "Did you mean"; `config list` output; `telemetry.enabled` still delegating unchanged. configuration-core 103 passed, argent-cli 285 passed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
3fff176a32 |
fix(cli): let a boolean flag take the value you typed (#699)
Fixes #586. ## The bug `--includeChildren false` left the flag **true** and silently dropped the word, so the tool ran with the opposite of what was asked. The reporter spent a while chasing a filter bug that did not exist — the output was simply the unfiltered kind. Reproduced on 0.18.1, device-free, with a discriminator that shows the flag's value directly: ``` $ argent run screenshot-diff --udid U --baselinePath A.png --currentPath B.png --captureCurrent=false { "summary": "Screenshot diff summary … pixel_mismatch: 1.79% …" <-- false, diff runs $ argent run screenshot-diff --udid U --baselinePath A.png --currentPath B.png --captureCurrent false [Tool:screenshot-diff] Provide either currentPath or captureCurrent, not both. <-- stayed TRUE ``` That message is the whole problem in one line: the user typed `false`, and the tool refuses on the grounds that they asked to capture. ## Why the old behaviour existed, and why it does not need to The parser deliberately declined to look ahead: > `// Bare boolean flag: true. Don't consume next argv to avoid surprising behavior when a positional follows.` That protects a case which does not exist. `parseFlags` has exactly one caller, and **`positional` is never read anywhere in the repo** — every bare token is collected and dropped, for booleans and for every other type. There is no positional to steal. The module header had documented the space form as legal all along: ``` // --name value (string / number / integer / boolean-with-value) ``` This matters because several tool booleans **default to true**, so `false` is the only way to say what you mean: `screenshot.includeImageInContext`, `network-request.includeBody`, `debugger-component-tree.onScreenOnly`, `debugger-inspect-element.resolveSourceMaps`. ## What changed A boolean flag consumes a following token when it is exactly the word `true`/`false`, case-insensitively. `1`/`0` and everything else are left alone — a bare number after a switch is more likely an unrelated argument than a value, and guessing at an ambiguous token is what caused this bug. `--flag -- false` still keeps the word positional. Three consequences are handled here rather than left to bite later: - **`argent run` now reports arguments it ignored.** This is what stops `--flag 0` and `--flag yes` being the same silent inversion, in a syntax users are now being taught works. stderr, so `--json` stays parseable. - **`--no-flag true` / `--no-flag false` now name the positive form** instead of quietly meaning false. One is a contradiction, the other a double negative. - **`--flag=True` used to throw** while `--flag True` would now work — the inline coercion was case-sensitive and did not trim. Both paths now share one helper, so a single word cannot get two answers one function apart. The **flow** and **lens** parsers take switches that accept no value at all, and would have silently ignored the syntax their users just learned elsewhere. They now refuse it — in the flow parser that also stops `false` being swallowed as the flow name, since its first bare token is the flow to run. `--help` gains **one legend line** for tools with a boolean field, rather than widening the flag column on every field of every tool: ``` Booleans: --flag or --flag true sets true; --flag false, --flag=false, or --no-flag sets false. ``` It deliberately does not begin with `--`. `scripts/e2e-full/lib/discover-tools.sh` reads any such line in the `Flags:` section as a flag row and takes the first `--token` as its name, so a legend starting with a flag would inject a phantom flag into every tool model it builds. **There is a test pinning that shape**, verified live: running the harness's awk over the new help output yields exactly the real flags and no legend row. ## Verified live ``` --captureCurrent false -> diff runs (was: "Provide either currentPath or captureCurrent, not both.") --captureCurrent=false -> unchanged --captureCurrent 0 -> "Note: ignoring unused argument(s): 0. Pass values as --flag <value> or --flag=<value>." screenshot --help -> legend present; harness awk parses only the real flags ``` 25 new tests; 8 confirmed to fail before and pass after. The rest pin what deliberately did *not* change: bare flag still true, a following flag or non-boolean word left alone, `1`/`0` still positional in the space form, `--` escape, string and unknown fields untouched, `--no-flag` semantics, boolean arrays, last-write-wins. The parser had **zero** boolean coverage before — which is why this survived. Full CLI suite 298 passing; lint, prettier and `typecheck:tests` clean. ## Scope and neighbours Only `flag-parser.ts` (the schema-driven parser for `argent run`) changes semantics. The other CLI parsers take fixed, default-off switches where `false` has no coherent meaning; three of them already reject a stray token outright, and the two that did not now do. `flow.ts`'s deliberate rejection of `--flag=value` is untouched. **#648** also edits `flag-parser.ts` (rewrites `renderFlagName`, inserts helpers at the same anchor). Behaviourally compatible — it keeps boolean → bare `--name` — so this is a textual conflict only, and either order works. ## Found while here, not fixed `scripts/e2e-full/lib/discover-tools.sh:60-64` detects flag kinds with `\bboolean\b` in awk, where `\b` is **backspace**, not a word boundary — so it never matches and every non-enum flag is typed `unknown`. That makes the `bad-type` validation case downstream unreachable. Pre-existing and unrelated to this fix; filing separately. --- > **Stacked on #648** (`filip/run-cli-validation-messages`). Both rewrite the same region of `packages/argent-cli/src/flag-parser.ts` — #648 factors out `flagNameFor`/`isJsonField` and rewrites `renderFlagName`, this one adds `booleanWord` beside them and a boolean legend to `formatSchemaUsage`. Merging them independently conflicts; this branch is rebased on #648, so review only the top commit. GitHub retargets it to `main` when #648 merges. --------- Co-authored-by: Filip131311 <f.kaminski2000@gmail.com> |
||
|
|
a6f1bef632 |
fix(cli): stop argent mcp --help starting the server, and argent tools --help dumping the catalogue (#656)
Fixes #638. ## The report understated it The issue says `argent mcp --help` "does not hang and does not start the server — it simply prints nothing". Reproducing it showed the opposite: ``` $ argent mcp --help < /dev/null ; echo $? 0 # 0 bytes ← what the reporter saw $ argent mcp --help # stdin open (a pipe, an editor, a CI step) # HANGS — I hit a 300s timeout ``` `--help` **starts the MCP stdio server**, which blocks reading JSON-RPC from stdin. The "prints nothing, exit 0" is just the server getting an immediate EOF from a closed stdin. So this is the same "`--help` runs the real command" bug that was fixed for the installers — `mcp` was missed. `startMcpServer` takes no argv at all (`mcp-server.ts:96` — its only parameter is `{ paths }`), so a help flag was *structurally* unreachable; there was nowhere downstream that could have handled it. ## Fix `mcp` joins the existing guarded set rather than growing a second help path. That set's defining property is "the handler never inspects its argv", and `mcp` qualifies more strongly than the installers do — it isn't merely ignoring `rest`, it is never handed it. **It cannot fire on a real launch.** The guard tests `rest`, which is `[]` for every generated editor config (all four shapes end at `mcp`, and `isArgentManagedEntry` requires exactly that), and `[].some(...)` evaluates no predicate. This is pinned in both directions, because a guard that *did* fire here would print usage onto the JSON-RPC channel and break every editor at handshake — the highest-cost regression in this file, and it was untested. The top-level table's `mcp` row now reads the same metadata as its own help, so the two cannot drift. **No rename.** An earlier draft renamed the module and six identifiers (`installer-help` → `command-help`, etc.) on the grounds that its comments had become false. They hadn't quite: fixing `mcp` *and* `tools` makes "every other subcommand parses `--help` itself" true again — I checked all ten siblings. Since #530 and #568 both edit `cli.ts`, a rename would have forced them through churn for no behavioural gain. Prose-only. ## Second commit: `argent tools --help` The issue mentions it in passing; it's a different mechanism and a genuinely dead branch. `argv.filter((a) => !a.startsWith("--"))` dropped `--help` before the subcommand was read, so it fell through to listing all 77 tools — **contacting the tool-server, starting one if none was running**, to do it. Only `-h` ever reached the existing help branch. Scoped to the subcommand position on purpose: a global check would have broken `argent tools describe <name> --help`, which today prints *that tool's* flags. It still does — there is a test. Separate commit so it can be reverted alone. ## Verification Live, against the built CLI: | | before | after | |---|---|---| | `mcp --help`, stdin open | hangs indefinitely | usage, returns immediately | | `mcp --help`, stdin closed | 0 bytes, exit 0 | 849 bytes of help, exit 0 | | `mcp` (real launch) | works | works — verified with an actual `initialize` handshake, server replies with its capabilities | | `mcp --metro-port 8082` | starts server | starts server (guard keys on help, not on "any argv") | | `tools --help` | 77-line catalogue | usage | | `tools describe gesture-tap --help` | tool's flags | tool's flags | | `argent --help` `mcp` row | — | byte-identical | argent 80 passed, argent-cli 284 passed. ## Note on the help text Every claim in it is sourced (stdio/JSON-RPC transport, stdout ownership, the `~/.argent/mcp-calls.log` default and `ARGENT_MCP_LOG` override, auto-spawn vs `ARGENT_TOOLS_URL`/`argent link`). It deliberately does **not** suggest running `argent mcp` by hand to check an install — that is the reported hang — and instead says it will look like it has hung and why. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
d08c3a3cfc |
fix(cli): name the missing flag instead of printing a Zod dump (#648)
Fixes #645. ## Before ``` $ argent run gesture-tap [ { "expected": "string", "code": "invalid_type", "path": ["udid"], "message": "Invalid input: expected string, received undefined" }, ... two more ] ``` ## After ``` $ argent run gesture-tap Error: missing required flags --udid, --x, --y argent run gesture-tap [flags] ...full per-flag help... ``` ## Scope The issue names the missing-flag case. Reproducing it showed the same raw dump has a second trigger: any constraint the tool itself rejects, which the CLI cannot know in advance. | invocation | before | after | |---|---|---| | `gesture-tap` (no flags) | raw issue array | `missing required flags --udid, --x, --y` | | `screenshot --scale 99` | raw issue array | `--scale Too big: expected number to be <=1` | | `gesture-scroll --udid X --x .5 --y .5` (zero deltas) | raw issue array | `Pass a non-zero deltaX and/or deltaY …` | | `run-sequence --udid X` | raw issue array | `missing required flag --steps-json` | | `gesture-tap --x abc` | already good | unchanged | | runtime failure | plain message, exit 1 | unchanged | Fixing only the first row would have left the CLI's error voice less predictable than before, so both go through the same renderer. ## How it works Two sources, one output. Missing flags are found locally from the tool's published schema — a **presence** check only, never a type or range judgement, which stays exclusively the server's. That is sound because the schema generator excludes defaulted fields from `required` (`debugger-connect` requires `device_id`, not `port`), so it cannot reject a call the server would have accepted. Everything else is read off the rejection the tool-server already returns. Both produce one report rendered by one function and reported through one call site, so the wording, the output channel and the exit code cannot drift between them. Recognition of a rejection is **structural** — the shape of the issue list plus the tool's own schema — never the wording of a message, which is not ours to depend on and changes between Zod releases. Four gates must all hold, and any failure leaves the existing handling untouched. Details worth a reviewer's attention: - **A rule spanning the whole payload reports no field.** `gesture-scroll` and `gesture-rotate` validate at the object level, and `screenshot-diff` is `.strict()`. Treating those as field-scoped would print `Error: --undefined …`; they are stated on their own instead. - **A rule firing on an *optional* field is not called a missing required flag.** `await-ui-element` refines on `expectedText`, which it does not require — reporting that as missing would contradict the help block printed directly underneath, where the field carries no `(required)` marker. - **The check runs after `--args` is merged**, so `argent run gesture-tap --args '{...}'` is not falsely reported as missing everything. Verified live: it reaches the device and taps. - Presence uses own-key semantics, so a required field named e.g. `constructor` is not treated as supplied by the prototype chain. ## Behaviour changes a reviewer should weigh - **A tool-rejected value now exits 2 instead of 1.** `run.ts` already uses 2 for bad input and 1 for a failed run; a Zod 400 means the tool never ran. No in-repo consumer tests for a specific code — `scripts/e2e-full` only checks zero/non-zero. Note this does **not** make 2 universal: an unknown tool name still exits 1, which is untouched here. - **`--json` gains a defined failure shape** (it had none — the blob printed regardless). One object on stderr, nothing on stdout, so `--json | jq` on a failed run reads an empty stream and a non-zero status: ```json { "error": "missing required flags --udid, --x, --y", "missing": ["--udid", "--x", "--y"], "issues": [] } ``` `issues` carries the tool's list verbatim, which is the raw data the issue asked to keep. - **One adjacent fix**, called out because it is not the reported bug: the global-option parse ran *outside* the error handler, so `argent run screenshot --out` printed a raw stack trace. Same bad-input-to-ugly-output defect, one line above the fix. ## Not done - The unknown-flag hole (`argent run gesture-tap --bogusflag 1` is silently accepted and the tool runs). The pass-through is deliberate — see the comment in `flag-parser.ts` — and belongs with #586, the same CLI-argument family. - `argent flow` builds its own payload and has its own required check; it cannot reach this path. - The server still returns the issue list verbatim, so MCP and other clients are unaffected. All prettifying is CLI-side. ## Verification Live against the built CLI for every row of the table above, plus: `debugger-connect` asks only for `--device_id` and never `--port`; `--args '{...}'` reaches the device and taps; a thrown runtime error still prints its bare message with no `Error:` prefix, no help block, exit 1. 43 new tests. `run-telemetry.test.ts` passes **unedited** — it already asserted the non-Zod passthrough, which makes it the regression guard for the untouched path. Full CLI suite 320 passed, registry 87 passed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- > **#699 is stacked on this branch** — it conflicts with this one in `flag-parser.ts`, so it was rebased on top rather than left to collide at merge time. Merge this first. |
||
|
|
23b9ae5c87 |
chore: bump to 0.18.1 (#603)
Patch bump. First release whose npm tarball carries `mcpName` (added in #598), which unblocks the MCP registry publish (`mcp-publisher publish` rejects `0.18.0` because its manifest predates the field). `server.json` is bumped alongside `packages/*` for the first time — `scripts/check-workspace-versions.mjs` now enforces it (`All workspace packages and server.json are at 0.18.1` locally). | Commit | Change | | --- | --- | | `eea6c27c` | `style`: fix prettier formatting on main (#604) | | `7e4ece69` | `chore`: server.json for the official MCP registry (#598) | | `fc09612f` | `feat(config)`: `recordings.directory` — configurable save location for screen recordings (#601) | | `cccda92d` | `feat(config)`: `ios.additionalDeviceSets` — additive array of extra CoreSimulator device sets (#542) | Version-only diff, rebased on main as of #604. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
cccda92d15 |
feat(config): ios.additionalDeviceSets — additive array of extra CoreSimulator device sets (#542)
## Summary **Stacked on #534.** Adds the first *additive* configuration entry on the new schema — `ios.additionalDeviceSets`, a list of extra CoreSimulator device-set directories — delivering the config side of the #401 discussion on the #534 machinery. ### The schema entry One registry entry in `config-schema.ts`: - key `ios.additionalDeviceSets`, scopes `project` + `global`, `parse: asStringArray`, **`merge: "union"`** Unlike `lens.agent` (project *shadows* global), this is additive: the effective value is the **union of both scopes** — the global baseline first, the project's extras appended, deduplicated. A repo can commit the device sets its tooling needs to `.argent/config.json` and they *extend* (never replace) the user's global list. `argent config list/get/set/unset` handle the array key with zero command changes — arrays already round-trip through the JSON value coercion. ### The runtime reader `getAdditionalIosDeviceSets(options): string[]` in `config-access.ts` — the surface iOS tooling should call. It reads each scope separately so paths normalize against the base the author meant: - `~` / `~/…` expands to the home directory (honoring the sandbox `homeDir` override) - relative **project** entries resolve against the **project root**, relative **global** entries against **home** - output order matches the `union` preset (global first), with **post-normalization** duplicates dropped — `~/DeviceSets/ci` in global and its absolute spelling in the project file collapse to one entry - empty array when unset Support refactor: `resolveHomeDir()` extracted in `paths.ts` (the HOME/USERPROFILE + override logic previously inlined in `argentHomeDir`/`configDir`). ### Out of scope (follow-up) Actually pointing simctl at the extra sets. There is no central simctl wrapper today (~25 call sites build `xcrun simctl …` argv inline, and `ax-prefs.ts` hardcodes the default set's `CoreSimulator/Devices` path), so consuming this reader in `list-devices`/boot/etc. is its own change — the runtime-read-off-disk design here means that consumer needs no CLI/env threading, matching how #534's description framed the split. ## Testing - `configuration-core`: 5 new tests (82 total, green) — additive union across scopes + per-scope file isolation, non-array rejection + entry normalization, reader `~`/relative resolution per scope, post-normalization dedup, empty-when-unset - `argent-cli`: new real-filesystem CLI round-trip (274 total, green) — `config set` an array at each scope, effective `get` shows the union while `get --scope` shows only that scope's entries - `tsc` build + `typecheck:tests` + eslint clean on touched packages |
||
|
|
646f726bc4 |
chore: bump to 0.18.0 (#596)
bump debated myself whether this should be a QoL patch or a minor release and i think the latter makes more sense | Commit | Change | | --- | --- | | `7189333a` | `feat(tool-server)`: human-readable log message (#582) | | `5ac69349` | `feat(flow)`: CSS combinator scopes for flow selectors (#569) | | `e23f0e28` | `feat(flow)`: snapshot `cropOn` for single-element visual diffs (#516) | | `6a53e66a` | `feat(flow)`: rotate directive (#546) | | `68ceb314` | `ci`: cut over npm publishing to the unified trusted-publishing workflow (#571) | | `2546788a` | `ci`: unified publish-npm workflow (#565) | |
||
|
|
a0460eeefe |
chore: bump workspace packages to 0.17.0 (#554)
## What
Lockstep-bump all 16 `packages/*` workspace packages (and the matching
`package-lock.json` entries) from `0.16.1` → **0.17.0**.
Diff is exactly 32 version-field swaps across 17 files — no formatting
or
dependency churn.
## Verification
- **Lockfile CI gate reproduced locally on the exact CI toolchain**
(Node
24.14.1 / npm 11.11.0, matching `lockfile.yml`'s `node-version: "24"`):
`npm install --package-lock-only --ignore-scripts` on top of the commit,
then `git diff --exit-code -- package-lock.json` → **clean**. npm's own
regeneration reproduced the hand-edit byte-for-byte, so none of the
`"peer": true` churn that #513/#539 had to keep out reappears here.
- `node scripts/check-workspace-versions.mjs` → `All workspace packages
are
at 0.17.0.`
- Remaining `repo-hygiene.yml` checks pass: no conflict markers, no
focused
tests.
- `prettier --check` on all 17 changed files → `All matched files use
Prettier code style!`
- **Runtime version actually reports 0.17.0**: `getInstalledVersion()`
(`argent-installer/src/utils.ts:384`) and
`getGloballyInstalledVersion()`
(`topology.ts:94`) both read `version` straight out of the resolved
package
root's `package.json` at runtime, so the bump is the whole mechanism —
no
generated constant or second version file to keep in sync. Exercised
that
resolution against the bumped tree: `PACKAGE_ROOT` → `packages/argent`
(`@swmansion/argent`) → `0.17.0`.
- Verified all 16 lockfile hits belong to workspace packages (parsed the
lockfile's `packages{}` and matched every `0.16.1` entry to a
`packages/*` path) — no unrelated dependency happened to sit at 0.16.1.
- No test asserts a release version string; the only `0.1x.x` matches
under
`*.test.ts` are historical references inside comments.
## Note
`packages/argent-private` needs no pin change here. Its release version
is
carried purely as a git tag — the repo has no version field on the
release
line (its only `package.json` is a `private: true` research CLI on an
independent `0.1.0` line) — and `v0.17.0` is now tagged at `2d6c292`,
which
is **already** the commit this superproject pins. `v0.16.0` and
`v0.16.1`
sit on that same commit, so the submodule entry is correct as-is and
stays
out of this diff.
|
||
|
|
2251c7cf46 |
feat(screen-recording): screen-recording-start/stop tools with per-call stop reminders (#517)
## What Adds a `screen-recording-start` / `screen-recording-stop` tool pair (naming follows `native-profiler-start/stop`) that records the device screen to an mp4 and returns it as a downloadable artifact. Capture runs through **simulator-server** — the same backend `screenshot` and every interaction tool already drive. simulator-server publishes the device screen as an MJPEG stream (`/stream.mjpeg`), so recording needs no platform-specific recorder: the tool-server subscribes to that stream, paces the frames onto a fixed 30 fps timeline, and pipes them into a single ffmpeg process that encodes — and watermarks — straight to the final mp4. The stream is multi-subscriber, so a preview window can stay open on the same device while recording. - **One path for every device simulator-server drives** — iOS simulators, Android emulators, and physical Android devices. - **Native resolution, constant 30 fps** — h264, `+faststart`, no post-processing pass. - Session state lives in a per-device `ScreenRecordingSession` blueprint (mirrors the native-profiler session): time-limit cap with auto-finalize, unexpected-exit recovery (a capture that ended on its own is still retrievable via stop), always-startable state reset on stop, and dispose-on-shutdown cleanup through the registry. ## Why the frames are paced host-side A device only emits a frame when the screen **changes**. Pointing ffmpeg straight at the stream therefore produces a video whose length is the sum of the moments something moved — a still screen collapses to a fraction of a second — and an ffmpeg blocked on a silent socket does not answer a stop signal promptly either (measured: 12 s late). Re-emitting the last frame on a wall-clock schedule fixes both: **video duration always equals real elapsed time**, and stop is a stdin EOF that finalizes in ~60 ms. Duplicate frames cost almost nothing once encoded (a 10 s recording of a static screen is ~1 MB). ## Finishing pass folded into capture The Argent watermark (logo + `By @swmansion`, bottom-left, sized relative to the frame) is stamped by the same ffmpeg invocation that encodes the capture, so there is no second encode and no temp intermediate. Its colour is chosen **per pixel** from the background luma underneath it — the white logo over dark areas, a runtime-derived near-black copy over light areas — so it keeps maximum contrast against whatever is behind it. It sits at 20 % opacity and is opt-out via `argent disable video-watermark` (read when the recording starts). Frame geometry comes from the first JPEG's SOF header, so no `ffprobe` is needed. ffmpeg is now the recorder rather than an optional finisher, so a missing ffmpeg fails `screen-recording-start` up front with an install hint instead of silently degrading the output at stop time. ## Reminder plumbing While a recording is running — or ended on its own but was never retrieved — **every** tool result carries a `note` telling the agent the recording is still going and how to stop it. It rides the same `note` field as the update reminder but is per-call and unsuppressed; both notes compose when they coincide. The note text switches to "already ended, retrieve the file" once the cap fires or the capture dies. ## Skill New `argent-screen-recording` skill (+ routing stanza in `rules/argent.md`): documents the start → interact → stop workflow and instructs the agent to schedule its own reminder/wakeup for the expected end of the recording immediately after starting one (harness reminder tool if available, otherwise a background `sleep` whose completion notification pulls it back). Scores 10/10 on `scripts/grade-skills.mjs`. ## Verification - Unit: 45 tests across `screen-recording.test.ts`, `mjpeg-stream.test.ts`, `watermark.test.ts`, `http-recording-note.test.ts`; full tool-server suite **2808 tests / 280 files green**; `tsc --noEmit -p tsconfig.test.json`, root `tsc --build`, eslint, prettier all clean. - E2E on a live tool-server (`POST /tools/...`), driving real interactions between start and stop: - **iOS sim** (iPhone 16 Pro Max, iOS 18.5): 1320×2868 h264, `r_frame_rate=30/1`, 338 frames, container duration 11.27 s vs reported `durationMs` 11314 — **within 50 ms of wall clock**. Extracted frames show real screen content (mid app-switcher animation) with the watermark rendered light-over-dark. - **Android emulator** (Pixel 3a, API 34): 1080×2220 h264, 30 fps, 353 frames, duration 11.77 s vs `durationMs` 11793 — within 30 ms. 512 KB for a mostly-static home screen. - **Static screen** (no interaction at all, 10 s): 300 frames, duration exactly 10.000 s — the case the previous `recordVideo` implementation reduced to ~0.03 s. - **Long silence then activity** (15 s untouched, then an app launch): stream survived, no warning, frames after the silence show the new screen. This caught a real bug during review — the connect timeout was also acting as a socket *inactivity* timeout and tore the stream down at 10 s of stillness; fixed and covered by a regression test that fails without the fix. - **Time-limit cap**: `timeLimitSeconds: 5` self-finalized at 4.97 s / 149 frames, the per-call note flipped to "already ended (it hit its 5s time limit)", and a stop issued 11 s later still returned the video with the matching warning. ## Not supported (capability-gated with clear errors) tvOS simulators, physical iPhones, Chromium apps, Vega, and remote (`remote:`-prefixed) simulators — none of them expose a readable frame stream. --------- Co-authored-by: Ignacy Łątka <claude-hubert.gancarczyk@swmansion.com> |
||
|
|
8d2a499eaf |
Project-scope configuration with schema-driven merge + argent config (#534)
## Summary Adds a general, **scoped** configuration system on top of the shared `config.json`, plus a universal `argent config` command to manage it. Motivated by the discussion on #401 (iOS device set): config that needs to work naturally with the MCP server should be read off disk at runtime like telemetry consent is — not threaded through CLI flags/env and a kill-and-respawn dance. This lands the reusable machinery; the device-set becomes a one-line schema entry + a runtime reader on top of it. Four pieces, matching the requirements: ### 1. Project-scope config files `config.json` now resolves at two scopes: - `~/.argent/config.json` (**global**, default) - `<project-root>/.argent/config.json` (**project**, resolved by the same `.argent`/`.git`/`package.json` marker walk as feature flags) `configFilePath` / `readConfigObject` / `updateConfig` take an optional `scope` (default `global`), so **every existing caller is unchanged** (telemetry, notices, lens). Per-scope lock + atomic write are preserved. ### 2. Schema-driven merged reads `getConfigValue(def)` reads each scope the value's schema entry allows, merges them under the entry's policy, and falls back to the entry's default. This is the reader consumers should call instead of poking at a single global file. The existing **lens** getters (`getRememberedAgent` etc.) are migrated onto it as the first live consumer — a repo can now pin its lens agent, falling back to the user's global choice. ### 3. A registration file for configurations (`config-schema.ts`) Adding a configuration is a one-liner: `key`, `description`, allowed `scopes`, a `parse` validator, and a `merge` policy. Merge presets: - `prioritize-local` (project wins) · `prioritize-global` (global wins) - `prioritize-restrictive` (booleans/numbers — opt-out/stricter wins) - `union` / `intersection` (arrays) - …plus an escape hatch: pass your **own** `(inputs) => value` function for a bespoke rule. ### 4. `argent config` — one universal command `argent config <list | get | set | unset>` with `--scope global|project` and `--json`. New configs are added as **schema entries**, not new top-level `argent` commands, to avoid bloat. **Telemetry stays backward-compatible** on its dedicated `argent telemetry` command (which also drains/resets the live client). It's surfaced *read-only* under `argent config` via a `manageCommand` guard — so `argent config set telemetry.enabled` refuses and points at `argent telemetry`, and a committed project file can never re-enable a global opt-out (`prioritize-restrictive`, global-only). ## Testing - **Unit / integration**: 71 configuration-core tests (merge presets + custom fn, dotted-path + prototype-pollution guards, scope merge, scope/validation/manage-command errors, `listConfig`, `coerceCliValue`) and 11 real-filesystem CLI tests driving the actual `config()` entry point across sandboxed HOME + project cwd. Full suites green: configuration-core 71, argent-cli 268, telemetry 281, argent 60. - **Live, on the built binary** (`node packages/argent/dist/cli.js …`): set global + project (from a deep subdir) → effective get returns the project value; per-scope gets return each file; both files land in the right place; `argent telemetry disable/enable` writes `config.json` and `argent config get telemetry.enabled` reflects it read-only; unknown-key / invalid-value / bad-scope errors exit non-zero with hints. - `tsc --build` across the workspace, prettier, and eslint (`--max-warnings 0`) all clean. ## Notes / follow-ups - Config-file reads are not yet fingerprint-cached the way telemetry's consent read is; a long-lived tool-server re-reads on each `getConfigValue`. Fine for CLI/low-frequency use; a hot runtime consumer (e.g. a future device-set reader) may want the same mtime/inode cache. |
||
|
|
1f5b155229 |
chore: bump workspace packages to 0.16.1 (#539)
## What Lockstep-bump all 16 `packages/*` workspace packages (and the matching `package-lock.json` entries) from `0.16.0` → **0.16.1**. ## Verification - `node scripts/check-workspace-versions.mjs` → `All workspace packages are at 0.16.1.` - Lockfile diff is exactly 16 version-field swaps — no unrelated churn (the `"peer": true` regeneration noise from a newer npm was again kept out, same as #513). The one remaining `0.16.0` in the lockfile is the unrelated `@humanfs/types` dev dependency. - `packages/argent-private` submodule left pinned at `v0.16.0` — no `v0.16.1` tag exists there yet. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
a0d1bbb1e7 |
feat(flow): indent nested steps in reports by block depth (#524)
Flow reports rendered every step flat, hiding which steps ran inside a `when:` block or a `run:` fragment. The tool-server now stamps `StepReport.depth` (+1 per block directive, omitted at top level) and the CLI/MCP renderers indent step labels and under-step lines by it: ``` ✓ 1 launch ✓ 2 when visible "What's new" — condition met ✓ 3 tap "Skip" ✓ 4 run dismiss [dismiss] ✓ 5 tap "OK" [dismiss] ``` Invariants: - **Wire-compatible both ways** — a flow with no block directives reports byte-identical to the pre-depth shape; a pre-depth server renders flat as before. - **Skipped ≡ entered** — skipped/errored/aborted blocks report the same depths as entered ones, keeping reports comparable run-to-run. - **Untrusted wire data** — renderers clamp depth (display cap, not a producer bound): hostile values render flat or at the cap, never throw or allocate huge strings. Also fixes a pre-existing bug: under-step warning/artifact lines now stay aligned past 99 steps. Tests pin depth on every producer path (entered/skipped/errored/aborted blocks, `run:` success/failure markers, `run:`-in-`when:`) and renderer alignment incl. the clamp boundary. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3f602a7a9b |
feat(flow): conditional when blocks (#498)
## What
A `when:` block runs a step list only when one condition holds — a UI
condition (`exists`/`visible`/`hidden`/`text`, the await/assert shapes)
or a static `platform` test. **No else**, by design: a block exists to
restore determinism (dismiss an interstitial, reconverge), never to
test two paths. Tap-if-present is a one-step block:
`when: { visible: "Got it" }` + `steps: [tap: "Got it"]`.
No per-step `optional:` — `when:` covers every directive once. The key
is rejected at parse with a pointer to `when:`, not silently dropped (a
Maestro habit that would leave a "can't fail" step hard-stopping the
flow).
## How
- The guard reuses the assert engine at the 1s grace (`timeout` is a
parse error), so a skipped block barely costs a clean run.
- Verdicts are evidence-based: no trustworthy read in the window →
indeterminate → the step **errors** ("could not evaluate" is not
"condition false"); a transient read failure after trusted reads showed
the condition false stays a clean skip.
- `platform` folds `ios-remote` into `ios` — the parser rejects that
spelling, so the fold is the only way a guard matches a remote sim.
- A skipped block reports one line per authored step, same shape for
unmet guard, hard stop, or cancellation; everything goes through
`pushReport`, so when lines stream live. Failures inside an entered
block hard-stop as usual.
- Results carry an explicit `aborted` flag; `ok` keys off
fail/error/cancel, not skip lines.
- Nesting is capped at 20, so a cyclic YAML alias is a structured parse
error, not a stack overflow.
## Testing
`vitest` 2546 pass (17 when-specific); `tsc --noEmit` (tool-server,
argent-mcp, argent-cli), eslint, prettier clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
b50d51a2de |
chore: bump workspace packages to 0.16.0 (#513)
## What Lockstep-bump all 16 `packages/*` workspace packages (and the matching `package-lock.json` entries) from `0.15.0` → **0.16.0**. ## Why The `packages/argent-private` submodule is already pinned at **v0.16.0**, while the workspace packages sat at 0.15.0. This aligns the whole repo on the single 0.16.0 version. ## Verification - `node scripts/check-workspace-versions.mjs` → `All workspace packages are at 0.16.0.` - `git submodule status` → clean (no `+`), pinned at `v0.16.0`. - Lockfile diff is exactly 17 version-field swaps (no unrelated churn — a full `npm install --package-lock-only` regeneration was rejected because it introduced unrelated `"peer": true` churn from a newer npm). |
||
|
|
07427917e2 |
fix(launcher,lens): drop the npm postinstall — its jobs move to runtime (#510)
## Why Shipping *any* `postinstall` makes pnpm 10+ wall the install behind its build gate: `ERR_PNPM_IGNORED_BUILDS`, `pnpm install` exits 1, and `pnpm exec argent` is unusable until the user runs `pnpm approve-builds` — their first experience of argent is a bricked CLI. The same script is also silently skipped by `--ignore-scripts`, Yarn PnP, and locked-down CI, so anything correctness-relevant in it was already unreliable (the codebase says as much in several places, e.g. `runTrustingDisk`). Live-verified during the 0.15.0 QA: with **all builds declined** in pnpm 11, argent installs, inits, and serves MCP fine — the postinstall was never load-bearing for install correctness. Removing it means pnpm users never see the approve-builds wall for argent itself. ## What the postinstall did, and where it went | Job | New home | |---|---| | SIGTERM a tool-server stranded by an upgrade that replaced its bundle dir (pnpm/yarn version-pinned layouts) | `sweepDeadStateFiles` in the launcher's slow path now retires **live** servers whose recorded bundle is gone from disk, with the same pid-identity guard as `killToolServerForInstallDir` (never signal a recycled pid; keep unidentifiable live records reachable for `server stop`). Same-bundle in-place bumps were already self-healed by the disk-authoritative version gate. | | `chmod +x` node-pty's macOS `spawn-helper` prebuilds (shipped without the exec bit; first `pty.spawn` otherwise fails `posix_spawnp`) | `ensureSpawnHelperExecutable()` in `lens-pty.ts`, run when the module loads, best-effort with the AppleScript fallback intact. | | Print the `argent init` hint | Dropped — init flows drive the install, and the hint never printed on pnpm/PnP anyway. | `ARGENT_SKIP_POSTINSTALL` in `update.ts` is kept deliberately: downgrades and pinned installs fetch older versions whose postinstall still exists and honors it. ## Testing - New `launcher-sweep.test.ts` (6 tests) exercises the sweep against **real processes**: live-server-with-deleted-bundle is terminated and its record unlinked; live server with intact bundle untouched; recycled-pid record kept, not signalled; dead-pid record unlinked; legacy slot untouched; a SIGTERM-trapping orphan doesn't hold the sweep (and the spawn lock above it) hostage for its kill grace window — the record is unlinked and the SIGKILL escalation runs in the background. - New `ensureSpawnHelperExecutable` tests (real fs, darwin-gated) verify the +x restore and silent failure; a regression test pins that `loadNodePty` tolerates req seams without `resolve`. - Full suites green in all touched packages: tools-client 154, installer 535, cli 248, mcp 76, argent 60. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
551c781fb0 |
feat(flow): support running end-to-end flows without agent (#429)
# Flow E2E
This PR turns flows from replayable tool-call recordings into a
declarative, portable e2e test system that runs headlessly via `argent
flow run <name>` — no LLM in the loop, non-zero exit on failure,
suitable for CI.
Agents can still replay flows via `flow-execute` and get the full output
from each step after it finishes.
## Flow format
Flows are YAML files in `.argent/flows/`, now built from **directives**
interpreted by the runner rather than raw tool calls:
- `launch` — terminate + relaunch the app under test (per-platform app
id map supported); its presence marks a flow as **e2e** (controls its
own start state) vs. a **fragment** (runs against current state,
declares an `executionPrerequisite`, composable via `run:` with
cycle/depth guards).
- `tap`, `type` (focus handshake + optional submit), `scroll-to`
(momentum-free, increments by half the container, `within:` for nested
scrollers), `await`/`assert` (`visible`/`exists`/`hidden`/`text`
conditions), `wait`, `snapshot`, `echo`, and raw `tool:` as escape hatch
— still fully supported, so previously recorded flows keep working
unchanged.
- **Selectors** are `{ text, identifier, role }`; a bare string is a
loose selector resolving identifier-first, then visible text. Any
directive failure hard-stops the flow; later steps report `skip`.
## Execution engine
- Flows store no device id — the runner binds a device at replay
(explicit `--device`/`--platform` or the single booted one).
- Selector resolution runs against **platform-unified UI trees**: iOS
native view hierarchy (full testID coverage, gated on native-devtools
connecting), Android full accessibility hierarchy (full resource-id
coverage), Chromium CDP DOM, and the Vega automation toolkit. A shared
flatten/text-hoisting pass lets `text` assertions read what an
identified container visibly shows. Vega is remote-driven, so touch
directives fail with guidance to use `tv-remote`/`keyboard`.
- Steps wait for the tree to **settle** (two consecutive identical
reads) before acting; `launch` additionally gates on platform
tree-source readiness instead of silently degrading.
## Visual snapshots
`snapshot` diffs a full-resolution screenshot against baselines in
`.argent/flows/__baselines__/<flow>/`, keyed by platform + resolution,
with configurable `maxMismatch` and `--update-baselines`. The status bar
is pinned for the whole run (iOS `simctl status_bar`, Android demo mode)
so clocks/battery don't drive diffs. Baseline/current/diff images are
returned as artifacts on failure (and the written baseline on first
capture/update); a clean pass carries none, keeping reports and agent
context lean.
## Recording
The record-live workflow (`flow-start-recording` → `flow-add-step` →
`flow-finish-recording`) now emits directives automatically: coordinate
taps are rewritten to portable selector `tap:` steps (selector captured
from the pre-tap tree), a leading `restart-app` becomes `launch:`,
`flow-execute` of a sibling fragment becomes `run:`, and device ids are
stripped. The skill instructs the agent to polish the saved YAML
afterwards (fold tap+keyboard into `type:`, swipes-to-reach into
`scroll-to:`, awaits into sugar form).
## Also included
- `argent flow run` / `argent flow list` CLI with per-step report
rendering, artifact materialization, and `--json` output.
- New structured failure codes for flow validation, device resolution,
composition, and devtools readiness.
- Extensive unit test coverage (tree adapters, selector matching,
scrolling, composition, type focus, snapshots, status bar).
|
||
|
|
2a01e407b3 |
chore: release 0.15.0 (submodule + workspace bump) (#484)
## What - Bump the `packages/argent-private` submodule pointer from `v0.11.0-8-g2953e89` to **v0.15.0** (`41484e4`). The superproject pin had drifted ~4 releases behind the checked-out submodule. - Lockstep-bump all `packages/*` workspace packages from `0.14.0` → **0.15.0** (and the matching `package-lock.json` entries) to line up with the submodule. ## Why The submodule and the workspace were out of sync: the working tree had `argent-private` checked out at v0.15.0 while the superproject still pinned the v0.11 era, and the JS packages sat at 0.14.0. This adopts v0.15.0 as the single, consistent version across the repo. ## Verification - `node scripts/check-workspace-versions.mjs` → `All workspace packages are at 0.15.0.` - `git submodule status` → clean (no `+`), pinned at `v0.15.0`. - Lockfile diff is exactly 15 version-field swaps (no unrelated churn). |
||
|
|
7bd017a796 |
feat(installer): committable repo-local (--local) install mode (#428)
## Summary Adds an opt-in **committable / repo-local** install mode so a team can version Argent _with the repo_. `argent init --local` installs `@swmansion/argent` as a project **devDependency** and writes MCP configs that launch the project-local copy — so after `git clone && npm install`, every teammate has the same Argent version and MCP wiring with **no global install and no per-developer `argent init`**. **Global mode stays the default** and is unchanged; everything here is additive and backward-compatible. Motivation: the workflow some users already do by hand (#207). Supersedes the earlier, since-closed #232 — this version is Windows-safe (`node`, not the `.bin` shim), handles Yarn PnP, and closes the cross-install tool-server hazards rather than leaving them. ## What changed **`init`** - `--local` / `--global` flags + an interactive "How should argent be installed?" prompt. Mutually exclusive; non-interactive (`--yes`) defaults to **global**. - Local mode installs the devDependency with the package manager detected from the project's **lockfile** (run with `cwd = projectRoot`). - MCP entry points at the local copy: `node node_modules/@swmansion/argent/dist/cli.js mcp` (the bin path is derived from the installed `package.json` `bin` and existence-checked — forward slashes, committable, Windows-safe). Yarn PnP → `yarn argent mcp`; an `npx --no-install argent mcp` fallback is used only when the bin can't be resolved. - Records the mode in a committed `<root>/.argent/install.json`. - Drops global-only adapters (Windsurf/Hermes) in local mode; prints team-setup / commit guidance. **`update`** — detects the project's mode; in local mode reads the installed version from `node_modules` (not the global binary / npx cache) and bumps the **devDependency + lockfile**, then re-emits the local MCP command. **`uninstall`** — scoped to the project's mode: a local-mode uninstall removes the **local devDependency only and never the shared global install**, and prunes `.argent/install.json`. Global-mode behavior is unchanged. **Cross-install hardening (the gate that makes per-project pinning safe)** - `launcher`: a tool-server is reused only when its `bundlePath` matches the caller's — a repo-local pin never silently runs against (or kills) a different version's server. - `postinstall`: only stops a tool-server spawned from *this package's own* bundle, so a devDependency install can't tear down an unrelated editor session. **Telemetry** — adds an `install_mode` dimension to init/update/uninstall events. ## Testing - New unit tests: install-mode helpers + record (`utils`), mode-aware `getMcpEntry`/`resolveLocalCommandMode` incl. the opencode array form (`mcp-configs`), local-only uninstall guard (`uninstall`), and bundlePath-gated tool-server reuse (`launcher`). - All existing suites green (installer 346, tools-client 115, telemetry 191, registry 83, argent 13, tool-server 1621). Build + lint clean. - **Live end-to-end** in an isolated sandbox: `init --local` → real MCP stdio handshake serves **69 tools from `node_modules`**; `update --local` reads the local version; `uninstall --local` removes the local install and **leaves the global install intact**; `init --global` unchanged; flag conflict rejected. ## Notes / known limitations - Recommended on mainstream platforms (macOS, Linux x64, Windows x64) with prebuilt native binaries; Linux arm64 / Windows arm need a C/C++ toolchain to build `tree-sitter` on `npm install` (documented in the README). - Concurrent sessions of two *different* Argent versions on one machine each spawn their own tool-server; an orphaned one idle-times-out (no session is ever broken). 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
3393d22edd |
feat(telemetry): derive distinct_id from a stable host fingerprint (#444)
## Summary Replaces the random-UUID telemetry `distinct_id` with a stable per-machine id derived from the host fingerprint, so PostHog's native "Unique users" counts machines (funnels / retention / breakdowns work with no HogQL). Every Argent entry point - installer, CLI, tool-server, MCP - derives the id from one shared code path, so a given machine reports a single identity across every event and across install / uninstall / reinstall. ## What changed - `distinct_id = uuidv5(hostFingerprint, NAMESPACE)`, where the fingerprint comes from `simulator-server fingerprint`. Deterministic, keeps the UUID shape, and never stores the raw hardware value in PostHog (only the SHA-1-derived UUID leaves the process). - The fingerprint is resolved in `buildPayload` - the single `capture()` path shared by every entry point - so installer / CLI / tool-server / MCP all produce the same id. Resolved lazily, once per process, memoized. - One-time, local-only migration: the first time a fingerprint resolves, `~/.argent/telemetry-id` is rewritten from the legacy random UUID to the derived id. No PostHog `alias` / `$identify` event is emitted. - Fallback: if the fingerprint can't be resolved (unsupported platform, binary absent), the stored or a fresh random UUID is used - identically for every entry point on that machine, self-healing to the derived id on the next successful resolve. - Hardening: never throws; the fingerprint is validated (exactly 64 hex, case-insensitive) before hashing; corrupt / empty / unreadable id files self-heal; the binary is spawned at most once per process. ## Why the id is stable across install -> uninstall -> reinstall Uninstall deletes `~/.argent/telemetry-id`. Because the id is `uuidv5(fingerprint)` and the fingerprint is deterministic per machine, a fresh install recomputes the identical id. Previously only the tool-server resolved the fingerprint while the installer emitted under a random UUID, so this cycle produced a different random id each time. ## Bundling note for reviewers `@argent/telemetry` now depends on `@argent/native-devtools-ios` to locate the binary. That package computes its bin dir from `__dirname` at module load, which is undefined in an ES module. The installer / CLI / MCP ship as ESM bundles, so `bundle-tools.cjs`'s ESM banner is extended to shim `__dirname` / `__filename` (the CJS tool-server bundle already has them natively). Without this shim those bundles would crash on load, so the banner change is load-bearing. ## Testing - Unit: 219 telemetry tests - RFC 4122 v5 vectors, migration, idempotency, case/length validation, corrupt-file self-heal, spawn-at-most-once, and all fallbacks. - E2E on a real machine + booted iPhone 16 Pro: installer, CLI, and tool-server runtimes all emit the same derived id; the id is stable across a delete/recreate (uninstall/reinstall) cycle; a legacy v4 id migrates locally with no alias/identify emitted; verified from an ESM bundle placed in the real install's `dist/` so the binary resolves via the bundled `__dirname` with no env override; tool calls against the device carry the same id. |
||
|
|
c5bab2a83d |
fix(cli): let a tool's own --args field win over the whole-payload flag (#458)
## Bug
`argent run flow-add-step` declares its own `args` field — a JSON string
holding the recorded step's tool arguments. But the CLI flag parser
hardcoded `--args` as a whole-payload escape hatch, so `--args` shadowed
the tool's own `args` field. The documented per-flag form
```
argent run flow-add-step --command gesture-tap --args '{"udid":"...","x":0.5,"y":0.5}'
```
silently dropped the step arguments: the `--args` JSON was consumed as
the whole payload and the tool's `args` field stayed `undefined`,
producing a misleading downstream validation error. Only the nested
full-payload form worked.
## Fix
Make `--args` schema-aware in `parseFlags()`:
- When the tool's schema declares a property named `args`
(`schema.properties.args`), `--args <value>` is routed through the
normal per-field handling and coerced by its declared type (a `string`
for flow-add-step, so the raw JSON string passes straight into the
`args` field). Both `--args value` and `--args=value` forms are
supported.
- When the schema does NOT declare `args`, the existing whole-payload
behavior is preserved exactly, including the `--args -` stdin sentinel.
`printToolHelp()` no longer prints the global whole-payload `--args
<json>` / `--args -` lines for tools that declare their own `args`
field, so the help stops contradicting the per-field `--args` shown in
the schema flags.
The change is general: any tool with an `args` field benefits, with no
special-casing by tool name.
## Verification
- `npm run build` (root tsc) — clean.
- `npm test -w @argent/cli` — 172 passed, including 6 new `parseFlags`
cases covering schema-with-`args` (space-separated + inline forms, plus
sibling fields) and schema-without-`args` (whole-payload preserved,
`--args -` sentinel, no-schema fallback).
- Prettier + ESLint (`--max-warnings 0`) clean on the touched files.
Full CLI end-to-end (running `flow-add-step` against a live tool-server
with an active flow recording) was not exercised here; the added unit
tests prove the parser behavior.
Fixes #452
---
### CLI end-to-end verification
Ran the documented per-flag form against a live tool-server with an
active flow recording:
- **Before (global argent 0.13.0):** `argent run flow-add-step --command
gesture-tap --args '{"udid":"...","x":0.5,"y":0.35}'` → `Invalid params
for tool "gesture-tap": udid expected string, received undefined ...`
(args dropped).
- **After (this branch):** same command → `{ "message": "Step added to
\"colltest\" flow", "toolResult": { "tapped": true } }`, and the
recorded YAML contains the step **with** its args (`udid`/`x`/`y`). The
per-flag form now works as documented.
|
||
|
|
a7722df8a2 |
fix(cli): reject empty numeric flags and array/-json interleave crash (#432)
Fixes two related defects in `packages/argent-cli/src/flag-parser.ts`,
the argv-to-JSON flag parser used by `argent run`. The package
previously had no dedicated test file for it; this PR adds one covering
both fixes.
## Defect A: empty/whitespace numeric values coerced to 0
`coerceScalar` validates number/integer fields with `Number.isNaN` /
`Number.isInteger`, but `Number("")` and `Number(" ")` both evaluate to
`0`, so those guards pass. An empty or whitespace-only value was
therefore silently accepted as `0` instead of being rejected.
Reachable via `--x=` (empty inline), `--x ""`, and `--x " "`.
- Observed: `parseFlags(["--x="], { type: "object", properties: { x: {
type: "number" } } })` returned `{ x: 0 }`.
- Expected: rejected, the same way a non-numeric string like `"abc"`
already is.
Fix: reject an empty or whitespace-only value up front for
number/integer types, throwing the same `FlagParseException` the
function already throws for `"abc"` (`--<flag> expected a
number/integer, got "<raw>"`). Valid numbers are unaffected.
## Defect B: array + `-json` interleave throws a raw TypeError
`seenArrayFields` is only populated by the array branch, but the
`--field-json` escape hatch writes `args[fieldName]` directly and can
set it to a non-array. If a field was already seen as an array, a later
array-form occurrence ran `(args[flag] as unknown[]).push(...)` on a
non-array value and threw a raw `TypeError`. `run.ts` only catches
`FlagParseException` and re-throws anything else, so the user got an
uncaught stack trace instead of a clean error.
Repro:
```
parseFlags(
["--tags", "a", "--tags-json", '"b"', "--tags", "c"],
{ type: "object", properties: { tags: { type: "array", items: { type: "string" } } } }
)
```
- Observed: throws `TypeError: args[flag].push is not a function`
(uncaught, surfaces as a stack trace through `run.ts`).
- Expected: a clean validation error.
Fix: in the array branch, when `args[flag]` is not actually an array
(because `--field-json` replaced it), throw a `FlagParseException`
explaining the `--field` and `--field-json` forms cannot be mixed for
the same field, never a raw `TypeError`.
## Tests
Adds `packages/argent-cli/test/flag-parser.test.ts`. The new tests fail
before the fix (4 failures) and pass after; the full `argent-cli` suite
stays green (90 passed). Behavior for valid inputs is unchanged.
|
||
|
|
c5b9857fa5 |
chore: bump all workspace packages to 0.14.0 (#454)
Bumps every package under `packages/*` from 0.13.0 → 0.14.0 in lockstep. - All 14 workspace packages bumped to `0.14.0`. - `package-lock.json` regenerated to match (also picks up npm's `"peer": true` normalization). - `npm run check:versions` passes: *All workspace packages are at 0.14.0.* Internal deps use `file:../` references, so no dependency version specifiers needed changing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
a92c859d07 |
feat(argent-lens): argent lens — bind a claude session to the preview window (#416)
## What Adds `argent lens` — open Argent Lens bound to a fresh `claude` session. ## Why The Lens preview window and the agent communicate through a shared, process-wide tool-server that carries **no per-agent identity** (only a coarse telemetry header). So when the user requests changes in the window, there's no reliable way to route them to the *specific* agent that opened it. `argent lens` sidesteps the problem by inverting ownership: the human launches Lens, and Lens spawns (and therefore owns) exactly one `claude` terminal. The binding is 1:1 by construction — no identity plumbing required. ## How it works 1. `argent lens` ensures a tool-server and **begins a CLI Lens session**, which opens the preview window up front (no `await_user_selection` needed) and keeps it open across rounds. 2. It spawns a tracked `claude` terminal in the current directory — a TypeScript port of the macOS applet's `osascript` spawn/track mechanism — seeded to propose variants without blocking. 3. A watcher polls `GET /preview/outcome`; on each submitted round it types a one-line feedback summary into that terminal, queuing it to the agent as its next prompt. Requesting changes therefore "talks back" to the bound agent instead of closing the window. macOS only (drives Terminal/iTerm via `osascript`); off-darwin it exits with a clear message. ## Changes - **CLI** `lens-terminal.ts` — `osascript` spawn/track + a new write-into-running-session path (the "outputting" channel; injects input into the running TUI, which a tty write cannot do). - **CLI** `lens.ts` — the command + feedback formatter. - **tool-server** — a `cliSession` flag in the variant store (opens the window, suppresses auto-close on submit); `POST /preview/cli-session` and `GET /preview/outcome` routes. - **UI** — relabels the submit action to "Request changes" while a CLI session is active. Behind the existing `argent-lens` feature flag. ## Verification - Build + typecheck across the workspace; ESLint (`--max-warnings 0`) + Prettier clean. - 34 new unit tests; full `@argent/cli` suite **118 passing**, tool-server variant/preview suites **46 passing**. - **E2E (observed on this machine):** - **tool-server HTTP** — `cli-session` toggles `cliSession` true/false; submitted feedback round-trips through `/preview/outcome`. - **terminal injection** — spawned a window running `cat`, confirmed `writeToSession` lands text in the running program's **stdin** (both iTerm and Terminal), with embedded newlines flattened. - **full relay** — the real `watchAndRelay` loop against a real tool-server typed two distinct submission rounds' feedback into a stand-in agent terminal (dedup on `completedAt` verified). - `argent lens --help` and the feature-flag gate (exit 1 with guidance, no spawn). Note: a real end-to-end with a live `claude` agent was not run (it would drive a paid interactive session); the agent-facing channel is proven by injecting into a stand-in process's stdin via the exact same code path. --------- Co-authored-by: Filip131311 <contact@filipkaminski.com> |
||
|
|
6bc5376746 |
fix(tool-server): stop list-devices from hanging - wedged-VVD stall and CLI socket leak (#419)
## Summary
`list-devices` (an `alwaysLoad` tool called at session start and
frequently after)
could appear to **hang**. Investigation found **two independent
causes**, both fixed here:
1. **Server-side (Vega):** a wedged Vega VVD turned `list-devices` into
a ~40s stall
and leaked `vega` CLI subprocesses.
2. **Client-side (CLI):** every `argent run …` lingered a constant **~6s
after it had
already printed its result**, because a health-check `fetch` never
drained its
response body and kept an undici keep-alive socket ref'd until the
server closed it.
Net effect for `argent run list-devices`: **6.15s → 0.91s**, and the
worst-case
wedged-VVD stall drops from ~40s to a bounded short call.
---
## Problem 1 — wedged Vega VVD stalls `list-devices` (~40s) and leaks
processes
On a host with the Vega SDK installed and a **running-but-unresponsive
VVD**,
`list-devices`' Vega discovery path made **two serial 20s CLI calls**
that both hung:
- `vega device list` → SIGKILL at 20s
- then (recovery) `vega device info` → SIGKILL at another 20s → **~40s
total**
Three compounding defects:
- `execFileAsync` resolves on stdio **close**, not child **exit**. The
`vega` launcher
forks a `python3 → node → vda` worker tree; an orphaned worker holds the
stdout pipe
open, so the call stayed pending for the full timeout even after the
launcher was
killed.
- The `device info` recovery fired even when `device list` had **hung**,
doubling the
cost and spawning a second hanging subprocess.
- On timeout we killed only the **direct child**, so the `python3 → node
→ vda` worker
tree was **orphaned, not reaped** — leaking a process tree per call (a
wedged host had
accumulated ~269 of them).
### Fix
- **`runVega` (`vega-cli.ts`):** switched from `execFileAsync` to
`spawn` with
**`detached: true`**, making the launcher its own **process-group
leader**, and on
timeout SIGKILL the **whole process group** (`process.kill(-pid)`) —
which reaps the
entire `python3 → node → vda` tree, **not just the launcher**. We also
destroy our
stdio stream ends so a pipe-holding worker can't keep the call pending.
(`execFile`
silently drops `detached`, which is why the switch to `spawn` was
required.)
- **Success path doesn't depend on stdio EOF either:** we prefer `close`
(fully
drained), but if a grandchild holds the stdout pipe open after a **clean
exit**, we
fall back to `exit` + a short (~1s) drain grace and resolve with the
captured
output — so a finished call can't be stalled into the timeout and then
rejected,
discarding valid data. The output cap is measured in real UTF-8 bytes.
For that
guarantee to hold at the boundary, the `exit` handler **disarms the main
timeout
timer**: once the child has terminated the wall-clock deadline is moot,
so a clean
exit landing within the drain grace of the deadline resolves with its
output instead
of being mis-rejected as a `timeout` (which would also wrongly suppress
the
`listVegaDevices` recovery for a still-running VVD).
- **Belt-and-suspenders for a worker that escapes the group:** an
earlier
investigation reported the worker tree `setsid()`-ing into its own
session (which
would defeat a group-only SIGKILL). To be safe regardless of which is
true, on
timeout we **snapshot the launcher's descendants from the process table
*before*
killing** (a setsid'd worker keeps its `ppid` pointing at the launcher
until the
launcher dies, so a `ppid` tree-walk still reaches it) and SIGKILL those
pids
individually after the group kill. The snapshot is **precise** — only
this
launcher's own descendants, by pid, so a concurrent `vega` call's
workers are never
touched — and bounded, and the group kill runs regardless of whether it
succeeds.
- A real-subprocess test confirms **0 orphans** after a timeout,
including a worker
deliberately spawned into its **own** process group (so the group kill
alone cannot
reach it — the test fails without the descendant sweep).
- **`listVegaDevices` (`vega-devices.ts`):** skip the `device info`
recovery only when
`device list` **timed out** (a wedged agent — keyed off the failure's
`error_kind: "timeout"`), so a hung list no longer pays for a second
timeout wait. A
*fast* (non-timeout) list failure still recovers a genuinely-running VVD
rather than
mis-reporting it as stopped. Tightened discovery timeouts (20s → 6s/4s —
a generous
multiple of the ~1s healthy time so a cold-start `vega` launcher isn't
spuriously
timed out, with their sum (10s) kept under the 15s branch deadline with
margin).
- **`list-devices.ts`:** added a 15s `withDeadline` backstop around the
Android and Vega
branches so no single branch can stall the whole fan-out (returns
partial results +
logs a note). Sized to sit ~5s above the Vega branch's ~10s worst case,
so the
backstop never truncates a slow-but-completing branch (which would drop
a real
device); an invariant test guards that ordering.
- **`readAvdName` (`adb.ts`):** run the two `getprop` probes
concurrently so a wedged
emulator costs 5s, not 10s.
---
## Problem 2 — `argent run` hangs ~6s after printing output (CLI socket
leak)
Independent of Vega. The tool-server returns `list-devices` in <1s, but
the CLI process
took a **constant ~6s** to exit. Any path that calls `process.exit()`
was instant; any
path that returned naturally (`argent run …`, `argent run … --help`,
`argent tools`)
lingered ~6s.
**Root cause:** `isToolsServerHealthy` (`launcher.ts`) did
`const res = await fetch('/tools'); return res.ok;` and **never drained
the body**.
`/tools` is a large (~112KB) payload, so undici keeps the keep-alive
socket **ref'd**
until the tool-server's idle `keepAliveTimeout` (~5s) closes it.
`ensureToolsServer`
runs this health check on every invocation, and `run()`'s success path
has no
`process.exit(0)`, so the ref'd socket holds the event loop open.
(Confirmed
telemetry-independent — `DO_NOT_TRACK` made no difference.) The same
undrained-body
pattern existed in `link.ts` (`argent link`).
### Fix
Drain the response body before returning in both health-check-style
fetches:
```ts
await res.body?.cancel().catch(() => {});
return res.ok;
```
(`fetchTools`/`callTool` were already fine — they `await res.json()`.)
---
## Testing
**Empirical, end-to-end** (on a machine reproducing the wedged VVD):
| Command | Before | After |
| --- | --- | --- |
| `argent run list-devices` | 6.15s | **0.91s** |
| `argent run list-devices --help` | 6.14s | **0.16s** |
| `argent tools` | 6.15s | **0.16s** |
| handle probe after run | 2 sockets ref'd for ~6s | **none — immediate
exit** |
- Raw `POST /tools/list-devices` was ~0.7s throughout — confirming the
~5.4s was pure
CLI teardown, not tool work.
- **Process-group reap proven empirically:** the live `vega → bash →
python3 → node`
tree all shares the detached launcher's PGID; a group-SIGKILL left **0
survivors**.
A real-subprocess test (unmocked `runVega` vs. a fake hanging `vega` on
`PATH`) confirms
it settles at its deadline **and** leaves **0 orphaned processes**.
**Automated:**
- `vega-cli-timeout.test.ts` (real-subprocess test) — `runVega` rejects
on its deadline,
**reaps the entire worker tree (0 orphans)** — the fake's hung worker is
spawned into its
**own** process group, so the test fails without the descendant sweep —
and returns
normally when the CLI responds. Also covers the boundary case: a **clean
exit whose
drain grace outlasts the deadline** resolves with its captured output
rather than being
rejected as a `timeout` (guards the `exit`-handler timer disarm above).
- `vega-adb-connect-recovery.test.ts` — a *timed-out* `device list` does
not trigger a
second `device info` call (no stacking); a *fast, non-timeout* failure
**still** recovers
a running VVD.
- `list-devices-deadline.test.ts` — `withDeadline` returns the branch
value on the happy
path and the fallback (with a logged note) when a branch exceeds the
deadline.
- `list-devices.test.ts` — added a `spawn` mock (vega moved off
`execFile`).
- `launcher-helpers.test.ts` — `isToolsServerHealthy` cancels (drains)
its response body.
- Suites green: **tool-server (1610), argent-tools-client (109),
argent-cli (84)**.
- eslint (`--max-warnings 0`), prettier, and `tsc` clean.
---
|
||
|
|
5f9dd48e2a |
fix(tool-server): prevent duplicate tool-servers after an nvm node switch (#385)
## Problem Switching Node versions with **nvm** can leave **two (or more) `tool-server` processes alive at once** while `~/.argent/tool-server.json` tracks only one. The extra servers are orphaned (untracked, on leaked ports) until their 30‑minute idle timeout. This was reproduced live: a machine had **three** `node …/dist/tool-server.cjs start` processes running, all spawned within the same minute, with state tracking only the last one. ## Root cause `@argent/tools-client` `ensureToolsServer()` ran a non-atomic `read state → check alive+healthy → clearState → spawn → writeState` sequence with: 1. **No cross-process serialization.** An nvm switch relaunches `argent mcp` under the new Node while the *previous* MCP's 30s health monitor reconnects — several launchers run `ensureToolsServer()` at nearly the same time, all observe "no healthy server", and each spawns its own **detached** server on a fresh free port. Last‑writer‑wins on the state file orphans the rest. 2. **No kill-before-respawn.** When the tracked server was alive but unhealthy, the old code just `clearState()`d and spawned a new one, leaving the old detached process running untracked. ## Fix (`packages/argent-tools-client/src/launcher.ts`) - **O_EXCL spawn lock** (`~/.argent/tool-server.lock`) serialises the spawn decision across processes: per-acquisition nonce, stale‑steal (dead holder / aged lock), bounded wait, and a hot‑spin‑proof reclaim loop. Inside the lock, `ensureToolsServer` **double‑checks** for a peer‑spawned healthy server before spawning, so a burst of concurrent launches collapses to a single server. - **Kill-before-respawn** terminates a wedged server we own before spawning its replacement, with three guards so it can never signal the wrong process: - `managed === "autospawn"` — a user's `argent server start` server is now tagged `managed: "cli"` and is **never** killed by the auto‑spawn path; - a **structural** `node <bundlePath> start` command-line identity match (not a bare substring); - a re-check of that identity immediately before each signal (covers PID reuse across the SIGTERM grace window). - **`spawnToolsServer` reaps its detached child on every reject path**, so a readiness timeout can no longer leak a child that binds its port moments later as an untracked second server. The lock-free fast path (reuse a healthy tracked server) is unchanged, so steady‑state behaviour and latency are unaffected; the lock is only taken when a spawn is actually needed. ## Tests New `packages/argent-tools-client/test/launcher-duplicate-spawn.test.ts`: - a 5× concurrent launch burst collapses to **one** server; - an alive‑but‑unhealthy `autospawn` server is terminated (not orphaned) on respawn; - a recycled pid whose command line doesn't match is **never** signalled; - a `managed: "cli"` server is left alive instead of being killed; - a readiness timeout reaps the spawned child (no orphan). Each case was verified to **fail against the pre-fix code** and pass with the fix. Existing `launcher-spawn` / `launcher-state` suites are unchanged and green. > Note: the `@swmansion/argent` `dispatcher › forwards SIGTERM to the child binary` unit test is a pre-existing flake (alternates pass/fail independent of this change, which doesn't touch that package). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Radoniusz Cloudiusz <radoniusz@radoniusz.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0718e622ab |
chore(deps)(deps): bump the npm-minor-patch group across 2 directories with 9 updates (#422)
Bumps the npm-minor-patch group with 9 updates in the / directory: | Package | From | To | | --- | --- | --- | | [globals](https://github.com/sindresorhus/globals) | `17.6.0` | `17.7.0` | | [prettier](https://github.com/prettier/prettier) | `3.8.4` | `3.8.5` | | [semver](https://github.com/npm/node-semver) | `7.8.4` | `7.8.5` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.61.1` | `8.62.0` | | [@clack/prompts](https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts) | `1.5.1` | `1.6.0` | | [posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node) | `5.35.0` | `5.38.6` | | [smol-toml](https://github.com/squirrelchat/smol-toml) | `1.6.1` | `1.7.0` | | [electron](https://github.com/electron/electron) | `42.4.1` | `42.5.0` | | [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) | `5.9.2` | `5.9.3` | Bumps the npm-minor-patch group with 1 update in the /packages/telemetry directory: [posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node). Updates `globals` from 17.6.0 to 17.7.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/sindresorhus/globals/releases">globals's releases</a>.</em></p> <blockquote> <h2>v17.7.0</h2> <ul> <li>Update globals (2026-06-22) (<a href="https://redirect.github.com/sindresorhus/globals/issues/345">#345</a>) 33b75f9</li> </ul> <hr /> <p><a href="https://github.com/sindresorhus/globals/compare/v17.6.0...v17.7.0">https://github.com/sindresorhus/globals/compare/v17.6.0...v17.7.0</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/sindresorhus/globals/commit/a19670cc86c1218e915657c55ea02ba3e7623834"><code>a19670c</code></a> 17.7.0</li> <li><a href="https://github.com/sindresorhus/globals/commit/9611620bad0ea45531ae0261b986ce694374e42c"><code>9611620</code></a> Update actions (<a href="https://redirect.github.com/sindresorhus/globals/issues/346">#346</a>)</li> <li><a href="https://github.com/sindresorhus/globals/commit/33b75f9ddeb6f9ddd81251b043667eb8104a8d15"><code>33b75f9</code></a> Update globals (2026-06-22) (<a href="https://redirect.github.com/sindresorhus/globals/issues/345">#345</a>)</li> <li><a href="https://github.com/sindresorhus/globals/commit/887dd529bde24486b7f9e31d1b2c515df2b7cf5c"><code>887dd52</code></a> Fix build script (<a href="https://redirect.github.com/sindresorhus/globals/issues/344">#344</a>)</li> <li>See full diff in <a href="https://github.com/sindresorhus/globals/compare/v17.6.0...v17.7.0">compare view</a></li> </ul> </details> <br /> Updates `prettier` from 3.8.4 to 3.8.5 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/prettier/prettier/releases">prettier's releases</a>.</em></p> <blockquote> <h2>3.8.5</h2> <ul> <li>Fix Flow variance annotation print (<a href="https://redirect.github.com/prettier/prettier/pull/19022">#19022</a> by <a href="https://github.com/marcoww6"><code>@marcoww6</code></a>)</li> </ul> <p>🔗 <a href="https://github.com/prettier/prettier/blob/3.8.5/CHANGELOG.md#385">Changelog</a></p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's changelog</a>.</em></p> <blockquote> <h1>3.8.5</h1> <p><a href="https://github.com/prettier/prettier/compare/3.8.4...3.8.5">diff</a></p> <h4>Flow: Support <code>readonly</code> as a variance annotation (<a href="https://redirect.github.com/prettier/prettier/pull/19022">#19022</a> by <a href="https://github.com/marcoww6"><code>@marcoww6</code></a>)</h4> <p>Flow now accepts <code>readonly</code> as a property variance annotation, equivalent to <code>+</code> (covariant/read-only).</p> <!-- raw HTML omitted --> <pre lang="jsx"><code>// Input type T = { readonly foo: string, }; <p>// Prettier 3.8.4<br /> SyntaxError</p> <p>// Prettier 3.8.5<br /> type T = {<br /> readonly foo: string,<br /> };<br /> </code></pre></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/prettier/prettier/commit/1ce8c47015ab1e9dfa99f6238df60ff1e70e1498"><code>1ce8c47</code></a> Release 3.8.5</li> <li><a href="https://github.com/prettier/prettier/commit/74923fa8316cd78f8461e77290915155efbcd714"><code>74923fa</code></a> Fix variance annotation</li> <li><a href="https://github.com/prettier/prettier/commit/8b4eb339d43edba35a509ff324d4cdb4416b3efc"><code>8b4eb33</code></a> Bump Prettier dependency to 3.8.4</li> <li><a href="https://github.com/prettier/prettier/commit/e9cbdfa1b755e68ea6f97b0b321524a5169a85d1"><code>e9cbdfa</code></a> Clean changelog_unreleased</li> <li>See full diff in <a href="https://github.com/prettier/prettier/compare/3.8.4...3.8.5">compare view</a></li> </ul> </details> <br /> Updates `semver` from 7.8.4 to 7.8.5 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/npm/node-semver/releases">semver's releases</a>.</em></p> <blockquote> <h2>v7.8.5</h2> <h2><a href="https://github.com/npm/node-semver/compare/v7.8.4...v7.8.5">7.8.5</a> (2026-06-19)</h2> <h3>Bug Fixes</h3> <ul> <li><a href="https://github.com/npm/node-semver/commit/9c8692ae05416e9dbe88d95ffe2b80e6964550fe"><code>9c8692a</code></a> <a href="https://redirect.github.com/npm/node-semver/pull/878">#878</a> include prereleases in tilde range lower bound with includePrerelease (<a href="https://redirect.github.com/npm/node-semver/issues/878">#878</a>) (<a href="https://github.com/chatman-media"><code>@chatman-media</code></a>)</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/npm/node-semver/blob/main/CHANGELOG.md">semver's changelog</a>.</em></p> <blockquote> <h2><a href="https://github.com/npm/node-semver/compare/v7.8.4...v7.8.5">7.8.5</a> (2026-06-19)</h2> <h3>Bug Fixes</h3> <ul> <li><a href="https://github.com/npm/node-semver/commit/9c8692ae05416e9dbe88d95ffe2b80e6964550fe"><code>9c8692a</code></a> <a href="https://redirect.github.com/npm/node-semver/pull/878">#878</a> include prereleases in tilde range lower bound with includePrerelease (<a href="https://redirect.github.com/npm/node-semver/issues/878">#878</a>) (<a href="https://github.com/chatman-media"><code>@chatman-media</code></a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/npm/node-semver/commit/6e05b7637396ac66522cff8731f07cfe0ef49a29"><code>6e05b76</code></a> chore: release 7.8.5 (<a href="https://redirect.github.com/npm/node-semver/issues/879">#879</a>)</li> <li><a href="https://github.com/npm/node-semver/commit/9c8692ae05416e9dbe88d95ffe2b80e6964550fe"><code>9c8692a</code></a> fix: include prereleases in tilde range lower bound with includePrerelease (#...</li> <li>See full diff in <a href="https://github.com/npm/node-semver/compare/v7.8.4...v7.8.5">compare view</a></li> </ul> </details> <br /> Updates `typescript-eslint` from 8.61.1 to 8.62.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/typescript-eslint/typescript-eslint/releases">typescript-eslint's releases</a>.</em></p> <blockquote> <h2>v8.62.0</h2> <h2>8.62.0 (2026-06-22)</h2> <h3>🚀 Features</h3> <ul> <li>remove redundant package.json "files" (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12444">#12444</a>)</li> </ul> <h3>🩹 Fixes</h3> <ul> <li>add "files" to rule-schema-to-typescript-types (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12441">#12441</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Kirk Waiblinger <a href="https://github.com/kirkwaiblinger"><code>@kirkwaiblinger</code></a></li> </ul> <p>See <a href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.0">GitHub Releases</a> for more information.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md">typescript-eslint's changelog</a>.</em></p> <blockquote> <h2>8.62.0 (2026-06-22)</h2> <h3>🚀 Features</h3> <ul> <li>remove redundant package.json "files" (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12444">#12444</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Kirk Waiblinger <a href="https://github.com/kirkwaiblinger"><code>@kirkwaiblinger</code></a></li> </ul> <p>See <a href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.0">GitHub Releases</a> for more information.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/54e285728e5d8cb83fadb8041189f0c3b4ab436a"><code>54e2857</code></a> chore(release): publish 8.62.0</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/81e4c2654c3f4d923766a888691add2c45b5d64a"><code>81e4c26</code></a> feat: remove redundant package.json "files" (<a href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint/issues/12444">#12444</a>)</li> <li>See full diff in <a href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.0/packages/typescript-eslint">compare view</a></li> </ul> </details> <br /> Updates `@clack/prompts` from 1.5.1 to 1.6.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/bombshell-dev/clack/releases">@clack/prompts's releases</a>.</em></p> <blockquote> <h2><code>@clack/prompts</code><a href="https://github.com/1"><code>@1</code></a>.6.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/bombshell-dev/clack/pull/568">#568</a> <a href="https://github.com/bombshell-dev/clack/commit/f87933fb7b3f4c401b9e51a152b95cb8e7200fe5"><code>f87933f</code></a> Thanks <a href="https://github.com/florian-lefebvre"><code>@florian-lefebvre</code></a>! - Updates default formatter of <code>note()</code> to note dim lines anymore</p> <p>If you want the old behavior, provide a <code>format()</code> function:</p> <pre lang="diff"><code>import { note } from '@clack/prompts'; +import { styleText } from 'node:util'; <p>note( 'You can edit the file src/index.jsx', 'Next steps.'</p> <ul> <li>{ format: (text) => styleText('dim', text) } ); </code></pre></li> </ul> </li> <li> <p><a href="https://redirect.github.com/bombshell-dev/clack/pull/567">#567</a> <a href="https://github.com/bombshell-dev/clack/commit/cc6aab50186cff8a02dc98e9cfd3897c29a33b15"><code>cc6aab5</code></a> Thanks <a href="https://github.com/dreyfus92"><code>@dreyfus92</code></a>! - Add keyboard instruction footers to <code>select</code>, <code>multiselect</code>, and <code>groupMultiselect</code> in the active state, matching autocomplete. No option — always shown.</p> </li> </ul> <h3>Patch Changes</h3> <ul> <li>Updated dependencies [<a href="https://github.com/bombshell-dev/clack/commit/2f2b52f77cbfa9af618c6d929249ab8395fc37a1"><code>2f2b52f</code></a>, <a href="https://github.com/bombshell-dev/clack/commit/e1b6ee71a76e17a3c33ba7ee6e5fb34e886233bb"><code>e1b6ee7</code></a>]: <ul> <li><code>@clack/core</code><a href="https://github.com/1"><code>@1</code></a>.4.2</li> </ul> </li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/bombshell-dev/clack/blob/main/packages/prompts/CHANGELOG.md">@clack/prompts's changelog</a>.</em></p> <blockquote> <h2>1.6.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/bombshell-dev/clack/pull/568">#568</a> <a href="https://github.com/bombshell-dev/clack/commit/f87933fb7b3f4c401b9e51a152b95cb8e7200fe5"><code>f87933f</code></a> Thanks <a href="https://github.com/florian-lefebvre"><code>@florian-lefebvre</code></a>! - Updates default formatter of <code>note()</code> to note dim lines anymore</p> <p>If you want the old behavior, provide a <code>format()</code> function:</p> <pre lang="diff"><code>import { note } from '@clack/prompts'; +import { styleText } from 'node:util'; <p>note( 'You can edit the file src/index.jsx', 'Next steps.'</p> <ul> <li>{ format: (text) => styleText('dim', text) } ); </code></pre></li> </ul> </li> <li> <p><a href="https://redirect.github.com/bombshell-dev/clack/pull/567">#567</a> <a href="https://github.com/bombshell-dev/clack/commit/cc6aab50186cff8a02dc98e9cfd3897c29a33b15"><code>cc6aab5</code></a> Thanks <a href="https://github.com/dreyfus92"><code>@dreyfus92</code></a>! - Add keyboard instruction footers to <code>select</code>, <code>multiselect</code>, and <code>groupMultiselect</code> in the active state, matching autocomplete. No option — always shown.</p> </li> </ul> <h3>Patch Changes</h3> <ul> <li>Updated dependencies [<a href="https://github.com/bombshell-dev/clack/commit/2f2b52f77cbfa9af618c6d929249ab8395fc37a1"><code>2f2b52f</code></a>, <a href="https://github.com/bombshell-dev/clack/commit/e1b6ee71a76e17a3c33ba7ee6e5fb34e886233bb"><code>e1b6ee7</code></a>]: <ul> <li><code>@clack/core</code><a href="https://github.com/1"><code>@1</code></a>.4.2</li> </ul> </li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/bombshell-dev/clack/commit/0e700562dfb54bd693d98c5b7e28d34213fcb226"><code>0e70056</code></a> [ci] release (<a href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/562">#562</a>)</li> <li><a href="https://github.com/bombshell-dev/clack/commit/f87933fb7b3f4c401b9e51a152b95cb8e7200fe5"><code>f87933f</code></a> fix(prompts): do not dim note contents (<a href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/568">#568</a>)</li> <li><a href="https://github.com/bombshell-dev/clack/commit/cc6aab50186cff8a02dc98e9cfd3897c29a33b15"><code>cc6aab5</code></a> feat(prompts): add instructions footer for <code>select</code>, <code>multi-select</code> & `group-...</li> <li>See full diff in <a href="https://github.com/bombshell-dev/clack/commits/@clack/prompts@1.6.0/packages/prompts">compare view</a></li> </ul> </details> <br /> Updates `posthog-node` from 5.35.0 to 5.38.6 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/PostHog/posthog-js/releases">posthog-node's releases</a>.</em></p> <blockquote> <h2>posthog-node@5.38.6</h2> <h2>5.38.6</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a> <a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> Thanks <a href="https://github.com/GauthierPLM"><code>@GauthierPLM</code></a>! - Only stamp $groups from the top-level <code>groups</code> when present (2026-06-26)</li> </ul> <h2>posthog-node@5.38.5</h2> <h2>5.38.5</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a> <a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Dedupe feature flag called events per returned flag value. (2026-06-24)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.3</li> </ul> </li> </ul> <h2>posthog-node@5.38.4</h2> <h2>5.38.4</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a> <a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> Thanks <a href="https://github.com/dustinbyrne"><code>@dustinbyrne</code></a>! - Respect <code>disable_capture_url_hashes</code> when capturing Express and NestJS request URLs. (2026-06-23)</li> </ul> <h2>posthog-node@5.38.3</h2> <h2>5.38.3</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a> <a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Deprecate the Node SDK Sentry integration and point users to PostHog Error Tracking for Node. (2026-06-23)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.0</li> </ul> </li> </ul> <h2>posthog-node@5.38.2</h2> <h2>5.38.2</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a> <a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Validate custom event UUID overrides and generate new UUIDs when invalid. (2026-06-19)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.3</li> </ul> </li> </ul> <h2>posthog-node@5.38.1</h2> <h2>5.38.1</h2> <h3>Patch Changes</h3> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md">posthog-node's changelog</a>.</em></p> <blockquote> <h2>5.38.6</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a> <a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> Thanks <a href="https://github.com/GauthierPLM"><code>@GauthierPLM</code></a>! - Only stamp $groups from the top-level <code>groups</code> when present (2026-06-26)</li> </ul> <h2>5.38.5</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a> <a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Dedupe feature flag called events per returned flag value. (2026-06-24)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.3</li> </ul> </li> </ul> <h2>5.38.4</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a> <a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> Thanks <a href="https://github.com/dustinbyrne"><code>@dustinbyrne</code></a>! - Respect <code>disable_capture_url_hashes</code> when capturing Express and NestJS request URLs. (2026-06-23)</li> </ul> <h2>5.38.3</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a> <a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Deprecate the Node SDK Sentry integration and point users to PostHog Error Tracking for Node. (2026-06-23)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.0</li> </ul> </li> </ul> <h2>5.38.2</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a> <a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Validate custom event UUID overrides and generate new UUIDs when invalid. (2026-06-19)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.3</li> </ul> </li> </ul> <h2>5.38.1</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3886">#3886</a> <a href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Stop sending deprecated no-op top-level <code>type</code>, <code>library</code>, and <code>library_version</code> fields in event batch payloads. Use <code>properties.$lib</code> and <code>properties.$lib_version</code> for SDK metadata; legacy queued <code>library</code> and <code>library_version</code> values are used as fallbacks when the official <code>$</code> properties are missing. (2026-06-18)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.2</li> </ul> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/PostHog/posthog-js/commit/d2faa824af1af2cb50b47178fb953bfc8812b389"><code>d2faa82</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> fix: preserve $groups passed via properties (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3896">#3896</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/5c4838f1671ece80d153aaeb6d946bb3e8c8240a"><code>5c4838f</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> fix: Dedupe feature flag called events by response (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3955">#3955</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/2a60c21134dd118c2367e56129bc9427079c3841"><code>2a60c21</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> fix(node): respect URL hash capture config (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3938">#3938</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/a3136c6de3114ee3d58e59a0c86f2b9c4f59a2b0"><code>a3136c6</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/bf6947d3e12ac512c99185be9b8b134c04eb563a"><code>bf6947d</code></a> feat(react-native): support session replay event triggers (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3932">#3932</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/2628a1af5f06f73cb2252de002e0da4b2d1a25a5"><code>2628a1a</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> chore(node): mark Sentry integration deprecated (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3937">#3937</a>)</li> <li>Additional commits viewable in <a href="https://github.com/PostHog/posthog-js/commits/posthog-node@5.38.6/packages/node">compare view</a></li> </ul> </details> <br /> Updates `smol-toml` from 1.6.1 to 1.7.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/squirrelchat/smol-toml/releases">smol-toml's releases</a>.</em></p> <blockquote> <h2>v1.7.0</h2> <p>This version slightly changes the behaviour of stringify: integers beyond the safe range are always emitted as float numbers.</p> <p>String decode logic has been rewritten, it is a bit faster now and uses a single-pass approach instead of a dual-pass approach as it did previously. The code should be a bit smaller too, though I didn't actually measure that.</p> <p>The package is now published with source-maps, declaration-maps, and a copy of the original TypeScript source files. This will improve your DX if you're like me and like Ctrl+Click'ing things a lot. ;)</p> <h2>What's Changed</h2> <ul> <li>improve performance of the string decode logic by <a href="https://github.com/cyyynthia"><code>@cyyynthia</code></a></li> <li>fix(stringify): emit integer-valued numbers beyond the safe-integer range as floats by <a href="https://github.com/spokodev"><code>@spokodev</code></a> in <a href="https://redirect.github.com/squirrelchat/smol-toml/pull/56">squirrelchat/smol-toml#56</a></li> <li>publish source-maps, declaration-maps, and original source files by <a href="https://github.com/cyyynthia"><code>@cyyynthia</code></a></li> <li>miscellaneous repository maintenance tasks by <a href="https://github.com/cyyynthia"><code>@cyyynthia</code></a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/spokodev"><code>@spokodev</code></a> made their first contribution in <a href="https://redirect.github.com/squirrelchat/smol-toml/pull/56">squirrelchat/smol-toml#56</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/squirrelchat/smol-toml/compare/v1.6.1...v1.7.0">https://github.com/squirrelchat/smol-toml/compare/v1.6.1...v1.7.0</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/squirrelchat/smol-toml/commit/a62f06faf4e45ab0a581def86c116f9706edc9df"><code>a62f06f</code></a> revert: keep using vite 7</li> <li><a href="https://github.com/squirrelchat/smol-toml/commit/89aa9a394ffc544dce77ab0ff24dfd8204e1f091"><code>89aa9a3</code></a> chore: remove prepare script</li> <li><a href="https://github.com/squirrelchat/smol-toml/commit/17c797475f1516da6614e31a1be9f4958bf296c0"><code>17c7974</code></a> chore: make devEngine more lax w/ node version</li> <li><a href="https://github.com/squirrelchat/smol-toml/commit/e5280a34630eb25086dca746bedda28ea21ec3d9"><code>e5280a3</code></a> ci: checkout repo first</li> <li><a href="https://github.com/squirrelchat/smol-toml/commit/241c256ebda07b2d225c91b7468ed8c90d0e71df"><code>241c256</code></a> chore: version bump</li> <li><a href="https://github.com/squirrelchat/smol-toml/commit/0bfe7f4667fa46122663b7b6114d9514dbd1a91f"><code>0bfe7f4</code></a> chore: build cjs with rolldown instead of esbuild</li> <li><a href="https://github.com/squirrelchat/smol-toml/commit/e0620ab2ac89c40d291f33df41b110277b1acedf"><code>e0620ab</code></a> chore: fmt</li> <li><a href="https://github.com/squirrelchat/smol-toml/commit/96114cb58312084286ae61470ba0f017b87b8023"><code>96114cb</code></a> test: add tests for large integers</li> <li><a href="https://github.com/squirrelchat/smol-toml/commit/f4537b6b3763e761069efdd7d688711c8409645c"><code>f4537b6</code></a> fix: handle missed edge-cases in string parse</li> <li><a href="https://github.com/squirrelchat/smol-toml/commit/7b39aed388cdc811189a6a3683fbee2f8d344244"><code>7b39aed</code></a> chore: include source files in published package</li> <li>Additional commits viewable in <a href="https://github.com/squirrelchat/smol-toml/compare/v1.6.1...v1.7.0">compare view</a></li> </ul> </details> <br /> Updates `electron` from 42.4.1 to 42.5.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/electron/electron/releases">electron's releases</a>.</em></p> <blockquote> <h2>electron v42.5.0</h2> <h1>Release Notes for v42.5.0</h1> <h2>Fixes</h2> <ul> <li>Fixed Windows edge case in <code>NativeWindowViews::MoveBehindTaskBarIfNeeded()</code>. <a href="https://redirect.github.com/electron/electron/pull/52022">#52022</a> <!-- raw HTML omitted -->(Also in <a href="https://redirect.github.com/electron/electron/pull/52023">41</a>, <a href="https://redirect.github.com/electron/electron/pull/52021">43</a>)<!-- raw HTML omitted --></li> <li>Fixed an issue where clicking the maximize button could progressively shrink the window in some Wayland environments. <a href="https://redirect.github.com/electron/electron/pull/52045">#52045</a> <!-- raw HTML omitted -->(Also in <a href="https://redirect.github.com/electron/electron/pull/52039">43</a>)<!-- raw HTML omitted --></li> </ul> <h2>Other Changes</h2> <ul> <li>Backported fixes from upstream Chromium. <a href="https://redirect.github.com/electron/electron/pull/51975">#51975</a></li> <li>Updated Chromium to 148.0.7778.271. <a href="https://redirect.github.com/electron/electron/pull/52038">#52038</a></li> <li>Updated Node.js to v24.17.0. <a href="https://redirect.github.com/electron/electron/pull/52066">#52066</a></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/electron/electron/commit/d7bccf5b2f27969b4e7f34cf877f55cf0813be7a"><code>d7bccf5</code></a> fix: UAF in api::UtilityProcessWrapper (<a href="https://redirect.github.com/electron/electron/issues/52094">#52094</a>)</li> <li><a href="https://github.com/electron/electron/commit/fb151dcdf1ef7685cb5254b571d9ec50a480c1ae"><code>fb151dc</code></a> chore: bump chromium to 148.0.7778.271 (42-x-y) (<a href="https://redirect.github.com/electron/electron/issues/52038">#52038</a>)</li> <li><a href="https://github.com/electron/electron/commit/526fa0df025b50aea9a8011efa52753db8e7839a"><code>526fa0d</code></a> chore: bump node to v24.17.0 (42-x-y) (<a href="https://redirect.github.com/electron/electron/issues/52066">#52066</a>)</li> <li><a href="https://github.com/electron/electron/commit/22b84b5c304c512005c55569f6344d3e0db12fad"><code>22b84b5</code></a> fix: frameless window shrinks on maximize() when compositor declines (<a href="https://redirect.github.com/electron/electron/issues/52045">#52045</a>)</li> <li><a href="https://github.com/electron/electron/commit/15e8d1cf5db75ed5db00d2f4889deca1ee2c1004"><code>15e8d1c</code></a> docs: fix heap dump screenshot in contentTracing (<a href="https://redirect.github.com/electron/electron/issues/52031">#52031</a>)</li> <li><a href="https://github.com/electron/electron/commit/8902c75dca77c80793fe69971d53b7226d921bf9"><code>8902c75</code></a> build: bump <code>@electron/docs-parser</code> to 3.0.1 (<a href="https://redirect.github.com/electron/electron/issues/52052">#52052</a>)</li> <li><a href="https://github.com/electron/electron/commit/30bf7db164d941ee167cbc2f42f7629b1a6fe452"><code>30bf7db</code></a> test: use node16 moduleResolution in tsconfig and clean up imports (<a href="https://redirect.github.com/electron/electron/issues/52040">#52040</a>)</li> <li><a href="https://github.com/electron/electron/commit/d251baeeb3b0c92a66eaca6ab76542b4bb02e46a"><code>d251bae</code></a> ci: bump linux 64k kernel version (<a href="https://redirect.github.com/electron/electron/issues/52037">#52037</a>)</li> <li><a href="https://github.com/electron/electron/commit/ad73d520595a149149ef60bfbee16fd520b68ee7"><code>ad73d52</code></a> fix: win: check for valid taskbar handle (<a href="https://redirect.github.com/electron/electron/issues/52022">#52022</a>)</li> <li><a href="https://github.com/electron/electron/commit/6abd0348c9ec3fdd8eef4ca4029fbd8747000815"><code>6abd034</code></a> ci: replace dorny/paths-filter usage (reland) (<a href="https://redirect.github.com/electron/electron/issues/52033">#52033</a>)</li> <li>Additional commits viewable in <a href="https://github.com/electron/electron/compare/v42.4.1...v42.5.0">compare view</a></li> </ul> </details> <br /> Updates `fast-xml-parser` from 5.9.2 to 5.9.3 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/NaturalIntelligence/fast-xml-parser/releases">fast-xml-parser's releases</a>.</em></p> <blockquote> <h2>v5.9.3</h2> <h2>What's Changed</h2> <ul> <li>Harden GitHub Actions workflows by <a href="https://github.com/martincostello"><code>@martincostello</code></a> in <a href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/841">NaturalIntelligence/fast-xml-parser#841</a></li> <li>GitHub Actions workflow fixes by <a href="https://github.com/martincostello"><code>@martincostello</code></a> in <a href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/844">NaturalIntelligence/fast-xml-parser#844</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/martincostello"><code>@martincostello</code></a> made their first contribution in <a href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/841">NaturalIntelligence/fast-xml-parser#841</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.9.2...v5.9.3">https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.9.2...v5.9.3</a></p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md">fast-xml-parser's changelog</a>.</em></p> <blockquote> <p><!-- raw HTML omitted -->Note: If you find missing information about particular minor version, that version must have been changed without any functional change in this library.<!-- raw HTML omitted --></p> <p>Note: Due to some last quick changes on v4, detail of v4.5.3 & v4.5.4 are not updated here. v4.5.4x is the last tag of v4 in github repository. I'm extremely sorry for the confusion</p> <p>*<em>5.9.3 / 2026-06-19</em></p> <ul> <li>update strnum</li> </ul> <p>*<em>5.9.2 / 2026-06-17</em></p> <ul> <li>dummy release to test changes in github action</li> </ul> <p>*<em>5.9.1 / 2026-06-17</em></p> <ul> <li>dummy release to test release from github action</li> </ul> <p>*<em>5.9.0 / 2026-06-15</em></p> <ul> <li>update strnum to 2.3.0 <ul> <li>you can set hex, binary, enotation, infinity, unicode</li> </ul> </li> <li>validate unsafe HTML or XML data in doctype entities unsing 'is-unsafe' library. User can override rules by overriding EntityDecoder.</li> </ul> <p>*<em>5.8.0 / 2026-05-12</em></p> <ul> <li>integrate xml-naming to validate DOCTYPE entity name and notation name (using qname becaue of backward compatibility) <ul> <li>This will consider xml-version as well. '1.0' is default</li> </ul> </li> <li>update strnum to 2.3.0 <ul> <li>You can set octal and binary parsing which is bydeault off</li> </ul> </li> <li>update fast-xml-builder to 1.2.0 <ul> <li>can sanitize tag names if found invalid</li> <li>fix format output</li> </ul> </li> </ul> <p><strong>5.7.3 / 2006-05-05</strong></p> <ul> <li>fix: alwaysCreateTextNode should create text node when attributes are present for self closing node</li> <li>fix stop node expression when ns prefix is removed (found by <a href="https://github.com/iruizsalinas">iruizsalinas</a>)</li> <li>update XML Builder to 1.1.7</li> <li>mark addEntity deprecated</li> </ul> <p><strong>5.7.2 / 2026-04-25</strong></p> <ul> <li>allow numerical external entity for backward compatibility</li> <li>fix <a href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/705">#705</a>: attributesGroupName working with preserveOrder</li> <li>fix <a href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/817">#817</a>: stackoverflow when tag expression is very long</li> </ul> <p><strong>5.7.1 / 2026-04-20</strong></p> <ul> <li>fix typo in CJS typing file</li> </ul> <p><strong>5.7.0 / 2026-04-17</strong></p> <ul> <li>Use <code>@nodable/entities</code> v2.1.0 <ul> <li>breaking changes <ul> <li>single entity scan. You're not allowed to user entity value to form another entity name.</li> <li>you cant add numeric external entity</li> <li>entity error message when expantion limit is crossed might change</li> </ul> </li> </ul> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/93a09f108d6f457a84e607a6806a7bfd6abc3e55"><code>93a09f1</code></a> 5.9.3</li> <li><a href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/9c1905edd7ea28db70770f758e293fcf079060c5"><code>9c1905e</code></a> update strnum</li> <li><a href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/8d71292a9aaaa8f4a24294e636bd191cfbf4d8a7"><code>8d71292</code></a> GitHub Actions workflow fixes (<a href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/844">#844</a>)</li> <li><a href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/784a0442b697e1498e1f3c3fc1a057ad57424858"><code>784a044</code></a> Harden GitHub Actions workflows (<a href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/841">#841</a>)</li> <li>See full diff in <a href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.9.2...v5.9.3">compare view</a></li> </ul> </details> <br /> Updates `posthog-node` from 5.35.0 to 5.38.6 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/PostHog/posthog-js/releases">posthog-node's releases</a>.</em></p> <blockquote> <h2>posthog-node@5.38.6</h2> <h2>5.38.6</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a> <a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> Thanks <a href="https://github.com/GauthierPLM"><code>@GauthierPLM</code></a>! - Only stamp $groups from the top-level <code>groups</code> when present (2026-06-26)</li> </ul> <h2>posthog-node@5.38.5</h2> <h2>5.38.5</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a> <a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Dedupe feature flag called events per returned flag value. (2026-06-24)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.3</li> </ul> </li> </ul> <h2>posthog-node@5.38.4</h2> <h2>5.38.4</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a> <a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> Thanks <a href="https://github.com/dustinbyrne"><code>@dustinbyrne</code></a>! - Respect <code>disable_capture_url_hashes</code> when capturing Express and NestJS request URLs. (2026-06-23)</li> </ul> <h2>posthog-node@5.38.3</h2> <h2>5.38.3</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a> <a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Deprecate the Node SDK Sentry integration and point users to PostHog Error Tracking for Node. (2026-06-23)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.0</li> </ul> </li> </ul> <h2>posthog-node@5.38.2</h2> <h2>5.38.2</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a> <a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Validate custom event UUID overrides and generate new UUIDs when invalid. (2026-06-19)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.3</li> </ul> </li> </ul> <h2>posthog-node@5.38.1</h2> <h2>5.38.1</h2> <h3>Patch Changes</h3> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md">posthog-node's changelog</a>.</em></p> <blockquote> <h2>5.38.6</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a> <a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> Thanks <a href="https://github.com/GauthierPLM"><code>@GauthierPLM</code></a>! - Only stamp $groups from the top-level <code>groups</code> when present (2026-06-26)</li> </ul> <h2>5.38.5</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a> <a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Dedupe feature flag called events per returned flag value. (2026-06-24)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.3</li> </ul> </li> </ul> <h2>5.38.4</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a> <a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> Thanks <a href="https://github.com/dustinbyrne"><code>@dustinbyrne</code></a>! - Respect <code>disable_capture_url_hashes</code> when capturing Express and NestJS request URLs. (2026-06-23)</li> </ul> <h2>5.38.3</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a> <a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Deprecate the Node SDK Sentry integration and point users to PostHog Error Tracking for Node. (2026-06-23)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.0</li> </ul> </li> </ul> <h2>5.38.2</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a> <a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Validate custom event UUID overrides and generate new UUIDs when invalid. (2026-06-19)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.3</li> </ul> </li> </ul> <h2>5.38.1</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3886">#3886</a> <a href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Stop sending deprecated no-op top-level <code>type</code>, <code>library</code>, and <code>library_version</code> fields in event batch payloads. Use <code>properties.$lib</code> and <code>properties.$lib_version</code> for SDK metadata; legacy queued <code>library</code> and <code>library_version</code> values are used as fallbacks when the official <code>$</code> properties are missing. (2026-06-18)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.2</li> </ul> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/PostHog/posthog-js/commit/d2faa824af1af2cb50b47178fb953bfc8812b389"><code>d2faa82</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> fix: preserve $groups passed via properties (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3896">#3896</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/5c4838f1671ece80d153aaeb6d946bb3e8c8240a"><code>5c4838f</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> fix: Dedupe feature flag called events by response (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3955">#3955</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/2a60c21134dd118c2367e56129bc9427079c3841"><code>2a60c21</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> fix(node): respect URL hash capture config (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3938">#3938</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/a3136c6de3114ee3d58e59a0c86f2b9c4f59a2b0"><code>a3136c6</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/bf6947d3e12ac512c99185be9b8b134c04eb563a"><code>bf6947d</code></a> feat(react-native): support session replay event triggers (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3932">#3932</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/2628a1af5f06f73cb2252de002e0da4b2d1a25a5"><code>2628a1a</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> chore(node): mark Sentry integration deprecated (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3937">#3937</a>)</li> <li>Additional commits viewable in <a href="https://github.com/PostHog/posthog-js/commits/posthog-node@5.38.6/packages/node">compare view</a></li> </ul> </details> <br /> Updates `posthog-node` from 5.35.0 to 5.38.6 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/PostHog/posthog-js/releases">posthog-node's releases</a>.</em></p> <blockquote> <h2>posthog-node@5.38.6</h2> <h2>5.38.6</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a> <a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> Thanks <a href="https://github.com/GauthierPLM"><code>@GauthierPLM</code></a>! - Only stamp $groups from the top-level <code>groups</code> when present (2026-06-26)</li> </ul> <h2>posthog-node@5.38.5</h2> <h2>5.38.5</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a> <a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Dedupe feature flag called events per returned flag value. (2026-06-24)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.3</li> </ul> </li> </ul> <h2>posthog-node@5.38.4</h2> <h2>5.38.4</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a> <a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> Thanks <a href="https://github.com/dustinbyrne"><code>@dustinbyrne</code></a>! - Respect <code>disable_capture_url_hashes</code> when capturing Express and NestJS request URLs. (2026-06-23)</li> </ul> <h2>posthog-node@5.38.3</h2> <h2>5.38.3</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a> <a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Deprecate the Node SDK Sentry integration and point users to PostHog Error Tracking for Node. (2026-06-23)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.0</li> </ul> </li> </ul> <h2>posthog-node@5.38.2</h2> <h2>5.38.2</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a> <a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Validate custom event UUID overrides and generate new UUIDs when invalid. (2026-06-19)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.3</li> </ul> </li> </ul> <h2>posthog-node@5.38.1</h2> <h2>5.38.1</h2> <h3>Patch Changes</h3> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md">posthog-node's changelog</a>.</em></p> <blockquote> <h2>5.38.6</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a> <a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> Thanks <a href="https://github.com/GauthierPLM"><code>@GauthierPLM</code></a>! - Only stamp $groups from the top-level <code>groups</code> when present (2026-06-26)</li> </ul> <h2>5.38.5</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a> <a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Dedupe feature flag called events per returned flag value. (2026-06-24)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.3</li> </ul> </li> </ul> <h2>5.38.4</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a> <a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> Thanks <a href="https://github.com/dustinbyrne"><code>@dustinbyrne</code></a>! - Respect <code>disable_capture_url_hashes</code> when capturing Express and NestJS request URLs. (2026-06-23)</li> </ul> <h2>5.38.3</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a> <a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Deprecate the Node SDK Sentry integration and point users to PostHog Error Tracking for Node. (2026-06-23)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.0</li> </ul> </li> </ul> <h2>5.38.2</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a> <a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Validate custom event UUID overrides and generate new UUIDs when invalid. (2026-06-19)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.3</li> </ul> </li> </ul> <h2>5.38.1</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3886">#3886</a> <a href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Stop sending deprecated no-op top-level <code>type</code>, <code>library</code>, and <code>library_version</code> fields in event batch payloads. Use <code>properties.$lib</code> and <code>properties.$lib_version</code> for SDK metadata; legacy queued <code>library</code> and <code>library_version</code> values are used as fallbacks when the official <code>$</code> properties are missing. (2026-06-18)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.2</li> </ul> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/PostHog/posthog-js/commit/d2faa824af1af2cb50b47178fb953bfc8812b389"><code>d2faa82</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> fix: preserve $groups passed via properties (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3896">#3896</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/5c4838f1671ece80d153aaeb6d946bb3e8c8240a"><code>5c4838f</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> fix: Dedupe feature flag called events by response (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3955">#3955</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/2a60c21134dd118c2367e56129bc9427079c3841"><code>2a60c21</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> fix(node): respect URL hash capture config (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3938">#3938</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/a3136c6de3114ee3d58e59a0c86f2b9c4f59a2b0"><code>a3136c6</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/bf6947d3e12ac512c99185be9b8b134c04eb563a"><code>bf6947d</code></a> feat(react-native): support session replay event triggers (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3932">#3932</a>)</li> <li><a href="https://github.com/PostHog/posthog-js/commit/2628a1af5f06f73cb2252de002e0da4b2d1a25a5"><code>2628a1a</code></a> chore: update versions and lockfile [version bump]</li> <li><a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> chore(node): mark Sentry integration deprecated (<a href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3937">#3937</a>)</li> <li>Additional commits viewable in <a href="https://github.com/PostHog/posthog-js/commits/posthog-node@5.38.6/packages/node">compare view</a></li> </ul> </details> <br /> Updates `posthog-node` from 5.35.0 to 5.38.6 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/PostHog/posthog-js/releases">posthog-node's releases</a>.</em></p> <blockquote> <h2>posthog-node@5.38.6</h2> <h2>5.38.6</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a> <a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> Thanks <a href="https://github.com/GauthierPLM"><code>@GauthierPLM</code></a>! - Only stamp $groups from the top-level <code>groups</code> when present (2026-06-26)</li> </ul> <h2>posthog-node@5.38.5</h2> <h2>5.38.5</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a> <a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Dedupe feature flag called events per returned flag value. (2026-06-24)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.3</li> </ul> </li> </ul> <h2>posthog-node@5.38.4</h2> <h2>5.38.4</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a> <a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> Thanks <a href="https://github.com/dustinbyrne"><code>@dustinbyrne</code></a>! - Respect <code>disable_capture_url_hashes</code> when capturing Express and NestJS request URLs. (2026-06-23)</li> </ul> <h2>posthog-node@5.38.3</h2> <h2>5.38.3</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a> <a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Deprecate the Node SDK Sentry integration and point users to PostHog Error Tracking for Node. (2026-06-23)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.0</li> </ul> </li> </ul> <h2>posthog-node@5.38.2</h2> <h2>5.38.2</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a> <a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Validate custom event UUID overrides and generate new UUIDs when invalid. (2026-06-19)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.3</li> </ul> </li> </ul> <h2>posthog-node@5.38.1</h2> <h2>5.38.1</h2> <h3>Patch Changes</h3> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md">posthog-node's changelog</a>.</em></p> <blockquote> <h2>5.38.6</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a> <a href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a> Thanks <a href="https://github.com/GauthierPLM"><code>@GauthierPLM</code></a>! - Only stamp $groups from the top-level <code>groups</code> when present (2026-06-26)</li> </ul> <h2>5.38.5</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a> <a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Dedupe feature flag called events per returned flag value. (2026-06-24)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.3</li> </ul> </li> </ul> <h2>5.38.4</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a> <a href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a> Thanks <a href="https://github.com/dustinbyrne"><code>@dustinbyrne</code></a>! - Respect <code>disable_capture_url_hashes</code> when capturing Express and NestJS request URLs. (2026-06-23)</li> </ul> <h2>5.38.3</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a> <a href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Deprecate the Node SDK Sentry integration and point users to PostHog Error Tracking for Node. (2026-06-23)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.37.0</li> </ul> </li> </ul> <h2>5.38.2</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a> <a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Validate custom event UUID overrides and generate new UUIDs when invalid. (2026-06-19)</li> <li>Updated dependencies [<a href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]: <ul> <li><code>@posthog/core</code><a href="https://github.com/1"><code>@1</code></a>.35.3</li> </ul> </li> </ul> <h2>5.38.1</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/PostHog/posthog-js/pull/3886">#3886</a> <a href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a> Thanks <a href="https://github.com/marandaneto"><code>@marandaneto</code></a>! - Stop sending deprecated no-op top-level <code>type<... _Description has been truncated_ Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
02695e6704 |
chore(deps)(deps): bump the npm-major group across 1 directory with 3 updates (#413)
Bumps the npm-major group with 3 updates in the / directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [dotenv](https://github.com/motdotla/dotenv) and [ini](https://github.com/npm/ini). Updates `@types/node` from 25.9.3 to 26.0.1 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare view</a></li> </ul> </details> <br /> Updates `dotenv` from 16.6.1 to 17.4.2 <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md">dotenv's changelog</a>.</em></p> <blockquote> <h2><a href="https://github.com/motdotla/dotenv/compare/v17.4.1...v17.4.2">17.4.2</a> (2026-04-12)</h2> <h3>Changed</h3> <ul> <li>Improved skill files - tightened up details (<a href="https://redirect.github.com/motdotla/dotenv/pull/1009">#1009</a>)</li> </ul> <h2><a href="https://github.com/motdotla/dotenv/compare/v17.4.0...v17.4.1">17.4.1</a> (2026-04-05)</h2> <h3>Changed</h3> <ul> <li>Change text <code>injecting</code> to <code>injected</code> (<a href="https://redirect.github.com/motdotla/dotenv/pull/1005">#1005</a>)</li> </ul> <h2><a href="https://github.com/motdotla/dotenv/compare/v17.3.1...v17.4.0">17.4.0</a> (2026-04-01)</h2> <h3>Added</h3> <ul> <li>Add <code>skills/</code> folder with focused agent skills: <code>skills/dotenv/SKILL.md</code> (core usage) and <code>skills/dotenvx/SKILL.md</code> (encryption, multiple environments, variable expansion) for AI coding agent discovery via the skills.sh ecosystem (<code>npx skills add motdotla/dotenv</code>)</li> </ul> <h3>Changed</h3> <ul> <li>Tighten up logs: <code>◇ injecting env (14) from .env</code> (<a href="https://redirect.github.com/motdotla/dotenv/pull/1003">#1003</a>)</li> </ul> <h2><a href="https://github.com/motdotla/dotenv/compare/v17.3.0...v17.3.1">17.3.1</a> (2026-02-12)</h2> <h3>Changed</h3> <ul> <li>Fix as2 example command in README and update spanish README</li> </ul> <h2><a href="https://github.com/motdotla/dotenv/compare/v17.2.4...v17.3.0">17.3.0</a> (2026-02-12)</h2> <h3>Added</h3> <ul> <li>Add a new README section on dotenv’s approach to the agentic future.</li> </ul> <h3>Changed</h3> <ul> <li>Rewrite README to get humans started more quickly with less noise while simultaneously making more accessible for llms and agents to go deeper into details.</li> </ul> <h2><a href="https://github.com/motdotla/dotenv/compare/v17.2.3...v17.2.4">17.2.4</a> (2026-02-05)</h2> <h3>Changed</h3> <ul> <li>Make <code>DotenvPopulateInput</code> accept <code>NodeJS.ProcessEnv</code> type (<a href="https://redirect.github.com/motdotla/dotenv/pull/915">#915</a>)</li> </ul> <ul> <li>Give back to dotenv by checking out my newest project <a href="https://github.com/vestauth/vestauth">vestauth</a>. It is auth for agents. Thank you for using my software.</li> </ul> <h2><a href="https://github.com/motdotla/dotenv/compare/v17.2.2...v17.2.3">17.2.3</a> (2025-09-29)</h2> <h3>Changed</h3> <ul> <li>Fixed typescript error definition (<a href="https://redirect.github.com/motdotla/dotenv/pull/912">#912</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/motdotla/dotenv/commit/f116f70310abab44fbfddbaeb833698b5bf84a9b"><code>f116f70</code></a> 17.4.2</li> <li><a href="https://github.com/motdotla/dotenv/commit/3a8161274fdd745239b86e604f4a7e972a1d3902"><code>3a81612</code></a> fix visual order of faq</li> <li><a href="https://github.com/motdotla/dotenv/commit/13f55a89e136b2024e68d277b836dd5260fc16cf"><code>13f55a8</code></a> Merge branch 'skill'</li> <li><a href="https://github.com/motdotla/dotenv/commit/4bbbf73f0906bd69975c48bf310a84b686e5b1b4"><code>4bbbf73</code></a> reorganize faq</li> <li><a href="https://github.com/motdotla/dotenv/commit/c3da64bb2ba1d0e02f8b9b2b7ccb7e6f7a51d56c"><code>c3da64b</code></a> Merge pull request <a href="https://redirect.github.com/motdotla/dotenv/issues/1009">#1009</a> from motdotla/skill</li> <li><a href="https://github.com/motdotla/dotenv/commit/6f743b173fbd6c26f7eab7040d251f9a6c8b977d"><code>6f743b1</code></a> update source</li> <li><a href="https://github.com/motdotla/dotenv/commit/fc2c6247e858a32d4024cb06a5b0c79aa35851f5"><code>fc2c624</code></a> update skill</li> <li><a href="https://github.com/motdotla/dotenv/commit/972315ba74bb2bbba4483d112e853fd26006ef8a"><code>972315b</code></a> Tighten up skill</li> <li><a href="https://github.com/motdotla/dotenv/commit/2795fce3d1ed07b4c570f1e06ab1c0d533c86997"><code>2795fce</code></a> reorganize faq</li> <li><a href="https://github.com/motdotla/dotenv/commit/d5495d4ae8e4e41ef9a682c9e00c81552794274e"><code>d5495d4</code></a> adjust skill</li> <li>Additional commits viewable in <a href="https://github.com/motdotla/dotenv/compare/v16.6.1...v17.4.2">compare view</a></li> </ul> </details> <br /> Updates `ini` from 4.1.3 to 7.0.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/npm/ini/releases">ini's releases</a>.</em></p> <blockquote> <h2>v7.0.0</h2> <h2><a href="https://github.com/npm/ini/compare/v6.0.0...v7.0.0">7.0.0</a> (2026-05-08)</h2> <h3>⚠️ BREAKING CHANGES</h3> <ul> <li><code>ini</code> now supports node <code>^22.22.2 || ^24.15.0 || >=26.0.0</code></li> <li>template-oss-apply</li> </ul> <h3>Features</h3> <ul> <li><a href="https://github.com/npm/ini/commit/55b6841261aad580ce979c1ada97885a0617b965"><code>55b6841</code></a> <a href="https://redirect.github.com/npm/ini/pull/301">#301</a> bump to new node engine range (<a href="https://github.com/owlstronaut"><code>@owlstronaut</code></a>)</li> <li><a href="https://github.com/npm/ini/commit/2b11ba8283afa33211844be6fa29794970ccf7b7"><code>2b11ba8</code></a> <a href="https://redirect.github.com/npm/ini/pull/301">#301</a> template-oss-apply (<a href="https://github.com/owlstronaut"><code>@owlstronaut</code></a>)</li> </ul> <h3>Chores</h3> <ul> <li><a href="https://github.com/npm/ini/commit/f6ed5bee3d7b1c175b687c703213217b23df9199"><code>f6ed5be</code></a> <a href="https://redirect.github.com/npm/ini/pull/301">#301</a> template-oss-apply (<a href="https://github.com/owlstronaut"><code>@owlstronaut</code></a>)</li> <li><a href="https://github.com/npm/ini/commit/a2c835ec4690c5e67651a403d0f952aaff018f42"><code>a2c835e</code></a> <a href="https://redirect.github.com/npm/ini/pull/293">#293</a> bump <code>@npmcli/eslint-config</code> from 5.1.0 to 6.0.0 (<a href="https://redirect.github.com/npm/ini/issues/293">#293</a>) (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot])</li> <li><a href="https://github.com/npm/ini/commit/3661dce193f03675ec6ddc0938e2483d0ab41f38"><code>3661dce</code></a> <a href="https://redirect.github.com/npm/ini/pull/296">#296</a> bump <code>@npmcli/template-oss</code> from 4.28.0 to 4.28.1 (<a href="https://redirect.github.com/npm/ini/issues/296">#296</a>) (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot], <a href="https://github.com/npm-cli-bot"><code>@npm-cli-bot</code></a>)</li> </ul> <h2>v6.0.0</h2> <h2><a href="https://github.com/npm/ini/compare/v5.0.0...v6.0.0">6.0.0</a> (2025-10-22)</h2> <h3>⚠️ BREAKING CHANGES</h3> <ul> <li><code>ini</code> now supports node <code>^20.17.0 || >=22.9.0</code></li> </ul> <h3>Bug Fixes</h3> <ul> <li><a href="https://github.com/npm/ini/commit/6d395a4aaf7d7a5e32e250809cb7bd04c6a27b66"><code>6d395a4</code></a> <a href="https://redirect.github.com/npm/ini/pull/291">#291</a> align to npm 11 node engine range (<a href="https://redirect.github.com/npm/ini/issues/291">#291</a>) (<a href="https://github.com/owlstronaut"><code>@owlstronaut</code></a>)</li> </ul> <h3>Chores</h3> <ul> <li><a href="https://github.com/npm/ini/commit/23a36a9ca194d97486ed367f63d13a2cde1d1ce5"><code>23a36a9</code></a> <a href="https://redirect.github.com/npm/ini/pull/290">#290</a> bump <code>@npmcli/template-oss</code> from 4.26.0 to 4.27.1 (<a href="https://redirect.github.com/npm/ini/issues/290">#290</a>) (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot], <a href="https://github.com/npm-cli-bot"><code>@npm-cli-bot</code></a>)</li> </ul> <h2>v5.0.0</h2> <h2><a href="https://github.com/npm/ini/compare/v4.1.3...v5.0.0">5.0.0</a> (2024-09-03)</h2> <h3>⚠️ BREAKING CHANGES</h3> <ul> <li><code>ini</code> now supports node <code>^18.17.0 || >=20.5.0</code></li> </ul> <h3>Bug Fixes</h3> <ul> <li><a href="https://github.com/npm/ini/commit/3eca645de8586b8349acd79211b01a9199d8fd32"><code>3eca645</code></a> <a href="https://redirect.github.com/npm/ini/pull/279">#279</a> align to npm 10 node engine range (<a href="https://github.com/hashtagchris"><code>@hashtagchris</code></a>)</li> </ul> <h3>Chores</h3> <ul> <li><a href="https://github.com/npm/ini/commit/c89e2090d7989bb91255ec46eaaf5f8cb1c34b34"><code>c89e209</code></a> <a href="https://redirect.github.com/npm/ini/pull/279">#279</a> run template-oss-apply (<a href="https://github.com/hashtagchris"><code>@hashtagchris</code></a>)</li> <li><a href="https://github.com/npm/ini/commit/fc447500efe6523746fd9db9d3123485eed14b76"><code>fc44750</code></a> <a href="https://redirect.github.com/npm/ini/pull/276">#276</a> bump <code>@npmcli/eslint-config</code> from 4.0.5 to 5.0.0 (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot])</li> <li><a href="https://github.com/npm/ini/commit/21c20bb1729005ab46e0f248cea1a881c6ab5b49"><code>21c20bb</code></a> <a href="https://redirect.github.com/npm/ini/pull/277">#277</a> postinstall for dependabot template-oss PR (<a href="https://github.com/hashtagchris"><code>@hashtagchris</code></a>)</li> <li><a href="https://github.com/npm/ini/commit/44b3b503fdd923fb230b6a4d859014b9364ee466"><code>44b3b50</code></a> <a href="https://redirect.github.com/npm/ini/pull/277">#277</a> bump <code>@npmcli/template-oss</code> from 4.23.1 to 4.23.3 (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot])</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/npm/ini/blob/main/CHANGELOG.md">ini's changelog</a>.</em></p> <blockquote> <h2><a href="https://github.com/npm/ini/compare/v6.0.0...v7.0.0">7.0.0</a> (2026-05-08)</h2> <h3>⚠️ BREAKING CHANGES</h3> <ul> <li><code>ini</code> now supports node <code>^22.22.2 || ^24.15.0 || >=26.0.0</code></li> <li>template-oss-apply</li> </ul> <h3>Features</h3> <ul> <li><a href="https://github.com/npm/ini/commit/55b6841261aad580ce979c1ada97885a0617b965"><code>55b6841</code></a> <a href="https://redirect.github.com/npm/ini/pull/301">#301</a> bump to new node engine range (<a href="https://github.com/owlstronaut"><code>@owlstronaut</code></a>)</li> <li><a href="https://github.com/npm/ini/commit/2b11ba8283afa33211844be6fa29794970ccf7b7"><code>2b11ba8</code></a> <a href="https://redirect.github.com/npm/ini/pull/301">#301</a> template-oss-apply (<a href="https://github.com/owlstronaut"><code>@owlstronaut</code></a>)</li> </ul> <h3>Chores</h3> <ul> <li><a href="https://github.com/npm/ini/commit/f6ed5bee3d7b1c175b687c703213217b23df9199"><code>f6ed5be</code></a> <a href="https://redirect.github.com/npm/ini/pull/301">#301</a> template-oss-apply (<a href="https://github.com/owlstronaut"><code>@owlstronaut</code></a>)</li> <li><a href="https://github.com/npm/ini/commit/a2c835ec4690c5e67651a403d0f952aaff018f42"><code>a2c835e</code></a> <a href="https://redirect.github.com/npm/ini/pull/293">#293</a> bump <code>@npmcli/eslint-config</code> from 5.1.0 to 6.0.0 (<a href="https://redirect.github.com/npm/ini/issues/293">#293</a>) (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot])</li> <li><a href="https://github.com/npm/ini/commit/3661dce193f03675ec6ddc0938e2483d0ab41f38"><code>3661dce</code></a> <a href="https://redirect.github.com/npm/ini/pull/296">#296</a> bump <code>@npmcli/template-oss</code> from 4.28.0 to 4.28.1 (<a href="https://redirect.github.com/npm/ini/issues/296">#296</a>) (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot], <a href="https://github.com/npm-cli-bot"><code>@npm-cli-bot</code></a>)</li> </ul> <h2><a href="https://github.com/npm/ini/compare/v5.0.0...v6.0.0">6.0.0</a> (2025-10-22)</h2> <h3>⚠️ BREAKING CHANGES</h3> <ul> <li><code>ini</code> now supports node <code>^20.17.0 || >=22.9.0</code></li> </ul> <h3>Bug Fixes</h3> <ul> <li><a href="https://github.com/npm/ini/commit/6d395a4aaf7d7a5e32e250809cb7bd04c6a27b66"><code>6d395a4</code></a> <a href="https://redirect.github.com/npm/ini/pull/291">#291</a> align to npm 11 node engine range (<a href="https://redirect.github.com/npm/ini/issues/291">#291</a>) (<a href="https://github.com/owlstronaut"><code>@owlstronaut</code></a>)</li> </ul> <h3>Chores</h3> <ul> <li><a href="https://github.com/npm/ini/commit/23a36a9ca194d97486ed367f63d13a2cde1d1ce5"><code>23a36a9</code></a> <a href="https://redirect.github.com/npm/ini/pull/290">#290</a> bump <code>@npmcli/template-oss</code> from 4.26.0 to 4.27.1 (<a href="https://redirect.github.com/npm/ini/issues/290">#290</a>) (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot], <a href="https://github.com/npm-cli-bot"><code>@npm-cli-bot</code></a>)</li> </ul> <h2><a href="https://github.com/npm/ini/compare/v4.1.3...v5.0.0">5.0.0</a> (2024-09-03)</h2> <h3>⚠️ BREAKING CHANGES</h3> <ul> <li><code>ini</code> now supports node <code>^18.17.0 || >=20.5.0</code></li> </ul> <h3>Bug Fixes</h3> <ul> <li><a href="https://github.com/npm/ini/commit/3eca645de8586b8349acd79211b01a9199d8fd32"><code>3eca645</code></a> <a href="https://redirect.github.com/npm/ini/pull/279">#279</a> align to npm 10 node engine range (<a href="https://github.com/hashtagchris"><code>@hashtagchris</code></a>)</li> </ul> <h3>Chores</h3> <ul> <li><a href="https://github.com/npm/ini/commit/c89e2090d7989bb91255ec46eaaf5f8cb1c34b34"><code>c89e209</code></a> <a href="https://redirect.github.com/npm/ini/pull/279">#279</a> run template-oss-apply (<a href="https://github.com/hashtagchris"><code>@hashtagchris</code></a>)</li> <li><a href="https://github.com/npm/ini/commit/fc447500efe6523746fd9db9d3123485eed14b76"><code>fc44750</code></a> <a href="https://redirect.github.com/npm/ini/pull/276">#276</a> bump <code>@npmcli/eslint-config</code> from 4.0.5 to 5.0.0 (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot])</li> <li><a href="https://github.com/npm/ini/commit/21c20bb1729005ab46e0f248cea1a881c6ab5b49"><code>21c20bb</code></a> <a href="https://redirect.github.com/npm/ini/pull/277">#277</a> postinstall for dependabot template-oss PR (<a href="https://github.com/hashtagchris"><code>@hashtagchris</code></a>)</li> <li><a href="https://github.com/npm/ini/commit/44b3b503fdd923fb230b6a4d859014b9364ee466"><code>44b3b50</code></a> <a href="https://redirect.github.com/npm/ini/pull/277">#277</a> bump <code>@npmcli/template-oss</code> from 4.23.1 to 4.23.3 (<a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot])</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/npm/ini/commit/847941ced4fb8465f0ccb383fd8b15c7e5aa09fc"><code>847941c</code></a> chore: release 7.0.0 (<a href="https://redirect.github.com/npm/ini/issues/302">#302</a>)</li> <li><a href="https://github.com/npm/ini/commit/f6ed5bee3d7b1c175b687c703213217b23df9199"><code>f6ed5be</code></a> chore: template-oss-apply</li> <li><a href="https://github.com/npm/ini/commit/55b6841261aad580ce979c1ada97885a0617b965"><code>55b6841</code></a> feat!: bump to new node engine range</li> <li><a href="https://github.com/npm/ini/commit/2b11ba8283afa33211844be6fa29794970ccf7b7"><code>2b11ba8</code></a> feat!: template-oss-apply</li> <li><a href="https://github.com/npm/ini/commit/e8d16cbbbb91d503fe14163024bd2a01a0446d32"><code>e8d16cb</code></a> deps & engine update</li> <li><a href="https://github.com/npm/ini/commit/3661dce193f03675ec6ddc0938e2483d0ab41f38"><code>3661dce</code></a> chore: bump <code>@npmcli/template-oss</code> from 4.28.0 to 4.28.1 (<a href="https://redirect.github.com/npm/ini/issues/296">#296</a>)</li> <li><a href="https://github.com/npm/ini/commit/5d67f4b487a00aa3e2119776192030356e0161ba"><code>5d67f4b</code></a> chore: bump <code>@npmcli/template-oss</code> from 4.27.1 to 4.28.0 (<a href="https://redirect.github.com/npm/ini/issues/294">#294</a>)</li> <li><a href="https://github.com/npm/ini/commit/a2c835ec4690c5e67651a403d0f952aaff018f42"><code>a2c835e</code></a> chore: bump <code>@npmcli/eslint-config</code> from 5.1.0 to 6.0.0 (<a href="https://redirect.github.com/npm/ini/issues/293">#293</a>)</li> <li><a href="https://github.com/npm/ini/commit/180a8d5c72d7f13ed70c53619132f3a8ee5ac6ed"><code>180a8d5</code></a> chore: release 6.0.0 (<a href="https://redirect.github.com/npm/ini/issues/292">#292</a>)</li> <li><a href="https://github.com/npm/ini/commit/6d395a4aaf7d7a5e32e250809cb7bd04c6a27b66"><code>6d395a4</code></a> fix!: align to npm 11 node engine range (<a href="https://redirect.github.com/npm/ini/issues/291">#291</a>)</li> <li>Additional commits viewable in <a href="https://github.com/npm/ini/compare/v4.1.3...v7.0.0">compare view</a></li> </ul> </details> <details> <summary>Maintainer changes</summary> <p>This version was pushed to npm by <a href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new releaser for ini since your current version.</p> </details> <br /> Updates `@types/node` from 25.9.3 to 26.0.1 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare view</a></li> </ul> </details> <br /> Updates `@types/node` from 25.9.3 to 26.0.1 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare view</a></li> </ul> </details> <br /> Updates `@types/node` from 25.9.3 to 26.0.1 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare view</a></li> </ul> </details> <br /> Updates `@types/node` from 25.9.3 to 26.0.1 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare view</a></li> </ul> </details> <br /> Updates `@types/node` from 25.9.3 to 26.0.1 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare view</a></li> </ul> </details> <br /> Updates `@types/node` from 25.9.3 to 26.0.1 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare view</a></li> </ul> </details> <br /> Updates `@types/node` from 25.9.3 to 26.0.1 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
270c9a158a |
chore: bump to 0.13.0 (#412)
as per title |
||
|
|
31bb59fb60 |
chore: bump all workspace packages to 0.12.1 (#381)
Bumps every workspace package under `packages/*` from `0.12.0` → `0.12.1`, keeping the monorepo's lockstep versioning intact. ## Changes - `version` bumped to `0.12.1` in all 15 workspace `package.json` files - `package-lock.json` workspace entries synced to match ## Verification - `npm run check:versions` → `All workspace packages are at 0.12.1.` 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
8483fe23f9 |
feat: add telemetry (#274)
## Summary Adds anonymous, opt-out telemetry for Argent installer, update/uninstall flows, CLI telemetry controls, tool-server lifecycle, and tool invocation health signals. ## What Changed - Added a new private `@argent/telemetry` package - Introduced typed telemetry events with runtime property allowlisting/sanitization. - Added lazy anonymous identity creation in `~/.argent/telemetry-id`. - Added consent handling via: `argent telemetry enable`, `argent telemetry disable`, `argent telemetry status` - Added local debug logging with `ARGENT_TELEMETRY_DEBUG=1`. - Tracks installer funnel events for init, global install/update decisions, editor selection, allowlist decisions, skill installation, rules/agents copy, update, and uninstall. - Tracks tool-server start/stop and tool invocation/complete/fail events. - Adds per-tool invocation IDs so request metadata can be correlated without relying on ordering. - Records only coarse platform metadata (`ios` / `android`) for device tools - Ensures short-lived commands drain telemetry with bounded shutdown before exit. - Wires telemetry package into bundling and injects full Argent CLI version metadata at build time. - Added central `FAILURE_CODES` and `FailureSignal` helpers in `@argent/registry`. - Attached structured, sanitized failure metadata across registry errors, tool-server tools/services, HTTP validation/dependency gates, installer flows, and `argent run`. - Added telemetry allowlisting for safe failure fields only: static error codes/stages, coarse area/kind/command, bounded exit codes, allowlisted signals/spawn codes, and coarse network failure classes. - Split CLI-local `argent run` failures into `cli:run_fail` so they are not double-counted as tool failures. - Added installer telemetry for failed package actions, update/uninstall failures, rules/agents copy results, and skill refresh results. - Made telemetry consent writes atomic and kept telemetry status side-effect free via `peekAnonId()`. - Adds a coarse, anonymous signal to tool telemetry: which AI coding tool is driving the MCP server (Claude Code, Codex, Cursor, Gemini, VS Code, Windsurf, Zed, opencode, Copilot, or other). ## Tests Added / Updated - Telemetry unit coverage for consent precedence, identity creation, base props, sanitization, PostHog config, debug logging, shutdown, opt-out, and forget behavior. - Registry/tool-server tests for invocation IDs, platform metadata boundaries, startup failure flushing, and tool lifecycle telemetry. - Installer tests for final telemetry drain, update action classification, and uninstall identity cleanup ordering. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
02e500884f |
chore(deps): npm-minor-patch group excluding tree-sitter (fixes #348) (#355)
## What Applies the safe updates from dependabot #348 (`npm-minor-patch` group) and **excludes the one bump that breaks the build**, `tree-sitter`. | Package | From | To | | --- | --- | --- | | prettier | 3.8.3 | 3.8.4 | | @clack/prompts | 1.1.0 / 1.4.0 | 1.5.1 | | @types/node | 25.9.0 | 25.9.3 | | esbuild | 0.28.0 | 0.28.1 | | semver | 7.7.4 / 7.8.0 | 7.8.4 | | vitest | 4.1.6 | 4.1.9 | | tree-sitter | 0.21.1 | **held at 0.21.1** | (`ws` was part of #348's group but is already at 8.21.0 on `main`, so it's untouched here.) ## Why tree-sitter is excluded `tree-sitter@0.25.0` ships **no Node 24 prebuilds**, and its `binding.gyp` forces `-std=c++17` while Node 22+ v8 (cppgc) headers require C++20, so the native build fails `npm ci`/`npm install` on current Node. That's exactly why #348's `ESLint` job (Node 24) fails at install: ``` .../node_modules/tree-sitter ... node-gyp-build .../include/node/v8config.h:13:2: error: #error "C++20 or later required." ``` (#348's `Unit tests` job is pinned to Node 20, where it still compiles — which is why that PR looks half-green.) There is no tree-sitter release between 0.22.4 and 0.25.0, so there's no newer working version. `^0.21.1` ships N-API prebuilds (no compile) and installs cleanly on Node 20 / 22 / 24. This PR also adds a Dependabot `ignore` for tree-sitter minor/major so the group stops re-proposing the broken bump. Patch updates stay allowed; drop the ignore once upstream restores prebuilt binaries. ## Verification (local) - Lockfile regenerated on **Node 20 / npm 10.8.2** (`npm install --package-lock-only --ignore-scripts`), idempotent under a re-run — won't trip the strict lockfile check. tree-sitter resolves to 0.21.1 (prebuilt, no native compile). - Full CI-equivalent on **Node 20**: `tsc --build`, `eslint --max-warnings 0`, `prettier --check`, `typecheck:tests`/`typecheck:scripts`, full unit suite incl. **1250 tool-server tests** — all green. - `npm ci` from the committed lockfile succeeds. - **Live device E2E**: ran the built tool-server with the bumped deps, captured a real screenshot of a booted iPhone 16 Pro Max (`simulator-client` uses `ws`), then downloaded the PNG artifact over HTTP — `200`, 331 KB PNG. Supersedes #348 (Dependabot auto-closes it once the safe bumps land and tree-sitter is ignored). |
||
|
|
c832109573 |
chore: bump versions to 0.12.0 (#358)
Bumps the package suite from `0.11.0` to **`0.12.0`**. ## Changed - All release packages `0.11.0 → 0.12.0`: `@swmansion/argent`, `@argent/cli`, `@argent/installer`, `@argent/mcp`, `@argent/tools-client`, `@argent/native-devtools-android`, `@argent/native-devtools-ios`, `@argent/registry`, `@argent/skills`, `@argent/tool-server`, `@argent/update-core`, `@argent/configuration-core`. - `@argent/preview-window` `0.9.0 → 0.12.0` — aligned to the suite (was lagging a release behind; `private`/unpublished). - Regenerated the matching `package-lock.json` workspace version entries. ## Not changed - `@argent/ui` stays at `0.0.1` (separate pre-release track). - The private workspace root has no version. - Internal deps use `file:../` specifiers, so no dependency ranges needed updating. No source/runtime changes — `argent --version` reads from `package.json` at runtime. |
||
|
|
f3d108a5a1 |
feat: Argent Lens (#271)
Adds a UI for proposing and choosing between multiple agent-generated variants on a running app. **MCP tools** (`packages/tool-server/src/tools/variants/`) - `propose_variant` — non-blocking; agent stages a variant for an on-screen element (text/label/identifier/role matcher). - `await_user_selection` — one blocking call; parks until the human submits, then returns the picks + free-form annotations + a global comment. **Preview UI** (`packages/ui`) - Floating cards anchored to live element frames via the existing `describe` tree, with connector lines. - Comment-mode inspector: hover spotlight (blur+dim everything else), right-click any element to pin a comment. - Sticky padded hitbox + trajectory-aware dismissal so focus doesn't drop on cursor jitter. - Footbar [Complete] [Clear], confirm modal for destructive actions, green toast on submit. - Single source of feel constants in `theme.css`. **Electron host** (`packages/preview-window`) - New workspace package. Frameless transparent BrowserWindow at fixed size; the squeeze open/close is a CSS `transform: scaleY` on `<html>`, GPU-composited — no OS-level resize. - Tool-server spawns it on `await_user_selection` park, reuses it across re-awaits, and asks it to animate-close on submit (1s grace so the toast is visible). - Stdio-line IPC (no extra ports). Ephemeral throughout — the renderer hits the tool-server's already-ephemeral `/preview/*` HTTP. --------- Co-authored-by: Filip131311 <contact@filipkaminski.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
acfe80ab6b |
chore: add ESLint with typescript-eslint (#349)
## What Adds type-aware linting to the workspace. Until now the only static gate on JS/TS was Prettier (formatting) and `tsc` — there was no linter, so the whole class of bugs `tsc` doesn't catch (floating promises, misused promises, throwing non-Errors, unsafe stringification, dead code) went unguarded across ~450 TS files. - **`eslint.config.mjs`** — flat config. `recommendedTypeChecked` runs on the TS sources via an explicit `project` list (each package keeps tests in a separate `tsconfig.test.json` that `projectService` can't auto-discover, so it's listed explicitly). Type-checked rules are disabled for the loosely-typed plain-JS build/dev scripts. The `argent-private` submodule and build artifacts are ignored (mirrors `.prettierignore`). - **`lint` / `lint:fix`** npm scripts. - **`.github/workflows/lint.yml`** — builds the workspace (type-aware rules need the referenced projects' declarations) then runs `eslint .`. Node 24 (ESLint 10 requires `^20.19 || ^22.13 || >=24`). ## Rule calibration This is an adoption PR, so it's deliberately calibrated rather than maximal: - **Kept on (bug-catchers):** `no-floating-promises`, `no-misused-promises` (with `checksVoidReturn: false` to drop the noisy callback variant), `only-throw-error`, `no-base-to-string`, `prefer-promise-reject-errors`, `no-unused-vars` (`_`-prefix escape hatch), plus the ESLint recommended set. - **Parked off as documented debt:** the high-volume style/`any`-family rules — `no-unsafe-*`, `require-await`, `no-unnecessary-type-assertion`, `restrict-template-expressions`, `unbound-method`, `no-explicit-any`, etc. These flag broad classes of pre-existing code and are out of scope here; each is commented as a ratchet target for follow-up passes. All findings from the kept-on rules are fixed in this PR (dead code/imports, error-cause chaining, **3 genuine floating promises**, non-Error throws, unsafe stringification). Stale unused `eslint-disable` directives left over in the tree were removed by `--fix`. ## Verification Locally, all green: `eslint .` (0 errors), `tsc --build`, `typecheck:tests`/`typecheck:scripts`, the full `vitest` suite (no behavior change from the fixes), and `prettier --check`. ## Not included (intentionally) Other static-analysis gates discussed (CodeQL, dependency-audit, ShellCheck, actionlint) are out of scope for this PR. |
||
|
|
fa86905fbd |
chore: bump versions to 0.11.0 (#322)
Bumps the package suite from `0.10.0` to **`0.11.0`**. ## Changed - All release packages `0.10.0 → 0.11.0`: `@swmansion/argent`, `@argent/cli`, `@argent/installer`, `@argent/mcp`, `@argent/tools-client`, `@argent/native-devtools-android`, `@argent/native-devtools-ios`, `@argent/registry`, `@argent/skills`, `@argent/tool-server`, `@argent/update-core`. - `@argent/configuration-core` `0.9.0 → 0.11.0` — was lagging a release behind (extracted in #295); aligned to the suite. (It's `private`/unpublished, so this is internal-only.) - Regenerated `package-lock.json` to reflect the new workspace versions. ## Not changed - `@argent/ui` stays at `0.0.1` (separate pre-release track). - The private workspace root has no version. - Internal deps use `file:../` specifiers, so no dependency ranges needed updating. No source/runtime changes — `argent --version` reads from `package.json` at runtime. |
||
|
|
92cc69a75c |
feat: extract configuration-core and migrate auto-screenshot to feature flag (#295)
## Summary - Introduces `@argent/configuration-core` as the shared source of truth for feature-flag storage, the registry, and `isFlagEnabled`, so MCP and CLI can import the same primitives without coupling runtime code to CLI-only modules. - Replaces `ARGENT_AUTO_SCREENSHOT` with the off-by-default `disable-auto-screenshot` flag (auto-screenshot stays on unless enabled via `argent enable disable-auto-screenshot`); renames `ARGENT_AUTO_SHUTDOWN` to `ARGENT_TOOL_SERVER_SHUTDOWN_ON_MCP_EXIT`. - Keeps `@argent/cli` as the command layer (`enable` / `disable` / `flags`) with backward-compat re-exports, colored flag state output (picocolors), and `--help` listings of available flags. --------- Co-authored-by: Filip131311 <contact@filipkaminski.com> Co-authored-by: Cursor <cursoragent@cursor.com> |