Skip to content

Add project agents from the selector#396

Open
pascalandr wants to merge 3 commits into
NeuralNomadsAI:devfrom
Pagecran:fix/dynamic-agent-selector
Open

Add project agents from the selector#396
pascalandr wants to merge 3 commits into
NeuralNomadsAI:devfrom
Pagecran:fix/dynamic-agent-selector

Conversation

@pascalandr
Copy link
Copy Markdown
Contributor

@pascalandr pascalandr commented May 7, 2026

Summary

  • Adds an inline project-agent form beside the existing agent selector.
  • Creates real OpenCode workspace agent files at .opencode/agents/<name>.md so new entries persist with the opened project.
  • Refreshes the OpenCode agent list and selects the new agent only after OpenCode reports it back.

Scope

This is intentionally a minimal starting point for discussion around #120:

  • Workspace/project agents only for now.
  • Creation only for now.
  • No built-in edit/delete flow in this PR; users can still edit the generated .opencode/agents/<name>.md file through the workspace files.

Follow-up Questions

  • Should CodeNomad add an explicit agent-editing flow after creation?
  • Should users be able to create local/global user agents in addition to workspace agents?
  • Should the creation flow offer more advanced templates or presets?

Testing

  • npm run typecheck --workspace @codenomad/ui
  • npm run typecheck --workspace @neuralnomads/codenomad
  • npm run build --workspace @codenomad/ui

Fixes #120

@pascalandr pascalandr requested a review from a team May 7, 2026 06:38
@pascalandr
Copy link
Copy Markdown
Contributor Author

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented May 7, 2026

Gatekeeper review for latest PR state (837b589 vs origin/dev). Request understood as: review for regressions and implementation quality, not make changes.

Verdict: do not merge yet. The feature direction is reasonable, but there are correctness and UX/a11y risks that should be fixed before this lands.

Findings:

  1. Blocker: the UI can report/select an agent that OpenCode never loaded.
    packages/ui/src/components/agent-selector.tsx:117-120 writes the file, then awaits fetchAgents, then unconditionally upsertAgents and selects the new agent. But fetchAgents catches and swallows failures in packages/ui/src/stores/session-api.ts:524-555, so this path succeeds even if the OpenCode SDK call fails, the generated file is rejected, or the agents list remains stale. After that, messages can be sent with session.agent set to a name OpenCode may not know. Make the refresh authoritative: return/throw from fetchAgents, verify the created agent appears in the fetched list, and only then select it. Avoid local upsertAgent as a success path unless it is strictly optimistic and rolled back on failure.

  2. Major: file creation is non-atomic and can overwrite a concurrently-created agent.
    packages/ui/src/components/agent-selector.tsx:109-117 does a read-before-write existence check, while packages/server/src/filesystem/browser.ts:88-90 always overwrites with fs.writeFileSync. Two UI windows, two bot runs, or a manual file creation between the GET and PUT can lose data. This should be a server-side create operation using no-overwrite semantics (wx) and returning 409 for existing files, or the existing write endpoint should support an explicit create-only mode.

  3. Major: embedding a multi-field form inside a Kobalte Select popup is a fragile accessibility/interaction pattern.
    packages/ui/src/components/agent-selector.tsx:185-227 puts inputs, textarea, and a button inside Select.Content next to Select.Listbox, then suppresses pointer/key events to keep it usable. A select/listbox popup is expected to contain selectable options, not a form; this risks broken keyboard navigation, focus management, screen-reader semantics, and typeahead behavior. A safer implementation is a separate “Add project agent” button that opens a dialog/popover, then refreshes/selects the agent after successful creation.

Better implementation direction:

  • Add a small server-side createWorkspaceAgent endpoint that validates the agent name, writes .opencode/agents/<name>.md atomically, and returns a clear conflict/error response.
  • Let the UI call that endpoint, refresh agents, verify the new agent is present, and select only after verification.
  • Keep the selector as a selector; launch creation from a dedicated dialog or inline panel outside the select listbox semantics.

Verification:

  • npm run typecheck --workspace @codenomad/ui could not complete in this checkout because dependencies/types are missing (solid-js, debug, marked, @opencode-ai/sdk, etc.).
  • npm run typecheck --workspace @neuralnomads/codenomad could not complete because @types/node is missing.
  • npm run build --workspace @codenomad/ui could not complete because vite is missing.

Residual note: the worktree already had an unrelated modified .opencode/package-lock.json; I left it untouched.

--
Yours,
CodeNomadBot

@pascalandr pascalandr changed the base branch from main to dev May 7, 2026 08:03
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/25483719347

Artifacts expire in 7 days.
Artifacts: (none found on this run)

@pascalandr pascalandr force-pushed the fix/dynamic-agent-selector branch from 3b93154 to 22371b1 Compare May 7, 2026 08:09
@pascalandr
Copy link
Copy Markdown
Contributor Author

@CodeNomadBot

Same review please.

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented May 7, 2026

Gatekeeper review for latest PR state (22371b1 vs origin/dev). Request understood as: repeat the strict regression/implementation-quality review after the PR updates, not make code changes.

Verdict: do not merge yet. The previous atomic-create and “select only after OpenCode sees it” issues are materially improved, and the select-popup form concern was addressed by moving the form outside Select.Content. One new regression remains from the fix.

Findings:

  1. Major: fetchAgents now throws for all callers, which can abort unrelated instance hydration.
    packages/ui/src/stores/session-api.ts:527-557 changed fetchAgents from logging/swallowing errors to throwing. That is needed for agent creation verification, but this function is also used by initial hydration in packages/ui/src/stores/instances.ts:256-259. If agent loading fails, hydrateInstanceData now skips fetchProviders, ensureInstanceConfigLoaded, fetchCommands, pending permissions, and pending questions. A malformed agent file or transient /app.agents failure can therefore break much more of the workspace UI than just the agent selector. Keep the authoritative behavior for creation, but don’t change the global semantics for every caller. For example, add an option like fetchAgents(instanceId, { throwOnError: true }) for the creation path, or create a dedicated refreshAgentsStrict helper.

Resolved from previous review:

  • The UI no longer locally upserts/selects an agent that OpenCode did not report back. packages/ui/src/components/agent-selector.tsx:111-115 now verifies the refreshed list before selection.
  • Agent file creation is now server-side and uses wx/no-overwrite semantics via packages/server/src/filesystem/browser.ts:84-90, packages/server/src/workspaces/manager.ts:92-96, and packages/server/src/server/routes/workspaces.ts:144-159.
  • The form is no longer embedded inside the Kobalte Select listbox popup; it is rendered beside/below the selector in packages/ui/src/components/agent-selector.tsx:194-227.

Implementation notes:

  • The create endpoint shape is appropriately tight for this PR: validated agent name, fixed .opencode/agents/<name>.md path, atomic conflict handling.
  • The UI still leaves a created-but-not-loaded file on disk if OpenCode rejects or delays loading it. That is acceptable for this minimal scope if the product is comfortable with the user editing/removing the file manually, but the toast should make that recovery path clear in a future polish pass.

Verification:

  • npm run typecheck --workspace @codenomad/ui could not complete in this checkout because dependencies/types are missing (solid-js, @opencode-ai/sdk, @tauri-apps/*, etc.).
  • npm run typecheck --workspace @neuralnomads/codenomad could not complete because @types/node is missing.
  • npm run build --workspace @codenomad/ui could not complete because vite is missing.

No unrelated local worktree changes were present this run.

--
Yours,
CodeNomadBot

@shantur
Copy link
Copy Markdown
Collaborator

shantur commented May 7, 2026

@pascalandr - I think we need to discuss this feature in detail.

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.

[Feature] support to add a new agent dynamically

2 participants