Documentation
¶
Overview ¶
Package execshared holds behavior shared by the executor implementations (claudeexecutor, googleexecutor, openaiexecutor) so each backend applies identical semantics: prompt-suffix handling, resource-label defaults, the submit routing predicate, the bounded tool-call dispatch pool, and the submit-gate validation tail.
Index ¶
- func AppendUserPromptSuffix(prompt string, suffix *promptbuilder.Prompt) (string, error)
- func DefaultResourceLabels(labels map[string]string) map[string]string
- func DispatchToolCalls[Call any](calls []Call, concurrency int, isSubmit func(Call) bool, ...)
- func FailTurnUnlessSuspended[T any](llmTurn *agenttrace.LLMTurn[T], err error)
- func GateSubmission[Response any](ctx context.Context, outcome toolcall.SubmitOutcome[Response], ...) (map[string]any, bool, error)
- func HeldOutPartition[Meta any](tools map[string]Meta, submitName string, submitConfigured bool, ...) (isSubmit, isSuspend, heldOut func(name string) bool, err error)
- func SubmitPredicate[Meta any](tools map[string]Meta, submitToolName string, submitConfigured bool) func(name string) bool
- func ValidateSuspendToolName(suspendName, submitName string) error
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AppendUserPromptSuffix ¶
func AppendUserPromptSuffix(prompt string, suffix *promptbuilder.Prompt) (string, error)
AppendUserPromptSuffix appends the built suffix to the prompt with a blank-line separator. A nil suffix returns the prompt unchanged. The suffix must be fully bound; a Build failure (for example an unbound placeholder) is returned wrapped.
Example ¶
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/executor/internal/execshared"
"chainguard.dev/driftlessaf/agents/promptbuilder"
)
func main() {
suffix, err := promptbuilder.NewPrompt("Focus on error handling.")
if err != nil {
panic(err)
}
prompt, err := execshared.AppendUserPromptSuffix("changeset payload", suffix)
if err != nil {
panic(err)
}
fmt.Println(prompt)
}
Output: changeset payload Focus on error handling.
func DefaultResourceLabels ¶
DefaultResourceLabels returns the resource labels for billing and observability attribution, starting from defaults derived from environment variables:
- service_name: from K_SERVICE, falling back to CLOUD_RUN_JOB (defaults to "unknown")
- product: from CHAINGUARD_PRODUCT (defaults to "unknown")
- team: from CHAINGUARD_TEAM (defaults to "unknown")
Custom labels override defaults if they use the same keys.
Reading the environment here is deliberate: these labels attribute a deployment, so the defaults come from deployment-level env vars set by the service's infrastructure, mirroring how cloudrun resolves the workload identity. The labels parameter is the explicit configuration path for callers that need to override or extend them.
Example ¶
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/executor/internal/execshared"
)
func main() {
// Custom labels override the environment-derived defaults on key match.
labels := execshared.DefaultResourceLabels(map[string]string{"team": "platform"})
fmt.Println(labels["team"])
}
Output: platform
func DispatchToolCalls ¶ added in v0.7.76
func DispatchToolCalls[Call any](calls []Call, concurrency int, isSubmit func(Call) bool, run func(i int, call Call))
DispatchToolCalls runs a single turn's tool calls under a bounded errgroup pool. The model may emit several independent tool calls in one turn (parallel tool use); a concurrency of 1 (or less) runs them strictly in order, higher values run them concurrently. run(i, call) must record its outcome in per-index slots so concurrent handlers never race on shared state, and must be safe for concurrent use when concurrency exceeds 1.
Calls matching isSubmit are held out of the pool and run sequentially only after every pooled handler has finished: a submission claims the turn's work is complete, and its result validators may read state the other handlers produce (worktrees, files), so they must observe the finished state rather than race the handlers still producing it. Slot order is preserved, so the callers' in-order result consumption is unaffected.
Example ¶
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/executor/internal/execshared"
)
func main() {
// Submit calls are held out of the pool and run only after every other
// handler has finished; a concurrency of 1 runs the pooled calls
// strictly in order.
calls := []string{"submit_result", "read_file", "list_dir"}
var order []string
execshared.DispatchToolCalls(calls, 1,
func(call string) bool { return call == "submit_result" },
func(_ int, call string) { order = append(order, call) },
)
fmt.Println(order)
}
Output: [read_file list_dir submit_result]
func FailTurnUnlessSuspended ¶ added in v0.9.13
func FailTurnUnlessSuspended[T any](llmTurn *agenttrace.LLMTurn[T], err error)
FailTurnUnlessSuspended records err on the turn unless it is a *checkpoint.Suspension: a suspension is an intentional halt, not a failure, so the turn must not be marked Failed nor its span stamped Error (the trace-level disposition is set by Trace.Suspend on the suspend path). Every executor's per-turn error defer routes through this one carve-out so the three backends cannot drift on what counts as a failed turn.
Example ¶
package main
import (
"context"
"fmt"
"chainguard.dev/driftlessaf/agents/agenttrace"
"chainguard.dev/driftlessaf/agents/checkpoint"
"chainguard.dev/driftlessaf/agents/executor/internal/execshared"
)
func main() {
ctx := context.Background()
trace, _ := agenttrace.StartTrace[string](ctx, "prompt")
llmTurn := trace.BeginTurn(0, "anthropic", "model")
defer llmTurn.End()
// A suspension is an intentional halt, not a failure: the turn is left
// unfailed. Any other error would mark the turn failed.
execshared.FailTurnUnlessSuspended(llmTurn, &checkpoint.Suspension{})
fmt.Println("turn not failed")
}
Output: turn not failed
func GateSubmission ¶ added in v0.7.76
func GateSubmission[Response any]( ctx context.Context, outcome toolcall.SubmitOutcome[Response], trace *agenttrace.Trace[Response], callID, toolName string, args map[string]any, validators []callbacks.ResultValidator[Response], rec *telemetry.Recorder, submitToolName string, resultPtr *Response, ) (map[string]any, bool, error)
GateSubmission gates a terminal submit tool call on the registered result validators — the provider-neutral tail of every executor's evaluateSubmission, applied after the backend's submit handler has parsed the call into outcome. callID, toolName, and args identify the call on the trace; submitToolName names the tool in the rejection payload. It returns the tool result to send back to the model and whether the response committed as the run's final result (written through resultPtr). A rejected submission returns the validators' findings so the model can address them and submit again; a validator error aborts the run.
Example ¶
package main
import (
"context"
"fmt"
"chainguard.dev/driftlessaf/agents/agenttrace"
"chainguard.dev/driftlessaf/agents/executor/internal/execshared"
"chainguard.dev/driftlessaf/agents/executor/internal/telemetry"
"chainguard.dev/driftlessaf/agents/metrics"
"chainguard.dev/driftlessaf/agents/toolcall"
)
func main() {
type reviewResult struct {
Verdict string `json:"verdict"`
}
ctx := context.Background()
trace, _ := agenttrace.StartTrace[reviewResult](ctx, "prompt")
rec := telemetry.NewRecorder(metrics.NewGenAI("example"), "model", "provider", nil, nil)
// The backend's submit handler parsed the model's call into an accepted
// outcome; the gate runs the validators and commits the response.
var result reviewResult
toolResult, committed, err := execshared.GateSubmission(ctx,
toolcall.SubmitOutcome[reviewResult]{
Accepted: true,
Response: reviewResult{Verdict: "pass"},
Reasoning: "all checks passed",
ToolResult: map[string]any{"success": true},
},
trace, "call-1", "submit_result", map[string]any{"verdict": "pass"},
nil, rec, "submit_result", &result)
if err != nil {
panic(err)
}
fmt.Println(committed, result.Verdict, toolResult["success"])
}
Output: true pass true
func HeldOutPartition ¶ added in v0.9.13
func HeldOutPartition[Meta any](tools map[string]Meta, submitName string, submitConfigured bool, suspendName string, suspendConfigured bool) (isSubmit, isSuspend, heldOut func(name string) bool, err error)
HeldOutPartition builds the per-run tool partition every executor's turn loop uses: isSubmit and isSuspend route by name with identical not-shadowed-by-caller-tool semantics (see SubmitPredicate), and heldOut is their union — the set DispatchToolCalls holds out of the concurrent pool so a held-out call runs only after every sibling handler has finished and its real result is in the transcript. It fails when the suspend tool is shadowed by a caller-registered tool of the same name: silently routing the call to the caller's handler would no-op the operator's intent to pause.
Example ¶
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/executor/internal/execshared"
)
func main() {
// The suspend tool joins the submit tool in the held-out set; a
// caller-registered tool is dispatched in the concurrent pool.
tools := map[string]struct{}{"read_file": {}}
isSubmit, isSuspend, heldOut, err := execshared.HeldOutPartition(tools, "submit_result", true, "ask_human", true)
if err != nil {
panic(err)
}
fmt.Println(isSubmit("submit_result"), isSuspend("ask_human"), heldOut("read_file"))
}
Output: true true false
func SubmitPredicate ¶ added in v0.7.76
func SubmitPredicate[Meta any](tools map[string]Meta, submitToolName string, submitConfigured bool) func(name string) bool
SubmitPredicate returns the routing predicate for a run's terminal submit tool: a call routes to submit when its name is not registered as a regular tool, matches the submit tool's name, and a submit handler is configured (submitConfigured). Each executor builds one instance per Execute and uses it for both executeToolCall's dispatch switch and DispatchToolCalls' held-out-of-pool partition, so the two sites cannot drift.
Example ¶
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/executor/internal/execshared"
)
func main() {
tools := map[string]struct{}{"read_file": {}}
isSubmit := execshared.SubmitPredicate(tools, "submit_result", true)
fmt.Println(isSubmit("submit_result"), isSubmit("read_file"), isSubmit("other"))
}
Output: true false false
func ValidateSuspendToolName ¶ added in v0.9.13
ValidateSuspendToolName is the construction-time guard shared by every executor's suspend option: the suspend tool must have a name, and it must not collide with the terminal submit tool — a collision would make the pause check intercept every submission so the run could never terminate.
Example ¶
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/executor/internal/execshared"
)
func main() {
fmt.Println(execshared.ValidateSuspendToolName("ask_human", "submit_result"))
fmt.Println(execshared.ValidateSuspendToolName("submit_result", "submit_result"))
}
Output: <nil> suspend tool "submit_result" collides with the submit tool name; the run could never terminate
Types ¶
This section is empty.