feat(spring): jamjet-agent Spring Boot starter (Track 9 Phase C, completes the ADK) - #10
Conversation
|
Warning Review limit reached
More reviews will be available in 36 minutes and 26 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughA new Changesjamjet-agent Spring Boot Starter
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
jamjet-agent-spring-boot-starter/README.md (2)
109-114: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueIncomplete CGLIB section or missing trailing newline.
The CGLIB section ends abruptly at line 113-114 with an unclosed narrative. Add a concluding sentence or transition, and ensure the file terminates with a newline.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@jamjet-agent-spring-boot-starter/README.md` around lines 109 - 114, The CGLIB-proxied tool beans section in README is incomplete and ends abruptly, so update the documentation by adding a closing sentence or transition in the CGLIB-proxied tool beans section and ensure the README ends with a trailing newline. Keep the wording consistent with the surrounding prose and use the existing “CGLIB-proxied tool beans” heading and “@Tool methods” explanation to place the fix.
26-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winClarify the actual bean type in the auto-configuration table.
The table says
JavaToolWorker (as a SmartLifecycle), but the auto-configuration exposesJavaToolWorkerLifecycle(which wrapsJavaToolWorker). Users autowiring by type may expectJavaToolWorkeritself. Consider rewording toJavaToolWorkerLifecycleor noting it explicitly.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@jamjet-agent-spring-boot-starter/README.md` around lines 26 - 29, Update the auto-configuration table entry to match the actual bean exposed by the starter: the current `JavaToolWorker (as a SmartLifecycle)` wording is misleading because the configuration registers `JavaToolWorkerLifecycle` wrapping `JavaToolWorker`. Reword the row in the README to reference `JavaToolWorkerLifecycle` explicitly, or clarify that the lifecycle bean is what gets auto-configured so users know what type is available for autowiring.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@jamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/JavaToolWorkerLifecycle.java`:
- Around line 43-45: `JavaToolWorkerLifecycle.start()` is creating the worker
thread as a daemon, which allows the JVM to exit before `JavaToolWorker`
finishes processing. Update the thread setup in `start()` so the `worker::run`
thread is non-daemon, and rely on `stop()` in `JavaToolWorkerLifecycle` for
shutdown coordination; use the `thread`, `worker`, and `start()`/`stop()`
symbols to locate the change.
In
`@jamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/ToolBeanRegistrar.java`:
- Around line 72-76: `afterSingletonsInstantiated()` in `ToolBeanRegistrar` only
scans already-created singletons via `beanFactory.getSingleton(name)`, so lazy
`@Tool` beans are skipped and never registered. Update the registration flow to
discover candidate tool beans from bean metadata and register them even if they
have not been instantiated yet, or explicitly detect lazy tool beans and fail
fast with a clear error. Use the existing `ToolBeanRegistrar`,
`afterSingletonsInstantiated()`, and `ToolRegistry` flow to locate and fix the
incomplete discovery logic.
---
Nitpick comments:
In `@jamjet-agent-spring-boot-starter/README.md`:
- Around line 109-114: The CGLIB-proxied tool beans section in README is
incomplete and ends abruptly, so update the documentation by adding a closing
sentence or transition in the CGLIB-proxied tool beans section and ensure the
README ends with a trailing newline. Keep the wording consistent with the
surrounding prose and use the existing “CGLIB-proxied tool beans” heading and
“@Tool methods” explanation to place the fix.
- Around line 26-29: Update the auto-configuration table entry to match the
actual bean exposed by the starter: the current `JavaToolWorker (as a
SmartLifecycle)` wording is misleading because the configuration registers
`JavaToolWorkerLifecycle` wrapping `JavaToolWorker`. Reword the row in the
README to reference `JavaToolWorkerLifecycle` explicitly, or clarify that the
lifecycle bean is what gets auto-configured so users know what type is available
for autowiring.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 37400d05-9c48-427b-bddc-cfed8145f24c
📒 Files selected for processing (14)
jamjet-agent-spring-boot-starter/README.mdjamjet-agent-spring-boot-starter/pom.xmljamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/JamjetAgentAutoConfiguration.javajamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/JamjetAgentProperties.javajamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/JavaToolWorkerLifecycle.javajamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/ToolBeanRegistrar.javajamjet-agent-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsjamjet-agent-spring-boot-starter/src/test/java/dev/jamjet/agent/spring/JamjetAgentAutoConfigurationTest.javajamjet-agent-spring-boot-starter/src/test/java/dev/jamjet/agent/spring/ToolScanningTest.javajamjet-agent-spring-boot-starter/src/test/java/dev/jamjet/agent/spring/example/CalculatorAgentConfig.javajamjet-agent-spring-boot-starter/src/test/java/dev/jamjet/agent/spring/example/CalculatorSpringApplication.javajamjet-agent-spring-boot-starter/src/test/java/dev/jamjet/agent/spring/example/CalculatorSpringWiringTest.javajamjet-agent-spring-boot-starter/src/test/java/dev/jamjet/agent/spring/example/CalculatorTools.javapom.xml
| thread = new Thread(worker::run, threadName); | ||
| thread.setDaemon(true); | ||
| thread.start(); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## JavaToolWorkerLifecycle\n'
sed -n '1,220p' jamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/JavaToolWorkerLifecycle.java
printf '\n## Search for related lifecycle / worker references\n'
rg -n "JavaToolWorkerLifecycle|worker::run|setDaemon\\(|SmartLifecycle|DisposableBean|`@PreDestroy`|shutdown|non-web|CLI|background-worker|durable worker|java_tool" \
jamjet-agent-spring-boot-starter src . -g '!**/target/**' -g '!**/build/**'Repository: jamjet-labs/jamjet-runtime-java
Length of output: 19392
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## README excerpt\n'
sed -n '1,140p' jamjet-agent-spring-boot-starter/README.md
printf '\n## Auto-configuration and properties excerpts\n'
sed -n '1,180p' jamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/JamjetAgentAutoConfiguration.java
printf '\n---\n'
sed -n '1,180p' jamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/JamjetAgentProperties.java
printf '\n## Example application excerpt\n'
sed -n '1,160p' jamjet-agent-spring-boot-starter/src/test/java/dev/jamjet/agent/spring/example/CalculatorSpringApplication.javaRepository: jamjet-labs/jamjet-runtime-java
Length of output: 15436
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## JavaToolWorker thread/executor setup\n'
sed -n '1,260p' jamjet-agent/src/main/java/dev/jamjet/agent/worker/JavaToolWorker.java
printf '\n## JavaToolWorker later sections\n'
sed -n '260,420p' jamjet-agent/src/main/java/dev/jamjet/agent/worker/JavaToolWorker.java
printf '\n## Calculator example test maybe references lifecycle expectations\n'
sed -n '1,240p' jamjet-agent-spring-boot-starter/src/test/java/dev/jamjet/agent/spring/JamjetAgentAutoConfigurationTest.javaRepository: jamjet-labs/jamjet-runtime-java
Length of output: 22687
Run the Spring worker on a non-daemon thread.
JavaToolWorkerLifecycle.start() marks the worker as daemon, so a non-web Spring Boot app can exit before java_tool drains. If this starter is meant to keep processing for the life of the context, let stop() handle shutdown instead.
Suggested change
thread = new Thread(worker::run, threadName);
- thread.setDaemon(true);
thread.start();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| thread = new Thread(worker::run, threadName); | |
| thread.setDaemon(true); | |
| thread.start(); | |
| thread = new Thread(worker::run, threadName); | |
| thread.start(); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@jamjet-agent-spring-boot-starter/src/main/java/dev/jamjet/agent/spring/JavaToolWorkerLifecycle.java`
around lines 43 - 45, `JavaToolWorkerLifecycle.start()` is creating the worker
thread as a daemon, which allows the JVM to exit before `JavaToolWorker`
finishes processing. Update the thread setup in `start()` so the `worker::run`
thread is non-daemon, and rely on `stop()` in `JavaToolWorkerLifecycle` for
shutdown coordination; use the `thread`, `worker`, and `start()`/`stop()`
symbols to locate the change.
…d + README Finding 1: scan bean definitions via getType (no forced instantiation) so a @lazy @tool bean is discovered and registered instead of silently dropped by the old singleton-only scan. getBean is called only for holders that declare a @tool method (no eager creation of unrelated lazy beans), restricted to singletons. CGLIB target resolution and the duplicate-name fail-fast are preserved. Adds a slice test that fails before the fix and passes after. Finding 2: document why the JavaToolWorker thread is a daemon (graceful stop() handles the normal path; on hard JVM exit the engine reclaims the leased item via lease expiry; non-daemon would risk hanging shutdown). No behavior change. Finding 3: README names the autowirable JavaToolWorkerLifecycle bean and finishes the CGLIB-proxied tool beans section.
What
Track 9 Phase C (the last piece): a Spring Boot starter for the Java
Agent. A newjamjet-agent-spring-boot-startermodule so a Spring developer adds one dependency, declares anAgentbean with@ToolSpring beans and governance, and gets a governed durable agent running on the engine.JamjetAgentAutoConfiguration(registered via the Boot 3AutoConfiguration.imports) exposes aJamjetEngineClient, aToolRegistry, aToolBeanRegistrar, and aJavaToolWorkerLifecyclefromJamjetAgentProperties(jamjet.agent.*). Every bean is@ConditionalOnMissingBean, so an application can override any of them.ToolBeanRegistrarscans Spring beans for@Toolmethods and registers them into the sharedToolRegistry, resolving the real CGLIB target first so proxied beans (for example@Transactionalcomponents) are not missed.JavaToolWorkerLifecycle(aSmartLifecycle) drains thejava_toolqueue on a daemon thread on context start and stops it cleanly on shutdown, gated byjamjet.agent.worker.enabled.The starter depends only on
jamjet-agentand Spring Boot. It deliberately does not pull in spring-ai or langchain4j, so the auto-configuration's bean return types always link and the known@ConditionalOnClassNoClassDefFoundErroris avoided.Safety
A whole-branch review confirmed: the dependency set avoids the
@ConditionalOnClassbug; the CGLIB@Toolresolution is correct and proven by a non-vacuous test (a genuinely CGLIB-proxied bean is registered, and removing the unwrap makes it vanish); the worker lifecycle starts without blocking startup and stops cleanly with no leaked thread, and is disableable; and theToolRegistryis a single shared bean across the registrar, the worker, and anAgent.Tests
mvn verifygreen: 11 module tests (the bean wiring,@ConditionalOnMissingBeanoverrides, the plain and CGLIB-proxied@Toolscanning, the lifecycle present/absent by flag, duplicate-tool-name rejection, and the worked Spring agent), plus the upstreamjamjet-runtime-core(85) andjamjet-agent(78).Track 9 complete
With Phase A (the engine
java_toolqueue), the fenced completion hardening, Phase B (the JavaAgentbuilder and durable tool-worker), and this Spring starter, a JVM developer can author a governed durable agent in idiomatic Java or Spring. This is the last track of the 9-track ADK plan.Follow-ups
A vestigial properties field,
@Tooldiscovery on superclasses (shared with the core registry), and the Jackson baseline bump (deferred from Phase B).Summary by CodeRabbit
New Features
Documentation
Tests