Files
klinsmayaandClaude Fable 5.1 8e04e8ba25 feat(server): open-connector connection service and task token lifecycle
Add the open-connector business integration: user-owned OAuth and
API-key connections mirrored in user_connector_connection, a DB-backed
OAuth state table so callbacks survive restarts and replicas, and a
per-task runtime token minted at enqueue with allowedConnections pinned
to the agent owner's connections in the agent's service allowlist. The
token id is stored in agent_task_queue.runtime_connector_handles and
released when the task reaches a terminal state; a reaper entry point
releases stale handles and expired OAuth states.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XqqpKySn2iWtXriK3Gsgak
2026-10-20 17:32:05 +00:00

73 lines
2.2 KiB
Go

package runtimeapps
import (
"encoding/json"
"strings"
)
// ConnectedApp is non-secret task-scoped metadata that tells the daemon which
// external app capabilities were actually mounted for a run.
type ConnectedApp struct {
Provider string `json:"provider"`
ServerName string `json:"server_name"`
ToolkitSlug string `json:"toolkit_slug"`
ToolkitName string `json:"toolkit_name,omitempty"`
// ConnectionName is the provider-side name of the connection the agent
// must pass when calling the provider's tools. Set by providers that allow
// several connections per service (open-connector); empty otherwise.
ConnectionName string `json:"connection_name,omitempty"`
// IsDefault marks the user's default connection of the service.
IsDefault bool `json:"is_default,omitempty"`
}
// MCPOverlayResult carries both the secret-bearing MCP config overlay and the
// non-secret app list used to brief the agent.
type MCPOverlayResult struct {
MCPOverlay json.RawMessage
ConnectedApps []ConnectedApp
// ResourceHandle is an opaque, non-secret, provider-private handle for a
// per-task resource created while building the overlay (open-connector:
// the runtime token id). It is persisted with the queue row and handed
// back to ReleaseTaskResources. Empty when nothing needs releasing.
ResourceHandle string
}
// DisplayNameForToolkitSlug returns a compact human-readable label without
// making a catalog call in the enqueue path. Brand casing here is intentionally
// best-effort; the slug remains the functional identifier in every brief.
func DisplayNameForToolkitSlug(slug string) string {
slug = strings.TrimSpace(slug)
if slug == "" {
return ""
}
switch slug {
case "github":
return "GitHub"
case "gmail":
return "Gmail"
case "linkedin":
return "LinkedIn"
}
words := strings.FieldsFunc(slug, func(r rune) bool {
return r == '_' || r == '-'
})
if len(words) == 0 {
return slug
}
for i, word := range words {
words[i] = titleASCII(word)
}
return strings.Join(words, " ")
}
func titleASCII(s string) string {
if s == "" {
return ""
}
b := []byte(strings.ToLower(s))
if b[0] >= 'a' && b[0] <= 'z' {
b[0] -= 'a' - 'A'
}
return string(b)
}