mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
- Add a database-backed supervisor with durable scheduling, fenced leases, cooldowns, bounded worker concurrency and expired-lease restart recovery. - Default new goals to persistent execution with zero meaning unlimited cumulative budget; preserve legacy goals and explicit positive limits. - Yield bounded graph segments to the supervisor instead of ending unfinished goals at graph-local continuation limits. Require persisted checklist evidence before accepting completion, including concurrent criterion edits. - Share conversation admission across interactive and background execution; preserve partial replies, usage and queued user input during interruption. - Persist Stop and missing-input pauses, respect approval boundaries, and commit resume and approval-denial transitions with correct transactions. - Retry identifiable transient failures with backoff; retain visible pauses for budget limits and errors that require review instead of replaying tools. - Expose owner-authorized execution status and reconnectable scheduling events; add H2, MySQL and Kingbase migrations, API types and bilingual documentation. Validation: 298 focused backend tests passed, including persistence, restart scheduling, approval races, cancellation, admission and existing runtime tests. Frontend type checking, bundled-doc parity and ID precision checks passed. V188 is registered and all three dialects have unique migration versions; the migration-map audit still reports 95 pre-existing missing registrations. Scope: single-backend native runtime. Recovery checks existing state before repeating effects; this does not promise exactly-once external tool execution.
52 lines
2.9 KiB
Java
52 lines
2.9 KiB
Java
package vip.mate.agent;
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
import org.springframework.test.util.ReflectionTestUtils;
|
|
import reactor.core.publisher.Flux;
|
|
import vip.mate.memory.MemoryProperties;
|
|
import java.util.function.BiFunction;
|
|
import java.util.function.Function;
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
|
|
class AgentServiceTurnAdmissionTest {
|
|
@Test void onlyInteractiveCompletionWakesApprovalWaitEvenAfterContextHasExited() {
|
|
MemoryProperties properties = new MemoryProperties();
|
|
properties.setLifecycleMediatorEnabled(false);
|
|
AgentService service = new AgentService(null,null,null,null,properties,null,null);
|
|
var events = org.mockito.Mockito.mock(org.springframework.context.ApplicationEventPublisher.class);
|
|
ReflectionTestUtils.setField(service,"events",events);
|
|
var source = reactor.core.publisher.Sinks.<String>empty();
|
|
BiFunction<String,String,Flux<String>> invoke = (message,conversation) -> source.asMono().flux();
|
|
Function<String,String> content = Function.identity();
|
|
Flux<String> automatic = ReflectionTestUtils.invokeMethod(service,"withLifecycleFlux",1L,"a","conv",invoke,content);
|
|
vip.mate.agent.context.GoalContinuationContext.call(automatic::subscribe);
|
|
source.tryEmitEmpty();
|
|
org.mockito.Mockito.verifyNoInteractions(events);
|
|
|
|
BiFunction<String,String,Flux<String>> interactiveInvoke = (message,conversation) -> Flux.empty();
|
|
Flux<String> interactive = ReflectionTestUtils.invokeMethod(service,"withLifecycleFlux",1L,"b","conv",interactiveInvoke,content);
|
|
interactive.blockLast();
|
|
org.mockito.Mockito.verify(events).publishEvent(new vip.mate.goal.service.GoalExecutionSignal.TurnFinished("conv"));
|
|
}
|
|
|
|
@Test void admissionIsLazySharedAndReleasedOnCancellation() {
|
|
MemoryProperties properties = new MemoryProperties();
|
|
properties.setLifecycleMediatorEnabled(false);
|
|
AgentService service = new AgentService(null,null,null,null,properties,null,null);
|
|
BiFunction<String,String,Flux<String>> invoke = (message,conversation) -> Flux.never();
|
|
Function<String,String> content = Function.identity();
|
|
Flux<String> first = ReflectionTestUtils.invokeMethod(service,"withLifecycleFlux",1L,"a","conv",invoke,content);
|
|
Flux<String> second = ReflectionTestUtils.invokeMethod(service,"withLifecycleFlux",1L,"b","conv",invoke,content);
|
|
assertNotNull(first); assertNotNull(second);
|
|
var subscription = first.subscribe();
|
|
var error = new java.util.concurrent.atomic.AtomicReference<Throwable>();
|
|
second.subscribe(value -> {},error::set);
|
|
assertNotNull(error.get(),"a second turn must not execute alongside the first");
|
|
subscription.dispose();
|
|
error.set(null);
|
|
var third = second.subscribe(value -> {},error::set);
|
|
assertNull(error.get());
|
|
third.dispose();
|
|
}
|
|
}
|