Commit Graph

21 Commits

Author SHA1 Message Date
github-actions[bot] a39f132b91 Update versions to 1.19.0-SNAPSHOT and 1.19.0-beta29-SNAPSHOT 2026-07-17 13:42:56 +00:00
github-actions[bot] 66ad5ee6d5 Release versions 1.18.0 and 1.18.0-beta28 2026-07-17 12:34:45 +00:00
github-actions[bot] 7d7c3349d7 Update versions to 1.18.0-SNAPSHOT and 1.18.0-beta28-SNAPSHOT 2026-06-26 14:49:40 +00:00
github-actions[bot] 207407aec9 Release versions 1.17.0 and 1.17.0-beta27 2026-06-26 13:13:06 +00:00
github-actions[bot] 01de41d641 Update versions to 1.17.0-SNAPSHOT and 1.17.0-beta27-SNAPSHOT 2026-06-06 06:46:38 +00:00
github-actions[bot] cd836845dd Release versions 1.16.0 and 1.16.0-beta26 2026-06-05 15:46:56 +00:00
github-actions[bot] 6185599e37 Update versions to 1.16.0-SNAPSHOT and 1.16.0-beta26-SNAPSHOT 2026-05-15 16:21:14 +00:00
github-actions[bot] d0e54aa006 Release versions 1.15.0 and 1.15.0-beta25 2026-05-15 15:55:12 +00:00
renovate[bot] aade6726e7
chore(deps): update dependency org.jsoup:jsoup to v1.22.2 (#5222)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [org.jsoup:jsoup](https://jsoup.org/)
([source](https://redirect.github.com/jhy/jsoup)) | `1.18.3` → `1.22.2`
|
![age](https://developer.mend.io/api/mc/badges/age/maven/org.jsoup:jsoup/1.22.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jsoup:jsoup/1.18.3/1.22.2?slim=true)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/2069) for more information.

---

### Release Notes

<details>
<summary>jhy/jsoup (org.jsoup:jsoup)</summary>

###
[`v1.22.2`](https://redirect.github.com/jhy/jsoup/blob/HEAD/CHANGES.md#1222-2026-Apr-20)

##### Improvements

- Expanded and clarified `NodeTraversor` support for in-place DOM
rewrites during `NodeVisitor.head()`. Current-node edits such as
`remove`, `replace`, and `unwrap` now recover more predictably, while
traversal stays within the original root subtree. This makes single-pass
tree cleanup and normalization visitors easier to write, for example
when unwrapping presentational elements or replacing text nodes as you
walk the DOM.
[#&#8203;2472](https://redirect.github.com/jhy/jsoup/issues/2472)
- Documentation: clarified that a configured `Cleaner` may be reused
across concurrent threads, and that shared `Safelist` instances should
not be mutated while in use.
[#&#8203;2473](https://redirect.github.com/jhy/jsoup/issues/2473)
- Updated the default HTML `TagSet` for current HTML elements: added
`dialog`, `search`, `picture`, and `slot`; made `ins`, `del`, `button`,
`audio`, `video`, and `canvas` inline by default (`Tag#isInline()`,
aligned to phrasing content in the spec); and added readable
`Element.text()` boundaries for controls and embedded objects via the
new `Tag.TextBoundary` option. This improves pretty-printing and keeps
normalized text from running adjacent words together.
[#&#8203;2493](https://redirect.github.com/jhy/jsoup/pull/2493)

##### Bug Fixes

- Android (R8/ProGuard): added a rule to ignore the optional `re2j`
dependency when not present.
[#&#8203;2459](https://redirect.github.com/jhy/jsoup/issues/2459)
- Fixed a `NodeTraversor` regression in 1.21.2 where removing or
replacing the current node during `head()` could revisit the replacement
node and loop indefinitely. The traversal docs now also clarify which
inserted nodes are visited in the current pass.
[#&#8203;2472](https://redirect.github.com/jhy/jsoup/issues/2472)
- Parsing during charset sniffing no longer fails if an advisory
`available()` call throws `IOException`, as seen on JDK 8
`HttpURLConnection`.
[#&#8203;2474](https://redirect.github.com/jhy/jsoup/issues/2474)
- `Cleaner` no longer makes relative URL attributes in the input
document absolute when cleaning or validating a `Document`. URL
normalization now applies only to the cleaned output, and
`Safelist.isSafeAttribute()` is side effect free.
[#&#8203;2475](https://redirect.github.com/jhy/jsoup/issues/2475)
- `Cleaner` no longer duplicates enforced attributes when the input
`Document` preserves attribute case. A case-variant source attribute is
now replaced by the enforced attribute in the cleaned output.
[#&#8203;2476](https://redirect.github.com/jhy/jsoup/issues/2476)
- If a per-request SOCKS proxy is configured, jsoup now avoids using the
JDK `HttpClient`, because the JDK would silently ignore that proxy and
attempt to connect directly. Those requests now fall back to the legacy
`HttpURLConnection` transport instead, which does support SOCKS.
[#&#8203;2468](https://redirect.github.com/jhy/jsoup/issues/2468)
- `Connection.Response.streamParser()` and `DataUtil.streamParser(Path,
...)` could fail on small inputs without a declared charset, if the
initial 5 KB charset sniff fully consumed the input and closed it before
the stream parse began.
[#&#8203;2483](https://redirect.github.com/jhy/jsoup/issues/2483)
- In XML mode, doctypes with an internal subset, such as `<!DOCTYPE root
[<!ENTITY name "value">]>`, now round-trip correctly. The subset is
preserved as raw text only; entities are not expanded and external DTDs
are not loaded.
[#&#8203;2486](https://redirect.github.com/jhy/jsoup/issues/2486)

##### Build Changes

- Migrated the integration test server from Jetty to Netty, which
actively maintains support for our minimum JDK target (8).
[#&#8203;2491](https://redirect.github.com/jhy/jsoup/pull/2491)

###
[`v1.22.1`](https://redirect.github.com/jhy/jsoup/blob/HEAD/CHANGES.md#1221-2026-Jan-01)

##### Improvements

- Added support for using the `re2j` regular expression engine for
regex-based CSS selectors (e.g. `[attr~=regex]`, `:matches(regex)`),
which ensures linear-time performance for regex evaluation. This allows
safer handling of arbitrary user-supplied query regexes. To enable, add
the `com.google.re2j` dependency to your classpath, e.g.:

```xml
  <dependency>
    <groupId>com.google.re2j</groupId>
    <artifactId>re2j</artifactId>
    <version>1.8</version>
  </dependency>
```

(If you already have that dependency in your classpath, but you want to
keep using the Java regex engine, you can disable re2j via
`System.setProperty("jsoup.useRe2j", "false")`.) You can confirm that
the re2j engine has been enabled correctly by calling
`org.jsoup.helper.Regex.usingRe2j()`.
[#&#8203;2407](https://redirect.github.com/jhy/jsoup/pull/2407)

- Added an instance method `Parser#unescape(String, boolean)` that
unescapes HTML entities using the parser's configuration (e.g. to
support error tracking), complementing the existing static utility
`Parser.unescapeEntities(String, boolean)`.
[#&#8203;2396](https://redirect.github.com/jhy/jsoup/pull/2396)
- Added a configurable maximum parser depth (to limit the number of open
elements on stack) to both HTML and XML parsers. The HTML parser now
defaults to a depth of 512 to match browser behavior, and protect
against unbounded stack growth, while the XML parser keeps unlimited
depth by default, but can opt into a limit via
`org.jsoup.parser.Parser#setMaxDepth`.
[#&#8203;2421](https://redirect.github.com/jhy/jsoup/issues/2421)
- Build: added CI coverage for JDK 25
[#&#8203;2403](https://redirect.github.com/jhy/jsoup/pull/2403)
- Build: added a CI fuzzer for contextual fragment parsing (in addition
to existing full body HTML and XML fuzzers). [oss-fuzz
#&#8203;14041](https://redirect.github.com/google/oss-fuzz/pull/14041)

##### Changes

- Set a removal schedule of jsoup 1.24.1 for previously deprecated APIs.

##### Bug Fixes

- Previously cached child `Elements` of an `Element` were not correctly
invalidated in `Node#replaceWith(Node)`, which could lead to incorrect
results when subsequently calling `Element#children()`.
[#&#8203;2391](https://redirect.github.com/jhy/jsoup/issues/2391)
- Attribute selector values are now compared literally without trimming.
Previously, jsoup trimmed whitespace from selector values and from
element attribute values, which could cause mismatches with browser
behavior (e.g. `[attr=" foo "]`). Now matches align with the CSS
specification and browser engines.
[#&#8203;2380](https://redirect.github.com/jhy/jsoup/issues/2380)
- When using the JDK HttpClient, any system default proxy
(`ProxySelector.getDefault()`) was ignored. Now, the system proxy is
used if a per-request proxy is not set.
[#&#8203;2388](https://redirect.github.com/jhy/jsoup/issues/2388),
[#&#8203;2390](https://redirect.github.com/jhy/jsoup/pull/2390)
- A `ValidationException` could be thrown in the adoption agency
algorithm with particularly broken input. Now logged as a parse error.
[#&#8203;2393](https://redirect.github.com/jhy/jsoup/issues/2393)
- Null characters in the HTML body were not consistently removed; and in
foreign content were not correctly replaced.
[#&#8203;2395](https://redirect.github.com/jhy/jsoup/issues/2395)
- An `IndexOutOfBoundsException` could be thrown when parsing a body
fragment with crafted input. Now logged as a parse error.
[#&#8203;2397](https://redirect.github.com/jhy/jsoup/issues/2397),
[#&#8203;2406](https://redirect.github.com/jhy/jsoup/issues/2406)
- When using StructuralEvaluators (e.g., a `parent child` selector)
across many retained threads, their memoized results could also be
retained, increasing memory use. These results are now cleared
immediately after use, reducing overall memory consumption.
[#&#8203;2411](https://redirect.github.com/jhy/jsoup/issues/2411)
- Cloning a `Parser` now preserves any custom `TagSet` applied to the
parser.
[#&#8203;2422](https://redirect.github.com/jhy/jsoup/issues/2422),
[#&#8203;2423](https://redirect.github.com/jhy/jsoup/pull/2423)
- Custom tags marked as `Tag.Void` now parse and serialize like the
built-in void elements: they no longer consume following content, and
the XML serializer emits the expected self-closing form.
[#&#8203;2425](https://redirect.github.com/jhy/jsoup/issues/2425)
- The `<br>` element is once again classified as an inline tag
(`Tag.isBlock() == false`), matching common developer expectations and
its role as phrasing content in HTML, while pretty-printing and text
extraction continue to treat it as a line break in the rendered output.
[#&#8203;2387](https://redirect.github.com/jhy/jsoup/issues/2387),
[#&#8203;2439](https://redirect.github.com/jhy/jsoup/issues/2439)
- Fixed an intermittent truncation issue when fetching and parsing
remote documents via `Jsoup.connect(url).get()`. On responses without a
charset header, the initial charset sniff could sometimes (depending on
buffering / `available()` behavior) be mistaken for end-of-stream and a
partial parse reused, dropping trailing content.
[#&#8203;2448](https://redirect.github.com/jhy/jsoup/issues/2448)
- `TagSet` copies no longer mutate their template during lazy lookups,
preventing cross-thread `ConcurrentModificationException` when parsing
with shared sessions.
[#&#8203;2453](https://redirect.github.com/jhy/jsoup/pull/2453)
- Fixed parsing of `<svg>` `foreignObject` content nested within a
`<p>`, which could incorrectly move the HTML subtree outside the SVG.
[#&#8203;2452](https://redirect.github.com/jhy/jsoup/issues/2452)

##### Internal Changes

- Deprecated internal helper `org.jsoup.internal.Functions` (for removal
in v1.23.1). This was previously used to support older Android API
levels without full `java.util.function` coverage; jsoup now requires
core library desugaring so this indirection is no longer necessary.
[#&#8203;2412](https://redirect.github.com/jhy/jsoup/pull/2412)

###
[`v1.21.2`](https://redirect.github.com/jhy/jsoup/blob/HEAD/CHANGES.md#1212-2025-Aug-25)

##### Changes

- Deprecated internal (yet visible) methods
`Normalizer#normalize(String, bool)` and
`Attribute#shouldCollapseAttribute(Document.OutputSettings)`. These will
be removed in a future version.
- Deprecated `Connection#sslSocketFactory(SSLSocketFactory)` in favor of
the new `Connection#sslContext(SSLContext)`. Using `sslSocketFactory`
will force the use of the legacy `HttpUrlConnection` implementation,
which does not support HTTP/2.
[#&#8203;2370](https://redirect.github.com/jhy/jsoup/pull/2370)

##### Improvements

- When pretty-printing, if there are consecutive text nodes (via DOM
manipulation), the non-significant whitespace between them will be
collapsed.
[#&#8203;2349](https://redirect.github.com/jhy/jsoup/pull/2349).
- Updated `Connection.Response#statusMessage()` to return a simple
loggable string message (e.g. "OK") when using the `HttpClient`
implementation, which doesn't otherwise return any server-set status
message.
[#&#8203;2356](https://redirect.github.com/jhy/jsoup/issues/2346)
- `Attributes#size()` and `Attributes#isEmpty()` now exclude any
internal attributes (such as user data) from their count. This aligns
with the attributes' serialized output and iterator.
[#&#8203;2369](https://redirect.github.com/jhy/jsoup/pull/2369)
- Added `Connection#sslContext(SSLContext)` to provide a custom SSL
(TLS) context to requests, supporting both the `HttpClient` and the
legacy `HttUrlConnection` implementations.
[#&#8203;2370](https://redirect.github.com/jhy/jsoup/pull/2370)
- Performance optimizations for DOM manipulation methods including when
repeatedly removing an element's first child
(`element.child(0).remove()`, and when using
`Parser#parseBodyFragement()` to parse a large number of direct
children.
[#&#8203;2373](https://redirect.github.com/jhy/jsoup/pull/2373).

##### Bug Fixes

- When parsing from an InputStream and a multibyte character happened to
straddle a buffer boundary, the stream would not be completely read.
[#&#8203;2353](https://redirect.github.com/jhy/jsoup/issues/2353).
- In `NodeTraversor`, if a last child element was removed during the
`head()` call, the parent would be visited twice.
[#&#8203;2355](https://redirect.github.com/jhy/jsoup/issues/2355).
- Cloning an Element that has an Attributes object would add an empty
internal user-data attribute to that clone, which would cause unexpected
results for `Attributes#size()` and `Attributes#isEmpty()`.
[#&#8203;2356](https://redirect.github.com/jhy/jsoup/issues/2356)
- In a multithreaded application where multiple threads are calling
`Element#children()` on the same element concurrently, a race condition
could happen when the method was generating the internal child element
cache (a filtered view of its child nodes). Since concurrent reads of
DOM objects should be threadsafe without external synchronization, this
method has been updated to execute atomically.
[#&#8203;2366](https://redirect.github.com/jhy/jsoup/issues/2366)
- When parsing HTML with svg:script elements in SVG elements, don't
enter the Text insertion mode, but continue to parse as foreign content.
Otherwise, misnested HTML could then cause an IndexOutOfBoundsException.
[#&#8203;2374](https://redirect.github.com/jhy/jsoup/issues/2374)
- Malformed HTML could throw an IndexOutOfBoundsException during the
adoption agency.
[#&#8203;2377](https://redirect.github.com/jhy/jsoup/pull/2377).

###
[`v1.21.1`](https://redirect.github.com/jhy/jsoup/blob/HEAD/CHANGES.md#1211-2025-Jun-23)

##### Changes

- Removed previously deprecated methods.
[#&#8203;2317](https://redirect.github.com/jhy/jsoup/pull/2317)
- Deprecated the `:matchText` pseduo-selector due to its side effects on
the DOM; use the new `::textnode` selector and the
`Element#selectNodes(String css, Class type)` method instead.
[#&#8203;2343](https://redirect.github.com/jhy/jsoup/pull/2343)
- Deprecated `Connection.Response#bufferUp()` in lieu of
`Connection.Response#readFully()` which can throw a checked IOException.
- Deprecated internal methods `Validate#ensureNotNull` (replaced by
typed `Validate#expectNotNull`); protected HTML appenders from Attribute
and Node.
- If you happen to be using any of the deprecated methods, please take
the opportunity now to migrate away from them, as they will be removed
in a future release.

##### Improvements

- Enhanced the `Selector` to support direct matching against nodes such
as comments and text nodes. For example, you can now find an element
that follows a specific comment: `::comment:contains(prices) + p` will
select `p` elements immediately after a `<!-- prices: -->` comment.
Supported types include `::node`, `::leafnode`, `::comment`, `::text`,
`::data`, and `::cdata`. Node contextual selectors like
`::node:contains(text)`, `:matches(regex)`, and `:blank` are also
supported. Introduced `Element#selectNodes(String css)` and
`Element#selectNodes(String css, Class nodeType)` for direct node
selection.
[#&#8203;2324](https://redirect.github.com/jhy/jsoup/pull/2324)
- Added `TagSet#onNewTag(Consumer<Tag> customizer)`: register a callback
that’s invoked for each new or cloned Tag when it’s inserted into the
set. Enables dynamic tweaks of tag options (for example, marking all
custom tags as self-closing, or everything in a given namespace as
preserving whitespace).
- Made `TokenQueue` and `CharacterReader` autocloseable, to ensure that
they will release their buffers back to the buffer pool, for later
reuse.
- Added `Selector#evaluatorOf(String css)`, as a clearer way to obtain
an Evaluator from a CSS query. An alias of `QueryParser.parse(String
css)`.
- Custom tags (defined via the `TagSet`) in a foreign namespace (e.g.
SVG) can be configured to parse as data tags.
- Added `NodeVisitor#traverse(Node)` to simplify node traversal calls
(vs. importing `NodeTraversor`).
- Updated the default user-agent string to improve compatibility.
[#&#8203;2341](https://redirect.github.com/jhy/jsoup/issues/2341)
- The HTML parser now allows the specific text-data type (Data, RcData)
to be customized for known tags. (Previously, that was only supported on
custom tags.)
[#&#8203;2326](https://redirect.github.com/jhy/jsoup/issues/2326).
- Added `Connection#readFully()` as a replacement for
`Connection#bufferUp()` with an explicit IOException. Similarly, added
`Connection#readBody()` over `Connection#body()`. Deprecated
`Connection#bufferUp()`.
[#&#8203;2327](https://redirect.github.com/jhy/jsoup/pull/2327)
- When serializing HTML, the `<` and `>` characters are now escaped in
attributes. This helps prevent a class of mutation XSS attacks.
[#&#8203;2337](https://redirect.github.com/jhy/jsoup/pull/2337)
- Changed `Connection` to prefer using the JDK's HttpClient over
HttpUrlConnection, if available, to enable HTTP/2 support by default.
Users can disable via `-Djsoup.useHttpClient=false`.
[#&#8203;2340](https://redirect.github.com/jhy/jsoup/pull/2340)

##### Bug Fixes

- The contents of a `script` in a `svg` foreign context should be parsed
as script data, not text.
[#&#8203;2320](https://redirect.github.com/jhy/jsoup/issues/2320)
- `Tag#isFormSubmittable()` was updating the Tag's options.
[#&#8203;2323](https://redirect.github.com/jhy/jsoup/issues/2323)
- The HTML pretty-printer would incorrectly trim whitespace when text
followed an inline element in a block element.
[#&#8203;2325](https://redirect.github.com/jhy/jsoup/issues/2325)
- Custom tags with hyphens or other non-letter characters in their names
now work correctly as Data or RcData tags. Their closing tags are now
tokenized properly.
[#&#8203;2332](https://redirect.github.com/jhy/jsoup/issues/2332)
- When cloning an Element, the clone would retain the source's cached
child Element list (if any), which could lead to incorrect results when
modifying the clone's child elements.
[#&#8203;2334](https://redirect.github.com/jhy/jsoup/issues/2334)

###
[`v1.20.1`](https://redirect.github.com/jhy/jsoup/blob/HEAD/CHANGES.md#1201-2025-Apr-29)

##### Changes

- To better follow the HTML5 spec and current browsers, the HTML parser
no longer allows self-closing tags (`<foo />`)
to close HTML elements by default. Foreign content (SVG, MathML), and
content parsed with the XML parser, still
supports self-closing tags. If you need specific HTML tags to support
self-closing, you can register a custom tag via
the `TagSet` configured in `Parser.tagSet()`, using
`Tag#set(Tag.SelfClose)`. Standard void tags (such as `<img>`,
  `<br>`, etc.) continue to behave as usual and are not affected by this
change.
[#&#8203;2300](https://redirect.github.com/jhy/jsoup/issues/2300).
- The following internal components have been **deprecated**. If you do
happen to be using any of these, please take the opportunity now to
migrate away from them, as they will be removed in jsoup 1.21.1.
- `ChangeNotifyingArrayList`, `Document.updateMetaCharsetElement()`,
`Document.updateMetaCharsetElement(boolean)`,
`HtmlTreeBuilder.isContentForTagData(String)`,
`Parser.isContentForTagData(String)`,
`Parser.setTreeBuilder(TreeBuilder)`, `Tag.formatAsBlock()`,
`Tag.isFormListed()`, `TokenQueue.addFirst(String)`,
`TokenQueue.chompTo(String)`, `TokenQueue.chompToIgnoreCase(String)`,
`TokenQueue.consumeToIgnoreCase(String)`, `TokenQueue.consumeWord()`,
`TokenQueue.matchesAny(String...)`

##### Functional Improvements

- Rebuilt the HTML pretty-printer, to simplify and consolidate the
implementation, improve consistency, support custom
Tags, and provide a cleaner path for ongoing improvements. The specific
HTML produced by the pretty-printer may be
different from previous versions.
[#&#8203;2286](https://redirect.github.com/jhy/jsoup/issues/2286).
- Added the ability to define custom tags, and to modify properties of
known tags, via the `TagSet` tag collection.
  Their properties can impact both the parse and how content is
serialized (output as HTML or XML).
[#&#8203;2285](https://redirect.github.com/jhy/jsoup/issues/2285).
- `Element.cssSelector()` will prefer to return shorter selectors by
using ancestor IDs when available and unique. E.g.
`#id > div > p` instead of `html > body > div > div > p`
[#&#8203;2283](https://redirect.github.com/jhy/jsoup/pull/2283).
- Added `Elements.deselect(int index)`, `Elements.deselect(Object o)`,
and `Elements.deselectAll()` methods to remove
elements from the `Elements` list without removing them from the
underlying DOM. Also added `Elements.asList()` method
to get a modifiable list of elements without affecting the DOM.
(Individual Elements remain linked to the
DOM.) [#&#8203;2100](https://redirect.github.com/jhy/jsoup/issues/2100).
- Added support for sending a request body from an InputStream with
`Connection.requestBodyStream(InputStream stream)`.
[#&#8203;1122](https://redirect.github.com/jhy/jsoup/issues/1122).
- The XML parser now supports scoped xmlns: prefix namespace
declarations, and applies the correct namespace to Tags and
Attributes. Also, added `Tag#prefix()`, `Tag#localName()`,
`Attribute#prefix()`, `Attribute#localName()`, and
`Attribute#namespace()` to retrieve these.
[#&#8203;2299](https://redirect.github.com/jhy/jsoup/issues/2299).
- CSS identifiers are now escaped and unescaped correctly to the CSS
spec. `Element#cssSelector()` will emit
appropriately escaped selectors, and the QueryParser supports those.
Added `Selector.escapeCssIdentifier()` and
`Selector.unescapeCssIdentifier()`.
[#&#8203;2297](https://redirect.github.com/jhy/jsoup/pull/2297),
[#&#8203;2305](https://redirect.github.com/jhy/jsoup/pull/2305)

##### Structure and Performance Improvements

- Refactored the CSS `QueryParser` into a clearer recursive descent
parser. [#&#8203;2310](https://redirect.github.com/jhy/jsoup/pull/2310).
- CSS selectors with consecutive combinators (e.g. `div >> p`) will
throw an explicit parse
exception.
[#&#8203;2311](https://redirect.github.com/jhy/jsoup/pull/2311).
- Performance: reduced the shallow size of an Element from 40 to 32
bytes, and the NodeList from 32 to 24.
  [#&#8203;2307](https://redirect.github.com/jhy/jsoup/pull/2307).
- Performance: reduced GC load of new StringBuilders when tokenizing
input
  HTML. [#&#8203;2304](https://redirect.github.com/jhy/jsoup/pull/2304).
- Made `Parser` instances threadsafe, so that inadvertent use of the
same instance across threads will not lead to
  errors. For actual concurrency, use `Parser#newInstance()` per
thread. [#&#8203;2314](https://redirect.github.com/jhy/jsoup/pull/2314).

##### Bug Fixes

- Element names containing characters invalid in XML are now normalized
to valid XML names when
serializing.
[#&#8203;1496](https://redirect.github.com/jhy/jsoup/issues/1496).
- When serializing to XML, characters that are invalid in XML 1.0 should
be removed (not
encoded).
[#&#8203;1743](https://redirect.github.com/jhy/jsoup/issues/1743).
- When converting a `Document` to the W3C DOM in `W3CDom`, elements with
an attribute in an undeclared namespace now
get a declaration of `xmlns:prefix="undefined"`. This allows subsequent
serialization to XML via `W3CDom.asString()`
to succeed.
[#&#8203;2087](https://redirect.github.com/jhy/jsoup/issues/2087).
- The `StreamParser` could emit the final elements of a document twice,
due to how `onNodeCompleted` was fired when closing out the stack.
[#&#8203;2295](https://redirect.github.com/jhy/jsoup/issues/2295).
- When parsing with the XML parser and error tracking enabled, the
trailing `?` in `<?xml version="1.0"?>` would
incorrectly emit an error.
[#&#8203;2298](https://redirect.github.com/jhy/jsoup/issues/2298).
- Calling `Element#cssSelector()` on an element with combining
characters in the class or ID now produces the correct output.
[#&#8203;1984](https://redirect.github.com/jhy/jsoup/issues/1984).

###
[`v1.19.1`](https://redirect.github.com/jhy/jsoup/blob/HEAD/CHANGES.md#1191-2025-Mar-04)

##### Changes

- Added support for **http/2** requests in `Jsoup.connect()`, when
running on Java 11+, via the Java HttpClient
implementation.
[#&#8203;2257](https://redirect.github.com/jhy/jsoup/pull/2257).
- In this version of jsoup, the default is to make requests via the
HttpUrlConnection implementation: use
**`System.setProperty("jsoup.useHttpClient", "true");`** to enable
making requests via the HttpClient instead ,
which will enable http/2 support, if available. This will become the
default in a later version of jsoup, so now is
    a good time to validate it.
- If you are repackaging the jsoup jar in your deployment (i.e. creating
a shaded- or a fat-jar), make sure to specify
    that as a Multi-Release
    JAR.
- If the `HttpClient` impl is not available in your JRE, requests will
continue to be made via
    `HttpURLConnection` (in `http/1.1` mode).
- Updated the minimum Android API Level validation from 10 to **21**. As
with previous jsoup versions, Android
developers need to enable core library desugaring. The minimum Java
version remains Java 8.
  [#&#8203;2173](https://redirect.github.com/jhy/jsoup/pull/2173)
- Removed previously deprecated class: `org.jsoup.UncheckedIOException`
(replace with `java.io.UncheckedIOException`);
moved previously deprecated method `Element Element#forEach(Consumer)`
to
`void Element#forEach(Consumer())`.
[#&#8203;2246](https://redirect.github.com/jhy/jsoup/pull/2246)
- Deprecated the methods `Document#updateMetaCharsetElement(boolean)`
and `Document#updateMetaCharsetElement()`, as the
setting had no effect. When `Document#charset(Charset)` is called, the
document's meta charset or XML encoding
instruction is always set.
[#&#8203;2247](https://redirect.github.com/jhy/jsoup/pull/2247)

##### Improvements

- When cleaning HTML with a `Safelist` that preserves relative links,
the `isValid()` method will now consider these
links valid. Additionally, the enforced attribute `rel=nofollow` will
only be added to external links when configured
in the safelist.
[#&#8203;2245](https://redirect.github.com/jhy/jsoup/pull/2245)
- Added `Element#selectStream(String query)` and
`Element#selectStream(Evaluator)` methods, that return a `Stream` of
matching elements. Elements are evaluated and returned as they are
found, and the stream can be
terminated early.
[#&#8203;2092](https://redirect.github.com/jhy/jsoup/pull/2092)
- `Element` objects now implement `Iterable`, enabling them to be used
in enhanced for loops.
- Added support for fragment parsing from a `Reader` via
`Parser#parseFragmentInput(Reader, Element, String)`.
[#&#8203;1177](https://redirect.github.com/jhy/jsoup/issues/1177)
- Reintroduced CLI executable examples, in `jsoup-examples.jar`.
[#&#8203;1702](https://redirect.github.com/jhy/jsoup/issues/1702)
- Optimized performance of selectors like `#id .class` (and other
similar descendant queries) by around 4.6x, by better
  balancing the Ancestor evaluator's cost function in the query
planner.
[#&#8203;2254](https://redirect.github.com/jhy/jsoup/issues/2254)
- Removed the legacy parsing rules for `<isindex>` tags, which would
autovivify a `form` element with labels. This is no
  longer in the spec.
- Added `Elements.selectFirst(String cssQuery)` and
`Elements.expectFirst(String cssQuery)`, to select the first
matching element from an `Elements` list.
[#&#8203;2263](https://redirect.github.com/jhy/jsoup/pull/2263/)
- When parsing with the XML parser, XML Declarations and Processing
Instructions are directly handled, vs bouncing
through the HTML parser's bogus comment handler. Serialization for
non-doctype declarations no longer end with a
spurious `!`.
[#&#8203;2275](https://redirect.github.com/jhy/jsoup/pull/2275)
- When converting parsed HTML to XML or the W3C DOM, element names
containing `<` are normalized to `_` to ensure valid
XML. For example, `<foo<bar>` becomes `<foo_bar>`, as XML does not allow
`<` in element names, but HTML5
  does. [#&#8203;2276](https://redirect.github.com/jhy/jsoup/pull/2276)
- Reimplemented the HTML5 Adoption Agency Algorithm to the current spec.
This handles mis-nested formating / structural elements.
[#&#8203;2278](https://redirect.github.com/jhy/jsoup/pull/2278)

##### Bug Fixes

- If an element has an `;` in an attribute name, it could not be
converted to a W3C DOM element, and so subsequent XPath
queries could miss that element. Now, the attribute name is more
completely
normalized.
[#&#8203;2244](https://redirect.github.com/jhy/jsoup/issues/2244)
- For backwards compatibility, reverted the internal attribute key for
doctype names to
"name".
[#&#8203;2241](https://redirect.github.com/jhy/jsoup/issues/2241)
- In `Connection`, skip cookies that have no name, rather than throwing
a validation
exception.
[#&#8203;2242](https://redirect.github.com/jhy/jsoup/issues/2242)
- When running on JDK 1.8, the error `java.lang.NoSuchMethodError:
java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;`
could be thrown when calling `Response#body()` after parsing from a URL
and the buffer size was
exceeded.
[#&#8203;2250](https://redirect.github.com/jhy/jsoup/pull/2250)
- For backwards compatibility, allow `null` InputStream inputs to
`Jsoup.parse(InputStream stream, ...)`, by returning
an empty `Document`.
[#&#8203;2252](https://redirect.github.com/jhy/jsoup/issues/2252)
- A `template` tag containing an `li` within an open `li` would be
parsed incorrectly, as it was not recognized as a
"special" tag (which have additional processing rules). Also, added the
SVG and MathML namespace tags to the list of
special tags.
[#&#8203;2258](https://redirect.github.com/jhy/jsoup/issues/2258)
- A `template` tag containing a `button` within an open `button` would
be parsed incorrectly, as the "in button scope"
check was not aware of the `template` element. Corrected other instances
including MathML and SVG elements,
also. [#&#8203;2271](https://redirect.github.com/jhy/jsoup/issues/2271)
- An `:nth-child` selector with a negative digit-less step, such as
`:nth-child(-n+2)`, would be parsed incorrectly as a
positive step, and so would not match as expected.
[#&#8203;1147](https://redirect.github.com/jhy/jsoup/issues/1147)
- Calling `doc.charset(charset)` on an empty XML document would throw an
`IndexOutOfBoundsException`.
[#&#8203;2266](https://redirect.github.com/jhy/jsoup/issues/2266)
- Fixed a memory leak when reusing a nested `StructuralEvaluator` (e.g.,
a selector ancestor chain like `A B C`) by
ensuring cache reset calls cascade to inner members.
[#&#8203;2277](https://redirect.github.com/jhy/jsoup/issues/2277)
- Concurrent calls to `doc.clone().append(html)` were not supported.
When a document was cloned, its `Parser` was not cloned but was a
shallow copy of the original parser.
[#&#8203;2281](https://redirect.github.com/jhy/jsoup/issues/2281)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/langchain4j/langchain4j).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzMuNiIsInVwZGF0ZWRJblZlciI6IjQzLjE3My42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-14 10:09:13 +02:00
renovate[bot] f379321148
chore(deps): update dependency org.apache.pdfbox:pdfbox to v3.0.7 (#5173)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [org.apache.pdfbox:pdfbox](https://www.apache.org/)
([source](https://svn.apache.org/viewvc/pdfbox/tags/3.0.7/pdfbox)) |
`3.0.5` → `3.0.7` |
![age](https://developer.mend.io/api/mc/badges/age/maven/org.apache.pdfbox:pdfbox/3.0.7?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.apache.pdfbox:pdfbox/3.0.5/3.0.7?slim=true)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/2069) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/langchain4j/langchain4j).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzMuNiIsInVwZGF0ZWRJblZlciI6IjQzLjE3My42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-12 17:10:07 +02:00
github-actions[bot] 628ac34c01 Update versions to 1.15.0-SNAPSHOT and 1.15.0-beta25-SNAPSHOT 2026-04-30 18:43:12 +00:00
github-actions[bot] 4917afa297 Release versions 1.14.0 and 1.14.0-beta24 2026-04-30 18:10:30 +00:00
github-actions[bot] 4798a89d66 Update versions to 1.14.0-SNAPSHOT and 1.14.0-beta24-SNAPSHOT 2026-04-09 14:41:27 +00:00
github-actions[bot] 759cd9a236 Release versions 1.13.0 and 1.13.0-beta23 2026-04-09 13:07:30 +00:00
Dmytro Liubarskyi e10abf04d0
Update versions to 1.13.0-SNAPSHOT and 1.13.0-beta23-SNAPSHOT (#4710)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-13 11:39:50 +01:00
Dmytro Liubarskyi c92ea033e4
Update versions to 1.13.0-SNAPSHOT and 1.13.0-beta22-SNAPSHOT (#4666)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-05 17:18:22 +01:00
Dmytro Liubarskyi 336b2accce
Update versions to 1.12.0-SNAPSHOT and 1.12.0-beta20-SNAPSHOT (#4537)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-04 14:27:44 +01:00
Dmytro Liubarskyi 778be1b360
Update versions to 1.11.0-SNAPSHOT and 1.11.0-beta19-SNAPSHOT (#4285)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-24 15:38:05 +01:00
Dmytro Liubarskyi ca6097e35d
Update versions to 1.10.0-SNAPSHOT and 1.10.0-beta18-SNAPSHOT (#4152)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-11-28 12:21:30 +01:00
Dmytro Liubarskyi bc0801a4df
Update versions to 1.10.0-SNAPSHOT and 1.10.0-beta17-SNAPSHOT (#4140)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-11-26 17:38:36 +01:00
Mario Fusco 0f8e3861dc
Introduce a generic agentic planner and reimplement all agentic patterns through it (#3929)
## Custom agentic patterns

The `langchain4j-agentic` module provides out-of-the-box a few useful
agentic pattern, but what if none of them fit the specific needs of your
application? In this case it is possible to create your own custom
pattern, that orchestrates the interactions among a set of subagents in
a way that is tailored to your requirements.

In more details an agentic pattern is simply the specification of an
execution plan for the subagents that it coordinates. This plan can be
defined by implementing the following `Planner` interface:

```java
public interface Planner {

    default void init(AgenticScope agenticScope, AgentInstance plannerAgent, List<AgentInstance> subagents) { }

    default Action firstAction(AgenticScope agenticScope) {
        return nextAction(agenticScope, null);
    }

    Action nextAction(AgenticScope agenticScope, AgentExecution previousAgentExecution);
}
```

This interface has three methods: `init`, `firstAction`, and
`nextAction`. The `init` method is called once at the beginning of the
execution, and can be used to initialize any state or data structures
needed by the planner. The `firstAction` method is called to determine
the first action to be taken by the agentic pattern, while the
`nextAction` method is called after each agent execution to determine
the next action to be taken based on the current state of the
`AgenticScope` and the result of the previous agent execution.

The `Action` class returned by the `firstAction` and `nextAction`
methods represents the next step to be taken by the agentic pattern, and
can be one either a list of one or more subagents to be called next, or
a signal that the execution has been completed. If the action specifies
only one subagent invocation, then it will be executed sequentially, and
in the same thread that is executing the planner itself, while if there
are more than one, they will be executed in parallel using the provided
`Executor` or the LangChain4j default one.

All the built-in agentic patterns are also written in terms of this
`Planner` abstraction and giving a look at their implementation can
clarify how this works and be a good starting point to create your own
custom patterns. For instance the parallel workflow is probably the
simplest of those implementation, and it is defined as follows:

```java
public class ParallelPlanner implements Planner {

    private List<AgentInstance> agents;

    @Override
    public void init(AgenticScope agenticScope, AgentInstance plannerAgent, List<AgentInstance> subagents) {
        this.agents = subagents;
    }

    @Override
    public Action firstAction(AgenticScope agenticScope) {
        return call(agents);
    }

    @Override
    public Action nextAction(final AgenticScope agenticScope, final AgentExecution previousAgentExecution) {
        return done();
    }
}
```

Here the `init` method simply stores the list of subagents with which
the parallel workflow has been configured, while the `firstAction`
method returns an action that calls all those agents in parallel. Once
this parallel execution is completed, there isn't any other action to be
taken, so the `nextAction` method simply returns `done()` used to signal
the termination of the execution.

The `Planner` implementing the sequential workflow is only slightly more
complex, as it needs to keep track of the next subagent to be invoked
using an internal cursor, and then return the appropriate action in the
`nextAction` method or signal the termination of the execution when all
subagents have been invoked.

```java
public class SequentialPlanner implements Planner {

    private List<AgentInstance> agents;
    private int agentCursor = 0;

    @Override
    public void init(AgenticScope agenticScope, AgentInstance plannerAgent, List<AgentInstance> subagents) {
        this.agents = subagents;
    }

    @Override
    public Action nextAction(AgenticScope agenticScope, AgentExecution previousAgentExecution) {
        return agentCursor >= agents.size() ? done() : call(agents.get(agentCursor++));
    }
}
```

To understand how to define an agentic system from a planner
implementation, it is possible, for example, to create an instance of
the formerly discussed sequential workflow generating a novel for a
topic and then editing it for a specific style and audience, as it
follows:

```java
UntypedAgent novelCreator = AgenticServices.plannerBuilder()
                .subAgents(creativeWriter, audienceEditor, styleEditor)
                .outputKey("story")
                .planner(SequentialPlanner::new)
                .build();
```

which is totally equivalent to use the dedicated API for sequential
workflows:

```java
UntypedAgent novelCreator = AgenticServices.sequenceBuilder()
                .subAgents(creativeWriter, audienceEditor, styleEditor)
                .outputKey("story")
                .build();
```

The `plannerBuilder()` method is similar to all other agent builders,
with the only difference that it requires to provide a
`Supplier<Planner>` returning a new instance of the specific planner to
be used by this agentic system. Of course an agentic system implementing
a custom planner can be seamlessly combined with any other of the
agentic pattern offered out-of-the-box by the `langchain4j-agentic`
module.

Having clarified how this `Planner` abstraction works, it is now
possible to create your own custom agentic patterns by implementing it.
The following sections provide two examples of custom patterns that can
be useful in different scenarios.

### Goal oriented agentic pattern

The workflow patterns and the supervisor agent represents the two
extremes of the spectrum of possible agentic systems: the former is
completely deterministic and rigid, forcing to decide in advance the
sequence of agents to be invoked, while the latter is completely
flexible and adaptive, but delegates the decision of the sequence of
agents to be invoked to a non-deterministic LLM. However, there are
cases where a middle ground between these two extremes can be more
appropriate, allowing agents to work towards a specific goal in a
relatively flexible way, but also determining how these agents should be
invoked in an algorithmic way.

In order to put this approach in practice, not only the whole agentic
system needs to define a goal, but also each subagent needs to declare
its own pre and postconditions. This is necessary to calculate the
sequence of agent invocations that lead to the achievement of the goal
in the fastest possible way. However, all this information are
implicitly already present in the agentic system, as those pre and
postconditions are nothing else than the required inputs and produced
outputs of each agent, and the final goal is simply the desired outputs
of the whole agentic system.

Following this idea, it is possible to calculate a dependency graph of
all the subagents participating in the agentic system, and then to
implement a `Planner` that is capable of analyzing the initial state of
the `AgenticScope`, comparing it with the desired goal, and then using
that graph to determine the sequence of agent invocations that can lead
to the achievement of that goal.

```java
public class GoalOrientedPlanner implements Planner {

    private String goal;

    private GoalOrientedSearchGraph graph;
    private List<AgentInstance> path;

    private int agentCursor = 0;

    @Override
    public void init(AgenticScope agenticScope, AgentInstance plannerAgent, List<AgentInstance> subagents) {
        this.goal = plannerAgent.outputKey();
        this.graph = new GoalOrientedSearchGraph(subagents);
    }

    @Override
    public Action firstAction(AgenticScope agenticScope) {
        path = graph.search(agenticScope.state().keySet(), goal);
        if (path.isEmpty()) {
            throw new IllegalStateException("No path found for goal: " + goal);
        }
        return call(path.get(agentCursor++));
    }

    @Override
    public Action nextAction(AgenticScope agenticScope, AgentExecution previousAgentExecution) {
        return agentCursor >= path.size() ? done() : call(path.get(agentCursor++));
    }
}
```

As anticipated, here the goal coincides with the final output of the
planner-based agentic pattern itself, while the path from the initial
state to the goal is calculated using a `GoalOrientedSearchGraph`, that
is built analyzing the input and output keys of all subagents. The
sequence of agents to be invoked is then calculated as the shortest path
on that graph from the current state to the desired goal.

To give a practical example of how this works, let's try to build a
goal-oriented agentic system that can extract the name and zodiac sign
of a person from a prompt, generate the horoscope for that sign, look
for a related story on the internet and finally create a nice writeup
combining all this information. We can achieve this set of tasks by
using the following 5 agents:

```java
public interface HoroscopeGenerator {
    @SystemMessage(
            """
            You are an astrologist that generates horoscopes based on the user's name and zodiac sign.
            """)
    @UserMessage("""
            Generate the horoscope for {{person}} who is a {{sign}}.
            """)
    @Agent("An astrologist that generates horoscopes based on the user's name and zodiac sign.")
    String horoscope(@V("person") Person person, @V("sign") Sign sign);
}

public interface PersonExtractor {

    @UserMessage("""
            Extract a person from the following prompt: {{prompt}}
            """)
    @Agent("Extract a person from user's prompt")
    Person extractPerson(@V("prompt") String prompt);
}

public interface SignExtractor {

    @UserMessage("""
            Extract the zodiac sign of a person from the following prompt: {{prompt}}
            """)
    @Agent("Extract a person from user's prompt")
    Sign extractSign(@V("prompt") String prompt);
}

public interface Writer {
    @UserMessage("""
            Create an amusing writeup for {{person}} based on the following:
            - their horoscope: {{horoscope}}
            - a current news story: {{story}}
            """)
    @Agent("Create an amusing writeup for the target person based on their horoscope and current news stories")
    String write(@V("person") Person person, @V("horoscope") String horoscope, @V("story") String story);
}

public interface StoryFinder {

    @SystemMessage("""
            You're a story finder, use the provided web search tools, calling it once and only once,
            to find a fictional and funny story on the internet about the user provided topic.
            """)
    @UserMessage("""
            Find a story on the internet for {{person}} who has the following horoscope: {{horoscope}}.
            """)
    @Agent("Find a story on the internet for a given person with a given horoscope")
    String findStory(@V("person") Person person, @V("horoscope") String horoscope);
}
```

Leveraging the `GoalOrientedPlanner` developed before, these agents can
be combined in a goal-oriented agentic system as follows:

```java
HoroscopeGenerator horoscopeGenerator = AgenticServices.agentBuilder(HoroscopeGenerator.class)
        .chatModel(baseModel())
        .outputKey("horoscope")
        .build();

PersonExtractor personExtractor = AgenticServices.agentBuilder(PersonExtractor.class)
        .chatModel(baseModel())
        .outputKey("person")
        .build();

SignExtractor signExtractor = AgenticServices.agentBuilder(SignExtractor.class)
        .chatModel(baseModel())
        .outputKey("sign")
        .build();

Writer writer = AgenticServices.agentBuilder(Writer.class)
        .chatModel(baseModel())
        .outputKey("writeup")
        .build();

StoryFinder storyFinder = AgenticServices.agentBuilder(StoryFinder.class)
        .chatModel(baseModel())
        .tools(new WebSearchTool())
        .outputKey("story")
        .build();

UntypedAgent horoscopeAgent = AgenticServices.plannerBuilder()
        .subAgents(horoscopeGenerator, personExtractor, signExtractor, writer, storyFinder)
        .outputKey("writeup")
        .planner(GoalOrientedPlanner::new)
        .build();
```

As anticipated, the overall goal of this agentic system is to produce a
`writeup` which is also the output key of the GOAP-based planner itself.
Taking into account the inputs and outputs of all subagents, the
dependency graph built by the `GoalOrientedSearchGraph` will look like
this:

<img width="970" height="871" alt="goap"
src="https://github.com/user-attachments/assets/0249b79f-9839-427c-9548-58774c371c30"
/>

When invoking this agentic system with a prompt like "My name is Mario
and my zodiac sign is pisces"

```java
Map<String, Object> input = Map.of("prompt", "My name is Mario and my zodiac sign is pisces");
String writeup = horoscopeAgent.invoke(input);
```

the `GoalOrientedPlanner` will analyze the initial state of the
`AgenticScope`, that contains only the `prompt` variable, and then it
will calculate the shortest path on the dependency graph from that
initial state to the desired goal, which is the `writeup`, so that the
resulting sequence of agent invocations will be:

```
Agents path sequence: [extractPerson, extractSign, horoscope, findStory, write]
```
Note that, as anticipated, this goal-oriented agentic pattern can be
mixed and combined with any other of the existing agentic patterns. For
instance this possibility can be used to overcome an evident limitation
of this approach that, being optimized to reach a specific goal
following the shortest possible path, structurally doesn't allow loops,
so in some cases it could be useful to have a loop agentic pattern as a
subagent of this goal-oriented one.

### Peer-to-peer agentic pattern

All the agentic system discussed up to this point are based on a
centralized and hierarchical architecture. In fact all the workflow
patterns had a well-defined top-level agent coordinating the activities
of multiple sub-agents in a programmatically predetermined way. Even the
supervisor pattern, which is more flexible and dynamic thanks to the
presence of its LLM-based planner agent, still relies on a coordinator
agent that controls the interactions among the various sub-agents. This
typology of architectures are suitable for many applications and
scenarios, but they can also have some limitations, especially in terms
of scalability and fault tolerance. This is why we may want to offer an
alternative peer-to-peer approach for multi-agent systems, that can
overcome these limitations by adopting a more decentralized and
distributed strategy.

In a peer-to-peer agentic systems there isn't any top level agent, and
all agents are equal peers that are coordinated through the state of the
`AgenticScope`. In particular, an agent is triggered by the presence of
its own required inputs as state variables in the `AgenticScope`.
Subsequently, a change in one or more of those variables, produced by
the output of a different agent, can retrigger the invocation of that
agent again. The process terminates either when the `AgenticScope`
reaches a stable state and no agent can be invoked anymore, or when the
predefined exit condition is satisfied, or when a maximum number of
agent invocations has been reached. A `Planner` implementation that
realizes this peer-to-peer agentic pattern could be written as it
follows:

```java
public class P2PPlanner implements Planner {

    private final int maxAgentsInvocations;
    private final BiPredicate<AgenticScope, Integer> exitCondition;

    private int invocationCounter = 0;
    private Map<String, AgentActivator> agentActivators;

    public P2PPlanner(int maxAgentsInvocations, BiPredicate<AgenticScope, Integer> exitCondition) {
        this(null, maxAgentsInvocations, exitCondition);
    }

    @Override
    public void init(AgenticScope agenticScope, AgentInstance plannerAgent, List<AgentInstance> subagents) {
        this.agentActivators = subagents.stream().collect(toMap(AgentInstance::name, AgentActivator::new));
    }

    @Override
    public Action nextAction(AgenticScope agenticScope, AgentExecution previousAgentExecution) {
        if (terminated(agenticScope)) {
            return done();
        }

        AgentActivator lastExecutedAgent = agentActivators.get(previousAgentExecution.agentSpec().uniqueName());
        lastExecutedAgent.finishExecution();
        agentActivators.values().forEach(a -> a.onStateChanged(lastExecutedAgent.agent.outputKey()));

        AgentInstance[] agentsToCall = agentActivators.values().stream()
                .filter(agentActivator -> agentActivator.canActivate(agenticScope))
                .peek(AgentActivator::startExecution)
                .map(AgentActivator::agent)
                .toArray(AgentInstance[]::new);
        invocationCounter += agentsToCall.length;
        return call(agentsToCall);
    }

    private boolean terminated(AgenticScope agenticScope) {
        return invocationCounter > maxAgentsInvocations || exitCondition.test(agenticScope, invocationCounter);
    }
}
```

Here the `P2PPlanner` keeps track of the number of agent invocations
performed so far, and uses an `AgentActivator` for each subagent to
determine if it can be invoked based on the current state of the
`AgenticScope`. The `nextAction` method checks if the exit condition has
been met or if the maximum number of invocations has been reached, and
if not, it identifies all agents that can be activated based on the
current state, marks them as started, and returns an action to call
them.

To give a practical example of how this works let's try to build a
peer-to-peer agentic system that can perform a scientific research and
formulate new hypothesis on a given topic, so that the API of this
service could be something like:

```java
public interface ResearchAgent {

    @Agent("Conduct research on a given topic")
    String research(@V("topic") String topic);
}
```

To this purpose the following 5 agents can be defined:

```java
public interface LiteratureAgent {

    @SystemMessage("Search for scientific literature on the given topic and return a summary of the findings.")
    @UserMessage("""
            You are a scientific literature search agent.
            Your task is to find relevant scientific papers on the topic provided by the user and summarize them.
            Use the provided tool to search for scientific papers and return a summary of your findings.
            The topic is: {{topic}}
            """)
    @Agent("Search for scientific literature on a given topic")
    String searchLiterature(@V("topic") String topic);
}

public interface HypothesisAgent {

    @SystemMessage("Based on the research findings, formulate a clear and concise hypothesis related to the given topic.")
    @UserMessage("""
            You are a hypothesis formulation agent.
            Your task is to formulate a clear and concise hypothesis based on the research findings provided by the user.
            The topic is: {{topic}}
            The research findings are: {{researchFindings}}
            """)
    @Agent("Formulate hypothesis around a give topic based on research findings")
    String makeHypothesis(@V("topic") String topic, @V("researchFindings") String researchFindings);
}

public interface CriticAgent {

    @SystemMessage("Critically evaluate the given hypothesis related to the specified topic. Provide constructive feedback and suggest improvements if necessary.")
    @UserMessage("""
            You are a critical evaluation agent.
            Your task is to critically evaluate the hypothesis provided by the user in relation to the specified topic.
            Provide constructive feedback and suggest improvements if necessary.
            If you need to, you can also perform additional research to validate or confute the hypothesis using the provided tool.
            The topic is: {{topic}}
            The hypothesis is: {{hypothesis}}
            """)
    @Agent("Critically evaluate a hypothesis related to a given topic")
    String criticHypothesis(@V("topic") String topic, @V("hypothesis") String hypothesis);
}

public interface ValidationAgent {

    @SystemMessage("Validate the provided hypothesis on the given topic based on the critique provided.")
    @UserMessage("""
            You are a validation agent.
            Your task is to validate the hypothesis provided by the user in relation to the specified topic based on the critique provided.
            Validate the provided hypothesis, either confirming it or reformulating a different hypothesis based on the critique.
            The topic is: {{topic}}
            The hypothesis is: {{hypothesis}}
            The critique is: {{critique}}
            """)
    @Agent("Validate a hypothesis based on a given topic and critique")
    String validateHypothesis(@V("topic") String topic, @V("hypothesis") String hypothesis, @V("critique") String critique);
}

public interface ScorerAgent {

    @SystemMessage("Score the provided hypothesis on the given topic based on the critique provided.")
    @UserMessage("""
            You are a scoring agent.
            Your task is to score the hypothesis provided by the user in relation to the specified topic based on the critique provided.
            Score the provided hypothesis on a scale from 0.0 to 1.0, where 0.0 means the hypothesis is completely invalid and 1.0 means the hypothesis is fully valid.
            The topic is: {{topic}}
            The hypothesis is: {{hypothesis}}
            The critique is: {{critique}}
            """)
    @Agent("Score a hypothesis based on a given topic and critique")
    double scoreHypothesis(@V("topic") String topic, @V("hypothesis") String hypothesis, @V("critique") String critique);
}
```

These agents will be all provided with a tool capable of performing
research on scientific literature, for instance downloading academic
papers from arXiv, and then added to the P2P agentic system:

```java
ArxivCrawler arxivCrawler = new ArxivCrawler();

LiteratureAgent literatureAgent = AgenticServices.agentBuilder(LiteratureAgent.class)
        .chatModel(baseModel())
        .tools(arxivCrawler)
        .outputKey("researchFindings")
        .build();
HypothesisAgent hypothesisAgent = AgenticServices.agentBuilder(HypothesisAgent.class)
        .chatModel(baseModel())
        .tools(arxivCrawler)
        .outputKey("hypothesis")
        .build();
CriticAgent criticAgent = AgenticServices.agentBuilder(CriticAgent.class)
        .chatModel(baseModel())
        .tools(arxivCrawler)
        .outputKey("critique")
        .build();
ValidationAgent validationAgent = AgenticServices.agentBuilder(ValidationAgent.class)
        .chatModel(baseModel())
        .tools(arxivCrawler)
        .outputKey("hypothesis")
        .build();
ScorerAgent scorerAgent = AgenticServices.agentBuilder(ScorerAgent.class)
        .chatModel(baseModel())
        .tools(arxivCrawler)
        .outputKey("score")
        .build();

ResearchAgent researcher = AgenticServices.plannerBuilder(ResearchAgent.class)
        .subAgents(literatureAgent, hypothesisAgent, criticAgent, validationAgent, scorerAgent)
        .outputKey("hypothesis")
        .planner(() -> new P2PPlanner(10, agenticScope -> {
            if (!agenticScope.hasState("score")) {
                return false;
            }
            double score = agenticScope.readState("score", 0.0);
            System.out.println("Current hypothesis score: " + score);
            return score >= 0.85;
        }))
        .build();

String hypothesis = researcher.research("black holes");
```

With this configuration the `researcher` p2p coordinator is passed with
the topic of the research. At this point the only agent that can be
invoked is the `literatureAgent`, because it is the only one that has
all its required inputs, in this case the `topic`, present in the
`AgenticScope`. The invocation of this agent produces the
`researchFindings` variable, which is added to the `AgenticScope` state,
and this new variable triggers the invocation of the `HypothesisAgent`.
Then this produces a `hypothesis` that in turn triggers the
`criticAgent`. Finally, the `ValidationAgent` takes in input both the
`hypothesis` and the `critique` and generates a new `hypothesis` that
eventually retriggers the other agents again. In the meanwhile the
`ScorerAgent` gives a `score` to the `hypothesis` and the process
terminates when this `score` is greater than or equal to 0.85, or when a
maximum of 10 agents invocations have been performed. The following
image summarizes all the agents and variables involved in this
execution.

<img width="1693" height="1101" alt="p2p"
src="https://github.com/user-attachments/assets/38d96c6d-c4b8-49ac-ae5f-6741067a2ab7"
/>


For instance a typical run of this example could terminate because the
`ScorerAgent` produced a score above the predetermined threshold

```
Current hypothesis score: 0.95
```

and the final output could be something like:

```
Based on the provided references, here are some key points about stochastic gravitational wave backgrounds (SGWBs) from primordial black holes (PBHs):

1. **Detection Rates and Sources:**
   - The detection rate of gravity waves emitted during parabolic encounters of stellar black holes in globular clusters was estimated by Kocsis et al. [85].
   - Gravitational wave bursts from PBH hyperbolic encounters were discussed by García-Bellido and Nesseris [93].

2. **Energy Emission:**
   - The energy spectrum of gravitational waves from hyperbolic encounters was studied by De Vittori, Jetzer, and Klein [88].
   - Gravitational wave energy emission and detection rates for PBH hyperbolic encounters were analyzed by García-Bellido and Nesseris [90].

3. **Template Banks:**
   - Template banks for gravitational waveforms from coalescing binary black holes (including non-spinning binaries) were developed by Ajith et al. [92].

4. **Constraints on PBHs:**
   - Constraints on primordial black holes were reviewed by Carr, Kohri, Sendouda, and Yokoyama [98].
   - Universal gravitational wave signatures of cosmological solitons were discussed by Lozanov, Sasaki, and Takhistov [100].

5. **Induced SGWBs:**
   - Doubly peaked induced stochastic gravitational wave backgrounds were tested for baryogenesis from primordial black holes by Bhaumik et al. [101].
   - Distinct signatures of spinning PBH domination and evaporation, including doubly peaked gravitational waves, dark relics, and CMB complementarity, were explored by Bhaumik et al. [101].

6. **Future Detectors:**
   - Future detectors like Taiji, LISA, DECIGO, Big Bang Observer, Cosmic Explorer, Einstein Telescope, and KAGRA are expected to contribute significantly to the detection of SGWBs from PBHs.

7. **Pulsar Timing Arrays:**
   - Pulsar timing arrays have been used to search for an isotropic stochastic gravitational wave background [73-75].

8. **Template Banks and Simulations:**
   - Template banks like those developed by Ajith et al. are crucial for matching observed signals with theoretical predictions.
```
2025-11-11 16:18:41 +01:00