Conversation
There was a problem hiding this comment.
Pull request overview
Adds first-class workspace agent records and enables selecting an optional agent participant when creating private workspace chats, with UI cues to distinguish agent-backed chats and protections against duplicate chat submissions.
Changes:
- Introduce
WorkspaceAgent+ seeding/repair flows and connect agents to chat participants. - Update chat creation to optionally attach an agent participant and render agent-backed chats distinctly in the shell.
- Add a session-backed submission token to prevent duplicate chat creation, plus feature tests covering agent participation and duplicate submits.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Feature/WorkspaceChatManagementTest.php | Adds chat submission token coverage + blank-name and duplicate-submit tests. |
| tests/Feature/WorkspaceAgentParticipantTest.php | Verifies default agent seeding, agent participant chat creation, and UI rendering. |
| tests/Feature/SurrealWorkspaceModelTest.php | Adds Surreal migration/repair test for legacy key → agent_key workflow. |
| tests/Feature/DesktopShellTest.php | Updates shell assertions to include agent participant UI text. |
| resources/views/welcome.blade.php | Adds agent participant selector, submission token hidden input, and submit-once JS behavior; renders meta labels. |
| database/migrations/2026_03_27_152146_create_workspace_agents_table.php | Creates workspace_agents table (with driver-specific constraints). |
| database/migrations/2026_03_27_152203_add_workspace_agent_id_to_workspace_chat_participants_table.php | Adds nullable workspace_agent_id to chat participants (FK on non-Surreal). |
| database/migrations/2026_03_27_155657_repair_workspace_agents_agent_key_column.php | Upgrade repair migration to add/populate agent_key from legacy key. |
| database/migrations/2026_03_27_160806_drop_legacy_key_column_from_workspace_agents_table.php | Drops legacy key column after repair. |
| database/factories/WorkspaceChatParticipantFactory.php | Adds workspace_agent_id + helper for creating agent participants. |
| database/factories/WorkspaceAgentFactory.php | Adds factory + workspace guide preset. |
| app/Support/Connections/InstanceConnectionManager.php | Ensures default agents exist for active/created workspaces. |
| app/Support/Chats/WorkspaceChatManager.php | Supports optional agent participant and validates blank names / invalid workspace agents. |
| app/Support/Chats/WorkspaceAgentManager.php | New manager to seed and fetch workspace agents. |
| app/Models/WorkspaceChatParticipant.php | Adds workspace_agent_id fillable + agent() relation. |
| app/Models/WorkspaceAgent.php | New model for workspace agents + relations. |
| app/Models/Workspace.php | Adds agents() relation. |
| app/Http/Requests/StoreWorkspaceChatRequest.php | Trims chat name and validates submission token + optional agent id. |
| app/Http/Controllers/HomeController.php | Builds available agent list, sets chat submission token, and adds agent-backed chat rendering metadata. |
| app/Http/Controllers/ChatController.php | Enforces one-time submission token check on chat creation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
database/migrations/2026_03_27_174741_add_has_agent_participant_to_workspace_chats_table.php
Outdated
Show resolved
Hide resolved
database/migrations/2026_03_27_155657_repair_workspace_agents_agent_key_column.php
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $duplicateAgentIds = $existingAgents | ||
| ->get($definition['agent_key']) | ||
| ?->slice(1) | ||
| ->map(fn (WorkspaceAgent $duplicateAgent): int => (int) $duplicateAgent->getKey()) | ||
| ->all() ?? []; | ||
|
|
||
| if ($duplicateAgentIds !== []) { | ||
| $workspace->agents()->whereKey($duplicateAgentIds)->delete(); | ||
| } |
There was a problem hiding this comment.
ensureDefaults() deletes duplicate agents (same agent_key) unconditionally. If any existing workspace_chat_participants.workspace_agent_id rows reference one of the deleted duplicates, those participants will become orphaned (or null via FK nullOnDelete) and the chat will lose its agent association. Before deleting duplicates, re-point any related chat participants to the surviving agent (or skip deletion when duplicates are referenced) so repairs don’t silently drop existing agent-backed chats.
| @@ -36,8 +43,13 @@ public function store( | |||
| $chatManager->createChat($activeWorkspace, $request->user(), $viewerIdentity, [ | |||
| 'name' => $request->validated('chat_name'), | |||
| 'kind' => $request->validated('chat_kind'), | |||
| 'workspace_agent_id' => $request->integer('workspace_agent_id') ?: null, | |||
| ]); | |||
|
|
|||
| if (hash_equals((string) $request->session()->get('chat.create_token'), $submittedToken)) { | |||
| $request->session()->forget('chat.create_token'); | |||
| } | |||
There was a problem hiding this comment.
The submission-token protection isn’t race-safe: the token is validated, then the chat is created, and only afterwards the session token is cleared. Two near-simultaneous POSTs can both pass the initial hash_equals check and create duplicate chats. Consider consuming/invalidating the token before creating the chat (or using an atomic server-side idempotency mechanism such as a DB-backed unique token per user/workspace) so concurrent requests can’t both succeed.
| foreach ($chatIds as $chatId) { | ||
| DB::table('workspace_chats') | ||
| ->where('id', $chatId) |
There was a problem hiding this comment.
This backfill loops and runs one update per chat id inside each chunk. This can become very slow on large datasets. Prefer a single bulk update per chunk (e.g., whereIn('id', $chatIds)), or even a single update using a subquery/join where supported, to avoid N updates.
| foreach ($chatIds as $chatId) { | |
| DB::table('workspace_chats') | |
| ->where('id', $chatId) | |
| if (! empty($chatIds)) { | |
| DB::table('workspace_chats') | |
| ->whereIn('id', $chatIds) |
Summary
Testing
php artisan test --compact tests/Feature/WorkspaceAgentParticipantTest.php tests/Feature/DesktopShellTest.php tests/Feature/WorkspaceChatManagementTest.php tests/Feature/WorkspaceManagementTest.php tests/Feature/InstanceConnectionManagementTest.php tests/Feature/DesktopUiFeatureFlagTest.php tests/Feature/SurrealWorkspaceModelTest.phpvendor/bin/pint --dirty --format agentCloses #135