rename mcp internals to space

This commit is contained in:
Prasanna A P 2026-07-29 22:00:16 -07:00
parent 8a9fcd52dc
commit 30e1242bc5
25 changed files with 132 additions and 143 deletions

View file

@ -136,7 +136,7 @@ discovery and rejection tests still run.
## Storage And Rollout
`WorkspaceState` stores only the active space's container tag. It never stores bearer
`SpaceState` stores only the active space's container tag. It never stores bearer
tokens, MCP client identity, or protocol messages.
The old `SupermemoryMCP` class and binding remain inert for one rollout. This

View file

@ -7,7 +7,7 @@ import { validateOAuthToken, type AuthUser } from "./auth"
import { SupermemoryMCP } from "./legacy-protocol-state"
import { createSupermemoryServer } from "./server"
import type { ActorContext, ServerEnv } from "./types"
import { WorkspaceState } from "./workspace-state"
import { SpaceState } from "./space-state"
type Bindings = ServerEnv
@ -205,7 +205,7 @@ app.all("/", (c) => handleMcpRequest(c, "/mcp"))
app.all("/mcp", (c) => handleMcpRequest(c))
app.all("/mcp/", (c) => handleMcpRequest(c, "/mcp"))
export { SupermemoryMCP, WorkspaceState }
export { SpaceState, SupermemoryMCP }
export type { ActorContext, ServerEnv }
export default app

View file

@ -3,14 +3,14 @@ import { DEFAULT_PROJECT_ID, type SupermemoryClient } from "../client"
import {
compactDescription,
formatFactSection,
formatWorkspaceRow,
sortWorkspaces,
workspaceDisplayName,
workspaceMetadata,
} from "../workspace-presentation"
formatSpaceRow,
sortSpaces,
spaceDisplayName,
spaceMetadata,
} from "../space-presentation"
const CONTEXT_FACT_LIMIT = 8
const RECENT_WORKSPACE_LIMIT = 3
const RECENT_SPACE_LIMIT = 3
export function registerContextPrompt(
server: McpServer,
@ -26,24 +26,24 @@ export function registerContextPrompt(
try {
const selectedTag = await resolveContainerTag()
const activeKey = selectedTag ?? DEFAULT_PROJECT_ID
const [profileResult, workspaces] = await Promise.all([
const [profileResult, spaces] = await Promise.all([
getClient(activeKey).getProfile(),
getClient().listContainerTags(),
])
const activeWorkspace = workspaces.find(
(workspace) => workspace.containerTag === activeKey,
const activeSpace = spaces.find(
(space) => space.containerTag === activeKey,
)
const activeLabel = workspaceDisplayName(activeWorkspace, activeKey)
const activeLabel = spaceDisplayName(activeSpace, activeKey)
const fallback = selectedTag ? "" : " (default)"
const parts: string[] = [
"# Supermemory Context",
`Active space: ${activeLabel} [${activeKey}]${fallback}`,
]
if (activeWorkspace) {
const metadata = workspaceMetadata(activeWorkspace)
if (activeSpace) {
const metadata = spaceMetadata(activeSpace)
if (metadata) parts.push(metadata)
const description = compactDescription(activeWorkspace.description)
const description = compactDescription(activeSpace.description)
if (description) parts.push(description)
}
@ -68,15 +68,15 @@ export function registerContextPrompt(
parts.push("No profile facts are available for this space yet.")
}
const recentWorkspaces = sortWorkspaces(workspaces, activeKey)
.filter((workspace) => workspace.containerTag !== activeKey)
.slice(0, RECENT_WORKSPACE_LIMIT)
if (recentWorkspaces.length > 0) {
const recentSpaces = sortSpaces(spaces, activeKey)
.filter((space) => space.containerTag !== activeKey)
.slice(0, RECENT_SPACE_LIMIT)
if (recentSpaces.length > 0) {
parts.push(
"",
"## Recently Active Spaces",
...recentWorkspaces.map((workspace) =>
formatWorkspaceRow(workspace, activeKey, 100),
...recentSpaces.map((space) =>
formatSpaceRow(space, activeKey, 100),
),
)
}

View file

@ -1,10 +1,10 @@
import type { McpServer } from "@modelcontextprotocol/server"
import { DEFAULT_PROJECT_ID, type SupermemoryClient } from "../client"
import {
formatWorkspaceRow,
sortWorkspaces,
workspaceDisplayName,
} from "../workspace-presentation"
formatSpaceRow,
sortSpaces,
spaceDisplayName,
} from "../space-presentation"
export function registerContainerTagsResource(
server: McpServer,
@ -18,13 +18,13 @@ export function registerContainerTagsResource(
resolveContainerTag(),
])
const activeKey = selectedTag ?? DEFAULT_PROJECT_ID
const activeWorkspace = containerTags.find(
(workspace) => workspace.containerTag === activeKey,
const activeSpace = containerTags.find(
(space) => space.containerTag === activeKey,
)
const rows = sortWorkspaces(containerTags, activeKey).map((workspace) =>
formatWorkspaceRow(workspace, activeKey),
const rows = sortSpaces(containerTags, activeKey).map((space) =>
formatSpaceRow(space, activeKey),
)
const activeLabel = workspaceDisplayName(activeWorkspace, activeKey)
const activeLabel = spaceDisplayName(activeSpace, activeKey)
const fallback = selectedTag ? "" : " (default)"
const text = [
"# My Spaces",

View file

@ -3,9 +3,9 @@ import { DEFAULT_PROJECT_ID, type SupermemoryClient } from "../client"
import {
compactDescription,
formatFactSection,
workspaceDisplayName,
workspaceMetadata,
} from "../workspace-presentation"
spaceDisplayName,
spaceMetadata,
} from "../space-presentation"
const PROFILE_FACT_LIMIT = 12
@ -21,24 +21,24 @@ export function registerProfileResource(
async () => {
const selectedTag = await resolveContainerTag()
const activeKey = selectedTag ?? DEFAULT_PROJECT_ID
const [profileResult, workspaces] = await Promise.all([
const [profileResult, spaces] = await Promise.all([
getClient(activeKey).getProfile(),
getClient().listContainerTags(),
])
const activeWorkspace = workspaces.find(
(workspace) => workspace.containerTag === activeKey,
const activeSpace = spaces.find(
(space) => space.containerTag === activeKey,
)
const activeLabel = workspaceDisplayName(activeWorkspace, activeKey)
const activeLabel = spaceDisplayName(activeSpace, activeKey)
const fallback = selectedTag ? "" : " (default)"
const parts: string[] = [
"# Active Space Profile",
`Space: ${activeLabel} [${activeKey}]${fallback}`,
]
if (activeWorkspace) {
const metadata = workspaceMetadata(activeWorkspace)
if (activeSpace) {
const metadata = spaceMetadata(activeSpace)
if (metadata) parts.push(metadata)
const description = compactDescription(activeWorkspace.description)
const description = compactDescription(activeSpace.description)
if (description) parts.push(description)
}

View file

@ -18,9 +18,9 @@ import { registerAllTools } from "./tools"
import { errorResult } from "./tools/types"
import type { ActorContext, ServerEnv } from "./types"
import {
resolveContainerTag as resolveWorkspaceContainerTag,
workspaceStateName,
} from "./workspace"
resolveContainerTag as resolveSpaceContainerTag,
spaceStateName,
} from "./space"
const DEFAULT_API_URL = "https://api.supermemory.ai"
@ -53,17 +53,15 @@ export function createSupermemoryServer(
version: "1.0.0",
})
const apiUrl = env.API_URL || DEFAULT_API_URL
const workspaceState = env.WORKSPACE_STATE.getByName(
workspaceStateName(actor),
)
const spaceState = env.SPACE_STATE.getByName(spaceStateName(actor))
const getClient = (containerTag?: string) =>
new SupermemoryClient(actor.bearerToken, containerTag, apiUrl)
const getActiveContainerTag = () => workspaceState.getActiveContainerTag()
const getActiveContainerTag = () => spaceState.getActiveContainerTag()
const setActiveContainerTag = (containerTag: string) =>
workspaceState.setActiveContainerTag(containerTag)
spaceState.setActiveContainerTag(containerTag)
const resolveContainerTag = (explicit?: string) =>
resolveWorkspaceContainerTag(explicit, getActiveContainerTag)
resolveSpaceContainerTag(explicit, getActiveContainerTag)
const analytics = createPosthogAnalytics(env, actor, waitUntil)
const toolServer = createTrackedToolServer(
server,

View file

@ -3,9 +3,9 @@ import type { ContainerTag } from "../shared/types"
import {
compactDescription,
formatFactSection,
formatWorkspaceRow,
sortWorkspaces,
} from "./workspace-presentation"
formatSpaceRow,
sortSpaces,
} from "./space-presentation"
const space = (
containerTag: string,
@ -27,7 +27,7 @@ const space = (
describe("space presentation", () => {
it("keeps the active space first, then sorts by activity", () => {
const sorted = sortWorkspaces(
const sorted = sortSpaces(
[
space("older", "2026-01-01T00:00:00.000Z"),
space("active", "2025-01-01T00:00:00.000Z"),
@ -44,7 +44,7 @@ describe("space presentation", () => {
})
it("formats compact rows without internal database IDs", () => {
const row = formatWorkspaceRow(
const row = formatSpaceRow(
space("project-key", "2026-07-29T19:44:28.177Z"),
"project-key",
)

View file

@ -5,11 +5,11 @@ const DEFAULT_DESCRIPTION_LIMIT = 160
const plural = (count: number, singular: string, pluralForm: string) =>
`${count} ${count === 1 ? singular : pluralForm}`
export function workspaceDisplayName(
workspace: ContainerTag | undefined,
export function spaceDisplayName(
space: ContainerTag | undefined,
key: string,
): string {
return workspace?.name || key
return space?.name || key
}
export function compactDescription(
@ -39,43 +39,40 @@ export function formatActivityDate(value: string | null): string | undefined {
}).format(date)
}
export function workspaceMetadata(workspace: ContainerTag): string {
const lastActivity = formatActivityDate(workspace.lastActivityAt)
export function spaceMetadata(space: ContainerTag): string {
const lastActivity = formatActivityDate(space.lastActivityAt)
const fields = [
workspace.visibility
? `${workspace.visibility.charAt(0).toUpperCase()}${workspace.visibility.slice(1)}`
space.visibility
? `${space.visibility.charAt(0).toUpperCase()}${space.visibility.slice(1)}`
: undefined,
plural(workspace.documentCount, "document", "documents"),
plural(workspace.memoryCount, "memory", "memories"),
plural(space.documentCount, "document", "documents"),
plural(space.memoryCount, "memory", "memories"),
lastActivity ? `Last active ${lastActivity}` : undefined,
]
return fields.filter(Boolean).join(" · ")
}
export function formatWorkspaceRow(
workspace: ContainerTag,
export function formatSpaceRow(
space: ContainerTag,
activeKey: string,
descriptionLimit = DEFAULT_DESCRIPTION_LIMIT,
): string {
const active = workspace.containerTag === activeKey ? " · Active" : ""
const metadata = workspaceMetadata(workspace)
const description = compactDescription(
workspace.description,
descriptionLimit,
)
const active = space.containerTag === activeKey ? " · Active" : ""
const metadata = spaceMetadata(space)
const description = compactDescription(space.description, descriptionLimit)
const firstLine =
`- ${workspaceDisplayName(workspace, workspace.containerTag)} ` +
`[${workspace.containerTag}]${active}${metadata ? ` · ${metadata}` : ""}`
`- ${spaceDisplayName(space, space.containerTag)} ` +
`[${space.containerTag}]${active}${metadata ? ` · ${metadata}` : ""}`
return description ? `${firstLine}\n ${description}` : firstLine
}
export function sortWorkspaces(
workspaces: ContainerTag[],
export function sortSpaces(
spaces: ContainerTag[],
activeKey: string,
): ContainerTag[] {
return [...workspaces].sort((left, right) => {
return [...spaces].sort((left, right) => {
if (left.containerTag === activeKey) return -1
if (right.containerTag === activeKey) return 1

View file

@ -3,7 +3,7 @@ import { containerTagSchema } from "./container-tag"
const ACTIVE_CONTAINER_TAG_KEY = "activeContainerTag"
export class WorkspaceState extends DurableObject {
export class SpaceState extends DurableObject {
async getActiveContainerTag(): Promise<string | undefined> {
return this.ctx.storage.get<string>(ACTIVE_CONTAINER_TAG_KEY)
}

View file

@ -1,16 +1,16 @@
import { describe, expect, it, vi } from "vitest"
import { optionalContainerTagSchema } from "./container-tag"
import { resolveContainerTag, workspaceStateName } from "./workspace"
import { resolveContainerTag, spaceStateName } from "./space"
describe("workspace application state", () => {
describe("space application state", () => {
it("keys active state by organization and user without collisions", () => {
expect(
workspaceStateName({
spaceStateName({
organizationId: "org:one",
userId: "user:two",
}),
).not.toBe(
workspaceStateName({
spaceStateName({
organizationId: "org",
userId: "one:user:two",
}),

View file

@ -1,9 +1,9 @@
import type { ActorContext } from "./types"
export function workspaceStateName(
export function spaceStateName(
actor: Pick<ActorContext, "organizationId" | "userId">,
): string {
return `workspace:${JSON.stringify([actor.organizationId, actor.userId])}`
return `space:${JSON.stringify([actor.organizationId, actor.userId])}`
}
export async function resolveContainerTag(

View file

@ -1,4 +1,4 @@
import type { WorkspaceState } from "./workspace-state"
import type { SpaceState } from "./space-state"
export interface ActorContext {
userId: string
@ -8,7 +8,7 @@ export interface ActorContext {
}
export interface ServerEnv {
WORKSPACE_STATE: DurableObjectNamespace<WorkspaceState>
SPACE_STATE: DurableObjectNamespace<SpaceState>
API_URL?: string
MCP_RESOURCE?: string
ALLOWED_MCP_ORIGIN_HOSTNAMES?: string

View file

@ -10,12 +10,7 @@ interface Props {
onClick: (containerTag: string) => void
}
export function WorkspaceCard({
containerTag,
active,
access,
onClick,
}: Props) {
export function SpaceCard({ containerTag, active, access, onClick }: Props) {
const name = containerTag.name || formatTagLabel(containerTag.containerTag)
const docs = containerTag.documentCount
const mems = containerTag.memoryCount
@ -26,26 +21,26 @@ export function WorkspaceCard({
return (
<button
className="workspace-card"
className="space-card"
data-active={active}
onClick={() => onClick(containerTag.containerTag)}
type="button"
>
<span className="workspace-card-inner">
<span className="workspace-card-main">
<span className="space-card-inner">
<span className="space-card-main">
<span
className={cn(
"workspace-card-title truncate text-sm font-medium",
"space-card-title truncate text-sm font-medium",
active ? "text-accent" : "text-text-primary",
)}
>
{name}
</span>
<span className="workspace-card-meta text-[11px] leading-normal text-text-muted">
<span className="space-card-meta text-[11px] leading-normal text-text-muted">
{meta}
</span>
</span>
<span className="workspace-card-trailing">
<span className="space-card-trailing">
{access ? <PermissionBadge permission={access.permission} /> : null}
</span>
</span>

View file

@ -1,8 +1,7 @@
import { formatTagLabel } from "../lib/formatTag"
// A calm workspace pill: a small accent dot + the workspace name on a neutral
// surface. Reads like a "space" tag rather than a blue-on-blue system badge.
export function WorkspaceChip({ containerTag }: { containerTag: string }) {
// A calm space pill: a small accent dot + the space name on a neutral surface.
export function SpaceChip({ containerTag }: { containerTag: string }) {
return (
<span className="inline-flex items-center gap-1.5 rounded-full bg-bg-muted px-2.5 py-1 text-(length:--text-xs) font-medium text-text-primary">
<span aria-hidden className="size-1.5 shrink-0 rounded-full bg-accent" />

View file

@ -183,8 +183,8 @@
line-height: 1;
}
/* ── Workspace picker — a framed, scrollable list of rows ─────────────────── */
.workspace-picker-grid {
/* ── Space picker — a framed, scrollable list of rows ─────────────────────── */
.space-picker-grid {
display: flex;
flex-direction: column;
max-height: 320px;
@ -198,11 +198,11 @@
scrollbar-width: none;
}
.workspace-picker-grid::-webkit-scrollbar {
.space-picker-grid::-webkit-scrollbar {
display: none;
}
.workspace-card {
.space-card {
position: relative;
display: block;
width: 100%;
@ -214,34 +214,34 @@
transition: background-color 0.15s ease;
}
.workspace-card:not(:last-child) {
.space-card:not(:last-child) {
border-bottom: 1px solid var(--border-muted);
}
.workspace-card:hover {
.space-card:hover {
background: var(--card-bg-hover);
}
.workspace-card:focus-visible {
.space-card:focus-visible {
outline: none;
background: var(--card-bg-hover);
box-shadow: inset 0 0 0 2px var(--accent-ring);
}
/* Active workspace is marked by an accent-tinted row + accent-colored name
* (see WorkspaceCard) no rail, no check, so the trailing badge stays aligned. */
.workspace-card[data-active="true"] {
/* Active space is marked by an accent-tinted row + accent-colored name
* (see SpaceCard) no rail, no check, so the trailing badge stays aligned. */
.space-card[data-active="true"] {
background: var(--card-active-bg);
}
.workspace-card-inner {
.space-card-inner {
display: flex;
align-items: center;
gap: var(--space-3);
min-width: 0;
}
.workspace-card-main {
.space-card-main {
display: flex;
flex-direction: column;
gap: 2px;
@ -249,11 +249,11 @@
min-width: 0;
}
.workspace-card-title {
.space-card-title {
min-width: 0;
}
.workspace-card-trailing {
.space-card-trailing {
display: flex;
align-items: center;
gap: var(--space-3);

View file

@ -5,17 +5,17 @@ import { Button } from "./Button"
import { Popover, PopoverContent, PopoverTrigger } from "./Popover"
import { Tooltip, TooltipContent, TooltipTrigger } from "./Tooltip"
export interface WorkspaceOption {
export interface SpaceOption {
value: string
label: string
description?: string
icon?: ReactNode
}
interface WorkspaceSelectProps {
interface SpaceSelectProps {
value: string | null
onValueChange: (value: string) => void
options: WorkspaceOption[]
options: SpaceOption[]
placeholder?: string
emptyText?: string
searchPlaceholder?: string
@ -25,7 +25,7 @@ interface WorkspaceSelectProps {
// Ports console-v2's ChipSelect/ContainerTagSelect: a Popover-anchored,
// searchable list. Scales to hundreds of spaces where chips can't.
export function WorkspaceSelect({
export function SpaceSelect({
value,
onValueChange,
options,
@ -34,7 +34,7 @@ export function WorkspaceSelect({
searchPlaceholder = "Search spaces…",
disabled = false,
className,
}: WorkspaceSelectProps) {
}: SpaceSelectProps) {
const [open, setOpen] = useState(false)
const [query, setQuery] = useState("")
const selected = value ? options.find((o) => o.value === value) : null
@ -63,7 +63,7 @@ export function WorkspaceSelect({
<Button
brandFont={false}
className={cn(
"workspace-select-trigger w-full justify-between normal-case tracking-normal font-medium",
"space-select-trigger w-full justify-between normal-case tracking-normal font-medium",
"shadow-[var(--shadow-inset)]",
"data-[state=open]:border-[var(--border-accent)]",
className,

View file

@ -21,4 +21,4 @@ export {
TooltipProvider,
TooltipTrigger,
} from "./Tooltip"
export { type WorkspaceOption, WorkspaceSelect } from "./WorkspaceSelect"
export { type SpaceOption, SpaceSelect } from "./SpaceSelect"

View file

@ -1,6 +1,6 @@
import { type ReactNode, useEffect, useState } from "react"
import { WidgetShell } from "../App"
import { WorkspaceCard } from "../components/WorkspaceCard"
import { SpaceCard } from "../components/SpaceCard"
import {
ActionGroup,
Badge,
@ -13,7 +13,7 @@ import {
PageHeader,
Stack,
TextArea,
WorkspaceSelect,
SpaceSelect,
} from "../design/ui"
import { Confirmation } from "../views/Confirmation"
import { ErrorView } from "../views/Error"
@ -288,7 +288,7 @@ export function Studio() {
<Section title="Composite components">
<div className="flex flex-wrap gap-(--space-4)">
<div className="w-64">
<WorkspaceCard
<SpaceCard
access={mockAssignedTags[0]}
active={false}
containerTag={mockContainerTags[0]}
@ -296,7 +296,7 @@ export function Studio() {
/>
</div>
<div className="w-64">
<WorkspaceCard
<SpaceCard
access={mockAssignedTags[1]}
active
containerTag={mockContainerTags[1]}
@ -304,7 +304,7 @@ export function Studio() {
/>
</div>
<div className="w-64">
<WorkspaceCard
<SpaceCard
active={false}
containerTag={mockContainerTags[3]}
onClick={() => {}}
@ -334,7 +334,7 @@ export function Studio() {
hint="Searchable Popover — scales to hundreds of spaces."
label="Space select"
>
<WorkspaceSelect
<SpaceSelect
onValueChange={setSelectValue}
options={mockContainerTags.map((t) => ({
value: t.containerTag,

View file

@ -1,4 +1,4 @@
import { WorkspaceChip } from "../components/WorkspaceChip"
import { SpaceChip } from "../components/SpaceChip"
import { Stack } from "../design/ui"
import { Check } from "../lib/icons"
@ -20,7 +20,7 @@ export function Confirmation({ containerTag }: Props) {
<div className="text-(length:--text-sm) font-semibold text-text-primary">
Active space set
</div>
<WorkspaceChip containerTag={containerTag} />
<SpaceChip containerTag={containerTag} />
</Stack>
<p className="max-w-xs text-(length:--text-xs) leading-relaxed text-text-muted">
Saves and recalls will use this space until you change it.

View file

@ -4,7 +4,7 @@ import type {
ContainerTagAccess,
ViewMessage,
} from "../../shared/types"
import { WorkspaceCard } from "../components/WorkspaceCard"
import { SpaceCard } from "../components/SpaceCard"
import { Input, PageHeader } from "../design/ui"
import { useApp } from "../hooks/useApp"
import { useLog } from "../hooks/useLog"
@ -120,19 +120,19 @@ export function Picker({
) : null}
{filtered.length === 0 ? (
<div className="workspace-picker-grid items-center justify-center py-(--space-8)">
<div className="space-picker-grid items-center justify-center py-(--space-8)">
<p className="px-(--space-4) text-center text-(length:--text-sm) text-text-muted">
No spaces match {query}.
</p>
</div>
) : (
<div className="workspace-picker-grid">
<div className="space-picker-grid">
{filtered.map((tag) => {
const access = assignedTags?.find(
(t) => t.containerTag === tag.containerTag,
)
return (
<WorkspaceCard
<SpaceCard
access={access}
active={activeTag === tag.containerTag}
containerTag={tag}

View file

@ -7,7 +7,7 @@ import {
PageHeader,
Stack,
TextArea,
WorkspaceSelect,
SpaceSelect,
} from "../design/ui"
import { useApp } from "../hooks/useApp"
import { useLog } from "../hooks/useLog"
@ -121,7 +121,7 @@ export function Save({
{writableTags.length > 0 ? (
<Field label="Space">
<WorkspaceSelect
<SpaceSelect
onValueChange={setSelectedTag}
options={options}
value={selectedTag}

View file

@ -1,4 +1,4 @@
import { WorkspaceChip } from "../components/WorkspaceChip"
import { SpaceChip } from "../components/SpaceChip"
import { Stack } from "../design/ui"
import { Check } from "../lib/icons"
@ -30,7 +30,7 @@ export function Success(props: Props) {
<div className="text-(length:--text-sm) font-semibold text-text-primary">
{isUpload ? `Uploaded ${props.fileName}` : "Memory saved"}
</div>
<WorkspaceChip containerTag={props.containerTag} />
<SpaceChip containerTag={props.containerTag} />
</Stack>
</Stack>
)

View file

@ -7,7 +7,7 @@ import {
FileUpload,
PageHeader,
Stack,
WorkspaceSelect,
SpaceSelect,
} from "../design/ui"
import { useApp } from "../hooks/useApp"
import { useLog } from "../hooks/useLog"
@ -151,7 +151,7 @@ export function Upload({
{writableTags.length > 0 ? (
<Field label="Space">
<WorkspaceSelect
<SpaceSelect
onValueChange={setSelectedTag}
options={options}
value={selectedTag}

View file

@ -29,8 +29,8 @@
"class_name": "SupermemoryMCP"
},
{
"name": "WORKSPACE_STATE",
"class_name": "WorkspaceState"
"name": "SPACE_STATE",
"class_name": "SpaceState"
}
]
},
@ -42,7 +42,7 @@
},
{
"tag": "v2",
"new_sqlite_classes": ["WorkspaceState"]
"new_sqlite_classes": ["SpaceState"]
}
],