Interface WebAgentSessionManager
- All Superinterfaces:
AutoCloseable
- All Known Implementing Classes:
DefaultWebAgentSessionManager
Wraps AgentSession with the cross-node concerns required by a web deployment:
distributed locking per conversation, cross-node signal fan-out, mailbox hand-off when the calling node is not the
lock holder, and idempotency.
The manager is application-scoped: one instance per process, lifetime ≥ the AgentSessionFactory it owns. It is
AutoCloseable so application shutdown can release local sessions and disconnect from any cross-node SPI
backends.
Design reference: docs/design/implemented/web-agent-session-manager-design.md §5.1.
-
Method Summary
Modifier and TypeMethodDescriptionbuilder()Build a new manager.voidclose()Close the manager.default booleancloseGracefully(Duration timeout) Stop accepting new submits and wait up totimeoutfor in-flight turns to finish, then close the manager.default voiddeleteConversation(at.aimon.core.agent.conversation.ConversationId conversationId) Permanently delete the conversation, including the history persisted inConversationRepository.Flow.Publisher<at.aimon.core.agent.stream.AgentExecutionEvent> events(at.aimon.core.agent.conversation.ConversationId conversationId) Subscribe to streaming progress events forconversationId.voidinterrupt(at.aimon.core.agent.conversation.ConversationId conversationId, at.aimon.core.agent.interrupt.InterruptReason reason) Trip an interrupt on the active turn, if any.voidreleaseConversation(at.aimon.core.agent.conversation.ConversationId conversationId) Release the local session cache entry and emitonComplete()on the correspondingevents()publisher.submit(WebSubmitRequest request) Submit one turn of input to the conversation identified byrequest.
-
Method Details
-
builder
Build a new manager. SeeWebAgentSessionManagerBuilderfor the required SPIs and operational tuning parameters.- Returns:
- a fresh builder
-
submit
Submit one turn of input to the conversation identified byrequest.The manager evaluates
WebSubmitRequest.getAgentRef()against the conversation's current binding (design §3.6) and either runs the turn locally (lock acquired) or delivers the input to the cross-node inbox (lock held elsewhere).- Parameters:
request- the submit request (must not be null)- Returns:
- outcome describing whether the turn ran here or was queued for another node
- Throws:
at.aimon.core.agent.session.exception.ConflictingAgentException- when the request'sagentRefdiffers from the conversation's existing binding
-
events
Flow.Publisher<at.aimon.core.agent.stream.AgentExecutionEvent> events(at.aimon.core.agent.conversation.ConversationId conversationId) Subscribe to streaming progress events forconversationId.The publisher fan-outs both locally-emitted events (when this node owns the turn) and remote events arriving via the signal bus (when another node owns the turn). Multi-subscriber: several SSE clients may observe the same conversation simultaneously. Subscribers receive
onComplete()onreleaseConversation(ConversationId).- Parameters:
conversationId- the conversation (must not be null)- Returns:
- a
Flow.Publisherof events
-
interrupt
void interrupt(at.aimon.core.agent.conversation.ConversationId conversationId, at.aimon.core.agent.interrupt.InterruptReason reason) Trip an interrupt on the active turn, if any.The manager broadcasts an
INTERRUPTsignal — every node trips its own active session for that conversation. Idempotent: when no turn is active anywhere, the call is a silent no-op.- Parameters:
conversationId- the conversation (must not be null)reason- the interrupt classification (must not be null)
-
releaseConversation
void releaseConversation(at.aimon.core.agent.conversation.ConversationId conversationId) Release the local session cache entry and emitonComplete()on the correspondingevents()publisher.History stored in
ConversationRepositoryis preserved — submitting again with the same id resumes the conversation. For permanent deletion (history removal) calldeleteConversation(ConversationId)instead; directrepository.delete(id)bypasses the manager's lock and races in-flight turns (design §13 issue #10).- Parameters:
conversationId- the conversation (must not be null)
-
deleteConversation
default void deleteConversation(at.aimon.core.agent.conversation.ConversationId conversationId) Permanently delete the conversation, including the history persisted inConversationRepository.Implementations MUST acquire the conversation lock first (broadcasting
INTERRUPT(SESSION_RELEASED)on contention so the current holder yields) and only then callrepository.delete(id). This closes design §13 issue #10: a directrepository.delete(id)from outside the manager would race the in-flight turn (the holder may still be writing history) and corrupt cluster state.After delete the manager performs the same teardown as
releaseConversation(ConversationId)(cache eviction, inbox purge, terminalInterruptedAt,onComplete,EVICTbroadcast).The default implementation throws
UnsupportedOperationExceptionso existing in-process tests and minimal implementations remain source-compatible.- Parameters:
conversationId- the conversation (must not be null)- Throws:
IllegalStateException- if the lock cannot be acquired within the implementation's bounded retry budget — the caller may retry later
-
close
void close()Close the manager. Local sessions are closed; application-scoped collaborators (factory, executor, repository, scheduling components) are not.- Specified by:
closein interfaceAutoCloseable
-
closeGracefully
Stop accepting new submits and wait up totimeoutfor in-flight turns to finish, then close the manager. Subsequentsubmit(WebSubmitRequest)calls during draining throwIllegalStateException.If the timeout elapses before all turns drain, surviving turns are interrupted with
InterruptReason.SYSTEM_SHUTDOWNand the manager proceeds to a hard close. The lock release on each surviving turn flips the lease so other nodes can take over.- Parameters:
timeout- maximum time to wait for in-flight turns;Duration.ZEROdisables draining and matchesclose()semantics (must not be null, must not be negative)- Returns:
trueif all in-flight turns drained cleanly withintimeout,falseon timeout
-