+ Agent Memory vs Workspace Memory:{' '}
+ This tab edits only this agent's private working memory (a scratchpad stored in the database).
+ To browse or edit all workspace memory files (daily logs, knowledge base, MEMORY.md, etc.), visit the{' '}
+ Memory Browser page.
+
+
{/* Memory Content */}
+
+
)}
diff --git a/src/components/panels/agent-squad-panel-phase3.tsx b/src/components/panels/agent-squad-panel-phase3.tsx
index 97b827c..9dad847 100644
--- a/src/components/panels/agent-squad-panel-phase3.tsx
+++ b/src/components/panels/agent-squad-panel-phase3.tsx
@@ -96,7 +96,14 @@ export function AgentSquadPanelPhase3() {
setSyncToast(null)
try {
const response = await fetch('/api/agents/sync', { method: 'POST' })
+ if (response.status === 401) {
+ window.location.assign('/login?next=%2Fagents')
+ return
+ }
const data = await response.json()
+ if (response.status === 403) {
+ throw new Error('Admin access required for agent sync')
+ }
if (!response.ok) throw new Error(data.error || 'Sync failed')
setSyncToast(`Synced ${data.synced} agents (${data.created} new, ${data.updated} updated)`)
fetchAgents()
@@ -116,7 +123,17 @@ export function AgentSquadPanelPhase3() {
if (agents.length === 0) setLoading(true)
const response = await fetch('/api/agents')
- if (!response.ok) throw new Error('Failed to fetch agents')
+ if (response.status === 401) {
+ window.location.assign('/login?next=%2Fagents')
+ return
+ }
+ if (response.status === 403) {
+ throw new Error('Access denied')
+ }
+ if (!response.ok) {
+ const data = await response.json().catch(() => ({}))
+ throw new Error(data.error || 'Failed to fetch agents')
+ }
const data = await response.json()
setAgents(data.agents || [])
diff --git a/src/components/panels/memory-browser-panel.tsx b/src/components/panels/memory-browser-panel.tsx
index 61c9229..48022b5 100644
--- a/src/components/panels/memory-browser-panel.tsx
+++ b/src/components/panels/memory-browser-panel.tsx
@@ -47,7 +47,7 @@ export function MemoryBrowserPanel() {
setMemoryFiles(data.tree || [])
// Auto-expand some common directories
- setExpandedFolders(new Set(['daily', 'knowledge']))
+ setExpandedFolders(new Set(['daily', 'knowledge', 'memory', 'knowledge-base']))
} catch (error) {
log.error('Failed to load file tree:', error)
} finally {
@@ -61,15 +61,14 @@ export function MemoryBrowserPanel() {
const getFilteredFiles = () => {
if (activeTab === 'all') return memoryFiles
-
- return memoryFiles.filter(file => {
- if (activeTab === 'daily') {
- return file.name === 'daily' || file.path.includes('daily/')
- }
- if (activeTab === 'knowledge') {
- return file.name === 'knowledge' || file.path.includes('knowledge/')
- }
- return true
+
+ const tabPrefixes = activeTab === 'daily'
+ ? ['daily/', 'memory/']
+ : ['knowledge/', 'knowledge-base/']
+
+ return memoryFiles.filter((file) => {
+ const normalizedPath = `${file.path.replace(/\\/g, '/')}/`
+ return tabPrefixes.some((prefix) => normalizedPath.startsWith(prefix))
})
}
@@ -731,6 +730,8 @@ function CreateFileModal({
onChange={(e) => setFilePath(e.target.value)}
className="w-full px-3 py-2 bg-surface-1 border border-border rounded-md text-foreground focus:outline-none focus:ring-1 focus:ring-primary/50"
>
+
+
diff --git a/src/components/panels/office-panel.tsx b/src/components/panels/office-panel.tsx
index 521301b..ed0ada5 100644
--- a/src/components/panels/office-panel.tsx
+++ b/src/components/panels/office-panel.tsx
@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback, useMemo } from 'react'
import { useMissionControl, Agent } from '@/store'
type ViewMode = 'office' | 'org-chart'
+type OrgSegmentMode = 'category' | 'role' | 'status'
interface Desk {
agent: Agent
@@ -75,6 +76,7 @@ export function OfficePanel() {
const { agents } = useMissionControl()
const [localAgents, setLocalAgents] = useState([])
const [viewMode, setViewMode] = useState('office')
+ const [orgSegmentMode, setOrgSegmentMode] = useState('category')
const [selectedAgent, setSelectedAgent] = useState(null)
const [loading, setLoading] = useState(true)
@@ -123,6 +125,64 @@ export function OfficePanel() {
return groups
}, [displayAgents])
+ const categoryGroups = useMemo(() => {
+ const groups = new Map()
+ const getCategory = (agent: Agent): string => {
+ const name = (agent.name || '').toLowerCase()
+ if (name.startsWith('habi-')) return 'Habi Lanes'
+ if (name.startsWith('ops-')) return 'Ops Automation'
+ if (name.includes('canary')) return 'Canary'
+ if (name.startsWith('main')) return 'Core'
+ if (name.startsWith('remote-')) return 'Remote'
+ return 'Other'
+ }
+
+ for (const a of displayAgents) {
+ const category = getCategory(a)
+ if (!groups.has(category)) groups.set(category, [])
+ groups.get(category)!.push(a)
+ }
+
+ const order = ['Habi Lanes', 'Ops Automation', 'Core', 'Canary', 'Remote', 'Other']
+ return new Map(
+ [...groups.entries()].sort(([a], [b]) => {
+ const ai = order.indexOf(a)
+ const bi = order.indexOf(b)
+ const av = ai === -1 ? Number.MAX_SAFE_INTEGER : ai
+ const bv = bi === -1 ? Number.MAX_SAFE_INTEGER : bi
+ if (av !== bv) return av - bv
+ return a.localeCompare(b)
+ })
+ )
+ }, [displayAgents])
+
+ const statusGroups = useMemo(() => {
+ const groups = new Map()
+ for (const a of displayAgents) {
+ const key = statusLabel[a.status] || a.status
+ if (!groups.has(key)) groups.set(key, [])
+ groups.get(key)!.push(a)
+ }
+
+ const order = ['Working', 'Available', 'Error', 'Away']
+ return new Map(
+ [...groups.entries()].sort(([a], [b]) => {
+ const ai = order.indexOf(a)
+ const bi = order.indexOf(b)
+ const av = ai === -1 ? Number.MAX_SAFE_INTEGER : ai
+ const bv = bi === -1 ? Number.MAX_SAFE_INTEGER : bi
+ if (av !== bv) return av - bv
+ return a.localeCompare(b)
+ })
+ )
+ }, [displayAgents])
+
+ const orgGroups = useMemo(() => {
+ if (orgSegmentMode === 'role') return roleGroups
+ if (orgSegmentMode === 'status') return statusGroups
+ return categoryGroups
+ }, [categoryGroups, orgSegmentMode, roleGroups, statusGroups])
+
if (loading && displayAgents.length === 0) {
return (
@@ -237,11 +297,40 @@ export function OfficePanel() {
+ Workspace Management:{' '}
+ To create or manage workspaces (tenant instances), go to the{' '}
+ {' '}
+ panel under Admin > Super Admin in the sidebar. From there you can create new client instances, manage tenants, and monitor provisioning jobs.
+
+ )}
+
{/* Feedback */}
{feedback && (
-
+
+
+
+
@@ -452,17 +460,21 @@ export function SuperAdminPanel() {
)}
-
-
- {createExpanded && (
+ {createExpanded && (
+
+
+
Create New Workspace
+
+
- Add a new workspace/client instance here. Fill the form below and click Create + Queue.
+ Fill in the workspace details below and click Create + Queue to provision a new client instance.
{gatewayLoadError && (
@@ -540,8 +552,8 @@ export function SuperAdminPanel() {