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
6 changes: 1 addition & 5 deletions apps/daemon/internal/agent/contracttest/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,8 @@ func TextLifecycle(t *testing.T, fixture TextFixture) {
if err := first.Cancel(ctx); err != nil {
t.Fatal(err)
}
steerer, ok := second.(agent.DurableSteerer)
if !ok {
t.Fatal("public text Turn must implement DurableSteerer")
}
var written atomic.Int32
if err := steerer.SteerWithReceipt(ctx, proto.PromptSteerPayload{InputID: "contract-input", Input: fixture.SteeringInput}, func() { written.Add(1) }); err != nil {
if err := second.SteerWithReceipt(ctx, proto.PromptSteerPayload{InputID: "contract-input", Input: fixture.SteeringInput}, func() { written.Add(1) }); err != nil {
t.Fatalf("active input after stale cancellation: %v", err)
}
if written.Load() != 1 {
Expand Down
7 changes: 0 additions & 7 deletions apps/daemon/internal/agent/functions.go

This file was deleted.

34 changes: 33 additions & 1 deletion apps/daemon/internal/agent/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ package agent

import (
"context"
"errors"
"io"
"reflect"
"strings"
Expand Down Expand Up @@ -145,6 +146,7 @@ type Executor interface {
type Turn interface {
Session
DurableSteerer
FunctionResultSubmitter
// Success confirms closed output and settled native input, function and
// child-work obligations. Errors cannot prove cancellation.
AwaitSettlement(context.Context) (TurnSettlement, error)
Expand Down Expand Up @@ -182,12 +184,42 @@ type DurableSteerer interface {
SteerWithReceipt(context.Context, proto.PromptSteerPayload, func()) error
}

// FunctionResultSubmitter delivers a result for an outstanding native call.
// FunctionResultSubmitter is mandatory on every public Turn; support comes from the declaration. It delivers a result for an outstanding native call.
// A successful return requires its native application receipt.
type FunctionResultSubmitter interface {
SubmitFunctionResult(context.Context, proto.FunctionResultPayload) error
}

// Protocol errors.

// ErrUnsupportedOperation marks an intentional absence of optional behavior.
// Implementations wrap it with a static reason and perform no native effects.
// It is distinct from unavailable resources, failures and uncertain outcomes.
var ErrUnsupportedOperation = errors.New("agent: unsupported operation")

// ErrUnknownFunctionCall means the function call is no longer pending.
var ErrUnknownFunctionCall = errors.New("agent: function call is no longer pending")

// ErrSteeringNotReady means no input was sent because the turn is starting.
var ErrSteeringNotReady = errors.New("agent: turn is not ready for input")

// ErrSteeringInactive means no input was sent because the run ended or was cancelled.
var ErrSteeringInactive = errors.New("agent: run is no longer active")

// ErrSteeringRejected means the engine explicitly rejected the input.
var ErrSteeringRejected = errors.New("agent: input rejected")

// Installation is an optional adapter-owned native distribution contract.
// It supplies activation paths and readiness checks, never execution or model
// configuration. Runtime owns copying, checksums, publication and installation locks.
type Installation struct {
AgentKind string
Version string
Supported func() bool
Environment func(directory, node string) map[string]string
Check func(context.Context, string, string, []string) error
}

// Kind registration.

// RegisterKind installs the heartbeat descriptor and model configuration for an
Expand Down
14 changes: 0 additions & 14 deletions apps/daemon/internal/agent/installation.go

This file was deleted.

14 changes: 0 additions & 14 deletions apps/daemon/internal/agent/steering.go

This file was deleted.

8 changes: 0 additions & 8 deletions apps/daemon/internal/agent/unsupported.go

This file was deleted.

14 changes: 0 additions & 14 deletions apps/daemon/internal/agent/workspace_directory.go

This file was deleted.

18 changes: 0 additions & 18 deletions apps/daemon/internal/agent/workspace_read.go

This file was deleted.

4 changes: 4 additions & 0 deletions apps/daemon/internal/dispatch/executor_cancel_receipt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ func (t *receiptCancelTurn) AwaitSettlement(ctx context.Context) (agent.TurnSett
func (t *receiptCancelTurn) CancellationOutcome() proto.DonePayload {
return proto.DonePayload{Content: "observed", Metadata: map[string]any{proto.DoneMetaAgentSessionID: "native-session"}}
}
func (*receiptCancelTurn) SubmitFunctionResult(context.Context, proto.FunctionResultPayload) error {
return agent.ErrUnsupportedOperation
}

func (t *receiptCancelTurn) SteerWithReceipt(ctx context.Context, _ proto.PromptSteerPayload, written func()) error {
written()
close(t.written)
Expand Down
4 changes: 4 additions & 0 deletions apps/daemon/internal/dispatch/executor_handoff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ func TestPreparedDonePublishesAfterExecutorHandoff(t *testing.T) {
}

// These fixtures exercise settlement only; active input is deliberately rejected.
func (*terminalHandoffTurn) SubmitFunctionResult(context.Context, proto.FunctionResultPayload) error {
return agent.ErrUnsupportedOperation
}

func (*terminalHandoffTurn) SteerWithReceipt(context.Context, proto.PromptSteerPayload, func()) error {
return agent.ErrSteeringRejected
}
4 changes: 4 additions & 0 deletions apps/daemon/internal/dispatch/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ func (t *reusableTurn) finish() {
close(t.settled)
})
}
func (*reusableTurn) SubmitFunctionResult(context.Context, proto.FunctionResultPayload) error {
return agent.ErrUnsupportedOperation
}

func (t *reusableTurn) Cancel(context.Context) error { t.cancels.Add(1); t.finish(); return nil }
func (t *reusableTurn) CancellationOutcome() proto.DonePayload {
return proto.DonePayload{Content: t.id, Metadata: map[string]any{proto.DoneMetaAgentSessionID: "native-session"}}
Expand Down
11 changes: 2 additions & 9 deletions apps/daemon/internal/dispatch/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ func (r *Router) handleFunctionResult(ctx context.Context, env proto.Envelope) e
r.mu.Lock()
state := r.sessions[env.ID]
session, finishOperation, ready := r.preparedOperationLocked(state)
var submitter agent.FunctionResultSubmitter
if ready {
submitter, _ = session.(agent.FunctionResultSubmitter)
}
r.mu.Unlock()
if state != nil && !ready {
return r.sendInteractionDecisionAck(ctx, env.ID, result.DeliveryID, false, "not_ready", "function call is waiting for the native session")
Expand All @@ -61,13 +57,10 @@ func (r *Router) handleFunctionResult(ctx context.Context, env proto.Envelope) e
if state != nil && !state.capabilities.FunctionTools.IsSupported() {
return r.sendInteractionDecisionAck(ctx, env.ID, result.DeliveryID, false, "unsupported", "The runtime declaration does not support function results.")
}
if ready && submitter == nil {
return r.sendInteractionDecisionAck(ctx, env.ID, result.DeliveryID, false, "contract_violation", "Declared function capability has no implementation.")
}
if submitter == nil {
if session == nil {
return r.sendInteractionDecisionAck(ctx, env.ID, result.DeliveryID, false, "not_pending", "function call is no longer pending")
}
if err := submitter.SubmitFunctionResult(ctx, result); err != nil {
if err := session.SubmitFunctionResult(ctx, result); err != nil {
code := "runtime_error"
if errors.Is(err, agent.ErrUnsupportedOperation) {
code = "contract_violation"
Expand Down
11 changes: 6 additions & 5 deletions apps/daemon/internal/dispatch/native_file_results_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package dispatch
import (
"errors"
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/agent"
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/localworkspace"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
"github.com/google/uuid"
"io/fs"
Expand All @@ -14,13 +15,13 @@ func TestNativeDirectoryFailureMapping(t *testing.T) {
err error
outcome, code string
}{
{agent.ErrWorkspaceNotDirectory, "rejected", proto.WorkspaceReadNotDirectory},
{agent.ErrWorkspaceReadInvalid, "rejected", "invalid_request"},
{localworkspace.ErrWorkspaceNotDirectory, "rejected", proto.WorkspaceReadNotDirectory},
{localworkspace.ErrWorkspaceReadInvalid, "rejected", "invalid_request"},
{fs.ErrNotExist, "rejected", "not_found"},
{fs.ErrPermission, "rejected", "permission_denied"},
{agent.ErrWorkspaceReadUncertain, "unknown", "read_unconfirmed"},
{localworkspace.ErrWorkspaceReadUncertain, "unknown", "read_unconfirmed"},
} {
got := workspaceReadResult(agent.WorkspaceReadResult{}, test.err, 0)
got := workspaceReadResult(localworkspace.WorkspaceReadResult{}, test.err, 0)
if got.Outcome != test.outcome || got.ErrorCode != test.code {
t.Fatal(test.err, got)
}
Expand All @@ -32,7 +33,7 @@ func TestLocalUploadUnknownRetainsOwner(t *testing.T) {
if err != nil {
t.Fatal(err)
}
got := workspaceWriteResult(agent.WorkspaceWriteResult{}, errors.New("unconfirmed mutation"), 3)
got := workspaceWriteResult(localworkspace.WorkspaceWriteResult{}, errors.New("unconfirmed mutation"), 3)
if got.Outcome != "unknown" {
t.Fatal(got)
}
Expand Down
25 changes: 7 additions & 18 deletions apps/daemon/internal/dispatch/workspace_read.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"strings"
"time"

"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/agent"
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/localworkspace"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
)
Expand Down Expand Up @@ -82,19 +81,9 @@ func executeWorkspaceRead(ctx context.Context, binding *localworkspace.Binding,
if request.Operation == "directory" {
read, err := binding.ListWorkspaceDirectory(ctx, request.Path, request.MaxEntries)
if err != nil {
return workspaceReadResult(agent.WorkspaceReadResult{}, err, 0)
return workspaceReadResult(localworkspace.WorkspaceReadResult{}, err, 0)
}
if read.Entries == nil || len(read.Entries) > request.MaxEntries {
return workspaceReadResult(agent.WorkspaceReadResult{}, agent.ErrWorkspaceReadUncertain, 0)
}
directory := &proto.WorkspaceDirectoryResult{Entries: make([]proto.WorkspaceDirectoryEntry, 0, len(read.Entries)), Truncated: read.Truncated}
for _, entry := range read.Entries {
directory.Entries = append(directory.Entries, proto.WorkspaceDirectoryEntry{Name: entry.Name, Kind: entry.Kind, SizeBytes: entry.SizeBytes})
}
if !proto.ValidWorkspaceDirectory(directory, request.MaxEntries) {
return workspaceReadResult(agent.WorkspaceReadResult{}, agent.ErrWorkspaceReadUncertain, 0)
}
return proto.WorkspaceReadResultPayload{Outcome: "completed", Directory: directory, CloseAcknowledged: true}
return proto.WorkspaceReadResultPayload{Outcome: "completed", Directory: read, CloseAcknowledged: true}
}
read, err := binding.ReadWorkspaceFile(ctx, request.Path, request.MaxBytes)
return workspaceReadResult(read, err, request.MaxBytes)
Expand All @@ -104,18 +93,18 @@ func rejectedWorkspaceRead(code string) proto.WorkspaceReadResultPayload {
return proto.WorkspaceReadResultPayload{Outcome: "rejected", ErrorCode: code}
}

func workspaceReadResult(read agent.WorkspaceReadResult, err error, limit int) proto.WorkspaceReadResultPayload {
func workspaceReadResult(read localworkspace.WorkspaceReadResult, err error, limit int) proto.WorkspaceReadResultPayload {
if err == nil && len(read.Data) <= limit && (!read.Truncated || len(read.Data) == limit) {
return proto.WorkspaceReadResultPayload{Outcome: "completed", Data: read.Data, Truncated: read.Truncated, CloseAcknowledged: true}
}
for _, failure := range []struct {
err error
code string
}{
{agent.ErrWorkspaceReadUnavailable, "resource_unavailable"},
{agent.ErrWorkspaceReadBusy, "read_capacity"},
{agent.ErrWorkspaceReadInvalid, "invalid_request"},
{agent.ErrWorkspaceNotDirectory, proto.WorkspaceReadNotDirectory},
{localworkspace.ErrWorkspaceReadUnavailable, "resource_unavailable"},
{localworkspace.ErrWorkspaceReadBusy, "read_capacity"},
{localworkspace.ErrWorkspaceReadInvalid, "invalid_request"},
{localworkspace.ErrWorkspaceNotDirectory, proto.WorkspaceReadNotDirectory},
{fs.ErrNotExist, "not_found"},
{fs.ErrPermission, "permission_denied"},
} {
Expand Down
18 changes: 9 additions & 9 deletions apps/daemon/internal/dispatch/workspace_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"errors"
"time"

"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/agent"
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/localworkspace"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
"github.com/google/uuid"
)
Expand Down Expand Up @@ -139,16 +139,16 @@ func rejectedWorkspaceWrite(code string) proto.WorkspaceWriteResultPayload {
return proto.WorkspaceWriteResultPayload{Outcome: "rejected", ErrorCode: code}
}

func workspaceWriteResult(write agent.WorkspaceWriteResult, err error, size int) proto.WorkspaceWriteResultPayload {
func workspaceWriteResult(write localworkspace.WorkspaceWriteResult, err error, size int) proto.WorkspaceWriteResultPayload {
if err == nil && write.SizeBytes == int64(size) {
return proto.WorkspaceWriteResultPayload{Outcome: "completed", SizeBytes: size}
}
for _, conflict := range []struct {
err error
reason string
}{
{agent.ErrWorkspaceWriteDirectory, proto.WorkspaceWriteReasonDirectory},
{agent.ErrWorkspaceWriteUnsafe, proto.WorkspaceWriteReasonUnsafe},
{localworkspace.ErrWorkspaceWriteDirectory, proto.WorkspaceWriteReasonDirectory},
{localworkspace.ErrWorkspaceWriteUnsafe, proto.WorkspaceWriteReasonUnsafe},
} {
if errors.Is(err, conflict.err) {
return proto.WorkspaceWriteResultPayload{Outcome: "rejected", ErrorCode: "write_rejected", Reason: conflict.reason}
Expand All @@ -158,11 +158,11 @@ func workspaceWriteResult(write agent.WorkspaceWriteResult, err error, size int)
err error
code string
}{
{agent.ErrWorkspaceWriteUnsupported, "write_unsupported"},
{agent.ErrWorkspaceWriteUnavailable, "resource_unavailable"},
{agent.ErrWorkspaceWriteBusy, "write_capacity"},
{agent.ErrWorkspaceWriteInvalid, "invalid_request"},
{agent.ErrWorkspaceWriteRejected, "write_rejected"},
{localworkspace.ErrWorkspaceWriteUnsupported, "write_unsupported"},
{localworkspace.ErrWorkspaceWriteUnavailable, "resource_unavailable"},
{localworkspace.ErrWorkspaceWriteBusy, "write_capacity"},
{localworkspace.ErrWorkspaceWriteInvalid, "invalid_request"},
{localworkspace.ErrWorkspaceWriteRejected, "write_rejected"},
} {
if errors.Is(err, failure.err) {
return rejectedWorkspaceWrite(failure.code)
Expand Down
5 changes: 2 additions & 3 deletions apps/daemon/internal/localworkspace/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import (
"io/fs"
"strings"

"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/agent"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
)

func (b *Binding) ListWorkspaceDirectory(ctx context.Context, path string, limit int) (agent.WorkspaceDirectoryResult, error) {
func (b *Binding) ListWorkspaceDirectory(ctx context.Context, path string, limit int) (*proto.WorkspaceDirectoryResult, error) {
if limit < 1 || limit > proto.WorkspaceDirectoryMaxEntries || len(path) > 4096 || strings.ContainsAny(path, "\\\x00\r\n") || (path != "" && (path == "." || !fs.ValidPath(path))) {
return agent.WorkspaceDirectoryResult{}, agent.ErrWorkspaceReadInvalid
return nil, ErrWorkspaceReadInvalid
}
return b.listNativeDirectory(ctx, path, limit)
}
5 changes: 2 additions & 3 deletions apps/daemon/internal/localworkspace/directory_native_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package localworkspace

import (
"errors"
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/agent"
"os"
"path/filepath"
"testing"
Expand All @@ -21,7 +20,7 @@ func TestNativeDirectoryAPI(t *testing.T) {
t.Fatal(got, err)
}
for _, path := range []string{"missing", "nested/data.bin"} {
if _, err := b.ListWorkspaceDirectory(t.Context(), path, 10); !errors.Is(err, agent.ErrWorkspaceNotDirectory) {
if _, err := b.ListWorkspaceDirectory(t.Context(), path, 10); !errors.Is(err, ErrWorkspaceNotDirectory) {
t.Fatal(path, err)
}
}
Expand All @@ -47,7 +46,7 @@ func TestNativeFileReadPrefixAndEntryKinds(t *testing.T) {
if err != nil || got.Truncated || len(got.Data) != 3 {
t.Fatal(got, err)
}
if _, err = b.ReadWorkspaceFile(t.Context(), "nested", 3); !errors.Is(err, agent.ErrWorkspaceReadInvalid) {
if _, err = b.ReadWorkspaceFile(t.Context(), "nested", 3); !errors.Is(err, ErrWorkspaceReadInvalid) {
t.Fatal(err)
}
if _, err = b.ReadWorkspaceFile(t.Context(), "missing", 3); !os.IsNotExist(err) {
Expand Down
Loading
Loading