fix: send profileArn for social accounts, improve error messages
- Remove IsSocial guard from sqliteKiroAuth.ProfileARN(); the Kiro API now requires profileArn on all requests including personal accounts - Add OLLIE_KIRO_PROFILE_ARN env var for static token auth - Add repeated error detection (3x identical error triggers corrective nudge) - Include expected format in tool dispatch error messages
This commit is contained in:
parent
75f137dda5
commit
a50149d075
|
|
@ -76,6 +76,8 @@ func run(ctx context.Context, cfg agentConfig, state state) error {
|
|||
var roundsWithoutPlan int // consecutive tool rounds without a PLAN: block
|
||||
var lastAction string // previous round's LastAction for stall detection
|
||||
var stallRounds int // consecutive rounds with unchanged LastAction
|
||||
var lastErrorSig string // signature of previous round's error(s) for repeat detection
|
||||
var repeatErrorCount int // consecutive rounds with identical error signature
|
||||
// resultCache stores outputs of read-safe tool calls keyed by name+args.
|
||||
// Only tools classified as parallel-safe (immutable reads) are cached.
|
||||
// sync.Map is required because execOne may run in concurrent goroutines.
|
||||
|
|
@ -488,6 +490,36 @@ func run(ctx context.Context, cfg agentConfig, state state) error {
|
|||
}, nil)
|
||||
}
|
||||
|
||||
// Repeated identical error detection: if the same error occurs 3 times
|
||||
// in a row, the model is stuck on a format/argument issue. Inject the
|
||||
// error content as a corrective nudge immediately rather than waiting
|
||||
// for the generic soft limit.
|
||||
if allErrors && len(results) > 0 {
|
||||
var errSig strings.Builder
|
||||
for _, r := range results {
|
||||
if r.IsError {
|
||||
errSig.WriteString(r.Content)
|
||||
errSig.WriteByte('\n')
|
||||
}
|
||||
}
|
||||
sig := errSig.String()
|
||||
if sig == lastErrorSig {
|
||||
repeatErrorCount++
|
||||
} else {
|
||||
repeatErrorCount = 1
|
||||
lastErrorSig = sig
|
||||
}
|
||||
if repeatErrorCount == 3 {
|
||||
state.update(backend.Message{
|
||||
Role: "user",
|
||||
Content: "[system: you have made the same malformed tool call 3 times in a row. Read the error message carefully and fix the arguments. The error was: " + strings.TrimSpace(results[0].Content) + "]",
|
||||
}, nil)
|
||||
}
|
||||
} else {
|
||||
repeatErrorCount = 0
|
||||
lastErrorSig = ""
|
||||
}
|
||||
|
||||
// Replan gate: if the model has been calling tools without emitting a
|
||||
// PLAN: block, force it to replan before continuing.
|
||||
if strings.Contains(msg.Content, "PLAN:") {
|
||||
|
|
|
|||
|
|
@ -646,14 +646,20 @@ type kiroAuthSource interface {
|
|||
|
||||
// staticKiroAuth holds a pre-issued bearer token.
|
||||
type staticKiroAuth struct {
|
||||
token string
|
||||
token string
|
||||
profileARN string
|
||||
}
|
||||
|
||||
func (s *staticKiroAuth) DefaultEndpoint(_ context.Context) (string, error) {
|
||||
if s.profileARN != "" {
|
||||
return kiroDefaultEndpoint(s.profileARN)
|
||||
}
|
||||
return "", fmt.Errorf("base URL not configured and no profile ARN available with static auth")
|
||||
}
|
||||
|
||||
func (s *staticKiroAuth) ProfileARN(_ context.Context) (string, error) { return "", nil }
|
||||
func (s *staticKiroAuth) ProfileARN(_ context.Context) (string, error) {
|
||||
return s.profileARN, nil
|
||||
}
|
||||
|
||||
func (s *staticKiroAuth) AccessToken(_ context.Context) (string, error) {
|
||||
t := strings.TrimSpace(s.token)
|
||||
|
|
@ -712,10 +718,6 @@ func (s *sqliteKiroAuth) ProfileARN(ctx context.Context) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Personal/social accounts don't send profileArn in requests.
|
||||
if state.IsSocial {
|
||||
return "", nil
|
||||
}
|
||||
return strings.TrimSpace(state.ProfileARN), nil
|
||||
}
|
||||
|
||||
|
|
@ -1377,7 +1379,10 @@ func kiroUpdateSSOCacheToken(path, accessToken, refreshToken, expiresAt string)
|
|||
func newKiroAuthSource(apiKey string) (kiroAuthSource, error) {
|
||||
apiKey = strings.TrimSpace(apiKey)
|
||||
if !strings.HasPrefix(strings.ToLower(apiKey), "sqlite://") {
|
||||
return &staticKiroAuth{token: apiKey}, nil
|
||||
return &staticKiroAuth{
|
||||
token: apiKey,
|
||||
profileARN: strings.TrimSpace(os.Getenv("OLLIE_KIRO_PROFILE_ARN")),
|
||||
}, nil
|
||||
}
|
||||
// Parse sqlite:// URI
|
||||
parsed, err := url.Parse(apiKey)
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ func dispatchExecuteCode(ctx context.Context, e *Server, args json.RawMessage) (
|
|||
return "", fmt.Errorf("execute_code: bad args: %w", err)
|
||||
}
|
||||
if len(steps) == 0 {
|
||||
return "", fmt.Errorf("execute_code: steps is required")
|
||||
return "", fmt.Errorf("execute_code: steps is required. Format: {\"steps\": [{\"code\": \"your code here\"}]}")
|
||||
}
|
||||
return e.dispatchSteps(ctx, steps, timeout, sandboxName)
|
||||
}
|
||||
|
|
@ -203,10 +203,10 @@ func dispatchExecuteCode(ctx context.Context, e *Server, args json.RawMessage) (
|
|||
func dispatchCallTool(ctx context.Context, e *Server, args json.RawMessage) (string, error) {
|
||||
calls, timeout, sandboxName, err := execCallToolArgs(args)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("call_tool: bad args: %w", err)
|
||||
return "", fmt.Errorf("call_tool: bad args: %w. Expected format: {\"calls\": [{\"tool\": \"tool_name\", \"args\": [\"arg1\", ...]}]}", err)
|
||||
}
|
||||
if len(calls) == 0 {
|
||||
return "", fmt.Errorf("call_tool: calls is required")
|
||||
return "", fmt.Errorf("call_tool: calls is required. Format: {\"calls\": [{\"tool\": \"tool_name\", \"args\": [\"arg1\", ...]}]}")
|
||||
}
|
||||
// Validate: every top-level call must name a tool (or be a parallel group of tools).
|
||||
for i, c := range calls {
|
||||
|
|
@ -238,7 +238,7 @@ func dispatchPipeCall(ctx context.Context, e *Server, args json.RawMessage) (str
|
|||
return "", fmt.Errorf("pipe: bad args: %w", err)
|
||||
}
|
||||
if len(stages) == 0 {
|
||||
return "", fmt.Errorf("pipe: stages is required")
|
||||
return "", fmt.Errorf("pipe: stages is required. Format: {\"stages\": [{\"code\": \"...\"} or {\"tool\": \"name\", \"args\": [...]}]}")
|
||||
}
|
||||
return e.dispatchPipe(ctx, stages, timeout, sandboxName)
|
||||
}
|
||||
|
|
|
|||
Reference in New Issue