mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
76 lines
3.7 KiB
Java
76 lines
3.7 KiB
Java
package vip.mate.agent.delegation;
|
|
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Immutable snapshot of one delegation layer's runtime identity.
|
|
*
|
|
* <p>This is the canonical value object that carries "who am I in the delegation
|
|
* tree" down a single child agent run: tree depth, the immediate parent
|
|
* conversation, the human-facing root conversation, the subagent id of the layer
|
|
* currently executing, and the tool deny set in force for this layer.
|
|
*
|
|
* <p>It exists as a first-class, named record (rather than an anonymous frame
|
|
* buried in a ThreadLocal stack) so the same identity can later be passed
|
|
* explicitly through the call chain instead of being reconstructed from
|
|
* thread-local state — explicit passing survives virtual-thread and reactive
|
|
* hops, where a thread-confined stack does not. {@link vip.mate.tool.builtin.DelegationContext}
|
|
* currently holds a stack of these per thread; callers that already have a
|
|
* context in hand should prefer threading it explicitly.
|
|
*
|
|
* @param depth 1-based tree depth; {@code 0} means the top-level
|
|
* (non-delegated) call.
|
|
* @param parentConversationId the immediate parent conversation that spawned
|
|
* this layer, or {@code null} at the top level.
|
|
* @param rootConversationId the human-facing conversation at the top of the
|
|
* whole delegation tree; every layer carries it
|
|
* unchanged so a deep child's progress events can
|
|
* broadcast to the stream the user is watching.
|
|
* @param currentSubagentId the subagent id of the layer executing now; a
|
|
* deeper child reads it as its own parent id to
|
|
* reconstruct the spawn tree.
|
|
* @param deniedTools tool names this layer's agent may not call;
|
|
* normalised to a non-null immutable set.
|
|
*
|
|
* @author MateClaw Team
|
|
*/
|
|
public record SubagentRunContext(
|
|
int depth,
|
|
String parentConversationId,
|
|
String rootConversationId,
|
|
String currentSubagentId,
|
|
Set<String> deniedTools
|
|
) {
|
|
|
|
/** The top-level context: not inside any delegation. */
|
|
public static final SubagentRunContext ROOT = new SubagentRunContext(0, null, null, null, Set.of());
|
|
|
|
public SubagentRunContext {
|
|
// Normalise the deny set so every read site gets a non-null immutable
|
|
// view without re-checking — mirrors the old accessor's null guard.
|
|
deniedTools = (deniedTools == null) ? Set.of() : Set.copyOf(deniedTools);
|
|
}
|
|
|
|
/** True when this context represents a delegated (sub-agent) layer. */
|
|
public boolean isDelegated() {
|
|
return depth > 0;
|
|
}
|
|
|
|
/**
|
|
* Build the context for the next layer spawned beneath this one. The root
|
|
* conversation is inherited unchanged (falling back to the child's parent
|
|
* conversation when this is the first delegation), and depth advances by one.
|
|
*
|
|
* @param childParentConversationId the spawning conversation for the child
|
|
* @param childSubagentId the subagent id assigned to the child
|
|
* @param childDeniedTools tool deny set for the child
|
|
*/
|
|
public SubagentRunContext childFrame(String childParentConversationId,
|
|
String childSubagentId,
|
|
Set<String> childDeniedTools) {
|
|
String inheritedRoot = (rootConversationId != null) ? rootConversationId : childParentConversationId;
|
|
return new SubagentRunContext(depth + 1, childParentConversationId,
|
|
inheritedRoot, childSubagentId, childDeniedTools);
|
|
}
|
|
}
|