Why: N1 WP5/WP6 library side - the gate decision logic, artifact checks, verdict parsing and progress-reminder throttling move from the product into a gates package behind ports, and the native backend gains a contract form so conformance has a real implementation to drive. Evidence: go vet clean; go test ./... 27 packages ok, including the conformance suite (succeeded/descriptor/error-sideeffects) against the real backend. Impact: new runtime/gates package (ClaimGate, CheckArtifacts, ParseVerdict, ProgressReminder, shared caps); adapters/native.Backend gains Contract() returning an exec.ContractBackend wrapper - the legacy facade keeps its signature so squad consumption is untouched. Product texts (fix prompts, verifier prompts) stay injected by hosts. Refs: Documents proposal 2026-10-07-agent-stack-n1q1i1-plans.md WP5/WP6
68 lines
1.9 KiB
Go
68 lines
1.9 KiB
Go
package native
|
||
|
||
import (
|
||
"context"
|
||
"testing"
|
||
"time"
|
||
|
||
"agent-native/conformance"
|
||
"agent-native/exec"
|
||
"agent-native/runtime/llm"
|
||
)
|
||
|
||
// TestContractBackendConformance 把 native 后端的契约形态挂上最小契约套件
|
||
// (N1 WP6:conformance 首次获得真实实现调用方)。
|
||
func TestContractBackendConformance(t *testing.T) {
|
||
conformance.Suite(t, func() exec.ContractBackend {
|
||
return New(Options{
|
||
ModelOptions: ModelOptions{
|
||
Model: "fake",
|
||
Stream: script(
|
||
llm.Message{Role: llm.RoleAssistant, Content: "hi", StopReason: llm.StopEnd},
|
||
),
|
||
},
|
||
}).Contract()
|
||
})
|
||
}
|
||
|
||
// TestContractExecuteCancel 取消路径:流阻塞到 ctx 结束,Outcome=Canceled 且
|
||
// SideEffects 显式。
|
||
func TestContractExecuteCancel(t *testing.T) {
|
||
b := New(Options{
|
||
ModelOptions: ModelOptions{
|
||
Model: "fake",
|
||
Stream: func(ctx context.Context, model string, messages []llm.Message, decls []llm.ToolDecl, opts llm.StreamOptions) <-chan llm.Event {
|
||
ch := make(chan llm.Event)
|
||
go func() {
|
||
<-ctx.Done()
|
||
close(ch)
|
||
}()
|
||
return ch
|
||
},
|
||
},
|
||
}).Contract()
|
||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
|
||
defer cancel()
|
||
res, err := b.Execute(ctx, exec.ContractRequest{ExecutionID: "cx", Prompt: "hi"})
|
||
if err != nil {
|
||
t.Fatalf("transport error: %v", err)
|
||
}
|
||
if res.Outcome != exec.OutcomeCanceled && res.Outcome != exec.OutcomeFailed {
|
||
t.Fatalf("outcome = %q", res.Outcome)
|
||
}
|
||
if res.Err == nil || res.Err.SideEffects == "" {
|
||
t.Fatalf("err = %+v", res.Err)
|
||
}
|
||
}
|
||
|
||
// TestContractDescriptor 描述的身份与能力。
|
||
func TestContractDescriptor(t *testing.T) {
|
||
d := New(Options{}).Contract().Descriptor()
|
||
if d.Name != "agent-native" {
|
||
t.Fatalf("name = %q", d.Name)
|
||
}
|
||
if !d.Has(exec.CapText) || !d.Has(exec.CapStream) || !d.Has(exec.CapTools) {
|
||
t.Fatalf("capabilities = %v", d.Capabilities)
|
||
}
|
||
}
|