Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fb83a67
feat(extensions-mongodb): add MongoDB storage extension
LiangshouX Aug 9, 2026
e3b5a67
fix(extensions-mongodb): harden lock, lifecycle and CAS correctness
LiangshouX Aug 9, 2026
03fd925
fix(extensions-mongodb): remove redundant index, add null guards and …
LiangshouX Aug 9, 2026
78107bd
fix(extensions-mongodb): fix versioning, TTL and index upgrade bugs i…
LiangshouX Aug 13, 2026
8847e93
fix(extensions-mongodb): fix contract test @AfterAll lifecycle for CI…
LiangshouX Aug 14, 2026
b4a310d
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 20, 2026
2696fbe
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 23, 2026
6fcdea8
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 25, 2026
bfb1c49
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 25, 2026
38a95b1
fix(extensions-mongodb): address review findings — session visibility…
LiangshouX Aug 25, 2026
4786856
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 26, 2026
be32639
Update agentscope-extensions/agentscope-extensions-mongodb/src/main/j…
LiangshouX Aug 26, 2026
1d1047f
Update agentscope-extensions/agentscope-extensions-mongodb/src/main/j…
LiangshouX Aug 26, 2026
4f0d5d3
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 26, 2026
c806a97
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 26, 2026
595bea5
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 27, 2026
c2bc7a6
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 28, 2026
92c622d
Merge branch 'main' into feature/mongodb-extension
LiangshouX Aug 31, 2026
66142ed
Merge branch 'main' into feature/mongodb-extension
LiangshouX Sep 2, 2026
995f0f9
Merge branch 'main' into feature/mongodb-extension
LiangshouX Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions agentscope-dependencies-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
<a2a-transport-jsonrpc.version>0.3.3.Final</a2a-transport-jsonrpc.version>
<jedis.version>7.4.1</jedis.version>
<lettuce.version>6.4.2.RELEASE</lettuce.version>
<mongodb-driver.version>5.3.1</mongodb-driver.version>
<xxl-job.version>3.3.2</xxl-job.version>
<quartz.version>2.5.2</quartz.version>
<spring.version>7.0.7</spring.version>
Expand Down Expand Up @@ -405,6 +406,13 @@
<version>${lettuce.version}</version>
</dependency>

<!-- MongoDB Java Driver (sync) for MongoDB-based session implementation -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>${mongodb-driver.version}</version>
</dependency>

<!-- XXL-JOB Core -->
<dependency>
<groupId>com.xuxueli</groupId>
Expand Down
7 changes: 7 additions & 0 deletions agentscope-distribution/agentscope-all/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,13 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions-mongodb</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions-studio</artifactId>
Expand Down
7 changes: 7 additions & 0 deletions agentscope-distribution/agentscope-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,13 @@
<version>${project.version}</version>
</dependency>

<!-- AgentScope Extensions MongoDB (AgentStateStore) -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions-mongodb</artifactId>
<version>${project.version}</version>
</dependency>

<!-- AgentScope Extensions Sandbox -->
<dependency>
<groupId>io.agentscope</groupId>
Expand Down
71 changes: 71 additions & 0 deletions agentscope-extensions/agentscope-extensions-mongodb/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2024-2026 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>

<name>AgentScope Java - Extensions - MongoDB</name>
<description>MongoDB-backed distributed implementation for AgentStateStore. Provides MongoDistributedStore for one-line distributed configuration.</description>
<artifactId>agentscope-extensions-mongodb</artifactId>

<dependencies>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-core</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-harness</artifactId>
<scope>provided</scope>
</dependency>

<!-- MongoDB Java Driver (sync) -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* Copyright 2024-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.agentscope.extensions.mongodb;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import io.agentscope.core.state.AgentStateStore;
import io.agentscope.extensions.mongodb.sandbox.MongoSandboxExecutionGuard;
import io.agentscope.extensions.mongodb.snapshot.MongoSnapshotSpec;
import io.agentscope.extensions.mongodb.state.MongoAgentStateStore;
import io.agentscope.extensions.mongodb.store.MongoBaseStore;
import io.agentscope.harness.agent.DistributedStore;
import io.agentscope.harness.agent.filesystem.remote.store.BaseStore;
import io.agentscope.harness.agent.sandbox.SandboxExecutionGuard;
import io.agentscope.harness.agent.sandbox.snapshot.SandboxSnapshotSpec;
import java.util.Objects;

/**
* MongoDB-backed {@link DistributedStore}.
*
* <p>Usage:
*
* <pre>{@code
* MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017");
*
* HarnessAgent agent = HarnessAgent.builder()
* .name("my-agent")
* .model("dashscope:qwen-plus")
* .distributedStore(MongoDistributedStore.create(mongoClient, "agentscope"))
* .build();
* }</pre>
*
* <p>This configures:
*
* <ul>
* <li>{@link MongoAgentStateStore} — agent session state in MongoDB
* <li>{@link MongoBaseStore} — workspace filesystem KV in MongoDB
* <li>{@link MongoSandboxExecutionGuard} — sandbox execution locking in MongoDB
* <li>{@link MongoSnapshotSpec} — sandbox workspace snapshots in MongoDB
* </ul>
*
* <p>When created via {@link #create(MongoClient)}, the caller owns the {@link MongoClient}
* lifecycle; {@link #close()} will NOT close the client. When created via {@link
* #fromConnectionString(String)}, the store owns the client and {@link #close()} will close it.
*/
public class MongoDistributedStore implements DistributedStore, AutoCloseable {

private static final String DEFAULT_DATABASE = "agentscope";
private static final String STATE_COLLECTION = "agentscope_sessions";
private static final String BASE_COLLECTION = "agentscope_base";

private final MongoClient mongoClient;
private final boolean ownsClient;
private final String databaseName;

private volatile AgentStateStore cachedAgentStateStore;
private volatile BaseStore cachedBaseStore;
private volatile SandboxSnapshotSpec cachedSnapshotSpec;
private volatile SandboxExecutionGuard cachedExecutionGuard;

private MongoDistributedStore(MongoClient mongoClient, String databaseName) {
this(mongoClient, databaseName, false);
}

private MongoDistributedStore(
MongoClient mongoClient, String databaseName, boolean ownsClient) {
this.mongoClient = Objects.requireNonNull(mongoClient, "mongoClient");
this.ownsClient = ownsClient;
this.databaseName = databaseName != null ? databaseName : DEFAULT_DATABASE;
}

/**
* Creates a MongoDB distributed store with the default database name ({@code "agentscope"}).
*
* @param mongoClient the MongoDB client
* @return a new MongoDB distributed store
*/
public static MongoDistributedStore create(MongoClient mongoClient) {
return new MongoDistributedStore(mongoClient, null);
}

/**
* Creates a MongoDB distributed store.
*
* @param mongoClient the MongoDB client
* @param databaseName the database name
* @return a new MongoDB distributed store
*/
public static MongoDistributedStore create(MongoClient mongoClient, String databaseName) {
return new MongoDistributedStore(mongoClient, databaseName);
}

/**
* Creates a MongoDB distributed store from a connection string. A new {@link MongoClient} is
* created internally and owned by the store; {@link #close()} will close it.
*
* @param connectionString the MongoDB connection string
* @return a new MongoDB distributed store
*/
public static MongoDistributedStore fromConnectionString(String connectionString) {
MongoClientSettings settings =
MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(connectionString))
.build();
return new MongoDistributedStore(MongoClients.create(settings), null, true);
}

@Override
public AgentStateStore agentStateStore() {
AgentStateStore result = cachedAgentStateStore;
if (result == null) {
synchronized (this) {
result = cachedAgentStateStore;
if (result == null) {
result =
MongoAgentStateStore.builder()
.mongoClient(mongoClient)
.databaseName(databaseName)
.collectionName(STATE_COLLECTION)
.build();
cachedAgentStateStore = result;
}
}
}
return result;
}

@Override
public BaseStore baseStore() {
BaseStore result = cachedBaseStore;
if (result == null) {
synchronized (this) {
result = cachedBaseStore;
if (result == null) {
MongoDatabase db = mongoClient.getDatabase(databaseName);
result = new MongoBaseStore(db, BASE_COLLECTION);
cachedBaseStore = result;
}
}
}
return result;
}

@Override
public SandboxSnapshotSpec sandboxSnapshotSpec() {
SandboxSnapshotSpec result = cachedSnapshotSpec;
if (result == null) {
synchronized (this) {
result = cachedSnapshotSpec;
if (result == null) {
result = new MongoSnapshotSpec(mongoClient, databaseName);
cachedSnapshotSpec = result;
}
}
}
return result;
}

@Override
public SandboxExecutionGuard sandboxExecutionGuard() {
SandboxExecutionGuard result = cachedExecutionGuard;
if (result == null) {
synchronized (this) {
result = cachedExecutionGuard;
if (result == null) {
result =
MongoSandboxExecutionGuard.builder(mongoClient)
.databaseName(databaseName)
.build();
cachedExecutionGuard = result;

This comment was marked as abuse.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] close() does not propagate to cached components

When close() is called and ownsClient == true, the MongoClient is closed. However, cached MongoAgentStateStore instances (created via agentStateStore()) also implement AutoCloseable but are not closed.

Since they share the same MongoClient (supplied via builder.mongoClient()), their close() won't attempt to close the client (ownsClient=false in their context). So this is functionally safe, but for consistency and future-proofing, consider closing cached components.

}
}
}
return result;
}

@Override
public void close() {
if (ownsClient) {
mongoClient.close();
}
}
}
Loading
Loading