Skip to content
Open
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
9 changes: 9 additions & 0 deletions PRIMITIVES.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ MCP servers, validated against a toolspec.
- **Execution & MCP plumbing:** `pkg/tools/exec/` (sandboxed CLI execution, with
`remote/` and `fake/`), `pkg/tools/mcp/` (`spec/`, `oauth/`, `probe/`,
`render/`, `trust/`, `validator/`)
- **Browser toolbox:** `pkg/apis/v1alpha1/browsertoolbox_types.go` (a
per-session browser: site allowlist/blocklist, act/readonly interaction,
ephemeral state); `pkg/agent/tool/browser/` synthesizes the observation tools
(navigate/read/find/follow/scroll/back/wait_for, +screenshot) over one
chromedp session; `pkg/tools/browsergate/` is the per-session gate process
(authenticated DevTools + policy-enforced egress) the sandbox pod runs;
`pkg/tools/browserpolicy/` is the shared site-matching decision both the gate
and the tool's own pre-check consult. No dedicated controller yet (Phase A) —
identity-scoped sites and acting tools (click/type) arrive in later phases.
- **Binaries:** `internal/cmd/streamclient/` (claims a streaming ToolCall via
the gateway); tool execution is otherwise driven from `internal/cmd/runner/`

Expand Down
1 change: 1 addition & 0 deletions cmd/oap/internal/installcmd/clean.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ var crGroupVersionResources = []schema.GroupVersionResource{
{Group: "agentprimitives.authzed.com", Version: "v1alpha1", Resource: "useridentities"},
{Group: "agentprimitives.authzed.com", Version: "v1alpha1", Resource: "sessionuseridentities"},
{Group: "agentprimitives.authzed.com", Version: "v1alpha1", Resource: "sidecartoolboxes"},
{Group: "agentprimitives.authzed.com", Version: "v1alpha1", Resource: "browsertoolboxes"},
{Group: "agentprimitives.authzed.com", Version: "v1alpha1", Resource: "clusteragentsettings"},
{Group: "agentprimitives.authzed.com", Version: "v1alpha1", Resource: "agentsettings"},
{Group: "agentprimitives.authzed.com", Version: "v1alpha1", Resource: "skills"},
Expand Down
9 changes: 8 additions & 1 deletion cmd/oap/internal/installcmd/dockerfile_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ import (
// runner and sandbox are intentionally exempt: the operator synthesizes their
// Pods with an explicit numeric runAsUser (1000) that overrides the image USER,
// and their workspace UID story is deliberately 1000. The detector image is
// exempt too (no fixed runAsNonRoot Deployment in the install bundle).
// exempt too (no fixed runAsNonRoot Deployment in the install bundle). The
// browser toolbox image's exemption is purely STRUCTURAL: this test requires
// an explicit --target stage and the browser Dockerfile has exactly one
// runtime stage with no Stage selector, so there is nothing for the stage
// scanner to look up. Its USER is nonetheless numeric (`USER 65532`,
// images/browser/Dockerfile) for the same kubelet reason this test exists —
// the exemption skips the structural requirement, not the invariant.
//
// This is an EXEMPTION set, not an inclusion set: TestServiceImagesUseNumericUser
// iterates apimage.All (the long-lived platform Deployments) and requires a
Expand All @@ -43,6 +49,7 @@ var imagesExemptFromNumericUser = map[string]bool{
apimage.Runner.Target: true,
apimage.Sandbox.Target: true,
apimage.Detector.Target: true,
apimage.Browser.Target: true,
}

var numericUserRe = regexp.MustCompile(`^[0-9]+(:[0-9]+)?$`)
Expand Down
1 change: 1 addition & 0 deletions cmd/oap/internal/toolscmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
// Side-effect imports: each kind's init() registers itself with
// pkg/tools/kinds/registry so the verbs below can dispatch.
"github.com/authzed/openagentprimitives/cmd/oap/internal/apcmd"
_ "github.com/authzed/openagentprimitives/pkg/tools/kinds/browsertoolbox"
_ "github.com/authzed/openagentprimitives/pkg/tools/kinds/mcp"
_ "github.com/authzed/openagentprimitives/pkg/tools/kinds/sandbox"
_ "github.com/authzed/openagentprimitives/pkg/tools/kinds/sidecartoolbox"
Expand Down
21 changes: 21 additions & 0 deletions config/crds/agentprimitives.authzed.com_agentclasses.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,27 @@ spec:
type: object
type: array
x-kubernetes-list-type: atomic
browserToolboxes:
description: |-
BrowserToolboxes references BrowserToolbox CRs in the same namespace
whose per-session browser each session gets. Each ref's Name is the
LLM-facing prefix; pattern [a-z0-9_-]{1,32}.
items:
description: AgentClassBrowserToolboxRef references a BrowserToolbox
CR in the same namespace.
properties:
name:
description: Name is the LLM-visible tool prefix; pattern [a-z0-9_-]{1,32}.
pattern: ^[a-z0-9_-]{1,32}$
type: string
ref:
description: Ref names the BrowserToolbox CR in the same namespace.
type: string
required:
- name
- ref
type: object
type: array
budget:
description: |-
Budget is optional: when omitted, the class inherits the resolved tier
Expand Down
183 changes: 183 additions & 0 deletions config/crds/agentprimitives.authzed.com_agentsessions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2650,6 +2650,189 @@ spec:
format: int32
type: integer
type: object
resolvedBrowserToolboxes:
description: |-
ResolvedBrowserToolboxes snapshots each referenced BrowserToolbox at
session start, frozen for the session lifetime exactly like
ResolvedSidecarToolboxes. Empty when AgentClass.spec.browserToolboxes
is empty.
items:
description: |-
ResolvedBrowserToolbox snapshots one BrowserToolbox CR for a running
AgentSession, mirroring ResolvedSidecarToolbox's shape: the LLM-facing
prefix, the frozen spec, and the operator-allocated pod/gate the runner
uses to reach the per-session browser.
properties:
browserPodIP:
description: |-
BrowserPodIP is the per-session browser pod's IP, reflected once Ready;
the runner reaches its gate at http://<BrowserPodIP>:<GatePort>.
type: string
browserPodName:
description: BrowserPodName is the per-session browser pod's
name.
type: string
effectiveSites:
description: |-
EffectiveSites is the resolved site list the running browser's egress
is actually evaluated against -- in Phase A, a straight carry-through
of Spec.Access.Sites with Source "explicit".
items:
description: |-
BrowserEffectiveSite is one resolved site entry in a ResolvedBrowserToolbox's
EffectiveSites. Source records where the entry came from: Phase A only ever
writes "explicit" (a BrowserAccess.Sites entry carried through verbatim);
"identity" is a Phase B addition once a bound identity can contribute its
own sites to the effective set.
properties:
exact:
description: Exact mirrors BrowserSite.Exact for this
entry.
type: boolean
host:
description: Host is the resolved hostname, matched exactly
as BrowserSite.Host.
type: string
source:
description: |-
Source is where this entry came from. "explicit" only in Phase A;
"identity" arrives in Phase B.
type: string
required:
- host
- source
type: object
type: array
gatePort:
description: |-
GatePort is the operator-allocated port the runner uses to reach the
browser pod's gate process.
format: int32
type: integer
name:
description: |-
Name is the LLM-facing prefix (the `name` field from
AgentClassBrowserToolboxRef).
type: string
podFailure:
description: |-
PodFailure records a TERMINAL container state on the browser pod
(CrashLoopBackOff, ImagePullBackOff) -- reuses SidecarPodFailure's shape
exactly; see that type's doc comment for why it carries no timestamp.
properties:
exitCode:
description: |-
ExitCode is the container's last terminated exit code. Nil when the
container never ran (e.g. the image could not be pulled).
format: int32
type: integer
message:
description: |-
Message is the most actionable text available: the container's last
terminated message when present — the crashing container's log tail,
thanks to the pod's FallbackToLogsOnError policy — otherwise the
kubelet's waiting message. Preferring the log tail is what makes the
field worth surfacing; the waiting message alone is content-free for a
crash loop ("back-off Ns restarting failed container=…").
type: string
reason:
description: |-
Reason is the kubelet's terminal container Waiting reason, e.g.
"CrashLoopBackOff" or "ImagePullBackOff".
type: string
required:
- reason
type: object
ref:
description: |-
Ref is the BrowserToolbox CR name (the `ref` field from
AgentClassBrowserToolboxRef).
type: string
spec:
description: Spec is the BrowserToolbox spec snapshot at session-start
time.
properties:
access:
description: |-
Access is the site allowlist/blocklist the browser's egress is
evaluated against.
properties:
mode:
description: |-
Mode is "allowlist" (only Sites are reachable) or "blocklist" (every
site except Sites is reachable).
enum:
- allowlist
- blocklist
type: string
sites:
description: |-
Sites is the set of hosts Mode interprets. Matching is suffix-by-label
unless Exact is set: a Site with Host "example.com" also matches
"www.example.com" and "a.b.example.com", but never a lookalike like
"notexample.com" -- pkg/tools/browserpolicy.Match does label-boundary
comparison, not a raw string suffix check. Exact pins the entry to the
literal host only.
items:
description: BrowserSite is one host entry in a BrowserAccess
list.
properties:
exact:
description: |-
Exact pins this entry to the literal Host only, disabling the default
suffix-by-label subdomain match.
type: boolean
host:
description: |-
Host is the DNS hostname this entry covers, matched suffix-by-label
(see BrowserAccess.Sites) unless Exact is set.
maxLength: 253
pattern: ^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?)*$
type: string
required:
- host
type: object
type: array
x-kubernetes-list-map-keys:
- host
x-kubernetes-list-type: map
required:
- mode
type: object
interaction:
default: act
description: |-
Interaction gates whether the LLM may act on the page (click, type,
navigate) or only read its rendered content.
act -- full interaction.
readonly -- observation only; no mutating actions are dispatched.
enum:
- act
- readonly
type: string
state:
default: ephemeral
description: |-
State selects how the browser's session state (cookies, storage) is
retained across turns.
Phase A: ephemeral is the only state; persistent arrives with identities (Phase C).
enum:
- ephemeral
type: string
required:
- access
type: object
tokenHash:
description: |-
TokenHash is a hash of the resolved per-session gate token; a change
triggers pod replacement.
type: string
required:
- name
- ref
- spec
type: object
type: array
resolvedContentGuardDetectors:
description: |-
ResolvedContentGuardDetectors snapshots the detector sidecars injected for
Expand Down
Loading
Loading