Skip to content

feat: custom agents (bring-your-own-agent)#96

Merged
Wintersta7e merged 18 commits into
mainfrom
feat/custom-agents
Jun 24, 2026
Merged

feat: custom agents (bring-your-own-agent)#96
Wintersta7e merged 18 commits into
mainfrom
feat/custom-agents

Conversation

@Wintersta7e

Copy link
Copy Markdown
Owner

Summary

Adds custom agents (bring-your-own-agent): register your own CLI agents (e.g. Ollama, a local model runner, a personal wrapper) alongside the seven built-ins, so the deck isn't limited to a fixed list.

  • Define them in the Agents screen (add / edit / clone / delete) or by hand-editing <userData>/agents.toml — each is a binary + default args + non-secret env + display metadata (name, icon, colour, context window).
  • First-class: they appear in the new-session picker, command palette, per-project default, and workflow nodes, and launch in both terminal sessions and workflow runs.
  • Safe by construction: binary/args/env are validated (charset, length, blocked + credential-shaped env keys) and shell-quoted; env reaches the child via the process env, never a shell string. The strict BuiltinAgentId union is preserved (built-in maps stay exhaustive via satisfies); only the runtime registry boundary accepts arbitrary ids, gated by registry.has.

Notes

  • Phase 1: console-command agents. Secrets and a local-model endpoint redirect (point an agent at a local OpenAI-compatible server) are deferred to a follow-up via safeStorage.
  • Workflow nodes run custom agents best-effort (raw output, no injected --print) — a console agent has no headless mode; this is surfaced in the node UI.
  • Verified end-to-end against real agents (registered → resolved → launched via the WSL path).
  • Full suite green (~1474 tests); lint / typecheck / format / build clean. Bumps the app to 6.11.0.

Rename AgentId->BuiltinAgentId (strict union) and add
AgentId = BuiltinAgentId | (string & {}) for the runtime boundary;
rename isAgentId->isBuiltinAgent. Foundation for user-defined agents.

Co-Authored-By: Rooty
DETECTORS/READERS key on BuiltinAgentId so widening AgentId can't
degrade them to index signatures; resolveActiveModel narrows with
isBuiltinAgent before indexing. Custom agents -> null model.

Co-Authored-By: Rooty
Pure, IO-free CustomAgentSpec type + validateCustomAgent gate:
id/binary charset, bounds, curated colour tokens, BLOCKED_ENV +
credential-key rejection. Reused by main (load/save) and renderer (form).

Co-Authored-By: Rooty
AgentRegistry reads agents.toml, validates + merges with the 7
builtins, exposes has/byId/binaryFor/argsFor/envFor/knownIds.
Bad entries skipped with warnings; redacted wire descriptors omit env.

Co-Authored-By: Rooty
Add LD_AUDIT/LD_PROFILE/BASH_ENV/ENV alongside the existing hijack
vars; document the denylist as best-effort defense-in-depth (the
trust boundary is the user's own local config).

Co-Authored-By: Rooty
saveCustom/deleteCustom validate, persist via atomicWrite + TOML
stringify, then reload. Round-trips args/env/ui; rejects invalid +
builtin-shadow without writing.

Co-Authored-By: Rooty
Live AgentRegistry in main (load at startup), reachable via
agents:getRegistry/saveCustom/deleteCustom + a registryChange push.
Move AgentDescriptorWire to shared so the bridge can type it.

Co-Authored-By: Rooty
Custom agents now pass spawn/visibility/effective-context gates via
registry.has, with their own context window folded into the lookup;
env snapshot returns a neutral blank for non-builtins. Auto-updater
stays builtin-only.

Co-Authored-By: Rooty
pty-manager resolves custom binary/args via the registry (each
shellQuoted) and merges non-secret env into the child process env,
never a shell string. Builtin launch behavior unchanged.

Co-Authored-By: Rooty
node-runners resolves custom binary/args from the registry (no
--print, shell-quoted path probe); validateWorkflow takes an optional
knownAgentIds set so the engine accepts custom ids while existing
callers keep the builtin default.

Co-Authored-By: Rooty
Zustand slice bootstraps the merged registry over IPC and refreshes on
registryChange (templates-slice pattern); selectAgentMeta resolves
display metadata with a degraded fallback for dangling/custom ids.

Co-Authored-By: Rooty
Adds a Custom Agents section to the Agents screen with add/edit/clone/
delete, and a focus-trapped modal (essentials/appearance/advanced)
with live validateCustomAgent + curated colour swatches + env-secret
warnings. Delete routes through ConfirmDialog.

Co-Authored-By: Rooty
New-session picker, project-default dropdown, command palette,
AgentSelector and the workflow node editor now enumerate the merged
registry (visibleAgents preserved); custom workflow nodes show a
best-effort hint.

Co-Authored-By: Rooty
Wire selectAgentMeta(useAgentRegistry(), id) into ~18 session/home/
history/workflow display components so custom agents show their real
name/icon/short/colour (not raw id + --accent); fix AgentChipB1
render-nothing, ProjectCardB1 filter-out, EnvTab snapshot skip, and the
canvas node dropdown; flag deleted agents as 'no longer registered'.
Seed the renderer registry with builtin descriptors so builtins resolve
pre-IPC-pull.

Co-Authored-By: Rooty
- workflow runner now passes custom-agent env via the child env option
- renameWorkflow threads the merged agent id set (no false Unknown agent)
- non-lossy edit/clone via agents:getCustomSpec (args/env pre-filled)
- ipc-pty consumes the shared BLOCKED_ENV_KEYS (closes denylist drift)
- agents.toml parse warnings surface as a notification
- dangling-agent spawn shows a visible 'no longer registered' notice

Co-Authored-By: Rooty
DRY the builtin->descriptor mapping (toBuiltinDescriptor), add a
useAgentMeta(id) hook for single-id display sites, extract the
effective-context defaults closure, drop dead agentShort(). Behavior
and security posture unchanged; tests green.

Co-Authored-By: Rooty
@Wintersta7e Wintersta7e merged commit 8848ed8 into main Jun 24, 2026
8 checks passed
@Wintersta7e Wintersta7e deleted the feat/custom-agents branch June 24, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant