Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ func TestLiveClaudeSDKCancelResume(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()
out := make(chan proto.Envelope, 64)
request := proto.PromptRequestPayload{RunID: uuid.NewString(), Input: proto.TextInput(prompt), AgentSessionID: resume, DisableExecutionEnvironment: true, DisableSubagents: true, ExecutionControls: &proto.ExecutionControls{TextVerbosity: "medium"}, Model: "MiniMax-M3", ModelProvider: provider, SystemPrompt: "Follow the user's requested format. Preserve the exact verification value in conversation history. Use no tools."}
running, err := startSingleTurn(ctx, config, request, out)
request := proto.PromptRequestPayload{AgentSessionID: resume, DisableExecutionEnvironment: true, DisableSubagents: true, ExecutionControls: &proto.ExecutionControls{TextVerbosity: "medium"}, Model: "MiniMax-M3", ModelProvider: provider, SystemPrompt: "Follow the user's requested format. Preserve the exact verification value in conversation history. Use no tools."}
running, err := startSingleTurn(ctx, config, request, uuid.NewString(), proto.TextInput(prompt), out)
if err != nil {
t.Fatal(err)
}
Expand Down
8 changes: 4 additions & 4 deletions apps/daemon/internal/agent/claudesdk/cancellation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestCancellationWaitsForDrainAndPublishesOutcome(t *testing.T) {
defer cancel()
// A stopped consumer must not prevent native output draining or cancellation.
out := make(chan proto.Envelope)
running, err := startSingleTurn(ctx, config, cancellationRequest(), out)
running, err := startSingleTurn(ctx, config, cancellationRequest(), "run", proto.TextInput("hello"), out)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -89,7 +89,7 @@ func TestFailureKeepsOnlyVerifiedNativeIdentity(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
out := make(chan proto.Envelope, 8)
running, err := startSingleTurn(ctx, cancellationConfig(root, mode), cancellationRequest(), out)
running, err := startSingleTurn(ctx, cancellationConfig(root, mode), cancellationRequest(), "run", proto.TextInput("hello"), out)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -125,7 +125,7 @@ func TestCancellationDrainsIntoReadyConsumer(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
out := make(chan proto.Envelope, 16)
running, err := startSingleTurn(ctx, config, cancellationRequest(), out)
running, err := startSingleTurn(ctx, config, cancellationRequest(), "run", proto.TextInput("hello"), out)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -163,7 +163,7 @@ func cancellationConfig(root, mode string) Config {
}

func cancellationRequest() proto.PromptRequestPayload {
return proto.PromptRequestPayload{ModelProvider: fixtureProvider(), RunID: "run", Input: proto.TextInput("hello"), AgentSessionID: "native-session", Model: "fake-model", SystemPrompt: "instructions"}
return proto.PromptRequestPayload{ModelProvider: fixtureProvider(), AgentSessionID: "native-session", Model: "fake-model", SystemPrompt: "instructions"}
}

func runCancellationHelper(request startRequest, mode string, scanner *bufio.Scanner, emit func(bridgeEvent)) {
Expand Down
8 changes: 4 additions & 4 deletions apps/daemon/internal/agent/claudesdk/commands_session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

func TestWorkspaceCommandsRequirePackagedFeature(t *testing.T) {
config := preparationFixture(t, "old-command-runtime")
if _, err := NewExecutorFactory(config)(t.Context(), preparationRequest()); err == nil || !strings.Contains(err.Error(), "workspace preparation is unavailable") {
if _, err := NewExecutorFactory(config)(t.Context(), prepared(t, preparationRequest())); err == nil || !strings.Contains(err.Error(), "workspace preparation is unavailable") {
t.Fatal("old bridge accepted command observations", err)
}
if _, err := os.Stat(filepath.Join(config.StateDir, "launched")); !os.IsNotExist(err) {
Expand All @@ -28,7 +28,7 @@ func TestWorkspaceCommandsRequirePackagedFeature(t *testing.T) {

func TestWorkspaceCommandFramesKeepStartIdentityAndObservedOutput(t *testing.T) {
config := preparationFixture(t, "commands-success")
resource, err := NewExecutorFactory(config)(t.Context(), preparationRequest())
resource, err := NewExecutorFactory(config)(t.Context(), prepared(t, preparationRequest()))
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -77,15 +77,15 @@ func TestWorkspaceCommandCancellationAndBridgeFailuresCloseOnlyPendingCalls(t *t
req := workspaceRequest()
req.AgentSessionID = "native-session"
out := make(chan proto.Envelope, 32)
s, err := startSingleTurn(ctx, config, req, out)
s, err := startSingleTurn(ctx, config, req, "run", proto.TextInput("hello"), out)
if err != nil {
t.Fatal(err)
}
defer s.Cancel(context.Background())
frames := map[string][]proto.ToolCallPayload{}
failed, done := false, 0
for event := range out {
if event.ID != req.RunID || event.Type == proto.TypeCommandOutput {
if event.ID != "run" || event.Type == proto.TypeCommandOutput {
t.Fatal("command frame changed execution identity or fabricated deltas")
}
switch event.Type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ func TestClassifiedBridgeFailurePreservesTerminalEvidence(t *testing.T) {
root := t.TempDir()
t.Setenv("OAC_RUNTIME_HOME", root)
config := Config{Node: os.Args[0], Entrypoint: filepath.Join(root, "worker"), StateDir: filepath.Join(root, "state"), Env: []string{"GO_CLAUDE_SDK_HELPER=1", "SDK_HELPER_MODE=classified-" + mode, "GORACE=atexit_sleep_ms=0"}}
req := proto.PromptRequestPayload{ModelProvider: fixtureProvider(), RunID: "run", Input: proto.TextInput("hello"), AgentSessionID: "native-session", Model: "fake-model", SystemPrompt: "instructions"}
req := proto.PromptRequestPayload{ModelProvider: fixtureProvider(), AgentSessionID: "native-session", Model: "fake-model", SystemPrompt: "instructions"}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
out := make(chan proto.Envelope, 16)
s, err := startSingleTurn(ctx, config, req, out)
s, err := startSingleTurn(ctx, config, req, "run", proto.TextInput("hello"), out)
if err != nil {
t.Fatal(err)
}
Expand Down
12 changes: 6 additions & 6 deletions apps/daemon/internal/agent/claudesdk/execution_controls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ func TestExecutionControlsPreserveNativeDefaultsAndInstructions(t *testing.T) {
t.Setenv("OAC_RUNTIME_HOME", root)
config := Config{Entrypoint: filepath.Join(root, "worker"), StateDir: filepath.Join(root, "state")}
request := proto.PromptRequestPayload{ModelProvider: fixtureProvider(), AgentSessionID: "native-session", Model: "native-model", SystemPrompt: "Keep these exact instructions.\nDo not replace them."}
ordinary, _, err := prepareConfiguration(config, request)
ordinary, _, err := prepareConfiguration(config, prepared(t, request))
if err != nil {
t.Fatal(err)
}
request.ExecutionControls = &proto.ExecutionControls{TextVerbosity: "medium"}
before, _ := json.Marshal(request)
controlled, _, err := prepareConfiguration(config, request)
controlled, _, err := prepareConfiguration(config, prepared(t, request))
if err != nil {
t.Fatal(err)
}
Expand All @@ -39,8 +39,8 @@ func TestMCPWithoutEnvironmentNoneRejectedBeforeSetup(t *testing.T) {
t.Setenv("OAC_RUNTIME_HOME", root)
config := Config{Node: "must-not-run", Entrypoint: filepath.Join(root, "worker"), StateDir: filepath.Join(root, "state")}
servers := []proto.MCPHTTPServer{{ConnectionOrigin: "service", ServerLabel: "remote", ServerURL: "https://example.test/mcp"}}
request := proto.PromptRequestPayload{RunID: "run", Input: proto.TextInput("Input"), MCPHTTPServers: &servers, Model: "fixture", ModelProvider: fixtureProvider()}
_, err := startSingleTurn(t.Context(), config, request, make(chan proto.Envelope, 1))
request := proto.PromptRequestPayload{MCPHTTPServers: &servers, Model: "fixture", ModelProvider: fixtureProvider()}
_, err := startSingleTurn(t.Context(), config, request, "run", proto.TextInput("Input"), make(chan proto.Envelope, 1))
if err == nil || !strings.Contains(err.Error(), "service-origin MCP requires a service execution host") {
t.Fatal("MCP reached an unsupported environment", err)
}
Expand All @@ -55,7 +55,7 @@ func TestStructuredOutputConfigurationReachesNativeUnchanged(t *testing.T) {
config := Config{Entrypoint: filepath.Join(root, "worker"), StateDir: filepath.Join(root, "state")}
schema := json.RawMessage(`{"type":"object","properties":{"n":{"const":9007199254740992}}}`)
request := proto.PromptRequestPayload{ModelProvider: fixtureProvider(), DisableSubagents: true, Model: "model", SystemPrompt: "Original instructions.", ExecutionControls: &proto.ExecutionControls{TextVerbosity: "medium", OutputFormat: &proto.OutputFormat{Type: "json_schema", Schema: schema}}}
start, _, err := prepareConfiguration(config, request)
start, _, err := prepareConfiguration(config, prepared(t, request))
if err != nil {
t.Fatal(err)
}
Expand All @@ -75,7 +75,7 @@ func TestToolDiscoveryPreservesFrozenFunctions(t *testing.T) {
{Name: "clock", Description: "Clock", Parameters: json.RawMessage(`{"properties":{}}`)},
{Name: "note", Description: "Note", Parameters: json.RawMessage(`{"type":["object","null"]}`)},
}}
start, _, err := prepareConfiguration(config, request)
start, _, err := prepareConfiguration(config, prepared(t, request))
if err != nil || !start.ToolSearch || !reflect.DeepEqual(start.Functions[0], request.FunctionTools[0]) || string(start.Functions[1].Parameters) != `{"properties":{},"type":"object"}` || string(start.Functions[2].Parameters) != `{"type":"object"}` {
t.Fatal("function discovery changed native definitions", err)
}
Expand Down
12 changes: 1 addition & 11 deletions apps/daemon/internal/agent/claudesdk/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,10 @@ func NewExecutorFactory(config Config) agent.ExecutorFactory {
config.Workspace = &workspace
}
checked := &runtimeCheckCache{}
return func(ctx context.Context, req proto.PromptRequestPayload) (agent.Executor, error) {
return func(ctx context.Context, req agent.PrepareRequest) (agent.Executor, error) {
if ctx == nil {
ctx = context.Background()
}
if err := preparationOnly(req); err != nil {
return nil, err
}
start, env, err := prepareConfiguration(config, req)
if err != nil {
return nil, err
Expand All @@ -48,13 +45,6 @@ func NewExecutorFactory(config Config) agent.ExecutorFactory {
}
}

func preparationOnly(req proto.PromptRequestPayload) error {
if req.RunID != "" || len(req.Input) != 0 {
return errors.New("claudesdk: Executor preparation cannot submit input")
}
return nil
}

// startExecutor checks the installed bridge against probe, starts it through
// run and waits until it is ready for Turns.
func startExecutor(ctx context.Context, checked *runtimeCheckCache, probe Config, start startRequest, run func() (*session, error)) (agent.Executor, error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestExecutorNativeConfirmationSurvivesCleanup(t *testing.T) {
if mode == "pending_function" || mode == "pending_function_unconfirmed" {
req.FunctionTools = []proto.FunctionTool{{Name: "lookup", Parameters: json.RawMessage(`{"type":"object"}`)}}
}
owner, err := NewExecutorFactory(config)(t.Context(), req)
owner, err := NewExecutorFactory(config)(t.Context(), prepared(t, req))
if err != nil {
t.Fatal(err)
}
Expand Down
15 changes: 9 additions & 6 deletions apps/daemon/internal/agent/claudesdk/executor_fixture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import (
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
)

// startSingleTurn prepares an Executor for one Turn and closes it once that
// Turn settles, so each test observes the complete native lifecycle.
func startSingleTurn(ctx context.Context, config Config, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Turn, error) {
run, input := req.RunID, req.Input
req.RunID, req.Input = "", nil
resource, err := NewExecutorFactory(config)(ctx, req)
// startSingleTurn prepares an Executor as the registry does, starts one Turn
// and closes the Executor once that Turn settles, so each test observes the
// complete native lifecycle.
func startSingleTurn(ctx context.Context, config Config, req proto.PromptRequestPayload, run string, input proto.MessageInput, out chan<- proto.Envelope) (agent.Turn, error) {
configuration, err := Declaration.Configuration.Prepare(req)
if err != nil {
return nil, err
}
resource, err := NewExecutorFactory(config)(ctx, agent.PrepareRequest{PromptRequestPayload: req, Prepared: configuration})
if err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ func TestLiveClaudeExecutorReuseAndCancel(t *testing.T) {
defer persist()
request := proto.PromptRequestPayload{DisableExecutionEnvironment: true, DisableSubagents: true, ExecutionControls: &proto.ExecutionControls{TextVerbosity: "medium"}, Model: model, ModelProvider: provider, SystemPrompt: "Follow requested formats briefly. Remember the exact verification marker across the conversation. Use no tools."}
factory := NewExecutorFactory(config)
prepared := time.Now()
owner, err := factory(ctx, request)
started := time.Now()
owner, err := factory(ctx, prepared(t, request))
if err != nil {
t.Fatal("executor preparation failed", err)
}
evidence.PrepareMS = time.Since(prepared).Milliseconds()
evidence.PrepareMS = time.Since(started).Milliseconds()
defer func() {
closeCtx, stop := context.WithTimeout(context.Background(), 10*time.Second)
defer stop()
Expand Down Expand Up @@ -195,7 +195,7 @@ func TestLiveClaudeExecutorReuseAndCancel(t *testing.T) {
}
request.AgentSessionID = native
request.RequireExistingNativeSession = true
owner, err = factory(ctx, request)
owner, err = factory(ctx, prepared(t, request))
if err != nil {
t.Fatal("history recovery failed", err)
}
Expand Down
14 changes: 7 additions & 7 deletions apps/daemon/internal/agent/claudesdk/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func awaitExecutorTurn(t *testing.T, turn agent.Turn, out <-chan proto.Envelope,
func TestExecutorRetainsProcessAcrossTurnsAndCancellation(t *testing.T) {
config, req := persistentConfig(t, "")
req.ModelProvider = &modelprovider.Provider{Protocol: modelprovider.Anthropic, BaseURL: "https://provider.example/anthropic", APIKey: "fixture-key"}
owner, err := NewExecutorFactory(config)(t.Context(), req)
owner, err := NewExecutorFactory(config)(t.Context(), prepared(t, req))
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -188,7 +188,7 @@ func TestExecutorRetainsProcessAcrossTurnsAndCancellation(t *testing.T) {
}
func TestExecutorLateTurnEventInvalidatesWithoutRetargeting(t *testing.T) {
config, req := persistentConfig(t, "late")
owner, err := NewExecutorFactory(config)(t.Context(), req)
owner, err := NewExecutorFactory(config)(t.Context(), prepared(t, req))
if err != nil {
t.Fatal(err)
}
Expand All @@ -213,7 +213,7 @@ func TestExecutorCachesReadinessUntilInstalledArtifactChanges(t *testing.T) {
config, req := persistentConfig(t, "")
factory := NewExecutorFactory(config)
for range 2 {
owner, err := factory(t.Context(), req)
owner, err := factory(t.Context(), prepared(t, req))
if err != nil {
t.Fatal(err)
}
Expand All @@ -232,7 +232,7 @@ func TestExecutorCachesReadinessUntilInstalledArtifactChanges(t *testing.T) {
if err := os.WriteFile(config.Entrypoint, []byte("version-two-changed"), 0600); err != nil {
t.Fatal(err)
}
owner, err := factory(t.Context(), req)
owner, err := factory(t.Context(), prepared(t, req))
if err != nil {
t.Fatal(err)
}
Expand All @@ -244,7 +244,7 @@ func TestExecutorCachesReadinessUntilInstalledArtifactChanges(t *testing.T) {

func TestExecutorSeparatesPreInputRejectionFromUnknownWrite(t *testing.T) {
config, req := persistentConfig(t, "block")
owner, err := NewExecutorFactory(config)(t.Context(), req)
owner, err := NewExecutorFactory(config)(t.Context(), prepared(t, req))
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -278,7 +278,7 @@ func TestExecutorSeparatesPreInputRejectionFromUnknownWrite(t *testing.T) {

func TestExecutorCancellationDeadlineInterruptsBlockedTransport(t *testing.T) {
config, req := persistentConfig(t, "block")
owner, err := NewExecutorFactory(config)(t.Context(), req)
owner, err := NewExecutorFactory(config)(t.Context(), prepared(t, req))
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -314,7 +314,7 @@ func TestExecutorCancellationDeadlineInterruptsBlockedTransport(t *testing.T) {

func TestSharedTextLifecycle(t *testing.T) {
config, req := persistentConfig(t, "text_contract")
owner, err := NewExecutorFactory(config)(t.Context(), req)
owner, err := NewExecutorFactory(config)(t.Context(), prepared(t, req))
if err != nil {
t.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions apps/daemon/internal/agent/claudesdk/functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ func TestFunctionTurnNativeReceipts(t *testing.T) {
root := t.TempDir()
t.Setenv("OAC_RUNTIME_HOME", root)
config := Config{Node: os.Args[0], Entrypoint: filepath.Join(root, "worker"), StateDir: filepath.Join(root, "state"), Env: []string{"GO_CLAUDE_SDK_HELPER=1", "SDK_HELPER_MODE=" + mode, "GORACE=atexit_sleep_ms=0"}}
request := proto.PromptRequestPayload{ModelProvider: fixtureProvider(), RunID: "run", Input: proto.TextInput("hello"), AgentSessionID: "native-session", Model: "fake-model", SystemPrompt: "instructions", FunctionTools: []proto.FunctionTool{{Name: "lookup", Description: "Lookup.", Parameters: json.RawMessage(`{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}}}`)}}}
request := proto.PromptRequestPayload{ModelProvider: fixtureProvider(), AgentSessionID: "native-session", Model: "fake-model", SystemPrompt: "instructions", FunctionTools: []proto.FunctionTool{{Name: "lookup", Description: "Lookup.", Parameters: json.RawMessage(`{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}}}`)}}}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
out := make(chan proto.Envelope, 16)
running, err := startSingleTurn(ctx, config, request, out)
running, err := startSingleTurn(ctx, config, request, "run", proto.TextInput("hello"), out)
if err != nil {
t.Fatal(err)
}
Expand Down
7 changes: 1 addition & 6 deletions apps/daemon/internal/agent/claudesdk/harness_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import (
"testing"

"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
"github.com/MiniMax-AI/OpenAgentCore/internal/harnessconfig"
)

func TestHarnessConfigReachesBridge(t *testing.T) {
root := t.TempDir()
t.Setenv("OAC_RUNTIME_HOME", root)
config := Config{Entrypoint: filepath.Join(root, "main.js"), StateDir: filepath.Join(root, "state")}
req := proto.PromptRequestPayload{ModelProvider: fixtureProvider(), Model: "fixture", HarnessConfig: proto.HarnessConfig(`{"effort":"high","thinking":{"type":"enabled","budgetTokens":1024}}`)}
start, _, err := prepareConfiguration(config, req)
start, _, err := prepareConfiguration(config, prepared(t, req))
if err != nil {
t.Fatal(err)
}
Expand All @@ -33,8 +32,4 @@ func TestHarnessConfigReachesBridge(t *testing.T) {
if applied["effort"] != "high" || applied["thinking"].(map[string]any)["budgetTokens"] != float64(1024) {
t.Fatal("bridge lost native configuration")
}
req.HarnessConfig = proto.HarnessConfig(`{"env":{"ANTHROPIC_BASE_URL":"bypass"}}`)
if _, _, err = prepareConfiguration(config, req); err != harnessconfig.ErrHarnessConfig {
t.Fatalf("provider override accepted: %v", err)
}
}
Loading
Loading